the cutting room mirror

Final Fight 2

This article has a talk page!
view on tcrf.net

This page contains notes for the game Final Fight 2.

Addresses on this page correspond to the USA version unless noted otherwise.

Breakpoints#

80811A, execute - accumulator holds start addresses of palette (+830000)
8180B1, execute - accumulator holds sound effect index to play

(Source: JLukas)

Enemy List#

These Pro Action Replay codes can be used to change the first two enemies that appear at the beginning of the first stage. Presumably, U.Andore and F.Andore would've used values 0603, 0604, respectively, but no slots are reserved for them.

84C265xx 
84C266xx 

84C26Dxx 
84C26Exx 

Alternately, change the following values directly in a hex editor:

00024265
00024266

0002426D
0002426E
Enemy Index, Name
0300 Mic 
0301 Schot 
0302 Bull 
0303 Mark 
0304 Jack 
0305 Elijah 

For the group above, add 10 to the second byte to make the enemy spawn in a "Kneeling, facing right" position. Add 20 to make them spawn "Kneeling, facing left". As two examples, the codes for the first two kneeling enemies early in stage 1-1 are 0310 (Mic kneeling facing right) and 0323 (Mark kneeling facing left)

Enemy Index, Name
0400 Elick 
0500 Atlas 
0501 Jony 
0600 Andore Jr. 
0601 Andore 
0602 G.Andore 
0700 Leon 
0701 Robert 
0800 Eliot 
0801 Elias 
0900 Joe 
0A00 Won Won 
0B00 Freddie 
0C00 Bratken 
0D00 Philippe 
0E00 Rolent 
0F00 Retu 

(Source: Emptyeye (The direct addresses to change; info about Group 3 kneeling spawns), JLukas (Everything else in the section))

Enemy Names#

The enemy names are stored in the chunk of memory beginning at 00016A4B and ending at 00016B9A when viewed in a hex editor. The data for a given enemy is 12 bytes long--the first byte is presumably a pointer to the enemy icon, but this isn't known for sure. Then there's white space, and finally the enemy name. In other words, the enemy names are left-padded with spaces. Note that the first three entries are currently not known as to what they actually are (Possibly encrypted data for displaying the player character names/icons?). "Plaintext" enemy data, therefore, begins at 00016A6F.

For example, to change Mic's name to Michael, you would change the row of data found at 00016A70:

20 20 20 20 20 20 20 20 4D 49 43 04 20 20 20 20 |        MIC.    |

to:

20 20 20 20 4D 49 43 48 41 45 4C 04 20 20 20 20 |    MICHAEL.    |

Do not change it to the below. More accurately, while you can change it to the below, you won't get the results you expect--Mic will still be called Mic, while Schot won't have a name and will have gibberish for an icon.

20 20 20 20 20 20 20 20 4D 49 43 48 41 45 4C 04 |        MICHAEL.|

(Source: Emptyeye)

Player Character Stats#

The following addresses correspond to the statistics for the three playable characters, their starting health, and their attacks. Numbers in parentheses in the comments are the corresponding decimal values.

When viewed in a hex editor, the corresponding beginning address is 00016B9B.

82EB9B  8D  
82EB9C  00  //Maki's starting/maximum health. 2 bytes, lower byte first (141)
82EB9D  00  //Maki's defense (00)
82EB9E  2E  //Maki's Extra Joy damage (46)
82EB9F  1E  //Maki's knife damage (30)
82EBA0  1E  //Maki's tonfa damage (30)
82EBA1  14  //Maki's 2X4 damage (20)
82EBA2  08  //Damage of Maki's first knee after grabbing an enemy (8)
82EBA3  0E  //Damage of Maki's second knee after grabbing an enemy (14)
82EBA4  18  //Damage of Maki's third knee after grabbing an enemy (24)
82EBA5  1C  //Damage of Maki's throw (28)
82EBA6  08  //Damage taken by an enemy "catching" a thrown enemy (8)
82EBA7  06  //Damage of Maki's basic jab (6)
82EBA8  08  //Damage of the third hit in Maki's combo (8)
82EBA9  0C  //Damage of the fourth hit in Maki's combo (12)
82EBAA  14  //Damage of the last hit in Maki's combo (20)
82EBAB  0C  //Damage of Maki's "standing" jump kick (12)
82EBAC  08  //Damage of Maki's "Down+Attack" in the air (8)
82EBAD  0E  //Damage of Maki's "flying" jump kick  (14)

82EBAE  8D
82EBAF  00  //Haggar's starting/maximum health. 2 bytes, lower byte first (141)
82EBB0  00  //Haggar's defense (00)
82EBB1  24  //Haggar's Extra Joy damage (36)
82EBB2  1E  //Haggar's knife damage (30)
82EBB3  1E  //Haggar's tonfa damage (30)
82EBB4  14  //Haggar's 2X4 damage (20)
82EBB5  0A  //Damage of Haggar's first headbutt after grabbing an enemy (10)
82EBB6  10  //Damage of Haggar's second headbutt after grabbing an enemy (16)
82EBB7  24  //Damage of Haggar's third headbutt after grabbing an enemy (36)
82EBB8  24  //Damage of Haggar's suplex (36)
82EBB9  08  //Damage taken by an enemy "catching" a suplexed/piledriven enemy (8)
82EBBA  28  //Damage of Haggar's spinning piledriver (40)
82EBBB  12  //Damage of Haggar's basic jab (18)
82EBBC  12  //Damage of the last hit in Haggar's combo (18)
82EBBD  14  //Damage of any of Haggar's flying attacks (20)
82EBBE  08  //?? Unknown at this time

82EBBF  8D
82EBC0  00  //Carlos's starting/maximum health. 2 bytes, lower byte first (141)
82EBC1  00  //Carlos's defense (00)
82EBC2  24  //Carlos's Extra Joy damage (36)
82EBC3  1E  //Carlos's knife damage (30)
82EBC4  1E  //Carlos's tonfa damage (30)
82EBC5  14  //Carlos's 2X4 damage (20)
82EBC6  09  //Damage of Carlos's first knee after grabbing an enemy (9)
82EBC7  10  //Damage of Carlos's second knee after grabbing an enemy (16)
82EBC8  1C  //Damage of Carlos's third knee after grabbing an enemy (28)
82EBC9  1C  //Damage of Carlos's throw (28)
82EBCA  08  //Damage taken by an enemy "catching" a thrown enemy (8)
82EBCB  0A  //Damage of Carlos's basic jab (10)
82EBCC  0A  //Damage of the third hit in Carlos's combo (10)
82EBCD  10  //Damage of the last hit in Carlos's combo (10)
82EBCE  0C  //Damage of Carlos's "standing" jump kick (12)
82EBCF  08  //Damage of Carlos's "Down+Attack" in the air (8)
82EBD0  0E  //Damage of Carlos's "flying" jump kick  (14)

(Source: Emptyeye)

Enemy Stats#

The enemy statistics begin immediately after the above player statistics (Viewed in a hex editor, beginning at 00016BD1). The format is as follows:

-The life of the "group" of enemies, in two bytes (Smaller byte first), for each of 32 potential "internal" difficulty levels. This takes up a total of 64 bytes.
-The defense of the group, taking up 32 bytes, one for each potential internal difficulty.
-Numerous groups of 32 bytes, presumably attack values for that group's various attacks.

The beginning addresses for each group are the following locations. Group numbers correspond to the first byte in the "Enemy Index" section above:

Group 03 (Mic/Schot/Bull/Mark/Jack/Elijah): 82EBD1
Group 04 (Elick): 82ECD1
Group 05 (Jony/Atlas): 82EDB1
Group 06 (Andore Jr./Andore/G.Andore): 82EEB1
Group 07 (Leon/Robert): 82EFF1
Group 08 (Elias/Eliot): 82F111
(Group 09, the Joes, do not seem to appear in this block. Perhaps their data is all hard-coded elsewhere)
Group 0A (Won Won): 82F1D1
Group 0B (Freddie): 82F2F1
Group 0C (Bratken): 82F451
Group 0D (Philippe): 82F5D1
Group 0E (Rolent): 82F6B1
Group 0F (Retu): 82F791

(Source: Emptyeye)

The Ranking System#

This needs some investigation.
Discuss ideas and findings on the talk page.
Specifically: Research the difficulties that aren't normal to confirm they work the same way. Also figure out how the Joe enemies work--the game handles them completely differently than every other enemy/enemy group, but the specifics are unknown right now.

An arcade-style ranking system is used in the game in at least some capacity--each difficulty adjusts starting rank, maximum rank, and how quickly rank increases. Further, the game checks rank every time damage is given or received. Funnily, though, on Normal difficulty, only one enemy in the whole game is definitely affected by this rank (Philippe, the boss of Stage 4), and that one enemy always has 0 defense regardless of their rank!

The ranking system itself is similar, though not identical, to Final Fight SNES's ranking system. There are a total of 32 possible ranks, from 00 to 31 (1F). Every so often, the rank increases--again, how fast is determined by the difficulty.

Where Final Fight 2 differs from its predecessor is in how rank is decreased. There's no decrease on losing a life. Instead, the rank reverts to the starting rank of the chosen difficulty each time you clear a section of a stage (A section being defined as the screen fading out and back in).

The code that checks whether or not to increase the rank each frame is below. In a hex editor, this code begins at 000055B2:

$80/D5B2 C2 20     REP #$20    //Set status to 16-bit
$80/D5B4 AD D6 AD   LDA $ADD6   
$80/D5B7 1A     INC         //Increment frame counter (Stored in 7EADD6) 
$80/D5B8 CD D4 AD   CMP $ADD4   //Compare to 7EADD4 (How often to increment rank [Every X frames])
$80/D5BB 90 16      BCC $D5D3   //SKIP rank incrementing/zeroing out frame count if NOT time to increment
$80/D5BD E2 20      SEP #$20    //Set status to 8-bit
$80/D5BF AD D8 AD   LDA $ADD8   
$80/D5C2 1A     INC     //Otherwise, load and increment rank
$80/D5C3 CD D3 AD   CMP $ADD3   
$80/D5C6 90 03      BCC $D5CB   
$80/D5C8 AD D3 AD   LDA $ADD3   //If necessary (IE new rank is > max allowed rank), set rank to max allowed rank.
$80/D5CB 8D D8 AD   STA $ADD8   //Store new rank
$80/D5CE C2 20      REP #$20    //Set status to 16-bit
$80/D5D0 A9 00 00   LDA #$0000  //zero out frame counter (If necessary--only after incrementing rank)
$80/D5D3 8D D6 AD   STA $ADD6   //store new frame counter value
$80/D5D6 60         RTS         //Return

(Source: Emptyeye)

Starting, Maximum, and Change Frequency of Rank#

The below table shows the memory for where the values of the starting rank, maximum rank, and frequency of change of rank is stored in the ROM. Viewed in a hex editor, the corresponding beginning address is 0001D872. Decimal conversions of the values are provided in parantheses at the end of the comments.

83D872 00  //Easy starting rank (0)
83D873  17  //Easy maximum rank (23)
83D874  03  //Normal starting rank (3)
83D875  19  //Normal maximum rank (25)
83D876  05  //Hard starting rank (5)
83D877  1B  //Hard maximum rank (27)
83D878  07  //Expert starting rank (7)
83D879  1D  //Expert maximum rank (29)
83D87A  58 02   //Easy rank change frequency (600) (10 seconds)
83D87C  03  
83D87D  02  //?? These two bytes are unknown at this time. 
83D87E  58 02   //Normal rank change frequency (600) (10 seconds)
83D880  02  
83D881  01  //?? These two bytes are unknown at this time. 
83D882  E0 01   //Hard rank change frequency (480) (8 seconds)
83D884  01  
83D885  01  //?? These two bytes are unknown at this time. 
83D886  2C 01   //Expert rank change frequency (300) (5 seconds)
83D888  01  
83D889  00  //?? These two bytes are unknown at this time

The placement of the unknown bytes, and the values of the first unknown byte in each set, suggest either that the rank system was at one point meant to work like Final Fight SNES, IE where rank would slightly decrease on losing a life, or that Capcom just kept the leftover rank code from Final Fight SNES rather than completely cleaning it up. Rank does not decrease on losing a life in Final Fight 2.

Also notice that the maximum rank on Expert difficulty is 29, although enemies can be (And are--at least one boss on Expert difficulty is hard-coded with a rank of 31) hard-coded with ranks higher than that. More generally, enemies can be hard coded with a rank higher than the maximum rank of the difficulty.

(Source: Emptyeye)

The Damage Formula#

Determining damage to an enemy is pretty simple:
-Take the value of the attack in the "Player Character Stats" section above.
-Multiply that value by (32 - enemy defense)
-Divide that result by 32
-Round down, if necessary

Note that this formula is also used for determining damage from an enemy to the player. However, since all three player characters have a defense value of 00 (See the Player Character Stats section above), this simplifies to always being the value of the enemy attack.

(Source: Emptyeye)

Display 8 or 16-bit value#

The programming to display an 8-bit hex value is used (by the continue countdown timer in 2P mode when one player runs out of lives), but not the 16-bit part. No pattern matches for 22468580 (JSL $808546) were found.

Display 16-bit value
$80/8546 E2 20       SEP #$20
$80/8548 48          PHA                     //Push the upper 8 bits to display later
$80/8549 EB          XBA
$80/854A 22 51 85 80 JSL $808551[$80:8551]   //Display 8-bit value
$80/854E E2 20       SEP #$20
$80/8550 68          PLA                     //Pull the upper 8 bits
Display 8-bit value
$80/8551 48          PHA
$80/8552 C2 30       REP #$30
$80/8554 29 F0 00    AND #$00F0              //Keep only upper 4 bits (first digit)
$80/8557 4A          LSR A
$80/8558 4A          LSR A
$80/8559 4A          LSR A
$80/855A AA          TAX
$80/855B BF 7F E4 82 LDA $82E47F,x           //Get tile index
$80/855F 99 3E 22    STA $223E,y             //Store to tilemap
$80/8562 C8          INY
$80/8563 C8          INY
$80/8564 E2 20       SEP #$20
$80/8566 68          PLA
$80/8567 C2 20       REP #$20
$80/8569 29 0F 00    AND #$000F              //Keep only lower 4 bits (second digit)
$80/856C 0A          ASL A
$80/856D AA          TAX                     //
$80/856E BF 7F E4 82 LDA $82E47F,x           //Get tile index
$80/8572 99 3E 22    STA $223E,y             //Store to tilemap
$80/8575 C8          INY
$80/8576 C8          INY
$80/8577 6B          RTL

Tile Index Values#

82E47F 3028 3128 3228 3328 3428 3528 3628 3728 0(1(2(3(4(5(6(7(
82E48F 3828 3928 4128 4228 4328 4428 4528 4628 8(9(A(B(C(D(E(F(

(Source: JLukas)

Same Player Cheat#

$80/A886 BD A4 AD    LDA $ADA4,x             //Load the set of player button presses starting at 7EADA4
$80/A889 DF D6 E4 82 CMP $82E4D6,x           //Compare to stored button presses table starting at 82E4D6
Success
$80/A895 A9 00 4C    LDA #$4C00              //Set background to blue
$80/A898 8D 3E 20    STA $203E  [$7E:203E]
$80/A89B E2 20       SEP #$20
$80/A89D EE CD AD    INC $ADCD  [$7E:ADCD]   //Turn on same player cheat

(Source: JLukas)

Stored Button Presses Table#

82E4D6 0004 = down
82E4D8 0004 = down
82E4DA 0008 = up
82E4DC 0008 = up
82E4DE 0001 = right
82E4E0 0002 = left
82E4E2 0001 = right
82E4E4 0002 = left
82E4E6 2000 = L
82E4E8 1000 = R

(Source: JLukas)

Palettes#

Copy $20 byte palettes from ROM to RAM. The starting ROM address for palettes is $83F560
$80/8117 69 60 F5    ADC #$F560
$80/811A AA          TAX
$80/811B A9 1F 00    LDA #$001F
$80/811E 54 7E 83    MVN 83 7E

(Source: JLukas)

Palette Offsets#

Offset, Enemy index, Palette index, Name

83F560 ----     Maki
83F580 ----     Haggar
83F5A0 ----     Carlos
83F5C0 0300     Mic
83F5E0 0301     Schot
83F600 0302     Bull
83F620 0800     Eliot
83F640 0400     Elick
83F660 0500     Atlas
83F680
83F6A0 ----     Drumcan
83F6C0
83F6E0 0600 $0C Andore Jr.
83F700 0601 $0D Andore
83F720 0602 $0E G.Andore
83F740 0801     Elias
83F760 0A00     Won Won
83F780
83F7A0
83F7C0
83F7E0
83F800 ----     (yellow border around character portrait on character select screen)
83F820 0303     Mark
83F840 0304     Jack
83F860 0305     Elijah
83F880
83F8A0
83F8C0
83F8E0
83F900
83F920 0501     Jony
83F940
83F960
83F980
83F9A0
83F9C0
83F9E0
83FA00
83FA20
83FA40
83FA60
83FA80
83FAA0 0E00     Rolent
83FAC0
83FAE0
83FB00
83FB20
83FB40
83FB60
83FB80
83FBA0 ----     Bottle
83FBC0
83FBE0
83FC00
83FC20
83FC40
83FC60
83FC80
83FCA0
83FCC0
83FCE0
83FD00
83FD20
83FD40
83FD60 0900     Joe
83FD80 0700     Leon
83FDA0 0701     Robert
83FDC0 ----     Maki (same player 2P mode, blue)
83FDE0 ----     Haggar (same player 2P mode, blue)
83FE00 ----     Carlos (same player 2P mode, orange)
83FE20 ----
83FE40 ----     (identical to 83FE20) - A duplicate set is found in the Japanese version (ie, it has 4 copies total of this palette) This may be unused in the USA version.
83FE60-83FFFF unused space ($01A0 bytes)

(Source: JLukas)

Music Pointers#

In a hex editor, the following values correspond to the music indexes for various points in the game. To change the track that loads, change these values. The values correspond to their value in the sound test plus 10 --in other words, if you want to make music 03 play in an area, use 13 as the value. For song 0E, use 1E. And so on.

A byte with 9X means "continue playing song X from the previous section". You'll see this in the pointers for rounds 2-2 and 2-3, 3-2, 5-2, and 5-5.

3A2E   Capcom screen
2BBD    First intro song
2F29    Second intro song
5C41    Character select
56F6    "Intro to Round 1"
14435   Round 1-1
24140   Round 1-2
24141   Round 1-3
14667   Round clear for Round 1 through 5
24142   Round 2-1
24143   Round 2-2 (As mentioned above, "9X" means "continue playing song number X from the previous section")
24144   Round 2-3
E4C9    Bonus Round (Both of them)
24146   Round 3-1
24147   Round 3-2
24148   Round 4-1
24149   Round 4-2
2414B   Round 5-1
2124C   Round 5-2 
2414D   Round 5-3 (Which is a brief cutscene and nothing else)
2414E   Round 5-4 
2414F   Round 5-5
24150   Round 6-1
24151   Round 6-2
24152   Round 6-3
24153   Round 6-4
26EA9   Round 6-5
115CF   Round 6 clear
4398    Ending theme
61F7    Continue screen

(Source: Emptyeye)

Continue Screen#

This is very incomplete.

Water Rising#

$80/E710 E2 20     SEP #$20
$80/E712 E6 7E      INC $7E
$80/E714 A5 7E      LDA $7E
$80/E716 C9 0A      CMP #$0A
$80/E718 90 18      BCC $E732 //If incrementer less than 10, DO NOT raise water (incrementer increases every frame)
$80/E71A 64 7E      STZ $7E   //Otherwise, zero out incrementer (Located at 0019CB) and proceed.
$80/E71C AD 0E D6   LDA $d60e
$80/E71F C9 00      CMP #$00
$80/E721 D0 0F      BNE $E732 //Compare 7ED60E to 0 and DO NOT raise water if NOT equal to 0. This seems to get set to something 
                  //besides 0 once the "Game Over" begins. 
$80/E723 C2 20      REP #$20
$80/E725 AD 49 AB   LDA $AB49
$80/E728 C9 B0 FF   CMP #$FFB0
$80/E72B B0 05      BCS $E732 //Compare current water height (Located at 7EAB49) to 176. If greater than or equal to 176, DO NOT 
                  //raise it.
$80/E72D EE 49 AB   INC $AB49 //Otherwise, we've passed all the checks, so raise the water one pixel and continue.
$80/E730 80 00      BRA $E732

(Source: Emptyeye)

Timer Counting Down (One Player)#

$80/E381 E2 20         SEP #$20 
$80/E383 E6 7F      INC $7F 
$80/E385 A5 7F      LDA $7F 
$80/E387 C9 3C      CMP #$3C 
$80/E389 90 15      BCC $E3A0 //If incrementer less than 60, DO NOT decrement timer (incrementer increases every frame)
$80/E38B 64 7F      STZ $7F   //Otherwise, 0 out incrementer (located at 00107F) and proceed.
$80/E38D E2 08      SEP #$08
$80/E38F A5 7E      LDA $7E
$80/E391 38     SEC
$80/E392 E9 01      SBC #$01
$80/E394 85 7E      STA $7E   //Decrease timer (Located at 7E107E in RAM)
$80/E396 C2 08      REP #$08
$80/E398 30 74      BMI $E40E //If negative flag (Countdown at -1), jump to Game Over routine
$80/E39A A9 41      LDA #$41 
$80/E39C 22 B1      JSL $8180B1 //Otherwise, load 65 to A and jump to 8180B1

(Source: Emptyeye)