lib/gamefic/plot/darkroom.rb in gamefic-1.7.0 vs lib/gamefic/plot/darkroom.rb in gamefic-2.0.0
- old
+ new
@@ -1,264 +1,92 @@
-module Gamefic
- # Create and restore plot snapshots.
- #
- class Plot::Darkroom
- attr_reader :plot
-
- def initialize plot
- @plot = plot
- end
-
- # Create a snapshot of the plot.
- #
- # @return [Hash]
- def save
- result = { entities: [], players: [], subplots: [], instance_variables: {} }
- entity_store.clear
- player_store.clear
- entity_store.concat plot.entities
- player_store.concat plot.players
- plot.subplots.each { |s| entity_store.concat s.entities }
- entity_store.uniq!
- entity_store.each do |e|
- result[:entities].push hash_entity(e)
- end
- player_store.each do |p|
- result[:players].push hash_entity(p)
- end
- plot.theater.instance_variables.each { |i|
- v = plot.theater.instance_variable_get(i)
- result[:instance_variables][i] = serialize(v) if can_serialize?(v)
- }
- plot.subplots.each { |s|
- result[:subplots].push hash_subplot(s)
- }
- result
- end
-
- # Restore a snapshot.
- #
- def restore snapshot
- entity_store.clear
- player_store.clear
- plot.subplots.each { |s| s.conclude }
- plot.entities[plot.initial_state[:entities].length..-1].each { |e| plot.destroy e }
- entity_store.concat plot.entities[0..plot.initial_state[:entities].length-1]
- entity_store.uniq!
- player_store.concat plot.players
- i = 0
- snapshot[:entities].each { |h|
- if entity_store[i].nil?
- e = plot.stage do
- cls = self.const_get(h[:class])
- make cls
- end
- entity_store.push e
- end
- i += 1
- }
- snapshot[:subplots].each { |s|
- sp = plot.stage do
- cls = const_get(s[:class])
- branch cls
- end
- # @todo Assuming one player
- sp.introduce player_store[0] unless player_store.empty?
- rebuild_subplot sp, s
- }
- i = 0
- snapshot[:entities].each { |h|
- rebuild1 entity_store[i], h
- i += 1
- }
- i = 0
- snapshot[:players].each { |p|
- rebuild1 player_store[i], p
- i += 1
- }
- i = 0
- snapshot[:entities].each { |h|
- rebuild2 entity_store[i], h
- i += 1
- }
- i = 0
- snapshot[:players].each { |h|
- rebuild2 player_store[i], h
- i += 1
- }
- snapshot[:instance_variables].each_pair { |k, v|
- plot.theater.instance_variable_set(k, unserialize(v))
- }
- end
-
- private
-
- def hash_blacklist
- [:@parent, :@children, :@last_action, :@scene, :@next_scene, :@playbook, :@performance_stack, :@buffer_stack, :@messages, :@state]
- end
-
- def can_serialize? v
- 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?
- if v.kind_of?(Array)
- v.each do |e|
- result = can_serialize?(e)
- return false if result == false
- end
- true
- elsif v.kind_of?(Hash)
- v.each_pair do |k, v|
- result = can_serialize?(k)
- return false if result == false
- result = can_serialize?(v)
- return false if result == false
- end
- true
- else
- false
- end
- end
-
- def is_scene_class?(v)
- if v.kind_of?(Class)
- s = v
- until s.nil?
- return true if s == Gamefic::Scene::Base
- s = s.superclass
- end
- false
- else
- false
- end
- end
-
- def serialize v
- if v.kind_of?(Array)
- result = []
- v.each do |e|
- result.push serialize(e)
- end
- result
- elsif v.kind_of?(Hash)
- result = {}
- v.each_pair do |k, v|
- result[serialize(k)] = serialize(v)
- end
- result
- elsif is_scene_class?(v)
- i = plot.scene_classes.index(v)
- "#<SIN_#{i}>"
- elsif v.kind_of?(Gamefic::Entity)
- i = entity_store.index(v)
- if i.nil?
- i = player_store.index(v)
- if i.nil?
- raise "#{v} not found in plot"
- nil
- else
- "#<PIN_#{i}>"
- end
- else
- "#<EIN_#{i}>"
- end
- else
- v
- end
- end
-
- def unserialize v
- if v.kind_of?(Array)
- result = []
- v.each do |e|
- result.push unserialize(e)
- end
- result
- elsif v.kind_of?(Hash)
- result = {}
- v.each_pair do |k, v|
- result[unserialize(k)] = unserialize(v)
- end
- result
- elsif v.kind_of?(String)
- if m = v.match(/#<SIN_([0-9]+)>/)
- plot.scene_classes[m[1].to_i]
- elsif m = v.match(/#<EIN_([0-9]+)>/)
- entity_store[m[1].to_i]
- elsif m = v.match(/#<PIN_([0-9]+)>/)
- player_store[m[1].to_i]
- else
- v
- end
- else
- v
- end
- end
-
- def rebuild1 e, h
- h.each_pair do |k, v|
- if k.to_s.start_with?('@')
- e.instance_variable_set(k, unserialize(v))
- end
- end
- end
-
- def rebuild2 e, h
- h.each_pair do |k, v|
- if k.to_s != 'class' and !k.to_s.start_with?('@')
- e.send("#{k}=", unserialize(v))
- end
- end
- end
-
- def hash_subplot s
- result = { entities: [], instance_variables: {}, theater_instance_variables: {} }
- s.instance_variables.each { |i|
- v = s.instance_variable_get(i)
- result[:instance_variables][i] = serialize(v) if can_serialize?(v)
- }
- s.theater.instance_variables.each { |i|
- v = s.theater.instance_variable_get(i)
- result[:theater_instance_variables][i] = serialize(v) if can_serialize?(v)
- }
- s.entities.each { |s|
- result[:entities].push serialize(s)
- }
- result[:class] = s.class.to_s.split('::').last
- result
- end
-
- def rebuild_subplot s, h
- s.entities.each { |e|
- s.destroy e
- }
- h[:instance_variables].each_pair { |k, v|
- s.instance_variable_set(k, unserialize(v))
- }
- h[:theater_instance_variables].each_pair { |k, v|
- s.theater.instance_variable_set(k, unserialize(v))
- }
- i = 0
- h[:entities].each { |e|
- s.add_entity unserialize(e)
- i += 1
- }
- end
-
- def entity_store
- @entity_store ||= []
- end
-
- def player_store
- @player_store ||= []
- end
-
- def hash_entity e
- h = {}
- e.instance_variables.each { |i|
- v = e.instance_variable_get(i)
- h[i] = serialize(v) unless hash_blacklist.include?(i) or !can_serialize?(v)
- }
- h[:class] = e.class.to_s.split('::').last
- h[:parent] = serialize(e.parent)
- h
- end
- end
-end
+module Gamefic
+ # Create and restore plot snapshots.
+ #
+ class Plot
+ class Darkroom
+ # @return [Gamefic::Plot]
+ attr_reader :plot
+
+ def initialize plot
+ @plot = plot
+ end
+
+ # Create a snapshot of the plot.
+ #
+ # @return [Hash]
+ def save reduce: false
+ result = {
+ 'elements' => Gamefic::Index.serials,
+ 'entities' => plot.entities.map(&:to_serial),
+ 'players' => plot.players.map(&:to_serial),
+ 'theater_instance_variables' => plot.theater.serialize_instance_variables,
+ 'subplots' => plot.subplots.reject(&:concluded?).map { |s| serialize_subplot(s) },
+ 'metadata' => plot.metadata
+ }
+ end
+
+ # Restore a snapshot.
+ #
+ # @param snapshot [Hash]
+ def restore snapshot
+ Gamefic::Index.elements.map(&:destroy)
+ Gamefic::Index.unserialize snapshot['elements']
+ plot.entities.clear
+ snapshot['entities'].each do |ser|
+ plot.entities.push Index.from_serial(ser)
+ end
+
+ snapshot['theater_instance_variables'].each_pair do |k, s|
+ v = Gamefic::Index.from_serial(s)
+ next if v == "#<UNKNOWN>"
+ plot.theater.instance_variable_set(k, v)
+ end
+
+ snapshot['subplots'].each { |s| unserialize_subplot(s) }
+ end
+
+ private
+
+ def namespace_to_constant string
+ space = Object
+ string.split('::').each do |part|
+ space = space.const_get(part)
+ end
+ space
+ end
+
+ def serialize_subplot s
+ {
+ 'class' => s.class.to_s,
+ 'entities' => s.entities.map(&:to_serial),
+ 'instance_variables' => s.serialize_instance_variables,
+ 'theater_instance_variables' => s.theater.serialize_instance_variables
+ }
+ end
+
+ def unserialize_subplot s
+ cls = namespace_to_constant(s['class'])
+ sp = cls.allocate
+ sp.instance_variable_set(:@plot, plot)
+ s['entities'].each do |e|
+ sp.entities.push Gamefic::Index.from_serial(e)
+ end
+ s['instance_variables'].each_pair do |k, v|
+ next if v == "#<UNKNOWN>"
+ sp.instance_variable_set(k, Gamefic::Index.from_serial(v))
+ end
+ s['theater_instance_variables'].each_pair do |k, v|
+ next if v == "#<UNKNOWN>"
+ sp.theater.instance_variable_set(k, Gamefic::Index.from_serial(v))
+ end
+ plot.subplots.push sp
+ sp.send(:run_scripts)
+ # @todo Assuming one player
+ if plot.players.first
+ sp.players.push plot.players.first
+ plot.players.first.playbooks.push sp.playbook unless plot.players.first.playbooks.include?(sp.playbook)
+ end
+ sp
+ end
+ end
+ end
+end