-
Notifications
You must be signed in to change notification settings - Fork 11
/
misc.t
2736 lines (2346 loc) · 92.5 KB
/
misc.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"
/*
* Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved.
* Adapted for adv3Lite by Eric Eve
*
* adv3Lite Library - miscellaneous definitions
*
* This module contains miscellaneous definitions that don't have a
* natural grouping with any larger modules, and which aren't complex
* enough to justify modules of their own.
*/
/* include the library header */
#include "advlite.h"
/* ------------------------------------------------------------------------ */
/*
* When a call is made to a property not defined or inherited by the
* target object, the system will automatically invoke this method. The
* method will be invoked with a property pointer as its first argument,
* and the original arguments as the remaining arguments. The first
* argument gives the property that was invoked and not defined by the
* object. A typical definition in an object would look like this:
*
* propNotDefined(prop, [args]) { ... }
*
* If this method is not defined by the object, the system simply
* returns nil as the value of the undefined property evaluation or
* method invocation.
*/
property propNotDefined;
export propNotDefined;
/* ------------------------------------------------------------------------ */
/*
* We refer to some properties defined primarily in score.t - that's an
* optional module, though, so make sure the compiler has heard of these.
*/
property calcMaxScore, runScoreNotifier;
/* ------------------------------------------------------------------------ */
/*
* The library base class for the gameMain object.
*
* Each game MUST define an object called 'gameMain' to define how the
* game starts up. You can use GameMainDef as the base class of your
* 'gameMain' object, in which case the only thing you're required to
* specify in your object is the 'initialPlayerChar' property - you can
* inherit everything else from the GameMainDef class if you don't
* require any further customizations.
*/
class GameMainDef: object
/*
* The initial player character. Each game's 'gameMain' object MUST
* define this to refer to the Actor object that serves as the
* initial player character.
*/
initialPlayerChar = nil
/*
* Show the game's introduction. This routine is called by the
* default newGame() just before entering the main command loop. The
* command loop starts off by showing the initial room description,
* so there's no need to do that here.
*
* Most games will want to override this, to show a prologue message
* setting up the game's initial situation for the player. We don't
* show anything by default.
*/
showIntro() { }
/*
* Show the "goodbye" message. This is called after the main command
* loop terminates.
*
* We don't show anything by default. If you want to show a "thanks
* for playing" type of message as the game exits, override this
* routine with the desired text.
*/
showGoodbye() { }
/*
* Begin a new game. This default implementation shows the
* introductory message, calls the main command loop, and finally
* shows the goodbye message.
*
* You can override this routine if you want to customize the startup
* protocol. For example, if you want to create a pre-game options
* menu, you could override this routine to show the list of options
* and process the user's input. If you need only to customize the
* introduction and goodbye messages, you can simply override
* showIntro() and showGoodbye() instead.
*/
newGame()
{
/* Create an action context in case any startup code needs it */
gAction = Look.createInstance();
gActor = initialPlayerChar;
/*
* Show the statusline before we display our introductory. This
* will help minimize redrawing - if we waited until after
* displaying some text, we might have to redraw some of the
* screen to rearrange things for the new screen area taken up by
* the status line, which could be visible to the user. By
* setting up the status line first, we'll probably have less to
* redraw because we won't have anything on the screen yet when
* figuring the layout.
*/
statusLine.showStatusLine();
/* show the introduction */
showIntro();
/* run the game, showing the initial location's full description */
runGame(true);
/* show the end-of-game message */
showGoodbye();
}
/*
* Restore a game and start it running. This is invoked when the
* user launches the interpreter using a saved game file; for
* example, on a Macintosh, this happens when the user double-clicks
* on a saved game file on the desktop.
*
* This default implementation bypasses any normal introduction
* messages: we simply restore the game file if possible, and
* immediately start the game's main command loop. Most games won't
* need to override this, but you can if you need some special effect
* in the restore-at-startup case.
*/
restoreAndRunGame(filename)
{
local succ;
/* mention that we're about to restore the saved position */
DMsg(note main restore, 'Game restored.<.p>');
/* try restoring it */
succ = Restore.startupRestore(filename);
/* show a blank line after the restore result message */
"<.p>";
/* if we were successful, run the game */
if (succ)
{
/*
* Run the command loop. There's no need to show the room
* description, since the RESTORE action will have already
* done so.
*/
runGame(nil);
/* show the end-of-game message */
showGoodbye();
}
}
/*
* Set the interpreter window title, if applicable to the local
* platform. This simply displays a <TITLE> tag to set the title to
* the string found in the versionInfo object.
*/
setGameTitle()
{
/* write the <TITLE> tag with the game's name */
"<title><<versionInfo.name>></title>";
}
/*
* Set up the HTML-mode about-box. By default, this does nothing.
* Games can use this routine to show an <ABOUTBOX> tag, if desired,
* to set up the contents of an about-box for HTML TADS platforms.
*
* Note that an <ABOUTBOX> tag must be re-initialized each time the
* main game window is cleared, so this routine should be called
* again after any call to clearScreen().
*/
setAboutBox()
{
/* we don't show any about-box by default */
}
/*
* Build a saved game metadata table. This returns a LookupTable
* containing string key/value pairs that are stored in saved game
* files, providing descriptive information that can be displayed to
* the user when browsing a collection of save files. This is called
* each time we execute a SAVE command, so that we store the current
* context of the game.
*
* Some interpreters display information from this table when
* presenting the user with a list of files for RESTORE. The
* contents of the table are intentionally open-ended to allow for
* future extensions, but at the moment, the following keys are
* specifically defined (note that capitalization must be exact):
*
* UserDesc - descriptive text entered by the user (this should
* simply be the contents of the 'userDesc' parameter). This is
* treated as ordinary plain text (i.e., no HTML or other markups are
* interpreted in this text).
*
* AutoDesc - descriptive text generated by the game to describe the
* saved position. This text can contain the simple HTML markups
* <b>..</b>, <i>..</i>, and <br> for formatting.
*
* Return nil if you don't want to save any metadata information.
*
* 'userDesc' is an optional string entered by the user via the Save
* Game dialog. Some interpreters let the user enter a description
* for a saved game via the file selector dialog; the descriptive
* text is separate from the filename, and is intended to let the
* user enter a more free-form description than would be allowed in a
* filename. This text, if any, is passed to use via the 'userDesc'
* parameter.
*/
getSaveDesc(userDesc)
{
/* create the lookup table */
local t = new LookupTable();
/* store the user description, if provided */
if (userDesc != nil)
t['UserDesc'] = userDesc;
/* start our auto description with the current room name */
desc = gPlayerChar.outermostVisibleParent().roomTitle + '; ';
/* if we're keeping score, include the score */
if (libGlobal.scoreObj != nil)
desc += toString(libGlobal.scoreObj.totalScore) + ' points in ';
/* add the number of turns so far */
desc += toString(libGlobal.totalTurns) + ' moves';
/* add the auto description */
t['AutoDesc'] = desc;
/* return the table */
return t;
}
/*
* The gameMain object also specifies some settings that control
* optional library behavior. If you want the standard library
* behavior, you can just inherit the default settings from this
* class. Some games might want to select non-default variations,
* though.
*/
/*
* The maximum number of points possible in the game. If the game
* includes the scoring module at all, and this is non-nil, the SCORE
* and FULL SCORE commands will display this value to the player as a
* rough indication of how much farther there is to go in the game.
*
* By default, we initialize this on demand, by calculating the sum
* of the point values of the Achievement objects in the game. The
* game can override this if needed to specify a specific maximum
* possible score, rather than relying on the automatic calculation.
*/
maxScore()
{
local m;
/* ask the score module (if any) to compute the maximum score */
m = (libGlobal.scoreObj != nil
? libGlobal.scoreObj.calcMaxScore : nil);
/* supersede this initializer with the calculated value */
maxScore = m;
/* return the result */
return m;
}
/*
* The score ranking list - this provides a list of names for
* various score levels. If the game provides a non-nil list here,
* the SCORE and FULL SCORE commands will show the rank along with
* the score ("This makes you a Master Adventurer").
*
* This is a list of score entries. Each score entry is itself a
* list of two elements: the first element is the minimum score for
* the rank, and the second is a string describing the rank. The
* ranks should be given in ascending order, since we simply search
* the list for the first item whose minimum score is greater than
* our score, and use the preceding item. The first entry in the
* list would normally have a minimum of zero points, since it
* should give the initial, lowest rank.
*
* If this is set to nil, which it is by default, we'll simply skip
* score ranks entirely.
*/
scoreRankTable = nil
/*
* If this flag is true then room description listings and examine
* listings use a parenthetical style to show subcontents (e.g. "On the
* table you see a box (in which is a brass key)") instead of showing each
* item and its contents in a separate paragraph.
*/
useParentheticalListing = nil
/*
* If this flag is true then room description listings will include a
* paragraph break between each set of subcontents listings (i.e. the
* listing of the contents of each item in the room that has visible
* contents). If it is nil the subcontents listings will all be run into a
* single paragraph. Note that the global setting defined here can be
* overridden on individual rooms.
*/
paraBrksBtwnSubcontents = true
/*
* Option flag: allow ALL to be used for every verb. This is true by
* default, which means that players will be allowed to use ALL with
* any command - OPEN ALL, EXAMINE ALL, etc.
*
* Some authors don't like to allow players to use ALL with so many
* verbs, because they think it's a sort of "cheating" when players
* try things like OPEN ALL. This option lets you disable ALL for
* most verbs; if you set this to nil, only the basic inventory
* management verbs (TAKE, TAKE FROM, DROP, PUT IN, PUT ON) will
* allow ALL, and other verbs will simply respond with an error
* ("'All' isn't allowed with that verb").
*
* If you're writing an especially puzzle-oriented game, you might
* want to set this to nil. It's a trade-off though, as some people
* will think your game is less player-friendly if you disable ALL.
*/
allVerbsAllowAll = true
/*
* Should the "before" notifications (beforeAction, roomBeforeAction, and
* actorAction) run before or after the "check" phase?
*
* In many ways it's more logical and useful to run "check" first. That
* way, you can consider the action to be more or less committed by the
* time the "before" notifiers are invoked. Of course, a command is never
* truly* committed until it's actually been executed, since a "before"
* handler could always cancel it. But this is relatively rare - "before"
* handlers usually carry out side effects, so it's very useful to be able
* to know that the command has already passed all of its own internal
* checks by the time "before" is invoked - that way, you can invoke side
* effects without worrying that the command will subsequently fail.
*/
beforeRunsBeforeCheck = nil
/*
* Flag, should this game be in the past tense. By default the game is in
* the present tense.
*
* For a wider selection of tenses override Narrator.tense instead.
*/
usePastTense = nil
/*
* The AGAIN command could be interpreted in two different ways. It could
* repeat the resolved action (using precisely the same objects as
* before), or it could act as if the player had retyped the command and
* then parse it again from scratch (which might result in a different
* interpretation of the command, or different objects being selected).
* The former interpretation is used if againRepeatsParse is nil; the
* latter if it's true.
*/
againRepeatsParse = true
/*
* Flag. If this is true the game attempts to switch the againRepeatsParse
* flag between true and nil to give the contextually better
* interpretation of AGAIN. This should be regarded as somewhat
* experimental for now.
*/
autoSwitchAgain = true
/*
* Is this game in verbose mode? By default we make it so, but players can
* change this with the BRIEF/TERSE command.
*/
verbose = true
/*
* Is this game in fast GO TO mode? By default we make it not, so that the
* GO TO command moves the player character only one step of the way at a
* time, but if this is set to true the GO TO command will keep moving the
* player until either the destination is reached or an obstacle is
* encountered.
*/
fastGoTo = nil
;
/* ------------------------------------------------------------------------ */
/*
* Clear the main game window. In most cases, you should call this
* rather than calling the low-level clearScreen() function directly,
* since this routine takes care of a couple of chores that should
* usually be done at the same time.
*
* First, we flush the transcript to ensure that no left-over reports
* that were displayed before we cleared the screen will show up on the
* new screen. Second, we call the low-level clearScreen() function to
* actually clear the display window. Finally, we re-display any
* <ABOUTBOX> tag, to ensure that the about-box will still be around;
* this is necessary because any existing <ABOUTBOX> tag is lost after
* the screen is cleared.
*/
cls()
{ /* clear the screen */
aioClearScreen();
}
/* ------------------------------------------------------------------------ */
/*
* Determine if the given object overrides the definition of the given
* property inherited from the given base class. Returns true if the
* object derives from the given base class, and the object's definition
* of the property comes from a different place than the base class's
* definition of the property.
*/
overrides(obj, base, prop)
{
return (obj.ofKind(base)
&& (obj.propDefined(prop, PropDefGetClass)
!= base.propDefined(prop, PropDefGetClass)));
}
/* ------------------------------------------------------------------------ */
/*
* Library Pre-Initializer. This object performs the following
* initialization operations immediately after compilation is completed:
*
* - adds each defined Thing to its container's contents list
*
* - adds each defined Sense to the global sense list
*
* This object is named so that other libraries and/or user code can
* create initialization order dependencies upon it.
// */
adv3LibPreinit: PreinitObject
execute()
{
/* set the initial player character, as specified in gameMain */
gPlayerChar = gameMain.initialPlayerChar;
/*
* Attach the command sequencer output filter, the
* language-specific message parameter substitution filter, the
* style tag formatter filter, and the paragraph filter to the
* main output stream. Stack them so that the paragraph manager
* is at the bottom, since the library tag filter can produce
* paragraph tags and thus needs to sit atop the paragraph
* filter. Put the command sequencer above those, since it
* might need to write style tags. Finally, put the sense
* context filter on top of those.
*/
mainOutputStream.addOutputFilter(typographicalOutputFilter);
mainOutputStream.addOutputFilter(mainParagraphManager);
mainOutputStream.addOutputFilter(styleTagFilter);
mainOutputStream.addOutputFilter(cquoteOutputFilter);
mainOutputStream.addOutputFilter(commandSequencer);
/*
* Attach our message parameter filter and style tag filter to
* the status line streams. We don't need most of the main
* window's filters in the status line.
*/
statusTagOutputStream.addOutputFilter(styleTagFilter);
statusLeftOutputStream.addOutputFilter(styleTagFilter);
statusLeftOutputStream.addOutputFilter(cquoteOutputFilter);
statusRightOutputStream.addOutputFilter(styleTagFilter);
}
;
//
/* ------------------------------------------------------------------------ */
/*
* Library Initializer. This object performs the following
* initialization operations each time the game is started:
*
* - sets up the library's default output function
*/
adv3LibInit: InitObject
execute()
{
/*
* Set up our default output function. Note that we must do
* this during run-time initialization each time we start the
* game, rather than during pre-initialization, because the
* default output function state is not part of the load-image
* configuration.
*/
t3SetSay(say);
}
;
/* ------------------------------------------------------------------------ */
/*
* Generic script object. This class can be used to implement a simple state
* machine.
*
* We define Script in misc.t rather than eventList.t so that other parts of
* the library can safely test whether something is ofKind(Script) even it
* eventList.t is not present. The various types and subclasses of script are
* defined in eventList.t to allow them to be optionally excluded from the
* build if they're not needed in a particular game.
*/
class Script: object
/*
* Get the current state. This returns a value that gives the
* current state of the script, which is usually simply an integer.
*/
getScriptState()
{
/* by default, return our state property */
return curScriptState;
}
/*
* Process the next step of the script. This routine must be
* overridden to perform the action of the script. This routine's
* action should call getScriptState() to get our current state, and
* should update the internal state appropriately to take us to the
* next step after the current one.
*
* By default, we don't do anything at all.
*/
doScript()
{
/* override to carry out the script */
}
/*
* Property giving our current state. This should never be used
* directly; instead, getScriptState() should always be used, since
* getScriptState() can be overridden so that the state depends on
* something other than this internal state property. The meaning of
* the state identifier is specific to each subclass.
*/
curScriptState = 0
;
/* ------------------------------------------------------------------------ */
/*
* Library global variables
*/
libGlobal: object
/*
* The current library messages object. This is the source object
* for messages that don't logically relate to the actor carrying out
* the comamand. It's mostly used for meta-command replies, and for
* text fragments that are used to construct descriptions.
*
* This message object isn't generally used for parser messages or
* action replies - most of those come from the objects given by the
* current actor's getParserMessageObj() or getActionMessageObj(),
* respectively.
*
* By default, this is set to libMessages. The library never changes
* this itself, but a game can change this if it wants to switch to a
* new set of messages during a game. (If you don't need to change
* messages during a game, but simply want to customize some of the
* default messages, you don't need to set this variable - you can
* simply use 'modify libMessages' instead. This variable is
* designed for cases where you want to *dynamically* change the
* standard messages during the game.)
*/
libMessageObj = libMessages
/*
* The current player character. If it hasn't already been defined, we call getPlayerChar to
* identify it then set ourself to the value it returns.
*/
playerChar()
{
/* Get our player character */
local pc = getPlayerChar();
/* Set ourself to the player character */
playerChar = pc;
/* Return the player character. */
return pc;
}
/* The name of the current player character */
playerCharName = nil
/*
* The global score object. We use a global for this, rather than
* referencing libScore directly, to allow the score module to be
* left out entirely if the game doesn't make use of scoring. The
* score module should set this during pre-initialization.
*/
scoreObj = nil
/*
* The global Footnote class object. We use a global for this,
* rather than referencing Footnote directly, to allow the footnote
* module to be left out entirely if the game doesn't make use of
* footnotes. The footnote class should set this during
* pre-initialization.
*/
footnoteClass = nil
/* the total number of turns so far */
totalTurns = 0
/*
* flag: the parser is in 'debug' mode, in which it displays the
* parse tree for each command entered
*/
parserDebugMode = nil
/*
* Most recent command, for 'undo' purposes. This is the last
* command the player character performed, or the last initial
* command a player directed to an NPC.
*
* Note that if the player directed a series of commands to an NPC
* with a single command line, only the first command on such a
* command line is retained here, because it is only the first such
* command that counts as a player's turn in terms of the game
* clock. Subsequent commands are executed by the NPC's on the
* NPC's own time, and do not count against the PC's game clock
* time. The first command counts against the PC's clock because of
* the time it takes the PC to give the command to the NPC.
*/
lastCommandForUndo = ''
/*
* Most recent target actor phrase; this goes with
* lastCommandForUndo. This is nil if the last command did not
* specify an actor (i.e., was implicitly for the player character),
* otherwise is the string the player typed specifying a target
* actor.
*/
lastActorForUndo = ''
/* The text of the last command to be repeated by Again */
lastCommandForAgain = ''
/*
* Current command information. We keep track of the current
* command's actor and action here.
*/
curActor = nil
curIssuingActor = nil
curAction = nil
/* The current Command object */
curCommand = nil
/* The last action to be performed. */
lastAction = nil
/* The previous Command object */
lastCommand = nil
/* the exitLister object, if included in the build */
exitListerObj = nil
/* the hint manager, if included in the build */
hintManagerObj = nil
/* the extra hint manager, if included in the build */
extraHintManagerObj = nil
/*
* The game's IFID, as defined in the game's main module ID object.
* If the game has multiple IFIDs in the module list, this will store
* only the first IFID in the list. NOTE: the library initializes
* this automatically during preinit; don't set this manually.
*/
IFID = nil
/*
* Command line arguments. The library sets this to a list of
* strings containing the arguments passed to the program on the
* command line. This list contains the command line arguments
* parsed according to the local conventions for the operating system
* and C++ library. The standard parsing procedure used by most
* systems is to break the line into tokens delimited by space
* characters. Many systems also allow space characters to be
* embedded in tokens by quoting the tokens. The first argument is
* always the name of the .t3 file currently executing.
*/
commandLineArgs = []
/*
* Retrieve a "switch" from the command line. Switches are options
* specifies with the conventional Unix "-xxx" notation. This
* searches for a command option that equals the given string or
* starts with the given substring. If we find it, we return the
* part of the option after the given substring - this is
* conventionally the value of the switch. For example, the command
* line might look like this:
*
*. t3run mygame.t3 -name=MyGame -user=Bob
*
* Searching for '-name=' would return 'MyGame', and searching for
* '-user=' would return' Bob'.
*
* If the switch is found but has no value attached, the return value
* is an empty string. If the switch isn't found at all, the return
* value is nil.
*/
getCommandSwitch(s)
{
/* search from argument 2 to the last switch argument */
local args = commandLineArgs;
for (local i in 2..args.length())
{
/*
* if this isn't a switch, or is the special "-" last switch
* marker, we're done
*/
local a = args[i];
if (!a.startsWith('-') || a == '-')
return nil;
/* check for a match */
if (a.startsWith(s))
return a.substr(s.length() + 1);
}
/* didn't find it */
return nil;
}
/*
* The last location visited by the player char before a travel action.
* Noted to allow travel back.
*/
lastLoc = nil
/*
* A lookup table to store information about the destinations of direction
* properties not connected to objects (i.e. direction properties defined
* as strings or methods
*/
extraDestInfo = static [ * -> unknownDest_ ]
/*
* Add an item to the extraDestInfo table keyed on the source room plus
* the direction taken, with the value being the destination arrived at
* (which most of the time will probably be the same as the source, since
* in most cases where we create one of these records, no travel will have
* taken place.
*/
addExtraDestInfo(source, dirn, dest)
{
if(extraDestInfo == nil)
extraDestInfo = [ * -> unknownDest_ ];
/*
* Record the extra dest info in the extraDestInfo table unless it's
* already set to nil, which is a signal that we don't want the
* pathfinder or other code to use this information.
*/
if(extraDestInfo[[source, dirn]] not in (nil, varDest_))
extraDestInfo[[source, dirn]] = dest;
}
/*
* Flag: do we want revealing something (through setRevealed or <.reveal> to update PC and NPC
* knowledge? By default we do, but games that want to strictly separate Player and Player
* Character knowledge may wish to set this to nil.
*/
informOnReveal = true
/*
* Mark a tag as revealed. This adds an entry for the tag to the revealedNameTab table and to
* the informedNamedTab of the player character (who might, in principle, change during the
* course of the game). We simply set the table entry to 'true'; the presence of the tag in
* the table constitutes the indication that the tag has been revealed.
*
* (Games and library extensions can use 'modify' to override this and store more information
* in the table entry. For example, you could store the time when the information was first
* revealed, or the location where it was learned. If you do override this, just be sure to
* set the revealedNameTab entry for the tag to a non-nil and non-zero value, so that any code
*
testing the presence of the table entry will see that the slot is indeed set.)
*
* We put the revealedNameTab table and the setRevealed method here rather than on
* conversationManager so that it's available to games that don't include actor.t.
*/
setRevealed(tag, arg?)
{
local val = revealedNameTab[tag];
/* We don't want to overwrite an existing value by accident. */
if(val == nil)
/* Add the tag to our revealedNameTab */
revealedNameTab[tag] = (arg == nil ? true : arg);
/*
* We also don't want to set an existing value to nil; an arg of nil means leave the
* existing value alone
.*/
else if(arg != nil)
revealedNameTab[tag] = arg;
/* If we're in a conversation, update the last fact mentioned. */
if(gPlayerChar.currentInterlocutor)
lastFactMentioned = tag;
/*
* Add the tag to the playerCharacter's informedNameTab, provided we want revealing to
* update PC and NPC knowledge.
*/
if(informOnReveal)
gPlayerChar.setInformed(tag, arg);
}
/*
* Do we want the getRevealed(tag) method to return only true or nil, or do we want it to
* return the value associated with tag in the revealedNameTab. By default we opt for the
* latter, but we provide the other option in case it's needed for backward compatibility.
*/
revealedTrueOrFalseOnly = nil
/*
* The same option is provided for getInformed(); by default we use the same value as for
* revealedTrueOrFalseOnly
*/
informedTrueOrFalseOnly = revealedTrueOrFalseOnly
/* Get the value associated with tag. */
getRevealed(tag)
{
/* Obtained the value associated with tag in the revealedNameTab */
local val = revealedNameTab[tag];
/*
* Return eiher whether val is not nil, or val itself, according to the value of
* revealedTrueOrFalseOnly.
*/
return revealedTrueOrFalseOnly ? (val != nil) : val;
}
/*
* Mark a tag as unrevealed. This removes the entry for the tag from our
* revealedNameTab table.
*
* We put the revealedNameTab table and the setRevealed method here rather
* than on conversationManager so that it's available to games that don't
* include actor.t.
*/
setUnrevealed(tag)
{
revealedNameTab.removeElement(tag);
// local tab = gPlayerChar.informedNameTab;
//
// if(tab)
// tab.removeElement[tag];
}
/*
* The global lookup table of all revealed keys. This table is keyed
* by the string naming the revelation; the value associated with
* each key is not used (we always just set it to true).
*/
revealedNameTab = static new LookupTable(32, 32)
/*
* The symbol table for every game object.
*/
objectNameTab = nil
/* The thought manager object, if it exists. */
thoughtManagerObj = nil
/* The object last written on */
lastWrittenOnObj = nil
/* The object last typed on */
lastTypedOnObj = nil
/* The last (latest) topic mentioned in the current conversation. */
lastTopicMentioned = nil
/* The last fact mentioned in the current conversation */
lastFactMentioned = nil
/* The most recent reason a ConvAgendaItem was invoked. */
reasonInvoked = nil
/* The most recently active agendaItem */
agendaItem = nil
/*
* our name table for parameter substitutions - a LookupTable that we set
* up during preinit
*/
nameTable_ = static new LookupTable()
/*
* Flag determining whether inventory listing should be in the wide (nil) or tall (true)
* format. By default we start out with the wide format (inventoryTall = nil), although game
* code could override this.
*/
inventoryTall = nil
/*
* Flag: do we wish to present the player with an enumerated list of disambiguation options
* (e.g. "Which coin do you mean:(1) the gold coin or (2) the silver coin? to which they can
* simply reply 1 or 2). By default we do but game authors can disable this behaviour by
* setting this flag to nil.
*/
enumerateDisambigOptions = true
/*
* The current number of disambiguation options to choose from. This is for use by the
* DisambigPreparser to prevent acceptance of a number out of range.
*/
disambigLen = 0
/*
* A list of objects in the game with alternating vocabulary. This is maintained and used by
* the library and shouldn't normally be changed by game code.
*/
altVocabLst = []
;
/* object representing an unknown destination */
unknownDest_: Room 'unknown'
;
/* object representing a variable destination */
varDest_: Room 'unknown'
;
/* ------------------------------------------------------------------------ */
/*
* FinishType objects are used in finishGameMsg() to indicate what kind
* of game-over message to display. We provide a couple of standard
* objects for the most common cases.
*/
class FinishType: object
/* the finishing message, as a string or library message property */
finishMsg = nil
;
/* 'death' - the game has ended due to the player character's demise */
ftDeath: FinishType finishMsg = BMsg(finish death, 'YOU HAVE DIED');
/* 'victory' - the player has won the game */
ftVictory: FinishType finishMsg = BMsg(finish victory,'YOU HAVE WON');
/* 'failure' - the game has ended in failure (but not necessarily death) */
ftFailure: FinishType finishMsg = BMsg(finish failure, 'YOU HAVE FAILED');
/* 'game over' - the game has simply ended */
ftGameOver: FinishType finishMsg = BMsg(finish game over, 'GAME OVER');
/*
* Finish the game, showing a message explaining why the game has ended.
* This can be called when an event occurs that ends the game, such as
* the player character's death, winning, or any other endpoint in the
* story.
*
* We'll show a message defined by 'msg', using a standard format. The