Picking up from
where we left off, at
$2BD98 we come across the
Obj_Animal object, which as we learned before, is the small animal that spawns both from a defeated enemy
and from an act 2 end capsule. And right at the start of its code, we find this seemingly innocuous byte array:
byte_2BDDA: dc.b 5, 1
dc.b 0, 3
dc.b 5, 1
dc.b 0, 5
dc.b 6, 5
dc.b 2, 3
dc.b 6, 1
dc.b 6, 5
dc.b 6, 5
dc.b 6, 5
dc.b 6, 5
dc.b 6, 5
dc.b 6, 5
This is actually a very dangerous byte array! When the animal object is first initialized, it uses the value of the current zone, plus the value in register
d0 (which is randomly set to either
0 or
1), to index the
byte_2BDDA array, and put the resulting byte into offset
$30 of its own SST:
loc_2BF4A:
moveq #0,d1
move.b (Current_zone).w,d1
add.w d1,d1
add.w d0,d1
lea byte_2BDDA,a1
move.b (a1,d1.w),d0
move.b d0,$30(a0)
Then at
loc_2BFEA, after the animal first lands on the floor, it feeds this value into the
routine counter at offset
5 of its own SST, in order to determine whether it should hop or fly away:
move.b $30(a0),d0
add.b d0,d0
addq.b #4,d0
move.b d0,5(a0)
It is therefore vital that the
byte_2BDDA array contain two entries for each level in the game, as these determine which two animals may spawn in any given stage. With Sonic 3 in particular, every stage past Launch Base Zone reuses the same animal combination as Flying Battery Zone. In Sonic & Knuckles, only Doomsday Zone retains this property.
byte_2BDDA: dc.b 5, 1 ; AIZ byte_2C7BA: dc.b 5, 1 ; AIZ
dc.b 0, 3 ; HCZ dc.b 0, 3 ; HCZ
dc.b 5, 1 ; MGZ dc.b 5, 1 ; MGZ
dc.b 0, 5 ; CNZ dc.b 0, 5 ; CNZ
dc.b 6, 5 ; FBZ dc.b 6, 5 ; FBZ
dc.b 2, 3 ; ICZ dc.b 2, 3 ; ICZ
dc.b 6, 1 ; LBZ dc.b 5, 1 ; LBZ
dc.b 6, 5 ; MHZ dc.b 6, 1 ; MHZ
dc.b 6, 5 ; SOZ dc.b 0, 1 ; SOZ
dc.b 6, 5 ; LRZ dc.b 5, 1 ; LRZ
dc.b 6, 5 ; SSZ dc.b 0, 5 ; SSZ
dc.b 6, 5 ; DEZ dc.b 6, 1 ; DEZ
dc.b 6, 5 ; DDZ dc.b 6, 5 ; DDZ
dc.b 5, 1 ; Ending
dc.b 5, 1 ; ALZ
dc.b 5, 1 ; BPZ
dc.b 5, 1 ; DPZ
dc.b 5, 1 ; CGZ
dc.b 5, 1 ; EMZ
dc.b 5, 1 ; Gumball
dc.b 5, 1 ; Pachinko
dc.b 5, 1 ; Slots
dc.b 5, 1 ; LRZ Boss
dc.b 5, 1 ; DEZ Boss
Interestingly, in Sonic & Knuckles, the array was extended to cover every level slot, rather than stopping at Doomsday Zone. This was probably done for the sake of the Lava Reef Zone boss act, in which animals spawn from the capsule at the very end of the stage. Indeed, if we look at the numbers, all of the new entries in the array use the same animal combination as Lava Reef Zone.
The length of this array in Sonic 3 suggests that attempting to load any level slot beyond Doomsday Zone is inherently dangerous. But in reality, it only becomes a problem if an animal is spawned while playing the stage; it shouldn't affect whether the stage can load or not.
What does affect it are the animals'
graphics, which are loaded by the
PLCLoad_AnimalsAndExplosion function at
$543F4 when the level starts. Much like the animal object, it uses the value of the current zone as an offset to an array, which contains pointers to the
PLCs for each stage's combination of animals.
And therein lies the rub, because in Sonic 3, this array only goes up to Launch Base Zone:
off_54414: dc.w PLC_54422-off_54414 ; AIZ off_85FFE: dc.w PLC_8602E-off_85FFE ; AIZ
dc.w PLC_54430-off_54414 ; HCZ dc.w PLC_8603C-off_85FFE ; HCZ
dc.w PLC_5443E-off_54414 ; MGZ dc.w PLC_8604A-off_85FFE ; MGZ
dc.w PLC_5444C-off_54414 ; CNZ dc.w PLC_86058-off_85FFE ; CNZ
dc.w PLC_5445A-off_54414 ; FBZ dc.w PLC_86066-off_85FFE ; FBZ
dc.w PLC_54468-off_54414 ; ICZ dc.w PLC_86074-off_85FFE ; ICZ
dc.w PLC_54476-off_54414 ; LBZ dc.w PLC_86082-off_85FFE ; LBZ
dc.w PLC_86090-off_85FFE ; MHZ
dc.w PLC_8609E-off_85FFE ; SOZ
dc.w PLC_860AC-off_85FFE ; LRZ
dc.w PLC_860BA-off_85FFE ; SSZ
dc.w PLC_860C8-off_85FFE ; DEZ
dc.w PLC_860D6-off_85FFE ; DDZ
dc.w PLC_860E4-off_85FFE ; Ending
dc.w PLC_860E4-off_85FFE ; ALZ
dc.w PLC_860E4-off_85FFE ; BPZ
dc.w PLC_860E4-off_85FFE ; DPZ
dc.w PLC_860E4-off_85FFE ; CGZ
dc.w PLC_860E4-off_85FFE ; EMZ
dc.w PLC_860E4-off_85FFE ; Gumball
dc.w PLC_860E4-off_85FFE ; Pachinko
dc.w PLC_860E4-off_85FFE ; Slots
dc.w PLC_860E4-off_85FFE ; LRZ Boss
dc.w PLC_860E4-off_85FFE ; DEZ Boss
PLC_54422: dc.w 1 PLC_8602E: dc.w 1
dc.l ArtNem_BlueFlicky dc.l ArtNem_BlueFlicky
dc.w $B000 dc.w $B000
dc.l ArtNem_Chicken dc.l ArtNem_Chicken
dc.w $B240 dc.w $B240
PLC_54430: dc.w 1 PLC_8603C: dc.w 1
dc.l ArtNem_Rabbit dc.l ArtNem_Rabbit
dc.w $B000 dc.w $B000
dc.l ArtNem_Seal dc.l ArtNem_Seal
dc.w $B240 dc.w $B240
PLC_5443E: dc.w 1 PLC_8604A: dc.w 1
dc.l ArtNem_BlueFlicky dc.l ArtNem_BlueFlicky
dc.w $B000 dc.w $B000
dc.l ArtNem_Chicken dc.l ArtNem_Chicken
dc.w $B240 dc.w $B240
PLC_5444C: dc.w 1 PLC_86058: dc.w 1
dc.l ArtNem_Rabbit dc.l ArtNem_Rabbit
dc.w $B000 dc.w $B000
dc.l ArtNem_BlueFlicky dc.l ArtNem_BlueFlicky
dc.w $B240 dc.w $B240
PLC_5445A: dc.w 1 PLC_86066: dc.w 1
dc.l ArtNem_Squirrel dc.l ArtNem_Squirrel
dc.w $B000 dc.w $B000
dc.l ArtNem_BlueFlicky dc.l ArtNem_BlueFlicky
dc.w $B240 dc.w $B240
PLC_54468: dc.w 1 PLC_86074: dc.w 1
dc.l ArtNem_Penguin dc.l ArtNem_Penguin
dc.w $B000 dc.w $B000
dc.l ArtNem_Seal dc.l ArtNem_Seal
dc.w $B240 dc.w $B240
PLC_54476: dc.w 1 PLC_86082: dc.w 1
dc.l ArtNem_Squirrel dc.l ArtNem_BlueFlicky
dc.w $B000 dc.w $B000
dc.l ArtNem_Chicken dc.l ArtNem_Chicken
dc.w $B240 dc.w $B240
PLC_86090: dc.w 1
dc.l ArtNem_Squirrel
dc.w $B000
dc.l ArtNem_Chicken
dc.w $B240
PLC_8609E: dc.w 1
dc.l ArtNem_Rabbit
dc.w $B000
dc.l ArtNem_Chicken
dc.w $B240
PLC_860AC: dc.w 1
dc.l ArtNem_BlueFlicky
dc.w $B000
dc.l ArtNem_Chicken
dc.w $B240
PLC_860BA: dc.w 1
dc.l ArtNem_Rabbit
dc.w $B000
dc.l ArtNem_Chicken
dc.w $B240
PLC_860C8: dc.w 1
dc.l ArtNem_Squirrel
dc.w $B000
dc.l ArtNem_Chicken
dc.w $B240
PLC_860D6: dc.w 1
dc.l ArtNem_Squirrel
dc.w $B000
dc.l ArtNem_Chicken
dc.w $B240
PLC_860E4: dc.w 1
dc.l ArtNem_BlueFlicky
dc.w $B000
dc.l ArtNem_Chicken
dc.w $B240
So even if we were to fix up the
level load block for the Sonic & Knuckles stages, the PLCLoad_AnimalsAndExplosion function would then take the value of the current zone,
go past the end of the pointer array straight into the actual PLC definitions, and eventually attempt to load a nonsensical PLC, which would most definitely crash the game. Huzzah!
PLC_54476: dc.w 1 PLC_86082: dc.w 1
dc.l ArtNem_Squirrel dc.l ArtNem_BlueFlicky
dc.w $B000 dc.w $B000
dc.l ArtNem_Chicken dc.l ArtNem_Chicken
dc.w $B240 dc.w $B240
It was only by looking at the animal data side-by-side like this that I stumbled upon one of the most obscure differences between Sonic 3 and Sonic & Knuckles that I know of. In Sonic 3, the animals which appear in Launch Base Zone are Ricky (the squirrel) and Cucky (the chicken). In Sonic & Knuckles however, Ricky was replaced by Flicky (the flicky).
This wasn't an accident; if you check the
byte_2BDDA array at the top of this post, you'll see that the animal type was changed from 6 to 5 there, too.
Finally, on the subject of animal types, here's something that always bugged me. In Sonic 1, there were seven different animals. From left to right, they are: Pocky, Cucky, Pecky, Rocky, Picky, Flicky and Ricky.
Sonic 2 then added five more, whose names I can't find a source for: an eagle, a mouse, a monkey, a turtle and a bear.
When it came time to make Sonic 3, the developers decided to scale back to the original cast of animals. Indeed, if you look at the
byte_2BDDA array, you'll find that they're numbered 0 through 6... but animal 4 is nowhere to be seen.
Picky appears to have disappeared; not even his graphics can be found within the ROM. Maybe he's still hiding in the Casino Night Zone?