gamefic 0.6.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/bin/gamefic +3 -0
  3. data/lib/gamefic/character.rb +42 -6
  4. data/lib/gamefic/director/parser.rb +25 -25
  5. data/lib/gamefic/engine/tty.rb +14 -2
  6. data/lib/gamefic/engine.rb +8 -7
  7. data/lib/gamefic/grammar/gender.rb +1 -1
  8. data/lib/gamefic/grammar/pronouns.rb +53 -8
  9. data/lib/gamefic/grammar/verbs.rb +1 -0
  10. data/lib/gamefic/grammar/word_adapter.rb +31 -18
  11. data/lib/gamefic/html.rb +27 -13
  12. data/lib/gamefic/plot/article_mount.rb +1 -1
  13. data/lib/gamefic/plot/scene_mount.rb +27 -110
  14. data/lib/gamefic/{snapshots.rb → plot/snapshot.rb} +60 -29
  15. data/lib/gamefic/plot/you_mount.rb +1 -1
  16. data/lib/gamefic/plot.rb +56 -29
  17. data/lib/gamefic/query/base.rb +3 -1
  18. data/lib/gamefic/scene/active.rb +11 -18
  19. data/lib/gamefic/scene/base.rb +21 -0
  20. data/lib/gamefic/scene/conclusion.rb +11 -0
  21. data/lib/gamefic/scene/custom.rb +21 -0
  22. data/lib/gamefic/scene/multiple_choice/input.rb +11 -0
  23. data/lib/gamefic/scene/multiple_choice.rb +73 -0
  24. data/lib/gamefic/scene/passive.rb +17 -0
  25. data/lib/gamefic/scene/pause.rb +24 -0
  26. data/lib/gamefic/scene/question.rb +21 -0
  27. data/lib/gamefic/scene/yes_or_no.rb +30 -0
  28. data/lib/gamefic/scene.rb +10 -120
  29. data/lib/gamefic/script/base.rb +7 -2
  30. data/lib/gamefic/script.rb +4 -0
  31. data/lib/gamefic/shell/command/base.rb +38 -0
  32. data/lib/gamefic/shell/command/play.rb +51 -0
  33. data/lib/gamefic/shell/command.rb +4 -0
  34. data/lib/gamefic/shell/registry.rb +13 -0
  35. data/lib/gamefic/shell.rb +14 -71
  36. data/lib/gamefic/source/file.rb +1 -1
  37. data/lib/gamefic/source.rb +5 -0
  38. data/lib/gamefic/tester.rb +0 -1
  39. data/lib/gamefic/version.rb +1 -1
  40. data/lib/gamefic.rb +1 -6
  41. metadata +69 -61
  42. data/lib/gamefic/scene/concluded.rb +0 -22
  43. data/lib/gamefic/scene/multiplechoice.rb +0 -74
  44. data/lib/gamefic/scene/paused.rb +0 -26
  45. data/lib/gamefic/scene/yesorno.rb +0 -43
@@ -4,50 +4,25 @@ end
4
4
  module Gamefic
5
5
 
6
6
  module Plot::SceneMount
7
- # Get a Hash of SceneManager objects.
8
- #
9
- # @return [Hash<Symbol, SceneManager>]
10
- def scene_managers
11
- if @scene_managers.nil?
12
- @scene_managers ||= {}
13
- @scene_managers[:active] = ActiveSceneManager.new
14
- @scene_managers[:concluded] = ConcludedSceneManager.new
15
- end
16
- @scene_managers
17
- end
18
-
19
7
  # Create a multiple-choice scene.
20
8
  # The user will be required to make a valid choice to continue
21
9
  #
22
10
  # @yieldparam [Character]
23
- # @yieldparam [MultipleChoiceSceneData]
24
- def multiple_choice key, *args, &block
25
- scene_managers[key] = MultipleChoiceSceneManager.new do |config|
26
- config.start do |actor, data|
27
- data.options = args
28
- end
29
- config.finish(&block)
30
- end
11
+ # @yieldparam [String]
12
+ def multiple_choice key, options, &block
13
+ scenes[key] = Scene::MultipleChoice.new(
14
+ options: options,
15
+ finish: block
16
+ )
31
17
  end
32
18
 
33
19
  # Create a yes-or-no scene.
34
20
  # The user will be required to answer Yes or No to continue.
35
21
  #
36
22
  # @yieldparam [Character]
37
- # @yieldparam [YesOrNoSceneData]
23
+ # @yieldparam [String] "yes" or "no"
38
24
  def yes_or_no key, prompt = nil, &block
39
- manager = YesOrNoSceneManager.new do |config|
40
- config.prompt = prompt
41
- config.finish do |actor, data|
42
- if data.answer.nil?
43
- actor.tell "Please answer Yes or No."
44
- else
45
- data.next_cue ||= :active
46
- block.call(actor, data)
47
- end
48
- end
49
- end
50
- scene_managers[key] = manager
25
+ scenes[key] = Scene::YesOrNo.new(prompt, &block)
51
26
  end
52
27
 
53
28
  # Create a scene with a prompt.
@@ -55,18 +30,11 @@ module Gamefic
55
30
  # from the user.
56
31
  #
57
32
  # @param key [Symbol] A unique name for the scene.
58
- # @param prompt [String] The prompt message to display to the user.
33
+ # @param prompt [String] The input prompt to display to the user.
59
34
  # @yieldparam [Character]
60
- # @yieldparam [SceneData]
61
- def prompt key, prompt, &block
62
- scene_managers[key] = SceneManager.new do |config|
63
- config.prompt = prompt
64
- config.finish do |actor, data|
65
- data.next_cue ||= :active
66
- block.call actor, data
67
- end
68
- end
69
- scene_managers[key].state = "Prompted"
35
+ # @yieldparam [String]
36
+ def question key, prompt, &block
37
+ scenes[key] = Scene::Question.new prompt, &block
70
38
  end
71
39
 
72
40
  # Create a scene that pauses the game.
@@ -75,15 +43,8 @@ module Gamefic
75
43
  #
76
44
  # @param key [Symbol] A unique name for the scene.
77
45
  # @yieldparam [Character]
78
- # @yieldparam [SceneData]
79
46
  def pause key, &block
80
- manager = PausedSceneManager.new do |config|
81
- config.start do |actor, data|
82
- data.next_cue = :active
83
- block.call actor, data
84
- end
85
- end
86
- scene_managers[key] = manager
47
+ scenes[key] = Scene::Pause.new &block
87
48
  end
88
49
 
89
50
  # Create a conclusion.
@@ -91,32 +52,18 @@ module Gamefic
91
52
  #
92
53
  # @param key [Symbol] A unique name for the scene.
93
54
  # @yieldparam [Character]
94
- # @yieldparam [SceneData]
95
55
  def conclusion key, &block
96
- manager = ConcludedSceneManager.new do |config|
97
- config.start(&block)
98
- end
99
- scene_managers[key] = manager
56
+ scenes[key] = Scene::Conclusion.new &block
100
57
  end
101
58
 
102
59
  # Create a generic scene.
103
- # After the scene is complete, it will automatically start the next cue.
60
+ # After the scene is complete, it will automatically start the next
61
+ # prepared scene, or the :active scene if none is prepared.
104
62
  #
105
63
  # @param [Symbol] A unique name for the scene.
106
64
  # @yieldparam [Character]
107
- # @yieldparam [SceneData]
108
65
  def scene key, &block
109
- scene = SceneManager.new do |manager|
110
- manager.start do |actor, data|
111
- data.next_cue = :active
112
- block.call(actor, data) if !block.nil?
113
- cue actor, data.next_cue
114
- actor.scene.start actor
115
- end
116
- # Since generic scenes always cue a new scene, there's no reason to
117
- # define a finish block.
118
- end
119
- scene_managers[key] = scene
66
+ scenes[key] = Scene::Passive.new &block
120
67
  end
121
68
 
122
69
  # Branch to a new scene based on a list of options.
@@ -132,50 +79,20 @@ module Gamefic
132
79
  # actor.tell "You went to scene two"
133
80
  # end
134
81
  # introduction do |actor|
135
- # cue actor, :select_one_or_two # The actor will be prompted to select "one" or "two" and get sent to the corresponding scene
82
+ # actor.cue, :select_one_or_two # The actor will be prompted to select "one" or "two" and get sent to the corresponding scene
136
83
  # end
137
84
  #
138
85
  # @param key [Symbol] A unique name for the scene.
139
- # @param options [Hash] A Hash of options and associated scene keys.
140
- def branch key, options
141
- multiple_choice key, *options.keys do |actor, data|
142
- cue actor, options[data.selection]
143
- end
144
- end
145
-
146
- # Cue a Character's next scene
147
- #
148
- # @param actor [Character] The character being cued
149
- # @param key [Symbol] The name of the scene
150
- def cue actor, key
151
- if !actor.scene.nil? and actor.scene.state == "Concluded"
152
- return
153
- end
154
- if key.nil?
155
- raise "Cueing scene with nil key"
156
- end
157
- manager = scene_managers[key]
158
- if manager.nil?
159
- raise "No '#{key}' scene found"
160
- else
161
- actor.scene = manager.prepare key
162
- end
163
- @scene
164
- end
165
-
166
- # This is functionally identical to #cue, but it also raises an
167
- # exception if the selected scene is not a Concluded state.
168
- #
169
- # @param actor [Character] The character being cued
170
- # @param key [Symbol] The name of the scene
171
- def conclude actor, key
172
- key = :concluded if key.nil?
173
- manager = scene_managers[key]
174
- if manager.state != "Concluded"
175
- raise NotConclusionError("Cued scene '#{key}' is not a conclusion")
176
- end
177
- cue actor, key
86
+ # @param map [Hash] A Hash of options and associated scene keys.
87
+ def branch key, map
88
+ scenes[key] = Scene::MultipleChoice.new(
89
+ options: map.keys,
90
+ finish: proc { |actor, input|
91
+ actor.cue map[input.choice]
92
+ }
93
+ )
178
94
  end
95
+
179
96
  end
180
97
 
181
98
  end
@@ -1,13 +1,8 @@
1
1
  require 'json'
2
2
 
3
3
  module Gamefic
4
- class Snapshots
5
- attr_accessor :history
6
- def initialize entities
7
- @history = []
8
- @entities = entities
9
- end
10
- def save entities
4
+ module Plot::Snapshot
5
+ def save
11
6
  store = []
12
7
  index = 0
13
8
  entities.each { |e|
@@ -19,11 +14,11 @@ module Gamefic
19
14
  end
20
15
  if e.respond_to?(m) == true
21
16
  begin
22
- xxx = e.send(m)
23
- if xxx == false
17
+ val = e.send(m)
18
+ if val == false
24
19
  hash[con] = false
25
- elsif xxx
26
- hash[con] = serialize_obj(xxx)
20
+ elsif val
21
+ hash[con] = serialize_obj(val)
27
22
  else
28
23
  hash[con] = nil
29
24
  end
@@ -32,6 +27,7 @@ module Gamefic
32
27
  end
33
28
  end
34
29
  }
30
+ hash[:class] = e.class.to_s
35
31
  hash[:session] = {}
36
32
  e.session.each_pair { |k, v|
37
33
  hash[:session][k] = serialize_obj(v)
@@ -39,29 +35,68 @@ module Gamefic
39
35
  store.push hash
40
36
  index += 1
41
37
  }
42
- if @history.length > 10
43
- @history.shift
38
+ if @initial_state.nil?
39
+ @initial_state = store
40
+ store = []
41
+ @initial_state.length.times do
42
+ store.push {}
43
+ end
44
+ else
45
+ store = reduce(store)
44
46
  end
45
- json = JSON.generate(store)
46
- #puts json
47
- json
47
+ store
48
48
  end
49
49
  def restore snapshot
50
- data = JSON.parse(snapshot)
50
+ internal_restore snapshot, true
51
+ end
52
+ private
53
+ def internal_restore snapshot, with_restore = true
54
+ if with_restore
55
+ @entities[@initial_state.length..-1].each { |e|
56
+ e.parent = nil
57
+ }
58
+ @entities.slice! @initial_state.length..-1
59
+ internal_restore @initial_state, false
60
+ end
51
61
  index = 0
52
- data.each { |hash|
62
+ snapshot.each { |hash|
63
+ if entities[index].nil?
64
+ if with_restore
65
+ cls = Kernel.const_get(hash[:class])
66
+ entities[index] = make cls
67
+ else
68
+ break
69
+ end
70
+ end
53
71
  hash.each_pair { |k, v|
54
- @entities[index].send("#{k}=", unserialize(v)) if k.to_s != "session"
55
- }
56
- #unser = unserialize(hash["session"])
57
- hash["session"].each_pair { |k, v|
58
- @entities[index].session[k.to_sym] = unserialize(v)
72
+ if k == :scene
73
+ entities[index].cue v.to_sym
74
+ else
75
+ entities[index].send("#{k}=", unserialize(v)) if k != :session and k != :class
76
+ end
59
77
  }
78
+ unless hash[:session].nil?
79
+ hash[:session].each_pair { |k, v|
80
+ entities[index].session[k.to_sym] = unserialize(v)
81
+ }
82
+ end
60
83
  index += 1
61
84
  }
62
85
  end
63
- def blacklist
64
- [:children, :session, :scene, :object_of_pronoun, :test_queue, :test_queue_scene, :test_queue_length, :testing]
86
+ def reduce entities
87
+ reduced = []
88
+ index = 0
89
+ entities.each { |e|
90
+ r = {}
91
+ e.each_pair { |k, v|
92
+ if index >= @initial_state.length or @initial_state[index][k] != v
93
+ r[k] = v
94
+ end
95
+ }
96
+ reduced.push r
97
+ index += 1
98
+ }
99
+ reduced
65
100
  end
66
101
  private
67
102
  def can_serialize? obj
@@ -98,8 +133,6 @@ module Gamefic
98
133
  return "#<EIN_#{@entities.index(obj)}>"
99
134
  elsif obj.kind_of?(Direction)
100
135
  return "#<DIR_#{obj.name}>"
101
- #elsif obj.kind_of?(Symbol)
102
- # return "#<SYM_#{obj.to_s}>"
103
136
  end
104
137
  end
105
138
  return obj
@@ -123,8 +156,6 @@ module Gamefic
123
156
  @entities[i]
124
157
  elsif obj.to_s.match(/^#<DIR_[a-z]+>$/)
125
158
  Direction.find(obj[6..-2])
126
- #elsif obj.to_s.match(/^#<SYM_[a-z]+>$/)
127
- # Direction.find(obj[6..-2].to_sym)
128
159
  else
129
160
  obj
130
161
  end
@@ -2,7 +2,7 @@ require 'gamefic'
2
2
  require 'gamefic/grammar'
3
3
 
4
4
  module Gamefic
5
- module YouMount
5
+ module Plot::YouMount
6
6
  class YouGrammarSet
7
7
  include Grammar::Gender
8
8
  include Grammar::Person
data/lib/gamefic/plot.rb CHANGED
@@ -5,8 +5,6 @@ require 'gamefic/tester'
5
5
  require 'gamefic/source'
6
6
  require 'gamefic/script'
7
7
  require 'gamefic/query'
8
- require 'gamefic/plot/article_mount'
9
- require 'gamefic/plot/you_mount'
10
8
 
11
9
  module Gamefic
12
10
 
@@ -15,35 +13,52 @@ module Gamefic
15
13
  autoload :CommandMount, 'gamefic/plot/command_mount'
16
14
  autoload :EntityMount, 'gamefic/plot/entity_mount'
17
15
  autoload :QueryMount, 'gamefic/plot/query_mount'
18
- #autoload :ArticleMount, 'gamefic/plot/article_mount'
19
- #autoload :YouMount, 'gamefic/plot/you_mount'
16
+ autoload :ArticleMount, 'gamefic/plot/article_mount'
17
+ autoload :YouMount, 'gamefic/plot/you_mount'
18
+ autoload :Snapshot, 'gamefic/plot/snapshot'
19
+
20
20
  attr_reader :commands, :imported_scripts, :rules, :asserts, :source
21
- attr_accessor :default_scene
21
+ # TODO Metadata could use better protection
22
+ attr_accessor :default_scene, :metadata
22
23
  include Stage
23
24
  # TODO This include is only here to make the module's methods visible in the IDE.
24
25
  # Gamefic Studio has a PlotStageMetaMapper that handles it, but it doesn't run if
25
26
  # the plugin isn't activated.
26
- include Gamefic, Tester, SceneMount, CommandMount, EntityMount, QueryMount, ArticleMount, YouMount
27
- mount Gamefic, Tester, SceneMount, CommandMount, EntityMount, QueryMount, ArticleMount, YouMount
28
- expose :script, :introduction, :assert_action, :on_update, :on_player_update, :entities, :on_ready, :on_player_ready, :players
27
+ #include Gamefic, Tester, SceneMount, CommandMount, EntityMount, QueryMount, ArticleMount, YouMount, Snapshot
28
+ mount Gamefic, Tester, SceneMount, CommandMount, EntityMount, QueryMount, ArticleMount, YouMount, Snapshot
29
+ expose :script, :introduction, :assert_action, :before_player_update, :on_update, :on_player_update, :entities, :on_ready, :on_player_ready, :players, :scenes, :metadata
29
30
 
30
31
  # @param [Source::Base]
31
32
  def initialize(source = nil)
32
33
  @source = source || Source::Text.new({})
33
- @commands = Hash.new
34
- @syntaxes = Array.new
35
- @ready_procs = Array.new
36
- @update_procs = Array.new
37
- @player_ready = Array.new
38
- @player_procs = Array.new
39
- @working_scripts = Array.new
40
- @imported_scripts = Array.new
41
- @entities = Array.new
42
- @players = Array.new
43
- @asserts = Hash.new
34
+ @commands = {}
35
+ @syntaxes = []
36
+ @ready_procs = []
37
+ @before_player_update_procs = []
38
+ @update_procs = []
39
+ @player_ready = []
40
+ @player_procs = []
41
+ @working_scripts = []
42
+ @imported_scripts = []
43
+ @entities = []
44
+ @players = []
45
+ @asserts = {}
44
46
  @default_scene = :active
45
47
  post_initialize
46
48
  end
49
+
50
+ def scenes
51
+ if @scenes.nil?
52
+ @scenes = {}
53
+ @scenes[:active] = Scene::Active.new
54
+ @scenes[:concluded] = Scene::Conclusion.new
55
+ end
56
+ @scenes
57
+ end
58
+
59
+ def concluded?(actor)
60
+ scenes[actor.scene].kind_of?(Scene::Conclusion)
61
+ end
47
62
 
48
63
  # Get an Array of all Actions defined in the Plot.
49
64
  #
@@ -158,7 +173,7 @@ module Gamefic
158
173
  # by the plot, which would be :active by default. We could
159
174
  # get it like player.cue nil.
160
175
  if player.scene.nil?
161
- cue player, default_scene
176
+ player.cue :active
162
177
  ready
163
178
  update
164
179
  end
@@ -175,7 +190,6 @@ module Gamefic
175
190
  @player_ready.each { |block|
176
191
  block.call player
177
192
  }
178
- player.scene.start player
179
193
  }
180
194
  end
181
195
 
@@ -184,14 +198,24 @@ module Gamefic
184
198
  def update
185
199
  # Update the plot.
186
200
  @players.each { |player|
187
- process_input player
201
+ # TODO: This really doesn't belong here. We need a before_update in the plot.
202
+ @before_player_update_procs.each { |p|
203
+ p.call player
204
+ }
205
+ this_scene = player.next_scene || player.scene
206
+ player.prepare nil
207
+ if this_scene != player.scene
208
+ player.cue this_scene
209
+ player.queue.shift
210
+ else
211
+ process_input player
212
+ end
188
213
  }
189
214
  @entities.each { |e|
190
215
  e.update
191
216
  }
192
217
  @players.each { |player|
193
218
  update_player player
194
- cue player, player.scene.data.next_cue if !player.scene.data.next_cue.nil?
195
219
  }
196
220
  @update_procs.each { |p|
197
221
  p.call
@@ -242,22 +266,25 @@ module Gamefic
242
266
  @player_procs.push block
243
267
  end
244
268
 
269
+ # Add a block to be executed for each player before the turn's update is
270
+ # performed.
271
+ #
272
+ # @yieldparam [Character]
273
+ def before_player_update &block
274
+ @before_player_update_procs.push block
275
+ end
276
+
245
277
  private
246
278
  def process_input player
247
279
  line = player.queue.shift
248
280
  if !line.nil?
249
- player.scene.finish player, line
250
- #cue player, player.scene.data.next_cue if !player.scene.data.next_cue.nil?
281
+ scenes[player.scene].finish player, line
251
282
  end
252
283
  end
253
284
  def update_player player
254
285
  @player_procs.each { |proc|
255
286
  proc.call player
256
287
  }
257
- # HACK Exception for running tests
258
- if player[:testing] == true
259
- cue player, :test
260
- end
261
288
  end
262
289
  def rem_entity(entity)
263
290
  @entities.delete(entity)
@@ -16,6 +16,8 @@ module Gamefic::Query
16
16
  @arguments = arguments
17
17
  @match_hash = Hash.new
18
18
  end
19
+ # Check whether the query allows ambiguous matches.
20
+ # If allowed, this query's
19
21
  def allow_ambiguous?
20
22
  false
21
23
  end
@@ -110,7 +112,7 @@ module Gamefic::Query
110
112
  arguments.each { |a|
111
113
  if a.kind_of?(Class) or a.kind_of?(Module)
112
114
  my_classes.push a
113
- elsif a.kind_of?(Entity)
115
+ elsif a.kind_of?(Gamefic::Entity)
114
116
  my_objects.push a
115
117
  elsif a.kind_of?(Symbol)
116
118
  if my_classes.length == 0 and my_objects.length == 0
@@ -1,25 +1,18 @@
1
1
  module Gamefic
2
2
 
3
- class ActiveSceneManager < SceneManager
4
- def scene_class
5
- ActiveScene
3
+ # Active Scenes handle the default command prompt, where input is parsed
4
+ # into an Action performed by the Character. This is the default scene in
5
+ # a Plot.
6
+ #
7
+ class Scene::Active < Scene::Base
8
+ def start actor
9
+ # TODO Anything necessary here?
6
10
  end
7
- def state
8
- @state ||= "Active"
9
- end
10
- end
11
-
12
- class ActiveScene < Scene
13
11
  def finish actor, input
14
- @data.input = input
15
- if @finish.nil?
16
- last_order = actor.perform data.input
17
- # HACK Set the last_order here so inline performs don't set it
18
- actor.send(:last_order=, last_order)
19
- else
20
- @finish.call actor, data
21
- end
12
+ last_order = actor.perform input
13
+ # HACK Set the last_order here so inline performs don't set it
14
+ actor.send(:last_order=, last_order)
22
15
  end
23
16
  end
24
-
17
+
25
18
  end
@@ -0,0 +1,21 @@
1
+ module Gamefic::Scene
2
+
3
+ # The Base Scene is not intended for instantiation. Other Scene classes
4
+ # should inherit from it.
5
+ #
6
+ class Base
7
+ def prompt
8
+ @prompt ||= '>'
9
+ end
10
+ def start actor
11
+
12
+ end
13
+ def finish actor, input
14
+
15
+ end
16
+ def state
17
+ self.class.to_s.split('::').last
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,11 @@
1
+ module Gamefic
2
+
3
+ # A Conclusion ends the Plot (or the character's participation in it).
4
+ #
5
+ class Scene::Conclusion < Scene::Custom
6
+ def initialize &block
7
+ @start = block
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,21 @@
1
+ module Gamefic
2
+
3
+ # A Custom Scene is a generic scene that allows for complete configuration
4
+ # of its behavior upon instantiation. It is suitable for direct instantion
5
+ # or extension by other Scene classes.
6
+ #
7
+ class Scene::Custom < Scene::Base
8
+ def initialize config = {}
9
+ @start = config[:start]
10
+ @finish = config[:finish]
11
+ @prompt = config[:prompt]
12
+ end
13
+ def start actor
14
+ @start.call(actor) unless @start.nil?
15
+ end
16
+ def finish actor, input
17
+ @finish.call(actor, input) unless @finish.nil?
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,11 @@
1
+ module Gamefic
2
+ class Gamefic::Scene::MultipleChoice::Input
3
+ attr_reader :raw, :number, :index, :choice
4
+ def initialize raw, index, choice
5
+ @raw = raw
6
+ @index = index
7
+ @number = index + 1
8
+ @choice = choice
9
+ end
10
+ end
11
+ end