aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/driver.s89
-rw-r--r--src/enemies.s108
-rw-r--r--src/explosions.s14
-rw-r--r--src/player.s32
4 files changed, 228 insertions, 15 deletions
diff --git a/src/driver.s b/src/driver.s
index 8d6ee52..8a0d452 100644
--- a/src/driver.s
+++ b/src/driver.s
@@ -2,12 +2,18 @@
.scope Driver
;; Timer for the player to be able to pick up the joypad upon entering the
- ;; game.
+ ;; game (either when transitioning from the title or when losing a life).
;;
;; NOTE: this memory address is shared with `zp_title_timer`, as they can
;; never conflict with each other.
zp_player_timer = $30 ; asan:ignore
- PLAYER_TIMER_VALUE = HZ * 2
+ PLAYER_TIMER_FULL_VALUE = HZ * 3
+ PLAYER_TIMER_DEV_VALUE = HZ / 2
+ .ifdef PARTIAL
+ PLAYER_TIMER_VALUE = PLAYER_TIMER_DEV_VALUE
+ .else
+ PLAYER_TIMER_VALUE = PLAYER_TIMER_FULL_VALUE
+ .endif
.ifdef PAL
;; Frame counter which resets every 5 frames.
@@ -32,6 +38,12 @@
;; Same as `zp_next_bullet_cycle` but for enemies.
zp_next_enemy_cycle = $37
+ ;; Whether sprites have already been moved out in the 'move_sprites_out'
+ ;; situation. It's probably a waste of resources to spend a full byte for
+ ;; this, but I didn't see where to put it either, and we still have plenty
+ ;; of RAM left.
+ zp_moved_out = $38
+
;; Switch from the title screen to the main screen. Note that this function
;; is to be called with the PPU disabled. If that's not the case, then it
;; will set the proper values to disable it on the next `nmi` call and set
@@ -65,11 +77,7 @@
sta PPU::zp_mask
;; Setup the player timer.
- .ifdef PARTIAL
- lda #1
- .else
- lda #PLAYER_TIMER_VALUE
- .endif
+ lda #PLAYER_TIMER_VALUE
sta zp_player_timer
;; Mark the state of the game as "game". That is, the player has
@@ -82,14 +90,48 @@
rts
.endproc
+ ;; Move enemies and bullets out of the screen. This is done by setting the
+ ;; 'inactive' state for each object.
+ .proc move_sprites_out
+ ldx #0
+ lda #$FF
+
+ ;; Invalidate all enemies.
+ ldy #Enemies::ENEMIES_POOL_CAPACITY
+ @enemies_reset_loop:
+ sta Enemies::zp_enemies_pool_base, x
+ NEXT_ENEMY_INDEX_X
+ dey
+ bne @enemies_reset_loop
+
+ ;; Invalidate all bullets.
+ ldy #Bullets::BULLETS_POOL_CAPACITY
+ @bullets_reset_loop:
+ sta Bullets::zp_bullets_pool_base, x
+ NEXT_BULLET_INDEX_X
+ dey
+ bne @bullets_reset_loop
+
+ ;; Set that we have done this operation so it's not done in future
+ ;; cycles.
+ lda #1
+ sta Driver::zp_moved_out
+
+ rts
+ .endproc
+
.proc update
+ ;; If the player timer is over, jump to the game immediately. Otherwise
+ ;; decrement the counter.
lda zp_player_timer
beq @game
dec zp_player_timer
beq @load_player
+ ;; TODO: items falling down.
;; TODO: blinking of the selected player (every HZ count?).
+
rts
@load_player:
@@ -98,15 +140,22 @@
jsr Enemies::init
jsr Explosions::init
- ;; Initialize pause timer.
+ ;; Initialize pause timer and whether sprites have been moved out of the
+ ;; screen.
lda #0
sta zp_pause_timer
+ sta Driver::zp_moved_out
;; Initialize variables for sprite cycling.
sta zp_next_bullet_cycle
sta zp_next_enemy_cycle
@game:
+ ;; Has the player died?
+ lda Globals::zp_flags
+ and #$10
+ bne @do_minimal_update
+
;; Check if the player is toggling the `pause` state.
lda #(Joypad::BUTTON_START | Joypad::BUTTON_SELECT)
and Joypad::zp_buttons1
@@ -158,10 +207,32 @@
jsr Player::update
jsr Bullets::update
jsr Enemies::update
+ @do_minimal_update:
jsr Explosions::update
- ;; TODO: check if player has died
+ ;; Has the player died? If it is dead, then we need to remove all
+ ;; sprites except for objects and explosions, and whenever
+ ;; explosions/items are done moving we can set the timer again to start
+ ;; over with the game screen.
+ lda Globals::zp_flags
+ and #$10
+ beq @sprite_cycling
+
+ ;; Invalidate bullets and enemies if we haven't already.
+ lda Driver::zp_moved_out
+ bne @check_explosions
+ jsr move_sprites_out
+
+ @check_explosions:
+ ;; Are there still active explosions?
+ lda Explosions::zp_active
+ bne @sprite_cycling
+
+ ;; Nope! Then set the player's timer.
+ lda #PLAYER_TIMER_VALUE
+ sta zp_player_timer
+ @sprite_cycling:
__fallthrough__ sprite_cycling
.endproc
diff --git a/src/enemies.s b/src/enemies.s
index 5acadc5..0279958 100644
--- a/src/enemies.s
+++ b/src/enemies.s
@@ -86,6 +86,15 @@
CURRENT_TILES_BYTES = ENEMIES_POOL_CAPACITY * 4
zp_current_tiles = $F0 ; asan:reserve CURRENT_TILES_BYTES
+ ;; Cached values for the tile coordinates from the player. This is set
+ ;; before enemy update, and it's then used during collision check for each
+ ;; enemy.
+ zp_player_tile_left = $FC
+ zp_player_tile_right = $FD
+ zp_player_tile_top = $FE
+ zp_player_tile_waist = $FF
+ zp_player_tile_bottom = $CF
+
;; Values for the counter of enemies that fall.
;;
;; NOTE: values for this have to fit into a nibble.
@@ -234,6 +243,45 @@
.proc update
ldx #0
+ ;; Save the player's tile coordinates now as it will be useful/faster
+ ;; for collision checking with each enemy.
+ lda Player::zp_screen_y
+ tay
+ lsr
+ lsr
+ lsr
+ sta Enemies::zp_player_tile_top
+ tya
+ clc
+ adc #Player::PLAYER_WAIST
+ lsr
+ lsr
+ lsr
+ sta Enemies::zp_player_tile_waist
+ tya
+ clc
+ adc #Player::PLAYER_HEIGHT
+ lsr
+ lsr
+ lsr
+ sta Enemies::zp_player_tile_bottom
+
+ lda Player::zp_screen_x
+ tay
+ clc
+ adc #Player::LEFT_OFFSET
+ lsr
+ lsr
+ lsr
+ sta Enemies::zp_player_tile_left
+ tya
+ clc
+ adc #(Player::PLAYER_WIDTH / 2)
+ lsr
+ lsr
+ lsr
+ sta Enemies::zp_player_tile_right
+
;; The loop index will be moved out of the 'y' register since movement
;; handlers might need to use it. Note that we loop over all the pool
;; instead of just deciding on active ones. This is just to give dead
@@ -330,7 +378,14 @@
lsr
sta Enemies::zp_current_tiles + 1, x
- ;; TODO: collision with player
+ ;; Does this enemy collide with the player?
+ jsr Enemies::collides_with_player
+ beq @increase_index_next
+
+ ;; Ooops, the player just kicked the bucket! Call the handlers for the
+ ;; enemy and the player and return early.
+ jsr Enemies::bite_the_dust
+ JAL Player::die_bart_die
@increase_index_next:
;; Move the 'x' register to the current enemy for this iteration.
@@ -486,12 +541,59 @@
rts
.endproc
+ ;; Sets 'a' to 1 if the current enemy collides with the player, 0 otherwise.
+ .proc collides_with_player
+ ;; Top left/right are done only once because the top of the player is
+ ;; just the head, which in anchored to one side. Hence, depending on
+ ;; where the player is heading, we will check for top left or right.
+ bit Player::zp_state
+ bvs @set_left
+ lda Enemies::zp_player_tile_right
+ bne @store_top
+ @set_left:
+ lda Enemies::zp_player_tile_left
+ @store_top:
+ sta Globals::zp_arg1
+ lda Enemies::zp_player_tile_top
+ sta Globals::zp_arg0
+ jsr collides
+ bne @end
+
+ ;; Waist left
+ lda Enemies::zp_player_tile_left
+ sta Globals::zp_arg1
+ lda Enemies::zp_player_tile_waist
+ sta Globals::zp_arg0
+ jsr collides
+ bne @end
+
+ ;; Bottom left
+ lda Enemies::zp_player_tile_bottom
+ sta Globals::zp_arg0
+ jsr collides
+ bne @end
+
+ ;; Waist right
+ lda Enemies::zp_player_tile_right
+ sta Globals::zp_arg1
+ lda Enemies::zp_player_tile_waist
+ sta Globals::zp_arg0
+ jsr collides
+ bne @end
+
+ ;; Bottom right
+ lda Enemies::zp_player_tile_bottom
+ sta Globals::zp_arg0
+ jsr collides
+
+ @end:
+ rts
+ .endproc
+
;; The enemy has been set to dust, remove it.
.proc bite_the_dust
dec Enemies::zp_enemies_pool_size
- ;; TODO: this assumes we are coming from within Enemies always. What
- ;; about impacting bullets?
ldx Enemies::zp_pool_index
;; Invalidate this enemy.
diff --git a/src/explosions.s b/src/explosions.s
index 6e04668..58a700e 100644
--- a/src/explosions.s
+++ b/src/explosions.s
@@ -31,19 +31,22 @@
;; 3. X coordinate.
zp_pool_base = $70 ; asan:reserve EXPLOSIONS_POOL_CAPACITY_BYTES
+ ;; Number of active explosions at the moment.
+ zp_active = $7C
+
;; The amount of time each explosion frame will take.
FRAME_TIME = HZ / 20
;; Initialize the pool of explosions for the game.
.proc init
lda #0
+ sta Explosions::zp_active
+
ldx #0
ldy #EXPLOSIONS_POOL_CAPACITY
-
@loop:
sta Explosions::zp_pool_base, x
NEXT_EXPLOSION_INDEX_X
-
dey
bne @loop
@@ -73,6 +76,9 @@
sta Explosions::zp_pool_base + 1, x
lda Globals::zp_arg3
sta Explosions::zp_pool_base + 2, x
+
+ ;; Increase the number of active explosions and quit.
+ inc Explosions::zp_active
rts
@next:
@@ -122,7 +128,9 @@
bne @next
@explosion_done:
- ;; We are actually done. Invalidate the explosion.
+ ;; We are actually done. Decrement the number of active explosions and
+ ;; invalidate this one.
+ dec Explosions::zp_active
ldy #0
__fallthrough__ @set_and_next
diff --git a/src/player.s b/src/player.s
index df2ee68..a821f0e 100644
--- a/src/player.s
+++ b/src/player.s
@@ -114,6 +114,11 @@
;; Initialize the player's sprite. Note that for the sprite to look
;; correctly on screen you still need to call `Player::update` afterwards.
.proc init
+ ;; Make sure that the 'dead' bit from the global flags is zeroed out.
+ lda Globals::zp_flags
+ and #%11101111
+ sta Globals::zp_flags
+
;; Initial state.
lda #%01000100
sta zp_state
@@ -822,4 +827,31 @@
rts
.endproc
+
+ ;; That's just german for "the Bart, the".
+ .proc die_bart_die
+ ;; TODO: dec lifes
+
+ ;; Move the player's sprites out of the screen.
+ ldx #0
+ lda #$FF
+ sta OAM::m_sprites, x
+ sta OAM::m_sprites + 4, x
+ sta OAM::m_sprites + 8, x
+ sta OAM::m_sprites + 12, x
+ sta OAM::m_sprites + 16, x
+ sta OAM::m_sprites + 20, x
+
+ ;; Set the player as dead.
+ lda #$10
+ ora Globals::zp_flags
+ sta Globals::zp_flags
+
+ ;; Create an explosion.
+ lda Player::zp_screen_y
+ sta Globals::zp_arg2
+ lda Player::zp_screen_x
+ sta Globals::zp_arg3
+ JAL Explosions::create
+ .endproc
.endscope