story-teller 1.1.3
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 +7 -0
- data/LICENSE +623 -0
- data/README.md +188 -0
- data/Rakefile +58 -0
- data/config/database.yml +37 -0
- data/exe/inform.rb +6 -0
- data/game/config.yml +5 -0
- data/game/example.inf +90 -0
- data/game/example.rb +105 -0
- data/game/forms/example_form.rb +2 -0
- data/game/grammar/admin.inf.rb +185 -0
- data/game/grammar/builder.inf.rb +310 -0
- data/game/grammar/game_grammar.inf.rb +6 -0
- data/game/grammar/meta.inf.rb +41 -0
- data/game/languages/english.rb +571 -0
- data/game/models/example_model.rb +2 -0
- data/game/modules/example_module.rb +9 -0
- data/game/modules/parser_extensions.rb +264 -0
- data/game/rules/example_state.rb +2 -0
- data/game/scripts/example_script.rb +2 -0
- data/game/topics/example_topic.rb +2 -0
- data/game/verbs/game_verbs.rb +35 -0
- data/game/verbs/metaverbs.rb +2066 -0
- data/lib/story_teller/application.rb +82 -0
- data/lib/story_teller/cli.rb +35 -0
- data/lib/story_teller/color.rb +144 -0
- data/lib/story_teller/config.rb +61 -0
- data/lib/story_teller/curses_adapter.rb +30 -0
- data/lib/story_teller/database.rb +527 -0
- data/lib/story_teller/game/loader.rb +276 -0
- data/lib/story_teller/game.rb +22 -0
- data/lib/story_teller/inform/models.rb +42 -0
- data/lib/story_teller/inform/relational/link.rb +239 -0
- data/lib/story_teller/inform/relational/module.rb +203 -0
- data/lib/story_teller/inform/relational/object.rb +546 -0
- data/lib/story_teller/inform/relational/tag.rb +152 -0
- data/lib/story_teller/options.rb +151 -0
- data/lib/story_teller/persistence.rb +340 -0
- data/lib/story_teller/player_character.rb +99 -0
- data/lib/story_teller/privileges.rb +55 -0
- data/lib/story_teller/runtime.rb +381 -0
- data/lib/story_teller/snapshots.rb +412 -0
- data/lib/story_teller/terminal.rb +58 -0
- data/lib/story_teller/version.rb +24 -0
- data/lib/story_teller_cli.rb +34 -0
- metadata +158 -0
|
@@ -0,0 +1,2066 @@
|
|
|
1
|
+
# game/verbs/metaverbs.rb
|
|
2
|
+
# encoding: utf-8
|
|
3
|
+
# frozen_string_literal: false
|
|
4
|
+
|
|
5
|
+
# Copyright Nels Nelson 2008-2025 but freely usable (see license)
|
|
6
|
+
#
|
|
7
|
+
# This file is part of the StoryTeller.
|
|
8
|
+
#
|
|
9
|
+
# The StoryTeller is free software: you can redistribute it and/or
|
|
10
|
+
# modify it under the terms of the GNU General Public License as published
|
|
11
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
|
12
|
+
# (at your option) any later version.
|
|
13
|
+
#
|
|
14
|
+
# The StoryTeller is distributed in the hope that it will be useful,
|
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
# GNU General Public License for more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the GNU General Public License
|
|
20
|
+
# along with the StoryTeller. If not, see <http://www.gnu.org/licenses/>.
|
|
21
|
+
|
|
22
|
+
require 'fileutils'
|
|
23
|
+
|
|
24
|
+
# The Metaverbs module
|
|
25
|
+
module Metaverbs
|
|
26
|
+
Parser = Inform::Parser
|
|
27
|
+
Newline = "\n".freeze
|
|
28
|
+
|
|
29
|
+
def TestSub
|
|
30
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
31
|
+
case special
|
|
32
|
+
|
|
33
|
+
when /loop/
|
|
34
|
+
add_event do
|
|
35
|
+
iteration = 0
|
|
36
|
+
loop do
|
|
37
|
+
publish 'Iteration: ' + (iteration += 1)
|
|
38
|
+
sleep 1
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
when /events/
|
|
42
|
+
if special_number1.nil?
|
|
43
|
+
add_event do
|
|
44
|
+
delay(0.5) do
|
|
45
|
+
@player.inform "Something happened."
|
|
46
|
+
add_event do
|
|
47
|
+
"Something descendant happened."
|
|
48
|
+
end
|
|
49
|
+
end.delay(0.5) do
|
|
50
|
+
"Something else will happen..."
|
|
51
|
+
end.delay(1) do
|
|
52
|
+
"In 3..."
|
|
53
|
+
end.delay(1) do
|
|
54
|
+
"2..."
|
|
55
|
+
end.delay(1) do
|
|
56
|
+
"1..."
|
|
57
|
+
end
|
|
58
|
+
end.delay(5) do
|
|
59
|
+
"Something else happened."
|
|
60
|
+
end
|
|
61
|
+
"Testing events..."
|
|
62
|
+
else
|
|
63
|
+
n = special_number1.to_i
|
|
64
|
+
add_event do
|
|
65
|
+
@player.inform "Something happened."
|
|
66
|
+
end.delay(0.5) do
|
|
67
|
+
"Something else will happen..."
|
|
68
|
+
end.delay(0.5) do
|
|
69
|
+
# TODO: Why is there a maximum of 6927??? FIXME
|
|
70
|
+
@player.inform "#{n} events will happen..."
|
|
71
|
+
e = add_event do
|
|
72
|
+
"Something happened. [0]"
|
|
73
|
+
end
|
|
74
|
+
(n - 1).times do |i|
|
|
75
|
+
e = e.add_event do
|
|
76
|
+
format("Something happened. [%<iteration>s]", iteration: i + 1)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
"Testing #{special_number1} events..."
|
|
82
|
+
end
|
|
83
|
+
when /error/
|
|
84
|
+
println "Testing errors..."
|
|
85
|
+
raise "Testing errors..."
|
|
86
|
+
else
|
|
87
|
+
"You passed the test."
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def IsInSub
|
|
92
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
93
|
+
if noun.in? second
|
|
94
|
+
The(noun) + " is in " + the(second) + "."
|
|
95
|
+
elsif second == player
|
|
96
|
+
L__M(:Drop, 2, noun)
|
|
97
|
+
else
|
|
98
|
+
The(noun) + " is not in " + the(second) + "."
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
alias IsNotInSub IsInSub
|
|
102
|
+
|
|
103
|
+
def admins
|
|
104
|
+
StoryTeller::Runtime.instance
|
|
105
|
+
.privileged_identities(:admin).map(&:privilege_label).sort
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def AdminsSub
|
|
109
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
110
|
+
|
|
111
|
+
names = admins
|
|
112
|
+
return 'There are no current admins.' if names.empty?
|
|
113
|
+
|
|
114
|
+
"Current admins are: #{names.join(', ')}"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def builders
|
|
118
|
+
StoryTeller::Runtime.instance
|
|
119
|
+
.privileged_identities(:builder).map(&:privilege_label).sort
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def BuildersSub
|
|
123
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
124
|
+
|
|
125
|
+
names = builders
|
|
126
|
+
return 'There are no current builders.' if names.empty?
|
|
127
|
+
|
|
128
|
+
"Current builders are: #{names.join(', ')}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def ActionSub
|
|
132
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
133
|
+
target = noun
|
|
134
|
+
command = text
|
|
135
|
+
target.add_event do
|
|
136
|
+
target.parse(command)
|
|
137
|
+
end
|
|
138
|
+
"You force " + the(target) + " to " + command + "."
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def AgainSub
|
|
142
|
+
history.pop
|
|
143
|
+
parse history.last
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
ConverterTemplate = 'to_%<data_format>'.freeze
|
|
147
|
+
|
|
148
|
+
def AncestorsSub
|
|
149
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
150
|
+
o = @player
|
|
151
|
+
o = noun if noun.object?
|
|
152
|
+
a = o.ancestors
|
|
153
|
+
unless special.nil?
|
|
154
|
+
converter = format(ConverterTemplate, data_format: special).to_sym
|
|
155
|
+
return a.send(converter, { include: %i[tagged modularized] }) if a.respond_to? converter
|
|
156
|
+
end
|
|
157
|
+
a.identities
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def AssignSub
|
|
161
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
162
|
+
return "That value is invalid." if text.nil? || text.empty?
|
|
163
|
+
property = special.to_sym
|
|
164
|
+
if property == :name
|
|
165
|
+
noun.values[:name] = text
|
|
166
|
+
noun.save
|
|
167
|
+
return "The #{property} of " + the(noun) + " is " + ensure_punctuation(noun.name_words)
|
|
168
|
+
elsif Object.methods.include?(property)
|
|
169
|
+
return "That property may not be modified."
|
|
170
|
+
else
|
|
171
|
+
value = text.number? ? text.to_n : text
|
|
172
|
+
# The following can accidentally invoke a method of the noun object:
|
|
173
|
+
# noun.&property, value
|
|
174
|
+
# Don't do that here, since property is user input. Do this instead:
|
|
175
|
+
noun.properties[property] = value
|
|
176
|
+
noun.save
|
|
177
|
+
end
|
|
178
|
+
"The #{property} of " + the(noun) + " is " + ensure_punctuation(noun.&property)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def UnassignSub
|
|
182
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
183
|
+
property = special.to_sym
|
|
184
|
+
noun.properties.delete special
|
|
185
|
+
noun.properties.delete property
|
|
186
|
+
noun.save
|
|
187
|
+
The(noun) + " no longer has #{property}."
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def AssignRoomSub
|
|
191
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
192
|
+
invoke :Assign, location, text
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def UnassignRoomSub
|
|
196
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
197
|
+
invoke :Unassign, location, text
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def AttributesSub
|
|
201
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
202
|
+
if !special.nil? && Inform::Tag.respond_to?(special)
|
|
203
|
+
result = Inform::Tag.send(special)
|
|
204
|
+
return result || "None."
|
|
205
|
+
end
|
|
206
|
+
Inform::Tag.map(&:name).sort
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def AreasScope
|
|
210
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
211
|
+
case @scope_stage
|
|
212
|
+
when 1 then false # Return single objects only
|
|
213
|
+
when 2
|
|
214
|
+
areas = Inform::Object.where(object_type: 'Area').all
|
|
215
|
+
ScopeWithin(areas); true
|
|
216
|
+
when 3 then L__M(:Take, 8) # TODO: This is broken somehow FIXME
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def AreaSub
|
|
221
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
222
|
+
return "For some reason your location cannot be determined." if location.nil?
|
|
223
|
+
location.link :area, noun if noun.nil? && noun.is_a?(Area)
|
|
224
|
+
area = location.linkto :area
|
|
225
|
+
while parent(area)
|
|
226
|
+
println "You are in a sub-area called " + A(area) + " [#{area.identity}]."
|
|
227
|
+
area = parent(area)
|
|
228
|
+
end
|
|
229
|
+
return "This room does not belong to any area." unless area
|
|
230
|
+
"The main area here is called " + A(area) + " [#{area.identity}]."
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def AreasSub
|
|
234
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
235
|
+
areas = Inform::Object.where(object_type: 'Area').order(:name).all
|
|
236
|
+
return "There are no areas." if areas.empty?
|
|
237
|
+
println "The following areas are available:"
|
|
238
|
+
areas.identities
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def BackupSub
|
|
242
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
243
|
+
unless Inform::Game.config.include?(:admin_email)
|
|
244
|
+
return "Please define admin_email in the config.yml file."
|
|
245
|
+
end
|
|
246
|
+
unless Inform::Game.config.include?(:game_name)
|
|
247
|
+
return "Please define game_name in the config.yml file."
|
|
248
|
+
end
|
|
249
|
+
unless special == 'wetrun'
|
|
250
|
+
println `scripts/backup.rb`.strip
|
|
251
|
+
return "Try again with 'backup wetrun' if you really want to do this."
|
|
252
|
+
end
|
|
253
|
+
database = Inform::Game.config[:game_name]
|
|
254
|
+
event do
|
|
255
|
+
file = `scripts/backup.rb --wet-run`.strip
|
|
256
|
+
return "Failed to backup #{database} database." unless File.exist? file
|
|
257
|
+
to_address = StoryTeller::IO::Session.of(@player).login.account.email
|
|
258
|
+
from_address = Inform::Game.config[:admin_email]
|
|
259
|
+
subject = 'Database backup'
|
|
260
|
+
message = "This is a copy of the #{database} database in SQL dump format."
|
|
261
|
+
send_email to_address, from_address, subject, message, file
|
|
262
|
+
new_line
|
|
263
|
+
println "A file was created at #{file} containing a backup of the #{database} database."
|
|
264
|
+
"A copy of that file has been e-mailed to #{to_address}."
|
|
265
|
+
end
|
|
266
|
+
"Backing up the #{database} database..."
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# TODO: When the game shuts down, or the player quits, all of
|
|
270
|
+
# this session re-wiring must be undone.
|
|
271
|
+
def BecomeSub
|
|
272
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
273
|
+
begin
|
|
274
|
+
take @player, :admin
|
|
275
|
+
give noun, :admin
|
|
276
|
+
session = StoryTeller::IO::Session.of(@player)
|
|
277
|
+
if session
|
|
278
|
+
@player.inflib.unsubscribe session
|
|
279
|
+
# @player.unsubscribe session
|
|
280
|
+
session.outbound.unsubscribe_all
|
|
281
|
+
|
|
282
|
+
unsubscribe @player
|
|
283
|
+
@player = actor = selfobj = noun
|
|
284
|
+
|
|
285
|
+
# session.inbound.subscribe @player.inflib
|
|
286
|
+
# @player.inflib.subscribe session
|
|
287
|
+
@player.inflib.subscribe session.outbound
|
|
288
|
+
@player.subscribe session.outbound
|
|
289
|
+
session.control(@player)
|
|
290
|
+
else
|
|
291
|
+
unsubscribe @player
|
|
292
|
+
@player = actor = selfobj = noun
|
|
293
|
+
if session
|
|
294
|
+
session.inbound.subscribe @player.inflib
|
|
295
|
+
@player.inflib.subscribe session
|
|
296
|
+
@player.inflib.subscribe session.outbound
|
|
297
|
+
@player.subscribe session.outbound
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
_invoke :Look
|
|
301
|
+
rescue StandardError => e
|
|
302
|
+
log.error "Error becoming other #{noun}", e
|
|
303
|
+
end
|
|
304
|
+
true
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def BellSub
|
|
308
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
309
|
+
"\aBong!"
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def BootSub
|
|
313
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
314
|
+
return L__M(:Boot, 1) if noun.nil?
|
|
315
|
+
StoryTeller::IO::Session.of(noun).disconnect
|
|
316
|
+
"Dropped connection for #{noun}."
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def BranchSub
|
|
320
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
321
|
+
o = noun.object? ? noun : @player
|
|
322
|
+
b = o.branch
|
|
323
|
+
unless special.nil?
|
|
324
|
+
converter = format(ConverterTemplate, data_format: special).to_sym
|
|
325
|
+
return b.send(converter, { include: %i[tagged modularized] }) if branch.respond_to?(converter)
|
|
326
|
+
end
|
|
327
|
+
b
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def BroadcastSub
|
|
331
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
332
|
+
StoryTeller::IO::Session.players.inform text
|
|
333
|
+
"[ Informed every player of your communication: #{text} ]"
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def ClassesSub
|
|
337
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
338
|
+
return "ObjectSpace is not enabled" unless ObjectSpace.enabled?
|
|
339
|
+
ObjectSpace.each_object(Class) do |klass|
|
|
340
|
+
loop do
|
|
341
|
+
print klass
|
|
342
|
+
klass = klass.superclass
|
|
343
|
+
break if klass.nil?
|
|
344
|
+
print " < "
|
|
345
|
+
end
|
|
346
|
+
println
|
|
347
|
+
end
|
|
348
|
+
""
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def ClassifySub
|
|
352
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
353
|
+
unless special.nil?
|
|
354
|
+
klass = find_class special
|
|
355
|
+
return "No such class #{special}." if klass.nil?
|
|
356
|
+
o = noun.classify_as klass
|
|
357
|
+
PronounNotice(o)
|
|
358
|
+
end
|
|
359
|
+
"The class of " + the(o) + " is " + o.object_type + "."
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def TaxonomySub
|
|
363
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
364
|
+
inheritance = noun.class.ancestors
|
|
365
|
+
taxonomy = inheritance[0..inheritance.index(Inform::Object)]
|
|
366
|
+
"The taxonomy of " + the(noun) + " is " + taxonomy.join(' < ') + "."
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def ClearSub
|
|
370
|
+
"Not yet implemented."
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def clear_history
|
|
374
|
+
n = history.length
|
|
375
|
+
history.clear
|
|
376
|
+
"Forgot #{n} commands."
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def ClockSub
|
|
380
|
+
require 'lib/clock'
|
|
381
|
+
"You turn the clock #{Clock.toggle}."
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def StopwatchSub
|
|
385
|
+
require 'lib/stopwatch'
|
|
386
|
+
println "You turn the stopwatch #{Stopwatch.toggle}."
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
Colors = %i[red green yellow blue purple cyan white black].freeze
|
|
390
|
+
Styles = %i[bold italic underline blink flash inverse hidden strikethrough].freeze
|
|
391
|
+
SeventySpacesString = ' ' * 70
|
|
392
|
+
|
|
393
|
+
def ColorSub
|
|
394
|
+
style :bold
|
|
395
|
+
print "bold"
|
|
396
|
+
unstyle :bold
|
|
397
|
+
new_line
|
|
398
|
+
|
|
399
|
+
color :black_on_white
|
|
400
|
+
bgcolor :white
|
|
401
|
+
print "black on white"
|
|
402
|
+
unbgcolor :white
|
|
403
|
+
uncolor :black_on_white
|
|
404
|
+
new_line
|
|
405
|
+
|
|
406
|
+
Colors.each do |color_name|
|
|
407
|
+
color(color_name); print color_name.to_s; uncolor(color_name); new_line
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
Colors.each do |color_name|
|
|
411
|
+
bgcolor(color_name); print SeventySpacesString; unbgcolor(color_name); new_line
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
Styles.each do |style_name|
|
|
415
|
+
style(style_name); print style_name.to_s; unstyle(style_name); new_line
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
false
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def CommandsSub
|
|
422
|
+
more(many_per_line(Inform::Grammar::Verbs.all(@player)))
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def CommandsByModeSub
|
|
426
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
427
|
+
return CommandsSub() if special.nil? || (special.to_sym == :admin && !privileged?(:admin))
|
|
428
|
+
more(many_per_line(Inform::Grammar::Verbs.by_mode(special)))
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def ConnectionsSub
|
|
432
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
433
|
+
if noun
|
|
434
|
+
println "#{player} #=> #{player.connections}"
|
|
435
|
+
return true
|
|
436
|
+
end
|
|
437
|
+
StoryTeller::IO::Session.players.each do |player|
|
|
438
|
+
println "#{player} #=> #{player.connections}"
|
|
439
|
+
end
|
|
440
|
+
true
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def CopySub
|
|
444
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
445
|
+
if noun
|
|
446
|
+
o = noun.clone
|
|
447
|
+
@player << o
|
|
448
|
+
PronounNotice(o)
|
|
449
|
+
"You create a copy of " + the(noun) + "."
|
|
450
|
+
else
|
|
451
|
+
"Sorry, that could not be copied."
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def CreateSub
|
|
456
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
457
|
+
words = text.split
|
|
458
|
+
words.shift while Inform::English::Articles.include?(words.first)
|
|
459
|
+
name = words.join(' ').strip
|
|
460
|
+
return "Creation failed." if name.empty?
|
|
461
|
+
o = Inform::Object.new(name: name)
|
|
462
|
+
@player << o
|
|
463
|
+
PronounNotice(o)
|
|
464
|
+
"You create " + a(o) + "!"
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def CreateBeforeSub
|
|
468
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
469
|
+
"You are trying to " + text + " when you " + special + " " + the(second) + "."
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def CreateBeforeRoomSub
|
|
473
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
474
|
+
"When you " + special + " " + the(location) + ", you will be informed that \"" + text + "\""
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def CreateDoorSub
|
|
478
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
479
|
+
return unless noun.respond_to? :door_dir
|
|
480
|
+
here = location
|
|
481
|
+
room = here.linkto(noun.door_dir)
|
|
482
|
+
unless room
|
|
483
|
+
room = Room.create
|
|
484
|
+
room.description = "There is nothing here yet."
|
|
485
|
+
room.save
|
|
486
|
+
room.name = "Room #{room.id}"
|
|
487
|
+
room.save
|
|
488
|
+
room.link :author, @player
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
door = Door.new(name: text || 'door')
|
|
492
|
+
here << door
|
|
493
|
+
room << door.portal
|
|
494
|
+
door.to room
|
|
495
|
+
|
|
496
|
+
opposite_direc = Inform::English::CompassDirectionOpposites[noun]
|
|
497
|
+
door.door_dir = noun.door_dir
|
|
498
|
+
door.portal.door_dir = opposite_direc.door_dir
|
|
499
|
+
here.link noun.door_dir, door
|
|
500
|
+
room.link opposite_direc.door_dir, door.portal
|
|
501
|
+
room.refresh
|
|
502
|
+
here.refresh
|
|
503
|
+
|
|
504
|
+
PronounNotice(door.portal)
|
|
505
|
+
_invoke :Goto, room
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
def id_parse_routine
|
|
509
|
+
l = TryNumber(@wn); @wn += 1
|
|
510
|
+
return false unless l.number?
|
|
511
|
+
getobject(l.to_i)
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
class Room < Inform::Object; end
|
|
515
|
+
class Region < Inform::Object; end
|
|
516
|
+
class Area < Region; end
|
|
517
|
+
|
|
518
|
+
def CreateRoomSub
|
|
519
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
520
|
+
room = Room.create
|
|
521
|
+
room.description = "There is nothing here yet."
|
|
522
|
+
room.save
|
|
523
|
+
room.name = text ? text.titleize : "Room #{room.id}"
|
|
524
|
+
room.save
|
|
525
|
+
room.link :author, @player
|
|
526
|
+
|
|
527
|
+
# Links
|
|
528
|
+
if !noun.nil? && !second.nil? && [noun, second].all? { |a| a.respond_to? :door_dir }
|
|
529
|
+
parent(@player).link noun.door_dir, room
|
|
530
|
+
room.link second.door_dir, parent(@player)
|
|
531
|
+
elsif !noun.nil?
|
|
532
|
+
parent(@player).link noun.door_dir, room
|
|
533
|
+
room.link Inform::English::CompassDirectionOpposites[noun].door_dir, parent(@player)
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
_invoke :Goto, room
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
def CreatePairSub
|
|
540
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
541
|
+
words = text.split
|
|
542
|
+
words.shift while Inform::English::Articles.include?(words.first)
|
|
543
|
+
name = words.join(' ').strip
|
|
544
|
+
return "Creation failed." if name.empty?
|
|
545
|
+
o = Pairable::Pair.new(name: name)
|
|
546
|
+
@player << o
|
|
547
|
+
PronounNotice(o)
|
|
548
|
+
"You create " + a(o) + "!"
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
def DebugSub
|
|
552
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
553
|
+
if special_number1
|
|
554
|
+
session&.settings&.[]=(:parser_trace, special_number1)
|
|
555
|
+
debug special_number1
|
|
556
|
+
println "Set parser trace to level #{@parser_trace}."
|
|
557
|
+
else
|
|
558
|
+
debug
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
if defined? DEBUG
|
|
562
|
+
"Debugging is on."
|
|
563
|
+
else
|
|
564
|
+
"Debugging is off."
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def DeleteSub
|
|
569
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
570
|
+
# Never remove stuff like Compass directions or the dark
|
|
571
|
+
return "Sorry, " + the(noun) + " is a library object." if noun.ephemeral?
|
|
572
|
+
|
|
573
|
+
noun.remove
|
|
574
|
+
The(noun) + " has been removed."
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def DescendantsSub(include_ephemeral_objects = nil)
|
|
578
|
+
include_ephemeral_objects = false if include_ephemeral_objects.nil?
|
|
579
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
580
|
+
o = noun.object? ? noun : @player
|
|
581
|
+
d = o.descendants
|
|
582
|
+
if include_ephemeral_objects
|
|
583
|
+
ephemeral_descendants = o.ephemeral_children +
|
|
584
|
+
o.ephemeral_children.map(&:descendants).flatten
|
|
585
|
+
d |= ephemeral_descendants
|
|
586
|
+
end
|
|
587
|
+
unless special.nil?
|
|
588
|
+
converter = format(ConverterTemplate, data_format: special).to_sym
|
|
589
|
+
return d.send(converter, { include: %i[tagged modularized] }) if d.respond_to?(converter)
|
|
590
|
+
end
|
|
591
|
+
if d.empty?
|
|
592
|
+
"None"
|
|
593
|
+
else
|
|
594
|
+
d.identities
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
def AllDescendantsSub
|
|
599
|
+
DescendantsSub(true)
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
def ClearDescriptionSub
|
|
603
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
604
|
+
noun.description = noun == location ? "There is nothing here yet." : nil
|
|
605
|
+
noun.save
|
|
606
|
+
println The(noun) + " is now described as the following:"
|
|
607
|
+
return PlayerTo(noun, 2) if noun == location
|
|
608
|
+
_invoke :Examine, noun
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def DescribeSub(obj = noun)
|
|
612
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
613
|
+
if !obj.nil? && !text.nil?
|
|
614
|
+
reset_prompt
|
|
615
|
+
obj.description = capitalize_sentences(ensure_punctuation(text))
|
|
616
|
+
obj.save
|
|
617
|
+
new_line
|
|
618
|
+
println The(obj) + " is now described as the following:"
|
|
619
|
+
return PlayerTo(obj, 2) if obj == location
|
|
620
|
+
_invoke :Examine, obj
|
|
621
|
+
elsif !solicited?
|
|
622
|
+
solicit "By all means, describe " + the(obj) + ": "
|
|
623
|
+
end
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def DescribeRoomSub
|
|
627
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
628
|
+
DescribeSub(location)
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def ClearRoomDescriptionSub
|
|
632
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
633
|
+
_invoke :ClearDescription, location
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
DefaultPurgeSubParams = { force: false }.freeze
|
|
637
|
+
|
|
638
|
+
def PurgeSub(params = {})
|
|
639
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
640
|
+
params = DefaultPurgeSubParams.merge(params)
|
|
641
|
+
force = params[:force]
|
|
642
|
+
# Stuff like Compass directions or the dark may not be destroyed
|
|
643
|
+
return "Sorry, " + the(noun) + " (#{noun.identity}) is a library object." if noun.ephemeral?
|
|
644
|
+
|
|
645
|
+
# The player must explicitly remove the :prized attribute from an
|
|
646
|
+
# object before destroying it
|
|
647
|
+
return The(noun) + " (#{noun.identity}) is a prized object." if noun.has? :prized
|
|
648
|
+
|
|
649
|
+
# Require confirmation when destroying character objects
|
|
650
|
+
if defined?(Character) && noun.is_a?(Character) && !force && !asked?
|
|
651
|
+
return yesorno "Are you sure you want to destroy the database record for the " +
|
|
652
|
+
"character #{noun} (#{noun.identity})? "
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
# TODO: Also check descendants of rooms in a hub area or region being destroyed
|
|
656
|
+
if defined?(Character) && noun.descendants.any?(Character) && !force
|
|
657
|
+
return "One or more Characters occupying " + the(noun) + " (#{noun.identity}) must " +
|
|
658
|
+
"be destroyed individually."
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
PlayerTo(@player.spawn_point) if noun == parent(@player)
|
|
662
|
+
message = "You have destroyed the database record for " + the(noun) + " (#{noun.identity})."
|
|
663
|
+
destroy noun
|
|
664
|
+
message
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
def PurgeDangerouslySub
|
|
668
|
+
PurgeSub(force: true)
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
def PurgeAllOrphansSub
|
|
672
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
673
|
+
return ask "Are you sure you want to purge all orphaned objects?" unless asked?
|
|
674
|
+
for obj in Inform::Object.all
|
|
675
|
+
next if obj.is_a?(Character) || obj.is_a?(Room)
|
|
676
|
+
unless obj.parent
|
|
677
|
+
destroy obj
|
|
678
|
+
println "You have destroyed the database record for " + a(obj) + "."
|
|
679
|
+
end
|
|
680
|
+
end
|
|
681
|
+
"Done purging orphaned objects from the database."
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
def ExportSub
|
|
685
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
686
|
+
# Never export stuff like Compass directions or the dark
|
|
687
|
+
return "Sorry, " + the(noun) + " is a library object." if noun.ephemeral?
|
|
688
|
+
|
|
689
|
+
type = special ? special.to_sym : :xml
|
|
690
|
+
|
|
691
|
+
if export noun, type
|
|
692
|
+
"Exported " + a(noun) + " [#{noun.identity}] as #{type}."
|
|
693
|
+
else
|
|
694
|
+
"Failed to export " + a(noun) + " [#{noun.identity}] as #{type}."
|
|
695
|
+
end
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
def ImportSub
|
|
699
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
700
|
+
file = text
|
|
701
|
+
return "Specify a file for importing." if file.nil?
|
|
702
|
+
return "That's not a file." unless File.exist? file
|
|
703
|
+
|
|
704
|
+
obj = import_object file
|
|
705
|
+
return "Import failed." if obj.nil?
|
|
706
|
+
println obj.inspect
|
|
707
|
+
|
|
708
|
+
move obj, @player
|
|
709
|
+
"Imported " + a(obj) + " [" + obj.identity + "]."
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
def FetchSub
|
|
713
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
714
|
+
return "Your search - #{text} - did not match any objects." unless noun
|
|
715
|
+
|
|
716
|
+
# Never fetch stuff like Compass directions or the dark
|
|
717
|
+
return "Sorry, " + the(noun) + " is a library object." if noun.ephemeral?
|
|
718
|
+
|
|
719
|
+
move noun, @player
|
|
720
|
+
PronounNotice(noun)
|
|
721
|
+
"Fetched " + a(noun) + " [" + noun.identity + "]."
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
def FindSub
|
|
725
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
726
|
+
return "Your search - #{text} - did not match any objects." if @search_results.empty?
|
|
727
|
+
n = @search_results.count
|
|
728
|
+
println "#{n} " + pluralize("instance", n) + " found."
|
|
729
|
+
@search_results.each do |obj|
|
|
730
|
+
print "Found " + a(obj) + " [" + obj.identity + "]"
|
|
731
|
+
if (parent_obj = parent(obj))
|
|
732
|
+
if parent_obj.has?(:supporter) then print " on "
|
|
733
|
+
else print " in "
|
|
734
|
+
end
|
|
735
|
+
print a(parent_obj) + " [" + parent_obj.identity + "]"
|
|
736
|
+
end
|
|
737
|
+
println "."
|
|
738
|
+
end
|
|
739
|
+
false
|
|
740
|
+
end
|
|
741
|
+
|
|
742
|
+
def FindByClassSub
|
|
743
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
744
|
+
results = Inform::Object.where(Sequel.like(:object_type, "%#{special}%")).order(:name, :id)
|
|
745
|
+
return "Your search - class #{special} - did not match any objects." if results.empty?
|
|
746
|
+
n = results.count
|
|
747
|
+
println "#{n} " + pluralize("instance", n) + " found."
|
|
748
|
+
results.each do |obj|
|
|
749
|
+
print "Found " + a(obj) + " [" + obj.identity + "]"
|
|
750
|
+
print " in " + a(parent(obj)) + " [" + parent(obj).identity + "]" if parent(obj)
|
|
751
|
+
println "."
|
|
752
|
+
end
|
|
753
|
+
false
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
def FindByPropertySub
|
|
757
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
758
|
+
query = text == 'anything' ? '' : text
|
|
759
|
+
results = Inform::Object.where(Sequel.like(:properties, "%:#{special}: #{query}%")).order(:name, :id)
|
|
760
|
+
return "Your search - for object #{special} set to #{text} - did not match any objects." if results.empty?
|
|
761
|
+
n = results.count
|
|
762
|
+
println "#{n} " + pluralize("instance", n) + " found."
|
|
763
|
+
results.each do |obj|
|
|
764
|
+
print "Found " + a(obj) + " [" + obj.identity + "]"
|
|
765
|
+
print " in " + a(parent(obj)) + " [" + parent(obj).identity + "]" if parent(obj)
|
|
766
|
+
println "."
|
|
767
|
+
end
|
|
768
|
+
false
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
def FindByDescriptionSub
|
|
772
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
773
|
+
query = text == 'anything' ? '' : text
|
|
774
|
+
results = Inform::Object.where(Sequel.like(:description, "%#{query}%")).order(:name, :id)
|
|
775
|
+
return "Your search - for object.description set to #{text} - did not match any objects." if results.empty?
|
|
776
|
+
n = results.count
|
|
777
|
+
println "#{n} " + pluralize("instance", n) + " found."
|
|
778
|
+
results.each do |obj|
|
|
779
|
+
print "Found " + a(obj) + " [" + obj.identity + "]"
|
|
780
|
+
print " in " + a(parent(obj)) + " [" + parent(obj).identity + "]" if parent(obj)
|
|
781
|
+
println "."
|
|
782
|
+
end
|
|
783
|
+
false
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
# TODO: Remove (2026-05-03)
|
|
787
|
+
# def find_by_tag_name(tag_name)
|
|
788
|
+
# Inform::Object.select_all(:object)
|
|
789
|
+
# .join(:tagged, object_id: :id)
|
|
790
|
+
# .join(:tag, id: :tag_id)
|
|
791
|
+
# .where([[Sequel[:tag][:name], tag_name]].to_h).order(:name, :id)
|
|
792
|
+
# end
|
|
793
|
+
def find_by_tag_name(tag_name)
|
|
794
|
+
tag = tag_name.to_s
|
|
795
|
+
|
|
796
|
+
Inform::Object.all
|
|
797
|
+
.select { |obj| Array(obj.tags).map(&:to_s).include?(tag) }
|
|
798
|
+
.sort_by { |obj| [Array(obj.name).join(', '), (obj.id || 0).to_i] }
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
def FindByAttributeSub
|
|
802
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
803
|
+
results = find_by_tag_name(special.to_s)
|
|
804
|
+
return "Your search - for objects having #{special} - did not match any objects." if results.empty?
|
|
805
|
+
n = results.count
|
|
806
|
+
println "#{n} " + pluralize("instance", n) + " found."
|
|
807
|
+
results.each do |obj|
|
|
808
|
+
print "Found " + a(obj) + " [" + obj.identity + "]"
|
|
809
|
+
print " in " + a(parent(obj)) + " [" + parent(obj).identity + "]" if parent(obj)
|
|
810
|
+
println "."
|
|
811
|
+
end
|
|
812
|
+
false
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
# TODO: Remove (2026-05-03)
|
|
816
|
+
# def find_by_module_name(module_name)
|
|
817
|
+
# Inform::Object.select_all(:object)
|
|
818
|
+
# .join(:modularized, object_id: :id)
|
|
819
|
+
# .join(:module, id: :module_id)
|
|
820
|
+
# .where([[Sequel[:module][:name], module_name]].to_h).order(:name, :id)
|
|
821
|
+
# end
|
|
822
|
+
def find_by_module_name(module_name)
|
|
823
|
+
mod = module_name.to_s
|
|
824
|
+
|
|
825
|
+
Inform::Object.all
|
|
826
|
+
.select { |obj| Array(obj.modules).map(&:to_s).include?(mod) }
|
|
827
|
+
.sort_by { |obj| [Array(obj.name).join(', '), (obj.id || 0).to_i] }
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
def FindByModuleSub
|
|
831
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
832
|
+
results = find_by_module_name(special.to_s)
|
|
833
|
+
return "Your search - for objects with the #{special} module - did not match any objects." if results.empty?
|
|
834
|
+
n = results.count
|
|
835
|
+
println "#{n} " + pluralize("instance", n) + " found."
|
|
836
|
+
results.each do |obj|
|
|
837
|
+
print "Found " + a(obj) + " [" + obj.identity + "]"
|
|
838
|
+
print " in " + a(parent(obj)) + " [" + parent(obj).identity + "]" if parent(obj)
|
|
839
|
+
println "."
|
|
840
|
+
end
|
|
841
|
+
false
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
def find_by_link_name_or_linked(link_name, linked_obj)
|
|
845
|
+
return Inform::Link.where([[Sequel[:link][:name], link_name]].to_h).order(:name, :id).all if linked_obj.nil?
|
|
846
|
+
Inform::Link.where(
|
|
847
|
+
Sequel.expr([[Sequel[:link][:name], link_name]].to_h) & (
|
|
848
|
+
Sequel.expr([[Sequel[:link][:from_id], linked_obj.id]].to_h) |
|
|
849
|
+
Sequel.expr([[Sequel[:link][:to_id], linked_obj.id]].to_h)
|
|
850
|
+
)
|
|
851
|
+
).order(:name, :id)
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
def FindByLinkSub
|
|
855
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
856
|
+
results = find_by_link_name_or_linked(special.to_s, second)
|
|
857
|
+
return "Your search - for objects linked through #{special} - did not match any objects." if results.empty?
|
|
858
|
+
n = results.count
|
|
859
|
+
println "#{n} " + pluralize("instance", n) + " found."
|
|
860
|
+
results.each do |link|
|
|
861
|
+
obj = link.from
|
|
862
|
+
print "Found " + a(obj) + " [" + obj.identity + "]"
|
|
863
|
+
print " in " + a(parent(obj)) + " [" + parent(obj).identity + "]" if parent(obj)
|
|
864
|
+
print " linked to " + a(link.to) + " through " + link.name
|
|
865
|
+
println "."
|
|
866
|
+
end
|
|
867
|
+
false
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
def FlushSub
|
|
871
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
872
|
+
flush_automatically
|
|
873
|
+
|
|
874
|
+
if defined? FLUSH_IO_INSTANTLY
|
|
875
|
+
"IO will always flush."
|
|
876
|
+
else
|
|
877
|
+
"IO will buffer."
|
|
878
|
+
end
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
def ReadmeSub
|
|
882
|
+
file_path = 'README.md'
|
|
883
|
+
println markdown_metadata(file_path)
|
|
884
|
+
`cat #{file_path}`
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
def GenerateSub
|
|
888
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
889
|
+
"Not yet implemented."
|
|
890
|
+
end
|
|
891
|
+
|
|
892
|
+
def RegenerateSub
|
|
893
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
894
|
+
"Not yet implemented."
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
def GrammarsSub
|
|
898
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
899
|
+
Inform.load_grammars
|
|
900
|
+
"Reloaded grammar specifications."
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
def ReturnSub
|
|
904
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
905
|
+
destination = @player.linkto :previous_location
|
|
906
|
+
return "[Not a safe place.]" unless destination.is_a?(Inform::Object)
|
|
907
|
+
return "Cannot go to there." if destination == @player
|
|
908
|
+
PlayerTo(destination)
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
def HelpSub
|
|
912
|
+
return "That's not how life works." unless topic
|
|
913
|
+
verb = Inform::Grammar::Verbs.lookup topic.to_s, @player
|
|
914
|
+
if verb
|
|
915
|
+
"\e[51m#{verb}\e[56m"
|
|
916
|
+
else
|
|
917
|
+
"No help on that topic."
|
|
918
|
+
end
|
|
919
|
+
end
|
|
920
|
+
|
|
921
|
+
def htop
|
|
922
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
923
|
+
"Not yet implemented."
|
|
924
|
+
# htop_path = `which htop`.strip
|
|
925
|
+
# if htop_path.empty?
|
|
926
|
+
# if macos?
|
|
927
|
+
# return "Please install htop from: https://github.com/htop-dev/htop.git"
|
|
928
|
+
# else
|
|
929
|
+
# return "Please install htop : sudo apt install htop"
|
|
930
|
+
# end
|
|
931
|
+
# end
|
|
932
|
+
# println "#{htop_path}"
|
|
933
|
+
# output = IO.popen(htop_path, "r+") do |pipe|
|
|
934
|
+
# println pipe.readlines.join("\n")
|
|
935
|
+
# pipe.write "q"
|
|
936
|
+
# pipe.flush
|
|
937
|
+
# pipe.close_write
|
|
938
|
+
# begin
|
|
939
|
+
# pipe.read
|
|
940
|
+
# rescue StandardError => e
|
|
941
|
+
# log.warn e.message
|
|
942
|
+
# end
|
|
943
|
+
# end
|
|
944
|
+
# output = IO.popen(htop_path, "r+") do |pipe|
|
|
945
|
+
# out pipe.readlines.join("\n")
|
|
946
|
+
# pipe.write "q"
|
|
947
|
+
# pipe.flush
|
|
948
|
+
# pipe.close_write
|
|
949
|
+
# begin
|
|
950
|
+
# pipe.read
|
|
951
|
+
# rescue StandardError => e
|
|
952
|
+
# log.warn e.message
|
|
953
|
+
# end
|
|
954
|
+
# end
|
|
955
|
+
# true
|
|
956
|
+
end
|
|
957
|
+
|
|
958
|
+
def HistorySub
|
|
959
|
+
history = @player.inflib&.history
|
|
960
|
+
return "Your history is empty." if history.respond_to?(:empty?) && history.empty?
|
|
961
|
+
return history.join(Newline) if history.respond_to?(:join) && special_number1.nil?
|
|
962
|
+
@history_limit = special_number1
|
|
963
|
+
"You will now remember #{@history_limit} commands in your history."
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
def IdentifySub
|
|
967
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
968
|
+
noun.identity
|
|
969
|
+
end
|
|
970
|
+
|
|
971
|
+
def IdentitiesSub
|
|
972
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
973
|
+
return GlobalSequelIdentityMap.inspect if defined? GlobalSequelIdentityMap
|
|
974
|
+
"Identity mapping is not enabled."
|
|
975
|
+
end
|
|
976
|
+
|
|
977
|
+
InspectTemplate = %(%<short_name>s:
|
|
978
|
+
name: %<name>s
|
|
979
|
+
id: %<id>s
|
|
980
|
+
class: %<classification>s
|
|
981
|
+
description: %<description>s
|
|
982
|
+
parent: %<parent>s
|
|
983
|
+
children: %<children>s
|
|
984
|
+
created: %<created_at>s
|
|
985
|
+
modifed: %<modified_at>s
|
|
986
|
+
properties:
|
|
987
|
+
%<properties>s
|
|
988
|
+
|
|
989
|
+
attributes:
|
|
990
|
+
%<attributes>s
|
|
991
|
+
|
|
992
|
+
links:
|
|
993
|
+
%<links>s
|
|
994
|
+
|
|
995
|
+
modules:
|
|
996
|
+
%<modules>s).freeze
|
|
997
|
+
|
|
998
|
+
ObjectReprTemplate = '%<obj>s [%<identity>s]'.freeze
|
|
999
|
+
ObjectInspectFields = %i[
|
|
1000
|
+
id classification description short_name name parent children created_at modified_at
|
|
1001
|
+
properties attributes links modules
|
|
1002
|
+
].freeze
|
|
1003
|
+
DefaultObjectValues = ObjectInspectFields.each_with_object({}) { |key, memo| memo[key] = 'none' }
|
|
1004
|
+
InspectDelimiter = %(\n ).freeze
|
|
1005
|
+
PropertyReprTemplate = '%<key>s: %<value>s'.freeze
|
|
1006
|
+
|
|
1007
|
+
def InspectSub
|
|
1008
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1009
|
+
raise Parser::CantSeeError if noun.nil?
|
|
1010
|
+
noun.refresh if noun.respond_to? :refresh
|
|
1011
|
+
|
|
1012
|
+
object_field_values = {}
|
|
1013
|
+
object_field_values[:id] = noun.id
|
|
1014
|
+
object_field_values[:classification] = noun.classification
|
|
1015
|
+
object_field_values[:description] = noun.values[:description] unless noun.values[:description].empty?
|
|
1016
|
+
object_field_values[:short_name] = noun.short_name if !noun.short_name.nil? && !noun.short_name.empty?
|
|
1017
|
+
object_field_values[:name] = noun.name_words
|
|
1018
|
+
object_field_values[:created_at] = noun.created_at
|
|
1019
|
+
object_field_values[:modified_at] = noun.modified_at
|
|
1020
|
+
unless (pobj = parent(noun)).nil?
|
|
1021
|
+
object_field_values[:parent] = format(ObjectReprTemplate, obj: pobj, identity: pobj.identity)
|
|
1022
|
+
end
|
|
1023
|
+
unless noun.children.empty?
|
|
1024
|
+
object_field_values[:children] = noun.children.sort.map do |obj|
|
|
1025
|
+
format(ObjectReprTemplate, obj: obj, identity: obj.identity)
|
|
1026
|
+
end
|
|
1027
|
+
end
|
|
1028
|
+
unless noun.properties.empty?
|
|
1029
|
+
properties = noun.properties.sort.map do |key, value|
|
|
1030
|
+
format(PropertyReprTemplate, key: key, value: value)
|
|
1031
|
+
end
|
|
1032
|
+
object_field_values[:properties] = properties.join(InspectDelimiter)
|
|
1033
|
+
end
|
|
1034
|
+
unless noun.attributes.empty?
|
|
1035
|
+
object_field_values[:attributes] = noun.attributes.sort.join(InspectDelimiter)
|
|
1036
|
+
end
|
|
1037
|
+
object_field_values[:links] = noun.links.sort.join(InspectDelimiter) unless noun.links.empty?
|
|
1038
|
+
object_field_values[:modules] = noun.modules.sort.map(&:name).join(InspectDelimiter) unless noun.modules.empty?
|
|
1039
|
+
|
|
1040
|
+
format(InspectTemplate, **DefaultObjectValues, **object_field_values)
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
def InspectLocationSub
|
|
1044
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1045
|
+
invoke :Inspect, location
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
def InspectParentSub
|
|
1049
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1050
|
+
invoke :Inspect, parent(@player)
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
def InspectModuleSub
|
|
1054
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1055
|
+
mod = find_module(special.capitalize)
|
|
1056
|
+
return "No such module." if mod.nil?
|
|
1057
|
+
mod.ancestors.reverse.collect { |m| m.instance_methods(false) }.flatten
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
def RequestAssistanceSub
|
|
1061
|
+
a = admins
|
|
1062
|
+
failure_message = "[There are currently no admins available. Please try again later.]"
|
|
1063
|
+
unless Inform::Game.config.include?(:admin_email)
|
|
1064
|
+
log.error "Please define AdminEmail in the Configuration module."
|
|
1065
|
+
return failure_message
|
|
1066
|
+
end
|
|
1067
|
+
if a.empty?
|
|
1068
|
+
return "#{failure_message}\n" +
|
|
1069
|
+
"[You may also try sending an e-mail to: #{Inform::Game.config[:admin_email]}]"
|
|
1070
|
+
end
|
|
1071
|
+
a.inform "Assistance request from #{@player.name} (#{@player.id}) " +
|
|
1072
|
+
"in " + the(location) + "(#{location.id}) at #{Time.now}"
|
|
1073
|
+
"[The admins have been informed of your request for assistance.]"
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
def InterruptSub
|
|
1077
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1078
|
+
if !noun.nil?
|
|
1079
|
+
noun.interrupt
|
|
1080
|
+
"You interrupt " + the(noun) + "."
|
|
1081
|
+
else
|
|
1082
|
+
@player.interrupt
|
|
1083
|
+
"You interrupt yourself."
|
|
1084
|
+
end
|
|
1085
|
+
end
|
|
1086
|
+
|
|
1087
|
+
def LibrariesSub
|
|
1088
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1089
|
+
InformLibrary.libraries.each do |k, v|
|
|
1090
|
+
println "#{k} => #{v.identity} #{v}"
|
|
1091
|
+
end
|
|
1092
|
+
true
|
|
1093
|
+
end
|
|
1094
|
+
|
|
1095
|
+
ProtectedLinks = %i[author].freeze
|
|
1096
|
+
|
|
1097
|
+
def LinkSub
|
|
1098
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1099
|
+
if special == noun
|
|
1100
|
+
to = second
|
|
1101
|
+
from = parent(@player)
|
|
1102
|
+
link = special.nil? || second.nil? ? noun.&(:door_dir) : special
|
|
1103
|
+
elsif !special.nil? && !noun.nil? && !second.nil?
|
|
1104
|
+
link = special
|
|
1105
|
+
from = noun
|
|
1106
|
+
to = second
|
|
1107
|
+
elsif !special.nil? && !noun.nil?
|
|
1108
|
+
link = special
|
|
1109
|
+
from = parent(@player)
|
|
1110
|
+
to = noun
|
|
1111
|
+
else
|
|
1112
|
+
link = noun.&:door_dir
|
|
1113
|
+
from = parent(@player)
|
|
1114
|
+
to = second
|
|
1115
|
+
end
|
|
1116
|
+
|
|
1117
|
+
if link.nil? || from.nil? || to.nil?
|
|
1118
|
+
"Sorry, I'm having trouble figuring out what you want to " \
|
|
1119
|
+
"link. (This is probably a bug.)"
|
|
1120
|
+
else
|
|
1121
|
+
from.link(link, to)
|
|
1122
|
+
"You link " + the(from) + " to " + the(to) + " through #{link}."
|
|
1123
|
+
end
|
|
1124
|
+
end
|
|
1125
|
+
|
|
1126
|
+
def ListTogetherSub
|
|
1127
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1128
|
+
return "Only two objects may be listed together." if noun.nil? || second.nil?
|
|
1129
|
+
noun.link :list_together, second
|
|
1130
|
+
second.link :list_together, noun
|
|
1131
|
+
The(from) + " and " + the(to) + " will now be listed together."
|
|
1132
|
+
end
|
|
1133
|
+
|
|
1134
|
+
def UnlinkSub
|
|
1135
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1136
|
+
if !noun.nil? && noun != special
|
|
1137
|
+
if !special.nil?
|
|
1138
|
+
link = special
|
|
1139
|
+
from = noun
|
|
1140
|
+
else
|
|
1141
|
+
link = noun.&:door_dir
|
|
1142
|
+
from = parent(@player)
|
|
1143
|
+
end
|
|
1144
|
+
else
|
|
1145
|
+
link = special
|
|
1146
|
+
from = parent(@player) || @player
|
|
1147
|
+
end
|
|
1148
|
+
if link.nil? || from.nil?
|
|
1149
|
+
return "Sorry, I'm having trouble figuring out what you want to unlink. " \
|
|
1150
|
+
"(This is probably a bug.)"
|
|
1151
|
+
end
|
|
1152
|
+
return "Sorry, that's a protected link." if ProtectedLinks.include?(link.to_sym) && !privileged?(:admin)
|
|
1153
|
+
|
|
1154
|
+
to = from.unlink link
|
|
1155
|
+
"The #{link} link between " + the(from) + " and " + the(to) + " has been severed."
|
|
1156
|
+
end
|
|
1157
|
+
|
|
1158
|
+
def LocateSub
|
|
1159
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1160
|
+
parent_obj = parent(noun)
|
|
1161
|
+
A(noun) + " is in " + a(parent_obj) + " [" + parent_obj.identity + "]."
|
|
1162
|
+
end
|
|
1163
|
+
|
|
1164
|
+
def LocationSub
|
|
1165
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1166
|
+
obj = location
|
|
1167
|
+
unless special.nil?
|
|
1168
|
+
converter = format(ConverterTemplate, data_format: special).to_sym
|
|
1169
|
+
return obj.send(converter, { include: %i[tagged modularized] }) if obj.respond_to? converter
|
|
1170
|
+
end
|
|
1171
|
+
"Your location is " + the(obj) + "."
|
|
1172
|
+
end
|
|
1173
|
+
|
|
1174
|
+
def LoginSub
|
|
1175
|
+
AfterRoutines()
|
|
1176
|
+
true
|
|
1177
|
+
end
|
|
1178
|
+
|
|
1179
|
+
def MaterializeSub
|
|
1180
|
+
AfterRoutines()
|
|
1181
|
+
true
|
|
1182
|
+
end
|
|
1183
|
+
|
|
1184
|
+
def LogsSub
|
|
1185
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1186
|
+
`tail -n#{special_number1 || 20} #{ServerLogFile}`
|
|
1187
|
+
end
|
|
1188
|
+
|
|
1189
|
+
def all_other_players
|
|
1190
|
+
StoryTeller::IO::Session.players - [@player]
|
|
1191
|
+
end
|
|
1192
|
+
|
|
1193
|
+
def MassBootSub
|
|
1194
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1195
|
+
players = all_other_players
|
|
1196
|
+
return "No other connections to drop." if players.empty?
|
|
1197
|
+
players.each { |player| StoryTeller::IO::Session.of(player).disconnect }
|
|
1198
|
+
"Dropped other connections."
|
|
1199
|
+
end
|
|
1200
|
+
|
|
1201
|
+
def CacheSub
|
|
1202
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1203
|
+
return "Caching is not enabled." unless defined? GlobalCache
|
|
1204
|
+
GlobalCache.values.map(&:id)
|
|
1205
|
+
end
|
|
1206
|
+
|
|
1207
|
+
def ModulesSub
|
|
1208
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1209
|
+
l = noun.modules.map(&:name)
|
|
1210
|
+
return The(noun) + " has no special modules." if l.empty?
|
|
1211
|
+
The(noun) + " is imbued with the #{l} module" + (l.length > 1 ? 's' : '') + "."
|
|
1212
|
+
end
|
|
1213
|
+
|
|
1214
|
+
def ApplyBehaviorSub
|
|
1215
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1216
|
+
if !special.nil? && !noun.nil?
|
|
1217
|
+
mod = find_module(special)
|
|
1218
|
+
mod ||= find_module(special.capitalize)
|
|
1219
|
+
return "No such module." if mod.nil? || !mod.is_a?(Module)
|
|
1220
|
+
return The(noun) + " is already imbued with the #{mod} module." if noun.is_a?(mod)
|
|
1221
|
+
noun.mod mod
|
|
1222
|
+
if defined?(DEBUG)
|
|
1223
|
+
mod.instance_methods.each do |method|
|
|
1224
|
+
println "#{noun} now responds to #{method}" if noun.respond_to?(method)
|
|
1225
|
+
end
|
|
1226
|
+
println "Is " + the(noun) + " now a #{mod}? #{noun.is_a? mod}."
|
|
1227
|
+
end
|
|
1228
|
+
"You have added the #{mod} module to " + the(noun) + "."
|
|
1229
|
+
elsif !noun.nil?
|
|
1230
|
+
ModulesSub()
|
|
1231
|
+
else
|
|
1232
|
+
"Module application failed."
|
|
1233
|
+
end
|
|
1234
|
+
end
|
|
1235
|
+
|
|
1236
|
+
def RemoveBehaviorSub
|
|
1237
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1238
|
+
if !special.nil? && !noun.nil?
|
|
1239
|
+
mod = find_module special
|
|
1240
|
+
mod ||= find_module special.capitalize
|
|
1241
|
+
println "No such module." unless !mod.nil? && mod.is_a?(Module)
|
|
1242
|
+
if !mod.nil? && mod.is_a?(Module)
|
|
1243
|
+
return The(noun) + " is not imbued with the #{mod} module." unless noun.is_a? mod
|
|
1244
|
+
else
|
|
1245
|
+
mod = 'unknown'
|
|
1246
|
+
end
|
|
1247
|
+
noun.unmod special
|
|
1248
|
+
if defined? DEBUG
|
|
1249
|
+
mod.instance_methods.each do |method|
|
|
1250
|
+
println "#{noun} now responds to #{method}" if noun.respond_to? method
|
|
1251
|
+
end
|
|
1252
|
+
println "Is " + the(noun) + " now a #{mod}? #{noun.is_a? mod}."
|
|
1253
|
+
end
|
|
1254
|
+
"You have removed the #{mod} module from " + the(noun) + "."
|
|
1255
|
+
elsif noun
|
|
1256
|
+
ModulesSub()
|
|
1257
|
+
else
|
|
1258
|
+
"Module removal failed."
|
|
1259
|
+
end
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
def motd
|
|
1263
|
+
motd_file = File.new(MudLib::MOTD_FILE_PATH)
|
|
1264
|
+
require motd_file
|
|
1265
|
+
|
|
1266
|
+
%(#{MOTD}
|
|
1267
|
+
The current time is now #{Time.new}.)
|
|
1268
|
+
end
|
|
1269
|
+
|
|
1270
|
+
def NameSub
|
|
1271
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1272
|
+
return @player.name if noun.nil?
|
|
1273
|
+
return noun.name if text.nil?
|
|
1274
|
+
|
|
1275
|
+
the_old_name = the(noun)
|
|
1276
|
+
log.warn "#{self}.NameSub text: #{text}"
|
|
1277
|
+
noun.short_name = text
|
|
1278
|
+
noun.save
|
|
1279
|
+
PlayerTo(noun, 1) if noun == location
|
|
1280
|
+
"You rename " + the_old_name + " to " + a(noun) + "."
|
|
1281
|
+
end
|
|
1282
|
+
|
|
1283
|
+
def NameRoomSub
|
|
1284
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1285
|
+
log.warn "#{self}.NameRoomSub text: #{text}"
|
|
1286
|
+
_invoke :Name, location, text[0].capitalize + text[1..]
|
|
1287
|
+
end
|
|
1288
|
+
|
|
1289
|
+
def Places1Sub
|
|
1290
|
+
unless special.nil?
|
|
1291
|
+
dataset = Inform::Room.naked.all
|
|
1292
|
+
converter = format(ConverterTemplate, data_format: special).to_sym
|
|
1293
|
+
return dataset.send(converter, { include: %i[tagged modularized] }) if dataset.respond_to? converter
|
|
1294
|
+
end
|
|
1295
|
+
Inform::Room.order(:name).all.identities
|
|
1296
|
+
end
|
|
1297
|
+
|
|
1298
|
+
def Objects1Sub
|
|
1299
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1300
|
+
# TODO: Support pagination
|
|
1301
|
+
if ObjectSpace.enabled?
|
|
1302
|
+
s = ''
|
|
1303
|
+
ObjectSpace.each_object(Inform::Object) { |x| s << x + ", " }
|
|
1304
|
+
return s[0..-3]
|
|
1305
|
+
end
|
|
1306
|
+
unless special.nil?
|
|
1307
|
+
dataset = Inform::Object.naked.all
|
|
1308
|
+
converter = format(ConverterTemplate, data_format: special).to_sym
|
|
1309
|
+
return dataset.send(converter, { include: %i[tagged modularized] }) if dataset.respond_to? converter
|
|
1310
|
+
end
|
|
1311
|
+
Inform::Object.order(:name).all.identities
|
|
1312
|
+
end
|
|
1313
|
+
|
|
1314
|
+
def OnSub
|
|
1315
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1316
|
+
"You are trying to make " + the(noun) + " " + text + " when the player does a " + special + " to it."
|
|
1317
|
+
end
|
|
1318
|
+
|
|
1319
|
+
def OptionsSub
|
|
1320
|
+
log.debug "OptionsSub special: #{special}"
|
|
1321
|
+
opts = session&.settings
|
|
1322
|
+
return "You can't set preferences." if opts.nil?
|
|
1323
|
+
if !special.nil? && !text.nil?
|
|
1324
|
+
text = nil if text == 'nothing'
|
|
1325
|
+
opts[special] = text
|
|
1326
|
+
end
|
|
1327
|
+
opts.to_yaml
|
|
1328
|
+
end
|
|
1329
|
+
|
|
1330
|
+
def os
|
|
1331
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1332
|
+
"The operating system of this server is " + java.lang.System.getProperty('os.name') + "."
|
|
1333
|
+
end
|
|
1334
|
+
|
|
1335
|
+
def PredictableSub
|
|
1336
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1337
|
+
setrandom 100
|
|
1338
|
+
"[Random number generator now predictable.]"
|
|
1339
|
+
end
|
|
1340
|
+
|
|
1341
|
+
def SpecificallyPredictableSub
|
|
1342
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1343
|
+
setrandom special_number1.to_i
|
|
1344
|
+
"[Random number generator now predictable.]"
|
|
1345
|
+
end
|
|
1346
|
+
|
|
1347
|
+
def PrepositionsSub
|
|
1348
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1349
|
+
Inform::English::Prepositions
|
|
1350
|
+
end
|
|
1351
|
+
|
|
1352
|
+
def ProceedSub
|
|
1353
|
+
return "Not implemented." unless @player.respond_to? :proceed
|
|
1354
|
+
@player.proceed
|
|
1355
|
+
end
|
|
1356
|
+
|
|
1357
|
+
def ProcsSub
|
|
1358
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1359
|
+
available_processors
|
|
1360
|
+
end
|
|
1361
|
+
|
|
1362
|
+
def CpuSub
|
|
1363
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1364
|
+
`ps -p #{pid} -o %cpu | tail -n1 | tr -s ' ' | cut -d ' ' -f 2`
|
|
1365
|
+
end
|
|
1366
|
+
|
|
1367
|
+
def ProfileSub
|
|
1368
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1369
|
+
bytes = memory_usage + ' B'
|
|
1370
|
+
if !special.nil? && defined?(Unit)
|
|
1371
|
+
unit_name = special.length <= 2 ? special.upcase : special
|
|
1372
|
+
bytes = Unit.new(bytes).to(unit_name).to_s
|
|
1373
|
+
end
|
|
1374
|
+
Story + " is using #{bytes.to_human} of memory."
|
|
1375
|
+
rescue ArgumentError
|
|
1376
|
+
"The unit given is invalid."
|
|
1377
|
+
end
|
|
1378
|
+
|
|
1379
|
+
def ReferenceSub
|
|
1380
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1381
|
+
the_old_name = the(noun)
|
|
1382
|
+
noun.values[:name] = text
|
|
1383
|
+
noun.save
|
|
1384
|
+
print "You may now refer to " + the_old_name + " as "
|
|
1385
|
+
synonyms = text.split(/[,\s]+/)
|
|
1386
|
+
if synonyms.length > 1
|
|
1387
|
+
synonyms[0..-2].each do |s|
|
|
1388
|
+
print "'" + s + "', "
|
|
1389
|
+
end
|
|
1390
|
+
print "or "
|
|
1391
|
+
end
|
|
1392
|
+
"'#{synonyms.last}'."
|
|
1393
|
+
end
|
|
1394
|
+
|
|
1395
|
+
def RefreshSub
|
|
1396
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1397
|
+
noun.refresh
|
|
1398
|
+
noun.tags
|
|
1399
|
+
"Refreshed " + the(noun) + "."
|
|
1400
|
+
end
|
|
1401
|
+
|
|
1402
|
+
def reload_feature_safely(feature)
|
|
1403
|
+
reload_feature(feature)
|
|
1404
|
+
println "Reloaded #{feature}"
|
|
1405
|
+
rescue StandardError => e
|
|
1406
|
+
println "Failed to reload #{feature}: #{e.message}"
|
|
1407
|
+
end
|
|
1408
|
+
|
|
1409
|
+
def reload
|
|
1410
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1411
|
+
reload_feature_safely(EntryPoint)
|
|
1412
|
+
|
|
1413
|
+
features = ['inform.rb', 'english.rb', 'metaverbs.rb', 'emotes.rb', 'verbs.rb']
|
|
1414
|
+
features.each do |feature|
|
|
1415
|
+
reload_feature_safely(feature)
|
|
1416
|
+
end
|
|
1417
|
+
|
|
1418
|
+
println "Reloading grammars..."
|
|
1419
|
+
Inform.load_grammar 'grammar'
|
|
1420
|
+
Inform.load_grammar 'emotes'
|
|
1421
|
+
|
|
1422
|
+
Inform.reload_game
|
|
1423
|
+
|
|
1424
|
+
"Done reloading."
|
|
1425
|
+
end
|
|
1426
|
+
|
|
1427
|
+
def GarbageCollectionSub
|
|
1428
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1429
|
+
add_event(delay: 1) do
|
|
1430
|
+
collect_garbage
|
|
1431
|
+
println "Garbage collection complete."
|
|
1432
|
+
end
|
|
1433
|
+
"Having the Java Virtual Machine collect its garbage..."
|
|
1434
|
+
end
|
|
1435
|
+
|
|
1436
|
+
def LibraryMethodsSub
|
|
1437
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1438
|
+
StoryTeller::Library.methods_index
|
|
1439
|
+
end
|
|
1440
|
+
|
|
1441
|
+
def MethodSub
|
|
1442
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1443
|
+
m = special.to_sym
|
|
1444
|
+
print "The method ##{m}(...) is "
|
|
1445
|
+
if noun
|
|
1446
|
+
print "not " unless noun.respond_to?(m)
|
|
1447
|
+
"available on " + the(noun) + "."
|
|
1448
|
+
else
|
|
1449
|
+
print "not " unless StoryTeller::Library.methods_index.include?(m)
|
|
1450
|
+
"available."
|
|
1451
|
+
end
|
|
1452
|
+
end
|
|
1453
|
+
|
|
1454
|
+
def ResetSub
|
|
1455
|
+
noun.properties.clear
|
|
1456
|
+
noun.save
|
|
1457
|
+
"You have reset the properties for " + the(noun) + "."
|
|
1458
|
+
end
|
|
1459
|
+
|
|
1460
|
+
def RestartSub
|
|
1461
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1462
|
+
if time
|
|
1463
|
+
println "Time: #{time}"
|
|
1464
|
+
elsif special_number1
|
|
1465
|
+
println "Number: #{special_number1}"
|
|
1466
|
+
end
|
|
1467
|
+
add_event(delay: 2.0) do
|
|
1468
|
+
StoryTeller::IO::Session.players.inform "[ Broadcast: Scheduled server restart. ]"
|
|
1469
|
+
StoryTeller::IO::Session.players.each do |player|
|
|
1470
|
+
StoryTeller::IO::Session.of(player).disconnect "Server is restarting."
|
|
1471
|
+
end
|
|
1472
|
+
sleep 1
|
|
1473
|
+
builder = java.lang.ProcessBuilder.new('./zmud')
|
|
1474
|
+
builder.start()
|
|
1475
|
+
java.lang.System.exit(0)
|
|
1476
|
+
end
|
|
1477
|
+
"Scheduled server restart."
|
|
1478
|
+
end
|
|
1479
|
+
|
|
1480
|
+
def RestoreSub(save_dir_path = StoryTeller::Runtime.game_saves_dir_path)
|
|
1481
|
+
FileUtils.mkdir_p(save_dir_path)
|
|
1482
|
+
default_restore_file = `ls -t #{save_dir_path}`.strip.split.first
|
|
1483
|
+
|
|
1484
|
+
return solicit("Enter a file path.\nDefault is \"#{default_restore_file}\": ") unless solicited?
|
|
1485
|
+
|
|
1486
|
+
save_file_name = text.empty? ? default_restore_file : text
|
|
1487
|
+
save_file_path = File.join(save_dir_path, save_file_name)
|
|
1488
|
+
return println L__M(:Restore, 1) unless File.exist?(save_file_path)
|
|
1489
|
+
restoration_successful = restore(save_file_path)
|
|
1490
|
+
return println L__M(:Restore, 1) unless restoration_successful
|
|
1491
|
+
println "Restored."
|
|
1492
|
+
prompt
|
|
1493
|
+
session.state = :playing
|
|
1494
|
+
end
|
|
1495
|
+
|
|
1496
|
+
def restore(file_path)
|
|
1497
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1498
|
+
restore_cmd = []
|
|
1499
|
+
restore_cmd << File.join('scripts', 'restore.rb')
|
|
1500
|
+
restore_cmd << file_path
|
|
1501
|
+
# restore_cmd << "--wet-run"
|
|
1502
|
+
restore_cmd = restore_cmd.join(' ')
|
|
1503
|
+
result = `#{restore_cmd}`.strip
|
|
1504
|
+
log.debug "Restored: #{result}"
|
|
1505
|
+
result == :specific
|
|
1506
|
+
end
|
|
1507
|
+
|
|
1508
|
+
# TODO: Implement unit test for this TESTME
|
|
1509
|
+
def RegionsScope
|
|
1510
|
+
case @scope_stage
|
|
1511
|
+
when 1 then false # Return single objects only
|
|
1512
|
+
when 2
|
|
1513
|
+
regions = Inform::Object.where(object_type: 'Region').all
|
|
1514
|
+
ScopeWithin(regions); true
|
|
1515
|
+
when 3 then L__M(:Take, 8) # TODO: This is broken somehow FIXME
|
|
1516
|
+
end
|
|
1517
|
+
end
|
|
1518
|
+
|
|
1519
|
+
def RegionSub
|
|
1520
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1521
|
+
return "For some reason your location cannot be determined." if location.nil?
|
|
1522
|
+
location.link :region, noun if !noun.nil? && noun.is_a?(Region)
|
|
1523
|
+
region = location.linkto :region
|
|
1524
|
+
loop do
|
|
1525
|
+
break if parent(region).nil?
|
|
1526
|
+
println "You are in a sub-region called " + A(region) + " [#{region.identity}]."
|
|
1527
|
+
region = parent(region)
|
|
1528
|
+
end
|
|
1529
|
+
return "This room does not belong to any region." if region.nil?
|
|
1530
|
+
"The main region here is called " + A(region) + " [#{region.identity}]."
|
|
1531
|
+
end
|
|
1532
|
+
|
|
1533
|
+
def RegionsSub
|
|
1534
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1535
|
+
regions = Inform::Object.where(object_type: 'Region').order(:name).all
|
|
1536
|
+
return "There are no regions." if regions.empty?
|
|
1537
|
+
println "The following regions are available:"
|
|
1538
|
+
regions.identities
|
|
1539
|
+
end
|
|
1540
|
+
|
|
1541
|
+
def RoomsSub
|
|
1542
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1543
|
+
unless special.nil?
|
|
1544
|
+
dataset = Room.naked.all
|
|
1545
|
+
converter = format(ConverterTemplate, data_format: special).to_sym
|
|
1546
|
+
return dataset.send(converter, { include: %i[tagged modularized] }) if dataset.respond_to? converter
|
|
1547
|
+
end
|
|
1548
|
+
Room.all.identities
|
|
1549
|
+
end
|
|
1550
|
+
|
|
1551
|
+
def RootSub
|
|
1552
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1553
|
+
obj = noun || @player
|
|
1554
|
+
root = obj.root
|
|
1555
|
+
"The root of " + the(obj) + " is " + a(root) + " [" + root.identity + "]."
|
|
1556
|
+
end
|
|
1557
|
+
|
|
1558
|
+
def SaveSub
|
|
1559
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1560
|
+
return "It would take a very long time to save everything at once." if noun.nil?
|
|
1561
|
+
noun.save
|
|
1562
|
+
The(noun) + " has been saved."
|
|
1563
|
+
end
|
|
1564
|
+
|
|
1565
|
+
def send_email(to, from, subject, message = '', attachment = nil)
|
|
1566
|
+
email_cmd = []
|
|
1567
|
+
email_cmd << 'scripts/email.rb'
|
|
1568
|
+
email_cmd << %(--to="#{to}")
|
|
1569
|
+
email_cmd << %(--from="#{from}")
|
|
1570
|
+
email_cmd << %(--subject="#{subject}")
|
|
1571
|
+
email_cmd << %(--message="#{message}")
|
|
1572
|
+
email_cmd << %(--attachment="#{attachment}")
|
|
1573
|
+
email_cmd = email_cmd.join(' ')
|
|
1574
|
+
`#{email_cmd}`.strip
|
|
1575
|
+
end
|
|
1576
|
+
|
|
1577
|
+
def SessionsSub
|
|
1578
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1579
|
+
StoryTeller::IO::Session.players.each do |player|
|
|
1580
|
+
session = StoryTeller::IO::Session.of(player)
|
|
1581
|
+
println "#{player} #=> #{session} [#{session.status}:#{session.state}]"
|
|
1582
|
+
end
|
|
1583
|
+
true
|
|
1584
|
+
end
|
|
1585
|
+
|
|
1586
|
+
def SiblingsSub
|
|
1587
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1588
|
+
o = noun.object? ? noun : @player
|
|
1589
|
+
s = o.siblings
|
|
1590
|
+
unless special.nil?
|
|
1591
|
+
converter = format(ConverterTemplate, data_format: special).to_sym
|
|
1592
|
+
return s.send(converter, { include: %i[tagged modularized] }) if s.respond_to? converter
|
|
1593
|
+
end
|
|
1594
|
+
s.sort.identities
|
|
1595
|
+
end
|
|
1596
|
+
|
|
1597
|
+
# override :InformShowVerbSub, :ShowVerbSub
|
|
1598
|
+
def ShowVerbSub
|
|
1599
|
+
log.warn "#{self}.Metaverbs#ShowVerbSub"
|
|
1600
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1601
|
+
InformShowVerbSub()
|
|
1602
|
+
end
|
|
1603
|
+
|
|
1604
|
+
# override :InformShowobjSub, :ShowobjSub
|
|
1605
|
+
def ShowobjSub
|
|
1606
|
+
log.warn "#{self}.Metaverbs#ShowobjSub"
|
|
1607
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1608
|
+
InformShowobjSub()
|
|
1609
|
+
end
|
|
1610
|
+
|
|
1611
|
+
def StateSub
|
|
1612
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1613
|
+
if !noun.nil? && noun != special
|
|
1614
|
+
return The(noun) + " has no session." unless noun.respond_to?(:session) && !noun.session.nil?
|
|
1615
|
+
noun.session_state = noun.session.state = special.to_sym if special
|
|
1616
|
+
The(noun) + "'s session's state is #{noun.session.state}."
|
|
1617
|
+
else
|
|
1618
|
+
return "Your character has no session." unless @player.respond_to?(:session) && !@player.session.nil?
|
|
1619
|
+
@player.session_state = @player.session.state = special.to_sym unless special.nil?
|
|
1620
|
+
"Your session's state is #{@player.session.state}."
|
|
1621
|
+
end
|
|
1622
|
+
end
|
|
1623
|
+
|
|
1624
|
+
def EphemeralObjectsSub
|
|
1625
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1626
|
+
Inform::EphemeralObjects.values.map(&:name)
|
|
1627
|
+
end
|
|
1628
|
+
|
|
1629
|
+
def SystemSpecsSub
|
|
1630
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1631
|
+
class_loader_methods = (class_loader.methods - Object.methods).grep(/get_.*/).sort
|
|
1632
|
+
class_loader_parent = class_loader.get_parent()
|
|
1633
|
+
class_loader_grandparent = class_loader_parent.get_parent()
|
|
1634
|
+
class_loader_great_grandparent = class_loader_grandparent.get_parent()
|
|
1635
|
+
println "JRuby:"
|
|
1636
|
+
println " Class loader:"
|
|
1637
|
+
println " #{class_loader} (#{class_loader.inspect})"
|
|
1638
|
+
println " Classloader methods:"
|
|
1639
|
+
println class_loader_methods.inspect
|
|
1640
|
+
println " Classpath:"
|
|
1641
|
+
println " #{$CLASSPATH}"
|
|
1642
|
+
println " Packages:"
|
|
1643
|
+
class_loader.packages.each { |x| println " #{x}" }
|
|
1644
|
+
println " URLs:"
|
|
1645
|
+
class_loader.getURLs().each { |x| println " #{x}" }
|
|
1646
|
+
println " Parent:"
|
|
1647
|
+
println " #{class_loader_parent} (#{class_loader_parent.inspect})"
|
|
1648
|
+
println " Grandparent:"
|
|
1649
|
+
println " #{class_loader_grandparent} (#{class_loader_grandparent.inspect})"
|
|
1650
|
+
println " Great-grandparent:"
|
|
1651
|
+
println " #{class_loader_great_grandparent} (#{class_loader_great_grandparent.inspect})"
|
|
1652
|
+
|
|
1653
|
+
println
|
|
1654
|
+
println " Runtime:"
|
|
1655
|
+
println " #{JRuby.runtime} (#{JRuby.runtime.inspect})"
|
|
1656
|
+
println " Runtime methods:"
|
|
1657
|
+
println " #{(JRuby.runtime.methods - Object.methods).grep(/get_.*/).sort.inspect}"
|
|
1658
|
+
println
|
|
1659
|
+
println " Object space:"
|
|
1660
|
+
println " #{object_space} (#{object_space.inspect})"
|
|
1661
|
+
println " #{(object_space.methods - Object.methods).grep(/get_.*/).sort.inspect}"
|
|
1662
|
+
println
|
|
1663
|
+
println " Load service:"
|
|
1664
|
+
println " #{load_service} (#{load_service.inspect})"
|
|
1665
|
+
println " Load service features:"
|
|
1666
|
+
load_service.loaded_features.each { |x| println " #{x}" }
|
|
1667
|
+
println " Load service methods:"
|
|
1668
|
+
println " #{(load_service.methods - Object.methods).grep(/get_.*/).sort.inspect}"
|
|
1669
|
+
true
|
|
1670
|
+
rescue NotImplementedError
|
|
1671
|
+
"Not implemented."
|
|
1672
|
+
end
|
|
1673
|
+
|
|
1674
|
+
def ShutdownSub
|
|
1675
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1676
|
+
if !time.nil?
|
|
1677
|
+
println "Time: #{time}"
|
|
1678
|
+
elsif !special_number1.nil?
|
|
1679
|
+
println "Number: #{special_number1}"
|
|
1680
|
+
end
|
|
1681
|
+
add_event(delay: 2.0) do
|
|
1682
|
+
StoryTeller::IO::Session.players.inform "[ Broadcast: Shutting down. ]"
|
|
1683
|
+
StoryTeller::IO::Session.players.each do |player|
|
|
1684
|
+
StoryTeller::IO::Session.of(player).disconnect "Server is shutting down."
|
|
1685
|
+
end
|
|
1686
|
+
sleep 1
|
|
1687
|
+
java.lang.System.exit(0)
|
|
1688
|
+
end
|
|
1689
|
+
"Scheduled server shutdown."
|
|
1690
|
+
end
|
|
1691
|
+
alias shutdown ShutdownSub
|
|
1692
|
+
alias shut_down shutdown
|
|
1693
|
+
|
|
1694
|
+
def StackTraceSub
|
|
1695
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1696
|
+
caller.join("\n")
|
|
1697
|
+
end
|
|
1698
|
+
|
|
1699
|
+
def SubscribersSub
|
|
1700
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1701
|
+
if noun.nil?
|
|
1702
|
+
Inform::Subscribers::ALL.each do |key, value|
|
|
1703
|
+
println "#{key} #=> #{value}"
|
|
1704
|
+
end
|
|
1705
|
+
else
|
|
1706
|
+
noun.subscribers.each do |subscriber|
|
|
1707
|
+
println subscriber.to_s
|
|
1708
|
+
end
|
|
1709
|
+
end
|
|
1710
|
+
true
|
|
1711
|
+
end
|
|
1712
|
+
|
|
1713
|
+
def TagsSub
|
|
1714
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1715
|
+
if noun.attributes.empty?
|
|
1716
|
+
The(noun) + " has no attributes."
|
|
1717
|
+
else
|
|
1718
|
+
The(noun) + " has " + noun.attributes.join(' ') + "."
|
|
1719
|
+
end
|
|
1720
|
+
end
|
|
1721
|
+
|
|
1722
|
+
ProtectedTags = %w[admin builder].freeze
|
|
1723
|
+
|
|
1724
|
+
def TagSub
|
|
1725
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1726
|
+
attributes = text ? text.split : []
|
|
1727
|
+
attributes -= language_descriptors.keys
|
|
1728
|
+
attributes -= ProtectedTags unless privileged?(:admin)
|
|
1729
|
+
unless attributes.empty?
|
|
1730
|
+
if attributes.any? { |a| /^!/.match?(a) }
|
|
1731
|
+
attributes.each do |attribute|
|
|
1732
|
+
if attribute.gsub!(/(not|!)/, '')
|
|
1733
|
+
take noun, attribute.to_sym
|
|
1734
|
+
else
|
|
1735
|
+
give noun, attribute.to_sym
|
|
1736
|
+
end
|
|
1737
|
+
end
|
|
1738
|
+
else
|
|
1739
|
+
give noun, attributes
|
|
1740
|
+
end
|
|
1741
|
+
end
|
|
1742
|
+
TagsSub()
|
|
1743
|
+
end
|
|
1744
|
+
|
|
1745
|
+
def UntagSub
|
|
1746
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1747
|
+
attributes = text ? text.split : []
|
|
1748
|
+
attributes -= language_descriptors.keys
|
|
1749
|
+
attributes -= ProtectedTags unless privileged?(:admin)
|
|
1750
|
+
unless attributes.empty?
|
|
1751
|
+
take noun, attributes unless attributes.empty?
|
|
1752
|
+
println The(noun) + " no longer has " + attributes.join(' ') + "."
|
|
1753
|
+
end
|
|
1754
|
+
TagsSub()
|
|
1755
|
+
end
|
|
1756
|
+
|
|
1757
|
+
def TagRoomSub
|
|
1758
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1759
|
+
invoke :Tag, location, text
|
|
1760
|
+
end
|
|
1761
|
+
|
|
1762
|
+
def UntagRoomSub
|
|
1763
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1764
|
+
invoke :Untag, location, text
|
|
1765
|
+
end
|
|
1766
|
+
|
|
1767
|
+
def UsersSub
|
|
1768
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1769
|
+
Account.select(:username).all.map(&:username)
|
|
1770
|
+
end
|
|
1771
|
+
|
|
1772
|
+
def CreateUserSub
|
|
1773
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1774
|
+
account = Account.find(username: text)
|
|
1775
|
+
return "There is already an account with that username." unless account.nil?
|
|
1776
|
+
account = Account.create(username: text)
|
|
1777
|
+
return "Failed to create account." if account.nil?
|
|
1778
|
+
"Created account for #{account.username}."
|
|
1779
|
+
end
|
|
1780
|
+
|
|
1781
|
+
def DeleteUserSub
|
|
1782
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1783
|
+
account = Account.find(username: text)
|
|
1784
|
+
return "There is no account with that username." if account.nil?
|
|
1785
|
+
return ask("Are you sure you want to delete the account for #{text}?") unless asked?
|
|
1786
|
+
if account.destroy
|
|
1787
|
+
"Deleted account for #{account.username}."
|
|
1788
|
+
else
|
|
1789
|
+
"Failed to delete account."
|
|
1790
|
+
end
|
|
1791
|
+
end
|
|
1792
|
+
|
|
1793
|
+
def PasswordSub
|
|
1794
|
+
if session[:account].nil? && session[:password].nil?
|
|
1795
|
+
account = Account.find_by_character @player
|
|
1796
|
+
return "There is no account associated with your user!" if account.nil?
|
|
1797
|
+
session[:account] = account
|
|
1798
|
+
solicit "Enter new password: "
|
|
1799
|
+
elsif session[:password].nil?
|
|
1800
|
+
return solicit "Enter new password: " if text.empty?
|
|
1801
|
+
session[:password] = text
|
|
1802
|
+
solicit "Confirm new password: "
|
|
1803
|
+
elsif !session[:account].nil? && !session[:password].nil?
|
|
1804
|
+
password = session.delete(:password)
|
|
1805
|
+
account = session.delete(:account)
|
|
1806
|
+
if text == password
|
|
1807
|
+
account.password = text
|
|
1808
|
+
if account.save
|
|
1809
|
+
println "Set password for #{account.username}."
|
|
1810
|
+
else
|
|
1811
|
+
println "Failed to set password."
|
|
1812
|
+
end
|
|
1813
|
+
else
|
|
1814
|
+
println "Passwords do not match."
|
|
1815
|
+
end
|
|
1816
|
+
session.state = :playing
|
|
1817
|
+
end
|
|
1818
|
+
end
|
|
1819
|
+
|
|
1820
|
+
def UserPasswordSub
|
|
1821
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1822
|
+
if session[:account].nil? && session[:password].nil?
|
|
1823
|
+
account = @search_results.first
|
|
1824
|
+
return "There is no account with that username." if account.nil?
|
|
1825
|
+
session[:account] = account
|
|
1826
|
+
solicit "Enter new password: "
|
|
1827
|
+
elsif session[:password].nil?
|
|
1828
|
+
session[:password] = text
|
|
1829
|
+
solicit "Confirm new password: "
|
|
1830
|
+
elsif !session[:account].nil? && !session[:password].nil?
|
|
1831
|
+
password = session.delete(:password)
|
|
1832
|
+
account = session.delete(:account)
|
|
1833
|
+
if text == password
|
|
1834
|
+
account.password = text
|
|
1835
|
+
if account.save
|
|
1836
|
+
println "Set password for #{account.username}."
|
|
1837
|
+
else
|
|
1838
|
+
println "Failed to set password."
|
|
1839
|
+
end
|
|
1840
|
+
else
|
|
1841
|
+
println "Passwords do not match."
|
|
1842
|
+
end
|
|
1843
|
+
session.state = :playing
|
|
1844
|
+
end
|
|
1845
|
+
end
|
|
1846
|
+
|
|
1847
|
+
def CharactersSub
|
|
1848
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1849
|
+
return Inform::Object.where(Sequel.like(:object_type, "%Character%")).order(:name).all if text.nil?
|
|
1850
|
+
account = Account.find(username: text)
|
|
1851
|
+
return "There is no account with that username." if account.nil?
|
|
1852
|
+
"Characters for account #{account} are #{account.characters}."
|
|
1853
|
+
end
|
|
1854
|
+
|
|
1855
|
+
def TeleportSub
|
|
1856
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1857
|
+
target = noun
|
|
1858
|
+
l = second || @player.location
|
|
1859
|
+
target.event do
|
|
1860
|
+
target.println "Inexplicably, you feel momentarily serene."
|
|
1861
|
+
target.new_line
|
|
1862
|
+
target.inflib.PlayerTo(l)
|
|
1863
|
+
end
|
|
1864
|
+
"You teleport " + the(target) + " to " + the(l) + "."
|
|
1865
|
+
end
|
|
1866
|
+
|
|
1867
|
+
def ThreadSub
|
|
1868
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1869
|
+
println "Main thread is #{Thread.main}"
|
|
1870
|
+
println "Current thread is #{Thread.current}"
|
|
1871
|
+
true
|
|
1872
|
+
end
|
|
1873
|
+
|
|
1874
|
+
def TimeSub
|
|
1875
|
+
Time.now
|
|
1876
|
+
end
|
|
1877
|
+
|
|
1878
|
+
def ActiveSub
|
|
1879
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1880
|
+
active = Inform::Events::ActiveObjects.to_a - Inform::Daemons::Schedules.keys.to_a
|
|
1881
|
+
return "No active objects." if active.empty?
|
|
1882
|
+
active.sort
|
|
1883
|
+
end
|
|
1884
|
+
|
|
1885
|
+
def DaemonsSub
|
|
1886
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1887
|
+
println "[Daemons listing " + (Inform::Daemons.on? ? "on" : "off") + ".]"
|
|
1888
|
+
println "Scanning daemons..."
|
|
1889
|
+
add_event do
|
|
1890
|
+
Inform::Daemons.rescan
|
|
1891
|
+
daemons = Inform::Daemons::Spawn.map(&:name).sort
|
|
1892
|
+
return "There are no known daemons." if daemons.empty?
|
|
1893
|
+
n = daemons.length
|
|
1894
|
+
println "There " + IsorAre(n) + " active " + pluralize("daemon", n) + ":"
|
|
1895
|
+
daemons
|
|
1896
|
+
end
|
|
1897
|
+
true
|
|
1898
|
+
end
|
|
1899
|
+
|
|
1900
|
+
def daemons_listing_status
|
|
1901
|
+
return "on" if Inform::Daemons.on?
|
|
1902
|
+
"off"
|
|
1903
|
+
end
|
|
1904
|
+
|
|
1905
|
+
def DaemonsOnSub
|
|
1906
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1907
|
+
Inform::Daemons.start
|
|
1908
|
+
"[Daemons listing #{daemons_listing_status}.]"
|
|
1909
|
+
end
|
|
1910
|
+
|
|
1911
|
+
def DaemonsOffSub
|
|
1912
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1913
|
+
Inform::Daemons.stop
|
|
1914
|
+
"[Daemons listing #{daemons_listing_status}.]"
|
|
1915
|
+
end
|
|
1916
|
+
|
|
1917
|
+
def FrequencySub
|
|
1918
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1919
|
+
Inform::Game.config[:daemons_period] = special_number1 unless special_number1.nil?
|
|
1920
|
+
"Daemon frequency is #{Inform::Game.config[:daemons_period]} milliseconds."
|
|
1921
|
+
end
|
|
1922
|
+
|
|
1923
|
+
def TimersOnSub
|
|
1924
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1925
|
+
@debug_flag = @debug_flag | 4; "[Timers listing on.]"
|
|
1926
|
+
end
|
|
1927
|
+
|
|
1928
|
+
def TimersOffSub
|
|
1929
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1930
|
+
@debug_flag = @debug_flag & 11; "[Timers listing off.]"
|
|
1931
|
+
end
|
|
1932
|
+
|
|
1933
|
+
def TraceOnSub
|
|
1934
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1935
|
+
DebugSub()
|
|
1936
|
+
end
|
|
1937
|
+
|
|
1938
|
+
def TraceLevelSub
|
|
1939
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1940
|
+
DebugSub()
|
|
1941
|
+
end
|
|
1942
|
+
|
|
1943
|
+
def VocabularySub
|
|
1944
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1945
|
+
Inform::Dictionary.sort.to_s
|
|
1946
|
+
end
|
|
1947
|
+
|
|
1948
|
+
def WeatherSub
|
|
1949
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
1950
|
+
weather = noun
|
|
1951
|
+
return "For some reason your location cannot be determined." if location.nil?
|
|
1952
|
+
area = location.linkto :area
|
|
1953
|
+
loop do
|
|
1954
|
+
area_parent = parent(area)
|
|
1955
|
+
break if area_parent.nil?
|
|
1956
|
+
area = area_parent
|
|
1957
|
+
end
|
|
1958
|
+
if weather.nil?
|
|
1959
|
+
return "No weather here." if area.nil?
|
|
1960
|
+
current_weather = area.children.find { |o| o.is_a? Weather }
|
|
1961
|
+
return "No weather here." if current_weather.nil?
|
|
1962
|
+
new_line
|
|
1963
|
+
style :bold
|
|
1964
|
+
color :yellow
|
|
1965
|
+
println current_weather.name
|
|
1966
|
+
uncolor :yellow
|
|
1967
|
+
unstyle :bold
|
|
1968
|
+
return current_weather.description
|
|
1969
|
+
end
|
|
1970
|
+
return "Cannot set weather here." if area.nil?
|
|
1971
|
+
area.children.each { |o| o.remove if o.is_a? Weather }
|
|
1972
|
+
move weather, area
|
|
1973
|
+
"The weather for " + the(area) + " is now " + the(weather) + "."
|
|
1974
|
+
end
|
|
1975
|
+
|
|
1976
|
+
def WhoamiSub
|
|
1977
|
+
"You are " + a(@player) + "."
|
|
1978
|
+
end
|
|
1979
|
+
|
|
1980
|
+
def WhoamiVerboseSub
|
|
1981
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
1982
|
+
"#{@player.identity} (" + a(@player) + ") => " +
|
|
1983
|
+
StoryTeller::IO::Session.of(@player).channel.to_s
|
|
1984
|
+
end
|
|
1985
|
+
|
|
1986
|
+
def WrapSub
|
|
1987
|
+
session = StoryTeller::IO::Session.of(@player)
|
|
1988
|
+
return "Cannot set preferences." if session.preferences.nil?
|
|
1989
|
+
word_wrap = special_number1
|
|
1990
|
+
# prefs.word_wrap = word_wrap unless word_wrap.nil?
|
|
1991
|
+
session.preferences[:word_wrap] = word_wrap unless word_wrap.nil?
|
|
1992
|
+
log.debug "#{@player} preferences are: #{session.preferences.inspect}"
|
|
1993
|
+
"Lines will now wrap at #{session.preferences[:word_wrap]} columns."
|
|
1994
|
+
end
|
|
1995
|
+
|
|
1996
|
+
def VisitedSub
|
|
1997
|
+
@player.visited.join(', ')
|
|
1998
|
+
end
|
|
1999
|
+
|
|
2000
|
+
def markdown_metadata(file_path, metadata = {})
|
|
2001
|
+
metadata['path'] = file_path
|
|
2002
|
+
"\n#{metadata.to_yaml}---"
|
|
2003
|
+
end
|
|
2004
|
+
|
|
2005
|
+
def ruby_metadata(file_path)
|
|
2006
|
+
"# file: #{file_path}"
|
|
2007
|
+
end
|
|
2008
|
+
|
|
2009
|
+
def ConcatenateSub
|
|
2010
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
2011
|
+
file_path = text
|
|
2012
|
+
println ruby_metadata(file_path) if /.*\.rb$/.match?(file_path)
|
|
2013
|
+
println markdown_metadata(file_path) if /.*\.md$/.match?(file_path)
|
|
2014
|
+
`cat #{file_path}`
|
|
2015
|
+
end
|
|
2016
|
+
|
|
2017
|
+
def ListStatsSub
|
|
2018
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
2019
|
+
file_path = text
|
|
2020
|
+
return `/bin/ls -l #{file_path}`.strip unless file_path.empty?
|
|
2021
|
+
`/bin/ls -l`.strip
|
|
2022
|
+
end
|
|
2023
|
+
|
|
2024
|
+
def HostSub
|
|
2025
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
2026
|
+
`hostname`.strip
|
|
2027
|
+
end
|
|
2028
|
+
|
|
2029
|
+
def pid
|
|
2030
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
2031
|
+
java.lang.management.ManagementFactory.getRuntimeMXBean().name.split('@').first
|
|
2032
|
+
rescue StandardError
|
|
2033
|
+
println "I can't seem to determine my process ID: #{e.message}"
|
|
2034
|
+
false
|
|
2035
|
+
end
|
|
2036
|
+
end
|
|
2037
|
+
|
|
2038
|
+
# module PrivilegedInformVerbs
|
|
2039
|
+
module PrivilegedInformVerbs
|
|
2040
|
+
def ScopeSub
|
|
2041
|
+
raise Parser::VerbUnrecognized unless privileged?(:admin)
|
|
2042
|
+
|
|
2043
|
+
super
|
|
2044
|
+
end
|
|
2045
|
+
|
|
2046
|
+
def XTreeSub
|
|
2047
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
2048
|
+
|
|
2049
|
+
super
|
|
2050
|
+
end
|
|
2051
|
+
|
|
2052
|
+
def GotoSub
|
|
2053
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
2054
|
+
|
|
2055
|
+
super
|
|
2056
|
+
end
|
|
2057
|
+
|
|
2058
|
+
def GonearSub
|
|
2059
|
+
raise Parser::VerbUnrecognized unless privileged?(:builder)
|
|
2060
|
+
|
|
2061
|
+
super
|
|
2062
|
+
end
|
|
2063
|
+
end
|
|
2064
|
+
|
|
2065
|
+
Inform::Verbs.prepend PrivilegedInformVerbs
|
|
2066
|
+
Inform::Verbs.include Metaverbs
|