Touch_ChkValue: move.b collision_flags(a1),d1 ; Get its collision_flags andi.b #$C0,d1 ; Get only collision type bits beq.w Touch_Enemy ; If 00, enemy, branch cmpi.b #$C0,d1 beq.w Touch_Special ; If 11, "special thing for starpole", branch tst.b d1 bmi.w Touch_ChkHurt ; If 10, "harmful", branch ... ; If 01...Let's start with an easy one. When the collision type is 10, the code branches to Touch_ChkHurt, which is responsible for collision with most harmful objects: things like enemy projectiles, fireballs, lasers. This comes with built-in checks for stuff like invincibility and immunity granted by an elemental barrier.
Touch_ChkHurt: move.b status_secondary(a0),d0 andi.b #$73,d0 ; Does player have any shields or is invincible? beq.s Touch_ChkHurt_NoPowerUp ; If not, branch and.b shield_reaction(a1),d0 ; Does one of the player's shields grant immunity to this object?? bne.s Touch_ChkHurt_Return ; If so, branch ...
When the collision type is 00, the code branches to Touch_Enemy, which handles collision with most enemies, as well as bosses. This comes with all the rigamole necessary to determine whether the player can currently damage their foe. In the event that none of these checks pass, the code defaults to calling Touch_ChkHurt.
Touch_Enemy: btst #2,status_secondary(a0) ; Is player invincible? bne.s .checkhurtenemy ; If so, branch cmpi.b #9,anim(a0) ; Is player in their spin dash animation? beq.s .checkhurtenemy ; If so, branch cmpi.b #2,anim(a0) ; Is player in their rolling animation? beq.s .checkhurtenemy ; If so, branch cmpi.b #2,character_id(a0) ; Is player Knuckles? bne.s .notknuckles ; If not, branch cmpi.b #1,double_jump_flag(a0) ; Is Knuckles gliding? beq.s .checkhurtenemy ; If so, branch cmpi.b #3,double_jump_flag(a0) ; Is Knuckles sliding across the ground after gliding? beq.s .checkhurtenemy ; If so, branch bra.w Touch_ChkHurt ; --------------------------------------------------------------------------- .notknuckles: cmpi.b #1,character_id(a0) ; Is player Tails bne.w Touch_ChkHurt ; If not, branch tst.b double_jump_flag(a0) ; Is Tails flying ("gravity-affected") beq.w Touch_ChkHurt ; If not, branch btst #Status_Underwater,status(a0) ; Is Tails underwater bne.w Touch_ChkHurt ; If not, branch ...However, if any of the checks pass, this code goes on to increase the current bonus chain, add the appropriate number of points to the player's score, and make the enemy explode. How it achieves the latter is quite interesting: it overwrites the enemy's own code pointer with the address for the explosion object, and resets the object's routine counter to zero, ensuring the explosion is initialized properly, and that it spawns both a small animal and a score popup.
move.w (Chain_bonus_counter).w,d0 ; Get copy of chain bonus counter addq.w #2,(Chain_bonus_counter).w ; Add 2 to chain bonus counter cmpi.w #6,d0 ; Has the counter already surpassed 5? blo.s loc_101C4 ; If not, branch moveq #6,d0 ; Cap counter at 6 loc_101C4: move.w d0,objoff_3E(a1) move.w Enemy_Points(pc,d0.w),d0 ; Get appropriate number of points cmpi.w #16*2,(Chain_bonus_counter).w ; Have 16 enemies been destroyed? blo.s loc_101DE ; If not, branch move.w #1000,d0 ; Fix bonus to 10000 points move.w #$A,objoff_3E(a1) loc_101DE: movea.w a0,a3 bsr.w HUD_AddToScore move.l #Obj_Explosion,(a1) ; Create enemy destruction explosion move.b #0,routine(a1) ...Now, obviously, the explosion object doesn't need collision, so it never calls the Add_SpriteToCollisionResponseList function. This ensures that Touch_Enemy only runs once per enemy... or does it?
Remember how I mentioned that in a Sonic and Tails game, the collision response list is actually processed twice, once by each player? If Sonic and Tails happen to defeat the same enemy on the same exact frame, Touch_Enemy actually runs twice, the bonus chain is increased twice and the player is awarded points as if two enemies were defeated.
Next time, we'll take a look at the remaining two collision types.
Perhaps I can use this in Launch Base Zone to speed up my infinite lives... (evil laugh)
ReplyDelete