In the previous post, we looked at a small fragment of code. In this post, we look at the Innkeeper program, which accounts for roughly half of the engine code. The code is included in its entirety and I will go through it in the order that it appears in the file.
We kick off with the copyright note and few two letter variable declarations. The most important variable here is KA - this a pointer to the binary game data. This is followed in memory by KB, which holds the character data. This is stored in a region of memory below BASIC and will not be erased when a new program is loaded.
73 REM TEMPLE OF APSHAI INNKEEPER - APPLE II APPLESOFT COPYRIGHT 1979 AUTOMATED SIMULATIONS, REV1(DISK)
74 DIM NA$(5),W(5),P(5),NC$(6),CH(6),SD(2),SP(2),SW(2),NW$(6),WW(5),WP(5),WD(5),WR(5),DS(1,1),DW(5,1),R$(6),RR(6),NS$(2)
75 KA = 4194:Q = 60:KB = KA + 2251
76 J8 = 248:J0 = 59:J7 = 249:JQ = 1
Being a RPG, we now have some tables. Starting with the armor that you can purchase and the six core stats.
77 DATA "NONE",0,0,"LEATHER",150,30,"RINGMAIL",350,100,"CHAIN MAIL",500,150,"PARTIAL PLATE",750,250,"FULL PLATE",1000,1000,"INTELLIGENCE","INTUITION","EGO","STRENGTH","CONSTITUTION","DEXTERITY"
78 FOR I = 0 TO 5: READ NA$(I),W(I),P(I):W(I) = INT (W(I) / 16 + .5): NEXT I
79 W(0) = 0: FOR I = 1 TO 6: READ NC$(I): NEXT I
The armor table is as follows, with the weight rather oddly being expressed in ounces and converted to pounds on read. Weight is important in ToA, since the game has an encumberance system. The protection afforded by armor is implicit on its order in this table - i.e. None has an AC of 0, Full plate an AC of 5. The types of armor would be familiar to players of D&D - which is helpful, as the game provides no information about the mechanical effect of different armor types.
Weight (Oz) Price (Sp) AC
None 0 0 0
Leather 150 30 1
Ring 350 100 2
Chain 500 150 3
Partial 750 250 4
Full 1000 1000 5
The six characteristics are:
Intelligence
Intuition
Ego
Strength
Constitution
Dexterity
The manual suggests that if converting from "another system" ( by which they mean D&D - TSR, D&D's publishers - were extremely protective of their trademark in this period, so the game is never mentioned by name ), one should interpret "Intuition" as ( D&D's) "Wisdom" and "Ego" as "Charisma". However, the upper three stats play a fairly minor role.
These tables are followed by Weapons
80 DATA "DAGGER",1,5,5,3,7,12,"SHORTSWORD",2,14,6,8,9,12,"BROADSWORD",3,18,7,10,9,13,"HAND-AND-A-HALF SWORD",6,35,8,16,9,14,"GREAT SWORD",9,70,10,15,7,15,"MAGIC SWORD"
85 FOR I = 1 TO 5: READ NW$(I),WW(I),WP(I),WD(I),WR(I): FOR J = 0 TO 1: READ DW(I,J): NEXT J: NEXT I: READ NW$(6)
Weight (lbs) Price Damage Str Req Dex Low Dex High
Dagger 1 5 5 3 7 12
Short 2 14 6 8 9 12
Broad 3 18 7 10 9 13
HnHalf 6 35 8 16 9 14
Great 9 70 10 15 7 15
Magic
Again, weapons have a weight ( now in lbs ), a price, a damage score, a minimum strength requirement and a dexterity range. Below "Dex Low", an attack penalty is incurred, above "Dex High" a bonus is received. Magic swords are special and don't have any explicit stats. The "Great Sword", while doing the most damage, is also the only two handed weapon, and so incompatible with wielding a shield:
90 DATA 6,3,10,8,15,12,5,15,6,17
95 FOR I = 1 TO 2: READ SW(I),SD(I),SP(I): FOR J = 0 TO 1: READ DS(I - 1,J): NEXT J: NEXT I
99 LS = 0:SM = 0
Weight (lbs) AC Price Dex Low Dex High
Small 6 3 10 8 15
Large 12 5 15 6 17
Like weapons, these have a dexterity range. The Armor class of the shield indicates how much protection it affords.
These items are purchased from the Innkeeper. This involves haggling and the following table presents the Innkeeper's responses. Each has a weight to manage the probability that it is emitted, which yields a bit of variety and improves immersion.
100 DATA "I'D NOT PART WITH THESE FINE GOODS FOR THAT PITTANCE! MAYHAP FOR",5,"NOT SO CHEAP, MY FRIEND! BUT FOR THEE, JUST",10,"BLACKHEART! THOU TAKEST THE FOOD FROM MY CHILDREN'S MOUTHS! I'LL NOT SETTLE FOR LESS THAN",5
110 DATA "WELL, LIFE IS SHORT AND THY ARSE LONG! WHAT SAY THEE TO",2,"HMM..BUT SUCH FINE WORKMANSHIP. I COULD NOT PART WITH THIS FOR LESS THAN",10,"A POX ON THEE! BUT I'D TAKE",3
130 RN = 6:RT = 0: FOR I = 1 TO RN: READ R$(I),RR(I):RT = RT + RR(I): NEXT I
The shield names themselves are read in next, before jumping to the main body of the program.
140 DATA "NONE","SMALL","LARGE": FOR I = 0 TO 2: READ NS$(I): NEXT I 800 GOTO 1000
Line 1000 checks the contents of an address 12 below the character record. This stores the
magic number 123 if the innkeeper (INN) had been loaded by the Dunjonmaster.
If this is not the case, then a title screen is displayed. HOME, VTAB and HTAB are cursor positioning commands.
900 HOME : VTAB (8): HTAB (14): PRINT "DUNJONQUEST": VTAB (10): HTAB (10): PRINT "THE TEMPLE OF APSHAI": VTAB (16): HTAB (13): PRINT "COPYRIGHT 1979": VTAB (18): HTAB (9): PRINT "AUTOMATED SIMULATIONS"
910 POKE KB,0
920 VTAB (23): HTAB (9): PRINT "HIT ANY KEY TO CONTINUE"
930 I = RND (5) * 5: IF PEEK ( - 16384) < 127 THEN 930
932 POKE - 16368,0: RETURN
990 RETURN
1000 IF PEEK (KB - 12) < > 123 THEN GOSUB 900
On pressing a key, you find yourself conversing with the Innkeeper. Based on the value of the magic number, if we are coming from the dungeon, we are asked if we want to see our treasure inventory. The reason we need to see it, is so that we can manually, on a piece of paper, add up how much money we have using the master the treasure key in the manual. ToA very much operates on an honour system. After all, you'd only be cheating yourself. We also initialise the weight carried to 5 lbs ( clothing ,etc. we assume)
1001 HOME :WC = 5: PRINT "THUS QUOTH THE INNKEEPER:"
1002 FOR I = 1 TO 1500: NEXT I
1003 IF PEEK (KB - 12) = 123 THEN INPUT "WOULDST THOU KNOW WHAT TREASURES THOU HAST? ";A$: IF LEFT$ (A$,1) = "Y" THEN GOSUB 9920
1004 GOSUB 9940: IF PEEK (KB - 12) = 123 THEN GOSUB 4500
1005 IF PEEK (KB - 12) = 123 THEN PRINT "I TRUST THINE WAS A PLEASANT SOJURN WITHIN THE TEMPLE.": PRINT "ART BUT PASSING THROUGH THE INN AND WISHTO RETURN";: INPUT A$: IF LEFT$ (A$,1) = "Y" THEN GOSUB 9900: INPUT "HOW MANY SILVER PIECES HAST THOU? ";MO:OM = MO
1006 IF PEEK (KB - 12) = 123 THEN IF LEFT$ (A$,1) = "Y" THEN GOSUB 2600: GOTO 1205
If we didn't come from the dungeon, we fall through to here. This is the game's rather roundabout way of asking if we want to generate a character or restore an existing one.
1008 POKE KB - 12,0
1009 HOME : POKE KA,0
1010 PRINT "HAIL AND WELL MET! SHALL I FIND THEE A GOODLY CHARACTER, OR HAST THOU BROUGHT ONE WITH THEE? PRITHEE, SAY YEA IF I SHOULD";: INPUT ". ";C$: IF LEFT$ (C$,1) = "Y" THEN GOSUB 2200
1011 IF LEFT$ (C$,1) = "Y" GOTO 1200
If we don't want to generate a character, we can load one from disk
1020 INPUT "HAST THOU A CHARACTER ON DISK?";C$: IF LEFT$ (C$,1) < > "Y" THEN GOSUB 2400: GOTO 1200
1025 D$ = CHR$ (4)
1030 INPUT "WHAT BE THE FELLOW'S NAME?";C$: PRINT "READING IN CHARACTERISTICS FOR "C$
1038 PRINT D$;"OPEN "C$
1040 PRINT D$;"READ "C$: FOR I = 61 TO 96: INPUT J: POKE KA - I,J: NEXT I: INPUT J: PRINT "LAST ON LEVEL "J
1050 FOR I = 0 TO 37: INPUT J: POKE KB - 12 + I,J: NEXT I: PRINT D$;"CLOSE"
1060 POKE KB - 12,123: GOTO 1003
depending on exactly which options we chose, we end up at either 1200 or 1205. We end up at 1200 if we rolled a new character or entered one manually. Otherwise we find ourselves at 1205. The subroutines off of this are related to purchasing various pieces of equipment:
2600 weapons
2800 shields
2000 armor
3000 bow and arrows
3200 healing salves
Calls to these are gated by the character having enough money ( MO ).
It is also here that the players base "ToHit" (PB) score is computed. This is
11 - WeaponPoints(WP) - Level/2
Weapon points are computed from dexterity and "Level" is the character's advancement level. In combat, a D20 is rolled against this value ( adjusted by some modifiers ) and if the player rolls higher, then a hit is scored. This is clearly inspired by this 1st edition D&D attack matrix, although armor works slightly differently in the dunjonquest games.
PA is similar but from the monster's point of view.
1200 OM = MO: INPUT "CHARACTER NAME? ";NM$: IF MO > 0 THEN GOSUB 2600
1205 IF MO > 0 THEN GOSUB 2800
1206 IF PS = 0 THEN WP = WP + SP:SP = 0
1207 PB = PB - WP - INT ( PEEK (KB + 15) / 2)
1208 IF PB < 0 THEN PB = 0
1210 IF WP > 0 THEN PA = PA + WP - 1
1211 IF WP < 0 THEN PA = PA + WP + 1
1212 IF MO < > 0 THEN GOSUB 2000: GOSUB 3000: GOSUB 3200
Next we enter the monster speed. ToA is a semi real time game: the player and the monsters take it in turns, but the player only has a limited amount of time to enter their move. The amount of time granted is controlled by the "monster speed" variable.
1215 SE = 100
1216 INPUT "MONSTER SPEED (SLOW, MEDIUM, OR FAST) ? ";C$: IF LEFT$ (C$,1) = "S" THEN SE = 250
1217 IF LEFT$ (C$,1) = "F" THEN SE = 60
1218 IF LEFT$ (C$,1) = "M" THEN SE = 120
Finally, everything is safely packed away outside of BASIC and the DM program is loaded.
1219 POKE KB + 9,SE: POKE KB + 11,HS: POKE KB + 13,RS: POKE KB + 14,RM: POKE KB + 12,WC: POKE KB + 7,WM
1220 POKE KB + 24,CA: POKE KB + 6,HN: POKE KB + 11,HS
1221 POKE KB + 23,AS: POKE KB + 7,WM: POKE KB + 18,PB: POKE KB + 19,PA: POKE KB + 21,IT: POKE KB + 22,EG: POKE KB + 4,SP: POKE KB + 25,DX:J = INT (EX / 65536): POKE KB + 3,J:JJ = INT (EX / 256 - J * 256): POKE KB + 2,JJ: POKE KB + 1,EX - JJ * 256 - J * 65536
1223 WC = 5 + WW( PEEK (KB + 17)) + W(AA) + SW( INT ( PEEK (KB) / 2)) + 1 + (RS + RM) / 10
1225 POKE KB + 13,RS: POKE KB + 14,RM: POKE KB + 12,WC: POKE KB + 20,CH(1)
1230 POKE KB + 10,SM
1260 POKE KB,PS
1270 POKE KB + 8,AA: IF PEEK (KB - 12) = 123 GOTO 1400
1315 J = LEN (NM$): IF J > 11 THEN J = 11
1320 FOR I = 1 TO J: POKE KB - 12 + I, ASC ( MID$ (NM$,I,1)): NEXT I
1330 IF J < 11 THEN FOR I = J + 1 TO 11: POKE KB - 12 + I, ASC (" "): NEXT I
1400 GOSUB 4500
1500 POKE KB - 12,123
1900 PRINT : PRINT "WHEN THOU ART READY TO CONTINUE, PRESS 'RETURN'.": GET A$: PRINT : PRINT "I SHALL NOW LOAD THE DUNJONMASTER.": PRINT CHR$ (04);"RUN DM"
The remainder of the code in INN is related to shopping and character generation. First Armor. This prints the table above, although it only shows the weight
and price - the manual gives a little additional information, but for the most part the player has to figure out the mechanics for themselves. The routine at 4000 is used in the purchase subroutines to implement haggling.
2000 INPUT "WILT THOU BUY NEW ARMOR? ";A$: IF LEFT$ (A$,1) < > "Y" THEN RETURN
2001 FOR I = 1 TO 900: NEXT : HOME : PRINT TAB( 5);"TYPE"; TAB( 20);"WEIGHT"; TAB( 30);"PRICE"
2005 PRINT
2010 FOR I = 1 TO 5: PRINT NA$(I); TAB( 20);W(I); TAB( 30);P(I): NEXT I
2020 INPUT "WHAT SORT OF ARMOR WOULDST THOU WEAR? ";A$: IF LEFT$ (A$,1) = "N" THEN AM = 0:AA = 0: RETURN
2021 FOR I = 1 TO 5: IF LEFT$ (A$,2) = LEFT$ (NA$(I),2) THEN N = I: GOTO 2026
2022 NEXT I: PRINT "I HAVE NOT ";A$;" FOR SALE": GOTO 2020
2024 IF PEEK (KB - 12) = 123 THEN WC = WC - W(AA)
2026 LO = .3 * P(N):AK = P(N):A1 = AK: GOSUB 4000: IF OO = 0 GOTO 2020
Once armor is purchased (or otherwise acquired), update money and weight. Also clears (KB+16) which holds a bonus for magic armor.
2030 AA = N:MO = MO - OO:WC = WC + W(N): POKE KB + 16,0
2040 PRINT "THOU HAST ";MO;" SILVER PIECES LEFT": PRINT "IN THY PURSE.": RETURN
The next routine will be familiar to anyone who has ever played DnD: a subroutine to generate 3d6 to supply the core stats:
2190 J = INT ( RND (6) * 6 + 1) + INT ( RND (6) * 6 + 1) + INT ( RND (6) * 6 + 1): RETURN
Followed by the actual character generator:
2200 HB = 0:SC = 0: FOR I = 1 TO 6: GOSUB 2190:CH(I) = J:SC = SC + J: NEXT I: IF SC < 60 OR CH(4) < 8 OR CH(5) < 7 GOTO 2200
There's no option to re-roll, but the generator attempts
to ensure that your character is viable by re-rolling until the total score is at least 60 and Strength and Constitution have reasonable values.
Once we have some reasonable core stats, the remaining variables are filled in, the character's level ( KB+15) is set to 1 and some money is awarded ( 3d6 * 10 silver pieces ).
2205 EX = 0:RS = 0:RM = 0:AA = 0:PS = 0: POKE KB + 17,0:SP = 0:WP = 0:AM = 0:SM = 0:HS = 0:HN = 0: POKE KB + 15,1
2210 HOME : PRINT TAB( 5);"THY QUALITIES:": PRINT : FOR I = 1 TO 6: PRINT NC$(I); TAB( 25);CH(I): NEXT
2215 FOR I = 1 TO 6: POKE KA - 97 + I,CH(I): NEXT
2220 PRINT " ": GOSUB 2190:MO = J * 10: PRINT "THOU HAST ";MO;" PIECES OF SILVER"
2225 GOSUB 2250:L = 1: POKE KB + 16,0
2230 EG = CH(3):IN = CH(1):AS = CH(4):CA = CH(5):IT = CH(2):DX = CH(6):PA = 11:PB = 11
2240 RETURN
2250 FOR I = KA - 90 TO KA - 81: POKE I,0
2255 NEXT I: RETURN
The following long, but straight forward, slab of code, allows one to enter a character manually. The writers of ToA expected many of their players to be DnD players who would want to take their pencil and paper characters into the game. The manual had quidelines for converting, but there is nothing to prevent you from creating super human characters. Again, the feeling was that you would be only cheating yourself of fun.
2400 POKE KB + 15,0: FOR I = 1 TO 6: PRINT "ENTER ";NC$(I);: INPUT ": ";CH(I): IF CH(I) > = 0 AND CH(I) < = 18 THEN POKE KA - 97 + I,CH(I): GOTO 2403
2401 IF CH(I) > 18 THEN PRINT CH(I);" BE TOO HIGH. NO MORE THAN 18 CAN": PRINT "IT BE.":I = I - 1: GOTO 2403
2402 IF CH(I) < 0 THEN PRINT CH(I);"? ART THOU SUPERNATURAL, OR MERELY": PRINT "A FOOL? THOU CAN HAVE NO NEGATIVE TRAIT!":I = I - 1
2403 NEXT I
2405 INPUT "THY CHARACTER'S EXPERIENCE IS? ";EX: IF EX > 16000000 THEN PRINT "THY CHARACTER IS TOO WORLDWISE; FIND ANOTHER, MY FRIEND.": GOTO 2400
2410 E = EX / 1000: FOR L = 1 TO 20: IF 2 ^ L < E THEN NEXT L
2420 INPUT "HOW MUCH MONEY HAST THOU TO SPEND? ";MO
2425 GOSUB 2500
2427 PRINT "WHAT KIND OF SWORD HAST THOU?": INPUT A$: FOR I = 1 TO 5: IF LEFT$ (A$,1) = LEFT$ (NW$(I),1) THEN IF CH(4) > = WR(I) THEN N = I:OO = 0: GOSUB 2690: GOTO 2429
2428 NEXT I: PRINT "THOU CANNOT TAKE A ";A$;: PRINT " TO THE": PRINT "DUNJON. THOU MUST BUY ANOTHER."
2429 PRINT "WHAT SORT OF ARMOR DOST THOU WEAR?": INPUT A$: FOR I = 0 TO 5: IF LEFT$ (A$,2) = LEFT$ (NA$(I),2) THEN N = I:OO = 0: GOSUB 2030: GOTO 2435
2432 NEXT I: PRINT "THOU CANNOT WEAR ";A$: PRINT "IN THE DUNJON": GOTO 2429
2435 N = 0: INPUT "HAST THOU A SHIELD? ";A$: IF LEFT$ (A$,1) = "Y" THEN INPUT "BE IT LARGE OR SMALL? ";A$:N = 1: IF LEFT$ (A$,1) = "L" THEN N = 2
2437 IF N > 0 THEN GOSUB 2865
2439 HB = 0: INPUT "HAST THOU A BOW? ";A$: IF LEFT$ (A$,1) = "Y" THEN HB = 1
2440 INPUT "HOW MANY ARROWS HAST THOU? ";RS: IF RS > 60 THEN PRINT "THOU CAN HAVE BUT SIXTY": GOTO 2440
2445 INPUT "HOW MANY MAGIC ARROWS? ";RM: IF RM > 60 THEN PRINT "THOU CAN HAVE BUT SIXTY": GOTO 2445
2450 INPUT "HOW MANY HEALING POTIONS HAST THOU? ";HN: INPUT "HOW MANY HEALING SALVES HAST THOU? ";HS: IF HS > 10 THEN HS = 10
2480 INPUT "IS THY SWORD MAGICAL? ";A$: IF LEFT$ (A$,1) = "Y" THEN INPUT "WHAT BE THE PLUS? ";SM
2485 POKE KB + 16,0: INPUT "IS THY ARMOR MAGICAL? ";A$: IF LEFT$ (A$,1) = "Y" THEN INPUT "WHAT BE THE PLUS? ";AM: POKE KB + 16,AM
2490 GOSUB 2250: GOTO 2230
Next we have the level up code. I am not convinced this code will work correctly, but the intent is to increase the player's core stats as they gain experience.
2500 WP = INT ((L + 1) / 2):SP = L - WP: IF W2 = 1 THEN WP = L:SP = 0
2503 IF PEEK (KB + 15) = L THEN RETURN
2504 POKE KB + 15,L
2505 IF PEEK (KB - 12) = 123 THEN J = L - 1:L = 2: IF J < 0 THEN J = 0: ON J GOTO 2510,2520,2530,2540,2550,2510,2520,2530,2540,2550
2510 L = L - 1: IF L = 0 THEN 2565
2515 IF CH(5) > = 9 THEN CH(4) = CH(4) + 1: GOTO 2520
2516 CH(5) = CH(5) + 1
2520 L = L - 1: IF L = 0 THEN 2565
2525 IF CH(6) > = 9 THEN CH(5) = CH(5) + 1: GOTO 2530
2526 CH(6) = CH(6) + 1
2530 L = L - 1: IF L = 0 THEN 2565
2535 CH(5) = CH(5) + 1
2540 L = L - 1: IF L = 0 THEN 2565
2545 IF CH(6) < 9 THEN CH(6) = CH(6) + 1: GOTO 2550
2546 IF CH(4) > = CH(5) THEN CH(5) = CH(5) + 1: GOTO 2550
2547 CH(4) = CH(4) + 1
2550 L = L - 1: IF L = 0 THEN 2565
2555 IF CH(2) > = CH(3) THEN CH(3) = CH(3) + 1: GOTO 2560
2556 CH(2) = CH(2) + 1
2560 GOTO 2510
2565 FOR I = 1 TO 6:M = CH(I) - 18: IF M < = 0 THEN 2580
2570 CH(I) = 18: FOR J = 1 TO M: IF CH(4) < 18 THEN CH(4) = CH(4) + 1: GOTO 2575
2571 IF CH(5) < 18 THEN CH(5) = CH(5) + 1: GOTO 2575
2572 IF CH(6) < 18 THEN CH(6) = CH(6) + 1: GOTO 2575
2574 R = RND (1) * 3:CH(R) = CH(R) + 1
2575 NEXT J
2580 NEXT I:CA = CH(5):AS = CH(4):DX = CH(6):EG = CH(3):IT = CH(2): RETURN
Similar to armor purchasing is weapon purchasing. The only real difference is the strength requirement.
2600 IF MO = 0 THEN RETURN
2605 PRINT "WILT THOU BUY ONE OF OUR FINE SWORDS?": INPUT "";A$: IF LEFT$ (A$,1) = "N" THEN N = PEEK (KB + 17): GOSUB 2694: RETURN
2606 W2 = 0
2645 HOME
2650 PRINT "WEAPON"; TAB( 20);"WEIGHT"; TAB( 30);"PRICE"
2660 PRINT : FOR I = 1 TO 5: PRINT NW$(I); TAB( 23);WW(I); TAB( 30);WP(I): NEXT I
2670 PRINT "WHAT WEAPON WILT THOU PURCHASE?": INPUT "";A$: FOR I = 1 TO 5: IF LEFT$ (A$,1) = LEFT$ (NW$(I),1) THEN N = I: GOTO 2675
2671 IF LEFT$ (A$,1) = "N" THEN N = PEEK (KB + 17): GOSUB 2694: RETURN
2672 NEXT I: PRINT "I HAVE NO SUCH WEAPON AS THAT.": GOTO 2670
2675 IF WR(N) > AS THEN PRINT "THOU CANNOT WIELD SUCH A GREAT WEAPON.": GOTO 2670
2677 PRINT "FEAST THINE EYES 'PON THIS FINE": PRINT NW$(N);: IF RND (4) * 4 > 2 THEN PRINT ". 'TIS SURE TO": PRINT "DRINK THY FOE'S BLOOD.": GOTO 2679
2678 PRINT " 'TIS WELL FORGED IRON."
2679 LO = .3 * WP(N):AK = WP(N):A1 = AK: GOSUB 4000: IF OO = 0 GOTO 2670
2680 MO = MO - OO: PRINT "THOU HAST ";MO;" SILVER PIECES LEFT."
Compute weapon stats. The maximum damage that a weapon can do is:
WeaponDamage * Strength / 10
Where "WeaponDamage" comes from the weapon table. The weapon points check dexterity against the Lo/Hi ranges, although it looks like the subtraction of the low range is missing ( this is fixed in the sequel ) so this is bugged. The actual bonus penalty is roughly Log2 of this difference - so if this was working properly, we would have roughly a -3 - +3 range.
The flag W2 is set to indicate that a weapon is two handed.
2690 WM = INT (WD(N) * AS / 10 + .5):SM = 0: POKE KB + 10,0: POKE KB + 17,N:WC = WC + WW(N): IF N = 5 THEN W2 = 1
2694 IF DW(N,0) > CH(6) THEN WP = WP + CH(6): GOTO 2696
2695 IF DW(N,1) < CH(6) THEN WP = WP + CH(6) - DW(N,1)
2696 IF WP > 0 THEN WP = INT (1.3 * LOG (WP) + 1)
2700 RETURN
Shield purchasing is the similar to weapons and armor, but skipped if the character is wielding a two-handed great sword.
2800 IF W2 = 1 THEN PS = 0: POKE KB,0:SP = 0: RETURN
2840 INPUT "WILT THOU BUY A SHIELD? ";A$: IF LEFT$ (A$,1) = "N" THEN GOSUB 2885: RETURN
2841 HOME : PRINT "SHIELD WEIGHT ASK": PRINT : PRINT "SMALL"; TAB( 11);SW(1); TAB( 22);SP(1): PRINT "LARGE"; TAB( 11);SW(2); TAB( 22);SP(2): PRINT : INPUT "WHAT SORT? ";C$: IF LEFT$ (C$,1) = "L" THEN N = 2: GOTO 2843
2842 N = 1
2843 IF LEFT$ (C$,1) < > "L" AND LEFT$ (C$,1) < > "S" THEN N = 0: POKE KB,0:PS = 0: RETURN
2850 LO = .3 * SP(N):AK = SP(N):A1 = AK: GOSUB 4000: IF OO = 0 THEN 2840
Shield points computed like weapon points ( although this time correctly!)
2865 IF DS(N - 1,0) > DX THEN SP = SP + DX - DS(N - 1,0): GOTO 2870
2866 IF DS(N - 1,1) < DX THEN SP = SP + DX - DS(N - 1,1)
2870 MO = MO - OO: PRINT "THOU HAST ";MO;" SILVER PIECES LEFT.":WC = WC + SW(N):PS = SD(N): POKE KB,PS: GOSUB 2885: RETURN
2885 IF PS = 5 THEN SP = INT (SP / 2)
2890 IF SP > 0 THEN SP = INT (1.3 * LOG (SP) + 1)
2895 SP = 2 * PS + SP: RETURN
Purchasing a bow and arrows. One oddity is that bow ownership is not recorded in the character record, and a character is always assumed to have purchased a bow when the character is restored after visiting the dungeon. This is fixed in the sequel.
3000 IF HB > 0 GOTO 3030
3010 INPUT "WILT THOU BUY A BOW? ";C$: IF LEFT$ (C$,1) = "N" THEN RETURN
3020 PRINT "I'VE A FINE BOW, YEW AND NEARLY NEW, FOR12 SILVER PIECES.":LO = 4:AK = 12:A1 = AK: GOSUB 4000: IF OO = 0 THEN RETURN
3025 MO = MO - OO: PRINT "THOU HAST ";MO;" REMAINING."
3030 IF RS + RM > 60 THEN RS = 60 - RM: GOTO 3050
3040 N = 0: INPUT "HOW MANY ARROWS WILT THOU BUY (AT TWO PER SILVER PIECE)? ";N: IF INT ((N + 1) / 2) > MO THEN PRINT "NO CREDIT!": GOTO 3040
3041 IF RS + RM + N > 60 THEN PRINT "THOU CANST CARRY BUT SIXTY ARROWS. BUY FEWER.": GOTO 3040
3045 RS = RS + N:MO = MO - INT ((N + 1) / 2)
3050 RETURN
Healing Salves - i.e. healing potions.
3200 REM
3210 INPUT "HOW MANY SALVES WILT THOU BUY (THEY COSTTHEE 10 SILVER PIECES EACH)? ";N: IF 10 * N > MO THEN PRINT "NO CREDIT!": GOTO 3210
3211 MO = MO - N * 10:HS = HS + N: IF HS > 10 THEN N = HS - 10:MO = MO + 10 * N:HS = 10: PRINT "MORE THAN TEN WILL DO THEE NO GOOD!"
3220 IF MO < .35 * OM THEN PRINT NM$;"! THOU SPENDTHRIFT!": PRINT "THOU HAST BUT ";MO;" SILVER PIECES LEFT": PRINT "TO THEE!": RETURN
3225 PRINT NM$;", THOU ART FRUGAL. THOU": PRINT "HAST ";MO;" SILVER PIECES LEFT."
3230 RETURN
The haggle subroutine is used by all the purchase routines above. It's one of the few places in the game where the intelligence stat is used. This, along with ego, determines by how much the Innkeeper adjusts his asking price in response to the player's offer. The important lines are 4058 and 4060. Here we consider the difference between the player's offer and the asking price. A fraction of this is added to the player's previous offer weighted by the player's stats and a non-uniform random number. This number is generated by feeding a uniformly generated number through square root (SQR) to get a distribution biased towards 1. If the sum of ego and intelligence are 20 or greater, the Innkeeper will move his asking price towards the player's. If they are less than 20, there is some chance that he will raise his asking price.
4000 IF RND (3) * 3 > 2 THEN PRINT "WHAT MAY BE THY OFFER, ";NM$;"?": GOTO 4010
4005 PRINT "WHAT OFFEREST THOU?"
4010 INPUT OO:OO = INT (OO): IF OO > MO THEN PRINT "LIAR, THOU HAST BUT ";MO;".": GOTO 4000
4020 IF OO = 0 THEN LS = 0: RETURN
4030 IF OO < = LO THEN GOSUB 4800: GOTO 4000
4035 IF OO < = LS THEN GOSUB 4700: GOTO 4000
4040 IF OO > = AK THEN PRINT "DONE!":LS = 0: RETURN
4050 IF OO > AK - .3 * RND (1) ^ 2 * AK THEN IF OO < .6 * A1 THEN PRINT "THOU ART A HARD BARGAINER,": PRINT NM$:LS = 0: RETURN
4051 IF OO > AK - .3 * RND (1) ^ 2 * AK THEN PRINT "DONE!":LS = 0: RETURN
4055 IF RND (200) * 200 + 1 = 200 THEN PRINT "I SEE THE GODS LOOK FAVORABLY UPON THEE,SO TAKE IT FOR THAT.":LS = 0: RETURN
4058 B = 20 / (CH(1) + CH(3)): IF CH(3) > 12 AND CH(1) < 10 THEN B = 20 / (CH(1) + 20 - CH(3))
4060 AK = OO + (AK - OO) * SQR (1 - RND (1)) * B
4070 LS = OO: GOSUB 4900: GOTO 4000
The character summary screen
4500 CH(1) = PEEK (KB + 20):CH(2) = PEEK (KB + 21):CH(3) = PEEK (KB + 22):CH(4) = PEEK (KB + 23):CH(5) = PEEK (KB + 24):CH(6) = PEEK (KB + 25):SM = PEEK (KB + 10):RM = PEEK (KB + 14):AA = PEEK (KB + 8):WM = PEEK (KB + 7) - SM:WN = PEEK (KB + 17)
4501 RS = PEEK (KB + 13)
4505 IF SM < > 0 THEN WN = 6
4510 GOSUB 9800: HOME : PRINT "CHARACTER SUMMARY FOR ";NM$
4520 PRINT : FOR I = 1 TO 6: PRINT NC$(I); TAB( 20); PEEK (KA - 97 + I): NEXT I
4525 J = 0: IF PEEK (KB) = 3 THEN J = 1
4526 IF PEEK (KB) = 5 THEN J = 2
4530 PRINT "WEAPON: ";NW$(WN);: IF WN = 6 THEN PRINT " PLUS "; PEEK (KB + 10);
4531 PRINT
4532 PRINT "ARMOR: ";NA$(AA);: IF PEEK (KB + 16) > 0 THEN PRINT " PLUS "; PEEK (KB + 16);
4533 PRINT
4535 PRINT "ARROWS: ";RS; TAB( 20);"MAGIC ARROWS: ";RM
4536 PRINT "SHIELD: ";NS$(J)
4537 PRINT "SALVES: "; PEEK (KB + 11); TAB( 20);"ELIXIRS: "; PEEK (KB + 6)
4538 PRINT "SILVER: ";MO
4540 PRINT "EXPERIENCE: "; PEEK (KB + 1) + 256 * PEEK (KB + 2) + 65536 * PEEK (KB + 3)
4541 PRINT "WEIGHT CARRIED: "; PEEK (KB + 12)
4550 RETURN
A subroutine to emit a random response when the player makes an offer lower or equal to the last.
4700 R = RND (3) * 3: ON RND (3) * 3 + 1 GOTO 4710,4720,4730
4710 PRINT "DOST THOU TAKE ME FOR A DOLT?": RETURN
4720 PRINT "ART THOU A FOOL OR A KNAVE, ";NM$;"?": PRINT "MAKE AN OFFER HIGHER THAN THY LAST!": RETURN
4730 PRINT "PERCHANCE THY WOULDST NOT HAVE THIS AT ALL?": RETURN
A subroutine to emit a random response if the player offers less than the minimum price.
4800 IF RND (100) * 100 > 50 THEN PRINT "HA! TIS LESS THAN I PAID FOR IT!": RETURN
4801 PRINT "I SPIT UPON THY PALTRY OFFER!": RETURN
A subroutine to emit a response to a reasonable offer.
4900 FOR I1 = 1 TO 10:R = RND (RTT) * RT: NEXT I1:RQ = 0:R = RND (RT) * RT: FOR I1 = 1 TO RN:RQ = RQ + RR(I1): IF RQ < R THEN NEXT I1
4901 PRINT R$(I1);" "; INT (AK + .999): RETURN
A subroutine to restore the characters name from the binary data
9800 NM$ = "": FOR I = 1 TO 11:NM$ = NM$ + CHR$ ( PEEK (KB - 12 + I)): NEXT I
9810 FOR I = 1 TO 10: IF MID$ (NM$,12 - I,1) < > " " GOTO 9830
9820 NEXT I
9830 NM$ = LEFT$ (NM$,12 - I): RETURN
Restores character stats after returning from the dungeon. Experience is a 24-bit value, with
level being Log2 of experience. The new level ( NL ) is not used anywhere, which makes me a bit sceptical that the level up code actually works. Also note that we set HB to1 ( have bow ) regardless of whether the player actually purchased on before entering the dungeon.
9900 SM = PEEK (KB + 10):HS = PEEK (KB + 11):HN = PEEK (KB + 6):RS = PEEK (KB + 13):RM = PEEK (KB + 14): FOR I = 1 TO 6:CH(I) = PEEK (KB + 19 + I): NEXT :AA = PEEK (KB + 8):WM = PEEK (KB + 7):PS = PEEK (KB):WC = PEEK (KB + 12)
9901 IF PEEK (KB + 17) = 5 THEN W2 = 1
9902 IF AA < > 5 THEN W2 = 0
9907 EX = PEEK (KB + 1) + 256 * PEEK (KB + 2) + 65536 * PEEK (KB + 3): IF EX > 1999 THEN NL = LOG (EX / 1000) / LOG (2) + 1
9908 IF EX < = 1999 THEN L = 1
9909 HB = 1: GOSUB 2500: GOSUB 2230
9910 RETURN
That's the INN program. We now move onto the more involved DM program.
Commenti