# # Akalabeth Main Game File

This is basically a diary of my attempts to understand the BASIC code, in the order I went through it.

Over time, I may break things off into more of a reference structure.

The very last routine is a good place to start. It's the adventure shop.

First we clear the screen and print the title.

``````60200  HOME : PRINT "WELCOME TO THE ADVENTURE SHOP"
``````

Next we ask the player what item they want to buy (they just press a key).

``````60210  PRINT "WHICH ITEM SHALT THOU BUY ";: GET Q\$: IF Q\$ = "Q" THEN  PRINT : PRINT "BYE": FOR Z = 1 TO 1000: NEXT : TEXT : HOME : RETURN
``````

If `"Q"` is pressed, we say `BYE`, pause, clear the screen again and return.

Next, given what was pressed, we work out the corresponding index `Z` into some array and the price `P`.

We initially set `Z` to `-1` so we know if we've fallen through the if statements without matching anything.

``````60215 Z =  - 1
60220  IF Q\$ = "F" THEN  PRINT "FOOD":Z = 0:P = 1
60221  IF Q\$ = "R" THEN  PRINT "RAPIER":Z = 1:P = 8
60222  IF Q\$ = "A" THEN  PRINT "AXE":Z = 2:P = 5
60223  IF Q\$ = "S" THEN  PRINT "SHIELD":Z = 3:P = 6
60224  IF Q\$ = "B" THEN  PRINT "BOW":Z = 4:P = 3
60225  IF Q\$ = "M" THEN  PRINT "AMULET":Z = 5:P = 15
``````

If `Z` is still `-1`, we did match anything so display and message and try again:

``````60226  IF Z =  - 1 THEN  PRINT Q\$: PRINT "I'M SORRY WE DON'T HAVE THAT.": GOTO 60210
``````

Next we check if the player's class allows them to have a rapier or bow. Class is stored in `PT\$` and `M` corresponds to a Mage.

``````60227  IF Q\$ = "R" AND PT\$ = "M" THEN  PRINT "I'M SORRY MAGES": PRINT "CAN'T USE THAT!": GOTO 60210
60228  IF Q\$ = "B" AND PT\$ = "M" THEN  PRINT "I'M SORRY MAGES": PRINT "CAN'T USE THAT!": GOTO 60210
``````

Next we check if the player can afford the price `P`. `C(5)` seems to be the amount of gold the player has.

``````60230  IF C(5) - P < 0 THEN  PRINT "M'LORD THOU CAN NOT AFFORD THAT ITEM.": GOTO 60210
``````

`PW` seams to be an array of how many items of each type the player has. Normally buying an item increments its count by 1 but food is incremented by 10 so in that case we first increment it by 9 before the general case increments it by another one. We also subtract the price `P` from `C(5)`.

``````60235  IF Z = 0 THEN PW(Z) = PW(Z) + 9
60236 PW(Z) = PW(Z) + 1:C(5) = C(5) - P
``````

Finally, we update the display with the new counts:

``````60237  VTAB (10): HTAB (16): PRINT C(5);"  "
60240  VTAB (5 + Z): HTAB (25 -  LEN ( STR\$ (PW(Z)))): PRINT PW(Z);: HTAB (1): VTAB (14): PRINT
``````

and go back to asking if they want to buy anything else:

``````60250  GOTO 60210
``````

So, in summary:

• `C(5)` seems to contain the amount of gold the player has
• `PW()` seems to contain the count of various items the player has

The index of `PW()` seems to range over:

• `0` = FOOD
• `1` = RAPIER
• `2` = AXE
• `3` = SHIELD
• `4` = BOW
• `5` = AMULET

On the screen:

• `(16, 10)` seems to hold the amount of gold
• `(5 + z, 25 - len(PW(z)))` holds `PW(z)`

## # Stats and Inventory Display

The routine above the Adventure Shop seems to be for displaying stats and inventory.

``````60080  TEXT : HOME : PRINT : PRINT : PRINT "     STAT'S              WEAPONS": PRINT : FOR X = 0 TO 5: PRINT C\$(X);C(X); TAB 24);"0-";W\$(X): NEXT : POKE 34,12: HOME : POKE 35,15
``````

This suggests that `C\$()` contains the name of the stats and `C()` the value (which would make `5` correspond to gold (see above). It also suggests `W\$()` contains the names of the items.

The `POKE 34,12` and `POKE 35,15` set the top and bottom of the text window.

The `TAB 24)` without opening parentheses is odd and may be a bug in my de-tokenizer.

We show the key to quit:

``````60081  VTAB (11): HTAB (18): PRINT "Q-QUIT"
``````

If the amount of food is more than zero, we clear the hires screen (not sure why only if the amount of food is more than zero):

``````60082  IF PW(0) > 0 THEN  CALL 62450
``````

Next we display the item counts, as we saw in the shop code:

``````60085  FOR Z = 0 TO 5: VTAB (5 + Z): HTAB (25 -  LEN ( STR\$ (PW(Z)))): PRINT PW(Z);: NEXT
``````

Next we print the `PRICE`, `DAMAGE`, and `ITEM` headers:

``````60090  VTAB (17): HTAB (5): PRINT "PRICE";: HTAB (15): PRINT "DAMAGE";: HTAB (25): PRINT "ITEM"
``````

The names of the items:

``````60100  FOR X = 0 TO 5: VTAB (19 + X): HTAB (25): PRINT W\$(X): NEXT
``````

And various hard-coded values:

``````60110  VTAB (19): HTAB (5): PRINT "1 FOR 10": HTAB (15): PRINT "N/A": VTAB (20): HTAB (5): PRINT "8": HTAB (15): PRINT "1-10": VTAB (21): HTAB (5): PRINT "5": HTAB (15): PRINT "1-5"
60120  VTAB (22): HTAB (5): PRINT "6": HTAB (15): PRINT "1": VTAB (23): HTAB (5): PRINT "3": HTAB (15): PRINT "1-4": VTAB (24): HTAB (5): PRINT "15": HTAB (15): PRINT "?????": HOME
60130  RETURN
``````

So far:

• `C()` seems to contain the player stats (include `5` = GOLD)
• `C\$()` seems to contain the names of the stats
• `PW()` seems to contain the count of various items the player has
• `W\$()` seems to contain the names of the items

## # Item Name Setup

The routine at 60070 sets up the item names, then GOSUBs to the stat/inventory display and adventure shop.

``````60070  DIM W\$(5): DATA    "FOOD","RAPIER","AXE","SHIELD","BOW AND ARROWS","MAGIC AMULET": FOR X = 0 TO 5: READ W\$(X): NEXT
60075  GOSUB 60080: GOSUB 60200: RETURN
``````

Note that 60080 and 60200 are GOSUB'd to elsewhere so this is not the only entry point to those routines.

## # Character Creation

The last (or actually first) of the 60000-series routines seems to be character creation and initialization.

First we ask for the player's lucky number which is put in `LN` and will be used to generate a random `ZZ`.

``````60000  TEXT : HOME : VTAB (5): INPUT "TYPE THY LUCKY NUMBER.....";Q\$:LN =  VAL (Q\$)
``````

Next we ask for the level of play `LP` and repeat until the value is between 1 and 10:

``````60005  VTAB (7): INPUT "LEVEL OF PLAY (1-10)......";Q\$:LP =  INT ( VAL (Q\$))
60006  IF LP < 1 OR LP > 10 THEN 60005
``````

We generate a random `ZZ` based on the player's lucky number `LN`.

``````60010 ZZ =  RND ( -  ABS (LN))
``````

Here are our stat names. Note they are padded to length 15:

``````60020  DATA   "HIT POINTS.....","STRENGTH.......","DEXTERITY......","STAMINA........","WISDOM.........","GOLD..........."
``````

Next we declare `PW()` (item/weapon counts), declare and initialize `C\$()` (stats names) and declare `C()` (stats values):

``````60025  DIM PW(5)
60030  DIM C\$(5): FOR X = 0 TO 5: READ C\$(X): NEXT
60040  DIM C(5)
``````

Next we declare `M\$()`, `ML%` and `MZ%`:

``````60041  DIM M\$(10),ML%(10,1),MZ%(10,1)
``````

And initialize `M\$` with the monster names:

``````60042  DATA       "SKELETON","THIEF","GIANT RAT","ORC","VIPER","CARRION CRAWLER","GREMLIN","MIMIC","DAEMON","BALROG"
60043  FOR X = 1 TO 10: READ M\$(X): NEXT
``````

Next we initialize the player stats:

``````60050  FOR X = 0 TO 5:C(X) =  INT ( SQR ( RND (1)) * 21 + 4): NEXT X
``````

And display them, asking the player to accept them:

``````60060  HOME : VTAB (8): FOR X = 0 TO 5: PRINT C\$(X),C(X): NEXT : PRINT : PRINT "SHALT THOU PLAY WITH THESE QUALITIES?": HTAB (20): GET Q\$: IF Q\$ <  > "Y" THEN 60050
``````

Finally, we ask if they want to be a fighter or a mage (putting `M` or `F` in `PT\$`):

``````60061  VTAB (15): PRINT : PRINT "AND SHALT THOU BE A FIGHTER OR A MAGE?": HTAB (20): GET PT\$
``````

When we're done, we go to the routine at 60070 that sets up the item names and GOSUBs to the stat/inventory display and adventure shop. The RETURN in 60075 will effectively act as the RETURN from this routine.

``````60062  IF PT\$ = "M" OR PT\$ = "F" THEN 60070
``````

Or, if the class selection was invalid, we ask again:

``````60063  GOTO 60061
``````

The global variables we know so far:

• `C\$()` player stat/quality names (padded to 15)
• `C()` player stat/quality values
• `W\$()` weapon/item names
• `PW()` player weapon/item counts
• `M\$` monster names
• `ML%` don't know yet
• `MZ%` don't know yet
• `PT\$` player type/class (`F` for Fighter, `M` for Mage)
• `LN` lucky number (used in generation of `ZZ`)
• `ZZ` don't know yet

## # Stats / Qualities

• `0` = HIT POINTS
• `1` = STRENGTH
• `2` = DEXTERITY
• `3` = STAMINA
• `4` = WISDOM
• `5` = GOLD

## # Weapons/Items

• `0` = FOOD
• `1`= RAPIER
• `2`= AXE
• `3`= SHIELD
• `4` = BOW AND ARROWS
• `5` = AMULET

## # Monsters

• `1` = SKELETON
• `2` = THIEF
• `3` = GIANT RAT
• `4` = ORC
• `5` = VIPER
• `6` = CARRION CRAWLER
• `7` = GREMLIN
• `8` = MIMIC
• `9` = DAEMON
• `10` = BALROG

## # Weapon/Items Prices and Damage

(displayed on lines 19–24 and horizontal tabs at 5 and 15)

``````1 FOR 10  N/A
8         1-10
5         1-5
6         1
3         1-4
15        ?????
``````

## # Subroutines

There are 15 `GOSUB`s in Akalabeth.

They go to:

• 100 (two times)
• 200
• 500 (four times)
• 2000
• 4000

And the three we've already seen:

• 60000
• 60080 (three times)
• 60200 (two times)

There are only 8 `RETURN`s. They are at:

• 190 (return for 100 although it's possible to start at 90)
• 490 (return for 200 although line 491 should form part of this section too)
• 590 (return for 500)
• 2090 (return for 2000)
• 4999 (return for 4000)
• 60075 (return for 60000)
• 60130 (return for 60080)
• 60210 (60200 actually goes until 60250 but that last line is a go to back to 60210 which returns if the player quits from the shop)

Other code sections not covered by this:

• 1000–1700
• 3087–3089
• 6000–6060 (player death)
• 7000–7990 (Lord British's Castle, including quest bestowal, quest hand-in and game completion)

## # Lord British's Castle

This code is lines 7000–7990. We get there from line 1515.

We clear the screen and go to text mode.

``````7000  HOME : TEXT : HOME
7001  CALL 62450
``````

`CALL 62450` clears the hires screen to black.

If the player has a name (in `PN\$`) they've been here before so we skip the next part.

``````7010  IF PN\$ <  > "" THEN 7500
``````

However, if the player is new we welcome them.

### # Newcomer

``````7020  PRINT : PRINT : PRINT "     WELCOME PEASANT INTO THE HALLS OF": PRINT "THE MIGHTY LORD BRITISH. HEREIN THOU MAYCHOOSE TO DARE BATTLE WITH THE EVIL": PRINT "CREATURES OF THE DEPTHS, FOR GREAT": PRINT "REWARD!"
``````

``````7030  PRINT : PRINT "WHAT IS THY NAME PEASANT ";: INPUT PN\$
``````

We next ask if they want grand adventure. If they don't answer "Y" we remove their name and GOTO 1090.

``````7040  PRINT "DOEST THOU WISH FOR GRAND ADVENTURE ? ";: GET Q\$: IF Q\$ <  > "Y" THEN  PRINT : PRINT "THEN LEAVE AND BEGONE!":PN\$ = "": PRINT : PRINT "         PRESS -SPACE- TO CONT.";: GET Q\$: GOTO 1090
``````

However, if they accept, we set them a task:

``````7045  PRINT
7050  PRINT : PRINT "GOOD! THOU SHALT TRY TO BECOME A ": PRINT "KNIGHT!!!": PRINT : PRINT "THY FIRST TASK IS TO GO INTO THE": PRINT "DUNGEONS AND TO RETURN ONLY AFTER": PRINT "KILLING A(N) ";:TASK =  INT (C(4) / 3): PRINT M\$(TASK)
``````

`TASK` is initially calculated from the player's WISDOM stat and indicates the monster to kill (equivalent to the index into `M\$` which gives us the monster's name).

At quest bestowal, the player gains 1 point on all stats (including GOLD). Again we exit with a GOTO 1090.

``````7060  PRINT : PRINT "     GO NOW UPON THIS QUEST, AND MAY": PRINT "LADY LUCK BE FAIR UNTO YOU.....": PRINT ".....ALSO I, BRITISH, HAVE INCREASED": PRINT "EACH OF THY ATTRIBUTES BY ONE!"

7070  PRINT : PRINT "         PRESS -SPACE- TO CONT.";: GET Q\$: FOR X = 0 TO 5:C(X) = C(X) + 1: NEXT : HOME : GOTO 1090
``````

### # Return

First we check if `TASK` is still positive (indicating a quest was assigned but not completed).

If so, we remind the player of their task and send them back with a GOTO 1090.

``````7500  IF TASK > 0 THEN  PRINT : PRINT : PRINT PN\$;" WHY HAST THOU RETURNED?": PRINT "THOU MUST KILL A(N) ";M\$(TASK): PRINT "GO NOW AND COMPLETE THY QUEST!": PRINT : PRINT "         PRESS -SPACE- TO CONT.";: GET Q\$: HOME : GOTO 1090
``````

If `ABS(TASK) = 10` then, at this point, `TASK` must be `-10`. If this is the case, the player has become a knight.

``````7510  PRINT : PRINT : PRINT : PRINT "AAHH!!.....";PN\$: PRINT : PRINT "THOU HAST ACOMPLISHED THY QUEST!": IF  ABS (TASK) = 10 THEN 7900
``````

If the player hasn't done enough to become a knight, we give them a new quest (with `TASK` += 1).

We then GOTO 7060 (the last part of the initial quest bestowal) where stats are increased and we leave the castle (GOTO 1090):

``````7520  PRINT "UNFORTUNATELY, THIS IS NOT ENOUGH TO": PRINT "BECOME A KNIGHT.":TASK =  ABS (TASK) + 1: PRINT : PRINT "NOW THOU MUST KILL A(N) ";M\$(TASK)
7530  GOTO 7060
``````

We can see from the above that a positive `TASK` means "kill monster `M\$(TASK)`" whereas a negative `TASK` means, "player has successfully killed monster `M\$(ABS(TASK))`".

### # Knighthood

Now, if the player has completed TASK 10 (the BALROG), they become a knight.

``````7900  TEXT : HOME : PRINT : PRINT : PRINT :PN\$ = "LORD " + PN\$: PRINT "     ";PN\$;","
7910  PRINT "       THOU HAST PROVED THYSELF WORTHY": PRINT "OF KNIGHTHOOD, CONTINUE PLAY IF THOU": PRINT "DOTH WISH, BUT THOU HAST ACOMPLISHED": PRINT "THE MAIN OBJECTIVE OF THIS GAME..."
``````

We skip if the level being played at was 10.

``````7920  IF LP = 10 THEN 7950
``````

If the level wasn't 10, we suggest them trying at one higher level than they just played.

``````7930  PRINT : PRINT "   NOW MAYBE THOU ART FOOLHEARTY": PRINT "ENOUGH TO TRY DIFFICULTY LEVEL ";LP + 1
``````

We exit with a GOTO 7070 which still ups the stats but doesn't show the message saying that's what's being done.

``````7940  GOTO 7070
``````

However, if the BALROG is killed on level 10, we reach here, with a suggestion to call California Pacific Computer to report the accomplishment.

``````7950  PRINT : PRINT "...CALL CALIFORNIA PACIFIC COMPUTER": PRINT "AT (415)-569-9126 TO REPORT THIS": PRINT "AMAZING FEAT!"
``````

We still bump the stats on exit from the castle (although again, without mentioning it).

``````7990  GOTO 7070
``````

So three new global variables are now known:

• `PN\$` player name
• `LP` difficulty level being played at
• `TASK` if positive, the monster the player is on a quest to kill, if negative then `ABS(TASK)` gives the quest monster just killed

## # Player Death

Player death is dealt with in lines 6000–6060. 6000 is called from line 1093 if HIT POINTS (`C(0)`) hits or drops below 0.

First we set the window width to 40 then print the death announcement.

``````6000  POKE 33,40: PRINT : PRINT : PRINT "        WE MOURN THE PASSING OF"
``````

If the player name is longer than 22 characters or there isn't a player name, we just call the player "THE PEASANT":

``````6005  IF  LEN (PN\$) > 22 THEN PN\$ = ""
6010  IF PN\$ = "" THEN PN\$ = "THE PEASANT"
``````

We then add " AND HIS COMPUTER" to the name.

``````6020 PN\$ = PN\$ + " AND HIS COMPUTER"
``````

We centre the name:

``````6030  HTAB (20 -  INT ( LEN (PN\$) / 2)): PRINT PN\$
``````

And offer a resurrection via the ESC key:

``````6035  PRINT "  TO INVOKE A MIRACLE OF RESSURECTION"
6040  PRINT "             <HIT ESC KEY>";
``````

We loop until we get the ESC key:

``````6050  IF  PEEK ( - 16384) = 155 THEN 1
6060  GOTO 6050
``````

On ESC we go back to the start of the program on line 1.

So far, I've noticed the following typos:

• ACOMPLISHED (line 7510)
• RESSURECTION (line 6035)

## # Commands

The command loop and command handling is done in lines 1000–1700.

First we go to the bottom of the screen and print the prompt. `CALL -868` clears the line to the right of the cursor:

``````1000  VTAB (24): PRINT "COMMAND? ";: CALL  - 868
``````

Then we loop until we get a key press:

``````1001 X =  PEEK ( - 16384): IF X < 128 THEN 1001
``````

We see how much memory is available (not yet sure why):

``````1002 Q =  FRE (0)
``````

and reset the keyboard:

``````1010  POKE  - 16368,0
``````

### # Command Dispatch

Many commands are handled differently depending on whether we're outside or inside.

These commands are:

• `CR` (141) NORTH 1100 or FORWARD 1150
• `RIGHT ARROW` (149) EAST 1200 or TURN RIGHT 1250
• `LEFT ARROW` (136) WEST 1300 or TURN LEFT 1350
• `/` (175) SOUTH 1400 or TURN AROUND 1450
• `X` (216) GO 1500 or 1550
• `A` or `27` (193 or 155) ATTACK 1600 or 1650

`SGN(INOUT)` will be 0 if we're outside and 1 if we're inside.

``````1030  IF X = 141 THEN  ON  SGN (INOUT) + 1 GOTO 1100,1150
1040  IF X = 149 THEN  ON  SGN (INOUT) + 1 GOTO 1200,1250
1050  IF X = 136 THEN  ON  SGN (INOUT) + 1 GOTO 1300,1350
1060  IF X = 175 THEN  ON  SGN (INOUT) + 1 GOTO 1400,1450
1070  IF X = 216 THEN  ON  SGN (INOUT) + 1 GOTO 1500,1550
1080  IF X = 193 OR X = 155 THEN  ON  SGN (INOUT) + 1 GOTO 1600,1650
``````

`SPACE` passes:

``````1081  IF X = 160 THEN  PRINT "PASS": GOTO 1090
``````

`S` shows stats/inventory:

``````1085  IF X = 211 THEN 1700
``````

`P` toggles PAUSE ON/OFF which we store as `PA` (1 for ON, 0 for OFF):

``````1086  IF X = 208 THEN  IF PA = 1 THEN PA = 0: PRINT "PAUSE OFF": GOTO 1000
1087  IF X = 208 THEN  IF PA = 0 THEN PA = 1: PRINT "PAUSE ON": GOTO 1000
``````

Note that PAUSE ON/OFF doesn't call the Post-Command code (so doesn't reduce food, etc)

Any other keys and we print "HUH?" and go back to the start of this routine:

``````1089  PRINT "HUH?": GOTO 1000
``````

### # Post-Command

Once the command is executed we normally GOTO line 1090.

First we reduce food by 1 (or 0.1 if we're inside). If FOOD hits 0 then we set HIT POINTS to 0 too, announce the player has starved and go to line 1093 (which in turn will send us to the Death routine at 6000)

``````1090 PW(0) = PW(0) - 1 +  SGN (INOUT) * .9: IF PW(0) < 0 THEN C(0) = 0: PRINT : PRINT "YOU HAVE STARVED!!!!!": GOTO 1093
``````

If we haven't starved, though, we update our FOOD / HIT POINTS / GOLD display:

``````1091  POKE 33,40: VTAB (22): HTAB (30): PRINT "FOOD=";PW(0);: CALL  - 868: VTAB (23): HTAB (30): PRINT "H.P.=";C(0);: CALL  - 868: VTAB (24): HTAB (30): PRINT "GOLD=";C(5);: CALL  - 868: POKE 33,29: HTAB (1)
``````

We round our FOOD to one decimal place.

``````1092 PW(0) =  INT (PW(0) * 10) / 10
``````

If HIT POINTS are zero or less, we go to the Death handling routine:

``````1093  IF C(0) <  = 0 THEN 6000
``````

If we're in a dungeon (`IN` is same as `INOUT`) we move the monsters around. If this has resulted in HIT POINTS being zero or less, we go back to the previous line to go to the Death handling routine.

``````1095  IF IN > 0 THEN  GOSUB 4000: IF C(0) <  = 0 THEN 1093
``````

The following is the same as line 1091. I guess we do it again because the monster movement in the previous line may have changed some values.

``````1096  POKE 33,40: VTAB (22): HTAB (30): PRINT "FOOD=";PW(0);: CALL  - 868: VTAB (23): HTAB (30): PRINT "H.P.=";C(0);: CALL  - 868: VTAB (24): HTAB (30): PRINT "GOLD=";C(5);: CALL  - 868: POKE 33,29: HTAB (1)
``````

Not yet sure what GOSUB 100 or GOSUB 200 do. Perhaps draw.

``````1097  IF INOUT = 0 THEN  GOSUB 100: GOTO 1000
1098  IF INOUT > 0 THEN  GOSUB 200: GOTO 1000
``````

### # NORTH (Outside)

If there's not a mountain in the way, move north.

``````1100  PRINT "NORTH": IF TER%(TX,TY - 1) = 1 THEN  PRINT "YOU CAN'T PASS THE MOUNTAINS": GOTO 1090
1110 TY = TY - 1: GOTO 1090
``````

### # FORWARD (Inside)

If it's possible to move forward, make the move:

``````1150  IF DNG%(PX + DX,PY + DY) <  > 1 AND DNG%(PX + DX,PY + DY) < 10 THEN PX = PX + DX:PY = PY + DY
1155  PRINT "FORWARD"
``````

First we check if the location the player moved to has a TRAP. If it does, the player loses a random amount of HIT POINTS based on dungeon level, `MR` is set to 1 (don't know what that is yet), the dungeon level is incremented and we GOSUB to 500 to generate a new dudgeon map. Note that `INOUT` and `IN` are the same variable.

``````1160  IF DNG%(PX,PY) = 2 THEN  PRINT "AAARRRGGGHHH!!! A TRAP!":C(0) = C(0) -  INT ( RND (1) * INOUT + 3):MR = 1:INOUT = INOUT + 1: PRINT "FALLING TO LEVEL ";IN: GOSUB 500: GOTO 1090
``````

If there's a chest where the player is located, we take the chest, randomly give an amount of GOLD based on dungeon level as well as an item.

``````1165 Z = 0
1170  IF DNG%(PX,PY) = 5 THEN DNG%(PX,PY) = 0: PRINT "GOLD!!!!!":Z =  INT ( RND (1) * 5 * INOUT + INOUT): PRINT Z;"-PIECES OF EIGHT":C(5) = C(5) + Z
1175  IF Z > 0 THEN Z =  INT ( RND (1) * 6): PRINT "AND A ";W\$(Z):PW(Z) = PW(Z) + 1: GOTO 1090
1190  GOTO 1090
``````

### # EAST (Outside)

Moving EAST is the same as moving NORTH but we increment `TX` instead of decrementing `TY`.

``````1200  PRINT "EAST": IF TER%(TX + 1,TY) = 1 THEN  PRINT "YOU CAN'T PASS THE MOUNTAINS": GOTO 1090
1210 TX = TX + 1: GOTO 1090
``````

### # TURN RIGHT (Inside)

Rotate the player:

``````1250  PRINT "TURN RIGHT"
1255  IF DX <  > 0 THEN DY = DX:DX = 0: GOTO 1090
1260 DX =  - DY:DY = 0: GOTO 1090
``````

### # WEST (Outside)

Moving WEST is the same as moving EAST but we decrement `TX` instead of incrementing it.

``````1300  PRINT "WEST": IF TER%(TX - 1,TY) = 1 THEN  PRINT "YOU CAN'T PASS THE MOUNTAINS": GOTO 1090
1310 TX = TX - 1: GOTO 1090
``````

### # TURN LEFT (Inside)

Rotate the player:

``````1350  PRINT "TURN LEFT"
1355  IF DX <  > 0 THEN DY =  - DX:DX = 0: GOTO 1090
1360 DX = DY:DY = 0: GOTO 1090
``````

### # SOUTH (Outside)

Moving SOUTH is the same as moving NORTH but we increment `TY` instead of decrementing it.

``````1400  PRINT "SOUTH": IF TER%(TX,TY + 1) = 1 THEN  PRINT "YOU CAN'T PASS THE MOUNTAINS": GOTO 1090
1410 TY = TY + 1: GOTO 1090
``````

### # TURN AROUND (Inside)

Flip the direction of the player:

``````1450  PRINT "TURN AROUND":DX =  - DX:DY =  - DY: GOTO 1090
``````

### # GO (Outside)

If it's a town, enter the shop:

``````1500  IF TE%(TX,TY) = 3 THEN  GOSUB 60080: GOSUB 60200: GOTO 1090
``````

If it's a dungeon and `INOUT` is 0 (not sure when it wouldn't be at this point), enter the dungeon. Set `INOUT` to 1, randomize the dungeon map (GOSUB 500) then initialize the player location and direction.

``````1510  IF TE%(TX,TY) = 4 AND INOUT = 0 THEN  PRINT "GO DUNGEON": PRINT "PLEASE WAIT ":INOUT = 1: GOSUB 500:DX = 1:DY = 0:PX = 1:PY = 1: GOTO 1090
``````

If it's Lord British's castle, enter:

``````1515  IF TE%(TX,TY) = 5 THEN 7000
``````

Otherwise print "HUH?" and loop back (although don't do post-command):

``````1520  PRINT "HUH?": GOTO 1000
``````

### # GO (Inside)

Skip ahead if it's not a ladder down (not yet sure distinction between 7 and 9):

``````1550  IF DNG%(PX,PY) <  > 7 AND DNG%(PX,PY) <  > 9 THEN 1580
``````

Otherwise say we're going down a level, increment `INOUT` and randomize the next level (GOSUB 500):

``````1555  PRINT "GO DOWN TO LEVEL ";INOUT + 1
1560 INOUT = INOUT + 1: GOSUB 500:MR = 1: GOTO 1090
``````

So here's where we go if it's not a down ladder (7 or 9). If it's also not an up ladder (8), print "HUH?"

``````1580  IF DNG%(PX,PY) <  > 8 THEN  PRINT "HUH?": GOTO 1090
``````

But if it's an up ladder...

If the player was already at the first level, print that they're leaving and change `IN` (`INOUT`) to 0:

``````1581  IF IN = 1 THEN  PRINT "LEAVE DUNGEON":IN = 0: GOTO 1586
``````

Otherwise say they're going up a level, decrement `INOUT` and randomize the dungeon level (GOSUB 500). Also set `MR` to 1 (not yet sure what that's for).

``````1584  PRINT "GO UP TO LEVEL ";INOUT - 1
1585 INOUT = INOUT - 1: GOSUB 500:MR = 1
``````

If we've left the dungeon, increment the HIT POINTS (`C(0)`) by `LK` (which is incremented on monster kill below).

``````1586  IF IN = 0 THEN  PRINT "THOU HAST GAINED": PRINT LK;" HIT POINTS":C(0) = C(0) + LK:LK = 0
1587  GOTO 1090
``````

### # ATTACK (Outside)

Attack is a no-op outside (although we run the post-command to reduce food, etc)

``````1600  GOTO 1090
``````

### # ATTACK (Inside)

See separate analysis below.

### # STATISTICS / INVENTORY

Call the stats/inventory routine then prompt to hit CR to go back.

``````1700  GOSUB 60080: HOME : PRINT "PRESS -CR- TO CONTINUE";: INPUT Q\$: TEXT : HOME : GOTO 1090
``````

What we learnt from the command handling routines above:

• `TER%()` contains the terrain type at an outside location
• `DNG%()` contains what is at a particular location in a dungeon
• `TX` and `TY` are the player's location outside
• `PX` and `PY` are the player's location inside
• `DX` and `DY` are how `PX` and `PY` will change if the player steps forward (i.e. they give the direction the player is facing in the dungeon)

Values of `TER%()` are:

• `1` = MOUNTAINS
• `3` = TOWN (SHOP)
• `4` = DUNGEON
• `5` = LORD BRITISH'S CASTLE

Values of `DNG%()` are:

• `1` = WALL?
• `2` = TRAP
• `5` = CHEST
• `7` or `9` = DOWN LADDER
• `8` = UP LADDER

## # Attack

First we initialize `MN` (the monster being attacked) and `DAM` (the damage being done) to 0. Then we ask what weapon the player wants to use:

``````1650 MN = 0:DAM = 0: PRINT "ATTACK": PRINT "WHICH WEAPON ";: GET Q\$
``````

Then, depending on what they select, we set the appropriate damage `DAM`. But if the player does not own the item, say so and go back to asking which weapon:

``````1651  IF Q\$ = "R" THEN DAM = 10: PRINT "RAPIER": IF PW(1) < 1 THEN  PRINT "NOT OWNED": GOTO 1650
1652  IF Q\$ = "A" THEN DAM = 5: PRINT "AXE": IF PW(2) < 1 THEN  PRINT "NOT OWNED": GOTO 1650
1653  IF Q\$ = "S" THEN DAM = 1: PRINT "SHIELD": IF PW(3) < 1 THEN  PRINT "NOT OWNED": GOTO 1650
1654  IF Q\$ = "B" THEN DAM = 4: PRINT "BOW": IF PW(4) < 1 THEN  PRINT "NOT OWNED": GOTO 1650
``````

If MAGIC AMULET has been selected, skip down to line 1680:

``````1655  IF Q\$ = "M" THEN  PRINT "MAGIC AMULET": GOTO 1680
``````

Check player class (`\$PT`) and, if player is a mage and they picked a BOW or RAPIER, tell them they can't use it and go back to asking which weapon again:

``````1656  IF Q\$ = "B" AND PT\$ = "M" THEN  PRINT "MAGES CAN'T USE BOWS!": GOTO 1650
1657  IF Q\$ = "R" AND PT\$ = "M" THEN  PRINT "MAGES CAN'T USE RAPIERS!": GOTO 1650
``````

If none of the weapons matched, `DAM` will still be 0 and we'll assume HANDS.

``````1659  IF DAM = 0 THEN  PRINT "HANDS"
``````

If it's an AXE or BOW, we skip ahead to line 1670:

``````1660  IF DAM = 5 OR DAM = 4 THEN 1670
``````

### # Melee Weapon

This next line is unique to Melee Weapons. We work out which monster type is in the direction the player is facing. (Looks like `DN%()` might contain 10 x the monster level/type at that location).

``````1661 MN = DN%(PX + DX,PY + DY) / 10:MN =  INT (MN)
``````

### # Common Attack Code

But from this point on, the code is shared with Ranged weapons too...

If there are no monsters, `MN` will be < 1 (not sure if it will ever be negative). The hit chance is based on DEXTERITY, the monster level and the dungeon level.

``````1662  IF MN < 1 OR C(2) -  RND (1) * 25 < MN + INOUT THEN  PRINT "YOU MISSED": GOTO 1668
``````

If we hit, we work out actual damage (based on `DAM` and STRENGTH) and put it back in `DAM` and reduce the HIT POINTS accordingly. `MZ%(MN,1)` must contain the HIT POINTS of the monster of type `MN`.

``````1663  PRINT "HIT!!! ":DAM = ( RND (1) * DAM + C(1) / 5):MZ%(MN,1) = MZ%(MN,1) - DAM
``````

We display the monster's new HIT POINTS:

``````1664  PRINT M\$(MN);"'S HIT POINTS=";MZ%(MN,1)
``````

If that has dropped to 0 or less, it's a kill. The reward is an amount of gold equal to the monster level + dungeon level.

``````1665  IF MZ%(MN,1) < 1 THEN  PRINT "THOU HAST KILLED A ";M\$(MN): PRINT "THOU SHALT RECEIVE":DA =  INT (MN + IN): PRINT DA;" PIECES OF EIGHT"
``````

We add the gold to the player inventory. We then remove the monster from the `DNG%` map and set `MZ%(MN,0)` to 0. `ML%(MN,0)`must contain the X-cordinate of the monster and `ML%(MN,1)` the Y-coordinate.

``````1666  IF MZ%(MN,1) < 1 THEN C(5) =  INT (C(5) + DA):DNG%(ML%(MN,0),ML%(MN,1)) = DNG%(ML%(MN,0),ML%(MN,1)) - 10 * MN:MZ%(MN,0) = 0
``````

`LK` is the number of HIT POINTS the player will gain when they leave the dungeon. We increment `LK` by half the monster level `MN` times the dungeon level `IN`. If the monster was the quest monster, we mark the quest done (by negating `TASK`).

``````1667 LK = LK +  INT (MN * IN / 2): IF MN = TASK THEN TASK =  - TASK
``````

Regardless of hit or miss, we end up here...

If PAUSE is ON, we wait for a CR to continue:

``````1668  IF PA = 1 THEN  PRINT "-CR- TO CONT. ";: INPUT Q\$
``````

And we're done.

``````1669  GOTO 1090
``````

### # Axe or Bow

If it's an AXE, we give the player the option to THROW (ranged) or SWING (melee). If they SWING, we just go back to the Melee Weapon section above.

``````1670  IF DAM = 5 THEN  PRINT "TO THROW OR SWING:";: GET Q\$: IF Q\$ <  > "T" THEN  PRINT "SWING": GOTO 1661
``````

If it's an AXE (and we've already established at this point we're throwing it) we reduce the count by one:

``````1671  IF DAM = 5 THEN  PRINT "THROW":PW(2) = PW(2) - 1
``````

### # Ranged Attack

We iterate over the 5 squares in the direction the player is facing. If we go off the map (a coordinate hit 0 or 10), we go back to Common Attack Code with `MN` still 0.

If `DNG%(x, y)` has a monster, it will contain `monster level * 10`.

``````1672  FOR Y = 1 TO 5: IF PX + DX * Y < 1 OR PX + DX * Y > 9 OR PY + DY * Y > 9 OR PY + DY * Y < 0 THEN 1662
1673 MN = DNG%(PX + DX * Y,PY + DY * Y):MN =  INT (MN / 10): IF MN > 0 THEN 1662
1674  NEXT : GOTO 1662
``````

### # Magic Amulet

If there's no magic amulet in the player's possession, we go back to asking them which weapon to use.

``````1680  IF PW(5) < 1 THEN  PRINT "NONE OWNED": GOTO 1650
``````

If the player is a fighter, the amulet's action is random:

``````1681  IF PT\$ = "F" THEN Q =  INT ( RND (1) * 4 + 1): GOTO 1685
``````

If the player is a mage, they get to pick the amulet's action:

``````1682  PRINT "1-LADDER-UP","2-LADDER-DN": PRINT "3-KILL","4-BAD??": PRINT "CHOICE ";: GET Q\$:Q =  VAL (Q\$): PRINT Q: IF Q < 1 OR Q > 4 THEN 1682
``````

But in that case, there's a 25% chance the amulet will be on its last charge (and will be removed from the inventory).

``````1683  IF  RND (1) > .75 THEN  PRINT "LAST CHARGE ON THIS AMULET!":PW(5) = PW(5) - 1
``````

Regardless of fighter or mage, we end up here.

``````1685  ON Q GOTO 1686,1690,1691,1692
``````

Q = 1. Make an up-ladder:

``````1686  PRINT "LADDER UP":DNG%(PX,PY) = 8: GOTO 1090
``````

Q = 2. Make a down-ladder:

``````1690  PRINT "LADDER DOWN":DNG%(PX,PY) = 7: GOTO 1090
``````

Q = 3. A magic attack. Set the damage to 10 + dungeon level and go to Ranged Attack.

``````1691  PRINT "MAGIC ATTACK":DAM = 10 + INOUT: GOTO 1672
``````

``````1692  ON  INT ( RND (1) * 3 + 1) GOTO 1693,1695,1697
``````

a) get turned into a TOAD and have STRENGTH, DEXTERITY, STAMINA and WISDOM all turned to 3.

``````1693  PRINT "YOU HAVE BEEN TURNED": PRINT "INTO A TOAD!"
1694  FOR Z2 = 1 TO 4:C(Z2) = 3: NEXT Z2: GOTO 1090
``````

b) get turned into a LIZARD MAN and have HIT POINTS, STRENGTH, DEXTERITY, STAMINA and WISDOM all multiplied by 2.5:

``````1695  PRINT "YOU HAVE BEEN TURNED": PRINT "INTO A LIZARD MAN": FOR Y = 0 TO 4:C(Y) =  INT (C(Y) * 2.5): NEXT : GOTO 1090
``````

c) backfire and halve HIT POINTS

``````1697  PRINT "BACKFIRE":C(0) = C(0) / 2: GOTO 1090
``````

More global variables, we've learnt about in the attack code:

• `DNG%()` (or `DN%()`) stores the monster type/level in the tens column. So `37` would mean a GIANT RAT (3) AND a DOWN LADDER (7).
• `MZ%(MN,0)` unsure but is set to 0 when monster killed.
• `MZ%(MN,1)` stores the HIT POINTS of the monster of type `MN` at this dungeon level
• `ML%(MN,0), ML%(MN,1)` location of monster of type `MN` at this dungeon level
• `LK` hit points to gain on leaving dungeon

## # Generating a Dungeon Level

Dungeon level generation is a routine from lines 500–590 (with a call out to 2000).

The dungeon level information is primarily stored in `DNG%()` (sometimes referred to as just `DN%()`) which is an 11 x 11 matrix.

We generate a ZZ but I still don't know what that's for.

``````500 ZZ =  RND ( -  ABS (LN) - TX * 10 - TY * 1000 + INOUT * 31.4)
``````

Next we zero out the inside of `DNG%()`:

``````501  FOR X = 1 TO 9: FOR Y = 1 TO 9:DNG%(X,Y) = 0: NEXT : NEXT
``````

Then we set the outsides to WALLS (`1`):

``````510  FOR X = 0 TO 10:DNG%(X,0) = 1:DNG%(X,10) = 1:DNG%(0,X) = 1:DNG%(10,X) = 1: NEXT
``````

Then we draw rows of walls on the even coordinates.

``````520  FOR X = 2 TO 8 STEP 2: FOR Y = 1 TO 9:DNG%(X,Y) = 1:DNG(Y,X) = 1: NEXT : NEXT
``````

The result at this point is:

``````11111111111
10101010101
11111111111
10101010101
11111111111
10101010101
11111111111
10101010101
11111111111
10101010101
11111111111
``````

Then, for each element of those rows of inside walls, we randomly replace the wall with a TRAP (2), ? (3), ? (4), CHEST (5) or DOWN LADDER? (9).

``````530  FOR X = 2 TO 8 STEP 2: FOR Y = 1 TO 9 STEP 2
540  IF  RND (1) > .95 THEN DNG%(X,Y) = 2
541  IF  RND (1) > .95 THEN DNG%(Y,X) = 2
542  IF  RND (1) > .6 THEN DNG%(Y,X) = 3
543  IF  RND (1) > .6 THEN DNG%(X,Y) = 3
544  IF  RND (1) > .6 THEN DNG%(X,Y) = 4
545  IF  RND (1) > .6 THEN DNG%(Y,X) = 4
546  IF  RND (1) > .97 THEN DNG%(Y,X) = 9
547  IF  RND (1) > .97 THEN DNG%(X,Y) = 9
548  IF  RND (1) > .94 THEN DNG%(X,Y) = 5
549  IF  RND (1) > .94 THEN DNG%(Y,X) = 5
568  NEXT : NEXT
``````

(2,1) is always cleared. If the player is on an even level, (7,3) is a DOWN LADDER and (3, 7) is an UP LADDER. If the player is on an odd level, (7,3) is an UP LADDER and (3,7) is a DOWN LADDER (of course).

``````569 DNG%(2,1) = 0: IF INOUT / 2 =  INT (INOUT / 2) THEN DNG%(7,3) = 7:DNG%(3,7) = 8
570  IF INOUT / 2 <  >  INT (INOUT / 2) THEN DNG%(7,3) = 8:DNG%(3,7) = 7
``````

If the player is on the first level of the dungeon (closest to surface) then (1,1) is an UP LADDER instead of (7,3). Presumably this is where the player starts.

``````580  IF INOUT = 1 THEN DNG%(1,1) = 8:DNG%(7,3) = 0
``````

We call out to 2000 to place monsters.

``````585  GOSUB 2000
``````

And we're done.

``````590  RETURN
``````

## # Monster Placement

We initialize `NM` (the number of monsters created for this dungeon level) to 0 and loop 10 times.

``````2000 NM = 0: FOR X = 1 TO 10
``````

`X`, our loop variable will be the monster level/type.

We set `MZ%(X,0)` for the monster to 0 and the number of HIT POINTS it has (`MZ%(X,1)`) to 3 + monster level + dungeon level (although oddly we override this once we actually place the monster below).

``````2005 MZ%(X,0) = 0:MZ%(X,1) = X + 3 + INOUT
``````

If the monster is more than two levels higher than the dungeon level, we skip it and continue the loop. Even if the monster in consideration is an okay level, there's still only a 40% chance we have one.

``````2010  IF X - 2 > INO OR  RND (1) > .4 THEN 2090
``````

Next we pick a location for the monster.

``````2020 ML%(X,0) =  INT ( RND (1) * 9 + 1):ML%(X,1) =  INT ( RND (1) * 9 + 1)
``````

If it's already occupied (not just by a monster but anything, like a wall) we go back and pick a new location:

``````2030  IF DNG%(ML%(X,0),ML%(X,1)) <  > 0 THEN 2020
``````

If it's the same location as the player, we go back and pick a new location:

``````2040  IF ML%(X,0) = PX AND ML%(X,1) = PY THEN 2020
``````

Now that we've established we definitely have a monster, we place it in `DNG%()` (by putting the monster type/level in the tens column):

``````2050 DNG%(ML%(X,0),ML%(X,1)) = X * 10
``````

We set MZ%(X,0) to 1 for the monster and increment our monster count for the dungeon level.

``````2051 MZ%(X,0) = 1
2052 NM = NM + 1
``````

Finally we set the HIT POINTS to be 2 * (monster level + dungeon level * player difficulty level).

``````2055 MZ%(X,1) = X * 2 + IN * 2 * LP
``````

And we loop back for the next monster type before returning.

``````2090  NEXT : RETURN
``````

## # Monster Movement

We iterate through the 10 types of monsters and check if any of them exist at this level of the dungeon. If not, we skip this entire routine.

``````4000  FOR MM = 1 TO 10: IF MZ%(MM,0) = 0 THEN 4999
``````

So, there's one at level `MM`. It's location is currently given by `ML%(MM,0)` and `ML%(MM,1)`.

First we calculate the distant between the monster and the player:

``````4010 RA =  SQR ((PX - ML%(MM,0)) ; 2 + (PY - ML%(MM,1)) ; 2)
``````

(is ; 2 really how you square? Is that a de-tokenization bug?)

If the HIT POINTS is less than the dungeon level * the player difficulty level, skip to 4030:

``````4011  IF MZ%(MM,1) < IN * LP THEN 4030
``````

``````4020  IF RA < 1.3 THEN 4500
``````

If it's a MIMIC within a distance of 3, it won't do anything and skip to the next monster.

``````4025  IF MM = 8 AND RA < 3 THEN 4999
``````

Calculate the direction the player is in from the monster:

``````4030 X1 =  SGN (PX - ML%(MM,0)):Y1 =  SGN (PY - ML%(MM,1))
``````

If the HIT POINTS is less than the dungeon level * the player difficulty level, flip the directions (i.e. plan to move away from the player):

``````4031  IF MZ%(MM,1) < IN * LP THEN X1 =  - X1:Y1 =  - Y1
``````

If the the player is due east or west (i.e. the change in `Y` is 0), skip the next part.

``````4035  IF Y1 = 0 THEN 4045
``````

(the player isn't due east or west so we're going to need to move north or south) Look at what's in the north/south direction of desired travel. If it's a WALL (1) or another monster or TRAP (2), skip to 4045.

``````4040 D = DNG%(ML%(MM,0),(ML%(MM,1) + Y1 + .5)): IF D = 1 OR D > 9 OR D = 2 THEN 4045
``````

But if the monster can move in that north/south direction, set `X1` to 0 (as it won't be moving east/west this turn).

``````4042 X1 = 0: GOTO 4050
``````

We get here if the player is due east/west or if something it blocking the monster from going north/south. As the monster won't be moving north/south this turn, we set `Y1` to 0. If the player is still due north/south, though, we skip the next line and go to 4050.

``````4045 Y1 = 0: IF X1 = 0 THEN 4050
``````

The monster desires to go east/west. It looks to see what's in the desired direction. If it's a WALL (1) or another monster or a TRAP (2), then set `X1` to 0 (as it can't move in that direction) and go to 4081.

``````4046 D = DN%((ML%(MM,0) + X1 + .5),ML%(MM,1)): IF D = 1 OR D > 9 OR D = 2 THEN X1 = 0: GOTO 4081
``````

We remove the monster from its existing place in `DNG%()`:

``````4050 DNG%(ML%(MM,0),ML%(MM,1)) = DNG%(ML%(MM,0),ML%(MM,1)) - 10 * MM
``````

If the move would place the monster on the place as the player, we skip the rest and go to the next monster (note they would leave it removed it from `DNG%()`, though):

``````4055  IF ML%(MM,0) + X1 = PX AND ML%(MM,1) + Y1 = PY THEN 4999
``````

Otherwise, we move the monster:

``````4060 ML%(MM,0) = ML%(MM,0) + X1:ML%(MM,1) = ML%(MM,1) + Y1
``````

and update the `DNG%()` to include it at its new location:

``````4080 DNG%(ML%(MM,0),ML%(MM,1)) = (DNG%(ML%(MM,0),ML%(MM,1)) + 10 * MM + .5)
``````

(not sure what the `+ .5` here and above is for).

If we get here and there was a movement, we skip to the next monster:

``````4081  IF X1 <  > 0 OR Y1 <  > 0 THEN 4999
``````

If the HIT POINTS is less than the dungeon level * player difficulty level and the player is adjacent to the monster, go to Monster Attack:

``````4082  IF MZ%(MM,1) < IN * LP AND RA < 1.3 THEN 4500
``````

Otherwise, if the HIT POINTS is less than the dungeon level * player difficulty level, the HIT POINTS rise by monster level + dungeon level (healing):

``````4083  IF MZ%(MM,1) < IN * LP THEN MZ%(MM,1) = MZ%(MM,1) + MM + IN
``````

And we move to the next monster:

``````4499  GOTO 4999
``````

### # Monster Attack

If the monster is a THIEF or a GREMLIN, it steals rather than attacks to we skip to Monster Theft:

``````4500  IF MM = 2 OR MM = 7 THEN 4600
``````

We tell the player what they are being attacked by:

``````4509  PRINT "YOU ARE BEING ATTACKED": PRINT "BY A ";M\$(MM)
``````

We determine whether they hit based on whether the player has a shield, the player stamina, the monster level and the dungeon level:

``````4510  IF  RND (1) * 20 -  SGN (PW(3)) - C(3) + MM + IN < 0 THEN  PRINT "MISSED": GOTO 4525
``````

If it's a hit, the HIT POINTS lost is base on monster level + dungeon level:

``````4520  PRINT "HIT":C(0) = C(0) -  INT ( RND (1) * MM + IN)
``````

If PAUSE is ON we wait for the player to hit CR:

``````4525  IF PA = 1 THEN  PRINT "-CR- TO CONT. ";: INPUT Q\$
``````

Then go to the next monster to move:

``````4530  GOTO 4999
``````

### # Monster Theft

There is a 50% chance the thief or gremlin will just attack anyway:

``````4600  IF  RND (1) < .5 THEN 4509
``````

But if they steal...

If they are a gremlin, half the food will be gone:

``````4610  IF MM = 7 THEN PW(0) =  INT (PW(0) / 2): PRINT "A GREMLIN STOLE SOME FOOD": GOTO 4525
``````

Otherwise (i.e. they are a thief), one of the possessions the player has will be stolen at random:

``````4620 ZZ =  INT ( RND (1) * 6): IF PW(ZZ) < 1 THEN 4620
4630  PRINT "A THIEF STOLE A ";W\$(ZZ):PW(ZZ) = PW(ZZ) - 1: GOTO 4525
``````

And finally we go to the next monster then return:

``````4999  NEXT : RETURN
``````

## # Drawing Terrain

The terrain drawing routine appears to be lines 100–190.

As we've already seen, the terrain is stored in `TER%()` and we know from the outside movement commands that `TX` and `TY` store the location of the player.

We further more know that the values of `TER%()` are:

• `1` = MOUNTAINS
• `3` = TOWN (SHOP)
• `4` = DUNGEON
• `5` = LORD BRITISH'S CASTLE

I presume therefore that `2` must be the little squares (never knew what they were supposed to be).

We switch to hires graphics mode and loop over [-1, 0, 1] x [-1, 0, 1] to draw the terrain tile the player is on and the eight surrounding.

``````100  HGR : FOR Y =  - 1 TO 1: FOR X =  - 1 TO 1
``````

We next draw the crosshairs indicated the player's location:

105 HPLOT 138,75 TO 142,75: HPLOT 140,73 TO 140,77

We retrieve the terrain type and put it in `ZZ` then work out the top left corner of the tile to draw. The tiles are 50 x 50 and start at (65, 0).

``````110 ZZ = TER%(TX + X,TY + Y):X1 = 65 + (X + 1) * 50:Y1 = (Y + 1) * 50
``````

### # "Little Square" (2)

``````120  IF ZZ = 2 THEN  HPLOT X1 + 20,Y1 + 20 TO X1 + 30,Y1 + 20 TO X1 + 30,Y1 + 30 TO X1 + 20,Y1 + 30 TO X1 + 20,Y1 + 20
``````

### # Town/Shop (3)

``````130  IF ZZ = 3 THEN  HPLOT X1 + 10,Y1 + 10 TO X1 + 20,Y1 + 10 TO X1 + 20,Y1 + 40 TO X1 + 10,Y1 + 40 TO X1 + 10,Y1 + 30 TO X1 + 40,Y1 + 30 TO X1 + 40,Y1 + 40 TO X1 + 30,Y1 + 40 TO X1 + 30,Y1 + 10 TO X1 + 40,Y1 + 10 TO X1 + 40,Y1 + 20 TO X1 + 10,Y1 + 20 TO X1 + 10,Y1 + 10
``````

### # Dungeon (4)

``````140  IF ZZ = 4 THEN  HPLOT X1 + 20,Y1 + 20 TO X1 + 30,Y1 + 30: HPLOT X1 + 20,Y1 + 30 TO X1 + 30,Y1 + 20
``````

### # Lord British's Castle (5)

``````150  IF ZZ = 5 THEN  HPLOT X1,Y1 TO X1 + 50,Y1 TO X1 + 50,Y1 + 50 TO X1,Y1 + 50 TO X1,Y1: HPLOT X1 + 10,Y1 + 10 TO X1 + 10,Y1 + 40 TO X1 + 40,Y1 + 40 TO X1 + 40,Y1 + 10 TO X1 + 10,Y1 + 10 TO X1 + 40,Y1 + 40: HPLOT X1 + 10,Y1 + 40 TO X1 + 40,Y1 + 10
``````

### # Mountains (1)

``````160  IF ZZ = 1 THEN  HPLOT X1 + 10,Y1 + 50 TO X1 + 10,Y1 + 40 TO X1 + 20,Y1 + 30 TO X1 + 40,Y1 + 30 TO X1 + 40,Y1 + 50: HPLOT X1,Y1 + 10 TO X1 + 10,Y1 + 10: HPLOT X1 + 50,Y1 + 10 TO X1 + 40,Y1 + 10: HPLOT X1,Y1 + 40 TO X1 + 10,Y1 + 40: HPLOT X1 + 40,Y1 + 40 TO X1 + 50,Y1 + 40
170  IF ZZ = 1 THEN  HPLOT X1 + 10,Y1 TO X1 + 10,Y1 + 20 TO X1 + 20,Y1 + 20 TO X1 + 20,Y1 + 30 TO X1 + 30,Y1 + 30 TO X1 + 30,Y1 + 10 TO X1 + 40,Y1 + 10 TO X1 + 40,Y1
``````

Then we loop back for the next tile and then return when all 9 are done:

``````190  NEXT : NEXT : RETURN
``````

## # Initialization

It's about time we got to the first 30 lines of the program.

In case of error...

``````0  ONERR  GOTO 4
1  REM
``````

Reset input / output

``````4  PR # 0: IN # 0
``````

Set HIMEM to \$BFFF (which I guess would effectively remove DOS)

``````5  HIMEM: 49151
``````

Clear all variables and GOSUB to the Character Creation routine.

``````7  CLEAR : GOSUB 60000
``````

Get a random number based on the player's lucky number.

``````8 ZZ =  RND ( -  ABS (LN))
``````

Can't fine where a variable `LE` is used anywhere.

``````9 LEVEL = 0
``````

Switch to text mode and print the welcome message:

``````10  TEXT : HOME : NORMAL : VTAB (12): PRINT " WELCOME TO AKALABETH, WORLD OF DOOM!"
``````

Initialize the arrays:

``````20  DIM DN%(10,10),TE%(20,20),XX%(10),YY%(10),PER%(10,3),LD%(10,5),CD%(10,3),FT%(10,5),LAD%(10,3)
``````
• `DN%(10,10)` is for dungeon level maps
• `TE%(20,20)` is for terrain map
• `XX%(10)` unknown
• `YY%(10)` unknown
• `PER%(10,3)` unknown
• `LD%(10,5)` unknown
• `CD%(10,3)` unknown
• `FT%(10,5)` unknown
• `LAD%(10,3)` unknown

Given we haven't seen any of the unknown ones anywhere else yet, they must be related to drawing the dungeon images.

### # Randomize the Terrain Map

Set the outsides to mountains:

``````30  FOR X = 0 TO 20:TE%(X,0) = 1:TE%(0,X) = 1:TE%(X,20) = 1:TE%(20,X) = 1: NEXT
``````

Warn the player that this next bit could take a while...

``````35 : VTAB (23): PRINT "  (PLEASE WAIT)";
``````

Completely randomize the inside 19 x 19 terrain tiles (except for Lord British's Castle). I'm still not sure what the `; 5 * 4.5` in the `INT` would do:

``````40  FOR X = 1 TO 19: FOR Y = 1 TO 19:TE%(X,Y) =  INT ( RND (1) ; 5 * 4.5)
``````

However, if we roll a town/shop, there's a 50% chance it will be changed to empty land:

``````41  IF TE%(X,Y) = 3 AND  RND (1) > .5 THEN TE%(X,Y) = 0
42  NEXT : PRINT ".";: NEXT
``````

We pick one place to put Lord British's Castle. We then randomly pick a starting location and make that position a town/shop.

``````50 TE%( INT ( RND (1) * 19 + 1), INT ( RND (1) * 19 + 1)) = 5:TX =  INT ( RND (1) * 19 + 1):TY =  INT ( RND (1) * 19 + 1):TE%(TX,TY) = 3
``````

I guess that makes it possible (1 in 361 chance) that there'll be no Lord British in a game because the starting location (and hence the starting town) overwrote Lord British's Castle.

Lines 51–62 seem to be initializing some lookup tables for the dungeon drawing so we'll get to those in a bit. The rest of the initialization code is...

Clear the screen and set the graphics color to white:

``````68  HOME : HCOLOR= 3
``````

Then set the top edge and the width of the text area to be 20 and 29 and go to the top of that area:

``````69  POKE 34,20: POKE 33,29: HOME
``````

Finally we draw the surrounding terrain and go to the command loop:

``````70  GOSUB 100: GOTO 1000
``````

So, we just have the following left:

• an explanation of `XX%(10)`, `YY%(10)`, `PER%(10,3)`, `LD%(10,5)`, `CD%(10,3)`, `FT%(10,5)`, `LAD%(10,3)`
• the initialization in lines 51–62
• the routines in lines 200–491 and 3087–3089

all of which are almost certainly to do with drawing the dungeons.

Because it's hard to understand what is being initialized in lines 51–62 without seeing how it's later used, let's take a look into the routines starting at line 200.

We enter hires graphics mode, set `DIS` (distance?) to zero and set the plot colour to white:

``````200  HGR :DIS = 0: HCOLOR= 3
``````

Next we calculate `CENT`, `LEFT` and `RIGH` (`RIGHT`). Recall that `PX` and `PY` are the player's location and `DX` and `DY` give the direction they are looking.

``````202 CENT = DNG%(PX + DX * DIS,PY + DY * DIS):LEFT = DNG%(PX + DX * DIS + DY,PY + DY * DIS - DX):RIGH = DNG%(PX + DX * DIS - DY,PY + DY * DIS + DX)
``````

We'll come back to line 204 .

Now `CENT`, `LEFT` and `RIGHT` contain what is in front and to the left and right. The tens column is each case is used for the monster (if any) and the ones column for the tile. We put into `MC` the monster in front and change `CENT` to just be the tile. Similarly we change `LEFT` and `RIGHT` to just contain the tile (with no monster info):

``````205 CENT =  INT (CENT):LEFT =  INT (LEFT):RIGHT =  INT (RIGHT)
206 MC =  INT (CENT / 10):CENT = CENT - MC * 10:LEFT =  INT ((LEFT / 10 -  INT (LEF / 10)) * 10 + .1):RIGH =  INT ((RIGH / 10 -  INT (RIG / 10)) * 10 + .1)
``````

If `DIS` is zero then we skip to 216...

``````208  IF DIS = 0 THEN 216
``````

Otherwise...

We're still not sure what `3` or `4` correspond to (doors?) but if we have a WALL (`1`) or a `3` or `4` we draw a line from (`L1`,`T1`) to (`R1`,`T1`) to (`R1`,`B1`) to (`L1`,`B1`) to (`L1`,`T1`).

``````210  IF CENT = 1 OR CENT = 3 OR CENT = 4 THEN  HPLOT L1,T1 TO R1,T1 TO R1,B1 TO L1,B1 TO L1,T1
``````

And if it's a WALL or `3`, that's it, we go on to line 260.

``````212  IF CENT = 1 OR CENT = 3 THEN EN = 1: GOTO 260
``````

If it is a `4` we additionally draw 4 more lines.

``````214  IF CENT = 4 THEN  HPLOT CD%(DIS,0),CD%(DIS,3) TO CD%(DIS,0),CD%(DIS,2) TO CD%(DIS,1),CD%(DIS,2) TO CD%(DIS,1),CD%(DIS,3):EN = 1: GOTO 260
``````

So does this tell us anything about these variables?

L, T, R and B almost certainly stand for left, top, right and bottom. `L1`, `T1`, `R1` and `B1` must be coordinates of the rectangle for a wall straight in front of the player.

They were initialized as:

``````204 L1 = PER%(DIS,0):R1 = PER%(DIS,1):T1 = PER%(DIS,2):B1 = PER%(DIS,3):L2 = PER%(DIS + 1,0):R2 = PER%(DIS + 1,1):T2 = PER%(DIS + 1,2):B2 = PER%(DIS + 1,3)
``````

So we seem to have unlocked what `PER%(10,3)` is for. `PER%(d, n)` tells us the `n`-th coordinate used to draw a wall a distance `d` away. I suspect `PER` may be short for "perspective".

Let's take a look at the initialization:

Into `XX%(0)` and `YY%(0)` we put our vanishing point (roughly center of the screen):

``````51 XX%(0) = 139:YY%(0) = 79
``````

Next we step 2, 4, 6, ... 20 and calculate `XX%(X/2)` and `YY%(X/2)` (i.e. 1, 2, ... 10):

``````52  FOR X = 2 TO 20 STEP 2:XX%(X / 2) =  INT ( ATN (1 / X) /  ATN (1) * 140 + .5):YY%(X / 2) =  INT (XX%(X / 2) * 4 / 7)
``````

I'll explain the trigonometry in a little bit but note here that the `4 / 7` is just because that's the ratio of the viewport height to the viewport width and so once we've worked out the `XX` for a given distance, we can just get the `YY` by multiplying by this.

This enables us to calculate our perspective. 0 is left, 1 is right, 2 is top, 3 is bottom.

``````53 PER%(X / 2,0) = 139 - XX%(X / 2):PER%(X / 2,1) = 139 + XX%(X / 2):PER%(X / 2,2) = 79 - YY%(X / 2):PER%(X / 2,3) = 79 + YY%(X / 2): NEXT
``````

And finally we set our zero-distance coordinates to be the edge of the screen:

``````54 PER%(0,0) = 0:PER%(0,1) = 279:PER%(0,2) = 0:PE%(0,3) = 159
``````

And what about `CD%(10,3)` seen in line 214?

It is initialized on line 55:

``````55  FOR X = 1 TO 10:CD%(X,0) = 139 - XX%(X) / 3:CD%(X,1) = 139 + XX%(X) / 3:CD%(X,2) = 79 - YY%(X) * .7:CD%(X,3) = 79 + YY%(X): NEXT : PRINT ".";
``````

This is very similar to PER but `(X,0)` and `(X,1)` are scaled by 1/3, and `(X,2)` by 0.7.

The only thing `CD%()` is used for is line 214 (drawing tile `4`).

If there's a wall on the left, we draw the part where it hits the ceiling and the floor:

``````216  IF LEFT = 1 OR LEFT = 3 OR LEFT = 4 THEN  HPLOT L1,T1 TO L2,T2: HPLOT L1,B1 TO L2,B2
``````

And likewise the right:

``````218  IF RIGH = 1 OR RIGH = 3 OR RIGH = 4 THEN  HPLOT R1,T1 TO R2,T2: HPLOT R1,B1 TO R2,B2
``````

If there's a `4` on the left (we still don't know what `4` is) we draw most based on `LD%`:

``````220  IF LEFT = 4 AND DIS > 0 THEN  HPLOT LD%(DIS,0),LD%(DIS,4) TO LD%(DIS,0),LD%(DIS,2) TO LD%(DIS,1),LD%(DIS,3) TO LD%(DIS,1),LD%(DIS,5)
222  IF LEFT = 4 AND DIS = 0 THEN  HPLOT 0,LD%(DIS,2) - 3 TO LD%(DIS,1),LD%(DIS,3) TO LD%(DIS,1),LD%(DIS,5)
``````

And similarly on right, but mirror image:

``````224  IF RIGH = 4 AND DIS > 0 THEN  HPLOT 279 - LD%(DIS,0),LD%(DIS,4) TO 279 - LD%(DIS,0),LD%(DIS,2) TO 279 - LD%(DIS,1),LD%(DIS,3) TO 279 - LD%(DIS,1),LD%(DIS,5)
226  IF RIGH = 4 AND DIS = 0 THEN  HPLOT 279,LD%(DIS,2) - 3 TO 279 - LD%(DIS,1),LD%(DIS,3) TO 279 - LD%(DIS,1),LD%(DIS,5)
``````

We need to draw some extra lines if we're not a WALL (or `3` or `4`):

``````228  IF LEFT = 3 OR LEFT = 1 OR LEFT = 4 THEN 234
``````

If the distance we're drawing is not zero, we draw a vertical line on the left:

``````230  IF DIS <  > 0 THEN  HPLOT L1,T1 TO L1,B1
``````

and regardless of distance we draw another trapezoid:

``````232  HPLOT L1,T2 TO L2,T2 TO L2,B2 TO L1,B2
``````

and we do the same for the right:

``````234  IF RIGH = 3 OR RIGH = 1 OR RIGH = 4 THEN 240
236  IF DIS <  > 0 THEN  HPLOT R1,T1 TO R1,B1
238  HPLOT R1,T2 TO R2,T2 TO R2,B2 TO R1,B2
``````