gamefic 2.0.1 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|