Add a new zone in Sonic 1
From Sonic Retro
(Original guide by FraGag)
In Sonic 1, there are technically 7 zones (not counting the special stage):
- 00: Green Hill Zone
- 01: Labyrinth Zone
- 02: Marble Zone
- 03: Star Light Zone
- 04: Spring Yard Zone
- 05: Scrap Brain Zone
- 06: Ending sequence
This guide will help you to add a new zone (which I'll name Alpha Beta Zone (ABZ) in this guide) as zone 07 using Hivebrain's Sonic 1 disassembly. Remember to change ABZ to the name of your zone(s) in the given examples. Additional zones could be added by following these steps again. Some steps only need to be done once to support up to 256 zones; those steps are clearly identified as such.
Note that it is important to follow all these steps before attempting to try running the game, unless you entirely removed the feature related to the step you're about to follow. Otherwise, the game could try to load the wrong data or run invalid code.
Contents
- 1 Palette cycling routine
- 2 Collision pointers
- 3 Level size array
- 4 Start location array
- 5 Unknown data related to the level size
- 6 Background scrolling speed
- 7 Background layer deformation
- 8 Dynamic screen resizing (and other level events)
- 9 Zone title card
- 10 Level order
- 11 Music playlists
- 12 Animated level graphics
- 13 Object debug list
- 14 Pattern load cues (zone and animals)
- 15 Animal Subtypes and Mappings
- 16 Zone palette
- 17 Main level load blocks
- 18 Level layout index
- 19 Objects positions index
- 20 Build errors
- 21 Conclusion
Palette cycling routine
The PalCycle_Load routine is used to dynamically replace some colors in the palette at predefined intervals. The routine uses an offset table to run the code corresponding to the zone the player is currently playing. You must add a routine (although it can be empty) for each level you add to the game, otherwise strange things will happen. Here is what PalCycle should look like after adding one zone:
PalCycle: dc.w PalCycle_GHZ-PalCycle
dc.w PalCycle_LZ-PalCycle
dc.w PalCycle_MZ-PalCycle
dc.w PalCycle_SLZ-PalCycle
dc.w PalCycle_SYZ-PalCycle
dc.w PalCycle_SBZ-PalCycle
dc.w PalCycle_GHZ-PalCycle
dc.w PalCycle_ABZ-PalCycle
You will probably want to replace ABZ with what corresponds to the name of your new zone.
Once you've added the zone to the offset table, we must define the routine. If you want no cycling palette, you can use this routine:
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PalCycle_ABZ: ; XREF: PalCycle
rts
; End of function PalCycle_ABZ
Put this code close to the offset table, either right after the table, or after the end of PalCycle_SBZ (just before Pal_TitleCyc).
Collision pointers
The new zone needs collision data. You may already have this data if you used SonED2. To define collision for your new zone, open up _inc\Collision index pointers.asm and add a line for your zone. If this is the first zone you add, you will also need to define collision for the ending sequence "zone", as the collision is usually loaded elsewhere for the ending sequence. For the ending sequence, the collision data from Green Hill Zone will do. If you used collision data from another zone for your own, you can point to that zone's collision data directly; otherwise, you must point to your own data and incbin that data.
Here is what _inc\Collision index pointers.asm should look like with one additional zone, assuming the zone uses independent collision data:
; ---------------------------------------------------------------------------
; Collision index pointers
; ---------------------------------------------------------------------------
dc.l Col_GHZ
dc.l Col_LZ
dc.l Col_MZ
dc.l Col_SLZ
dc.l Col_SYZ
dc.l Col_SBZ
dc.l Col_GHZ ; Ending sequence
dc.l Col_ABZ
If you used independant collision data, you must also include it in your ROM. If want to keep your disassembly organized, you would include it where the rest of the collision data is defined. Find Col_SBZ, and after the even under it, add something like the following:
Col_ABZ: incbin collide\abz.bin ; ABZ index
even
Level size array
The level size array is used to set the level boundaries and to limit the camera movement when initially loading a level. The data is located in misc\lvl_size.bin. Each act has 12 ($C) bytes and since the game reserves 4 acts for each zone for simplicity's sake, you will need to add 48 ($30) bytes for a zone. Look at the values used for the other zones to determine what works, and make sure to test it in the game.
Start location array
This array is used to position Sonic when starting a level, or restarting a level when a lamppost has not been hit yet. The data is located in misc\sloc_lev.bin. Each act has 4 bytes: 2 bytes for the X position and 2 bytes for the Y position.
There is some unknown data that is placed in RAM, which seems to be mostly unused. For the sake of completeness, we're going to add data for each new zone. The data is located at dword_61B4. By looking at the data itself, we can see that every zone has the same data, except for Green Hill Zone and the ending (which uses GHZ art). Let's use the one that appears the most often:
dword_61B4: dc.l $700100, $1000100
dc.l $8000100, $1000000
dc.l $8000100, $1000000
dc.l $8000100, $1000000
dc.l $8000100, $1000000
dc.l $8000100, $1000000
dc.l $700100, $1000100
dc.l $8000100, $1000000
Background scrolling speed
This subroutine is used to set the scroll speed of some backgrounds. If your background isn't too fancy, you can just use an empty routine. First, edit the offset table like this:
BgScroll_Index: dc.w BgScroll_GHZ-BgScroll_Index, BgScroll_LZ-BgScroll_Index
dc.w BgScroll_MZ-BgScroll_Index, BgScroll_SLZ-BgScroll_Index
dc.w BgScroll_SYZ-BgScroll_Index, BgScroll_SBZ-BgScroll_Index
dc.w BgScroll_End-BgScroll_Index, BgScroll_ABZ-BgScroll_Index
Then, add an empty routine after the routine for the ending.
; ===========================================================================
BgScroll_ABZ: ; XREF: BgScroll_Index
rts
Background layer deformation
When moving in the level, the background lines sometimes scroll at different speeds. This is controlled by the background layer deformation routines. If you just want a plain scrolling background, you can use the routine for Labyrinth Zone. Otherwise, you'll have to program your own routine.
Here's what Deform_Index should look like:
Deform_Index: dc.w Deform_GHZ-Deform_Index, Deform_LZ-Deform_Index
dc.w Deform_MZ-Deform_Index, Deform_SLZ-Deform_Index
dc.w Deform_SYZ-Deform_Index, Deform_SBZ-Deform_Index
dc.w Deform_GHZ-Deform_Index, Deform_ABZ-Deform_Index
Dynamic screen resizing (and other level events)
This set of routines controls the camera and level boundaries when moving around in the level. They also create the boss objects in act 3. Add an entry for your new zone:
Resize_Index: dc.w Resize_GHZ-Resize_Index, Resize_LZ-Resize_Index
dc.w Resize_MZ-Resize_Index, Resize_SLZ-Resize_Index
dc.w Resize_SYZ-Resize_Index, Resize_SBZ-Resize_Index
dc.w Resize_Ending-Resize_Index, Resize_ABZ-Resize_Index
Then add the routine itself. If you have absolutely no need for dynamic screen resizing, you can simply create an empty routine:
; ---------------------------------------------------------------------------
; Alpha Beta Zone sequence dynamic screen resizing (empty)
; ---------------------------------------------------------------------------
Resize_ABZ: ; XREF: Resize_Index
rts
; ===========================================================================
If you need dynamic screen resizing (or think you might eventually), you can take this as a base to have separate code for each act:
; ---------------------------------------------------------------------------
; Alpha Beta Zone sequence dynamic screen resizing (empty)
; ---------------------------------------------------------------------------
Resize_ABZ: ; XREF: Resize_Index
moveq #0,d0
move.b ($FFFFFE11).w,d0
add.w d0,d0
move.w Resize_ABZx(pc,d0.w),d0
jmp Resize_ABZx(pc,d0.w)
; ===========================================================================
Resize_ABZx: dc.w Resize_ABZ1-Resize_ABZx
dc.w Resize_ABZ2-Resize_ABZx
dc.w Resize_ABZ3-Resize_ABZx
; ===========================================================================
Resize_ABZ1:
rts
; ===========================================================================
Resize_ABZ2:
rts
; ===========================================================================
Resize_ABZ3:
rts
; ===========================================================================
Zone title card
First-time fix
You probably want to give your zone a cool name too, huh? Before you add new zones, you'll need to edit the title card object a little, because it's made to support only 6 zones. This only needs to be done once, and will support any number of zones (up to 256).
First, go to Obj34_CheckFZ and replace this:
Obj34_CheckFZ:
move.w d0,d2
cmpi.w #$502,($FFFFFE10).w ; check if level is FZ
bne.s Obj34_LoadConfig
moveq #6,d0 ; load title card number 6 (FZ)
moveq #$B,d2 ; use "FINAL" mappings
with this:
Obj34_CheckFZ:
move.w d0,d2
cmpi.w #$502,($FFFFFE10).w ; check if level is FZ
bne.s Obj34_CheckNew
moveq #6,d0 ; load title card number 6 (FZ)
moveq #$B,d2 ; use "FINAL" mappings
Obj34_CheckNew:
cmpi.b #7,($FFFFFE10).w ; check if level is in the new zones
blo.s Obj34_LoadConfig
addq.b #$C-7,d2 ; use correct mappings
Zone-specific data
When that's done, you need to add the actual data for your new zone. This section might be useful to edit the title card: SCHG:Sonic_the_Hedgehog/Text Editing#Level Title Cards. You should put the data for your new zone at the end of the mappings, i.e. after byte_CB8A, just before the even. You'll also need to add a pointer to your new mapping at Map_obj34:
Map_obj34: dc.w byte_C9FE-Map_obj34
dc.w byte_CA2C-Map_obj34
dc.w byte_CA5A-Map_obj34
dc.w byte_CA7A-Map_obj34
dc.w byte_CAA8-Map_obj34
dc.w byte_CADC-Map_obj34
dc.w byte_CB10-Map_obj34
dc.w byte_CB26-Map_obj34
dc.w byte_CB31-Map_obj34
dc.w byte_CB3C-Map_obj34
dc.w byte_CB47-Map_obj34
dc.w byte_CB8A-Map_obj34
dc.w TitleCard_ABZ-Map_obj34
This is what the mapping for Alpha Beta Zone looks like:
TitleCard_ABZ: dc.b 9
dc.b $F8, 5, 0, 0, $B0 ; A
dc.b $F8, 5, 0, $26, $C0 ; L
dc.b $F8, 5, 0, $36, $D0 ; P
dc.b $F8, 5, 0, $1C, $E0 ; H
dc.b $F8, 5, 0, 0, $F0 ; A
dc.b $F8, 5, 0, 4, $10 ; B
dc.b $F8, 5, 0, $10, $20 ; E
dc.b $F8, 5, 0, $42, $30 ; T
dc.b $F8, 5, 0, 0, $40 ; A
You will also need to position the parts of the title card manually in Obj34_ConData. Add a line like this at the end of the table
dc.w 0, $120, $FF04, $144, $41C, $15C, $21C, $15C ; ABZ
Level order
The level order tells which level to load when an act has been completed. The data is stored in misc\lvl_ord.bin. Each act takes 2 bytes (the zone and the act), so each zone takes 8 bytes. Again, there is no data for the ending zone, so add 8 "00" bytes at the end of the file, then add the zone and act the game should bring the player to when the level is complete.
Don't understand how it works? Let's take an example: let's say you just completed Marble Zone act 2. The game needs to know what level it will load next. Since Marble Zone is zone 02, skip 2 * 8 bytes, i.e. 16 or $10 bytes. Then, skip 2 more bytes for act 1. At 00000012, there's "02 02", which means that zone 02 (Marble Zone) act 02 (act 3 — remember that the zone and act numbers are zero-based) will be loaded.
If you want to incorporate your new zone in the level flow, you'll need to edit at least one of the values to point to your new zone, then at the end of your zone, point back to one of the existing zones (or yet another new zone).
Music playlists
First-time fix (optional)
Sonic 1 has 2 music playlists: one to play music when loading the level and one to resume it when invincibility wears off. To simplify things, you could simply use the same playlist; otherwise, you'll have to keep them synchronized. To do that, remove those lines:
; ---------------------------------------------------------------------------
; Music to play after invincibility wears off
; ---------------------------------------------------------------------------
MusicList2: incbin misc\muslist2.bin
even
Then, replace MusicList2 with MusicList. You can now delete muslist2.bin, but you may also want to keep it in case something went wrong.
Zone-specific data
Now, edit muslist1.bin (and muslist2.bin if you're keeping it). Each zone takes a byte and tells which music track to play. This file already has 00 for zone 07; you should replace it with the actual track you want to play. If you have more than 8 zones, just add a byte. The even in the main ASM file will ensure that everything builds correctly. If you use both playlists, they should have the same size.
Animated level graphics
Some zones have animated art in the foreground or in the background. This is handled by AniArt_Load. The zones that don't have any animated art use AniArt_none, and to keep things simple, this is what I will do for ABZ:
AniArt_Index: dc.w AniArt_GHZ-AniArt_Index, AniArt_none-AniArt_Index
dc.w AniArt_MZ-AniArt_Index, AniArt_none-AniArt_Index
dc.w AniArt_none-AniArt_Index, AniArt_SBZ-AniArt_Index
dc.w AniArt_Ending-AniArt_Index, AniArt_none-AniArt_Index
Object debug list
If you didn't remove debug mode from your hack, you must add an object debug list to your zone. First, add a pointer to the new list for your zone in _inc\Debug list pointers.asm:
dc.w Debug_GHZ-DebugList
dc.w Debug_LZ-DebugList
dc.w Debug_MZ-DebugList
dc.w Debug_SLZ-DebugList
dc.w Debug_SYZ-DebugList
dc.w Debug_SBZ-DebugList
dc.w Debug_Ending-DebugList
dc.w Debug_ABZ-DebugList
Then, add the new list to the main ASM file:
; ---------------------------------------------------------------------------
; Debug list - Alpha Beta
; ---------------------------------------------------------------------------
Debug_ABZ:
include "_inc\Debug list - ABZ.asm"
Finally, create a file with the actual list:
; ---------------------------------------------------------------------------
; Debug list - Alpha Beta
; ---------------------------------------------------------------------------
dc.w 3
dc.l Map_obj25+$25000000
dc.b 0, 0, $27, $B2
dc.l Map_obj26+$26000000
dc.b 0, 0, 6, $80
dc.l Map_obj79+$79000000
dc.b 1, 0, 7, $A0
even
Pattern load cues (zone and animals)
Zone-specific data
The pattern load cues are used to load art to be displayed on the screen. The way the PLCs are arranged in Sonic 1, there will be some problems with the animal PLCs, but the zone PLCs are retrieved from the main level load blocks, so no special treatment is required for those.
First, open _inc\Pattern load cues.asm and add the PLCs for your new zone at the end of the table at the beginning:
dc.w PLC_ABZ-ArtLoadCues, PLC_ABZ2-ArtLoadCues, PLC_ABZAnimals-ArtLoadCues
If you have added other PLCs before this, you may need to adjust some indices in the code below. If you declare your PLCs in another way, the code in this section won't work. The rest of this section only needs to be followed once.
First-time fix
Now, since the animals PLCs are scattered across the table, we are going to use this new subroutine to load the correct PLC. Put it just before LoadPLC:
; ---------------------------------------------------------------------------
; Subroutine to load the art for the animals for the current zone
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
LoadAnimalPLC:
moveq #0,d0
move.b ($FFFFFE10).w,d0
cmpi.w #7,d0
bhs.s LoadAnimalPLC_New
addi.w #$15,d0
bra.s LoadPLC
; ---------------------------------------------------------------------------
LoadAnimalPLC_New:
subi.w #7,d0
; multiply d0 by 3
move.w d0,d1
add.w d0,d0
add.w d1,d0
; add $22 (this is the index of the animal PLC for the first added zone)
addi.w #$22,d0
; bra.s LoadPLC
; End of function LoadAnimalPLC
Now that we have this subroutine, we can replace the two instances in the code that load the animal graphics. Go to Level_ClrCardArt and replace this:
Level_ClrCardArt:
moveq #2,d0
jsr (LoadPLC).l ; load explosion patterns
moveq #0,d0
move.b ($FFFFFE10).w,d0
addi.w #$15,d0
jsr (LoadPLC).l ; load animal patterns (level no. + $15)
with this:
Level_ClrCardArt:
moveq #2,d0
jsr (LoadPLC).l ; load explosion patterns
jsr (LoadAnimalPLC).l ; load animal patterns
Then, go to Obj34_ChangeArt and replace this:
Obj34_ChangeArt: ; XREF: Obj34_ChkPos2
cmpi.b #4,$24(a0)
bne.s Obj34_Delete
moveq #2,d0
jsr (LoadPLC).l ; load explosion patterns
moveq #0,d0
move.b ($FFFFFE10).w,d0
addi.w #$15,d0
jsr (LoadPLC).l ; load animal patterns
with this:
Obj34_ChangeArt: ; XREF: Obj34_ChkPos2
cmpi.b #4,$24(a0)
bne.s Obj34_Delete
moveq #2,d0
jsr (LoadPLC).l ; load explosion patterns
jsr (LoadAnimalPLC).l ; load animal patterns
Animal Subtypes and Mappings
Zone-specific data
The previous section was for loading Animal art for the new zones but that still leaves choosing which animals we want in the zone and loading the correct mappings.
If you check Obj28_FromEnemy you'll see that there is a zone check for Obj28_VarIndex and Obj28_Variables. Scroll up and you'll see:
Obj28_VarIndex: dc.b 0, 5, 2, 3, 6, 3, 4, 5, 4, 1, 0, 1
Obj28_Variables:dc.w $FE00, $FC00
dc.l Map_obj28
dc.w $FE00, $FD00 ; horizontal speed, vertical speed
dc.l Map_obj28a ; mappings address
dc.w $FE80, $FD00
dc.l Map_obj28
dc.w $FEC0, $FE80
dc.l Map_obj28a
dc.w $FE40, $FD00
dc.l Map_obj28b
dc.w $FD00, $FC00
dc.l Map_obj28a
dc.w $FD80, $FC80
dc.l Map_obj28b
Obj28_VarIndex is for choosing which two animals are used for the zone and Obj28_Variables is the speed they move at and their mappings. Without these you'll get incorrect garbage animals and crashes. So we are gonna add new entries like so including one for the Ending for Obj28_VarIndex:
Obj28_VarIndex:
dc.b 0, 5 ; Pocky and Flicky in GHZ
dc.b 2, 3 ; Pecky and Rocky in LZ
dc.b 6, 3 ; Ricky and Rocky in MZ
dc.b 4, 5 ; Picky and Flicky in SLZ
dc.b 4, 1 ; Picky and Cucky in SYZ
dc.b 0, 1 ; Pocky and Cucky in SBZ
dc.b 0, 5 ; Pocky and Flicky in Ending (Unused)
dc.b 0, 5 ; Pocky and Flicky in ABZ
Obj28_Variables:
; horizontal speed, vertical speed
; mappings address
dc.w $FE00, $FC00 ; Pocky and Flicky in GHZ
dc.l Map_obj28
dc.w $FE00, $FD00 ; Pecky and Rocky in LZ
dc.l Map_obj28a
dc.w $FE80, $FD00 ; Ricky and Rocky in MZ
dc.l Map_obj28
dc.w $FEC0, $FE80 ; Picky and Flicky in SLZ
dc.l Map_obj28a
dc.w $FE40, $FD00 ; Picky and Cucky in SYZ
dc.l Map_obj28b
dc.w $FD00, $FC00 ; Pocky and Cucky in SBZ
dc.l Map_obj28a
dc.w $FD80, $FC80 ; Ending (Unused)
dc.l Map_obj28b
dc.w $FE00, $FC00 ; Pocky and Flicky in ABZ
dc.l Map_obj28
Now with that done you'll have no issues with the Animals in your new zone!
Zone palette
Your new level needs a palette, otherwise it will be all black or full of random colors. Open _inc\Pallet pointers.asm and add these lines at the end of the file:
dc.l Pal_ABZ
dc.w $FB20
dc.w $17
Then, add this line after Pal_Ending:
Pal_ABZ: incbin pallet\abz.bin
Main level load blocks
The main level load blocks tell the game what to load for a specific zone. They point to the level patterns, to the 16x16 blocks, to the 256x256 chunks, the 2 pattern load cues used to load the art for the level and the palette used in the level. This information is stored in _inc\Main level load blocks.asm.
By looking at how this data is defined for the existing levels and reading the comments at the beginning of the file, you should be able to figure out how to configure your zone. Remember to take the actual PLC indices from the PLC table (in _inc\Pattern load cues.asm). Here is how ABZ is defined (this goes just before the even directive):
dc.l Nem_ABZ+$20000000
dc.l Blk16_ABZ+$21000000
dc.l Blk256_ABZ
dc.b 0, $8C, $14, $14
You must also incbin the data for the zone. Add these lines before Nem_Eggman:
Blk16_ABZ: incbin map16\abz.bin
even
Nem_ABZ: incbin artnem\8x8abz.bin ; ABZ primary patterns
even
Blk256_ABZ: incbin map256\abz.bin
even
Level layout index
This is probably one of the first things you'd have thought of, yet we're doing it only now! Still, it must be done, so go to Level_Index. This huge array defines the foreground layout, the background layout and another layout that appears to be unused (most of the time, it is empty). For simplicity, I will assume that you have 3 acts, and that all your acts use the same background (look at Scrap Brain Zone for an example of a zone with different backgrounds).
First, add entries at the end of the table for your new zone, like this (byte_6A320 points to an empty layout):
dc.w Level_ABZ1-Level_Index, Level_ABZbg-Level_Index, byte_6A320-Level_Index
dc.w Level_ABZ2-Level_Index, Level_ABZbg-Level_Index, byte_6A320-Level_Index
dc.w Level_ABZ3-Level_Index, Level_ABZbg-Level_Index, byte_6A320-Level_Index
dc.w byte_6A320-Level_Index, byte_6A320-Level_Index, byte_6A320-Level_Index
Then, incbin the layouts. Add this after byte_6A320:
Level_ABZ1: incbin levels\abz1.bin
even
Level_ABZ2: incbin levels\abz2.bin
even
Level_ABZ3: incbin levels\abz3.bin
even
Level_ABZbg: incbin levels\abzbg.bin
even
Objects positions index
First-time fix
Your levels would be boring if they didn't have objects, wouldn't they? However, there's a small problem with how the object layouts are organized in the ROM: the platforms on conveyor belts from Labyrinth Zone and Scrap Brain Zone use data that is put in the same data table as the rest of the object layouts, so we can't add data for new zones without breaking them. Fortunately, it's not too hard to work around this issue by editing the way they are referenced. This only needs to be done once.
First, we will add 2 labels to effectively break the table in 3. We will then use those labels in their respective tables to compute the offsets. The end of the table should look like this:
; ...
dc.w ObjPos_End-ObjPos_Index, ObjPos_Null-ObjPos_Index
dc.w ObjPos_End-ObjPos_Index, ObjPos_Null-ObjPos_Index
dc.w ObjPos_End-ObjPos_Index, ObjPos_Null-ObjPos_Index
dc.w ObjPos_End-ObjPos_Index, ObjPos_Null-ObjPos_Index
ObjPos_LZxpf_Index:
dc.w ObjPos_LZ1pf1-ObjPos_LZxpf_Index, ObjPos_LZ1pf2-ObjPos_LZxpf_Index
dc.w ObjPos_LZ2pf1-ObjPos_LZxpf_Index, ObjPos_LZ2pf2-ObjPos_LZxpf_Index
dc.w ObjPos_LZ3pf1-ObjPos_LZxpf_Index, ObjPos_LZ3pf2-ObjPos_LZxpf_Index
dc.w ObjPos_LZ1pf1-ObjPos_LZxpf_Index, ObjPos_LZ1pf2-ObjPos_LZxpf_Index
ObjPos_SBZ1pf_Index:
dc.w ObjPos_SBZ1pf1-ObjPos_SBZ1pf_Index, ObjPos_SBZ1pf2-ObjPos_SBZ1pf_Index
dc.w ObjPos_SBZ1pf3-ObjPos_SBZ1pf_Index, ObjPos_SBZ1pf4-ObjPos_SBZ1pf_Index
dc.w ObjPos_SBZ1pf5-ObjPos_SBZ1pf_Index, ObjPos_SBZ1pf6-ObjPos_SBZ1pf_Index
dc.w ObjPos_SBZ1pf1-ObjPos_SBZ1pf_Index, ObjPos_SBZ1pf2-ObjPos_SBZ1pf_Index
Then, we will edit the platform objects to reference the data by using these new labels. Go to loc_12460. Replace these two lines:
addi.w #$70,d0
lea (ObjPos_Index).l,a2
with this single line:
lea (ObjPos_LZxpf_Index).l,a2
Now, go to loc_1639A. Similarly, replace these two lines:
addi.w #$80,d0
lea (ObjPos_Index).l,a2
with this line:
lea (ObjPos_SBZ1pf_Index).l,a2
Zone-specific data
Now that this is done, we can add object layouts for new zones right before ObjPos_LZxpf_Index (without making the platforms behave incorrectly), like this:
dc.w ObjPos_ABZ1-ObjPos_Index, ObjPos_Null-ObjPos_Index
dc.w ObjPos_ABZ2-ObjPos_Index, ObjPos_Null-ObjPos_Index
dc.w ObjPos_ABZ3-ObjPos_Index, ObjPos_Null-ObjPos_Index
dc.w ObjPos_ABZ1-ObjPos_Index, ObjPos_Null-ObjPos_Index
Then, incbin the object positions:
ObjPos_ABZ1: incbin objpos\abz1.bin
even
ObjPos_ABZ2: incbin objpos\abz2.bin
even
ObjPos_ABZ3: incbin objpos\abz3.bin
even
Build errors
After applying these steps on a clean Sonic 1 disassembly, I got the following build errors:
- Error : Branch (32784 bytes) is out of range (near Obj89_Move)
- Error : Illegal value (136)
The first error points to this line:
bra.w DisplaySprite
Replace bra.w with jmp to fix this error. If you've further altered your hack, you may encounter similar "out of range" errors. If that happens, replace bra.s with bra.w or bra.w with jmp.
The second errors occurs on this line:
move.l LoopTileNums(pc,d0.w),($FFFFF7AC).w
Since we extended the start location array, the loop tile numbers are a bit too far away. You can fix this quickly by moving these 4 lines just after LevSz_Unk:
moveq #0,d0
move.b ($FFFFFE10).w,d0
lsl.b #2,d0
move.l LoopTileNums(pc,d0.w),($FFFFF7AC).w
Conclusion
If you've applied these steps correctly, you should be able to build your ROM and enjoy your new level. Here's a list of the binary files you need to add to be able to build your ROM:
- pallet\abz.bin
- map16\abz.bin
- artnem\8x8abz.bin
- map256\abz.bin
- collide\abz.bin
- levels\abz1.bin
- levels\abz2.bin
- levels\abz3.bin
- levels\abzbg.bin
- objpos\abz1.bin
- objpos\abz2.bin
- objpos\abz3.bin
Note by Shadow05 If you're adding a 4th act add this.
- levels\abz4.bin
- objpos\abz4.bin
And if you're adding dynamic tiles, collison and palettes add this.
- map16\ab2z.bin
- artnem\8x8abz2.bin
- map256\abz2.bin
- map16\abz3.bin
- artnem\8x8abz3.bin
- map256\abz3.bin
- pallet\abz2.bin
- pallet\abz3.bin
- collide\abz2.bin
- collide\abz3.bin
If you didn't do so yet, you may want to follow Puto's guide to fix the SEGA sound.
|Add a new zone in Sonic 1]]