gamefic 0.2.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/gamefic/action.rb +87 -56
- data/lib/gamefic/ansi.rb +55 -0
- data/lib/gamefic/character.rb +130 -76
- data/lib/gamefic/command.rb +19 -0
- data/lib/gamefic/core_ext/array.rb +51 -40
- data/lib/gamefic/core_ext/string.rb +4 -0
- data/lib/gamefic/describable.rb +108 -46
- data/lib/gamefic/direction.rb +46 -0
- data/lib/gamefic/director/delegate.rb +91 -0
- data/lib/gamefic/director/order.rb +10 -0
- data/lib/gamefic/director/parser.rb +119 -0
- data/lib/gamefic/director.rb +16 -197
- data/lib/gamefic/engine/cgi.rb +221 -0
- data/lib/gamefic/engine/tty.rb +237 -0
- data/lib/gamefic/engine.rb +88 -67
- data/lib/gamefic/entity.rb +96 -69
- data/lib/gamefic/grammar/conjugator.rb +20 -0
- data/lib/gamefic/grammar/gender.rb +11 -0
- data/lib/gamefic/grammar/person.rb +10 -0
- data/lib/gamefic/grammar/plural.rb +13 -0
- data/lib/gamefic/grammar/pronouns.rb +60 -0
- data/lib/gamefic/grammar/tense.rb +6 -0
- data/lib/gamefic/grammar/verb_set.rb +43 -0
- data/lib/gamefic/grammar/verbs.rb +25 -0
- data/lib/gamefic/grammar/word_adapter.rb +36 -0
- data/lib/gamefic/grammar.rb +13 -0
- data/lib/gamefic/html.rb +53 -0
- data/lib/gamefic/keywords.rb +51 -33
- data/lib/gamefic/node.rb +65 -58
- data/lib/gamefic/plot/article_mount.rb +22 -0
- data/lib/gamefic/plot/command_mount.rb +88 -0
- data/lib/gamefic/plot/entity_mount.rb +45 -0
- data/lib/gamefic/plot/query_mount.rb +9 -0
- data/lib/gamefic/plot/scene_mount.rb +181 -0
- data/lib/gamefic/plot/you_mount.rb +22 -0
- data/lib/gamefic/plot.rb +296 -247
- data/lib/gamefic/query/ambiguous_children.rb +5 -0
- data/lib/gamefic/query/base.rb +265 -0
- data/lib/gamefic/query/children.rb +10 -0
- data/lib/gamefic/query/expression.rb +47 -0
- data/lib/gamefic/query/family.rb +10 -0
- data/lib/gamefic/query/many_children.rb +7 -0
- data/lib/gamefic/query/matches.rb +11 -0
- data/lib/gamefic/query/parent.rb +10 -0
- data/lib/gamefic/query/plural_children.rb +14 -0
- data/lib/gamefic/query/self.rb +10 -0
- data/lib/gamefic/query/siblings.rb +10 -0
- data/lib/gamefic/query/text.rb +43 -0
- data/lib/gamefic/query.rb +19 -203
- data/lib/gamefic/rule.rb +18 -0
- data/lib/gamefic/scene/active.rb +25 -0
- data/lib/gamefic/scene/concluded.rb +22 -0
- data/lib/gamefic/scene/multiplechoice.rb +74 -0
- data/lib/gamefic/scene/paused.rb +26 -0
- data/lib/gamefic/scene/yesorno.rb +43 -0
- data/lib/gamefic/scene.rb +125 -0
- data/lib/gamefic/script/base.rb +33 -0
- data/lib/gamefic/script/file.rb +14 -0
- data/lib/gamefic/script/text.rb +14 -0
- data/lib/gamefic/script.rb +9 -0
- data/lib/gamefic/serialized.rb +24 -0
- data/lib/gamefic/shell.rb +9 -247
- data/lib/gamefic/snapshots.rb +134 -0
- data/lib/gamefic/source/base.rb +12 -0
- data/lib/gamefic/source/file.rb +23 -0
- data/lib/gamefic/source/text.rb +16 -0
- data/lib/gamefic/source.rb +9 -0
- data/lib/gamefic/stage.rb +75 -0
- data/lib/gamefic/syntax.rb +106 -124
- data/lib/gamefic/tester.rb +20 -0
- data/lib/gamefic/version.rb +3 -0
- data/lib/gamefic.rb +18 -12
- metadata +102 -70
- data/lib/gamefic/base.rb +0 -10
- data/lib/gamefic/before.rb +0 -12
- data/lib/gamefic/import/basics/actions/close.rb +0 -16
- data/lib/gamefic/import/basics/actions/commands.rb +0 -3
- data/lib/gamefic/import/basics/actions/drop-in.rb +0 -17
- data/lib/gamefic/import/basics/actions/drop-on.rb +0 -16
- data/lib/gamefic/import/basics/actions/drop.rb +0 -30
- data/lib/gamefic/import/basics/actions/enter.rb +0 -16
- data/lib/gamefic/import/basics/actions/go.rb +0 -35
- data/lib/gamefic/import/basics/actions/inventory.rb +0 -8
- data/lib/gamefic/import/basics/actions/leave.rb +0 -29
- data/lib/gamefic/import/basics/actions/look-in-at.rb +0 -27
- data/lib/gamefic/import/basics/actions/look-under.rb +0 -3
- data/lib/gamefic/import/basics/actions/look.rb +0 -71
- data/lib/gamefic/import/basics/actions/nil.rb +0 -25
- data/lib/gamefic/import/basics/actions/open.rb +0 -23
- data/lib/gamefic/import/basics/actions/quit.rb +0 -3
- data/lib/gamefic/import/basics/actions/take.rb +0 -107
- data/lib/gamefic/import/basics/entities/container.rb +0 -8
- data/lib/gamefic/import/basics/entities/entity.rb +0 -11
- data/lib/gamefic/import/basics/entities/fixture.rb +0 -5
- data/lib/gamefic/import/basics/entities/item.rb +0 -5
- data/lib/gamefic/import/basics/entities/portal.rb +0 -40
- data/lib/gamefic/import/basics/entities/room.rb +0 -30
- data/lib/gamefic/import/basics/entities/scenery.rb +0 -5
- data/lib/gamefic/import/basics/entities/supporter.rb +0 -6
- data/lib/gamefic/import/basics/entities/thing.rb +0 -16
- data/lib/gamefic/import/basics/queries/reachable.rb +0 -38
- data/lib/gamefic/import/basics/queries/room.rb +0 -8
- data/lib/gamefic/import/basics/queries/visible.rb +0 -32
- data/lib/gamefic/import/basics/rules/has-enough-light.rb +0 -14
- data/lib/gamefic/import/basics.old/actions/container.rb +0 -112
- data/lib/gamefic/import/basics.old/actions/inventory.rb +0 -50
- data/lib/gamefic/import/basics.old/actions/look.rb +0 -53
- data/lib/gamefic/import/basics.old/actions/meta.rb +0 -6
- data/lib/gamefic/import/basics.old/actions/traversal.rb +0 -35
- data/lib/gamefic/import/basics.old/actions.rb +0 -1
- data/lib/gamefic/import/basics.old/entities/container.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/fixture.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/item.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/portal.rb +0 -43
- data/lib/gamefic/import/basics.old/entities/room.rb +0 -27
- data/lib/gamefic/import/basics.old/entities/scenery.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/supporter.rb +0 -3
- data/lib/gamefic/import/basics.old/entities.rb +0 -1
- data/lib/gamefic/import/basics.old/room_modes.rb +0 -48
- data/lib/gamefic/import/basics.rb +0 -6
- data/lib/gamefic/import/room_modes.rb +0 -48
- data/lib/gamefic/import/standard.rb +0 -1
- data/lib/gamefic/meta.rb +0 -12
- data/lib/gamefic/optionset.rb +0 -114
- data/lib/gamefic/requirement.rb +0 -14
- data/lib/gamefic/story.rb +0 -14
- data/lib/gamefic/thing.rb +0 -7
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Gamefic
|
5
|
+
|
6
|
+
class Entity
|
7
|
+
attr_reader :cgi_key
|
8
|
+
@@cgi_key_index = 0
|
9
|
+
alias_method :orig_post_initialize, :post_initialize
|
10
|
+
def post_initialize
|
11
|
+
orig_post_initialize
|
12
|
+
@cgi_key = "entity_#{@@cgi_key_index}"
|
13
|
+
@@cgi_key_index += 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Cgi
|
18
|
+
|
19
|
+
class Key
|
20
|
+
def initialize(key_value)
|
21
|
+
@value = key_value
|
22
|
+
end
|
23
|
+
def value
|
24
|
+
@value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Engine < Gamefic::Engine
|
29
|
+
attr_reader :user
|
30
|
+
def initialize(plot, args = {})
|
31
|
+
super(plot)
|
32
|
+
@session_file = args[:session_file] || 'save.dat'
|
33
|
+
@message_format = args[:message_format] || :html
|
34
|
+
@response_format = args[:response_format] || :json
|
35
|
+
@new_game = args[:new_game] || false
|
36
|
+
@entity_keys = Hash.new
|
37
|
+
@introducing = true
|
38
|
+
end
|
39
|
+
def post_initialize
|
40
|
+
@user = Cgi::User.new @plot
|
41
|
+
end
|
42
|
+
def begin_session
|
43
|
+
# Initialize keys for all entities
|
44
|
+
@plot.entities.each { |e|
|
45
|
+
@entity_keys[e.cgi_key] = e
|
46
|
+
}
|
47
|
+
@entity_keys['yourself'] = @user.character
|
48
|
+
if !@new_game and @session_file != nil
|
49
|
+
if File.exist?(@session_file)
|
50
|
+
load @session_file
|
51
|
+
@introducing = false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
def run
|
56
|
+
if @introducing == true
|
57
|
+
@plot.introduce @user.character
|
58
|
+
else
|
59
|
+
@plot.instance_variable_get(:@players).push @user.character
|
60
|
+
tick
|
61
|
+
end
|
62
|
+
response = Hash.new
|
63
|
+
response[:output] = @user.stream.flush
|
64
|
+
response[:prompt] = @user.character.state.prompt
|
65
|
+
response[:state] = @user.character.state.class.to_s.split('::').last
|
66
|
+
return JSON.generate(response)
|
67
|
+
end
|
68
|
+
def end_session
|
69
|
+
save @session_file
|
70
|
+
end
|
71
|
+
private
|
72
|
+
def load(filename)
|
73
|
+
x = File.open(filename, "r")
|
74
|
+
ser = x.read
|
75
|
+
x.close
|
76
|
+
data = Marshal.restore(ser)
|
77
|
+
data.each { |k, h|
|
78
|
+
if k == 'yourself'
|
79
|
+
entity = @entity_keys['yourself']
|
80
|
+
else
|
81
|
+
entity = @entity_keys[k]
|
82
|
+
end
|
83
|
+
h.each { |s, v|
|
84
|
+
if s == :session
|
85
|
+
entity.instance_variable_set(:@session, v)
|
86
|
+
elsif s == :state
|
87
|
+
entity.state = v
|
88
|
+
elsif s == :options
|
89
|
+
v.each { |opt|
|
90
|
+
entity.is opt
|
91
|
+
}
|
92
|
+
else
|
93
|
+
writer = "#{s.to_s[1..-1]}="
|
94
|
+
writer.untaint
|
95
|
+
if entity.respond_to?(writer)
|
96
|
+
if v.kind_of?(Key)
|
97
|
+
entity.send(writer, @entity_keys[v.value])
|
98
|
+
elsif v.kind_of?(CharacterState::Base)
|
99
|
+
v.instance_variable_set(:@character, entity)
|
100
|
+
entity.instance_variable_set(s, v)
|
101
|
+
elsif v.kind_of?(Array)
|
102
|
+
entity.send(writer, decode_array(v))
|
103
|
+
else
|
104
|
+
entity.send(writer, v)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
}
|
109
|
+
}
|
110
|
+
@restored = true
|
111
|
+
end
|
112
|
+
def save(filename)
|
113
|
+
data = Hash.new
|
114
|
+
@plot.entities.each { |e|
|
115
|
+
data[e.cgi_key] = entity_hash(e)
|
116
|
+
}
|
117
|
+
f = File.new(filename, "w")
|
118
|
+
f.write Marshal.dump data
|
119
|
+
f.close
|
120
|
+
end
|
121
|
+
def entity_hash(e)
|
122
|
+
hash = Hash.new
|
123
|
+
e.instance_variables.each { |v|
|
124
|
+
next if v == :@state or v == :@option_array or v == :@update_procs
|
125
|
+
writer = "#{v.to_s[1..-1]}="
|
126
|
+
if e.respond_to?(writer)
|
127
|
+
value = e.instance_variable_get(v)
|
128
|
+
if value.kind_of?(String) or value.kind_of?(Numeric) or value.kind_of?(TrueClass) or value.kind_of?(FalseClass) or value.kind_of?(Entity) or value.kind_of?(Character) or value == nil or value.kind_of?(Array)
|
129
|
+
if value.kind_of?(Entity)
|
130
|
+
if value == @user.character
|
131
|
+
hash[v] = Key.new('yourself')
|
132
|
+
else
|
133
|
+
hash[v] = Key.new(value.cgi_key)
|
134
|
+
end
|
135
|
+
elsif value.kind_of?(CharacterState::Base)
|
136
|
+
value.instance_variable_set(:@character, nil)
|
137
|
+
hash[v] = value
|
138
|
+
elsif value.kind_of?(Array)
|
139
|
+
hash[v] = encode_array(value)
|
140
|
+
else
|
141
|
+
hash[v] = value
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
}
|
146
|
+
if e.respond_to?("state")
|
147
|
+
hash[:state] = e.state_name
|
148
|
+
end
|
149
|
+
if e.respond_to?("option_array")
|
150
|
+
hash[:options] = e.option_array
|
151
|
+
end
|
152
|
+
hash[:session] = Hash.new
|
153
|
+
e.session.each { |k, v|
|
154
|
+
if v.kind_of?(Entity)
|
155
|
+
hash[:session][k] = Key.new(v.cgi_key)
|
156
|
+
elsif v.kind_of?(Array)
|
157
|
+
hash[:session][k] = encode_array(v)
|
158
|
+
else
|
159
|
+
hash[:session][k] = v
|
160
|
+
end
|
161
|
+
}
|
162
|
+
hash
|
163
|
+
end
|
164
|
+
def encode_array(array)
|
165
|
+
result = Array.new
|
166
|
+
array.each { |item|
|
167
|
+
if item.kind_of?(Entity)
|
168
|
+
result.push Key.new(item.cgi_key)
|
169
|
+
else
|
170
|
+
result.push item
|
171
|
+
end
|
172
|
+
}
|
173
|
+
result
|
174
|
+
end
|
175
|
+
def decode_array(array)
|
176
|
+
result = Array.new
|
177
|
+
array.each { |item|
|
178
|
+
if item.kind_of?(Key)
|
179
|
+
result.push @entity_keys[item.value]
|
180
|
+
else
|
181
|
+
result.push item
|
182
|
+
end
|
183
|
+
}
|
184
|
+
result
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class User < Gamefic::User
|
189
|
+
def post_initialize
|
190
|
+
@stream = Cgi::UserStream.new
|
191
|
+
@state = UserState.new self
|
192
|
+
#@character = @plot.make Character, :name => 'pWlayer'
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class UserStream < Gamefic::UserStream
|
197
|
+
def initialize
|
198
|
+
super
|
199
|
+
@output = ''
|
200
|
+
end
|
201
|
+
def flush
|
202
|
+
buffer = "#{@output}"
|
203
|
+
@output.clear
|
204
|
+
buffer
|
205
|
+
end
|
206
|
+
def send(data)
|
207
|
+
#if data.start_with?(Ansi::NEL)
|
208
|
+
# data = "<p>#{data[Ansi::NEL.length..-1].rstrip}</p>"
|
209
|
+
#end
|
210
|
+
@output = "#{@output}#{data}"
|
211
|
+
end
|
212
|
+
def select(prompt)
|
213
|
+
# TODO: non-blocking read
|
214
|
+
line = STDIN.gets
|
215
|
+
@queue.push line
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'gamefic/engine'
|
2
|
+
require 'rexml/document'
|
3
|
+
require 'gamefic/ansi'
|
4
|
+
require 'gamefic/html'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'io/console'
|
8
|
+
rescue LoadError
|
9
|
+
puts "This version of Ruby does not support io/console. Text may not wrap correctly."
|
10
|
+
if RUBY_VERSION.split('.')[0].to_i < 2
|
11
|
+
puts "It is recommended that you upgrade to Ruby 2.0.0 or higher."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Gamefic
|
16
|
+
|
17
|
+
module Tty
|
18
|
+
class Engine < Gamefic::Engine
|
19
|
+
def post_initialize
|
20
|
+
@user = Tty::User.new @plot
|
21
|
+
end
|
22
|
+
end
|
23
|
+
class User < Gamefic::User
|
24
|
+
def post_initialize
|
25
|
+
@stream = Tty::UserStream.new
|
26
|
+
@state = UserState.new self
|
27
|
+
end
|
28
|
+
def ansi=(val)
|
29
|
+
@stream.ansi = val
|
30
|
+
end
|
31
|
+
def ansi
|
32
|
+
@stream.ansi
|
33
|
+
end
|
34
|
+
def save filename, json
|
35
|
+
if json.nil?
|
36
|
+
@character.tell "Nothing to save."
|
37
|
+
end
|
38
|
+
if filename.nil?
|
39
|
+
stream.select "Enter the filename to save:"
|
40
|
+
filename = stream.queue.pop
|
41
|
+
end
|
42
|
+
if filename != ''
|
43
|
+
File.open(filename, 'w') do |f|
|
44
|
+
f.write json
|
45
|
+
end
|
46
|
+
@character.tell "Game saved."
|
47
|
+
end
|
48
|
+
end
|
49
|
+
def restore filename
|
50
|
+
if filename.nil?
|
51
|
+
stream.select "Enter the filename to restore:"
|
52
|
+
filename = stream.queue.pop
|
53
|
+
end
|
54
|
+
if filename != ''
|
55
|
+
if File.exists?(filename)
|
56
|
+
return File.read(filename)
|
57
|
+
else
|
58
|
+
@character.tell "File \"#{filename}\" not found."
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
class UserStream < Gamefic::UserStream
|
64
|
+
include Ansi
|
65
|
+
include Ansi::Code
|
66
|
+
def initialize
|
67
|
+
super
|
68
|
+
end
|
69
|
+
def size
|
70
|
+
begin
|
71
|
+
return STDOUT.winsize.reverse
|
72
|
+
rescue
|
73
|
+
return [nil,nil]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
def flush
|
77
|
+
data = @buffer.clone
|
78
|
+
@buffer.clear
|
79
|
+
return if data.strip == ''
|
80
|
+
output = ''
|
81
|
+
begin
|
82
|
+
doc = Html.parse("<body>#{data}</body>")
|
83
|
+
format_recursively doc
|
84
|
+
texts = REXML::XPath.match(doc, './/text()')
|
85
|
+
output = texts.join('').gsub(/'/, "'").gsub(/"/, '"').gsub(/</, '<').gsub(/>/, '>')
|
86
|
+
output += Ansi.graphics_mode(Attribute::NORMAL)
|
87
|
+
output = Html::decode(output)
|
88
|
+
rescue REXML::ParseException => e
|
89
|
+
puts e.inspect
|
90
|
+
output = Html.encode(data) + "\n\n"
|
91
|
+
end
|
92
|
+
output.gsub!(/(\n\n)+/, "\n\n")
|
93
|
+
width = size[0]
|
94
|
+
if width.nil?
|
95
|
+
output
|
96
|
+
else
|
97
|
+
"#{terminalize(output, width - 1)}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
def select(prompt)
|
101
|
+
super
|
102
|
+
print "\n"
|
103
|
+
end
|
104
|
+
def has_code?(fmt, code)
|
105
|
+
if fmt.kind_of?(Array)
|
106
|
+
return fmt.flatten.include?(code)
|
107
|
+
end
|
108
|
+
return fmt == code
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def format_recursively(top, stack = nil)
|
114
|
+
ol_index = 1
|
115
|
+
stack ||= [Attribute::NORMAL]
|
116
|
+
top.elements.each { |element|
|
117
|
+
formats = [Attribute::NORMAL]
|
118
|
+
classes = element.attribute('class').to_s.split(' ')
|
119
|
+
if classes.include?('hint')
|
120
|
+
formats.push Foreground::YELLOW
|
121
|
+
end
|
122
|
+
case element.name
|
123
|
+
when 'strong', 'b'
|
124
|
+
formats.push Attribute::BOLD
|
125
|
+
when 'em', 'i', 'u'
|
126
|
+
formats.push Attribute::UNDERSCORE
|
127
|
+
when 'a'
|
128
|
+
if element.attributes['rel'].to_s == 'gamefic'
|
129
|
+
element.attributes['href'] = element.attributes['href'][5..-1]
|
130
|
+
formats.push [Attribute::UNDERSCORE, Extra::COMMAND]
|
131
|
+
else
|
132
|
+
formats.push [Attribute::UNDERSCORE, Extra::HREF]
|
133
|
+
end
|
134
|
+
when 'li'
|
135
|
+
if top.name == 'ol'
|
136
|
+
element.text = "#{ol_index}. #{element.text}"
|
137
|
+
ol_index += 1
|
138
|
+
else
|
139
|
+
element.text = "* #{element.text}"
|
140
|
+
end
|
141
|
+
formats.push [Extra::LINE]
|
142
|
+
when 'img'
|
143
|
+
formats.push [Extra::IGNORED]
|
144
|
+
when 'p', 'ol', 'ul'
|
145
|
+
formats.push Extra::BLOCK
|
146
|
+
when 'nav'
|
147
|
+
formats.push Extra::BLOCK
|
148
|
+
when 'h1', 'h2', 'h3', 'h4', 'h5'
|
149
|
+
formats.push [Attribute::BOLD, Extra::BLOCK, Extra::UPPERCASE]
|
150
|
+
when 'kbd'
|
151
|
+
formats.push [Foreground::GREEN]
|
152
|
+
end
|
153
|
+
if has_code?(stack, Extra::IGNORED)
|
154
|
+
element.parent.delete_element(element)
|
155
|
+
end
|
156
|
+
stack.push formats
|
157
|
+
if has_code?(stack, Extra::UPPERCASE)
|
158
|
+
element.texts.each { |text|
|
159
|
+
text.value.upcase!
|
160
|
+
}
|
161
|
+
end
|
162
|
+
element.texts.each { |text|
|
163
|
+
text.value = "#{Ansi.graphics_mode(*stack)}#{text.value}"
|
164
|
+
}
|
165
|
+
if has_code?(stack.last, Extra::IMAGE)
|
166
|
+
element.text = "#{element.attribute('alt') ? element.attribute('alt') : '[Image]'}"
|
167
|
+
end
|
168
|
+
format_recursively element, stack
|
169
|
+
if has_code?(stack.last, Extra::COMMAND)
|
170
|
+
if element.attribute('data-command').to_s != ''
|
171
|
+
tmp = stack.pop
|
172
|
+
element.add_text "#{Ansi.graphics_mode(*stack)}"
|
173
|
+
element.add_text " [#{element.attribute('data-command')}]"
|
174
|
+
stack.push tmp
|
175
|
+
element.add_text "#{Ansi.graphics_mode(*stack)}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
if has_code?(stack.last, Extra::BLOCK)
|
179
|
+
element.add_text("\n\n")
|
180
|
+
elsif has_code?(stack.last, Extra::LINE)
|
181
|
+
if !has_code?(stack[-2], Extra::BLOCK) || element != top.elements.to_a.last
|
182
|
+
element.add_text("\n")
|
183
|
+
end
|
184
|
+
end
|
185
|
+
if has_code?(stack.last, Extra::HREF)
|
186
|
+
if element.attribute('href').to_s != "#"
|
187
|
+
tmp = stack.pop
|
188
|
+
element.add_text "#{Ansi.graphics_mode(*stack)}"
|
189
|
+
element.add_text(" [#{element.attribute('href')}]")
|
190
|
+
stack.push tmp
|
191
|
+
element.add_text "#{Ansi.graphics_mode(*stack)}"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
if has_code?(stack.last, Extra::IMAGE)
|
195
|
+
element.add_text(" [#{element.attribute('src')}]")
|
196
|
+
if !has_code?(stack, Extra::BLOCK)
|
197
|
+
element.add_text("\n\n")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
stack.pop
|
201
|
+
}
|
202
|
+
end
|
203
|
+
def terminalize string, max_length
|
204
|
+
i = 0
|
205
|
+
output = ''
|
206
|
+
line_length = 0
|
207
|
+
while i < string.length
|
208
|
+
line_length += 1
|
209
|
+
char = string[i,1]
|
210
|
+
if char == "\e"
|
211
|
+
# Right now, graphics modes are the only supported ANSI sequences.
|
212
|
+
end_of_seq = string.index("m", i)
|
213
|
+
output += string[i..end_of_seq]
|
214
|
+
i = end_of_seq + 1
|
215
|
+
elsif char == " "
|
216
|
+
next_space = string.index(/[\s]/, i + 1)
|
217
|
+
if !next_space.nil? and line_length + (next_space - i) > max_length
|
218
|
+
output += "\n"
|
219
|
+
line_length = 0
|
220
|
+
else
|
221
|
+
output += char
|
222
|
+
end
|
223
|
+
i += 1
|
224
|
+
else
|
225
|
+
if char == "\n"
|
226
|
+
line_length = 0
|
227
|
+
end
|
228
|
+
output += char
|
229
|
+
i += 1
|
230
|
+
end
|
231
|
+
end
|
232
|
+
output #output.strip
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
data/lib/gamefic/engine.rb
CHANGED
@@ -1,86 +1,107 @@
|
|
1
1
|
module Gamefic
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
class Engine
|
4
|
+
def initialize(plot)
|
5
|
+
@plot = plot
|
6
6
|
post_initialize
|
7
|
-
|
7
|
+
end
|
8
8
|
def post_initialize
|
9
9
|
@user = User.new @plot
|
10
10
|
end
|
11
|
-
|
11
|
+
def run
|
12
12
|
@plot.introduce @user.character
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
13
|
+
print @user.state.output
|
14
|
+
while @user.character.scene.state != "Concluded"
|
15
|
+
turn
|
16
|
+
end
|
17
|
+
print @user.state.output
|
18
|
+
end
|
19
|
+
def turn
|
20
|
+
@plot.ready
|
21
|
+
print @user.state.output
|
22
|
+
# HACK Exception for test scenes
|
23
|
+
if @user.character.scene.key != :test
|
24
|
+
if @user.character.scene.state != "Concluded"
|
25
|
+
@user.stream.select @user.character.scene.data.prompt
|
26
|
+
@user.state.input
|
27
|
+
end
|
28
|
+
end
|
29
|
+
@plot.update
|
30
|
+
print @user.state.output
|
31
|
+
end
|
32
|
+
end
|
26
33
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@character = Character.new @plot, :name => 'yourself', :synonyms => 'self myself you me'
|
34
|
+
class User
|
35
|
+
attr_reader :state, :character, :story
|
36
|
+
def initialize(plot)
|
37
|
+
@plot = plot
|
38
|
+
@character = Character.new @plot, :name => 'yourself', :synonyms => 'self myself you me', :proper_named => true
|
32
39
|
@character.connect self
|
33
40
|
post_initialize
|
34
|
-
|
41
|
+
end
|
35
42
|
def post_initialize
|
36
|
-
|
37
|
-
|
43
|
+
@stream = UserStream.new
|
44
|
+
@state = UserState.new self
|
45
|
+
end
|
46
|
+
def stream
|
47
|
+
@stream ||= UserStream.new
|
38
48
|
end
|
39
|
-
def stream
|
40
|
-
@stream ||= UserStream.new
|
41
|
-
end
|
42
49
|
def state
|
43
50
|
@state ||= UserState.new(self)
|
44
51
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
52
|
+
def state=(state_class)
|
53
|
+
@state = state_class.new self
|
54
|
+
end
|
55
|
+
def refresh
|
56
|
+
# Nothing to do
|
57
|
+
end
|
58
|
+
def quit
|
59
|
+
#exit
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class UserStream
|
64
|
+
def initialize
|
65
|
+
@queue = Array.new
|
66
|
+
@buffer = ''
|
67
|
+
end
|
68
|
+
def flush
|
69
|
+
tmp = @buffer.clone
|
70
|
+
#@buffer.clear
|
71
|
+
@buffer = ''
|
72
|
+
tmp
|
73
|
+
end
|
74
|
+
def send(data)
|
75
|
+
# Quick and dirty HTML sanitization
|
76
|
+
#data.gsub!(/<[a-z]+[^>]*>/i, "")
|
77
|
+
#data.gsub!(/<\/[^>]*>/, "")
|
78
|
+
@buffer += data
|
79
|
+
end
|
80
|
+
def select(prompt)
|
81
|
+
print "#{prompt} "
|
82
|
+
STDOUT.flush
|
83
|
+
line = STDIN.gets
|
66
84
|
@queue.push line.strip
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
85
|
+
end
|
86
|
+
def recv
|
87
|
+
@queue.shift
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class UserState
|
92
|
+
attr_reader :user
|
93
|
+
def initialize(user)
|
94
|
+
@user = user
|
95
|
+
end
|
96
|
+
def input
|
97
|
+
line = @user.stream.recv
|
98
|
+
if line != nil
|
81
99
|
@user.character.queue.push line
|
82
|
-
|
83
|
-
|
84
|
-
|
100
|
+
end
|
101
|
+
end
|
102
|
+
def output
|
103
|
+
@user.stream.flush
|
104
|
+
end
|
105
|
+
end
|
85
106
|
|
86
107
|
end
|