With the discovery of throw boxes in Slam Masters and the Marvel games, I figured it would be a simple job to go back and fill them out in the older games. Sadly, no.
Boxes are computed by assembling several pieces of information, including the area in ROM where the box data starts and the box ID that says which one to draw. The IDs for attack and vulnerability are either put directly in the object's alotted memory space or in a part of ROM pointed to by an animation frame pointer in the object's space. But for most of the games I examined, the IDs for throws are not put anywhere. The game runs a different subroutine for every different throw and loads the hardcoded ID directly into the data registers for further processing.
This may be an old technique to reduce RAM usage. Since most throws are decided in less than one frame, there would be no need to store information about them. However, it's a pain in the ass for me. Lua can read memory but cannot tell what's going on under the hood. Since those IDs are never written to memory (unless you count the processor's registers) there's no way to read them and know which box should be drawn.
When I refer to PC (program code) numbers I'm talking about the location in ROM where the code is. This info is output by the debugger and might be useful to someone who wants to investigate further.
In Vsav, Victor's fierce throw and air throw have hardcoded IDs 0x40 and 0x46 (PC $033E38 and $033D70). Throw IDs refer to the same table at offset 0x8C as attack boxes. The pushbox is the throwable box.
Gem Fighter throws use the same address table as attack boxes (offset 0x88) with a hardcoded throw ID. Felicia's normal throw has ID 0x60 (PC $033E08). There is a throwable box that is derived by reading a pointer at offset 0x1C and an ID at offset 0xB from that, but it seems to be identical to the pushbox in size and position.
SFA2 doesn't even use boxes per se for determining throws. Ryu's ground throws check whether the opponent's pushbox comes within 0x14 times 2 of his base position (PC $029624 - $029626). On the other hand, Charlie's air throw compares the distance between the character positions and does not examine the opponent's pushbox. The centers must be within 0x40 horizontally (PC $02EDAA - $02EDAC) and the target must be no more than 0x18 higher or 0x8 lower than Charlie (PC $02EDAE - $02EDB0).
I didn't look at SF2 but it's not likely to be any cleaner.
It's possible (I'm talking theoretical here) to make a Lua function that sets a breakpoint at a specified PC position and traces the emulation until a specified set of instructions occurs, extracts the desired values from those instructions, and uses them in its own execution. But even if that were implemented, we'd still need to have a firm understanding of how the games are coded in order to know what arguments to pass it.
SFA3, at least, puts those throw IDs in RAM at offset 0x32F. Nice. Except they don't go back to zero after the throw attempt is done. The game just stops checking, but the script doesn't know when to stop, so the yellow boxes won't go away. So, I looked for clues in memory that would tell when the throw stops.
I noticed the eight bytes at offset 0x4 seem to control the character's status. My first attempt was to compile a table of status values that correspond to throwing and check the character's state against it every frame. If no match, don't draw. Problem was it's not specific enough. Some throw values for some characters are non-throw values for others. What's more, the status value updates before the throw ID updates, so you see the throwbox from a frame ago instead of the current one.
So that would have been ugly. Then I thought of a miraculously simple solution: Just zero out the throw ID after every frame. With memory.writebyte
. It's OK because the ID gets rewritten before every active frame. The game doesn't care. Hell, I'm doing what it should be doing itself.
Bingo, yellow only appears on active throw frames. Don't blink, cause the active time is typically one frame. This fix caused me to discover that Zangief's 360+K throw is not continuously active. One frame at the beginning causes the atomic suplex and a few at the end do the powerbomb. Kind of easy to confirm but not obvious at first. Other observations: weaker versions of command throws have longer range, and a few characters have huge vertical range on their air throws.
In other news, I discovered why there was no attack box shown for Rolento's trip wire super. It's governed by completely different code. The attack ID is the byte 0x82 from Rolento's base address, divided by two, and plus 0x3E. That gets processed like any other attack or throw ID, but you also have to add the distance of the wire's killzone, which is read from offset 0x1E4.
This may be the only attack that requires this procedure. Hey guys, WTF? Just make it a projectile or make it a non-projectile. What were you thinking? Like throws, it grabs the opponent's green box, but unlike a typical throw it can be blocked low. It could have gone either way but I decided to make it red so the damn thing stands out a little more. The weaker versions have a wider grab area, but they are still too slow to be useful.
As with the throw IDs, the tripwire ID does not return to zero when the move is over. In order to prevent the box from being drawn forever I have to zero something out myself. I can't mess with offset 0x3E as it's used for other purposes, so I instead have the killzone distance reset.
Can you link the script which shows sfa3 throwboxes?
Posted by: CWheezy | 10/30/2010 at 09:48 PM
Or even better, sf2 series stuff
Posted by: CWheezy | 10/30/2010 at 09:55 PM
You can find em all here:
http://mame-rr.googlecode.com/svn/lua/
SFA3 is handled by the "cps2" script.
Posted by: dammit | 10/30/2010 at 11:44 PM
thanks sir, you are a scholar and gentleman for making these
Posted by: CWheezy | 10/31/2010 at 02:42 AM
I try.
Posted by: dammit | 10/31/2010 at 01:53 PM