Prev: A27A Up: Map Next: A4B8
A399: Checks for scenery collisions
Called every frame to see whether the hero car has left the road or hit something at the roadside.
If the fork is visible and the fork countdown has reached zero the whole job is handed to check_fork_scenery_collisions. Otherwise the last two entries of the centre x-position table say whether the car is on the road, half off it or fully off, and that becomes off_road and the crash spin. Inside a tunnel going off the road means hitting a wall, and the high byte of road_pos picks which one.
Last, the right and then the left object IDs are read from the road buffer, their collision widths looked up, and scenery_hit is called if the car's horizontal position falls inside the object's zone.
Used by the routines at main_loop and drive_attract_demo.
check_scenery_collisions A399 LD HL,$0048 HL = $0048
A39C LD DE,$01D8 DE = $01D8 -- constant indicates road centre?
A39F EXX Bank
A3A0 LD A,($A265) Load fork_visible
A3A3 AND A Set flags
A3A4 JR Z,csc_fork_not_visible_or_fork_in_progress Jump to csc_fork_not_visible_or_fork_in_progress if zero
The fork is visible.
A3A6 LD A,($A266) Load fork_countdown
A3A9 AND A Set flags
A3AA JP Z,check_fork_scenery_collisions Exit via check_fork_scenery_collisions if zero
Currently forking.
A3AD DEC A Decrement the fork_countdown value
A3AE RET Z Return if zero -- can't collide immediately before fork?
Check left hand side of car. $EAFE and $EAFC are the last two words of the page layout_objects fills: the left and right hand x-positions for the nearest road slice, pushed in that order from $EB00 downwards. They are rewritten every frame, which is why they vibrate in the debugger. They say where a roadside object on each side would stand, not that one is there.
csc_fork_not_visible_or_fork_in_progress A3AF LD HL,($EAFE) Read signed word at $EAFE
When car off road left ~ 176 When car half off road left ~ 133 When car in lane 1 ~ 85 When car in lane 2 ~ 38 When car in lane 3 ~ -36 When car in lane 4 ~ -84 When car half off road right ~ -153 When car off road right ~ -186
A3B2 LD A,H Set flags from sign of word: 0 when hero car on left side, 255 if right
A3B3 AND A
A3B4 JR NZ,csc_clear_trigger_lane_change_sfx Jump to csc_clear_trigger_lane_change_sfx if non-zero -- right side
Otherwise left side.
A3B6 PUSH HL Jump to csc_clear_trigger_lane_change_sfx if HL < 64
A3B7 LD BC,$0040
A3BA SBC HL,BC
A3BC POP HL
A3BD JR C,csc_clear_trigger_lane_change_sfx
A3BF PUSH HL Jump to csc_check_right_hand if HL < 106
A3C0 LD C,$6A
A3C2 SBC HL,BC
A3C4 POP HL
A3C5 JR C,csc_check_right_hand
A3C7 LD C,$85 HL -= 133
A3C9 SBC HL,BC
A3CB INC A A++ -- A == 1 => partially off-road (note: A is zero here)
A3CC JR C,csc_store_off_road Jump to csc_store_off_road if HL < 133 (note: INC A doesn't affect carry)
A3CE INC A A++ -- A == 2 => fully off-road
A3CF JR csc_store_off_road Jump to csc_store_off_road
csc_clear_trigger_lane_change_sfx A3D1 XOR A trigger_left_hand_passed_object_sfx = 0. Reached only when the car is alongside the left hand object, which stops the passed-object sound effect from firing
A3D2 LD ($A23D),A
Check right hand side of car. Car off road left ~ (not hit!) Car half off road left ~ 0x170 ish Car in lane 1 ~ 0x164 Car in lane 2 ~ 0x117 Car in lane 3 ~ 0xe0 Car in lane 4 ~ 0x98 Car half off road right ~ 0x4e Car off road right ~ 0x4b
csc_check_right_hand A3D5 LD HL,($EAFC) Read signed word at $EAFC -- the right hand x-position for the nearest slice, where csc_fork_not_visible_or_fork_in_progress read the left
A3D8 LD A,H Set flags from sign of word: 1 when hero car on left side, 0 if right
A3D9 AND A
A3DA LD A,$00 Init value for trigger_right_hand_passed_object_sfx -- this must stop the passed object sfx from playing
A3DC JR NZ,csc_store_sfx_trigger Jump to csc_store_sfx_trigger if non-zero
A3DE PUSH HL Jump to csc_store_sfx_trigger if HL >= 190
A3DF LD BC,$00BE
A3E2 SBC HL,BC
A3E4 POP HL
A3E5 JR NC,csc_store_sfx_trigger
A3E7 PUSH HL Jump to csc_store_off_road if HL >= 142
A3E8 LD BC,$008E
A3EB SBC HL,BC
A3ED POP HL
A3EE JR NC,csc_store_off_road
A3F0 LD C,$7C HL -= 124
A3F2 SBC HL,BC
A3F4 INC A A++ -- A == 1 => partially off-road
A3F5 JR NC,csc_store_off_road Jump to csc_store_off_road if no carry
A3F7 INC A A++ -- A == 2 => fully off-road
A3F8 JR csc_store_off_road Jump to csc_store_off_road
csc_store_sfx_trigger A3FA LD ($A23C),A trigger_right_hand_passed_object_sfx = A
csc_store_off_road A3FD LD ($A23E),A off_road = A -- 0/1/2 => on-road/one wheel off-road/both wheels off-road
A400 AND A Set flags
A401 LD C,$00 C = 0 (not self modified)
A403 JR Z,csc_store_crash_spin Jump if not off road
Otherwise we're off-road.
A405 LD A,($A240) Load road_buffer_offset.lo into A
A408 ADD A,$40 Add 64 so it's the lanes data offset
A40A LD L,A Point HL at road buffer lanes data
A40B LD H,$EE
A40D LD A,(HL) Read lane data byte
A40E BIT 6,A Test bit 6 -- clear for plain road, set for a tunnel,
A410 JR Z,csc_store_crash_spin a fork or a dirt track. Jump if clear
A412 RLA Test bit 7
A413 JR C,csc_store_crash_spin Jump if set
We arrive here if we're far into the tunnel.
A415 BIT 3,A Test bit 3 (was bit 2 before RLA)
A417 LD A,($A26D) Load road_pos high byte
A41A JR Z,csc_a426 Jump if bit 3 clear
A41C LD C,A C = A
A41D LD A,$14 A = 20
A41F EX AF,AF' Bank it
A420 LD A,C A = C & 1
A421 AND $01
A423 JP scenery_hit Exit via scenery_hit
csc_a426 A426 LD HL,$00D1 HL = 209
A429 LD DE,$0195 DE = 405
A42C EXX Unbank from A39F
A42D AND A Set flags -- A is still road_pos high byte, loaded before the jump in; EXX doesn't touch AF
A42E LD C,$01 C = 1
A430 JR Z,csc_hit_tunnel_wall
A432 INC C C = 2
csc_hit_tunnel_wall A433 PUSH BC Preserve BC
A434 LD BC,$0604 Effect 6 (tunnel wall hit), Priority 4
A437 CALL start_sfx Call start_sfx
A43A POP BC Restore BC
csc_store_crash_spin A43B LD A,C A = C
A43C LD ($B3DC),A ($B3DC) = A
A43F EXX
A440 LD ($B396),HL ($B396) = HL
A443 LD ($B3A4),DE ($B3A4) = DE
A447 AND A Set flags from A (was C)
A448 RET NZ Return if non-zero
-- RIGHT SIDE OBJECT HIT CHECKING --
A449 LD A,($A240) Load road_buffer_offset.lo into A
A44C ADD A,$60 Add 96 so it's the right side objects data offset
A44E LD L,A Point HL at road buffer right side objects data
A44F EX AF,AF' Bank road buffer offset
A450 LD H,$EE (cont.)
A452 LD A,($A240) Load road_buffer_offset.lo into A
A455 RLA Top bit -> carry
A456 LD A,(HL) Read a right side object data byte
A457 JR NC,csc_a45a Jump if top bit was clear
A459 INC L L++ -- so if the top bit of the road buffer offset is set we take next byte?
csc_a45a A45A OR (HL) OR in the (next) byte
A45B JR Z,csc_a480 Jump if it's zero -- no object
Build a graphics_defs address.
A45D LD C,A HL = (*addrof_right_hand_objects)[A * 7]
A45E ADD A,A
A45F ADD A,A
A460 ADD A,A
A461 SUB C
A462 LD C,A
A463 LD B,$00
A465 LD HL,($5CFA)
A468 ADD HL,BC
Read collision values.
A469 LD C,(HL) C = *HL++ -- e.g. $5E5B. a higher value
A46A INC HL
A46B LD E,(HL) E = *HL++ -- e.g. $5E5C. a lower value
A46C INC HL
A46D LD A,(HL) A = *HL -- e.g. $5E5D. a speed cap
A46E LD D,B DE = E, BC = C -- B is zero from A463 above
Check for collisions with scenery (right hand side).
A46F LD HL,($EAFC) HL = *$EAFC -- near end of road drawing words (centre)... must be position of car
A472 PUSH HL Jump to csc_a480 if HL >= BC
A473 SBC HL,BC
A475 POP HL
A476 JR NC,csc_a480
A478 SBC HL,DE Jump to csc_a480 if HL < DE
A47A JR C,csc_a480
A47C EX AF,AF' Bank speed cap
A47D XOR A Set flip flag to 0
A47E JR csc_hit_scenery Jump to csc_hit_scenery
csc_a480 A480 EX AF,AF' Unbank road buffer offset or/and bank mystery value in A
-- LEFT SIDE OBJECT HIT CHECKING --
A481 ADD A,$20 Add 32 so it's the left side objects data offset
A483 LD L,A Point HL at road buffer left side objects data
A484 LD H,$EE
A486 LD A,($A240) Load road_buffer_offset.lo into A
A489 RLA Top bit -> carry
A48A LD A,(HL) Read a left side object data byte
A48B JR NC,csc_a48e Jump if top bit of road_buffer_offset.lo was clear
A48D INC L Advance to next data byte
csc_a48e A48E OR (HL) OR in the (next) byte
A48F RET Z Return if combined value is zero -- no object
A490 LD C,A HL = (*addrof_left_hand_objects)[A * 7] -- indexing left hand objects
A491 ADD A,A
A492 ADD A,A
A493 ADD A,A
A494 SUB C
A495 LD C,A
A496 LD B,$00
A498 LD HL,($5D00)
A49B ADD HL,BC
Read collision values.
A49C LD C,(HL) C = *HL++ -- must be min/left hit coord
A49D INC HL
A49E LD E,(HL) E = *HL++ -- must be max/right hit coord
A49F INC HL
A4A0 LD A,(HL) A = *HL -- a speed cap
A4A1 LD D,B DE = E, BC = C -- B is zero from A496 above
Check for collisions with scenery (left hand side).
A4A2 LD HL,($EAFE) HL = *$EAFE -- suspected position of car/road
A4A5 PUSH HL Return if HL < BC
A4A6 SBC HL,BC
A4A8 POP HL
A4A9 RET C
A4AA SBC HL,DE Return if HL >= DE
A4AC RET NC
A4AD EX AF,AF' Bank speed cap
A4AE LD A,$01 Set flip flag to 1
Arrive here if hit scenery, e.g. drove into a tree or a lamp post.
csc_hit_scenery A4B0 PUSH AF Preserve AF -- suspected left hand flag
A4B1 LD BC,$0403 Effect 4 (scenery crash), Priority 3
A4B4 CALL start_sfx Call start_sfx
A4B7 POP AF Restore AF
FALLTHROUGH
Prev: A27A Up: Map Next: A4B8