Friday, September 29, 2017

HyperTouch_Special

Hyper Sonic and Hyper Knuckles both have special abilities which destroy all enemies currently on screen. From what we've learned so far about responsive object collision, it should be clear that at some point these abilities must process the collision response list, identify all objects with a collision type of 00, and run a handler to increase the current bonus chain, add the appropriate number of points to the player's score, and make the enemy explode. In fact, hyper abilities have their own version of the Touch_ChkValue routine, which looks like this:
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:
    rts
Right 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
    rts
Here'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:
    rts
Here 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.

1 comment:

  1. In regards to that glitch...

    1.) I think no one cared because it's on Sonic/Tails's route.
    2.) There's an invisible object there, apparently.
    3.) Because I want to, https://www.youtube.com/watch?v=vTFSHtt5Tf4

    ReplyDelete