-
Notifications
You must be signed in to change notification settings - Fork 0
/
lookDir.t
657 lines (552 loc) · 22.7 KB
/
lookDir.t
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>
ModuleID
{
name = 'LookDir'
byline = 'by Eric Eve'
htmlByline = 'by <a href="mailto:eric.eve@hmc.ox.ac.uk">Eric Eve</a>'
version = '3.0'
listingOrder = 72
}
/***************************************************************************
* LookDir (TADS 3 Libary Extension)
* by Eric Eve
* Version 3.0 (April 2005)
*
* To use this extension, simply include it in the list of your project\'s source
* files after the standard adv3 libary files. This extension
* allows commands such as LOOK NORTH and LOOK DOWN (i.e. Look + direction)
* To use, add dirLook properties (e.g. northLook, downLook) to room definitions
* as required: e.g.:
*
* entryWay : Room 'Entrance Hall'
* "This imposing entrance hall continues to the north. The front
* door is directly to the south and a series of fusty old portraits
* hang on the west wall. "
*
* northLook = "The hall continues some way in that direction. "
* southLook = "That way lies the front door. "
* westLook = "There\'s not much there except some fusty old portraits. "
* north = hall
* south = frontDoor
* ;
*
* N.B. There is no need define these properties on locations where you don\'t want them to be
* used. There is only ever a need to define one of these properties as nil if you want to
* override an xxxLook property defined on a superclass; alternatively you may want to define
* an xxxLook property as a method that returns nil under certain conditions.
*/
/*
* Version 3 (April 2005) adds a check for there being enough light to see by,
* and various forms of handling for LOOK DIR in a dark location.
*
* It also adds code to map looking in a direction to examining the appropropriate room
* part (e.g. LOOK EAST becomes EXAMINE EAST WALL if no eastLook property is defined)
*
* It also adds a defaultLook(dir) method to provide a default message for looking in directions
* not dealt with by xxxLook properties or room parts. This may be overridden as desired on
* individual rooms.
*
* It also adds appropriate handling for NestedRooms.
*
* It adds describeRemoteRoom() and listRemoteContents() methods to allow an xxxLook
* property to describe a remote location visible through a SenseConnector.
*
* Finally, it incorporates support for Michel Nizette\'s pasttense extension.
*/
property pastTenseExtensionPresent;
enum showRoomPart;
DefineIAction(LookDir)
execAction()
{
local dirn = dirMatch.dir;
local prop = dirLook.dirTab[dirn];
local loc = gActor.roomLocation.lookLocation(dirn);
local hasDisplayed = nil;
local rPart;
/*
* If the player simply types LOOK IN treat it as an incomplete
* form of the library\'s LookInAction, and redirect the command
* accordingly.
*/
if(dirn == inDirection)
askForDobj(LookIn);
/*
* If it\'s too dark to see, diplay a failure message to that effect. But allow
* this to be overridden for special cases (e.g. the player char may be able to see
* a distant light in the dark).
*
* There are three ways you can go about this. The easiest, and generally
* preferred, way is simply to define an xxxxLookDark property on the location
* where you want a custom message displayed in the dark. For example, if
* you have a dark cave with an entrance to the west, you may want to display
* a message about light coming in from tbe west when an actor LOOKS WEST in the cave.
* In which case define a westLookDark property on the cave thus:
*
* westLookDark = "{You/he} see{s} a small amount of light seeping in through
* the cave entrance. "
*
* The second is to override
* tooDarkForDirLook(dirn) on the appropriate location so that it returns
* nil. The location will then be treated as if it were lit for the purposes
* of processing the LOOK DIR command.
*
* The third is to override lookDirInDark(dirn) on the
* appropriate location to display whatever custom messages(s) you want
* displayed when an actor looks to dirn in the dark.
*
*/
if(!gActor.isLocationLit && loc.tooDarkForDirLook(dirn))
{
return;
}
/*
* Otherwise, if the current location defines the appropriate
* dirLook property (or method), let that handle the action.
*/
if(prop != nil && loc.propDefined(prop) && loc.propType(prop) != TypeNil)
{
/*
* Even if prop is defined and is non-nil, it may fail to display anything,
* so we test to ensure it does. Failure to display may occur because prop
* is a method or expression that evaluates to nil, or because it evaluates
* to a single-quoted string.
*/
local val;
hasDisplayed = mainOutputStream.watchForOutput( {: val = loc.(prop) } );
/*
* If we displayed something, we\'re done, unless we returned showRoomPart
* The test for returning showRoomPart is a special case to allow us to display
* a custom message followed by a description of the appropriate room part.
* e.g.:
*
* northLook { "You look north across the room. "; return showRoomPart; }
*
* This would display "You look north across the room. " followed by the description
* of the north wall, if there is one (or else display the defaultLook message,
* which probably wouldn\'t be what you want).
*/
if(hasDisplayed && val != showRoomPart)
return;
/*
* If we didn\'t display something, perhaps loc.(prop) evaluated to a
* single-quoted string; in which case display the string and return.
*/
if(dataType(val) == TypeSString)
{
say(val);
return;
}
}
/*
* If no outLook was defined, treat LOOK OUT as an incomplete LOOK THROUGH
* command.
*/
if(dirn == outDirection)
askForDobj(LookThrough);
/*
* Otherwise, if the player specified a shipboard direction (e.g. LOOK PORT) and
* we\'re not in a shipboard room, display the standard 'not aboard ship' message.
*
* Note that putting this check *after* the previous one allows an author to
* define a portLook, starboardLook, aftLook or foreLook even on a non-shipboard
* room; whether this is useful is left to authors to decide, not dictated by
* this extension.
*
*/
else if(dirn.ofKind(ShipboardDirection) && !loc.isShipboard)
libMessages.notOnboardShip();
/*
* Otherwise see if we can find a room part matching the compass direction name, and, if
* so, examine it, provided this location allows it.
*/
else if(loc.lookDirToRoomPart(dirn) && (rPart = findRoomPart(dirn)) != nil)
{
/* If we found an appropriate room part, examine it */
replaceAction(Examine, rPart);
}
/*
* Otherwise print the default message "You see nothing remarkable by looking <<dir>>. ",
* unless we previously displayed something else.
*/
else if(!hasDisplayed)
loc.defaultLook(dirn);
}
/*
* This is made a separate method in case it needs to be customised for
* languages other than English.
*/
findRoomPart(dirn)
{
local loc = gActor.getOutermostRoom;
/* If the command was LOOK DOWN try looking at the current room's floor object */
if(dirn == downDirection)
return loc.roomFloor;
/* If the command was LOOK UP try to find the sky or ceiling object */
if(dirn == upDirection)
return loc.roomParts.valWhich(
{ x: nilToList(x.noun).indexOf(dirLook.sky) != nil
|| nilToList(x.noun).indexOf(dirLook.ceiling) != nil } );
/* Otherwise try to find a wall (or other room part) with an adjective that matches our direction name */
return loc.roomParts.valWhich(
{x: nilToList(x.adjective).indexOf(dirn.name) != nil });
}
;
/*
* Set up dirLook properties for all directions except 'in', since the library
* already defines a 'look in x' verb.
*/
dirLook : PreinitObject
dirTab = nil
dirDarkTab = nil
execute()
{
/* Make the LookUpTable values a bit bigger that strictly necessary in case
* an author wants to add custom directions (even though it would work well
* enough even if we didn\'t do this).
*
* e.g.
*
* modify dirLook
* execute()
* {
* inherited;
* dirTab[fooDirection} = &fooLook;
* }
* ;
*/
dirTab = new LookupTable(20, 20);
dirTab[northDirection] = &northLook;
dirTab[eastDirection] = &eastLook;
dirTab[southDirection] = &southLook;
dirTab[westDirection] = &westLook;
dirTab[northeastDirection] = &northeastLook;
dirTab[southeastDirection] = &southeastLook;
dirTab[southwestDirection] = &southwestLook;
dirTab[northwestDirection] = &northwestLook;
dirTab[upDirection] = &upLook;
dirTab[downDirection] = &downLook;
dirTab[portDirection] = &portLook;
dirTab[starboardDirection] = &starboardLook;
dirTab[foreDirection] = &foreLook;
dirTab[aftDirection] = &aftLook;
dirTab[outLook] = &outLook;
/* Equivalent lookupTable for looking in the dark */
dirDarkTab = new LookupTable(20, 20);
dirDarkTab[northDirection] = &northLookDark;
dirDarkTab[eastDirection] = &eastLookDark;
dirDarkTab[southDirection] = &southLookDark;
dirDarkTab[westDirection] = &westLookDark;
dirDarkTab[northeastDirection] = &northeastLookDark;
dirDarkTab[southeastDirection] = &southeastLookDark;
dirDarkTab[southwestDirection] = &southwestLookDark;
dirDarkTab[northwestDirection] = &northwestLookDark;
dirDarkTab[upDirection] = &upLookDark;
dirDarkTab[downDirection] = &downLookDark;
dirDarkTab[portDirection] = &portLookDark;
dirDarkTab[starboardDirection] = &starboardLookDark;
dirDarkTab[foreDirection] = &foreLookDark;
dirDarkTab[aftDirection] = &aftLookDark;
dirDarkTab[outLook] = &outLookDark;
}
/*
* We use this rather roundabout way of specifying the words for
* 'sky' and 'ceiling' to make it a bit easier for a non-English
* language version specify what it needs.
*/
sky = static defaultSky.name
ceiling = static defaultCeiling.name
;
modify BasicLocation
/*
* By default allow mapping LOOK DIR to examining the corresponding room part
* (e.g. LOOK NORTH = EXAMINE NORTH WALL if we have a north wall and if northLook isn\'t
* already defined). If I\'m a nested room, then by default pick up the value of this
* property from my enclosing location.
*/
lookDirToRoomPart(dirn) { return location == nil ? true : location.lookDirToRoomPart(dirn); }
/*
* By default, delegate a LookDir command to my immediate container
* if I have one, unless I define the appropriate xxxLook property,
* in which case use me.
*
*
*
* Note that defining an xxxLook property on a NestedRoom to anything other than nil,
* a double-quoted or single-quoted string, or a routine that prints a string or returns
* a single-quoted string will cause the NestedRoom\'s defaultLook method to be used.
* If you want this effect you can, for example, define northLook = true on a NestedRoom
* to force LOOK NORTH to use the NestedRoom\'s defaultLook rather than the enclosing room\'s
* northLook, north wall or defaultLook (as the case may be).
*/
lookLocation(dirn)
{
local prop = dirLook.dirTab[dirn];
/*
* If I have no location, or if I define the appropriate xxxLook property
* and it\'s not simply nil, or if my enclosing location can\'t be seen by
* the actor, or if the actor is looking in a direction occluded by me,
* then let me handle the lookDir command. Otherwise, pass it up the
* chain to my enclosing location.
*/
/*
* We want to test if the actor could see out if there was enough light,
* regardless of whether there is or not, so that we don\'t lose any of
* the enclosing location\'s xxxLook properties which are meant to be visible
* in the dark. So we turn the lights on for the purposes of checking the
* lookAroundCeiling, and then restore the light level to what it was before.
*
* In any case, if we\'re in the dark on a chair, bed or platform, it makes
* sense for our enclosing location to deal with the 'too dark' situation,
* so by default we won\'t make absence of light a reason for not being able
* to see out.
*/
try
{
lookDirTestProbe.baseMoveInto(self);
return isLookAroundCeiling(gActor, gActor)
|| (propDefined(prop) && propType(prop) != TypeNil)
|| lookDirOccluded(dirn)
? self : location.lookLocation(dirn);
}
finally
{
lookDirTestProbe.baseMoveInto(nil);
}
}
/*
* The message to display if nothing else matches.
* This can be overridden on individual rooms if required;
* it can also be made to vary by direction.
*/
defaultLook(dirn) { defaultReport(¬hingRemarkable, dirn) ; }
/*
* By default we check whether the direction the actor is trying to look
* in is one that is in the list of directions occluded by the NestedRoom.
* In some cases it may be easier to override this method than the
* occludedDirs list; e.g. if the only direction an actor can see out
* of the NestedRoom is north, then override this method thus:
*
* lookDirOccluded(dirn) { return dirn != northDirection; }
*
* This check is most likely to be relevant for Booths, but may be applied
* to any NestedRoom. It has no effect on a top-level room, since if this
* BasicLocation has no enclosing room, this test will never be applied.
*/
lookDirOccluded(dirn)
{
return occludedDirs.indexOf(dirn) != nil;
}
/*
* A list of directions occluded by the NestedRoom (i.e. directions in which the actor
* would not be able to see out of the NestedRoom because the NestedRoom wall is in the way).
* This must be defined by game authors on individual NestedRooms, since this library
* extension has no way of knowing the configuration of individual NestedRooms.
*
* e.g. occludedDirs = [northDirection, northeastDirection, eastDirection]
*
* This is most likely to be relevant for Booths. Note that this list has no effect on
* Rooms, since these have no enclosing location which might be viewed in any case.
*/
occludedDirs = []
/*
* This method is called if a LOOK DIR command is issued in a dark location.
* It should not normally be necessary to override it; override lookDirInDark()
* instead to change the behaviour.
*
* By default, this method displays the standard tooDarkTooSeeThatWayMsg,
* but this is only displayed if lookDirInDark() does not display anything.
*
* To allow LOOK DIR to work in the dark as if it were light, override this
* method to return nil.
*/
tooDarkForDirLook(dirn)
{
if(!mainOutputStream.watchForOutput( {: lookDirInDark(dirn) }) )
reportFailure(&tooDarkToSeeThatWayMsg, dirn);
return true;
}
/*
* Override this method to customize the effect of trying to LOOK DIR in the dark.
* You could display an alternative "too dark" message, or you could display a message
* appropriate to the situation when something can be seen in a particular direction.
*
* For example, an actor in a dark location might still be able to see a distant light
* elsewhere, or light coming down a passage.
*
* By default this method checks for an xxxLookDark property, and, if it finds
* it, displays its message, so you can just define any xxxLookDark properties you need.
*/
lookDirInDark(dirn)
{
local prop = dirLook.dirDarkTab[dirn];
if(propDefined(prop) && propType(prop) != TypeNil)
{
local val = self.(prop);
if(dataType(val) == TypeSString)
say(val);
}
}
/*
* The describeRemoteRoom method is provided to enable looking towards another location
* joined to the first by a SenseConnector (typically a DistanceConnector). This method
* assumes that the remote location has a suitable roomRemoteDesc method defined to
* described how it looks from where the actor is standing.
*
* For example if you had a large hall divided into two rooms, northHall and southHall,
* joined by a DistanceConnector, and northHall provided a suitable roomRemoteDesc
* method, you could define:
*
* southHall : Room 'South End of Hall' 'the south end of the hall'
* "This large hall continues to the north. "
* north = northHall
* northLook { describeRemoteRoom(northHall); }
* ;
*
* The advantage of this is that the contents of northHall will also be appropriately
* listed when the player issues the command LOOK NORTH.
*/
describeRemoteRoom(loc) { describeRemoteRoomPov(loc, gActor, gActor); }
describeRemoteRoomPov(loc, actor, pov)
{
loc.roomRemoteDesc(actor);
listRemoteContentsPov(loc, actor, pov);
}
/*
* You can use the listRemoteContents method as an alternative to describeRemoteRoom
* when you want your xxxLook property to give its own description of the other location
* but you still want to list the contents of the other location; e.g.
* southHall : Room 'South End of Hall' 'the south end of the hall'
* "This large hall continues to the north. "
* north = northHall
* northLook
* {
* "You look north towards the far end of the hall.<.p>";
* listRemoteContents(northHall);
* }
* ;
*
* listRemoteContents(otherRoom) lists the contents of otherRoom as seen from the perspective
* of the actor doing the looking; thus, for example, if the looking is being done through a
* DistanceConnector, the objects in otherRoom will be listed using any distant or remote
* properties that have been defined, such as distantDesc or remoteInitSpecialDesc(pov), and
* they will be listed as being present in otherRoom rather than the actor\'s immediate location.
*/
listRemoteContents(otherLocation) { listRemoteContentsPov(otherLocation, gActor, gActor); }
/*
* In order to achieve the desired effect of listing the contents of the other room
* as they appear from the current actor\'s location, we have to lookAround the actor\'s
* current location, but exclude everything from the list of objects that is not in
* the other location.
*/
listRemoteContentsPov(otherLocation, actor, pov )
{
/*
* We can\'t pass otherLocation to adjustLookAroundTable as a parameter, since there\'s
* no such parameter defined on this method. Instead we pass it through a property.
*/
listLocation_ = otherLocation;
try
{
lookAroundPov(actor, pov, LookListSpecials | LookListPortables);
}
finally
{
/* Restore listLocation_ to nil so that lookAround will function normally in other contexts */
listLocation_ = nil;
}
}
/*
* If listLocation_ is not nil, then remove everything from the table of objects
* to be listed apart from objects in listLocation_. This will cause lookAround() to
* list only the objects in that location.
*/
adjustLookAroundTable(tab, pov, actor)
{
inherited(tab, pov, actor);
if(listLocation_ != nil)
{
local lst = tab.keysToList();
foreach(local cur in lst)
{
if(!cur.isIn(listLocation_))
tab.removeElement(cur);
}
}
}
/*
* Internal property for passing a value to adjustLookAroundTable. This should NOT
* be overridden by game authors without very good reason.
*/
listLocation_ = nil
;
/*
* A test probe to see if we could see into a location were it lit
*/
lookDirTestProbe : Thing
brightness = 3
;
/*
* A default room part may give its description in response to a LookDir command; in this
* case it needs to name itself rather than refer to itself simply as 'it', since
* "You see nothing unusual about it" is a poor response to (e.g.) "LOOK NORTH"
*/
modify RoomPart
desc() { defaultReport(¬hingUnusualAboutMsg, self); }
;
/*==========================================================================================
* Language-Specific elements
*/
/*
* A couple of convenience macros for building messages that either use or don\'t
* use the pasttense.t library extension according to whether or not it\'s present
*
* We define them here rather than in a header file, firstly because we don\'t need
* them anywhere else, and secondly to avoid any potential clash with any other
* extension that may want to define its own macros for compatibility with pasttense.t.
*/
#define seesStr (languageGlobals.pastTenseExtensionPresent ? '{sees}' : 'see{s}')
#define itsStr (languageGlobals.pastTenseExtensionPresent ? 'It{\'s| was}' : 'It\'s')
/* Allow a variety of phrasings from a laconic L N through LOOK NORTH to LOOK TO THE NORTH. */
VerbRule(LookDir)
('look' |'l'| 'peer'|'examine'|'x') ('to' ('the' |) |) singleDir
: LookDirAction
verbPhrase = 'look/looking {where} '
;
/*
* English-language specific modifications to playerActionMessages
*/
modify playerActionMessages
nothingRemarkable(dirn)
{
return '{You/he} ' + seesStr + ' nothing remarkable '+ dirName(dirn) + '. ';
}
tooDarkToSeeThatWayMsg(dirn)
{
return itsStr + ' too dark to see anything ' + dirName(dirn) + '. ';
}
nothingUnusualAboutMsg(obj)
{
gMessageParams(obj);
return '{You/he} ' + seesStr + ' nothing unusual about {the obj/him}. ';
}
dirName(dirn)
{
switch(dirn)
{
case upDirection:
return 'above {it actor/him}';
case downDirection:
return 'beneath {it actor/him}';
case portDirection:
case starboardDirection:
return 'to ' + dirn.name;
case aftDirection:
case foreDirection:
return dirn.name;
default:
return 'to the ' + dirn.name;
}
}
;