How to implement "Dimps ring physics" in Sonic 1

Discussion in 'Tutorials Archive' started by Shockwave, Apr 28, 2014.

Thread Status:
Not open for further replies.
  1. Shockwave

    Shockwave 3 Time Stones Member

    Joined:
    Dec 18, 2013
    Messages:
    121
    Location:
    LA, CA
    Thought I would go ahead and create a small tutorial for this. I don't know how many people out there would be interested, but I figured there may be a few at least and this is something I actually wish more people did, so hopefully someone out there will find this useful. =P

     

    So, whenever I talk to peope regarding Sonic games, one thing I tend to hear pretty often is that the rings make the game too easy since Sonic can always pick one back up when he gets hit. A feature I like to bring up in this case is how in both of the Sonic Rush games, as well as Sonic 4, rings will begin to fly farther the more the player gets hit. Whenever I tell people about this, they agree that it's a good design choice that makes it a bit trickier to play recklessly. They also agree that it's something that should be used in more Sonic games.

     

    And honestly, it isn't that hard to implement at all.

     

    I'm writing this tutorial on the assumption that you've already followed SpirituInsanum's tutorial on speeding up ring loss processing, so if you haven't already looked into that, then(first of all, why the hell not? Second of all) go do so. This can be done without those changes implemented, but it's just so much nicer when the game doesn't slow to a crawl everytime Sonic loses a high number of rings.

     

    Oh, and I'm using Hivebrain's disassembley for this, but it should be easy enough to follow with the HG/GitHub disassembley, especially if you already did the tutorial for speeding up the ring loss process.

     

    First, go to Obj37_MakeRings. You should have this:

     


    Code:
    Obj37_MakeRings: ; XREF: Obj37_CountRings
                    move.b #$37,0(a1) ; load bouncing ring object
                    addq.b #2,$24(a1)
                    move.b #8,$16(a1)
                    move.b #8,$17(a1)
                    move.w 8(a0),8(a1)
                    move.w $C(a0),$C(a1)
                    move.l #Map_obj25,4(a1)
                    move.w #$27B2,2(a1)
                    move.b #4,1(a1)
                    move.w #3,$18(a1)
                    move.b #$47,$20(a1)
                    move.b #8,$19(a1)
                    move.b #-1,($FFFFFEC6).w
                    move.w (a3)+,$10(a1)
                    move.w (a3)+,$12(a1)
                    dbf d5,Obj37_Loop ; repeat for number of rings (max 31)
    

     

    Right before that last line, add this:

     


    Code:
                    move.b ($FFFFD03B).w,d2 ; get the number of times Sonic has been hit
                    cmpi.b #1,d2 ; is it more than once?
                    ble.s @Repeat ; if not, branch
                    ext d2 ; change value from byte to word
                    muls.w #$50,d2 ; multiply by #$50
                    sub.w d2,$12(a1) ; change y-velocity of each ring
    
    @Repeat:
    

     

    This will check the value in byte $3B for Sonic. If it's one, nothing happens. If above one, the value will be extended to a word value and multiplied by $50, then that will be subtracted from each ring's y-velocity, making them fly much higher (of course you can also adjust that number to you're liking, should you want to make the rings fly even farther, or tone it down a bit).

     

    "But $3B doesn't do anything for Sonic."

    ...which is why we're gonna add a few more lines in for that right now.

     

    Go to HurtSonic (open "_incObj/sub ReactToItem.asm" in the HG disassembly) and you'll see this:

     


    Code:
    HurtSonic:
                    tst.b ($FFFFFE2C).w ; does Sonic have a shield?
                    bne.s Hurt_Shield ; if yes, branch
                    tst.w ($FFFFFE20).w ; does Sonic have any rings?
                    beq.w Hurt_NoRings ; if not, branch
                    jsr SingleObjLoad
                    bne.s Hurt_Shield
                    move.b #$37,0(a1) ; load bouncing multi rings object
                    move.w 8(a0),8(a1)
                    move.w $C(a0),$C(a1)
    

     

    Place this right after the second bne intruction:

     


    Code:
                    cmpi.b #6,$3B(a0) ; has Sonic been hurt 6 times already?
                    beq.s @LoadRings ; if yes, branch
                    addq.b #1,$3C(a0) ; increase the hit counter
    
    @LoadRings:
    

     

    With this here, $3B will have one added to it every time Sonic is hurt and looses rings. Sonic has already been hit 6 times, then this will get skipped over, but you could always change it to a higher value if you wanted to punish the player for even more hits.

     

    Anyways, that's all there is to it. Save, build, and go ram Sonic's face into a wall of spikes a couple times or something. Let me know if there are any questions/issues.
     
    Last edited by a moderator: Aug 27, 2014
    Nat The Porcupine likes this.
  2. SpirituInsanum

    SpirituInsanum Well-Known Member Member

    Joined:
    Feb 11, 2010
    Messages:
    642
    Nice.

    I would recommend doing that check & multiplication before the loops though, so you calculate that new additional speed in d2 only once, it will be kept there since d2 isn't used anywhere else during the loop.

    So there would be something like that, probably right after Obj37_CountRings:


                   moveq #0,d2
                   move.b ($FFFFD03C).w,d2 ; get the number of times Sonic has been hit
                   subi.b #1,d2 ; is it more than once? (if you do a cmpi, the first time it's accelerated it will use 2 as a base)
                   beq.s @skip ; skip if d2=0
                   muls.w #$50,d2 ; multiply by #$50
    @skip:
    And only this line before the dbf in the loop:

    Code:
                    sub.w d2,$12(a1) ; change y-velocity of each ring
     
Thread Status:
Not open for further replies.