aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.nasm/segments.txt2
-rw-r--r--CHANGELOG.md2
-rw-r--r--include/asm.s5
-rw-r--r--src/assets.s2
-rw-r--r--src/driver.s8
-rw-r--r--src/items.s110
-rw-r--r--src/jetpac.s1
-rw-r--r--src/over.s58
8 files changed, 152 insertions, 36 deletions
diff --git a/.nasm/segments.txt b/.nasm/segments.txt
index 403b691..ffbe519 100644
--- a/.nasm/segments.txt
+++ b/.nasm/segments.txt
@@ -1,4 +1,4 @@
- HEADER: 16/16 (100%)
-- ROM0: 8066/32762 (24.62%)
+- ROM0: 8238/32762 (25.14%)
- ROMV: 6/6 (100%)
- ROM2: 8192/8192 (100%)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f0fbde8..11057c3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,7 +24,7 @@ adapted to the reality of the NES/Famicom. This means:
hard to do and probably not worth it. In the end: different machine, different
rules. Hence, bullets are handled in a similar way as other games for the
NES/Famicom, even if it's not particularly close to the original.
-- As an homage to Donkey Kong 64, you can **collect a coin** after completing 16
+- As an homage to Donkey Kong 64, you can **collect a coin** after completing 8
stages. This coin features a chameleon as a reference to SUSE, since I
originally bootstrapped this project during [Hackweek
23](https://hackweek.opensuse.org/projects/port-the-jetpac-game-to-the-nes).
diff --git a/include/asm.s b/include/asm.s
index e44aedf..09229f9 100644
--- a/include/asm.s
+++ b/include/asm.s
@@ -1,8 +1,3 @@
-;; Sanity check for the 'LEVEL' build parameter.
-.if !(LEVEL >= 0 && LEVEL < 8)
- .error "You have defined a bad 'LEVEL' value, it should be between 0 and 7 (both included)"
-.endif
-
;; Jump And Link: jump to subroutine but use the return address that the caller
;; had whenever the given subroutine runs `rts`. In other words, "link" the
;; return address from the caller to the callee.
diff --git a/src/assets.s b/src/assets.s
index cfcddbf..26f6546 100644
--- a/src/assets.s
+++ b/src/assets.s
@@ -180,7 +180,7 @@
;; 2: enemy 2, fuel & bonuses
.byte $0F, $16, $24, $28
;; 3: SUSE easter egg
- .byte $0F, $16, $10, $2B
+ .byte $0F, $16, $1B, $2B
.endproc
;; Having 2KB for screen data is quite wasteful, but since it's such a
diff --git a/src/driver.s b/src/driver.s
index e1c2eb7..bfa6294 100644
--- a/src/driver.s
+++ b/src/driver.s
@@ -323,12 +323,16 @@
and #%00000110
bne @reset_timer
- ;; No! Toggle the game over bit.
- ;; TODO: missing the coin game over.
+ ;; No! Set the game over bit (with or without coin).
lda Globals::zp_flags
ora #%00000010
sta Globals::zp_flags
+ lda Items::zp_state
+ and #$04
+ beq @invalidate_items
+ inc Globals::zp_flags
+ @invalidate_items:
;; 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.
diff --git a/src/items.s b/src/items.s
index 731756d..de0a28e 100644
--- a/src/items.s
+++ b/src/items.s
@@ -44,7 +44,7 @@
;; |- F: falling.
;; |- D: dropping: together with 'falling', but the player cannot re-grab it.
;; |- C: 1: collectable (i.e. disappears on collision); 0: part (i.e. follows the player)
- ;; |- K: object kind (00: high shuttle; 01: mid shuttle; 10: fuel; 11: regular item; 100: coin)
+ ;; |- K: object kind (000: high shuttle; 001: mid shuttle; 010: fuel; 011: regular item; 100: coin)
;;
;; 2. Y coordinate.
;; 3. X coordinate.
@@ -72,11 +72,12 @@
;; Bitmap which holds different boolean values for the state of items in
;; general.
;;
- ;; |GNS- --FF|
+ ;; |GNS- -CFF|
;; |
;; |- G: the player is Grabbing an item
;; |- N: a fuel tank is Needed.
;; |- S: there is a fuel tank on Screen.
+ ;; |- C: SUSE's Coin has been collected.
;; |- F: number of Falling items.
zp_state = $CA
@@ -128,7 +129,9 @@
lda Globals::zp_level_kind
bne @other_screens
- lda #0
+ ;; Zero out the state except for the 'C' bit.
+ lda Items::zp_state
+ and #$04
sta Items::zp_state
;; We haven't collected anything yet, but it's convenient for us to mock
@@ -179,13 +182,16 @@
;; Palettes.
lda #0
- sta Items::zp_current_tiles + 6, x
+ sta Items::zp_current_tiles + 5, x
- beq @invalidate_third
+ beq @coin_or_invalidate_third
@other_screens:
- ;; Fuel tanks are needed, that's all.
- lda #%01000000
+ ;; Zero out the state except for the 'C' bit. The 'N' bit needs to be
+ ;; set always.
+ lda Items::zp_state
+ and #$04
+ ora #$40
sta Items::zp_state
;; Shuttle parts are counted as "collected". This makes the
@@ -197,6 +203,46 @@
lda #$FF
sta Items::zp_pool_base, x
sta Items::zp_pool_base + 3, x
+ bne @invalidate_third
+
+ @coin_or_invalidate_third:
+ ;; If we have finished the game once, and we have not collected the
+ ;; SUSE's coin yet, let it be.
+ lda Globals::zp_level
+ cmp #8
+ bne @invalidate_third
+ lda Items::zp_state
+ and #$04
+ bne @invalidate_third
+
+ ;; SUSE's coin will fall from the sky. Hence, compute this new item as a
+ ;; falling one.
+ inc Items::zp_state
+
+ ;; SUSE's coin can be Collected, is identified by the '100' kind, and is
+ ;; in a Falling state.
+ lda #%01001100
+ sta Items::zp_pool_base + 6, x
+
+ ;; Falling from the sky at the right platform.
+ lda #Background::UPPER_MARGIN_Y_COORD
+ sta Items::zp_pool_base + 7, x
+ lsr
+ lsr
+ lsr
+ sta Items::zp_current_tiles + 6, x
+ lda #$D0
+ sta Items::zp_pool_base + 8, x
+ lsr
+ lsr
+ lsr
+ sta Items::zp_current_tiles + 7, x
+
+ ;; Default palette, the tile ID is simply ignored.
+ lda #0
+ sta Items::zp_current_tiles + 8, x
+
+ rts
@invalidate_third:
;; Always invalidate the third item.
@@ -248,26 +294,23 @@
cmp #$01
bne @do_fuel_or_regular
lda #$06
- bne @no_attributes
+ beq @do_fuel_or_regular
+
+ @no_attributes:
+ sta Globals::zp_arg0
+ lda #0
+ sta Globals::zp_arg1
+ JAL allocate_metasprite_x_y
@do_fuel_or_regular:
- ;; Is it a fuel tank?
+ ;; Switch statement for the kind of item (regular, fuel, coin).
lda Items::zp_pool_base, x
- and #$03
+ and #$07
cmp #2
- bne @regular
+ beq @fuel
cmp #4
beq @coin
- ;; Then just pick the tile from the fuel tank and pick the right
- ;; palette.
- lda #$0C
- sta Globals::zp_arg0
- lda #2
- sta Globals::zp_arg1
- JAL allocate_metasprite_x_y
-
- @regular:
;; This is a regular item
lda Items::zp_current_tiles + 2, x
lsr
@@ -284,12 +327,19 @@
sta Globals::zp_arg0
JAL allocate_metasprite_x_y
+ @fuel:
+ ;; Then just pick the tile from the fuel tank and pick the right
+ ;; palette.
+ lda #$0C
+ sta Globals::zp_arg0
+ lda #2
+ sta Globals::zp_arg1
+ JAL allocate_metasprite_x_y
+
@coin:
lda #$0A
-
- @no_attributes:
sta Globals::zp_arg0
- lda #0
+ lda #3
sta Globals::zp_arg1
JAL allocate_metasprite_x_y
@@ -709,8 +759,6 @@
;; We start by generating a new state. If the state is asking for a fuel
;; tank, let it be. Otherwise it will be a regular item.
- ;;
- ;; TODO: coin support.
lda Items::zp_state
and #$40
beq @regular
@@ -862,6 +910,18 @@
.proc collect
ldx Items::zp_pool_index
+ ;; Are we collecting the one and only SUSE coin?!
+ lda Items::zp_pool_base, x
+ and #$07
+ cmp #4
+ bne @check_fall
+
+ ;; Hell, yeah!
+ lda Items::zp_state
+ ora #$04
+ sta Items::zp_state
+
+ @check_fall:
;; If the collected item was actually falling down, decrease the number
;; of falling items.
lda Items::zp_pool_base, x
diff --git a/src/jetpac.s b/src/jetpac.s
index 2d397ac..f6a53a9 100644
--- a/src/jetpac.s
+++ b/src/jetpac.s
@@ -129,6 +129,7 @@
sta Joypad::zp_buttons
sta Joypad::zp_prev
sta Player::zp_state
+ sta Items::zp_state
;; Initialize the level. We allow the build system to pass its own value for
;; this in `LEVEL`, just in case we want to debug the enemy of a specific
diff --git a/src/over.s b/src/over.s
index cb59ef8..cca59ae 100644
--- a/src/over.s
+++ b/src/over.s
@@ -67,9 +67,16 @@
@do_render:
jsr Over::clear_out_screen
- ;; TODO: coin game over.
+ lda Globals::zp_flags
+ and #$03
+ cmp #2
+ beq @regular
+ jsr Over::render_coin_game_over
+ jmp @next
+ @regular:
jsr Over::render_regular_game_over
+ @next:
;; Enable back the PPU (only background).
lda #%00001110
sta PPU::zp_mask
@@ -234,4 +241,53 @@
;; "OVER"
.byte $29, $30, $1F, $2C, $FF
.endproc
+
+ ;; Render the "Game over" in the case the player has collected SUSE's coin.
+ ;; TODO: see how much it can be merged
+ ;; TODO: not centered nor fully realized.
+ .proc render_coin_game_over
+ ;; Set the position.
+ bit PPU::m_status
+ ldx #$29
+ stx PPU::m_address
+ ldx #$6C
+ stx PPU::m_address
+
+ ;; And just iterate over the "message" until we reach the end of string
+ ;; $FF character.
+ ldx #0
+ @message_loop:
+ lda message, x
+ cmp #$FF
+ beq @out
+ sta PPU::m_data
+ inx
+ bne @message_loop
+
+ @out:
+ ;; Reset attributes for the end of the message.
+ bit PPU::m_status
+ ldx #$2B
+ stx PPU::m_address
+ ldx #$D5
+ stx PPU::m_address
+ lda #0
+ sta PPU::m_data
+ sta PPU::m_data
+ sta PPU::m_data
+
+ rts
+
+ message:
+ ;; "YOU "
+ .byte $33, $29, $2F, $00
+ ;; "ARE "
+ .byte $1B, $2C, $1F, $00
+ ;; "A "
+ .byte $1B, $00
+ ;; "SUPER "
+ .byte $2D, $2F, $2A, $1F, $2C, $00
+ ;; "PLAYER!" TODO
+ .byte $2A, $26, $1B, $33, $1F, $2C, $FF
+ .endproc
.endscope