aboutsummaryrefslogtreecommitdiff
path: root/space/src/states/bullets.s
blob: af74ce7d3da7eef54b9a1ab7b5f4e6fdaa430db3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
;;;
;; Implements the pool of bullets available when pressing B. The main code
;; should just want to call `init` for initializing the memory space, and then
;; `update` on each main game loop iteration. The following addresses are
;; reserved:
;;      -> $40-$41: internal data.
;;      -> $0208-$0213: OAM data (this is a pool of three sprites).
;;
;; NOTE: the code in here touch the addresses on OAM directly, instead of
;; keeping them in some internal addresses on zero page. This is usually not a
;; good idea, and more so if we were to do collision checking and stuff like
;; that. That being said, because of its simplicity, it's not that big of a
;; deal.
.scope Bullets
    ;; The number of bullets shown on screen for the current frame.
    m_bullets_screen = $40

    ;; Frame counter. See `FRAMES` below.
    m_frames = $41

    ;; How many frames have to pass to allow the user to shoot another bullet
    ;; after the previous one.
    FRAMES = 15

    ;; Initializes the pool of bullets.
    .proc init
        ;; Initializing variables.

        lda #0
        sta m_bullets_screen

        lda #FRAMES
        sta m_frames

        ;; Set X and Y positions off-screen for the three available slots.

        lda #$ff
        sta $208
        sta $20C
        sta $210
        sta $20B
        sta $20F
        sta $213

        ;; All three sprites are instances of the same tile.

        lda #4
        sta $209
        sta $20D
        sta $210

        ;; Nothing special for these sprites, set attributes to 0.

        lda #0
        sta $20A
        sta $20E
        sta $212

        rts
    .endproc

    ;; Update bullets on screen, check if a new one has to be shown, etc.
    .proc update
        ;; If the frame counter has the same value as our allowed one, we can go
        ;; into the `bullets_pressed` subroutine, otherwise we will skip it
        ;; altogether.
        lda m_frames
        cmp #FRAMES
        beq @check
        inc m_frames
        jmp @position
    @check:
        jsr bullets_pressed
    @position:
        jsr update_positions

        rts
    .endproc

    ;; Shows a new bullet right in front of the ship if the player requested it
    ;; and it is possible.
    .proc bullets_pressed
        ;; If we reached the maximum of bullets on screen, return early.
        lda m_bullets_screen
        cmp #3
        bne :+
        rts
    :
        ;; If the B button is not pressed, return early.
        lda #Joypad::BUTTON_B
        and Joypad::m_buttons1
        bne :+
        rts
    :
        ;; The following code will loop through the three bullet sprites and
        ;; find one that is free (off-screen). If one could be found, it will
        ;; assign it to the ship's position.
        ldx #0
    @loop:
        ;; Is the Y position on this sprite $FF? If so then it's free and we can
        ;; jump into @set so we re-initialize the position for this bullet.
        lda $208, x
        cmp #$FF
        beq @set

        ;; Oops, that was not the case. At this point, is this the last bullet?
        ;; If so, we can go to the @end, otherwise we increase the index and try
        ;; again.
        cpx #8
        beq @end
        inx
        inx
        inx
        inx
        jmp @loop
    @set:
        ;; At the current index we have a bullet to initialize. Hence, give it
        ;; the Y value from the player and the X one (+4 so it's at the center
        ;; of the ship on the X axis).
        lda Player::m_screen_y
        sta $208, x
        lda Player::m_screen_x
        clc
        adc #4
        inx
        inx
        inx
        sta $208, x

        ;; Reset the `m_frames` so to disallow too many bullets being shot at
        ;; once, and increate the `m_bullets_screen` variable.
        lda #0
        sta m_frames
        inc m_bullets_screen
    @end:
        rts
    .endproc

    ;; Updates the positions from the bullets being shown on screen. If one
    ;; bullet goes off screen, then it's properly freed so it can be picked up
    ;; by another B press.
    .proc update_positions
        ldx #0
    @loop:
        ;; Do nothing if the bullet is free.
        lda $208, x
        cmp #$FF
        beq @next

        ;; The indexed bullet is shown on screen, and we should make it move
        ;; upward by 10 units. That being said, if its Y position is lesser or
        ;; equal than 10, we can directly free it now. Otherwise, just update
        ;; the Y value by performing this subtraction.
        cmp #10
        bcc @free
        beq @free
        sec
        sbc #10
        jmp @save
    @free:
        ;; This bullet should be freed, decrease `m_bullets_screen` and set `a`
        ;; to an off-screen value.
        dec m_bullets_screen
        lda #$FF
    @save:
        ;; Either way you reach this, in `a` we have the Y value to be stored.
        sta $208, x
    @next:
        ;; If we are already at the last bullet, jump to the @end. Otherwise
        ;; increase the index and loop again.
        cpx #8
        beq @end
        inx
        inx
        inx
        inx
        jmp @loop
    @end:
        rts
    .endproc
.endscope