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
@@ -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