← 9CA6 Drag the bat's extension down Map 9F9D → Remove a ball that has passed the bat's losing edge
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 Drag the bat's extension down Map 9F9D → Remove a ball that has passed the bat's losing edge
Fork me on GitHub