forked from EricEve/adv3lite
-
Notifications
You must be signed in to change notification settings - Fork 0
/
travel.t
1928 lines (1618 loc) · 60.4 KB
/
travel.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
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#charset "us-ascii"
#include "advlite.h"
/*
* ****************************************************************************
* room.t
* This module forms part of the adv3Lite library
* (c) 2012-13 Eric Eve
*/
property lastTravelInfo;
property cannotGoShowExits;
/*
* A Room is a top location in which the player character, other actors and
* other objects may be located. It may represent any discrete unit of space,
* not necessarily a room in a building. Normally actors may only interact
* with objects in the same room as themselves, but the senseRegion module
* allows us to define sensory connections between rooms.
*/
class Room: TravelConnector, Thing
/*
* The direction properties (north, south, etc.) define what happens when
* travel is attempted in the corresponding direction. A direction
* property may be defined as another Room (in which case traveling in the
* corresponding direction takes the actor directly to that Room), or to a
* TravelConnector (including a Door or Stairway), or to a single-quoted
* or double-quoted string (which is then simply displayed) or to a method
* (which is then executed). It is recommended that methods only be used
* when the effect of attempted travel is something other than ordinary
* travel; to impose conditions on travel or define the side-effects of
* travel it's usually better to use a TravelConnector object.
*/
north = nil
east = nil
south = nil
west = nil
up = nil
down = nil
in = nil
out = nil
southeast = nil
southwest = nil
northeast = nil
northwest = nil
port = nil
starboard = nil
aft = nil
fore = nil
/*
* Are compass directions allowed for travel from this room? By default
* we'll allow thema anywhere, but game code may wish to override this for
* rooms that are aboard a vessel.
*/
allowCompassDirections = true
/*
* Are shipboard directions meaningful in this room? By default we'll make
* them so if and only if this room defines at least one shipboard
* directional exit. Game code may wish to modify this, for example, on
* the hold of a ship that only defines an up direction but where
* shipboard directions would still in principle be meaningful.
*/
allowShipboardDirections()
{
for(local dir in ShipboardDirection.shipboardDirections)
{
if(propType(dir.dirProp) != TypeNil)
return true;
}
return nil;
}
/*
* A Room is normally lit, but if we want a dark room we can override
* isLit to nil.
*/
isLit = true
/* A Room is always fixed in place. */
isFixed = true
/* A Room is always open */
isOpen = true
/*
* A Room is lit within it it's illuminated (it's either lit itself or
* contains a light source
*/
litWithin()
{
return isIlluminated;
}
/*
* Since a Room provides the TravelConnector interface, we need to define
* where it leads to when one attempts to travel via it; a Room always
* leads to itself (i.e. traveling via a Room takes one to that Room).
*/
destination { return self; }
/* By default our destination is known if we've been visited */
isDestinationKnown = (visited)
/* Has this room been visited? */
visited = nil
/*
* Although we don't define room parts in general, we do give every Room a
* floor so that the parser can refer to objects 'on the ground' when
* asking disambiguation questions. By default we supply every Room with
* the defaultGround MultiLoc object to represent its floor. You can if
* you like replace this with a custom floor object in particular rooms,
* but it's highly recommended that you define your custom floor to be of
* the Floor class. It's also legal to define floorObj as nil on a Room
* that represents an obviously floorless place, such as the top of a mast
* or tree.
*/
floorObj = defaultGround
/*
* When executing travel we move the traveler into the room. Then, if the
* traveler is the player char we perform a look around in the room,
* provided we should look around on entering the room. actor is the actor
* doing the traveling, traveler is the traveler doing the traveling
* (normally the same as actor unless actor is in a Vehicle, in which case
* traveler will be the Vehicle) and conn is the TravelConnector the
* vehicle is traversing in order to reach this room.
*/
execTravel(actor, traveler, conn)
{
/* Note whether we want to look around on entering this room. */
local lookAroundOnEntering = lookOnEnter(actor);
/*
* Note the traveler's current location, so we can check subsequently
* whether travel actually took place.
*/
local oldLoc = traveler.getOutermostRoom();
/*
* Get our destination when starting from oldLoc (for a room this
* should normally evaluate to self)
*/
local dest = getDestination(oldLoc);
/* Carry out the before travel notification */
conn.beforeTravelNotifications(traveler);
/*
* Note the actor's old travel info in case we have to restore it
* after a failed travel attempt.
*/
if(actor != gPlayerChar)
local oldTravelInfo = actor.lastTravelInfo;
if(actor == gPlayerChar)
{
/*
* Before carrying out the travel make a note of the room the
* player character is about to leave.
*/
libGlobal.lastLoc = oldLoc;
}
/*
* Otherwise if the player character can see the actor traverse the
* connector, note the fact on the actor, so that the information is
* available should the player character wish to follow the actor.
*/
else if(Q.canSee(gPlayerChar, actor))
actor.lastTravelInfo = [oldLoc, conn];
/*
* Note that actor is traversing the Travel Connector. This can be
* used to carry out any side-effects of the travel, such as
* describing it.
*/
conn.noteTraversal(actor);
/* Notify the actor's current room that the actor is about to depart. */
oldLoc.notifyDeparture(actor, dest);
/* Move the traveling object into its destination */
traveler.actionMoveInto(dest);
if(gPlayerChar.isOrIsIn(traveler))
{
/*
* Notify any actors in the location that the player character has
* just arrived.
*/
local notifyList = allContents.subset({o: o.ofKind(Actor)});
notifyList.forEach({a: a.pcArrivalTurn = gTurns });
/* Show a room description if appropriate */
if(lookAroundOnEntering)
lookAroundWithin();
}
/*
* Execute the after travel notifications, provided that the actor
* actually ended up in a new location.
*/
if(self != oldLoc)
{
conn.afterTravelNotifications(traveler);
}
/*
* If we're not the player character and we failed to go anywhere,
* restore our old travel info.
*/
if(actor != gPlayerChar && actor.getOutermostRoom == oldLoc)
actor.lastTravelInfo = oldTravelInfo;
}
/*
* Should we look around on entering this room? By default we should; this
* is overridden in senseRegion.t to provide for the possibility of a
* "continuous space" implementation.
*/
lookOnEnter(obj)
{
return true;
}
/* A Room's outermost room is itself. */
getOutermostRoom { return self; }
/*
* The Message to display if travel is disallowed in any given direction
* (because the corresponding direction property of the Room is nil).
*/
cannotGoThatWayMsg = BMsg(cannot go, '{I} {can\'t} go that way. ' )
/*
* The method that is called when travel is attempted in a direction
* (given the dir parameter) for which nothing is defined. By default we
* simply display the cannotGoThatWayMsg followed by a list of exits, but
* this can be overridden if desired, and different responses given for
* different directions. Note that the dir parameter will be passed as a
* direction object. e.g. northDir.
*/
cannotGoThatWay(dir)
{
"<<cannotGoThatWayMsg>>";
if(gExitLister != nil)
gExitLister.cannotGoShowExits(gActor, self);
"<.p>";
}
/*
* The message to display when travel is attempted in the dark, either in
* a direction for which no destination (or other handling) is defined, or
* in a direction in which the exit is not visible in the dark.
*/
cannotGoThatWayInDarkMsg = BMsg(cannot go in dark, 'It{dummy}{\'s} too dark
to see where {i}{\'m} going. ')
/*
* The method that's called when travel is attempted by an undefined or
* invisible exit in the dark. By default we display the
* cannotGoThatWayInDarkMsg followed by a list of visible exits, but game
* code can override this.
*/
cannotGoThatWayInDark(dir)
{
"<<cannotGoThatWayInDarkMsg>><.p>";
if(gExitLister != nil)
gExitLister.cannotGoShowExits(gActor, self);
"<.p>";
}
/*
* Normally we don't allow travel from this location if both it and the
* destination are in darkness. To allow travel from this location in any
* case set allowDarkTravel to true.
*/
allowDarkTravel = nil
/* Call the before action notifications on this room and its regions */
notifyBefore()
{
/* Call our own roomBeforeAction() */
roomBeforeAction();
/*
* Notify all the regions we're in of the action that's about to take
* place.
*/
foreach(local reg in valToList(regions))
reg.notifyBefore();
}
/* Call the after action notifications on this room and its regions */
notifyAfter()
{
/* Call our own roomAfterAction() */
roomAfterAction();
/*
* Notify all the regions we're in of the action that's just taken
* place.
*/
foreach(local reg in valToList(regions))
reg.notifyAfter();
}
/*
* roomBeforeAction and roomAfterAction are called just before and after
* the action phases of the current action. Individual instances can
* override to react to the particular actions. */
roomBeforeAction() { }
roomAfterAction() { }
/*
* beforeTravel(traveler, connector) is called on the room traveler is
* in just as traveler is about to attempt travel via connector (a
* TravelConnector object).
*/
beforeTravel(traveler, connector) { }
/*
* afterTravel(traveler, connector) is called on the room traveler has
* just arrived in via connector.
*/
afterTravel(traveler, connector) { }
/* show the exit list in the status line */
showStatuslineExits()
{
/* if we have a global exit lister, ask it to show the exits */
if (gExitLister != nil)
gExitLister.showStatuslineExits();
}
/* The name of the room as it appears in the status line. */
statusName(actor)
{
local nestedLoc = '';
/*
* If the actor is not directly in the room we add the actor's
* immediate container in parentheses after the room name.
*/
if(!actor.location.ofKind(Room))
nestedLoc = BMsg(actor nested location name,
' (<<actor.location.objInPrep>>
<<actor.location.theName>>)');
/*
* If the Room is illuminated, display its ordinary room title,
* followed by the actor's immediate location if it's not the Room. If
* the Room is in darkness, use the darkName instead of the roomTitle.
*/
if(isIlluminated)
"<<roomTitle>><<nestedLoc>>";
else
"<<darkName>><<nestedLoc>>";
}
/*
* Anything in the Room is deemed to be inside it (this sounds
* tautologous, but it's why we give Room a contType of In).
*/
contType = In
/*
* This method is invoked on the player char's current room at the end of
* every action. By default we run our doScript() method if we're also a
* Script (that is, if the Room has been mixed in with an EventList
* class), thereby facilitating the display of atmospheric messages.
*/
roomDaemon()
{
if(ofKind(Script))
doScript();
}
/*
* This room can optionally be in one or more regions. The regions
* property hold the region or a list of regions I'm in.
*/
regions = nil
/*
* A Room can't be in another Room or a Thing, but it can notionally be in
* a Region, so we check to see if we're in the list of our regions.
*/
isIn(region)
{
return valToList(regions).indexWhich({x: x.isOrIsIn(region)}) != nil;
}
/* Add this room to the room list of all the regions it's in */
addToRegions()
{
foreach(local reg in valToList(regions))
reg.addToRoomList(self);
}
/*
* The list of all the regions this room belongs to. This is calculated
* the first time this property is queried and then stored in the
* property.
*/
allRegions()
{
local ar = getAllRegions();
allRegions = ar;
return ar;
}
/* Calculate a list of all the regions this room belongs to */
getAllRegions()
{
local thisRegions = new Vector(valToList(regions));
foreach(local reg in valToList(regions))
thisRegions.appendUnique(reg.allRegions);
return thisRegions.toList();
}
/* return a list of regions that both this room and other are common to. */
regionsInCommonWith(other)
{
return allRegions.subset({x: x.roomList.indexOf(other) != nil});
}
/*
* Carry out the notifications for a traveler leaving this room to go to
* dest.
*/
notifyDeparture(traveler, dest)
{
/* Notify the current room of the impending departure */
travelerLeaving(traveler, dest);
/*
* Notify any regions the traveler is about to leave of the impending
* departure */
local commonRegs = regionsInCommonWith(dest);
/*
* The regions I'm about to leave are all the regions this room is in
* less those that this room has in common with my destination.
*/
local regsLeft = allRegions - commonRegs;
/*
* Notify all the regions that the traveler is leaving that the
* traveler is leaving to go to dest.
*/
foreach(local reg in regsLeft)
reg.travelerLeaving(traveler, dest);
/*
* The regions I'm about to enter are all the regions the destination
* room is in, less those this room has in common with the
* destination.
*/
local regsEntered = dest.allRegions - commonRegs;
/* Notify any regions I'm about to enter of my impending arrival. */
foreach(local reg in regsEntered)
reg.travelerEntering(traveler, self);
/* notify the destination room of the impending arrival */
dest.travelerEntering(traveler, self);
}
/*
* This method is invoked when traveler is about to leave this room and go
* to dest.
*/
travelerLeaving(traveler, dest) { }
/*
* This method is invoked when traveler is about to enter this room
* from origin.
*/
travelerEntering(traveler, origin) { }
/* A Room has no interiorParent since it's a top-level container. */
interiorParent()
{
return nil;
}
/*
* Add extra items into scope for the action. By default we simply add the
* items from our extraScopeItems list together with those of any regions
* we're it. This allows commonly visible items such as the sky to be
* added to scope in dark outdoor rooms, for instance.
*/
addExtraScopeItems(action)
{
/*
* Append the extra scope items defined on this Room to the action's
* scope list.
*/
action.scopeList =
action.scopeList.appendUnique(valToList(extraScopeItems));
/* Add any extra scope items defined on any regions we're in. */
foreach(local reg in valToList(regions))
reg.addExtraScopeItems(action);
/*
* By default we'll also add our floor object to scope if we have one
* and it isn't already in scope.
*/
if(floorObj != nil)
action.scopeList = action.scopeList.appendUnique([floorObj]);
}
/*
* A list of extra items to be added to scope when an action is carried
* out in this room.
*/
extraScopeItems = []
/* The location at which a Room was last seen is always itself. */
lastSeenAt = (self)
/*
* Convenience method to set information about the destination dirn from
* this room. The dirn parameter should be specified as a direction object
* (e.g. northDir) and the dest parameter as a room. Note this is only
* meaningful for direction properties specified as methods (as opposed to
* Rooms, Doors or other TravelConnectors or as strings), and is only
* useful for priming the route finder at the start of the game before the
* player has tried to go in this direction from this room. Once the
* player tries this direction the dest info table will be overwritten
* with information about where it actually leads.
*/
setDestInfo(dirn, dest)
{
libGlobal.addExtraDestInfo(self, dirn, dest);
}
/*
* The getDirection method returns the direction by which one would need
* to travel from this room to travel via the connector conn (or nil if
* none of the room's direction properties point to conn).
*/
getDirection(conn)
{
for(local dir = firstObj(Direction); dir != nil; dir = nextObj(dir,
Direction))
{
if(propType(dir.dirProp) == TypeObject && self.(dir.dirProp) == conn)
return dir;
}
return nil;
}
/*
* By default we don't want the examineStatus method of a Room to do
* anything except displaying the stateDesc, should we have defined one.
* In particular we don't want it to list the contents of the Room, since
* Looking Around will do this anyway.
*/
examineStatus() { display(&stateDesc); }
/* Examining a Room is the same as looking around within it. */
dobjFor(Examine)
{
action() { lookAroundWithin(); }
}
/* Going out of a Room is the same as executing an OUT command */
dobjFor(GetOutOf)
{
action() { GoOut.execAction(gCommand); }
}
/*
* Pushing an object out of a Room is the same as pushing it via the OUT
* exit.
*/
iobjFor(PushTravelGetOutOf)
{
action()
{
gCommand.verbProd.dirMatch = object { dir = outDir; };
gAction = PushTravelDir;
PushTravelDir.execAction(gCommand);
}
}
/*
* Optional method that returns a single-quoted string explaining why
* target (normally an object in a remote location) cannot be reached from
* this room. By default we just return the target's tooFarAwayMsg but
* this can be overridden, for example, to return the same format of
* message for every target that can't be reached from this room (e.g.
* "You can't reach [the target] from the meadow. ") ]
*/
cannotReachTargetMsg(target)
{
return target.tooFarAwayMsg;
}
;
/*
* A Door is something that can be open and closed (and optionally locked),
* and which must be open to allow travel through. Doors are defined in pairs,
* with each Door representing one side of the door and pointing to the other
* side via its otherSide property. */
class Door: TravelConnector, Thing
/* A door is generally openable */
isOpenable = true
/* Most doors start out closed. */
isOpen = nil
/* Doors generally aren't listed separately in room descriptions. */
isListed = nil
/*
* A door is something fixed in place, not something that can be picked up
* and carried around.
*/
isFixed = true
/*
* By default we leave game authors to decide if and how they want to
* report whether a door is open or closed.
*/
openStatusReportable = nil
/*
* Flag, do we want to attempt to unlock this door via an implicit action
* if someone attempts to open it while it's locked?
*/
autoUnlock = nil
/*
* A physical door is represented by two objects in code, each
* representing one side of the door and each present in one of the two
* locations the door connects. Each side needs to point to the other side
* through its otherSide property.
*/
otherSide = nil
/*
* We're visible in the dark if the room on the other side of us is
* illuminated
*/
visibleInDark
{
if(destination != nil && transmitsLight)
return destination.isIlluminated;
return nil;
}
/* A Door is something it makes sense to go through. */
canGoThroughMe = true
/* Make a Door open (stat = true) or closed (stat = nil) */
makeOpen(stat)
{
/* Carry out the inherited handling. */
inherited(stat);
/*
* If we have an otherSide, make it open or closed at the same time so
* both sides of the Door stay in sync.
*/
if(otherSide != nil)
{
otherSide.isOpen = stat;
if(stat)
otherSide.opened = true;
}
}
/* Make a Door locked (stat = true) or unlocked (stat = nil) */
makeLocked(stat)
{
/* Carry out the inherited handling. */
inherited(stat);
/*
* If we have an otherSide, make it locked or unlocked at the same
* time so both sides of the Door stay in sync.
*/
if(otherSide != nil)
otherSide.isLocked = stat;
}
/*
* The most likely barrier to travel through a door is that the door is
* closed and locked, so we check for than after the other kinds of travel
* barrier.
*/
checkTravelBarriers(traveler)
{
/*
* Carry out the inherited checking of travel barriers and return nil
* if they fail to indicate that travel through the door is not
* possible.
*/
if(inherited(traveler) == nil)
return nil;
/* If the Door isn't open, try to open it via an implicit action. */
if(!isOpen)
{
if(tryImplicitAction(Open, self))
"<<gAction.buildImplicitActionAnnouncement(true)>>";
/*
* If we're not allowed to open this door via an implicit action
* (because opening it is marked as dangerous or nonObvious at the
* verify stage) display a message explaining why the travel can't
* be carried out.
*/
else
{
local obj = self;
gMessageParams(obj);
say(cannotGoThroughClosedDoorMsg);
}
}
/*
* We pass the travel barrier test if and only if the door ends up
* open.
*/
return isOpen;
}
/* Execute travel through this door. */
execTravel(actor, traveler, conn)
{
/*
* Carry out the inherited handling (which delegates most of the work
* to our destination).
*/
inherited(actor, traveler, conn);
/*
* If the actor carrying out the travel is the player character, note
* that the player character now knows where both sides of the door
* lead to.
*/
if(otherSide != nil && actor == gPlayerChar &&
actor.isIn(destination))
{
otherSide.isDestinationKnown = true;
isDestinationKnown = true;
}
}
/*
* The message to display if travel is attempted through this door when
* it's closed and we're not allowed to open it via an implicit action.
*/
cannotGoThroughClosedDoorMsg = BMsg(cannot go through closed door,
'{The subj obj} {is} in the way. ')
/*
* By default the player character doesn't start off knowing where this
* door leads. Once the pc has been through the door in either direction
* this becomes true on both sides of the door.
*/
isDestinationKnown = nil
/* Preinitialize a door */
preinitThing()
{
/* Carry out the inherited handling */
inherited;
/*
* in addition to carrying out Thing's preinitialization, carry out
* some additional housekeeping to ensure that this door is in sync
* with its other side.
*/
if(otherSide == nil)
{
/*
* If the otherSide hasn't been defined and we're compiling for
* debugging, display a warning message.
*/
#ifdef __DEBUG
"WARNING!!! <<theName>> in << getOutermostRoom != nil ?
getOutermostRoom.name : 'nil'>> has no otherside.<.p>";
#endif
}
else
{
/*
* If our otherSide doesn't already point to us, make it do so.
* This allows game authors to get away with only specifying one
* side of the connection.
*/
if(otherSide.otherSide != self)
otherSide.otherSide = self;
/*
* If we've made one side of the door locked, the chances are we
* intend the other side of the door to start out locked too.
*/
if(isLocked)
otherSide.isLocked = true;
/*
* Likewise, if we've made one side of the door open, the chances
* are we intend the other side of the door to start out open too.
*/
if(isOpen)
otherSide.isOpen = true;
/* Add the other side to our list of facets. */
getFacets += otherSide;
}
}
/* The destination is the room to which this door leads. */
destination()
{
/* If we don't have an other side, then we don't lead anywhere. */
if(otherSide == nil)
return nil;
/* Otherwise this door leads to the room containing its other side */
return otherSide.getOutermostRoom;
}
/* Going through a door is the same as traveling via it. */
dobjFor(GoThrough)
{
action() { travelVia(gActor); }
}
/* Entering a door is the same as going through it. */
dobjFor(Enter) asDobjFor(GoThrough)
/*
* The appropriate action for push an object via a door is
* PushTravelThrough
*/
PushTravelVia = PushTravelThrough
/*
* Display message announcing that traveler has left via this door. The
* traveler would normally be an NPC visible to the player character.
*/
sayDeparting(traveler)
{
gMessageParams(traveler);
DMsg(say departing through door, '{The subj traveler} {leaves} through
{1}. ', theName);
}
/*
* Display message announcing that follower is following leader through
* this door.
*/
sayActorFollowing(follower, leader)
{
/* Create message parameter substitutions for the follower and leader */
gMessageParams(follower, leader);
DMsg(say following through door, '{The subj follower} follow{s/ed} {the
leader} through {1}. ', theName);
}
traversalMsg = BMsg(traverse door, 'through {1}', theName)
;
/*
* A TravelConnector is an object that can be attached to the directional
* exit property of a room to facilitate (or optionally block) travel in the
* associated direction and carry out any side-effects of that travel. A
* TravelConnector may be used as an abstract object to implement travel, or
* a subclass of TravelConnector such as Door, Passage, StairwayUp or
* StairwayDown may be used to represent a physical object via which travel
* occurs. The Room class also inherits from TravelConnector.
*
* Whether the base TravelConnector class or one of its subclasses is used,
* travel is carried out via a TravelConnector by calling its travelVia()
* method.
*/
class TravelConnector: object
/*
* Is this connector apparent? That is, would it be apparent to an
* observer under normal lighting conditions, as opposed to being
* concealed? By default we'll suppose a TravelConnector is apparent
* unless it's explicitly hidden.
*/
isConnectorApparent = !isHidden
/*
* Should this exit be shown in the exit lister? By default we'll assumed
* it should be it it's visible.
*/
isConnectorListed = isConnectorVisible
/*
* Does light pass through this TravelConnector from its destination (so
* that it's visible in the dark even its location is dark.).
*/
transmitsLight = true
/*
* A TravelConnector (or at least, the exit it represents) is visible if
* it's apparent (i.e. not concealed in some way) and if the lighting
* conditions are adequate, or if it's visible in the dark.
*/
isConnectorVisible()
{
local loc = gPlayerChar.getOutermostRoom();
local dest = getDestination(loc);
return (isConnectorApparent &&
(loc.isIlluminated
|| (dest != nil && dest.isIlluminated
&& transmitsLight)
|| visibleInDark));
}
/* The room to which this TravelConnector leads when it is traversed */
destination = nil
/*
* The room to which this TravelConnector leads when it is traversed from
* origin.
*/
getDestination(origin)
{
return destination;
}
/*
* Does the player char know where this travel connector leads? By default
* s/he doesn't until s/he's visited its destination, but this could be
* overridden for an area the PC is supposed to know well when the game
* starts, such as their own house.
*/
isDestinationKnown()
{
local loc = gPlayerChar.getOutermostRoom();
local dest = getDestination(loc);
return (dest != nil && dest.isDestinationKnown);
}
/* A travel connector is usually open. */
isOpen = true
/*
* Carrier out travel via this connector, first checking that travel