Sonic 1 Hivebrain Guide - How to add the Air Roll/Flying Spin Attack.

Discussion in 'Tutorials' started by Inferno, Dec 6, 2019.

  1. Inferno

    Inferno Rom Hacker Member

    Joined:
    Oct 27, 2015
    Messages:
    39
    Location:
    Hidden Palace Zone, Westside Island
    NOTE: This guide isn't just copy-paste. This is intended to teach more than just how to port the Air Roll.

    EDIT (1st, 12/6/19): Removed a mention of how putting the bsr just anywhere in Obj01_MdJump/Obj01_MdJump2 would screw up the jump, and moved the placement of the bsr, based off of Iso Kilo's observations.

    EDIT (2nd, 12/6/19): Unifed this with the Sonic Retro tutorial.

    EDIT: (3rd, 12/6/19) Undid edit one of this day due to bugs being caused by the changes. This should fix your issues.

    EDIT: (4th, 4/7/20) FINALLY added Aier's optimized version of this code. As a result, the tutorial has been completely rearranged and partly rewritten.


    As you likely know, whenever you jump onto a spring in a Sonic game, you enter a vulnerable state. However, in Sonic Triple Trouble, you could press a jump button while in the spring animation to curl up into a ball:

    [​IMG]

    Now, this isn't in Sonic 1, 2, or 3 & Knuckles. However, this guide can show you how to add this move to Sonic 1's Hivebrain disasm, so that you can do this in Sonic 1.

    NOTE: For those not using the S1 Hivebrain disasm or S1 at all, porting should be easy. Make sure to look for the equivalent addresses and routines.

    Now, we want Sonic to be able to do the air roll ONLY if he's in the air and he isn't already rolling, right? Well, luckily for us, there's already a routine which is branched to when these criteria are set! This routine is Obj01_MdJump. This would be the best place for us to put the first branch to our code, so go to Obj01_MdJump. This is what it should look like normally:

    Code:
    Obj01_MdJump:                ; XREF: Obj01_Modes
            bsr.w    Sonic_JumpHeight
            bsr.w    Sonic_ChgJumpDir
            bsr.w    Sonic_LevelBound
            jsr    ObjectFall
            btst    #6,$22(a0)
            beq.s    loc_12E5C
            subi.w    #$28,$12(a0)
    
    loc_12E5C:
            bsr.w    Sonic_JumpAngle
            bsr.w    Sonic_Floor
            rts    
    Now, we could arguably put it in anywhere, but the best place IS the top of the routine. So put this:
    Code:
    bsr.w   Sonic_AirRoll
    Right under the Obj01_MdJump label.

    Now, bsr and jsr are a special kind of branch. After the code it branches to is finished, it'll automatically return to the point where we branched from and continue from right below it. This will be important for the format of our Air Roll code.

    So, we've created a branch to a new subroutine. The problem is, it doesn't exist! If you try to build right now, you'll get an error. Now, we have to create the subroutine itself.

    Put the proper label within Sonic's code, I've personally put it below Sonic_JumpHeight's function.

    Now, we have to actually code the action. First of all, we should filter out cases where Sonic has already performed the move, and therefore, in a ball. Luckily, in RAM, there's a SST which stores the current object's animation, which just so happens to be Sonic! Sonic's animation ID for rolling in Sonic 1 is #$2. The SST in question is $1C. IF you know what's about to happen, good job, you already know of the cmp (Compare) instruction. This instruction compares one thing to another thing. Using this, we can perform a branch using beq (Branch if Equal) and bne (Branch if Not Equal). This is what the end result should look like:
    Code:
    Sonic_AirRoll:
            cmpi.b  #2,$1C(a0)      ; are we already in rolling animation
            beq.s   AirRoll_Return   ; if yes, return
    If you want the move to, just like in Triple Trouble, only be able to occur if Sonic's in his spring animation, change the animation ID to the spring animation (#$10, IIRC) and invert the branch (beq -> bne, bne -> beq).

    Now, we should probably add this "AirRoll_Return" routine to begin with. So put this:

    Code:
    AirRoll_Return:
            rts
    Right below the current Sonic_AirRoll: code.

    Now, this is rts (Return To Subroutine) . The name should hint at it's function. Now, rts returns to the point where the current routine/subroutine was branched from, and continues code execution from there, much like what bsr and jsr automatically do. So, in order to keep the game from executing code it shouldn't, this routine was added.

    Now, we have a move and it won't trigger if you aren't in a ball, but it currently does nothing. Now, let's code the actual move!

    Now, we only want the code to execute if A/B/C is pressed, right?

    Now, this tutorial should show you how to utilize button presses, so check it out real quick, because I'd rather not copy what Selbi had to say.

    Now that you are back, you should have created something like this, below Sonic_AirRoll, but above AirRoll_Return:

    Code:
    AirRoll_CheckBtn:
            move.b ($FFFFF603).w,d0 ; Move $FFFFF603 to d0
            andi.b #$70,d0 ; Has A/B/C been pressed?
            beq.s  AirRoll_Return
    There's a new label here to help with readability. This will filter out all cases where Sonic DID NOT press A/B/C. However, it still does nothing! But, wait! Now that we have everything filtered out, we can actually do stuff now! Now, do you remember how $1C in the SST has the current object's animation stored to it? We are about to use that information again. There's a handy instruction called move (move), which moves a thing to a location. We can move #$2 to $1C in order to set Sonic's animation to be the rolling animation!

    So, in the same label, below the above button test, add these lines, which make Sonic roll, and play the roll sound:
    Code:
            move.b #2,$1C(a0) ; Set Sonic's animation to rolling.
            move.w    #$BC,d0
            jsr    (PlaySound_Special).l ;    play Sonic rolling sound
    Now, with how we set up the code, this will now fall through to AirRoll_Return, meaning that we don't leak down into Sonic_JumpHeight! Meaning, we are done!

    Enjoy the Air Roll!

    Credits:

    LoganTheSonicPlayer/Inferno Gear - Creating the guide.
    Iso Kilo - Responding to my question regarding a snag I hit while creating this code.
    Aier - Optimizing the code a while back. Sorry for not updating this yet, guys.
     
    Last edited: Jun 22, 2020
  2. Iso Kilo

    Iso Kilo Is it a fox? Is it a wolf? It's Kilo! Member

    Joined:
    Oct 9, 2017
    Messages:
    327
    Location:
    A warm and lovely place~
    Not true. After playing with the guide myself, I had placed the bsr to Sonic_AirRoll at the very start of Obj01_MdJump and Obj01_MdJump2 and they functioned just as well if you were to put it in loc_12E5C and loc_12EA6.
     
    Inferno likes this.
  3. Inferno

    Inferno Rom Hacker Member

    Joined:
    Oct 27, 2015
    Messages:
    39
    Location:
    Hidden Palace Zone, Westside Island
    Alright, the guide's been edited to remove that mention and to move the code's placement to the top of the routines.
     
    Iso Kilo likes this.
  4. Tanman Tanner

    Tanman Tanner Well-Known Member Member

    Joined:
    Dec 23, 2016
    Messages:
    113
    Location:
    Buffalo, New York
    Something to note when I was testing with this: You can use it repeatedly to basically 'float' in air. Additionally, if you go into the skid animation and do the jump, it'll send you flying backwards. However, my version of the disassembly is the GitHub variant, but it shouldn't be that far different.
     
    Inferno likes this.
  5. Inferno

    Inferno Rom Hacker Member

    Joined:
    Oct 27, 2015
    Messages:
    39
    Location:
    Hidden Palace Zone, Westside Island
    That's strange. I haven't encountered that issue when testing it myself.

    My guess is either the air check and the animation check need to be flipped around order wise or placing the bsr where the guide originally put it, within the subroutine that's first branched to when Obj01_MdJump/Obj01_MdJump2 check for if Sonic's underwater should fix it.

    Thanks for pointing out this issue.

    EDIT: Edit has been made to main guide which likely fixes the issue.
     
    Last edited: Dec 7, 2019
  6. Tanman Tanner

    Tanman Tanner Well-Known Member Member

    Joined:
    Dec 23, 2016
    Messages:
    113
    Location:
    Buffalo, New York
    It's partially fixed. You can still mash jump after doing the roll jump to slowly descend.
    Also, this could be a conflict with my S2 Spindash code, but if you do a roll jump after spindashing down a U shaped ramp, it'll send you flying to the right. It could of been a freak coincidence on my side, but I just might as well mention it.
    EDIT: The bug with the 'skid' animation allowing you to flying forward/backward still exists, except you can't do it on the ground anymore, only in the air. I'm almost confident now it's a problem on my side, but I might be wrong.
     
    Inferno likes this.
  7. Inferno

    Inferno Rom Hacker Member

    Joined:
    Oct 27, 2015
    Messages:
    39
    Location:
    Hidden Palace Zone, Westside Island
    At this point, it's identical to my code in my hack, which does have the S2 Spindash. Try swapping the checks in AirRoll_Checks (since it's flipped in my hack's code), and if that does not fix it, it's an issue solely on your end.
     
    Last edited: Dec 9, 2019
  8. Tanman Tanner

    Tanman Tanner Well-Known Member Member

    Joined:
    Dec 23, 2016
    Messages:
    113
    Location:
    Buffalo, New York
    I did as you asked. Yeah, it's a problem on my end. It's an old ROM Hack though, and I have no plans to release it. I'll re-try this on a fresh copy and see if I see any issues then.
     
    Inferno likes this.
  9. ADudeCalledLeo

    ADudeCalledLeo I'll make a ROM hack one of these days... Member

    Joined:
    Oct 21, 2017
    Messages:
    13
    Location:
    Null Space
    Some trivia about the 2 "MdJump" routines: "MdJump2" is used when Sonic actually jumps, while "MdJump" is used when Sonic is airborne otherwise. In Sonic 1, the routines are the same (which leads me to question why Sonic Team split them up...)

    Theoretically, the branch to the move routine should only be needed in "MdJump", since the game thinks "Sonic in air + Sonic in ball state = Sonic jumped, let's go to MdJump2". Doing this will fix the floating exploit that Tanman had, since the player won't be able to use the move if Sonic is rolled up.

    ...also, checking the "in air" bit of Sonic's status field is probably a bit redundant, considering, again, you're in the "jump/airborne" routines. :p
     
    ProjectFM and Inferno like this.
  10. Inferno

    Inferno Rom Hacker Member

    Joined:
    Oct 27, 2015
    Messages:
    39
    Location:
    Hidden Palace Zone, Westside Island
    For your first thing, thanks! I'll apply that on top of future optimizations coming to the guide.
    For the second thing, yea, I already know, someone already pointed that out to me on Discord, but thanks for pointing it out as well.

    I'll be honest, I've been too lazy to add the optimizations in quite yet, lol. I'll get to that this weekend,
     
  11. Inferno

    Inferno Rom Hacker Member

    Joined:
    Oct 27, 2015
    Messages:
    39
    Location:
    Hidden Palace Zone, Westside Island
    Finally added Aier's optimizations into the tutorial, which required quite the rewrite. Any unneeded lines have been removed, AND the code has been rearranged to prevent potential issues.

    EDIT: Oh, and can confirm, works about as well in Sonic 3 and Sonic 2 as in Sonic 1.
     
    Last edited: Apr 14, 2020
  12. Inferno

    Inferno Rom Hacker Member

    Joined:
    Oct 27, 2015
    Messages:
    39
    Location:
    Hidden Palace Zone, Westside Island
    Put the changelog as a spoiler for viewing convenience.
     
    DeltaWooloo likes this.