So I have been romhacking Sonic 1 to implement a new character, and yellow numbers appear on the jump sprites. Anyone got a fix?
In Sonic 1, the way the player's sprite graphics are loaded is that it takes the current sprite frame, loads it into an area of main memory, which is then loaded into video memory. That area of main memory has a fixed size, so that means if you have too many tiles in a frame, then it will only load what it can fit in there. The yellow numbers are the points graphics that display when you destroy a badnik. They are placed right after Sonic's graphics area in video memory. Due to the extra tiles not being loaded over into that area, the areas of the sprite that should have those extra unloaded tiles appear as those numbers. There are 2 fixes that come to mind. You can either expand the size of the sprite graphics area in main memory, or you can use the DMA queue, which basically allows you to easily load graphics straight from ROM into video memory without having to store the graphics in main memory first or having to deal with a limited space for loading the player graphics in. I personally recommend the second option. There's even a Sonic 1 implementation in part 3 of the spindash porting guide in the wiki, so it shouldn't be difficult to implement. Now, because with this you are allowing the extra tiles to load in video memory, they will overwrite the points graphics, so they will have to be relocated to another spot in video memory.
So I'm finally using the object code Markey provided 300 years ago (thanks Markey!). I get how it works and have it up and running in Sonic 1. It's a lot of fun, and I've played with it a bit. I'm trying to port it to Sonic 2 now and uh, I can't figure out how to import new art for a new object. Code (Text): move.l #Obj17_MapUnc,mappings(a0) move.w #make_art_tile(ArtTile_ArtNem_Pole,0,0),art_tile(ao) #And then much later near the end of the rom, right before the "align $100" that says it's free space. even ArtNem_Pole: BINCLUDE "art/nemesis/Pole.bin" even Obj17_MapUnc: BINCLUDE "mappings/sprite/newObj17.bin" #And in constants ArtTile_ArtNem_Pole = $03BE Forgive any whitespacing issues you see, I'm retyping this from another machine. Where have I screwed up?
Open "newObj17.bin" with a hex editor, and share what the values are inside of it, I have a... ...feeeling.
Usually the wild sprites are a sign the sprite rendering routine is given a rouge list of random crap. Given the mappings you've shared are fine, then it has to be something within the object's routine itself. It could be the map frame ID is wrong, or perhaps some wrong subroutine calls, there is no way of telling from the above information.
Thanks Markey The changes I listed above are the only changes I've made. Obj17 is the unused helix spike object from Sonic 1, at loc_10324. The rest of this object's code hasn't been touched, including the other times it tries to move the helix graphics to a0. Maybe that's it?
That's why then. The spike log changes the frame ID rotating around from 00 to 07 in a loop. You only have one frame (map frame 00), you need another 7 (01 to 07).
Well I went back into Flex2 and cloned the sprite a few times to fill out the animation and got this. It no longer softlocks, so that's a plus. Quick note: This specific look is a result of me changing the existing label in Obj17 to point to my mappings file. I changed it back to the way I quoted up above but still had a similar issue. Just different tiles showed up (Coconuts ones, actually).
Nope, didn't know I had to do something like that. It's there now, it's that weird pole looking thing. Code (Text): PlrList_Ehz1: plrlistheader plreq ArtTile_ArtNem_Waterfall, ArtNem_Waterfall plreq ArtTile_ArtNem_EHZ_Bridge, ArtNem_EHZ_Bridge plreq ArtTile_ArtNem_Buzzer_Fireball, ArtNem_HtzFireball1 plreq ArtTile_ArtNem_Buzzer, ArtNem_Buzzer plreq ArtTile_ArtNem_Coconuts, ArtNem_Coconuts plreq ArtTile_ArtNem_Masher, ArtNem_Masher plreq ArtTile_ArtNem_Pole, ArtNem_Pole PlrList_Ehz1_End So that's what the PLC list looks like for EHZ now. It's fine now, but some frames look like the above. What else have I done wrong? I'd like to avoid this in the future And yea the palette is wrong, but I'll figure that out later.
Alright, so how would I expand the sprite graphics in the main memory? It sounds a lot easier than the second fix and I'm a lazy piece of crap.
Just gonna steal this from an SSRG MarkeyJester made... Ultimately, though, adding a DMA queue will be better for you in the long run as it saves spaces in memory and is more flexible. You will still need to move the points graphics. Regardless of how you approach it, they will still get overwritten by the additional tiles being loaded for the player's sprite. @Aerosol It may help to show more of the object code or the mappings data again, because it still looks like there's still some kind of mappings error or sprite frame error.
Is anyone experienced with adding DPLCs to objects. I've tried adding DPLC to a projectile of mine to save tile space, but the issue is that now the object doesn't delete when the routine commands it to: Code (Text): lea (Ani_Laser).l,a1 jsr AnimateSprite bsr.w LoadProjDynPLC jsr (TouchResponse).l ;Touch_KillEnemy, Touch_Monitor jmp DisplaySprite Obj_Blob_Destroy: clr.b ($FFFFFFA4).w ; clear flag jmp DeleteObject ; loc_1D1AC: LoadProjDynPLC: moveq #0,d0 move.b mapping_frame(a0),d0 ; load frame number ; loc_1D1B2: LoadProjDynPLC_Part2: cmp.b objoff_30(a0),d0 beq.w return_ProjDPLC move.b d0,objoff_30(a0) lea (MapRUnc_Proj).l,a2 add.w d0,d0 adda.w (a2,d0.w),a2 move.w (a2)+,d5 subq.w #1,d5 bmi.s return_ProjDPLC move.w #$9280,d4 ; ****** Here ; loc_1D1D2: PPLC_ReadEntry: moveq #0,d1 move.w (a2)+,d1 move.w d1,d3 lsr.w #8,d3 andi.w #$F0,d3 addi.w #$10,d3 andi.w #$FFF,d1 lsl.l #5,d1 addi.l #ArtUnc_Proj,d1 move.w d4,d2 add.w d3,d4 add.w d3,d4 jsr (QueueDMATransfer).l dbf d5,PPLC_ReadEntry ; repeat for number of entries return_ProjDPLC: rts ; =========================================================================== Ani_Laser: dc.w laser0-Ani_Laser laser0: dc.b 1,1,2,3,$FF even Map_Obj4D: include "ProjMap_Unc.asm" even MapRUnc_Proj: include "ProjPLC_Unc.asm" even Any idea what I'm doing wrong here?
Hitaxas pitched in. Turns out the object code trying to load sub objects is what screwed it up. He went over the existing code and showed me what he stripped out and now my pole is displaying in game properly. Neat. Aaaand I thought I posted this a while ago but I forgot to hit reply. Sorry @Ralakimus There's a few more objects I want to add though, and apparently I have precious little tile space to add them in. Is there some...thing I can port from Sonic 3 to Sonic 2 to free up room or something? I know I can overwrite the letters used during the Sonic and Tails screen but I (ironically) want something a bit less hacky. A more complete solution to the space problem.
Ah right sorry: Code (Text): Obj4D: moveq #0,d0 move.b $24(a0),d0 move.w Obj_Blob_Index(pc,d0),d0 jmp Obj_Blob_Index(pc,d0) Obj_Blob_Index: dc.w Obj_Blob_Init-Obj_Blob_Index dc.w Obj_Blob_Move-Obj_Blob_Index dc.w Obj_Blob_Destroy-Obj_Blob_Index Obj_Blob_Init: move.l #Map_Obj4D,mappings(a0) ; ($C) move.w #$2,priority(a0) move.b #$8,width_pixels(a0) move.w #$9280/$20,art_tile(a0) move.b #$4,render_flags(a0) move.b #$4,y_radius(a0) ; $A lea (MainCharacter).w,a1 cmpi.b #$25,anim(a1) ; is NICOLE attack being performed? beq.w MinThrow ; if not, branch cmpi.b #$30,anim(a1) ; is NICOLE attack being performed? beq.w MaxThrow ; if not, branch MinThrow: move.w #$2C0,x_vel(a0) ; speed of laser move.w #-$430,y_vel(a0) ; speed of laser move.b #$38,$30(a0) ; distance laser will travel bra.w Obj4D_Cont MaxThrow: move.w #$740,x_vel(a0) ; speed of laser move.w #-$490,y_vel(a0) ; speed of laser move.b #$38,$30(a0) ; distance laser will travel Obj4D_Cont: btst #0,($FFFFB001).w beq.s + neg.w x_vel(a0) add.w #$40,x_pos(a0) bra.w ++ + sub.w #$40,x_pos(a0) + move.w #$9280,d2 addq.b #2,$24(a0) ; Obj_Blob_Move: cmp.b #0,$30(a0) bne.w ContCounter bra.w Obj_Blob_Destroy ContCounter: sub.b #$1,$30(a0) bsr.w ObjectMoveAndFall move.b $40,$20(a0) ; lol clr.w d0 move.b $1A(a0),d0 asl.w #$1,d0 ; *2 for correct offset lea (ArtUnc_Proj).l,a1 adda.w (a1,d0.w),a1 move.w #$9280,d2 lea (Ani_Laser).l,a1 jsr AnimateSprite bsr.w LoadProjDynPLC jsr (TouchResponse).l ;Touch_KillEnemy, Touch_Monitor jmp DisplaySprite Obj_Blob_Destroy: clr.b ($FFFFFFA4).w ; clear flag jmp DeleteObject ; loc_1D1AC: LoadProjDynPLC: moveq #0,d0 move.b mapping_frame(a0),d0 ; load frame number ; loc_1D1B2: LoadProjDynPLC_Part2: cmp.b objoff_30(a0),d0 beq.w return_ProjDPLC move.b d0,objoff_30(a0) lea (MapRUnc_Proj).l,a2 add.w d0,d0 adda.w (a2,d0.w),a2 move.w (a2)+,d5 subq.w #1,d5 bmi.s return_ProjDPLC move.w #$9280,d4 ; ****** Here ; loc_1D1D2: PPLC_ReadEntry: moveq #0,d1 move.w (a2)+,d1 move.w d1,d3 lsr.w #8,d3 andi.w #$F0,d3 addi.w #$10,d3 andi.w #$FFF,d1 lsl.l #5,d1 addi.l #ArtUnc_Proj,d1 move.w d4,d2 add.w d3,d4 add.w d3,d4 jsr (QueueDMATransfer).l dbf d5,PPLC_ReadEntry ; repeat for number of entries return_ProjDPLC: rts ; =========================================================================== Ani_Laser: dc.w laser0-Ani_Laser laser0: dc.b 1,1,2,3,$FF even Map_Obj4D: include "ProjMap_Unc.asm" even MapRUnc_Proj: include "ProjPLC_Unc.asm" even
Right here you're decrementing $30(a0) and bailing out when it gets down to zero. However... Right here you're using $30(a0) to store the last queued PLC frame. If frame zero never gets queued, then we'll continue taking the branch to ContCounter forever. Just move either of these to a different byte and you should be good.
One thing that comes to mind is having the shield and invincibility stars art be uncompressed, have both objects share the same VRAM space, like in S3K, since neither of them show up simultaneously, so it just makes sense to only have the current powerup graphics loaded at one time. Bonus points if you have them use DPLCs so only the currently shown art is loaded into VRAM at once. I don't think it'll save a whole lot of space, but it certainly helps, although I think this would only work out in 1P mode, since it is possible for Sonic to have a shield and Tails to have invincibility and vice versa in 2P mode. Maybe someone who has more experience working with Sonic 2 can chime in. You can also pull a Sonic CD and split off the level into sections, and only load the graphics for certain objects that are used in each section.
I actually wouldn't mind ripping out 2p mode entirely at some point, so if doing this breaks anything I'm not worried about it