Turn order, double attacks and kicks
Four oddities players have reported, traced to the game's own code: the Spikefish Flute cancelling enemy turns (the Koru flute glitch), the War God Icon sometimes giving one attack string where two are expected, Side Kick missing floating enemies even with War God Boots, and a level-3 dance special swallowing the next one. Each section says what happens in plain words first, then gives the addresses so other reverse engineers can check it.
SCUS_942.54) unless a table says otherwise; ctx means the battle context block, found through the pointer at 0x8007BD24.
The Spikefish Flute cancels enemy turns (the Koru flute glitch)
- Reported as
- In a boss fight the flute skips the party's remaining turns, and for every party member who already acted it also cancels an enemy turn, slowest enemy first. Against Koru it can stop Dead End Crisis for good (community report).
- Cause
- The flute's turn-skip adds one to the round's turn count per living party member instead of per member still waiting, so the round ends early
- Where
- Spikefish cast module, PROT entry 0925, tail at
0x801F79B8; round-over test in the battle overlay, PROT entry 0898, at0x801E67C8 - Affects
- Every fight flagged as scripted (boss fights), on the USA, Japanese and French discs; only Koru's script can be pushed off its end
- Status
- Explained - from the code and disc data; the early round end and a boss's thrown-away decision were captured on a poked state in the evolved Cort fight; Koru's own fight was not run
A battle round is a count, not a list of turns. One byte counts how many turns have been taken this round, and the round ends when that count reaches the number of fighters. Who goes next is decided fresh after every action: the fighter holding the highest unspent initiative key.
The flute is an escape item. In a fight flagged as scripted it cannot flee, so only the tail of its code runs, and that tail is meant to throw away the party's remaining turns. It does it by adding one to the turn count for every living party member, subtracting one, and zeroing the party's keys. With p party members already done, the count runs p too far and the round ends p turns early. Only monsters still hold keys, and the highest key always goes first, so the turns that fall off the end belong to the slowest enemies.
So a flute used as the round's first action skips nobody: the rest of the party loses its turns and every enemy still acts. The boss's place in the order does not matter, only how many party members went before the flute. A boss that has already acted has nothing left to lose.
Why Koru never recovers
Koru has no timer. His script picks a move from the battle's round number, and only round number 4 means Dead End Crisis. The round number rises at every round end whoever acted. End that round before he moves and the number is 5, which his script has no entry for; it does not come back to 4 until the byte wraps. The on-screen countdown is 4 - round number stored in one byte, which is why it reads 255. Nothing writes 255 anywhere.
| Counter | Countdown shown | Spell id stored |
|---|---|---|
| 0 | 4 | 0xA2 |
| 1 | 3 | 0xA3 |
| 2 | 2 | 0xA4 |
| 3 | 1 | 0xA5 |
| 4 | 0 | 0xA1 Dead End Crisis |
| 5 and up | 255, 254, ... | nothing scripted |
With nothing scripted, the generic monster picker is all that is left. Koru's record carries no usable spell, so it builds a plain attack from his two costed attack animations. Both list their hit at frame 200, on clips only 64 and 30 frames long, so the hit never fires: the animation plays and nothing lands. His real specials take their damage and effect sprites from his own cast module, which a plain attack never runs.
In an ordinary, non-scripted fight the same item ends the battle: the party walks off and the escape teardown runs.
The boss still decides on a turn it never gets
- Flute endsparty keys are zero, the count is already too high
- Pickthe boss holds the highest key, so it is picked and its script chooses a move ·
0x801E5D58,0x801DAF48 - Round-over testthe count has reached the end; the chosen move is discarded ·
0x801E67C8 - Next roundround number +1, every key rolled again ·
0x801E6810,0x801DA780
For Koru this changes nothing, because his script keeps no memory. For a boss whose script writes something down at the moment it chooses, that note still changes on the discarded turn. Evolved Cort is the clear case: the game records "Ultra Charge was used" when he decides, not when the move plays, and clears it when he decides on Final Crisis. A thrown-away decision can therefore wipe out a charged Final Crisis, or make him fire one next turn without ever visibly charging.
Keys are rolled fresh every round (speed plus a random amount up to half of it), so a fast party's flute user sometimes rolls first, and then the flute skips nobody. That is the "sometimes" in reports about other bosses.
What each boss loses
Each row is read from that boss's own script in the battle overlay.
| Boss | Move | What a flute skip does |
|---|---|---|
| Koru | Dead End Crisis | Gone for good; the script runs off its end and the countdown shows 255 |
| Cort, first form | Mystic Shield | Cannot be skipped: on the opening round his key is forced to 30000, so he always moves before any flute. Later picks are fresh rolls; a skipped turn costs only that turn |
| Cort, evolved | Ultra Charge into Final Crisis | The charge is a flag his script flips when it picks, not when the move plays. A discarded Final Crisis pick erases the charge (observed); a discarded Ultra Charge pick (1 in 4 with 200 MP) arms a Final Crisis with no charge shown and no MP spent on it |
| Zeto | Call Wave into Big Wave | The latch is written by the move itself, so a discarded pick changes nothing; a skipped Big Wave is chosen again next turn |
| Xain | Bull Charge into Terio Punch | Same latch as Zeto |
| Gi, Che, Lu | Blazing Slash / Megaton Press / Plasma Strike on every round whose counter is 2 mod 3 | Not made up; the next one is three rounds later |
| Van Saryu | Jugger Power (once, at one third HP) | A discarded pick spends the one use; it is never cast |
| Rogue | Element Change on even rounds | A skipped change leaves the old element's attack in use |
The flag applies to every fight launched from a formation row whose first byte is non-zero: the two Piura, Caruban and Golem in Rikuroa, the Viguro pair, Zeto, all three Songi, all three Berserkers, Van Saryu, Dohati, Xain, the Kemaro pair, the Puera fights and Zora in the Floating Castle, late Tetsu, both Gaza, Lu / Che / Gi, Koru, Rogue, Jette, Caruban $2 / $3 and both Cort forms. Lapis, the opening Tetsu, Queen Bee and Gold Face are not flagged, so there the flute simply flees.
Watched in the evolved Cort fight
Captured on a poked state. The run starts from a save state of the real evolved Cort fight as reached in play (Vahn, Noa and Gala; the scripted-fight byte reads 4), on the retail disc; that state has an unrelated place-name patch resident in memory. Written by hand: party HP held at 9000; party speed, so that the order is seat 0, seat 1, Cort, seat 2; the queued actions of seats 1 and 2, changed to the Spikefish cast; and, on the opening round only, one random draw in Cort's script so that he takes Ultra Charge. His Final Crisis pick on the next round was not forced.
| Moment | What happened | Charge flag 0x801C8FE4 | Turn cursor |
|---|---|---|---|
| Opening round, after a party action | Cort's script runs and stores at 0x801EBAD4 | 0 to 1 | 2 |
| Opening round, later | Cort is dispatched and Ultra Charge plays | 1 | 3 |
| Next round, seat 0 attacks | An ordinary turn | 1 | 0 to 1 |
| Next round, seat 1 casts the flute | The tail at 0x801F79E0 runs once per living party seat, three passes, and zeroes the party keys | 1 | 1 to 3 |
| State 0x50 after the flute | Cort's script runs, queues Final Crisis and stores at 0x801EBB14 | 1 to 0 | 3 |
| State 0x5A | Round over: cursor 4 equals the fighter count, round counter 1 to 2, keys rolled again | 0 | 4 |
Three more runs of the same fight show the other case: with the flute user as the first party member to act, the cursor goes to 3 of 4, the other two party turns are dropped and Cort takes his turn.
Not watched: Koru's own fight (the 255 countdown and the attacks that never land rest on the code and his monster record), the Ultra Charge direction of the thrown-away decision, and Van Saryu's one-time Jugger Power.
The Japanese and French discs
The trick is not particular to the USA disc. The Japanese build (SCPS_100.59) is a different compile with its overlays loaded higher and some context fields at other offsets, and every link of the chain is there with the same logic. Koru's monster data is byte-identical from the end of his name onward. The French build (SCES_019.44) is the USA code relocated: its flute tail (0x801F83E0) and round-over step (0x801E70EC) match instruction for instruction. Neither regional disc was run; this is from their code.
| Link | USA | Japan |
|---|---|---|
| Scripted-fight test in the flute module | 0x801F73E8, ctx+0x287 | 0x801FA858, ctx+0x1E7 |
| Tail entry | 0x801F79B8 | 0x801FAE58 |
| +1 per living party seat | 0x801F79D8..0x801F79FC | 0x801FAE7C..0x801FAEA0 |
| Item refund | 0x801F7A08..0x801F7A30 | 0x801FAEAC..0x801FAED4 |
| -1 | 0x801F7A54..0x801F7A60 | 0x801FAEF8..0x801FAF04 |
| Party keys zeroed | 0x801F7A70 / 7A7C / 7A88 | 0x801FAF14 / AF24 / AF34 |
| End-of-action step and round-over compare | 0x801E679C..0x801E67E4 | 0x801E96E4..0x801E9730 |
| Round counter and its one writer | ctx+0x28A, 0x801E6810 | ctx+0x1EA, 0x801E9760 |
| Koru's script and jump table | 0x801EB52C, 0x801CF49C | 0x801EEA10, 0x801D1258 |
| Countdown 4 - counter | 0x801D0F94..0x801D0FA4 | 0x801D2ED0..0x801D2EE8 |
| State 0x50 calls the picker | 0x801E5D58 | 0x801E8C74 |
| Picker calls the monster AI | 0x801DAF48 | 0x801DE304 |
The instructions
The cast module FUN_801F6A00 (PROT 0925). s4 = ctx + 0x11, so 0x9(s4) is the turn cursor ctx+0x1A:
801f73e8 lbu v0,0x287(a0) ; scripted-fight byte
801f73f0 bne v0,zero,0x801f79b8 ; scripted: skip the escape
801f79e0 lhu v0,0x14c(v0) ; seat HP
801f79f0 lbu v0,0x9(s4)
801f79fc sb v0,0x9(s4) ; cursor += 1 per LIVING party seat
801f7a2c jal 0x800421d4 ; refund an unspent Item turn
801f7a60 sb v0,0x9(s4) ; cursor -= 1
801f7a70 sh zero,0x16c(v1) ; party keys = 0 (also 7a7c, 7a88)
The end-of-action gate in FUN_801E295C (PROT 0898):
801e67a4 addiu v0,v0,1 ; cursor += 1
801e67b4 lbu v1,0(a2) ; party count
801e67b8 lbu a0,1(a2) ; monster count
801e67bc lbu a1,0x14(s5) ; ctx+0x25, dropped without acting
801e67c8 slt v0,v0,v1 ; cursor < party + monsters - dropped ?
Koru's arm of the monster AI:
801eb538 lbu v1,0x28a(v0) ; round counter
801eb540 sltiu v0,v1,5
801eb544 beqz v0,0x801ebdac ; 5 or more: nothing scripted
Evolved Cort's arm. Both stores are inside the pick; neither of his cast modules (PROT 0961, 0962) touches the word:
801eba74 lw v0,4(s0) ; 0x801C8FE4, the charge flag
801eba7c bnez v0,0x801ebae8 ; charged: the Final Crisis arm
801eba8c andi v0,v0,3 ; else a 1-in-4 roll, needs 200 MP
801ebab8 sb v0,0x1df(s4) ; queue 0xA5 Ultra Charge
801ebad4 sw v0,4(s0) ; flag += 1
801ebaec li v0,0xb4 ; queue Final Crisis
801ebb14 sw v0,4(v1) ; flag -= 1
With P seated party members, L alive, M monsters and c turns taken before the flute user: the cursor is c + L after the flute's turn, and the round continues only while it is below P + M - ctx[+0x25].
Zeto and Xain keep their two-step latch at 0x801C8FF0 + seat*4; their scripts only read it and the cast modules write it, which is why a discarded pick costs them nothing. First-form Cort's key is forced to 30000 by the seeder when the first formation id is 0xB4 and the round counter is 0 (0x801DAAB8..0x801DAAF0).
Addresses
| Address | Image | What | Evidence |
|---|---|---|---|
0x801F6A00 | PROT 0925 | Spikefish cast module body | disassembly |
0x801F73E8 | PROT 0925 | Reads the scripted-fight byte ctx+0x287 | disassembly |
0x801F78F8 | PROT 0925 | Ordinary fight: stores action state 0x65 (escape) | disassembly |
0x801F79B8..0x801F7A88 | PROT 0925 | The tail: cursor +1 per living seat, item refund, -1, party keys zeroed | disassembly, capture |
0x800421D4 | SCUS | Give-item routine used for the refund | disassembly |
ctx+0x1A | RAM (ctx = *0x8007BD24) | Turn cursor: turns taken this round | disassembly, capture |
ctx+0x25 | RAM | Fighters dropped from the round without acting | disassembly |
ctx+0x287 | RAM | Scripted-fight byte; reads 4 in boss fights reached in play | disassembly, capture |
ctx+0x28A | RAM | Round counter | disassembly, capture |
actor+0x16C | RAM (table 0x801C9370) | Initiative key, 0 = spent | disassembly |
0x801E679C..0x801E67E4 | PROT 0898 | End-of-action gate; round-over compare at 0x801E67C8 | disassembly, capture |
0x801E6800..0x801E6810 | PROT 0898 | Round counter +1 | disassembly |
0x801DA780 | PROT 0898 | Initiative seeder, called every round from 0x801D0ED8 | disassembly |
0x801DAAB8..0x801DAAF0 | PROT 0898 | Seeder: formation 0xB4, round 0, seat-3 key = 30000 | disassembly |
0x801DABA4 | PROT 0898 | Next-fighter picker; calls monster AI at 0x801DAF48 | disassembly |
0x801E5D58 | PROT 0898 | State 0x50 calls the picker before the 0x5A test | disassembly, capture |
0x801CF1CC | PROT 0898 | Monster AI jump table, index monster id - 4 | disc data |
0x801EB52C | PROT 0898 | Koru's AI arm; sltiu v0,v1,5 at 0x801EB540 | disassembly |
0x801CF49C | PROT 0898 | Koru's five-entry jump table | disc data |
0x801D0F94..0x801D0FA4 | PROT 0898 | Countdown 4 - counter stored at 0x801F6958 | disassembly |
0x801EC468 | PROT 0898 | Melee kernel's hit-frame gate | disassembly |
monster 0xB6, entries 5 and 6 | PROT 0867 | Hit frame 200, cost 200, clips 64 / 30 frames | disc data |
0x801C8FE4 | RAM | Shared one-word AI latch in the per-battle block 0x801C8FE0 (zeroed at 0x8005601C): evolved Cort's charge, Van Saryu's one-shot, Rogue's element | disassembly, capture |
0x801EBAD4 / 0x801EBB14 | PROT 0898 | Evolved Cort: charge flag +1 on the Ultra Charge pick / -1 on the Final Crisis pick | disassembly, capture |
0x801EBDA8 | PROT 0898 | Van Saryu: one-shot flag +1 on the Jugger Power pick | disassembly |
0x801C8FF0 + seat*4 | RAM | Two-step latch, set and cleared by the cast module (PROT 0946 0x801F6CCC / 0x801F6BEC, PROT 0953 0x801F6DD0 / 0x801F6C90) | disassembly |
0x801EB7CC..0x801EB81C | PROT 0898 | Gi / Che / Lu: special when round counter % 3 == 2 | disassembly |
Verifying it
The Koru half is the part still to be run. Break on 0x801F79F0 and 0x801E67C8. Watch the byte at ctx+0x1A, the byte at ctx+0x28A, the byte at 0x801F6958, and the halfword +0x16C of all four fighters. With the flute used second or third on the round where the counter reads 4, the three party keys drop to zero together, the cursor jumps by (living party - 1), and the counter goes 4 to 5 while Koru's key is still non-zero. Repeat with the flute used first: Koru acts.
For a thrown-away decision, add a write watch on 0x801C8FE4 and a breakpoint on the action dispatch at 0x801E2CDC: the word changes in state 0x50 and no dispatch for the boss follows before the round counter moves.
Open: whether a member who is alive but cannot act (petrified or captured) shifts the overshoot by one, since the flute's loop tests HP only; the two uncaptured rows above (evolved Cort's Ultra Charge direction, Van Saryu); and which of the flagged Rikuroa rows belongs to which of Caruban, the Golem and the Piura.
The War God Icon sometimes gives only one attack string
- Reported as
- The accessory should make the wearer attack twice in a row, but it randomly fails to; players describe it in solo Muscle Dome Master Course fights (community report).
- Cause
- The "second pass used" byte doubles as the Low command's AP cost, so typing commands sets it; only an attacker stepping up to a target clears it
- Where
- Battle action state machine
FUN_801E295Cand the arts gauge buildFUN_801D388C, PROT entry 0898; byte atctx+0x16 - Affects
- Every wearer, plain attacks, Arts and counterattacks; in practice one-on-one fights with hand-typed commands
- Status
- Explained - read from the code, and the refusal captured on a poked state (the wearer was nudged into reach); a refusal with nothing nudged, and how often Noa or Gala hit it, are not measured
There is no dice roll. At the end of the first attack string the game grants the replay only if a one-byte pass counter reads zero.
That byte is shared with something unrelated: the AP cost of the Low command on the arts-entry screen. Every time a fighter's command gauge is built for typing commands by hand, the byte is set to that cost, 30. A doubled attack also leaves it at 2 or more. The game clears it in exactly one place: at the end of the short "step up to the target" phase of an attack.
So the marker reads "second pass already used" at the start of every round whose commands were typed, and stays that way until some attacker, party or monster, has to close the distance to a target. If the wearer swings before anyone has done that and is already within reach, the step-up phase is skipped, nothing has cleared the byte, and the replay is refused. The single pass that does run deals its damage normally.
Fighters are not walked back after an action. A monster that has just hit the wearer is left standing at the very edge of the wearer's reach, and which side of the edge depends on the knock-back of the move it used and on who the wearer is: Vahn's reach is 43 units shorter than Noa's and 96 shorter than Gala's. The failing round is: the wearer is the first to attack, types commands by hand, and targets the monster that hit them last round while it is still within reach. In a crowded fight the other attackers shove the target away and clear the byte, so it almost never lines up. In a one-on-one fight, which is where players report it, it can.
Avoiding it: choose Auto instead of typing commands, attack a different monster, or let anyone else attack first.
| Sets the byte non-zero | Clears it | Leaves it as it is |
|---|---|---|
| Typing commands (the Low cost, 30) | A party attacker who has to step up | A party attacker already in reach |
| A granted second pass (one plus its strikes) | A monster attack that starts out of reach, for all but six monsters | A monster already in reach, and the six that walk in: the three Caruban, Evil Shadow, Shade, Nightmare |
| Magic, items, Spirit |
Being in reach is not enough on its own: a wearer who starts in reach with a clean byte still doubles. A counterattack by the wearer is decided the same way, since the counterer takes over inside the strike loop and inherits whatever the byte holds. Nothing else cuts the second string: the target cannot die between the passes (the first pass's damage is held back until the second ends), there is no AP check, and monsters never counter.
Watched in play
Two fights were run on the retail disc: three-on-one against Gaza and one-on-one against a Gobu Gobu. No available save has the Icon equipped, so in every run the ability bit was written onto Vahn by hand (bit 0x2000 set in the word at 0x800847FC). The Gaza state has an unrelated arts-AP patch resident in the battle overlay; the Gobu Gobu state is retail apart from four data bytes.
| What | How it was set up | What was seen |
|---|---|---|
| A normal double | Ability bit poked, nothing else | Out of reach, state 0x19, clear at 0x801E35B4, strikes with the byte at 0, the check at 0x801E3A18 reads 0, grant, the second pass bumps the byte to 6. No HP is written until the second pass ends |
| In reach with a clean byte | Ability bit poked; Vahn's position not touched | The monster steps up to hit Vahn, which clears the byte, and stays beside him. Vahn takes the in-reach bypass and the check still reads 0: granted |
| The refusal | Captured on a poked state: ability bit; HP 9000 on both sides; Vahn's maximum HP raised each round so that he acts first; Vahn moved 49 to 78 units toward the target at the range test | Bypass taken, two strikes bump the byte from 30 to 32, the check reads 32, recovery, no second pass. One string, damage 84 to 88 committed normally. Three rounds running |
| The control | The same run, with the byte also poked to 0 at 0x801E3A10 | Second pass runs, 178 committed instead of 88. The next round, with no such poke, is refused again |
| Every store to the byte | A write breakpoint over ctx+0x14..0x17 through both fights: about sixty attacks, a monster's magic cast, manual and Auto entry | Only the four stores listed below write ctx+0x16. No summon, cast-module action or item use ran under the watch; for those the list rests on a reference sweep of every code image |
| Battle setup | A save taken at the first prompt of the Gaza fight | ctx+0x14..0x17 read 00 00 00 00 |
| The gauge store | Manual entry, both fights | Opening directional entry sets ctx+0x15 and ctx+0x16 to 30 every time; under Auto the byte keeps its old value |
How close an unassisted wearer gets. With his position left alone, Vahn attacking the Gobu Gobu that had just hit him was 0 to 49 units outside reach in eleven rounds and inside it in one. In ten of those eleven the margin is smaller than his own 43-unit handicap, so Noa or Gala standing in the same spot would have been in reach; that is an inference from Vahn's numbers, not a capture with them wearing the Icon. Against Gaza with three attackers, every start was far out of reach.
The instructions
s5 = ctx + 0x11: 5(s5) is the pass counter ctx+0x16, 4(s5) the strike cursor, 2(s5) the acting seat. The grant, at the end of the string:
801e39bc sltiu v0,v0,3 ; party seat
801e39fc lw v0,0x6bc(v0) ; character record +0xF4
801e3a04 andi v0,v0,0x2000 ; War God Icon bit
801e3a10 lbu v0,5(s5) ; pass counter
801e3a18 bnez v0,0x801e3a74 ; NON-ZERO: no second pass
801e3a38 sb zero,4(s5) ; rewind the strike cursor
801e3a40 sb v0,5(s5) ; counter += 1
The one clear, on leaving state 0x19, and the bypass in state 0x14:
801e35b0 sb v0,7(v1) ; state = 0x1E
801e35b4 sb zero,5(s5) ; pass counter = 0
801e31f4 jal 0x8004e2f0 ; range check
801e3200 bnez v0,0x801e3230 ; out of reach: approach
801e3210 sb v0,7(v1) ; in reach: state = 0x1E, counter untouched
The store outside the attack loop, in the arts gauge build (case 9 of FUN_801D388C, s4 = ctx + 0x11):
801d3b3c sb v0,3(a2) ; a2 = s4 + i, i = 0..3: ctx+0x14+i
801d3bc8 sltiu v0,a3,4 ; Left, High, Low, Right
i = 2, the Low command, is ctx+0x16. Four stores cover the byte and no others: the gauge cost (0x801D3B3C), the clear (0x801E35B4), a per-strike bump that only fires when it is already non-zero (0x801E37BC) and the grant (0x801E3A40). Reading the counter as private to the attack loop misses the first of these. The list comes from a reference sweep of the executable and every overlay that follows the context pointer through both its lui-relative and gp-relative loads.
Battle setup zeroes the byte: FUN_80055B6C clears the whole battle block (0x80055F14..0x80055F24). So in a fight entered with Auto the first attack always doubles, and with typed commands the byte is already 30 before the first swing.
Reach: a party attacker is in reach of a monster when reach_bias[character] + distance < size * 16, with the bias table at 0x80078878 (+43 Vahn, 0 Noa, -53 Gala) and size the monster record's byte +0x1F. The walk chain (states 0x15..0x18) needs an action entry tagged 0x20; six of the 186 monster records have one.
The damage kernel withholds the HP write only while the counter is below 2 (0x801EE114), so a refused pass, whose byte is 2 or more, still applies its damage. The per-strike bump is a one-byte increment: a byte left stale long enough wraps through zero and the next string is granted again.
Addresses
| Address | Image | What | Evidence |
|---|---|---|---|
ctx+0x16 | RAM (ctx = *0x8007BD24) | Attack x2 pass counter, and the Low command's AP cost on the arts-entry screen | disassembly, capture |
0x80084140 + (id-1)*0x414 + 0x6BC | RAM | Character record +0xF4; bit 0x2000 is the War God Icon | disassembly |
0x801E39BC..0x801E3A64 | PROT 0898 | Second-pass grant and its guards; zero test at 0x801E3A18 | disassembly, capture |
0x801E3768..0x801E37BC | PROT 0898 | Per-strike counter bump | disassembly, capture |
0x801E35B4 | PROT 0898 | The only clear, on the state 0x19 exit | disassembly, capture |
0x801D3B3C | PROT 0898 | Gauge build in FUN_801D388C: per-command AP cost into ctx+0x14+i; i = 2 is the counter | disassembly, capture |
0x801E31F4..0x801E3210 | PROT 0898 | State 0x14: in reach goes straight to 0x1E | disassembly, capture |
0x801E3260 | PROT 0898 | Looks up the tag-0x20 walk entry that selects the walk chain | disassembly |
0x801E3548..0x801E3558 | PROT 0898 | State 0x18 into 0x1E, no clear | disassembly |
0x801E3664..0x801E36DC | PROT 0898 | Counterattack swap inside the strike loop | disassembly |
0x801DAF74..0x801DB050 | PROT 0898 | Counter latch 0x801F6970: armed only against a party seat with the Counterattack bit | disassembly |
0x801EE0F8..0x801EE120 | PROT 0898 | Damage kernel carry test, counter below 2 (twin at 0x801EE954) | disassembly, capture |
0x8004E2F0 | SCUS | Range metric, 0 = in reach; party-on-monster test at 0x8004E344..0x8004E36C | disassembly, capture |
0x80078878 | SCUS | Reach bias per character: +43, 0, -53 | disc data |
monster record +0x1F | PROT 0867 | Size byte; the reach threshold is size * 16 | disc data |
0x80055F14..0x80055F24 | SCUS | Battle init FUN_80055B6C zeroes the battle block, context included | disassembly |
0x801DA850..0x801DA8C4 | PROT 0898 | Seeder's wound bonus for a party seat, from max HP - HP | disassembly |
0x801CEDA4 / 0x801CEDA8 | PROT 0898 | Jump-table slots of states 0x18 / 0x19 | disc data |
Verifying it
Watch the byte at ctx+0x16. Break on 0x801E3A18: a non-zero read there is a refused second pass. Break on 0x801E320C with ctx+0x13 < 3 to catch the in-reach bypass, and on 0x801E31FC to log the range result. Writing zero to the byte at 0x801E3A10 restores the double.
The run that would close the remaining gap: a memory-card save with the Icon on Noa or Gala in a one-on-one Muscle Dome fight, commands typed by hand, the wearer faster than the opponent, logging the range result and the string-end read over ten rounds. The prediction is "in reach" and a non-zero read on most rounds that follow the opponent's attack.
Open: a refusal with nothing nudged; how often a Noa or Gala wearer starts in reach; the wearer's own counterattack and the one-byte wrap are read from the code, not captured.
Side Kick's first hit misses floating enemies even with War God Boots
- Reported as
- War God Boots let Gala's low kick hit floating enemies, but Side Kick still misses its first hit against them (community report).
- Cause
- Reach is a property of each hit's power byte. The boots swap in a different low-kick record; Side Kick is an art with its own fixed bytes
- Where
- Melee kernel
FUN_801EC3E4, PROT entry 0898, test at0x801EC488; bytes in Gala's battle file, PROT entry 0865 - Affects
- One art hit in the whole game; the floating monsters listed below
- Status
- Explained - from the code and disc data, no capture
Whether a hit can reach a floating enemy is decided by the hit's own power byte, not by anything on the attacker. Low hits come in two encodings: one that cannot reach a floater and one that always connects.
The boots grant no ability. Every pair of shoes ships its own Low and High kick, and the War God Boots' Low kick simply carries the always-connects byte where Gala's other shoes carry the restricted one.
| Footwear | Low kick | High kick |
|---|---|---|
| none / Power Shoes / Fighting Boots | 13 restricted | 0E restricted |
| War God Boots | 1D free | 18 free |
| Ra-Seru Thongs | 13 restricted | 18 free |
Side Kick is not a shoe's kick. It is an art, stored in Gala's art bank with the fixed power run 12 17. The first byte is the restricted kind, so the first hit misses a floater; the second connects. Changing boots never touches an art record. Across all three characters' art banks it is the only hit encoded this way.
The instructions
801ec488 sltiu v0,a3,3 ; party attacker only
801ec494 lbu a0,0(a1) ; this hit's power byte
801ec49c sltiu a1,a0,0x16 ; 0x16 and up always connects
801ec4c0 lbu v1,0x1e(v0) ; target monster record +0x1E
801ec4cc sltiu v0,a0,0x11 ; class 2: bytes 0x11..0x15 miss
801ec53c sltiu v0,v0,0x11 ; class 3: bytes below 0x11 miss
801ec554 sb v0,0x263(v1) ; miss flag, no roll, no damage
Direction commands play swing records spliced from the equipped items (the footwear section's first record is the Low kick, its second the High kick; the power run is the record's first bytes). Arts play a record from the art bank, selected as record constant - 0x10, power run at +0x24. Side Kick is Gala's action constant 0x23.
Who and what it applies to
Floating monsters (height class 2): Evil Fly, Death Wings, Demon Fly, Drake Ghost, Guardian, Ghost Knight, the Theeder, Vera, Nighto, Viguro, Gola Gola, Slippery, Puera and Gilium families, Pump Bat, Devil Pump, Boogie Pump, Hornet, Killer Bee, Queen Bee, Spikefish, Mad Bird, Wailing Bird, Mage, Sorcerer, Thermo, Amethyst, Garnet, Topaz, the Evil Bat records, Lapis, Rogue and Zora.
Short monsters (class 3, missed by restricted High kicks): Mushroom, Mushrin, Mush Mush, Frog, Gel Frog, King Frog.
| Character | Reaches floaters | Restricted |
|---|---|---|
| Vahn | Triumph Boots, Ra-Seru Boots | none, Warrior, Ironman, Master, Expert, Hero Boots |
| Noa | nothing | every shoe, Ra-Seru Shoes included |
| Gala | War God Boots | none, Power Shoes, Fighting Boots, Ra-Seru Thongs |
Addresses
| Address | Image | What | Evidence |
|---|---|---|---|
0x801EC488..0x801EC554 | PROT 0898 | Limb-versus-height miss test in the melee kernel FUN_801EC3E4 | disassembly |
0x801F64EC | PROT 0898 | Power multiplier table 0C 12 14 16 1C | disc data |
monster record +0x1E | PROT 0867 | Height class: 2 floating, 3 short | disc data |
ctx+0x263 | RAM | Miss strobe | disassembly |
0x80052FA0 / 0x800557B8 | SCUS | Battle-file assembler and swing-record copy | repo doc |
footwear section +0x04 / +0x08 | PROT 0863..0865 | Offsets of the Low / High swing records; power run at record +0 | disc data |
art bank record (0x23-0x10), +0x24 | PROT 0865 | Side Kick's power run 12 17, hit frames 7 and 16 | disc data |
Side Kick = action constant 0x23 | - | Gala's learned-art slot 8 | repo doc |
Verifying it
Fight an Evil Fly with Gala in War God Boots and break on 0x801EC494. A plain Low reads 0x1D and connects. Side Kick reads 0x12 on its first hit, branches to 0x801EC548 and sets the miss flag; its second reads 0x17 and connects. Changing the art record's first power byte from 0x12 to 0x1C (same multiplier, free range) makes the first hit land.
Open: whether 0x12 is a deliberate low sweep or an authoring slip cannot be told from the data.
The level-3 dance special swallows the next special
- Reported as
- In the dance minigame, the level-3 special animation "eats" the next special if two level-3 specials are done in a row (community report). The special is the Triangle move, the lesson's "groovy move". An early fan guide gives the same warning: used at level 3, it lasts so long that you miss the next move.
- Cause
- Presses are only judged while the idle or dance loop plays; the level-3 Triangle move runs 6.83 beats, past the next scoring slot and the next combo's first note
- Where
- Dance overlay, PROT entry 0980: per-dancer tick
FUN_801D1358, gate at0x801D168C - Affects
- Every dancer and every triggered move; worst at level 3
- Status
- Explained - read from code and data, and captured in the running game with scripted button presses and nothing written to memory
A dancer's button presses are judged only while its body is playing the idle or the basic dance loop. Every triggered move, whether a combo finisher, the Triangle "groovy move" or the flinch after a wrong press, is a one-shot animation, and until it reaches its last frame the judge is not called at all. A press made during it is not stored, not counted as a miss, and for Triangle does not use up one of the three per song. It vanishes.
The level-3 animations are the longest. The level-3 Triangle move lasts 6.83 beats. The next scoring slot closes at most 4.75 beats after the press, so a second Triangle there is always lost. The next level-3 combo starts 6 beats after the press, so its first note is lost too and that combo cannot be completed. A Triangle on the last note of that combo, 8 beats on, does register.
Holding the button does not help: the game only sees a button on the frame it goes down.
| Move | Frames of animation | Lock, game frames | Lock, delta units | Lock, beats |
|---|---|---|---|---|
| wrong-press flinch | 30 | 40 | 120 | 4.27 |
| level-1 combo | 15 / 20 | 20 / 27 | 60 / 81 | 2.14 / 2.88 |
| level-2 combo | 26 | 35 | 105 | 3.74 |
| level-3 combo | 30 | 40 | 120 | 4.27 |
| level-1 Triangle | 18 | 24 | 72 | 2.56 |
| level-2 Triangle | 34 | 46 | 138 | 4.91 |
| level-3 Triangle | 48 | 64 | 192 | 6.83 |
Triangle pays its real reward only on a combo slot (every fourth beat) inside the timing window. A press made up to 0.75 beat into one slot can be followed on the next slot no later than 4.75 beats afterwards:
| First move | Lock, beats | What it costs afterwards |
|---|---|---|
| level-1 Triangle | 2.56 | nothing: a press on the next slot is judged |
| level-2 Triangle | 4.91 | the whole next slot, every time |
| level-3 Triangle | 6.83 | the whole next slot, and the first note of the next level-3 combo, 6 beats on. A press 8 beats on is judged |
| level-3 combo finisher | 4.27 | a next-slot press is judged only if it comes at least 4.27 beats (40 game frames) after the finishing press |
| wrong-press flinch | 4.27 | as for a level-3 finisher |
The level-3 chart places one three-note combo per eight beats, so two combo finishers in a row never collide. A Triangle dropped inside the lock does not reduce the stock. A Triangle pressed after the lock ends but outside a combo-slot window does spend one, for only a few points, which is the other way a second special can feel swallowed. Triangle pressed together with Square or Circle is judged as Triangle alone, so "with the last command of a combination" means "instead of it".
The code also has a per-dancer "queued move" flag that would start a follow-up move when the current one ends. The move number is written on every completed combo, but the flag itself is only ever written zero: nothing else on the disc that can be loaded with the dance hall refers to it, and a write watch during the runs saw only the two zero stores. The follow-up never fires. It reads as an unfinished "chain the next move" path, which is an inference.
Watched in play
The qualifier (Noa and two rivals) was run in the retail dance hall. Every input went through the emulator's controller; level 3 was reached by pressing the chart's own notes; nothing was written to memory. The run starts from a save state taken on the way into the hall, which then loads the dance code from the retail disc. That state has an unrelated patch resident in the main executable: four code sites differ from the disc, none in the frame-timing or animation code, and the dance overlay's code is byte-identical to the disc's.
Triangle on consecutive combo slots at level 3:
| Game frame | Beat | Animation bound | Judge called | Triangles left | Score |
|---|---|---|---|---|---|
| 543 | 51 | loop to 17 | yes | 3 to 2 | 180 to 255 |
| 581 | 55 | 17 | no, though the press is in the pad word | 2 | 255 |
| 607 | 57 | loop again | yes | 2 | 255 |
| 618 | 59 | loop to 17 | yes | 2 to 1 | 255 to 330 |
| 656 | 63 | 17 | no | 1 | 330 |
| 693 | 67 | loop to 17 | yes | 1 to 0 | 330 to 405 |
The gate stayed shut for exactly 64 game frames after each judged level-3 Triangle. Other runs: from the start of a song, the level-1 Triangle reopens the gate after 24 frames and the next press is judged, the level-2 Triangle holds it for 46 and the press on the next slot is neither judged nor spent. With the next combo played normally after a level-3 Triangle, its first note is not judged, the other two score as plain hits and the combo does not close. With Triangle pressed on that combo's last note instead, it is judged and pays in full. A dropped Triangle held down for 80 frames, through the end of the lock, shows in the pad word on one frame only and never fires.
The instructions
801d168c lw v0,0x10(s1) ; idle clip id
801d1690 lh v1,0x5c(s0) ; clip bound right now
801d1698 beq v1,v0,0x801d16b4 ; idle: judge
801d16a0 lw v0,0x18(s1) ; dance-loop clip id
801d16ac bne v1,v0,0x801d1750 ; anything else: skip the judge
801d16b4 jal 0x801d1af4 ; judge / award routine
The clip driver FUN_800204F8 steps the cursor by (rate * 2 + div - 1) / div times the frame-delta byte at 0x1F800393 and raises the end flag at frames * 16 - 1. Noa's moves all run at rate 8 with divisor 4, a step of 4. The hall runs at a frame delta of 3: the dance init writes the frame-step floor (0x801CF070) and the frame driver FUN_80016B6C applies it, and it read 3 on every play frame. So the cursor moves 12 per game frame and the gate is shut for exactly ceil((frames * 16 - 1) / 12) game frames after the judged one. There is no extra frame for the release: the driver call at the end of the tick that binds the clip is already its first step.
The beat clock advances by the same frame delta times ten, with a beat of 0x119 units, so one beat is 28.1 delta units or 9.37 game frames. A level-2 Triangle reopens the gate 1380 beat-clock units after the press; the next slot's window has closed by 1333.
Addresses
| Address | Image | What | Evidence |
|---|---|---|---|
0x801D1358 | PROT 0980 | Per-dancer tick | disassembly |
0x801D168C..0x801D16B4 | PROT 0980 | Judging gate: bound clip must be idle or dance loop | disassembly, capture |
0x801D14C8 | PROT 0980 | Clip end-flag test (+0x62 & 0x100) | disassembly |
0x801D1AF4 | PROT 0980 | Judge / award routine | disassembly |
0x801D1C9C | PROT 0980 | Triangle clip pick, lane*2 + 0x10 | disassembly |
0x801D1CA8..0x801D1CCC | PROT 0980 | Combo-slot and window test for Triangle | disassembly |
0x801D1C98 | PROT 0980 | Triangle stock decrement | disassembly |
0x801D1E5C / 0x801D1F9C | PROT 0980 | Direction arms skip when the hit latch 0x801D548C is set, so Triangle pre-empts them | disassembly |
0x801D534C | RAM | Triangle stock, one word per dancer | disassembly, capture |
0x801D55CC / 0x801D560C | RAM | Queued-move flag (only ever written zero) and move number | disassembly, capture |
0x801D4E1C | PROT 0980 | Dancer descriptor table; move pairs from +0x28 | disc data |
0x801D509C | PROT 0980 | Step chart, three rows of 0x20 beats | disc data |
0x801CF070 | PROT 0980 | Dance init writes the frame-step floor, 3 | disassembly |
0x8007B9D8 | RAM | Frame-step floor | disassembly, capture |
0x80016B6C | SCUS | Frame driver that rebuilds 0x1F800393 and applies the floor | disassembly |
0x800204F8 | SCUS | Clip driver | disassembly |
0x1F800393 | RAM | Frame-delta byte both clocks scale by; 3 in the hall | disassembly, capture |
0x8007B874 | RAM | Pad word of newly pressed buttons; Triangle is 0x0010 | capture |
clip records 7..17 | PROT 1229 | Frame counts of Noa's moves | disc data |
beat 0x119, window 0xD2 | - | Beat clock constants | repo doc |
Verifying it
Break on 0x801D16B4 for dancer 0 and log the frame number. After a level-3 Triangle the next hit comes 64 game frames later at the hall's frame delta of 3. Or watch the bound clip id at actor +0x5C: presses made while it reads 0x11 show in the pad word at 0x8007B874 but never reach 0x801D1AF4, and the stock word at 0x801D534C does not change.
The capture ran the qualifier only; the gate is the same code in every mode of the minigame.
See also
Battle action state machine · damage formulas · art data · player battle files · accessory passives · the arts command gauge · the endless camera orbit