srs_game 0.Alexis
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.travis.yml +3 -0
- data/.yardopts +2 -0
- data/Gemfile +6 -0
- data/LICENSE +12 -0
- data/README.md +57 -0
- data/Rakefile.rb +42 -0
- data/examples/pokemon_clone.rb +68 -0
- data/lib/srs_game/basic.rb +13 -0
- data/lib/srs_game/tamera.rb +67 -0
- data/lib/srs_game/tia.rb +7 -0
- data/lib/srs_game/version.rb +3 -0
- data/lib/srs_game.rb +429 -0
- data/spec/game_spec.rb +33 -0
- data/spec/helpers_spec.rb +62 -0
- data/spec/location_spec.rb +70 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/srserver_spec.rb +18 -0
- data/srs_game.gemspec +21 -0
- metadata +71 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Copyright (c) 2012 Jackson Willis
|
2
|
+
|
3
|
+
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
4
|
+
|
5
|
+
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
6
|
+
|
7
|
+
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
8
|
+
If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
9
|
+
|
10
|
+
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
11
|
+
|
12
|
+
3. This notice may not be removed or altered from any source distribution.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
SRS GAME
|
2
|
+
========
|
3
|
+
|
4
|
+
[](https://secure.travis-ci.org/jacksonwillis/srs_game)
|
5
|
+
[](https://gemnasium.com/jacksonwillis/srs_game)
|
6
|
+
|
7
|
+

|
8
|
+
|
9
|
+
Welcome
|
10
|
+
-------
|
11
|
+
|
12
|
+
SRS GAME is a framework and a collection of text-based games written in the [Ruby](http://www.ruby-lang.org/) 1.9 programming language.
|
13
|
+
|
14
|
+
If you find a bug, feel free to [create an issue](https://github.com/jacksonwillis/srs_game/issues/new).
|
15
|
+
|
16
|
+
Installation
|
17
|
+
------------
|
18
|
+
|
19
|
+
*Main article: [Installation](https://github.com/jacksonwillis/srs_game/wiki/Installation)*
|
20
|
+
|
21
|
+
Playing the game
|
22
|
+
----------------
|
23
|
+
|
24
|
+
$ rake play:tamera # Play as the goddess Tamera
|
25
|
+
$ rake play:tia # Play as a follower of the Cult of Tia
|
26
|
+
|
27
|
+
Running a server
|
28
|
+
----------------
|
29
|
+
|
30
|
+
$ rake server:tamera HOST=127.0.0.1 PORT=5446 MAX_CONNECTIONS=4
|
31
|
+
|
32
|
+
Testing
|
33
|
+
-------
|
34
|
+
|
35
|
+
To test, just run `rake`
|
36
|
+
or check out the [automated tests](https://secure.travis-ci.org/jacksonwillis/srs_game).
|
37
|
+
|
38
|
+
API
|
39
|
+
---
|
40
|
+
|
41
|
+
irb > require "srs_game/tamera"
|
42
|
+
=> true
|
43
|
+
irb > game = Game.new(Tamera, color: false)
|
44
|
+
=> #<SRSGame::Game:0x000000022f1337>
|
45
|
+
irb > game.send "look"
|
46
|
+
=> "You find yourself in the Main Room.\nItems here are an AM/FM Radio.\nExits are east and west."
|
47
|
+
irb > game.send "north"
|
48
|
+
=> "NOPE. Can't go that way."
|
49
|
+
irb > game.send "west"
|
50
|
+
=> ""
|
51
|
+
irb > game.room
|
52
|
+
=> #<SRSGame::Location "the West Room" @items=[] exits=["east", "west"]>
|
53
|
+
|
54
|
+
License
|
55
|
+
-------
|
56
|
+
|
57
|
+
SRS GAME is released under the zlib license. See [LICENSE](https://github.com/jacksonwillis/srs_game/blob/master/LICENSE)
|
data/Rakefile.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
# This file is part of SRS GAME <http://github.com/jacksonwillis/srs_game/>.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
6
|
+
require "srs_game"
|
7
|
+
require "rake"
|
8
|
+
|
9
|
+
require "bundler/gem_tasks"
|
10
|
+
|
11
|
+
namespace :play do
|
12
|
+
desc "Play as a follower of the Cult of Tia"
|
13
|
+
task :tia do
|
14
|
+
require "srs_game/tia"
|
15
|
+
Game.new(Tia, color: true).play
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Play as the goddess Tamera"
|
19
|
+
task :tamera do
|
20
|
+
require "srs_game/tamera"
|
21
|
+
Game.new(Tamera, color: true).play
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
namespace :server do
|
26
|
+
desc "Telnet server for Tia"
|
27
|
+
task :tia do
|
28
|
+
require "srs_game/tia"
|
29
|
+
SRServer.play(Tia, ENV["PORT"], ENV["HOST"], ENV["MAX_CONNECTIONS"])
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Telnet server for Tamera"
|
33
|
+
task :tamera do
|
34
|
+
require "srs_game/tamera"
|
35
|
+
SRServer.play(Tamera, ENV["PORT"], ENV["HOST"], ENV["MAX_CONNECTIONS"])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require "rspec/core/rake_task"
|
40
|
+
RSpec::Core::RakeTask.new
|
41
|
+
|
42
|
+
task :default => :spec
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
# This file is part of SRS GAME <http://github.com/jacksonwillis/srs_game/>.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
6
|
+
require "srs_game"
|
7
|
+
include SRSGame
|
8
|
+
|
9
|
+
module PokemonClone
|
10
|
+
class Computer < Item
|
11
|
+
interactable_as :computer
|
12
|
+
name "red's computer"
|
13
|
+
end
|
14
|
+
|
15
|
+
class Bed < Item
|
16
|
+
interactable_as :bed
|
17
|
+
name "YOUR BED"
|
18
|
+
end
|
19
|
+
|
20
|
+
def greeting
|
21
|
+
" /\"*-.\n" << \
|
22
|
+
" / `-.\n" << \
|
23
|
+
" / `-.\n" << \
|
24
|
+
" / `-.\n" << \
|
25
|
+
" `\"*-._ `-.\n" << \
|
26
|
+
" \"*-. .-'\n" << \
|
27
|
+
" .-' .-'\n" << \
|
28
|
+
" <' <'\n" << \
|
29
|
+
" `-. `-. .\n" << \
|
30
|
+
" .' .-' $\n" << \
|
31
|
+
" _ .' .-' bug :$;\n" << \
|
32
|
+
" T$bp.L.-*\"\"*-._ d$b\n" << \
|
33
|
+
" `TP `-. `-. : T$\n" << \
|
34
|
+
" .' `. `. `. ; ;\n" << \
|
35
|
+
" / `. \\ _. \\: :\n" << \
|
36
|
+
" / `..-\" ; |\n" << \
|
37
|
+
" : / ;\n" << \
|
38
|
+
" ; \\ / _ :\n" << \
|
39
|
+
" /`--'\\ .' $o$ |\n" << \
|
40
|
+
"/ / `./-, `\"' _ :\n" << \
|
41
|
+
"'-' : ; _ ' $o$ ;\n" << \
|
42
|
+
" ;Y\" |\"-. `\"' /\n" << \
|
43
|
+
" | `. L.' .-. /`*.\n" << \
|
44
|
+
" : `-. ; :' \\\n" << \
|
45
|
+
" ; :`*-._L.-'`-. :\n" << \
|
46
|
+
" : ; `-.*\n" << \
|
47
|
+
" \\ /\n" << \
|
48
|
+
" \"\" ~* PokemonClone.rb *~\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
def main_room
|
52
|
+
house = L.new(:name => "outside your house")
|
53
|
+
house.in = L.new(:name => "in your living room")
|
54
|
+
house.in.up = L.new(:name => "at the top of your stairs")
|
55
|
+
|
56
|
+
room = house.in.up.north = L.new(:name => "at the threshold your room")
|
57
|
+
room.north = L.new(:name => "standing in the middle of your room", :items => [Bed.new])
|
58
|
+
room.north.west = L.new(:name => "at your computer", :items => [Computer.new])
|
59
|
+
|
60
|
+
house.south = L.new(:name => "at the Pallet Town plaza")
|
61
|
+
house
|
62
|
+
end
|
63
|
+
|
64
|
+
class Commands < SRSGame::Commands
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
SRSGame::Game.new(PokemonClone, color: true).play if __FILE__ == $0
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
# This file is part of SRS GAME <http://github.com/jacksonwillis/srs_game/>.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../../", __FILE__)
|
6
|
+
require "srs_game"
|
7
|
+
include SRSGame
|
8
|
+
|
9
|
+
module SRSGame::Basic
|
10
|
+
def main_room
|
11
|
+
Location.new
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
# This file is part of SRS GAME <http://github.com/jacksonwillis/srs_game/>.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../../", __FILE__)
|
6
|
+
require "srs_game"
|
7
|
+
include SRSGame
|
8
|
+
|
9
|
+
# Play as the goddess Tamera
|
10
|
+
module SRSGame::Tamera
|
11
|
+
class BallaAssSpoon < Item
|
12
|
+
name "a Balla Ass Spoon"
|
13
|
+
interactable_as :spoon
|
14
|
+
end
|
15
|
+
|
16
|
+
class CrystalBall < Item
|
17
|
+
name "a Crystal Ball"
|
18
|
+
interactable_as :crystal
|
19
|
+
end
|
20
|
+
|
21
|
+
class Radio < Item
|
22
|
+
name "an AM/FM Radio"
|
23
|
+
interactable_as :radio
|
24
|
+
end
|
25
|
+
|
26
|
+
# Room where you begin in
|
27
|
+
def main_room
|
28
|
+
main = L.new do |l|
|
29
|
+
l.name = "in the Main Room"
|
30
|
+
l.items = [Radio.new]
|
31
|
+
end
|
32
|
+
|
33
|
+
main.east = L.new do |l|
|
34
|
+
l.name = "in the East Room"
|
35
|
+
l.items = [BallaAssSpoon.new, CrystalBall.new]
|
36
|
+
end
|
37
|
+
|
38
|
+
dungeon = main.east.south = L.new do |l|
|
39
|
+
l.name = "a creepy dungeon"
|
40
|
+
end
|
41
|
+
|
42
|
+
dungeon.south = L.new do |l|
|
43
|
+
l.name = "Hell"
|
44
|
+
end
|
45
|
+
|
46
|
+
west_room = main.west = L.new do |l|
|
47
|
+
l.name = "the West Room"
|
48
|
+
end
|
49
|
+
|
50
|
+
far_west = west_room.west = L.new do |l|
|
51
|
+
l.name = "the Far West Room"
|
52
|
+
end
|
53
|
+
|
54
|
+
far_west.north = L.new do |l|
|
55
|
+
l.name = "the North West Room"
|
56
|
+
end
|
57
|
+
|
58
|
+
return main
|
59
|
+
end
|
60
|
+
|
61
|
+
# Emitted before the game starts
|
62
|
+
def greeting
|
63
|
+
"\n ##### ##### ## # #### ######\n # # # # # # # # #\n # # # # # # # #### #####\n ##### ##### ###### # # #\n # # # # # # # # #\n # # # # # # #### ######\n\n\n ##### ######\n # # #\n ##### #####\n # # #\n # # #\n ##### ######\n\n\n ##### ####\n # # #\n # # #\n # # #\n # # #\n # ####\n\n\n ##### ## # # ###### ##### ##\n # # # ## ## # # # # #\n # # # # ## # ##### # # # #\n # ###### # # # ##### ######\n # # # # # # # # # #\n # # # # # ###### # # # #\n\n"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
SRSGame.play Tamera if __FILE__ == $0
|
data/lib/srs_game/tia.rb
ADDED
data/lib/srs_game.rb
ADDED
@@ -0,0 +1,429 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
# This file is part of SRS GAME <http://github.com/jacksonwillis/srs_game/>.
|
4
|
+
|
5
|
+
require "zlib"
|
6
|
+
require "base64"
|
7
|
+
require "readline"
|
8
|
+
require "gserver"
|
9
|
+
require "term/ansicolor"
|
10
|
+
|
11
|
+
include Term::ANSIColor
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift File.expand_path("./", __FILE__)
|
14
|
+
require "srs_game/version"
|
15
|
+
require "srs_game/basic"
|
16
|
+
|
17
|
+
class Object
|
18
|
+
def blank?
|
19
|
+
if respond_to?(:empty?)
|
20
|
+
empty?
|
21
|
+
else
|
22
|
+
!self
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def unblank?
|
27
|
+
!self.blank?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Removes "_" from beginning of line and downcases it
|
31
|
+
def command_pp
|
32
|
+
to_s.gsub(/^_/, "").downcase
|
33
|
+
end
|
34
|
+
|
35
|
+
# From ActiveSupport[http://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
|
36
|
+
# Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:
|
37
|
+
# * :words_connector:: - The sign or word used to join the elements in arrays with two or more elements (default: ", ")
|
38
|
+
# * :two_words_connector:: - The sign or word used to join the elements in arrays with two elements (default: " and ")
|
39
|
+
# * :last_word_connector:: - The sign or word used to join the last element in arrays with three or more elements (default: ", and ")
|
40
|
+
# * :bold:: - Bolds the elements being joined. (default: false)
|
41
|
+
# @return [String]
|
42
|
+
def to_sentence(options = {})
|
43
|
+
words_connector = options[:words_connector] || ", "
|
44
|
+
two_words_connector = options[:two_words_connector] || " and "
|
45
|
+
last_word_connector = options[:last_word_connector] || ", and "
|
46
|
+
|
47
|
+
if options[:bold]
|
48
|
+
return to_sentence Array[self] unless respond_to? :map!
|
49
|
+
|
50
|
+
map!(&:to_s)
|
51
|
+
map!(&:bold)
|
52
|
+
end
|
53
|
+
|
54
|
+
case length
|
55
|
+
when 0
|
56
|
+
""
|
57
|
+
when 1
|
58
|
+
self[0].to_s.dup
|
59
|
+
when 2
|
60
|
+
"#{self[0]}#{two_words_connector}#{self[1]}"
|
61
|
+
else
|
62
|
+
"#{self[0...-1].join(words_connector)}#{last_word_connector}#{self[-1]}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class String
|
68
|
+
TRUE_WORDS = %w{t true yes y}
|
69
|
+
FALSE_WORDS = %w{f false no n}
|
70
|
+
|
71
|
+
# Convert to boolean value matching TRUE_WORDS or FALSE_WORDS
|
72
|
+
# @return [Boolean, nil]
|
73
|
+
def to_bool
|
74
|
+
dc = to_s.downcase
|
75
|
+
|
76
|
+
if TRUE_WORDS.include?(dc)
|
77
|
+
true
|
78
|
+
elsif FALSE_WORDS.include?(dc)
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Can the string be converted to a boolean value?
|
84
|
+
# @return [Boolean]
|
85
|
+
def boolean?
|
86
|
+
!to_bool.nil?
|
87
|
+
end
|
88
|
+
|
89
|
+
# Does the string represent a numeral in Ruby?
|
90
|
+
# @return [Boolean]
|
91
|
+
def numeric?
|
92
|
+
!!Float(self) rescue false
|
93
|
+
end
|
94
|
+
|
95
|
+
# Turn the string in to an array of arguments.
|
96
|
+
# It tries to convert words into booleans and floats. Example:
|
97
|
+
# "3.14 yes gaga false".args #=> [3.14, true, "gaga", false]
|
98
|
+
# @return [Array]
|
99
|
+
def args
|
100
|
+
strip.words.map do |arg|
|
101
|
+
next if arg.empty?
|
102
|
+
|
103
|
+
if arg.boolean?
|
104
|
+
arg.to_bool
|
105
|
+
elsif arg.numeric?
|
106
|
+
Float(arg)
|
107
|
+
else
|
108
|
+
arg
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Scans the string for groups of non-whitespace characters.
|
114
|
+
# @return [Array]
|
115
|
+
def words
|
116
|
+
scan(/\S+/)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class IO
|
121
|
+
def rainbow_say(str, greeting_speed = 50)
|
122
|
+
colors = [:red, :yellow, :green, :cyan, :blue, :magenta].cycle
|
123
|
+
|
124
|
+
str.each_line do |line|
|
125
|
+
print (line =~ /\S/ ? line.__send__(colors.next) : line)
|
126
|
+
sleep 1.0 / greeting_speed
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
module SRSGame
|
132
|
+
class DirectionError < ::TypeError; end
|
133
|
+
class DONE_WITH_SRS_GAME < ::SystemExit; end
|
134
|
+
|
135
|
+
# From Dwemthy's Array by _why the lucky stiff
|
136
|
+
# http://mislav.uniqpath.com/poignant-guide/dwemthy/
|
137
|
+
class Traitable
|
138
|
+
# Get a metaclass for this class
|
139
|
+
def self.metaclass; class << self; self; end; end
|
140
|
+
|
141
|
+
# Advanced metaprogramming code for nice, clean traits
|
142
|
+
def self.traits( *arr )
|
143
|
+
return @traits if arr.empty?
|
144
|
+
|
145
|
+
# 1. Set up accessors for each variable
|
146
|
+
attr_accessor *arr
|
147
|
+
|
148
|
+
# 2. Add a new class method to for each trait.
|
149
|
+
arr.each do |a|
|
150
|
+
metaclass.instance_eval do
|
151
|
+
define_method( a ) do |val|
|
152
|
+
@traits ||= {}
|
153
|
+
@traits[a] = val
|
154
|
+
end # define_method
|
155
|
+
end # instance_eval
|
156
|
+
end # each
|
157
|
+
|
158
|
+
# 3. For each traitable object, the `initialize' method
|
159
|
+
# should use the default number for each trait.
|
160
|
+
class_eval do
|
161
|
+
define_method( :initialize ) do
|
162
|
+
self.class.traits.each do |k,v|
|
163
|
+
instance_variable_set("@#{k}", v)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class Item < Traitable
|
172
|
+
traits :interactable_as, # TODO: rename this trait
|
173
|
+
:name,
|
174
|
+
:description,
|
175
|
+
:takeable
|
176
|
+
|
177
|
+
# Defaults
|
178
|
+
interactable_as :item
|
179
|
+
name "an Item"
|
180
|
+
description ""
|
181
|
+
takeable false
|
182
|
+
|
183
|
+
def to_s
|
184
|
+
name
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class Location; end
|
189
|
+
# SRSGame::L is a shortcut for SRSGame::Location
|
190
|
+
L = Location
|
191
|
+
|
192
|
+
class Location
|
193
|
+
def self.direction_relationships
|
194
|
+
[["north", "south"], ["east", "west"], ["up", "down"], ["in", "out"]]
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.mirrored_directions
|
198
|
+
direction_relationships + direction_relationships.map { |a| a.reverse }
|
199
|
+
end
|
200
|
+
|
201
|
+
# All directions available
|
202
|
+
def self.directions
|
203
|
+
direction_relationships.flatten
|
204
|
+
end
|
205
|
+
|
206
|
+
attr_reader(*L.directions)
|
207
|
+
attr_accessor :name, :description, :items
|
208
|
+
|
209
|
+
def initialize(params = {}, &block)
|
210
|
+
@name = params[:name] || "a room"
|
211
|
+
@description = params[:description].to_s
|
212
|
+
@items = params[:items].to_a
|
213
|
+
|
214
|
+
block.call(self) if block.respond_to? :call
|
215
|
+
end
|
216
|
+
|
217
|
+
def item_grep(str)
|
218
|
+
@items.select { |item| str == item.interactable_as.to_s.downcase }
|
219
|
+
end
|
220
|
+
|
221
|
+
def items_here
|
222
|
+
"Items here are #{@items.map(&:to_s).to_sentence(:bold => true)}."
|
223
|
+
end
|
224
|
+
|
225
|
+
# We have to create our own setter methods to do the mirrored direction relationships
|
226
|
+
L.mirrored_directions.each do |dir|
|
227
|
+
direction, opposite = *dir
|
228
|
+
define_method(direction + "=") do |loc|
|
229
|
+
# set @direction to loc
|
230
|
+
instance_variable_set "@" + direction, loc
|
231
|
+
# _direction is @direction
|
232
|
+
_direction = instance_variable_get("@" + direction)
|
233
|
+
_direction.__send__(opposite + "=", self) unless _direction.__send__(opposite)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Available exits
|
238
|
+
# @return [Array]
|
239
|
+
def exits
|
240
|
+
L.directions.select { |dir| __send__(dir) }
|
241
|
+
end
|
242
|
+
|
243
|
+
# Information displayed when a room is entered.
|
244
|
+
def info
|
245
|
+
o = "You find yourself #{@name.bold}."
|
246
|
+
o << " #{@description}" if @description.unblank?
|
247
|
+
o << "\n" << items_here if @items.unblank?
|
248
|
+
o << "\nExits are #{exits.to_sentence(:bold => true)}." if exits.unblank?
|
249
|
+
o
|
250
|
+
end
|
251
|
+
|
252
|
+
def go(direction)
|
253
|
+
if exits.include?(direction.to_s)
|
254
|
+
__send__(direction)
|
255
|
+
else
|
256
|
+
raise DirectionError, "Can't go #{direction} from #{@name}."
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def use_item(s, &block)
|
261
|
+
if (found = item_grep(s)).length == 1
|
262
|
+
block.call found[0]
|
263
|
+
else
|
264
|
+
items_here
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def to_s
|
269
|
+
"#<SRSGame::Location #{@name.inspect} @items=#{@items.inspect} exits=#{exits.inspect}>"
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# Class methods beginning with `+_+' are available as commands during the game
|
274
|
+
class Commands
|
275
|
+
# Called when a non-existing command is entered during the game
|
276
|
+
def method_missing(m, *a)
|
277
|
+
"#{self.class}##{m.downcase}: not found".red
|
278
|
+
end
|
279
|
+
|
280
|
+
# Methods that start with `+_+' and don't end with `+_+'
|
281
|
+
def callable_methods
|
282
|
+
methods.grep(/^_\w+[^_]$/)
|
283
|
+
end
|
284
|
+
|
285
|
+
def matching_methods(match)
|
286
|
+
callable_methods.grep(/^_#{match}/)
|
287
|
+
end
|
288
|
+
|
289
|
+
# Parse input and +__send__+ it
|
290
|
+
def parse(input, game)
|
291
|
+
method = input.words.first.command_pp
|
292
|
+
|
293
|
+
arguments = input.words[1..-1]
|
294
|
+
|
295
|
+
output = __send__("_#{method}", arguments.join(" "), game)
|
296
|
+
end
|
297
|
+
|
298
|
+
#################################
|
299
|
+
# Methods available as commands #
|
300
|
+
#################################
|
301
|
+
|
302
|
+
# Define all directions as commands
|
303
|
+
L.directions.each do |direction|
|
304
|
+
define_method("_#{direction}") do |a, g|
|
305
|
+
begin
|
306
|
+
g.go!(direction)
|
307
|
+
rescue DirectionError => e
|
308
|
+
e
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# Quit the game
|
314
|
+
def _exit(a, g)
|
315
|
+
raise DONE_WITH_SRS_GAME
|
316
|
+
end
|
317
|
+
|
318
|
+
def _look(a, g)
|
319
|
+
if a.args.blank?
|
320
|
+
g.room.info
|
321
|
+
else # We are looking for an item
|
322
|
+
g.room.use_item(a) { |item| "You see #{item.name.bold}." }
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# Display help text
|
327
|
+
def _help(a, g)
|
328
|
+
"All available commands:\n#{callable_methods.map(&:command_pp).to_sentence(:bold => true)}"
|
329
|
+
end
|
330
|
+
|
331
|
+
# Alias commands
|
332
|
+
alias :_quit :_exit
|
333
|
+
alias :_? :_help
|
334
|
+
end
|
335
|
+
|
336
|
+
class Game
|
337
|
+
attr_accessor :room, :command
|
338
|
+
|
339
|
+
def initialize(mod = SRSGame::Basic, options = {})
|
340
|
+
raise ArgumentError, "Can't use #{mod} for SRSGame module" unless mod.is_a? Module
|
341
|
+
extend mod
|
342
|
+
|
343
|
+
@options = options
|
344
|
+
@room = main_room
|
345
|
+
#@travel_path = [@room]
|
346
|
+
@command = mod.const_get(:Commands).new
|
347
|
+
end
|
348
|
+
|
349
|
+
def go!(*directions)
|
350
|
+
if directions.size >= 2
|
351
|
+
directions.each { |dir| go! dir }
|
352
|
+
else
|
353
|
+
new_room = @room.go(directions[0])
|
354
|
+
@room = new_room
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
#def current_room
|
359
|
+
# @travel_path[-1]
|
360
|
+
#end
|
361
|
+
|
362
|
+
#def last_room
|
363
|
+
# @travel_path[-2]
|
364
|
+
#end
|
365
|
+
|
366
|
+
#def same_room?
|
367
|
+
# current_room.eql? last_room
|
368
|
+
#end
|
369
|
+
|
370
|
+
def prompt
|
371
|
+
pr = "$ ".bold.blue
|
372
|
+
@options[:color] ? pr : pr.uncolored
|
373
|
+
end
|
374
|
+
|
375
|
+
def send(input)
|
376
|
+
output = @command.parse(input, self)
|
377
|
+
@options[:color] ? output : output.uncolored
|
378
|
+
end
|
379
|
+
|
380
|
+
def play
|
381
|
+
Readline.completion_append_character = " "
|
382
|
+
|
383
|
+
begin
|
384
|
+
loop do
|
385
|
+
Readline.completion_proc = proc { |match| @command.matching_methods(match).map(&:command_pp) }
|
386
|
+
input = Readline.readline(prompt, true)
|
387
|
+
puts send(input) unless input.blank?
|
388
|
+
end
|
389
|
+
rescue DONE_WITH_SRS_GAME
|
390
|
+
puts "\nHave a nice day!"
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
class SRServer < GServer
|
396
|
+
## ♫ 54-46 was my number ♫ ##
|
397
|
+
DEFAULT_PORT = 54_46
|
398
|
+
MAX_CONNECTIONS = 4
|
399
|
+
|
400
|
+
attr_reader :mod
|
401
|
+
|
402
|
+
def initialize(mod, port=DEFAULT_PORT, host=DEFAULT_HOST, max_connections=MAX_CONNECTIONS)
|
403
|
+
@mod = mod
|
404
|
+
super(port, host, max_connections, $stderr, true, true)
|
405
|
+
end
|
406
|
+
|
407
|
+
def serve(io)
|
408
|
+
game = Game.new(@mod, direct: false)
|
409
|
+
|
410
|
+
begin
|
411
|
+
loop do
|
412
|
+
io.print game.prompt
|
413
|
+
input = io.readline
|
414
|
+
io.puts game.send(input) unless input.blank?
|
415
|
+
end
|
416
|
+
rescue DONE_WITH_SRS_GAME
|
417
|
+
io.puts "\nHave a nice day!"
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
def self.play(*args)
|
422
|
+
server = new(*args)
|
423
|
+
server.audit = true
|
424
|
+
server.start
|
425
|
+
trap(:INT) { server.stop }
|
426
|
+
server.join
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
data/spec/game_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
# This file is part of SRS GAME <http://github.com/jacksonwillis/srs_game/>.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../", __FILE__)
|
6
|
+
require "spec_helper"
|
7
|
+
|
8
|
+
describe SRSGame::Game do
|
9
|
+
it "has a room" do
|
10
|
+
Game.new.room.should be_instance_of Location
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#go!" do
|
14
|
+
it "travels" do
|
15
|
+
game = Game.new
|
16
|
+
|
17
|
+
kitchen = Location.new
|
18
|
+
basement = Location.new { |l| l.up = kitchen }
|
19
|
+
sidewalk = Location.new { |l| l.in = kitchen }
|
20
|
+
bus_stop = Location.new { |l| l.north = sidewalk }
|
21
|
+
|
22
|
+
game.room = basement
|
23
|
+
->{game.go! :up, :out, :south}.should change {game.room}.from(basement).to(bus_stop)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
describe "#_quit" do
|
29
|
+
it "quits" do
|
30
|
+
->{Game.new.send "quit"}.should raise_error DONE_WITH_SRS_GAME
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
# This file is part of SRS GAME <http://github.com/jacksonwillis/srs_game/>.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../", __FILE__)
|
6
|
+
require "spec_helper"
|
7
|
+
|
8
|
+
describe "helpers" do
|
9
|
+
it "determines blankness" do
|
10
|
+
[].blank?.should be_true
|
11
|
+
"".blank?.should be_true
|
12
|
+
false.blank?.should be_true
|
13
|
+
true.blank?.should be_false
|
14
|
+
3.14.blank?.should be_false
|
15
|
+
nil.unblank?.should be_false
|
16
|
+
"foo".unblank?.should be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
it "formats commands" do
|
20
|
+
"_baz".command_pp.should eq "baz"
|
21
|
+
:_Senate.command_pp.should eq "senate"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "converts arrays to sentences" do
|
25
|
+
[1,2,3].to_sentence.should eq "1, 2, and 3"
|
26
|
+
["jack", "jill"].to_sentence.should eq "jack and jill"
|
27
|
+
["Something"].to_sentence.should eq "Something"
|
28
|
+
[].to_sentence.should eq ""
|
29
|
+
["Kinada", "Dwor", "apple", "green"].to_sentence(bold: true).should eq \
|
30
|
+
"\e[1mKinada\e[0m, \e[1mDwor\e[0m, \e[1mapple\e[0m, and \e[1mgreen\e[0m"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "converts string to booleans" do
|
34
|
+
"yes".to_bool.should be_true
|
35
|
+
"no".to_bool.should be_false
|
36
|
+
"f".to_bool.should be_false
|
37
|
+
"t".to_bool.should be_true
|
38
|
+
"true".to_bool.should be_true
|
39
|
+
"foo".to_bool.should be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it "identifies if a string represents a boolean" do
|
43
|
+
"yes".boolean?.should be_true
|
44
|
+
"false".boolean?.should be_true
|
45
|
+
"foo".boolean?.should be_false
|
46
|
+
end
|
47
|
+
|
48
|
+
it "identifies if a string represents a numeric" do
|
49
|
+
"3.14".numeric?.should be_true
|
50
|
+
"42".numeric?.should be_true
|
51
|
+
"4.2e2".numeric?.should be_true
|
52
|
+
"foo".numeric?.should be_false
|
53
|
+
end
|
54
|
+
|
55
|
+
it "parses a string into arguments" do
|
56
|
+
"3.14 yes gaga false".args.should eq [3.14, true, "gaga", false]
|
57
|
+
"tia".args.should eq ["tia"]
|
58
|
+
"".args.should eq []
|
59
|
+
" ".args.should eq []
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
# This file is part of SRS GAME <http://github.com/jacksonwillis/srs_game/>.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../", __FILE__)
|
6
|
+
require "spec_helper"
|
7
|
+
|
8
|
+
describe SRSGame::Location do
|
9
|
+
it "instantiates rooms" do
|
10
|
+
name = "some room"
|
11
|
+
|
12
|
+
location = Location.new do |l|
|
13
|
+
l.name = name
|
14
|
+
end
|
15
|
+
|
16
|
+
location.name.should eq name
|
17
|
+
location.inspect.should be_an_instance_of(String)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#do" do
|
21
|
+
it "only lets you occupy reality" do
|
22
|
+
location = Location.new # no exits
|
23
|
+
->{location.go("west")}.should raise_error SRSGame::DirectionError
|
24
|
+
->{location.go(:down)}.should raise_error SRSGame::DirectionError
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#info" do
|
29
|
+
it "describes a room" do
|
30
|
+
class SomeItem < Item
|
31
|
+
name "some item"
|
32
|
+
end
|
33
|
+
|
34
|
+
location = Location.new do |l|
|
35
|
+
l.name = "in a room"
|
36
|
+
l.description = "A picture hangs on the wall."
|
37
|
+
l.items = [SomeItem.new]
|
38
|
+
l.south = L.new
|
39
|
+
l.out = L.new
|
40
|
+
end
|
41
|
+
|
42
|
+
location.info.uncolored.should eq "You find yourself in a room. A picture hangs on the wall." <<
|
43
|
+
"\nItems here are some item.\nExits are south and out."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "looks for items" do
|
48
|
+
class FooItem < Item
|
49
|
+
interactable_as :foo
|
50
|
+
end
|
51
|
+
|
52
|
+
class BarItem < Item
|
53
|
+
interactable_as :bar
|
54
|
+
end
|
55
|
+
|
56
|
+
foo = FooItem.new
|
57
|
+
bar = BarItem.new
|
58
|
+
|
59
|
+
location = Location.new { |l| l.items = [foo, bar] }
|
60
|
+
|
61
|
+
location.item_grep("foo").should eq [foo]
|
62
|
+
location.item_grep("bar").should eq [bar]
|
63
|
+
location.item_grep("baz").should eq []
|
64
|
+
location.item_grep("").should eq []
|
65
|
+
end
|
66
|
+
|
67
|
+
it "has a prompt" do
|
68
|
+
Game.new.prompt.should eq "$ "
|
69
|
+
end
|
70
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
# This file is part of SRS GAME <http://github.com/jacksonwillis/srs_game/>.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../", __FILE__)
|
6
|
+
require "spec_helper"
|
7
|
+
|
8
|
+
describe SRSGame::SRServer do
|
9
|
+
describe "::new" do
|
10
|
+
it "makes a default server" do
|
11
|
+
server = SRServer.new Basic
|
12
|
+
server.mod.should eq Basic
|
13
|
+
server.host.should eq SRServer::DEFAULT_HOST
|
14
|
+
server.port.should eq SRServer::DEFAULT_PORT
|
15
|
+
server.maxConnections.should eq SRServer::MAX_CONNECTIONS
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/srs_game.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
# This file is part of SRS GAME <http://github.com/jacksonwillis/srs_game/>.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../lib/", __FILE__)
|
6
|
+
require "srs_game/version"
|
7
|
+
|
8
|
+
Gem::Specification.new do |gem|
|
9
|
+
gem.authors = ["Jackson Willis"]
|
10
|
+
gem.email = ["jcwillis.school@gmail.com"]
|
11
|
+
gem.description = "A framework and collection of text-based games"
|
12
|
+
gem.summary = "A framework and collection of text-based games"
|
13
|
+
gem.homepage = "http://github.com/jacksonwillis/srs_game"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($\)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.name = "srs_game"
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
gem.version = SRSGame::VERSION
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: srs_game
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.Alexis
|
5
|
+
prerelease: 2
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jackson Willis
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-31 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A framework and collection of text-based games
|
15
|
+
email:
|
16
|
+
- jcwillis.school@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- .rspec
|
23
|
+
- .travis.yml
|
24
|
+
- .yardopts
|
25
|
+
- Gemfile
|
26
|
+
- LICENSE
|
27
|
+
- README.md
|
28
|
+
- Rakefile.rb
|
29
|
+
- examples/pokemon_clone.rb
|
30
|
+
- lib/srs_game.rb
|
31
|
+
- lib/srs_game/basic.rb
|
32
|
+
- lib/srs_game/tamera.rb
|
33
|
+
- lib/srs_game/tia.rb
|
34
|
+
- lib/srs_game/version.rb
|
35
|
+
- spec/game_spec.rb
|
36
|
+
- spec/helpers_spec.rb
|
37
|
+
- spec/location_spec.rb
|
38
|
+
- spec/spec_helper.rb
|
39
|
+
- spec/srserver_spec.rb
|
40
|
+
- srs_game.gemspec
|
41
|
+
homepage: http://github.com/jacksonwillis/srs_game
|
42
|
+
licenses: []
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>'
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: 1.3.1
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.8.10
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: A framework and collection of text-based games
|
65
|
+
test_files:
|
66
|
+
- spec/game_spec.rb
|
67
|
+
- spec/helpers_spec.rb
|
68
|
+
- spec/location_spec.rb
|
69
|
+
- spec/spec_helper.rb
|
70
|
+
- spec/srserver_spec.rb
|
71
|
+
has_rdoc:
|