### Caverns of Freitag for the Apple II
### Copyright 1982 by David Shapiro
### Published by Muse Software
###
### Disassembly of "CF"

### SETUP ###

 1  ONERR  GOTO 400
# disconnect DOS; call $6D1E and $6D1B (CF.OBJ)
 2  PR# 0: IN# 0: CALL 27934: CALL 27931
# set HIMEM to $6D00
 4  HIMEM: 27904
# Set WNDBTM to 23 (guards bottom line).  C2/C9 never used again
 6 C2 = 256: POKE 35,23:C0 = 36:C9 = 9
 8 BX = 0:ZA = 0
 9  & CLR,0: & WINDOFF
# query game parameters (skill in SL%, speed-1 in 793 ($319))
 10  GOSUB 50000
# generate a random number between 1 and N
 15  DEF  FN R(X) =  INT ( RND (1) * X + 1)
 20 ML = 40960:MX = 79:MY = 79:CX = 0:CY = 35:AX = 1:AY = 4
# set player start position (44081=$AC31 -> X=1 Y=39), set under-tile to Inn
 21 K9 = MY + 1: POKE 44081,2:T0 = C0 + 2
# poke in the dragon parts (30-35)
# adress 40960 + 80*71 + 75 = 46715 ($B67B)
 25  POKE ML + K9 * 71 + 75,30: POKE ML + K9 * 71 + 76,31: POKE ML + K9 * 71 + 77,34
 26  POKE ML + K9 * 72 + 75,32: POKE ML + K9 * 72 + 76,33: POKE ML + K9 * 72 + 77,35
# TB is 32704 ($7FC0); 34048 is $8500
 30 TB = 34048 - 84 * 16:EP = 0:EL = 1:HP = 125:BH = 125:AR = 12:EN = 25 * SL%
 40 DP = 1:XL = 1:YL = 1:XR = 8:YR = 7:HW = 1:SM = 0:RR = 0
 50  DIM HH(35): DIM AA%(35): DIM M$(35)
# set BX/BY to pointers to monster data
# initially 110 monsters created on map
 53 NM = 110:BX = 16384:BY = BX + 170:BT = BY + 170: DIM MH%(175)
# set $31A = skill level + 4 (5-13)
 54  POKE 794,SL% + 4
# $300=$03, $301=110 call $6D12 (create serpents?); set $4E to random
 55  POKE 768,3: POKE 769,NM: CALL 27922: POKE 78, RND (1) * 256
# call $6D12 (create 62 chests?)
 56  POKE 768,C0 + 1: POKE 769,62: CALL 27922
 59  GOTO 63


### find random empty map location, return in ZX/ZY/ZL ###
# ZX,ZY = random number between 0 and 79.
# ZL = $A000 + 80 * ZY + ZX
 60 ZX =  FN R(80) - 1:ZY =  FN R(80) - 1:ZL = ML + K9 * ZY + ZX: IF  PEEK (ZL) THEN 60
 61  RETURN 


### Initialization, continued ###

# generate 110 monsters; this is repeated below (line 63 can be replaced with a REM)
 63  FOR ZA = 1 TO NM:ZB =  PEEK (ML + (MY + 1) *  PEEK (BY + ZA) +  PEEK (BX + ZA)): POKE BT + ZA,ZB:MH%(ZA) = HH(ZB): NEXT 
# 775=$307
 66  POKE 775,0
# init rank names
 67  FOR A = 1 TO 9: READ RN$(A): NEXT : DATA CANNON FODDER,STABLE BOY,NOVICE,ADVENTURER,SWORDSMAN,VETERAN,MIGHTY WARRIOR,LORD       ,DRAGON SLAYER
# read monster names and attributes
 80  FOR A = 3 TO 35: READ M$(A),HH(A),AA%(A): NEXT 
# repeat the thing we did on line 63, this time with values in HH
# (the "POKE BT+ZA,ZB" appears to be redundant)
 83  FOR ZA = 1 TO NM:ZB =  PEEK (ML + (MY + 1) *  PEEK (BY + ZA) +  PEEK (BX + ZA)): POKE BT + ZA,ZB:MH%(ZA) = HH(ZB): NEXT 
# update display, enable hi=res; 800=$320
 90  GOSUB 10000: GOSUB 10100: & HIRES: POKE 800,0
# monster slot 0 doesn't get populated; used for dragon (which is on
#  the map but not included in BX/BY/BT).
 95 MH%(0) = 350

### MAIN LOOP ###

 100  GOSUB 20000
 110  GOSUB 30000
 120  GOTO 100


## onerr goto ##
 400  RESUME 


### monsters: name, hit points, attack damage
# the "dwagon" doesn't actually have 10K hits; see line 95
 1000  DATA SERPENT,30,20
 1001  DATA ELECTRIC MOTH,15,15
 1002  DATA MAD ROBOT,50,30
 1003  DATA BURBLEBLORT,75,45
 1004  DATA GRIFFIN,150,100
 1005  DATA FLAMEBAT,100, 70
 1006  DATA INVISOID,20,20
 1007  DATA THUNDERBUG,5,150 
 1008  DATA COLDCRYSTAL,300,8
 1009  DATA PHOENIX,200,150 
 1010  DATA MIMIC,88,200
 1011  DATA HEALER,1,1
 1012  DATA WIZARD,65,20
 1013  DATA ,,
 1014  DATA ,,
 1015  DATA ,,
 1016  DATA ,,
 1017  DATA ,,
 1018  DATA ,,
 1019  DATA ,,
 1020  DATA ,,
 1021  DATA ,,
 1022  DATA ,,
 1023  DATA ,,
 1024  DATA ,,
 1025  DATA ,,
 1026  DATA ,,
 1027  DATA DWAGON,,
 1028  DATA DWAGON,,
 1029  DATA DWAGON,,
 1030  DATA DWAGON,,
 1031  DATA DWAGON,,
 1032  DATA DWAGON,10000,120

### redraw hi-res screen ###
# Set $300/301 to the map center.  If in hi-res mode, call 27904=$6D00
 10000  POKE 768,CX: POKE 769,CY: IF DP > 1 THEN  RETURN 
 10010  CALL 27904: RETURN 

### draw character sheet ###

 10100  REM 
 10105  POKE 34,0: POKE 35,24: HOME : POKE 35,23: PRINT NN$ TAB( 29)"+----------+";
 10110  FOR A = 1 TO 9: PRINT  TAB( 29)"!          !";: NEXT : PRINT  TAB( 29)"+----------+"
 10120  VTAB 3: PRINT "RANK:   "RN$(EL): PRINT "HITS:   "HP"/"BH
 10130  PRINT "ARROWS: "AR: PRINT "GOLD:   "GP
 10135  PRINT "EXP:    "EP
# set WNDTOP
 10140  POKE 34,12
 10143  VTAB 10: IF MS THEN  PRINT "MAGIC SWORD +"MS
 10146  VTAB 11: IF SH THEN  PRINT "MAGIC SHIELD +"SH
 10150  GOSUB 10200: REM  REPLOT WINDMAP
 10160  GOSUB 10300
 10190  RETURN 

## draw the text mini-map (unless showing map) ##
 10200  IF DP = 3 THEN  RETURN 
 10210  POKE 768,CX: POKE 769,CY: CALL 27907: RETURN 

## update wield (unless showing map) ##
 10300  IF DP = 3 THEN  RETURN 
 10310  VTAB 8: HTAB 1: PRINT "HOLDING: ";: IF HW = 1 THEN  PRINT "SWORD": RETURN 
 10320  PRINT "BOW  ": RETURN 

## update gold (unless showing map) ##
 10400  IF DP = 3 THEN  RETURN 
 10410  VTAB 6: HTAB 9: PRINT "     ": VTAB 6: HTAB 9: PRINT GP: RETURN 

## update arrows (unless showing map) ##
 10500  IF DP = 3 THEN  RETURN 
 10510  VTAB 5: HTAB 9: PRINT AR"  ": RETURN 

## update HP (unless showing map) ##
 10600  IF DP = 3 THEN  RETURN 
 10610  VTAB 4: HTAB 9: PRINT HP"/"BH"     ": RETURN 

## update experience rank (unless showing map) ##
 10700  IF DP = 3 THEN  RETURN 
 10710  VTAB 3: HTAB 9: PRINT RN$(EL)"     ": RETURN 

### draw map ###
 11000  POKE 770,AX: POKE 771,AY: CALL 27910: RETURN 

### redraw a single tile ###
# $305=X, $306=Y, $304=DP, call $6D09 ###
 12000  POKE 773,X: POKE 774,Y: POKE 772,DP: CALL 27913: RETURN 

### print message ###
# no messages in map mode, ever; clear urgent flag and return
 13000  IF DP = 3 THEN O9 = 0: RETURN 
# double-space it
 13020  IF H9 = 0 THEN  VTAB 23: PRINT : PRINT : VTAB 22:H9 = 1
# if the message ends in '!' or '.' we flash it when messages are on
 13030  PRINT M$;: IF  RIGHT$ (M$,1) = "!" OR  RIGHT$ (M$,1) = "." THEN 13040
 13035  RETURN 
# if hi-res, and messages are enabled or this is urgent, show it briefly
 13040 H9 = 0: IF DP = 1 AND (SM = 1 OR O9 = 1) THEN  & WINDOW: FOR A = 1 TO 888: NEXT : & WINDOFF
 13050 O9 = 0: RETURN 

### arrow animation ###
# pass in ZX/ZY to indicate direction and SX/SY to indicate position
 14000  REM 
 14010  IF ZX = 1 THEN 14100
 14020  IF ZX =  - 1 THEN 14200
 14030  IF ZY = 1 THEN 14300
 14040 A1 = SX * 28 + 12:A2 = SY * 21 + 12:A3 = 3:A4 = 7935:A5 = 2: FOR Z6 = 0 TO 20 * ( ABS (Y1 - SY)) STEP 6: FOR B = 1 TO 2: & CHARSET,A1,A2 - Z6,A3,A4,A5: NEXT B,Z6: RETURN 
 14100 A1 = SX * 28:A2 = SY * 21 + 9:A3 = 0:A4 = 7935:A5 = 2: FOR Z6 = 0 TO 27 *  ABS (X1 - SX) STEP 9: FOR B = 1 TO 2: & CHARSET,A1 + Z6,A2,A3,A4,A5: NEXT B,Z6: RETURN 
 14200 A1 = SX * 28 + 18:A2 = SY * 21 + 9:A3 = 1:A4 = 7935:A5 = 2: FOR Z6 = 0 TO 27 *  ABS (X1 - SX) STEP 9: FOR B = 1 TO 2: & CHARSET,A1 - Z6,A2,A3,A4,A5: NEXT B,Z6: RETURN 
 14300 A1 = SX * 28 + 12:A2 = SY * 21:A3 = 4:A4 = 7935:A5 = 2: FOR Z6 = 0 TO 20 * ( ABS (Y1 - SY)) STEP 6: FOR B = 1 TO 2: & CHARSET,A1,A2 + Z6,A3,A4,A5: NEXT B,Z6: RETURN 

# draw "hit" graphic; data set is at 7935 ($1EFF, CF.MISC)
 15000  & CHARSET,(X - CX) * 28 + 9,(Y - CY) * 21 + 6,2,7935,2: RETURN 

### update player bitmap when RR <> 0 ###
 15500 ZW = 25: IF RR =  - 4 THEN ZW = 17 + HW

### update player bitmap (val in ZW) ###
# TB is $7FC0 (SKETCHZ); 168 is 84*2 == item #2 (the player)
 16000 Z1 = TB + ZA + 168
# ZW is icon source (18=sword, 16/17/19/20=bow, 25=bird, 36=blank)
  :Z2 = TB + ZA + 84 * ZW
# set destination and source
  : POKE 7,Z1 / 256: POKE 6,Z1 - 256 *  PEEK (7)
  : POKE 9,Z2 / 256: POKE 8,Z2 - 256 *  PEEK (9)
# call 27925=$6D15 to copy 84 bytes from $08/09 to $06/07
  : CALL 27925
# update X/Y to current position
  :X = CX + AX:Y = CY + AY
# redraw tile
  : GOTO 12000


### update display and wait for input or timeout ###

# set $304 to display mode and call $6D0C.  If $308 is nonzero
#  (input pending), head to 20050 to handle it.
 20000  POKE 772,DP: CALL 27916: IF  PEEK (776) THEN 20050
# If we're in the post-run cooldown, update RR.
 20025  IF RR < 0 THEN RR = RR + 1
 20030  RETURN 

 20040 TM =  PEEK (775): & CBOX,1,TM,189,TM + 1,191,1,0
 20045  POKE 775,0: RETURN 

### handle user input ###
# if we're in run cooldown, player doesn't get to move
 20050  IF RR < 0 THEN 20025
# get the input, see if it's a display mode (1-3)
 20051  GET A$:ZA =  VAL (A$): IF ZA = 0 THEN 20085
# always in mode 1 (hi-res) while running
 20052  IF RR THEN 20090
# handle mode switch
 20055  IF ZA = 1 THEN DP = 1: GOSUB 10000: & HIRES: GOSUB 10100: GOTO 20000
 20060  IF ZA > 2 THEN 20080
 20065  IF DP = 3 THEN DP = 2: GOSUB 10100
 20070 DP = 2: & HROFF: GOTO 20000
 20080  IF ZA = 3 THEN  & HROFF: GOSUB 11000:DP = ZA: GOTO 20000
# check movement, unless we're stick to a mimic (S2)
 20085  IF S2 = 1 THEN 20200
 20090 ZQ = 1:ZF = 0: FOR DY =  - 1 TO 1: FOR DX =  - 1 TO 1: IF A$ =  MID$ ("TYUGHJBNM",ZQ,1) THEN ZX = DX:ZY = DY:ZF = ZQ:DX = 1:DY = 1
 20100 ZQ = ZQ + 1: NEXT DX,DY: IF ZF = 0 THEN 20200
 20110 X = CX + AX:Y = CY + AY:ZZ =  PEEK (ML + K9 * (Y + ZY) + X + ZX): IF ZZ = 0 OR ZZ = 14 OR ZZ > C0 THEN 20140
# run into a wall? 1-3 HP dam
 20120  IF ZZ = 1 THEN  POKE 6,2: CALL 27946:M$ = "OOF!  HIT A WALL, ": GOSUB 13000:ZD =  FN R(3): GOSUB 51000: GOTO 20040
 20122  IF A$ = "H" THEN 20040
# 27946=$6D2A
 20130  POKE 6,2: CALL 27946
 20135 M$ = "BLOCKED BY " + M$(ZZ) + "!": GOSUB 13000: GOTO 20040
# set Z9 to the thing we moved onto (empty, healer, chest, Inn)
 20140 Z9 = ZZ: POKE ML + K9 * Y + X,T0: GOSUB 12000:X = X + ZX:Y = Y + ZY:AX = AX + ZX:AY = AY + ZY: POKE ML + (MY + 1) * Y + X,2:T0 = 0
 20145  IF DP = 3 THEN  GOSUB 11000
# something special about column 74?
 20147  IF AX + CX = 74 AND ZX <  > 0 THEN 20152
# have we reached the edge of the display?
 20150  IF AX >  = XL AND AX <  = XR AND AY >  = YL AND AY <  = YR THEN 20160
# yes, recenter map on our current position
# don't push past edges of map
 20152 CX = X - 4:CY = Y - 4: IF CX < 0 THEN CX = 0
 20153  IF CY < 0 THEN CY = 0
 20154  IF CX > MX - 9 THEN CX = MX - 9
 20155  IF CY > MY - 8 THEN CY = MY - 8
# update AX/AY
 20156 AX = X - CX:AY = Y - CY: GOSUB 10200: GOSUB 10000
 20160  GOSUB 12000: POKE 6,0: IF RR THEN  POKE 6,1
# call $6D2A; if we moved onto something other than empty space, branch
 20161  CALL 27946: IF Z9 THEN 20170
# if run mode is active, decrement it
 20163  IF RR = 0 THEN 20040
 20165 RR = RR - 1: IF RR = 0 THEN RR =  - 4: GOSUB 15500: GOTO 20040
 20167  IF RR = 4 THEN O9 = 1:M$ = "THE SPELL IS WEARING OFF.": GOSUB 13000
# on even counts, player moves twice
 20168  IF RR / 2 =  INT (RR / 2) THEN 20000
 20169  GOTO 20040

## handle movement onto something (chest, healer, Inn)
 20170  IF Z9 <  > C0 + 1 THEN 20185
# open chest; U2 is set to 1 if it's a teleport trap
 20175  GOSUB 52000: IF U2 THEN 20152
 20180  GOTO 20040
# if it's the Inn then handle that
 20185  IF Z9 = C0 + 2 THEN  GOSUB 53000: GOTO 20040
# if it's a healer then do that
 20190  IF Z9 = 14 THEN  GOSUB 54000: GOTO 20040
## shouldn't fall through here ##

# can't attack, change modes, or various other things or while running
 20200  IF RR THEN  IF A$ <  >  CHR$ (27) THEN 20000
# check for attack key
# sets XZ/ZY to {-1,0,1} based on direction; sets ZF>0 on match
 20205 ZQ = 1:ZX = 0:ZY = 0:ZF = 0: FOR DY =  - 1 TO 1: FOR DX =  - 1 TO 1: IF  ASC (A$) =  ASC ( MID$ ("TYUG*JBNM",ZQ,1)) - 64 THEN ZX = DX:ZY = DY:ZF = ZQ
 20210 ZQ = ZQ + 1: NEXT DX,DY: IF ZF = 0 THEN 20500
# compute X/Y of attacked square; set ZZ to tile contents
 20215 X = CX + AX + ZX:Y = CY + AY + ZY:ZZ =  PEEK (ML + K9 * Y + X)
# ZZ is type of object at target location
 20220  ON HW GOTO 20240,20350
# swing sword; if attacked a monster, branch
 20240  IF ZZ > 1 AND ZZ < C0 + 1 THEN 20250
 20243 M$ = "SWISH...": IF ZZ = 1 THEN M$ = "CLUNK!"
 20244  POKE 6,0: CALL 27943
 20246  GOSUB 13000: RETURN 
# damage is 1-10 * level, plus bonus for a magic sword
 20250 ZD =  FN R(10) * (EL + MS)
# figure out which monster we hit (781=$30d, 27949=$6D2D)
# (returns 0 if nothing found, which indicates it's part of the dragon)
 20255  POKE 781,NM: POKE 6,X: POKE 7,Y: CALL 27949:ZF =  PEEK (781)
# play a sound (27943=$6D27)
 20262  GOSUB 15000: POKE 6,1: CALL 27943: GOSUB 15000
 20265 Z5 = ZF
 20270 MH%(ZF) = MH%(ZF) - ZD:M$ = "YOU HIT THE " + M$(ZZ) + " FOR " +  STR$ (ZD) + " POINTS.": GOSUB 13000
# if monster is still alive, branch
 20271  IF MH%(ZF) > 0 THEN 20040
# monster is dead; if Z5 is zero (meaning we didn't find it in the
#  table) then we must've killed the dragon
 20272  IF Z5 = 0 THEN 40000
 20273 KL% = KL% + 1
# set Z8 to type of monster we just killed; if it was a Mimic, clear "stuck" flag
 20275 Z8 = ZZ: IF ZZ = 13 THEN S2 = 0
# draw "poof" death (27943=$6D27)
 20283  POKE ML + K9 * Y + X,C0 + 3: GOSUB 12000: POKE 6,4: CALL 27943
 20285  POKE ML + K9 * Y + X,0: GOSUB 12000
# pick a new monster type at random (3-15)
  :ZT = 2 +  FN R(13)
# update experience points
 20290 EP = EP + HH(Z8)
# find an empty map square, spawn a replacement, and put it in monster table
  : GOSUB 60: POKE BX + Z5,ZX: POKE BY + Z5,ZY: POKE BT + Z5,ZT: POKE ZL,ZT
# if it's on-screen, draw it
  :X = ZX:Y = ZY: GOSUB 12000
# set the monster's health according to type, then pick a new type in case we want to expand the set
 20292 MH%(ZF) = HH(ZT):ZT = 2 +  FN R(13)
# if there's fewer than 170 monsters, spawn another one
 20293  IF NM < 169 THEN NM = NM + 1: GOSUB 60: POKE BX + NM,ZX: POKE BY + NM,ZY: POKE BT + NM,ZT: POKE ZL,ZT:X = ZX:Y = ZY: GOSUB 12000:MH%(NM) = HH(ZT)
 20295  IF DP <  > 3 THEN  VTAB 7: HTAB 1: PRINT "EXP:    "EP
 20300  RETURN 

### shoot arrow ###
# ZX/ZY are {-1,0,1} indicating direction of shot; player cannot shoot
#  diagonally, so one should be zero
 20350  IF ZX AND ZY THEN 20000
# update player tile with direction-specific bow image
 20351 ZW = 18 + ZX + 2 * ZY: GOSUB 16000:X = CX + AX + ZX:Y = CY + AY + ZY
 20353  IF AR = 0 THEN M$ = "OUT OF ARROWS!": GOSUB 13000: GOTO 20040
# reduce arrow count
 20355 AR = AR - 1: IF DP < 3 THEN  VTAB 5: HTAB 9: PRINT "   ": VTAB 5: HTAB 9: PRINT AR
# check to see if the adjacent tile in the firing direction is occupied
 20360  IF ZZ > 0 THEN M$ = "TOO CLOSE!": GOSUB 13000: GOTO 20040
 20365 SX = X - CX:SY = Y - CY
# loop to figure out what we hit:
 20370 X1 = X - CX:Y1 = Y - CY
 20380  IF X1 < 0 OR X1 > 9 OR Y1 < 0 OR Y1 > 9 THEN 20410
 20390 ZZ =  PEEK (ML + K9 * Y + X): IF ZZ <  > 1 AND ZZ <  > C0 + 1 AND ZZ <  > C0 + 2 THEN 20420
 20410  GOSUB 14000:M$ = "MISSED...": GOSUB 13000: GOTO 20040
 20420  IF ZZ = 0 THEN X = X + ZX:Y = Y + ZY: GOTO 20370
 20425  GOSUB 14000
# hit something that isn't a wall, chest, or Inn; compute arrow damage
 20430 ZD =  FN R(6) * (EL + 2): GOTO 20255

# not reachable?
 20435  GOSUB 14000

### continue checking keys ###
 20500  IF A$ <  > "O" THEN 20550
# toggle messages on/off
 20510 ZJ = 1 - SM: INVERSE :SM = 1:M$ = "MESSAGES O": IF ZJ = 0 THEN M$ = M$ + "FF."
 20520  IF ZJ = 1 THEN M$ = M$ + "N."
 20530  GOSUB 13000: NORMAL :SM = ZJ: GOTO 20000
 20550  IF A$ <  > ":" THEN 20610
# recenter map
 20560 X = CX + AX:Y = CY + AY:CX = X - 4:CY = Y - 4: IF CX < 0 THEN CX = 0
 20570  IF CY < 0 THEN CY = 0
 20580  IF CX > MX - 9 THEN CX = MX - 9
 20590  IF CY > MY - 8 THEN CY = MY - 8
 20600 AX = X - CX:AY = Y - CY: GOSUB 10000: GOSUB 10200: GOTO 20000
 20610  IF A$ <  > " " THEN 20700
# toggle weapon 1/2
 20620 HW = 3 - HW: GOSUB 10300
 20630 ZW = 17 + HW: GOSUB 16000: GOTO 20040
 20700  IF (A$ = "A" OR A$ = "P") AND (DP = 3 OR T0 = 0) THEN 20000
 20710  IF A$ <  > "A" THEN 20800
# 10 gold for 5 arrows
 20730 ZA = 5: IF AR > 94 THEN M$ = "CAN'T CARRY ANY MORE!": GOSUB 13000: GOTO 20040
 20740  IF GP < ZA * 2 THEN M$ = "YOU NEED MORE GOLD FIRST.": GOSUB 13000: GOTO 20040
 20750 GP = GP - ZA * 2:AR = AR + ZA: GOSUB 10400: GOSUB 10500
 20760  GOTO 20040
 20800  IF A$ <  > "P" THEN 20900
# 25 gold for 5 HP
 20830 ZA = 5
 20840  IF GP < ZA * 5 THEN M$ = "YOU NEED MORE GOLD FIRST.": GOSUB 13000: GOTO 20040
 20850 GP = GP - ZA * 5:BH = BH + ZA:HP = BH: GOSUB 10400: GOSUB 10600: GOTO 20040
 20900  IF A$ <  >  CHR$ (27) THEN 20000
# if currently running, cancel run mode
 20905  IF RR > 0 THEN RR =  - 4: GOSUB 15500: GOTO 20040
 20910  IF RR THEN 20000
# enable run mode
 20920 RR = 24 - SL%:M$ = "RUN AWAY!": GOSUB 15500: GOSUB 13000:ZA = 1: GOTO 20055


### move monsters ###
 30000 TN% = TN% + 1
 30005  POKE 777,CX + AX: POKE 778,CY + AY: POKE 772,DP: POKE 770,AX: POKE 771,AY
# 27919=$6D0F
 30010  POKE 781,NM: CALL 27919: IF  PEEK (782) THEN  GOSUB 31000
 30012 Z7 = 795
# check for zap by wizards -- window X/Y will be other than $ff/$ff
#  first wizard's coords in 795/796; second coords in 797/798
 30014  IF  PEEK (Z7) < 30 THEN  GOSUB 33000:Z7 = Z7 + 2: IF Z7 < 799 THEN 30014
# check for hit by dragon flame
 30020  IF  PEEK (799) THEN  GOSUB 42000
# if we're next to the dragon, it will attack us
 30030  IF CX + AX > 73 THEN  IF CY + AY > 70 AND CY + AY < 74 AND EL < 9 THEN MT = 35: GOSUB 32010
 30050  RETURN 

### handle all monster attacks ###
# 782($30e): number of attacks (0-3)
# 783-786($30f-312): attacking monster ID (0-169)
 31000  FOR Z7 = 1 TO  PEEK (782):ZC =  PEEK (782 + Z7): GOSUB 32000: NEXT Z7: RETURN 

### handle single monster attack (ident 0-169 in ZC) ###
# get monster type
 32000 MT =  PEEK (BT + ZC)
# check for block (can't block with bow, when running, or when
#  attacked by Invisoid)
 32010  IF HW = 1 AND RR = 0 THEN  IF  RND (1) < (SH + EL) / 20 AND MT <  > 9 THEN M$ = M$(MT) + " BLOCKED WITH SHIELD.": GOSUB 13000: RETURN 
 32011 X = CX + AX:Y = CY + AY
 32012  GOSUB 15000: POKE 6,2: CALL 27943: GOSUB 15000
 32015 M$ = M$(MT) + " ATTACKS, ": GOSUB 13000
# damage is limited to 70% of value in table
 32020 ZD =  FN R(AA%(MT) * .7): GOSUB 51000
# successful attack by mimic makes you immobile
  : IF MT = 13 AND S2 = 0 THEN M$ = "YOU'RE STUCK TO THE MIMIC!":S2 = 1:O9 = 1: GOSUB 13000
 32030  RETURN 

### wizard ranged attack ###
 33000 ZX =  PEEK (Z7):ZY =  PEEK (Z7 + 1):ZB = ZX * 28 + 17:ZC = ZY * 21 + 13:ZD = 816:ZG = 0:ZH = 2
# if vertical attack, branch
 33010  IF ZX = AX THEN 33040
# horizontal attack
 33020 ZF =  ABS (ZX - AX) * 16:ZE = 2: IF AX < ZX THEN ZE = 0
 33030  GOTO 33050
 33040 ZF =  ABS (ZY - AY) * 16:ZE = 3: IF AY < ZY THEN ZE = 1
 33050  IF ZF = 16 OR ZF > 100 THEN  RETURN 
# draw twice in "invert" mode (ZH=2); first draws, second erases
#  ZA = colorIndex = 5 (white)
#  ZB = screen X coord of wizard
#  ZC = screen Y coord of wizard
#  ZD = data ptr (816/$330)
#  ZE = direction (0=left, 1=up, 2=right, 3=down)
#  ZF = distance in squares, * 16
#  ZG = angle = 0
#  ZH = drawMode = 2 (invert)
 33051  POKE 6,0: FOR ZZ = 1 TO 2: FOR ZA = 5 TO 5: & LINESET,ZA,ZB,ZC,ZD,ZE,ZF,ZG,ZH: NEXT : IF ZZ = 1 THEN  POKE 6,5: CALL 27943
 33052  NEXT 
# damage is (1-50)+25 rather than melee monster damage
 33060 M$ = "ZAPPO!  ": GOSUB 13000:ZD =  FN R(50) + 25: GOSUB 51000: RETURN 

### dragon is dead ###
# remove all dragon tiles
 40000 ZX = 75 - CX:ZY = 71 - CY: FOR ZA = 71 TO 72: FOR ZB = 75 TO 77: POKE ML + K9 * ZA + ZB,0: NEXT : NEXT 
# draw a solid box in "invert" mode on the dragon, and play sound
 40010 ZX = ZX * 28:ZY = ZY * 21: & BOX,ZX,ZY,ZX + 83,ZY + 41,2,2: POKE 6,3: CALL 27943
 40020  GOSUB 10000: GOSUB 10200
# disable dragon breath
  : POKE 800,9
# give up to 800 HP
 40030 ZA = 100 * (9 - EL):BH = BH + ZA:HP = HP + ZA:EL = 9: GOSUB 10600: GOSUB 10700: RETURN 

### hit by dragon flame attack ###
 42000 M$ = "SIZZLE! ":ZD = 250: GOSUB 13000: GOSUB 51000: RETURN 

### initial setup -- ask player for game parameters ###
 50000  TEXT 
 50010  HOME : INPUT "YOUR NAME, WARRIOR? ";NN$: IF  LEN (NN$) > 16 THEN NN$ =  LEFT$ (NN$,16)
 50020  PRINT : PRINT "SKILL LEVEL (1-9)? ";
 50030  GET A$:SL% =  VAL (A$): IF SL% = 0 THEN 50030
 50040  PRINT A$: PRINT : INPUT "GAME SPEED (1-50)? ";A$:ZA =  VAL (A$): IF ZA = 0 THEN ZA = 1
 50045  IF ZA > 50 THEN ZA = 50
 # store speed-1 in $319
 50050  POKE 793,ZA - 1:ZD = 0: IF SL% > 7 THEN ZD = 1
# for skill levels 8/9 add a couple of walls to make the maze harder to traverse
#  (7,39) and (6,42) are visible from starting point
 50051  POKE 44087,ZD: POKE 44326,ZD
 50100  PRINT : PRINT : PRINT : PRINT : INVERSE : PRINT "ONE MOMENT WHILE I AWAKEN THE MONSTERS.": NORMAL : RETURN 

### handle player being hit ###
# damage taken is in ZD
 51000 M$ =  STR$ (ZD) + " HITS.": GOSUB 13000
 51020 HP = HP - ZD: GOSUB 10600: IF HP > 0 THEN 51060
# play funeral song ($6D24)
 51025  CALL 27940: IF DP = 3 THEN DP = 2: GOSUB 10100
 51027  INVERSE 
 51030 M$ = "YOU'RE DEAD!": GOSUB 13000: NORMAL : POKE  - 16368,0: TEXT : VTAB 24: HTAB 1: PRINT "PRESS ANY KEY TO PLAY AGAIN....";: GET A$
 51040  GOSUB 57000: RUN 
 51060  IF H4 = 0 AND (HP / BH) < .33 THEN H4 = 1:O9 = 1:M$ = "YOU'RE FEELING WEAK.": GOSUB 13000
 51070  IF H4 = 1 THEN  IF (HP / BH) < .1 THEN H4 = 2:O9 = 1:M$ = "YOU'VE LOST A LOT OF BLOOD...": GOSUB 13000
 51080  RETURN 

### open chest ###
# clear teleport flag, set urgent message flag
 52000 U2 = 0:O9 = 1
# every other time we open a chest, create a new one at a random location
 52005 N4 = 1 - N4: IF N4 THEN  GOSUB 60: POKE ZL,C0 + 1
# 83% chance of 1-50 gold coins, 17% chance of other
 52010  IF  RND (1) > .83 THEN 52030
 52020 ZG =  FN R(50):M$ = "YOU FOUND " +  STR$ (ZG) + " GOLD COINS.":GP = GP + ZG: GOSUB 13000: GOSUB 10400: RETURN 
## other: 20% chance each of arrows, +N magic sword, magic shield, empty, trap
 52030  ON  FN R(5) GOTO 52040,52100,52200,52300,52400
 52040 ZA =  FN R(30) + 2:M$ = "YOU FOUND " +  STR$ (ZA) + " ARROWS.": GOSUB 13000:AR = AR + ZA: IF AR > 99 THEN AR = 99
 52045  GOSUB 10500: RETURN 
 52050 ZP =  FN R(3):M$ = "YOU FOUND A +" +  STR$ (ZP) + " MAGIC S": RETURN 
 52100  GOSUB 52050:M$ = M$ + "WORD!": GOSUB 13000
 52110  IF ZP > MS THEN MS = ZP: IF DP <  > 3 THEN  VTAB 10: HTAB 1: PRINT "MAGIC SWORD +"MS
 52120  RETURN 
 52200  GOSUB 52050:M$ = M$ + "HIELD!": GOSUB 13000
 52210  IF ZP > SH THEN SH = ZP: IF DP <  > 3 THEN  VTAB 11: HTAB 1: PRINT "MAGIC SHIELD +"SH
 52220  RETURN 
 52300 M$ = "THE CHEST IS EMPTY!": GOSUB 13000: RETURN 
# clear the square we were on, then find a random empty square
# play teleportation sound (27943=$6D27)
 52400  POKE ML + K9 * (CY + AY) + CX + AX,0:M$ = "IT'S A TELEPORT TRAP!": GOSUB 13000: GOSUB 60:X = ZX:Y = ZY:U2 = 1: POKE ZL,2: POKE 6,3: CALL 27943: RETURN 

### moved onto Inn ###
 53000  REM INN
# if we're a Dragon Slayer, game won
 53001  IF EL = 9 THEN 60000
# set prev tile = Inn
 53005 T0 = C0 + 2
# handle level-up
 53010  IF EP >  = EN THEN O9 = 1:BH = BH + 100:EL = EL + 1: GOSUB 10700:M$ = "YOU WENT UP A LEVEL!": GOSUB 13000:EN = EN * 2: IF EL > 7 THEN EN = 1E10
# restore hit points to max, reset health warning
 53020 HP = BH: GOSUB 10600:H4 = 0
 53025 M$ = "HIT A TO BUY ARROWS, P FOR HIT POINTS.": GOSUB 13000
 53030  RETURN 

### touched a healer ###
 54000 ZD =  FN R(150) + 25: IF ZD > BH - HP THEN ZD = BH - HP
 54010 M$ = "YOU REGAIN " +  STR$ (ZD) + " HIT POINTS.":O9 = 1: POKE 6,4: CALL 27943: GOSUB 13000:HP = HP + ZD: GOSUB 10600
# remove the healer, and replace it with a randomly-placed wizard
 54020  POKE 781,NM: POKE 6,CX + AX: POKE 7,CY + AY: CALL 27949:Z9 =  PEEK (781): POKE BT + Z9,15:XX = T0: GOSUB 60: POKE BX + Z9,ZX: POKE BY + Z9,ZY: POKE ZL,15:MH%(15) = HH(15)
 54030  RETURN 

### post-death cleanup ###
# update player bitmap
# BUG: should set RR to 0 to reset player bitmap
 57000  TEXT : IF RR > 0 THEN  GOSUB 15500
# BUG: C0 is a blank image, so you're invisible if you were holding the bow
 57010  IF HW = 2 THEN ZW = C0: GOSUB 16000
 57020  RETURN 

### victory ###
# 31232=$7A00, fireworks display
 60000  HOME : TEXT : VTAB 21: PRINT "THOU HAST SLAIN THE DRAGON AND ESCAPED! ": PRINT "PRESS ANY KEY TO PLAY AGAIN...": CALL 31232: RUN