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
|
.segment "CODE"
.scope Background
;; Screen coordinate on the Y axis where elements can begin to appear (e.g.
;; upper bound for new enemies, starting point for falling items, etc.).
UPPER_MARGIN_Y_COORD = $1A
;; Screen coordinates on the Y axis for the ground.
GROUND_Y_COORD = $C8
;; Returns whether the given tile position collides with a background
;; platform or not. It expects two memory arguments: zp_arg0 and zp_arg1,
;; which contain the Y and the X tile coordinates respectively.
;;
;; The boolean value is directly set into the `a` register; but the memory
;; will not be written in any way. Hence, you can still rely on the old
;; `zp_arg0` and `zp_arg1` values even after calling this function.
;;
;; The 'y' register is preserved.
.proc collides
;; We iterate first on the rows, as that's how the data on
;; `Background::platforms` is actually sorted by.
ldx #0
@row_check:
lda Background::platforms, x
;; Is this the end of the list?
cmp #$FF
bne @continue
;; Yes, begone!
lda #0
rts
@continue:
;; Prepare for either row check (which require one 'inx') or the
;; next iteration (which require three 'inx').
inx
;; The first byte is the vertical tile coordinate. If that doesn't
;; match, go for the next one.
cmp Globals::zp_arg0
beq @column_check
inx
inx
jmp @row_check
@column_check:
;; Check the left edge.
;;
;; NOTE: small optimization on sky and ground which have $00 for the
;; left edge.
lda Background::platforms, x
beq @yes
cmp Globals::zp_arg1
bcs @no
;; Check the right edge.
inx
lda Background::platforms, x
cmp Globals::zp_arg1
bcc @no
@yes:
lda #1
rts
@no:
lda #0
rts
.endproc
;; To make them easier to traverse when performing background collision
;; checking, each platform is laid out in tile coordinates and spanning
;; three bytes: tile row, tile column beginning, tile column end.
;;
;; NOTE: this is wholeheartedly distinct to implementations like in
;; github.com/mssola/code.nes. In there, and in examples such as the ones in
;; `scroll`, a map is built up when loading the background and collision
;; checking is a matter of determining the metatile index on that map and
;; that's it. Here it's not possible because we are operating at the tile
;; level, not a metatile level. This in turn has been done this way to
;; better replicate the original experience. Mapping tiles would be a huge
;; hit on memory, so we have to do things in a more rudimentary way.
;; Fortunately for us, this is a rather small list, and traversing it each
;; time is not too expensive.
platforms:
;; Top of the screen.
.byte $03, $00, $FF
;; Left platform.
.byte $09, $18, $1D
;; Center platform.
.byte $0C, $03, $08
;; Right platform.
.byte $0F, $0F, $12
;; Ground.
.byte $19, $00, $FF
;; End of the list.
.byte $FF
.endscope
|