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