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