inform6lib 0.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.
@@ -0,0 +1,2481 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # rubocop: disable Lint/UselessAssignment
5
+
6
+ # ==============================================================================
7
+ # VerbLib: Core of standard verbs library.
8
+ #
9
+ # Supplied for use with Inform 6 -- Release 6/11 -- Serial number 040227
10
+ #
11
+ # Copyright Graham Nelson 1993-2004 but freely usable (see manuals)
12
+ #
13
+ # This file is automatically Included in your game file by "VerbLib".
14
+ # ==============================================================================
15
+
16
+ # Copyright Nels Nelson 2008-2022 but freely usable (see license)
17
+ #
18
+ # You should have received a copy of the Artistic License
19
+ # along with the Inform6 Ruby Port.
20
+ #
21
+ # If not, see <https://www.perlfoundation.org/artistic-license-20.html>.
22
+
23
+ # The Inform module
24
+ module Inform
25
+ NEWLINE_BIT = 1 # New-line after each entry
26
+ INDENT_BIT = 2 # Indent each entry by depth
27
+ FULLINV_BIT = 4 # Full inventory information after entry
28
+ ENGLISH_BIT = 8 # English sentence style, with commas and and
29
+ RECURSE_BIT = 16 # Recurse downwards with usual rules
30
+ ALWAYS_BIT = 32 # Always recurse downwards
31
+ TERSE_BIT = 64 # More terse English style
32
+ PARTINV_BIT = 128 # Only brief inventory information after entry
33
+ DEFART_BIT = 256 # Use the definite article in list
34
+ WORKFLAG_BIT = 512 # At top level (only), only list objects
35
+ # which have the "workflag" attribute
36
+ ISARE_BIT = 1024 # Print " is" or " are" before list
37
+ CONCEAL_BIT = 2048 # Omit objects with "concealed" or "scenery":
38
+ # if WORKFLAG_BIT also set, then does _not_
39
+ # apply at top level, but does lower down
40
+ NOARTICLE_BIT = 4096 # Print no articles, definite or not
41
+
42
+ EXTRAINDENT_BIT = 8192 # Used only by I7: extra indentation of 1 level
43
+ EXTRA_WORKFLAG_BIT = 16_384 # Ditto: like WORKFLAG_BIT but at level 1
44
+
45
+ # The Verbs module
46
+ module Verbs
47
+ include Inform::English
48
+
49
+ def Banner
50
+ if defined?(Story) && !Story.nil? && !Story.empty?
51
+ style :bold
52
+ print Story
53
+ style :roman
54
+ end
55
+
56
+ print Headline if defined?(Headline) && !Headline.nil? && !Headline.empty?
57
+ print " by " + Story_Author if defined?(Story_Author) && !Story_Author.nil? && !Story_Author.empty?
58
+ print "Release " + (HDR_GAMERELEASE & 0x03ff)
59
+ print " / Serial number "
60
+ print HDR_GAMESERIAL
61
+ # defined?(TARGET_)
62
+ print " / #{executable} v"; inversion
63
+ print " Library " + LibRelease.to_s + " "
64
+ if defined? STRICT_MODE
65
+ print "S"
66
+ end
67
+ # defined? STRICT_MODE
68
+ if defined? INFIX
69
+ print "X"
70
+ else
71
+ if defined? DEBUG
72
+ print "D"
73
+ end
74
+ # defined? DEBUG
75
+ end
76
+ # defined? INFIX
77
+ new_line
78
+ end
79
+
80
+ def VersionSub
81
+ Banner()
82
+ if standard_interpreter > 0
83
+ print "Standard interpreter " + (standard_interpreter / 256) + "." + (standard_interpreter % 256) +
84
+ " (" + HDR_TERPNUMBER[0]
85
+ if version_number == 6
86
+ print '.' + HDR_TERPVERSION[0]
87
+ else
88
+ print HDR_TERPVERSION[0]
89
+ end
90
+ print ") / "
91
+ else
92
+ print "Interpreter " + HDR_TERPNUMBER[0] + " Version "
93
+ if version_number == 6
94
+ print HDR_TERPVERSION[0].to_s
95
+ else
96
+ print HDR_TERPVERSION[0]
97
+ end
98
+ print " / "
99
+ end
100
+ end
101
+
102
+ def RunTimeError(n, p1 = nil, p2 = nil)
103
+ if defined? DEBUG
104
+ print "** Library error #{n} (p1,p2) **\n** "
105
+ case n
106
+ when 1 then print "preposition not found (this should not occur)"
107
+ when 2 then print "Property value not routine or string: \"#{p2}\" of \"#{p1}\"" +
108
+ " (#{p1})";
109
+ when 3 then print "Entry in property list not routine or string: \"#{p2}\" list of \"#{p1}\"" +
110
+ " (#{p1})"
111
+ when 4 then print "Too many timers/daemons are active simultaneously. " +
112
+ "The limit is the library constant MAX_TIMERS (currently #{MAX_TIMERS}) " +
113
+ "and should be increased"
114
+ when 5 then print "Object \"#{p1}\" has no \"time_left\" property"
115
+ when 7 then print "The object \"#{p1}\" can only be used as a player object if it has " +
116
+ "the \"number\" property"
117
+ when 8 then print "Attempt to take random entry from an empty table array"
118
+ when 9 then print "#{p1} is not a valid direction property number"
119
+ when 10 then print "The player-object is outside the object tree"
120
+ when 11 then print "The room \"#{p1}\" has no \"description\" property"
121
+ when 12 then print "Tried to set a non-existent pronoun using SetPronoun"
122
+ when 13 then print "A 'topic' token can only be followed by a preposition"
123
+ else
124
+ print "(unexplained)";
125
+ end
126
+ " **"
127
+ else
128
+ "** Library error #{n} (#{p1},#{p2}) **"
129
+ end
130
+ end
131
+
132
+ # ----------------------------------------------------------------------------
133
+ # The WriteListFrom routine, a flexible object-lister taking care of
134
+ # plurals, inventory information, various formats and so on. This is used
135
+ # by everything in the library which ever wants to list anything.
136
+ #
137
+ # If there were no objects to list, it prints nothing and returns false;
138
+ # otherwise it returns true.
139
+ #
140
+ # o is the object, and style is a bitmap, whose bits are given by:
141
+ # ----------------------------------------------------------------------------
142
+
143
+ def NextEntry(o, odepth, lt_value = @lt_value, c_style = @c_style, &_block)
144
+ loop do
145
+ o = o.sibling
146
+ return nil if o.nil?
147
+ next if lt_value == true && o.list_together != lt_value
148
+ next if (c_style & WORKFLAG_BIT) != 0 && odepth == 0 && o.hasnt?(:workflag)
149
+ next if (c_style & CONCEAL_BIT) != 0 && o.hasany?(:concealed, :scenery)
150
+ return o
151
+ end
152
+ end
153
+
154
+ def WillRecurs(o, c_style = @c_style)
155
+ return true if (c_style & ALWAYS_BIT) != 0
156
+ return false if (c_style & RECURSE_BIT) == 0
157
+ return true if o.hasany?(:transparent, :supporter)
158
+ return true if o.has?(:open, :container)
159
+ return false
160
+ end
161
+
162
+ def ListEqual(o1, o2, c_style = @c_style)
163
+ return false if child(o1) && WillRecurs(o1)
164
+ return false if child(o2) && WillRecurs(o2)
165
+ if c_style & (FULLINV_BIT + PARTINV_BIT) != 0
166
+ return false if (o1.hasnt?(:worn) && o2.has?(:worn)) || (o2.hasnt?(:worn) && o1.has?(:worn))
167
+ return false if (o1.hasnt?(:light) && o2.has?(:light)) || (o2.hasnt?(:light) && o1.has?(:light))
168
+ if o1.has?(:container)
169
+ return false if o2.hasnt?(:container)
170
+ return false if o1.has?(:open) && o2.hasnt?(:open)
171
+ return false if o2.has?(:open) && o1.hasnt?(:open)
172
+ elsif o2.has?(:container)
173
+ return false
174
+ end
175
+ end
176
+ return Identical(o1, o2)
177
+ end
178
+
179
+ def SortTogether(obj, value)
180
+ # println "Sorting together possessions of " + obj + " by value " + value
181
+ # x = child(obj)
182
+ # loop do
183
+ # break if x.nil?
184
+ # println the(x) + " no: " + x + " lt: " + x.list_together
185
+ # x = sibling(x)
186
+ # end
187
+ a = [] # out
188
+ b = [] # in
189
+ while child(obj)
190
+ if child(obj).list_together != value
191
+ a << child(obj)
192
+ else
193
+ b << child(obj)
194
+ end
195
+ child(obj).delete
196
+ end
197
+ b.each { |o| move o, obj } # out
198
+ a.each { |o| move o, obj } # in
199
+ end
200
+
201
+ def SortOutList(obj)
202
+ # print "\n\nSorting out list from " + obj.name + "\n "
203
+ # i = child(@location)
204
+ # loop do
205
+ # break if i.nil?
206
+ # print i.name + " --> "
207
+ # i = sibling(i)
208
+ # end
209
+ # new_line
210
+ catch :AP_SOL do
211
+ i = obj
212
+ loop do
213
+ break if i.nil?
214
+ k = i.list_together
215
+ unless k.nil?
216
+ i = sibling(i)
217
+ loop do
218
+ break if i.nil? || i.list_together != k
219
+ i = sibling(i)
220
+ end
221
+ return false if i.nil?
222
+ l = sibling(i)
223
+ loop do
224
+ break if l.nil?
225
+ if l.list_together == k
226
+ SortTogether(parent(obj), k)
227
+ obj = parent(child(obj))
228
+ throw AP_SOL
229
+ end
230
+ l = sibling(l)
231
+ end
232
+ end
233
+ i = sibling(i)
234
+ end
235
+ end
236
+ # AP_SOL
237
+ end
238
+
239
+ def Print__Spaces(n)
240
+ return if n == 0
241
+ spaces n
242
+ end
243
+
244
+ def WriteListFrom(o, style, depth = 0, &query)
245
+ if o.nil? || parent(o).nil? || (!query.nil? && parent(o).none?(&query))
246
+ print NOTHING__TX
247
+ new_line if style & NEWLINE_BIT != 0
248
+ return
249
+ end
250
+ if o == parent(child(o))
251
+ SortOutList(o)
252
+ o = parent(child(o))
253
+ end
254
+ # @c_style = style
255
+ # @wlf_indent = 0
256
+ WriteListR(o, depth, stack_pointer = 0, c_style = style, &query)
257
+ true
258
+ end
259
+
260
+ def WriteListR(o, depth, stack_pointer = 0,
261
+ c_style = @c_style, lt_value = @lt_value, listing_together = @listing_together,
262
+ listing_size = @listing_size, wlf_indent = @wlf_indent, &query)
263
+ return if o.nil? || parent(o).nil?
264
+ senc = 0
265
+
266
+ if depth > 0 && o == parent(child(o))
267
+ SortOutList(o)
268
+ o = parent(child(o))
269
+ end
270
+
271
+ loop do
272
+ return false if o.nil?
273
+ if (c_style & WORKFLAG_BIT) != 0 && depth == 0 && o.hasnt?(:workflag)
274
+ o = o.sibling
275
+ next
276
+ end
277
+ if (c_style & CONCEAL_BIT) != 0 && o.hasany?(:concealed, :scenery)
278
+ o = o.sibling
279
+ next
280
+ end
281
+ if o == @player || (!query.nil? && !query.call(o))
282
+ o = o.sibling
283
+ next
284
+ end
285
+ break
286
+ end
287
+
288
+ classes_p = @match_classes.is_a?(Array) ? @match_classes.dup : []
289
+ sizes_p = @match_list.is_a?(Array) ? @match_list.dup : []
290
+
291
+ i = o; j = 0; k = 0
292
+ loop do
293
+ break if i.nil?
294
+ classes_p[j] = 0
295
+ k += 1 if i.&:plural
296
+ i = NextEntry(i, depth, lt_value, c_style, &query)
297
+ j += 1
298
+ end
299
+
300
+ if (c_style & ISARE_BIT) != 0
301
+ if j == 1 && o.hasnt?(:pluralname) then print IS__TX
302
+ else print ARE__TX
303
+ end
304
+ if (c_style & NEWLINE_BIT) != 0 then println ":"
305
+ else print ' '
306
+ end
307
+ c_style = c_style - ISARE_BIT
308
+ end
309
+
310
+ stack_pointer += (j + 1)
311
+
312
+ if k < 2 # It takes two to plural
313
+ return EconomyVersion(o, j, depth, senc, stack_pointer,
314
+ c_style, lt_value,
315
+ listing_together, listing_size,
316
+ wlf_indent, &query)
317
+ end
318
+
319
+ n = 1; i = o; k = 0
320
+ loop do
321
+ break if k >= j
322
+ if classes_p[k] == 0
323
+ classes_p[k] = n; sizes_p[n] = 1
324
+ l = NextEntry(i, depth, lt_value, c_style, &query)
325
+ m = k + 1
326
+ loop do
327
+ break if l.nil? || m >= j
328
+ if classes_p[m] == 0 && i.&(:plural) && l.&(:plural)
329
+ if ListEqual(i, l)
330
+ sizes_p[n] += 1
331
+ classes_p[m] = n
332
+ end
333
+ end
334
+ l = NextEntry(l, depth, lt_value, c_style, &query)
335
+ m += 1
336
+ end
337
+ n += 1
338
+ end
339
+ i = NextEntry(i, depth, lt_value, c_style, &query)
340
+ k += 1
341
+ end
342
+ n -= 1
343
+
344
+ i = 1; j = o; k = 0
345
+ loop do
346
+ break if i > n
347
+ loop do
348
+ break if (classes_p[k] == i) || (classes_p[k] == -i)
349
+ k += 1; j = NextEntry(j, depth, lt_value, c_style, &query)
350
+ end
351
+ m = sizes_p[i]
352
+ if j.nil? then mr = nil
353
+ else
354
+ if !j.list_together.nil? || (!lt_value.nil? && [2, 3].include?(ZRegion(j.list_together)) &&
355
+ j.list_together == mr)
356
+ senc -= 1
357
+ end
358
+ mr = j.list_together
359
+ end
360
+ i += 1
361
+ senc += 1
362
+ end
363
+ senc -= 1
364
+
365
+ i = 1; j = o; k = 0; mr = nil
366
+ loop do
367
+ break if senc < 0
368
+ loop do
369
+ break if (classes_p[k] == i) || (classes_p[k] == -i)
370
+ k += 1; j = NextEntry(j, depth, lt_value, c_style, &query)
371
+ end
372
+ if j.nil? # TODO: FIXME and remove
373
+ log.warn "classes_p: #{classes_p.inspect}"
374
+ log.warn "classes_p k: #{k}, i: #{i}"
375
+ log.warn "FIXME: Too many entries in classes_p"
376
+ break
377
+ end
378
+ if !j.list_together.nil? || !lt_value.nil?
379
+ if j.list_together == mr
380
+ # Omit_FL2
381
+ i += 1
382
+ next
383
+ end
384
+ k2 = NextEntry(j, depth, lt_value, c_style, &query)
385
+ if k2.nil? || k2.list_together != j.list_together
386
+ # Omit_WL2
387
+
388
+ if WriteBeforeEntry(j, depth, -senc, c_style, wlf_indent) == 1
389
+ # Omit_FL2
390
+ i += 1
391
+ senc -= 1
392
+ next
393
+ end
394
+
395
+ if sizes_p[i] == 1
396
+ if (c_style & NOARTICLE_BIT) != 0 then print j.name
397
+ else
398
+ if (c_style & DEFART_BIT) != 0 then print the(j) else print a(j) end
399
+ end
400
+ else
401
+ PrefaceByArticle(j, 1, sizes_p[i]) if (c_style & DEFART_BIT) != 0
402
+ print number(sizes_p[i]) + " "
403
+ PrintOrRun(j, :plural, 1)
404
+ end
405
+
406
+ if sizes_p[i] > 1 && j.hasnt?(:pluralname)
407
+ give j, :pluralname
408
+ WriteAfterEntry(j, depth, stack_pointer,
409
+ c_style, lt_value,
410
+ listing_together,
411
+ listing_size)
412
+ take j, :pluralname
413
+ else
414
+ WriteAfterEntry(j, depth, stack_pointer,
415
+ c_style, lt_value,
416
+ listing_together,
417
+ listing_size)
418
+ end
419
+
420
+ # Omit_EL2
421
+
422
+ if (c_style & ENGLISH_BIT) != 0
423
+ print AND__TX if senc == 1
424
+ print COMMA__TX if senc > 1
425
+ end
426
+
427
+ # Omit_FL2
428
+
429
+ i += 1; senc -= 1
430
+ next
431
+ end
432
+
433
+ k2 = ZRegion(j.list_together)
434
+ if [2, 3].include?(k2)
435
+ q = j; listing_size = 1; l = k; m = i
436
+ loop do
437
+ break unless m < n && q.list_together == j.list_together
438
+ m += 1
439
+ loop do
440
+ break unless (classes_p[l] != m) && (classes_p[l] != -m)
441
+ l += 1; q = NextEntry(q, depth, lt_value, c_style, &query)
442
+ end
443
+ listing_size += 1 if q.list_together == j.list_together
444
+ end
445
+ # print " [" + listing_size.to_s + "] "
446
+ if listing_size == 1
447
+ # Omit_WL2
448
+
449
+ if WriteBeforeEntry(j, depth, -senc, c_style, wlf_indent) == 1
450
+ # Omit_FL2
451
+ i += 1; senc -= 1
452
+ next
453
+ end
454
+
455
+ if sizes_p[i] == 1
456
+ if (c_style & NOARTICLE_BIT) != 0 then print j.name
457
+ else
458
+ if (c_style & DEFART_BIT) != 0 then print the(j) else print a(j) end
459
+ end
460
+ else
461
+ PrefaceByArticle(j, 1, sizes_p[i]) if (c_style & DEFART_BIT) != 0
462
+ print number(sizes_p[i]) + " "
463
+ PrintOrRun(j, :plural, 1)
464
+ end
465
+
466
+ if sizes_p[i] > 1 && j.hasnt?(:pluralname)
467
+ give j, :pluralname
468
+ WriteAfterEntry(j, depth, stack_pointer,
469
+ c_style, lt_value,
470
+ listing_together,
471
+ listing_size)
472
+ take j, :pluralname
473
+ else
474
+ WriteAfterEntry(j, depth, stack_pointer,
475
+ c_style, lt_value,
476
+ listing_together,
477
+ listing_size)
478
+ end
479
+
480
+ # Omit_EL2
481
+
482
+ if (c_style & ENGLISH_BIT) != 0
483
+ print AND__TX if senc == 1
484
+ print COMMA__TX if senc > 1
485
+ end
486
+
487
+ # Omit_FL2
488
+
489
+ i += 1; senc -= 1
490
+ end
491
+
492
+ Print__Spaces(2 * (depth + wlf_indent)) if (c_style & INDENT_BIT) != 0
493
+
494
+ if k2 == 3
495
+ q = 0
496
+ listing_size.times { |l2| q += sizes_p[l2 + i] }
497
+ EnglishNumber(q); print " "
498
+ print j.list_together
499
+ print " (" if (c_style & ENGLISH_BIT) != 0
500
+ println ":" if (c_style & INDENT_BIT) != 0
501
+ end
502
+ q = c_style
503
+ if k2 != 3
504
+ @inventory_stage = 1
505
+ @parser_one = j; @parser_two = depth + wlf_indent
506
+ if RunRoutines(j, :list_together) == 1
507
+ # Omit__Sublist2
508
+
509
+ new_line if (q & NEWLINE_BIT) != 0 && (c_style & NEWLINE_BIT) == 0
510
+ c_style = q
511
+ mr = j.list_together
512
+
513
+ # Omit_EL2
514
+
515
+ if (c_style & ENGLISH_BIT) != 0
516
+ print AND__TX if senc == 1
517
+ print COMMA__TX if senc > 1
518
+ end
519
+
520
+ i += 1; senc -= 1
521
+ next
522
+ end
523
+ end
524
+
525
+ # if defined? TARGET_ZCODE
526
+ # push lt_value; push listing_together; push listing_size;
527
+ # else # TARGET_GLULX
528
+ # copy lt_value, sp; copy listing_together, sp; copy listing_size, sp
529
+ # end
530
+ # # defined? TARGET_GLULX
531
+
532
+ # @lt_value = j.list_together; @listing_together = j; @wlf_indent += 1
533
+ WriteListR(j, depth, stack_pointer,
534
+ c_style, lt_value = j.list_together,
535
+ listing_together = j, listing_size,
536
+ wlf_indent + 1, &query)
537
+ # ; @wlf_indent -= 1
538
+
539
+ # if defined? TARGET_ZCODE
540
+ # pull listing_size; pull listing_together; pull lt_value
541
+ # else # TARGET_GLULX
542
+ # copy sp, listing_size
543
+ # copy sp, listing_together
544
+ # copy sp, lt_value
545
+ # end
546
+ # # TARGET_ZCODE
547
+
548
+ if k2 == 3
549
+ print ")" if (q & ENGLISH_BIT) != 0
550
+ else
551
+ @inventory_stage = 2
552
+ @parser_one = j; @parser_two = depth + wlf_indent
553
+ RunRoutines(j, :list_together)
554
+ end
555
+
556
+ # label Omit__Sublist2
557
+
558
+ new_line if (q & NEWLINE_BIT) != 0 && (c_style & NEWLINE_BIT) == 0
559
+ c_style = q
560
+ mr = j.list_together
561
+
562
+ # Omit_EL2
563
+
564
+ if (c_style & ENGLISH_BIT) != 0
565
+ print AND__TX if senc == 1
566
+ print COMMA__TX if senc > 1
567
+ end
568
+
569
+ i += 1; senc -= 1
570
+ next
571
+ end
572
+ end
573
+ # if !j.list_together.nil? || !lt_value.nil?
574
+
575
+ # label Omit_WL2
576
+
577
+ if WriteBeforeEntry(j, depth, -senc, c_style, wlf_indent) == 1
578
+ # Omit_FL2
579
+ i += 1; senc -= 1
580
+ next
581
+ end
582
+
583
+ if sizes_p[i] == 1
584
+ if (c_style & NOARTICLE_BIT) != 0 then print j.name
585
+ else
586
+ if (c_style & DEFART_BIT) != 0 then print the(j) else print a(j) end
587
+ end
588
+ else
589
+ PrefaceByArticle(j, 1, sizes_p[i]) if (c_style & DEFART_BIT) != 0
590
+ print number(sizes_p[i]) + " "
591
+ PrintOrRun(j, :plural, 1)
592
+ end
593
+
594
+ if sizes_p[i] > 1 && j.hasnt?(:pluralname)
595
+ give j, :pluralname
596
+ WriteAfterEntry(j, depth, stack_pointer,
597
+ c_style, lt_value,
598
+ listing_together,
599
+ listing_size)
600
+ take j, :pluralname
601
+ else
602
+ WriteAfterEntry(j, depth, stack_pointer,
603
+ c_style, lt_value,
604
+ listing_together,
605
+ listing_size)
606
+ end
607
+
608
+ # label Omit_EL2
609
+
610
+ if (c_style & ENGLISH_BIT) != 0
611
+ print AND__TX if senc == 1
612
+ print COMMA__TX if senc > 1
613
+ end
614
+
615
+ # label Omit_FL2
616
+
617
+ i += 1; senc -= 1
618
+ end
619
+ # loop
620
+ return true
621
+ end
622
+ # def WriteListR
623
+
624
+ def EconomyVersion(o, j, depth, senc, stack_pointer = 0,
625
+ c_style = @c_style, lt_value = @lt_value,
626
+ listing_together = @listing_together,
627
+ listing_size = @listing_size,
628
+ wlf_indent = @wlf_indent, &query)
629
+ n = j; i = 1; j = o
630
+ loop do
631
+ break unless i <= n
632
+ if !j.list_together.nil? || (!lt_value.nil? &&
633
+ [2, 3].include?(ZRegion(j.list_together)) && j.list_together == mr)
634
+ senc -= 1
635
+ end
636
+ mr = j.list_together
637
+ j = NextEntry(j, depth, lt_value, c_style, &query)
638
+ i += 1; senc += 1
639
+ end
640
+
641
+ i = 1; j = o; mr = nil
642
+ loop do
643
+ break unless i <= senc
644
+ if !j.list_together.nil? || !lt_value.nil?
645
+ if j.list_together == mr
646
+ # Omit_FL
647
+ j = NextEntry(j, depth, lt_value, c_style, &query)
648
+ next
649
+ end
650
+ k = NextEntry(j, depth, lt_value, c_style, &query)
651
+ if k.nil? || (k.list_together != j.list_together)
652
+ # Omit_WL
653
+
654
+ if WriteBeforeEntry(j, depth, i - senc, c_style, wlf_indent) == 1
655
+ # Omit_FL
656
+ j = NextEntry(j, depth, lt_value, c_style, &query)
657
+ i += 1
658
+ next
659
+ end
660
+
661
+ if (c_style & NOARTICLE_BIT) != 0 then print j.name
662
+ else
663
+ if (c_style & DEFART_BIT) != 0 then print the(j) else print a(j) end
664
+ end
665
+ WriteAfterEntry(j, depth, stack_pointer,
666
+ c_style, lt_value,
667
+ listing_together,
668
+ listing_size)
669
+
670
+ # label Omit_EL
671
+
672
+ if (c_style & ENGLISH_BIT) != 0
673
+ print AND__TX if i == (senc - 1)
674
+ print COMMA__TX if i < (senc - 1)
675
+ end
676
+
677
+ # label Omit_FL
678
+
679
+ j = NextEntry(j, depth, lt_value, c_style, &query)
680
+ i += 1
681
+ next
682
+ end
683
+
684
+ k = ZRegion(j.list_together)
685
+ if [2, 3].include?(k)
686
+ Print__Spaces(2 * (depth + wlf_indent)) if (c_style & INDENT_BIT) != 0
687
+ if k == 3
688
+ q = j; l = 0
689
+ loop do
690
+ q = NextEntry(q, depth, lt_value, c_style, &query); l += 1
691
+ break if q.nil? || q.list_together != j.list_together
692
+ end
693
+ EnglishNumber(l); print " "
694
+ print j.list_together
695
+ print " (" if (c_style & ENGLISH_BIT) != 0
696
+ print ":\n" if (c_style & INDENT_BIT) != 0
697
+ end
698
+ q = c_style
699
+ if k != 3
700
+ @inventory_stage = 1
701
+ @parser_one = j; @parser_two = depth + wlf_indent
702
+ if RunRoutines(j, :list_together) == 1
703
+ # Omit__Sublist
704
+
705
+ new_line if (q & NEWLINE_BIT) != 0 && (c_style & NEWLINE_BIT) == 0
706
+ c_style = q
707
+ mr = j.list_together
708
+
709
+ # Omit_EL
710
+
711
+ if (c_style & ENGLISH_BIT) != 0
712
+ print AND__TX if i == (senc - 1)
713
+ print COMMA__TX if i < (senc - 1)
714
+ end
715
+
716
+ # Omit_FL
717
+
718
+ j = NextEntry(j, depth, lt_value, c_style, &query)
719
+ i += 1
720
+ next
721
+ end
722
+ end
723
+
724
+ # if defined? TARGET_ZCODE
725
+ # push lt_value; push listing_together; push listing_size
726
+ # else # defined? TARGET_GLULX
727
+ # copy lt_value, sp; copy listing_together, sp; copy listing_size, sp
728
+ # end
729
+ # # TARGET_ZCODE
730
+
731
+ # lt_value = j.list_together; listing_together = j; wlf_indent += 1
732
+ WriteListR(j, depth, stack_pointer,
733
+ c_style, lt_value = j.list_together,
734
+ listing_together = j, listing_size,
735
+ wlf_indent + 1, &query)
736
+ # ; @wlf_indent -= 1
737
+
738
+ # if defined? TARGET_ZCODE
739
+ # pull listing_size; pull listing_together; pull lt_value
740
+ # else # defined? TARGET_GLULX
741
+ # copy sp, listing_size; copy sp, listing_together; copy sp, lt_value
742
+ # end
743
+ # # TARGET_ZCODE
744
+
745
+ if k == 3
746
+ print ")" if (q & ENGLISH_BIT) != 0
747
+ else
748
+ @inventory_stage = 2
749
+ @parser_one = j; @parser_two = depth + wlf_indent
750
+ RunRoutines(j, :list_together)
751
+ end
752
+
753
+ # label Omit__Sublist
754
+
755
+ new_line if (q & NEWLINE_BIT) != 0 && (c_style & NEWLINE_BIT) == 0
756
+ c_style = q
757
+ mr = j.list_together
758
+
759
+ # Omit_EL
760
+
761
+ if (c_style & ENGLISH_BIT) != 0
762
+ print AND__TX if i == (senc - 1)
763
+ print COMMA__TX if i < (senc - 1)
764
+ end
765
+
766
+ # Omit_FL
767
+
768
+ j = NextEntry(j, depth, lt_value, c_style, &query)
769
+ i += 1
770
+ next
771
+ end
772
+ # if [2, 3].include?(k)
773
+ end
774
+ # if !j.list_together.nil? || !lt_value.nil?
775
+
776
+ # label Omit_WL
777
+
778
+ if WriteBeforeEntry(j, depth, i - senc, c_style, wlf_indent) == 1
779
+ # Omit_FL
780
+ j = NextEntry(j, depth, lt_value, c_style, &query)
781
+ i += 1
782
+ next
783
+ end
784
+
785
+ if (c_style & NOARTICLE_BIT) != 0 then print j.name
786
+ else
787
+ if (c_style & DEFART_BIT) != 0 then print the(j) else print a(j) end
788
+ end
789
+ WriteAfterEntry(j, depth, stack_pointer,
790
+ c_style, lt_value,
791
+ listing_together,
792
+ listing_size)
793
+
794
+ # label Omit_EL
795
+
796
+ if (c_style & ENGLISH_BIT) != 0
797
+ print AND__TX if i == (senc - 1)
798
+ print COMMA__TX if i < (senc - 1)
799
+ end
800
+
801
+ # label Omit_FL
802
+
803
+ j = NextEntry(j, depth, lt_value, c_style, &query)
804
+ i += 1
805
+ end
806
+ # loop do break unless i <= senc
807
+ end
808
+ # end EconomyVersion (WriteListR)
809
+
810
+ def WriteBeforeEntry(o, depth, sentencepos, c_style = @c_style, wlf_indent = 0)
811
+ flag = false
812
+
813
+ @inventory_stage = 1
814
+ Print__Spaces(2 * (depth + wlf_indent)) if (c_style & INDENT_BIT) != 0
815
+ if o.respond_to?(:invent) && (c_style & (PARTINV_BIT | FULLINV_BIT)) != 0 # This line changed
816
+ flag = PrintOrRun(o, :invent, 1)
817
+ if flag
818
+ if (c_style & ENGLISH_BIT) == 1
819
+ print AND__TX if sentencepos == -1
820
+ print COMMA__TX if sentencepos < -1
821
+ end
822
+ new_line if (c_style & NEWLINE_BIT) == 1
823
+ end
824
+ end
825
+ flag
826
+ end
827
+
828
+ def WriteAfterEntry(o, depth, stack_p,
829
+ c_style = @c_style, _lt_value = @lt_value,
830
+ _listing_together = @listing_together,
831
+ _listing_size = @listing_size, &query)
832
+ recurse_flag = false
833
+ parenth_flag = false
834
+ eldest_child = nil
835
+ child_count = 0
836
+ combo = nil
837
+ j = false
838
+
839
+ @inventory_stage = 2
840
+ if (c_style & PARTINV_BIT) == 1
841
+ # Begin the Inform6 Ruby Port addition
842
+ if o.respond_to?(:invent) && RunRoutines(o, :invent)
843
+ return true if (c_style & NEWLINE_BIT) == 0
844
+ print ""
845
+ return
846
+ end
847
+ # End the Inform6 Ruby Port addition
848
+
849
+ combo = 0
850
+ combo += 1 if o.has?(:light) && @location.hasnt?(:light)
851
+ combo += 2 if o.has?(:container) && o.hasnt?(:open)
852
+ if o.has?(:container) && (o.has?(:open) || o.has?(:transparent))
853
+ o.objectloop do |i|
854
+ if i.hasnt?(:concealed) && i.hasnt?(:scenery)
855
+ j = true; break
856
+ end
857
+ end
858
+ combo += 4 if j.nil?
859
+ end
860
+ L__M(:ListMiscellany, combo, o) if combo > 0
861
+ end
862
+ # end of PARTINV_BIT processing
863
+
864
+ if (c_style & FULLINV_BIT) != 0
865
+ if o.respond_to?(:invent) && RunRoutines(o, :invent)
866
+ return true if (c_style & NEWLINE_BIT) == 0
867
+ print ""
868
+ return
869
+ end
870
+
871
+ if o.has?(:light) && o.has?(:worn) then L__M(:ListMiscellany, 8, o); parenth_flag = true
872
+ else
873
+ if o.has?(:light) then L__M(:ListMiscellany, 9, o); parenth_flag = true end
874
+ if o.has?(:worn) then L__M(:ListMiscellany, 10, o); parenth_flag = true end
875
+ end
876
+
877
+ if o.has? :container
878
+ if o.has? :openable
879
+ if parenth_flag then print AND__TX
880
+ else L__M(:ListMiscellany, 11, o)
881
+ end
882
+ if o.has? :open
883
+ if child(o) then L__M(:ListMiscellany, 12, o)
884
+ else L__M(:ListMiscellany, 13, o)
885
+ end
886
+ else
887
+ if o.has?(:lockable) && o.has?(:locked) then L__M(:ListMiscellany, 15, o)
888
+ else L__M(:ListMiscellany, 14, o)
889
+ end
890
+ end
891
+ parenth_flag = true
892
+ else
893
+ if child(o) == 0 && o.has?(:transparent)
894
+ if parenth_flag then L__M(:ListMiscellany, 16, o)
895
+ else L__M(:ListMiscellany, 17, o)
896
+ end
897
+ end
898
+ end
899
+ end
900
+
901
+ print ")" if parenth_flag
902
+ end
903
+ # end of FULLINV_BIT processing
904
+
905
+ if (c_style & CONCEAL_BIT) != 0
906
+ child_count = 0
907
+ o.objectloop do |p|
908
+ if p.hasnt?(:concealed) && p.hasnt?(:scenery) then child_count += 1; eldest_child = p; end
909
+ end
910
+ else
911
+ child_count = children(o); eldest_child = child(o)
912
+ end
913
+
914
+ if child_count != 0 && (c_style & ALWAYS_BIT) != 0
915
+ L__M(:ListMiscellany, 18, o) if (c_style & ENGLISH_BIT) != 0
916
+ recurse_flag = true
917
+ end
918
+
919
+ if child_count != 0 && (c_style & RECURSE_BIT) != 0
920
+ if o.has? :supporter
921
+ if (c_style & ENGLISH_BIT) != 0
922
+ if (c_style & TERSE_BIT) != 0 then L__M(:ListMiscellany, 19, o)
923
+ else L__M(:ListMiscellany, 20, o)
924
+ end
925
+ if o.has? :animate then print WHOM__TX
926
+ else print WHICH__TX
927
+ end
928
+ end
929
+ recurse_flag = true
930
+ end
931
+ if o.has?(:container) && (o.has?(:open) || o.has?(:transparent))
932
+ if (c_style & ENGLISH_BIT) != 0
933
+ if (c_style & TERSE_BIT) != 0 then L__M(:ListMiscellany, 21, o)
934
+ else L__M(:ListMiscellany, 22, o)
935
+ end
936
+ if o.has? :animate then print WHOM__TX
937
+ else print WHICH__TX
938
+ end
939
+ end
940
+ recurse_flag = true
941
+ end
942
+ end
943
+
944
+ if recurse_flag && (c_style & ENGLISH_BIT) != 0
945
+ if child_count > 1 || (eldest_child && eldest_child.has?(:pluralname)) then print ARE2__TX
946
+ else print IS2__TX
947
+ end
948
+ end
949
+
950
+ new_line if (c_style & NEWLINE_BIT) != 0
951
+
952
+ return unless recurse_flag
953
+
954
+ o = child(o)
955
+ # if defined? TARGET_ZCODE
956
+ # push lt_value; @push listing_together; @push listing_size
957
+ # else # defined? TARGET_GLULX
958
+ # copy lt_value, sp; copy listing_together, sp; copy listing_size, sp
959
+ # end
960
+ # # TARGET_ZCODE
961
+
962
+ # @lt_value = 0; @listing_together = 0; @listing_size = 0
963
+ WriteListR(o, depth + 1, stack_p,
964
+ c_style, lt_value = nil,
965
+ listing_together = nil, listing_size = 0, &query)
966
+ # if defined? TARGET_ZCODE
967
+ # pull listing_size; pull listing_together; pull lt_value
968
+ # else # defined? TARGET_GLULX
969
+ # copy sp, listing_size; copy sp, listing_together; copy sp, lt_value
970
+ # end
971
+ # # TARGET_ZCODE
972
+ print ")" if (c_style & TERSE_BIT) != 0
973
+ end
974
+ # def WriteAfterEntry
975
+
976
+ # ----------------------------------------------------------------------------
977
+ # A cunning routine (which could have been a daemon, but isn't, for the
978
+ # sake of efficiency) to move objects which could be in many rooms about
979
+ # so that the player never catches one not in place
980
+ # ----------------------------------------------------------------------------
981
+
982
+ def MoveFloatingObjects
983
+ return if @location.nil? || @location == @player
984
+ flag = false
985
+ objectloop do |i|
986
+ address = i.&(:found_in)
987
+ if !address.nil? && i.hasnt?(:absent) && !IndirectlyContains(player, i)
988
+ if ZRegion(address) == 2
989
+ if !i.&(:found_in).nil? then move i, @location
990
+ else remove i
991
+ end
992
+ else
993
+ i.&(:found_in).each do |m|
994
+ if m == @location || m.in?(@location)
995
+ move i, @location if i.notin? @location
996
+ flag = true
997
+ end
998
+ end
999
+ remove i if flag == false && !parent(i).nil?
1000
+ end
1001
+ end
1002
+ end
1003
+ end
1004
+
1005
+ # ----------------------------------------------------------------------------
1006
+ # Two little routines for moving the player safely.
1007
+ # ----------------------------------------------------------------------------
1008
+
1009
+ def PlayerTo(newplace, flag = 0)
1010
+ move @player, newplace
1011
+ loop do
1012
+ parent_newplace = parent(newplace)
1013
+ break if parent_newplace.nil?
1014
+ newplace = parent_newplace
1015
+ end
1016
+ @location = newplace
1017
+ @real_location = @location; MoveFloatingObjects()
1018
+ AdjustLight(true)
1019
+ _invoke :Look if flag == 0
1020
+ if flag == 1 then NoteArrival(); ScoreArrival(); end
1021
+ LookSub(true) if flag == 2
1022
+ end
1023
+
1024
+ def MovePlayer(direc); _invoke :Go, direc; _invoke :Look; end
1025
+
1026
+ # ----------------------------------------------------------------------------
1027
+ # The handy YesOrNo routine, and some "meta" verbs
1028
+ # ----------------------------------------------------------------------------
1029
+
1030
+ # TODO: Figure out how this could work in a non-blocking IO system. FIXME
1031
+ # The read method would have to create an evented future promise or
1032
+ # something. That seems like it would cause this code to block on the
1033
+ # read command, and avoiding such blocking behavior was the entire point
1034
+ # of using Netty.
1035
+ def YesOrNo
1036
+ loop do
1037
+ @input = Inform::Runtime.instance.read
1038
+ DrawStatusLine() if !location.nil? && !parent(@player).nil?
1039
+ j = @input
1040
+ unless @input.empty? # at least one word entered
1041
+ i = @input.split.first
1042
+ return true if [YES1__WD, YES2__WD, YES3__WD].include?(i)
1043
+ return false if [NO1__WD, NO2__WD, NO3__WD].include?(i)
1044
+ end
1045
+ L__M(:Quit, 1); print "> "
1046
+ end
1047
+ end
1048
+
1049
+ # TODO: How could this work in a non-blocking IO system? FIXME
1050
+ def QuitSub
1051
+ L__M(:Quit, 2)
1052
+ quit if YesOrNo()
1053
+ end
1054
+
1055
+ def NotifyOnSub; notify_mode = 1; L__M(:NotifyOn); end
1056
+ def NotifyOffSub; notify_mode = 0; L__M(:NotifyOff); end
1057
+
1058
+ def Places1Sub
1059
+ j = 0
1060
+ k = 0
1061
+ L__M(:Places, 1)
1062
+ objectloop { |i| j += 1 if i.has?(:visited) }
1063
+ objectloop do |i|
1064
+ if i.has? :visited
1065
+ print i.name; k += 1
1066
+ if k == j then L__M(:Places, 2); return; end
1067
+ if k == j - 1 then print AND__TX
1068
+ else print COMMA__TX
1069
+ end
1070
+ end
1071
+ end
1072
+ end
1073
+
1074
+ # def Objects1Sub
1075
+ # j = 0
1076
+ # L__M(:Objects, 1)
1077
+ # objectloop do |i|
1078
+ # if i.has? :moved
1079
+ # f = 1; print the(i); j = parent(i)
1080
+ # unless j.nil?
1081
+ # if j == player
1082
+ # if i.has?(:worn) L__M(:Objects, 3)
1083
+ # else L__M(:Objects, 4)
1084
+ # end
1085
+ # new_line
1086
+ # break
1087
+ # end
1088
+ # if j.has?(:animate) then L__M(:Objects, 5); new_line; break; end
1089
+ # if j.has?(:visited) then L__M(:Objects, 6, j); new_line; break; end
1090
+ # if j.has?(:container) then L__M(:Objects, 8, j); new_line; break; end
1091
+ # if j.has?(:supporter) then L__M(:Objects, 9, j); new_line; break; end
1092
+ # if j.has?(:enterable) then L__M(:Objects, 7, j); new_line; break; end
1093
+ # end
1094
+ # end
1095
+ # L__M(:Objects, 10)
1096
+ # new_line
1097
+ # end
1098
+ # if (f == 0) L__M(##Objects, 2);
1099
+ # end
1100
+
1101
+ # See: metaverbs.rb
1102
+
1103
+ # ----------------------------------------------------------------------------
1104
+ # The scoring system
1105
+ # ----------------------------------------------------------------------------
1106
+
1107
+ # See: metaverbs.rb
1108
+
1109
+ # ----------------------------------------------------------------------------
1110
+ # Real verbs start here: Inventory
1111
+ # ----------------------------------------------------------------------------
1112
+
1113
+ def InvWideSub
1114
+ @inventory_style = ENGLISH_BIT + RECURSE_BIT + FULLINV_BIT
1115
+ invoke :Inv
1116
+ end
1117
+
1118
+ def InvTallSub
1119
+ @inventory_style = NEWLINE_BIT + RECURSE_BIT + INDENT_BIT + FULLINV_BIT
1120
+ invoke :Inv
1121
+ end
1122
+
1123
+ def InvSub
1124
+ return L__M(:Inv, 1) unless child(@player)
1125
+ return InvTallSub() if @inventory_style == 0
1126
+
1127
+ L__M(:Inv, 2)
1128
+ if (@inventory_style & NEWLINE_BIT) != 0 then L__M(:Inv, 3) else print " " end
1129
+
1130
+ WriteListFrom(child(@player), @inventory_style, 1)
1131
+ L__M(:Inv, 4) if (@inventory_style & ENGLISH_BIT) != 0
1132
+
1133
+ unless defined? MANUAL_PRONOUNS
1134
+ @player.objectloop { |x| PronounNotice(x) }
1135
+ end
1136
+
1137
+ AfterRoutines()
1138
+ end
1139
+
1140
+ # ----------------------------------------------------------------------------
1141
+ # The object tree and determining the possibility of moves
1142
+ # ----------------------------------------------------------------------------
1143
+
1144
+ def CommonAncestor(o1, o2)
1145
+ # Find the nearest object indirectly containing o1 and o2,
1146
+ # or return nil if there is no common ancestor.
1147
+ i = o1
1148
+ loop do
1149
+ break if i.nil?
1150
+ j = o2
1151
+ loop do
1152
+ break if j.nil?
1153
+ return i if j == i
1154
+ j = j.parent
1155
+ end
1156
+ i = i.parent
1157
+ end
1158
+ return nil
1159
+ end
1160
+
1161
+ def IndirectlyContains(o1, o2)
1162
+ return true if o1 == o2
1163
+ # Does o1 indirectly contain o2? (Same as testing if their common ancestor is o1.)
1164
+ return o1.descendants.include?(o2) # TODO: FIXME
1165
+ end
1166
+
1167
+ def ObjectScopedBySomething(item)
1168
+ i = item; return false unless i
1169
+ Inform::Object.each do |j|
1170
+ next unless j.respond_to?(:add_to_scope)
1171
+ return j if j.add_to_scope.include?(j)
1172
+ end
1173
+ return false
1174
+ end
1175
+
1176
+ def ObjectIsUntouchable(item, flag1 = nil, flag2 = nil, persona = nil)
1177
+ # Determine if there's any barrier preventing the player from moving
1178
+ # things to "item". Return false if no barrier; otherwise print a
1179
+ # suitable message and return true.
1180
+ # If flag1 is set, do not print any message.
1181
+ # If flag2 is set, also apply Take/Remove restrictions.
1182
+ # If persona is set, use that rather than the player.
1183
+
1184
+ flag1 = @player if flag1.nil?
1185
+ flag2 = @player if flag2.nil?
1186
+ persona = @player if persona.nil?
1187
+
1188
+ # If the item has been added to scope by something, it's first necessary
1189
+ # for that something to be touchable.
1190
+
1191
+ ancestor = CommonAncestor(persona, item)
1192
+ if ancestor.nil?
1193
+ ancestor = item
1194
+ loop do
1195
+ break unless ancestor && (i = ObjectScopedBySomething(ancestor)).nil?
1196
+ ancestor = parent(ancestor)
1197
+ end
1198
+ unless i.nil?
1199
+ return false
1200
+ # return false if ObjectIsUntouchable(i, flag1, flag2) # TODO: FIXME
1201
+ # An item immediately added to scope
1202
+ end
1203
+ else
1204
+
1205
+ # First, a barrier between the persona and the ancestor. The persona
1206
+ # can only be in a sequence of enterable objects, and only closed
1207
+ # containers form a barrier.
1208
+
1209
+ if persona != ancestor
1210
+ i = parent(persona)
1211
+ loop do
1212
+ break unless i != ancestor
1213
+ if i.has?(:container) && i.hasnt?(:open)
1214
+ return true if flag1
1215
+ return L__M(:Take, 9, i)
1216
+ end
1217
+ i = parent(i)
1218
+ end
1219
+ end
1220
+
1221
+ # Second, a barrier between the item and the ancestor. The item can
1222
+ # be carried by someone, part of a piece of machinery, in or on top
1223
+ # of something and so on.
1224
+
1225
+ if item != ancestor
1226
+ i = parent(item)
1227
+ loop do
1228
+ break unless !i.nil? && i != ancestor
1229
+ if flag2 && i.hasnt?(:container, :supporter) # && i.hasnt?(:supporter)
1230
+ if i.has?(:animate)
1231
+ return true if flag1
1232
+ return L__M(:Take, 6, i)
1233
+ end
1234
+ if i.has?(:transparent)
1235
+ return true if flag1
1236
+ return L__M(:Take, 7, i)
1237
+ end
1238
+ return true if flag1
1239
+ return L__M(:Take, 8, item)
1240
+ end
1241
+ if !i.nil? && i.has?(:container) && i.hasnt?(:open)
1242
+ return true if flag1
1243
+ return L__M(:Take, 9, i)
1244
+ end
1245
+ i = parent(i)
1246
+ end
1247
+ end
1248
+ end
1249
+ # if ancestor.nil?
1250
+
1251
+ return false
1252
+ end
1253
+ # def ObjectIsUntouchable
1254
+
1255
+ def AttemptToTakeObject(item)
1256
+ # Try to transfer the given item to the player: return false
1257
+ # if successful, true if unsuccessful, printing a suitable message
1258
+ # in the latter case.
1259
+ # People cannot ordinarily be taken.
1260
+ if item == @player then println L__M(:Take, 2); return true; end
1261
+ if item.has? :animate then println L__M(:Take, 3, item); return true; end
1262
+
1263
+ ancestor = CommonAncestor(@player, item)
1264
+
1265
+ if ancestor.nil?
1266
+ i = ObjectScopedBySomething(item)
1267
+ ancestor = CommonAncestor(@player, i) if i
1268
+ end
1269
+
1270
+ # Is the player indirectly inside the item?
1271
+ return L__M(:Take, 4, item) if ancestor == item
1272
+
1273
+ # Does the player already directly contain the item?
1274
+ return L__M(:Take, 5, item) if item.in? @player
1275
+
1276
+ # Can the player touch the item, or is there (e.g.) a closed container
1277
+ # in the way?
1278
+ return true if ObjectIsUntouchable(item, false, true)
1279
+
1280
+ # The item is now known to be accessible.
1281
+
1282
+ # Consult the immediate possessor of the item, if it's in a container
1283
+ # which the player is not in.
1284
+
1285
+ i = parent(item)
1286
+ if i != ancestor && i.hasany?(:container, :supporter)
1287
+ after_recipient = i
1288
+ k = @action; @action = :LetGo
1289
+ j = RunRoutines(i, :before)
1290
+ @action = k
1291
+ return true if j
1292
+ end
1293
+
1294
+ if item.has? :scenery then println L__M(:Take, 10, item); return true; end
1295
+ if item.has? :static then println L__M(:Take, 11, item); return true; end
1296
+
1297
+ # The item is now known to be available for taking. Is the player
1298
+ # carrying too much? If so, possibly juggle items into the rucksack
1299
+ # to make room.
1300
+
1301
+ k = 0; @player.objectloop { |j2| k += 1 if j2.hasnt? :worn }
1302
+
1303
+ if ValueOrRun(@player, :capacity) < k
1304
+ # TODO: FIXME
1305
+ # return L__M(:Take, 12) if SACK_OBJECT.nil?
1306
+ #
1307
+ # if SACK_OBJECT.parent != @player
1308
+ # return L__M(:Take, 12)
1309
+ # end
1310
+ # j = nil
1311
+ # @player.objectloop { |k|
1312
+ # if k != SACK_OBJECT && k.hasnt?(:worn) && k.hasnt?(:light); j = k; end
1313
+ # }
1314
+ #
1315
+ # if !j.nil?
1316
+ # L__M(:Take, 13, j)
1317
+ # @keep_silent = true; Insert j, SACK_OBJECT; keep_silent = false
1318
+ # return true if !j < SACK_OBJECT
1319
+ # end
1320
+ #
1321
+ # return L__M(:Take, 12)
1322
+ end
1323
+
1324
+ # Transfer the item.
1325
+
1326
+ @player.publish L__M(:Take, 15, noun)
1327
+ move item, @player
1328
+
1329
+ # Send "after" message to the object letting go of the item, if any.
1330
+
1331
+ unless after_recipient.nil?
1332
+ k = @action; @action = :LetGo
1333
+ j = RunRoutines(after_recipient, :after)
1334
+ @action = k
1335
+ return true if j
1336
+ end
1337
+ return false
1338
+ end
1339
+
1340
+ # ----------------------------------------------------------------------------
1341
+ # Object movement verbs
1342
+ # ----------------------------------------------------------------------------
1343
+
1344
+ def TakeSub
1345
+ i = parent(noun)
1346
+ if noun.notin?(@player) || !@onotheld_mode
1347
+ return true if AttemptToTakeObject(noun)
1348
+ end
1349
+ return true if AfterRoutines()
1350
+ @notheld_mode = @onotheld_mode
1351
+ return true if @notheld_mode || @keep_silent
1352
+ L__M(:Take, 1)
1353
+ end
1354
+
1355
+ def RemoveSub
1356
+ i = parent(noun)
1357
+ return L__M(:Remove, 1, noun) if i.has?(:container) && i.hasnt?(:open)
1358
+ return L__M(:Remove, 2, noun) if i != second
1359
+ return L__M(:Take, 6, i) if i.has? :animate
1360
+ return true if AttemptToTakeObject(noun)
1361
+ @action = :Remove; return true if AfterRoutines()
1362
+ @action = :Take; return true if AfterRoutines()
1363
+ return true if @keep_silent
1364
+ second = i if second.nil? || i == @location # TODO: FIXME
1365
+ return L__M(:Remove, 3, noun)
1366
+ end
1367
+
1368
+ def DropSub
1369
+ return L__M(:PutOn, 4) if noun == @player
1370
+ return L__M(:Drop, 1, noun) if noun.in? @player.parent
1371
+ return L__M(:Drop, 2, noun) if noun.notin? @player
1372
+ if noun.has? :worn
1373
+ L__M(:Drop, 3, noun)
1374
+ disrobe noun
1375
+ return true if noun.in?(@player) && noun.has?(:worn)
1376
+ end
1377
+ move noun, @player.parent
1378
+ return true if AfterRoutines()
1379
+ return true if @keep_silent
1380
+ @player.publish L__M(:Drop, 5, noun)
1381
+ return L__M(:Drop, 4, noun)
1382
+ end
1383
+
1384
+ def PutOnSub
1385
+ @receive_action = :PutOn
1386
+ # return drop noun end if second == $d_obj || @player.in?(second) # TODO: FIXME
1387
+ return invoke(:Drop, noun) if second == $d_obj || @player.in?(second)
1388
+ return L__M(:Drop, 1, noun) if parent(noun) == second
1389
+ return L__M(:PutOn, 1, noun) if parent(noun) != @player
1390
+
1391
+ ancestor = CommonAncestor(noun, second)
1392
+ return L__M(:PutOn, 2, noun) if ancestor == noun
1393
+ return if ObjectIsUntouchable(second)
1394
+
1395
+ if second != ancestor
1396
+ @action = :Receive
1397
+ if RunRoutines(second, :before) then @action = :PutOn; return; end
1398
+ @action = :PutOn
1399
+ end
1400
+ return L__M(:PutOn, 3, second) if second.hasnt? :supporter
1401
+ return L__M(:PutOn, 4) if ancestor == @player
1402
+ if noun.has? :worn
1403
+ L__M(:PutOn, 5, noun); disrobe noun; return if noun.has? :worn
1404
+ end
1405
+
1406
+ if children(second) >= ValueOrRun(second, :capacity)
1407
+ return L__M(:PutOn, 6, second)
1408
+ end
1409
+
1410
+ move noun, second
1411
+
1412
+ return if AfterRoutines()
1413
+
1414
+ if second != ancestor
1415
+ @action = :Receive
1416
+ if RunRoutines(second, :after) then @action = :PutOn; return true; end
1417
+ @action = :PutOn
1418
+ end
1419
+ return if @keep_silent
1420
+ @player.publish L__M(:PutOn, 9, noun)
1421
+ return L__M(:PutOn, 7) if @multiflag
1422
+ L__M(:PutOn, 8, noun)
1423
+ end
1424
+
1425
+ def InsertSub
1426
+ @receive_action = :Insert
1427
+ return drop noun if second == $d_obj || @player.in?(second)
1428
+ return L__M(:Drop, 1, noun) if noun.parent == second
1429
+ return L__M(:Insert, 1, noun) if noun.parent != @player
1430
+
1431
+ ancestor = CommonAncestor(noun, second)
1432
+ return L__M(:Insert, 5, noun) if ancestor == noun
1433
+ return if ObjectIsUntouchable(second)
1434
+
1435
+ if second != ancestor
1436
+ @action = :Receive
1437
+ if RunRoutines(second, :before) then @action = :Insert; return true; end
1438
+ @action = :Insert
1439
+ if second.has?(:container) && second.hasnt?(:open)
1440
+ return L__M(:Insert, 3, second)
1441
+ end
1442
+ end
1443
+ return L__M(:Insert, 2, second) if second.hasnt? :container
1444
+
1445
+ if noun.has? :worn
1446
+ L__M(:Insert, 6, noun); disrobe noun; return if noun.has? :worn
1447
+ end
1448
+
1449
+ if children(second) >= ValueOrRun(second, :capacity)
1450
+ return L__M(:Insert, 7, second)
1451
+ end
1452
+
1453
+ move noun, second
1454
+
1455
+ return true if AfterRoutines()
1456
+
1457
+ if second != ancestor
1458
+ @action = :Receive
1459
+ if RunRoutines(second, :after) then @action = :Insert; return true; end
1460
+ @action = :Insert
1461
+ end
1462
+ return true if @keep_silent
1463
+ @player.publish L__M(:Insert, 10, noun)
1464
+ return L__M(:Insert, 8, noun) if @multiflag
1465
+ L__M(:Insert, 9, noun)
1466
+ end
1467
+
1468
+ # ----------------------------------------------------------------------------
1469
+ # Empties and transfers are routed through the actions above
1470
+ # ----------------------------------------------------------------------------
1471
+
1472
+ def TransferSub
1473
+ return if noun.notin?(@player) && AttemptToTakeObject(noun)
1474
+ invoke :PutOn, noun, second if second.has? :supporter
1475
+ invoke :Drop, noun if second == $d_obj
1476
+ invoke :Insert, noun, second
1477
+ end
1478
+
1479
+ def EmptySub; @second = $d_obj; EmptyTSub(); end
1480
+
1481
+ def EmptyTSub
1482
+ return L__M(:EmptyT, 4) if noun == second
1483
+ return if ObjectIsUntouchable(noun)
1484
+ return L__M(:EmptyT, 1, noun) if noun.hasnt? :container
1485
+ return L__M(:EmptyT, 2, noun) if noun.hasnt? :open
1486
+ if second != $d_obj # && second != parent(@player)
1487
+ if second.hasnt? :supporter
1488
+ return L__M(:EmptyT, 1, second) if second.hasnt? :container
1489
+ return L__M(:EmptyT, 2, second) if second.hasnt? :open
1490
+ end
1491
+ end
1492
+ i = child(noun); k = children(noun)
1493
+ return L__M(:EmptyT, 3, noun) unless i
1494
+ loop do
1495
+ break if i.nil?
1496
+ j = i.sibling
1497
+ flag = false
1498
+ flag = true if ObjectIsUntouchable(noun)
1499
+ flag = true if noun.hasnt? :container
1500
+ flag = true if noun.hasnt? :open
1501
+ if second != $d_obj
1502
+ if second.hasnt? :supporter
1503
+ flag = true if second.hasnt? :container
1504
+ flag = true if second.hasnt? :open
1505
+ end
1506
+ end
1507
+ flag = true if k == 0
1508
+ k -= 1
1509
+ break if flag
1510
+ print i + ": " unless @keep_silent
1511
+ _invoke :Transfer, i, second
1512
+ i = j
1513
+ end
1514
+ end
1515
+
1516
+ # ----------------------------------------------------------------------------
1517
+ # Gifts
1518
+ # ----------------------------------------------------------------------------
1519
+
1520
+ def GiveSub
1521
+ return L__M(:Give, 1, noun) if parent(noun) != @player
1522
+ if second == @player
1523
+ @player.publish A(player) + " juggles " + a(noun) + " for a second or two."
1524
+ return L__M(:Give, 2, noun)
1525
+ end
1526
+ return false if RunLife(second, :Give)
1527
+ L__M(:Give, 3, second)
1528
+ end
1529
+
1530
+ def GiveRSub; _invoke :Give, second, noun; end
1531
+
1532
+ def ShowSub
1533
+ return L__M(:Show, 1, noun) if parent(noun) != @player
1534
+ _invoke :Examine, noun if second == @player
1535
+ return false if RunLife(second, :Show)
1536
+ L__M(:Show, 2, second)
1537
+ end
1538
+
1539
+ def ShowRSub; _invoke :Show, second, noun; end
1540
+
1541
+ # ----------------------------------------------------------------------------
1542
+ # Travelling around verbs
1543
+ # ----------------------------------------------------------------------------
1544
+
1545
+ def EnterSub
1546
+ # return go noun if noun.in?(Compass) || noun.has?(:door) # TODO: FIXME
1547
+ return invoke(:Go, noun) if noun.in?(Compass) || noun.has?(:door)
1548
+
1549
+ return L__M(:Enter, 1, noun) if @player.in? noun
1550
+ return L__M(:Enter, 2, noun) if noun.hasnt? :enterable
1551
+ return L__M(:Enter, 3, noun) if noun.has?(:container) && noun.hasnt?(:open)
1552
+
1553
+ if parent(@player) != parent(noun)
1554
+ ancestor = CommonAncestor(@player, noun)
1555
+ return L__M(:Enter, 4, noun) if ancestor == @player || ancestor.nil?
1556
+ loop do
1557
+ break if @player.in?(ancestor)
1558
+ j = parent(@player)
1559
+ k = @keep_silent
1560
+ if parent(j) != ancestor || noun != ancestor
1561
+ L__M(:Enter, 6, j)
1562
+ @keep_silent = true
1563
+ end
1564
+ _invoke :Exit
1565
+ @keep_silent = k
1566
+ return if @player.in?(j)
1567
+ end
1568
+ return if @player.in?(noun)
1569
+ if noun.notin?(ancestor)
1570
+ j = parent(noun)
1571
+ loop do
1572
+ break if parent(j) == ancestor
1573
+ j = parent(j)
1574
+ end
1575
+ L__M(:Enter, 7, j)
1576
+ _invoke :Enter, j
1577
+ return if @player.notin?(j)
1578
+ return invoke(:Enter, noun)
1579
+ end
1580
+ end
1581
+
1582
+ move @player, noun
1583
+ return true if AfterRoutines()
1584
+ return true if @keep_silent
1585
+ Locale(noun)
1586
+ @player.publish L__M(:Enter, 8, noun)
1587
+ L__M(:Enter, 5, noun)
1588
+ end
1589
+
1590
+ def GetOffSub
1591
+ _invoke :Exit if parent(@player) == noun
1592
+ L__M(:GetOff, 1, noun)
1593
+ end
1594
+
1595
+ def ExitSub
1596
+ parent_obj = parent(@player)
1597
+ return L__M(:Exit, 4, noun) if !noun.nil? && noun != parent_obj
1598
+ if parent_obj == @location || (@location == thedark && parent_obj == @real_location)
1599
+ if @location.&(:out_to) || (@location == thedark && @real_location.&(:out_to))
1600
+ invoke :Go, $out_obj
1601
+ end
1602
+ return L__M(:Exit, 1)
1603
+ end
1604
+ return L__M(:Exit, 2, parent_obj) if parent_obj.has?(:container) && parent_obj.hasnt?(:open)
1605
+
1606
+ move @player, parent(parent_obj)
1607
+ stand @player
1608
+
1609
+ return true if AfterRoutines()
1610
+ return true if @keep_silent
1611
+ @player.publish L__M(:Exit, 5, parent_obj)
1612
+ L__M(:Exit, 3, parent_obj)
1613
+ LookSub(true)
1614
+ end
1615
+
1616
+ def VagueGoSub; L__M(:VagueGo); end
1617
+
1618
+ def GoInSub; go $in_obj; end
1619
+
1620
+ def GoSub
1621
+ i = @player.parent
1622
+ return RunTimeError(10) if i.nil?
1623
+ RunRoutines(@player, :before) # Attempt to allow for route recollection TODO: Evaluate if necessary
1624
+
1625
+ # first, check if any PushDir object is touchable
1626
+ return if !second.nil? && !second.in?(Compass) && ObjectIsUntouchable(second)
1627
+
1628
+ old_loc = @location
1629
+ movewith = nil
1630
+ if (@location != thedark && i != @location) || (@location == thedark && i != @real_location)
1631
+ j = @location
1632
+ @location = @real_location if @location == thedark
1633
+ k = RunRoutines(i, :before); @location = j if k != 3
1634
+ if k
1635
+ movewith = i; i = i.parent
1636
+ else
1637
+ L__M(:Go,1,i) unless k
1638
+ return true
1639
+ end
1640
+ end
1641
+
1642
+ return L__M(:Go, 6, noun) if noun.door_dir.nil?
1643
+
1644
+ thedir = noun.door_dir
1645
+
1646
+ j = i.&(thedir)
1647
+ return true if j == true
1648
+ if j && j.is_a?(String) then print j.to_s; new_line; return false; end
1649
+
1650
+ unless j
1651
+ if i.respond_to?(:cant_go) && i.cant_go != CANTGO__TX then PrintOrRun(i, :cant_go)
1652
+ else L__M(:Go,2)
1653
+ end
1654
+ return false
1655
+ end
1656
+
1657
+ if j.has? :door
1658
+ return L__M(:Go, 2) if j.has? :concealed
1659
+ if j.hasnt? :open
1660
+ return L__M(:Go, 3, j) if noun == $u_obj
1661
+ return L__M(:Go, 4, j) if noun == $d_obj
1662
+ return L__M(:Go, 5, j)
1663
+ end
1664
+ k = RunRoutines(j,:door_to)
1665
+ if k == false then return L__M(:Go, 6, j) end
1666
+ if k == true then return true end
1667
+ j = k
1668
+ end
1669
+
1670
+ orig_loc = @real_location
1671
+ @player.publish L__M(:Go, 7, noun) unless @keep_silent
1672
+
1673
+ if movewith.nil? then move @player, j else move movewith, j end
1674
+
1675
+ light_adjusted @player
1676
+
1677
+ # Special addition for ensuring location is never forgotten after player logout.
1678
+ @player.link :location, j
1679
+ @player.descendants.each do |o|
1680
+ o.link :location, j if o.linked? :location
1681
+ end
1682
+ movewith.link :location, j if !movewith.nil? && movewith.linked?(:location)
1683
+
1684
+ @location = j; MoveFloatingObjects();
1685
+ if OffersLight(j)
1686
+ @location = j; @real_location = j; @lightflag = true
1687
+ else
1688
+ if old_loc == thedark
1689
+ DarkToDark()
1690
+ return true if @deadflag
1691
+ end
1692
+ @real_location = j
1693
+ @location = thedark; @lightflag = false
1694
+ end
1695
+
1696
+ @player.publish L__M(:Go, 8, ArrivalDir(j, orig_loc)) unless @keep_silent
1697
+
1698
+ return true if AfterRoutines()
1699
+ return true if @keep_silent
1700
+ LookSub(true)
1701
+ end
1702
+
1703
+ def ArrivalDir(dest, source = @real_location)
1704
+ link = dest.links.find { |l| l.to == source }
1705
+ return nil if link.nil?
1706
+ # TODO: Doesn't work for door objects FIXME
1707
+ direc = Compass.find { |dir| dir.door_dir == link.name.to_sym }
1708
+ end
1709
+
1710
+ # ----------------------------------------------------------------------------
1711
+ # Describing the world. SayWhatsOn(object) does just that (producing
1712
+ # no text if nothing except possibly "scenery" and "concealed" items are).
1713
+ # Locale(object) runs through the "tail end" of a Look-style room
1714
+ # description for the contents of the object, printing up suitable
1715
+ # descriptions as it goes.
1716
+ # ----------------------------------------------------------------------------
1717
+
1718
+ def SayWhatsOn(descon)
1719
+ return false if descon == parent(@player)
1720
+ f = descon.any? { |j| j.hasnt?(:concealed) && j.hasnt?(:scenery) }
1721
+ return false unless f
1722
+ println L__M(:Look, 4, descon)
1723
+ end
1724
+
1725
+ def NotSupportingThePlayer(o)
1726
+ i = parent(@player)
1727
+ loop do
1728
+ break if i.nil? || i == @visibility_ceiling
1729
+ return false if i == o
1730
+ i = parent(i)
1731
+ return true if !i.nil? && i.hasnt?(:supporter)
1732
+ end
1733
+ true
1734
+ end
1735
+
1736
+ def Locale(descin, text1 = nil, text2 = nil, flag = false)
1737
+ descin.each { |o| take o, :workflag }
1738
+ k = 0
1739
+ n = 0
1740
+ descin.each do |obj|
1741
+ # Only doing this because the player may no longer be concealed if
1742
+ # the player is to be visible to other players.
1743
+ next if obj == @player || obj.has?(:concealed)
1744
+
1745
+ next unless obj.hasnt?(:concealed) && NotSupportingThePlayer(obj)
1746
+ unless defined? MANUAL_PRONOUNS
1747
+ PronounNotice(obj)
1748
+ end
1749
+
1750
+ give obj, :workflag
1751
+ k += 1
1752
+ property = :initial; f2 = 0
1753
+
1754
+ if obj.hasnt? :scenery
1755
+ if (obj.has?(:door) || obj.has?(:container)) && obj.has?(:open) && obj.respond_to?(:when_open)
1756
+ property = :when_open; f2 = 1
1757
+ elsif (obj.has?(:door) || obj.has?(:container)) && obj.hasnt?(:open) && obj.respond_to?(:when_closed)
1758
+ property = :when_closed; f2 = 1
1759
+ elsif obj.has?(:switchable) && obj.has?(:on) && obj.respond_to?(:when_on)
1760
+ property = :when_on; f2 = 1
1761
+ elsif obj.has?(:switchable) && obj.hasnt?(:on) && obj.respond_to?(:when_off)
1762
+ property = :when_off; f2 = 1
1763
+ end
1764
+
1765
+ if obj.hasnt?(:moved) || obj.respond_to?(:describe) || f2 == 1
1766
+ if obj.respond_to?(:describe) && RunRoutines(obj, :describe)
1767
+ flag = true
1768
+ take obj, :workflag; k -= 1
1769
+ else
1770
+ j = obj.&property
1771
+ unless j.nil?
1772
+ new_line
1773
+ PrintOrRun(obj, property)
1774
+ flag = true
1775
+ take obj, :workflag; k -= 1
1776
+ SayWhatsOn(obj) if obj.has?(:supporter) && !child(obj).nil?
1777
+ end
1778
+ end
1779
+ end
1780
+ elsif obj.has?(:supporter) && !child(obj).nil?
1781
+ SayWhatsOn(obj)
1782
+ end
1783
+ end
1784
+ # descin.each do
1785
+
1786
+ return 0 if k == 0
1787
+
1788
+ if !text1.nil? && !text1.empty?
1789
+ new_line
1790
+ text1 = text2 if flag
1791
+ print text1 + " "
1792
+ WriteListFrom(child(descin), ENGLISH_BIT + RECURSE_BIT + PARTINV_BIT +
1793
+ TERSE_BIT + CONCEAL_BIT + WORKFLAG_BIT)
1794
+ return k + j
1795
+ end
1796
+
1797
+ if flag then println L__M(:Look, 5, descin)
1798
+ else println L__M(:Look, 6, descin)
1799
+ end
1800
+ end
1801
+
1802
+ # ----------------------------------------------------------------------------
1803
+ # Looking. LookSub(true) is allowed to abbreviate long descriptions, but
1804
+ # LookSub(false) (which is what happens when the Look action is generated)
1805
+ # isn't. (Except that these are over-ridden by the player-set lookmode.)
1806
+ # ----------------------------------------------------------------------------
1807
+
1808
+ def LMode1Sub; @lookmode = 1; print Story; L__M(:LMode1); end # Brief
1809
+
1810
+ def LMode2Sub; @lookmode = 2; print Story; L__M(:LMode2); end # Verbose
1811
+
1812
+ def LMode3Sub; @lookmode = 3; print Story; L__M(:LMode3); end # Superbrief
1813
+
1814
+ def ScoreArrival
1815
+ # unless @player.visited.include?(@location)
1816
+ # @player.visited.push @location
1817
+ # if @location.has? :scored
1818
+ # score = score + ROOM_SCORE
1819
+ # places_score = places_score + ROOM_SCORE
1820
+ # end
1821
+ # end
1822
+ end
1823
+
1824
+ def NoteArrival
1825
+ if @location == thedark
1826
+ @lastdesc = thedark
1827
+ return
1828
+ end
1829
+ return if @location == @lastdesc
1830
+ PrintOrRun(@location, :initial) if @location.respond_to? :initial
1831
+ descin = @location
1832
+ NewRoom()
1833
+ @lastdesc = descin
1834
+ end
1835
+
1836
+ def FindVisibilityLevels
1837
+ @visibility_levels = 1
1838
+ @visibility_ceiling = parent(@player)
1839
+ loop do
1840
+ break unless parent(@visibility_ceiling) &&
1841
+ (@visibility_ceiling.hasnt?(:container) || @visibility_ceiling.hasany?(:open, :transparent))
1842
+ @visibility_ceiling = parent(@visibility_ceiling)
1843
+ @visibility_levels += 1
1844
+ end
1845
+ @visibility_levels
1846
+ end
1847
+
1848
+ def LookSub(allow_abbrev = nil)
1849
+ allow_abbrev = false if allow_abbrev.nil?
1850
+ return RunTimeError(10) if parent(@player).nil?
1851
+
1852
+ if OffersLight(@real_location)
1853
+ @location = @real_location; @lightflag = true
1854
+ else
1855
+ @location = thedark; @lightflag = false
1856
+ end
1857
+
1858
+ if @location == thedark
1859
+ @visibility_levels = 0
1860
+ @visibility_ceiling = thedark
1861
+ NoteArrival()
1862
+ else
1863
+ @visibility_levels = FindVisibilityLevels()
1864
+ if @visibility_ceiling == @location
1865
+ NoteArrival()
1866
+ if @visibility_ceiling != @location
1867
+ # TODO: Remove or figure this out.
1868
+ log.warn "Not sure why this would ever happen"
1869
+ return "The visibility ceiling is no longer the same as the " +
1870
+ "location. This is probably a very serious error."
1871
+ end
1872
+ end
1873
+ end
1874
+
1875
+ # Printing the top line: e.g.
1876
+ # Octagonal Room (on the table) (as Frodo)
1877
+ new_line
1878
+ style :bold
1879
+ color :yellow
1880
+ if @visibility_levels == 0
1881
+ print DARKNESS__TX
1882
+ else
1883
+ if @visibility_ceiling != @location then print The(@visibility_ceiling)
1884
+ else print name(@visibility_ceiling)
1885
+ end
1886
+ end
1887
+ uncolor :yellow
1888
+ unstyle :bold
1889
+
1890
+ j = 1
1891
+ i = @player.parent
1892
+ loop do
1893
+ break unless j < @visibility_levels
1894
+ if i.has?(:supporter)
1895
+ L__M(:Look, 1, i)
1896
+ else
1897
+ L__M(:Look, 2, i)
1898
+ end
1899
+ j += 1
1900
+ i = i.parent
1901
+ end
1902
+
1903
+ L__M(:Look, 3, @player) if @print_player_flag
1904
+ L__M(:Look, 9, @player) if @player.respond_to?(:builder?) && @player.builder?
1905
+ new_line
1906
+
1907
+ # TODO: FIXME
1908
+ # if @location.outside?
1909
+ # # PrintOrRun(region, :description) if @player.prefers? :verbose_regions
1910
+ # # PrintOrRun(area, :description) if @player.prefers? :verbose_areas
1911
+ # end
1912
+
1913
+ # The room description (if visible)
1914
+
1915
+ if @lookmode < 3 && @visibility_ceiling == @location
1916
+ if allow_abbrev == false || @lookmode == 2 || !@player.visited.include?(@location)
1917
+ if @location.respond_to? :describe
1918
+ RunRoutines(@location, :describe)
1919
+ else
1920
+ if @location.respond_to?(:description)
1921
+ PrintOrRun(@location, :description)
1922
+ else
1923
+ RunTimeError(11, @location)
1924
+ end
1925
+ end
1926
+ end
1927
+ end
1928
+
1929
+ nl_flag = true if @visibility_ceiling == @location
1930
+
1931
+ if @visibility_levels == 0
1932
+ Locale(thedark)
1933
+ else
1934
+ i = @player
1935
+ j = @visibility_levels
1936
+ loop do
1937
+ break unless j > 0
1938
+ give i, :workflag
1939
+ i = parent(i)
1940
+ j -= 1
1941
+ end
1942
+
1943
+ j = @visibility_levels
1944
+ loop do
1945
+ break unless j > 0
1946
+ k = 0
1947
+ i = @player
1948
+ loop do
1949
+ break unless k < j
1950
+ i = parent(i)
1951
+ k += 1
1952
+ end
1953
+ if i.respond_to? :inside_description
1954
+ if nl_flag
1955
+ new_line
1956
+ else
1957
+ nl_flag = true
1958
+ end
1959
+ PrintOrRun(i, :inside_description)
1960
+ end
1961
+ nl_flag = true if Locale(i)
1962
+ j -= 1
1963
+ end
1964
+ end
1965
+
1966
+ LookRoutine()
1967
+ ScoreArrival()
1968
+
1969
+ @action = :Look
1970
+ return true if AfterRoutines()
1971
+ false
1972
+ end
1973
+
1974
+ def ExamineSub
1975
+ return L__M(:Examine, 1) if @location == thedark
1976
+ unless @keep_silent
1977
+ if noun.has? :animate
1978
+ @action = :LookAt
1979
+ if noun.in?(@player)
1980
+ @player.publish A(@player) + " looks at " + hisorher(@player, noun) + "."
1981
+ elsif noun != @player
1982
+ @player.publish A(@player) + " looks over at " + a(noun) + "."
1983
+ end
1984
+ else
1985
+ if noun.in?(@player)
1986
+ @player.publish A(@player) + " examines " + hisorher(@player, noun) + "."
1987
+ else
1988
+ @player.publish A(@player) + " examines " + a(noun) + "."
1989
+ end
1990
+ end
1991
+ end
1992
+ i = noun.description
1993
+ # if noun.values[:description].nil? && noun.properties[:description].nil? && noun.&(:description).nil?
1994
+ if i.nil?
1995
+ return _invoke :Search, noun if noun.has? :container
1996
+ if noun.has? :switchable then L__M(:Examine, 3, noun); return false; end
1997
+ return L__M(:Examine, 2, noun)
1998
+ end
1999
+ PrintOrRun(noun, :description)
2000
+ L__M(:Examine, 3, noun) if noun.has? :switchable
2001
+ return true if AfterRoutines()
2002
+ end
2003
+
2004
+ def LookUnderSub
2005
+ return L__M(:LookUnder, 1) if @location == thedark
2006
+ @player.publish L__M(:LookUnder, 3, noun)
2007
+ L__M(:LookUnder, 2)
2008
+ end
2009
+
2010
+ def VisibleContents(o)
2011
+ o ? o.count { |i| i != @player && i.hasnt?(:concealed, :scenery) } : 0
2012
+ end
2013
+
2014
+ def SearchSub
2015
+ return L__M(:Search, 1, noun) if @location == thedark
2016
+ return if ObjectIsUntouchable(noun)
2017
+ f = VisibleContents(noun)
2018
+ if noun.has? :supporter
2019
+ @player.publish L__M(:Search, 8, noun) unless @keep_silent
2020
+ return L__M(:Search, 2, noun) if f == 0
2021
+ return L__M(:Search, 3, noun)
2022
+ end
2023
+ return L__M(:Search, 4, noun) if noun.hasnt? :container
2024
+ return L__M(:Search, 5, noun) if noun.hasnt?(:transparent) && noun.hasnt?(:open)
2025
+ return true if AfterRoutines()
2026
+
2027
+ @player.publish L__M(:Search, 8, noun) unless @keep_silent
2028
+ return L__M(:Search, 6, noun) if f == 0
2029
+ L__M(:Search, 7, noun)
2030
+ end
2031
+
2032
+ # ----------------------------------------------------------------------------
2033
+ # Verbs which change the state of objects without moving them
2034
+ # ----------------------------------------------------------------------------
2035
+
2036
+ def UnlockSub
2037
+ return if ObjectIsUntouchable(noun)
2038
+ return L__M(:Unlock, 1, noun) if noun.hasnt? :lockable
2039
+ return L__M(:Unlock, 2, noun) if noun.hasnt? :locked
2040
+ return L__M(:Unlock, 3, second) if noun.&(:with_key) != second
2041
+ take noun, :locked
2042
+ return true if AfterRoutines()
2043
+ return true if @keep_silent
2044
+ @player.publish L__M(:Unlock, 5, noun)
2045
+ L__M(:Unlock, 4, noun)
2046
+ end
2047
+
2048
+ def LockSub
2049
+ return if ObjectIsUntouchable(noun)
2050
+ return L__M(:Lock, 1, noun) if noun.hasnt? :lockable
2051
+ return L__M(:Lock, 2, noun) if noun.has? :locked
2052
+ return L__M(:Lock, 3, noun) if noun.has? :open
2053
+ return L__M(:Lock, 4, second) if noun.&(:with_key) != second
2054
+ give noun, :locked
2055
+ return true if AfterRoutines()
2056
+ return true if @keep_silent
2057
+ @player.publish L__M(:Lock, 6, noun)
2058
+ L__M(:Lock, 5, noun)
2059
+ end
2060
+
2061
+ def SwitchonSub
2062
+ return if ObjectIsUntouchable(noun)
2063
+ return L__M(:SwitchOn, 1, noun) if noun.hasnt? :switchable
2064
+ return L__M(:SwitchOn, 2, noun) if noun.has? :on
2065
+ give noun, :on
2066
+ return true if AfterRoutines()
2067
+ return true if @keep_silent
2068
+ @player.publish L__M(:SwitchOn, 4, noun)
2069
+ L__M(:SwitchOn, 3, noun)
2070
+ end
2071
+
2072
+ def SwitchoffSub
2073
+ return if ObjectIsUntouchable(noun)
2074
+ return L__M(:SwitchOff, 1, noun) if noun.hasnt? :switchable
2075
+ return L__M(:SwitchOff, 2, noun) if noun.hasnt? :on
2076
+ take noun, :on
2077
+ return true if AfterRoutines()
2078
+ return true if @keep_silent
2079
+ @player.publish L__M(:SwitchOff, 4, noun)
2080
+ L__M(:SwitchOff, 3, noun)
2081
+ end
2082
+
2083
+ def OpenSub
2084
+ return if ObjectIsUntouchable(noun)
2085
+ return L__M(:Open, 1, noun) if noun.hasnt? :openable
2086
+ return L__M(:Open, 2, noun) if noun.has? :locked
2087
+ return L__M(:Open, 3, noun) if noun.has? :open
2088
+ give noun, :open
2089
+ return true if AfterRoutines()
2090
+ return true if @keep_silent
2091
+ @player.publish L__M(:Open, 6, noun)
2092
+ if noun.has?(:container) && noun.hasnt?(:transparent) &&
2093
+ @location != thedark &&
2094
+ VisibleContents(noun) != 0 && IndirectlyContains(noun, @player)
2095
+ return L__M(:Open, 4, noun)
2096
+ end
2097
+ L__M(:Open,5,noun)
2098
+ end
2099
+
2100
+ def CloseSub
2101
+ return if ObjectIsUntouchable(noun)
2102
+ return L__M(:Close, 1, noun) if noun.hasnt? :openable
2103
+ return L__M(:Close, 2, noun) if noun.hasnt? :open
2104
+ take noun, :open
2105
+ return true if AfterRoutines()
2106
+ return true if @keep_silent
2107
+ @player.publish L__M(:Close, 4, noun)
2108
+ L__M(:Close, 3, noun)
2109
+ end
2110
+
2111
+ def DisrobeSub
2112
+ return if ObjectIsUntouchable(noun)
2113
+ return L__M(:Disrobe, 1, noun) if noun.hasnt? :worn
2114
+ take noun, :worn
2115
+ return true if AfterRoutines()
2116
+ return true if @keep_silent
2117
+ @player.publish L__M(:Disrobe, 3, noun)
2118
+ L__M(:Disrobe, 2, noun)
2119
+ end
2120
+
2121
+ def WearSub
2122
+ return if ObjectIsUntouchable(noun)
2123
+ return L__M(:Wear, 1, noun) if noun.hasnt? :clothing
2124
+ return L__M(:Wear, 2, noun) if noun.parent != @player
2125
+ return L__M(:Wear, 3, noun) if noun.has? :worn
2126
+ give noun, :worn
2127
+ return true if AfterRoutines()
2128
+ return true if @keep_silent
2129
+ @player.publish L__M(:Wear, 5, noun)
2130
+ L__M(:Wear, 4, noun)
2131
+ end
2132
+
2133
+ def EatSub
2134
+ return if ObjectIsUntouchable(noun)
2135
+ return L__M(:Eat, 1, noun) if noun.hasnt? :edible
2136
+ if noun.has? :worn
2137
+ L__M(:Drop, 3, noun)
2138
+ _invoke :Disrobe, noun
2139
+ return true if noun.has?(:worn) && noun.in?(@player)
2140
+ end
2141
+ noun.destroy
2142
+ return true if AfterRoutines()
2143
+ return true if @keep_silent
2144
+ @player.publish L__M(:Eat, 3, noun)
2145
+ L__M(:Eat, 2, noun)
2146
+ end
2147
+
2148
+ # ----------------------------------------------------------------------------
2149
+ # Verbs which are really just stubs (anything which happens for these
2150
+ # actions must happen in before rules)
2151
+ # ----------------------------------------------------------------------------
2152
+
2153
+ def AllowPushDir
2154
+ return L__M(:PushDir, 2, noun) if parent(second) != Compass
2155
+ return L__M(:PushDir, 3, noun) if [$u_obj, $d_obj].include?(second)
2156
+ AfterRoutines(); i = noun; move i, @player
2157
+ invoke :Go, second
2158
+ if @location == thedark then move i, @real_location
2159
+ else move i, @location
2160
+ end
2161
+ end
2162
+
2163
+ def AnswerSub
2164
+ return false if !second.nil? && RunLife(second, :Answer)
2165
+ L__M(:Answer, 1, noun)
2166
+ end
2167
+
2168
+ def AskSub
2169
+ return false if RunLife(noun, :Ask)
2170
+ L__M(:Ask, 1, noun)
2171
+ end
2172
+
2173
+ def AskForSub
2174
+ inv if noun == @player
2175
+ L__M(:Order, 1, noun)
2176
+ end
2177
+
2178
+ def AskToSub
2179
+ return false if RunLife(noun, :AskTo)
2180
+ L__M(:Order, 1, noun)
2181
+ end
2182
+
2183
+ def AttackSub
2184
+ return if ObjectIsUntouchable(noun)
2185
+ return false if noun.has?(:animate) && RunLife(noun, :Attack)
2186
+ L__M(:Attack, 1, noun)
2187
+ end
2188
+
2189
+ def BlowSub; L__M(:Blow, 1, noun); end
2190
+
2191
+ def BurnSub; L__M(:Burn, 1, noun); end
2192
+
2193
+ def BuySub; L__M(:Buy, 1, noun); end
2194
+
2195
+ def ClimbSub; L__M(:Climb, 1, noun); end
2196
+
2197
+ def ConsultSub; L__M(:Consult, 1, noun); end
2198
+
2199
+ def CutSub; L__M(:Cut, 1, noun); end
2200
+
2201
+ def DigSub; L__M(:Dig, 1, noun); end
2202
+
2203
+ def DrinkSub; L__M(:Drink, 1, noun); end
2204
+
2205
+ def FillSub; L__M(:Fill, 1, noun); end
2206
+
2207
+ def JumpSub; L__M(:Jump, 1, noun); end
2208
+
2209
+ def JumpOverSub; L__M(:JumpOver, 1, noun); end
2210
+
2211
+ def KissSub
2212
+ return if ObjectIsUntouchable(noun)
2213
+ return false if RunLife(noun, :Kiss)
2214
+ return L__M(:Touch, 3, noun) if noun == @player
2215
+ # TODO: Publish a visible message
2216
+ L__M(:Kiss, 1, noun)
2217
+ end
2218
+
2219
+ def ListenSub; L__M(:Listen, 1, noun); end
2220
+
2221
+ def MildSub; L__M(:Mild, 1, noun); end
2222
+
2223
+ def NoSub; L__M(:No); end
2224
+
2225
+ def PraySub; L__M(:Pray, 1, noun); end
2226
+
2227
+ def PullSub
2228
+ return if ObjectIsUntouchable(noun)
2229
+ return L__M(:Pull, 1, noun) if noun.has? :static
2230
+ return L__M(:Pull, 2, noun) if noun.has? :scenery
2231
+ return L__M(:Pull, 4, noun) if noun.has? :animate
2232
+ return true if AfterRoutines()
2233
+ # TODO: Publish a visible message
2234
+ L__M(:Pull, 3, noun)
2235
+ end
2236
+
2237
+ def PushSub
2238
+ return if ObjectIsUntouchable(noun)
2239
+ return L__M(:Push, 1, noun) if noun.has? :static
2240
+ return L__M(:Push, 2, noun) if noun.has? :scenery
2241
+ return L__M(:Push, 4, noun) if noun.has? :animate
2242
+ return true if AfterRoutines()
2243
+ # TODO: Publish a visible message
2244
+ L__M(:Push, 3, noun)
2245
+ end
2246
+
2247
+ def PushDirSub
2248
+ return "You'll have to stand up first." if @player.hasany? :sitting, :kneeling, :prone
2249
+ L__M(:PushDir, 1, noun)
2250
+ end
2251
+
2252
+ def RubSub; @player.publish L__M(:Rub, 2, noun); L__M(:Rub, 1, noun); end
2253
+
2254
+ def SetSub; L__M(:Set, 1, noun); end
2255
+
2256
+ def SetToSub; L__M(:SetTo, 1, noun); end
2257
+
2258
+ def SingSub; @player.publish L__M(:Sing, 2, noun); L__M(:Sing, 1, noun); end
2259
+
2260
+ def SleepSub; @player.publish L__M(:Sleep, 2, noun); L__M(:Sleep, 1, noun); end
2261
+
2262
+ def SmellSub; @player.publish L__M(:Smell, 2, noun); L__M(:Smell, 1, noun); end
2263
+
2264
+ def SorrySub; @player.publish L__M(:Sorry, 2, noun); L__M(:Sorry, 1, noun); end
2265
+
2266
+ def SqueezeSub
2267
+ return if ObjectIsUntouchable(noun)
2268
+ if noun.has? :animate then return L__M(:Squeeze, 1, noun) end
2269
+ return true if AfterRoutines()
2270
+ L__M(:Squeeze, 2, noun)
2271
+ end
2272
+
2273
+ def StrongSub; L__M(:Strong, 1, noun); end
2274
+
2275
+ def SwimSub; L__M(:Swim, 1, noun); end
2276
+
2277
+ def SwingSub; L__M(:Swing, 1, noun); end
2278
+
2279
+ def TasteSub; L__M(:Taste, 1, noun); end
2280
+
2281
+ def TellSub
2282
+ return L__M(:Tell, 1, noun) if noun == @player
2283
+ return false if RunLife(noun, :Tell)
2284
+ L__M(:Tell, 2, noun)
2285
+ end
2286
+
2287
+ def ThinkSub
2288
+ @player.publish L__M(:Think, 2, noun)
2289
+ L__M(:Think, 1, noun)
2290
+ end
2291
+
2292
+ def ThrowAtSub
2293
+ return if ObjectIsUntouchable(noun)
2294
+ unless second.nil?
2295
+ @action = :ThrownAt
2296
+ if RunRoutines(second, :before) then @action = :ThrowAt; return true; end
2297
+ @action = :ThrowAt
2298
+ end
2299
+ if noun.has? :worn
2300
+ L__M(:Drop, 3, noun)
2301
+ disrobe noun
2302
+ return true if noun.has?(:worn) && noun.in?(@player)
2303
+ end
2304
+ unless second.nil?
2305
+ return L__M(:ThrowAt, 1) if second.hasnt? :animate
2306
+ return false if RunLife(second, :ThrowAt)
2307
+ end
2308
+ L__M(:ThrowAt, 2, noun)
2309
+ end
2310
+
2311
+ def TieSub; L__M(:Tie,1,noun); end
2312
+
2313
+ def TouchSub
2314
+ return L__M(:Touch, 3, noun) if noun == @player
2315
+ return if ObjectIsUntouchable(noun)
2316
+ return L__M(:Touch, 1, noun) if noun.has? :animate
2317
+ @player.publish L__M(:Touch,4,noun)
2318
+ L__M(:Touch,2,noun)
2319
+ end
2320
+
2321
+ def TurnSub
2322
+ return if ObjectIsUntouchable(noun)
2323
+ return L__M(:Turn, 1, noun) if noun.has? :static
2324
+ return L__M(:Turn, 2, noun) if noun.has? :scenery
2325
+ return L__M(:Turn, 4, noun) if noun.has? :animate
2326
+ L__M(:Turn, 3, noun)
2327
+ end
2328
+
2329
+ def WaitSub
2330
+ @player.publish L__M(:Wait, 2, noun)
2331
+ return true if AfterRoutines()
2332
+ L__M(:Wait, 1, noun)
2333
+ end
2334
+
2335
+ def WakeSub; L__M(:Wake, 1, noun); end
2336
+
2337
+ def WakeOtherSub
2338
+ return if ObjectIsUntouchable(noun)
2339
+ return false if RunLife(noun, :WakeOther)
2340
+ L__M(:WakeOther, 1, noun)
2341
+ end
2342
+
2343
+ def WaveSub
2344
+ return L__M(:Wave, 1, noun) if !noun.nil? && (noun.has?(:clothing, :worn) || noun.notin?(@player))
2345
+ @player.publish L__M(:Wave, 3, noun)
2346
+ L__M(:Wave, 2, noun)
2347
+ end
2348
+
2349
+ def WaveHandsSub
2350
+ if noun
2351
+ @player.publish L__M(:WaveHands, 4, noun)
2352
+ L__M(:WaveHands, 3, noun)
2353
+ else
2354
+ @player.publish L__M(:WaveHands, 2, noun)
2355
+ L__M(:WaveHands, 1, noun)
2356
+ end
2357
+ end
2358
+
2359
+ def YesSub; L__M(:Yes); end
2360
+
2361
+ # ----------------------------------------------------------------------------
2362
+ # Debugging verbs
2363
+ # ----------------------------------------------------------------------------
2364
+
2365
+ def XTestMove(obj, dest)
2366
+ if obj.is_a?(InformLibrary) || obj == LibraryMessages || obj.is_a?(Inform::System::Object)
2367
+ println "[Can't move #{obj.name}: it's a system object.]"
2368
+ return true
2369
+ end
2370
+ loop do
2371
+ break if dest.nil?
2372
+ println "[Can't move #{obj.name}: it would contain itself.]" if dest == obj
2373
+ dest = parent(dest)
2374
+ end
2375
+ false
2376
+ end
2377
+
2378
+ def XAbstractSub
2379
+ return if XTestMove(noun, second)
2380
+ move noun, second
2381
+ "[Abstracted.]"
2382
+ end
2383
+
2384
+ def XPurloinSub
2385
+ return if XTestMove(noun, player)
2386
+ move noun, player; give noun, :moved; take noun, :concealed
2387
+ "[Purloined.]"
2388
+ end
2389
+
2390
+ def XObj(obj, f = 0)
2391
+ if parent(obj) then print a(obj) else print obj end
2392
+ print " (#{obj.id}) "
2393
+ print "(in #{parent(obj)} #{parent(obj).id})" if f == 1 && parent(obj)
2394
+ new_line
2395
+ return true if child(obj).nil?
2396
+ if obj.is_a?(Class)
2397
+ WriteListFrom(child(obj), NEWLINE_BIT + INDENT_BIT + ALWAYS_BIT + NOARTICLE_BIT, 1)
2398
+ else
2399
+ WriteListFrom(child(obj), NEWLINE_BIT + INDENT_BIT + ALWAYS_BIT + FULLINV_BIT, 1)
2400
+ end
2401
+ new_line
2402
+ end
2403
+
2404
+ def XTreeSub
2405
+ raise Parser::VerbUnrecognized unless @player.builder?
2406
+ return XObj(noun, 1) unless noun.nil?
2407
+ objectloop { |i|
2408
+ XObj(i) if i.object? && parent(i).nil?
2409
+ }
2410
+ end
2411
+
2412
+ def GotoSub
2413
+ raise Parser::VerbUnrecognized unless @player.builder?
2414
+ log.debug "<Goto noun> #=> #{noun}"
2415
+ if !noun.is_a?(Inform::Object)
2416
+ "[Not a safe place.]"
2417
+ elsif noun != @player
2418
+ PlayerTo(noun)
2419
+ else
2420
+ "Cannot go to there."
2421
+ end
2422
+ end
2423
+
2424
+ def GonearSub
2425
+ raise Parser::VerbUnrecognized unless @player.builder?
2426
+ x = noun
2427
+ loop do
2428
+ break if parent(x).nil?
2429
+ x = parent(x)
2430
+ end
2431
+ PlayerTo(x)
2432
+ end
2433
+
2434
+ def Print_ScL(obj); println (@x_scope_count += 1) + ": " + a(obj) + " (" + obj.id + ")"; end
2435
+
2436
+ def ScopeSub
2437
+ raise Parser::VerbUnrecognized unless @player.admin?
2438
+ @x_scope_count = 0
2439
+ LoopOverScope(method(:Print_ScL), noun)
2440
+ "Nothing is in scope." if @x_scope_count == 0
2441
+ end
2442
+
2443
+ # ----------------------------------------------------------------------------
2444
+ # Finally: the mechanism for library text (the text is in the language defn)
2445
+ # ----------------------------------------------------------------------------
2446
+
2447
+ # def L__M(act, n, x1)
2448
+ # s = @sw__var
2449
+ # @sw__var = act
2450
+ # n = 1 if n == 0
2451
+ # L___M(n,x1)
2452
+ # ensure
2453
+ # @sw__var = s
2454
+ # end
2455
+
2456
+ # def L___M(n, x1)
2457
+ # s = @action
2458
+ # lm_n = n
2459
+ # lm_o = x1
2460
+ # @action = @sw__var
2461
+ # unless RunRoutines(LibraryMessages, :before).nil? then @action = s; return false end
2462
+ # unless LibraryExtensions.RunWhile(:ext_messages, nil).nil? then @action = s; return false end
2463
+ # @action = s
2464
+ # LanguageLM(n, x1)
2465
+ # end
2466
+ end
2467
+ # module Verbs
2468
+ end
2469
+ # module Inform
2470
+
2471
+ # rubocop: enable Lint/UselessAssignment
2472
+
2473
+ # The Inform module
2474
+ module Inform
2475
+ # The Parser module
2476
+ module Parser
2477
+ include Inform::Verbs
2478
+ end
2479
+ end
2480
+
2481
+ # ==============================================================================