I've been playing around with code recently, and after testing what I've made and loving it, I decided to show all of you my code with a tutorial. But wait, isn't it minor code changes? There's no point to make a tutorial for one simple code. Which is why, after confirming with a staff, I decided to make this thread where you can share minor code changes that can make a difference technically or during gameplay. It can be from any game if it has a disassembly and you explain what changes it brings to the game. It's a neat way to save thread space and more into collaborating with your ideas. Let's see how this thread goes then: I'll start with a simple guide: Be invulnerable after losing your invincibility status In Sonic games, when the invincibility time is over and you hit a badnik by accident, it can make the game really annoying for you since you have to go back and get rings. If you want to have a few seconds of invulnerability after you lose your invincibility, here is what you can do: In the code where it removes your invincibility, insert this below: Code: subq.b #2,$24(a0) move.w #$78,$30(a0) I will list you the labels from different Sonic 1 disassemblies: Sonic 1 Hivebrain: Obj01_RmvInvin Sonic 1 GitHub: @removeinvincible (under the file "Sonic Display.asm" in the _incObj folder) What it does is the first line will set the invulnerability frames and the second line will run 70 frames of invulnerability before you go back to normal. A pretty neat upgrade if you fancy not getting accidental hits. Now it's your turn! What small piece of code would you want to share that you believe isn't thread-worthy? Good luck!
Went ahead and pinned the thread, as I can see this gaining some traction & being popular with newcomers.
(This is technically a repost, but since the old thread has been moved to the archives, it felt fitting to have that one-liner in here as well.) Basically, when you roll really, really fast into a Caterkiller from Marble Zone and Scrap Brain Zone, you will still get damaged despite having destroyed it. In the original game this wasn't really much of a noticable issue, since the locations where you can even gain enough speed to roll this fast are scarce, if they exist at all. But nowadays, the spin dash is everywhere and it's very easy to bash head-on right into a Caterkiller at mach speed at any time. The issue is that the spiked body segments don't get deleted immediately when destroying the head, but rather 1 frame later. So if you happen to roll fast enough to travel the distance to the body in that single frame, you still get damaged. Fixing it literally takes a single line. Go to loc_16C7C and add this below the label: Code: clr.b $20(a1) Now, when the head gets destroyed, all touch response values (which are responsible for objects being solid, breakable, damaging, etc.) from the body spikes are getting set to 0, which essentially means harmless and without collision. This is more straight forward than deleting all the body segments immediately, though that would obviously work as well.
If you have added the Spindash, heed this warning if you intend for any of your levels to be longer than 64 256x256 chunks, or 128 128x128 chunks. (I'll be using GitHub here, as it's where I had to face this issue). If you just added horizontal scroll delay, MoveScreenHoriz, at the top, should look like this: Code: MoveScreenHoriz: move.w (Horiz_scroll_delay).w,d1 beq.s @cont1 subi.w #$100,d1 move.w d1,(Horiz_scroll_delay).w moveq #0,d1 move.b (Horiz_scroll_delay).w,d1 lsl.b #2,d1 addq.b #4,d1 move.w (v_trackpos).w,d0 sub.b d1,d0 lea (v_tracksonic).w,a1 move.w (a1,d0.w),d0 and.w #$3FFF,d0 bra.s @cont2 See that and? S3K increased it to 7FFF. Why? Because otherwise, the scroll delay completely breaks past 64 256x256 chunks, or 128 128x128 chunks. So, if your levels are going to be bigger than 64 256x256 chunks, or 128 128x128 chunks, then change that and. (Note that this would only be possible if you modified the level layout format.)
Changing the layout of the results card.. This is more of a personal preference, but if you don't like the layout of the result cards in Sonic 1, seen here: Spoiler: Original Sonic 1 end results screen And you want a customized version similar to this: Spoiler: Modified results screen Here is how you modify the end result layout then, shall we? If we jump over to Obj3A_Config, which is where you will see this: Code: Obj3A_Config: dc.w 4, $124, $BC ; x-start, x-main, y-main dc.b 2, 0 ; routine number, frame number (changes) dc.w $FEE0, $120, $D0 ; SONIC HAS dc.b 2, 1 dc.w $40C, $14C, $D6 ; PASSED dc.b 2, 6 dc.w $520, $120, $EC ; act number dc.b 2, 2 dc.w $540, $120, $FC ; score dc.b 2, 3 dc.w $560, $120, $10C ; time bonus dc.b 2, 4 dc.w $20C, $14C, $CC dc.b 2, 5 This is the layout code, and this is where the results layout lies in place. You may notice that the code shown looks different. This is because I labelled the code to make it easier for you to modify the positioning of the results screen. To any of you that are struggling with adjecting that layout, let me run down with what you need to know. If you are considering changing the X axis of the layout, increase the x-main and it will move to the right (decreasing is to the left), but doing so will cause stuttering when loading the results screen so the amount of times you increased the X axis will be the amount of times you need to increase (decrease if you're moving it to the left) the x-start code. For the Y axis, you can increase it to risen the layout (decrase to lower it) however you do not need to modify the starting position as the results screen flies horizontally, not vertically. You know the example I provided above? If you would like to use it, replace Obj3A_Config with this: Code: Obj3A_Config: ; routine number, frame number (changes) ; x-start, x-main, y-main dc.w 4, $124, $BC ; SONIC HAS dc.b 2, 0 dc.w $FEE0, $120, $D0 ; PASSED dc.b 2, 1 dc.w $40C, $14C, $D6 ; act number dc.b 2, 6 dc.w $520, $120, $122 ; score dc.b 2, 2 dc.w $540, $120, $F2 ; time bonus dc.b 2, 3 dc.w $560, $120, $102 ; ring bonus dc.b 2, 4 dc.w $20C, $14C, $CC ; The blue bit of the card dc.b 2, 5 This version has been modified to make the layout similar to Sonic 2 by moving the "score" text below and moving the "time bonus" and "ring bonus" slightly up. And there you go! You should understand how the end results layout works. It is easy once you understand how the X and Y axis works in the screen and possibly help you for other bits of code. Enjoy having a custom results screen!
S3K-Esque Act Clear Timing If for some reason you want the Act Clear music to play when the results tallies are fully positioned on screen, like with Sonic 3 and Knuckles, here's how this can be done. It can go alongside DeltaWooloo's switched around results layout thingy. This is meant for the 2005 Hivebrain disassembly, but I'm sure a similar concoction can be made in the GitHub disassembly. Head to loc_ECD0 in sonic1.asm and you'll notice a command for 8E to be played when the signpost stops spinning. Change the 8E to E0, which is the fade out command. Next, go to Obj3A_ChkPos and underneath the neg.w d1 line, insert this: That way when the act result screen tallies are all in place (has passed text, time/ring bonus tally, etc.), the act clear song will play. For the special stage results, the same applies. Delete the lines of code calling for #$8E to be played in loc_47D4. And when you get to Obj7E_ChkPos, you'd add those two lines that call for the song to play when all the tallies are displayed, right underneath the same neg.w d1 line. Not sure if this has been conducted before or properly well constructed, but if anyone's got feedback for how this can be better, lemme know. Thanks in advance!
This is a nice short and easy thing actually but I guess it's more of a personal preference (as you mentioned in it as well).
Unsure if it's mini enough, but oh well. Separate Boss Music Tracks One thing to bring up is that you'll need to follow this guide to get started: http://info.sonicretro.org/SCHG_How-to:Expand_the_music_index_from_$94_to_$9F This can expand the music index to allow more tracks, either all the way to 9F or to a limit of your choice. First, get some boss music ported to Sonic 1's SMPS driver (modded 68K DAC Type 1B) with SMPSConv. At least four tracks will do considering the GHZ and FZ bosses. If you port five tracks, check out the last optional step for a cute bonus. Include the tracks in the MusicIndex and the incbin Music lines near the end of sonic1.asm. The former looks like this: Code: MusicIndex: dc.l Music81, Music82 dc.l Music83, Music84 (the rest of the tracks are there, trust me) The latter would look like this, with 8C as an example: Code: Music8C: incbin sound\music8C.bin even Now let's get to the meat of this guide. The header Resize_GHZ3boss has a command to play the boss music which is at the start of loc_6ED0, and similar loc's are present with the Resize_boss headers for other zones. These all would use 8C due to it being the primary boss track. The section of the code would be this: Code: move.w #$8C,d0 bsr.w PlaySound ; play boss music With the four additions to the music index, use the added songs' digits in place of 8C, mainly with the MZ, SYZ, SLZ, and LZ Resize_boss sections of code. Final Zone uses a different track anyway, so it would be pointless to redirect it. BONUS: If you added a fifth song as part of the SBZ 2 to 3 cutscene, the same can be done under Obj3A_SBZ2. Much like my last mini-guide, a similar concoction can be done in the GitHub disassembly as I've used Hivebrain '05. No credit is required for this one, unless it's for whoever made the songs you're porting. Thanks!
Make the music resume after death. What I've hated in Sonic games is when you die, the music restarts. It makes sense since you are restarting the level; however, in my opinion, it bugs me because I want to get the chance to listen to the whole song rather than waiting for the music to come back to the part you have missed. If you're going to have that feature removed in Sonic 1 (for Sonic 2 and 3K, you should be fine doing it yourself), here is what you do: Firstly, I would suggest hunting down for a free RAM byte. If you don't know which RAM bytes are unused, it would advise running down here. For the sake of the tutorial, I would represent a RAM byte as $FFFFXXXX. Now don't think about using that byte; it is invalid and is only used as an example, so you need to replace that byte with the one you've found. In any case, here we go! Firstly, we need to set the RAM after Sonic dies, so we need to jump to Kill_Sound and paste this at the end: Code: tst.b ($FFFFF7AA).w ; is boss mode on? bne.s Kill_NoDeath ; if yes, branch move.b #1,($FFFFXXXX).w This will set the byte to 1, which can allow us to include the feature we desire; also, the reason why I have the code to check the boss mode because there was originally a bug where if you died during a fight with Eggman, the boss music still plays after you reset. So what this does is it'll check if the boss mode is on and, if so, branch to the next label, skipping the RAM byte we've just added. Now, let's jump over to the label "Level", and this is what you see: Code: Level: ; XREF: GameModeArray bset #7,($FFFFF600).w ; add $80 to screen mode (for pre level sequence) move.b #0,($FFFFFFD0).w tst.w ($FFFFFFF0).w bmi.s loc_37B6 move.b #$E0,d0 bsr.w PlaySound_Special ; fade out music We need to comment between the code that checks for RAM "$FFFFFFF0" to the end. The reason why is because the music would fade out after you reset the level, thus preventing us from having the song restart and also, the RAM check is useless as it will branch to the code below, so let's comment out these codes. Last but not least, let's move onto Level_GetBgm and from here, you should insert this at the start: Code: tst.b ($FFFFXXXX).w ; DW: has the RAM been set bne MusicLoop ; DW: if yes, branch and skip the music loading code below This checks if we set the RAM from the code to Sonic's death and then jumps into a new label skipping the code that loads the song in. The last step we have to do is set the new label we have added, so let go to Level_PlayBGM, and below that, insert this: Code: MusicLoop: move.w #$E3,d0 jsr (PlaySound).l ; run the music at normal speed move.b #0,($FFFFXXXX).w move.b #$34,($FFFFD080).w ; load title card object This does set the RAM byte to 0, runs music at normal speed, if you have speed shoes and loads the title card, and speaking of the code, you can see Level_PlayBGM has the same title card loading code we have, so we need to remove it there. Now you may think you are done; however, there is one final step to this procedure. When you get a game over and play the levels again, you can see the music hasn't been loaded. This is due to the code we've set being skipped as the Game Over routine occurs, so to fix that, you need to go to SEGAScreen and clear the RAM byte like this: Code: SegaScreen: ; XREF: GameModeArray move.b #$E4,d0 bsr.w PlaySound_Special ; stop music bsr.w ClearPLC bsr.w Pal_MakeFlash clr.b ($FFFFXXXX).w One little oversight people told me in Discord is that the music holds for a bit after you die. Part of the reason this occurs is due to the zone's art being Nemesis, which you know takes a while to load, and also, there is the title card object that is also Nemesis, which causes the slowdown. To fix this bug, jump over to loc_37B6 and remove these lines. Code: move #$2700,sr move #$2300,sr This wouldn't be a problem if you ported a Z80 driver to your hack, but if you still have the Sonic 1 driver, this is worth removing if you don't fancy experiencing it. And you should have that feature intact; I hope you enjoy it. I want to thank TheInvisibleSun for initially showing me how to do it and how it's presented, which led me to expand upon it over time. Also, special thanks to Nat the Porcupine for showing me the fix to the minor bug I presented above. The only bug is if you get invincibility, it doesn't want to restart after death, even with checking the RAM like the boss mode. I will fix this as soon as possible. Update: presented a mini bug that I've found.
I think I have a sort-of tutorial. (For github disasm) HOW TO CHANGE YOUR ROM'S HEADER Very simple. Look for these lines in Sonic.asm: Title_Local: dc.b "SONIC THE HEDGEHOG " ; Domestic name Title_Int: dc.b "SONIC THE HEDGEHOG " ; International name Change the text in the parenthesis to whatever you like. Pointless, but felt like pointing it out.
Implement the Shrinking animation for losing Special Stages in S1 A quirk of S1 beta remakes that I did find interesting was utilizing an unused animation for Sonic in some areas. For example, one remake uses the shrink animation for when you lose a special stage (touching the Goal block anyway). This lil guide will show you how to include that animation. Look for Obj09_GOAL inside the sonic1.asm file, and under the move.w command for "change item" plop this line of code underneath: Code: move.b #$19,$1C(a0) ; use "shrinking" animation This will ensure for when you touch the Goal block, the shrink animation plays. Like before, this is with the Hivebrain 05 disassembly, but a workaround could be crafted with the GitHub version. Cheers!
Actually, you can't just put whatever there. If I remember correctly, the amount of characters in the header is 48. I don't know why, I just know that's how it is. In most emulators, it'll show this, instead of Sonic The Hedgehog. "SONIC THE HEDGEHOG " A fix for this, however is just adding spaces until you reach 48 characters, or cutting down on some (if you have more than 48).
Sorry for double posting but I feel like this is better off here than it's own thread. Working With Special Stages in Sonic 1 This allows you to change the speed of the stages. Hivebrain Code: move.w #$40,($FFFFF782).w ; set stage rotation speed Github Code: move.w #$40,(v_ssrotate).w ; set stage rotation speed See these lines? They set the rotation speed in the special stage, which is set to 40 by default. For example, If I wanted to make the special stages rotate a bit faster, I could change the speed from 40 to 50, or if I didn't want them to rotate at all, I could either set the value to zero, or remove the line entirely (though I don't recommend it).
Oof... this doesn't seem to work; here's why. Firstly, putting the end of act song at "Obj3A_ChkPos" wouldn't work since the code handles the title card moving to its position and placing the code there wouldn't work as it will keep playing the song over again until everything is at its destination. And also I think rather than setting it to GotThroughAct, why don't we load the fading out command earlier in case you want to have specific events such as showing the results straight after a boss. Luckily, I took a look at the S3K disassembly, and I'd figured another solution to this. Before we get started, go to loc_ECD0 (loc_47D4 for the special stage) and remove any code that redirects you to the fade-out command or act clear music. Firstly, go to Obj3A_Main (Obj7E_Main for the special stage) and, at the top, place the code to the music, fading out the command. If you don't know what that is, here is the code: Code: move.w #$E0,d0 jsr (PlaySound_Special).l ; fade out music Part of the reason I placed it is that the command can work as soon as the results object comes in rather than waiting for a routine to do its job, that being GotThroughAct. Next, go to loc_C61A (loc_C86C for the special stage) and place the code to the initialisation of the results music below the second line. Code: move.w #$8E,d0 jsr (PlaySound_Special).l ; play Got-Through Act music While this isn't how Sonic 3 and Knuckles does it due to the code setting extra checks for different levels; however, this is identical to how it would run in the game. I will say Speems did make a good attempt at making it feel like S3K; good job.
Okay, so this has always annoyed me when altering Sonic 3 and Knuckles (or just playing Sonic Classic Heroes, which reused the same code). When the screen flashes it makes sure to keep the black on the transparency colour for PAL/Overscan users, which most other Genesis games did during this time. However, it does limit background colours, optimisation and potential layering (Look at Aquatic Ruins leaves for example). That, and the game uses this colour by mistake anyway! So let's fix this issue. Just note that with water, only Hydrocity separates the transparency colour properly For Hyper Sonic the flash data is at VInt_8. It should look like this Code: stopZ80 bsr.w Poll_Controllers tst.b (Hyper_Sonic_flash_timer).w beq.s VInt_8_NoFlash ; flash screen white when Hyper Sonic's double jump move is used subq.b #1,(Hyper_Sonic_flash_timer).w lea (VDP_data_port).l,a6 move.l #vdpComm($0000,CRAM,WRITE),(VDP_control_port).l move.w #$EEE,d0 move.w #$1F,d1 - move.w d0,(a6) dbf d1,- ; fill entire first and second palette lines with white move.w #0,(a6) ; keep backdrop black move.w #$1E,d1 - move.w d0,(a6) dbf d1,- ; fill remaining colours with white bra.s VInt_8_Cont Even to a guy who can't read Assembly like myself, the issue is pretty obvious. It fills the object art completely, then stops at the start of the third palette (which the Sonic Engine uses as it's transparent colour) and continues 1: Remove these lines, they're unnecessary after the fix Code: move.w #0,(a6) ; keep backdrop black move.w #$1E,d1 - move.w d0,(a6) dbf d1,- ; fill remaining colours with white 2: change $1F to $3F, this fills the entire palette with white Code: move.l #vdpComm($0000,CRAM,WRITE),(VDP_control_port).l move.w #$EEE,d0 move.w #$1F,d1 And... yeah, that's it for Hyper Sonic Now, when the Lightning shield goes into water it uses different code. We're just going to do the same for Obj_Lightning_Shield_FlashWater, which should look like this Code: Obj_Lightning_Shield_FlashWater: move.l #Obj_Lightning_Shield_DestroyUnderwater2,(a0) andi.b #$8E,status_secondary(a2) ; Sets Status_Shield, Status_FireShield, Status_LtngShield, and Status_BublShield to 0 ; Flashes the underwater palette white lea (Water_palette).w,a1 lea (Target_water_palette).w,a2 move.w #($80/4)-1,d0 ; Size of Water_palette/4-1 loc_197F2: move.l (a1),(a2)+ ; Backup palette entries move.l #$0EEE0EEE,(a1)+ ; Overwrite palette entries with white dbf d0,loc_197F2 ; Loop until entire thing is overwritten move.w #0,-$40(a1) ; Set the first colour in the third palette line to black move.b #3,anim_frame_timer(a0) rts ...nevermind, it's even easier, just remove this Code: move.w #0,-$40(a1) ; Set the first colour in the third palette line to black There are probably more examples of this, but these are the easiest to do on command