Edited the page! https://info.sonicretro.org/SCHG_How-to:Port_Sonic_3's_Sound_Driver_to_Sonic_2 Is this good or not? I don't know if this is the right place to post this but... yeah. Anyways, here is a question I have: how can I make Tails fly like Sonic 3 in Sonic 2? By that I mean, in Tails Alone, if you double-tap a jump button, Tails will fly in Sonic 3. Also, in Sonic & Tails, if you hold up and double-tap the jump button as Sonic, Tails CPU will carry you. How can I implement that? I don't want to switch to Sonic 3.
Asking questions again, can I remove the 2P mode from my hack? I don't really need it and it's just taking space for me.
In Sonic 3 and Knuckles, is Dynamic_object_RAM_end the start address of the last slot in dynamic or the first byte AFTER all of dynamic object ram? I ask for several reasons. Create_New_Sprite looks for an available slot as the counter goes from 89 to 0. It looks through 90 slots starting at the SECOND SLOT (lea next_object(a1),a1 ). If it couldnt find any the counter will decrement to -1 and rts.... Because it starts at the second slot, it will try to find the slot at Dynamic_object_RAM_end if the preceeding 89 slots are being used. Does this mean that this is the last slot and NOT right after the end of dynamic object ram? Why does Create_New_Sprite start searching at the second slot? Code: Create_New_Sprite: lea (Dynamic_object_RAM).w,a1 moveq #((Dynamic_object_RAM_end-Dynamic_object_RAM)/object_size)-1,d0 bra.s loc_1BB0A loc_1BB0A: lea next_object(a1),a1 tst.l (a1) dbeq d0,loc_1BB0A locret_1BB14: rts Now the skdisasm equates state dynamic object ram has 90 slots, each 0x4A byte. By that, definiton, the first slot starts at Dynamic_object_RAM and last slot is the 0x4A bytes before Dynamic_object_RAM_end. Which is contrst to the above. Also, Level_object_RAM starts at Dynamic_object_RAM_end. Why is this seem different. By this, it appears Dynamic_object_RAM_end is after dynamic object ram and is some unknown 0X4a bytes for Level. FFFFB0DE : Dynamic_object_RAM ds.b object_size*90 ; $1A04 bytes ; 90 objects FFFFCAE2 : =$FFFFFFFFFFFFCAE2 Dynamic_object_RAM_end = * FFFFCAE2 : =$FFFFFFFFFFFFCAE2 Level_object_RAM = Dynamic_object_RAM_end ; $4EA bytes ; various fixed in-level objects FFFFCAE2 : ds.b object_size ; unknown
Question. What is the purpose of this specific invisible solid object in EHZ 1? I made some changes to a subroutine of SlopeObject in an effort to get character abillities to function on bridges and platforms, but in doing so, coming into contact with this specific subtype of Obj74 seems to crash the game. Removing it does some funky stuff to the level collision...
Looks like it's just a regular invisible wall to me? It only exists in KiS2, so maybe it's there to stop you from climbing up into the spring? No idea why removing it would affect the collision otherwise though, it's a solid wall/floor anyways.
Strange. Removing it causes an issue at the next loop on the top path, causing characters to fall through the floor, as if a plane swapper was interacted with. But I didn't change the original object layout aside from that one solid object. Basically, somehow collision path is being changed to 2. Edit: I fully restored the original object layout (I.E without any new objects), and now it the problem seems to have disappeared. Unsure what caused it.
I wanted to confirm I understand correctly how the Camera catches up vertically, as sonic jumps. Below is Sonic 2's ScrollVerti code section that does such ScrollVerti ;.checkBoundaryCrossed_inAir: ; If Sonic's in the air, he has $20 pixels above and below him to move without disturbing the camera. ; The camera movement is also only capped at $10 pixels. addi.w #$20,d0 sub.w d3,d0 bcs.s .doScroll_fast ; If Sonic is above the boundary, scroll to catch up to him subi.w #$40,d0 bcc.s .doScroll_fast ; If Sonic is below the boundary, scroll to catch up to him tst.b (Camera_Max_Y_Pos_Changing).w ; is the max Y pos changing? bne.s .scrollUpOrDown_maxYPosChanging ; if it is, branch bra.s .doNotScroll I made a diagram illustrating hopefully correctly what it does. Here is a reference to what each color line represents: green playery - cameray ( - 5 if sonic is rolling ) black = plus 0x20 blue = Camera_Y_pos_bias magenta = minus 0x40 the red boxes represent sonic . I messed up making his origin at top left it should be his middle ^.^ If sonic's Y is in the yellow rectangle region, Sonic is above the boundary ie: bcs.s .doScroll_fast ; If Sonic is above the boundary, scroll to catch up to him If sinc's Y is in the cyan rectangle region, Sonic is below the boundary ie: bcc.s .doScroll_fast ; If Sonic is below the boundary, scroll to catch up to him the green and blue rectangles represent that If Sonic's in the air, he has $20 pixels above and below him to move without disturbing the camera. Am I understanding this code correctly. Thank you and look forward to feedback
The Sonic physics guide on this site explains it quite well now thanks to lapper's & co. recent hard work, I checked it out the other day and it was quite comprehensive.
I'm working on a ROM hack and I ported Sonic's S1 pallet to Sonic 2. But every time I load up a level, the title card's pallet is all messed up! What's even weirder is when the level finishes up loading, the normal pallet loads in! What did I do wrong? (i used SonMapEd to port the pallet.)
Thank you I checked physics guide and either I am incorrect in my understanding of how the camera y catches up or I misunderstand that guide confirms it . I’ll clarify in a little while.
I am trying to apply this guide to the current version of the Sonic 2 GitHub disassembly, but I've run into a bit of a problem. The tutorial is for an older version that had the size of bytes to transfer hard-coded: Code (Text): moveq #$15,d0 ; Corrected to #$E in the tutorial The current disassembly uses a macro that I assume automatically calculates the size of the transfer based on the size of the PLC_Buffer as set in the constants include: Code (Text): moveq #bytesToLcnt(Plc_Buffer_Only_End-Plc_Buffer-6),d0 I tried simply swapping the macro call in place of the tutorial's hardcoded $E like this: Code (Text): ProcessDPLC_Pop: lea (Plc_Buffer).w,a0 lea 6(a0),a1 moveq #bytesToLcnt(Plc_Buffer_Only_End-Plc_Buffer-6),d0 - move.l (a1)+,(a0)+ move.w (a1)+,(a0)+ dbf d0,- moveq #0,d0 move.l d0,(a0)+ ; clear the last cue to avoid overcopying it move.w d0,(a0)+ ; rts However, it resulted in the game crashing with an illegal instruction exception about two seconds into a level (screenshot of the crash report is attached). Is there something else I need to change here to make it work? (I do suspect the "lea 6(a0),a1" may be a hardcoded value that needs to change, but I am not certain.)
bytesToLcnt is equal to the value divided by 4, then subtracted by 1. What you should do is either set that moveq directly to $E or, preferably, do this: Code (Text): moveq #(Plc_Buffer_Only_End-Plc_Buffer-6)/6-1,d0 The 6 value only needs to change if you change the PLC format in the game itself.
Thank you. I'm very new to programming in general, so I was unable to determine exactly what operation that macro performed. I did indeed set it manually to $E as a workaround, but I knew there had to be a way to retain the dynamic value. :>
It should be noted that many of these values and macros can be found in s2.constants.asm and s2.macros.asm in the current disassembly.
I have a question about the sprite rotation differences between the Genesis titles and Sonic Mania. I've noticed that there's a big difference in how the rotation "timing" is handled. Let me show you what I mean: Spoiler Here's an obvious one. In Mania, Sonic does not rotate at all when going up this slope in Green Hill Act 1: However, he rotates in both vanilla Sonic 1: and Sonic 1 Smooth Edition (where he rotates all over the place anyway, due to how the Classic rotating system works): Spoiler This is a weird one. In Sonic Mania, going up this loop does not rotate Sonic as soon as his angle changes. Instead, he suddenly turns 45 degrees at some point, after which he almost instantly falls off: For comparison, here's Sonic 1 Smooth Edition: And here's vanilla Sonic 1, where he seems to turn 45 degrees somewhat lower than he does in Mania: I have also noticed the same behavior occurs on Chemical Plant Zone's pipes. Spoiler Then we have this, which is by far the weirdest one. On this part of the loop in Mania, Sonic seemingly turns at the same time he does in Sonic 1: Also, here's a funny bug that I assume is caused by odd collision: Overall, the differences are quite baffling to me, so if anyone knows how the rotation system works in Mania, I'd love it if they could explain it here.
Having an issue with another tutorial that I'm trying to apply. I'm attempting to apply this optimization to Sonic 1, which requires a new variable (which I've equated to $FFFFFED0) that must be cleared every time the ring counter is reset. Code (Text): moveq #0,d0 move.w d0,(v_rings).w ; clear rings move.w #100,(ring_1up_limit).w ; <------- reset ring 1-up limit to 100 move.l d0,(v_time).w ; clear time move.l d0,(v_score).w ; clear score Unfortunately, the instances of this that are required in Lev_Sel_LevelSS and PlayLevel routines result in an "illegal value" build error in the former: Code (Text): LevSel_Level_SS: add.w d0,d0 move.w LevSel_Ptrs(pc,d0.w),d0 ; Inserting either of the indicated lines below results in an "Illegal Value" build error at this location bmi.w LevelSelect cmpi.w #id_SS*$100,d0 ; check if level is 0700 (Special Stage) bne.s LevSel_Level ; if not, branch move.b #id_Special,(v_gamemode).w ; set screen mode to $10 (Special Stage) clr.w (v_zone).w ; clear level move.b #3,(v_lives).w ; set lives to 3 moveq #0,d0 move.w d0,(v_rings).w ; clear rings move.w #100,(ring_1up_limit).w ; <------- move.l d0,(v_time).w ; clear time move.l d0,(v_score).w ; clear score ... PlayLevel: move.b #id_Level,(v_gamemode).w ; set screen mode to $0C (level) move.b #3,(v_lives).w ; set lives to 3 moveq #0,d0 move.w d0,(v_rings).w ; clear rings move.w #100,(ring_1up_limit).w ; <------- move.l d0,(v_time).w ; clear time move.l d0,(v_score).w ; clear score ... I unfortunately have no clue what could be going on. Any ideas?
From my understanding, what's happening here is: By inserting any of those lines between Code (Text): move.w LevSel_Ptrs(pc,d0.w),d0 and the table it's referencing, you're barely placing LevSel_Ptrs outside of the program counter (pc)'s reach, which is 128 bytes. To fix this, all you have to do is load LevSel_Ptrs into an unused address register. I'll use a0 in this example. Replace this: Code (Text): move.w LevSel_Ptrs(pc,d0.w),d0 With this: Code (Text): lea (LevSel_Ptrs).l,a0 move.w (a0,d0.w),d0 It should work now.