HyperTouch_ChkValue: tst.b render_flags(a1) ; Is object on-screen? bpl.s locret_10524 ; If not, return (screen-nuke only affects what's on-screen) andi.b #$C0,d0 ; Get collision_flags type data beq.s HyperTouch_Enemy ; If 00, enemy, branch cmpi.b #$C0,d0 beq.w HyperTouch_Special ; If 11, "special thing for starpole", branch tst.b d0 bmi.s HyperTouch_Harmful ; If 10, "harmful", branch locret_10524: rtsRight off the bat, there's an additional check done on the object's render_flags attribute: when the object is off-screen, the high bit of this attribute is cleared, and the object is ignored. Objects with a collision type of 01 are also ignored, but surprisingly enough, all other types are still processed. To find out why, let's take a look at each collision handler.
It should be no surprise that HyperTouch_Enemy exists. After all, the goal of the hyper ability is to destroy all enemies currently on screen! The first major difference relative to Touch_Enemy is that it doesn't affect "special" enemies, such as bosses or enemies that take multiple hits, such as the Relief enemies in Marble Garden Zone. As for the other major difference, I'll just let the disassembly comments do the talking:
HyperTouch_Enemy: tst.b collision_property(a1) ; Is this a special enemy? beq.s HyperTouch_DestroyEnemy ; If not, branch rts ; --------------------------------------------------------------------------- ; Similar to other enemy destruction subroutines, but this one doesn't make the player bounce HyperTouch_DestroyEnemy: ...Next up we have HyperTouch_Harmful. All this does is look for enemy projectiles, and make them fly away similarly to how they bounce off elemental barriers:
HyperTouch_Harmful: move.b shield_reaction(a1),d0 andi.b #8,d0 ; Should the object be bounced away by a shield? bne.w Touch_ChkHurt_Bounce_Projectile ; If so, branch rts
Finally, the namesake of this post, HyperTouch_Special. Why does it need to exist? It's time to learn the truth.
The truth is that not all enemies uses the enemy collision type. Touch_Enemy is quite strict about what happens to the enemy when it touches a player: it either damages the player without being notified that the collision even happened, or it is suddenly and unexpectedly turned into an explosion, preventing it from reacting to the collision in any way.
The enemies in Hydrocity Zone are particularly fond of that kind of behavior, such as the Blastoid enemy, which causes underlying walkways to collapse when it's defeated, or the Jawz enemy, which kills itself when it runs into the player.
Thus, we are in the unfortunate situation where, in order to have the hyper ability affect every enemy, it must also affect every single object tagged with the special collision type. Here's what the handler looks like:
HyperTouch_Special: ori.b #3,collision_property(a1) cmpi.w #3,(Player_mode).w ; Are we in Knuckles Alone mode? bne.s loc_105B6 ; If not, branch move.w x_pos(a1),(Player_2+x_pos).w ; ??? move.w y_pos(a1),(Player_2+y_pos).w ; ??? loc_105B6: move.b #2,(Player_2+anim).w ; Put sidekick in his rolling animation bset #Status_InAir,(Player_2+status).w rtsHere's the rationale for that first line of code: in order to make sure the enemy is defeated, the collision flag for player 1 is set. Player 1 is currently hyper, meaning they're invincible, meaning there will never be a scenario where the enemy damages the player and survives the collision.
As a consequence to this action, whenever player 1 uses their hyper ability, they get bounced around by every bumper currently on screen. I guess the developers didn't want player 1 to feel lonely, so they also set the flag for player 2, and added the two lines at the bottom to make sure player 2 bounces off in their rolling animation.
I am just as confused as the disassembly comments are about the code in the middle.
Unfortunately, the developers didn't consider that player 2 may be riding an object which overrides the player's controls, so that last line of code causes them to enter a sort of limbo where they're riding the object, but also in mid-air.
Here's a saner approach to the problem:
HyperTouch_Special: ori.b #3,collision_property(a1) tst.w (Player_mode).w ; Are we in Sonic and Tails mode? bne.s .return ; If not, branch clr.b (Player_2+object_control).w move.b #2,(Player_2+anim).w ; Put sidekick in his rolling animation bset #Status_InAir,(Player_2+status).w .return: rtsHere we make sure to also clear player 2's object_control attribute, releasing them from their ride, and for the love of god, only do any of this in a Sonic and Tails game. Build the ROM, and verify that player 2 bounces off properly now.
Of course, this does nothing to alleviate other woes incurred from processing special collision in the first place, such as hyper abilities activating the warp stars atop a star post, or bumpers causing Knuckles to be crushed into walls.