gamefic 1.4.1 → 1.5.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 -2
- data/lib/gamefic/character.rb +31 -45
- data/lib/gamefic/director/delegate.rb +46 -24
- data/lib/gamefic/director/order.rb +7 -0
- data/lib/gamefic/director/parser.rb +11 -11
- data/lib/gamefic/engine/base.rb +2 -3
- data/lib/gamefic/entity.rb +8 -26
- data/lib/gamefic/plot.rb +38 -242
- data/lib/gamefic/plot/callbacks.rb +101 -0
- data/lib/gamefic/plot/command_mount.rb +70 -40
- data/lib/gamefic/plot/entities.rb +82 -0
- data/lib/gamefic/plot/host.rb +46 -0
- data/lib/gamefic/plot/playbook.rb +192 -0
- data/lib/gamefic/plot/players.rb +15 -0
- data/lib/gamefic/plot/scene_mount.rb +69 -31
- data/lib/gamefic/plot/snapshot.rb +20 -5
- data/lib/gamefic/scene/active.rb +8 -1
- data/lib/gamefic/scene/base.rb +4 -26
- data/lib/gamefic/scene/custom.rb +53 -3
- data/lib/gamefic/scene/multiple_choice.rb +1 -0
- data/lib/gamefic/scene/yes_or_no.rb +1 -1
- data/lib/gamefic/scene_data/multiple_scene.rb +0 -4
- data/lib/gamefic/shell.rb +0 -1
- data/lib/gamefic/source/file.rb +1 -1
- data/lib/gamefic/stage.rb +10 -2
- data/lib/gamefic/subplot.rb +70 -53
- data/lib/gamefic/syntax.rb +9 -2
- data/lib/gamefic/tester.rb +1 -1
- data/lib/gamefic/text.rb +8 -0
- data/lib/gamefic/{ansi.rb → text/ansi.rb} +12 -15
- data/lib/gamefic/text/html.rb +68 -0
- data/lib/gamefic/text/html/conversions.rb +250 -0
- data/lib/gamefic/text/html/entities.rb +9 -0
- data/lib/gamefic/tty.rb +2 -0
- data/lib/gamefic/user/tty.rb +2 -4
- data/lib/gamefic/version.rb +1 -1
- metadata +12 -8
- data/lib/gamefic/direction.rb +0 -46
- data/lib/gamefic/html.rb +0 -68
- data/lib/gamefic/html_to_ansi.rb +0 -185
- data/lib/gamefic/plot/entity_mount.rb +0 -45
- data/lib/gamefic/rule.rb +0 -18
@@ -1,20 +1,50 @@
|
|
1
|
-
class NotConclusionError < Exception
|
2
|
-
end
|
3
|
-
|
4
1
|
module Gamefic
|
5
2
|
|
6
3
|
module Plot::SceneMount
|
4
|
+
def default_scene
|
5
|
+
@default_scene ||= Scene::Active.new(self)
|
6
|
+
end
|
7
|
+
|
8
|
+
def default_conclusion
|
9
|
+
@default_conclusion ||= Scene::Conclusion.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# Add a block to be executed when a player is added to the game.
|
13
|
+
# Each Plot can only have one introduction. Subsequent calls will
|
14
|
+
# overwrite the existing one.
|
15
|
+
#
|
16
|
+
# @example Welcome the player to the game
|
17
|
+
# introduction do |actor|
|
18
|
+
# actor.tell "Welcome to the game!"
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @yieldparam [Character]
|
22
|
+
def introduction (&proc)
|
23
|
+
@introduction = proc
|
24
|
+
end
|
25
|
+
|
26
|
+
# Introduce a player to the game.
|
27
|
+
# This method is typically called by the Engine that manages game execution.
|
28
|
+
def introduce(player)
|
29
|
+
player.playbook = playbook
|
30
|
+
player.cue default_scene
|
31
|
+
p_players.push player
|
32
|
+
@introduction.call(player) unless @introduction.nil?
|
33
|
+
end
|
34
|
+
|
7
35
|
# Create a multiple-choice scene.
|
8
36
|
# The user will be required to make a valid choice to continue.
|
9
37
|
#
|
10
38
|
# @yieldparam [Character]
|
11
39
|
# @yieldparam [Scene::Data::MultipleChoice]
|
12
|
-
def multiple_choice
|
13
|
-
|
14
|
-
|
40
|
+
def multiple_choice *choices, &block
|
41
|
+
s = Scene::MultipleChoice.new
|
42
|
+
s.on_start do |actor, data|
|
43
|
+
data.options.clear
|
15
44
|
data.options.push *choices
|
16
45
|
end
|
17
|
-
|
46
|
+
s.on_finish &block
|
47
|
+
s
|
18
48
|
end
|
19
49
|
|
20
50
|
# Create a yes-or-no scene.
|
@@ -22,22 +52,27 @@ module Gamefic
|
|
22
52
|
#
|
23
53
|
# @yieldparam [Character]
|
24
54
|
# @yieldparam [String] "yes" or "no"
|
25
|
-
def yes_or_no
|
26
|
-
|
55
|
+
def yes_or_no prompt = nil, &block
|
56
|
+
s = Scene::YesOrNo.new
|
27
57
|
unless prompt.nil?
|
28
|
-
|
58
|
+
s.on_start do |actor, data|
|
29
59
|
data.prompt = prompt
|
30
60
|
end
|
31
61
|
end
|
32
|
-
|
62
|
+
s.on_finish do |actor, data|
|
63
|
+
block.call actor, data unless block.nil?
|
64
|
+
actor.cue default_scene if actor.scene == s and actor.next_scene.nil?
|
65
|
+
end
|
66
|
+
s
|
33
67
|
end
|
34
68
|
|
35
|
-
def question
|
36
|
-
|
37
|
-
|
69
|
+
def question prompt = 'What is your answer?', &block
|
70
|
+
s = Scene::Custom.new
|
71
|
+
s.on_start do |actor, data|
|
38
72
|
data.prompt = prompt
|
39
73
|
end
|
40
|
-
|
74
|
+
s.on_finish &block
|
75
|
+
s
|
41
76
|
end
|
42
77
|
|
43
78
|
# Create a scene that pauses the game.
|
@@ -48,15 +83,17 @@ module Gamefic
|
|
48
83
|
# @param prompt [String] The text to display when prompting the user to continue.
|
49
84
|
# @yieldparam [Character]
|
50
85
|
# @yieldparam [Scene::Data::Base]
|
51
|
-
def pause
|
52
|
-
|
53
|
-
|
86
|
+
def pause prompt = nil, &block
|
87
|
+
s = Scene::Pause.new
|
88
|
+
s.on_start do |actor, data|
|
54
89
|
data.prompt = prompt unless prompt.nil?
|
55
90
|
block.call actor, data unless block.nil?
|
56
91
|
end
|
57
|
-
|
58
|
-
actor.cue :active if actor.scene == key and actor.next_scene.nil?
|
92
|
+
s.on_finish do |actor, data|
|
93
|
+
#actor.cue :active if actor.scene == key and actor.next_scene.nil?
|
94
|
+
actor.cue default_scene if actor.scene == s and actor.next_scene.nil?
|
59
95
|
end
|
96
|
+
s
|
60
97
|
end
|
61
98
|
|
62
99
|
# Create a conclusion.
|
@@ -66,9 +103,10 @@ module Gamefic
|
|
66
103
|
# @param key [Symbol] A unique name for the scene.
|
67
104
|
# @yieldparam [Character]
|
68
105
|
# @yieldparam [Scene::Data::Base]
|
69
|
-
def conclusion
|
70
|
-
|
71
|
-
|
106
|
+
def conclusion &block
|
107
|
+
s = Scene::Conclusion.new
|
108
|
+
s.on_start &block
|
109
|
+
s
|
72
110
|
end
|
73
111
|
|
74
112
|
# Create a custom scene.
|
@@ -106,10 +144,10 @@ module Gamefic
|
|
106
144
|
# @param key [Symbol] A unique name for the scene.
|
107
145
|
# @param key [cls] The class of scene to be instantiated.
|
108
146
|
# @yieldparam [Scene::Custom] The instantiated scene.
|
109
|
-
def scene
|
110
|
-
|
111
|
-
|
112
|
-
|
147
|
+
def scene cls = Scene::Custom, &block
|
148
|
+
s = cls.new
|
149
|
+
yield s if block_given?
|
150
|
+
s
|
113
151
|
end
|
114
152
|
|
115
153
|
# Choose a new scene based on a list of options.
|
@@ -130,15 +168,15 @@ module Gamefic
|
|
130
168
|
#
|
131
169
|
# @param key [Symbol] A unique name for the scene.
|
132
170
|
# @param map [Hash] A Hash of options and associated scene keys.
|
133
|
-
def multiple_scene
|
134
|
-
|
135
|
-
|
171
|
+
def multiple_scene map
|
172
|
+
s = Scene::MultipleScene.new
|
173
|
+
s.on_start do |actor, data|
|
136
174
|
map.each { |k, v|
|
137
175
|
data.map k, v
|
138
176
|
}
|
139
177
|
end
|
178
|
+
s
|
140
179
|
end
|
141
|
-
|
142
180
|
end
|
143
181
|
|
144
182
|
end
|
@@ -38,10 +38,10 @@ module Gamefic
|
|
38
38
|
# Restore the plot to the state of its first snapshot.
|
39
39
|
#
|
40
40
|
def restore_initial_state
|
41
|
-
|
41
|
+
p_entities[@initial_state.length..-1].each { |e|
|
42
42
|
e.parent = nil
|
43
43
|
}
|
44
|
-
|
44
|
+
p_entities.slice! @initial_state.length..-1
|
45
45
|
internal_restore @initial_state
|
46
46
|
end
|
47
47
|
|
@@ -86,13 +86,14 @@ module Gamefic
|
|
86
86
|
snapshot.each { |hash|
|
87
87
|
if entities[index].nil?
|
88
88
|
cls = Kernel.const_get(hash[:class])
|
89
|
-
|
89
|
+
p_entities[index] = make cls
|
90
90
|
end
|
91
91
|
internal_restore_hash hash, index
|
92
92
|
index += 1
|
93
93
|
}
|
94
94
|
nil
|
95
95
|
end
|
96
|
+
|
96
97
|
def internal_restore_hash hash, index
|
97
98
|
hash.each { |k, v|
|
98
99
|
if k == :scene
|
@@ -108,6 +109,7 @@ module Gamefic
|
|
108
109
|
}
|
109
110
|
nil
|
110
111
|
end
|
112
|
+
|
111
113
|
def reduce entities
|
112
114
|
reduced = []
|
113
115
|
index = 0
|
@@ -123,6 +125,7 @@ module Gamefic
|
|
123
125
|
}
|
124
126
|
reduced
|
125
127
|
end
|
128
|
+
|
126
129
|
def can_serialize? obj
|
127
130
|
return true if (obj == true or obj == false or obj.nil?)
|
128
131
|
allowed = [String, Fixnum, Float, Numeric, Entity, Direction, Hash, Array, Symbol]
|
@@ -131,6 +134,7 @@ module Gamefic
|
|
131
134
|
}
|
132
135
|
false
|
133
136
|
end
|
137
|
+
|
134
138
|
def serialize_obj obj
|
135
139
|
return nil if obj.nil?
|
136
140
|
return false if obj == false
|
@@ -140,13 +144,14 @@ module Gamefic
|
|
140
144
|
return serialize_array obj
|
141
145
|
else
|
142
146
|
if obj.kind_of?(Entity)
|
143
|
-
return "#<EIN_#{
|
147
|
+
return "#<EIN_#{p_entities.index(obj)}>"
|
144
148
|
elsif obj.kind_of?(Direction)
|
145
149
|
return "#<DIR_#{obj.name}>"
|
146
150
|
end
|
147
151
|
end
|
148
152
|
return obj
|
149
153
|
end
|
154
|
+
|
150
155
|
def serialize_hash obj
|
151
156
|
hash = {}
|
152
157
|
obj.each_pair { |k, v|
|
@@ -156,6 +161,7 @@ module Gamefic
|
|
156
161
|
}
|
157
162
|
return hash
|
158
163
|
end
|
164
|
+
|
159
165
|
def serialize_array obj
|
160
166
|
arr = []
|
161
167
|
obj.each_index { |i|
|
@@ -167,6 +173,7 @@ module Gamefic
|
|
167
173
|
}
|
168
174
|
return arr
|
169
175
|
end
|
176
|
+
|
170
177
|
def unserialize obj
|
171
178
|
if obj.kind_of?(Hash)
|
172
179
|
unserialize_hash obj
|
@@ -174,13 +181,14 @@ module Gamefic
|
|
174
181
|
unserialize_array obj
|
175
182
|
elsif obj.to_s.match(/^#<EIN_[0-9]+>$/)
|
176
183
|
i = obj[6..-2].to_i
|
177
|
-
|
184
|
+
p_entities[i]
|
178
185
|
elsif obj.to_s.match(/^#<DIR_[a-z]+>$/)
|
179
186
|
Direction.find(obj[6..-2])
|
180
187
|
else
|
181
188
|
obj
|
182
189
|
end
|
183
190
|
end
|
191
|
+
|
184
192
|
def unserialize_hash obj
|
185
193
|
hash = {}
|
186
194
|
obj.each_pair { |k, v|
|
@@ -188,6 +196,7 @@ module Gamefic
|
|
188
196
|
}
|
189
197
|
hash
|
190
198
|
end
|
199
|
+
|
191
200
|
def unserialize_array obj
|
192
201
|
arr = []
|
193
202
|
obj.each_index { |i|
|
@@ -195,7 +204,10 @@ module Gamefic
|
|
195
204
|
}
|
196
205
|
arr
|
197
206
|
end
|
207
|
+
|
198
208
|
def save_subplots
|
209
|
+
# TODO: Subplot snapshots are temporarily disabled.
|
210
|
+
return []
|
199
211
|
arr = []
|
200
212
|
subplots.each { |s|
|
201
213
|
hash = {}
|
@@ -210,7 +222,10 @@ module Gamefic
|
|
210
222
|
}
|
211
223
|
arr
|
212
224
|
end
|
225
|
+
|
213
226
|
def restore_subplots arr
|
227
|
+
# TODO: Subplot snapshots are temporarily disabled.
|
228
|
+
return
|
214
229
|
players.each { |p|
|
215
230
|
p.send(:p_subplots).clear
|
216
231
|
}
|
data/lib/gamefic/scene/active.rb
CHANGED
@@ -5,8 +5,15 @@ module Gamefic
|
|
5
5
|
# a Plot.
|
6
6
|
#
|
7
7
|
class Scene::Active < Scene::Base
|
8
|
+
attr_reader :plot
|
9
|
+
|
10
|
+
def initialize plot
|
11
|
+
@plot = plot
|
12
|
+
end
|
13
|
+
|
8
14
|
def finish actor, input
|
9
|
-
actor.perform input
|
15
|
+
o = actor.perform input.strip
|
16
|
+
actor.performed o
|
10
17
|
end
|
11
18
|
end
|
12
19
|
|
data/lib/gamefic/scene/base.rb
CHANGED
@@ -5,43 +5,21 @@ module Gamefic
|
|
5
5
|
#
|
6
6
|
class Scene::Base
|
7
7
|
def start actor
|
8
|
-
start_data_for actor
|
9
8
|
end
|
10
9
|
|
11
10
|
def finish actor, input
|
12
|
-
finish_data_for actor, input
|
13
11
|
end
|
14
12
|
|
15
|
-
def type
|
16
|
-
self.class.to_s.split('::').last
|
17
|
-
end
|
18
|
-
|
19
|
-
def data_class
|
20
|
-
SceneData::Base
|
21
|
-
end
|
22
|
-
|
23
13
|
# Get the prompt to be displayed to the user when accepting input.
|
24
14
|
#
|
25
15
|
# @return [String] The text to be displayed.
|
26
16
|
def prompt_for actor
|
27
|
-
|
17
|
+
'>'
|
28
18
|
end
|
29
19
|
|
30
|
-
|
31
|
-
|
32
|
-
def character_data
|
33
|
-
@character_data ||= {}
|
34
|
-
end
|
35
|
-
|
36
|
-
def start_data_for actor
|
37
|
-
character_data[actor] = data_class.new
|
38
|
-
end
|
39
|
-
|
40
|
-
def finish_data_for actor, input
|
41
|
-
data = character_data[actor]
|
42
|
-
data.input = input.strip
|
43
|
-
data
|
20
|
+
def type
|
21
|
+
self.class.to_s.split('::').last
|
44
22
|
end
|
45
23
|
end
|
46
|
-
|
24
|
+
|
47
25
|
end
|
data/lib/gamefic/scene/custom.rb
CHANGED
@@ -1,32 +1,69 @@
|
|
1
1
|
module Gamefic
|
2
2
|
|
3
|
-
# A Custom Scene
|
4
|
-
#
|
5
|
-
# or extension by other Scene classes.
|
3
|
+
# A Custom Scene allows for complete configuration of its behavior upon
|
4
|
+
# instantiation. It is suitable for direct instantiation or subclassing.
|
6
5
|
#
|
7
6
|
class Scene::Custom < Scene::Base
|
8
7
|
def initialize
|
9
8
|
yield self if block_given?
|
10
9
|
end
|
10
|
+
|
11
|
+
def data_class
|
12
|
+
SceneData::Base
|
13
|
+
end
|
14
|
+
|
15
|
+
# Define a block to be executed at the start of the scene.
|
16
|
+
# Unlike on_finish, start blocks may be executed more than once per turn,
|
17
|
+
# and more than one scene may be started in a single turn.
|
18
|
+
# It always gets executed in a plot's on_ready event and whenever it gets
|
19
|
+
# cued. (If the character is already in the scene being cued, on_start
|
20
|
+
# does not get repeated.)
|
21
|
+
#
|
22
|
+
# @yieldparam [Character]
|
23
|
+
# @yieldparam [SceneData::Base]
|
11
24
|
def on_start &block
|
12
25
|
@start = block
|
13
26
|
end
|
14
27
|
|
28
|
+
# Define a block to be executed at the end of the scene.
|
29
|
+
# The scene data passed to this block will include the character's input
|
30
|
+
# for this turn.
|
31
|
+
# Unlike on_start, finish only gets executed once per turn, during the
|
32
|
+
# plot's on_update event.
|
33
|
+
#
|
34
|
+
# @yieldparam [Character]
|
35
|
+
# @yieldparam [SceneData::Base]
|
15
36
|
def on_finish &block
|
16
37
|
@finish = block
|
17
38
|
end
|
18
39
|
|
40
|
+
# Start the scene.
|
41
|
+
# This method typically gets called from the plot during the on_ready
|
42
|
+
# event and whenever a character cues a scene.
|
43
|
+
#
|
19
44
|
def start actor
|
20
45
|
data = start_data_for(actor)
|
21
46
|
do_start_block actor, data
|
22
47
|
data
|
23
48
|
end
|
24
49
|
|
50
|
+
# End the scene.
|
51
|
+
# This method typically gets called from the plot during the on_update
|
52
|
+
# event.
|
53
|
+
#
|
25
54
|
def finish actor, input
|
26
55
|
data = finish_data_for(actor, input)
|
27
56
|
do_finish_block actor, data
|
28
57
|
end
|
29
58
|
|
59
|
+
# Get the text to be displayed to the user when receiving input.
|
60
|
+
#
|
61
|
+
# @return [String]
|
62
|
+
def prompt_for actor
|
63
|
+
return character_data[actor].prompt unless character_data[actor].nil?
|
64
|
+
'>'
|
65
|
+
end
|
66
|
+
|
30
67
|
private
|
31
68
|
|
32
69
|
def do_start_block actor, data
|
@@ -37,6 +74,19 @@ module Gamefic
|
|
37
74
|
@finish.call actor, data unless @finish.nil?
|
38
75
|
end
|
39
76
|
|
77
|
+
def character_data
|
78
|
+
@character_data ||= {}
|
79
|
+
end
|
80
|
+
|
81
|
+
def start_data_for actor
|
82
|
+
character_data[actor] ||= data_class.new
|
83
|
+
end
|
84
|
+
|
85
|
+
def finish_data_for actor, input
|
86
|
+
data = character_data[actor]
|
87
|
+
data.input = input.strip
|
88
|
+
data
|
89
|
+
end
|
40
90
|
end
|
41
91
|
|
42
92
|
end
|