gamefic 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +13 -0
- data/lib/gamefic.rb +1 -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 -2
- data/lib/gamefic/plot.rb +10 -12
- data/lib/gamefic/plot/darkroom.rb +49 -61
- data/lib/gamefic/plot/snapshot.rb +13 -5
- 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.rb +0 -1
- data/lib/gamefic/scene/base.rb +7 -2
- data/lib/gamefic/scene/conclusion.rb +1 -1
- data/lib/gamefic/scene/multiple_choice.rb +2 -12
- data/lib/gamefic/scene/pause.rb +1 -1
- data/lib/gamefic/scene/yes_or_no.rb +1 -1
- data/lib/gamefic/scriptable.rb +1 -0
- data/lib/gamefic/serialize.rb +172 -17
- data/lib/gamefic/subplot.rb +11 -2
- 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 +11 -14
- data/lib/gamefic/world/playbook.rb +30 -40
- data/lib/gamefic/world/players.rb +18 -2
- data/lib/gamefic/world/scenes.rb +13 -13
- metadata +18 -18
- data/lib/gamefic/index.rb +0 -121
- data/lib/gamefic/scene/custom.rb +0 -7
data/lib/gamefic/subplot.rb
CHANGED
@@ -4,22 +4,30 @@ module Gamefic
|
|
4
4
|
class Subplot #< Container
|
5
5
|
include World
|
6
6
|
include Scriptable
|
7
|
+
include Gamefic::Serialize
|
7
8
|
# @!parse extend Scriptable::ClassMethods
|
8
9
|
|
9
10
|
# @return [Gamefic::Plot]
|
10
11
|
attr_reader :plot
|
11
12
|
|
12
13
|
# @param plot [Gamefic::Plot]
|
13
|
-
# @param introduce [Gamefic::Actor]
|
14
|
-
# @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]
|
15
17
|
def initialize plot, introduce: nil, next_cue: nil, **more
|
16
18
|
@plot = plot
|
17
19
|
@next_cue = next_cue
|
18
20
|
@concluded = false
|
21
|
+
@more = more
|
19
22
|
configure more
|
20
23
|
run_scripts
|
21
24
|
playbook.freeze
|
22
25
|
self.introduce introduce unless introduce.nil?
|
26
|
+
@static = [self] + scene_classes + entities
|
27
|
+
end
|
28
|
+
|
29
|
+
def static
|
30
|
+
plot.static
|
23
31
|
end
|
24
32
|
|
25
33
|
def players
|
@@ -63,6 +71,7 @@ module Gamefic
|
|
63
71
|
# @todo I'm not sure why rejecting nils is necessary here. It's only an
|
64
72
|
# issue in Opal.
|
65
73
|
entities.reject(&:nil?).each { |e| destroy e }
|
74
|
+
# plot.static.remove(scene_classes + entities)
|
66
75
|
end
|
67
76
|
|
68
77
|
def concluded?
|
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
|
@@ -43,11 +43,18 @@ module Gamefic
|
|
43
43
|
# @param [Gamefic::Entity] The entity to remove
|
44
44
|
def destroy entity
|
45
45
|
entity.parent = nil
|
46
|
-
index = entities.index(entity)
|
47
|
-
return if index.nil? || index < static_entity_length - 1
|
48
|
-
entities.delete_at index
|
46
|
+
# index = entities.index(entity)
|
47
|
+
# return if index.nil? || index < static_entity_length - 1
|
48
|
+
# entities.delete_at index
|
49
|
+
# players.delete entity
|
50
|
+
# entity.destroy
|
51
|
+
|
52
|
+
# @todo It might make sense to destroy the entity completely now. It
|
53
|
+
# will still have a reference in the index, but that shouldn't impact
|
54
|
+
# the current state of the plot.
|
55
|
+
return if static.include?(entity)
|
56
|
+
entities.delete entity
|
49
57
|
players.delete entity
|
50
|
-
entity.destroy
|
51
58
|
end
|
52
59
|
|
53
60
|
# Pick an entity based on its description.
|
@@ -86,16 +93,6 @@ module Gamefic
|
|
86
93
|
def players
|
87
94
|
@players ||= []
|
88
95
|
end
|
89
|
-
|
90
|
-
private
|
91
|
-
|
92
|
-
def mark_static_entities
|
93
|
-
@static_entity_length ||= entities.length
|
94
|
-
end
|
95
|
-
|
96
|
-
def static_entity_length
|
97
|
-
@static_entity_length || 0
|
98
|
-
end
|
99
96
|
end
|
100
97
|
end
|
101
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
|
data/lib/gamefic/world/scenes.rb
CHANGED
@@ -35,7 +35,7 @@ module Gamefic
|
|
35
35
|
players.push player
|
36
36
|
@introduction.call(player) unless @introduction.nil?
|
37
37
|
# @todo Find a better way to persist player characters
|
38
|
-
Gamefic::Index.stick
|
38
|
+
# Gamefic::Index.stick
|
39
39
|
end
|
40
40
|
|
41
41
|
# Create a multiple-choice scene.
|
@@ -95,10 +95,10 @@ module Gamefic
|
|
95
95
|
#
|
96
96
|
# @param prompt [String]
|
97
97
|
# @yieldparam [Gamefic::Actor]
|
98
|
-
# @yieldparam [Gamefic::Scene::
|
99
|
-
# @return [Class<Gamefic::Scene::
|
98
|
+
# @yieldparam [Gamefic::Scene::Base]
|
99
|
+
# @return [Class<Gamefic::Scene::Base>]
|
100
100
|
def question prompt = 'What is your answer?', &block
|
101
|
-
s = Scene::
|
101
|
+
s = Scene::Base.subclass do |actor, scene|
|
102
102
|
scene.prompt = prompt
|
103
103
|
scene.on_finish &block
|
104
104
|
end
|
@@ -144,29 +144,29 @@ module Gamefic
|
|
144
144
|
scene_classes.push s
|
145
145
|
s
|
146
146
|
end
|
147
|
-
|
147
|
+
|
148
148
|
# Create a custom scene.
|
149
149
|
#
|
150
150
|
# Custom scenes should always specify the next scene to be cued or
|
151
151
|
# prepared. If not, the scene will get repeated on the next turn.
|
152
152
|
#
|
153
|
-
# This method creates a Scene::
|
153
|
+
# This method creates a Scene::Base by default. You can customize other
|
154
154
|
# scene types by specifying the class to create.
|
155
155
|
#
|
156
156
|
# @example Ask the user for a name
|
157
|
-
# @scene = custom do |scene|
|
158
|
-
#
|
159
|
-
# scene.on_finish do
|
160
|
-
# actor.name =
|
157
|
+
# @scene = custom do |actor, scene|
|
158
|
+
# scene.prompt = "What's your name?"
|
159
|
+
# scene.on_finish do
|
160
|
+
# actor.name = scene.input
|
161
161
|
# actor.tell "Hello, #{actor.name}!"
|
162
|
-
# actor.cue
|
162
|
+
# actor.cue default_scene
|
163
163
|
# end
|
164
164
|
# end
|
165
165
|
#
|
166
166
|
# @param cls [Class] The class of scene to be instantiated.
|
167
167
|
# @yieldparam [Gamefic::Actor]
|
168
|
-
# @return [Class<Gamefic::Scene::
|
169
|
-
def custom cls = Scene::
|
168
|
+
# @return [Class<Gamefic::Scene::Base>]
|
169
|
+
def custom cls = Scene::Base, &block
|
170
170
|
s = cls.subclass &block
|
171
171
|
scene_classes.push s
|
172
172
|
s
|
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.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fred Snyder
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-03 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
|
@@ -74,6 +74,7 @@ files:
|
|
74
74
|
- ".rspec"
|
75
75
|
- ".rubocop.yml"
|
76
76
|
- ".solargraph.yml"
|
77
|
+
- CHANGELOG.md
|
77
78
|
- Gemfile
|
78
79
|
- LICENSE
|
79
80
|
- README.md
|
@@ -89,7 +90,6 @@ files:
|
|
89
90
|
- lib/gamefic/describable.rb
|
90
91
|
- lib/gamefic/element.rb
|
91
92
|
- lib/gamefic/entity.rb
|
92
|
-
- lib/gamefic/index.rb
|
93
93
|
- lib/gamefic/keywords.rb
|
94
94
|
- lib/gamefic/messaging.rb
|
95
95
|
- lib/gamefic/node.rb
|
@@ -108,11 +108,11 @@ files:
|
|
108
108
|
- lib/gamefic/query/parent.rb
|
109
109
|
- lib/gamefic/query/siblings.rb
|
110
110
|
- lib/gamefic/query/text.rb
|
111
|
+
- lib/gamefic/query/tree.rb
|
111
112
|
- lib/gamefic/scene.rb
|
112
113
|
- lib/gamefic/scene/activity.rb
|
113
114
|
- lib/gamefic/scene/base.rb
|
114
115
|
- lib/gamefic/scene/conclusion.rb
|
115
|
-
- lib/gamefic/scene/custom.rb
|
116
116
|
- lib/gamefic/scene/multiple_choice.rb
|
117
117
|
- lib/gamefic/scene/multiple_scene.rb
|
118
118
|
- lib/gamefic/scene/pause.rb
|
@@ -133,7 +133,7 @@ homepage: http://gamefic.com
|
|
133
133
|
licenses:
|
134
134
|
- MIT
|
135
135
|
metadata: {}
|
136
|
-
post_install_message:
|
136
|
+
post_install_message:
|
137
137
|
rdoc_options: []
|
138
138
|
require_paths:
|
139
139
|
- lib
|
@@ -148,8 +148,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
148
|
- !ruby/object:Gem::Version
|
149
149
|
version: '0'
|
150
150
|
requirements: []
|
151
|
-
rubygems_version: 3.
|
152
|
-
signing_key:
|
151
|
+
rubygems_version: 3.1.6
|
152
|
+
signing_key:
|
153
153
|
specification_version: 4
|
154
154
|
summary: Gamefic
|
155
155
|
test_files: []
|