System(s) |
Playstation, PSP, PSN (POPS) |
Bug Type | Gameplay |
Region Introduced | |
Patch Version | 2.01.065b |
The final battle with Luca Blight is one of the most difficult fights in the game. Luca can attack three times per turn. He has a three-hit combination with his sword that does enormous damage to one person, a special move that attacks one column of the party for high damage with a fire element bonus, and a second, nearly identical special move that hits one row for slightly less damage per person. Luca can easily mow down most parties, unless the player prepares thoroughly, or invests a fair amount of time leveling up characters. However, the bug can be exploited for a ridiculously easy win.
In this situation, where a special module must manage three parties, the bug is that the programmer assumed that no player in their right mind would form anything less than a full party to take on Luca, and they would certainly never let the Hero go alone. It's dangerous. Normally, when even a single character is removed, the party is reorganized so that the front row is filled, and any empty spots are shifted to the back row. There is a convenient function that does all this work. It is simply not called by the special module after emptying the Hero's party. Viktor's party, and Flik's party are both initialized with leaders in the first position, so they cannot be exploited.
The code below is responsible for emptying the Hero's party. It resides in /CDROM/140_HONP/PARTYCE1.BIN. In order to reorganize the party, a single function can be called that resides in the main executable, and is loaded at 0x80073894. After emptying a party, this function would simply shift the hero to the first position. There are also script commands, and code in other modules that similarly empty the party. Had they been used, instead of writing a duplicate routine, the bug would not exist.
RAM:80110C00 move $s0, $0 RAM:80110C04 li $s1, 0x80072F04 # Routine: GetIDForPartyPos RAM:80110C0C li $s5, 0x80072844 # Routine: MP_CharaRecStat RAM:80110C14 li $s3, 0x8007344C # Routine: RemoveFromParty RAM:80110C1C sb $0, 2($s4) RAM:80110C20 RAM:80110C20 loc_80110C20: # CODE XREF: RAM:80110C78j RAM:80110C20 sw $s1, -0x5304($s2) RAM:80110C24 jalr $s1 RAM:80110C28 move $a0, $s0 RAM:80110C2C beqz $v0, loc_80110C70 RAM:80110C30 nop RAM:80110C34 sw $s1, -0x5304($s2) RAM:80110C38 jalr $s1 RAM:80110C3C move $a0, $s0 RAM:80110C40 li $v1, 1 RAM:80110C44 beq $v0, $v1, loc_80110C70 RAM:80110C48 move $a0, $s0 RAM:80110C4C jalr $s1 RAM:80110C50 sw $s1, -0x5304($s2) RAM:80110C54 li $a0, 0x26 RAM:80110C58 move $a1, $v0 RAM:80110C5C jalr $s5 RAM:80110C60 sw $s5, -0x5304($s2) RAM:80110C64 move $a0, $s0 RAM:80110C68 RAM:80110C68 CallRemoveFromParty_00: # After this, it should call 0x80073894 - ReformPartyOnExit RAM:80110C68 jalr $s3 RAM:80110C6C sw $s3, -0x5304($s2) RAM:80110C70 RAM:80110C70 loc_80110C70: # CODE XREF: RAM:80110C2Cj RAM:80110C70 # RAM:80110C44j RAM:80110C70 addiu $s0, 1 RAM:80110C74 slti $v0, $s0, 6 RAM:80110C78 bnez $v0, loc_80110C20 RAM:80110C7C li $a0, 0x25 RAM:80110C80 li $s0, 0x80072844
; Suikoden II Luca Bug/Party Change Fix ; Written by Pyriel ; .psx .align 4 ; all the $s registers are pretty much fair game. ; Using $v1 for address (already loaded). .openfile PARTYCE1.BIN, 0x8010DC50 .headersize 0 .org 0x80110C20 ; file location will be 0x2FD0 .area 0x80110C80-. ; replace loop that empties party loop: jalr $s1 move $a0, $s0 beqz $v0, continue nop jalr $s1 move $a0, $s0 li $v1, 1 beq $v0, $v1, continue move $a0, $s0 jalr $s1 nop li $a0, 0x26 jalr $s5 move $a1, $v0 move $a0, $s0 jalr $s3 ; RemoveFromParty After this, it should call 0x80073894 - ReformPartyOnExit nop continue: addiu $s0, 1 slti $v0, $s0, 6 bnez $v0, loop nop jal 0x80073894 nop li $a0, 0x25 .endarea .close