srs_game 0.Alexis
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/jacksonwillis/srs_game.png?branch=master)](https://secure.travis-ci.org/jacksonwillis/srs_game)
|
5
|
+
[![Dependency Status](https://gemnasium.com/jacksonwillis/srs_game.png)](https://gemnasium.com/jacksonwillis/srs_game)
|
6
|
+
|
7
|
+
![SRS GAME logo](https://github.com/downloads/jacksonwillis/srs_game/srs_game.png)
|
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:
|