Actions

SCHG How-to

Add Hill Top Zone to 2 Player VS Mode

From Sonic Retro

(Original guide by E-122-Psi)

(Special thanks to MoDule and D.A. Garden)

This was largely done in the Xenowhirl edit, though the variations in GitHub shouldn't be too hard to find.

The main reason Sonic 2's VS Mode is so small is because of the lack of compatibility with many levels in split screen (mainly tile mapping setups). Hill Top Zone is at least a step of the way there, largely due to a mix of using a lot of Emerald Hill's mappings and tiles, most of the sprites already being optimised for it, and even some leftover data from what seems to be a failed attempt at making the zone work in VS mode. Now we can make it (mostly) fully realised:

Htz 2p Screenshot 01.png


Step 1: Making it available in the VS menu:

Menu Select

First we need to go to MenuScreen and change what one of the icons in the 2P menu directs to.

In this case we will change the special stage, so we go to the directs in word_8E52 and change the last entry to 7 (Hill Top's placement in the game's code):

word_8E52:
    dc.w    0
    dc.w  $B00    ; 1
    dc.w  $C00    ; 2
    dc.w  $700    ; 3

Icon

Now we need to edit the icon to match it. The data for the level image is in off_8F7E.

Again to exemplify we'll edit the special stage icon, so it will be the final two entries, editing the art and palette.

off_8F7E:
    dc.l byte_874A
    dc.l byte_878C
    dc.w $4104
    dc.w 3
    dc.w $FF
    dc.w $330
    dc.l byte_8757
    dc.l byte_878C
    dc.w $412C
    dc.w 3
    dc.w $5FF
    dc.w $3A8
    dc.l byte_8764
    dc.l byte_878C
    dc.w $4784
    dc.w 3
    dc.w $6FF
    dc.w $3C0
    dc.l byte_877F
    dc.l byte_878C
    dc.w $47AC
    dc.w 3
    dc.w $2FF
    dc.w $360

The final part is the easiest to find. Go to word_8732 and then find the entries below reading the 2 player text, and edit accordingly.

Remember when altering the header to keep it the same number characters listed before it.

Also remember to edit "STAGE" into "ZONE" if you're editing Special Stage.

    ; 2-player mode menu text

byte_874A:    dc.b  $B,"EMERALD HILL"
byte_8757:    dc.b  $B," MYSTIC CAVE"
byte_8764:    dc.b  $B,"CASINO NIGHT"
byte_8771:    dc.b  $C,"  HILL TOP   "
byte_877F:    dc.b  $B,"  HILL TOP  "
byte_878C:    dc.b   4,"ZONE "
byte_8792:    dc.b   4,"ZONE "
byte_8798:    dc.b   8,"GAME OVER"
        dc.b   8,"TIME OVER"
byte_87AC:    dc.b   6,"NO GAME"
byte_87B4:    dc.b   3,"TIED"
byte_87B9:    dc.b   2," 1P"
byte_87BD:    dc.b   2," 2P"

Htz menu fix 01.png

And there you go Hill Top is now accessible.


Step 2: Making the pathway work in 2 player mode:

Now if you're playing through it now, you'll notice some incompatibilities, in all likeliness you won't be able to complete the level even.

Now we're going to edit the zone's edit routines to be skipped over for two player mode, omitting the camera detection, the boss triggers, and earthquake effects:

LevEvents_HTZ_Routine1:
    tst.w    (Two_player_mode).w
    bne.s    loc_HTZ_2P1
    cmpi.w    #$400,(Camera_Y_pos).w
    bcs.s    LevEvents_HTZ_Routine1_Part2
    cmpi.w    #$1800,(Camera_X_pos).w
    bcs.s    LevEvents_HTZ_Routine1_Part2
    move.b    #1,($FFFFEEBC).w
    move.l    (Camera_X_pos).w,($FFFFEE08).w
    move.l    (Camera_Y_pos).w,($FFFFEE0C).w
    moveq    #0,d0
    move.w    d0,($FFFFEEB4).w
    move.w    d0,($FFFFEEB6).w
    move.w    d0,($FFFFEEE2).w
    move.w    #320,($FFFFEEE4).w
    subi.w    #$100,($FFFFEE0C).w
    move.w    #0,($FFFFEEE6).w
    addq.b    #2,(Dynamic_Resize_Routine).w ; => LevEvents_HTZ_Routine2

return_E9E8:
    rts

; ---------------------------------------------------------------------------
loc_HTZ_2P1:
    move.w    #$2800,(Camera_Max_X_pos).w
    move.w    #$2800,(Tails_Max_X_pos).w

    rts

We'll do the same for Act Two's routine, remember to add a check on the branch right at the start above the index:

 ; sub_EBEA:
LevEvents_HTZ2:
    tst.w    (Two_player_mode).w
    bne.s    LevEvents_HTZ2_2
    bsr.w    LevEvents_HTZ2_Prepare
LevEvents_HTZ2_2:
    moveq    #0,d0
    move.b    (Dynamic_Resize_Routine).w,d0
    move.w    LevEvents_HTZ2_Index(pc,d0.w),d0
    jmp    LevEvents_HTZ2_Index(pc,d0.w)
; ===========================================================================
; off_EBFC:
LevEvents_HTZ2_Index:
    dc.w LevEvents_HTZ2_Routine1 - LevEvents_HTZ2_Index    ;  0
    dc.w LevEvents_HTZ2_Routine2 - LevEvents_HTZ2_Index    ;  2
    dc.w LevEvents_HTZ2_Routine3 - LevEvents_HTZ2_Index    ;  4
    dc.w LevEvents_HTZ2_Routine4 - LevEvents_HTZ2_Index    ;  6
    dc.w LevEvents_HTZ2_Routine5 - LevEvents_HTZ2_Index    ;  8
    dc.w LevEvents_HTZ2_Routine6 - LevEvents_HTZ2_Index    ; $A
    dc.w LevEvents_HTZ2_Routine7 - LevEvents_HTZ2_Index    ; $C
    dc.w LevEvents_HTZ2_Routine8 - LevEvents_HTZ2_Index    ; $E
    dc.w LevEvents_HTZ2_Routine9 - LevEvents_HTZ2_Index    ;$10
; ===========================================================================
; loc_EC0E:
LevEvents_HTZ2_Routine1:
    tst.w    (Two_player_mode).w
    bne.s    loc_HTZ_2P2
    cmpi.w    #$14C0,(Camera_X_pos).w
    bcs.s    loc_EC6C
    move.b    #1,($FFFFEEBC).w
    move.l    (Camera_X_pos).w,($FFFFEE08).w
    move.l    (Camera_Y_pos).w,($FFFFEE0C).w
    moveq    #0,d0
    move.w    d0,($FFFFEEB4).w
    move.w    d0,($FFFFEEB6).w
    move.w    d0,($FFFFEEE2).w
    move.w    #$2C0,($FFFFEEE4).w
    subi.w    #$100,($FFFFEE0C).w
    move.w    #0,($FFFFEEE6).w
    addq.b    #2,(Dynamic_Resize_Routine).w ; => LevEvents_HTZ2_Routine2
    cmpi.w    #$380,(Camera_Y_pos).w
    bcs.s    return_EC6A
    move.w    #-$680,($FFFFEEE2).w
    addi.w    #$480,($FFFFEE08).w
    move.w    #$300,($FFFFEEE4).w
    addq.b    #6,(Dynamic_Resize_Routine).w ; => LevEvents_HTZ2_Routine5

return_EC6A:
    rts
; ---------------------------------------------------------------------------
loc_HTZ_2P2:
    move.w    #$2D80,(Camera_Max_X_pos).w
    move.w    #$2D80,(Tails_Max_X_pos).w

    rts

Now while this fixes the pathway, you may still notice some issues while playing. The levels, especially Act 2, are a bit overcrowded with objects, which causes some serious lag and in some cases even breaks the quota and causes objects to stop loading at a certain point.

Even if you reach the end of Act Two, there is no signpost, it will just activate the boss instead.


IMPORTANT: Make backup copies of the object files for both acts of Hill Top if you want to preserve the 1 player layouts.

Go to a level editor like SonLVL and skim down some of the objects that aren't needed.

Get rid of all traces of Object 30, that is the earthquake data, which doesn't work in two player mode and would break the flow anyway (no wonder the remake disabled it for online).

I'd personally advise taking out some of the one way barriers, which are kind of pointless now, and the Rexons, which have a duplicate loading issue which I haven't yet been able to fix (the airlifts have a similar issue, though we can work around that).

Take note the objects are loaded in a vertical scroll from top to bottom of the level as you progress, so try not to have too many objects in the same area and in line with each other in terms of y-position, otherwise they may not load or will heavily slow down the game. Using the tutorial to add the Sonic 3 and Knuckles objects manager might also give you more leeway in how much you can keep.

Also remember to add some objects according to the changes to multiplayer, the top route of Act Two for example will be unreachable without the earthquake collision, while the rising lava sections will now be empty and have bottomless pits.

Oh and don't forget to add a nice signpost a little after where we've added a new max camera position in Act Two. Don't worry, it won't appear in one player mode.

Signpost Edit 01.png


Step 3: Fixing the airlifts:

You may have noticed the airlifts don't quite work in splitscreen, disappearing about halfway through the journey and screwing up the player's gravity:

Airlift Glitch 01.png

So we're going to give them a bit of a boost up for split screen, while in SonLVL, highlight every airlift one by one, and mark both their Long Distance and Remember State entries as 'True'.

Airlift Fix 02.png

This has the downside that none of the airlifts will regenerate for players lagging behind. Luckily, MoDule has managed to make a fix so the airlifts will still respawn set in long distance/remember state mode.

First go to loc_21E10 and redirect the despawning branch to MarkObjGone_P1 instead of JmpTo5_MarkObjGone:

loc_21E10:
    move.w    x_pos(a0),-(sp)
    bsr.w    loc_21E2C
    moveq    #0,d1
    move.b    width_pixels(a0),d1
    move.w    #-$28,d3
    move.w    (sp)+,d4
    bsr.w    JmpTo3_PlatformObject
    jmp    (MarkObjGone_P1).l

Next we're gonna have this object's routine handle the spawning behaviour for the bridge poles as well, go to loc_21E7C and edit the loading branch for obj1C into this:

loc_21E7C:
    bsr.w    JmpTo4_ObjectMove
    subq.w    #1,objoff_34(a0)
    bne.s    return_21EC0
    addq.b    #2,routine_secondary(a0)
    move.b    #2,mapping_frame(a0)
    move.w    #0,x_vel(a0)
    move.w    #0,y_vel(a0)
    bsr.w    JmpTo4_SingleObjLoad2
    bne.s    return_21EC0
    _move.b    #$16,0(a1) ; load obj16
    move.w    x_pos(a0),x_pos(a1)
    move.w    y_pos(a0),y_pos(a1)
    move.b    render_flags(a0),render_flags(a1)
    move.b    #4,routine(a1)    ; => Obj16_Vines
    move.l    #Obj16_MapUnc_21F14,mappings(a1)
    move.w    #$43E6,art_tile(a1)
    jsr    Adjust2PArtPointer2
    ori.b    #4,render_flags(a1)
    move.b    #$20,width_pixels(a1)
    move.b    #1,mapping_frame(a1)
    move.b    #1,priority(a1)

return_21EC0:
    rts

Now just put a despawning direct for the replacement vine object under return_21ECO:

; ===========================================================================
; Replaces use of Obj1C

Obj16_Vines:
    jmp    MarkObjGone_P1

Don't forget to add this as a new routine too:

; ===========================================================================
off_21DBA:
    dc.w loc_21DBE-off_21DBA
    dc.w loc_21E10-off_21DBA; 1
    dc.w Obj16_Vines-off_21DBA; 2
; ===========================================================================

Step 4: Making separate 1p and 2p Mode Object files:

When you're fine with the edits and object layouts and the level is playing properly in VS, go to level/objects and rename both act files as HTZ_1_2P and HTZ_2_2P. Now take your backup files from before and add them back into the folder.


Now we need to redirect the asm to them in two player mode. Thankfully Casino Night already does the same thing so we have the right direction already.

; ============== END RELATIVE OFFSET LIST ===================================

loc_17AB8:
    addq.b    #2,(Obj_placement_routine).w
    move.w    (Current_ZoneAndAct).w,d0    ; If level == $0F (ARZ)...
    ror.b    #1,d0        ; then this yields $87...
    lsr.w    #6,d0        ; and this yields $0002.
    lea    (Off_Objects).l,a0    ; Next, we load the first pointer in the object layout list pointer index,
    movea.l    a0,a1        ; then copy it for quicker use later.
    adda.w    (a0,d0.w),a0    ; (Point1 * 2) + $0002
    tst.w    (Two_player_mode).w    ; skip if not in 2-player vs mode
    beq.s    loc_17AF0
    cmpi.b    #$C,(Current_Zone).w    ; skip if not Casino Night Zone
    bne.s    loc_17AB8_HTZ
    lea    (Objects_CNZ1_2P).l,a0    ; CNZ 1 2-player object layout
    tst.b    (Current_Act).w        ; skip if not past act 1
    beq.s    loc_17AF0
    lea    (Objects_CNZ2_2P).l,a0    ; CNZ 2 2-player object layout
loc_17AB8_HTZ:
    cmpi.b    #$7,(Current_Zone).w    ; skip if not Hill Top Zone
    bne.s    loc_17AF0
    lea    (Objects_HTZ1_2P).l,a0    ; HTZ 1 2-player object layout
    tst.b    (Current_Act).w        ; skip if not past act 1
    beq.s    loc_17AF0
    lea    (Objects_HTZ2_2P).l,a0    ; HTZ 2 2-player object layout

We'll put the two player layouts next to CNZ multiplayer ones, which is right under return_18028:

;---------------------------------------------------------------------------------------
; CNZ act 1 object layout for 2-player mode (various objects were deleted)
;---------------------------------------------------------------------------------------
; byte_1802A;
Objects_CNZ1_2P:    BINCLUDE    "level/objects/CNZ_1_2P.bin"
;---------------------------------------------------------------------------------------
; CNZ act 2 object layout for 2-player mode (various objects were deleted)
;---------------------------------------------------------------------------------------
; byte_18492:
Objects_CNZ2_2P:    BINCLUDE    "level/objects/CNZ_2_2P.bin"
;---------------------------------------------------------------------------------------
; HTZ act 1 object layout for 2-player mode (various objects were deleted)
;---------------------------------------------------------------------------------------
; byte_1802A;
Objects_HTZ1_2P:    BINCLUDE    "level/objects/HTZ_1_2P.bin"
;---------------------------------------------------------------------------------------
; HTZ act 2 object layout for 2-player mode (various objects were deleted)
;---------------------------------------------------------------------------------------
; byte_18492:
Objects_HTZ2_2P:    BINCLUDE    "level/objects/HTZ_2_2P.bin"

Now try both modes and they should each have separate layouts according your edits and the originals.

Note however since the level editors won't recognise the 2p files now they're renamed, so you'll need label them the original names again if you wanna make any more edits to them.

Step 5: Fix the background art:

Now to fix that botched background, which is one of the more convoluted tasks. Thankfully some leftover scroll data has made it a bit easier for us. The developers seem to have planned ahead for a Hill Top split screen and disabled the background scrolling effects (likely so they don't cause conflicts from two screens) so we can just do likewise.

The reason the far mountains are disabled is because their VRAM location conflicts with splitscreen data put in the same area, so we have to move it for two player mode if we are to reactivate it.

IMPORTANT: First again backup your 16x16 and 128x128 mapping files of HTZ (and EHZ just to be safe).

Go to Dynamic_HTZ and edit like so:

Dynamic_HTZ:
    lea    ($FFFFF7F0).w,a3
    tst.w    (Two_player_mode).w
    bne.w    Dynamic_HTZ_2P
    moveq    #0,d0
    move.w    (Camera_X_pos).w,d1
    neg.w    d1
    asr.w    #3,d1
    move.w    (Camera_X_pos).w,d0
    move.w    d0,d2
    andi.w    #$F,d2
    seq.b    d2
    ext.w    d2
    lsr.w    #4,d0
    add.w    d1,d0
    add.w    d2,d0
    subi.w    #$10,d0
    divu.w    #$30,d0
    swap    d0
    cmp.b    1(a3),d0
    beq.s    BranchTo_loc_3FE5C
    move.b    d0,1(a3)
    move.w    d0,d2
    andi.w    #7,d0
    add.w    d0,d0
    add.w    d0,d0
    add.w    d0,d0
    move.w    d0,d1
    add.w    d0,d0
    add.w    d1,d0
    andi.w    #$38,d2
    lsr.w    #2,d2
    add.w    d2,d0
    lea    word_3FD9C(pc,d0.w),a4
    moveq    #5,d5 ;5
    move.w    #-$6000,d4
    jmp    loc_3FD7C

Dynamic_HTZ_2P:
    lea    word_3FD9C(pc,d0.w),a4
    moveq    #5,d5 ;5
    move.w    #$E800,d4

loc_3FD7C:
    moveq    #-1,d1
    move.w    (a4)+,d1
    andi.l    #$FFFFFF,d1 ;$FFFFFF
    move.w    d4,d2
    moveq    #$40,d3
    jsr    (QueueDMATransfer).l
    addi.w    #$80,d4
    dbf    d5,loc_3FD7C

Do the same for the cloud patterns below it:

loc_3FE5C:
    lea    ($FFFFA800).w,a1
    tst.w    (Two_player_mode).w
    bne.w    Dynamic_HTZ_2P_2
    move.w    (Camera_X_pos).w,d2
    neg.w    d2
    asr.w    #3,d2
Dynamic_HTZ_2P_2:
    move.l    a2,-(sp)
    lea    (ArtUnc_HTZClouds).l,a0
    lea    ($FFFF7C00).l,a2
    moveq    #$F,d1 ;$F
    jsr    Adjust2PArtPointer

loc_3FE78:
    move.w    (a1)+,d0
    neg.w    d0
    add.w    d2,d0
    andi.w    #$1F,d0 ;1F
    lsr.w    #1,d0
    bcc.s    loc_3FE8A
    addi.w    #$200,d0

loc_3FE8A:
    lea    (a0,d0.w),a4
    lsr.w    #1,d0
    bcs.s    loc_3FEB4
    move.l    (a4)+,(a2)+
    adda.w    #$3C,a2
    move.l    (a4)+,(a2)+
    adda.w    #$3C,a2
    move.l    (a4)+,(a2)+
    adda.w    #$3C,a2
    move.l    (a4)+,(a2)+
    suba.w    #$C0,a2 ;$C0
    adda.w    #$20,a0
    dbf    d1,loc_3FE78
    bra.s    loc_3FEEC
; ===========================================================================

loc_3FEB4:
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    adda.w    #$3C,a2
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    adda.w    #$3C,a2
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    adda.w    #$3C,a2
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    move.b    (a4)+,(a2)+
    suba.w    #$C0,a2
    adda.w    #$20,a0
    dbf    d1,loc_3FE78

loc_3FEEC:
    tst.w    (Two_player_mode).w
    bne.w    loc_3FEEC_2P
    move.l    #$FF7C00,d1
    move.w    #-$5D00,d2
    move.w    #$80,d3
    jsr    (QueueDMATransfer).l
    movea.l    (sp)+,a2
    addq.w    #2,a3
    bra.w    loc_3FF30

loc_3FEEC_2P:
    move.l    #$FF7C00,d1
    move.w    #$EB00,d2
    move.w    #$80,d3
    jsr    (QueueDMATransfer).l
    movea.l    (sp)+,a2
    addq.w    #2,a3
    bra.w    loc_3FF30

Now here's the more difficult bit. We're gonna have to manually edit the two tile chunks using the dynamic path. Find them and replace them with some of the blank blocks in HTZ's file.

Tile edit 02.png

Now edit the blocks to display tiles 740 to 75F according to this pattern:

Tiles edit 01 copy.png

Remember the first and second columns use palette row 2 and the bottom one uses 3.

Or if you wanna go the lazy route (can hardly blame ya if you do), just put these files (info) (14 kB) in the two needed folders.


Step 6: Making separate 1p and 2p tile mappings:

Well that's the 2P background sorted, but we have an issue. Now one player mode's background is botched. We need separate mappings and redirects for each mode.

A very special thanx to user:Devon for providing this fix. An expansion on separate data entries per act and mode made by them can be read
Sonic Retro
here
.

First, rename the HTZ mapping files in both folder like you did the object files and then add back in the original unedited ones into their respective folders.

Now add your newly renamed 2P mapping folders into the directory:

;----------------------------------------------------------------------------------
; EHZ 16x16 block mappings (Kosinski compression) ; was: (Kozinski compression)
BM16_EHZ:    BINCLUDE    "mappings/16x16/EHZ.bin"
;-----------------------------------------------------------------------------------
; EHZ/HTZ main level patterns (Kosinski compression)
; ArtKoz_95C24:
ArtKos_EHZ:    BINCLUDE    "art/kosinski/EHZ_HTZ.bin"
;-----------------------------------------------------------------------------------
; HTZ 16x16 block mappings (Kosinski compression)
BM16_HTZ:    BINCLUDE    "mappings/16x16/HTZ.bin"
;-----------------------------------------------------------------------------------
; HTZ 16x16 block mappings (Kosinski compression)
BM16_HTZ_2P:    BINCLUDE    "mappings/16x16/HTZ_2P.bin"
;-----------------------------------------------------------------------------------
; HTZ pattern suppliment to EHZ level patterns (Kosinski compression)
; ArtKoz_98AB4:
ArtKos_HTZ:    BINCLUDE    "art/kosinski/HTZ_Supp.bin"
;-----------------------------------------------------------------------------------
; EHZ/HTZ 128x128 block mappings (Kosinski compression)
BM128_EHZ:    BINCLUDE    "mappings/128x128/EHZ_HTZ.bin"
;-----------------------------------------------------------------------------------
; EHZ/HTZ 128x128 block mappings (Kosinski compression)
BM128_EHZ_2P:    BINCLUDE    "mappings/128x128/EHZ_HTZ_2P.bin"
;-----------------------------------------------------------------------------------
; MTZ 16x16 block mappings (Kosinski compression)
BM16_MTZ:    BINCLUDE    "mappings/16x16/MTZ.bin"
;-----------------------------------------------------------------------------------

Next we're going to edit the patch for the 16x16 maps already existing in the asm inloadZoneBlockMaps:

; Loads block and bigblock mappings for the current Zone.

loadZoneBlockMaps:
    moveq    #0,d0
    move.b    (Current_Zone).w,d0
    add.w    d0,d0
    add.w    d0,d0
    move.w    d0,d1
    add.w    d0,d0
    add.w    d1,d0
    lea    (LevelArtPointers).l,a2
    lea    (a2,d0.w),a2
    move.l    a2,-(sp)
    addq.w    #4,a2
    move.l    (a2)+,d0
    andi.l    #$FFFFFF,d0
    movea.l    d0,a0
    lea    (Block_Table).w,a1
    bsr.w    JmpTo_KosDec    ; load block maps
    cmpi.b    #7,(Current_Zone).w
    bne.s    +
    tst.w    (Two_player_mode).w
    beq.s    Not_2P_BM16
    lea    ($FFFF9980).w,a1
    lea    (BM16_HTZ_2P).l,a0
    bsr.w    JmpTo_KosDec    ; patch for Hill Top Zone block map
    jmp    +
Not_2P_BM16:
    lea    ($FFFF9980).w,a1
    lea    (BM16_HTZ).l,a0
    bsr.w    JmpTo_KosDec    ; patch for Hill Top Zone block map
+
    tst.w    (Two_player_mode).w
    beq.s    loc_E430
    lea    (Block_Table).w,a1
    move.w    #$BFF,d2

Now we're gonna go to LevelArtPointers. What we're going to do is duplicate every level so that the rest of the asm will recognise 2 player counterparts as separate (this will handy if you want to edit any other two player levels). Edit your new 2 player directory for Hill Top to have the new 2P mappings like so:

; BEGIN SArt_Ptrs Art_Ptrs_Array[17]
; dword_42594: MainLoadBlocks: saArtPtrs:
LevelArtPointers:
    levartptrs   4,  5,  4, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   0 ; EHZ  ; EMERALD HILL ZONE
    levartptrs   4,  5,  4, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   0 ; EHZ  ; EMERALD HILL ZONE (2 PLAYER)
    levartptrs   6,  7,  5, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   1 ; LEV1 ; LEVEL 1 (UNUSED)
    levartptrs   6,  7,  5, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   1 ; LEV1 ; LEVEL 1 (UNUSED) (2 PLAYER)
    levartptrs   8,  9,  6, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   2 ; LEV2 ; LEVEL 2 (UNUSED)
    levartptrs   8,  9,  6, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   2 ; LEV2 ; LEVEL 2 (UNUSED) (2 PLAYER)
    levartptrs  $A, $B,  7, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   3 ; LEV3 ; LEVEL 3 (UNUSED)
    levartptrs  $A, $B,  7, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   3 ; LEV3 ; LEVEL 3 (UNUSED) (2 PLAYER)
    levartptrs  $C, $D,  8, ArtKos_MTZ, BM16_MTZ, BM128_MTZ ;   4 ; MTZ  ; METROPOLIS ZONE ACTS 1 & 2
    levartptrs  $C, $D,  8, ArtKos_MTZ, BM16_MTZ, BM128_MTZ ;   4 ; MTZ  ; METROPOLIS ZONE ACTS 1 & 2 (2 PLAYER)
    levartptrs  $C, $D,  8, ArtKos_MTZ, BM16_MTZ, BM128_MTZ ;   5 ; MTZ3 ; METROPOLIS ZONE ACT 3
    levartptrs  $C, $D,  8, ArtKos_MTZ, BM16_MTZ, BM128_MTZ ;   5 ; MTZ3 ; METROPOLIS ZONE ACT 3 (2 PLAYER)
    levartptrs $10,$11, $A, ArtKos_SCZ, BM16_WFZ, BM128_WFZ ;   6 ; WFZ  ; WING FORTRESS ZONE
    levartptrs $10,$11, $A, ArtKos_SCZ, BM16_WFZ, BM128_WFZ ;   6 ; WFZ  ; WING FORTRESS ZONE (2 PLAYER)
    levartptrs $12,$13, $B, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   7 ; HTZ  ; HILL TOP ZONE
    levartptrs $12,$13, $B, ArtKos_EHZ, BM16_EHZ, BM128_EHZ_2P ;   7 ; HTZ  ; HILL TOP ZONE (2 PLAYER)
    levartptrs $14,$15, $C,   BM16_OOZ,   BM16_OOZ,  BM16_OOZ ;   8 ; HPZ  ; HIDDEN PALACE ZONE (UNUSED)
    levartptrs $14,$15, $C,   BM16_OOZ,   BM16_OOZ,  BM16_OOZ ;   8 ; HPZ  ; HIDDEN PALACE ZONE (UNUSED) (2 PLAYER)
    levartptrs $16,$17, $D, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   9 ; LEV9 ; LEVEL 9 (UNUSED)
    levartptrs $16,$17, $D, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   9 ; LEV9 ; LEVEL 9 (UNUSED) (2 PLAYER)
    levartptrs $18,$19, $E, ArtKos_OOZ, BM16_OOZ, BM128_OOZ ;  $A ; OOZ  ; OIL OCEAN ZONE
    levartptrs $18,$19, $E, ArtKos_OOZ, BM16_OOZ, BM128_OOZ ;  $A ; OOZ  ; OIL OCEAN ZONE (2 PLAYER)
    levartptrs $1A,$1B, $F, ArtKos_MCZ, BM16_MCZ, BM128_MCZ ;  $B ; MCZ  ; MYSTIC CAVE ZONE
    levartptrs $1A,$1B, $F, ArtKos_MCZ, BM16_MCZ, BM128_MCZ ;  $B ; MCZ  ; MYSTIC CAVE ZONE (2 PLAYER)
    levartptrs $1C,$1D,$10, ArtKos_CNZ, BM16_CNZ, BM128_CNZ ;  $C ; CNZ  ; CASINO NIGHT ZONE
    levartptrs $1C,$1D,$10, ArtKos_CNZ, BM16_CNZ, BM128_CNZ ;  $C ; CNZ  ; CASINO NIGHT ZONE (2 PLAYER)
    levartptrs $1E,$1F,$11, ArtKos_CPZ, BM16_CPZ, BM128_CPZ ;  $D ; CPZ  ; CHEMICAL PLANT ZONE
    levartptrs $1E,$1F,$11, ArtKos_CPZ, BM16_CPZ, BM128_CPZ ;  $D ; CPZ  ; CHEMICAL PLANT ZONE (2 PLAYER)
    levartptrs $20,$21,$12, ArtKos_CPZ, BM16_CPZ, BM128_CPZ ;  $E ; DEZ  ; DEATH EGG ZONE
    levartptrs $20,$21,$12, ArtKos_CPZ, BM16_CPZ, BM128_CPZ ;  $E ; DEZ  ; DEATH EGG ZONE (2 PLAYER)
    levartptrs $22,$23,$13, ArtKos_ARZ, BM16_ARZ, BM128_ARZ ;  $F ; ARZ  ; AQUATIC RUIN ZONE
    levartptrs $22,$23,$13, ArtKos_ARZ, BM16_ARZ, BM128_ARZ ;  $F ; ARZ  ; AQUATIC RUIN ZONE (2 PLAYER)
    levartptrs $24,$25,$14, ArtKos_SCZ, BM16_WFZ, BM128_WFZ ; $10 ; SCZ  ; SKY CHASE ZONE
    levartptrs $24,$25,$14, ArtKos_SCZ, BM16_WFZ, BM128_WFZ ; $10 ; SCZ  ; SKY CHASE ZONE (2 PLAYER)

Then, replace the levartptrs macro above it with this:

; declare some global variables to be used by the levartptrs macro
cur_zone_id := 0
cur_zone_str := "0"
cur_zone_2p := 0

; macro for declaring a "main level load block" (MLLB)
levartptrs macro plc1,plc2,palette,art,map16x16,map128x128
    dc.l (plc1<<24)|art
    dc.l (plc2<<24)|map16x16
    dc.l (palette<<24)|map128x128
cur_zone_2p := cur_zone_2p+12
    if cur_zone_2p>=24
cur_zone_2p := 0
cur_zone_id := cur_zone_id+1
cur_zone_str := "\{cur_zone_id}"
    endif
    endm

And have this added after the LevelArtPointers table:

if (cur_zone_2p<>0)&&(MOMPASS=1)
    message "Warning: Table LevelArtPointers's last entry does not have a 2P entry"
    endif

Then, go to Level and go to the comment that says "; multiply d0 by 12, the size of a level art load block". Insert this before "lea (LevelArtPointers).l,a2":

    add.w    d0,d0
    tst.w    (Two_player_mode).w
    beq.s    .not_2p_mode
    addi.w    #12,d0

.not_2p_mode:

Then go to both sub_4E98 and loadZoneBlockMaps and add the same code before both instances of "lea (LevelArtPointers).l,a2".

With that, you should now be able to set up level data pointers for 2P mode for any zone.

Step 7: Fixing the Badniks:

All three badniks have some minor bugs you may want to smooth out:

Rexxon

Rexxon has some similar issues as the airlifts, having issues loading in split screen that leads to multiple versions of it activating every time a player crosses its y-position. To fix this activate the Remember State and Long Distance flags in SonLVL.

Also to make Rexxon's projectile render properly in split screen, just add a single blank tile to his art file. Don't worry there's free space in the VRAM so it won't overwrite anything.

Rexon Fix 01.png

Rexxon can still make the level kinda laggy though so be warned.

Sol

Sol only shoots his fireballs at Sonic, which works fine in one player but can be a bit problem for player two. Edit this line in Sol's routine to make him fire at Tails as well:

loc_371FA:
    cmpi.w    #$50,d0
    bcc.s    loc_371DC_Tails
    tst.w    (Debug_placement_mode).w
    bne.s    loc_371DC_Tails
    move.b    #1,anim(a0)

loc_371DC_Tails:
    tst.w    (Two_player_mode).w
    beq.w    loc_3720C
    move.w    (Sidekick+x_pos).w,d0
    sub.w    x_pos(a0),d0
    bcc.s    loc_371E8_Tails
    neg.w    d0

loc_371E8_Tails:
    cmpi.w    #$A0,d0
    bcc.s    loc_3720C
    move.w    (Sidekick+y_pos).w,d0
    sub.w    y_pos(a0),d0
    bcc.s    loc_371FA_Tails
    neg.w    d0

loc_371FA_Tails:
    cmpi.w    #$50,d0
    bcc.s    loc_3720C
    tst.w    (Debug_placement_mode).w
    bne.s    loc_3720C
    move.b    #1,anim(a0)

loc_3720C:
    bsr.w    JmpTo26_ObjectMove
    lea    (off_372D2).l,a1
    bsr.w    JmpTo25_AnimateSprite
    andi.b    #3,mapping_frame(a0)
    bra.w    JmpTo39_MarkObjGone
; ===========================================================================

Spiker

Spiker is also bugged up in split screen, however it has nothing to do with the sprites or mappings, they work perfectly fine. They are just loaded in another section of VRAM split screen occupies (this is also the reason the title card is corrupted slightly).

Spiker's subobj data is ass to work with, I can't redirect the VRAM location for the life of me, meaning his step is the most needlessly convoluted to do.

First take these mappings (info) (108 bytes) that relocate to a compatible part in the VRAM ($EC00) and put them in your mapping folder.

Add a new subobjdata for your new mapping at the end of obj93's routine:

; off_3707C:
Obj92_SubObjData:
    dc.l Obj92_Obj93_MapUnc_37092
    dc.w 0
    dc.w $404
    dc.w $1012
Obj92_2P_SubObjData:
    dc.l Obj92_Obj93_MapUnc_2P
    dc.w 0
    dc.w $404
    dc.w $1012
; animation script
off_37086:
    dc.w byte_3708A-off_37086
    dc.w byte_3708E-off_37086; 1
byte_3708A:
    dc.b   9,  0,  1,$FF
byte_3708E:
    dc.b   9,  2,  3,$FF
    even
; ---------------------------------------------------------------------------
; sprite mappings
; ---------------------------------------------------------------------------
Obj92_Obj93_MapUnc_37092:    BINCLUDE "mappings/sprite/obj93.bin"
; ---------------------------------------------------------------------------
; sprite mappings
; ---------------------------------------------------------------------------
Obj92_Obj93_MapUnc_2P:    BINCLUDE "mappings/sprite/obj93_2P.bin"
; ===========================================================================

Go to SubObjData_Index and add in your new subobj entry (if it's your first edit it will be entry $AE):

SubObjData_Index:
    dc.w Obj8C_SubObjData - SubObjData_Index    ; $0
    dc.w Obj8D_SubObjData - SubObjData_Index    ; $2
    dc.w Obj90_SubObjData - SubObjData_Index    ; $4
    dc.w Obj90_SubObjData2 - SubObjData_Index    ; $6
    dc.w Obj91_SubObjData - SubObjData_Index    ; $8
    dc.w Obj92_SubObjData - SubObjData_Index    ; $A
    dc.w Invalid_SubObjData - SubObjData_Index    ; $C
    dc.w Obj94_SubObjData - SubObjData_Index    ; $E
    dc.w Obj94_SubObjData2 - SubObjData_Index    ; $10
    dc.w Obj99_SubObjData2 - SubObjData_Index    ; $12
    dc.w Obj99_SubObjData - SubObjData_Index    ; $14
    dc.w Obj9A_SubObjData - SubObjData_Index    ; $16
    dc.w Obj9B_SubObjData - SubObjData_Index    ; $18
    dc.w Obj9C_SubObjData - SubObjData_Index    ; $1A
    dc.w Obj9A_SubObjData2 - SubObjData_Index    ; $1C
    dc.w Obj9D_SubObjData - SubObjData_Index    ; $1E
    dc.w Obj9D_SubObjData2 - SubObjData_Index    ; $20
    dc.w Obj9E_SubObjData - SubObjData_Index    ; $22
    dc.w Obj9F_SubObjData - SubObjData_Index    ; $24
    dc.w ObjA0_SubObjData - SubObjData_Index    ; $26
    dc.w ObjA1_SubObjData - SubObjData_Index    ; $28
    dc.w ObjA2_SubObjData - SubObjData_Index    ; $2A
    dc.w ObjA3_SubObjData - SubObjData_Index    ; $2C
    dc.w ObjA4_SubObjData - SubObjData_Index    ; $2E
    dc.w ObjA4_SubObjData2 - SubObjData_Index    ; $30
    dc.w ObjA5_SubObjData - SubObjData_Index    ; $32
    dc.w ObjA6_SubObjData - SubObjData_Index    ; $34
    dc.w ObjA7_SubObjData - SubObjData_Index    ; $36
    dc.w ObjA7_SubObjData2 - SubObjData_Index    ; $38
    dc.w ObjA8_SubObjData - SubObjData_Index    ; $3A
    dc.w ObjA8_SubObjData2 - SubObjData_Index    ; $3C
    dc.w ObjA7_SubObjData3 - SubObjData_Index    ; $3E
    dc.w ObjAC_SubObjData - SubObjData_Index    ; $40
    dc.w ObjAD_SubObjData - SubObjData_Index    ; $42
    dc.w ObjAD_SubObjData2 - SubObjData_Index    ; $44
    dc.w ObjAD_SubObjData3 - SubObjData_Index    ; $46
    dc.w ObjAF_SubObjData2 - SubObjData_Index    ; $48
    dc.w ObjAF_SubObjData - SubObjData_Index    ; $4A
    dc.w ObjB0_SubObjData - SubObjData_Index    ; $4C
    dc.w ObjB1_SubObjData - SubObjData_Index    ; $4E
    dc.w ObjB2_SubObjData - SubObjData_Index    ; $50
    dc.w ObjB2_SubObjData - SubObjData_Index    ; $52
    dc.w ObjB2_SubObjData - SubObjData_Index    ; $54
    dc.w ObjBC_SubObjData2 - SubObjData_Index    ; $56
    dc.w ObjBC_SubObjData2 - SubObjData_Index    ; $58
    dc.w ObjB3_SubObjData - SubObjData_Index    ; $5A
    dc.w ObjB2_SubObjData2 - SubObjData_Index    ; $5C
    dc.w ObjB3_SubObjData - SubObjData_Index    ; $5E
    dc.w ObjB3_SubObjData - SubObjData_Index    ; $60
    dc.w ObjB3_SubObjData - SubObjData_Index    ; $62
    dc.w ObjB4_SubObjData - SubObjData_Index    ; $64
    dc.w ObjB5_SubObjData - SubObjData_Index    ; $66
    dc.w ObjB5_SubObjData - SubObjData_Index    ; $68
    dc.w ObjB6_SubObjData - SubObjData_Index    ; $6A
    dc.w ObjB6_SubObjData - SubObjData_Index    ; $6C
    dc.w ObjB6_SubObjData - SubObjData_Index    ; $6E
    dc.w ObjB6_SubObjData - SubObjData_Index    ; $70
    dc.w ObjB7_SubObjData - SubObjData_Index    ; $72
    dc.w ObjB8_SubObjData - SubObjData_Index    ; $74
    dc.w ObjB9_SubObjData - SubObjData_Index    ; $76
    dc.w ObjBA_SubObjData - SubObjData_Index    ; $78
    dc.w ObjBB_SubObjData - SubObjData_Index    ; $7A
    dc.w ObjBC_SubObjData2 - SubObjData_Index    ; $7C
    dc.w ObjBD_SubObjData - SubObjData_Index    ; $7E
    dc.w ObjBD_SubObjData - SubObjData_Index    ; $80
    dc.w ObjBE_SubObjData - SubObjData_Index    ; $82
    dc.w ObjBE_SubObjData2 - SubObjData_Index    ; $84
    dc.w ObjC0_SubObjData - SubObjData_Index    ; $86
    dc.w ObjC1_SubObjData - SubObjData_Index    ; $88
    dc.w ObjC2_SubObjData - SubObjData_Index    ; $8A
    dc.w Invalid_SubObjData2 - SubObjData_Index    ; $8C
    dc.w ObjB8_SubObjData2 - SubObjData_Index    ; $8E
    dc.w ObjC3_SubObjData - SubObjData_Index    ; $90
    dc.w ObjC5_SubObjData - SubObjData_Index    ; $92
    dc.w ObjC5_SubObjData2 - SubObjData_Index    ; $94
    dc.w ObjC5_SubObjData3 - SubObjData_Index    ; $96
    dc.w ObjC5_SubObjData3 - SubObjData_Index    ; $98
    dc.w ObjC5_SubObjData3 - SubObjData_Index    ; $9A
    dc.w ObjC5_SubObjData3 - SubObjData_Index    ; $9C
    dc.w ObjC5_SubObjData3 - SubObjData_Index    ; $9E
    dc.w ObjC6_SubObjData2 - SubObjData_Index    ; $A0
    dc.w ObjC5_SubObjData4 - SubObjData_Index    ; $A2
    dc.w ObjAF_SubObjData3 - SubObjData_Index    ; $A4
    dc.w ObjC6_SubObjData3 - SubObjData_Index    ; $A6
    dc.w ObjC6_SubObjData4 - SubObjData_Index    ; $A8
    dc.w ObjC6_SubObjData - SubObjData_Index    ; $AA
    dc.w ObjC8_SubObjData - SubObjData_Index    ; $AC
    dc.w Obj92_2P_SubObjData - SubObjData_Index    ; $AE
; ===========================================================================

Go to the start of Spiker (obj92)'s routine and edit it to recognise the new subtype in 2 player mode:

loc_36F24:
    tst.w    (Two_player_mode).w
    beq.w    loc_36F24_1P
    move.b    #$AE,subtype(a0) ; <== Obj93_SubObjData2
loc_36F24_1P:
    bsr.w    LoadSubObject
    bsr.w    JmpTo64_Adjust2PArtPointer
    move.b    #$40,objoff_2A(a0)
    move.w    #$80,x_vel(a0)
    bchg    #0,status(a0)
    rts

Add an extra PLC cue at the end of the list (underneath PLC_3A) like so:

;---------------------------------------------------------------------------------------
; Pattern load queue
; Tails end of level results screen
;---------------------------------------------------------------------------------------
PLC_3A: plrlistheader
    plreq $B000, ArtNem_TitleCard
    plreq $B600, ArtNem_ResultsText
    plreq $BE80, ArtNem_MiniTails
    plreq $A800, ArtNem_Perfect
PLC_3A_End
;---------------------------------------------------------------------------------------
; PATTERN LOAD REQUEST LIST
; Hill Top Zone 2P primary
;---------------------------------------------------------------------------------------
PlrList_Htz2P: plrlistheader
    plreq $73C0, ArtNem_Buzzer_Fireball
    plreq $7640, ArtNem_HtzRock
    plreq $78C0, ArtNem_HtzSeeSaw
    plreq $7BC0, ArtNem_Sol
    plreq $6FC0, ArtNem_Rexon
    plreq $EC00, ArtNem_Spiker
    plreq $8680, ArtNem_Spikes
    plreq $8780, ArtNem_DignlSprng
    plreq $8B80, ArtNem_VrtclSprng
    plreq $8E00, ArtNem_HrzntlSprng
PlrList_Htz2P_End

List it accordingly at the end of the ArtLoadCues (if it is the first new cue you have made it should be $43):

.....
    dc.w PLC_37 - ArtLoadCues    ; 63
    dc.w PLC_38 - ArtLoadCues    ; 64
    dc.w PLC_39 - ArtLoadCues    ; 65
    dc.w PLC_3A - ArtLoadCues    ; 66
    dc.w PlrList_Htz2P - ArtLoadCues    ; 67 $43

Now edit your new 2 player LevelArtPointers to read for your new PLC list:

.....
    levartptrs $12,$13, $B, ArtKos_EHZ, BM16_EHZ, BM128_EHZ ;   7 ; HTZ  ; HILL TOP ZONE
    levartptrs $43,$13, $B, ArtKos_EHZ, BM16_EHZ, BM128_EHZ_2P ;   7 ; HTZ  ; HILL TOP ZONE (2 PLAYER)

I don't think this was worth the hassle at all just to fix one badnik (hackers that know good VRAM tricks can likely can find a much easier way) though I suppose this trick can also come in handy if you wanna fiddle around with more things between the 1 player and 2 player art files.

Step 8: Just For Fun:

If you want to add your own unique track for 2P Hill Top for that extra bit of authenticity, just go to 2P Music Playlist.

This list solely edits what music tracks play for split screen conviniently enough so you can give Hill Top whatever theme you want without changing single player's.

;----------------------------------------------------------------------------
; 2P Music Playlist
;----------------------------------------------------------------------------
; byte_3EB2:
MusicList2:
    dc.b  $C+$80    ; 0  ; EHZ 2P
    dc.b   2+$80    ; 1
    dc.b   5+$80    ; 2
    dc.b   4+$80    ; 3
    dc.b   5+$80    ; 4
    dc.b   5+$80    ; 5
    dc.b  $F+$80    ; 6
    dc.b   6+$80    ; 7    ;HTZ 2P
    dc.b $10+$80    ; 8
    dc.b  $D+$80    ; 9
    dc.b   4+$80    ; 10
    dc.b   3+$80    ; 11 ; MCZ 2P
    dc.b   8+$80    ; 12 ; CNZ 2P
    dc.b  $E+$80    ; 13
    dc.b  $A+$80    ; 14
    dc.b   7+$80    ; 15
    dc.b  $D+$80    ; 16
    dc.b   0    ; 17
; ===========================================================================

And then you have it, a (mostly) perfectly functioning Hill Top Zone in Two Player Mode. Enjoy racing in one more level.

SCHG How-To Guide: Sonic the Hedgehog 2 (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix Super Sonic Bugs | Use Correct Height When Roll Jumping | Fix Jump Height Bug When Exiting Water | Fix Screen Boundary Spin Dash Bug | Correct Drowning Bugs | Fix Camera Y Position for Tails | Fix Tails Subanimation Error | Fix Tails' Respawn Speeds | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix Rexon Crash | Fix Monitor Collision Bug | Fix EHZ Deformation Bug | Correct CPZ Boss Attack Behavior | Fix Bug in ARZ Boss Arrow's Platform Behavior | Fix ARZ Boss Walking on Air Glitch | Fix ARZ Boss Sprite Behavior | Fix Multiple CNZ Boss Bugs | Fix HTZ Background Scrolling Mountains | Fix OOZ Launcher Speed Up Glitch | Fix DEZ Giant Mech Collision Glitch | Fix Boss Deconstruction Behavior | Fix Speed Bugs | Fix 14 Continues Cheat | Fix Debug Mode Crash | Fix 99+ Lives | Fix Sonic 2's Sega Screen
Design Choices
Remove the Air Speed Cap | Disable Floor Collision While Dying | Modify Super Sonic Transformation Methods & Behavior | Enable/Disable Tails in Certain Levels | Collide with Water After Being Hurt | Retain Rings When Returning at a Star Post | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Insert LZ Water Ripple Effect | Restore Lost CPZ Boss Feature | Prevent SCZ Tornado Spin Dash Death | Improve ObjectMove Subroutines | Port S3K Rings Manager | Port S3K Object Manager | Port S3K Priority Manager | Edit Level Order with ASM‎ | Alter Ring Requirements in Special Stages | Make Special Stage Characters Use Normal DPLCs | Speed Up Ring Loss Process | Change spike behaviour in Sonic 2
Adding Features
Create Insta-kill and High Jump Monitors | Create Clone and Special Stage Monitors | Port Knuckles
Sound Features
Expand Music Index to Start at $00 | Port Sonic 1 Sound Driver | Port Sonic 2 Clone Driver | Port Sonic 3 Sound Driver | Port Flamewing's Sonic 3 & Knuckles Sound Driver | Expand the Music Index to Start at $00 (Sonic 2 Clone Driver Version) | Play Different Songs Per Act
Extending the Game
Extend the Level Index Past $10 | Extend the Level Select | Extend Water Tables | Add Extra Characters | Free Up 2 Universal SSTs


|Add Hill Top Zone to 2 Player VS Mode]]