├── .gitignore ├── .markdownlint.json ├── .rspec ├── .travis.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin └── gemwarrior ├── data ├── default_world.yaml ├── fantasy_names.yaml ├── minimal1.yaml ├── minimal2.yaml └── minimal3.yaml ├── gemwarrior.gemspec ├── lib └── gemwarrior │ ├── arena.rb │ ├── battle.rb │ ├── entities │ ├── armor.rb │ ├── armor │ │ ├── iron_helmet.rb │ │ └── leather_jerkin.rb │ ├── creature.rb │ ├── creatures │ │ ├── cow.rb │ │ ├── goat.rb │ │ └── pig.rb │ ├── entity.rb │ ├── item.rb │ ├── items │ │ ├── apple.rb │ │ ├── arena_door.rb │ │ ├── bed.rb │ │ ├── bookcase.rb │ │ ├── bullet.rb │ │ ├── chest.rb │ │ ├── couch.rb │ │ ├── cup.rb │ │ ├── dehumidifier.rb │ │ ├── feather.rb │ │ ├── floor_tile.rb │ │ ├── flower.rb │ │ ├── herb.rb │ │ ├── hut.rb │ │ ├── keystone.rb │ │ ├── ladder.rb │ │ ├── letter.rb │ │ ├── locker.rb │ │ ├── locker_corner.rb │ │ ├── map.rb │ │ ├── massive_door.rb │ │ ├── pedestal.rb │ │ ├── pond.rb │ │ ├── red_key.rb │ │ ├── rope.rb │ │ ├── sand_jewel.rb │ │ ├── shovel.rb │ │ ├── small_hole.rb │ │ ├── snowman.rb │ │ ├── sparkly_thing.rb │ │ ├── stonemite.rb │ │ ├── tent.rb │ │ ├── throne.rb │ │ ├── tree.rb │ │ └── waterfall.rb │ ├── location.rb │ ├── monster.rb │ ├── monsters │ │ ├── alexandrat.rb │ │ ├── amberoo.rb │ │ ├── amethystle.rb │ │ ├── apatiger.rb │ │ ├── aquamarine.rb │ │ ├── bloodstorm.rb │ │ ├── bosses │ │ │ ├── emerald.rb │ │ │ ├── garynetty.rb │ │ │ └── jaspern.rb │ │ ├── citrinaga.rb │ │ ├── coraliz.rb │ │ ├── cubicat.rb │ │ └── diaman.rb │ ├── people │ │ ├── arena_master.rb │ │ ├── drunk_man.rb │ │ ├── queen_ruby.rb │ │ ├── rockney.rb │ │ ├── shifty_woman.rb │ │ ├── thin_man.rb │ │ └── ware_hawker.rb │ ├── person.rb │ ├── player.rb │ ├── weapon.rb │ └── weapons │ │ ├── dagger.rb │ │ ├── gun.rb │ │ ├── mace.rb │ │ ├── opalaser.rb │ │ ├── spear.rb │ │ ├── stalactite.rb │ │ └── stone.rb │ ├── evaluator.rb │ ├── game.rb │ ├── game_assets.rb │ ├── game_options.rb │ ├── inventory.rb │ ├── misc │ ├── animation.rb │ ├── audio.rb │ ├── audio_cues.rb │ ├── bloops_cues.rb │ ├── formatting.rb │ ├── hr.rb │ ├── musical_notes.rb │ ├── name_generator.rb │ ├── player_levels.rb │ ├── timer.rb │ └── wordlist.rb │ ├── repl.rb │ ├── version.rb │ └── world.rb ├── spec ├── gemwarrior_spec.rb └── spec_helper.rb └── tests ├── compare_versions.rb ├── getch_test.rb ├── hooch.rb ├── item_hashes.rb ├── json_api_call.rb ├── module_hashes.rb ├── test-class └── test-class.rb ├── test-feep └── test-feep.rb ├── test-save-load ├── save-load.rb └── save.conf └── test-singleton ├── game.rb └── game_options.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .gemwarrior 2 | .bundle/ 3 | .yardoc 4 | Gemfile.lock 5 | *.gct 6 | *.gem 7 | !bin/gemwarrior 8 | _yardoc/ 9 | assets/ 10 | coverage/* 11 | doc/ 12 | img/ 13 | pkg/ 14 | spec/reports/ 15 | tmp/ 16 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD033": false, 3 | "MD040": false, 4 | "MD041": false 5 | } 6 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2.1 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Michael Chadwick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | /-+-+-+ +-+-+-+-+-+-+-\ 3 | |G|E|M| |W|A|R|R|I|O|R| 4 | \-+-+-+ +-+-+-+-+-+-+-/ 5 | ``` 6 | 7 | logo courtesy of [ascii generator](http://www.network-science.de/ascii/) 8 | 9 | [![Gem Version](https://badge.fury.io/rb/gemwarrior.svg)](http://badge.fury.io/rb/gemwarrior) 10 | 11 | **Gem Warrior** is a text adventure that takes place in the land of **Jool**, 12 | where randomized fortune is just as likely as *mayhem*. 13 | 14 | You take up the mantle of **1.upto(rand(5..10)) {print rand(65..90).chr}**, 15 | a gifted young acolyte who has been tasked by the queen herself, **Ruby**, 16 | to venture off into the unknown to recapture a **Shiny Thingtm** 17 | that holds great power within its crystallized boundaries. Unfortunately, it 18 | was stolen recently by a crazed madperson named **Emerald**, bent on using 19 | its frightening power for **Evil**. You are **Good**, obviously, and the 20 | rightful caretaker of such power, but he will not give it up willingly, and 21 | has cursed all the creatures of the land into doing his bidding, which is 22 | largely tearing you limb from limb. 23 | 24 | Start in your poor, super lame cottage, where you live alone, subsisting off 25 | the sale of polished rocks you scavenge all day for in the neighboring caves. 26 | Once tasked with your quest, travel throughout the land of Jool, eventually 27 | reaching the sky tower that Emerald resides in with his stolen goods, 28 | laughing to himself, occasionally. 29 | 30 | As you travel you will discover sights and sounds of the land, all of which 31 | are new to you because you don't really get out much. Visit towns with 32 | merchants willing to trade coin for wares they bought off of other adventurers 33 | who didn't last the previous attempts at thwartion. Sleep in a tent (or on the 34 | ground, if that's all that's available) to regain your enumerated status 35 | points, which are conveniently located in your peripheral vision (i.e. the 36 | console window). Eventually, if you're skilled, you just might reach the place 37 | known as **Emerald's Sky Tower**, figure out how to part him from his 38 | **ShinyThingtm**, and then do what is "right". 39 | 40 | ## PLAY 41 | 42 | ### How to Get to Jool 43 | 44 | 1. `ruby -v` should return `ruby 2.something`; else install [Ruby](https://www.ruby-lang.org) 45 | 2. `gem -v` should return `2.something`; else install [RubyGems](https://rubygems.org) 46 | 3. `(sudo) gem install gemwarrior` 47 | 4. `gemwarrior` 48 | 49 | Run the commands above and you'll be whisked away to Jool, ready to start or 50 | continue your quest to defeat Emerald and take back the coveted Shiny Thing(tm) 51 | that you will bring back to Queen Ruby (or will you...?). 52 | 53 | ### Main Commands 54 | 55 | `> character` - character check for visual identity 56 | `> inventory [object]` - check your inventory (or an individual item within) 57 | `> rest` - take a load off and replenish hp 58 | `> look [object]` - look at current location and its items and monsters 59 | `> take [object]` - take an item from a location 60 | `> use [object]` - use an item from your inventory or current location 61 | `> drop [object]` - drop an item from your inventory 62 | `> equip [object]` - designate an item in your inventory your weapon 63 | `> unequip [object]` - stop using an item in your inventory as your weapon 64 | `> go [direction]` - go in a direction, if possible 65 | (north|east|south|west work as shortcuts) 66 | `> attack [monster]` - attack a monster 67 | `> change [attribute]` - change some things about yourself 68 | `> help` - display available commands 69 | `> version` - display current game version 70 | `> quit` - quit this nonsense w/ prompt 71 | `> quit!` - quit this nonsense w/o prompt 72 | 73 | ## CONTRIBUTE 74 | 75 | 1. `git clone https://github.com/michaelchadwick/gemwarrior.git` 76 | 2. `cd /path/to/gemwarrior` 77 | 3. `vi /file/you/want/to/change.rb` 78 | 4. `gem build` 79 | 5. `gem install --local /path/to/gemwarrior/pkg/gemwarrior.VERSION.gem` 80 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | require 'bundler/gem_tasks' 3 | 4 | # Default directory to look in is `/specs` 5 | # Run with `rake spec` 6 | RSpec::Core::RakeTask.new(:spec) do |task| 7 | task.rspec_opts = ['--color', '--format', 'documentation'] 8 | end 9 | 10 | task :deploy do |t| 11 | sh "git push origin master" 12 | sh "rake build" 13 | 14 | file = Dir.glob("pkg/*").max_by {|f| File.mtime(f)} 15 | 16 | sh "gem push #{file}" 17 | end 18 | 19 | task :default => :spec 20 | -------------------------------------------------------------------------------- /bin/gemwarrior: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'optparse' 4 | require 'os' 5 | require 'fileutils' 6 | 7 | require_relative '../lib/gemwarrior/game' 8 | require_relative '../lib/gemwarrior/game_options' 9 | require_relative '../lib/gemwarrior/version' 10 | 11 | include Gemwarrior 12 | 13 | GW_NAME = 'Gem Warrior' 14 | GW_HOME_PATH = "#{Dir.home}/.gemwarrior" 15 | GW_SAVE_FILE_YAML_PATH = "#{GW_HOME_PATH}/gw_sav.yaml" 16 | GW_SAVE_FILE_BIN_PATH = "#{GW_HOME_PATH}/gw_sav.dat" 17 | GW_SAVE_FILE_MODE_DEFAULT = 'Y' # YAML 18 | GW_OPTS_FILE_PATH = "#{GW_HOME_PATH}/gw_opts" 19 | GW_LOG_FILE_PATH = "#{GW_HOME_PATH}/gw_log" 20 | GW_WRAP_WIDTH_DEFAULT = 80 21 | 22 | def print_error(error) 23 | case error 24 | when OptionParser::InvalidOption 25 | puts "#{GW_NAME}: illegal option #{error.args.join(' ')}" 26 | else 27 | puts "An unexpected error occurred while running #{GW_NAME}:" 28 | puts " #{error}\n" 29 | end 30 | end 31 | 32 | def init_config(world) 33 | Dir.mkdir(GW_HOME_PATH) unless Dir.exist?(GW_HOME_PATH) 34 | 35 | log_file_path = GW_LOG_FILE_PATH 36 | options_file_path = GW_OPTS_FILE_PATH 37 | save_file_mode_default = GW_SAVE_FILE_MODE_DEFAULT 38 | save_file_bin_path = GW_SAVE_FILE_BIN_PATH 39 | save_file_yaml_path = GW_SAVE_FILE_YAML_PATH 40 | sound_system_default = OS.windows? ? 'win32-sound' : 'bloops' 41 | world_yaml_path = File.expand_path("../../data/#{world}.yaml", __FILE__) 42 | world_bin_path = File.expand_path("../../data/#{world}.bin", __FILE__) 43 | wrap_width = GW_WRAP_WIDTH_DEFAULT 44 | 45 | GameOptions.add 'log_file_path', log_file_path 46 | GameOptions.add 'options_file_path', options_file_path 47 | GameOptions.add 'save_file_bin_path', save_file_bin_path 48 | GameOptions.add 'save_file_mode', save_file_mode_default 49 | GameOptions.add 'save_file_yaml_path', save_file_yaml_path 50 | GameOptions.add 'sound_system', sound_system_default 51 | GameOptions.add 'world_bin_path', world_bin_path 52 | GameOptions.add 'world_yaml_path', world_yaml_path 53 | GameOptions.add 'wrap_width', wrap_width 54 | end 55 | 56 | def read_options_file 57 | if !File.exist?(GW_OPTS_FILE_PATH) 58 | FileUtils.mkdir(GW_HOME_PATH) 59 | File.write(GW_OPTS_FILE_PATH, "") 60 | end 61 | 62 | options = {} 63 | 64 | File.open(GW_OPTS_FILE_PATH).readlines.each do |line| 65 | kv = line.chomp.split(':') 66 | options[kv[0].to_sym] = kv[1] 67 | end 68 | 69 | # check for null 70 | return options[:sound_enabled] ? options : nil 71 | end 72 | 73 | def parse_options_cli 74 | # default options 75 | options = { 76 | beast_mode: false, 77 | debug_mode: false, 78 | extra_command: nil, 79 | fight_completion: false, 80 | god_mode: false, 81 | new_skip: false, 82 | resume_skip: false, 83 | sound_enabled: false, 84 | sound_system: 'bloops', 85 | sound_volume: 0.3, 86 | use_wordnik: false, 87 | world_name: 'default_world' 88 | } 89 | 90 | # options file has next precedence 91 | unless (opts_file = read_options_file).nil? 92 | options[:fight_completion] = (opts_file[:fight_completion].eql?('false') ? false : true) if opts_file[:fight_completion] 93 | 94 | options[:sound_enabled] = (opts_file[:sound_enabled].eql?('false') ? false : true) if opts_file[:sound_enabled] 95 | 96 | options[:sound_system] = (opts_file[:sound_system]) if opts_file[:sound_system] 97 | 98 | options[:sound_volume] = (opts_file[:sound_volume].to_f) if opts_file[:sound_volume] 99 | 100 | options[:use_wordnik] = (opts_file[:use_wordnik].eql?('false') ? false : true) if opts_file[:use_wordnik] 101 | 102 | opts_file[:world_name] = (opts_file[:world_name]) if opts_file[:world_name] 103 | end 104 | 105 | # command line has next precedence 106 | optparse = OptionParser.new do |opts| 107 | opts.on('-b', '--beast', 'Enable debug[beastmode]') do 108 | options[:beast_mode] = true 109 | end 110 | 111 | opts.on('-d', '--debug', 'Enable debug commands in-game') do 112 | options[:debug_mode] = true 113 | end 114 | 115 | opts.on('-f', '--fight-completion', 'Fighting without specifying an enemy will attack first one it finds') do 116 | options[:fight_completion] = false 117 | end 118 | 119 | opts.on('-g', '--god', 'Enable debug[godmode]') do 120 | options[:god_mode] = true 121 | end 122 | 123 | opts.on('-k', '--wordnik', 'Enable Wordnik to generate more diverse, dynamic descriptors of entities') do 124 | options[:use_wordnik] = true 125 | end 126 | 127 | opts.on('-n', '--new', 'Immediately start a new game, skipping main menu') do 128 | options[:new_skip] = true 129 | end 130 | 131 | opts.on('-r', '--resume', 'Immediately resume the saved game, skipping main menu') do 132 | options[:resume_skip] = true 133 | end 134 | 135 | opts.on('-s', '--sound', 'Enable sound (experimental)') do 136 | options[:sound_enabled] = true 137 | end 138 | 139 | opts.on('-v', '--version', 'Display version number and exit') do 140 | puts "#{GW_NAME} v#{Gemwarrior::VERSION}" 141 | exit 142 | end 143 | 144 | opts.on('-w', '--world WORLD_NAME', String, 'Build Gemwarrior with alternate world data file') do |world_name| 145 | options[:world_name] = world_name 146 | end 147 | 148 | opts.on('-x', '--xtra COMMAND,PARAMS', String, 'Run a command, with optional params, immediately upon beginning the game') do |xc| 149 | options[:extra_command] = xc.gsub(',',' ') 150 | end 151 | end 152 | 153 | optparse.parse!() 154 | 155 | return options 156 | end 157 | 158 | begin 159 | options = parse_options_cli 160 | 161 | init_config(options[:world_name]) 162 | 163 | Gemwarrior::Game.new(options) 164 | rescue => e 165 | print_error(e) 166 | puts ("#{self.class} - #{e.class}: #{e.message}" + e.backtrace).join("\n") 167 | exit(false) 168 | end 169 | -------------------------------------------------------------------------------- /data/minimal1.yaml: -------------------------------------------------------------------------------- 1 | --- !ruby/object:Gemwarrior::World 2 | locations: 3 | - !ruby/object:Gemwarrior::Location 4 | name: home 5 | name_display: Home 6 | visited: true 7 | description: The little, unimportant, decrepit shack that you live in. What the place lacks in decisive magnanimity is made up for in cozy squalidness. Your bed covers much of the northern corner. Your beloved family chest sits at the foot of the bed. 8 | coords: &2 9 | :x: 5 10 | :y: 0 11 | :z: 0 12 | paths: 13 | :north: true 14 | :east: true 15 | :south: false 16 | :west: true 17 | danger_level: :none 18 | monster_level_range: 19 | items: 20 | - !ruby/object:Gemwarrior::Bed 21 | equipped: false 22 | consumable: false 23 | used: false 24 | used_again: false 25 | number_of_uses: 26 | name: bed 27 | name_display: Bed 28 | description: The place where you sleep when you are not adventuring. 29 | takeable: false 30 | talkable: false 31 | useable: true 32 | equippable: false 33 | monsters_abounding: [] 34 | bosses_abounding: [] 35 | checked_for_monsters: false 36 | player: !ruby/object:Gemwarrior::Player 37 | name: Player 38 | description: |- 39 | Picked to do battle against a wizened madman for a shiny something or other for world-saving purposes. 40 | face: facey 41 | hands: handsy 42 | mood: moody 43 | level: 1 44 | xp: 0 45 | hp_cur: 30 46 | hp_max: 30 47 | atk_lo: 1 48 | atk_hi: 2 49 | defense: 1 50 | dexterity: 3 51 | inventory: !ruby/object:Gemwarrior::Inventory 52 | armor: 53 | items: [] 54 | weapon: 55 | rox: 0 56 | cur_coords: *2 57 | special_abilities: [] 58 | monsters_killed: 0 59 | bosses_killed: 0 60 | items_taken: 0 61 | movements_made: 0 62 | rests_taken: 0 63 | deaths: 0 64 | emerald_beaten: false 65 | shifty_jeweled: false 66 | shifty_to_jewel: false 67 | duration: 68 | -------------------------------------------------------------------------------- /data/minimal2.yaml: -------------------------------------------------------------------------------- 1 | --- !ruby/object:Gemwarrior::World 2 | locations: 3 | - !ruby/object:Gemwarrior::Location 4 | name: home 5 | name_display: Home 6 | visited: true 7 | description: The little, unimportant, decrepit shack that you live in. What the place lacks in decisive magnanimity is made up for in cozy squalidness. Your bed covers much of the northern corner. Your beloved family chest sits at the foot of the bed. 8 | coords: &2 9 | :x: 5 10 | :y: 0 11 | :z: 0 12 | paths: 13 | :north: true 14 | :east: true 15 | :south: false 16 | :west: true 17 | danger_level: :none 18 | monster_level_range: 19 | monsters_abounding: [] 20 | bosses_abounding: [] 21 | checked_for_monsters: false 22 | emerald_beaten: false 23 | shifty_jeweled: false 24 | shifty_to_jewel: false 25 | duration: 26 | -------------------------------------------------------------------------------- /data/minimal3.yaml: -------------------------------------------------------------------------------- 1 | --- !ruby/object:Gemwarrior::World 2 | -------------------------------------------------------------------------------- /gemwarrior.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | 5 | require 'gemwarrior/version' 6 | 7 | source_uri = 'https://github.com/michaelchadwick/gemwarrior' 8 | rubygem_uri = 'http://rubygems.org/gems/gemwarrior' 9 | 10 | Gem::Specification.new do |spec| 11 | spec.name = 'gemwarrior' 12 | spec.summary = 'RubyGem text adventure' 13 | spec.version = Gemwarrior::VERSION 14 | spec.platform = Gem::Platform::RUBY 15 | spec.authors = ['Michael Chadwick'] 16 | spec.email = ['mike@neb.host'] 17 | spec.homepage = rubygem_uri 18 | spec.license = 'MIT' 19 | spec.description = 'A fun text adventure in the form of a RubyGem!' 20 | 21 | spec.files = `git ls-files`.split("\n") 22 | spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 23 | spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } 24 | spec.require_paths = ['lib'] 25 | 26 | spec.metadata = { 27 | "documentation_uri" => source_uri, 28 | "homepage_uri" => source_uri, 29 | "source_code_uri" => source_uri 30 | } 31 | spec.required_ruby_version = '>= 2.0' 32 | spec.post_install_message = "Type 'gemwarrior' to start adventuring!" 33 | 34 | ## required deps 35 | spec.add_runtime_dependency 'clocker', '~> 0.1.6' 36 | spec.add_runtime_dependency 'colorize', '~> 1.0' 37 | spec.add_runtime_dependency 'gems', '~> 1.2' 38 | spec.add_runtime_dependency 'http', '~> 5.1' 39 | spec.add_runtime_dependency 'json', '~> 2.6' 40 | spec.add_runtime_dependency 'matrext', '~> 1.0' 41 | spec.add_runtime_dependency 'os', '~> 0.9', '>= 0.9.6' 42 | spec.add_runtime_dependency 'psych', '~> 5.1' 43 | ## optional sound systems 44 | # spec.add_runtime_dependency 'bloopsaphone', '>= 0.4' 45 | # spec.add_runtime_dependency 'feep', '~> 0.1.3' 46 | # spec.add_runtime_dependency 'win32-sound', '~> 0.6.0' 47 | 48 | ## development deps 49 | spec.add_development_dependency 'awesome_print', '~> 1.9' 50 | spec.add_development_dependency 'bundler', '~> 2.4' 51 | spec.add_development_dependency 'bundler-audit', '~> 0.9' 52 | spec.add_development_dependency 'guard', '~> 2.18' 53 | spec.add_development_dependency 'guard-rspec', '~> 4.7' 54 | spec.add_development_dependency 'pry-byebug', '~> 3.10' 55 | spec.add_development_dependency 'rake', '~> 13.0' 56 | spec.add_development_dependency 'rspec', '~> 3.12.0' 57 | spec.add_development_dependency 'rspec-nc', '~> 0.3' 58 | spec.add_development_dependency 'rubocop', '~> 1.50' 59 | end 60 | -------------------------------------------------------------------------------- /lib/gemwarrior/arena.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/arena.rb 2 | # Arena series of battles 3 | 4 | require_relative 'battle' 5 | 6 | module Gemwarrior 7 | class Arena 8 | # CONSTANTS 9 | BONUS_ROX_MULTIPLIER = 25 10 | BONUS_XP_MULTIPLIER = 10 11 | 12 | attr_accessor :world, :player 13 | 14 | def initialize(options) 15 | self.world = options.fetch(:world) 16 | self.player = options.fetch(:player) 17 | end 18 | 19 | def start 20 | print_arena_intro 21 | 22 | arena_monsters_vanquished = 0 23 | 24 | loop do 25 | monster = generate_monster 26 | battle = Battle.new(world: world, player: player, monster: monster) 27 | result = battle.start(is_arena: true) 28 | 29 | return 'death' if result.eql?('death') 30 | 31 | arena_monsters_vanquished += 1 32 | 33 | print ' Do you wish to continue fighting in the Arena? (y/n) ' 34 | answer = gets.chomp.downcase 35 | 36 | case answer 37 | when 'y', 'yes' 38 | puts 39 | next 40 | else 41 | bonus_rox = arena_monsters_vanquished * BONUS_ROX_MULTIPLIER 42 | bonus_xp = arena_monsters_vanquished * BONUS_XP_MULTIPLIER 43 | player.rox = player.rox + bonus_rox 44 | player.xp = player.xp + bonus_xp 45 | puts 46 | puts ' You decided you\'ve had enough of the exhausting Arena for one day and exit the main stage.' 47 | puts " You defeated #{arena_monsters_vanquished} monsters!" 48 | puts " You have gained #{bonus_rox} rox and #{bonus_xp} XP!" 49 | 50 | return print_arena_outro 51 | end 52 | end 53 | end 54 | 55 | private 56 | 57 | def generate_monster 58 | random_monster = nil 59 | 60 | loop do 61 | random_monster = GameMonsters.data[rand(0..GameMonsters.data.length - 1)].clone 62 | 63 | break unless random_monster.is_boss 64 | end 65 | 66 | random_monster.clone 67 | end 68 | 69 | def print_arena_intro 70 | puts '**************************'.colorize(:red) 71 | puts '* YOU ENTER THE ARENA!!! *'.colorize(:red) 72 | puts '**************************'.colorize(:red) 73 | end 74 | 75 | def print_arena_outro 76 | puts '**************************'.colorize(:red) 77 | puts '* YOU LEAVE THE ARENA!!! *'.colorize(:red) 78 | puts '**************************'.colorize(:red) 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/armor.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/armor.rb 2 | # Entity::Item::Armor base class 3 | 4 | require_relative 'item' 5 | 6 | module Gemwarrior 7 | class Armor < Item 8 | attr_accessor :defense 9 | 10 | def initialize 11 | super 12 | 13 | self.equippable = true 14 | self.defense = 0 15 | self.is_armor = true 16 | end 17 | 18 | def use(world) 19 | 'Save the donning of this piece of armor for battle.' 20 | end 21 | 22 | def describe_detailed 23 | desc_text = "\"#{name_display}\"\n".colorize(:yellow) 24 | desc_text << "(#{name})\n".colorize(:green) 25 | desc_text << "#{description}\n".colorize(:white) 26 | desc_text << "ARMOR? #{is_armor}\n".colorize(:white) 27 | desc_text << "DEFENSE: #{defense}\n".colorize(:white) 28 | desc_text << "TAKEABLE? #{takeable}\n".colorize(:white) 29 | desc_text << "USEABLE? #{useable}\n".colorize(:white) 30 | desc_text << "TALKABLE? #{talkable}\n".colorize(:white) 31 | desc_text << "CONSUMABLE? #{consumable}\n".colorize(:white) 32 | desc_text << "EQUIPPABLE? #{equippable}\n".colorize(:white) 33 | desc_text << "EQUIPPED? #{equipped}\n".colorize(:white) 34 | desc_text << "USED? #{used}\n".colorize(:white) 35 | desc_text << "USED AGAIN? #{used_again}\n".colorize(:white) 36 | desc_text << "USES LEFT? #{number_of_uses}\n".colorize(:white) unless number_of_uses.nil? 37 | desc_text 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/armor/iron_helmet.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/armor/iron_helmet.rb 2 | # Entity::Item::Armor::IronHelmet 3 | 4 | require_relative '../armor' 5 | 6 | module Gemwarrior 7 | class IronHelmet < Armor 8 | def initialize 9 | super 10 | 11 | self.name = 'iron_helmet' 12 | self.name_display = 'Iron Helmet' 13 | self.description = 'The noggin is better off when encased in a heavy, yet safe, bowl-shaped carapace made of solid iron.' 14 | self.defense = 3 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/armor/leather_jerkin.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/armor/leather_jerkin.rb 2 | # Entity::Item::Armor::LeatherJerkin 3 | 4 | require_relative '../armor' 5 | 6 | module Gemwarrior 7 | class LeatherJerkin < Armor 8 | def initialize 9 | super 10 | 11 | self.name = 'leather_jerkin' 12 | self.name_display = 'Leather Jerkin' 13 | self.description = 'Brownish-black in color and fairly form-fitting, this jerkin will soften any blow a bit, at least.' 14 | self.defense = 2 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/creature.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/creature.rb 2 | # Entity::Creature base class 3 | 4 | require_relative 'entity' 5 | 6 | module Gemwarrior 7 | class Creature < Entity 8 | attr_accessor :face, 9 | :hands, 10 | :mood, 11 | :level, 12 | :xp, 13 | :hp_cur, 14 | :hp_max, 15 | :atk_lo, 16 | :atk_hi, 17 | :defense, 18 | :dexterity, 19 | :inventory, 20 | :rox 21 | 22 | attr_reader :use, 23 | :speak 24 | 25 | def initialize 26 | super 27 | 28 | self.name = 'creature' 29 | self.name_display = Formatting::upstyle(name) 30 | self.description = 'A creature.' 31 | self.face = 'face-y' 32 | self.hands = 'handsy' 33 | self.mood = 'moody' 34 | self.level = 1 35 | self.xp = 0 36 | self.hp_cur = 10 37 | self.hp_max = 10 38 | self.atk_lo = 1 39 | self.atk_hi = 1 40 | self.defense = 1 41 | self.dexterity = 1 42 | self.inventory = Inventory.new 43 | self.rox = 0 44 | self.talkable = true 45 | self.used = false 46 | end 47 | 48 | def use(world) 49 | 'That creature does not seem to want to talk to you right now.' 50 | end 51 | 52 | def describe 53 | desc_text = "#{description}".colorize(:white) 54 | desc_text << "\n" 55 | desc_text << "The creature has several distinguishing features, as well: face is #{face.colorize(:yellow)}, hands are #{hands.colorize(:yellow)}, and mood is, generally, #{mood.colorize(:yellow)}." 56 | desc_text 57 | end 58 | 59 | def describe_detailed 60 | desc_text = "\"#{name_display}\"\n".colorize(:yellow) 61 | desc_text << "(#{name})\n".colorize(:green) 62 | desc_text << "#{description}\n".colorize(:white) 63 | desc_text << "FACE : #{face}\n".colorize(:white) 64 | desc_text << "HANDS : #{hands}\n".colorize(:white) 65 | desc_text << "MOOD : #{mood}\n".colorize(:white) 66 | desc_text << "LEVEL : #{level}\n".colorize(:white) 67 | desc_text << "XP : #{xp}\n".colorize(:white) 68 | desc_text << "HP : #{hp_cur}/#{hp_max}\n".colorize(:white) 69 | desc_text << "ATK_RANGE : #{atk_lo}-#{atk_hi}\n".colorize(:white) 70 | desc_text << "DEFENSE : #{defense}\n".colorize(:white) 71 | desc_text << "DEXTERITY : #{dexterity}\n".colorize(:white) 72 | desc_text << "ROX : #{rox}\n".colorize(:white) 73 | desc_text << "INVENTORY : #{inventory.contents}\n".colorize(:white) 74 | desc_text 75 | end 76 | 77 | def speak(s, no_end_quote = false, no_line_feed = false) 78 | text = ">> \"#{s}" 79 | text += "\"" unless no_end_quote 80 | text += "\n" unless no_line_feed 81 | text = text.colorize(:yellow) 82 | if no_line_feed 83 | print text 84 | else 85 | print text.gsub(/(.{1,#{GameOptions.data['wrap_width']}})(\s+|\Z)/, "\\1\n") 86 | end 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/creatures/cow.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/creatures/cow.rb 2 | # Entity::Creature::Cow 3 | 4 | require_relative '../creature' 5 | 6 | module Gemwarrior 7 | class Cow < Creature 8 | def initialize 9 | super 10 | 11 | self.name = 'cow' 12 | self.name_display = 'Cow' 13 | self.description = 'Grazing on some fake grass, unperturbed, this black and white herd animal looks bored.' 14 | self.face = 'blank' 15 | self.hands = 'stampy' 16 | self.mood = 'reserved' 17 | end 18 | 19 | def use(world) 20 | puts '>> "Moo."' 21 | { type: nil, data: nil } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/creatures/goat.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/people/goat.rb 2 | # Entity::Creature::Goat 3 | 4 | require_relative '../creature' 5 | 6 | module Gemwarrior 7 | class Goat < Creature 8 | def initialize 9 | super 10 | 11 | self.name = 'goat' 12 | self.name_display = 'Goat' 13 | self.description = 'The scruff is strong with this one as it chews through what appears to be a recent mystery novel most likely thrown into the pen by a passerby.' 14 | self.face = 'busy' 15 | self.hands = 'hoofy' 16 | self.mood = 'content' 17 | end 18 | 19 | def use(world) 20 | puts '>> "Baa."' 21 | { type: nil, data: nil } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/creatures/pig.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/people/pig.rb 2 | # Entity::Creature::Pig 3 | 4 | require_relative '../creature' 5 | 6 | module Gemwarrior 7 | class Pig < Creature 8 | def initialize 9 | super 10 | 11 | self.name = 'pig' 12 | self.name_display = 'Pig' 13 | self.description = 'Dirty, eating slop, but still kind of cute. Yep, this is a pig.' 14 | self.face = 'messy' 15 | self.hands = 'muddy' 16 | self.mood = 'restless' 17 | end 18 | 19 | def use(world) 20 | puts '>> "Oink."' 21 | { type: nil, data: nil } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/entity.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/entity.rb 2 | # Base class for a describable object 3 | 4 | require_relative '../game_options' 5 | require_relative '../misc/formatting' 6 | require_relative '../misc/animation' 7 | 8 | module Gemwarrior 9 | class Entity 10 | attr_accessor :name, 11 | :name_display, 12 | :description, 13 | :takeable, 14 | :useable, 15 | :useable_battle, 16 | :talkable, 17 | :consumable, 18 | :equippable, 19 | :equipped, 20 | :used, 21 | :used_again, 22 | :number_of_uses 23 | 24 | attr_reader :describe, 25 | :describe_detailed, 26 | :display_shopping_cart 27 | 28 | def initialize 29 | self.name = 'entity' 30 | self.name_display = Formatting::upstyle(name) 31 | self.description = 'An entity.' 32 | self.useable = true 33 | self.useable_battle = false 34 | self.talkable = false 35 | self.consumable = false 36 | self.takeable = true 37 | self.equippable = false 38 | self.equipped = false 39 | self.used = false 40 | self.used_again = false 41 | self.number_of_uses = nil 42 | end 43 | 44 | def use(world) 45 | 'That does not appear to be useable.' 46 | end 47 | 48 | def describe(world) 49 | desc_text = "#{description}".colorize(:white) 50 | desc_text 51 | end 52 | 53 | def describe_detailed(world) 54 | desc_text = "\"#{name_display}\"\n".colorize(:yellow) 55 | desc_text << "(#{name})\n".colorize(:green) 56 | desc_text << "#{description}\n".colorize(:white) 57 | desc_text << "TAKEABLE? #{takeable}\n".colorize(:white) 58 | desc_text << "USEABLE? #{useable}\n".colorize(:white) 59 | desc_text << "TALKABLE? #{talkable}\n".colorize(:white) 60 | desc_text << "CONSUMABLE? #{consumable}\n".colorize(:white) 61 | desc_text << "EQUIPPABLE? #{equippable}\n".colorize(:white) 62 | desc_text << "EQUIPPED? #{equipped}\n".colorize(:white) 63 | desc_text << "USED? #{used}\n".colorize(:white) 64 | desc_text << "USED AGAIN? #{used_again}\n".colorize(:white) 65 | desc_text << "USES LEFT? #{number_of_uses}\n".colorize(:white) unless number_of_uses.nil? 66 | desc_text 67 | end 68 | 69 | def display_shopping_cart(cart) 70 | puts "ITEMS SELECTED: #{cart.map(&:name).join(', ')}" 71 | end 72 | 73 | def puts(s = '', width = GameOptions.data['wrap_width']) 74 | super s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n") unless s.nil? 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/item.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/item.rb 2 | # Entity::Item base class 3 | 4 | require_relative 'entity' 5 | 6 | module Gemwarrior 7 | class Item < Entity 8 | attr_accessor :is_armor, 9 | :is_weapon 10 | 11 | attr_reader :use 12 | 13 | def initialize 14 | super 15 | 16 | self.is_armor = false 17 | self.is_weapon = false 18 | end 19 | 20 | def use(world) 21 | 'That item does not do anything...yet.' 22 | end 23 | 24 | def describe_detailed(world) 25 | desc_text = "\"#{name_display}\"\n".colorize(:yellow) 26 | desc_text << "(#{name})\n".colorize(:green) 27 | desc_text << "#{description}\n".colorize(:white) 28 | desc_text << "ARMOR? #{is_armor}\n".colorize(:white) 29 | desc_text << "WEAPON? #{is_weapon}\n".colorize(:white) 30 | desc_text << "TAKEABLE? #{takeable}\n".colorize(:white) 31 | desc_text << "USEABLE? #{useable}\n".colorize(:white) 32 | desc_text << "TALKABLE? #{talkable}\n".colorize(:white) 33 | desc_text << "CONSUMABLE? #{consumable}\n".colorize(:white) 34 | desc_text << "EQUIPPABLE? #{equippable}\n".colorize(:white) 35 | desc_text << "EQUIPPED? #{equipped}\n".colorize(:white) 36 | desc_text << "USED? #{used}\n".colorize(:white) 37 | desc_text << "USED AGAIN? #{used_again}\n".colorize(:white) 38 | desc_text << "USES LEFT? #{number_of_uses}\n".colorize(:white) unless number_of_uses.nil? 39 | desc_text 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/apple.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/apple.rb 2 | # Entity::Item::Apple 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Apple < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'apple' 12 | self.name_display = 'Apple' 13 | self.description = 'Reddish-orangeish in color, this fruit looks sweet, but it is heavy and feels more like a rock you would sooner not bite into.' 14 | self.takeable = true 15 | end 16 | 17 | def use(world) 18 | if !self.used 19 | puts 'You attempt to consume the apple, but stop midway through your first bite. Something tells you this is not a good idea, and you stop.' 20 | puts 21 | self.used = true 22 | else 23 | puts 'Remembering the odd feeling you got the last time you attempted to use the apple, you refrain.' 24 | { type: nil, data: nil } 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/arena_door.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/arena_door.rb 2 | # Entity::Item::ArenaDoor 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class ArenaDoor < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'arena_door' 12 | self.name_display = 'Arena Door' 13 | self.description = 'The Arena is massive, with its numerous columns and stone walls stretching to the sky, but its entrance door is no slouch, keeping apace. Made of reinforced granite and impossible to break down, it nevertheless opens for you while battles are in session.' 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/bed.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/bed.rb 2 | # Entity::Item::Bed 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Bed < Item 8 | # CONSTANTS 9 | USE_TEXT = '** ZZZZZ **' 10 | 11 | def initialize 12 | super 13 | 14 | self.name = 'bed' 15 | self.name_display = 'Bed' 16 | self.description = 'The place where you sleep when you are not adventuring.' 17 | end 18 | 19 | def use(world) 20 | if world.player.at_full_hp? 21 | puts 'You feel perfectly healthy and decide not to actually use the bed. Besides, the trail of fire ants currently leading up to and around the furniture seem somehow uninviting.' 22 | { type: nil, data: nil } 23 | else 24 | Animation.run(phrase: USE_TEXT) 25 | puts 'You unmake the bed, get under the covers, close your eyes, and begin to think about all the things you need to do today. You realize sleep is not one of them and quickly get back up, remake the bed, and get on about your day.' 26 | puts '>> You regain all of your hit points.'.colorize(:green) 27 | { type: 'rest', data: world.player.hp_max-world.player.hp_cur } 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/bookcase.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/bookcase.rb 2 | # Entity::Item::Bookcase 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Bookcase < Item 8 | # CONSTANTS 9 | OPEN_TEXT = '** BRRRRRNNNGG **' 10 | 11 | def initialize 12 | super 13 | 14 | self.name = 'bookcase' 15 | self.name_display = 'Bookcase' 16 | self.description = 'This oaken monstrosity has six shelves, each about 3 feet high. Mathing, you determine it to be "huge", featuring many books within it.' 17 | end 18 | 19 | def use(world) 20 | forest_southwest = world.location_by_name('forest-southwest') 21 | 22 | if forest_southwest.paths[:west].eql? false 23 | if !self.used 24 | puts 'Many books look interesting, including one specific volume entitled "Use Me Again to Attempt Unlocking the Way", by Not Very Subtle.' 25 | puts 26 | self.used = true 27 | else 28 | puts 'You pull out the tome entitled "Use Me Again to Attempt Unlocking the Way".' 29 | STDIN.getc 30 | puts 'Opening the book, you see a question on the inside cover:' 31 | puts '>> "What do you get when an underground excavator is named after a famous "weird" guy?"' 32 | print '? ' 33 | answer = gets.chomp.downcase 34 | 35 | case answer 36 | when 'mineral' 37 | Audio.play_synth(:uncover_secret) 38 | forest_southwest.paths[:west] = true 39 | puts 40 | Animation.run(phrase: OPEN_TEXT) 41 | puts 42 | puts 'After you shout out your answer to the book\'s question to no one in particular, you manage to see a clearing in the forest to the west that was presumably not there before. Huh.' 43 | STDIN.getc 44 | 45 | puts 'Before getting back to your travels, however, you notice the book\'s back cover warns: "The path westward is a difficult one and has "ended" many an adventurer. Take care before venturing forth."' 46 | else 47 | puts 'For some reason, you blurt out an answer to the unhearing trees, feel embarrassed, and put the book back in the bookcase.' 48 | end 49 | end 50 | 51 | { type: nil, data: nil } 52 | else 53 | puts 'You pull out a book here and there, glancing at their weathered pages and scuffed bindings. Nothing really interests you and it is back to staring at the forest.' 54 | end 55 | 56 | { type: nil, data: nil } 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/bullet.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/bullet.rb 2 | # Entity::Item::Bullet 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Bullet < Item 8 | # CONSTANTS 9 | USE_TEXT = '** ZZZZZ **' 10 | 11 | def initialize 12 | super 13 | 14 | self.name = 'bullet' 15 | self.name_display = 'Bullet' 16 | self.description = 'Gunpowder packed into a small metallic tube, ready to be fired from...something.' 17 | self.takeable = true 18 | end 19 | 20 | def use(world) 21 | puts 'You could throw this at a monster, but somehow the force of it would be less than desired.' 22 | { type: nil, data: nil } 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/chest.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/chest.rb 2 | # Entity::Item::Chest 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Chest < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'chest' 12 | self.name_display = 'Chest' 13 | self.description = 'Well-crafted with solid oak, this family chest has intricate inlays all around the front and sides. It\'s the one thing from home you took with you when you left.' 14 | end 15 | 16 | def use(world) 17 | home = world.location_by_name('home') 18 | open_description = 'You open the chest and find little inside but some dust and faded memories of your childhood.' 19 | 20 | if self.used 21 | if home.contains_item?('leather_jerkin') 22 | open_description += ' The old sword fighting garment is still in there, too.' 23 | end 24 | puts open_description 25 | 26 | { type: nil, data: nil } 27 | else 28 | open_description += ' That, and a slightly dirty, but still useful garment you remember using while taking those sword fighting lessons as a small boy.' 29 | 30 | puts open_description 31 | 32 | home.items.push(LeatherJerkin.new) 33 | 34 | self.used = true 35 | 36 | { type: nil, data: nil } 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/couch.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/couch.rb 2 | # Entity::Item::Couch 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Couch < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'couch' 12 | self.name_display = 'Couch' 13 | self.description = 'Ever wanted to sit on a cloud? Now is your chance.' 14 | end 15 | 16 | def use(world) 17 | if world.player.at_full_hp? 18 | puts 'You "sit" on the impossibly soft surface of the furniture, but even after a few minutes of this seemingly heavenly hedonism you don\'t feel significantly better and decide to get up again.' 19 | { type: nil, data: nil } 20 | else 21 | puts 'Your body comes to rest somewhere below the surface of the cloudy apparatus, almost as if it were floating *amongst* the couch. The feeling is heavenly, and you actually feel somewhat better after getting back up.' 22 | puts '>> You regain a few hit points.'.colorize(:green) 23 | { type: 'rest', data: 4 } 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/cup.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/cup.rb 2 | # Entity::Item::Cup 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Cup < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'cup' 12 | self.name_display = 'Cup' 13 | self.description = 'A nice stone mug, perfect for putting things into and then using to carry such things from place to place.' 14 | self.takeable = true 15 | end 16 | 17 | def use(world) 18 | puts 'Nothing in the cup at the moment, so not very usable.' 19 | { type: nil, data: nil } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/dehumidifier.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/dehumidifier.rb 2 | # Entity::Item::Dehumidifier 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Dehumidifier < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'dehumidifier' 12 | self.name_display = 'Dehumidifier' 13 | self.description = 'Humidity stands approximately zero chance of surviving when in its presence.' 14 | self.takeable = true 15 | end 16 | 17 | def use(world) 18 | puts 'Despite your figeting, the lack of humidity remains the same.' 19 | { type: nil, data: nil } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/feather.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/feather.rb 2 | # Entity::Item::Feather 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Feather < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'feather' 12 | self.name_display = 'Feather' 13 | self.description = 'A blue and green feather. It is soft and tender, unlike the craven bird that probably shed it.' 14 | self.takeable = true 15 | end 16 | 17 | def use(world) 18 | puts 'Soft to the touch, you wonder what it could be used before besides temporary comfort.' 19 | { type: nil, data: nil } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/floor_tile.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/floor_tile.rb 2 | # Entity::Item::FloorTile 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class FloorTile < Item 8 | # CONSTANTS 9 | MOVE_TEXT = '** SHOOOOOM **' 10 | 11 | def initialize 12 | super 13 | 14 | self.name = 'floor_tile' 15 | self.name_display = 'Floor Tile' 16 | self.description = 'One of the floor tiles, rough-hewn but immaculate, looks...off. Pressable, even.' 17 | end 18 | 19 | def use(world) 20 | puts 'You slowly lower your foot onto the tile, and then gently depress it, through the floor. Your whole body begins to feel light, lifeless. You black out.' 21 | puts 22 | 23 | # stats 24 | world.player.movements_made += 1 25 | 26 | Animation.run(phrase: MOVE_TEXT) 27 | 28 | { type: 'move', data: 'Rock Piles' } 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/flower.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/flower.rb 2 | # Item::Flower 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Flower < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'flower' 12 | self.name_display = 'Flower' 13 | self.description = 'Petals the color of clear sky and a stem of bright white. A most curious plant.' 14 | self.takeable = true 15 | end 16 | 17 | def use(world) 18 | puts 'You inhale the flower, metaphorically. Smells nice.' 19 | { type: nil, data: nil } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/herb.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/herb.rb 2 | # Entity::Item::Herb 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Herb < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'herb' 12 | self.name_display = 'Herb' 13 | self.description = 'Green and leafy, this wild herb looks edible.' 14 | self.takeable = true 15 | self.consumable = true 16 | self.useable_battle = true 17 | end 18 | 19 | def use(world) 20 | puts 'You place the entire, smallish plant in your mouth, testing its texture. The mysterious herb is easily chewable, and you are able to swallow it without much effort. Slight tingles travel up and down your spine.' 21 | if world.player.at_full_hp? 22 | puts '>> The herb has no medicinal effect, as you already feel perfectly healthy, but it was kind of tasty.' 23 | { type: nil, data: nil } 24 | else 25 | hp_healed = rand(3..5) 26 | puts ">> You regain #{hp_healed} hit points.".colorize(:green) 27 | { type: 'health', data: hp_healed } 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/hut.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/hut.rb 2 | # Entity::Item::Hut 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Hut < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'hut' 12 | self.name_display = 'Hut' 13 | self.description = 'A simple thatched hut, sitting in the middle of the plains. Nothing about it seems odd, except its existence among the otherwise featureless landscape.' 14 | end 15 | 16 | def use(world) 17 | puts 'You peer inside, but it is completely vacant at the moment, leaving you marginally disappointed.' 18 | 19 | { type: nil, data: nil } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/keystone.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/keystone.rb 2 | # Entity::Item::Keystone 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Keystone < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'keystone' 12 | self.name_display = 'Keystone' 13 | self.description = 'Certainly greater than the sum of its parts, this smallish stone glows faintly and feels slick to the touch.' 14 | self.takeable = true 15 | end 16 | 17 | def use(world) 18 | puts 'The keystone glows a bit brighter when you grasp it tightly.' 19 | { type: nil, data: nil } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/ladder.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/ladder.rb 2 | # Entity::Item::Ladder 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Ladder < Item 8 | # CONSTANTS 9 | USE_TEXT = '** THUMP **' 10 | 11 | def initialize 12 | super 13 | 14 | self.name = 'ladder' 15 | self.name_display = 'Ladder' 16 | self.description = 'Rickety and crudely-fashioned, this ladder descends down into the dropoff, hopefully heading towards something...anything.' 17 | end 18 | 19 | def use(world) 20 | puts 'You grab onto the shaky, rough-hewn, wooden ladder with all your might and start to descend, being extra careful not to loose your grip, which with every moment becomes shakier and shakier.' 21 | puts 22 | 23 | # stats 24 | world.player.movements_made += 1 25 | 26 | Animation.run(phrase: USE_TEXT) 27 | 28 | puts 'The last couple of steps are more slippery than you anticipated, so you end up fumbling them, falling a few feet onto the hard ground below. When you regain your composure, you notice your conveyance for descending is now far above you and it is, unfortunately, your closest known exit.' 29 | puts 30 | 31 | { type: 'move_dangerous', data: 'metal_tunnel-south_entrance' } 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/letter.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/letter.rb 2 | # Entity::Item::Letter 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Letter < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'letter' 12 | self.name_display = 'Letter' 13 | self.description = 'A single page of thin paper, folded at the middle, with some excellent penmanship impressed upon it.' 14 | self.takeable = true 15 | end 16 | 17 | def use(world) 18 | if self.used 19 | print 'Do you want to read the letter again? (y/n) ' 20 | answer = gets.chomp.downcase 21 | 22 | case answer 23 | when 'y', 'yes' 24 | print "\n" 25 | print_letter(world) 26 | else 27 | { type: nil, data: nil } 28 | end 29 | else 30 | self.used = true 31 | print_letter(world) 32 | { type: 'xp', data: 3 } 33 | end 34 | end 35 | 36 | private 37 | 38 | def print_letter(world) 39 | puts 'The words of the queen echo in your head as you read the royal note sent to you again:' 40 | puts 41 | Animation.run(phrase: " Dear #{world.player.name},", speed: :insane) 42 | puts 43 | Animation.run(phrase: ' Oh, my! Jool is in trouble! The evil wizard/sorceror/conjuror/rocksmith/wily ', speed: :insane) 44 | Animation.run(phrase: " Emerald has absconded with our ShinyThing(tm)! It is vital that you, #{world.player.name}, ", speed: :insane) 45 | Animation.run(phrase: ' go to his tower in the sky in order to retrieve it before he does something', speed: :insane) 46 | Animation.run(phrase: ' terrible with it!', speed: :insane) 47 | puts 48 | Animation.run(phrase: ' Remember that one time you came to the castle, trying to sell stones you', speed: :insane) 49 | Animation.run(phrase: ' pilfered from a nearby cave? Remember how I laughed and told you to leave', speed: :insane) 50 | Animation.run(phrase: ' at once or I\'d have the royal guard take your head off? Ha!', speed: :insane) 51 | puts 52 | Animation.run(phrase: ' What a fool I was to cast such a special person out, as a mysterious stranger', speed: :insane) 53 | Animation.run(phrase: " in the night told me, before mysteriously disappearing, that you, #{world.player.name},", speed: :insane) 54 | Animation.run(phrase: ' are actually the only one who can save us (for some reason, but that\'s', speed: :insane) 55 | Animation.run(phrase: ' mysterious strangers for you, right?)!', speed: :insane) 56 | puts 57 | Animation.run(phrase: ' Please, I beg of you, save Jool from the potential terror that Emerald could', speed: :insane) 58 | Animation.run(phrase: " possibly wreak on all of us before it is too late! If you do, you, #{world.player.name},", speed: :insane) 59 | Animation.run(phrase: ' will be rewarded handsomely!', speed: :insane) 60 | puts 61 | Animation.run(phrase: ' Sincerely,', speed: :insane); 62 | Animation.run(phrase: ' Queen Ruby', speed: :insane); 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/locker.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/locker.rb 2 | # Entity::Item::Locker 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Locker < Item 8 | attr_accessor :locked 9 | 10 | def initialize 11 | super 12 | 13 | self.name = 'locker' 14 | self.name_display = 'Locker' 15 | self.description = 'A small, locked locker with a lock on it. You will need to unlock it to gain access to its insides.' 16 | self.takeable = false 17 | self.locked = true 18 | end 19 | 20 | def use(world) 21 | cur_location = world.location_by_coords(world.player.cur_coords) 22 | locker = cur_location.get_item_ref('locker') 23 | 24 | if locker.locked 25 | puts 'You pull on the lock, hoping it will unlock and leave the locker unlocked, but it is no good. You will need something else to unlock this locked locker.' 26 | 27 | return { type: nil, data: nil } 28 | else 29 | if self.used 30 | puts 'The locker is open and you can see a shiny gem inside.' 31 | 32 | return { type: nil, data: nil } 33 | else 34 | self.used = true 35 | puts 'You open the unlocked locker and look inside. Thereabouts, is a shiny gem of some sort.' 36 | cur_location.add_item('sand_jewel') 37 | 38 | return { type: nil, data: nil } 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/locker_corner.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/locker_corner.rb 2 | # Entity::Item::LockerCorner 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class LockerCorner < Item 8 | attr_accessor :locked 9 | 10 | def initialize 11 | super 12 | 13 | self.name = 'locker_corner' 14 | self.name_display = 'Locker (Corner)' 15 | self.description = 'The top corner of what appears to be a small locker is slightly sticking up from the sand floor.' 16 | self.takeable = false 17 | self.useable = true 18 | end 19 | 20 | def use(world) 21 | puts 'Pulling with all your might on the corner of the locker doesn\'t get it it budge one inch. Some other method is going to be needed.' 22 | 23 | { type: nil, data: nil } 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/map.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/map.rb 2 | # Entity::Item::Map 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Map < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'map' 12 | self.name_display = 'Map' 13 | self.description = 'The land of Jool is contained on this piece of canvas, in a useful, if not very detailed, manner.' 14 | self.takeable = true 15 | end 16 | 17 | def use(world) 18 | puts 'You unfold the piece of worn canvas and study the markings upon it, always making sure to update it with new places you find.' 19 | 20 | { type: 'action', data: 'map' } 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/massive_door.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/massive_door.rb 2 | # Entity::Item::MassiveDoor 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class MassiveDoor < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'massive_door' 12 | self.name_display = 'Massive Door' 13 | self.description = 'Translucent, but not transparent, this door constructed of condensed water vapor is like nothing you have ever seen. It has no keyhole, but it does have a stone-shaped depression floating centrally within it.' 14 | end 15 | 16 | def use(world) 17 | puts 'You attempt to open the seriously massive door that separates you from Emerald himself.' 18 | if world.player.inventory.contains_item?('keystone') 19 | Audio.play_synth(:uncover_secret) 20 | puts 'The keystone in your inventory glows as you approach the incredibly titanic-sized door, so you naturally pull it out and thrust it into the stone-shaped depression within the cloudy obstruction. The door "opens" in a way and you can now pass through.' 21 | { type: 'move', data: 'Sky Tower (Throne Room)' } 22 | else 23 | puts 'Your hand just goes right through the astonishingly gigantic door, but the rest of your body does not. A moment later, your hand is shoved backwards by some unknown force, and you remain where you were before your unsuccessful attempt.' 24 | { type: nil, data: nil } 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/pedestal.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/pedestal.rb 2 | # Entity::Item::Pedestal 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Pedestal < Item 8 | # CONSTANTS 9 | USE_TEXT = '** WHOOOOOSH **' 10 | 11 | attr_accessor :switches 12 | 13 | def initialize 14 | super 15 | 16 | self.name = 'pedestal' 17 | self.name_display = 'Pedestal' 18 | self.description = 'A pedestal about 4 feet in height rises up from the ground, with six switches arranged vertically above a large gem affixed to the top. The switches each have a word next to them in some language that looks familiar yet strange. Each letter is made of some kind of ink crudely splashed on stone, and each can be moved to arrange them in a different fashion than they are now. The large gem glitters with utter brilliance.' 19 | self.switches = init_switches 20 | end 21 | 22 | def init_switches 23 | self.switches = [ 24 | ['iolita', jumble('iolita'), nil], 25 | ['rockney', jumble('rockney'), nil], 26 | ['emerald', jumble('emerald'), nil], 27 | ['ruby', jumble('ruby'), nil], 28 | ['amberoo', jumble('amberoo'), nil], 29 | ['alexandrat', jumble('alexandrat'), nil] 30 | ] 31 | 32 | self.switches = self.switches.sort_by { rand } 33 | end 34 | 35 | def use(world) 36 | puts 'You look at the pedestal and its switches. The raised gem beckons you to push it and, deductively, you believe that pressing it will do something. However, those switches probably have something to do with the result.' 37 | 38 | loop do 39 | print_switches(self.switches) 40 | 41 | puts 42 | puts 'You can rearrange the letters in the words themselves, and you can push the large gem. What do you do?' 43 | puts 44 | puts '>> 1 Rearrange the letters in switch 1\'s word' 45 | puts '>> 2 Rearrange the letters in switch 2\'s word' 46 | puts '>> 3 Rearrange the letters in switch 3\'s word' 47 | puts '>> 4 Rearrange the letters in switch 4\'s word' 48 | puts '>> 5 Rearrange the letters in switch 5\'s word' 49 | puts '>> 6 Rearrange the letters in switch 6\'s word' 50 | puts '>> 7 Push the large gem into the pedestal' 51 | puts '>> 8 Stop this nonsense' 52 | puts 53 | 54 | print '> ' 55 | choice = gets.chomp! 56 | 57 | case choice 58 | when '1' 59 | switch1 = switches[0][2].nil? ? switches[0][1] : switches[0][2] 60 | puts "Switch 1: #{switch1}" 61 | puts 'What should the switch\'s word be set to?' 62 | switches[0][2] = get_new_word(switch1) 63 | when '2' 64 | switch2 = switches[1][2].nil? ? switches[1][1] : switches[1][2] 65 | puts "Switch 2: #{switch2}" 66 | puts 'What should the switch\'s word be set to?' 67 | switches[1][2] = get_new_word(switch2) 68 | when '3' 69 | switch3 = switches[2][2].nil? ? switches[2][1] : switches[2][2] 70 | puts "Switch 3: #{switch3}" 71 | puts 'What should the switch\'s word be set to?' 72 | switches[2][2] = get_new_word(switch3) 73 | when '4' 74 | switch4 = switches[3][2].nil? ? switches[3][1] : switches[3][2] 75 | puts "Switch 4: #{switch4}" 76 | puts 'What should the switch\'s word be set to?' 77 | switches[3][2] = get_new_word(switch4) 78 | when '5' 79 | switch5 = switches[4][2].nil? ? switches[4][1] : switches[4][2] 80 | puts "Switch 5: #{switch5}" 81 | puts 'What should the switch\'s word be set to?' 82 | switches[4][2] = get_new_word(switch5) 83 | when '6' 84 | switch6 = switches[5][2].nil? ? switches[5][1] : switches[5][2] 85 | puts "Switch 6: #{switch6}" 86 | puts 'What should the switch\'s word be set to?' 87 | switches[5][2] = get_new_word(switch6) 88 | when '7' 89 | pedestal_configured = true 90 | 91 | for i in 0..switches.length-1 do 92 | pedestal_configured = false unless switches[i][0].downcase.eql? switches[i][2].downcase 93 | end 94 | 95 | if pedestal_configured 96 | Audio.play_synth(:uncover_secret) 97 | puts 'You push the large gem into the pedestal and it descends without a hitch, almost as if it were meant to be. The pedestal begins to violently shake and a strong gust of wind picks you up off the ground. You feel completely taken aback and unsettled, but you have no choice: you are being whisked away somewhere into the sky, destination unknown.'.colorize(:yellow) 98 | 99 | # stats 100 | world.player.movements_made += 1 101 | 102 | Animation.run(phrase: USE_TEXT) 103 | return { type: 'move', data: 'Sky Tower (Entryway)' } 104 | else 105 | puts 'You attempt to push the large gem, but it puts up quite the resistance, and nothing much else happens. Your attention once again returns to the pedestal and its switches.'.colorize(:red) 106 | end 107 | when '8' 108 | puts 'You step away from the mysterious pedestal.' 109 | return { type: nil, data: nil } 110 | else 111 | next 112 | end 113 | end 114 | end 115 | 116 | private 117 | 118 | def print_switches(switches) 119 | puts 120 | 121 | switch1 = switches[0][2].nil? ? switches[0][1] : switches[0][2] 122 | puts "Switch 1: #{switch1}" 123 | 124 | switch2 = switches[1][2].nil? ? switches[1][1] : switches[1][2] 125 | puts "Switch 2: #{switch2}" 126 | 127 | switch3 = switches[2][2].nil? ? switches[2][1] : switches[2][2] 128 | puts "Switch 3: #{switch3}" 129 | 130 | switch4 = switches[3][2].nil? ? switches[3][1] : switches[3][2] 131 | puts "Switch 4: #{switch4}" 132 | 133 | switch5 = switches[4][2].nil? ? switches[4][1] : switches[4][2] 134 | puts "Switch 5: #{switch5}" 135 | 136 | switch6 = switches[5][2].nil? ? switches[5][1] : switches[5][2] 137 | puts "Switch 6: #{switch6}" 138 | end 139 | 140 | def jumble(word) 141 | word.split(//).sort_by { rand }.join('') 142 | end 143 | 144 | def get_new_word(switch) 145 | new_word = gets.chomp! 146 | if switch.downcase.split(//).sort == new_word.downcase.split(//).sort 147 | return new_word 148 | else 149 | puts 'Those letters are not in the original word, sorry.'.colorize(:red) 150 | return nil 151 | end 152 | end 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/pond.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/pond.rb 2 | # Entity::Item::Pond 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Pond < Item 8 | # CONTACTS 9 | NEEDED_ITEMS = ['dehumidifier', 'feather', 'gun', 'stalactite'] 10 | 11 | def initialize 12 | super 13 | 14 | self.name = 'pond' 15 | self.name_display = 'Pond' 16 | self.description = 'This tiny pool of water self-ripples every minute or so. Small, floating insects buzz around merrily. A small plaque lays at the foot, reading: "If the right objects curious doth possess, touch the water\'s surface and you\'ll get redress."' 17 | end 18 | 19 | def use(world) 20 | puts 'You gently place your fingers on the pond\'s rippling surface.' 21 | 22 | if (NEEDED_ITEMS - world.player.inventory.items.map(&:name)).empty? 23 | Audio.play_synth(:uncover_secret) 24 | puts 'The pond water explodes with a force that knocks you back onto the ground. When you come to, you notice the depression in the ground where the pond once was now has a new curious object!' 25 | self.description = 'A barren depression in the ground is all that is left of the pond.' 26 | return { type: 'item', data: 'Opalaser' } 27 | else 28 | puts 'You graze your fingers within the pond for a moment, feeling the coolness. You feel zen.' 29 | return { type: nil, data: nil } 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/red_key.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/red_key.rb 2 | # Entity::Item::RedKey 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class RedKey < Item 8 | attr_accessor :locked 9 | 10 | def initialize 11 | super 12 | 13 | self.name = 'red_key' 14 | self.name_display = 'Red Key' 15 | self.description = 'Redder than an embarrassed tomato in front of a moderate fire, but still very capable of being a key.' 16 | end 17 | 18 | def describe(world) 19 | super 20 | 21 | cur_location = world.location_by_coords(world.player.cur_coords) 22 | if cur_location.name.eql?('pain_quarry-west') 23 | return desc_text += ' The key "glows" quite intently.'.colorize(:red) 24 | elsif cur_location.name.include?('pain_quarry') 25 | return desc_text += ' The key "glows" almost so brightly you have to shield your eyes.'.colorize(:red) 26 | else 27 | return desc_text += ' The key lightly "glows".'.colorize(:red) 28 | end 29 | end 30 | 31 | def describe_detailed(world) 32 | desc_text = "\"#{name_display}\"\n".colorize(:yellow) 33 | desc_text << "(#{name})\n".colorize(:green) 34 | desc_text << "#{description}\n".colorize(:white) 35 | 36 | cur_location = world.location_by_coords(world.player.cur_coords) 37 | if cur_location.name.include?('pain_quarry') 38 | desc_text += "The key O==> *glows* quite intently.\n".colorize(:red) 39 | elsif cur_location.name.eql?('pain_quarry-west') 40 | desc_text += "The key O==> *shines* almost so brightly you have to shield your eyes.\n".colorize(:red) 41 | else 42 | desc_text += "The key O==> lightly *shimmers*.\n".colorize(:red) 43 | end 44 | 45 | desc_text << "TAKEABLE? #{takeable}\n".colorize(:white) 46 | desc_text << "USEABLE? #{useable}\n".colorize(:white) 47 | desc_text << "TALKABLE? #{talkable}\n".colorize(:white) 48 | desc_text << "CONSUMABLE? #{consumable}\n".colorize(:white) 49 | desc_text << "EQUIPPABLE? #{equippable}\n".colorize(:white) 50 | desc_text << "EQUIPPED? #{equipped}\n".colorize(:white) 51 | desc_text << "USED? #{used}\n".colorize(:white) 52 | desc_text << "USED AGAIN? #{used_again}\n".colorize(:white) 53 | desc_text << "USES LEFT? #{number_of_uses}\n".colorize(:white) unless number_of_uses.nil? 54 | desc_text 55 | end 56 | 57 | def use(world) 58 | cur_location = world.location_by_coords(world.player.cur_coords) 59 | 60 | if cur_location.contains_item?('locker') 61 | locker = cur_location.get_item_ref('locker') 62 | if locker.locked 63 | puts 'You place the reddish key into the lock on the locker. Moments later, after a quick turn, the locked locker is no longer locked.' 64 | 65 | locker.locked = false 66 | locker.description = 'A small, unlocked locker with a lock on it. You have unlocked it with a reddish key you found.' 67 | { type: nil, data: nil } 68 | else 69 | puts 'You place the reddish key into the lock on the locker. Moments later, after a quick turn, the locked locker is once again quite locked.' 70 | 71 | locker.locked = true 72 | locker.description = 'A small, locked locker with a lock on it. You will need to unlock it to gain access to its insides.' 73 | { type: nil, data: nil } 74 | end 75 | else 76 | puts 'You try to put the key into one of its natural habitats, but can\'t find anything suitable.' 77 | 78 | { type: nil, data: nil } 79 | end 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/rope.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/rope.rb 2 | # Entity::Item::Rope 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Rope < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'rope' 12 | self.name_display = 'Rope' 13 | self.description = 'For some reason, a sturdy rope hangs down from a small opening in the metal tunnel\'s ceiling. It appears to hold your weight when taut.' 14 | end 15 | 16 | def use(world) 17 | puts 'You hold on to the rope with both hands and begin to climb upwards towards the small opening in the ceiling.' 18 | puts 19 | 20 | puts 'After a few minutes you pull yourself up onto a field of pure driven snow. Without warning, the rope and opening in the floor vanish.' 21 | puts 22 | 23 | # stats 24 | world.player.movements_made += 1 25 | 26 | { type: 'move', data: 'Snow Fields (Southeast)' } 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/sand_jewel.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/sand_jewel.rb 2 | # Entity::Item::SandJewel 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class SandJewel < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'sand_jewel' 12 | self.name_display = 'Sand Jewel' 13 | self.description = 'As blue (or is it violet? or brown?) as it is brittle, this shiny rock feels warm to the touch.' 14 | end 15 | 16 | def use(world) 17 | puts 'You lift the sand jewel to the sky; rays of sunlight refract through it and nearly blind you.' 18 | { type: nil, data: nil } 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/shovel.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/shovel.rb 2 | # Entity::Item::Shovel 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Shovel < Item 8 | # CONSTANTS 9 | DIG_NOISE = '*DIG*' 10 | 11 | def initialize 12 | super 13 | 14 | self.name = 'shovel' 15 | self.name_display = 'Shovel' 16 | self.description = 'You can really "dig" this tool, despite its well-worn appearance.' 17 | end 18 | 19 | def use(world) 20 | cur_location = world.location_by_coords(world.player.cur_coords) 21 | 22 | if cur_location.name.eql?('pain_quarry-west') and cur_location.contains_item?('locker_corner') 23 | puts 'You bolster yourself and then begin the tedious job of digging the locker out of its several-inch-deep prison of sand with your trusty shovel.' 24 | puts 25 | 26 | Animation.run(phrase: DIG_NOISE) 27 | Animation.run(phrase: DIG_NOISE) 28 | Animation.run(phrase: DIG_NOISE) 29 | Animation.run(phrase: DIG_NOISE) 30 | Animation.run(phrase: DIG_NOISE) 31 | Animation.run(phrase: DIG_NOISE) 32 | Animation.run(phrase: DIG_NOISE) 33 | Animation.run(phrase: DIG_NOISE) 34 | 35 | puts 36 | puts 'You drop the head of the shovel into the ground, lean on it for a moment, and wipe the sweat from your brow. This quarry is really causing you considerable pain.' 37 | STDIN.getc 38 | 39 | Animation.run(phrase: DIG_NOISE) 40 | Animation.run(phrase: DIG_NOISE) 41 | Animation.run(phrase: DIG_NOISE) 42 | Animation.run(phrase: DIG_NOISE) 43 | Animation.run(phrase: DIG_NOISE) 44 | Animation.run(phrase: DIG_NOISE) 45 | Animation.run(phrase: DIG_NOISE) 46 | Animation.run(phrase: DIG_NOISE) 47 | 48 | puts 49 | puts 'After what feels like several hours, you finally unearth the locker from the ground, open it, and place it next to its previous location.' 50 | 51 | cur_location.add_item('locker') 52 | cur_location.remove_item('locker_corner') 53 | 54 | { type: 'dmg', data: rand(1..2) } 55 | else 56 | puts 'You grip the shovel by its handle and thrust it, head-first, into the sky. Huzzah!' 57 | 58 | { type: nil, data: nil } 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/small_hole.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/small_hole.rb 2 | # Entity::Item::SmallHole 3 | 4 | require_relative '../person' 5 | 6 | module Gemwarrior 7 | class SmallHole < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'small_hole' 12 | self.name_display = 'Small Hole' 13 | self.description = 'Amongst the rubble of the alcove, a small hole, barely big enough for a rodent, exists in an absently-minded way near the bottom of the wall.' 14 | end 15 | 16 | def use(world) 17 | if !self.used 18 | self.used = true 19 | 20 | Audio.play_synth(:uncover_secret) 21 | puts 'You lower yourself to the ground and attempt to peer in the hole in the wall. Just as you begin to think this is a fruitless endeavor, a pair of bright, beady eyes manifest, and an unexpectedly low voice speaks:' 22 | Person.new.speak('Hello. I\'m Rockney, of Rockney\'s Hole in the Wall. Pleasure!') 23 | 24 | tunnel_alcove = world.location_by_name('tunnel_alcove') 25 | tunnel_alcove.items.push(Rockney.new) 26 | puts world.describe(tunnel_alcove) 27 | else 28 | puts 'Rockney appears to still be in the small hole, patiently waiting for you.' 29 | end 30 | 31 | { type: nil, data: nil } 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/snowman.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/snowman.rb 2 | # Entity::Item::Snowman 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Snowman < Item 8 | # CONSTANTS 9 | USE_TEXT = '** FOOOOSH **' 10 | 11 | def initialize 12 | super 13 | 14 | self.name = 'snowman' 15 | self.name_display = 'Snowman' 16 | self.description = 'Standing solemnly in the snow, a man of snow solemnly stands.' 17 | end 18 | 19 | def use(world) 20 | puts 'You go to touch the snowy softness of the snowman when it magically comes to life! The frozen homunculus grabs you by the wrist and tosses you to the ground, only to follow this up by jumping onto you with its full, freezing, force. Your body, and mind, go numb.' 21 | puts 22 | 23 | Animation.run(phrase: USE_TEXT) 24 | 25 | { type: 'move_dangerous', data: 'Home' } 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/sparkly_thing.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/sparklything.rb 2 | # Entity::Item::SparklyThing 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class SparklyThing < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'sparkly_thing' 12 | self.name_display = 'SparklyThing(tm)' 13 | self.description = 'The sparkling that this thing does is unimaginably brilliant.' 14 | self.takeable = true 15 | end 16 | 17 | def use(world) 18 | puts 'Everything, and I mean *everything*, begins to sparkle. Huh.' 19 | { type: nil, data: nil } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/stonemite.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/stonemite.rb 2 | # Entity::Item::Stonemite 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Stonemite < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'stonemite' 12 | self.name_display = 'Stonemite' 13 | self.description = 'Stubby cave debris that is neat to look at, as it is off-grey and sparkly, but the size makes it unusable as anything but skipping on a lake.' 14 | self.takeable = true 15 | end 16 | 17 | def use(world) 18 | puts 'You turn the stonemite over and over in your hand. It continues to be a small rock of little import.' 19 | { type: nil, data: nil } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/tent.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/tent.rb 2 | # Entity::Item::Tent 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Tent < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'tent' 12 | self.name_display = 'Tent' 13 | self.description = 'A magical, two-room suite pops up when you flick this otherwise folded piece of canvas just right, perfect for a night\'s rest.' 14 | self.takeable = true 15 | self.number_of_uses = 5 16 | end 17 | 18 | def use(world) 19 | { type: 'tent', data: self.number_of_uses } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/throne.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/throne.rb 2 | # Entity::Item::Throne 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Throne < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'throne' 12 | self.name_display = 'Throne' 13 | self.description = 'Made of what appears to be unfulfilled desires and latent, flawed happiness, the well-crafted seat still looks kinda comfy. The wizard Emerald sits in it, glaring at you.' 14 | end 15 | 16 | def use(world) 17 | puts 'Your words fall on deaf chairs. Emerald continues to stare at you.' 18 | { type: nil, data: nil } 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/tree.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/tree.rb 2 | # Entity::Item::Tree 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Tree < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'tree' 12 | self.name_display = 'Tree' 13 | self.description = 'A mighty representation of nature, older than your father\'s father\'s second great-uncle.' 14 | end 15 | 16 | def use(world) 17 | if self.used_again 18 | puts 'The tree, its trunk, and the hole in its side all seem fairly unremarkable to you now.' 19 | 20 | return { type: nil, data: nil } 21 | elsif self.used 22 | self.used_again = true 23 | 24 | Audio.play_synth(:uncover_secret) 25 | 26 | puts 'Looking further into the small opening in the trunk your eye catches the light glinting off a few small metallic objects.' 27 | 28 | cur_loc = world.location_by_name('forest-southeast') 29 | cur_loc.items.push(Bullet.new) 30 | cur_loc.items.push(Bullet.new) 31 | cur_loc.items.push(Bullet.new) 32 | 33 | return { type: nil, data: nil } 34 | else 35 | self.used = true 36 | 37 | puts 'Taking a passing glance into the only hole in the tree big enough for anything to exist inside, you don\'t quite see anything of value.' 38 | 39 | { type: nil, data: nil } 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/items/waterfall.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/items/waterfall.rb 2 | # Entity::Item::Waterfall 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Waterfall < Item 8 | def initialize 9 | super 10 | 11 | self.name = 'waterfall' 12 | self.name_display = 'Waterfall' 13 | self.description = 'Gallons of murky, sparkling water fall downward from an unknown spot in the sky, ending in a pool on the ground, yet never overflowing.' 14 | end 15 | 16 | def use(world) 17 | puts 'You stretch out your hand and touch the waterfall. It stings you with its cold and forceful gushing. Your hand is now wet and rougher than before. In time, it will dry.' 18 | 19 | dmg = rand(0..1) 20 | 21 | puts '>> You lose a hit point.'.colorize(:red) if dmg > 0 22 | 23 | { type: 'dmg', data: dmg } 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/location.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/location.rb 2 | # Entity::Location 3 | # Place in the game 4 | 5 | require_relative 'entity' 6 | require_relative '../game_options' 7 | 8 | module Gemwarrior 9 | class Location < Entity 10 | # CONSTANTS 11 | DANGER_LEVEL = { none: 0, low: 15, moderate: 30, high: 55, assured: 100 } 12 | ERROR_ITEM_ADD_INVALID = 'That item cannot be added to the location\'s inventory.' 13 | ERROR_ITEM_REMOVE_INVALID = 'That item cannot be removed as it does not exist here.' 14 | ERROR_ITEM_REF_INVALID = 'That item cannot be accessed.' 15 | ERROR_SPAWN_NAME_INVALID = 'That monster does not exist in the game, and cannot be spawned.' 16 | 17 | attr_accessor :coords, 18 | :paths, 19 | :danger_level, 20 | :monster_level_range, 21 | :items, 22 | :monsters_abounding, 23 | :bosses_abounding, 24 | :checked_for_monsters, 25 | :visited 26 | 27 | def initialize(options) 28 | self.name = options.fetch(:name) 29 | self.description = options.fetch(:description) 30 | self.coords = options.fetch(:coords) 31 | self.paths = options.fetch(:paths) 32 | self.danger_level = options.fetch(:danger_level) 33 | self.monster_level_range = options.fetch(:monster_level_range) 34 | self.items = options.fetch(:items) 35 | self.monsters_abounding = options.fetch(:monsters_abounding) 36 | self.bosses_abounding = options.fetch(:bosses_abounding) 37 | self.checked_for_monsters = false 38 | self.visited = false 39 | end 40 | 41 | def describe 42 | desc_text = name.ljust(30).upcase.colorize(:green) 43 | desc_text << coords.values.to_a.to_s.colorize(:white) 44 | desc_text << " DL[#{danger_level.to_s.ljust(8)}] ".colorize(:white) if GameOptions.data['debug_mode'] 45 | desc_text << " MLR[#{monster_level_range.to_s.ljust(6)}] ".colorize(:white) if GameOptions.data['debug_mode'] 46 | desc_text << "\n" 47 | desc_text << "#{description}".colorize(:white) 48 | desc_text 49 | end 50 | 51 | def describe_detailed 52 | desc_text = "#{name_display.ljust(36).colorize(:yellow)} #{coords.values.to_a.to_s.colorize(:white)}\n" 53 | desc_text << "(#{name})\n".colorize(:green) 54 | desc_text << "#{description}\n".colorize(:white) 55 | desc_text << "DANGER_LEVEL : #{danger_level}\n".colorize(:white) 56 | desc_text << "MONSTER_LEVEL_RANGE: #{monster_level_range}\n".colorize(:white) 57 | desc_text 58 | end 59 | 60 | def contains_item?(item_name) 61 | self.items.map{|i| i.name.downcase}.include?(item_name.downcase) 62 | end 63 | 64 | def has_any_monsters? 65 | monsters_abounding.length > 0 66 | end 67 | 68 | def has_monster?(monster_name) 69 | monsters_abounding.map{|m| m.name.downcase}.include?(monster_name.downcase) 70 | end 71 | 72 | def has_boss?(boss_name) 73 | bosses_abounding.map{|b| b.name.downcase}.include?(boss_name.downcase) 74 | end 75 | 76 | def get_item_ref(item_name) 77 | all_items = GameItems.data | GameWeapons.data | GameArmor.data 78 | all_items.each do |game_item| 79 | if game_item.name.eql?(item_name) 80 | item_ref = self.items.find{ |i| i.name.downcase == item_name.downcase } 81 | return item_ref 82 | end 83 | end 84 | return ERROR_ITEM_REF_INVALID 85 | end 86 | 87 | def add_item(item_name_to_add) 88 | all_items = GameItems.data | GameWeapons.data | GameArmor.data 89 | all_items.each do |game_item| 90 | if game_item.name.eql?(item_name_to_add) 91 | self.items.push(game_item) 92 | return 93 | end 94 | end 95 | return ERROR_ITEM_ADD_INVALID 96 | end 97 | 98 | def remove_item(item_name) 99 | if contains_item?(item_name) 100 | self.items.delete_at(items.index(items.find { |i| i.name.downcase == item_name.downcase })) 101 | else 102 | ERROR_ITEM_REMOVE_INVALID 103 | end 104 | end 105 | 106 | def remove_monster(name) 107 | monsters_abounding.reject! { |monster| monster.name == name } 108 | bosses_abounding.reject! { |boss| boss.name == name } 109 | end 110 | 111 | def has_loc_to_the?(direction) 112 | case direction 113 | when 'n' 114 | direction = 'north' 115 | when 'e' 116 | direction = 'east' 117 | when 's' 118 | direction = 'south' 119 | when 'w' 120 | direction = 'west' 121 | end 122 | paths[direction.to_sym] 123 | end 124 | 125 | def monster_by_name(monster_name) 126 | monsters_list = monsters_abounding | bosses_abounding 127 | 128 | monsters_list.each do |m| 129 | if m.name.downcase.eql?(monster_name.downcase) 130 | return m.clone 131 | end 132 | end 133 | end 134 | 135 | def checked_for_monsters? 136 | checked_for_monsters 137 | end 138 | 139 | def should_spawn_monster? 140 | found = false 141 | unless danger_level.eql?(:none) 142 | max = DANGER_LEVEL[danger_level] 143 | trigger_values = 0..max 144 | actual_value = rand(1..100) 145 | 146 | if trigger_values.include?(actual_value) 147 | found = true 148 | end 149 | end 150 | return found 151 | end 152 | 153 | def list_items 154 | if items.empty? 155 | [] 156 | else 157 | # build hash out of location's items 158 | item_hash = {} 159 | self.items.map(&:name).each do |i| 160 | i_sym = i.to_sym 161 | if item_hash.keys.include? i_sym 162 | item_hash[i_sym] += 1 163 | else 164 | item_hash[i_sym] = 1 165 | end 166 | end 167 | 168 | # one item? return single element array 169 | if item_hash.length == 1 170 | i = item_hash.keys 171 | q = item_hash.values.join.to_i 172 | return q > 1 ? ["#{q} #{i}s"] : i 173 | # multiple items? build an array of strings 174 | else 175 | item_arr = [] 176 | item_hash.each do |i, q| 177 | if q > 1 178 | item_arr.push("#{i.to_s.colorize(:yellow)}#{'s'.colorize(:yellow)} x#{q}") 179 | else 180 | item_arr.push(i) 181 | end 182 | end 183 | 184 | return item_arr 185 | end 186 | end 187 | end 188 | 189 | def list_monsters 190 | monsters_abounding.length > 0 ? monsters_abounding.map(&:name) : [] 191 | end 192 | 193 | def list_bosses 194 | bosses_abounding.length > 0 ? bosses_abounding.map(&:name_display) : [] 195 | end 196 | 197 | def list_paths 198 | valid_paths = [] 199 | self.paths.each do |key, value| 200 | if value 201 | valid_paths.push(key.to_s) 202 | end 203 | end 204 | return valid_paths 205 | end 206 | 207 | def list_actionable_words 208 | actionable_words = [] 209 | actionable_words.push(monsters_abounding.map(&:name)) unless monsters_abounding.empty? 210 | actionable_words.push(bosses_abounding.map(&:name)) unless bosses_abounding.empty? 211 | actionable_words.push(items.map(&:name)) unless items.empty? 212 | actionable_words.join(', ') 213 | end 214 | 215 | def populate_monsters(monsters_available = nil, spawn = false, monster_type = nil) 216 | # debug spawn 217 | if spawn 218 | random_monster = nil 219 | 220 | # debug spawn random monster 221 | if monster_type.nil? 222 | loop do 223 | random_monster = monsters_available[rand(0..monsters_available.length-1)].clone 224 | 225 | monsters_abounding.push(random_monster) 226 | break 227 | end 228 | # debug spawn w/ monster_type 229 | else 230 | monster_type_to_spawn = monsters_available.find { |m| m.name.downcase == monster_type.downcase } 231 | if monster_type_to_spawn.nil? 232 | puts ERROR_SPAWN_NAME_INVALID.colorize(:red) 233 | puts 234 | else 235 | monsters_abounding.push(monster_type_to_spawn) 236 | end 237 | end 238 | # normal location spawn 239 | elsif should_spawn_monster? 240 | self.checked_for_monsters = true 241 | self.monsters_abounding = [] 242 | random_monster = nil 243 | 244 | # get random non-boss monster 245 | loop do 246 | random_monster = monsters_available[rand(0..monsters_available.length-1)].clone 247 | 248 | if !random_monster.is_boss && self.monster_level_range.include?(random_monster.level) 249 | monsters_abounding.push(random_monster) 250 | break 251 | end 252 | end 253 | else 254 | return nil 255 | end 256 | end 257 | end 258 | end 259 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monster.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monster.rb 2 | # Entity::Creature::Monster 3 | 4 | require_relative 'creature' 5 | require_relative 'items/herb' 6 | require_relative 'items/bullet' 7 | 8 | module Gemwarrior 9 | class Monster < Creature 10 | ITEM_POOL = [Herb.new, Bullet.new] 11 | 12 | attr_accessor :battlecry, 13 | :is_boss, 14 | :is_dead 15 | 16 | def initialize 17 | super 18 | 19 | self.inventory = Inventory.new 20 | self.useable = true 21 | self.talkable = true 22 | self.is_dead = false 23 | 3.times do 24 | if [true, false].sample 25 | self.inventory.add_item(ITEM_POOL[rand(0..ITEM_POOL.length-1)]) 26 | end 27 | end 28 | end 29 | 30 | def describe_detailed 31 | desc_text = "\"#{name_display}\"".colorize(:yellow) 32 | desc_text << '(BOSS)'.ljust(13).colorize(:yellow) if is_boss 33 | desc_text << "\n" 34 | desc_text << "(#{name})\n".colorize(:green) 35 | desc_text << "#{description}\n".colorize(:white) 36 | desc_text << "FACE : #{face}\n".colorize(:white) 37 | desc_text << "HANDS: #{hands}\n".colorize(:white) 38 | desc_text << "MOOD : #{mood}\n".colorize(:white) 39 | desc_text << "LVL : #{level}\n".colorize(:white) 40 | desc_text << "HP : #{hp_cur}/#{hp_max}\n".colorize(:white) 41 | desc_text << "ATK : #{atk_lo}-#{atk_hi}\n".colorize(:white) 42 | desc_text << "DEF : #{defense}\n".colorize(:white) 43 | desc_text << "DEX : #{dexterity}\n".colorize(:white) 44 | desc_text << "ROX : #{rox}\n".colorize(:white) 45 | desc_text << "XP : #{xp}\n".colorize(:white) 46 | desc_text << "INV : #{inventory.contents}\n".colorize(:white) 47 | desc_text << "DEAD? #{is_dead}\n".colorize(:white) 48 | desc_text << "TALK? #{talkable}\n".colorize(:white) 49 | desc_text << "USE? #{useable}\n".colorize(:white) 50 | desc_text 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/alexandrat.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/alexandrat.rb 2 | # Entity::Creature::Monster::Alexandrat 3 | 4 | require_relative '../monster' 5 | 6 | module Gemwarrior 7 | class Alexandrat < Monster 8 | def initialize 9 | super 10 | 11 | self.name = 'alexandrat' 12 | self.name_display = 'Alexandrat' 13 | self.description = 'Tiny, but fierce, color-changing rodent.' 14 | self.battlecry = 'Bitey, bitey!' 15 | self.face = 'ugly' 16 | self.hands = 'gnarled' 17 | self.mood = 'unchipper' 18 | 19 | self.level = rand(1..2) 20 | self.hp_cur = rand((level * 2)..(level * 3)) 21 | self.hp_max = hp_cur 22 | self.atk_lo = rand(level..(level * 1.5).floor) 23 | self.atk_hi = rand((level * 1.5).floor..(level * 2)) 24 | self.defense = rand(1..2) 25 | self.dexterity = rand(1..3) 26 | 27 | self.rox = rand((level * 2)..(level * 3)) 28 | self.xp = rand(level..(level * 2)) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/amberoo.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/amberoo.rb 2 | # Entity::Creature::Monster::Amberoo 3 | 4 | require_relative '../monster' 5 | 6 | module Gemwarrior 7 | class Amberoo < Monster 8 | def initialize 9 | super 10 | 11 | self.name = 'amberoo' 12 | self.name_display = 'Amberoo' 13 | self.description = 'Fossilized and jumping around like an adorably dangerous threat from the past.' 14 | self.battlecry = 'I\'m hoppin\' mad!' 15 | self.face = 'punchy' 16 | self.hands = 'balled' 17 | self.mood = 'jumpy' 18 | 19 | self.level = rand(1..2) 20 | self.hp_cur = rand((level * 2)..(level * 3)) 21 | self.hp_max = hp_cur 22 | self.atk_lo = rand(level..(level * 2)) 23 | self.atk_hi = rand((level * 2)..(level * 2.5).floor) 24 | self.defense = rand(2..3) 25 | self.dexterity = rand(2..4) 26 | 27 | self.rox = rand((level * 2)..(level * 3)) 28 | self.xp = rand(level..(level * 2)) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/amethystle.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/amethystle.rb 2 | # Entity::Creature::Monster::Amethystle 3 | 4 | require_relative '../monster' 5 | 6 | module Gemwarrior 7 | class Amethystle < Monster 8 | def initialize 9 | super 10 | 11 | self.name = 'amethystle' 12 | self.name_display = 'Amethystle' 13 | self.description = 'Sober and contemplative, it moves with purplish tentacles swaying in the breeze.' 14 | self.battlecry = 'You\'ve found yourself in quite the thorny issue!' 15 | self.face = 'sharp' 16 | self.hands = 'loose' 17 | self.mood = 'mesmerizing' 18 | 19 | self.level = rand(2..3) 20 | self.hp_cur = rand((level * 2)..(level * 3)) 21 | self.hp_max = hp_cur 22 | self.atk_lo = rand(level..(level * 1.5).floor) 23 | self.atk_hi = rand((level * 1.5).floor..(level * 2.5).floor) 24 | self.defense = rand(2..4) 25 | self.dexterity = rand(1..2) 26 | 27 | self.rox = rand((level * 2)..(level * 3)) 28 | self.xp = rand(level..(level * 2)) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/apatiger.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/apatiger.rb 2 | # Entity::Creature::Monster::Apatiger 3 | 4 | require_relative '../monster' 5 | 6 | module Gemwarrior 7 | class Apatiger < Monster 8 | def initialize 9 | super 10 | 11 | self.name = 'apatiger' 12 | self.name_display = 'Apatiger' 13 | self.description = 'Apathetic about most everything as it lazes around, save for eating you.' 14 | self.battlecry = 'Gggggggggrrrrrrrrrrrrrrrrooooooooooowwwwwwwwwwwwlllllllll!' 15 | self.face = 'calloused' 16 | self.hands = 'soft' 17 | self.mood = 'apathetic' 18 | 19 | self.level = rand(4..5) 20 | self.hp_cur = rand((level * 2)..(level * 3)) 21 | self.hp_max = hp_cur 22 | self.atk_lo = rand(level..(level * 1.5).floor) 23 | self.atk_hi = rand((level * 1.5).floor..(level * 2.5).floor) 24 | self.defense = rand(4..7) 25 | self.dexterity = rand(3..7) 26 | 27 | self.rox = rand((level * 2)..(level * 3)) 28 | self.xp = rand(level..(level * 2)) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/aquamarine.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/aquamarine.rb 2 | # Entity::Creature::Monster::Aquamarine 3 | 4 | require_relative '../monster' 5 | 6 | module Gemwarrior 7 | class Aquamarine < Monster 8 | def initialize 9 | super 10 | 11 | self.name = 'aquamarine' 12 | self.name_display = 'Aquamarine' 13 | self.description = 'It is but one of the few, the proud, the underwater.' 14 | self.battlecry = 'Attention! You are about to get smashed!' 15 | self.face = 'strained' 16 | self.hands = 'hairy' 17 | self.mood = 'tempered' 18 | 19 | self.level = rand(3..4) 20 | self.hp_cur = rand((level * 2)..(level * 3)) 21 | self.hp_max = hp_cur 22 | self.atk_lo = rand(level..(level * 1.5).floor) 23 | self.atk_hi = rand((level * 1.5).floor..(level * 2.5).floor) 24 | self.defense = rand(3..5) 25 | self.dexterity = rand(4..6) 26 | 27 | self.rox = rand((level * 2)..(level * 3)) 28 | self.xp = rand(level..(level * 2)) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/bloodstorm.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/bloodstorm.rb 2 | # Entity::Creature::Monster::Bloodstorm 3 | 4 | require_relative '../monster' 5 | 6 | module Gemwarrior 7 | class Bloodstorm < Monster 8 | def initialize 9 | super 10 | 11 | self.name = 'bloodstorm' 12 | self.name_display = 'Bloodstorm' 13 | self.description = 'A literal swirling, maniacal vortex of human hemoglobin.' 14 | self.battlecry = '/swirls' 15 | self.face = 'bloody' 16 | self.hands = 'bloody' 17 | self.mood = 'boiling' 18 | 19 | self.level = rand(5..6) 20 | self.hp_cur = rand((level * 2)..(level * 3)) 21 | self.hp_max = hp_cur 22 | self.atk_lo = rand(level..(level * 1.5).floor) 23 | self.atk_hi = rand((level * 1.5).floor..(level * 3).floor) 24 | self.defense = rand(6..7) 25 | self.dexterity = rand(5..7) 26 | 27 | self.rox = rand((level * 2)..(level * 3)) 28 | self.xp = rand(level..(level * 2)) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/bosses/emerald.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/bosses/emerald.rb 2 | # Entity::Creature::Monster::Emerald (BOSS) 3 | 4 | require_relative '../../monster' 5 | require_relative '../../items/sparkly_thing' 6 | 7 | module Gemwarrior 8 | class Emerald < Monster 9 | # CONSTANTS 10 | MOVE_TEXT = '** WHOOOOOOSH **' 11 | 12 | def initialize 13 | super 14 | 15 | self.name = 'emerald' 16 | self.name_display = 'Emerald' 17 | self.description = 'A wily, beefy, tower of a man, Emerald looks to be a champion of both wisdom, from what you\'ve heard, AND strength, from what you plainly see. He sports a constant glint in his eyes as he faces you, dead-on.' 18 | self.battlecry = 'You\'ve come for the SparklyThing(tm), I see. Well, you cannot have it, because it is mine, and I will wield it to my own desire! Oh, you think you are good and I am evil? To that I say: Ha ha ha ha ha! Prepare yourself fool: today your whole life crumbles!' 19 | self.face = 'gleaming' 20 | self.hands = 'tantalizing' 21 | self.mood = 'enraged' 22 | 23 | self.level = 15 24 | self.hp_cur = rand((level * 8)..(level * 9)) 25 | self.hp_max = hp_cur 26 | self.atk_lo = rand((level * 2)..(level * 2.5).floor) 27 | self.atk_hi = rand((level * 2.5).floor..(level * 3).floor) 28 | self.defense = rand(10..12) 29 | self.dexterity = rand(12..14) 30 | 31 | self.inventory = Inventory.new(items = [SparklyThing.new]) 32 | self.rox = rand((level * 9)..(level * 11)) 33 | self.xp = rand((level * 13)..(level * 15)) 34 | 35 | self.is_boss = true 36 | end 37 | 38 | def initiate_ending(world) 39 | # fanfare! 40 | Audio.play_synth(:win_game) 41 | 42 | # get reference to throne room, emerald monster, and throne item 43 | throne_room = world.location_by_coords(world.location_coords_by_name('Sky Tower (Throne Room)')) 44 | emerald = throne_room.bosses_abounding[0] 45 | throne = throne_room.items.each { |i| i.name == 'throne' }[0] 46 | 47 | # emerald is dead, so room and items change 48 | emerald.is_dead = true 49 | emerald.face = 'sullen' 50 | emerald.hands = 'limp' 51 | emerald.mood = 'blank' 52 | emerald.hp_cur = 0 53 | emerald.rox = 0 54 | emerald.description = 'Emerald slumps over in his chair, completely lifeless. The color has drained from his eyes and a pallor has been cast over his face. His reign is no more.' 55 | throne_room.description = 'Emerald\'s throne room feels much less imposing now that you have defeated him in mortal combat. Who knew?' 56 | throne_room.danger_level = :none 57 | throne_room.monster_level_range = nil 58 | throne_room.monsters_abounding = [] 59 | throne.description = 'The grim spectacle of Emerald\'s dead body drooping blankly in his chosen seat really brings down the market value of it, you guess.' 60 | 61 | # world knows you beat emerald 62 | world.emerald_beaten = true 63 | 64 | # remove sparkly_thing from emerald 65 | emerald.inventory.remove_item('sparkly_thing') 66 | # give player sparkly_thing 67 | world.player.inventory.items.push(SparklyThing.new) 68 | 69 | # ending text 70 | puts '<^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^>' 71 | puts "You beat #{name}! You win! You receive the #{"SparklyThing(tm)".colorize(:magenta)} and become the true #{"Gem Warrior".colorize(:yellow)}!" 72 | puts '<^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^><^>' 73 | STDIN.getc 74 | 75 | puts 'Suddenly, an icy feeling shoots up your back. You feel lighter than air. Blackness takes over.' 76 | STDIN.getc 77 | 78 | # onto the queen 79 | Animation.run(phrase: MOVE_TEXT) 80 | puts 81 | 82 | return { type: 'move', data: 'Queen Room'} 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/bosses/garynetty.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/bosses/garynetty.rb 2 | # Entity::Creature::Monster::Garrynetty (BOSS) 3 | 4 | require_relative '../../monster' 5 | require_relative '../../items/tent' 6 | 7 | module Gemwarrior 8 | class Garynetty < Monster 9 | def initialize 10 | super 11 | 12 | self.name = 'garynetty' 13 | self.name_display = 'Garynetty' 14 | self.description = 'Conservative, yet odd, the Garynetty is not messing around.' 15 | self.battlecry = '...?!' 16 | self.face = 'irregular' 17 | self.hands = 'sharp' 18 | self.mood = 'abrasive' 19 | 20 | self.level = rand(10..12) 21 | self.hp_cur = rand((level * 4.5).floor..(level * 5.5).floor) 22 | self.hp_max = hp_cur 23 | self.atk_lo = rand((level * 2)..(level * 2.5).floor) 24 | self.atk_hi = rand((level * 2.5).floor..(level * 3).floor) 25 | self.defense = rand(7..9) 26 | self.dexterity = rand(10..12) 27 | 28 | self.inventory = random_item 29 | self.rox = rand((level * 5)..(level * 6)) 30 | self.xp = rand((level * 7)..(level * 11)) 31 | 32 | self.is_boss = true 33 | end 34 | 35 | private 36 | 37 | def random_item 38 | if [true, false].sample 39 | Inventory.new(items = [Tent.new]) 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/bosses/jaspern.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/bosses/jaspern.rb 2 | # Entity::Creature::Monster::Jaspern (BOSS) 3 | 4 | require_relative '../../monster' 5 | require_relative '../../items/tent' 6 | 7 | module Gemwarrior 8 | class Jaspern < Monster 9 | def initialize 10 | super 11 | 12 | self.name = 'jaspern' 13 | self.name_display = 'Jaspern' 14 | self.description = 'Dark green hair, but yellow and brown skin, Jaspern is actually somewhat translucent, and he does not appear to be moveable or go-around-able.' 15 | self.battlecry = 'I am the keeper of this bridge! To go further, you must get through me!' 16 | self.face = 'crystalline' 17 | self.hands = 'small' 18 | self.mood = 'opaque' 19 | 20 | self.level = rand(7..8) 21 | self.hp_cur = rand((level * 4.5).floor..(level * 5.5).floor) 22 | self.hp_max = hp_cur 23 | self.atk_lo = rand((level * 2)..(level * 2.5).floor) 24 | self.atk_hi = rand((level * 2.5).floor..(level * 3).floor) 25 | self.defense = rand(5..7) 26 | self.dexterity = rand(8..9) 27 | 28 | self.inventory = random_item 29 | self.rox = rand((level * 6)..(level * 7)) 30 | self.xp = rand((level * 8)..(level * 10)) 31 | 32 | self.is_boss = true 33 | end 34 | 35 | def use(world) 36 | speak('You are not going any further north, little man. No matter of talking will move me!') 37 | end 38 | 39 | def river_bridge_success(world) 40 | # get object references 41 | river_bridge = world.location_by_name('river_bridge') 42 | jaspern = river_bridge.bosses_abounding[0] 43 | 44 | # mark jaspern as dead 45 | jaspern.is_dead = true 46 | jaspern.face = 'broken' 47 | jaspern.hands = 'limp' 48 | jaspern.mood = 'defeated' 49 | jaspern.hp_cur = 0 50 | jaspern.rox = 0 51 | jaspern.description = 'As Jaspern lies motionless upon the ground, you feel sorry for murdering him in cold blood without any provocation, but you feel like he was probably bad, and you really needed to explore further north. Regardless, it looks like you own the bridge now.' 52 | jaspern.inventory = Inventory.new 53 | 54 | # unlock northward travel 55 | river_bridge.description = 'The path northward on this well-constructed bridge is no longer blocked after your brutal scuffle with Jaspern, and yet the flowing river below seems unperturbed.' 56 | river_bridge.paths[:north] = true 57 | return 58 | end 59 | 60 | private 61 | 62 | def random_item 63 | if [true, false].sample 64 | Inventory.new(items = [Tent.new]) 65 | else 66 | Inventory.new 67 | end 68 | end 69 | end 70 | end -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/citrinaga.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/citrinaga.rb 2 | # Entity::Creature::Monster::Citrinaga 3 | 4 | require_relative '../monster' 5 | 6 | module Gemwarrior 7 | class Citrinaga < Monster 8 | def initialize 9 | super 10 | 11 | self.name = 'citrinaga' 12 | self.name_display = 'Citrinaga' 13 | self.description = 'Refreshing in its shiny, gleaming effectiveness at ending your life.' 14 | self.battlecry = 'Slice and dice so nice!' 15 | self.face = 'shiny' 16 | self.hands = 'glistening' 17 | self.mood = 'staid' 18 | 19 | self.level = rand(5..7) 20 | self.hp_cur = rand((level * 2)..(level*3)) 21 | self.hp_max = hp_cur 22 | self.atk_lo = rand(level..(level*1.5).floor) 23 | self.atk_hi = rand((level*1.5).floor..(level*3).floor) 24 | self.defense = rand(7..9) 25 | self.dexterity = rand(6..7) 26 | 27 | self.rox = rand((level * 2)..(level * 3)) 28 | self.xp = rand(level..(level * 3)) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/coraliz.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/coraliz.rb 2 | # Entity::Creature::Monster::Coraliz 3 | 4 | require_relative '../monster' 5 | 6 | module Gemwarrior 7 | class Coraliz < Monster 8 | def initialize 9 | super 10 | 11 | self.name = 'coraliz' 12 | self.name_display = 'Coraliz' 13 | self.description = 'Small blue lizard that slithers around, nipping at your ankles.' 14 | self.battlecry = 'Where am I? You\'ll never guess!' 15 | self.face = 'spotted' 16 | self.hands = 'slippery' 17 | self.mood = 'emotionless' 18 | 19 | self.level = rand(5..8) 20 | self.hp_cur = rand((level * 2)..(level*3)) 21 | self.hp_max = hp_cur 22 | self.atk_lo = rand(level..(level * 3).floor) 23 | self.atk_hi = rand((level * 3).floor..(level * 3).floor) 24 | self.defense = rand(4..6) 25 | self.dexterity = rand(7..9) 26 | 27 | self.rox = rand((level * 2)..(level * 3)) 28 | self.xp = rand(level..(level * 3)) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/cubicat.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/cubicat.rb 2 | # Entity::Creature::Monster::Cubicat 3 | 4 | require_relative '../monster' 5 | 6 | module Gemwarrior 7 | class Cubicat < Monster 8 | def initialize 9 | super 10 | 11 | self.name = 'cubicat' 12 | self.name_display = 'Cubicat' 13 | self.description = 'Perfectly geometrically cubed feline, fresh from its woven enclosure, claws at the ready.' 14 | self.battlecry = 'I don\'t really care, as long as you die!' 15 | self.face = 'striking' 16 | self.hands = 'grippy' 17 | self.mood = 'salacious' 18 | 19 | self.level = rand(6..8) 20 | self.hp_cur = rand((level * 2)..(level * 3)) 21 | self.hp_max = hp_cur 22 | self.atk_lo = rand(level..(level * 2).floor) 23 | self.atk_hi = rand((level * 2).floor..(level * 3).floor) 24 | self.defense = rand(5..7) 25 | self.dexterity = rand(8..10) 26 | 27 | self.rox = rand((level * 2)..(level * 3)) 28 | self.xp = rand(level..(level * 2)) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/monsters/diaman.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/monsters/diaman.rb 2 | # Entity::Creature::Monster::Diaman 3 | 4 | require_relative '../monster' 5 | 6 | module Gemwarrior 7 | class Diaman < Monster 8 | def initialize 9 | super 10 | 11 | self.name = 'diaman' 12 | self.name_display = 'Diaman' 13 | self.description = 'Crystalline structure in the form of a man, lumbering toward you, with outstretched, edged pincers.' 14 | self.battlecry = 'Precious human, prepare to be lost to the annals of time!' 15 | self.face = 'bright' 16 | self.hands = 'jagged' 17 | self.mood = 'adamant' 18 | 19 | self.level = rand(8..10) 20 | self.hp_cur = rand((level * 2)..(level * 3)) 21 | self.hp_max = hp_cur 22 | self.atk_lo = rand(level..(level * 2.5).floor) 23 | self.atk_hi = rand((level * 2.5).floor..(level * 3).floor) 24 | self.defense = rand(6..7) 25 | self.dexterity = rand(8..10) 26 | 27 | self.rox = rand((level * 2)..(level * 3)) 28 | self.xp = rand(level..(level * 2)) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/people/arena_master.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/people/arena_master.rb 2 | # Entity::Creature::Person::ArenaMaster 3 | 4 | require_relative '../person' 5 | 6 | module Gemwarrior 7 | class ArenaMaster < Person 8 | # CONSTANTS 9 | ARENA_FEE = 50 10 | ARENA_MASTER_NAME = 'Iolita' 11 | 12 | def initialize 13 | super 14 | 15 | self.name = 'arena_master' 16 | self.name_display = 'Arena Master' 17 | self.description = 'She wears simple clothing, but carries herself with an air of authority. You think she may be the person to talk with if you want to engage in battle.' 18 | end 19 | 20 | def use(world) 21 | puts "You approach #{ARENA_MASTER_NAME.colorize(color: :white, background: :black)}, the Arena Master, and ask to prove your mettle in the arena. She snickers to herself, but sees you have a good spirit about you." 22 | puts 23 | 24 | if world.player.rox >= ARENA_FEE 25 | print "She asks for the requisite payment: #{ARENA_FEE} rox. Do you pay up? (y/n) " 26 | answer = gets.chomp.downcase 27 | 28 | case answer 29 | when 'y', 'yes' 30 | world.player.rox -= ARENA_FEE 31 | puts 32 | puts 'She pockets the money and motions toward the center of the arena. She reminds you that you will be facing an ever-worsening onslaught of monsters. Each one you dispatch nets you a bonus cache of rox in addition to whatever the monster gives you. You will also become more experienced the longer you last. Finally, you can give up at any time between battles.' 33 | puts 34 | puts 'She finishes by wishing you good luck!' 35 | 36 | return { type: 'arena', data: nil } 37 | else 38 | puts 39 | puts 'She gives you a dirty look, as you have obviously wasted her time. You are told not to mess around with her anymore, and she turns away from you.' 40 | return { type: nil, data: nil } 41 | end 42 | else 43 | puts 'She can tell you seem particularly poor today and says to come back when that has changed.' 44 | puts 45 | return { type: nil, data: nil } 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/people/drunk_man.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/people/drunk_man.rb 2 | # Entity::Creature::Person::DrunkMan 3 | 4 | require_relative '../person' 5 | require_relative '../../misc/formatting' 6 | 7 | module Gemwarrior 8 | class DrunkMan < Person 9 | include Formatting 10 | 11 | def initialize 12 | super 13 | 14 | self.name = 'drunk_man' 15 | self.name_display = 'Drunk Man' 16 | self.description = 'Some supernatural force is surely keeping this obviously smashed individual from toppling over to the ground. The inebriated fellow somehow continues to stumble about in a small circle near a smattering of shipping crates, looking simultaneously dazed and cheerful.' 17 | end 18 | 19 | def use(world) 20 | choose_blurting 21 | 22 | self.used = [true, false].sample 23 | 24 | { type: nil, data: nil } 25 | end 26 | 27 | private 28 | 29 | def choose_blurting 30 | choice = [1, 2, 3, 4].sample 31 | 32 | case choice 33 | when 1 34 | speak(to_hooch('I still can\'t believe I lost at the Arena! I was doing so well, and then a slippery citrinaga got a cheap shot on me.')) 35 | speak(to_hooch('Ehhh. Someday I\'ll be back and I\'ll be victorious. That smarmy Arena Master ain\'t gettin\' the last word!')) 36 | when 2 37 | speak(to_hooch('Maybe I just needed a better weapon that last fight in the arena. Yeah! That must be it.')) 38 | when 3 39 | speak(to_hooch('Man, my head really hurts. I\'m not sure if it\'s because of the fighting or the booze.')) 40 | speak(to_hooch('I should probably get something else to drink.')) 41 | puts 'The man looks like he has thought of something genius for a moment, but then scratches his head while stumbling around in his well-worn circle again.' 42 | when 4 43 | speak(to_hooch('Ahhhhhhhhhhhhhhhhhhhh!')) 44 | puts 'He begins to quickly become frantic as he notices you approaching, and then falls over, crumpled to the floor.' 45 | puts 46 | puts 'You approach to check if he\'s still breathing. As you get closer, he gets back up, hardly noticing you, and begins his spiral once again.' 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/people/queen_ruby.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/people/queen_ruby.rb 2 | # Entity::Creature::Person::QueenRuby 3 | 4 | require_relative '../person' 5 | require_relative '../../misc/animation' 6 | 7 | module Gemwarrior 8 | class QueenRuby < Person 9 | # CONSTANTS 10 | MOVE_TEXT = '** WHOOOOOOSH **' 11 | 12 | def initialize 13 | super 14 | 15 | self.name = 'queen_ruby' 16 | self.name_display = 'Queen Ruby' 17 | self.description = 'Queen Ruby glimmers like she was made from the substance of her name. She wears a wan smile, and her hands are delicately intertwined as she sits patiently.' 18 | end 19 | 20 | def use(world) 21 | speak('Thank you for bringing back the ShinyThing(tm)! The land of Jool is saved!') 22 | STDIN.getc 23 | speak("Please, #{world.player.name}, hand the ShinyThing(tm) to me, before all is lost!") 24 | 25 | print 'Hand over the ShinyThing(tm)? (y/n) ' 26 | answer = gets.chomp.downcase 27 | 28 | case answer 29 | when 'y', 'yes' 30 | if world.player.inventory.contains_item?('sparkly_thing') 31 | world.player.inventory.remove_item('sparkly_thing') 32 | speak('Oh, thank you! Now that the evil Emerald is defeated, and I, Queen Ruby, have the ShinyThing(tm) again, peace can come to the land of Jool.') 33 | speak('Your reward is', no_end_quote = true, no_line_feed = true) 34 | Animation.run(phrase: '......', oneline: true, speed: :slow, color: :yellow, numeric: false, alpha: false) 35 | Animation.run(phrase: 'my thanks!', oneline: true, speed: :insane, color: :yellow) 36 | print '"'.colorize(:yellow) 37 | print "\n" 38 | STDIN.getc 39 | puts 'You feel an audible groan spill out of your mouth, but Queen Ruby doesn\'t seem to notice.' 40 | STDIN.getc 41 | speak('Now, be a dear and run on home.') 42 | STDIN.getc 43 | puts 'And with that, she waves her arm in a tired, yet mystical, manner. Your mind and sight go blank, and you "poof" out of existence.' 44 | puts 45 | 46 | Animation.run(phrase: MOVE_TEXT) 47 | puts 48 | return { type: 'move', data: 'home' } 49 | else 50 | speak('Hold on a minute!', no_end_quote = false, no_line_feed = true) 51 | STDIN.getc 52 | speak('You don\'t even have the SparklyThing(tm)!', no_end_quote = false, no_line_feed = true) 53 | STDIN.getc 54 | speak('How on earth did you even get here without unknowningly being transported after acquiring it?', no_end_quote = false, no_line_feed = true) 55 | STDIN.getc 56 | speak('...', no_end_quote = false, no_line_feed = true) 57 | STDIN.getc 58 | speak('...are you a wizard?!') 59 | 60 | return { type: nil, data: nil } 61 | end 62 | when 'n', 'no' 63 | speak('No? No??? Well, you are not leaving this room until you do, so think long and hard about that.') 64 | return { type: nil, data: nil } 65 | else 66 | speak('Hmm. I see. Take your time and let me know when you are going to give me the ShinyThing(tm), all right?') 67 | return { type: nil, data: nil } 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/people/rockney.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/people/rockney.rb 2 | # Entity::Creature::Person::Rockney 3 | 4 | require_relative '../item' 5 | require_relative '../items/herb' 6 | require_relative '../weapons/dagger' 7 | 8 | module Gemwarrior 9 | class Rockney < Person 10 | # CONSTANTS 11 | PRICE_HERB = 5 12 | PRICE_DAGGER = 10 13 | PLAYER_ROX_INSUFFICIENT = 'Pity. You are a bit short on funds to purchase that item.' 14 | PLAYER_ITEMS_ADDITIONAL = 'Anything else?' 15 | PLAYER_COMMAND_INVALID = 'Huh?' 16 | 17 | def initialize 18 | super 19 | 20 | self.name = 'rockney' 21 | self.name_display = 'Rockney' 22 | self.description = 'The rat with a name looks at you, not with malice, but with kindness. Odd for a rodent hiding in a hole in a dark, metal tunnel.' 23 | self.talkable = true 24 | end 25 | 26 | def use(world) 27 | if !self.used 28 | puts 'You\'re not sure what to expect when you confront the small animal living in the crevice, but you figure it\'s this or doing anything else at all, so...' 29 | puts 30 | end 31 | 32 | rat_shop(world) 33 | end 34 | 35 | private 36 | 37 | def rat_shop(world) 38 | player_rox_remaining = world.player.rox 39 | amount_spent = 0 40 | items_purchased = [] 41 | 42 | herb = Herb.new 43 | dagger = Dagger.new 44 | 45 | speak('Hello, wanderer. Welcome to my establishment, as it were. Are you in need of anything?') 46 | puts 47 | puts 'The creature gently shoves a small slip of paper out of his hole and towards you. You take a peek and notice it has a list of things with prices on it.' 48 | puts 49 | puts 'Rockney\'s Hole in the Wall'.colorize(:cyan) 50 | puts '--------------------------' 51 | puts "(1) #{'Herb'.colorize(:yellow)} - #{PRICE_HERB} rox" 52 | puts " #{herb.description}" 53 | puts "(2) #{'Dagger'.colorize(:yellow)} - #{PRICE_DAGGER} rox" 54 | puts " #{dagger.description}" 55 | puts " Attack: +#{dagger.atk_lo}-#{dagger.atk_hi} (current: #{world.player.atk_lo}-#{world.player.atk_hi})" 56 | puts 57 | speak('What are you in need of?') 58 | 59 | loop do 60 | puts " 1 - Herb #{PRICE_HERB}" 61 | puts " 2 - Dagger #{PRICE_DAGGER}" 62 | print ' x - leave' 63 | if items_purchased.length > 0 64 | print ", and buy items\n" 65 | else 66 | print "\n" 67 | end 68 | puts 69 | print 'REMAINING ROX: ' 70 | Animation.run(phrase: player_rox_remaining.to_s, oneline: true) 71 | print "\n" 72 | display_shopping_cart(items_purchased) 73 | print '[ROCKNEY]>? ' 74 | 75 | choice = gets.chomp.downcase 76 | 77 | case choice 78 | when '1' 79 | if player_rox_remaining >= PRICE_HERB 80 | player_rox_remaining -= PRICE_HERB 81 | items_purchased.push(Herb.new) 82 | amount_spent += PRICE_HERB 83 | 84 | speak('Excellent choice.') 85 | speak(PLAYER_ITEMS_ADDITIONAL) 86 | next 87 | else 88 | speak(PLAYER_ROX_INSUFFICIENT) 89 | next 90 | end 91 | when '2' 92 | if world.player.rox >= PRICE_DAGGER 93 | world.player.rox -= PRICE_DAGGER 94 | items_purchased.push(Dagger.new) 95 | amount_spent += PRICE_DAGGER 96 | 97 | display_shopping_cart(items_purchased) 98 | speak('A fine blade, indeed.') 99 | speak(PLAYER_ITEMS_ADDITIONAL) 100 | next 101 | else 102 | speak(PLAYER_ROX_INSUFFICIENT) 103 | next 104 | end 105 | when 'x' 106 | return_type = { type: nil, data: nil } 107 | if items_purchased.length > 0 108 | display_shopping_cart(items_purchased) 109 | speak('Are you certain you wish to buy these things? (y/n)') 110 | print '[ROCKNEY]> ' 111 | 112 | answer = gets.chomp.downcase 113 | 114 | return_type = nil 115 | case answer 116 | when 'y', 'yes' 117 | world.player.rox -= amount_spent 118 | speak('Enjoy!') 119 | return_type = { type: 'purchase', data: items_purchased } 120 | else 121 | return_type = { type: nil, data: nil } 122 | end 123 | end 124 | speak('If you need anything further, I\'m always in this hole...') 125 | return return_type 126 | else 127 | speak(PLAYER_COMMAND_INVALID) 128 | next 129 | end 130 | end 131 | end 132 | end 133 | end 134 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/people/shifty_woman.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/people/shifty_woman.rb 2 | # Entity::Creature::Person::ShiftyWoman 3 | 4 | require_relative '../person' 5 | 6 | module Gemwarrior 7 | class ShiftyWoman < Person 8 | # CONSTANTS 9 | SHOO_TEXT = '** BAMF **' 10 | 11 | def initialize 12 | super 13 | 14 | self.name = 'shifty_woman' 15 | self.name_display = 'Shifty Woman' 16 | self.description = 'Sharply dressed with impeccable style, you still can\'t shake the feeling that this otherwise ordinary woman is up to something. It might be the way she almost impulsively looks back and forth along the town street while rubbing her hands together menacingly.' 17 | end 18 | 19 | def use(world) 20 | if self.used 21 | puts 'Attempting a friendly overture again, the woman turns and looks directly at you. Her brown eyes glint in the sun, almost turning an ochre hue. Her look burns you to the core, causing you to physically recoil a little.' 22 | STDIN.getc 23 | puts 'She then growls in a low voice at you:' 24 | speak('Have you heard of Emerald, the good-for-nothing wizard that doomed our little world of Jool by absconding with the SparklyThing(tm)?') 25 | STDIN.getc 26 | puts 'Before you can even begin to answer she throws up her hands and continues, stealing a look off to the side:' 27 | speak('My life was fine before that idiot decided he needed MORE power than he already has.') 28 | STDIN.getc 29 | puts 'She crosses her arms and looks down, seemingly in thought. One of her hands rolls around, a small flicker of light dancing around it as it moves.' 30 | STDIN.getc 31 | speak("If only I had some #{"tanzanite".colorize(:blue)}...I could cook up a spell that would take down ol' Em faster than he could lift a wand!") 32 | STDIN.getc 33 | if world.player.inventory.contains_item?('sand_jewel') 34 | puts 'Her head tilts upward, ever so slightly, and she looks at you, one eyebrow cocked:' 35 | speak('I can sense you might have what I need. I know you want to get rid of Emerald and take back the SparklyThing(tm) to give to our beloved queen, too.') 36 | STDIN.getc 37 | speak('I can help. Just give me that little piece of shiny you somehow came across in your travels, and I\'ll do everything in my power to make our shared goal a reality.') 38 | print 'Give the shifty woman your Sand Jewel? (y/n) ' 39 | answer = gets.chomp.downcase 40 | 41 | case answer 42 | when 'y', 'yes' 43 | world.player.inventory.remove_item('sand_jewel') 44 | world.shifty_to_jewel = true 45 | speak('Yes...this will do nicely.') 46 | STDIN.getc 47 | speak('Forgive me, but I must take my leave for now. When the time is right, I will return!') 48 | puts 'And with that, she disappears. No puff of smoke or magical to-do...she is just no more.' 49 | else 50 | speak('Bah! Begone, fool!') 51 | 52 | Animation.run(phrase: SHOO_TEXT) 53 | puts 54 | 55 | puts "An aura of electric light surrounds her as you are physically pushed back a foot or so. You feel instantly, uh, #{WordList.new('adjective').get_random_value}." 56 | end 57 | else 58 | puts 'She grumbles to herself a little before giving you a little "shoo" and recommences looking shifty.' 59 | end 60 | else 61 | puts 'The woman averts her eyes from you as you commence with a greeting, giving a little scowl while she is at it.' 62 | 63 | self.used = true 64 | end 65 | 66 | { type: nil, data: nil } 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/people/thin_man.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/people/thin_man.rb 2 | # Entity::Creature::Person::ThinMan 3 | 4 | require_relative '../person' 5 | 6 | module Gemwarrior 7 | class ThinMan < Person 8 | def initialize 9 | super 10 | 11 | self.name = 'thin_man' 12 | self.name_display = 'Thin Man' 13 | self.description = 'An almost shockingly gaunt man is sitting on the ground, resting against a wall. He wears a patchwork quilt of a hat, and his slender frame is covered by a simple brown tunic. His feet point comically toward the sky in brown boots while his head dips down slightly, eyes watching something in the distance you can\'t see.' 14 | end 15 | 16 | def use(world) 17 | if self.used 18 | puts 'The thin man barely moves his head as he puts up a single grim hand, motioning you to back off.' 19 | else 20 | puts 'The thin man lifts his head up slightly, meets your gaze, and responds to your greeting:' 21 | speak('I may be incredibly spare right now, but I was once a strapping young person, such as yourself. Just in case you ever fall during your journey, be sure to approach unlikely sources, as you may find those who will help you.') 22 | speak('Now, please, allow me my peace.') 23 | 24 | self.used = true 25 | end 26 | 27 | { type: nil, data: nil } 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/people/ware_hawker.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/people/ware_hawker.rb 2 | # Entity::Creature::Person::WareHawker 3 | 4 | require_relative '../person' 5 | require_relative '../weapons/mace' 6 | require_relative '../weapons/spear' 7 | require_relative '../armor/iron_helmet' 8 | 9 | module Gemwarrior 10 | class WareHawker < Person 11 | # CONSTANTS 12 | PRICE_IRON_HELMET = 10 13 | PRICE_MACE = 30 14 | PRICE_SPEAR = 40 15 | PLAYER_ROX_INSUFFICIENT = 'You have insufficient rox to purchase that. Quit testing me, human.' 16 | PLAYER_ITEMS_ADDITIONAL = 'Will there be something else?' 17 | PLAYER_COMMAND_INVALID = 'That means nothing to me.' 18 | 19 | def initialize 20 | super 21 | 22 | self.name = 'ware_hawker' 23 | self.name_display = 'Ware Hawker' 24 | self.description = 'A literal anthropomorphic hawk has set up shop behind a crudely-made table. Some wares are scattered atop its surface, seemingly within anyone\'s grasp, but the hawk\'s piercing eyes seem to belie this observation.' 25 | end 26 | 27 | def use(world) 28 | if !self.used 29 | puts 'You greet the hawk, mentioning that you are interested in the objects presented.' 30 | STDIN.getc 31 | puts 'The hawk speaks in a beautiful, yet commanding, tone:' 32 | end 33 | 34 | hawk_shop(world) 35 | end 36 | 37 | private 38 | 39 | def hawk_shop(world) 40 | player_rox_remaining = world.player.rox 41 | amount_spent = 0 42 | items_purchased = [] 43 | 44 | iron_helmet = IronHelmet.new 45 | mace = Mace.new 46 | spear = Spear.new 47 | 48 | speak('I hope you have rox, human. My time affords business transactions, not idle chit chat. What do you want?') 49 | STDIN.getc 50 | 51 | puts 'A feathered arm quickly moves across the table in an arc suggesting to you that a choice is to be made. Each object has a price tag underneath it, painstakingly written in ink.' 52 | puts 53 | 54 | puts 'Hawk Shop'.colorize(:cyan) 55 | puts '---------' 56 | puts "(1) #{'Iron Helmet'.colorize(:yellow)} - #{PRICE_IRON_HELMET} rox" 57 | puts " #{iron_helmet.description}" 58 | puts " Defense: +#{iron_helmet.defense} (current: #{world.player.defense})" 59 | puts "(2) #{'Mace'.colorize(:yellow)} - #{PRICE_MACE} rox" 60 | puts " #{mace.description}" 61 | puts " Attack: +#{mace.atk_lo}-#{mace.atk_hi} (current: #{world.player.atk_lo}-#{world.player.atk_hi})" 62 | puts "(3) #{'Spear'.colorize(:yellow)} - #{PRICE_SPEAR} rox" 63 | puts " #{spear.description}" 64 | puts " Attack: +#{spear.atk_lo}-#{spear.atk_hi} (current: #{world.player.atk_lo}-#{world.player.atk_hi})" 65 | puts 66 | speak('Choose. Now.') 67 | 68 | loop do 69 | puts " 1 - Iron Helmet (#{PRICE_IRON_HELMET})" 70 | puts " 2 - Mace (#{PRICE_MACE})" 71 | puts " 3 - Spear (#{PRICE_SPEAR})" 72 | print ' x - leave' 73 | if items_purchased.length > 0 74 | print ", and buy items\n" 75 | else 76 | print "\n" 77 | end 78 | puts 79 | print 'REMAINING ROX: ' 80 | Animation.run(phrase: player_rox_remaining.to_s, oneline: true) 81 | print "\n" 82 | display_shopping_cart(items_purchased) 83 | print '[HAWK]> ' 84 | 85 | choice = gets.chomp.downcase 86 | 87 | case choice 88 | when '1' 89 | if player_rox_remaining >= PRICE_IRON_HELMET 90 | player_rox_remaining -= PRICE_IRON_HELMET 91 | items_purchased.push(IronHelmet.new) 92 | amount_spent += PRICE_IRON_HELMET 93 | 94 | speak('The iron helmet? I see.') 95 | speak(PLAYER_ITEMS_ADDITIONAL) 96 | next 97 | else 98 | speak(PLAYER_ROX_INSUFFICIENT) 99 | next 100 | end 101 | when '2' 102 | if player_rox_remaining >= PRICE_MACE 103 | player_rox_remaining -= PRICE_MACE 104 | items_purchased.push(Mace.new) 105 | amount_spent += PRICE_MACE 106 | 107 | speak('Yes, fine. Buy a mace.') 108 | speak(PLAYER_ITEMS_ADDITIONAL) 109 | next 110 | else 111 | speak(PLAYER_ROX_INSUFFICIENT) 112 | next 113 | end 114 | when '3' 115 | if player_rox_remaining >= PRICE_SPEAR 116 | player_rox_remaining -= PRICE_SPEAR 117 | items_purchased.push(Spear.new) 118 | amount_spent += PRICE_SPEAR 119 | 120 | speak('Hmm. Very well, then. If the spear is what you wish.') 121 | speak(PLAYER_ITEMS_ADDITIONAL) 122 | next 123 | else 124 | speak(PLAYER_ROX_INSUFFICIENT) 125 | next 126 | end 127 | when 'x' 128 | return_type = { type: nil, data: nil } 129 | if items_purchased.length > 0 130 | display_shopping_cart(items_purchased) 131 | speak('Are you certain you wish to buy these things? (y/n)') 132 | print '[HAWK]> ' 133 | answer = gets.chomp.downcase 134 | 135 | case answer 136 | when 'y', 'yes' 137 | world.player.rox -= amount_spent 138 | speak('Take them.') 139 | return_type = { type: 'purchase', data: items_purchased } 140 | end 141 | end 142 | speak('Our business is complete then.') 143 | return return_type 144 | else 145 | speak(PLAYER_COMMAND_INVALID) 146 | next 147 | end 148 | end 149 | end 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/person.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/person.rb 2 | # Entity::Creature::Person base class 3 | 4 | require_relative 'creature' 5 | 6 | module Gemwarrior 7 | class Person < Creature 8 | def initialize 9 | super 10 | 11 | self.name = 'person.' 12 | self.name_display = Formatting::upstyle(name) 13 | self.description = 'It appears to be a person of some kind.' 14 | self.takeable = false 15 | end 16 | 17 | def use(world) 18 | 'That person does not seem to want to talk to you right now.' 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/weapon.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/weapon.rb 2 | # Entity::Item::Weapon base class 3 | 4 | require_relative 'item' 5 | 6 | module Gemwarrior 7 | class Weapon < Item 8 | attr_accessor :atk_lo, 9 | :atk_hi, 10 | :dex_mod, 11 | :is_weapon 12 | 13 | def initialize 14 | super 15 | 16 | self.atk_lo = 0 17 | self.atk_hi = 0 18 | self.dex_mod = 0 19 | self.takeable = true 20 | self.equippable = true 21 | self.is_weapon = true 22 | end 23 | 24 | def use(world) 25 | 'Save the brandishing of your weapon for battle.' 26 | end 27 | 28 | def describe_detailed 29 | desc_text = "\"#{name_display}\"\n".colorize(:yellow) 30 | desc_text << "(#{name})\n".colorize(:green) 31 | desc_text << "#{description}\n".colorize(:white) 32 | desc_text << "WEAPON? #{is_weapon}\n".colorize(:white) 33 | desc_text << "ATTACK_RANGE : #{atk_lo}-#{atk_hi}\n".colorize(:white) 34 | desc_text << "TAKEABLE? #{takeable}\n".colorize(:white) 35 | desc_text << "USEABLE? #{useable}\n".colorize(:white) 36 | desc_text << "TALKABLE? #{talkable}\n".colorize(:white) 37 | desc_text << "CONSUMABLE? #{consumable}\n".colorize(:white) 38 | desc_text << "EQUIPPABLE? #{equippable}\n".colorize(:white) 39 | desc_text << "EQUIPPED? #{equipped}\n".colorize(:white) 40 | desc_text << "USED? #{used}\n".colorize(:white) 41 | desc_text << "USED AGAIN? #{used_again}\n".colorize(:white) 42 | desc_text << "USES LEFT? #{number_of_uses}\n".colorize(:white) unless number_of_uses.nil? 43 | desc_text 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/weapons/dagger.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/weapons/dagger.rb 2 | # Entity::Item::Weapon::Dagger 3 | 4 | require_relative '../weapon' 5 | 6 | module Gemwarrior 7 | class Dagger < Weapon 8 | def initialize 9 | super 10 | 11 | self.name = 'dagger' 12 | self.name_display = 'Dagger' 13 | self.description = 'Flint that has been sharpened to a point, attached to a block of smooth granite by thin rope. Truly a work of art.' 14 | self.atk_lo = 1 15 | self.atk_hi = 3 16 | self.dex_mod = 1 17 | end 18 | 19 | def use(world) 20 | puts 'You graze the blade of the dagger lightly and find it to be plenty sharp.' 21 | { type: nil, data: nil } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/weapons/gun.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/weapons/gun.rb 2 | # Entity::Item::Weapon::Gun 3 | 4 | require_relative '../weapon' 5 | 6 | module Gemwarrior 7 | class Gun < Weapon 8 | def initialize 9 | super 10 | 11 | self.name = 'gun' 12 | self.name_display = 'Gun' 13 | self.description = 'Pew pew goes this firearm, you suspect (if it has bullets).' 14 | self.atk_lo = 2 15 | self.atk_hi = 6 16 | self.dex_mod = 2 17 | end 18 | 19 | def use(world) 20 | puts 'You pull the trigger on the gun, but it does not fire. An inscription on the barrel reads: "Only shoots when pointed at a monster." How safe!' 21 | { type: nil, data: nil } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/weapons/mace.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/weapons/mace.rb 2 | # Entity::Item::Weapon::Mace 3 | 4 | require_relative '../weapon' 5 | 6 | module Gemwarrior 7 | class Mace < Weapon 8 | def initialize 9 | super 10 | 11 | self.name = 'mace' 12 | self.name_display = 'Mace' 13 | self.description = 'Sharp spikes atop a steel ball, affixed to a sturdy wooden handle. You could do damage with this.' 14 | self.atk_lo = 4 15 | self.atk_hi = 6 16 | self.dex_mod = -2 17 | end 18 | 19 | def use(world) 20 | puts 'You swing the mace around a few times, really testing out its smashability.' 21 | { type: nil, data: nil } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/weapons/opalaser.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/weapons/opalaser.rb 2 | # Entity::Item::Weapon::Opalaser 3 | 4 | require_relative '../item' 5 | 6 | module Gemwarrior 7 | class Opalaser < Weapon 8 | def initialize 9 | super 10 | 11 | self.name = 'opalaser' 12 | self.name_display = 'Opalaser' 13 | self.description = 'Gleaming with supernatural light, this object feels alien, yet familiar.' 14 | self.atk_lo = 9 15 | self.atk_hi = 11 16 | self.dex_mod = 2 17 | end 18 | 19 | def use(world) 20 | puts 'You pull the "trigger" on the opalaser, but nothing happens.' 21 | { type: nil, data: nil } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/weapons/spear.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/weapons/spear.rb 2 | # Entity::Item::Weapon::Spear 3 | 4 | require_relative '../weapon' 5 | 6 | module Gemwarrior 7 | class Spear < Weapon 8 | def initialize 9 | super 10 | 11 | self.name = 'spear' 12 | self.name_display = 'Spear' 13 | self.description = 'About six feet in length and razor-sharp at the point, this spear requires two hands, but only one good stab.' 14 | self.atk_lo = 3 15 | self.atk_hi = 7 16 | self.dex_mod = -1 17 | end 18 | 19 | def use(world) 20 | puts 'This spear does, indeed, appear to strike fear upon that which is near.' 21 | { type: nil, data: nil } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/weapons/stalactite.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/weapons/stalactite.rb 2 | # Entity::Item::Weapon::Stalactite 3 | 4 | require_relative '../weapon' 5 | 6 | module Gemwarrior 7 | class Stalactite < Weapon 8 | def initialize 9 | super 10 | 11 | self.name = 'stalactite' 12 | self.name_display = 'Stalactite' 13 | self.description = 'Long protrusion of cave adornment, broken off and fallen to the ground, where the stalagmites sneer at it from.' 14 | self.atk_lo = 2 15 | self.atk_hi = 3 16 | self.dex_mod = -1 17 | end 18 | 19 | def use(world) 20 | puts 'You wave the stalactite around in the air a bit, testing its weight and precision. It is a thin, long piece of rock taken from a cave wall, for sure, though.' 21 | { type: nil, data: nil } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gemwarrior/entities/weapons/stone.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/entities/weapons/stone.rb 2 | # Entity::Item::Weapon::Stone 3 | 4 | require_relative '../weapon' 5 | 6 | module Gemwarrior 7 | class Stone < Weapon 8 | def initialize 9 | super 10 | 11 | self.name = 'stone' 12 | self.name_display = 'Stone' 13 | self.description = 'A small, yet quite sharp, sedimentary pebble, suitable for tossing in amusement, and perhaps combat.' 14 | self.atk_lo = 1 15 | self.atk_hi = 2 16 | self.dex_mod = -1 17 | end 18 | 19 | def use(world) 20 | puts 'You toss the stone a few feet into the air, and then it falls back into your palm. The experience was truly thrilling.' 21 | { type: nil, data: nil } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gemwarrior/game.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/game.rb 2 | # Main launching point for Gem Warrior 3 | 4 | require 'colorize' 5 | require 'matrext' 6 | 7 | require_relative 'entities/armor/iron_helmet' 8 | require_relative 'entities/items/herb' 9 | require_relative 'entities/weapons/dagger' 10 | require_relative 'entities/player' 11 | require_relative 'misc/animation' 12 | require_relative 'misc/audio' 13 | require_relative 'misc/formatting' 14 | require_relative 'misc/hr' 15 | require_relative 'evaluator' 16 | require_relative 'game_assets' 17 | require_relative 'game_options' 18 | require_relative 'inventory' 19 | require_relative 'repl' 20 | require_relative 'world' 21 | 22 | module Gemwarrior 23 | class Game 24 | # CONSTANTS 25 | INVENTORY_DEFAULT = Inventory.new 26 | INVENTORY_DEBUG = Inventory.new([Herb.new, Herb.new, Herb.new]) 27 | ROX_DEFAULT = 0 28 | ROX_DEBUG = 300 29 | 30 | attr_accessor :world, 31 | :evaluator, 32 | :repl, 33 | :monsters, 34 | :weapons 35 | 36 | def initialize(options) 37 | # set game options 38 | self.init_game_options(options) 39 | 40 | # add classes for creatures, monsters, people, items, weapons, and armor to game 41 | # also add them to the global GameAssets 42 | self.init_creatures 43 | self.init_monsters 44 | self.init_people 45 | self.init_items 46 | self.init_weapons 47 | self.init_armor 48 | 49 | # create new world based on yaml/marshall data 50 | self.world = init_world 51 | 52 | # spawn bosses inside world 53 | self.init_bosses 54 | 55 | # update some aspects of world's player to make more dynamic 56 | self.world.player.name = world.player.generate_name 57 | self.world.player.face = world.player.generate_face 58 | self.world.player.hands = world.player.generate_hands 59 | self.world.player.mood = world.player.generate_mood 60 | self.world.player.inventory = GameOptions.data['debug_mode'] ? INVENTORY_DEBUG : INVENTORY_DEFAULT 61 | self.world.player.rox = GameOptions.data['debug_mode'] ? ROX_DEBUG : ROX_DEFAULT 62 | 63 | # set some global variables 64 | self.world.duration = { mins: 0, secs: 0, ms: 0 } 65 | self.world.emerald_beaten = false 66 | self.world.shifty_to_jewel = false 67 | self.world.shifty_has_jeweled = false 68 | 69 | # mark home as visited 70 | self.world.location_by_name('home').visited = true 71 | 72 | # create options file if not existing 73 | self.update_options_file 74 | 75 | # create the console 76 | self.evaluator = Evaluator.new(world) 77 | self.repl = Repl.new(self, world, evaluator) 78 | 79 | # enter Jool! 80 | self.repl.start('look', options.fetch(:extra_command), options.fetch(:new_skip), options.fetch(:resume_skip)) 81 | end 82 | 83 | def update_options_file 84 | File.open(GameOptions.data['options_file_path'], 'w') do |f| 85 | f.puts "fight_completion:#{GameOptions.data['fight_completion']}" 86 | f.puts "sound_enabled:#{GameOptions.data['sound_enabled']}" 87 | f.puts "sound_system:#{GameOptions.data['sound_system']}" 88 | f.puts "sound_volume:#{GameOptions.data['sound_volume']}" 89 | f.puts "use_wordnik:#{GameOptions.data['use_wordnik']}" 90 | end 91 | end 92 | 93 | private 94 | 95 | # convert an entity filename to a class so it can be added to game asset singleton registry 96 | def file_to_class(filename) 97 | filename_to_string = Formatting.upstyle(filename.split('/').last.split('.')[0], no_space: true) 98 | Gemwarrior.const_get(filename_to_string).new 99 | end 100 | 101 | def init_game_options(options) 102 | GameOptions.add 'beast_mode', options.fetch(:beast_mode) 103 | GameOptions.add 'debug_mode', options.fetch(:debug_mode) 104 | GameOptions.add 'fight_completion', options.fetch(:fight_completion) 105 | GameOptions.add 'god_mode', options.fetch(:god_mode) 106 | GameOptions.add 'sound_enabled', options.fetch(:sound_enabled) 107 | GameOptions.add 'sound_system', options.fetch(:sound_system) 108 | GameOptions.add 'sound_volume', options.fetch(:sound_volume) 109 | GameOptions.add 'use_wordnik', options.fetch(:use_wordnik) 110 | 111 | # require needed files for selected sound_system if sound_enabled 112 | if GameOptions.data['sound_enabled'] 113 | Audio.init 114 | end 115 | end 116 | 117 | def init_bosses 118 | ## Pain Quarry 119 | world.location_by_name('pain_quarry-southeast').bosses_abounding.push(Gemwarrior.const_get('Garynetty').new) 120 | world.location_by_name('pain_quarry-east').bosses_abounding.push(Gemwarrior.const_get('Garynetty').new) 121 | world.location_by_name('pain_quarry-central').bosses_abounding.push(Gemwarrior.const_get('Garynetty').new) 122 | world.location_by_name('pain_quarry-south').bosses_abounding.push(Gemwarrior.const_get('Garynetty').new) 123 | world.location_by_name('pain_quarry-west').bosses_abounding.push(Gemwarrior.const_get('Garynetty').new) 124 | world.location_by_name('pain_quarry-northwest').bosses_abounding.push(Gemwarrior.const_get('Garynetty').new) 125 | world.location_by_name('pain_quarry-north').bosses_abounding.push(Gemwarrior.const_get('Garynetty').new) 126 | ## River Bridge 127 | world.location_by_name('river_bridge').bosses_abounding.push(Gemwarrior.const_get('Jaspern').new) 128 | ## Throne Room 129 | world.location_by_name('sky_tower-throne_room').bosses_abounding.push(Gemwarrior.const_get('Emerald').new) 130 | end 131 | 132 | def init_creatures 133 | Dir.glob(File.expand_path('../entities/creatures/*.rb', __FILE__)).each do |creature| 134 | require_relative creature 135 | GameCreatures.add(file_to_class(creature)) 136 | end 137 | end 138 | 139 | def init_monsters 140 | Dir.glob(File.expand_path('../entities/monsters/*.rb', __FILE__)).each do |monster| 141 | require_relative monster 142 | GameMonsters.add(file_to_class(monster)) 143 | end 144 | Dir.glob(File.expand_path('../entities/monsters/bosses/*.rb', __FILE__)).each do |boss| 145 | require_relative boss 146 | GameMonsters.add(file_to_class(boss)) 147 | end 148 | end 149 | 150 | def init_people 151 | Dir.glob(File.expand_path('../entities/people/*.rb', __FILE__)).each do |person| 152 | require_relative person 153 | GamePeople.add(file_to_class(person)) 154 | end 155 | end 156 | 157 | def init_items 158 | Dir.glob(File.expand_path('../entities/items/*.rb', __FILE__)).each do |item| 159 | require_relative item 160 | GameItems.add(file_to_class(item)) 161 | end 162 | end 163 | 164 | def init_weapons 165 | Dir.glob(File.expand_path('../entities/weapons/*.rb', __FILE__)).each do |weapon| 166 | require_relative weapon 167 | GameWeapons.add(file_to_class(weapon)) 168 | end 169 | end 170 | 171 | def init_armor 172 | Dir.glob(File.expand_path('../entities/armor/*.rb', __FILE__)).each do |armor| 173 | require_relative armor 174 | GameArmor.add(file_to_class(armor)) 175 | end 176 | end 177 | 178 | def init_world 179 | mode = GameOptions.data['save_file_mode'] 180 | 181 | if mode.eql?('Y') 182 | if File.exist?(GameOptions.data['world_yaml_path']) 183 | File.open(GameOptions.data['world_yaml_path'], 'r') do |f| 184 | return YAML.unsafe_load(f) 185 | end 186 | else 187 | puts "Error: cannot load #{GameOptions.data['world_yaml_path']}." 188 | end 189 | else 190 | if File.exist?(GameOptions.data['world_bin_path']) 191 | File.open(GameOptions.data['world_bin_path'], 'r') do |f| 192 | return Marshal.load(f) 193 | end 194 | else 195 | puts "Error: cannot load #{GameOptions.data['world_bin_path']}." 196 | end 197 | end 198 | end 199 | end 200 | end 201 | -------------------------------------------------------------------------------- /lib/gemwarrior/game_assets.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/game_assets.rb 2 | # Gem Warrior Global Assets 3 | ## Game Armor 4 | ## Game Creatures 5 | ## Game Items 6 | ## Game Monsters 7 | ## Game People 8 | ## Game Weapons 9 | 10 | module Gemwarrior 11 | module GameArmor 12 | def self.add obj 13 | @@data ||= [] 14 | @@data.push(obj) 15 | end 16 | 17 | def self.data 18 | @@data ||= [] 19 | end 20 | 21 | def self.get(name) 22 | self.data.find { |i| i.name.downcase == name.downcase } 23 | end 24 | end 25 | 26 | module GameCreatures 27 | def self.add obj 28 | @@data ||= [] 29 | @@data.push(obj) 30 | end 31 | 32 | def self.data 33 | @@data ||= [] 34 | end 35 | 36 | def self.get(name) 37 | self.data.find { |i| i.name.downcase == name.downcase } 38 | end 39 | end 40 | 41 | module GameItems 42 | def self.add obj 43 | @@data ||= [] 44 | @@data.push(obj) 45 | end 46 | 47 | def self.data 48 | @@data ||= [] 49 | end 50 | 51 | def self.get(name) 52 | self.data.find { |i| i.name.downcase == name.downcase } 53 | end 54 | end 55 | 56 | module GameMonsters 57 | def self.add obj 58 | @@data ||= [] 59 | @@data.push(obj) 60 | end 61 | 62 | def self.data 63 | @@data ||= [] 64 | end 65 | 66 | def self.get(name) 67 | self.data.find { |i| i.name.downcase == name.downcase } 68 | end 69 | end 70 | 71 | module GamePeople 72 | def self.add obj 73 | @@data ||= [] 74 | @@data.push(obj) 75 | end 76 | 77 | def self.data 78 | @@data ||= [] 79 | end 80 | 81 | def self.get(name) 82 | self.data.find { |i| i.name.downcase == name.downcase } 83 | end 84 | end 85 | 86 | module GameWeapons 87 | def self.add obj 88 | @@data ||= [] 89 | @@data.push(obj) 90 | end 91 | 92 | def self.data 93 | @@data ||= [] 94 | end 95 | 96 | def self.get(name) 97 | self.data.find { |i| i.name.downcase == name.downcase } 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /lib/gemwarrior/game_options.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/game_options.rb 2 | # Gem Warrior Global Game Options 3 | 4 | module Gemwarrior 5 | module GameOptions 6 | def self.add key, value 7 | @@data ||= {} 8 | @@data[key] = value 9 | end 10 | 11 | def self.data 12 | @@data ||= {} 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/gemwarrior/inventory.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/inventory.rb 2 | # Collection of items a creature possesses 3 | 4 | require_relative 'game_options' 5 | 6 | module Gemwarrior 7 | class Inventory 8 | # CONSTANTS 9 | ERROR_ITEM_REMOVE_INVALID = 'Your inventory does not contain that item, so you cannot drop it.' 10 | ERROR_ITEM_ADD_UNTAKEABLE = 'That would be great if you could take that, wouldn\'t it? Huh!' 11 | ERROR_ITEM_ADD_INVALID = 'That item cannot be taken or does not exist.' 12 | ERROR_ITEM_DESCRIBE_INVALID = 'That does not seem to be in the inventory.' 13 | ERROR_ITEM_EQUIP_INVALID = 'You do not possess anything called that to equip.' 14 | ERROR_ITEM_EQUIP_NONARMAMENT = 'That item cannot be equipped.' 15 | ERROR_ITEM_UNEQUIP_INVALID = 'You do not possess anything called that to unequip.' 16 | ERROR_ITEM_UNEQUIP_NONARMAMENT = 'That item cannot be unequipped.' 17 | VOWELS = 'aeiou' 18 | 19 | attr_accessor :items, 20 | :weapon, 21 | :armor 22 | 23 | def initialize(items = [], weapon = nil, armor = nil) 24 | self.items = items 25 | self.weapon = weapon 26 | self.armor = armor 27 | end 28 | 29 | def an_words 30 | ['herb'] 31 | end 32 | 33 | def article_chooser(word) 34 | (VOWELS.include?(word.to_s[0]) or an_words.include?(word.to_s)) ? 'an' : 'a' 35 | end 36 | 37 | def is_empty? 38 | self.items.nil? || self.items.empty? 39 | end 40 | 41 | # non-English-like contents of inventory for simple lists 42 | def contents 43 | if is_empty? 44 | nil 45 | else 46 | item_hash = {} 47 | self.items.map(&:name).each do |i| 48 | i_sym = i.to_sym 49 | if item_hash.keys.include? i_sym 50 | item_hash[i_sym] += 1 51 | else 52 | item_hash[i_sym] = 1 53 | end 54 | end 55 | 56 | # one item? return string 57 | if item_hash.length == 1 58 | i = item_hash.keys.join 59 | q = item_hash.values.join.to_i 60 | return q > 1 ? "#{i}s x#{q}" : i 61 | # multiple items? return array of strings to mush together 62 | else 63 | item_arr = [] 64 | item_hash.each do |i, q| 65 | if q > 1 66 | item_arr.push("#{i}s x#{q}") 67 | else 68 | item_arr.push(i) 69 | end 70 | end 71 | 72 | return item_arr.join(', ') 73 | end 74 | end 75 | end 76 | 77 | # English-like sentence summary of inventory 78 | def list_contents 79 | if is_empty? 80 | 'You possess nothing.' 81 | else 82 | # build hash of inventory's items 83 | item_hash = {} 84 | self.items.map(&:name).each do |i| 85 | i_sym = i.to_sym 86 | if item_hash.keys.include? i_sym 87 | item_hash[i_sym] += 1 88 | else 89 | item_hash[i_sym] = 1 90 | end 91 | end 92 | 93 | # one item? return string 94 | if item_hash.length == 1 95 | i = item_hash.keys.join 96 | q = item_hash.values.join.to_i 97 | if q > 1 98 | return "You have #{q} #{i.to_s.colorize(:yellow)}#{'s'.colorize(:yellow)}." 99 | else 100 | return "You have #{self.article_chooser(i)} #{i.to_s.colorize(:yellow)}." 101 | end 102 | # multiple items? return array of strings to mush together 103 | else 104 | item_list_text = 'You have ' 105 | item_arr = [] 106 | item_hash.each do |i, q| 107 | if q > 1 108 | item_arr.push("#{q} #{i.to_s.colorize(:yellow)}#{'s'.colorize(:yellow)}") 109 | else 110 | item_arr.push("#{self.article_chooser(i)} #{i.to_s.colorize(:yellow)}") 111 | end 112 | end 113 | 114 | item_arr[-1].replace("and #{item_arr[-1]}.") 115 | 116 | return item_list_text << item_arr.join(', ') 117 | end 118 | end 119 | end 120 | 121 | def contains_item?(item_name) 122 | self.items.map{ |i| i.name.downcase }.include?(item_name.downcase) 123 | end 124 | 125 | def contains_battle_item? 126 | battle_item_found = false 127 | self.items.each do |i| 128 | battle_item_found = true if i.useable_battle 129 | end 130 | battle_item_found 131 | end 132 | 133 | def list_battle_items 134 | battle_items = [] 135 | self.items.each do |i| 136 | battle_items.push(i) if i.useable_battle 137 | end 138 | battle_items 139 | end 140 | 141 | def describe_item(item_name, world) 142 | if contains_item?(item_name) 143 | self.items.each do |i| 144 | if i.name.eql?(item_name) 145 | if GameOptions.data['debug_mode'] 146 | return i.describe_detailed(world) 147 | else 148 | return i.describe(world) 149 | end 150 | end 151 | end 152 | else 153 | ERROR_ITEM_DESCRIBE_INVALID 154 | end 155 | end 156 | 157 | def equip_item(item_name) 158 | if contains_item?(item_name) 159 | self.items.each do |i| 160 | if i.name.eql?(item_name) 161 | if i.equippable 162 | i.equipped = true 163 | if i.is_weapon 164 | self.weapon = i 165 | return "The #{i.name.colorize(:yellow)} has taken charge, and been equipped." 166 | elsif i.is_armor 167 | self.armor = i 168 | return "The #{i.name.colorize(:yellow)} has fortified you, and been equipped." 169 | end 170 | else 171 | return ERROR_ITEM_EQUIP_NONARMAMENT 172 | end 173 | end 174 | end 175 | else 176 | ERROR_ITEM_EQUIP_INVALID 177 | end 178 | end 179 | 180 | def unequip_item(item_name) 181 | if contains_item?(item_name) 182 | self.items.each do |i| 183 | if i.name.eql?(item_name) 184 | if i.equippable 185 | i.equipped = false 186 | if i.is_weapon 187 | self.weapon = nil 188 | return "The #{i.name.colorize(:yellow)} has been demoted to unequipped." 189 | elsif i.is_armor 190 | self.armor = nil 191 | return "The #{i.name.colorize(:yellow)} has been demoted to unequipped." 192 | end 193 | else 194 | return ERROR_ITEM_UNEQUIP_NONARMAMENT 195 | end 196 | end 197 | end 198 | else 199 | ERROR_ITEM_UNEQUIP_INVALID 200 | end 201 | end 202 | 203 | def add_item(item_name, cur_loc = nil, player = nil) 204 | if cur_loc.nil? 205 | self.items.push(item_name) 206 | else 207 | cur_loc.items.each do |i| 208 | if i.name.eql?(item_name) 209 | if i.takeable 210 | self.items.push(i) 211 | cur_loc.remove_item(item_name) 212 | 213 | # stats 214 | player.items_taken += 1 215 | 216 | return "#{"Added".colorize(:green)} #{item_name.colorize(:yellow)} #{"to your increasing collection of bits of tid".colorize(:green)}." 217 | else 218 | return ERROR_ITEM_ADD_UNTAKEABLE.colorize(:red) 219 | end 220 | end 221 | end 222 | end 223 | ERROR_ITEM_ADD_INVALID.colorize(:red) 224 | end 225 | 226 | def drop_item(item_name, cur_loc) 227 | if contains_item?(item_name) 228 | remove_item(item_name) 229 | cur_loc.add_item(item_name) 230 | "You dropped #{item_name.colorize(:yellow)}." 231 | else 232 | ERROR_ITEM_REMOVE_INVALID 233 | end 234 | end 235 | 236 | def remove_item(item_name) 237 | self.items.delete_at(self.items.map(&:name).index(item_name) || self.items.length) 238 | unless self.weapon.nil? 239 | self.weapon = nil if self.weapon.name.eql?(item_name) 240 | end 241 | end 242 | end 243 | end 244 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/animation.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/animation.rb 2 | # Animation routine using matrext 3 | 4 | module Gemwarrior 5 | module Animation 6 | def self.run(opts) 7 | options = { 8 | oneline: false, 9 | speed: nil, 10 | alpha: true, 11 | numeric: true, 12 | random: true, 13 | color: nil, 14 | background: nil 15 | }.merge(opts) 16 | 17 | th = Thread.new do 18 | print Matrext.process({ 19 | phrase: options.fetch(:phrase), 20 | oneline: options.fetch(:oneline), 21 | speed: options.fetch(:speed), 22 | alpha: options.fetch(:alpha), 23 | numeric: options.fetch(:numeric), 24 | random: options.fetch(:random), 25 | color: options.fetch(:color), 26 | background: options.fetch(:background) 27 | }) 28 | end 29 | 30 | return th.join 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/audio.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/audio.rb 2 | # Audio cues using either synth or samples 3 | # Synth: win32-sound, feep, or bloopsaphone depending on platform/choice 4 | # Samples: small wav files 5 | 6 | require_relative '../game_options' 7 | 8 | module Gemwarrior 9 | module Audio 10 | # CONSTANTS 11 | ERROR_SOUND_NOT_ENABLED = 'Sound is disabled! Enable in main options to hear sound.' 12 | ERROR_SOUND_SYSTEM_NOT_CHOSEN = 'Sound system not chosen! Choose one in main options to hear sound.' 13 | ERROR_BLOOPS_NOT_FOUND = 'bloopsaphone gem not found. Try \'gem install bloopsaphone\'.' 14 | ERROR_FEEP_NOT_FOUND = 'feep gem not found. Try \'gem install feep\'.' 15 | ERROR_WIN32_NOT_FOUND = 'win32-sound gem not found. Try \'gem install win32-sound\'.' 16 | 17 | def self.init 18 | if GameOptions.data['sound_system'].eql?('win32-sound') 19 | begin 20 | require 'win32/sound' 21 | rescue 22 | GameOptions.data['errors'] = "#{GameOptions.data['sound_system']} could not be loaded. You may need to run 'gem install #{GameOptions.data['sound_system']}'. Silence for now." 23 | end 24 | elsif GameOptions.data['sound_system'].eql?('feep') 25 | begin 26 | require 'feep' 27 | rescue 28 | GameOptions.data['errors'] = "#{GameOptions.data['sound_system']} could not be loaded. You may need to run 'gem install #{GameOptions.data['sound_system']}'. Silence for now." 29 | end 30 | elsif GameOptions.data['sound_system'].eql?('bloops') 31 | begin 32 | require 'bloops' 33 | rescue 34 | GameOptions.data['errors'] = "#{GameOptions.data['sound_system']} could not be loaded. You may need to run 'gem install #{GameOptions.data['sound_system']}aphone'. Silence for now." 35 | end 36 | else 37 | GameOptions.data['errors'] = "'#{GameOptions.data['sound_system']}' is not a valid sound system. Audio subsystem load failed." 38 | end 39 | end 40 | 41 | def self.get_cues 42 | if GameOptions.data['sound_enabled'] 43 | case GameOptions.data['sound_system'] 44 | when 'win32-sound', 'feep' 45 | require_relative 'audio_cues' 46 | AudioCues.cues 47 | when 'bloops' 48 | require_relative 'bloops_cues' 49 | BloopsCues.cues 50 | end 51 | end 52 | end 53 | 54 | def self.play_synth(audio_cue_symbol) 55 | if GameOptions.data['sound_enabled'] 56 | if GameOptions.data['sound_system'] 57 | case GameOptions.data['sound_system'] 58 | when 'win32-sound' 59 | if system('gem list -ie win32-sound') 60 | require_relative 'audio_cues' 61 | require_relative 'musical_notes' 62 | 63 | if AudioCues.cues[audio_cue_symbol] 64 | Thread.start do 65 | AudioCues.cues[audio_cue_symbol][:synth].each do |seq| 66 | threads = [] 67 | seq[:frequencies].split(',').each do |note| 68 | threads << Thread.new do 69 | Win32::Sound::play_freq(Notes::NOTE_FREQ[note], seq[:duration], GameOptions.data['sound_volume']) 70 | end 71 | end 72 | threads.each { |th| th.join } 73 | end 74 | end 75 | end 76 | else 77 | GameOptions.data['errors'] = ERROR_WIN32_NOT_FOUND 78 | end 79 | when 'feep' 80 | if system('gem list -ie feep') 81 | require_relative 'audio_cues' 82 | 83 | feep_defaults = { 84 | frequencies: '440', 85 | waveform: 'sine', 86 | volume: GameOptions.data['sound_volume'], 87 | duration: 500, 88 | notext: true 89 | } 90 | 91 | if AudioCues.cues[audio_cue_symbol] 92 | Thread.start do 93 | AudioCues.cues[audio_cue_symbol][:synth].each do |seq| 94 | seq = feep_defaults.merge(seq) 95 | 96 | Feep::Base.new({ 97 | freq_or_note: seq[:frequencies], 98 | waveform: seq[:waveform], 99 | volume: seq[:volume], 100 | duration: seq[:duration], 101 | notext: seq[:notext] 102 | }) 103 | end 104 | end 105 | end 106 | else 107 | GameOptions.data['errors'] = ERROR_FEEP_NOT_FOUND 108 | end 109 | when 'bloops' 110 | if system('gem list -ie bloops') 111 | require_relative 'bloops_cues' 112 | 113 | if BloopsCues.cues[audio_cue_symbol] 114 | Thread.start do 115 | BloopsCues.cues[audio_cue_symbol][:synth].each do |seq| 116 | threads = [] 117 | 118 | seq.each do |note| 119 | threads << Thread.new do 120 | b = Bloops.new 121 | b.tempo = 240 122 | snd = b.sound Bloops::SQUARE 123 | snd.punch = GameOptions.data['sound_volume']/2 124 | snd.sustain = 0.7 125 | snd.decay = 0.2 126 | b.tune snd, note[1] 127 | b.play 128 | sleep 0.1 while !b.stopped? 129 | end 130 | end 131 | 132 | threads.each { |th| th.join } 133 | end 134 | end 135 | end 136 | else 137 | GameOptions.data['errors'] = ERROR_BLOOPS_NOT_FOUND 138 | end 139 | end 140 | else 141 | GameOptions.data['errors'] = ERROR_SOUND_SYSTEM_NOT_CHOSEN 142 | end 143 | else 144 | GameOptions.data['errors'] = ERROR_SOUND_NOT_ENABLED 145 | end 146 | end 147 | 148 | def self.play_sample(audio_cue_symbol) 149 | # future use 150 | end 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/audio_cues.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/audio_cues.rb 2 | # Audio cue symbol -> notes/wav_file 3 | 4 | module Gemwarrior 5 | module AudioCues 6 | def self.add key, val 7 | @@cues ||= {} 8 | @@cues[key] = val 9 | end 10 | 11 | def self.cues 12 | @@cues ||= {} 13 | end 14 | 15 | self.add :bg1, { 16 | synth: [ 17 | { frequencies: 'C4', duration: 400 }, 18 | { frequencies: 'C5', duration: 400 }, 19 | { frequencies: 'C4', duration: 400 }, 20 | { frequencies: 'C5', duration: 400 }, 21 | { frequencies: 'C4', duration: 400 }, 22 | { frequencies: 'C5', duration: 400 }, 23 | { frequencies: 'G4', duration: 400 }, 24 | { frequencies: 'A4', duration: 400 }, 25 | { frequencies: 'C4', duration: 400 }, 26 | { frequencies: 'C5', duration: 400 }, 27 | { frequencies: 'C4', duration: 400 }, 28 | { frequencies: 'C5', duration: 400 }, 29 | { frequencies: 'C4', duration: 400 }, 30 | { frequencies: 'C5', duration: 400 }, 31 | { frequencies: 'B4', duration: 400 }, 32 | { frequencies: 'A4', duration: 400 }, 33 | ], 34 | sample: 'bg1.wav' 35 | } 36 | 37 | self.add :battle_start, { 38 | synth: [ 39 | { frequencies: 'G4', duration: 50 }, 40 | { frequencies: 'G#4', duration: 50 }, 41 | { frequencies: 'G4', duration: 50 }, 42 | { frequencies: 'G#4', duration: 50 }, 43 | { frequencies: 'G4', duration: 50 }, 44 | { frequencies: 'G#4', duration: 50 } 45 | ], 46 | sample: 'battle_start.wav' 47 | } 48 | 49 | self.add :battle_player_attack, { 50 | synth: [{ frequencies: 'A4,E4,B5', duration: 75 }], 51 | sample: 'battle_player_attack.wav' 52 | } 53 | 54 | self.add :battle_player_miss, { 55 | synth: [{ frequencies: 'A4', duration: 75 }], 56 | sample: 'battle_player_miss.wav' 57 | } 58 | 59 | self.add :battle_monster_attack, { 60 | synth: [{ frequencies: 'B4,E#5,A5', duration: 75 }], 61 | sample: 'battle_monster_attack.wav' 62 | } 63 | 64 | self.add :battle_monster_miss, { 65 | synth: [{ frequencies: 'B4', duration: 75 }], 66 | sample: 'battle_monster_miss.wav' 67 | } 68 | 69 | self.add :battle_win_boss, { 70 | synth: [ 71 | { frequencies: 'E5', duration: 50 }, 72 | { frequencies: 'F5', duration: 50 }, 73 | { frequencies: 'E5', duration: 100 }, 74 | { frequencies: 'E5', duration: 50 }, 75 | { frequencies: 'F5', duration: 50 }, 76 | { frequencies: 'E5', duration: 100 }, 77 | { frequencies: 'E4,A4', duration: 50 }, 78 | { frequencies: 'E4,A4', duration: 200 } 79 | ], 80 | sample: 'battle_win_boss.wav' 81 | } 82 | 83 | self.add :battle_win_monster, { 84 | synth: [ 85 | { frequencies: 'D5', duration: 50 }, 86 | { frequencies: 'E5', duration: 50 }, 87 | { frequencies: 'D5', duration: 100 }, 88 | { frequencies: 'D4', duration: 50 }, 89 | { frequencies: 'D4', duration: 200 } 90 | ], 91 | sample: 'battle_win_monster.wav' 92 | } 93 | 94 | self.add :intro, { 95 | synth: [ 96 | { frequencies: 'C4,G4', duration: 350 }, 97 | { frequencies: 'D3,A4', duration: 85 }, 98 | { frequencies: 'E3,B4', duration: 85 }, 99 | { frequencies: 'F3,C5', duration: 170 }, 100 | { frequencies: 'F3,C5', duration: 170 }, 101 | { frequencies: 'F3,C5', duration: 170 }, 102 | { frequencies: 'G3,B4,D5', duration: 350 } 103 | ], 104 | sample: 'intro.wav' 105 | } 106 | 107 | self.add :player_level_up, { 108 | synth: [ 109 | { frequencies: 'D4,A4,D5,A5,D6', duration: 100 }, 110 | { frequencies: 'D4,A4,D5,A5,D6', duration: 350 }, 111 | { frequencies: 'E4,B4,E5,B5,E6', duration: 500 } 112 | ] 113 | } 114 | 115 | self.add :player_resurrection, { 116 | synth: [ 117 | { frequencies: 'D#5', duration: 100 }, 118 | { frequencies: 'A4', duration: 150 }, 119 | { frequencies: 'F#4', duration: 200 }, 120 | { frequencies: 'F4', duration: 1000 } 121 | ], 122 | sample: 'player_resurrection.wav' 123 | } 124 | 125 | self.add :player_travel, { 126 | synth: [ 127 | { frequencies: 'C4', duration: 75 }, 128 | { frequencies: 'D4', duration: 75 }, 129 | { frequencies: 'E4', duration: 75 } 130 | ], 131 | sample: 'player_travel.wav' 132 | } 133 | 134 | self.add :player_travel_east, { 135 | synth: [ 136 | { frequencies: 'E4', duration: 75 }, 137 | { frequencies: 'G4', duration: 75 }, 138 | { frequencies: 'E4', duration: 75 } 139 | ], 140 | sample: 'player_travel_east.wav' 141 | } 142 | 143 | self.add :player_travel_north, { 144 | synth: [ 145 | { frequencies: 'C4', duration: 75 }, 146 | { frequencies: 'D4', duration: 75 }, 147 | { frequencies: 'E4', duration: 75 } 148 | ], 149 | sample: 'player_travel_north.wav' 150 | } 151 | 152 | self.add :player_travel_south, { 153 | synth: [ 154 | { frequencies: 'E4', duration: 75 }, 155 | { frequencies: 'D4', duration: 75 }, 156 | { frequencies: 'C4', duration: 75 } 157 | ], 158 | sample: 'player_travel_south.wav' 159 | } 160 | 161 | self.add :player_travel_west, { 162 | synth: [ 163 | { frequencies: 'C4', duration: 75 }, 164 | { frequencies: 'A3', duration: 75 }, 165 | { frequencies: 'C4', duration: 75 } 166 | ], 167 | sample: 'player_travel_west.wav' 168 | } 169 | 170 | self.add :resume_game, { 171 | synth: [ 172 | { frequencies: 'C4', duration: 75 }, 173 | { frequencies: 'D4', duration: 75 }, 174 | { frequencies: 'E4', duration: 75 }, 175 | { frequencies: 'F#4', duration: 75 }, 176 | { frequencies: 'G#4', duration: 75 }, 177 | { frequencies: 'A#4', duration: 75 }, 178 | { frequencies: 'C5', duration: 75 }, 179 | { frequencies: 'D5', duration: 75 }, 180 | { frequencies: 'E5', duration: 75 }, 181 | { frequencies: 'F#5', duration: 75 } 182 | ], 183 | sample: 'test.wav' 184 | } 185 | 186 | self.add :test, { 187 | synth: [ 188 | { frequencies: 'F4', duration: 75 }, 189 | { frequencies: 'E5', duration: 75 }, 190 | { frequencies: 'C5', duration: 75 }, 191 | { frequencies: 'E5', duration: 75 } 192 | ], 193 | sample: 'test.wav' 194 | } 195 | 196 | self.add :uncover_secret, { 197 | synth: [ 198 | { frequencies: 'F5', duration: 150 }, 199 | { frequencies: 'E5', duration: 150 }, 200 | { frequencies: 'C#4', duration: 150 }, 201 | { frequencies: 'C4', duration: 150 }, 202 | { frequencies: 'D#5', duration: 150 }, 203 | { frequencies: 'F#5', duration: 150 }, 204 | { frequencies: 'A5', duration: 150 } 205 | ], 206 | sample: 'uncover_secret.wav' 207 | } 208 | 209 | self.add :win_game, { 210 | synth: [ 211 | { frequencies: 'G3', duration: 250 }, 212 | { frequencies: 'A3', duration: 50 }, 213 | { frequencies: 'B3', duration: 50 }, 214 | { frequencies: 'C4', duration: 50 }, 215 | { frequencies: 'D4', duration: 250 }, 216 | { frequencies: 'E4', duration: 50 }, 217 | { frequencies: 'F#4', duration: 50 }, 218 | { frequencies: 'G4', duration: 50 }, 219 | { frequencies: 'A4', duration: 250 }, 220 | { frequencies: 'B4', duration: 50 }, 221 | { frequencies: 'C5', duration: 50 }, 222 | { frequencies: 'D5', duration: 50 }, 223 | { frequencies: 'E5', duration: 50 }, 224 | { frequencies: 'F#5', duration: 50 }, 225 | { frequencies: 'G5', duration: 1000 } 226 | ], 227 | sample: 'win_game.wav' 228 | } 229 | end 230 | end 231 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/bloops_cues.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/bloops_cues.rb 2 | # Bloops cue symbol -> notes/wav_file 3 | 4 | module Gemwarrior 5 | module BloopsCues 6 | def self.add key, val 7 | @@cues ||= {} 8 | @@cues[key] = val 9 | end 10 | 11 | def self.cues 12 | @@cues ||= {} 13 | end 14 | 15 | self.add :bg1, { 16 | synth: [ 17 | { s1: '2:C4' }, 18 | { s1: '2:C5' }, 19 | { s1: '2:C4' }, 20 | { s1: '2:C5' }, 21 | { s1: '2:C4' }, 22 | { s1: '2:C5' }, 23 | { s1: '2:G4' }, 24 | { s1: '2:A4' }, 25 | { s1: '2:C4' }, 26 | { s1: '2:C5' }, 27 | { s1: '2:C4' }, 28 | { s1: '2:C5' }, 29 | { s1: '2:C4' }, 30 | { s1: '2:C5' }, 31 | { s1: '2:B4' }, 32 | { s1: '2:A4' }, 33 | ], 34 | sample: 'bg1.wav' 35 | } 36 | 37 | self.add :battle_start, { 38 | synth: [ 39 | { s1: '16:G4' }, 40 | { s1: '16:G#4' }, 41 | { s1: '16:G4' }, 42 | { s1: '16:G#4' }, 43 | { s1: '16:G4' }, 44 | { s1: '16:G#4' } 45 | ], 46 | sample: 'battle_start.wav' 47 | } 48 | 49 | self.add :battle_player_attack, { 50 | synth: [{ s1: '16:A4', s2: '16:E4', s3: '16:B5' }], 51 | sample: 'battle_player_attack.wav' 52 | } 53 | 54 | self.add :battle_player_miss, { 55 | synth: [{ s1: '8:A4' }], 56 | sample: 'battle_player_miss.wav' 57 | } 58 | 59 | self.add :battle_monster_attack, { 60 | synth: [{ s1: '16:B4', s2: '16:E#5', s3: '16:A5' }], 61 | sample: 'battle_monster_attack.wav' 62 | } 63 | 64 | self.add :battle_monster_miss, { 65 | synth: [{ s1: '8:B4' }], 66 | sample: 'battle_monster_miss.wav' 67 | } 68 | 69 | self.add :battle_win_boss, { 70 | synth: [ 71 | { s1: '16:E5' }, 72 | { s1: '16:F5' }, 73 | { s1: '8:E5' }, 74 | { s1: '16:E5' }, 75 | { s1: '16:F5' }, 76 | { s1: '8:E5' }, 77 | { s1: '16:E4', s2: '16:A4' }, 78 | { s1: '4:E4', s2: '4:A4' } 79 | ], 80 | sample: 'battle_win_boss.wav' 81 | } 82 | 83 | self.add :battle_win_monster, { 84 | synth: [ 85 | { s1: '16:D5' }, 86 | { s1: '16:E5' }, 87 | { s1: '8:D5' }, 88 | { s1: '16:D4' }, 89 | { s1: '4:D4' } 90 | ], 91 | sample: 'battle_win_monster.wav' 92 | } 93 | 94 | self.add :intro, { 95 | synth: [ 96 | { s1: '4:C4', s2: '4:G4' }, 97 | { s1: '16:D3', s2: '16:A4' }, 98 | { s1: '16:E3', s2: '16:B4' }, 99 | { s1: '8:F3', s2: '8:C5' }, 100 | { s1: '8:F3', s2: '8:C5' }, 101 | { s1: '8:F3', s2: '8:C5' }, 102 | { s1: '4:G3', s2: '4:B4', s3: '4:D5' } 103 | ], 104 | sample: 'intro.wav' 105 | } 106 | 107 | self.add :player_level_up, { 108 | synth: [ 109 | { s1: '8:D4', s2: '8:A4', s3: '8:D5', s4: '8:A5', s5: '8:D6' }, 110 | { s1: '4:D4', s2: '4:A4', s3: '4:D5', s4: '4:A5', s5: '4:D6' }, 111 | { s1: '1:E4', s2: '1:B4', s3: '1:E5', s4: '1:B5', s5: '1:E6' } 112 | ] 113 | } 114 | 115 | self.add :player_resurrection, { 116 | synth: [ 117 | { s1: '16:D#5' }, 118 | { s1: '14:A4' }, 119 | { s1: '12:F#4' }, 120 | { s1: '2:F4' } 121 | ], 122 | sample: 'player_resurrection.wav' 123 | } 124 | 125 | self.add :player_travel, { 126 | synth: [ 127 | { s1: '16:C4' }, 128 | { s1: '16:D4' }, 129 | { s1: '16:E4' } 130 | ], 131 | sample: 'player_travel.wav' 132 | } 133 | 134 | self.add :player_travel_east, { 135 | synth: [ 136 | { s1: '16:E4' }, 137 | { s1: '16:G4' }, 138 | { s1: '16:E4' } 139 | ], 140 | sample: 'player_travel_east.wav' 141 | } 142 | 143 | self.add :player_travel_north, { 144 | synth: [ 145 | { s1: '16:C4' }, 146 | { s1: '16:D4' }, 147 | { s1: '16:E4' } 148 | ], 149 | sample: 'player_travel_north.wav' 150 | } 151 | 152 | self.add :player_travel_south, { 153 | synth: [ 154 | { s1: '16:E4' }, 155 | { s1: '16:D4' }, 156 | { s1: '16:C4' } 157 | ], 158 | sample: 'player_travel_south.wav' 159 | } 160 | 161 | self.add :player_travel_west, { 162 | synth: [ 163 | { s1: '16:C4' }, 164 | { s1: '16:A4' }, 165 | { s1: '16:C4' } 166 | ], 167 | sample: 'player_travel_west.wav' 168 | } 169 | 170 | self.add :resume_game, { 171 | synth: [ 172 | { s1: '16:C4' }, 173 | { s1: '16:D4' }, 174 | { s1: '16:E4' }, 175 | { s1: '8:F#4' } 176 | ], 177 | sample: 'test.wav' 178 | } 179 | 180 | self.add :test, { 181 | synth: [ 182 | { s1: '4:F4' }, 183 | { s1: '4:E5' }, 184 | { s1: '4:C5' }, 185 | { s1: '4:E5' } 186 | ], 187 | sample: 'test.wav' 188 | } 189 | 190 | self.add :uncover_secret, { 191 | synth: [ 192 | { s1: '8:F5' }, 193 | { s1: '8:E5' }, 194 | { s1: '8:C#4' }, 195 | { s1: '8:C4' }, 196 | { s1: '8:D#5' }, 197 | { s1: '8:F#5' }, 198 | { s1: '8:A5' } 199 | ], 200 | sample: 'uncover_secret.wav' 201 | } 202 | 203 | self.add :win_game, { 204 | synth: [ 205 | { s1: '4:G3' }, 206 | { s1: '16:A3' }, 207 | { s1: '16:B3' }, 208 | { s1: '16:C4' }, 209 | { s1: '4:D4' }, 210 | { s1: '16:E4' }, 211 | { s1: '16:F#4' }, 212 | { s1: '16:G4' }, 213 | { s1: '4:A4' }, 214 | { s1: '16:B4' }, 215 | { s1: '16:C5' }, 216 | { s1: '16:D5' }, 217 | { s1: '16:E5' }, 218 | { s1: '16:F#5' }, 219 | { s1: '1:G5' } 220 | ], 221 | sample: 'win_game.wav' 222 | } 223 | end 224 | end 225 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/formatting.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/formatting.rb 2 | # Formatting methods for text 3 | 4 | module Gemwarrior 5 | module Formatting 6 | def self.upstyle(str_arr, no_space = false) 7 | if str_arr.is_a? Array 8 | str_arr_upstyled = [] 9 | 10 | str_arr.each do |str_arr_item| 11 | str_arr_item_upstyled = [] 12 | 13 | str_arr_item.split(' ').each do |s| 14 | str_arr_item_upstyled << upstyle_string(s, no_space) 15 | end 16 | 17 | str_arr_upstyled << str_arr_item_upstyled.join(' ') 18 | end 19 | 20 | str_arr_upstyled 21 | else 22 | upstyle_string(str_arr, no_space) 23 | end 24 | end 25 | 26 | def to_hooch(s) 27 | words = s.split(' ') 28 | words_hooched = [] 29 | 30 | words.each do |w| 31 | if rand(0..100) > 60 and w.length > 1 32 | w = w.split('') 33 | w = w.insert(rand(0..w.length-1), '*HIC*') 34 | w = w.join('') 35 | end 36 | words_hooched << w 37 | end 38 | 39 | words_hooched.join(' ') 40 | end 41 | 42 | private 43 | 44 | def self.upstyle_string(s, no_space) 45 | s_upstyle = '' 46 | s_upstyle << s[0].upcase 47 | 1.upto(s.length-1) do |i| 48 | if s[i-1].eql?('_') 49 | s_upstyle[i-1] = no_space ? '' : ' ' 50 | s_upstyle << s[i].upcase 51 | else 52 | s_upstyle << s[i].downcase 53 | end 54 | end 55 | s_upstyle 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/hr.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/hr.rb 2 | # Standardized, cross-platform horizontal rule 3 | # Cribbed extensively from https://github.com/ivantsepp/hr 4 | 5 | require_relative '../game_options' 6 | 7 | module Gemwarrior 8 | module Hr 9 | extend self 10 | 11 | def print(*patterns) 12 | Kernel.print string(*patterns) 13 | end 14 | 15 | def string(*patterns) 16 | options = patterns.last.is_a?(Hash) ? patterns.pop : {} 17 | screen_width = GameOptions.data['wrap_width'] 18 | output = patterns.map do |pattern| 19 | pattern = pattern.to_s 20 | times = (screen_width / pattern.length) + 1 21 | (pattern * times)[0..screen_width - 1] 22 | end.join 23 | output << "\n" 24 | options = options.inject({}){|tmp,(k,v)| tmp[k.to_sym] = v.to_sym; tmp} 25 | options.any? ? output.colorize(options) : output 26 | end 27 | 28 | private 29 | 30 | def command_exists?(command) 31 | ENV['PATH'].split(File::PATH_SEPARATOR).any? { |path| 32 | (File.exist? File.join(path, "#{command}")) || (File.exist? File.join(path, "#{command}.com")) || (File.exist? File.join(path, "#{command}.exe")) 33 | } 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/musical_notes.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/musical_notes.rb 2 | # Musical note->freq hash 3 | 4 | module Gemwarrior 5 | module Notes 6 | NOTE_FREQ = Hash[ 7 | 'C0' => 16.351, 8 | 'C#0' => 17.324, 9 | 'Db0' => 17.324, 10 | 'D0' => 18.354, 11 | 'D#0' => 19.445, 12 | 'Eb0' => 19.445, 13 | 'E0' => 20.601, 14 | 'F0' => 21.827, 15 | 'F#0' => 23.124, 16 | 'Gb0' => 23.124, 17 | 'G0' => 24.499, 18 | 'G#0' => 25.956, 19 | 'Ab0' => 25.956, 20 | 'A0' => 27.500, 21 | 'A#0' => 29.135, 22 | 'Bb0' => 29.135, 23 | 'B0' => 30.868, 24 | 'C1' => 32.703, 25 | 'C#1' => 34.648, 26 | 'Db1' => 34.648, 27 | 'D1' => 36.708, 28 | 'D#1' => 38.891, 29 | 'Eb1' => 38.891, 30 | 'E1' => 41.203, 31 | 'F1' => 43.654, 32 | 'F#1' => 46.249, 33 | 'Gb1' => 46.249, 34 | 'G1' => 48.999, 35 | 'G#1' => 51.913, 36 | 'Ab1' => 51.913, 37 | 'A1' => 55.000, 38 | 'A#1' => 58.270, 39 | 'Bb1' => 58.270, 40 | 'B1' => 61.375, 41 | 'C2' => 65.406, 42 | 'C#2' => 69.296, 43 | 'Db2' => 69.296, 44 | 'D2' => 73.416, 45 | 'D#2' => 77.782, 46 | 'Eb2' => 77.782, 47 | 'E2' => 82.407, 48 | 'F2' => 87.307, 49 | 'F#2' => 92.499, 50 | 'Gb2' => 92.499, 51 | 'G2' => 97.999, 52 | 'G#2' => 97.999, 53 | 'Ab2' => 103.826, 54 | 'A2' => 110.000, 55 | 'A#2' => 116.541, 56 | 'Bb2' => 116.541, 57 | 'B2' => 123.471, 58 | 'C3' => 130.813, 59 | 'C#3' => 138.591, 60 | 'Db3' => 138.591, 61 | 'D3' => 146.832, 62 | 'D#3' => 155.564, 63 | 'Eb3' => 155.564, 64 | 'E3' => 164.814, 65 | 'F3' => 174.614, 66 | 'F#3' => 184.997, 67 | 'Gb3' => 184.997, 68 | 'G3' => 195.998, 69 | 'G#3' => 207.652, 70 | 'Ab3' => 207.652, 71 | 'A3' => 220.000, 72 | 'A#3' => 233.082, 73 | 'Bb3' => 233.082, 74 | 'B3' => 246.942, 75 | 'C4' => 261.626, 76 | 'C#4' => 277.183, 77 | 'Db4' => 277.183, 78 | 'D4' => 293.665, 79 | 'D#4' => 311.127, 80 | 'Eb4' => 311.127, 81 | 'E4' => 329.628, 82 | 'F4' => 349.228, 83 | 'F#4' => 369.994, 84 | 'Gb4' => 369.994, 85 | 'G4' => 391.995, 86 | 'G#4' => 415.305, 87 | 'Ab4' => 415.305, 88 | 'A4' => 440.000, 89 | 'A#4' => 466.164, 90 | 'Bb4' => 466.164, 91 | 'B4' => 493.883, 92 | 'C5' => 523.251, 93 | 'C#5' => 554.365, 94 | 'Db5' => 554.365, 95 | 'D5' => 587.330, 96 | 'D#5' => 622.254, 97 | 'Eb5' => 622.254, 98 | 'E5' => 659.255, 99 | 'F5' => 698.457, 100 | 'F#5' => 739.989, 101 | 'Gb5' => 739.989, 102 | 'G5' => 783.991, 103 | 'G#5' => 830.609, 104 | 'Ab5' => 830.609, 105 | 'A5' => 880.000, 106 | 'A#5' => 932.328, 107 | 'Bb5' => 932.328, 108 | 'B5' => 987.767, 109 | 'C6' => 1046.502, 110 | 'C#6' => 1108.731, 111 | 'Db6' => 1108.731, 112 | 'D6' => 1174.659, 113 | 'D#6' => 1244.508, 114 | 'Eb6' => 1244.508, 115 | 'E6' => 1318.510, 116 | 'F6' => 1396.913, 117 | 'F#6' => 1479.978, 118 | 'Gb6' => 1479.978, 119 | 'G6' => 1567.982, 120 | 'G#6' => 1661.219, 121 | 'Ab6' => 1661.219, 122 | 'A6' => 1760.000, 123 | 'A#6' => 1864.655, 124 | 'Bb6' => 1864.655, 125 | 'B6' => 1975.533, 126 | 'C7' => 2093.005, 127 | 'C#7' => 2217.461, 128 | 'Db7' => 2217.461, 129 | 'D7' => 2349.318, 130 | 'D#7' => 2489.016, 131 | 'Eb7' => 2489.016, 132 | 'E7' => 2637.021, 133 | 'F7' => 2793.826, 134 | 'F#7' => 2959.956, 135 | 'Gb7' => 2959.956, 136 | 'G7' => 3135.964, 137 | 'G#7' => 3322.438, 138 | 'Ab7' => 3322.438, 139 | 'A7' => 3520.000, 140 | 'A#7' => 3729.310, 141 | 'Bb7' => 3729.310, 142 | 'B7' => 3951.066, 143 | 'C8' => 4186.009, 144 | 'C#8' => 4434.922, 145 | 'Db8' => 4434.922, 146 | 'D8' => 4698.636, 147 | 'D#8' => 4978.032, 148 | 'Eb8' => 4978.032, 149 | 'E8' => 5274.042, 150 | 'F8' => 5587.652, 151 | 'F#8' => 5919.910, 152 | 'Gb8' => 5919.910, 153 | 'G8' => 6271.928, 154 | 'G#8' => 6644.876, 155 | 'Ab8' => 6644.876, 156 | 'A8' => 7040.000, 157 | 'A#8' => 7458.620, 158 | 'Bb8' => 7458.620, 159 | 'B8' => 7902.132, 160 | 'C9' => 8372.018, 161 | 'C#9' => 8869.844, 162 | 'Db9' => 8869.844, 163 | 'D9' => 9397.272, 164 | 'D#9' => 9956.064, 165 | 'Eb9' => 9956.064, 166 | 'E9' => 10548.084, 167 | 'F9' => 11175.304, 168 | 'F#9' => 11839.820, 169 | 'Gb9' => 11839.820, 170 | 'G9' => 12543.856, 171 | 'G#9' => 13289.752, 172 | 'Ab9' => 13289.752, 173 | 'A9' => 14080.000, 174 | 'A#9' => 14917.240, 175 | 'Bb9' => 14917.240, 176 | 'B9' => 15804.264 177 | ] 178 | end 179 | end 180 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/name_generator.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/name_generator.rb 2 | # NameGenerator Ruby class (translated from name_generator.js) 3 | # written and released to the public domain by drow 4 | # http://creativecommons.org/publicdomain/zero/1.0/ 5 | 6 | require 'yaml' 7 | 8 | class NameGenerator 9 | attr_accessor :type, :name_set, :chain_cache 10 | 11 | def initialize(type) 12 | self.type = type 13 | self.name_set = get_name_set(self.type) 14 | self.chain_cache = {} 15 | end 16 | 17 | # load sample names 18 | def get_name_set(type) 19 | names = [] 20 | names_data = YAML.load_file(File.expand_path("../../../../data/#{type}_names.yaml", __FILE__)) 21 | names_data.each do |n| 22 | names.push(n) 23 | end 24 | return names 25 | end 26 | 27 | # generator function 28 | def generate_name 29 | chain = nil 30 | 31 | if (chain = markov_chain(self.type)) 32 | return markov_name(chain) 33 | end 34 | 35 | return '' 36 | end 37 | 38 | # generate multiple 39 | def generate_names(count = 1) 40 | list = [] 41 | 42 | (1..count).each { 43 | list.push(generate_name) 44 | } 45 | 46 | return list 47 | end 48 | 49 | # get markov chain by type 50 | def markov_chain(type) 51 | chain = nil 52 | 53 | if (chain = chain_cache[type]) 54 | return chain 55 | else 56 | if (list = name_set) 57 | chain = nil 58 | if (chain = construct_chain(list)) 59 | chain_cache[type] = chain 60 | return chain 61 | end 62 | end 63 | end 64 | 65 | return false 66 | end 67 | 68 | # construct markov chain from list of names 69 | def construct_chain(list) 70 | chain = {} 71 | 72 | for i in 0..list.length-1 73 | names = list[i].split(/\s+/) 74 | chain = incr_chain(chain, 'parts', names.length) 75 | 76 | for j in 0..names.length-1 77 | name = names[j].nil? ? [] : names[j] 78 | chain = incr_chain(chain, 'name_len', name.length) 79 | 80 | c = name[0, 1] 81 | chain = incr_chain(chain, 'initial', c) 82 | 83 | string = name[1..name.length] 84 | last_c = c 85 | 86 | while string.length > 0 do 87 | c = string[0, 1] 88 | chain = incr_chain(chain, last_c, c) 89 | 90 | string = string[1..string.length] 91 | last_c = c 92 | end 93 | end 94 | end 95 | 96 | return scale_chain(chain) 97 | end 98 | 99 | def incr_chain(chain, key, token) 100 | if chain[key] 101 | if chain[key][token] 102 | chain[key][token] += 1 103 | else 104 | chain[key][token] = 1 105 | end 106 | else 107 | chain[key] = {} 108 | chain[key][token] = 1 109 | end 110 | 111 | return chain 112 | end 113 | 114 | def scale_chain(chain) 115 | table_len = {} 116 | 117 | chain.each do |key, subkey| 118 | table_len[key] = 0 119 | 120 | subkey.each do |subsubkey, value| 121 | count = value 122 | weighted = (count ** 1.3).floor 123 | 124 | chain[key][subsubkey] = weighted 125 | table_len[key] += weighted 126 | end 127 | end 128 | 129 | chain['table_len'] = table_len 130 | 131 | return chain 132 | end 133 | 134 | # construct name from markov chain 135 | def markov_name(chain) 136 | parts = select_link(chain, 'parts') 137 | names = [] 138 | 139 | (0..parts-1).each { 140 | name_len = select_link(chain, 'name_len') 141 | c = select_link(chain, 'initial') 142 | name = c 143 | last_c = c 144 | 145 | while name.length < name_len do 146 | c = select_link(chain, last_c) 147 | name += c 148 | last_c = c 149 | end 150 | names.push(name) 151 | } 152 | 153 | return names.join(' ') 154 | end 155 | 156 | def select_link(chain, key) 157 | len = chain['table_len'][key] 158 | idx = (rand() * len).floor 159 | 160 | t = 0 161 | chain[key].each do |chain_key, chain_value| 162 | t += chain_value 163 | return chain_key if (idx < t) 164 | end 165 | 166 | return '-' 167 | end 168 | end 169 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/player_levels.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/player_levels.rb 2 | # Stats table for each level the player can reach 3 | 4 | module Gemwarrior 5 | module PlayerLevels 6 | def self.get_level_stats(level) 7 | case level 8 | when 1 9 | { 10 | level: 1, xp_start: 0, 11 | hp_max: 30, 12 | atk_lo: 1, atk_hi: 2, 13 | defense: 1, dexterity: 3, 14 | special_abilities: nil 15 | } 16 | when 2 17 | { 18 | level: 2, xp_start: 50, 19 | hp_max: 35, 20 | atk_lo: 2, atk_hi: 3, 21 | defense: 3, dexterity: 4, 22 | special_abilities: :rocking_vision 23 | } 24 | when 3 25 | { 26 | level: 3, xp_start: 120, 27 | hp_max: 45, 28 | atk_lo: 3, atk_hi: 5, 29 | defense: 5, dexterity: 6, 30 | special_abilities: :gleam 31 | } 32 | when 4 33 | { 34 | level: 4, xp_start: 250, 35 | hp_max: 55, 36 | atk_lo: 5, atk_hi: 6, 37 | defense: 6, dexterity: 8, 38 | special_abilities: :rock_slide 39 | } 40 | when 5 41 | { 42 | level: 5, xp_start: 600, 43 | hp_max: 70, 44 | atk_lo: 7, atk_hi: 8, 45 | defense: 8, dexterity: 9, 46 | special_abilities: :graniton 47 | } 48 | when 6 49 | { 50 | level: 6, xp_start: 1000, 51 | hp_max: 85, 52 | atk_lo: 8, atk_hi: 10, 53 | defense: 10, dexterity: 11, 54 | special_abilities: :stone_face 55 | } 56 | when 7 57 | { 58 | level: 7, xp_start: 1500, 59 | hp_max: 100, 60 | atk_lo: 10, atk_hi: 12, 61 | defense: 13, dexterity: 14, 62 | special_abilities: :breakthru 63 | } 64 | else 65 | { 66 | level: 8, xp_start: 10000, 67 | hp_max: 200, 68 | atk_lo: 50, atk_hi: 100, 69 | defense: 50, dexterity: 100, 70 | special_abilities: nil 71 | } 72 | end 73 | end 74 | 75 | def self.check_level(xp) 76 | if xp < 50 77 | 1 78 | elsif xp < 120 79 | 2 80 | elsif xp < 250 81 | 3 82 | elsif xp < 600 83 | 4 84 | elsif xp < 1000 85 | 5 86 | elsif xp < 1500 87 | 6 88 | elsif xp < 10000 89 | 7 90 | else 91 | 8 92 | end 93 | end 94 | 95 | def self.get_ability_description(ability) 96 | case ability 97 | when :rocking_vision # LV2 98 | 'Allows you to see the enemy hit points while in battle.' 99 | when :gleam # LV3 100 | 'The map now shows every place in Jool, whether you have been there or not.' 101 | when :rock_slide # LV4 102 | 'Adds a random boost to the player\'s attack in battle.' 103 | when :graniton # LV5 104 | 'Chance to be much more accurate in your attacks.' 105 | when :stone_face # LV6 106 | 'Chance to auto-win in battle against any non-boss monster (does not work in arena or if ambushed).' 107 | when :breakthru 108 | 'Teleport to any location, given that you can remember its name.' 109 | else 110 | 'Unsure, but it\'s probably cool!' 111 | end 112 | end 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/timer.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/timer.rb 2 | # Timer 3 | 4 | require 'observer' 5 | 6 | module Gemwarrior 7 | class Timer 8 | include Observable 9 | 10 | attr_accessor :duration_in_s, :timer_name, :background, :progress, :verbose, :command 11 | 12 | DEFAULTS = { 13 | duration_in_s: 5, 14 | timer_name: 'timer', 15 | background: true, 16 | progress: false, 17 | verbose: false, 18 | command: nil 19 | } 20 | 21 | def initialize(options = {}, repl) 22 | options = DEFAULTS.merge(options) 23 | 24 | self.duration_in_s = options[:duration_in_s] 25 | self.timer_name = options[:timer_name] 26 | self.background = options[:background] 27 | self.progress = options[:progress] 28 | self.verbose = options[:verbose] 29 | self.command = options[:command] 30 | 31 | add_observer(repl) 32 | end 33 | 34 | def start 35 | if background 36 | Thread.start { self.run } 37 | else 38 | self.run 39 | end 40 | end 41 | 42 | def run 43 | puts "#{timer_name} began at #{Time.now} for #{duration_in_s} seconds" if verbose 44 | 45 | end_time = Time.now + duration_in_s 46 | 47 | loop do 48 | sleep 1 49 | print '.' if progress 50 | if Time.now >= end_time 51 | return stop 52 | end 53 | end 54 | end 55 | 56 | def stop 57 | puts "\n#{timer_name} ended at #{Time.now}" if verbose 58 | changed 59 | notify_observers(self.command) unless self.command.nil? 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/gemwarrior/misc/wordlist.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/misc/wordlist.rb 2 | # List of words for flavor text, hopefully randomized from Wordnik 3 | 4 | require 'http' 5 | require 'json' 6 | 7 | require_relative '../game_options' 8 | 9 | module Gemwarrior 10 | class WordList 11 | STATIC_ADJECTIVE_VALUES = [ 12 | '5 o\'clock-shadowed', 'angry', 'aristocratic', 'calm', 'choice', 'clinical', 'cracked', 'depressed', 'dingy', 'excited', 'ginormous', 'handsome', 'hydrothermal', 'lackadaisical', 'man-sized', 'moist', 'non-venomous', 'picaresque', 'positive', 'relaxed', 'ruddy', 'smooth', 'shocked', 'sticky', 'tense', 'tingly', 'tired', 'toneless', 'unpolished', 'worn' 13 | ] 14 | STATIC_NOUN_VALUES = [ 15 | 'arrestor', 'blockhead', 'busker', 'candlestick', 'cigarette', 'clavinet', 'cursor', 'degeneration', 'devotchka', 'drive', 'earthquake', 'genie', 'granddaddy', 'haunter', 'heater', 'locality', 'nitrogen', 'quitter', 'raccoon', 'radish', 'recession', 'sheepdog', 'smorgasbord', 'softener', 'sphere', 'stage-hand', 'tsunami', 'tuber', 'whatsit', 'zillionaire' 16 | ] 17 | STATIC_NOUN_PLURAL_VALUES = [ 18 | 'abutments', 'asterisms', 'bains', 'blebs', 'blowholes', 'chapes', 'civility', 'crocuses', 'dancers', 'deniers', 'diastoles', 'dinges', 'dualism', 'ebullitions', 'extremities', 'fingering', 'gabardines', 'gullets', 'knops', 'nooks', 'payments', 'phaetons', 'scalawags', 'snickers', 'specters', 'splats', 'squiggles', 'thalamuses', 'wallets', 'xylophones' 19 | ] 20 | STATIC_VERB_VALUES = [ 21 | 'accentuate', 'accompany', 'blatter', 'bully', 'collide', 'crusade', 'disallow', 'entitle', 'infest', 'lateral', 'micturate', 'mourn', 'munge', 'numb', 'outdraw', 'overstep', 'plummet', 'refill', 'refurnish', 'reroute', 'rumple', 'scupper', 'smoosh', 'spifflicate', 'straighten', 'synthesize', 'terrorize', 'unshift', 'vociferate' 22 | ] 23 | 24 | attr_accessor :type, :limit, :words, :error 25 | 26 | def initialize(type = 'noun', limit = 10) 27 | self.type = type 28 | self.limit = limit 29 | self.words = populate_words(type, limit) 30 | self.error = nil 31 | end 32 | 33 | def get_random_value 34 | random_value = words[rand(0..limit)] 35 | 36 | return random_value.nil? ? get_random_value : random_value 37 | end 38 | 39 | def list_words 40 | words.join(',') 41 | end 42 | 43 | private 44 | 45 | def populate_words(type, limit = 10) 46 | url = 'http://api.wordnik.com:80/v4/words.json/randomWords' 47 | api_key = ENV['WORDNIK_API_KEY'] 48 | 49 | unless api_key.nil? || GameOptions.data['use_wordnik'] == false 50 | case type 51 | when 'noun', 'noun-plural', 'adjective', 'verb' 52 | else 53 | error = 'invalid wordlist type' 54 | return 55 | end 56 | 57 | json_return = HTTP.get( 58 | url, 59 | params: { 60 | hasDictionaryDef: true, 61 | includePartOfSpeech: type, 62 | minCorpusCount: 1, 63 | maxCorpusCount: -1, 64 | minDictionaryCount: 1, 65 | maxDictionaryCount: -1, 66 | minLength: 5, 67 | maxLength: 12, 68 | sortBy: 'alpha', 69 | sortOrder: 'asc', 70 | limit: limit, 71 | api_key: api_key 72 | } 73 | ) 74 | 75 | json_data = JSON.parse(json_return.to_s) 76 | 77 | if json_data.include?('type') && json_data.include?('message') 78 | error = "wordnik #{json_data['type']}: #{json_data['message']}" 79 | return get_static_values 80 | else 81 | word_list = [] 82 | json_data.map {|j| word_list.push(j['word'])} 83 | if word_list.length > 0 84 | return word_list 85 | else 86 | error = 'Empty array from Wordnik' 87 | return get_static_values(type) 88 | end 89 | end 90 | end 91 | 92 | return get_static_values(type) 93 | end 94 | 95 | def get_static_values(type = nil) 96 | static_values = [] 97 | 0.upto(10) do 98 | case type 99 | when 'noun' 100 | static_values.push(STATIC_NOUN_VALUES[rand(0..STATIC_NOUN_VALUES.length-1)]) 101 | when 'noun-plural' 102 | static_values.push(STATIC_NOUN_PLURAL_VALUES[rand(0..STATIC_NOUN_PLURAL_VALUES.length-1)]) 103 | when 'adjective' 104 | static_values.push(STATIC_ADJECTIVE_VALUES[rand(0..STATIC_ADJECTIVE_VALUES.length-1)]) 105 | when 'verb' 106 | static_values.push(STATIC_VERB_VALUES[rand(0..STATIC_VERB_VALUES.length-1)]) 107 | else 108 | error = 'invalid wordlist type' 109 | return 110 | end 111 | end 112 | return static_values 113 | end 114 | 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /lib/gemwarrior/version.rb: -------------------------------------------------------------------------------- 1 | # lib/gemwarrior/version.rb 2 | # Version of Gem Warrior 3 | 4 | module Gemwarrior 5 | VERSION = '0.15.20' 6 | end 7 | -------------------------------------------------------------------------------- /spec/gemwarrior_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | include Gemwarrior 3 | 4 | describe Gemwarrior do 5 | let(:default_load_options) do 6 | GameOptions.add 'log_file_path', "#{Dir.home}/.gemwarrior_log" 7 | GameOptions.add 'options_file_path', "#{Dir.home}/.gemwarrior_options" 8 | 9 | Game.new( 10 | beast_mode: false, 11 | debug_mode: false, 12 | god_mode: false, 13 | new_game: false, 14 | sound_enabled: false, 15 | sound_volume: 0.3, 16 | use_wordnik: false, 17 | extra_command: nil 18 | ) 19 | end 20 | 21 | describe 'basic properties' do 22 | it 'has a version number' do 23 | expect(VERSION).not_to be nil 24 | end 25 | it 'display help menu' do 26 | skip 'not implemented yet' do 27 | end 28 | end 29 | end 30 | 31 | describe 'game load' do 32 | context 'default options' do 33 | it 'displays the main menu' do 34 | skip 'not implemented yet' do 35 | end 36 | end 37 | end 38 | context 'new game option enabled' do 39 | it 'displays the main game prompt and runs the look command' do 40 | skip 'not implemented yet' do 41 | end 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # binary/executable 2 | $LOAD_PATH.unshift File.expand_path('../../bin', __FILE__) 3 | # main module 4 | $LOAD_PATH.unshift File.expand_path('../../lib/gemwarrior', __FILE__) 5 | require 'pry' 6 | require 'game' 7 | -------------------------------------------------------------------------------- /tests/compare_versions.rb: -------------------------------------------------------------------------------- 1 | new_release_available = false 2 | 3 | puts 'Checking releases...' 4 | 5 | remote_release = '0.10.3' 6 | local_release = '0.10.3' 7 | 8 | 0.upto(2) do |i| 9 | if remote_release.split('.')[i].to_i > local_release.split('.')[i].to_i 10 | new_release_available = true 11 | end 12 | end 13 | 14 | status_text = new_release_available ? "New v#{remote_release} available!" : 'You have the latest version.' 15 | 16 | puts "local_release: #{local_release}" 17 | puts "remote_release #{remote_release}" 18 | puts status_text -------------------------------------------------------------------------------- /tests/getch_test.rb: -------------------------------------------------------------------------------- 1 | require 'io/console' 2 | 3 | puts 'press a key using STDIN.getc' 4 | c = STDIN.getc 5 | puts "c: #{c.dump}" 6 | 7 | puts 'press another key using STDIN.getch' 8 | ch = STDIN.getch 9 | puts "ch: #{ch.dump}" 10 | -------------------------------------------------------------------------------- /tests/hooch.rb: -------------------------------------------------------------------------------- 1 | s = 'I still can\'t believe I lost at the Arena! I was doing so well, and then a slippery citrinaga got a cheap shot on me.' 2 | words = s.split(' ') 3 | words_hooched = [] 4 | 5 | words.each do |w| 6 | puts "w: #{w}" 7 | if rand(0..100) > 45 and w.length > 1 8 | w = w.split('') 9 | w = w.insert(rand(0..w.length-1), '*HIC*') 10 | w = w.join('') 11 | end 12 | words_hooched << w 13 | end 14 | 15 | puts 16 | puts "Final sentence: #{words_hooched.join(' ')}" 17 | -------------------------------------------------------------------------------- /tests/item_hashes.rb: -------------------------------------------------------------------------------- 1 | class Item 2 | attr_accessor :name 3 | 4 | def initialize(name = 'item') 5 | self.name = name 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /tests/json_api_call.rb: -------------------------------------------------------------------------------- 1 | require 'http' 2 | require 'json' 3 | 4 | url = 'http://api.wordnik.com:80/v4/words.json/randomWords' 5 | 6 | json_return = HTTP.get( 7 | url, 8 | :params => { 9 | :hasDictionaryDef => true, 10 | :includePartOfSpeech => 'noun-plural', 11 | :minCorpusCount => 10, 12 | :maxCorpusCount => -1, 13 | :minDictionaryCount => 1, 14 | :maxDictionaryCount => -1, 15 | :minLength => 5, 16 | :maxLength => 12, 17 | :sortBy => 'alpha', 18 | :sortOrder => 'asc', 19 | :limit => 10, 20 | :api_key => '63f5001dfacf2d619230e08591702875786da258b471becb6' 21 | } 22 | ) 23 | 24 | if json_return 25 | json_data = JSON.parse(json_return.to_s) 26 | 27 | if json_data.include?("type") && json_data.include?("message") 28 | puts "wordnik error: #{json_data["message"]}" 29 | else 30 | word_list = [] 31 | json_data.map {|j| word_list.push(j["word"])} 32 | if word_list.length > 0 33 | p word_list 34 | else 35 | puts 'no words received' 36 | end 37 | end 38 | else 39 | puts "json error: #{json_return.to_s}" 40 | end 41 | 42 | 43 | -------------------------------------------------------------------------------- /tests/module_hashes.rb: -------------------------------------------------------------------------------- 1 | module AudioCues 2 | def self.add key, val 3 | @@cues ||= {} 4 | @@cues[key] = val 5 | end 6 | 7 | def self.cues 8 | @@cues ||= {} 9 | end 10 | 11 | self.add :battle_start, { 12 | synth: [ 13 | { frequencies: 'G4', duration: 50 }, 14 | { frequencies: 'G#4', duration: 50 }, 15 | { frequencies: 'G4', duration: 50 }, 16 | { frequencies: 'G#4', duration: 50 }, 17 | { frequencies: 'G4', duration: 50 }, 18 | { frequencies: 'G#4', duration: 50 } 19 | ], 20 | sample: 'battle_start.wav' 21 | } 22 | 23 | self.add :battle_player_attack, { 24 | synth: [{ frequencies: 'A4,E4,B5', duration: 75 }], 25 | sample: 'battle_player_attack.wav' 26 | } 27 | 28 | self.add :battle_player_miss, { 29 | synth: [{ frequencies: 'A4', duration: 75 }], 30 | sample: 'battle_player_miss.wav' 31 | } 32 | 33 | self.add :battle_monster_attack, { 34 | synth: [{ frequencies: 'B4,E#5,A5', duration: 75 }], 35 | sample: 'battle_monster_attack.wav' 36 | } 37 | 38 | self.add :battle_monster_miss, { 39 | synth: [{ frequencies: 'B4', duration: 75 }], 40 | sample: 'battle_monster_miss.wav' 41 | } 42 | 43 | self.add :intro, { 44 | synth: [ 45 | { frequencies: 'A3,E4,C#5,E5', duration: 300 }, 46 | { frequencies: 'A3,E4,C#5,F#5', duration: 600 } 47 | ], 48 | sample: 'intro.wav' 49 | } 50 | 51 | self.add :player_resurrection, { 52 | synth: [ 53 | { frequencies: 'D#5', duration: 100 }, 54 | { frequencies: 'A4', duration: 150 }, 55 | { frequencies: 'F#4', duration: 200 }, 56 | { frequencies: 'F4', duration: 1000 } 57 | ], 58 | sample: 'player_resurrection.wav' 59 | } 60 | 61 | self.add :player_travel, { 62 | synth: [ 63 | { frequencies: 'C4', duration: 75 }, 64 | { frequencies: 'D4', duration: 75 }, 65 | { frequencies: 'E4', duration: 75 } 66 | ], 67 | sample: 'player_travel.wav' 68 | } 69 | 70 | self.add :win_game, { 71 | synth: [ 72 | { frequencies: 'G3', duration: 250 }, 73 | { frequencies: 'A3', duration: 50 }, 74 | { frequencies: 'B3', duration: 50 }, 75 | { frequencies: 'C4', duration: 50 }, 76 | { frequencies: 'D4', duration: 250 }, 77 | { frequencies: 'E4', duration: 50 }, 78 | { frequencies: 'F#4', duration: 50 }, 79 | { frequencies: 'G4', duration: 50 }, 80 | { frequencies: 'A4', duration: 250 }, 81 | { frequencies: 'B4', duration: 50 }, 82 | { frequencies: 'C5', duration: 50 }, 83 | { frequencies: 'D5', duration: 50 }, 84 | { frequencies: 'E5', duration: 50 }, 85 | { frequencies: 'F#5', duration: 50 }, 86 | { frequencies: 'G5', duration: 1000 } 87 | ], 88 | sample: 'win_game.wav' 89 | } 90 | end 91 | -------------------------------------------------------------------------------- /tests/test-class/test-class.rb: -------------------------------------------------------------------------------- 1 | class Rat 2 | attr_accessor :name, 3 | :level, 4 | :hp 5 | 6 | def initialize(name = 'rat') 7 | self.name = name 8 | self.level = rand(1..4) 9 | self.hp = rand((self.level * 3)..(self.level * 5)) 10 | end 11 | end 12 | 13 | m = [Rat.new] 14 | 15 | rat_o = m[0] 16 | rat_c = m[0].clone 17 | 18 | puts "[initial]" 19 | puts "m[0] : #{m[0].inspect}" 20 | puts "rat_o : #{rat_o.inspect}" 21 | puts "rat_c : #{rat_c.inspect}" 22 | 23 | m[0].hp -= 1 24 | 25 | puts "[m[0] - 1hp]" 26 | puts "m[0] : #{m[0].inspect}" 27 | puts "rat_o : #{rat_o.inspect}" 28 | puts "rat_c : #{rat_c.inspect}" 29 | 30 | rat_o.hp -= 1 31 | 32 | puts "[rat_o - 1hp]" 33 | puts "m[0] : #{m[0].inspect}" 34 | puts "rat_o : #{rat_o.inspect}" 35 | puts "rat_c : #{rat_c.inspect}" 36 | 37 | rat_c.hp -= 1 38 | 39 | puts "[rat_c - 1hp]" 40 | puts "m[0] : #{m[0].inspect}" 41 | puts "rat_o : #{rat_o.inspect}" 42 | puts "rat_c : #{rat_c.inspect}" 43 | 44 | rat_c2 = m[0].clone 45 | 46 | puts "[rat_c2 created from m[0]]" 47 | puts "m[0] : #{m[0].inspect}" 48 | puts "rat_o : #{rat_o.inspect}" 49 | puts "rat_c : #{rat_c.inspect}" 50 | puts "rat_c2: #{rat_c2.inspect}" 51 | -------------------------------------------------------------------------------- /tests/test-feep/test-feep.rb: -------------------------------------------------------------------------------- 1 | require 'feep' 2 | 3 | sequence = [ 4 | {freq_or_note: 'A3', duration: 80}, 5 | {freq_or_note: 'E4', duration: 80}, 6 | {freq_or_note: 'G4', duration: 80}, 7 | {freq_or_note: 'C#5', duration: 80}, 8 | {freq_or_note: 'G5', duration: 240} 9 | ] 10 | 11 | defaults = { 12 | :freq_or_note => '440', 13 | :waveform => 'saw', 14 | :volume => 0.3, 15 | :duration => 100, 16 | :notext => true 17 | } 18 | 19 | sequence.each do |seq| 20 | puts "seq before merge: #{seq}" 21 | 22 | seq = defaults.merge(seq) 23 | 24 | puts "seq after merge: #{seq}" 25 | 26 | puts "playing #{seq[:freq_or_note]}" 27 | 28 | Feep::Base.new({ 29 | :freq_or_note => seq[:freq_or_note], 30 | :waveform => seq[:waveform], 31 | :volume => seq[:volume], 32 | :duration => seq[:duration], 33 | :notext => seq[:notext] 34 | }) 35 | end -------------------------------------------------------------------------------- /tests/test-save-load/save-load.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'io/console' 3 | 4 | class SaveLoad 5 | SAVE_DATA_FILE = 'save.conf' 6 | 7 | attr_accessor :data_changed 8 | 9 | def initialize 10 | self.data_changed = false 11 | end 12 | 13 | module Config 14 | def self.add key, value 15 | @@data ||= {} 16 | @@data[key] = value 17 | end 18 | 19 | def self.data 20 | @@data ||= {} 21 | end 22 | end 23 | 24 | def show_change_data_menu 25 | puts 'Change Data' 26 | puts '-----------' 27 | puts 28 | puts " (1) Name: #{Config.data['name']}" 29 | puts " (2) Age: #{Config.data['age']}" 30 | puts " (3) Hometown: #{Config.data['hometown']}" 31 | puts 32 | puts ' (q) Back to main menu' 33 | puts 34 | print 'data> ' 35 | choice = STDIN.getch.downcase 36 | 37 | case choice 38 | when '1' 39 | print 'Name: ' 40 | answer = gets.chomp 41 | 42 | unless answer.empty? 43 | if Config.data['name'].nil? 44 | Config.add 'name', answer 45 | else 46 | Config.data['name'] = answer 47 | end 48 | self.data_changed = true 49 | end 50 | show_change_data_menu 51 | when '2' 52 | print 'Age: ' 53 | answer = gets.chomp 54 | 55 | unless answer.empty? 56 | if Config.data['age'].nil? 57 | Config.add 'age', answer 58 | else 59 | Config.data['age'] = answer 60 | end 61 | self.data_changed = true 62 | end 63 | show_change_data_menu 64 | when '3' 65 | print 'Hometown: ' 66 | answer = gets.chomp 67 | 68 | unless answer.empty? 69 | if Config.data['hometown'].nil? 70 | Config.add 'hometown', answer 71 | else 72 | Config.data['hometown'] = answer 73 | end 74 | self.data_changed = true 75 | end 76 | show_change_data_menu 77 | when 'q', 'x' 78 | puts 79 | return 80 | else 81 | show_change_data_menu 82 | end 83 | end 84 | 85 | def show_main_menu 86 | puts 'Main Menu' 87 | puts '---------' 88 | puts 89 | puts ' (1) Display loaded data' 90 | puts ' (2) Change data' 91 | puts ' (3) Read from save file' 92 | puts ' (4) Write to save file' 93 | puts 94 | puts ' (q) Quit from script' 95 | puts 96 | print 'menu> ' 97 | choice = STDIN.getch.downcase 98 | 99 | case choice 100 | when '1' 101 | puts choice 102 | display_loaded_data 103 | show_main_menu 104 | when '2' 105 | puts choice 106 | show_change_data_menu 107 | show_main_menu 108 | when '3' 109 | puts choice 110 | read_from_save 111 | show_main_menu 112 | when '4' 113 | puts choice 114 | write_to_save 115 | show_main_menu 116 | when 'x', 'q' 117 | puts choice 118 | if data_changed 119 | puts 'You changed stuff. Do you want to save before exiting? (y/n)' 120 | answer = STDIN.getch.downcase 121 | 122 | case answer 123 | when 'y' 124 | write_to_save 125 | end 126 | end 127 | puts "Exiting..." 128 | exit 129 | else 130 | show_main_menu 131 | end 132 | end 133 | 134 | def display_loaded_data 135 | if Config.data.empty? 136 | puts 'No loaded data yet.' 137 | puts 138 | else 139 | Config.data.each do |key, val| 140 | puts "#{key} : #{val}" 141 | end 142 | puts 143 | end 144 | end 145 | 146 | def read_from_save 147 | if File.exist?(SAVE_DATA_FILE) 148 | #save_data = YAML.load_file(SAVE_DATA_FILE) 149 | 150 | print 'Overwrite current session with saved data? (y/n) ' 151 | a = STDIN.getch.chomp.downcase 152 | 153 | case a 154 | when 'y' 155 | File.open(SAVE_DATA_FILE).readlines.each do |line| 156 | l = line.split(':') 157 | Config.add l[0], l[1] 158 | end 159 | puts "Successfully read from #{SAVE_DATA_FILE}" 160 | puts 161 | end 162 | else 163 | puts 'No save file.' 164 | end 165 | end 166 | 167 | def write_to_save 168 | File.open(SAVE_DATA_FILE, 'w') do |f| 169 | Config.data.each do |key, val| 170 | f.puts "#{key}:#{val}" 171 | end 172 | end 173 | puts "Wrote to #{SAVE_DATA_FILE}" 174 | puts 175 | end 176 | end 177 | 178 | puts 'Welcome to the Save/Load data test!' 179 | puts 180 | sl = SaveLoad.new 181 | sl.show_main_menu 182 | -------------------------------------------------------------------------------- /tests/test-save-load/save.conf: -------------------------------------------------------------------------------- 1 | name:mike 2 | age:34 3 | -------------------------------------------------------------------------------- /tests/test-singleton/game.rb: -------------------------------------------------------------------------------- 1 | require_relative 'game_options' 2 | 3 | puts GameOptions.data 4 | puts GameOptions.data['sound_enabled'] 5 | -------------------------------------------------------------------------------- /tests/test-singleton/game_options.rb: -------------------------------------------------------------------------------- 1 | module GameOptions 2 | def self.add key, value 3 | @@data ||= {} 4 | @@data[key] = value 5 | end 6 | 7 | def self.data 8 | @@data ||= {} 9 | end 10 | end 11 | 12 | GameOptions.data 13 | 14 | GameOptions.add 'sound_enabled', true 15 | GameOptions.add 'sound_volume', 0.3 16 | --------------------------------------------------------------------------------