# Encounter Data into Battle Setup

Lecture ID: lecture_w15_d01
Week 15, Day 1

## 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 15 material to the overall course plan.

## Transcript
All right, let’s pick up from the machinery we already have and move one boundary outward. Last week, when we were inside the battle engine, we treated the battle as if it already existed. We had battlers. We had chosen actions. We had turn-order arrays. We had script dispatch. We had current move state, damage state, status timers, and the shared globals that make the battle engine feel like one coordinated machine rather than a pile of independent objects. Today we are not going to re-teach that interior. Today’s question is: how does the game get from “the player stepped in grass” or “the player talked to a trainer” into that battle machine in the first place?

So write this on the board, or at least hold it in your head as the shape of the day: encounter data becomes battle setup, battle setup becomes battle runtime, battle result becomes world continuation. That is the pipe. And week 15 is about both ends of that pipe. Today we focus on the first half, encounter data into battle setup. Next time we focus on the second half, resolving battle outcomes back to the world.

The reason this topic matters architecturally is that a creature RPG has two very different-feeling sources of battle. A wild battle feels stochastic. The player walks through an area; the map, encounter table, terrain behavior, random numbers, time or version conditions, and party state may all contribute. A trainer battle feels authored. The player crosses a line of sight or presses A on an NPC; a script or event object identifies a specific trainer; the trainer table supplies party structure, AI flags, items, class, name, and reward behavior. But once the battle begins, the battle engine should not care very much whether the opponent came from a grass roll or a trainer record. It needs parties, battle type flags, scripts or transition metadata, result storage, and a callback path for returning control.

That is the architectural move. Different encounter resolvers, common battle setup object.

Now, FireRed does not literally present this to us in the clean vocabulary I just used. It is a C program for the Game Boy Advance, and its boundaries are expressed through globals, callbacks, task transitions, script variables, battle flags, and structs. But conceptually, that is exactly the seam we are studying. In the source packet for this week, the main evidence lives in pokefirered/src/battle_setup.c, specifically chunk_494d132a59422c85, which is representative because its include list alone tells us this file is not “just battle.” It includes overworld, item, sound, pokemon, load_save, safari_zone, quest_log, script, event_data, event_object_movement, metatile behavior, field control, screen effects, versus seeker, battle, battle transition, and battle communication concerns. That is a signal. Battle setup is an integration layer. It is where the world’s event system and the battle engine shake hands.

Notice how different that is from battle_main. In the prior lectures, we looked at battle_main as the active combat loop. This week’s packet points us to pokefirered/src/battle_main.c chunk_1c5a1226d8d9afb7 for battle main result flow and pokefirered/src/battle_main.c chunk_46361bdbd92dae37 for cleanup and result continuation. That tells us something important: result handling is not a single print statement at the end of a fight. It is part of the same architectural bridge. The battle must finish, save or expose the correct result, clean up battle resources, and then return to overworld or script continuation with enough information for win/loss routing.

Let me slow that down with a concrete example.

Imagine the player is walking through Route 3 grass. The map logic detects movement. The metatile behavior says this tile is grass, and the encounter subsystem consults wild encounter data. In this week’s source focus, that high-signal data source is pokefirered/src/data/wild_encounters.json. The important point is not the exact syntax of every encounter slot. The important point is that the wild encounter source is data, not bespoke code for each patch of grass. Area data supplies possible species, levels, encounter rates, and method-specific tables. The resolver’s job is to turn “I am on this map, on this terrain, with this random roll” into “there is or is not a wild opponent, and if there is, here is the opponent specification.”

Now contrast that with a trainer. The player enters a trainer’s sight range. The event object and script machinery identify that trainer. The trainer data source, named in the packet as pokefirered/src/data/trainers.h, supplies authored trainer information. Again, the important architectural detail is not that a trainer is “just another wild encounter.” It is not. The trainer has an identity, a class, name text, party composition, prize money behavior, possible items, AI flags, and event flags so they do not challenge you again in the normal case. But after the resolver has interpreted that authored record, the battle engine still needs a setup: battle type, enemy party, player party, scripts, transition, and where to return.

So if I draw this on the board, I would draw two boxes on the left. Top box: wild encounter resolver. Bottom box: trainer battle resolver. Then I draw arrows from both into one middle box labeled battle setup. Then from battle setup into battle main. And from battle main into battle result. Then from battle result back to overworld script or map control.

That middle box is the lab target this week. Your workshop asks you to implement a wild encounter resolver and trainer battle resolver that both create the same battle setup object and return a battle result consumed by overworld logic. That is not an arbitrary exercise. It is forcing you to reproduce FireRed’s architectural idea in a cleaner small form.

Now, because you are advanced learners at this point in the course, I want you to notice a subtle thing. “Same setup object” does not mean “same source data.” It means the battle engine receives a normalized runtime representation. This is one of the central lessons of the entire course. We have seen it in maps, scripts, party data, item use, save persistence, and now battle setup. Data may originate in many formats and many subsystems, but runtime systems prefer stable contracts.

Headers are part of that story. The packet points us to pokefirered/include/battle.h chunk_f5cd027bf13ab47d for BattleStruct and result-related state. We are going to use that as a contract clue. In a C codebase like FireRed, a header is not just a convenience for prototypes. It is a boundary announcement. When result-related state is visible through battle.h, other systems can coordinate around battle outcomes without owning the internals of damage calculation or turn execution. That is the design pressure you should imitate in your projects: the overworld does not need to know how paralysis speed modification worked. It needs to know whether the player won, lost, fled, caught something, blacked out, or should resume a script branch.

Check for understanding here. If I ask, “Should the wild encounter resolver directly call damage calculation?” the answer should be obviously no. If I ask, “Should the trainer event script know the exact turn-order array used by battle_main?” also no. But if I ask, “Should both systems agree on battle type flags and result values?” yes. That is the shared contract.

Let’s bring in Pokemon Showdown now, carefully. Showdown is not our main architecture source for this course. FireRed is. FireRed gives us the creature RPG as a shipped GBA game with callbacks, tasks, global state, event scripts, and data tables embedded into a ROM-oriented build. But Showdown is very useful as a secondary comparison because it shows the same broad problem under a very different set of constraints. Showdown has a Dex model, mods, simulator turns, battle data hooks, and regression tests. The research packet reminds us to use modules like pokemon-showdown/data, pokemon-showdown/data/mods, pokemon-showdown/server, and pokemon-showdown/test as recurring comparison areas, while still keeping FireRed primary.

Why is that comparison useful today? Because Showdown makes data hook design more explicit. In Showdown, battle behavior is strongly shaped by data objects and callbacks. The packet gives us pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f as an example where damage calculation consults active move data, checks immunities, handles OHKO moves, invokes a damage callback, handles fixed damage, and so on. That is inside battle resolution, not encounter setup, but it illustrates Showdown’s style: the simulator consults Dex data and modded rule hooks during execution. FireRed, by contrast, uses C globals, battle scripts, constants, tables, and specialized setup functions. The shared architectural principle is not the implementation language. The shared principle is that data and rules are normalized before or during battle execution through well-defined access paths.

Let’s be precise about the difference. In FireRed, a wild encounter table is not a modded Dex overlay. A trainer record is not a JavaScript object with callbacks. FireRed’s architecture is much closer to “compiled data plus runtime globals plus script and callback transitions.” Showdown’s architecture is closer to “data modules plus Dex lookup plus simulator objects plus tests.” But both systems must answer the same questions: what battle are we starting, which rule set applies, which combatants exist, what state is initialized, and how do we verify that a change did not break expected behavior?

That last question, verification, becomes more important from here forward. Earlier in the course, when you were learning map headers or task IDs, a lot of your tests could be trace-based: does this callback get set, does this task run, does this script variable change? Now the behavior is combinatorial. A trainer party can vary. A wild table can vary. A battle can end in win, loss, flee, capture, forced warp, Safari behavior, or scripted continuation. So regression testing becomes the discipline that keeps data-driven systems from collapsing under their own flexibility.

Showdown is valuable here because it has a culture of deterministic battle tests. We are not replacing FireRed with Showdown, but we are borrowing a habit. If a simulator turn or data hook changes, you want a small test proving that the result remains correct. Your week 15 lab should be thought of the same way. If you build a wild encounter resolver and a trainer battle resolver, you should test that both produce the same setup shape. Then you should test that a result object can be consumed by overworld logic without knowing which resolver produced it.

Let me give you a small imagined version. Suppose your simplified setup object has fields like battleKind, playerParty, opponentParty, opponentIdentity, transitionStyle, canRun, victoryScript, defeatScript, and resultDestination. For a wild Pidgey in grass, battleKind is wild, opponentIdentity may be species-level data rather than a named trainer, canRun is true, and victoryScript may just be return to map. For a Youngster trainer, battleKind is trainer, opponentIdentity is a trainer id, canRun is false, and victoryScript may set a defeated flag and continue a script. The battle engine consumes opponentParty either way. The overworld consumes resultDestination and outcome either way. That is the point.

Now, if you are thinking, “But FireRed has a lot more flags than that,” correct. And that is why this is week 15, not week 3. There are trainer battles, wild battles, Safari battles, link battles, tutorial battles, roaming encounters, special scripted battles, and transitions. The representative battle setup chunk, pokefirered/src/battle_setup.c chunk_494d132a59422c85, signals this complexity through the modules it touches. Safari zone and quest log are not random includes. Script and event data are not incidental. Field effects and battle transition are not decoration. This setup layer is where many world-level conditions get translated into battle-level configuration.

Let’s pause and connect this to the course plan. In weeks 1 and 2, we emphasized runtime shell, data generation, and callback/task structure. In the overworld section, we traced map headers, layout references, object events, and scripts. In the party and data sections, we treated creatures as structured records rather than just sprites on screen. In weeks 12 through 14, we moved into the battle engine: battle main as coordinated state, chosen actions by battler, turn-order arrays, battle scripts as combat bytecode, current move data, damage, hit effects, status, and side timers. Week 15 now attaches that engine back to the world. This is the moment where the course stops treating “battle” as a standalone module and treats it as part of the RPG loop.

And that loop is the actual product architecture. Walk, inspect, trigger, battle, resolve, return, persist, continue. That is the loop players feel. Source files and structs are our way of explaining how the loop is made.

Here is a check for understanding before we go further. If a player loses to a trainer, where should the decision about map return or blackout routing live? Should it live in the move damage function? No. Should it live in the wild encounter table? No. Should it be represented in result-related battle state and then consumed by overworld or script continuation logic? Yes. That is exactly why the packet pairs battle setup sources with pokefirered/src/battle_main.c chunk_1c5a1226d8d9afb7 for result flow, chunk_46361bdbd92dae37 for cleanup and continuation, and pokefirered/include/battle.h chunk_f5cd027bf13ab47d for BattleStruct and result-related state.

Now I want to make the distinction between “selection” and “initialization.” Encounter selection answers, “What battle, if any, should happen?” Battle initialization answers, “Given that battle, what runtime state must exist before the battle main callback can safely run?” These are often blurred in student projects. You roll the random encounter and immediately start populating battle globals in the same function. That may work for a toy prototype, but it becomes brittle as soon as trainer battles, scripted encounters, repel effects, special maps, or rematches enter the system.

A cleaner mental model is this. Resolver functions are allowed to inspect world context. Setup functions are allowed to initialize battle context. The battle loop is allowed to execute battle context. Result handlers are allowed to translate battle outcome back into world context. Each stage has different knowledge.

Let’s put that into a specific FireRed-style trace. The player steps. Field control and avatar movement logic eventually give the game a chance to consider grass behavior. Wild encounter data from pokefirered/src/data/wild_encounters.json is the data-side input. If the resolver says no encounter, the overworld continues. If yes, battle setup takes over. The setup path needs to preserve enough overworld return information so that after the battle, the player does not wake up in an undefined callback. It also needs to initialize battle flags so battle_main does not have to infer whether this is wild, trainer, Safari, or something else by asking the map again. Once battle begins, the system can use the battle main machinery we studied earlier. At the end, result flow and cleanup in pokefirered/src/battle_main.c chunk_1c5a1226d8d9afb7 and chunk_46361bdbd92dae37 bridge back out.

Trainer flow has a parallel trace. Event object or script context identifies a trainer. Trainer table data from pokefirered/src/data/trainers.h supplies the authored opponent. The resolver converts trainer id plus world context into battle setup. The setup initializes battle type and opponent party. The battle runs. The result determines whether the defeated flag is set, whether the script continues, whether the player blacks out, and how overworld control resumes.

Do you see the symmetry? Wild and trainer battles differ heavily before setup and after result interpretation, but during battle they become compatible enough to share an engine.

This is also why “data-driven” does not mean “no code.” That phrase gets abused. FireRed is data-driven in the sense that tables and records determine a large amount of visible behavior. But code still interprets the data, normalizes it, enforces constraints, initializes state, and routes results. Pokemon Showdown is also data-driven, but in a different idiom: Dex data, mods, scripts, and simulator turns cooperate, and tests lock down behavior. If you take only the data and ignore the interpreter, you do not have architecture. You have a spreadsheet.

So as we proceed today, keep asking four questions. First, what is the source data? Second, what world context selects or activates that data? Third, what battle setup state is produced? Fourth, what result state must survive the battle so the world can continue correctly?

That is the frame for the rest of the lecture. Next we are going to zoom into the two resolver families more carefully: wild encounter inputs first, then trainer table inputs. After that, we will talk about the transition from setup into battle main without re-teaching the battle main loop itself. Then we will set up the result side so that Wednesday’s lecture can focus on win/loss routing and overworld return in detail.

Good. Let’s take wild encounters first, because they are the cleaner case architecturally. A wild encounter begins with the world asking a question: given where the player is standing, given what kind of tile or movement just occurred, given any active modifiers like Repel or special cases, is there a creature to fight, and if so, which one? Notice the shape of that question. It is not a battle question yet. It is a world question whose answer may become a battle setup.

So, if I write this on the board, I am not going to start with “battle.” I am going to start with “map context.” Map context points to an encounter table. The encounter table gives candidate species and levels. The resolver applies rules, random selection, and suppression checks. Only after that do we cross into battle setup. That boundary is exactly why the week is organized as trainer data, wild encounter inputs, and battle result routing, rather than simply “more battle engine.”

The packet names pokefirered/src/data/wild_encounters.json as the high-signal wild encounter source for today, and the important word in that filename is not just “encounters”; it is “data.” The data can say, in effect, this area has these possible wild creatures at these levels, with these encounter groups. But that file does not know that the player just stepped into grass. It does not know whether a transition is already running. It does not know how battle graphics are loaded. It does not know what happens after the fight ends. Those responsibilities live in the surrounding runtime.

And this is a pattern you should now recognize from earlier weeks. We saw map headers that did not themselves execute maps. We saw script data that did not itself execute script commands. We saw move data that did not itself calculate a full turn. So here, wild encounter data does not itself start a battle. It is selected by context and interpreted by code.

Let’s do a concrete example. Suppose the player steps into tall grass on a route. The overworld movement code reaches a point where it can ask, “does this step produce a wild battle?” The resolver looks at the current map’s encounter information, chooses an encounter slot, chooses a species and level, and then prepares the opponent side. What must be produced? At minimum, we need the opponent creature instance, the battle type flags, the transition to use, and the continuation path back to the field. The data table can provide species and level. The resolver has to provide context: this is a wild battle, not a trainer battle; the player party is the player’s current party; the post-battle destination is the same overworld state, not a trainer script victory branch.

Now pause there. Check for understanding: if I give you just pokefirered/src/data/wild_encounters.json, can you tell me how the battle screen fades in? No. You can infer that a battle might occur in that area, but the transition and load path are not properties of the encounter table alone. That is why pokefirered/src/battle_setup.c, chunk_494d132a59422c85, is so useful today. Even from the packet excerpt, look at the include surface. It pulls in task.h, overworld.h, pokemon.h, load_save.h, safari_zone.h, quest_log.h, script.h, event_data.h, event_object_movement.h, field control headers, battle.h, battle_transition.h, and battle-related interfaces. That include list is not trivia. It tells us battle setup is a boundary module. It sits between field state, event state, creature construction, transition effects, and the battle engine.

That is the point I want you to internalize: battle setup is not “the first turn.” It is the adapter between world semantics and battle semantics.

Now let’s contrast that with trainer battles. A trainer battle begins from a different world question. It is usually not “what random creature appears?” It is “which authored opponent has been activated?” The packet names pokefirered/src/data/trainers.h as the high-signal trainer table for this part. A trainer record is not just a species-level slot in the same sense as wild encounters. It is authored battle content. It can imply a trainer class, a name, a party, items or AI-related details depending on the entry shape, and the fact that defeating this opponent changes world progression.

So if I write the trainer path next to the wild path on the board, the left side is different. Wild path: map context, encounter table, random slot, generated opponent. Trainer path: object event or script context, trainer id, trainer table entry, authored party. But now look at the right side. Both paths have to converge. They both must produce a battle configuration the engine can consume. The engine should not care that much whether the enemy was chosen from grass or from a trainer table once we are inside the battle proper. It cares about battlers, sides, parties, rules, flags, and result storage.

This is why your week 15 lab asks you to implement a wild encounter resolver and a trainer battle resolver that both create the same battle setup object and return a battle result consumed by overworld logic. That lab is intentionally cleaner than FireRed’s C architecture. FireRed is a GBA game with global state, callbacks, tasks, and tightly coupled transition code. In your lab, we ask for a named setup object because that makes the boundary explicit. But do not mistake the lab’s neat object for a claim that the original source is organized as a modern service interface. In FireRed, the interface is distributed across functions, globals, flags, and continuation callbacks.

Now, advanced design question: what belongs in the common setup object, and what belongs only in the source-specific resolver? Let’s reason it out. Species selection belongs to the wild resolver. Trainer party lookup belongs to the trainer resolver. But the final opponent party, the battle mode, the transition request, and the post-battle continuation belong to the common setup. If you put random wild slot selection inside the battle engine, you have polluted the battle engine with overworld encounter policy. If you put trainer defeat flags inside the turn resolver, you have polluted combat with world progression. The setup boundary is where those meanings are translated, not where they are forgotten.

Here is a useful test. Imagine you are adding a new source of battle: a scripted gift creature that immediately challenges the player, or a tower mode opponent generated from a seed. If your architecture is healthy, you should be able to add a third resolver that also produces the same setup shape. You should not need to rewrite the turn system. You should not need to teach damage calculation about map ids. You should not need to teach the overworld about individual move effects. The boundary protects both sides.

This also gives us a good moment to use Pokemon Showdown as a comparison, carefully. Showdown is not our primary architecture source for this course. FireRed is. But Showdown is excellent for seeing how a battle simulator treats data and hooks in a more testable, data-driven environment. The research packet points us toward pokemon-showdown/data, pokemon-showdown/data/mods, pokemon-showdown/server, and pokemon-showdown/test as recurring modules, and it specifically highlights data files like pokemon-showdown/data/learnsets.ts and mod overlays such as pokemon-showdown/data/mods/gen9dlc1/learnsets.ts, pokemon-showdown/data/mods/gen9predlc/learnsets.ts, and pokemon-showdown/data/mods/gen8dlc1/learnsets.ts. The architectural comparison is that Showdown has a Dex and mod system that can answer, “under this ruleset, what is this species, move, learnset, or effect?” FireRed, by contrast, is not organized around a runtime mod Dex in the same way. It is a compiled game whose tables and code cooperate for a fixed product.

But the warning is the same in both systems: data is not behavior unless something interprets it. The packet gives us pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, where the script logic checks things like immunity, OHKO behavior, damage callbacks, fixed damage, and level-based damage. That is a battle hook example. The move or effect data can expose a callback or special field, but the simulator turn logic must call it in the right context. That is analogous to today’s FireRed point. A wild encounter table can expose candidate encounters, and a trainer table can expose authored parties, but battle setup code must call and translate those inputs at the correct time.

So, if you are building your own creature RPG, do not say, “we have encounter JSON, therefore we have encounters.” Say, “we have encounter JSON, a resolver that binds it to map context, a setup interface that normalizes the result, a battle runtime that consumes that setup, and result routing that returns control to the world.” That sentence is architecture. The first sentence is just storage.

Now let’s move one step closer to the handoff into battle main. I am not going to re-teach the battle main callback, because we already spent time on Battle Main as Coordinated State, and you have already worked with chosen actions by battler, turn-order arrays, shared battle resources, and script command dispatch. Instead, today we care about the entry contract. What must be true before battle main can do its job?

The player party must be available. The opponent side must be constructed or referenced. Battle type flags must be set so the engine knows what category of battle it is handling. Any special rules, such as safari behavior or trainer restrictions, must be represented before the combat loop starts. Visual and audio transition state must be scheduled. And some result location must exist so the eventual outcome can be observed after the battle.

That last piece matters more than it sounds. If battle setup launches combat but does not establish how the answer comes back, the world cannot continue coherently. Did the player win? Lose? Flee? Was the opponent caught? Did a trainer need to mark itself defeated? Should the player return to the same map script, run a loss script, warp to a healing location, or resume free movement? These are not turn-order questions. They are continuation questions.

The source focus for this week points to pokefirered/src/battle_main.c, chunk_1c5a1226d8d9afb7, for battle main result flow, and pokefirered/src/battle_main.c, chunk_46361bdbd92dae37, for cleanup and result continuation. It also points to pokefirered/include/battle.h, chunk_f5cd027bf13ab47d, for BattleStruct and result-related state. We are using those chunks as the evidence for the claim that battle outcome is not merely printed to the screen and forgotten. It is stored, cleaned up around, and then consumed by the broader runtime. Wednesday’s lecture will live mostly on that side of the boundary.

Let’s make this concrete with two outcomes. First, a wild battle where the player defeats the wild creature. The result says, in plain terms, the battle ended and the player did not lose. The overworld can usually resume. Maybe there are special cases, but the default shape is return to field control. Second, a trainer battle where the player defeats the trainer. The same broad “player won” result is not enough by itself. The world also needs to know which trainer was defeated, whether to set an event flag, whether to run post-battle dialogue, whether to disable future challenge behavior, and where to resume the script. Same battle result category, richer world consequence.

That is why I want you to separate “combat outcome” from “world consequence.” Combat outcome is the battle engine’s answer. World consequence is the overworld or script layer’s interpretation of that answer. A trainer victory is not only a battle fact; it is a progression fact.

Here is a check for understanding. Suppose your lab implementation returns only true or false from battle, where true means player won and false means player lost. Is that sufficient for wild and trainer battles? For a toy demo, maybe. For this week’s architectural target, no. You need at least enough result information for the caller to distinguish win, loss, escape or cancellation where applicable, capture if your design supports it, and the source context that tells the world what to do next. The source context might not be in the battle result itself; it might be held by the caller as pending continuation state. But somewhere, the system must preserve that meaning.

Let me say that another way. The battle engine can answer “what happened in battle.” The world must answer “what does that mean here?”

Now, bring your attention back to pokefirered/src/battle_setup.c, chunk_494d132a59422c85. The representative chunk’s include list shows battle setup touching script, event data, field effects, player avatar control, field message boxes, metatile behavior, quest log, safari zone, and battle transition. That is precisely the module shape you would expect when a game is leaving the field but has not yet become pure battle. It has to freeze or redirect field behavior, prepare battle resources, possibly preserve script state, and start a transition. So when you read a file like that, do not just scan for function names. Ask what worlds it imports. A boundary file reveals itself by the number of subsystems it has to coordinate.

This is also why regression testing is easier in Showdown than in FireRed, and why that comparison is useful but limited. Showdown can run simulator turns in a controlled Node-based environment, load a Dex or modded Dex, create battles under specific formats, and assert outcomes. The packet highlights pokemon-showdown/test as a recurring module for that reason. If a move hook changes, a regression test can construct the battle state and verify the result. FireRed can be tested, but it is not architected first as a headless battle simulator with the same ergonomics. Its battle setup is deeply tied to GBA callbacks, tasks, graphics transitions, save state, field state, and event scripts.

That does not make FireRed worse. It means it is solving a different product problem under different constraints. The GBA game has to move from overworld to battle with audiovisual continuity, memory constraints, hardware-facing callbacks, and compiled data. Showdown has to model competitive battle rules across generations and mods with deterministic server-side simulations. We use FireRed to understand integrated game architecture. We use Showdown as a secondary comparison when we want to see modern data hooks, ruleset overlays, and regression testing more clearly.

Now I want to do a small board exercise. Imagine a rectangle labeled “BattleSetupRequest.” On the left, draw two arrows into it. One arrow says “WildEncounterResolver.” The other says “TrainerBattleResolver.” Under WildEncounterResolver, write “map, terrain, encounter table, random slot.” Under TrainerBattleResolver, write “trainer id, trainer table, script or object event.” Inside BattleSetupRequest, write “player party, opponent party, battle type, rules, transition, continuation token.” On the right, draw an arrow into “BattleMain.” Then, from BattleMain, draw an arrow to “BattleResult.” Then from BattleResult, draw arrows back to “Overworld continuation” and “Script/event updates.”

That diagram is not claiming FireRed literally has a struct with that exact name. It is our architectural lens. FireRed’s actual implementation is lower-level and more distributed. But if you cannot draw this diagram, you will get lost in the source, because you will treat every global assignment as equally meaningful. The diagram lets you ask: is this line selecting source data, normalizing battle setup, running combat, storing outcome, or routing world continuation?

This is the level of sophistication I expect at week 15. Earlier in the course, it was enough to say, “this table feeds this behavior.” After the midterm and the week 14 design work, that is no longer enough. You should now be able to say which layer owns the table, which runtime context activates it, which state crosses the boundary, which callback or task family advances the process, and which result state allows the caller to resume.

Let’s apply that to trainer data one more time. A trainer table entry in pokefirered/src/data/trainers.h is stable authored content. It can be reused whenever that trainer id is referenced. But the trainer battle instance is contextual. The player may approach from a line of sight. The player may press A to talk. A script may force the battle. A rematch system may select a different variant. The table does not own those activation rules. The overworld and script systems do. So, in your lab, do not make the trainer table object call startBattle by itself. Let the trainer resolver receive an activation context and return setup data. That distinction will keep your design from turning every data record into a little god object.

Wild encounters have the opposite temptation. Students often over-randomize too late. They carry an “encounter table id” into battle and let the battle decide what appeared. That is usually the wrong boundary. By the time combat begins, the opponent should exist. Its species, level, stats, and initial state should be determined. Otherwise, replay, debugging, and result interpretation become awkward. If the player catches the creature, what exactly was caught? If the battle intro displays the species, when was it selected? If an ability or item modifies encounter rates, where did that rule apply? Those are resolver questions.

Now, there are exceptions in real engines. You might lazily instantiate some visual resources. You might defer some derived stats until party construction. But the semantic identity of the encounter should be resolved before battle main begins. That is the architectural point.

Let’s briefly connect this to prior battle lectures without reopening them. Once battle main has the setup, the systems we already studied can take over: battler slots, chosen actions, turn ordering, battle script command dispatch, current move state, damage and hit effects, status and side timers. Today’s boundary determines what initial world is handed to those systems. If the boundary is wrong, the turn engine may still run perfectly and the game may still be architecturally broken. A beautiful damage formula does not help if the wrong trainer party was loaded or if the world cannot tell whether to mark the trainer defeated.

And that is a real production lesson. Many battle bugs are not inside damage calculation. They are in setup and teardown. Wrong opponent. Wrong battle type. Wrong continuation. Wrong flag. Wrong post-battle script. Wrong return map state. Those are week 15 bugs.

So, before we move to the transition mechanics in more detail, I want you to hold onto three distinctions. Data source is not activation context. Activation context is not normalized battle setup. Battle outcome is not world consequence. If those three distinctions are clear, FireRed’s structure becomes much easier to read, and Showdown’s contrasting architecture becomes more useful instead of distracting.

In the next stretch, we will keep that board diagram visible and walk from the normalized setup side into the transition and load side: what gets prepared before the battle main callback owns the frame, and how the game avoids losing the world state it has to return to.

So, pick up at the right side of that diagram. We have a normalized battle setup object conceptually, even if FireRed does not package it as one tidy modern object with that exact name. The important architectural move is that the game has to cross a boundary. On one side, we are in the field runtime: map objects, scripts, player avatar, encounter checks, trainer sight lines, event flags. On the other side, we are in battle: battlers, parties, battle type flags, controllers, battle scripts, result variables. The crossing itself is not just a fade effect. It is a state transfer.

Let me say that again because it is the core of today’s lecture. The transition into battle is not merely presentation. It is where the engine freezes one world-state machine and prepares another one to take over.

If you look at pokefirered/src/battle_setup.c, chunk chunk_494d132a59422c85, even the include list tells a story. This file reaches into task.h, overworld.h, item.h, load_save.h, safari_zone.h, quest_log.h, script.h, event_data.h, event_object_movement.h, field_player_avatar.h, field_screen_effect.h, battle.h, battle_transition.h, and battle_controllers.h. That is not accidental clutter. That is the source-level evidence that battle setup is a junction module. It knows about field state, event state, transition presentation, party data, and battle ownership. It is not “the battle engine” in the narrow sense. It is the adapter between the overworld and the battle engine.

Now, board cue here. I am drawing a vertical line. Left side: overworld runtime. Right side: battle runtime. In the middle, I am writing “setup bridge.” Under overworld, I will put “map, script, object, encounter, trainer id.” Under battle, I will put “battle type flags, opponent data, party references, callback continuation, result storage.” That middle bridge is where most clone projects become fragile, because they let the wild encounter path and the trainer path each improvise their own handoff.

Let’s take a concrete example. Suppose the player steps into tall grass on Route 3. The wild encounter resolver samples from wild_encounters.json. That source, pokefirered/src/data/wild_encounters.json, is high-signal for today because it is the external data source for area-based encounter candidates. But that JSON is not a battle. It can tell you the map group or location context, encounter method, species candidates, levels, and rates. It cannot tell you all by itself whether the battle should allow running, whether Safari Zone rules apply, what transition visual to use, or what callback should resume the field afterward. Those are activation-context decisions.

Now contrast that with a trainer battle. The trainer table in pokefirered/src/data/trainers.h is likewise high-signal, but in a different direction. A trainer record points toward party composition, trainer class, name, items, AI-related setup, and so on. It is much closer to “opponent identity” than wild_encounters.json is. But even that trainer table is still not the complete battle setup. The trainer record does not, by itself, know that a particular map event just spotted the player from three tiles away, that a pre-battle script line should run, or that after victory a flag needs to prevent rematching unless a rematch system intervenes.

Do you see the symmetry? Wild encounter data is not enough. Trainer data is not enough. Both need a caller context. And then both need to become one shared shape that the battle engine can consume.

Check for understanding. If I ask, “Where should the code decide the species of a random wild Pidgey?” you should point toward the wild encounter resolver, using pokefirered/src/data/wild_encounters.json. If I ask, “Where should the code decide that this battle returns to the overworld callback rather than to a menu callback?” you should not point to the wild table. You should point to battle setup, to the bridge layer. If I ask, “Where should Flamethrower’s burn chance be resolved?” that is later, inside battle behavior, and we are not re-teaching that today.

This is also where our prior weeks matter. In weeks 12 through 14, you traced data records through globals, callbacks, tasks, scripts, structs, and visible behavior. That tracing discipline is no longer a warm-up exercise. Week 15 asks you to apply it across a system boundary. The homework and quiz last week checked whether you could design minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence. Today I am raising the bar: your clone architecture now has to let those systems exchange control without smearing responsibilities together.

So, let’s walk the bridge more slowly.

Before battle main owns the frame, FireRed needs to prepare enough information that the battle engine is not asking the field engine questions every turn. That is a critical separation. During battle, the engine should not be repeatedly asking, “Which map tile did this come from?” or “Which object event noticed the player?” Those questions belong before battle. The battle engine should receive the battle type, parties, opponent identity, terrain-relevant or mode-relevant flags, and enough continuation state to return afterward.

This is why the battle setup layer has to preserve world state. The world is not destroyed when the battle begins. It is suspended. The player’s map position, object events, script context, and post-battle routing still matter. If the player wins a trainer battle, the object event may need to stop challenging the player. If the player loses, the game may route to a Pokémon Center or some other loss consequence. If the player runs from a wild battle, the overworld resumes in place. If Safari rules are active, the result may consume special state. If the battle is interrupted by link or special cases, even more continuation rules appear.

Notice how different that is from the internal turn loop we discussed in recent lectures. There, chosen actions by battler and turn-order arrays help decide what happens next inside combat. Here, the question is not “Which battler acts first?” The question is “Who gets control when battle stops?” Those are both state-machine questions, but they live at different levels.

Now let’s bring in Pokémon Showdown carefully, as a secondary comparison. Showdown is useful here because it makes a different architectural tradeoff. In the Showdown codebase, battle data and battle simulation are organized around a Dex and mods system, with data modules such as pokemon-showdown/data/learnsets.ts and modded data like pokemon-showdown/data/mods/gen9dlc1/learnsets.ts, pokemon-showdown/data/mods/gen9predlc/learnsets.ts, and pokemon-showdown/data/mods/gen8dlc1/learnsets.ts. That structure is excellent for saying, “Given this generation or ruleset, what is the authoritative data for species, moves, learnsets, and effects?” It supports a simulator that can run deterministic turns and regression tests. But it is not modeling a Game Boy Advance overworld with map callbacks, NPC object events, and script continuations.

So when we compare Showdown to FireRed, do not say, “Showdown is cleaner, therefore FireRed should have been written like Showdown.” That is too shallow. Showdown’s main product is the battle simulator and competitive rules engine. FireRed’s main product is a cartridge RPG where battle is embedded inside exploration, events, flags, story progression, inventory, money, and save state. Showdown’s hooks are shaped by battle correctness across formats. FireRed’s hooks are shaped by entering and leaving battle from an overworld.

A good advanced architecture comparison asks: what boundary is each project optimizing?

For Showdown, the boundary is often between ruleset data and simulation behavior. The Dex/mod layer lets the simulator ask, “Under this generation or mod, what does this species or move mean?” The old simulator documentation in pokemon-showdown/old-simulator-doc.txt is one of the recurring course modules because it shows the project thinking explicitly about effects, moves, and battle events. The FireRed boundary today is different: it is between field activation and battle setup. Both are data-driven, but the data hooks are located at different seams in the runtime.

Let me make that concrete with a clone-design example. Suppose you are building the lab for this week: implement a wild encounter resolver and trainer battle resolver that both create the same battle setup object and return a battle result consumed by overworld logic. In your clone, you might define a BattleSetup with fields like mode, playerPartyRef, opponentParty, opponentTrainerId, canRun, transitionKind, backgroundKind, returnCallback, preBattleScriptId, postBattleScriptId, and worldContinuationToken. I am not saying FireRed has exactly that neat struct. I am saying this is the modern shape that expresses the responsibility FireRed distributes through globals, flags, callbacks, and setup functions.

For wild battles, the resolver fills opponentParty by sampling species and level from the area encounter data. opponentTrainerId is empty. canRun is usually true. postBattleScriptId is probably absent. worldContinuationToken points back to the current map state.

For trainer battles, the resolver fills opponentTrainerId from the trainer event or script, loads party data from the trainer table, canRun is false under normal rules, and postBattleScriptId or trainer-object state matters. The same BattleSetup shape moves forward, but different fields are populated.

This is the key design pattern: different sources, same handoff.

Now, if you fail to normalize this, your code will start branching everywhere. The battle main loop will ask, “Am I wild? Am I trainer? Am I Safari? Am I scripted? Am I rematch?” Some branching is unavoidable because battle modes are real. But if every subsystem has to rediscover the origin story of the battle, your architecture has leaked. The setup layer did not do its job.

FireRed’s C architecture naturally uses shared globals and flags more than a modern object-oriented or data-oriented engine might. We should not pretend it is a clean dependency-injected service graph. But the architectural intent is still legible. pokefirered/src/battle_setup.c, chunk chunk_494d132a59422c85, sitting among overworld, script, event data, transition, and battle headers, is exactly the kind of junction we expect in this engine. It prepares the conditions under which the already-studied battle main callback can safely take over.

Let’s pause for a check. Imagine a student submission where wild encounters directly call StartBattleWithWildMon, trainer encounters directly call StartTrainerBattle, and each one separately decides the fade, result handling, and callback return. Is that automatically wrong? Not necessarily for a tiny prototype. But for this course, at week 15, it is incomplete. You now have enough systems in play that you need a shared setup representation, even if it is internal. Otherwise tomorrow’s lecture, resolving battle outcomes back to the world, becomes a pile of special cases.

And that leads us to result storage.

We are not resolving outcomes in full yet, but setup has to anticipate outcomes. If the engine enters battle without remembering how to continue, it cannot exit correctly. The battle result path in pokefirered/src/battle_main.c is represented in the packet by chunk chunk_1c5a1226d8d9afb7 for battle main result flow and chunk chunk_46361bdbd92dae37 for cleanup and result continuation. Those chunks matter because they remind us that “battle ended” is not a single event. It is a sequence: determine result, clean up battle-owned resources, restore or continue the appropriate non-battle state, and let the world interpret the result.

The include-level and struct-level evidence also lines up with this. pokefirered/include/battle.h, chunk chunk_f5cd027bf13ab47d, is identified in the packet around BattleStruct and result-related state. That tells us result information is part of the battle contract, not just a local variable printed at the victory screen. It must survive long enough to be consumed by code outside the immediate damage and action execution path.

Here is the subtle point: the battle result is not the same thing as the overworld consequence. A battle result might be win, loss, ran, caught, fled, draw, or special outcome. The world consequence might be set trainer defeated flag, remove temporary object, advance script, warp player, deduct money, return to field, update quest log, or trigger a rematch cooldown. Those are not the same layer.

Write that in your notes as a rule: battle decides what happened in battle; overworld decides what that means in the world.

Now, in FireRed, because this is a GBA C codebase, the boundary is not expressed with a pristine event bus. It is expressed through callback changes, globals, script state, flags, and cleanup functions. That is why earlier in the course we spent so much time on SetMainCallback2, tasks, and scripts. At the time, those may have felt like separate lectures. Today they converge. Battle setup needs callbacks because the engine must hand frame ownership from field to battle. It needs scripts because trainer battles and post-battle dialogue are world events. It needs event data because flags determine whether an NPC can challenge again. It needs transition code because the player must see a coherent mode switch. And it needs battle data because the battle engine must know what it is fighting.

Let’s do a board trace.

I’ll write “Trainer sees player.” Arrow to “field event or script activation.” Arrow to “trainer id resolved.” Arrow to “trainer table lookup in pokefirered/src/data/trainers.h.” Arrow to “battle setup fills battle type and opponent party.” Arrow to “transition/load.” Arrow to “battle main owns frames.” Then a dotted arrow forward, not fully today, to “result stored in battle_main.” Then “cleanup and continuation.” Then “overworld consumes result.”

Now underneath, the wild version.

“Step in grass.” Arrow to “encounter check.” Arrow to “wild encounter table lookup in pokefirered/src/data/wild_encounters.json.” Arrow to “species and level sampled.” Arrow to “battle setup fills wild battle type and generated opponent party.” Arrow to the same “transition/load.” Arrow to same “battle main owns frames.” Same dotted result path. Same overworld consumption.

The two traces differ at the left and at some consequence rules on the right. They should converge in the middle.

That convergence is what your lab is trying to force. If your wild resolver returns one kind of object and your trainer resolver returns another kind of object, you have missed the lesson. You can have different constructors. You can have different source data. You can have different validation. But the battle entry point should receive a normalized setup.

Let’s compare again to Showdown, because this is where the comparison becomes practically useful for testing. Showdown’s simulator culture leans heavily toward deterministic battle turns and regression testing. The data in pokemon-showdown/data and pokemon-showdown/data/mods makes it possible to run the same battle scenario under known rules and verify that a change did not break expected behavior. In FireRed-style architecture, your comparable regression tests for week 15 should not be only “Tackle does correct damage.” We already know how to think about move behavior from prior battle-script lectures. Your tests should include “wild encounter setup produces a battle with the expected mode and generated opponent,” “trainer setup produces a no-run battle with the expected trainer party,” and “a win result routes to the expected overworld continuation.”

This is an advanced testing point. Test the boundary, not just the algorithm.

For example, a good week 15 regression test might seed the random generator, place the player in a map with a known wild encounter table, run the resolver, and assert that the produced BattleSetup has mode wild, a legal species from that map’s table, a legal level range, canRun true, and a return continuation that points back to field control. Another test might activate trainer 42, assert that the trainer party came from the trainer table, that canRun is false, that the trainer id is preserved, and that the post-battle continuation can set the defeated flag after a win.

Notice what those tests do not assert. They do not assert the entire battle turn sequence. That belongs to the battle simulator layer. They assert the data handoff. That is today’s topic.

Now, let me ask you a comprehension question out loud. If a trainer battle accidentally allows the player to run, where do you start looking? Not damage calculation. Not move effects. Not turn order. You start at battle type flags and setup. If a wild battle starts against the wrong species for the map, where do you start? The wild encounter table and resolver. If the correct species appears but after the battle the NPC dialogue resumes incorrectly, where do you start? Result continuation, script state, and overworld routing. Three bugs, three different layers.

That diagnostic discipline is part of the course plan. In week 1, we decomposed the RPG into runtime shell, overworld maps, event scripting, creature data, battle, inventory, UI, save, and link systems. At the time, that decomposition was a map of the territory. By week 15, it becomes a debugging tool. You should be able to say, “This symptom belongs to a boundary between systems,” not just “Something is wrong in battle.”

A lot of professional game bugs live exactly here. The battle itself is correct, but the wrong battle started. The battle result is correct, but the world consumed it incorrectly. The world consequence is correct, but the setup forgot to preserve the needed identity. When you see those, think bridge layer.

Now I want to make one more subtle distinction before we continue into transition mechanics. The transition visual is downstream of the setup decision, but upstream of battle execution. It is not purely cosmetic because it participates in timing and ownership transfer. On the GBA, you cannot casually have every subsystem draw whenever it wants. The main callback, VBlank work, tasks, sprites, palettes, and decompression work all have to be coordinated. So when pokefirered/src/battle_setup.c includes battle_transition.h in chunk chunk_494d132a59422c85, treat that as an architectural clue. Transition is part of mode switching. It gives the engine time and structure to leave field presentation and load battle presentation.

But be careful: transition is not where opponent data should be invented. Transition should not decide that the trainer has a Bulbasaur. Transition should display and sequence the handoff after setup has already decided what kind of battle is coming. If you mix those responsibilities, you create hard-to-test code, because now visual timing affects battle identity.

In a modern clone, I would usually model this as three separate steps. First, resolve encounter or trainer activation into a BattleSetup. Second, run a BattleTransition using fields from that setup. Third, enter BattleScene or BattleMain with the prepared setup. FireRed distributes this across C functions, globals, and callbacks, but the conceptual separation still helps you read it.

Let me give you a failure mode. Suppose your transition code samples the wild encounter at the moment the screen finishes fading. During testing, someone changes transition timing or skips animations in fast mode. Suddenly the encounter distribution changes, or worse, the random stream changes and affects later battle behavior. That is a design smell. Sampling the encounter belongs before the transition, in the setup phase. The transition should consume a decision, not create it.

The same applies to trainer battles. If the trainer party is loaded lazily after the transition, and the object event state changes before then, you can accidentally load the wrong trainer or lose the script context. FireRed’s architecture is designed around constrained hardware and global state, but the reason we study it is that these ordering problems are still real in modern engines.

Now, quick local recap before we go deeper. We have three layers on the board. The data source layer includes trainers.h and wild_encounters.json. The activation layer includes map events, scripts, trainer sight, grass steps, Safari context, and other world conditions. The normalized setup layer prepares battle type, opponent identity, party data, continuation, and transition information. Then battle main can run using the foundations we already covered in recent lectures. Afterward, result flow and cleanup, represented in pokefirered/src/battle_main.c chunks chunk_1c5a1226d8d9afb7 and chunk_46361bdbd92dae37, carry us toward tomorrow’s topic: how the world consumes the outcome.

So as we move into the next section, keep asking this question: at this line of code, is the engine still deciding what battle this is, or is it now merely presenting and loading a battle that has already been decided? That question will keep you from confusing setup, transition, battle execution, and aftermath.

Now, let’s make that question practical. Suppose the player takes a step in grass. The grass itself is not a battle. The map is not a battle. The wild encounter table is not a battle. Even the random roll, by itself, is not yet the battle engine. What we have is a chain of increasingly specific commitments. First, the overworld says, “This movement may trigger an encounter.” Then the encounter resolver says, “Given this map and encounter table, here is a species and level.” Then battle setup says, “Given that resolved encounter, configure the global battle state and launch the transition.” Only after that do we enter the battle main machinery that we studied last week.

So if I draw this on the board, I am going to draw it as a funnel, not as a flat list. At the wide top of the funnel, we have world context: map, terrain, player movement, flags, Safari state, Repel-like effects, and any other encounter modifiers. In the middle, we have data lookup: for wild battles, the table represented by pokefirered/src/data/wild_encounters.json; for trainer battles, the trainer table represented by pokefirered/src/data/trainers.h. At the narrow bottom, we have one normalized battle setup object, or in FireRed’s actual C architecture, one normalized collection of globals, flags, selected opponent identifiers, party slots, and callback continuations.

That normalized bottom is the important architecture lesson. FireRed does not want every battle origin to become its own battle engine. It wants wild grass, trainer sight, scripted boss encounters, Safari encounters, and special cases to converge into the same battle system. That is why pokefirered/src/battle_setup.c, especially the representative transition and load material in chunk_494d132a59422c85, is such a useful source point for us today. Notice the include list in that chunk. It pulls in overworld, script, event data, field player avatar, Safari zone, quest log, battle, battle transition, and battle communication. That include list is not just boilerplate. It is a map of responsibility. Battle setup sits at a border. It knows enough about the world to leave it cleanly, and enough about battle to initialize it correctly.

Let me give you a concrete example. Imagine a normal wild encounter with a level 5 Pidgey in grass. The wild encounter resolver’s output, conceptually, is small: species Pidgey, level 5, maybe encounter slot metadata, maybe whether this is a special encounter class. But battle setup needs more than species and level. It must arrange the opponent party representation, mark the battle as wild rather than trainer, choose the appropriate battle type flags, prepare the transition, and remember how to return to the field when the battle is over. So the setup layer translates “a wild Pidgey happened” into “the battle engine can now run a battle with a player side and an enemy side, with these parties and these rules.”

Now contrast that with a trainer battle. The trainer data in pokefirered/src/data/trainers.h is much richer than a wild slot. It can imply a trainer class, name, party composition, AI behavior, reward money, intro text, defeat text, and so on. But after the trainer has been selected, after the sight event or script has committed to that opponent, battle main still should not care that the trainer was standing three tiles north of the player. It should care that the opponent side has a party, that this is a trainer battle, that certain scripts or continuation states should run afterward, and that the battle result has consequences. The engine’s elegance comes from compressing very different world origins into one battle-shaped contract.

Check your understanding here. If I ask, “Where should the game decide whether Youngster Ben saw the player?” the answer is not battle main. That belongs in the overworld activation and script layer. If I ask, “Where should the game calculate damage from Tackle?” the answer is not battle setup. That belongs in the battle execution layer we studied in the recent lectures on battle scripts, current move data, damage, and shared state. If I ask, “Where should the game mark that this battle is a trainer battle and load the trainer party?” now we are in today’s layer, the encounter-to-battle setup layer.

This is also the right place to bring in Pokemon Showdown, but carefully. Pokemon Showdown is not our replacement architecture. FireRed remains the primary source because we are studying a cartridge-era RPG that must return to maps, scripts, callbacks, and saveable world state. Showdown is useful as a secondary comparison because it makes some battle data hooks explicit in a modern simulator style. In the research packet, the recurring Showdown modules include pokemon-showdown/data, pokemon-showdown/data/mods, pokemon-showdown/server, and pokemon-showdown/test. That tells us something: Showdown’s battle architecture emphasizes a Dex abstraction, modded data overlays, simulator turns, and regression tests. FireRed emphasizes compiled data tables, global runtime state, callbacks, tasks, scripts, and hardware-conscious transitions.

So, on the side of the board, I will write “FireRed: world-to-battle pipeline” and “Showdown: data-to-simulation pipeline.” Those are not enemies. They are two answers to different constraints.

In Showdown, if you want to ask, “What is a move, what is a species, what does this generation change,” you tend to go through the Dex and mods structure. The packet gives examples like pokemon-showdown/data/learnsets.ts and the modded learnset paths such as pokemon-showdown/data/mods/gen9dlc1/learnsets.ts and pokemon-showdown/data/mods/gen9predlc/learnsets.ts. The architectural point is that Showdown can represent rule variants as layered data and scripts. FireRed, by contrast, is not trying to host every generation as a selectable simulator mod. It is shipping one concrete game, with one world and one battle system, and its setup code is built to move the player from that world into that battle.

But Showdown gives us a useful testing thought. In a simulator, you can often construct a battle directly from data, advance turns, and assert outcomes in tests. That is part of why pokemon-showdown/test appears in the research packet as a recurring module. In FireRed, we have to think harder. If our lab asks you to implement a wild encounter resolver and a trainer battle resolver that both create the same battle setup object and return a battle result consumed by overworld logic, you should borrow the test philosophy from Showdown, not the whole architecture. That means you should ask: can I test the resolver separately from the renderer? Can I test that a trainer ID produces the expected setup shape? Can I test that a wild encounter slot produces the same normalized setup shape as a trainer encounter, except for battle type and opponent source?

That is the comparison I want you to keep. Showdown encourages us to make battle data hooks explicit and regression-testable. FireRed teaches us how those hooks fit into an overworld RPG with callbacks, transitions, scripts, and persistent event state.

Now, let’s tie that back to FireRed’s source points. The packet identifies pokefirered/src/battle_setup.c chunk_494d132a59422c85 as representative for battle transition, load, and event-related setup. The include dependencies there show this file touching help system, overworld, item, sound, Pokemon data, load-save, Safari zone, quest log, script, field effects, metatile behavior, player avatar, battle, battle transition, and battle communication. That is exactly the border file you would expect. If battle setup were pure battle math, those includes would look strange. But it is not pure battle math. It is the airport terminal between the world and combat.

Then, after the battle runs, the flow must come back out. The packet points us to pokefirered/src/battle_main.c chunk_1c5a1226d8d9afb7 for battle main result flow and pokefirered/src/battle_main.c chunk_46361bdbd92dae37 for cleanup and result continuation. We are not going to re-teach the battle main callback today, because we already used that as a foundation in the previous lectures. But I do want you to notice the symmetry. Battle setup moves from world state into battle state. Battle result flow moves from battle state back into world state. Tomorrow’s lecture lives on the second half of that bridge.

Pause for a local recap. A wild encounter table answers, “What can appear here?” A trainer table answers, “Who can challenge the player, and with what party?” The activation layer answers, “Is this the moment to start that battle?” The setup layer answers, “What exact battle should the engine run?” Battle main answers, “How does the battle proceed?” Result flow answers, “What does the world do with the outcome?”

Now let’s make this more advanced, because by week 15 you should be suspicious of vague words like “starts” and “loads.” Starting a battle is not one operation. It is at least four operations that happen close together. First, the game selects or receives an encounter source. Second, it writes battle configuration into shared state. Third, it schedules or switches into a transition path. Fourth, it lets the battle callback take ownership of frames. If you compress those into one phrase, you will misplace logic.

For example, suppose your clone engine has a function called startBattle. If startBattle rolls the wild encounter table, creates the opponent party, fades the screen, runs the entire battle loop, writes the result, changes map flags, and warps the player to a Pokemon Center after losing, then that function is too large architecturally. It may work for a prototype, but it gives you no clean place to test. A FireRed-inspired architecture would split that responsibility. Your encounter resolver chooses the encounter. Your battle setup builder normalizes it. Your battle scene runner executes it. Your battle result consumer updates the world.

Let’s use a trainer example. A trainer object event detects the player. A script or event routine identifies the trainer. That trainer ID indexes the data in pokefirered/src/data/trainers.h. The setup layer uses that ID to prepare a trainer battle. The battle engine runs. The result flow records whether the player won or lost and chooses the correct continuation. If the player wins, the trainer may set a defeated flag, show post-battle text, and stop challenging the player. If the player loses, the game may route to loss handling. The battle engine should not be personally responsible for knowing every map object that might be affected. It should produce a result that the world layer can consume.

This is where the include of event_data and script facilities in pokefirered/src/battle_setup.c chunk_494d132a59422c85 matters. Battle setup is close enough to event state to participate in launch and continuation, but that does not mean the battle math layer should become an event scripting engine. Again, border modules know about both sides. Core modules should not become everything.

Let me ask a check-for-understanding question. If a student writes a lab solution where wild battles return a Boolean called playerWon, and trainer battles return an integer called trainerOutcome, and scripted battles directly mutate map flags inside the battle loop, what is the architectural smell? The smell is that there is no normalized battle result. Each origin has invented its own output shape. That makes overworld return logic brittle, because every caller has to remember a different contract. The lab’s phrase “both create the same battle setup object and return a battle result consumed by overworld logic” is intentionally pushing you away from that.

A better design is to have a setup record with fields like battle kind, opponent source, opponent party, rules or flags, entry transition, and continuation token. Then have a result record with fields like outcome, escaped or caught or defeated, money or reward implications, and continuation token. I am not saying FireRed literally uses a modern heap-allocated object with those exact fields. In C on the GBA, this is often distributed across globals, constants, arrays, and structs. The conceptual architecture, however, is the same: normalize input before battle and normalize output after battle.

The BattleStruct and result-related state in pokefirered/include/battle.h chunk_f5cd027bf13ab47d is relevant here because headers are contracts. We made this point earlier in the course when studying headers as cross-module contracts, and now it becomes concrete. A header tells other modules what battle state can be named and relied upon. That does not mean every field is beautiful domain modeling by modern standards. It means the engine has a shared vocabulary for battle state, including state that survives across phases of battle execution and result handling.

Now, let’s bring Showdown back for one minute. In Showdown, because the simulator is often driven by explicit battle objects and turn progression, regression testing can instantiate scenarios directly. You can say, in spirit, “Given these teams and this modded Dex, when these choices are made, the resulting battle state should match expectations.” The packet’s Showdown example pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f shows damage logic using move objects, active moves, immunities, OHKO checks, damage callbacks, and fixed-damage handling. That is much deeper inside battle execution than today’s FireRed setup layer, but it illustrates the modern simulator pattern: data hooks and scripts are designed to be invoked inside a controlled simulation.

FireRed’s setup challenge is different. It has to enter the battle from a living map and later return to that map. That is why, in this course, Showdown is useful for comparison but dangerous as a replacement. If you copy Showdown’s direct simulator entry point into a FireRed-style RPG, you may get a nice battle test harness while losing the core world integration problem. If you copy FireRed’s world-coupled setup into a server simulator, you may end up with unnecessary global state and awkward tests. Architecture is always a response to the product shape.

Now I want to connect this to the course plan, because week 15 is not isolated. In weeks 1 and 2, we talked about the runtime shell, AgbMain, main callbacks, task allocation, and generated data. That gave us the idea that visible behavior is produced by data records moving through callback and task machinery. In the overworld weeks, we looked at maps, object events, scripts, and field behavior. In the creature data weeks, we looked at species, parties, moves, and storage. In the recent battle weeks, we studied battle main as coordinated state, chosen actions by battler, turn-order arrays, battle scripts as bytecode, and damage/status through shared state. Today’s lecture connects those units. Encounter data into battle setup is a joining lecture. It says: here is how the map and data systems hand control to the battle system without dissolving into one untestable blob.

And this is why your week 15 homework is more architectural than it may look. If I ask you to implement a wild encounter resolver and a trainer battle resolver, I am not primarily asking, “Can you pick a random species?” You can all do that. I am asking, “Can you make two different input domains converge into one battle setup contract?” And when I ask that both return a battle result consumed by overworld logic, I am not primarily asking, “Can you use an enum?” I am asking, “Can you preserve the boundary between battle execution and world consequence?”

Let’s walk through a possible board-work version of that lab. I would write a function called resolveWildEncounter that takes map encounter data and world context. It returns either no encounter or a resolved wild encounter. Then I would write buildBattleSetupFromWild, which takes the resolved encounter and returns the normalized setup. Separately, I would write resolveTrainerBattle, which takes a trainer ID or event context and returns a resolved trainer encounter. Then buildBattleSetupFromTrainer returns the same kind of setup record. At that point, runBattle does not need two public entry points for wild and trainer logic. It needs one setup contract.

After the battle, consumeBattleResult takes the setup token and result. For a wild battle, maybe it returns the player to the same map, updates caught species data, or handles escape. For a trainer battle, maybe it sets a defeated flag, awards money, advances dialogue, or handles loss routing. This is where tomorrow’s topic begins: resolving battle outcomes back to the world. Today we stop at the handoff and keep the return path visible.

One more check for understanding. If you were writing a regression test inspired by Showdown, what would you test first in this week’s lab? I would not start with animation. I would not start with a full turn-by-turn battle. I would test that a wild encounter and a trainer encounter both produce valid setup records with the same required fields. Then I would test that a victory result and a loss result are both consumed by overworld logic without the battle runner knowing map-specific details. That is the FireRed lesson, sharpened by Showdown’s test culture.

So, as we continue, keep the architecture layered in your head. FireRed gives us the primary model: data tables, event activation, battle setup, transition, battle main, result continuation, and overworld return. The source packet anchors that in pokefirered/src/data/trainers.h, pokefirered/src/data/wild_encounters.json, pokefirered/src/battle_setup.c chunk_494d132a59422c85, pokefirered/src/battle_main.c chunk_1c5a1226d8d9afb7, pokefirered/src/battle_main.c chunk_46361bdbd92dae37, and pokefirered/include/battle.h chunk_f5cd027bf13ab47d. Showdown gives us the secondary lens: explicit Dex data, mods, simulator turns, and regression tests. We use that lens to ask better questions of FireRed, not to erase FireRed’s constraints.

Now, with that model established, the next thing I want to examine is the shape of “battle type” itself. Because battle type is where many students accidentally hide half the architecture. A battle type flag is not just a label for the user interface. It can determine which parties are loaded, whether escape is allowed, whether trainer payout exists, whether capture is possible, which intro music or transition is appropriate, whether Safari rules apply, whether link behavior applies, and what result meanings are legal. In other words, battle type is a compact encoding of assumptions. If you design it casually, every later phase will pay for that casualness.

So let’s make that more concrete. Imagine I write, on the board, a little field named battleType. Very innocent-looking. One word, maybe a bitmask, maybe an enum, maybe a small struct field. And then beside it I write three incoming cases: wild grass encounter, scripted trainer battle, and Safari encounter. If all three cases simply say, “start battle,” your architecture is lying to you. They do not mean the same thing.

For a normal wild encounter, the setup needs species, level, maybe slot-derived encounter metadata, and the player’s party. The opponent side is synthesized. There is no trainer class, no trainer payout, no post-defeat trainer script, and capture is allowed under normal rules. For a trainer battle, the opponent side is not synthesized from a map encounter slot. It is loaded from authored trainer data, and that is why pokefirered/src/data/trainers.h matters this week as a high-signal table source. The trainer battle has identity. It has a trainer id, trainer class, party format, item rules, money rules, music rules, and an event-script continuation after the battle. For Safari, the creature may still originate from wild encounter machinery, but the legal player actions and result meanings are different. The architecture has to know that before the first battle menu is displayed.

Now pause there, because this is a common advanced-level trap. You already know enough battle engine internals from weeks 13 and 14 that you may be tempted to think the hard part begins once battlers are on screen. But in a data-driven RPG, much of the hard part is deciding which assumptions are allowed to enter the battle engine in the first place. FireRed’s setup layer is doing that boundary work. The research packet points us to pokefirered/src/battle_setup.c chunk_494d132a59422c85, and even from the include surface alone you can see the dependency fan-in: overworld, script, event data, Safari zone, quest log, field movement, battle transition, battle communication, battle tower, and battle records all sit near this setup layer. That is not accidental clutter. That is the architectural location where world state is translated into battle state.

So, if I draw the board version, I do not draw overworld directly calling damage calculation. That would be the wrong mental model. I draw overworld and script state feeding an encounter resolver. That resolver produces a battle setup object, even if the original C code expresses that through globals and flags rather than through a neat object value. Then the setup code chooses battle type, opponent data, transition behavior, and callback continuation. Only after that do we enter the battle main loop that we have already studied. We are not going to re-teach chosen actions, turn order arrays, or battle script dispatch today. Those are downstream consumers. Today’s question is: what shape must the entrance ticket have so that those downstream systems are not forced to guess?

Here is a concrete example. Suppose the player steps into tall grass on Route 3. The wild encounter table, named in our packet as pokefirered/src/data/wild_encounters.json, gives us an authored input space. It is data, not behavior. It can tell us which species and level ranges are plausible for a location and encounter type. But it does not, by itself, know whether the player has a repel active, whether the party lead has an ability affecting encounters, whether a script has disabled encounters, whether the map transition has just occurred, or whether the engine is currently allowed to leave overworld mode. Those checks belong in runtime logic. Once the runtime accepts that an encounter occurs, the selected species and level are placed into a battle setup path, and battle type says, effectively, “this is wild; construct the enemy side accordingly; allow capture; use wild result semantics.”

Now compare that with a trainer standing on the same map. The map object event and script may determine that line-of-sight has triggered. The trainer id then points into pokefirered/src/data/trainers.h. Here the opponent side is not a random species-level pair. It is an authored party, and the setup path must preserve the trainer identity because result handling later depends on it. When pokefirered/src/battle_main.c chunk_1c5a1226d8d9afb7 and chunk_46361bdbd92dae37 are called out in the packet for battle result flow and cleanup or continuation, that is our reminder that battle setup and battle outcome are paired. If the setup forgets the trainer identity, the result continuation cannot reliably mark the trainer defeated, route the loss, pay money, resume the script, or return the player to the right overworld context.

Check your understanding here. If I give you a bug report that says, “After defeating a trainer, the overworld returns correctly, but the trainer can be fought again immediately,” where would you look first? Not damage calculation. Not move priority. Not the sprite OAM code. You would inspect the path from trainer encounter setup to result continuation. Was the trainer id stored? Was the relevant event flag set after victory? Did the callback return to the script continuation that performs that update? That is the kind of diagnosis week 15 expects you to make without wandering through the entire battle engine.

Now let’s bring in Pokemon Showdown, carefully. Showdown is useful here because it is much more explicit about data hooks and testable simulation boundaries, but it solves a different product problem. FireRed is a GBA RPG with an overworld, scripts, persistent flags, maps, party storage, and hardware-shaped callbacks. Showdown is a battle simulator and server platform. So when we look at Showdown’s Dex and mods, we are not saying, “FireRed should have been written this way.” We are saying, “Showdown exposes some seams that FireRed compresses into C globals, flags, tables, and callback transitions.”

For example, Showdown’s data and mod system lets battle behavior vary by generation or ruleset through explicit layered data. The research packet points repeatedly to pokemon-showdown/data, including learnsets and modded learnsets such as pokemon-showdown/data/mods/gen9dlc1/learnsets.ts, pokemon-showdown/data/mods/gen9predlc/learnsets.ts, and pokemon-showdown/data/mods/gen8dlc1/learnsets.ts. That is a different expression of the same architectural pressure: battle setup must bind a battle to a rule context. In FireRed, the rule context is mostly fixed by the cartridge and by battle type flags. In Showdown, the simulator may ask the Dex for data under a selected mod. Either way, a battle cannot be interpreted without the data universe it belongs to.

Let me say that another way, because it matters for your workshop design. A battle setup object should not only say, “opponent is species X at level Y.” It should also imply, or explicitly contain, the rule environment in which species X, level Y, moves, items, win conditions, and result handling are meaningful. In FireRed clone architecture, that may be a small field: wild, trainer, double, Safari, scripted, link, tower, or whatever subset you implement. In a Showdown-like simulator, that may be a format id or mod id that routes through Dex lookups and rule tables. The details differ, but the design question is the same: what data has to be selected before turn one?

Now, on the slide, I would put two columns. Left column: FireRed. Right column: Showdown. Under FireRed I write, “world-driven battle setup.” Under Showdown I write, “format-driven battle construction.” FireRed asks, “What did the player touch in the world, and what persistent state should change afterward?” Showdown asks, “What ruleset, teams, and simulator state define this battle, and how do we produce deterministic results?” Both need battle data hooks, but the hooks attach to different surrounding systems.

This is why pokefirered/include/battle.h chunk_f5cd027bf13ab47d is an important source for us. The BattleStruct and result-related state are not merely internal bookkeeping. They are part of the contract between setup, battle execution, and aftermath. The battle engine needs local state while it runs, yes, but the world also needs a compact answer afterward. Did the player win? Lose? Flee? Capture? White out? End a link battle? Trigger a special facility rule? If you cannot represent that result cleanly, then overworld code starts depending on fragile side effects. And once overworld code depends on fragile side effects, your game becomes difficult to test, difficult to script, and difficult to extend.

Let’s test that with a small design exercise. Suppose your lab implementation has two functions: resolveWildEncounter and resolveTrainerBattle. If resolveWildEncounter directly calls startWildBattle, and resolveTrainerBattle directly calls startTrainerBattle, with two unrelated parameter lists, your first version may work. But by the time you implement battle results, you will notice duplication. Both need the player party. Both need a battle type. Both need opponent party construction. Both need a return callback or continuation. Both need to produce a battle result consumed by overworld logic. That is why the lab asks you to create the same battle setup object from both sources. The goal is not object-oriented ceremony. The goal is to make the boundary explicit enough that result handling can be shared.

A good minimal setup object might contain battleType, playerPartyRef, opponentSource, opponentParty or opponentGenerator, transitionKind, musicKind, canRun, canCatch, rewardPolicy, and returnContinuation. In C, FireRed does not hand you exactly that tidy object in one file. It uses globals, flags, and functions across modules. But for architecture learning, naming the conceptual object is valuable. It lets you reason about the invariant: once battle setup is committed, battle main should not need to ask the map why this fight exists. It should run the fight under the supplied assumptions and emit a result.

Now, because you are advanced students, I want to sharpen the phrase “emit a result.” A result is not the same thing as every side effect that happened during battle. Experience gain, HP changes, PP changes, captured creature data, money loss, trainer defeated flag, and script variables may all be affected. But architecturally, we want a summarized battle outcome that drives routing. FireRed’s battle result flow, again represented in the packet by pokefirered/src/battle_main.c chunk_1c5a1226d8d9afb7 and cleanup continuation by chunk_46361bdbd92dae37, is where that routing becomes visible. The battle engine does not simply vanish when the last HP bar drains. It has to unwind battle resources, interpret the result, and return control to a world callback or script path.

Here is the board cue: I am drawing an arrow from “setup assumptions” to “legal results.” This arrow is very important. In a wild battle, “caught” is legal. In a normal trainer battle, “caught opponent’s creature” is not legal. In a Safari battle, “threw bait” and “Safari balls remaining” are meaningful; in a normal trainer battle, they are nonsense. In a link battle, payout or overworld trainer flag updates may be nonsense. In a battle facility, victory may advance a streak rather than clear a map trainer. So if you encode battle type weakly, you either allow impossible results or you scatter defensive checks everywhere.

Pokemon Showdown gives us a useful contrast on this exact point. In Showdown, battle construction is tied to a ruleset and a simulator turn model. A battle is advanced through simulator turns, and regression tests can assert that a particular ruleset produces a particular outcome. The research packet points to pokemon-showdown/test as a high-value area for regression testing, and to pokemon-showdown/data and mods as the data layer. It also gives us pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f, where damage behavior is expressed through scripted callbacks and active move handling. That is not FireRed’s battle script bytecode model, but it highlights a shared idea: battle behavior is not just raw numbers; it is data plus hooks plus a selected rule context.

So when we say “Dex/mods” in Showdown, think “which universe of facts and hooks is this battle using?” When we say “battle type” in FireRed, think “which world-origin assumptions and result semantics is this battle using?” Those are not identical abstractions, but they rhyme architecturally. Showdown’s explicitness helps us ask better questions of FireRed: Where is the rule context selected? Where are hooks attached? Where is the result summarized? Where are tests possible? FireRed’s constraints help us avoid overgeneralizing from server-side TypeScript into a handheld RPG runtime.

Let me give you a bug example that crosses both worlds. In a FireRed-like clone, suppose a wild encounter generated from the grass table starts correctly, but after winning, the game resumes at the wrong script label and awards trainer prize money. That tells us the problem is almost certainly not in the turn engine. The setup object probably carried trainer-style continuation or reward policy into a wild battle. In Showdown terms, that is like constructing a battle under the wrong format or injecting the wrong rule hook. The simulator might execute individual turns perfectly while the battle as a whole is semantically wrong. Correct turns do not save an incorrect setup.

This is one reason regression testing belongs in this week’s conversation. FireRed itself is not organized like a modern unit-tested simulator, but our clone architecture can be. For the lab, I would expect tests at the boundary, not only inside the turn algorithm. A test can say: given a wild encounter table entry and a deterministic random seed, resolver returns a setup with battleType wild, canCatch true, rewardPolicy none, opponent source generated, and result continuation overworld return. Another test can say: given trainer id BrockJunior, resolver returns battleType trainer, canCatch false, rewardPolicy trainerPayout, opponent source trainer table, and continuation trainer script. Then a result test can say: when trainer battle result is victory, overworld logic marks that trainer defeated; when wild battle result is fled, it does not mark any trainer defeated. Those tests are small, but they protect the architecture.

Notice how this connects backward to our earlier weeks. In week 1, we decomposed the RPG into runtime shell, overworld, script VM, creature data, battle, inventory, UI, save, and link systems. At the time, that decomposition may have sounded like a map of topics. Now it is a map of obligations. Encounter setup touches overworld, scripts, creature data, battle, and save flags. Battle results touch overworld return, script continuation, party persistence, and sometimes inventory or money. The callback shell from early in the course, including SetMainCallback2 from pokefirered/src/main.c chunk_6cfd90f6d0136799 in the research packet, is still relevant because battle is not just a function call. It is a mode transition. We use that earlier foundation now without re-teaching it: setup selects the next mode, battle main runs that mode, result cleanup selects the continuation.

And this is also why the week 14 homework matters. You designed minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence. Week 15 asks whether those pieces can actually exchange control and data. A map loader that cannot start a battle is isolated. A battle turn engine that cannot return a result is isolated. A script system that cannot wait for battle completion is isolated. The architecture becomes a game only when these systems agree on contracts.

Let’s do a quick comprehension check. I will describe three fields, and I want you to tell me whether they belong to encounter input, battle setup, battle runtime, or battle result. Species table slot for Route 3 grass. That is encounter input. Whether capture is allowed. That is battle setup, derived from battle type. Current selected move for battler two. That is battle runtime, and we covered the relevant shape in previous lectures. Whether the trainer defeated flag should be set. That is battle result consumed by overworld or script logic, although it depends on setup preserving trainer identity. If you hesitated on that last one, good. That hesitation is exactly where architecture lives. The flag update occurs after battle, but it can only be correct if setup stored enough origin information before battle.

Now I want to emphasize a design smell. If your battle main code contains lots of questions like, “Am I on Route 3?” or “Which map object started me?” you are probably leaking overworld concerns into the battle engine. Some exceptions exist for very cartridge-specific features, but as a design principle, battle main should know battle-relevant facts, not map-authoring details. FireRed’s real code has historical and hardware-driven compromises, and we should respect that. But as architects reading pokefirered/src/battle_setup.c chunk_494d132a59422c85 beside pokefirered/src/battle_main.c chunk_1c5a1226d8d9afb7, we can still identify the intended separation: setup translates world context into battle context; battle main executes; cleanup and result continuation translate back.

Pokemon Showdown’s simulator-turn model makes that separation easier to see because tests can instantiate a battle without an overworld. You can create teams, select a format, run turns, and assert outcomes. That is powerful. But it also means Showdown does not teach us, by itself, how to resume a tile-based RPG map after combat. For that, FireRed remains the main source. Showdown is our secondary comparison for explicit Dex data, modded rules, simulator hooks, and regression discipline. FireRed is our source for how a creature battle is embedded in an adventure game.

So, as we move into the next part, keep one sentence in your notes: battle setup is a contract, not a launch button. It binds encounter origin, rule assumptions, party construction, presentation choices, and result semantics before the battle main callback takes over. If that contract is strong, the rest of the system can be modular. If that contract is weak, you will end up repairing meaning after the fact with special cases. And in a data-driven creature RPG, special cases at the boundary between world and battle are some of the most expensive special cases you can create.

Now, let me put that contract idea beside the Showdown comparison, because this is where a lot of advanced students accidentally flatten two very different architectures into one generic word, “battle.” In FireRed, the battle is not merely a simulation. It is a temporary mode inside a larger adventure runtime. In Showdown, the battle is much closer to the product itself: construct teams, apply a rule set, execute turns, produce a log, verify deterministic behavior. So when I say Showdown is useful to us today, I do not mean, “replace the FireRed setup pipeline with Showdown’s model.” I mean, “notice what Showdown makes explicit that FireRed often encodes through globals, flags, callbacks, and table conventions.”

Board cue here: I am drawing two boxes. On the left, FireRed. On the right, Showdown. Under FireRed I am writing world state, encounter source, battle setup, battle main, result return, overworld continuation. Under Showdown I am writing Dex, format or mod, team objects, battle simulator, event hooks, replay or test assertion. Same noun, battle, but different architectural center of gravity.

In FireRed, our key source for today’s setup boundary is still pokefirered/src/battle_setup.c, chunk_494d132a59422c85. The include list alone is revealing. That chunk reaches across overworld, items, save loading, safari zone, quest log, script, event data, event object movement, field effects, battle transition, battle communication, and battle. That is not accidental messiness. It tells you battle setup is a crossing point. It is where map logic, event logic, player party logic, visual transition logic, and battle engine assumptions get reconciled before control is handed to the battle runtime.

Now compare that to the Showdown-side example in pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f. That excerpt shows code taking a move-like input, normalizing it into an active move, testing immunity, checking OHKO behavior, invoking a damage callback, and applying fixed damage rules. That is a simulator hook pattern. The battle object can ask the Dex and rule scripts, “What does this move mean under this mod?” In FireRed, the comparable question is usually answered earlier and more statically, by tables, constants, compiled data, and battle scripts. Showdown says, “Let the active rule environment interpret this.” FireRed says, “Prepare the correct battle context and let the battle engine run with known constants and state.”

Check for understanding: if I hand you a wild encounter from pokefirered/src/data/wild_encounters.json and a trainer record from pokefirered/src/data/trainers.h, should those become two completely different battle engines? No. They should become two different producers of a shared battle setup contract. The origin differs. The party construction differs. The post-battle routing differs. But once the battle begins, the combat core should not be full of, “Oh, if this came from tall grass do this, and if this came from a trainer table do that,” except where the rules of the battle genuinely require it.

Concrete example. Imagine the player steps into grass on Route 3 and rolls a wild Pidgey. The resolver reads the route’s encounter table, chooses a species and level, creates or references the enemy party representation, sets battle type flags appropriate to a wild battle, chooses the transition, and records enough return context so the overworld knows what to do afterward. Now imagine the player talks to a trainer. The trainer resolver reads the trainer table, obtains trainer class, party type, party members, AI or item assumptions if present in that data model, text pointers or script continuation hooks, and sets trainer battle flags. Different input path, yes. Different setup data, yes. But both should converge on, “Here is the battle to run; here is who participates; here is what kind of battle this is; here is how the result should be interpreted.”

This is why pokefirered/include/battle.h, chunk_f5cd027bf13ab47d, matters in today’s lecture. The BattleStruct and result-related state are not just storage trivia. They are part of the return contract. When the battle engine finishes, the overworld does not want a philosophical essay about every turn. It wants an actionable result: won, lost, fled, caught, blacked out, special facility behavior, script continuation, or some other route-specific meaning. Then pokefirered/src/battle_main.c, chunk_1c5a1226d8d9afb7 and chunk_46361bdbd92dae37, become important because those chunks represent the flow from battle conclusion into cleanup and continuation. You should read those result paths as the other half of setup. Setup asks, “What are we starting?” Result flow asks, “What does that completed battle mean back in the world?”

Let’s pause and connect this to the week. Week 15 is Trainer Data, Wild Encounters, and Battle Results. That is not three random topics. It is a boundary week. Monday is about encounter data into battle setup. The next lecture is about resolving battle outcomes back to the world. The lab asks you to implement a wild encounter resolver and trainer battle resolver that both create the same battle setup object and return a battle result consumed by overworld logic. That lab is not asking for busywork. It is asking you to preserve architectural shape.

And because you are advanced students, I want to make the design constraint sharper. The setup object should not be a dumping ground. A dumping ground says, “Here are thirty fields, maybe some of them are meaningful.” A contract says, “These are the fields required by the battle runtime, these are the fields required by presentation, these are the fields required by result routing, and these are the fields only the producer knows.” If you cannot explain which category a field belongs to, you probably have not finished the design.

Now, how does Showdown help here? Showdown gives us a clean mental model for rule environment separation. In the research packet, the recurring Showdown modules are pokemon-showdown/data, pokemon-showdown/data/mods, pokemon-showdown/server, and pokemon-showdown/test. For today, the important lesson is that Showdown can run different rules by selecting different Dex or mod behavior. The pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, example is a tiny view of that philosophy. The damage calculation is not merely hardwired to one eternal interpretation. The active move and rule context can change behavior. That is powerful for a simulator.

FireRed, however, is not trying to be a general competitive simulator across generations. FireRed is trying to be a cartridge RPG with a fixed adventure, fixed generation rules, fixed memory constraints, and tightly integrated overworld behavior. So FireRed’s battle setup is less about selecting an open-ended rules mod and more about assembling a known battle mode from world data. Trainer battle? Wild battle? Safari? Link? Special scripted battle? Each one is a known branch of the adventure system.

So, board cue: I am writing “explicit rule environment” under Showdown and “embedded adventure context” under FireRed. Do not rank these as better and worse. They optimize for different things. Showdown optimizes for modded simulation, turn logs, tests, and fast iteration over rules. FireRed optimizes for a cohesive single-player adventure with direct transitions from map scripts into combat and back.

This also explains the regression testing difference. In Showdown, because the simulator is the product, regression tests can assert battle logs, turn outcomes, rule interactions, and mod-specific behavior. That is why the research packet points us toward pokemon-showdown/test as a major recurring module. If a rule hook changes, the simulator can prove that a known battle scenario still produces the expected outcome. In FireRed, decompilation and reconstruction give us source visibility, but the original architecture was not built around a modern unit-test suite. For our course project, we borrow Showdown’s discipline without borrowing its entire architecture. Your resolver lab can have tests that say, “Given this wild encounter table input, produce this setup.” “Given this trainer record, produce this setup.” “Given this battle result, route to this overworld continuation.” Those tests are Showdown-like in spirit, but FireRed-like in domain.

Let me give a concrete test-style example in spoken form. Suppose your wild encounter resolver receives area “Viridian Forest,” slot roll 37, level roll 4, and random seed S. The expected setup says battle kind equals wild, opponent party contains one generated monster with species and level determined by the encounter table, player party source equals current save party, escape rules are enabled unless restricted by the map or script, capture is enabled unless a special mode disables it, and return continuation is “resume overworld at same tile after battle cleanup.” Now suppose your trainer resolver receives trainer id 82. The expected setup says battle kind equals trainer, opponent party comes from trainer table, trainer metadata is attached for intro and defeat handling, capture is disabled, fleeing is generally disabled or treated differently, and return continuation is “resume script after trainer battle result.” The point is not the exact labels. The point is that both tests compare setup contracts, not battle internals.

This is where I want you to be careful with the word “data-driven.” A weak version of data-driven design means, “I loaded something from JSON.” That is not enough. pokefirered/src/data/wild_encounters.json is data-driven because the map-to-encounter relationship can be represented as data, but the design becomes meaningful only when the resolver interprets that data into runtime state consistently. pokefirered/src/data/trainers.h is data-driven in a different style: compiled C data, not runtime JSON, but still a table-driven source of battle configuration. The source format is less important than the architectural role. Both are data records that feed behavior.

You have seen this pattern since the start of the course. In week 1 we decomposed the game into runtime shell, overworld, event scripting, creature data, battle, inventory, UI, save, and link systems. In week 2 we followed headers and generated data as cross-module contracts. In weeks 12 through 14 we moved into battle internals: main callback, chosen actions by battler, turn-order arrays, battle scripts, current move data, damage, status, and side timers. Today we are not reopening those internals. We are asking how the adventure runtime arrives at that battle engine with a coherent initial state, and how it expects meaning to come back.

A quick check: why is it dangerous to let the battle engine directly query the current map’s wild encounter table once the fight has started? Think it through. Yes, because that creates an unnecessary dependency from battle internals back into overworld encounter data. If the battle needs the species, level, battle type, escape permissions, or capture permissions, setup should have already supplied them. The engine should not need to ask, “Where did I come from?” every time it needs to decide a combat rule. It should receive the rule-relevant facts.

There are exceptions, of course, and exceptions are where architecture gets interesting. Some battle modes really do need special external meaning. Safari behavior, link behavior, scripted battle constraints, tutorial battles, or facility battles may need additional flags or handlers. But even there, the principle remains: make the specialness explicit in the setup contract. Do not let it leak as dozens of hidden map checks scattered across battle logic.

Let’s use the prior battle lectures for one minute, but only as a bridge. We already covered chosen actions by battler and turn-order arrays. Those are inside the battle engine’s per-turn coordination. The setup phase should not precompute an entire battle. It should not decide turn order, damage, move effects, or status progression. That belongs to the battle runtime and battle scripts. Setup prepares participants, flags, resources, callbacks, and result semantics. If your setup object starts containing “turn one expected damage,” you have crossed the boundary into simulation. If your simulator starts containing “which map script should resume after this trainer loses,” you have crossed the boundary back into overworld. Good architecture keeps those crossings rare and named.

Now I want to bring in a small but important distinction: source of truth versus adapter. FireRed’s trainer table and wild encounter data are sources of truth for encounter creation. The resolver is an adapter. The battle setup structure or globals are the contract. The battle main loop is the consumer. The result storage is the return contract. The overworld script or field logic is the final consumer. In Showdown, the Dex and mods are sources of truth for rule interpretation, teams are input data, the battle simulator is the consumer, and test logs are a verification surface. Once you can name these roles, you stop arguing vaguely about whether something “belongs in battle.” You ask, “Is this source data, adapter logic, contract state, simulation state, or return state?”

Board cue: I am writing those five labels vertically: source data, adapter logic, contract state, simulation state, return state. For your homework this week, I expect you to be able to classify fields and functions this way. If you cite pokefirered/src/battle_setup.c, chunk_494d132a59422c85, and say “this is adapter territory,” that is a defensible claim because the chunk’s includes show it coordinating between field systems and battle systems. If you cite pokefirered/src/battle_main.c, chunk_1c5a1226d8d9afb7, for result flow, you are now talking return state and continuation. If you cite pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, you are talking simulator hook behavior under a particular modded rules context.

Let’s do another check for understanding, and I want you to answer silently before I answer it. A trainer battle and a wild battle both need an opponent party. Are those opponent parties equivalent? Architecturally, no. They are equivalent at the narrow point where battle needs battlers with species, level, moves, stats, status, and items. But they are not equivalent at the provenance level. A wild monster may be generated from an encounter slot and may become capturable. A trainer monster is part of a trainer party definition and normally is not capturable. If your setup erases provenance too early, you may accidentally allow captures in trainer battles or mishandle post-battle trainer flags. If your setup preserves too much provenance inside combat logic, you may make ordinary move resolution depend on whether the opponent came from grass or a trainer. The contract needs to preserve the right meaning at the right layer.

That is the advanced move here: not too little information, not too much information, but information at the proper abstraction level. “Can capture opponent” is a battle rule flag. “This opponent came from wild encounter table row 7” is probably setup provenance or debug information, not something damage calculation should care about. “Trainer id 82 defeated” is return routing information. “Opponent’s current HP is 14” is simulation state. Keep those separate.

And this is where FireRed’s style can feel foreign if you come from modern engine design. FireRed often expresses these boundaries through global variables, bitfields, callback changes, and task or script continuations rather than through a single neat object called BattleSetup. Our lab deliberately asks you to make a setup object because we are learning the architecture, not reproducing every C-era implementation constraint. But the object you design should still reflect FireRed’s actual boundary. It should be informed by pokefirered/src/battle_setup.c, chunk_494d132a59422c85, not by a generic RPG tutorial.

Now, when we connect week 15 to the overall course plan, here is the larger reason this matters. Creature RPGs are not battle simulators with a map bolted on. They are stateful adventure systems in which battles are one major mode of progression. A battle may change money, party HP, experience, flags, script progress, trainer rematch state, inventory, Pokédex data, caught species data, quest log entries, and player position. Some of those are handled during or after battle. Some are prepared before battle. But all of them depend on the setup and result boundary being understandable.

So, if I were reviewing your architecture document, I would ask: where is battle origin represented? Where are rule flags represented? Where are participants constructed? Where is presentation selected? Where is the continuation stored? Where is the result translated back into world effects? And can wild and trainer encounters share the same path after their source-specific resolver runs? If the answer is yes, you are building a system that can grow. If the answer is no, every new battle kind will multiply your branches.

Let’s end this segment with a short synthesis, but not the lecture’s final synthesis yet. FireRed gives us the embedded adventure architecture: trainer data and wild encounter data flow through battle setup into a battle engine, then battle results return to overworld and scripts. Showdown gives us a secondary lens: explicit Dex data, mod-specific hooks, simulator turns, and regression testing as first-class engineering practices. For this course, we keep FireRed as the source of architectural truth because our question is not only “how does a turn resolve?” We already spent recent lectures on that. Today’s question is, “How does a world event become a battle, and how does that battle remain meaningful when it returns to the world?” That is the question your week 15 lab is designed to make concrete, and that is the question we will keep refining as we move from setup into battle outcomes.

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