v2.1.0 - order relic options
ci/woodpecker/push/c64 Pipeline was successful

This commit is contained in:
2026-08-29 11:21:31 +02:00
parent b155b46dca
commit 266c9ae84c
6 changed files with 305 additions and 34 deletions
+10 -3
View File
@@ -30,7 +30,7 @@ cells, an animated exit portal, a raster color band behind the
status row, and a fade to black between all screens. The menu floats
over a dark maze backdrop under the big gold-washed title.
The options screen adds two optional twists:
The options screen adds three optional twists:
- **worker gun** - player 1 shoots a bullet with fire/`space`; a hit
enemy respawns on a random cell after a pause.
@@ -38,6 +38,13 @@ The options screen adds two optional twists:
denied until the bar on the status row drains (pixel by pixel) and
the readout flips back from *wait* to *ready* (the cursor also
turns red while recharging).
- **relic order** - numbered relics lie scattered over the corridors
and the worker has to gather them *in order*, 1 first, then 2, and
so on, before the portal will take him. Until the last one is his
the exit stays sealed (a dull grey, and bumping into it blips the
border red); the relic he is after next pulses white, and the
status row counts them off next to the portal glyph. Every level
adds one more: two on level 1 up to nine on level 8.
Screens: TTG intro (ported from rabbitc64) -> pyramid title card
(the pyramid, the trapped worker and the pulsing sun of Ra) -> main
@@ -60,8 +67,8 @@ Test builds can boot straight into one state:
```
acme -DAUTOPLAY=1 -f cbm -o blessingofra.prg main.asm
# also: AUTOOPTIONS, AUTOSTORY, AUTOPYRAMID, AUTOCREDITS,
# AUTOWIN, AUTOLOSE; add -DFORCEOPTS=1 to start with both
# options turned on
# AUTOWIN, AUTOLOSE; add -DFORCEOPTS=1 to start with all
# three options turned on
```
## Files
+3
View File
@@ -97,6 +97,8 @@ CH_POST = $e1 ; bolted plate: fixed pivot post
CH_EXIT = $e2 ; the portal out (4 animated glyph frames)
CH_FLOOR = $20 ; space: corridor
CH_BAR = $e3 ; 7 chars: a bar cell 1-7 pixels full
CH_RELIC = $31 ; the relics wear their order: '1'-'9'
NRELIC = 9 ; most relics on a level (level 8)
; ---- the actors are hardware sprites ----
SP_RUN = 0 ; player 1, the worker
@@ -113,6 +115,7 @@ COL_HILITE = YELLOW ; walls the cursor's spin would move
COL_SCARAB = CYAN ; the scarabs
COL_SCORP = LRED ; the scorpions
COL_STING = PURPLE ; the venom sting
COL_RELIC = LBLUE ; the numbered relics (relic order option)
STATCOL = BROWN ; background band behind the status row
; ---- game timing ----
+2 -1
View File
@@ -14,7 +14,8 @@
; intro.asm - TTG boot logo (ported from rabbitc64)
; pyramid.asm - title card: the pyramid and the sun of ra
; menu.asm - main menu over a dark maze backdrop
; options.asm - optional extras: worker gun, spin cooldown
; options.asm - optional extras: worker gun, spin cooldown,
; relic order
; story.asm - the legend + who steers what
; credits.asm - the crew (content from rabbitc64, new look)
; play.asm - maze generation + the game itself
+1 -1
View File
@@ -6,5 +6,5 @@
"site": "https://git.teletypegames.org/games/labyrintwo",
"repo": "https://git.teletypegames.org/games/blessingofra",
"license": "MIT",
"version": "2.0.0"
"version": "2.1.0"
}
+36 -16
View File
@@ -4,11 +4,14 @@
; respawns elsewhere after a pause
; spin cooldown - ra needs a recharge between two spins,
; shown on the status row
; relic order - numbered relics have to be collected in
; order before the portal opens, one more
; of them on every level
; start level - which level a fresh run begins on (1-8)
; the settings live here and survive between levels
; -----------------------------------------------------------
OPT_ITEMS = 4
OPT_ITEMS = 5
options_init:
jsr clear_screen
@@ -17,7 +20,8 @@ options_init:
+prtext 21, 6, LGREY, txt_oh1, txt_oh1e-txt_oh1
+prtext 22, 4, LGREY, txt_oh2, txt_oh2e-txt_oh2
+prtext 23, 5, LGREY, txt_oh3, txt_oh3e-txt_oh3
+prtext 23, 2, LGREY, txt_oh3, txt_oh3e-txt_oh3
+prtext 24, 5, LGREY, txt_oh4, txt_oh4e-txt_oh4
lda #0
sta optsel
@@ -56,7 +60,7 @@ ou_udrel:
sta udheld
ou_udend:
; the three lines, the picked one in yellow
; the option lines, the picked one in yellow
ldx #0
oo_items:
txa
@@ -68,11 +72,14 @@ oo_items:
cpx #OPT_ITEMS
bne oo_items
; the current values next to the three settings
; the current value next to each setting
ldx opt_shoot
lda #10
lda #7
jsr put_oval
ldx opt_cool
lda #10
jsr put_oval
ldx opt_relic
lda #13
jsr put_oval
lda opt_level ; the start level as a digit
@@ -83,6 +90,7 @@ oo_items:
; blinking '>' cursor in front of the picked line
lda #$20
sta SCREEN+7*40+7
sta SCREEN+10*40+7
sta SCREEN+13*40+7
sta SCREEN+16*40+7
@@ -110,10 +118,12 @@ ou_fire:
jsr start_edge
beq ou_done
lda optsel
cmp #3
cmp #4
beq ou_back
cmp #2
cmp #3
beq ou_tlevel
cmp #2
beq ou_trelic
cmp #1
beq ou_tcool
lda opt_shoot
@@ -125,6 +135,11 @@ ou_tcool:
eor #1
sta opt_cool
jmp ou_tsfx
ou_trelic:
lda opt_relic
eor #1
sta opt_relic
jmp ou_tsfx
ou_tlevel:
ldx opt_level ; cycle 1-8, wrapping back to 1
inx
@@ -179,15 +194,15 @@ put_oval:
jmp print_text
; ---- option tables ----
oitem_lo: !byte <txt_ogun, <txt_ocool, <txt_olev, <txt_oback
oitem_hi: !byte >txt_ogun, >txt_ocool, >txt_olev, >txt_oback
oitem_len: !byte 10, 13, 11, 4
oitem_row: !byte 10, 13, 16, 19
oitem_lo: !byte <txt_ogun, <txt_ocool, <txt_orel, <txt_olev, <txt_oback
oitem_hi: !byte >txt_ogun, >txt_ocool, >txt_orel, >txt_olev, >txt_oback
oitem_len: !byte 10, 13, 11, 11, 4
oitem_row: !byte 7, 10, 13, 16, 19
ocur_lo: ; cursor cells: column 7 of those rows
!byte <(SCREEN+10*40+7), <(SCREEN+13*40+7)
!byte <(SCREEN+7*40+7), <(SCREEN+10*40+7), <(SCREEN+13*40+7)
!byte <(SCREEN+16*40+7), <(SCREEN+19*40+7)
ocur_hi:
!byte >(SCREEN+10*40+7), >(SCREEN+13*40+7)
!byte >(SCREEN+7*40+7), >(SCREEN+10*40+7), >(SCREEN+13*40+7)
!byte >(SCREEN+16*40+7), >(SCREEN+19*40+7)
ovtx_lo: !byte <txt_off, <txt_on
ovtx_hi: !byte >txt_off, >txt_on
@@ -196,6 +211,7 @@ ovcol: !byte LGREY, LGREEN
; ---- option texts ----
txt_ogun: !scr "worker gun"
txt_ocool: !scr "spin cooldown"
txt_orel: !scr "relic order"
txt_olev: !scr "start level"
txt_oback: !scr "back"
txt_on: !scr "on "
@@ -204,16 +220,20 @@ txt_oh1: !scr "gun: p1 fire shoots a bullet"
txt_oh1e:
txt_oh2: !scr "cooldown: spins need a recharge"
txt_oh2e:
txt_oh3: !scr "start: a fresh run's first level"
txt_oh3: !scr "relics: grab the numbers in order"
txt_oh3e:
txt_oh4: !scr "start: a fresh run's first level"
txt_oh4e:
; ---- the settings themselves (session-wide) ----
!ifdef FORCEOPTS { ; test builds start with both turned on
!ifdef FORCEOPTS { ; test builds start with them all on
opt_shoot: !byte 1
opt_cool: !byte 1
opt_relic: !byte 1
} else {
opt_shoot: !byte 0 ; 0 = classic game, 1 = the worker shoots
opt_cool: !byte 0 ; 1 = spins need a recharge
opt_relic: !byte 0 ; 1 = collect the relics in order first
}
opt_level: !byte 1 ; a fresh run's first level: 1-8
optsel: !byte 0 ; picked line: 0-3
optsel: !byte 0 ; picked line: 0-4
+253 -13
View File
@@ -22,6 +22,10 @@
; random cell after RESPAWN_TIME frames
; opt_cool - a spin starts a COOL_TIME recharge, shown as
; a draining 1/8-step bar on the status row
; opt_relic - numbered relics are scattered over the cells;
; the worker must pick them up in order before
; the portal will take him, one more relic each
; level (2 on level 1 up to 9 on level 8)
;
; screen layout: status on row 0 (over the colored raster
; band), the centered lattice on rows 1-23, hints on row 24
@@ -37,7 +41,10 @@ play_init:
sta SCREEN+6
lda #YELLOW
sta COLRAM+6
lda opt_relic ; relic mode keeps its own counter here
bne pi_goal
+prtext 0, 34, LGREY, txt_goal, txt_goale-txt_goal
pi_goal:
+prtext 24, 4, DGREY, txt_hint, txt_hinte-txt_hint
lda mzsize ; this level's lattice: a square
@@ -150,6 +157,7 @@ ps_exit:
lda #COL_STING
sta SPRCOL+SP_STING
jsr place_relics ; first: the enemies then avoid their cells
jsr spawn_enemies
; input state: fire still held from the previous screen
@@ -530,6 +538,165 @@ sn_free:
etypecol: !byte COL_SCARAB, COL_SCORP
; ============================================================
; the relics (relic order option) - numbered treasures on the
; maze cells; the worker must collect them in order before
; the portal will let him out
; ============================================================
; ---- scatter this level's relics over random cells: one
; more each level, 2 on level 1 up to 9 on level 8 ----
place_relics:
lda #0
sta rnext
sta rcount
sta ri
lda opt_relic
beq pl_done
lda mzsize ; level 1-8 -> 2-9 relics
sec
sbc #MZMIN-2
sta rcount
pl_one:
jsr rand8
and #$0f
cmp mzcw
bcs pl_one
asl ; cell column -> screen column
sec
adc offx
sta ttx
pl_ry:
jsr rand8
and #$0f
cmp mzch
bcs pl_ry
asl
sec
adc offy
sta tty
lda ttx ; never on the worker's own starting cell...
cmp px
bne pl_free
lda tty
cmp py
beq pl_one
pl_free:
jsr relic_at ; ...nor on a cell already holding one
bcs pl_one
ldx ri
lda ttx
sta rlx,x
lda tty
sta rly,x
jsr draw_relic
inc ri
lda ri
cmp rcount
bne pl_one
pl_done:
rts
; ---- stamp relic X on (ttx, tty): its order is its digit ----
draw_relic:
txa
clc
adc #CH_RELIC
pha
ldx tty
jsr row_ptr
ldy ttx
pla
sta (scrlo),y
lda #COL_RELIC
sta (collo),y
rts
; ---- C set when one of the ri placed relics sits on
; (ttx, tty) - the placement loop's duplicate check ----
relic_at:
ldx ri
beq rlk_no
rlk_loop:
dex
lda rlx,x
cmp ttx
bne rlk_next
lda rly,x
cmp tty
beq rlk_yes
rlk_next:
cpx #0
bne rlk_loop
rlk_no:
clc
rts
rlk_yes:
sec
rts
; ---- the worker stepped onto a cell: if it holds the relic
; that is next in the order, it goes into his hands ----
take_relic:
lda opt_relic
beq tk_done
ldx rnext
cpx rcount
bcs tk_done ; all of them already in hand
lda rlx,x
cmp px
bne tk_done
lda rly,x
cmp py
bne tk_done
ldx py ; the cell goes back to plain corridor
jsr row_ptr
ldy px
lda #CH_FLOOR
sta (scrlo),y
lda #BLACK
sta (collo),y
inc rnext
lda #4 ; a white border blip marks the find
sta bflash
lda #WHITE
sta bcolor
lda #SFX_SELECT
jmp sfx_play
tk_done:
rts
; ---- C set when the portal will take the worker: always in
; the classic game, once every relic is his in relic mode ----
exit_open:
lda opt_relic
beq eo_yes
lda rnext
cmp rcount
bcc eo_no
eo_yes:
sec
rts
eo_no:
clc
rts
; ---- C set when the char in A can be walked or flown
; through: the corridor floor and the relics lying on it ----
open_ch:
cmp #CH_FLOOR
beq oc_yes
cmp #CH_RELIC ; the relic digits sit on open floor
bcc oc_no
cmp #CH_RELIC+NRELIC
bcs oc_no
oc_yes:
sec
rts
oc_no:
clc
rts
; ============================================================
; one frame of play
; ============================================================
@@ -570,6 +737,7 @@ pu_nocool:
jsr upd_cursor
jsr upd_flash
jsr upd_status
jsr upd_goal
jsr upd_clock
jmp upd_sprites
pu_end:
@@ -658,9 +826,9 @@ ur_try:
ldy ttx
lda (scrlo),y
cmp #CH_EXIT
beq ur_win
cmp #CH_FLOOR
bne ur_stop ; a wall or a post: blocked
beq ur_exit
jsr open_ch
bcc ur_stop ; a wall or a post: blocked
jsr enemy_at ; walking into an enemy still hurts
bcs ur_dead
lda es_on ; and so does the venom sting
@@ -676,12 +844,17 @@ ur_go:
sta px
lda tty
sta py
jmp take_relic ; the cell may hold the next relic
ur_stop:
rts
ur_win:
ur_exit:
jsr exit_open
bcc ur_sealed
lda #1
sta endflag
rts
ur_sealed: ; relics still out there: the portal is shut
jmp deny_blip
ur_dead:
lda #2
sta endflag
@@ -703,8 +876,8 @@ upd_shot:
jsr row_ptr
ldy ttx
lda (scrlo),y
cmp #CH_FLOOR
beq sh_open
jsr open_ch
bcs sh_open
lda #0 ; a wall (or the portal) stops it
sta s_on
jmp sh_spawn
@@ -881,7 +1054,7 @@ do_rotate:
lda cool
beq dr_ready
dr_deny:
jmp rot_deny ; still recharging / a slot is taken
jmp deny_blip ; still recharging / a slot is taken
dr_ready:
lda kx ; nobody may stand in the four slots
sta ttx
@@ -961,8 +1134,10 @@ dr_ready:
dr_nocool:
lda #SFX_ROTATE
jmp sfx_play
rot_deny:
lda #6 ; red border blip on a denied spin
; ---- red border blip + buzz: a denied spin, or the worker
; knocking on a portal the relics have not opened yet ----
deny_blip:
lda #6
sta bflash
lda #RED
sta bcolor
@@ -1126,8 +1301,8 @@ en_norun:
jsr row_ptr
ldy ttx
lda (scrlo),y
cmp #CH_FLOOR
beq en_open
jsr open_ch
bcs en_open
sec
rts
en_open:
@@ -1247,8 +1422,8 @@ ust_norun:
jsr row_ptr
ldy ttx
lda (scrlo),y
cmp #CH_FLOOR
beq ust_fly
jsr open_ch
bcs ust_fly
lda #0 ; a wall swallows it
sta es_on
rts
@@ -1268,6 +1443,8 @@ upd_flash:
ldx exrow
jsr row_ptr
ldy excol
jsr exit_open
bcc uf_exlock
lda framectr
and #8
bne uf_exw
@@ -1275,6 +1452,9 @@ upd_flash:
jmp uf_exset
uf_exw:
lda #WHITE
jmp uf_exset
uf_exlock:
lda #DGREY ; sealed while a relic is still missing
uf_exset:
sta (collo),y
lda rotflash ; the spun walls flash, then re-highlight
@@ -1298,6 +1478,29 @@ uf_hot:
lda #COL_HILITE
uf_cset:
sta SPRCOL+SP_CUR
; fall through: the relic he is after next pulses white
relic_blink:
lda opt_relic
beq rb_done
ldx rnext
cpx rcount
bcs rb_done
lda rlx,x
sta ptmp
lda rly,x
tax
jsr row_ptr
ldy ptmp
lda framectr
and #8
bne rb_white
lda #COL_RELIC
jmp rb_set
rb_white:
lda #WHITE
rb_set:
sta (collo),y
rb_done:
rts
; ---- status row: the spin recharge readout (cooldown option)
@@ -1350,6 +1553,38 @@ us_clr:
bpl us_clr
rts
; ---- relic mode's readout in the goal corner: relics in
; hand / relics on the level, next to the portal glyph; green
; once the last one is his ----
upd_goal:
lda opt_relic
bne ug_show
rts
ug_show:
lda rnext
ora #$30
sta SCREEN+34
lda #$2f ; '/'
sta SCREEN+35
lda rcount
ora #$30
sta SCREEN+36
lda #$20
sta SCREEN+37
lda #CH_EXIT
sta SCREEN+38
jsr exit_open
lda #LRED
bcc ug_col
lda #LGREEN
ug_col:
ldx #4
ug_loop:
sta COLRAM+34,x
dex
bpl ug_loop
rts
; ---- the run clock, live on the status row ----
upd_clock:
lda tmin
@@ -1529,6 +1764,9 @@ exrow: !byte 0 ; screen row of the exit portal
endflag: !byte 0 ; 0 = playing, 1 = escaped, 2 = caught
cool: !byte 0 ; spin recharge frames left (cooldown option)
rotflash:!byte 0 ; frames the freshly spun walls stay white
rcount: !byte 0 ; relics on this level (relic order option)
rnext: !byte 0 ; the one he must pick up next
ri: !byte 0 ; relic placement loop index
s_on: !byte 0 ; bullet in flight?
sx: !byte 0 ; bullet position and heading
sy: !byte 0
@@ -1579,6 +1817,8 @@ ex: !fill NENEMIES, 0 ; enemy positions and headings
ey: !fill NENEMIES, 0
edir: !fill NENEMIES, 0
edead: !fill NENEMIES, 0 ; respawn countdown, 0 = alive
rlx: !fill NRELIC, 0 ; relic positions (screen col / row)
rly: !fill NRELIC, 0
; ---- maze generator buffers ----
visited: !fill NCELLS, 0