gamefic 1.3.2 → 1.4.0
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.
- checksums.yaml +4 -4
- data/lib/gamefic.rb +1 -0
- data/lib/gamefic/character.rb +19 -13
- data/lib/gamefic/engine.rb +2 -103
- data/lib/gamefic/engine/base.rb +55 -0
- data/lib/gamefic/engine/tty.rb +20 -254
- data/lib/gamefic/html.rb +20 -19
- data/lib/gamefic/html_to_ansi.rb +185 -0
- data/lib/gamefic/plot.rb +21 -29
- data/lib/gamefic/plot/scene_mount.rb +103 -36
- data/lib/gamefic/scene.rb +3 -2
- data/lib/gamefic/scene/active.rb +1 -6
- data/lib/gamefic/scene/base.rb +34 -8
- data/lib/gamefic/scene/conclusion.rb +0 -3
- data/lib/gamefic/scene/custom.rb +28 -7
- data/lib/gamefic/scene/multiple_choice.rb +31 -38
- data/lib/gamefic/scene/multiple_scene.rb +15 -0
- data/lib/gamefic/scene/pause.rb +4 -15
- data/lib/gamefic/scene/yes_or_no.rb +8 -13
- data/lib/gamefic/scene_data.rb +9 -0
- data/lib/gamefic/scene_data/base.rb +12 -0
- data/lib/gamefic/scene_data/multiple_choice.rb +19 -0
- data/lib/gamefic/scene_data/multiple_scene.rb +25 -0
- data/lib/gamefic/scene_data/yes_or_no.rb +18 -0
- data/lib/gamefic/shell.rb +77 -78
- data/lib/gamefic/tester.rb +1 -1
- data/lib/gamefic/tty.rb +8 -0
- data/lib/gamefic/user.rb +8 -0
- data/lib/gamefic/user/base.rb +21 -0
- data/lib/gamefic/user/buffer.rb +25 -0
- data/lib/gamefic/user/tty.rb +55 -0
- data/lib/gamefic/version.rb +1 -1
- metadata +15 -6
- data/lib/gamefic/engine/cgi.rb +0 -221
- data/lib/gamefic/scene/multiple_choice/input.rb +0 -11
- data/lib/gamefic/scene/passive.rb +0 -17
- data/lib/gamefic/scene/question.rb +0 -21
data/lib/gamefic/tester.rb
CHANGED
data/lib/gamefic/tty.rb
ADDED
data/lib/gamefic/user.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Gamefic
|
2
|
+
|
3
|
+
# The base user provides methods for handling messages received from plots.
|
4
|
+
#
|
5
|
+
class User::Base
|
6
|
+
def send message
|
7
|
+
buffer.send message
|
8
|
+
end
|
9
|
+
|
10
|
+
def flush
|
11
|
+
buffer.flush
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def buffer
|
17
|
+
@buffer ||= User::Buffer.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Gamefic
|
2
|
+
|
3
|
+
# A simple buffer class for collecting and returning data received from
|
4
|
+
# plots.
|
5
|
+
class User::Buffer
|
6
|
+
def initialize
|
7
|
+
@data = ''
|
8
|
+
end
|
9
|
+
|
10
|
+
# Append a message to the buffer.
|
11
|
+
def send message
|
12
|
+
@data += message
|
13
|
+
end
|
14
|
+
|
15
|
+
# Get the current data and clear the buffer.
|
16
|
+
#
|
17
|
+
# @return [String]
|
18
|
+
def flush
|
19
|
+
tmp = @data
|
20
|
+
@data = ''
|
21
|
+
tmp
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'gamefic/engine'
|
2
|
+
require 'json'
|
3
|
+
require 'gamefic/html_to_ansi'
|
4
|
+
|
5
|
+
module Gamefic
|
6
|
+
|
7
|
+
# Extend User::Base to convert HTML into ANSI text.
|
8
|
+
#
|
9
|
+
class User::Tty < User::Base
|
10
|
+
include HtmlToAnsi
|
11
|
+
|
12
|
+
def save filename, snapshot
|
13
|
+
data = snapshot.merge(:metadata => @character.plot.metadata)
|
14
|
+
json = JSON.generate data
|
15
|
+
if json.nil?
|
16
|
+
@character.tell "Nothing to save."
|
17
|
+
end
|
18
|
+
if filename.nil?
|
19
|
+
stream.select "Enter the filename to save:"
|
20
|
+
filename = stream.queue.pop
|
21
|
+
end
|
22
|
+
if filename != ''
|
23
|
+
File.open(filename, 'w') do |f|
|
24
|
+
f.write json
|
25
|
+
end
|
26
|
+
@character.tell "Game saved."
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def restore filename
|
31
|
+
if filename.nil?
|
32
|
+
stream.select "Enter the filename to restore:"
|
33
|
+
filename = stream.queue.pop
|
34
|
+
end
|
35
|
+
if filename != ''
|
36
|
+
if File.exists?(filename)
|
37
|
+
data = JSON.parse File.read(filename), symbolize_names: true
|
38
|
+
if (data[:metadata] != @character.plot.metadata)
|
39
|
+
@character.tell "The save file is not compatible with this version of the game."
|
40
|
+
else
|
41
|
+
return data
|
42
|
+
end
|
43
|
+
else
|
44
|
+
@character.tell "File \"#{filename}\" not found."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def flush
|
51
|
+
html_to_ansi(super)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/lib/gamefic/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gamefic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fred Snyder
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01
|
11
|
+
date: 2017-02-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -146,7 +146,7 @@ files:
|
|
146
146
|
- lib/gamefic/director/order.rb
|
147
147
|
- lib/gamefic/director/parser.rb
|
148
148
|
- lib/gamefic/engine.rb
|
149
|
-
- lib/gamefic/engine/
|
149
|
+
- lib/gamefic/engine/base.rb
|
150
150
|
- lib/gamefic/engine/tty.rb
|
151
151
|
- lib/gamefic/entity.rb
|
152
152
|
- lib/gamefic/grammar.rb
|
@@ -160,6 +160,7 @@ files:
|
|
160
160
|
- lib/gamefic/grammar/verbs.rb
|
161
161
|
- lib/gamefic/grammar/word_adapter.rb
|
162
162
|
- lib/gamefic/html.rb
|
163
|
+
- lib/gamefic/html_to_ansi.rb
|
163
164
|
- lib/gamefic/keywords.rb
|
164
165
|
- lib/gamefic/node.rb
|
165
166
|
- lib/gamefic/plot.rb
|
@@ -190,11 +191,14 @@ files:
|
|
190
191
|
- lib/gamefic/scene/conclusion.rb
|
191
192
|
- lib/gamefic/scene/custom.rb
|
192
193
|
- lib/gamefic/scene/multiple_choice.rb
|
193
|
-
- lib/gamefic/scene/
|
194
|
-
- lib/gamefic/scene/passive.rb
|
194
|
+
- lib/gamefic/scene/multiple_scene.rb
|
195
195
|
- lib/gamefic/scene/pause.rb
|
196
|
-
- lib/gamefic/scene/question.rb
|
197
196
|
- lib/gamefic/scene/yes_or_no.rb
|
197
|
+
- lib/gamefic/scene_data.rb
|
198
|
+
- lib/gamefic/scene_data/base.rb
|
199
|
+
- lib/gamefic/scene_data/multiple_choice.rb
|
200
|
+
- lib/gamefic/scene_data/multiple_scene.rb
|
201
|
+
- lib/gamefic/scene_data/yes_or_no.rb
|
198
202
|
- lib/gamefic/script.rb
|
199
203
|
- lib/gamefic/script/base.rb
|
200
204
|
- lib/gamefic/script/file.rb
|
@@ -209,6 +213,11 @@ files:
|
|
209
213
|
- lib/gamefic/subplot.rb
|
210
214
|
- lib/gamefic/syntax.rb
|
211
215
|
- lib/gamefic/tester.rb
|
216
|
+
- lib/gamefic/tty.rb
|
217
|
+
- lib/gamefic/user.rb
|
218
|
+
- lib/gamefic/user/base.rb
|
219
|
+
- lib/gamefic/user/buffer.rb
|
220
|
+
- lib/gamefic/user/tty.rb
|
212
221
|
- lib/gamefic/version.rb
|
213
222
|
homepage: http://gamefic.com
|
214
223
|
licenses:
|
data/lib/gamefic/engine/cgi.rb
DELETED
@@ -1,221 +0,0 @@
|
|
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
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
# Passive Scenes immediately cue another scene after they're finished. If no
|
4
|
-
# scene has been cued or prepared, it defaults to the :active scene.
|
5
|
-
#
|
6
|
-
class Scene::Passive < Scene::Custom
|
7
|
-
def initialize &block
|
8
|
-
@start = block
|
9
|
-
end
|
10
|
-
def start actor
|
11
|
-
this_scene = actor.scene
|
12
|
-
super
|
13
|
-
actor.cue :active if (actor.scene == this_scene and actor.next_scene.nil?)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|