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 +4 -4
- data/CHANGELOG.md +2 -0
- data/lib/gamefic.rb +3 -2
- data/lib/gamefic/element.rb +8 -4
- data/lib/gamefic/plot.rb +6 -3
- data/lib/gamefic/plot/darkroom.rb +49 -61
- data/lib/gamefic/plot/index.rb +92 -0
- data/lib/gamefic/plot/snapshot.rb +13 -5
- data/lib/gamefic/scene/base.rb +3 -0
- data/lib/gamefic/scriptable.rb +1 -0
- data/lib/gamefic/serialize.rb +159 -17
- data/lib/gamefic/subplot.rb +18 -0
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic/world/entities.rb +18 -11
- data/lib/gamefic/world/scenes.rb +2 -2
- metadata +4 -3
- data/lib/gamefic/index.rb +0 -121
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ac3f26cc5dcb62f6b4af8f0a365a316a4324605efecac8fd852f9980f00e336
|
4
|
+
data.tar.gz: 11348faf9a1c471c11ca4da93f419167db25a92ec7617fd73fb592fdf0ec7d7f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bd78b541afec9fd2129d785442ddc811dacbc42c41f9c683329776f90ada2e105578324ef73fa7dedd7fc26ed828917f1a796765f0f1b74ade334b54c94d5ab
|
7
|
+
data.tar.gz: d3a982efcb6bdd0c306ebdab70ee722fcd1040d6cf51501b738b8bca65e955689dcfe2d77aa5e8684e1f0e18d27b71579410f51a6189d23014da47156bbbe3cf
|
data/CHANGELOG.md
ADDED
data/lib/gamefic.rb
CHANGED
@@ -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
|
20
|
+
require 'gamefic/plot'
|
21
|
+
require 'gamefic/plot/index'
|
21
22
|
require 'gamefic/subplot'
|
data/lib/gamefic/element.rb
CHANGED
@@ -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
|
-
|
15
|
-
|
16
|
-
|
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
|
data/lib/gamefic/plot.rb
CHANGED
@@ -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
|
-
|
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
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
'
|
21
|
-
'
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
-
|
39
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
'
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
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
|
data/lib/gamefic/scene/base.rb
CHANGED
data/lib/gamefic/scriptable.rb
CHANGED
data/lib/gamefic/serialize.rb
CHANGED
@@ -1,15 +1,66 @@
|
|
1
1
|
module Gamefic
|
2
2
|
module Serialize
|
3
|
-
def to_serial
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
|
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
|
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
|
-
|
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[
|
206
|
+
result['data'].push [k2, v2]
|
65
207
|
end
|
66
208
|
result
|
67
209
|
end
|
data/lib/gamefic/subplot.rb
CHANGED
@@ -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
|
data/lib/gamefic/version.rb
CHANGED
@@ -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
|
-
|
94
|
-
end
|
99
|
+
# def mark_static_entities
|
100
|
+
# @static_entity_length ||= entities.length
|
101
|
+
# end
|
95
102
|
|
96
|
-
def static_entity_length
|
97
|
-
|
98
|
-
end
|
103
|
+
# def static_entity_length
|
104
|
+
# @static_entity_length || 0
|
105
|
+
# end
|
99
106
|
end
|
100
107
|
end
|
101
108
|
end
|
data/lib/gamefic/world/scenes.rb
CHANGED
@@ -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.
|
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-
|
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
|
data/lib/gamefic/index.rb
DELETED
@@ -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
|