gamefic 2.1.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -4
- data/lib/gamefic/action.rb +5 -5
- data/lib/gamefic/active.rb +33 -66
- data/lib/gamefic/core_ext/array.rb +1 -1
- data/lib/gamefic/core_ext/string.rb +2 -8
- data/lib/gamefic/dispatcher.rb +76 -0
- data/lib/gamefic/element.rb +7 -8
- data/lib/gamefic/entity.rb +3 -3
- data/lib/gamefic/plot.rb +0 -1
- data/lib/gamefic/scene/activity.rb +1 -3
- data/lib/gamefic/serialize.rb +2 -26
- data/lib/gamefic/syntax.rb +24 -18
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic/world/commands.rb +0 -19
- data/lib/gamefic/world/playbook.rb +26 -62
- data/lib/gamefic.rb +2 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 361eaf767babb3c8b5d7e47616d326a85e1b42269da33c518de33f16f9568451
|
4
|
+
data.tar.gz: 96f4d79e833f93c41714c5781cf863d7e0b8832e90c746f6bafe6164e10095b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9ad845f6f0271da17b64268b911f5d3fe40fbfd7fd563b411f6ef02e15b8a117abcd924d3586072040f428aafc0a92827458639f9f2c0ba9fa4883af7f33e05
|
7
|
+
data.tar.gz: 6f65996ac0ee16805763dd25fa89b1809a89acd0e44cb5d55f83ce22636aeadb6e43b733a15d44e6a397a636d5abde899c332d3601af60bcc4322a0c0ad1fb72
|
data/CHANGELOG.md
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
-
|
1
|
+
## 2.2.0 - September 4, 2021
|
2
|
+
- Dynamically inherit default attributes
|
3
|
+
|
4
|
+
## 2.1.1 - July 23, 2021
|
2
5
|
- Remove gamefic/scene/custom autoload
|
3
6
|
|
4
|
-
|
7
|
+
## 2.1.0 - June 21, 2021
|
5
8
|
- Remove redundant MultipleChoice prompt
|
6
9
|
- Deprecate Scene::Custom
|
7
10
|
|
8
|
-
|
11
|
+
## 2.0.3 - December 14, 2020
|
9
12
|
- Remove unused Index class
|
10
13
|
- Active#conclude accepts data argument
|
11
14
|
|
12
|
-
|
15
|
+
## 2.0.2 - April 25, 2020
|
13
16
|
- Improved snapshot serialization
|
data/lib/gamefic/action.rb
CHANGED
@@ -123,10 +123,8 @@ module Gamefic
|
|
123
123
|
|
124
124
|
def valid? actor, objects
|
125
125
|
return false if objects.length != queries.length
|
126
|
-
|
127
|
-
queries.each do |p|
|
126
|
+
queries.each_with_index do |p, i|
|
128
127
|
return false unless p.include?(actor, objects[i])
|
129
|
-
i += 1
|
130
128
|
end
|
131
129
|
true
|
132
130
|
end
|
@@ -135,9 +133,11 @@ module Gamefic
|
|
135
133
|
# provided tokens, or nil if the tokens are invalid.
|
136
134
|
#
|
137
135
|
# @param action [Gamefic::Entity]
|
138
|
-
# @param
|
136
|
+
# @param command [Command]
|
139
137
|
# @return [self, nil]
|
140
|
-
def attempt actor,
|
138
|
+
def attempt actor, command
|
139
|
+
return nil if command.verb != verb
|
140
|
+
tokens = command.arguments
|
141
141
|
result = []
|
142
142
|
matches = Gamefic::Query::Matches.new([], '', '')
|
143
143
|
queries.each_with_index do |p, i|
|
data/lib/gamefic/active.rb
CHANGED
@@ -6,12 +6,6 @@ module Gamefic
|
|
6
6
|
# subclass that includes this module.
|
7
7
|
#
|
8
8
|
module Active
|
9
|
-
# The last action executed by the entity, as reported by the
|
10
|
-
# Active#performed method.
|
11
|
-
#
|
12
|
-
# @return [Gamefic::Action]
|
13
|
-
attr_reader :last_action
|
14
|
-
|
15
9
|
# The scene in which the entity is currently participating.
|
16
10
|
#
|
17
11
|
# @return [Gamefic::Scene::Base]
|
@@ -104,17 +98,29 @@ module Gamefic
|
|
104
98
|
# @example Send a command as a verb with parameters
|
105
99
|
# character.perform :take, @key
|
106
100
|
#
|
101
|
+
# @todo Modify this method so it only accepts a single command.
|
102
|
+
# Verbs with parameters should use Active#execute instead.
|
103
|
+
# It might be necessary to support command splats with a deprecation
|
104
|
+
# warning until version 3.
|
105
|
+
#
|
107
106
|
# @return [Gamefic::Action]
|
108
107
|
def perform(*command)
|
109
|
-
|
110
|
-
|
111
|
-
|
108
|
+
if command.length > 1
|
109
|
+
execute command.first, *command[1..-1]
|
110
|
+
else
|
111
|
+
dispatchers.push Dispatcher.dispatch(self, command.first.to_s)
|
112
|
+
proceed
|
113
|
+
dispatchers.pop
|
114
|
+
end
|
112
115
|
end
|
113
116
|
|
114
117
|
# Quietly perform a command.
|
115
118
|
# This method executes the command exactly as #perform does, except it
|
116
119
|
# buffers the resulting output instead of sending it to the user.
|
117
120
|
#
|
121
|
+
# @todo Modify this method so it only accepts a single command.
|
122
|
+
# See Active#perform for more information.
|
123
|
+
#
|
118
124
|
# @return [String] The output that resulted from performing the command.
|
119
125
|
def quietly(*command)
|
120
126
|
clear_buffer if buffer_stack == 0
|
@@ -137,9 +143,9 @@ module Gamefic
|
|
137
143
|
#
|
138
144
|
# @return [Gamefic::Action]
|
139
145
|
def execute(verb, *params, quietly: false)
|
140
|
-
|
141
|
-
|
142
|
-
|
146
|
+
dispatchers.push Dispatcher.dispatch_from_params(self, verb, params)
|
147
|
+
proceed quietly: quietly
|
148
|
+
dispatchers.pop
|
143
149
|
end
|
144
150
|
|
145
151
|
# Proceed to the next Action in the current stack.
|
@@ -166,20 +172,19 @@ module Gamefic
|
|
166
172
|
# end
|
167
173
|
#
|
168
174
|
def proceed quietly: false
|
169
|
-
return if
|
170
|
-
a =
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
end
|
176
|
-
set_buffer_stack(buffer_stack + 1)
|
177
|
-
end
|
178
|
-
a.execute
|
179
|
-
if quietly
|
180
|
-
set_buffer_stack(buffer_stack - 1)
|
181
|
-
@buffer
|
175
|
+
return if dispatchers.empty?
|
176
|
+
a = dispatchers.last.next
|
177
|
+
return if a.nil?
|
178
|
+
if quietly
|
179
|
+
if buffer_stack == 0
|
180
|
+
@buffer = ""
|
182
181
|
end
|
182
|
+
set_buffer_stack(buffer_stack + 1)
|
183
|
+
end
|
184
|
+
a.execute
|
185
|
+
if quietly
|
186
|
+
set_buffer_stack(buffer_stack - 1)
|
187
|
+
@buffer
|
183
188
|
end
|
184
189
|
end
|
185
190
|
|
@@ -235,14 +240,6 @@ module Gamefic
|
|
235
240
|
!scene.nil? && scene.kind_of?(Scene::Conclusion)
|
236
241
|
end
|
237
242
|
|
238
|
-
# Record the last action the entity executed. This method is typically
|
239
|
-
# called when the entity performs an action in response to user input.
|
240
|
-
#
|
241
|
-
def performed action
|
242
|
-
action.freeze
|
243
|
-
@last_action = action
|
244
|
-
end
|
245
|
-
|
246
243
|
def accessible?
|
247
244
|
false
|
248
245
|
end
|
@@ -270,37 +267,7 @@ module Gamefic
|
|
270
267
|
|
271
268
|
# @return [Array<Gamefic::Scene::Base>]
|
272
269
|
def entered_scenes
|
273
|
-
@entered_scenes ||= []
|
274
|
-
end
|
275
|
-
|
276
|
-
# @param actions [Array<Gamefic::Action>]
|
277
|
-
# @param quietly [Boolean]
|
278
|
-
def execute_stack actions, quietly: false
|
279
|
-
return nil if actions.empty?
|
280
|
-
a = actions.first
|
281
|
-
okay = true
|
282
|
-
unless a.meta?
|
283
|
-
playbooks.reverse.each do |playbook|
|
284
|
-
okay = validate_playbook playbook, a
|
285
|
-
break unless okay
|
286
|
-
end
|
287
|
-
end
|
288
|
-
if okay
|
289
|
-
performance_stack.push actions
|
290
|
-
proceed quietly: quietly
|
291
|
-
performance_stack.pop
|
292
|
-
end
|
293
|
-
a
|
294
|
-
end
|
295
|
-
|
296
|
-
def validate_playbook playbook, action
|
297
|
-
okay = true
|
298
|
-
playbook.validators.each { |v|
|
299
|
-
result = v.call(self, action.verb, action.parameters)
|
300
|
-
okay = (result != false)
|
301
|
-
break unless okay
|
302
|
-
}
|
303
|
-
okay
|
270
|
+
@entered_scenes ||= []
|
304
271
|
end
|
305
272
|
|
306
273
|
def buffer_stack
|
@@ -324,8 +291,8 @@ module Gamefic
|
|
324
291
|
@buffer = ''
|
325
292
|
end
|
326
293
|
|
327
|
-
def
|
328
|
-
@
|
294
|
+
def dispatchers
|
295
|
+
@dispatchers ||= []
|
329
296
|
end
|
330
297
|
end
|
331
298
|
end
|
@@ -6,15 +6,9 @@ class String
|
|
6
6
|
#
|
7
7
|
# @return [String] The capitalized text
|
8
8
|
def capitalize_first
|
9
|
-
"#{self[0,1].upcase}#{self[1,self.length]}"
|
10
|
-
end
|
11
|
-
|
12
|
-
# An alias for #capitalize_first.
|
13
|
-
#
|
14
|
-
# @return [String]
|
15
|
-
def cap_first
|
16
|
-
self.capitalize_first
|
9
|
+
"#{self[0, 1].upcase}#{self[1, self.length]}"
|
17
10
|
end
|
11
|
+
alias cap_first capitalize_first
|
18
12
|
|
19
13
|
# Get an array of words split by any whitespace.
|
20
14
|
#
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Gamefic
|
2
|
+
# The action selector for character commands.
|
3
|
+
#
|
4
|
+
class Dispatcher
|
5
|
+
# @param actor [Actor]
|
6
|
+
# @param commands [Array<Command>]
|
7
|
+
# @param actions [Array<Action>]
|
8
|
+
def initialize actor, commands = [], actions = []
|
9
|
+
@actor = actor
|
10
|
+
@commands = commands
|
11
|
+
@actions = actions
|
12
|
+
end
|
13
|
+
|
14
|
+
def merge dispatcher
|
15
|
+
commands.concat dispatcher.commands
|
16
|
+
actions.concat dispatcher.actions
|
17
|
+
end
|
18
|
+
|
19
|
+
def next
|
20
|
+
instance = nil
|
21
|
+
while instance.nil? && !@actions.empty?
|
22
|
+
action = actions.shift
|
23
|
+
commands.each do |cmd|
|
24
|
+
instance = action.attempt(actor, cmd)
|
25
|
+
if instance
|
26
|
+
unless instance.meta?
|
27
|
+
actor.playbooks.reverse.each do |playbook|
|
28
|
+
return nil unless validate_playbook(playbook, instance)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
break
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
instance
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param actor [Active]
|
39
|
+
# @param command [String]
|
40
|
+
# @return [Dispatcher]
|
41
|
+
def self.dispatch actor, command
|
42
|
+
group = actor.playbooks.reverse.map { |p| p.dispatch(actor, command) }
|
43
|
+
dispatcher = Dispatcher.new(actor)
|
44
|
+
group.each { |d| dispatcher.merge d }
|
45
|
+
dispatcher
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param actor [Active]
|
49
|
+
# @param verb [Symbol]
|
50
|
+
# @param params [Array<Object>]
|
51
|
+
# @return [Dispatcher]
|
52
|
+
def self.dispatch_from_params actor, verb, params
|
53
|
+
group = actor.playbooks.reverse.map { |p| p.dispatch_from_params(actor, verb, params) }
|
54
|
+
dispatcher = Dispatcher.new(actor)
|
55
|
+
group.each { |d| dispatcher.merge d }
|
56
|
+
dispatcher
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
# @return [Actor]
|
62
|
+
attr_reader :actor
|
63
|
+
|
64
|
+
# @return [Array<Command>]
|
65
|
+
attr_reader :commands
|
66
|
+
|
67
|
+
# @return [Array<Action>]
|
68
|
+
attr_reader :actions
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def validate_playbook playbook, action
|
73
|
+
playbook.validators.all? { |v| v.call(actor, action.verb, action.parameters) != false }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/gamefic/element.rb
CHANGED
@@ -7,13 +7,16 @@ module Gamefic
|
|
7
7
|
#
|
8
8
|
class Element
|
9
9
|
include Gamefic::Describable
|
10
|
-
# include Gamefic::Index
|
11
10
|
include Gamefic::Serialize
|
12
11
|
|
13
|
-
# @todo It would be nice if this initialization wasn't necessary.
|
14
12
|
def initialize(args = {})
|
15
|
-
|
16
|
-
|
13
|
+
klass = self.class
|
14
|
+
defaults = {}
|
15
|
+
while klass <= Element
|
16
|
+
defaults = klass.default_attributes.merge(defaults)
|
17
|
+
klass = klass.superclass
|
18
|
+
end
|
19
|
+
defaults.merge(args).each_pair do |k, v|
|
17
20
|
public_send "#{k}=", v
|
18
21
|
end
|
19
22
|
post_initialize
|
@@ -38,10 +41,6 @@ module Gamefic
|
|
38
41
|
def default_attributes
|
39
42
|
@default_attributes ||= {}
|
40
43
|
end
|
41
|
-
|
42
|
-
def inherited subclass
|
43
|
-
subclass.set_default default_attributes
|
44
|
-
end
|
45
44
|
end
|
46
45
|
end
|
47
46
|
end
|
data/lib/gamefic/entity.rb
CHANGED
@@ -13,10 +13,10 @@ module Gamefic
|
|
13
13
|
|
14
14
|
# Set the Entity's parent.
|
15
15
|
#
|
16
|
-
# @param node [Gamefic::Entity] The new parent.
|
16
|
+
# @param node [Gamefic::Entity, nil] The new parent.
|
17
17
|
def parent=(node)
|
18
|
-
if node
|
19
|
-
raise "Entity's parent must be an Entity"
|
18
|
+
if node && node.is_a?(Entity) == false
|
19
|
+
raise ArgumentError, "Entity's parent must be an Entity"
|
20
20
|
end
|
21
21
|
super
|
22
22
|
end
|
data/lib/gamefic/plot.rb
CHANGED
data/lib/gamefic/serialize.rb
CHANGED
@@ -32,13 +32,6 @@ module Gamefic
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
def self.instances
|
36
|
-
GC.start
|
37
|
-
result = []
|
38
|
-
ObjectSpace.each_object(Gamefic::Serialize) { |obj| result.push obj }
|
39
|
-
result
|
40
|
-
end
|
41
|
-
|
42
35
|
# @param string [String]
|
43
36
|
# @return [Object]
|
44
37
|
def self.string_to_constant string
|
@@ -71,7 +64,7 @@ class Object
|
|
71
64
|
end
|
72
65
|
|
73
66
|
def from_serial(index = [])
|
74
|
-
if self.is_a?(Hash)
|
67
|
+
if self.is_a?(Hash)
|
75
68
|
if self['instance']
|
76
69
|
elematch = self['instance'].match(/^#<ELE_([\d]+)>$/)
|
77
70
|
object = index[elematch[1].to_i]
|
@@ -110,25 +103,8 @@ class Object
|
|
110
103
|
return index.index(match[1].to_i) if match
|
111
104
|
match = self.match(/#<SYM:([a-z0-9_\?\!]+)>/i)
|
112
105
|
return match[1].to_sym if match
|
113
|
-
|
106
|
+
return nil if self == '#<UNKNOWN>'
|
114
107
|
self
|
115
|
-
elsif self.is_a?(Hash)
|
116
|
-
result = {}
|
117
|
-
unknown = false
|
118
|
-
self.each_pair do |k, v|
|
119
|
-
k2 = k.from_serial(index)
|
120
|
-
v2 = v.from_serial(index)
|
121
|
-
if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
|
122
|
-
unknown = true
|
123
|
-
break
|
124
|
-
end
|
125
|
-
result[k2] = v2
|
126
|
-
end
|
127
|
-
result = "#<UNKNOWN>" if unknown
|
128
|
-
result
|
129
|
-
elsif self && self != true
|
130
|
-
STDERR.puts "Unable to unserialize #{self.class}"
|
131
|
-
nil
|
132
108
|
else
|
133
109
|
# true, false, or nil
|
134
110
|
self
|
data/lib/gamefic/syntax.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'gamefic/command'
|
2
|
-
|
3
1
|
module Gamefic
|
4
2
|
class Syntax
|
5
3
|
attr_reader :token_count, :first_word, :verb, :template, :command
|
@@ -57,17 +55,18 @@ module Gamefic
|
|
57
55
|
m = text.match(@regexp)
|
58
56
|
return nil if m.nil?
|
59
57
|
arguments = []
|
60
|
-
# HACK: Skip the first word if the verb is not nil.
|
61
|
-
# This is ugly.
|
62
58
|
b = @verb.nil? ? 0 : 1
|
59
|
+
xverb = @verb
|
63
60
|
@replace.to_s.split_words[b..-1].each { |r|
|
64
61
|
if r.match(/^\{\$[0-9]+\}$/)
|
65
62
|
arguments.push m[r[2..-2].to_i]
|
63
|
+
elsif arguments.empty? && xverb.nil?
|
64
|
+
xverb = r.to_sym
|
66
65
|
else
|
67
66
|
arguments.push r
|
68
67
|
end
|
69
68
|
}
|
70
|
-
Command.new
|
69
|
+
Command.new xverb, arguments
|
71
70
|
end
|
72
71
|
|
73
72
|
# Determine if the specified text matches the syntax's expected pattern.
|
@@ -90,25 +89,32 @@ module Gamefic
|
|
90
89
|
signature == other.signature
|
91
90
|
end
|
92
91
|
|
93
|
-
|
92
|
+
def eql?(other)
|
93
|
+
self == other
|
94
|
+
end
|
95
|
+
|
96
|
+
def hash
|
97
|
+
signature.hash
|
98
|
+
end
|
99
|
+
|
100
|
+
# Tokenize an Array of Commands from the specified text. The resulting
|
101
|
+
# array is in descending order of specificity, i.e., most to least matched
|
102
|
+
# tokens.
|
94
103
|
#
|
95
104
|
# @param text [String] The text to tokenize.
|
96
105
|
# @param syntaxes [Array<Gamefic::Syntax>] The Syntaxes to use.
|
97
106
|
# @return [Array<Gamefic::Command>] The tokenized commands.
|
98
107
|
def self.tokenize text, syntaxes
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
b.arguments.length <=> a.arguments.length
|
108
|
+
syntaxes
|
109
|
+
.map { |syn| syn.tokenize(text) }
|
110
|
+
.reject(&:nil?)
|
111
|
+
.sort do |a, b|
|
112
|
+
if a.arguments.length == b.arguments.length
|
113
|
+
b.verb.to_s <=> a.verb.to_s
|
114
|
+
else
|
115
|
+
b.arguments.length <=> a.arguments.length
|
116
|
+
end
|
109
117
|
end
|
110
|
-
}
|
111
|
-
matches
|
112
118
|
end
|
113
119
|
end
|
114
120
|
end
|
data/lib/gamefic/version.rb
CHANGED
@@ -95,25 +95,6 @@ module Gamefic
|
|
95
95
|
playbook.meta command, *queries, &proc
|
96
96
|
end
|
97
97
|
|
98
|
-
# Declare a dismabiguation response for actions.
|
99
|
-
# The disambiguator is executed when an action expects an argument to
|
100
|
-
# reference a specific entity but its query matched more than one. For
|
101
|
-
# example, "red" might refer to either a red key or a red book.
|
102
|
-
#
|
103
|
-
# If a disambiguator is not defined, the playbook will use its default
|
104
|
-
# implementation.
|
105
|
-
#
|
106
|
-
# @example Tell the player the list of ambiguous entities.
|
107
|
-
# disambiguate do |actor, entities|
|
108
|
-
# actor.tell "I don't know which you mean: #{entities.join_or}."
|
109
|
-
# end
|
110
|
-
#
|
111
|
-
# @yieldparam [Gamefic::Actor]
|
112
|
-
# @yieldparam [Array<Gamefic::Entity>]
|
113
|
-
def disambiguate &block
|
114
|
-
playbook.disambiguate &block
|
115
|
-
end
|
116
|
-
|
117
98
|
# Validate an order before a character can execute its command.
|
118
99
|
#
|
119
100
|
# @yieldparam [Gamefic::Director::Order]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Gamefic
|
2
4
|
module World
|
3
5
|
# A collection of rules for performing commands.
|
@@ -13,11 +15,14 @@ module Gamefic
|
|
13
15
|
# @return [Array<Proc>]
|
14
16
|
attr_reader :validators
|
15
17
|
|
16
|
-
|
18
|
+
# @param commands [Hash]
|
19
|
+
# @param syntaxes [Array<Syntax>, Set<Syntax>]
|
20
|
+
# @param validators [Array]
|
21
|
+
def initialize commands: {}, syntaxes: [], validators: []
|
17
22
|
@commands = commands
|
18
|
-
@
|
23
|
+
@syntax_set = syntaxes.to_set
|
24
|
+
sort_syntaxes
|
19
25
|
@validators = validators
|
20
|
-
@disambiguator = disambiguator
|
21
26
|
end
|
22
27
|
|
23
28
|
# An array of available actions.
|
@@ -34,25 +39,6 @@ module Gamefic
|
|
34
39
|
@commands.keys
|
35
40
|
end
|
36
41
|
|
37
|
-
# Get the action for handling ambiguous entity references.
|
38
|
-
#
|
39
|
-
def disambiguator
|
40
|
-
@disambiguator ||= Action.subclass(nil, Query::Base.new) do |actor, entities|
|
41
|
-
definites = []
|
42
|
-
entities.each do |entity|
|
43
|
-
definites.push entity.definitely
|
44
|
-
end
|
45
|
-
actor.tell "I don't know which you mean: #{definites.join_or}."
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Set the action for handling ambiguous entity references.
|
50
|
-
#
|
51
|
-
def disambiguate &block
|
52
|
-
@disambiguator = Action.subclass(nil, Query::Base.new, meta: true, &block)
|
53
|
-
@disambiguator
|
54
|
-
end
|
55
|
-
|
56
42
|
# Add a block that determines whether an action can be executed.
|
57
43
|
#
|
58
44
|
def validate &block
|
@@ -138,35 +124,16 @@ module Gamefic
|
|
138
124
|
syn
|
139
125
|
end
|
140
126
|
|
141
|
-
# Get
|
142
|
-
#
|
143
|
-
# The command can either be a single string (e.g., "examine book") or a
|
144
|
-
# list of tokens (e.g., :examine, @book).
|
145
|
-
#
|
146
|
-
# @return [Array<Gamefic::Action>]
|
147
|
-
def dispatch(actor, *command)
|
148
|
-
result = []
|
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?
|
151
|
-
result
|
152
|
-
end
|
153
|
-
|
154
|
-
# Get an array of actions, derived from the specified command, that the
|
155
|
-
# actor can potentially execute.
|
156
|
-
# The command should be a plain-text string, e.g., "examine the book."
|
127
|
+
# Get a Dispatcher to select actions that can potentially be executed
|
128
|
+
# from the specified command string.
|
157
129
|
#
|
158
|
-
# @
|
159
|
-
|
160
|
-
|
130
|
+
# @param actor [Actor]
|
131
|
+
# @param text [String]
|
132
|
+
# @return [Dispatcher]
|
133
|
+
def dispatch(actor, text)
|
161
134
|
commands = Syntax.tokenize(text, actor.syntaxes)
|
162
|
-
commands.
|
163
|
-
|
164
|
-
next if a.hidden?
|
165
|
-
o = a.attempt(actor, c.arguments)
|
166
|
-
result.unshift o unless o.nil?
|
167
|
-
end
|
168
|
-
end
|
169
|
-
sort_and_reduce_actions result
|
135
|
+
actions = commands.flat_map { |cmd| actions_for(cmd.verb).reject(&:hidden?) }
|
136
|
+
Dispatcher.new(actor, commands, sort_and_reduce_actions(actions))
|
170
137
|
end
|
171
138
|
|
172
139
|
# Get an array of actions, derived from the specified verb and params,
|
@@ -174,12 +141,8 @@ module Gamefic
|
|
174
141
|
#
|
175
142
|
# @return [Array<Gamefic::Action>]
|
176
143
|
def dispatch_from_params actor, verb, params
|
177
|
-
result = []
|
178
144
|
available = actions_for(verb)
|
179
|
-
|
180
|
-
result.unshift a.new(actor, params) if a.valid?(actor, params)
|
181
|
-
end
|
182
|
-
sort_and_reduce_actions result
|
145
|
+
Dispatcher.new(actor, [Command.new(verb, params)], sort_and_reduce_actions(available))
|
183
146
|
end
|
184
147
|
|
185
148
|
# Duplicate the playbook.
|
@@ -224,22 +187,23 @@ module Gamefic
|
|
224
187
|
|
225
188
|
def add_syntax syntax
|
226
189
|
raise "No actions exist for \"#{syntax.verb}\"" if @commands[syntax.verb].nil?
|
190
|
+
sort_syntaxes if @syntax_set.add?(syntax)
|
191
|
+
end
|
227
192
|
|
228
|
-
|
229
|
-
|
230
|
-
|
193
|
+
def sort_and_reduce_actions arr
|
194
|
+
arr.sort_by.with_index { |a, i| [a.rank, i] }.reverse.uniq
|
195
|
+
end
|
196
|
+
|
197
|
+
def sort_syntaxes
|
198
|
+
@syntaxes = @syntax_set.sort do |a, b|
|
231
199
|
if a.token_count == b.token_count
|
232
|
-
# For syntaxes of the same length,
|
200
|
+
# For syntaxes of the same length, sort first word
|
233
201
|
b.first_word <=> a.first_word
|
234
202
|
else
|
235
203
|
b.token_count <=> a.token_count
|
236
204
|
end
|
237
205
|
end
|
238
206
|
end
|
239
|
-
|
240
|
-
def sort_and_reduce_actions arr
|
241
|
-
arr.sort_by.with_index { |a, i| [a.rank, -i]}.reverse.uniq(&:class)
|
242
|
-
end
|
243
207
|
end
|
244
208
|
end
|
245
209
|
end
|
data/lib/gamefic.rb
CHANGED
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.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fred Snyder
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -88,6 +88,7 @@ files:
|
|
88
88
|
- lib/gamefic/core_ext/array.rb
|
89
89
|
- lib/gamefic/core_ext/string.rb
|
90
90
|
- lib/gamefic/describable.rb
|
91
|
+
- lib/gamefic/dispatcher.rb
|
91
92
|
- lib/gamefic/element.rb
|
92
93
|
- lib/gamefic/entity.rb
|
93
94
|
- lib/gamefic/keywords.rb
|