gamefic 1.7.0 → 2.0.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 (99) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +13 -0
  5. data/.solargraph.yml +5 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +20 -0
  8. data/README.md +28 -0
  9. data/Rakefile +10 -0
  10. data/gamefic.gemspec +27 -0
  11. data/lib/gamefic.rb +7 -6
  12. data/lib/gamefic/action.rb +38 -28
  13. data/lib/gamefic/active.rb +325 -280
  14. data/lib/gamefic/actor.rb +8 -5
  15. data/lib/gamefic/command.rb +9 -7
  16. data/lib/gamefic/core_ext/array.rb +24 -49
  17. data/lib/gamefic/core_ext/string.rb +25 -16
  18. data/lib/gamefic/describable.rb +21 -23
  19. data/lib/gamefic/element.rb +43 -31
  20. data/lib/gamefic/entity.rb +6 -12
  21. data/lib/gamefic/index.rb +121 -0
  22. data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
  23. data/lib/gamefic/messaging.rb +43 -44
  24. data/lib/gamefic/node.rb +14 -5
  25. data/lib/gamefic/plot.rb +69 -89
  26. data/lib/gamefic/plot/darkroom.rb +92 -264
  27. data/lib/gamefic/plot/host.rb +42 -48
  28. data/lib/gamefic/plot/snapshot.rb +5 -18
  29. data/lib/gamefic/query.rb +14 -18
  30. data/lib/gamefic/query/base.rb +30 -18
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/external.rb +18 -14
  33. data/lib/gamefic/query/family.rb +1 -7
  34. data/lib/gamefic/query/matches.rb +75 -67
  35. data/lib/gamefic/query/parent.rb +0 -0
  36. data/lib/gamefic/query/siblings.rb +0 -0
  37. data/lib/gamefic/query/text.rb +2 -1
  38. data/lib/gamefic/scene.rb +0 -2
  39. data/lib/gamefic/scene/activity.rb +24 -26
  40. data/lib/gamefic/scene/base.rb +64 -8
  41. data/lib/gamefic/scene/conclusion.rb +0 -2
  42. data/lib/gamefic/scene/custom.rb +0 -2
  43. data/lib/gamefic/scene/multiple_choice.rb +18 -3
  44. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  45. data/lib/gamefic/scene/pause.rb +7 -2
  46. data/lib/gamefic/scene/yes_or_no.rb +21 -9
  47. data/lib/gamefic/scriptable.rb +87 -0
  48. data/lib/gamefic/serialize.rb +68 -0
  49. data/lib/gamefic/subplot.rb +29 -35
  50. data/lib/gamefic/syntax.rb +14 -13
  51. data/lib/gamefic/version.rb +3 -3
  52. data/lib/gamefic/world.rb +16 -0
  53. data/lib/gamefic/world/callbacks.rb +135 -0
  54. data/lib/gamefic/world/commands.rb +184 -0
  55. data/lib/gamefic/{plot → world}/entities.rb +30 -29
  56. data/lib/gamefic/{plot → world}/playbook.rb +255 -240
  57. data/lib/gamefic/world/players.rb +21 -0
  58. data/lib/gamefic/world/scenes.rb +226 -0
  59. metadata +41 -92
  60. data/bin/gamefic +0 -9
  61. data/lib/gamefic/engine.rb +0 -7
  62. data/lib/gamefic/engine/base.rb +0 -59
  63. data/lib/gamefic/engine/tty.rb +0 -24
  64. data/lib/gamefic/grammar.rb +0 -13
  65. data/lib/gamefic/grammar/conjugator.rb +0 -20
  66. data/lib/gamefic/grammar/gender.rb +0 -11
  67. data/lib/gamefic/grammar/person.rb +0 -10
  68. data/lib/gamefic/grammar/plural.rb +0 -13
  69. data/lib/gamefic/grammar/pronouns.rb +0 -106
  70. data/lib/gamefic/grammar/tense.rb +0 -6
  71. data/lib/gamefic/grammar/verb_set.rb +0 -43
  72. data/lib/gamefic/grammar/verbs.rb +0 -26
  73. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  74. data/lib/gamefic/plot/articles.rb +0 -22
  75. data/lib/gamefic/plot/callbacks.rb +0 -126
  76. data/lib/gamefic/plot/commands.rb +0 -120
  77. data/lib/gamefic/plot/players.rb +0 -15
  78. data/lib/gamefic/plot/scenes.rb +0 -187
  79. data/lib/gamefic/plot/theater.rb +0 -73
  80. data/lib/gamefic/plot/you_mount.rb +0 -22
  81. data/lib/gamefic/script.rb +0 -13
  82. data/lib/gamefic/script/base.rb +0 -42
  83. data/lib/gamefic/script/file.rb +0 -14
  84. data/lib/gamefic/script/text.rb +0 -14
  85. data/lib/gamefic/shell.rb +0 -76
  86. data/lib/gamefic/source.rb +0 -14
  87. data/lib/gamefic/source/base.rb +0 -12
  88. data/lib/gamefic/source/file.rb +0 -23
  89. data/lib/gamefic/source/text.rb +0 -16
  90. data/lib/gamefic/tester.rb +0 -19
  91. data/lib/gamefic/text.rb +0 -8
  92. data/lib/gamefic/text/ansi.rb +0 -53
  93. data/lib/gamefic/text/html.rb +0 -68
  94. data/lib/gamefic/text/html/conversions.rb +0 -250
  95. data/lib/gamefic/text/html/entities.rb +0 -9
  96. data/lib/gamefic/tty.rb +0 -10
  97. data/lib/gamefic/user.rb +0 -7
  98. data/lib/gamefic/user/base.rb +0 -29
  99. data/lib/gamefic/user/tty.rb +0 -38
@@ -1,264 +1,92 @@
1
- module Gamefic
2
- # Create and restore plot snapshots.
3
- #
4
- class Plot::Darkroom
5
- attr_reader :plot
6
-
7
- def initialize plot
8
- @plot = plot
9
- end
10
-
11
- # Create a snapshot of the plot.
12
- #
13
- # @return [Hash]
14
- def save
15
- result = { entities: [], players: [], subplots: [], instance_variables: {} }
16
- entity_store.clear
17
- player_store.clear
18
- entity_store.concat plot.entities
19
- player_store.concat plot.players
20
- plot.subplots.each { |s| entity_store.concat s.entities }
21
- entity_store.uniq!
22
- entity_store.each do |e|
23
- result[:entities].push hash_entity(e)
24
- end
25
- player_store.each do |p|
26
- result[:players].push hash_entity(p)
27
- end
28
- plot.theater.instance_variables.each { |i|
29
- v = plot.theater.instance_variable_get(i)
30
- result[:instance_variables][i] = serialize(v) if can_serialize?(v)
31
- }
32
- plot.subplots.each { |s|
33
- result[:subplots].push hash_subplot(s)
34
- }
35
- result
36
- end
37
-
38
- # Restore a snapshot.
39
- #
40
- def restore snapshot
41
- entity_store.clear
42
- player_store.clear
43
- plot.subplots.each { |s| s.conclude }
44
- plot.entities[plot.initial_state[:entities].length..-1].each { |e| plot.destroy e }
45
- entity_store.concat plot.entities[0..plot.initial_state[:entities].length-1]
46
- entity_store.uniq!
47
- player_store.concat plot.players
48
- i = 0
49
- snapshot[:entities].each { |h|
50
- if entity_store[i].nil?
51
- e = plot.stage do
52
- cls = self.const_get(h[:class])
53
- make cls
54
- end
55
- entity_store.push e
56
- end
57
- i += 1
58
- }
59
- snapshot[:subplots].each { |s|
60
- sp = plot.stage do
61
- cls = const_get(s[:class])
62
- branch cls
63
- end
64
- # @todo Assuming one player
65
- sp.introduce player_store[0] unless player_store.empty?
66
- rebuild_subplot sp, s
67
- }
68
- i = 0
69
- snapshot[:entities].each { |h|
70
- rebuild1 entity_store[i], h
71
- i += 1
72
- }
73
- i = 0
74
- snapshot[:players].each { |p|
75
- rebuild1 player_store[i], p
76
- i += 1
77
- }
78
- i = 0
79
- snapshot[:entities].each { |h|
80
- rebuild2 entity_store[i], h
81
- i += 1
82
- }
83
- i = 0
84
- snapshot[:players].each { |h|
85
- rebuild2 player_store[i], h
86
- i += 1
87
- }
88
- snapshot[:instance_variables].each_pair { |k, v|
89
- plot.theater.instance_variable_set(k, unserialize(v))
90
- }
91
- end
92
-
93
- private
94
-
95
- def hash_blacklist
96
- [:@parent, :@children, :@last_action, :@scene, :@next_scene, :@playbook, :@performance_stack, :@buffer_stack, :@messages, :@state]
97
- end
98
-
99
- def can_serialize? v
100
- return true if v.kind_of?(String) or v.kind_of?(Numeric) or v.kind_of?(Symbol) or v.kind_of?(Gamefic::Entity) or is_scene_class?(v) or v == true or v == false or v.nil?
101
- if v.kind_of?(Array)
102
- v.each do |e|
103
- result = can_serialize?(e)
104
- return false if result == false
105
- end
106
- true
107
- elsif v.kind_of?(Hash)
108
- v.each_pair do |k, v|
109
- result = can_serialize?(k)
110
- return false if result == false
111
- result = can_serialize?(v)
112
- return false if result == false
113
- end
114
- true
115
- else
116
- false
117
- end
118
- end
119
-
120
- def is_scene_class?(v)
121
- if v.kind_of?(Class)
122
- s = v
123
- until s.nil?
124
- return true if s == Gamefic::Scene::Base
125
- s = s.superclass
126
- end
127
- false
128
- else
129
- false
130
- end
131
- end
132
-
133
- def serialize v
134
- if v.kind_of?(Array)
135
- result = []
136
- v.each do |e|
137
- result.push serialize(e)
138
- end
139
- result
140
- elsif v.kind_of?(Hash)
141
- result = {}
142
- v.each_pair do |k, v|
143
- result[serialize(k)] = serialize(v)
144
- end
145
- result
146
- elsif is_scene_class?(v)
147
- i = plot.scene_classes.index(v)
148
- "#<SIN_#{i}>"
149
- elsif v.kind_of?(Gamefic::Entity)
150
- i = entity_store.index(v)
151
- if i.nil?
152
- i = player_store.index(v)
153
- if i.nil?
154
- raise "#{v} not found in plot"
155
- nil
156
- else
157
- "#<PIN_#{i}>"
158
- end
159
- else
160
- "#<EIN_#{i}>"
161
- end
162
- else
163
- v
164
- end
165
- end
166
-
167
- def unserialize v
168
- if v.kind_of?(Array)
169
- result = []
170
- v.each do |e|
171
- result.push unserialize(e)
172
- end
173
- result
174
- elsif v.kind_of?(Hash)
175
- result = {}
176
- v.each_pair do |k, v|
177
- result[unserialize(k)] = unserialize(v)
178
- end
179
- result
180
- elsif v.kind_of?(String)
181
- if m = v.match(/#<SIN_([0-9]+)>/)
182
- plot.scene_classes[m[1].to_i]
183
- elsif m = v.match(/#<EIN_([0-9]+)>/)
184
- entity_store[m[1].to_i]
185
- elsif m = v.match(/#<PIN_([0-9]+)>/)
186
- player_store[m[1].to_i]
187
- else
188
- v
189
- end
190
- else
191
- v
192
- end
193
- end
194
-
195
- def rebuild1 e, h
196
- h.each_pair do |k, v|
197
- if k.to_s.start_with?('@')
198
- e.instance_variable_set(k, unserialize(v))
199
- end
200
- end
201
- end
202
-
203
- def rebuild2 e, h
204
- h.each_pair do |k, v|
205
- if k.to_s != 'class' and !k.to_s.start_with?('@')
206
- e.send("#{k}=", unserialize(v))
207
- end
208
- end
209
- end
210
-
211
- def hash_subplot s
212
- result = { entities: [], instance_variables: {}, theater_instance_variables: {} }
213
- s.instance_variables.each { |i|
214
- v = s.instance_variable_get(i)
215
- result[:instance_variables][i] = serialize(v) if can_serialize?(v)
216
- }
217
- s.theater.instance_variables.each { |i|
218
- v = s.theater.instance_variable_get(i)
219
- result[:theater_instance_variables][i] = serialize(v) if can_serialize?(v)
220
- }
221
- s.entities.each { |s|
222
- result[:entities].push serialize(s)
223
- }
224
- result[:class] = s.class.to_s.split('::').last
225
- result
226
- end
227
-
228
- def rebuild_subplot s, h
229
- s.entities.each { |e|
230
- s.destroy e
231
- }
232
- h[:instance_variables].each_pair { |k, v|
233
- s.instance_variable_set(k, unserialize(v))
234
- }
235
- h[:theater_instance_variables].each_pair { |k, v|
236
- s.theater.instance_variable_set(k, unserialize(v))
237
- }
238
- i = 0
239
- h[:entities].each { |e|
240
- s.add_entity unserialize(e)
241
- i += 1
242
- }
243
- end
244
-
245
- def entity_store
246
- @entity_store ||= []
247
- end
248
-
249
- def player_store
250
- @player_store ||= []
251
- end
252
-
253
- def hash_entity e
254
- h = {}
255
- e.instance_variables.each { |i|
256
- v = e.instance_variable_get(i)
257
- h[i] = serialize(v) unless hash_blacklist.include?(i) or !can_serialize?(v)
258
- }
259
- h[:class] = e.class.to_s.split('::').last
260
- h[:parent] = serialize(e.parent)
261
- h
262
- end
263
- end
264
- end
1
+ module Gamefic
2
+ # Create and restore plot snapshots.
3
+ #
4
+ class Plot
5
+ class Darkroom
6
+ # @return [Gamefic::Plot]
7
+ attr_reader :plot
8
+
9
+ def initialize plot
10
+ @plot = plot
11
+ end
12
+
13
+ # Create a snapshot of the plot.
14
+ #
15
+ # @return [Hash]
16
+ def save reduce: false
17
+ result = {
18
+ 'elements' => Gamefic::Index.serials,
19
+ 'entities' => plot.entities.map(&:to_serial),
20
+ 'players' => plot.players.map(&:to_serial),
21
+ 'theater_instance_variables' => plot.theater.serialize_instance_variables,
22
+ 'subplots' => plot.subplots.reject(&:concluded?).map { |s| serialize_subplot(s) },
23
+ 'metadata' => plot.metadata
24
+ }
25
+ end
26
+
27
+ # Restore a snapshot.
28
+ #
29
+ # @param snapshot [Hash]
30
+ def restore snapshot
31
+ Gamefic::Index.elements.map(&:destroy)
32
+ Gamefic::Index.unserialize snapshot['elements']
33
+ plot.entities.clear
34
+ snapshot['entities'].each do |ser|
35
+ plot.entities.push Index.from_serial(ser)
36
+ end
37
+
38
+ snapshot['theater_instance_variables'].each_pair do |k, s|
39
+ v = Gamefic::Index.from_serial(s)
40
+ next if v == "#<UNKNOWN>"
41
+ plot.theater.instance_variable_set(k, v)
42
+ end
43
+
44
+ snapshot['subplots'].each { |s| unserialize_subplot(s) }
45
+ end
46
+
47
+ private
48
+
49
+ def namespace_to_constant string
50
+ space = Object
51
+ string.split('::').each do |part|
52
+ space = space.const_get(part)
53
+ end
54
+ space
55
+ end
56
+
57
+ def serialize_subplot s
58
+ {
59
+ 'class' => s.class.to_s,
60
+ 'entities' => s.entities.map(&:to_serial),
61
+ 'instance_variables' => s.serialize_instance_variables,
62
+ 'theater_instance_variables' => s.theater.serialize_instance_variables
63
+ }
64
+ end
65
+
66
+ def unserialize_subplot s
67
+ cls = namespace_to_constant(s['class'])
68
+ sp = cls.allocate
69
+ sp.instance_variable_set(:@plot, plot)
70
+ s['entities'].each do |e|
71
+ sp.entities.push Gamefic::Index.from_serial(e)
72
+ end
73
+ s['instance_variables'].each_pair do |k, v|
74
+ next if v == "#<UNKNOWN>"
75
+ sp.instance_variable_set(k, Gamefic::Index.from_serial(v))
76
+ end
77
+ s['theater_instance_variables'].each_pair do |k, v|
78
+ next if v == "#<UNKNOWN>"
79
+ sp.theater.instance_variable_set(k, Gamefic::Index.from_serial(v))
80
+ end
81
+ plot.subplots.push sp
82
+ sp.send(:run_scripts)
83
+ # @todo Assuming one player
84
+ if plot.players.first
85
+ sp.players.push plot.players.first
86
+ plot.players.first.playbooks.push sp.playbook unless plot.players.first.playbooks.include?(sp.playbook)
87
+ end
88
+ sp
89
+ end
90
+ end
91
+ end
92
+ end
@@ -1,48 +1,42 @@
1
- require 'gamefic/subplot'
2
-
3
- module Gamefic
4
-
5
- module Plot::Host
6
- # Get an array of all the current subplots.
7
- #
8
- # @return [Array<Subplot>]
9
- def subplots
10
- p_subplots.clone
11
- end
12
-
13
- # Start a new subplot based on the provided class.
14
- #
15
- # @param subplot_class [Class] The class of the subplot to be created (Subplot by default)
16
- # @return [Subplot]
17
- def branch subplot_class = Gamefic::Subplot, introduce: nil, next_cue: nil
18
- subplot = subplot_class.new(self, introduce: introduce, next_cue: next_cue)
19
- p_subplots.push subplot
20
- subplot
21
- end
22
-
23
- # Get the player's current subplots.
24
- #
25
- # @return [Array<Subplot>]
26
- def subplots_featuring player
27
- result = []
28
- subplots.each { |s|
29
- result.push s if s.players.include?(player)
30
- }
31
- result
32
- end
33
-
34
- # Determine whether the player is involved in a subplot.
35
- #
36
- # @return [Boolean]
37
- def in_subplot? player
38
- !subplots_featuring(player).empty?
39
- end
40
-
41
- private
42
-
43
- def p_subplots
44
- @p_subplots ||= []
45
- end
46
- end
47
-
48
- end
1
+ require 'gamefic/subplot'
2
+
3
+ module Gamefic
4
+ # Methods for hosting and managing subplots.
5
+ #
6
+ module Plot::Host
7
+ # Get an array of all the current subplots.
8
+ #
9
+ # @return [Array<Subplot>]
10
+ def subplots
11
+ @subplots ||= []
12
+ end
13
+
14
+ # Start a new subplot based on the provided class.
15
+ #
16
+ # @param subplot_class [Class<Gamefic::Subplot>] The class of the subplot to be created (Subplot by default)
17
+ # @return [Gamefic::Subplot]
18
+ def branch subplot_class = Gamefic::Subplot, introduce: nil, next_cue: nil, **more
19
+ subplot = subplot_class.new(self, introduce: introduce, next_cue: next_cue, **more)
20
+ subplots.push subplot
21
+ subplot
22
+ end
23
+
24
+ # Get the player's current subplots.
25
+ #
26
+ # @return [Array<Subplot>]
27
+ def subplots_featuring player
28
+ result = []
29
+ subplots.each { |s|
30
+ result.push s if s.players.include?(player)
31
+ }
32
+ result
33
+ end
34
+
35
+ # Determine whether the player is involved in a subplot.
36
+ #
37
+ # @return [Boolean]
38
+ def in_subplot? player
39
+ !subplots_featuring(player).empty?
40
+ end
41
+ end
42
+ end
@@ -4,29 +4,16 @@ module Gamefic
4
4
  module Plot::Snapshot
5
5
  # @return [Hash]
6
6
  def save
7
- initial_state
8
- internal_save
7
+ Gamefic::Plot::Darkroom.new(self).save
9
8
  end
10
9
 
11
10
  def restore snapshot
12
- # HACK Force conclusion of current subplots
13
- p_subplots.each { |s| s.conclude }
14
- p_subplots.clear
11
+ snapshot = JSON.parse(snapshot, symbolize_names: false) if snapshot.is_a?(String)
12
+ # HACK: Force conclusion of current subplots
13
+ subplots.each { |s| s.conclude }
14
+ subplots.clear
15
15
  Gamefic::Plot::Darkroom.new(self).restore(snapshot)
16
16
  entities.each { |e| e.flush }
17
17
  end
18
-
19
- def initial_state
20
- if @initial_state.nil?
21
- @initial_state = internal_save
22
- end
23
- @initial_state
24
- end
25
-
26
- private
27
-
28
- def internal_save
29
- Gamefic::Plot::Darkroom.new(self).save
30
- end
31
18
  end
32
19
  end