886 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			886 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
_runtimePath_ "../../runtime"
 | 
						|
_runtimeStart_ &h7FFF
 | 
						|
_arraysStart_ &h7FFF
 | 
						|
_codeRomType_ ROMv5a
 | 
						|
 | 
						|
'size of your most complex expression, (temporary variables required)*2, defaults to 8
 | 
						|
_tempVarSize_ 6
 | 
						|
 | 
						|
'free string work area, (better not use any of the string runtime!)
 | 
						|
free STRINGWORKAREA
 | 
						|
 | 
						|
'free scanline 0, 1, 118 and 119 for code and data
 | 
						|
free &h0800, 160
 | 
						|
free &h0900, 160
 | 
						|
free &h7E00, 160
 | 
						|
free &h7F00, 160
 | 
						|
 | 
						|
'use this after _runtimeStart_ to specify maximum number of sprites if you have more than 48
 | 
						|
_maxNumSprites_ 61
 | 
						|
 | 
						|
'defines the amount of contiguous RAM needed for sprite stripes, (in this case 15*6 + 1), also min address and search direction
 | 
						|
_spriteStripeChunks_ 15, &h3BA0, descending
 | 
						|
 | 
						|
module "PucMonData_ROMv5a.m"
 | 
						|
 | 
						|
'livesDots <8:lives><8:dots>
 | 
						|
'levelPain <8:level><8:pain>
 | 
						|
'timeTicks <8:time><8:ticks>
 | 
						|
'flags <8:puc flags><8:ghost flags>, puc flags XXXXXXXD, ghost flags XXXXXXSD : where S = slowed, D = dead
 | 
						|
 | 
						|
'define vars without intitialisation, (initialised in initVars)
 | 
						|
def livesDots, levelPain, timeTicks, flags, gmode, gx, gy, gxd, gyd, gdir, mz, pyd, oxd, oyd, puci, pucj, ti, tj, bi, bj, zi, zj, px, py, pxd, bonus, eatTicks, scaredTicks
 | 
						|
 | 
						|
gosub initSystem
 | 
						|
'call drawGrid
 | 
						|
'call drawCells
 | 
						|
call drawMaze
 | 
						|
    
 | 
						|
reset:
 | 
						|
    gosub resetLevel
 | 
						|
    
 | 
						|
start:
 | 
						|
    gosub startLevel
 | 
						|
    
 | 
						|
init:
 | 
						|
    call initVars
 | 
						|
 | 
						|
repeat
 | 
						|
    call waitArbitrary
 | 
						|
 | 
						|
    'change sprite priority based on who is agressor
 | 
						|
    if gmode &&= SCARED_MODE
 | 
						|
        gosub drawGhosts
 | 
						|
        call drawPucMon
 | 
						|
    else
 | 
						|
        call drawPucMon
 | 
						|
        gosub drawGhosts
 | 
						|
    endif
 | 
						|
 | 
						|
    'level complete
 | 
						|
    if eatTicks.lo &&= 0
 | 
						|
        if livesDots.lo &&= 0
 | 
						|
            goto nextLevel
 | 
						|
        endif
 | 
						|
    endif
 | 
						|
    
 | 
						|
    'puc died
 | 
						|
    if flags.hi &&= 1
 | 
						|
        flags.hi = 0
 | 
						|
        livesDots.hi = livesDots.hi - 1
 | 
						|
        call drawDeath
 | 
						|
        call drawDots, 0 'refresh
 | 
						|
        if livesDots.hi &&= 0
 | 
						|
            goto reset
 | 
						|
        endif
 | 
						|
        goto init
 | 
						|
    endif
 | 
						|
 | 
						|
    'tunnels
 | 
						|
    sprite NoFlip, Tunnel, (1+ORIGIN_X),   (51+ORIGIN_Y)
 | 
						|
    sprite NoFlip, Tunnel, (123+ORIGIN_X), (51+ORIGIN_Y)
 | 
						|
    
 | 
						|
    call drawPills
 | 
						|
    
 | 
						|
    gosub setGhostsMode
 | 
						|
    call moveGhosts
 | 
						|
 | 
						|
    gosub handleInput
 | 
						|
    gosub movePucMon
 | 
						|
    
 | 
						|
    inc timeTicks.lo
 | 
						|
forever
 | 
						|
 | 
						|
'fast divide by 5 for ci and cj, only works for the domain 0<->319
 | 
						|
'this sub need to be fast, so place it early in the source file so that it will most likely
 | 
						|
'be placed in a large RAM page and thus not require page jumps
 | 
						|
'there is no contiguous memory left on a 32K system for 160byte and 120byte LUTs
 | 
						|
proc div5
 | 
						|
    local i, j
 | 
						|
    
 | 
						|
    ci = ci - (ci LSR 6)                             'error term
 | 
						|
    i  = ci LSL 4 : ci = (i LSL 1) + i + (ci LSL 2)  'multiply by 52
 | 
						|
    ci = ci.hi                                       'divide by 256
 | 
						|
 | 
						|
    cj = cj - (cj LSR 6)                             'error term
 | 
						|
    j  = cj LSL 4 : cj = (j LSL 1) + j + (cj LSL 2)  'multiply by 52
 | 
						|
    cj = cj.hi                                       'divide by 256
 | 
						|
endproc
 | 
						|
 | 
						|
'fast modulus by 5 for mx and my, only works for the domain 0<->20734
 | 
						|
'this sub need to be fast, so place it early in the source file so that it will most likely
 | 
						|
'be placed in a large RAM page and thus not require page jumps and allow us to use full BRA optimisations
 | 
						|
'there is no contiguous memory left on a 32K system for 160byte and 120byte LUTs
 | 
						|
mod5:
 | 
						|
    mx = mx.hi + mx.lo : mx = (mx LSR 4) + (mx AND &h000F)
 | 
						|
    if mx &&> 14
 | 
						|
        mx = mx - 15
 | 
						|
    elseif mx &&> 9
 | 
						|
        mx = mx - 10
 | 
						|
    elseif mx &&> 4
 | 
						|
        mx = mx - 5
 | 
						|
    endif
 | 
						|
    
 | 
						|
    my = my.hi + my.lo : my = (my LSR 4) + (my AND &h000F)
 | 
						|
    if my &&> 14
 | 
						|
        my = my - 15
 | 
						|
    elseif my &&> 9
 | 
						|
        my = my - 10
 | 
						|
    elseif my &&> 4
 | 
						|
        my = my - 5 
 | 
						|
    endif
 | 
						|
return
 | 
						|
 | 
						|
proc drawPucMon
 | 
						|
    local xf, yf
 | 
						|
    
 | 
						|
    if mz &&= WALL
 | 
						|
        sprite NoFlip, PucLt + 2, px, py
 | 
						|
        return
 | 
						|
    endif
 | 
						|
    
 | 
						|
    xf = (px LSR 1) AND 3
 | 
						|
    yf = (py LSR 1) AND 3
 | 
						|
    if pxd &&= 1
 | 
						|
        sprite FlipX, PucRt + xf, px, py
 | 
						|
    elseif pxd &= -1
 | 
						|
        sprite NoFlip, PucLt + xf, px, py
 | 
						|
    elseif pyd &&= 1
 | 
						|
        sprite FlipY, PucDn + yf, px, py
 | 
						|
    else
 | 
						|
        sprite NoFlip, PucUp + yf, px, py
 | 
						|
    endif
 | 
						|
    px = px + pxd
 | 
						|
    py = py + pyd
 | 
						|
endproc
 | 
						|
 | 
						|
drawGhosts:
 | 
						|
    for gidx=0 &to NUM_GHOSTS-1
 | 
						|
        gosub getGhostVars
 | 
						|
 | 
						|
        'if puc died or level is complete, then erase ghost
 | 
						|
        if &((flags.hi) OR (livesDots.lo = 0))
 | 
						|
            sprite NoFlip, Erase12x9, gx, gy
 | 
						|
            goto drawNextGhost
 | 
						|
        endif
 | 
						|
 | 
						|
        'ghost dead, scared or normal
 | 
						|
        if &(flags.lo AND 1)
 | 
						|
            gosub drawEyesGhost
 | 
						|
        elseif gmode &&= SCARED_MODE
 | 
						|
            if timeTicks.hi &&< (8-levelPain.hi)
 | 
						|
                call drawScaredGhost
 | 
						|
            else
 | 
						|
                if &((timeTicks.lo LSR 3) AND 1)
 | 
						|
                    call drawNormalGhost
 | 
						|
                else
 | 
						|
                    call drawScaredGhost
 | 
						|
                endif
 | 
						|
            endif
 | 
						|
        else
 | 
						|
            call drawNormalGhost
 | 
						|
        endif
 | 
						|
        
 | 
						|
drawNextGhost:        
 | 
						|
    next gidx
 | 
						|
return
 | 
						|
 | 
						|
proc drawNormalGhost
 | 
						|
    local i, xf, yf, yskirt
 | 
						|
    
 | 
						|
    i = gidx LSL 2
 | 
						|
    xf = (gx LSR 2) AND 1
 | 
						|
    yf = (gy LSR 2) AND 1
 | 
						|
    yskirt = gy + 7
 | 
						|
    
 | 
						|
    if (gxd)
 | 
						|
        if gxd &&= 1
 | 
						|
            sprite FlipX, peek(@gframes + 0 + i), gx, gy
 | 
						|
        else
 | 
						|
            sprite NoFlip, peek(@gframes + 1 + i), gx, gy
 | 
						|
        endif
 | 
						|
        if &(xf) then sprite NoFlip, peek(@gframes1 + gidx), gx, yskirt  'draw skirt
 | 
						|
    else
 | 
						|
        if gyd &&= 1
 | 
						|
            sprite NoFlip, peek(@gframes + 2 + i), gx, gy
 | 
						|
            inc yskirt
 | 
						|
        else
 | 
						|
            sprite NoFlip, peek(@gframes + 3 + i), gx, gy
 | 
						|
        endif
 | 
						|
        if &(yf) then sprite NoFlip, peek(@gframes1 + gidx), gx, yskirt  'draw skirt
 | 
						|
    endif
 | 
						|
endproc
 | 
						|
 | 
						|
proc drawScaredGhost
 | 
						|
    local xf, yf, yskirt
 | 
						|
 | 
						|
    xf = (gx LSR 2) AND 1
 | 
						|
    yf = (gy LSR 2) AND 1
 | 
						|
 | 
						|
    yskirt = gy + 7
 | 
						|
 | 
						|
    if (gxd)
 | 
						|
        if gxd &&= 1
 | 
						|
            sprite FlipX,  ScaredRt, gx, gy
 | 
						|
        else
 | 
						|
            sprite NoFlip, ScaredLt, gx, gy
 | 
						|
        endif
 | 
						|
        if (xf) then sprite NoFlip, Scared_1, gx, yskirt  'draw skirt
 | 
						|
    else
 | 
						|
        if gyd &&= 1
 | 
						|
            sprite NoFlip, ScaredDn, gx, gy
 | 
						|
            inc yskirt
 | 
						|
        else
 | 
						|
            sprite NoFlip, ScaredUp, gx, gy
 | 
						|
        endif
 | 
						|
        if &(yf) then sprite NoFlip, Scared_1, gx, yskirt  'draw skirt
 | 
						|
    endif
 | 
						|
endproc
 | 
						|
 | 
						|
drawEyesGhost:
 | 
						|
    if gxd &&= 1
 | 
						|
        sprite FlipX,  EyesRt, gx+2, gy+3
 | 
						|
    elseif gxd &= -1
 | 
						|
        sprite NoFlip, EyesLt, gx+2, gy+3
 | 
						|
    elseif gyd &&= 1
 | 
						|
        sprite NoFlip, EyesDn, gx+2, gy+3
 | 
						|
    else
 | 
						|
        sprite FlipY,  EyesUp, gx+2, gy+3
 | 
						|
    endif
 | 
						|
return
 | 
						|
 | 
						|
proc drawPills
 | 
						|
    local i
 | 
						|
    
 | 
						|
    i = timeTicks.lo AND 7
 | 
						|
    if i &&= 0
 | 
						|
        i = (timeTicks.lo LSR 3) AND 1
 | 
						|
        if peek(PILL_LT) &= PILL then sprite NoFlip, Pill + i, (1*5)  + (ORIGIN_X-2), (2*5)  + (ORIGIN_Y - 3)
 | 
						|
        if peek(PILL_RT) &&= PILL then sprite NoFlip, Pill + i, (26*5) + (ORIGIN_X-2), (2*5)  + (ORIGIN_Y - 3)
 | 
						|
        if peek(PILL_LB) &&= PILL then sprite NoFlip, Pill + i, (1*5)  + (ORIGIN_X-2), (18*5) + (ORIGIN_Y - 3)
 | 
						|
        if peek(PILL_RB) &&= PILL then sprite NoFlip, Pill + i, (26*5) + (ORIGIN_X-2), (18*5) + (ORIGIN_Y - 3)
 | 
						|
    endif
 | 
						|
endproc
 | 
						|
 | 
						|
movePucMon:
 | 
						|
    gosub coordsPucMon
 | 
						|
    puci = ci - pxd
 | 
						|
    pucj = cj - pyd
 | 
						|
        
 | 
						|
    'dots, pills, doors and tunnels
 | 
						|
    eatTicks.hi = 0
 | 
						|
    mz = maze(cj, ci)
 | 
						|
    if (mz AND &h0F) &&= DOT
 | 
						|
        eatTicks.hi = 1
 | 
						|
        maze(cj, ci) = (mz AND &hF0) OR RDOT
 | 
						|
        bcdint @pointsBCD, 10
 | 
						|
        call drawScore
 | 
						|
        livesDots.lo = livesDots.lo - 1
 | 
						|
    elseif mz &&= PILL
 | 
						|
        maze(cj, ci) = (mz AND &hF0) OR RPILL
 | 
						|
        gmode = SCARED_MODE
 | 
						|
        timeTicks = 0
 | 
						|
        bcdint @pointsBCD, 50
 | 
						|
        call drawScore
 | 
						|
        livesDots.lo = livesDots.lo - 1
 | 
						|
    elseif mz &&>= DOOR
 | 
						|
        pxd = oxd : pyd = oyd
 | 
						|
        gosub coordsPucMon
 | 
						|
        mz = maze(cj, ci) 
 | 
						|
    elseif mz &&= TUNNEL
 | 
						|
        px = (129+ORIGIN_X) - px
 | 
						|
    endif
 | 
						|
 | 
						|
    if &((eatTicks.hi) OR (eatTicks.lo))
 | 
						|
        call eatSound
 | 
						|
    endif
 | 
						|
return
 | 
						|
 | 
						|
proc moveGhosts
 | 
						|
    for gidx=0 to NUM_GHOSTS-1
 | 
						|
        gosub getGhostVars
 | 
						|
 | 
						|
        'ghost slowed
 | 
						|
        slowed = 0
 | 
						|
        if (gmode = SCARED_MODE) OR (flags.lo AND 2)
 | 
						|
            if &(timeTicks.lo XOR 255 AND 1)  'if timeTicks.lo AND 1 = 0
 | 
						|
                if &(flags.lo XOR 255 AND 1) 'if flags.lo     AND 1 = 0
 | 
						|
                    slowed = 1
 | 
						|
                    gxd = 0 : gyd = gxd
 | 
						|
                endif
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
        
 | 
						|
        'ghost position
 | 
						|
        gx = gx + gxd
 | 
						|
        gy = gy + gyd
 | 
						|
        
 | 
						|
        'ghost colliding with puc
 | 
						|
        if flags.lo XOR 255 AND 1  'if flags.lo AND 1 = 0
 | 
						|
            if abs(gx - px) &<= 3
 | 
						|
                if abs(gy - py) &<= 3
 | 
						|
                    sprite NoFlip, Erase12x9, gx, gy
 | 
						|
                    if gmode &= SCARED_MODE
 | 
						|
                        sprite NoFlip, PucLt + 2, px, py
 | 
						|
                        call deadSound
 | 
						|
                        flags.lo = flags.lo OR 1 : ghostsFlags(gidx) = flags.lo
 | 
						|
                        bcdint @pointsBCD, bonus : bonus = bonus + bonus
 | 
						|
                        call drawScore
 | 
						|
                    else
 | 
						|
                        flags.hi = 1
 | 
						|
                    endif
 | 
						|
                endif
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
 | 
						|
        'ghost reverses direction
 | 
						|
        if gmode &&= SCARED_MODE
 | 
						|
            if timeTicks.hi &&= 0
 | 
						|
                if timeTicks.lo &&= 1
 | 
						|
                    gxd = -gxd : gyd = -gyd
 | 
						|
                endif
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
 | 
						|
        'skip ghost AI if not centered on a tile
 | 
						|
        mx = gx : my = gy : gosub mod5
 | 
						|
        if mx &&<> 3 then goto moveNextGhost
 | 
						|
        if my &&<> 3 then goto moveNextGhost
 | 
						|
 | 
						|
        'ghost look ahead indices
 | 
						|
        ci = gx + gxd + gxd + gxd + -(ORIGIN_X-6)
 | 
						|
        cj = gy + gyd + gyd + gyd +  (ORIGIN_Y+2)
 | 
						|
        call div5
 | 
						|
 | 
						|
        'ghost dot indices
 | 
						|
        di = ci - gxd - gxd
 | 
						|
        dj = cj - gyd - gyd
 | 
						|
 | 
						|
        'ghost indices
 | 
						|
        gi = ci - gxd
 | 
						|
        gj = cj - gyd
 | 
						|
 | 
						|
        'ghost replaces dot
 | 
						|
        mz = maze(dj, di)
 | 
						|
        if slowed &&= 0 then gosub replaceDot
 | 
						|
 | 
						|
        'ghost leaves home
 | 
						|
        if maze(gj, gi) &&= EXIT
 | 
						|
            if &(flags.lo XOR 255 AND 1)
 | 
						|
                if gmode &&<> SCARED_MODE
 | 
						|
                    gxd = 0 : gyd = -1
 | 
						|
                endif
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
 | 
						|
        'ghost's next target
 | 
						|
        on gidx gosub getBlinkyTarget, getPinkyTarget, getInkyTarget, getClydeTarget
 | 
						|
 | 
						|
        'ghost home
 | 
						|
        if gi &= 13
 | 
						|
            if gj &= 10
 | 
						|
                flags.lo = flags.lo AND &hFE : ghostsFlags(gidx) = flags.lo
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
 | 
						|
        'ghost died, so head home
 | 
						|
        if &(flags.lo AND 1)
 | 
						|
            ti = 12 : tj = 11
 | 
						|
        endif
 | 
						|
 | 
						|
        'ghost walls, tunnel and junctions
 | 
						|
        mz = maze(cj, ci)
 | 
						|
        if mz &= WALL
 | 
						|
            gosub getWallDir
 | 
						|
            call getGhostDir, gdir
 | 
						|
        elseif mz &&= TUNNEL
 | 
						|
            gx = (129+ORIGIN_X) - gx
 | 
						|
        elseif mz &&= SLOW
 | 
						|
            flags.lo = flags.lo OR 2 : ghostsFlags(gidx) = flags.lo
 | 
						|
        else
 | 
						|
            'reset slow flag
 | 
						|
            if &(flags.lo AND 2)
 | 
						|
                flags.lo = flags.lo AND &hFD : ghostsFlags(gidx) = flags.lo
 | 
						|
            endif
 | 
						|
 | 
						|
            'junctions and entering cage whilst dead
 | 
						|
            mz = maze(gj, gi) AND &hF0
 | 
						|
            if &((mz = JUNC) OR (((mz = DOOR) OR (mz = ENTER)) AND (flags.lo AND 1)))
 | 
						|
                gosub getJuncDir
 | 
						|
                call getGhostDir, gdir
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
 | 
						|
moveNextGhost:
 | 
						|
        'ghost vars update when not slowed
 | 
						|
        if slowed &&= 0
 | 
						|
            gosub setGhostVars
 | 
						|
        endif
 | 
						|
    next gidx
 | 
						|
    
 | 
						|
    if gmode &&= SCARED_MODE
 | 
						|
        call scaredSound
 | 
						|
    else
 | 
						|
        call chaseSound
 | 
						|
    endif
 | 
						|
endproc
 | 
						|
 | 
						|
getGhostVars:
 | 
						|
    gx = ghostsX(gidx).lo : gy = ghostsY(gidx).lo
 | 
						|
    gxd = ghostsXd(gidx)  : gyd = ghostsYd(gidx)
 | 
						|
    flags.lo = ghostsFlags(gidx)
 | 
						|
return
 | 
						|
 | 
						|
setGhostVars:
 | 
						|
    ghostsX(gidx) = gx : ghostsY(gidx) = gy
 | 
						|
    ghostsXd(gidx) = gxd : ghostsYd(gidx) = gyd
 | 
						|
return
 | 
						|
 | 
						|
proc getGhostDir, dir
 | 
						|
    dir = dir LSL 2
 | 
						|
    gxd = deek(@GHOST_DIRS + dir + 0)
 | 
						|
    gyd = deek(@GHOST_DIRS + dir + 2)
 | 
						|
endproc
 | 
						|
 | 
						|
setGhostsMode:
 | 
						|
    if &(timeTicks.lo AND &h1F) then return
 | 
						|
 | 
						|
    'roughly every second when running at ~30fps, (mode 2)
 | 
						|
    inc timeTicks.hi
 | 
						|
    
 | 
						|
    'ghost scared mode lasts approx 10 seconds
 | 
						|
    if gmode &&= SCARED_MODE
 | 
						|
        if timeTicks.hi &&> (10 - levelPain.hi)
 | 
						|
            timeTicks.hi = 0
 | 
						|
            bonus = 200
 | 
						|
            gmode = CHASE_MODE
 | 
						|
        endif
 | 
						|
        return
 | 
						|
    endif
 | 
						|
    
 | 
						|
    'ghosts return to chase mode
 | 
						|
    if timeTicks.hi &&> 30
 | 
						|
        timeTicks.hi = 0
 | 
						|
        gmode = CHASE_MODE
 | 
						|
        return
 | 
						|
    endif
 | 
						|
    
 | 
						|
    'ghosts scatter for 10 seconds out of every 30 seconds    
 | 
						|
    if timeTicks.hi &&> (20 + levelPain.hi)
 | 
						|
        gmode = SCATTER_MODE
 | 
						|
    endif
 | 
						|
return
 | 
						|
 | 
						|
'Blinkys target is Puc
 | 
						|
getBlinkyTarget:
 | 
						|
    'save Blinkys indices for inky's targeting
 | 
						|
    bi = gi : bj = gj
 | 
						|
 | 
						|
    if gmode &&= CHASE_MODE    
 | 
						|
        ti = puci : tj = pucj
 | 
						|
        return
 | 
						|
    endif
 | 
						|
    
 | 
						|
    'scatter target
 | 
						|
    ti = 27 : tj = 0
 | 
						|
return
 | 
						|
 | 
						|
'Pinkys target is 4 tiles ahead of Pucs current direction
 | 
						|
getPinkyTarget:
 | 
						|
    if gmode &&= CHASE_MODE
 | 
						|
        ti = puci + pxd + pxd + pxd + pxd
 | 
						|
        tj = pucj + pyd + pyd + pyd + pyd
 | 
						|
        return
 | 
						|
    endif
 | 
						|
    
 | 
						|
    'scatter target
 | 
						|
    ti = 0 : tj = 0
 | 
						|
return
 | 
						|
 | 
						|
'Inkys target is (vector from Blinky to (Puc + 2)) * 2
 | 
						|
getInkyTarget:
 | 
						|
    if gmode &&= CHASE_MODE
 | 
						|
        ti = puci + pxd + pxd
 | 
						|
        tj = pucj + pyd + pyd
 | 
						|
        zi = ti - bi : zj = tj - bj 'vector from Blinky to (Puc + 2)
 | 
						|
        ti = ti + zi
 | 
						|
        tj = tj + zj
 | 
						|
        return
 | 
						|
    endif
 | 
						|
    
 | 
						|
    'scatter target
 | 
						|
    ti = 27 : tj = 23
 | 
						|
return
 | 
						|
 | 
						|
'Clyde acts like Blinky until he gets within 8 tiles, then he scatters
 | 
						|
getClydeTarget:
 | 
						|
    if gmode &= CHASE_MODE
 | 
						|
        if abs(puci - gi) + abs(pucj - gj) &> 8 'taxi-cab distance
 | 
						|
            ti = puci : tj = pucj
 | 
						|
            return
 | 
						|
        endif
 | 
						|
    endif
 | 
						|
    
 | 
						|
    'scatter target
 | 
						|
    ti = 0 : tj = 23
 | 
						|
return
 | 
						|
 | 
						|
getJuncDir:
 | 
						|
    if (gxd)
 | 
						|
        if (abs(tj - gj))
 | 
						|
            if maze(gj - 1, gi) &&<> WALL
 | 
						|
                gdir = 3 : if tj &&< gj then return
 | 
						|
            endif
 | 
						|
            if maze(gj + 1, gi) &&<> WALL
 | 
						|
                gdir = 1 : if tj &&> gj then return
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
    else
 | 
						|
        if (abs(ti - gi))
 | 
						|
            if maze(gj, gi - 1) &&<> WALL
 | 
						|
                gdir = 2 : if ti &&< gi then return
 | 
						|
            endif
 | 
						|
            if maze(gj, gi + 1) &&<> WALL
 | 
						|
                gdir = 0 : if ti &&> gi then return
 | 
						|
            endif
 | 
						|
        endif
 | 
						|
    endif
 | 
						|
 | 
						|
    if gxd &&= 1
 | 
						|
        gdir = 0
 | 
						|
    elseif gxd &&= -1
 | 
						|
        gdir = 2
 | 
						|
    elseif gyd &&= 1
 | 
						|
        gdir = 1
 | 
						|
    else
 | 
						|
        gdir = 3
 | 
						|
    endif
 | 
						|
return
 | 
						|
 | 
						|
getWallDir:
 | 
						|
    if (gxd)
 | 
						|
        if maze(gj - 1, gi) &&<> WALL
 | 
						|
            gdir = 3 : if tj &&< gj then return
 | 
						|
        endif
 | 
						|
        if maze(gj + 1, gi) &&<> WALL then gdir = 1
 | 
						|
    else
 | 
						|
        if maze(gj, gi - 1) &<> WALL
 | 
						|
            gdir = 2 : if ti &&< gi then return
 | 
						|
        endif
 | 
						|
        if maze(gj, gi + 1) &&<> WALL then gdir = 0
 | 
						|
    endif
 | 
						|
return
 | 
						|
 | 
						|
replaceDot:
 | 
						|
    if (mz AND &h0F) &&= DOT
 | 
						|
        di = (di LSL 2) + di + ORIGIN_X   'di = di*5 + 12
 | 
						|
        dj = (dj LSL 2) + dj + ORIGIN_Y   'dj = dj*5 + 2
 | 
						|
        poke ((dj + 8) LSL 8) + di, &h2B  'convert dj, di to vram address
 | 
						|
    endif
 | 
						|
return
 | 
						|
 | 
						|
coordsPucMon:
 | 
						|
    ci = px + pxd + pxd + pxd + -(ORIGIN_X-6)
 | 
						|
    cj = py + pyd + pyd + pyd +  (ORIGIN_Y+2)
 | 
						|
    call div5
 | 
						|
return
 | 
						|
 | 
						|
handleInput:
 | 
						|
    oxd = pxd : oyd = pyd
 | 
						|
    mx = px : my = py : gosub mod5
 | 
						|
    gosub get("BUTTON_STATE")
 | 
						|
return
 | 
						|
 | 
						|
253: if my &&= 3 then pyd = 0 : pxd = pyd-1
 | 
						|
     return
 | 
						|
 | 
						|
254: if my &= 3  then pyd = 0 : pxd = pyd+1
 | 
						|
     return
 | 
						|
 | 
						|
247: if mx &&= 3 then pxd = 0 : pyd = pxd-1
 | 
						|
     return
 | 
						|
 | 
						|
251: if mx &&= 3 then pxd = 0 : pyd = pxd+1
 | 
						|
     return
 | 
						|
 | 
						|
proc drawScore
 | 
						|
    local i, char
 | 
						|
    
 | 
						|
    bcdadd @pointsBCD, @scoreBCD, SCORE_LEN
 | 
						|
    char = SCORE_X+ORIGIN_X
 | 
						|
    for i=0 to SCORE_LEN-1
 | 
						|
        sprite NoFlip, Digit + peek(@scoreBCD + SCORE_LEN-1 - i), char, SCORE_Y+ORIGIN_Y
 | 
						|
        char = char + 6
 | 
						|
    next i
 | 
						|
    
 | 
						|
    'bcdcmp requires bcd addrs to point to msd
 | 
						|
    if bcdcmp(@scoreBCD+(SCORE_LEN-1), @highBCD+(SCORE_LEN-1), SCORE_LEN) &&= 1
 | 
						|
        bcdcpy @scoreBCD, @highBCD, SCORE_LEN
 | 
						|
        call drawHigh
 | 
						|
    endif
 | 
						|
endproc
 | 
						|
 | 
						|
proc drawHigh
 | 
						|
    local i, char
 | 
						|
    
 | 
						|
    char = HIGH_X+ORIGIN_X
 | 
						|
    for i=0 to SCORE_LEN-1
 | 
						|
        sprite NoFlip, Digit + peek(@highBCD + SCORE_LEN-1 - i), char, HIGH_Y+ORIGIN_Y
 | 
						|
        char = char + 6
 | 
						|
    next i
 | 
						|
endproc
 | 
						|
 | 
						|
proc drawLevel
 | 
						|
    local i, char
 | 
						|
    
 | 
						|
    sprite NoFlip, Level, LEVEL_X+ORIGIN_X, LEVEL_Y+ORIGIN_Y
 | 
						|
    char = LEVEL_X+ORIGIN_X + 6
 | 
						|
    for i=0 &to LEVEL_LEN-4
 | 
						|
        sprite NoFlip, Digit + peek(@levelBCD + LEVEL_LEN-4 - i), char, LEVEL_Y+ORIGIN_Y
 | 
						|
        char = char + 6
 | 
						|
    next i
 | 
						|
endproc
 | 
						|
 | 
						|
proc drawLives
 | 
						|
    local i, puc
 | 
						|
 | 
						|
    i = 1    
 | 
						|
    puc = LIFE_X+ORIGIN_X
 | 
						|
 | 
						|
    while i &&<= livesDots.hi
 | 
						|
        sprite NoFlip, Life, puc, LIFE_Y+ORIGIN_Y
 | 
						|
        puc = puc + 6
 | 
						|
        inc i
 | 
						|
    wend
 | 
						|
    
 | 
						|
    while i &&<= MAX_LIVES
 | 
						|
        sprite NoFlip, Erase6x6, puc, LIFE_Y+ORIGIN_Y
 | 
						|
        puc = puc + 6
 | 
						|
        inc i
 | 
						|
    wend
 | 
						|
endproc
 | 
						|
 | 
						|
dim enotes%(4) = 58, 61, 63, 60, 57
 | 
						|
proc eatSound
 | 
						|
    local n
 | 
						|
 | 
						|
    n = get("MIDI_NOTE", peek(@enotes + eatTicks.lo))
 | 
						|
    sound on, 2, n, 63, 1
 | 
						|
    inc eatTicks.lo
 | 
						|
    if eatTicks.lo &&>= 4
 | 
						|
        eatTicks.lo = 0
 | 
						|
        sound off, 2
 | 
						|
    endif
 | 
						|
endproc
 | 
						|
 | 
						|
proc scaredSound
 | 
						|
    sound on, 1, scaredTicks, 63, 0
 | 
						|
    scaredTicks = scaredTicks - 650
 | 
						|
    if scaredTicks &&<= 5000 then scaredTicks = 10000
 | 
						|
    set SOUND_TIMER, 3
 | 
						|
endproc
 | 
						|
 | 
						|
dim gnotes%(15) = 69, 71, 72, 74, 76, 77, 79, 81, 83, 81, 79, 77, 76, 74, 72, 71
 | 
						|
proc chaseSound
 | 
						|
    local n, v, f
 | 
						|
    
 | 
						|
    'volume of chase sound gets higher as blinky gets closer
 | 
						|
    'v = abs(puci - bi) + abs(pucj - bj)
 | 
						|
    
 | 
						|
    'frequency escalates as dots decrease
 | 
						|
    f = ((212 - livesDots.lo) LSR 4) LSL 8
 | 
						|
    
 | 
						|
    n = get("MIDI_NOTE", peek(@gnotes + (timeTicks.lo AND 15)))
 | 
						|
    sound on, 1, n + f, 63, 0 '48-v, 0
 | 
						|
    set SOUND_TIMER, 3
 | 
						|
endproc
 | 
						|
 | 
						|
proc deadSound
 | 
						|
    local i, v, f
 | 
						|
    
 | 
						|
    f = 1000
 | 
						|
    sound off, 1
 | 
						|
    repeat
 | 
						|
        for v=1 &to 62 step 2
 | 
						|
            sound on, 2, f, v, 2
 | 
						|
            set SOUND_TIMER, 1
 | 
						|
        next v
 | 
						|
        for v=63 &downto 1 step 2
 | 
						|
            sound on, 2, f, v, 2
 | 
						|
            set SOUND_TIMER, 1
 | 
						|
        next v
 | 
						|
        f = f + 400
 | 
						|
    until f &>= 10000
 | 
						|
    sound off, 2
 | 
						|
endproc
 | 
						|
 | 
						|
dim dnotes%(15) = 75, 74, 73, 72, 71, 70, 69, 68, 67, 68, 69, 70, 71, 72, 73, 74
 | 
						|
proc drawDeath
 | 
						|
    local i, j, n, f
 | 
						|
 | 
						|
    call drawLives
 | 
						|
 | 
						|
    f = 0
 | 
						|
    sound off, 1
 | 
						|
    for i=0 to 128
 | 
						|
        n = get("MIDI_NOTE", peek(@dnotes + (i AND 15)))
 | 
						|
        sound on, 2, n - f, 63 - (i LSR 2), 3
 | 
						|
        set SOUND_TIMER, 1
 | 
						|
        for j=1 &to 250 : next j
 | 
						|
        if ((i LSR 3) AND 1) &&= 0
 | 
						|
            sprite NoFlip, Erase12x9, px, py
 | 
						|
        else
 | 
						|
            sprite NoFlip, PucLt + 2, px, py
 | 
						|
        endif
 | 
						|
        if (i AND 7) &&= 0
 | 
						|
            f = f + 200
 | 
						|
        endif
 | 
						|
    next i
 | 
						|
    sound off, 2
 | 
						|
endproc
 | 
						|
 | 
						|
proc drawMaze
 | 
						|
    local i
 | 
						|
    
 | 
						|
    set FG_COLOUR, &h30
 | 
						|
    for i=0 &to (NUM_MAZE_PIECES*2 - 2) step 4
 | 
						|
        set CURSOR_XY, deek(@MAZE_PIECES + i)     : polyR deek(@MAZE_ADDRS + i)
 | 
						|
        set CURSOR_XY, deek(@MAZE_PIECES + i + 2) : polyR deek(@MAZE_ADDRS + i + 2), FLIPX
 | 
						|
    next i
 | 
						|
    
 | 
						|
    set FG_COLOUR, 0 '&h2B
 | 
						|
endproc
 | 
						|
 | 
						|
'redraw=0 is refresh, redraw=1 is redraw
 | 
						|
proc drawDots, redraw
 | 
						|
    set FG_COLOUR, &h2B
 | 
						|
    for cj=0 to 23
 | 
						|
        for ci=0 to 27
 | 
						|
            mz = maze(cj, ci)
 | 
						|
            if redraw &&= 1
 | 
						|
                if (mz AND &h0F) &&= RDOT         'reset dots
 | 
						|
                    mz = (mz AND &hF0) OR DOT
 | 
						|
                elseif (mz AND &h0F) &&= RPILL   'reset pills
 | 
						|
                    mz = (mz AND &hF0) OR PILL
 | 
						|
                endif
 | 
						|
                maze(cj, ci) = mz
 | 
						|
            endif
 | 
						|
            if (mz AND &h0F) &= DOT
 | 
						|
                pset (ci LSL 2) + ci + ORIGIN_X, (cj LSL 2) + cj + ORIGIN_Y
 | 
						|
            endif
 | 
						|
        next ci
 | 
						|
    next cj
 | 
						|
endproc
 | 
						|
 | 
						|
nextLevel:
 | 
						|
    if levelPain.lo &&= 99 then poke &h0101, &h40  'level100 easter egg, good luck once you get to level 100!
 | 
						|
    inc levelPain.lo
 | 
						|
    inc levelPain.hi
 | 
						|
goto start
 | 
						|
 | 
						|
proc initVars
 | 
						|
    local i, corner
 | 
						|
    
 | 
						|
    'initialises all variables, (to zero), starting at @timeTicks
 | 
						|
    init vars @timeTicks
 | 
						|
    
 | 
						|
    px = (63+ORIGIN_X)
 | 
						|
    py = (86+ORIGIN_Y)
 | 
						|
    pxd = -1
 | 
						|
    bonus = 200
 | 
						|
    scaredTicks = 10000
 | 
						|
    
 | 
						|
    'reset ghost vars
 | 
						|
    corner = (rnd(0) AND 3)
 | 
						|
    for gidx=0 &to (NUM_GHOSTS - 1)
 | 
						|
        if gidx &&= 0
 | 
						|
            i = gidx LSL 1                          'blinky is always reset to the same position
 | 
						|
        else
 | 
						|
            i = (((corner + gidx) AND 3) + 1) LSL 1 'pinky, inky and clyde are reset to 1 of 4 random cage corners
 | 
						|
        endif
 | 
						|
        gx = deek(@ghostsXr + i)
 | 
						|
        gy = deek(@ghostsYr + i)
 | 
						|
        gxd = -1                                    'ghosts initially move left
 | 
						|
        gosub setGhostVars
 | 
						|
    next gidx
 | 
						|
endproc
 | 
						|
 | 
						|
startLevel:
 | 
						|
    sprite NoFlip, Erase12x9, px, py
 | 
						|
    livesDots.lo = 212
 | 
						|
    bcdint @pointsBCD, 0
 | 
						|
    bcdint @levelBCD, levelPain.lo
 | 
						|
    if levelPain.hi &&> 7 then levelPain.hi = 7
 | 
						|
    
 | 
						|
    call drawDots, 1 'redraw
 | 
						|
    call drawScore
 | 
						|
    call drawHigh
 | 
						|
    call drawLives
 | 
						|
    call drawLevel
 | 
						|
    
 | 
						|
    if levelPain.lo &&= 0
 | 
						|
        play music, MUSIC_INTRO, 2
 | 
						|
    else
 | 
						|
        wait 120
 | 
						|
    endif
 | 
						|
    'repeat
 | 
						|
    'until get("BUTTON_STATE") &<> 255
 | 
						|
return
 | 
						|
 | 
						|
resetLevel:
 | 
						|
    levelPain = 0+0*256
 | 
						|
    livesDots = 3*256
 | 
						|
    bcdint @scoreBCD, 0
 | 
						|
    poke &h0101, 0
 | 
						|
return
 | 
						|
 | 
						|
initSystem:
 | 
						|
    'audio fix for ROMv5a
 | 
						|
    poke &h21, peek(&h21) OR 3
 | 
						|
 | 
						|
    px = (63+ORIGIN_X) : py = (86+ORIGIN_Y)
 | 
						|
    
 | 
						|
    'scanline 0 and 1 are replaced with scanline 2, (hide scanlines 0 and 1)
 | 
						|
    poke &h0100, &h0A
 | 
						|
    poke &h0102, &h0A
 | 
						|
    
 | 
						|
    'scanline 118 and 119 are replaced with scanline 117, (hide scanlines 118 and 119)
 | 
						|
    poke &h01EC, &h7D
 | 
						|
    poke &h01EE, &h7D
 | 
						|
    
 | 
						|
    'use cls rect as we are using hidden parts of VRAM for code and data
 | 
						|
    mode 2
 | 
						|
    set FGBG_COLOUR, 0
 | 
						|
    cls &h0A00, 160, 116
 | 
						|
return
 | 
						|
 | 
						|
proc waitArbitrary
 | 
						|
    local i
 | 
						|
    
 | 
						|
    for i=0 &to 60
 | 
						|
    next i
 | 
						|
return
 | 
						|
 | 
						|
'proc drawGrid
 | 
						|
'    local i
 | 
						|
'
 | 
						|
'    set FG_COLOUR, &h15
 | 
						|
'    for i=ORIGIN_X to 140+ORIGIN_X step 5
 | 
						|
'        line i, 0, i, 119
 | 
						|
'    next i
 | 
						|
'    for i=ORIGIN_Y to 115+ORIGIN_Y step 5
 | 
						|
'        line 0, i, 159, i
 | 
						|
'    next i
 | 
						|
'endproc
 | 
						|
 | 
						|
'proc drawCells
 | 
						|
'    local i
 | 
						|
'
 | 
						|
'    set FG_COLOUR, &h15
 | 
						|
'    for i=10 to 150 step 5
 | 
						|
'        line i, 0, i, 119
 | 
						|
'    next i
 | 
						|
'    for i=0 to 115 step 5
 | 
						|
'        line 0, i, 159, i
 | 
						|
'    next i
 | 
						|
'endproc |