From b767b6adfdd3614f87fbe30d42771ed6ff696634 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sat, 13 Jan 2018 10:10:07 -0500 Subject: [PATCH 01/22] Update ruby, bundler, and rspec versions --- .travis.yml | 4 ++-- goby.gemspec | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2fd724c..6651b54 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ sudo: false language: ruby rvm: - - 2.4.0 -before_install: gem install bundler -v 1.14.6 + - 2.5.0 +before_install: gem install bundler -v 1.16.1 notifications: email: false diff --git a/goby.gemspec b/goby.gemspec index 8aeaa6d..460cdfc 100644 --- a/goby.gemspec +++ b/goby.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_development_dependency "bundler", "~> 1.14" + spec.add_development_dependency "bundler", "~> 1.16" spec.add_development_dependency "rake", "~> 10.0" - spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "rspec", "~> 3.5" end From 4daa6a2bd26ba6c93afa90e252e68d063cd6c3e0 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sat, 13 Jan 2018 10:10:49 -0500 Subject: [PATCH 02/22] Run bundle install (bundler 1.16.1) --- bin/bundle | 105 ++++++++++++++++++++++++++++++++++++++++++++++ bin/coveralls | 12 ++++++ bin/goby | 29 +++++++++++++ bin/htmldiff | 12 ++++++ bin/ldiff | 12 ++++++ bin/rake | 12 ++++++ bin/rspec | 12 ++++++ bin/term_cdiff | 12 ++++++ bin/term_colortab | 12 ++++++ bin/term_decolor | 12 ++++++ bin/term_display | 12 ++++++ bin/term_mandel | 12 ++++++ bin/thor | 12 ++++++ 13 files changed, 266 insertions(+) create mode 100755 bin/bundle create mode 100755 bin/goby diff --git a/bin/bundle b/bin/bundle new file mode 100755 index 0000000..524dfd3 --- /dev/null +++ b/bin/bundle @@ -0,0 +1,105 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundle' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "rubygems" + +m = Module.new do + module_function + + def invoked_as_script? + File.expand_path($0) == File.expand_path(__FILE__) + end + + def env_var_version + ENV["BUNDLER_VERSION"] + end + + def cli_arg_version + return unless invoked_as_script? # don't want to hijack other binstubs + return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + bundler_version = nil + update_index = nil + ARGV.each_with_index do |a, i| + if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN + bundler_version = a + end + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ + bundler_version = $1 || ">= 0.a" + update_index = i + end + bundler_version + end + + def gemfile + gemfile = ENV["BUNDLE_GEMFILE"] + return gemfile if gemfile && !gemfile.empty? + + File.expand_path("../../Gemfile", __FILE__) + end + + def lockfile + lockfile = + case File.basename(gemfile) + when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) + else "#{gemfile}.lock" + end + File.expand_path(lockfile) + end + + def lockfile_version + return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) + end + + def bundler_version + @bundler_version ||= begin + env_var_version || cli_arg_version || + lockfile_version || "#{Gem::Requirement.default}.a" + end + end + + def load_bundler! + ENV["BUNDLE_GEMFILE"] ||= gemfile + + # must dup string for RG < 1.8 compatibility + activate_bundler(bundler_version.dup) + end + + def activate_bundler(bundler_version) + if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0") + bundler_version = "< 2" + end + gem_error = activation_error_handling do + gem "bundler", bundler_version + end + return if gem_error.nil? + require_error = activation_error_handling do + require "bundler/version" + end + return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`" + exit 42 + end + + def activation_error_handling + yield + nil + rescue StandardError, LoadError => e + e + end +end + +m.load_bundler! + +if m.invoked_as_script? + load Gem.bin_path("bundler", "bundle") +end diff --git a/bin/coveralls b/bin/coveralls index be276e8..813bb36 100755 --- a/bin/coveralls +++ b/bin/coveralls @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" diff --git a/bin/goby b/bin/goby new file mode 100755 index 0000000..d208401 --- /dev/null +++ b/bin/goby @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'goby' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "pathname" +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("goby", "goby") diff --git a/bin/htmldiff b/bin/htmldiff index 09c8259..fcb1240 100755 --- a/bin/htmldiff +++ b/bin/htmldiff @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" diff --git a/bin/ldiff b/bin/ldiff index a5e9564..48f40d6 100755 --- a/bin/ldiff +++ b/bin/ldiff @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" diff --git a/bin/rake b/bin/rake index 486010f..ea0e293 100755 --- a/bin/rake +++ b/bin/rake @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" diff --git a/bin/rspec b/bin/rspec index d738b23..9c652c5 100755 --- a/bin/rspec +++ b/bin/rspec @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" diff --git a/bin/term_cdiff b/bin/term_cdiff index b290733..669d7d8 100755 --- a/bin/term_cdiff +++ b/bin/term_cdiff @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" diff --git a/bin/term_colortab b/bin/term_colortab index fa63abe..f03be70 100755 --- a/bin/term_colortab +++ b/bin/term_colortab @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" diff --git a/bin/term_decolor b/bin/term_decolor index e5b803b..238b84c 100755 --- a/bin/term_decolor +++ b/bin/term_decolor @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" diff --git a/bin/term_display b/bin/term_display index eb7f8f1..84c6191 100755 --- a/bin/term_display +++ b/bin/term_display @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" diff --git a/bin/term_mandel b/bin/term_mandel index 8fa8335..0c06443 100755 --- a/bin/term_mandel +++ b/bin/term_mandel @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" diff --git a/bin/thor b/bin/thor index 63f10e5..22fe98f 100755 --- a/bin/thor +++ b/bin/thor @@ -1,5 +1,6 @@ #!/usr/bin/env ruby # frozen_string_literal: true + # # This file was generated by Bundler. # @@ -11,6 +12,17 @@ require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) +bundle_binstub = File.expand_path("../bundle", __FILE__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + require "rubygems" require "bundler/setup" From e5ec19e489ccd45dd1417abe29f38a1ea91d4d7a Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sat, 13 Jan 2018 10:46:09 -0500 Subject: [PATCH 03/22] Update tests to reflect new location type --- spec/goby/entity/player_spec.rb | 109 ++++++++++++++++---------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/spec/goby/entity/player_spec.rb b/spec/goby/entity/player_spec.rb index ec0e348..71024c5 100644 --- a/spec/goby/entity/player_spec.rb +++ b/spec/goby/entity/player_spec.rb @@ -37,8 +37,8 @@ expect(player.gold).to eq 0 expect(player.outfit).to eq Hash.new expect(player.battle_commands).to eq Array.new - expect(player.map).to eq Player::DEFAULT_MAP - expect(player.location).to eq Player::DEFAULT_LOCATION + expect(player.location.map).to eq Player::DEFAULT_MAP + expect(player.location.coords).to eq Player::DEFAULT_COORDS end it "correctly assigns custom parameters" do @@ -59,8 +59,7 @@ BattleCommand.new(name: "Yell"), BattleCommand.new(name: "Run") ], - map: map, - location: C[1, 1]) + location: Location.new(map, C[1, 1])) expect(hero.name).to eq "Hero" expect(hero.stats[:max_hp]).to eq 50 expect(hero.stats[:hp]).to eq 35 @@ -76,34 +75,34 @@ BattleCommand.new(name: "Run"), BattleCommand.new(name: "Yell") ] - expect(hero.map).to eq map - expect(hero.location).to eq C[1, 1] + expect(hero.location.map).to eq map + expect(hero.location.coords).to eq C[1, 1] end context "places the player in the default map & location" do it "receives the nil map" do - player = Player.new(location: C[2, 4]) - expect(player.map).to eq Player::DEFAULT_MAP - expect(player.location).to eq Player::DEFAULT_LOCATION + player = Player.new(location: Location.new(nil, C[2, 4])) + expect(player.location.map).to eq Player::DEFAULT_MAP + expect(player.location.coords).to eq Player::DEFAULT_COORDS end - it "receives the nil location" do - player = Player.new(map: Map.new) - expect(player.map).to eq Player::DEFAULT_MAP - expect(player.location).to eq Player::DEFAULT_LOCATION + it "receives nil coordinates" do + player = Player.new(location: Location.new(Map.new, nil)) + expect(player.location.map).to eq Player::DEFAULT_MAP + expect(player.location.coords).to eq Player::DEFAULT_COORDS end it "receives an out-of-bounds location" do - player = Player.new(map: Map.new, location: C[0, 1]) - expect(player.map).to eq Player::DEFAULT_MAP - expect(player.location).to eq Player::DEFAULT_LOCATION + player = Player.new(location: Location.new(Map.new, C[0, 1])) + expect(player.location.map).to eq Player::DEFAULT_MAP + expect(player.location.coords).to eq Player::DEFAULT_COORDS end it "receives an impassable location" do - player = Player.new(map: Map.new(tiles: [[Tile.new(passable: false)]]), - location: C[0, 0]) - expect(player.map).to eq Player::DEFAULT_MAP - expect(player.location).to eq Player::DEFAULT_LOCATION + player = Player.new(location: Location.new( + Map.new(tiles: [[Tile.new(passable: false)]]), C[0, 0])) + expect(player.location.map).to eq Player::DEFAULT_MAP + expect(player.location.coords).to eq Player::DEFAULT_COORDS end end @@ -159,29 +158,29 @@ context "move to" do it "correctly moves the player to a passable tile" do - dude.move_to(C[2, 1]) - expect(dude.map).to eq map - expect(dude.location).to eq C[2, 1] + dude.move_to(Location.new(dude.location.map, C[2, 1])) + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq C[2, 1] end it "prevents the player from moving on an impassable tile" do - dude.move_to(C[2, 2]) - expect(dude.map).to eq map - expect(dude.location).to eq center + dude.move_to(Location.new(dude.location.map, C[2, 2])) + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq center end it "prevents the player from moving on a nonexistent tile" do - dude.move_to(C[3, 3]) - expect(dude.map).to eq map - expect(dude.location).to eq center + dude.move_to(Location.new(dude.location.map, C[3, 3])) + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq center end it "saves the information from previous maps" do - dude.move_to(C[0, 0], chest_map) + dude.move_to(Location.new(chest_map, C[0, 0])) interpret_command("open", dude) expect(dude.gold).to eq 15 - dude.move_to(C[1, 1], Map.new) - dude.move_to(C[0, 0], Map.new(name: "Chest Map")) + dude.move_to(Location.new(Map.new, C[0, 0])) + dude.move_to(Location.new(Map.new(name: "Chest Map"), C[0, 0])) interpret_command("open", dude) expect(dude.gold).to eq 15 dude.move_right @@ -193,14 +192,14 @@ context "move up" do it "correctly moves the player to a passable tile" do dude.move_up - expect(dude.map).to eq map - expect(dude.location).to eq C[0, 1] + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq C[0, 1] end it "prevents the player from moving on a nonexistent tile" do dude.move_up; dude.move_up - expect(dude.map).to eq map - expect(dude.location).to eq C[0, 1] + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq C[0, 1] end end @@ -209,9 +208,11 @@ 20.times do __stdin("Attack\n") do dude.move_right - expect(dude.map).to eq map - expect(dude.location).to eq C[1, 2] + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq C[1, 2] dude.move_left + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq C[1, 1] end end end @@ -219,8 +220,8 @@ it "prevents the player from moving on a nonexistent tile" do __stdin("Attack\n") do dude.move_right; dude.move_right - expect(dude.map).to eq map - expect(dude.location).to eq C[1, 2] + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq C[1, 2] end end end @@ -228,28 +229,28 @@ context "move down" do it "correctly moves the player to a passable tile" do dude.move_down - expect(dude.map).to eq map - expect(dude.location).to eq C[2, 1] + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq C[2, 1] end it "prevents the player from moving on a nonexistent tile" do dude.move_down; dude.move_down - expect(dude.map).to eq map - expect(dude.location).to eq C[2, 1] + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq C[2, 1] end end context "move left" do it "correctly moves the player to a passable tile" do dude.move_left - expect(dude.map).to eq map - expect(dude.location).to eq C[1, 0] + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq C[1, 0] end it "prevents the player from moving on a nonexistent tile" do dude.move_left; dude.move_left - expect(dude.map).to eq map - expect(dude.location).to eq C[1, 0] + expect(dude.location.map).to eq map + expect(dude.location.coords).to eq C[1, 0] end end @@ -302,7 +303,7 @@ context "print tile" do it "should display the marker on the player's location" do - expect { dude.print_tile(dude.location) }.to output("¶ ").to_stdout + expect { dude.print_tile(dude.location.coords) }.to output("¶ ").to_stdout end it "should display the graphic of the tile elsewhere" do @@ -345,7 +346,7 @@ end # Newb should die and go to respawn location. expect(newb.gold).to eq 25 - expect(newb.location).to eq C[1, 1] + expect(newb.location.coords).to eq C[1, 1] end it "should allow the stronger player to win as the attacker" do @@ -354,7 +355,7 @@ end # Weaker Player should die and go to respawn location. expect(newb.gold).to eq 25 - expect(newb.location).to eq C[1, 1] + expect(newb.location.coords).to eq C[1, 1] # Stronger Player should get weaker Players gold expect(dude.gold).to eq (35) end @@ -365,7 +366,7 @@ end # Weaker Player should die and go to respawn location. expect(newb.gold).to eq 25 - expect(newb.location).to eq C[1, 1] + expect(newb.location.coords).to eq C[1, 1] # Stronger Player should get weaker Players gold expect(dude.gold).to eq (35) end @@ -375,9 +376,9 @@ context "die" do it "moves the player back to the map's regen location" do dude.move_down - expect(dude.location).to eq C[2, 1] + expect(dude.location.coords).to eq C[2, 1] dude.die - expect(dude.location).to eq map.regen_location + expect(dude.location.coords).to eq map.regen_location end it "recovers the player's HP to max" do From e546836fd5a558c00eb691b08cd91fdd4eea7bba Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sat, 13 Jan 2018 11:03:43 -0500 Subject: [PATCH 04/22] Implement Location class (w/ initialize test) --- lib/goby/util.rb | 16 ++++++++++++++++ spec/goby/util_spec.rb | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/lib/goby/util.rb b/lib/goby/util.rb index 14adb54..2000553 100644 --- a/lib/goby/util.rb +++ b/lib/goby/util.rb @@ -29,6 +29,22 @@ def ==(rhs) attr_accessor :first, :second end + # The combination of a map and some coordinates, + # which determine a specific position/location on the map. + class Location + + # Location constructor. + # + # @param [Map] map the map component. + # @param [C(Integer, Integer)] + def initialize(map, coords) + @map = map + @coords = coords + end + + attr_reader :map, :coords + end + # Simple player input script. # # @param [Boolean] lowercase mark true if response should be returned lowercase. diff --git a/spec/goby/util_spec.rb b/spec/goby/util_spec.rb index e8038dc..e2d899b 100644 --- a/spec/goby/util_spec.rb +++ b/spec/goby/util_spec.rb @@ -33,6 +33,15 @@ end end + context "location" do + it "should correctly initialize the location" do + map = Map.new(name: "Test Map") + location = Location.new(map, C[0, 0]) + expect(location.map.name).to eq "Test Map" + expect(location.coords).to eq C[0, 0] + end + end + context "player input" do before (:each) { Readline::HISTORY.pop until Readline::HISTORY.size <= 0 } From 76a6b5659178adecd4577b9caf72f4b8260a3d3c Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sat, 13 Jan 2018 12:08:19 -0500 Subject: [PATCH 05/22] Implement Location functionality through Player --- lib/goby/driver.rb | 2 +- lib/goby/entity/player.rb | 97 +++++++++++++++++---------------- lib/goby/map/map.rb | 8 +-- lib/goby/world_command.rb | 6 +- res/scaffold/simple/farm.rb | 4 +- res/scaffold/simple/main.rb | 2 +- spec/goby/entity/player_spec.rb | 16 +++--- spec/goby/map/map_spec.rb | 6 +- spec/goby/world_command_spec.rb | 21 ++++--- 9 files changed, 81 insertions(+), 81 deletions(-) diff --git a/lib/goby/driver.rb b/lib/goby/driver.rb index 160c19d..b998557 100644 --- a/lib/goby/driver.rb +++ b/lib/goby/driver.rb @@ -30,7 +30,7 @@ def clear_and_minimap(player) def run_turn(player) # Play music and re-display the minimap (when appropriate). - play_music(player.map.music) if player.map.music + play_music(player.map.music) if player.location.map.music if player.moved clear_and_minimap(player) player.moved = false diff --git a/lib/goby/entity/player.rb b/lib/goby/entity/player.rb index 5764fcc..913c159 100644 --- a/lib/goby/entity/player.rb +++ b/lib/goby/entity/player.rb @@ -3,7 +3,7 @@ module Goby # Extends upon Entity by providing a location in the - # form of a Map and a pair of y-x coordinates. Overrides + # form of a Map and a pair of y-x location. Overrides # some methods to accept input during battle. class Player < Entity @@ -13,7 +13,7 @@ class Player < Entity # Default map when no "good" map & location specified. DEFAULT_MAP = Map.new(tiles: [[Tile.new]]) # Default location when no "good" map & location specified. - DEFAULT_LOCATION = C[0, 0] + DEFAULT_COORDS = C[0, 0] # distance in each direction that tiles are acted upon # used in: update_map, print_minimap @@ -25,27 +25,26 @@ class Player < Entity # @param [Integer] gold the currency used for economical transactions. # @param [[BattleCommand]] battle_commands the commands that can be used in battle. # @param [Hash] outfit the collection of equippable items currently worn. - # @param [Map] map the map on which the player is located. - # @param [C(Integer,Integer)] location the 2D index of the map (the exact tile). + # @param [Location] location at which the player should start. def initialize(name: "Player", stats: {}, inventory: [], gold: 0, battle_commands: [], - outfit: {}, map: nil, location: nil) + outfit: {}, location: nil) super(name: name, stats: stats, inventory: inventory, gold: gold, outfit: outfit) @saved_maps = Hash.new # Ensure that the map and the location are valid. new_map = DEFAULT_MAP - new_location = DEFAULT_LOCATION - if (map && location) - y = location.first; x = location.second - if (map.in_bounds(y, x) && map.tiles[y][x].passable) - new_map = map - new_location = location + new_coords = DEFAULT_COORDS + if (location && location.map && location.coords) + y = location.coords.first; x = location.coords.second + if (location.map.in_bounds(y, x) && location.map.tiles[y][x].passable) + new_map = location.map + new_coords = location.coords end end add_battle_commands(battle_commands) - move_to(new_location, new_map) + move_to(Location.new(new_map, new_coords)) @saved_maps = Hash.new end @@ -122,11 +121,11 @@ def choose_item_and_on_whom(enemy) def die sleep(2) unless ENV['TEST'] - # TODO: fix next line. regen_location could be nil or "bad." - @location = @map.regen_location + # TODO: fix next line. regen_coords could be nil or "bad." + @location = Location.new(@location.map, @location.map.regen_coords) type("After being knocked out in battle,\n") - type("you wake up in #{@map.name}.\n\n") + type("you wake up in #{@location.map.name}.\n\n") sleep(2) unless ENV['TEST'] @@ -136,34 +135,36 @@ def die # Moves the player down. Increases 'y' coordinate by 1. def move_down - down_tile = C[@location.first + 1, @location.second] - move_to(down_tile) + down_tile = C[@location.coords.first + 1, @location.coords.second] + move_to(Location.new(@location.map, down_tile)) end # Moves the player left. Decreases 'x' coordinate by 1. def move_left - left_tile = C[@location.first, @location.second - 1] - move_to(left_tile) + left_tile = C[@location.coords.first, @location.coords.second - 1] + move_to(Location.new(@location.map, left_tile)) end # Moves the player right. Increases 'x' coordinate by 1. def move_right - right_tile = C[@location.first, @location.second + 1] - move_to(right_tile) + right_tile = C[@location.coords.first, @location.coords.second + 1] + move_to(Location.new(@location.map, right_tile)) end # Safe setter function for location and map. # - # @param [C(Integer, Integer)] coordinates the new location. - # @param [Map] map the (possibly) new map. - def move_to(coordinates, map = @map) + # @param [Location] location the new location. + def move_to(location) + + map = location.map + y = location.coords.first + x = location.coords.second + # Prevents operations on nil. return if map.nil? - y = coordinates.first; x = coordinates.second - # Save the map. - @saved_maps[@map.name] = @map if @map + @saved_maps[@location.map.name] = @location.map if @location # Even if the player hasn't moved, we still change to true. # This is because we want to re-display the minimap anyway. @@ -172,14 +173,14 @@ def move_to(coordinates, map = @map) # Prevents moving onto nonexistent and impassable tiles. return if !(map.in_bounds(y, x) && map.tiles[y][x].passable) - @map = @saved_maps[map.name] ? @saved_maps[map.name] : map - @location = coordinates - tile = @map.tiles[y][x] - + # Update the location and surrounding tiles. + @location = Location.new( + @saved_maps[map.name] ? @saved_maps[map.name] : map, location.coords) update_map + tile = @location.map.tiles[y][x] unless tile.monsters.empty? - # 50% chance to encounter monster. + # 50% chance to encounter monster (TODO: too high?) if [true, false].sample clone = tile.monsters[Random.rand(0..(tile.monsters.size-1))].clone battle(clone) @@ -189,8 +190,8 @@ def move_to(coordinates, map = @map) # Moves the player up. Decreases 'y' coordinate by 1. def move_up - up_tile = C[@location.first - 1, @location.second] - move_to(up_tile) + up_tile = C[@location.coords.first - 1, @location.coords.second] + move_to(Location.new(@location.map, up_tile)) end # Prints the map in regards to what the player has seen. @@ -200,9 +201,9 @@ def print_map # Provide some spacing from the edge of the terminal. 3.times { print " " }; - print @map.name + "\n\n" + print @location.map.name + "\n\n" - @map.tiles.each_with_index do |row, r| + @location.map.tiles.each_with_index do |row, r| # Provide spacing for the beginning of each row. 2.times { print " " } @@ -224,39 +225,39 @@ def print_map # Prints a minimap of nearby tiles (using VIEW_DISTANCE). def print_minimap print "\n" - for y in (@location.first-VIEW_DISTANCE)..(@location.first+VIEW_DISTANCE) + for y in (@location.coords.first-VIEW_DISTANCE)..(@location.coords.first+VIEW_DISTANCE) # skip to next line if out of bounds from above map next if y.negative? # centers minimap 10.times { print " " } - for x in (@location.second-VIEW_DISTANCE)..(@location.second+VIEW_DISTANCE) + for x in (@location.coords.second-VIEW_DISTANCE)..(@location.coords.second+VIEW_DISTANCE) # Prevents operations on nonexistent tiles. - print_tile(C[y, x]) if (@map.in_bounds(y, x)) + print_tile(C[y, x]) if (@location.map.in_bounds(y, x)) end # new line if this row is not out of bounds - print "\n" if y < @map.tiles.size + print "\n" if y < @location.map.tiles.size end print "\n" end # Prints the tile based on the player's location. # - # @param [C(Integer, Integer)] coords the y-x coordinates of the tile. + # @param [C(Integer, Integer)] coords the y-x location of the tile. def print_tile(coords) - if ((@location.first == coords.first) && (@location.second == coords.second)) + if ((@location.coords.first == coords.first) && (@location.coords.second == coords.second)) print "¶ " else - print @map.tiles[coords.first][coords.second].to_s + print @location.map.tiles[coords.first][coords.second].to_s end end # Updates the 'seen' attributes of the tiles on the player's current map. # - # @param [C(Integer, Integer)] coordinates to update seen attribute for tiles on the map - def update_map(coordinates = @location) - for y in (coordinates.first-VIEW_DISTANCE)..(coordinates.first+VIEW_DISTANCE) - for x in (coordinates.second-VIEW_DISTANCE)..(coordinates.second+VIEW_DISTANCE) - @map.tiles[y][x].seen = true if (@map.in_bounds(y, x)) + # @param [Location] location to update seen attribute for tiles on the map. + def update_map(location = @location) + for y in (location.coords.first-VIEW_DISTANCE)..(location.coords.first+VIEW_DISTANCE) + for x in (location.coords.second-VIEW_DISTANCE)..(location.coords.second+VIEW_DISTANCE) + @location.map.tiles[y][x].seen = true if (@location.map.in_bounds(y, x)) end end end diff --git a/lib/goby/map/map.rb b/lib/goby/map/map.rb index b1ebbf2..7d33029 100644 --- a/lib/goby/map/map.rb +++ b/lib/goby/map/map.rb @@ -5,11 +5,11 @@ class Map # @param [String] name the name. # @param [[Tile]] tiles the content of the map. - # @param [C(Integer, Integer)] regen_location respawn-on-death coordinates. - def initialize(name: "Map", tiles: [[Tile.new]], regen_location: C[0,0], music: nil) + # @param [C(Integer, Integer)] regen_coords respawn-on-death coordinates. + def initialize(name: "Map", tiles: [[Tile.new]], regen_coords: C[0,0], music: nil) @name = name @tiles = tiles - @regen_location = regen_location + @regen_coords = regen_coords @music = music end @@ -40,7 +40,7 @@ def ==(rhs) return @name == rhs.name end - attr_accessor :name, :tiles, :regen_location, :music + attr_accessor :name, :tiles, :regen_coords, :music end diff --git a/lib/goby/world_command.rb b/lib/goby/world_command.rb index 187bf8f..bd1611b 100644 --- a/lib/goby/world_command.rb +++ b/lib/goby/world_command.rb @@ -37,7 +37,7 @@ def display_default_commands # # @param [Player] player the player who wants to see the special commands. def display_special_commands(player) - events = player.map.tiles[player.location.first][player.location.second].events + events = player.location.map.tiles[player.location.coords.first][player.location.coords.second].events if events.nonempty? && events.any? { |event| event.visible } print SPECIAL_COMMANDS_HEADER + (events.reduce([]) do |commands, event| @@ -59,7 +59,7 @@ def help(player) # # @param [Player] player the player who needs the tile description. def describe_tile(player) - tile = player.map.tiles[player.location.first][player.location.second] + tile = player.location.map.tiles[player.location.coords.first][player.location.coords.second] events = tile.events player.print_minimap @@ -134,7 +134,7 @@ def interpret_command(command, player) end # Other commands. - events = player.map.tiles[player.location.first][player.location.second].events + events = player.location.map.tiles[player.location.coords.first][player.location.coords.second].events events.each do |event| if (event.visible && words[0] && words[0].casecmp(event.command).zero?) event.run(player) diff --git a/res/scaffold/simple/farm.rb b/res/scaffold/simple/farm.rb index e2c05e7..4e58824 100644 --- a/res/scaffold/simple/farm.rb +++ b/res/scaffold/simple/farm.rb @@ -3,13 +3,13 @@ # the Map - each point is referred to as a Tile. class Farm < Map def initialize - super(name: "Farm", regen_location: C[2,2]) + super(name: "Farm", regen_coords: C[1, 1]) # Define the main tiles on this map. grass = Tile.new(description: "You are standing on some grass.") # Fill the map with "grass." - @tiles = Array.new(5) { Array.new(5) { grass.clone } } + @tiles = Array.new(9) { Array.new(5) { grass.clone } } end end diff --git a/res/scaffold/simple/main.rb b/res/scaffold/simple/main.rb index f56479c..016e8fd 100644 --- a/res/scaffold/simple/main.rb +++ b/res/scaffold/simple/main.rb @@ -29,7 +29,7 @@ # Use the Player constructor to set the # initial Map, (y,x) location, stats, # gold, inventory, and more. - player = Player.new(map: Farm.new, location: C[2,2]) + player = Player.new(location: Location.new(Farm.new, C[1, 1])) end diff --git a/spec/goby/entity/player_spec.rb b/spec/goby/entity/player_spec.rb index 71024c5..68340b8 100644 --- a/spec/goby/entity/player_spec.rb +++ b/spec/goby/entity/player_spec.rb @@ -6,23 +6,23 @@ let!(:map) { Map.new(tiles: [[Tile.new(passable: false), Tile.new, Tile.new(passable: false)], [Tile.new, Tile.new, Tile.new(monsters: [Monster.new(battle_commands: [Attack.new(success_rate: 0)])])], [Tile.new(passable: false), Tile.new, Tile.new(passable: false)]], - regen_location: C[1, 1]) } - let!(:center) { map.regen_location } + regen_coords: C[1, 1]) } + let!(:center) { map.regen_coords } let!(:passable) { Tile::DEFAULT_PASSABLE } let!(:impassable) { Tile::DEFAULT_IMPASSABLE } let!(:dude) { Player.new(stats: {attack: 10, agility: 10000, map_hp: 2000}, gold: 10, battle_commands: [Attack.new(strength: 20), Escape.new, Use.new], - map: map, location: center) } + location: Location.new(map, center)) } let!(:slime) { Monster.new(battle_commands: [Attack.new(success_rate: 0)], gold: 5000, treasures: [C[Item.new, 1]]) } let!(:newb) { Player.new(battle_commands: [Attack.new(success_rate: 0)], - gold: 50, map: map, location: center) } + gold: 50, location: Location.new(map, center)) } let!(:dragon) { Monster.new(stats: {attack: 50, agility: 10000}, battle_commands: [Attack.new(strength: 50)]) } let!(:chest_map) { Map.new(name: "Chest Map", tiles: [[Tile.new(events: [Chest.new(gold: 5)]), Tile.new(events: [Chest.new(gold: 5)])]], - regen_location: C[0, 0]) } + regen_coords: C[0, 0]) } context "constructor" do it "has the correct default parameters" do @@ -256,7 +256,7 @@ context "update map" do let!(:line_map) { Map.new(tiles: [[Tile.new, Tile.new, Tile.new, Tile.new]]) } - let!(:player) { Player.new(map: line_map, location: C[0, 0]) } + let!(:player) { Player.new(location: Location.new(line_map, C[0, 0])) } it "uses default argument to update tiles" do player.update_map @@ -264,7 +264,7 @@ end it "uses given argument to update tiles" do - player.update_map(C[0, 2]) + player.update_map(Location.new(player.location.map, C[0, 2])) expect(line_map.tiles[0][3].seen).to eq true end end @@ -378,7 +378,7 @@ dude.move_down expect(dude.location.coords).to eq C[2, 1] dude.die - expect(dude.location.coords).to eq map.regen_location + expect(dude.location.coords).to eq map.regen_coords end it "recovers the player's HP to max" do diff --git a/spec/goby/map/map_spec.rb b/spec/goby/map/map_spec.rb index 0180bf7..e49d486 100644 --- a/spec/goby/map/map_spec.rb +++ b/spec/goby/map/map_spec.rb @@ -4,21 +4,21 @@ let(:lake) { Map.new(name: "Lake", tiles: [ [ Tile.new, Tile.new(passable: false) ] ], - regen_location: C[0,1]) } + regen_coords: C[0,1]) } context "constructor" do it "has the correct default parameters" do map = Map.new expect(map.name).to eq "Map" expect(map.tiles[0][0].passable).to be true - expect(map.regen_location).to eq C[0,0] + expect(map.regen_coords).to eq C[0,0] end it "correctly assigns custom parameters" do expect(lake.name).to eq "Lake" expect(lake.tiles[0][0].passable).to be true expect(lake.tiles[0][1].passable).to be false - expect(lake.regen_location).to eq C[0,1] + expect(lake.regen_coords).to eq C[0,1] end end diff --git a/spec/goby/world_command_spec.rb b/spec/goby/world_command_spec.rb index 96e18cf..a8b0d5e 100644 --- a/spec/goby/world_command_spec.rb +++ b/spec/goby/world_command_spec.rb @@ -9,15 +9,14 @@ Tile.new(events: [Event.new(visible: false)]) ], [ Tile.new(events: [Shop.new, NPC.new]), Tile.new(events: [Event.new(visible: false), Shop.new, NPC.new]) ] ], - regen_location: C[0,0]) } + regen_coords: C[0,0]) } let!(:player) { Player.new(stats: { max_hp: 10, hp: 3 }, inventory: [ C[Food.new(name: "Banana", recovers: 5), 1], C[Food.new(name: "Onion", disposable: false), 1], C[Item.new(name: "Big Book of Stuff"), 1], C[Helmet.new, 1] ], - map: map, - location: C[0, 0]) } + location: Location.new(map, C[0, 0])) } context "display default commands" do it "should print the default commands" do @@ -82,13 +81,13 @@ context "lowercase" do it "should correctly move the player around" do interpret_command("s", player) - expect(player.location).to eq C[1, 0] + expect(player.location.coords).to eq C[1, 0] interpret_command("d", player) - expect(player.location).to eq C[1, 1] + expect(player.location.coords).to eq C[1, 1] interpret_command("w", player) - expect(player.location).to eq C[0, 1] + expect(player.location.coords).to eq C[0, 1] interpret_command("a", player) - expect(player.location).to eq C[0, 0] + expect(player.location.coords).to eq C[0, 0] end it "should display the help text" do @@ -179,13 +178,13 @@ context "case-insensitive" do it "should correctly move the player around" do interpret_command("S", player) - expect(player.location).to eq C[1, 0] + expect(player.location.coords).to eq C[1, 0] interpret_command("D", player) - expect(player.location).to eq C[1, 1] + expect(player.location.coords).to eq C[1, 1] interpret_command("W", player) - expect(player.location).to eq C[0, 1] + expect(player.location.coords).to eq C[0, 1] interpret_command("A", player) - expect(player.location).to eq C[0, 0] + expect(player.location.coords).to eq C[0, 0] end end From 33210133dc2b48d8e1803977ebd4fdbfc1dc8389 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sat, 13 Jan 2018 12:30:57 -0500 Subject: [PATCH 06/22] Remove old map variable from Player --- lib/goby/entity/player.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/goby/entity/player.rb b/lib/goby/entity/player.rb index 913c159..196a203 100644 --- a/lib/goby/entity/player.rb +++ b/lib/goby/entity/player.rb @@ -293,7 +293,7 @@ def sample_gold gold_lost end - attr_reader :map, :location, :saved_maps + attr_reader :location, :saved_maps attr_accessor :moved end From c7091e0d4ec86959e4bad3f878a947ba56242ab1 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sun, 14 Jan 2018 14:40:06 -0500 Subject: [PATCH 07/22] Fix typo: crash from invalid 'map' --- lib/goby/driver.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/goby/driver.rb b/lib/goby/driver.rb index b998557..78198ea 100644 --- a/lib/goby/driver.rb +++ b/lib/goby/driver.rb @@ -30,7 +30,8 @@ def clear_and_minimap(player) def run_turn(player) # Play music and re-display the minimap (when appropriate). - play_music(player.map.music) if player.location.map.music + music = player.location.map.music + play_music(music) if music if player.moved clear_and_minimap(player) player.moved = false From 3a7050bf016620732423db87e6153222ea679d5e Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sun, 14 Jan 2018 15:16:59 -0500 Subject: [PATCH 08/22] Add respawn_location to Player class --- lib/goby/entity/player.rb | 8 +++++--- spec/goby/entity/player_spec.rb | 6 +++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/goby/entity/player.rb b/lib/goby/entity/player.rb index 196a203..ad56371 100644 --- a/lib/goby/entity/player.rb +++ b/lib/goby/entity/player.rb @@ -25,9 +25,10 @@ class Player < Entity # @param [Integer] gold the currency used for economical transactions. # @param [[BattleCommand]] battle_commands the commands that can be used in battle. # @param [Hash] outfit the collection of equippable items currently worn. - # @param [Location] location at which the player should start. + # @param [Location] location the place at which the player should start. + # @param [Location] respawn_location the place at which the player respawns. def initialize(name: "Player", stats: {}, inventory: [], gold: 0, battle_commands: [], - outfit: {}, location: nil) + outfit: {}, location: nil, respawn_location: nil) super(name: name, stats: stats, inventory: inventory, gold: gold, outfit: outfit) @saved_maps = Hash.new @@ -45,6 +46,7 @@ def initialize(name: "Player", stats: {}, inventory: [], gold: 0, battle_command add_battle_commands(battle_commands) move_to(Location.new(new_map, new_coords)) + @respawn_location = respawn_location @saved_maps = Hash.new end @@ -293,7 +295,7 @@ def sample_gold gold_lost end - attr_reader :location, :saved_maps + attr_reader :location, :respawn_location, :saved_maps attr_accessor :moved end diff --git a/spec/goby/entity/player_spec.rb b/spec/goby/entity/player_spec.rb index 68340b8..f42d2c1 100644 --- a/spec/goby/entity/player_spec.rb +++ b/spec/goby/entity/player_spec.rb @@ -39,6 +39,7 @@ expect(player.battle_commands).to eq Array.new expect(player.location.map).to eq Player::DEFAULT_MAP expect(player.location.coords).to eq Player::DEFAULT_COORDS + expect(player.respawn_location).to be_nil end it "correctly assigns custom parameters" do @@ -59,7 +60,8 @@ BattleCommand.new(name: "Yell"), BattleCommand.new(name: "Run") ], - location: Location.new(map, C[1, 1])) + location: Location.new(map, C[1, 1]), + respawn_location: Location.new(map, C[1, 2])) expect(hero.name).to eq "Hero" expect(hero.stats[:max_hp]).to eq 50 expect(hero.stats[:hp]).to eq 35 @@ -77,6 +79,8 @@ ] expect(hero.location.map).to eq map expect(hero.location.coords).to eq C[1, 1] + expect(hero.respawn_location.map).to eq map + expect(hero.respawn_location.coords).to eq C[1, 2] end context "places the player in the default map & location" do From d604ca4319b41af8fb8e07aa12bf00ba33ebab56 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sun, 14 Jan 2018 15:51:36 -0500 Subject: [PATCH 09/22] Use respawn_location for Player death --- lib/goby/entity/player.rb | 4 ++-- spec/goby/entity/monster_spec.rb | 2 +- spec/goby/entity/player_spec.rb | 24 +++++++++++++----------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/goby/entity/player.rb b/lib/goby/entity/player.rb index ad56371..3cbb04d 100644 --- a/lib/goby/entity/player.rb +++ b/lib/goby/entity/player.rb @@ -123,8 +123,8 @@ def choose_item_and_on_whom(enemy) def die sleep(2) unless ENV['TEST'] - # TODO: fix next line. regen_coords could be nil or "bad." - @location = Location.new(@location.map, @location.map.regen_coords) + # TODO: No respawn location? Display 'GAME OVER' & end game. + @location = @respawn_location type("After being knocked out in battle,\n") type("you wake up in #{@location.map.name}.\n\n") diff --git a/spec/goby/entity/monster_spec.rb b/spec/goby/entity/monster_spec.rb index af41578..df31435 100644 --- a/spec/goby/entity/monster_spec.rb +++ b/spec/goby/entity/monster_spec.rb @@ -31,7 +31,7 @@ let!(:slime) { Monster.new(battle_commands: [Attack.new(success_rate: 0)], gold: 5000, treasures: [C[slime_item, 1]]) } let(:newb) { Player.new(battle_commands: [Attack.new(success_rate: 0)], - gold: 50) } + gold: 50, respawn_location: Location.new(Map.new, C[0, 0])) } context "constructor" do it "has the correct default parameters" do diff --git a/spec/goby/entity/player_spec.rb b/spec/goby/entity/player_spec.rb index f42d2c1..d09db2a 100644 --- a/spec/goby/entity/player_spec.rb +++ b/spec/goby/entity/player_spec.rb @@ -17,7 +17,8 @@ let!(:slime) { Monster.new(battle_commands: [Attack.new(success_rate: 0)], gold: 5000, treasures: [C[Item.new, 1]]) } let!(:newb) { Player.new(battle_commands: [Attack.new(success_rate: 0)], - gold: 50, location: Location.new(map, center)) } + gold: 50, location: Location.new(map, center), + respawn_location: Location.new(map, C[1, 2])) } let!(:dragon) { Monster.new(stats: {attack: 50, agility: 10000}, battle_commands: [Attack.new(strength: 50)]) } let!(:chest_map) { Map.new(name: "Chest Map", @@ -350,7 +351,7 @@ end # Newb should die and go to respawn location. expect(newb.gold).to eq 25 - expect(newb.location.coords).to eq C[1, 1] + expect(newb.location.coords).to eq C[1, 2] end it "should allow the stronger player to win as the attacker" do @@ -359,7 +360,7 @@ end # Weaker Player should die and go to respawn location. expect(newb.gold).to eq 25 - expect(newb.location.coords).to eq C[1, 1] + expect(newb.location.coords).to eq C[1, 2] # Stronger Player should get weaker Players gold expect(dude.gold).to eq (35) end @@ -370,7 +371,7 @@ end # Weaker Player should die and go to respawn location. expect(newb.gold).to eq 25 - expect(newb.location.coords).to eq C[1, 1] + expect(newb.location.coords).to eq C[1, 2] # Stronger Player should get weaker Players gold expect(dude.gold).to eq (35) end @@ -379,16 +380,17 @@ context "die" do it "moves the player back to the map's regen location" do - dude.move_down - expect(dude.location.coords).to eq C[2, 1] - dude.die - expect(dude.location.coords).to eq map.regen_coords + newb.move_down + expect(newb.location.coords).to eq C[2, 1] + newb.die + expect(newb.location.map).to eq map + expect(newb.location.coords).to eq C[1, 2] end it "recovers the player's HP to max" do - dude.set_stats(hp: 0) - dude.die - expect(dude.stats[:hp]).to eq dude.stats[:max_hp] + newb.set_stats(hp: 0) + newb.die + expect(newb.stats[:hp]).to eq newb.stats[:max_hp] end end From e37ad2e2c25cf96cc8ea593adfa21663c80dc9d3 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sun, 14 Jan 2018 16:01:36 -0500 Subject: [PATCH 10/22] Remove references to regen_coords; update scaffold project --- lib/goby/map/map.rb | 6 ++---- res/scaffold/simple/farm.rb | 2 +- res/scaffold/simple/main.rb | 7 ++++--- spec/goby/entity/player_spec.rb | 8 +++----- spec/goby/map/map_spec.rb | 5 +---- spec/goby/world_command_spec.rb | 3 +-- 6 files changed, 12 insertions(+), 19 deletions(-) diff --git a/lib/goby/map/map.rb b/lib/goby/map/map.rb index 7d33029..d4bd5c5 100644 --- a/lib/goby/map/map.rb +++ b/lib/goby/map/map.rb @@ -5,11 +5,9 @@ class Map # @param [String] name the name. # @param [[Tile]] tiles the content of the map. - # @param [C(Integer, Integer)] regen_coords respawn-on-death coordinates. - def initialize(name: "Map", tiles: [[Tile.new]], regen_coords: C[0,0], music: nil) + def initialize(name: "Map", tiles: [[Tile.new]], music: nil) @name = name @tiles = tiles - @regen_coords = regen_coords @music = music end @@ -40,7 +38,7 @@ def ==(rhs) return @name == rhs.name end - attr_accessor :name, :tiles, :regen_coords, :music + attr_accessor :name, :tiles, :music end diff --git a/res/scaffold/simple/farm.rb b/res/scaffold/simple/farm.rb index 4e58824..a16033a 100644 --- a/res/scaffold/simple/farm.rb +++ b/res/scaffold/simple/farm.rb @@ -3,7 +3,7 @@ # the Map - each point is referred to as a Tile. class Farm < Map def initialize - super(name: "Farm", regen_coords: C[1, 1]) + super(name: "Farm") # Define the main tiles on this map. grass = Tile.new(description: "You are standing on some grass.") diff --git a/res/scaffold/simple/main.rb b/res/scaffold/simple/main.rb index 016e8fd..abc51d8 100644 --- a/res/scaffold/simple/main.rb +++ b/res/scaffold/simple/main.rb @@ -25,11 +25,12 @@ # No load? Create a new player. if player.nil? + # A Location specifies the Map and (y,x) coordinates of a Player. + home = Location.new(Farm.new, C[1, 1]) # Use the Player constructor to set the - # initial Map, (y,x) location, stats, - # gold, inventory, and more. - player = Player.new(location: Location.new(Farm.new, C[1, 1])) + # location, stats, gold, inventory, and more. + player = Player.new(location: home, respawn_location: home) end diff --git a/spec/goby/entity/player_spec.rb b/spec/goby/entity/player_spec.rb index d09db2a..bf98f2d 100644 --- a/spec/goby/entity/player_spec.rb +++ b/spec/goby/entity/player_spec.rb @@ -5,9 +5,8 @@ # Constructs a map in the shape of a plus sign. let!(:map) { Map.new(tiles: [[Tile.new(passable: false), Tile.new, Tile.new(passable: false)], [Tile.new, Tile.new, Tile.new(monsters: [Monster.new(battle_commands: [Attack.new(success_rate: 0)])])], - [Tile.new(passable: false), Tile.new, Tile.new(passable: false)]], - regen_coords: C[1, 1]) } - let!(:center) { map.regen_coords } + [Tile.new(passable: false), Tile.new, Tile.new(passable: false)]]) } + let!(:center) { C[1, 1] } let!(:passable) { Tile::DEFAULT_PASSABLE } let!(:impassable) { Tile::DEFAULT_IMPASSABLE } @@ -22,8 +21,7 @@ let!(:dragon) { Monster.new(stats: {attack: 50, agility: 10000}, battle_commands: [Attack.new(strength: 50)]) } let!(:chest_map) { Map.new(name: "Chest Map", - tiles: [[Tile.new(events: [Chest.new(gold: 5)]), Tile.new(events: [Chest.new(gold: 5)])]], - regen_coords: C[0, 0]) } + tiles: [[Tile.new(events: [Chest.new(gold: 5)]), Tile.new(events: [Chest.new(gold: 5)])]]) } context "constructor" do it "has the correct default parameters" do diff --git a/spec/goby/map/map_spec.rb b/spec/goby/map/map_spec.rb index e49d486..a8cdede 100644 --- a/spec/goby/map/map_spec.rb +++ b/spec/goby/map/map_spec.rb @@ -3,22 +3,19 @@ RSpec.describe Map do let(:lake) { Map.new(name: "Lake", - tiles: [ [ Tile.new, Tile.new(passable: false) ] ], - regen_coords: C[0,1]) } + tiles: [ [ Tile.new, Tile.new(passable: false) ] ] ) } context "constructor" do it "has the correct default parameters" do map = Map.new expect(map.name).to eq "Map" expect(map.tiles[0][0].passable).to be true - expect(map.regen_coords).to eq C[0,0] end it "correctly assigns custom parameters" do expect(lake.name).to eq "Lake" expect(lake.tiles[0][0].passable).to be true expect(lake.tiles[0][1].passable).to be false - expect(lake.regen_coords).to eq C[0,1] end end diff --git a/spec/goby/world_command_spec.rb b/spec/goby/world_command_spec.rb index a8b0d5e..23426e9 100644 --- a/spec/goby/world_command_spec.rb +++ b/spec/goby/world_command_spec.rb @@ -8,8 +8,7 @@ Tile.new(events: [NPC.new]), Tile.new(events: [Event.new(visible: false)]) ], [ Tile.new(events: [Shop.new, NPC.new]), - Tile.new(events: [Event.new(visible: false), Shop.new, NPC.new]) ] ], - regen_coords: C[0,0]) } + Tile.new(events: [Event.new(visible: false), Shop.new, NPC.new]) ] ] ) } let!(:player) { Player.new(stats: { max_hp: 10, hp: 3 }, inventory: [ C[Food.new(name: "Banana", recovers: 5), 1], From a78a0a4f6fddf87bd8f711439baf1f7ee15da321 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Mon, 15 Jan 2018 17:54:54 -0500 Subject: [PATCH 11/22] Set respawn tostart location for no specified respawn_location --- lib/goby/entity/player.rb | 4 +--- res/scaffold/simple/main.rb | 2 +- spec/goby/entity/player_spec.rb | 9 ++++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/goby/entity/player.rb b/lib/goby/entity/player.rb index 3cbb04d..4b5d8b4 100644 --- a/lib/goby/entity/player.rb +++ b/lib/goby/entity/player.rb @@ -46,7 +46,7 @@ def initialize(name: "Player", stats: {}, inventory: [], gold: 0, battle_command add_battle_commands(battle_commands) move_to(Location.new(new_map, new_coords)) - @respawn_location = respawn_location + @respawn_location = respawn_location || @location @saved_maps = Hash.new end @@ -123,9 +123,7 @@ def choose_item_and_on_whom(enemy) def die sleep(2) unless ENV['TEST'] - # TODO: No respawn location? Display 'GAME OVER' & end game. @location = @respawn_location - type("After being knocked out in battle,\n") type("you wake up in #{@location.map.name}.\n\n") diff --git a/res/scaffold/simple/main.rb b/res/scaffold/simple/main.rb index abc51d8..4cb26a1 100644 --- a/res/scaffold/simple/main.rb +++ b/res/scaffold/simple/main.rb @@ -30,7 +30,7 @@ # Use the Player constructor to set the # location, stats, gold, inventory, and more. - player = Player.new(location: home, respawn_location: home) + player = Player.new(location: home) end diff --git a/spec/goby/entity/player_spec.rb b/spec/goby/entity/player_spec.rb index bf98f2d..8b47cd8 100644 --- a/spec/goby/entity/player_spec.rb +++ b/spec/goby/entity/player_spec.rb @@ -38,7 +38,8 @@ expect(player.battle_commands).to eq Array.new expect(player.location.map).to eq Player::DEFAULT_MAP expect(player.location.coords).to eq Player::DEFAULT_COORDS - expect(player.respawn_location).to be_nil + expect(player.respawn_location.map).to eq Player::DEFAULT_MAP + expect(player.respawn_location.coords).to eq Player::DEFAULT_COORDS end it "correctly assigns custom parameters" do @@ -82,6 +83,12 @@ expect(hero.respawn_location.coords).to eq C[1, 2] end + it "sets respawn to start location for no respawn_location" do + player = Player.new(location: Location.new(map, C[1, 1])) + expect(player.respawn_location.map).to eq map + expect(player.respawn_location.coords).to eq C[1, 1] + end + context "places the player in the default map & location" do it "receives the nil map" do player = Player.new(location: Location.new(nil, C[2, 4])) From db8ab0f51240f5974b301299bc79121d18a6bc09 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Mon, 15 Jan 2018 18:15:22 -0500 Subject: [PATCH 12/22] Allow write access to Player's respawn location --- lib/goby/entity/player.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/goby/entity/player.rb b/lib/goby/entity/player.rb index 4b5d8b4..55314f2 100644 --- a/lib/goby/entity/player.rb +++ b/lib/goby/entity/player.rb @@ -293,8 +293,8 @@ def sample_gold gold_lost end - attr_reader :location, :respawn_location, :saved_maps - attr_accessor :moved + attr_reader :location, :saved_maps + attr_accessor :moved, :respawn_location end From bff3c42425a2e61d5ccf4e64d34a4716469a7eb5 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Mon, 15 Jan 2018 18:44:43 -0500 Subject: [PATCH 13/22] Use location setter so graphics work correctly --- lib/goby/entity/player.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/goby/entity/player.rb b/lib/goby/entity/player.rb index 55314f2..d0c27c9 100644 --- a/lib/goby/entity/player.rb +++ b/lib/goby/entity/player.rb @@ -123,7 +123,7 @@ def choose_item_and_on_whom(enemy) def die sleep(2) unless ENV['TEST'] - @location = @respawn_location + move_to(@respawn_location) type("After being knocked out in battle,\n") type("you wake up in #{@location.map.name}.\n\n") From 63bf73d2ca1a7f56d7afdd738ae1103728402c65 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Mon, 15 Jan 2018 18:52:12 -0500 Subject: [PATCH 14/22] Update newb to not respawn on monster's tile --- spec/goby/entity/player_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/goby/entity/player_spec.rb b/spec/goby/entity/player_spec.rb index 8b47cd8..51150bc 100644 --- a/spec/goby/entity/player_spec.rb +++ b/spec/goby/entity/player_spec.rb @@ -17,7 +17,7 @@ gold: 5000, treasures: [C[Item.new, 1]]) } let!(:newb) { Player.new(battle_commands: [Attack.new(success_rate: 0)], gold: 50, location: Location.new(map, center), - respawn_location: Location.new(map, C[1, 2])) } + respawn_location: Location.new(map, C[2, 1])) } let!(:dragon) { Monster.new(stats: {attack: 50, agility: 10000}, battle_commands: [Attack.new(strength: 50)]) } let!(:chest_map) { Map.new(name: "Chest Map", @@ -356,7 +356,7 @@ end # Newb should die and go to respawn location. expect(newb.gold).to eq 25 - expect(newb.location.coords).to eq C[1, 2] + expect(newb.location.coords).to eq C[2, 1] end it "should allow the stronger player to win as the attacker" do @@ -365,7 +365,7 @@ end # Weaker Player should die and go to respawn location. expect(newb.gold).to eq 25 - expect(newb.location.coords).to eq C[1, 2] + expect(newb.location.coords).to eq C[2, 1] # Stronger Player should get weaker Players gold expect(dude.gold).to eq (35) end @@ -376,7 +376,7 @@ end # Weaker Player should die and go to respawn location. expect(newb.gold).to eq 25 - expect(newb.location.coords).to eq C[1, 2] + expect(newb.location.coords).to eq C[2, 1] # Stronger Player should get weaker Players gold expect(dude.gold).to eq (35) end @@ -385,11 +385,11 @@ context "die" do it "moves the player back to the map's regen location" do - newb.move_down - expect(newb.location.coords).to eq C[2, 1] + newb.move_left + expect(newb.location.coords).to eq C[1, 0] newb.die expect(newb.location.map).to eq map - expect(newb.location.coords).to eq C[1, 2] + expect(newb.location.coords).to eq C[2, 1] end it "recovers the player's HP to max" do From 7613eec89b0c471127011fe09f6b78b04fd75a6d Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Mon, 15 Jan 2018 18:59:04 -0500 Subject: [PATCH 15/22] Update test description to refer to respawn location --- spec/goby/entity/player_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/goby/entity/player_spec.rb b/spec/goby/entity/player_spec.rb index 51150bc..f3c30d0 100644 --- a/spec/goby/entity/player_spec.rb +++ b/spec/goby/entity/player_spec.rb @@ -384,7 +384,7 @@ end context "die" do - it "moves the player back to the map's regen location" do + it "moves the player back to his/her respawn location" do newb.move_left expect(newb.location.coords).to eq C[1, 0] newb.die From 99cf695fc4d9f89e3dec8374e98360bd63170b49 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Tue, 23 Jan 2018 19:04:49 -0500 Subject: [PATCH 16/22] Remove extraneous newlines from shop interactions --- lib/goby/event/shop.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/goby/event/shop.rb b/lib/goby/event/shop.rb index 3b8a334..f49c43a 100644 --- a/lib/goby/event/shop.rb +++ b/lib/goby/event/shop.rb @@ -39,7 +39,7 @@ def buy(player) return if name.casecmp("none").zero? if index.nil? # non-existent item. - print "\nI don't have #{name}!\n\n" + print "I don't have #{name}!\n\n" return end @@ -48,7 +48,6 @@ def buy(player) print "How many do you want?: " amount_to_buy = player_input total_cost = amount_to_buy.to_i * item.price - print "\n" if total_cost > player.gold # not enough gold. puts "You don't have enough gold!" @@ -148,7 +147,6 @@ def sell(player) print "What would you like to sell? (or none): " input = player_input index = player.has_item(input) - print "\n" # The player does not want to sell an item. return if input.casecmp("none").zero? @@ -169,7 +167,6 @@ def sell(player) puts "I'll buy that for #{purchase_price(item)} gold." print "How many do you want to sell?: " amount_to_sell = player_input.to_i - print "\n" if amount_to_sell > item_count # more than in the inventory. print "You don't have that many to sell!\n\n" From af366ff9e28d35e3948a0c1c0a1f40d8b529902c Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Thu, 25 Jan 2018 17:49:22 -0500 Subject: [PATCH 17/22] Rewrite handle_victory as a 'virtual' function --- lib/goby/entity/fighter.rb | 19 ++++++++----------- lib/goby/entity/monster.rb | 11 ++++++++--- lib/goby/entity/player.rb | 19 ++++++++++--------- spec/goby/entity/monster_spec.rb | 2 +- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/lib/goby/entity/fighter.rb b/lib/goby/entity/fighter.rb index cc5ac9d..1f135fb 100644 --- a/lib/goby/entity/fighter.rb +++ b/lib/goby/entity/fighter.rb @@ -14,6 +14,14 @@ def die raise(NotImplementedError, 'A Fighter Entity must know how to die') end + # Handles how a Fighter behaves after winning a battle. + # Subclasses must override this function. + # + # @param [Fighter] fighter the Fighter who lost the battle. + def handle_victory(fighter) + raise(NotImplementedError, 'A Fighter Entity must know how to handle victory') + end + # The function that returns the treasure given by an Entity after losing a battle. # # @return [Item] the reward for the victor of the battle (or nil - no treasure). @@ -94,17 +102,6 @@ def choose_item_and_on_whom(enemy) return C[item, whom] end - # Handles how an Entity behaves after winning a battle. - # - # @param [Entity] entity the Entity who lost the battle. - def handle_victory(entity) - # Determine the rewards for defeating the entity. - gold = entity.sample_gold - treasure = entity.sample_treasures - - add_loot(gold, [treasure]) unless gold.nil? && treasure.nil? - end - # Returns the index of the specified command, if it exists. # # @param [BattleCommand, String] cmd the battle command (or its name). diff --git a/lib/goby/entity/monster.rb b/lib/goby/entity/monster.rb index 6593c1e..ff0d1f8 100644 --- a/lib/goby/entity/monster.rb +++ b/lib/goby/entity/monster.rb @@ -47,10 +47,15 @@ def clone return monster end - # What to do if the Monster dies in a Battle - # + # What to do if the Monster dies in a Battle. def die - #Do nothing special + # Do nothing special. + end + + # What to do if a Monster wins a Battle. + def handle_victory(fighter) + # Take some of the Player's gold. + fighter.sample_gold end # The amount gold given to a victorious Entity after losing a battle diff --git a/lib/goby/entity/player.rb b/lib/goby/entity/player.rb index d0c27c9..9cdac88 100644 --- a/lib/goby/entity/player.rb +++ b/lib/goby/entity/player.rb @@ -133,6 +133,16 @@ def die set_stats(hp: @stats[:max_hp]) end + # Retrieve loot obtained by defeating the enemy. + # + # @param [Fighter] fighter the Fighter who lost the battle. + def handle_victory(fighter) + type("#{@name} defeated the #{fighter.name}!\n") + gold = fighter.sample_gold + treasure = fighter.sample_treasures + add_loot(gold, [treasure]) unless gold.nil? && treasure.nil? + end + # Moves the player down. Increases 'y' coordinate by 1. def move_down down_tile = C[@location.coords.first + 1, @location.coords.second] @@ -262,15 +272,6 @@ def update_map(location = @location) end end - # How the Player behaves after winning a battle. - # - # @param [Entity] entity the Entity who lost the battle. - def handle_victory(entity) - type("You defeated the #{entity.name}!\n") - super(entity) - print "\n" - end - # The treasure given by a Player after losing a battle. # # @return [Item] the reward for the victor of the battle (or nil - no treasure). diff --git a/spec/goby/entity/monster_spec.rb b/spec/goby/entity/monster_spec.rb index df31435..e3a5a7b 100644 --- a/spec/goby/entity/monster_spec.rb +++ b/spec/goby/entity/monster_spec.rb @@ -124,7 +124,7 @@ wolf.battle(newb) end # The amount of gold the Monster had + that returned by the Player - expect(wolf.gold).to eq 35 + expect(newb.gold).to eq 25 end From 932937d8c7b4148933aad8293c0d30711c0fcd1e Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Thu, 25 Jan 2018 18:11:38 -0500 Subject: [PATCH 18/22] Require user input after showing battle loot --- lib/goby/entity/player.rb | 3 +++ spec/goby/entity/monster_spec.rb | 2 +- spec/goby/entity/player_spec.rb | 10 +++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/goby/entity/player.rb b/lib/goby/entity/player.rb index 9cdac88..2b45668 100644 --- a/lib/goby/entity/player.rb +++ b/lib/goby/entity/player.rb @@ -141,6 +141,9 @@ def handle_victory(fighter) gold = fighter.sample_gold treasure = fighter.sample_treasures add_loot(gold, [treasure]) unless gold.nil? && treasure.nil? + + type("Press enter to continue...") + player_input end # Moves the player down. Increases 'y' coordinate by 1. diff --git a/spec/goby/entity/monster_spec.rb b/spec/goby/entity/monster_spec.rb index e3a5a7b..17e65d6 100644 --- a/spec/goby/entity/monster_spec.rb +++ b/spec/goby/entity/monster_spec.rb @@ -129,7 +129,7 @@ it "should allow the player to win in this example" do - __stdin("attack\n") do + __stdin("attack\n", "\n") do slime.battle(dude) end expect(dude.inventory.size).to eq 1 diff --git a/spec/goby/entity/player_spec.rb b/spec/goby/entity/player_spec.rb index f3c30d0..578235c 100644 --- a/spec/goby/entity/player_spec.rb +++ b/spec/goby/entity/player_spec.rb @@ -216,7 +216,7 @@ context "move right" do it "correctly moves the player to a passable tile" do 20.times do - __stdin("Attack\n") do + __stdin("Attack\n", "\n") do dude.move_right expect(dude.location.map).to eq map expect(dude.location.coords).to eq C[1, 2] @@ -228,7 +228,7 @@ end it "prevents the player from moving on a nonexistent tile" do - __stdin("Attack\n") do + __stdin("Attack\n", "\n") do dude.move_right; dude.move_right expect(dude.location.map).to eq map expect(dude.location.coords).to eq C[1, 2] @@ -336,7 +336,7 @@ context "battle" do it "should allow the player to win in this example" do - __stdin("attack\n") do + __stdin("attack\n", "\n") do dude.battle(slime) end expect(dude.inventory.size).to eq 1 @@ -360,7 +360,7 @@ end it "should allow the stronger player to win as the attacker" do - __stdin("attack\nattack\n") do + __stdin("attack\nattack\n", "\n") do dude.battle(newb) end # Weaker Player should die and go to respawn location. @@ -371,7 +371,7 @@ end it "should allow the stronger player to win as the defender" do - __stdin("attack\nattack\n") do + __stdin("attack\nattack\n", "\n") do newb.battle(dude) end # Weaker Player should die and go to respawn location. From bb69685320181f5bb4e58ce6769dcd3436bb132d Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Thu, 25 Jan 2018 18:16:00 -0500 Subject: [PATCH 19/22] Write test for Fighter without handle_victory impl. --- spec/goby/entity/fighter_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/goby/entity/fighter_spec.rb b/spec/goby/entity/fighter_spec.rb index 9c57e56..5b86d7f 100644 --- a/spec/goby/entity/fighter_spec.rb +++ b/spec/goby/entity/fighter_spec.rb @@ -25,6 +25,10 @@ def initialize(name: "Fighter", stats: {}, inventory: [], gold: 0, battle_comman expect { empty_fighter.die }.to raise_error(NotImplementedError, 'A Fighter Entity must know how to die') end + it "forces :handle_victory to be implemented" do + expect { empty_fighter.handle_victory(fighter) }.to raise_error(NotImplementedError, 'A Fighter Entity must know how to handle victory') + end + it "forces :sample_treasures to be implemented" do expect { empty_fighter.sample_treasures }.to raise_error(NotImplementedError, 'A Fighter Entity must know whether it returns treasure or not after losing a battle') end From ad8aef644490ec5e914f51e7466f791afda78188 Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Fri, 26 Jan 2018 18:54:59 -0500 Subject: [PATCH 20/22] Complete 100% docs w/o errors; misc. cleanup --- lib/goby/battle/battle.rb | 1 + lib/goby/entity/fighter.rb | 43 ++++++++++++++++---------------- lib/goby/event/npc.rb | 4 +-- lib/goby/extension.rb | 1 + lib/goby/util.rb | 10 +++++--- spec/goby/entity/fighter_spec.rb | 10 ++++---- 6 files changed, 37 insertions(+), 32 deletions(-) diff --git a/lib/goby/battle/battle.rb b/lib/goby/battle/battle.rb index c2b0ff6..fb60a94 100644 --- a/lib/goby/battle/battle.rb +++ b/lib/goby/battle/battle.rb @@ -2,6 +2,7 @@ module Goby + # Representation of a fight between two Fighters. class Battle # @param [Entity] entity_a the first entity in the battle diff --git a/lib/goby/entity/fighter.rb b/lib/goby/entity/fighter.rb index 1f135fb..0dca888 100644 --- a/lib/goby/entity/fighter.rb +++ b/lib/goby/entity/fighter.rb @@ -2,16 +2,17 @@ module Goby + # Methods and variables for something that can battle with another Fighter. module Fighter - class UnfightableEntityException < Exception + # Exception thrown when a non-Fighter tries to enter battle. + class UnfightableException < Exception end - # The function that handles how an Entity behaves after losing a battle. + # The function that handles how an Fighter behaves after losing a battle. # Subclasses must override this function. - # def die - raise(NotImplementedError, 'A Fighter Entity must know how to die') + raise(NotImplementedError, 'A Fighter must know how to die.') end # Handles how a Fighter behaves after winning a battle. @@ -19,24 +20,24 @@ def die # # @param [Fighter] fighter the Fighter who lost the battle. def handle_victory(fighter) - raise(NotImplementedError, 'A Fighter Entity must know how to handle victory') + raise(NotImplementedError, 'A Fighter must know how to handle victory.') end - # The function that returns the treasure given by an Entity after losing a battle. + # The function that returns the treasure given by a Fighter after losing a battle. # # @return [Item] the reward for the victor of the battle (or nil - no treasure). def sample_treasures - raise(NotImplementedError, 'A Fighter Entity must know whether it returns treasure or not after losing a battle') + raise(NotImplementedError, 'A Fighter must know whether it returns treasure or not after losing a battle.') end - # The function that returns the gold given by an Entity after losing a battle. + # The function that returns the gold given by a Fighter after losing a battle. # - # @return[Integer] the amount of gold to award the victorious Entity + # @return[Integer] the amount of gold to award the victorious Fighter def sample_gold - raise(NotImplementedError, 'A Fighter Entity must return some gold after losing a battle') + raise(NotImplementedError, 'A Fighter must return some gold after losing a battle.') end - # Adds the specified battle command to the entity's collection. + # Adds the specified battle command to the Fighter's collection. # # @param [BattleCommand] command the command being added. def add_battle_command(command) @@ -46,19 +47,19 @@ def add_battle_command(command) battle_commands.sort! { |x, y| x.name <=> y.name } end - # Adds the specified battle commands to the entity's collection. + # Adds the specified battle commands to the Fighter's collection. # # @param [Array] battle_commands the commands being added. def add_battle_commands(battle_commands) battle_commands.each { |command| add_battle_command(command) } end - # Engages in battle with the specified entity. + # Engages in battle with the specified Entity. # # @param [Entity] entity the opponent of the battle. def battle(entity) unless entity.class.included_modules.include?(Fighter) - raise(UnfightableEntityException, "You can't start a battle with an Entity of type #{entity.class} as it doesn't implement the Fighter module") + raise(UnfightableException, "You can't start a battle with an Entity of type #{entity.class} as it doesn't implement the Fighter module") end system("clear") unless ENV['TEST'] @@ -83,7 +84,7 @@ def battle_commands @battle_commands end - # Determines how the entity should select an attack in battle. + # Determines how the Fighter should select an attack in battle. # Override this method for control over this functionality. # # @return [BattleCommand] the chosen battle command. @@ -91,11 +92,11 @@ def choose_attack battle_commands[Random.rand(@battle_commands.length)] end - # Determines how the entity should select the item and on whom + # Determines how the Fighter should select the item and on whom # during battle (Use command). Return nil on error. # - # @param [Entity] enemy the opponent in battle. - # @return [C(Item, Entity)] the item and on whom it is to be used. + # @param [Fighter] enemy the opponent in battle. + # @return [C(Item, Fighter)] the item and on whom it is to be used. def choose_item_and_on_whom(enemy) item = @inventory[Random.rand(@inventory.length)].first whom = [self, enemy].sample @@ -113,7 +114,7 @@ def has_battle_command(cmd) return end - # Removes the battle command, if it exists, from the entity's collection. + # Removes the battle command, if it exists, from the Fighter's collection. # # @param [BattleCommand, String] command the command being removed. def remove_battle_command(command) @@ -132,7 +133,7 @@ def print_battle_commands(header = "Battle Commands:") print "\n" end - # Appends battle commands to the end of the Entity print_status output. + # Appends battle commands to the end of the Fighter print_status output. def print_status super print_battle_commands unless battle_commands.empty? @@ -140,7 +141,7 @@ def print_status # Uses the agility levels of the two Fighters to determine who should go first. # - # @param [Entity] fighter the opponent with whom the calling Fighter is competing. + # @param [Fighter] fighter the opponent with whom the calling Fighter is competing. # @return [Boolean] true when calling Fighter should go first. Otherwise, false. def sample_agilities(fighter) sum = fighter.stats[:agility] + stats[:agility] diff --git a/lib/goby/event/npc.rb b/lib/goby/event/npc.rb index 3279d18..081f45d 100644 --- a/lib/goby/event/npc.rb +++ b/lib/goby/event/npc.rb @@ -22,9 +22,9 @@ def run(player) say "Hello!\n\n" end - # Function that allows NPCs to output a string of words + # Function that allows NPCs to output a string of words. # - # @param [String] string of words to be ouput + # @param [String] words string of words for the NPC to speak. def say(words) type "#{name}: #{words}" end diff --git a/lib/goby/extension.rb b/lib/goby/extension.rb index 466f703..9f3cf9e 100644 --- a/lib/goby/extension.rb +++ b/lib/goby/extension.rb @@ -2,6 +2,7 @@ # Provides additional methods for Array. class Array + # Returns true iff the array is not empty. def nonempty? return !empty? end diff --git a/lib/goby/util.rb b/lib/goby/util.rb index 2000553..0874dd6 100644 --- a/lib/goby/util.rb +++ b/lib/goby/util.rb @@ -1,6 +1,8 @@ require 'readline' require 'yaml' +# Collection of classes, modules, and functions that make +# up the Goby framework. module Goby # Stores a pair of values as a couple. @@ -10,8 +12,8 @@ class C # # @param [Object] first the first object in the pair. # @param [Object] second the second object in the pair. - def self.[](a, b) - C.new(a, b) + def self.[](first, second) + C.new(first, second) end # @param [Object] first the first object in the pair. @@ -29,14 +31,14 @@ def ==(rhs) attr_accessor :first, :second end - # The combination of a map and some coordinates, + # The combination of a map and y-x coordinates, # which determine a specific position/location on the map. class Location # Location constructor. # # @param [Map] map the map component. - # @param [C(Integer, Integer)] + # @param [C(Integer, Integer)] coords a pair of y-x coordinates. def initialize(map, coords) @map = map @coords = coords diff --git a/spec/goby/entity/fighter_spec.rb b/spec/goby/entity/fighter_spec.rb index 5b86d7f..6ef0942 100644 --- a/spec/goby/entity/fighter_spec.rb +++ b/spec/goby/entity/fighter_spec.rb @@ -22,19 +22,19 @@ def initialize(name: "Fighter", stats: {}, inventory: [], gold: 0, battle_comman context "placeholder methods" do it "forces :die to be implemented" do - expect { empty_fighter.die }.to raise_error(NotImplementedError, 'A Fighter Entity must know how to die') + expect { empty_fighter.die }.to raise_error(NotImplementedError, 'A Fighter must know how to die.') end it "forces :handle_victory to be implemented" do - expect { empty_fighter.handle_victory(fighter) }.to raise_error(NotImplementedError, 'A Fighter Entity must know how to handle victory') + expect { empty_fighter.handle_victory(fighter) }.to raise_error(NotImplementedError, 'A Fighter must know how to handle victory.') end it "forces :sample_treasures to be implemented" do - expect { empty_fighter.sample_treasures }.to raise_error(NotImplementedError, 'A Fighter Entity must know whether it returns treasure or not after losing a battle') + expect { empty_fighter.sample_treasures }.to raise_error(NotImplementedError, 'A Fighter must know whether it returns treasure or not after losing a battle.') end it "forces :sample_gold to be implemented" do - expect { empty_fighter.sample_gold }.to raise_error(NotImplementedError, 'A Fighter Entity must return some gold after losing a battle') + expect { empty_fighter.sample_gold }.to raise_error(NotImplementedError, 'A Fighter must return some gold after losing a battle.') end end @@ -59,7 +59,7 @@ def initialize(name: "Fighter", stats: {}, inventory: [], gold: 0, battle_comman context "battle" do it "raises an error when starting a battle against a non-Fighter Entity" do - expect {empty_fighter.battle(Class.new)}.to raise_error(Fighter::UnfightableEntityException, + expect {empty_fighter.battle(Class.new)}.to raise_error(Fighter::UnfightableException, "You can't start a battle with an Entity of type Class as it doesn't implement the Fighter module") end end From 5f940b5d90527f5ca49feccca970392ed64bb41f Mon Sep 17 00:00:00 2001 From: Nicholas Date: Sat, 27 Jan 2018 00:28:06 +0000 Subject: [PATCH 21/22] Update README.md for 0.2.0 release --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 47ef5e5..0b92f8f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Goby [![Build Status](https://travis-ci.org/nskins/goby.png)](https://travis-ci.org/nskins/goby) [![Coverage Status](https://coveralls.io/repos/github/nskins/goby/badge.svg?branch=master)](https://coveralls.io/github/nskins/goby?branch=master) -Goby is a Ruby framework for creating [text](https://en.wikipedia.org/wiki/Text-based_game) [RPGs](https://en.wikipedia.org/wiki/Role-playing_game). In the past 30+ years, the fine and respectable craft of text RPG development has been forgotten in favor of flashing lights and CGI cutscenes; however, there was once a time when the video game experience meant more than Dynamic Super Resolution™. +Goby is a Ruby framework for creating [text](https://en.wikipedia.org/wiki/Text-based_game) [RPGs](https://en.wikipedia.org/wiki/Role-playing_game). Goby comes with out-of-the-box support for 2D map development, background music, PvP/PvM battles, customizable items & events, stats, equipment, and so much more. With thorough testing and documentation, it's even easy to expand upon the framework for special, unique features. If you are looking to create the next classic command-line RPG, then look no further! -Text RPG development is a relatively fast and simple process. That's why our mission is to provide the highest degree of functionality, utilizing object-oriented design principles, so that users spend the majority of their time on the *fun* part of video game development: content creation! +Goby will always be free and open source software. If you have any questions, please contact the owner at nskins@umich.edu. He will be happy to assist you. ## Getting Started -Tutorials and examples are **coming soon**. Complete documentation is available. In order to start using Goby in your application, follow these instructions: +In order to start using Goby in your application, follow these instructions: Add this line to your application's Gemfile: @@ -16,7 +16,7 @@ gem 'goby' And then execute: - $ bundle + $ bundle install Or install it yourself as: @@ -30,4 +30,4 @@ Thank you for your interest in contributing! Please read our [guidelines](https: We use [YARD](https://github.com/lsegal/yard) for documentation. In order to generate the documentation (which will be stored in the doc/ directory), run the following command in the project's root directory: -```yardoc ``` + $ yardoc From ad4e7cface6b158d58b4a0554339e38328d4c11b Mon Sep 17 00:00:00 2001 From: Nicholas Skinsacos Date: Sat, 27 Jan 2018 11:38:22 -0500 Subject: [PATCH 22/22] Update Goby version to 0.2.0 --- goby.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goby.gemspec b/goby.gemspec index 460cdfc..ed65d59 100644 --- a/goby.gemspec +++ b/goby.gemspec @@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) Gem::Specification.new do |spec| spec.name = "goby" - spec.version = "0.1.0" + spec.version = "0.2.0" spec.authors = ["Nicholas Skinsacos"] spec.email = ["nskins@umich.edu"]