gamefic 3.4.0 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +1 -1
- data/lib/gamefic/active/epic.rb +1 -0
- data/lib/gamefic/active.rb +4 -0
- data/lib/gamefic/chapter.rb +25 -42
- data/lib/gamefic/command.rb +40 -1
- data/lib/gamefic/dispatcher.rb +5 -31
- data/lib/gamefic/entity.rb +26 -0
- data/lib/gamefic/narrative.rb +9 -5
- data/lib/gamefic/plot.rb +5 -0
- data/lib/gamefic/proxy.rb +46 -0
- data/lib/gamefic/query/base.rb +28 -12
- data/lib/gamefic/query/general.rb +3 -12
- data/lib/gamefic/query/result.rb +4 -1
- data/lib/gamefic/query/scoped.rb +1 -18
- data/lib/gamefic/query/text.rb +13 -12
- data/lib/gamefic/response.rb +65 -34
- data/lib/gamefic/scanner/base.rb +44 -0
- data/lib/gamefic/scanner/fuzzy.rb +17 -0
- data/lib/gamefic/scanner/fuzzy_nesting.rb +14 -0
- data/lib/gamefic/scanner/nesting.rb +39 -0
- data/lib/gamefic/scanner/result.rb +50 -0
- data/lib/gamefic/scanner/strict.rb +31 -0
- data/lib/gamefic/scanner.rb +33 -111
- data/lib/gamefic/scope/descendants.rb +16 -0
- data/lib/gamefic/scope/family.rb +31 -8
- data/lib/gamefic/scope.rb +1 -0
- data/lib/gamefic/scriptable/actions.rb +4 -23
- data/lib/gamefic/scriptable/entities.rb +4 -1
- data/lib/gamefic/scriptable/plot_proxies.rb +16 -0
- data/lib/gamefic/scriptable/proxies.rb +31 -0
- data/lib/gamefic/scriptable/queries.rb +15 -7
- data/lib/gamefic/scriptable/scenes.rb +7 -1
- data/lib/gamefic/scriptable.rb +67 -15
- data/lib/gamefic/stage.rb +2 -2
- data/lib/gamefic/subplot.rb +27 -1
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic.rb +1 -1
- metadata +12 -4
- data/lib/gamefic/composer.rb +0 -70
- data/lib/gamefic/scriptable/proxy.rb +0 -69
data/lib/gamefic/response.rb
CHANGED
@@ -8,19 +8,18 @@ module Gamefic
|
|
8
8
|
# @return [Symbol]
|
9
9
|
attr_reader :verb
|
10
10
|
|
11
|
-
# @return [Array<Query::Base>]
|
11
|
+
# @return [Array<Query::Base, Query::Text>]
|
12
12
|
attr_reader :queries
|
13
13
|
|
14
14
|
# @param verb [Symbol]
|
15
15
|
# @param narrative [Narrative]
|
16
|
-
# @param
|
16
|
+
# @param args [Array<Object>]
|
17
17
|
# @param meta [Boolean]
|
18
18
|
# @param block [Proc]
|
19
|
-
def initialize verb, narrative, *
|
19
|
+
def initialize verb, narrative, *args, meta: false, &block
|
20
20
|
@verb = verb
|
21
|
-
@queries =
|
21
|
+
@queries = map_queries(args, narrative)
|
22
22
|
@meta = meta
|
23
|
-
@block = block
|
24
23
|
@callback = Callback.new(narrative, block)
|
25
24
|
end
|
26
25
|
|
@@ -57,53 +56,85 @@ module Gamefic
|
|
57
56
|
# @param actor [Active]
|
58
57
|
# @param command [Command]
|
59
58
|
def accept? actor, command
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
return false unless query.accept?(actor, command.arguments[idx])
|
64
|
-
end
|
65
|
-
|
66
|
-
true
|
59
|
+
command.verb == verb &&
|
60
|
+
command.arguments.length == queries.length &&
|
61
|
+
queries.zip(command.arguments).all? { |query, argument| query.accept?(actor, argument) }
|
67
62
|
end
|
68
63
|
|
69
64
|
def execute *args
|
70
|
-
@callback.run
|
65
|
+
@callback.run(*args)
|
71
66
|
end
|
72
67
|
|
73
68
|
def precision
|
74
69
|
@precision ||= calculate_precision
|
75
70
|
end
|
76
71
|
|
72
|
+
# Turn an actor and an expression into a command by matching the
|
73
|
+
# expression's tokens to queries. Return nil if the expression
|
74
|
+
# could not be matched.
|
75
|
+
#
|
76
|
+
# @param actor [Actor]
|
77
|
+
# @param expression [Expression]
|
78
|
+
# @return [Command, nil]
|
79
|
+
def to_command actor, expression
|
80
|
+
return nil unless expression.verb == verb && expression.tokens.length <= queries.length
|
81
|
+
|
82
|
+
results = filter(actor, expression)
|
83
|
+
return nil unless results
|
84
|
+
|
85
|
+
Command.new(
|
86
|
+
verb,
|
87
|
+
results.map(&:match),
|
88
|
+
results.sum(&:strictness),
|
89
|
+
precision
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
77
93
|
private
|
78
94
|
|
95
|
+
def filter actor, expression
|
96
|
+
remainder = ''
|
97
|
+
result = queries.zip(expression.tokens)
|
98
|
+
.map do |query, token|
|
99
|
+
token = "#{remainder} #{token}".strip
|
100
|
+
result = query.filter(actor, token)
|
101
|
+
return nil unless result.match
|
102
|
+
|
103
|
+
remainder = result.remainder
|
104
|
+
result
|
105
|
+
end
|
106
|
+
result if remainder.empty?
|
107
|
+
end
|
108
|
+
|
79
109
|
def generate_default_syntax
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
queries.each do |_c|
|
84
|
-
num = 1
|
85
|
-
new_name = ":var"
|
86
|
-
while used_names.include? new_name
|
87
|
-
num += 1
|
88
|
-
new_name = ":var#{num}"
|
89
|
-
end
|
90
|
-
used_names.push new_name
|
91
|
-
user_friendly += " #{new_name}"
|
92
|
-
args.push new_name
|
93
|
-
end
|
94
|
-
Syntax.new(user_friendly.strip, "#{verb} #{args.join(' ')}".strip)
|
110
|
+
args = queries.length.times.map { |num| num.zero? ? ':var' : ":var#{num + 1}" }
|
111
|
+
tmpl = "#{verb} #{args.join(' ')}".strip
|
112
|
+
Syntax.new(tmpl.gsub('_', ' '), tmpl)
|
95
113
|
end
|
96
114
|
|
97
115
|
def calculate_precision
|
98
|
-
total =
|
99
|
-
|
100
|
-
total -= 1000 if verb.nil?
|
116
|
+
total = queries.sum(&:precision)
|
117
|
+
total -= 1000 unless verb
|
101
118
|
total
|
102
119
|
end
|
103
120
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
121
|
+
def map_queries args, narrative
|
122
|
+
args.map do |arg|
|
123
|
+
select_query(arg, narrative).tap { |qry| qry.narrative = narrative }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def select_query arg, narrative
|
128
|
+
case arg
|
129
|
+
when Entity, Class, Module, Proc, Proxy
|
130
|
+
narrative.available(arg)
|
131
|
+
when String, Regexp
|
132
|
+
narrative.plaintext(arg)
|
133
|
+
when Query::Base, Query::Text
|
134
|
+
arg
|
135
|
+
else
|
136
|
+
raise ArgumentError, "invalid argument in response: #{arg.inspect}"
|
137
|
+
end
|
107
138
|
end
|
108
139
|
end
|
109
140
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# A base class for scanners that match tokens to entities.
|
6
|
+
#
|
7
|
+
class Base
|
8
|
+
# @return [Array<Entity>]
|
9
|
+
attr_reader :selection
|
10
|
+
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :token
|
13
|
+
|
14
|
+
# @param selection [Array<Entity>]
|
15
|
+
# @param token [String]
|
16
|
+
def initialize selection, token
|
17
|
+
@selection = selection
|
18
|
+
@token = token
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Result]
|
22
|
+
def scan
|
23
|
+
unmatched_result
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param selection [Array<Entity>]
|
27
|
+
# @param token [String]
|
28
|
+
# @return [Result]
|
29
|
+
def self.scan selection, token
|
30
|
+
new(selection, token).scan
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def unmatched_result
|
36
|
+
Result.unmatched(selection, token, self.class)
|
37
|
+
end
|
38
|
+
|
39
|
+
def matched_result matched, remainder
|
40
|
+
Result.new(selection, token, matched, remainder, self.class)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# Fuzzy token matching.
|
6
|
+
#
|
7
|
+
# An entity will match a word in a fuzzy scan if it matches the beginning
|
8
|
+
# of one of the entity's keywords, e.g., `pen` is a fuzzy token match for
|
9
|
+
# the keyword `pencil`.
|
10
|
+
#
|
11
|
+
class Fuzzy < Strict
|
12
|
+
def match_word available, word
|
13
|
+
available.select { |obj| obj.keywords.any? { |wrd| wrd.start_with?(word) } }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# Fuzzy scanning for entities inside of other entities, e.g., `soc in draw`
|
6
|
+
# would match `sock in drawer`.
|
7
|
+
#
|
8
|
+
class FuzzyNesting < Nesting
|
9
|
+
def subprocessor
|
10
|
+
Fuzzy
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# Strict scanning for entities inside of other entities, e.g., `sock inside drawer`.
|
6
|
+
#
|
7
|
+
class Nesting < Base
|
8
|
+
NEST_REGEXP = / in | on | of | from | inside | inside of | from inside | off | out | out of /.freeze
|
9
|
+
|
10
|
+
def subprocessor
|
11
|
+
Strict
|
12
|
+
end
|
13
|
+
|
14
|
+
def scan
|
15
|
+
return Result.unmatched(selection, token, self.class) unless token =~ NEST_REGEXP
|
16
|
+
|
17
|
+
denest selection, token
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def denest objects, token
|
23
|
+
near = objects
|
24
|
+
far = objects
|
25
|
+
parts = token.split(NEST_REGEXP)
|
26
|
+
until parts.empty?
|
27
|
+
current = parts.pop
|
28
|
+
last_result = subprocessor.scan(near, current)
|
29
|
+
last_result = subprocessor.scan(far, current) if last_result.matched.empty? && near != far
|
30
|
+
return Result.unmatched(selection, token, self.class) if last_result.matched.empty? || last_result.matched.length > 1
|
31
|
+
|
32
|
+
near = last_result.matched.first.children & objects
|
33
|
+
far = last_result.matched.first.flatten & objects
|
34
|
+
end
|
35
|
+
last_result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# The result of an attempt to scan objects against a token in a Scanner. It
|
6
|
+
# provides an array of matching objects, the text that matched them, and the
|
7
|
+
# text that remains unmatched.
|
8
|
+
#
|
9
|
+
class Result
|
10
|
+
# The scanned objects
|
11
|
+
#
|
12
|
+
# @return [Array<Entity>, String, Regexp]
|
13
|
+
attr_reader :scanned
|
14
|
+
|
15
|
+
# The scanned token
|
16
|
+
#
|
17
|
+
# @return [String]
|
18
|
+
attr_reader :token
|
19
|
+
|
20
|
+
# The matched objects
|
21
|
+
#
|
22
|
+
# @return [Array<Entity>, String]
|
23
|
+
attr_reader :matched
|
24
|
+
alias match matched
|
25
|
+
|
26
|
+
# The remaining (unmatched) portion of the token
|
27
|
+
#
|
28
|
+
# @return [String]
|
29
|
+
attr_reader :remainder
|
30
|
+
|
31
|
+
attr_reader :processor
|
32
|
+
|
33
|
+
def initialize scanned, token, matched, remainder, processor
|
34
|
+
@scanned = scanned
|
35
|
+
@token = token
|
36
|
+
@matched = matched
|
37
|
+
@remainder = remainder
|
38
|
+
@processor = processor
|
39
|
+
end
|
40
|
+
|
41
|
+
def strictness
|
42
|
+
@strictness ||= Scanner.strictness(processor)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.unmatched scanned, token, processor
|
46
|
+
new(scanned, token, [], token, processor)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# Strict token matching.
|
6
|
+
#
|
7
|
+
# An entity will only match a word in a strict scan if the entire word
|
8
|
+
# matches one of the entity's keywords.
|
9
|
+
#
|
10
|
+
class Strict < Base
|
11
|
+
# @return [Result]
|
12
|
+
def scan
|
13
|
+
words = token.keywords
|
14
|
+
available = selection.clone
|
15
|
+
filtered = []
|
16
|
+
words.each_with_index do |word, idx|
|
17
|
+
tested = match_word(available, word)
|
18
|
+
return matched_result(filtered, words[idx..].join(' ')) if tested.empty?
|
19
|
+
|
20
|
+
filtered = tested
|
21
|
+
available = filtered
|
22
|
+
end
|
23
|
+
matched_result filtered, ''
|
24
|
+
end
|
25
|
+
|
26
|
+
def match_word available, word
|
27
|
+
available.select { |obj| obj.keywords.include?(word) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/gamefic/scanner.rb
CHANGED
@@ -1,132 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'gamefic/scanner/result'
|
4
|
+
require 'gamefic/scanner/base'
|
5
|
+
require 'gamefic/scanner/strict'
|
6
|
+
require 'gamefic/scanner/fuzzy'
|
7
|
+
require 'gamefic/scanner/nesting'
|
8
|
+
require 'gamefic/scanner/fuzzy_nesting'
|
9
|
+
|
3
10
|
module Gamefic
|
4
11
|
# A module for matching objects to tokens.
|
5
12
|
#
|
6
13
|
module Scanner
|
7
|
-
|
8
|
-
|
9
|
-
# The result of an attempt to scan objects against a token in a Scanner. It
|
10
|
-
# provides an array of matching objects, the text that matched them, and the
|
11
|
-
# text that remains unmatched.
|
12
|
-
#
|
13
|
-
class Result
|
14
|
-
# The scanned objects
|
15
|
-
#
|
16
|
-
# @return [Array<Entity>, String, Regexp]
|
17
|
-
attr_reader :scanned
|
18
|
-
|
19
|
-
# The scanned token
|
20
|
-
#
|
21
|
-
# @return [String]
|
22
|
-
attr_reader :token
|
23
|
-
|
24
|
-
# The matched objects
|
25
|
-
#
|
26
|
-
# @return [Array<Entity>, String]
|
27
|
-
attr_reader :matched
|
28
|
-
|
29
|
-
# The remaining (unmatched) portion of the token
|
30
|
-
#
|
31
|
-
# @return [String]
|
32
|
-
attr_reader :remainder
|
33
|
-
|
34
|
-
def initialize scanned, token, matched, remainder
|
35
|
-
@scanned = scanned
|
36
|
-
@token = token
|
37
|
-
@matched = matched
|
38
|
-
@remainder = remainder
|
39
|
-
end
|
40
|
-
end
|
14
|
+
DEFAULT_PROCESSORS = [Nesting, Strict, FuzzyNesting, Fuzzy].freeze
|
41
15
|
|
42
16
|
# Scan entities against a token.
|
43
17
|
#
|
44
|
-
# @param selection [Array<Entity
|
18
|
+
# @param selection [Array<Entity>]
|
45
19
|
# @param token [String]
|
46
20
|
# @return [Result]
|
47
21
|
def self.scan selection, token
|
48
|
-
|
49
|
-
|
22
|
+
result = nil
|
23
|
+
processors.each do |processor|
|
24
|
+
result = processor.scan(selection, token)
|
25
|
+
break unless result.matched.empty?
|
26
|
+
end
|
27
|
+
result
|
50
28
|
end
|
51
29
|
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
30
|
+
# Select the scanner processors to use in entity queries. Each processor
|
31
|
+
# will be used in order until one of them returns matches. The default
|
32
|
+
# processor list is `DEFAULT_PROCESSORS`.
|
33
|
+
#
|
34
|
+
# Processor classes should be in order from most to least strict rules
|
35
|
+
# for matching tokens to entities.
|
36
|
+
#
|
37
|
+
# @param klasses [Array<Class<Base>>]
|
38
|
+
# @return [Array<Class<Base>>]
|
39
|
+
def self.use *klasses
|
40
|
+
processors.replace klasses.flatten
|
59
41
|
end
|
60
42
|
|
61
|
-
# @
|
62
|
-
|
63
|
-
|
64
|
-
def self.fuzzy selection, token
|
65
|
-
return scan_text(selection, token) unless selection.is_a?(Array)
|
66
|
-
|
67
|
-
scan_strict_or_fuzzy(selection, token, :select_fuzzy)
|
43
|
+
# @return [Array<Class<Base>>]
|
44
|
+
def self.processors
|
45
|
+
@processors ||= []
|
68
46
|
end
|
69
47
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def scan_strict_or_fuzzy objects, token, method
|
74
|
-
if nested?(token) && objects.all?(&:children)
|
75
|
-
denest(objects, token)
|
76
|
-
else
|
77
|
-
words = token.keywords
|
78
|
-
available = objects.clone
|
79
|
-
filtered = []
|
80
|
-
words.each_with_index do |word, idx|
|
81
|
-
tested = send(method, available, word)
|
82
|
-
return Result.new(objects, token, filtered, words[idx..].join(' ')) if tested.empty?
|
83
|
-
|
84
|
-
filtered = tested
|
85
|
-
available = filtered
|
86
|
-
end
|
87
|
-
Result.new(objects, token, filtered, '')
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def select_strict available, word
|
92
|
-
available.select { |obj| obj.keywords.include?(word) }
|
93
|
-
end
|
94
|
-
|
95
|
-
def select_fuzzy available, word
|
96
|
-
available.select { |obj| obj.keywords.any? { |wrd| wrd.start_with?(word) } }
|
97
|
-
end
|
98
|
-
|
99
|
-
def nested?(token)
|
100
|
-
token.match(NEST_REGEXP)
|
101
|
-
end
|
102
|
-
|
103
|
-
def scan_text selection, token
|
104
|
-
case selection
|
105
|
-
when Regexp
|
106
|
-
return Result.new(selection, token, token, '') if token =~ selection
|
107
|
-
else
|
108
|
-
return Result.new(selection, token, selection, token[selection.length..]) if token.start_with?(selection)
|
109
|
-
end
|
110
|
-
Result.new(selection, token, '', token)
|
111
|
-
end
|
112
|
-
|
113
|
-
def denest(objects, token)
|
114
|
-
parts = token.split(NEST_REGEXP)
|
115
|
-
current = parts.pop
|
116
|
-
last_result = scan(objects, current)
|
117
|
-
until parts.empty?
|
118
|
-
current = "#{parts.last} #{current}"
|
119
|
-
result = scan(last_result.matched, current)
|
120
|
-
break if result.matched.empty?
|
121
|
-
|
122
|
-
parts.pop
|
123
|
-
last_result = result
|
124
|
-
end
|
125
|
-
return Result.new(objects, token, [], '') if last_result.matched.empty? || last_result.matched.length > 1
|
126
|
-
return last_result if parts.empty?
|
127
|
-
|
128
|
-
denest(last_result.matched.first.children, parts.join(' '))
|
129
|
-
end
|
48
|
+
def self.strictness processor
|
49
|
+
(processors.length - (processors.find_index(processor) || processors.length)) * 100
|
130
50
|
end
|
51
|
+
|
52
|
+
use DEFAULT_PROCESSORS
|
131
53
|
end
|
132
54
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scope
|
5
|
+
# The Descendants scope returns an entity's children and accessible
|
6
|
+
# descendants.
|
7
|
+
#
|
8
|
+
class Descendants < Base
|
9
|
+
def matches
|
10
|
+
context.children.flat_map do |child|
|
11
|
+
[child] + subquery_accessible(child)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/gamefic/scope/family.rb
CHANGED
@@ -2,18 +2,41 @@
|
|
2
2
|
|
3
3
|
module Gamefic
|
4
4
|
module Scope
|
5
|
-
# The Family scope returns an entity's
|
5
|
+
# The Family scope returns an entity's ascendants, descendants, siblings,
|
6
|
+
# and siblings' descendants.
|
6
7
|
#
|
7
8
|
class Family < Base
|
8
9
|
def matches
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
match_ascendants + match_descendants + match_siblings
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def match_ascendants
|
16
|
+
[].tap do |result|
|
17
|
+
here = context.parent
|
18
|
+
while here
|
19
|
+
result.push here
|
20
|
+
here = here.parent
|
21
|
+
end
|
15
22
|
end
|
16
|
-
|
23
|
+
end
|
24
|
+
|
25
|
+
def match_descendants
|
26
|
+
context.children.flat_map do |child|
|
27
|
+
[child] + subquery_accessible(child)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def match_siblings
|
32
|
+
return [] unless context.parent
|
33
|
+
|
34
|
+
context.parent
|
35
|
+
.children
|
36
|
+
.that_are_not(context)
|
37
|
+
.flat_map do |child|
|
38
|
+
[child] + subquery_accessible(child)
|
39
|
+
end
|
17
40
|
end
|
18
41
|
end
|
19
42
|
end
|
data/lib/gamefic/scope.rb
CHANGED
@@ -26,11 +26,10 @@ module Gamefic
|
|
26
26
|
# end
|
27
27
|
#
|
28
28
|
# @param verb [Symbol] An imperative verb for the command
|
29
|
-
# @param
|
29
|
+
# @param args [Array<Object>] Filters for the command's tokens
|
30
30
|
# @yieldparam [Gamefic::Actor]
|
31
31
|
# @return [Symbol]
|
32
|
-
def respond(verb, *
|
33
|
-
args = map_response_args(queries)
|
32
|
+
def respond(verb, *args, &proc)
|
34
33
|
rulebook.calls.add_response Response.new(verb, self, *args, &proc)
|
35
34
|
verb
|
36
35
|
end
|
@@ -47,11 +46,10 @@ module Gamefic
|
|
47
46
|
# end
|
48
47
|
#
|
49
48
|
# @param verb [Symbol] An imperative verb for the command
|
50
|
-
# @param
|
49
|
+
# @param args [Array<Object>] Filters for the command's tokens
|
51
50
|
# @yieldparam [Gamefic::Actor]
|
52
51
|
# @return [Symbol]
|
53
|
-
def meta(verb, *
|
54
|
-
args = map_response_args(queries)
|
52
|
+
def meta(verb, *args, &proc)
|
55
53
|
rulebook.calls.add_response Response.new(verb, self, *args, meta: true, &proc)
|
56
54
|
verb
|
57
55
|
end
|
@@ -134,23 +132,6 @@ module Gamefic
|
|
134
132
|
def syntaxes
|
135
133
|
rulebook.syntaxes
|
136
134
|
end
|
137
|
-
|
138
|
-
private
|
139
|
-
|
140
|
-
def map_response_args args
|
141
|
-
args.map do |arg|
|
142
|
-
case arg
|
143
|
-
when Entity, Class, Module, Proc, Proxy::Agent
|
144
|
-
available(arg)
|
145
|
-
when String, Regexp
|
146
|
-
plaintext(arg)
|
147
|
-
when Gamefic::Query::Base, Gamefic::Query::Text
|
148
|
-
arg
|
149
|
-
else
|
150
|
-
raise ArgumentError, "invalid argument in response: #{arg.inspect}"
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
135
|
end
|
155
136
|
end
|
156
137
|
end
|
@@ -9,7 +9,7 @@ module Gamefic
|
|
9
9
|
# #make and #destroy instead.
|
10
10
|
#
|
11
11
|
module Entities
|
12
|
-
include
|
12
|
+
include Proxies
|
13
13
|
|
14
14
|
def entity_vault
|
15
15
|
@entity_vault ||= Vault.new
|
@@ -63,6 +63,9 @@ module Gamefic
|
|
63
63
|
|
64
64
|
# Same as #pick, but raise an error if a unique match could not be found.
|
65
65
|
#
|
66
|
+
#
|
67
|
+
# @raise [RuntimeError] if a unique match was not found.
|
68
|
+
#
|
66
69
|
# @param description [String]
|
67
70
|
# @return [Gamefic::Entity, nil]
|
68
71
|
def pick! description
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scriptable
|
5
|
+
module PlotProxies
|
6
|
+
def lazy_plot key
|
7
|
+
Proxy.new(:attr, [:plot, key])
|
8
|
+
end
|
9
|
+
alias _plot lazy_plot
|
10
|
+
|
11
|
+
def attr_plot attr
|
12
|
+
define_method(attr) { plot.send(attr) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|