From 84e856c5eb0ee6357c3087bbc0ab8282f8806394 Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Sat, 18 Jul 2026 18:46:43 +0200 Subject: [PATCH] options --- README.md | 26 +++-- common.asm | 4 +- controls.asm | 3 + defs.asm | 7 +- main.asm | 10 +- menu.asm | 78 +++++++++++---- metadata.json | 2 +- options.asm | 194 +++++++++++++++++++++++++++++++++++ play.asm | 272 ++++++++++++++++++++++++++++++++++++++++++++++---- sound.asm | 13 +-- win.asm | 4 +- 11 files changed, 555 insertions(+), 58 deletions(-) create mode 100644 options.asm diff --git a/README.md b/README.md index 656fe8d..089d9ba 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# labyrinth +# labyrinth (LabyrinTwo) -Two-player maze duel for the Commodore 64 (ACME + VICE), in the same -spirit as [rabbitc64](../rabbitc64). +**LabyrinTwo** - a two-player maze duel for the Commodore 64 +(ACME + VICE), in the same spirit as [rabbitc64](../rabbitc64). A fresh maze is carved every round on a 39x23 lattice where the walls are exactly as wide as the corridors. **Player 1** (joystick 2, or @@ -12,10 +12,19 @@ fire (or `return`) spins the four wall pieces around the cursor by 90 degrees, reshaping the maze in real time. A spin is denied while anyone is standing in one of the four slots. +The options screen adds two optional twists: + +- **runner gun** - player 1 shoots a bullet with fire/`space`; a hit + enemy respawns on a random cell after a pause. +- **spin cooldown** - each spin starts a recharge; further spins are + denied until the draining bar on the status row runs out and the + readout flips back from *wait* to *ready* (the cursor also dims + while recharging). + Screens: TTG intro (ported from rabbitc64) -> main menu -(play / rules / controls / credits) -> game -> win (back to the menu) -or game over (fire retries with a fresh maze, `m` backs out to the -menu). +(play / options / rules / controls / credits) -> game -> win (back +to the menu) or game over (fire retries with a fresh maze, `m` backs +out to the menu). ## Build & run @@ -29,7 +38,9 @@ Test builds can boot straight into one state: ``` acme -DAUTOPLAY=1 -f cbm -o labyrinth.prg main.asm -# also: AUTORULES, AUTOCONTROLS, AUTOCREDITS, AUTOWIN, AUTOLOSE +# also: AUTOOPTIONS, AUTORULES, AUTOCONTROLS, AUTOCREDITS, +# AUTOWIN, AUTOLOSE; add -DFORCEOPTS=1 to start with both +# options turned on ``` ## Files @@ -38,6 +49,7 @@ acme -DAUTOPLAY=1 -f cbm -o labyrinth.prg main.asm - `defs.asm` - registers, constants, zero page, macros - `intro.asm` - TTG boot logo with the gold color wash - `menu.asm` - main menu card +- `options.asm` - the optional extras and their toggle screen - `rules.asm` - how to play - `controls.asm` - joystick + keyboard mappings - `credits.asm` - the crew diff --git a/common.asm b/common.asm index 4b8d28a..bf38582 100644 --- a/common.asm +++ b/common.asm @@ -357,7 +357,9 @@ bigmap: ; screen code (a=1..z=26) -> glyph index !byte 20, 18, 21, 0, 15, 0 ; u-z ; ---- shared texts (screen codes) ---- -txt_title: !scr "labyrinth" +; the title is split so the two words can wear two colors +txt_t1: !scr "labyrin" +txt_t2: !scr "two" txt_ttgname: !scr "teletype games" txt_presents:!scr "presents" txt_return: !scr "fire = return" diff --git a/controls.asm b/controls.asm index 716c8b2..b3f554c 100644 --- a/controls.asm +++ b/controls.asm @@ -10,6 +10,7 @@ controls_init: +prtext 8, 9, LGREEN, txt_c1, txt_c1e-txt_c1 +prtext 10, 6, WHITE, txt_c2, txt_c2e-txt_c2 + +prtext 11, 6, LGREY, txt_c2b, txt_c2be-txt_c2b +prtext 13, 6, LBLUE, txt_c3, txt_c3e-txt_c3 +prtext 15, 6, WHITE, txt_c4, txt_c4e-txt_c4 @@ -42,6 +43,8 @@ txt_c1: !scr "player 1 - the runner" txt_c1e: txt_c2: !scr "move: joystick 2 or w a s d" txt_c2e: +txt_c2b: !scr "gun: fire or space, if enabled" +txt_c2be: txt_c3: !scr "player 2 - the wall spinner" txt_c3e: txt_c4: !scr "move: joystick 1 or i j k l" diff --git a/defs.asm b/defs.asm index 2a7d452..ee68dca 100644 --- a/defs.asm +++ b/defs.asm @@ -31,7 +31,9 @@ SFX_ROTATE = 2 ; wall spin: noise whoosh SFX_DENY = 3 ; blocked spin: low buzz SFX_WIN = 4 ; escape: rising jingle SFX_LOSE = 5 ; caught: descending sweep -NSFX = 6 +SFX_SHOT = 6 ; gun fired: short high tick +SFX_HIT = 7 ; enemy hit: noise burst +NSFX = 8 ; ---- colors ---- BLACK = 0 @@ -74,6 +76,7 @@ CH_FLOOR = $20 ; space: corridor CH_EXIT = $5a ; diamond: the goal CH_PLAYER = $51 ; filled ball: the runner CH_ENEMY = $57 ; open circle: an enemy +CH_SHOT = $2a ; asterisk: the runner's bullet COL_WALL = MGREY ; spinnable wall pieces COL_POST = DGREY ; fixed pivot posts @@ -84,6 +87,8 @@ REP_HOLD = 10 ; runner: frames before a held dir repeats REP_PERIOD = 5 ; runner: frames between repeat steps CREP_HOLD = 12 ; cursor: same, a touch slower CREP_PERIOD = 7 +COOL_TIME = 160 ; spin cooldown option: recharge frames +RESPAWN_TIME = 150 ; gun option: frames until a hit enemy returns RAS_SYNC = 251 ; raster line polled for the frame sync ; ---- zero page pointers (same slots as rabbitc64) ---- diff --git a/main.asm b/main.asm index c69c4c5..c389c2c 100644 --- a/main.asm +++ b/main.asm @@ -1,5 +1,5 @@ ; ----------------------------------------------------------- -; main.asm - LABYRINTH for the C64 +; main.asm - LABYRINTWO for the C64 ; two-player maze duel: player 1 runs the corridors toward ; the green diamond exit while dodging the roaming colored ; enemies; player 2 rides the wall corners with a cursor @@ -9,7 +9,8 @@ ; Files: ; defs.asm - constants, zero page, macros ; intro.asm - TTG boot logo (ported from rabbitc64) -; menu.asm - main menu: play / rules / credits +; menu.asm - main menu: play / options / rules / ... +; options.asm - optional extras: runner gun, spin cooldown ; rules.asm - how to play ; controls.asm- joystick + keyboard mappings ; credits.asm - the crew (content from rabbitc64, new look) @@ -57,6 +58,9 @@ start: !ifdef AUTOCONTROLS { jsr controls_init } else { +!ifdef AUTOOPTIONS { + jsr options_init +} else { !ifdef AUTOCREDITS { jsr credits_init } else { @@ -73,6 +77,7 @@ start: } } } +} ; ============================================================ ; main loop - one state update per PAL frame, synced to the @@ -106,6 +111,7 @@ wf_hit: ; ============================================================ !src "intro.asm" !src "menu.asm" +!src "options.asm" !src "rules.asm" !src "controls.asm" !src "credits.asm" diff --git a/menu.asm b/menu.asm index b453e31..6f9e670 100644 --- a/menu.asm +++ b/menu.asm @@ -1,16 +1,18 @@ ; ----------------------------------------------------------- -; menu.asm - full-screen menu card: big LABYRINTH title with -; the gold wash, and a four-item menu: play / rules / +; menu.asm - full-screen menu card: big LABYRINTWO title in +; two colors (gold wash on LABYRIN, cool blue shimmer on +; TWO), and a five-item menu: play / options / rules / ; controls / credits (joystick or w/s up/down + fire, space ; or return) ; ----------------------------------------------------------- -MENU_ITEMS = 4 +MENU_ITEMS = 5 menu_init: jsr clear_screen - +prbig 2, 2, YELLOW, txt_title, 9 + +prbig 2, 0, YELLOW, txt_t1, 7 + +prbig 2, 28, CYAN, txt_t2, 3 +prtext 9, 13, LGREY, txt_ttgname, 14 lda #0 @@ -21,7 +23,8 @@ menu_init: rts menu_update: - ; gold wash rolling down the big title (rows 2-6) + ; the title rows 2-6: gold wash on LABYRIN (columns 0-26), + ; cool blue shimmer on TWO (columns 27-38) inc washphase lda #0 sta g8row @@ -39,7 +42,7 @@ mw_row: tax lda washtbl,x sta rtmp - ldy #37 ; the title spans columns 2-36 + ldy #26 mw_col: lda (scrlo),y cmp #$a0 ; only touch the letter blocks @@ -48,8 +51,26 @@ mw_col: sta (collo),y mw_skip: dey - cpy #1 - bne mw_col + bpl mw_col + lda washphase + lsr + clc + adc g8row + and #7 + tax + lda cooltbl,x + sta rtmp + ldy #38 +mw_col2: + lda (scrlo),y + cmp #$a0 + bne mw_skip2 + lda rtmp + sta (collo),y +mw_skip2: + dey + cpy #26 + bne mw_col2 inc g8row lda g8row cmp #5 @@ -104,10 +125,11 @@ mm_items: ; blinking '>' cursor in front of the picked line lda #$20 - sta SCREEN+12*40+15 - sta SCREEN+14*40+15 - sta SCREEN+16*40+15 - sta SCREEN+18*40+15 + sta SCREEN+11*40+15 + sta SCREEN+13*40+15 + sta SCREEN+15*40+15 + sta SCREEN+17*40+15 + sta SCREEN+19*40+15 lda framectr and #16 bne mu_start @@ -135,12 +157,16 @@ mu_start: lda menusel beq mg_play cmp #1 - beq mg_rules + beq mg_options cmp #2 + beq mg_rules + cmp #3 beq mg_controls jmp credits_init ; each init's rts returns to the main loop mg_play: jmp play_init +mg_options: + jmp options_init mg_rules: jmp rules_init mg_controls: @@ -169,19 +195,29 @@ dm_store: jmp print_text ; ---- menu tables ---- -mitem_lo: !byte txt_play, >txt_rules, >txt_controls, >txt_credits -mitem_len: !byte 4, 5, 8, 7 -mitem_row: !byte 12, 14, 16, 18 +mitem_lo: + !byte txt_play, >txt_options, >txt_rules + !byte >txt_controls, >txt_credits +mitem_len: !byte 4, 7, 5, 8, 7 +mitem_row: !byte 11, 13, 15, 17, 19 mcur_lo: ; cursor cells: column 15 of those rows - !byte <(SCREEN+12*40+15), <(SCREEN+14*40+15) - !byte <(SCREEN+16*40+15), <(SCREEN+18*40+15) + !byte <(SCREEN+11*40+15), <(SCREEN+13*40+15) + !byte <(SCREEN+15*40+15), <(SCREEN+17*40+15) + !byte <(SCREEN+19*40+15) mcur_hi: - !byte >(SCREEN+12*40+15), >(SCREEN+14*40+15) - !byte >(SCREEN+16*40+15), >(SCREEN+18*40+15) + !byte >(SCREEN+11*40+15), >(SCREEN+13*40+15) + !byte >(SCREEN+15*40+15), >(SCREEN+17*40+15) + !byte >(SCREEN+19*40+15) + +cooltbl: ; blue-cyan-white shimmer for the TWO + !byte $06, $0e, $03, $01, $01, $03, $0e, $06 ; ---- menu texts ---- txt_play: !scr "play" +txt_options: !scr "options" txt_rules: !scr "rules" txt_credits: !scr "credits" txt_info: !scr "p1: joy2 / wasd - runner" diff --git a/metadata.json b/metadata.json index 49943ee..b6d03c8 100644 --- a/metadata.json +++ b/metadata.json @@ -1,6 +1,6 @@ { "name": "labyrinth", - "title": "Labyrinth", + "title": "LabyrinTwo", "author": "Teletype Games", "desc": "Two-player maze duel: one player runs for the exit while the other spins the walls of the labyrinth around their corners. Dodge the enemies, find the diamond.", "site": "https://git.teletype.hu/games/labyrinth", diff --git a/options.asm b/options.asm new file mode 100644 index 0000000..6a1620f --- /dev/null +++ b/options.asm @@ -0,0 +1,194 @@ +; ----------------------------------------------------------- +; options.asm - optional extras, toggled with fire: +; runner gun - player 1 shoots with fire; a hit enemy +; respawns elsewhere after a pause +; spin cooldown - the wall spinner needs a recharge between +; two spins, shown on the status row +; the settings live here and survive between rounds +; ----------------------------------------------------------- + +OPT_ITEMS = 3 + +options_init: + jsr clear_screen + + +prbig 1, 6, WHITE, txt_options, 7 + + +prtext 19, 6, LGREY, txt_oh1, txt_oh1e-txt_oh1 + +prtext 20, 4, LGREY, txt_oh2, txt_oh2e-txt_oh2 + + lda #0 + sta optsel + +setupd options_update + rts + +options_update: + ; joystick / w-s steps the cursor (edge detected) + jsr read_joy + lda joyb + and #3 + beq ou_udrel + lda udheld + bne ou_udend + lda #1 + sta udheld + lda joyb + and #1 ; up + beq ou_down + lda optsel + beq ou_udend + dec optsel + lda #SFX_CURSOR + jsr sfx_play + jmp ou_udend +ou_down: + lda optsel + cmp #OPT_ITEMS-1 + beq ou_udend + inc optsel + lda #SFX_CURSOR + jsr sfx_play + jmp ou_udend +ou_udrel: + lda #0 + sta udheld +ou_udend: + + ; the three lines, the picked one in yellow + ldx #0 +oo_items: + txa + pha + jsr draw_oitem + pla + tax + inx + cpx #OPT_ITEMS + bne oo_items + + ; the current values next to the two settings + ldx opt_shoot + lda #10 + jsr put_oval + ldx opt_cool + lda #13 + jsr put_oval + + ; blinking '>' cursor in front of the picked line + lda #$20 + sta SCREEN+10*40+7 + sta SCREEN+13*40+7 + sta SCREEN+16*40+7 + lda framectr + and #16 + bne ou_fire + ldx optsel + lda ocur_lo,x + sta scrlo + lda ocur_hi,x + sta scrhi + ldy #0 + lda #62 ; '>' + sta (scrlo),y + lda scrhi + clc + adc #$d4 ; same cell in color RAM + sta scrhi + lda #YELLOW + sta (scrlo),y + +ou_fire: + ; fire toggles the picked setting / leaves on back + jsr start_edge + beq ou_done + lda optsel + cmp #2 + beq ou_back + cmp #1 + beq ou_tcool + lda opt_shoot + eor #1 + sta opt_shoot + jmp ou_tsfx +ou_tcool: + lda opt_cool + eor #1 + sta opt_cool +ou_tsfx: + lda #SFX_SELECT + jsr sfx_play +ou_done: + rts +ou_back: + lda #SFX_SELECT + jsr sfx_play + jmp menu_init + +; ---- draw option line X, yellow when it is the picked one ---- +draw_oitem: + lda oitem_lo,x + sta txtlo + lda oitem_hi,x + sta txthi + lda oitem_row,x + sta prow + lda #9 + sta pcol + lda oitem_len,x + sta plen + lda #WHITE + cpx optsel + bne do_store + lda #YELLOW +do_store: + sta pcolor + jmp print_text + +; ---- print the on/off value: X = 0/1, A = screen row ---- +put_oval: + sta prow + lda ovtx_lo,x + sta txtlo + lda ovtx_hi,x + sta txthi + lda ovcol,x + sta pcolor + lda #24 + sta pcol + lda #3 + sta plen + jmp print_text + +; ---- option tables ---- +oitem_lo: !byte txt_ogun, >txt_ocool, >txt_oback +oitem_len: !byte 10, 13, 4 +oitem_row: !byte 10, 13, 16 +ocur_lo: ; cursor cells: column 7 of those rows + !byte <(SCREEN+10*40+7), <(SCREEN+13*40+7), <(SCREEN+16*40+7) +ocur_hi: + !byte >(SCREEN+10*40+7), >(SCREEN+13*40+7), >(SCREEN+16*40+7) +ovtx_lo: !byte txt_off, >txt_on +ovcol: !byte LGREY, LGREEN + +; ---- option texts ---- +txt_ogun: !scr "runner gun" +txt_ocool: !scr "spin cooldown" +txt_oback: !scr "back" +txt_on: !scr "on " +txt_off: !scr "off" +txt_oh1: !scr "gun: p1 fire shoots a bullet" +txt_oh1e: +txt_oh2: !scr "cooldown: spins need a recharge" +txt_oh2e: + +; ---- the settings themselves (session-wide) ---- +!ifdef FORCEOPTS { ; test builds start with both turned on +opt_shoot: !byte 1 +opt_cool: !byte 1 +} else { +opt_shoot: !byte 0 ; 0 = classic game, 1 = the runner shoots +opt_cool: !byte 0 ; 1 = spins need a recharge +} +optsel: !byte 0 ; picked line: 0-2 diff --git a/play.asm b/play.asm index add0360..7f06018 100644 --- a/play.asm +++ b/play.asm @@ -6,6 +6,14 @@ ; four wall pieces around the cursor by 90 degrees; the ; roaming colored enemies end the round on touch ; +; optional extras (see options.asm): +; opt_shoot - player 1's fire shoots a bullet in the last +; direction faced; a hit enemy respawns on a +; random cell after RESPAWN_TIME frames +; opt_cool - a spin starts a COOL_TIME recharge, shown as +; a draining bar on the status row; spins are +; denied (buzz) until it runs out +; ; screen layout: status on row 0, the 39x23 maze lattice on ; rows 1-23 (cells = odd col / even row, posts = even col / ; odd row, wall slots = the rest), a hint line on row 24 @@ -13,7 +21,8 @@ play_init: jsr clear_screen - +prtext 0, 0, WHITE, txt_title, 9 + +prtext 0, 0, WHITE, txt_t1, 7 + +prtext 0, 7, CYAN, txt_t2, 3 +prtext 0, 28, LGREY, txt_goal, txt_goale-txt_goal +prtext 24, 6, DGREY, txt_hint, txt_hinte-txt_hint @@ -81,11 +90,14 @@ pi_exit: lda #COL_EXIT sta (collo),y - ; the runner starts in the top-left cell + ; the runner starts in the top-left cell, facing right lda #1 sta px + sta fdx lda #2 sta py + lda #0 + sta fdy jsr draw_player ; the corner cursor starts on the center post @@ -96,14 +108,17 @@ pi_exit: jsr spawn_enemies - ; input state: a fire still held from the previous screen - ; must be released before it can spin walls + ; input state: fire still held from the previous screen + ; must be released before it can spin or shoot lda #0 sta rheld sta cheld sta endflag + sta s_on + sta cool lda #1 sta cfheld + sta rfheld +setupd play_update rts @@ -260,7 +275,15 @@ mul19: !byte 0, 19, 38, 57, 76, 95, 114, 133, 152, 171, 190 spawn_enemies: lda #0 sta ei -sn_loop: +sn_all: + jsr spawn_one + inc ei + lda ei + cmp #NENEMIES + bne sn_all + rts + +spawn_one: ; place enemy ei on a random free cell sn_rx: jsr rand8 and #$0f @@ -303,10 +326,8 @@ sn_ry: jsr rand8 and #3 sta edir,x - inc ei - lda ei - cmp #NENEMIES - bne sn_loop + lda #0 + sta edead,x rts ecoltab: ; anything but the white runner / grey walls @@ -316,14 +337,20 @@ ecoltab: ; anything but the white runner / grey walls ; one frame of play ; ============================================================ play_update: + lda cool ; the spin recharge ticks down + beq pu_nocool + dec cool +pu_nocool: jsr upd_runner lda endflag bne pu_end + jsr upd_shot jsr upd_enemies lda endflag bne pu_end jsr upd_cursor jsr upd_flash + jsr upd_status rts pu_end: cmp #1 @@ -361,6 +388,7 @@ ur_first: lda #REP_HOLD sta rtimer ur_move: + ; decode the direction; it also becomes the gun's facing ldx px ldy py lda rdir @@ -371,15 +399,31 @@ ur_move: lsr bcs ur_left inx ; right + lda #1 + sta fdx + lda #0 + sta fdy jmp ur_try ur_up: dey + lda #0 + sta fdx + lda #$ff + sta fdy jmp ur_try ur_down: iny + lda #0 + sta fdx + lda #1 + sta fdy jmp ur_try ur_left: dex + lda #$ff + sta fdx + lda #0 + sta fdy ur_try: stx ttx sty tty @@ -393,7 +437,7 @@ ur_try: beq ur_win cmp #CH_ENEMY beq ur_dead - rts ; a wall: blocked + rts ; a wall (or the bullet): blocked ur_go: ldx py jsr row_ptr @@ -426,6 +470,111 @@ draw_player: sta (collo),y rts +; ---- the optional gun: one straight-flying bullet ---- +upd_shot: + lda s_on + beq sh_nofly + ldx sy ; wipe the bullet from its old spot - + jsr row_ptr ; but only if it is really there: right + ldy sx ; after firing this is the runner's own + lda (scrlo),y ; cell, and the runner must stay drawn + cmp #CH_SHOT + bne sh_step + lda #CH_FLOOR + sta (scrlo),y + lda #BLACK + sta (collo),y +sh_step: + lda sx ; one step along its heading + clc + adc sdx + sta ttx + lda sy + clc + adc sdy + sta tty + ldx tty + jsr row_ptr + ldy ttx + lda (scrlo),y + cmp #CH_FLOOR + beq sh_fly + cmp #CH_ENEMY + beq sh_hit + lda #0 ; a wall (or anything else) stops it + sta s_on + jmp sh_nofly +sh_fly: + lda ttx + sta sx + lda tty + sta sy + lda #CH_SHOT + sta (scrlo),y + lda #YELLOW + sta (collo),y + jmp sh_nofly +sh_hit: + lda #0 + sta s_on + jsr kill_enemy ; scrlo/y still point at the victim +sh_nofly: + lda opt_shoot ; fire spawns a bullet (edge detected) + beq sh_done + lda joyb ; read_joy ran in upd_runner this frame + and #JOY_FIRE + beq sh_frel + lda rfheld + bne sh_done + lda #1 + sta rfheld + lda s_on + bne sh_done ; one bullet at a time + lda px + sta sx + lda py + sta sy + lda fdx + sta sdx + lda fdy + sta sdy + lda #1 + sta s_on + lda #SFX_SHOT + jmp sfx_play +sh_frel: + lda #0 + sta rfheld +sh_done: + rts + +; ---- the bullet found an enemy at (ttx, tty): it starts its +; respawn timer; the caller's scrlo/y still address its cell ---- +kill_enemy: + ldx #0 +ke_loop: + lda edead,x + bne ke_next + lda ex,x + cmp ttx + bne ke_next + lda ey,x + cmp tty + bne ke_next + lda #RESPAWN_TIME + sta edead,x + lda #CH_FLOOR + sta (scrlo),y + lda #BLACK + sta (collo),y + lda #SFX_HIT + jmp sfx_play +ke_next: + inx + cpx #NENEMIES + bne ke_loop + rts + ; ---- player 2: the corner cursor + the wall spin ---- upd_cursor: jsr read_joy2 @@ -513,8 +662,16 @@ uc_done: rts ; ---- spin the four wall slots around post (kx, ky) by 90 -; degrees clockwise; denied while anyone stands in them ---- +; degrees clockwise; denied while anyone stands in them or +; while the optional cooldown is still recharging ---- do_rotate: + lda opt_cool + beq dr_ready + lda cool + beq dr_ready + lda #SFX_DENY ; still recharging + jmp sfx_play +dr_ready: ldx ky ; gather the four slot chars dex jsr row_ptr @@ -573,6 +730,11 @@ do_rotate: iny lda nch jsr put_slot + lda opt_cool ; the spin starts the recharge + beq dr_nocool + lda #COOL_TIME + sta cool +dr_nocool: lda #SFX_ROTATE jmp sfx_play rot_deny: @@ -608,6 +770,13 @@ upd_enemies: sta ei ue_loop: ldx ei + lda edead,x + beq ue_alive + dec edead,x ; shot down: waiting to respawn + bne ue_next + jsr spawn_one ; the timer ran out: back on a fresh cell + jmp ue_next +ue_alive: lda framectr clc adc estag,x @@ -667,7 +836,7 @@ etry_dir: ; A = direction; C clear when it moved (or won) beq ed_move cmp #CH_PLAYER beq ed_kill - sec ; wall, exit or another enemy: blocked + sec ; wall, exit, bullet or another enemy rts ed_kill: lda #2 @@ -704,7 +873,8 @@ ed_move: clc rts -; ---- per-frame flashes: the exit blinks, the cursor pulses ---- +; ---- per-frame flashes: the exit blinks; the cursor pulses +; yellow-red, dimmed to grey-red while the spin recharges ---- upd_flash: ldx exrow jsr row_ptr @@ -721,7 +891,20 @@ uf_exset: ldx ky jsr row_ptr ldy kx - lda framectr + lda opt_cool + beq uf_hot + lda cool + beq uf_hot + lda framectr ; recharging: dimmed pulse + and #4 + bne uf_cdred + lda #DGREY + jmp uf_cset +uf_cdred: + lda #RED + jmp uf_cset +uf_hot: + lda framectr ; ready: bright pulse and #4 bne uf_cred lda #YELLOW @@ -732,23 +915,77 @@ uf_cset: sta (collo),y rts +; ---- status row: the spin recharge readout (cooldown option) +; "wait" + a draining bar while recharging, "ready" after ---- +upd_status: + lda opt_cool + bne us_show + rts +us_show: + lda cool + beq us_ready + +prtext 0, 12, LRED, txt_wait, 4 + lda cool ; blocks left: (cool + 15) / 16 = 1-10 + clc + adc #15 + lsr + lsr + lsr + lsr + sta ptmp + ldx #9 +us_bar: + cpx ptmp + bcc us_blk ; x < blocks: still filled + lda #$20 + jmp us_put +us_blk: + lda #$a0 +us_put: + sta SCREEN+18,x ; bar cells: row 0, columns 18-27 + lda #LRED + sta COLRAM+18,x + dex + bpl us_bar + rts +us_ready: + +prtext 0, 12, LGREEN, txt_ready, 5 + lda #$20 ; clear the bar area + ldx #9 +us_clr: + sta SCREEN+18,x + dex + bpl us_clr + rts + ; ---- play texts ---- -txt_goal: !scr "reach the " - !byte CH_EXIT +txt_goal: !scr "reach the " + !byte CH_EXIT txt_goale: -txt_hint: !scr "p1: joy2/wasd p2: joy1/ijkl" +txt_hint: !scr "p1: joy2/wasd p2: joy1/ijkl" txt_hinte: +txt_wait: !scr "wait" +txt_ready: !scr "ready" ; ---- play variables ---- px: !byte 0 ; runner position (screen col / row) py: !byte 0 +fdx: !byte 0 ; runner facing: the gun aims this way +fdy: !byte 0 kx: !byte 0 ; corner cursor position (post col / row) ky: !byte 0 exrow: !byte 0 ; screen row of the exit diamond endflag: !byte 0 ; 0 = playing, 1 = escaped, 2 = caught +cool: !byte 0 ; spin recharge frames left (cooldown option) +s_on: !byte 0 ; bullet in flight? +sx: !byte 0 ; bullet position and heading +sy: !byte 0 +sdx: !byte 0 +sdy: !byte 0 rheld: !byte 0 ; runner direction repeat state rtimer: !byte 0 rdir: !byte 0 +rfheld: !byte 0 ; runner fire edge detection state cheld: !byte 0 ; cursor direction repeat state ctimer: !byte 0 cdir: !byte 0 @@ -776,6 +1013,7 @@ ex: !fill NENEMIES, 0 ; enemy positions, headings, colors ey: !fill NENEMIES, 0 edir: !fill NENEMIES, 0 ecol: !fill NENEMIES, 0 +edead: !fill NENEMIES, 0 ; respawn countdown, 0 = alive ; ---- maze generator buffers ---- visited: !fill NCELLS, 0 diff --git a/sound.asm b/sound.asm index f9513fb..44873ed 100644 --- a/sound.asm +++ b/sound.asm @@ -57,12 +57,13 @@ su_slide: su_idle: rts -; ---- effect tables: cursor, select, rotate, deny, win, lose ---- -sfxfreq: !byte $50, $20, $12, $06, $20, $38 -sfxslide: !byte $00, $04, $fe, $00, $02, $ff -sfxctrl: !byte $11, $21, $81, $21, $11, $21 -sfxad: !byte $06, $08, $08, $08, $0a, $0a -sfxdur: !byte $04, $0a, $08, $0c, $28, $30 +; ---- effect tables: +; cursor, select, rotate, deny, win, lose, shot, hit ---- +sfxfreq: !byte $50, $20, $12, $06, $20, $38, $60, $40 +sfxslide: !byte $00, $04, $fe, $00, $02, $ff, $fa, $fc +sfxctrl: !byte $11, $21, $81, $21, $11, $21, $11, $81 +sfxad: !byte $06, $08, $08, $08, $0a, $0a, $05, $08 +sfxdur: !byte $04, $0a, $08, $0c, $28, $30, $03, $08 ; ---- driver variables ---- sfx_timer: !byte 0 ; frames left of the current effect diff --git a/win.asm b/win.asm index 51a1441..905e434 100644 --- a/win.asm +++ b/win.asm @@ -9,7 +9,7 @@ win_init: +prbig 3, 6, YELLOW, txt_escaped, 7 +prtext 11, 15, WHITE, txt_welldone, txt_welldonee-txt_welldone - +prtext 13, 9, LGREY, txt_beaten, txt_beatene-txt_beaten + +prtext 13, 10, LGREY, txt_beaten, txt_beatene-txt_beaten lda #0 sta washphase @@ -70,7 +70,7 @@ wu_stay: txt_escaped: !scr "escaped" txt_welldone: !scr "well done!" txt_welldonee: -txt_beaten: !scr "you beat the labyrinth" +txt_beaten: !scr "you beat labyrintwo" txt_beatene: txt_firemenu: !scr "fire = menu" txt_firemenue: