don't click here

Sonic CD Quirks/Deconstruction

Discussion in 'Engineering & Reverse Engineering' started by Devon, Jul 11, 2022.

  1. Brainulator

    Brainulator

    Regular garden-variety member Member
    I wonder what this would look like if the code were referenced and in a more complete/functional state.
     
  2. Lurker

    Lurker

    sleep Member
    74
    26
    18
  3. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,299
    1,507
    93
    your mom
    That someone is me, by the way; I run that account. I forgot to crosspost the video here lol
     
    Last edited: Dec 22, 2022
  4. Brainulator

    Brainulator

    Regular garden-variety member Member
    I feel a bit of a delay in between seeking out the PCM versus CD-DA data, but for what it's worth, that actually sounds pretty nice, awkward cutting off aside.
     
  5. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,299
    1,507
    93
    your mom
    The 1996 PC version of Sonic CD adds additional code in the floor collision function that fixes a slight visual and physics bug for when you're on a flat surface, but the left sensor is detecting a block with a steep enough angle.

    [​IMG]

    I will use Sonic 1 as a quick example to illustrate my point, as that is what's most convenient at this moment. Here, I have placed these 2 blocks next to each other. The left block has a flat surface after the sloped part.

    [​IMG]

    Under normal circumstances, when both sensors are on a flat surface, it'll use the angle from the left sensor. In this case, if Sonic's left sensor is on that left block, his angle will be set to the angle of that slope.

    [​IMG]

    With the fix in place, Sonic can walk on that flat bit on the left block just fine.

    [​IMG]

    This fix is NOT present in the Sega CD version of the game.

    Here's the bug in action with Lapper and Mercury's overlay script running. Look at Sonic's left floor sensor as it approaches the surface of the left block.

    [​IMG]

    By the way, I ported the fix to 68000 assembly. In the floor mode, place this between where the sensors are checked (after the 2 calls to FindFloor) and the call to the function that picks a sensor to use ("bsr.w Sonic_Angle" in Sonic 1):
    Code (Text):
    1.  
    2.                 cmpi.b  #-1,(a4)                        ; Is the left sensor's angle set to snap to a flat surface?
    3.                 beq.s   .NoFix                          ; If so, branch
    4.                 move.w  d0,d2                           ; Are we on a completely flat surface?
    5.                 or.w    d1,d2
    6.                 bne.s   .NoFix                          ; If not, branch
    7.                 cmpi.b  #-$10,(a4)                      ; Is the left sensor's angle set to a steep enough angle?
    8.                 bge.s   .NoFix                          ; If it's not steep enough, branch
    9.                 cmpi.b  #-$40,(a4)
    10.                 blt.s   .NoFix                          ; If it's too steep/out of range, branch
    11.                 st      (a4)                            ; Force left sensor's angle to snap to a flat surface
    12.              
    13. .NoFix:
     
    Last edited: Dec 26, 2022
    • Like Like x 5
    • Informative Informative x 2
    • Useful Useful x 1
    • List
  6. Bobblen

    Bobblen

    Member
    403
    206
    43
    I'm fascinated by the fact that the 96 pc port is basically a rev 02. Most of the pc ports I've looked at barely go to the trouble of feature parity!
     
  7. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,299
    1,507
    93
    your mom
    Continuing the topic of collision detection changes, the Sega CD version of Sonic CD adds additional code that fixes a bug when Sonic is standing on top of a column with a negative height (starts from the top and points downwards).

    [​IMG]

    In this example, Sonic is standing on top of a block that's solid in the top half, so each column in the block has a negative height. The block's angle is also set to 0x80, which would normally make Sonic go upside down when he touches it.

    [​IMG]

    Without this fix, Sonic would react like this when colliding with that log.

    [​IMG]

    None of the other Genesis titles fix this bug.
     
  8. Blue Spikeball

    Blue Spikeball

    Member
    2,411
    1,010
    93
    Funny how they fixed details like those, yet missed more obvious bugs introduced by the port like special stage palette issues.
     
  9. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,299
    1,507
    93
    your mom
    Let's talk about level map data. The original Sega CD version allocates double the memory for map data from Sonic 1, and thus allows for levels to be double the height... kinda, not really actually...

    [​IMG]

    The collision detection takes advantage of this, bar Tidal Tempest, which is set to the original limitations due to level wrapping.

    [​IMG]

    ...except that the drawing functions don't, and still retain the original limits.

    [​IMG]

    The map data itself also does not go beyond the original limitations, either. There is not a single stage that is taller than 0x800 pixels.

    As a side note, when calculating the X position index for getting map data, it limits it down to 0-0x7F, which is not correct, because each row in the map data is actually 2 64 chunk wide rows interlaced with each other, with the first for plane A, and the second for plane B. The limit should be 0x3F.

    [​IMG]



    Now, let's discuss how the 1996 PC version handles getting map data for collision. It's a bit of a doozy.

    [​IMG]

    A few things were added. Such as preventing the position from going negative in Palmtree Panic Act 1 and Metallic Madness. There's also what appears to be a first attempt to port over the original m68k code that's bugged, and is made redundant by what's next.

    The original AND masks are used for calculating the position indices, but are effectively overridden. The PC version doesn't not actually have the double allocated map memory and sticks to the Sonic 1 limitations. What was originally a Tidal Tempest only limitation is effectively put into use in all levels with the "iYwork &= 7" line, and it also fixes the horizontal out of bounds issue by adding "iWork &= 0x3F".

    Finally, it... does the negative position check again for Palmtree Panic Act 1, which is completely redundant considering that the calculated indices won't even be negative.
     
    • Informative Informative x 5
    • Like Like x 2
    • List
  10. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,299
    1,507
    93
    your mom
    I probably mentioned this elsewhere and forgot, but I found this darkly amusing. According to the Gems Collection symbols, the official names of the functions that removes animals and enemies according to the type of future are "friend_suicide" and "enemy_suicide" respectively, and they are found in a file called "SUICIDE.C".

    [​IMG]

    Of course, I understand that these were written by Japanese developers, and they most likely didn't understand the implications, but holy shit, lol. This is basically Sonic CD's "Genocide City" moment in a way.

    Speaking of which, when a badnik is removed when a good future is achieved, they're supposed to play the explosion sound effect if they were on screen. However, the check for if they're on screen is called AFTER they are destroyed, so the on screen flag is cleared, and the sound is never played.

    [​IMG]

    As a side note, the PC/Gems Collection version of Sonic CD moves the A+B+C button check for when your paused to restart the level outside of the pause check, which would make it possible to press A+B+C to restart the level even when not paused...

    [​IMG]

    However, the PC and GameCube versions only allocate 1 button for jumping, and the PS2 version only allocates A to the cross button and B to the rest of the face buttons, so it's not possible to trigger anyways.

    The music pause function in the PC version is also bugged. It uses an incorrect operator when checking if the flag is already set, and also gets the check for if the invincibility or speed shoes music is playing in the past backwards, as "sub_sync(0x90)" is for pausing PCM music.

    [​IMG]

    The only reason why music pausing even works is because the pause handler manually calls "CDPause()" itself. "sub_sync" doesn't even handle command IDs 0x90 and 0xD5 in the port!

    The music unpause function has the first check correct, but the invincibility/speed shoes music check when in the past is still wrong.

    [​IMG]

    And again, IDs 0x91 and 0xD6 aren't even handled in sub_sync, so this is all redundant anyways.
     
    Last edited: Dec 27, 2022
  11. Clownacy

    Clownacy

    Tech Member
    1,076
    638
    93
    Ring Sound Bug
    Here's a relevant cross-post of an old bug explanation of mine:

    Every time the player collects a ring, the game plays the ring sound on a different speaker, alternating between left and right. Sonic CD's SFX sound driver does this in a boneheaded way. The way that it's done in Sonic 1/2/3 is that there are two separate ring sounds, and the driver decides which one is played when a ring is collected. Both versions of the ring sound are different: playing on a different speaker (of course), running on a different FM channel, and even having slightly different durations (except for in Sonic 3).

    If the two sounds were to use the same FM channel, then you would only ever be able to hear one of them at a time. This means that if you were to grab two rings in quick succession, then instead of hearing the ring sound coming from both speakers, you'd only hear it in one.

    This is what Sonic CD screws up, but the reason behind it is the real dumb part:

    Instead of the driver alternating between the two ring sounds, Sonic CD makes the sounds themselves do it: whenever a ring is collected, the game always tries to play the ring sound that uses the rightmost speaker, and this sound then uses a custom command which I've named 'smpsDoRing':
    Code (Text):
    1.  
    2. ; FM4 Data
    3. RingR_FM4:
    4.    smpsDoRing
    5.    smpsSetvoice        $00
    6.    smpsPan             panRight, $00
    7.    dc.b   nE5, $05, nG5, $05, nC6, $1B
    8.    smpsStop
    What this command does is force the game to start playing the left ring sound every other time it is used. This has the effect of making the left ring sound play every other time a ring sound plays.

    This should work, right? Nope, it doesn't.

    Because every attempt to play the ring sound will first cause the right ring sound to play, the developers needed to make the left ring sound stop the right ring sound from playing, so that playing the left ring sound doesn't cause the right ring sound to play too. To do this, they made both ring sounds play on the same FM channel. This causes the two sounds to always cancel-out each other when they play, making it impossible to hear both speakers play the ring sound when collecting two rings at once.

    This issue is at its worst when the player quickly collects a line of rings, as each ring sound will be abruptly cut-off by the next one, meaning that the only ring sound that the player will get to hear finish is the last one. This can be heard as early as the very start of the first level of the game. In Sonic 1/2/3, this is not the case: the left ring sound can only be cut-off by the left ring sound, and the right ring sound can only be cut-off by the right ring sound, meaning that ring sounds only get cut off every two rings that the player collects, not one.

    Sound Priority Bug
    Sonic CD's sound driver has a second bug: SMPS has a sound priority system, which prevents lower-priority sounds from interrupting higher-priority sounds. This is useful, for example, for preventing the ring-pickup sound from stopping and interrupting the much-louder ring-loss sound. The way this is done is with a big array, where each value in the array belongs to a certain sound ID. So, for instance, sound 0x90 would get the first value in the array, 0x91 would get the second value, etc. The problem is that Sonic CD only has enough entries in this table for sound IDs 0x90 through to 0xDF, despite using a higher sound ID for the 'stop all sounds' command. This means that, when the game uses the 'stop all sounds' command, the driver will read a nonsensical priority value from past the end of the priority array. If the nonsensical priority value is lower than the value priority of the sound that is currently playing, then the stop-all-sounds command will not take effect.

    This might sound like a non-issue: what are the chances of this having a noticeable effect? When does the game even submit a stop-all-sounds command anyway?

    During time travel - that's when. And of course this bug happens to be an important part of it.

    Yes, you read that right: the time travel scene depends on this bug in order to work properly. As the screen fades to white, the game submits a stop-all-sounds command, but because the time travel sound's priority is higher than the command's broken priority, it doesn't actually do anything. If this bug were to be fixed, then the time travel sound would abruptly cut-off long before it normally ends.

    I discovered both of these bugs while making my replacement Sonic CD SFX sound driver. It's based on Sonic & Knuckles' driver, which lacks this bug, causing the time travel sound to end early. Replacing this bug exactly as it is in Sonic CD's driver does not work, as the nonsensical priority data after the end of the priority array is actually the start of sound 0x90's track data (the skidding sound). Because the data of a sound varies from driver to driver, this bug simply cannot be accurately recreated in a different driver by making the priority table too short.

    So how did I eventually recreate this bug in my replacement driver? I copied the raw data of sound 0x90 from the original driver and added it to the end of my driver's priority table. I essentially turned a part of sound 0x90 into priority data. With this, the behaviour caused by the bug is retained without the actual bug existing.

    If you're wondering why I was creating a replacement for Sonic CD's SFX sound driver, it was so I could restore the original jump sound. You can read more about it here.
     
    Last edited: Aug 14, 2023
    • Like Like x 6
    • Informative Informative x 1
    • List
  12. rata

    rata

    Member
    691
    73
    28
    Argentina
    Trying to be useful somehow.
    Of all the flaws Sonic CD has, I must be the only one that prefers its jump sound. I find it less ear piercing that the squarewave one.
     
  13. Clownacy

    Clownacy

    Tech Member
    1,076
    638
    93
    You're not: I've been getting YouTube comments that say the same for years.
     
    Last edited: Dec 29, 2022
  14. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,299
    1,507
    93
    your mom
    @Clownacy I went ahead and reposted the ring sound bug and sound priority bug explanations on my Sonic CD Deconstructed account, and provided additional examples to help explain them. I should note that the "stop all sounds" command is played every time it exits an MMD file, not just when leaving the time warp cutscene.

    [​IMG]

    As an addendum, they also forgot to assign the drowning sound (0xDF) a priority value.
     
    Last edited: Nov 16, 2023
  15. Its funny untill now, I never noticed it had sound bugs. Still for me the best 2D sonic game ever LOL
     
  16. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,299
    1,507
    93
    your mom
    And now, why it's not good practice to copy and paste functions across files...

    Introducing, "block_chk". This function is part of feature where you can place a 16x16 block wherever in a level. It's used in Collision Chaos to erase glass bumper blocks (they're not objects, they're embedded into the stage chunks), and also was used by the prototype waterfall and earthquake effects. This function specifically checks if the position the block is to be placed at is on screen. If it isn't, then the drawing isn't performed, just the data setting.

    When they ported over the function initially, it seems they accidentally implemented the comparisons incorrectly. This can be seen in R11A, for example, where it's not used:
    [​IMG]

    For Collision Chaos, though, they did fix the comparisons. Otherwise, the glass bumpers wouldn't have functioned correctly, after all. But, now there's this situation where the function is broken in certain stages, and fixed in others.
     
  17. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,299
    1,507
    93
    your mom
    3D ramp/loop

    I made a video about how a 3D ramp/loop works in Palmtree Panic Act 1 Present:


    Additional notes:
    • Gravity is only disabled if Sonic is going fast enough (at least 8 upwards). If he slows down, then gravity is enabled, which is why when you let go of the jump button, he starts falling.
    • He will also only be boosted forwards if he's moving fast enough.
    Metal Sonic projector sprite cropping

    The Metal Sonic projector's sprites have the 1st row of pixels cropped out in the 1996 PC version:
    [​IMG]

    This actually extends to the 2011 remake a little, where a couple sprites have that cropping issue, but others are just fine:
    [​IMG]

    Unreferenced bridge object in the v0.02 Prototype

    So, in the v0.02 prototype of Sonic CD, object ID 0x25 just points to a blank object. However, there is leftover code that was assigned to that ID. By adding the pointer back in, it can be seen that object 0x25 was a log bridge! The graphics for it were already found, and in fact, are also loaded into VRAM. The mappings were also referenced in the debug mode object list. If you restore the bridge pointer and re-enable debug mode, you can place bridges.



    Interesting thing to note is that bridges were actually placed in the level earlier in the development, but got dummied out, with a row of those invisible platforms being put in its place.

    Also, if you set its subtype value to be nonzero, it does this:
    [​IMG]
     
    Last edited: Jan 25, 2023
    • Informative Informative x 12
    • Like Like x 4
    • List
  18. This reminds me of how the bridges in AIZ and HCZ are coded to break; would be cool to see this restored and recoded fully!
     
  19. JayKuriN

    JayKuriN

    Defeated Dreamer Member
    I don't quite recall how far I got on my personal disassembly of R11B from 0.02, It was subpar for the time since this was over a year ago by this point, late 2021. What I do remember specifically, is a fuckton of unreferenced pieces of object code (or seeming like such). I might go through and look through it again. I also remember specifically that object ID 06 was weird, so was ID 0C. I don't know if the time zone matters so I'll assume it does not for now, but pattern I did notice was that most of them seemed to check a value before jumping to execution. If you do know what this is, could you explain?
     
  20. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,299
    1,507
    93
    your mom
    Object 06 is just a test badnik object (it's called "test_act" in the source code, according to the Gems Collection symbols). It can still be found in the final.

    Object 0C is a completely erased object that was called "exit2_set", which is related to those splashes that appear when you exit through tunnel that has water on top. My best guess is that it would've triggered a splash on collision (the unused earthquake object had the player collide with an object called "jisin_set", which spawned another object called "jisin" that actually performed the crumbling). However, instead, they opted to have a door that makes the splash instead if it's set to do so. All the code for "exit2_set" has been erased, but its pointer was kept in, so it just points to whatever is after it, a.k.a. the tunnel door collision check routine, and they kept it like that all the way to the final version.

    That may have all been the bridge object. The code for it is *massive*. It has 17 routines! Otherwise, with R11B, I have not found anything that's not in R11A.

    I will bring to light these 2 small objects that I also found in R11A that are also unreferenced, but they don't really do much of anything.
    Code (Text):
    1. UnkObject1:
    2.     moveq   #0,d0
    3.     move.b  oRoutine(a0),d0
    4.     move.w  .Index(pc,d0.w),d0
    5.     jmp     .Index(pc,d0.w)
    6.  
    7. ; ---------------------------------------------------------------------------
    8.  
    9. .Index:
    10.     dc.w    UnkObject1_Init-.Index
    11.     dc.w    UnkObject1_Main-.Index
    12.  
    13. ; ---------------------------------------------------------------------------
    14.  
    15. UnkObject1_Init:
    16.     addq.b    #2,oRoutine(a0)
    17.     move.l    #MapSpr_UnkObject1,oMap(a0)
    18.     move.b    #4,oSprFlags(a0)
    19.     move.b    #1,oPriority(a0)
    20.     move.b    #$10,oWidth(a0)
    21.     move.w    #$400,oTile(a0)
    22.     move.w    #$C00,oTile(a0)
    23.     move.w    #$1400,oTile(a0)
    24.     move.w    #$24FC,oTile(a0)
    25.  
    26.     move.l    (objPlayerSlot.oX).w,d0
    27.     addi.l    #$A0000,d0
    28.     move.l    d0,oX(a0)
    29.     move.l    (objPlayerSlot.oY).w,d0
    30.     subi.l    #$320000,d0
    31.     move.l    d0,oY(a0)
    32.     rts
    33.  
    34. ; ---------------------------------------------------------------------------
    35.  
    36. UnkObject1_Main:
    37.     move.l    (objPlayerSlot.oX).w,d0
    38.     addi.l    #$A0000,d0
    39.     move.l    d0,oX(a0)
    40.     move.l    (objPlayerSlot.oY).w,d0
    41.     subi.l    #$320000,d0
    42.     move.l    d0,oY(a0)
    43.     move.b    #0,oAnim(a0)
    44.     lea    (Ani_UnkObject1).l,a1
    45.     jsr    AnimateObject
    46.     jmp    DrawObject
    47.     rts
    48.  
    49. ; ---------------------------------------------------------------------------
    50.     ...
    51. ; ---------------------------------------------------------------------------
    52.  
    53. Ani_UnkObject1:
    54.     dc.w    .Ani0-Ani_UnkObject1
    55.     dc.w    .Ani1-Ani_UnkObject1
    56.     dc.w    .Ani2-Ani_UnkObject1
    57.  
    58. .Ani0:
    59.     dc.b    $1D, 0, 1, $FF
    60.  
    61. .Ani1:
    62.     dc.b    0, 0, $FF
    63.    even
    64.  
    65. .Ani2:
    66.     dc.b    0, 1, $FF
    67.     even
    68.  
    69. ; ---------------------------------------------------------------------------
    70.  
    71. MapSpr_UnkObject1:
    72.     dc.w .Spr0-MapSpr_UnkObject1
    73.     dc.w .Spr1-MapSpr_UnkObject1
    74.  
    75. .Spr0:
    76.     dc.b    1
    77.     dc.b    $F8, 5, 0, 0, $F8
    78.  
    79. .Spr1:
    80.     dc.b    1
    81.     dc.b    $F8, 5, 0, 4, $F8
    82.  
    Code (Text):
    1. UnkObject2:
    2.     moveq    #0,d0
    3.     move.b    oRoutine(a0),d0
    4.     move.w    .Index(pc,d0.w),d0
    5.     jmp    .Index(pc,d0.w)
    6.  
    7. ; ---------------------------------------------------------------------------
    8.  
    9. .Index:
    10.     dc.w    UnkObject2_Init-.Index
    11.     dc.w    UnkObject2_Main-.Index
    12.  
    13. ; ---------------------------------------------------------------------------
    14.  
    15. UnkObject2_Init:
    16.     addq.b    #2,oRoutine(a0)
    17.     move.l    #MapSpr_UnkObject2,oMap(a0)
    18.     move.b    #4,oSprFlags(a0)
    19.     move.b    #1,oPriority(a0)
    20.     move.b    #$10,oWidth(a0)
    21.     move.w    #$47A,oTile(a0)
    22.  
    23.     move.l    (objPlayerSlot.oX).w,d0
    24.     addi.l    #$32,d0
    25.     move.l    d0,oX(a0)
    26.     move.l    (objPlayerSlot.oY).w,d0
    27.     subi.l    #$320000,d0
    28.     move.l    d0,oY(a0)
    29.     rts
    30.  
    31. ; ---------------------------------------------------------------------------
    32.  
    33. UnkObject2_Main:
    34.     move.l    (objPlayerSlot.oX).w,d0
    35.     addi.l    #$320000,d0
    36.     move.l    d0,oX(a0)
    37.     move.l    (objPlayerSlot.oY).w,d0
    38.     subi.l    #$320000,d0
    39.     move.l    d0,oY(a0)
    40.     lea    (Ani_StaticObj).l,a1
    41.     jsr    AnimateObject
    42.     jmp    DrawObject
    43.     rts
    44.  
    45. ; ---------------------------------------------------------------------------
    46.     ...
    47. ; ---------------------------------------------------------------------------
    48.  
    49. Ani_StaticObj:
    50.     dc.w    .Ani0-Ani_StaticObj
    51.     dc.w    .Ani1-Ani_StaticObj
    52.     dc.w    .Ani2-Ani_StaticObj
    53.     dc.w    .Ani3-Ani_StaticObj
    54.     dc.w    .Ani4-Ani_StaticObj
    55.     dc.w    .Ani5-Ani_StaticObj
    56.  
    57. .Ani0:
    58.     dc.b    $1D, 0, $FF
    59.     even
    60.  
    61. .Ani1:
    62.     dc.b    $1D, 1, $FF
    63.     even
    64.  
    65. .Ani2:
    66.     dc.b    $1D, 2, $FF
    67.     even
    68.  
    69. .Ani3:
    70.     dc.b    $1D, 3, $FF
    71.     even
    72.  
    73. .Ani4:
    74.     dc.b    $1D, 4, $FF
    75.     even
    76.  
    77. .Ani5:
    78.     dc.b    $1D, 5, $FF
    79.     even
    80.  
    81. ; ---------------------------------------------------------------------------
    82.  
    83. MapSpr_UnkObject2:
    84.     dc.w .Spr0-MapSpr_UnkObject2
    85.     dc.w .Spr1-MapSpr_UnkObject2
    86.     dc.w .Spr2-MapSpr_UnkObject2
    87.  
    88. .Spr0:
    89.     dc.b    2
    90.     dc.b    $F0, 7, 0, 0, $F0
    91.     dc.b    $F0, 7, 0, 0, 0
    92.  
    93. .Spr1:
    94.     dc.b    2
    95.     dc.b    $F0, $D, 0, 8, $F0
    96.     dc.b    0, $D, 0, 8, 0
    97.  
    98. .Spr2:
    99.     dc.b    2
    100.     dc.b    $F0, $D, 8, 8, $F0
    101.     dc.b    0, $D, 8, 8, 0
    102.     even
    103.  

    Those 2 objects are basically the same, they just set themselves to Sonic's position, offseted. They don't do anything else. The only difference is their mappings and animation data. I currently do not know if there are any graphics data that would fit them. The first object is found with all the badniks, and the second one is found among the springboard, unused seesaw, bridge, spikes, and unused swing.

    Also, here are some unused mappings after the second unreferenced object's mappings:
    Code (Text):
    1. MapSpr_Unknown:
    2.     dc.w    .Spr0-MapSpr_Unknown
    3.     dc.w    .Spr1-MapSpr_Unknown
    4.     dc.w    .Spr2-MapSpr_Unknown
    5.     dc.w    .Spr3-MapSpr_Unknown
    6.  
    7. .Spr0:
    8.     dc.b    4
    9.     dc.b    $F0, 3, 0, 0, $F0
    10.     dc.b    $F1, 3, 0, 0, $F8
    11.     dc.b    $F2, 3, 0, 0, 0
    12.     dc.b    $F3, 3, 0, 0, 8
    13.  
    14. .Spr1:
    15.     dc.b    4
    16.     dc.b    $F0, $C, 0, 4, $F0
    17.     dc.b    $F8, $C, 0, 4, $F1
    18.     dc.b    0, $C, 0, 4, $F2
    19.     dc.b    8, $C, 0, 4, $F3
    20.  
    21. .Spr2:
    22.     dc.b    4
    23.     dc.b    $F0, $C, 8, 4, $F3
    24.     dc.b    $F8, $C, 8, 4, $F2
    25.     dc.b    0, $C, 8, 4, $F1
    26.     dc.b    8, $C, 8, 4, $F0
    27.  
    28. .Spr3:
    29.     dc.b    2
    30.     dc.b    $F0, 3, 0, 0, $F4
    31.     dc.b    $F0, 3, 0, 0, 4
    32.  

    And also this set of mappings data that doesn't even have an index table attached:
    Code (Text):
    1.     dc.b    1
    2.     dc.b    $F8, 5, 0, 0, $F8
    3.     dc.b    1
    4.     dc.b    $F8, $D, 0, 4, $F0
    5.     dc.b    1
    6.     dc.b    $FC, 0, 0, $C, $FC
    7.     dc.b    1
    8.     dc.b    $F0, 3, 0, $D, $FC
    9.     dc.b    1
    10.     dc.b    $F0, 3, 0, $11, $FC
    11.     dc.b    1
    12.     dc.b    $F8, 5, 8, 0, $F8
    13.     dc.b    1
    14.     dc.b    $F8, $D, 8, 4, $F0
    15.     dc.b    1
    16.     dc.b    $FC, 0, 8, $C, $FC
    17.     dc.b    1
    18.     dc.b    $F0, 3, 8, $D, $FC
    19.     dc.b    1
    20.     dc.b    $F0, 3, 8, $11, $FC

    Again, not sure if there are any graphics that would fit them.

    If you are referring to this:
    Code (Text):
    1. ObjMosqui:
    2.     move.b  obj.oSubtype(a0),d0
    3.     bmi.w   ObjMosquiBroken
    4.     bra.w   ObjMosquiNormal
    5.  
    6. ; ---------------------------------------------------------------------------
    7.  
    8. ObjPataBata:
    9.     move.b  obj.oSubtype(a0),d0
    10.     bmi.w   ObjPataBataBroken
    11.     bra.w   ObjPataBataNormal
    12.  
    13. ; ---------------------------------------------------------------------------
    14.  
    15. ObjAnton:
    16.     move.b  obj.oSubtype(a0),d0
    17.     bmi.w   ObjAntonBroken
    18.     bra.w   ObjAntonNormal
    19.  
    20. ; ---------------------------------------------------------------------------
    21.  
    22. ObjTagaTaga:
    23.     move.b  obj.oSubtype(a0),d0
    24.     bmi.w   ObjTagaTagaBroken
    25.     bra.w   ObjTagaTagaNormal
    26.  
    27. ; ---------------------------------------------------------------------------
    28.  
    29. ObjTamabboh:
    30.     move.b  obj.oSubtype(a0),d0
    31.     bmi.w   ObjTamabbohBroken
    32.     bra.w   ObjTamabbohNormal

    Then as you can see, it's using the subtype variable to distinguish between a busted badnik and a normal one. They basically made the 2 types separate objects, just tied under a singular ID, distinguished by the subtype ID. In the final, they merged them and placed checks whereever needed.

    If you are not referring to this, could you provide examples of what you mean?
     
    Last edited: Jan 27, 2023
    • Like Like x 3
    • Informative Informative x 2
    • List