gamefic 2.0.1 → 2.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04c82a67567327c04056889b9f2b18e8756c917449cad5c5b0fbee780a5086fe
4
- data.tar.gz: 552ca9a8bf3a8d9816158a5472399f0425eb4e5afb918cd41a4f6d72b3501f6b
3
+ metadata.gz: 6ac3f26cc5dcb62f6b4af8f0a365a316a4324605efecac8fd852f9980f00e336
4
+ data.tar.gz: 11348faf9a1c471c11ca4da93f419167db25a92ec7617fd73fb592fdf0ec7d7f
5
5
  SHA512:
6
- metadata.gz: 269912641fca497c003d815918fc7da4797500bdf62367fe21a1630ddcb9b7ad3e7157f8440d0de4e9ffb6107d268b70756e5dd7d0caeda9097f9ca602f2e0f2
7
- data.tar.gz: 4b4a1179eff5209df4204976107b5da6d40368eda34f787fea421891f1d4047f70a78be58f3e07b424ca453220314e0e241142262ae524536125405070af0295
6
+ metadata.gz: 1bd78b541afec9fd2129d785442ddc811dacbc42c41f9c683329776f90ada2e105578324ef73fa7dedd7fc26ed828917f1a796765f0f1b74ade334b54c94d5ab
7
+ data.tar.gz: d3a982efcb6bdd0c306ebdab70ee722fcd1040d6cf51501b738b8bca65e955689dcfe2d77aa5e8684e1f0e18d27b71579410f51a6189d23014da47156bbbe3cf
@@ -0,0 +1,2 @@
1
+ # 2.0.2 - April 25, 2020
2
+ - Improved snapshot serialization
@@ -5,7 +5,7 @@ require 'gamefic/core_ext/array'
5
5
  require 'gamefic/core_ext/string'
6
6
 
7
7
  require 'gamefic/describable'
8
- require 'gamefic/index'
8
+ # require 'gamefic/index'
9
9
  require 'gamefic/serialize'
10
10
  require 'gamefic/element'
11
11
  require 'gamefic/entity'
@@ -17,5 +17,6 @@ require "gamefic/action"
17
17
  require "gamefic/syntax"
18
18
  require 'gamefic/world'
19
19
  require 'gamefic/scriptable'
20
- require "gamefic/plot"
20
+ require 'gamefic/plot'
21
+ require 'gamefic/plot/index'
21
22
  require 'gamefic/subplot'
@@ -7,13 +7,17 @@ module Gamefic
7
7
  #
8
8
  class Element
9
9
  include Gamefic::Describable
10
- include Gamefic::Index
10
+ # include Gamefic::Index
11
+ include Gamefic::Serialize
11
12
 
12
13
  # @todo It would be nice if this initialization wasn't necessary.
13
14
  def initialize(args = {})
14
- super self.class.default_attributes.merge(args)
15
- post_initialize
16
- yield self if block_given?
15
+ # super self.class.default_attributes.merge(args)
16
+ self.class.default_attributes.merge(args).each_pair do |k, v|
17
+ public_send "#{k}=", v
18
+ end
19
+ post_initialize
20
+ yield self if block_given?
17
21
  end
18
22
 
19
23
  def post_initialize
@@ -16,20 +16,23 @@ module Gamefic
16
16
  # @return [Hash]
17
17
  attr_reader :metadata
18
18
 
19
+ attr_reader :static
20
+
19
21
  include World
20
22
  include Scriptable
21
23
  # @!parse extend Scriptable::ClassMethods
22
24
  include Snapshot
23
25
  include Host
26
+ include Serialize
27
+
28
+ exclude_from_serial [:@static]
24
29
 
25
30
  # @param structure [Gamefic::Structure]
26
31
  # @param metadata [Hash]
27
32
  def initialize metadata: {}
28
- Gamefic::Index.clear
29
33
  @metadata = metadata
30
34
  run_scripts
31
- mark_static_entities
32
- Gamefic::Index.stick
35
+ @static = [self] + scene_classes + entities
33
36
  end
34
37
 
35
38
  def player_class cls = nil
@@ -13,14 +13,21 @@ module Gamefic
13
13
  # Create a snapshot of the plot.
14
14
  #
15
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
16
+ def save
17
+ index = plot.static + plot.players
18
+ plot.to_serial(index)
19
+ {
20
+ 'program' => {}, # @todo Metadata for version control, etc.
21
+ 'index' => index.map do |i|
22
+ if i.is_a?(Gamefic::Serialize)
23
+ {
24
+ 'class' => i.class.to_s,
25
+ 'ivars' => i.serialize_instance_variables(index)
26
+ }
27
+ else
28
+ i.to_serial(index)
29
+ end
30
+ end
24
31
  }
25
32
  end
26
33
 
@@ -28,64 +35,45 @@ module Gamefic
28
35
  #
29
36
  # @param snapshot [Hash]
30
37
  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
38
+ # @todo Use `program` for verification
37
39
 
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
40
+ plot.subplots.each(&:conclude)
41
+ plot.subplots.clear
43
42
 
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)
43
+ index = plot.static + plot.players
44
+ snapshot['index'].each_with_index do |obj, idx|
45
+ next if index[idx]
46
+ elematch = obj['class'].match(/^#<ELE_([\d]+)>$/)
47
+ if elematch
48
+ klass = index[elematch[1].to_i]
49
+ else
50
+ klass = Gamefic::Serialize.string_to_constant(obj['class'])
51
+ end
52
+ index.push klass.allocate
53
53
  end
54
- space
55
- end
56
54
 
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)
55
+ snapshot['index'].each_with_index do |obj, idx|
56
+ if index[idx].class.to_s != obj['class']
57
+ STDERR.puts "MISMATCH: #{index[idx].class} is not #{obj['class']}"
58
+ STDERR.puts obj.inspect
59
+ end
60
+ obj['ivars'].each_pair do |k, v|
61
+ next if k == '@subplots'
62
+ uns = v.from_serial(index)
63
+ next if uns == "#<UNKNOWN>"
64
+ index[idx].instance_variable_set(k, uns)
65
+ end
66
+ if index[idx].is_a?(Gamefic::Subplot)
67
+ index[idx].extend Gamefic::Scriptable
68
+ index[idx].instance_variable_set(:@theater, nil)
69
+ index[idx].send(:run_scripts)
70
+ index[idx].players.each do |pl|
71
+ pl.playbooks.push index[idx].playbook unless pl.playbooks.include?(index[idx].playbook)
72
+ end
73
+ index[idx].instance_variable_set(:@static, [index[idx]] + index[idx].scene_classes + index[idx].entities)
74
+ plot.subplots.push index[idx]
75
+ end
87
76
  end
88
- sp
89
77
  end
90
78
  end
91
79
  end
@@ -0,0 +1,92 @@
1
+ module Gamefic
2
+ class Plot
3
+ # A fixed index of plot elements. Plots and subplots use an index to track
4
+ # objects created in scripts.
5
+ #
6
+ class Index
7
+ # @param elements [Array]
8
+ def initialize elements
9
+ @elements = elements.uniq.freeze
10
+ end
11
+
12
+ # @return [Boolean]
13
+ def include? element
14
+ @elements.include?(element)
15
+ end
16
+
17
+ def all
18
+ @elements
19
+ end
20
+
21
+ def concat elements
22
+ @elements = (@elements + elements).uniq.freeze
23
+ end
24
+
25
+ def remove elements
26
+ @elements = (@elements - elements).uniq.freeze
27
+ end
28
+
29
+ # @return [Object]
30
+ def element index
31
+ @elements[index]
32
+ end
33
+
34
+ def id_for(element)
35
+ include?(element) ? "#<ELE_#{@elements.index(element)}>" : nil
36
+ end
37
+
38
+ # def self.from_serial serial, static
39
+ # if serial.is_a?(Hash) && (serial['class'] || serial['element'])
40
+ # if serial['class']
41
+ # elematch = serial['class'].match(/^#<ELE_([\d]+)>$/)
42
+ # if elematch
43
+ # klass = static.element(elematch[1].to_i)
44
+ # else
45
+ # klass = eval(serial['class'])
46
+ # end
47
+ # object = klass.allocate
48
+ # elsif serial['element']
49
+ # object = static.element(serial['element'])
50
+ # end
51
+ # serial.each_pair do |k, v|
52
+ # next unless k.to_s.start_with?('@')
53
+ # object.instance_variable_set(k, from_serial(v, static))
54
+ # end
55
+ # object
56
+ # elsif serial.is_a?(Numeric)
57
+ # serial
58
+ # elsif serial.is_a?(String)
59
+ # match = serial.match(/#<ELE_([0-9]+)>/)
60
+ # return static.element(match[1].to_i) if match
61
+ # match = serial.match(/#<SYM:([a-z0-9_\?\!]+)>/i)
62
+ # return match[1].to_sym if match
63
+ # serial
64
+ # elsif serial.is_a?(Array)
65
+ # result = serial.map { |e| from_serial(e, static) }
66
+ # result = "#<UNKNOWN>" if result.any? { |e| e == "#<UNKNOWN>" }
67
+ # result
68
+ # elsif serial.is_a?(Hash)
69
+ # result = {}
70
+ # unknown = false
71
+ # serial.each_pair do |k, v|
72
+ # k2 = from_serial(k, static)
73
+ # v2 = from_serial(v, static)
74
+ # if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
75
+ # unknown = true
76
+ # break
77
+ # end
78
+ # result[k2] = v2
79
+ # end
80
+ # result = "#<UNKNOWN>" if unknown
81
+ # result
82
+ # elsif serial && serial != true
83
+ # STDERR.puts "Unable to unserialize #{serial.class}"
84
+ # nil
85
+ # else
86
+ # # true, false, or nil
87
+ # serial
88
+ # end
89
+ # end
90
+ end
91
+ end
92
+ end
@@ -2,18 +2,26 @@ require 'json'
2
2
 
3
3
  module Gamefic
4
4
  module Plot::Snapshot
5
+ # Save the current game state as a data hash.
6
+ # See Gamefic::Plot::Darkroom for more information about
7
+ # the data format.
8
+ #
5
9
  # @return [Hash]
6
10
  def save
7
11
  Gamefic::Plot::Darkroom.new(self).save
8
12
  end
9
13
 
14
+ # Restore the game state from a snapshot.
15
+ #
16
+ # If `snapshot` is a string, parse it as a JSON object.
17
+ #
18
+ # @note The string conversion is performed as a convenience for web apps.
19
+ #
20
+ # @param snapshot [Hash, String]
21
+ # @return [void]
10
22
  def restore snapshot
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
23
+ snapshot = JSON.parse(snapshot) if snapshot.is_a?(String)
15
24
  Gamefic::Plot::Darkroom.new(self).restore(snapshot)
16
- entities.each { |e| e.flush }
17
25
  end
18
26
  end
19
27
  end
@@ -3,6 +3,9 @@ module Gamefic
3
3
  # should inherit from it.
4
4
  #
5
5
  class Scene::Base
6
+ include Gamefic::Serialize
7
+ extend Gamefic::Serialize
8
+
6
9
  # The scene's primary actor.
7
10
  #
8
11
  # @return [Gamefic::Actor]
@@ -80,6 +80,7 @@ Gamefic::Scriptable.module_exec do
80
80
  instance.public_send :public_send, symbol, *args, &block
81
81
  end
82
82
  end
83
+ theater.extend Gamefic::Serialize
83
84
  theater
84
85
  end
85
86
  end
@@ -1,15 +1,66 @@
1
1
  module Gamefic
2
2
  module Serialize
3
- def to_serial
4
- {
5
- 'class' => self.class.to_s
6
- }.merge serialize_instance_variables
3
+ def to_serial(index = [])
4
+ if index.include?(self)
5
+ {
6
+ 'instance' => "#<ELE_#{index.index(self)}>",
7
+ 'ivars' => {}
8
+ }
9
+ else
10
+ if self.class == Class && self.name
11
+ {
12
+ 'class' => 'Class',
13
+ 'name' => name
14
+ }
15
+ else
16
+ index.push self if self.is_a?(Gamefic::Serialize)
17
+ {
18
+ 'class' => serialized_class(index),
19
+ 'ivars' => serialize_instance_variables(index)
20
+ }
21
+ end
22
+ end
23
+ end
24
+
25
+ def serialized_class index
26
+ if index.include?(self.class)
27
+ "#<ELE_#{index.index(self.class)}>"
28
+ else
29
+ self.class.to_s
30
+ end
31
+ end
32
+
33
+ def self.instances
34
+ GC.start
35
+ result = []
36
+ ObjectSpace.each_object(Gamefic::Serialize) { |obj| result.push obj }
37
+ result
38
+ end
39
+
40
+ # @param string [String]
41
+ # @return [Object]
42
+ def self.string_to_constant string
43
+ space = Object
44
+ string.split('::').each do |part|
45
+ space = space.const_get(part)
46
+ end
47
+ space
7
48
  end
8
49
  end
9
50
  end
10
51
 
11
52
  class Object
12
- def to_serial
53
+ class << self
54
+ def exclude_from_serial ary
55
+ @excluded_from_serial = ary
56
+ end
57
+
58
+ def excluded_from_serial
59
+ @excluded_from_serial ||= []
60
+ end
61
+ end
62
+
63
+ def to_serial(_index)
13
64
  return self if [true, false, nil].include?(self)
14
65
  # @todo This warning is a little too spammy. Set up a logger so it can be
15
66
  # limited to an info or debug level.
@@ -17,51 +68,142 @@ class Object
17
68
  "#<UNKNOWN>"
18
69
  end
19
70
 
20
- def serialize_instance_variables
71
+ def from_serial(index = [])
72
+ if self.is_a?(Hash) && (self['class'] || self['instance'])
73
+ if self['instance']
74
+ elematch = self['instance'].match(/^#<ELE_([\d]+)>$/)
75
+ object = index[elematch[1].to_i]
76
+ raise "Unable to load indexed element ##{elematch[1]} #{self}" if object.nil?
77
+ elsif self['class']
78
+ if self['class'] == 'Hash'
79
+ object = {}
80
+ self['data'].each do |arr|
81
+ object[arr[0].from_serial(index)] = arr[1].from_serial(index)
82
+ end
83
+ return object
84
+ elsif self['class'] == 'Class'
85
+ return Gamefic::Serialize.string_to_constant(self['name'])
86
+ else
87
+ elematch = self['class'].match(/^#<ELE_([\d]+)>$/)
88
+ if elematch
89
+ klass = index[elematch[1].to_i]
90
+ else
91
+ klass = Gamefic::Serialize.string_to_constant(self['class'])
92
+ end
93
+ raise "Unable to find class #{self['class']} #{self}" if klass.nil?
94
+ object = klass.allocate
95
+ index.push object if object.is_a?(Gamefic::Serialize)
96
+ end
97
+ end
98
+ self['ivars'].each_pair do |k, v|
99
+ object.instance_variable_set(k, v.from_serial(index))
100
+ end
101
+ object
102
+ elsif self.is_a?(Numeric)
103
+ self
104
+ elsif self.is_a?(String)
105
+ match = self.match(/#<ELE_([0-9]+)>/)
106
+ return index.index(match[1].to_i) if match
107
+ match = self.match(/#<SYM:([a-z0-9_\?\!]+)>/i)
108
+ return match[1].to_sym if match
109
+ # return nil if self == '#<UNKNOWN>'
110
+ self
111
+ elsif self.is_a?(Hash)
112
+ result = {}
113
+ unknown = false
114
+ self.each_pair do |k, v|
115
+ k2 = k.from_serial(index)
116
+ v2 = v.from_serial(index)
117
+ if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
118
+ unknown = true
119
+ break
120
+ end
121
+ result[k2] = v2
122
+ end
123
+ result = "#<UNKNOWN>" if unknown
124
+ result
125
+ elsif self && self != true
126
+ STDERR.puts "Unable to unserialize #{self.class}"
127
+ nil
128
+ else
129
+ # true, false, or nil
130
+ self
131
+ end
132
+ end
133
+
134
+ def serialize_instance_variables(index)
21
135
  result = {}
22
136
  instance_variables.each do |k|
23
- result[k.to_s] = instance_variable_get(k).to_serial
137
+ next if self.class.excluded_from_serial.include?(k)
138
+ val = instance_variable_get(k)
139
+ if index.include?(val)
140
+ result[k.to_s] = {
141
+ 'instance' => "#<ELE_#{index.index(val)}>",
142
+ 'ivars' => {}
143
+ }
144
+ else
145
+ result[k.to_s] = val.to_serial(index)
146
+ end
24
147
  end
25
148
  result
26
149
  end
27
150
  end
28
151
 
152
+ class Class
153
+ def to_serial(index = [])
154
+ if name.nil?
155
+ super
156
+ else
157
+ {
158
+ 'class' => 'Class',
159
+ 'name' => name
160
+ }
161
+ end
162
+ end
163
+ end
164
+
29
165
  class Symbol
30
- def to_serial
166
+ def to_serial(_index = [])
31
167
  "#<SYM:#{self}>"
32
168
  end
33
169
  end
34
170
 
35
171
  class String
36
- def to_serial
172
+ def to_serial(_index = [])
37
173
  self
38
174
  end
39
175
  end
40
176
 
41
177
  class Numeric
42
- def to_serial
178
+ def to_serial(_index = [])
43
179
  self
44
180
  end
45
181
  end
46
182
 
47
183
  class Array
48
- def to_serial
184
+ def to_serial(index = [])
49
185
  map do |e|
50
- s = e.to_serial
186
+ s = e.to_serial(index)
51
187
  return "#<UNKNOWN>" if s == "#<UNKNOWN>"
52
188
  s
53
189
  end
54
190
  end
191
+
192
+ def from_serial(index = [])
193
+ result = map { |e| e.from_serial(index) }
194
+ result = "#<UNKNOWN>" if result.any? { |e| e == "#<UNKNOWN>" }
195
+ result
196
+ end
55
197
  end
56
198
 
57
199
  class Hash
58
- def to_serial
59
- result = {}
200
+ def to_serial(index = [])
201
+ result = {'class' => 'Hash', 'data' => []}
60
202
  each_pair do |key, value|
61
- k2 = key.to_serial
62
- v2 = value.to_serial
203
+ k2 = key.to_serial(index)
204
+ v2 = value.to_serial(index)
63
205
  return "#<UNKNOWN>" if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
64
- result[k2] = v2
206
+ result['data'].push [k2, v2]
65
207
  end
66
208
  result
67
209
  end
@@ -4,6 +4,7 @@ module Gamefic
4
4
  class Subplot #< Container
5
5
  include World
6
6
  include Scriptable
7
+ include Gamefic::Serialize
7
8
  # @!parse extend Scriptable::ClassMethods
8
9
 
9
10
  # @return [Gamefic::Plot]
@@ -16,10 +17,16 @@ module Gamefic
16
17
  @plot = plot
17
18
  @next_cue = next_cue
18
19
  @concluded = false
20
+ @more = more
19
21
  configure more
20
22
  run_scripts
21
23
  playbook.freeze
22
24
  self.introduce introduce unless introduce.nil?
25
+ @static = [self] + scene_classes + entities
26
+ end
27
+
28
+ def static
29
+ plot.static
23
30
  end
24
31
 
25
32
  def players
@@ -63,6 +70,7 @@ module Gamefic
63
70
  # @todo I'm not sure why rejecting nils is necessary here. It's only an
64
71
  # issue in Opal.
65
72
  entities.reject(&:nil?).each { |e| destroy e }
73
+ # plot.static.remove(scene_classes + entities)
66
74
  end
67
75
 
68
76
  def concluded?
@@ -90,5 +98,15 @@ module Gamefic
90
98
  #
91
99
  def configure more
92
100
  end
101
+
102
+ # def to_serial(index)
103
+ # puts "Serializing #{self}"
104
+ # super
105
+ # end
106
+
107
+ # def from_serial index = []
108
+ # # @todo Customize subplot unserialization
109
+ # super
110
+ # end
93
111
  end
94
112
  end
@@ -1,3 +1,3 @@
1
1
  module Gamefic
2
- VERSION = '2.0.1'
2
+ VERSION = '2.0.2'
3
3
  end
@@ -43,11 +43,18 @@ module Gamefic
43
43
  # @param [Gamefic::Entity] The entity to remove
44
44
  def destroy entity
45
45
  entity.parent = nil
46
- index = entities.index(entity)
47
- return if index.nil? || index < static_entity_length - 1
48
- entities.delete_at index
46
+ # index = entities.index(entity)
47
+ # return if index.nil? || index < static_entity_length - 1
48
+ # entities.delete_at index
49
+ # players.delete entity
50
+ # entity.destroy
51
+
52
+ # @todo It might make sense to destroy the entity completely now. It
53
+ # will still have a reference in the index, but that shouldn't impact
54
+ # the current state of the plot.
55
+ return if static.include?(entity)
56
+ entities.delete entity
49
57
  players.delete entity
50
- entity.destroy
51
58
  end
52
59
 
53
60
  # Pick an entity based on its description.
@@ -87,15 +94,15 @@ module Gamefic
87
94
  @players ||= []
88
95
  end
89
96
 
90
- private
97
+ # private
91
98
 
92
- def mark_static_entities
93
- @static_entity_length ||= entities.length
94
- end
99
+ # def mark_static_entities
100
+ # @static_entity_length ||= entities.length
101
+ # end
95
102
 
96
- def static_entity_length
97
- @static_entity_length || 0
98
- end
103
+ # def static_entity_length
104
+ # @static_entity_length || 0
105
+ # end
99
106
  end
100
107
  end
101
108
  end
@@ -35,7 +35,7 @@ module Gamefic
35
35
  players.push player
36
36
  @introduction.call(player) unless @introduction.nil?
37
37
  # @todo Find a better way to persist player characters
38
- Gamefic::Index.stick
38
+ # Gamefic::Index.stick
39
39
  end
40
40
 
41
41
  # Create a multiple-choice scene.
@@ -144,7 +144,7 @@ module Gamefic
144
144
  scene_classes.push s
145
145
  s
146
146
  end
147
-
147
+
148
148
  # Create a custom scene.
149
149
  #
150
150
  # Custom scenes should always specify the next scene to be cued or
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gamefic
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-26 00:00:00.000000000 Z
11
+ date: 2020-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -74,6 +74,7 @@ files:
74
74
  - ".rspec"
75
75
  - ".rubocop.yml"
76
76
  - ".solargraph.yml"
77
+ - CHANGELOG.md
77
78
  - Gemfile
78
79
  - LICENSE
79
80
  - README.md
@@ -89,13 +90,13 @@ files:
89
90
  - lib/gamefic/describable.rb
90
91
  - lib/gamefic/element.rb
91
92
  - lib/gamefic/entity.rb
92
- - lib/gamefic/index.rb
93
93
  - lib/gamefic/keywords.rb
94
94
  - lib/gamefic/messaging.rb
95
95
  - lib/gamefic/node.rb
96
96
  - lib/gamefic/plot.rb
97
97
  - lib/gamefic/plot/darkroom.rb
98
98
  - lib/gamefic/plot/host.rb
99
+ - lib/gamefic/plot/index.rb
99
100
  - lib/gamefic/plot/snapshot.rb
100
101
  - lib/gamefic/query.rb
101
102
  - lib/gamefic/query/base.rb
@@ -1,121 +0,0 @@
1
- require 'json'
2
-
3
- module Gamefic
4
- module Index
5
- @@elements = []
6
- @@stuck_length = 0
7
-
8
- def initialize **data
9
- data.each_pair do |k, v|
10
- public_send "#{k}=", v
11
- end
12
- @@elements.push self
13
- end
14
-
15
- def to_serial
16
- index = @@elements.index(self)
17
- raise RuntimeError, "#{self} is not an indexed element" unless index
18
- "#<ELE_#{index}>"
19
- end
20
-
21
- def destroy
22
- @@elements.delete self unless Index.stuck?(self)
23
- end
24
-
25
- def self.elements
26
- @@elements
27
- end
28
-
29
- def self.serials
30
- result = []
31
- @@elements.each do |e|
32
- d = {}
33
- d['class'] = e.class.to_s
34
- e.instance_variables.each do |k|
35
- v = e.instance_variable_get(k)
36
- d[k] = v.to_serial
37
- end
38
- result.push d
39
- end
40
- result
41
- end
42
-
43
- def self.from_serial serial
44
- if serial.is_a?(Hash) && serial['class']
45
- klass = eval(serial['class'])
46
- object = klass.allocate
47
- serial.each_pair do |k, v|
48
- next unless k.to_s.start_with?('@')
49
- object.instance_variable_set(k, from_serial(v))
50
- end
51
- object
52
- elsif serial.is_a?(Numeric)
53
- serial
54
- elsif serial.is_a?(String)
55
- match = serial.match(/#<ELE_([0-9]+)>/)
56
- return Gamefic::Index.elements[match[1].to_i] if match
57
- match = serial.match(/#<SYM:([a-z0-9_\?\!]+)>/i)
58
- return match[1].to_sym if match
59
- serial
60
- elsif serial.is_a?(Array)
61
- result = serial.map { |e| from_serial(e) }
62
- result = "#<UNKNOWN>" if result.any? { |e| e == "#<UNKNOWN>" }
63
- result
64
- elsif serial.is_a?(Hash)
65
- result = {}
66
- unknown = false
67
- serial.each_pair do |k, v|
68
- k2 = from_serial(k)
69
- v2 = from_serial(v)
70
- if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
71
- unknown = true
72
- break
73
- end
74
- result[k2] = v2
75
- end
76
- result = "#<UNKNOWN>" if unknown
77
- result
78
- elsif serial && serial != true
79
- STDERR.puts "Unable to unserialize #{serial.class}"
80
- nil
81
- else
82
- # true, false, or nil
83
- serial
84
- end
85
- end
86
-
87
- def self.unserialize serials
88
- serials.each_with_index do |s, i|
89
- next if elements[i]
90
- klass = eval(s['class'])
91
- klass.new
92
- end
93
- serials.each_with_index do |s, i|
94
- s.each_pair do |k, v|
95
- next unless k.to_s.start_with?('@')
96
- next if v == "#<UNKNOWN>"
97
- elements[i].instance_variable_set(k, from_serial(v))
98
- end
99
- end
100
- elements
101
- end
102
-
103
- def self.stick
104
- @@stuck_length = @@elements.length
105
- end
106
-
107
- def self.stuck
108
- @@stuck_length
109
- end
110
-
111
- def self.clear
112
- @@stuck_length = 0
113
- @@elements.clear
114
- end
115
-
116
- def self.stuck? thing
117
- index = @@elements.index(thing)
118
- index && index <= @@stuck_length - 1
119
- end
120
- end
121
- end