gamefic 1.4.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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