# Full Game Trace from Map to Save

Lecture ID: lecture_w24_d02
Week 24, Day 2

## Objectives
- Use Pokemon Showdown as a secondary comparison for battle data hooks, Dex/mods, simulator turns, and regression testing without replacing FireRed as the main architecture source.
- Connect week 24 material to the overall course plan.

## Transcript
All right, let’s pick up from where Monday left us. Monday we treated battle tests as executable specifications. Today we widen the lens. We are no longer asking only, “Can I prove this one battle mechanic behaves correctly?” We are asking, “Can I trace one player-visible gameplay sequence through the whole machine, from saved bytes, to map load, to script, to battle, to state mutation, and back into persistence?”

And because this is week twenty-four, I want you to notice the difference in standard. In week two, if you could say, “This table points to that callback,” that was useful. In week twelve, if you could trace a script command into a flag update, that was good. Here, at the end of the course, I expect you to carry the whole chain without hand-waving. Not every line of code, no, but every architectural boundary. Data record. Runtime global. Callback or task. Script or battle engine. Result state. Save serialization. That is the level of trace your final architecture submission needs.

Let me put the first pass on the board. Imagine a player loads a save inside a Pokémon Center, walks outside, steps into a trainer’s line of sight, triggers a trainer script, enters battle, resolves one turn, wins, receives money or a flag update, returns to the overworld, and saves. That is our reference sequence. We will refine it all lecture.

Now, before we go into FireRed, let’s place Showdown correctly. Pokémon Showdown is not our main architecture source for this course. FireRed is. FireRed is the cartridge-era creature RPG whose runtime shell, overworld, scripts, battle transition, party data, and save system we are tracing. Showdown is a secondary comparison. It is valuable because it gives us clean, inspectable battle data hooks, Dex and mod layering, simulator turn APIs, and regression tests. But it does not replace FireRed’s architecture. Showdown helps us name some testing ideas more sharply; FireRed gives us the full game system.

That distinction matters today because a full game trace is not the same thing as a battle simulator trace. In Showdown, a test can create a battle directly. The testing documentation in pokemon-showdown/test/TESTS.md, chunk_134ac9f75b9d6477, discusses createBattle, deterministic seeds, and forceRandomChance. That is exactly what a simulator test should do: bypass the lobby, bypass the overworld, bypass cartridge save state, and construct the smallest battle state that exercises the mechanic. In FireRed, by contrast, a real player does not begin at “createBattle.” A real player begins at a booted runtime, a loaded save block, a map header, an object event, a script pointer, and then eventually a battle setup path.

So when we use Showdown today, use it as a comparison microscope. It shows us what clean battle verification looks like. It does not show us how FireRed’s overworld hands control to battle and then resumes.

Let’s start with the front half of the FireRed trace. Suppose the save has already been selected and loaded. At this point, the save system has reconstructed the long-lived state that the overworld needs: party, bag, flags, variables, player location, map identity, money, Pokédex state, and so on. We are not going to re-teach the whole save layout right now, because that was earlier in the module, but I do want to mark the architectural role. Save is not just “write bytes to flash.” Save is the continuity contract between play sessions. If a flag says a trainer has been defeated, if a warp says the player is in a given map group and map number, if the party has changed, then the save system must preserve those values in the form the runtime expects to resume.

In the packet for today, pokefirered/src/save.c, chunk_9900b8674c89af66, is our anchor for save continuation. We will come back to it later in the trace. For now, I want you to write in the margin: save is upstream of overworld construction and downstream of gameplay mutation. That is, save feeds the map load, and save later records the consequences of the map, script, and battle.

Now move one step forward. The player’s location stored in save has to become an active map. FireRed’s overworld map load path is represented for today by pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, specifically LoadMapFromWarp. The name is already telling us something architectural. The system is not merely drawing a background. It is loading a map from a warp context. That means the map identity is tied to a transition record: where am I going, what entrance coordinates apply, what header and layout should become active, and what object events and scripts become relevant?

This is one of the places where a novice trace usually gets too vague. They say, “The map loads.” But in this course that is not enough. Which map? From what source of truth? Into what runtime structures? With which next control path? LoadMapFromWarp is valuable because it gives you a concrete source-level name to pin the transition to. In your final trace, a sentence like “the saved or scripted warp context is consumed by the overworld loading path, with LoadMapFromWarp in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, as the anchor” is the kind of claim I want. It says source, role, and boundary.

Now, check for understanding here. If I ask, “Why is LoadMapFromWarp part of a save-to-map trace even when the player did not just step on a visible door tile?” what is the answer? The answer is that “warp” is not only a player-facing doorway concept. It is a runtime way of representing entry into a map at a coordinate and context. Loading a saved location still needs the same kind of map activation logic: choose the map, establish the player position, load the layout and events, and prepare the overworld callback loop to run. Good. That is the level of abstraction I want you to keep.

Once the map is active, we are back inside the runtime shell you learned much earlier: main callback slots, per-frame task execution, and subsystem callbacks. I will not re-teach AgbMain or SetMainCallback2 today. We have already used pokefirered/src/main.c and the task system enough times that you should be comfortable with the pattern. The important point is that overworld gameplay is not a magical while-loop owned by the map. It is scheduled through the engine shell. The callback gives the broad mode, and tasks or field-control routines do the per-frame work.

So in our trace, after LoadMapFromWarp has done its work, the player is not simply “on the map.” More precisely, the runtime has entered an overworld mode in which input, camera, object events, field effects, and scripts can be processed frame by frame. That is why the earlier weeks mattered. The runtime shell is not trivia; it is what lets the same program be an overworld, then a menu, then a battle, then an overworld again without becoming one unmaintainable giant function.

Let’s make the example concrete. The player loads in a Pokémon Center. The active map has a layout, tile behaviors, object events for NPCs, warp events for doors or stairs, coordinate events, and scripts. The player presses down, then down again, walks to the exit tile, and crosses the threshold. The field-control logic sees tile or event behavior, and a warp transition is prepared. That eventually routes to map loading again, because stepping through a door and loading from save are both map activation problems at the architectural level. Different trigger, same family of machinery.

Now imagine that the destination map contains a trainer object whose event definition says, in effect, “I can notice the player, run a script, and start a battle.” I am simplifying the exact data fields here, but not the architecture. The active map data makes the object possible. The overworld object system makes it visible and interactive. The field or script system decides when its logic should run. The battle setup code then becomes the bridge between the overworld and the battle engine.

For battle setup, the research packet gives us pokefirered/src/battle_setup.c, chunk_494d132a59422c85. Even in the excerpt, the include list is revealing. It includes overworld.h, item.h, load_save.h, safari_zone.h, quest_log.h, script.h, script_pokemon_util.h, event_data.h, event_object_movement.h, field_control_avatar.h, field_player_avatar.h, battle.h, battle_transition.h, and battle_controllers.h. Do you see the architectural message in that include list? Battle setup is a boundary module. It knows enough about the field to leave it cleanly, enough about scripts and event data to interpret the cause of battle, enough about party and items to configure the combatants, and enough about the battle engine and transition system to hand off control.

That include list is not just clutter. It is a map of responsibility pressure. If a module imports overworld, script, event data, battle, transition, and save-adjacent helpers, it is probably sitting at a major runtime boundary. In your final project, when you identify a boundary module, do not only quote a function name. Read its dependencies. The dependencies often tell you what kinds of state are being bridged.

Now let’s compare that to Showdown for a moment. In Showdown tests, as described in pokemon-showdown/test/common.js, chunk_979fde4c3177ea4f, createBattle can be given options including deterministic seed configuration. That is beautifully direct. A test can construct a battle with the desired format, teams, and random behavior. Then pokemon-showdown/test/TESTS.md, chunk_e8da8b9a442469fd, emphasizes making choices and writing RNG-independent assertions where possible. In other words, the Showdown test harness asks: “What is the smallest controlled simulator state that proves this mechanic?”

FireRed’s full trace asks a different question: “What runtime path causes this battle to exist for the player?” Both are valuable. Showdown helps us design precise battle assertions. FireRed forces us to trace the complete game path.

Let’s use a concrete battle turn. Our trainer sends out a Pidgey. The player has a Bulbasaur. The player chooses Tackle. The trainer chooses Tackle. A turn resolves. HP changes. Maybe no one faints. In Showdown, a regression test might create a battle, make choices, and then assert that a Pokémon’s HP changed or that a stat stage has a certain value. The assertion vocabulary in pokemon-showdown/test/assert.js, chunk_26026d44764fa919, includes assertions around species, fainted status, and full HP. The later assert.js chunk, chunk_f2ab14d37f397660, includes stat stage and HP-change assertions. These are good models for how to phrase expectations. You do not want a test that says, “the battle seems fine.” You want a test that says, “this Pokémon is fainted,” “this species is active,” “this HP changed by this amount,” or “this stat stage is now minus one.”

Now translate that idea back to FireRed architecture. We may not have the same JavaScript assertion helper in FireRed, but our analysis can still use the same precision. At the start of battle, what must be true? The party data must identify the player’s active Pokémon. The trainer data must identify the opponent’s party or generated encounter. The battle type flags must distinguish trainer battle from wild battle or special battle. The transition must have moved execution away from field control and into battle mode. During the turn, move choice and AI choice must become battle actions. The battle engine mutates HP, status, stat stages, volatile effects, experience-related state, and result flags. After battle, control must return to the overworld with script continuation or post-battle cleanup.

That is our big mental model: FireRed performs a cross-system orchestration; Showdown provides a clean reference for battle-local controllability.

Let me pause and ask another check. Why would it be a mistake to use Showdown’s createBattle as the main explanation for FireRed trainer battles? Someone tell me. Yes, because createBattle is a simulator construction API. It deliberately skips the overworld, map event, script, transition, save, and cartridge runtime shell. It is excellent for battle mechanic tests, but it does not explain how a Game Boy Advance RPG moves from walking on a map to fighting an NPC and then back to the map. Good.

Now, on the board, I am going to draw this as a horizontal pipeline, but I want you to remember that the implementation is not literally one linear function. First box: saved state. Second: map load. Third: overworld frame loop. Fourth: event or script trigger. Fifth: battle setup. Sixth: battle engine turn. Seventh: result handling. Eighth: script and field restoration. Ninth: save continuation. The line is a pedagogical trace, not a literal call stack.

That distinction is especially important for advanced learners. A full game trace often alternates between call-stack reasoning and state-machine reasoning. If I press A on an NPC, there may be a fairly direct call path into script handling. But if I enter battle, there may be a mode switch, a transition, and later a callback that resumes another subsystem. You cannot represent the whole game as one stack trace because the game frequently schedules work across frames. That is why we spent so much time earlier on callbacks and tasks. They are what make “later” possible.

Let’s now connect this to the course plan. Weeks one through three gave you the runtime and data vocabulary. You learned to stop treating a ROM as a pile of assets and start treating it as a system of tables, callbacks, tasks, and generated data. The middle of the course separated the major domains: overworld, maps, scripts, creature data, inventory, UI, battle, and save. Weeks twenty-two through twenty-four then pulled those domains back together. Week twenty-two asked you to trace data to behavior. Week twenty-three asked you to identify concrete files, structs, functions, constants, data tables, and state variables. This week asks you to verify and integrate. That is why Monday’s battle tests matter, and that is why today’s full trace matters.

By the final assessment, I do not want a catalog. A catalog says, “Here is the battle system, here is the save system, here is the map system.” A trace says, “Here is how this gameplay moment crosses those systems, and here are the source anchors that prove it.” The trace is the professional artifact. The catalog is only raw material.

Let’s return to our example and go slower through the map-to-battle boundary. The player walks into a trainer’s line of sight. At the player-visible level, the trainer turns, an exclamation appears, the trainer walks up, text appears, and battle starts. At the architecture level, several things are being coordinated. The overworld object system must know where the trainer object is and what movement or facing updates are allowed. The event system must know that this object has trainer behavior. The script system must display text and prepare battle parameters. Battle setup must configure battle type, opponent identity, transition behavior, music or visual transition, and then yield control to battle.

Notice that “start battle” is not a single conceptual operation. It has at least three layers. One, narrative and event meaning: this trainer has challenged the player. Two, field runtime meaning: freeze or redirect overworld control, run approach movement and dialogue, transition away from field mode. Three, battle engine meaning: initialize combatants, rules, active battlers, turn state, controllers, and battle outcome state. If you collapse those layers, you lose the architecture.

This is where pokefirered/src/battle_setup.c, chunk_494d132a59422c85, is again useful as a source anchor. Its dependency surface shows that battle setup is not merely battle logic. It is not where we should put every damage formula detail, and it is not merely a script interpreter. It is the adapter between field events and battle runtime. Good architecture traces identify adapters.

Now let’s bring in the assertion vocabulary again, but carefully. In Showdown, a test can assert species, fainted state, full HP, stat stages, and HP changes using helpers from pokemon-showdown/test/assert.js, chunk_26026d44764fa919 and chunk_f2ab14d37f397660. For FireRed, if you are writing an architecture trace rather than an automated test, you can still borrow the discipline. After the battle begins, state your expectations in terms of combat state. “The player active battler is derived from party slot zero unless the game has forced a different lead.” “The opposing trainer’s first party member becomes the enemy active battler.” “After Tackle resolves, HP state changes according to the damage routine, and fainted state remains false if HP is above zero.” These are not vague gameplay descriptions. They are state claims.

And then after the battle result, make state claims outside battle. “The defeated trainer flag is set.” “The script continuation can advance past the battle.” “The player is returned to overworld control.” “The party’s current HP values persist outside battle.” “If the player saves, the mutated party and event flags are written through the save continuation path, anchored by pokefirered/src/save.c, chunk_9900b8674c89af66.” That is how you connect the engine’s temporary battle state to long-lived RPG progression.

Let me give you a bad trace and then improve it. Bad trace: “The game loads the map, the player fights a trainer, then the game saves.” That is true, but useless. Better trace: “Save data supplies the player’s location and long-lived party and flag state. The overworld activates the destination map through the map loading path, with LoadMapFromWarp in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, as the source anchor. The overworld callback and task-driven frame loop process movement and field interactions until a trainer event script triggers battle setup. The battle setup boundary, represented by pokefirered/src/battle_setup.c, chunk_494d132a59422c85, consumes script and event context and configures battle transition and battle engine entry. Battle resolution mutates combat state and outcome state. Post-battle script handling updates flags and returns the player to field control. A later save records the updated party, flags, and location through the save continuation path in pokefirered/src/save.c, chunk_9900b8674c89af66.” That is still compressed, but now it is architectural.

Now, you might ask, where exactly do tests enter this? Because today is not only “draw a trace.” It is week twenty-four, and the week theme is deterministic tests and full system trace. The connection is this: a trace tells you what must be preserved; tests tell you what can be checked. Showdown gives us a particularly clean example of battle tests as executable specifications. pokemon-showdown/test/TESTS.md, chunk_134ac9f75b9d6477, shows the harness orientation around createBattle and deterministic control. pokemon-showdown/test/TESTS.md, chunk_e8da8b9a442469fd, emphasizes makeChoices and assertions that avoid unnecessary dependence on random outcomes. pokemon-showdown/test/common.js, chunk_979fde4c3177ea4f, shows seedable battle creation. Those are not FireRed’s exact tools, but they teach the test design principle: isolate randomness, construct known state, make precise assertions, and preserve replayability.

For FireRed, the comparable discipline might be a scripted emulator test, a debug build, a save fixture, or a human-readable trace with memory watchpoints. The implementation technology differs. The intellectual standard is the same. You need a known initial state, a controlled sequence of inputs or events, observable state changes, and a way to distinguish intended behavior from accidental behavior.

Let’s take the one-turn example. If we were testing Showdown, we might seed the battle, call makeChoices for both players, and assert HP changes. If we were tracing FireRed, we might start from a save fixture at a known map tile, step into a trainer event, record the battle transition, choose Tackle, and then inspect the resulting party HP after battle. The FireRed trace has more systems, more callback transitions, and more persistence concerns. The Showdown test has cleaner simulator control. Both are useful, but they answer different questions.

I want to emphasize this because some of you in the homework have been tempted to write, “Showdown does this better.” That is not an architectural analysis. Showdown is optimized for a server-side competitive simulator with data mods, battle formats, and regression tests. FireRed is optimized for a handheld RPG runtime with maps, scripts, memory constraints, asset tables, link features, field effects, and persistent progression. “Better” is the wrong comparison. Ask instead: what boundary is each architecture making easy?

Showdown makes it easy to instantiate battle state directly. FireRed makes it possible to integrate battle into a spatial RPG adventure. Showdown’s Dex and mods, which we covered in prior lectures, make generation-specific data lookup and inheritance explicit. FireRed’s data tables and C modules make cartridge-era static data and runtime state coordination explicit. Showdown’s simulator turn loop, which we covered last time, makes choice submission and action resolution easy to test. FireRed’s overworld-to-battle path makes it possible for a trainer standing on a map to become a battle encounter and then a persistent defeated flag. Different systems, different success criteria.

Now look back at the board pipeline. Saved state, map load, overworld loop, event trigger, battle setup, battle turn, result handling, field restoration, save. For the rest of this lecture, we are going to walk that pipeline in more detail. We will not try to memorize every function name. Instead, we will practice source-grounded handoff reasoning. At each boundary, I will ask: what state enters, what state is transformed, what module owns the transformation, what state leaves, and what would a regression check assert?

That last question is the new week twenty-four muscle. It is not enough to say, “This system hands off to that system.” Ask what could break. If LoadMapFromWarp mishandles entry coordinates, the player appears in the wrong place. If event flags are not loaded, a defeated trainer can challenge again. If battle setup fails to carry trainer identity, the wrong opponent appears. If battle result handling fails to set the trainer flag, the overworld and save systems disagree with what the player just experienced. If save continuation writes stale party data, the battle “happened” visually but not persistently. A full trace is also a failure map.

And that is where we will go next: we will take the first three boxes, saved state into active map into overworld control, and we will annotate the exact assumptions each box must satisfy before the trainer event can even exist.

So, pick up your pen for a moment, because I want this next part to be almost mechanical. On the board I am going to write three labels: saved state, map realization, overworld control. Those are not three features. They are three phases in which the game changes the meaning of the same data.

Saved state is inert. It is bytes that claim, “The player was here. These flags were true. This party existed. These variables had these values.” Map realization is when those bytes are used to construct an active world. Overworld control is when that constructed world becomes interactive again, frame by frame, through callbacks, tasks, scripts, and input handling. The trace only becomes useful when we refuse to blur those phases together.

Now, FireRed’s `LoadMapFromWarp` in `pokefirered/src/overworld.c`, chunk `chunk_7ea6afd1253d6817`, is a good anchor here because its name is deceptively simple. It sounds like “load the map,” but architecturally it means: resolve the player’s entry context, select the target map, prepare the map header and layout relationship, place the avatar, and get the field runtime into a condition where the normal overworld loop can resume. If that sounds like a lot, good. It is a lot. The function name is a doorway; the trace is what tells us what passes through that doorway.

Let’s make this concrete. Suppose the player saved in front of a trainer on Route 3, turned off the game, loaded the save, took one step, and triggered the trainer’s line of sight. The naive trace says, “Load save, load map, walk, start battle.” That is not wrong, but it is useless for debugging. The architectural trace asks: when the save is loaded, where is the current map group and map number stored? When `LoadMapFromWarp` realizes the map, which warp or saved position decides the avatar’s tile? When objects are instantiated, which event object records are eligible to appear? When the trainer scans for the player, which flag prevents a defeated trainer from re-firing? And when the battle ends, which result path updates that flag before save continuation writes persistent state?

Pause there. Notice the shape of the question. We are not asking whether FireRed has maps, trainers, battles, and saves. We already know that. Week 24 expects you to ask whether the handoff between those systems preserves the right facts.

Here is the board version. I am drawing a horizontal line. On the left: save block state. In the middle: map header, layout, object events, script environment. On the right: active overworld tasks and callbacks. Under the line, I am writing “assumptions.” First assumption: the save data has already been validated or initialized enough that the map identity is meaningful. Second assumption: the map load does not merely decompress visual tiles; it reconstructs gameplay affordances, including object events and scripts. Third assumption: the player avatar’s position is coherent with collision, camera, and event trigger logic. Fourth assumption: event flags and variables are available before event logic tests them.

That fourth one is the one students underweight. A trainer event is not just an NPC with a sprite. It is a conditional behavior over persistent progress. If the flag layer is late, missing, or stale, the map can look correct while its social memory is wrong. The trainer is standing in the right place, the player is standing in the right place, and yet the world has forgotten that the trainer was defeated.

Now, compare this with Pokemon Showdown, carefully, as a secondary comparison. Showdown does not have a Game Boy Advance overworld, a map warp loader, or save continuation in the FireRed sense. But it is extremely useful for isolating battle state assumptions because its tests let us construct battles directly. The testing guide in `pokemon-showdown/test/TESTS.md`, chunk `chunk_134ac9f75b9d6477`, points us toward `createBattle`, seeded setup, and tools like controlled random chances. Then `pokemon-showdown/test/common.js`, chunk `chunk_979fde4c3177ea4f`, reinforces that a battle can be created with options, including deterministic seed behavior. That gives us a contrast: Showdown can start at “battle exists”; FireRed must earn the battle through overworld state, event script state, and battle setup.

So do not use Showdown to replace the FireRed trace. Use it to sharpen a slice of the trace. In FireRed, the question is, “How did the player arrive at a battle from map control?” In Showdown, the question is, “Given a constructed battle, how do we assert the battle changed state correctly?” Different architecture, useful comparison.

Let me say that another way, because it is a common final-project mistake. If your architecture trace says, “We can test FireRed like Showdown by calling createBattle,” you have missed the point. FireRed’s architecture is not organized around a public simulator API. The better statement is: “Showdown shows us what executable battle specifications can look like, and FireRed asks us to build a source trace around the less isolated runtime path that eventually reaches battle.” That is the mature comparison.

Now let’s continue the Route 3 example. The player loads a save. We are not going to re-teach the save system from scratch, but we do need to connect it to today’s path. The save continuation code in `pokefirered/src/save.c`, chunk `chunk_9900b8674c89af66`, matters because persistence is not just a final disk write at the end of a session. Save continuation is part of the guarantee that, after a transition or reload, the runtime can resume with coherent state. In your trace, save code is not a box labeled “storage.” It is the source of truth for the next active world.

So when I ask, “What could break before the trainer event can even exist?” I want answers like this. The current map could be wrong. The current warp could be wrong. The player’s coordinates could be wrong. The map object templates could be loaded but filtered incorrectly. The trainer’s defeated flag could be incorrectly set, causing the trainer not to challenge. Or incorrectly unset, causing the trainer to challenge again. The script context could be uninitialized, so the object exists but cannot run its event behavior. The callback could still be in a transition mode, so input or event checks do not run when expected. These are all architecture-level failures.

Check for understanding. If the screen shows the right map and the right trainer sprite, does that prove the load trace is correct? No. It proves only that some visual and object placement pieces worked. It does not prove flags, variables, scripts, collision, trainer sight, battle setup, or save persistence are coherent. In an advanced trace, visible correctness is evidence, not proof.

Now, I am going to draw a vertical slice through the map object. At the top I write “data record.” Beneath that, “runtime object.” Beneath that, “script behavior.” Beneath that, “battle setup.” The trainer begins as data. A map contains event object definitions. The runtime instantiates or references those definitions under conditions. The trainer behavior uses script and field logic to decide whether contact or sight should initiate interaction. Battle setup then converts that field interaction into a battle entry.

This is where `pokefirered/src/battle_setup.c`, chunk `chunk_494d132a59422c85`, becomes relevant. The research packet excerpt shows that battle setup includes headers for task, overworld, item, sound, pokemon, load save, quest log, script, event data, event object movement, field map, field player avatar, battle, battle transition, and more. That include list is not decoration. It is a dependency map. It tells us battle setup is not a pure combat module. It sits at a boundary where field state, script state, event data, party data, transition effects, and battle constants all meet.

When students ask, “Why is battle setup so tangled?” the answer is: because starting a battle is not only a combat action. It is a world action. The world must pause or transition. The trainer identity must be carried. The party must be available. The script must know what to resume after the battle. The battle transition must take over presentation. The eventual result must feed back into field progress. That is not accidental complexity; that is the cost of crossing a major runtime boundary in a creature RPG.

Now let’s compare one narrow part to Showdown again. In Showdown, from the test perspective, we can construct a battle with known participants, make choices, and assert state. The testing material in `pokemon-showdown/test/TESTS.md`, chunk `chunk_e8da8b9a442469fd`, points to `makeChoices` and RNG-independent assertions. And the assertion helpers in `pokemon-showdown/test/assert.js`, chunks `chunk_26026d44764fa919` and `chunk_f2ab14d37f397660`, give vocabulary for species, fainted state, full HP, stat stages, and HP changes. That is powerful because the test names the expected combat state directly.

FireRed’s full trace needs an equivalent vocabulary, but broader. Not just “Pidgey fainted” or “Charmander lost 12 HP.” It needs “trainer flag set,” “party experience updated,” “script resumed at the correct continuation,” “overworld callback restored,” “player control returned,” and “save contains the post-battle party and event state.” Showdown teaches us the value of precise assertions. FireRed forces us to expand the assertion surface beyond battle.

Let’s do a small imaginary test, spoken as architecture rather than code. Initial condition: player save data says current map is Route 3, player coordinates are one tile below a trainer’s sight line, trainer defeated flag is false, party has one eligible Pokemon, bag state is irrelevant, and no modal menu is open. Action: load save, allow map to initialize, press up once. Expected result: player movement completes, trainer sight logic claims control, script starts trainer encounter, battle setup uses the correct trainer, battle transition starts, and after the player wins, defeated flag is true, party gains any appropriate changes, overworld resumes, and saving afterward persists the result.

That is the lab trace in miniature. The final workshop asks you to produce this for one gameplay sequence: load save, enter map, move player, trigger script, start battle, resolve a turn, award result, update flags, items, party, return to overworld, and persist state. Do you see why the sequence is long? It is long because the game is long-lived state crossing multiple engines.

Now, board cue: I am circling “resolve a turn.” This is where we borrow from the last few Showdown lectures, but only briefly. We have already covered Dex as a battle data facade, generation mods, request state, player choices, and action queue resolution. We are not going to re-teach those. What matters today is that Showdown’s simulator turn loop gives us a clean mental model of battle as a controlled state machine with explicit choices and replayable outcomes. In FireRed, the corresponding path is more embedded in the handheld runtime, but the trace question is similar: what state enters the turn, what choices or commands are accepted, what deterministic or random effects are applied, and what state exits the turn?

The advanced move here is to separate two kinds of determinism. First, test determinism: can I make the same scenario produce the same result repeatedly? Showdown supports this directly through seeded battle setup and controlled chance behavior, as the `TESTS.md` and `common.js` chunks show. Second, architectural determinism: can I identify which subsystem owns each transition so that a bug has a home? FireRed may not expose the same ergonomic test API, but we can still demand an architectural trace that makes ownership explicit.

For example, suppose after battle the trainer remains defeated until you reload, but after reload the trainer challenges again. Where is the bug likely to be? Not in damage calculation. Not in the visual battle transition. Probably in the event flag persistence path, or in the relationship between battle result handling and save continuation. Suppose instead the trainer never starts the battle, but talking manually works. That points away from battle setup and toward field movement, facing, line-of-sight detection, or script trigger conditions. Suppose the battle starts with the wrong trainer party. Now we inspect trainer identity transfer from event object or script into battle setup. That is how a trace becomes diagnostic.

Check for understanding again. In a full-game trace, why do we avoid saying “the battle system updates the world”? Because it hides the contract. The battle engine may compute a result, but field scripts, event data, flags, party mutation, callbacks, and save code participate in making that result part of the world. “Battle system updates the world” is too coarse to test and too coarse to debug.

Let me connect this back to the whole course plan. In weeks 1 and 2, we treated the game as a layered runtime: startup, callbacks, tasks, data tables, generated assets, and C modules. In the middle of the course, we decomposed the creature-collector RPG into maps, scripts, Pokemon data, battle, inventory, UI, save, and link systems. In weeks 22 and 23, we tightened that into source tracing: data records through globals, callbacks, tasks, scripts, structs, and functions. Week 24 is the capstone move. We are no longer satisfied with knowing where the pieces are. We are asking whether the pieces preserve a player-visible fact across the entire program.

That player-visible fact might be simple: “I defeated this trainer.” But the implementation of that fact is distributed. The battle has to happen. The win has to be recognized. The script has to continue. The flag has to be set. The overworld has to reflect it. The save has to persist it. After reload, the map has to honor it. That is why this lecture is called a full game trace from map to save. It is not because maps and saves are the only systems. It is because they are the endpoints of continuity.

Now, one more concrete example before we move onward. Imagine a wild encounter rather than a trainer. The map load assumptions still matter: map identity, terrain, encounter tables, player position, movement state. But the post-battle assumptions differ. A wild battle does not set a trainer defeated flag. It may alter party HP, PP, status, caught Pokemon, item counts if a ball was used, Pokedex seen and caught flags, and money usually not in the same way a trainer battle would. So the trace changes. Same engine family, different persistence contract.

This is why your final architecture trace must name the gameplay sequence precisely. “Start a battle” is not precise enough. Trainer battle, wild battle, scripted legendary encounter, rival battle, Safari encounter, link battle: each one crosses the boundary differently. FireRed’s architecture gives you shared machinery, but shared machinery does not mean identical contracts.

And again, Showdown gives us a useful contrast. Its Dex and mods architecture lets battle data hooks vary by generation or format. The packet includes examples like `pokemon-showdown/data/mods/gen4/items.ts`, chunk `chunk_ef574f63c118f2f6`, where item behavior can inherit and override hooks, and `pokemon-showdown/data/mods/gen9ssb/moves.ts`, chunk `chunk_f562e1983e9fdeff`, where move behavior carries event-style hooks such as prepare, try, and condition effects. Those are battle-data hooks in a simulator-first architecture. FireRed has data-driven behavior too, but the boundary around maps, scripts, and save is much more central to today’s trace.

So the comparison is not “old C game versus modern TypeScript game.” The comparison is: where does each architecture put the hook? Showdown often puts it in battle data and simulator events. FireRed often puts the important continuity hook at the seam between field script, event flags, battle setup, party mutation, and save state. If you can articulate that difference, you are thinking architecturally rather than just cataloging files.

Now, let’s return to our first three boxes and annotate them as preconditions for the trainer event.

Saved state must provide identity, position, progression, and party. Identity means which map and which player context. Position means coordinates and facing or enough related state to restore control correctly. Progression means flags and variables. Party means the Pokemon data that can legally enter battle and later absorb changes.

Map realization must provide geometry, objects, scripts, and camera context. Geometry means tiles and collision, not just art. Objects means the trainer is represented in the field. Scripts means the behavior attached to interaction or sight can run. Camera context matters because the player’s visible and logical position must align; otherwise debugging becomes deeply misleading.

Overworld control must provide input, movement completion, event checks, and callback stability. Input starts the movement. Movement completion matters because tile triggers and trainer sight are generally not just “button down means event.” Event checks decide what claims control. Callback stability means the game is in the correct mode to run these checks instead of being stuck in fade, transition, menu, or script lock.

If any of those fail, the trainer event may not exist as an actionable event even if the sprite is visible. That sentence is worth writing down. Visibility is not actionability. Data presence is not runtime eligibility. Runtime eligibility is not persistence. Each layer earns the next one.

Now I want you to do a quick mental exercise. No laptops, just answer internally. You load a save. The player appears in the correct room. The NPC is visible. Pressing A talks to the NPC. But stepping into the NPC’s line of sight does nothing. Which box do you inspect first? Not save, probably. Not basic map realization, because the NPC exists and interaction script works. I would inspect overworld control around movement completion, trainer sight detection, object facing, and the specific script condition for sight-triggered trainer encounters. If pressing A starts the same trainer battle correctly, battle setup is probably fine. That is how evidence narrows the trace.

Now change the symptom. Stepping into sight starts the encounter, the battle plays, you win, the overworld resumes, but after saving and reloading the trainer battles again. Which box? The early boxes looked good. The failure is in the result-to-persistence path: event flag set timing, save continuation, or the saved flag block. That is where `pokefirered/src/save.c`, chunk `chunk_9900b8674c89af66`, re-enters the story. The bug is not “trainer battle broken.” The bug is “world memory of trainer battle not persisted.”

That distinction is exactly what I expect in week 24 work. The homework and quiz have already checked your ability to use Showdown as a secondary comparison and to trace FireRed through real source structures. The final raises the bar: you must make your trace falsifiable. A falsifiable trace says what state should exist before and after each boundary, and what observation would contradict it.

So, as we move into the next segment, keep the Route 3 trainer in mind. We have now established the preconditions: saved state becomes an active map, active map becomes overworld control, overworld control makes the trainer event actionable. Next, we will follow the moment of capture, where the field runtime gives up control to script and battle setup. That is the point where a visible NPC becomes a combat session, and it is also where many architecture traces become too vague unless we force ourselves to name the handoff precisely.

So let us freeze the picture right at that handoff. The player is standing in the overworld. The map is live. The trainer object is not just a sprite anymore; it is a map object with event identity, position, facing, script attachment, and progression conditions. When the trainer catches the player, the architecture is no longer only “movement code noticed something.” It becomes “field control selected an event, script logic claimed the frame sequence, and battle setup prepared a transition into a different runtime.”

Now, put that on the board as a boundary statement. Before the boundary, we have overworld state: map header loaded, object events instantiated, player avatar controlled by the field loop, flags and variables available to scripts, and save-backed party and bag data sitting in memory. After the boundary, we should be able to name a battle configuration: opponent type, party source, battle flags, transition, music, and the callback path that will eventually leave field rendering and enter battle rendering. That is the level of specificity I expect in the final trace.

FireRed gives us this in pieces rather than in one grand “start RPG encounter” function. That matters. In pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, the source focus is LoadMapFromWarp. That is our earlier boundary: saved location and warp information are translated into an active map context. But when a trainer battle begins, we are not loading a new map; we are temporarily suspending the field system’s normal control. So do not describe trainer battle as “the game loads battle mode” and stop there. That is too vague. Say instead: map runtime remains the origin of the event, script/battle setup constructs the battle, battle transition changes the active execution context, and afterward control must return to the overworld with event consequences applied.

Look at the includes around pokefirered/src/battle_setup.c, chunk_494d132a59422c85. I want you to notice the architecture signal there. That file pulls in task, overworld, item, sound, pokemon, load_save, safari_zone, quest_log, script, event_data, event_object_movement, field control, field player avatar, field screen effect, field message box, battle, battle_transition, and battle communication headers. Even from the include surface, before reading a single implementation body, you can infer that battle setup is not purely a combat module. It is a bridge. It knows enough about the field to be entered from events, enough about scripts and event data to respect progression state, enough about party and items to configure combat, and enough about battle transition to surrender the screen to the battle engine.

That is a useful architectural habit. Sometimes the fastest way to orient yourself in a C codebase is not to search for the most dramatic function name. It is to inspect the include boundary and ask, “Which systems must this file coordinate?” In this case, battle_setup.c is telling you: I sit at the crossing of event scripting, overworld state, player party, transition effects, and battle runtime.

Now, concrete Route 3 example. Suppose our player steps into a trainer’s line of sight. The visible result is familiar: exclamation mark, trainer walks toward the player, dialogue appears, then battle starts. But our trace must split that into state changes. First, detection: some field-side logic has concluded that the trainer event is actionable. Second, script ownership: the player no longer has ordinary free movement, because the trainer encounter sequence has to run atomically enough that you cannot walk away during the challenge. Third, pre-battle script: the trainer’s text and any setup logic execute. Fourth, battle setup: the game identifies which trainer party and which battle type apply. Fifth, transition: the main runtime moves from field presentation to battle presentation. Sixth, battle result returns: win, loss, flee restrictions, payout, trainer flag, party changes, and possibly quest log or script continuation.

Quick check for understanding. If I ask you, “Where is the trainer’s defeated state persisted?” the answer should not be “in the battle engine.” The battle engine may produce the outcome, but the defeated state is progression state. It belongs with event flags or variables that scripts and maps can consult later. That distinction is central. Combat resolves whether the player won. The script and event-data layer decide what that means for the world after combat.

Let me connect this to Showdown carefully, because this is where students often overcorrect. Pokemon Showdown is a very useful comparison for battle determinism and test expression, but it is not our model for FireRed’s whole game runtime. Showdown’s battle object is constructed in a server simulator environment. FireRed’s battle begins as an interruption of a handheld overworld. Those are different outer architectures. But Showdown teaches us a vocabulary for making combat claims precise.

For example, in pokemon-showdown/test/common.js, chunk_979fde4c3177ea4f, the test helper material around createBattle options and deterministic seed exists because tests need reproducible battle worlds. In pokemon-showdown/test/TESTS.md, chunk_134ac9f75b9d6477, the focus on createBattle, seeds, and forceRandomChance shows that test authors are expected to control randomness instead of hoping the desired branch occurs. That is directly relevant to our FireRed trace, even if FireRed itself is not using that JavaScript API. The lesson is: when the architecture contains random encounter rolls, damage variation, critical hits, accuracy checks, or secondary effects, a serious test trace needs either controlled random state or assertions that avoid depending on the random branch.

Now, on the board, I am going to write two versions of a bad and good claim. Bad claim: “The player uses a move and the enemy takes damage.” Better claim: “Given this party state, this selected move, this opponent, and this controlled random path, after the turn the opponent’s HP decreases by the expected range or exact amount, and no unrelated faint flag is set.” That second claim can be falsified. If HP does not change, it fails. If the wrong Pokemon faints, it fails. If the game applies the effect before a required message or before a switch decision, the trace can catch that too.

Showdown’s assertion helpers make this discipline explicit. The research packet points us to pokemon-showdown/test/assert.js, chunk_26026d44764fa919, for assertions around species, fainted, and fullHP, and to pokemon-showdown/test/assert.js, chunk_f2ab14d37f397660, for statStage and HP-change assertions. Again, do not import that design wholesale into FireRed. Instead, borrow the testing posture. FireRed’s combat state is distributed across battle globals, party structures, status fields, script return paths, and save-backed data. But your assertions should still sound like assertions, not like narration. “The lead party member remains species X.” “The opponent trainer battle flag is set after victory.” “The item count decreases by one after use.” “The party’s HP field persists after returning to the overworld and saving.”

Notice what this does to the full game trace. It turns the trace from a story into an executable mental model. The story says, “I fought a trainer and saved.” The model says, “A loaded save provided party and location; LoadMapFromWarp in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, made that location active; overworld control and event object state made the trainer interaction possible; battle_setup.c, chunk_494d132a59422c85, sits at the bridge into combat; combat changed party and progression state; save.c, chunk_9900b8674c89af66, belongs to the continuation of persistence; after reload, the map, party, and trainer-defeated state should agree with the post-battle state.” That is the chain we want.

Let us pause on “should agree,” because that phrase hides many bugs. Imagine a student implementation of a creature RPG. The player defeats the Route 3 trainer. The sprite stops challenging them during the current session, so the student thinks the feature works. Then they save, reload, and the trainer challenges again. What failed? Not the battle turn. Not the dialogue. Not even the local event script during the same session. The failed boundary is persistence of progression state. The in-memory defeated marker did not become save-backed state, or save-backed state did not reload into the event condition used by the map.

This is exactly why week 24 is positioned after the earlier architecture weeks. In week 1, when we discussed AgbMain, callbacks, and the runtime shell, it would have been premature to demand a full Route 3 trace. You did not yet have enough vocabulary. In week 2, when we studied data tables, generated assets, and headers as contracts, we were building the habit of following data into code. In weeks 22 and 23, when your homework and quiz checked whether you could identify files, structs, functions, constants, data tables, and state variables, we were sharpening source navigation. Now, in week 24, the final step is not “find the code.” It is “prove that your explanation survives a complete gameplay sequence.”

That is also why the lab asks you to produce a final architecture trace for one sequence: load save, enter map, move player, trigger script, start battle, resolve a turn, award result, update flags, items, and party, return to overworld, and persist state. Each comma in that lab prompt is a boundary. If your trace skips one, you have likely hidden an assumption.

Let us take the battle turn itself for a moment, but only as much as we need. We already covered Showdown’s request state, player choices, commit flow, and action queue resolution, so I am not going to reteach that loop. What matters today is how it gives us a comparison point. In Showdown, pokemon-showdown/test/TESTS.md, chunk_e8da8b9a442469fd, emphasizes makeChoices and RNG-independent assertions. The practical message is that a test does not merely start a battle; it advances it through selected choices and then checks stable outcomes. That maps neatly onto our FireRed trace. “Start battle” is not enough. You must ask: what is the first committed player action, what opponent action is selected, what state changes after resolution, and which of those changes leave the battle subsystem?

Here is a concrete example. Suppose the player’s Charmander uses Ember against a trainer’s Caterpie. For a deterministic final trace, you do not need to calculate every possible damage formula branch unless the assignment specifically asks for formula verification. You can state the controlled condition. If random variation is fixed, assert exact HP loss. If random variation is not fixed, assert an allowed HP interval and assert that the battle remains in progress unless HP reaches zero. If burn chance is not controlled, do not build the main correctness claim around whether burn occurs. That is Showdown’s lesson from seeds and forceRandomChance in TESTS.md, chunk_134ac9f75b9d6477. You either control chance or you avoid making the test depend on chance.

Now, FireRed’s architecture gives you a different kind of determinism problem: not just battle RNG, but frame-driven execution. Overworld events unfold across callbacks, tasks, scripts, and transitions. A trace that says “then battle starts” may span many frames. So the equivalent of Showdown’s makeChoices discipline is to identify stable observation points. Before trainer detection. After script claim. Before battle transition. At battle main control. After turn resolution. After battle result. After return to field. After save. You do not have to assert every frame. You do have to assert the boundaries.

Let me ask a diagnostic question. If the player loses the battle, should the same postconditions hold as if the player wins? No. Some state changes are shared, like party HP changes during the combat. Some are conditional, like trainer defeated flags and reward payout. Some may be redirected, like blackout handling or map return location. Therefore your trace needs branches. Advanced architecture tracing is not a single happy path with all uncertainty erased. It is a controlled path with named alternatives.

And this is where source evidence protects you from folklore. The research packet highlights save.c, chunk_9900b8674c89af66, as save continuation. We are not going to pretend from that excerpt alone that every exact flag write is visible in the save function. That would be an invented claim. The source-supported claim is narrower and stronger: save.c participates in persistence continuation, so a full trace must eventually cross from modified runtime state into the save system. The event flag may be set elsewhere, the party mutation may occur elsewhere, the bag count may change elsewhere, but persistence is not complete until the save path records the relevant save block state. That is the kind of precision I want. You are allowed to say what a source chunk supports, and you are also responsible for not making it support more than it shows.

Now, let us compare with Showdown’s Dex and mods one more time, but briefly. Earlier we treated Dex as a battle data facade and generation mods as a way to layer rule differences over shared data. That is still useful here because battle setup needs data hooks. FireRed has compiled data tables and constants. Showdown has data modules and modded lookups. In both cases, a move name on screen is the endpoint of a lookup path, not the whole architecture. The difference is that FireRed’s lookup is embedded in a GBA runtime with save blocks and field callbacks, while Showdown’s lookup is embedded in a simulator that can create isolated battles for tests. So when you write your final, you can say, “Showdown would test this battle state with createBattle, deterministic seeds, makeChoices, and assert helpers; FireRed realizes the same gameplay concept through map events, battle setup, battle globals, and save-backed state.” That is a mature comparison. It respects both systems.

Let us do a quick board trace in words. Left column: player-visible action. Right column: architectural claim. “Load save” becomes “save-backed blocks hydrate runtime state.” “Appear on Route 3” becomes “LoadMapFromWarp activates the map context,” supported by pokefirered/src/overworld.c, chunk_7ea6afd1253d6817. “Walk north” becomes “field control updates player avatar and checks metatile or event interactions.” “Trainer spots player” becomes “object event and script logic claim control.” “Dialogue appears” becomes “scripted message state runs inside the field runtime.” “Battle swirl begins” becomes “battle setup and battle_transition coordinate the handoff,” with battle_setup.c, chunk_494d132a59422c85, showing that bridge role. “Move is selected” becomes “battle input is committed into the combat turn.” “Damage happens” becomes “battle state mutates party or opponent HP, with RNG either controlled or bounded.” “Trainer defeated” becomes “progression state changes outside the narrow damage calculation.” “Return to map” becomes “field callback and script continuation regain control.” “Save” becomes “modified state crosses into save continuation,” supported at the persistence boundary by pokefirered/src/save.c, chunk_9900b8674c89af66.

Check yourself against this: if I remove the words “battle” and “map” from your trace, can I still see where the data lives and what function families move it? If not, your trace is probably too narrative. If I remove the player-visible description, can I still tell why the code is doing anything meaningful? If not, your trace is probably too implementation-obsessed. The professional trace keeps both sides connected.

One more example, because this often helps. Suppose after battle the player receives money and an item is consumed during the fight. Your trace should not collapse those into “inventory updates.” Money, bag items, and party HP may be stored in different structures and updated by different systems. The bag item might be decremented during battle item use. Prize money might be awarded in battle result handling or script continuation. Party HP might already be changed before the result screen. The save function does not decide these semantics; it persists the state that those systems have already produced. So a falsifiable postcondition after saving and reloading might be: the trainer no longer initiates battle, the consumed item count remains lower, the party HP reflects the battle result or recovery path, and the player’s location is the expected overworld location. Four claims, four ways to catch an architecture mistake.

That is the real value of deterministic testing as an architectural lens. The test is not only for catching regressions. It teaches you what the system believes its boundaries are. Showdown makes that visible with createBattle, seeded options, makeChoices, and targeted assertions in the test files. FireRed makes it visible through source boundaries: overworld loading, event scripts, battle setup, battle state, and save continuation. Same intellectual move, different runtime shape.

So, as we continue, keep sharpening the verb you use at each boundary. Do not say “the game handles it” when you can say “the script claims control,” “battle setup configures the encounter,” “the transition changes the active presentation,” “the battle result mutates progression state,” or “the save path persists the modified blocks.” Those verbs are not decoration. They are how you prevent your final architecture trace from becoming a plot summary.

Now let’s put that discipline onto the full trace. On the board, I’m going to write the sequence in deliberately plain language first: load save, load map, run overworld, accept movement, trigger script, configure battle, run battle turn, apply result, return to overworld, save. That sounds almost too obvious. But the architecture work begins when we refuse to leave those as story beats. Each phrase has to become a source boundary.

So, “load map” is not a vibe. In our FireRed trace, it points us toward pokefirered/src/overworld.c, specifically the LoadMapFromWarp material in chunk_7ea6afd1253d6817. That is the moment where a warp is not merely a coordinate in the designer’s head anymore. It becomes a concrete load operation that selects map state, prepares the runtime view of that state, and places the player into a world that the callback and task machinery can advance frame by frame. Notice how this connects all the way back to week one. We said early in the course that FireRed is not one giant game loop full of special cases. It is a runtime shell that swaps callbacks, schedules tasks, and lets data-driven subsystems claim control. Week twenty-four is where you prove that thesis with a complete trace.

Now, if I ask you, “Where is the player?” there are several correct answers, depending on which architectural layer we mean. At the save layer, the player is in persisted location data. At the map layer, the player is an object event positioned in a loaded map. At the rendering layer, the player is a sprite and camera target. At the script layer, the player may be an actor whose movement is temporarily constrained. At the battle layer, the player is not walking at all; the party and trainer identity have been projected into battle state. Advanced architecture work means you do not collapse those meanings into one word.

Let’s use a concrete example. Suppose our gameplay sequence is: the player resumes from a saved game in a town, walks north onto a route, steps into a trainer’s line of sight, the trainer script fires, a battle starts, the player uses a move, wins, receives money, a defeated-trainer flag is set, and then the game returns to the route and later saves. If your final project says, “The player loads the game and battles the trainer,” that is a plot summary. If it says, “The save continuation path restores persisted state, overworld loading materializes the current map, per-frame tasks process avatar movement, event scripting claims control when the trainer condition is met, battle setup constructs the encounter, the battle engine mutates party and result state, and save code persists the modified blocks,” then you are doing architecture.

Pause there and check yourself. Can you point to the exact moment where this stops being overworld behavior and starts being battle preparation? Not battle execution yet. Preparation. In FireRed, that boundary is supported by the battle setup module, and the research packet highlights pokefirered/src/battle_setup.c chunk_494d132a59422c85 as a high-value anchor. Even from the include surface alone, that file tells us something important: it sits at a junction. It includes overworld, item, safari, quest log, script, event data, field movement, metatile behavior, field effects, battle, battle transition, and battle communication concerns. That is not random include noise. That is architectural evidence that battle setup is a bridge module. It translates field-world causes into battle-world initialization.

And that is exactly where Pokemon Showdown is useful as a comparison, but only if we keep it in its proper place. Showdown lets us see an extremely clean testing-facing battle boundary. In pokemon-showdown/test/common.js chunk_979fde4c3177ea4f, the course packet points us to createBattle options and deterministic seed setup. In pokemon-showdown/test/TESTS.md chunk_134ac9f75b9d6477, it points us to createBattle, seeds, and forceRandomChance. That testing API lets a test author say, in effect, “Construct a battle under controlled conditions, drive it, and observe it.” FireRed usually does not expose that same clean public simulator API because it is a GBA game runtime. But the idea is transferable: identify the smallest boundary where battle state becomes controllable enough to reason about.

Now, I want to emphasize a subtle point. Showdown’s createBattle is not a replacement model for FireRed’s battle setup. Do not rewrite FireRed in your mind as though it secretly has a modern test harness hiding under it. Instead, use Showdown to ask better questions. What is the FireRed equivalent of battle construction? What data must already exist before that construction can occur? Which globals or structures represent the party, the opponent, the battle type, the terrain or encounter context, and the eventual result? Which module hands control back afterward? Those are the comparison questions. Showdown gives us a crisp vocabulary; FireRed gives us the actual architecture we are tracing.

Let me bring in the assertion side because this is where many final traces become too vague. Showdown’s test assertions are not just convenience helpers. They define an assertion vocabulary for combat state. The packet identifies pokemon-showdown/test/assert.js chunk_26026d44764fa919 for species, fainted, and fullHP assertions, and pokemon-showdown/test/assert.js chunk_f2ab14d37f397660 for statStage and HP-change assertions. That matters because a good architecture trace needs observable checkpoints. After a battle turn, do not merely say “damage is dealt.” Say what changed and where you would observe it. HP changed. A fainted condition may have been reached. A stat stage may have changed. A species identity remains stable even as volatile battle state changes. Those are separate assertions.

In FireRed terms, you will not necessarily write the exact same assertion helpers, but for the lab you should think in that style. After the player’s move resolves, what would you assert? You might assert that the opponent’s HP decreased. You might assert that the party member’s current HP, PP, status, or experience changed. You might assert that the battle outcome variable indicates a win. You might assert that the defeated trainer’s event flag is now set. You might assert that the overworld has resumed with the trainer no longer eligible to challenge the player again. And if the player saves afterward, you might assert that the save continuation path has durable access to those modified blocks. That last part is where pokefirered/src/save.c chunk_9900b8674c89af66, the save continuation chunk in the packet, becomes part of the trace rather than an appendix.

Let’s do a quick check for understanding. If I give you this sentence, “The trainer is defeated, so the route updates,” what is missing? Think it through. The missing piece is the contract between battle result and progression state. “Defeated” is not enough. The game has to represent that fact in some persistent or semi-persistent form, usually through event data, flags, variables, party mutation, money, items, or script continuation. Then the overworld has to consult that representation later. In other words, the route does not update because the story knows the trainer lost. It updates because state changed in a representation the route logic can read.

Now, let’s put Showdown beside that. In Showdown, after you call makeChoices, you can assert against the battle state. The packet flags pokemon-showdown/test/TESTS.md chunk_e8da8b9a442469fd for makeChoices and RNG-independent assertions. That is a very useful phrase: RNG-independent assertions. In the final week, this becomes part of your engineering maturity. A brittle test says, “This exact damage number must happen,” even when the mechanics include a random damage roll. A better test either controls the randomness with a seed or asserts something that should be true regardless of the random branch. Showdown gives us both tools: deterministic setup through seeds, and assertion style that can avoid overfitting to random details.

FireRed’s original codebase was not built around the same kind of JavaScript unit-test ergonomics, but the architectural principle still applies. When tracing FireRed, separate deterministic structure from probabilistic outcome. The map loading path should be deterministic for a given save and warp. The script trigger should be deterministic for a given player position, facing, and event flags. Battle damage may involve randomness, but battle setup has deterministic parts: parties, trainer data, battle type, transition, callback handoff. Save persistence should be deterministic with respect to the modified state. If you mix those together, your trace becomes mushy. If you separate them, your trace becomes testable.

I’m going to write two columns on the board now. Left column: FireRed runtime boundary. Right column: Showdown comparison point. For FireRed map entry, the comparison is not Dex. It is not mods. It is not simulator turns. The comparison is controlled setup. Showdown’s createBattle gives controlled setup for battle; FireRed’s LoadMapFromWarp in pokefirered/src/overworld.c chunk_7ea6afd1253d6817 gives us a controlled source entry into map state. For FireRed battle start, the comparison is Showdown’s battle construction and seed options from pokemon-showdown/test/common.js chunk_979fde4c3177ea4f and pokemon-showdown/test/TESTS.md chunk_134ac9f75b9d6477. For FireRed turn resolution, the comparison is Showdown’s makeChoices flow from pokemon-showdown/test/TESTS.md chunk_e8da8b9a442469fd. For FireRed post-turn inspection, the comparison is the assertion vocabulary in pokemon-showdown/test/assert.js chunks chunk_26026d44764fa919 and chunk_f2ab14d37f397660. For FireRed persistence, the comparison is not Showdown at all, because Showdown battles are usually not responsible for cartridge-style save blocks. There we return fully to pokefirered/src/save.c chunk_9900b8674c89af66.

That final point is important. Secondary comparison means secondary. Showdown is excellent for battle data hooks, generation-aware data, simulator turn tests, and regression vocabulary. It is not our model for map streaming, script-driven overworld progression, or GBA save architecture. If your final trace starts sounding like “FireRed should have createBattle and makeChoices,” you have lost the historical and architectural specificity of the primary source. The right move is, “Showdown exposes this boundary cleanly in tests; FireRed implements the analogous gameplay boundary through callback, script, battle setup, and save code.”

Let’s now walk the sequence at a higher fidelity.

The saved game resumes. At this stage, the architecture is concerned with recovering durable state into runtime memory. We are not going to re-teach the entire save system here, but we do need the verb. The save path persists and restores blocks of state that other systems later interpret. This is why save is not “the end of gameplay.” Save is an infrastructure system underneath every long-lived consequence in the game. Your current map, party, items, progression flags, and many other details matter because the save system gives them continuity across sessions.

Then the map loads. The warp or continuation context determines which map is entered, and LoadMapFromWarp in pokefirered/src/overworld.c chunk_7ea6afd1253d6817 is our source anchor for that kind of transition. At this point, data records become active runtime context. The map header, layout, object events, scripts, and player placement are no longer just static data. They are now the current world.

Then the player moves. This is where earlier weeks about the runtime shell matter. The callback and task system frame the action. We are not reopening the full discussion of AgbMain, SetMainCallback2, task IDs, and RunTasks, because you have already been assessed on that. But you should use it. Movement is not magic. It advances because the active callback allows overworld tasks and field systems to run per frame. The player presses a direction, the avatar system interprets it, movement state advances, collision and metatile behavior matter, and object/event logic gets a chance to react.

Then a script triggers. Here, the architecture changes ownership. The overworld is still visually present, but script logic can claim control. This is one of those places where your verb matters. The script does not merely “happen.” It claims or coordinates control over player input, messages, trainer approach, flags, and the eventual decision to call into battle setup. When we looked at headers as cross-module contracts earlier in the course, this is the kind of moment we were preparing for. The script layer needs callable services from movement, message boxes, event data, and battle setup. It is a conductor, not because it owns all state, but because it sequences state changes across modules.

Then battle setup configures the encounter. Again, pokefirered/src/battle_setup.c chunk_494d132a59422c85 is a good source anchor because it shows battle setup living near overworld, scripts, event data, movement, transitions, and battle. In a trainer battle, setup has to establish who is fighting, under what rules, and how the presentation will leave the field interface. This is a boundary where the system converts an overworld cause into a battle initial condition.

Then the battle transition changes the active presentation. That phrase is intentionally precise. A transition is not the battle mechanics themselves. It is a presentation and control handoff, the player-visible bridge between field and battle. In a polished commercial RPG, this bridge matters because the player experiences the system as continuous, even though the code is crossing a major subsystem boundary.

Then the turn resolves. Here is where our prior Showdown weeks become useful but should not be repeated from scratch. We already covered request state, player choices, commit flow, action queue resolution, and runMove responsibilities. For today, the key is how that material supports full-system tracing. In Showdown, makeChoices in pokemon-showdown/test/TESTS.md chunk_e8da8b9a442469fd is a concise testing surface for committing turn decisions. In FireRed, the player’s selected move and battle commands pass through the battle engine’s own state machine and UI. Different implementation shape, same architectural question: where do choices become committed actions, and where do actions become state mutation?

Then the result is awarded. This is a rich step, not a single variable. A win can mutate party experience, level state, learned moves, money, trainer defeat flags, scripts, item state, Pokédex-related observations, and sometimes quest or event variables. Do not assume every battle touches every category. Your job in the lab is to choose one concrete gameplay sequence and trace the actual categories involved. If your example is a trainer battle with no item reward, do not invent an item update just to make the trace look complete. If your example includes an item given by a follow-up script, then include it and identify the script boundary.

Then the overworld resumes. This is another place students sometimes skip too fast. Returning to the overworld is not simply “battle ends.” The system must restore the field context, allow scripts to continue or finish, update object behavior, and return control to normal movement. A defeated trainer may remain visible but no longer challenge. A one-time item may disappear. A blocking NPC may move. The important phrase is: battle result is reinterpreted by overworld and script systems. State mutated in battle becomes meaningful again only because the overworld reads it.

Finally, the state persists. The save continuation material in pokefirered/src/save.c chunk_9900b8674c89af66 is the correct anchor for talking about this stage. The player’s victory is only durable if the relevant state is included in what save writes and later restores. This closes the loop from runtime behavior back to data. But remember, we are not closing the whole lecture yet; we are closing this pass over the trace.

Let me ask another check-for-understanding question. In the sequence I just gave, where would you put a regression test if you only had time for one? There is not one universally correct answer. If you are testing battle mechanics, you might isolate the battle turn, as Showdown does with createBattle, seeded setup, makeChoices, and assertions. If you are testing game progression, you might test that defeating the trainer sets the correct flag and prevents repeat battle. If you are testing persistence, you might save after victory, reload, and assert that the trainer remains defeated and the party changes remain. The right test depends on the boundary you most fear breaking.

That is why week twenty-four is not just “testing week.” It is architecture week under the pressure of verification. Deterministic tests force you to name initial state, action, and expected state. Full traces force you to name subsystem ownership. Together, they expose vague thinking.

Now, in your final architecture trace, I want you to avoid two common mistakes. The first mistake is over-localizing everything. That is when you say, “This is in battle_setup.c,” as though identifying one file explains the system. It does not. A bridge file is meaningful because of what it connects. The second mistake is over-globalizing everything. That is when you say, “The engine updates the world,” which is true in the same way that saying “the university educates students” is true. It hides the actual machinery. Your target is the middle level: source-grounded verbs attached to module boundaries.

So, for the lab sequence, a strong paragraph might sound like this: after save continuation restores the player’s durable state, the overworld load path materializes the current map through the LoadMapFromWarp boundary in pokefirered/src/overworld.c chunk_7ea6afd1253d6817. The active overworld runtime then advances movement frame by frame until event logic claims control. A trainer script uses event state to determine eligibility, calls into battle setup, and battle setup, anchored by pokefirered/src/battle_setup.c chunk_494d132a59422c85, constructs the encounter and transition into battle. A battle turn mutates combat state; in a testable comparison, Showdown would drive the analogous turn through makeChoices from pokemon-showdown/test/TESTS.md chunk_e8da8b9a442469fd and inspect HP, fainting, species, or stat-stage assertions using pokemon-showdown/test/assert.js chunk_26026d44764fa919 and chunk_f2ab14d37f397660. FireRed then carries the battle result back into progression state, resumes the overworld/script context, and the save path in pokefirered/src/save.c chunk_9900b8674c89af66 is responsible for persistence.

That is the level of specificity I am looking for. Not every noun needs a ten-minute explanation. But every subsystem transition needs a verb and a source anchor.

One final synthesis for this segment before we move to the next layer of the trace: the whole course has been building toward this. Weeks one through three gave you runtime shell and data flow. The middle of the course gave you maps, scripts, creatures, inventory, battle, and UI as cooperating systems. Weeks twenty-two and twenty-three asked you to identify files, structs, constants, functions, and globals with enough precision that you could stop hand-waving. Week twenty-four now asks whether you can make that knowledge executable, or at least test-shaped. FireRed remains the main architecture source. Showdown remains the secondary comparison that sharpens our language around battle data hooks, deterministic setup, simulator turns, and regression assertions. When you combine them correctly, you get a trace that is not merely “what the player saw,” and not merely “what files exist,” but how a creature RPG carries meaning from data, through runtime control, into player-visible behavior, and back into saved state.

So now let’s take the trace one layer deeper. We have been moving left to right, from save state into overworld state, from overworld state into script and battle setup, and from battle result back into persistent state. But for the final architecture trace, I want you to become very precise about what kind of boundary you are crossing at each step. A map load boundary is not the same kind of boundary as a script boundary. A script boundary is not the same as a battle transition boundary. A battle transition is not the same as a save boundary. They all feel like “the game changed modes” to the player, but architecturally they are different contracts.

Let me say that again, because this is one of the mistakes I see in final projects. The player says, “I walked into a trainer battle and then saved afterward.” The architect says, “The engine restored persistent fields, loaded a map from a warp context, resumed the overworld callback and task system, evaluated event objects and script triggers, staged a battle through battle setup, handed control to battle callbacks, mutated party and event state, restored the field engine, and eventually serialized the updated save blocks.” Same gameplay. Different fidelity.

On the board, I’m going to write the phrase “mode transition does not imply ownership transfer.” That is an important sentence. When FireRed moves from overworld to battle, the battle engine does not own the entire game forever. It receives a prepared slice of state, performs a highly structured sequence, and returns consequences to systems that existed before the battle began. The party is still the party. Flags are still flags. The map context is still meaningful. The save system is still the long-term owner of persistence. Battle is a major subsystem, yes, but not a separate game.

This is why the include list in FireRed source files is not just noise. When we looked at battle setup, the research packet pointed us to pokefirered/src/battle_setup.c, chunk_494d132a59422c85, and the excerpt already tells a story before we even reach function bodies. That file includes overworld, item, load_save, safari_zone, quest_log, script, script_pokemon_util, event_data, event_object_movement, metatile_behavior, field control, field player avatar, field effects, battle, battle_transition, and battle-related headers. That is not an accident. Battle setup is a boundary module. It knows enough about the field to leave it cleanly, enough about battle to enter it cleanly, and enough about event data to make the result meaningful afterward.

Compare that with Pokemon Showdown only as a secondary lens. In Showdown, tests can create a battle directly with helper APIs; the test harness is intentionally allowed to bypass the overworld because there is no overworld. The source packet points us to pokemon-showdown/test/common.js, chunk_979fde4c3177ea4f, for createBattle options and deterministic seed, and to pokemon-showdown/test/TESTS.md, chunk_134ac9f75b9d6477, for createBattle, seeds, and forceRandomChance. That is useful to us because it makes battle setup explicit and repeatable. But do not import that mental model backward into FireRed too aggressively. FireRed cannot say, “just create a battle” in a vacuum, because the battle has to be compatible with where the player was, which object started it, which script was running, which flags may be set afterward, which party data exists, and which callback has to resume.

Now, check for understanding here. If I ask, “Where does deterministic battle setup live in FireRed?” the answer is not, “in one convenient test helper.” FireRed’s retail architecture was not designed around unit tests in the Showdown sense. The deterministic trace you produce is reconstructed from real engine boundaries. In Showdown, deterministic setup is a first-class test convenience. In FireRed, deterministic reasoning comes from controlling inputs, reading state, and understanding the chain of callbacks, tasks, scripts, and save blocks.

Let’s make this concrete. Suppose the gameplay sequence is: load a saved game in a town, walk north onto a trainer’s line of sight, start a battle, use Tackle, win, receive money, set the trainer defeated flag, return to the map, and save. The first thing I do in the trace is not open the battle code. I begin at persistence. The save system has restored the party, map position or warp-related context, event flags, inventory, money, and other long-lived state. The source focus for today includes pokefirered/src/save.c, chunk_9900b8674c89af66, labeled as save continuation. I want you to treat that as the end and the beginning of the loop. Save is not merely “write bytes at the end.” Save is the reason the next boot can reconstruct a coherent world.

Then the player enters the overworld. We have a specific source anchor for map loading: pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, for LoadMapFromWarp. We are not going to pretend that one function explains the entire overworld. But it is a very useful anchor because a warp is a compact representation of “where the player should be in the world now.” Map group, map number, warp ID, position, or related context moves from saved or runtime travel state into the active map runtime. That means the map is not just a background image. It is a selected data region plus object events, scripts, connections, metatile behavior, music, and runtime objects.

Notice the phrase I just used: “selected data region.” That should echo the earlier weeks. In week two, we talked about data tables becoming runtime behavior. In week twenty-two, you were asked to trace from data records through globals, callbacks, tasks, scripts, structs, and concrete functions. In week twenty-three, you had to identify files and constants, not just describe them. So now, in week twenty-four, if your trace says “load map,” I expect you to say what kind of data became active and which runtime machinery consumed it.

Now the player moves. We already covered task and callback architecture earlier in the course, so I’m not going to re-teach RunTasks or SetMainCallback2. But I do want to connect them forward. Movement is not a free-floating while loop. It happens under the active main callback, with per-frame tasks and field control code deciding what the avatar can do, whether input is accepted, whether movement scripts block control, and whether a metatile or event object should trigger a script. So when your trace reaches “move player,” you should ask: is this player-controlled movement, forced movement, script movement, object event movement, or transition movement? These are not the same thing.

Here is a small board trace. I’ll say it aloud as I write it. Save state restores player and world. LoadMapFromWarp selects the active map. Field callback and tasks resume input and object updates. Movement updates avatar position. The tile and event environment are checked. A script starts. Battle setup prepares transition. Battle callback resolves turn. Result mutates party and flags. Field resumes. Save writes the new persistent snapshot.

That is the skeleton. But a final architecture trace needs muscle.

Let’s zoom in on the script-to-battle handoff. A trainer encounter is not just “battle starts because NPC saw you.” The NPC is an event object with data. The map has event metadata. The trainer has script behavior or trainer battle parameters. The line-of-sight check produces a scripted sequence: exclamation, movement, text, and then battle setup. Here FireRed’s design is deeply data-driven, but in the C/GBA style, not in the Showdown TypeScript style. You have constants, tables, headers, compiled scripts, and globals. Showdown gives you flexible runtime data hooks; FireRed gives you compiled data and engine functions that know where to look.

This is a good place to use Showdown carefully. In Showdown, a move, item, or ability can have hooks such as damage modification, residual effects, or move-attempt checks. The research packet gives us examples in pokemon-showdown/data/mods/gen4/items.ts, chunk_ef574f63c118f2f6, where an item can inherit behavior and override hooks like damage modification or base power logic. It also points to pokemon-showdown/data/mods/gen9ssb/moves.ts, chunk_f562e1983e9fdeff, where a move-like effect defines onTryMove, onPrepareHit, volatile status behavior, and condition callbacks. Those hooks are excellent for understanding battle behavior as attached executable data.

But FireRed is the authority for this course. So the comparison is this: Showdown makes battle behavior testable by expressing many mechanics as data objects with hook functions and by giving test code direct ways to create battles and make choices. FireRed expresses battle behavior through C functions, tables, constants, and state machines integrated with the rest of the cartridge runtime. If you say, “FireRed should just use Showdown’s hook system,” you are missing the point of the comparison. The architectures serve different constraints. Showdown optimizes for simulator correctness, mod layering, online battle execution, and regression tests. FireRed optimizes for a fixed-generation retail game running on constrained hardware with compiled assets and a broad single-player overworld.

Let me pause and check the room. If I give you an item behavior in Showdown and an item behavior in FireRed, what question should you ask first? Not “which one is cleaner?” That is too vague. Ask, “Where is behavior attached, and who calls it?” In Showdown, behavior may be attached to a data object and invoked by the simulator at a well-defined hook point. In FireRed, behavior may be encoded in switch logic, battle scripts, constants, item effect handlers, or engine routines. Same design problem, different attachment point.

Now let’s return to the concrete trace and enter the battle. Battle setup in FireRed must preserve the illusion of continuity. The player came from a map. The screen transitions. Music changes. The party is loaded into battle structures. Opponent data is selected. Battle type flags matter. The engine must know whether this is wild, trainer, double, safari, link, or another special case. The earlier source excerpt for pokefirered/src/battle_setup.c, chunk_494d132a59422c85, is valuable exactly because it shows that battle setup is not isolated from safari zone, quest log, event data, scripts, field effects, and transition code. Those includes are architectural fingerprints.

In Showdown tests, the comparable moment is createBattle. Again, not equivalent, comparable. The test harness says, “Give me a battle with these teams, format, options, and seed.” pokemon-showdown/test/common.js, chunk_979fde4c3177ea4f, anchors that deterministic creation helper. The testing guide in pokemon-showdown/test/TESTS.md, chunk_134ac9f75b9d6477, describes createBattle, seeds, and forceRandomChance, which gives tests a way to pin down randomness. That is why Showdown can write compact executable specifications for mechanics. You can say, “Set up this battle, force this chance, make these choices, assert this state.” FireRed does not hand you that harness, but your final trace should still aspire to that clarity.

Now, once the battle begins, do not skip the turn. “Resolve a turn” is not one event. At minimum, for our trace, you need request state, player choice, opponent choice, action ordering, move execution, damage or miss or effect resolution, faint checks, experience or result flow, and return conditions. We covered Showdown’s request state, makeChoices flow, and action queue earlier, so I’m only going to point back to it. The source focus includes pokemon-showdown/test/TESTS.md, chunk_e8da8b9a442469fd, for makeChoices and RNG-independent assertions. That matters because in a robust regression test, you usually do not assert every incidental log line if what you care about is “the target fainted” or “HP decreased by this amount” or “stat stage changed.” You choose assertions that match the mechanic’s contract.

And Showdown’s assertion vocabulary is a useful teaching comparison here. The packet names pokemon-showdown/test/assert.js, chunk_26026d44764fa919, for species, fainted, and fullHP assertions, and pokemon-showdown/test/assert.js, chunk_f2ab14d37f397660, for statStage and HP-change assertions. That is more than convenience. It is a vocabulary for saying what counts as correct. The tests do not just ask, “did the code run?” They ask, “is the combat state now what the rules require?” Species identity, fainted status, full HP, stat stage, HP delta: these are semantic assertions.

For your FireRed final trace, you can borrow that mindset even when you cannot borrow the exact helper. If your trainer battle says the player’s Charmander used Scratch and the opposing Caterpie fainted, your trace should name the state changes. Current HP changed. Faint state became true. Battle outcome advanced. Experience may be awarded. Money may be awarded. The trainer defeated flag may be set. The party’s HP and experience are now different from before. The map event object should no longer initiate the same trainer battle after the defeated flag is set. And if the player saves, that changed state must survive reboot.

Let’s do a quick hypothetical. Imagine a student final trace says, “After the battle, the game updates the save.” I would mark that as too coarse. Which state changed? Is it party HP? Experience? Level? Learned move? Money? Bag item? Flag? Variable? Quest log? Object visibility? Map position? Some of those are persistent. Some are runtime-only. Some are reconstructed from persistent flags. Some are derived. Architecture is largely the discipline of not flattening those differences.

The party data is a particularly good example. During battle, combat has its own active representation of battlers, stats, volatile state, move PP, HP, and so on. After battle, not every battle-local concept goes back into the party. Confusion, turn order, temporary stat stages, and many volatile effects are battle-scoped. HP, experience, level, moves learned, held item changes, and status conditions may persist depending on the mechanic. So in your trace, you should distinguish “battle state was resolved” from “party model was mutated.” That distinction is one of the reasons creature RPG architecture is interesting.

Now I’m going to put a comparison table on the board verbally, but I won’t draw it as a table in your notes. On the FireRed side: compiled data, C structs, globals, callbacks, tasks, scripts, battle setup, save blocks. On the Showdown side: Dex data facade, mods, battle object, test helpers, seeded RNG, makeChoices, assertions, replay logs. The bridge between them is not implementation reuse. The bridge is architectural language. Showdown helps us name battle correctness and deterministic tests. FireRed teaches us how a complete cartridge RPG threads battle through map, script, party, UI, and save.

This is why the lab for today asks for one full gameplay sequence, not five isolated diagrams. If you produce five diagrams, one for map, one for script, one for battle, one for inventory, one for save, you may know the modules, but you have not yet shown that you understand the handoffs. A full trace is a story of ownership, representation, and control.

Let’s check understanding with a slightly harder question. A trainer has been defeated. When the player leaves the map and comes back, the trainer still exists visually but does not challenge again. Where is that fact represented? The easy answer is “a flag,” and that is often right at the gameplay level. But the architectural answer is richer. The event script or trainer system checks event data. The object event may still be loaded from map data. Its behavior changes because persistent event state says the battle has already occurred. If the player saves, the save system must include that event state. When the map is loaded again through the overworld path, including the kind of path anchored by LoadMapFromWarp in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, the runtime can reconstruct the map with that progression fact in place.

That is the sort of answer I want on the final. Not necessarily those exact words, but that level of separation.

Now, some of you may be wondering how detailed the final source map needs to be. The answer is: detailed enough that another advanced student could follow your references and verify your trace. You do not need to paste a hundred functions. You do need to name source files and the kind of responsibility they carry. For today, the essential anchors include pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, for map loading through LoadMapFromWarp; pokefirered/src/battle_setup.c, chunk_494d132a59422c85, for the field-to-battle boundary; pokefirered/src/save.c, chunk_9900b8674c89af66, for save continuation; and the Showdown testing sources we have just discussed for deterministic battle setup and assertions. Use those anchors as evidence, not decoration.

Let’s talk about regression now, because week twenty-four is not only a trace week. It is also a verification week. In Showdown, a regression test might set up a battle with a seed, force a random chance, choose moves, and assert that a stat stage or HP value changed correctly. The test becomes an executable specification. The source packet’s TESTS.md chunks, chunk_134ac9f75b9d6477 and chunk_e8da8b9a442469fd, support exactly that workflow: createBattle, seed control, forceRandomChance, makeChoices, and assertions that do not depend on irrelevant randomness. FireRed’s original codebase does not give us that same test surface, but the principle still applies. A good architecture trace should be test-shaped. It should make clear what inputs are controlled, which state transitions are expected, and which outputs prove the behavior.

For example, if you were writing a harness around a FireRed decompilation, you might not begin by testing the whole game loop. You might first test smaller deterministic functions where possible. Then you might create integration checks around scripted events. For battle, you would need to control RNG or choose mechanics that do not depend on RNG. That is where Showdown’s example is useful pedagogically. It reminds us not to build tests whose only assertion is “the battle ended.” A better test says, “given this party, this opponent, this move choice, and this controlled random path, HP changed by this amount, the opponent fainted, the defeated flag was set, and the save state contains the progression change.”

That last clause is the important one for today: “and the save state contains the progression change.” The title of the lecture is not “battle trace.” It is “Full Game Trace from Map to Save.” If your sequence starts in map data and ends in battle result but never returns to persistence, it is unfinished. If it starts in save and ends in map display but never explains behavior, it is unfinished. The complete loop is what makes the system a game rather than a pile of features.

Let’s recap where we are, but briefly. FireRed is our primary architecture source because it shows the full single-player creature RPG stack. Showdown is our secondary comparison because it has unusually clear battle data hooks, modded data layering, simulator turn control, and regression testing vocabulary. In the final project, you should not replace FireRed with Showdown. You should use Showdown to sharpen your description of battle correctness, then return to FireRed to explain how that battle lives inside the cartridge-scale architecture.

Now we are going to move next into the save side of the trace in more detail. Before we do, I want one more check for understanding. If a student says, “The battle engine sets the save file,” what is wrong with that sentence? Think for a moment. The problem is ownership. Battle may cause state changes that are later persisted. It may update party data, flags, money, or other game state through established structures. But the save system is responsible for serialization and persistence. Saying “battle sets the save file” collapses cause, mutation, and persistence into one vague verb. At an advanced level, that is exactly what we are training ourselves not to do.

So, as we continue, keep three questions in front of you. What representation is active right now? Which subsystem has control right now? And which changes will still matter after the machine is turned off? If you can answer those three questions at every step from LoadMapFromWarp through battle setup, through a resolved turn, through event updates, and into save continuation, then you have the architecture in your hands rather than just in your notes.

Now, let me put one final trace on the board, and I want you to listen for those three questions as we move. The player resumes from a save. The active representation is persistent save data being expanded into runtime state. Control is in the startup and continuation path. The changes that matter are whatever gets copied out of the serialized save into globals, party structures, event flags, bag pockets, map location, and callback state. When FireRed later moves into the overworld, the active representation is no longer “the save file” in the everyday sense. It is runtime state initialized from that save. That distinction is visible in the way our source packet separates the save continuation material in pokefirered/src/save.c, chunk_9900b8674c89af66, from the map-loading machinery in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, where LoadMapFromWarp belongs to the overworld transition story, not to serialization itself.

So if I ask, “Where is the player?” the lazy answer is, “In the save.” The architectural answer is, “The saved location has been used to reconstruct the current warp and map context, and then the overworld loader uses that context to install the map runtime.” That is the difference between a user-facing description and an engine trace.

Now suppose the player takes one step. What is active? A per-frame overworld loop. Which subsystem has control? The main callback and task system are driving field behavior, while player avatar, event object movement, collision, metatile behavior, and scripts cooperate underneath. What may persist? Not the fact that the D-pad was pressed for one frame. Maybe a changed event flag, maybe a moved object if the game chooses to encode it, maybe a newly obtained item, maybe a party update after a battle. But most frame-level facts evaporate. This is one of the most important lessons in the course: games are full of meaningful state that is not permanent state.

Let me say that again because it is a good exam sentence. Not all state is save state. Some state is display state. Some is control state. Some is animation state. Some is interpreter state. Some is battle working state. Some is persistent progression state. If you cannot classify the state, you cannot reliably trace the architecture.

Now we trigger a script. The player steps onto a tile or talks to a trainer. The active representation shifts from movement and collision into script execution. The subsystem with control is now the event scripting layer, although it is still being advanced inside the overworld shell. This is where many traces become muddy. Students write, “The map starts a battle.” Close, but not precise. The map does not decide battle semantics. The map or object event provides the placement and trigger context. The script supplies the authored progression logic. Battle setup prepares the battle transition and battle configuration. The battle engine resolves the battle.

In the source packet, pokefirered/src/battle_setup.c, chunk_494d132a59422c85, is useful precisely because its include surface shows it sitting at a crossing point. It includes overworld, item, safari zone, quest log, script, event data, event object movement, field effects, field map, player avatar, battle, battle transition, and battle communication concerns. That does not mean battle setup “owns” all those systems. It means battle setup is a bridge. At an advanced level, reading include lists is not a substitute for reading functions, but it is a very strong hint about architectural boundary pressure. A module with that kind of include surface is usually negotiating a transition between worlds.

Here is the concrete example. A trainer sees the player. Before battle, the overworld is animating the trainer approach, locking player control, maybe showing an exclamation mark, then running text. The script layer is sequencing the event. Then battle setup packages the encounter. Once the battle engine has control, the trainer object on the map is not walking around making AI decisions. The active representation has shifted. We now care about battle participants, party slots, moves, HP, stat stages, volatile state, turn order, and the battle controller flow. When the battle ends, results flow back outward: defeated trainer flags, money, experience, party HP, fainted state, maybe registered rematches or quest log consequences depending on context. Then control returns to the overworld, where the script may continue.

Pause and check yourself here. If I point at the trainer sprite on the map during the battle transition and ask, “Is this the battle opponent?” what should you say? You should say, “It is the authored map object that led to the opponent, but the active battle opponent is represented in battle data.” Good. That answer shows you understand representation shifts.

Now, where does Pokemon Showdown help us today? Not by replacing this trace. FireRed is still our architectural authority for this course. Showdown helps as a comparison object where battle behavior is made unusually testable and inspectable. We already covered Dex as a data facade, generation mods, request state, player choices, and queue resolution, so I am not going to re-teach those. Instead, I want you to use Showdown as a verification lens.

In Pokemon Showdown, the test documentation in pokemon-showdown/test/TESTS.md, chunk_134ac9f75b9d6477, emphasizes createBattle, seeds, and forceRandomChance. The testing helpers in pokemon-showdown/test/common.js, chunk_979fde4c3177ea4f, support createBattle options and deterministic seed setup. That is a very modern articulation of something we have been trying to do manually with FireRed traces: make the initial state explicit enough that the result can be checked. Showdown can say, in effect, “Create this battle under these rules, with this seed, then make these choices.” FireRed’s original architecture was built for the cartridge runtime, not for a Node test harness, but the mental discipline transfers.

And then Showdown’s assertion vocabulary matters. In pokemon-showdown/test/assert.js, chunk_26026d44764fa919, the source packet identifies assertions around species, fainted, and full HP checks. In pokemon-showdown/test/assert.js, chunk_f2ab14d37f397660, it identifies stat stage and HP-change assertions. Those are not just conveniences. They encode what a battle test considers observable. That is an architectural choice. A good assertion vocabulary says, “These are the stable facts we expect to inspect after a turn.” It avoids over-coupling the test to every incidental implementation detail.

Let me connect that back to FireRed. If we are writing our final architecture trace for the lab sequence, we do not merely say, “A battle happened and the player won.” We name the observable changes. Party member HP changed. Experience may have changed. A trainer defeated flag may have changed. Money may have changed. Inventory might change if the script awards an item. The map script’s progression point may move forward. When saving occurs, save continuation and serialization persist the appropriate structures. We are doing, in prose and source references, what a good regression test does in code: setting up a scenario, executing a controlled sequence, and asserting the important outcomes.

Now I want to draw a two-column comparison on the board. Left column, FireRed. Right column, Showdown. Under FireRed, write “map data, script, battle setup, battle engine, save.” Under Showdown, write “Dex/mod data, battle factory, choices, turn simulation, assertions.” These are not the same architecture. Do not flatten them. FireRed is an integrated handheld RPG with overworld, maps, object events, scripts, party, inventory, UI, link behavior, and persistence. Showdown is a simulator and server-oriented battle system with a highly explicit battle API. But the comparison is productive because both systems must answer the same deeper questions: What data defines the possible behavior? What runtime state is active? What command advances the system? What observable facts prove correctness?

A concrete Showdown-style example would be: create a battle with two known species, make the first turn choices, force or seed randomness, then assert HP changed by the expected amount or that a stat stage was modified. Again, we are not re-teaching the full Showdown turn loop, but note the clarity of that test shape. The player choices are explicit. The random source can be controlled. The postconditions are named in domain language. In FireRed, the equivalent final project trace would be: start from a known save, load the relevant map through the warp path, move to the trigger, execute the script, enter battle setup, resolve the selected turn, record the result changes, return to the overworld, and save. The FireRed trace is broader because it includes the whole RPG shell, not only battle.

Let me ask a check-for-understanding question. If your final trace says, “The game calls the battle code, then after the battle it saves,” what is missing? Several things. You have not identified the transition point from script to battle setup. You have not named the active battle representation. You have not described what state changed during battle. You have not distinguished returning to overworld from persisting to storage. And you have not shown whether the save is automatic, user-initiated, or part of a continuation path. That sentence is a summary, not a trace.

Now let us tie week 24 to the whole course. Back in week 1, we treated FireRed as a set of cooperating state machines and data tables. That was not a slogan. It was the organizing principle for everything since. We looked at the runtime shell: AgbMain, callback slots, tasks, RunTasks, and the habit of per-frame dispatch. We looked at headers as cross-module contracts. We looked at generated data versus runtime data. We looked at maps as records that point to layouts, scripts, object events, warps, and callbacks. We looked at creature data as tables that become behavior only when a runtime subsystem asks a question of them. We looked at UI, inventory, party, battle, scripting, and persistence as separate but connected domains.

Week 24 is where that all compresses into a single professional skill: given a player-visible sequence, can you walk backward and forward through the engine without losing the boundaries? That is why the final lab sequence is intentionally long. Load save. Enter map. Move player. Trigger script. Start battle. Resolve a turn. Award result. Update flags, items, or party. Return to overworld. Persist state. That sequence is not long because I want busywork. It is long because real game architecture is cross-system architecture. The interesting bugs usually live at the joins.

For example, if a trainer can be rebattled incorrectly after losing once, the bug might not be in damage calculation at all. It might be in the event flag update. If the party HP is wrong after saving and reloading, the battle result may have been computed correctly, but the persistent party structure or save path may be suspect. If an item reward appears in text but not in the bag, the script may have displayed the message without committing the inventory change, or the condition path may skip the update. If a battle test is flaky, Showdown teaches us to suspect uncontrolled randomness or assertions that depend on incidental values; FireRed teaches us also to suspect runtime state that was not reset between scenarios.

This is why our assessment bar has gone up. Earlier in the course, it was acceptable to identify a file and say broadly what subsystem it belonged to. By week 24, that is no longer enough. The homework and quizzes have already checked the ability to use Showdown as a secondary comparison for battle hooks, Dex/mods, simulator turns, and regression testing. They have also checked the ability to decompose the RPG into runtime shell, overworld, scripting, creature data, battle, inventory, UI, save, and link systems. The final assessment asks you to synthesize, not recite.

Let me give you the phrasing I want you to practice. Do not say, “The game loads the map and runs events.” Say, “The saved or scripted location selects a warp context; LoadMapFromWarp in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, belongs to the overworld path that installs the current map runtime; event scripts then run as authored control logic on top of that loaded map.” Do not say, “The test checks the Pokemon.” Say, “Showdown’s test helpers create a deterministic battle state, as described in pokemon-showdown/test/common.js, chunk_979fde4c3177ea4f, and its assertions name battle-domain postconditions like species, fainted state, full HP, stat stages, and HP deltas, as seen in pokemon-showdown/test/assert.js, chunk_26026d44764fa919, and chunk_f2ab14d37f397660.” Do not say, “The battle saves the result.” Say, “Battle mutates gameplay state, and the save system later serializes the persistent subset, with save continuation material represented separately in pokefirered/src/save.c, chunk_9900b8674c89af66.”

Those are not fancy sentences for their own sake. They preserve causality. Architecture is causality with names.

Now, final board pass. I am going to write four verbs: select, install, execute, persist. Select: data and current state select a map, a script branch, a party member, a move, an item, a battle format. Install: the engine turns selected data into active runtime structures, such as the current map or battle state. Execute: callbacks, tasks, scripts, and battle turn logic advance those structures. Persist: the save system commits the durable subset. Most architecture errors in student traces come from using one of these verbs where another belongs. A map header does not execute. A script does not persist by existing. A save file does not animate the player. A battle turn does not, by itself, explain how the overworld resumes.

So, as you finish the lab, I want one clean sequence. Start with the representation. Name the controlling subsystem. Name the source anchor. Name the state changes. Then say what survives. If you use Showdown, use it as a mirror for test discipline: explicit setup, deterministic inputs, domain assertions, replayable outcomes. Do not let it pull the center of gravity away from FireRed. FireRed is the full creature RPG architecture. Showdown is our sharp comparison for battle data hooks and verification.

And that brings us to the close of the course arc. We began by refusing to treat a game as a monolith. We end by refusing to treat a gameplay moment as magic. The player sees a simple story: continue game, walk, meet trainer, battle, win, receive reward, keep playing, save. Underneath that story is a chain of representations and control transfers: serialized data, runtime globals, callbacks, tasks, map records, object events, scripts, battle setup, battle state, result mutation, overworld restoration, and persistence. When you can trace that chain, you are no longer only reading source code. You are reading system behavior. That is the professional skill this course has been building toward.

## Source References
- pokefirered/src/battle_setup.c
- pokefirered/src/daycare.c
- pokemon-showdown/data/mods/gen4/items.ts
- pokemon-showdown/data/mods/gen9ssb/moves.ts
- pokefirered/src/pokemon_summary_screen.c
- pokemon-showdown/data/text/moves.ts
