aboutsummaryrefslogtreecommitdiff
path: root/src/explosions.s
diff options
context:
space:
mode:
Diffstat (limited to 'src/explosions.s')
-rw-r--r--src/explosions.s224
1 files changed, 224 insertions, 0 deletions
diff --git a/src/explosions.s b/src/explosions.s
new file mode 100644
index 0000000..6e04668
--- /dev/null
+++ b/src/explosions.s
@@ -0,0 +1,224 @@
+.segment "CODE"
+
+;; Assuming that the 'x' register indexes an explosion on its pool, increment
+;; the register as many times as to point to the next one. Bound checking is not
+;; performed, it's up to the caller to implement that.
+.macro NEXT_EXPLOSION_INDEX_X
+ inx
+ inx
+ inx
+.endmacro
+
+.scope Explosions
+ ;; Maximum amount of explosions allowed on screen at the same time. At
+ ;; maximum it can happen that all enemies explode at the same time (3), plus
+ ;; some animation (e.g. player blasting off).
+ EXPLOSIONS_POOL_CAPACITY = 3 + 1
+
+ ;; The capacity of the explosions pool in bytes.
+ EXPLOSIONS_POOL_CAPACITY_BYTES = EXPLOSIONS_POOL_CAPACITY * 3
+
+ ;; Base address for the pool of explosions used on this game. The pool has
+ ;; #EXPLOSIONS_POOL_CAPACITY capacity of explosion objects where each one is
+ ;; 3 bytes long:
+ ;; 1. State:
+ ;; |Att- TTTT|; where:
+ ;; |
+ ;; |- A: active if 1; inactive if 0.
+ ;; |- t: tile ID.
+ ;; |- T: timer.
+ ;; 2. Y coordinate.
+ ;; 3. X coordinate.
+ zp_pool_base = $70 ; asan:reserve EXPLOSIONS_POOL_CAPACITY_BYTES
+
+ ;; 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
+ ldx #0
+ ldy #EXPLOSIONS_POOL_CAPACITY
+
+ @loop:
+ sta Explosions::zp_pool_base, x
+ NEXT_EXPLOSION_INDEX_X
+
+ dey
+ bne @loop
+
+ rts
+ .endproc
+
+ ;; Create a new explosion object on the Y coordinates on `Globals::zp_arg2`
+ ;; and the X coordinates on `Globals::zp_arg3`.
+ ;;
+ ;; NOTE: in the (extremely unlikely) case that no free spot is found on the
+ ;; pool of objects, then nothing is done (i.e. no boom boom).
+ .proc create
+ ldx #0
+ ldy #EXPLOSIONS_POOL_CAPACITY
+
+ @loop:
+ ;; If it's already active, then skip this spot.
+ lda Explosions::zp_pool_base, x
+ and #$80
+ bne @next
+
+ ;; We've got a free spot! Then just activate it and set the timer. After
+ ;; that set the coordinates as given in the arguments.
+ lda #($80 | FRAME_TIME)
+ sta Explosions::zp_pool_base, x
+ lda Globals::zp_arg2
+ sta Explosions::zp_pool_base + 1, x
+ lda Globals::zp_arg3
+ sta Explosions::zp_pool_base + 2, x
+ rts
+
+ @next:
+ NEXT_EXPLOSION_INDEX_X
+
+ dey
+ bne @loop
+
+ rts
+ .endproc
+
+ ;; Update all active explosions.
+ .proc update
+ ldx #0
+ ldy #EXPLOSIONS_POOL_CAPACITY
+
+ ;; We need the 'y' register free to do faster register operations.
+ sty Globals::zp_idx
+
+ @loop:
+ ;; Is it active?
+ lda Explosions::zp_pool_base, x
+ tay
+ and #$80
+ beq @next
+
+ ;; Yes! Decrement the timer and check if it ran out.
+ dey
+ tya
+ and #$0F
+ bne @set_and_next
+
+ ;; Timer's up! Go to the next explosion phase and check if we are done.
+ tya
+ clc
+ adc #$20
+ tay
+ and #$60
+ cmp #$60
+ beq @explosion_done
+
+ ;; We are not done yet. Then grab the high nibble as stored on the 'y'
+ ;; register and reset the timer on the low nibble. That's our new value.
+ tya
+ ora #FRAME_TIME
+ sta Explosions::zp_pool_base, x
+ bne @next
+
+ @explosion_done:
+ ;; We are actually done. Invalidate the explosion.
+ ldy #0
+ __fallthrough__ @set_and_next
+
+ @set_and_next:
+ sty Explosions::zp_pool_base, x
+
+ @next:
+ NEXT_EXPLOSION_INDEX_X
+
+ dec Globals::zp_idx
+ bne @loop
+
+ rts
+ .endproc
+
+ ;; Allocate an explosion indexed by 'x' from the `Explosions::zp_pool_base`
+ ;; buffer, and set it to OAM-reserved space indexed via 'y'.
+ ;;
+ ;; The 'y' register will be updated by increasing its value by 16,
+ ;; indicating the amount of bytes allocated in OAM space.
+ ;;
+ ;; The 'x' register will be preserved.
+ ;;
+ ;; The 'Globals::zp_tmp0' and the 'Globals::zp_tmp1' memory regions are also
+ ;; tampered by this function.
+ ;;
+ ;; NOTE: this function assumes that the explosion is in a valid
+ ;; state. That's up to the caller to check before calling this function.
+ .proc allocate_x_y
+ ;; Preserve both indices.
+ sty Globals::zp_tmp0
+ stx Globals::zp_tmp1
+
+ ;; Y coordinates for each sprite of the explosion.
+ lda Explosions::zp_pool_base + 1, x
+ sta OAM::m_sprites, y ; top left
+ sta OAM::m_sprites + 4, y ; top right
+ clc
+ adc #8
+ sta OAM::m_sprites + 8, y ; bottom left
+ sta OAM::m_sprites + 12, y ; bottom right
+
+ ;; Select the tile ID. It depends on the phase as defined in the 'state'
+ ;; value.
+ lda Explosions::zp_pool_base, x
+ and #$60
+ beq @first
+ cmp #$20
+ beq @second
+ lda #$B0
+ ldx #$C0
+ bne @set_tile
+ @first:
+ lda #$70
+ ldx #$80
+ bne @set_tile
+ @second:
+ lda #$90
+ ldx #$A0
+ __fallthrough__ @set_tile
+
+ @set_tile:
+ sta OAM::m_sprites + 1, y ; top left
+ clc
+ adc #1
+ sta OAM::m_sprites + 5, y ; top right
+
+ txa
+ sta OAM::m_sprites + 9, y ; bottom left
+ inx
+ txa
+ sta OAM::m_sprites + 13, y ; bottom right
+
+ ;; No special attributes.
+ lda #0
+ sta OAM::m_sprites + 2, y ; top left
+ sta OAM::m_sprites + 6, y ; top right
+ sta OAM::m_sprites + 10, y ; bottom left
+ sta OAM::m_sprites + 14, y ; bottom right
+
+ ;; The X-coordinate for each sprite.
+ ldx Globals::zp_tmp1
+ lda Explosions::zp_pool_base + 2, x ; top left
+ sta OAM::m_sprites + 3, y
+ sta OAM::m_sprites + 11, y ; bottom left
+ clc
+ adc #8
+ sta OAM::m_sprites + 7, y ; top right
+ sta OAM::m_sprites + 15, y ; bottom right
+
+ ;; And update the 'y' register to notify 16 bytes were stored.
+ lda Globals::zp_tmp0
+ clc
+ adc #16
+ tay
+
+ rts
+ .endproc
+.endscope