# Starting a Battle from World State

Lecture ID: lecture_w12_d02
Week 12, Day 2

## Objectives
- Identify the FireRed files, structs, functions, constants, data tables, and state variables that implement each major game system.
- Connect week 12 material to the overall course plan.

## Transcript
All right, let’s pick up exactly where yesterday’s lecture left us. Yesterday, we treated include/battle.h as a kind of architectural contract, not just a pile of constants. Today I want you to follow one specific crossing of that contract: the moment where ordinary world state becomes battle state. And I mean that very literally. The player is walking on a map, or a script is running, or a trainer object has noticed the player, and a few frames later the runtime is no longer primarily an overworld runtime. It is preparing the battle engine.

So the question for today is not, “How does a battle turn work?” We are not there yet. The question is, “Who has the authority to start a battle, what data must be gathered before the battle engine can begin, and where is the handoff encoded?” That is the architectural seam for today’s lecture.

Let me put that on the board as a chain. World state, encounter decision, setup record, transition, battle callback, return callback. I’m deliberately saying “record” in the middle there, even though FireRed does not package all of this into one neat modern object the way many of you might in a new engine. The concept is still there. The battle needs a type, an opponent identity, party information, flags, music and transition choices, and a way back to the world or script after the battle resolves. Some of those are globals. Some are event variables. Some are pulled from data tables. Some are encoded in constants defined in battle.h. That distribution is the point.

Now, recall from our prior runtime-shell weeks that the game is not a single while-loop with a giant switch that says overworld, battle, menu, save, and so on. We saw AgbMain establish the runtime responsibilities, and we saw the main callback slots, especially SetMainCallback2, as the way a major mode becomes the active per-frame owner. I’m not going to reteach callbacks or task IDs today. You already used that model in earlier labs. But I do want you to keep that mechanism in your head because starting a battle is one of the cleanest examples of why FireRed’s architecture relies on callbacks as mode ownership.

In the source packet, battle_setup.c is our main bridge file. Notice the include surface in pokefirered/src/battle_setup.c, chunk_494d132a59422c85. It pulls in task.h, overworld.h, pokemon.h, script.h, event_data.h, event_object_movement.h, metatile_behavior.h, field_control_avatar.h, field_player_avatar.h, field_screen_effect.h, field_message_box.h, battle.h, battle_transition.h, and battle_controllers.h among others. That include list is not trivia. It is evidence that battle setup sits between systems. It knows enough about the field to read encounter conditions and manipulate scripts or avatar state, enough about Pokémon data to create or select opponents, enough about battle.h to speak the battle subsystem’s language, and enough about transitions and callbacks to leave the overworld cleanly.

Let’s do a concrete pass. Imagine the player takes a step in tall grass on Route 2. At that instant, the runtime is still in overworld mode. The map is loaded. The player avatar has a position and facing direction. The tile underfoot has metatile behavior. Event variables and flags contain progression state. The player party lives in its own party storage, which we have already treated as a shared model read by summary screens, party menus, and move interfaces. Now some field-side logic decides, probabilistically and conditionally, that this step should produce a wild encounter. The battle engine does not make that decision. That is important. The battle engine is not scanning grass tiles every frame. The field system is doing field work.

Once the decision is made, the setup layer has to translate “a wild encounter happened here” into battle-compatible state. On the board, I’m writing two columns. In the left column: field facts. Map group and number, tile behavior, encounter table selection, player party, script context, flags such as Safari or special area state, and callback to resume field. In the right column: battle facts. Battle type flags, opponent species and level, battler positions, player party slots, enemy party or wild mon buffer, battle script or outcome handling, transition effect, and controller mode.

Pause there. Check for understanding. If I ask, “Does the battle system own the map encounter table?” the answer should be no. It consumes the result of encounter setup. If I ask, “Does the overworld execute move accuracy calculations?” also no. That belongs later, after ownership has passed into battle. The setup layer is where these domains touch without fully absorbing each other.

Now, yesterday we looked at include/battle.h as the battle subsystem map. Today the key chunks are the battler identity and action constants in pokefirered/include/battle.h, chunk_4079f42fde2b4370; the trainer party structs in pokefirered/include/battle.h, chunk_4e3097f639b05787; and the trainer table contract in pokefirered/include/battle.h, chunk_403d30271e2270d6. Those chunks matter because the setup code cannot merely say “fight this guy.” It has to express the battle in terms the battle engine understands.

Let’s unpack that. Battler position constants answer a deceptively deep question: who is sitting in each battle slot? A single wild battle has a player-side battler and an opponent-side battler. A double battle can have left and right positions. Link battles and special cases complicate this further. FireRed’s battle engine needs stable numeric identities for battlers because scripts, controllers, health boxes, sprites, moves, and action buffers all need to agree on which participant is being referenced. So when battle.h defines battler positions and action constants in chunk_4079f42fde2b4370, that is not just “some constants.” That is the shared vocabulary that keeps the battle controller, UI, animation, and logic layers synchronized.

Here is the advanced architecture point: the overworld never needs to know how a battler chooses B_ACTION_USE_MOVE versus B_ACTION_SWITCH. But the setup layer absolutely needs to choose a battle shape that will later make those actions legal. If you set up a Safari battle, the available player actions are not the same as a standard trainer battle. If you set up a trainer battle, running may be disallowed. If you set up a double battle, battler positions and party availability matter. So battle setup is not turn logic, but it constrains the action space that turn logic will later expose.

Now let’s move from wild battles to trainer battles, because trainer battles make the data contract more visible. A trainer in the world begins as an object event plus script behavior. The player walks into range, or presses A, or a script manually starts the encounter. The field side knows which trainer event was involved and usually which trainer ID should be used. But the actual battle party is not stored on that map object as a full party of Pokémon. The trainer ID points into trainer data. That is where battle.h’s trainer party structs and trainer table contract enter.

In pokefirered/include/battle.h, chunk_4e3097f639b05787, the trainer party structs define the possible shapes of a trainer’s party records. This is the familiar FireRed data-driven compromise: not every trainer mon needs the same amount of data. Some trainer Pokémon use default moves and no held item. Some specify an item. Some specify custom moves. Some specify both. The table contract, in pokefirered/include/battle.h, chunk_403d30271e2270d6, gives the setup and battle code a stable way to interpret those records through trainer metadata. So a trainer battle starts from a compact ID, but by the time battle starts, that ID has selected a class, party size, party data format, AI hints or flags, encounter presentation, and the party records themselves.

Let me give you a concrete example in engine terms, not exact line-by-line code. Suppose a Bug Catcher object event on a route triggers a battle. The map event or script identifies a trainer number. The setup code records that trainer number into battle-related state. It sets battle type flags indicating a trainer battle rather than a wild battle. It may prepare trainer approach behavior or message flow first, depending on whether this was line-of-sight detection or direct interaction. Then, when the battle engine initializes enemy party data, it uses the trainer table contract to interpret that trainer’s party records. If the trainer’s party format says “default moves, no item,” then the battle party can be built using species and level plus ordinary move-generation rules. If the format says “custom moves,” the record supplies the move list directly. We covered move slots and PP earlier, so I will not reteach that representation here. The important thing is that party construction crosses from trainer table data into the same battle-compatible Pokémon structure that turn logic will later use.

This is also why your lab this week asks you to design a battle setup record that can start wild, trainer, and scripted battles. I am not asking for that because FireRed literally uses one beautiful clean struct for every case. I am asking because designing that record forces you to account for every cross-system dependency. If your record only has “enemy species” and “enemy level,” it can start a wild battle, maybe. It cannot start a trainer battle because it lacks trainer ID and party table interpretation. If it only has “trainer ID,” it cannot start a scripted legendary encounter unless you also encode battle type, species, level, script continuation, and special flags. If it has no return callback, it can start the battle but cannot responsibly return to the world.

Now look back at the source focus for today. The setup work is anchored in pokefirered/src/battle_setup.c, chunk_0e0ab33ad5402d71, described in the packet as battle setup; chunk_db24b720e895b599, described as setup flow; and chunk_a8e4b22a367933e7, described as battle start handoff. Those names are already telling you the three conceptual phases. First, collect or establish the battle’s identity. Second, move through the field-side setup flow, including scripts, messages, trainer detection, and transition preparation. Third, perform the handoff into the battle runtime.

Let’s slow down on “handoff,” because this is where students often blur terms. A visual transition is not the handoff by itself. The swirl, flash, fade, or trainer intro is presentation. The handoff is the change in runtime ownership and state initialization that makes battle code the next active mode. FireRed often coordinates those together because a player experiences them as one event: screen effect, battle intro, fight menu. But architecturally, you should distinguish them. The transition hides the mode change and gives the player feedback. The callback change makes the mode change real.

Board cue: I’m drawing a horizontal timeline. On the left: overworld callback active. Then field trigger. Then setup writes battle globals and event state. Then transition task or screen effect. Then SetMainCallback2 or equivalent callback scheduling into battle initialization. Then battle main owns the frame. Then battle outcome returns through a callback or script continuation. That is the shape I want you to see.

Now, let’s test that shape against a scripted battle. Scripted battles are important because they prove that “battle starts from world state” does not only mean random encounters or trainer vision. A script can force a battle as part of narrative progression. The script system may set variables, choose a species, choose a trainer, display text, start a battle, and then branch based on outcome. That means battle setup must cooperate with script context. The battle cannot simply overwrite the world and forget who called it. After the battle, the script may need to continue, set flags, give an item, remove an object event, or warp the player. That is why battle_setup.c including script.h and event_data.h in chunk_494d132a59422c85 is meaningful evidence. Battle setup is not isolated combat code. It is a world-state bridge.

Here is a useful comparison to earlier weeks. In the move relearner lecture, we treated the menu as a state machine that reads party data, presents choices, mutates move slots, and returns to the caller. That was a localized flow. Battle setup is similar in the sense that it also has a caller, state preparation, UI or transition, mutation, and return. But the scale is larger. It crosses the entire mode boundary. It may affect music, object events, scripts, party health, money, trainer flags, and map continuation. So we reuse the state-machine mental model, but we apply it at system level.

Let’s do a check for understanding with a deliberately wrong design. Suppose I write a clone engine where the overworld object for a trainer directly contains an array of six full monster structs, and when the player touches the trainer, the overworld calls Battle.start(enemyParty). No trainer ID, no trainer table, no battle flags, no return callback, no script context. What breaks?

First, data reuse breaks. The trainer object becomes heavy and map-bound. Second, rematches and scripted variations become awkward because the same trainer identity is not cleanly separated from the object instance. Third, battle type behavior becomes implicit or hard-coded. Fourth, returning to the correct script or overworld state becomes fragile. Fifth, the engine loses the ability to share battle setup between trainer sight, direct talk, scripted battle, and special cases. FireRed’s architecture is older and very C-like, but it avoids those particular failures by letting identifiers, flags, tables, globals, and callbacks cooperate.

Now, I want to connect this to assessment expectations. By week 12, after homework and quizzes that asked you to identify files, structs, functions, constants, data tables, and state variables, I expect you to be precise. Saying “the battle code starts the battle” is not a sufficient answer anymore. A strong answer says: battle_setup.c is the bridge from field conditions and script triggers into battle initialization; battle.h provides the constants and struct contracts; trainer IDs are resolved through trainer table data; battler positions and action constants define the battle engine vocabulary; the main callback mechanism transfers frame ownership; and event variables or callbacks preserve continuation after battle. That is the level of architectural tracing expected on the week 12 quiz and the midterm.

Let me say that again in a more natural debugging form. If a trainer battle starts with the wrong Pokémon, where do you inspect? You do not begin in the move accuracy code. You inspect the trainer ID from the event or script, then the trainer table contract, then the party format and party data. If a wild battle starts in the wrong area, you inspect encounter decision and map context before battle initialization. If the battle starts correctly but returns to the wrong place afterward, you inspect callback and script continuation state. If the wrong action menu appears, you inspect battle type flags and battle mode setup, not the overworld trainer object.

Now let’s return to the three battle_setup.c chunks. In chunk_0e0ab33ad5402d71, our source focus labels the central battle setup material. Treat this as the region where the engine gathers the facts. In chunk_db24b720e895b599, the setup flow is where those facts become a staged transition rather than an instantaneous jump. In chunk_a8e4b22a367933e7, the battle start handoff is where the system commits: the battle runtime is about to own execution. I’m phrasing that carefully because in C source the logic may be split across static helpers, tasks, and callbacks rather than a single function called “CommitBattle.” Your architectural reading should follow responsibility, not just function names.

A common advanced mistake is to look only for one canonical entry point. There are multiple ways to arrive at battle setup: random wild encounter, trainer line-of-sight, trainer interaction, scripted trainer battle, scripted wild battle, special modes. The elegance is not that all paths are syntactically identical. The elegance is that they converge on the battle subsystem contract before battle begins. That contract includes flags, parties, battler identities, controller expectations, and return behavior.

Let’s put a small pseudo-record on the board, just as a thinking tool. I would write: battleKind, battleFlags, mapContext, trainerId optional, wildSpecies optional, wildLevel optional, opponentPartySource, playerPartySource, scriptContinuation, returnCallback, transitionKind, musicKind, outcomeDestination. Again, this is not me claiming FireRed has exactly this struct. This is the conceptual record your lab design should approximate. Every field should answer a question the real engine must answer somewhere.

For example, battleKind distinguishes wild, trainer, Safari, scripted, and link or special cases. battleFlags communicate constraints to battle initialization and later action menus. mapContext explains where the encounter came from, especially for wild tables and post-battle return. trainerId selects the trainer table entry. wildSpecies and wildLevel are enough for many wild battles but not for trainer battles. opponentPartySource tells you whether to build from trainer structs, generate from species and level, or use a special scripted source. playerPartySource is usually the player party, but remembering it explicitly helps you reason about link and special battle cases later. scriptContinuation and returnCallback are what keep the battle from becoming a one-way trip.

Now, because this is an advanced course, I also want you to notice the asymmetry between data and execution. Trainer data is static or generated data. Event variables are runtime state. The main callback is execution control. The setup code is glue. Battle.h is a compile-time contract. The visible battle is a player experience assembled from all of these. FireRed’s architecture is not “data-driven” in the sense that every behavior is declarative. It is data-driven in the more practical creature-RPG sense: large content surfaces, like trainer parties and species encounters, are table-driven, while the runtime uses C control flow and global state to stage and interpret those tables.

This is where the Pokemon Showdown comparison remains useful but secondary. Showdown’s data files, such as pokemon-showdown/data/text/moves.ts, chunk_92a6298340525a85, show a modern simulator ecosystem with explicit move text and behavior descriptions organized for deterministic battle simulation and tooling. But for this course, Showdown is not our architecture authority. FireRed is. FireRed teaches the embedded console structure: callbacks, tasks, global state, generated data, compact table formats, and field-to-battle transitions. Showdown helps you compare battle data hooks and testability later, especially when we discuss regression testing, but it does not replace the FireRed handoff model.

Before we pause this first portion, let’s walk one complete scenario in plain language. The player steps into grass. The field system sees movement complete and evaluates the tile and encounter context. A wild encounter is selected. Battle setup records that this is a wild battle, chooses or receives the species and level, prepares the opponent Pokémon data, marks the relevant battle type flags, selects a transition, and schedules the battle start. The overworld callback does not keep running the map as though nothing happened. Ownership passes through the transition into battle initialization. Battle code then uses the constants and shared contracts from battle.h, including battler identity conventions from chunk_4079f42fde2b4370, to create a coherent battle scene. When the fight ends, outcome and continuation state decide how the world resumes.

Now the trainer version. The trainer sees the player or is activated by a script. The field side identifies the trainer and may run approach movement and dialogue. Setup records the trainer battle and trainer ID. The trainer table contract from battle.h chunk_403d30271e2270d6 tells the engine how trainer metadata points at party records, while the trainer party structs from chunk_4e3097f639b05787 tell it how to decode the opponent’s Pokémon. Battle type flags restrict behavior appropriately. The transition and battle callback perform the mode change. Afterward, world state records the trainer as defeated or resumes the controlling script.

Here is the check I want you to answer silently. In both examples, what is the minimum thing that must survive across the transition? Not the grass tile. Not the trainer object animation. The minimum is the battle description plus the continuation description. What battle am I starting, and what do I do when it is over? If your design captures those two halves, you are thinking architecturally. If it captures only the opponent, you are thinking like a single combat demo, not like a full creature-collector RPG.

We will continue from that exact point next, by looking more closely at the setup flow and the handoff path in battle_setup.c, especially how the engine stages transition work instead of simply jumping from map code straight into battle code.

Good, so now keep that question in your head, because battle_setup.c is almost an answer key to it. It is not the battle engine itself. It is not the move executor. It is not the damage calculator. It is the adapter between world state and battle state. That adapter has to say, “Pause the field, preserve the right continuation, describe the kind of battle, choose presentation details, and then hand control to the battle callback.” That is the architectural role.

Look at the board for a moment. I am going to write a four-stage path, and I want you to resist making it too magical. Stage one: world detects encounter. Stage two: setup records battle facts. Stage three: transition task runs. Stage four: CB2_InitBattle takes over. That is the shape we care about.

The source packet gives us a very good anchor here. In pokefirered/src/battle_setup.c, chunk_494d132a59422c85 shows the include surface for this module: it pulls in task, overworld, item, sound, pokemon, load_save, safari_zone, quest_log, script, event_data, event object movement, metatile behavior, field control, field player avatar, field screen effects, battle.h, battle_transition, battle_controllers, and constants. That list is not just boilerplate. It tells you the module sits exactly at a boundary. It knows enough about the field to stop it, enough about scripts to resume them, enough about party and enemy data to classify the encounter, and enough about battle to set flags and begin the handoff.

Now, let’s make the handoff concrete. In the setup path, FireRed does not simply call the battle loop directly from grass movement code. Instead, battle_setup.c creates a transition task. In the setup-flow chunk, pokefirered/src/battle_setup.c chunk_db24b720e895b599, the pattern is that the engine stages a field transition, waits for it to complete, cleans up overworld windows and tilemaps, and only then calls into battle initialization through SetMainCallback2 with CB2_InitBattle. That matters because this is still the same callback/task architecture we studied back in week one. You should hear an echo of AgbMain, SetMainCallback2, CreateTask, and RunTasks. The runtime shell has not gone away just because we are entering a battle. Battle entry is still a callback change, mediated by tasks.

Pause there. If I say “mediated by tasks,” what do I mean? I mean the game is not stack-calling from overworld movement into a complete battle and then returning like a normal blocking function call. It is scheduling visible transition work over frames. The field is still alive long enough to animate the transition. The task stores small state, advances when the visual transition finishes, and then swaps the main callback. That is a recurring Game Boy Advance pattern, and it is one reason FireRed’s architecture is a set of cooperating state machines rather than one giant synchronous script.

Let’s connect that to the two examples from the previous segment. In a wild battle, the world state usually provides the location context and an already-created enemy Pokémon in gEnemyParty. In a trainer battle, the world state provides a trainer script record, a trainer object identity, and a trainer ID that indexes the trainer table. Those are not symmetrical inputs. Wild battle setup is closer to “the encounter system has already produced a monster; now classify the battle and start it.” Trainer battle setup is closer to “parse this event-script data, remember the trainer, remember the speeches and continuation addresses, and then start a trainer-type battle.”

So if you are designing the lab record this week, do not flatten those two cases into one vague field called opponent. That would lose information. A correct battle setup record has to distinguish opponent source. Is the opponent a generated wild party slot? Is it trainer table index gTrainerBattleOpponent_A? Is it a scripted legendary already placed into gEnemyParty? Is it a Safari encounter where the battle menu changes? Those are different contracts.

Now, on the board, under “battle facts,” I am writing gBattleTypeFlags. This is one of the most important variables in the handoff. battle.h is our subsystem map here. The packet points to pokefirered/include/battle.h chunk_4079f42fde2b4370 for battler identity and action constants, and to later battle.h globals in the research synthesis as the place where major battle variables such as gBattleTypeFlags and gBattleOutcome are exposed as cross-module state. The setup module writes gBattleTypeFlags before battle initialization, and battle initialization reads that value to decide what kind of battle world it is entering.

Think of gBattleTypeFlags as a compact feature declaration. Trainer battle? Set the trainer bit. Safari battle? Set the Safari bit. Ghost battle? Set the ghost bit. Scripted wild battle? Set the scripted-wild bit. Early rival tutorial? Add the first-battle bit. The exact bit definitions live in the constants layer, but the architectural fact is visible in pokefirered/src/battle_setup.c chunk_0e0ab33ad5402d71 and chunk_a8e4b22a367933e7: setup functions write gBattleTypeFlags immediately before creating the battle start task.

Here is a concrete example. Suppose the player steps in tall grass and a normal wild encounter has been selected. StartWildBattle checks special cases first. If the player is in the Safari Zone, it routes to Safari battle setup. If the player is in Pokémon Tower without the Silph Scope, it routes to the ghost case. Otherwise, it uses the standard wild battle path. In the standard wild path, FireRed locks player field controls, freezes object events, stops the avatar, stores CB2_EndWildBattle in gMain.savedCallback, clears gBattleTypeFlags to zero for an ordinary wild battle, creates the battle start task using a wild transition, and increments battle statistics. That behavior is supported by pokefirered/src/battle_setup.c chunk_0e0ab33ad5402d71.

Notice the order. Freeze field, set continuation, set battle type, create transition task. This is the pattern. If you reorder it poorly in your own clone, you can create strange bugs. For example, if you create the transition task before the callback is stored, the battle can initialize without a valid return path. If you forget to lock controls before the transition, input can leak into field state during the fade. If you forget to clear or set gBattleTypeFlags, the battle engine may interpret an ordinary wild battle as a previous special mode. These are not abstract purity concerns. This is the kind of small state leakage that makes RPG engines feel haunted to debug.

Now, compare trainer battle setup. Trainer battle setup has more text and script continuation state. In pokefirered/src/battle_setup.c chunk_db24b720e895b599, there is a small parameter-loading system around struct TrainerBattleParameter. The source uses parameter specs such as ordinary battle params, continue-script params, double battle params, no-intro params, early rival params, and continue-script double params. The important architecture point is that trainer script bytes are decoded into module-level setup variables: trainer battle mode, opponent trainer ID, object event local ID, intro speech, defeat speech, victory speech, cannot-battle speech, script return address, and battle end script.

That is a lot of state, but it is not arbitrary state. It is exactly the two-half contract we named earlier. First half: what battle am I starting? That includes trainer ID, battle mode, and double-battle status. Second half: what do I do when it is over? That includes defeat text, post-battle script address, end script, trainer flag update, and field callback. The parameter loader is FireRed’s way of turning compact script data into those two halves.

This is where battle.h’s trainer table contract enters. pokefirered/include/battle.h chunk_4e3097f639b05787 defines trainer party shapes: no item with default moves, item with default moves, no item with custom moves, item with custom moves, and the union that points to one of those layouts. pokefirered/include/battle.h chunk_403d30271e2270d6 then exposes the broader trainer table contract through struct Trainer and gTrainers. So battle_setup.c does not need to embed every opponent monster in the field script. The script can name a trainer, and the battle side can later resolve trainer class, party size, party flags, items, AI flags, double-battle status, and party pointer through gTrainers.

Check for understanding. If a trainer object sees the player and starts a battle, where should the complete enemy party be stored in the script command? Silently answer. It should not be fully duplicated there. The script should carry enough identity to select the trainer and the relevant encounter behavior. The full party composition belongs to the trainer data table contract. That is exactly why gTrainerBattleOpponent_A is so central. It is a bridge from event script world into battle data world.

Let me say that in a slightly different way, because this is a midterm-level distinction. The world layer is allowed to choose which battle occurs. It is not supposed to own all battle content. FireRed keeps the trainer content in trainer structs and party arrays, with battle.h documenting the shapes, while battle_setup.c records the selected trainer ID and battle mode. In your own architecture, if map scripts contain hand-authored JSON blobs of full enemy parties everywhere, you have made the map system too powerful and the trainer data system too weak. You can do that for a prototype, but it will not scale cleanly.

Now let’s follow the trainer start path one step farther. StartTrainerBattle, in the handoff part of pokefirered/src/battle_setup.c chunk_a8e4b22a367933e7, sets gBattleTypeFlags to BATTLE_TYPE_TRAINER. In the early rival tutorial case, it can add the first-battle flag. It stores CB2_EndTrainerBattle in gMain.savedCallback. It calls the trainer battle transition path, and it stops the script context. That last piece is subtle. The script does not keep executing underneath the battle. The battle becomes the main activity. The continuation is stored, and the script engine will be resumed through the proper return-to-field path later.

This is a good place to connect to week eleven. When we decomposed the RPG into runtime shell, overworld maps, event scripting, creature data, battle, inventory, UI, save, and link systems, the danger was treating those boxes as independent. Starting a battle proves they are not independent. The transition crosses at least five boxes: overworld controls and objects, event script state, creature party state, battle flags and trainer data, and callback scheduling. The reason architecture matters is that the crossing must be explicit.

Now, someone may ask, “Why use gMain.savedCallback instead of just returning to the previous callback automatically?” Good question. Because not every battle returns the same way. A normal wild battle returns to field. A scripted wild battle returns to field and continues the script with map music. A trainer battle returns to field, continues script, sets the trainer flag if appropriate, and records quest log events. A lost battle may go to white-out instead. The return path is not just “go back.” It is outcome-sensitive continuation logic.

In the end callbacks, FireRed reads gBattleOutcome. For wild battles, CB2_EndWildBattle clears palette and OAM state, checks whether the player was defeated, and either sets the main callback to white-out or returns to field. For scripted wild battles, the non-defeat path returns to field while continuing the script and playing map music. For trainer battles, the non-defeat path sets the battled trainer flag and handles quest log end-of-battle work. Those details are in pokefirered/src/battle_setup.c chunk_a8e4b22a367933e7. This is why the continuation half of the contract cannot be an afterthought.

Let’s do a small design exercise verbally. Imagine you are building a clone engine and you define a BattleStartRecord. You put battleKind, opponentTrainerId, enemyPartyPointer, transitionId, musicId, returnCallback, afterBattleScript, trainerObjectLocalId, and outcomePolicy. That record is much closer to FireRed’s real shape than a record that says only species and level. For wild encounters, opponentTrainerId is empty and enemyPartyPointer or generated enemy party is meaningful. For trainer encounters, opponentTrainerId is meaningful and enemyPartyPointer is resolved later from trainer data. For scripted legendaries, enemyParty may already be explicitly created, battleKind includes legendary or scripted wild, and returnCallback resumes the script.

Now, I want you to notice another nice architectural touch: transition selection is not purely cosmetic in the source layout, but it is separated from battle rules. FireRed chooses wild and trainer transitions based on terrain and rough level comparison. The setup file includes BattleSetup_GetTerrainId, GetBattleTransitionTypeByMap, GetWildBattleTransition, and GetTrainerBattleTransition. These functions inspect map type, metatile behavior, surfing state, flash level, player party level, enemy level, trainer class, Elite Four IDs, Champion class, and double-battle status. This behavior is in pokefirered/src/battle_setup.c chunk_db24b720e895b599. But once the transition is selected, it is still just an entry animation. It should not be tangled into damage calculation or turn order.

This is one of those advanced architecture details I expect you to be able to defend. Presentation is not absent from setup. Setup absolutely chooses music and transition. But presentation choices are made as setup metadata, not as core battle rules. That division lets the battle engine initialize according to battle type while the transition system handles the field-to-battle visual movement.

Now, check yourself again. If GetTrainerBattleTransition looks at gTrainers[gTrainerBattleOpponent_A].doubleBattle, does that mean the transition function owns double battle semantics? No. It means the transition function uses trainer metadata to choose a suitable animation. The battle semantics still come from the trainer data, battle flags, battler positions, party indexes, controllers, and later battle-main logic. This is the kind of dependency you should describe carefully in your homework. “Reads a field for presentation selection” is not the same as “owns the gameplay rule.”

Let’s bring battle.h back in. Last lecture, we treated battle.h as a blueprint, not because headers are magical, but because they reveal the contract surface. The battler position macros and battle action constants in pokefirered/include/battle.h chunk_4079f42fde2b4370 describe the language the battle engine will use after setup: battlers have positions and sides; actions include use move, use item, switch, run, Safari actions, old man tutorial actions, script execution, finish states, and none. Battle setup does not choose each turn’s B_ACTION_USE_MOVE or B_ACTION_SWITCH. Its job is earlier. It creates the conditions under which those later action constants make sense.

So if you draw the timeline, battle_setup.c is before B_ACTION selection. It answers “which battle instance?” Battle main later answers “which battler acts, and with what action?” The difference matters. Do not mix the encounter contract with the turn contract.

Let me use a concrete trainer example. Say a Bug Catcher on a route notices the player. The overworld object system detects line of sight and creates an approach event. The script data for that trainer is passed into BattleSetup_ConfigureTrainerBattle. The setup loader records that this is an ordinary trainer battle, stores the trainer ID into gTrainerBattleOpponent_A, stores the object local ID, stores intro and defeat speech pointers, and stores the address to continue after battle. Then the script path can show encounter text and eventually call StartTrainerBattle. StartTrainerBattle sets BATTLE_TYPE_TRAINER, stores CB2_EndTrainerBattle as the saved callback, starts the transition task, and stops the script context. The task runs the visual transition, then SetMainCallback2 changes control to CB2_InitBattle. After the battle engine finishes, gMain.savedCallback leads to CB2_EndTrainerBattle, which checks gBattleOutcome, returns to field or white-out, sets the trainer flag if the player won, and lets the field script continue.

That is the whole architectural arc. Not every function name there needs to be memorized, but the responsibilities do.

Now, quick recap before we move to the next layer. First, battle_setup.c is the adapter between world state and battle state. Second, it preserves both battle description and continuation description. Third, gBattleTypeFlags is the compact classification handed to battle initialization. Fourth, gTrainerBattleOpponent_A connects trainer scripts to the gTrainers data table, whose party structures are declared in battle.h chunks chunk_4e3097f639b05787 and chunk_403d30271e2270d6. Fifth, the transition is staged by a task before CB2_InitBattle, so this remains part of the same callback/task architecture we have been tracing since the beginning of the course.

For the next stretch, we are going to stay on that handoff boundary but zoom in on failure and outcome. Because a robust battle-start design is not proved by the happy path where the player wins a normal trainer fight. It is proved by Safari exits, white-out, running, catching, scripted legendaries, special ghost handling, rematches, and early rival tutorial behavior. Those cases are where you can see whether your architecture really has an outcome policy or whether it just assumes the battle returns normally.

So let’s put that on the board as a contract, because this is the point where a lot of otherwise good student designs become vague. On the left side of the board I am writing entry policy. On the right side I am writing return policy. Entry policy answers, “What kind of battle is this, who or what is being fought, what party data must be present, what music and transition should play, and what callback will receive control afterward?” Return policy answers, “When the battle engine gives me an outcome code, what does the overworld do with it?”

That distinction matters because FireRed does not just say, “start battle,” and then hope the world knows what happened. It installs state before the battle, and it installs a callback that will interpret the result afterward. The setup side is in pokefirered/src/battle_setup.c, especially the start and handoff material in chunk_0e0ab33ad5402d71, chunk_ced967cc16dd08b4, chunk_c2a497f08d79f8bd, chunk_0f66d8f840eed7be, and chunk_4476559f476b1634. The shared battle globals that make this possible, including gBattleOutcome, gBattleTypeFlags, and gTrainerBattleOpponent_A, are exposed through pokefirered/include/battle.h in chunk_3b8014717a063ec2 and chunk_b4f085c32725f246. So we are still in the same architectural theme: a header publishes the contract, a setup module writes globals and callbacks, and a later subsystem consumes those globals.

Now, remember, I am not asking you to admire globals as a universal design pattern. In a modern engine, you might wrap this in a battle launch object, an ECS event, a scene transition descriptor, or a coroutine. But in this codebase, the architectural unit is very often “globals plus callback plus task plus script continuation.” We have seen that since AgbMain, SetMainCallback2, and RunTasks. The important advanced move is not to say, “Globals bad.” The important advanced move is to identify which globals are acting as the boundary surface between systems.

Let’s start with the simplest world-to-battle branch. StartWildBattle checks the current world condition. If the Safari flag is set, it calls DoSafariBattle. If the player is in Pokemon Tower without the Silph Scope, it calls DoGhostBattle. Otherwise it calls DoStandardWildBattle. That source path is pokefirered/src/battle_setup.c chunk_0e0ab33ad5402d71 and chunk_ced967cc16dd08b4. Notice how much world state is being consulted before the battle engine is allowed to initialize. Safari mode is not a move rule. Pokemon Tower ghost visibility is not a turn-order rule. These are overworld or progression rules that shape the battle before it exists.

Now I’ll write three fields on the board under “entry policy”: saved callback, battle type flags, and enemy party. For a standard wild battle, the saved callback becomes CB2_EndWildBattle, gBattleTypeFlags is cleared to zero, and the transition task is created. For a Safari battle, the saved callback becomes CB2_EndSafariBattle, and gBattleTypeFlags receives BATTLE_TYPE_SAFARI. For a ghost battle, the saved callback is again CB2_EndWildBattle, but the battle type is BATTLE_TYPE_GHOST, and the enemy party’s first nickname is set to the ghost text. That is not just flavor. It means the setup code is allowed to alter presentation-facing creature data at the boundary. It does not need to teach the whole battle engine about every map script that led there.

Pause and check your understanding. If I ask, “Where should the decision that this is a Safari battle live?” the answer is not “in the catch-rate formula,” and it is not “inside the Safari action menu only.” The first decision lives at setup, because the setup must choose BATTLE_TYPE_SAFARI and must choose CB2_EndSafariBattle. Later, the controller and command code can specialize Safari actions, but the identity of the session has already been marked.

The outcome constants make the return side explicit. In pokefirered/include/constants/battle.h chunk_f23fdc665a1d3660 and chunk_49567347507fc5d4, the game has named outcomes such as B_OUTCOME_WON, B_OUTCOME_LOST, B_OUTCOME_DREW, B_OUTCOME_RAN, B_OUTCOME_PLAYER_TELEPORTED, B_OUTCOME_MON_FLED, B_OUTCOME_CAUGHT, and B_OUTCOME_NO_SAFARI_BALLS. Those names are small, but they are an architecture. They are the vocabulary the battle engine uses to speak back to the overworld. A poor clone architecture often returns only true or false. Did the player win, yes or no? FireRed needs more than that. Running is not losing. Catching is not merely winning. A wild monster fleeing is not the same as the player teleporting. Running out of Safari Balls is not the same as fainting.

Now look at the helper IsPlayerDefeated in pokefirered/src/battle_setup.c chunk_7320fc7a27ba2790. It classifies B_OUTCOME_LOST and B_OUTCOME_DREW as defeat, and it classifies won, ran, player teleported, mon fled, and caught as not defeated. That is a compact policy layer. It is not the whole outcome policy, but it is the part that decides whether to route to white-out. In CB2_EndWildBattle, in pokefirered/src/battle_setup.c chunk_4a0360caf5e686c2, the callback clears visual state, checks IsPlayerDefeated(gBattleOutcome), and either sends the player to CB2_WhiteOut or returns to the field. For scripted wild battles, CB2_EndScriptedWildBattle does the same white-out test but returns through CB2_ReturnToFieldContinueScriptPlayMapMusic if the player did not lose. That difference is subtle and essential. Ordinary wild battles resume the field. Scripted wild battles resume the field and continue script flow.

Let’s slow down there. Suppose your lab record has a field called afterBattleCallback, and for every wild battle you set it to “return to map.” That will pass your first happy-path test. It will fail a scripted legendary. Why? Because the script may need to continue after the battle to set a flag, remove an object, play a message, or branch on the result. The difference between CB2_ReturnToField and CB2_ReturnToFieldContinueScriptPlayMapMusic is not cosmetic. It is the difference between “the battle was an interruption” and “the battle was a command inside an event script.”

Here is the concrete example I want you to carry into the workshop. Consider the Marowak battle in Pokemon Tower. StartMarowakBattle is in pokefirered/src/battle_setup.c chunk_c2a497f08d79f8bd. It locks player controls, sets gMain.savedCallback to CB2_EndMarowakBattle, checks for the Silph Scope, and then either marks the battle as a ghost without unveiling or creates a specific female level 30 Marowak and uses BATTLE_TYPE_GHOST combined with BATTLE_TYPE_GHOST_UNVEILED. It also sets the enemy nickname to the ghost text. So the start function is doing progression logic, party construction, flags, and callback selection.

Then look at CB2_EndMarowakBattle in pokefirered/src/battle_setup.c chunk_4a0360caf5e686c2. If the player is defeated, white-out. If not defeated, it inspects gBattleOutcome. If the outcome is B_OUTCOME_WON, it sets gSpecialVar_Result to false. Otherwise, it sets gSpecialVar_Result to true. Then it returns to the field while continuing the script. That means the battle’s return policy is not merely “won means good.” It is mapping battle outcome into a script variable with semantics specific to the tower event. In plain language, the script wants to know whether the obstacle should still block the player. Winning clears it. Other non-defeat outcomes can still mean the event was not resolved.

That is the kind of case I expect advanced students to notice. You cannot model all battle outcomes as a single enum unless the overworld also has a place to interpret that enum. FireRed uses gSpecialVar_Result as a bridge back into event scripting. We saw gSpecialVar_Result earlier in the course as a script-visible result register. Here it is again, but now it is carrying battle outcome policy back from a combat subsystem into a map event. This is why the course keeps insisting on “data table to global to callback to script to visible behavior.” The visible behavior is the player either proceeding through the tower or being forced back. The technical cause is a battle end callback writing a script variable.

Now, Safari gives us a different shape of outcome policy. EnterSafariMode and ExitSafariMode live in pokefirered/src/safari_zone.c chunk_cffa0a7a0416ad2a. EnterSafariMode sets the Safari flag, gives the player thirty Safari Balls, and initializes the step counter. StartWildBattle later sees that flag and routes to DoSafariBattle in pokefirered/src/battle_setup.c chunk_0e0ab33ad5402d71 and chunk_ced967cc16dd08b4. DoSafariBattle sets gMain.savedCallback to CB2_EndSafariBattle and sets BATTLE_TYPE_SAFARI. That means the battle is not just a wild battle with a different menu. It is a battle whose return callback belongs to the Safari system.

Then CB2_EndSafariBattle, in pokefirered/src/safari_zone.c chunk_c1bacf20d1806a90, checks the remaining Safari Ball count and the battle outcome. If gNumSafariBalls is not zero, it simply returns to the field. If the battle outcome is B_OUTCOME_NO_SAFARI_BALLS, it runs the out-of-balls script immediately, warps into the map, sets a field callback, and loads the map. If the outcome is B_OUTCOME_CAUGHT, it sets up another Safari out-of-balls script path and returns while continuing script music flow. I am deliberately saying this a little slowly because this is the point where a diagram helps.

Imagine the board now as a loop. The Safari entrance script sets a mode flag and counters. The overworld step logic decrements those counters. Wild encounter setup reads the mode flag. Battle setup chooses Safari battle flags and a Safari end callback. The battle engine consumes the flags and eventually writes gBattleOutcome. The Safari end callback reads both gBattleOutcome and gNumSafariBalls, then chooses a map or script continuation. That is not a line. It is a loop across the overworld, battle, script, and map loading systems.

Check for understanding: why is B_OUTCOME_NO_SAFARI_BALLS not enough by itself? Because the Safari return policy also checks gNumSafariBalls. The outcome tells you what happened in the battle. The world state tells you what that means for the Safari session. The same idea will come back when we talk about save persistence and post-battle flags. An outcome code is not a complete story. It is an event interpreted against state.

Now let’s connect this to trainer battles, because the trainer path adds the data-table contract from Monday’s lecture. The trainer opponent ID, gTrainerBattleOpponent_A, points into gTrainers. The trainer party record shapes were declared in battle.h, which we connected to the trainer table contract in pokefirered/include/battle.h chunk_4e3097f639b05787 and chunk_403d30271e2270d6. In battle setup, BattleSetup_ConfigureTrainerBattle parses script arguments into a set of static variables: sTrainerBattleMode, gTrainerBattleOpponent_A, sTrainerObjectEventLocalId, intro speech, defeat speech, victory speech, cannot-battle speech, optional battle script return address, and battle end script. That parameter loading apparatus is visible in pokefirered/src/battle_setup.c chunk_3990d161a046e490, chunk_e498869ba4bf7f6e, chunk_fd09f1affbf0032b, and chunk_7320fc7a27ba2790.

That sounds mechanical, but it is a major design point. Trainer battle setup is not just “trainer ID.” It is trainer ID plus mode plus object identity plus text pointers plus return address. So a trainer encounter is a small protocol between the script VM and the battle system. If you under-design that record, you will end up hard-coding too much in scripts or hard-coding too much in battle setup. FireRed chooses an intermediate form: scripts encode parameters, battle_setup.c loads them into globals, and event scripts like the normal, double, rematch, and no-intro trainer paths are returned as the next script address. The function BattleSetup_ConfigureTrainerBattle returns different event scripts depending on sTrainerBattleMode, including normal, double, rematch, and early rival cases, in pokefirered/src/battle_setup.c chunk_db24b720e895b599 and related parameter chunks.

Now watch the trainer start handoff. StartTrainerBattle sets gBattleTypeFlags to BATTLE_TYPE_TRAINER. If this is the early rival tutorial case and the rival flags include RIVAL_BATTLE_TUTORIAL, it also sets BATTLE_TYPE_FIRST_BATTLE. Then it sets gMain.savedCallback to CB2_EndTrainerBattle, calls DoTrainerBattle, and stops the script context. That is pokefirered/src/battle_setup.c chunk_a8e4b22a367933e7. Stopping the script context is important. The script does not keep executing while the battle scene is running. The callback path will decide how and when script execution resumes afterward.

Now here is the advanced part: the end callback for trainer battles is not one branch. CB2_EndTrainerBattle treats early rival battles specially. If the player is defeated in that mode, it writes gSpecialVar_Result true and may heal the player’s party depending on sRivalBattleFlags. If the flag does not request healing, it white-outs. If the player did not lose, it writes gSpecialVar_Result false, returns to field while continuing script music, sets the trainer flag, and records quest log events. For ordinary trainer battles, secret base battles return differently, defeats white-out, and non-defeats set the battled trainer flag and handle quest log completion. That is in pokefirered/src/battle_setup.c chunk_a8e4b22a367933e7.

So if I ask you, “What is a trainer battle in FireRed?” I do not want the answer, “A battle with BATTLE_TYPE_TRAINER.” That is only one field. A trainer battle is a configured script-mediated encounter whose trainer ID indexes gTrainers, whose party layout is interpreted through battle.h trainer structs, whose object local ID may become gSpecialVar_LastTalked, whose mode selects intro and continuation behavior, whose saved callback determines post-battle policy, and whose outcome may set flags, heal the party, white-out, continue a script, or clear rematch state. That is the level of answer expected on the week 12 quiz and the midterm. At this point in the course, naming a file is not enough. You need to name the state crossing the boundary.

Let’s bring in transition selection for a moment, because it is easy to treat transitions as visual sugar. The transition task is actually the staging area between worlds. Task_BattleStart, in pokefirered/src/battle_setup.c chunk_4476559f476b1634, waits until poison field effects are not active, disables the help system, starts the battle transition, waits for it to finish, re-enables the help system, cleans up overworld windows and tilemaps, calls SetMainCallback2(CB2_InitBattle), restarts wild encounter immunity steps, clears the poison step counter, and destroys itself. That is a per-frame task doing controlled handoff. This is exactly the runtime shell architecture from week 1 showing up inside the battle doorway. The task is not the battle. The task is the airlock.

And the selected transition itself is data-driven from map and party conditions. GetBattleTransitionTypeByMap looks at the player’s destination coordinates, metatile behavior, flash level, surfability, and map type in pokefirered/src/battle_setup.c chunk_8de134d27be31aa0. GetWildBattleTransition compares the enemy level with the first usable player party level in chunk_d7e7e08326b17b49. GetTrainerBattleTransition has special cases for secret base, Elite Four, Champion, double battle minimum party count, and then compares summed party levels in chunk_c48eb03ee7e3cc4a. This is a good example of the course’s larger theme: not every table is a monster table or move table. The transition tables are also data tables, and they connect world geometry, party state, trainer data, and visible presentation.

Now, do not let the visual layer distract you from the contract. The transition task is allowed to clean up the overworld because the system is leaving the overworld callback. It is allowed to set CB2_InitBattle because the entry globals are already prepared. It is allowed to clear poison step counters and restart encounter immunity because those are overworld stepping policies that must not leak through the transition. A clean battle-start architecture has to decide what happens to those little bits of state. If your design starts a battle but leaves overworld input, poison stepping, encounter counters, or script execution in an undefined state, you have not actually designed the handoff.

Let’s do one more check. I’ll ask it as a design question, because this is close to your lab. You are designing one battle setup record that can start wild, trainer, and scripted battles. What fields must it include? Think before I answer. It needs some representation of battle type flags. It needs an opponent source, which may be an already-created gEnemyParty slot for a wild or legendary battle, or a trainer ID for gTrainers. It needs a world source, such as map type, metatile behavior, Safari flag, Silph Scope condition, or script mode, depending on the battle. It needs a transition and music policy. It needs a saved callback or return policy. It needs a script continuation policy, including whether gSpecialVar_Result should be written. It may need object-event identity for trainers, because the trainer’s local ID ties the post-battle flag and text behavior back to the map. And for trainer battles it needs text pointers and mode, because intro, defeat, victory, and cannot-battle text are part of the setup record, not an afterthought.

That is the difference between a battle launcher and a battle setup contract. A launcher says, “Call InitBattle.” A contract says, “Here are all the state variables the battle engine and the overworld will exchange.”

Tie this back to prior weeks. When we studied party data across gameplay screens, we cared about party slot ownership and summary screen reads. Today, those ideas are being used rather than re-taught. GetSumOfPlayerPartyLevel walks gPlayerParty and reads species, HP, and level to choose transitions, in pokefirered/src/battle_setup.c chunk_47d6c227b1dedfcc. When we studied move learning as a menu state machine, we talked about a UI flow that pauses field control, manipulates party data, then returns through callbacks. Today, battle setup is the same class of architecture at a larger scale. When we studied battle.h as a subsystem blueprint, we mapped battler positions, actions, and trainer party structs. Today, those header contracts are being exercised at the moment the overworld says, “A battle should now exist.”

And looking forward, this is why week 12 sits at the entrance to the battle engine module. Next we can study turns, commands, controllers, AI, and battle scripts without pretending they float in isolation. The battle loop receives a session whose type flags, parties, opponent IDs, terrain, music, transition, and return callback have already been chosen. Later, when we reach inventory, save, and link systems, the same question will come back: what state crosses the subsystem boundary, and who owns the return path?

So the section recap is this: FireRed starts battles from world state by classifying the encounter, writing the global battle contract, staging the visual and callback transition through a task, and preserving an outcome-specific return path. The happy path is only one proof case. The stronger proof cases are Safari, ghosts, Marowak, legendaries, rematches, and early rival battles, because those force the architecture to distinguish “battle ended” from “the world knows what this ending means.” Next, we will keep that model on the board and trace how the actual battle initialization consumes the flags and party records that setup has prepared.

Good. Now, keep that last sentence in view, because we are moving from the setup side to the consumption side. Setup has prepared a contract. Battle initialization is going to read that contract and build the actual battle runtime from it.

I am going to write three phrases on the board: world classification, battle contract, battle runtime. Those are not three separate features. They are three moments in the handoff. World classification answers, “What kind of encounter did the player just trigger?” Battle contract answers, “What minimum state must survive the transition?” Battle runtime answers, “How do we turn that minimum state into battlers, parties, controller behavior, music, text, and outcome rules?”

This is why I want you to resist saying, “The overworld calls the battle.” That is too vague for architecture work. In FireRed, the overworld side does not simply jump into a giant battle function with every parameter on the stack. Instead, the setup code writes globals and staged state, and then the callback transition moves control into the battle subsystem. You can see the dependency surface in pokefirered/src/battle_setup.c, especially the setup and handoff material in chunk_0e0ab33ad5402d71, chunk_db24b720e895b599, and chunk_a8e4b22a367933e7. The include list itself, shown in pokefirered/src/battle_setup.c chunk_494d132a59422c85, is already a clue: battle setup touches overworld, script, event data, object movement, metatile behavior, transition code, trainer logic, and battle headers. That file is not “battle” in isolation. It is the adapter between field state and battle state.

Now let us make that precise. Suppose the player takes a step into tall grass. Before the battle engine can do anything meaningful, the world engine has already answered several questions. Is the player on a map where encounters are enabled? What tile behavior is underfoot? Is this ordinary grass, water, cave floor, or some other terrain class? Is the player in Safari Zone context? Is there a scripted override? Has the encounter generator already selected a species and level? We are not re-teaching wild encounter selection today; the important point is that, by the time battle setup is invoked, the system is no longer asking a broad design question like, “Could a monster appear here?” It is now asking a narrower interface question: “Given that a battle is starting, which flags and return behavior do we install?”

For a normal wild battle, the contract is comparatively small. The battle type needs to say wild, not trainer. The player party is implicitly the player party we have studied before. The opponent party is going to be synthesized as a wild mon, not copied from a trainer table. The terrain must be preserved because the battle scene needs to know which visual background and sometimes which contextual rules apply. The return callback normally points back to the field. And the battle transition must be staged so that the player sees the audiovisual bridge rather than an abrupt callback swap.

Pause there. Check your understanding: if the wild battle starts from grass, should the battle engine independently inspect the overworld tile again after the screen transition? No. That would couple battle initialization back to live map inspection. The setup layer should capture the relevant terrain classification before the transition, then the battle side consumes that captured value. That is the architecture pattern we care about.

Now compare that with a trainer battle. A trainer battle cannot be represented only by “battle type equals trainer.” It needs at least one trainer ID, and sometimes two. It needs the trainer party source. It needs intro and defeat text behavior. It needs trainer flags so the same trainer is not fought repeatedly unless rematch logic says otherwise. It needs music selection. It may need a post-battle script continuation. The trainer table contract in pokefirered/include/battle.h chunk_403d30271e2270d6 is important here because it tells us what the battle subsystem expects a trainer record to provide. And the trainer party struct definitions in pokefirered/include/battle.h chunk_4e3097f639b05787 tell us why one trainer ID is enough to locate quite a bit of battle data: species, level, held item presence, custom moves or default moves, depending on the party flags.

Let me say that more concretely. If the script says, in effect, “start battle against trainer X,” the setup layer does not need to copy every enemy Pokémon into some giant script-local object. It can record the opponent ID, set the trainer battle type, and let battle initialization use the trainer table. That is data-driven architecture. The world says which record applies. The battle system knows how to interpret that record. The trainer table remains the stable contract between them.

This is also where battle.h from yesterday earns its keep. In pokefirered/include/battle.h chunk_4079f42fde2b4370, the battler identity and action constants give the battle engine a vocabulary that the overworld does not have to understand deeply. The overworld does not need to know how the enemy-left battler action queue will be advanced. It does not need to know whether a controller is waiting for move selection or switching or item use. It only needs to mark the encounter type and opponent source clearly enough that the battle subsystem can create the right battler positions and controllers later.

So, on the board, I am drawing a line. Left side: field callback, tasks, scripts, map objects, event flags. Right side: battle callback, battler positions, parties, action constants, battle script flow. In the middle: battle setup. The mistake many advanced students make is to put too much intelligence in the middle. The adapter should classify and transfer. It should not become a second battle engine.

Let us walk a trainer example all the way through. Imagine the player walks into a trainer’s line of sight. The overworld object system detects facing and range, the trainer approach behavior takes over, and script-controlled text starts. By the time battle setup receives the trainer battle command data, the map object and script systems have already done their jobs. Setup parses the trainer battle mode, reads the trainer ID, stores the relevant intro or defeat continuations, sets the battle type flags, assigns the opponent ID, and prepares the transition. Then a task waits through the fade or transition effect and eventually hands control to the battle callback. That task staging is the same general runtime pattern we have seen since the beginning of the course: AgbMain owns the frame loop, SetMainCallback2 changes the major mode, and RunTasks gives per-frame state machines a place to finish transitional work before control fully moves elsewhere.

Notice the continuity. In week 1, that callback/task model may have felt like infrastructure. Now it is carrying one of the most important gameplay handoffs in the whole RPG. The same pattern that lets a menu animate or a field effect finish now lets the game move from walking on a map to resolving a battle without losing the return path.

Now, what happens when battle initialization consumes the setup contract? It checks the battle type flags first. That is the first discriminator. Wild battle? Build an enemy party from wild encounter state. Trainer battle? Look up gTrainers by opponent ID. Double battle? Allocate battler positions and parties differently. Safari? Use Safari-specific command behavior instead of the ordinary fight-bag-party-run assumptions. The constants in battle.h are not just names. They are branch conditions that protect major architectural differences.

Here is the key design point: the same battle engine can run many encounter types because battle setup normalizes world causes into battle flags. Tall grass, fishing, legendary interaction, trainer sight, scripted rival fight, ghost reveal, and Safari encounter all have different world causes. But the battle system does not want a hundred world-specific entry points. It wants a smaller vocabulary: wild, trainer, double, Safari, special, link, scripted conditions, and so on. That vocabulary is encoded through battle constants, global state variables, trainer records, and callbacks.

Let me give you a concrete clone-engine example, because this will matter in the workshop. Suppose your design has one structure called BattleSetupRecord. It has fields for battleKind, terrainKind, trainerA, trainerB, wildSpecies, wildLevel, transitionKind, musicId, returnCallback, victoryScript, defeatScript, and flags. Good. That is not over-engineering. That is you making explicit what FireRed spreads across globals, script arguments, data tables, and callback state. But here is the advanced constraint: not every field is meaningful for every kind. For wild battles, trainerA is invalid. For trainer battles, wildSpecies is invalid unless you are doing a scripted hybrid. For Safari battles, the command menu and outcome rules differ. For legendary battles, you may need world flags after capture or defeat. So your setup record also needs invariants. A good battle setup record is not merely a bag of optional values. It is a discriminated contract.

Now, quick check. If trainerA is set but the battle type does not include trainer battle semantics, what bug class have we created? Yes, an inconsistent contract. Depending on how forgiving your battle initialization is, it may ignore the trainer ID, load the wrong party source, select the wrong music, or return to the wrong script state. In a large C codebase, those inconsistencies are often not caught by the type system. They are prevented by disciplined setup functions and by keeping the number of legitimate setup paths small.

That is one reason FireRed’s setup flow is worth studying even when it looks old-fashioned. It is not modern type-safe architecture, but it has strong procedural boundaries. Script commands configure trainer battles. Wild encounter logic configures wild battles. Special encounters use special setup helpers. The battle start handoff centralizes the transition. You can inspect pokefirered/src/battle_setup.c chunk_db24b720e895b599 for that setup flow and chunk_a8e4b22a367933e7 for the handoff pattern we are discussing. The code is telling us, “Do classification here, do callback transition here, let the battle system initialize after the transition.”

Now let us connect this to party ownership, but briefly, because we have already covered it. The player’s party is not passed as a newly allocated battle parameter. The party model already lives in global save-backed state, and many screens read it: summary, party menu, move learning, item use, and battle. When battle starts, the battle system can refer to the player party through the established party state. That is why previous work on move slots, PP, stat calculation, and party slot ownership matters here. Setup does not need to restate all that. It only identifies the encounter and lets battle initialization derive the runtime battlers from the existing party model.

Enemy trainer parties are different. They are not save-backed player-owned party slots. They are data records. The trainer party structs in pokefirered/include/battle.h chunk_4e3097f639b05787 exist because trainer parties need compact static representations. Some trainers use default moves, which means the battle initialization can derive moves from species and level. Some trainers use custom moves. Some include held items. That small family of structs is an elegant data-table solution to a content authoring problem: do not force every trainer to specify every detail if defaults are enough, but allow important battles to override moves and items.

So if I ask, “Where does Brock’s team come from?” an advanced answer is not just “from the trainer table.” The better answer is: the trainer ID selected by setup indexes the trainer table contract declared through battle.h, the trainer record indicates which party representation applies, the party pointer leads to one of the trainer mon struct arrays, and battle initialization materializes those records into active enemy Pokémon state. That is the full chain: world event to trainer ID, trainer ID to data record, data record to party struct, party struct to battle runtime.

Let us add one more board cue. I am writing “ownership versus interpretation.” The overworld owns the cause of the battle. The script owns scripted continuation. The trainer data table owns the static trainer definition. The player save owns the player party. The battle engine owns turn resolution and battler actions. Battle setup interprets enough of each side to connect them, but it should not steal ownership from them.

This becomes especially visible with return behavior. A normal wild battle can return to the field and continue. A trainer battle may need to set a defeated flag, run post-battle text, or resume a script. A loss may return the player through blackout handling. A Safari battle returns to a Safari state where balls, steps, and zone rules matter. A special ghost battle or Marowak event may need to update story flags differently from an ordinary fainted wild Pokémon. The phrase from the prior section was important: battle ended is not the same as world consequence resolved.

Let me test that with a question. Suppose a scripted legendary battle ends because the player runs away. Should the battle engine itself decide whether the overworld object disappears forever? In a clean architecture, no. The battle engine reports or exposes the outcome; the world or script layer applies the narrative rule. FireRed sometimes uses globals and special-case flags rather than a neat result object, but the architectural separation is still visible. The battle engine resolves combat. The field/script side decides what that outcome means for map state and story progression.

Now, compare this to the move relearner state machine we studied earlier. There, a menu state machine had to preserve enough state to return to the correct list, party slot, move slot, and script continuation. Here, battle setup has to preserve enough state to return to the correct overworld behavior. Different subsystem, same principle: a temporary mode must know what it owes the caller when it finishes. The difference is that battle is much larger and has many more outcomes.

This is why, in the lab, I do not want you to design only the happy path. If your BattleSetupRecord can start a normal wild battle and a normal trainer battle, that is baseline. The stronger design handles a trainer rematch, a double battle, a Safari encounter, and a scripted boss or legendary. Those cases force you to specify which variables are world-owned, which are battle-owned, and which are handoff-owned. If you cannot say who clears the trainer flag, who chooses music, who freezes map objects, who restores field control, and who consumes the trainer party table, then the architecture is still fuzzy.

Let us move one level deeper into flags. Battle type flags are attractive because they allow composition. A trainer double battle is not a completely separate universe from a trainer single battle; it is trainer plus double. Safari is not merely wild; it changes command behavior and capture/run semantics. Link battles add another axis. The advantage is flexibility. The danger is invalid combinations. This is a classic C-era engine tradeoff: bitflags are compact and fast, but correctness depends on setup discipline and downstream checks.

So when you read pokefirered/include/battle.h chunk_4079f42fde2b4370, do not treat it as a list of trivia. Treat it as the battle subsystem’s algebra. These constants define the categories the battle engine can reason about. Then when you read pokefirered/src/battle_setup.c chunk_0e0ab33ad5402d71, ask: where are those categories selected from world state? And when you read the trainer table contract in pokefirered/include/battle.h chunk_403d30271e2270d6, ask: which categories require table lookup rather than generated wild opponent state?

Here is another concrete example. Early rival battles are trainer battles, but they often have special context. The opponent identity may depend on the player’s starter choice or story progress. The battle may have special music or scripted lines. The return path may continue a story script rather than merely unfreezing the field. Architecturally, this does not mean you create an entirely separate battle engine called RivalBattleEngine. You let scripts and setup select the correct trainer ID, text, flags, and continuation, and then the ordinary trainer battle machinery does the actual combat. That is how data-driven design avoids duplicating systems for content variation.

Now I want to zoom out to the course plan for a moment. Weeks 12 through 16 are not just “the battle weeks.” They are where the earlier systems begin to converge. The runtime shell gives us callbacks and tasks. The overworld gives us maps, events, objects, and scripts. The creature data model gives us species, stats, moves, and party slots. The UI work gives us menus and return paths. Now battle setup forces those systems to cooperate. Later, when we examine inventory during battle, save persistence after battle, and link battle constraints, the same interface question will return: what crosses the subsystem boundary, and in what form?

That question is also why Pokemon Showdown is useful only as a comparison, not as our primary architecture model. Showdown’s data files, such as pokemon-showdown/data/text/moves.ts chunk_92a6298340525a85, are excellent examples of modern text and effect metadata organization, and its battle simulator architecture is designed for deterministic server-side resolution and testing. But FireRed is a handheld game architecture with callbacks, VRAM constraints, field transitions, and script continuations. So we can borrow comparison vocabulary, especially when thinking about data hooks and regression testing, but we cannot replace FireRed’s callback-and-overworld handoff with a server simulator mental model.

Let us do one more check for understanding before we continue. I will describe three pieces of state, and you tell me who should own each one. First, the player’s current party Pokémon and their PP. That is player party/save-backed creature state, consumed by battle but not created by battle setup. Second, the trainer’s static species and level list. That is trainer table data, selected by trainer ID. Third, the fact that the player was challenged by a visible trainer object on a specific map and should resume a script afterward. That belongs to the overworld/script side, with setup preserving the callback or continuation needed to get back there.

Good. That separation is what lets the codebase scale. If the battle engine owned all three, every story script and map object rule would leak into battle. If the overworld owned all three, battle could not initialize itself consistently. The setup layer gives us just enough sharing.

Now, as we move into the next part, keep your eyes on consumption. We have been mostly on the producer side: how field state produces battle setup state. Next we ask how the battle side consumes it. Which globals does initialization inspect first? How does it decide the number of battlers? How does it choose player-left, opponent-left, player-right, and opponent-right positions? How does it convert trainer party records into enemy party memory? And how do action constants become meaningful only after battler controllers are installed?

So the next board diagram will start with the values written by setup and end with active battler structures. That is the moment where “a trainer saw me” becomes “opponent-left has a Pokémon, a controller, a move set, a sprite, a healthbox, and legal actions.” Keep that transformation in mind, because it is one of the cleanest examples in the course of a game turning world narrative into runtime machinery.

All right, let’s draw that transformation now. On the left side of the board, put field world state. In the middle, put battle setup state. On the right side, put battle runtime state. The mistake I want you to avoid is drawing a direct arrow from “trainer object on the map” to “enemy battler attacks.” That skips the contract. FireRed does not let the overworld reach into the turn engine and say, “start using Tackle.” The overworld writes a relatively small set of battle setup facts, then hands control to a different subsystem.

This is why pokefirered/src/battle_setup.c is such a good architectural file for us. In chunk chunk_494d132a59422c85, even the include list tells the story. That file pulls in overworld, script, event data, field message, field control, battle.h, battle_transition, and battle-related constants. It is not “just battle code.” It is boundary code. It knows enough about the field to freeze the player and enough about battle to describe what kind of battle should start, but it is not the full battle engine.

Now, in the middle of the board, write gBattleTypeFlags. Circle it. If you only remember one battle setup variable today, remember that one. It is the compressed declaration of what kind of battle the battle engine is about to initialize. Wild battle? Trainer battle? Double battle? Safari-like special rules? Link battle? The engine should not rediscover that by interrogating the map. By the time battle initialization begins, the map has already spoken through this bitfield.

Next to it, write trainer opponent identifiers, especially the idea represented by gTrainerBattleOpponent_A and, when relevant, a second opponent slot. Do not confuse those with battlers. A trainer ID is not a battler. A trainer ID is a key into a trainer data table. A battler is a live battle participant with a side, a position, a controller, a current Pokémon, possible actions, status, sprites, healthbox state, and a place in the turn pipeline. The trainer ID belongs to setup and loading. The battler belongs to battle runtime.

This is where the source split becomes very clean. pokefirered/include/battle.h, chunk chunk_403d30271e2270d6, gives us the trainer table contract. That means battle code can treat trainer definitions as structured data rather than as script fragments. And pokefirered/include/battle.h, chunk chunk_4e3097f639b05787, defines the trainer party struct shapes. Those structs are the bridge between “Brock has these Pokémon” as data and “the opponent party memory now contains concrete Pokémon records” as runtime state.

Let me make that concrete. Suppose an NPC trainer sees the player. On the field side, an object event and a script decide that the trainer wants a battle. The setup flow in pokefirered/src/battle_setup.c, especially the chunks identified for battle setup and setup flow, chunk_0e0ab33ad5402d71 and chunk_db24b720e895b599, is responsible for converting that field event into battle setup variables. It does not build the whole enemy AI. It does not animate every attack. It says, in effect, “the next battle is a trainer battle, here is the trainer table entry, here is the callback path for returning to the field, and here is the transition into battle.”

Then the handoff happens. In the same file, chunk_a8e4b22a367933e7 is the important handoff region for our purposes. Connect that to week one. We already studied the main callback shell and SetMainCallback2 in pokefirered/src/main.c, chunk_6cfd90f6d0136799. We do not need to re-teach the callback model now, but we should recognize it when it appears. Battle startup is not magical. It is another takeover of the main loop’s second callback. The field does not keep running as the primary mode while battle happens. The program changes which state machine owns the frame.

Pause there and check your understanding. If a trainer sees the player, what is the first architectural conversion? It is not “trainer attacks.” It is “field condition writes battle setup state.” Then the callback handoff gives another subsystem ownership of the frame. That is the architectural move.

Now let’s move to the right side of the board: battle runtime. The battle side consumes setup state in stages. The first stage is interpretation of battle type. gBattleTypeFlags tells the initialization code how many battler positions are needed and what special controllers or command sets may be legal. In an ordinary single wild battle, the active battler count is two. In an ordinary single trainer battle, still two. In a double battle, four. The trainer’s party size does not decide the number of active battlers. A trainer may have six Pokémon, but in a single battle the active opponent battler count begins as one. Party size describes reserves. Battler count describes the live battlefield.

This distinction is one of the places where advanced students start to see why the engine stays manageable. The party is a roster. The battler is a battlefield role. The same party slot can become attached to a battler, faint, be replaced by another party slot, and later be represented in UI or post-battle results. But the party slot and the battler position are not the same identity.

Now look at pokefirered/include/battle.h, chunk chunk_4079f42fde2b4370. That chunk is our source anchor for battler identity and action constants. The position constants give names to the meaningful battlefield seats: player-left, opponent-left, player-right, opponent-right. These are not decorative names. They let unrelated systems agree. Sprite placement, controller selection, target selection, healthbox placement, and move legality all need a common language for “which participant is this?”

So if I write opponent-left on the board, I am not merely describing screen geometry. I am describing a semantic slot. In a single trainer battle, opponent-left is the opponent’s active combatant. In a double battle, opponent-left and opponent-right may both be occupied. In a player double battle, player-left and player-right may both matter. This lets the battle engine scale from two participants to four without rewriting every concept as a special case.

Here is the trap. Do not say, “battler zero is always my first party Pokémon and battler one is always the enemy.” That may be a convenient mental shortcut for the simplest case, but the architecture uses position and side abstractions because the system must handle more than the simplest case. The constants in battle.h are there because battle logic should ask, “what side and position is this battler?” rather than hard-code one story about one battle format.

Now, how do trainer records become enemy party memory? Start with the data table contract. The trainer table tells the game which trainer class, name, party size, party data pointer, battle flags, and related fields belong to a trainer. The party structs in pokefirered/include/battle.h, chunk chunk_4e3097f639b05787, matter because trainer parties are not all stored with the same richness. Some trainer Pokémon are simple: species and level, default moves, no held item. Others carry custom moves or held items. The struct variants allow the data table to avoid paying for unnecessary fields on every trainer while still supporting richer boss or special encounters.

So, board cue: draw gTrainers indexed by trainer ID, then an arrow to a party data pointer, then an arrow to gEnemyParty. That last arrow is the runtime materialization step. The trainer record is const data. gEnemyParty is mutable battle-ready party memory. Once a Pokémon is created there, it has current HP, PP, status-bearing data, personality-derived details, and the other concrete fields needed by battle code. We already studied party slot ownership and summary screen reads, so I will not re-teach the Pokémon data model. Just carry that forward: battle does not fight with trainer table rows directly. It fights with Pokémon records loaded from those rows.

For a wild battle, the source of the opponent is different, but the destination is conceptually similar. There is no trainer table entry to expand. The field encounter system has already selected or supplied a wild species and level. Battle setup says this is a wild battle, and battle initialization creates the enemy-side Pokémon from that encounter information. So in the diagram, the trainer path goes through gTrainers, while the wild path goes through encounter selection, but both paths converge on enemy party memory and active battler assignment.

Now let’s add controllers. This is where the action constants from battle.h become meaningful. Constants such as “use move,” “switch,” “use item,” or “run” are not free-floating gameplay behavior. They are options interpreted through a battler controller. A player-controlled battler gets its action through input and menus. An opponent trainer battler gets its action through AI and trainer rules. A wild battler may have a different decision path. Safari-style battles constrain the command set differently. Link battles introduce still more coordination concerns.

So when you see action constants in pokefirered/include/battle.h, chunk chunk_4079f42fde2b4370, read them as vocabulary, not as execution by themselves. The vocabulary only does work after a controller is installed for a battler and after the battle mode has determined which actions are legal. This is the same design idea we saw in the move relearner state machine, but at a larger scale. The constant names alone do not produce behavior. They become behavior when a state machine reads them, validates them, and advances.

Let’s test that with a concrete scenario. A youngster trainer on a route sees the player. The overworld detects line of sight and starts the trainer approach. The script and setup code identify the trainer. Battle setup writes trainer battle flags and the trainer opponent ID. The field transitions out. Battle initialization reads the battle type flags and sees that this is a trainer battle, not a wild battle. It decides there are two active battlers for a normal single battle. It assigns the player-side active Pokémon to player-left and the trainer-side active Pokémon to opponent-left. It loads the trainer’s party definition from the trainer table into enemy party memory. It installs a player controller for the player battler and an opponent controller for the trainer battler. Only after all of that does an action like “use move” become a live decision in the turn system.

Now, what would change for a double battle? Do not change the whole diagram. Change the flags and the number of occupied positions. That is the point. The same contract expands. The setup state says the battle type requires double-battle handling. Initialization prepares player-left and player-right, opponent-left and opponent-right, subject to the exact battle variant. Targeting and UI now have to represent four battlefield seats, but they are using the same positional vocabulary rather than inventing a second architecture.

And what changes for a wild battle? Again, not the whole diagram. Remove the trainer table lookup. Keep the setup flags. Keep enemy party materialization. Keep battler positions. Keep controller installation. A wild opponent still needs opponent-left, a Pokémon record, a sprite, a healthbox, and an action decision path. The difference is where the opponent Pokémon came from and which actions are legal for the player. For example, running may be a normal wild-battle action but not a normal trainer-battle action. That is not a property of the move list. That is a property of the battle context.

Here is a check for understanding. If I hand you a bug report that says, “After a scripted trainer battle, the wrong enemy party appears,” where do you start? You do not start in the move execution code. You start at the setup contract. Was the correct trainer ID written? Did the battle type flags describe the intended battle? Did the handoff preserve the intended callback path? Then you inspect the trainer table entry and party struct variant. Only after confirming that the correct enemy party was materialized would you move deeper into battler initialization.

Second check. If the bug says, “In double battles, the right-side opponent’s healthbox appears in the wrong place,” do you blame trainer party structs first? Probably not. That sounds like battler position, side, sprite, or healthbox placement logic. The party table may be perfectly correct. The issue may be that opponent-right is being treated as opponent-left, or that code is assuming a two-battler layout. That is why the position constants matter as an architectural contract.

Third check. If the bug says, “The player can run from a trainer battle,” what setup fact should you suspect immediately? Battle type. The legal action set should be constrained by the battle context. If the system thinks it is a wild battle, or if a controller is using the wrong command availability rules, the action constants may be interpreted incorrectly.

Now connect this to the lab. When I ask you to design a battle setup record that can start wild, trainer, and scripted battles, I am not asking for a vague class called BattleStarter. I am asking you to name the contract. Your record needs to represent battle type flags, opponent trainer identifiers where applicable, wild species and level or a reference to the selected encounter where applicable, party slots or party ownership expectations, script return callbacks, field freeze state, transition choice, and any special-case flags that affect controller commands. The lesson from FireRed is that battle setup is not a giant object containing the whole battle. It is a carefully bounded handoff record.

That is also why this week belongs exactly where it is in the course. Earlier, we studied stat calculation inputs, move slots and PP, learnset tables, summary screen reads, and move-learning menus. Those gave us confidence that a Pokémon is not a loose bag of text. It is structured runtime data derived from tables and modified through systems. Today, we are watching the overworld choose when that structured party data becomes battle data. In the next stretch of the course, when we discuss turn execution, AI, battle scripting, effects, and UI synchronization, this setup contract will be assumed. We will not ask every turn, “where did the opponent come from?” The answer is: the field world wrote setup state, battle initialization consumed it, and active battlers were created from that contract.

So the local synthesis is this: a battle starts before the battle engine begins animating anything. It starts as a world-state decision. But it becomes a battle only when setup state is consumed into battler count, battler positions, party memory, controller assignments, and legal action vocabulary. That is the clean transformation. A route trainer, a patch of grass, or a script command all become the same kind of runtime machinery: occupied battlefield positions with Pokémon records and controllers. Next, we will stay on the battle side of the boundary and walk through that initialization sequence more slowly, especially the point where active battlers are connected to sprites, healthboxes, and the first request for an action.

Good. So let us now move one step inward. We have crossed the door from field logic to battle logic, but we should not imagine that the battle engine wakes up already knowing everything. It does not begin with a beautiful object called “battle” that contains every participant, scene, and rule. FireRed is much more C-like and much more global-state-oriented than that. The handoff is a set of variables, flags, constants, party records, and callback choices that are interpreted in a particular order.

Let me put that on the board as a sentence: battle setup is not object construction; it is state preparation followed by callback transfer. That distinction matters. In a modern engine, you might instantiate a BattleScene object, pass it an EncounterDescriptor, and let dependency injection handle the rest. FireRed instead says, effectively, “write the right global setup state, set the main callback, and let the next subsystem consume that state.” That is why `pokefirered/src/battle_setup.c`, especially the setup flow chunks we have been using, `chunk_db24b720e895b599` and `chunk_a8e4b22a367933e7`, is such an important architectural bridge. It is not merely convenience code. It is the place where the overworld’s interpretation of an encounter becomes the battle engine’s starting conditions.

Now, a check for understanding before we go further. If I ask, “Where is the wild Pokémon chosen?” you should not answer, “inside the turn engine.” That would be too late. If I ask, “Where does the battle decide that the player has four move buttons?” you should not answer, “inside the route map.” That would be too early. The encounter source and the interface vocabulary are split across the boundary. The world decides that a battle should happen and prepares encounter identity. The battle subsystem consumes that setup and then exposes battler positions, actions, healthboxes, controllers, and menus. That split is one of the main lessons of this week.

So let us trace a concrete example. Imagine the player steps into grass on Route 3. The overworld frame loop is still alive. We are still in the field callback world that we studied earlier in the semester: main callback, per-frame tasks, object events, scripts, tile behaviors. The tile behavior and encounter logic decide that a wild encounter is eligible. At that moment, the game has a map context, a player position, a party in save-backed memory, random number state, and a candidate encounter table. None of that is yet a visible battle. There is no enemy healthbox on screen merely because grass was stepped on. Instead, setup code records the relevant battle type, arranges the opponent data, chooses the transition, and schedules the shift away from overworld control.

This is why the include list at the top of `pokefirered/src/battle_setup.c` is itself meaningful evidence. In the packet, `pokefirered/src/battle_setup.c` `chunk_494d132a59422c85` shows that this file includes `overworld.h`, `pokemon.h`, `event_data.h`, `event_object_movement.h`, `field_player_avatar.h`, `battle.h`, `battle_transition.h`, and related field and script headers. That is not random clutter. That include set tells you that battle setup has to speak both languages. It must understand the field world well enough to freeze or redirect it, and it must understand the battle world well enough to fill in the contract expected by the battle engine.

Now I am going to draw two columns on the board. Left column: field-owned facts. Right column: battle-owned facts. In the left column I write map, tile, object event, trainer ID, script pointer, player avatar, callback to return to. In the right column I write battle type flags, battler IDs, battler positions, party indexes, controller commands, action constants, transition kind. The handoff is not all left or all right. Battle setup is the narrow bridge between these columns.

Let us stay with the wild battle example. The player’s party already exists. We covered party slot ownership in previous lectures, so I will not re-teach the data model, but remember the important connection: battle does not invent the player’s Pokémon from scratch. It reads from the same party records that the summary screen and move systems read from. Earlier, when we studied `pokefirered/src/pokemon_summary_screen.c` `chunk_e8e17a42bbf17d46`, the relevant lesson was that gameplay screens are views over existing Pokémon data, not separate copies of creature identity. Battle follows that same architectural idea, but with more aggressive runtime state layered on top. The party record gives the engine species, stats, moves, PP, held item, current HP, status, and so on. The battle engine then wraps that with active battler state: position, side, chosen action, animation state, healthbox, controller, and volatile battle conditions.

For the wild opponent, by contrast, setup must create or select an opponent Pokémon record before the battle can use it. That is a key asymmetry. The player’s side usually comes from existing party memory. The wild side is generated from encounter data and stored into battle-facing enemy party memory. Trainer battles are different again: the opponent is not randomly generated from grass, but pulled through a trainer table contract. That is why the trainer party structs in `pokefirered/include/battle.h` `chunk_4e3097f639b05787` and the trainer table contract in `pokefirered/include/battle.h` `chunk_403d30271e2270d6` are part of this week’s source focus. They define the shape of trainer-owned Pokémon before battle setup converts them into active opponents.

Notice the design pressure here. Wild battle setup needs encounter generation. Trainer battle setup needs trainer table decoding. Scripted battle setup may need special variables or explicit species and level choices. But after the boundary, the battle engine strongly prefers a unified view: there are battlers, parties, sides, actions, controllers. That is the architectural compression. Many sources of encounter become one battlefield vocabulary.

Now, if you are designing your lab record this week, this is where your thinking should become precise. It is not enough to say, “battle type equals wild” or “battle type equals trainer.” Your record has to answer what state is owned by the overworld, what state is copied or referenced, what state is consumed exactly once, and what callback resumes after battle. For a wild battle, your setup record needs some representation of species or encounter table result, level, battle type flags, transition type, player party source, enemy party destination, and post-battle continuation. For a trainer battle, it needs trainer ID, trainer party format, trainer party pointer or table index, seen-defeated flags or event variables, and the script continuation. For a scripted battle, it may need explicit overrides: forced species, special battle type flags, alternate music, no-running rules, or a callback that returns to a script command rather than ordinary field movement.

Let us now connect that to `battle.h`. Last time, we treated `pokefirered/include/battle.h` as a subsystem map. Today I want you to see why that was not just a header-reading exercise. The battler identity and action constants in `pokefirered/include/battle.h` `chunk_4079f42fde2b4370` give the rest of the program a shared vocabulary for what a participant can be and what a participant can request. That vocabulary is necessary because setup cannot simply say, “start a fight.” It has to prepare a runtime in which a battler may occupy player-left, opponent-right, partner positions, or link/multibattle variants, and then eventually request actions such as fighting, using an item, switching, running, or doing nothing under special conditions. Even before the first menu appears, the engine needs a legal space of possible action.

Here is the subtle point, and advanced students should hold onto it: action constants are not just UI labels. They are protocol values. The player-facing menu is one consumer of them, but the AI, link controllers, scripts, and forced actions also need a common representation. That is why we keep using the phrase interface contract. When the battle engine asks a battler controller for input, it is not asking for a button press in the abstract. It is asking for an action in a constrained vocabulary. If your custom RPG architecture does not define that vocabulary early, your battle setup will become tangled with UI assumptions.

Let me ask a quick room question. Suppose you are implementing a double battle. Does the meaning of “player battler” still mean exactly one active creature? No. It means a side or ownership relation that may include multiple occupied positions. That is why battler position constants and side constants need to exist independently from party slots. Party slot zero might be the lead Pokémon in the player party, but battler zero is a runtime participant index. Those two often correlate at the start of a simple single battle, but they are not the same concept. If you collapse them, switching, double battles, faint replacement, and link battles become painful.

So, in our Route 3 example, the first non-fainted party member may become the player’s active battler. But the party slot remains a party slot. The battler is an active projection of that party slot into battlefield state. On the opponent side, the generated wild Pokémon may be placed into enemy party memory and then projected into an opponent battler position. This is the same pattern we saw in earlier weeks with summary screens and move-learning menus, but with a more volatile runtime wrapper. The summary screen reads and edits selected party data through menu state. The battle engine reads party data, then creates a live combat role with controller and action state.

Now, let us talk about callback transfer because it is easy to hand-wave. Earlier in the course, when we studied `AgbMain`, `SetMainCallback2`, and `RunTasks`, the key lesson was that FireRed’s runtime shell is callback-driven. A system becomes “the current game mode” when it owns the main callback and the tasks that run under it. Battle setup therefore cannot merely call a function that draws battle until it returns, like a blocking subroutine. It has to move the whole program into a different mode. That is why the handoff chunk, `pokefirered/src/battle_setup.c` `chunk_a8e4b22a367933e7`, matters so much. It is the point in the setup story where prepared battle state becomes scheduled battle execution.

On the board, I am writing: no blocking battle call. Underline that. The GBA game loop does not pause the overworld function on a stack frame for five minutes while the battle runs. It changes callbacks, fades, transitions, initializes battle resources, and later returns through a saved continuation. This is why battle setup must remember where to go after the battle. Losing that return path would mean the game could win the battle but have no coherent way to resume the map, script, or trainer event.

Here is a practical example from a clone architecture. Suppose you write `StartBattle(enemy)` and inside it you run a while loop until the battle ends. That may work in a terminal prototype. It is not the FireRed architecture. In FireRed’s style, you instead prepare something like `gBattleTypeFlags`, populate enemy party state or trainer state, set transition state, install a battle callback, and let the main loop continue frame by frame. The battle owns the screen for a while. When it is done, it calls back into the field or script continuation. That continuation might set a trainer defeated flag, resume a script, give money, show post-battle text, or restore player control.

Now, check your understanding here. Why is a callback return more flexible than a direct return from a blocking battle function? Because the battle may be entered from different sources. Grass encounters return to ordinary overworld movement. Trainer battles return to trainer event logic and may need to mark the trainer defeated. Scripted legendary encounters may return to a script that hides an object, sets a story flag, or starts dialogue. Safari battles may return to Safari Zone state. Link battles may return to a communication or multiplayer flow. One battle engine, several return contexts. Callback-based architecture supports that variety without requiring the turn engine to know every field scenario in detail.

This is also where constants and flags become architectural. Battle type flags are not decorative. They alter setup and later behavior. A trainer battle needs trainer opponent data and payout behavior. A wild battle permits capture under ordinary rules. A Safari battle changes command options. A scripted battle may suppress normal escape behavior. A double battle changes battler count and positions. A link battle changes controllers and synchronization. The flag word becomes a compact description of the battle’s mode. That description is read by multiple subsystems, so it must be stable and shared.

Now, if we zoom out to the week context, you should see why this lecture follows “battle.h as the Battle System Blueprint.” The header gives the vocabulary. `battle_setup.c` uses that vocabulary to translate world state into battle startup. Then the battle main code consumes it. We are deliberately walking from contract to construction to execution. That mirrors the overall course plan: data records, globals, callbacks, tasks, scripts, structs, and visible behavior. You saw that pattern in maps. You saw it in party screens. You saw it in move learning. Now you are seeing it at one of the most important boundaries in the whole game.

Let us do a more detailed trainer example, because trainer battles expose the contract more clearly. Imagine an NPC trainer spots the player. In the overworld, that trainer is an object event with position, movement behavior, sight range, local script, and event flags. The trainer is not yet a battler. The map system detects the interaction, locks player movement, runs approach behavior if needed, and begins the trainer script. The script or trainer logic identifies a trainer ID. That ID points into trainer data, whose party shape is described by the trainer party structs and table contract in `pokefirered/include/battle.h` `chunk_4e3097f639b05787` and `chunk_403d30271e2270d6`. Depending on the trainer party format, the entries may include species and level, or also moves and held items. Setup must interpret that format correctly.

Now ask: why not store every trainer Pokémon as a full party Pokémon record in the map file? Because data-driven games need compact tables and reusable formats. A trainer party table is a specification for generating or populating enemy battle data, not necessarily a fully materialized runtime party at rest. This is exactly the kind of distinction advanced architecture students need to make. Persistent or static content can be compressed and declarative. Runtime content often needs expanded, mutable state. In battle, HP changes, status changes, PP changes, volatile effects appear and disappear, sprites animate, and AI decisions consume current state. So setup is partly a decoding step: it turns compact trainer data into battle-ready party records and active battlers.

And this gives us another check for understanding. If a trainer has three Pokémon, does that mean there are three opponent battlers on screen? In a normal single battle, no. It means there are three Pokémon in the opponent party, with one active at a time. The active battler is a runtime slot on the battlefield. The trainer party is a roster. Those are related but distinct layers. If the active Pokémon faints, replacement logic chooses another party member and projects it into the active battler position. The battler position may remain opponent-left, while the party slot behind it changes.

That separation is one of the reasons FireRed can support switching, faint replacement, trainer rosters, and double battle variants without making every creature permanently visible. The battle engine maintains battler slots as active roles. Party data supplies the roster behind those roles. Setup initializes the first mapping. Later battle flow updates it.

Let us also bring in the previous week’s work carefully. We studied move slots and PP, stat calculation inputs, level-up moves, and move relearner state machines. We do not need to re-teach those, but they matter here because battle setup assumes the party Pokémon are already valid creature records. If the player’s lead Pokémon has four moves and current PP, setup does not ask the learnset table what the Pokémon should know today. It uses the existing party record. That is a major architectural boundary. Learnsets and move learning systems determine how the party record got its moves. Battle setup simply brings that record into combat.

This prevents a class of design mistakes. If your clone recalculates a creature’s moves from species and level every time a battle starts, you erase player history. You erase TMs, move relearner choices, deletions, and special cases. FireRed does not do that. The battle starts from the actual party state. That is why we spent so much time on party data across gameplay screens. Battle is one more consumer of that state, not the owner of creature history.

Now let me gesture to the screen for a moment. In `pokefirered/src/battle_setup.c` `chunk_0e0ab33ad5402d71`, the source focus identifies battle setup itself; in `chunk_db24b720e895b599`, the setup flow becomes visible; and in `chunk_a8e4b22a367933e7`, the battle start handoff is the architectural hinge. Pair those with `battle.h` chunks for battler constants and trainer party contracts, and you have a source-backed chain: header vocabulary, setup translation, callback handoff. That is the chain I expect you to cite in the week 12 homework, not vaguely “the battle system.”

A concrete way to write that in your notes is: source event produces encounter intent; setup code writes battle mode and opponent data; callback transfer gives battle ownership of the frame loop; battle initialization maps party records to battler positions and controllers. That sentence is nearly the whole lecture in compressed form.

Let us now slow down on controllers. A battler is not just a Pokémon. A battler has an owner of decisions. For the player, the controller is usually human input through the battle UI. For a trainer opponent, it is AI. For a wild opponent, it is also AI, but under different mode assumptions. For link battles, the controller may represent remote input. For Safari-like modes, the player command vocabulary changes. This is why battler action constants in `battle.h` are part of setup architecture. Once a battler exists, the engine must know how to request, receive, and interpret that battler’s action.

Think of it as a message protocol. The battle engine says, “Controller for battler A, choose an action.” The controller eventually provides an action code and parameters. If the action is fight, the parameter is a move choice and often a target. If the action is switch, the parameter is a party index. If the action is item, the parameter is an item and sometimes a target party member. If the action is run, the battle type flags decide whether that action is legal and what resolution procedure applies. So setup does not merely create visuals. It creates the conditions under which legal action messages can exist.

Now, a useful comparison to modern data-driven systems. Pokemon Showdown, which we use only as a secondary comparison, tends to represent battle behavior in highly explicit simulator data and scripts. The research packet points to files like `pokemon-showdown/data/text/moves.ts` `chunk_92a6298340525a85`, where move text and effect descriptions are data-facing. That is useful for seeing another architecture’s battle data surface. But FireRed remains our primary source here because we are studying how a GBA RPG crosses from overworld runtime into battle runtime. Showdown helps us think about deterministic battle modeling and data hooks; it does not replace the FireRed callback and setup story.

So if you cite Showdown in this week’s homework, use it as contrast, not as the main answer. The main answer belongs to `pokefirered/include/battle.h` and `pokefirered/src/battle_setup.c`. FireRed’s battle setup problem includes field transitions, event flags, script callbacks, player avatar state, and GBA frame ownership. Those are not the same problem as a server-side simulator accepting two teams and producing a turn log.

Let us return to the moment after setup has handed off. The screen is transitioning. The overworld is no longer the active visual mode. Battle resources begin to load. The active parties are known. Battler count is determined. Positions are assigned. Sprites and healthboxes must be created. The first player-facing request is not yet “select a move” in the abstract; it is “given this battle mode, these battlers, these parties, and these controllers, request the next legal action.” That is why the last sentence of the previous segment said we would stay on the battle side of the boundary. We are now preparing to inspect how active battlers connect to sprites, healthboxes, and first action request.

Before we do that in the next segment, I want to give you a recap checkpoint. What have we established today? First, a battle starts as a world-state decision. Second, setup code translates that decision into battle-facing state. Third, `battle.h` supplies the shared vocabulary for battlers, actions, trainer party formats, and trainer table contracts. Fourth, callback transfer changes which subsystem owns the frame loop. Fifth, party data and battler state are related but distinct. Sixth, trainer, wild, and scripted battles differ before setup, but they converge into the same battlefield machinery afterward.

Now, test yourself with this example. A scripted legendary encounter begins when the player presses A on a stationary overworld object. The script shows text, maybe plays a cry, and starts a battle. What are the minimum pieces of state you need? You need the species and level or a way to derive them. You need battle type flags indicating the mode. You need enemy party initialization. You need the player party source. You need a transition and callback handoff. You need a return continuation so that after the battle the script can decide whether to remove the object, set an event flag, or continue dialogue. And once the battle begins, you need the same battler-position and action vocabulary as every other battle. That is the point: the entry path is special, but the battle runtime wants normalization.

One last check for understanding, and I want you to answer this in architectural language. Why is `battle_setup.c` allowed to include both field headers and battle headers? Because it is a boundary module. It is supposed to know enough about both sides to translate between them. But the deeper battle turn engine should not need to know every map interaction, and the ordinary map movement code should not need to know every battle action constant. Boundary modules are allowed to be bilingual. Core modules should be more disciplined.

That principle connects directly to the final part of the course. In weeks 13 through 16, we will keep asking where each system boundary lives. Inventory to battle. Battle to experience gain. Battle to evolution. Save data to runtime state. Link communication to battle controllers. The technique is the same one we have used all semester: find the data tables, find the structs, find the globals, find the callback or task handoff, and then trace the player-visible behavior. Week 12 is not a detour into battle trivia. It is where the architecture becomes visibly integrated.

So, as we move forward, keep the board diagram in mind: field-owned facts on the left, battle-owned facts on the right, setup code as the bridge. When you read the source, do not flatten everything into “the battle starts here.” Ask a sharper question: what source of world state is being translated, what battle contract is being filled, and which callback owns the next frame? That is the level of precision expected now, especially with the week 12 homework and quiz and the cumulative midterm sitting in this part of the course.

## Source References
- pokefirered/src/daycare.c
- pokefirered/src/pokemon_summary_screen.c
- pokefirered/src/battle_setup.c
- pokefirered/src/learn_move.c
- pokemon-showdown/data/text/moves.ts
- pokemon-showdown/data/abilities.ts
