| ← 9CA6 | Map | 9F9D → |
9CB3: Per-frame prize/effect object animation manager
|
Runs once per frame from the main loop, as a priority chain of prize and effect blocks.
Each block is gated by its own arm flag. Block 1 runs when armed and does not end the chain. Of blocks 2 to 11, the first one armed runs and returns, so later blocks wait for a later frame.
The chain can arm a sound, lose a ball or re-serve one.
|
|||
| manage_objects | 9CB3 | LD A,(capture_slide) | Block 1, the ball-capture slide, which the orb arms. Run only while the slide is armed. |
| 9CB6 | OR A | ||
| 9CB7 | JR Z,fx_bat_sprite_missile | ||
| 9CB9 | LD IX,ball_a_object | Force ball A's sprite record active so the captured ball stays drawn. | |
| 9CBD | SET 0,(IX+$08) | ||
| 9CC1 | LD A,(bat_side) | Bat on the left? Take the leftward slide; otherwise drag right. | |
| 9CC4 | OR A | ||
| 9CC5 | JR NZ,fx_slide_left | ||
| 9CC7 | LD A,$05 | Bat on the right: drag the ball toward it. | |
| 9CC9 | ADD A,(IX+$00) | ||
| 9CCC | LD (IX+$00),A | ||
| 9CCF | CP $EA | Reached the losing edge behind the bat? | |
| 9CD1 | JR C,fx_bat_sprite_missile | ||
| 9CD3 | XOR A | Arrived: clear the capture flags. | |
| 9CD4 | LD (capture_slide),A | ||
| 9CD7 | LD (capture_active),A | ||
| 9CDA | CALL lose_ball | Lose the ball. | |
| 9CDD | JR fx_bat_sprite_missile | ||
| fx_slide_left | 9CDF | LD A,(IX+$00) | Bat on the left: drag the captured ball toward it. |
| 9CE2 | SUB $05 | ||
| 9CE4 | LD (IX+$00),A | ||
| 9CE7 | CP $06 | ||
| 9CE9 | JR NC,fx_bat_sprite_missile | ||
| 9CEB | XOR A | Past the left edge: clear the capture flags. | |
| 9CEC | LD (capture_slide),A | ||
| 9CEF | LD (capture_active),A | ||
| 9CF2 | CALL lose_ball | ||
| fx_bat_sprite_missile | 9CF5 | LD A,(fx_bat_missile_arm) | Block 2: swap the bat to the missile-bat sprite. Armed by the missile prize. |
| 9CF8 | OR A | ||
| 9CF9 | JR Z,fx_bat_sprite_normal | ||
| 9CFB | LD IX,object_table | Point the bat sprite at the missile-bat bitmap. | |
| 9CFF | LD HL,slot_bat_missile | ||
| 9D02 | LD (IX+$06),L | ||
| 9D05 | LD (IX+$07),H | ||
| 9D08 | XOR A | ||
| 9D09 | LD (fx_bat_missile_arm),A | ||
| 9D0C | RET | ||
| fx_bat_sprite_normal | 9D0D | LD A,(fx_bat_normal_arm) | Block 3: restore the bat to its normal sprite. Armed when the missile bat ends: at the launch, at the loss of the last ball, or when another prize is taken. |
| 9D10 | OR A | ||
| 9D11 | JR Z,fx_obj2_retract | ||
| 9D13 | LD IX,object_table | ||
| 9D17 | LD HL,slot_bat_normal | Point the bat sprite back at the normal bat bitmap. | |
| 9D1A | LD (IX+$06),L | ||
| 9D1D | LD (IX+$07),H | ||
| 9D20 | XOR A | ||
| 9D21 | LD (fx_bat_normal_arm),A | ||
| 9D24 | RET | ||
| fx_obj2_retract | 9D25 | LD A,(fx_retract_arm) | Block 4: retract animation for the secondary object, 16 frames. |
| 9D28 | OR A | ||
| 9D29 | JR Z,fx_expand_grow | ||
| 9D2B | CP $01 | ||
| 9D2D | JR NZ,manage_objects_0 | ||
| 9D2F | LD A,$01 | First frame only: arm the death sound and end the missile shot. Clear the two-ball flag and the missile-shot flag. | |
| 9D31 | LD (death_burst_counter),A | ||
| 9D34 | XOR A | ||
| 9D35 | LD (ball_b_in_play),A | ||
| 9D38 | LD (missile_shot),A | ||
| manage_objects_0 | 9D3B | LD A,(fx_retract_arm) | Look up this frame's sprite in the burst frame table, two bytes per frame. |
| 9D3E | ADD A,A | ||
| 9D3F | LD HL,anim_death_burst | ||
| 9D42 | LD E,A | ||
| 9D43 | LD D,$00 | ||
| 9D45 | ADD HL,DE | ||
| 9D46 | LD E,(HL) | ||
| 9D47 | INC HL | ||
| 9D48 | LD D,(HL) | ||
| 9D49 | LD IX,obj2_object | Set the secondary object to that frame's sprite. | |
| 9D4D | LD (IX+$06),E | ||
| 9D50 | LD (IX+$07),D | ||
| 9D53 | LD HL,fx_retract_arm | ||
| 9D56 | INC (HL) | ||
| 9D57 | LD A,(HL) | ||
| 9D58 | CP $11 | Advance the frame counter through frames 1–16, done when it steps past the last. | |
| 9D5A | RET NZ | ||
| 9D5B | XOR A | ||
| 9D5C | LD (fx_retract_arm),A | ||
| 9D5F | RES 0,(IX+$08) | Finished: deactivate the secondary object. | |
| 9D63 | RET | ||
| fx_expand_grow | 9D64 | LD A,(fx_grow_arm) | Block 5: bat-expand grow. Armed by the expand prize; attaches the extension below the bat over 8 frames. |
| 9D67 | OR A | ||
| 9D68 | JR Z,fx_expand_shrink | ||
| 9D6A | CP $01 | ||
| 9D6C | JR NZ,manage_objects_1 | ||
| 9D6E | LD A,$01 | First frame: arm the bat-grows sound. | |
| 9D70 | LD (sfx_grow),A | ||
| 9D73 | LD HL,bat_ext_active | Activate the extension and seed its position from the bat's. | |
| 9D76 | SET 0,(HL) | ||
| 9D78 | LD HL,(object_table) | ||
| 9D7B | LD (bat_ext_object),HL | ||
| 9D7E | LD HL,slot_bat_exp_top | Point the bat at the top half of the expanded-bat sprites and the extension at the bottom half. | |
| 9D81 | LD (bat_sprite_ptr),HL | ||
| 9D84 | LD HL,slot_bat_exp_bottom | ||
| 9D87 | LD (bat_ext_sprite_ptr),HL | ||
| manage_objects_1 | 9D8A | LD IX,object_table | While the bat top is below the ceiling of its travel, nudge the bat up to make room. This frame grows by a single pixel. |
| 9D8E | LD A,(IX+$01) | ||
| 9D91 | CP $26 | ||
| 9D93 | JR C,manage_objects_2 | ||
| 9D95 | DEC (IX+$01) | ||
| 9D98 | LD B,$01 | ||
| 9D9A | JR manage_objects_3 | ||
| manage_objects_2 | 9D9C | LD B,$02 | With no room to make, the extension grows at the faster rate. |
| manage_objects_3 | 9D9E | LD IX,bat_ext_object | Grow the extension downward to the floor of the bat's travel, B px this frame. |
| manage_objects_4 | 9DA2 | LD A,(IX+$01) | |
| 9DA5 | CP $9A | ||
| 9DA7 | JR NC,manage_objects_5 | ||
| 9DA9 | INC (IX+$01) | ||
| 9DAC | DJNZ manage_objects_4 | ||
| 9DAE | JR manage_objects_6 | ||
| manage_objects_5 | 9DB0 | LD IX,object_table | At that limit, stop the growth and back the bat up a pixel. |
| 9DB4 | DEC (IX+$01) | ||
| manage_objects_6 | 9DB7 | LD HL,fx_grow_arm | Count this frame of the grow. |
| 9DBA | INC (HL) | ||
| 9DBB | LD A,(HL) | ||
| 9DBC | CP $09 | The counter reaches its limit on the 8th frame, which finishes the grow and clears the flag. | |
| 9DBE | RET NZ | ||
| 9DBF | LD (HL),$00 | ||
| 9DC1 | RET | ||
| fx_expand_shrink | 9DC2 | LD A,(fx_shrink_arm) | Block 6: bat-expand shrink, the reverse of block 5 over 8 frames. Armed when the last ball is lost, or another prize is taken, while the bat is expanded. |
| 9DC5 | OR A | ||
| 9DC6 | JR Z,fx_bomb_detonate | ||
| 9DC8 | CP $01 | ||
| 9DCA | JR NZ,manage_objects_7 | ||
| 9DCC | LD A,$01 | First frame: arm the bat-shrinks sound. | |
| 9DCE | LD (sfx_shrink),A | ||
| manage_objects_7 | 9DD1 | LD IX,object_table | Each frame the bat moves down 1 px and the extension pulls up 1 px. |
| 9DD5 | INC (IX+$01) | ||
| 9DD8 | LD IX,bat_ext_object | ||
| 9DDC | DEC (IX+$01) | ||
| 9DDF | LD HL,fx_shrink_arm | The counter reaches its limit on the 8th frame, which finishes the shrink. | |
| 9DE2 | INC (HL) | ||
| 9DE3 | LD A,(HL) | ||
| 9DE4 | CP $09 | ||
| 9DE6 | RET NZ | ||
| 9DE7 | LD (HL),$00 | ||
| 9DE9 | XOR A | ||
| 9DEA | LD (expand_active),A | Clear the expand active flag. | |
| 9DED | LD HL,bat_ext_active | Deactivate the extension. | |
| 9DF0 | RES 0,(HL) | ||
| 9DF2 | LD HL,slot_bat_normal | Restore the normal bat sprite. | |
| 9DF5 | LD (bat_sprite_ptr),HL | ||
| 9DF8 | RET | ||
| fx_bomb_detonate | 9DF9 | LD A,(fx_bomb_arm) | Block 7: bomb detonation animation, 16 frames on the secondary object. Armed by the bomb prize. |
| 9DFC | OR A | ||
| 9DFD | JR Z,fx_death_respawn | ||
| 9DFF | CP $01 | ||
| 9E01 | JR NZ,manage_objects_8 | ||
| 9E03 | LD A,$01 | First frame: arm the bomb sound. | |
| 9E05 | LD (sfx_bomb),A | ||
| manage_objects_8 | 9E08 | LD A,(fx_bomb_arm) | Read the frame counter the two tests below branch on. |
| 9E0B | CP $08 | ||
| 9E0D | JR NZ,manage_objects_9 | ||
| 9E0F | CALL detonate_bomb | At frame 8, detonate the 3×3 blast. | |
| manage_objects_9 | 9E12 | LD A,(fx_bomb_arm) | Step the secondary object's sprite through the burst frame table. |
| 9E15 | ADD A,A | ||
| 9E16 | LD HL,anim_death_burst | ||
| 9E19 | LD E,A | ||
| 9E1A | LD D,$00 | ||
| 9E1C | ADD HL,DE | ||
| 9E1D | LD E,(HL) | ||
| 9E1E | INC HL | ||
| 9E1F | LD D,(HL) | ||
| 9E20 | LD IX,obj2_object | ||
| 9E24 | LD (IX+$06),E | ||
| 9E27 | LD (IX+$07),D | ||
| 9E2A | SET 0,(IX+$08) | Keep the secondary object active and count the frame, 16 in all. After the last, clear the flag. | |
| 9E2E | LD HL,fx_bomb_arm | ||
| 9E31 | INC (HL) | ||
| 9E32 | LD A,(HL) | ||
| 9E33 | CP $11 | ||
| 9E35 | RET NZ | ||
| 9E36 | XOR A | ||
| 9E37 | LD (fx_bomb_arm),A | ||
| 9E3A | RES 0,(IX+$08) | Done: deactivate the secondary object. | |
| 9E3E | RET | ||
| fx_death_respawn | 9E3F | LD A,(death_pause_timer) | Block 8: the ball-loss death-and-respawn sequence, 99 frames. Armed at the end of the ball loss. The death-pause timer freezes the bat the whole time. |
| 9E42 | OR A | ||
| 9E43 | JR Z,fx_ball_gem | ||
| 9E45 | CP $01 | ||
| 9E47 | JR NZ,manage_objects_10 | ||
| 9E49 | LD A,$01 | First frame: arm the death sound. | |
| 9E4B | LD (death_burst_counter),A | ||
| manage_objects_10 | 9E4E | LD IX,object_table | Point at the bat object and read the frame counter. |
| 9E52 | LD A,(death_pause_timer) | ||
| 9E55 | CP $11 | When the explosion ends, deactivate the bat. | |
| 9E57 | JR NZ,manage_objects_11 | ||
| 9E59 | RES 0,(IX+$08) | ||
| manage_objects_11 | 9E5D | CP $11 | Frames 1–16: animate the bat sprite through the explosion frames of the burst frame table. |
| 9E5F | JR NC,manage_objects_12 | ||
| 9E61 | LD A,(death_pause_timer) | ||
| 9E64 | ADD A,A | ||
| 9E65 | LD HL,anim_death_burst | ||
| 9E68 | LD E,A | ||
| 9E69 | LD D,$00 | ||
| 9E6B | ADD HL,DE | ||
| 9E6C | LD E,(HL) | ||
| 9E6D | INC HL | ||
| 9E6E | LD D,(HL) | ||
| 9E6F | LD (IX+$06),E | ||
| 9E72 | LD (IX+$07),D | ||
| 9E75 | SET 0,(IX+$08) | ||
| manage_objects_12 | 9E79 | LD HL,death_pause_timer | Hold until the post-death pause elapses. |
| 9E7C | INC (HL) | ||
| 9E7D | LD A,(HL) | ||
| 9E7E | CP $64 | ||
| 9E80 | RET NZ | ||
| 9E81 | XOR A | ||
| 9E82 | LD (death_pause_timer),A | ||
| 9E85 | CALL rearm_serve | Re-arm the pool to serve the next ball. | |
| 9E88 | RET | ||
| fx_ball_gem | 9E89 | LD A,(ball_gem_frame) | Block 9: spawn and fly the ball gem – the wave-script bonus target that pays ball B when ball A strikes it. The ball gem frame counter drives the spawn frames, then the in-flight update. |
| 9E8C | OR A | ||
| 9E8D | JR Z,fx_ball_b_spawn | ||
| 9E8F | LD IX,obj2_object | Activate the secondary object and look up this frame's sprite in the ball gem frame table, two bytes per frame. | |
| 9E93 | SET 0,(IX+$08) | ||
| 9E97 | ADD A,A | ||
| 9E98 | LD E,A | ||
| 9E99 | LD D,$00 | ||
| 9E9B | LD HL,anim_ball_gem | ||
| 9E9E | ADD HL,DE | ||
| 9E9F | LD C,(HL) | ||
| 9EA0 | INC HL | ||
| 9EA1 | LD B,(HL) | ||
| 9EA2 | LD (IX+$06),C | ||
| 9EA5 | LD (IX+$07),B | ||
| 9EA8 | LD HL,ball_gem_frame | ||
| 9EAB | INC (HL) | ||
| 9EAC | LD A,(HL) | At the end of the sequence the counter loops back to the start of the in-flight phase, which then repeats. | |
| 9EAD | CP $15 | ||
| 9EAF | JR NZ,manage_objects_13 | ||
| 9EB1 | LD (HL),$09 | ||
| manage_objects_13 | 9EB3 | CP $09 | While the gem still spawns – return until it starts to travel. |
| 9EB5 | RET C | ||
| 9EB6 | LD C,$02 | ||
| 9EB8 | LD IY,obj2_record | Advance the gem in flight one travel step. It wanders on diagonal headings only. | |
| 9EBC | LD A,$01 | ||
| 9EBE | CALL pick_wander_heading | ||
| 9EC1 | CALL object_avoid_walls | ||
| 9EC4 | CALL move_object | ||
| 9EC7 | CALL inhabitant_offscreen | ||
| 9ECA | INC (IY+$09) | Count the gem's travel steps. When the count reaches its span, end the flight. | |
| 9ECD | JR NZ,manage_objects_14 | ||
| 9ECF | INC (IY+$0A) | ||
| 9ED2 | LD A,(IY+$0A) | ||
| 9ED5 | CP $03 | ||
| 9ED7 | JR NZ,manage_objects_14 | ||
| 9ED9 | XOR A | ||
| 9EDA | LD (ball_gem_frame),A | Stop the gem and arm the despawn animation, block 11. | |
| 9EDD | INC A | ||
| 9EDE | LD (fx_despawn_arm),A | ||
| 9EE1 | RET | ||
| manage_objects_14 | 9EE2 | LD A,(ball_a_on_bat) | In flight: return while ball A is on the bat or captured. |
| 9EE5 | OR A | ||
| 9EE6 | RET NZ | ||
| 9EE7 | LD A,(capture_active) | ||
| 9EEA | OR A | ||
| 9EEB | RET NZ | ||
| 9EEC | LD A,$02 | Set the overlap operands and test the gem against ball A. | |
| 9EEE | LD (overlap_slot_1),A | ||
| 9EF1 | LD A,$01 | ||
| 9EF3 | LD (overlap_slot_2),A | ||
| 9EF6 | CALL overlap_pair | ||
| 9EF9 | OR A | ||
| 9EFA | RET Z | ||
| 9EFB | XOR A | ||
| 9EFC | LD (ball_gem_frame),A | On an overlap with ball A, end the flight and arm the second-ball spawn, block 10. | |
| 9EFF | INC A | ||
| 9F00 | LD (fx_spawn_ball_arm),A | ||
| 9F03 | RET | ||
| fx_ball_b_spawn | 9F04 | LD A,(fx_spawn_ball_arm) | Block 10: spawn ball B after a 9-frame sprite animation on the secondary object. Armed when ball A strikes the ball gem. |
| 9F07 | OR A | ||
| 9F08 | JR Z,fx_obj2_despawn | ||
| 9F0A | CP $01 | ||
| 9F0C | JR NZ,manage_objects_15 | ||
| 9F0E | CALL sfx_ball_b_spawn | First frame: play the spawn sound. | |
| manage_objects_15 | 9F11 | LD A,(fx_spawn_ball_arm) | Step the secondary object's sprite from the spawn frame table. |
| 9F14 | ADD A,A | ||
| 9F15 | LD E,A | ||
| 9F16 | LD D,$00 | ||
| 9F18 | LD HL,anim_ball_b_spawn | ||
| 9F1B | ADD HL,DE | ||
| 9F1C | LD C,(HL) | ||
| 9F1D | INC HL | ||
| 9F1E | LD B,(HL) | ||
| 9F1F | LD IX,obj2_object | ||
| 9F23 | LD (IX+$06),C | ||
| 9F26 | LD (IX+$07),B | ||
| 9F29 | LD HL,fx_spawn_ball_arm | ||
| 9F2C | INC (HL) | Count the frame. Return until the animation ends, then clear the counter. | |
| 9F2D | LD A,(HL) | ||
| 9F2E | CP $0A | ||
| 9F30 | RET NZ | ||
| 9F31 | LD (HL),$00 | ||
| 9F33 | LD A,$01 | Set the two-ball flag. | |
| 9F35 | LD (ball_b_in_play),A | ||
| 9F38 | LD A,(ball_a_heading) | Ball B launches on the heading opposite ball A's. | |
| 9F3B | ADD A,$24 | ||
| 9F3D | CP $90 | ||
| 9F3F | JR C,manage_objects_16 | ||
| 9F41 | SUB $48 | A result past the top of the heading range wraps back into it. | |
| manage_objects_16 | 9F43 | LD (ball_b_heading),A | Store the result as ball B's own heading. |
| 9F46 | LD A,$01 | Make ball B the active ball. | |
| 9F48 | LD (active_ball),A | ||
| 9F4B | CALL set_ball_velocity | Derive its velocity and copy ball A's speed into ball B. Clear ball B's re-hit gate, deflect cooldown, bounce count and catch bypass, and its pending heading with the flag that applies it. | |
| 9F4E | LD A,(ball_a_speed) | ||
| 9F51 | LD (ball_b_speed),A | ||
| 9F54 | XOR A | ||
| 9F55 | LD (ball_b_move_gate),A | ||
| 9F58 | LD (ball_b_deflect_cooldown),A | ||
| 9F5B | LD (ball_b_bounce_count),A | ||
| 9F5E | LD (ball_b_skip_catch),A | ||
| 9F61 | LD (pending_heading_b),A | ||
| 9F64 | LD (apply_heading_b),A | ||
| 9F67 | RET | ||
| fx_obj2_despawn | 9F68 | LD A,(fx_despawn_arm) | Block 11: secondary-object despawn animation, 16 frames. Armed when the ball gem expires without a hit. |
| 9F6B | OR A | ||
| 9F6C | RET Z | ||
| 9F6D | CP $01 | ||
| 9F6F | JR NZ,manage_objects_17 | ||
| 9F71 | LD A,$01 | First frame: arm the death sound. | |
| 9F73 | LD (death_burst_counter),A | ||
| manage_objects_17 | 9F76 | LD A,(fx_despawn_arm) | Step the secondary object's sprite through the burst frame table. |
| 9F79 | ADD A,A | ||
| 9F7A | LD E,A | ||
| 9F7B | LD D,$00 | ||
| 9F7D | LD HL,anim_death_burst | ||
| 9F80 | ADD HL,DE | ||
| 9F81 | LD C,(HL) | ||
| 9F82 | INC HL | ||
| 9F83 | LD B,(HL) | ||
| 9F84 | LD IX,obj2_object | ||
| 9F88 | LD (IX+$06),C | ||
| 9F8B | LD (IX+$07),B | ||
| 9F8E | LD HL,fx_despawn_arm | 16-frame counter | |
| 9F91 | INC (HL) | ||
| 9F92 | LD A,(HL) | ||
| 9F93 | CP $11 | ||
| 9F95 | RET NZ | ||
| 9F96 | LD (HL),$00 | Done: deactivate the secondary object. | |
| 9F98 | RES 0,(IX+$08) | ||
| 9F9C | RET | ||
| ← 9CA6 | Map | 9F9D → |
