# battle.h as the Battle System Blueprint

Lecture ID: lecture_w12_d01
Week 12, Day 1

## Objectives
- Trace FireRed gameplay from data records through globals, callbacks, tasks, scripts, structs, and concrete source functions.
- Connect week 12 material to the overall course plan.

## Transcript
Good afternoon. Today we move into week twelve, and this is the point in the course where the battle engine stops being a distant subsystem and becomes the thing that ties almost everything we have studied together. I want you to hear that carefully. Battle is not just “where moves happen.” Battle is where map state, script state, party state, trainer data, random encounter rules, callback scheduling, task scheduling, UI transitions, and generated creature records all have to agree on a common contract.

So our title today, “battle.h as the Battle System Blueprint,” is deliberately not “battle.c as the battle implementation.” We are starting with the header because, architecturally, the header tells us what the rest of the program is allowed to believe about battle. In FireRed, headers are not documentation in the modern API-reference sense, but they are interface contracts. They declare the constants, structs, globals, and function entry points that other modules use when they need to touch the battle system without becoming the battle system.

Let me put that on the board. I’m drawing a line from left to right. On the far left: map data and event scripts. Then overworld runtime. Then battle setup. Then battle globals. Then battle main loop. Then outcome handling back to overworld. That is the path for week twelve. The key thing today is that battle.h sits above the middle of that diagram. It names the shared vocabulary that lets those blocks communicate.

Now, remember where we are in the course. In the early weeks, we looked at AgbMain, the main callback slots, and SetMainCallback2. We studied RunTasks as a per-frame dispatch mechanism, and we practiced tracing visible behavior from data table to callback to task to screen. Then we moved through maps, object events, scripting, party data, stats, move slots, PP, learnsets, summary screen reads, and the move relearner as a menu state machine. I am not going to re-teach those today. I am going to lean on them. When I say “the player’s party,” I expect you to remember that this is not an abstract team object floating in the air. It is concrete party storage, concrete slots, concrete monster structs, and concrete access functions. When I say “callback handoff,” I expect you to remember that the program is not running a general operating-system event loop; it is explicitly changing which function owns the next frame.

So, let’s begin with the most important conceptual move. A battle is not started by the battle engine alone. A battle is assembled.

Say that back to yourself: a battle is assembled. There is world state: where the player is standing, what event object touched them, what trainer flag is active, whether grass generated an encounter, whether the script requested a special fight. There is party state: the player party, the enemy party, possible partner data, current HP, moves, PP, held items. There is trainer data: trainer ID, class, name, items, AI flags, party size, and a pointer into one of several party record formats. There is runtime control state: callbacks, tasks, transitions, saved callbacks, script contexts, flags. And then there is the battle engine proper, which consumes the prepared state.

The reason battle.h is powerful is that it exposes the names of this assembly language. In pokefirered/include/battle.h, chunk_4079f42fde2b4370, the header gives us battler identity and action constants. Those constants are not cosmetic. They encode a model of the combat stage. A battler is not merely “the first monster” or “the opponent’s monster.” A battler has a position. FireRed’s battle engine needs to distinguish player side from opponent side, and left flank from right flank, because the same battle loop supports singles, doubles, link formats, partner formats, and trainer variants. Even if a normal wild battle visibly shows one player creature and one wild creature, the architecture is wider than that visible case.

Let me pause and draw a small grid on the board. Two columns, left and right. Two rows, player and opponent. In a single battle, only one cell on each side is really active for the ordinary player experience. But the constants define the full stage. Player-left, opponent-left, player-right, opponent-right. That is the architectural move. The battle engine does not want every caller to invent its own way to say “the second opponent battler.” It wants a compact identity scheme that can be passed around by scripts, actions, AI, animation code, targeting code, and UI code.

Here is the first check for understanding. If I ask, “Why not just store a pointer to the Pokemon struct and call that the battler?” what is the problem? Think for a second. The problem is that the battler is not identical to the stored Pokemon record. The record tells you species, level, moves, PP, stats, friendship, personality, and so on. We covered that foundation already. But the battler tells you role in the current staged encounter. It answers questions like: whose side is this? Which active slot is this? Who can target this? Which sprite bank or healthbox belongs to this participant? Which action command is pending? So battler identity is a runtime battle concept layered on top of party data.

That distinction is one of the big architectural lessons of this week. Data records do not directly become gameplay. Data records are interpreted by runtime roles.

Now, still in battle.h, chunk_4079f42fde2b4370 also gives us action constants. Again, these are more than enum clutter. In a turn-based battle engine, a battler does not just “do stuff.” The engine moves through action categories. Use a move. Use an item. Switch. Run. Safari action. Execute a script-controlled action. Do nothing because fainted or otherwise unable. The exact constant names matter less for our lecture than the contract they form: the battle loop can store a chosen action as a compact code, compare it, dispatch on it, and coordinate all battlers without asking the UI, AI, or script layer to directly call deep battle internals.

That is classic data-driven engine architecture. You do not have every menu command immediately mutate the world. You have the menu produce an action record, and then the engine resolves the action under its own rules.

Let’s make that concrete. Suppose the player is in tall grass and a wild Pidgey is selected. I am using Pidgey just as a concrete example; the species choice is not the core issue. Before the battle engine can show turn one, something outside the battle core has already decided that an encounter exists. The overworld or encounter logic must arrange an enemy party slot. Battle setup must mark that the battle type is wild, not trainer. It must freeze or suspend ordinary field behavior. It must launch a transition. It must eventually hand control to the battle callback. Once inside battle, the active opponent battler is not “the wild data table.” It is the opponent-side active battler whose underlying monster data came from an enemy party record.

Now compare that to a trainer battle. The map event may reference a trainer ID. The trainer table tells us what kind of party record to use. The battle type is trainer. The opponent identity is a trainer, not just a generated wild monster. There may be trainer name, class, reward logic, pre-battle and post-battle scripts, party flags, AI flags, and item usage. The same battle stage still has battler positions. The same action system still resolves turns. But the setup path differs.

That is why week twelve’s source focus pairs battle.h with battle_setup.c. The header gives the contract; setup code performs the bridge.

Now let’s turn to trainer party structs. In pokefirered/include/battle.h, chunk_4e3097f639b05787, FireRed defines multiple trainer party record shapes. This is one of those places where an advanced reader should slow down, because it tells you how the designers balanced content authoring convenience against runtime precision.

A trainer’s Pokemon can be represented with no held item and default moves. Or with held item and default moves. Or with custom moves and no held item. Or with both held item and custom moves. Structurally, that means the data table does not force every trainer monster to pay for the most detailed representation. A youngster with a low-level Rattata does not need four custom move IDs and a held item field if default level-up moves are enough. A gym leader or rival may need custom moves, held items, or stronger AI context. So the party record format becomes part of the trainer contract.

Notice how this connects to our previous lectures without requiring us to re-open the whole move system. We already studied move slots and PP, level-up move data, and learnset tables. Here, the question is different. We are asking: when the enemy party is being created for a trainer, where do its initial moves come from? If the trainer party record says default moves, the setup path can derive moves from species and level using prior systems. If the record says custom moves, the table supplies them directly. That is an architectural reuse point. The trainer table does not duplicate the entire creature model; it supplies a compact authoring record that can be expanded into battle-ready Pokemon.

Let me write that on the board as a transformation: trainer party record becomes enemy party Pokemon becomes battler runtime state. Those are three different layers. If you collapse them in your mental model, you will misread the code.

The next chunk, pokefirered/include/battle.h, chunk_403d30271e2270d6, is important because it acts as the trainer table contract. The battle system and setup code need to know that there is a trainer table with records of a particular shape. That table is not just “some content.” It is a callable, indexable source of encounter configuration. When a map script or object event says, effectively, “start battle with trainer N,” the system can use that trainer ID as a key into structured battle data.

Let’s do a second check for understanding. If a trainer battle is started from an overworld event, which thing should the script pass around: all six Pokemon fully expanded, or a trainer identifier? In FireRed’s architecture, the answer is generally the identifier. Why? Because the trainer ID is stable content-level data. It lets setup code look up name, class, party flags, items, AI, party size, and party pointer consistently. If every script had to manually construct every enemy Pokemon, you would destroy the central table contract and scatter battle content across scripts.

That is exactly the kind of source-to-runtime boundary we have been tracing all semester. Data tables are not inert. They are the authoritative records that runtime systems expand.

Now, let’s bring battle_setup.c into the picture. In the research packet, pokefirered/src/battle_setup.c, chunk_494d132a59422c85, begins with a long include list: task.h, overworld.h, item.h, sound.h, pokemon.h, load_save.h, safari_zone.h, quest_log.h, script.h, script_pokemon_util.h, event_data.h, event_object_movement.h, metatile_behavior.h, field control headers, battle.h, battle_transition.h, and more. I want you to treat that include list as architectural evidence. A setup module with those dependencies is not a pure battle module. It is an adapter. It is the place where field runtime, event scripts, creature construction, sound, transition effects, and battle contracts meet.

If battle.h is the blueprint, battle_setup.c is the loading dock.

That metaphor is useful but let’s make it technical. The setup layer has to answer several questions before battle can begin. What type of battle is this? Which opponent or wild species is involved? Is the player allowed to escape? Is this a trainer, wild, Safari, link, scripted, or special battle? What transition animation should play? Which callback should run after the battle ends? Which scripts should resume? Which flags should be set so the overworld does not keep processing movement while the screen is entering battle?

In the week source focus, pokefirered/src/battle_setup.c, chunk_0e0ab33ad5402d71, is our battle setup anchor, chunk_db24b720e895b599 is the setup flow anchor, and chunk_a8e4b22a367933e7 is the battle start handoff anchor. We will spend more time on the exact flow next class, but today I want you to see how the header contract makes that flow possible. Setup can say, “this is a wild battle” or “this is a trainer battle” by setting battle-type state understood by battle. Setup can supply opponent identity through the agreed globals and structs. Setup can launch the transition and eventually yield to the battle callback because the runtime shell already supports callback replacement.

This is where the course plan folds back on itself. In week one, SetMainCallback2 looked like a low-level runtime convenience. In week twelve, it becomes the door into battle. In the task-system lectures, CreateTask and RunTasks looked like scheduling tools. In battle setup, they become a way to manage transitional behavior frame by frame. In the script lectures, script context looked like event plumbing. In battle setup, it becomes how a trainer event can suspend itself, start a battle, and later continue or mark itself defeated. In party lectures, party slots looked like data containers. In battle setup, they become the source material for battler construction.

So, when you are reading battle.h, do not read it as a list. Read it as a map of obligations. Every constant says, “other modules may encode this idea this way.” Every struct says, “content or runtime state must be shaped this way.” Every extern says, “there is shared global state somewhere else, and the system is allowed to coordinate through it.” That last point should make you a little uncomfortable in a modern software-engineering sense. FireRed uses globals aggressively. But on the Game Boy Advance, in C, in this engine style, globals are part of the architecture. The discipline is not “avoid all globals.” The discipline is “know which module owns which global meaning, and know which setup path is responsible for preparing it.”

Let’s walk one more concrete example, this time trainer-side. Imagine the player steps into a trainer’s line of sight. The overworld object event system detects the encounter condition. The event script or trainer approach logic identifies a trainer. The setup code records the trainer opponent. The trainer table contract from pokefirered/include/battle.h, chunk_403d30271e2270d6, lets the code look up that trainer record. The party format definitions from chunk_4e3097f639b05787 tell the code how to interpret the trainer’s party pointer. If the record has custom moves, those move IDs are copied into the generated enemy Pokemon. If it uses default moves, prior move-learning logic can be used to infer appropriate moves at level. Then the battle type and battler stage contract from chunk_4079f42fde2b4370 tell battle how to treat participants once the engine starts.

At no point did the map event need to know the detailed turn-resolution algorithm. At no point did the trainer table need to know the screen transition implementation. At no point did the battle loop need to know which grass tile or trainer object caused the fight. That separation is not perfect, and FireRed is not a pristine layered architecture, but the separation is real enough to study and reuse.

Now, I want to call out a common mistake. Students often look for “the battle start function” as though there is one magic place where the entire encounter begins. That instinct is understandable, but it is too coarse. There is a wild encounter decision. There is enemy party preparation. There is battle setup. There is transition creation. There is callback handoff. There is battle initialization. There is turn action selection. There is outcome handling. Depending on what you mean by “start,” any of those could be the start.

Advanced architecture work requires choosing the right boundary for the question. If your question is “how does a trainer ID become enemy Pokemon data,” you start with battle.h trainer structs and the trainer table contract. If your question is “how does the screen leave the overworld and enter battle,” you start with battle_setup.c setup flow and callback handoff. If your question is “how does the player’s selected command become resolved damage,” you move later into battle command and turn execution code. Today we are at the boundary before turn resolution.

Let me check the room. Suppose I give you three artifacts: a trainer ID, the player party array, and a battler action constant. Which one belongs earliest in the flow? The trainer ID belongs earliest as content-level encounter identity. The player party array already exists before setup but becomes relevant during battle preparation. The battler action constant belongs later, after battle has begun and a participant is choosing or receiving an action. If those feel interchangeable to you, slow down. They live at different layers of the runtime story.

And this is also how I want you to approach the lab. You are going to design a battle setup record that can start wild, trainer, and scripted battles. That record must say which overworld variables, party slots, trainer IDs, and callbacks it touches. Do not design it as a vague “BattleConfig” blob. Make the lifecycle explicit. For a wild battle, where does species and level come from? For a trainer battle, where does trainer identity come from? For a scripted battle, what overrides normal generation? What callback resumes after the battle? What global battle type flags must be prepared? What script state must pause? What party slots are read or written?

That lab is not busywork. It is a compression of FireRed’s architecture into your own design vocabulary.

So, to recap this first stretch: battle.h is our battle-system blueprint because it names battler positions, action categories, trainer party record formats, and trainer table expectations. The battler constants in pokefirered/include/battle.h, chunk_4079f42fde2b4370, define the staged identities used after the encounter is assembled. The trainer party structs in chunk_4e3097f639b05787 define how authored trainer content can be compact or detailed. The trainer table contract in chunk_403d30271e2270d6 makes trainer IDs usable as runtime encounter keys. And battle_setup.c, especially the setup and handoff material in pokefirered/src/battle_setup.c, chunks chunk_0e0ab33ad5402d71, chunk_db24b720e895b599, and chunk_a8e4b22a367933e7, is where the overworld side begins converting those contracts into an actual battle launch.

In the next portion, we will zoom in on the exact handoff from world state to battle state, and I want you to keep today’s board diagram in mind: records become globals, globals feed callbacks and tasks, callbacks and tasks produce visible behavior, and visible behavior eventually returns control to the world.

All right, let’s zoom in on that handoff. The most important thing to notice is that “start a battle” is not one kind of event. It is a family of events that all need to converge on a compatible battle runtime. A wild encounter, a trainer seeing the player, a scripted battle, a tutorial battle, and a special-case battle all begin from different world-side causes. But once the battle engine takes over, it needs a consistent vocabulary: who are the battlers, what side are they on, what action categories can they choose, what party data is available, and what callback or transition now owns the screen.

So, on the board, I’m going to draw three lanes. Left lane: world cause. Middle lane: battle setup. Right lane: battle runtime contract. In the left lane, I’ll put “grass encounter,” “trainer object event,” and “scripted encounter.” In the middle lane, I’ll put “select encounter data, set shared state, start transition.” In the right lane, I’ll put “battler positions, actions, party records, controller flow.” The middle lane is today’s bridge. The right lane is where battle.h becomes the blueprint.

Now, battle.h itself is not the bridge. That is the first advanced distinction. A header tells other modules what names, structs, constants, and globals exist. It does not by itself prove execution order. The research packet says this explicitly in its warning about pokefirered/include/battle.h: use it as an interface contract and pair it with implementation chunks for “what calls what.” So when we cite pokefirered/include/battle.h, chunk_4079f42fde2b4370, for battler identity and action constants, we are saying, “this is the public vocabulary the battle system exposes.” We are not saying, “this header starts battles.” Starting battles belongs to the implementation side, especially pokefirered/src/battle_setup.c, chunks chunk_0e0ab33ad5402d71, chunk_db24b720e895b599, and chunk_a8e4b22a367933e7, which the packet identifies as battle setup, setup flow, and battle start handoff.

Let’s make that concrete. Suppose the player takes a step in tall grass and the encounter system decides a wild Pokémon should appear. At the creature-data level, we already have prior foundations: species identity, level, move slots, PP, party ownership, and stat inputs. I am not going to re-teach those. What matters here is that the world-side event must produce enough information for battle setup to create or select an opponent and then move the game out of ordinary overworld control. The player does not experience that as “a global state migration.” The player experiences a screen transition and then a battle. But architecturally, a callback-controlled overworld mode is yielding to a callback-controlled battle mode.

This is why the include block in pokefirered/src/battle_setup.c, chunk_494d132a59422c85, is so revealing. That chunk includes overworld.h, script.h, event_data.h, event_object_movement.h, metatile_behavior.h, fieldmap.h, field_control_avatar.h, field_player_avatar.h, field_screen_effect.h, field_message_box.h, vs_seeker.h, battle.h, battle_transition.h, battle_controllers.h, and constants/battle_setup.h, among others. That is not random dependency sprawl. It tells us battle setup sits at a crowded border. It must understand enough about field state, event state, player avatar state, script state, and battle state to convert an overworld situation into a battle launch.

Check for understanding. If battle_setup.c includes both script.h and battle.h, what does that suggest? Not that scripts execute battle turns. It suggests the setup layer can be entered from or coordinated with the scripting/event layer, and then it must speak the battle system’s public contract. If it includes field_control_avatar.h and battle_transition.h, what does that suggest? That the launch is not just data creation. It also affects field control and visual transition. Good. That is the boundary we are reading.

Now let’s compare wild and trainer battles. A wild battle can be thought of as: current map and encounter conditions lead to a generated opponent. A trainer battle can be thought of as: a trainer identity leads to a trainer record, and that trainer record leads to authored party data. Those are different data origins. But they both need to become battlers in the battle runtime. That is where pokefirered/include/battle.h, chunk_4079f42fde2b4370, matters. The battle engine cannot keep thinking in terms of “the object event who saw me” or “the grass tile that rolled an encounter.” It needs staged identities like player-side battler, opponent-side battler, and action categories. The identity vocabulary is intentionally smaller than the world that produced it.

Here is the design lesson: setup narrows the world. The overworld contains maps, tiles, object events, scripts, flags, variables, avatar direction, trainer sight lines, weather, special cases, and music state. Battle cannot carry all of that as first-class logic inside every turn decision. So setup extracts the battle-relevant facts and installs them into the battle-facing structures and globals. Once that narrowing is done, the battle engine can ask battle questions: which battler acts, what action was chosen, what target exists, what move is current, what controller should respond. That narrowing is one of the reasons the architecture remains understandable.

Now, for trainer parties, the narrowing begins even earlier, in the data contract. The trainer party structs in pokefirered/include/battle.h, chunk_4e3097f639b05787, define that authored trainer content can have different shapes. Some trainer Pokémon are compact: species and level, with default moves and no held item. Others are more detailed: custom moves, items, or extra authored fields. The trainer table contract in pokefirered/include/battle.h, chunk_403d30271e2270d6, then makes trainer IDs usable as runtime encounter keys through the trainer table expectation, including gTrainers according to the packet’s source index. So a trainer battle is not “invent a party at runtime.” It is “use a trainer ID to select an authored trainer record, interpret that record’s party format, then prepare battle state.”

Board cue: I’m writing “trainer ID is not a party.” Under it, I’m writing “trainer ID points to trainer record; trainer record points to party data; party data is interpreted by flags or format; battle setup converts it into runtime opponents.” That middle phrase, “interpreted by format,” is the whole reason chunk_4e3097f639b05787 matters. The codebase can store trainer parties economically while still allowing important trainers to have custom moves or items.

Let’s run a concrete example. Imagine a route trainer with ID 120. The map object or script path identifies trainer 120 as the opponent. The trainer table entry gives the trainer’s metadata and party pointer. If the trainer uses a simple party format, the setup flow can read species and level and let default move generation fill in the rest. If the trainer uses a richer format, setup must honor the authored moves or items. In both cases, after setup, the battle runtime should not care whether the opponent came from the compact format or the rich format. It should see battler-side Pokémon with usable battle data. That is contract conversion.

This is also where your week 12 lab should be stricter than your earlier labs. Earlier in the course, I might accept a minimal trainer object that simply contains an array of monsters. Now, because you have already been assessed on identifying files, structs, constants, and state variables, I expect you to model the handoff more carefully. Your battle setup record should separate “why the battle starts” from “which runtime data battle needs.” A trainer ID is a cause-side key. A generated wild Pokémon is setup-side product. A battler index is battle-runtime identity. If you blur those into one field called opponent, you will paint yourself into a corner.

Pause for a check. Why should a trainer object event not become the battler identity directly? Because the object event belongs to the overworld. It has map position, movement, facing direction, local object ID, script association, and field behavior. A battler identity belongs to the battle engine. It has side, position, chosen action, move target, controller behavior, and battle state. The same trainer NPC can cause a battle, but it should not remain the unit of turn execution. Good.

Now let’s connect this to callbacks and tasks. In week one, we anchored the runtime shell with pokefirered/src/main.c, chunk_6cfd90f6d0136799, for SetMainCallback2, and pokefirered/src/task.c, chunk_cb0bb91a147bfb59, for CreateTask, plus chunk_3ffebc0341225936 for RunTasks. You should now hear those names whenever we talk about a mode transition. The overworld is not a blocking loop that calls battle and waits. FireRed’s structure is frame-based. A setup function or task can arrange state and then the main callback can be switched so the next frames are owned by transition or battle code. That is why battle_setup.c importing task.h in chunk_494d132a59422c85 is meaningful. It reminds us that battle launch participates in the same per-frame runtime discipline as menus and overworld behavior.

So, when we say “handoff,” do not imagine a single call stack shaped like overworld function calls battle function calls battle result returns. The actual architecture is more like: overworld detects or receives battle cause; setup writes shared battle-relevant state; a transition or task sequence runs over frames; callback ownership changes; battle runtime initializes its own state; later, after battle result handling, control returns to an overworld or script continuation. That is the source-grounded course thesis again: data table, runtime globals, callback or task or script, visible behavior, and eventually return.

Let me give you a comparison to the move relearner from last week, but briefly. In the move relearner, the script started a UI flow, a callback state machine preserved intent over frames, and a result variable told the script what happened. In battle setup, the stakes are larger, but the shape is familiar. A world event starts a subsystem transition. The subsystem cannot finish instantly because it has visual transition, loading, controller setup, and battle initialization. State must live across frames. Then some result eventually informs the world what to do next. We are not re-teaching move replacement here; we are using it as a prior foundation for recognizing staged, frame-safe handoffs.

Now, let’s talk about action constants. The battler action categories in pokefirered/include/battle.h, chunk_4079f42fde2b4370, are not merely turn UI labels. They are part of the battle engine’s coordination contract. A player may choose Fight, Bag, Pokémon, Run, or other action categories depending on context. A trainer AI may choose moves or switches. A link battle may have controller synchronization concerns. The key point is that action choice is indexed by battler, not by overworld cause. Once battle begins, the battle runtime stores and resolves chosen actions using battler-oriented structures. That is why the handoff must assign participants into battler positions before turn logic can be coherent.

A good way to test your own understanding is to ask, “At what point does the system stop asking map questions and start asking battler questions?” In a wild encounter, map questions include: where is the player, what tile or metatile behavior applies, what encounter table or condition is active? In a trainer encounter, map questions include: which object event, which trainer ID, whether line of sight triggered, whether the trainer has already been defeated. But after setup, battle questions include: which battler is on which side, what party slot is active, what action is selected, what move or item is being processed, and what battle script instruction is current in later weeks. The boundary between those question sets is the setup layer.

This is why I want you to be careful with globals. FireRed uses shared globals extensively, especially in battle, and battle.h later exposes major global battle variables in chunks such as chunk_8367bbcb71bfc88d according to the source index. But a global is not a design explanation by itself. If you say, “the battle starts because a global was set,” you have not explained the system. You must say who writes the global, what contract it satisfies, what callback or task reads it, and what player-visible behavior results. That is the bar for week 12.

Let’s do a small verbal trace. A trainer battle begins. What data record is first relevant? The trainer table contract, identified in pokefirered/include/battle.h, chunk_403d30271e2270d6. What authored party shape may be used? The trainer party structs from pokefirered/include/battle.h, chunk_4e3097f639b05787. What setup implementation family converts that into launch state? pokefirered/src/battle_setup.c, especially chunks chunk_0e0ab33ad5402d71 and chunk_db24b720e895b599 for battle setup and setup flow. What handoff material matters as the battle actually starts? pokefirered/src/battle_setup.c, chunk_a8e4b22a367933e7. What runtime vocabulary receives the result? The battler identity and action constants in pokefirered/include/battle.h, chunk_4079f42fde2b4370. And what broader engine mechanism lets control move between overworld and battle? The callback and task shell we anchored earlier with SetMainCallback2 in pokefirered/src/main.c, chunk_6cfd90f6d0136799, and RunTasks in pokefirered/src/task.c, chunk_3ffebc0341225936.

That is the kind of trace I expect you to be able to produce on the midterm. Not because I want citations for their own sake, but because citations force you to distinguish contract from implementation. battle.h is contract. battle_setup.c is implementation bridge. main.c and task.c are runtime shell. trainer records are authored data. Party Pokémon are runtime creature state. Player-visible battle is the outcome of all of those cooperating.

Now, one more concrete design example for the lab. Suppose you are designing a battle setup record for a clone. I would expect fields like battle kind, maybe wild or trainer or scripted; trainer ID when relevant; generated opponent species and level when relevant; player party source; opponent party source; return callback or script continuation; transition kind; maybe flags for special modes. What I do not want is one giant “Battle” object that already contains every turn variable before the battle has even started. Setup is not the battle main loop. Setup is the preparation layer.

If your record starts a wild battle, it may need to touch encounter-derived species and level, the player party, battle type information, transition choice, and the callback handoff. If it starts a trainer battle, it must touch trainer ID, trainer table lookup, trainer party format, defeated-or-not progression state, and the same transition handoff. If it starts a scripted battle, it may receive more direct parameters from script variables and must report back to the script world afterward. Same destination, different origins.

Check for understanding. What is shared among wild, trainer, and scripted battle setup? They all must eventually provide battle with participants, side identity, and a transition into battle runtime. What differs? The source of the opponent and the world-side bookkeeping. Wild battles derive from encounter conditions. Trainer battles derive from trainer table records and event progression. Scripted battles may derive from explicit script parameters or special event logic. Good.

So the synthesis for this portion is this: battle.h gives us the battle system’s public nouns and categories; battle_setup.c performs the world-to-battle translation; the callback and task shell makes that translation happen over frames; and trainer party records show how authored data can remain compact until setup interprets it. In the next portion, we will keep the same board diagram but follow the trainer path more closely, because trainer battles are the cleanest place to see IDs, data tables, event state, and battler runtime identity all line up in one trace.

All right, keep that diagram on the board, because now we are going to make the trainer path concrete. The important move here is to separate three identities that beginners often collapse into one. There is the trainer identity, which is an authored database identity. There is the object-event identity, which is the overworld person standing on the map. And there is the battler identity, which is the battle engine’s runtime slot after the encounter has crossed the boundary. If you mix those together, the architecture becomes foggy. If you keep them separate, FireRed’s setup code becomes much easier to read.

So, on the board, I am writing three labels: trainer ID, map object, battler slot. The trainer ID says, “Which row in the trainer table?” The map object says, “Which visible NPC did the player just interact with, or which NPC noticed the player?” The battler slot says, “Once battle begins, which side and position is this participant occupying?” That last vocabulary is part of the public battle contract in pokefirered/include/battle.h, chunk_4079f42fde2b4370, where the battle system exposes battler identity categories and action constants. That same area is where you see actions like using a move, using an item, switching, running, Safari-specific actions, script execution, and finished or empty action states. Those are not trainer-table facts. Those are turn-engine facts.

Now look at why that distinction matters. A trainer on Route 3 may be trainer number whatever in the trainer table, and an object event on the current map, and later an opponent-side battler occupying the opponent-left position. Those are not synonyms. They happen to line up during one encounter, but they are owned by different systems. The map engine owns the object event. The script system owns the command stream that says what kind of trainer battle this is. The setup layer owns the temporary globals that bridge world state into battle. The battle engine owns battler positions, side bits, action buffers, battle mons, and turn state.

This is where battle.h becomes a blueprint rather than just a header. In pokefirered/include/battle.h, chunk_4e3097f639b05787, the trainer party structs show four compact representations: no item with default moves, item with default moves, no item with custom moves, and item with custom moves. Then in pokefirered/include/battle.h, chunk_403d30271e2270d6, the trainer table contract ties those possible party layouts to a trainer record, including party flags, trainer class, encounter music and gender packing, trainer sprite, name, item list, double battle flag, AI flags, party size, and a union pointer to the party data. That is a very C-like way to say, “Authored trainer data can vary in richness, but consumers must interpret it through the flags.”

Let me pause there, because this is the part I want you to internalize for the midterm. This is not a generic object-oriented hierarchy. FireRed is not making a base TrainerParty class with virtual methods. It is using flags plus differently shaped records plus interpretation code. The cost is that callers must be disciplined. The benefit is compact data, predictable layout, and direct control over what gets compiled into the ROM. That connects directly back to week 2, when we talked about generated data and compile-time assets. The same philosophy is showing up again, but now the data is battle-authoring data.

Now let us trace a concrete trainer battle. Imagine an NPC sees the player. The overworld system determines that this object event should approach. At that moment, we are still outside battle. The player is on a map. Movement and scripts are active. There is no opponent battler yet. There is a trainer script, though, and that script contains encoded parameters for the battle: the mode, the trainer opponent ID, the local object identity, pointers to intro and defeat text, and the place the script should continue afterward. The setup layer has to unpack that script data into the globals that later code expects.

In pokefirered/src/battle_setup.c, chunk_db24b720e895b599, the setup flow makes this explicit with the trainer battle parameter tables and the function BattleSetup_ConfigureTrainerBattle. It starts by resetting trainer battle variables, reads the battle mode, and then chooses which parameter specification should be used. An ordinary battle does not need quite the same argument layout as a continue-script battle. A double battle has an extra cannot-battle speech path. An early rival battle has rival flags. A rematch battle resolves the rematch trainer ID. This is a small parser. It is not parsing JSON. It is parsing a byte stream from the event script into C globals.

And this is exactly the same architecture pattern we have been tracing all semester: data table or script bytes, then globals, then callback or task handoff, then visible behavior. The new thing in week 12 is that the destination is the battle engine. Earlier, when we studied the summary screen, the visible behavior was a UI reading party data. When we studied the move relearner, the visible behavior was a menu state machine mutating a move slot. Here, the visible behavior is a full transition into battle, and the bridge has to carry more responsibility: opponent identity, event progression, battle type, music, transition effect, and the callback that will resume the world after battle.

Now, notice the function ConfigureAndSetUpOneTrainerBattle. This is the cleanest example of how the object-event identity and trainer identity are connected without becoming the same thing. It stores the selected object event, sets gSpecialVar_LastTalked from that object’s local ID, calls BattleSetup_ConfigureTrainerBattle on the trainer script data, installs the script EventScript_DoTrainerBattleFromApproach, and locks player field controls. In plain English: “This NPC is the one engaging you, parse its trainer battle script, set up the script that performs the approach battle, and stop the player from moving while the encounter takes over.” That is not battle logic yet. That is encounter setup logic.

Check for understanding here. If I ask, “Where is the opponent’s authored trainer ID held during setup?” the answer is gTrainerBattleOpponent_A. If I ask, “Where is the map NPC identity held?” you should think about gSelectedObjectEvent and gSpecialVar_LastTalked. If I ask, “Where is the opponent’s battle-side position?” that is not answered by either of those. That becomes a battler position once the battle engine initializes, using the battler-side and battler-position vocabulary declared through the battle interface contract in pokefirered/include/battle.h, chunk_4079f42fde2b4370. Good.

Now we follow the next handoff. StartTrainerBattle is where the encounter becomes a battle request. In pokefirered/src/battle_setup.c, chunk_a8e4b22a367933e7, the battle start handoff sets gBattleTypeFlags to BATTLE_TYPE_TRAINER. For a special early rival tutorial case, it also marks the first-battle flag. Then it sets gMain.savedCallback to CB2_EndTrainerBattle, calls DoTrainerBattle, and stops the script context. That is dense, so let us unpack it carefully.

gBattleTypeFlags is how the battle engine gets a coarse category of encounter. Trainer, wild, Safari, ghost, roamer, legendary, scripted wild, and so on are not separate battle engines. They are mostly different configurations of the same engine. The flags tell initialization and later battle code which rules and presentation branches apply. gMain.savedCallback is the return address at the callback level. It tells the runtime shell, “When the battle callback is done, this is the callback that should receive control.” And ScriptContext_Stop matters because the script VM cannot keep driving the overworld while the battle engine owns the main callback. This is one of those places where week 1’s callback shell and week 9’s scripting work snap together.

DoTrainerBattle then creates the actual start task. The shared task is Task_BattleStart. It waits until poison field effects are not active, disables the help system, starts the field battle transition, waits until that transition is done, reenables 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 flow is visible in pokefirered/src/battle_setup.c, chunk_a8e4b22a367933e7, and it is one of the best examples in the whole codebase of “tasks are for staged, frame-by-frame handoff; callbacks are for owning the major mode.”

So if you are designing your workshop battle setup record this week, do not just put “callback: battle” in it and call it done. That is too vague. You need to specify what kind of battle it is, what opponent data source it uses, which party slots or enemy-party records it must prepare, which trainer or script IDs it stores, which overworld object and script variables it touches, which callback returns after battle, and whether a transition task is needed. FireRed’s code is giving you the design requirements by example. The record is not “start battle.” The record is “translate a world encounter into the exact globals, flags, callbacks, and data pointers expected by the battle runtime.”

Now, I want to connect this back to trainer party structs, because this is where a lot of advanced students make a subtle mistake. You might assume that once gTrainerBattleOpponent_A is set, all trainer party data immediately becomes instantiated Pokémon in enemy party slots. But conceptually, the trainer ID is still just a key into authored data. The party table describes the trainer’s possible party records. The setup and battle initialization code interpret that table when they need concrete battle mons. We can see the interpretation pattern even in transition selection. The helper GetSumOfEnemyPartyLevel in pokefirered/src/battle_setup.c, chunk_db24b720e895b599, looks up gTrainers[opponentId].partySize, switches on partyFlags, selects the correct member of the trainer party union, and sums levels from whichever struct shape is active.

That is a great little architectural clue. Transition choice does not need to fully instantiate the enemy party. It only needs enough authored data to compare enemy level against the player’s current party level. So it reads the compact trainer record directly. If partyFlags are zero, it reads TrainerMonNoItemDefaultMoves. If the custom moveset flag is set, it reads TrainerMonNoItemCustomMoves. If the held item flag is set, it reads TrainerMonItemDefaultMoves. If both are set, it reads TrainerMonItemCustomMoves. One trainer table contract, four physical record shapes, one interpretation switch.

Let me give you a concrete design analogy, but grounded in our code. Suppose you are authoring a gym leader. For an early route trainer, you might only need species, level, and IV-like difficulty information, so default moves are enough and no held item is needed. For a gym leader, you might want custom moves and held items. You do not want every trainer party entry in the entire game to pay the storage cost of held items and four explicit moves if most trainers do not need that detail. The FireRed contract lets the common case remain small while still supporting richer authored encounters. That is exactly what those trainer party structs in pokefirered/include/battle.h, chunk_4e3097f639b05787, are doing.

Now, watch the other side of the architecture. The battle action constants in battle.h are not part of those trainer records. A trainer’s authored party may say, “This trainer has a level 14 Rattata with default moves.” It does not say, “On turn one, use move.” The turn engine later chooses or receives an action, and that action is represented with constants such as B_ACTION_USE_MOVE, B_ACTION_USE_ITEM, B_ACTION_SWITCH, or B_ACTION_RUN, as exposed in pokefirered/include/battle.h, chunk_4079f42fde2b4370. That separation is essential. Party records define possible participants. Battle actions define runtime choices made after participants exist.

Let me ask it as a check. If you add a new trainer class that uses a special encounter song, which area are you probably touching? Trainer table fields and music selection around setup. If you add a new per-turn command like “focus” or “guard,” where would that belong? In the battle action vocabulary and the battle turn interpreter, not in trainer party records. If you add a fifth kind of trainer party record with ability overrides, where does the burden appear? In the trainer struct contract, the party flags, and every interpreter that switches over those flags. That is the discipline cost of this architecture.

There is another detail here that matters for week 12. GetTrainerBattleTransition does not treat all trainer battles identically. It checks for secret base, Elite Four, Champion, double battle status, terrain transition type, enemy party level, and player party level. The point is not just visual polish. The transition effect is a function of authored trainer identity, map context, and party strength. That means the battle transition is a world-to-battle affordance. It belongs in setup, not deep in turn resolution. The battle engine should not have to rediscover whether Lance deserves a special transition from scratch. The setup layer already has the trainer ID and can make that decision before CB2_InitBattle takes over.

Now I am going to write a small trace on the board in prose form. Trainer script bytes enter BattleSetup_ConfigureTrainerBattle. That function loads mode, opponent ID, object local ID, text pointers, and return script according to the mode’s parameter table. The setup code records map-side identity through gSelectedObjectEvent and gSpecialVar_LastTalked. The script path eventually calls StartTrainerBattle. StartTrainerBattle sets trainer battle flags, stores CB2_EndTrainerBattle as the saved callback, calls DoTrainerBattle, and stops the script context. DoTrainerBattle creates Task_BattleStart using GetTrainerBattleTransition. Task_BattleStart plays the transition over several frames, cleans up field UI, and then calls SetMainCallback2(CB2_InitBattle). Only then does the battle runtime become the owner of the screen and input loop.

That trace is the answer to the course objective for today. We are not merely saying “battle_setup starts a battle.” We are tracing from data records through globals, callbacks, tasks, scripts, structs, and concrete source functions. The source packet’s high-level include list for pokefirered/src/battle_setup.c, chunk_494d132a59422c85, also reinforces this bridge role: that file pulls in task, overworld, item, sound, Pokémon, save loading, Safari Zone, quest log, script, event data, event object movement, field controls, field effects, battle.h, battle transition, battle controllers, and constants. That include set is not accidental. battle_setup.c sits at a crossroads because starting a battle is not a battle-only operation.

This is why week 12 comes after the party and move weeks. When we studied stat calculation inputs, move slots and PP, level-up move data, and move eligibility, we were looking at what a Pokémon can be. When we studied party screens and move relearner flows, we were looking at how interface state reads and mutates the player’s party. Now, in the battle engine module, we are watching those earlier systems become runtime combat participants. We do not need to re-teach move slots today, but we do need to remember that the battle engine is not inventing creatures from nothing. It receives prepared party data and authored opponent data through contracts like these.

So the synthesis for this segment is this: trainer battles are the cleanest trace because they force every identity layer to show itself. The trainer table gives us authored opponent identity. The map object gives us overworld identity. The setup globals hold the temporary bridge. The script system decides which setup path is being requested. The task system stages the transition over frames. SetMainCallback2 hands ownership to CB2_InitBattle. And battle.h gives the shared vocabulary so that once battle begins, participants can be discussed as battlers, sides, positions, and actions rather than as map NPCs. In the next portion, we will use that same trace to compare wild battles and scripted wild battles, because the handoff machinery is similar, but the data source and progression bookkeeping are different.

Good. So now put trainer battles slightly to the side and keep the handoff machinery on the board. The important comparison is not, “trainer battle complicated, wild battle simple.” That is too shallow. The better comparison is, “trainer battle gets opponent identity from an authored trainer table, while wild battle gets opponent identity from a prepared enemy party slot.” The later battle engine wants battlers either way. It does not want to know whether the opponent came from grass, a trainer script, a legendary overworld object, or an old man tutorial. That conversion is exactly why battle setup exists.

So, on the board, I’m drawing the trace like this: overworld condition, then enemy party preparation, then battle type flags, then saved callback, then transition task, then CB2_InitBattle. That is the spine. In pokefirered/src/battle_setup.c chunk_0e0ab33ad5402d71, the source makes this visible by sitting at the crossing point of many systems: task.h, overworld.h, pokemon.h, script.h, event_data.h, field control, battle.h, battle_transition.h, and constants for maps, songs, trainers, and items. That include list is not noise. It is architectural evidence. Battle setup is not “the battle engine.” It is the adapter that knows just enough about the field, scripts, map terrain, parties, music, flags, and callbacks to move control from one subsystem to another.

Now look at a normal wild battle. The function StartWildBattle does not immediately initialize the battle engine. It chooses which setup path applies. Are we in Safari Zone? Then DoSafariBattle. Are we in Pokémon Tower without the Silph Scope? Then a ghost battle path. Otherwise DoStandardWildBattle. That branch is already a lesson in world-state-driven battle setup. The same player action, stepping into an encounter, does not always mean the same battle contract. The map location, inventory state, and mode flags shape the battle before CB2_InitBattle ever sees it.

In the ordinary case, DoStandardWildBattle locks player field controls, freezes object events, stops the avatar, assigns gMain.savedCallback to CB2_EndWildBattle, clears gBattleTypeFlags to zero, and calls CreateBattleStartTask with GetWildBattleTransition. That source path is in pokefirered/src/battle_setup.c chunk_db24b720e895b599 and chunk_a8e4b22a367933e7. Notice the order. The overworld is immobilized first, because you are leaving field interaction. The return callback is assigned before the battle begins, because the battle must know where to send control afterward. The type flags are set before initialization, because the battle engine will interpret mode from those flags. And only then does the transition task begin.

Check your understanding here: if the battle engine crashes or behaves oddly because it thinks a wild battle is a trainer battle, which variable are you suspicious of first? Yes, gBattleTypeFlags. Not the species table. Not the move table. Not the map layout. Those might be involved later, but the first mode declaration is the battle type flag. This is why battle.h exposes gBattleTypeFlags as part of the shared battle contract; the header is saying, “many files need to agree on the current battle mode.”

Now compare that to a trainer battle. StartTrainerBattle sets gBattleTypeFlags to BATTLE_TYPE_TRAINER, maybe adds BATTLE_TYPE_FIRST_BATTLE for the early rival tutorial case, assigns gMain.savedCallback to CB2_EndTrainerBattle, calls DoTrainerBattle, and stops the script context. The trainer setup flow also keeps gTrainerBattleOpponent_A as the authored opponent ID. That trainer ID then indexes gTrainers, whose contract appears in pokefirered/include/battle.h chunk_403d30271e2270d6. The trainer party shapes themselves, the four TrainerMon variants with item and custom-move combinations, are in pokefirered/include/battle.h chunk_4e3097f639b05787. For wild battles, there is no gTrainers lookup. The opponent is already in gEnemyParty[0], and setup only declares what kind of battle this is and how to transition into it.

That distinction matters for your week 12 lab. When you design a battle setup record that can start wild, trainer, and scripted battles, do not make every battle record look like a trainer. A wild battle record needs species, level, generation conditions, maybe map encounter source, and a destination enemy party slot. A trainer battle record needs trainer ID, object local ID, intro and defeat text pointers, return script address, and flags for progression. A scripted wild battle is in between. It behaves like a wild battle inside combat, but like a script-controlled event outside combat.

Let’s make that concrete. Suppose the player steps in grass and the encounter generator has already created a level 5 Pidgey in gEnemyParty[0]. DoStandardWildBattle does not need to know the Pidgey’s learnset details. We covered stat calculation inputs, move slots, PP, and species-derived move data earlier, and those foundations still matter, but setup’s job is not to recalculate the entire creature model in this moment. Setup’s job is to preserve world state, mark the battle mode, and hand off. Then GetWildBattleTransition reads the enemy level from gEnemyParty[0] and compares it to the leading player party level. The source uses GetMonData on gEnemyParty[0] and GetSumOfPlayerPartyLevel, then selects from the wild transition table. That is a small but elegant example of presentation depending on gameplay data without the setup code becoming the battle engine itself.

Now scripted wild battles. StartScriptedWildBattle locks player controls, sets gMain.savedCallback to CB2_EndScriptedWildBattle, sets gBattleTypeFlags to BATTLE_TYPE_WILD_SCRIPTED, starts the wild transition, and increments battle statistics. Same handoff shape, different return semantics. The callback difference is not cosmetic. CB2_EndWildBattle returns to field with the ordinary field callback behavior; CB2_EndScriptedWildBattle returns through CB2_ReturnToFieldContinueScriptPlayMapMusic, because the script may need to continue after combat. That is the key architectural idea: a scripted wild battle is not only distinguished by what you fight; it is distinguished by who owns control after the fight.

Let me say that again because it is one of those details that separates a good reverse engineer from a file tourist. In FireRed, “scripted” often means the post-event continuation is part of the contract. If you only set BATTLE_TYPE_WILD_SCRIPTED but return through the wrong callback, the battle itself may appear correct, but the world after the battle will be wrong. Maybe the legendary object does not disappear. Maybe a result variable is not observed. Maybe the script never resumes. The bug is not in damage calculation. The bug is in ownership transfer.

For a stronger example, look at the Marowak path. StartMarowakBattle locks controls, uses CB2_EndMarowakBattle, checks the Silph Scope, and either creates the unveiled Marowak or marks the ghost battle state. It then starts the transition and assigns the ghost nickname. The special end callback uses gBattleOutcome to set gSpecialVar_Result differently depending on whether the player won. That is not ordinary wild battle cleanup. That is story progression encoded as battle return behavior. So if we trace it, the source of truth runs from inventory state, to enemy party construction and battle flags, to the battle engine, to outcome, to script-visible result variable. That is exactly our course thesis: data table or world state, into globals, through callbacks and tasks, back into player-visible behavior.

Now, quick board cue: I’m drawing two columns. Left column says “ordinary wild.” Right column says “scripted wild.” Under ordinary wild: gEnemyParty[0], gBattleTypeFlags equals zero, saved callback CB2_EndWildBattle, return to field. Under scripted wild: gEnemyParty[0], BATTLE_TYPE_WILD_SCRIPTED or legendary and special flags, saved callback CB2_EndScriptedWildBattle or a specialized callback, return to field and continue script. The battle engine can still talk in battlers, sides, actions, and targets because battle.h supplies that vocabulary. In pokefirered/include/battle.h chunk_4079f42fde2b4370, the battler identity macros and B_ACTION constants define that internal language: use move, use item, switch, run, Safari actions, execute script, finish, none. Once CB2_InitBattle owns the frame loop, the opponent is no longer “the grass encounter” or “the Snorlax event” in the immediate combat loop. It is a battler with a side, position, controller, action, and battle mon record.

This is also where your earlier party-slot work comes forward. In week 11 we cared about party slot ownership because menus, summary screens, and move replacement workflows must not confuse “which creature is being inspected” with “which screen currently displays it.” Here the same discipline appears in battle form. gPlayerParty and gEnemyParty are storage. Battler IDs are runtime combat participants. Trainer IDs are authored opponent records. Object local IDs are overworld actors. Script pointers are control-flow positions. A sloppy architecture merges those identities. FireRed keeps them imperfectly, but usefully, separated.

And because this is an advanced course, I want you to notice the limits of that separation too. These are C globals, not a modern dependency-injected battle service. The handoff is disciplined, but it is not pure. gBattleTypeFlags, gMain.savedCallback, gTrainerBattleOpponent_A, gSpecialVar_Result, gSelectedObjectEvent, and gEnemyParty are shared state. The architecture works because the setup sequence is carefully staged. If you reorder it casually, you can create a bug that looks unrelated to the line you changed. For example, if you start the transition task before setting the saved callback, then once Task_BattleStart finishes and SetMainCallback2 points to CB2_InitBattle, the battle may eventually end without the correct route home. The failure appears minutes later, after an entirely different subsystem has run.

Let’s inspect the transition task conceptually. Task_BattleStart has a small state machine. In state zero, it waits until the poison field effect is not active, disables the help system, starts the field battle transition, and advances state. In state one, it waits for IsBattleTransitionDone, then 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. This is in pokefirered/src/battle_setup.c chunk_a8e4b22a367933e7. That is the runtime shell from week 1 returning in a battle-specific form. We are still in callback and task architecture. The battle does not begin because a blocking function says, “play transition, then battle.” It begins because a task advances over frames until it is safe to replace the main callback.

Here is the check for understanding. Why is Task_BattleStart a task rather than just a function that immediately calls CB2_InitBattle? Take a second. The answer is that the transition is visible, animated, and frame-based. The field still needs to update while the transition effect progresses. The game has to wait until the transition reports done. A direct immediate call would skip or corrupt the staged visual handoff. This is the same RunTasks idea we studied earlier, now applied to one of the most important mode changes in the game.

Now let’s tie in battle.h more tightly. The header is not only declaring trainer structs. It is also declaring the constants that let all these pieces speak the same mode language. The setup file assigns BATTLE_TYPE_SAFARI, BATTLE_TYPE_GHOST, BATTLE_TYPE_ROAMER, BATTLE_TYPE_LEGENDARY, BATTLE_TYPE_TRAINER, and so on. The battle engine later interprets those flags when deciding available actions, escape behavior, capture behavior, controller behavior, music assumptions, scripts, and end conditions. Meanwhile the action constants in battle.h tell the turn system what a battler selected. In a Safari battle, the action vocabulary includes watch carefully, Safari Ball, bait, go near, and run. That is why DoSafariBattle is not just a standard wild battle with a different map. It sets BATTLE_TYPE_SAFARI, uses a Safari-specific end callback, and thereby changes the action surface the player sees.

Slide cue: imagine three screenshots, all starting from the overworld. Grass encounter. Safari encounter. Legendary encounter. They all fade into a battle scene. To the player, that feels like one family of interactions. To the architecture, each one is a different setup contract with shared handoff machinery. This is the design pattern I want you to extract for your own engine: use one battle initialization pipeline, but do not flatten all encounter sources into one data shape too early. Preserve the authored differences until you have translated them into explicit battle flags, party records, callbacks, and post-battle outcomes.

The old man tutorial battle is a nice edge case. StartOldManTutorialBattle creates a male Weedle in gEnemyParty[0], locks player controls, sets gMain.savedCallback to CB2_ReturnToFieldContinueScriptPlayMapMusic, sets gBattleTypeFlags to BATTLE_TYPE_OLD_MAN_TUTORIAL, and starts a fixed slice transition. That is not a normal wild encounter, not a trainer battle, and not really a player-owned battle in the usual sense. But the setup contract still makes it enter the same battle system. This is why a mature creature RPG architecture benefits from explicit mode flags instead of trying to infer everything from species and party data. A Weedle in gEnemyParty[0] does not tell you whether the player is in a tutorial. BATTLE_TYPE_OLD_MAN_TUTORIAL does.

So, to connect this to the overall course plan, week 12 is where we stop treating battle as a sealed box. Weeks 1 through 11 gave us the pieces: main callbacks, tasks, maps, scripts, generated data, creature records, party slots, move data, and menu state machines. Now we are watching those pieces converge. battle.h gives the interface contract. battle_setup.c gives the crossing logic. Later in this module, when we go deeper into the battle engine itself, we will already know where its inputs came from and why its globals are shaped the way they are. That prevents a common mistake: opening battle_main.c and trying to understand everything from the first turn command outward. You need the setup trace first.

One last recap before we move to the next layer. Trainer battles are authored-opponent battles: trainer ID, gTrainers contract, trainer party structs, map object identity, script text, trainer flags, and trainer-specific return behavior. Ordinary wild battles are prepared-enemy-party battles: gEnemyParty[0], battle type flags usually zero, standard wild return callback, transition chosen from map type and level comparison. Scripted wild battles are prepared-enemy-party battles with script ownership: special flags, special callbacks, and often gSpecialVar_Result or continued script execution after combat. All of them pass through CreateBattleStartTask, Task_BattleStart, SetMainCallback2, and CB2_InitBattle. And once they cross that boundary, battle.h’s battler vocabulary takes over.

In the next segment, we will stay inside that boundary line for a little longer and ask what must already be true when CB2_InitBattle begins. That means we will look at the preconditions setup guarantees: which parties exist, which globals are meaningful, which callback owns return, and which identifiers have intentionally stopped mattering.

All right, so let’s pick up exactly at that line. We have crossed the boundary from “the overworld is deciding that a battle should happen” into “the battle engine is about to initialize itself.” And I want to slow down here because this is one of those places where an advanced reader can make a very expensive mistake. You can look at a function name like CB2_InitBattle and assume, well, that must be where the battle is chosen. It is not, at least not in the architectural sense we care about. By the time CB2_InitBattle is in control, the interesting act of selection has largely already happened.

Let me say that again in runtime language. CB2_InitBattle is not supposed to wander around the map runtime asking, “Did the player step in grass? Was there a trainer object? Is this a static legendary? Which script started me?” Those are upstream questions. Those belong to the overworld, script system, and battle setup layer. In the packet for this week, pokefirered/src/battle_setup.c chunk_494d132a59422c85 is already a clue because the include list is almost a map of the boundary: task.h, overworld.h, script.h, event_data.h, event_object_movement.h, field_player_avatar.h, battle.h, battle_transition.h. That file has one foot in field logic and one foot in battle logic. CB2_InitBattle should receive a world that has already been arranged.

Now, if I were at the board, I would draw a vertical line. On the left I would write “world identity,” and on the right I would write “battle identity.” World identity means things like the map object that saw the player, the script pointer that is waiting to resume, the encounter method, the tile behavior, the trainer event flag, the map type, and the player avatar state. Battle identity means battler positions, parties, battle type flags, selected opponent data, action constants, and callbacks. Week 12 is about that conversion line. battle.h is important because it tells us what vocabulary the right side uses.

So what must be true when CB2_InitBattle begins? First, the player party must be meaningful as party data, not as a menu selection, not as a summary-screen view, not as an abstract owned collection. We already spent time in the party and move lectures talking about party slot ownership, move slots, PP, stat calculation inputs, and summary screen reads, so I am not going to reteach those. I only want to pull them forward. The battle engine assumes the player party records are real Pokemon records with enough data to produce active combatants. The engine can calculate, display, animate, and mutate battle state, but it should not have to ask the overworld, “Which creatures does the player own?” That is already the party model’s responsibility.

Second, the opponent side must have a valid source. For an ordinary wild battle, that usually means gEnemyParty has been prepared, especially gEnemyParty[0] for the single wild opponent. For a trainer battle, the opponent source is not just “whatever happens to be in gEnemyParty right now.” It is the trainer contract: a trainer ID, a row in the trainer table, and a party structure whose shape matches the trainer’s declared party format. This is where pokefirered/include/battle.h chunk_4e3097f639b05787 matters, because the trainer party structs are not decorative. They define how trainer party data can be interpreted. And pokefirered/include/battle.h chunk_403d30271e2270d6 matters because the trainer table contract tells battle setup and battle initialization what a trainer record is allowed to mean.

Here is a concrete example. Suppose the player steps in grass and encounters a level 5 wild Pidgey. At setup time, the system needs to create or populate the opponent Pokemon data. Once battle starts, the opponent is just a battler backed by a party record. The battle engine should not care which grass tile generated it. Now suppose instead the player is seen by a Youngster on a route. At setup time, the system needs to identify the trainer, mark or later mark the right trainer event state, select that trainer’s party data, choose the trainer battle text behavior, and establish the return path. Once battle starts, the opposing side is again a battler backed by party data, but the source of that data is different. That distinction is crucial before the boundary, and it becomes less important after the boundary.

Check yourself on this: if two battles both show the opponent in the right-side battler position, does that mean they were set up the same way? No. Same battle-side representation does not imply same setup path. A wild battle and a trainer battle can converge into similar battler positions while having completely different upstream ownership.

Third, battle type flags must already say what kind of battle the engine is entering. These flags are a classic C-era subsystem contract. Rather than pass around a fat object called BattleSetupRecord, FireRed uses globals and flags. That is not how we would necessarily design a new engine today, but it is coherent for this runtime. When we read pokefirered/src/battle_setup.c chunk_0e0ab33ad5402d71 and chunk_db24b720e895b599 as setup flow, we should see them as code that prepares the global conditions under which battle initialization will run. And when pokefirered/src/battle_setup.c chunk_a8e4b22a367933e7 hands off to the start path, that handoff is not merely visual transition. It is a transfer of authority.

Let’s pause on that phrase, transfer of authority. In the overworld, the dominant questions are spatial and script-driven. Where is the player? What object is in front? What map script is active? What flag is set? What tile behavior applies? In the battle engine, the dominant questions become turn-driven and battler-driven. Which battler acts? Which action was selected? Which move slot is being used? Which side owns this target? Which battle script command is running? That is why pokefirered/include/battle.h chunk_4079f42fde2b4370, the chunk with battler identity and action constants, is so central. It gives names to the world after the conversion. Not map object number. Not trainer event object local ID. Battler position. Battler action. Side. Controller. That is the new grammar.

Now, I want to be careful here. The old identifiers do not all vanish physically. Some may remain in globals or be used after the battle returns. But architecturally, many of them stop being authoritative inside the battle. The trainer’s map object identity mattered for detection, approach movement, facing, and script triggering. Once the battle is running, the combat engine does not need to ask that object how to resolve damage. It needs the trainer data and party data. The script pointer or callback may matter when the battle ends, but it should not be consulted to decide whether Tackle hits.

This is the distinction between retained return context and active simulation context. Say that with me mentally: retained return context is not active simulation context. The callback that gets you back to the field is retained context. The battle type flags and battler positions are active simulation context. A trainer ID can straddle the line, because it may be needed to load trainer party and text, but the local object event that spotted the player is mostly a setup concern.

Let me put a small board diagram into words. Across the top, I write “Before CB2_InitBattle.” Under it: gBattleTypeFlags set, opponent source selected, parties valid, transition selected or in progress, return callback known, script continuation known if relevant. Then under “During battle init,” I write: battlers assigned, controllers installed, battle resources allocated, battle scripts selected, UI and animation state initialized. Then under “After battle,” I write: result interpreted, script or overworld callback resumes, trainer flags or special variables updated. The important thing is that the middle column should not rebuild the left column from scratch.

This is why the battle setup source pulls together such a broad list of dependencies. Again, look at pokefirered/src/battle_setup.c chunk_494d132a59422c85. The includes are not just clutter; they reveal that setup has to speak to tasks, help system, overworld, items, sound, Pokemon data, save loading, Safari Zone, quest log, scripts, event data, movement, metatile behavior, field control, screen effects, message boxes, VS Seeker, battle, battle transition, and trainer constants. That is a boundary module. Boundary modules often look messy because their job is translation. The correct question is not “why does this file know so many things?” The correct question is “does each dependency belong to the translation from one subsystem’s vocabulary to another?” In this case, yes, that is the architecture we are studying.

Now let’s make this more concrete with a scripted wild battle, because that is where students often blur the lines. Imagine a script starts a special encounter. It may set a species and level, prepare gEnemyParty, set a special battle type flag, and arrange a callback or script continuation after the fight. The opponent is still “wild” in the sense that it is not drawn from gTrainers. But it is not ordinary grass logic either. The script owns part of the meaning. It may care whether the player won, ran, caught the Pokemon, or failed. That result may be stored in a special variable or used to choose the next script branch. So before CB2_InitBattle, the setup must preserve script ownership of the return behavior. During battle, however, that scripted origin should be translated into flags and party records the battle engine understands.

Here is the check for understanding. If a scripted wild encounter and a random wild encounter both place one Pokemon in gEnemyParty[0], what extra thing does the scripted version need? Not a different species record structure. Not a new stat formula. It needs ownership of the after-battle meaning. It needs the callback or script continuation and the right flags so that the result can be interpreted by the code that asked for the battle in the first place.

This is the same principle we used earlier in the course when we traced data table to visible behavior. A map header does not itself draw the world; it points into layouts and scripts that the overworld runtime interprets. A species table does not itself animate a battle; it provides base data that party records and battle calculations consume. A move learnset table does not itself open the move relearner UI; it becomes meaningful when a menu state machine asks questions about eligibility and selection. Now, in week 12, battle.h does not itself start battles. It defines the contract that makes battle start code and battle runtime code agree.

Let’s talk about battler positions for a moment, because this is one of the cleanest examples of contract vocabulary. In the overworld, “player” usually means the avatar object on a map. In battle.h’s world, player-side identity is split into battler positions. You can have a player-left and player-right position, and opponent-side positions as well. That matters for double battles, link battles, Safari-like variants, and controller logic. The action constants in pokefirered/include/battle.h chunk_4079f42fde2b4370 do the same thing for behavior. They give the battle engine a finite language for “what is this battler doing this turn?” Fight, use item, switch, run, and so on, depending on the exact constant set. The key idea is not the individual numeric value. The key idea is that action selection becomes data that the engine can route through a turn processor.

If you are designing your workshop battle setup record this week, this is where your record has to be honest. It cannot just say, “start battle: true.” That is not enough. It needs to say what kind of battle, where the enemy party comes from, which trainer ID or species and level are relevant, what callback or script resumes afterward, what overworld variables are being consumed, and which globals must be initialized before the callback swap. The lab prompt asks for wild, trainer, and scripted battles because those three forms force you to separate enemy-party preparation from authored-opponent lookup and from script-owned return behavior.

Let me give you a deliberately flawed design. Suppose someone proposes a struct with fields: species, level, trainerId, callback. And they say, “If trainerId is nonzero, it is a trainer battle; otherwise use species and level.” That works for two toy cases, but it is already suspicious. What about trainer ID zero if zero is a valid or sentinel value? What about special wild battles whose return behavior is not the same as random wild battles? What about double battles? What about battle type flags that affect rules but do not map one-to-one to trainer versus wild? What about a trainer party whose data format includes custom moves or held items? The real FireRed architecture, through battle.h’s trainer party structs in chunk_4e3097f639b05787 and trainer table contract in chunk_403d30271e2270d6, tells us that opponent setup is more structured than “one ID or one species.”

Now, do not overcorrect either. You do not need an object-oriented inheritance hierarchy with WildBattleSetup, TrainerBattleSetup, ScriptedLegendaryBattleSetup, RematchBattleSetup, SafariBattleSetup, and so forth just to understand FireRed. This engine uses flags, globals, arrays, callbacks, and tables. Advanced understanding means you can see the shape of the implicit record even when the C code does not package it as one neat struct. The implicit setup record is distributed across globals and call paths.

That phrase is worth keeping: distributed setup record. When CreateBattleStartTask is called, and Task_BattleStart runs, and SetMainCallback2 eventually points the main loop at CB2_InitBattle, those functions are not carrying a modern heap-allocated context object through every layer. The context is already in the program state. We know from earlier weeks that SetMainCallback2 changes the main callback slot; this is the runtime shell’s way of saying, “the next frame belongs to a different subsystem.” We also know from the task lectures that tasks provide per-frame dispatch while a mode is still active. So battle setup uses a task to manage the start sequence and the transition, then hands off to a new callback mode. That connects week 1’s runtime shell directly to week 12’s battle entry.

Let’s run the sequence slowly in spoken form. The player’s field control detects or is told that a battle should start. Battle setup code determines the source: wild encounter, trainer encounter, scripted encounter, or another specialized case. It writes battle-relevant globals: flags, opponent identity, enemy party data or trainer data, and return behavior. It starts a battle start task. The task advances the transition and waits for the correct moment. Then the system uses SetMainCallback2 to install CB2_InitBattle. At that moment, the overworld is no longer the primary per-frame owner. The battle engine begins consuming the prepared state.

If I ask, “Where is the battle?” the answer depends on time. Before handoff, the battle is a promise encoded in setup globals and a task. After handoff, the battle is the active callback mode. That temporal distinction is not academic. It prevents bugs. If you mutate gEnemyParty after the handoff because a script continues running too long, you have violated the boundary. If you start a battle without setting the correct return callback, the battle may complete correctly and still strand the player in the wrong runtime state. If you set a trainer ID but fail to match the expected trainer party struct format, the engine may interpret valid bytes as the wrong kind of party data.

And that last point is where battle.h becomes a blueprint rather than a bucket of definitions. Header files in this codebase are cross-module contracts. We said that in week 2, and it becomes sharper here. pokefirered/include/battle.h is not merely “things battle.c needs.” It is the vocabulary shared by setup, battle runtime, trainer data, party interpretation, and other modules that need to speak battle. The chunks we are focusing on this week, chunk_4079f42fde2b4370 for battler identity and action constants, chunk_4e3097f639b05787 for trainer party structs, and chunk_403d30271e2270d6 for the trainer table contract, are three sides of the same blueprint. Who can act? What can they do? Where does the opponent data come from?

Let’s do another quick check. A trainer sees the player. The trainer’s object event starts movement and text. The battle starts. Inside the battle, which identifier should determine the opponent’s roster: the map object local ID or the trainer table ID? The trainer table ID. The map object local ID may be part of how we got here, and it may matter for map scripting, but the roster contract belongs to trainer data. If you answered “object ID,” you are still thinking in overworld terms after the architecture has crossed into battle terms.

Now, there is one nuance. Some setup paths have to remember enough overworld context to clean up after battle. A trainer may need to face the player, stop movement, display defeat text, set an event flag, or resume a script. A legendary encounter may need to disappear after capture or defeat. A Safari encounter may need different result handling. So I am not saying the battle engine exists in a vacuum. I am saying FireRed separates “simulate the battle” from “interpret why this battle happened.” The bridge is built from globals, callbacks, scripts, and flags.

This is also why the battle setup file’s relationship with script and event data is so important. The source excerpt in pokefirered/src/battle_setup.c chunk_494d132a59422c85 includes script.h, script_pokemon_util.h, event_data.h, and event_scripts.h alongside battle.h and battle_transition.h. That is the local evidence that battle setup is not just combat logic. It is event orchestration. It can read or write event variables, coordinate with script utilities, and then cross into battle initialization. If battle.h is the blueprint of the battle subsystem, battle_setup.c is the customs checkpoint where overworld passports are translated into battle credentials.

Let’s briefly connect this to Pokemon Showdown, but only as a comparison, not as our authority. In the research packet, pokemon-showdown/data/abilities.ts chunk_d5bdb17c16d6f1a4 shows ability hooks such as onModifyMove and onDamagingHit living in data objects that are interpreted by a simulator. That is a very different style from FireRed’s C globals and callback handoffs. But the architectural rhyme is useful: both systems require the battle runtime to receive data in a vocabulary it can execute. Showdown tends to make hooks explicit in JavaScript or TypeScript data. FireRed tends to make setup and runtime agreement explicit through headers, constants, structs, arrays, and global state. Do not confuse style with substance. The substance is contract-driven simulation.

Now, returning to CB2_InitBattle: what identifiers have intentionally stopped mattering? The grass tile’s exact metatile behavior has already done its job. The random encounter method has already done its job. The trainer object’s sightline has already done its job. The script command that requested a special encounter has already done its setup job, though the script may retain ownership of the result. What matters now is whether the battle globals are coherent.

Coherent means the battle type flags match the prepared data. It means trainer battles have trainer identifiers that can be resolved through the trainer table contract. It means wild battles have enemy party data ready. It means the player party is valid and contains usable records according to the rules that setup expects. It means the return callback or script continuation is not an afterthought. It means battler positions can be assigned using battle.h’s vocabulary without reaching back into field movement code.

I want you to notice that this is a stronger claim than “the code works.” We are describing an invariant. The invariant is: after battle setup handoff, battle initialization consumes a prepared battle description encoded in FireRed’s native global-and-constant style. If that invariant holds, the engine can remain modular despite not having modern module boundaries. If that invariant breaks, the symptoms may appear far away from the bug. A missing flag in setup can look like a broken battle UI. A wrong return callback can look like an overworld bug. A mismatched trainer party struct can look like corrupted Pokemon data.

That is why advanced debugging in this engine is trace debugging, not local guessing. You trace from data records to globals to callbacks to tasks to scripts to structs to concrete source functions. That is the course objective, and week 12 is where the whole method pays off. When you see CreateBattleStartTask, Task_BattleStart, SetMainCallback2, and CB2_InitBattle in the chain, do not treat them as isolated function trivia. Treat them as a pipeline of authority transfer.

Let’s finish this segment with a compact recap before we move deeper. Before CB2_InitBattle begins, the world must already have been reduced to battle terms. The player party must exist as party records. The opponent must be available either through prepared enemy party data or through a trainer table and trainer party struct contract. The battle type flags must describe the intended rule space. The callback or script return path must be known. The old overworld identifiers may remain as context, but they are no longer the primary language of combat. And battle.h, especially pokefirered/include/battle.h chunk_4079f42fde2b4370, chunk_4e3097f639b05787, and chunk_403d30271e2270d6, is the document that lets these different modules agree on what a battle-shaped world looks like.

In the next pass, we will take that invariant and press on it from the other side. Instead of asking, “What must setup guarantee before CB2_InitBattle?” we will ask, “What does the battle engine do first because it trusts those guarantees?” That is where battler controllers, action selection, party indexing, and the visible battle interface start to line up.

All right, so now we are standing on the battle engine’s side of the doorway. Setup has done its job, at least in theory. The world has been converted into battle language. The question is no longer, “Where was the player standing?” or “Which object event initiated contact?” The question becomes, “How many battlers exist, what sides do they occupy, what parties back them, what action vocabulary is legal, and what callback returns control afterward?” That is the mental turn I want you to make here.

If I were drawing this on the board, I would put a vertical line down the middle. On the left side I would write “overworld identity,” and underneath it: map, object event, trainer id, script pointer, player coordinates, encounter tile, fishing state, Safari state, quest log context. On the right side I would write “battle identity,” and underneath it: battler id, battler position, side, party index, action, battle type flags, controller, callback. The vertical line is the handoff. And the mistake many engine readers make is that they keep trying to interpret battle behavior using left-side terms after the program has already crossed to the right side.

So, for example, in the overworld a trainer is a map object. It has a local id. It has a facing direction. It has event object movement state. It can notice the player, walk forward, display an exclamation mark, and run a script. But inside the battle engine, that trainer is not primarily a map object anymore. The trainer has become a side with a party source, a trainer class, a trainer name, a party layout, an AI context, and a controller relationship. That transformation is why pokefirered/include/battle.h chunk_403d30271e2270d6 matters so much. The trainer table contract says, in effect, “If the setup code gives you this trainer id, here is the shape of the trainer record that the rest of the system is allowed to rely on.” And pokefirered/include/battle.h chunk_4e3097f639b05787 is adjacent to that because the trainer party structs define the legal shapes of the opponent’s party data.

Now, let’s slow that down with a concrete case. Imagine a standard single trainer battle against a route trainer. The overworld script or field control logic identifies the trainer. battle_setup.c, particularly the setup flow represented in pokefirered/src/battle_setup.c chunk_db24b720e895b599 and the battle start handoff in chunk_a8e4b22a367933e7, is part of the machinery that moves us from that field encounter into the battle callback world. Once the battle is starting, the battle engine should not have to ask, “Was this trainer standing on Route 3, or in a gym, or on a bridge?” Not for the basic turn loop. It needs to ask, “Which side is this trainer on? Which party data do I load? What action can each battler choose? What message buffers and animations are associated with the current battler state?”

This is where the battler constants in pokefirered/include/battle.h chunk_4079f42fde2b4370 are not decorative. They are not just labels for convenience. They are the compression scheme for the whole battle interface. A battler is not just “a Pokemon.” A battler is an active combat participant occupying a position in the battle layout. In a simple wild battle, you can think of the player’s active Pokemon as one battler and the wild opponent as another. But the abstraction has to scale beyond that. It has to tolerate doubles, link battles, Safari-like rule variants, scripted battles, and special cases where the normal player-versus-trainer mental model is insufficient.

So if I write on the board, “battler id is not party slot,” that is a key sentence. Say it back to yourself. Battler id is not party slot. A party slot is a record position in a party array. We talked about party slot ownership in the previous week when we discussed the party menu and summary screen, and we are not going to re-teach all of that now. The important bridge is this: party data persists across systems, but battler identity is runtime battle participation. Party slot zero may become the player’s active battler at battle start, but that relationship is mediated. It can change when the player switches. It can be absent if the party member has fainted. It can be represented through a battle party index or active battler mapping rather than by treating the party array itself as the battlefield.

Check your understanding here. If a player switches from the first party Pokemon to the third party Pokemon, did the player’s battler id necessarily become three? No. The player-side active battler position may remain the same battler id, while the party index backing that battler changes. That is the architectural reason we separate “who is on the battlefield right now” from “where is this creature stored in the player’s party data.” If you collapse those two, your engine will work for the first demo battle and then start cracking when you add switching, doubles, forced replacements, link play, or faint handling.

Now, why is this in battle.h rather than buried only in battle_main.c or some private module? Because many modules must speak the same battle vocabulary before the engine proper can run. battle_setup.c includes battle.h, and the source research packet shows battle_setup.c bringing together overworld, script, event data, field control, battle transition, and battle headers in pokefirered/src/battle_setup.c chunk_494d132a59422c85. That include list is evidence of a boundary module. It is not merely a pile of dependencies. It tells us that setup is the translator. It must understand field systems well enough to leave them, and battle systems well enough to enter them.

Let me put another board cue here. I am going to write, “headers are promises made before runtime.” In an engine like this, battle.h is not simply a convenience file for C compilation. It is an architectural promise. It says, “These constants and structs are shared enough that multiple subsystems must agree on them exactly.” The constants for battler positions and actions allow code that is not physically inside the same source file to coordinate. The trainer party structures allow generated or static trainer data to be consumed by battle setup and battle logic without ad hoc decoding. The trainer table contract allows a trainer id from the overworld or script layer to become a coherent battle opponent.

Now, remember the earlier course pattern: data record, global state, callback or task, concrete source function, visible behavior. We have used that trace for maps, scripts, UI state machines, and party screens. In battle setup, the data record might be a trainer table entry or a wild species and level selection. The global state includes battle type flags, selected trainer identifiers, enemy party buffers, callback slots, and battle-related globals. The callback transition is the move away from the overworld callback into the battle initialization callback. The concrete functions live in the setup and battle initialization path. The visible behavior is the battle transition effect, the appearance of the battle scene, the enemy send-out, the player send-out, and eventually the command menu.

The advanced move is to see that each part of that trace changes vocabulary at the boundary. A map object becomes trainer data. A party slot becomes active battler backing data. A script event becomes a return path. A tile encounter becomes a battle type. A player input mode becomes a battler action selection mode. That is why the title of today’s lecture is not “battle.h as a list of constants.” It is “battle.h as the Battle System Blueprint.” The header is telling you what categories the engine recognizes.

Let’s take action constants next, because they are easy to underestimate. In a turn-based creature RPG, player-visible action choices are things like fight, bag, Pokemon, run. Internally, those choices cannot remain as menu labels. They need to become action codes that the turn engine and controllers can dispatch. pokefirered/include/battle.h chunk_4079f42fde2b4370, as our source focus notes, covers battler identity and action constants. That means the same header that helps the engine say “which battler is this?” also helps say “what can this battler decide to do?” Identity and action are paired because a turn is not just an action; it is an action assigned to an actor in a particular battle position.

Suppose the player chooses “Fight” and then selects a move. We already covered move slots and PP in week 11, so I only want the bridge. The battle engine does not rediscover from scratch what a move slot is. The party data already has move ids and PP. But the battle action system has to represent the player’s decision in a form the turn engine can execute. That means “battler A selected action type fight, with move selection N, targeting battler B,” or something conceptually equivalent. The exact implementation details spread across battle source modules, but battle.h gives us the shared nouns. Without shared nouns, the controller, the command menu, the AI, the turn resolver, and the animation scripts would all invent slightly different meanings.

And this matters for AI too. A trainer’s active Pokemon choosing a move is not the same pathway as the player pressing A on a menu, but the result must enter the same battle action universe. This is a recurring architecture principle: different input sources, same internal command representation. Human input, AI decision, scripted forced action, and link partner selection must eventually be normalized. If they are not normalized, every downstream function has to ask, “Was this decision human? Was this decision AI? Was this decision scripted?” And once that branching spreads, the engine becomes much harder to reason about.

Let me ask a check question. In a clean battle architecture, should the damage calculation code care whether a move was selected by a human menu or by trainer AI? Generally, no. It should care about the move, the attacker, the defender, modifiers, battle state, and rule flags. It might care about effects that differ by battle mode, but it should not care about the input path as such. That is exactly why setup and action selection are boundary-normalizing layers.

Now look again at trainer party structs. The course packet points us to pokefirered/include/battle.h chunk_4e3097f639b05787 for those structs. Why multiple trainer party shapes? Because not all trainers need the same record richness. Some may specify only species and level. Others may specify moves, held items, or other richer configuration. From a data design point of view, this is a space-versus-expressiveness tradeoff. On the Game Boy Advance, that tradeoff is not theoretical. If every early-route trainer stored fully specified movesets, items, and custom attributes, the data footprint would grow for little gameplay gain. But if important trainers need bespoke movesets, the system must support richer records.

So the trainer table contract and the party struct contract cooperate. The trainer table can say, in some form, “this trainer’s party uses this kind of party data,” and the setup or battle loading code can interpret the party pointer accordingly. The battle engine can then materialize actual Pokemon data for the enemy side. That is the data-driven pattern we have traced all semester: compact source data, explicit struct contract, runtime expansion, then visible behavior. The trainer you see on screen is not hand-coded as a one-off battle function. The trainer is a data record interpreted through a shared contract.

Concrete example: a Youngster with two low-level Pokemon can be represented compactly. The trainer id selects the trainer table entry. The table entry points to party data shaped according to one of the trainer party structs. Setup uses that to populate the enemy party. Then battle initialization assigns active enemy battler state from that party. On the player side, the party already exists from save and overworld continuity. Once both sides are represented in battle terms, the intro can display the trainer’s class and name, send out the first opponent, and allow the command loop to begin. That entire flow depends on the contracts lining up. If the trainer table says the party data has one shape and the loader reads it as another, you do not get a subtle design disagreement. You get wrong species, wrong levels, bogus moves, or memory interpretation errors.

This is also where we connect to the upcoming lab. When you design a battle setup record that can start wild, trainer, and scripted battles, I will expect you to specify not only “enemy equals species X” or “trainer equals id Y.” I will expect you to say which overworld variables are consumed, which party slots are read or written, which trainer ids are stored, which battle flags are set, which callback resumes afterward, and what data must exist before the handoff. At this point in the course, a vague answer like “start the battle scene” is no longer enough. The midterm is cumulative for a reason. You now have enough architectural vocabulary to name the actual seams of responsibility.

Now, let’s bring callbacks into this more explicitly. Earlier in the course, we spent time on AgbMain, SetMainCallback2, task allocation, and RunTasks. I am not going to re-teach those mechanics, but I want you to use them. FireRed’s runtime shell gives the program a way to swap major modes. The overworld is a mode. Battle is a mode. Menus are modes. Transitions may be modes or task-driven bridges. When setup reaches the battle start handoff, represented in our focus by pokefirered/src/battle_setup.c chunk_a8e4b22a367933e7, the important architecture question is, “Which callback owns the frame now?” Once battle owns the frame, battle.h’s vocabulary becomes operational.

That is why callback handoff is more than a function call. A normal function call returns immediately to its caller. A callback mode change says, “For future frames, run this other world.” This is the same principle you saw with the move relearner menu state machine in pokefirered/src/learn_move.c chunk_af3598df6f092723, but here the stakes are larger. A menu state machine may temporarily occupy the UI. A battle callback occupies one of the central gameplay modes of the entire RPG. It must know how to initialize graphics, battler controllers, command state, healthboxes, text buffers, animation systems, and the turn loop. But all of that only makes sense if setup has already provided coherent inputs.

Let’s pause and test the invariant. What must be true before battle initialization can trust the world? First, the battle type must be set. Not just “a battle exists,” but what kind of battle: wild, trainer, special, link, Safari, double, or whatever combination the engine supports. Second, the sides must be backed by legal party data. The player party must be available. The enemy party must either be generated for wild battles or loaded from trainer data for trainer battles. Third, the callback or script continuation must be defined, because the battle is not the end of the program. Fourth, trainer identity, if relevant, must be stable. Fifth, any special rule context must be captured before the overworld context fades away.

If any one of those is missing, the battle engine has to become defensive in ways that make the architecture messy. A battle engine that constantly asks, “Do I really have an enemy party?” or “Do I know whether running is legal?” or “Do I know where to return?” is an engine whose setup boundary has failed. Some checks are still necessary, of course, but the conceptual contract should be strong.

Now I want to contrast wild and trainer setup, because this distinction reveals why battle.h needs both general battle constants and trainer-specific data structures. In a wild battle, the opponent is usually generated from encounter logic: species, level, maybe held item or special encounter attributes. The enemy does not need a trainer class, trainer name, or trainer party table entry. In a trainer battle, the opponent is selected through a trainer id, and that id maps to a data record. So both battles end up with battlers and parties, but they arrive there through different setup paths. The battle engine wants the final normalized form. The setup code handles the difference in origin.

If I draw that as arrows, wild goes: encounter table or scripted species, generated enemy Pokemon, enemy side party, active enemy battler. Trainer goes: trainer id, trainer table entry, trainer party struct, enemy side party, active enemy battler. Notice where the arrows converge. They converge before the command loop. That convergence is the purpose of setup.

The research packet’s source selection reinforces this. It points us to battle_setup.c chunks for setup and handoff, and battle.h chunks for battler constants, trainer party structs, and trainer table contract. Those are not random excerpts. Together they describe a pipeline. pokefirered/src/battle_setup.c chunk_0e0ab33ad5402d71 is part of battle setup. chunk_db24b720e895b599 is setup flow. chunk_a8e4b22a367933e7 is battle start handoff. Then pokefirered/include/battle.h chunk_4079f42fde2b4370, chunk_4e3097f639b05787, and chunk_403d30271e2270d6 define the shared terms that make the handoff meaningful.

One more subtle point before we move on. A header like battle.h can become large because the subsystem itself is large. That does not automatically mean the architecture is bad. In modern C projects, we might split interfaces more aggressively. We might have battle_types.h, trainer_data.h, battle_actions.h, battle_callbacks.h, and so on. But in a decompiled GBA project, you should read the header as an index of coupling. It tells you which concepts are shared broadly enough to require common declarations. Your job as an architect is not to complain that the header is big. Your job is to identify which parts of the header are true domain contracts and which parts are historical or convenience coupling.

For this lecture, the true domain contracts are the ones we have been naming: battler identity, action vocabulary, party data shape, trainer table shape, battle flags, and handoff assumptions. Those are the things your clone engine, your analysis paper, or your lab design must preserve even if your implementation language is different. If you write a modern TypeScript or Rust version, you do not need to reproduce the exact C header layout. But you do need an equivalent interface contract. Otherwise your overworld, scripts, trainer database, battle engine, and UI will not agree on what a battle is.

So, to close this segment, not the whole lecture, here is the synthesis I want you to carry into the next pass. Battle setup is a translation problem. battle.h is the dictionary. battle_setup.c is the interpreter walking from one language to the other. The overworld language talks about maps, events, scripts, objects, and encounter conditions. The battle language talks about battlers, sides, actions, parties, flags, trainers, and callbacks. Advanced engine work begins when you stop treating those as loose variables and start treating them as a contract with invariants. Next, we will use that contract to look at the first things the battle engine is allowed to do precisely because setup has already made those promises.

Now, watch the consequence of that promise. If setup has correctly filled the battle flags, selected the opponent party source, preserved the return callback, and identified the battler positions, then the battle engine can begin without asking overworld questions. That is the point. The battle engine should not have to ask, “Was this a trainer object on Route 3?” It should ask, “What side is this battler on, what action is legal, what party data do I read, and what script or callback resumes afterward?”

Let me put that on the board as a boundary line. Left side: map, object event, script, encounter condition. Right side: battler id, side, action, party, trainer metadata, battle type flags. The translation happens in the setup layer. In FireRed, the source packet makes this boundary very visible: `pokefirered/src/battle_setup.c` pulls in `event_data.h`, `script.h`, `event_object_movement.h`, `field_player_avatar.h`, and also `battle.h` and `battle_transition.h`, as shown in `pokefirered/src/battle_setup.c` chunk `chunk_494d132a59422c85`. That include list is not decoration. It is an architectural fingerprint. This file is allowed to know about field state and battle state because it is the bridge.

Now connect that back to `battle.h`. The battler identity and action constants in `pokefirered/include/battle.h` chunk `chunk_4079f42fde2b4370` are not merely convenient names. They define the vocabulary the engine uses once the bridge has been crossed. A battler is not “the trainer’s first monster on the map.” It is a battle-positioned participant with a side, an index, and a role in turn selection. Likewise, an action is not “the player pressed a menu item somewhere.” It is a battle action the core can schedule, validate, serialize through state machines, and resolve.

Concrete example. Suppose a trainer battle begins because the player steps into a trainer’s line of sight. In the overworld, the meaningful objects are coordinates, facing direction, object event id, trainer id, and script progress. But once the battle starts, the battle engine does not want the trainer’s facing direction. It wants the trainer party definition. That is why the trainer party structs in `pokefirered/include/battle.h` chunk `chunk_4e3097f639b05787` matter. They are the shape of the input record the battle engine expects. And the trainer table contract in `pokefirered/include/battle.h` chunk `chunk_403d30271e2270d6` tells the rest of the program, “If you give me this trainer id, here is the structured battle-relevant data that id resolves to.”

So when we say FireRed is data-driven, we are not saying data magically plays the game. We are saying data records are repeatedly converted into runtime commitments. Trainer id becomes trainer table row. Trainer table row becomes party struct. Party struct becomes opponent party slots. Battle flags and battler positions tell the engine how to interpret those slots. Then callbacks and tasks keep the runtime moving frame by frame. That is the same course pattern you have seen since week one: table to global, global to callback, callback to visible behavior. We are just applying it now to the highest-pressure subsystem in the game.

Check for understanding here. If I ask, “Where should a custom engine decide whether a battle is wild or trainer?” the advanced answer is not “inside the damage formula,” and it is not “inside the move menu.” The answer is: before the battle core starts resolving turns, in the setup contract. The battle core may branch on battle type flags, yes, but the decision should already have been encoded into the battle setup state. If you delay that decision too long, every downstream module starts sniffing overworld state, and your architecture becomes tangled.

This also explains why week 12 is positioned after the party and move-system material. We already studied party slot ownership, summary screen reads, move slots, PP, and stat inputs. We are not reopening those systems today. Instead, we are using them as established dependencies. A party slot is now battle input. A move slot is now an action option. A calculated stat is now something the battle engine can assume exists when it reads a participating creature. The battle setup layer does not calculate the whole creature model from scratch; it arranges which already-existing data becomes active in the battle context.

Now, one more board cue before we close. I am writing three phrases: “source of truth,” “handoff,” and “resume path.” Source of truth: trainer records, party structs, wild encounter data, and script variables. Handoff: the setup flow in `pokefirered/src/battle_setup.c`, especially the battle setup and setup-flow chunks named in our packet, `chunk_0e0ab33ad5402d71` and `chunk_db24b720e895b599`. Resume path: the battle start handoff represented by `pokefirered/src/battle_setup.c` chunk `chunk_a8e4b22a367933e7`, where the architecture has to preserve enough callback and script context to return the player to coherent overworld behavior afterward.

That last point is easy to underestimate. Starting a battle is only half of the interface. Returning from battle is part of the same contract. If a trainer battle ends, the world must know that trainer was defeated. If a wild battle ends, the map resumes without marking some unrelated trainer event. If a scripted battle ends, the script may need to continue at a precise point. So the setup record you design in lab cannot only say, “spawn opponent.” It must also say, “what state did I consume, what global flags did I set, what callback owns the next screen, and what continuation should run when battle is over?”

And that is where I want to close today’s lecture. `battle.h` is not the battle system implementation in full. It is more important than that in some ways: it is the blueprint that lets multiple systems agree on what a battle means before the battle begins. `battle_setup.c` then performs the translation from world state into that blueprint. Across this course, the recurring architecture lesson has been that FireRed is not one giant loop with special cases scattered everywhere. It is a set of cooperating state machines, data tables, globals, callbacks, tasks, and scripts, each passing carefully shaped records to the next layer. Week 12 is where that pattern becomes unavoidable. A creature RPG battle is not isolated combat logic. It is the moment where maps, scripts, trainer data, party data, UI, callbacks, and engine state all meet, and the only reason that meeting stays manageable is that the interface contract is explicit.

## Source References
- pokefirered/src/battle_setup.c
- pokefirered/src/daycare.c
- pokefirered/src/pokemon_summary_screen.c
- pokefirered/src/learn_move.c
- pokemon-showdown/data/abilities.ts
- pokemon-showdown/data/mods/gen2stadium2/scripts.ts
