gamefic 1.6.0 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +16 -0
- data/.solargraph.yml +5 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +10 -0
- data/gamefic.gemspec +27 -0
- data/lib/gamefic.rb +11 -8
- data/lib/gamefic/action.rb +68 -58
- data/lib/gamefic/active.rb +331 -0
- data/lib/gamefic/actor.rb +8 -0
- data/lib/gamefic/command.rb +9 -7
- data/lib/gamefic/core_ext/array.rb +27 -49
- data/lib/gamefic/core_ext/string.rb +25 -16
- data/lib/gamefic/describable.rb +37 -22
- data/lib/gamefic/element.rb +47 -0
- data/lib/gamefic/entity.rb +24 -48
- data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
- data/lib/gamefic/messaging.rb +43 -45
- data/lib/gamefic/node.rb +14 -5
- data/lib/gamefic/plot.rb +73 -85
- data/lib/gamefic/plot/darkroom.rb +80 -0
- data/lib/gamefic/plot/host.rb +42 -46
- data/lib/gamefic/plot/snapshot.rb +14 -214
- data/lib/gamefic/query.rb +15 -17
- data/lib/gamefic/query/base.rb +51 -42
- data/lib/gamefic/query/children.rb +0 -0
- data/lib/gamefic/query/descendants.rb +2 -2
- data/lib/gamefic/query/external.rb +18 -0
- data/lib/gamefic/query/family.rb +3 -7
- data/lib/gamefic/query/matches.rb +75 -67
- data/lib/gamefic/query/parent.rb +0 -0
- data/lib/gamefic/query/siblings.rb +0 -0
- data/lib/gamefic/query/text.rb +12 -12
- data/lib/gamefic/query/tree.rb +17 -0
- data/lib/gamefic/scene.rb +1 -5
- data/lib/gamefic/scene/{active.rb → activity.rb} +4 -6
- data/lib/gamefic/scene/base.rb +77 -13
- data/lib/gamefic/scene/conclusion.rb +0 -2
- data/lib/gamefic/scene/custom.rb +0 -2
- data/lib/gamefic/scene/multiple_choice.rb +18 -16
- data/lib/gamefic/scene/multiple_scene.rb +29 -20
- data/lib/gamefic/scene/pause.rb +7 -2
- data/lib/gamefic/scene/yes_or_no.rb +21 -9
- data/lib/gamefic/scriptable.rb +88 -0
- data/lib/gamefic/serialize.rb +223 -0
- data/lib/gamefic/subplot.rb +47 -51
- data/lib/gamefic/syntax.rb +15 -13
- data/lib/gamefic/version.rb +3 -3
- data/lib/gamefic/world.rb +18 -0
- data/lib/gamefic/world/callbacks.rb +135 -0
- data/lib/gamefic/world/commands.rb +184 -0
- data/lib/gamefic/world/entities.rb +98 -0
- data/lib/gamefic/{plot → world}/playbook.rb +245 -236
- data/lib/gamefic/world/players.rb +37 -0
- data/lib/gamefic/world/scenes.rb +226 -0
- metadata +40 -108
- data/bin/gamefic +0 -9
- data/lib/gamefic/character.rb +0 -232
- data/lib/gamefic/character/state.rb +0 -12
- data/lib/gamefic/engine.rb +0 -7
- data/lib/gamefic/engine/base.rb +0 -66
- data/lib/gamefic/engine/tty.rb +0 -24
- data/lib/gamefic/grammar.rb +0 -13
- data/lib/gamefic/grammar/conjugator.rb +0 -20
- data/lib/gamefic/grammar/gender.rb +0 -11
- data/lib/gamefic/grammar/person.rb +0 -10
- data/lib/gamefic/grammar/plural.rb +0 -13
- data/lib/gamefic/grammar/pronouns.rb +0 -105
- data/lib/gamefic/grammar/tense.rb +0 -6
- data/lib/gamefic/grammar/verb_set.rb +0 -43
- data/lib/gamefic/grammar/verbs.rb +0 -26
- data/lib/gamefic/grammar/word_adapter.rb +0 -49
- data/lib/gamefic/plot/articles.rb +0 -22
- data/lib/gamefic/plot/callbacks.rb +0 -127
- data/lib/gamefic/plot/commands.rb +0 -121
- data/lib/gamefic/plot/entities.rb +0 -88
- data/lib/gamefic/plot/players.rb +0 -15
- data/lib/gamefic/plot/scenes.rb +0 -149
- data/lib/gamefic/plot/theater.rb +0 -73
- data/lib/gamefic/plot/you_mount.rb +0 -22
- data/lib/gamefic/script.rb +0 -13
- data/lib/gamefic/script/base.rb +0 -42
- data/lib/gamefic/script/file.rb +0 -14
- data/lib/gamefic/script/text.rb +0 -14
- data/lib/gamefic/shell.rb +0 -76
- data/lib/gamefic/source.rb +0 -14
- data/lib/gamefic/source/base.rb +0 -12
- data/lib/gamefic/source/file.rb +0 -23
- data/lib/gamefic/source/text.rb +0 -16
- data/lib/gamefic/tester.rb +0 -19
- data/lib/gamefic/text.rb +0 -8
- data/lib/gamefic/text/ansi.rb +0 -53
- data/lib/gamefic/text/html.rb +0 -68
- data/lib/gamefic/text/html/conversions.rb +0 -250
- data/lib/gamefic/text/html/entities.rb +0 -9
- data/lib/gamefic/tty.rb +0 -10
- data/lib/gamefic/user.rb +0 -8
- data/lib/gamefic/user/base.rb +0 -15
- data/lib/gamefic/user/buffer.rb +0 -32
- data/lib/gamefic/user/tty.rb +0 -54
@@ -1,50 +1,52 @@
|
|
1
|
-
module Gamefic
|
2
|
-
module
|
3
|
-
SPLIT_REGEXP = /[\s]+/
|
4
|
-
|
5
|
-
# Get an array of keywords associated with this object.
|
6
|
-
# The default implementation splits the value of self.to_s into an array.
|
7
|
-
#
|
8
|
-
# @return [Array<String>]
|
9
|
-
def keywords
|
10
|
-
self.to_s.downcase.split(SPLIT_REGEXP).uniq
|
11
|
-
end
|
12
|
-
|
13
|
-
# Determine if this object matches the provided description.
|
14
|
-
# In a regular match, every word in the description must be a keyword.
|
15
|
-
# Fuzzy matches accept words if a keyword starts with it, e.g., "red"
|
16
|
-
# would be a fuzzy match for "reddish."
|
17
|
-
#
|
18
|
-
# @example
|
19
|
-
# dog = "big red dog"
|
20
|
-
# dog.extend Gamefic::Matchable
|
21
|
-
#
|
22
|
-
# dog.
|
23
|
-
# dog.
|
24
|
-
# dog.
|
25
|
-
#
|
26
|
-
# dog.
|
27
|
-
# dog.
|
28
|
-
#
|
29
|
-
# @
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
1
|
+
module Gamefic
|
2
|
+
module Keywords
|
3
|
+
SPLIT_REGEXP = /[\s]+/
|
4
|
+
|
5
|
+
# Get an array of keywords associated with this object.
|
6
|
+
# The default implementation splits the value of self.to_s into an array.
|
7
|
+
#
|
8
|
+
# @return [Array<String>]
|
9
|
+
def keywords
|
10
|
+
self.to_s.downcase.split(SPLIT_REGEXP).uniq
|
11
|
+
end
|
12
|
+
|
13
|
+
# Determine if this object matches the provided description.
|
14
|
+
# In a regular match, every word in the description must be a keyword.
|
15
|
+
# Fuzzy matches accept words if a keyword starts with it, e.g., "red"
|
16
|
+
# would be a fuzzy match for "reddish."
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# dog = "big red dog"
|
20
|
+
# dog.extend Gamefic::Matchable
|
21
|
+
#
|
22
|
+
# dog.specified?("red dog") #=> true
|
23
|
+
# dog.specified?("gray dog") #=> false
|
24
|
+
# dog.specified?("red do") #=> false
|
25
|
+
#
|
26
|
+
# dog.specified?("re do", fuzzy: true) #=> true
|
27
|
+
# dog.specified?("red og", fuzzy: true) #=> false
|
28
|
+
#
|
29
|
+
# @param description [String] The description to be compared
|
30
|
+
# @param fuzzy [Boolean] Use fuzzy matching (default is false)
|
31
|
+
# @return [Boolean]
|
32
|
+
def specified? description, fuzzy: false
|
33
|
+
words = description.split(SPLIT_REGEXP)
|
34
|
+
return false if words.empty?
|
35
|
+
matches = 0
|
36
|
+
available = keywords
|
37
|
+
words.each { |w|
|
38
|
+
if fuzzy
|
39
|
+
available.each { |k|
|
40
|
+
if k.gsub(/[^a-z0-9]/, '').start_with?(w.downcase.gsub(/[^a-z0-9]/, ''))
|
41
|
+
matches +=1
|
42
|
+
break
|
43
|
+
end
|
44
|
+
}
|
45
|
+
else
|
46
|
+
matches +=1 if available.include?(w.downcase)
|
47
|
+
end
|
48
|
+
}
|
49
|
+
matches == words.length
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/gamefic/messaging.rb
CHANGED
@@ -1,45 +1,43 @@
|
|
1
|
-
module Gamefic
|
2
|
-
module Messaging
|
3
|
-
# Send a message to the entity.
|
4
|
-
# This method will automatically wrap the message in HTML paragraphs.
|
5
|
-
# To send a message without paragraph formatting, use #stream instead.
|
6
|
-
#
|
7
|
-
# @param message [String]
|
8
|
-
def tell(message)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
1
|
+
module Gamefic
|
2
|
+
module Messaging
|
3
|
+
# Send a message to the entity.
|
4
|
+
# This method will automatically wrap the message in HTML paragraphs.
|
5
|
+
# To send a message without paragraph formatting, use #stream instead.
|
6
|
+
#
|
7
|
+
# @param message [String]
|
8
|
+
def tell(message)
|
9
|
+
@messages = @messages.to_s + format(message)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Send a message to the Character as raw text.
|
13
|
+
# Unlike #tell, this method will not wrap the message in HTML paragraphs.
|
14
|
+
#
|
15
|
+
# @param message [String]
|
16
|
+
def stream(message)
|
17
|
+
@messages = @messages.to_s + message
|
18
|
+
end
|
19
|
+
|
20
|
+
# Get all the currently buffered messages consolidated in a single string.
|
21
|
+
#
|
22
|
+
# @return [String]
|
23
|
+
def messages
|
24
|
+
@messages ||= ''
|
25
|
+
end
|
26
|
+
|
27
|
+
# Clear the buffered messages.
|
28
|
+
#
|
29
|
+
def flush
|
30
|
+
@messages = ''
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def format message
|
36
|
+
"<p>#{message.strip}</p>"
|
37
|
+
.gsub(/[ \t\r]*\n[ \t\r]*\n[ \t\r]*/, "</p><p>")
|
38
|
+
.gsub(/[ \t]*\n[ \t]*/, ' ')
|
39
|
+
.gsub(/<p>[\s]*<p>/, '<p>')
|
40
|
+
.gsub(/<\/p>[\s]*<\/p>/, '</p>')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/gamefic/node.rb
CHANGED
@@ -1,31 +1,37 @@
|
|
1
1
|
# Exception raised when setting a node's parent would cause
|
2
2
|
# a circular reference, e.g., A -> A or A -> B -> A
|
3
|
-
class CircularNodeReferenceError <
|
4
|
-
end
|
3
|
+
class CircularNodeReferenceError < RuntimeError; end
|
5
4
|
|
6
5
|
module Gamefic
|
7
|
-
|
8
6
|
module Node
|
7
|
+
# An array of the object's children.
|
8
|
+
#
|
9
9
|
# @return [Array]
|
10
10
|
def children
|
11
11
|
@children ||= []
|
12
12
|
@children.clone
|
13
13
|
end
|
14
14
|
|
15
|
+
# Get a flat array of all descendants.
|
16
|
+
#
|
15
17
|
# @return [Array]
|
16
18
|
def flatten
|
17
19
|
array = Array.new
|
18
20
|
children.each { |child|
|
19
21
|
array = array + recurse_flatten(child)
|
20
22
|
}
|
21
|
-
|
23
|
+
array
|
22
24
|
end
|
23
25
|
|
26
|
+
# The object's parent.
|
27
|
+
#
|
24
28
|
# @return [Object]
|
25
29
|
def parent
|
26
30
|
@parent
|
27
31
|
end
|
28
32
|
|
33
|
+
# Set the object's parent.
|
34
|
+
#
|
29
35
|
def parent=(node)
|
30
36
|
return if node == @parent
|
31
37
|
if node == self
|
@@ -49,6 +55,10 @@ module Gamefic
|
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
58
|
+
# Determine if external objects can interact with this object's children.
|
59
|
+
# For example, a game can designate that the contents of a bowl are
|
60
|
+
# accessible, while the contents of a locked safe are not.
|
61
|
+
#
|
52
62
|
# @return [Boolean]
|
53
63
|
def accessible?
|
54
64
|
true
|
@@ -81,5 +91,4 @@ module Gamefic
|
|
81
91
|
return array
|
82
92
|
end
|
83
93
|
end
|
84
|
-
|
85
94
|
end
|
data/lib/gamefic/plot.rb
CHANGED
@@ -1,123 +1,111 @@
|
|
1
|
-
# TODO: JSON support is currently experimental.
|
2
|
-
#require 'gamefic/entityloader'
|
3
|
-
require 'gamefic/tester'
|
4
|
-
require 'gamefic/source'
|
5
|
-
require 'gamefic/script'
|
6
1
|
require 'gamefic/query'
|
7
2
|
|
8
3
|
module Gamefic
|
9
|
-
|
4
|
+
# A plot controls the game narrative and manages the world model.
|
5
|
+
# Authors typically build plots through scripts that are executed in a
|
6
|
+
# special container called a stage. All of the elements that compose the
|
7
|
+
# narrative (characters, locations, scenes, etc.) reside in the stage's
|
8
|
+
# scope. Game engines use the plot to receive game data and process user
|
9
|
+
# input.
|
10
|
+
#
|
10
11
|
class Plot
|
11
|
-
autoload :Scenes, 'gamefic/plot/scenes'
|
12
|
-
autoload :Commands, 'gamefic/plot/commands'
|
13
|
-
autoload :Entities, 'gamefic/plot/entities'
|
14
|
-
autoload :Articles, 'gamefic/plot/articles'
|
15
|
-
autoload :YouMount, 'gamefic/plot/you_mount'
|
16
12
|
autoload :Snapshot, 'gamefic/plot/snapshot'
|
13
|
+
autoload :Darkroom, 'gamefic/plot/darkroom'
|
17
14
|
autoload :Host, 'gamefic/plot/host'
|
18
|
-
autoload :Players, 'gamefic/plot/players'
|
19
|
-
autoload :Playbook, 'gamefic/plot/playbook'
|
20
|
-
autoload :Callbacks, 'gamefic/plot/callbacks'
|
21
|
-
autoload :Theater, 'gamefic/plot/theater'
|
22
15
|
|
23
|
-
|
24
|
-
|
25
|
-
attr_accessor :metadata
|
26
|
-
include Theater
|
27
|
-
include Gamefic, Tester, Players, Scenes, Commands, Entities
|
28
|
-
include Articles, YouMount, Snapshot, Host, Callbacks
|
16
|
+
# @return [Hash]
|
17
|
+
attr_reader :metadata
|
29
18
|
|
30
|
-
|
31
|
-
def initialize(source = nil)
|
32
|
-
@source = source || Source::Text.new({})
|
33
|
-
@working_scripts = []
|
34
|
-
@imported_scripts = []
|
35
|
-
@running = false
|
36
|
-
post_initialize
|
37
|
-
end
|
19
|
+
attr_reader :static
|
38
20
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
21
|
+
include World
|
22
|
+
include Scriptable
|
23
|
+
# @!parse extend Scriptable::ClassMethods
|
24
|
+
include Snapshot
|
25
|
+
include Host
|
26
|
+
include Serialize
|
43
27
|
|
44
|
-
|
45
|
-
@running
|
46
|
-
end
|
28
|
+
exclude_from_serial [:@static]
|
47
29
|
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
@
|
53
|
-
end
|
54
|
-
|
55
|
-
def post_initialize
|
56
|
-
# TODO: Should this method be required by extended classes?
|
30
|
+
# @param metadata [Hash]
|
31
|
+
def initialize metadata: {}
|
32
|
+
@metadata = metadata
|
33
|
+
run_scripts
|
34
|
+
@static = [self] + scene_classes + entities
|
57
35
|
end
|
58
|
-
|
36
|
+
|
59
37
|
# Get an Array of the Plot's current Syntaxes.
|
60
38
|
#
|
61
39
|
# @return [Array<Syntax>]
|
62
40
|
def syntaxes
|
63
41
|
playbook.syntaxes
|
64
42
|
end
|
65
|
-
|
43
|
+
|
66
44
|
# Prepare the Plot for the next turn of gameplay.
|
67
|
-
# This method is typically called by the Engine that manages game
|
45
|
+
# This method is typically called by the Engine that manages game
|
46
|
+
# execution.
|
47
|
+
#
|
68
48
|
def ready
|
69
49
|
playbook.freeze
|
70
|
-
@running = true
|
71
50
|
call_ready
|
72
51
|
call_player_ready
|
73
|
-
|
52
|
+
subplots.each { |s| s.ready }
|
53
|
+
players.each do |p|
|
54
|
+
p.state.replace(
|
55
|
+
p.scene.state
|
56
|
+
.merge({
|
57
|
+
messages: p.messages,
|
58
|
+
last_prompt: p.last_prompt,
|
59
|
+
last_input: p.last_input,
|
60
|
+
queue: p.queue
|
61
|
+
})
|
62
|
+
.merge(p.state)
|
63
|
+
)
|
64
|
+
p.output.replace(p.state)
|
65
|
+
p.state.clear
|
66
|
+
end
|
74
67
|
end
|
75
|
-
|
68
|
+
|
76
69
|
# Update the Plot's current turn of gameplay.
|
77
|
-
# This method is typically called by the Engine that manages game
|
70
|
+
# This method is typically called by the Engine that manages game
|
71
|
+
# execution.
|
72
|
+
#
|
78
73
|
def update
|
79
74
|
entities.each { |e| e.flush }
|
80
75
|
call_before_player_update
|
81
|
-
|
82
|
-
|
76
|
+
players.each do |p|
|
77
|
+
p.performed nil
|
78
|
+
next unless p.scene
|
79
|
+
p.last_input = p.queue.last
|
80
|
+
p.last_prompt = p.scene.prompt
|
81
|
+
p.scene.update
|
82
|
+
if p.scene.is_a?(Scene::Conclusion)
|
83
|
+
player_conclude_procs.each do |proc|
|
84
|
+
proc.call p
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
83
88
|
call_player_update
|
84
89
|
call_update
|
85
|
-
|
86
|
-
|
90
|
+
subplots.delete_if(&:concluded?)
|
91
|
+
subplots.each(&:update)
|
87
92
|
end
|
88
93
|
|
89
|
-
|
94
|
+
# Send a message to a group of entities.
|
95
|
+
#
|
96
|
+
# @param entities [Array<Entity>]
|
97
|
+
# @param message [String]
|
98
|
+
def tell entities, message
|
90
99
|
entities.each { |entity|
|
91
|
-
entity.tell message
|
100
|
+
entity.tell message
|
92
101
|
}
|
93
102
|
end
|
94
|
-
|
95
|
-
# Load a script into the current Plot.
|
96
|
-
# This method is similar to Kernel#require, except that the script is
|
97
|
-
# evaluated within the Plot's context via #stage.
|
98
|
-
#
|
99
|
-
# @param path [String] The path to the script being evaluated
|
100
|
-
# @return [Boolean] true if the script was loaded by this call or false if it was already loaded.
|
101
|
-
def script path
|
102
|
-
imported_script = source.export(path)
|
103
|
-
if imported_script.nil?
|
104
|
-
raise LoadError.new("cannot load script -- #{path}")
|
105
|
-
end
|
106
|
-
if !@working_scripts.include?(imported_script) and !imported_scripts.include?(imported_script)
|
107
|
-
@working_scripts.push imported_script
|
108
|
-
# @hack Arguments need to be in different order if source returns proc
|
109
|
-
if imported_script.read.kind_of?(Proc)
|
110
|
-
stage &imported_script.read
|
111
|
-
else
|
112
|
-
stage imported_script.read, imported_script.absolute_path
|
113
|
-
end
|
114
|
-
@working_scripts.pop
|
115
|
-
imported_scripts.push imported_script
|
116
|
-
true
|
117
|
-
else
|
118
|
-
false
|
119
|
-
end
|
120
|
-
end
|
121
103
|
end
|
104
|
+
end
|
122
105
|
|
106
|
+
module Gamefic
|
107
|
+
# @yieldself [Gamefic::Plot]
|
108
|
+
def self.script &block
|
109
|
+
Gamefic::Plot.script &block
|
110
|
+
end
|
123
111
|
end
|
@@ -0,0 +1,80 @@
|
|
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
|
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
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
# Restore a snapshot.
|
35
|
+
#
|
36
|
+
# @param snapshot [Hash]
|
37
|
+
def restore snapshot
|
38
|
+
# @todo Use `program` for verification
|
39
|
+
|
40
|
+
plot.subplots.each(&:conclude)
|
41
|
+
plot.subplots.clear
|
42
|
+
|
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
|
+
end
|
54
|
+
|
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
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|