; ======================================================================
; RAIDERS OF THE LOST AISLE — an isometric adventure for the 48K
; Spectrum. It is Christmas Eve. The shop closes at midnight. Somewhere
; in this department store is the one toy your kid actually wants.
; Original game, code and art. From https://thespeccy.com
;
; Keys: Q = up, A = down, O = left, P = right (isometric axes),
;       S = start / play again.
;
; Build:  pasmo --tapbas raiders.asm raiders.tap
; ======================================================================

        org 32768

ORIGCOL: equ 16                 ; screen column (cells) of tile (0,0)
ORIGROW: equ 3                  ; screen row    (cells) of tile (0,0)
GRIDW:   equ 5                  ; tiles per side (u and v each 0..4)

TOPEN:   equ 0                  ; tile types
TWALL:   equ 1
TTOY:    equ 2
TTILL:   equ 3
TDOOR:   equ 5

; ======================================================================
start:
        call newtitle
mainloop:
        halt
        ld a,(state)
        cp 1
        jr z,frame_play
        or a
        jr z,frame_title
        call frame_end
        jr mainloop

frame_title:
        call key_s
        jr nz,mainloop
        call newgame
        jr mainloop

frame_play:
        ld hl,tick
        inc (hl)
        call movehero
        call moveguard
        call checkcatch
        call ticktimer
        call hud
        jr mainloop

frame_end:
        call key_s
        ret nz
        jp newgame

; ----------------------------------------------------------------------
; state setup
; ----------------------------------------------------------------------
newtitle:
        xor a
        ld (state),a
        call clearall
        ld b,2
        ld c,3
        ld de,t_title
        ld a,0x46
        call print
        ld b,4
        ld c,3
        ld de,t_sub
        ld a,0x45
        call print
        ld b,12
        ld c,8
        ld de,t_keys1
        ld a,0x47
        call print
        ld b,13
        ld c,2
        ld de,t_keys2
        ld a,0x47
        call print
        ld b,16
        ld c,8
        ld de,t_press
        ld a,0xC6
        call print
        ret

newgame:
        ld a,1
        ld (state),a
        xor a
        ld (toyflag),a
        ld (tick),a
        ld (timerdiv),a
        ld a,90
        ld (timeleft),a
        ld a,2
        ld (guardv),a
        ld a,1
        ld (guarddir),a
        call enterroom0
        ret

; enter room 0 (index 0,1,2 in (room) var) — separate label so newgame
; can call it directly without touching room number
enterroom0:
        xor a
        ld (room),a
        jp enterroom

; ----------------------------------------------------------------------
; room management
; ----------------------------------------------------------------------
; move hero to the current room's entry point and redraw everything
enterroom:
        ld a,(room)
        add a,a                   ; A = room*2 (each entry is 2 bytes)
        ld e,a
        ld d,0
        ld hl,room_entry
        add hl,de
        ld a,(hl)
        ld (herou),a
        inc hl
        ld a,(hl)
        ld (herov),a
        call drawroom
        ld de,t_go
        call message
        ret

; redraw the whole room: floor, obstacles, guard (if any), hero
drawroom:
        call clearall
        ld a,(room)
        ld hl,room_data
        ld d,0
        ld e,a
        ; hl += room*25
        push hl
        ld a,e
        ld b,25
        ld e,0
rm_mul: or a
        jr z,rm_mul_done
        push hl
        ld hl,25
        add hl,de
        ex de,hl
        pop hl
        dec a
        jr rm_mul
rm_mul_done:
        pop hl
        add hl,de
        ld (roomptr),hl
        ; draw all 25 tiles
        xor a
        ld (du),a
dr_u:   xor a
        ld (dv),a
dr_v:   ld hl,(roomptr)
        ld a,(hl)
        inc hl
        ld (roomptr),hl
        call drawtile
        ld a,(dv)
        inc a
        ld (dv),a
        cp GRIDW
        jr nz,dr_v
        ld a,(du)
        inc a
        ld (du),a
        cp GRIDW
        jr nz,dr_u
        ; guard only patrols in room 1 (the escalator corridor)
        ld a,(room)
        cp 1
        jr nz,dr_nog
        ld a,2
        ld (guardu),a
        call drawguard
dr_nog: call drawhero
        ret

; draw the tile at (du,dv) with type A
drawtile:
        push af
        ld a,(du)
        ld c,a
        ld a,(dv)
        ld d,a
        call project             ; -> B=row,C=col (cell units)
        pop af
        cp TWALL
        jr z,dt_wall
        cp TTOY
        jr z,dt_toy
        cp TTILL
        jr z,dt_till
        cp TDOOR
        jr z,dt_door
        ; plain floor: chequered by (du+dv) parity
        ld a,(du)
        ld e,a
        ld a,(dv)
        add a,e
        and 1
        jr z,dt_floorA
        ld a,0x45                ; cyan
        jr dt_putfloor
dt_floorA:
        ld a,0x41                ; blue
        jr dt_putfloor
dt_toy:
        ld a,0x46                ; bright yellow
        jr dt_putfloor
dt_till:
        ld a,0x44                ; green
        jr dt_putfloor
dt_door:
        ld a,0xC5                ; flashing cyan
        jr dt_putfloor
dt_wall:
        ld a,0x42                 ; red-ish shelving
        ld (tileattr),a
        ld de,solidcell
        call putcell
        push bc
        call attradr_rc
        ld a,(tileattr)
        ld (hl),a
        pop bc
        inc c
        push bc
        call putcell
        call attradr_rc
        ld a,(tileattr)
        ld (hl),a
        pop bc
        ; second row, one cell higher (the "shelf" sticks up)
        dec b
        push bc
        ld de,solidcell
        call putcell
        call attradr_rc
        ld a,(tileattr)
        ld (hl),a
        pop bc
        inc c
        call putcell
        call attradr_rc
        ld a,(tileattr)
        ld (hl),a
        ret
dt_putfloor:
        ld (tileattr),a
        ld de,solidcell
        call putcell
        push bc
        call attradr_rc
        ld a,(tileattr)
        ld (hl),a
        pop bc
        inc c
        push bc
        call putcell
        call attradr_rc
        ld a,(tileattr)
        ld (hl),a
        pop bc
        ret

; ----------------------------------------------------------------------
; projection: C=u, D=v -> B=row,C=col (cell units), all 8-bit safe
; col = ORIGCOL + (u-v)*2 ; row = ORIGROW + (u+v)
; ----------------------------------------------------------------------
project:
        ld a,c
        sub d
        add a,a                   ; (u-v)*2 ; small signed range, fits
        add a,ORIGCOL
        ld e,a                    ; E = col
        ld a,c
        add a,d
        add a,ORIGROW
        ld b,a                    ; B = row
        ld c,e
        ret

; ----------------------------------------------------------------------
; hero
; ----------------------------------------------------------------------
drawhero:
        ld a,(herou)
        ld c,a
        ld a,(herov)
        ld d,a
        call project
        dec b
        ld de,hero_spr
        call blit16
        ld a,(herou)
        ld c,a
        ld a,(herov)
        ld d,a
        call project
        dec b
        call attradr_rc
        ld a,0x46
        ld (hl),a
        inc hl
        ld (hl),a
        ld de,31
        add hl,de
        ld a,0x47
        ld (hl),a
        inc hl
        ld (hl),a
        ret

erasehero:
        ld a,(herou)
        ld c,a
        ld a,(herov)
        ld d,a
        call tileat                ; -> A = tile type at (herou,herov)
        call drawtile
        ret

; look up tile type at (du_reg=C,dv_reg=D) in the current room, -> A
; also leaves (du),(dv) set to that coordinate (drawtile needs them)
tileat:
        ld a,c
        ld (du),a
        ld a,d
        ld (dv),a
        ; HL = room_data + room*25 + du*5 + dv
        ld hl,room_data
        ld a,(room)
        ld b,a
tla_room: or a
        jr z,tla_room_done
        push af
        ld de,25
        add hl,de
        pop af
        dec a
        jr tla_room
tla_room_done:
        ld a,(du)
        ld b,a
tla_row: or a
        jr z,tla_row_done
        push af
        ld de,5
        add hl,de
        pop af
        dec a
        jr tla_row
tla_row_done:
        ld a,(dv)
        ld e,a
        ld d,0
        add hl,de
        ld a,(hl)
        ret

; move the hero if the target tile is walkable; handles toy/till/door
movehero:
        ld a,(tick)
        and 3
        ret nz                     ; move on exactly 1 frame in 4
        ; Q = up  -> v-1
        ld a,0xFB
        in a,(0xFE)
        rra
        jr c,mv_notq
        ld a,(herov)
        or a
        jr z,mv_notq
        dec a
        call trymove_v
mv_notq:
        ; A = down -> v+1
        ld a,0xFD
        in a,(0xFE)
        rra
        jr c,mv_nota
        ld a,(herov)
        cp GRIDW-1
        jr nc,mv_nota
        inc a
        call trymove_v
mv_nota:
        ; O = left -> u-1
        ld a,0xDF
        in a,(0xFE)
        rra
        rra
        jr c,mv_noto
        ld a,(herou)
        or a
        jr z,mv_noto
        dec a
        call trymove_u
mv_noto:
        ; P = right -> u+1
        ld a,0xDF
        in a,(0xFE)
        rra
        jr c,mv_notp
        ld a,(herou)
        cp GRIDW-1
        jr nc,mv_notp
        inc a
        call trymove_u
mv_notp:
        ret

; A = candidate new V; check walkable, move, handle tile effect
trymove_v:
        ld (candv),a
        ld a,(herou)
        ld c,a
        ld a,(candv)
        ld d,a
        call tileat
        cp TWALL
        ret z
        call erasehero
        ld a,(candv)
        ld (herov),a
        jp aftermove

; A = candidate new U
trymove_u:
        ld (candu),a
        ld a,(candu)
        ld c,a
        ld a,(herov)
        ld d,a
        call tileat
        cp TWALL
        ret z
        call erasehero
        ld a,(candu)
        ld (herou),a
        jp aftermove

aftermove:
        ld a,(herou)
        ld c,a
        ld a,(herov)
        ld d,a
        call tileat                ; A = tile hero now stands on
        cp TTOY
        jr z,am_toy
        cp TTILL
        jr z,am_till
        cp TDOOR
        jr z,am_door
        call drawhero
        ret
am_toy: ld a,1
        ld (toyflag),a
        ; turn the toy tile into plain floor so it doesn't re-trigger
        ld a,(herou)
        ld hl,room_data
        ld d,0
        ld e,a
        push de
        pop de
        call zerotile
        call drawhero
        ld de,t_gottoy
        call message
        ld b,60
        ld c,30
        call beep
        ret
am_till:
        ld a,(toyflag)
        or a
        jr z,am_till_notoy
        call drawhero
        jp gamewin
am_till_notoy:
        call drawhero
        ld de,t_needtoy
        call message
        ret
am_door:
        call drawhero
        ld a,(room)
        inc a
        cp 3
        jp z,gamewin              ; safety net; room 2's door doesn't exist
        ld (room),a
        jp enterroom

; overwrite the tile the hero is standing on with plain floor (used
; once the toy is collected, so walking back over it does nothing)
zerotile:
        ld a,(herou)
        ld b,a
        ld hl,room_data
        ld a,(room)
        ld e,a
        ld d,0
zt_mul: or a
        jr z,zt_muldone
        push af
        ld de,25
        add hl,de
        pop af
        dec a
        jr zt_mul
zt_muldone:
        ld a,b
zt_row: or a
        jr z,zt_rowdone
        push af
        ld de,5
        add hl,de
        pop af
        dec a
        jr zt_row
zt_rowdone:
        ld a,(herov)
        ld e,a
        ld d,0
        add hl,de
        ld (hl),TOPEN
        ret

; ----------------------------------------------------------------------
; guard (room 1 only): oscillates V between 1 and 3 at fixed U=2
; ----------------------------------------------------------------------
moveguard:
        ld a,(room)
        cp 1
        ret nz
        ld a,(tick)
        and 7
        ret nz
        call eraseguard
        ld a,(guarddir)
        or a
        jr z,mg_down
        ld a,(guardv)
        inc a
        ld (guardv),a
        cp 3
        jr nz,mg_draw
        xor a
        ld (guarddir),a
        jr mg_draw
mg_down:
        ld a,(guardv)
        dec a
        ld (guardv),a
        cp 1
        jr nz,mg_draw
        ld a,1
        ld (guarddir),a
mg_draw:
        call drawguard
        ret

drawguard:
        ld a,(guardu)
        ld c,a
        ld a,(guardv)
        ld d,a
        call project
        dec b
        ld de,guard_spr
        call blit16
        ld a,(guardu)
        ld c,a
        ld a,(guardv)
        ld d,a
        call project
        dec b
        call attradr_rc
        ld a,0x41
        ld (hl),a
        inc hl
        ld (hl),a
        ld de,31
        add hl,de
        ld a,0x47
        ld (hl),a
        inc hl
        ld (hl),a
        ret

eraseguard:
        ld a,(guardu)
        ld c,a
        ld a,(guardv)
        ld d,a
        call tileat
        call drawtile
        ret

; caught? hero and guard sharing a tile in room 1 costs time and
; sends the hero back to this room's entry point
checkcatch:
        ld a,(room)
        cp 1
        ret nz
        ; the entry tile is always safe: without this, a hero just
        ; reset there by a catch could get caught again on the very
        ; next frame if the guard hasn't moved off that tile yet,
        ; draining time in an uncontrolled loop rather than giving a
        ; moment to react
        ld a,(herou)
        cp 2
        ret nz
        ld a,(herov)
        or a
        ret z
        ld a,(herou)
        ld hl,guardu
        cp (hl)
        ret nz
        ld a,(herov)
        ld hl,guardv
        cp (hl)
        ret nz
        ; caught!
        ld a,2
        out (254),a
        ld b,150
        ld c,15
        call beep
        xor a
        out (254),a
        ld a,(timeleft)
        sub 10
        jr nc,cc1
        xor a
cc1:    ld (timeleft),a
        call erasehero
        call enterroom
        ld de,t_caught
        call message
        ld a,(timeleft)
        or a
        jp z,gamelose
        ret

; ----------------------------------------------------------------------
; timer, HUD, win/lose
; ----------------------------------------------------------------------
ticktimer:
        ld hl,timerdiv
        inc (hl)
        ld a,(hl)
        cp 50
        ret c
        xor a
        ld (timerdiv),a
        ld a,(timeleft)
        or a
        ret z
        dec a
        ld (timeleft),a
        or a
        jp z,gamelose
        ret

hud:
        ld a,(tick)
        and 15
        ret nz
        ld b,0
        ld c,0
        ld de,t_hudtoy
        ld a,0x47
        call print
        ld a,(toyflag)
        or a
        ld de,t_yes
        jr nz,hud1
        ld de,t_no
hud1:   ld b,0
        ld c,5
        ld a,0x46
        call print
        ld b,0
        ld c,20
        ld de,t_hudtime
        ld a,0x47
        call print
        ld a,(timeleft)
        ld l,a
        ld h,0
        ld b,0
        ld c,26
        call prnum
        ret

prnum:  push bc
        ld de,100
        call prdig
        ld de,10
        call prdig
        ld de,1
        pop bc
        ; fall through
prdig:  ld a,'0'-1
pd1:    inc a
        or a
        sbc hl,de
        jr nc,pd1
        add hl,de
        push hl
        push de
        call printch
        inc c
        pop de
        pop hl
        ret

gamewin:
        ld a,2
        ld (state),a
        call clearall
        ld b,10
        ld c,3
        ld de,t_win1
        ld a,0xC6
        call print
        ld b,12
        ld c,6
        ld de,t_win2
        ld a,0x45
        call print
        ld de,t_press
        call message
        ld b,50
        ld c,25
        call beep
        ld b,50
        ld c,18
        call beep
        ld b,50
        ld c,12
        call beep
        ret

gamelose:
        ld a,2
        ld (state),a
        call clearall
        ld b,10
        ld c,7
        ld de,t_lose1
        ld a,0xC2                 ; flashing bright red ink on black paper
        call print
        ld b,12
        ld c,6
        ld de,t_lose2
        ld a,0x47
        call print
        ld de,t_press
        call message
        ld b,40
        ld c,60
        call beep
        ld b,30
        ld c,90
        call beep
        ret

; ----------------------------------------------------------------------
; text
; ----------------------------------------------------------------------
print:
        ld (textattr),a
pr1:    ld a,(de)
        or a
        ret z
        push de
        call printch
        pop de
        inc de
        inc c
        jr pr1

printch:
        push bc
        sub 32
        ld l,a
        ld h,0
        add hl,hl
        add hl,hl
        add hl,hl
        ld de,font
        add hl,de
        ex de,hl
        call scradr_rc
        ld b,8
pch1:   ld a,(de)
        ld (hl),a
        inc de
        inc h
        djnz pch1
        pop bc
        push bc
        call attradr_rc
        ld a,(textattr)
        ld (hl),a
        pop bc
        ret

message:
        push de
        ld b,21
        ld c,0
mg1:    ld de,z_cell
        call putcell
        inc c
        ld a,c
        cp 32
        jr nz,mg1
        pop de
        ld b,21
        ld c,1
        ld a,0x45
        jp print

; ----------------------------------------------------------------------
; low-level addressing / blitting / io / rand
; ----------------------------------------------------------------------
scradr_rc:
        ld a,b
        and 0x18
        or 0x40
        ld h,a
        ld a,b
        and 7
        rrca
        rrca
        rrca
        or c
        ld l,a
        ret

attradr_rc:
        ld a,b
        rrca
        rrca
        rrca
        and 3
        add a,0x58
        ld h,a
        ld a,b
        and 7
        rrca
        rrca
        rrca
        or c
        ld l,a
        ret

; copy 8 bytes DE -> cell at row B col C; preserves B,C
putcell:
        push bc
        call scradr_rc
        ld b,8
put1:   ld a,(de)
        ld (hl),a
        inc de
        inc h
        djnz put1
        pop bc
        ret

; draw 16x16 sprite DE at cell rows B,B+1, col C
blit16:
        push bc
        call scradr_rc
        call cell8
        pop bc
        inc b
        call scradr_rc
cell8:
        ld b,8
c81:    push hl
        ld a,(de)
        ld (hl),a
        inc de
        inc l
        ld a,(de)
        ld (hl),a
        inc de
        pop hl
        inc h
        djnz c81
        ret

; wipe the whole bitmap and attribute plane
clearall:
        ld hl,16384
        ld de,16385
        ld bc,6143
        ld (hl),0
        ldir
        ld hl,22528
        ld de,22529
        ld bc,767
        ld (hl),0
        ldir
        xor a
        out (254),a
        ret

key_s:                       ; Z set if S is pressed
        ld a,0xFD
        in a,(0xFE)
        and 2
        ret

; beep: B = half-cycles, C = pitch delay
beep:
        ld a,0x10
bp1:    out (254),a
        xor 0x10
        push bc
bp2:    dec c
        jr nz,bp2
        pop bc
        djnz bp1
        xor a
        out (254),a
        ret

; ----------------------------------------------------------------------
; data
; ----------------------------------------------------------------------
state:    defb 0
tick:     defb 0
room:     defb 0
herou:    defb 4
herov:    defb 0
candu:    defb 0
candv:    defb 0
du:       defb 0
dv:       defb 0
roomptr:  defw 0
toyflag:  defb 0
timeleft: defb 90
timerdiv: defb 0
guardu:   defb 2
guardv:   defb 2
guarddir: defb 1
textattr: defb 0x47
tileattr: defb 0

; entry point (u,v) for each room
room_entry:
        defb 4,0
        defb 2,0
        defb 2,0

; three rooms x 25 tiles, row-major u=0..4, v=0..4
; room 0: TOY DEPARTMENT
room_data:
        defb 0,0,0,0,2
        defb 0,0,0,1,0
        defb 0,1,1,0,0
        defb 0,0,0,0,0
        defb 0,0,0,0,5
; room 1: ESCALATORS (guard corridor)
        defb 1,1,1,1,1
        defb 1,1,1,1,1
        defb 0,0,0,0,5
        defb 1,1,1,1,1
        defb 1,1,1,1,1
; room 2: CHECKOUT
        defb 0,0,0,0,0
        defb 0,0,0,0,0
        defb 0,0,1,0,0
        defb 0,0,1,0,0
        defb 0,0,0,0,3

; cell textures / sprites
z_cell:     defs 8,0
solidcell:  defb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF

hero_spr:
        defb 0x0F,0xF0        ; fedora brim
        defb 0x1F,0xF8        ; fedora crown
        defb 0x0F,0xF0        ; face
        defb 0x0F,0xF0        ; face
        defb 0x1F,0xF8        ; jacket shoulders
        defb 0x3F,0xFC        ; jacket + satchel strap
        defb 0x37,0xEC        ; satchel
        defb 0x3F,0xFC        ; jacket
        defb 0x1F,0xF8        ; jacket
        defb 0x1B,0xD8        ; legs apart
        defb 0x1B,0xD8
        defb 0x1B,0xD8
        defb 0x33,0xCC
        defb 0x33,0xCC
        defb 0x33,0xCC
        defb 0x00,0x00

guard_spr:
        defb 0x0F,0xF0        ; cap brim
        defb 0x1F,0xF8        ; cap
        defb 0x0F,0xF0        ; face
        defb 0x0F,0xF0        ; face
        defb 0x1F,0xF8        ; shoulders/uniform
        defb 0x3F,0xFC        ; uniform
        defb 0x3F,0xFC        ; uniform + badge
        defb 0x3F,0xFC        ; uniform
        defb 0x1F,0xF8        ; uniform
        defb 0x1B,0xD8        ; legs apart
        defb 0x1B,0xD8
        defb 0x1B,0xD8
        defb 0x33,0xCC
        defb 0x33,0xCC
        defb 0x33,0xCC
        defb 0x00,0x00

; strings
t_title:    defb "RAIDERS OF THE LOST AISLE",0
t_sub:      defb "A CHRISTMAS EVE ADVENTURE",0
t_keys1:    defb "Q A O P TO MOVE",0
t_keys2:    defb "FIND THE TOY, DODGE SECURITY",0
t_press:    defb "PRESS S TO START",0
t_go:       defb "FIND THE TOY BEFORE THEY CLOSE",0
t_gottoy:   defb "GOT IT! NOW GET TO THE CHECKOUT",0
t_needtoy:  defb "YOU NEED THE TOY FIRST",0
t_caught:   defb "SECURITY! BACK TO THE START",0
t_win1:     defb "HOME IN TIME FOR CHRISTMAS",0
t_win2:     defb "A CHRISTMAS MIRACLE",0
t_lose1:    defb "THE STORE HAS CLOSED",0
t_lose2:    defb "MAYBE NEXT YEAR, KIDDO",0
t_hudtoy:   defb "TOY:",0
t_hudtime:  defb "TIME:",0
t_yes:      defb "YES",0
t_no:       defb "NO ",0

        include "fontdata.inc"

        end start
