gamefic 2.0.0 → 2.1.1
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/.rubocop.yml +4 -1
- data/CHANGELOG.md +13 -0
- data/lib/gamefic.rb +1 -2
- data/lib/gamefic/action.rb +34 -38
- data/lib/gamefic/active.rb +18 -12
- data/lib/gamefic/core_ext/array.rb +3 -0
- data/lib/gamefic/element.rb +6 -2
- data/lib/gamefic/plot.rb +10 -12
- data/lib/gamefic/plot/darkroom.rb +49 -61
- data/lib/gamefic/plot/snapshot.rb +13 -5
- data/lib/gamefic/query.rb +1 -0
- data/lib/gamefic/query/base.rb +25 -24
- data/lib/gamefic/query/descendants.rb +2 -2
- data/lib/gamefic/query/family.rb +2 -0
- data/lib/gamefic/query/text.rb +10 -11
- data/lib/gamefic/query/tree.rb +17 -0
- data/lib/gamefic/scene.rb +0 -1
- data/lib/gamefic/scene/base.rb +7 -2
- data/lib/gamefic/scene/conclusion.rb +1 -1
- data/lib/gamefic/scene/multiple_choice.rb +2 -12
- data/lib/gamefic/scene/pause.rb +1 -1
- data/lib/gamefic/scene/yes_or_no.rb +1 -1
- data/lib/gamefic/scriptable.rb +1 -0
- data/lib/gamefic/serialize.rb +172 -17
- data/lib/gamefic/subplot.rb +11 -2
- data/lib/gamefic/syntax.rb +1 -0
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic/world.rb +2 -0
- data/lib/gamefic/world/commands.rb +8 -8
- data/lib/gamefic/world/entities.rb +11 -14
- data/lib/gamefic/world/playbook.rb +30 -40
- data/lib/gamefic/world/players.rb +18 -2
- data/lib/gamefic/world/scenes.rb +13 -13
- metadata +18 -18
- data/lib/gamefic/index.rb +0 -121
- data/lib/gamefic/scene/custom.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d4af1f6add0c467fce62acde7d4903e2165a0007312720ac0a43fb324e2a594
|
4
|
+
data.tar.gz: fe6adad9a8c85fd9eac1b9b6921e13f356da73489e9d82c95b63974f94f45df0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2b0fda9b69825c655f6dfa90fa4c1bca78a6a53ea625946d54f9981d11104660f1e4f312bc53086559345f0c211edf979f237e18fd4bf95d83a2755e7b1cc9b
|
7
|
+
data.tar.gz: b1c85ff6f5c103354af38588bc129c989929c259ef4062abd8e9a0e5f92100fcf2134e1d16f3d8e3180bbcacd672d85b3cf5d1f162a8ff19217a64dc419e9bd7
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# 2.1.1 - July 23, 2021
|
2
|
+
- Remove gamefic/scene/custom autoload
|
3
|
+
|
4
|
+
# 2.1.0 - June 21, 2021
|
5
|
+
- Remove redundant MultipleChoice prompt
|
6
|
+
- Deprecate Scene::Custom
|
7
|
+
|
8
|
+
# 2.0.3 - December 14, 2020
|
9
|
+
- Remove unused Index class
|
10
|
+
- Active#conclude accepts data argument
|
11
|
+
|
12
|
+
# 2.0.2 - April 25, 2020
|
13
|
+
- Improved snapshot serialization
|
data/lib/gamefic.rb
CHANGED
@@ -5,7 +5,6 @@ require 'gamefic/core_ext/array'
|
|
5
5
|
require 'gamefic/core_ext/string'
|
6
6
|
|
7
7
|
require 'gamefic/describable'
|
8
|
-
require 'gamefic/index'
|
9
8
|
require 'gamefic/serialize'
|
10
9
|
require 'gamefic/element'
|
11
10
|
require 'gamefic/entity'
|
@@ -17,5 +16,5 @@ require "gamefic/action"
|
|
17
16
|
require "gamefic/syntax"
|
18
17
|
require 'gamefic/world'
|
19
18
|
require 'gamefic/scriptable'
|
20
|
-
require
|
19
|
+
require 'gamefic/plot'
|
21
20
|
require 'gamefic/subplot'
|
data/lib/gamefic/action.rb
CHANGED
@@ -1,9 +1,4 @@
|
|
1
1
|
module Gamefic
|
2
|
-
# Exception raised when the Action's proc arity is not compatible with the
|
3
|
-
# number of queries
|
4
|
-
class ActionArgumentError < ArgumentError
|
5
|
-
end
|
6
|
-
|
7
2
|
class Action
|
8
3
|
# An array of objects on which the action will operate, e.g., an entity
|
9
4
|
# that is a direct object of a command.
|
@@ -54,25 +49,32 @@ module Gamefic
|
|
54
49
|
self.class.meta?
|
55
50
|
end
|
56
51
|
|
57
|
-
|
52
|
+
# @param verb [Symbol]
|
53
|
+
# @param queries [Array<Gamefic::Query::Base>]
|
54
|
+
# @param meta [Boolean]
|
55
|
+
# @return [Class<Action>]
|
56
|
+
def self.subclass verb, *queries, meta: false, &block
|
58
57
|
act = Class.new(self) do
|
59
58
|
self.verb = verb
|
60
59
|
self.meta = meta
|
61
|
-
|
60
|
+
queries.each do |q|
|
62
61
|
add_query q
|
63
|
-
|
62
|
+
end
|
64
63
|
on_execute &block
|
65
64
|
end
|
66
|
-
if !block.nil?
|
67
|
-
raise
|
65
|
+
if !block.nil? && act.queries.length + 1 != block.arity && block.arity > 0
|
66
|
+
raise ArgumentError.new("Number of parameters is not compatible with proc arguments")
|
68
67
|
end
|
69
68
|
act
|
70
69
|
end
|
71
70
|
|
72
71
|
class << self
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
attr_reader :verb
|
73
|
+
|
74
|
+
# The proc to call when the action is executed
|
75
|
+
#
|
76
|
+
# @return [Proc]
|
77
|
+
attr_reader :executor
|
76
78
|
|
77
79
|
def meta?
|
78
80
|
@meta ||= false
|
@@ -93,7 +95,7 @@ module Gamefic
|
|
93
95
|
|
94
96
|
def signature
|
95
97
|
# @todo This is clearly unfinished
|
96
|
-
"#{verb} #{queries.map{|m| m.signature}.join(', ')}"
|
98
|
+
"#{verb} #{queries.map {|m| m.signature}.join(', ')}"
|
97
99
|
end
|
98
100
|
|
99
101
|
# True if this action is not intended to be performed directly by a
|
@@ -107,19 +109,13 @@ module Gamefic
|
|
107
109
|
verb.to_s.start_with?('_')
|
108
110
|
end
|
109
111
|
|
110
|
-
#
|
111
|
-
#
|
112
|
-
# @return [Proc]
|
113
|
-
def executor
|
114
|
-
@executor
|
115
|
-
end
|
116
|
-
|
112
|
+
# @return [Integer]
|
117
113
|
def rank
|
118
114
|
if @rank.nil?
|
119
115
|
@rank = 0
|
120
|
-
queries.each
|
116
|
+
queries.each do |q|
|
121
117
|
@rank += (q.rank + 1)
|
122
|
-
|
118
|
+
end
|
123
119
|
@rank -= 1000 if verb.nil?
|
124
120
|
end
|
125
121
|
@rank
|
@@ -128,22 +124,27 @@ module Gamefic
|
|
128
124
|
def valid? actor, objects
|
129
125
|
return false if objects.length != queries.length
|
130
126
|
i = 0
|
131
|
-
queries.each
|
127
|
+
queries.each do |p|
|
132
128
|
return false unless p.include?(actor, objects[i])
|
133
129
|
i += 1
|
134
|
-
|
130
|
+
end
|
135
131
|
true
|
136
132
|
end
|
137
133
|
|
134
|
+
# Return an instance of this Action if the actor can execute it with the
|
135
|
+
# provided tokens, or nil if the tokens are invalid.
|
136
|
+
#
|
137
|
+
# @param action [Gamefic::Entity]
|
138
|
+
# @param tokens [Array<String>]
|
139
|
+
# @return [self, nil]
|
138
140
|
def attempt actor, tokens
|
139
|
-
i = 0
|
140
141
|
result = []
|
141
142
|
matches = Gamefic::Query::Matches.new([], '', '')
|
142
|
-
queries.
|
143
|
-
return nil if tokens[i].nil?
|
143
|
+
queries.each_with_index do |p, i|
|
144
|
+
return nil if tokens[i].nil? && matches.remaining == ''
|
144
145
|
matches = p.resolve(actor, "#{matches.remaining} #{tokens[i]}".strip, continued: (i < queries.length - 1))
|
145
146
|
return nil if matches.objects.empty?
|
146
|
-
accepted = matches.objects.select{|o| p.accept?(o)}
|
147
|
+
accepted = matches.objects.select { |o| p.accept?(o) }
|
147
148
|
return nil if accepted.empty?
|
148
149
|
if p.ambiguous?
|
149
150
|
result.push accepted
|
@@ -151,20 +152,15 @@ module Gamefic
|
|
151
152
|
return nil if accepted.length != 1
|
152
153
|
result.push accepted.first
|
153
154
|
end
|
154
|
-
|
155
|
-
|
156
|
-
self.new(actor, result)
|
155
|
+
end
|
156
|
+
new(actor, result)
|
157
157
|
end
|
158
158
|
|
159
159
|
protected
|
160
160
|
|
161
|
-
|
162
|
-
@verb = sym
|
163
|
-
end
|
161
|
+
attr_writer :verb
|
164
162
|
|
165
|
-
|
166
|
-
@meta = bool
|
167
|
-
end
|
163
|
+
attr_writer :meta
|
168
164
|
end
|
169
165
|
end
|
170
166
|
end
|
data/lib/gamefic/active.rb
CHANGED
@@ -117,9 +117,7 @@ module Gamefic
|
|
117
117
|
#
|
118
118
|
# @return [String] The output that resulted from performing the command.
|
119
119
|
def quietly(*command)
|
120
|
-
if buffer_stack == 0
|
121
|
-
clear_buffer
|
122
|
-
end
|
120
|
+
clear_buffer if buffer_stack == 0
|
123
121
|
set_buffer_stack buffer_stack + 1
|
124
122
|
self.perform *command
|
125
123
|
set_buffer_stack buffer_stack - 1
|
@@ -152,10 +150,12 @@ module Gamefic
|
|
152
150
|
# introduction do |actor|
|
153
151
|
# actor[:has_eaten] = false # Initial value
|
154
152
|
# end
|
153
|
+
#
|
155
154
|
# respond :eat do |actor|
|
156
155
|
# actor.tell "You eat something."
|
157
156
|
# actor[:has_eaten] = true
|
158
157
|
# end
|
158
|
+
#
|
159
159
|
# respond :eat do |actor|
|
160
160
|
# # This version will be executed first because it was implemented last
|
161
161
|
# if actor[:has_eaten]
|
@@ -187,13 +187,14 @@ module Gamefic
|
|
187
187
|
# Use #prepare if you want to declare a scene to be started at the
|
188
188
|
# beginning of the next turn.
|
189
189
|
#
|
190
|
-
# @param new_scene [Class]
|
191
|
-
|
190
|
+
# @param new_scene [Class<Scene::Base>]
|
191
|
+
# @param data [Hash] Additional scene data
|
192
|
+
def cue new_scene, **data
|
192
193
|
@next_scene = nil
|
193
194
|
if new_scene.nil?
|
194
195
|
@scene = nil
|
195
196
|
else
|
196
|
-
@scene = new_scene.new(self, **
|
197
|
+
@scene = new_scene.new(self, **data)
|
197
198
|
@scene.start
|
198
199
|
end
|
199
200
|
end
|
@@ -202,10 +203,11 @@ module Gamefic
|
|
202
203
|
# next turn. As opposed to #cue, a prepared scene will not start until the
|
203
204
|
# current scene finishes.
|
204
205
|
#
|
205
|
-
# @param new_scene [Class]
|
206
|
-
|
206
|
+
# @param new_scene [Class<Scene::Base>]
|
207
|
+
# @oaram data [Hash] Additional scene data
|
208
|
+
def prepare new_scene, **data
|
207
209
|
@next_scene = new_scene
|
208
|
-
@next_options =
|
210
|
+
@next_options = data
|
209
211
|
end
|
210
212
|
|
211
213
|
# Return true if the character is expected to be in the specified scene on
|
@@ -219,9 +221,11 @@ module Gamefic
|
|
219
221
|
# Cue a conclusion. This method works like #cue, except it will raise a
|
220
222
|
# NotConclusionError if the scene is not a Scene::Conclusion.
|
221
223
|
#
|
222
|
-
|
223
|
-
|
224
|
-
|
224
|
+
# @param new_scene [Class<Scene::Base>]
|
225
|
+
# @oaram data [Hash] Additional scene data
|
226
|
+
def conclude new_scene, **data
|
227
|
+
raise NotConclusionError unless new_scene <= Scene::Conclusion
|
228
|
+
cue new_scene, **data
|
225
229
|
end
|
226
230
|
|
227
231
|
# True if the character is in a conclusion.
|
@@ -269,6 +273,8 @@ module Gamefic
|
|
269
273
|
@entered_scenes ||= []
|
270
274
|
end
|
271
275
|
|
276
|
+
# @param actions [Array<Gamefic::Action>]
|
277
|
+
# @param quietly [Boolean]
|
272
278
|
def execute_stack actions, quietly: false
|
273
279
|
return nil if actions.empty?
|
274
280
|
a = actions.first
|
@@ -44,6 +44,9 @@ class Array
|
|
44
44
|
# animals = ['a dog', 'a cat', 'a mouse']
|
45
45
|
# animals.join_and #=> 'a dog, a cat, and a mouse'
|
46
46
|
#
|
47
|
+
# @param sep [String] The separator for all but the last element
|
48
|
+
# @param andSep [String] The separator for the last element
|
49
|
+
# @param serial [Boolean] Use serial separators (e.g., serial commas)
|
47
50
|
# @return [String]
|
48
51
|
def join_and(sep = ', ', andSep = ' and ', serial = true)
|
49
52
|
if self.length < 3
|
data/lib/gamefic/element.rb
CHANGED
@@ -7,11 +7,15 @@ 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
|
+
# 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
|
15
19
|
post_initialize
|
16
20
|
yield self if block_given?
|
17
21
|
end
|
data/lib/gamefic/plot.rb
CHANGED
@@ -16,25 +16,22 @@ 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
|
-
# @param structure [Gamefic::Structure]
|
26
30
|
# @param metadata [Hash]
|
27
31
|
def initialize metadata: {}
|
28
|
-
Gamefic::Index.clear
|
29
32
|
@metadata = metadata
|
30
33
|
run_scripts
|
31
|
-
|
32
|
-
Gamefic::Index.stick
|
33
|
-
end
|
34
|
-
|
35
|
-
def player_class cls = nil
|
36
|
-
@player_class = cls unless cls.nil?
|
37
|
-
@player_class ||= Gamefic::Actor
|
34
|
+
@static = [self] + scene_classes + entities
|
38
35
|
end
|
39
36
|
|
40
37
|
# Get an Array of the Plot's current Syntaxes.
|
@@ -59,7 +56,8 @@ module Gamefic
|
|
59
56
|
.merge({
|
60
57
|
messages: p.messages,
|
61
58
|
last_prompt: p.last_prompt,
|
62
|
-
last_input: p.last_input
|
59
|
+
last_input: p.last_input,
|
60
|
+
queue: p.queue
|
63
61
|
})
|
64
62
|
.merge(p.state)
|
65
63
|
)
|
@@ -89,8 +87,8 @@ module Gamefic
|
|
89
87
|
end
|
90
88
|
call_player_update
|
91
89
|
call_update
|
92
|
-
subplots.
|
93
|
-
subplots.
|
90
|
+
subplots.delete_if(&:concluded?)
|
91
|
+
subplots.each(&:update)
|
94
92
|
end
|
95
93
|
|
96
94
|
# Send a message to a group of entities.
|
@@ -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
|