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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gamefic.rb +1 -2
  3. data/lib/gamefic/character.rb +31 -45
  4. data/lib/gamefic/director/delegate.rb +46 -24
  5. data/lib/gamefic/director/order.rb +7 -0
  6. data/lib/gamefic/director/parser.rb +11 -11
  7. data/lib/gamefic/engine/base.rb +2 -3
  8. data/lib/gamefic/entity.rb +8 -26
  9. data/lib/gamefic/plot.rb +38 -242
  10. data/lib/gamefic/plot/callbacks.rb +101 -0
  11. data/lib/gamefic/plot/command_mount.rb +70 -40
  12. data/lib/gamefic/plot/entities.rb +82 -0
  13. data/lib/gamefic/plot/host.rb +46 -0
  14. data/lib/gamefic/plot/playbook.rb +192 -0
  15. data/lib/gamefic/plot/players.rb +15 -0
  16. data/lib/gamefic/plot/scene_mount.rb +69 -31
  17. data/lib/gamefic/plot/snapshot.rb +20 -5
  18. data/lib/gamefic/scene/active.rb +8 -1
  19. data/lib/gamefic/scene/base.rb +4 -26
  20. data/lib/gamefic/scene/custom.rb +53 -3
  21. data/lib/gamefic/scene/multiple_choice.rb +1 -0
  22. data/lib/gamefic/scene/yes_or_no.rb +1 -1
  23. data/lib/gamefic/scene_data/multiple_scene.rb +0 -4
  24. data/lib/gamefic/shell.rb +0 -1
  25. data/lib/gamefic/source/file.rb +1 -1
  26. data/lib/gamefic/stage.rb +10 -2
  27. data/lib/gamefic/subplot.rb +70 -53
  28. data/lib/gamefic/syntax.rb +9 -2
  29. data/lib/gamefic/tester.rb +1 -1
  30. data/lib/gamefic/text.rb +8 -0
  31. data/lib/gamefic/{ansi.rb → text/ansi.rb} +12 -15
  32. data/lib/gamefic/text/html.rb +68 -0
  33. data/lib/gamefic/text/html/conversions.rb +250 -0
  34. data/lib/gamefic/text/html/entities.rb +9 -0
  35. data/lib/gamefic/tty.rb +2 -0
  36. data/lib/gamefic/user/tty.rb +2 -4
  37. data/lib/gamefic/version.rb +1 -1
  38. metadata +12 -8
  39. data/lib/gamefic/direction.rb +0 -46
  40. data/lib/gamefic/html.rb +0 -68
  41. data/lib/gamefic/html_to_ansi.rb +0 -185
  42. data/lib/gamefic/plot/entity_mount.rb +0 -45
  43. 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, 'gamefic/plot/scene_mount'
12
+ autoload :SceneMount, 'gamefic/plot/scene_mount'
14
13
  autoload :CommandMount, 'gamefic/plot/command_mount'
15
- autoload :EntityMount, 'gamefic/plot/entity_mount'
16
- autoload :QueryMount, 'gamefic/plot/query_mount'
14
+ autoload :Entities, 'gamefic/plot/entities'
17
15
  autoload :ArticleMount, 'gamefic/plot/article_mount'
18
- autoload :YouMount, 'gamefic/plot/you_mount'
19
- autoload :Snapshot, 'gamefic/plot/snapshot'
20
-
21
- attr_reader :commands, :imported_scripts, :rules, :asserts, :source
22
- # TODO Metadata could use better protection
23
- attr_accessor :default_scene, :metadata
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
- # TODO This include is only here to make the module's methods visible in the IDE.
26
- # Gamefic Studio has a PlotStageMetaMapper that handles it, but it doesn't run if
27
- # the plugin isn't activated.
28
- #include Gamefic, Tester, SceneMount, CommandMount, EntityMount, QueryMount, ArticleMount, YouMount, Snapshot
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
- @entities = []
47
- @players = []
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 scenes
55
- if @scenes.nil?
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 concluded?(actor)
64
- scenes[actor.scene].kind_of?(Scene::Conclusion)
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
- @syntaxes.clone
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
- @ready_procs.each { |p| p.call }
182
- # Prepare player scenes for the update.
183
- @players.each { |player|
184
- this_scene = player.next_scene || player.scene
185
- player.prepare nil
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
- @players.each { |p| process_input p }
197
- @entities.each { |e| e.update }
198
- @players.each { |player| update_player player }
199
- @update_procs.each { |p| p.call }
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 "Script not found: #{path}"
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
- scenes[player.scene].finish player, line
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