How to make Sonic 2's Death Egg a standard 2-act Zone

Discussion in 'Tutorials' started by Speems, Aug 29, 2023.

  1. Speems

    Speems Well-Known Member Member

    Joined:
    Mar 14, 2017
    Messages:
    84
    Location:
    Rochester Hills, MI
    Something that was kinda interesting but weird was that Death Egg Zone exists in two games, yet only one makes use of a proper two-act system. That would be Sonic 3 and Knuckles, while the version present in Sonic 2 is only a single act. Want to make it closer to S3K's DEZ in terms of act structure? Here's how we can achieve it. This is performed in the most recent Github disassembly.

    Step 1: New Constant

    Before we continue, we need to establish a constant for act 2. Open up the s2.constants.asm file and scroll to the zone and act IDs table. Just before the prototype ID table is the entry we're looking for. Just edit it to represent the following:

    Code:
    death_egg_zone_act_1 =        (death_egg_zone<<8)|$00
    death_egg_zone_act_2 =        (death_egg_zone<<8)|$01
    Step 2: Signpost Check

    This is important to get to immediately as we'll get some graphical issues in the newly created Act 2 if we don't fix it. Scroll down to SetLevelEndType, which tells us which levels are not supposed to use a signpost (typically act 2's). You should see the following:

    Code:
    SetLevelEndType:
        move.w    #0,(Level_Has_Signpost).w    ; set level type to non-signpost
        tst.w    (Two_player_mode).w    ; is it two-player competitive mode?
        bne.s    LevelEnd_SetSignpost    ; if yes, branch
        nosignpost.w emerald_hill_zone_act_2
        nosignpost.w metropolis_zone_act_3
        nosignpost.w wing_fortress_zone_act_1
        nosignpost.w hill_top_zone_act_2
        nosignpost.w oil_ocean_zone_act_2
        nosignpost.s mystic_cave_zone_act_2
        nosignpost.s casino_night_zone_act_2
        nosignpost.s chemical_plant_zone_act_2
        nosignpost.s death_egg_zone_act_1
        nosignpost.s aquatic_ruin_zone_act_2
        nosignpost.s sky_chase_zone_act_1
    Just change the death_egg_zone_act_1 entry to instead use Act 2.

    Step 3: Level Events

    Scroll down to the DynamicLevelEventIndex table, and we should see an entry for LevEvents_DEZ. This is fine, except it only works for Act 1. We can incorporate a check for the current act and branching it to the act 2 label if we're in act 2.

    Code:
    LevEvents_DEZ:
        tst.b    (Current_Act).w
        bne.s    LevEvents_DEZ2
        rts
    ; ---------------------------------------------------------------------------
    LevEvents_DEZ2:
        moveq    #0,d0
        move.b    (Dynamic_Resize_Routine).w,d0
        move.w    LevEvents_DEZ_Index(pc,d0.w),d0
        jmp    LevEvents_DEZ_Index(pc,d0.w)
    Step 4: Level Offsets and ETC.

    Scroll down to Off_Level, and we will see two entries for DEZ. Change the first one to DEZ1, and the second one to DEZ2. This establishes proper layout data for the two acts.

    Code:
        zoneOffsetTableEntry.w Level_DEZ1    ; 28
        zoneOffsetTableEntry.w Level_DEZ2    ; 29
    In LevelSize, make sure the act 1 data for Death Egg is also in place for act 2, so we can make the former longer without screwing with the latter's size.

    Code:
        zoneTableEntry.w    $0,    $2780,    $0,     $C8    ; DEZ act 1
        zoneTableEntry.w    $0,    $1000,  $C8,     $C8    ; DEZ act 2
    In StartLocations, you'll see an entry for DEZ and a null entry below it. Copy and paste the sole entry below it, with the differences being the upper file being DEZ_1, and the lower file being DEZ_2. This allows proper standalone start positions for both acts.

    Code:
        zoneTableBinEntry    2, "startpos/DEZ_1.bin"    ; $0E
        zoneTableBinEntry    2, "startpos/DEZ_2.bin"
    In Obj34_ActNumber, loc_14146, and loc_14168, comment out the check for Death Egg. This allows the title card and results to make use of the Act 1/2 text, though the latter only shows up for Act 1. This is normal given how Act 2 now ends.

    In LevelOrder, replace the sole $FFFF entry with death_egg_zone_act_2. This allows the completion of Act 1 to move onto Act 2, and does not break any of the other levels in terms of order.

    Code:
    LevelOrder: zoneOrderedTable 2,2    ; WrdArr_LevelOrder
        zoneTableEntry.w  emerald_hill_zone_act_2
        zoneTableEntry.w  chemical_plant_zone_act_1    ; 1
        zoneTableEntry.w  emerald_hill_zone_act_1    ; 2
        zoneTableEntry.w  emerald_hill_zone_act_1    ; 3
        zoneTableEntry.w  wood_zone_act_2        ; 4
        zoneTableEntry.w  metropolis_zone_act_1        ; 5
        zoneTableEntry.w  emerald_hill_zone_act_1    ; 6
        zoneTableEntry.w  emerald_hill_zone_act_1    ; 7
        zoneTableEntry.w  metropolis_zone_act_2        ; 8
        zoneTableEntry.w  metropolis_zone_act_3        ; 9
        zoneTableEntry.w  sky_chase_zone_act_1        ; 10
        zoneTableEntry.w  emerald_hill_zone_act_1    ; 11
        zoneTableEntry.w  death_egg_zone_act_1        ; 12
        zoneTableEntry.w  emerald_hill_zone_act_1    ; 13
        zoneTableEntry.w  hill_top_zone_act_2        ; 14
        zoneTableEntry.w  mystic_cave_zone_act_1    ; 15
        zoneTableEntry.w  hidden_palace_zone_act_2     ; 16
        zoneTableEntry.w  oil_ocean_zone_act_1        ; 17
        zoneTableEntry.w  emerald_hill_zone_act_1    ; 18
        zoneTableEntry.w  emerald_hill_zone_act_1    ; 19
        zoneTableEntry.w  oil_ocean_zone_act_2        ; 20
        zoneTableEntry.w  metropolis_zone_act_1        ; 21
        zoneTableEntry.w  mystic_cave_zone_act_2    ; 22
        zoneTableEntry.w  oil_ocean_zone_act_1        ; 23
        zoneTableEntry.w  casino_night_zone_act_2    ; 24
        zoneTableEntry.w  hill_top_zone_act_1        ; 25
        zoneTableEntry.w  chemical_plant_zone_act_2    ; 26
        zoneTableEntry.w  aquatic_ruin_zone_act_1    ; 27
        zoneTableEntry.w  death_egg_zone_act_2        ; 28
        zoneTableEntry.w  emerald_hill_zone_act_1    ; 29
        zoneTableEntry.w  aquatic_ruin_zone_act_2    ; 30
        zoneTableEntry.w  casino_night_zone_act_1    ; 31
        zoneTableEntry.w  wing_fortress_zone_act_1     ; 32
        zoneTableEnd
    Don't forget that the Level_DEZ entry should be duplicated to make use of layouts for two acts, like so:

    Code:
    ;---------------------------------------------------------------------------------------
    ; DEZ act 1 level layout (Kosinski compression)
    Level_DEZ1:    BINCLUDE    "level/layout/DEZ_1.bin"
        even
    ;---------------------------------------------------------------------------------------
    ; DEZ act 2 level layout (Kosinski compression)
    Level_DEZ2:    BINCLUDE    "level/layout/DEZ_2.bin"
        even
    Step 5: Make Your Layout for DEZ Act 1

    After all of our code tweaks, any attempt to build will complain about the lack of certain files. Just duplicate the vanilla layout and startpos for DEZ, with the differences being the current filenames in s2.asm. You can also edit the SonLVL ini file to properly establish the layouts in the program like so:

    Code:
    [Death Egg Zone Act 1]
    tiles=../art/kosinski/CPZ_DEZ.bin
    blocks=../mappings/16x16/CPZ_DEZ.bin
    chunks=../mappings/128x128/CPZ_DEZ.bin
    layout=../level/layout/DEZ_1.bin
    objects=../level/objects/DEZ_1.bin
    rings=../level/rings/DEZ_1.bin
    palette=../art/palettes/SonicAndTails.bin:0:0:16|../art/palettes/DEZ.bin:0:16:48
    startpos=../startpos/DEZ_1.bin:Sonic:Level Start
    colind1=../collision/CPZ and DEZ primary 16x16 collision index.bin
    colind2=../collision/CPZ and DEZ secondary 16x16 collision index.bin
    objlst=objDEZ.ini
    animtiles1=../art/uncompressed/Animated background section (CPZ and DEZ).bin:0:0x0326:2
    animblocks=DEZ/AnimatedBlocks.bin
    [Death Egg Zone Act 2]
    tiles=../art/kosinski/CPZ_DEZ.bin
    blocks=../mappings/16x16/CPZ_DEZ.bin
    chunks=../mappings/128x128/CPZ_DEZ.bin
    layout=../level/layout/DEZ_2.bin
    objects=../level/objects/DEZ_2.bin
    rings=../level/rings/DEZ_2.bin
    palette=../art/palettes/SonicAndTails.bin:0:0:16|../art/palettes/DEZ.bin:0:16:48
    startpos=../startpos/DEZ_2.bin:Sonic:Level Start
    colind1=../collision/CPZ and DEZ primary 16x16 collision index.bin
    colind2=../collision/CPZ and DEZ secondary 16x16 collision index.bin
    objlst=objDEZ.ini
    animtiles1=../art/uncompressed/Animated background section (CPZ and DEZ).bin:0:0x0326:2
    animblocks=DEZ/AnimatedBlocks.bin
    After all that, you should be able to have a proper two-act system for Sonic 2's Death Egg Zone! Current issues at the moment are regarding the parallax may be somewhat glitchy in Act 1 if you're not careful. Just to be safe, change this line of code in the EndingSequence section...

    Code:
        move.w    #death_egg_zone_act_1,(Current_ZoneAndAct).w
    ...to reflect we're in act 2:

    Code:
        move.w    #death_egg_zone_act_2,(Current_ZoneAndAct).w
    The sequence itself shouldn't have problems with this tweak given that we have a proper constant. Then again, this should only be done if you've modified the Act 1 layout to have a bonkers change to the background.
     
  2. Sonbeta

    Sonbeta Newcomer Trialist

    Joined:
    Jul 2, 2023
    Messages:
    4
    Cool tutorial!
     
  3. JGamer2151

    JGamer2151 Well-Known Member/Lurker Member

    Joined:
    Dec 1, 2020
    Messages:
    96
    Interesting. Now I can only imagine if there were two normal acts of DEZ and a "third" one that is basically the DEZ in Sonic 2 in a similar fashion to MTZ…

    …if that’s how I think of it anyway, if I wanted just two normal acts of DEZ and leave the original DEZ intact as a "third" act.

    Overall, very interesting guide.