gamefic 3.4.0 → 3.6.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 +22 -0
- data/README.md +1 -1
- data/lib/gamefic/action.rb +1 -0
- 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 +49 -1
- data/lib/gamefic/dispatcher.rb +8 -31
- data/lib/gamefic/entity.rb +26 -0
- data/lib/gamefic/expression.rb +3 -0
- data/lib/gamefic/narrative.rb +9 -5
- data/lib/gamefic/node.rb +3 -5
- data/lib/gamefic/plot.rb +5 -0
- data/lib/gamefic/proxy/base.rb +27 -0
- data/lib/gamefic/proxy/config.rb +16 -0
- data/lib/gamefic/proxy/pick.rb +11 -0
- data/lib/gamefic/proxy/plot_pick.rb +11 -0
- data/lib/gamefic/proxy.rb +79 -0
- data/lib/gamefic/query/abstract.rb +12 -0
- data/lib/gamefic/query/base.rb +62 -16
- data/lib/gamefic/query/general.rb +6 -15
- data/lib/gamefic/query/result.rb +4 -1
- data/lib/gamefic/query/scoped.rb +3 -21
- data/lib/gamefic/query/text.rb +17 -15
- data/lib/gamefic/query.rb +1 -0
- data/lib/gamefic/response.rb +75 -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 +60 -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 +32 -17
- data/lib/gamefic/scriptable/plot_proxies.rb +29 -0
- data/lib/gamefic/scriptable/proxies.rb +31 -0
- data/lib/gamefic/scriptable/queries.rb +27 -8
- data/lib/gamefic/scriptable/scenes.rb +7 -1
- data/lib/gamefic/scriptable.rb +78 -16
- data/lib/gamefic/stage.rb +2 -2
- data/lib/gamefic/subplot.rb +33 -1
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic.rb +1 -1
- metadata +17 -4
- data/lib/gamefic/composer.rb +0 -70
- data/lib/gamefic/scriptable/proxy.rb +0 -69
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
|
@@ -49,30 +49,45 @@ module Gamefic
|
|
49
49
|
entity_vault.delete entity
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
def find *args
|
53
|
+
args.inject(entities) do |entities, arg|
|
54
|
+
case arg
|
55
|
+
when String
|
56
|
+
result = Scanner.scan(entities, arg)
|
57
|
+
result.remainder.empty? ? result.match : []
|
58
|
+
else
|
59
|
+
entities.that_are(arg)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Pick a unique entity based on the given arguments. String arguments are
|
65
|
+
# used to scan the entities for matching names and synonyms. Return nil
|
66
|
+
# if an entity could not be found or there is more than one possible
|
67
|
+
# match.
|
54
68
|
#
|
55
|
-
# @param description [
|
69
|
+
# @param description [Array]
|
56
70
|
# @return [Gamefic::Entity, nil]
|
57
|
-
def pick
|
58
|
-
|
59
|
-
return nil unless
|
71
|
+
def pick *args
|
72
|
+
matches = find(*args)
|
73
|
+
return nil unless matches.one?
|
60
74
|
|
61
|
-
|
75
|
+
matches.first
|
62
76
|
end
|
63
77
|
|
64
78
|
# Same as #pick, but raise an error if a unique match could not be found.
|
65
79
|
#
|
66
|
-
#
|
67
|
-
# @
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
raise "
|
80
|
+
#
|
81
|
+
# @raise [RuntimeError] if a unique match was not found.
|
82
|
+
#
|
83
|
+
# @param args [Array]
|
84
|
+
# @return [Gamefic::Entity]
|
85
|
+
def pick! *args
|
86
|
+
matches = find(*args)
|
87
|
+
raise "no entity matching '#{args.inspect}'" if matches.empty?
|
88
|
+
raise "multiple entities matching '#{args.inspect}': #{matches.join_and}" unless matches.one?
|
74
89
|
|
75
|
-
|
90
|
+
matches.first
|
76
91
|
end
|
77
92
|
end
|
78
93
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scriptable
|
5
|
+
module PlotProxies
|
6
|
+
def lazy_plot key
|
7
|
+
Logging.logger.warn "#{caller.first ? "#{caller.first}: " : ''}`lazy_plot` is deprecated. Use `plot_pick`, `plot_pick!, or pass the entity from the plot in a `config` option instead."
|
8
|
+
Proxy.new(:attr, [:plot, key])
|
9
|
+
end
|
10
|
+
alias _plot lazy_plot
|
11
|
+
|
12
|
+
def attr_plot attr
|
13
|
+
define_method(attr) { plot.send(attr) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def plot_pick *args
|
17
|
+
Proxy::PlotPick.new(*args)
|
18
|
+
end
|
19
|
+
alias lazy_plot_pick plot_pick
|
20
|
+
alias _plot_pick plot_pick
|
21
|
+
|
22
|
+
def plot_pick! *args
|
23
|
+
Proxy::PlotPick.new(*args)
|
24
|
+
end
|
25
|
+
alias lazy_plot_pick! plot_pick!
|
26
|
+
alias _plot_pick! plot_pick!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scriptable
|
5
|
+
# Methods for referencing entities from proxies.
|
6
|
+
#
|
7
|
+
module Proxies
|
8
|
+
# Convert a proxy into its referenced entity.
|
9
|
+
#
|
10
|
+
# This method can receive any kind of object. If it's a proxy, its entity
|
11
|
+
# will be returned. If it's an array, each of its elements will be
|
12
|
+
# unproxied. If it's a hash, each of its values will be unproxied. Any
|
13
|
+
# other object will be returned unchanged.
|
14
|
+
#
|
15
|
+
# @param object [Object]
|
16
|
+
# @return [Object]
|
17
|
+
def unproxy object
|
18
|
+
case object
|
19
|
+
when Proxy, Proxy::Base
|
20
|
+
object.fetch self
|
21
|
+
when Array
|
22
|
+
object.map { |obj| unproxy obj }
|
23
|
+
when Hash
|
24
|
+
object.transform_values { |val| unproxy val }
|
25
|
+
else
|
26
|
+
object
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -5,14 +5,25 @@ module Gamefic
|
|
5
5
|
# Scriptable methods related to creating action queries.
|
6
6
|
#
|
7
7
|
module Queries
|
8
|
-
include
|
8
|
+
include Proxies
|
9
9
|
|
10
10
|
# Define a query that searches the entire plot's entities.
|
11
11
|
#
|
12
12
|
# @param args [Array<Object>] Query arguments
|
13
13
|
# @return [Query::General]
|
14
14
|
def anywhere *args, ambiguous: false
|
15
|
-
Query::General.new -> { entities }, *
|
15
|
+
Query::General.new -> { entities }, *args, ambiguous: ambiguous, name: 'anywhere'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Define a query that searches for abstract entities.
|
19
|
+
#
|
20
|
+
# An abstract entity is a pseudo-entity that is describable but does
|
21
|
+
# not have a parent or children.
|
22
|
+
#
|
23
|
+
# @param args [Array<Object>] Query arguments
|
24
|
+
# @return [Query::Abstract]
|
25
|
+
def abstract *args, ambiguous: false
|
26
|
+
Query::Abstract.new -> { entities }, *args, ambiguous: ambiguous
|
16
27
|
end
|
17
28
|
|
18
29
|
# Define a query that searches an actor's family of entities. The
|
@@ -22,7 +33,7 @@ module Gamefic
|
|
22
33
|
# @param args [Array<Object>] Query arguments
|
23
34
|
# @return [Query::Scoped]
|
24
35
|
def available *args, ambiguous: false
|
25
|
-
Query::Scoped.new Scope::Family, *
|
36
|
+
Query::Scoped.new Scope::Family, *args, ambiguous: ambiguous, name: 'available'
|
26
37
|
end
|
27
38
|
alias family available
|
28
39
|
|
@@ -31,7 +42,7 @@ module Gamefic
|
|
31
42
|
# @param args [Array<Object>] Query arguments
|
32
43
|
# @return [Query::Scoped]
|
33
44
|
def parent *args, ambiguous: false
|
34
|
-
Query::Scoped.new Scope::Parent, *
|
45
|
+
Query::Scoped.new Scope::Parent, *args, ambiguous: ambiguous, name: 'parent'
|
35
46
|
end
|
36
47
|
|
37
48
|
# Define a query that searches an actor's children.
|
@@ -39,7 +50,15 @@ module Gamefic
|
|
39
50
|
# @param args [Array<Object>] Query arguments
|
40
51
|
# @return [Query::Scoped]
|
41
52
|
def children *args, ambiguous: false
|
42
|
-
Query::Scoped.new Scope::Children, *
|
53
|
+
Query::Scoped.new Scope::Children, *args, ambiguous: ambiguous, name: 'children'
|
54
|
+
end
|
55
|
+
|
56
|
+
# Define a query that searches an actor's descendants.
|
57
|
+
#
|
58
|
+
# @param args [Array<Object>] Query arguments
|
59
|
+
# @return [Query::Scoped]
|
60
|
+
def descendants *args, ambiguous: false
|
61
|
+
Query::Scoped.new Scope::Descendants, *args, ambiguous: ambiguous
|
43
62
|
end
|
44
63
|
|
45
64
|
# Define a query that searches an actor's siblings.
|
@@ -47,7 +66,7 @@ module Gamefic
|
|
47
66
|
# @param args [Array<Object>] Query arguments
|
48
67
|
# @return [Query::Scoped]
|
49
68
|
def siblings *args, ambiguous: false
|
50
|
-
Query::Scoped.new Scope::Siblings, *
|
69
|
+
Query::Scoped.new Scope::Siblings, *args, ambiguous: ambiguous, name: 'siblings'
|
51
70
|
end
|
52
71
|
|
53
72
|
# Define a query that returns the actor itself.
|
@@ -55,7 +74,7 @@ module Gamefic
|
|
55
74
|
# @param args [Array<Object>] Query arguments
|
56
75
|
# @return [Query::Scoped]
|
57
76
|
def myself *args, ambiguous: false
|
58
|
-
Query::Scoped.new Scope::Myself, *
|
77
|
+
Query::Scoped.new Scope::Myself, *args, ambiguous: ambiguous, name: 'myself'
|
59
78
|
end
|
60
79
|
|
61
80
|
# Define a query that performs a plaintext search. It can take a String
|
@@ -66,7 +85,7 @@ module Gamefic
|
|
66
85
|
# @param arg [String, Regexp] The string or regular expression to match
|
67
86
|
# @return [Query::Text]
|
68
87
|
def plaintext arg = /.*/
|
69
|
-
Query::Text.new arg
|
88
|
+
Query::Text.new arg, name: 'plaintext'
|
70
89
|
end
|
71
90
|
end
|
72
91
|
end
|
@@ -36,7 +36,6 @@ module Gamefic
|
|
36
36
|
rulebook.scenes.add klass.new(name, self, on_start: on_start, on_finish: on_finish, &blk)
|
37
37
|
name
|
38
38
|
end
|
39
|
-
alias scene block
|
40
39
|
|
41
40
|
def preface name, klass = Scene::Activity, &start
|
42
41
|
rulebook.scenes.add klass.new(name, self, on_start: start)
|
@@ -160,9 +159,16 @@ module Gamefic
|
|
160
159
|
on_start: start
|
161
160
|
end
|
162
161
|
|
162
|
+
# @return [Array<Symbol>]
|
163
163
|
def scenes
|
164
164
|
rulebook.scenes.names
|
165
165
|
end
|
166
|
+
|
167
|
+
# @param name [Symbol]
|
168
|
+
# @return [Scene::Default, nil]
|
169
|
+
def scene(name)
|
170
|
+
rulebook.scenes[name]
|
171
|
+
end
|
166
172
|
end
|
167
173
|
end
|
168
174
|
end
|
data/lib/gamefic/scriptable.rb
CHANGED
@@ -23,14 +23,14 @@ module Gamefic
|
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
module Scriptable
|
26
|
-
autoload :Actions,
|
27
|
-
autoload :Entities,
|
28
|
-
autoload :Events,
|
29
|
-
autoload :Queries,
|
30
|
-
autoload :
|
31
|
-
autoload :Scenes,
|
32
|
-
|
33
|
-
|
26
|
+
autoload :Actions, 'gamefic/scriptable/actions'
|
27
|
+
autoload :Entities, 'gamefic/scriptable/entities'
|
28
|
+
autoload :Events, 'gamefic/scriptable/events'
|
29
|
+
autoload :Queries, 'gamefic/scriptable/queries'
|
30
|
+
autoload :Proxies, 'gamefic/scriptable/proxies'
|
31
|
+
autoload :Scenes, 'gamefic/scriptable/scenes'
|
32
|
+
autoload :PlotProxies, 'gamefic/scriptable/plot_proxies'
|
33
|
+
|
34
34
|
include Queries
|
35
35
|
# @!parse
|
36
36
|
# include Scriptable::Actions
|
@@ -101,11 +101,12 @@ module Gamefic
|
|
101
101
|
# make_seed Gamefic::Entity, name: 'thing'
|
102
102
|
#
|
103
103
|
# @param klass [Class<Gamefic::Entity>]
|
104
|
+
# @return [Proxy]
|
104
105
|
def make_seed klass, **opts
|
105
|
-
@count ||= 0
|
106
106
|
seed { make(klass, **opts) }
|
107
|
-
Proxy::
|
107
|
+
Proxy::Pick.new(klass, opts[:name], raise: true)
|
108
108
|
end
|
109
|
+
alias make make_seed
|
109
110
|
|
110
111
|
# Seed an entity with an attribute method.
|
111
112
|
#
|
@@ -119,15 +120,76 @@ module Gamefic
|
|
119
120
|
#
|
120
121
|
# @param name [Symbol] The attribute name
|
121
122
|
# @param klass [Class<Gamefic::Entity>]
|
123
|
+
# @return [Proxy]
|
122
124
|
def attr_seed name, klass, **opts
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
125
|
+
ivname = "@#{name}"
|
126
|
+
define_method(name) do
|
127
|
+
return instance_variable_get(ivname) if instance_variable_defined?(ivname)
|
128
|
+
|
129
|
+
instance_variable_set(ivname, make(klass, **opts))
|
130
|
+
end
|
131
|
+
seed { send name }
|
132
|
+
Proxy.new(:attr, name)
|
133
|
+
end
|
134
|
+
|
135
|
+
# @param symbol [Symbol]
|
136
|
+
# @return [Proxy]
|
137
|
+
def proxy symbol
|
138
|
+
Logging.logger.warn "#{caller.first ? "#{caller.first}: " : ''}`proxy` is deprecated. Use `pick` or `pick!` instead."
|
139
|
+
if symbol.to_s.start_with?('@')
|
140
|
+
Proxy.new(:ivar, symbol)
|
141
|
+
else
|
142
|
+
Proxy.new(:attr, symbol)
|
127
143
|
end
|
128
|
-
Proxy::Agent.new(@count.tap { @count += 1 })
|
129
144
|
end
|
130
145
|
|
146
|
+
# Lazy reference an entity by its instance variable.
|
147
|
+
#
|
148
|
+
# @example
|
149
|
+
# lazy_ivar(:@variable)
|
150
|
+
#
|
151
|
+
# @param key [Symbol]
|
152
|
+
# @return [Proxy]
|
153
|
+
def lazy_ivar key
|
154
|
+
Gamefic.logger.warn "#{caller.first ? "#{caller.first}: " : ''}`lazy_ivar` is deprecated. Use `pick` or `pick!` instead."
|
155
|
+
Proxy.new(:ivar, key)
|
156
|
+
end
|
157
|
+
alias _ivar lazy_ivar
|
158
|
+
|
159
|
+
# Lazy reference an entity by its attribute or method.
|
160
|
+
#
|
161
|
+
# @example
|
162
|
+
# lazy_attr(:method)
|
163
|
+
#
|
164
|
+
# @param key [Symbol]
|
165
|
+
# @return [Proxy]
|
166
|
+
def lazy_attr key
|
167
|
+
Gamefic.logger.warn "#{caller.first ? "#{caller.first}: " : ''}`lazy_attr` is deprecated. Use `pick` or `pick!` instead."
|
168
|
+
Proxy.new(:attr, key)
|
169
|
+
end
|
170
|
+
alias _attr lazy_attr
|
171
|
+
|
172
|
+
# Lazy pick an entity.
|
173
|
+
#
|
174
|
+
# @example
|
175
|
+
# pick('the red box')
|
176
|
+
#
|
177
|
+
# @param args [Array]
|
178
|
+
# @return [Proxy]
|
179
|
+
def pick *args
|
180
|
+
Proxy::Pick.new(*args)
|
181
|
+
end
|
182
|
+
alias lazy_pick pick
|
183
|
+
alias _pick pick
|
184
|
+
|
185
|
+
# Lazy pick an entity or raise
|
186
|
+
#
|
187
|
+
def pick! *args
|
188
|
+
Proxy::Pick.new(*args)
|
189
|
+
end
|
190
|
+
alias lazy_pick! pick
|
191
|
+
alias _pick! pick
|
192
|
+
|
131
193
|
if RUBY_ENGINE == 'opal'
|
132
194
|
# :nocov:
|
133
195
|
def method_missing method, *args, &block
|
@@ -160,7 +222,7 @@ module Gamefic
|
|
160
222
|
#
|
161
223
|
# @return [Module<self>]
|
162
224
|
def no_scripts
|
163
|
-
Logging.logger.warn 'Calling `no_scripts` on Scriptable modules is no longer necessary.
|
225
|
+
Logging.logger.warn "#{caller.first ? "#{caller.first}: " : ''}Calling `no_scripts` on Scriptable modules is no longer necessary."
|
164
226
|
self
|
165
227
|
end
|
166
228
|
end
|
data/lib/gamefic/stage.rb
CHANGED
@@ -14,7 +14,7 @@ module Gamefic
|
|
14
14
|
|
15
15
|
OVERWRITEABLE_CLASSES = [String, Numeric, Symbol].freeze
|
16
16
|
|
17
|
-
SWAPPABLE_VALUES = [true, false
|
17
|
+
SWAPPABLE_VALUES = [true, false].freeze
|
18
18
|
|
19
19
|
class << self
|
20
20
|
private
|
@@ -37,7 +37,7 @@ module Gamefic
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def overwriteable? cval, nval
|
40
|
-
return true if swappable?(cval, nval)
|
40
|
+
return true if cval.nil? || swappable?(cval, nval)
|
41
41
|
|
42
42
|
allowed = OVERWRITEABLE_CLASSES.find { |klass| cval.is_a?(klass) }
|
43
43
|
allowed && cval.is_a?(allowed)
|