Simple bug and detail fixing thread

Discussion in 'Tutorials' started by FireRat, Mar 21, 2016.

  1. FireRat

    FireRat "The grand imitator..." Member

    Joined:
    Feb 25, 2016
    Messages:
    116
    I was thinking on submitting this one as a standalone tutorial, but when finished I got to doubt about its matter here. However, it might be cool to collect those little "notes" that does not deserve too much, but might still be worth sharing (for perfectionists);
    I thought on doing this thread to be used just like the Minihacks or Sonic Retro's "Various Bugfixes"...


    [Sonic 1; GitHub] Howto: Fix the Sonic controls at the signpost

    If I am correct, glitches and bugs that are found during testing process are classified in a minimumdetail-to-gamebreaking hierarchy; those minimum issues, although some may be very easy to trigger, they get fixed at last or just left behind (unless it is so minimal that does not even pass to the people's conscience). That seems to be the case here. This one is a very simple and trivial to fix; this is mostly another attempt of mine to bring a blatant issue into a wider attention.

    How the signpost appears to be planned to behave:
    • Right boundary remaining at the edge of the screen, not next to it.
    • Always after spinning sequence, it has to "capture" Sonic's controls, make him run to the right, no exception.
    • While controls are captured, the right boundary should shift, so Sonic can move off the screen.

    ... by the way, just like the prison capsule, guys!.


    For some reason, the right boundary is always next to the edge of the screen, so we can keep Sonic under our control if stays there. Even if we fix that part, we can still keep our control... the cause being for either a bad branch, or a bad width check.

    Step 1: Fixing the boundary
    The engine allows Sonic to pass a few pixels beyond it, by default. The bosses however setup a flag so it does not happen, to keep the gameplay appropiate. The fix for this should be: During spin this flag should be set, and should be clear after taking the controls.

    Open _incObj\0D Signpost.asm. The code that checks for Sonic's collision (and its effects) should be located at Sign_Touch; just set the flag to f_lockscreen right after the collision check. Finally this flag must be clear after the checks in Sign_SonicRun.

    Step 2: Fixing the controls
    Like previously stated, the fix consists on either fixing a bad width check or a bad branch. But I prefer to fix the latter mostly as a cosmetic decision; if the score tally's music plays before Sonic fully disappears of the screen feels a bit more arbitrary and perhaps less "forced", on my personal opinion.

    If you prefer to fix (increase) the range check, it is located at loc_EC70.

    If you prefer the branch fix, at Sign_SonicRun, there is a check to ensure that Sonic isn't rolling around. Just redirect its branch to a code return.

    EDIT: Grammar, clearification and spelling
     
    Last edited: Mar 21, 2016
    AbyssalLeopard likes this.
  2. TheStoneBanana

    TheStoneBanana The Bananaman Member

    Joined:
    Nov 27, 2013
    Messages:
    571
    Location:
    The Milky Way Galaxy
    In a small hack I'm making (you'll see what it is shortly), Sonic needed to stand on top of a spring without it bouncing. So, adding the correct checks and such, I noticed that Sonic was standing above the spring. Like, WAY above.

    [​IMG]
    The issue is actually a really simple oversight by Sonic Team which, until this point, hasn't been noticed because you typically don't stand on a spring. (You can't!)
    There is a routine called SolidObject which, as the name suggests, makes an object solid for Sonic to stand on, push on, or do whatever. The routine requires a couple parameters, which are:
    • d1 = width
    • d2 = height / 2 (when jumping)
    • d3 = height / 2 (when walking)
    • d4 = x-axis position
    So, that said, the issue comes into play when the spring is set as solid. Typically, d3 will be set to about 1 or 2 more than d2. However...
    Code:
    Obj41_Up:         ; XREF: Obj41_Index
         move.w   #$1B,d1
         move.w   #8,d2
         move.w   #$10,d3
         move.w   8(a0),d4
         bsr.w   SolidObject
         ...
    
    Look at what d3 is set to. It get's set to #$10 (16 in decimal) which is wrong. It's supposed to be #10 (10 in decimal). Fixing the oversight results in this:

    [​IMG]
    Much better, is it not?
    Once again, this isn't even going to be noticed in normal gameplay, so it's not that big of a deal. It's just a silly little oversight by Sonic Team that I felt like pointing out.

    EDIT: I just wanted to add that this bug is also prevalent in upside down springs and the fix is exactly the same. Sideways springs do not have this issue.
     
    Last edited: Jun 12, 2016
  3. MarkeyJester

    MarkeyJester It takes only one mistake. Member

    Joined:
    Jun 27, 2009
    Messages:
    2,511
    Location:
    Miss Fox's heart~
    This is why I don't think in both decimal and hexadecimal when programming, and solely stick with hex as much as possible. It prevents little silly mistakes like these d=

    Well done though, and well spotted!
     
    Novedicus and TheStoneBanana like this.
  4. Dandaman955

    Dandaman955 She wakes up with the sun... Member

    Joined:
    Jan 18, 2010
    Messages:
    848
    Location:
    North Wales
    Technically speaking, he's still a pixel off, anyway. :p
     
    HackGame, FireRat and TheStoneBanana like this.
  5. FireRat

    FireRat "The grand imitator..." Member

    Joined:
    Feb 25, 2016
    Messages:
    116
    Sonic 1's ending sequence allows to enable debug mode by holding A during boot, even if no cheat has been activated.

    Code:
            btst    #bitA,(v_jpadhold1).w ; is button A pressed?
            beq.s    End_LoadSonic    ; if not, branch
            move.b    #1,(f_debugmode).w ; enable debug mode
    
    End_LoadSonic:
    
    To fix the issue, simply do the same the normal levels do:

    Code:
            tst.b    (f_debugcheat).w ; has debug cheat been entered?    ; ++
            beq.s    End_LoadSonic    ; if not, branch            ; ++
            btst    #bitA,(v_jpadhold1).w ; is A button held?
            beq.s    End_LoadSonic    ; if not, branch
            move.b    #1,(f_debugmode).w ; enable debug mode
    
    End_LoadSonic:
    
    
     
  6. Selbi

    Selbi Are you friend, or foe real?! Retired Staff

    Joined:
    Jul 20, 2008
    Messages:
    2,378
    Location:
    Northern Germany
    Huh, I had no idea that bug even existed. Nice fix.

    Because I had to replay FZ to test it just now (too lazy to directly hack myself into the ending sequence) I was reminded of another glitch. Since this is such a small bug, I don't think anyone ever bothered to make a tutorial to fix it. Might as well do it here (I assume with "Simple bug and detail fixing thread" you mean this is open to anyone).

    Basically, when Eggman is hit, he flashes and becomes invulnerable for a short duration before he can get hit again. FZ is designed so that you shouldn't (normally) be able to hit him more than once while the pillars move out, yet if you time it just right, you indeed can. Design oversight or intentional I don't know, but the problem it causes is an underflow if you do it while the boss is being defeated, resulting in the boss fight continuing, getting another 255 lives as you hit it while it's exploding. Joy.

    Go to loc_19F6A and find this line:
    Code:
            move.b    #$64,$35(a0)
    The $64 is the amount of frames Eggman will flash (exactly 100 in decimal). The fix is quick and dirty, but does the job just fine. Simply increase that number to a ridiculous one like $FF, so that the window for another hit never opens. Bam.

    You could also entirely remove the countdown for the flashing counter, but I figured you may want to keep it in somehow (I did back then for ERaZor, where the flashing intervals vary between live and defeated boss).

    Maybe I'll do more of these small bug fixes. Feels like wasted effort to make entire threads of them.
     
    TheStoneBanana likes this.
  7. TheStoneBanana

    TheStoneBanana The Bananaman Member

    Joined:
    Nov 27, 2013
    Messages:
    571
    Location:
    The Milky Way Galaxy
    Have you ever noticed that, while on platforms, shields lag slightly behind Sonic? This issue isn't quite as noticable on platforms that just move back in forth in one direction, but on a swinging platform, the shield looks less than pleasing.


    The problem is this~
    Shields have reserved object RAM that they load into, so they are executed first. Platforms are loaded into dynamic object RAM, so they are executed afterwards. Platforms modify Sonic's position in order to appear moving on the platform, but this happens AFTER the shield copies Sonic's positioning for that frame. So, as a result, the shield does not correctly track Sonic on platforms.

    The fix is super duper simple. At the end of MvSonic2 (a part of MvSonicOnPtfm), add this:
    Code:
         tst.b   ($FFFFFE2C).w     ; does Sonic have a shield?
         beq.s   locret_7B62       ; if not, branch
         move.w   d0,($FFFFD180+$C).w   ; apply change to Shield's Y-Position
         sub.w    d2,($FFFFD180+8).w   ; apply change to Shield's X-Position
    
    All it does is check if Sonic has a shield, and if he does, it applies the changes the platform makes to the shield's position. Here, as you can see, the problem is solved:

     
    Last edited: Jun 16, 2016
  8. TheStoneBanana

    TheStoneBanana The Bananaman Member

    Joined:
    Nov 27, 2013
    Messages:
    571
    Location:
    The Milky Way Galaxy
    Nobody has posted in this thread for a while, so I assume it's alright for me to revive it with a double post.
    This is an extremely small detail that, honestly, most of you probably missed. This is the "Simple Bug and Detail Fixing Thread" after all, though.

    In the Sonic CD warping cutscene, take a look at Sonic.

    [​IMG]
    Notice anything? No? Well, let's compare Sonic's normal palette to the one used in this cutscene:

    [​IMG]
    Oh dear! It seems that the palette entry that is supposed to be black has been misplaced by a lime green color from the background palette! Never fear, this is a quite easy fix.
    In WARP__.MMD, go to address 0x70B (or if you're just going to edit the ISO itself, go to address 0x13C370B), and change it's value to 00. This will correct the color to be black, and will give us this result:

    [​IMG]
    Looks better, doesn't it? I agree.
    If you're curious, this coloring error was indeed fixed in the 2011 port of Sonic CD.
     
  9. TheStoneBanana

    TheStoneBanana The Bananaman Member

    Joined:
    Nov 27, 2013
    Messages:
    571
    Location:
    The Milky Way Galaxy
    Hey! So here is something that I guess isn't really considered a bug, but more of a detail fix. :)
    In Collision Chaos of Sonic CD, there are ladybug-like badniks known as Tentou which drop spiked-bombs like so:


    Pay attention to how the bomb itself is dropped. It snaps directly to the floor as soon as the Tentou drops it. This is a little odd, isn't it?
    Well, it seems that the bombs were supposed to drop, but doing so was avoided by changing the bomb's position to be directly on the floor before even spawning! To restore this functionality, you'll need to dig into the Tentou's object code. For this example here (as the Tentou's code is obviously duplicated across zones), I'll be using CCZ Act 1, Present.
    Go to address 0xBA8A. The code itself is what spawns the bomb that Tentou drops, and it appears something like this:
    Code:
    ...
        jsr     (SingleObjLoad+$200000)
        bne.s   locret_BAB0
        move.b  0(a0),0(a1)
        move.l  8(a0),8(a1)
        move.l  $C(a0),$C(a1)
        addi.w  #$10,$C(a1)      ; <---------
        move.b  #1,$28(a1)
    ...
    
    The line that I have commented an arrow at is the offending line. Simply nop it out, or make it add 0. Now, the bomb will actually fall, as demonstrated here:


    Why was this changed? I'm not sure. The dropping is a bit slow, so the bomb does seem to lag behind Tentou slightly, but I don't think that's too big of a deal for most people.
     
  10. FireRat

    FireRat "The grand imitator..." Member

    Joined:
    Feb 25, 2016
    Messages:
    116
    Interesting nitpick, though I'd argue that line was in there for better "visual correctness". Without it, the spikeball would have been hidden behind the badnik's sprite for some frames, then only be visible when it's horizontally quite far apart, making it look like it spawn misaligned, or pooping it from behind than the shooter at the bottom
     
    PsychoMMS, HackGame and MarkeyJester like this.
  11. Novedicus

    Novedicus Oh Member

    Joined:
    Aug 26, 2013
    Messages:
    627
    So, notice how bridges in Sonic 2 can only have an even amount of segments? Well, here's how you can allow for odd number of segments.

    In "Obj11_Init", there's this little segment of code:
    Code:
            lsr.w    #1,d0
            lsl.w    #4,d0    ; (d0 div 2) * 16
    
    This basically forces the number of segments to be even and then shifts it to what it needs to be. What you need to do is just change that to:
    Code:
            lsl.w   #3,d0   ; d0 * 8
    
    But, we aren't done yet. There's a little segment of code that appears to be an incorrect calculation for calculating the X position of the second set of bridge segments (for when it's needed) (although, this could be intentional, but I'm not quite sure). Take a look at this in the same routine:
    Code:
            move.w  d4,d0
            add.w   d0,d0
            add.w   d4,d0   ; d0*3
            move.w  sub2_x_pos(a1,d0.w),d0
    
    For one thing, it's mutliplying the offset used to get the X position value in the sub sprite data by 3, which is wrong. Sub sprite data each take up 6 bytes, not 3. So, what we need to do is just make it:
    Code:
            move.w  d4,d0
            add.w   d0,d0
            add.w   d4,d0
            add.w   d0,d0   ; d0*6
            move.w  sub2_x_pos(a1,d0.w),d0
    
    But there's still one more thing: because the segment count is based on 1 instead of 0 (meaning 1 = 1 segment, instead of 0 = 1 segment), it's getting the wrong data. For example, with 1 segment, it will multiply 1 by 6, and get the data from sub3_x_pos, since sub2_x_pos+6 = sub3_x_pos, which isn't used with only 1 segment. So, all we need to do is this, so that it will point to the correct data:
    Code:
            move.w  d4,d0
            add.w   d0,d0
            add.w   d4,d0
            add.w   d0,d0   ; d0*6
            move.w  sub2_x_pos-next_subspr(a1,d0.w),d0
    
    And that's it! I can't seem to find any issues with doing this, but if any of you find any, let me know.
     
  12. Novedicus

    Novedicus Oh Member

    Joined:
    Aug 26, 2013
    Messages:
    627
    So, a small update. So, with the new calculation I added is actually incorrect. The multiplication by 3 is intentional, since 2 segments is supposed to use sub3_x_pos and 4 segments use sub4_x_pos, etc, rather than 2 using sub4_x_pos, 4 using sub6_x_pos, etc. Here's a fixed version of it that handles odd numbered segments, by rounding down the offset:
    Code:
            move.w  d4,d0
            add.w   d0,d0
            add.w   d4,d0    ; d0*3
            btst    #0,d0    ; align the offset for odd numbered segments
            beq.s    .getX
            subq.w    #3,d0
    .getX:
            move.w  sub2_x_pos(a1,d0.w),d0
    
     
    Last edited: Jun 13, 2017