# Battle Main as Coordinated State

Lecture ID: lecture_w13_d01
Week 13, Day 1

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

## Transcript
All right, let’s begin week thirteen by tightening the thing that the midterm was trying to make visible. A FireRed battle is not “the damage formula plus some menus.” That is the beginner’s view. The advanced view is that battle is a coordinated state machine running inside the larger callback shell of the game. It has player-facing controllers, AI-facing decisions, script-facing bytecode, animation-facing sprite state, party-facing data ownership, and result-facing summary state, all moving in a deliberately staged order.

We already know the outer shell. AgbMain establishes the runtime loop, and SetMainCallback2 is how major modes take ownership of future frames; that was grounded earlier in pokefirered/src/main.c chunk_b35676322b169f85 and chunk_6cfd90f6d0136799. We also know that per-frame dispatch can be decomposed through task functions, as in pokefirered/src/task.c chunk_3ffebc0341225936. I am not re-teaching that today. I want you to use it as prior vocabulary. Battle main is one of the places where that vocabulary becomes unavoidable.

Now, on the board, I’m writing one sentence: “Battle main is a coordinator, not a monolith.” Underline coordinator. When you open pokefirered/src/battle_main.c, especially the battle-main flow focus in chunk_2017de6d7013d2b7, you see initialization, callback handoff, and setup of battle runtime structures. CB2_InitBattle is not the whole battle; it is the entry into the battle world. It allocates battle resources, allocates sprite-facing data, allocates mon sprite graphics, branches for multi battle setup, and then drops into internal initialization for ordinary cases. That is an architectural statement. The engine does not say, “Resolve the fight now.” It says, “Prepare a coordinated runtime environment that can advance over many frames.”

That is why the header matters so much. In pokefirered/include/battle.h chunk_825c6c75270bc90c, BattleResources collects pointers to support structures such as battle script stacks, callback stacks, AI thinking state, battle history, and stats-before-level-up storage. BattleResults records things that matter after or across the battle, such as faint counters, switches, healing item usage, turn count, caught species, and last used moves. And the large BattleStruct carries the turn machinery: move targets, party indexes, switch-in counters, chosen move positions, run tries, money multiplier, saved callback, chosen items, changed items, and many other fields. So when I say “coordinated state,” I mean this literally. The state is distributed across globals and structs that each serve a different part of the battle machine.

Now look at the companion chunk, pokefirered/include/battle.h chunk_8367bbcb71bfc88d. This gives us BattleScripting, BattleSpriteData, link buffers, gBattleTypeFlags, gCurrentMove, gChosenMove, gBattleWeather, gBattlersCount, gBattlerPositions, gBattlerControllerFuncs, gBattleControllerExecFlags, gBattleBufferA, gBattleBufferB, gBattleMainFunc, gBattlerAttacker, gBattleScripting, and gBattlescriptCurrInstr. That list is dense, but do not flatten it into “a lot of globals.” That would be too vague for this point in the course. Instead, classify them. Some identify who is acting. Some identify what move is being processed. Some track the script interpreter. Some track controller communication. Some track rendering and healthbox animation state. Some track battle mode and outcome.

Quick check for understanding. If I ask, “Where is the battle state?” the wrong advanced answer is “in BattleStruct” if you mean only BattleStruct. BattleStruct is central, yes, but not sufficient. The battle state is coordinated through BattleStruct, BattleResources, BattleResults, BattleScripting, arrays like gBattleMons, controller buffers, battler indexes, turn-order arrays, and the current battle-main function pointer. That is why debugging a battle bug requires following ownership, not just searching for one variable.

Now, let’s connect this to the source focus for today. In pokefirered/src/battle_main.c chunk_568e3366056a9ce4, the action dispatch table sTurnActionsFuncsTable maps battle action constants to handler functions: use move, use item, switch, run, Safari actions, execute script, try finish, finished, and the fainted-no-action case. Nearby, sEndTurnFuncsTable maps battle outcomes to end-turn handling functions. This is a beautiful compact example of data table to callback to behavior. We have used that phrase all semester, and battle main gives us a high-pressure version of it. The selected action does not become a giant if statement scattered everywhere. It becomes an action id that can index a handler table.

And pause there, because this is exactly where a strong clone design either gets clean or starts to rot. Suppose you are implementing the week thirteen lab: two battlers, chosen actions, speed ordering, current battler, current move, and a script pointer placeholder. The beginner implementation puts everything in one function: ask player, ask enemy, compare speed, print message, subtract HP, repeat. That works for ten minutes. But as soon as you add switching, items, faint replacement, move priority, scripts, animation waits, or link synchronization, the single function collapses. FireRed’s pattern is to normalize decisions into action ids, store them per battler, derive an ordered action list, and then let the turn executor walk that list.

So, on the board, I’m drawing four boxes. Box one: chosen action by battler. Box two: chosen move or target details. Box three: turn order arrays. Box four: current action execution. The key idea is that these boxes are not interchangeable. “Bulbasaur chose Vine Whip” is not the same fact as “Bulbasaur acts first.” A battler can choose a move and still be reordered by priority, speed, item, or special battle rule. This is why FireRed separates gChosenActionByBattler from gActionsByTurnOrder and gBattlerByTurnOrder. The header-level research packet explicitly calls out modeling a mini battle turn with B_ACTION_*, gChosenActionByBattler, gActionsByTurnOrder, gCurrentMove, and gBattlescriptCurrInstr as a lab pattern, grounded in pokefirered/include/battle.h chunk_8367bbcb71bfc88d and the battle-main chunks.

Concrete example. Imagine a two-battler single battle. Player battler zero chooses Tackle. Enemy battler one chooses Quick Attack. At action-selection time, both choices are stored from the point of view of the battler who chose them. So the player’s chosen action might be B_ACTION_USE_MOVE, and the enemy’s chosen action might also be B_ACTION_USE_MOVE. Their chosen move positions or chosen move ids live alongside that. But the turn-order phase must ask a different question: who resolves first? Quick Attack has priority, so even if the enemy is slower, the enemy may be placed earlier in gBattlerByTurnOrder, with the corresponding action in gActionsByTurnOrder. Only after that does the executor set the current acting battler and current move, then hand control into script or action handling.

That example is intentionally small, but the architecture scales. In a double battle, the difference between “the left-side partner has chosen” and “the whole side is ready” matters. In a link battle, controller readiness and buffers matter. In a Safari battle, the action vocabulary changes. In a forced recharge or multi-turn move, the engine may set the action without opening the ordinary player menu. FireRed’s state machine can absorb those cases because the representation is not “the menu said so.” It is “this battler has this normalized battle action, and the turn scheduler will decide when that action executes.”

Now, I want to bring in controller-facing state, because it is easy to skip if you read only the rules. Battle main does not merely compute invisible outcomes. It has to coordinate with battler controllers. The globals gBattlerControllerFuncs, gBattleControllerExecFlags, gBattleBufferA, and gBattleBufferB in pokefirered/include/battle.h chunk_8367bbcb71bfc88d tell us that there is a message-like layer between the central battle coordinator and the per-battler controller behavior. In practical terms, the battle core can emit a “choose action” request, wait while that controller or UI does its work, and then read back the result from a buffer. That is why a battle turn cannot be understood as one synchronous function call from menu to damage. It is frame-based coordination.

This also connects to sprite-facing state. The same battle.h chunk exposes BattleSpriteData and structures for battler sprite info, animation info, healthbox info, and battle bars. That means the core must preserve enough state for visible animation and healthbox updates to continue across frames. A move is not just “subtract HP.” It is attacker identity, target identity, current move id, animation arguments in BattleScripting, damage fields, move result flags, healthbox updates, and script instruction progression. At advanced level, I want you to stop treating visuals as an afterthought. On this hardware and in this architecture, visible behavior is part of the state choreography.

Let’s do a check question. Suppose the player selects a move and the move menu closes. Has the move happened yet? No. The selection has been recorded. The controller has reported a choice. The action may still need validation, target assignment, ordering, script setup, and execution. The fact that the UI accepted input is not identical to the fact that the battle action resolved. That distinction is one of the most important ideas for this week.

Now, how does battle main begin coordinating all of this? In pokefirered/src/battle_main.c chunk_2017de6d7013d2b7, CB2_InitBattle and CB2_InitBattleInternal show the transition into battle-owned runtime. The internal init resets video callbacks, clears VRAM, sets GPU registers, clears scanline effects, resets palette fade, initializes battle backgrounds, loads textbox and background assets, resets sprite data and tasks, draws the entry background, reserves sprite palettes, sets VBlankCB_Battle, calls SetUpBattleVars, and then chooses the next battle-start callback. That tells you something important: before turn logic can be meaningful, the battle has to own both logic state and presentation state. The callback is not only a calculator. It is the owner of a complete mode.

And because week twelve already covered starting a battle from world state, I will not re-teach wild and trainer setup. I will just say the relevant bridge: by the time CB2_InitBattle runs, battle setup has already translated overworld concerns into battle flags, parties, opponent ids, and return callbacks. That prior work was tied to battle_setup.c in week twelve. Today we are looking at what the battle mode does after it receives that contract.

Now, let’s use the action table again. The sTurnActionsFuncsTable in pokefirered/src/battle_main.c chunk_568e3366056a9ce4 is a dispatch table. A dispatch table is more than a convenience. It encodes the idea that actions are first-class values. If gCurrentActionFuncId is B_ACTION_USE_MOVE, you get HandleAction_UseMove. If it is B_ACTION_SWITCH, you get HandleAction_Switch. If it is B_ACTION_RUN, you get HandleAction_Run. That is a clean separation between scheduling and execution. The scheduler does not need to inline every action. It just needs to determine the next action id and next battler id.

The end-turn table does the same thing for outcomes. Battle outcome is state. It can be won, lost, ran, caught, fled, no Safari balls, and so on. The table maps those outcome constants to continuation functions. This is state machine design with C arrays and function pointers, not with an object-oriented class hierarchy. If you are writing a modern engine, you might express this with enums and match expressions, or command objects, or a reducer. FireRed expresses it with constants, globals, arrays, and function pointers.

Now I’m going to draw a little timeline. Frame one: battle main callback runs. It calls whatever gBattleMainFunc currently points to. That function may initialize, request controller work, or advance a script. Frame two: battle main callback runs again. Maybe controller flags are still set, so the coordinator waits. Frame three: the controller has written its result, so battle main records the chosen action and moves to the next state. This is why gBattleMainFunc, declared among the major battle globals in pokefirered/include/battle.h chunk_8367bbcb71bfc88d and defined in battle_main’s global state area, is such an important piece of the design. It is the battle subsystem’s internal “what phase am I in?” pointer.

A useful analogy, but keep it technical: SetMainCallback2 chooses the building. gBattleMainFunc chooses the room inside the building. The main game loop says, “We are in battle mode now.” The battle main function pointer says, “Inside battle mode, we are currently selecting actions, running a script, resolving an action, checking end-turn effects, or returning from battle.” Do not overextend the analogy, but it helps separate the outer callback shell from the inner battle scheduler.

Let’s connect to prior course work. Party slot ownership matters here because switching and faint replacement mutate which party member is represented by a battler. Summary screen reads matter because battle reads the same creature fields, but under battle timing constraints. The move relearner menu state machine matters because you have already seen that FireRed menus advance by staged callbacks and state variables; battle does the same thing at larger scale. battle.h as subsystem map matters because this is the week where the map becomes execution. Trainer party structs matter because the enemy side must have been materialized before the turn loop begins. We are not re-teaching any of that; we are spending it.

Now, if I were implementing the lab on a clean board, I would start with two arrays: chosenActionByBattler[2] and battlerByTurnOrder[2]. Then I would add actionsByTurnOrder[2]. Then currentTurnActionNumber, currentBattler, currentMove, and a script pointer placeholder. The invariant would be this: selection writes per-battler facts, ordering writes turn-list facts, execution reads the turn list. If you blur those stages, you will make later mechanics harder. For instance, if you store only “currentMove” during selection, the second battler’s choice overwrites the first. If you store only “chosen action” without a turn-order list, you have to recompute ordering every time you advance the action executor. If you store only battlers in order but not actions in order, switching and item cases become awkward.

Here is the advanced check. Why does FireRed need both gBattlerByTurnOrder and gActionsByTurnOrder? Because the ordered list is not just “who acts,” it is “which action does this actor perform at this ordered slot.” The same battler identity can be interpreted differently depending on whether the action is use move, switch, run, item, script, or finished. Keeping parallel arrays lets the executor say, “At turn index i, battler X performs action Y.” That is the minimal command queue for the turn.

And now I’ll add one more point before we pause this portion. BattleScripting is the bridge from action execution into script-driven effects. In pokefirered/include/battle.h chunk_8367bbcb71bfc88d, BattleScripting contains fields like damage multiplier, animation arguments, move-end state, battler with ability, multihit state, stat changer, get-exp state, learn-move state, and other script-facing values. gBattlescriptCurrInstr is the current instruction pointer for the battle script interpreter. So when a move begins resolving, the C action handler is not expected to hard-code every message, animation, secondary effect, and branch in one place. It can hand off to script instructions, using BattleScripting and current globals as shared context.

So the synthesis for this first segment is this: FireRed battle main is coordinated state across three layers. The outer layer is the game callback shell, where battle becomes the current major mode. The middle layer is battle main’s internal scheduler, with gBattleMainFunc, chosen actions, turn-order arrays, action dispatch tables, and outcome tables. The inner layer is script, controller, and sprite-facing state, where gBattlescriptCurrInstr, BattleScripting, controller buffers, execution flags, and BattleSpriteData let visible behavior unfold across frames. If you can keep those layers distinct, then the battle engine stops looking like a pile of globals and starts looking like a carefully staged runtime machine. We will keep going from that point into the actual action-selection and ordering flow next.

So, now let’s move one step inward. We have established the three layers. I want you to focus on the middle layer for a while: not the entire battle engine, not damage calculation yet, not move effects yet, but the coordinated state that says, “Whose decision do we have, what kind of action is it, when will it run, and what shared state must be valid while it runs?”

On the board, I’m going to write a deliberately small chain: chosen action, turn order, active battler, current move, script pointer. That chain is the spine of today’s lab. And the reason I’m writing it in that order is that this is also the order in which your mental model should proceed. A battler chooses something. The engine stores that choice in a per-battler slot. Later, the battle engine converts the set of choices into an execution order. Then, as it walks that order, it promotes one battler into the current actor, one action into the current action, and often one move into the current move. Then script or controller-facing code uses that current context to produce staged behavior.

Notice what FireRed is doing architecturally. It is avoiding a single enormous “turn object” in the modern object-oriented sense. Instead, it uses several arrays and globals that each answer a narrow question. This is exactly why, in `pokefirered/include/battle.h` chunk `chunk_8367bbcb71bfc88d`, the globals and `BattleScripting` material are so important as a map of shared battle context. The header is not merely exposing random variables. It is telling other compilation units, “Here are the shared coordinates of the battle machine.” That is a design choice. It has costs, yes, but it also matches the hardware, the C style, and the frame-staged design of the engine.

Let’s make this concrete. Imagine a two-battler wild battle. Player’s battler chooses Tackle. Enemy’s battler chooses Growl. At the moment the menu input is accepted, the engine does not need to immediately run Tackle. It needs to remember that the player battler selected a fight action and selected a specific move slot. So we have state that is naturally indexed by battler. Not by turn position yet. By battler. The question being answered is, “What did battler zero choose?” and “What did battler one choose?”

Then the engine reaches the ordering phase. Now the question changes. We are no longer asking, “What did each battler choose?” We are asking, “In what sequence should the chosen actions execute?” That is why turn-order arrays matter. They represent a different viewpoint over the same turn. The battler-indexed storage is about ownership of decisions. The turn-indexed storage is about scheduling.

Pause there, because this distinction is where many advanced students make subtle mistakes. A battler index and a turn-order index are both small integers, and in a two-battler battle they often appear to line up. That is a trap. Architecturally, they mean different things. Battler zero means a participant slot in the battle. Turn-order zero means the first scheduled action this turn. In a simple case, the first scheduled action might belong to battler zero. In another case, because of speed, priority, switching, item use, or forced behavior, the first scheduled action might belong to battler one. So if your implementation in the workshop accidentally treats these as interchangeable, it may pass the simplest test and fail as soon as speed ordering changes.

Now, check your understanding for a moment. If I say, “The player selected a move,” which structure should you expect to update first: a turn-order structure or a chosen-action-by-battler structure? Chosen action by battler. Right. If I say, “The faster participant should execute before the slower participant,” which structure is being prepared or consulted? Turn order. That distinction is the entire point of this middle layer.

In the FireRed source focus for today, `pokefirered/src/battle_main.c` chunk `chunk_a15739d3170f73c1` is the anchor for this turn and action flow. The details are dense, but the shape is readable if you keep the questions separate. Some state records intention. Some state records ordering. Some state records the currently executing actor. Some state records script progress. And then the per-frame callback keeps returning to the battle engine until each staged piece has completed.

Let me connect that to something you already know from previous weeks without re-teaching it. In the move relearner, which we looked at through `pokefirered/src/learn_move.c` chunk `chunk_af3598df6f092723`, the menu state machine stores where the player is in the interaction and advances through named states. That was a UI-centered state machine. Battle main is a combat-centered state machine. The same architectural habit is present: do not block the whole game while one conceptual operation happens. Instead, store enough state to resume next frame. But battle main is more sophisticated because multiple battlers, scripts, animations, controllers, and outcomes are all coordinating through the same timeline.

So, if I were coding the lab version on the board, I would first define something like `chosenActionByBattler[2]`. I would not start with damage. I would not start with animations. I would not even start with moves as full data records. I would begin with action identity. Maybe battler zero has action fight, battler one has action fight. Then I would have `chosenMoveByBattler[2]`, because a fight action needs an associated move slot or move id. Then I would build `battlerByTurnOrder[2]`. If battler one is faster, that array becomes one, zero. If battler zero is faster, it becomes zero, one.

Now watch the subtle handoff. When the scheduler says the next actor is battler one, the engine sets something equivalent to current battler equal to battler one. Then it loads the action chosen by battler one. If that action is fight, it loads the selected move for battler one into current move state. Only now does it have enough context to begin move execution. And when move execution begins, the script pointer placeholder becomes meaningful. In FireRed, that script pointer role is represented by `gBattlescriptCurrInstr`, and the surrounding scripting state appears in the battle globals described by `pokefirered/include/battle.h` chunk `chunk_8367bbcb71bfc88d`. In your lab, you do not have to implement the real bytecode interpreter. But you do need a placeholder that teaches the same architecture: the current action may enter a script-like staged routine, and that routine must remember where it is.

This is why I do not want you to write the lab as a single function called `runTurn()` that immediately prints both actions and exits. That would be logically correct for a toy simulator, but it would miss the architecture we are studying. FireRed’s battle loop is not just computing an answer. It is coordinating time. It must let text boxes appear, animations play, health bars drain, controller commands complete, sprites face the right direction, fainting checks run, and follow-up scripts branch. The engine therefore keeps state that outlives one C function call.

Let’s bring in `BattleResources` and `BattleResults` for a moment. In `pokefirered/include/battle.h` chunk `chunk_825c6c75270bc90c`, those structures are part of the broader battle contract. Conceptually, `BattleResources` gives the battle system a place for allocated runtime material that should be managed as a battle-lifetime resource rather than as a permanent global. `BattleResults` gives the battle a place to accumulate outcome information. That is different from chosen-action state. Chosen actions are turn-local or phase-local. Results summarize consequences that matter when the battle resolves or reports back to the outer game. That separation is important. Do not put everything in one bucket just because it all belongs to battle.

Here is a concrete example. Suppose the player defeats a wild Pokémon. During the turn, the engine needs current battler, current move, damage, target, script pointer, and execution flags. But after the battle, the overworld does not care what the second script instruction of Tackle was. It cares about the result: did the player win, lose, flee, catch the Pokémon, gain experience, or transition to some post-battle behavior? That is why result state is architecturally different from execution state. One is for running the machine. The other is for reporting what the machine produced.

This also connects back to the course-wide trace we keep using: data table to runtime globals to callback, task, or script to visible behavior. The battle engine sits at the most intense point of that trace. Move data and species data are not enough. Party data is not enough. Trainer party setup is not enough. Those pieces enter battle, but then battle main coordinates them through globals, callbacks, scripts, controllers, and sprites. That is why week 13 sits after our work on party ownership, summary data reads, move relearner menus, and battle setup. We needed to know where the participants came from before we could understand how the battle loop schedules them.

Let me ask another check-for-understanding question. If the player presses A on a move in the fight menu, should the health bar begin draining immediately? No. Not in this architecture. The input phase records a chosen action. Later phases validate, order, dispatch, script, animate, apply effects, and update visible state. The fact that a button press eventually causes HP loss does not mean those two things are adjacent in the control flow.

And this is also where advanced debugging gets easier. If a battler uses the wrong move, you ask: was the wrong move stored when the action was chosen, or was the wrong battler read during turn-order execution, or was the current move overwritten during script execution? Those are different bugs. If an action runs in the wrong order, you inspect the scheduling arrays, not the move data. If the correct move runs but the animation faces the wrong target, you inspect the controller and sprite-facing state, not the chosen-action array. This is the professional value of separating the layers.

Now, in `pokefirered/src/battle_main.c` chunk `chunk_568e3366056a9ce4`, the emphasis is battle state progression. That phrase, state progression, matters. The engine is not merely “inside battle.” It is at a specific point in a sequence. Waiting for commands is different from sorting actions. Sorting actions is different from executing an action. Executing an action is different from waiting for a controller command or script to finish. A global function pointer such as `gBattleMainFunc`, which we discussed in the first segment, lets the battle loop say, “On the next frame, resume at this phase.” That is a strong example of C-era state-machine design.

Let’s contrast that briefly with Pokémon Showdown, since our course memory tells us to use it as a secondary comparison but not as the authority. In Showdown, battle behavior is often expressed through structured data hooks and event callbacks. For example, `pokemon-showdown/data/mods/gen2stadium2/scripts.ts` chunk `chunk_3480e574205d5d0f` shows damage logic flowing through move objects, immunity checks, fixed damage cases, and callbacks. That is useful for understanding modern simulator organization. But FireRed’s engine is not primarily a TypeScript event system. FireRed is a GBA runtime machine coordinating C globals, function pointers, scripts, and controller state across frames. So Showdown helps us compare battle logic expression, while FireRed teaches us the embedded game architecture we are actually studying.

Back to the board. I am going to add two more labels under our chain: “ownership” and “view.” Chosen actions are owned by battlers. Turn order is a view over battlers for one scheduling pass. Current battler is a temporary execution focus. Current move is temporary execution context. Script pointer is staged continuation. That language is precise, and I want you to use it in your homework. If you write, “The array stores who goes first,” specify which array and what its index means. If you write, “The battler uses a move,” specify whether you are talking about the battler’s stored choice, the ordered action slot, or the currently executing global.

Now let’s walk through the two-battler example at a frame-staged level. Frame group one: the player menu and enemy selection routines eventually produce choices. The choices are stored per battler. Frame group two: the battle main phase notices all required choices are present. It prepares action order. Frame group three: it sets the first active battler from the order array. Frame group four: it dispatches based on that battler’s chosen action. If the action is fight, it prepares current move and target context. Frame group five: it points script execution at the appropriate script entry or placeholder. Frame group six and onward: script and controller-facing systems advance visible behavior, possibly over many frames. Only after that action is complete does battle main return to the ordered list and advance to the next scheduled battler.

That is the lab in miniature. If your implementation can preserve that sequence, you have captured the architecture even if your damage formula is fake and your script pointer is just a string or enum. The goal is not to rebuild FireRed in an afternoon. The goal is to encode the same separation of responsibilities.

Let’s tie this back to files. When you are tracing this in the real source, `pokefirered/src/battle_main.c` chunk `chunk_2017de6d7013d2b7` gives you battle main flow, `pokefirered/src/battle_main.c` chunk `chunk_568e3366056a9ce4` gives you state progression, and `pokefirered/src/battle_main.c` chunk `chunk_a15739d3170f73c1` gives you turn and action flow. The header support in `pokefirered/include/battle.h` chunk `chunk_825c6c75270bc90c` and chunk `chunk_8367bbcb71bfc88d` tells you which resources, results, scripting fields, and globals are part of the shared contract. That combination is what I mean by source-grounded architecture. We are not saying “battle engines usually do this.” We are saying FireRed’s files expose this division.

Now, one more subtle point before we move to controller and sprite-facing state. The chosen action is not always equivalent to the final executed behavior. A battler may choose fight, but before execution, the engine may discover that the battler is unable to move, has fainted, is recharging, is under an effect that modifies action, or is affected by another battle rule. This means the action scheduler cannot be naïve. It must preserve the original decision while still allowing execution-time rules to redirect or cancel behavior. That is another reason the engine needs staged state instead of one immediate calculation.

Think about a real classroom example. Suppose I ask three students to present in order of birth month. Each student first chooses a topic. Then I sort the students by birth month. When the first student stands up, I do not ask, “What topic is stored in presentation slot zero?” I ask, “Which student is in presentation slot zero, and what topic did that student choose?” That is the battler-versus-turn-order distinction. If the first presenter is Maria, I retrieve Maria’s topic. I do not retrieve whatever topic happens to be stored at index zero unless Maria is also student zero. FireRed’s battler arrays and order arrays work with exactly that kind of indirection.

So, if you remember one operational rule from this part, remember this: turn order stores battler identities, not independent action objects. The selected action remains associated with the battler. The order tells the engine which battler’s action to consult next. That is a small statement, but it prevents a lot of implementation errors.

Let’s do a quick verbal trace. Player battler is index zero, enemy battler is index one. Player chooses move slot two. Enemy chooses move slot zero. Enemy is faster. The order array becomes one, zero. The battle engine begins ordered slot zero, reads battler one, sets current battler to one, reads chosen action for battler one, reads chosen move slot for battler one, sets current move to the enemy’s move, and begins the script placeholder. When done, ordered slot one gives battler zero, and the same process repeats for the player. If you can say that without collapsing the indices, you understand the design.

We will keep building from there, because next the visible side becomes important. Once a battler is current and a move is current, the battle still cannot simply mutate numbers silently. It has to send commands to the battle controllers, wait for those commands, manage sprite state, and keep script execution aligned with what the player sees. That is where the inner layer becomes more than an implementation detail. It is the reason battle feels like a sequence of events rather than a spreadsheet update.

Good. So keep that sentence in your head: the engine has selected a current battler and a current move, but it has not yet made the battle visible. That distinction is the difference between a battle simulator and a battle engine. A simulator can compute, “Tackle did 8 damage,” and immediately update HP. FireRed has to stage that result through controller commands, text printers, sprite animation, palette fades, healthbox updates, and script commands that may pause until the visible work is done.

Now, on the board, I’m going to draw three layers. At the top, write “frame shell.” Under that, write “battle coordinator.” Under that, write “battler controllers and scripts.” The frame shell is the part we already know from earlier weeks: the GBA main loop calls the active callback slots every frame. We are not going to re-teach AgbMain or SetMainCallback2 today, but remember the architecture. FireRed does not run battle as a blocking function that returns a completed result. It installs battle callbacks, and then the battle progresses across frames.

In the battle source, the cleanest evidence is in pokefirered/src/battle_main.c, especially the battle main flow chunk, chunk_2017de6d7013d2b7. The visible callback, BattleMainCB2, does frame-facing work: AnimateSprites, BuildOamBuffer, RunTextPrinters, UpdatePaletteFade, and RunTasks. That means battle is still living inside the same runtime shell we studied in the first part of the course. It has not escaped into some separate engine. It is using the same per-frame dispatch philosophy, but now the state being dispatched is much richer.

Then the coordinator layer is BattleMainCB1. This is the line I want you to care about conceptually: BattleMainCB1 calls gBattleMainFunc, and then loops over battlers and calls gBattlerControllerFuncs for each active battler. That is a huge architectural statement. FireRed separates “what phase of battle are we in?” from “what does each battler controller need to do this frame?” The first is gBattleMainFunc. The second is the array of controller functions, one per battler. So when I say “Battle Main as Coordinated State,” I mean it very literally. The battle does not live in one variable. It is coordinated through function pointers, arrays, buffers, flags, and scripts that agree on the current phase.

Let’s connect that to the source names. In pokefirered/src/battle_main.c chunk_568e3366056a9ce4, the state progression is visible through functions like BeginBattleIntro, BattleStartClearSetData, HandleTurnActionSelectionState, SetActionsAndBattlersTurnOrder, CheckFocusPunch_ClearVarsBeforeTurnStarts, and RunTurnActionsFunctions. Those are not just helper functions. They are phases. The battle engine advances by assigning gBattleMainFunc to the next phase function. You should hear an echo from the move relearner lecture, where pokefirered/src/learn_move.c chunk_af3598df6f092723 showed a menu state machine progressing through named states. But here the stakes are higher, because battle state is shared across battlers, sprites, scripts, controllers, results, and party mutation.

Now, let’s take the specific moment from the end of the last segment. We have a chosen action for each battler. We have turn order arrays. We have selected the first ordered battler. In pokefirered/src/battle_main.c chunk_a15739d3170f73c1, HandleAction_UseMove begins by setting gBattlerAttacker from gBattlerByTurnOrder[gCurrentTurnActionNumber]. Notice what it does not do. It does not ask, “Who is the player?” It does not ask, “Was this originally battler zero?” It trusts the turn-order array. That is the whole point of the prior layer. The ordered slot tells the action handler who is current.

Then it sets up move state. It uses gBattleStruct->chosenMovePositions for the attacker, gBattleMons[gBattlerAttacker].moves for the actual move, and writes gCurrentMove and gChosenMove. If Struggle is needed, it overrides the normal move. If the battler is locked into a multi-turn move or recharge behavior, it reads gLockedMoves. If Encore applies, it reads gDisableStructs. So the chosen move is not simply “whatever button the player pressed.” It is the result of button choice plus volatile battle constraints plus move availability plus special cases. That is why the globals look dense. They are not elegant in a modern object-oriented sense, but they are carrying a real coordination burden.

Here is the concrete example. Suppose this is a simple one-on-one battle. Battler zero is the player’s active Pokemon. Battler one is the opponent. The player chose Quick Attack in move slot one. The opponent chose Tackle in move slot zero. During action selection, gChosenActionByBattler[0] and gChosenActionByBattler[1] both become B_ACTION_USE_MOVE. gBattleStruct->chosenMovePositions[0] is set to one, and gBattleStruct->chosenMovePositions[1] is set to zero. Then SetActionsAndBattlersTurnOrder builds gActionsByTurnOrder and gBattlerByTurnOrder. Because Quick Attack has priority, GetWhoStrikesFirst will cause battler zero to come before battler one, even if the opponent is faster. So ordered slot zero is battler zero, action use move. Ordered slot one is battler one, action use move.

Now RunTurnActionsFunctions runs. It reads gCurrentActionFuncId, calls the appropriate action function from the turn action table, and the use-move handler says, “The attacker is gBattlerByTurnOrder[gCurrentTurnActionNumber].” For ordered slot zero, that means attacker zero. It reads chosen move position one, loads the actual move from gBattleMons[0].moves[1], writes gCurrentMove, resolves target, and finally sets gBattlescriptCurrInstr to gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]. That final step is the bridge. We are moving from turn-order coordination into script execution. The move is no longer only an integer. It has selected a battle script entry point.

Check for understanding here. If I ask, “Where is the move slot stored before the move executes?” you should not answer gCurrentMove. gCurrentMove is the currently executing move. The chosen slot lives in gBattleStruct->chosenMovePositions for each battler. If I ask, “Where is the ordered battler stored?” you should not answer gActiveBattler by default. The ordered battler is in gBattlerByTurnOrder indexed by gCurrentTurnActionNumber. gActiveBattler is reused constantly as a loop variable and controller focus variable. That reuse is one of the reasons reading this file casually can be misleading.

Let me pause on gActiveBattler, because advanced readers often make a subtle mistake here. They assume “active battler” means “the battler whose turn is currently being executed.” Sometimes it does. But in BattleMainCB1, gActiveBattler is used to iterate over every battler so their controller functions can run. In HandleTurnActionSelectionState, it iterates through battlers to ask for choices. In target resolution, it may scan battlers looking for Lightning Rod redirection. So gActiveBattler is a working cursor. gBattlerAttacker and gBattlerTarget are semantically stronger during move execution. gBattlerByTurnOrder and gCurrentTurnActionNumber are stronger for ordered turn progression. This is one of the recurring lessons of this week: do not infer semantics from a friendly-looking global name. Infer semantics from who writes it, who reads it, and which phase you are in.

Now look at the support structures in battle.h. In pokefirered/include/battle.h chunk_825c6c75270bc90c, BattleResources groups pointers to battle-wide resources: ResourceFlags, BattleScriptsStack, BattleCallbacksStack, AI_ThinkingStruct, BattleHistory, and related storage. BattleResults records the battle’s durable summary: faint counters, switches, healing items, battle turn counter, last used moves, caught species, catch attempts. Those are different lifetimes. BattleResources is runtime working storage. BattleResults is the summary that survives as an outcome record. If you mix those up, you will design the wrong lab architecture. Your lab’s “current turn array” does not belong in a results struct. Your “last used move for reporting” does.

Then pokefirered/include/battle.h chunk_8367bbcb71bfc88d gives us BattleScripting and the extern globals that the script engine and battle engine share: gCurrentMove, gBattlerAttacker, gBattlerTarget, gBattleScripting, gBattlerByTurnOrder, gActionsByTurnOrder, and gChosenActionByBattler. That header is not merely a list of variables. It is the subsystem contract. The move effect scripts need to know the current move and the current attacker. Controller code needs to know which battler is being asked to choose. End-turn code needs battle results and outcome. The header tells us which pieces of state are intentionally shared across the battle subsystem.

Let’s bring in the visible side now. Once gBattlescriptCurrInstr points to a script, the engine does not instantly finish the script in one CPU burst. Script commands may emit controller commands, and controller commands may set execution flags. FireRed uses buffers like gBattleBufferA and gBattleBufferB, controller function pointers in gBattlerControllerFuncs, and gBattleControllerExecFlags to coordinate asynchronous visible work. “Asynchronous” here does not mean threads. It means multi-frame progress. A command is emitted, a battler is marked for controller execution, and the main battle script waits until controller execution flags clear.

That waiting behavior is crucial. If the battle script prints “Pikachu used Quick Attack!” and immediately subtracts HP, but the text printer and animation have not had frames to display, the game feels broken. So BattleMainCB2 keeps running frame services, while BattleMainCB1 keeps advancing battle logic only when the controller flags allow it. This is why you see checks like “if gBattleControllerExecFlags is zero, run the next script command.” The script engine is effectively saying, “I can continue only when the visible side has caught up.”

This is the same broad architectural principle we used when tracing data table to callback to visible behavior in the midterm review. But now the path is denser. For a move, the data table includes gBattleMoves and move effects. The runtime globals include gBattleMons, gChosenActionByBattler, gBattlerByTurnOrder, gCurrentMove, gBattlerAttacker, and gBattlerTarget. The callback layer includes BattleMainCB1 and BattleMainCB2. The script layer begins at gBattlescriptCurrInstr. The visible behavior emerges through controllers, text printers, tasks, sprites, and palette updates. So when homework_w13 asks you to implement a two-battler turn-order loop, do not write it as a single function that chooses, sorts, damages, and returns. That would be a correct calculation, but it would not model FireRed’s architecture.

For your lab, the placeholder script pointer matters. Even if your script pointer does nothing except point at a fake “move effect start,” include it. Why? Because it forces you to separate “decide what action should happen” from “execute the action as a staged sequence.” Today that pointer can be a placeholder. Later, it becomes where accuracy checks, animations, messages, damage formulas, faint checks, and end-turn effects attach. FireRed’s architecture already prepares for that complexity by routing move execution through scripts rather than hard-coding every move effect directly into the turn loop.

Let me give you a second check for understanding. Imagine a battler selected a move, but before execution Encore changes what it must use. Which state should win: the original chosen move by battler, or the battle condition at execution time? In FireRed, execution-time constraints can win. HandleAction_UseMove checks gDisableStructs, locked moves, no-valid-moves state, and the current move slot before it settles gCurrentMove. That means action selection records intent, but action execution validates and transforms intent under current battle rules. That is a sophisticated design point. It lets the action selection UI remain relatively simple while the execution phase enforces battle legality.

And now you can see why controller and sprite-facing state cannot be an afterthought. gBattlerSpriteIds, gHealthboxSpriteIds, gBattleSpritesDataPtr, gMonSpritesGfxPtr, and the controller functions are not “graphics extras.” They are synchronized endpoints of the same state machine. The battle logic says, “This battler is attacking this target with this move.” The visible layer has to answer, “Which sprite object represents that battler? Which healthbox should move? Which text is printing? Which animation is running? Has the palette fade finished?” BattleMainCB2’s frame work, shown in pokefirered/src/battle_main.c chunk_2017de6d7013d2b7, is the reason those visible endpoints keep updating even when the battle script is waiting.

So, recap this segment in one breath. BattleMainCB2 maintains the frame-facing services: sprites, OAM, text, fades, tasks. BattleMainCB1 advances battle coordination by calling gBattleMainFunc and then each battler controller. gBattleMainFunc names the current battle phase. gChosenActionByBattler stores each battler’s selected action. gActionsByTurnOrder and gBattlerByTurnOrder convert choices into execution order. gCurrentTurnActionNumber chooses the current ordered slot. gBattlerAttacker, gCurrentMove, and gBattlerTarget become the execution context. gBattlescriptCurrInstr moves that context into the script engine. gBattleControllerExecFlags prevents script logic from outrunning visible controller work.

That is the architecture we are going to keep using in the next part. We will move from “a current move exists” into “the script engine performs that move while preserving turn order, controller synchronization, and end-turn recovery.” Keep the board diagram visible, because the next mistake people usually make is to treat scripts as separate from the main loop. In FireRed they are not separate. They are one layer in the coordinated state system, and the battle main loop is the conductor that gives each layer just enough time to act.

So, with that conductor image still in front of us, let’s now slow down at the boundary between action selection and action performance. Up to this point, the battlers have chosen. The engine has sorted. The current ordered slot has been selected. But a battle is not finished just because the engine knows that battler zero is going to use Tackle before battler one uses Growl. The difficult architectural question is: how does the engine perform that decision without losing the rest of the turn?

And the answer is, FireRed does not “call a move” in the simple function-call sense. It installs state. It advances a battle script. It waits for controller-side work. It returns to the main loop. Then it advances again. That pattern is the heart of pokefirered/src/battle_main.c, especially the main-flow and state-progression material we are using from chunk_2017de6d7013d2b7 and chunk_568e3366056a9ce4. When you read those chunks, do not read them as one large algorithm that runs to completion. Read them as a per-frame negotiation among several state machines.

Let me put that on the board. I’m writing: “decision state,” arrow, “execution context,” arrow, “script pointer,” arrow, “controller wait,” arrow, “resume.” That last arrow is the important one. A beginner often draws battle execution as a straight line: choose move, calculate damage, update HP, show animation, next battler. But FireRed is running on a handheld game loop. It has sprites, windows, text boxes, palette fades, healthbox updates, link timing in some modes, and controller callbacks. So the battle engine must keep enough state in globals and resource structs to leave and come back safely.

Now, connect this to the header work we did last time. In pokefirered/include/battle.h chunk_825c6c75270bc90c, the BattleResources and BattleResults structures are part of the architectural contract. They tell us that battle state is not just “which monster is alive.” It includes allocated resources used during battle and a result summary that survives the battle. Then in pokefirered/include/battle.h chunk_8367bbcb71bfc88d, the BattleScripting area and global declarations expose the other half of the contract: script execution has its own working memory, but it is still integrated into the same battle system. So when I say “script pointer,” I do not mean a cute separate scripting toy. I mean a real execution pointer inside the battle architecture, coordinated with battler IDs, move IDs, controller flags, and the main battle phase.

Here is a concrete example. Suppose we have a simple single battle. Player’s active Pokemon chooses a damaging move. The opposing battler chooses a status move. The player’s battler is faster. At the action-selection level, gChosenActionByBattler records that the player battler chose “use move,” and the opponent also chose “use move.” Then the turn-order arrays, gActionsByTurnOrder and gBattlerByTurnOrder, arrange those choices so the faster battler’s action appears first. So far, this is just scheduling. No damage has happened.

When gCurrentTurnActionNumber points to the first ordered action, the engine promotes that scheduled action into current execution context. That means gBattlerAttacker becomes the active acting battler. gCurrentMove is populated with the move being performed. gBattlerTarget is resolved according to the move’s targeting rules and battle format. Then, and only then, the script layer can interpret the current move. This is the bridge covered by the turn and action flow in pokefirered/src/battle_main.c chunk_a15739d3170f73c1: the arrays give the system order, but the globals give the script layer a current world to operate on.

Now pause and check yourself. If I ask, “Where is the current battler?” the advanced answer is not one variable. It depends on what layer we mean. During scheduling, the battler is an entry in gBattlerByTurnOrder. During execution, the acting battler is often gBattlerAttacker. During controller dispatch, the battler may be represented by a controller flag or a battler-specific command buffer. During sprite-facing work, the visual side may be concerned with a battler sprite ID or healthbox state. Same creature, different layer, different identifier. That is why this lecture is called coordinated state, not “the battle loop variable.”

Let’s do a quick verbal trace, because this is exactly the kind of trace you should be able to perform on the week 13 homework. The main callback runs battle logic for the frame. The battle state says we are processing the next turn action. The current ordered action says battler A uses a move. The engine sets attacker, move, and target. The battle script pointer, gBattlescriptCurrInstr, points to the script instructions that implement the move sequence. The script interpreter advances until it issues something that requires visible work, such as text, animation, or healthbox update. Then gBattleControllerExecFlags prevents the logical side from racing ahead while the controller side catches up. On a later frame, once the visible work reports completion, the same coordinated state resumes.

That is the architectural move you need to internalize. FireRed is not merely calculating combat. It is pacing combat.

And this gives us a way to understand why battle_main.c has so many shared globals. In a modern engine, you might package this as a BattleContext object, a TurnScheduler object, an AnimationQueue object, and a ScriptVM object. FireRed’s C architecture instead uses module-level globals, structs allocated through battle resources, and callback-driven progression. That does not mean it is disorganized. It means the ownership model is different. The discipline is in the phase variables and the wait flags.

Let me compare this briefly to something we have already studied, without reteaching it. In the move relearner material from pokefirered/src/learn_move.c chunk_af3598df6f092723, we saw a menu state machine where the current menu state determines what the UI should do next. That system also advances across frames. But the battle engine is more demanding because several state machines are overlapping: turn scheduling, move scripting, controller communication, sprite updates, text windows, and result accumulation. So if the move relearner is a hallway with numbered doors, battle main is a train station with signals. The point is not complexity for its own sake. The point is coordinated timing.

Now, let’s look at failure modes. This is often the fastest way to understand architecture. Imagine you implement your lab loop with two battlers. You store chosen actions, sort by speed, set a current battler, and execute the move immediately. That is fine for a console simulation. But if you copy that mental model into FireRed, you will misunderstand why the engine has to wait. Suppose move damage is calculated, HP is reduced, and the next battler immediately begins its move in the same logical step. On the screen, the HP bar may still be animating. The text box may still be printing. The attack animation may not have finished. In FireRed’s architecture, the logical engine must not get too far ahead of the player-visible battle. gBattleControllerExecFlags is one of the mechanisms that keeps those layers synchronized.

So when you build the lab version, I want you to deliberately include a placeholder for that wait state. Even if your lab has no real animation, include a “script pointer placeholder” and a “controller busy” flag. Why? Because the architecture lesson is not just sorting by speed. The lesson is that turn order and move execution are interrupted, resumed, and observed. A two-battler loop that runs everything in one function is not structurally similar enough to FireRed. A two-battler loop that says “current script step is damage,” then “controller busy while damage is displayed,” then “resume and increment current turn action” is much closer.

Let’s check understanding with a small scenario. I’ll ask it and then answer it. If battler one is faster and uses a move that knocks out battler two, where should the engine decide that battler two’s scheduled action no longer executes? Is that a sorting problem? No. The original sorting was valid when the choices were made. The invalidation happens during execution progression, after the first action changes battle state. So when the main loop advances to the next ordered action, it must inspect the current battle state, not blindly trust the schedule as if nothing changed. This is one reason the turn-order array is not the whole battle. It is a plan, and the plan is interpreted against a changing battlefield.

That idea will matter later when we discuss fainting, switching, end-turn effects, and forced actions. But even now, it explains why FireRed’s battle code uses shared state so heavily. The next action cannot be decided from the original action list alone. It must consult battler status, HP, ability effects where relevant, side conditions, battle flags, and script outcomes. The script does not merely display the move; it mutates the battlefield that the scheduler must then respect.

Now I want to make one more connection to battle.h as our subsystem map. Last week, when we used pokefirered/include/battle.h as a blueprint, the temptation was to treat the header as a list of declarations. At this point in the course, I want you to read headers more aggressively. A header tells you what must be shared because multiple modules need to agree on it. BattleResources and BattleResults in chunk_825c6c75270bc90c tell you that setup, execution, and teardown all need a common memory story. BattleScripting and the globals in chunk_8367bbcb71bfc88d tell you that script execution is not isolated implementation detail; other parts of the battle engine need to know the scripting contract exists. That is architectural evidence.

Let’s return to the board. Under “execution context,” I’m writing four names: attacker, target, move, script instruction. These four are not equivalent. The attacker and target are battler identities. The move is data plus current interpretation. The script instruction is control flow. A subtle but important bug in student battle engines is mixing those categories. For example, storing “current move” inside the battler as if the battler owns it permanently. But in this engine, the current move is part of transient execution context. The battler may have selected it, but the turn system is currently performing it. That distinction becomes important when moves are redirected, replaced, fail, call other moves, or trigger scripts that examine a different battler from the original chooser.

You can see the same design pressure in the summary screen material we used earlier. In pokefirered/src/pokemon_summary_screen.c chunk_e8e17a42bbf17d46, the summary screen reads party and move data for presentation. It does not own the battle’s transient execution context. So a move shown on the summary screen and gCurrentMove in battle main are related by data identity, but they live in different architectural moments. One is persistent creature data. The other is current battle execution. Advanced architecture work depends on keeping that distinction sharp.

Now, a natural question is: why not make the script engine itself own turn order? Why not let the script say, “after this move, go to the next battler”? Because that would collapse layers that FireRed keeps separate. The script layer is excellent for move procedure: print message, accuracy check, animation, damage, effect, fainting hooks, and so on. The turn scheduler is responsible for whose action is being considered next. The controller layer is responsible for visible communication with battler controllers. The battle main loop coordinates them. If the script owned all of that, the script language would become the entire engine. FireRed does not do that. It uses scripts as a procedural layer inside the larger coordinated state machine.

This is also why we should be careful when comparing FireRed to Pokemon Showdown. Showdown is useful as a secondary comparison for deterministic battle tests and battle data hooks; our course memory from week 10 explicitly says that. But Showdown’s architecture is not our primary authority here. For today’s claim about FireRed’s main callback, chosen actions, turn-order arrays, battle resources, and controller wait flags, the source authority is pokefirered/src/battle_main.c chunks chunk_2017de6d7013d2b7, chunk_568e3366056a9ce4, and chunk_a15739d3170f73c1, plus pokefirered/include/battle.h chunks chunk_825c6c75270bc90c and chunk_8367bbcb71bfc88d. If you bring in Showdown, bring it in later as a contrast in testing and data modeling, not as a replacement mental model for GBA callback coordination.

Let me give you another concrete lab-style example. You have two battlers: Alice and Bruno. Alice chooses Attack. Bruno chooses Attack. Alice has speed 50, Bruno has speed 30. Your gChosenActionByBattler equivalent stores Attack for both battler zero and battler one. Your gBattlerByTurnOrder equivalent stores zero, then one. Your gActionsByTurnOrder equivalent stores Attack, then Attack. Your current turn action number starts at zero. The loop promotes battler zero into current attacker, sets current move to Alice’s chosen move, resolves Bruno as target, and sets script pointer to “start move.”

Now do not immediately subtract HP and jump to Bruno. Instead, step the script. First script step: announce move. That sets controller busy because text must be shown. Next frame, controller clears. Second script step: play animation. Busy again. Later, clear. Third script step: apply damage. Update HP data. Fourth step: animate HP bar. Busy again. Fifth step: check faint. If Bruno fainted, mark battler state accordingly. Then script completes. Only after script completion does current turn action number advance to one. At that point the scheduler asks, “Can battler one still act?” If Bruno fainted, no. If not, Bruno’s action becomes execution context.

That is the miniature version of the real design. It is also the reason your lab should include current battler, current move, and script pointer placeholder separately. If those are one variable, you cannot model the pause-and-resume behavior correctly.

Let’s pause for a check. If I erase gCurrentTurnActionNumber from the board, what breaks? The engine can still know which battler is currently attacking during a move, but it loses its place in the ordered turn list. After the current script finishes, it has no clean way to know whether to process the next scheduled battler or end the turn. If I erase gBattlerAttacker instead, what breaks? The script may know which turn slot it came from, but move procedures now lack a convenient current acting battler. If I erase gBattlescriptCurrInstr, what breaks? The engine can know who is acting, but it cannot resume the procedural sequence at the correct instruction after waiting. So each variable exists because it preserves a different kind of continuity.

That word “continuity” is worth keeping. The main battle callback gives temporal continuity. The turn arrays give ordering continuity. The current attacker, target, and move give execution continuity. The script pointer gives procedural continuity. The controller flags give visual synchronization continuity. Battle resources give memory continuity. Battle results give post-battle continuity. Together, those pieces let FireRed run a battle over many frames while presenting it as one coherent exchange to the player.

And that brings us back to the overall course plan. Since week 1, we have been tracing from data records through globals, callbacks, tasks, scripts, structs, and visible behavior. In the overworld, we did that with maps and object events. In UI screens, we did it with party slots and menu state. In battle setup, we did it with trainer party structs and wild encounter state. This week is the moment where all those methods pay off. Battle main is not a mysterious giant file if you ask the right questions: What state chooses the phase? What data records the player’s decision? What array records order? What globals define current execution? What pointer resumes scripts? What flags wait for presentation? What result structure survives teardown?

So the synthesis for this segment is this: FireRed’s battle main loop is not just a loop over battlers. It is a coordinator of partial progress. Every major piece we named is preserving some form of “where were we?” across frames. That is why the architecture can support animation, text, damage, fainting, switching, and eventual battle results without handing control permanently to any one subsystem. Keep that model in mind, because next we are going to push one level deeper into how end-of-action and end-of-turn progression fit into the same coordinated-state pattern.

All right, so let’s push into that next layer. When I say “end-of-action” and “end-of-turn,” I want you to hear two different boundaries. An action boundary is, “This battler’s scheduled thing is done, so the battle may advance to the next scheduled battler.” A turn boundary is, “All scheduled battlers have had their chance, so now the battle must resolve the shared consequences of the whole turn.” Those are not the same boundary, and FireRed keeps them separate very deliberately.

Look at the board for a moment. I’m going to write three pieces of state in a row: current turn action number, current action function id, and battle main function pointer. In source terms, we are talking about `gCurrentTurnActionNumber`, `gCurrentActionFuncId`, and `gBattleMainFunc`. The important architectural point is that FireRed does not merely say, “execute turn.” It says, “this frame, run the current battle phase; that phase may run the current action function; that action function may install a battle script; that script may wait for controllers; and only later will the current action be marked finished.” That is coordinated state.

Now, recall the runtime shell without re-teaching it. Earlier in the course, when we studied `pokefirered/src/main.c` `chunk_6cfd90f6d0136799`, we saw that the engine has callback slots, and `SetMainCallback2` changes the second main callback. The battle system plugs into that shell. In `pokefirered/src/battle_main.c` `chunk_2017de6d7013d2b7`, battle initialization eventually preserves the previous callback one, assigns `gMain.callback1` to the battle callback, and sets callback two to `BattleMainCB2`. That distinction matters. `BattleMainCB1` runs the battle logic by calling `gBattleMainFunc`, then gives each battler controller a chance to run. `BattleMainCB2`, meanwhile, services presentation: sprites, OAM, text printers, palette fade, and tasks. So if you have been mentally calling `BattleMainCB2` “the battle loop,” refine that. It is the per-frame presentation and task callback. The coordinated battle phase dispatcher is sitting behind callback one.

Let me pause there. Check for understanding: if a move animation is still playing, which side of the architecture is more likely to keep the battle from advancing logically? Not the mere fact that `BattleMainCB2` calls sprite animation. The key gate is state such as controller execution flags and script progression. The animation and text systems are serviced every frame, but the battle logic waits until the relevant controller or script state says, “I am done.” That is the pattern we have seen in menu state machines, but here it is applied to a much denser simulation.

Now let’s take a concrete example. Suppose we are in a simple one-on-one battle. The player chooses Tackle. The opponent chooses Growl. At selection time, the decisions are stored per battler, not in turn order yet. That is `gChosenActionByBattler`, and the move itself is represented separately through chosen move state, including `gChosenMoveByBattler` and the chosen move position inside `gBattleStruct`. Then the battle reaches `SetActionsAndBattlersTurnOrder`, which is in the turn/action flow covered by `pokefirered/src/battle_main.c` `chunk_a15739d3170f73c1`. That routine does something architecturally simple but very important: it converts “what each battler chose” into “what will execute in what order.” The results are `gActionsByTurnOrder` and `gBattlerByTurnOrder`.

On the board, I’ll draw it as two columns. Left column: chosen by battler. Battler zero, use move. Battler one, use move. Right column: ordered schedule. Slot zero, battler one, use move, if the opponent is faster. Slot one, battler zero, use move. Notice that this right column is not a copy of the battle party. It is not a copy of the visible sprites. It is not a copy of the controller order. It is a temporary execution schedule for the turn. That separation is why the engine can handle priority-like action categories, switching, item use, run attempts, Safari actions, and speed ordering without rewriting the meaning of the battler IDs themselves.

And this is also why the lab this week asks you to implement a two-battler turn-order loop with chosen actions, speed ordering, current battler, current move, and a script pointer placeholder. I am not asking you to build damage calculation yet. I am asking you to reproduce the architectural joint. Chosen actions are declarative: “I want to do this.” Turn order is scheduled: “This is when it happens.” Current battler and current move are execution context: “This is the action currently being interpreted.” The script pointer is resumable presentation and effect flow: “This is where the action’s detailed sequence continues.”

Now, after the schedule exists, FireRed does not immediately run every action in one call. It installs the next phase. In the real code, after ordering, it moves through the pre-turn cleanup and Focus Punch-related checks, then sets `gCurrentTurnActionNumber` to zero and loads `gCurrentActionFuncId` from `gActionsByTurnOrder[0]`. Then `gBattleMainFunc` becomes `RunTurnActionsFunctions`. That handoff is one of the cleanest places to see coordinated state. The phase pointer says, “We are now executing scheduled actions.” The turn action number says, “We are at schedule slot zero.” The action function id says, “The action in this slot is use move, switch, item, run, script, try finish, or finished.”

The action function table itself is a data table of behavior. In `pokefirered/src/battle_main.c` `chunk_568e3366056a9ce4`, the action IDs from `battle.h` correspond to handlers like `HandleAction_UseMove`, `HandleAction_UseItem`, `HandleAction_Switch`, `HandleAction_Run`, and `HandleAction_ActionFinished`. That is a pattern you should recognize from earlier course work: numeric or enum-like state selects a function table entry, and the entry mutates shared state so the next frame resumes in the right place. We saw a lighter version of that in the move relearner state machine in `pokefirered/src/learn_move.c` `chunk_af3598df6f092723`, but battle is more compositional. A menu state machine mostly coordinates windows and selection. Battle coordinates simulation, UI, animation, controller communication, scripts, results, and teardown.

Now let’s walk the Tackle example through `HandleAction_UseMove`. The handler first identifies the attacker from the schedule: `gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]`. This is subtle. It does not say, “active battler equals zero,” and it does not trust the original choice array. It reads the ordered schedule. Then it sets up move execution context: critical multiplier, damage multiplier, attack cancellation tracker, move result flags, multi-hit counter, miss type communication, current move position, chosen move position. Then it resolves the actual move. Maybe there are no valid moves and the move becomes Struggle. Maybe the battler is locked into a multi-turn move. Maybe Encore overrides the selected slot. Maybe the selected move was overwritten. Only after those constraints are handled does `gCurrentMove` become the move that the engine is actually about to interpret.

That distinction between chosen move and current move is a professional-level detail. Beginners often collapse them. They write, “player picked move X, therefore execute X.” FireRed knows better. The player’s input is only one claim on reality. The battle state may override it. The current move is the resolved execution fact. This is exactly why `pokefirered/include/battle.h` `chunk_8367bbcb71bfc88d` exposes globals such as `gCurrentMove`, `gBattlerAttacker`, `gBattlerTarget`, `gBattlerByTurnOrder`, `gActionsByTurnOrder`, and `gChosenActionByBattler`. The header is not just a bag of extern declarations. It is a map of which pieces of battle state are shared contracts across the battle subsystem.

Then targeting is resolved. The chosen target may not remain the final target. In double battles, Follow Me can redirect. Lightning Rod can redirect Electric-type moves. Random-target moves may choose among opposing positions. Absent battlers may force a flank correction. So again, the current execution context is not a naive copy of input. It is computed from current field state, side timers, move metadata, abilities, absent flags, positions, and battle type flags. Once the move and target are resolved, the action handler sets `gBattlescriptCurrInstr` to the script for the move effect and changes `gCurrentActionFuncId` to `B_ACTION_EXEC_SCRIPT`.

That last step is the hinge. The use-move handler does not itself print every message, animate every hit, subtract every HP value, process every status, and check every faint in one ordinary C call. It prepares the battle script interpreter. The battle script is then advanced by `RunBattleScriptCommands` or the callback-stack variant, depending on the context. So the action handler is not the whole action. It is the transition from scheduled action into script-driven action execution.

Let me ask the room: why is that useful? Think about a move like Tackle versus a move like a multi-hit move, or a move that causes a stat drop, or a move that faints the target, or a move that triggers ability text. You do not want the turn scheduler to know all those cases. The scheduler should know order. The action handler should know how to prepare the selected action. The battle script interpreter should know how to walk the detailed effect sequence. And controller state should know when the player-facing presentation has completed. That division is how the file remains large but not formless.

Now, action completion. There is a handler called `HandleAction_ActionFinished`. Conceptually, it is the “advance to next schedule slot” operation. It increments `gCurrentTurnActionNumber`, loads `gCurrentActionFuncId` from `gActionsByTurnOrder[gCurrentTurnActionNumber]`, clears special statuses, clears a bundle of hit marker flags, resets `gCurrentMove` to none, clears damage and result flags, resets battle scripting animation counters, clears dynamic move type and dynamic base power, resets `moveendState`, clears communication bytes, and empties the battle scripts stack size. This is not arbitrary cleanup. Each field it clears is a piece of per-action residue that would corrupt the next battler’s action if allowed to leak.

Here is the practical debugging lesson. If you build your lab loop and your second battler mysteriously inherits the first battler’s move, target, or “already printed attack string” state, you have probably failed to model an action boundary. In FireRed, the boundary is explicit. It is not just “increment index.” It is “increment index and clear the state that belongs only to the previous action.” This is one of those places where an advanced learner should become less impressed by the raw size of `battle_main.c` and more attentive to the lifecycle of each variable.

Now when `RunTurnActionsFunctions` sees that `gCurrentTurnActionNumber` has reached `gBattlersCount`, the action boundary becomes a turn boundary. The code clears passive damage from the hit marker and sets `gBattleMainFunc` from `sEndTurnFuncsTable[gBattleOutcome & 0x7F]`. That table is also in `pokefirered/src/battle_main.c` `chunk_568e3366056a9ce4`. If the outcome is zero, meaning no win, loss, run, capture, or similar terminal outcome has been established, the table selects `HandleEndTurn_ContinueBattle`. If the outcome indicates win, loss, ran, mon fled, caught, and so on, it selects the corresponding end-turn handler. So the same architectural trick appears again: state value selects a function pointer from a table, and the chosen function continues the frame-by-frame battle process.

Now, what happens in the non-terminal case? `HandleEndTurn_ContinueBattle` does not immediately ask for the next player input. It waits for controller execution flags to be clear. When it can proceed, it sets `gBattleMainFunc` to `BattleTurnPassed`, clears communication state, removes flinch flags, cancels multi-turn moves if sleep interrupts them, and resets trackers for turn effects, Wish and Perish Song, turn counters, and move result flags. Then `BattleTurnPassed` performs the end-of-turn pipeline: field end-turn effects, battler end-turn effects, fainted mon actions, Wish and Perish Song handling, cleanup, outcome check, turn counter increment, chosen action reset, chosen move reset, switch target reset, absent battler snapshot, and finally it returns to `HandleTurnActionSelectionState`.

Say that back in simpler language: after all battlers act, FireRed does not say, “next turn.” It says, “resolve shared consequences until nothing else needs to interrupt, and only then ask for new actions.” That is the key sophistication of this segment.

This is where `BattleResults` becomes relevant too. In `pokefirered/include/battle.h` `chunk_825c6c75270bc90c`, `struct BattleResults` records counters and facts that are meant to survive the flow of individual actions: faint counters, switch counter, healing item count, revive count, whether the player’s mon was damaged, whether the Master Ball was used, caught species, last opponent species, last used moves, catch attempts, and so on. During battle startup in `pokefirered/src/battle_main.c` `chunk_2017de6d7013d2b7`, those fields are initialized. During action execution, some are updated, such as last used move for player or opponent, or player switch count. During finish handling, more result facts may be finalized. This is not battle logic in the narrow “damage formula” sense; it is the persistence interface between battle and the rest of the game.

Connect that to prior weeks. When we studied party slot ownership and summary screen reads, especially around `pokefirered/src/pokemon_summary_screen.c` `chunk_e8e17a42bbf17d46`, we cared about which screen was allowed to read or mutate which party record. Battle raises the stakes. The battle engine has its own `gBattleMons` runtime copies, its own battler-to-party indexes, its own current attacker and target, and its own results structure for information that must be returned to the wider game. If you mutate the party directly at the wrong time, you bypass battle’s coordinated state. If you keep everything only in battle state and never write back results, the overworld and party systems never learn what happened. This is why week 13 sits exactly where it does in the course plan. You already know callbacks, tasks, data tables, party records, menu state machines, trainer party setup, and `battle.h` as a subsystem map. Now you are seeing the battle engine as the place where all of those patterns are under maximum pressure.

One more check for understanding before we close this segment. Suppose a battler uses an item. Does the engine need speed ordering for that item in the same way it needs speed ordering for two ordinary moves? In FireRed’s ordering routine, item use and switching are placed before the ordinary move actions in the schedule, and then the speed comparison is applied only where appropriate among the remaining action types. That means `gActionsByTurnOrder` is not merely “sort by speed.” It is “encode the battle rules’ ordering categories, then sort the parts that speed is allowed to decide.” If your lab implementation only sorts every action by speed, it may pass a toy move-versus-move test, but architecturally it will not match the kind of scheduler FireRed needs.

So the synthesis for this part is this: end-of-action and end-of-turn are two separate reset and routing layers. End-of-action advances through `gActionsByTurnOrder` and `gBattlerByTurnOrder`, clears per-action residue, and prepares the next scheduled battler. End-of-turn routes through `sEndTurnFuncsTable`, resolves field-wide and battler-wide consequences, updates durable battle results, clears turn-level choices, and returns the battle to action selection only when all interruptions have settled. Keep that separation in your head, because next we can use it to reason about battler globals and controller-facing state without treating the many globals as a random pile.

All right, so now let’s put a frame around the globals. The mistake I want you to avoid is looking at `battle_main.c` and saying, “FireRed has a lot of globals, therefore the battle engine is just global spaghetti.” That is too shallow. It has many globals, yes, but many of them are serving distinct time horizons. Some remember the whole battle. Some remember the current turn. Some remember the currently executing action. Some are not really game rules at all; they are synchronization state for the controllers, sprites, health boxes, palettes, and script interpreter.

So, on the board, I’m going to draw four horizontal bands. The top band is battle lifetime state. The second band is turn state. The third band is action state. The fourth band is presentation and controller state. And I want you to imagine the main battle callback sweeping over these bands every frame, not by recomputing the entire battle, but by asking, “Which state machine currently owns progress?” That is the key phrase: ownership of progress.

The source packet points us directly to this shape. `pokefirered/src/battle_main.c`, especially `chunk_2017de6d7013d2b7`, is our anchor for the battle main flow. The later `battle_main.c` chunks, `chunk_568e3366056a9ce4` for battle state progression and `chunk_a15739d3170f73c1` for turn and action flow, show why this cannot be understood as one flat loop. And then `pokefirered/include/battle.h` gives us the cross-module contract: `chunk_825c6c75270bc90c` for `BattleResources` and `BattleResults`, and `chunk_8367bbcb71bfc88d` for `BattleScripting` and the related globals. Notice the division already: resources, results, scripting, globals. Those are not random labels. They are architectural pressure points.

Let’s start with battle lifetime state. This is the state that exists because the battle as a whole exists. `gBattleTypeFlags` is a good example. Whether this is a wild battle, trainer battle, link battle, double battle, Safari battle, tutorial-style battle, and so on, changes what branches are legal throughout the entire encounter. That is not a per-move variable. It is not something you clear after Tackle resolves. It belongs to the whole battle session. In the same family, you should think about `gBattleOutcome`, `gBattleResults`, and the heap-allocated or resource-style structures grouped through `gBattleResources`. `BattleResults`, as exposed in `pokefirered/include/battle.h` `chunk_825c6c75270bc90c`, is important because it is not just a visual report. It is the bridge from battle back into the larger RPG. Did the player win? Did the player lose? Was a Pokémon caught? How much money changes hands? What fainting or battle result data must be remembered outside this frame?

This connects directly to week 12, but we are not going to re-teach battle setup. You already saw that starting a battle from world state means converting overworld context, trainer data, party data, and flags into battle initialization. Today we care about what happens after that conversion. Once the battle has been born, `BattleResources` and shared battle globals give the engine a place to hold the live version of that encounter. That is the top band.

Now the second band: turn state. This is where `gChosenActionByBattler`, `gActionsByTurnOrder`, and `gBattlerByTurnOrder` belong. These variables exist because a turn is not a single action. A turn is a batch of chosen intentions that must be validated, ordered, interrupted, partially skipped, and consumed. The player may choose Fight. The opponent AI may choose a move. In a double battle, two player-side battlers and two opponent-side battlers may all have selected actions. But selection order is not execution order. And execution order is not simply input order. That is why FireRed keeps chosen actions by battler separately from battlers by turn order.

Pause on that, because it is one of the most important design lessons of this week. “What did battler 0 choose?” and “Who acts first?” are different questions. In a clean implementation, they deserve different storage. If you use one array to mean both, your lab solution will become brittle immediately when you add switching, item use, move priority, fainting, charging moves, or skipped actions.

Here is a concrete example. Suppose battler 0 is the player’s active Pokémon and battler 1 is the opponent’s active Pokémon. The player chooses a normal attack. The opponent chooses a faster normal attack. In the selection band, you might have `gChosenActionByBattler[0]` recording that the player chose to use a move, and `gChosenActionByBattler[1]` recording that the opponent chose to use a move. The chosen move array, or the equivalent move-selection storage in the action flow, remembers which move each battler intended. But after ordering, `gBattlerByTurnOrder[0]` may be battler 1, and `gBattlerByTurnOrder[1]` may be battler 0. The chosen actions still belong to the battlers. The turn-order array belongs to the scheduler.

Check for understanding here. If battler 1 is faster, do we rewrite battler 0’s chosen action into slot 1 and battler 1’s chosen action into slot 0? No. Not conceptually. We build an execution ordering that points at battlers. That difference is exactly what gives the engine room to ask, “Whose action am I consuming now?” without losing the original per-battler intent.

Now we move to the third band, action state. This is where the globals feel especially dense: `gCurrentTurnActionNumber`, `gCurrentActionFuncId`, `gBattlerAttacker`, `gBattlerTarget`, `gCurrentMove`, `gChosenMove`, `gBattleMoveDamage`, `gMoveResultFlags`, and `gBattlescriptCurrInstr`. Some of these names vary in how they are used across branches, but architecturally they are all part of the same idea. The battle engine takes a scheduled action and lowers it into an executable working set.

That word “lowers” is useful. At the turn level, the engine says, “Battler 1 acts before battler 0.” At the action level, it says, “The current attacker is battler 1, the target is battler 0, the move being processed is this move ID, the current script pointer is here, and the damage/result flags are currently these.” The action layer is more concrete. It is not planning anymore. It is executing.

This is also where `BattleScripting`, from `pokefirered/include/battle.h` `chunk_8367bbcb71bfc88d`, becomes important. The script layer needs scratch space that persists across multiple script commands but should not necessarily become durable battle result state. For example, a battle script may need to remember a battler involved in an ability message, a temporary damage value, a stat-change encoding, a multi-hit counter, or some effect-specific scratch value. That is not the same kind of state as “the battle is a trainer battle,” and it is not the same kind of state as “the opponent chose Fight this turn.” It lives in the action-and-script band.

Let me say that in a way that should help you in code review. If you are adding a variable to your lab battle loop, ask which band it belongs to. Is it battle lifetime state? Then it should survive turns. Is it turn state? Then it should be cleared when the turn is fully over, not when one move finishes. Is it action state? Then it should be overwritten or cleared as the scheduler advances to the next battler. Is it presentation synchronization state? Then it should not be used as the source of truth for the battle rules.

That last category is the fourth band: controller-facing and sprite-facing state. FireRed’s battle engine is not a pure simulator that prints text to standard output. It is running on hardware where OAM, VRAM, palettes, windows, text printers, sound effects, health boxes, battler sprites, and link or controller buffers must advance over frames. So the battle main callback cannot just call one function and finish the turn instantly. It must often issue a command, wait for the relevant controller or animation path to finish, and only then resume rule execution.

This is why the main battle callback from `pokefirered/src/battle_main.c` `chunk_2017de6d7013d2b7` should remind you of our earlier callback and task discussions from week 1 and week 2, but at a higher density. In the overworld, we saw a main callback call task runners and sprite updates. In battle, the same general runtime shell idea applies, but there is a much richer internal contract. Battle logic, battle scripts, controller buffers, and visual state all need to cooperate. The main callback is not “the function that does all battle rules.” It is the per-frame coordinator that lets the current battle sub-state make progress when its dependencies are ready.

Now, let’s do board work with a miniature trace. I’ll write: frame N, current action number zero. The turn-order array says the first acting battler is battler 1. The action dispatcher reads `gBattlerByTurnOrder[gCurrentTurnActionNumber]`, sets the current attacker to battler 1, retrieves the chosen move for battler 1, determines or validates the target, and sets up `gCurrentMove` and the battle script pointer. The script starts. Maybe it first displays a “used move” message. That message is not just a string in a terminal. It goes through the battle text and controller path. The script may have to wait. So the battle main callback returns to the engine for the frame. On the next frame, it asks whether the controller work is still executing. If yes, it waits. If no, the script pointer advances. Then the animation may start. Wait again. Then damage may be calculated. Then HP bar animation. Wait again. Then faint check. Then the action ends.

The advanced point is that this waiting is not an implementation inconvenience. It shapes the architecture. Because FireRed must interleave rule execution with visible and audible effects, a “current move” cannot just be a local variable inside one deep call stack that disappears instantly. The current move, current attacker, current target, script pointer, and controller execution flags need to remain available across frames. That is why the globals are doing real work.

Here is the check: if `gBattlescriptCurrInstr` is pointing into a script that is waiting for an HP bar animation, should the battle engine advance `gCurrentTurnActionNumber` immediately? No. The current action is not done. The script is suspended, not complete. Advancing the turn-order index early would make the next battler start while the previous action’s visual and rule consequences are still unresolved. That would be a state ownership bug.

Now compare that to the lab you are building this week. Your lab says: implement a two-battler turn-order loop with chosen actions, speed ordering, current battler, current move, and a script pointer placeholder. The phrase “script pointer placeholder” is doing a lot of architectural work. I do not need your lab to implement FireRed’s full battle script VM. I do need your lab to separate “selected action” from “scheduled actor” from “currently executing action.” If your placeholder is just a function call that immediately returns, that is okay for the first pass. But design it as if it could suspend. For example, your action executor should be able to say, “I am still running,” “I finished,” or “I was cancelled.” That matches the shape of FireRed more closely than a single monolithic `resolveTurn()` that calculates everything in one breath.

Let’s make that concrete. Suppose your lab has two battlers: Embermon and Shellcub. Embermon has speed 40 and chooses Scratch. Shellcub has speed 55 and chooses Water Tap. During choice collection, store actions by battler ID: battler 0 chose Scratch, battler 1 chose Water Tap. During ordering, build turn order: slot 0 is battler 1, slot 1 is battler 0. During execution, set current battler to battler 1 and current move to Water Tap. Your script pointer placeholder might point to `SCRIPT_USE_MOVE_START`. It steps to `SCRIPT_PRINT_USED_MOVE`, then `SCRIPT_APPLY_DAMAGE`, then `SCRIPT_ANIMATE_HP`, then `SCRIPT_END_ACTION`. Only when it reaches end action do you increment the current turn action number. Then battler 0 gets its chance, unless it fainted, flinched, or otherwise lost its action.

That “unless” is where architecture earns its keep. If Shellcub knocks out Embermon, your scheduler should not blindly execute Embermon’s previously selected Scratch. The turn-order list can still contain battler 0, but the action dispatcher must validate whether the scheduled action is still legal at execution time. This is another reason selected actions and executable actions are separate. A choice can become invalid after it is chosen.

You have seen this pattern before in another domain. In the move relearner menu, from `pokefirered/src/learn_move.c` `chunk_af3598df6f092723`, the state machine did not assume every earlier menu decision remained valid forever. It moved through menu states, confirmations, replacement flows, and party data mutation. We are not re-teaching that menu today, but carry the principle forward: a player-facing choice becomes real only when the owning state machine reaches the mutation point. Battle is the same idea under much more pressure.

And the summary screen example from `pokefirered/src/pokemon_summary_screen.c` `chunk_e8e17a42bbf17d46` gives us the complementary idea. The summary screen reads creature and move data for display, but it is not the owner of battle execution. In battle, by contrast, the current action layer owns temporary rule execution. So if you see a move ID on a summary screen and a move ID in `gCurrentMove`, do not treat those as equivalent just because they are both move IDs. One is display-facing party inspection. The other is a live executable battle fact.

Let’s now connect this to constants and tables. The action IDs, battler positions, battle type flags, move IDs, and script command conventions are not magic numbers floating in the code. They are part of the header and constants ecosystem that lets modules agree. Last week, when we used `battle.h` as a subsystem map, we were preparing for exactly this moment. `pokefirered/include/battle.h` `chunk_8367bbcb71bfc88d` is valuable not because it contains every implementation detail, but because it tells other files what shared battle state exists and what shape that state has. Meanwhile, `battle_main.c` uses tables like the end-turn function table and action dispatch mechanisms to transform those declarations into behavior. So the architecture is: constants name legal states, structs group related memory, globals expose shared current state, tables encode dispatch order, and callbacks advance everything frame by frame.

Now, I want to give you a mental test for reading `battle_main.c`. When you find a global, do not ask first, “Is global state good or bad?” That question is too generic for this course. Ask instead, “Who writes it, who reads it, and over what duration is it valid?” Take `gBattlerAttacker`. It is meaningful during action execution. If some later UI code reads it after the battle has moved on, that would be suspicious. Take `gBattleResults`. It is meaningful across the battle and after the battle returns results to the outer game. Take `gChosenActionByBattler`. It is meaningful after action selection and before the turn has been fully consumed. Take controller execution flags and buffers. They are meaningful while coordinating per-frame presentation and should not become your authoritative rule model.

This is also where advanced students sometimes overcorrect. They say, “I would replace all of this with a pure immutable battle state and event queue.” In a modern simulator, maybe. Pokemon Showdown, which we have used only as a secondary comparison in prior weeks, leans much more naturally into simulator-style testing and data hooks; the research packet specifically flagged Showdown test and data modules as useful comparison material, such as `pokemon-showdown/test/common.js` and move data under `pokemon-showdown/data`. But FireRed is not trying to be a server-side battle simulator. It is a GBA game engine integrating rules, animation, memory limits, link behavior, UI, and save-facing results. So the better advanced exercise is not to declare FireRed impure. The better exercise is to identify which impurity serves which runtime obligation.

Let me ask a check-for-understanding question in that spirit. If a move animation is halfway finished, where should the truth live that the move being animated is the current move? In FireRed’s architecture, it cannot live only on the animation object, because the battle script and rule layer still need to resume around that move. It also cannot live only in the original chosen-action array, because the action has been lowered into current execution and may have changed through effects, targeting, or move substitution. The truth lives in the action-layer globals and script state, while the animation and controller layer mirrors or consumes it for presentation.

Now let’s talk about `gActiveBattler`, because it is a common source of confusion. A name like active battler sounds like it should mean “the battler whose turn it is.” Sometimes it is related to that, but in controller code it often means the battler whose controller buffer or command is currently being addressed. That may be the attacker, the target, a partner, or a battler whose sprite or health box is being updated. So do not collapse `gActiveBattler`, `gBattlerAttacker`, `gBattlerTarget`, and `gBattlerByTurnOrder[gCurrentTurnActionNumber]` into one concept. In simple moments they may hold the same number. Architecturally they answer different questions.

On the board, I’ll write four questions. Who is scheduled? Who is attacking? Who is targeted? Who is receiving a controller command? In a simple one-on-one damage move, the answers may be opponent, opponent, player, player or opponent depending on the command. In a double battle spread move, the answers diverge even more. In a switch, item, faint, or ability activation path, they diverge again. This is why FireRed uses several battler globals. They encode role, not just identity.

And that brings us to the controller-facing state. In a turn-based RPG, it is tempting to treat visuals as an output after rules. But in FireRed, visuals are interleaved with rules through scripts and controller commands. A message can precede damage. An animation can precede the HP reduction. A faint animation can trigger a replacement flow. Replacement flow can route back to selection, party menu, trainer AI, or battle end checks. So presentation is not merely decoration. It is part of the coordinated state progression, though it should not be confused with the source of battle legality.

This is why `battle_main.c` `chunk_568e3366056a9ce4` matters for today’s lecture. State progression in the battle engine is not just “run next function.” It is “run the next function that is legally able to progress, given current script state, controller completion, battler status, turn order, and battle outcome.” When you look at the main flow chunk and the turn/action chunk together, `chunk_2017de6d7013d2b7` and `chunk_a15739d3170f73c1`, the pattern is coordinated state rather than a single procedural story.

Let’s do one final miniature trace, and then I’ll close this segment. Player chooses a move. Opponent chooses a move. Chosen actions are stored by battler. The order table is built by category and speed. The current turn action number starts at zero. The first scheduled battler is copied into the current action context. The move and target are established. The battle script pointer is initialized. The script emits text and animation commands through controller-facing state. The main callback waits across frames while those commands complete. Damage and result flags are applied. Fainting, ability effects, held item effects, or other interruptions may insert more script work. Only after the action is truly complete does the scheduler advance. End-of-turn logic is still later; it is not the same reset boundary.

So, for your lab, the bar is now higher than “sort by speed.” I want to see the state bands. Store choices by battler. Build execution order separately. Track current action separately. Include a script pointer placeholder that can pause. Validate the action at execution time. Clear per-action scratch at action end, and clear per-turn choice state only at turn end. If you do that, your toy two-battler loop will have the same architectural skeleton as the FireRed battle main loop, even though it does not yet have FireRed’s full move rules, animation system, controller buffers, or battle script language.

The synthesis for this segment is this: FireRed’s battle globals are not a pile; they are a set of shared registers for cooperating state machines. `BattleResources` and `BattleResults` hold battle-lifetime context and consequences, as shown in `pokefirered/include/battle.h` `chunk_825c6c75270bc90c`. `BattleScripting` and the battle globals in `chunk_8367bbcb71bfc88d` support script-level and action-level execution. `battle_main.c` then coordinates those layers through main flow, progression, and turn/action dispatch, especially in `chunk_2017de6d7013d2b7`, `chunk_568e3366056a9ce4`, and `chunk_a15739d3170f73c1`. Keep those layers separate in your head, because next we can look more closely at how action choice, battler identity, and move execution become visible behavior without pretending that one variable named “current battler” could possibly mean everything.

So now let’s separate three phrases that are easy to blur together: selected action, acting battler, and target of the action. I’m going to say that again because many battle-engine bugs come from collapsing those three into one idea. Selected action is what a battler has decided to do. Acting battler is whose action is currently being executed. Target is the battler or slot that action is being applied to. In a simple battle these often line up in your intuition, so you do not notice the distinction. In a real battle engine, especially one with switching, fainting, priority, redirection, multi-target moves, controller delays, and animations, they cannot be treated as the same thing.

Imagine a two-battler lab version. Player chooses Tackle. Opponent chooses Growl. You store those choices in something like `chosenAction[0] = ACTION_USE_MOVE` and `chosenAction[1] = ACTION_USE_MOVE`. Then you sort a turn order array by speed, maybe `turnOrder[0] = 1`, `turnOrder[1] = 0` if the opponent is faster. Now when the turn executes, the current battler is not “the player because the player chose first.” The current battler is `turnOrder[turnIndex]`. That one extra indirection is the architectural idea. Choice is collected in battler-index space. Execution is performed in sorted-turn space. Effects may apply in target space. FireRed scales that idea across a much larger set of globals and controller-facing structures.

Let me put that on the board as a tiny model. Left column: battler identity, zero through three in the full engine, zero through one in your lab. Middle column: chosen action per battler. Right column: execution order, which is not the same array. Then below that I’ll write current battler, current move, current target, and script pointer. Those four are not the whole battle engine, but they are the minimum registers you need to explain why a turn can be resumed frame after frame instead of computed in one hidden function call.

That is the connection to `pokefirered/src/battle_main.c`. The source focus for today identifies three chunks: `chunk_2017de6d7013d2b7` for battle main flow, `chunk_568e3366056a9ce4` for battle state progression, and `chunk_a15739d3170f73c1` for turn and action flow. You should hear those as three layers of coordination. One layer says, “Are we in the battle callback, and what phase of battle are we in?” Another says, “Which state inside that phase advances this frame?” The third says, “Which battler action is being resolved, and what action-specific machinery should run?” If you read those chunks looking for one grand function called “do battle,” you will miss the architecture. The architecture is the handoff between state variables.

Now, a check for understanding. If a battler has chosen to use a move, but it faints before its position in the turn-order array arrives, what should the engine trust: the old chosen action, or the current battler state at execution time? Right, it must check current state at execution time. The action choice is a proposal captured earlier. It is not a guarantee that the action will still be legal. This is why the battle loop needs more than `chosenAction`. It needs battler flags, HP state, script state, move validity, and result tracking. The chosen action tells the engine where to begin; it does not exempt the engine from validating the world.

That same distinction is why `BattleResources` and `BattleResults`, from `pokefirered/include/battle.h` `chunk_825c6c75270bc90c`, matter as more than storage. `BattleResources` is part of the battle-lifetime context: the engine needs places to keep data that outlives one animation frame and one function call. `BattleResults` is the consequences side: what the battle will report back to the surrounding game after the battle shell is finished. And this is where week 13 connects back to the whole course. In week 1, we talked about the runtime shell and callbacks. In week 2, we talked about generated data and headers as contracts. In the overworld weeks, we saw map and script state cooperate. In the party weeks, we saw party slot ownership and summary-screen reads. Now battle brings all of those ideas together under higher pressure. It is a dense runtime shell, driven by tables and globals, exposed through callbacks, tasks, controllers, scripts, sprites, and persistent results.

Notice the phrase “report back to the surrounding game.” The battle does not exist as a detached simulator. When it ends, the overworld, party data, trainer flags, money, experience, captured species, and other consequences have to be reconciled. That is why `BattleResults` is so important architecturally. It is not just “who won?” It is the boundary object between a highly specialized battle state machine and the rest of the RPG. Earlier, when we studied party screens, the summary screen could read from party data without owning the whole world. Here, battle does mutate and accumulate consequences, but it still has to package those consequences in a form the larger game can understand.

Now let’s talk about `BattleScripting`, because this is where many advanced students initially go too abstract. In `pokefirered/include/battle.h` `chunk_8367bbcb71bfc88d`, the research packet flags `BattleScripting` and battle globals as a source for script-level and action-level execution. You should think of `BattleScripting` as a set of registers for the battle script interpreter and effect resolution layer. It is not the same thing as the high-level main callback. It is also not the same thing as the party model. It sits in the middle, where “use move” turns into “show message, calculate effect, apply damage or status, wait for animation, continue script.”

Here is a concrete example. Suppose battler zero uses Ember on battler one. At the lab level, you might do this in one function: print “Ember,” subtract HP, maybe roll burn, then finish. FireRed cannot afford to treat that as one invisible step. The move may have an animation. The controller for a battler may need to send or receive commands. The sprite may need to face the correct direction. The message box may need to print text and wait. Damage calculation may set temporary values. The result may trigger fainting logic. Fainting may trigger experience gain, replacement selection, or battle end. So the engine needs a script pointer or script-like progression variable that says, “We are here, inside the current effect sequence.” That is why a script-level structure exists beside action-level variables.

Let’s pause and test that. If I ask, “Where is the current move stored?” and you answer with only one variable name, that is not yet the mature answer. The mature answer is, “It depends which layer is asking.” There may be a selected move as part of a battler’s chosen input, a current move being executed, move data looked up from move tables, temporary move effects used by scripting, and animation or controller state that reflects that move. The architectural point is not to memorize one global. The point is to track which layer owns which meaning at which moment.

This is exactly the skill we assessed in the week 12 quiz and midterm: trace from data records through globals, callbacks, tasks, scripts, structs, and concrete source functions. Battle is where that tracing becomes non-negotiable. In a menu, if you lose track of one state variable, the cursor may behave strangely. In battle, if you lose track of one state variable, the wrong battler attacks, the wrong target faints, the wrong move animation plays, or the wrong result returns to the overworld. That is why today’s lecture is not just about `battle_main.c`; it is about coordinated state as a design discipline.

Now I want to bring in a small comparison without letting it take over. The research packet reminds us that Pokemon Showdown sources, such as `pokemon-showdown/data/mods/gen2stadium2/scripts.ts` `chunk_3480e574205d5d0f`, are useful as a secondary comparison for deterministic move and damage logic. That excerpt shows modern simulator code asking questions like whether a target has immunity, whether a move is an OHKO move, whether damage comes from a callback, level, fixed damage, or computed base power. That is valuable for comparison because it makes move-resolution logic explicit in a testable simulator style. But FireRed remains our architecture authority. FireRed is not just computing a turn outcome; it is also coordinating a Game Boy Advance frame loop, controllers, sprites, audio, text, battle scripts, and persistent game state. So when you borrow ideas from Showdown for tests, keep the boundary clear. Showdown is excellent for battle-rule modeling and regression tests. FireRed is the example of an integrated handheld RPG engine.

Let’s return to the turn-order loop. In the lab, I want you to implement a two-battler version with chosen actions, speed ordering, current battler, current move, and a script pointer placeholder. The placeholder matters. Do not just call `resolveMove()` and finish everything in one blocking call. Instead, give yourself a tiny state like `SCRIPT_START`, `SCRIPT_PRINT_MOVE`, `SCRIPT_APPLY_EFFECT`, `SCRIPT_WAIT`, `SCRIPT_DONE`. Even if those states are artificial for now, they train your architecture in the same shape as the real engine. You are not trying to clone FireRed’s entire battle script language this week. You are trying to preserve the distinction between action dispatch and stepwise effect resolution.

Suppose your lab battle starts with Bulbasaur versus Charmander. Bulbasaur speed 45, Charmander speed 65. Both choose moves. Your chosen action array says both selected `ACTION_USE_MOVE`. Your chosen move array says battler zero selected Vine Whip, battler one selected Ember. Your speed-sort pass produces turn order one, then zero. You set current battler to one. Current move becomes Ember. Current target becomes zero. Script pointer becomes the start of a simple move script. The script prints the move line, waits a frame or several frames, applies damage, checks whether the target fainted, and then returns `SCRIPT_DONE`. Only after that do you advance the turn index to the next entry in the turn-order array. If Bulbasaur fainted, the next action is skipped or redirected into faint/replacement logic depending on your lab scope. See how this already creates the skeleton of a real battle engine?

Now, let’s be precise about why this is better than writing one big `doTurn()` function. It gives every frame a coherent answer to the question, “What am I currently doing?” That answer may be “waiting for action input,” “sorting turn order,” “executing battler one’s move,” “inside the damage step of the move script,” or “checking battle results.” Once you can answer that question with explicit state, debugging becomes possible. Saving and restoring temporary behavior becomes possible. Delaying for animations becomes possible. Separating UI display from rule resolution becomes possible. And most importantly, future features have somewhere to attach.

This is also where sprite-facing and controller state enter the picture. The battle engine has to know more than rules. It has to know what each battler’s visual representation is doing, what command has been sent to a controller, whether the controller has acknowledged or finished, and whether the main loop can advance. Earlier in the course, when we studied `pokefirered/src/main.c` and the callback shell, we emphasized that the game advances by returning to a frame loop. Battle obeys that same reality. It cannot freeze the machine while one function narrates an entire turn internally. It coordinates visible behavior over time.

So, if I point to the board and circle “current battler,” I do not want you to say, “That is the battler whose sprite is moving.” Sometimes yes, but not always. It might be the battler whose action is being dispatched. A controller command may be in flight to another battler. A target sprite may be animating. A script may be applying an effect to a target while the attacker remains the logical source. This is the reason the real engine needs shared globals and structured resources. They are not elegant in a modern object-oriented sense, but they are functional in the environment they were designed for. The GBA codebase uses global registers, arrays, constants, and state machines because the whole engine is organized around explicit procedural coordination.

Let’s do another check for understanding. In your lab, if you store only `currentBattler` and `currentMove`, but not `currentTarget`, what bug will appear first? Usually the first bug is that you assume the target from the selection moment is always valid. That breaks when a target faints, when a move targets self, when a move targets the opposing side, or when targeting rules vary by action. Even in a two-battler lab, you can fake it for a while. But as soon as you add switching or fainting, your architecture needs an explicit target concept. FireRed’s full system has many more target-related details, but the beginner version of the lesson is simple: do not hide target identity inside the move function if the rest of the engine needs to reason about it.

Now, let me connect this to files and constants. We are not listing every battle constant today, but the include file matters because it is the cross-module contract. `pokefirered/include/battle.h` appears in other modules too; the research packet’s excerpts from `pokefirered/src/daycare.c` `chunk_119931105f546346`, `pokefirered/src/learn_move.c` `chunk_af3598df6f092723`, and `pokefirered/src/pokemon_summary_screen.c` `chunk_e8e17a42bbf17d46` show battle-related headers and constants appearing outside the battle loop itself. I am not using those files to re-teach daycare, move relearning, or the summary screen. We already did that. I am pointing out the architectural fact: battle is not isolated. Other game systems need battle vocabulary, move constants, party data, and transitions into or out of battle-adjacent UI. The include boundary tells us what concepts are shared broadly enough to become part of the project’s language.

That is why advanced architecture reading starts with headers, then follows state. If you open an implementation file first, you see behavior. If you open a header first, you see vocabulary. `battle.h` gives us names for battle resources, results, scripting structures, battler positions, and shared globals. `battle_main.c` then shows how those names are used over time. In this course, whenever we identify files, structs, functions, constants, data tables, and state variables for a major system, we are doing the same maneuver: vocabulary first, ownership second, flow third, consequences fourth.

Let’s apply that method to battle main. The vocabulary includes battlers, actions, moves, positions, resources, results, scripting, and controller state. Ownership is distributed: party data owns long-term creature identity; battle resources own battle-lifetime context; scripting state owns temporary effect progression; globals expose current registers to cooperating routines. Flow lives in `battle_main.c`, with the chunks named in today’s source focus. Consequences flow back through results and party mutation. That is the system map. If you can produce that map from memory by the end of week 13, you are no longer merely reading FireRed; you are reading it architecturally.

One subtle point: “global” does not automatically mean “bad design.” In many contemporary code reviews, people react to globals as a smell, and often they are right. But in this codebase, especially in battle, globals often act as shared registers across small procedural state machines. The better question is not “Is this global?” The better question is “What is the lifetime, who writes it, who reads it, and what phase makes that value meaningful?” A variable that is dangerous in one architecture may be intentional in another. For FireRed, the discipline is phase control. The engine must ensure that a register is interpreted only while the battle is in the phase where that register has meaning.

For example, a current move register during action execution is meaningful. During action selection, it may be uninitialized, stale, or only partially relevant. During battle cleanup, it may be irrelevant. So if you add a lab feature that reads `currentMove` from a UI display at all times, you are building a stale-state bug. Instead, the UI should know whether the system is in a phase where the current move is valid. This is the same kind of reasoning we used in menu state machines, especially in the move relearner source `pokefirered/src/learn_move.c` `chunk_af3598df6f092723`, where the packet identifies an explicit move relearner state machine. We are not re-teaching that menu. We are transferring the principle: phase determines interpretation.

Now, in the real FireRed battle, action choice also interacts with controller-facing state. The player controller, opponent controller, link controller, and other battler controller logic do not all behave like a local function call. The main loop may send a command and then wait for completion. That means a current action can be logically selected before its visual or controller consequence has completed. In a simple simulator, you can resolve the whole action instantly. In FireRed, visible execution is part of the architecture. This is why `battle_main.c` must be read as coordinated state rather than as a linear rules document.

Let’s make that concrete. If the engine says “battler one uses Ember,” there are several audiences for that fact. The rules layer needs move type, power, accuracy, and effect. The text layer needs a message. The animation layer needs a move animation and sprite participants. The audio layer may need a sound cue. The controller layer needs to know when commands are complete. The result layer may need to know if damage, fainting, or status occurred. The battle-end layer may need to know whether the outcome has changed. One phrase in player-visible language fans out into many cooperating states.

And that gives us the design lesson for your own creature RPG. Do not start by writing a “battle manager” that owns everything in a vague way. Start by defining the registers and lifetimes. Which data is battle-lifetime? Which data is per-turn? Which data is per-action? Which data is per-script-step? Which data is persistent party data? Which data is only visual? Which data is the final result? If you can answer those questions, your implementation can be simple and still scale.

Let me ask one final comprehension question for this segment. Why do we use a turn-order array instead of rearranging the battler records themselves by speed? Yes, because battler identity must remain stable. Battler zero is still battler zero. Its party slot, side, controller, sprite, HP, status, and position remain associated with that identity. Turn order is just an execution view over stable battlers. That is a very general architecture pattern: keep identity stable, create temporary ordered views for processing. We saw the same broad idea in data-table indexing earlier in the course, and we will see it again when battle effects and end-of-turn effects require their own passes.

So, to close today’s lecture, here is the synthesis. FireRed’s battle main is not a monolithic combat function. It is a coordinator for layered state machines. `pokefirered/include/battle.h` `chunk_825c6c75270bc90c` gives us battle-lifetime resources and result boundaries. `pokefirered/include/battle.h` `chunk_8367bbcb71bfc88d` gives us scripting state and shared battle globals. `pokefirered/src/battle_main.c` `chunk_2017de6d7013d2b7`, `chunk_568e3366056a9ce4`, and `chunk_a15739d3170f73c1` are where main flow, state progression, and action dispatch become a running battle. The important mental model is not “find the function that does the turn.” The important model is “follow the battler identity, chosen action, turn-order view, current action registers, script progression, controller completion, visible animation, and final result.”

For the lab, build the small version with respect for those boundaries. Two battlers are enough. Chosen actions per battler, speed-sorted order, current battler, current move, current target, and a script pointer placeholder are enough. But make those pieces explicit. If you do, your toy loop will already have the shape of a real RPG battle engine. If you do not, you will get something that works for one hard-coded Tackle exchange and collapses as soon as you add fainting, switching, priority, or animation delay. That is the standard I want you to hold for week 13: not completeness yet, but correct architectural pressure.

## Source References
- pokefirered/src/daycare.c
- pokefirered/src/learn_move.c
- pokefirered/src/pokemon_summary_screen.c
- pokemon-showdown/data/mods/gen2stadium2/scripts.ts
- pokemon-showdown/data/mods/gen9ssb/moves.ts
- pokemon-showdown/data/text/moves.ts
