gamefic 1.4.1 → 1.5.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.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
|