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
@@ -0,0 +1,15 @@
1
+ module Gamefic
2
+
3
+ module Plot::Players
4
+ def players
5
+ p_players.clone
6
+ end
7
+
8
+ private
9
+
10
+ def p_players
11
+ @p_players ||= []
12
+ end
13
+ end
14
+
15
+ end
@@ -1,20 +1,50 @@
1
- class NotConclusionError < Exception
2
- end
3
-
4
1
  module Gamefic
5
2
 
6
3
  module Plot::SceneMount
4
+ def default_scene
5
+ @default_scene ||= Scene::Active.new(self)
6
+ end
7
+
8
+ def default_conclusion
9
+ @default_conclusion ||= Scene::Conclusion.new
10
+ end
11
+
12
+ # Add a block to be executed when a player is added to the game.
13
+ # Each Plot can only have one introduction. Subsequent calls will
14
+ # overwrite the existing one.
15
+ #
16
+ # @example Welcome the player to the game
17
+ # introduction do |actor|
18
+ # actor.tell "Welcome to the game!"
19
+ # end
20
+ #
21
+ # @yieldparam [Character]
22
+ def introduction (&proc)
23
+ @introduction = proc
24
+ end
25
+
26
+ # Introduce a player to the game.
27
+ # This method is typically called by the Engine that manages game execution.
28
+ def introduce(player)
29
+ player.playbook = playbook
30
+ player.cue default_scene
31
+ p_players.push player
32
+ @introduction.call(player) unless @introduction.nil?
33
+ end
34
+
7
35
  # Create a multiple-choice scene.
8
36
  # The user will be required to make a valid choice to continue.
9
37
  #
10
38
  # @yieldparam [Character]
11
39
  # @yieldparam [Scene::Data::MultipleChoice]
12
- def multiple_choice key, *choices, &block
13
- scenes[key] = Scene::MultipleChoice.new
14
- scenes[key].on_start do |actor, data|
40
+ def multiple_choice *choices, &block
41
+ s = Scene::MultipleChoice.new
42
+ s.on_start do |actor, data|
43
+ data.options.clear
15
44
  data.options.push *choices
16
45
  end
17
- scenes[key].on_finish &block
46
+ s.on_finish &block
47
+ s
18
48
  end
19
49
 
20
50
  # Create a yes-or-no scene.
@@ -22,22 +52,27 @@ module Gamefic
22
52
  #
23
53
  # @yieldparam [Character]
24
54
  # @yieldparam [String] "yes" or "no"
25
- def yes_or_no key, prompt = nil, &block
26
- scenes[key] = Scene::YesOrNo.new
55
+ def yes_or_no prompt = nil, &block
56
+ s = Scene::YesOrNo.new
27
57
  unless prompt.nil?
28
- scenes[key].on_start do |actor, data|
58
+ s.on_start do |actor, data|
29
59
  data.prompt = prompt
30
60
  end
31
61
  end
32
- scenes[key].on_finish &block
62
+ s.on_finish do |actor, data|
63
+ block.call actor, data unless block.nil?
64
+ actor.cue default_scene if actor.scene == s and actor.next_scene.nil?
65
+ end
66
+ s
33
67
  end
34
68
 
35
- def question key, prompt = 'What is your answer?', &block
36
- scenes[key] = Scene::Custom.new
37
- scenes[key].on_start do |actor, data|
69
+ def question prompt = 'What is your answer?', &block
70
+ s = Scene::Custom.new
71
+ s.on_start do |actor, data|
38
72
  data.prompt = prompt
39
73
  end
40
- scenes[key].on_finish &block
74
+ s.on_finish &block
75
+ s
41
76
  end
42
77
 
43
78
  # Create a scene that pauses the game.
@@ -48,15 +83,17 @@ module Gamefic
48
83
  # @param prompt [String] The text to display when prompting the user to continue.
49
84
  # @yieldparam [Character]
50
85
  # @yieldparam [Scene::Data::Base]
51
- def pause key, prompt = nil, &block
52
- scenes[key] = Scene::Pause.new
53
- scenes[key].on_start do |actor, data|
86
+ def pause prompt = nil, &block
87
+ s = Scene::Pause.new
88
+ s.on_start do |actor, data|
54
89
  data.prompt = prompt unless prompt.nil?
55
90
  block.call actor, data unless block.nil?
56
91
  end
57
- scenes[key].on_finish do |actor, data|
58
- actor.cue :active if actor.scene == key and actor.next_scene.nil?
92
+ s.on_finish do |actor, data|
93
+ #actor.cue :active if actor.scene == key and actor.next_scene.nil?
94
+ actor.cue default_scene if actor.scene == s and actor.next_scene.nil?
59
95
  end
96
+ s
60
97
  end
61
98
 
62
99
  # Create a conclusion.
@@ -66,9 +103,10 @@ module Gamefic
66
103
  # @param key [Symbol] A unique name for the scene.
67
104
  # @yieldparam [Character]
68
105
  # @yieldparam [Scene::Data::Base]
69
- def conclusion key, &block
70
- scenes[key] = Scene::Conclusion.new
71
- scenes[key].on_start &block
106
+ def conclusion &block
107
+ s = Scene::Conclusion.new
108
+ s.on_start &block
109
+ s
72
110
  end
73
111
 
74
112
  # Create a custom scene.
@@ -106,10 +144,10 @@ module Gamefic
106
144
  # @param key [Symbol] A unique name for the scene.
107
145
  # @param key [cls] The class of scene to be instantiated.
108
146
  # @yieldparam [Scene::Custom] The instantiated scene.
109
- def scene key, cls = Scene::Custom, &block
110
- scenes[key] = cls.new
111
- #block.call scenes[key]
112
- yield scenes[key] if block_given?
147
+ def scene cls = Scene::Custom, &block
148
+ s = cls.new
149
+ yield s if block_given?
150
+ s
113
151
  end
114
152
 
115
153
  # Choose a new scene based on a list of options.
@@ -130,15 +168,15 @@ module Gamefic
130
168
  #
131
169
  # @param key [Symbol] A unique name for the scene.
132
170
  # @param map [Hash] A Hash of options and associated scene keys.
133
- def multiple_scene key, map
134
- scenes[key] = Scene::MultipleScene.new
135
- scenes[key].on_start do |actor, data|
171
+ def multiple_scene map
172
+ s = Scene::MultipleScene.new
173
+ s.on_start do |actor, data|
136
174
  map.each { |k, v|
137
175
  data.map k, v
138
176
  }
139
177
  end
178
+ s
140
179
  end
141
-
142
180
  end
143
181
 
144
182
  end
@@ -38,10 +38,10 @@ module Gamefic
38
38
  # Restore the plot to the state of its first snapshot.
39
39
  #
40
40
  def restore_initial_state
41
- @entities[@initial_state.length..-1].each { |e|
41
+ p_entities[@initial_state.length..-1].each { |e|
42
42
  e.parent = nil
43
43
  }
44
- @entities.slice! @initial_state.length..-1
44
+ p_entities.slice! @initial_state.length..-1
45
45
  internal_restore @initial_state
46
46
  end
47
47
 
@@ -86,13 +86,14 @@ module Gamefic
86
86
  snapshot.each { |hash|
87
87
  if entities[index].nil?
88
88
  cls = Kernel.const_get(hash[:class])
89
- @entities[index] = make cls
89
+ p_entities[index] = make cls
90
90
  end
91
91
  internal_restore_hash hash, index
92
92
  index += 1
93
93
  }
94
94
  nil
95
95
  end
96
+
96
97
  def internal_restore_hash hash, index
97
98
  hash.each { |k, v|
98
99
  if k == :scene
@@ -108,6 +109,7 @@ module Gamefic
108
109
  }
109
110
  nil
110
111
  end
112
+
111
113
  def reduce entities
112
114
  reduced = []
113
115
  index = 0
@@ -123,6 +125,7 @@ module Gamefic
123
125
  }
124
126
  reduced
125
127
  end
128
+
126
129
  def can_serialize? obj
127
130
  return true if (obj == true or obj == false or obj.nil?)
128
131
  allowed = [String, Fixnum, Float, Numeric, Entity, Direction, Hash, Array, Symbol]
@@ -131,6 +134,7 @@ module Gamefic
131
134
  }
132
135
  false
133
136
  end
137
+
134
138
  def serialize_obj obj
135
139
  return nil if obj.nil?
136
140
  return false if obj == false
@@ -140,13 +144,14 @@ module Gamefic
140
144
  return serialize_array obj
141
145
  else
142
146
  if obj.kind_of?(Entity)
143
- return "#<EIN_#{@entities.index(obj)}>"
147
+ return "#<EIN_#{p_entities.index(obj)}>"
144
148
  elsif obj.kind_of?(Direction)
145
149
  return "#<DIR_#{obj.name}>"
146
150
  end
147
151
  end
148
152
  return obj
149
153
  end
154
+
150
155
  def serialize_hash obj
151
156
  hash = {}
152
157
  obj.each_pair { |k, v|
@@ -156,6 +161,7 @@ module Gamefic
156
161
  }
157
162
  return hash
158
163
  end
164
+
159
165
  def serialize_array obj
160
166
  arr = []
161
167
  obj.each_index { |i|
@@ -167,6 +173,7 @@ module Gamefic
167
173
  }
168
174
  return arr
169
175
  end
176
+
170
177
  def unserialize obj
171
178
  if obj.kind_of?(Hash)
172
179
  unserialize_hash obj
@@ -174,13 +181,14 @@ module Gamefic
174
181
  unserialize_array obj
175
182
  elsif obj.to_s.match(/^#<EIN_[0-9]+>$/)
176
183
  i = obj[6..-2].to_i
177
- @entities[i]
184
+ p_entities[i]
178
185
  elsif obj.to_s.match(/^#<DIR_[a-z]+>$/)
179
186
  Direction.find(obj[6..-2])
180
187
  else
181
188
  obj
182
189
  end
183
190
  end
191
+
184
192
  def unserialize_hash obj
185
193
  hash = {}
186
194
  obj.each_pair { |k, v|
@@ -188,6 +196,7 @@ module Gamefic
188
196
  }
189
197
  hash
190
198
  end
199
+
191
200
  def unserialize_array obj
192
201
  arr = []
193
202
  obj.each_index { |i|
@@ -195,7 +204,10 @@ module Gamefic
195
204
  }
196
205
  arr
197
206
  end
207
+
198
208
  def save_subplots
209
+ # TODO: Subplot snapshots are temporarily disabled.
210
+ return []
199
211
  arr = []
200
212
  subplots.each { |s|
201
213
  hash = {}
@@ -210,7 +222,10 @@ module Gamefic
210
222
  }
211
223
  arr
212
224
  end
225
+
213
226
  def restore_subplots arr
227
+ # TODO: Subplot snapshots are temporarily disabled.
228
+ return
214
229
  players.each { |p|
215
230
  p.send(:p_subplots).clear
216
231
  }
@@ -5,8 +5,15 @@ module Gamefic
5
5
  # a Plot.
6
6
  #
7
7
  class Scene::Active < Scene::Base
8
+ attr_reader :plot
9
+
10
+ def initialize plot
11
+ @plot = plot
12
+ end
13
+
8
14
  def finish actor, input
9
- actor.perform input, from_user: true
15
+ o = actor.perform input.strip
16
+ actor.performed o
10
17
  end
11
18
  end
12
19
 
@@ -5,43 +5,21 @@ module Gamefic
5
5
  #
6
6
  class Scene::Base
7
7
  def start actor
8
- start_data_for actor
9
8
  end
10
9
 
11
10
  def finish actor, input
12
- finish_data_for actor, input
13
11
  end
14
12
 
15
- def type
16
- self.class.to_s.split('::').last
17
- end
18
-
19
- def data_class
20
- SceneData::Base
21
- end
22
-
23
13
  # Get the prompt to be displayed to the user when accepting input.
24
14
  #
25
15
  # @return [String] The text to be displayed.
26
16
  def prompt_for actor
27
- character_data[actor].prompt || '>'
17
+ '>'
28
18
  end
29
19
 
30
- private
31
-
32
- def character_data
33
- @character_data ||= {}
34
- end
35
-
36
- def start_data_for actor
37
- character_data[actor] = data_class.new
38
- end
39
-
40
- def finish_data_for actor, input
41
- data = character_data[actor]
42
- data.input = input.strip
43
- data
20
+ def type
21
+ self.class.to_s.split('::').last
44
22
  end
45
23
  end
46
-
24
+
47
25
  end
@@ -1,32 +1,69 @@
1
1
  module Gamefic
2
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 instantiation
5
- # or extension by other Scene classes.
3
+ # A Custom Scene allows for complete configuration of its behavior upon
4
+ # instantiation. It is suitable for direct instantiation or subclassing.
6
5
  #
7
6
  class Scene::Custom < Scene::Base
8
7
  def initialize
9
8
  yield self if block_given?
10
9
  end
10
+
11
+ def data_class
12
+ SceneData::Base
13
+ end
14
+
15
+ # Define a block to be executed at the start of the scene.
16
+ # Unlike on_finish, start blocks may be executed more than once per turn,
17
+ # and more than one scene may be started in a single turn.
18
+ # It always gets executed in a plot's on_ready event and whenever it gets
19
+ # cued. (If the character is already in the scene being cued, on_start
20
+ # does not get repeated.)
21
+ #
22
+ # @yieldparam [Character]
23
+ # @yieldparam [SceneData::Base]
11
24
  def on_start &block
12
25
  @start = block
13
26
  end
14
27
 
28
+ # Define a block to be executed at the end of the scene.
29
+ # The scene data passed to this block will include the character's input
30
+ # for this turn.
31
+ # Unlike on_start, finish only gets executed once per turn, during the
32
+ # plot's on_update event.
33
+ #
34
+ # @yieldparam [Character]
35
+ # @yieldparam [SceneData::Base]
15
36
  def on_finish &block
16
37
  @finish = block
17
38
  end
18
39
 
40
+ # Start the scene.
41
+ # This method typically gets called from the plot during the on_ready
42
+ # event and whenever a character cues a scene.
43
+ #
19
44
  def start actor
20
45
  data = start_data_for(actor)
21
46
  do_start_block actor, data
22
47
  data
23
48
  end
24
49
 
50
+ # End the scene.
51
+ # This method typically gets called from the plot during the on_update
52
+ # event.
53
+ #
25
54
  def finish actor, input
26
55
  data = finish_data_for(actor, input)
27
56
  do_finish_block actor, data
28
57
  end
29
58
 
59
+ # Get the text to be displayed to the user when receiving input.
60
+ #
61
+ # @return [String]
62
+ def prompt_for actor
63
+ return character_data[actor].prompt unless character_data[actor].nil?
64
+ '>'
65
+ end
66
+
30
67
  private
31
68
 
32
69
  def do_start_block actor, data
@@ -37,6 +74,19 @@ module Gamefic
37
74
  @finish.call actor, data unless @finish.nil?
38
75
  end
39
76
 
77
+ def character_data
78
+ @character_data ||= {}
79
+ end
80
+
81
+ def start_data_for actor
82
+ character_data[actor] ||= data_class.new
83
+ end
84
+
85
+ def finish_data_for actor, input
86
+ data = character_data[actor]
87
+ data.input = input.strip
88
+ data
89
+ end
40
90
  end
41
91
 
42
92
  end