Mini-tutorials Thread

Discussion in 'Tutorials' started by EpsilionDubwool, Feb 16, 2021.

Tags:
  1. Apple Boy

    Apple Boy Newcomer Trialist

    Joined:
    Jan 12, 2021
    Messages:
    3
    What address would I go to for the GitHub Disassembly?
     
  2. EpsilionDubwool

    EpsilionDubwool The noob next door Member

    Joined:
    Aug 7, 2019
    Messages:
    366
    Dunno what you mean by address. If you want to find the label, might I suggest this handy label comparison that compares Hivebrain's disassembly with SVN/GitHub's disassembly.

    In this case, you need to look for Got_Config
     
    Giovanni and TheInvisibleSun like this.
  3. SuperSayian Zrise

    SuperSayian Zrise Well-Known Member Exiled

    Joined:
    May 10, 2021
    Messages:
    78
    Using selbi's text code generator for Sonic 1. Anyways, with the text code generator you only have to worry about modding the blue oval, the act number and the bonuses. But most likely you are editing the SONIC HAS PASSED message. If so, use the Sonic 1 text code generator by selbi.
     
  4. EpsilionDubwool

    EpsilionDubwool The noob next door Member

    Joined:
    Aug 7, 2019
    Messages:
    366
    Well yeah... but the mini tutorial I wrote tells you about the structure of the results card, not the text.
     
    SuperSayian Zrise likes this.
  5. Scrap Sorra

    Scrap Sorra Well-Known Member Member

    Joined:
    Sep 18, 2020
    Messages:
    105
    Location:
    Sonic the Hedgehog for the Sega Genesis
    Play Animated Level GFX when the game is paused.

    Sonic CD does this kinda strange thing where if the game is paused animated level GFX doesn't stop playing, so let's add it to Sonic 1!

    In sonic1.asm, go to "AniArt_Load" and comment out or delete these two lines:

    Code:
    tst.w    ($FFFFF63A).w    ; is the game paused?
            bne.s    AniArt_Pause    ; if yes, branch
    That's it.


    In inc\AnimateLevelGfx.asm, comment out or delete these two lines at the start of AnimateLevelGfx:

    Code:
    tst.w    (f_pause).w    ; is the game paused?
            bne.s    @ispaused    ; if yes, branch
    That's it.

    Now animated level graphics will continue to play even when the game is paused.
     
  6. Painto

    Painto Arthurus Paintus Erinaceus Member

    Joined:
    Mar 24, 2014
    Messages:
    321
    Location:
    Lublin, Poland
    Fixing the swinging wrecking ball object in GHZ
    As you probably know, there's an unused subtype of the swinging platform which uses wrecking ball at its end instead of a platform. However if you intend to use it in your level design, you may notice a little error in it - while invincible (either after hurt or from powerup) you can stand on it like it's a normal platform.
    [​IMG]
    The fix to this is easy. This object ($15) is also used in Scrap Brain Zone for spikeballs on a chain and in that setup it works as intended (that is, you can't stand on it). Comparing both setups reveals that there is one difference: spikeball variant changes the object's routine to a simpler one, while wrecking ball one still uses the regular platform routines. This means the fix is as simple as setting the correct routine. Here's how to do that:

    Open sonic1.asm and search for loc_7AD4:. Just above it, you should see the following code:
    Code:
    		btst	#4,d1		; is object type $8X ?
    		beq.s	loc_7AD4	; if not, branch
    		move.l	#Map_obj48,4(a0) ; use GHZ ball	mappings
    		move.w	#$43AA,2(a0)
    		move.b	#1,$1A(a0)
    		move.b	#2,$18(a0)
    		move.b	#$81,$20(a0)	; make object hurt when	touched
    
    Under it add the following lines:
    Code:
    		move.b	#$C,$24(a0)	; set routine to Obj15_Action
    		bra.s	Obj15_Action	; skip solidity routines
    
    Save and build.
    Open _incObj/15 Swinging Platforms (part 1).asm and search for @not1X:. Just above it, you should see the following code:
    Code:
    		btst	#4,d1		; is object type $1X ?
    		beq.s	@not1X	; if not, branch
    		move.l	#Map_GBall,obMap(a0) ; use GHZ ball mappings
    		move.w	#$43AA,obGfx(a0)
    		move.b	#1,obFrame(a0)
    		move.b	#2,obPriority(a0)
    		move.b	#$81,obColType(a0) ; make object hurt when touched
    
    Under it add the following lines:
    Code:
    		move.b	#$C,obRoutine(a0)	; set routine to Swing_Action
    		bra.s	Swing_Action	; skip solidity routines
    
    Save and build.
     
    Last edited: Nov 14, 2021
  7. TheFieldWarrior

    TheFieldWarrior Green Villager is a treasure Member

    Joined:
    Oct 9, 2015
    Messages:
    93
    Location:
    United Kingdom
    How to add instant recovery from getting hit:

    Getting tired of taking major knockback and having your controls locked upon getting hit? Here is a way to remove both of these issues by utilising a couple of lines from an unused subroutine in Sonic 2 for instant recovery. I'll be using Sonic 1 (Hivebrain) for this guide but ideally you should be able to add this feature into any of the trilogy regardless of disassembly.


    Search for Obj01_Hurt and before the line "jsr SpeedToPos" add these two lines:

    Code:
            subq.b    #2,$24(a0)
            move.b  #0,$25(a0)

    Build the ROM and now Sonic will have instant recovery after taking a hit allowing you to control yourself during the hurt animation. Your i-frames start up sooner than normal however but you can always increase the i-frame timer, I personally keep the timer as it is as a trade off for the instant recovery.


    Spin Dash bug fix:

    If you have the Spin Dash implemented there is now a bug where if Sonic get's hit during the rev animation he will boost off after he's hit the ground, fixing this is simple: after the line "move.b #0,$25(a0)" add this:

    Code:
    move.b    #0,$39(a0)
    All done. There might be a better and more optimal way to add instant recovery so you're more than welcome to reply to this post with it
     
  8. Speems

    Speems Well-Known Member Member

    Joined:
    Mar 14, 2017
    Messages:
    69
    Location:
    Rochester Hills, MI
    How to Remove the Points Object in Sonic 1 (Hivebrain 2005)

    If you hate the occasional instance of the points art fucking up some aspects of your Sonic art, or need to save some VRAM, removing the points art/object may be the way to go. Here's how you can do it! This is based off a finding by Cinossu.

    1. Head to sonic1.asm and you'll need to find this line of code:

    Code:
            move.b    #$29,0(a1)    ; load points object
    There are three traces of this line of code throughout the asm. They are in loc_90C0, Obj47_Score, and Obj51_Smash. You've gotta delete those.


    2. Find Nem_Points in the same file, which is the incbin for the art.

    Code:
    Nem_Points:    incbin    artnem\points.bin    ; points from destroyed enemy or object
    
            even
    Delete it. Now the game will refuse to build due to it missing from the game but requiring the pattern load cues present. We need to take care of the pointer in another file.


    3. Head to the Pattern load cues.asm in the _inc folder, which has Nem_Points as the last part of Standard block 1.

    Code:
            dc.l Nem_Points        ; points from enemy
    
            dc.w $F2E0
    You think that simply deleting it will work, right? Wrong! Do that and it crashes right at the title screen when built. So what you need to do is change the dc.w digit from 4 to 3, lowering the amount of objects that need to be loaded, also making sure correct amounts of data are read.


    4. One last thing! Head to the Object pointers.asm and you'll see Obj29 like this:

    Code:
        dc.l Obj29, Obj2A, Obj2B, Obj2C
    Replace the singular instance with DeleteObject.


    Build the game and you should be able to dish out broken badniks and various level-specific objects, and still earn the points even with the art for it gone. You also have some extra free tiles in VRAM when you do it, that's always nice. There should be equivalent labeling in the Github disassembly, although the code for the points object may be in a separate ASM file.


    Credit should go to Cinossu for his input on simply disabling it, I prefer it considering this is merely a goodwill tutorial meant to help newcomers who think the points object/art is rather pesky. Thanks!


    EDIT: 12/13/2021 - All steps (except for 2) have been adjusted following criticism regarding RAM issues from vladikcomper.
     
    Last edited: Dec 13, 2021
  9. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    410
    @Speems, not to discourage you or anything, but the guide you've just posted is dangerous and gives some bad advice.

    First of all, you remove the points object (Obj29), but don't disable code that spawns it. This is dangerous and leads to memory leaks.

    If you check animals object (Obj28), it actually spawns points object here:

    Code:
    loc_90C0:
                    ; <...>
                    bsr.w    SingleObjLoad
                    bne.s    Obj28_Display
                    move.b    #$29,(a1)        ; load points object
                    ; <...>
    You make it so object slot ID $29 now points to ObjectFall, which is fine, except one thing: this object will never delete itself and free memory.

    And there are several other places, where object $29 is being created.

    Spawning broken objects like this is dangerous, because they'll occupy objects RAM forever and eventually prevent any other objects from loading. Try spamming and destroying as many badniks as you can in debug mode, you'll soon notice any level objects won't spawn at all.

    So, to fix this serious bug, your guide should be appended with the following:

    Find every occurrence of "move.b #$29,(a1)" in disassembly, and remove the related code, starting from "bsr.w SingleObjLoad" until the very last line where "X(a1)" is being written to...​

    There are quite a few places, actually.

    Or, better yet, use "DeleteObject" instead "ObjectFall" for object slot ID $29, to make this fail-proof and reliable.

    This is just plain bad advice, and it's teaching people wrong and dengerous things to do.

    If you get a crash, but instead of trying to figure it out, you chose to randomly hack in one thing instead of another, and you aren't even sure what that thing should be, you're clearly doing something wrong...

    You get a crash, because in the beginning of every Pattern Load Cues list there's a word that determines how many entries should be in that list. You removed an entry from the list, but you didn't decrease that number. If you decrease it, the game won't read incorrect data at the end of now-shorter list, and any crashes will be gone.
     
  10. Speems

    Speems Well-Known Member Member

    Joined:
    Mar 14, 2017
    Messages:
    69
    Location:
    Rochester Hills, MI
    @vladikcomper
    Post has been updated with the tidbits, thanks for informing me of the risks.
     
    Samey, Crimson Neo and vladikcomper like this.
  11. TheInvisibleSun

    TheInvisibleSun Visible Member

    Joined:
    Jul 2, 2013
    Messages:
    413
    Location:
    Buffalo, NY, USA
    Manual Special Stage Rotation Control in Sonic 1


    Would you like to have Sonic 1 Special Stages where you control the rotation with the d-pad, a la Sonic 4: Episode 1? It's actually quite simple, and should only involve about 7 lines of code.


    In the Special gamemode's init code, comment out this line:

    Code:
    move.w    #$40,(v_ssrotate).w ; set stage rotation speed
    Optionally, comment out this line under the 'SS_ShowLayout' subroutine, for smooth rotation (courtesy of Cinossu):

    Code:
    andi.b    #$FC,d0    
    Next, in Object 09's code, add these lines to disable Sonic's movement, and make your input rotate the stage instead:

    Code:
    ...
    
    Obj09_MoveLeft:
    
            sub.w #$XX,(v_ssangle).w    ; TIS - include whatever value you'd like, tweak to desired rotation speed
            rts    ;TIS     - Disable Sonic's left movement
            bset    #0,obStatus(a0)
            move.w    obInertia(a0),d0
    
            ...
    
    

    Code:
    Obj09_MoveRight:
    
            add.w #$XX,(v_ssangle).w    ;TIS - include whatever value you'd like, tweak to desired rotation speed
            rts      ;TIS - Disable Sonic's right movement
            bclr    #0,obStatus(a0)
            move.w    obInertia(a0),d0
            ...

    Finally, disable Object 09's jump here:



    Code:
    Obj09_Jump:
            rts    ;TIS - Disable Jump Routine
            move.b    (v_jpadpress2).w,d0
            andi.b    #btnABC,d0    ; is A,    B or C pressed?
            ...
    
    

    Enjoy!
     
    EddyTF, AXELsrh, CHRdutch and 3 others like this.
  12. EddyTF

    EddyTF #romhacker Member

    Joined:
    Jan 9, 2022
    Messages:
    54
    Location:
    Russia
    How to increase or decrease Boss Hits in Sonic 1 (Hivebrain Disasm)

    To increase or decrease the blows of the Boss of the Green Hill, you need to find loc_17772:

    Code:
    loc_17772:
            move.w    8(a0),$30(a0)
            move.w    $C(a0),$38(a0)
            move.b    #$F,$20(a0)
            move.b    #8,$21(a0)    ; set number of hits to 8
    We will need a line:

    Code:
    move.b    #8,$21(a0)    ; set number of hits to 8
    And at $8 we increase or decrease the hits

    For the boss of the Labyrinth - Obj77_Main:
    For the boss of the Marble - Obj73_Main:
    For the boss of the Star Light - Obj7A_Main:
    For the boss of the Spring Yard - Obj75_Main:
    For the boss of the Final - loc_19E5A:

    We are looking for the same line specified above.


    Enjoy hacking!;)
     
    Last edited: Mar 7, 2022
  13. Nik Pi

    Nik Pi Well-Known Member Member

    Joined:
    Feb 14, 2022
    Messages:
    66
    Location:
    Kazakhstan
    Wall recoil in final Sonic 2 (based on https://sonicresearch.org/community/index.php?threads/sonic-1-porting-s2na-wall-recoil.6052/):
    The first- you need to find in Obj01_CheckWallsOnGround this:
    Code:
    bset    #5,status(a0)
    move.w    #0,inertia(a0)
    And after this- place:
    Code:
    cmpi.w    #$300,$10(a0)    ; Check if player is at running speed
    bge.s    Sonic_WallRecoil    ; If so, bonk
    Second: In loc_1A6A8 after move.w #0,inertia(a0) insert:
    Code:
    cmpi.w    #-$300,$10(a0)    ; If player is at running speed to the left
    ble.s    Sonic_WallRecoil    ; Bonk!
    And now- before Sonic_MoveLeft write:
    Code:
    Sonic_WallRecoil:   ; Routine to bounce Sonic off a wall when going too fast
    move.b    #4,$24(a0)
    bsr.w    Sonic_ResetOnFloor
    bset    #1,$22(a0)  ; Set Sonic as in air
    move.w    #$FE00,d0   ; Move recoil speed into d0 for later
    tst.w    $10(a0) ; Check if player is moving right
    bpl.s    cont  ; If so, branch
    neg.w    d0  ; Negate recoil speed if facing left
    
    cont
    move.w    d0,$10(a0)  ; Move recoil speed into X speed
    move.w    #$FC00,$12(a0)  ; Vertical recoil speed
    move.w    #0,$14(a0)  ; Clear inertia
    move.b    #AniIDSonAni_Hurt2,anim(a0) ; Play recoil animation
    move.b    #1,$25(a0)
    move.w    #SndID_Hurt,d0
    jsr    (PlaySound).l
    rts ; Return
    I hope it will be useful for someone

    EDIT: Used code line
     
    Last edited: Mar 8, 2022
    EddyTF likes this.
  14. EddyTF

    EddyTF #romhacker Member

    Joined:
    Jan 9, 2022
    Messages:
    54
    Location:
    Russia
    How to display the inscription "Press Start Button" (Alternative) (Sonic 1 Hivebrain Disasm)

    Find Title_ClrObjRam2:

    We see this:

    Code:
    Title_ClrObjRam2:
            move.l    d0,(a1)+
            dbf    d1,Title_ClrObjRam2
    
            move.b    #$E,($FFFFD040).w ; load big Sonic object
            move.b    #$F,($FFFFD080).w ; load "PRESS    START BUTTON" object
            move.b    #$F,($FFFFD0C0).w ; load "TM" object
            move.b    #3,($FFFFD0DA).w
            move.b    #$F,($FFFFD100).w
            move.b    #2,($FFFFD11A).w
            jsr    ObjectsLoad
            bsr.w    DeformBgLayer
            jsr    BuildSprites
            moveq    #0,d0
            bsr.w    LoadPLC2
            move.w    #0,($FFFFFFE4).w
            move.w    #0,($FFFFFFE6).w
            move.w    ($FFFFF60C).w,d0
            ori.b    #$40,d0
            move.w    d0,($C00004).l
            bsr.w    Pal_FadeTo
    After dbf d1,Title_ClrObjRam2, insert:

    Code:
    jsr     DeleteObject2     ; clear object RAM to make room for the "Press Start Button" object
    Compile, check. Works :D

    Enjoy hacking!;)
     
    Nik Pi likes this.
  15. faith

    faith Well-Known Member Member

    Joined:
    Aug 26, 2013
    Messages:
    1,209
    I haven't seen anyone else bring this up, but I thought this was a tad interesting.

    So, you might know of the change to make special stages rotate smoother, which is done by removing the "andi.b #$FC,d0" line in SS_ShowLayout that limits the rotation value. Did you know that angle value limitation is also found in Sonic's special stage object and subtley affects his physics for jumping and gravity?

    In Obj09_Jump, you can find:
    Code:
            move.b    (v_ssangle).w,d0
            andi.b    #$FC,d0
            neg.b    d0
            subi.b    #$40,d0
            jsr    (CalcSine).l

    Which calculates the angle to jump at. Note the "andi" instruction, that same line that limits the sprite rotation.

    You can also find a similar line here in Obj09_Fall:
    Code:
            move.b    (v_ssangle).w,d0
            andi.b    #$FC,d0
            jsr    (CalcSine).l

    Which affects how gravity is calculated. Again, that "andi" instruction is there.

    So, if you wanna enable slightly smoother jumping and gravity, comment out those "andi" lines.

    Also, if you followed this guide, then there's 2 additional instances in Obj09_JumpHeight.
     
    Last edited: Jun 24, 2022
  16. Giovanni

    Giovanni It's Joevanni, not Geovanni. Member

    Joined:
    Apr 16, 2015
    Messages:
    276
    Location:
    Italy
    Vertical Speed Cap

    In the Mega Drive/Genesis 2D Sonic games, characters are able to pick up vertical speed indefinitely while falling, allowing the player to outspeed the camera and create some inconvenient scenarios, like this one:

    fallin.gif

    Here, Sonic should've been saved by the below platform, but instead has died, since object collision is ignored when your playable character is offscreen.

    You can prevent such scenarios by implementing a Vertical Speed Cap.

    Implementing this is really simple: what you've got to do is prevent the player's Y speed from ever increasing past $1000, as that's the speed needed to outrun the camera in any direction. There are two possible approaches you can take, but before proceeding, please know that the following examples target disassemblies without RAM equates, such as the Hivebrain version of Sonic 1. If your disassembly does use RAM equates, or you've changed the RAM location for the vertical speed, remember to change the RAM address in the code with your new RAM address/equate.

    Additionally, the guide makes mention of the "ObjectFall" routine. Please note that the object movement routines may be named differently across disassemblies. If you want to implement this in games other than Sonic 1, keep all of this in mind.

    Apply the Vertical Speed Cap only to individual characters/objects

    This method is the safest, as it allows you to implement the Vertical Speed Cap without affecting object behavior. It is also more optimal, as only the player objects will use this.

    Find your player's air movement routines. In Sonic 1 Hivebrain, for example, they are Obj01_MdJump and Obj01_MdJump2.

    Now look at the line that is being pointed at:

    Code:
            bsr.w    Sonic_JumpHeight
            bsr.w    Sonic_ChgJumpDir
            bsr.w    Sonic_LevelBound
            jsr    (ObjectFall).l        ; <- look at this line
            btst    #6,$22(a0)
            beq.s    loc_12E5C
            subi.w    #$28,$12(a0)
    This line jumps to a function used by many objects: it allows them to be moved at a set speed, and adds $38 to their vertical speed, which represents gravity.
    What you want to do is write a check that, before calling this routine, makes sure that Sonic's speed is not beyond $1000 - $38 (or $FC8), otherwise, force his speed to be $FC8 before the routine is called.

    It's really simple, all you need is these four lines:

    Code:
            cmp.w   #$FC8,$12(a0)   ; check if Sonic's Y speed is lower than this value
            ble.s   .skipline       ; if yes, branch
            move.w  #$FC8,$12(a0)    ; alter Sonic's Y speed
        .skipline:
    Paste these four lines right above the jump to ObjectFall in both air movement routines for Sonic. If your hack features multiple characters, you will need to do this for every single character.

    Your disassembly may not support temporary labels the same way mine does. If you're on ASM68K, replace the dot with an "@". If that doesn't work either, you can replace the label with anything you like.

    Apply the Vertical Speed Cap to everything

    This method is much easier to implement, as you need to add the above code only once. However, instead of applying code to individual characters, we apply it to the very routine that makes objects affected by gravity.

    In your disassembly, find the ObjectFall routine.

    Code:
    ObjectFall:
            move.l    8(a0),d2
            move.l    $C(a0),d3
            move.w    $10(a0),d0
            ext.l    d0
            asl.l    #8,d0
            add.l    d0,d2
            move.w    $12(a0),d0        ; <-- look at this line
            addi.w    #$38,$12(a0)    ; increase vertical speed
            ext.l    d0
            asl.l    #8,d0
            add.l    d0,d3
            move.l    d2,8(a0)
            move.l    d3,$C(a0)
            rts    
    You can stick the four lines of code from earlier right above the line that moves the Y speed to d0, and it will work just fine. However, this will use additional processing power whenever ObjectFall is ran by any object that needs it (a lot of them use it, in fact).

    If you're using Sonic 3 & Knuckles' ObjectFall routine, you should still find the line that loads the Y speed to d0 as normal. The routine will be a tad bit shorter, of course.

    Regardless of which approach you choose to take, the intended result is the following:

    fallin2.gif

    EDIT: Beware that if you followed this guide and your ROM hack features vertically wrapping levels, there will be a conflict that will result in the game being softlocked. You are advised not to use the vertical speed cap in conjunction with the linked guide (unless your hack does not feature vertically wrapping levels).
     
    Last edited: Jan 15, 2023
  17. EddyTF

    EddyTF #romhacker Member

    Joined:
    Jan 9, 2022
    Messages:
    54
    Location:
    Russia
    How to stop time after defeating the boss (Hivebrain 2005 Disassembly)

    As you know, after defeating the bosses, time does not stop. It's not very good. In this mini-guide I will tell you how to do it.

    So, we are looking for a label BossDefeated: and we see:
    Code:
    BossDefeated:
            move.b    ($FFFFFE0F).w,d0
            andi.b    #7,d0
            bne.s    locret_178A2
            jsr    SingleObjLoad
            bne.s    locret_178A2
            move.b    #$3F,0(a1)    ; load explosion object
            move.w    8(a0),8(a1)
            move.w    $C(a0),$C(a1)
            jsr    (RandomNumber).l
            move.w    d0,d1
            moveq    #0,d1
            move.b    d0,d1
            lsr.b    #2,d1
            subi.w    #$20,d1
            add.w    d1,8(a1)
            lsr.w    #8,d0
            lsr.b    #3,d0
            add.w    d0,$C(a1)
    And at the very end, after add.w d0,$C(a1), adding this line:
    Code:
    clr.b   ($FFFFFE1E).w   ; stop time counter

    Everything is simple. We save the ASM file, compile and check. Works)

    The guide is so simple that you can even not specify me in the credits))
     
  18. Giovanni

    Giovanni It's Joevanni, not Geovanni. Member

    Joined:
    Apr 16, 2015
    Messages:
    276
    Location:
    Italy
    Skip Score Tallies at the end of a level (for Sonic 1 2005)

    Congratulations! You finally got the fabled 24 second run in Green Hill Zone 1! Your reward? Around 8 seconds of unskippable hold time. Woo.

    Yeah, score tallies can be incredibly annoying. This guide provides two different ways to skip them.

    The code for score tallies explained

    The score tally routine, which starts at Obj3A_TimeBonus, fetches the bonuses that are calculated by the GotThroughAct subroutine (which is not the focus of this guide), and subtracts 10 (which appears as 100 in game, due to the way score is displayed) from each of them on every single frame, adding to the score until there is no further score to add (ergo, both bonus counters are zero).

    Changing the way it works for our needs is pretty simple. In this guide, I provide two methods: one much simpler, and one that is slightly more elaborate.

    1) Skip automatically (Classic Heroes style)

    Sonic Classic Heroes immediately skips all score tallies on the very first frame. This actually can provide some room for optimization of the score tally routine. We can rearrange, tweak, and remove portions of code to emulate the behavior of Classic Heroes.

    The way it works is pretty simple: instead of adding the score in increments of 10, we just add the entirety of the bonuses altogether on the first frame. This also removes the need to check for the presence of score bonuses.

    Here's how the code looks like after it is rearranged. You can read the comments for a further explanation of what is done. Portions of the original code have been commented out, with an explanation as to why.

    Code:
    Obj3A_TimeBonus:            ; XREF: Obj3A_Index
            bsr.w    DisplaySprite
            move.b    #1,($FFFFF7D6).w ; set time/ring bonus update flag
            moveq    #0,d0            ; clear d0 to initialize it
    ;        tst.w    ($FFFFF7D2).w    ; GIO: checking for the time bonus is unneeded
    ;        beq.s    Obj3A_RingBonus
            add.w    ($FFFFF7D2),d0        ; GIO: add the entire bonus to data register 0
            clr.w    ($FFFFF7D2).w ; GIO: clear the time bonus
    
    Obj3A_RingBonus:
    ;        tst.w    ($FFFFF7D4).w    ; GIO: checking for the ring bonus is also unneeded
    ;        beq.s    Obj3A_ChkBonus
            add.w    ($FFFFF7D4).w,d0        ; GIO: add the entire bonus to d0
            clr.w    ($FFFFF7D4).w ; GIO: clear the ring bonus
    
    Obj3A_ChkBonus:
    ;        tst.w    d0                ; GIO: no need for checking the bonus amount
    ;        bne.s    Obj3A_AddBonus    ; GIO: the Obj3A_AddBonus routine is entirely unneeded, so you can delete it.
            jsr    AddPoints            ; GIO: only thing you need from that routine is this jump to the subroutine that adds the points, which have been stored in d0.
            move.w    #$C5,d0
            jsr    (PlaySound_Special).l ;    play "ker-ching" sound
            addq.b    #2,$24(a0)
            cmpi.w    #$501,($FFFFFE10).w
            bne.s    Obj3A_SetDelay
            addq.b    #4,$24(a0)
    
    Obj3A_SetDelay:
            move.w    #180,$1E(a0)    ; set time delay to 3 seconds
    
    locret_C692:
            rts    
    2) Skip at the press of the A button (Mania style)

    This one is pretty much a combination of the original score tally routine and the Classic Heroes version of the routine. If A is pressed, the game uses the Classic Heroes code. If A isn't pressed, the game uses the original code. It's pretty easy to add one on top of the other.

    The way I do it is by first adding a label under the branch not equal to Obj3A_AddBonus, under which will lie the code for the Classic Heroes styled routine. I'll call this first new label "Obj3A_SkipTally".

    I then add a second label right above the line that moves $C5 in d0, and, right below the bne to Obj3A_AddBonus, I add a branch to that second new label, which prevents Obj3A_SkipTally's code from being ran unintentionally. This second new label will be called "Obj3A_Common".

    Finally, right above the check for $FFFFF7D2, I add a check for the A button. If the A button is pressed, branch to Obj3A_SkipTally. You want to check for the inputs that are stored in byte $FFFFF605, which are the controller's button presses. This translates to "if bit 6 of byte $FFFFF605 is not clear, branch". Alternatively, you can perform the check on byte $FFFFF604, if you prefer using held inputs.

    Below is what the code looks like with the changes made. This time, you DO need the Obj3A_AddBonus routine, so don't remove it.

    Code:
    Obj3A_TimeBonus:            ; XREF: Obj3A_Index
            bsr.w    DisplaySprite
            move.b    #1,($FFFFF7D6).w ; set time/ring bonus update flag
            moveq    #0,d0
            btst    #6,($FFFFF605).w ; GIO: is the A button pressed?
            bne.s   Obj3A_SkipTally    ; GIO: if yes, branch
            tst.w    ($FFFFF7D2).w    ; is time bonus    = zero?
            beq.s    Obj3A_RingBonus    ; if yes, branch
            addi.w    #10,d0        ; add 10 to score
            subi.w    #10,($FFFFF7D2).w ; subtract 10    from time bonus
    
    Obj3A_RingBonus:
            tst.w    ($FFFFF7D4).w    ; is ring bonus    = zero?
            beq.s    Obj3A_ChkBonus    ; if yes, branch
            addi.w    #10,d0        ; add 10 to score
            subi.w    #10,($FFFFF7D4).w ; subtract 10    from ring bonus
    
    Obj3A_ChkBonus:
            tst.w    d0        ; is there any bonus?
            bne.s    Obj3A_AddBonus    ; if yes, branch
            bra.s   Obj3A_Common
     
        Obj3A_SkipTally:
            add.w    ($FFFFF7D2),d0     ; GIO: add the entire bonus to data register 0
            clr.w    ($FFFFF7D2).w         ; GIO: clear the time bonus
            add.w    ($FFFFF7D4).w,d0   ; GIO: add the entire bonus to d0
            clr.w    ($FFFFF7D4).w         ; GIO: clear the ring bonus
            jsr    AddPoints            ; GIO: add up the points stored in d0
     
        Obj3A_Common:
            move.w    #$C5,d0
            jsr    (PlaySound_Special).l ;    play "ker-ching" sound
            addq.b    #2,$24(a0)
            cmpi.w    #$501,($FFFFFE10).w
            bne.s    Obj3A_SetDelay
            addq.b    #4,$24(a0)
    
    Obj3A_SetDelay:
            move.w    #180,$1E(a0)    ; set time delay to 3 seconds
    
    locret_C692:
            rts    
     
    Last edited: Sep 14, 2022
  19. Dark Shamil Khan

    Dark Shamil Khan TASer lol Member

    Joined:
    Nov 7, 2021
    Messages:
    60
    Location:
    Pakistan
    So uhh idk if this actually counts as a mini tutorial. But I'll call it a mini mini tutorial because funny.
    Anyhow this tutorial will showcase the one that Giovanni posted above. The second method of his tutorial. But instead there is a twist, instead of pressing A to skip the score tally, you can press A,B or C button to skip them.
    Below this line:
    Code:
    move.b     #1,($FFFFF7D6).w
    Remove this line:
    Code:
    moveq #0,d0
            btst #6,($FFFFF605).w
    and replace it with this line.
    Code:
    move.b  (v_jpadpress2).w,d0 ;read controller
    andi.b   #$70,d0  ;DSK: is ABC button pressed
    So yeah, that's the thing I did. Also if you are using the hivebrain disasm, then replace "_jpadpress2" with "$FFFFF603"
    Credits should all go to Giovanni for the main tutorial but yeah this was a short thing I did.
     
    Last edited: Sep 30, 2022
  20. Giovanni

    Giovanni It's Joevanni, not Geovanni. Member

    Joined:
    Apr 16, 2015
    Messages:
    276
    Location:
    Italy
    Make Time Overs reset your time when respawning at a checkpoint (For Sonic 1 REV00)

    A known feature of Time Overs is that, when you run out of time to clear a level, you restart the level from the checkpoint you died in, but with your timer completely reset. However, it's not always been the case.

    In REV00 of Sonic 1, dying from a Time Over won't reset the timer for the checkpoint, potentially leading to the player being SD-locked and being forced to reset their console and journey.

    Demonstration:

    timeoversdlock.gif

    Fixing this is a no-brainer: you've just got to port over exactly one line of code from REV01.

    For disassemblies based on Hivebrain 2005, or that otherwise do not include REV01 code, add this line right below the Obj39_ResetLvl label:

    Code:
            clr.l    ($FFFFFE38).w
    This line clears minutes, seconds and frame count of the checkpoint's timer whenever the player dies from a Time Over.

    For disassemblies based on GitHub, you're most likely already hacking REV01, and this guide won't be of use for you. However, if for whatever reason you chose to hack REV00 instead, open "_incObj\39 Game Over.asm", and remove these three lines in Over_ResetLvl:

    Code:
            if Revision=0
            else
            endc
    These three lines are what make the Time Over fix appear only in REV01 builds. Predictably enough, the removal of the three lines will make the fix appear in REV00 builds too.

    EDIT: I accidentally made a blunder and reset the wrong variable. Whoops!
     
    Last edited: Nov 5, 2022