This project involved analysis and modifications to the Street Fighter 2 Champion Edition ROM. Analysis was done via reverse engineering of the original game from binary. Modifications were done via a series of patches written in assembly language, assembled, then binary-inserted into SF2CE program ROMs.
The result (after few months of work) was several different versions (romhacks), involving dodging falling barrels, avoiding fires burning on the ground, catching birds, and leeching life from the enemy.
Unlike most - if not all - existing SF2CE romhacks, I did not modify the game mechanics and physics. I think those are fine the way they are... for now. Instead, I chose to - each time - add something "on top" of the original game.
Most of these versions are best suited for two players, because the computer is completely unaware of the "extra" rules. "Vampiric" version works well in one-player games.
Bird in Hand romhack
Catch birds to win rounds.
Burning Fires romhack
Avoid fires to avoid being toasted.
Barrels of Fun romhack
Dodge the falling barrels or be squashed.
Fire Serpent romhack
Avoid fire serpents to avoid being burnt to a crissssp.
Vampiric romhack
Damage your enemy to regain life. Bla, bla-bla!
Aimed Barrels romhack
Launch barrels on your enemy's head.
Aimed Fires romhack
Create fires under your enemy.
Downloads, screenshots, videos
Visit individual romhack pages above.
Work environment
ROM set: Street Fighter 2 CE (World, 920513)
ROM set filename: sf2ce.zip
Debuggers: MAME 0.267 built-in debugger (primary, used for program analysis), WinKawaks 1.65 built-in debugger (secondary, used to test modifications quickly)
Assembler: LEA (for assembling binary patches)
Hex editor: HxD
OS: Windows 10
Patching, byte swapping: custom-built tools
If you'd like to make your own Street Fighter 2 rom hacks, you can start from my toolchain found
here.
Technical information
In each modification, I removed original program's system checks, version screen, and licence screen; this is so the game boots faster. For the same reason, I skipped the intro building scrolling upward, transitioning directly to the title screen.
Many elements of the UI were removed, including score, "FREE PLAY", "PRESS START" messages at the top of the screen. The reason for this is to free up graphical tiles for me to use to display barrels, fires, etc.
Original game UI, showing multiple elements at the top (score, high score, INSERT COIN).
Modified game UI with many elements removed, and some new ones added.
From start to release, these seven Street Fighter 2 versions took several hundred hours of work. A large initial chunk of time was spent reverse engineering Street Fighter 2 (the original program). This consisted of analysis of the binary, data structures, overarching patterns, and then documentation and annotation of the disassembly generated by MAME's built-in debugger.
From there on, my time shifted more to development of new features. Analysis of the original program continued, progressively taking less time.
Achieving the modifications relied on patches, which were assembled via the LEA assembler, and then applied to the original game binaries via additional tools I created.
To build a modified ROM set, I reused the toolchain I created for the Knights of the Round romhacks, few years prior. One significant modification to the toolchain was to support 3 program ROMs (for Street Fighter) instead of the 2 (for Knights of the Round).
Each build relies on approximately 40 patches, most of them very small. Each patch replaces a contiguous portion of the original ROMs. Here are a few types:
- short circuit: These are few bytes in size and either NOP out a call, or RTS (return) early, to inhibit/disable certain functionality from the original game. Example: scrolling the tall building upward during the intro, to save time.
- custom main: This is a single, large patch with various entry points, meant to be entered from the original program. It resides in an unused portion of the 1.5 Megabyte program ROM, and writes/reads its state from an unused portion of the RAM.
- hooks: Their role is to be injected into the original program code and to JMP/JSR (jump or call) into the custom main patch, at its many entry points. Examples: "on intro start", "on title screen start".
Development log
The complete toolchain I made for Knights of the Round is going to be great help, since SF2 is also a CPS1 game.
Loaded ROM in MAME and confirmed via memory viewer placement of 3 separate ROMs in memory. They're backwards, with ROM 23 first, then 22, then 21; each is 512kb (0x80000).
Byteswapping the 3 ROMs makes it convenient to use a hex editor to look for English words, so that various strings can be found.
At least the strings on the bootup game information screen are 0-terminated, with '/' causing a line break. First byte is the attributes byte. Second and third bytes are X and Y locations.
Successfully coded and applied a "Hello world" patch, proving that the toolchain has been modified to build patched SF2CE ROMs. One difference from the Knights of the Round toolchain upon which it is based is that SF2CE uses 3 512kb program ROMs, whereas Knights of the Round only uses 2.
Capcom was actually wasteful, with over 300kb of ROM being unused. Of course, that's where my main patch will go, so I'm thankful for their waste. But if they weren't, perhaps SF2CE would've been a worse game...
Verified various hardware registers carried over from Knights of the Round, which are similar to KOTR.
Identified first routine, "print string". This routine is used to print introductory text with fade.
Strings are stored:
X, Y, palette, "some ascii", "/", X, Y, palette, ..., "/", X, Y, palette, 0
such that each line has its own locations and palette bytes.
Found a breakpoint which is hit every time anything animates on screen.
This led me to a sequence of many calls (bsr/jsr), which tend to exist in high level routines. High level routines are essential for understanding over-arching structure. By noping individual calls in the sequence, I can figure out what is lacking, which leads to discoveries of purpose of low level routines.
Routines dealing with gameplay elements (e.g. registering attacks) all follow a pattern of wrapper->jump table per character->character-specific routine.
Jump tables are indexed using a pointer read from offset 291 of one of two memory blocks. This can be a lead into player property storage. (since register_standing_normal_attacks is called alternatingly with per_character_jumptable[mem1[291]] and per_character_jumptable[mem2[291]])
Thus offset 291 of mem1 and mem2 looks like a character ID, with Ryu=0.
Figured out jump table order of entries for at least some routines. I pray that Capcom programmers kept jump table order consistent.
ryu(0) honda blanka guile ken chunli zangief dhalsim mbison sagat balrog vega
This is great because I can easily pass over any wrapper functions and into character-specific functions now
Confirmed suspected player state memory areas. By watching this memory, I was able to find some animation and movement routines. Since I don't yet have a good understanding of enough structure, I don't know how these fit in yet.
I'm interested in finding memory areas which deal with special attacks. By pure chance, I looked near the memory area for player 1 and found a circular buffer which seems to store last executed moves. Punches, kicks, special attacks each has an identifier which gets populated in the circular buffer as the moves are performed.
That the circular buffer stores unique values for special moves should lead me to special move routines, which are interesting to me. For example, a hadouken (ken and ryu) is 0x74.
From among many types of attacks, I believe I've isolated the top-most routine for the initiation of a hadouken. Confidence is high because I can see the value 0x74 being setup as an argument for a call into something which I've annotated as "save move to history".
0x74 for hadouken is hardcoded. I bet that I could find similar chunks of code - though with other values - if I wanted to modify each special move in the game.
At the start of a hadouken, (we used to call it a "ball" in Romanian... short for "ice ball" - which is what we thought it was!) a number of properties from player state are copied to another, as of yet unknown, memory area. I'm interested to find the properties of a single hadouken.
There are two memory areas which track projectile states, by player. Upon initiating a hadouken, an entry is initiated in these memory areas.
I was wrong - the same projectile state memory area is shared for both players.
I've found the offset of hadouken's X location.
From hadouken's X location, I worked out the "animate projectile" routine completely. This gives good insight into how projectile speeds are stored. This routine covers all kinds of projectiles, not just hadoukens.
Fun fact: the cyclists in chunli's stage count as projectiles.
Next, I would like to learn about allocation of projectiles, because it's quite clear that there is a large array in which they can be allocated. This is evident from the potential of 2 fighter-shot projectiles, plus background stuff. Not to mention that there exist SF hacks in which the screen fills with projectiles - and I doubt whoever hacked that reimplemented projectile logic to allow for higher capacity.
I also think I'm going to switch to development a bit, to get the main (large) patch building. I think I'll also disable all intro stuff (like I did in the KOTR hack), so the game boots faster. I really hope the trap knowledge is tranferrable, due to SF2 and KOTR being about the same age.
I still have a list of traps from my prior Knights of the Round work, and I bet that Capcom has kept things similar between games.
Trap 0 was "task add" in KOTR. Each screen/phase is added via trap 0. To skip all checks, warnings, etc. and go directly to the title screen is a matter of adding the "main screen" task first, instead of tasks such as "ROM tests screen".
There aren't many trap 0 invocations, so I will try noping them one by one to establish which represents the title screen.
Capcom added a level of indirection to trap invocations; I'm not sure why, but it explains why there are so few explicit trap invocations.
The way traps are setup is more confusing than in Knights of the Round. SF2 tasks do a lot more, so they're harder to isolate.
What I'm working on currently is speeding up the bootup of the ROM. There are many print string routines, a lot of copy-pasted code. However, I was able to isolate the bootup CPS1 checks (RAM, OBJECT, etc.) and I am now able to immediately bypass them - saving about 2.5 seconds of waiting.
Wrote a patch which prevents the long delay on the version screen, speeding up startup more.
Wrote a patch which removes the delay in a certain "print string" routine. It was relying on trap 3 (yield task delayed) to print characters one by one (such as on the warning screen). This speeds up startup.
Removed version and warning strings entirely, by terminating them early. The ROM now boots to the "street brawl" intro screen directly.
The large patch I call "custom main" is now integrated. It loads in a previously-unused portion of the program ROM. It begins with a jump table, with jumps to various custom routines I will be implementing. Hooks from original program ROM will jump/call into the jump tables. This allows the custom routines I write to grow as needed. I also found a chunk of RAM that's unused, so my variables can go there.
I stole Knights of the Round's "print to 8x8 layer" routine. After few adjustments, it works. This can be used on title screens, etc.
However, for in-game text display, Capcom uses tiles on 16x16 layers. This poses a problem because most tile "slots" are already in use on those layers. Symptoms appear when I try to display text that's long enough to overwrite other tiles, resulting in flicker around the screen, depending on which tile was overwritten.
The good news is that I can now display tiles during gameplay, opening the door to extra graphics.
The bad news is that the extra graphics, text, etc. are limited. I might disable certain currently-in-use tiles, such as the score counting at the end of a round to make room for more extra graphics.
Disabled the flashing FREEPLAY message at the top of the screen during gameplay; the one which invites player 2 to join in.
Also disabled the INSERTCOIN and PRESSSTART messages, not realizing that by accident I disabled any possibility of player 2 joining in. I'm going to save myself some time and work around this by forcing free play when the game reads DIP switches.
One thing I've found tricky when I worked on my last CPS1 game was figuring out how to tell in what game phase the program is. For example, if a modification is made to gameplay, then all code which implements said modification must only execute DURING actual gameplay; not during title screen, fighter select screen, continue screen, etc.
I knew that Capcom used the lower-end of the tile memory to place tiles relating to UI elements (score, messages, etc.). I watched that in the debugger to identify a memory location which changed in lockstep with the game clock. After some work, this lead to the design of an algorithm which can differentiate if the game is during combat or not. This is extremely useful, since any modification I make must be active exclusively during combat.
In summary, this is how I reached an algorithm to tell if the game is during active combat:
- identify tile memory location changing in lockstep with game clock
- that memory location then stores the clock tiles visible on screen
- watch clock tile memory to find instructions modifying it (ever tick)
- identify start of "write clock" routine (sometimes difficult because of variance in rts/bra reliance for returning)
- find call sites of routine, to identify higher-level consumer routines
- use discovered if/then nop shortcircuits to infer global variables which affect clock
- unveiled "clock freeze" variable, which freezes clock at 99 during "round preparation" and at whatever value it got to during "slow-motion defeat"
- therefore, game is during "active combat" when clock is being redrawn every frame AND clock freeze is off
In general, I want to develop algorithms which - based on heuristically-identified call trees - can yield with complete confidence the answer to the question "In what phase is the game?" - with answers such as "during round startup preparation", or "during active combat".
I don't like Capcom's liberal usage of jump tables. They make identifying routine call sites harder. I am, however, obliged to recognize my own use of jump tables for my custom ROM functions.
Elaborated algorithms to identify the following game phases: "round prepare", "active combat", "slowmo defeat".
Noped out a delay counter to remove demo mode (that exhibition-mode where the game shows you how to play). The result is that the game stays on the title screen forever, until the player starts. The reason for this removal is that it's easier to do than try to work out real rounds versus demo mode rounds.
Capcom made a good design decision to not sprinkle writes to scroll layer shift registers throughout the program. Instead, they store the shift amount (both X and Y) for each scroll layer in global variables. Every video frame they write those values once to the shift registers, at the start of the vblank interrupt handler.
Program now skips over delay at the top of building, on "street fight" intro screen.
Program now skips over "climbing upwards building", on "street fight" intro screen.
Additional information has shown that what I previously thought to be a fighter action history turned out to be the game's sounds queue! This explains why simple attacks (low, medium, high punches) all used the same entry in the history table - because they all sound alike ("woosh!").
Just like Knights of the Round, knowledge of the sound queue unlocks many doors. Relying on sound queue knowledge and the game's Test Menu to find the index of various sound (hadouken, stage music, etc.), one can find code paths pertinent to things like special attacks, menu actions, etc.
Few hours and patches later, I can now reliably identify the following additional game phases: "intro", "title screen".
When punched, the street fighter on the intro screen now also groans. I always thought Capcom should've put that in there themselves. After all, the guy falls down...
Removed the "HERE COMES A NEW CHALLENGER" and "PLAYER SELECT" messages, to gain a few more tile slots.
I'm trying to remove the name inscription (once a two-player game ends and the loser has a high score) from the UI and it's taking much longer than it should. I got it to about 90% of the way when I got an idea:
If I modify the built-in scores to be as high as possible, there is a VERY slim chance that any player will ever encounter the undesirable 10%...
... nobody cares about the score in Street Fighter, anyway.
They store the built-in ROM high scores in long (32bit) integers, in BCD encoding, so I can make them huge. Interestingly, Capcom stores 6 built-in high scores (lowest is 25,000), yet the high scores screen only displays top 5 scores (lowest is 30,000).
I found the palette storage space and worked out how to set individual colours. This coupled with knowledge of game phase "title screen" will allow me to create a nice animation of the big logo on the game's title screen.
A week's worth of work has paid off: by removing most of the UI shown during active combat, I've gained access to 14 tiles which are now never written to (thus causing flicker) during active combat by the program. These 14 tiles are now available solely to my modification(s).
To my surprise, elimination of most of the UI has freed up an additional 12 tiles in a nearby area. This brings the total to 26.
Tile starvation is a serious threat in this game. Capcom themselves re-worked some stage backgrounds from World Warrior to Champion Edition for this reason.
Figured out palettes and added a nice glowing effect to the big Street Fighter logo on the title screen.
I have ideas for a few modifications, but at least one of them will be about barrels. I wrote a function which displays animation frames of a breaking barrel. It is based on a helper function which can render tiles arranged in a rectangle in the tile storage area - e.g. that look "right" when viewed in Capcom's in-game tile viewer.
As I've been adding more functionality to the vblank interrupt handler, I've started to notice some stuttering in the music. Thankfully, I can hear the same stuttering when loading the original SF2CE ROM. It must be due to the MAME debugger window being active. Neither original nor modified SF2CE ROM stutters when the MAME is run normally (without the debugger).
Fixed a bug to make the contiguous tile drawing indeed generalized.
Wrote a routine which will animate a burning fire. I like the animation and I'm sure I'll find a use for it...
Figuring out animations (e.g. intro screen with the two street fighters) has been difficult. I want to modify them, and I found that their tile indices are stored in large arrays. I've made notes of which tiles are which, but there'd be too many to write down. Also, scrolling through the test menu's "object display" to see all tiles is very slow. To alleviate this, I recorded a video using MAME, of a scroll through all tiles in the game. This way, I can quickly seek through the close to 10,000 object tiles to find the ones I'm interested in.
Hid the white fighter from the intro screen. This taught me a new way to approach hiding something: turning tile pointer arrays into pointers to a blank tile. I'm going to keep the black fighter who gets hit. However, he will be hit by a falling barrel, not by the other fighter!
Capcom's poor division of game phases into tasks makes it harder to devise algorithms to determine game phase. For example, "select fighter" and "active combat" are implemented via the same task.
Worked out the identification of "select fighter screen" and "vs. screen" phases.
Wrote a random number generator based on controls, sound queue, player1/player2 state and certain video tiles in graphics RAM.
Most of the logic of my modification will be invoked at the end of Capcom's vblank interrupt handler. This way I'll have the chance to modify anything right after it's been set/created/modified by the original program. If any of my logic will need to happen BEFORE Capcom's, then I'll introduce another vblank interrupt handler hook - but that bridge need not be crossed at this time.
Created an overarching switch-case which allows quick insertion of logic based on the current game phase. For example, title screen and "select player" screen will display text containing the version name (that is, the name of my modified version) - while during combat, certain novel things will take place. (barrels falling down?)
I've generalized the barrel-drawing routine even further. Because I've been unable to find a large enough contiguous tile area to draw a barrel (which can be up to 20 tiles), the function takes in two pointers, allowing the caller to "split" the tiles into two different chunks. Tiles of barrels rendered during intro are located elsewhere than those of barrels rendered during combat - because different tile chunks are free during intro and combat.
I've begun work on a set of functions which will allow for creation of animated objects. This will allow me to create various "extra" things in-game, which will affect gameplay. The hard part is to make as much as possible of this code generalizable.
I now have something which can display a burning fire (tiles "borrowed" from those fires burning on the barrels bonus stage). I'd like to place them on the ground in "static" locations during gameplay, so I'm offsetting by the scroll layers' shift amount.
Fixed a bug in the object display function which, by not accounting for underflows after an offset by layer shift, would display an object in two different locations depending on the layer shift amount.
Generalized objects further, by factoring out animation. The general animation function now uses rendering function pointers stored in object state to reach specific rendering functions (e.g. fire, barrel)
Added an animation freeze feature to animatable objects. This will be useful for objects which fall down from the sky and break upon impact with ground or fighters
Added a total lifetime feature to animatable objects. This will be useful for objects which must be removed after a set amount of time.
Fixed a bug with animatable objects not being erased from the screen upon destruction.
Further improved animatable objects with additional callbacks. The domain is rich enough that I can now have fires which emanate "upward from the ground", rising up until they reach full height. They then remain at full height and continue to animate (flicker). This is accomplished with drawing increasingly more chunks of the fire tiles overlapped, stretching vertically as the "tip" moves towards its final destination - kind of like an accordion extending while one end is immobile.
More work on the barrel falling on the fighter during intro.
Came up with a way to determine when a game phase has JUST been entered. This is useful for single-fire events, such as the barrel on the intro.
Fixed a push/pop ordering bug which caused animated objects to split into multiple pieces at once, and sometimes crash the game.
Found a bug in the "is onscreen" determination, which is meant to keep objects from appearing in two spots at once, as the layers scroll horizontally. This one will take some experimentation to fix, but it's mandatory.
Fixed a horizontal underflow bug in "on screen" determination which caused animated objects to appear in two spots depending on scroll layer shift.
Figured out all screen limits (min/max X and Y) for animatable objects displayed during combat. This helps confine where objects can appear during gameplay.
I now have a barrel which falls down on the intro fighter's head. This was delicate because the following things had to work in concert: video frames per animation frame, barrel downward acceleration, synchronization of impact sound with fighter reeling animation, finding a good time to unfreeze animation - allowing barrel to animate into a crumple upon impact.
Fixed a bug in the object destruction function which caused object erasing callbacks to not be called.
Fixed a bug in the "destroy all objects" routine which sometimes caused the game to crash. Increased the defenses around loop counter registers in loops which invoke callbacks - to reduce future risk.
A structure has emerged in the main "callback" that I've set up: the vblank interrupt handler epilogue - which is hooked from right at the end of Capcom's vblank handler. I've built this structure to allow me to insert functionality anywhere in the game, based on observation and determination of game phases. It is a large switch-case statement with stuff like:
- begin intro screen
- begin intro screen first frame
- end intro screen first frame
- begin intro screen second and later frame
- begin intro screen second and later frame
- end intro screen
- begin title screen
- etc.
It is quite easy to insert for example a 1.1.1. to perform a task only on the first frame of the intro screen, such as initialize a countdown until the barrel falls on the fighter in the intro. This structure represents a crystallization of many hours of research into determining current game phase - though at a glance it looks quite obvious.
Fixed a strange bug which caused E. Honda's stage music to mysteriously start playing after a number of barrel falls. The issue was that I was calling Capcom's "sound enqueue" function to play the "barrel crash" sound while assuming that they preserved a certain address register. That wasn't the case, and it caused my continuation logic to inadvertently write to the game's sound queue values corresponding to E. Honda's stage music. The solution was to simply preserve one address register.
The title screen now has a falling barrel as well. Intro and title screens are now complete for "Barrels of Fun" version.
Fixed a bug in object erasing functions which prevented the bottom half of barrels from erasing properly.
Began work on barrels which appear during combat. At this point, they're dropping from the sky and crumpling upon impact with the ground. There's no collision detection with fighters yet, and I find them to be too hard to dodge - they might need a marker on the ground.
Collision with fighters is a bit tricky. I'm trying to find out to which part of the fighter fighter X refers. Some possibilities are:
- left-most tile of fighter
- centre
- left-most tile of fighter body (ignoring any outstretched limbs)
I decided to find this out experimentally by linining up a fighter with a perpetually-falling barrel, while I breakpoint to check both barrel and fighter X.
Through my experiments I'm looking at the difference between barrel X and fighter X - which I call delta X.
Ryu's X exhibits the same flat delta X to the barrel irrespective of whether he's facing left or right. (I'm testing both because most fighters are asymetrically wide towards the front versus back - Zangief is a good example, because his arms protrude forwards)
Zangief's X has a delta equal to Ryu's X delta when facing right, and a 0x20 higher delta X when facing left. This eliminates option 1.left-most tile of fighter.
So far, it looks like player X is not offset by layer shift, just like my own barrel X. This will make collision comparisons simpler.
My intuition tells me it's probably option 2.centre. The reason for this is that player X doesn't change when player facing direction changes.
I've gone through all fighters and "measured" their X deltas. The correct answer to the X delta/alignment question is option 3: left-most tile of fighter body. Alignment with fighter's body is reasonable from my measurements - that is, the limbs are ignored. In fact, fighter X in relation to fighter's body shape could even be arbitrary, since the game uses several hit boxes per fighter - which can be easily shifted/defined for each fighter separately.
There's too much variance in where the fighter's body actually is, in relation to the fighter's X coordinate. It varies with the direction in which the fighter is facing. For example, E. Honda's limbs protrude forward, so his body in relation to his (top-left corner) X varies.
This means that my single-box collision model needs to know which direction a fighter is facing, to shift the hitbox slightly.
Putting a memory "watchpoint" on a fighter's state is hopeless, because so many values change each video frame. I shifted towards a more conservative approach, where I watch chunks of memory that don't change often. Luckily, this led me to see which bytes change individually (that is, byte-writes, instead of word- or long-writes. This ultimately led me to the specific byte in fighter state which tracks the way the fighter is facing.
As an aside, I spent some more time watching fighter state memory and discovered some more useful things, such as some kind of a "phase" flag which tracks when fighter is crouching, standing, jumping, performing a special move. This single byte will be extremely useful to analyze how special moves are initiated.
Also, the fighter "phase" byte will be useful to fine-tune my collision detection with objects by accounting for crouching fighters. The reason for this is that Capcom kept the fighters' Y coordinate constant between crouching and standing. However, I want collision to occur "lower" if the fighter is crouching.
I've refined my single-box collision between objects and fighters to be based on a single rectangle around the fighter, which starts from Capcom's "fighter X" variable and is then adjusted for width, and positionally according to:
- which fighter it is
- whether the fighter is facing left or right
- whether the fighter is crouching
This model is important because it can serve any future modifications in which the player must avoid or collect objects.
Each adjustment has its own array, indexed by fighter type. My process is to use an infinitely-dropping barrel, and then play each fighter, while inching myself into the barrel, to see where the hit box should go - making pixel-level changes in the hit box adjustments array.
Figured out Ryu's horizontal adjustments. This means that horizontally, barrels now collide as intended with Ryu. I also got Ken for free.
This process is arduous, requiring many modify-compile-test cycles before a fighter is done. I use a save state with both players selecting the specific fighter whose hit box I'm adjusting. When I create the save state, I make sure the stage isn't India, because the elephants trumpeting has become annoying after hearing it so many times... I'm actually thinking of altogether noping out the elephants trumpeting on that stage.
Worked on the horizontal adjustments for all fighters. This took few hours, but the fighters's hit boxes are now finalized for the horizontal checks. Next will be the vertical collision, thus completing the object-to-fighter collision routines. These are generalized, and will thus be reusable for other types of objects I can think of.
For some reason, Capcom decided to invert in-game Y coordinates. This means that a fighter's Y increases upward on the screen. However, in the CPS1 graphics system, a tile's Y increases downward.
Vertical collision detection must take into account whether the fighter is crouching or not. Capcom decided to not change the fighter's Y while crouching, which means that I must find a way to discriminate between crouching and not crouching. The reason is obviously that collision must happen much lower on the screen when fighter is crouching.
I found a 2-byte value in memory which does track when the fighter crouches, attacks, etc. The bad news is that this value is the same for standing as well as crouching attacks. While I can clearly check whether the fighter is JUST crouching, I currently have no way to determine a crouch when fighter is attacking from a crouch.
Tried a byte value which seemed to act like a "freeze animation", but that proved unreliable.
Tried another byte value which seems to countdown a fighter's recovery from crouching to standing upon a momentary "tapping" of the down direction on joystick. Also unreliable... at least by itself. However, I found another byte which seems to mirror this one, perhaps a "last frame" state. Checking both of these and accepting one non-zero gives me a way to determine crouching with about 90% accuracy. This is sufficient for now, but I don't really like it. The "is crouching" function can be improved later.
I discovered a byte which tracks the joystick bitmap. I might be able to use the bit which tracks the "joystick down" switch to help with the determination of "is fighter crouching".
Added a barrel shadow on the ground, to serve as a visual cue to the barrel's trajectory.
Added a screen shake effect upon barrel impact. It is implemented by simply shifting all layers by few pixels every few frames.
I initially designed object state to represent width/height in tiles. I've since changed that to represent width/height in pixels, to simplify collision detection.
When a fighter crouches, the collision detection must be adjusted, to account for the fighter appearing "shorter" during the crouch. Initially, I was shifting the object vertically right before the collision check. This proved to be a poor approach, because objects that are BENEATH the fighter (such as when fighter is jumping over fire) would register a false collision. As a result, I changed the collision check code to instead "shrink" the fighter vertically, while pushing fighter's vertical location down.
Collision detection now works well, for objects from above and below, regardless of crouching or not. For ground objects, I've used fires which remain burning on the ground.
Problem: E. Honda can't jump over fires, which are 3 tiles tall.
Solution: introduce the concept of padding on objects, which effectively reduces the height of objects during collision detection. E. Honda can now clear a fire while jumping forward, but it's close...
Sagat also had this problem; his jumps are very high but very short. Jumping over fires is just a bit more difficult with fighters whose jumps are short.
Finished adjusting collision with fire for all fighters.
Street Fighter II Champion Edition still has the tiles which spell out "The World Warrior" as a title graphic. Capcom left them in there.
Since any modifications I make will have some kind of impact on fighters' life (e.g. barrel hits them and take away life), I am writing routines for manipulating fighter life meter. All fighters begin rounds with 144 life (0x90). However, the fighters don't lose at 0 life, so they, in fact, have 145 life.
I found the if-then-else structure which detects when a player wins by knockout or double knockout. This can lead to the routine which knocks a player down, which could be useful.
To figure out a way to knock a fighter down, I started from the code which determines a winner, based on fighters' remaining life. After about 2 hours of fruitless analysis, I changed course.
I now looked at the death yell sound effect, because that is enqueued ONCE, on the first frame of the determination that a fighter has died.
It didn't take long to realize that this path is flawed. This code is likely more complex than just "fall down", because the round has just ended, as well. Not to mention the "slowmo".
I finally discovered it. I can now reliably throw fighters to the floor. This is essential for the barrel impact to "feel" powerful, by way of the fighter being thrown to the ground. Not only that, but it takes a convenient argument to indicate direction of knockdown (left or right). I'm happy I foresaw the utility of this, and made my own collision code return relative positioning between fighter and object.
The only issue with Capcom's knockdown routine is that it also applies damage that varies based on yet-unknown state variables. This damage has to be removed, so that I can apply my own, predictable damage from my custom logic. From extensive experimentation, it seems like the "built-in" knockdown damage is either 7 or 8. To this, I've added enough damage to make it hit as hard as Ken's strong kick throw. I accepted the 1 damage point inconsistency, whose elimination would cost at least one hour of work.
Added a blinking indicator above fighters' head when taking fire damage.
Fighters now continually yell when damaged by fire.
A cool effect is to be had if a barrel drop would collide with both fighters, but one crouches - the other fighter gets it! You don't have to outrun the bear; you have to outrun your "friend".
Added a sound effect which informs that a fire is spawning. I was unable to find a suitable one, so I just opted for Dhalsim's voice saying "fire!". Interestingly, his "yoga fire" and "yoga flame" sound effects are in fact stored as two halves, where one of the second halves is "fire".
Through a lucky accident, I figured out how to flip a tile. Horizontal and vertical flipping are done via two bits in the palette word. The accident was I forgot to set a register which holds the palette, so the code ran with an arbitrary value which caused tile flipping.
For a long time, I was able to rely on save states to quickly test my code changes. But after some time, they mostly stopped working, causing machine resets upon loading state. For some time I was confused as to why it happened, since the structure of RAM (variable offsets, etc.) didn't change. After a while, I realized that when objects are created, pointers to ROM callback functions are stored in RAM (e.g. "on fire update", "on barrel animation advance"). As I was modifying the ROM (via my code changes), I was changing the addresses of ROM functions, then loading a save state whose RAM held a pointer to (now) garbage.
Created modifications for intro screen and title screen for the fire version.
Barrel sounds are incessant and loud, so I added an option to disable barrel sounds from the title screen.
Implemented functionality to disply gauges for p1 and p2. These can, for example, display how many barrels each player has available to launch. Barrels themselves take up many tiles, so the gauges had to share few tiles with the barrels. This resulted in some refactoring and tolerance of flickering of the gauges during barrel flight.
Fixed a bug whereby certain gauge tiles were not cleared properly upon the resource being spent.
Implemented functionality for a new kind of barrel, which is launched by either fighter by pressing the start button. There are limited barrels that can be launched, and its impact target area is the opposite fighter.
The first two releases: Barrels of Fun (random barrels) and Aimed Barrels (targeted barrels) are now ready for release.
A third release is ready: Burning Fires (up to 4 persistent, random fires).
Wrote functions which create and govern a fire serpent - a moving row of several fires, which spawns progressively - appearing to grow.
A fourth release is ready: Fire Serpent (row of several fires appears in the middle and then starts to move left or right, alternating).
I'm working on a new release, whereby players are able to spawn fires underneath the enemy's feet. One difference to Aimed Barrels is that multiple fires can exist at the same time. I've written functions which limit active fires to one per player, so one player doesn't just spam it, taking up all available object slots.
Fixed a bug whereby aimed fire gauges didn't clear (visually) after winning a round. Also ported this fix to Aimed Barrels.
A fifth release is ready: Aimed Fires, in which players can create fires under the enemy's feet. Unlike Aimed Barrels, which allows one falling barrel at a time, this release allows one created fire PER PLAYER. The fires are temporary and disappear after a short time. I decided on this because I didn't want this version to be too similar to the version with random, persistent fires.
I'm now working on a release which has to do with catching birds which fly through the air. The first player to catch a certain number of birds wins.
If bird catching is to be the most important goal, perhaps fighters should continually gain life.
I developed a nice-sounding way of making the bird tweet by repeatedly playing a certain sound effect.
Improved tweeting function to force a tweet right when the bird spawns, to make players aware it is spawning, although it may be offscreen (due to screen having scrolled far left or right).
I want the bird version to centre around catching birds, and not fighting. Of course, the fighting is what keeps the enemy AWAY from the bird you're trying to catch! One way to do this would be to make all hits cause 0 damage. From what I've been seeing, there are several sites for this, so it would require significant analysis. Another option is to cause fighters' lives to continually increase every frame; this has 2 advantages: 1. it's simple to implement, 2. it's visual, so it'll be clear that it's the intention that fighters can't kill each other.
One edge case here is running out of time, which in the original program considers life left to determine a winner - which I don't want. My choices here are: 1. freeze round clock forever, 2. do a check at round clock 1s and kill player with fewer birds.
Birds tweeting throughout their lifetime has quickly become annoying to me. I'm going to restrict the tweeting to only their descent - and not to their horizontal flight.
Another issue with these birds is palette. A light-coloured bird is hard to see on stages such as E. Honda's, and a dark-coloured bird is hard to see on stages such as Ryu's. I solved this by making the bird flash - that is, alternate between two colours.
With 8 birds that players must catch, it was getting a bit crowded in the "usual" display area (right underneath the life bar), so I moved the bird progress to above the life bar.
I've decided to freeze the round clock for the bird modification. Thus, the game will last until enough birds are collected.
I found the perfect sound effect to use for when a player catches a bird: the announcer saying "perfect".
Poor E. Honda really was dealt bad cards by Capcom. He's the only fighter who - at the moment - cannot catch birds when jumping forward. He's simply not tall enough and doesn't jump high enough. Surprisingly, when jumping backward, he CAN catch birds.
Just for the bird modification I had to make E. Honda's hitbox taller and wider for him to reliably catch birds.
I don't like the fact that if a fighter becomes cornered, the cornering fighter can monopolize the birds as the come in from behind the cornering player (that is, from the middle of the fighting area). I found a better solution, whereby the bird spawns midway between the two fighters (still flying in alternating directions horizontally). This forces the cornering fighter to jump, potentially giving a momentary advantage to the cornered fighter.
To make it a bit more interesting, the bird now randomizes a small horizontal offset from midway between the fighters when spawning.
A sixth release is ready: Bird in Hand, in which the fighters must catch birds to win rounds.
The next release will involve no extra mechanics, but will allow attacks to leech back life. I've made the leech percentage configurable from the title screen.
Leeching is based on a simple shift right of the damage amount; a shift by 2 (~25%) or by 1 (~50%) yields approximate amounts (e.g. not exactly 50% when damage done is numerically odd), so I had to denote the "vampiric amount" by low, medium, and high on the title screen.
I came up with a wacky idea for the intro: the white fighter remains hidden. A vampire slides in from the side and reaches the black fighter exactly when he gets hit. I built the vampire out of M. Bison's cape (2 animation frames) and Ryu's turned-away head from his ending sequence (where he walks away in the distance).
A seventh release is ready: Vampiric, in which the fighters leech back life upon dealing damage.