1
2(********************************************************************)
3(*                                                                  *)
4(*  wiz.sd7       Find treasures and fight monsters labyrinth game  *)
5(*  Copyright (C) 1990 - 1994, 2004, 2007, 2008  Thomas Mertes      *)
6(*                2011, 2013, 2021  Thomas Mertes                   *)
7(*                                                                  *)
8(*  This program is free software; you can redistribute it and/or   *)
9(*  modify it under the terms of the GNU General Public License as  *)
10(*  published by the Free Software Foundation; either version 2 of  *)
11(*  the License, or (at your option) any later version.             *)
12(*                                                                  *)
13(*  This program is distributed in the hope that it will be useful, *)
14(*  but WITHOUT ANY WARRANTY; without even the implied warranty of  *)
15(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *)
16(*  GNU General Public License for more details.                    *)
17(*                                                                  *)
18(*  You should have received a copy of the GNU General Public       *)
19(*  License along with this program; if not, write to the           *)
20(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
21(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
22(*                                                                  *)
23(********************************************************************)
24
25
26$ include "seed7_05.s7i";
27  include "keybd.s7i";
28  include "console.s7i";
29  include "editline.s7i";
30  include "wrinum.s7i";
31
32const integer: SIZE_LABY is 8;
33const integer: NUM_LEVELS is 8;
34const integer: RESTRICTED_CONNECTION_COUNT is 5;
35const integer: STAIRS_PER_LEVEL is 3;
36const integer: THINGS_PER_LEVEL is 1;
37const integer: VENDORS_PER_LEVEL is 1;
38const integer: OCCURRENCES_PER_LEVEL is 1;
39const integer: TRANSFERS_PER_LEVEL is 1;
40const integer: ARMOR_STRENGTH_FACTOR is 7;
41
42
43const type: speciesType is new enum
44    HOBBIT, ELF, HUMAN, DWARF
45  end enum;
46
47const func string: str (in speciesType: species) is
48  return [] ("hobbit", "elf", "human", "dwarf")[succ(ord(species))];
49
50enable_output(speciesType);
51
52
53const type: directType is new enum
54    NORTH, SOUTH, EAST, WEST, UP, DOWN
55  end enum;
56
57const func string: str (in directType: direct) is
58  return [] ("north", "south", "east", "west", "up", "down")[succ(ord(direct))];
59
60enable_output(directType);
61
62const type: directSet is set of directType;
63
64
65const type: objectType is new enum
66    NOOBJECT, LAMP, RUBY, NORNSTONE, PEARL, OPAL, GREENGEM, BLUEFLAME,
67    PALANTIR, SILMARIL, RUNESTAFF, ORBOFZOT
68  end enum;
69
70const func string: str (in objectType: anObject) is
71  return [] ("nothing", "lamp", "ruby red", "norn Stone", "pale pearl",
72             "opal Eye", "green Gem", "blue Flame", "Palantir", "Silmaril",
73             "Runestaff", "*ORB OF ZOT*")[succ(ord(anObject))];
74
75enable_output(objectType);
76
77const type: objectSet is set of objectType;
78
79const objectSet: treasureSet is {RUBY, NORNSTONE, PEARL, OPAL, GREENGEM,
80                                 BLUEFLAME, PALANTIR, SILMARIL};
81
82
83const type: animateType is new enum
84    NOBODY, KOBOLD, ORC, WOLF, GOBLIN, OGRE, TROLL, BEAR,
85    MINOTAUR, GARGOYLE, CHIMERA, BALROG, DRAGON, VENDOR
86  end enum;
87
88const func string: str (in animateType: anAnimate) is
89  return [] ("nobody", "kobold", "orc", "wolf", "goblin", "ogre", "troll",
90             "bear", "minotaur", "gargoyle", "chimera", "balrog", "dragon",
91             "vendor")[succ(ord(anAnimate))];
92
93enable_output(animateType);
94
95
96const type: armorType is new enum
97    NOARMOR, LEATHER, CHAINMAIL, PLATE
98  end enum;
99
100const func string: str (in armorType: anArmor) is
101  return [] ("no", "leather", "chainmail", "plate") [succ(ord(anArmor))];
102
103enable_output(armorType);
104
105
106const type: weaponType is new enum
107    NOWEAPON, DAGGER, MACE, SWORD
108  end enum;
109
110const func string: str (in weaponType: aWeapon) is
111  return [] ("no", "dagger", "mace", "sword") [succ(ord(aWeapon))];
112
113enable_output(weaponType);
114
115
116const type: commandType is new enum
117    ILLEGAL, GO_NORTH, GO_SOUTH, GO_EAST, GO_WEST, GO_UP, GO_DOWN, WAIT,
118    INVENTORY, HELP, LOOK, MAP, FLARE, USE_LAMP, ATTACK, CAST, BRIBE,
119    STATUS, OPEN, READ, GAZE, TELEPORT, DRINK, SELL, BUY, QUITCOMMAND
120  end enum;
121
122const type: contentType is new enum
123    EMPTYROOM, ENTRANCE, EMPTYCHEST, CHESTWITHSKELETON, CLOSEDCHEST, ORB,
124    POOL, BOOK
125  end enum;
126
127const type: transferType is new enum
128    NOTRANSFER, SINKHOLE, WARP
129  end enum;
130
131const type: occurType is new enum
132    NOOCCURRENCE, LEECH, LETHARGY, FORGET, STEALARMOR, STEALWEAPON,
133    STEALLAMP, STEALFLARES, STEALTREASURE, FINDGOLD, FINDFLARES
134  end enum;
135
136const type: playerType is new struct
137    var speciesType: species is HUMAN;
138    var boolean: isMale is TRUE;
139    var integer: strength is 2;
140    var integer: intelligence is 8;
141    var integer: dexterity is 14;
142    var integer: goldPieces is 10;
143    var armorType: armor is NOARMOR;
144    var integer: armorStrength is 0;
145    var weaponType: weapon is NOWEAPON;
146    var boolean: weaponBlocked is FALSE;
147    var integer: flares is 0;
148    var objectSet: possession is objectSet.value;
149    var integer: turns is 1;
150    var integer: mealHour is 0;
151    var boolean: living is TRUE;
152    var boolean: blind is FALSE;
153    var boolean: haveLeech is FALSE;
154    var boolean: lethargic is FALSE;
155    var boolean: forgetting is FALSE;
156    var boolean: leaveCastle is FALSE;
157    var boolean: quitDialog is FALSE;
158    var boolean: quitProgram is FALSE;
159  end struct;
160
161const type: fightStateType is new struct
162    var integer: monsterCount is NUM_LEVELS * 12;
163    var boolean: angryVendors is FALSE;
164    var integer: webCount is 0;
165    var integer: aggressionOfMonster is 0;
166    var integer: monsterStrength is 0;
167    var boolean: monsterPresent is FALSE;
168    var boolean: monsterWillAttack is FALSE;
169    var boolean: bribed is FALSE;
170  end struct;
171
172const type: roomType is new struct
173    var directSet: connections is {NORTH, SOUTH, EAST, WEST};
174    var transferType: transfer is NOTRANSFER;
175    var occurType: occurrence is NOOCCURRENCE;
176    var animateType: roomer is NOBODY;
177    var contentType: contents is EMPTYROOM;
178    var objectSet: objects is objectSet.value;
179    var boolean: visited is FALSE;
180    var integer: xPos is 0;
181    var integer: yPos is 0;
182    var integer: zPos is 0;
183  end struct;
184
185const type: roomRef is sub object interface;
186
187type_implements_interface(roomType, roomRef);
188
189const proc: writePos (in roomRef: aRoom) is DYNAMIC;
190const proc: enterRoom (inout roomRef: currentRoom, inout playerType: player,
191                       inout fightStateType: fightState) is DYNAMIC;
192const proc: incident (inout roomRef: currentRoom,
193                      inout playerType: player) is DYNAMIC;
194const proc: teleportTo (inout roomRef: currentRoom, inout playerType: player,
195                        inout fightStateType: fightState) is DYNAMIC;
196const proc: writeFightState (in roomRef: currentRoom, inout playerType: player,
197                             inout fightStateType: fightState) is DYNAMIC;
198const proc: executeCommand (inout roomRef: currentRoom, inout playerType: player,
199                            inout fightStateType: fightState) is DYNAMIC;
200const proc: removeFromRoom (inout roomRef: currentRoom,
201                            in objectType: treasure) is DYNAMIC;
202
203var integer: labyrinthNumber is 0;
204
205var roomRef: currentRoomRef is roomType.value;
206
207const type: objPlaceType is array [objectType] roomRef;
208var objPlaceType: objPlace is objectType times roomType.value;
209
210const type: labyrinthType is array array array roomType;
211var labyrinthType: labyrinth is SIZE_LABY times SIZE_LABY times NUM_LEVELS times roomType.value;
212
213
214const func integer: rangeLaby (in integer: number) is
215  return succ(pred(number) mod SIZE_LABY);
216
217
218const func integer: rangeLevel (in integer: number) is
219  return succ(pred(number) mod NUM_LEVELS);
220
221
222const func integer: range18 (in integer: number) is
223  return number <= 18 ? number: 18;
224
225
226const func speciesType: rand (attr speciesType) is
227  return rand(speciesType.first, speciesType.last);
228
229
230const func animateType: rand (attr animateType) is
231  return rand(KOBOLD, DRAGON);
232
233
234const proc: startText is func
235  begin
236    writeln;
237    writeln;
238    writeln;
239    writeln("*" mult 78);
240    writeln;
241    writeln("                       * * * THE WIZARD'S CASTLE * * *");
242    writeln("                                 Version 2.0");
243    writeln;
244    writeln("                                  Copyright");
245    writeln("               1990 - 1994, 2004, 2007, 2008, 2011, 2013, 2021");
246    writeln("                                Thomas  Mertes");
247    writeln;
248    writeln("*" mult 78);
249    writeln;
250    writeln(" A long time ago, in the age of the old universal empire  the  mighty  wizard");
251    writeln(" ZOT  lived  in  a  large  subterranean  castle, collecting a lot of fabulous");
252    writeln(" treasures during his long life. Feeling  the  sources  of  his  vital  power");
253    writeln(" draining  away, he created a great orb of power the *ORB OF ZOT*. To hide it");
254    writeln(" from the cretins of the  surface  beyond,  he  hired  a  group  of  esurient");
255    writeln(" monsters to guard the *ORB OF ZOT*. From that time onward, many a bold youth");
256    writeln(" has ventured into the castle, losing his  life  in  cruel  and  unimaginable");
257    writeln(" ways.");
258    writeln;
259    writeln("All right, bold one.");
260  end func; # startText
261
262
263const proc: writeHelp is func
264  begin
265    writeln;
266    writeln("*** WIZARD'S CASTLE COMMAND AND INFORMATION SUMMARY ***");
267    writeln;
268    writeln("The following commands are available:");
269    writeln;
270    writeln("H/ELP       N/ORTH      S/OUTH      E/AST       W/EST       U/P         D/OWN");
271    writeln("L/OOK       I/NVENTORY  M/AP        ST/ATUS     A/TTACK     C/AST       BR/IBE");
272    writeln("O/PEN       R/EAD       G/AZE       T/ELEPORT   DR/INK      SE/LL       B/UY");
273    writeln("F/LARE      LA/AMP      Q/UIT");
274    writeln;
275    writeln("The contents of rooms are as follows:");
276    writeln;
277    writeln(". = EMPTY ROOM   D = WAY DOWN     G = GOLD PIECES  P = MAGIC POOL   U = WAY UP");
278    writeln("B = BOOK         E = EXIT         M = MONSTER      S = SINKHOLE     V = VENDOR");
279    writeln("C = CHEST        F = FLARES       O = CRYSTAL ORB  T = TREASURE     W = WARP");
280    writeln;
281    writeln("The benefits of having treasures are:");
282    writeln;
283    writeln("ruby red -    AVOID LETHARGY     pale pearl -  AVOID LEECH");
284    writeln("green gem -   AVOID FORGETTING   opal eye -    CURES BLINDNESS");
285    writeln("blue flame -  DISSOLVES BOOKS    norn stone -  NO BENEFIT");
286    writeln("palantir -    NO BENEFIT         silmaril -    NO BENEFIT");
287  end func; # writeHelp
288
289
290const proc: randomRoom (inout integer: xPos, inout integer: yPos,
291    inout integer: zPos) is func
292  begin
293    xPos := rand(1, SIZE_LABY);
294    yPos := rand(1, SIZE_LABY);
295    zPos := rand(1, NUM_LEVELS);
296  end func; # randomRoom
297
298
299const proc: findUninhabitedRoom (inout integer: xPos, inout integer: yPos,
300    in integer: zPos) is func
301  begin
302    repeat
303      xPos := rand(1, SIZE_LABY);
304      yPos := rand(1, SIZE_LABY);
305    until labyrinth[xPos][yPos][zPos].roomer = NOBODY and
306          labyrinth[xPos][yPos][zPos].contents <> ENTRANCE;
307  end func; # findUninhabitedRoom
308
309
310const proc: findEmptyRoom (inout integer: xPos, inout integer: yPos,
311    in integer: zPos) is func
312  begin
313    repeat
314      xPos := rand(1, SIZE_LABY);
315      yPos := rand(1, SIZE_LABY);
316    until labyrinth[xPos][yPos][zPos].transfer = NOTRANSFER and
317          labyrinth[xPos][yPos][zPos].roomer = NOBODY and
318          labyrinth[xPos][yPos][zPos].contents = EMPTYROOM and
319          labyrinth[xPos][yPos][zPos].occurrence = NOOCCURRENCE and
320          labyrinth[xPos][yPos][zPos].objects = objectSet.value;
321  end func; # findEmptyRoom
322
323
324const proc: initRoom (inout roomType: aRoom, in integer: xPos, in integer: yPos,
325    in integer: zPos) is func
326  begin
327    aRoom := roomType.value;
328    aRoom.xPos := xPos;
329    aRoom.yPos := yPos;
330    aRoom.zPos := zPos;
331  end func; # initRoom
332
333
334const proc: initEntrance (inout roomType: aRoom) is func
335  begin
336    aRoom.connections := {NORTH, SOUTH, EAST, WEST};
337    aRoom.contents := ENTRANCE;
338    aRoom.visited := TRUE;
339  end func; # initEntrance
340
341
342const proc: initRoomConnections is func
343  local
344    const array directSet: restrictedConnections is [] (
345        {       SOUTH, EAST, WEST},
346        {NORTH,        EAST, WEST},
347        {NORTH, SOUTH,       WEST},
348        {NORTH, SOUTH, EAST      },
349        {NORTH, SOUTH            },
350        {NORTH,        EAST      },
351        {NORTH,              WEST},
352        {       SOUTH, EAST      },
353        {       SOUTH,       WEST},
354        {              EAST, WEST});
355    var directSet: connections is directSet.value;
356    var integer: count is 0;
357    var integer: xPos is 0;
358    var integer: yPos is 0;
359    var integer: zPos is 0;
360  begin
361    for xPos range 1 to SIZE_LABY do
362      for yPos range 1 to SIZE_LABY do
363        for zPos range 1 to NUM_LEVELS do
364          initRoom(labyrinth[xPos][yPos][zPos], xPos, yPos, zPos);
365        end for;
366        write(".");
367        flush(OUT);
368      end for;
369    end for;
370    writeln;
371    for count range 1 to RESTRICTED_CONNECTION_COUNT do
372      for connections range restrictedConnections do
373        randomRoom(xPos, yPos, zPos);
374        labyrinth[xPos][yPos][zPos].connections := connections;
375      end for;
376    end for;
377    for zPos range 1 to NUM_LEVELS do
378      xPos := rand(1, SIZE_LABY);
379      yPos := rand(1, SIZE_LABY);
380      labyrinth[xPos][yPos][zPos].connections := {rand(NORTH, WEST)};
381    end for;
382    initEntrance(labyrinth[rangeLaby(4)][1][1]);
383    for zPos range 1 to pred(NUM_LEVELS) do
384      for count range 1 to STAIRS_PER_LEVEL do
385        xPos := rand(1, SIZE_LABY);
386        yPos := rand(1, SIZE_LABY);
387        incl(labyrinth[xPos][yPos][zPos].connections, DOWN);
388        incl(labyrinth[xPos][yPos][succ(zPos)].connections, UP);
389      end for;
390    end for;
391  end func; # initRoomConnections
392
393
394const proc: initRoomProperties is func
395  local
396    var integer: count is 0;
397    var integer: xPos is 0;
398    var integer: yPos is 0;
399    var integer: zPos is 0;
400    var contentType: content is EMPTYROOM;
401    var animateType: animate is NOBODY;
402    var objectType: treasure is NOOBJECT;
403    var occurType: occurrence is NOOCCURRENCE;
404  begin
405    for zPos range 1 to NUM_LEVELS do
406      for count range 1 to THINGS_PER_LEVEL do
407        for content range [](CLOSEDCHEST, ORB, POOL, BOOK) do
408          findEmptyRoom(xPos, yPos, zPos);
409          labyrinth[xPos][yPos][zPos].contents := content;
410        end for;
411      end for;
412    end for;
413    for zPos range 1 to NUM_LEVELS do
414      for animate range KOBOLD to DRAGON do
415        findUninhabitedRoom(xPos, yPos, zPos);
416        labyrinth[xPos][yPos][zPos].roomer := animate;
417      end for;
418      for count range 1 to VENDORS_PER_LEVEL do
419        findUninhabitedRoom(xPos, yPos, zPos);
420        labyrinth[xPos][yPos][zPos].roomer := VENDOR;
421      end for;
422    end for;
423    for treasure range treasureSet do
424      randomRoom(xPos, yPos, zPos);
425      incl(labyrinth[xPos][yPos][zPos].objects, treasure);
426      objPlace[treasure] := labyrinth[xPos][yPos][zPos];
427    end for;
428    for zPos range 1 to NUM_LEVELS do
429      for count range 1 to OCCURRENCES_PER_LEVEL do
430        for occurrence range FINDGOLD to FINDFLARES do
431          xPos := rand(1, SIZE_LABY);
432          yPos := rand(1, SIZE_LABY);
433          labyrinth[xPos][yPos][zPos].occurrence := occurrence;
434        end for;
435      end for;
436    end for;
437    for occurrence range LEECH to STEALTREASURE do
438      randomRoom(xPos, yPos, zPos);
439      labyrinth[xPos][yPos][zPos].occurrence := occurrence;
440    end for;
441  end func; # initRoomProperties
442
443
444const proc: initRoomTransfers is func
445  local
446    var integer: count is 0;
447    var integer: xPos is 0;
448    var integer: yPos is 0;
449    var integer: zPos is 0;
450  begin
451    zPos := rand(1, NUM_LEVELS);
452    findUninhabitedRoom(xPos, yPos, zPos);
453    labyrinth[xPos][yPos][zPos].roomer := rand(animateType);
454    incl(labyrinth[xPos][yPos][zPos].objects, RUNESTAFF);
455    objPlace[RUNESTAFF] := labyrinth[xPos][yPos][zPos];
456    zPos := rand(1, NUM_LEVELS);
457    findEmptyRoom(xPos, yPos, zPos);
458    labyrinth[xPos][yPos][zPos].transfer := WARP;
459    labyrinth[xPos][yPos][zPos].objects := {ORBOFZOT};
460    objPlace[ORBOFZOT] := labyrinth[xPos][yPos][zPos];
461    for zPos range 1 to NUM_LEVELS do
462      for count range 1 to TRANSFERS_PER_LEVEL do
463        findEmptyRoom(xPos, yPos, zPos);
464        labyrinth[xPos][yPos][zPos].transfer := SINKHOLE;
465        findEmptyRoom(xPos, yPos, zPos);
466        labyrinth[xPos][yPos][zPos].transfer := WARP;
467      end for;
468    end for;
469  end func; # initRoomTransfers
470
471
472const func string: aOrAn (in string: word) is func
473  result
474    var string: wordWithIndefiniteArticle is "";
475  begin
476    if word <> "" then
477      if upper(word[1]) in {'A', 'E', 'I', 'O', 'U'} then
478        wordWithIndefiniteArticle := "an " & word;
479      else
480        wordWithIndefiniteArticle := "a " & word;
481      end if;
482    end if;
483  end func; # aOrAn
484
485
486const proc: DECLARE_A_OR_AN (in type: aType) is func
487  begin
488    const func string: aOrAn (in aType: aValue) is
489      return aOrAn(str(aValue));
490  end func;
491
492DECLARE_A_OR_AN(speciesType);
493DECLARE_A_OR_AN(weaponType);
494DECLARE_A_OR_AN(animateType);
495
496
497const func string: anyFood is
498  return rand([]("sandwiches", "stew", "soup", "burgers", "roast",
499                 "filet", "tace", "pie"));
500
501
502const func string: anyAdjective is
503  return rand([]("a large", "a strange", "an ugly", "an enormous",
504                 "a forbidding", "a horrible", "an exotic",
505                 "an ordinary"));
506
507
508const func string: numberName (in integer: number) is
509  return number <= 20 ? str(ENGLISH, number) : str(number);
510
511
512const func string: sexName (in boolean: isMale) is
513  return isMale ? "male" : "female";
514
515
516const func string: titleName (in boolean: isMale) is
517  return isMale ? "sir" : "madam";
518
519
520const func integer: countOwnedObjects (in playerType: player) is
521  return card(player.possession);
522
523
524const func integer: countOwnedTreasures (in playerType: player) is
525  return card(player.possession & treasureSet);
526
527
528const func objectType: ownedTreasure (in playerType: player) is
529  return rand(player.possession & treasureSet);
530
531
532const func integer: treasureNumber (in objectType: treasure) is
533  return succ(ord(treasure) - ord(RUBY));
534
535
536const proc: removeFromRoom (inout roomType: currentRoom,
537    in objectType: treasure) is func
538  begin
539    excl(currentRoom.objects, treasure);
540  end func; # removeFromRoom
541
542
543const proc: writePos (in roomType: aRoom) is func
544  begin
545    writeln("(" <& aRoom.xPos <& ", " <& aRoom.yPos <& ") Level: " <& aRoom.zPos);
546  end func; # writePos
547
548
549const proc: findGoldPieces (inout playerType: player, in integer: limit) is func
550  local
551    var integer: goldPieces is 0;
552  begin
553    goldPieces := rand(2, limit);
554    player.goldPieces +:= goldPieces;
555    writeln(numberName(goldPieces) <& " gold pieces!");
556    writeln("You now have " <& numberName(player.goldPieces) <& " GP'S.");
557  end func; # findGoldPieces
558
559
560const proc: findFlares (inout playerType: player, in integer: limit) is func
561  local
562    var integer: flares is 0;
563  begin
564    flares := rand(2, limit);
565    player.flares +:= flares;
566    writeln(numberName(flares) <& " flares. You now have " <&
567            numberName(player.flares) <& " flares.");
568  end func; # findFlares
569
570
571const func string: roomAdjective (in integer: roomId) is
572  return [0]("", "luxurious ", "expensive ", "wonderful ", "good ",
573             "worn out ", "fine ", "moss-grown ", "old ", "figured ",
574             "patterned ")[roomId rem 11];
575
576
577const func integer: roomIdInLevel (in roomType: aRoom) is
578  return 64 * pred(labyrinthNumber) + 8 * pred(aRoom.xPos) + pred(aRoom.yPos);
579
580
581const func integer: roomId (in roomType: aRoom) is
582  return 8 * roomIdInLevel(aRoom) + pred(aRoom.zPos);
583
584
585const proc: writeRoomDescription (in roomType: aRoom) is func
586  local
587    var integer: roomId is 0;
588  begin
589    if aRoom.contents = ENTRANCE then
590      writeln("the entrance hall. To the North the castle can be left.");
591    else
592      roomId := roomId(aRoom);
593      case roomId rem 23 of
594        when { 0}: write("a ");
595        when { 1}: write("a gigantic ");
596        when { 2}: write("a large ");
597        when { 3}: write("a long ");
598        when { 4}: write("a small ");
599        when { 5}: write("a narrow ");
600        when { 6}: write("a tiny ");
601        when { 7}: write("a round ");
602        when { 8}: write("an octagonal ");
603        when { 9}: write("a hexagonal ");
604        when {10}: write("a whitewashed ");
605        when {11}: write("a blue painted ");
606        when {12}: write("a black painted ");
607        when {13}: write("a red painted ");
608        when {14}: write("a green painted ");
609        when {15}: write("a brown painted ");
610        when {16}: write("a cold ");
611        when {17}: write("a windy ");
612        when {18}: write("a draughty ");
613        when {19}: write("an antique ");
614        when {20}: write("an old ");
615        when {21}: write("a dusty ");
616        when {22}: write("a dilapidated ");
617      end case;
618      case roomId rem 7 of
619        when {0}: write("dome ");
620        when {1}: write("hall ");
621        when {2}: write("room ");
622        when {3}: write("chamber ");
623        when {4}: write("corridor ");
624        when {5}: write("cavern ");
625        when {6}: write("tunnel ");
626      end case;
627      case roomId rem 17 of
628        when { 0}: write("with wooden planking.");
629        when { 1}: write("blasted out of the rock.");
630        when { 2}: write("that must have been a cellar.");
631        when { 3}: write("with all walls made out of bricks.");
632        when { 4}: write("that has a massive pillar in the centre.");
633        when { 5}: write("with an enormous chandelier hanging down.");
634        when { 6}: write("which has a lot of paintings on the walls.");
635        when { 7}: write("which is totally made from rustless steel.");
636        when { 8}: write("with lots of statues high above your head.");
637        when { 9}: write("with " <& aOrAn(roomAdjective(roomId)) <& "parquet.");
638        when {10}: write("with " <& aOrAn(roomAdjective(roomId)) <&
639                         "stone-floor.");
640        when {11}: write("with " <& aOrAn(roomAdjective(roomId)) <&
641                         "rug lying on the floor.");
642        when {12}: write("with " <& aOrAn(roomAdjective(roomId)) <&
643                         "fresco at the wall.");
644        when {13}: write("with " <& roomAdjective(roomId) <&
645                         "walls made from granite.");
646        when {14}: write("with " <& roomAdjective(roomId) <&
647                         "panelling at the walls.");
648        when {15}: write("with " <& roomAdjective(roomId) <&
649                         "paintings at the ceiling.");
650        when {16}: write("with a floor made out of " <&
651                         roomAdjective(roomId) <& "marble.");
652      end case;
653      writeln;
654    end if;
655  end func; # writeRoomDescription
656
657
658const proc: writeConnections (in roomType: aRoom) is func
659  local
660    var integer: roomIdInLevel is 0;
661    var integer: numConnections is 0;
662    var directType: direction is NORTH;
663  begin
664    if UP in aRoom.connections or
665        DOWN in aRoom.connections then
666      roomIdInLevel := roomIdInLevel(aRoom);
667      case roomIdInLevel rem 9 of
668        when {0}: write("At one wall are steps ");
669        when {1}: write("Here is a shaky rope ladder ");
670        when {2}: write("Here is a forbidding staircase ");
671        when {3}: write("Here you find a steel-ladder ");
672        when {4}: write("Here you find a very old staircase ");
673        when {5}: write("There is a narrow spiral staircase ");
674        when {6}: write("There is a rotten ladder ");
675        when {7}: write("There you find a wooden staircase ");
676        when {8}: write("There you find stone steps ");
677      end case;
678      case roomIdInLevel rem 2 of
679        when {0}: write("going ");
680        when {1}: write("leading ");
681      end case;
682      if UP in aRoom.connections then
683        if DOWN in aRoom.connections then
684          write("up and down");
685        else
686          write("up");
687        end if;
688      else
689        write("down");
690      end if;
691      case roomId(aRoom) rem 5 of
692        when {0}: writeln(" into deep darkness.");
693        when {1}: writeln(" into darkness.");
694        when {2}: writeln("ward into darkness.");
695        when {3}: writeln("ward.");
696        when {4}: writeln(".");
697      end case;
698    end if;
699    numConnections := card(aRoom.connections - {UP, DOWN});
700    if numConnections = 1 or numConnections = 2 then
701      write("The room can " <&
702             card(aRoom.connections) = 1 ? "only " : "" <&
703            "be left to the ");
704      for direction range NORTH to WEST do
705        if direction in aRoom.connections then
706          write(direction);
707          decr(numConnections);
708          if numConnections = 1 then
709            write(" and ");
710          end if;
711        end if;
712      end for;
713      writeln(".");
714    elsif numConnections = 3 then
715      write("There is no way to the ");
716      for direction range NORTH to WEST do
717        if direction not in aRoom.connections then
718          write(direction);
719        end if;
720      end for;
721      writeln(".");
722    end if;
723  end func; # writeConnections
724
725
726const proc: writeThings (in roomType: aRoom) is func
727  begin
728    case aRoom.contents of
729      when {EMPTYCHEST}:        writeln("Here is an empty chest.");
730      when {CHESTWITHSKELETON}: writeln("Here is an open chest with a skeleton in it.");
731      when {CLOSEDCHEST}:       writeln("Here is a chest.");
732      when {ORB}:               writeln("Here is a crystal orb.");
733      when {POOL}:              writeln("Here is a pool.");
734      when {BOOK}:              writeln("Here is a book.");
735    end case;
736  end func; # writeThings
737
738
739const proc: writeAnimates (in roomType: aRoom) is func
740  begin
741    if aRoom.roomer <> NOBODY then
742      writeln("In this room is " <& aOrAn(aRoom.roomer) <& ".");
743    end if;
744  end func; # writeAnimates
745
746
747const proc: writeObjects (in roomType: aRoom) is func
748  local
749    var integer: count is 0;
750    var integer: number is 0;
751    var objectType: obj is NOOBJECT;
752  begin
753    if aRoom.roomer = NOBODY then
754      count := card(aRoom.objects);
755      if count > 0 then
756        write("This room contains ");
757        for obj range aRoom.objects do
758          incr(number);
759          if number = 1 then
760            write("the ");
761          elsif number = count then
762            write(" and the ");
763          else
764            write(", the ");
765          end if;
766          write(obj);
767        end for;
768        writeln(".");
769      end if;
770    end if;
771  end func; # writeObjects
772
773
774const proc: writeRoomDetails (in roomType: currentRoom) is func
775  begin
776    writeConnections(currentRoom);
777    writeThings(currentRoom);
778    writeAnimates(currentRoom);
779    writeObjects(currentRoom);
780  end func; # writeRoomDetails
781
782
783const proc: look (inout roomType: currentRoom, in playerType: player) is func
784  begin
785    writeln;
786    write("You are in ");
787    if player.blind then
788      writeln("a room.");
789    else
790      writeRoomDescription(currentRoom);
791    end if;
792    writeRoomDetails(currentRoom);
793  end func; # look
794
795
796const func char: readChar is func
797  result
798    var char: ch is '\0;';
799  local
800    var string: stri is "";
801  begin
802    stri := upper(getln(IN));
803    if stri <> "" then
804      ch := stri[1];
805    end if;
806  end func; # readChar
807
808
809const func char: readChoice is func
810  result
811    var char: ch is ' ';
812  begin
813    writeln;
814    write("Your choice? ");
815    ch := readChar();
816  end func; # readChoice
817
818
819const proc: readNumber (inout integer: number, inout boolean: okay,
820    inout boolean: quit) is func
821  local
822    var string: stri is "";
823  begin
824    number := 0;
825    okay := TRUE;
826    stri := upper(getln(IN));
827    if stri <> "" then
828      if stri = "Q" then
829        okay := FALSE;
830        quit := TRUE;
831      else
832        block
833          number := integer(stri);
834        exception
835          catch RANGE_ERROR:
836            okay := FALSE;
837        end block;
838      end if;
839    end if;
840  end func; # readNumber
841
842
843const proc: readSpecies (inout playerType: player) is func
844  local
845    var boolean: okay is TRUE;
846  begin
847    if not player.quitDialog then
848      repeat
849        okay := TRUE;
850        writeln;
851        writeln("You may be an elf, dwarf, man, or hobbit.");
852        case readChoice() of
853          when {'H'}: player.species := HOBBIT;
854          when {'E'}: player.species := ELF;
855          when {'M'}: player.species := HUMAN;
856          when {'D'}: player.species := DWARF;
857          when {'Q'}: player.quitDialog := TRUE;
858          otherwise:
859            okay := FALSE;
860            writeln;
861            writeln("** That was incorrect. Please type E, D, M, H or Q.");
862        end case;
863      until okay;
864    end if;
865  end func; # readSpecies
866
867
868const proc: readSex (inout playerType: player) is func
869  local
870    var boolean: okay is TRUE;
871  begin
872    if not player.quitDialog then
873      repeat
874        okay := TRUE;
875        writeln;
876        write("Which sex do you prefer? ");
877        case readChar() of
878          when {'M'}: player.isMale := TRUE;
879          when {'F'}: player.isMale := FALSE;
880          when {'Q'}: player.quitDialog := TRUE;
881          otherwise:
882            okay := FALSE;
883            writeln;
884            writeln("** Cute " <& player.species <& ", real cute. Try M, F or Q.");
885        end case;
886      until okay;
887    end if;
888  end func; # readSex
889
890
891const proc: riseAttribute (inout playerType: player, in string: attrName,
892    inout integer: attribute, inout integer: otherPoints) is func
893  local
894    var integer: points is 0;
895    var boolean: okay is TRUE;
896  begin
897    repeat
898      writeln;
899      write("How many points do you wish to add to your " <& attrName <& "? ");
900      readNumber(points, okay, player.quitDialog);
901      if not player.quitDialog then
902        if okay then
903          if points > otherPoints then
904            writeln;
905            writeln("** Dear " <& player.species <& ", you have only " <&
906                    numberName(otherPoints) <& " point" <&
907                    otherPoints <> 1 ? "s." : ".");
908            okay := FALSE;
909          end if;
910        else
911          writeln;
912          writeln("** Would you please be so kind to type a number or q, " <&
913                  player.species <& ".");
914        end if;
915      end if;
916    until okay or player.quitDialog;
917    if okay then
918      attribute +:= points;
919      otherPoints -:= points;
920    end if;
921  end func; # riseAttribute
922
923
924const proc: readAttributes (inout playerType: player) is func
925  local
926    var integer: otherPoints is 0;
927  begin
928    if not player.quitDialog then
929      player.strength := 4 + 2 * ord(player.species);
930      player.intelligence := 8;
931      player.dexterity := 12 - 2 * ord(player.species);
932      if player.species = HOBBIT then
933        otherPoints := 4;
934      else
935        otherPoints := 8;
936      end if;
937      writeln;
938      writeln("Ok, " <& player.species <& ", you have the following attributes:");
939      writeln("STRENGTH = " <& player.strength <&
940              "  INTELLIGENCE = " <& player.intelligence <&
941              "  DEXTERITY = " <& player.dexterity);
942      writeln("and " <& otherPoints <& " other points to allocate as you wish.");
943      riseAttribute(player, "strength", player.strength, otherPoints);
944      if not player.quitDialog and otherPoints > 0 then
945        riseAttribute(player, "intelligence", player.intelligence, otherPoints);
946        if not player.quitDialog and otherPoints > 0 then
947          riseAttribute(player, "dexterity", player.dexterity, otherPoints);
948        end if;
949      end if;
950      if not player.quitDialog and otherPoints > 0 then
951        writeln;
952        write("I am sure that you can never use the saved ");
953        if otherPoints = 1 then
954          writeln("point.");
955        else
956          writeln(numberName(otherPoints) <& " points.");
957        end if;
958      end if;
959    end if;
960  end func; # readAttributes
961
962
963const proc: buyAttribute (inout playerType: player, in string: attrName,
964    inout integer: attribute, in integer: price) is func
965  local
966    var char: ch is ' ';
967    var boolean: okay is TRUE;
968  begin
969    if not player.quitDialog and player.goldPieces >= price then
970      repeat
971        repeat
972          okay := TRUE;
973          writeln;
974          writeln("Your " <& attrName <& " is now " <& attribute <&
975                  " and you have " <& player.goldPieces <& " GP'S.");
976          write("Do you want to buy a potion of " <& attrName <&
977                " for " <& price <& " GP'S? ");
978          ch := readChar();
979          case ch of
980            when {'Y'}:
981              player.goldPieces -:= price;
982              attribute := range18(attribute + rand(1, 6));
983            when {'N'}: noop;
984            when {'Q'}:
985              player.quitDialog := TRUE;
986            otherwise:
987              okay := FALSE;
988              writeln;
989              writeln("** Please answer Y, N or Q.");
990          end case;
991        until okay;
992      until ch <> 'Y' or player.goldPieces < price;
993      if ch <> 'Q' and player.goldPieces < price then
994        writeln;
995        writeln("Your " <& attrName <& " is now " <& attribute <& ".");
996      end if;
997    end if;
998  end func; # buyAttribute
999
1000
1001const proc: buyArmor (inout playerType: player, in integer: priceOfPlate,
1002    in integer: priceOfChainmail, in integer: priceOfLeather) is func
1003  local
1004    var char: ch is ' ';
1005    var boolean: okay is TRUE;
1006  begin
1007    if not player.quitDialog and player.goldPieces >= priceOfLeather then
1008      writeln;
1009      writeln("Ok, " <& player.species <& ", you have " <& player.goldPieces <&
1010              " gold pieces (GP'S) and " <& player.armor <& " armor.");
1011      repeat
1012        okay := TRUE;
1013        writeln("These are the types of armor you can buy:");
1014        writeln;
1015        if player.goldPieces >= priceOfPlate then
1016          writeln("  P/LATE     " <& priceOfPlate lpad 4 <& " GP'S");
1017        end if;
1018        if player.goldPieces >= priceOfChainmail then
1019          writeln("  C/HAINMAIL " <& priceOfChainmail lpad 4 <& " GP'S");
1020        end if;
1021        writeln("  L/EATHER   " <& priceOfLeather lpad 4 <& " GP'S");
1022        writeln("  N/OTHING      0 GP'S");
1023        ch := readChoice();
1024        if  (ch <> 'P' or player.goldPieces < priceOfPlate) and
1025            (ch <> 'C' or player.goldPieces < priceOfChainmail) and
1026             ch not in {'L', 'N', 'Q'} then
1027          okay := FALSE;
1028          writeln;
1029          writeln("** Are you " <& aOrAn(player.species) <& " or " <&
1030                  aOrAn(rand(animateType)) <& "?");
1031          writeln;
1032        end if;
1033      until okay;
1034      case ch of
1035        when {'P'}:
1036          player.armor := PLATE;
1037          player.armorStrength := ord(player.armor) * ARMOR_STRENGTH_FACTOR;
1038          player.goldPieces -:= priceOfPlate;
1039        when {'C'}:
1040          player.armor := CHAINMAIL;
1041          player.armorStrength := ord(player.armor) * ARMOR_STRENGTH_FACTOR;
1042          player.goldPieces -:= priceOfChainmail;
1043        when {'L'}:
1044          player.armor := LEATHER;
1045          player.armorStrength := ord(player.armor) * ARMOR_STRENGTH_FACTOR;
1046          player.goldPieces -:= priceOfLeather;
1047        when {'N'}: noop;
1048        when {'Q'}:
1049          player.quitDialog := TRUE;
1050      end case;
1051    end if;
1052  end func; # buyArmor
1053
1054
1055const proc: buyWeapon (inout playerType: player, in integer: priceOfSword,
1056    in integer: priceOfMace, in integer: priceOfDagger) is func
1057  local
1058    var char: ch is ' ';
1059    var boolean: okay is TRUE;
1060  begin
1061    if not player.quitDialog and player.goldPieces >= priceOfDagger then
1062      writeln;
1063      write("Ok, ");
1064      if player.armor <> NOARMOR then
1065        write("bold ");
1066      end if;
1067      write(player.species <& ", you have " <& player.goldPieces <&
1068            " GP'S left and ");
1069      if player.weapon = NOWEAPON then
1070        writeln("no weapon.");
1071      else
1072        writeln(aOrAn(player.weapon) <& ".");
1073      end if;
1074      repeat
1075        okay := TRUE;
1076        writeln("These are the types of weapons you can buy:");
1077        writeln;
1078        if player.goldPieces >= priceOfSword then
1079          writeln("  S/WORD     " <& priceOfSword lpad 4 <& " GP'S");
1080        end if;
1081        if player.goldPieces >= priceOfMace then
1082          writeln("  M/ACE      " <& priceOfMace lpad 4 <& " GP'S");
1083        end if;
1084        writeln("  D/AGGER    " <& priceOfDagger lpad 4 <& " GP'S");
1085        writeln("  N/OTHING      0 GP'S");
1086        ch := readChoice();
1087        if  (ch <> 'S' or player.goldPieces < priceOfSword) and
1088            (ch <> 'M' or player.goldPieces < priceOfMace) and
1089             ch not in {'D', 'N', 'Q'} then
1090          okay := FALSE;
1091          writeln;
1092          writeln("** Is your intelligence really " <&
1093                  numberName(player.intelligence) <& "?");
1094          writeln;
1095        end if;
1096      until okay;
1097      case ch of
1098        when {'S'}:
1099          player.weapon := SWORD;
1100          player.goldPieces -:= priceOfSword;
1101        when {'M'}:
1102          player.weapon := MACE;
1103          player.goldPieces -:= priceOfMace;
1104        when {'D'}:
1105          player.weapon := DAGGER;
1106          player.goldPieces -:= priceOfDagger;
1107        when {'N'}: noop;
1108        when {'Q'}:
1109          player.quitDialog := TRUE;
1110      end case;
1111    end if;
1112  end func; # buyWeapon
1113
1114
1115const proc: buyLamp (inout playerType: player, in integer: price) is func
1116  local
1117    var boolean: okay is TRUE;
1118  begin
1119    if not player.quitDialog and LAMP not in player.possession and
1120        player.goldPieces >= price then
1121      repeat
1122        okay := TRUE;
1123        writeln;
1124        write("Do you want to buy a lamp for " <& price <& " GP'S? ");
1125        case readChar() of
1126          when {'Y'}:
1127            if price > 10 then
1128              writeln;
1129              writeln("It's guaranteed to outlive you.");
1130            end if;
1131            incl(player.possession, LAMP);
1132            player.goldPieces -:= price;
1133          when {'N'}: noop;
1134          when {'Q'}:
1135            player.quitDialog := TRUE;
1136          otherwise:
1137            okay := FALSE;
1138            writeln;
1139            writeln("** Please answer Y, N or Q.");
1140        end case;
1141      until okay;
1142    end if;
1143  end func; # buyLamp
1144
1145
1146const proc: buyFlares (inout playerType: player) is func
1147  local
1148    var integer: flares is 0;
1149    var boolean: okay is TRUE;
1150  begin
1151    if not player.quitDialog and player.goldPieces >= 1 then
1152      writeln;
1153      writeln("Ok, " <& player.species <& ", you have " <& player.goldPieces <&
1154              " GP'S left.");
1155      repeat
1156        writeln;
1157        write("Five flares cost 1 GP. How many do you want? ");
1158        readNumber(flares, okay, player.quitDialog);
1159        if not player.quitDialog then
1160          if okay then
1161            if flares > 5 * player.goldPieces then
1162              writeln;
1163              writeln("** You can afford only " <& 5 * player.goldPieces <& ".");
1164              okay := FALSE;
1165            end if;
1166          else
1167            writeln;
1168            writeln("** If you don't want any, just type 0 (zero).");
1169          end if;
1170        end if;
1171      until okay or player.quitDialog;
1172      if okay then
1173        player.flares +:= flares;
1174        player.goldPieces -:= (flares + 4) div 5;
1175      end if;
1176    end if;
1177  end func; # buyFlares
1178
1179
1180const proc: buyTreasures (inout playerType: player) is func
1181  local
1182    var objectType: treasure is NOOBJECT;
1183    var integer: treasureNumber is 0;
1184    var integer: price is 0;
1185    var boolean: okay is TRUE;
1186  begin
1187    for treasure range treasureSet do
1188      if not player.quitDialog and treasure not in player.possession then
1189        treasureNumber := treasureNumber(treasure);
1190        price := 125 * treasureNumber + rand(1, 250) +
1191                 250 * pred(rand(1, treasureNumber));
1192        if price <= player.goldPieces then
1193          repeat
1194            okay := TRUE;
1195            writeln;
1196            write("Do you want to buy the " <& treasure <&
1197                  " for " <& price <& " GP'S? ");
1198            case readChar() of
1199              when {'Y'}:
1200                removeFromRoom(objPlace[treasure], treasure);
1201                incl(player.possession, treasure);
1202                player.goldPieces -:= price;
1203              when {'N'}: noop;
1204              when {'Q'}:
1205                player.quitDialog := TRUE;
1206              otherwise:
1207                okay := FALSE;
1208                writeln;
1209                writeln("** Please answer Y, N or Q.");
1210            end case;
1211          until okay;
1212        end if;
1213      end if;
1214    end for;
1215  end func; # buyTreasures
1216
1217
1218const proc: buy (in roomType: currentRoom, inout playerType: player) is func
1219  begin
1220    if currentRoom.roomer <> VENDOR then
1221      writeln;
1222      writeln("** You can only buy from a vendor!");
1223    elsif player.goldPieces < 100 then
1224      writeln;
1225      case rand(1, 14) of
1226        when { 1}: writeln("I do not play with funny money.");
1227        when { 2}: writeln("You need more gold pieces to trade.");
1228        when { 3}: writeln("You haven't got enough cash on hand.");
1229        when { 4}: writeln("Earn money and then come and try again.");
1230        when { 5}: writeln("You need hard currency to trade with me.");
1231        when { 6}: writeln("In capitalism real money is needed for trading.");
1232        when { 7}: writeln("Your dungeon express card -  You left home without it.");
1233        when { 8}: writeln("I don't give alms, " <& player.species <& ".");
1234        when { 9}: writeln("You're too poor to trade, " <& player.species <& ".");
1235        when {10}: writeln("I don't trade with a beggar, " <& player.species <& ".");
1236        when {11}: writeln("Even " <& aOrAn(rand(animateType)) <&
1237                           " knows that money is needed for trading.");
1238        when {12}: writeln("With " <& numberName(player.goldPieces) <&
1239                           " GP'S no trade can be done.");
1240        when {13}: writeln("It's typical for " <& sexName(player.isMale) <&
1241                           " " <& player.species <&
1242                           "s, that they want to trade without enough money.");
1243        when {14}: writeln("Sorry " <& titleName(player.isMale) <&
1244                           ", I'm afraid I don't give credit.");
1245      end case;
1246    else
1247      player.quitDialog := FALSE;
1248      buyArmor(player, 200, 150, 125);
1249      buyWeapon(player, 200, 150, 125);
1250      buyAttribute(player, "strength",     player.strength,     100);
1251      buyAttribute(player, "intelligence", player.intelligence, 100);
1252      buyAttribute(player, "dexterity",    player.dexterity,    100);
1253      buyLamp(player, 100);
1254      buyTreasures(player);
1255    end if;
1256  end func; # buy
1257
1258
1259const proc: sellTreasures (inout roomType: currentRoom,
1260    inout playerType: player) is func
1261  local
1262    var objectType: treasure is NOOBJECT;
1263    var integer: treasureNumber is 0;
1264    var integer: price is 0;
1265    var boolean: okay is TRUE;
1266  begin
1267    for treasure range treasureSet | {RUNESTAFF, ORBOFZOT} do
1268      if not player.quitDialog and treasure in player.possession then
1269        treasureNumber := treasureNumber(treasure);
1270        price := rand(1, 150) + 150 * pred(rand(1, treasureNumber));
1271        repeat
1272          okay := TRUE;
1273          writeln;
1274          write("Do you want to sell the " <& treasure <&
1275                " for " <& price <& " GP'S? ");
1276          case readChar() of
1277            when {'Y'}:
1278              excl(player.possession, treasure);
1279              incl(currentRoom.objects, treasure);
1280              objPlace[treasure] := currentRoom;
1281              player.goldPieces +:= price;
1282            when {'N'}: noop;
1283            when {'Q'}:
1284              player.quitDialog := TRUE;
1285            otherwise:
1286              okay := FALSE;
1287              writeln;
1288              writeln("** Please answer Y, N or Q.");
1289          end case;
1290        until okay;
1291      end if;
1292    end for;
1293  end func; # sellTreasures
1294
1295
1296const proc: sell (inout roomType: currentRoom, inout playerType: player) is func
1297  begin
1298    if currentRoom.roomer <> VENDOR then
1299      writeln;
1300      writeln("** You can only sell to a vendor!");
1301    elsif countOwnedTreasures(player) = 0 then
1302      writeln;
1303      writeln("** You have nothing to offer!");
1304    else
1305      player.quitDialog := FALSE;
1306      sellTreasures(currentRoom, player);
1307    end if;
1308  end func; # sell
1309
1310
1311const proc: checkArmor (inout playerType: player, in integer: strike) is func
1312  local
1313    var integer: damage is 0;
1314  begin
1315    if player.armor = NOARMOR then
1316      damage := strike;
1317    else
1318      if strike < ord(player.armor) then
1319        damage := 0;
1320        player.armorStrength -:= strike;
1321      else
1322        damage := strike - ord(player.armor);
1323        player.armorStrength -:= ord(player.armor);
1324      end if;
1325      if player.armorStrength < 0 then
1326        player.armorStrength := 0;
1327        player.armor := NOARMOR;
1328        writeln;
1329        writeln("YOUR ARMOR HAS BEEN DESTROYED . . . GOOD LUCK!");
1330      end if;
1331    end if;
1332    player.strength -:= damage;
1333  end func; # checkArmor
1334
1335
1336const proc: vendorDies (in roomType: currentRoom,
1337    inout playerType: player) is func
1338  local
1339    var objectType: treasure is NOOBJECT;
1340  begin
1341    writeln;
1342    writeln("You get all his wares:");
1343    writeln("  plate armor");
1344    player.armor := PLATE;
1345    player.armorStrength := ord(player.armor) * ARMOR_STRENGTH_FACTOR;
1346    writeln("  a sword");
1347    player.weapon := SWORD;
1348    writeln("  a strength potion");
1349    player.strength := range18(player.strength + rand(1, 6));
1350    writeln("  an intelligence potion");
1351    player.intelligence := range18(player.intelligence + rand(1, 6));
1352    writeln("  a dexterity potion");
1353    player.dexterity := range18(player.dexterity + rand(1, 6));
1354    if LAMP not in player.possession then
1355      writeln("  a lamp");
1356      incl(player.possession, LAMP);
1357    end if;
1358    for treasure range currentRoom.objects do
1359      writeln("  the " <& treasure);
1360    end for;
1361  end func; # vendorDies
1362
1363
1364const proc: monsterDies (inout roomType: currentRoom, inout playerType: player,
1365    inout fightStateType: fightState) is func
1366  local
1367    var animateType: monster is NOBODY;
1368    var objectType: treasure is NOOBJECT;
1369  begin
1370    monster := currentRoom.roomer;
1371    fightState.monsterPresent := FALSE;
1372    writeln;
1373    writeln("The " <& monster <& " lies dead at your feet!");
1374    if monster in {WOLF, BEAR, CHIMERA, BALROG, DRAGON} and
1375        player.mealHour + 60 <= player.turns then
1376      writeln;
1377      writeln("You spend an hour eating " <& monster <& " " <& anyFood() <& ".");
1378      player.mealHour := player.turns;
1379    end if;
1380    if monster = VENDOR then
1381      vendorDies(currentRoom, player);
1382    else
1383      for treasure range currentRoom.objects do
1384        writeln("\a");
1385        writeln("Great Zot! You've found the " <& treasure <& "!");
1386      end for;
1387      decr(fightState.monsterCount);
1388    end if;
1389    player.possession |:= currentRoom.objects;
1390    currentRoom.objects := objectSet.value;
1391    writeln;
1392    write("You get his hoard of ");
1393    findGoldPieces(player, 99);
1394    currentRoom.roomer := NOBODY;
1395  end func; # monsterDies
1396
1397
1398const proc: monsterAttacks (in animateType: monster, inout playerType: player,
1399    inout fightStateType: fightState) is func
1400  begin
1401    if fightState.webCount > 0 then
1402      decr(fightState.webCount);
1403      if fightState.webCount = 0 then
1404        writeln;
1405        writeln("The web just broke!");
1406      end if;
1407    end if;
1408    writeln;
1409    write("The " <& monster);
1410    if fightState.webCount > 0 then
1411      writeln(" is stuck and can't attack now!");
1412    elsif player.dexterity >= rand(3, 21) + 3 * ord(player.blind) then
1413      writeln(" attacks!  What luck, he missed you!");
1414    else
1415      writeln(" attacks!  Ouch! He hit you!");
1416      checkArmor(player, fightState.aggressionOfMonster);
1417      player.living := player.strength >= 1;
1418    end if;
1419  end func; # monsterAttacks
1420
1421
1422const proc: attackMonster (inout roomType: currentRoom, inout playerType: player,
1423    inout fightStateType: fightState) is func
1424  local
1425    var animateType: monster is NOBODY;
1426  begin
1427    monster := currentRoom.roomer;
1428    if player.weapon = NOWEAPON then
1429      writeln;
1430      writeln("** Pounding on " <& aOrAn(monster) <& " won't hurt it!");
1431    elsif player.weaponBlocked then
1432      writeln;
1433      writeln("** You can't beat it to death with a book!");
1434    elsif player.dexterity < rand(1, 20) + (3 * ord(player.blind)) then
1435      writeln;
1436      writeln("You missed, too bad!");
1437    else
1438      writeln;
1439      writeln("You hit the evil " <& monster <& ".");
1440      fightState.monsterStrength -:= ord(player.weapon);
1441      if monster in {GARGOYLE, DRAGON} then
1442        if rand(1, 8) = 1 then
1443          writeln;
1444          writeln("OH NO!  Your " <& player.weapon <& " broke!");
1445          player.weapon := NOWEAPON;
1446        end if;
1447      end if;
1448      if fightState.monsterStrength <= 0 then
1449        monsterDies(currentRoom, player, fightState);
1450      end if;
1451    end if;
1452  end func; # attackMonster
1453
1454
1455const proc: castSpell (inout roomType: currentRoom, inout playerType: player,
1456    inout fightStateType: fightState) is func
1457  local
1458    var integer: damage is 0;
1459    var char: ch is ' ';
1460  begin
1461    writeln;
1462    write("Which spell (web, fireball, deathspell)? ");
1463    ch := readChar();
1464    writeln;
1465    if ch = 'W' then
1466      decr(player.strength);
1467      fightState.webCount := rand(2, 9);
1468      player.living := player.strength >= 1;
1469    elsif ch = 'F' then
1470      decr(player.strength);
1471      decr(player.intelligence);
1472      if player.intelligence < 1 or player.strength < 1 then
1473        player.living := FALSE;
1474      else
1475        damage := rand(2, 14);
1476        writeln;
1477        writeln("It does " <& damage <& " points worth of damage.");
1478        fightState.monsterStrength -:= damage;
1479        if fightState.monsterStrength <= 0 then
1480          monsterDies(currentRoom, player, fightState);
1481        end if;
1482      end if;
1483    elsif ch = 'D' then
1484      write("DEATH . . . ");
1485      if player.intelligence < rand(16, 19) then
1486        writeln("YOURS!");
1487        player.intelligence := 0;
1488        player.living := FALSE;
1489      else
1490        writeln("HIS!");
1491        fightState.monsterStrength := 0;
1492        monsterDies(currentRoom, player, fightState);
1493      end if;
1494    else
1495      writeln("** Try one of the options given.");
1496    end if;
1497  end func; # castSpell
1498
1499
1500const proc: bribeMonster (inout roomType: currentRoom, inout playerType: player,
1501    inout fightStateType: fightState) is func
1502  local
1503    var objectType: treasure is NOOBJECT;
1504    var char: ch is ' ';
1505    var boolean: okay is TRUE;
1506  begin
1507    fightState.bribed := FALSE;
1508    if countOwnedTreasures(player) = 0 then
1509      writeln;
1510      writeln("All I want is your life!");
1511    else
1512      treasure := ownedTreasure(player);
1513      repeat
1514        okay := TRUE;
1515        writeln;
1516        write("I want the " <& treasure <& ". Will you give it to me? ");
1517        ch := readChar();
1518        if not ch in {'Y', 'N', 'Q'} then
1519          okay := FALSE;
1520          writeln;
1521          writeln("** Please answer yes or no.");
1522        end if;
1523      until okay;
1524      if ch = 'Y' then
1525        excl(player.possession, treasure);
1526        incl(currentRoom.objects, treasure);
1527        objPlace[treasure] := currentRoom;
1528        writeln;
1529        writeln("Ok, just don't tell anyone else.");
1530        if currentRoom.roomer = VENDOR then
1531          fightState.monsterPresent := FALSE;
1532          fightState.angryVendors := FALSE;
1533        end if;
1534        fightState.bribed := TRUE;
1535      end if;
1536    end if;
1537  end func; # bribeMonster
1538
1539
1540const proc: meetMonster (in roomType: currentRoom, in playerType: player,
1541    inout fightStateType: fightState) is func
1542  local
1543    var animateType: monster is NOBODY;
1544  begin
1545    monster := currentRoom.roomer;
1546    fightState.monsterPresent := TRUE;
1547    fightState.bribed := FALSE;
1548    fightState.webCount := 0;
1549    fightState.aggressionOfMonster := 1 + ord(monster) div 2;
1550    fightState.monsterStrength := ord(monster) + 2;
1551    if monster = VENDOR and not fightState.angryVendors then
1552      writeln;
1553      writeln("You'll be sorry that you did that!");
1554      fightState.angryVendors := TRUE;
1555    else
1556      writeln("You may attack or retreat.");
1557      if countOwnedTreasures(player) <> 0 then
1558        writeln("You can also attempt a bribe.");
1559      end if;
1560      if player.intelligence >= 15 then
1561        writeln("You can also cast a spell.");
1562      end if;
1563    end if;
1564    if (player.lethargic and RUBY not in player.possession) or
1565        player.blind or player.dexterity < rand(1, 18) then
1566      fightState.monsterWillAttack := TRUE;
1567    else
1568      fightState.monsterWillAttack := FALSE;
1569    end if;
1570  end func; # meetMonster
1571
1572
1573const proc: retreatFromMonster (in roomType: currentRoom,
1574    inout playerType: player, inout fightStateType: fightState) is func
1575  local
1576    var animateType: monster is NOBODY;
1577  begin
1578    if fightState.monsterPresent then
1579      if not fightState.bribed then
1580        monster := currentRoom.roomer;
1581        monsterAttacks(monster, player, fightState);
1582        if player.living then
1583          writeln;
1584          case rand(1, 7) of
1585            when {1}: writeln("You fake a blow and escape.");
1586            when {2}: writeln("You have escaped by turning and running.");
1587            when {3}: writeln("You jump to the left and escape to the right.");
1588            when {4}: writeln("What a furious trick. You escaped by doing nothing.");
1589            when {5}: writeln("The " <& monster <& " stumbled and you escaped.");
1590            when {6}: writeln("You escaped by jumping over the " <& monster <& "!");
1591            when {7}: writeln("You are lucky, you have escaped because the " <&
1592                              monster <& " was diverted by a cry.");
1593          end case;
1594        end if;
1595      end if;
1596      fightState.monsterPresent := FALSE;
1597    end if;
1598  end func; # retreatFromMonster
1599
1600
1601const proc: attack (inout roomType: currentRoom, inout playerType: player,
1602    inout fightStateType: fightState) is func
1603  begin
1604    if currentRoom.roomer = NOBODY then
1605      writeln;
1606      writeln("** There is nothing that can be attacked!");
1607    else
1608      fightState.bribed := FALSE;
1609      if not fightState.monsterPresent then  # attack VENDOR
1610        meetMonster(currentRoom, player, fightState);
1611      end if;
1612      attackMonster(currentRoom, player, fightState);
1613      fightState.monsterWillAttack := TRUE;
1614    end if;
1615  end func; # attack
1616
1617
1618const proc: cast (inout roomType: currentRoom, inout playerType: player,
1619    inout fightStateType: fightState) is func
1620  begin
1621    if currentRoom.roomer = NOBODY then
1622      writeln;
1623      writeln("** There is nothing that can be casted!");
1624    elsif player.intelligence < 15 then
1625      writeln;
1626      writeln("** Your intelligence must be 15 or more to cast a spell!");
1627    else
1628      fightState.bribed := FALSE;
1629      if not fightState.monsterPresent then  # cast VENDOR
1630        meetMonster(currentRoom, player, fightState);
1631      end if;
1632      castSpell(currentRoom, player, fightState);
1633      fightState.monsterWillAttack := TRUE;
1634    end if;
1635  end func; # cast
1636
1637
1638const proc: bribe (inout roomType: currentRoom, inout playerType: player,
1639    inout fightStateType: fightState) is func
1640  begin
1641    if currentRoom.roomer = NOBODY then
1642      writeln;
1643      writeln("** There is nobody that can be bribed!");
1644    elsif fightState.monsterPresent then
1645      if fightState.bribed then
1646        writeln;
1647        writeln("I will not give you more than your life.");
1648      else
1649        bribeMonster(currentRoom, player, fightState);
1650      end if;
1651    else
1652      writeln;
1653      writeln("** That does not work.");
1654    end if;
1655  end func; # bribe
1656
1657
1658const proc: meetVendor (inout roomType: currentRoom, in playerType: player,
1659    inout fightStateType: fightState) is func
1660  begin
1661    if fightState.angryVendors then
1662      meetMonster(currentRoom, player, fightState);
1663    else
1664      writeln("You may buy from, sell to, attack, or ignore the vendor.");
1665    end if;
1666  end func; # meetVendor
1667
1668
1669const proc: enterRoom (inout roomType: currentRoom, inout playerType: player,
1670    inout fightStateType: fightState) is func
1671  begin
1672    currentRoom.visited := TRUE;
1673    writeln;
1674    write("You are in ");
1675    if player.blind then
1676      writeln("a new room.");
1677    else
1678      writeRoomDescription(currentRoom);
1679    end if;
1680    case currentRoom.transfer of
1681      when {NOTRANSFER}:
1682        writeRoomDetails(currentRoom);
1683        if currentRoom.roomer = VENDOR then
1684          meetVendor(currentRoom, player, fightState);
1685        elsif currentRoom.roomer <> NOBODY then
1686          meetMonster(currentRoom, player, fightState);
1687        else
1688          if currentRoom.objects <> objectSet.value then
1689            writeln;
1690            if card(currentRoom.objects) = 1 then
1691              writeln("It's now yours!");
1692            else
1693              writeln("They're yours now!");
1694            end if;
1695            player.possession |:= currentRoom.objects;
1696            currentRoom.objects := objectSet.value;
1697          end if;
1698        end if;
1699      when {SINKHOLE}:
1700        case roomId(currentRoom) rem 5 of
1701          when {0}: writeln("Here you fall into a sinkhole.");
1702          when {1}: writeln("Here a trap-door opens under your feet and \
1703                            \you fall down.");
1704          when {2}: writeln("You have stepped into a pitfall.");
1705          when {3}: writeln("You step on an open trap-door and fall down.");
1706          when {4}: writeln("You fall into a hole hidden on the ground.");
1707        end case;
1708        currentRoomRef := labyrinth[currentRoom.xPos]
1709                                   [currentRoom.yPos]
1710                                   [rangeLevel(succ(currentRoom.zPos))];
1711        enterRoom(currentRoomRef, player, fightState);
1712      when {WARP}:
1713        write("This room contains a warp. You have been transferred to ");
1714        currentRoomRef := labyrinth[rand(1, SIZE_LABY)]
1715                                   [rand(1, SIZE_LABY)]
1716                                   [rand(1, NUM_LEVELS)];
1717        writePos(currentRoomRef);
1718        enterRoom(currentRoomRef, player, fightState);
1719    end case;
1720  end func; # enterRoom
1721
1722
1723const proc: readCoordinate (inout playerType: player, in char: coordname,
1724    inout integer: coordinate, in integer: coordinateMax) is func
1725  local
1726    var boolean: okay is TRUE;
1727  begin
1728    if not player.quitDialog then
1729      repeat
1730        writeln;
1731        write("Please enter the " <& coordname <& "-coordinate? ");
1732        readNumber(coordinate, okay, player.quitDialog);
1733        if okay then
1734          if coordinate < 1 or coordinate > coordinateMax then
1735            writeln;
1736            writeln("** Try a number from 1 to " <& coordinateMax <& ".");
1737            okay := FALSE;
1738          end if;
1739        elsif not player.quitDialog then
1740          writeln;
1741          writeln("** Would you please be so kind to type a digit, " <&
1742                  player.species <& ".");
1743        end if;
1744      until okay or player.quitDialog;
1745    end if;
1746  end func; # readCoordinate
1747
1748
1749const proc: teleportTo (inout roomType: currentRoom, inout playerType: player,
1750    inout fightStateType: fightState) is func
1751  begin
1752    if ORBOFZOT in currentRoom.objects then
1753      excl(player.possession, RUNESTAFF);
1754      player.possession |:= currentRoom.objects;
1755      currentRoom.objects := objectSet.value;
1756      currentRoom.visited := TRUE;
1757      currentRoom.transfer := NOTRANSFER;
1758      enterRoom(currentRoom, player, fightState);
1759      writeln;
1760      writeln("Great unmitigated Zot!");
1761      writeln;
1762      writeln("You just found the *ORB OF ZOT*!");
1763      writeln;
1764      writeln("The Runestaff has disappeared!");
1765    else
1766      enterRoom(currentRoom, player, fightState);
1767    end if;
1768  end func; # teleportTo
1769
1770
1771const proc: teleport (in roomType: currentRoom, inout playerType: player,
1772    inout fightStateType: fightState) is func
1773  local
1774    var integer: xPos is 0;
1775    var integer: yPos is 0;
1776    var integer: zPos is 0;
1777  begin
1778    if RUNESTAFF not in player.possession then
1779      writeln;
1780      writeln("** You can't teleport without the Runestaff!");
1781    else
1782      player.quitDialog := FALSE;
1783      readCoordinate(player, 'x', xPos, SIZE_LABY);
1784      readCoordinate(player, 'y', yPos, SIZE_LABY);
1785      readCoordinate(player, 'z', zPos, NUM_LEVELS);
1786      if player.quitDialog then
1787        writeln;
1788        writeln("** The Runesaff needs three coordinates, " <&
1789                player.species <& ".");
1790      else
1791        fightState.monsterPresent := FALSE;
1792        currentRoomRef := labyrinth[xPos][yPos][zPos];
1793        teleportTo(currentRoomRef, player, fightState);
1794      end if;
1795    end if;
1796  end func; # teleport
1797
1798
1799const proc: go (in roomType: currentRoom, inout playerType: player,
1800    inout fightStateType: fightState, in directType: direction) is func
1801  local
1802    const array [directType] integer: delta_x is [directType]( 0,  0,  1, -1,  0,  0);
1803    const array [directType] integer: delta_y is [directType](-1,  1,  0,  0,  0,  0);
1804    const array [directType] integer: delta_z is [directType]( 0,  0,  0,  0, -1,  1);
1805  begin
1806    if direction = NORTH and currentRoom.contents = ENTRANCE then
1807      writeln;
1808      write("Do you really want to leave the castle? ");
1809      if readChar() <> 'Y' then
1810        writeln;
1811        writeln("** Then don't say that you do!");
1812      else
1813        retreatFromMonster(currentRoom, player, fightState);
1814        player.leaveCastle := TRUE;
1815      end if;
1816    elsif direction in currentRoom.connections then
1817      retreatFromMonster(currentRoom, player, fightState);
1818      if player.living then
1819        currentRoomRef := labyrinth
1820            [ rangeLaby(currentRoom.xPos + delta_x[direction])]
1821            [ rangeLaby(currentRoom.yPos + delta_y[direction])]
1822            [rangeLevel(currentRoom.zPos + delta_z[direction])];
1823        enterRoom(currentRoomRef, player, fightState);
1824      end if;
1825    else
1826      writeln;
1827      writeln("** There is no way in this direction!");
1828    end if;
1829  end func; # go
1830
1831
1832const proc: status (in roomType: currentRoom, in playerType: player) is func
1833  begin
1834    writeln;
1835    if not player.blind then
1836      write("You are at ");
1837      writePos(currentRoom);
1838      writeln;
1839    end if;
1840    writeln("STRENGTH = " <& player.strength <&
1841            "  INTELLIGENCE = " <& player.intelligence <&
1842            "  DEXTERITY = " <& player.dexterity);
1843    writeln("OBJECTS = " <& countOwnedObjects(player) <&
1844            "  FLARES = " <& player.flares <&
1845            "  GOLD PIECES = " <& player.goldPieces);
1846    writeln("weapon = " <& player.weapon <& "  armor = " <& player.armor);
1847  end func; # status
1848
1849
1850const proc: listInventory (in playerType: player) is func
1851  local
1852    var boolean: anythinglisted is FALSE;
1853    var objectType: obj is NOOBJECT;
1854  begin
1855    if player.weapon <> NOWEAPON then
1856      anythinglisted := TRUE;
1857      writeln("  a " <& player.weapon);
1858    end if;
1859    if player.armor <> NOARMOR then
1860      anythinglisted := TRUE;
1861      writeln("  " <& player.armor <& " armor");
1862    end if;
1863    if player.flares > 0 then
1864      anythinglisted := TRUE;
1865      writeln("  " <& numberName(player.flares) <& " flare" <&
1866              player.flares <> 1 ? "s" : "");
1867    end if;
1868    if player.goldPieces > 0 then
1869      anythinglisted := TRUE;
1870      writeln("  " <& numberName(player.goldPieces) <& " gold piece" <&
1871              player.goldPieces <> 1 ? "s" : "");
1872    end if;
1873    for obj range player.possession do
1874      anythinglisted := TRUE;
1875      writeln("  the " <& obj);
1876    end for;
1877    if not anythinglisted then
1878      writeln("  nothing");
1879    end if;
1880  end func; # listInventory
1881
1882
1883const proc: inventory (in playerType: player) is func
1884  begin
1885    writeln;
1886    writeln("You have:");
1887    listInventory(player);
1888  end func; # inventory
1889
1890
1891const proc: contentInfo (in roomType: currentRoom, in roomType: aRoom) is func
1892  begin
1893    write(aRoom.xPos = currentRoom.xPos and
1894          aRoom.yPos = currentRoom.yPos ? "<" : " ");
1895    if aRoom.visited then
1896      case aRoom.transfer of
1897        when {NOTRANSFER}:
1898          if aRoom.roomer = NOBODY then
1899            if aRoom.contents <> EMPTYROOM then
1900              case aRoom.contents of
1901                when {ENTRANCE}:          write("E");
1902                when {EMPTYCHEST}:        write(".");
1903                when {CHESTWITHSKELETON}: write(".");
1904                when {CLOSEDCHEST}:       write("C");
1905                when {ORB}:               write("O");
1906                when {POOL}:              write("P");
1907                when {BOOK}:              write("B");
1908              end case;
1909            elsif aRoom.objects <> objectSet.value then
1910              write("T");
1911            elsif UP in aRoom.connections then
1912              write("U");
1913            elsif DOWN in aRoom.connections then
1914              write("D");
1915            elsif aRoom.occurrence = FINDFLARES then
1916              write("F");
1917            elsif aRoom.occurrence = FINDGOLD then
1918              write("G");
1919            else
1920              write(".");
1921            end if;
1922          elsif aRoom.roomer = VENDOR then
1923            write("V");
1924          else
1925            write("M");
1926          end if;
1927        when {SINKHOLE}: write("S");
1928        when {WARP}:     write("W");
1929      end case;
1930    else
1931      write(" ");
1932    end if;
1933    write(aRoom.xPos = currentRoom.xPos and
1934          aRoom.yPos = currentRoom.yPos ? ">  " : "   ");
1935  end func; # contentInfo
1936
1937
1938const proc: map (in roomType: currentRoom, in playerType: player) is func
1939  local
1940    var integer: xPos is 0;
1941    var integer: yPos is 0;
1942    var integer: zPos is 0;
1943  begin
1944    writeln;
1945    if player.blind then
1946      writeln("** You are blind, you dumb " <& player.species <& "!");
1947    else
1948      zPos := currentRoom.zPos;
1949      for yPos range 1 to SIZE_LABY do
1950        for xPos range 1 to SIZE_LABY do
1951          contentInfo(currentRoom, labyrinth[xPos][yPos][zPos]);
1952        end for;
1953        writeln;
1954        writeln;
1955      end for;
1956      write("You are at ");
1957      writePos(currentRoom);
1958    end if;
1959  end func; # map
1960
1961
1962const proc: flare (inout roomType: currentRoom, inout playerType: player) is func
1963  local
1964    var integer: x is 0;
1965    var integer: y is 0;
1966    var integer: xPos is 0;
1967    var integer: yPos is 0;
1968    var integer: zPos is 0;
1969  begin
1970    writeln;
1971    if player.flares = 0 then
1972      writeln("** Hey, bright one, you're out of flares!");
1973    elsif player.blind then
1974      writeln("** You can't see anything, you dumb " <& player.species <& "!");
1975    else
1976      decr(player.flares);
1977      zPos := currentRoom.zPos;
1978      for y range pred(currentRoom.yPos) to succ(currentRoom.yPos) do
1979        yPos := rangeLaby(y);
1980        for x range pred(currentRoom.xPos) to succ(currentRoom.xPos) do
1981          xPos := rangeLaby(x);
1982          labyrinth[xPos][yPos][zPos].visited := TRUE;
1983          contentInfo(currentRoom, labyrinth[xPos][yPos][zPos]);
1984        end for;
1985        writeln;
1986        writeln;
1987      end for;
1988      write("You are at ");
1989      writePos(currentRoom);
1990    end if;
1991  end func; # flare
1992
1993
1994const proc: shineIntoRoom (inout roomType: aRoom) is func
1995  begin
1996    aRoom.visited := TRUE;
1997    writeln;
1998    write("The lamp shines into a room at ");
1999    writePos(aRoom);
2000    write("You see ");
2001    writeRoomDescription(aRoom);
2002    writeRoomDetails(aRoom);
2003  end func; # shineIntoRoom
2004
2005
2006const proc: lamp (in roomType: currentRoom, in playerType: player) is func
2007  local
2008    var integer: xPos is 0;
2009    var integer: yPos is 0;
2010    var integer: zPos is 0;
2011    var char: ch is ' ';
2012  begin
2013    writeln;
2014    if LAMP not in player.possession then
2015      writeln("** You don't have a lamp, " <& player.species <& "!");
2016    elsif player.blind then
2017      writeln("** You are blind, you dumb " <& player.species <& "!");
2018    else
2019      write("Where do you want to shine the lamp (N, S, E, W)? ");
2020      ch := readChar();
2021      if not ch in {'N', 'S', 'E', 'W'} then
2022        writeln;
2023        writeln("** That's not a direction, " <& player.species <& "!");
2024      else
2025        xPos := currentRoom.xPos;
2026        yPos := currentRoom.yPos;
2027        zPos := currentRoom.zPos;
2028        case ch of
2029          when {'N'}: yPos := rangeLaby(pred(yPos));
2030          when {'S'}: yPos := rangeLaby(succ(yPos));
2031          when {'W'}: xPos := rangeLaby(pred(xPos));
2032          when {'E'}: xPos := rangeLaby(succ(xPos));
2033        end case;
2034        shineIntoRoom(labyrinth[xPos][yPos][zPos]);
2035      end if;
2036    end if;
2037  end func; # lamp
2038
2039
2040const proc: drink (in roomType: currentRoom, inout playerType: player) is func
2041  local
2042    var speciesType: newSpecies is HUMAN;
2043  begin
2044    writeln;
2045    if currentRoom.contents <> POOL then
2046      writeln("** If you want a drink, find a pool!");
2047    else
2048      write("You take a drink and ");
2049      case rand(1, 8) of
2050
2051        when {1}:
2052          player.strength := range18(player.strength + rand(1, 3));
2053          writeln("feel stronger.");
2054
2055        when {2}:
2056          player.strength -:= rand(1, 3);
2057          writeln("feel weaker.");
2058          player.living := player.strength >= 1 ;
2059
2060        when {3}:
2061          player.intelligence := range18(player.intelligence + rand(1, 3));
2062          writeln("feel smarter.");
2063
2064        when {4}:
2065          player.intelligence -:= rand(1, 3);
2066          writeln("feel dumber.");
2067          player.living := player.intelligence >= 1;
2068
2069        when {5}:
2070          player.dexterity := range18(player.dexterity + rand(1, 3));
2071          writeln("feel nimbler.");
2072
2073        when {6}:
2074          player.dexterity -:= rand(1, 3);
2075          writeln("feel clumsier.");
2076          player.living := player.dexterity >= 1;
2077
2078        when {7}:
2079          newSpecies := rand(speciesType.first, pred(speciesType.last));
2080          if newSpecies >= player.species then
2081            incr(newSpecies);
2082          end if;
2083          player.species := newSpecies;
2084          writeln("become " <& aOrAn(player.species) <& ".");
2085
2086        when {8}:
2087          player.isMale := not player.isMale;
2088          writeln("turn into a " <& sexName(player.isMale) <&
2089                  " " <& player.species <& ".");
2090      end case;
2091    end if;
2092  end func; # drink
2093
2094
2095const proc: read (inout roomType: currentRoom, inout playerType: player) is func
2096  begin
2097    writeln;
2098    if currentRoom.contents <> BOOK then
2099      writeln("** There is nothing that can be read!");
2100    elsif player.blind then
2101      writeln("** You are blind, you dumb " <& player.species <& "!");
2102    else
2103      writeln("You open the book and");
2104      case rand(1, 6) of
2105
2106        when {1}:
2107          writeln("Flash! Oh no! You are now a blind " <& player.species <& "!");
2108          player.blind := TRUE;
2109
2110        when {2}:
2111          case rand(1, 4) of
2112            when {1}: writeln("It's another volume of Zot's poetry. - YECH!!");
2113            when {2}: writeln("It's a manual of this game.");
2114            when {3}: writeln("It's a story about a dumb " <& player.species <&
2115                              " who finds a book and then dies.");
2116            when {4}: writeln("It's volume number " <&
2117                              numberName(rand(1, 20)) <& " of a novel.");
2118          end case;
2119
2120        when {3}:
2121          writeln("It's an old copy of play " <& rand(speciesType) <& ".");
2122
2123        when {4}:
2124          writeln("It's a manual of dexterity!");
2125          player.dexterity := 18;
2126
2127        when {5}:
2128          writeln("It's a manual of strength!");
2129          player.strength := 18;
2130
2131        when {6}:
2132          writeln("The book sticks to your hands -");
2133          writeln("Now you are unable to draw your weapon!");
2134          player.weaponBlocked := TRUE;
2135      end case;
2136      currentRoom.contents := EMPTYROOM;
2137    end if;
2138  end func; # read
2139
2140
2141const proc: viewRoomWithOrb (inout roomType: aRoom) is func
2142  begin
2143    aRoom.visited := TRUE;
2144    writeRoomDescription(aRoom);
2145    writeConnections(aRoom);
2146    write("You can also see that this room is at ");
2147    writePos(aRoom);
2148    writeAnimates(aRoom);
2149    writeObjects(aRoom);
2150  end func; # viewRoomWithOrb
2151
2152
2153const proc: gazeIntoOrb (inout playerType: player) is func
2154  begin
2155    write("You see ");
2156    case rand(1, 6) of
2157
2158      when {1}:
2159        case rand(1, 10) of
2160          when { 1}: writeln("your own burial!");
2161          when { 2}: writeln("your mouldering dead body!");
2162          when { 3}: writeln("yourself in a bloody heap!");
2163          when { 4}: writeln("yourself with your skull bashed in!");
2164          when { 5}: writeln("your broken skeleton lying on the ground!");
2165          when { 6}: writeln("a graveyard and a tombstone with your name!");
2166          when { 7}: writeln(aOrAn(rand(animateType)) <& " killing you!");
2167          when { 8}: writeln(aOrAn(rand(speciesType)) <&
2168                            " finding your faded bones!");
2169          when { 9}: writeln(aOrAn(rand(speciesType)) <& " which tells " <&
2170                            aOrAn(rand(speciesType)) <& " that you are dead!");
2171          when {10}: writeln("a " <& sexName(not player.isMale) <&
2172                             " " <& player.species <&
2173                             " giving flowers on your grave!");
2174        end case;
2175        decr(player.strength);
2176        write("This message makes you weaker. Your strength is now ");
2177        if player.strength < 1 then
2178          player.living := FALSE;
2179          writeln("zero!");
2180        else
2181          writeln(numberName(player.strength) <& ".");
2182        end if;
2183
2184      when {2}:
2185        case rand(1, 4) of
2186          when {1}: writeln("yourself drinking from a pool and becoming " <&
2187                            aOrAn(rand(animateType)) <& "!");
2188          when {2}: writeln(aOrAn(rand(animateType)) <&
2189                            " drinking from a pool and becoming " <&
2190                            aOrAn(rand(speciesType)) <& ".");
2191          when {3}: writeln(aOrAn(rand(speciesType)) <&
2192                            " drinking from a pool and becoming " <&
2193                            anyAdjective() <& " spider.");
2194          when {4}: writeln("a young " <& rand(FALSE, TRUE) ? "man" : "woman" <&
2195                            " drinking from a pool and becoming as old \
2196                            \as the hills.");
2197        end case;
2198
2199      when {3}:
2200        case rand(1, 2) of
2201          when {1}: writeln(aOrAn(rand(animateType)) <&
2202                            " gazing back at you.");
2203          when {2}: writeln("that you are watched from " <&
2204                            aOrAn(rand(speciesType)) <& ".");
2205        end case;
2206
2207      when {4}:
2208        viewRoomWithOrb(labyrinth[rand(1, SIZE_LABY)]
2209                                 [rand(1, SIZE_LABY)]
2210                                 [rand(1, NUM_LEVELS)]);
2211
2212      when {5}:
2213        write("the *ORB OF ZOT* at ");
2214        if rand(FALSE, TRUE) then
2215          writePos(objPlace[ORBOFZOT]);
2216        else
2217          writePos(labyrinth[rand(1, SIZE_LABY)]
2218                            [rand(1, SIZE_LABY)]
2219                            [rand(1, NUM_LEVELS)]);
2220        end if;
2221
2222      when {6}:
2223        case rand(1, 10) of
2224          when { 1}: writeln("a soap opera rerun.");
2225          when { 2}: writeln("a washing powder commercial.");
2226          when { 3}: writeln("an image to test the reception of the orb.");
2227          when { 4}: writeln("somebody sitting at a computer playing this game.");
2228          when { 5}: writeln("nothing because in the moment there are atmospherics.");
2229          when { 6}: writeln("a scientist demonstrating that an orb could never work.");
2230          when { 7}: writeln("the presentation of the new generation of orb's with sound.");
2231          when { 8}: writeln("that there is a 50% chance that what you see in an orb is correct.");
2232          when { 9}: writeln("yourself looking into an orb where you see yourself looking into ...");
2233          when {10}: writeln(aOrAn(rand(speciesType)) <&
2234                             " announcing todays program of the orb.");
2235        end case;
2236    end case;
2237  end func; # gazeIntoOrb
2238
2239
2240const proc: gaze (in roomType: currentRoom, inout playerType: player,
2241    inout fightStateType: fightState) is func
2242  begin
2243    writeln;
2244    if player.blind then
2245      writeln("** You can't see anything, you dumb " <& player.species <& "!");
2246    elsif currentRoom.roomer = NOBODY then
2247      case currentRoom.contents of
2248        when {EMPTYROOM}:         writeln("You are gazing at an empty wall.");
2249        when {ENTRANCE}:          writeln("You are gazing at the exit.");
2250        when {EMPTYCHEST}:        writeln("The chest does not fill with gazing at.");
2251        when {CHESTWITHSKELETON}: writeln("The skeleton looks horrible.");
2252        when {CLOSEDCHEST}:       writeln("The chest does not open with gazing at.");
2253        when {POOL}:              writeln("You see your ugly face mirror in the water.");
2254        when {BOOK}:              writeln("You are gazing at the book.");
2255        when {ORB}:               gazeIntoOrb(player);
2256      end case;
2257    elsif currentRoom.roomer = VENDOR then
2258      write("You are gazing at the Vendor. ");
2259      if player.isMale then
2260        writeln("But the Vendor does not like male " <& player.species <& ".");
2261      else
2262        writeln("The Vendor smiles and shows his wedding-ring.");
2263      end if;
2264    else
2265      write("The " <& currentRoom.roomer);
2266      if player.isMale then
2267        writeln(" is shocked by the scowl of a strong male " <&
2268                player.species <& ".");
2269        fightState.monsterWillAttack := FALSE;
2270      else
2271        writeln(" cannot be shocked by the scowl of a female " <&
2272                player.species <& ".");
2273      end if;
2274    end if;
2275  end func; # gaze
2276
2277
2278const proc: openChest (inout roomType: currentRoom, inout playerType: player,
2279  inout fightStateType: fightState) is func
2280  local
2281    var directType: direct is NORTH;
2282  begin
2283    if currentRoom.roomer <> NOBODY then
2284      writeln("The " <& currentRoom.roomer <&
2285              " does not allow to open the chest.");
2286    else
2287      write("You open the chest and ");
2288      case rand(1, 2) of
2289
2290        when {1}:
2291          write("find ");
2292          findGoldPieces(player, 99);
2293          currentRoom.contents := EMPTYCHEST;
2294
2295        when {2}:
2296          case rand(1, 7) of
2297
2298            when {1}:
2299              writeln("it is totally empty.");
2300              currentRoom.contents := EMPTYCHEST;
2301
2302            when {2}:
2303              writeln("it disappears in the moment you open it.");
2304              currentRoom.contents := EMPTYROOM;
2305
2306            when {3}:
2307              writeln("...  KABOOOM!  It explodes!!");
2308              checkArmor(player, rand(1, 6));
2309              player.living := player.strength >= 1;
2310              currentRoom.contents := EMPTYROOM;
2311
2312            when {4}:
2313              player.turns +:= 20;
2314              direct := rand(currentRoom.connections - {UP, DOWN});
2315              writeln("...  GAS!!  You stagger from the room to the " <&
2316                      direct <& "!");
2317              currentRoom.contents := EMPTYCHEST;
2318              go(currentRoom, player, fightState, direct);
2319
2320            when {5}:
2321              writeln("find " <& anyAdjective() <& " skeleton.");
2322              writeln("It seems that this " <& rand(speciesType) <&
2323                      " was also an adventurer.");
2324              currentRoom.contents := CHESTWITHSKELETON;
2325
2326            when {6}:
2327              write("find ");
2328              findFlares(player, 4);
2329              currentRoom.contents := EMPTYCHEST;
2330
2331            when {7}:
2332              if player.armor = NOARMOR then
2333                player.armor := LEATHER;
2334              end if;
2335              writeln("find a brand new " <& player.armor <& " armor.");
2336              player.armorStrength := ord(player.armor) * ARMOR_STRENGTH_FACTOR;
2337              currentRoom.contents := EMPTYCHEST;
2338          end case;
2339      end case;
2340    end if;
2341  end func; # openChest
2342
2343
2344const proc: open (inout roomType: currentRoom, inout playerType: player,
2345    inout fightStateType: fightState) is func
2346  begin
2347    writeln;
2348    if currentRoom.contents <> CLOSEDCHEST then
2349      writeln("** The only thing opened, was your big mouth!");
2350    else
2351      openChest(currentRoom, player, fightState);
2352    end if;
2353  end func; # open
2354
2355
2356const proc: quit (inout playerType: player) is func
2357  begin
2358    writeln;
2359    write("Do you really want to quit now? ");
2360    if readChar() <> 'Y' then
2361      writeln;
2362      writeln("** Then don't say that you do!");
2363    else
2364      player.quitProgram := TRUE;
2365    end if;
2366  end func; # quit
2367
2368
2369const proc: wait is func
2370  begin
2371    writeln;
2372    writeln("Waiting ...");
2373  end func; # wait
2374
2375
2376const proc: illegal (in playerType: player) is func
2377  begin
2378    writeln;
2379    writeln("** Silly " <& player.species <& ", that wasn't a valid command!");
2380  end func; # illegal
2381
2382
2383const func commandType: readCommand is func
2384  result
2385    var commandType: currentCommand is ILLEGAL;
2386  local
2387    var string: stri is "";
2388    var char: ch1 is ' ';
2389    var char: ch2 is ' ';
2390  begin
2391    writeln;
2392    write(" -> ");
2393    stri := upper(getln(IN));
2394    if stri = "" then
2395      currentCommand := WAIT;
2396    else
2397      ch1 := stri[1];
2398      if length(stri) >= 2 then
2399        ch2 := stri[2];
2400      end if;
2401      case ch1 of
2402        when {'A'}: currentCommand := ATTACK;
2403        when {'B'}: if ch2 = ' ' or ch2 = 'U' then
2404                      currentCommand := BUY;
2405                    elsif ch2 = 'R' then
2406                      currentCommand := BRIBE;
2407                    else
2408                      currentCommand := ILLEGAL;
2409                    end if;
2410        when {'C'}: currentCommand := CAST;
2411        when {'D'}: if ch2 = ' ' or ch2 = 'O' then
2412                      currentCommand := GO_DOWN;
2413                    elsif ch2 = 'R' then
2414                      currentCommand := DRINK;
2415                    else
2416                      currentCommand := ILLEGAL;
2417                    end if;
2418        when {'E'}: currentCommand := GO_EAST;
2419        when {'F'}: currentCommand := FLARE;
2420        when {'G'}: currentCommand := GAZE;
2421        when {'H'}: currentCommand := HELP;
2422        when {'I'}: currentCommand := INVENTORY;
2423        when {'L'}: if ch2 = ' ' or ch2 = 'O' then
2424                      currentCommand := LOOK;
2425                    elsif ch2 = 'A' then
2426                      currentCommand := USE_LAMP;
2427                    else
2428                      currentCommand := ILLEGAL;
2429                    end if;
2430        when {'M'}: currentCommand := MAP;
2431        when {'N'}: currentCommand := GO_NORTH;
2432        when {'O'}: currentCommand := OPEN;
2433        when {'Q'}: currentCommand := QUITCOMMAND;
2434        when {'R'}: currentCommand := READ;
2435        when {'S'}: if ch2 = ' ' or ch2 = 'O' then
2436                      currentCommand := GO_SOUTH;
2437                    elsif ch2 = 'T' then
2438                      currentCommand := STATUS;
2439                    elsif ch2 = 'E' then
2440                      currentCommand := SELL;
2441                    else
2442                      currentCommand := ILLEGAL;
2443                    end if;
2444        when {'T'}: currentCommand := TELEPORT;
2445        when {'U'}: currentCommand := GO_UP;
2446        when {'W'}: currentCommand := GO_WEST;
2447        otherwise:  currentCommand := ILLEGAL;
2448      end case;
2449    end if;
2450  end func; # readCommand
2451
2452
2453const proc: executeCommand (inout roomType: currentRoom,
2454    inout playerType: player, inout fightStateType: fightState) is func
2455  begin
2456    case readCommand() of
2457      when {GO_NORTH}:    go(currentRoom, player, fightState, NORTH);
2458      when {GO_SOUTH}:    go(currentRoom, player, fightState, SOUTH);
2459      when {GO_EAST}:     go(currentRoom, player, fightState, EAST);
2460      when {GO_WEST}:     go(currentRoom, player, fightState, WEST);
2461      when {GO_UP}:       go(currentRoom, player, fightState, UP);
2462      when {GO_DOWN}:     go(currentRoom, player, fightState, DOWN);
2463      when {WAIT}:        wait;
2464      when {INVENTORY}:   inventory(player);
2465      when {HELP}:        writeHelp;
2466      when {LOOK}:        look(currentRoom, player);
2467      when {MAP}:         map(currentRoom, player);
2468      when {FLARE}:       flare(currentRoom, player);
2469      when {USE_LAMP}:    lamp(currentRoom, player);
2470      when {ATTACK}:      attack(currentRoom, player, fightState);
2471      when {CAST}:        cast(currentRoom, player, fightState);
2472      when {BRIBE}:       bribe(currentRoom, player, fightState);
2473      when {STATUS}:      status(currentRoom, player);
2474      when {OPEN}:        open(currentRoom, player, fightState);
2475      when {READ}:        read(currentRoom, player);
2476      when {GAZE}:        gaze(currentRoom, player, fightState);
2477      when {TELEPORT}:    teleport(currentRoom, player, fightState);
2478      when {DRINK}:       drink(currentRoom, player);
2479      when {SELL}:        sell(currentRoom, player);
2480      when {BUY}:         buy(currentRoom, player);
2481      when {QUITCOMMAND}: quit(player);
2482      otherwise:          illegal(player);
2483    end case;
2484  end func; # executeCommand
2485
2486
2487const proc: writeFightState (in roomType: currentRoom, inout playerType: player,
2488    inout fightStateType: fightState) is func
2489  local
2490    var animateType: monster is NOBODY;
2491  begin
2492    if fightState.monsterPresent and not fightState.bribed and
2493        player.living and not player.quitProgram then
2494      monster := currentRoom.roomer;
2495      if fightState.monsterWillAttack then
2496        monsterAttacks(monster, player, fightState);
2497      end if;
2498      fightState.monsterWillAttack := TRUE;
2499      if player.living then
2500        writeln;
2501        writeln("You're facing " <& aOrAn(monster) <& "!");
2502        writeln("Your strength is " <& player.strength <&
2503                " and your dexterity is " <& player.dexterity <& ".");
2504      end if;
2505    end if;
2506  end func; # writeFightState
2507
2508
2509const proc: incident (inout roomType: currentRoom,
2510    inout playerType: player) is func
2511  local
2512    var objectType: treasure is NOOBJECT;
2513  begin
2514    if PEARL not in player.possession then
2515      if currentRoom.occurrence = LEECH then
2516        writeln("You pocket has a leetch. Now you will lose gold pieces.");
2517        currentRoom.occurrence := NOOCCURRENCE;
2518        player.haveLeech := TRUE;
2519      end if;
2520      if player.haveLeech then
2521        player.goldPieces -:= rand(1, 3);
2522        if player.goldPieces < 0 then
2523          player.goldPieces := 0;
2524        end if;
2525      end if;
2526    end if;
2527    if RUBY not in player.possession then
2528      if currentRoom.occurrence = LETHARGY then
2529        writeln("You feel that you are lethargic now.");
2530        currentRoom.occurrence := NOOCCURRENCE;
2531        player.lethargic := TRUE;
2532      end if;
2533      if player.lethargic then
2534        incr(player.turns);
2535      end if;
2536    end if;
2537    if GREENGEM not in player.possession then
2538      if currentRoom.occurrence = FORGET then
2539        writeln("You see a lot of strange signs on the ground, possibly runes.");
2540        writeln("You try to read them, but you found no sense.");
2541        writeln("You feel that the runes force you to forget the map of the castle.");
2542        currentRoom.occurrence := NOOCCURRENCE;
2543        player.forgetting := TRUE;
2544      end if;
2545      if player.forgetting then
2546        labyrinth[rand(1, SIZE_LABY)]
2547                 [rand(1, SIZE_LABY)]
2548                 [rand(1, NUM_LEVELS)].visited := FALSE;
2549      end if;
2550    end if;
2551    if player.armor <> NOARMOR and currentRoom.occurrence = STEALARMOR then
2552      writeln("You are knocked down from behind and somebody steals your " <&
2553              player.armor <& " armor.");
2554      currentRoom.occurrence := NOOCCURRENCE;
2555      player.armor := NOARMOR;
2556    end if;
2557    if player.weapon <> NOWEAPON and currentRoom.occurrence = STEALWEAPON then
2558      writeln("You realize that somebody has stolen your " <& player.weapon <& ".");
2559      currentRoom.occurrence := NOOCCURRENCE;
2560      player.weapon := NOWEAPON;
2561    end if;
2562    if LAMP in player.possession and currentRoom.occurrence = STEALLAMP then
2563      writeln("You realize that somebody has stolen your lamp.");
2564      currentRoom.occurrence := NOOCCURRENCE;
2565      excl(player.possession, LAMP);
2566    end if;
2567    if player.flares <> 0 and currentRoom.occurrence = STEALFLARES then
2568      writeln("You realize that somebody has stolen all your flares.");
2569      currentRoom.occurrence := NOOCCURRENCE;
2570      player.flares := 0;
2571    end if;
2572    if currentRoom.occurrence = STEALTREASURE then
2573      if countOwnedTreasures(player) <> 0 then
2574        treasure := ownedTreasure(player);
2575        writeln("You realize that somebody has stolen the " <& treasure <& ".");
2576        currentRoom.occurrence := NOOCCURRENCE;
2577        excl(player.possession, treasure);
2578      end if;
2579    end if;
2580    if currentRoom.occurrence = FINDGOLD then
2581      if currentRoom.roomer = NOBODY then
2582        write("Here you find ");
2583        findGoldPieces(player, 10);
2584      else
2585        writeln("Here are " <& numberName(rand(2, 11)) <&
2586                " GP'S. But the " <& currentRoom.roomer <&
2587                " is faster and takes them.");
2588      end if;
2589      currentRoom.occurrence := NOOCCURRENCE;
2590    end if;
2591    if currentRoom.occurrence = FINDFLARES then
2592      if currentRoom.roomer = NOBODY then
2593        write("Here you find ");
2594        findFlares(player, 4);
2595      else
2596        writeln("Here are " <& numberName(rand(2, 5)) <&
2597                " flares. But the " <& currentRoom.roomer <&
2598                " is faster and takes them.");
2599      end if;
2600      currentRoom.occurrence := NOOCCURRENCE;
2601    end if;
2602  end func; # incident
2603
2604
2605const proc: curesAndDissolves (inout playerType: player) is func
2606  begin
2607    if player.blind and OPAL in player.possession then
2608      writeln;
2609      writeln("The opal Eye cures your blindness!");
2610      player.blind := FALSE;
2611    end if;
2612    if player.weaponBlocked and BLUEFLAME in player.possession then
2613      writeln;
2614      writeln("The blue Flame dissolves the book!");
2615      player.weaponBlocked := FALSE;
2616    end if;
2617    if player.haveLeech and PEARL in player.possession then
2618      writeln;
2619      writeln("The pale pearl fixes the leech in your pocket.");
2620      player.haveLeech := FALSE;
2621    end if;
2622    if player.lethargic and RUBY in player.possession then
2623      writeln;
2624      writeln("The ruby red stops your lethargy.");
2625      player.lethargic := FALSE;
2626    end if;
2627    if player.forgetting and GREENGEM in player.possession then
2628      writeln;
2629      writeln("The green Gem stops the forgetting of the map.");
2630      player.forgetting := FALSE;
2631    end if;
2632  end func; # curesAndDissolves
2633
2634
2635const proc: writeRemark (in playerType: player) is func
2636  local
2637    var integer: number is 0;
2638  begin
2639    if rand(1, 5) = 1 then
2640      if player.blind then
2641        number := rand(1, 4);
2642      else
2643        number := rand(1, 5);
2644      end if;
2645      case number of
2646        when {1}:
2647          case rand(1, 8) of
2648            when {1}: writeln("You sneezed.");
2649            when {2}: writeln("You stepped on a frog.");
2650            when {3}: writeln("You have a fit of dizziness.");
2651            when {4}: writeln("You moved your hand through a spiders net.");
2652            when {5}: writeln("There are indications that somebody must have \
2653                              \been here recently.");
2654            when {6}: writeln("A blast of wind blows a cloud of dust across \
2655                              \the room.");
2656            when {7}: writeln("You touch " <& anyAdjective() <&
2657                              " insect that immediately flies away.");
2658            when {8}:
2659              writeln("The smell of " <& anyFood() <& " is in the air.");
2660          end case;
2661        when {2}:
2662          write("You smell ");
2663          case rand(1, 9) of
2664            when {1}: writeln("musty air.");
2665            when {2}: writeln("rotten flesh.");
2666            when {3}: writeln("a whiff of good french perfume.");
2667            when {4}: writeln("the bad odour of a mouldering body.");
2668            when {5}: writeln("the pleasant scent of a green meadow.");
2669            when {6}: writeln("the unpleasant stench of an acid.");
2670            when {7}: writeln("mouldering bones which must lie nearby.");
2671            when {8}: writeln(aOrAn(rand(animateType)) <& " frying.");
2672            when {9}: writeln("the presence of a " <&
2673                              sexName(not player.isMale) <& " " <&
2674                              player.species <& ".");
2675          end case;
2676        when {3}:
2677          write("You feel ");
2678          case rand(1, 10) of
2679            when { 1}: writeln("like you're being watched.");
2680            when { 2}: writeln("terribly frightened.");
2681            when { 3}: writeln("drops falling on your neck.");
2682            when { 4}: writeln("something touching your shoulder.");
2683            when { 5}: writeln("that you will be killed.");
2684            when { 6}: writeln("a cold wind blowing across the room.");
2685            when { 7}: writeln("that you get hungry.");
2686            when { 8}: writeln("vibrations at the ground.");
2687            when { 9}: writeln("danger in the vicinity.");
2688            when {10}: writeln("that a " <& sexName(not player.isMale) <&
2689                               " " <& rand(speciesType) <&
2690                               " must have been here recently.");
2691          end case;
2692        when {4}:
2693          write("You hear ");
2694          case rand(1, 11) of
2695            when { 1}: writeln("thunder.");
2696            when { 2}: writeln("moaning.");
2697            when { 3}: writeln("a scream.");
2698            when { 4}: writeln("a wumpus.");
2699            when { 5}: writeln("footsteps.");
2700            when { 6}: writeln("a door open.");
2701            when { 7}: writeln("a door slam.");
2702            when { 8}: writeln("rattling sounds.");
2703            when { 9}: writeln("somebody snigger.");
2704            when {10}: writeln("faint rustling noises.");
2705            when {11}: writeln("somebody whisper your name.");
2706          end case;
2707        when {5}:
2708          write("You see ");
2709          case rand(1, 8) of
2710            when {1}: writeln("a bat fly by.");
2711            when {2}: writeln("some flies.");
2712            when {3}: writeln("a shadow passing by.");
2713            when {4}: writeln("a rat crossing the room.");
2714            when {5}: writeln("two eyes glowing in the dark. \
2715                              \A moment later they disappear.");
2716            when {6}: writeln(anyAdjective() <& " footprint.");
2717            when {7}: writeln(anyAdjective() <& " spider running away.");
2718            when {8}: writeln("the mirage of the " <&
2719                              rand(LAMP, ORBOFZOT) <& ".");
2720          end case;
2721      end case;
2722    end if;
2723  end func; # writeRemark
2724
2725
2726const proc: die (in playerType: player) is func
2727  begin
2728    writeln("\a");
2729    writeln("*" mult 78);
2730    writeln("A noble effort, oh formerly living " <& player.species <& "!");
2731    writeln;
2732    write("You died due to lack of ");
2733    if player.strength < 1 then
2734      writeln("strength.");
2735    elsif player.intelligence < 1 then
2736      writeln("intelligence.");
2737    elsif player.dexterity < 1 then
2738      writeln("dexterity.");
2739    end if;
2740    writeln;
2741    writeln("At the time you died, you had:");
2742    listInventory(player);
2743    writeln;
2744    writeln("And it took you " <& player.turns <& " turns!");
2745  end func; # die
2746
2747
2748const proc: exitCastle (inout playerType: player) is func
2749  begin
2750    writeln("\a");
2751    write("You left the castle with");
2752    if ORBOFZOT not in player.possession then
2753      write("out");
2754    end if;
2755    writeln(" the *ORB OF ZOT*.");
2756    writeln;
2757    if ORBOFZOT in player.possession then
2758      writeln("An incredibly glorious victory!!");
2759      writeln;
2760      writeln("In addition, you got out with the following:");
2761      writeln("  your life");
2762      excl(player.possession, ORBOFZOT);
2763    else
2764      writeln("A less than awe-inspiring defeat.");
2765      writeln;
2766      writeln("When you left the castle, you had:");
2767      writeln("  your miserable life");
2768    end if;
2769    listInventory(player);
2770    writeln;
2771    writeln("And it took you " <& player.turns <& " turns!");
2772  end func; # exitCastle
2773
2774
2775const proc: main is func
2776  local
2777    var playerType: player is playerType.value;
2778    var fightStateType: fightState is fightStateType.value;
2779    var char: ch is ' ';
2780  begin
2781    OUT := STD_CONSOLE;
2782    IN := openEditLine(KEYBOARD, OUT);
2783    startText();
2784    repeat
2785      initRoomConnections();
2786      initRoomProperties();
2787      initRoomTransfers();
2788      labyrinthNumber := rand(1, 50);
2789      player := playerType.value;
2790      fightState := fightStateType.value;
2791      readSpecies(player);
2792      readSex(player);
2793      readAttributes(player);
2794      buyArmor(player, 7, 5, 3);
2795      buyWeapon(player, 7, 5, 3);
2796      buyLamp(player, 2);
2797      buyFlares(player);
2798      if not player.quitDialog then
2799        writeln;
2800        writeln("Ok, " <& player.species <& ", you are now entering the castle.");
2801        writeln("Type H for help.");
2802        currentRoomRef := labyrinth[rangeLaby(4)][1][1];
2803        enterRoom(currentRoomRef, player, fightState);
2804        repeat
2805          incr(player.turns);
2806          incident(currentRoomRef, player);
2807          curesAndDissolves(player);
2808          writeRemark(player);
2809          executeCommand(currentRoomRef, player, fightState);
2810          writeFightState(currentRoomRef, player, fightState);
2811        until not player.living or player.leaveCastle or player.quitProgram;
2812        if not player.living then
2813          die(player);
2814        elsif player.leaveCastle then
2815          exitCastle(player);
2816        end if;
2817      end if;
2818      repeat
2819        writeln;
2820        write("Are you foolish enough to want to play again? ");
2821        ch := readChar();
2822        if not ch in {'Y', 'N'} then
2823          writeln;
2824          writeln("** Please answer yes or no.");
2825        end if;
2826      until ch in {'Y', 'N'};
2827      writeln;
2828      if ch = 'Y' then
2829        writeln("Some " <& player.species <& "s never learn!");
2830      else
2831        writeln("Maybe dumb " <& player.species <& " is not so dumb after all!");
2832      end if;
2833    until ch = 'N';
2834  end func; # main
2835