From 3b238d047207441a9ed4c3ae503f323e04b62bef Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Mon, 16 Mar 2026 22:04:19 +0100 Subject: Wait for items to fall before changing screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit More specifically, whenever the player dies and we need to reset the screen, wait for items to fall in the same way as we do for explosions to disappear. This also brought with it some other changes like removing sprite reset on screen initialization. Signed-off-by: Miquel Sabaté Solà --- src/driver.s | 28 +++++--- src/items.s | 207 +++++++++++++++++++++++++++++++++-------------------------- src/jetpac.s | 2 +- src/player.s | 5 +- 4 files changed, 137 insertions(+), 105 deletions(-) (limited to 'src') diff --git a/src/driver.s b/src/driver.s index 448efa2..f747c4b 100644 --- a/src/driver.s +++ b/src/driver.s @@ -161,15 +161,6 @@ dey bne @bullets_reset_loop - ;; Invalidate all items. - ldx #0 - ldy #Items::POOL_CAPACITY - @items_reset_loop: - sta Items::zp_pool_base, x - NEXT_ITEM_INDEX_X - dey - bne @items_reset_loop - ;; Set that we have done this operation so it's not done in future ;; cycles. lda Driver::zp_flags @@ -324,7 +315,12 @@ lda Explosions::zp_active bne @sprite_cycling - ;; After all the explosions have been done, is any player alive? + ;; Are there still falling items? + lda Items::zp_state + and #$03 + bne @sprite_cycling + + ;; After all the explosions/items have been done, is any player alive? lda Globals::zp_multiplayer and #%00000110 bne @reset_timer @@ -335,6 +331,18 @@ ora #%00000010 sta Globals::zp_flags + ;; Invalidate items, which were skipped on move_sprites_out() on purpose + ;; to keep them after each death. But since we are about to go to the + ;; title screen, now they are no longer useful. + lda #$FF + ldx #0 + ldy #Items::POOL_CAPACITY + @items_reset_loop: + sta Items::zp_pool_base, x + NEXT_ITEM_INDEX_X + dey + bne @items_reset_loop + @reset_timer: ;; Reset the player's timer to enter the game screen again. lda #PLAYER_TIMER_VALUE diff --git a/src/items.s b/src/items.s index 756da2b..e1ebed6 100644 --- a/src/items.s +++ b/src/items.s @@ -10,6 +10,13 @@ .endmacro .scope Items + ;;; + ;; Shadowed variables from the Player scope. This is mainly to be + ;; compatible with cl65. + zp_player_screen_y = $40 ; asan:ignore + zp_player_screen_x = $45 ; asan:ignore + PLAYER_WAIST = $0C + ;; Maximum amount of items allowed on screen at the same time. POOL_CAPACITY = 3 @@ -43,13 +50,12 @@ ;; ;; TODO: stabilize and document. ;; - ;; |GNS- FFAA| + ;; |GNS- --FF| ;; | ;; |- G: the player is grabbing an item ;; |- N: a fuel tank is needed. ;; |- S: there is a fuel tank on screen. (TODO: needed?) ;; |- F: number of falling items. - ;; |- A: number of active items. zp_state = $CA ;; Number of shuttle parts (or fuel tanks) that have been collected so far. @@ -85,11 +91,12 @@ ;; deaths. Hence, they will only be re-initialized on either after a game ;; over, or switching to a new level. .proc init_level + ldx #0 + lda Globals::zp_level_kind bne @other_screens - ;; We are going to allocate two shuttle parts, and hence two items. - lda #2 + lda #0 sta Items::zp_state ;; We haven't collected anything yet, but it's convenient for us to mock @@ -98,51 +105,10 @@ lda #1 sta Items::zp_collected - rts - - @other_screens: - ;; Fuel tanks are needed, that's all. - lda #%01000000 - sta Items::zp_state - - ;; Shuttle parts are counted as "collected". This makes the - ;; implementation on other parts easier. - lda #3 - sta Items::zp_collected - - rts - .endproc - - ;; Initialize which sprites are to appear when initializing the screen. - ;; - ;; NOTE: this should _only_ be called whenever we initialize the - ;; screen. This happens either when switching to it for the first time, but - ;; also after a death. That is, unlike Items::init_level() things here are - ;; reset for good. - .proc init - ;; Initialize the timer. - lda #ITEM_TIMER_LO - sta Items::zp_timer - lda #ITEM_TIMER_HI - sta Items::zp_timer + 1 - - ;; State of the top part of the shuttle. - ldx #0 - ldy #0 - sty Items::zp_pool_base, x - - ;; Has the top shuttle part been collected yet? If not we will allocate - ;; the first slot for it. - lda Items::zp_collected - cmp #3 - bcc @set_top_shuttle - - ;; Invalidate the first slot, as it should not be allocated yet. - lda #$FF + ;; State for the top part of the shuttle. + lda #0 sta Items::zp_pool_base, x - bne @mid_shuttle - @set_top_shuttle: ;; Screen and tile coordinates for the top part of the shuttle. lda #$4F sta Items::zp_pool_base + 1, x @@ -161,19 +127,6 @@ lda #0 sta Items::zp_current_tiles + 2, x - @mid_shuttle: - ;; Has the mid shuttle part been collected yet? If not we will allocate - ;; the second slot for it. - lda Items::zp_collected - cmp #2 - bcc @set_mid_shuttle - - ;; Invalidate the second slot, as it should not be allocated yet. - lda #$FF - sta Items::zp_pool_base + 3, x - bne @invalidate_third - - @set_mid_shuttle: ;; State of the middle part of the shuttle. lda #1 sta Items::zp_pool_base + 3, x @@ -196,10 +149,43 @@ lda #0 sta Items::zp_current_tiles + 6, x + beq @invalidate_third + + @other_screens: + ;; Fuel tanks are needed, that's all. + lda #%01000000 + sta Items::zp_state + + ;; Shuttle parts are counted as "collected". This makes the + ;; implementation on other parts easier. + lda #3 + sta Items::zp_collected + + ;; Invalidate the first and the second slots. + lda #$FF + sta Items::zp_pool_base, x + sta Items::zp_pool_base + 3, x + @invalidate_third: - ;; Always invalidte the third item. - ldy #$FF - sty Items::zp_pool_base + 6, x + ;; Always invalidate the third item. + lda #$FF + sta Items::zp_pool_base + 6, x + + rts + .endproc + + ;; Initialize a fresh screen. + ;; + ;; NOTE: this should _only_ be called whenever we initialize the + ;; screen. This happens either when switching to it for the first time, but + ;; also after a death. That is, unlike Items::init_level() things here are + ;; reset for good. + .proc init + ;; Initialize the timer. + lda #ITEM_TIMER_LO + sta Items::zp_timer + lda #ITEM_TIMER_HI + sta Items::zp_timer + 1 rts .endproc @@ -353,14 +339,14 @@ ;; can be used for collision checking. Note that we are targetting for ;; the center of the player, which feels at a fair point for item ;; interactions. - lda Player::zp_screen_y + lda zp_player_screen_y clc - adc #Player::PLAYER_WAIST + adc #PLAYER_WAIST lsr lsr lsr sta Globals::zp_arg0 - lda Player::zp_screen_x + lda zp_player_screen_x lsr lsr lsr @@ -396,11 +382,11 @@ ;; Follow the player. ;; Neither of the above. Then, just follow the player. - lda Player::zp_screen_y + lda zp_player_screen_y clc adc #8 sta Items::zp_pool_base + 1, x - lda Player::zp_screen_x + lda zp_player_screen_x sta Items::zp_pool_base + 2, x ;; Are we at the zone where we must drop items? @@ -421,9 +407,8 @@ ;; Unset the 'grabbing' bit and increase the number of falling items. lda Items::zp_state and #$7F - clc - adc #$04 sta Items::zp_state + inc Items::zp_state ;; And we force the item to be on the exact X screen position so to ;; adjust from the player's subpixel movement. @@ -485,9 +470,7 @@ ;; Now we unset the 'S' bit, which is unconditionally true regardless of ;; the collection state. That being said, if we still need to collect ;; more fuel tanks (the rocket has all its parts and we have not filled - ;; it with all tanks), then we set the 'N' bit. As a cherry on top, we - ;; take advantge of these operations to also decrease the number of - ;; falling/active items. + ;; it with all tanks), then we set the 'N' bit. lda Items::zp_state ldy Items::zp_collected cpy #3 @@ -497,10 +480,11 @@ ora #$40 @set_new_state: and #%11011111 - sec - sbc #$05 ; NOTE: $04 (falling) + $01 (active) sta Items::zp_state + ;; Decrease the number of falling items. + dec Items::zp_state + ;; Save the index of this free slot. stx Globals::zp_arg3 @@ -516,8 +500,11 @@ ;; Collision checks. @check_collision: - ;; Check collision with the player. Otherwise let's check for background - ;; collision. + ;; Check collision with the player if it's alive. Otherwise let's check + ;; for background collision. + lda Globals::zp_flags + and #$10 + bne @background jsr Items::collides_with_player beq @background @@ -560,12 +547,7 @@ bit Globals::zp_tmp0 bvc @set_modes and #%10111111 - tay - lda Items::zp_state - sec - sbc #$04 - sta Items::zp_state - tya + dec Items::zp_state @set_modes: sta Items::zp_pool_base, x @@ -608,10 +590,7 @@ sta Items::zp_pool_base, x ;; And we need to decrease the number of falling items. - lda Items::zp_state - sec - sbc #$04 - sta Items::zp_state + dec Items::zp_state @preserve_and_next: ldx Items::zp_pool_index @@ -721,11 +700,8 @@ ldx Items::zp_pool_index sta Items::zp_current_tiles + 2, x - ;; Update the state to reflect a new active & falling item. - lda Items::zp_state - clc - adc #5 ; NOTE: #4: falling; #1: active - sta Items::zp_state + ;; Update the state to reflect a new falling item. + inc Items::zp_state rts @@ -773,12 +749,59 @@ rts .endproc + ;; Let go the item from the player if there is one being grabbed. + .proc let_go_on_death + ;; First of all, we need do check if the player was actually holding an + ;; item. + ldx #0 + ldy #Items::POOL_CAPACITY + + @loop: + lda Items::zp_pool_base, x + cmp #$FF + beq @next + and #$80 + bne @found + + @next: + NEXT_ITEM_INDEX_X + dey + bne @loop + rts + + @found: + ;; The player was indeed grabbing an item. Then unset the P flag and set + ;; the F one. + lda Items::zp_pool_base, x + and #$7F + ora #$40 + sta Items::zp_pool_base, x + ;; TODO: what if it died while on the ground + + ;; Unset the 'grabbing' bit, and increase the number of falling items. + lda Items::zp_state + and #$7F + sta Items::zp_state + inc Items::zp_state + + rts + .endproc + ;; Collect an item as indexed by 'zp_pool_index'. This function assumes that ;; the item is already valid. ;; ;; NOTE: the 'y' register is preserved. .proc collect ldx Items::zp_pool_index + + ;; If the collected item was actually falling down, decrease the number + ;; of falling items. + lda Items::zp_pool_base, x + and #$40 + beq @invalidate + dec Items::zp_state + + @invalidate: lda #$FF sta Items::zp_pool_base, x diff --git a/src/jetpac.s b/src/jetpac.s index d2ed90b..2d397ac 100644 --- a/src/jetpac.s +++ b/src/jetpac.s @@ -42,10 +42,10 @@ .include "background.s" .include "prng.s" .include "explosions.s" +.include "items.s" .include "player.s" .include "enemies.s" .include "bullets.s" -.include "items.s" .include "title.s" .include "over.s" .include "driver.s" diff --git a/src/player.s b/src/player.s index 48b0bb0..fa649c6 100644 --- a/src/player.s +++ b/src/player.s @@ -834,6 +834,9 @@ ;; That's just german for "the Bart, the". .proc die_bart_die + ;; If the player was grabbing an item when it happened, let go of it. + jsr Items::let_go_on_death + ;; Decrement the life. lda Globals::zp_multiplayer and #$01 @@ -841,8 +844,6 @@ dec Player::zp_lifes, x bne @nmi_update - ;; TODO: if it was grabbing an item, turn it into 'falling' state. - ;; If this poor guy is over, then mark it in the multiplayer bitmap. cpx #0 bne @player_2_over -- cgit v1.2.3