gamefic 2.0.2 → 2.0.3
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 +4 -0
- data/lib/gamefic.rb +0 -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 -6
- data/lib/gamefic/plot.rb +2 -8
- 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/multiple_choice.rb +1 -1
- data/lib/gamefic/serialize.rb +13 -0
- data/lib/gamefic/subplot.rb +3 -12
- 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 +0 -10
- data/lib/gamefic/world/playbook.rb +30 -40
- data/lib/gamefic/world/players.rb +18 -2
- metadata +14 -14
- data/lib/gamefic/plot/index.rb +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ac7da649347939f4c5baadc676151b8a8c73714552c48a290de75bd072bb801
|
4
|
+
data.tar.gz: 6d83150ed62df24a4056e3e54ff5b499bc5f7a69ebe8d5cfaddb261f64573af4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e068317caa9fbfb1f8a73bde4725f5df3f3bcad95ba87d44c8f973759cdc991e0fbbcea874fd451eacbd6ffd2ea2ade9144e61f2d358f31fec6a7f1e97ca3a10
|
7
|
+
data.tar.gz: 1ab813a5cb14307bc844c42424c9503bae6f73a48d12979a5369812619e5a2e80dfab2ac6181401045511286743fc20f35968bd46e5071cf24f296e3c1c20e36
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
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'
|
@@ -18,5 +17,4 @@ require "gamefic/syntax"
|
|
18
17
|
require 'gamefic/world'
|
19
18
|
require 'gamefic/scriptable'
|
20
19
|
require 'gamefic/plot'
|
21
|
-
require 'gamefic/plot/index'
|
22
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
@@ -12,12 +12,12 @@ module Gamefic
|
|
12
12
|
|
13
13
|
# @todo It would be nice if this initialization wasn't necessary.
|
14
14
|
def initialize(args = {})
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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?
|
21
21
|
end
|
22
22
|
|
23
23
|
def post_initialize
|
data/lib/gamefic/plot.rb
CHANGED
@@ -27,7 +27,6 @@ module Gamefic
|
|
27
27
|
|
28
28
|
exclude_from_serial [:@static]
|
29
29
|
|
30
|
-
# @param structure [Gamefic::Structure]
|
31
30
|
# @param metadata [Hash]
|
32
31
|
def initialize metadata: {}
|
33
32
|
@metadata = metadata
|
@@ -35,11 +34,6 @@ module Gamefic
|
|
35
34
|
@static = [self] + scene_classes + entities
|
36
35
|
end
|
37
36
|
|
38
|
-
def player_class cls = nil
|
39
|
-
@player_class = cls unless cls.nil?
|
40
|
-
@player_class ||= Gamefic::Actor
|
41
|
-
end
|
42
|
-
|
43
37
|
# Get an Array of the Plot's current Syntaxes.
|
44
38
|
#
|
45
39
|
# @return [Array<Syntax>]
|
@@ -93,8 +87,8 @@ module Gamefic
|
|
93
87
|
end
|
94
88
|
call_player_update
|
95
89
|
call_update
|
96
|
-
subplots.
|
97
|
-
subplots.
|
90
|
+
subplots.delete_if(&:concluded?)
|
91
|
+
subplots.each(&:update)
|
98
92
|
end
|
99
93
|
|
100
94
|
# Send a message to a group of entities.
|
data/lib/gamefic/query.rb
CHANGED
@@ -5,6 +5,7 @@ module Gamefic
|
|
5
5
|
autoload :Descendants, 'gamefic/query/descendants'
|
6
6
|
autoload :External, 'gamefic/query/external'
|
7
7
|
autoload :Family, 'gamefic/query/family'
|
8
|
+
autoload :Tree, 'gamefic/query/tree'
|
8
9
|
autoload :Itself, 'gamefic/query/itself'
|
9
10
|
autoload :Matches, 'gamefic/query/matches'
|
10
11
|
autoload :Parent, 'gamefic/query/parent'
|
data/lib/gamefic/query/base.rb
CHANGED
@@ -55,13 +55,18 @@ module Gamefic
|
|
55
55
|
result.include?(object)
|
56
56
|
end
|
57
57
|
|
58
|
+
# A ranking of how precise the query's arguments are.
|
59
|
+
#
|
60
|
+
# Query precision is a factor in calculating Action#rank.
|
61
|
+
#
|
62
|
+
# @return [Integer]
|
58
63
|
def precision
|
59
64
|
if @precision.nil?
|
60
65
|
@precision = 1
|
61
66
|
arguments.each { |a|
|
62
|
-
if a.
|
67
|
+
if a.is_a?(Class)
|
63
68
|
@precision += 100
|
64
|
-
elsif a.
|
69
|
+
elsif a.is_a?(Gamefic::Entity)
|
65
70
|
@precision += 1000
|
66
71
|
end
|
67
72
|
}
|
@@ -69,10 +74,7 @@ module Gamefic
|
|
69
74
|
end
|
70
75
|
@precision
|
71
76
|
end
|
72
|
-
|
73
|
-
def rank
|
74
|
-
precision
|
75
|
-
end
|
77
|
+
alias rank precision
|
76
78
|
|
77
79
|
def signature
|
78
80
|
"#{self.class.to_s.split('::').last.downcase}(#{simplify_arguments.join(', ')})"
|
@@ -83,18 +85,18 @@ module Gamefic
|
|
83
85
|
# @return [Boolean]
|
84
86
|
def accept?(entity)
|
85
87
|
result = true
|
86
|
-
arguments.each
|
87
|
-
if a.
|
88
|
-
|
89
|
-
elsif a.
|
90
|
-
|
91
|
-
elsif a.is_a?(Module)
|
92
|
-
|
88
|
+
arguments.each do |a|
|
89
|
+
result = if a.is_a?(Symbol)
|
90
|
+
(entity.send(a) != false)
|
91
|
+
elsif a.is_a?(Regexp)
|
92
|
+
!entity.to_s.match(a).nil?
|
93
|
+
elsif a.is_a?(Module) || a.is_a?(Class)
|
94
|
+
entity.is_a?(a)
|
93
95
|
else
|
94
|
-
|
96
|
+
(entity == a)
|
95
97
|
end
|
96
98
|
break if result == false
|
97
|
-
|
99
|
+
end
|
98
100
|
result
|
99
101
|
end
|
100
102
|
|
@@ -109,10 +111,10 @@ module Gamefic
|
|
109
111
|
return [] if entity.nil?
|
110
112
|
result = []
|
111
113
|
if entity.accessible?
|
112
|
-
entity.children.each
|
114
|
+
entity.children.each do |c|
|
113
115
|
result.push c
|
114
116
|
result.concat subquery_accessible(c)
|
115
|
-
|
117
|
+
end
|
116
118
|
end
|
117
119
|
result
|
118
120
|
end
|
@@ -121,7 +123,7 @@ module Gamefic
|
|
121
123
|
|
122
124
|
def simplify_arguments
|
123
125
|
arguments.map do |a|
|
124
|
-
if a.
|
126
|
+
if a.is_a?(Class) || a.is_a?(Object)
|
125
127
|
a.to_s.split('::').last.downcase
|
126
128
|
else
|
127
129
|
a.to_s.downcase
|
@@ -136,13 +138,12 @@ module Gamefic
|
|
136
138
|
def denest(objects, token)
|
137
139
|
parts = token.split(NEST_REGEXP)
|
138
140
|
current = parts.pop
|
139
|
-
last_result = objects.select{ |e| e.specified?(current) }
|
140
|
-
last_result = objects.select{ |e| e.specified?(current, fuzzy: true) } if last_result.empty?
|
141
|
-
|
142
|
-
while parts.length > 0
|
141
|
+
last_result = objects.select { |e| e.specified?(current) }
|
142
|
+
last_result = objects.select { |e| e.specified?(current, fuzzy: true) } if last_result.empty?
|
143
|
+
until parts.empty?
|
143
144
|
current = "#{parts.last} #{current}"
|
144
|
-
result = last_result.select{ |e| e.specified?(current) }
|
145
|
-
result = last_result.select{ |e| e.specified?(current, fuzzy: true) } if result.empty?
|
145
|
+
result = last_result.select { |e| e.specified?(current) }
|
146
|
+
result = last_result.select { |e| e.specified?(current, fuzzy: true) } if result.empty?
|
146
147
|
break if result.empty?
|
147
148
|
parts.pop
|
148
149
|
last_result = result
|
data/lib/gamefic/query/family.rb
CHANGED
data/lib/gamefic/query/text.rb
CHANGED
@@ -2,14 +2,15 @@ module Gamefic
|
|
2
2
|
module Query
|
3
3
|
class Text < Base
|
4
4
|
def initialize *arguments
|
5
|
-
arguments.each
|
6
|
-
if (a.kind_of?(Symbol)
|
5
|
+
arguments.each do |a|
|
6
|
+
if (a.kind_of?(Symbol) || a.kind_of?(String)) && !a.to_s.end_with?('?')
|
7
7
|
raise ArgumentError.new("Text query arguments can only be boolean method names (:method?) or regular expressions")
|
8
8
|
end
|
9
|
-
|
9
|
+
end
|
10
10
|
super
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
|
+
def resolve _subject, token, continued: false
|
13
14
|
return Matches.new([], '', token) unless accept?(token)
|
14
15
|
parts = token.split(Keywords::SPLIT_REGEXP)
|
15
16
|
cursor = []
|
@@ -22,20 +23,18 @@ module Gamefic
|
|
22
23
|
}
|
23
24
|
if continued
|
24
25
|
Matches.new([matches.join(' ')], matches.join(' '), parts[i..-1].join(' '))
|
26
|
+
elsif matches.length == parts.length
|
27
|
+
Matches.new([matches.join(' ')], matches.join(' '), '')
|
25
28
|
else
|
26
|
-
|
27
|
-
Matches.new([matches.join(' ')], matches.join(' '), '')
|
28
|
-
else
|
29
|
-
Matches.new([], '', parts.join(' '))
|
30
|
-
end
|
29
|
+
Matches.new([], '', parts.join(' '))
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
34
|
-
def include?
|
33
|
+
def include? _subject, token
|
35
34
|
accept?(token)
|
36
35
|
end
|
37
36
|
|
38
|
-
def accept?
|
37
|
+
def accept? entity
|
39
38
|
return false unless entity.kind_of?(String) and !entity.empty?
|
40
39
|
super
|
41
40
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
# Query to retrieve all of the subject's ancestors, siblings, and descendants.
|
4
|
+
#
|
5
|
+
class Tree < Family
|
6
|
+
def context_from(subject)
|
7
|
+
result = super
|
8
|
+
parent = subject.parent
|
9
|
+
until parent.nil?
|
10
|
+
result.unshift parent
|
11
|
+
parent = parent.parent
|
12
|
+
end
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/gamefic/serialize.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Gamefic
|
2
4
|
module Serialize
|
3
5
|
def to_serial(index = [])
|
@@ -83,6 +85,8 @@ class Object
|
|
83
85
|
return object
|
84
86
|
elsif self['class'] == 'Class'
|
85
87
|
return Gamefic::Serialize.string_to_constant(self['name'])
|
88
|
+
elsif self['class'] == 'Set'
|
89
|
+
return Set.new(self['data'].map { |el| el.from_serial(index) })
|
86
90
|
else
|
87
91
|
elematch = self['class'].match(/^#<ELE_([\d]+)>$/)
|
88
92
|
if elematch
|
@@ -208,3 +212,12 @@ class Hash
|
|
208
212
|
result
|
209
213
|
end
|
210
214
|
end
|
215
|
+
|
216
|
+
class Set
|
217
|
+
def to_serial(index = [])
|
218
|
+
{
|
219
|
+
'class' => 'Set',
|
220
|
+
'data' => to_a.map { |el| el.to_serial(index) }
|
221
|
+
}
|
222
|
+
end
|
223
|
+
end
|
data/lib/gamefic/subplot.rb
CHANGED
@@ -11,8 +11,9 @@ module Gamefic
|
|
11
11
|
attr_reader :plot
|
12
12
|
|
13
13
|
# @param plot [Gamefic::Plot]
|
14
|
-
# @param introduce [Gamefic::Actor]
|
15
|
-
# @param next_cue [Class<Gamefic::Scene::Base
|
14
|
+
# @param introduce [Gamefic::Actor, nil]
|
15
|
+
# @param next_cue [Class<Gamefic::Scene::Base>, nil]
|
16
|
+
# @param more [Hash]
|
16
17
|
def initialize plot, introduce: nil, next_cue: nil, **more
|
17
18
|
@plot = plot
|
18
19
|
@next_cue = next_cue
|
@@ -98,15 +99,5 @@ module Gamefic
|
|
98
99
|
#
|
99
100
|
def configure more
|
100
101
|
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
|
111
102
|
end
|
112
103
|
end
|
data/lib/gamefic/syntax.rb
CHANGED
data/lib/gamefic/version.rb
CHANGED
data/lib/gamefic/world.rb
CHANGED
@@ -96,7 +96,7 @@ module Gamefic
|
|
96
96
|
end
|
97
97
|
|
98
98
|
# Declare a dismabiguation response for actions.
|
99
|
-
# The
|
99
|
+
# The disambiguator is executed when an action expects an argument to
|
100
100
|
# reference a specific entity but its query matched more than one. For
|
101
101
|
# example, "red" might refer to either a red key or a red book.
|
102
102
|
#
|
@@ -144,7 +144,7 @@ module Gamefic
|
|
144
144
|
#
|
145
145
|
# @return [Array<String>]
|
146
146
|
def verbs
|
147
|
-
playbook.verbs.map
|
147
|
+
playbook.verbs.map(&:to_s).reject { |v| v.start_with?('_') }
|
148
148
|
end
|
149
149
|
|
150
150
|
# Get an Array of all Actions defined in the Plot.
|
@@ -164,20 +164,20 @@ module Gamefic
|
|
164
164
|
|
165
165
|
private
|
166
166
|
|
167
|
+
# @param queries [Array]
|
168
|
+
# @return [Array<Query::Base>]
|
167
169
|
def map_response_args queries
|
168
|
-
|
169
|
-
queries.each do |q|
|
170
|
+
queries.map do |q|
|
170
171
|
if q.is_a?(Regexp)
|
171
|
-
|
172
|
+
Gamefic::Query::Text.new(q)
|
172
173
|
elsif q.is_a?(Gamefic::Query::Base)
|
173
|
-
|
174
|
+
q
|
174
175
|
elsif q.is_a?(Gamefic::Element) || (q.is_a?(Class) && q <= Gamefic::Element)
|
175
|
-
|
176
|
+
get_default_query.new(q)
|
176
177
|
else
|
177
178
|
raise ArgumentError.new("Invalid argument for response: #{q}")
|
178
179
|
end
|
179
180
|
end
|
180
|
-
result
|
181
181
|
end
|
182
182
|
end
|
183
183
|
end
|
@@ -93,16 +93,6 @@ module Gamefic
|
|
93
93
|
def players
|
94
94
|
@players ||= []
|
95
95
|
end
|
96
|
-
|
97
|
-
# private
|
98
|
-
|
99
|
-
# def mark_static_entities
|
100
|
-
# @static_entity_length ||= entities.length
|
101
|
-
# end
|
102
|
-
|
103
|
-
# def static_entity_length
|
104
|
-
# @static_entity_length || 0
|
105
|
-
# end
|
106
96
|
end
|
107
97
|
end
|
108
98
|
end
|
@@ -3,6 +3,16 @@ module Gamefic
|
|
3
3
|
# A collection of rules for performing commands.
|
4
4
|
#
|
5
5
|
class Playbook
|
6
|
+
# An array of available syntaxes.
|
7
|
+
#
|
8
|
+
# @return [Array<Gamefic::Syntax>]
|
9
|
+
attr_reader :syntaxes
|
10
|
+
|
11
|
+
# An array of defined validators.
|
12
|
+
#
|
13
|
+
# @return [Array<Proc>]
|
14
|
+
attr_reader :validators
|
15
|
+
|
6
16
|
def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
|
7
17
|
@commands = commands
|
8
18
|
@syntaxes = syntaxes
|
@@ -10,13 +20,6 @@ module Gamefic
|
|
10
20
|
@disambiguator = disambiguator
|
11
21
|
end
|
12
22
|
|
13
|
-
# An array of available syntaxes.
|
14
|
-
#
|
15
|
-
# @return [Array<Gamefic::Syntax>]
|
16
|
-
def syntaxes
|
17
|
-
@syntaxes
|
18
|
-
end
|
19
|
-
|
20
23
|
# An array of available actions.
|
21
24
|
#
|
22
25
|
# @return [Array<Gamefic::Action>]
|
@@ -31,21 +34,14 @@ module Gamefic
|
|
31
34
|
@commands.keys
|
32
35
|
end
|
33
36
|
|
34
|
-
# An array of defined validators.
|
35
|
-
#
|
36
|
-
# @return [Array<Proc>]
|
37
|
-
def validators
|
38
|
-
@validators
|
39
|
-
end
|
40
|
-
|
41
37
|
# Get the action for handling ambiguous entity references.
|
42
38
|
#
|
43
39
|
def disambiguator
|
44
40
|
@disambiguator ||= Action.subclass(nil, Query::Base.new) do |actor, entities|
|
45
41
|
definites = []
|
46
|
-
entities.each
|
42
|
+
entities.each do |entity|
|
47
43
|
definites.push entity.definitely
|
48
|
-
|
44
|
+
end
|
49
45
|
actor.tell "I don't know which you mean: #{definites.join_or}."
|
50
46
|
end
|
51
47
|
end
|
@@ -66,7 +62,7 @@ module Gamefic
|
|
66
62
|
# Get an Array of all Actions associated with the specified verb.
|
67
63
|
#
|
68
64
|
# @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
|
69
|
-
# @return [Array<Action
|
65
|
+
# @return [Array<Class<Action>>] The verb's associated Actions
|
70
66
|
def actions_for verb
|
71
67
|
@commands[verb] || []
|
72
68
|
end
|
@@ -150,12 +146,8 @@ module Gamefic
|
|
150
146
|
# @return [Array<Gamefic::Action>]
|
151
147
|
def dispatch(actor, *command)
|
152
148
|
result = []
|
153
|
-
if command.length > 1
|
154
|
-
|
155
|
-
end
|
156
|
-
if result.empty?
|
157
|
-
result.concat dispatch_from_string(actor, command.join(' '))
|
158
|
-
end
|
149
|
+
result.concat dispatch_from_params(actor, command[0], command[1..-1]) if command.length > 1
|
150
|
+
result.concat dispatch_from_string(actor, command.join(' ')) if result.empty?
|
159
151
|
result
|
160
152
|
end
|
161
153
|
|
@@ -167,14 +159,13 @@ module Gamefic
|
|
167
159
|
def dispatch_from_string actor, text
|
168
160
|
result = []
|
169
161
|
commands = Syntax.tokenize(text, actor.syntaxes)
|
170
|
-
commands.each
|
171
|
-
|
172
|
-
available.each { |a|
|
162
|
+
commands.each do |c|
|
163
|
+
actions_for(c.verb).each do |a|
|
173
164
|
next if a.hidden?
|
174
165
|
o = a.attempt(actor, c.arguments)
|
175
166
|
result.unshift o unless o.nil?
|
176
|
-
|
177
|
-
|
167
|
+
end
|
168
|
+
end
|
178
169
|
sort_and_reduce_actions result
|
179
170
|
end
|
180
171
|
|
@@ -185,9 +176,9 @@ module Gamefic
|
|
185
176
|
def dispatch_from_params actor, verb, params
|
186
177
|
result = []
|
187
178
|
available = actions_for(verb)
|
188
|
-
available.each
|
179
|
+
available.each do |a|
|
189
180
|
result.unshift a.new(actor, params) if a.valid?(actor, params)
|
190
|
-
|
181
|
+
end
|
191
182
|
sort_and_reduce_actions result
|
192
183
|
end
|
193
184
|
|
@@ -217,38 +208,37 @@ module Gamefic
|
|
217
208
|
user_friendly = action.verb.to_s.gsub(/_/, ' ')
|
218
209
|
args = []
|
219
210
|
used_names = []
|
220
|
-
action.queries.each
|
211
|
+
action.queries.each do |_c|
|
221
212
|
num = 1
|
222
213
|
new_name = ":var"
|
223
214
|
while used_names.include? new_name
|
224
|
-
num
|
215
|
+
num += 1
|
225
216
|
new_name = ":var#{num}"
|
226
217
|
end
|
227
218
|
used_names.push new_name
|
228
219
|
user_friendly += " #{new_name}"
|
229
220
|
args.push new_name
|
230
|
-
|
221
|
+
end
|
231
222
|
add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}") unless action.verb.to_s.start_with?('_')
|
232
223
|
end
|
233
224
|
|
234
225
|
def add_syntax syntax
|
235
|
-
if @commands[syntax.verb]
|
236
|
-
|
237
|
-
end
|
226
|
+
raise "No actions exist for \"#{syntax.verb}\"" if @commands[syntax.verb].nil?
|
227
|
+
|
238
228
|
@syntaxes.unshift syntax
|
239
|
-
@syntaxes.uniq!
|
240
|
-
@syntaxes.sort!
|
229
|
+
@syntaxes.uniq!(&:signature)
|
230
|
+
@syntaxes.sort! do |a, b|
|
241
231
|
if a.token_count == b.token_count
|
242
232
|
# For syntaxes of the same length, length of action takes precedence
|
243
233
|
b.first_word <=> a.first_word
|
244
234
|
else
|
245
235
|
b.token_count <=> a.token_count
|
246
236
|
end
|
247
|
-
|
237
|
+
end
|
248
238
|
end
|
249
239
|
|
250
240
|
def sort_and_reduce_actions arr
|
251
|
-
arr.sort_by.with_index{|a, i| [a.rank, -i]}.reverse.uniq(&:class)
|
241
|
+
arr.sort_by.with_index { |a, i| [a.rank, -i]}.reverse.uniq(&:class)
|
252
242
|
end
|
253
243
|
end
|
254
244
|
end
|
@@ -2,6 +2,7 @@ module Gamefic
|
|
2
2
|
module World
|
3
3
|
module Players
|
4
4
|
include Gamefic::World::Entities
|
5
|
+
include Gamefic::World::Commands
|
5
6
|
|
6
7
|
# An array of entities that are currently connected to users.
|
7
8
|
#
|
@@ -10,12 +11,27 @@ module Gamefic
|
|
10
11
|
@players ||= []
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
+
def player_class cls = nil
|
15
|
+
STDERR.puts "Modifying player_class this way is deprecated. Use set_player_class instead" unless cls.nil?
|
16
|
+
@player_class = cls unless cls.nil?
|
17
|
+
@player_class ||= Gamefic::Actor
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param cls [Class]
|
21
|
+
def set_player_class cls
|
22
|
+
unless cls < Gamefic::Active && cls <= Gamefic::Entity
|
23
|
+
raise ArgumentError, "Player class must be an active entity"
|
24
|
+
end
|
25
|
+
@player_class = cls
|
26
|
+
end
|
27
|
+
|
28
|
+
# Make a character that a player will control on introduction.
|
14
29
|
#
|
15
30
|
# @return [Gamefic::Actor]
|
16
|
-
def
|
31
|
+
def make_player_character
|
17
32
|
cast player_class, name: 'yourself', synonyms: 'self myself you me', proper_named: true
|
18
33
|
end
|
34
|
+
alias get_player_character make_player_character
|
19
35
|
end
|
20
36
|
end
|
21
37
|
end
|
metadata
CHANGED
@@ -1,55 +1,55 @@
|
|
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.3
|
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-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '12.3'
|
20
|
-
- - "
|
20
|
+
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: '12.3'
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- - "
|
27
|
+
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '12.3'
|
30
|
-
- - "
|
30
|
+
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '12.3'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: rspec
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - ">="
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: 3.5.0
|
40
37
|
- - "~>"
|
41
38
|
- !ruby/object:Gem::Version
|
42
39
|
version: '3.5'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 3.5.0
|
43
43
|
type: :development
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- - ">="
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: 3.5.0
|
50
47
|
- - "~>"
|
51
48
|
- !ruby/object:Gem::Version
|
52
49
|
version: '3.5'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 3.5.0
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
54
|
name: simplecov
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,7 +96,6 @@ files:
|
|
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
|
100
99
|
- lib/gamefic/plot/snapshot.rb
|
101
100
|
- lib/gamefic/query.rb
|
102
101
|
- lib/gamefic/query/base.rb
|
@@ -109,6 +108,7 @@ files:
|
|
109
108
|
- lib/gamefic/query/parent.rb
|
110
109
|
- lib/gamefic/query/siblings.rb
|
111
110
|
- lib/gamefic/query/text.rb
|
111
|
+
- lib/gamefic/query/tree.rb
|
112
112
|
- lib/gamefic/scene.rb
|
113
113
|
- lib/gamefic/scene/activity.rb
|
114
114
|
- lib/gamefic/scene/base.rb
|
@@ -149,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
149
149
|
- !ruby/object:Gem::Version
|
150
150
|
version: '0'
|
151
151
|
requirements: []
|
152
|
-
rubygems_version: 3.
|
152
|
+
rubygems_version: 3.1.2
|
153
153
|
signing_key:
|
154
154
|
specification_version: 4
|
155
155
|
summary: Gamefic
|
data/lib/gamefic/plot/index.rb
DELETED
@@ -1,92 +0,0 @@
|
|
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
|