From 28f1191fdeefbe82b6c71d0e9cb1b704e600544b Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Sun, 26 Jul 2026 19:01:43 +0200 Subject: [PATCH] blessing of ra --- Makefile | 2 +- README.md | 73 +++++--- common.asm | 44 ++++- controls.asm | 55 ------ defs.asm | 41 ++-- gfx.asm | 93 +++++---- intro.asm | 7 +- main.asm | 37 ++-- menu.asm | 132 ++++--------- metadata.json | 8 +- options.asm | 12 +- over.asm | 17 +- play.asm | 510 ++++++++++++++++++++++++++++++++++++++------------ pyramid.asm | 116 ++++++++++++ rules.asm | 56 ------ story.asm | 72 +++++++ win.asm | 12 +- 17 files changed, 839 insertions(+), 448 deletions(-) delete mode 100644 controls.asm create mode 100644 pyramid.asm delete mode 100644 rules.asm create mode 100644 story.asm diff --git a/Makefile b/Makefile index 709f652..dc30290 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # Makefile – C64 project builder (ACME + VICE) # ----------------------------------------- -PROJECT = labyrintwo +PROJECT = blessingofra ASM = acme EMU = x64sc diff --git a/README.md b/README.md index 8c2a200..20878ef 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,65 @@ -# labyrintwo (LabyrinTwo) +# Blessing of Ra -**LabyrinTwo** - a two-player maze duel for the Commodore 64 +**Blessing of Ra** - a two-player pyramid escape for the Commodore 64 (ACME + VICE), in the same spirit as [rabbitc64](../rabbitc64). -A fresh maze carves itself on screen at the start of every round on -a 39x23 lattice where the walls are exactly as wide as the corridors. -**Player 1** (joystick 2, or `w a s d`) runs the corridors toward the -green exit portal while dodging the roaming enemies. **Player 2** -(joystick 1, or `i j k l`) rides the corners of the walls with a -bracket cursor - 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 border -blips red); the spun pieces flash white. +The legend: when the great pyramid rose, a worker was sealed in its +peak. He prayed to Ra until the sun god took pity - now Ra bends the +pyramid's labyrinth walls to lead him out. -The look: brick walls and bolted pivot posts from a custom RAM -charset, every actor (runner, cursor, bullet, enemies) a hardware -sprite gliding smoothly between the 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. +**Player 1** (joystick 2, or `w a s d`) is the worker, running the +corridors toward the green exit portal. **Player 2** (joystick 1, or +`i j k l`) is Ra: a bracket cursor rides the corners of the walls - +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 border blips red); +the spun pieces flash white. + +Eight levels, each maze one cell bigger than the last: a square that +starts at 4x4 cells and grows to 11x11 - the tallest square lattice +the screen fits - always centered on screen. One more enemy joins +each level (up to four): the cyan **scarabs** roam the corridors, +and the light-red **scorpions** also spit a venom sting whenever +they line up with the worker - the purple drop flies down the +corridor until a wall swallows it. + +The look: a desert palette of sandstone walls and brown pivot posts +from a custom RAM charset, every actor (worker, cursor, bullet, +sting, enemies) a hardware sprite gliding smoothly between the +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: -- **runner gun** - player 1 shoots a bullet with fire/`space`; a hit +- **worker 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 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). -Screens: TTG intro (ported from rabbitc64) -> main 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). +Screens: TTG intro (ported from rabbitc64) -> pyramid title card +(the pyramid, the trapped worker and the pulsing sun of Ra) -> main +menu (play / options / story / credits) -> game -> next level, and +after the last one the escape screen (back to the menu); game over +lets fire retry the same level with a fresh maze, `m` backs out to +the menu. The story screen tells the legend and lists who steers +what. ## Build & run ``` make deps # macOS: brew install acme vice -make build # -> labyrintwo.prg +make build # -> blessingofra.prg make run # build + launch in x64sc ``` Test builds can boot straight into one state: ``` -acme -DAUTOPLAY=1 -f cbm -o labyrintwo.prg main.asm -# also: AUTOOPTIONS, AUTORULES, AUTOCONTROLS, AUTOCREDITS, +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 ``` @@ -56,15 +70,16 @@ acme -DAUTOPLAY=1 -f cbm -o labyrintwo.prg main.asm the border/status-band raster work - `defs.asm` - registers, constants, zero page, macros - `intro.asm` - TTG boot logo with the gold color wash +- `pyramid.asm` - title card: the pyramid, the worker, the sun of Ra - `menu.asm` - main menu card over a dark maze backdrop - `options.asm` - the optional extras and their toggle screen -- `rules.asm` - how to play -- `controls.asm` - joystick + keyboard mappings +- `story.asm` - the legend of the worker and Ra + the controls - `credits.asm` - the crew -- `play.asm` - maze generation (recursive backtracker, animated) - + the game +- `play.asm` - maze geometry + generation (recursive backtracker, + animated) + the game and its eight growing levels - `win.asm` / `over.asm` - end screens -- `common.asm` - shared routines: printing, big block font, input, RNG +- `common.asm` - shared routines: printing, big block font, the + title wash, input, RNG - `sound.asm` - single-voice SID SFX driver - `gfx.asm` - RAM charset with the custom tiles, the sprite engine, the portal animation and the fade-to-black transition diff --git a/common.asm b/common.asm index bf38582..74e4f99 100644 --- a/common.asm +++ b/common.asm @@ -356,10 +356,48 @@ bigmap: ; screen code (a=1..z=26) -> glyph index !byte 0, 7, 8, 9, 10, 11, 0, 12, 13, 14 ; k-t !byte 20, 18, 21, 0, 15, 0 ; u-z +; ---- gold wash over the two big title lines (rows 1-5 and +; 7-11): only the $a0 letter blocks are recolored, so it is +; safe over any backdrop ---- +wash_title: + inc washphase + lda #0 + sta g8row +wt_row: + ldx g8row + lda wtrows,x + tax + jsr row_ptr + lda washphase + lsr ; half speed keeps the shimmer gentle + clc + adc g8row + and #15 + tax + lda washtbl,x + sta rtmp + ldy #39 +wt_col: + lda (scrlo),y + cmp #$a0 ; only touch the letter blocks + bne wt_skip + lda rtmp + sta (collo),y +wt_skip: + dey + bpl wt_col + inc g8row + lda g8row + cmp #10 + bne wt_row + rts + +wtrows: !byte 1, 2, 3, 4, 5, 7, 8, 9, 10, 11 + ; ---- shared texts (screen codes) ---- -; the title is split so the two words can wear two colors -txt_t1: !scr "labyrin" -txt_t2: !scr "two" +; the title is split so the two lines can be placed apart +txt_t1: !scr "blessing" +txt_t2: !scr "of ra" txt_ttgname: !scr "teletype games" txt_presents:!scr "presents" txt_return: !scr "fire = return" diff --git a/controls.asm b/controls.asm deleted file mode 100644 index 86b975a..0000000 --- a/controls.asm +++ /dev/null @@ -1,55 +0,0 @@ -; ----------------------------------------------------------- -; controls.asm - who steers what: joystick and keyboard -; mappings for both players, one static card; fire returns -; ----------------------------------------------------------- - -controls_init: - jsr clear_screen - - +prbig 1, 4, WHITE, txt_controls, 8 - - +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 - +prtext 17, 10, WHITE, txt_c5, txt_c5e-txt_c5 - - +prtext 20, 5, LGREY, txt_c6, txt_c6e-txt_c6 - - +setupd controls_update - rts - -controls_update: - ; blink the return prompt - lda framectr - and #32 - bne co_hide - +prtext 22, 13, YELLOW, txt_return, 13 - jmp co_input -co_hide: - +prtext 22, 13, YELLOW, txt_blank, 13 -co_input: - jsr start_edge - beq co_stay - +fadeto menu_init -co_stay: - rts - -; ---- controls texts ---- -txt_controls: !scr "controls" -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" -txt_c4e: -txt_c5: !scr "spin: fire or return" -txt_c5e: -txt_c6: !scr "menus: fire, space or return" -txt_c6e: diff --git a/defs.asm b/defs.asm index 49e7154..46f7332 100644 --- a/defs.asm +++ b/defs.asm @@ -45,7 +45,7 @@ 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 -SFX_SHOT = 6 ; gun fired: short high tick +SFX_SHOT = 6 ; gun / scorpion sting: short high tick SFX_HIT = 7 ; enemy hit: noise burst NSFX = 8 @@ -75,34 +75,45 @@ JOY_RIGHT = 8 JOY_FIRE = 16 ; ---- the maze ---- -; 19x11 cells on a 39x23 char lattice (screen rows 1-23): -; cell = odd column, even row (walkable) -; post = even column, odd row (pivot, always solid) +; a lattice of (2*cells+1) chars per side, centered on rows +; 1-23; local coordinates inside the lattice: +; cell = odd column, odd row (walkable) +; post = even column, even row (pivot, always solid) ; slot = the rest: wall pieces that spin around the posts ; wall thickness therefore equals corridor width: one cell -MAZE_CW = 19 ; cells across -MAZE_CH = 11 ; cells down +; the levels are squares, one cell bigger each level: MZMIN +; up to MZMAX (11x11 cells = 23x23 chars, the tallest square +; the screen fits); the menu backdrop uses the full lattice +MZMIN = 4 ; level 1: 4x4 cells +MZMAX = 11 ; level 8: 11x11 cells +MAZE_CW = 19 ; the largest lattice: the menu backdrop +MAZE_CH = 11 NCELLS = MAZE_CW * MAZE_CH -NENEMIES = 4 +NENEMIES = 4 ; enemy sprite slots (a level uses 1-4) ; ---- characters (the custom tiles live in the RAM charset) ---- -CH_WALL = $e0 ; brick block: spinnable wall piece +CH_WALL = $e0 ; sandstone block: spinnable wall piece 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 ; ---- the actors are hardware sprites ---- -SP_RUN = 0 ; player 1, the runner -SP_CUR = 1 ; player 2, the corner cursor -SP_SHOT = 2 ; the optional bullet -SP_EN = 3 ; the enemies ride sprites 3-6 +SP_RUN = 0 ; player 1, the worker +SP_CUR = 1 ; player 2, ra's cursor +SP_SHOT = 2 ; the optional bullet +SP_EN = 3 ; the enemies ride sprites 3-6 +SP_STING = 7 ; the scorpions' venom sting -COL_WALL = MGREY ; spinnable wall pieces -COL_POST = DGREY ; fixed pivot posts +; ---- desert palette ---- +COL_WALL = ORANGE ; sandstone wall pieces +COL_POST = BROWN ; fixed pivot posts COL_EXIT = LGREEN ; the exit portal COL_HILITE = YELLOW ; walls the cursor's spin would move -STATCOL = BLUE ; background band behind the status row +COL_SCARAB = CYAN ; the scarabs +COL_SCORP = LRED ; the scorpions +COL_STING = PURPLE ; the venom sting +STATCOL = BROWN ; background band behind the status row ; ---- game timing ---- REP_HOLD = 10 ; runner: frames before a held dir repeats diff --git a/gfx.asm b/gfx.asm index d8127e4..1779a41 100644 --- a/gfx.asm +++ b/gfx.asm @@ -1,11 +1,11 @@ ; ----------------------------------------------------------- ; gfx.asm - the graphics layer: ; - the ROM font is copied into RAM and a handful of custom -; tiles are stamped over it (brick wall, pivot post, the -; animated exit portal, the 1/8-step cooldown bar cells) +; tiles are stamped over it (sandstone wall, pivot post, +; the animated exit portal, the 1/8-step cooldown bar) ; - a small sprite engine: every actor lives on the char ; grid, its sprite glides toward the cell a few pixels a -; frame (the runner bobs, the enemies wave) +; frame (the worker bobs, the enemies wave) ; - fade to black: the transition between two screens ; ----------------------------------------------------------- @@ -308,7 +308,7 @@ fadetbl: ; each color, one step darker !byte $02, $00, $02, $00, $0b, $05, $06, $0c ; ---- the custom tiles ---- -tile_gfx: ; CH_WALL: bricks with dark mortar +tile_gfx: ; CH_WALL: sandstone blocks, dark mortar !byte %11111111 !byte %11110111 !byte %11110111 @@ -430,52 +430,63 @@ spr_cur0: ; the corner cursor: four brackets !byte %10000001, 0, 0 !byte %11100111, 0, 0 !fill 40, 0 -spr_ena0: ; enemies 0-1: a ghost, skirt waving - !byte %00111100, 0, 0 - !byte %01111110, 0, 0 - !byte %11011011, 0, 0 - !byte %11011011, 0, 0 - !byte %11111111, 0, 0 - !byte %11111111, 0, 0 - !byte %11111111, 0, 0 - !byte %10110101, 0, 0 - !fill 40, 0 -spr_ena1: - !byte %00111100, 0, 0 - !byte %01111110, 0, 0 - !byte %11011011, 0, 0 - !byte %11011011, 0, 0 - !byte %11111111, 0, 0 - !byte %11111111, 0, 0 - !byte %11111111, 0, 0 - !byte %01101101, 0, 0 - !fill 40, 0 -spr_enb0: ; enemies 2-3: a crawler, legs pumping +spr_sting0: ; the scorpion's venom sting: a drop !byte %00000000, 0, 0 + !byte %00011000, 0, 0 + !byte %00111100, 0, 0 + !byte %01111110, 0, 0 + !byte %00111100, 0, 0 + !byte %00011000, 0, 0 + !byte %00000000, 0, 0 + !byte %00000000, 0, 0 + !fill 40, 0 +spr_ena0: ; even enemies: a scarab, legs out... + !byte %00100100, 0, 0 !byte %10111101, 0, 0 !byte %01111110, 0, 0 !byte %11011011, 0, 0 - !byte %01111110, 0, 0 - !byte %10111101, 0, 0 - !byte %00000000, 0, 0 - !byte %00000000, 0, 0 - !fill 40, 0 -spr_enb1: - !byte %00000000, 0, 0 - !byte %00111100, 0, 0 - !byte %01111110, 0, 0 !byte %11011011, 0, 0 !byte %01111110, 0, 0 + !byte %10111101, 0, 0 + !byte %00100100, 0, 0 + !fill 40, 0 +spr_ena1: ; ...and tucked as it scuttles + !byte %00100100, 0, 0 !byte %00111100, 0, 0 - !byte %00000000, 0, 0 - !byte %00000000, 0, 0 + !byte %11111111, 0, 0 + !byte %01011010, 0, 0 + !byte %01011010, 0, 0 + !byte %11111111, 0, 0 + !byte %00111100, 0, 0 + !byte %00100100, 0, 0 + !fill 40, 0 +spr_enb0: ; odd enemies: a scorpion, claws up, + !byte %11000011, 0, 0 ; the tail swinging right... + !byte %01100110, 0, 0 + !byte %00111100, 0, 0 + !byte %01111110, 0, 0 + !byte %00111100, 0, 0 + !byte %00011000, 0, 0 + !byte %00001100, 0, 0 + !byte %00011110, 0, 0 + !fill 40, 0 +spr_enb1: ; ...and left + !byte %11000011, 0, 0 + !byte %01100110, 0, 0 + !byte %00111100, 0, 0 + !byte %01111110, 0, 0 + !byte %00111100, 0, 0 + !byte %00011000, 0, 0 + !byte %00110000, 0, 0 + !byte %01111000, 0, 0 !fill 40, 0 -SPR_RUN = spr_run0 DIV 64 -SPR_SHOT = spr_shot0 DIV 64 -SPR_CUR = spr_cur0 DIV 64 -SPR_ENA = spr_ena0 DIV 64 -SPR_ENB = spr_enb0 DIV 64 +SPR_RUN = spr_run0 DIV 64 +SPR_SHOT = spr_shot0 DIV 64 +SPR_CUR = spr_cur0 DIV 64 +SPR_STING = spr_sting0 DIV 64 +SPR_ENA = spr_ena0 DIV 64 +SPR_ENB = spr_enb0 DIV 64 !if spr_run0 < $2000 { !error "sprite data slid under $2000: the VIC sees the char ROM shadow there" } !if * > CHARSET { !error "the program ran into the charset at $3800" } diff --git a/intro.asm b/intro.asm index 5dfd647..af67aa5 100644 --- a/intro.asm +++ b/intro.asm @@ -2,7 +2,8 @@ ; intro.asm - boot screen ported from rabbitc64: giant TTG ; letters built from 8x8 bitmaps with a gold color wash ; running down them, TELETYPE GAMES / PRESENTS below; -; advances to the menu after a few seconds or on fire +; advances to the pyramid title card after a few seconds or +; on fire ; ----------------------------------------------------------- INTRO_LROW = 3 ; top cell row of the giant letters @@ -78,11 +79,11 @@ iw_skip: ; fire skips, otherwise wait out the timer jsr start_edge beq iw_tick - +fadeto menu_init + +fadeto pyramid_init iw_tick: dec introt bne iw_done - +fadeto menu_init + +fadeto pyramid_init iw_done: rts diff --git a/main.asm b/main.asm index ef434cc..864d112 100644 --- a/main.asm +++ b/main.asm @@ -1,18 +1,21 @@ ; ----------------------------------------------------------- -; main.asm - LABYRINTWO for the C64 -; two-player maze duel: player 1 runs the corridors toward -; the green exit portal while dodging the roaming colored -; enemies; player 2 rides the wall corners with a cursor -; and fire spins the four wall pieces around it by 90 -; degrees, reshaping the maze under the runner's feet +; main.asm - BLESSING OF RA for the C64 +; two-player pyramid escape: player 1 is a worker sealed in +; the great pyramid, running the corridors toward the exit +; portal while dodging the scarabs and scorpions (whose +; venom stings fly down the corridors); player 2 is ra: the +; cursor rides the wall corners and fire spins the four +; wall pieces around it by 90 degrees, reshaping the maze +; under the worker's feet; eight levels, each maze one cell +; bigger than the last (4x4 up to 11x11) ; ; Files: ; defs.asm - constants, zero page, macros ; 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: runner gun, spin cooldown -; rules.asm - how to play -; controls.asm- joystick + keyboard mappings +; options.asm - optional extras: worker gun, spin cooldown +; story.asm - the legend + who steers what ; credits.asm - the crew (content from rabbitc64, new look) ; play.asm - maze generation + the game itself ; win.asm - escape screen, returns to the menu @@ -21,7 +24,7 @@ ; sound.asm - single-voice SFX driver ; gfx.asm - RAM charset, sprite engine, fades ; -; Build: acme -f cbm -o labyrintwo.prg main.asm +; Build: acme -f cbm -o blessingofra.prg main.asm ; ----------------------------------------------------------- * = $0801 @@ -54,11 +57,11 @@ start: !ifdef AUTOPLAY { ; test builds boot straight into one state jsr play_init } else { -!ifdef AUTORULES { - jsr rules_init +!ifdef AUTOSTORY { + jsr story_init } else { -!ifdef AUTOCONTROLS { - jsr controls_init +!ifdef AUTOPYRAMID { + jsr pyramid_init } else { !ifdef AUTOOPTIONS { jsr options_init @@ -72,7 +75,7 @@ start: !ifdef AUTOLOSE { jsr lose_init } else { - jsr intro_init ; TTG logo, then the menu + jsr intro_init ; TTG logo, then the pyramid, then the menu } } } @@ -145,10 +148,10 @@ wf_hit: ; the states and everything they share ; ============================================================ !src "intro.asm" +!src "pyramid.asm" !src "menu.asm" !src "options.asm" -!src "rules.asm" -!src "controls.asm" +!src "story.asm" !src "credits.asm" !src "play.asm" !src "win.asm" diff --git a/menu.asm b/menu.asm index 9fa48a2..5a42902 100644 --- a/menu.asm +++ b/menu.asm @@ -1,26 +1,30 @@ ; ----------------------------------------------------------- -; 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.asm - full-screen menu card: big BLESSING / OF RA +; title under the rolling gold sun wash, and a four-item +; menu: play / options / story / credits (joystick or w/s +; up/down + fire, space or return) ; ----------------------------------------------------------- -MENU_ITEMS = 5 +MENU_ITEMS = 4 menu_init: jsr clear_screen ; a dark maze weaves behind the whole card - lda #DGREY + lda #MAZE_CW + sta mzcw + lda #MAZE_CH + sta mzch + jsr set_maze + lda #BROWN sta latwcol sta latpcol jsr draw_lattice jsr gen_init jsr gen_maze_full - ; a clean panel for the menu itself (rows 9-22, cols 4-35) - ldx #9 + ; a clean panel for the menu itself (rows 13-24, cols 4-35) + ldx #13 mi_panel: stx ptmp jsr row_ptr @@ -35,12 +39,12 @@ mi_pcol: bne mi_pcol ldx ptmp inx - cpx #23 + cpx #25 bne mi_panel - +prbig 2, 0, YELLOW, txt_t1, 7 - +prbig 2, 28, CYAN, txt_t2, 3 - +prtext 9, 13, LGREY, txt_ttgname, 14 + +prbig 1, 4, YELLOW, txt_t1, 8 + +prbig 7, 10, ORANGE, txt_t2, 5 + +prtext 13, 13, LGREY, txt_ttgname, 14 lda #0 sta menusel @@ -50,58 +54,7 @@ mi_pcol: rts menu_update: - ; 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 -mw_row: - lda #2 - clc - adc g8row - tax - jsr row_ptr - lda washphase - lsr - clc - adc g8row - and #15 - tax - lda washtbl,x - sta rtmp - ldy #26 -mw_col: - lda (scrlo),y - cmp #$a0 ; only touch the letter blocks - bne mw_skip - lda rtmp - sta (collo),y -mw_skip: - dey - 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 - bne mw_row + jsr wash_title ; gold sun wash on both title lines ; joystick up/down steps the cursor (edge detected) jsr read_joy @@ -135,8 +88,8 @@ mu_udrel: mu_udend: ; footer: both control lines always visible - +prtext 21, 8, LGREY, txt_info, txt_infoe-txt_info - +prtext 22, 5, LGREY, txt_info2, txt_info2e-txt_info2 + +prtext 23, 7, LGREY, txt_info, txt_infoe-txt_info + +prtext 24, 11, LGREY, txt_info2, txt_info2e-txt_info2 ; the menu items: the picked line is yellow ldx #0 @@ -152,11 +105,10 @@ mm_items: ; blinking '>' cursor in front of the picked line lda #$20 - 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 + sta SCREEN+21*40+15 lda framectr and #16 bne mu_start @@ -186,18 +138,20 @@ mu_start: cmp #1 beq mg_options cmp #2 - beq mg_rules - cmp #3 - beq mg_controls + beq mg_story +fadeto credits_init ; every screen change fades to black mg_play: + lda #MZMIN ; a fresh run: level 1, clock zeroed + sta mzsize + lda #0 + sta tframe + sta tsec + sta tmin +fadeto play_init mg_options: +fadeto options_init -mg_rules: - +fadeto rules_init -mg_controls: - +fadeto controls_init +mg_story: + +fadeto story_init mu_done: rts @@ -223,35 +177,27 @@ dm_store: ; ---- menu tables ---- 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 + !byte >txt_play, >txt_options, >txt_story, >txt_credits +mitem_len: !byte 4, 7, 5, 7 +mitem_row: !byte 15, 17, 19, 21 mcur_lo: ; cursor cells: column 15 of those rows - !byte <(SCREEN+11*40+15), <(SCREEN+13*40+15) !byte <(SCREEN+15*40+15), <(SCREEN+17*40+15) - !byte <(SCREEN+19*40+15) + !byte <(SCREEN+19*40+15), <(SCREEN+21*40+15) mcur_hi: - !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 + !byte >(SCREEN+19*40+15), >(SCREEN+21*40+15) ; ---- 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" +txt_info: !scr "p1: joy2/wasd - the worker" txt_infoe: -txt_info2: !scr "p2: joy1 / ijkl - wall spinner" +txt_info2: !scr "p2: joy1/ijkl - ra" txt_info2e: ; ---- menu variables ---- -menusel: !byte 0 ; picked line: 0-2 +menusel: !byte 0 ; picked line: 0-3 udheld: !byte 0 ; joystick up/down edge detection state diff --git a/metadata.json b/metadata.json index 5f0d647..9d18a5f 100644 --- a/metadata.json +++ b/metadata.json @@ -1,9 +1,9 @@ { - "name": "labyrintwo", - "title": "LabyrinTwo", + "name": "blessingofra", + "title": "Blessing of Ra", "author": "Teletype Games", - "desc": "Two-player maze duel: one player runs for the exit while the other spins the walls of the labyrintwo around their corners. Dodge the enemies, find the diamond.", + "desc": "Two-player pyramid escape: a worker sealed in the great pyramid runs for the exit while Ra bends the maze walls around him. Scarabs and scorpions - and their flying venom stings - hunt him through eight levels, from a 4x4 maze up to 11x11.", "site": "https://git.teletype.hu/games/labyrintwo", "license": "MIT", - "version": "1.0.0" + "version": "2.0.0" } diff --git a/options.asm b/options.asm index 5e53524..547a819 100644 --- a/options.asm +++ b/options.asm @@ -1,10 +1,10 @@ ; ----------------------------------------------------------- ; options.asm - optional extras, toggled with fire: -; runner gun - player 1 shoots with fire; a hit enemy +; worker 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 +; spin cooldown - ra needs a recharge between two spins, +; shown on the status row +; the settings live here and survive between levels ; ----------------------------------------------------------- OPT_ITEMS = 3 @@ -173,7 +173,7 @@ ovtx_hi: !byte >txt_off, >txt_on ovcol: !byte LGREY, LGREEN ; ---- option texts ---- -txt_ogun: !scr "runner gun" +txt_ogun: !scr "worker gun" txt_ocool: !scr "spin cooldown" txt_oback: !scr "back" txt_on: !scr "on " @@ -188,7 +188,7 @@ txt_oh2e: opt_shoot: !byte 1 opt_cool: !byte 1 } else { -opt_shoot: !byte 0 ; 0 = classic game, 1 = the runner shoots +opt_shoot: !byte 0 ; 0 = classic game, 1 = the worker shoots opt_cool: !byte 0 ; 1 = spins need a recharge } optsel: !byte 0 ; picked line: 0-2 diff --git a/over.asm b/over.asm index 8ec951c..6cd9ee4 100644 --- a/over.asm +++ b/over.asm @@ -1,6 +1,7 @@ ; ----------------------------------------------------------- ; over.asm - game over screen: big red GAME OVER, fire -; restarts with a fresh maze, the m key backs out to the menu +; retries the same level with a fresh maze, the m key backs +; out to the menu ; ----------------------------------------------------------- lose_init: @@ -11,7 +12,14 @@ lose_init: +prbig 3, 2, RED, txt_gameover, 9 - +prtext 11, 10, WHITE, txt_caught, txt_caughte-txt_caught + +prtext 11, 8, WHITE, txt_caught, txt_caughte-txt_caught + + lda mzsize ; which level claimed him + sec + sbc #MZMIN-1 + ora #$30 + sta txt_felld + +prtext 13, 10, LGREY, txt_fell, txt_felle-txt_fell +prtext 21, 13, LGREY, txt_mmenu, txt_mmenue-txt_mmenu @@ -51,8 +59,11 @@ lu_done: ; ---- game over texts ---- txt_gameover: !scr "game over" -txt_caught: !scr "the enemies got you" +txt_caught: !scr "the pyramid claimed you" txt_caughte: +txt_fell: !scr "you fell on level " +txt_felld: !scr "1" +txt_felle: txt_retry: !scr "fire = try again" txt_retrye: txt_mmenu: !scr "m = main menu" diff --git a/play.asm b/play.asm index 0bbe50a..f9c0502 100644 --- a/play.asm +++ b/play.asm @@ -1,15 +1,20 @@ ; ----------------------------------------------------------- -; play.asm - the game: a fresh maze carves itself on screen -; every round (recursive backtracker, a few steps a frame), -; player 1 (joystick 2) runs the corridors toward the exit -; portal, player 2 (joystick 1) rides the wall corners and -; fire spins the four wall pieces around the cursor by 90 -; degrees; the roaming enemies end the round on touch +; play.asm - the game: eight levels of pyramid maze, each one +; cell bigger than the last (4x4 up to 11x11, always square, +; centered on screen); a fresh maze carves itself at the +; start of every level (recursive backtracker, a few steps a +; frame), player 1 (joystick 2) is the trapped worker running +; for the exit, player 2 (joystick 1) is ra: the cursor rides +; the wall corners and fire spins the four wall pieces around +; it by 90 degrees; scarabs roam the corridors on the +; worker's life, and the scorpions also spit venom stings +; down them ; ; the walls, posts and the portal are chars (the screen stays -; the wall data structure), but every actor - runner, cursor, -; bullet, enemies - is a hardware sprite gliding between the -; cells; actor collisions are grid coordinate checks +; the wall data structure), but every actor - worker, cursor, +; bullet, enemies, sting - is a hardware sprite gliding +; between the cells; actor collisions are grid coordinate +; checks ; ; optional extras (see options.asm): ; opt_shoot - player 1's fire shoots a bullet in the last @@ -19,15 +24,34 @@ ; a draining 1/8-step bar on the status row ; ; screen layout: status on row 0 (over the colored raster -; band), the 39x23 maze lattice on rows 1-23, hints on row 24 +; band), the centered lattice on rows 1-23, hints on row 24 ; ----------------------------------------------------------- play_init: jsr clear_screen - +prtext 0, 0, WHITE, txt_t1, 7 - +prtext 0, 7, CYAN, txt_t2, 3 + +prtext 0, 0, WHITE, txt_level, 5 + lda mzsize ; level number 1-8 next to the label + sec + sbc #MZMIN-1 + ora #$30 + sta SCREEN+6 + lda #YELLOW + sta COLRAM+6 +prtext 0, 34, LGREY, txt_goal, txt_goale-txt_goal - +prtext 24, 6, DGREY, txt_hint, txt_hinte-txt_hint + +prtext 24, 4, DGREY, txt_hint, txt_hinte-txt_hint + + lda mzsize ; this level's lattice: a square + sta mzcw + sta mzch + jsr set_maze + lda mzsize ; one more enemy each level, capped + sec + sbc #MZMIN-1 + cmp #NENEMIES + bcc pi_ne + lda #NENEMIES +pi_ne: + sta nenem lda #COL_WALL sta latwcol @@ -60,26 +84,31 @@ play_start: ps_exit: jsr rand8 and #$0f - cmp #MAZE_CH + cmp mzch bcs ps_exit - asl ; cell row -> screen row - clc - adc #2 + asl ; cell row -> screen row (offy + 2*cy + 1) + sec + adc offy sta exrow tax jsr row_ptr - ldy #38 + ldy excol lda #CH_EXIT sta (scrlo),y lda #COL_EXIT sta (collo),y - ; the runner starts in the top-left cell, facing right - lda #1 + ; the worker starts in the top-left cell, facing right + lda offx + clc + adc #1 sta px - sta fdx - lda #2 + lda offy + clc + adc #1 sta py + lda #1 + sta fdx lda #0 sta fdy lda px @@ -90,10 +119,18 @@ ps_exit: ldx #SP_RUN jsr spr_jump - ; the corner cursor starts on the center post - lda #18 + ; ra's cursor starts on the center post + lda mzcw + lsr + asl + clc + adc offx sta kx - lda #11 + lda mzch + lsr + asl + clc + adc offy sta ky lda #COL_HILITE jsr mark_slots @@ -107,9 +144,11 @@ ps_exit: lda #WHITE sta SPRCOL+SP_RUN - sta SPRCOL+SP_CUR - lda #YELLOW sta SPRCOL+SP_SHOT + lda #COL_HILITE ; ra wears the sun's color + sta SPRCOL+SP_CUR + lda #COL_STING + sta SPRCOL+SP_STING jsr spawn_enemies @@ -122,11 +161,9 @@ ps_exit: sta s_on sta sx sta sy + sta es_on sta cool sta rotflash - sta tframe ; the clock starts when the maze is done - sta tsec - sta tmin lda #1 sta cfheld sta rfheld @@ -134,34 +171,101 @@ ps_exit: jmp upd_sprites ; positions, shapes and enables now ; ============================================================ -; maze generation - recursive backtracker over the 19x11 -; cells; the screen itself is the wall data structure +; maze geometry - the lattice is sized and placed from the +; cell counts in mzcw / mzch (the levels are squares, the +; menu backdrop is the full 19x11) ; ============================================================ -; fill rows 1-23 of the lattice: brick walls in latwcol, the -; pivot posts (even col, odd row) in latpcol +; from mzcw/mzch: screen offsets, the exit column, the +; visited-index multiply table, ra's post bounds and the +; enemies' spawn half +set_maze: + lda #19 ; offx = (39 - (2*cw+1)) / 2 = 19 - cw + sec + sbc mzcw + sta offx + lda #12 ; rows 1-23: offy = 12 - ch + sec + sbc mzch + sta offy + lda #0 ; multab[i] = i * mzcw + tax +sm_mul: + sta multab,x + clc + adc mzcw + inx + cpx #MZMAX + bne sm_mul + lda mzcw ; the right border column holds the exit + asl + clc + adc offx + sta excol + sec ; interior posts: offx+2 .. offx+2*cw-2 + sbc #2 + sta kmaxx + lda offx + clc + adc #2 + sta kminx + lda mzch + asl + clc + adc offy + sec + sbc #2 + sta kmaxy + lda offy + clc + adc #2 + sta kminy + lda mzcw ; enemies spawn on the right half + lsr + sta enhalf + rts + +; fill the lattice with sandstone in latwcol, the pivot posts +; (even local column and row) in latpcol draw_lattice: - ldx #1 + lda offy + sta ptmp dl_fill: - stx ptmp + ldx ptmp jsr row_ptr - ldy #38 + lda mzcw + asl + sta lcnt + inc lcnt ; 2*cw+1 chars per lattice row + ldy offx dl_col: lda #CH_WALL sta (scrlo),y lda latwcol sta (collo),y - dey - bpl dl_col - ldx ptmp - inx - cpx #24 - bne dl_fill - ldx #1 ; post rows 1, 3, ... 23 + iny + dec lcnt + bne dl_col + inc ptmp + lda mzch + asl + clc + adc offy ; the bottom lattice row + cmp ptmp + bcs dl_fill + lda offy ; post rows: offy, offy+2, ... + sta ptmp dl_prow: - stx ptmp + ldx ptmp jsr row_ptr - ldy #38 ; post columns 38, 36, ... 0 + lda mzcw + asl + clc + adc offx + tay ; post columns from the right edge down + lda mzcw + sta lcnt + inc lcnt ; cw+1 posts per post row dl_pcol: lda #CH_POST sta (scrlo),y @@ -169,14 +273,23 @@ dl_pcol: sta (collo),y dey dey - bpl dl_pcol - ldx ptmp - inx - inx - cpx #25 - bcc dl_prow + dec lcnt + bne dl_pcol + inc ptmp + inc ptmp + lda mzch + asl + clc + adc offy + cmp ptmp + bcs dl_prow rts +; ============================================================ +; maze generation - recursive backtracker over the cells; +; the screen itself is the wall data structure +; ============================================================ + ; reset the generator: nothing visited, start on cell (0,0) gen_init: ldx #0 @@ -206,17 +319,17 @@ gm_scan: clc adc dxtab,x sta gnx - cmp #MAZE_CW ; unsigned: -1 wraps to $ff and fails too + cmp mzcw ; unsigned: -1 wraps to $ff and fails too bcs gm_next ldx gtd lda gcy clc adc dytab,x sta gny - cmp #MAZE_CH + cmp mzch bcs gm_next ldx gny - lda mul19,x + lda multab,x clc adc gnx tax @@ -285,9 +398,9 @@ gen_maze_full: ; the menu backdrop wants it all at once bcc gen_maze_full rts -gv_mark: ; visited[gcy * 19 + gcx] = 1 +gv_mark: ; visited[gcy * mzcw + gcx] = 1 ldx gcy - lda mul19,x + lda multab,x clc adc gcx tax @@ -295,17 +408,17 @@ gv_mark: ; visited[gcy * 19 + gcx] = 1 sta visited,x rts -carve_cell: ; open the cell (2*gcx+1, 2*gcy+2) +carve_cell: ; open the cell at (offx+2*gcx+1, offy+2*gcy+1) lda gcy asl - clc - adc #2 + sec + adc offy tax jsr row_ptr lda gcx asl - clc - adc #1 + sec + adc offx tay lda #CH_FLOOR sta (scrlo),y @@ -317,8 +430,8 @@ carve_wall: ; open the wall slot toward direction gtd ldx gtd lda gcy asl - clc - adc #2 + sec + adc offy clc adc dytab,x tax @@ -326,8 +439,8 @@ carve_wall: ; open the wall slot toward direction gtd ldx gtd lda gcx asl - clc - adc #1 + sec + adc offx clc adc dxtab,x tay @@ -339,9 +452,9 @@ carve_wall: ; open the wall slot toward direction gtd dxtab: !byte 0, 0, $ff, 1 ; up, down, left, right dytab: !byte $ff, 1, 0, 0 -mul19: !byte 0, 19, 38, 57, 76, 95, 114, 133, 152, 171, 190 -; ---- enemies: random colors, random right-half cells ---- +; ---- enemies: even index = scarab, odd = scorpion; random +; right-half cells, away from the worker's start ---- spawn_enemies: lda #0 sta ei @@ -349,7 +462,7 @@ sn_all: jsr spawn_one inc ei lda ei - cmp #NENEMIES + cmp nenem bne sn_all rts @@ -357,22 +470,22 @@ spawn_one: ; place enemy ei on a random free cell sn_rx: jsr rand8 and #$0f - cmp #MAZE_CH + cmp mzcw bcs sn_rx - clc - adc #MAZE_CW-MAZE_CH ; cell column 8-18: away from the start + cmp enhalf ; keep to the right half + bcc sn_rx asl - clc - adc #1 - sta ttx ; candidate screen column + sec + adc offx ; cell column -> screen column + sta ttx sn_ry: jsr rand8 and #$0f - cmp #MAZE_CH + cmp mzch bcs sn_ry asl - clc - adc #2 + sec + adc offy sta tty ; candidate screen row ldx tty jsr row_ptr @@ -380,7 +493,7 @@ sn_ry: lda (scrlo),y cmp #CH_FLOOR ; only a carved cell will do... bne sn_rx - lda ttx ; ...without the runner on it... + lda ttx ; ...without the worker on it... cmp px bne sn_free lda tty @@ -394,11 +507,10 @@ sn_free: sta ex,x lda tty sta ey,x - jsr rand8 - and #7 + lda ei + and #1 ; the type wears its own color tay - lda ecoltab,y - sta ecol,x + lda etypecol,y sta SPRCOL+SP_EN,x jsr rand8 and #3 @@ -416,14 +528,13 @@ sn_free: tax jmp spr_jump -ecoltab: ; anything but the white runner / grey walls - !byte RED, CYAN, PURPLE, YELLOW, ORANGE, LRED, LBLUE, BLUE +etypecol: !byte COL_SCARAB, COL_SCORP ; ============================================================ ; one frame of play ; ============================================================ play_update: - inc tframe ; the round clock: 50 frames = one second + inc tframe ; the run clock: 50 frames = one second lda tframe cmp #50 bne pu_clock @@ -453,6 +564,9 @@ pu_nocool: jsr upd_enemies lda endflag bne pu_end + jsr upd_sting + lda endflag + bne pu_end jsr upd_cursor jsr upd_flash jsr upd_status @@ -467,6 +581,12 @@ pu_end: pu_win: lda #SFX_WIN jsr sfx_play + lda mzsize ; the last level won: the blessing is earned + cmp #MZMAX + bcs pw_final + inc mzsize ; otherwise the next maze is one cell bigger + +fadeto play_init +pw_final: +fadeto win_init ; ---- player 1: walk the corridors, one cell per step ---- @@ -543,6 +663,15 @@ ur_try: bne 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 + beq ur_go + lda ttx + cmp esx + bne ur_go + lda tty + cmp esy + beq ur_dead +ur_go: lda ttx sta px lda tty @@ -649,12 +778,12 @@ ea_loop: rts ea_next: inx - cpx #NENEMIES + cpx nenem bne ea_loop clc rts -; ---- player 2: the corner cursor + the wall spin ---- +; ---- player 2: ra's corner cursor + the wall spin ---- upd_cursor: jsr read_joy2 sta j2bits @@ -689,26 +818,28 @@ uc_move: bcs uc_down lsr bcs uc_left - cpx #36 ; right: interior posts end at column 36 + cpx kmaxx ; right: the interior posts end here bcs uc_fire inx inx jmp uc_go uc_up: - cpy #4 ; top interior post row is 3 + cpy kminy bcc uc_fire + beq uc_fire dey dey jmp uc_go uc_down: - cpy #21 ; bottom interior post row is 21 + cpy kmaxy bcs uc_fire iny iny jmp uc_go uc_left: - cpx #3 ; left interior post column is 2 + cpx kminx bcc uc_fire + beq uc_fire dex dex uc_go: @@ -849,13 +980,22 @@ so_norun: jsr enemy_at bcs so_yes lda s_on - beq so_no + beq so_nosht lda sx cmp ttx - bne so_no + bne so_nosht lda sy cmp tty beq so_yes +so_nosht: + lda es_on ; the sting blocks a spin too + beq so_no + lda esx + cmp ttx + bne so_no + lda esy + cmp tty + beq so_yes so_no: clc rts @@ -927,10 +1067,11 @@ ue_alive: jsr enemy_step lda endflag bne ue_out + jsr try_sting ue_next: inc ei lda ei - cmp #NENEMIES + cmp nenem bne ue_loop ue_out: rts @@ -970,7 +1111,7 @@ etry_dir: ; A = direction; C clear when it moved (or won) clc adc dytab,x sta tty - lda px ; onto the runner: the round is over + lda px ; onto the worker: the level is over cmp ttx bne en_norun lda py @@ -1020,6 +1161,105 @@ en_move: clc rts +; ---- a scorpion (odd index) spits a venom sting when it +; lines up with the worker; one sting flies at a time ---- +try_sting: + lda ei + and #1 + beq ts_no ; scarabs do not shoot + lda es_on + bne ts_no + jsr rand8 ; menacing, not machine-gun constant + and #3 + bne ts_no + ldx ei + lda ex,x + cmp px + beq ts_col + lda ey,x + cmp py + beq ts_row +ts_no: + rts +ts_col: ; same column: the sting flies vertically + lda #0 + sta esdx + ldy #1 + lda py + cmp ey,x + bcs ts_cgo + ldy #$ff +ts_cgo: + sty esdy + jmp ts_fire +ts_row: ; same row: horizontally + lda #0 + sta esdy + ldy #1 + lda px + cmp ex,x + bcs ts_rgo + ldy #$ff +ts_rgo: + sty esdx +ts_fire: + lda ex,x + sta esx + sta tgc + lda ey,x + sta esy + sta tgr + jsr set_target ; the sprite pops in on the scorpion, + ldx #SP_STING ; not gliding over from the last sting + jsr spr_jump + lda #1 + sta es_on + lda #SFX_SHOT + jmp sfx_play + +; ---- the sting flies a char every other frame; a wall +; swallows it, the worker dies on it ---- +upd_sting: + lda es_on + beq ust_done + lda framectr + and #1 + bne ust_done + lda esx + clc + adc esdx + sta ttx + lda esy + clc + adc esdy + sta tty + lda px ; onto the worker: the level is over + cmp ttx + bne ust_norun + lda py + cmp tty + bne ust_norun + lda #2 + sta endflag + rts +ust_norun: + ldx tty + jsr row_ptr + ldy ttx + lda (scrlo),y + cmp #CH_FLOOR + beq ust_fly + lda #0 ; a wall swallows it + sta es_on + rts +ust_fly: + lda ttx + sta esx + lda tty + sta esy +ust_done: + rts + ; ---- per-frame flashes: the portal spins and blinks, the ; freshly spun walls flash white, the cursor shows the ; recharge state on its own color ---- @@ -1027,7 +1267,7 @@ upd_flash: jsr upd_portal ; cycle the portal glyph ldx exrow jsr row_ptr - ldy #38 + ldy excol lda framectr and #8 bne uf_exw @@ -1055,7 +1295,7 @@ uf_norf: lda #RED jmp uf_cset uf_hot: - lda #WHITE + lda #COL_HILITE uf_cset: sta SPRCOL+SP_CUR rts @@ -1110,7 +1350,7 @@ us_clr: bpl us_clr rts -; ---- the round clock, live on the status row ---- +; ---- the run clock, live on the status row ---- upd_clock: lda tmin jsr two_digits @@ -1137,7 +1377,7 @@ upd_sprites: lsr lsr sta frame8 - ; the runner glides 2 pixels a frame + ; the worker glides 2 pixels a frame lda px sta tgc lda py @@ -1165,6 +1405,16 @@ upd_sprites: jsr set_target ldx #SP_SHOT jsr spr_jump + ; the sting glides 4: a char every other frame, smoothly + lda esx + sta tgc + lda esy + sta tgr + jsr set_target + lda #4 + sta gspd + ldx #SP_STING + jsr spr_glide ; the enemies glide 1 lda #1 sta gspd @@ -1183,9 +1433,9 @@ us_en: jsr spr_glide ldx ptmp inx - cpx #NENEMIES + cpx nenem bne us_en - ; shapes: the runner bobs, each enemy waves on its own beat + ; shapes: the worker bobs, each enemy waves on its own beat lda frame8 and #1 clc @@ -1195,6 +1445,8 @@ us_en: sta SPRPTR+SP_CUR lda #SPR_SHOT sta SPRPTR+SP_SHOT + lda #SPR_STING + sta SPRPTR+SP_STING ldx #0 us_shape: txa @@ -1202,23 +1454,30 @@ us_shape: adc frame8 and #1 sta rtmp - lda #SPR_ENA ; enemies 0-1 are ghosts, 2-3 crawlers - cpx #2 - bcc us_seta + txa + and #1 ; even index = scarab, odd = scorpion + beq us_scar lda #SPR_ENB + bne us_seta ; always taken +us_scar: + lda #SPR_ENA us_seta: clc adc rtmp sta SPRPTR+SP_EN,x inx - cpx #NENEMIES + cpx nenem bne us_shape - ; enable: runner + cursor + the bullet and the live enemies + ; enable: worker + cursor + bullet, sting, live enemies lda #%00000011 ldy s_on beq us_nosht ora #%00000100 us_nosht: + ldy es_on + beq us_nostg + ora #%10000000 +us_nostg: sta rtmp ldx #0 us_alive: @@ -1229,7 +1488,7 @@ us_alive: sta rtmp us_dead: inx - cpx #NENEMIES + cpx nenem bne us_alive lda rtmp sta SPR_EN @@ -1238,18 +1497,31 @@ us_dead: ebit: !byte 8, 16, 32, 64 ; ---- play texts ---- +txt_level: !scr "level" txt_goal: !scr "exit " !byte CH_EXIT txt_goale: -txt_hint: !scr "p1: joy2/wasd p2: joy1/ijkl" +txt_hint: !scr "worker: joy2/wasd ra: joy1/ijkl" txt_hinte: txt_wait: !scr "wait" txt_ready: !scr "ready" ; ---- play variables ---- -px: !byte 0 ; runner position (screen col / row) +mzsize: !byte MZMIN ; this level's cells per side (4-11) +mzcw: !byte 0 ; lattice cells across / down +mzch: !byte 0 +offx: !byte 0 ; screen offset of the lattice +offy: !byte 0 +excol: !byte 0 ; screen column of the right border wall +enhalf: !byte 0 ; enemies spawn from this cell column +nenem: !byte 0 ; live enemy slots this level (1-4) +kminx: !byte 0 ; ra's interior post bounds +kmaxx: !byte 0 +kminy: !byte 0 +kmaxy: !byte 0 +px: !byte 0 ; worker position (screen col / row) py: !byte 0 -fdx: !byte 0 ; runner facing: the gun aims this way +fdx: !byte 0 ; worker facing: the gun aims this way fdy: !byte 0 kx: !byte 0 ; corner cursor position (post col / row) ky: !byte 0 @@ -1262,16 +1534,22 @@ sx: !byte 0 ; bullet position and heading sy: !byte 0 sdx: !byte 0 sdy: !byte 0 -rheld: !byte 0 ; runner direction repeat state +es_on: !byte 0 ; venom sting in flight? +esx: !byte 0 ; sting position and heading +esy: !byte 0 +esdx: !byte 0 +esdy: !byte 0 +rheld: !byte 0 ; worker direction repeat state rtimer: !byte 0 rdir: !byte 0 -rfheld: !byte 0 ; runner fire edge detection state +rfheld: !byte 0 ; worker fire edge detection state cheld: !byte 0 ; cursor direction repeat state ctimer: !byte 0 cdir: !byte 0 cfheld: !byte 0 ; cursor fire edge detection state j2bits: !byte 0 ptmp: !byte 0 ; scratch +lcnt: !byte 0 ; draw_lattice column counter ttx: !byte 0 ; move target (col / row) tty: !byte 0 nch: !byte 0 ; the four slot chars around the cursor @@ -1281,7 +1559,7 @@ wch: !byte 0 hlcol: !byte 0 ; mark_slots color parameter latwcol: !byte 0 ; draw_lattice colors: walls, posts latpcol: !byte 0 -tframe: !byte 0 ; round clock: frame, second, minute +tframe: !byte 0 ; run clock: frame, second, minute tsec: !byte 0 tmin: !byte 0 frame8: !byte 0 ; framectr / 8: the animation beat @@ -1296,10 +1574,10 @@ gtd: !byte 0 gnx: !byte 0 gny: !byte 0 gdlist: !fill 4, 0 -ex: !fill NENEMIES, 0 ; enemy positions, headings, colors +multab: !fill MZMAX, 0 ; row * mzcw for the visited index +ex: !fill NENEMIES, 0 ; enemy positions and headings ey: !fill NENEMIES, 0 edir: !fill NENEMIES, 0 -ecol: !fill NENEMIES, 0 edead: !fill NENEMIES, 0 ; respawn countdown, 0 = alive ; ---- maze generator buffers ---- diff --git a/pyramid.asm b/pyramid.asm new file mode 100644 index 0000000..4295e84 --- /dev/null +++ b/pyramid.asm @@ -0,0 +1,116 @@ +; ----------------------------------------------------------- +; pyramid.asm - the title card between the TTG logo and the +; menu: big BLESSING / OF RA under the rolling gold wash, a +; sandstone pyramid rising below with the trapped worker +; glinting at its peak and the sun of ra pulsing beside it; +; advances to the menu after a few seconds or on fire +; ----------------------------------------------------------- + +PYR_TIME = 250 ; frames before it advances on its own + +pyramid_init: + jsr clear_screen + + +prbig 1, 4, YELLOW, txt_t1, 8 + +prbig 7, 10, ORANGE, txt_t2, 5 + + ; the pyramid: ten block rows, apex on row 13 + lda #0 + sta ptmp +py_prow: + lda #13 + clc + adc ptmp + tax + jsr row_ptr + lda #19 ; row r spans columns 19-r .. 20+r + sec + sbc ptmp + tay + lda ptmp + asl + clc + adc #2 ; 2r+2 blocks on this row + sta rtmp2 +py_pcol: + lda #$a0 + sta (scrlo),y + ldx ptmp + lda pyrcols,x ; sunlit top, shaded base + sta (collo),y + iny + dec rtmp2 + bne py_pcol + inc ptmp + lda ptmp + cmp #10 + bne py_prow + + ; the worker glints at the peak + lda #$51 ; filled dot + sta SCREEN+12*40+19 + lda #WHITE + sta COLRAM+12*40+19 + + ; the sun of ra beside the pyramid + lda #$a0 + sta SCREEN+14*40+32 + sta SCREEN+14*40+33 + sta SCREEN+15*40+31 + sta SCREEN+15*40+32 + sta SCREEN+15*40+33 + sta SCREEN+15*40+34 + sta SCREEN+16*40+32 + sta SCREEN+16*40+33 + + +prtext 24, 5, LGREY, txt_pray, txt_praye-txt_pray + + lda #PYR_TIME + sta pyrt + lda #0 + sta washphase + +setupd pyramid_update + rts + +pyramid_update: + jsr wash_title ; the gold wash rolls down the title + + ; the sun pulses + lda framectr + lsr + lsr + lsr + and #3 + tax + lda sunpulse,x + sta COLRAM+14*40+32 + sta COLRAM+14*40+33 + sta COLRAM+15*40+31 + sta COLRAM+15*40+32 + sta COLRAM+15*40+33 + sta COLRAM+15*40+34 + sta COLRAM+16*40+32 + sta COLRAM+16*40+33 + + ; fire skips, otherwise wait out the timer + jsr start_edge + beq py_tick + +fadeto menu_init +py_tick: + dec pyrt + bne py_done + +fadeto menu_init +py_done: + rts + +; ---- tables and texts ---- +pyrcols: ; pyramid rows top to bottom + !byte YELLOW, YELLOW, ORANGE, ORANGE, ORANGE + !byte ORANGE, ORANGE, BROWN, BROWN, BROWN +sunpulse: ; the sun of ra breathes + !byte YELLOW, YELLOW, WHITE, YELLOW +txt_pray: !scr "a worker prays at the peak..." +txt_praye: + +; ---- pyramid variables ---- +pyrt: !byte 0 ; frames left before auto-advancing diff --git a/rules.asm b/rules.asm deleted file mode 100644 index 2a58e04..0000000 --- a/rules.asm +++ /dev/null @@ -1,56 +0,0 @@ -; ----------------------------------------------------------- -; rules.asm - how to play, one static card; fire returns -; ----------------------------------------------------------- - -rules_init: - jsr clear_screen - - +prbig 1, 10, WHITE, txt_rules, 5 - - +prtext 8, 9, LGREEN, txt_r1, txt_r1e-txt_r1 - +prtext 9, 8, WHITE, txt_r2, txt_r2e-txt_r2 - +prtext 10, 7, WHITE, txt_r3, txt_r3e-txt_r3 - - +prtext 12, 9, LBLUE, txt_r4, txt_r4e-txt_r4 - +prtext 13, 6, WHITE, txt_r5, txt_r5e-txt_r5 - +prtext 14, 6, WHITE, txt_r6, txt_r6e-txt_r6 - +prtext 15, 7, WHITE, txt_r7, txt_r7e-txt_r7 - - +prtext 17, 7, LRED, txt_r8, txt_r8e-txt_r8 - - +setupd rules_update - rts - -rules_update: - ; blink the return prompt - lda framectr - and #32 - bne ru_hide - +prtext 21, 13, YELLOW, txt_return, 13 - jmp ru_input -ru_hide: - +prtext 21, 13, YELLOW, txt_blank, 13 -ru_input: - jsr start_edge - beq ru_stay - +fadeto menu_init -ru_stay: - rts - -; ---- rules texts ---- -txt_r1: !scr "player 1 - joystick 2" -txt_r1e: -txt_r2: !scr "run the maze and escape" -txt_r2e: -txt_r3: !scr "through the green diamond." -txt_r3e: -txt_r4: !scr "player 2 - joystick 1" -txt_r4e: -txt_r5: !scr "ride the corners of the walls;" -txt_r5e: -txt_r6: !scr "fire spins the four wall pieces" -txt_r6e: -txt_r7: !scr "around the cursor by 90 degrees." -txt_r7e: -txt_r8: !scr "avoid the colored enemies!" -txt_r8e: diff --git a/story.asm b/story.asm new file mode 100644 index 0000000..518c0da --- /dev/null +++ b/story.asm @@ -0,0 +1,72 @@ +; ----------------------------------------------------------- +; story.asm - the legend of the trapped worker and ra, plus +; who steers what (the old rules and controls cards merged +; into one); a static card, fire returns to the menu +; ----------------------------------------------------------- + +story_init: + jsr clear_screen + + +prbig 1, 10, WHITE, txt_story, 5 + + +prtext 7, 1, WHITE, txt_s1, txt_s1e-txt_s1 + +prtext 8, 2, WHITE, txt_s2, txt_s2e-txt_s2 + +prtext 9, 4, WHITE, txt_s3, txt_s3e-txt_s3 + +prtext 10, 4, YELLOW, txt_s4, txt_s4e-txt_s4 + +prtext 11, 8, YELLOW, txt_s5, txt_s5e-txt_s5 + + +prtext 13, 2, LRED, txt_s6, txt_s6e-txt_s6 + +prtext 14, 4, LRED, txt_s7, txt_s7e-txt_s7 + + +prtext 16, 6, LGREEN, txt_s8, txt_s8e-txt_s8 + +prtext 17, 5, LGREY, txt_s9, txt_s9e-txt_s9 + +prtext 18, 10, LBLUE, txt_s10, txt_s10e-txt_s10 + +prtext 19, 3, LGREY, txt_s11, txt_s11e-txt_s11 + + +prtext 21, 6, LGREY, txt_s12, txt_s12e-txt_s12 + + +setupd story_update + rts + +story_update: + ; blink the return prompt + lda framectr + and #32 + bne st_hide + +prtext 23, 13, YELLOW, txt_return, 13 + jmp st_input +st_hide: + +prtext 23, 13, YELLOW, txt_blank, 13 +st_input: + jsr start_edge + beq st_stay + +fadeto menu_init +st_stay: + rts + +; ---- story texts ---- +txt_story: !scr "story" +txt_s1: !scr "when the great pyramid rose, a worker" +txt_s1e: +txt_s2: !scr "was sealed in its peak. he prayed to" +txt_s2e: +txt_s3: !scr "ra until the sun god took pity:" +txt_s3e: +txt_s4: !scr "now ra bends the pyramid's walls" +txt_s4e: +txt_s5: !scr "to lead the worker out." +txt_s5e: +txt_s6: !scr "beware the scarabs and the scorpions" +txt_s6e: +txt_s7: !scr "- a scorpion's sting flies far!" +txt_s7e: +txt_s8: !scr "p1 - the worker: joy2 / wasd" +txt_s8e: +txt_s9: !scr "gun: fire or space, if enabled" +txt_s9e: +txt_s10: !scr "p2 - ra: joy1 / ijkl" +txt_s10e: +txt_s11: !scr "fire spins the walls at the cursor" +txt_s11e: +txt_s12: !scr "menus: fire, space or return" +txt_s12e: diff --git a/win.asm b/win.asm index 2e92e53..26d13b0 100644 --- a/win.asm +++ b/win.asm @@ -1,6 +1,6 @@ ; ----------------------------------------------------------- -; win.asm - the escape screen: big ESCAPED with the gold -; wash; fire returns to the main menu +; win.asm - the escape screen after the last level: big +; ESCAPED with the gold wash; fire returns to the main menu ; ----------------------------------------------------------- win_init: @@ -8,8 +8,8 @@ win_init: +prbig 3, 6, YELLOW, txt_escaped, 7 - +prtext 11, 15, WHITE, txt_welldone, txt_welldonee-txt_welldone - +prtext 13, 10, LGREY, txt_beaten, txt_beatene-txt_beaten + +prtext 11, 10, WHITE, txt_welldone, txt_welldonee-txt_welldone + +prtext 13, 5, LGREY, txt_beaten, txt_beatene-txt_beaten ; the round time into the readout: "time: mm:ss" lda tmin @@ -100,9 +100,9 @@ td_ones: ; ---- win texts ---- txt_escaped: !scr "escaped" -txt_welldone: !scr "well done!" +txt_welldone: !scr "ra smiles upon you!" txt_welldonee: -txt_beaten: !scr "you beat labyrintwo" +txt_beaten: !scr "you escaped the pyramid of ra" txt_beatene: txt_time: !scr "time: " txt_timev: !scr "00:00"