I remember this happening to me too in Sandopolis Act 2. Kept getting lost and I just BARELY managed to beat the boss before time killed me. Let's just say... I got shocked with the time bonus.
Yeah, I thought it was some kind of overflow at first, I found out it was deliberate only eventually.
I never knew this was a thing! I know Sonic 2's code all too well to know it's not in there. But I'm curious if this is specific to Sonic & Knuckles Collection, or if the same thing happens in Sonic 3 and Sonic & Knuckles on the Genesis.
So I learned in Sonic Triple Trouble that you get a bonus if you finish the special stages at exactly 0:00 on the clock, and from that tried to do the same in main stages in it and at least Sonic Chaos to see if you got a bigger bonus (which may not have been true, though both gave you some extra points for doing so). From that I decided to see if I could also hit 9:59 in Sonic 3 and get a bonus, and did there too.
I added a file to keep external resources that don't seem to be placed with object code. The most interesting piece of data is the following table that stores which animals can pop out of enemies and the end-of-level capsule per zone: Code (C): #define POCKY_RABBIT 0 #define CUCKY_CHICKEN 1 #define PECKY_PINGUIN 2 #define ROCKY_SEAL 3 #define FLICKY_FLICKY 5 #define RICKY_SQUIRREL 6 unsigned char zone_friend_data[24][2] = { { FLICKY_FLICKY, CUCKY_CHICKEN }, // Angel Island { POCKY_RABBIT, ROCKY_SEAL }, // Hydrocity { FLICKY_FLICKY, CUCKY_CHICKEN }, // Marble Garden { POCKY_RABBIT, FLICKY_FLICKY }, // Carnival Night { RICKY_SQUIRREL, FLICKY_FLICKY }, // Flying Battery { PECKY_PINGUIN, ROCKY_SEAL }, // Icecap { FLICKY_FLICKY, CUCKY_CHICKEN }, // Launch Base { RICKY_SQUIRREL, CUCKY_CHICKEN }, // Mushroom Hill { POCKY_RABBIT, CUCKY_CHICKEN }, // Sandopolis { FLICKY_FLICKY, CUCKY_CHICKEN }, // Lava Reef { POCKY_RABBIT, FLICKY_FLICKY }, // Sky Sanctuary { RICKY_SQUIRREL, CUCKY_CHICKEN }, // Death Egg { RICKY_SQUIRREL, FLICKY_FLICKY }, // Doomsdag { FLICKY_FLICKY, CUCKY_CHICKEN }, { FLICKY_FLICKY, CUCKY_CHICKEN }, { FLICKY_FLICKY, CUCKY_CHICKEN }, { FLICKY_FLICKY, CUCKY_CHICKEN }, { FLICKY_FLICKY, CUCKY_CHICKEN }, { FLICKY_FLICKY, CUCKY_CHICKEN }, { FLICKY_FLICKY, CUCKY_CHICKEN }, { FLICKY_FLICKY, CUCKY_CHICKEN }, { FLICKY_FLICKY, CUCKY_CHICKEN }, { FLICKY_FLICKY, CUCKY_CHICKEN }, { FLICKY_FLICKY, CUCKY_CHICKEN }, }; For fun, let's see how often each animal is represented: Pocky the rabbit: 4 Cucky the chicken: 7 Pecky the pinguin: 1 Rocky the seal: 2 Flicky the flicky: 7 Ricky the squirrel: 3 Cucky and Flicky are clearly overrepresented. And that's without counting their entries in zones after Doomsday Zone!
I've been focusing on porting code related to Angel Island Zone. The code for its four enemies have been ported! Those being Cyclone/Rhinobot, Tullipon/Bloominator, Saruder/Monkey Dude, and Meramora/Catakiller Jr. Tullipon might be the simplest enemy in the game. It doesn't move. All it does is wait to appear on the screen, start a timer, then once that reaches -1 it'll shoot on frame 6 and 14. When it has finished animating, it waits once more. If you want to get an idea of how Sonic 3 works internally, this is a good place to start. There are two interesting things about Saruder. First, we all know it throws a coconut, right? Well, the game's labels don't refer to a coconut, but a banana. Maybe it was meant to throw that instead at some earlier point in development? Possibly related to the banana, the enemy's code has two data structures that seem to go unused. The first is called saru00_arm_set_tbl2, and it's meant to be used with function set_act00. Its data does nothing more than assign saru00_banana as the routine pointer. The second is saru00_arm_set_tbl2. I don't know what it's supposed to be used with yet. All I can see is that it has a pointer to saru00_arm. But each arm already has a structure with initialisation data. Then again, I can't determine where the coconut or banana is instantiated or even thrown (though there's plenty of code to move the arm when it takes a shot), so maybe that's done somewhere else and I have yet to uncover that reference.
That's interesting! If anything, I kinda figured Saruder was built on top of/heavy referenced Coconuts' code from Sonic 2 where that character does throw a coconut. All the stuff we have for Sonic 2 seems to suggest that's how that character was always intended to attack and Sonic 3 seems to have different naming conventions altogether skimming this thread. So...that's something. I kinda wonder if it was changed because the coconut just seemed more harmful to Sonic or it was easier to see in a busy environment. Nothing I'm saying here is terribly handy for your C port, but I did want to at least stop by and say thanks for your efforts. Your finds have been fun reads.
I've been looking at the code for the zone title cards, and am now wondering if the zone order I have for the 2P zones is correct. This is the enum for the zone order I have at the moment: Code (C): typedef enum zone_id { ANGEL_ISLAND = 0x00, HYDROCITY = 0x01, MARBLE_GARDEN = 0x02, CARNIVAL_NIGHT = 0x03, FLYING_BATTERY = 0x04, ICECAP = 0x05, LAUNCH_BASE = 0x06, MUSHROOM_HILL = 0x07, SANDOPOLIS = 0x08, LAVA_REEF = 0x09, SKY_SANCTUARY = 0x0A, DEATH_EGG = 0x0B, DOOMSDAY = 0x0C, ANGEL_ISLAND_DUMMY_AND_ENDING = 0x0D, AZURE_LAKE = 0x0E, BALLOON_PARK = 0x0F, DESERT_PALACE = 0x10, CHROME_GADGET = 0x11, ENDLESS_MINE = 0x12, ZONE_GUMBALL_MACHINE = 0x13, ZONE_SLOT_MACHINE = 0x14, ZONE_ROLLING_JUMP = 0x15, LAVA_REEF_BOSS_AND_HIDDEN_PALACE = 0x16, DEATH_EGG_BOSS_AND_MASTER_EMERALD_SHRINE = 0x17 } zone_id; What confuses me is that in the disassembly, Balloon Park is followed by Chrome Gadget, whereas in the list on Sonic the Hedgehog 3/Hidden content, it's followed by Desert Palace. Which one is right?
Now the code for the title cards has been committed! This is the second unlabelled object I've ported. The disassembly wasn't as documented for this one, resulting in lots of unknown values, which frustrated me. However, when I sat down to study them, I noticed similarities with actclear (end-of-act results tallying), and in the end they've all been documented. Phew!
I've been porting the code for springs, which more than once calls collision checking code. So I figured I'd also look at that to get a better understanding of what parameters to send and what to expect back. Almost 500 lines of code later: it's a mess, and I don't get it (which should be obvious by how much I just plainly transcribed the ASM to C). That portion was not labelled, after all, and documentation in the disassemblies is sparse. Can anyone familiar with how Sonic 2 or Sonic 3 handle collision lend me a hand? EDIT: Oh, I didn't see that Sonic 2's disassembly has loads of notes at SolidObject_OnScreenTest. Will have to take a closer look.
Also, https://info.sonicretro.org/Sonic_Physics_Guide#Collision. While it doesn't contain code (I think), it should help you figure out what happens and why.