gamefic 2.0.2 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|