gamefic 1.7.0 → 2.0.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 +5 -5
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +13 -0
- data/.solargraph.yml +5 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +10 -0
- data/gamefic.gemspec +27 -0
- data/lib/gamefic.rb +7 -6
- data/lib/gamefic/action.rb +38 -28
- data/lib/gamefic/active.rb +325 -280
- data/lib/gamefic/actor.rb +8 -5
- data/lib/gamefic/command.rb +9 -7
- data/lib/gamefic/core_ext/array.rb +24 -49
- data/lib/gamefic/core_ext/string.rb +25 -16
- data/lib/gamefic/describable.rb +21 -23
- data/lib/gamefic/element.rb +43 -31
- data/lib/gamefic/entity.rb +6 -12
- data/lib/gamefic/index.rb +121 -0
- data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
- data/lib/gamefic/messaging.rb +43 -44
- data/lib/gamefic/node.rb +14 -5
- data/lib/gamefic/plot.rb +69 -89
- data/lib/gamefic/plot/darkroom.rb +92 -264
- data/lib/gamefic/plot/host.rb +42 -48
- data/lib/gamefic/plot/snapshot.rb +5 -18
- data/lib/gamefic/query.rb +14 -18
- data/lib/gamefic/query/base.rb +30 -18
- data/lib/gamefic/query/children.rb +0 -0
- data/lib/gamefic/query/external.rb +18 -14
- data/lib/gamefic/query/family.rb +1 -7
- data/lib/gamefic/query/matches.rb +75 -67
- data/lib/gamefic/query/parent.rb +0 -0
- data/lib/gamefic/query/siblings.rb +0 -0
- data/lib/gamefic/query/text.rb +2 -1
- data/lib/gamefic/scene.rb +0 -2
- data/lib/gamefic/scene/activity.rb +24 -26
- data/lib/gamefic/scene/base.rb +64 -8
- data/lib/gamefic/scene/conclusion.rb +0 -2
- data/lib/gamefic/scene/custom.rb +0 -2
- data/lib/gamefic/scene/multiple_choice.rb +18 -3
- data/lib/gamefic/scene/multiple_scene.rb +29 -20
- data/lib/gamefic/scene/pause.rb +7 -2
- data/lib/gamefic/scene/yes_or_no.rb +21 -9
- data/lib/gamefic/scriptable.rb +87 -0
- data/lib/gamefic/serialize.rb +68 -0
- data/lib/gamefic/subplot.rb +29 -35
- data/lib/gamefic/syntax.rb +14 -13
- data/lib/gamefic/version.rb +3 -3
- data/lib/gamefic/world.rb +16 -0
- data/lib/gamefic/world/callbacks.rb +135 -0
- data/lib/gamefic/world/commands.rb +184 -0
- data/lib/gamefic/{plot → world}/entities.rb +30 -29
- data/lib/gamefic/{plot → world}/playbook.rb +255 -240
- data/lib/gamefic/world/players.rb +21 -0
- data/lib/gamefic/world/scenes.rb +226 -0
- metadata +41 -92
- data/bin/gamefic +0 -9
- data/lib/gamefic/engine.rb +0 -7
- data/lib/gamefic/engine/base.rb +0 -59
- data/lib/gamefic/engine/tty.rb +0 -24
- data/lib/gamefic/grammar.rb +0 -13
- data/lib/gamefic/grammar/conjugator.rb +0 -20
- data/lib/gamefic/grammar/gender.rb +0 -11
- data/lib/gamefic/grammar/person.rb +0 -10
- data/lib/gamefic/grammar/plural.rb +0 -13
- data/lib/gamefic/grammar/pronouns.rb +0 -106
- data/lib/gamefic/grammar/tense.rb +0 -6
- data/lib/gamefic/grammar/verb_set.rb +0 -43
- data/lib/gamefic/grammar/verbs.rb +0 -26
- data/lib/gamefic/grammar/word_adapter.rb +0 -49
- data/lib/gamefic/plot/articles.rb +0 -22
- data/lib/gamefic/plot/callbacks.rb +0 -126
- data/lib/gamefic/plot/commands.rb +0 -120
- data/lib/gamefic/plot/players.rb +0 -15
- data/lib/gamefic/plot/scenes.rb +0 -187
- data/lib/gamefic/plot/theater.rb +0 -73
- data/lib/gamefic/plot/you_mount.rb +0 -22
- data/lib/gamefic/script.rb +0 -13
- data/lib/gamefic/script/base.rb +0 -42
- data/lib/gamefic/script/file.rb +0 -14
- data/lib/gamefic/script/text.rb +0 -14
- data/lib/gamefic/shell.rb +0 -76
- data/lib/gamefic/source.rb +0 -14
- data/lib/gamefic/source/base.rb +0 -12
- data/lib/gamefic/source/file.rb +0 -23
- data/lib/gamefic/source/text.rb +0 -16
- data/lib/gamefic/tester.rb +0 -19
- data/lib/gamefic/text.rb +0 -8
- data/lib/gamefic/text/ansi.rb +0 -53
- data/lib/gamefic/text/html.rb +0 -68
- data/lib/gamefic/text/html/conversions.rb +0 -250
- data/lib/gamefic/text/html/entities.rb +0 -9
- data/lib/gamefic/tty.rb +0 -10
- data/lib/gamefic/user.rb +0 -7
- data/lib/gamefic/user/base.rb +0 -29
- data/lib/gamefic/user/tty.rb +0 -38
@@ -1,240 +1,255 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def
|
17
|
-
@
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
#
|
42
|
-
#
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
#
|
61
|
-
#
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
# @
|
118
|
-
# @
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
def
|
152
|
-
result = []
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
end
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
1
|
+
module Gamefic
|
2
|
+
module World
|
3
|
+
# A collection of rules for performing commands.
|
4
|
+
#
|
5
|
+
class Playbook
|
6
|
+
def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
|
7
|
+
@commands = commands
|
8
|
+
@syntaxes = syntaxes
|
9
|
+
@validators = validators
|
10
|
+
@disambiguator = disambiguator
|
11
|
+
end
|
12
|
+
|
13
|
+
# An array of available syntaxes.
|
14
|
+
#
|
15
|
+
# @return [Array<Gamefic::Syntax>]
|
16
|
+
def syntaxes
|
17
|
+
@syntaxes
|
18
|
+
end
|
19
|
+
|
20
|
+
# An array of available actions.
|
21
|
+
#
|
22
|
+
# @return [Array<Gamefic::Action>]
|
23
|
+
def actions
|
24
|
+
@commands.values.flatten
|
25
|
+
end
|
26
|
+
|
27
|
+
# An array of recognized verbs.
|
28
|
+
#
|
29
|
+
# @return [Array<Symbol>]
|
30
|
+
def verbs
|
31
|
+
@commands.keys
|
32
|
+
end
|
33
|
+
|
34
|
+
# An array of defined validators.
|
35
|
+
#
|
36
|
+
# @return [Array<Proc>]
|
37
|
+
def validators
|
38
|
+
@validators
|
39
|
+
end
|
40
|
+
|
41
|
+
# Get the action for handling ambiguous entity references.
|
42
|
+
#
|
43
|
+
def disambiguator
|
44
|
+
@disambiguator ||= Action.subclass(nil, Query::Base.new) do |actor, entities|
|
45
|
+
definites = []
|
46
|
+
entities.each { |entity|
|
47
|
+
definites.push entity.definitely
|
48
|
+
}
|
49
|
+
actor.tell "I don't know which you mean: #{definites.join_or}."
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set the action for handling ambiguous entity references.
|
54
|
+
#
|
55
|
+
def disambiguate &block
|
56
|
+
@disambiguator = Action.subclass(nil, Query::Base.new, meta: true, &block)
|
57
|
+
@disambiguator
|
58
|
+
end
|
59
|
+
|
60
|
+
# Add a block that determines whether an action can be executed.
|
61
|
+
#
|
62
|
+
def validate &block
|
63
|
+
@validators.push block
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get an Array of all Actions associated with the specified verb.
|
67
|
+
#
|
68
|
+
# @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
|
69
|
+
# @return [Array<Action>] The verb's associated Actions
|
70
|
+
def actions_for verb
|
71
|
+
@commands[verb] || []
|
72
|
+
end
|
73
|
+
|
74
|
+
# Create an Action that responds to a command.
|
75
|
+
# An Action uses the command argument to identify the imperative verb that
|
76
|
+
# triggers the action.
|
77
|
+
# It can also accept queries to tokenize the remainder of the input and
|
78
|
+
# filter for particular entities or properties.
|
79
|
+
# The block argument contains the code to be executed when the input
|
80
|
+
# matches all of the Action's criteria (i.e., verb and queries).
|
81
|
+
#
|
82
|
+
# @example A simple Action.
|
83
|
+
# respond :salute do |actor|
|
84
|
+
# actor.tell "Hello, sir!"
|
85
|
+
# end
|
86
|
+
# # The command "salute" will respond "Hello, sir!"
|
87
|
+
#
|
88
|
+
# @example An Action that accepts a Character
|
89
|
+
# respond :salute, Use.visible(Character) do |actor, character|
|
90
|
+
# actor.tell "#{The character} returns your salute."
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# @param verb [Symbol] An imperative verb for the command
|
94
|
+
# @param queries [Array<Query::Base>] Filters for the command's tokens
|
95
|
+
# @yieldparam [Gamefic::Actor]
|
96
|
+
# @return [Class<Gamefic::Action>]
|
97
|
+
def respond(verb, *queries, &proc)
|
98
|
+
act = Action.subclass verb, *queries, &proc
|
99
|
+
add_action act
|
100
|
+
act
|
101
|
+
end
|
102
|
+
|
103
|
+
# Create a Meta Action that responds to a command.
|
104
|
+
# Meta Actions are very similar to standard Actions, except the Plot
|
105
|
+
# understands them to be commands that operate above and/or outside of the
|
106
|
+
# actual game world. Examples of Meta Actions are commands that report the
|
107
|
+
# player's current score, save and restore saved games, or list the game's
|
108
|
+
# credits.
|
109
|
+
#
|
110
|
+
# @example A simple Meta Action
|
111
|
+
# meta :credits do |actor|
|
112
|
+
# actor.tell "This game was written by John Smith."
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# @param verb [Symbol] An imperative verb for the command
|
116
|
+
# @param queries [Array<Query::Base>] Filters for the command's tokens
|
117
|
+
# @yieldparam [Gamefic::Actor]
|
118
|
+
# @return [Class<Gamefic::Action>]
|
119
|
+
def meta(verb, *queries, &proc)
|
120
|
+
act = Action.subclass verb, *queries, meta: true, &proc
|
121
|
+
add_action act
|
122
|
+
act
|
123
|
+
end
|
124
|
+
|
125
|
+
# Create an alternate Syntax for an Action.
|
126
|
+
# The command and its translation can be parameterized.
|
127
|
+
#
|
128
|
+
# @example Create a synonym for the Inventory Action.
|
129
|
+
# interpret "catalogue", "inventory"
|
130
|
+
# # The command "catalogue" will be translated to "inventory"
|
131
|
+
#
|
132
|
+
# @example Create a parameterized synonym for the Look Action.
|
133
|
+
# interpret "scrutinize :entity", "look :entity"
|
134
|
+
# # The command "scrutinize chair" will be translated to "look chair"
|
135
|
+
#
|
136
|
+
# @param input [String] The format of the original command
|
137
|
+
# @param translation [String] The format of the translated command
|
138
|
+
# @return [Syntax] the Syntax object
|
139
|
+
def interpret(input, translation)
|
140
|
+
syn = Syntax.new(input, translation)
|
141
|
+
add_syntax syn
|
142
|
+
syn
|
143
|
+
end
|
144
|
+
|
145
|
+
# Get an array of actions, derived from the specified command, that the
|
146
|
+
# actor can potentially execute.
|
147
|
+
# The command can either be a single string (e.g., "examine book") or a
|
148
|
+
# list of tokens (e.g., :examine, @book).
|
149
|
+
#
|
150
|
+
# @return [Array<Gamefic::Action>]
|
151
|
+
def dispatch(actor, *command)
|
152
|
+
result = []
|
153
|
+
if command.length > 1
|
154
|
+
result.concat dispatch_from_params(actor, command[0], command[1..-1])
|
155
|
+
end
|
156
|
+
if result.empty?
|
157
|
+
result.concat dispatch_from_string(actor, command.join(' '))
|
158
|
+
end
|
159
|
+
result
|
160
|
+
end
|
161
|
+
|
162
|
+
# Get an array of actions, derived from the specified command, that the
|
163
|
+
# actor can potentially execute.
|
164
|
+
# The command should be a plain-text string, e.g., "examine the book."
|
165
|
+
#
|
166
|
+
# @return [Array<Gamefic::Action>]
|
167
|
+
def dispatch_from_string actor, text
|
168
|
+
result = []
|
169
|
+
commands = Syntax.tokenize(text, actor.syntaxes)
|
170
|
+
commands.each { |c|
|
171
|
+
available = actions_for(c.verb)
|
172
|
+
available.each { |a|
|
173
|
+
next if a.hidden?
|
174
|
+
o = a.attempt(actor, c.arguments)
|
175
|
+
result.unshift o unless o.nil?
|
176
|
+
}
|
177
|
+
}
|
178
|
+
sort_and_reduce_actions result
|
179
|
+
end
|
180
|
+
|
181
|
+
# Get an array of actions, derived from the specified verb and params,
|
182
|
+
# that the actor can potentially execute.
|
183
|
+
#
|
184
|
+
# @return [Array<Gamefic::Action>]
|
185
|
+
def dispatch_from_params actor, verb, params
|
186
|
+
result = []
|
187
|
+
available = actions_for(verb)
|
188
|
+
available.each { |a|
|
189
|
+
result.unshift a.new(actor, params) if a.valid?(actor, params)
|
190
|
+
}
|
191
|
+
sort_and_reduce_actions result
|
192
|
+
end
|
193
|
+
|
194
|
+
# Duplicate the playbook.
|
195
|
+
# This method will duplicate the commands hash and the syntax array so
|
196
|
+
# the new playbook can be modified without affecting the original.
|
197
|
+
#
|
198
|
+
# @return [Playbook]
|
199
|
+
def dup
|
200
|
+
Playbook.new commands: @commands.dup, syntaxes: @syntaxes.dup
|
201
|
+
end
|
202
|
+
|
203
|
+
def freeze
|
204
|
+
@commands.freeze
|
205
|
+
@syntaxes.freeze
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def add_action(action)
|
211
|
+
@commands[action.verb] ||= []
|
212
|
+
@commands[action.verb].push action
|
213
|
+
generate_default_syntax action
|
214
|
+
end
|
215
|
+
|
216
|
+
def generate_default_syntax action
|
217
|
+
user_friendly = action.verb.to_s.gsub(/_/, ' ')
|
218
|
+
args = []
|
219
|
+
used_names = []
|
220
|
+
action.queries.each { |c|
|
221
|
+
num = 1
|
222
|
+
new_name = ":var"
|
223
|
+
while used_names.include? new_name
|
224
|
+
num = num + 1
|
225
|
+
new_name = ":var#{num}"
|
226
|
+
end
|
227
|
+
used_names.push new_name
|
228
|
+
user_friendly += " #{new_name}"
|
229
|
+
args.push new_name
|
230
|
+
}
|
231
|
+
add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}") unless action.verb.to_s.start_with?('_')
|
232
|
+
end
|
233
|
+
|
234
|
+
def add_syntax syntax
|
235
|
+
if @commands[syntax.verb] == nil
|
236
|
+
raise "No actions exist for \"#{syntax.verb}\""
|
237
|
+
end
|
238
|
+
@syntaxes.unshift syntax
|
239
|
+
@syntaxes.uniq! &:signature
|
240
|
+
@syntaxes.sort! { |a, b|
|
241
|
+
if a.token_count == b.token_count
|
242
|
+
# For syntaxes of the same length, length of action takes precedence
|
243
|
+
b.first_word <=> a.first_word
|
244
|
+
else
|
245
|
+
b.token_count <=> a.token_count
|
246
|
+
end
|
247
|
+
}
|
248
|
+
end
|
249
|
+
|
250
|
+
def sort_and_reduce_actions arr
|
251
|
+
arr.sort_by.with_index{|a, i| [a.rank, -i]}.reverse.uniq(&:class)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|