Author: nihil, hobbitdur
FF8 monster battle scripts are divided into 5 sections, init, turn, counter, death and pre-counter.
Each section contains code that is executed at different times during the battle.
- Init: executes once when the monster is loaded into battle.
- Turn: executes once the monster’s ATB bar fills. This happens after the monsters turn counter is incremented.
- Counter: executes after the monster is affected by a battle command. Note that this means its impossible to change a monsters stats/elemental resists before it takes damage, and that it cannot detect Sleep.
- Death: executes when the monster’s HP reaches 0 or it is afflicted with the Death status. (Eject may not trigger this section - this needs further testing.)
- Pre-Counter: similar to counter but executed before it. Unlike the other sections, pre-counter runs synchronously inside the damage-application code, at the exact moment the hit is being resolved: the engine has already computed the damage and picked the default hit/death reaction animation, but has not yet committed the hit data. This unique timing is what gives pre-counter its special powers: it is the only section where setHitAnim works (it overrides the reaction animation for the very hit being processed — the classic vanilla use is checking for the Death status and swapping in a custom death animation), and it is also why launching an attack from this section crashes the game (the engine is in the middle of resolving another action). Ifs, variable assignments and stat changes can also be used safely here.
The order of execution when a monster is attacked is: pre-counter -> death (if killed) -> counter.
This order is now fully explained by the engine’s scheduling (verified via decompile, 2026-07): pre-counter runs synchronously in the middle of the hit, while death and counter are queued — during damage application each victim is stamped with a pending reaction (counter if it survived, death if it died with scripted death), and once the attacker’s action fully completes, those reactions are queued in the order the targets were hit. For a multi-hit attack, pre-counter runs once, on the final hit.
Note: pre-counter code will ONLY be executed after an attack that kills a monster if the monster’s death section has code in it (apart from “return”). Mechanism (traced 2026-07): at monster load, the engine checks the first bytecode byte of the death section — if it is not the return/stop opcode (0x00), a “scripted death” flag is set. On a killing blow, both the death script and the pre-counter call go through that flag’s branch. On ordinary (non-killing) hits, pre-counter is not gated by this flag at all and runs for every monster. Byte-exact quirk: only the first byte of the death section is checked, so a death section starting with return followed by more code is treated as empty (the extra code is unreachable anyway).
Note: if the death section is empty, it will function like a die opcode.
Note: if the death section is NOT empty, it is mandatory for it to eventually execute a die opcode, otherwise the monster will continue fighting, even on 0 HP, making the battle unwinnable for the player and forcing them to run. If running is not an option, this results in a soft lock. Mechanism: when a scripted-death monster is killed, the engine applies the Death status, runs pre-counter, then removes the Death status again and defers the actual death to the queued death section — the monster really is alive at 0 HP until its script executes die.
The PC runtime interpreter, section dispatch functions, the scheduling queue, and a full deep-dive on pre-counter are documented in Enemy AI VM Runtime. Beyond the 5 scripted sections, the same dispatcher also handles 4 engine-internal “sections” that contain no monster bytecode: Doom expiry (5), Regen ticks (6), the Odin/Gilgamesh/Phoenix auto-summons (7) and Angelo auto-actions (8) — see the same page.
- Opcodes
- Opcode 0x00 (0) - return
- Opcode 0x01 (1) - print
- Opcode 0x02 (2) - if
- Summary
- Parameters
- SubjectID
- General info
- Subject list
- Additional info:
- HP OF SPECIFIC TARGET
- 2. Random Number Check
- 3. Status Check
- 4. Alive Count Check
- 5. Specific Entity Alive Check
- 6. Last Action / Turn Count Check
- 7. Entity Alive Check (In specific slot)
- 8. Sex check
- 9. GF Check (Not yet obtained)
- 10. Odin Acquisition Check (true if obtained)
- 11. Timer Check
- 12. Variable Check
- Opcode 0x03 (3) - prepareMagic
- Opcode 0x04 (4) - target
- Opcode 0x06 (6) - usePrepared
- Opcode 0x07 (7) - prepareMonsterAbility
- Opcode 0x08 (8) - die
- Opcode 0x0B (11) - useRandom
- Opcode 0x0C (12) - use
- Opcode 0x0D (13) - noOp13
- Opcode 0x0E (14) - var
- Opcode 0x0F (15) - bvar
- Opcode 0x11 (17) - gvar
- Opcode 0x12 (18) - add
- Opcode 0x13 (19) - badd
- Opcode 0x15 (21) - gadd
- Opcode 0x16 (22) - recover
- Opcode 0x17 (23) - setEscape
- Opcode 0x18 (24) - printSpeed
- Opcode 0x1C (28) - waitText
- Opcode 0x1D (29) - leave
- Opcode 0x1F (31) - enter
- Opcode 0x20 (32) - waitTextFast
- Opcode 0x24 (36) - fillAtb
- Opcode 0x25 (37) - setScanText
- Opcode 0x28 (40) - statChange
- Opcode 0x29 (41) - draw
- Opcode 0x2A (42) - cast
- Opcode 0x2E (46) - blowAway
- Opcode 0x2F (47) - targetable
- Opcode 0x30 (48) - untargetable
- Opcode 0x34 (52) - enable
- Additional opcodes (not yet fully written up)
Opcodes
Monster AI sections are composed of one-byte opcodes, each followed by a variable number of the bytes used as parameters.
These opcodes define the monster’s behaviour in battle.
Note: descriptions are written from the monster’s perspective, so “opposing party” refers to the party of playable characters.
Opcode 0x00 (0) - return
Summary
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x00 | return | 1 | End monster’s turn |
This opcode is used to end the monster’s turn, preventing further execution of code.
It is mandatory for every battle script section to end with a return.
Note: the underlying engine calls this opcode stop internally; IfritAI and this wiki use the friendlier name return.
Parameters
None
Opcode 0x01 (1) - print
Displays a battle message.
Summary
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x01 | 2 | Print text |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | TextID | int | The index of the text |
Texts are defined in section 7 of c0mxxx.dat files.
Each text has an ID, starting from 0 and incrementing with each subsequent text.
TextID corresponds to this ID.
Note that battle message speed is ignored.
Opcode 0x02 (2) - if
Summary
The if opcode allow to execute code only on certain condition
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x02 | if | 1 | Define condition |
Parameters
The if opcode is a really complex one with lots of different possibilities.
It is composed of a subject that define how the other parameters are used.
Then it uses 3 parameters for the condition: the left part of the condition, the right part, and the comparator.
So it does something like this: [ConditionLeftPart] [Comparator] [ConditionRightPart], for ex: COMBAT SCENE == 76
Then there is a jump param to define the jump size if the condition is not met
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | SubjectID | SubjectID | Define what the if will be about |
| 2 | 1 | ConditionLeftPart | Vary | The left condition |
| 3 | 1 | Comparator | Comparator | The comparator of the condition |
| 4 | 1 | ConditionRightPart | Vary | The right condition |
| 5 | 1 | Padding | Unused | Always 0x00 (changing it has no impact) |
| 6 | 2 | Jump | int | The number of byte to jump if the condition is false |
SubjectID
General info
The SubjectID defines what will be the type of the left part and right part of the condition
The Short text is a description of the subject id.
The Left text is the meaning of the ConditionLeftPart. {} is to be replaced with the value hold by ConditionLeftPart depending of Param left type
The Param left type define the type to use to translate the ConditionLeftPart
Same logic for the ConditionRightPart
The Param list can feed the param type with additional info.
Subject list
| SubjectID | Short text | Left text | Param left type | Right text | Param right type | Param list |
|---|---|---|---|---|---|---|
| 0 | HP OF SPECIFIC TARGET | HP_SPE of {} | target_advanced_specific | percent | ||
| 1 | HP OF GENERIC TARGET | HP_GEN of {} | target_advanced_generic | percent | ||
| 2 | RANDOM VALUE | RANDOM VALUE BETWEEN 0 AND {} | int_shift | int | [-1] | |
| 3 | COMBAT SCENE | COMBAT SCENE | int | |||
| 4 | STATUS OF SPECIFIC TARGET | STATUS_SPE OF {} | target_advanced_specific | status_ai | ||
| 5 | STATUS OF GENERIC TARGET | STATUS_GEN OF {} | target_advanced_generic | status_ai | ||
| 6 | NUMBER OF MEMBER | NUMBER OF MEMBER OF {} | target_advanced_generic | int | ||
| 9 | ALIVE | ALIVE | target_advanced_specific | |||
| 10 | ATTACKER | ATTACKER | subject10 | complex | ||
| 14 | GROUP LEVEL | GROUP LEVEL OF {} | target_advanced_generic | int | ||
| 15 | ALIVE IN SLOT | ALLY IN SLOT {} | int_right | ALIVE | alive | |
| 16 | GENDER CHECK | AT LEAST ONE ENEMY ALIVE HAS GENDER | gender | |||
| 17 | GFORCE OBTAINED | Gforce to be stolen | const | Not yet stolen | const | [200, 204] |
| 18 | ODIN OBTAINED | {} POSSESS ODIN | target_advanced_generic | int | ||
| 19 | COUNTDOWN | COUNTDOWN | int | |||
| 20 | STATUS OF ALL IN TEAM | STATUS OF ALL IN {} | target_advanced_generic | status_ai | ||
| >20 | Var | var | var_name | int |
If the SubjectID is > 20, it means the subject is of type var, the ConditionLeftPart is the ID of the var and the ConditionRightPart the value of the var to be compared
Additional info:
Here are additional info for some values
HP OF SPECIFIC TARGET
The ConditionLeftPart 0xCB (203) is persists across battles
- Byte #1: 0x00 (Self / Last command user) or 0x01 (Any ally / opponent)
- Byte #2:
- 0xC8 (200): Own HP / Any opponent’s HP
- 0xC9 (201): Unused / Any ally’s HP
- 0xCB (203): Last command user’s HP (persists across battles) / Unused
- Byte #4: Remaining HP percentage
- 0x0A (10) = 100%
- 0x09 (9) = 90%
- . . .
2. Random Number Check
- Byte #1: 0x02
- Byte #2: Exclusive upper bound of range (0 to this value - 1)
- Byte #4: Number to compare against
3. Status Check
At least 1 member:
- Byte #1: 0x04 (Self / Last command user) or 0x05 (Any ally / opponent)
- Byte #2:
- 0xC8 (200): Self / Any opponent
- 0xC9 (201): Unused / Any ally
- 0xCB (203): Last command user / Unused
- Byte #4: Status ID
All members of party:
- Byte #1: 0x14 (20)
- Byte #2:
- 0xC8 (200): Opposing part
- 0xC9 (201): Own party
- Byte #4: Status ID
4. Alive Count Check
- Byte #1: 0x06
- Byte #2:
- 0xC8 (200): Opposing party
- 0xC9 (201): Own party
- Byte #4: Number to compare against
5. Specific Entity Alive Check
- Byte #1: 0x09
- Byte #2: 0xC8 (Unused?)
- Byte #4: Entity ID
6. Last Action / Turn Count Check
- Byte #1: 0x0A (10)
- Byte #2:
- 0x00: Damage Type (Byte #4: 0x00 = Physical, 0x01 = Magical)
- 0x01: Last command user ID (Byte #4 = Entity ID)
- 0x02: Turn Counter that increases as soon as the ATB bar is full (Byte #4 = Number)
- 0x03: Command (Byte #4: 0x01 = Attack, 0x02 = Magic, 0x04 = Item, 0x06 = Draw, 0xFE = GF)
- 0x04: ID (Byte #4: Magic/Item/GF ID)
- 0x05: Element (Byte #4 = Element ID)
- 0xCB (203): Last command user party (Byte #4: 0xC8 = Own part, 0xC9 = Opposing party) TO BE TESTED
7. Entity Alive Check (In specific slot)
- Byte #1: 0x0F (15)
- Byte #2: 0xC8 (Unused?)
- Byte #4: Target slot + 3 (e.g., 0x03 for slot 0)
8. Sex check
- Byte #1: 0x10 (16)
- Byte #4:
- 0xCA (202): Male
- 0xCB (203): Female
9. GF Check (Not yet obtained)
- Byte #1: 0x11 (17)
- Byte #2: 0xC8 (Unused?)
- Byte #4: 0xCC (204)
10. Odin Acquisition Check (true if obtained)
- Byte #1: 0x12 (18)
- Byte #2: 0xC8 (Unused?)
- Byte #4: 0x00 / 0x01 (true / false)
11. Timer Check
- Byte #1: 0x13 (19)
- Byte #2: 0xC8 (Unused?)
12. Variable Check
- Byte #1: Variable ID
- Byte #2: 0xC8 (Unused?)
- Byte #4: Value to compare against
Some info for memory help on variable check for local variable: To work as expect, it needs to be 0xC8 (200). But values of 0xCB and value from 220 to 227 (like the local variable ID).
For local var. If var == subjectID, then v93 = BATTLE_VAR if 0xC8, v92 = attacker_slot_id if 0xCB, v92 = last_attacker_slot_id
Opcode 0x03 (3) - prepareMagic
Summary
Stores a magic ID in the monster’s stored action, so that it may be used by the opcode silentUse.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x03 | prepareMagic | 1 | Stores magic ID for later use |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Magic ID | Magic | Magic ID |
Opcode 0x04 (4) - target
Summary
This opcode defines a target, it must be used before any opcode that requires a target (like launching an ability).
If the target is a specific playable character who isn’t currently targetable, the character in the slot of the original target -1 will be targeted, if the original target was slot 0, the new target will be slot 1 instead.
Note that the original target will still be targeted by opcodes draw and blowAway (TO BE TESTED: silentUse).
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x04 | target | 2 | Print text |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Target | TargetBasic | The target |
OR
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Var | LocalVar | Var containing target |
OR
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Monster ID | c0mxxx.dat files ID + 0x10 | Monster ID |
Opcode 0x06 (6) - usePrepared
Summary
Uses the monster’s stored action.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x06 | usePrepared | 1 | Uses stored action |
Parameters
None
Opcode 0x07 (7) - prepareMonsterAbility
Summary
Stores an monster ability ID in the monster’s stored action, so that it may be used by the opcode silentUse.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x07 | prepareMonsterAbility | 1 | Stores monster ability ID for later use |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Monster Ability ID | Monster Ability ID | Monster Ability ID |
Opcode 0x08 (8) - die
Summary
Causes monster that executes this opcode to die.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x08 | die | 1 | Causes monster to die |
Parameters
None
Opcode 0x0B (11) - useRandom
Summary
Picks one of 3 abilities to use randomly, then uses it.
Requires target to have been used.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x0B | useRandom | 4 | Use random ability between 3 |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | MonsterLineAbility1 | MonsterLineAbility | The first ability line |
| 2 | 1 | MonsterLineAbility2 | MonsterLineAbility | The second ability line |
| 3 | 1 | MonsterLineAbility3 | MonsterLineAbility | The third ability line |
Opcode 0x0C (12) - use
Summary
Use one ability, requires target to have been used.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x0C | use | 2 | Use one ability |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | MonsterLineAbility | MonsterLineAbility | The ability line |
Opcode 0x0D (13) - noOp13
(Previously documented as unknown13; renamed once its behavior was confirmed.)
Confirmed via decompile (2026-07): reads its 1-byte parameter into a scratch variable that nothing consumes, then continues to the next opcode (it shares its exit path with doNothing). It is a true 2-byte no-op with no side effect. Not used by any monster in the vanilla game.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x0D | noOp13 | 2 | No-op; parameter is read then discarded |
Opcode 0x0E (14) - var
Summary
Sets local variable that will be only accessible by this monster during the battle.
Can also be used to initialize a battle variable, in case the battle variable already has a value this opcode will be ignored.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x0E | var | 3 | Set local var to specific value |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Var | LocalVar | The var to store the data |
| 2 | 1 | Value | int | The value var is set to |
If Value is 203, the ID of the Last Attacker will be stored in the variable.
Opcode 0x0F (15) - bvar
Sets battle variable (accessible by all monsters).
Summary
Sets global variable (accessible by all monsters)
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x0F | bvar | 3 | Set battle var to value |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Var | LocalVar | The var to store the data |
| 2 | 1 | Value | int | The value var is set to |
Opcode 0x11 (17) - gvar
Summary
Sets savemap variable (not sure how it it stored).
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x11 | gvar | 3 | Set savemap var to value |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Var | SavemapVar | The var to store the data |
| 2 | 1 | Value | int | The value var is set to |
Opcode 0x12 (18) - add
Summary
Adds value to local variable that will be only accessible by this monster during the battle.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x12 | add | 3 | Add value to local var |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Var | LocalVar | The var to store the data |
| 2 | 1 | Value | int | The value to add to the var |
If Value is equal to 0xCB (203), then the added value is equal to the last attacker’s slot.
Opcode 0x13 (19) - badd
Summary
Adds value to battle var (accessible by all monsters).
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x13 | badd | 3 | Add value to battle var |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Var | GlobalVar | The var to store the data |
| 2 | 1 | Value | int | The value to add to the var |
Opcode 0x15 (21) - gadd
Summary
Adds value to savemap var (not sure where it is stored).
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x15 | gadd | 3 | Add value to savemap var |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Var | SavemapVar | The var to store the data |
| 2 | 1 | Value | int | The value to add to the var |
Opcode 0x16 (22) - recover
Summary
Sets remaining HP to max HP.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x16 | recover | 1 | Sets remaining HP to max HP |
Parameters
None
Opcode 0x17 (23) - setEscape
Summary
Allows/Disallows escaping in the current battle.
Correction: the underlying engine name for this opcode is cannotEscape (reference data comment: “Deactivate Run away”), which implies the boolean below is the opposite polarity of what was previously documented here. This needs to be confirmed in-game: it is likely that True blocks escape rather than allowing it.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x17 | setEscape | 2 | Sets ability to escape |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | EscapeActivated | Bool | Previously documented as “True to allow escape, False to disallow it” — likely backwards, see correction above. TO BE TESTED. |
Opcode 0x18 (24) - printSpeed
Summary
Displays a battle message, respecting the battle message speed setting.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x18 | printSpeed | 2 | Print text with battle message speed setting |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | TextID | int | The index of the text |
Texts are defined in section 7 of c0mxxx.dat files.
Each text has an ID, starting from 0 and incrementing with each subsequent text.
TextID corresponds to this ID.
Opcode 0x1C (28) - waitText
Summary
Waits for a specified amount of texts to finish displaying before resuming the battle.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x18 | waitText | 2 | Waits for text/s to finish displaying |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | NbText | int | The number of texts to wait for |
Opcode 0x1D (29) - leave
Summary
Makes the monster in a specified encounter slot leave combat.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x1D | leave | 2 | Makes a monster leave combat |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Target | int | Monster that’s made to leave |
Target is the slot in which the monster currently is in the fight.
Note that if 200 is used, the monster executing this opcode will be used as Target.
Opcode 0x1F (31) - enter
Summary
Makes the monster in a specified encounter slot enter combat, by setting it’s Enabled, NOT Targetable and NOT Loaded flags to true.
Whilst possible, it is not advisable to use enter on an encounter slot if the monster in that slot is currently in the fight.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x1F | enter | 2 | Makes a monster enter combat |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Target | int | Encounter slot of the monster |
Target is the monster’s encounter slot, as defined in scene.out.
Opcode 0x20 (32) - waitTextFast
Summary
This is not used by any monster, analyse of the code is not complete, but it seems to be like waitText but 2 times faster.
Wait the previous message like wait text
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x20 | waitTextFast | 2 | Waits for text/s to finish displaying, twice as fast as waitText |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | NbText | int | The number of texts to wait for |
Opcode 0x24 (36) - fillAtb
Summary
Fills the monster’s ATB bar, readying it for its turn.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x24 | fillAtb | 1 | Fills monster’s ATB bar |
Parameters
None
Opcode 0x25 (37) - setScanText
Summary
Sets the extra scan text to a specific battle text. This text can be found in the bottom left section of the scan screen, where info such as HP, monster name, level and elemental affinities is shown.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x25 | setScanText | 2 | Sets extra scan text |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | TextID | int | The index of the text |
Texts are defined in section 7 of c0mxxx.dat files.
Each text has an ID, starting from 0 and incrementing with each subsequent text.
TextID corresponds to this ID.
Opcode 0x28 (40) - statChange
Summary
Sets the multiplier for a chosen stat.
The base stat itself remains unchanged, but for calculations during battle, the stat used is affected by the multiplier.
For example, if statChange is used to increase a stat to 500% of its original value, setting the multiplier back to 100% will restore it to its base value.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x28 | statChange | 3 | Changes a stat |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Stat | Stat | The stat that will be changed |
| 2 | 1 | Multiplier | int | The multiplier for said stat |
Note that Multiplier is multiplied by 10, so for example if its set to 0x28 (40), the stat will be multiplied by 0x28 * 0x0A = 0x190 (40 * 10 = 400)
Opcode 0x29 (41) - draw
Summary
Makes previous ability draw and store a randomly selected magic from target (use after opcode useRandom or use).
Since monsters can only hold one drawn magic at a time, using draw again will overwrite the previously stored magic.
In the case where draw is used on a playable character that has no magic or a monster, the message showing what magic was stolen will appear only the first time and a nameless magic with no effect that looks and sounds like Cure will be stored.
If a monster uses draw on itself and then uses cast, the game will crash.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x29 | draw | 1 | Draws magic |
Parameters
None
Opcode 0x2A (42) - cast
Summary
Casts magic that’s been stored by using the draw opcode.
If no magic is stored, does nothing.
Using this after the monster used draw on itself will crash the game.
Note that casting magic with this opcode will not consume the stored magic.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x2A | cast | 1 | Casts drawn magic |
Parameters
None
Opcode 0x2E (46) - blowAway
Summary
Makes previous ability remove a randomly selected magic from the target’s inventory (use after opcode useRandom or use).
Note that if the blown-away magic was junctioned to a stat, Elemental Attack/Defense, or Status Attack/Defense, the affected attribute will lose its junction bonus.
Does nothing if the target has no magic.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x2E | blowAway | 1 | Blow away magic |
Parameters
None
Opcode 0x2F (47) - targetable
Summary
Sets this monster’s NOT Targetable flag to False.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x2F | targetable | 1 | Makes monster targetable |
Parameters
None
Opcode 0x30 (48) - untargetable
Summary
Sets this monster’s NOT Targetable flag to True.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x30 | untargetable | 1 | Makes monster untargetable |
Parameters
None
Opcode 0x34 (52) - enable
Summary
Sets this monster’s Enabled flag to True.
| Opcode | IfritAI name | Size | Short description |
|---|---|---|---|
| 0x34 | enable | 1 | Enables monster |
Parameters
| Position | Size | Name | Type | Short description |
|---|---|---|---|---|
| 1 | 1 | Target | int | Encounter slot of the monster |
Target is the monster’s encounter slot, as defined in scene.out.
Additional opcodes (not yet fully written up)
The 30 opcodes above are the ones that had a full write-up. IfritAI’s reference data (ai_vanilla.json) confirms 59 opcodes total exist, so the following 25 were missing from this page entirely. They’re real, working opcodes — just not documented in as much depth yet. Descriptions below are confirmed against the decompiled engine code (2026-07); anyone expanding these into full sections (with worked examples, like the ones above) would be doing the wiki a favor.
Six of these opcodes were previously misnamed (or named before their behavior was understood) in IfritAI’s reference data and have been renamed after this verification pass — see the callouts below:
| Old name | New name | Why |
|---|---|---|
remain | vanish | The code silently removes the monster from battle — the opposite of “remaining” |
addMaxHP | addCurrentHP | The code writes current HP, never max HP |
anim | setHitAnim | It doesn’t play an animation — it sets the hit/death reaction animation of the current hit (pre-counter only) |
unknown13 | noOp13 | Behavior is now known: a verified no-op |
enterAlt | enterWithAnim | “Alt” said nothing; the actual difference from enter is the played entrance animation |
targetAllySlot | targetBattleSlot | The parameter is a raw battle slot, and nothing restricts it to allies |
| Opcode | IfritAI name | Size | Description | Notes |
|---|---|---|---|---|
| 0x05 (5) | prepareAnim | 1 | Sets the animation-sequence ID (section 5) that will be used when the stored action is launched with usePrepared. Note that use/useRandom overwrite this with the ability’s own animation from section 7, so it only has an effect on the prepare→usePrepared path | Used by monsters 11, 12, 91, 109, 122, 124, 126 |
| 0x09 (9) | setHitAnim (renamed from anim) | 1 | Sets the hit/death reaction animation (a section 5 sequence ID) that this monster plays for the hit currently being resolved. How it works: when a hit lands, the engine first derives the default reaction animation from the attack’s kernel data (its animationTriggered byte, with miss = dodge and crit = stagger overrides), then runs the monster’s pre-counter section, then commits the value into the hit data. An opcode 9 call inside pre-counter therefore lands in exactly the right window to override the reaction. Outside pre-counter (Init/Turn/Counter/Death) the value is recomputed before being read, so it does nothing — which is why earlier testing from the Turn section showed “no impact in game”. The engine’s own default for a normal death is sequence 3, which is why almost every vanilla usage is setHitAnim(3) guarded by a Death-status check (force the standard death animation), while special monsters use custom sequences (12–16, 36, 49, 59) for unique death/hit reactions | Renamed 2026-07. Used by ~25 vanilla monsters, exclusively in the pre-counter section |
| 0x19 (25) | doNothing | 1 | Reads and discards one padding byte; a true no-op | |
| 0x1A (26) | printAndLock | 1 | Displays a battle message and blocks further script execution until that message finishes displaying | |
| 0x1B (27) | enterWithAnim (renamed from enterAlt) | 2 | Makes the monster in encounter slot {param1} enter combat with an entrance action/animation played immediately (battle slot = param1 + 3). Internally it queues and executes the same command family as the Phoenix-Pinion/kamikaze effects (sub-type 3), raises the same internal flag as prepareSummon, then makes the monster present (loaded, visible, targetable) once the entrance animation unlocks. This is the “scripted reinforcement with fanfare” variant of enter: compare c0m074 (Biggs), which uses enterWithAnim(1, 0) for Wedge’s dramatic entrance but plain enter(2) for a silent reinforcement | Renamed 2026-07. Param2 is passed down as the command’s target-slot sub-parameter; its exact role is still unconfirmed |
| 0x1E (30) | specialAction | 1 | Queues a special action by ID, indexing the special action list | |
| 0x22 (34) | printAlt | 2 | Displays a battle message with a configurable wait/delay time (2nd parameter). Not used by any monster script | |
| 0x23 (35) | jump | 2 | The raw unconditional jump — this is what implements if’s else/endif branches under the hood | See Enemy AI VM Runtime |
| 0x26 (38) | targetStatus | 4 | Builds a target mask from all actors matching a status condition (target type, comparator, status ID) | |
| 0x27 (39) | autoStatus | 2 | Sets or clears a status flag on self. Status IDs ≥ 16 write to the status_2 field (offset by 16), IDs < 16 write to status_1 | |
| 0x2B (43) | targetBattleSlot (renamed from targetAllySlot) | 1 | Targets a battle slot directly: sets the pending target mask to 1 << param, with no +3 conversion — so the parameter is a raw battle slot (0–2 = characters, 3+ = monsters), not an encounter slot as previously believed, and nothing in the code restricts it to allies. The old “seems to require assignSlot” note is now fully explained by its only vanilla user, c0m124 (Mobile Type 8): the script first pins its two probes with assignSlot(4, 4) and assignSlot(5, 5) (encounter slot into that same-numbered battle slot — which is why the parameter was mistakable for an encounter slot), then uses targetBattleSlot(4) / targetBattleSlot(5) to aim its revival abilities at them. Without the assignSlot pinning, the probes’ battle slots wouldn’t be predictable and the hardcoded values would miss | Renamed 2026-07 |
| 0x2C (44) | vanish (renamed from remain) | 0 | Silently removes this monster from battle: sets Death status, Not Targetable, and clears the “enabled” flag — with no death sequence/message. The old name remain described the opposite of what the code does | Renamed 2026-07. Only known to be used by monster IDs 87 and 91 — worth checking those scripts to see if it’s used to hide a “shell” slot while another part of the same monster keeps fighting |
| 0x2D (45) | elemDmgMod | 3 | Writes a 16-bit value into a per-monster local-var slot indexed by element ID; some later damage calculation presumably reads it back as a resistance modifier. IfritAI encodes the authored percentage as 900 - percent when compiling — this transform is unverified against actual in-game damage output | |
| 0x31 (49) | giveGF | 1 | Gives the player a specific GF and records it in the save data | Used by Tomberry |
| 0x32 (50) | prepareSummon | 0 | Sets an internal “preparing a summon” flag; no parameters | |
| 0x33 (51) | activate | 0 | Queues an “activate” battle task; no parameters | |
| 0x35 (53) | loadAndTargetable | 1 | Finalizes a monster’s entrance: once the current animation unlocks, clears the NOT Loaded, NOT Visible and NOT Targetable flags on the given battle slot, recomputes the enable masks and starts the slot’s periodic effects (Regen ticks etc.). Parameter value 209 means “the slot filled by the most recent enter/assignSlot” | Typical pattern: assignSlot → … → loadAndTargetable(209) |
| 0x36 (54) | gilgamesh | 0 | Sets the savegame flag that marks Gilgamesh as available | |
| 0x37 (55) | giveCard | 1 | Adds a specific Triple Triad card to the battle’s card-drop list | |
| 0x38 (56) | giveItem | 1 | Adds a specific item to the battle’s item drop/steal list | |
| 0x39 (57) | gameOver | 0 | Forces a Game Over | |
| 0x3A (58) | targetableSlot | 1 | Clears only the NOT Targetable flag on the given battle slot (unlike loadAndTargetable, it does not touch NOT Loaded / NOT Visible), then queues an enable-mask recompute. The “unexpected results” reported when testing it likely come from using it on a slot that isn’t loaded/visible yet, or from passing an encounter slot where a battle slot is expected | |
| 0x3B (59) | assignSlot | 2 | Brings the monster from encounter slot {param1} into battle slot {param2} — identical to enter except the destination battle slot is explicit. Battle slot 0 = auto-search the first free monster slot (confirmed in code; fails with slot 255 if none is free). Loads the monster’s skeleton/weapon/animations, records the slot as “last entered” (consumable by loadAndTargetable(209)), and makes it present once the animation unlocks | Used by monsters 124 and 126 |
| 0x3C (60) | addCurrentHP (renamed from addMaxHP) | 1 | Adds a signed 16-bit value directly to the monster’s current HP. The old name and its own description (“Increase max HP (but not current)”) both claimed it touched max HP — the decompiled code only ever writes current_hp, never max_hp | Renamed 2026-07. Negative values presumably work as direct damage; worth confirming there’s no clamp to max HP |
| 0x3D (61) | proofOfOmega | 0 | Gives the player “Proof of Omega”. The item ID (127) is hardcoded in the engine function, not passed as a script parameter | Only used by monster ID 114 (Omega Weapon) |