|
# Pokemon Battle System Architecture Analysis |
|
|
|
## Overview |
|
|
|
The Pokemon Emerald battle system is a complex, state-driven architecture that orchestrates combat between multiple Pokemon. Rather than being a single monolithic system, it's composed of several interconnected subsystems that handle different aspects of battle functionality. |
|
|
|
## High-Level Architecture |
|
|
|
### Core Components |
|
|
|
1. **Battle State Machine** (`battle_main.c`) |
|
2. **Battle Controllers** (`battle_controller_*.c` files) |
|
3. **Animation System** (`battle_anim*.c` files) |
|
4. **Script Engine** (`battle_script_commands.c`) |
|
5. **User Interface** (`battle_interface.c`) |
|
6. **Data Management** (Pokemon storage and battle state) |
|
|
|
## 1. Battle State Management |
|
|
|
### Main Battle Loop |
|
The battle system is built around a main function pointer (`gBattleMainFunc`) that controls the overall battle flow: |
|
|
|
```c |
|
// Central battle state function pointer |
|
void (*gBattleMainFunc)(void); |
|
``` |
|
|
|
### Key State Variables |
|
- `gBattleStruct` - Central battle state container |
|
- `gBattleMons[MAX_BATTLERS_COUNT]` - Active Pokemon data |
|
- `gBattlerPositions[]` - Battler position tracking |
|
- `gBattleTypeFlags` - Battle mode flags (wild, trainer, double, etc.) |
|
|
|
gBattleStruct definition: |
|
```c |
|
struct BattleStruct |
|
{ |
|
u8 turnEffectsTracker; |
|
u8 turnEffectsBattlerId; |
|
u8 unused_0; |
|
u8 turnCountersTracker; |
|
u8 wrappedMove[MAX_BATTLERS_COUNT * 2]; // Leftover from Ruby's ewram access. |
|
u8 moveTarget[MAX_BATTLERS_COUNT]; |
|
u8 expGetterMonId; |
|
u8 unused_1; |
|
u8 wildVictorySong; |
|
u8 dynamicMoveType; |
|
u8 wrappedBy[MAX_BATTLERS_COUNT]; |
|
u16 assistPossibleMoves[PARTY_SIZE * MAX_MON_MOVES]; // Each of mons can know max 4 moves. |
|
u8 focusPunchBattlerId; |
|
u8 battlerPreventingSwitchout; |
|
u8 moneyMultiplier; |
|
u8 savedTurnActionNumber; |
|
u8 switchInAbilitiesCounter; |
|
u8 faintedActionsState; |
|
u8 faintedActionsBattlerId; |
|
u16 expValue; |
|
u8 scriptPartyIdx; // for printing the nickname |
|
u8 sentInPokes; |
|
bool8 selectionScriptFinished[MAX_BATTLERS_COUNT]; |
|
u8 battlerPartyIndexes[MAX_BATTLERS_COUNT]; |
|
u8 monToSwitchIntoId[MAX_BATTLERS_COUNT]; |
|
u8 battlerPartyOrders[MAX_BATTLERS_COUNT][PARTY_SIZE / 2]; |
|
u8 runTries; |
|
u8 caughtMonNick[POKEMON_NAME_LENGTH + 1]; |
|
u8 unused_2; |
|
u8 safariGoNearCounter; |
|
u8 safariPkblThrowCounter; |
|
u8 safariEscapeFactor; |
|
u8 safariCatchFactor; |
|
u8 linkBattleVsSpriteId_V; // The letter "V" |
|
u8 linkBattleVsSpriteId_S; // The letter "S" |
|
u8 formToChangeInto; |
|
u8 chosenMovePositions[MAX_BATTLERS_COUNT]; |
|
u8 stateIdAfterSelScript[MAX_BATTLERS_COUNT]; |
|
u8 unused_3[3]; |
|
u8 prevSelectedPartySlot; |
|
u8 unused_4[2]; |
|
u8 stringMoveType; |
|
u8 expGetterBattlerId; |
|
u8 unused_5; |
|
u8 absentBattlerFlags; |
|
u8 palaceFlags; // First 4 bits are "is <= 50% HP and not asleep" for each battler, last 4 bits are selected moves to pass to AI |
|
u8 field_93; // related to choosing pokemon? |
|
u8 wallyBattleState; |
|
u8 wallyMovesState; |
|
u8 wallyWaitFrames; |
|
u8 wallyMoveFrames; |
|
u8 lastTakenMove[MAX_BATTLERS_COUNT * 2 * 2]; // Last move that a battler was hit with. This field seems to erroneously take 16 bytes instead of 8. |
|
u16 hpOnSwitchout[NUM_BATTLE_SIDES]; |
|
u32 savedBattleTypeFlags; |
|
u8 abilityPreventingSwitchout; |
|
u8 hpScale; |
|
u8 synchronizeMoveEffect; |
|
bool8 anyMonHasTransformed; |
|
void (*savedCallback)(void); |
|
u16 usedHeldItems[MAX_BATTLERS_COUNT]; |
|
u8 chosenItem[MAX_BATTLERS_COUNT]; // why is this an u8? |
|
u8 AI_itemType[2]; |
|
u8 AI_itemFlags[2]; |
|
u16 choicedMove[MAX_BATTLERS_COUNT]; |
|
u16 changedItems[MAX_BATTLERS_COUNT]; |
|
u8 intimidateBattler; |
|
u8 switchInItemsCounter; |
|
u8 arenaTurnCounter; |
|
u8 turnSideTracker; |
|
u8 unused_6[3]; |
|
u8 givenExpMons; // Bits for enemy party's Pokémon that gave exp to player's party. |
|
u8 lastTakenMoveFrom[MAX_BATTLERS_COUNT * MAX_BATTLERS_COUNT * 2]; // a 3-D array [target][attacker][byte] |
|
u16 castformPalette[NUM_CASTFORM_FORMS][16]; |
|
union { |
|
struct LinkBattlerHeader linkBattlerHeader; |
|
u32 battleVideo[2]; |
|
} multiBuffer; |
|
u8 wishPerishSongState; |
|
u8 wishPerishSongBattlerId; |
|
bool8 overworldWeatherDone; |
|
u8 atkCancellerTracker; |
|
struct BattleTvMovePoints tvMovePoints; |
|
struct BattleTv tv; |
|
u8 unused_7[0x28]; |
|
u8 AI_monToSwitchIntoId[MAX_BATTLERS_COUNT]; |
|
s8 arenaMindPoints[2]; |
|
s8 arenaSkillPoints[2]; |
|
u16 arenaStartHp[2]; |
|
u8 arenaLostPlayerMons; // Bits for party member, lost as in referee's decision, not by fainting. |
|
u8 arenaLostOpponentMons; |
|
u8 alreadyStatusedMoveAttempt; // As bits for battlers; For example when using Thunder Wave on an already paralyzed Pokémon. |
|
}; |
|
``` |
|
|
|
### Battle Flow States |
|
The battle progresses through distinct phases: |
|
1. **Initialization** - Setup battlers, load graphics |
|
2. **Turn Selection** - Player/AI choose actions |
|
3. **Action Resolution** - Execute moves in priority order |
|
4. **Turn Cleanup** - Apply end-of-turn effects |
|
5. **Battle End** - Victory/defeat/capture resolution |
|
|
|
## 2. Controller Architecture |
|
|
|
### Battler Controllers |
|
Each battler (player, opponent, partner, etc.) has its own controller that handles: |
|
- Input processing |
|
- Animation requests |
|
- Data updates |
|
- UI management |
|
|
|
```c |
|
// Controller function pointer array |
|
void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(void); |
|
``` |
|
|
|
### Controller Types |
|
- **Player Controller** (`battle_controller_player.c`) - Human input |
|
- **Opponent Controller** (`battle_controller_opponent.c`) - AI decisions |
|
- **Link Controllers** - Network battlers |
|
- **Special Controllers** - Safari Zone, Wally, etc. |
|
|
|
### Command System |
|
Controllers communicate via command buffers: |
|
```c |
|
// Controller commands (simplified) |
|
CONTROLLER_GETMONDATA // Request Pokemon data |
|
CONTROLLER_CHOOSEACTION // Select battle action |
|
CONTROLLER_MOVEANIMATION // Play move animation |
|
CONTROLLER_HEALTHBARUPDATE // Update HP display |
|
``` |
|
|
|
## 3. Animation System |
|
|
|
### Animation Pipeline |
|
Animations are script-driven and hierarchical: |
|
|
|
1. **Move Animation Selection** - Choose appropriate animation |
|
2. **Script Execution** - Run animation script commands |
|
3. **Sprite Management** - Create/animate visual elements |
|
4. **Audio Synchronization** - Play sound effects |
|
5. **Cleanup** - Remove temporary sprites |
|
|
|
### Animation Scripts |
|
Each move has an associated animation script: |
|
```c |
|
const u8 *const gBattleAnims_Moves[]; |
|
``` |
|
|
|
### Animation Commands |
|
Scripts use specialized commands: |
|
- `loadspritegfx` - Load sprite graphics |
|
- `createsprite` - Create animated sprite |
|
- `playse` - Play sound effect |
|
- `delay` - Wait specified frames |
|
- `monbg` - Modify Pokemon background |
|
|
|
### Visual Task System |
|
Complex animations use visual tasks for frame-by-frame control: |
|
```c |
|
u8 gAnimVisualTaskCount; // Active visual task counter |
|
``` |
|
|
|
## 4. Pokemon Data Storage and Retrieval |
|
|
|
### Battle Pokemon Structure |
|
Active Pokemon are stored in `gBattleMons[]`: |
|
```c |
|
struct BattlePokemon { |
|
u16 species; |
|
u16 attack; |
|
u16 defense; |
|
u16 speed; |
|
u16 spAttack; |
|
u16 spDefense; |
|
u16 moves[MAX_MON_MOVES]; |
|
u32 hp; |
|
u8 level; |
|
u8 pp[MAX_MON_MOVES]; |
|
// ... status, abilities, etc. |
|
}; |
|
``` |
|
|
|
### Data Synchronization |
|
The system maintains consistency between: |
|
- **Party Data** - Full Pokemon in party array |
|
- **Battle Data** - Reduced battle-specific data |
|
- **Display Data** - UI representation |
|
|
|
### Data Flow |
|
1. **Load Phase** - Copy party data to battle structure |
|
2. **Battle Phase** - Modify battle data only |
|
3. **Update Phase** - Sync changes back to party |
|
|
|
## 5. Menu Systems and Input Handling |
|
|
|
### Action Selection Menu |
|
The main battle menu uses a state-driven approach: |
|
|
|
```c |
|
static void PlayerHandleChooseAction(void) { |
|
// Present: Fight, Pokemon, Bag, Run options |
|
// Handle input and set action type |
|
} |
|
``` |
|
|
|
### Move Selection Interface |
|
Move selection involves: |
|
1. **Display Moves** - Show available moves with PP |
|
2. **Input Handling** - Process directional input |
|
3. **Move Info** - Display type, power, accuracy |
|
4. **Target Selection** - Choose targets for multi-target moves |
|
|
|
### Menu State Management |
|
- Cursor position tracking |
|
- Input validation |
|
- Visual feedback (highlighting, animations) |
|
- Transition between menu levels |
|
|
|
## 6. Action Processing and Effect Application |
|
|
|
### Turn Order Resolution |
|
Actions are sorted by priority: |
|
1. **Switch Pokemon** (highest priority) |
|
2. **Use Items** |
|
3. **Use Moves** (sorted by speed/priority) |
|
4. **Flee** |
|
|
|
### Move Execution Pipeline |
|
When a move is used: |
|
|
|
1. **Validation** - Check if move can be used |
|
2. **Target Selection** - Determine valid targets |
|
3. **Script Execution** - Run move's battle script |
|
4. **Animation** - Play visual/audio effects |
|
5. **Damage Calculation** - Apply damage formulas |
|
6. **Effect Application** - Apply status effects, stat changes |
|
7. **Cleanup** - Update battle state |
|
|
|
### Battle Script System |
|
Moves use script commands for effects: |
|
```c |
|
static void Cmd_attackstring(void); // Display attack message |
|
static void Cmd_attackanimation(void); // Play attack animation |
|
static void Cmd_damagecalc(void); // Calculate damage |
|
static void Cmd_healthbarupdate(void); // Update HP bar |
|
``` |
|
|
|
### Gradual Information Delivery |
|
The system delivers information to players progressively: |
|
|
|
1. **Action Announcement** - "Pokemon used Move!" |
|
2. **Animation Phase** - Visual move animation |
|
3. **Result Messages** - Effectiveness, critical hits |
|
4. **Damage Application** - Gradual HP bar drain |
|
5. **Status Updates** - Additional effects |
|
6. **Turn Cleanup** - End-of-turn effects |
|
|
|
## 7. Key Programming Patterns |
|
|
|
### State Machine Architecture |
|
The battle system extensively uses function pointers for state management: |
|
```c |
|
void (*gBattleMainFunc)(void); // Main battle state |
|
void (*gBattlerControllerFuncs[])(void); // Controller states |
|
void (*gAnimScriptCallback)(void); // Animation states |
|
``` |
|
|
|
### Command-Driven Controllers |
|
Controllers process commands via lookup tables: |
|
```c |
|
static void (*const sPlayerBufferCommands[])(void) = { |
|
[CONTROLLER_GETMONDATA] = PlayerHandleGetMonData, |
|
[CONTROLLER_CHOOSEACTION] = PlayerHandleChooseAction, |
|
// ... |
|
}; |
|
``` |
|
|
|
### Script-Based Effects |
|
Both animations and move effects use bytecode scripts for flexibility: |
|
- Animation scripts control visual presentation |
|
- Battle scripts control game logic and effects |
|
|
|
### Asynchronous Processing |
|
The system handles multiple concurrent operations: |
|
- Animations can run while processing user input |
|
- Multiple battlers can be in different states |
|
- Background tasks handle gradual effects (HP drain, etc.) |
|
|
|
## 8. Memory Management |
|
|
|
### Battle Resources Structure |
|
```c |
|
struct BattleResources { |
|
struct SecretBase *secretBase; |
|
struct ResourceFlags *flags; |
|
struct BattleScriptsStack *battleScriptsStack; |
|
struct AI_ThinkingStruct *ai; |
|
struct BattleHistory *battleHistory; |
|
}; |
|
``` |
|
|
|
### Dynamic Allocation |
|
- Battle resources are allocated at battle start |
|
- Freed when battle ends |
|
- Sprites and animations use temporary allocation |
|
|
|
## 9. Extension Points |
|
|
|
The architecture provides several extension mechanisms: |
|
- **New Move Effects** - Add scripts to move effect table |
|
- **Custom Animations** - Create new animation scripts |
|
- **Battle Types** - Add flags and specialized controllers |
|
- **AI Behaviors** - Extend AI decision trees |
|
|
|
## Summary |
|
|
|
The Pokemon battle system demonstrates sophisticated game architecture through: |
|
|
|
1. **Separation of Concerns** - Controllers, animations, scripts, and UI are distinct |
|
2. **Data-Driven Design** - Moves, animations, and effects defined in data tables |
|
3. **State Management** - Clear state machines for battle flow and individual components |
|
4. **Asynchronous Processing** - Multiple concurrent operations with proper coordination |
|
5. **Extensibility** - Modular design allows for easy addition of new content |
|
|
|
This architecture allows complex battle interactions while maintaining code organization and enabling the rich, interactive experience that defines Pokemon battles. |
|
|