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
data/lib/gamefic/plot.rb
CHANGED
@@ -5,80 +5,47 @@ require 'gamefic/tester'
|
|
5
5
|
require 'gamefic/source'
|
6
6
|
require 'gamefic/script'
|
7
7
|
require 'gamefic/query'
|
8
|
-
require 'gamefic/subplot'
|
9
8
|
|
10
9
|
module Gamefic
|
11
10
|
|
12
11
|
class Plot
|
13
|
-
autoload :SceneMount,
|
12
|
+
autoload :SceneMount, 'gamefic/plot/scene_mount'
|
14
13
|
autoload :CommandMount, 'gamefic/plot/command_mount'
|
15
|
-
autoload :
|
16
|
-
autoload :QueryMount, 'gamefic/plot/query_mount'
|
14
|
+
autoload :Entities, 'gamefic/plot/entities'
|
17
15
|
autoload :ArticleMount, 'gamefic/plot/article_mount'
|
18
|
-
autoload :YouMount,
|
19
|
-
autoload :Snapshot,
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
autoload :YouMount, 'gamefic/plot/you_mount'
|
17
|
+
autoload :Snapshot, 'gamefic/plot/snapshot'
|
18
|
+
autoload :Host, 'gamefic/plot/host'
|
19
|
+
autoload :Players, 'gamefic/plot/players'
|
20
|
+
autoload :Playbook, 'gamefic/plot/playbook'
|
21
|
+
autoload :Callbacks, 'gamefic/plot/callbacks'
|
22
|
+
|
23
|
+
attr_reader :commands, :imported_scripts, :source
|
24
|
+
# TODO: Metadata could use better protection
|
25
|
+
attr_accessor :metadata
|
24
26
|
include Stage
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
mount Gamefic, Tester, SceneMount, CommandMount, EntityMount, QueryMount,
|
30
|
-
ArticleMount, YouMount, Snapshot, Subplot::Host
|
31
|
-
expose :script, :introduction, :assert_action,
|
32
|
-
:on_update, :on_player_update, :entities, :on_ready, :on_player_ready,
|
33
|
-
:players, :scenes, :metadata
|
34
|
-
|
27
|
+
mount Gamefic, Tester, Players, SceneMount, CommandMount, Entities,
|
28
|
+
ArticleMount, YouMount, Snapshot, Host, Callbacks
|
29
|
+
expose :script, :metadata
|
30
|
+
|
35
31
|
# @param [Source::Base]
|
36
32
|
def initialize(source = nil)
|
37
33
|
@source = source || Source::Text.new({})
|
38
|
-
@commands = {}
|
39
|
-
@syntaxes = []
|
40
|
-
@ready_procs = []
|
41
|
-
@update_procs = []
|
42
|
-
@player_ready = []
|
43
|
-
@player_procs = []
|
44
34
|
@working_scripts = []
|
45
35
|
@imported_scripts = []
|
46
|
-
@
|
47
|
-
@
|
48
|
-
@asserts = {}
|
49
|
-
@default_scene = :active
|
50
|
-
@subplots = []
|
36
|
+
@running = false
|
37
|
+
@playbook = Playbook.new
|
51
38
|
post_initialize
|
52
39
|
end
|
53
40
|
|
54
|
-
def
|
55
|
-
|
56
|
-
@scenes = {}
|
57
|
-
@scenes[:active] = Scene::Active.new
|
58
|
-
@scenes[:concluded] = Scene::Conclusion.new
|
59
|
-
end
|
60
|
-
@scenes
|
41
|
+
def playbook
|
42
|
+
@playbook ||= Playbook.new
|
61
43
|
end
|
62
|
-
|
63
|
-
def
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
# Get an Array of all Actions defined in the Plot.
|
68
|
-
#
|
69
|
-
# @return [Array<Action>]
|
70
|
-
def actions
|
71
|
-
@commands.values.flatten
|
72
|
-
end
|
73
|
-
|
74
|
-
# Get an Array of all Actions associated with the specified verb.
|
75
|
-
#
|
76
|
-
# @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
|
77
|
-
# @return [Array<Action>] The verb's associated Actions
|
78
|
-
def actions_with_verb(verb)
|
79
|
-
@commands[verb].clone || []
|
44
|
+
|
45
|
+
def running?
|
46
|
+
@running
|
80
47
|
end
|
81
|
-
|
48
|
+
|
82
49
|
# Get an Array of all scripts that have been imported into the Plot.
|
83
50
|
#
|
84
51
|
# @return [Array<Script>] The imported scripts
|
@@ -86,117 +53,36 @@ module Gamefic
|
|
86
53
|
@imported_scripts ||= []
|
87
54
|
end
|
88
55
|
|
89
|
-
# Add a Block to be executed for the given verb.
|
90
|
-
# If the block returns false, the Action is cancelled.
|
91
|
-
#
|
92
|
-
# @example Require the player to have a property enabled before performing the Action.
|
93
|
-
# assert_action :authorize do |actor, verb, arguments|
|
94
|
-
# if actor[:can_authorize] == true
|
95
|
-
# true
|
96
|
-
# else
|
97
|
-
# actor.tell "You don't have permission to use the authorize command."
|
98
|
-
# false
|
99
|
-
# end
|
100
|
-
# end
|
101
|
-
#
|
102
|
-
# @yieldparam [Character] The character performing the Action.
|
103
|
-
# @yieldparam [Symbol] The verb associated with the Action.
|
104
|
-
# @yieldparam [Array] The arguments that will be passed to the Action's #execute method.
|
105
|
-
def assert_action name, &block
|
106
|
-
@asserts[name] = Assert.new(name, &block)
|
107
|
-
end
|
108
|
-
|
109
56
|
def post_initialize
|
110
57
|
# TODO: Should this method be required by extended classes?
|
111
58
|
end
|
112
59
|
|
113
|
-
# Get an Array of the Plot's current Entities.
|
114
|
-
#
|
115
|
-
# @return [Array<Entity>]
|
116
|
-
def entities
|
117
|
-
@entities.clone
|
118
|
-
end
|
119
|
-
|
120
60
|
# Get an Array of the Plot's current Syntaxes.
|
121
61
|
#
|
122
62
|
# @return [Array<Syntax>]
|
123
63
|
def syntaxes
|
124
|
-
|
125
|
-
end
|
126
|
-
|
127
|
-
# Get an Array of current players.
|
128
|
-
#
|
129
|
-
# @return [Array<Character>] The players.
|
130
|
-
def players
|
131
|
-
@players.clone
|
132
|
-
end
|
133
|
-
|
134
|
-
# Add a block to be executed on preparation of every turn.
|
135
|
-
# Each on_ready block is executed once per turn, as opposed to
|
136
|
-
# on_player_ready blocks, which are executed once for each player.
|
137
|
-
#
|
138
|
-
# @example Increment a turn counter
|
139
|
-
# turn = 0
|
140
|
-
# on_ready do
|
141
|
-
# turn += 1
|
142
|
-
# end
|
143
|
-
#
|
144
|
-
def on_ready(&block)
|
145
|
-
@ready_procs.push block
|
146
|
-
end
|
147
|
-
|
148
|
-
# Add a block to be executed after the Plot is finished updating a turn.
|
149
|
-
# Each on_update block is executed once per turn, as opposed to
|
150
|
-
# on_player_update blocks, which are executed once for each player.
|
151
|
-
def on_update(&block)
|
152
|
-
@update_procs.push block
|
153
|
-
end
|
154
|
-
|
155
|
-
# Add a block to be executed when a player is added to the game.
|
156
|
-
# Each Plot can only have one introduction. Subsequent calls will
|
157
|
-
# overwrite the existing one.
|
158
|
-
#
|
159
|
-
# @example Welcome the player to the game
|
160
|
-
# introduction do |actor|
|
161
|
-
# actor.tell "Welcome to the game!"
|
162
|
-
# end
|
163
|
-
#
|
164
|
-
# @yieldparam [Character]
|
165
|
-
def introduction (&proc)
|
166
|
-
@introduction = proc
|
167
|
-
end
|
168
|
-
|
169
|
-
# Introduce a player to the game.
|
170
|
-
# This method is typically called by the Engine that manages game execution.
|
171
|
-
def introduce(player)
|
172
|
-
player.extend Subplot::Feature
|
173
|
-
player.cue :active
|
174
|
-
@players.push player
|
175
|
-
@introduction.call(player) unless @introduction.nil?
|
64
|
+
playbook.syntaxes
|
176
65
|
end
|
177
66
|
|
178
67
|
# Prepare the Plot for the next turn of gameplay.
|
179
68
|
# This method is typically called by the Engine that manages game execution.
|
180
69
|
def ready
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
player.cue this_scene unless player.scene == this_scene
|
187
|
-
@player_ready.each { |block|
|
188
|
-
block.call player
|
189
|
-
}
|
190
|
-
}
|
70
|
+
playbook.freeze
|
71
|
+
@running = true
|
72
|
+
call_ready
|
73
|
+
call_player_ready
|
74
|
+
p_subplots.each { |s| s.ready }
|
191
75
|
end
|
192
76
|
|
193
77
|
# Update the Plot's current turn of gameplay.
|
194
78
|
# This method is typically called by the Engine that manages game execution.
|
195
79
|
def update
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
80
|
+
p_players.each { |p| process_input p }
|
81
|
+
p_entities.each { |e| e.update }
|
82
|
+
call_player_update
|
83
|
+
call_update
|
84
|
+
p_subplots.each { |s| s.update unless s.concluded? }
|
85
|
+
p_subplots.delete_if { |s| s.concluded? }
|
200
86
|
end
|
201
87
|
|
202
88
|
def tell entities, message, refresh = false
|
@@ -214,7 +100,7 @@ module Gamefic
|
|
214
100
|
def script path
|
215
101
|
imported_script = source.export(path)
|
216
102
|
if imported_script.nil?
|
217
|
-
raise "
|
103
|
+
raise LoadError.new("cannot load script -- #{path}")
|
218
104
|
end
|
219
105
|
if !@working_scripts.include?(imported_script) and !imported_scripts.include?(imported_script)
|
220
106
|
@working_scripts.push imported_script
|
@@ -227,105 +113,15 @@ module Gamefic
|
|
227
113
|
end
|
228
114
|
end
|
229
115
|
|
230
|
-
# Add a block to be executed for each player when the Plot prepares them
|
231
|
-
# for the next turn in the game.
|
232
|
-
#
|
233
|
-
# @yieldparam [Character]
|
234
|
-
def on_player_ready &block
|
235
|
-
@player_ready.push block
|
236
|
-
end
|
237
|
-
|
238
|
-
# Add a block to be executed for each player after they have completed a
|
239
|
-
# turn in the game.
|
240
|
-
#
|
241
|
-
# @yieldparam [Character]
|
242
|
-
def on_player_update &block
|
243
|
-
@player_procs.push block
|
244
|
-
end
|
245
|
-
|
246
116
|
private
|
247
117
|
|
248
118
|
def process_input player
|
249
119
|
line = player.queue.shift
|
250
120
|
if !line.nil?
|
251
|
-
|
121
|
+
player.scene.finish player, line
|
252
122
|
end
|
253
123
|
end
|
254
124
|
|
255
|
-
def update_player player
|
256
|
-
@player_procs.each { |proc|
|
257
|
-
proc.call player
|
258
|
-
}
|
259
|
-
end
|
260
|
-
|
261
|
-
def rem_entity(entity)
|
262
|
-
@entities.delete(entity)
|
263
|
-
@players.delete(entity)
|
264
|
-
end
|
265
|
-
|
266
|
-
def add_syntax syntax
|
267
|
-
if @commands[syntax.verb] == nil
|
268
|
-
raise "Action \"#{syntax.verb}\" does not exist."
|
269
|
-
end
|
270
|
-
# Delete duplicate syntaxes
|
271
|
-
@syntaxes = @syntaxes.delete_if { |existing|
|
272
|
-
existing == syntax
|
273
|
-
}
|
274
|
-
@syntaxes.unshift syntax
|
275
|
-
@syntaxes.sort! { |a, b|
|
276
|
-
if a.token_count == b.token_count
|
277
|
-
# For syntaxes of the same length, length of action takes precedence
|
278
|
-
b.first_word <=> a.first_word
|
279
|
-
else
|
280
|
-
b.token_count <=> a.token_count
|
281
|
-
end
|
282
|
-
}
|
283
|
-
end
|
284
|
-
|
285
|
-
def add_action(action)
|
286
|
-
@commands[action.verb] ||= []
|
287
|
-
@commands[action.verb].unshift action
|
288
|
-
@commands[action.verb].sort! { |a, b|
|
289
|
-
if a.specificity == b.specificity
|
290
|
-
# Newer action takes precedence
|
291
|
-
b.order_key <=> a.order_key
|
292
|
-
else
|
293
|
-
# Higher specificity takes precedence
|
294
|
-
b.specificity <=> a.specificity
|
295
|
-
end
|
296
|
-
}
|
297
|
-
generate_default_syntax action
|
298
|
-
end
|
299
|
-
|
300
|
-
def generate_default_syntax action
|
301
|
-
user_friendly = action.verb.to_s.gsub(/_/, ' ')
|
302
|
-
args = []
|
303
|
-
used_names = []
|
304
|
-
action.queries.each { |c|
|
305
|
-
num = 1
|
306
|
-
new_name = ":var"
|
307
|
-
while used_names.include? new_name
|
308
|
-
num = num + 1
|
309
|
-
new_name = ":var#{num}"
|
310
|
-
end
|
311
|
-
used_names.push new_name
|
312
|
-
user_friendly += " #{new_name}"
|
313
|
-
args.push new_name
|
314
|
-
}
|
315
|
-
add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}")
|
316
|
-
end
|
317
|
-
|
318
|
-
def rem_action(action)
|
319
|
-
@commands[action.verb].delete(action)
|
320
|
-
end
|
321
|
-
|
322
|
-
def rem_syntax(syntax)
|
323
|
-
@syntaxes.delete syntax
|
324
|
-
end
|
325
|
-
|
326
|
-
def add_entity(entity)
|
327
|
-
@entities.push entity
|
328
|
-
end
|
329
125
|
end
|
330
126
|
|
331
127
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Gamefic
|
2
|
+
|
3
|
+
module Plot::Callbacks
|
4
|
+
# Add a block to be executed on preparation of every turn.
|
5
|
+
#
|
6
|
+
# @example Increment a turn counter
|
7
|
+
# turn = 0
|
8
|
+
# on_ready do
|
9
|
+
# turn += 1
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
def on_ready &block
|
13
|
+
p_ready_procs.push block
|
14
|
+
end
|
15
|
+
|
16
|
+
# Add a block to be executed after the Plot is finished updating a turn.
|
17
|
+
#
|
18
|
+
def on_update &block
|
19
|
+
p_update_procs.push block
|
20
|
+
end
|
21
|
+
|
22
|
+
# Add a block to be executed for each player at the beginning of a turn.
|
23
|
+
#
|
24
|
+
# @example Tell the player how many turns they've played.
|
25
|
+
# on_player_ready do |player|
|
26
|
+
# player[:turns] ||= 0
|
27
|
+
# if player[:turns] > 0
|
28
|
+
# player.tell "Turn #{player[:turns]}"
|
29
|
+
# end
|
30
|
+
# player[:turns] += 1
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# @yieldparam [Character]
|
34
|
+
def on_player_ready &block
|
35
|
+
p_player_ready_procs.push block
|
36
|
+
end
|
37
|
+
|
38
|
+
# Add a block to be executed for each player at the end of a turn.
|
39
|
+
#
|
40
|
+
# @yieldparam [Character]
|
41
|
+
def on_player_update &block
|
42
|
+
p_player_update_procs.push block
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Execute the on_ready blocks. This method is typically called by the
|
48
|
+
# Plot while beginning a turn.
|
49
|
+
#
|
50
|
+
def call_ready
|
51
|
+
p_ready_procs.each { |p| p.call }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Execute the on_update blocks. This method is typically called by the
|
55
|
+
# Plot while ending a turn.
|
56
|
+
#
|
57
|
+
def call_update
|
58
|
+
p_update_procs.each { |p| p.call }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Execute the on_player_ready blocks for each player. This method is
|
62
|
+
# typically called by the Plot while beginning a turn, immediately after
|
63
|
+
# the on_ready blocks.
|
64
|
+
#
|
65
|
+
def call_player_ready
|
66
|
+
p_players.each { |player|
|
67
|
+
this_scene = player.next_scene || player.scene
|
68
|
+
player.prepare nil
|
69
|
+
player.cue this_scene unless player.scene == this_scene
|
70
|
+
p_player_ready_procs.each { |block| block.call player }
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
# Execute the on_player_update blocks for each player. This method is
|
75
|
+
# typically called by the Plot while ending a turn, immediately before the
|
76
|
+
# on_ready blocks.
|
77
|
+
#
|
78
|
+
def call_player_update
|
79
|
+
p_players.each { |player|
|
80
|
+
p_player_update_procs.each { |block| block.call player }
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def p_ready_procs
|
85
|
+
@p_ready_procs ||= []
|
86
|
+
end
|
87
|
+
|
88
|
+
def p_update_procs
|
89
|
+
@p_update_procs ||= []
|
90
|
+
end
|
91
|
+
|
92
|
+
def p_player_ready_procs
|
93
|
+
@p_player_ready_procs ||= []
|
94
|
+
end
|
95
|
+
|
96
|
+
def p_player_update_procs
|
97
|
+
@p_player_update_procs ||= []
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|