Sonic 2 special stage - Bug (Tiny Bombs)

Discussion in 'Engineering & Reverse Engineering' started by Bentoc, Jun 2, 2021.

  1. Bentoc

    Bentoc

    Member
    3
    8
    3
    France
    My first post ... Hello everyone !

    Did you ever notice some inconsistency of Bombs size in Sonic 2 special stage ?

    A simple line of objects show differents bomb size at same z position :
    [​IMG]

    [​IMG]

    [​IMG]

    Bombs are not intended to be that small, look at their much bigger shadows !

    Here's the explaination:

    z_pos of objects in special stage is managed by objoff_30 :
    $1 is front of camera, the higher the value, the more distant the object

    The following code set the animation of the Bomb based on z_pos :

    Code (ASM):
    1.  
    2. loc_35150:
    3.     cmpi.b    #$A,anim(a0)          
    4.     beq.s    return_3516A                    ; return if explosion, no need to scale the object
    5.     move.w    objoff_30(a0),d0
    6.     cmpi.w    #$1D,d0
    7.     ble.s    loc_35164
    8.     moveq    #$1E,d0                          ; cap the most distant value
    9.  
    10. loc_35164:
    11.     move.b    byte_35180(pc,d0.w),anim(a0) ; set new animation
    12.  
    13. return_3516A:
    14.     rts
    15. ; ===========================================================================
    16.  
    17. loc_3516C:
    18.     move.l    (sp)+,d0
    19.     move.l    objoff_34(a0),d0
    20.     beq.w    JmpTo63_DeleteObject
    21.     movea.l    d0,a1 ; a1=object
    22.     st    objoff_2A(a1)
    23.  
    24.     if removeJmpTos
    25. JmpTo63_DeleteObject ; JmpTo
    26.     endif
    27.  
    28.     jmpto    (DeleteObject).l, JmpTo63_DeleteObject
    29. ; ===========================================================================
    30. byte_35180:
    31.     dc.b   9,  9,  9,  8,  8,  7,  7,  6,  6,  5,  5,  4,  4,  3,  3,  3
    32.     dc.b   2,  2,  2,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0; 16
    33. ; ===========================================================================
    34.  
    The table byte_35180 defines animation id, the values are OK.
    So why bombs are sometimes small when they are near Camera ?
    Have a look at Bomb animation script :

    Code (ASM):
    1.  
    2. ; animation script:
    3. ; off_364CE:
    4. Ani_obj61:    offsetTable
    5.         offsetTableEntry.w byte_364E4    ;  0
    6.         offsetTableEntry.w byte_364E7    ;  1
    7.         offsetTableEntry.w byte_364EA    ;  2
    8.         offsetTableEntry.w byte_364ED    ;  3
    9.         offsetTableEntry.w byte_364F0    ;  4
    10.         offsetTableEntry.w byte_364F3    ;  5
    11.         offsetTableEntry.w byte_364F6    ;  6
    12.         offsetTableEntry.w byte_364F9    ;  7
    13.         offsetTableEntry.w byte_364FC    ;  8
    14.         offsetTableEntry.w byte_364FF    ;  9
    15.         offsetTableEntry.w byte_36502    ; $A
    16. byte_364E4: dc.b  $B,  0,$FF
    17.     rev02even
    18. byte_364E7: dc.b  $B,  1,$FF
    19.     rev02even
    20. byte_364EA: dc.b  $B,  2,$FF
    21.     rev02even
    22. byte_364ED: dc.b  $B,  3,$FF
    23.     rev02even
    24. byte_364F0: dc.b  $B,  4,$FF
    25.     rev02even
    26. byte_364F3: dc.b  $B,  5,$FF
    27.     rev02even
    28. byte_364F6: dc.b  $B,  6,$FF
    29.     rev02even
    30. byte_364F9: dc.b  $B,  7,$FF
    31.     rev02even
    32. byte_364FC: dc.b  $B,  8,$FF
    33.     rev02even
    34. byte_364FF: dc.b  $B,  9,$FF
    35.     rev02even
    36. byte_36502: dc.b   2, $A, $B, $C,$FF
    37.     even
    38.  
    anim: $0 is small bomb, $9 is biggest bomb, $A is explosion
    The animation duration is $B with only one image.

    Now the animation routine :

    Code (ASM):
    1.  
    2. loc_3539E:
    3.     subq.b    #1,anim_frame_duration(a0)
    4.     bpl.s    return_353E8                               ; even if new anim is set, frame will not be updated until frame duration reach -1
    5.     moveq    #0,d0
    6.     move.b    anim(a0),d0
    7.     add.w    d0,d0
    8.     adda.w    (a1,d0.w),a1
    9.     move.b    (a1),anim_frame_duration(a0)
    10.     moveq    #0,d1
    11.     move.b    anim_frame(a0),d1
    12.     move.b    1(a1,d1.w),d0
    13.     bpl.s    loc_353CA
    14.     move.b    #0,anim_frame(a0)
    15.     move.b    1(a1),d0
    16.  
    17. loc_353CA:
    18.     andi.b    #$7F,d0
    19.     move.b    d0,mapping_frame(a0)
    20.     move.b    status(a0),d1
    21.     andi.b    #3,d1
    22.     andi.b    #$FC,render_flags(a0)
    23.     or.b    d1,render_flags(a0)
    24.     addq.b    #1,anim_frame(a0)
    25.  
    26. return_353E8:
    27.     rts
    28.  
    Here it is : even if new anim is set, frame will not be updated until frame duration reach -1
    As the animation last 12 frames, it will skip a lot of values in byte_35180 table !

    To complete this analysis, the following code explain why shadows scale is ok. It is simply linked to the animation of the parent object.

    Code (ASM):
    1.  
    2. loc_3534E:
    3.     moveq    #9,d0
    4.     sub.b    anim(a1),d0
    5.  
    A simple fix is to set animation duration to 0 instead of $B, now it looks like this :

    [​IMG]

    No more tiny Bombs ...

    So why the animation duration is $B ?
    - maybe a last time adjustment to lower the size of bombs (instead of setting different values in byte_35180 ?)
    - maybe bombs had animations that were removed

    This bug also applies to Rings that use the same animation routine ... but as their anim duration is 5, it is much less visible.

    A better fix to this bug is to modify the animation routine for sure !
     
    Last edited: Jun 6, 2021
    • Informative Informative x 14
    • Like Like x 6
    • List
  2. MarkeyJester

    MarkeyJester

    A D V A N C E Resident Jester
    2,078
    211
    43
    Japan
    Oh my absolute fucking god...

    I am so surprised at how this is easily missed! Well done for not only noticing, but also for finding a fix!
     
  3. Sonic Hachelle-Bee

    Sonic Hachelle-Bee

    Taking a Sand Shower Tech Member
    747
    69
    28
    Lyon, France
    Sonic 2 Long Version
    That is very well spotted!

    This is true for bombs, rings, and... the Chaos Emerald. Now, this is beautiful (and animation is smooth):

    Chaos Emerald.png

    I have a more polished fix to propose.

    In loc_35164, save the last animation value in the unused byte $23 of the bomb/ring/emerald object. Possibly, the animation value will be updated the following line:
    Code (ASM):
    1. loc_35164:
    2.     move.b    anim(a0),$23(a0)
    3.     move.b    byte_35180(pc,d0.w),anim(a0)
    In loc_3539E, test $23 with the current animation value (that possibly changed above). If the values are not the same, skip the test on the frame duration. Then, reset $23 with the current animation value:
    Code (ASM):
    1. loc_3539E:
    2.     move.b    $23(a0),d0
    3.     cmp.b    anim(a0),d0
    4.     bne.s    .skip
    5.     subq.b    #1,anim_frame_duration(a0)
    6.     bpl.s    return_353E8
    7. .skip:
    8.     move.b    anim(a0),$23(a0)
    9.     moveq    #0,d0
     
    Last edited: Jun 6, 2021
    • Informative Informative x 5
    • List
  4. Alex Field

    Alex Field

    シュート! カオス・エメラルド・ザが消えようとしている! Member
    119
    69
    28
    Downunda, Mobius
    Sonic the Hedgehog 2+, Sonic the Hedgehog 3+
    Cool fix.
     
  5. Spicy Bread SSR

    Spicy Bread SSR

    You can call me Mal if you like Member
    8
    5
    3
    Sorry to bring this forum up again, but I think what I say might help someone who wants to make a proper fix

    Since I suck at coding, I decided to just remove the code causing the animation issue (loc_3539E). The rings spaz out out due to having an animation and using the same code, but this does fix the bomb size issue, and restores the unused chaos emerald animation. Yep, that's it.

    By going with this method, our only issue is trying to animate the rings while moving them somewhat smoothly. Maybe we can do that using Hachelle's proposed method?