gamefic 1.6.0 → 1.7.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 +4 -1
- data/lib/gamefic/action.rb +4 -0
- data/lib/gamefic/{character.rb → active.rb} +280 -232
- data/lib/gamefic/actor.rb +5 -0
- data/lib/gamefic/core_ext/string.rb +12 -12
- data/lib/gamefic/describable.rb +18 -1
- data/lib/gamefic/element.rb +31 -0
- data/lib/gamefic/engine/base.rb +17 -24
- data/lib/gamefic/entity.rb +18 -36
- data/lib/gamefic/grammar/gender.rb +1 -1
- data/lib/gamefic/grammar/pronouns.rb +3 -2
- data/lib/gamefic/messaging.rb +0 -1
- data/lib/gamefic/plot.rb +14 -4
- data/lib/gamefic/plot/callbacks.rb +1 -2
- data/lib/gamefic/plot/commands.rb +3 -4
- data/lib/gamefic/plot/darkroom.rb +264 -0
- data/lib/gamefic/plot/entities.rb +13 -1
- data/lib/gamefic/plot/host.rb +10 -8
- data/lib/gamefic/plot/playbook.rb +17 -13
- data/lib/gamefic/plot/scenes.rb +50 -12
- data/lib/gamefic/plot/snapshot.rb +15 -210
- data/lib/gamefic/query.rb +1 -0
- data/lib/gamefic/query/base.rb +15 -19
- data/lib/gamefic/query/external.rb +14 -0
- data/lib/gamefic/scene.rb +2 -4
- data/lib/gamefic/scene/{active.rb → activity.rb} +26 -26
- data/lib/gamefic/scene/base.rb +12 -7
- data/lib/gamefic/scene/multiple_choice.rb +0 -13
- data/lib/gamefic/scene/multiple_scene.rb +1 -1
- data/lib/gamefic/subplot.rb +16 -23
- data/lib/gamefic/user.rb +0 -1
- data/lib/gamefic/user/base.rb +15 -1
- data/lib/gamefic/user/tty.rb +15 -31
- data/lib/gamefic/version.rb +1 -1
- metadata +8 -26
- data/lib/gamefic/character/state.rb +0 -12
- data/lib/gamefic/user/buffer.rb +0 -32
@@ -18,7 +18,19 @@ module Gamefic
|
|
18
18
|
end
|
19
19
|
p_entities.push ent
|
20
20
|
p_dynamic.push ent if running?
|
21
|
-
ent
|
21
|
+
ent
|
22
|
+
end
|
23
|
+
|
24
|
+
# Cast an active entity.
|
25
|
+
# This method is similar to make, but it also provides the plot's
|
26
|
+
# playbook to the entity so it can perform actions. The entity should
|
27
|
+
# either be a kind of Gamefic::Actor or include the Gamefic::Active
|
28
|
+
# module.
|
29
|
+
#
|
30
|
+
# @return [Gamefic::Actor, Gamefic::Active]
|
31
|
+
def cast cls, args = {}, &block
|
32
|
+
ent = make cls, args, &block
|
33
|
+
ent.playbooks.push playbook
|
22
34
|
ent
|
23
35
|
end
|
24
36
|
|
data/lib/gamefic/plot/host.rb
CHANGED
@@ -14,30 +14,32 @@ module Gamefic
|
|
14
14
|
#
|
15
15
|
# @param subplot_class [Class] The class of the subplot to be created (Subplot by default)
|
16
16
|
# @return [Subplot]
|
17
|
-
def branch subplot_class = Gamefic::Subplot, introduce: nil, next_cue: nil
|
18
|
-
subplot = subplot_class.new(self, introduce: introduce, next_cue: next_cue
|
17
|
+
def branch subplot_class = Gamefic::Subplot, introduce: nil, next_cue: nil
|
18
|
+
subplot = subplot_class.new(self, introduce: introduce, next_cue: next_cue)
|
19
19
|
p_subplots.push subplot
|
20
20
|
subplot
|
21
21
|
end
|
22
22
|
|
23
|
-
# Get the player's current
|
23
|
+
# Get the player's current subplots.
|
24
24
|
#
|
25
|
-
# @return [Subplot]
|
26
|
-
def
|
25
|
+
# @return [Array<Subplot>]
|
26
|
+
def subplots_featuring player
|
27
|
+
result = []
|
27
28
|
subplots.each { |s|
|
28
|
-
|
29
|
+
result.push s if s.players.include?(player)
|
29
30
|
}
|
30
|
-
|
31
|
+
result
|
31
32
|
end
|
32
33
|
|
33
34
|
# Determine whether the player is involved in a subplot.
|
34
35
|
#
|
35
36
|
# @return [Boolean]
|
36
37
|
def in_subplot? player
|
37
|
-
!
|
38
|
+
!subplots_featuring(player).empty?
|
38
39
|
end
|
39
40
|
|
40
41
|
private
|
42
|
+
|
41
43
|
def p_subplots
|
42
44
|
@p_subplots ||= []
|
43
45
|
end
|
@@ -131,14 +131,7 @@ module Gamefic
|
|
131
131
|
if result.empty?
|
132
132
|
result.concat dispatch_from_string(actor, command.join(' '))
|
133
133
|
end
|
134
|
-
result
|
135
|
-
if a.rank == b.rank
|
136
|
-
b.order_key <=> a.order_key
|
137
|
-
else
|
138
|
-
b.rank <=> a.rank
|
139
|
-
end
|
140
|
-
}
|
141
|
-
result.uniq{|a| a.class}
|
134
|
+
result
|
142
135
|
end
|
143
136
|
|
144
137
|
def dispatch_from_string actor, text
|
@@ -147,11 +140,12 @@ module Gamefic
|
|
147
140
|
commands.each { |c|
|
148
141
|
available = actions_for(c.verb)
|
149
142
|
available.each { |a|
|
143
|
+
next if a.hidden?
|
150
144
|
o = a.attempt(actor, c.arguments)
|
151
145
|
result.unshift o unless o.nil?
|
152
146
|
}
|
153
147
|
}
|
154
|
-
result
|
148
|
+
sort_and_reduce_actions result
|
155
149
|
end
|
156
150
|
|
157
151
|
def dispatch_from_params actor, verb, params
|
@@ -160,7 +154,7 @@ module Gamefic
|
|
160
154
|
available.each { |a|
|
161
155
|
result.unshift a.new(actor, params) if a.valid?(actor, params)
|
162
156
|
}
|
163
|
-
result
|
157
|
+
sort_and_reduce_actions result
|
164
158
|
end
|
165
159
|
|
166
160
|
# Duplicate the playbook.
|
@@ -223,10 +217,20 @@ module Gamefic
|
|
223
217
|
}
|
224
218
|
end
|
225
219
|
|
220
|
+
def sort_and_reduce_actions arr
|
221
|
+
arr.sort { |a,b|
|
222
|
+
if a.rank == b.rank
|
223
|
+
b.order_key <=> a.order_key
|
224
|
+
else
|
225
|
+
b.rank <=> a.rank
|
226
|
+
end
|
227
|
+
}.uniq{|a| a.class}
|
228
|
+
end
|
229
|
+
|
226
230
|
def raise_order_key
|
227
|
-
|
228
|
-
tmp =
|
229
|
-
|
231
|
+
@@order_key ||= 0
|
232
|
+
tmp = @@order_key
|
233
|
+
@@order_key += 1
|
230
234
|
tmp
|
231
235
|
end
|
232
236
|
|
data/lib/gamefic/plot/scenes.rb
CHANGED
@@ -2,7 +2,7 @@ module Gamefic
|
|
2
2
|
|
3
3
|
module Plot::Scenes
|
4
4
|
def default_scene
|
5
|
-
@default_scene ||= Scene::
|
5
|
+
@default_scene ||= Scene::Activity
|
6
6
|
end
|
7
7
|
|
8
8
|
def default_conclusion
|
@@ -19,7 +19,7 @@ module Gamefic
|
|
19
19
|
# end
|
20
20
|
#
|
21
21
|
# @yieldparam [Gamefic::Character]
|
22
|
-
def introduction
|
22
|
+
def introduction(&proc)
|
23
23
|
@introduction = proc
|
24
24
|
end
|
25
25
|
|
@@ -28,7 +28,7 @@ module Gamefic
|
|
28
28
|
#
|
29
29
|
# @param [Gamefic::Character]
|
30
30
|
def introduce(player)
|
31
|
-
player.playbook
|
31
|
+
player.playbooks.push playbook unless player.playbooks.include?(playbook)
|
32
32
|
player.cue default_scene
|
33
33
|
p_players.push player
|
34
34
|
@introduction.call(player) unless @introduction.nil?
|
@@ -40,10 +40,12 @@ module Gamefic
|
|
40
40
|
# @yieldparam [Gamefic::Character]
|
41
41
|
# @yieldparam [Gamefic::Scene::Data::MultipleChoice]
|
42
42
|
def multiple_choice *choices, &block
|
43
|
-
Scene::MultipleChoice.subclass do |actor, scene|
|
43
|
+
s = Scene::MultipleChoice.subclass do |actor, scene|
|
44
44
|
scene.options.concat choices
|
45
45
|
scene.on_finish &block
|
46
46
|
end
|
47
|
+
scene_classes.push s
|
48
|
+
s
|
47
49
|
end
|
48
50
|
|
49
51
|
# Create a yes-or-no scene.
|
@@ -52,17 +54,30 @@ module Gamefic
|
|
52
54
|
# @yieldparam [Gamefic::Character]
|
53
55
|
# @yieldparam [Gamefic::Scene::YesOrNo]
|
54
56
|
def yes_or_no prompt = nil, &block
|
55
|
-
Scene::YesOrNo.subclass do |actor, scene|
|
57
|
+
s = Scene::YesOrNo.subclass do |actor, scene|
|
56
58
|
scene.prompt = prompt
|
57
59
|
scene.on_finish &block
|
58
60
|
end
|
61
|
+
scene_classes.push s
|
62
|
+
s
|
59
63
|
end
|
60
|
-
|
64
|
+
|
65
|
+
# Create a scene with custom processing on user input.
|
66
|
+
#
|
67
|
+
# @example Echo the user's response
|
68
|
+
# @scene = question 'What do you say?' do |actor, scene|
|
69
|
+
# actor.tell "You said #{scene.input}"
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# @yieldparam [Gamefic::Character]
|
73
|
+
# @yieldparam [Gamefic::Scene::YesOrNo]
|
61
74
|
def question prompt = 'What is your answer?', &block
|
62
|
-
Scene::Custom.subclass do |actor, scene|
|
75
|
+
s = Scene::Custom.subclass do |actor, scene|
|
63
76
|
scene.prompt = prompt
|
64
77
|
scene.on_finish &block
|
65
78
|
end
|
79
|
+
scene_classes.push s
|
80
|
+
s
|
66
81
|
end
|
67
82
|
|
68
83
|
# Create a scene that pauses the game.
|
@@ -73,10 +88,12 @@ module Gamefic
|
|
73
88
|
# @yieldparam [Gamefic::Character]
|
74
89
|
# @yieldparam [Gamefic::Scene::Pause]
|
75
90
|
def pause prompt = nil, &block
|
76
|
-
Scene::Pause.subclass do |actor, scene|
|
91
|
+
s = Scene::Pause.subclass do |actor, scene|
|
77
92
|
scene.prompt = prompt unless prompt.nil?
|
78
93
|
block.call(actor, scene) unless block.nil?
|
79
94
|
end
|
95
|
+
scene_classes.push s
|
96
|
+
s
|
80
97
|
end
|
81
98
|
|
82
99
|
# Create a conclusion.
|
@@ -86,7 +103,9 @@ module Gamefic
|
|
86
103
|
# @yieldparam [Gamefic::Character]
|
87
104
|
# @yieldparam [Gamefic::Scene::Conclusion]
|
88
105
|
def conclusion &block
|
89
|
-
Scene::Conclusion.subclass &block
|
106
|
+
s = Scene::Conclusion.subclass &block
|
107
|
+
scene_classes.push s
|
108
|
+
s
|
90
109
|
end
|
91
110
|
|
92
111
|
# Create a custom scene.
|
@@ -111,7 +130,9 @@ module Gamefic
|
|
111
130
|
# @yieldparam [Gamefic::Character]
|
112
131
|
# @yieldparam [Scene::Custom] The instantiated scene.
|
113
132
|
def custom cls = Scene::Custom, &block
|
114
|
-
cls.subclass &block
|
133
|
+
s = cls.subclass &block
|
134
|
+
scene_classes.push s
|
135
|
+
s
|
115
136
|
end
|
116
137
|
|
117
138
|
# Choose a new scene based on a list of options.
|
@@ -133,16 +154,33 @@ module Gamefic
|
|
133
154
|
# actor.cue select_one_or_two # The actor will be prompted to select "one" or "two" and get sent to the corresponding scene
|
134
155
|
# end
|
135
156
|
#
|
136
|
-
# @
|
157
|
+
# @example Customize options
|
158
|
+
# scene_one = pause # do...
|
159
|
+
# scene_two = pause # do...
|
160
|
+
#
|
161
|
+
# # Some event in the game sets actor[:can_go_to_scene_two] to true
|
162
|
+
#
|
163
|
+
# select_one_or_two = multiple_scene do |actor, scene|
|
164
|
+
# scene.map "Go to scene one", scene_one
|
165
|
+
# scene.map "Go to scene two", scene_two if actor[:can_go_to_scene_two]
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# @param map [Hash] A Hash of options and associated scenes.
|
137
169
|
# @yieldparam [Gamefic::Character]
|
138
170
|
# @yieldparam [Gamefic::Scene::MultipleScene]
|
139
171
|
def multiple_scene map = {}, &block
|
140
|
-
Scene::MultipleScene.subclass do |actor, scene|
|
172
|
+
s = Scene::MultipleScene.subclass do |actor, scene|
|
141
173
|
map.each_pair { |k, v|
|
142
174
|
scene.map k, v
|
143
175
|
}
|
144
176
|
block.call actor, scene unless block.nil?
|
145
177
|
end
|
178
|
+
scene_classes.push s
|
179
|
+
s
|
180
|
+
end
|
181
|
+
|
182
|
+
def scene_classes
|
183
|
+
@scene_classes ||= []
|
146
184
|
end
|
147
185
|
end
|
148
186
|
|
@@ -2,226 +2,31 @@ require 'json'
|
|
2
2
|
|
3
3
|
module Gamefic
|
4
4
|
module Plot::Snapshot
|
5
|
-
|
6
|
-
# Take a snapshot of the plot's current state.
|
7
|
-
# The snapshot is a hash with two keys: entities and subplots.
|
8
|
-
#
|
9
5
|
# @return [Hash]
|
10
6
|
def save
|
11
|
-
|
12
|
-
|
13
|
-
@initial_state = store
|
14
|
-
store = []
|
15
|
-
@initial_state.length.times do
|
16
|
-
store.push {}
|
17
|
-
end
|
18
|
-
else
|
19
|
-
store = reduce(store)
|
20
|
-
end
|
21
|
-
return {
|
22
|
-
entities: store,
|
23
|
-
subplots: save_subplots,
|
24
|
-
metadata: metadata
|
25
|
-
}
|
26
|
-
end
|
27
|
-
|
28
|
-
# Restore the plot to the state of the provided snapshot.
|
29
|
-
#
|
30
|
-
# @param snapshot [Hash]
|
31
|
-
def restore snapshot
|
32
|
-
restore_initial_state
|
33
|
-
internal_restore snapshot[:entities]
|
34
|
-
restore_subplots snapshot[:subplots]
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
# Restore the plot to the state of its first snapshot.
|
40
|
-
#
|
41
|
-
def restore_initial_state
|
42
|
-
p_entities[@initial_state.length..-1].each { |e|
|
43
|
-
e.parent = nil
|
44
|
-
}
|
45
|
-
p_entities.slice! @initial_state.length..-1
|
46
|
-
internal_restore @initial_state
|
47
|
-
end
|
48
|
-
|
49
|
-
def get_entity_hash
|
50
|
-
store = []
|
51
|
-
entities.each { |e|
|
52
|
-
hash = {}
|
53
|
-
e.instance_variables.each { |k|
|
54
|
-
next if k == :@children
|
55
|
-
v = e.instance_variable_get(k)
|
56
|
-
hash[k] = v
|
57
|
-
}
|
58
|
-
hash[:class] = e.class.to_s
|
59
|
-
store.push serialize_object(hash)
|
60
|
-
}
|
61
|
-
store
|
62
|
-
end
|
63
|
-
|
64
|
-
def internal_restore snapshot
|
65
|
-
index = 0
|
66
|
-
snapshot.each { |hash|
|
67
|
-
if entities[index].nil?
|
68
|
-
cls = Kernel.const_get(hash[:class])
|
69
|
-
p_entities[index] = make cls
|
70
|
-
end
|
71
|
-
internal_restore_hash hash, index
|
72
|
-
index += 1
|
73
|
-
}
|
74
|
-
nil
|
75
|
-
end
|
76
|
-
|
77
|
-
def internal_restore_hash hash, index
|
78
|
-
hash.each { |k, v|
|
79
|
-
if k == :scene
|
80
|
-
entities[index].cue v.to_sym
|
81
|
-
elsif (k != :class)
|
82
|
-
entities[index].instance_variable_set(k, unserialize(v))
|
83
|
-
end
|
84
|
-
}
|
85
|
-
nil
|
86
|
-
end
|
87
|
-
|
88
|
-
def reduce entities
|
89
|
-
reduced = []
|
90
|
-
index = 0
|
91
|
-
entities.each { |e|
|
92
|
-
r = {}
|
93
|
-
e.each_pair { |k, v|
|
94
|
-
if index >= @initial_state.length or @initial_state[index][k] != v
|
95
|
-
r[k] = v
|
96
|
-
end
|
97
|
-
}
|
98
|
-
reduced.push r
|
99
|
-
index += 1
|
100
|
-
}
|
101
|
-
reduced
|
102
|
-
end
|
103
|
-
|
104
|
-
def can_serialize? obj
|
105
|
-
return true if (obj == true or obj == false or obj.nil?)
|
106
|
-
allowed = [String, Fixnum, Float, Numeric, Entity, Direction, Hash, Array, Symbol]
|
107
|
-
allowed.each { |a|
|
108
|
-
return true if obj.kind_of?(a)
|
109
|
-
}
|
110
|
-
false
|
7
|
+
initial_state
|
8
|
+
internal_save
|
111
9
|
end
|
112
10
|
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
return serialize_array obj
|
120
|
-
else
|
121
|
-
if obj.kind_of?(Entity)
|
122
|
-
return "#<EIN_#{p_entities.index(obj)}>"
|
123
|
-
elsif obj.kind_of?(Direction)
|
124
|
-
return "#<DIR_#{obj.name}>"
|
125
|
-
end
|
126
|
-
end
|
127
|
-
return obj
|
128
|
-
end
|
129
|
-
|
130
|
-
def serialize_hash obj
|
131
|
-
hash = {}
|
132
|
-
obj.each_pair { |k, v|
|
133
|
-
if can_serialize?(k) and can_serialize?(v)
|
134
|
-
hash[serialize_object(k)] = serialize_object(v)
|
135
|
-
end
|
136
|
-
}
|
137
|
-
return hash
|
138
|
-
end
|
139
|
-
|
140
|
-
def serialize_array obj
|
141
|
-
arr = []
|
142
|
-
obj.each_index { |i|
|
143
|
-
if can_serialize?(obj[i])
|
144
|
-
arr[i] = serialize_object(obj[i])
|
145
|
-
else
|
146
|
-
raise "Bad array in snapshot"
|
147
|
-
end
|
148
|
-
}
|
149
|
-
return arr
|
11
|
+
def restore snapshot
|
12
|
+
# HACK Force conclusion of current subplots
|
13
|
+
p_subplots.each { |s| s.conclude }
|
14
|
+
p_subplots.clear
|
15
|
+
Gamefic::Plot::Darkroom.new(self).restore(snapshot)
|
16
|
+
entities.each { |e| e.flush }
|
150
17
|
end
|
151
18
|
|
152
|
-
def
|
153
|
-
if
|
154
|
-
|
155
|
-
elsif obj.kind_of?(Array)
|
156
|
-
unserialize_array obj
|
157
|
-
elsif obj.to_s.match(/^#<EIN_[0-9]+>$/)
|
158
|
-
i = obj[6..-2].to_i
|
159
|
-
p_entities[i]
|
160
|
-
elsif obj.to_s.match(/^#<DIR_[a-z]+>$/)
|
161
|
-
Direction.find(obj[6..-2])
|
162
|
-
else
|
163
|
-
obj
|
19
|
+
def initial_state
|
20
|
+
if @initial_state.nil?
|
21
|
+
@initial_state = internal_save
|
164
22
|
end
|
23
|
+
@initial_state
|
165
24
|
end
|
166
25
|
|
167
|
-
|
168
|
-
hash = {}
|
169
|
-
obj.each_pair { |k, v|
|
170
|
-
hash[unserialize(k)] = unserialize(v)
|
171
|
-
}
|
172
|
-
hash
|
173
|
-
end
|
174
|
-
|
175
|
-
def unserialize_array obj
|
176
|
-
arr = []
|
177
|
-
obj.each_index { |i|
|
178
|
-
arr[i] = unserialize(obj[i])
|
179
|
-
}
|
180
|
-
arr
|
181
|
-
end
|
26
|
+
private
|
182
27
|
|
183
|
-
def
|
184
|
-
|
185
|
-
return []
|
186
|
-
arr = []
|
187
|
-
subplots.each { |s|
|
188
|
-
hash = {}
|
189
|
-
hash[:class] = s.class.to_s
|
190
|
-
s.instance_variables.each { |k|
|
191
|
-
v = s.instance_variable_get(k)
|
192
|
-
if can_serialize?(v)
|
193
|
-
hash[k] = serialize_object(v)
|
194
|
-
end
|
195
|
-
}
|
196
|
-
arr.push hash
|
197
|
-
}
|
198
|
-
arr
|
199
|
-
end
|
200
|
-
|
201
|
-
def restore_subplots arr
|
202
|
-
# TODO: Subplot snapshots are temporarily disabled.
|
203
|
-
return
|
204
|
-
players.each { |p|
|
205
|
-
p.send(:p_subplots).clear
|
206
|
-
}
|
207
|
-
p_subplots.clear
|
208
|
-
arr.each { |hash|
|
209
|
-
cls = Kernel.const_get(hash[:class])
|
210
|
-
subplot = cls.new self
|
211
|
-
hash.each { |k, v|
|
212
|
-
if k != :class
|
213
|
-
subplot.instance_variable_set(k, unserialize(v))
|
214
|
-
end
|
215
|
-
}
|
216
|
-
subplot.players.each { |p|
|
217
|
-
p.send(:p_subplots).push subplot
|
218
|
-
}
|
219
|
-
subplot.entities.each { |e|
|
220
|
-
e.extend Subplot::Element
|
221
|
-
e.instance_variable_set(:@subplot, subplot)
|
222
|
-
}
|
223
|
-
p_subplots.push subplot
|
224
|
-
}
|
28
|
+
def internal_save
|
29
|
+
Gamefic::Plot::Darkroom.new(self).save
|
225
30
|
end
|
226
31
|
end
|
227
32
|
end
|