gigatron/rom/Contrib/at67/gbas/games/PucMon/PucMon_ROMv5a.gbas
2025-01-28 19:17:01 +03:00

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