Sega Master System / Mark III / Game Gear|
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew
3D Gunner 2D hackPosted: Fri Dec 20, 2019 11:46 pm
So I tried to hack 3D Runner.
First observation is that RAM address $c71e seems to invert on every frame. Examining the code I can see that it inverts it and adjusts the settings (name table location or X scroll) at $247 and $21c for the title screen and in-game respectively.
Nop-ing out the "neg" opcodes achieves a consistent right eye image. However, when shooting in-game the shot is consistently quite far to the left of the aimed point, seemingly in the position I'd see for the other eye - but the actual shot location shows nothing, and has no effect. Logically, the gun is pointing at a 2D location on the screen and the game needs to interpret that into two shot positions - but it seems to consistently choose the "wrong one" when I apply this hack (and have the "right eye" shot be where the gun was really aiming, which I guess is a mistake).
Elsewhere in the code I see that it also reads from $c71e at a routine from $1091, and uses it look up a value from a table at $1135. This table contains pairs of (-x, +x) values and this code seems to be about applying a perspective shift to sprite X positions based on which eye is open. This seems not to be related to the problem at hand. It's also read at a routine near $1cbb, which is related to the "starfield" effect.
The gun data handling is at $d07, and is quite similar to the example gun code in the official docs. However I can see that it seems to be correctly computing the X coordinate of where we shoot, so the game must be applying some offset to it.
Failing to figure this out, I tried just hacking the jump so it always shows the left eye (changing "jp nz" to "jp"). This doesn't help at all.
Eventually I figured it out: each frame, the game is computing the sprite locations for the "next" frame (i.e. the other eye). So when it sees the right eye is active, it computes sprite data for the left eye to see. I need to invert that. It is taking the "eye" flag (actually all 8 bits of $c71e), masking to bit 7, ORing with the sprite "depth" (which is 7 bits), then rotating it to get a 2-bytes-per-entry table index (with bit 0 always set to pick the second of the two). I always have $c71e = 0, so I want this mask to yield a $80 instead of a $00 (so the game will compute the "other side" sprites). I can thus patch the "or $80" to a "ld a, $80".
And thus we have the patch, in three bytes! I didn't hack the title screen to say "2D Gunner" though :) And it took me ages to figure out.
I used Emulicious to make the disassembly, and debugged in a mixture of Emulicious and Meka. Emulicious had a few helpful things - jumping between the disassembly and memory editor (right-click or ctrl+alt+M) to apply patches (as hex), and after applying them I can save the modifications as an IPS file from the Memory Editor's File menu. Not quite able to let me patch by typing assembly, but that would be insanely hard...
I also made a patch to disable the screen flash - as emulators don't need the flash to know where you clicked. Slightly surprisingly, Emulicious does need the flash - it doesn't register any shots if you apply that patch. For Meka it is OK, and we can play a little more comfortably.
||Posted: Sat Dec 21, 2019 2:12 am|
I had my fingers crossed that this was being looked into - I can't wait to try it out on the Mega Drive.
Thank you so much.
||Posted: Sun Dec 22, 2019 1:32 pm|
|Nice work maxim!|