diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..aa7d490 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1377554 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..5483e25 --- /dev/null +++ b/Gemfile @@ -0,0 +1,7 @@ +# A sample Gemfile +source "https://rubygems.org" + +# gem "rails" +gem "pry" +gem "pry-stack_explorer" +gem "colorize" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..6107556 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,23 @@ +GEM + remote: https://rubygems.org/ + specs: + binding_of_caller (0.7.2) + debug_inspector (>= 0.0.1) + coderay (1.1.0) + debug_inspector (0.0.2) + method_source (0.8.2) + pry (0.10.1) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + pry-stack_explorer (0.4.9.1) + binding_of_caller (>= 0.7) + pry (>= 0.9.11) + slop (3.6.0) + +PLATFORMS + ruby + +DEPENDENCIES + pry + pry-stack_explorer diff --git a/Jays-Game/controller.rb b/Jays-Game/controller.rb deleted file mode 100644 index c043b14..0000000 --- a/Jays-Game/controller.rb +++ /dev/null @@ -1,37 +0,0 @@ -require_relative 'view' -require_relative 'model' - -class GameController - include GameView - - def run! - todoList = List.new - - Print::run_spinner - Print::title_screen - - #Make todoList methods like RESTful endpoints (new/edit/update/delete) - #Think Backbone Model & View - - loop do - Print::menu - case Print::fetch_user_input - when "V" - Print::print_list(todoList.todos) - when "A" - todoList.add_todo(Print::serialize_todo) - when "C" - todoList.complete_todo(Print::completed_id.to_i) - when "D" - todoList.delete_todo(Print::deleted_id.to_i) - when "Q" - puts "We're done" - exit - else - Print::error_message - end - end - end -end - -GameController.new.run! diff --git a/Jays-Game/model.rb b/Jays-Game/model.rb deleted file mode 100644 index 4708f62..0000000 --- a/Jays-Game/model.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'faker' -require 'pry' - -class Todo - attr_reader :id, :title, :description, :completed - - def initialize args - @id = args[:id] - @title = args[:title] - @description = args[:description] - @completed = false - end - - def mark_completed - @completed = true - end - - def completed? - @completed - end -end - -class List - attr_reader :todos - - def initialize - @primary_id = 0 - @todos = [] - populate_dummy_todos - end - - def add_todo(input) - @todos << Todo.new(input.merge(fetch_id)) - end - - def complete_todo(id) - completed_item = @todos.select { |todo| todo.id == id } - fail "No item matching that id" unless completed_item - completed_item[0].mark_completed - end - - def delete_todo(id) - @todos.delete_if { |n| n.id == id } - end - - def populate_dummy_todos - 5.times do - add_todo(title: Faker::Lorem.word, description: Faker::Lorem.sentence) - end - end - - private - - def fetch_id - {id: @primary_id += 1 } - end -end diff --git a/Jays-Game/view.rb b/Jays-Game/view.rb deleted file mode 100644 index e90ae7c..0000000 --- a/Jays-Game/view.rb +++ /dev/null @@ -1,83 +0,0 @@ -module GameView - - module Print - - class << self - def run_spinner - print "Loading (please wait) " - 5.times { print "."; sleep 1; } - print "\n" - end - - def error_message - puts "That's not a command key. Try again!" - end - - def title_screen -title = < " - gets.chomp - end - end - end -end diff --git a/Roberts-Game/Command.rb b/Roberts-Game/Command.rb new file mode 100644 index 0000000..6119222 --- /dev/null +++ b/Roberts-Game/Command.rb @@ -0,0 +1,286 @@ + +=begin + +NOTE TO SELF - move string formatting stuff to view class/module +maybe make Message class and subclasses and let view sort it out? + +=end + +class Command + def self.parse sentence + rejections = %w(a an the some) + words = sentence.split.map(&:downcase).reject { |word| rejections.member? word }.map(&:to_sym) + + return UnknownCommand.new if words.empty? + + case words[0] + when :exit, :quit + return ExitCommand.new + when :help + return HelpCommand.new + when :look + return LookAroundCommand.new if words.length == 1 + if words[1] == :at && words.length > 2 + item = Assets::get_item words[2..-1].join('_').to_sym + return DontSeeCommand.new words[2..-1].join(' ') if item.nil? + return LookAtCommand.new item + end + when :go + return UnknownCommand.new if words.length != 2 + case words[1] + when :north, :south, :east, :west, :up, :down + return GoCommand.new words[1] + end + when :unlock + return UnknownCommand.new if words.length == 1 + with_index = words.index :with + return UnknownCommand.new if with_index.nil? + + words_a = words[1...with_index] + sym_a = words_a.join('_').to_sym + item_a = Assets::get_item sym_a + return DontSeeCommand.new words_a.join(' ') if item_a.nil? + + words_b = words[with_index + 1..-1] + sym_b = words_b.join('_').to_sym + item_b = Assets::get_item sym_b + return DontSeeCommand.new words_b.join(' ') if item_b.nil? + + return UnlockWithCommand.new item_a, item_b + when :take + return UnknownCommand.new if words.length == 1 + item = Assets::get_item words[1..-1].join('_').to_sym + return DontSeeCommand.new words[1..-1].join(' ') if item.nil? + return TakeCommand.new item + when :eat + return UnknownCommand.new if words.length == 1 + item = Assets::get_item words[1..-1].join('_').to_sym + return DontSeeCommand.new words[1..-1].join(' ') if item.nil? + return DisplayMessageCommand.new "You can't eat #{item.determined_name}." if !item.is_food + return ConsumeCommand.new item, "eat" + when :equip + return UnknownCommand.new if words.length == 1 + item = Assets::get_item words[1..-1].join('_').to_sym + return DontSeeCommand.new words[1..-1].join(' ') if item.nil? + return DisplayMessageCommand.new "You can't equip #{item.determined_name}." if !item.is_weapon && !item.is_armor + return EquipCommand.new item + when :read + return UnknownCommand.new if words.length == 1 + item = Assets::get_item words[1..-1].join('_').to_sym + return DontSeeCommand.new words[1..-1].join(' ') if item.nil? + return DisplayMessageCommand.new "You can't read #{item.determined_name}." if !item.text + return ReadCommand.new item + when :attack + return UnknownCommand.new if words.length == 1 + entity = Assets::get_entity words[1..-1].join('_').to_sym + return DontSeeCommand.new words[1..-1].join(' ') if entity.nil? + return AttackCommand.new entity + when :make + return MakePlayerGodlyCommand.new(true) if words[1] == :me && words[2] == :godly + return MakePlayerGodlyCommand.new(false) if words[1] == :me && words[2] == :normal + return UnknownCommand.new + end + + return UnknownCommand.new + end + + def run player + end +end + +class UnknownCommand < Command; end +class ExitCommand < Command; end +class HelpCommand < Command; end +class LookAroundCommand < Command; end + +class GoCommand < Command + def initialize direction + @direction = direction + end + + def run player + player.go @direction + end +end + +class UnlockWithCommand < Command + def initialize item_a, item_b + @item_a = item_a + @item_b = item_b + end + + def run player + if !player.can_reach_item? @item_a + DontSeeCommand.new(@item_a).run player + return + end + + if !player.can_reach_item? @item_b + DontSeeCommand.new(@item_b).run player + return + end + + if @item_a.unlock_with @item_b + player.notify "You have unlocked the #{@item_a.name} with the #{@item_b.name}." + player.use_up @item_b + else + player.notify "The #{@item_b.name} does not unlock the #{@item_a.name}" + end + end +end + +class DisplayMessageCommand + def initialize message + @message = message + end + + def run player + player.notify @message + end +end + +class TakeCommand + def initialize item + @item = item + end + + def run player + if !@item.takeable + player.notify "You can't take #{@item.determined_name}." + return + end + if !player.location.contains? @item + DontSeeCommand.new(@item).run player + return + end + player.location.delete_item @item + player.hold @item + player.notify "You took the #{@item.name}." + end +end + +class DontSeeCommand + def initialize thing + @thing = thing + end + + def run player + case @thing + when String + name = "any \"#{@thing}\"" + when Item, Entity + name = @thing.determined_name + else + name = "that" + end + + player.notify "I don't see #{name} near here." + end +end + +class LookAtCommand + def initialize item + @item = item + end + + def run player + contents = @item.contains.map { |inner_item| inner_item.determined_name }.join ", " + contents = "Inside you see: " + contents if !contents.empty? + player.notify "You look at the #{@item.name}. #{@item.description} #{contents}" + end +end + +class ConsumeCommand + def initialize item, verb + @item = item + @verb = verb + end + + def run player + player.notify "You #{@verb} the #{@item.name}." + player.heal @item.hp_bonus if !@item.hp_bonus.nil? + player.use_up @item + end +end + +class EquipCommand + def initialize item + @item = item + end + + def run player + player.equip @item + end +end + +class AttackCommand + def initialize entity + @entity = entity + end + + def run player + if !player.location.entities.member? @entity + DontSeeCommand.new(@entity).run player + return + end + + if player.weapon + weapon_name = player.weapon.name + damage = player.weapon + elsif + weapon_name = "bare hands" + damage = player.godly? ? 10000 : 1 + end + damage_dealt = @entity.take_damage damage + player.notify "You attack the #{@entity.name} with your #{weapon_name} for #{damage_dealt} points." + if @entity.dead? + player.location.delete_entity @entity + player.notify "You have defeated #{@entity.determined_name}!" + if @entity == Assets::get_entity(:storm_demon) + player.won = true + elsif !!@entity.drops + player.location.items[:ground] = @entity.drops + player.notify "It dropped #{@entity.drops.determined_name}." + end + end + end +end + +class ReadCommand + def initialize item + @item = item + end + + def run player + text = "\"#{@item.text}\"" + text = jumble text if !@item.easy_to_read && player.location != Assets::get_location(:study) + player.notify "The #{@item.name} reads: #{text}" + end + + private + + def jumble text + result = "" + text.each_char do |char| + if /[[:upper:]]/.match(char) + result << [*'A'..'Z'].sample + elsif /[[:lower:]]/.match(char) + result << [*'a'..'z'].sample + else + result << char + end + end + result << " (You are not clever enough to decipher this message.)" + return result + end +end + +class MakePlayerGodlyCommand + def initialize value + @value = value + end + + def run player + player.godly = @value + end +end diff --git a/Roberts-Game/controller.rb b/Roberts-Game/controller.rb new file mode 100644 index 0000000..3a2dd91 --- /dev/null +++ b/Roberts-Game/controller.rb @@ -0,0 +1,53 @@ +require_relative 'view' +require_relative 'models/Player' +require_relative 'Command' + +class GameController + include GameView + + def initialize + @player = Player.new + end + + def run! + Print::title_screen + + Print::look_around_message @player + + loop do + command = Print::fetch_command + + case command + when ExitCommand + Print::exit_message + exit + when HelpCommand + Print::help_message + next + when UnknownCommand + Print::idk_message + next + else + command.run @player + command = LookAroundCommand.new + end + + if @player.won? + Print::win_message + exit + end + + @player.do_environmental_effect + @player.location.tick @player + + if @player.dead? + Print::death_message @player + exit + end + + Print::look_around_message @player + end + end +end + +GameController.new.run! diff --git a/Roberts-Game/data/assets.rb b/Roberts-Game/data/assets.rb new file mode 100644 index 0000000..0f5661c --- /dev/null +++ b/Roberts-Game/data/assets.rb @@ -0,0 +1,113 @@ + +require 'yaml' + +require_relative "../models/EnvironmentalEffect" +require_relative "../models/Item" +require_relative "../models/Location" +require_relative "../models/Entity" + +require_relative "proxy/EnvironmentalEffectProxy" +require_relative "proxy/ItemProxy" +require_relative "proxy/LocationProxy" +require_relative "proxy/EntityProxy" + +module Assets + @instanced = nil + @raw = nil + + def self.get_location name + check_loaded + return nil if @raw[:locations][name].nil? + @instanced[:locations][name] = LocationProxy.new @raw[:locations][name] if @instanced[:locations][name].nil? + return @instanced[:locations][name] + end + + def self.get_environmental_effect name + check_loaded + return nil if @raw[:environmental_effects][name].nil? + @instanced[:environmental_effects][name] = EnvironmentalEffectProxy.new @raw[:environmental_effects][name] if @instanced[:environmental_effects][name].nil? + return @instanced[:environmental_effects][name] + end + + def self.get_item name + check_loaded + return nil if @raw[:items][name].nil? + @instanced[:items][name] = ItemProxy.new name, @raw[:items][name] if @instanced[:items][name].nil? + return @instanced[:items][name] + end + + def self.get_entity name + check_loaded + return nil if @raw[:entities][name].nil? + @instanced[:entities][name] = EntityProxy.new name, @raw[:entities][name] if @instanced[:entities][name].nil? + return @instanced[:entities][name] + end + + def self.get_string name + check_loaded + @raw[:strings][name] + end + + private + + def self.check_loaded + load if !@raw + end + + def self.load + working_dir = File.dirname(File.expand_path(__FILE__)) + @raw = { + locations: YAML::load_file(File.join(working_dir, 'locations.yml')), + environmental_effects: YAML::load_file(File.join(working_dir, 'environmental_effects.yml')), + items: YAML::load_file(File.join(working_dir, 'items.yml')), + strings: YAML::load_file(File.join(working_dir, 'strings.yml')), + entities: YAML::load_file(File.join(working_dir, 'entities.yml')) + } + + @raw[:items] = symbolize @raw[:items], :unlocks_with, :contains + @raw[:entities] = symbolize @raw[:entities], :drops, :weakness + @raw[:environmental_effects] = symbolize @raw[:environmental_effects] + @raw[:locations] = symbolize @raw[:locations], :environmental_effect, :paths, :items, :entities + @raw[:strings] = symbolize @raw[:strings] + + @instanced = { + locations: {}, + environmental_effects: {}, + items: {}, + entities: {} + } + end + + def self.symbolize dataset, *symbols + result = {} + dataset.each do |data_key, data| + data_key = data_key.to_sym + if data.is_a? String + result[data_key] = data + else + new_data = {} + data.each do |key, value| + key = key.to_sym + + if symbols.member? key + case value + when Array + value.map! { |val| val.to_sym } + when Hash + new_value = {} + value.each { |k,v| new_value[k.to_sym] = v.to_sym } + value = new_value + when NilClass + # don't attempt to modify + else + value = value.to_sym + end + end + new_data[key] = value + end + result[data_key] = new_data + end + end + return result + end +end diff --git a/Roberts-Game/data/entities.yml b/Roberts-Game/data/entities.yml new file mode 100644 index 0000000..1ba7251 --- /dev/null +++ b/Roberts-Game/data/entities.yml @@ -0,0 +1,40 @@ + +skeleton: + description: It's laughing at you. It sounds insane. + health: 6 + drops: red_book + damage: + - 0 + - 0 + - 0 + - 1 + - 2 + +wraith: + description: It looks frightening. It's ghostly image lingers in your mind after you look away. + health: 15 + weakness: blue_book + drops: lens + damage: + - 1 + - 2 + - 3 + +hell_hound: + description: It's a large black dog with a slobbering maw full of razor sharp teeth and glowing red eyes. + health: 50 + weakness: towel_rod + drops: hounds_tooth + damage: + - 3 + - 4 + - 5 + +storm_demon: + description: It must be what is causing the storm prevening you from going home. + health: 100 + weakness: hounds_tooth + damage: + - 5 + - 6 + - 7 diff --git a/Roberts-Game/data/environmental_effects.yml b/Roberts-Game/data/environmental_effects.yml new file mode 100644 index 0000000..b32bcff --- /dev/null +++ b/Roberts-Game/data/environmental_effects.yml @@ -0,0 +1,63 @@ + +storm: + ambient_description: A storm has been raging for hours. You need shelter. + damage_description: The storm takes it's toll on your health. + damage: + - 0 + - 0 + - 0 + - 1 + +dark: + ambient_description: It's dark. + damage_description: You tripped over something in the dark. + damage: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 1 + +smart: + ambient_description: You feel smarter for some reason. + damage_description: All this thinking makes your head hurt. + damage: + - 0 + - 0 + - 0 + - 0 + - 1 + +cold: + ambient_description: It's cold down here. + damage_description: You shiver through a cold gust. + damage: + - 0 + - 0 + - 1 + +scary: + ambient_description: It's pretty scary in here. + damage_description: Your fear overtakes you. + damage: + - 0 + - 1 + - 2 + +very_scary: + ambient_description: You are frightened. + damage_description: Your fear overtakes you. + damage: + - 1 + - 2 + - 3 + +etheral: + ambient_description: Pure thoughts dance inside your head. + damage_description: (blablabla) + damage: + - 0 diff --git a/Roberts-Game/data/items.yml b/Roberts-Game/data/items.yml new file mode 100644 index 0000000..1fe2381 --- /dev/null +++ b/Roberts-Game/data/items.yml @@ -0,0 +1,200 @@ + +rusty_gate_key: + description: It's a key for the front gate. How convenient! It won't always be this easy. Welcome to Hellhouse ;) + takeable: true + +rusty_gate: + description: It looks old. + is_door: true + locked: true + unlocks_with: rusty_gate_key + +unkempt_bush: + description: It hasn't been trimmed in decades. + contains: + - front_door_key + +front_door_key: + description: It has a label that says "front door." + takeable: true + +front_door: + description: It's large and sturdy looking. It's actually kind of ominous. + is_door: true + locked: true + unlocks_with: front_door_key + +apple: + description: It's bright red apple. You wonder how it hasn't decayed. + determiner: an + takeable: true + is_food: true + hp_bonus: 3 + +orange: + description: It's big, round, and orange. You wonder how it hasn't decayed. + determiner: an + takeable: true + is_food: true + hp_bonus: 3 + +pizza: + description: It smells delicious! It's still good. The pepperoni must have been cured well. + takeable: true + is_food: true + hp_bonus: 10 + +turkey: + description: It looks perfectly moist, as if it has just been cooked. The smell recalls memories of better days; the days before the accident. + takeable: true + is_food: true + hp_bonus: 10 + +knife: + description: It's very sharp. Someone could really get hurt if they aren't careful. + takeable: true + is_weapon: true + weapon_damage: 2 + +drawer: + description: It slides out easily. + contains: + - knife + +oven: + description: It's old, but big enough to cook a large amount of food. The original owners must have liked to entertain. + contains: + - pizza + - turkey + +countertop: + description: It's covered in dust. + contains: + - apple + - orange + - oven + - drawer + +red_book: + description: The text is in a language you don't recognize. The binding is of a very high quality red leather. + takeable: true + text: Locked doors need keys. Keys unlock doors. This knowledge is key. + +blue_book: + description: It's a book about banishing evil spirits. + takeable: true + is_weapon: true + weapon_damage: 1 + text: Evil is bad. Good is good. This knowledge is the greatest weapon against a wraith. + +oak_bookshelf: + description: It's full of books with red covers. + is_door: true + unlocks_with: red_book + +walnut_bookshelf: + description: It's full of books with blue covers. + contains: + - blue_book + +first_hint: + description: Read this if you are stuck. + determiner: the + text: Look at the rusty gate key. + takeable: true + easy_to_read: true + +second_hint: + description: Read this if you are stuck. + determiner: the + text: There is a secret passage to the dungeon somewhere. + takeable: true + +third_hint: + description: Read this if you are stuck. + determiner: the + text: You can't rely on your sense of direction down here. + takeable: true + +fourth_hint: + description: Read this if you are stuck. + determiner: the + text: Are doors and windows really all that different? + takeable: true + +licorice: + description: It's kind of stale, but oh well. + takeable: true + is_food: true + hp_bonus: 8 + +heavenly_hammer: + description: It's a hammer made from the ether itself. + takeable: true + +picture_window: + description: It's a stained glass mural depicting a demonic scene. + is_door: true + unlocks_with: heavenly_hammer + locked: true + +lens: + description: It's a small, glass lens. You're not sure what it fits into. + takeable: true + +telescope: + description: It's a very nice telescope. It's pointed at the sky but it doesn't seem to move. + is_door: true + locked: true + unlocks_with: lens + +bathroom_key: + description: It's a key for the bathroom. + takeable: true + +bathroom_door: + description: It's an ornate wooden door. + is_door: true + locked: true + unlocks_with: bathroom_key + +egg_sandwich: + description: It's smells weird but it looks ok. + is_food: true + takeable: true + hp_bonus: 7 + +towel_rod: + description: It's made of iron. + is_weapon: true + weapon_damage: 3 + takeable: true + +chainmail: + description: It's made of sturdy steel rings. + is_armor: true + damage_protection: 4 + takeable: true + determiner: some + +wardrobe: + description: It's is expertly carved from fine wood. + contains: + - chainmail + +bedroom_door: + description: It's an ornate wooden door. + is_door: true + locked: true + unlocks_with: bedroom_key + +bedroom_key: + description: It's a key for the bedroom + takeable: true + +hounds_tooth: + description: It's extremely sharp! + is_weapon: true + weapon_damage: 20 + takeable: true + diff --git a/Roberts-Game/data/locations.yml b/Roberts-Game/data/locations.yml new file mode 100644 index 0000000..ff2397f --- /dev/null +++ b/Roberts-Game/data/locations.yml @@ -0,0 +1,254 @@ + +front_gate: + short_description: a rusted gate + long_description: You are standing at the entrance to an abandoned estate. + environmental_effect: storm + paths: + north: stone_walkway + items: + north: rusty_gate + south: first_hint + ground: rusty_gate_key + +stone_walkway: + short_description: a stone walkway + long_description: You are standing on a stone walkway. + environmental_effect: storm + paths: + north: front_door + south: front_gate + items: + east: unkempt_bush + +front_door: + short_description: a large, sturdy door + long_description: You are standing at the front entrance to the mansion. + environmental_effect: storm + paths: + north: foyer + south: stone_walkway + items: + north: front_door + +foyer: + short_description: the foyer + long_description: You are standing in the foyer of the mansion. + environmental_effect: dark + paths: + south: front_door + north: main_hall + east: kitchen + west: library + +library: + short_description: the library + long_description: You are standing in a large room filled with books. The books fill the room with their ancient odor. + environmental_effect: dark + paths: + north: study + east: foyer + west: secret_staircase + items: + south: walnut_bookshelf + west: oak_bookshelf + +study: + short_description: the study + long_description: You are standing in the study. + environmental_effect: smart + paths: + south: library + east: main_hall + +main_hall: + short_description: the main hall + long_description: You are standing in the main hall. The carpets are exquisite! + environmental_effect: dark + paths: + west: study + east: dining_room + north: stairs_bottom + south: foyer + items: + ground: second_hint + +stairs_bottom: + short_description: the bottom of the stairs + long_description: You are standing on a grand staircase made of marble. You can't imagine how much this cost. + environmental_effect: dark + paths: + south: main_hall + ceiling: stairs_top + +stairs_top: + short_description: the top of the stairs + long_description: You are standing at the top of a beautiful staircase. + environmental_effect: dark + paths: + ground: stairs_bottom + south: upper_hall + +upper_hall: + short_description: a dim hallway + long_description: You are standing in a dim hallway. + environmental_effect: dark + paths: + north: stairs_top + west: observatory + east: master_bedroom + south: bathroom + items: + south: bathroom_door + east: bedroom_door + +master_bedroom: + short_description: a spacious bedroom + long_description: You are standing in a luxurious bedroom. You're pretty sure it's bigger than your apartment. + environmental_effect: dark + paths: + west: upper_hall + items: + north: wardrobe + +bathroom: + short_description: a bathroom + long_description: You are standing in a bathroom. The tile work is quite impressive. + environmental_effect: dark + paths: + north: upper_hall + items: + east: towel_rod + +observatory: + short_description: an observatory + long_description: You are standing in an observatory. Star charts decorate the walls. + environmental_effect: dark + paths: + east: upper_hall + west: ether + items: + west: telescope + +ether: + short_description: the ether + long_description: You are floating in the ether. It's impossible to describe with language. + environmental_effect: etheral + paths: + east: observatory + items: + west: heavenly_hammer + +dining_room: + short_description: the dining room + long_description: You are standing in the dining room. + environmental_effect: dark + paths: + west: main_hall + south: kitchen + entities: + - skeleton + +kitchen: + short_description: the kitchen + long_description: You are standing in the kitchen. It's filled with dated appliances. + environmental_effect: dark + paths: + west: foyer + north: dining_room + items: + east: countertop + +secret_staircase: + short_description: a secret staircase + long_description: You are standing in a secret staircase connecting the main level with the underground. + environmental_effect: dark + paths: + east: library + ground: dungeon_entrance + +dungeon_entrance: + short_description: the dungeon entrance + long_description: You are standing in the entrance to the dungeon. + environmental_effect: cold + paths: + ceiling: secret_staircase + north: damp_room + entities: + - wraith + items: + west: bedroom_key + +damp_room: + short_description: a dungeon chamber + long_description: You are standing in a damp room. + environmental_effect: scary + items: + ground: third_hint + paths: + south: dungeon_entrance + north: slimy_room + east: smelly_room + west: slimy_room + +slimy_room: + short_description: a dungeon chamber + long_description: You are standing in a room covered in a thick, gooey slime. + environmental_effect: scary + paths: + south: damp_room + north: damp_room + east: moist_room + west: moist_room + items: + ground: licorice + +smelly_room: + short_description: a dungeon chamber + long_description: You are standing in a room whose stench is almost unbearable. + environmental_effect: scary + paths: + east: moist_room + west: damp_room + items: + ground: bathroom_key + north: egg_sandwich + +moist_room: + short_description: a dungeon chamber + long_description: You are standing in a room with incredibly moist air. + environmental_effect: scary + paths: + north: smelly_room + south: lava_room + east: slimy_room + west: slimy_room + +lava_room: + short_description: a dungeon chamber + long_description: You are standing in a room with a narrow walkway above a pool of molten iron. + environmental_effect: scary + paths: + north: moist_room + east: chapel + entities: + - hell_hound + +chapel: + short_description: a chapel + long_description: You are standing in a large, underground chapel. + environmental_effect: very_scary + paths: + east: lava_room + north: final_room + items: + north: picture_window + east: fourth_hint + +final_room: + short_description: a dark room + long_description: You are standing in the chamber + environmental_effect: very_scary + paths: + south: chapel + + diff --git a/Roberts-Game/data/proxy/EntityProxy.rb b/Roberts-Game/data/proxy/EntityProxy.rb new file mode 100644 index 0000000..dd367db --- /dev/null +++ b/Roberts-Game/data/proxy/EntityProxy.rb @@ -0,0 +1,42 @@ + +class EntityProxy + def initialize sym, raw + @sym = sym + @raw = raw + end + + def name + target.name + end + + def description + target.description + end + + def determined_name + target.determined_name + end + + def tick player + target.tick player + end + + def take_damage item + target.take_damage item + end + + def dead? + target.dead? + end + + def drops + target.drops + end + + private + + def target + @target = Entity.new @sym, @raw if !@target + return @target + end +end diff --git a/Roberts-Game/data/proxy/EnvironmentalEffectProxy.rb b/Roberts-Game/data/proxy/EnvironmentalEffectProxy.rb new file mode 100644 index 0000000..2a74348 --- /dev/null +++ b/Roberts-Game/data/proxy/EnvironmentalEffectProxy.rb @@ -0,0 +1,21 @@ + +class EnvironmentalEffectProxy + def initialize data + @raw = data + end + + def ambient_description + target.ambient_description + end + + def run player_state + target.run player_state + end + + private + + def target + @target = EnvironmentalEffect.new @raw if @target.nil? + return @target + end +end diff --git a/Roberts-Game/data/proxy/ItemProxy.rb b/Roberts-Game/data/proxy/ItemProxy.rb new file mode 100644 index 0000000..a069ae2 --- /dev/null +++ b/Roberts-Game/data/proxy/ItemProxy.rb @@ -0,0 +1,86 @@ + +class ItemProxy + def initialize sym, data + @sym = sym + @raw = data + end + + def name + target.name + end + + def description + target.description + end + + def determiner + target.determiner + end + + def blocking? + target.blocking? + end + + def unlock_with item + target.unlock_with item + end + + def takeable + target.takeable + end + + def determined_name + target.determined_name + end + + def contains + target.contains + end + + def contains? item + target.contains? item + end + + def delete_item item + target.delete_item item + end + + def is_food + target.is_food + end + + def hp_bonus + target.hp_bonus + end + + def is_weapon + target.is_weapon + end + + def weapon_damage + target.weapon_damage + end + + def easy_to_read + target.easy_to_read + end + + def text + target.text + end + + def is_armor + target.is_armor + end + + def damage_protection + target.damage_protection + end + + private + + def target + @target = Item.new @sym, @raw if @target.nil? + return @target + end +end diff --git a/Roberts-Game/data/proxy/LocationProxy.rb b/Roberts-Game/data/proxy/LocationProxy.rb new file mode 100644 index 0000000..315d390 --- /dev/null +++ b/Roberts-Game/data/proxy/LocationProxy.rb @@ -0,0 +1,57 @@ + +class LocationProxy + def initialize data + @raw = data + end + + def short_description + target.short_description + end + + def long_description + target.long_description + end + + def paths + target.paths + end + + def environmental_effect + target.environmental_effect + end + + def items + target.items + end + + def blocked? direction + target.blocked? direction + end + + def delete_item item + target.delete_item item + end + + def contains? item + target.contains? item + end + + def entities + target.entities + end + + def tick player + target.tick player + end + + def delete_entity player + target.delete_entity player + end + + private + + def target + @target = Location.new @raw if @target.nil? + return @target + end +end diff --git a/Roberts-Game/data/strings.yml b/Roberts-Game/data/strings.yml new file mode 100644 index 0000000..4729907 --- /dev/null +++ b/Roberts-Game/data/strings.yml @@ -0,0 +1,72 @@ +title: | + ................................................................................... + . _______ _ _ _______ _______ _______ . + .|\ /|( ____ \( \ ( \ |\ /|( ___ )|\ /|( ____ \( ____ \. + .| ) ( || ( \/| ( | ( | ) ( || ( ) || ) ( || ( \/| ( \/. + .| (___) || (__ | | | | | (___) || | | || | | || (_____ | (__ . + .| ___ || __) | | | | | ___ || | | || | | |(_____ )| __) . + .| ( ) || ( | | | | | ( ) || | | || | | | ) || ( . + .| ) ( || (____/\| (____/\| (____/\| ) ( || (___) || (___) |/\____) || (____/\. + .|/ \|(_______/(_______/(_______/|/ \|(_______)(_______)\_______)(_______/. + ... ROBERT LUDE 2015 .............................................................. +idk: I don't know what you mean. Please rephrase your command or type "help" +help: | + Available commands: + help + exit + quit + look + look at [item] + go [north|south|east|west|up|down] + unlock [item] with [item] + take [item] + open [item] + eat [item] + equip [item] + attack [entity] + read [item] + + When an [item] is required, you may specify any item from your inventory or your current location. Keep in mind that not every item is immediately visible and you may have to look around! +bye: Better luck next time! +hp_perfect: You are in perfect health. +hp_okay: You feel okay. +hp_not_so_good: You don't feel so good. +hp_dying: You are dying. +hp_dead: "\u2620 \u2620 \u2620 YOU ARE DEAD. \u2620 \u2620 \u2620" +solution: | + 1. Front gate: + "unlock rusty gate with rusty gate key" + "go north" + 2. Stone walkway: + "take front door key" + "go north" + 3. Front door: + "unlock front door with front door key" + 4. Kitchen: + "equip knife" + Recommended: + "take apple" + "take orange" + "take turkey" + "take pizza" + 5. Dining room: + "attack skeleton" (repeat until defeated; eat to replenish HP if necessary) + "take red book" + 6. Library: + "take blue book" + "unlock oak bookshelf with red book" + 7. Dungeon entrance: + "equip blue book" + "attack wraith" + 8. +win_message: | + You leave the dark room. + You leave the chapel. + You leave the dungeon. + You climb back into the library. + You exit the house through the foyer. + The storm has cleared. + You may now go home. + + YOU HAVE DEFEATED HELL HOUSE. + Thanks for playing! diff --git a/Roberts-Game/models/Entity.rb b/Roberts-Game/models/Entity.rb new file mode 100644 index 0000000..6884318 --- /dev/null +++ b/Roberts-Game/models/Entity.rb @@ -0,0 +1,39 @@ + +class Entity + attr_reader :name, :description, :drops + + def initialize sym, data + @name = sym.to_s.split('_').join(' ') + @determiner = data[:determiner] || 'a' + @damage = data[:damage] || [] + @description = data[:description] + @health = data[:health] + @drops = Assets::get_item data[:drops] + @weakness = Assets::get_item data[:weakness] + end + + def determined_name + "#{@determiner} #{@name}" + end + + def tick player + damage = @damage.sample + return if damage <= 0 + player.take_damage damage, "#{determined_name.capitalize} attacks you." + end + + def take_damage damage + if damage.kind_of? ItemProxy + damage_amount = damage.weapon_damage + damage_amount *= 5 if @weakness == damage + elsif + damage_amount = damage + end + @health = [0, @health - damage_amount].max + return damage_amount + end + + def dead? + @health <= 0 + end +end diff --git a/Roberts-Game/models/EnvironmentalEffect.rb b/Roberts-Game/models/EnvironmentalEffect.rb new file mode 100644 index 0000000..3e15c89 --- /dev/null +++ b/Roberts-Game/models/EnvironmentalEffect.rb @@ -0,0 +1,21 @@ + +class EnvironmentalEffect + attr_reader :ambient_description + + def initialize data + @do_nothing = !data + return if @do_nothing + + @ambient_description = data[:ambient_description] + @damage_description = data[:damage_description] + @damage = data[:damage] + end + + def run player_state + return if @do_nothing + damage = @damage.sample + return if damage <= 0 + player_state.take_damage damage, @damage_description + end + +end diff --git a/Roberts-Game/models/Item.rb b/Roberts-Game/models/Item.rb new file mode 100644 index 0000000..b2caeed --- /dev/null +++ b/Roberts-Game/models/Item.rb @@ -0,0 +1,60 @@ + +class Item + attr_reader :determiner, :takeable, :description, :contains, :is_food, :hp_bonus, :is_weapon, :weapon_damage, :text, :easy_to_read, :is_armor, :damage_protection + + def initialize sym, data + @sym = sym + @description = data[:description] + @is_door = !!data[:is_door] + @is_food = !!data[:is_food] + @locked = !!data[:locked] + @unlocks_with = data[:unlocks_with] + @determiner = data[:determiner] || 'a' + @takeable = data[:takeable] || false + @contains = (data[:contains] || []).map{|sym| Assets::get_item(sym) } + @hp_bonus = data[:hp_bonus] + @is_weapon = data[:is_weapon] + @weapon_damage = data[:weapon_damage] + @easy_to_read = !!data[:easy_to_read] + @text = data[:text] + @is_armor = !!data[:is_armor] + @damage_protection = data[:damage_protection] || 0 + + @unlocks_with = Assets::get_item @unlocks_with if !@unlocks_with.nil? + end + + def name + @sym.to_s.split('_').join(' ') + end + + def blocking? + @is_door && !@unlocked + end + + def unlock_with key + if @unlocks_with == key + @unlocked = true + return true + else + return false + end + end + + def determined_name + "#{@determiner} #{name}" + end + + def contains? item + return true if @contains.member? item + !!@contains.find { |inner_item| inner_item.contains? item } + end + + def delete_item item + return false if !contains? item + if @contains.member? item + @contains.delete item + return true + end + !!@contains.find { |inner_item| inner_item.delete_item item } + end +end diff --git a/Roberts-Game/models/Location.rb b/Roberts-Game/models/Location.rb new file mode 100644 index 0000000..1bf26eb --- /dev/null +++ b/Roberts-Game/models/Location.rb @@ -0,0 +1,47 @@ + +require_relative "../data/assets" + +class Location + attr_reader :short_description, :long_description, :paths, :environmental_effect, :items, :entities + + def initialize data + @short_description = data[:short_description] + @long_description = data[:long_description] + @environmental_effect = data[:environmental_effect] || :none + @paths = data[:paths] || {} + @items = data[:items] || {} + @entities = (data[:entities] || []).map { |sym| Assets::get_entity sym } + + @environmental_effect = Assets::get_environmental_effect @environmental_effect + @paths.each { |key,sym| @paths[key] = Assets::get_location sym } + @items.each { |key,sym| @items[key] = Assets::get_item sym } + end + + def blocked? direction + return false if @items[direction].nil? + @items[direction].blocking? + end + + def delete_item item + return false if !contains? item + if @items.value? item + @items.delete @items.key item + return true + end + !!@items.find { |key, inner_item| inner_item.delete_item item } + end + + def delete_entity entity + @entities.delete entity + end + + def contains? item + return true if @items.value? item + !!@items.find { |key, inner_item| inner_item.contains? item } + end + + def tick player + @entities.reject! { |entity| entity.dead? } + @entities.each { |entity| entity.tick player } + end +end diff --git a/Roberts-Game/models/Player.rb b/Roberts-Game/models/Player.rb new file mode 100644 index 0000000..9e6b6c4 --- /dev/null +++ b/Roberts-Game/models/Player.rb @@ -0,0 +1,119 @@ + +require_relative "../data/assets.rb" + +class Player + + attr_reader :location, :health, :messages, :inventory, :weapon, :armor + attr_writer :godly + attr_writer :won + + def initialize + @location = Assets::get_location :front_gate + @health = 10 + @messages = [] + @inventory = [] + @godly = false + @won = false + end + + def do_environmental_effect + return if @godly + @location.environmental_effect.run self + end + + def take_damage amount, message + return if @godly + amount -= @armor.damage_protection if !@armor.nil? + return if amount <= 0 + notify "#{message} (#{amount} damage)" + @health = [0, @health - amount].max + end + + def clear_messages + @messages = [] + end + + def dead? + @health <= 0 + end + + def go direction + if !@location.entities.empty? && !@godly + notify "An entity here won't let you leave!" + return + end + + direction = :ground if direction == :down + direction = :ceiling if direction == :up + + desired_location = @location.paths[direction] + + if desired_location.nil? + notify "You can't go that way." + return + end + + if @location.blocked?(direction) && !@godly + notify "The way is blocked by the #{@location.items[direction].name}." + return + end + + @location = desired_location + end + + def can_reach_item? item + return @location.contains?(item) || @inventory.member?(item) || (@weapon == item) || @godly + end + + def notify message + messages << message + end + + def is_holding? item + @inventory.member? item + end + + def hold item + @inventory << item + end + + def use_up item + if @location.items.value? item + @location.delete_item item + return + end + + if @inventory.member? item + @inventory.delete item + return + end + end + + def heal amount + notify "You gain #{amount} HP." + @health = [10, @health + amount].min + end + + def equip item + if item.is_weapon + hold @weapon if !@weapon.nil? + @weapon = item + elsif item.is_armor + hold @armor if !@armor.nil? + @armor = item + else + return + end + + use_up item + notify "You have equipped #{item.determined_name}." + end + + def godly? + @godly + end + + def won? + @won + end +end diff --git a/Roberts-Game/view.rb b/Roberts-Game/view.rb new file mode 100644 index 0000000..70711f3 --- /dev/null +++ b/Roberts-Game/view.rb @@ -0,0 +1,107 @@ +require_relative "Command" +require_relative "data/assets" + +module GameView + + module Print + + class << self + + def idk_message + puts Assets::get_string :idk + end + + def title_screen + puts Assets::get_string :title + end + + def state_description player + puts player.location.long_description + end + + def fetch_command + print "\n> " + command = Command::parse gets.chomp + puts + return command + end + + def exit_message + puts Assets::get_string :bye + end + + def help_message + puts Assets::get_string :help + end + + def look_around_message player + show_player_health player + show_player_messages player + show_player_location player + show_player_inventory player + end + + def death_message player + show_player_health player + show_player_messages player + exit_message + end + + def win_message + puts Assets::get_string :win_message + end + + private + + def show_player_messages player + return if player.messages.empty? + player.messages.each { |message| puts message } + player.clear_messages + end + + def show_player_health player + health_meter = "HP [" + "=" * player.health + " " * (10 - player.health) + "]" + + human_message = Assets::get_string case player.health + when 10 then Assets::get_string :hp_perfect + when 8...10 then Assets::get_string :hp_okay + when 4...8 then Assets::get_string :hp_not_so_good + when 1...4 then Assets::get_string :hp_dying + else Assets::get_string :hp_dead + end + + puts "#{health_meter} #{human_message}" + puts "YOU ARE GODLY." if player.godly? + end + + def show_player_location player + puts player.location.long_description + puts player.location.environmental_effect.ambient_description if !player.location.environmental_effect.nil? + + player.location.items.each do |place,item| + case place + when :ground + place_string = " on the ground" + when :north, :south, :east, :west + place_string = " to the #{place.to_s}" + else + place_string = "" + end + puts "There is #{item.determined_name}#{place_string}." + end + + player.location.paths.each do |direction,location| + puts "To the #{direction.to_s}, you can see #{location.short_description}." if !player.location.blocked? direction + end + + player.location.entities.each { |entity| puts "#{entity.determined_name.capitalize} is here." } + end + + def show_player_inventory player + puts "You are holding: #{player.inventory.map {|item| "#{item.determiner} #{item.name}"}.join(", ")}" if !player.inventory.empty? + puts "You have #{player.weapon.determined_name} equipped." if !!player.weapon + puts "You have #{player.armor.determined_name} equipped." if !!player.armor + end + end + end +end