# Actions, Turn Order, and Battler Globals

Lecture ID: lecture_w13_d02
Week 13, Day 2

## Objectives
- Design minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence.
- Connect week 13 material to the overall course plan.

## Transcript
All right, let’s pick up exactly where we left off. Last time we treated the battle main callback as a coordinator: not as “the battle,” not as the damage system, not as the animation system, but as the active runtime owner that keeps a large set of shared battle state moving forward one frame at a time. Today we are going to tighten the lens. We are going to ask: once the battle system has multiple battlers, multiple possible actions, item usage, move execution, switching, fainting, scripts, messages, animations, and persistent results, how does it avoid becoming an untraceable knot?

The answer is not that FireRed makes everything beautifully isolated. It does not. The answer is that FireRed makes the shared state explicit enough, and disciplined enough, that many small state machines can cooperate. That is a different architectural lesson from the one you may have learned in cleaner modern engines. Here, the important thing is not “avoid globals at all costs.” The important thing is: if you are going to have globals, which a Game Boy Advance RPG absolutely does, then the globals must have roles. Some globals represent chosen intent. Some represent current execution. Some represent temporary script parameters. Some represent persistent battle results. Some represent controller-facing state. Some represent sprite-facing presentation. If you mix those categories mentally, you will misread the engine.

Let me put that on the board. I’m going to write five phrases, and I want you to keep these five apart for the rest of today: selected action, ordered turn, current battler, current operation, shared result. Selected action means, “What did this battler choose?” Ordered turn means, “When will that chosen thing be processed?” Current battler means, “Whose effect are we executing right now?” Current operation means, “What move, item, switch, script command, or controller command is currently in flight?” Shared result means, “What has this battle produced that survives the immediate animation or command?”

Now, if that sounds abstract, good. It should sound abstract at first, because the source code is not organized as a tidy textbook hierarchy. The job of this lecture is to give you the mental hierarchy you need in order to read code that was written for hardware and workflow constraints, not for our comfort.

The core source focus for today is pokefirered/src/battle_main.c, especially chunk_2017de6d7013d2b7 for the main battle flow, chunk_568e3366056a9ce4 for battle state progression, and chunk_a15739d3170f73c1 for the turn and action flow. We also need pokefirered/include/battle.h, especially chunk_825c6c75270bc90c for BattleResources and BattleResults, and chunk_8367bbcb71bfc88d for BattleScripting and the battle globals. Those chunk names matter because, as you saw in the midterm, I am no longer asking you to talk about “the battle system” in general. I am asking you to trace behavior through concrete files, globals, structs, callbacks, and progression points.

Let’s start with a small clone problem. Suppose we are building the minimal version of a creature RPG. We have two battlers. Each battler has a party slot. Each party slot has HP, speed, a list of moves, and maybe a held item later. The player chooses a move. The opponent chooses a move. Then the engine decides who goes first. Then it executes action one, possibly causing damage or fainting. Then it executes action two, unless that action is no longer valid. Then it ends the turn, saves any necessary result, and returns to action selection.

That is the student version. It is correct, but too compressed. The FireRed version is more layered. It does not merely say, “player chooses move, enemy chooses move, faster one goes first.” It has chosen action arrays, battler identifiers, turn-order arrays, current move globals, script pointers, battle resources, controller callbacks, sprite state, and result structures. The point is not that every clone needs all of that. The point is that a clone needs a minimal equivalent of each responsibility if it wants to grow without collapsing.

So, on the board, I’ll sketch the smallest serious version.

Imagine an array called chosenActionByBattler. Battler zero has MOVE. Battler one has MOVE. Then another array called chosenMoveByBattler. Battler zero has Tackle. Battler one has Ember. Then a turnOrder array. If battler one is faster, turnOrder is one, zero. Then a loop index, maybe turnIndex, starts at zero. The engine says currentBattler equals turnOrder[turnIndex]. CurrentMove equals chosenMoveByBattler[currentBattler]. Then it calls a move execution routine, or more accurately, it enters a move execution script. After that script finishes, turnIndex increments.

That is the architectural skeleton of today’s lab. But let’s be very precise: in a FireRed-style engine, the turn loop is not just a for-loop that instantly runs to completion. It is staged across frames. It may yield to text printing. It may yield to animation. It may wait for a controller command to acknowledge completion. It may run a battle script instruction that mutates state and then returns control to the main callback. This is why the lecture title includes “Battler Globals.” The engine needs public, shared, frame-persistent variables that say, “Here is the battler we are currently talking about,” and, “Here is the move we are currently resolving,” because the execution may be split across many callbacks and scripts.

This connects directly to the course thesis from the research packet: FireRed is best understood as a stack of cooperating state machines plus data tables, where behavior is traced from data table, to runtime globals, to callback, task, script, struct, and finally to visible behavior. That thesis is supported across the packet, and it is especially visible today in pokefirered/src/battle_main.c chunk_a15739d3170f73c1, where chosen actions and turn flow are not just passive data; they become the order in which the battle engine advances.

Now, check for understanding before we go deeper. If I ask, “Where is the player’s selected move stored?” that is a different question from, “Whose move is currently being executed?” And both are different from, “Which battler is visually animating right now?” If you collapse those into one variable in your clone, your first demo might work. Your second feature will hurt. Your third feature will probably break switching, items, or fainting. So the design lesson is separation of roles, even when the actual storage is global.

Let’s use a concrete example. We have Charmander versus Squirtle. Charmander is battler zero, Squirtle is battler one. Charmander chooses Scratch. Squirtle chooses Water Gun. Squirtle is faster. At action selection time, the chosen action array says both battlers chose a move. The chosen move array says battler zero chose Scratch, battler one chose Water Gun. But the current battler is not meaningful yet, or at least it is not the execution owner yet. After sorting, the turn-order array says battler one first, battler zero second. Now current battler becomes one. Current move becomes Water Gun. The script system or move execution logic reads current battler and current move, computes target, displays text, applies damage, updates HP, maybe sets a fainted flag, and then yields until animation and text are done.

Now suppose Charmander faints from Water Gun. What happens to Charmander’s previously chosen Scratch? This is where architecture matters. If you directly called Scratch when the player selected it, you have no chance to cancel it properly. If you merely stored intent and later validated it at execution time, you can say, “Battler zero is now unable to act.” So a serious battle engine stores chosen actions as intent, orders intent into a turn sequence, and validates intent at the moment of execution. That is the difference between a menu simulator and a battle engine.

In FireRed’s structure, pokefirered/src/battle_main.c chunk_568e3366056a9ce4 is part of the evidence for this kind of staged progression. Battle state does not proceed as a single immediate function call. It advances through cases and phases. And pokefirered/src/battle_main.c chunk_2017de6d7013d2b7 supports the broader claim that the battle main flow is coordinated through a callback-oriented progression, not a single blocking procedure. Again, this is not just historical trivia. If you build your clone with one blocking procedure, you will struggle the moment you add animation, message boxes, link battles, or any UI that takes multiple frames.

Let me pause and make the connection to prior weeks, but only briefly. When we studied party slot ownership, the big lesson was that a visible creature in a menu is not just a loose object; it is a view over party data with an owner and a slot. When we studied the summary screen, we saw reads from party data organized for display. When we studied the move relearner in pokefirered/src/learn_move.c chunk_af3598df6f092723, we saw a menu state machine controlling the staged selection and mutation of move data. Today, battle action selection builds on all of that. It is another state machine, but with higher pressure: multiple actors, order-dependent effects, frame delays, scripts, and results that must be reported back to the overworld or save system.

So, we are not re-learning party storage today. We are using it. The battler’s party slot is the durable creature record. The battler state is the battle-facing runtime wrapper. The chosen action is the turn-facing intent. The current battler globals are the script-facing execution context. If you can say those four sentences cleanly, you are ready for the advanced parts of the battle engine.

Now let’s bring in battle.h. The reason I assigned pokefirered/include/battle.h before drilling deeper into battle_main.c is that battle.h acts as a subsystem map. In chunk_825c6c75270bc90c, the packet identifies BattleResources and BattleResults as important structures. That tells us something architectural: FireRed distinguishes the resources needed while a battle is running from the results that matter after the battle has produced an outcome. That distinction is essential in a clone. You do not want your save file storing every temporary battle script pointer. You also do not want your battle loop losing information like money gained, caught species, trainer flags, or outcome state just because the animation subsystem ended.

Then in pokefirered/include/battle.h chunk_8367bbcb71bfc88d, BattleScripting and globals become visible as cross-module contracts. A battle script is not merely a text script. It is a compact control program for battle behavior. It needs parameters. It needs a current attacker, current target, current move, effect flags, message selectors, and temporary storage. In a modern engine, you might wrap all of that inside a BattleContext object and pass a reference everywhere. In FireRed, many of those responsibilities are represented by globals and shared structs. The advanced reading skill is to identify which global belongs to which layer of meaning.

Here is a useful rule of thumb. If a variable answers, “What did someone choose?” it belongs to selection. If it answers, “Who goes next?” it belongs to ordering. If it answers, “Who is acting right now?” it belongs to execution. If it answers, “What should the script interpreter do next?” it belongs to scripting. If it answers, “What should be visible?” it belongs to controller or sprite-facing state. If it answers, “What do we carry out of the battle?” it belongs to results.

Let’s turn that into minimal clone architecture. On the board I’ll write a tiny data model in words, not code yet. We need a MapRuntime, because battles usually begin from the world. It has a map id, player position, object events, and a script pointer or script state. We need a ScriptRuntime, because talking to a trainer, stepping into tall grass, or using an item can trigger a script that decides whether to start a battle. We need PartyData, because battle participants come from persistent creature records. We need BattleRuntime, because turn execution needs temporary battler state. We need InventoryRuntime, because item use can happen either in the overworld or in battle. And we need SaveData, because after the battle, changed HP, used items, flags, money, caught creatures, and position may persist.

Those are not separate games. They are layers of one game. Week 13 sits in the middle because battle is where many layers collide. Battle reads party data, consumes item data, displays UI, runs scripts, mutates results, and eventually returns control to the overworld. That is why this week is difficult, and it is why it is central to the course plan.

Let’s do a check. Suppose your clone has a Potion. The player uses Potion in battle. Where does that action live? Do not say “inventory.” That is only one part. First, the chosen action by battler may be ITEM instead of MOVE. Second, the chosen item parameter may be Potion. Third, the target may be a party slot or battler. Fourth, turn ordering may treat item use with special priority, maybe before moves, depending on your rules. Fifth, execution must decrement inventory. Sixth, execution must heal the party creature or battle HP mirror. Seventh, controller and sprite systems may show a message and HP bar animation. Eighth, battle results or persistent party data must reflect the final HP and consumed item. If you store that as one direct call from the bag menu to “heal now,” you bypass the turn architecture. Sometimes that is acceptable for a tiny demo. It is not acceptable for the clone we are designing in this course.

This is also where the FireRed architecture teaches restraint. You do not need to make an over-abstract command bus. You do need to make sure that item use is represented as an action with parameters, not merely as a UI button callback. The UI gathers intent. The battle loop orders and executes intent. The script or effect routine performs the action. The result layer persists the consequences.

Let me compare that briefly with the secondary reference we have used carefully in this course, Pokemon Showdown. The research packet reminds us that Showdown is useful as a comparison for battle data hooks and deterministic turn testing, but not as the primary architecture source. For example, pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f shows a very different style: damage calculation can call move callbacks, check immunities, fixed damage, and base power in a simulator-oriented TypeScript architecture. That is valuable for thinking about testable battle rules. But FireRed’s architectural lesson today is not primarily the formula. It is the runtime coordination: who chose, who acts, what script pointer runs, what global identifies the battler, and how the callback returns next frame.

So, if you borrow one idea from Showdown here, borrow determinism. If you choose a known initial state and known actions, the turn order and outcome should be reproducible. But if you borrow your architecture, stay grounded in FireRed: battle_main.c and battle.h are our authority for this module.

Now I want to walk through the life of one turn at a higher fidelity. First, the battle engine waits until all battlers that need input have chosen actions. Player input may arrive through a menu. Enemy input may be generated by AI. Link input may arrive from another device. The chosen actions are stored per battler. Second, the engine builds or updates the turn-order array. For move actions, speed matters. For certain non-move actions, priority rules may override speed. Third, the engine sets execution globals for the next battler in turn order. Fourth, it validates the action. Is the battler fainted? Is the move disabled? Is there enough PP? Is the target still valid? Fifth, it enters the appropriate execution path: move script, item script, switch handling, run attempt, or special case. Sixth, it yields as needed to controllers, animations, text, and sprite updates. Seventh, when the action is complete, it advances the turn cursor. Eighth, when all ordered actions are exhausted, it handles end-of-turn effects and either returns to action selection or ends the battle.

Notice how much of that is not “damage.” Damage is just one operation inside a broader scheduling and scripting architecture.

Let’s make the board work more concrete. I’ll write this as a trace: chosenAction[0] equals MOVE. chosenMove[0] equals Scratch. chosenAction[1] equals MOVE. chosenMove[1] equals Water Gun. speed[0] equals 12. speed[1] equals 14. turnOrder equals [1, 0]. turnIndex equals 0. currentBattler equals 1. currentMove equals Water Gun. currentTarget equals 0. battleScriptPointer equals script for executing a damaging move.

Now, class, what changes after Water Gun hits? HP for battler zero changes. Maybe the party data mirror changes, or the battle struct’s HP changes and later synchronizes. A damage value temporary changes. A message state changes. The sprite-facing HP bar state changes. A fainted status may change. The result might not yet say the battle is over, because the engine must process fainting and replacement or victory checks. Then the script pointer advances. The current battler may still be one while the message finishes, because the operation is not done. Only later does turnIndex advance and currentBattler become zero, and even then the validation step may cancel Scratch because Charmander fainted.

That last sentence is one of the central points of the day: current battler is an execution cursor, not a moral claim that the battler will successfully act. The engine still has to ask whether the action remains legal.

Let me ask a check-for-understanding question in a slightly sharper form. If a battler selects a move, then flinches before its turn, should chosenMoveByBattler be erased? Maybe, but that is not the cleanest first thought. The chosen move was historically true. The battler did choose it. The execution validator or script layer can decide that the action fails due to flinch. This distinction lets messages say the right thing, lets locks and repeated moves behave correctly, and lets debugging traces remain intelligible. In a minimal clone, you may clear it at end of turn, but during the turn it is useful to preserve selected intent and separately store execution-blocking conditions.

This is exactly the kind of design maturity I expect after the week 12 midterm. Earlier in the course, it was enough to identify a data table and a callback. Now you need to preserve distinctions between data that looks superficially similar. “Selected move” and “current move” may hold the same move id for a few frames, but they do not mean the same thing.

Let’s connect this to controller and sprite-facing state before we pause. The battle engine is not only calculating outcomes; it is directing a performance. FireRed’s battle state has to communicate with battler controllers, health boxes, sprites, animations, palettes, text windows, and sound. That is why shared globals and resources are not only about rules. Some of them exist because a sprite callback or controller command needs to know which battler or which visual resource is active. The battle loop says, in effect, “We are resolving battler one’s move; now ask the controller to play the command; now wait until the controller reports done.” That waiting is why the callback architecture matters.

If you are designing the lab version, do not implement all of that. Implement the placeholder. Have a script pointer placeholder, or a script step enum, that can say DISPLAY_MOVE_TEXT, APPLY_DAMAGE, WAIT_ANIMATION, CHECK_FAINT, COMPLETE_ACTION. Even if WAIT_ANIMATION is a no-op for now, the slot is architecturally important. You are making room for the same kind of staged progression seen in pokefirered/src/battle_main.c chunk_568e3366056a9ce4.

And now, a quick recap of where we are, because this is dense. Battle turns should be understood as a pipeline from selected intent to ordered execution to scripted effects to visible controller work to persistent results. FireRed exposes that pipeline through battle_main.c flow chunks, especially chunk_2017de6d7013d2b7, chunk_568e3366056a9ce4, and chunk_a15739d3170f73c1, and through battle.h structures and globals in chunk_825c6c75270bc90c and chunk_8367bbcb71bfc88d. In your clone, you should not copy every global. You should copy the separation of responsibilities.

We will continue from this point by looking more carefully at how the turn-order array and current battler variables let the engine handle moves, items, switches, and failed actions through one coordinated progression rather than a pile of special-case menu callbacks.

So let us now put a slightly sharper edge on that sentence: the turn-order array is not just a sorted list. It is the battle engine’s commitment to a temporary execution schedule. That schedule can be invalidated, amended, skipped, or consumed, but while the engine is inside that phase, every other piece of state should be interpreted relative to that schedule. This is why current battler variables matter so much. If I say “the current move is Tackle,” that statement is incomplete unless I also know whose Tackle it is, who is targeted, which action slot is being processed, and what script cursor is currently advancing the consequences.

On the board, I am going to write four names, and I want you to keep them separate even though a small clone might store them in fewer fields: chosen action, ordered battler, current battler, and active script. Chosen action is intent. Ordered battler is scheduling. Current battler is execution context. Active script is consequence flow. Those are four different architectural ideas. FireRed’s actual implementation has more names, more arrays, more special cases, and more hardware-facing details, but the reason we are studying pokefirered/src/battle_main.c chunk_a15739d3170f73c1 today is that it shows this style of turn and action flow: the engine does not simply call “use move” from the fight menu and finish the whole move right there. It records, orders, and advances.

Now, suppose we have a minimal two-battler clone. The player’s Bulbasaur selects Vine Whip. The opponent’s Charmander selects Scratch. At selection time, you do not yet want to mutate HP. You do not yet want to spawn damage numbers. You do not yet want to play the faint animation. You want to record intent. So for battler zero, chosenAction is “use move,” chosenMove is Vine Whip, chosenTarget is battler one. For battler one, chosenAction is “use move,” chosenMove is Scratch, chosenTarget is battler zero. Then, after both sides have committed, you build turnOrder. Maybe Charmander is faster. So turnOrder becomes one, then zero. The crucial point is that Bulbasaur’s choice still exists in its original slot. It does not move into Charmander’s slot. The schedule says battler one executes first, not that battler one owns all first-position data.

Check for understanding here: if Charmander moves first and knocks Bulbasaur out, do we delete Bulbasaur’s chosen action immediately? Not necessarily. In a robust architecture, we usually leave the selected action alone and let the execution phase ask, “Can this battler still act?” That distinction prevents a lot of bugs. The selected action is historical intent. The current eligibility is runtime condition. Those are not the same thing.

This is where the shared globals become both powerful and dangerous. In FireRed, battle.h is not just a bag of declarations. The packet points us specifically to pokefirered/include/battle.h chunk_825c6c75270bc90c for BattleResources and BattleResults, and to pokefirered/include/battle.h chunk_8367bbcb71bfc88d for BattleScripting and globals. Those chunks matter because they show that battle state is intentionally shared across pieces of the battle engine. Scripts need to know what battler is active. Controllers need to know what visual request to perform. Result structures need to survive long enough to return back to the overworld. This is not object-oriented encapsulation in the modern sense. It is coordinated procedural state. But coordinated does not mean random. It means the globals are meaningful because the main loop advances them in a disciplined order.

In your clone, if you are writing this in C, C++, TypeScript, Rust, Python, it does not matter for the design lesson. Do not start by making a massive “BattleGlobalEverything” object because FireRed has many globals. Start by making a battle context with a few intentional fields. Put in party references, battler state, chosen actions, turn order, current turn index, current battler, current move, current target, script pointer, and battle result. That is enough to reproduce the architectural shape. Later, when you need animation queues, text buffers, controller commands, item messages, or ability hooks, you will have places to attach them. But the minimal version should already have the same rhythm as the source: select, order, execute, script, display, resolve.

Let me connect that to the course plan for a moment, because week 13 is not an isolated battle detour. In the first half of the course, we learned to trace from data records through runtime systems to visible behavior. We did it with map headers and layouts. We did it with scripts. We did it with party data and summary reads. We did it with move learning, where pokefirered/src/learn_move.c chunk_af3598df6f092723 showed a menu state machine rather than a one-shot command. Today’s battle turn loop is the same kind of architecture, only denser. The difference is that a battle turn is a high-conflict state machine. Multiple actors have chosen actions. Some actions cancel other actions. Some actions rewrite targets. Some actions create scripts that take multiple frames to finish. Some actions lead to persistent outcomes like experience, money, consumed items, fainted party members, or trainer victory flags.

So when we talk about designing minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence, we are really talking about preserving boundaries. Map loading should not know how Scratch deals damage. Script execution should not own the entire party model. Party data should not directly push battle animations. Battle turns should not write save files every time HP changes. Item use should route through the correct context, because using a potion in the overworld and using a potion in battle share item data but not timing semantics. Save persistence should capture stable game state, not temporary script cursors and animation queues unless the platform explicitly supports mid-battle saving.

Let us make that concrete. Imagine a Potion in battle. The player chooses “Bag,” then “Potion,” then chooses Bulbasaur. In a weak clone architecture, the bag menu might immediately add 20 HP, close the menu, print text, and return to the battle. That will seem to work for the first demo. Then the first time you add turn order, enemy attacks, item priority, or link battle behavior, it becomes a mess. In a better architecture, the menu records a chosen action: battler zero chose “use item,” item is Potion, target is party slot or battler zero depending on your model. Then the action ordering phase knows that item actions may have a different priority from ordinary moves. Then the execution phase sets currentBattler to zero, currentItem to Potion, currentTarget to zero, and starts an item-use script. The script applies HP restoration at the right moment, displays the message at the right moment, triggers the controller-facing update at the right moment, and then yields back to the main battle progression.

That is exactly the kind of staged progression we are reading in FireRed’s battle main flow. The packet identifies pokefirered/src/battle_main.c chunk_2017de6d7013d2b7 as battle main flow, chunk_568e3366056a9ce4 as battle state progression, and chunk_a15739d3170f73c1 as turn/action flow. I am citing those together because no single chunk should be treated as “the battle engine.” The architecture emerges from their cooperation. One piece owns the broad callback-driven battle progression. One piece advances state. One piece deals with chosen actions and turn execution. And battle.h gives the cross-module vocabulary that allows those pieces to talk.

Now, here is a subtle but important invariant. The current battler is not always the same as the player. It is not always the same as the attacker. It is not even always the same as the battler whose sprite is currently animating, once scripts and controllers get involved. Current battler means “the battler whose action context the engine is currently resolving,” and you should be very precise about that. If a move causes recoil, the attacker is damaged. If a move causes drain, the target is damaged and the attacker heals. If an item targets an ally, the user and target differ. If a forced switch occurs, the battler being replaced, the party slot being selected, and the battler whose controller is receiving input may be different. FireRed has to survive all of those distinctions.

Let me ask it as a check for understanding. If battler zero uses a Potion on battler zero, currentBattler and currentTarget may match. If battler zero uses an item on a second party member in a switch-style context, should currentTarget still mean battler zero? No. In a minimal clone, you may decide that battle items only target active battlers, and that is fine for a first implementation. But the architecture should leave room for party-slot targets, because the course has already established party slot ownership as a foundation. We are not re-teaching party slots today, but we are using that idea. Battle actions often reference battler slots, while party operations reference party slots. Confusing those two is one of the easiest ways to create bugs that only appear when switching, fainting, or healing a non-lead creature.

On the board, I am going to sketch a tiny execution loop in words, not syntax. First, if we are in selection state, collect actions until every battler that must choose has chosen. Second, build turn order from chosen actions, speed, and action priority. Third, set turn index to zero. Fourth, while turn index is less than battler count, read orderedBattler from turnOrder at turn index. Fifth, set currentBattler to orderedBattler. Sixth, validate that this battler can act. Seventh, load the action’s relevant context, such as currentMove or currentItem. Eighth, start or continue a script until the script completes. Ninth, increment turn index. Tenth, after all actions, run end-of-turn effects and check battle result.

Notice the phrase “start or continue a script.” That is important. On the Game Boy Advance, and in FireRed’s engine style, many operations are spread across frames. Text boxes wait. Animations wait. Controller callbacks wait. Sprite updates wait. Sound effects may be queued. So an action is not always a single function call that finishes immediately. This is why, when we studied the runtime shell earlier, we cared about main callbacks, task dispatch, and staged state. That earlier material now pays off. The battle main callback can keep re-entering the battle engine each frame, and the battle engine can say, “I am still resolving the same action; the script pointer has not completed yet.” That is the architectural bridge from callback shell to battle logic.

This also explains why controller-facing state and sprite-facing state are not just rendering details. A beginner might say, “Well, the battle calculation is the real system, and sprites are just presentation.” For a deterministic simulator, maybe. But FireRed is a shipped RPG on constrained hardware. Visible behavior is part of the engine’s pacing contract. The battle engine must not overwrite the next action while the previous action’s text, animation, and HP bar movement are still underway. So controller state becomes part of the synchronization mechanism. It is not merely decorative. It tells the core progression whether a requested visual operation has completed.

This is a useful comparison point with Pokemon Showdown, but keep the hierarchy straight. FireRed remains our primary architecture source. Showdown is helpful when we want to think about deterministic battle data hooks and regression tests. The packet includes pokemon-showdown/data/text/moves.ts chunk_92a6298340525a85, where move text describes effects like damage at the end of each turn, and pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f, which exposes damage calculation logic in a very different environment. Those are useful comparisons because Showdown’s battle model is designed for a server simulator and testing ecosystem, not for a GBA callback-and-controller visual pipeline. The conceptual overlap is turn effects and move consequences. The architectural difference is where timing lives.

So, if you are building the lab loop this week, do not imitate Showdown’s whole event engine, and do not imitate FireRed’s entire global surface. Take FireRed’s coordination model and make it small. Two battlers. Chosen action array of length two. Turn order array of length two. Current battler integer. Current move identifier. Optional current target. Script pointer placeholder, even if it just points to a small enum state like “print move name,” “apply damage,” “wait,” and “done.” That placeholder is the seed of a real scripting layer. It lets you avoid hard-coding every effect directly into the turn loop.

Now let us work through a concrete example with failure, because success cases hide architecture problems. Bulbasaur chooses Vine Whip. Charmander chooses Scratch. Charmander is faster and acts first. Scratch deals enough damage to faint Bulbasaur. The engine completes Charmander’s script: text, animation, damage application, HP bar update, faint check, faint message, maybe experience queue later, depending on battle type. Now the turn index advances. The next ordered battler is Bulbasaur. The action says “use move, Vine Whip.” But the validation step checks current HP and status. Bulbasaur cannot act. What happens? The engine should not crash because currentMove still exists. It should not target Charmander with a fainted attacker. It should route through a failed-action path or skip path. That path might print nothing, or it might print a message depending on the reason. Sleep, paralysis, flinch, fainting, recharge, and confusion may all have different messages. But architecturally, they share the idea that chosen intent is filtered at execution time.

That one example also shows why battle results should be distinct from temporary action state. If Bulbasaur fainting ends the battle because it is the last party member, the engine should set or derive a battle result. FireRed’s battle.h BattleResults material in pokefirered/include/battle.h chunk_825c6c75270bc90c is relevant here because battle outcomes must be communicated back out of the battle subsystem. The overworld needs to know whether to return control to the player, warp to a Pokecenter-style location, trigger a trainer defeated script, award money, or resume a field script. Your clone’s save persistence should care about that outcome after the battle resolves. It should not be mixed into the moment-to-moment currentMove handling.

Let us pause and test the model. I will say a field, and you tell me mentally whether it belongs to long-lived save state, battle context, action context, or visual controller state. Party HP after damage. That is party or creature state, and eventually save-relevant. Current battler while resolving Scratch. That is battle action context, temporary. Text box waiting for the player to press A. That is UI or controller-facing state, temporary. Trainer defeated flag after victory. That is progression state and save-relevant. Turn order for the current round. Temporary battle context. Map layout ID. Overworld map loading state, save or runtime depending on whether it is current location metadata. The reason I am mixing map and battle examples is that the course goal is not “memorize battle_main.c.” The goal is to recognize which state belongs to which layer.

This is also why we spent time earlier on map loading and script execution. A battle often begins because the overworld script system or encounter system requests it, and it ends by returning a result to that world. In a minimal clone, you might have a map loader that reads a map ID, loads a layout, places objects, and starts an event script when the player talks to a trainer. That script says, in effect, “start trainer battle with this trainer party.” The battle system receives trainer party data, initializes battlers from party slots, runs the battle loop, and eventually returns win or loss. The script then continues: set defeated flag, give reward, change trainer object behavior. The save system persists the defeated flag, party mutations, inventory changes, and current location. That is the whole creature RPG architecture in miniature.

Now, advanced students, here is where I want you to be careful. Minimal does not mean naive. A minimal clone can still have correct ownership boundaries. It can still have a disciplined state progression. It can still distinguish data definitions from runtime state. In fact, it is easier to preserve those boundaries when the clone is small. If your map loader directly calls battle damage functions, you have already lost the architectural thread. If your battle action directly writes a save file, you have already mixed layers. If your item definition contains hard-coded UI behavior, you are making future contexts harder. The FireRed source is full of pragmatic C, but the high-level separation remains visible because systems communicate through known structures, globals, callbacks, scripts, and results.

Let me bring back battle scripting specifically. pokefirered/include/battle.h chunk_8367bbcb71bfc88d is useful because BattleScripting and the related globals show the engine needs a place to remember script-local facts. A move effect may need to know a working damage value, a target, a string to print, a status to apply, or a branch destination. You do not want every move effect to invent its own pile of local static variables. You want a shared script context whose lifetime is tied to battle script execution. In your clone, this might simply be a struct called BattleScriptContext with fields like pointer, attacker, target, move, damage, and messageId. If you later add more effects, that structure becomes the stable workspace for them.

A good question at this point is, “Why not just use the call stack?” In a normal desktop program, you might implement a move as a function that calls animation, waits, applies damage, waits, and returns. But if waiting means several frames of main-loop execution, the call stack cannot naturally hold that state unless you use coroutines, async functions, generators, or a manually advanced state machine. FireRed uses the idioms available and appropriate for its codebase: globals, callbacks, scripts, and state variables. In a modern clone, you may use coroutines, but the architectural problem is identical. Something must remember where the action paused.

So the script pointer placeholder in this week’s lab is not decorative. It is the difference between “my move function ran” and “my battle engine can pause and resume a consequence sequence.” Even if your placeholder only has three states, it trains the right mental model. First frame: set message to “Charmander used Scratch.” Later frame: after message completes, request animation. Later frame: after animation completes, apply damage and start HP bar. Later frame: after HP bar completes, check faint. That sequence is exactly why a current battler variable remains relevant across multiple frames.

Let us do one more check for understanding. If the player opens the fight menu and selects Vine Whip, what is the current battler? During menu selection, the input battler may be the player’s active battler, but the turn execution current battler may not yet be set. If you set currentBattler too early and then reuse it for execution, you may accidentally assume the player acts first. Better is to store selected intent in the battler’s action slot, then set currentBattler from turnOrder during execution. That is a small design choice, but it prevents an entire class of speed-order bugs.

And this returns us to today’s central claim. Battle architecture is not primarily about damage formulas. Damage formulas are important, yes, and later you can compare formal testable calculations with something like pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f. But FireRed’s battle engine challenge is coordination: callbacks, chosen actions, ordered battlers, shared globals, script progression, controller synchronization, party mutation, and result handoff. If you get that coordination right in a small clone, adding more move effects becomes an extension problem. If you get it wrong, every new move becomes a new special case.

I want to leave this segment with a local recap before we move into the next layer. Chosen actions preserve intent. Turn-order arrays create a schedule. Current battler variables define execution context. Script state lets consequences unfold over time. Controller-facing state prevents the logic from outrunning the presentation. Battle resources and results create boundaries around the subsystem. And the bridge back to the broader RPG is through party data, inventory data, event scripts, map state, and save persistence. In the next stretch, we will take that model and pressure-test it against switches, item actions, and end-of-turn effects, because those are the cases that reveal whether your turn loop is truly a coordinated state machine or just a sequence of move calls wearing a nicer name.

Now, let’s pressure-test that model.

Imagine the simplest case first: two battlers, both choose a damaging move, no priority, no switching, no item use, no fainting until after damage. That is the friendly case. In that world, your turn loop can almost look like a classroom example. Record action for battler zero. Record action for battler one. Compare speed. Execute faster action. Execute slower action. End the turn.

But FireRed’s architecture is not built only for the friendly case, and your clone architecture should not be either. The reason the chosen-action array matters is that “what the player picked” is not the same thing as “what will happen when this battler’s scheduled slot arrives.” The reason the turn-order array matters is that the schedule can be computed once, then interpreted step by step. And the reason the current-battler globals matter is that the battle engine needs one coherent execution context while it is walking through scripts, controllers, animations, damage, messages, and side effects.

So, on the board, I’m going to write a small trace. Not code yet, just state.

Turn begins. Battler A chooses Fight, move Ember. Battler B chooses Fight, move Tackle. Chosen actions become Fight, Fight. Chosen moves become Ember, Tackle. Turn order becomes A, then B, because A is faster. Current battler becomes A. Current move becomes Ember. Script pointer moves to “use move.” Damage is applied. Message waits. Animation waits. Then the script finishes. Current battler becomes B. Current move becomes Tackle. Script pointer again moves to “use move.” And only after that do we enter end-of-turn cleanup.

The important part is the separation. We are not recursively calling one move from another move. We are not letting the UI directly perform damage. We are not letting the menu callback mutate battle flow as it wishes. We are staging intent, scheduling execution, and then interpreting consequences.

That is the architecture you can see reflected in today’s source focus. The battle main flow in pokefirered/src/battle_main.c, chunk_2017de6d7013d2b7, is not merely a giant “do battle” function. It is a coordinator. The later battle state progression in pokefirered/src/battle_main.c, chunk_568e3366056a9ce4, gives us that repeated sense of advancing through phases, not completing an entire battle in one ordinary function call. And the turn and action flow in pokefirered/src/battle_main.c, chunk_a15739d3170f73c1, is the source-level anchor for today’s idea that action choice, ordering, and execution context are separate concerns.

Now, let’s complicate the example with a switch.

Suppose battler A chooses Fight with Ember. Battler B chooses Switch to party slot two. In many battle systems, switching has priority over ordinary moves. So even if battler B is slower, the action schedule may place B before A. But notice what changes and what does not change.

The chosen action for B is Switch. The selected target slot is party slot two. The chosen action for A is Fight. The selected move is Ember. The turn-order array might become B, then A. Current battler first becomes B. The battle script or command path performs the switch. The visual controller withdraws the sprite, changes the battler’s active party slot, sends out the replacement, updates health boxes, waits for messages. Then current battler becomes A, current move becomes Ember, and A attacks the new active target if the target is still legal.

This is where weaker clone engines usually break. They attach the move target too early, or they store a direct pointer to a battler object that is later replaced, or they assume the opposing battler cannot change between selection and execution. A more robust approach stores intent in a compact way and validates it at execution time. “I intend to attack opposing position one” is safer than “I intend to damage exactly this old object reference no matter what has happened.” The FireRed-style architecture is very comfortable with that because battler positions and party slots have already been made explicit. We covered battler positions previously, so I am not going to re-teach that mapping here, but I want you to see why it becomes useful now. Position is a stable battle concept. Party slot ownership is a stable party concept. The active monster occupying a battler position can change.

Check yourself here. If a switch occurs before a move, should the faster move target the old monster or the incoming monster? In the standard creature RPG model, it targets the currently active legal target when the move resolves, unless the specific move or rule says otherwise. So your architecture needs a moment of resolution, not just a moment of selection.

Now item use.

Item use reveals another boundary. Items belong to the inventory and party systems, but their consequences may occur inside battle. A potion is stored in the bag. The selected recipient is often a party member. The healing may affect the active battler’s HP box, the party data, the displayed sprite state, and the battle result if that item prevents a loss. That means item use is not “battle logic only,” and it is not “bag logic only.” It is a cross-system operation coordinated through battle state.

This is why the includes in adjacent modules are useful evidence for our architecture. The summary screen source, pokefirered/src/pokemon_summary_screen.c, chunk_e8e17a42bbf17d46, includes pokemon data, item constants, party menu, and battle-related headers. That does not mean the summary screen owns battle. It means mature game systems share contracts through headers and carefully chosen access paths. Similarly, pokefirered/src/daycare.c, chunk_119931105f546346, pulls together data, battle, party, event, and UI-related headers because long-running RPG features sit at the intersection of subsystems. That is a recurring FireRed lesson: you do not get clean architecture by pretending systems never touch. You get clean architecture by making the boundaries explicit.

For our minimal clone, item use during battle can be represented as a chosen action. Let’s say action type UseItem, item id Potion, target party slot zero. The turn scheduler gives item use its priority tier. When that action executes, the battle engine asks the inventory model, “Is this item present and usable in this context?” Then it asks the party model, “Is this target valid?” Then it applies the effect through a battle-aware path, because if the target is active, the controller-facing health display needs updating. Finally, it consumes the item, unless the item rules say otherwise.

Notice that order. Validate inventory. Validate target. Apply effect. Consume item. Update presentation. Record any result flags. If you consume first and then discover the target cannot be healed, you have made a persistence bug. If you update the visible health bar but forget party data, you have made a save bug. If you update party data but do not update battler-facing state, you have made a presentation desync.

Let me make this concrete with a small board trace.

Battler A, player side, chooses UseItem, Potion, target party slot zero. Battler B, enemy side, chooses Fight, Tackle. Item action priority wins. Current battler becomes A, but current move is none. That is already an important design point. Current battler does not always imply current move. The execution context must be broad enough for non-move actions. Script pointer moves to “use item message and effect.” Potion count decreases. Party slot zero HP increases. If party slot zero is currently active, battler HP display updates. Then the enemy action executes. Current battler becomes B. Current move becomes Tackle.

A common mistake is to make “turn order” mean “move order.” That works until the first switch, item, recharge, escape attempt, obedience check, forced action, or skipped action. So for your lab, I want the array to be turn order, not move order. It should hold battler ids. The action table tells you what that battler intends to do when its slot comes up.

Now, there is another subtle point. What if a battler faints before its scheduled action?

Suppose A is faster and knocks out B. B’s chosen action is still in the chosen-action array. B’s battler id may still appear in the turn-order array. But when the scheduler reaches B, the executor must validate, “Can this battler act?” If fainted, no. The slot is skipped or diverted into faint handling, depending on the exact phase of the battle engine.

This is one of the reasons FireRed’s battle state is so global-heavy. I do not mean “global-heavy” as an insult today. In a battle engine on the Game Boy Advance, with many small controller callbacks, scripts, animation waits, and state transitions, shared globals are the coordination surface. The battle resources and results definitions in pokefirered/include/battle.h, chunk_825c6c75270bc90c, are a good reminder that there is scoped battle-owned memory and there is a result boundary. The BattleScripting and globals material in pokefirered/include/battle.h, chunk_8367bbcb71bfc88d, is our anchor for the idea that script execution context is not incidental. It is an explicit subsystem-level concern.

Let me pause for a check for understanding.

If I ask you, “Where should the decision that B cannot act because it fainted live?” I do not want “in the animation” as the answer. I do not want “in the menu” as the answer. The right architectural answer is: in the action execution or battle state progression layer, at the point where a scheduled battler is about to act. The animation may show the faint. The menu may later be disabled. The party data may record the HP as zero. But the scheduler or executor is responsible for not treating a fainted battler as an ordinary actor.

Now, end-of-turn effects.

End-of-turn is where the battle loop stops being a list of two actions and becomes a state machine. Poison damage, burn damage, weather, binding effects, leech-style effects, field effects, delayed moves, faint checks, experience, forced switch prompts, victory checks. These are not chosen actions, but they are turn consequences. They often need their own ordering. They often need messages and animations. They often can cause fainting, which can trigger still more state.

For a minimal clone, I would not implement all of those effects. But I would reserve the architectural slot. After all scheduled battler actions have been processed, enter EndTurnEffects. In that phase, maintain an index or script pointer just like you do for battler actions. Process one effect at a time. Wait for messages. Wait for animations. Apply damage or healing. Check fainting. Continue. Then advance to the next turn only when the queue is empty.

This is the same lesson we learned with the move relearner, but at a higher stakes level. In pokefirered/src/learn_move.c, chunk_af3598df6f092723, the source comments and structure identify the move relearner as a menu state machine. That was a UI-local state machine. It staged fades, menu list creation, selection, replacement, and return to script. Battle is the same architectural idea under more pressure. The state machine is bigger, more shared, and more consequence-heavy, but the pattern is recognizable: do a little work, store state, yield to the frame loop, resume.

That “yield and resume” idea matters because this is not a server simulation that can compute everything instantly and then dump a result. FireRed is a frame-driven game. The player sees text boxes. Sprites slide. HP bars drain. Sound effects play. The battle controller must not outrun the presentation. So when you design your clone, even if your first version is text-only, I want you to preserve the idea that execution may take multiple ticks. A function like executeNextBattleStep is usually healthier than a function like resolveWholeTurnImmediately, unless you are writing a pure test simulator.

Now, since many of you used Pokemon Showdown as a secondary comparison in week ten, let me contrast carefully without shifting our authority away from FireRed. Showdown is extremely valuable for modern deterministic battle testing and data hooks. The research packet points us to pokemon-showdown/test as a recurring module around assertions, moves, battle, and species, and to pokemon-showdown/data for move and learnset data. But the architecture we are studying today is FireRed’s runtime architecture. Showdown can help you think about battle rules as testable transformations. FireRed helps you understand battle as a coordinated state machine embedded in a graphical RPG runtime. Those are related but not identical lessons.

For example, pokemon-showdown/data/text/moves.ts, chunk_92a6298340525a85, describes move effects with turn-based consequences like residual damage across multiple turns. That is useful conceptually because it reminds us that effects can outlive the initial move. But in FireRed-style architecture, that kind of consequence must also become messages, animations, per-frame waits, battler globals, and persistent battle state. A simulator can say “apply residual damage.” A handheld RPG has to ask, “Which battler is current? Which message is on screen? Is the HP bar done draining? Did fainting interrupt the remaining effects?”

So if you want a minimal clone that can grow, do not merely create a move function that subtracts HP. Create a battle stepper.

Here is the design I would accept for the lab.

You have party data outside battle. Each party member has species, level, current HP, max HP, speed, moves, maybe status if you are extending. When battle starts, create battle state from party references or copied battler views. You have two battlers for now. Each battler has a side, a party slot, current HP access, selected action, selected move, and maybe a controller state. You have a chosenActions array indexed by battler id. You have a turnOrder array containing battler ids. You have currentBattler, currentTarget, currentMove, and battlePhase. You have a script pointer placeholder, even if it is just an enum like ScriptUseMove, ScriptUseItem, ScriptSwitch, ScriptEndTurn. Then your update loop advances one small step at a time.

And yes, some of that may feel ceremonial for two battlers. Good. Architecture often feels ceremonial in the toy case because it is protecting the non-toy case. The lab is not asking you to write all of FireRed. It is asking you to choose the seams that will survive when you add a third action type.

Let’s do a concrete mini-turn.

Player’s Charmander has speed 15 and chooses Scratch. Enemy Rattata has speed 18 and chooses Tackle. During action collection, chosenActions[0] becomes Fight and chosenMoves[0] becomes Scratch. chosenActions[1] becomes Fight and chosenMoves[1] becomes Tackle. During ordering, turnOrder becomes [1, 0]. The battle phase becomes ExecuteTurn. The turn index starts at zero.

On the first update of ExecuteTurn, currentBattler becomes 1. currentMove becomes Tackle. currentTarget becomes 0. scriptPointer becomes UseMoveStart. The next update prints or queues “Enemy Rattata used Tackle.” The next update applies accuracy if you have it. The next applies damage. The next waits for HP animation or, in a text clone, waits for user acknowledgement. Then scriptPointer becomes ScriptDone.

The turn executor increments turn index. Now currentBattler becomes 0. Before Charmander acts, validate that Charmander is not fainted. If Charmander survived, currentMove becomes Scratch and the same script sequence begins. If Charmander fainted, skip the move and enter faint handling. After all scheduled actions are done, battlePhase becomes EndTurn. EndTurn checks residual effects. Then battlePhase becomes ChooseActions for the next turn, unless victory or loss has been reached.

Now the check for understanding: in that trace, how many different meanings did “current” have? Current battler was the actor. Current move was the selected move being resolved. Current target was the recipient after validation. Current script step was the sub-action inside the action. Current phase was the larger battle state. If you collapse all of those into one variable called state, you will quickly make a state machine that nobody can reason about. If you split them into meaningful layers, the complexity becomes visible and manageable.

This is why I keep using the phrase “coordinated state,” not just “state.” Lots of programs have state. The question is whether the state variables have clear jobs.

Let’s connect this back to save persistence, because that is one of the week thirteen objectives and it is easy to forget during battle architecture. You usually do not save in the middle of an ordinary battle in a classic handheld creature RPG. But battle still mutates persistent data. HP changes. PP changes. Items are consumed. Experience may be awarded. Party order may change if switching or faint replacement changes active slots in some designs. Trainer flags may be set after victory. Money may change. Script progress may continue after the battle result.

So BattleResults is not just a scoreboard. It is a bridge back to the overworld and persistence layer. The battle may know “player won,” “player lost,” “caught monster,” “ran away,” or “trainer defeated.” The overworld script can then continue from that result. Event flags can be set. The map can reload or resume. The save system can later persist those changed variables.

This ties directly to the course spine from week one onward. We decomposed the RPG into runtime shell, overworld maps, event scripting, creature data, battle, inventory, UI, save, and link systems. By week twelve, your midterm expected you to trace data records through globals, callbacks, tasks, scripts, structs, and source functions. Week thirteen is where those pieces collide inside the battle engine. A battle is not an isolated minigame. It is the densest meeting point of party data, move data, item data, controller callbacks, script progression, animation state, and persistent outcomes.

So when you design the minimal clone version of map loading, script execution, party data, battle turns, item use, and save persistence, keep the flow honest. Map loading places the player and events into a world. Script execution starts a trainer battle or wild encounter. Party data supplies the battlers. Battle turns mutate party-facing values and produce a result. Item use crosses inventory and battle. Save persistence records the durable consequences after control returns to the RPG shell.

Do not build those as six unrelated demos. Build them as a chain.

I’ll say that again because it is probably the most important design instruction for your project: build them as a chain. A minimal map can be a room id and an NPC id. A minimal script can be “trainer sees player, start battle, after win set flag.” A minimal party can be two creatures with HP, speed, and one move. A minimal battle can be the two-battler loop we just described. A minimal item can be a potion. A minimal save can serialize party HP, inventory count, map id, and trainer-defeated flag. That is small, but architecturally complete.

And if you wire it that way, you will understand why FireRed has the shape it has. The source is not large because the authors enjoyed indirection. It is large because a visible RPG feature is almost never owned by one system. A trainer battle begins from map and script state, uses party and trainer data, runs through battle globals and resources, talks to controllers and sprites, returns a battle result, updates event flags, and eventually becomes save data.

Let’s hold there for this segment. The local synthesis is this: switches prove that targets must be resolved at execution time; item use proves that battle actions can cross subsystem boundaries; fainting proves that scheduled actions still need validation; end-of-turn effects prove that the turn loop must continue after chosen actions are gone; and persistence proves that battle results matter outside the battle screen. Next we will turn that into the lab structure: the two-battler loop with chosen actions, speed ordering, current battler, current move, and a script pointer placeholder, and we will be very precise about what state belongs in each layer.

So now let’s turn the architecture into something you can actually implement in the workshop without accidentally building a whole battle engine. And I want to be careful here, because at this point in the course most of you are past the danger of under-designing the system. The more common danger now is over-designing it. You have seen enough FireRed source by now that you can imagine every global, every callback, every controller queue, every battle script command, every sprite animation path, and every after-battle consequence. That imagination is useful, but for the lab we need to compress the shape of the system, not reproduce its full mass.

On the board, I’m going to draw four boxes. The first box is persistent game state. The second is battle resources. The third is turn execution state. The fourth is presentation-facing state. These are not four separate programs. They are four layers of responsibility. And if you confuse them, your clone will technically run for the first two turns and then become impossible to extend.

Persistent game state is the layer that survives the battle. Party data lives there. Inventory counts live there. Event flags and trainer defeated flags live there. Save persistence eventually serializes that layer. Battle resources are the temporary working copy and scratch space for the battle. That is where battler slots, active HP, chosen actions, turn order, and battle result accumulate while the battle screen owns control. Turn execution state is narrower. That is the current battler, current target, current move, current action, and the script pointer or phase pointer that says what the engine is currently doing. Presentation-facing state is what controllers and sprites need in order to display the decision, animation, health bar, text, or selection prompt.

Now, notice how this matches the way FireRed is organized without requiring us to copy every detail. The week’s source focus in pokefirered/src/battle_main.c, especially chunk_2017de6d7013d2b7 for the broad battle main flow, chunk_568e3366056a9ce4 for battle state progression, and chunk_a15739d3170f73c1 for turn and action flow, is not just “a big C file where battle happens.” It is a coordination site. It is where global battle state, controller progress, selected actions, and execution phases meet. Then pokefirered/include/battle.h, particularly chunk_825c6c75270bc90c for BattleResources and BattleResults and chunk_8367bbcb71bfc88d for BattleScripting and globals, gives us the contract vocabulary: this is the stuff the rest of the program is allowed to know exists.

So for the lab, your clone should not start by asking, “How do I make Ember work?” That is too specific. It should ask, “Where would Ember’s identity live before execution, where would its effects run during execution, and where would its visible consequences be requested?” If you can answer those three questions, then adding Ember later is incremental. If you cannot answer them, then every move becomes a special case.

Let’s make this concrete. Suppose battler zero is the player’s Charmander. Battler one is the opposing Rattata. The player chooses Scratch. The opponent chooses Tackle. In the simplest possible implementation, chosenAction[0] is fight, chosenMove[0] is Scratch, chosenTarget[0] is one. chosenAction[1] is fight, chosenMove[1] is Tackle, chosenTarget[1] is zero. Then the ordering phase compares effective speed and writes a turnOrder array. Maybe Charmander has speed 12 and Rattata has speed 14, so turnOrder[0] is one and turnOrder[1] is zero. That array is not the battle. It is a schedule. It is a claim about what we intend to execute if nothing invalidates it.

And here is the first advanced point: a turn order array should not be treated as proof that an action can execute. It is only proof that an action was eligible when the schedule was built. If battler one moves first and knocks out battler zero, then battler zero’s scheduled Scratch must be revalidated before execution. This is exactly the kind of pattern we were pointing at when we said fainting proves scheduled actions still need validation. FireRed’s action flow in pokefirered/src/battle_main.c chunk_a15739d3170f73c1 is valuable here because it shows the battle loop as a progression through selected actions and state, not as a single atomic “sort and run” function.

Let me pause and check understanding. If I sort the battlers by speed and then immediately apply both moves without checking HP before the second move, what bug have I introduced? Yes, exactly: a fainted battler can still act. And the more subtle version is that a battler might not be fainted but the target might no longer be valid. In a two-battler lab that mostly means the target fainted or disappeared because the battle ended. In a larger system it can mean redirection, switching, semi-invulnerable states, double battles, or controller-side cancellation.

Now, I want you to write down a sentence, not code, just a sentence: “The current battler is a cursor into shared battle state.” That phrasing matters. The current battler is not a copy of a creature. It is not the owner of the battle. It is a cursor. When currentBattler is one, currentMove can be loaded from chosenMove[1], currentTarget can be loaded from chosenTarget[1], and the execution script can say, in effect, “run the move script for battler one’s selected move against its resolved target.” In FireRed vocabulary, this is why the BattleScripting globals in pokefirered/include/battle.h chunk_8367bbcb71bfc88d are so important architecturally. They allow the engine to carry temporary script-facing facts without pretending those facts are permanent party data.

This is also where we connect back to the move relearner without re-teaching it. In pokefirered/src/learn_move.c chunk_af3598df6f092723, the move relearner is described as a menu state machine. It reads a party member, presents choices, then mutates move data through a controlled flow. Battle move execution is also state-machine work, but the ownership is different. The move relearner is a UI-driven party mutation flow. The battle engine is a temporary encounter simulation that may read party data, mirror some of it into battler state, and later commit results. Same creature data foundation, different layer of authority.

On the board, under “battle resources,” I’m going to write activeHP, selectedAction, selectedMove, selectedTarget, turnOrder, battleResult. Under “turn execution,” I’m going to write currentBattler, currentMove, currentTarget, scriptPtr, phase. The reason I separate selectedMove from currentMove is not aesthetic. It prevents a category error. selectedMove is what a battler chose. currentMove is what the engine is now resolving. Usually they match. Sometimes they do not. If a move calls another move, if an item changes the action, if a status condition prevents action, if a script redirects execution, you need a current move concept that can diverge from the original choice.

For our lab, the script pointer placeholder can be very small. It can be an enum phase. It can be a function pointer. It can be an index into a tiny array of script steps. The important thing is that move execution is not buried directly inside the turn-order loop. You do not want the loop to say, “for each battler, subtract damage and print text and check fainting and end the battle.” That works only until the first effect needs to pause for text, wait for an animation, branch on a miss, or run end-of-turn damage.

A minimal lab version can speak in phases: begin action, announce move, apply effect, check fainting, finish action. That is enough to teach the architecture. Later, each phase can become a real battle script command. The research packet’s emphasis on FireRed as “data table to runtime globals to callback/task/script to player-visible behavior” is exactly the habit I want you using here. The selected move comes from data. The battle globals say who is acting. The script or phase system coordinates effects. The controller and sprite layer makes it visible.

Let’s compare that briefly with Pokemon Showdown, because we used it in earlier assessments as a secondary comparison, not as the main authority. In pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f, damage calculation is organized around active move objects, immunity checks, fixed damage cases, damage callbacks, and base power logic. That is a simulator-style model: highly testable, data-rich, and server-oriented. FireRed’s battle engine is constrained by a handheld runtime and a callback-driven presentation loop. So we learn a principle from Showdown, but we do not replace the FireRed architecture with it. The principle is that move identity and move resolution are separable. The FireRed lesson is that resolution must cooperate with frame-by-frame state, scripts, controllers, and shared globals.

Now let’s walk through one complete lab turn at the level of state transitions. The battle begins the turn in input collection. Both battlers need a chosen action. In your two-battler lab, the player action can be hardcoded or read from a tiny menu, and the enemy action can be chosen by a simple AI that always selects move zero. Once both entries are filled, you enter order construction. You compare speed, break ties deterministically or randomly, and write turnOrder. Then you set turnIndex to zero. You do not execute from the battler array directly. You execute from turnOrder[turnIndex]. That lets the schedule remain explicit and inspectable.

The first execution step sets currentBattler to turnOrder[0]. Then it reads selectedAction[currentBattler]. If the action is fight, it sets currentMove and currentTarget. Then it sets scriptPtr or phase to the beginning of the move script placeholder. The phase runner advances one small step at a time. Maybe on frame one it emits “Rattata used Tackle.” Maybe on frame two it applies damage. Maybe on frame three it checks whether Charmander fainted. In a text-only lab, those frames can be loop iterations. In a graphical version, they become actual callbacks over time.

Here is the check for understanding I would ask in the room: why should the phase runner not directly increment turnIndex before fainting checks finish? Take a second. The reason is that fainting is part of the action’s consequences. If you advance the turn cursor too early, you can begin resolving the next action while the previous action’s result is not fully committed. In a real engine, that creates visible bugs: HP bars lag behind state, fainted sprites remain targetable, after-move abilities or scripts run in the wrong order, and result flags are set too late.

This is why BattleResults belongs in the broader battle resource layer, not in the individual action phase alone. pokefirered/include/battle.h chunk_825c6c75270bc90c is important because it places battle resources and battle results in the same architectural conversation. A battle result is not merely “who fainted on this frame.” It is the accumulated outcome that other systems will read after battle control returns. Did the player win? Did the player lose? Was the battle escaped from? Was a monster caught? Was a trainer defeated? Those are not presentation details. Those are return values from the battle subsystem to the overworld and persistence layers.

Let me connect that to the earlier map and script material, briefly. We already covered map headers, object events, and script execution earlier in the course, so I am not going to rebuild that. But remember the ownership direction. The map and script layer may start a trainer battle. It may pass trainer identity or battle setup information into the battle system. After the battle, it may inspect the result and update flags. It should not step through the opponent’s turn. Conversely, the battle engine should not decide which map object disappears permanently after victory except by returning results or setting agreed-upon flags through the proper pathway. That separation is what keeps a creature RPG from becoming one giant function named “play game.”

Now, item use is the place where this gets uncomfortable. Suppose the player chooses Potion instead of Scratch. chosenAction[0] is item. selectedItem[0] is Potion. selectedTarget[0] might be the player’s active battler. In a naive model, items belong to inventory, so you might say, “just subtract one Potion from the bag and heal the party member.” But in battle, the action has turn cost, ordering rules, legality rules, text, animation, and result interactions. So item use crosses subsystem boundaries. It reads or mutates inventory, it mutates battler HP, it may later commit party HP, and it consumes a turn action. That does not mean inventory owns battle. It means battle execution calls into item behavior through a controlled interface.

In the minimal lab, implement Potion as an action type that heals activeHP by a fixed amount and decrements a temporary item count. You do not need a full bag menu. But you do need the architectural shape: chosenAction says item, currentBattler identifies the actor, selectedItem identifies the item, scriptPtr points to an item-use placeholder, and the battle resources update HP and item count. Later, when we return to inventory and save persistence in the final module, this same shape will let you decide what must be committed to persistent state.

Let’s also name the sprite-facing state, because advanced students often skip it in prototypes and then have to carve it in later. The battle core should be able to say, “play move animation,” “update HP bar,” “show message,” “wait for controller acknowledgement,” or “mark sprite fainted.” It should not know the exact tile offset or OAM details for the sprite. FireRed has controller and sprite-facing structures because the engine has to coordinate logical state with a display pipeline. We are abstracting that in the lab, but we are not pretending it does not exist. A text log is already a presentation-facing layer. It is just the cheapest one.

So a clean lab design might have a battle state object with battlers, choices, order, current fields, and result. It might have a tiny party model outside that battle state. It might have a function that initializes battle battlers from party slots. It might have a function that commits the battle outcome back to party slots. That commit function is deliberately boring. It copies HP back. It records fainted state. It maybe records experience later, but not this week. Boring commit functions are a good sign. They mean the simulation and persistence boundary is visible.

Now, one more subtlety: do not store everything everywhere. If party slot zero owns the permanent creature, and battler zero is the active battle participant, you do not need five different permanent copies of the same speed value. You can copy what the battle needs for fast access or temporary modification, but you should know whether each field is authoritative or cached. For example, activeHP in battle may be authoritative during battle. Party HP may be stale until commit, or it may be updated immediately depending on your design. Both models can work in a small clone, but mixing them without a rule is what causes bugs.

Let’s do a quick diagnostic. If after a Potion you update partyHP but the damage system reads activeHP, what happens? The display might show healing in one place but the next damage calculation still uses the old activeHP. Or the reverse: the battler survives in battle but the party screen after battle says it fainted. That is not a math bug. That is an ownership bug. The fix is not “add more if statements.” The fix is to choose the authoritative field for the duration of battle and commit across the boundary deliberately.

This also previews save persistence. Save does not serialize your current scriptPtr in the ordinary battle lab model. Save serializes durable progress. Party state, inventory state, flags, trainer defeated state, map position, and so on. If your clone allows saving mid-battle, that is a more complex design, but FireRed-style course architecture for us treats battle as a mode that returns a result to the larger game. The result is what becomes durable. That is why the battle result structure matters as an interface, not just as a convenience variable.

Before we move on, let’s recap the lab target in spoken form. You will build a two-battler battle loop. Each battler has a chosen action. The engine waits until choices are present. It builds a speed-ordered turn array. It sets current battler from that array. It resolves the selected action through a small phase or script placeholder. It validates that the actor and target are still legal. It applies effects. It records fainting and battle result. It advances to the next scheduled battler only after the current action’s consequences are complete. Then, after chosen actions are exhausted, it runs any end-of-turn placeholder effects before returning to input collection or ending the battle.

And I want you to hear the phrase “after chosen actions are exhausted.” That is not decorative. End-of-turn behavior is one of the clearest proofs that the turn is larger than the action list. The actions are the middle of the turn, not the whole turn. Weather, poison, binding damage, delayed effects, residual healing, and other mechanics live after selected actions. We are not implementing all of that in the lab, but we are reserving the slot. Pokemon Showdown’s move text data, for example pokemon-showdown/data/text/moves.ts chunk_92a6298340525a85, includes descriptions of effects that apply at the end of each turn. That is useful supporting evidence for the general battle design principle: a turn loop needs phases beyond “each battler uses one move.”

So when you implement the placeholder, do not call it “doPoisonMaybeSomeday.” Call it something general like endTurnPhase or runResidualEffects. Today it may do nothing. Architecturally, it is a real part of the loop. A no-op phase is still a design claim.

Now, let me give you one final concrete example before we stop this segment. Charmander chooses Scratch. Rattata chooses Potion, because let’s allow the enemy to use an item for demonstration. Rattata is faster. The order array says Rattata, then Charmander. The engine sets currentBattler to Rattata. It sees item action. It sets currentItem to Potion, currentTarget to Rattata, and phase to item announcement. The item phase heals Rattata and decrements the item count. Then it checks whether the battle ended. It did not. Now turnIndex advances. currentBattler becomes Charmander. Before Scratch runs, the engine checks Charmander is not fainted. Charmander is alive. It checks the target, Rattata, is targetable. Rattata is alive. It loads Scratch as currentMove and runs the move phase. Scratch deals damage. Rattata faints. The faint check sets battleResult if that was the last opposing battler. Now the engine does not blindly run end-of-turn poison against a battle that is over unless your rules say it should. It enters the battle-end transition and returns a result to the caller.

That little story contains nearly all of week 13’s architecture. Chosen actions are records of intent. Turn order is a schedule. Current battler is a cursor. Current move is execution state. Script pointer is resumable control. Battle resources are temporary shared state. Battle result is the return value to the larger game. And party, inventory, map scripts, controllers, sprites, and save persistence all touch the battle, but none of them should erase its internal structure.

We will pick up from exactly there by translating this spoken model into the lab skeleton, and I am going to be strict about naming, because in this topic names are not cosmetic. A variable named currentBattler, chosenBattler, activeBattler, and partyIndex can all point at numbers, but they do not mean the same thing.

So, let’s put the lab skeleton on the board, but let’s do it in the same architecture language we have been using all week. I am not going to start with animation. I am not going to start with damage math. I am not even going to start with move effects. I want the core loop first, because if the loop is wrong, every rich feature you add later will attach to the wrong surface.

I’ll write this as five names across the top of the board: chosen actions, turn order, current battler, current move, script pointer. Now, notice the order. Chosen actions come before turn order. That is not just a coding convenience. That is the distinction between collecting intent and scheduling execution. If the player chooses Fight and selects a move, that is intent. If the opponent AI chooses a move, that is intent. If an item is selected, if a switch is selected, if a forced action is inserted by the engine, those are all intents. Turn order is the next layer. The engine asks, given these intents and given the battle rules, who gets processed first, second, third, and so on?

That distinction is visible in FireRed’s battle architecture. When we look at pokefirered/src/battle_main.c chunk_a15739d3170f73c1, the important architectural idea is not merely that there is a variable somewhere for an action. The important idea is that action selection and action execution are separated by turn-flow state. The battle has to know what each battler intends to do, and then it has to walk those intentions in a governed order. And when we pair that with pokefirered/include/battle.h chunk_825c6c75270bc90c, where BattleResources and BattleResults help describe the shared temporary battle state and the return surface of the battle, you can see why our clone has to preserve the shape even if it simplifies the content.

For the lab, your minimal version can be very small. Two battlers. One player creature. One enemy creature. Each battler has speed, hit points, a chosen action, maybe a selected move ID, and a flag for whether it has fainted. The turn-order array can have exactly two entries. The script pointer placeholder can just be a string, an enum, or an integer label for now. But the relationship between these pieces has to be real. Do not collapse everything into one function called doBattleTurn that reads input, decides order, applies damage, prints messages, checks victory, and returns all in a single blur. That might work for the first demonstration. It will betray you by the third feature.

Let me give you a concrete example. Suppose we have Embermouse versus Bulbtoad. Embermouse has speed 15 and uses Scratch. Bulbtoad has speed 10 and uses Growl. The chosen action array might say battler zero chose action use move, move Scratch, and battler one chose action use move, move Growl. Then the order builder reads those records and writes turnOrder[0] equals battler zero, turnOrder[1] equals battler one. Now execution begins. currentBattler becomes battler zero. currentMove becomes Scratch. scriptPointer becomes something like SCRIPT_MOVE_START, or if you are using a function pointer model, it points to the next little procedure in the move execution chain. Scratch resolves. Damage is applied. The script advances through message, animation placeholder, damage application, faint check, and completion. Then currentBattler becomes battler one. currentMove becomes Growl. The script pointer resets or moves into the next script sequence. That is the essence.

Now, here is the check for understanding. If Bulbtoad faints from Scratch, do we delete Bulbtoad’s chosen action from the chosen action array? No. We generally do not need to erase the historical intent. We skip execution because the battler can no longer act, or we mark the action as cancelled, depending on the design. That is a different operation. The action record says what was chosen. The execution guard says whether it can still happen. That is the kind of distinction advanced engine code depends on.

And this is where names matter. If you call the loop variable selectedBattler, you may accidentally use it both for “the battler whose command is currently being selected” and “the battler whose action is currently resolving.” Those are not the same thing. In FireRed, the global battle state has many battler-facing variables because the system is coordinating controllers, sprites, scripts, and battle logic. The declarations and shared surfaces in pokefirered/include/battle.h chunk_8367bbcb71bfc88d are a reminder that the battle engine is not a pure local function. It is a frame-by-frame system, with global state used as coordination state. That does not mean we should casually make everything global in our clone. It means we should understand why globals exist in the original: they are the common language among modules that do not all execute at the same time.

Let’s pause on that for a second, because this is one of the easiest mistakes to make when building a teaching clone. A beginner clone often says, “Globals are bad, so I will pass everything through parameters.” A beginner clone on the other side says, “FireRed uses globals, so I will make everything global.” Both are too simple. The better answer is to ask what lifetime and ownership the value has. A party slot belongs to the party data. We built that foundation earlier when we studied party ownership and summary screen reads, and pokefirered/src/pokemon_summary_screen.c chunk_e8e17a42bbf17d46 was useful precisely because it showed party data being read by a UI surface without making the summary screen the owner of the creature. Battle temporary state belongs to the battle session. Save data belongs to persistence. Map header data belongs to the world and map-loading layer. A global can be an acceptable implementation mechanism for session-wide coordination, but it should not confuse ownership.

So, in your lab, I would accept a BattleState object that contains battlers, chosenActions, turnOrder, currentBattler, currentMove, scriptPointer, result, and phase. That is the clean modern teaching version. If you want to imitate the GBA style more closely, you could make a few module-level variables, but still keep the conceptual boundaries. What I do not want is a Battler object that owns the save file, the map, the bag, the battle result, the script VM, and the screen. That is not architecture. That is a junk drawer.

Now let’s connect this to map loading, script execution, item use, and save persistence, because today’s objective is not “write a battle loop in isolation.” The larger objective is to design minimal clone versions of the full creature RPG path. Think back to our data-table-to-runtime-global-to-callback-or-script-to-visible-behavior trace. We have used that trace since the early weeks. The research synthesis for the course keeps emphasizing FireRed as cooperating state machines plus data tables, and that is exactly the lens we need here. The battle engine is one state machine, but it is entered from the overworld, influenced by party and bag data, and eventually returns a result that the caller can interpret.

Imagine the player steps on a grass tile. The map loader has already loaded the current map header, layout, encounters, and object events. We are not re-teaching map headers today, but remember the prior point: map data is not gameplay by itself. It becomes gameplay when the overworld runtime interprets it. A field script or encounter check says, “Start a wild battle.” That setup selects a species, creates or references a temporary enemy battler, and enters the battle main callback. That callback structure ties directly back to the runtime shell material from week 1 and week 2: AgbMain, SetMainCallback2, and the task loop were not trivia. They explain how a whole mode of gameplay can occupy the frame loop. The research packet flags pokefirered/src/main.c chunk_b35676322b169f85 for AgbMain, pokefirered/src/main.c chunk_6cfd90f6d0136799 for SetMainCallback2, and pokefirered/src/task.c chunk_3ffebc0341225936 for RunTasks. We are now seeing why those earlier mechanisms mattered.

In the clone, map loading can be minimal. A map record has an ID, a width, a height, a tile grid, maybe a list of script triggers, and maybe an encounter table. Script execution can also be minimal. A script pointer can point into a list of commands like show text, give item, start battle, set flag, warp, end. Party data can be minimal. A party is an array of creature records. Item use can be minimal. A bag is a dictionary of item IDs to counts, and item effects are functions or table entries. Save persistence can be minimal. Serialize map ID, player position, flags, party, bag, and maybe play time. But the battle turn loop must fit into that world. It is not a toy floating next to the RPG. It is a mode entered by scripts and exited with a result.

Let me mark this on the board as a flow: overworld callback, script command start battle, battle setup, battle main callback, action selection, turn execution, battle result, return callback, script continuation. That is the whole course in one line. And what I want you to notice is that “battle result” is not just a message saying “you won.” It is a value the surrounding game can use. Did the player win? Lose? Run? Catch the creature? Should the trainer flag be set? Should the party be healed somewhere else? Should the map music resume? Should a post-battle script continue? FireRed’s battle header surface, especially pokefirered/include/battle.h chunk_825c6c75270bc90c with BattleResults, gives us a concrete source-backed reason to design that return value instead of letting battle code directly mutate every possible caller.

Now, let’s talk about item use inside the turn system, because this is where a lot of clone projects quietly become inconsistent. If the player uses a potion from the battle menu, is that just an immediate bag operation? Well, partly. The bag count must go down. The target’s HP must go up. But in a turn-based battle, item use is also an action. It occupies the player’s turn. It may have priority relative to moves. It may be disallowed under certain rules. It may trigger messages and animations. So the chosen action should be action use item, with item ID and target battler or party slot. The bag system owns the inventory count, but the battle action owns the timing. Those are separate responsibilities.

Here is the example. Embermouse has 3 HP left. The player chooses Potion. Bulbtoad chooses Tackle. The chosen action array says battler zero use item Potion on battler zero, battler one use move Tackle. The order builder applies the rule that item actions happen before normal moves. So currentBattler becomes battler zero, currentItem or currentActionDetail is Potion, and the script pointer enters the item-use script. The item effect calls into inventory or receives a validated item effect from inventory, decrements the count, heals Embermouse, prints the message, and completes. Then currentBattler becomes battler one and Tackle resolves. If Tackle still knocks Embermouse out, that happens after the potion. If the potion allowed Embermouse to survive, the architecture supported the rule because item use was scheduled, not improvised.

Check yourself here. If you decrement the potion the moment the menu cursor touches Potion, what goes wrong? You have coupled UI preview to commitment. If the player backs out, the item is already gone. If action selection is interrupted, the inventory has mutated before the battle turn owns the action. The cleaner point of mutation is when the item action is committed or when it executes, depending on the exact rule set you are modeling. For our lab, I want the mutation at execution time, because it makes the timeline visible.

Now, how does the script pointer fit into this? In the original GBA-style battle engine, a script pointer gives the system a resumable way to process complex battle events across frames. We are simplifying, but we should not delete the concept. If a move does five things, the engine may not want to do them all as one invisible function. It may need to wait for text, wait for an animation, wait for a controller response, update sprites, then continue. This is why the battle script concept is so central in the shared battle declarations in pokefirered/include/battle.h chunk_8367bbcb71bfc88d. The script-facing state is a control surface. It lets battle logic become a sequence.

In your lab, your script pointer placeholder could be comically simple. It could be an enum with values like StartAction, ShowUseMessage, ApplyEffect, CheckFaint, EndAction. Each frame, or each call to stepBattle, runs one script stage and advances the pointer. If you are writing a command-line clone, you might run the whole script immediately, but still model the stage variable. That way, when you later add text boxes, animations, or asynchronous controller messages, you have somewhere to attach them. This is the same lesson we saw in the move relearner menu state machine, and pokefirered/src/learn_move.c chunk_af3598df6f092723 was useful there because it made menu progress explicit. We are not re-teaching that menu, but the pattern carries forward: explicit states make delayed interaction manageable.

Let me say that another way. A script pointer is not there because C programmers enjoy indirection. It is there because game behavior unfolds over time. If you do not represent time in the architecture, time leaks into random flags, nested callbacks, and special cases.

Now we need to handle current move. Why not just read chosenActions[currentBattler].move every time? In a two-battler lab, you can. But currentMove is still a useful execution state, because the move being executed can be transformed, copied, replaced, disabled, or interpreted through a move table. Even in a minimal engine, setting currentMove at the start of action execution makes the code’s intention clear. We are saying, “The action record selected this move; now this move is the active executable move.” That allows the move script to refer to currentMove without caring where it came from.

Pokemon Showdown is not our primary architecture source, but it is a useful comparison for battle data hooks and deterministic testing. The research packet points to pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f, where move damage calculation is routed through structured move properties and callbacks such as damageCallback, fixed damage, and immunity checks. We are not porting Showdown into FireRed. But we can learn from the same separation: a selected move is data plus behavior hooks, and the battle runtime asks the move what kind of effect it has. In FireRed, the implementation style is different and much more GBA-era, but the design pressure is similar. Moves are not just numbers. They are entries into a rule system.

For the lab, maybe moves have ID, name, power, accuracy, priority, and effect type. Scratch has power 40, priority 0, effect damage. Potion is not a move, but an item action with priority above moves. Growl has power 0, effect lower attack. That is enough. When the current move resolves, the move executor can read the move table and apply the correct script path. If you later add Quick Attack, the order builder can sort by priority before speed. If you later add Protect, the action can create a temporary status before the opponent’s damage script checks it. If you later add faint replacement, the battle result and controller state can request a switch. The minimal architecture can grow because the names already mean the right things.

Let’s now talk about battler globals and sprite-facing state. In FireRed, battlers are not just party slots. A battler is a combat participant with a position, side, controller, sprite, selected action, current status, and links back to party data. Earlier we covered battler positions and actions, and I do not want to repeat that lecture. The point for today is that the battle engine often needs a current battler index because many systems need to agree on whose turn, whose sprite, whose controller, whose health box, whose move animation, and whose party data are being referenced. A current battler global is a coordination cursor.

In a clone, if you are drawing to screen, currentBattler might determine which sprite shakes, which health bar animates, and which text color or name appears. If you are not drawing yet, you should still keep the value. Print “Embermouse used Scratch” by reading the battler name through currentBattler. Apply damage to the target selected by currentBattler’s move. Store lastDamage maybe. Update fainted flags. Then advance. That is the thin version of sprite-facing state. Later, sprite state can subscribe to or read from the same execution state.

Here is a common bug. A student writes executeMove(attacker, defender, move), and inside that function directly prints messages, subtracts HP, checks fainting, and chooses the next battler. It looks neat because the function has parameters. But then they add a move that hits both opponents, or an item that targets a party slot, or a switch action, or an animation that needs to know the attacking sprite after damage but before faint. Suddenly attacker and defender are not enough. The FireRed-style architecture looks heavier at first because it keeps shared battle state, but that shared state exists because the battle is a coordinated process, not a single mathematical operation.

Now, to be clear, I am not telling you to overbuild. For this lab, do not implement full multi-battles. Do not implement every move effect. Do not implement the actual FireRed script bytecode. I want the minimal clone version. But minimal means the smallest faithful shape, not the fewest lines of code. A faithful two-battler loop has separate collection of actions, order construction, execution cursor, effect resolution, and result production.

Let’s walk one full turn in slow motion, because this is the part I want you to be able to write from memory.

At the start of the battle phase, the battle state phase is SelectActions. The player controller is asked for an action. In a command-line lab, maybe this is a prompt. In a test, it is predetermined input. The enemy controller chooses through a simple AI, maybe always first move. Those choices fill chosenActions[0] and chosenActions[1]. The phase becomes BuildTurnOrder. The order builder reads each chosen action. It gives item actions high priority, move actions their move priority, switch actions perhaps higher priority, and then breaks ties by speed. It writes battler indices into turnOrder. It sets turnIndex to zero. The phase becomes ExecuteTurn.

Now currentBattler becomes turnOrder[turnIndex]. The engine checks whether that battler can act. Is it fainted? Is the action cancelled? Is there a forced condition? In our minimal clone, fainted is enough. If it cannot act, increment turnIndex. If it can, load the chosen action. If it is a move, set currentMove. Set scriptPointer to MoveStart. Then step through the script. Show message. Apply accuracy if you have it. Apply damage or effect. Check faint. Mark result if battle is over. End action. Then increment turnIndex. If turnIndex reaches battler count, phase becomes EndTurn. EndTurn could process poison, weather, or nothing in the lab. Then phase returns to SelectActions unless result is no longer None. If result is Win, Lose, Run, or Caught, phase becomes BattleEnd and the battle returns to the overworld caller.

That is the lab. It sounds almost too plain, but it is the exact structure that lets a battle engine become a battle engine.

Let’s connect save persistence now, because save files are where ownership mistakes become permanent. During battle, HP changes to the player’s party may need to persist after battle. Enemy temporary data usually does not. Bag item counts used in battle should persist. The map position usually should not change during battle unless the result triggers a warp or scripted movement after returning. Flags for defeated trainers should usually be set by the overworld script after the battle result, not by the battle damage loop itself. So in the clone, saving should serialize the durable game state, not the whole temporary BattleState unless you are intentionally supporting mid-battle saves.

If you serialize currentBattler, scriptPointer, turnOrder, and chosenActions into your ordinary save file by accident, you are probably mixing session state with persistent state. Some games do support saving during battle, but then you must deliberately design battle persistence. FireRed’s BattleResources idea, again visible in pokefirered/include/battle.h chunk_825c6c75270bc90c, helps us reason about that boundary. Temporary battle resources are not the same as the save block. They can read from durable systems and write back selected outcomes, but they are not automatically durable themselves.

Here is the board cue I would put in a box: persistent state survives mode changes; battle session state coordinates the current mode; frame state coordinates what happens right now. Party data and bag counts are persistent. Battle resources are session state. currentBattler and scriptPointer are frame-to-frame execution state inside the session. If you keep those categories separate, your clone will be much easier to extend.

Let me test that with a scenario. The player enters a trainer battle. During the battle, the player uses a potion. The player wins. What persists? The potion count should persist. The party’s new HP and experience changes should persist if your clone implements them. The trainer defeated flag should persist, but preferably because the post-battle script sets it after reading the battle result. The enemy battler’s temporary HP does not need to persist. currentMove does not persist. turnOrder does not persist. The battle text script pointer does not persist. Good.

Now suppose the player loses. What persists? That depends on your game rules. In a Pokemon-like structure, party HP may persist as fainted, money may change, and a warp to a healing location may happen. But again, the battle loop should not become the whole world. It returns Loss. The caller or a higher-level battle-end handler applies the world consequence. This is the meaning of a battle result as a contract.

We can now weave this back to the week 13 source focus. pokefirered/src/battle_main.c chunk_2017de6d7013d2b7 and chunk_568e3366056a9ce4 are useful because they show battle main flow and battle state progression as coordinated state, not as a single pure calculation. chunk_a15739d3170f73c1 is the one I want you to mentally attach to turn and action flow. The include file chunks, pokefirered/include/battle.h chunk_825c6c75270bc90c and chunk_8367bbcb71bfc88d, are the contract surface: resources, results, scripting, globals. The architecture is distributed across source and header because the battle engine is not private to one function. It is a subsystem.

And that is also why I keep referring to controllers. A controller in this context is not just a physical gamepad. It is the interface by which a battler’s side supplies choices and receives instructions. Player controller, AI controller, link opponent controller, Safari controller, tutorial controller: all of these can present different behavior while the battle loop still says, “I need an action for battler N.” In the lab, you can model this with a function chooseAction(battleState, battlerIndex). For the player, it asks input. For enemy AI, it returns a move. For tests, it returns scripted choices. That is already a controller abstraction.

This is one place where advanced students should be stricter than the average tutorial. If your enemy AI directly calls applyDamage, it is not choosing an action; it is invading execution. If your UI directly subtracts HP, it is not selecting; it is executing. If your save system directly changes the battle phase, it is not persistence; it is control flow corruption. These are the boundaries that keep the engine legible.

Now, I want to give you a small pseudo-implementation verbally, not as code syntax, but as architecture. The BattleState has battlers zero and one. Each battler has name, hp, maxHp, speed, moves, side, partyIndex, fainted. The state has chosenActions length two. Each chosen action has type, moveSlot, itemId, target. The state has turnOrder length two, turnIndex, currentBattler, currentTarget, currentMove, scriptStage, result, and phase. The main function is stepBattle. It looks at phase. If phase is SelectActions, it asks controllers for actions. If BuildTurnOrder, it fills the order. If ExecuteTurn, it either starts the next action or advances the current script stage. If EndTurn, it handles end-of-turn effects. If BattleEnd, it returns the result or signals completion.

Notice that stepBattle can be called once per frame, or once per command-line loop, or repeatedly in a unit test until completion. That is powerful. It gives you deterministic testing. It also matches the frame-based nature of FireRed’s runtime without requiring you to copy every implementation detail. The research packet’s broader course thesis says FireRed should be treated as data tables plus cooperating state machines, and this is exactly what we are doing.

Let’s do one more check for understanding. Where should speed sorting happen? It should happen after actions are chosen and before action execution. Why not during action selection? Because action priority may depend on what was chosen. You cannot fully order the turn until you know whether someone used a priority move, an item, or a switch. Why not during each action execution? Because the schedule should be stable for the turn unless a rule explicitly modifies it. Build the schedule, then walk it.

What about ties? In the lab, you can make ties deterministic by battler index, or you can randomize them. If you randomize them, use the battle RNG path, not the UI path. A deterministic test should be able to control it. This matters later when we discuss regression tests. Pokemon Showdown’s test suite, flagged in the research packet under pokemon-showdown/test with files like pokemon-showdown/test/TESTS.md, pokemon-showdown/test/assert.js, and pokemon-showdown/test/common.js, is useful as a secondary comparison because simulator behavior must be testable. Again, Showdown is not the primary architecture source for FireRed, but it reinforces the value of deterministic turn inputs and assertions.

Now, because this is week 13, I expect your lab work to show maturity from the midterm. The midterm checked whether you could decompose a creature-collector RPG into runtime shell, overworld maps, scripts, party, battle, inventory, UI, save, and link systems. It also checked whether you could trace behavior from data records through globals, callbacks, tasks, scripts, structs, and source functions. So for this lab, a working demo is not enough. I want a small trace. When you submit or discuss your implementation, be able to say: this map script starts the battle; this battle setup reads party slot zero; these controller functions fill chosen actions; this order builder writes the turn-order array; this execution phase updates current battler and current move; this script placeholder applies the effect; this result returns to the caller; this save function persists only durable state. That trace is the assignment’s intellectual content.

Let’s also clarify what not to do. Do not model the entire creature as the battler. The battler can reference a party creature, but it is not identical to the party creature. In a very small clone you might store HP directly on the party creature and let the battler point at it, but conceptually the battler is the battle participant. It may have volatile statuses, temporary stat stages, last move used, turn flags, sprite state, controller state, and position. Party data is the durable creature record. We built this foundation when we studied summary screen reads and party mutation from UI. Carry it forward.

Do not make move execution return directly to map code. Move execution belongs inside the battle session. The battle session returns the result. The map script continues afterward. If a move causes the battle to end, set result and move to battle-end phase. Do not call loadMap from inside Scratch.

Do not let inventory decide turn order. Inventory can validate item count and describe item effects. The battle order builder decides when an item action happens relative to moves. That means the same potion item can be used from the overworld menu with one timing rule and from battle with another timing rule. Same item data, different mode controller.

Do not save everything just because it exists in memory. Persist what the game world needs after shutdown. Temporary execution cursors are not automatically save data.

Let’s now tie this to the broader course plan, because week 13 is not an isolated island. Weeks 12 through 16 are the battle engine architecture module. Week 12 gave us the doorway into battle: battle.h as the subsystem map, battler positions and actions, trainer party structs, wild and trainer battle setup. This week, week 13, gives us the operating rhythm: battle main callback, chosen actions, turn order, resources, globals, current battler, script-facing state. The next layer will deepen specific battle rules, move effects, controller details, and user-facing presentation. Then, as we approach the final part of the course, we will keep reconnecting battle back to persistence, UI, link constraints, and testability.

That is why the lab is small but important. A two-battler turn-order loop is the seed of the whole battle engine. If the seed has the right shape, you can add move priority, switching, items, faint replacement, experience, catching, trainer flags, and battle animations without tearing it apart. If the seed is just a pile of direct function calls, every new feature becomes a patch over a patch.

Let’s finish this part with one final concrete run.

The overworld script says start wild battle. Battle setup creates enemy Bulbtoad and maps player party slot zero to battler zero. BattleState.result is None. Phase is SelectActions. Player chooses Potion on Embermouse. Enemy AI chooses Tackle. chosenActions now contains two records of intent. BuildTurnOrder sees item before normal move, so turnOrder is zero, one. ExecuteTurn sets currentBattler to zero. The selected action is use item. scriptStage is ItemStart. The script prints the item message, decrements the bag count, heals Embermouse, updates the health display state, and ends the action. turnIndex advances. currentBattler becomes one. currentMove becomes Tackle. scriptStage becomes MoveStart. The move script prints the move message, applies damage, checks faint, and ends. EndTurn has no poison or weather in our lab, so it returns to SelectActions. If Embermouse’s HP is now zero, result becomes Loss or the battle requests replacement, depending on the rules you implemented. In the minimal lab, result can be Loss. BattleEnd returns Loss to the overworld. The caller decides what loss means.

That single run touches map script, party data, inventory, chosen actions, turn ordering, current battler, current move, script pointer, battle result, and persistence boundaries. And it does so without pretending we have implemented all of FireRed. That is the discipline of a good clone architecture: simplify content, preserve structure.

So when you sit down for the workshop, do not ask first, “How do I calculate damage?” Ask, “Where does the chosen action live? Who builds the schedule? What is the execution cursor? What state is temporary? What result leaves the battle?” Once those answers are clean, damage is just one effect script among many. The architecture is the part that makes the rest of the game possible.

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