piclets / pokemon_emerald_docs /battle_system_architecture.md
Fraser's picture
Remove problematic public/assets/logo.PNG to fix deployment
50135bd
# 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.