# Entering the Overworld Runtime

Lecture ID: lecture_w04_d01
Week 4, Day 1

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

## Transcript
All right, let’s come back to the runtime thread we left open at the end of last week. Up to now, we have been treating maps mostly as records: map headers, layout references, object events, warp events, and the distinction between generated static data and runtime state. Today we move one layer closer to the machine while staying one layer above raw hardware. The question is no longer, “What data describes Pallet Town?” The question is, “What runtime takes control once the game decides the player should be standing in Pallet Town?”

That shift matters. In a creature RPG, the map is not just a background. It is the coordination surface for movement, scripts, NPCs, wild encounters, menus, field moves, collision, audio, callbacks, and eventually battle handoff. So when we say “entering the overworld runtime,” we are not just saying “load a map.” We are saying: install the correct main callback, resolve destination state, hydrate the map’s runtime structures from static data, start the field systems, and then return control to a per-frame loop that can keep the world alive.

Let me put a small phrase on the board, because this will anchor the first hour: data names the place; state names the arrival; callback owns the frame.

Pause on that. Data names the place. That is the map group and map number, and all the static records we studied last week. State names the arrival. That is the warp destination: which warp, which coordinates, which facing direction, and what transition behavior. Callback owns the frame. That is where CB2_Overworld comes in, in pokefirered/src/overworld.c, chunk_7ca71901368c46f7. We are not going to re-teach SetMainCallback2 today; you already used that machinery in the week 2 tracing work. But we are going to use it as prior foundation. A callback is not a vague “function pointer somewhere.” In this architecture, it is the active runtime mode. If main callback 2 is the overworld callback, then the game’s frame budget is being spent advancing the overworld.

Now, if you are building a minimal clone, this is one of the first places where you should resist the temptation to start with the player sprite. Many student clones begin with, “I can draw a tilemap and move a character.” That is fine for a graphics prototype, but architecturally it skips the decisive question: what owns the loop? In FireRed, the answer is not “the player.” The answer is the currently installed runtime callback. The player is one subsystem inside that runtime.

So imagine our clone has six systems on the course checklist: map loading, script execution, party data, battle turns, item use, and save persistence. Week 4 is where those systems begin to meet. The map loader has to know where to place the player. The script engine has to run map scripts and object scripts once the map is active. Party data has to be reachable because scripts may check party members, items may target party members, and battle setup may consume party state. Battle turns are not active yet in the overworld, but battle setup is reachable from encounters, trainers, and scripted events. Item use may happen from the field menu, and save persistence has to capture enough overworld state that re-entering later reconstructs the same world. You can see the shape: the overworld runtime is not one feature. It is a hub.

Let’s walk carefully into FireRed’s version of that hub.

We begin with CB2_Overworld. The naming convention is already telling us something. This is a callback in the second main callback slot, the one we have previously associated with major runtime modes. When CB2_Overworld is active, every frame the engine comes back to overworld responsibilities. In a simplified clone, you might call this function Overworld_UpdateFrame. But that name, while clear, hides an important historical detail: FireRed is built around callback slots and task dispatch, not around a single object-oriented Game class with virtual update methods. So in your notes, translate CB2_Overworld like this: “the installed per-frame owner for map play.”

Here is the first check for understanding. If I ask, “Does CB2_Overworld load Pallet Town from disk every frame?” the answer should be immediate: no. The callback owns ongoing runtime behavior, but the heavy map load sequence is a transition process. Loading is not the same thing as living. Once the map is loaded, the callback advances the living world: tasks, scripts, field effects, input handling, object movement, and screen updates. That distinction is a common source of architectural bugs in clones. If your map loader and your per-frame map runtime are the same blob of code, you will eventually reload state accidentally, reset scripts accidentally, or make warps impossible to reason about.

Now, turn to the map data load sequence in pokefirered/src/overworld.c, chunk_f00e575dedd069ed. We are using the chunk as evidence for the claim that FireRed separates the question “what map should be current?” from the work of populating the runtime with that map’s data. Again, I am not going to re-teach map headers or layout registry references; those were week 3. The week 4 move is to ask how those fields become active state. Static map data by itself is inert. A map header does not move the player. A warp event does not teleport anyone by existing. The runtime has to choose a destination, load the associated map metadata, construct collision and event context, place the player avatar, schedule scripts, and then hand control back to the frame loop.

Let me draw a narrow flow on the board. Not code yet, just architectural verbs: choose destination, load header, load layout, load events, resolve entry point, initialize field systems, install callback.

This is not a universal exact list of function calls; it is the mental model we need before we read the control flow. The precise FireRed path matters, and we will inspect it across today and the next lecture, especially LoadMapFromWarp in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817. But if you keep the verbs separate, you will understand why the real code has multiple globals and helper functions instead of one romantic LoadEverything function.

Now let’s bring in warp destination state. The source focus names pokefirered/src/overworld.c, chunk_9b2a7fa129935fc9, for warp destination. Think of warp destination as a little envelope of intent. It says, “The next map entry should be this group, this number, through this warp, or at these coordinates.” It is not the map itself. It is not the loaded layout. It is not the player object. It is transitional state. That is why it is so important.

Here is the concrete example. Suppose the player steps on a door tile in Pallet Town and enters a house. Last week, when we looked at map data, we cared that the source map has a warp event and that Pallet Town’s map data exists in pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf. Today we care about the runtime interpretation. The player collides with or activates a warp event. The game records a destination. That destination may refer to a map group and map number, plus a warp id on the destination map. Then a transition begins. During the transition, the current map is no longer the truth we are moving toward; the warp destination is the truth. After loading, the destination state has been consumed into current map state and player position.

Notice what this buys us. If warp destination state is explicit, then loading a map from a door, loading after a save, loading after a script command, and loading after a special transition can share a conceptual pathway. They do not all have to fake a button press. They can all say, “Here is where the overworld should enter next.” That is clean architecture, even in a C codebase full of globals.

For a minimal clone, I would want something like this in design, not necessarily syntax: a currentMapId, a pendingWarpDestination, a playerSpawn, and a mapRuntimeMode. The pending warp destination is set by field interaction or script execution. The loader consumes it. The runtime callback then advances the loaded map. And if you are thinking ahead to save persistence, yes, some of that state must be serialized, but not all of it. Current map and player coordinates probably belong in the save file. A half-consumed fade transition probably does not, unless your clone deliberately supports saving mid-transition. This is the kind of boundary thinking that prevents save files from becoming dumps of every global variable.

Let’s check understanding another way. If the player’s party contains a poisoned creature, where does that state live? Not in the map. Party data is its own model. But can the overworld runtime care about it? Yes. A field step may trigger poison damage in older games, an item use may target the party, a script may inspect the party, and a battle transition will read the party. So the overworld runtime does not own party data in the deep model sense, but it coordinates moments where party data matters. That distinction, ownership versus access, is one of the advanced-level expectations for this week.

Now, connect this to the broader codebase evidence. In pokefirered/src/battle_setup.c, chunk_494d132a59422c85, the include list itself is instructive: it pulls in overworld.h, item.h, pokemon.h, load_save.h, script.h, event data, field systems, battle.h, battle_transition.h, and more. I am not citing that include list because includes are architecture by themselves; we have already been careful about that. I am citing it because battle setup sits at a boundary where overworld, scripts, party, items, and battle all become relevant. That is exactly why week 4 cannot treat map runtime as a graphics-only subsystem. When the overworld reaches a trainer encounter, a wild encounter, or a scripted battle, it must hand off into battle setup with enough context to build the battle state correctly.

In a clone, you can model that handoff minimally. Your overworld runtime detects an encounter. It calls something like StartBattle with a BattleRequest. The request says whether this is wild or trainer, which opponent data to use, what background or terrain applies, and which callback to resume after battle. The party model supplies creatures. The battle turn engine then runs its own runtime. When battle ends, control returns to the overworld callback or equivalent map mode. The exact FireRed details are richer, but the architectural move is the same: one runtime mode yields to another through explicit transition state.

Now, I want to slow down on Pallet Town as a reference case. Pallet Town is pedagogically useful because it is small enough that we are not drowning in special cases, but it is still a real map. Its data is in pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf. Last week we treated that kind of map JSON as generated-source input, not runtime JSON parsing. That point is already covered, so today we simply use it. The map file gives us a static reference: what events and layout references exist for Pallet Town. The overworld loader does not think, “I am parsing a friendly JSON file at runtime.” It thinks in terms of compiled data structures produced by the build pipeline. The runtime is consuming C data, not doing a web-app-style fetch of JSON.

So if I write on the board, “Pallet Town map.json arrow compiled map data arrow runtime map,” the key part today is the second arrow. We are not redoing the first arrow. The second arrow is where LoadMapFromWarp and the map data load sequence live.

Let me give you a clone pseudocode sketch verbally. Do not copy syntax; copy boundaries. We have a function called requestWarp(destination). It stores pendingWarpDestination. The current overworld update sees that a transition is requested, starts a fade, and temporarily disables player movement. When the fade reaches black, loadMapFromWarp reads pendingWarpDestination. It resolves destination map id. If the destination uses a warp id, it looks up that warp in the destination map’s warp table and derives player coordinates. If the destination provides explicit coordinates, it uses those. Then it loads map metadata, loads layout and collision, instantiates object events, places the player, queues any map scripts, clears pendingWarpDestination, and returns to Overworld_UpdateFrame. That is your minimal clone version of the FireRed pattern.

Now, what is missing from that sketch? Many things. Audio, weather, camera scroll, metatile behavior, NPC movement templates, field effects, special map callbacks, connection maps, and script flags. But for the course objective, the sketch has the correct bones. It separates map data, transient warp destination, load control flow, script scheduling, and per-frame runtime ownership. If your clone has those separations, you can grow it. If it does not, every later system will feel like it has to tunnel through the map loader.

Let’s do a more advanced check. Why do we not just store the destination player x and y directly in the source warp event and avoid destination warp ids? Think it through. A warp id allows the destination map to own its own entry points. If the house interior changes, the destination warp table can define where entrance zero places the player. Multiple source maps or scripts can target the same destination warp. It also makes bidirectional door relationships easier to maintain as data. Direct coordinates are sometimes useful, especially for special scripted placement, but using warp ids gives a data-driven indirection point. That is exactly the kind of indirection we want in a large creature RPG.

This also helps explain why “data-driven” does not mean “no control flow.” FireRed’s map system is data-driven because map identities, layouts, events, and warps live in data tables. But entering the overworld is control-flow heavy. A pending warp is interpreted. A loader sequence runs. A callback is installed. Scripts may be scheduled. Data does not execute itself. The engine executes, using data as input. That sentence should sound familiar from week 2, when we traced data table to callback to visible behavior. Today we are applying it to a specific runtime doorway.

Let’s connect to script execution before we leave this opening segment. When a map is loaded, scripts are not an ornamental add-on. They are part of what makes the map semantically alive. A minimal clone needs at least a tiny script executor: commands like set flag, check flag, show message, move player, give item, start battle, warp, and end. In FireRed, script integration touches overworld and event systems; we saw evidence of cross-boundary use in files like pokefirered/src/learn_move.c, chunk_af3598df6f092723, where script.h, task.h, field_fadetransition.h, and overworld.h appear around a menu-driven state machine. Again, the point is not that the move relearner is today’s topic. The point is that field interactions, menu callbacks, and scripts routinely cooperate through the same architectural substrate. The overworld is where many of those interactions are initiated or resumed.

Item use has the same shape. In pokefirered/src/pokemon_summary_screen.c, chunk_e8e17a42bbf17d46, the surrounding includes show party menu, item constants, battle, region map, field specials, and overworld-related surfaces in the orbit of a UI that inspects creature state. For our purposes, item use in the overworld means the map runtime must be able to pause field movement, open a UI, let the item system mutate inventory or party data, and then return. A potion affects party data. A Repel affects overworld encounter logic. An Escape Rope may request a warp. A field move may run a script-like effect. So even before we implement a full item database in a clone, we should design item effects as commands that can target world state, party state, or transition state.

Now I’ll ask the room: if an Escape Rope requests a warp, should the item system directly load the map? I would argue no, for the minimal architecture we are building. The item effect should request a transition, perhaps by setting a pending destination or invoking a field effect that does so. The overworld transition machinery should load the map. That keeps item use from becoming a second hidden map loader. Any time you create a second hidden map loader, you create a future bug where one path initializes scripts correctly and the other path forgets.

Save persistence follows naturally. A save file for this clone needs party data, inventory data, progression flags, current map identity, player position or entrance state, and enough world variables to resume meaningfully. It does not need to serialize the entire callback pointer model in a naive way. Instead, on load, the game can reconstruct the runtime: set current map from saved location, load map data through a load-from-save path, place player, initialize overworld systems, and install the overworld callback. FireRed has dedicated load-save code, and the battle setup evidence in pokefirered/src/battle_setup.c, chunk_494d132a59422c85, reminds us that load_save.h is one of the cross-system dependencies near gameplay transitions. For our clone design, the lesson is sharper: persistent state should be stable game facts, while callbacks and tasks are usually reconstructed runtime machinery.

Let’s recap this opening portion before we go deeper into LoadMapFromWarp. We are advancing from last week’s static map anatomy into runtime entry. CB2_Overworld in pokefirered/src/overworld.c, chunk_7ca71901368c46f7, is the per-frame owner once the overworld is active. The map data load sequence in pokefirered/src/overworld.c, chunk_f00e575dedd069ed, is the bridge from compiled map records into live field state. Warp destination state in pokefirered/src/overworld.c, chunk_9b2a7fa129935fc9, is transitional intent, not the map itself. LoadMapFromWarp in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, is our key control-flow case for turning that intent into a loaded map. And Pallet Town’s data in pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf, gives us a small reference map so the example stays concrete.

The larger synthesis, for this part of the lecture, is this: the overworld runtime is the architectural meeting point where data, state, and frame ownership become visible behavior. If you are cloning this kind of RPG, do not start by making every feature call every other feature. Start by defining the few handoff objects: map id, warp destination, script request, battle request, item effect, save snapshot. Then give each runtime mode a clear owner. That is how a small prototype remains compatible with a large architecture.

Hold that model. Next we are going to step through the warp path more mechanically, from a source map event to a destination map load, and we will be very precise about map group, map number, warp id, coordinates, and the callback handoff.

All right, so let’s put the warp path on the board as a state transition rather than as a magical door. I want you to resist the beginner’s reading of a warp, which is, “the player steps on a tile and the game changes maps.” That is true only from the player’s point of view. Architecturally, a warp is a staged request. It is a request to replace one overworld configuration with another while preserving enough global player state that the new map can be entered coherently on the next frame sequence.

So, board left side: source map. Board middle: warp destination state. Board right side: loaded destination map plus callback ownership. That is the shape. Source map, warp state, destination map, callback ownership. If your clone has those four ideas, even in a tiny form, you can grow the system later.

Now let’s make that precise. In FireRed, the source map is not just a rendered background. It is a map header, a layout reference, event data, scripts, music, region metadata, and so on. We already covered those fields last week, so I am not going to re-teach the map record. What matters today is that one category of event data can say, in effect, “when the player crosses here, resolve a destination.” The destination is not merely a filename. The destination needs map group, map number, warp id, and sometimes coordinate information that comes indirectly through that warp id.

In the source focus for this week, pokefirered/src/overworld.c chunk_9b2a7fa129935fc9 is the important anchor for warp destination state. When I say “warp destination state,” I mean a small remembered set of values describing where the engine is supposed to go next. Not a whole map. Not every object. Not every script. Just enough identity and placement information to let the map loading path do its job. This is an example of a pattern we have seen repeatedly since week one: FireRed tends to keep large systems loosely coordinated through narrow state records, globals, callbacks, and tasks rather than through one giant object graph.

Let’s do a concrete example. Imagine the player leaves the front door of the Pallet Town house. I am not asking you yet to memorize the exact warp entry. We are using Pallet Town because pokefirered/data/maps/PalletTown/map.json chunk_05643f6bfaa3b9bf gives us a small, readable reference case for talking about a destination map. The source event says, conceptually, “send this player to Pallet Town, and place them at the matching entrance.” In a minimal clone, you could represent that source event as something like: current map equals PlayerHouse, warp id equals front door, destination group equals PalletTownGroup, destination number equals PalletTown, destination warp id equals house entrance. The real project uses its own constants, structs, and compiled data, but the architectural meaning is the same.

Now, check yourself for a moment. If the source warp already names the destination map, why do we still care about a destination warp id? Think about that. Why not store exact x and y coordinates directly in every source warp?

The answer is indirection and maintainability. If five different places can send you to the same door, you do not want five separate coordinate copies unless the design really requires it. You want the destination map to own its entrances. The source says which entrance it targets. The destination map describes where that entrance physically is. That way, if you move the door by one tile while editing the destination map, you update the destination-side event data, not every incoming source.

For a clone, this is one of the first places where advanced design discipline matters. A beginner clone often hard-codes “if entering town, set player x to 9 and y to 12.” That works until the first map edit. A better clone has a map registry, a warp registry per map, and a resolver. The resolver takes map group, map number, warp id, and returns a resolved location. That resolved location can include map id, player x, player y, facing direction, and maybe an entry behavior like “play door animation” or “fade in from black.”

Now look at the middle of the board. I am writing, “set warp request before load.” That is the mechanical heart of the flow. The engine does not want every tile behavior routine to know how to tear down and rebuild the overworld. Instead, the interaction code records the request, then transfers control to a loading routine. That is why pokefirered/src/overworld.c chunk_7ea6afd1253d6817, the LoadMapFromWarp focus chunk, matters so much this week. LoadMapFromWarp is the architectural boundary where “we know where we want to go” becomes “begin constructing the destination overworld state.”

And here is where CB2_Overworld comes back into view. The game’s second main callback, which we discussed when we studied SetMainCallback2 earlier in the course, owns a runtime mode. The specific source focus for today, pokefirered/src/overworld.c chunk_7ca71901368c46f7, anchors CB2_Overworld as the map runtime callback. Once the destination map is loaded and the overworld is active, CB2_Overworld is the per-frame owner for the map experience. So the warp sequence is not just data lookup. It is also a callback handoff. We are moving from whatever transitional state we were in, through loading, into the overworld callback that can run tasks, field input, scripts, movement, and rendering updates in the appropriate rhythm.

Let me slow that down because it is the part people often blur. Loading a map and running a map are not the same responsibility. Loading is construction. Running is frame ownership. If your clone has one function called enterMap that loads data, spawns NPCs, starts music, polls input, runs scripts, detects battles, opens the menu, saves the game, and draws the screen forever, your prototype might appear to work for two maps, but you have hidden all the transitions inside one untestable knot. FireRed’s architecture is older and C-based, but the separation is still visible: set state, load data, install runtime callback, then let the callback and task systems advance the game frame by frame.

Now, board cue: I am going to write a minimal clone flow, but I am going to say it in architecture language rather than syntax. Step one, player movement reports that the player entered a warp trigger. Step two, the field control layer builds or selects a warp destination request. Step three, the engine stores the destination as map group, map number, and warp id. Step four, LoadMapFromWarp reads that request and begins destination map loading. Step five, the map data load sequence fetches the map header, layout, event data, and related configuration. Step six, the destination warp id is resolved into player coordinates. Step seven, object events and map scripts are initialized for the new map. Step eight, the callback is handed back to the overworld runtime, CB2_Overworld, so the map can actually run.

Notice how little battle logic appears in that list. Almost none. Notice how little inventory logic appears in that list. Almost none. That is intentional. The overworld can lead to battle, item use, party menus, saving, and scripts, but during a warp it should not need to know the internal details of all of those systems. It only needs to preserve the correct shared state so those systems can resume or be invoked later.

This connects to the design objective for week four: minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence. The word “minimal” is not an excuse to make the boundaries sloppy. Minimal means each system has the smallest version of its real responsibility. A minimal map loader loads the current map’s identity, layout reference, events, and entry point. A minimal script executor can run a list of commands like show text, set flag, give item, and start battle. A minimal party model stores creatures, stats, moves, health, and status. A minimal battle turn loop chooses actions, orders them, applies move effects, checks fainting, and exits with a result. A minimal item system maps item ids to effects and validates where they can be used. A minimal save system snapshots durable state: current map, position, party, inventory, flags, variables, and progression.

The reason I am putting all of those on the same board as the warp flow is that the warp flow is one of the first serious tests of whether the clone architecture is coherent. If your map loader cannot restore the player after a warp, your save system will also struggle to restore the player after loading a file. If your script executor can change flags during a map entry script, your save system needs to persist those flags. If an NPC script starts a battle, then the battle setup code needs a clean request path similar to the warp request path.

You can see this cross-system style in FireRed’s source organization. For example, pokefirered/src/battle_setup.c chunk_494d132a59422c85 includes many of the neighboring systems that battle setup must coordinate with: overworld, item, sound, Pokémon data, load/save, safari zone, quest log, script, event data, field effects, field control, and battle transition. I am citing that chunk not because we are teaching battle setup in detail today, but because it demonstrates the architectural truth: battle is entered from the field through a handoff, and that handoff lives near the same runtime shell ideas we are studying. The mature engine has many modules, but the handoff shape is still comprehensible.

Let’s come back to map loading. In pokefirered/src/overworld.c chunk_f00e575dedd069ed, our source focus is the map data load sequence. We are interested in the order, because order is architecture. You cannot place the player on a map before you know what map you loaded. You cannot spawn events correctly before the event list and layout context are available. You cannot run map scripts correctly if the script environment is not attached to the current map. The exact FireRed functions matter when we read the source in lab, but the transferable lesson is this: load identity first, then load data dependencies, then initialize dynamic runtime state, then enter the frame callback.

Here is a check for understanding. Suppose your clone loads the destination map layout and draws it before resolving the destination warp id. Is that always wrong?

Not necessarily. Drawing early can be fine if the player is hidden during a fade and if the position is resolved before gameplay resumes. But if object spawning, camera placement, or map scripts depend on player position, then late coordinate resolution can cause incorrect behavior. You might spawn NPCs relative to the wrong camera. You might run an entry script that checks the wrong tile. You might briefly show the player in a default location. So the question is not “is drawing before coordinate resolution morally forbidden?” The question is “which later systems observe the unresolved state?” Advanced architecture is about controlling observation. Bad initialization order is dangerous because systems can see a half-built world.

Now, let’s make that concrete with Pallet Town as our destination. Pallet Town’s map data in pokefirered/data/maps/PalletTown/map.json chunk_05643f6bfaa3b9bf is a small enough reference that you can mentally hold it. In a clone, Pallet Town might have a layout id, a music id, a list of object events, a list of warp events, and map scripts. A warp event on the Pallet Town side might describe a doorway into a house. An incoming warp into Pallet Town might target one of those warp records, and the resolver says, “place the player just outside this doorway, facing down,” or whatever the destination semantics require. The important point is that Pallet Town is not a class with a custom enter function for every door. It is data plus shared runtime behavior. That is the data-driven architecture we want, but now grounded in actual runtime flow instead of just records.

Let me pause and ask you to predict a bug. If a designer deletes a warp event from Pallet Town but another map still points to that warp id, what should your clone do?

There are a few acceptable answers depending on your build philosophy. In a student prototype, I would prefer a loud development-time error: “destination warp id missing for map group and map number.” In a shipped game, you might need a fallback, such as a default spawn point, but that fallback should still log or assert in debug builds. What you should not do is silently place the player at zero, zero unless your engine explicitly defines zero, zero as a safe fallback. Silent coordinate fallbacks are how you get players stuck in walls, scripts firing out of order, and saves that restore into invalid positions.

This is why week four’s lab asks you to trace a warp from source map event to destination map load and then implement pseudocode for resolving map group, map number, warp id, player coordinates, and callback handoff. That is not busywork. It is the spine of the overworld module. If you can trace this path cleanly, then later when we add scripts, trainers, item pickups, wild encounters, and save reloads, you will have a place to attach each feature without confusing ownership.

Now I want to connect this to script execution, because scripts sit right next to map load. When a destination map finishes loading, the map is not merely a static scene. It may run an on-enter script. It may check flags. It may enable or disable objects. It may start a message, a movement sequence, or a battle. In a minimal clone, script execution should not be a giant switch hidden inside the map loader. It should be its own small interpreter or command runner. The map loader can request, “run this map entry script,” but the script system owns command execution.

Here is a tiny example. The player enters Pallet Town after receiving a starter. A map entry script checks a flag, maybe “hasStarter,” and if the flag is false, it does nothing special. If the flag is true, perhaps an NPC becomes available or a rival event changes. The map loader should not know what “hasStarter” means. It should provide the current map to the script layer, and the script layer should read flags from event data. That style matches the FireRed pattern we have been tracing: visible behavior emerges from data records, globals, callbacks, tasks, scripts, and concrete functions, not from every subsystem knowing every story rule.

A similar principle applies to party data. The overworld needs to know enough about the party to answer field questions. Can the player use Surf? Does the party have a conscious creature? Should poison damage tick after steps? Can an item be used on a selected party member? But the map loader should not rebuild creatures. Party data belongs to the player state and save state. The overworld can call into party utilities or trigger menus, but it should not own the creature model. When we get to battle turns, the battle system will consume a battle-ready view of the party, apply damage and status changes, and then return results to the broader game state.

You have already seen hints of these boundaries in the source packets beyond overworld. For instance, pokefirered/src/pokemon_summary_screen.c chunk_e8e17a42bbf17d46 includes Pokémon data, summary screen code, task support, link support, overworld, items, battle main, party menu, and field specials. That inclusion pattern tells us the summary screen is a UI mode that touches party data and can be entered from different contexts, but it is still its own mode. Likewise, pokefirered/src/learn_move.c chunk_af3598df6f092723 explicitly frames the move relearner as a state machine around menu states and callback-style flow. Those are later examples of the same architectural habit: isolate a mode, store the state it needs, and re-enter the proper owner when done.

Let’s do another check. In your clone, after the player uses a potion from the field menu, who should update the creature’s HP: the item menu, the item effect function, the party model, or the overworld?

The clean answer is that the item effect function should request or perform the HP change through the party model’s sanctioned interface. The item menu selects the item and target. The item data tells you what the item does. The party model owns valid HP mutation. The overworld is simply the context from which the menu was opened. If the overworld directly says, “selectedCreature.hp += 20,” then you have made item behavior harder to reuse in battle, harder to validate against max HP, and harder to persist reliably.

Now save persistence. Saving is the long-term version of a warp. That may sound strange, so let me unpack it. A warp says, “construct a new runtime map from a compact destination state.” A save load says, “construct the runtime game from a compact durable state.” Both require discipline about what gets stored and what gets reconstructed. You store current map group, current map number, coordinates, facing, party, inventory, flags, variables, and other durable progression. You usually do not store every transient task, every fade animation step, every temporary message box counter, or every half-finished callback detail unless the game explicitly supports saving in those moments.

That difference between durable state and runtime state is essential. FireRed’s architecture repeatedly separates persistent player progress from transient runtime control. We saw the runtime shell in week one with main callbacks and tasks. Now week four shows the same idea in the overworld: the map can be reconstructed from compact identity and data, then CB2_Overworld owns the per-frame behavior. If your save file is full of rendering internals, you have probably saved too much. If your save file cannot reconstruct the current location and player state, you have saved too little.

Let’s return to the callback handoff one more time before we move into the more mechanical trace. In the earlier weeks, we studied AgbMain startup responsibilities, SetMainCallback2, task allocation, task ids, and RunTasks as per-frame dispatch. Today we are using that foundation rather than re-teaching it. The important connection is that CB2_Overworld is not just another function name. It is the installed owner of the game’s second main callback while the player is in the field. That is why pokefirered/src/overworld.c chunk_7ca71901368c46f7 matters. It is where the abstract callback machinery becomes a concrete mode: the overworld.

And this is also why I keep emphasizing handoffs. A well-designed creature RPG is not a single loop with many if statements. It is a set of runtime modes that hand control to one another. Overworld hands to menu. Menu may hand back to overworld. Overworld may hand to battle setup. Battle setup hands to battle. Battle returns a result. The result may resume a script. The script may set flags and return to overworld. Save and load reconstruct the appropriate mode from durable state. That is the course plan in miniature. Weeks three through five are the overworld map runtime because it is the busiest junction in that network.

Let me give you a final concrete trace for this segment. The player steps onto a warp tile. The field movement logic detects that the tile or event under the player is a warp trigger. The source event provides a destination map group, destination map number, and destination warp id. The engine stores that destination in warp state, the concept anchored for us by pokefirered/src/overworld.c chunk_9b2a7fa129935fc9. The game begins a transition, often visually hidden by fade or movement animation. LoadMapFromWarp, our focus in pokefirered/src/overworld.c chunk_7ea6afd1253d6817, consumes the stored warp request. The map data load sequence, represented in our packet by pokefirered/src/overworld.c chunk_f00e575dedd069ed, loads the destination map’s data and initializes runtime structures. If the destination is Pallet Town, the data record we can reason about is pokefirered/data/maps/PalletTown/map.json chunk_05643f6bfaa3b9bf. The destination warp id resolves to coordinates and entry behavior. Then the overworld runtime callback, CB2_Overworld from pokefirered/src/overworld.c chunk_7ca71901368c46f7, resumes ownership so normal map play can continue.

That is the whole shape. Source event, compact destination, loader, resolved placement, runtime callback. Say that to yourself once: source event, compact destination, loader, resolved placement, runtime callback.

Now, for the next part, I want you to keep one question active: where exactly should each piece of information live? If the map id lives in the source event, that makes sense. If destination coordinates live only in the source event, that may be duplication. If player party lives in the map, that is wrong. If item effects live in the overworld, that is also wrong. If save persistence stores only the map name but not the warp or coordinates, it may be under-specified. We are going to use the warp trace as a diagnostic tool for all of those design choices, because a warp is small enough to understand and large enough to expose the architecture.

So let’s put that question on the board. I’m going to draw six boxes, left to right. Map runtime. Script runtime. Party model. Battle engine. Item system. Save system. And now I’m going to draw one arrow that starts in the map runtime and eventually comes back to the map runtime. That arrow is the player doing something ordinary, like walking out of the house in Pallet Town, taking a step, hitting a script trigger, opening the party menu, using a potion, maybe later getting into a battle, and then saving.

The architectural lesson is that the overworld is not “the game.” The overworld is the active stage on which many systems get invoked. That distinction matters. In FireRed, the source focus for today gives us that stage very clearly: pokefirered/src/overworld.c, chunk_7ca71901368c46f7, where CB2_Overworld is the map runtime callback. That does not mean every game mechanic belongs in CB2_Overworld. It means CB2_Overworld is the per-frame environment that keeps the field experience alive. It coordinates the map, tasks, input, movement, scripts, transitions, and callbacks back into other systems.

Now, if you are designing a minimal clone, I want you to resist the temptation to make a giant Overworld class that owns everything. That is the beginner mistake, and by week 4 you should be able to spot it. A minimal clone needs a map loader, yes. It needs script execution, yes. It needs party data, battle turns, item use, and save persistence. But those are not all one object. They are cooperating state machines with different ownership boundaries.

Let’s use the warp example to test that. Suppose the player leaves the starting house in Pallet Town. The source map contains a warp event. That event says, in compact form, “when the player steps here, go to this map group, this map number, this warp id,” or some equivalent destination reference. Then the loader resolves that reference. In the FireRed material, that resolution is anchored by the warp destination state in pokefirered/src/overworld.c, chunk_9b2a7fa129935fc9, and the control flow through LoadMapFromWarp in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817. The destination map data itself is not invented dynamically. It is selected from the compiled data tables, and our Pallet Town reference case is pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf.

So here is the clone version. Not the full engine version. The clone version. You need a MapId, maybe group and number if you are mirroring FireRed, or just a string if your clone is smaller. You need a WarpId. You need a destination resolver that takes those identifiers and returns a concrete loaded map plus a spawn position. You need a player placement step. And then you need to hand control back to the overworld runtime callback.

Notice what is not in that list. Party HP is not in the warp. Inventory is not in the warp. Battle move priority is not in the warp. Whether the player has Oak’s Parcel is not in the map layout. Some of those things might be consulted by scripts on the destination map, but they should not be owned by the map load operation itself.

Let me make that concrete. Imagine a minimal data record like this. I’m not asking you to copy this as syntax; I’m asking you to hear the separation. Source map has a warp event at x equals 7, y equals 12. The event says destination map equals PalletTown, destination warp equals 2. The PalletTown map has warp 2 with x equals 5, y equals 8, facing down. The player object is moved there. Then the runtime switches to overworld update.

That sounds almost trivial, but it is exactly the kind of triviality that protects the rest of your engine. If the source event stored the destination coordinates directly, then every source map would need to know the exact internal layout of its destination. If you later move the door tile on the destination map, you now have stale coordinates scattered across multiple source maps. FireRed’s structure pushes us toward a better model: compact destination state, followed by loader resolution, followed by placement.

Check for understanding here. If a warp from Route 1 leads to Pallet Town, and Pallet Town’s entrance coordinate changes during development, where should you prefer to update that coordinate? In the destination’s warp definition, not in every source event that points to it. Exactly. That is the maintainability argument.

Now let’s connect that to script execution, because this is where clone projects often get muddy. A script is not the same thing as a warp, although a warp can trigger script-like behavior. A script is a sequence of commands interpreted by a script engine. It can check flags, set variables, display text, give items, start battles, move NPCs, or call into special functions. In FireRed, you can see the dependency pattern in other modules too. For example, pokefirered/src/battle_setup.c, chunk_494d132a59422c85, includes overworld, item, pokemon, script, event data, event object movement, field systems, and battle transition headers. That inclusion pattern is evidence of a coordination boundary. Battle setup sits near the crossing point between field events and battle runtime. It is not just “battle math.” It knows how a field encounter becomes a battle.

For your minimal clone, script execution should be its own runtime component. The overworld may ask, “Is there a script to run?” and then it may suspend free movement while that script is active. But the script engine should own script instruction pointer, current command, blocking state, and script-local waits. If a text box is open, the script is waiting. If an NPC movement route is active, the script may be waiting. If a battle is launched, the script may yield to battle and resume after battle result.

I’m going to write a small lifecycle on the board: input, movement, trigger detection, script start, script step, possible callback change, resume. That is your loop. The overworld runtime sees player input. Movement resolves. Tile triggers and object interactions are checked. If a script begins, normal movement is blocked. The script steps each frame until it either finishes or hands control to another callback.

Now compare that to CB2_Overworld. Again, our anchor is pokefirered/src/overworld.c, chunk_7ca71901368c46f7. The point is not that your clone must have that exact function name. The point is that a runtime callback gives you a stable per-frame place for field behavior. We already covered SetMainCallback2 earlier in the course, so I will not re-teach callback slots. I just want you to use that prior idea now. Entering the overworld means installing a particular per-frame world. Leaving the overworld means installing another per-frame world, such as a menu, battle, naming screen, or transition state.

Party data is the next boundary. Let’s say a script says, “Give the player a starter.” That script should not directly mutate some random sprite object on the map and call that the player’s team. It should call a party API. In a minimal clone, party data is a list of creatures, each with species, level, current HP, moves, status, experience, and maybe held item. Advanced learners should immediately ask: which fields are persistent, which fields are derived, and which fields are temporary? Species and level persist. Current HP persists. Battle-only volatile state, like flinch this turn, does not persist as part of the overworld party model. Derived stats may be recalculated from base stats, level, nature-like modifiers if your clone has them, and individual values if your clone has them.

FireRed’s broader source packet reinforces this module separation. The research notes describe the course thesis as FireRed being a stack of cooperating state machines plus data tables: callback and task shell, overworld map runtime, script and progression VM, creature and party model, battle engine, inventory and UI, and save persistence. That is not decorative language. It is the architecture we are tracing. When pokefirered/src/pokemon_summary_screen.c, chunk_e8e17a42bbf17d46, includes pokemon, task, link, overworld, item, battle_main, party_menu, and related constants, it shows that party-facing UI is another crossing point. The summary screen reads and presents party data, may be reachable from the overworld, and may interact with item or move systems, but it is not the owner of the whole game.

Let’s do a concrete example. The player opens the menu and uses a Potion on Bulbasaur. Where does that behavior live? The menu owns cursor movement and selection. The item system owns “Potion restores 20 HP.” The party model owns Bulbasaur’s current HP. The script engine might not be involved at all if this is ordinary menu use. The overworld callback may be suspended while the menu callback runs. Save persistence later records the updated current HP and consumed item quantity.

If, instead, you put the Potion effect in the overworld map object, you have made item use impossible outside the overworld. What happens in battle? What happens in a party menu launched from another context? What happens if a script gives the player a healing prompt? You would duplicate logic. So for a clone, define item use as a command that takes a target and a context. Some items are field-only. Some are battle-only. Some can work in both. The item effect function should be shared, but context rules should gate where it can be invoked.

Quick check: if Rare Candy increases a creature’s level, should that operation belong primarily to the bag menu or to the creature growth system? It belongs to the creature growth system, with the bag menu calling it after validating that the item was selected and the target is legal. Good. The UI initiates; the model changes through an appropriate domain operation.

Battle turns follow the same discipline. The overworld can trigger a battle, but it should not resolve move order or damage. In FireRed, the field-to-battle crossing is visible in pokefirered/src/battle_setup.c, chunk_494d132a59422c85, which is loaded with dependencies around battle transition, overworld, script, event data, and pokemon. That file’s role, as suggested by the source packet, is not “all battle logic.” It is setup: taking conditions from the field and arranging entry into battle. Later, when we compare battle modeling with Pokemon Showdown, we will use Showdown as a secondary reference for deterministic battle data and testable move behavior. But FireRed remains the authority for this course’s architecture.

For a minimal battle turn, the design can be very small. Each side chooses an action. The engine orders actions by priority and speed. It applies move effects. It updates HP and status. It checks fainting. It emits messages. Then it either asks for the next turn or exits with a battle result. That battle result is what the overworld or script layer cares about. Did the player win? Lose? Catch the creature? Flee? The overworld does not need to know how damage was calculated on turn three. It needs the outcome and any persistent mutations to party and inventory.

Now, save persistence. This is where the warp diagnostic becomes especially useful. What must be saved so the player can turn the game off and return to the correct place? At minimum: current map identity, player coordinates, facing direction, party state, inventory state, event flags and variables, and enough callback or mode information to resume safely in the overworld. In a polished engine, you usually do not save arbitrary execution inside a battle or halfway through every possible script unless the game is designed for that. You save at controlled boundaries. FireRed has its own save machinery, and we are not deep-diving it today, but the architectural idea is stable: persistent state is not the same as all runtime state.

Let me say that again because it is a common source of bugs. Persistent state is the subset of runtime state required to reconstruct the intended game situation. It is not a memory dump of every task, callback, temporary animation counter, and pointer. If you save the animation frame of a door opening but not the event flag that says the door is unlocked, you saved the wrong thing. If you save the map name but not the player coordinates, the player returns to some default spawn and your game feels broken. If you save coordinates but not the map group or map number, you cannot interpret those coordinates.

Now back to the map data load sequence. The source packet points us to pokefirered/src/overworld.c, chunk_f00e575dedd069ed, for map data load. Here is the conceptual sequence I want you to keep: choose map header, load layout, load tiles and collision behavior, instantiate events, place player, run map-entry scripts or callbacks, then begin normal overworld update. Some of those pieces are data; some are runtime allocations; some are scripts. And this is where last week’s map record lecture pays off. We already studied map header fields, layout references, object events, warp events, and dynamic warps. Today we are no longer cataloging them. Today we are asking how they become the active world.

Take Pallet Town as the reference case. In pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf, the map record is data. It is not a running town. It does not update NPCs by itself. It does not know the player’s current party. It describes the map’s static and event structure. The overworld loader turns that record into active runtime state. That is the difference between design-time representation and runtime behavior.

Let’s imagine a student clone with a file called maps/pallet_town.json. It contains dimensions, tileset id, collision layer, warps, signs, NPCs, and scripts. The loader reads it, builds a MapRuntime object, creates NPC runtime instances from NPC records, resolves the destination warp into player placement, and starts the map callback. That is fine. Even though FireRed does not literally parse JSON at runtime in the same way your clone might, the architectural separation still applies. We already covered generated data versus runtime JSON in week 2, so just carry the distinction forward: data representation is not the same as runtime ownership.

Now I want to pressure-test the design. Suppose the player enters Pallet Town after receiving the Pokédex. An NPC has different dialogue. Where does that condition live? The map script can check an event flag. The event flag lives in progression state, which is persistent. The dialogue text may be referenced by the script. The NPC runtime instance is just the thing being interacted with. If you store “NPC dialogue version equals two” inside the NPC object itself and forget to save it, the game resets. If you store it as a global event flag, the script can consistently decide what to say.

Suppose the player enters Pallet Town while poisoned. Does map loading cure poison? No, unless a script or special rule says it does. Party state persists independently of map load. Suppose the player uses an Antidote. Does the map need to know that? Usually no. The item system validates the item, the party system clears status, the inventory decrements the count, and the UI reports success. The map runtime simply resumes afterward.

Suppose the player steps into grass and triggers a wild battle. The map runtime detects encounter conditions. Battle setup prepares the transition. Battle runtime owns the fight. After battle, the result returns to the field, and the overworld resumes with updated party and inventory. That is why I keep using the phrase “handoff.” Good architecture is often about clean handoffs, not about one master object knowing everything.

Let’s do another check for understanding. I’ll describe three facts, and you tell me which subsystem should own each one. First: “This door tile leads to destination warp 3 on PalletTown.” That is map event data. Second: “The player has two Potions.” That is inventory persistent state. Third: “The currently selected battle move has priority plus one.” That is battle action data, derived from move data during battle resolution. Good. Now if your answer to all three was “the player object,” your clone will become painful very quickly.

A useful way to design the minimal clone is to define explicit records crossing subsystem boundaries. A WarpRequest crosses from field trigger detection into map loading. A ScriptCommand crosses from script data into script interpreter behavior. A BattleStartRequest crosses from overworld or script into battle setup. An ItemUseRequest crosses from menu into item logic. A SaveSnapshot crosses from runtime state into persistence. These records should be small. They should carry identifiers and necessary context, not entire subsystems.

For example, BattleStartRequest might include battle type, enemy species or trainer id, terrain or background id, and return callback. It should not include every animation frame or the whole map runtime. ItemUseRequest might include item id, target party index, and context. SaveSnapshot might include map id, coordinates, facing, party list, bag quantities, flags, variables, and maybe play time. These are clone-level designs, but they are inspired directly by the source architecture we are studying.

Now, one caution. “Minimal” does not mean “unstructured.” Minimal means you intentionally choose fewer features while preserving correct boundaries. A minimal clone can have only one town, two creatures, four items, one battle type, and a tiny save file. But if it makes the map loader own party mutation, or makes battle code parse map files, it is not architecturally minimal. It is tangled. Small tangled systems become large tangled systems.

Let me bring us back to the week’s lab. You are going to trace a warp from source map event to destination map load, then implement pseudocode for resolving map group, map number, warp ID, player coordinates, and callback handoff. The key phrase is “callback handoff.” The warp does not merely change numbers. It changes the active runtime scene. LoadMapFromWarp, in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, is important because it sits at that exact moment where compact destination state becomes an active map. CB2_Overworld, in chunk_7ca71901368c46f7, is important because that active map then needs a per-frame home.

So your pseudocode should not stop at “currentMap equals destinationMap.” That is under-designed. It should say, resolve destination. Load map data. Build runtime map state. Place player. Initialize field objects. Run entry hooks if needed. Set main callback or return to overworld callback. Resume frame loop. If you cannot say where those steps occur, you have not really designed map loading yet.

And with that, we have widened the warp trace into a full architecture diagnostic. We started with a door. We ended with ownership boundaries across map loading, scripting, party data, battle turns, item use, and saving. That is exactly the point of week 4. The overworld is where the player feels the game as a continuous world, but internally it is a dispatcher, a coordinator, and a runtime host. In the next stretch, we will slow down inside the load sequence itself and ask what has to be initialized before the first visible frame of the new map is allowed to run.

So let’s do that. Let’s slow the motion down until it almost feels too slow, because that is where architecture becomes visible.

Imagine the player has just stepped onto a warp tile. The visible fiction says, “I entered a door.” The runtime reality is more careful. The game has to decide whether that tile really means a warp, identify the destination, preserve enough state to know where the player came from, move from one map’s loaded resources to another map’s loaded resources, and then re-enter the normal overworld frame loop without leaving half-initialized state behind. The important word there is “re-enter.” A map load is not the whole game starting over. It is a controlled interruption of the overworld runtime.

Put that sentence on the board, please: map loading is a controlled interruption, not a reboot.

That one distinction will save you from a lot of bad clone architecture. Beginners often build map transitions by clearing everything and reconstructing the world from scratch, and for a tiny prototype that may look fine for one route and one town. But as soon as you have following systems, script flags, party state, inventory state, pending callbacks, field effects, audio, and save location, “clear everything” becomes dangerous. FireRed’s architecture is old and hardware-constrained, but it is disciplined about separating persistent player state from transient map runtime state. That discipline is exactly what we want to steal for a minimal clone.

Now, I am going to draw a little stack on the board. At the top, write “CB2_Overworld.” Under it, write “per-frame overworld host.” Under that, “tasks, scripts, objects, input, camera.” And off to the side, write “map load path.” CB2_Overworld is the callback that owns the active overworld runtime. In our source packet, that is the focus of pokefirered/src/overworld.c, chunk_7ca71901368c46f7. Do not treat that as merely a function name. Treat it as an architectural address. It tells you where the game is once normal field play has resumed.

So when we say, “What must be initialized before the first visible frame?” we are really asking, “What assumptions does CB2_Overworld get to make?” It should not have to wonder whether the current map header exists. It should not have to wonder whether the player avatar has coordinates. It should not have to wonder whether event objects have been created from the map’s object definitions. It should not have to parse the destination warp every frame. Those are load-time responsibilities. The frame callback should run the world, not discover the world from nothing every frame.

Check your understanding here. If I gave you a clone project and you found code inside the main overworld update that says, every frame, “if currentMap is null, load town one,” what would you say? Right, you would say the callback is compensating for a missing transition boundary. It is not fatal in a toy program, but it is a smell. The callback should have a valid current map by the time it is installed or resumed.

Now let’s connect that to the warp path. The source focus gives us two critical pieces: warp destination state in pokefirered/src/overworld.c, chunk_9b2a7fa129935fc9, and LoadMapFromWarp in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817. Those names already imply the design separation. One part records or resolves the destination. Another part performs the load from that resolved destination. That is a much better model than passing around loose integers through five unrelated systems.

For a minimal clone, I would make this explicit. I would have a WarpDestination structure with a map group, map number, warp id, and optional direct x and y coordinates. If the warp id is valid, the loader finds the corresponding destination warp point in the target map. If direct coordinates are present, the loader places the player there directly. And I would also store an incoming direction or spawn behavior, because entering from a door and appearing after a scripted teleport may not animate the same way. Notice what we are doing: we are not inventing a huge engine. We are just refusing to let “go to map” mean a bag of unrelated global edits.

Now, point your attention to the Pallet Town map data. The packet identifies pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf, as our small reference case. We are not going to re-teach the whole map record structure; you already did that in week 3, and your homework and lab work made you identify the map header fields, layout references, object events, warp events, and dynamic warps. Today the question is different. Today we ask how that static record becomes the currently running world.

The answer is a staged load. On the board, I am going to write the clone version as a sequence. Read destination. Select map record. Load layout. Load collision and metatile behavior. Instantiate map events. Place player. Configure camera. Start scripts or callbacks. Resume overworld loop.

That sequence is simple enough to implement in a student engine, but each line is doing real work. “Select map record” means choosing the right data table entry. In FireRed terms, that is tied to map group and map number, and our source packet points us toward the map data load sequence in pokefirered/src/overworld.c, chunk_f00e575dedd069ed. “Load layout” means we now know dimensions and tile layers. “Load collision and metatile behavior” means movement code can answer whether the player may step onto a tile. “Instantiate map events” means object events, warps, background events, and coordinate triggers are now represented in runtime form. “Place player” means the avatar has a concrete position in the new map’s coordinate space. “Configure camera” means the first frame will show the correct part of the map. “Start scripts or callbacks” means the map may run entry behavior before control is fully returned to the player.

Here is the concrete example. Suppose our minimal clone has two maps: PalletTown and PlayerHouse1F. PalletTown contains a warp event at the house door. That warp event says, in effect, “target map: PlayerHouse1F; target warp id: 0.” The player steps on the door tile. The field control code detects the warp event. It writes WarpDestination { group: townGroup, map: playerHouse1F, warpId: 0 }. Then it starts a transition. During the transition, LoadMapFromWarp consumes that destination. It looks up PlayerHouse1F. It finds warp id 0 in that map’s warp list. It reads that warp’s coordinates. It sets currentMap to PlayerHouse1F. It creates the player at those coordinates. Only after that does the overworld callback resume.

Now, what is the wrong implementation? The wrong implementation is: when the player steps on the door, directly set player.x equals 5 and player.y equals 8 and set currentMapName equals “house.” That will work exactly once, for exactly one door, and then collapse when two doors lead to the same map, or when the same destination map is entered from different sides, or when scripts need to know the previous map. Warp id exists to avoid hard-coding destination coordinates at the source. The source warp should know where it leads. The destination map should know where arrivals appear.

Pause there. Does everyone see why the destination owns the arrival coordinates? If I remodel the inside of the house, I should update the house’s warp event coordinates, not every outside door that points to it. That is data ownership. It is the same principle we used when we distinguished layout registry references from the map header last week, but now we are applying it dynamically.

Let me push this one step further, because advanced learners should care about failure modes. What happens if the destination warp id is invalid? In a professional toolchain, that should be caught before runtime. In a teaching clone, I would still guard it. If target warp id is missing, log a map-load error and fall back to a default spawn for that map, or refuse the transition in debug builds. What I would not do is silently place the player at zero, zero unless zero, zero is explicitly a valid default. Silent fallback creates haunted bugs in map systems. You walk into a door and appear in the corner of a void because nobody checked the data contract.

Now let’s connect scripts. The overworld loader cannot be isolated from script execution, because map entry can trigger scripts. You might enter a town and immediately run an introductory event. You might step through a door and the map simply gives control back. You might arrive after a warp that sets a temporary flag or clears a field effect. FireRed’s script system is connected across modules; our packet shows many modules include script-related headers, for example pokefirered/src/learn_move.c, chunk_af3598df6f092723, includes script and task machinery around a menu state machine, and pokefirered/src/battle_setup.c, chunk_494d132a59422c85, includes overworld, script, event data, field effects, and battle transition headers in the same neighborhood. That is not accidental. Gameplay features cross the boundary between world state, scripts, menus, and battles.

For a minimal clone, do not build a full script VM on day one. Build the smallest useful script runner. A script can be a list of commands: showText, setFlag, giveItem, startBattle, warp, wait, releasePlayer. The overworld runtime should be able to say, “A script is active, so normal input is paused.” Each frame, CB2_Overworld’s clone calls scriptRunner.update before or alongside object updates. If the script is waiting on text, it does not advance. If the script has completed, it releases control.

Here is the key architectural point. Script execution should not replace the overworld runtime. It should be hosted by it, unless the script explicitly transitions to another major mode, like battle or a full-screen menu. That is exactly why our earlier work on callbacks and tasks matters. We are not re-teaching SetMainCallback2 today, but you should feel it under the floorboards. The game moves between modes by installing callbacks, and within a mode it uses smaller schedulers and state machines. The overworld is one such mode.

Now I want to bring in party data, because this is where students often design too locally. A map script might say, “give the player a starter.” A field item might say, “use Potion on party slot 2.” A trainer encounter might say, “start battle using this opponent party.” None of those systems should store the canonical party inside the map. The party is persistent player state. The map may trigger mutations to it, but it does not own it.

In a minimal clone, your Party object is probably just an array of Creature instances, maximum six, with species, level, current HP, moves, status, held item if you support one, and experience if needed. The map runtime can inspect the party for field rules. The script runner can add a creature. The battle system can borrow references or make a battle view of party members. The save system persists it. But the map loader does not recreate it on every warp. That is the line I want you to be able to defend.

Ask yourself: when I enter Pallet Town from Route 1, should my party be reloaded from the Pallet Town map file? Obviously no. That sounds silly when said aloud, but clone projects accidentally do this when they put “initial player state” inside a scene file and reload the scene on every transition. The scene file should contain scene data. The save file should contain player state.

Now battle turns. We will devote much more time to battle architecture later, and Pokemon Showdown will become useful as a comparison point for deterministic battle modeling. The packet includes Pokemon Showdown battle script material such as pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, where damage calculation logic branches through move properties, immunities, fixed-damage behavior, and callbacks. For today, though, I only want the overworld connection. A battle is a mode transition initiated from the overworld. The battle system may be triggered by a script, a trainer sightline, a random encounter, or an item-like interaction. FireRed’s battle setup source, pokefirered/src/battle_setup.c, chunk_494d132a59422c85, is useful evidence here because it sits at the boundary: it includes overworld-related, script-related, field-related, and battle-transition-related headers. That is a boundary module, not just battle math.

In your minimal clone, the overworld should not compute battle damage. It should produce a BattleRequest. Something like: playerParty, opponentParty or wildSpecies, battleType, terrain, returnCallback, victoryScript, defeatScript. Then the battle mode owns turn order, move selection, damage, fainting, experience, and result. When battle ends, it returns a BattleResult to the overworld or script runner. The overworld then decides what happens next: resume control, continue a trainer script, warp the player to a healing location, or open dialogue.

Let’s do a quick check. If your map object has a method called calculateDamage, what is wrong? Yes, wrong owner. If your battle engine directly loads Pallet Town after defeat, what is wrong? Also wrong owner, or at least a suspicious shortcut. The battle can report defeat. A higher-level game flow or script can decide where defeat sends the player. Ownership goes both ways.

Item use is similar. An item can be used in battle, in the field, or from a menu, and the same item id may have different legality depending on context. FireRed’s codebase reflects this broad crossing of menu, party, item, and overworld systems. For example, pokefirered/src/pokemon_summary_screen.c, chunk_e8e17a42bbf17d46, pulls together pokemon, item constants, party menu, battle main, region map, and field specials. That kind of include cluster is not something to imitate blindly, but it tells us the gameplay surface is interconnected. A summary screen is not only display; it is a user interface over persistent creature data and context-sensitive commands.

In the clone, give items a small effect table. Potion: target party creature, field allowed yes, battle allowed yes, effect heal 20. Escape Rope: field allowed in caves, battle allowed no, effect warp to escape destination. Poké Ball equivalent: battle allowed against wild encounter, field allowed no, effect attempt capture. The item system should validate context before applying effect. The script runner may call giveItem. The menu may call useItem. The battle may call useBattleItem. But the item effect should not be duplicated in three places.

Now saving. Saving is where all of these ownership boundaries become testable. If your save file only stores the current map name and player x and y, then you have not saved the game. You have saved a camera bookmark. A creature RPG save needs persistent player state: current location, facing direction, party, inventory, flags, variables, defeated trainers, story progress, maybe money, options, play time, and enough warp or respawn information to restore safely. It does not need to save transient per-frame animation counters or every temporary callback unless you are implementing suspend states, which FireRed is not treating as a general emulator save state in the design sense we care about.

So in a minimal clone, I want you to separate SaveData from RuntimeState. SaveData persists. RuntimeState is reconstructed. SaveData says current map group, current map number, player coordinates, player facing, party array, bag contents, flags, variables. RuntimeState says loaded map pointer, active script pointer, object instances, camera pixel offset, input lock counter, fade state. On load, you read SaveData and then run a map load sequence similar to LoadMapFromWarp, except the destination is “saved position” rather than “warp target.” This is a powerful simplification: all roads into the overworld become variants of “resolve destination, load map, place player, resume callback.”

That phrase belongs on the board too: all roads into the overworld resolve a destination, load a map, place the player, and resume the callback.

Now let’s tie it back to this week’s lab. Your lab asks you to trace a warp from source map event to destination map load, then implement pseudocode for resolving map group, map number, warp ID, player coordinates, and callback handoff. That is not busywork. It is a compression of the entire architecture. If you can implement that pseudocode cleanly, you are already practicing the same boundaries you need for scripts, battles, items, and saves.

I would expect advanced pseudocode to look something like this when spoken, not necessarily written in this exact syntax. On warp trigger, create a pending destination from the source warp. Begin transition and lock player input. When transition reaches the load point, call loadMapFromWarp with the pending destination. Inside that function, fetch the target map by group and number. Resolve the destination warp id into target coordinates. Set current map identity. Load map data and layout. Instantiate runtime events. Set player coordinates and facing. Run map entry setup. Install or resume overworld callback. Clear pending warp.

Notice that this is not many lines. The sophistication is not in making the pseudocode long. The sophistication is in making each line have a clear owner. Fetch target map is data registry work. Resolve warp is map event work. Instantiate runtime events is overworld runtime work. Set party HP is not in this function. Calculate battle damage is not in this function. Serialize save file is not in this function. Clean architecture is often the art of not doing tempting things in the current function.

Let’s pause for a second and test the chain verbally. I will ask, and you answer in your head. Where does Pallet Town’s static data live? In the map data, with Pallet Town represented in pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf. Where is the overworld frame host we are focusing on? CB2_Overworld in pokefirered/src/overworld.c, chunk_7ca71901368c46f7. Where is the warp destination idea grounded in today’s source focus? pokefirered/src/overworld.c, chunk_9b2a7fa129935fc9. Where is the named load-from-warp control path? pokefirered/src/overworld.c, chunk_7ea6afd1253d6817. Where is the broader map data load sequence? pokefirered/src/overworld.c, chunk_f00e575dedd069ed. If you can point to those locations conceptually, then your mental model has handles.

Now, one subtlety before we wrap this segment: the first visible frame is a contract. The player does not see the loader, but the loader’s quality is visible in what does not go wrong. No one praises a door transition because event objects were instantiated before collision checks. But they notice immediately if the player appears under an NPC, if the camera snaps after one frame, if a script starts before the fade completes, if input leaks during the transition, or if the wrong music plays for half a second. Runtime architecture expresses itself as polish.

So when we say “entering the overworld runtime,” we do not mean merely calling a function named CB2_Overworld. We mean reaching a state where all of the callback’s assumptions are valid. The current map is known. The destination has been consumed. The player has a legal position. Map events exist as runtime objects. Scripts know whether they should run. Persistent state remains persistent. Temporary load state has been cleared. The next frame can be ordinary.

And that gives us the synthesis for this part of the lecture. Week 4 is the hinge between the static world we studied in week 3 and the living runtime we will keep expanding through week 5. Map records, layout references, and object events are not the end of the story; they are the ingredients. The overworld callback is not the whole story either; it is the host that expects those ingredients to have been prepared. Warp resolution is the narrow corridor where data, state, and control flow all have to agree. Once you understand that corridor, you can design the clone versions of map loading, script execution, party ownership, battle entry and return, item effects, and save restoration with the same pattern: persistent state stays owned, transient runtime state is rebuilt, mode transitions are explicit, and callbacks resume only after their assumptions are true.

In the next pass, we are going to take that sequence and press harder on the exact boundary between destination resolution and map data loading, because those two are easy to blur. Destination resolution answers, “Where are we going?” Map data loading answers, “What must exist before that place can run?” Those are related questions, but they are not the same question.

So, put those two questions side by side in your notes, because they give you a very good design test for your own engine. If you have a function called loadMap, and inside it you are also deciding which door the player came through, repairing missing coordinates, changing the music, spawning objects, running entrance scripts, and deciding whether to fade from black, then the name loadMap is probably lying to you. It may still work. FireRed has plenty of dense control flow. But architecturally, you want to be able to separate the destination decision from the runtime construction.

Now, let me make that less abstract. Suppose the player walks into the door of the Pallet Town player house. The source map event says, in effect, there is a warp here, and that warp points to some group, some map, and some warp identifier in the destination. We already established in the previous pass that map records give us fields, layouts, events, and scripts, and I am not going to re-teach that structure now. What matters today is what happens after the player has triggered that doorway. The system is not simply moving a sprite. The system is changing which world is active.

On the board, I am going to write four boxes. First box: persistent save state. Second box: warp destination state. Third box: loaded map runtime. Fourth box: main callback. Those are not all the structs in FireRed, of course, but they are the four roles I want you to keep distinct.

The persistent save state is the durable story of the player. It answers questions like: which map was I on when the game was saved, what is my party, what flags and variables are set, what items do I own? The warp destination state is smaller and more immediate. It answers: where should the next map load put the avatar? The loaded map runtime is rebuilt. It answers: what tiles, connections, events, objects, and scripts are now active in memory? The main callback answers: which mode owns the frame loop right now?

That last piece is why CB2_Overworld matters so much. In pokefirered/src/overworld.c, chunk_7ca71901368c46f7, CB2_Overworld is the map runtime callback. This is not just a function that happens to run while you are outdoors. It is the frame-level owner of the overworld mode. Earlier in the course, when we studied SetMainCallback2 and the task loop, you learned that the game advances by handing control to callbacks and then letting per-frame tasks do their work. Today we use that prior foundation directly. Once the map has been resolved and loaded, CB2_Overworld is the callback that can safely assume, “I am in the overworld; the overworld systems have been prepared enough to tick.”

Notice the word safely. A callback is a promise about assumptions. If you set the overworld callback before map data exists, you have given the program permission to ask questions that have no valid answer yet. Where is the player sprite? Which metatile is underfoot? Which object events should be active? Which scripts are waiting? Which camera bounds apply? If those answers are stale, the player may see the old world while standing in the new one. If those answers are null, you crash. If those answers are partially rebuilt, you get the worst class of bug: the one that only happens after entering a particular door from a particular previous map.

So for a minimal clone, I want you to copy the architectural shape, not every implementation detail. Let’s say we are designing this in pseudocode. We would not start with a giant enterDoor function that mutates everything. We would write something more like: resolveWarp, loadMapData, placePlayer, initializeMapRuntime, setMainModeOverworld. That is the design. And yes, in a production C codebase those responsibilities may be split across more functions, flags, special cases, and callbacks, but the mental model stays clean.

Now let’s attach that to FireRed’s actual source focus. The warp destination logic lives in the area represented by pokefirered/src/overworld.c, chunk_9b2a7fa129935fc9. LoadMapFromWarp, in pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, is part of the control flow that consumes that destination state and begins the map loading path. The map data load sequence, pokefirered/src/overworld.c, chunk_f00e575dedd069ed, is where we should mentally shift from “where are we going?” to “what must be loaded before this place can operate?” And the Pallet Town reference record, pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf, is useful because it is small enough to reason about without getting lost in dungeon exceptions.

Quick check for understanding. If I ask, “Which warp did the player use?” am I asking a destination-resolution question or a map-loading question? Destination resolution. Good. If I ask, “Which object events exist on the newly active map?” that is map loading and runtime initialization. If I ask, “Which callback owns the next frame?” that is mode handoff. These are adjacent, but they are not interchangeable.

Now, let’s increase the sophistication, because by week 4 you have already done the tracing homework. I do not want you to stop at, “There is a function call.” I want you to ask what state is valid before and after the call. That is the professional habit here.

Before LoadMapFromWarp, the engine has some kind of reason to leave the current map. It might be a door, stairs, a scripted movement, a special transition, or a restored save path. After LoadMapFromWarp, the engine should be moving toward a coherent destination. But the player should not be running around yet. There is an in-between phase where the engine is allowed to dismantle old runtime state and assemble new runtime state. This is one of the most important ideas in runtime architecture: there are moments where the world is intentionally not playable.

This applies beyond maps. Think about battle entry. When the overworld hands control to battle, the game should not keep processing ordinary tile movement as though nothing happened. In pokefirered/src/battle_setup.c, chunk_494d132a59422c85, you can see battle setup sitting at an intersection of overworld, task, item, script, battle transition, battle, and field systems through its included modules. That is evidence of a boundary-crossing subsystem. Battle entry is not just “call battle.” It is a controlled handoff from one runtime mode to another. The same design rule applies: persistent state remains owned, transient battle state is constructed, the callback changes only when the new mode’s assumptions are true, and return to the overworld requires reconstruction or resumption of overworld state.

For your clone, that means your battle system should not directly rummage through map loading code. It may need to know the player’s party. It may need to know the enemy encounter. It may need to know what callback to return to. But it should not also decide which map layout is active. If battle code needs to restore the overworld after a fight, it should do that through a clear transition boundary.

Same with item use. A Potion is not “an overworld thing” just because you can use it from the menu while standing in the overworld. A Potion is an inventory-owned effect applied to party-owned creature data, often through UI-owned selection flow, while the current runtime mode is temporarily paused or covered. That is a mouthful, but it is the architecture. When you later implement item use in the minimal clone, the simple version is: choose item, choose target, validate effect, mutate party state, decrement inventory, return to previous mode. Do not bury that inside the tile system.

Let’s do a concrete example. Imagine our clone has a player with one creature, a creature with current HP 12 out of 30, and a bag containing two Potions. The player opens the menu in the overworld. The overworld callback is still the broad owner of the world mode, but the menu may install its own callback or task layer depending on the engine design. The player selects Potion. The item system asks the party system, “Which creature is a valid target?” The party system exposes enough data for validation. The item effect changes HP from 12 to 32, clamps it to 30, and the inventory count goes from two to one. Then the UI closes, and control returns to the overworld runtime.

What should not happen? The Potion should not reload Pallet Town. It should not reset the warp destination. It should not rebuild object events. It should not change the main callback to battle. That sounds obvious when stated this way, but this is how architecture rots in small clones: you put everything in the one file that currently has access to input, and suddenly using an item knows about maps, battles, save slots, and rendering. FireRed’s codebase is large and historically shaped, but the recurring pattern is still visible: systems cooperate through globals, callbacks, tasks, scripts, and structured data rather than every feature becoming one giant loop.

Now, script execution sits in the middle of this. Scripts are especially tempting to overgeneralize because they can appear to do everything. An NPC script can give an item, set a flag, start a battle, move an object, warp the player, or show text. But architecture-wise, the script engine is a coordinator. It should not mean “all game logic is script logic.” A script command should request a service from the proper subsystem. Give item talks to inventory. Start battle talks to battle setup. Warp talks to overworld transition state. Set flag talks to event data.

So in the minimal clone, your script VM can be tiny. It does not need FireRed’s full instruction set. But it should preserve the division of responsibility. You might implement commands like message, setFlag, giveItem, startBattle, warp, and end. Each command either mutates event state, asks another system to perform a domain operation, or suspends until a mode returns. The important phrase there is suspends until a mode returns. If a script starts a battle, it cannot just keep executing the next line as though the battle happened instantly, unless your battle is also instantaneous. In a real runtime, the script yields, the battle callback owns frames for a while, and then the script or overworld resumes based on the result.

Let me pause here for a check. If an NPC script says, “You looked at me, now battle,” who owns the turn order calculation? Not the script. The battle system owns it. Who owns marking the trainer as defeated afterward? That is likely event state, requested as part of the battle result handling. Who owns returning the player to the loaded map? The mode transition machinery, with the overworld callback resuming after its assumptions are restored. Good. That separation is what keeps a clone from becoming a bowl of special cases.

Now let’s connect this back to Pallet Town. Pallet Town is a good reference not because it is simple in the sense of unimportant, but because it has the ordinary ingredients without the dramatic edge cases of a dungeon, link facility, or scripted set piece. In pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf, the map record gives us a data-side description that the runtime can consume. We are not re-teaching map header fields today; you already covered those. Instead, look at the role of that record in the load sequence. It is not executable behavior by itself. It is an ingredient. The map loader uses records like this to assemble the active runtime context.

That word ingredient is useful. Data is not the game. Code is not the game. The running game is the agreement between data, code, and state over time. Pallet Town’s data can sit on disk forever and no player can move in it. CB2_Overworld can exist as code forever and no player can move in it. The player’s save can say they belong in a map, but until the runtime loads the map and hands the frame loop to overworld behavior, nothing is playable. Architecture is the sequence that makes those pieces agree.

On the board, I’ll sketch the minimal clone load path now. First, read destination: mapGroup, mapNumber, warpId. Second, find map record. Third, find layout reference and load collision or metatile behavior data. Fourth, load events: object events, warps, triggers, scripts. Fifth, resolve player coordinates, either from the destination warp or explicit coordinates. Sixth, create runtime objects and camera state. Seventh, run map entry hooks as appropriate. Eighth, set current mode to overworld.

The order is not arbitrary. For example, you cannot correctly place the player relative to a destination warp if you have not loaded or found the destination map’s warp table. You cannot correctly decide which objects are visible if you have not applied flags and map event state. You cannot safely process input if collision data is not valid. And you cannot run a map script that references local objects unless those object identities have been initialized. That is why the load sequence matters.

Now, one subtlety: in many engines, coordinates may come from multiple sources. A warp may point to a warp ID, and that ID may imply coordinates. A special warp may directly specify coordinates. A save restore may use saved map and position. A scripted teleport may override normal entrance behavior. This is where students often make the wrong abstraction. They try to make every transition look like a door. Do not do that. Instead, make every transition produce a common destination state.

That is the architecture I want you to take from FireRed’s warp machinery. The destination state is a normalized handoff object, even if in C it is represented through globals and helper functions rather than a neat modern object. Your clone might literally make it a struct called PendingMapLoad with fields for mapId, entryKind, warpId, x, y, facing, transitionStyle, and returnCallback. FireRed’s original code will not look exactly like that, and we are not pretending it does. We are extracting the architectural lesson from pokefirered/src/overworld.c, especially the relationship among chunk_9b2a7fa129935fc9 for warp destination state, chunk_7ea6afd1253d6817 for LoadMapFromWarp, chunk_f00e575dedd069ed for map data loading, and chunk_7ca71901368c46f7 for CB2_Overworld.

That gives us a nice bridge to save persistence. Saving should persist durable facts, not every transient runtime pointer. If you save in Pallet Town, you need enough information to restore Pallet Town and place the player correctly. You do not need to serialize the exact temporary task list in the same way you might serialize party data or flags. In a minimal clone, save persistence should include player identity, party creatures, inventory, event flags and variables, current map identifier, player position, facing direction, and maybe story or system settings. When loading, you rebuild the runtime from those durable facts.

This is the same pattern again: persistent state stays owned, transient runtime state is rebuilt. I am repeating that phrase because it is the spine of this week. A save file is not a memory dump of your whole program unless you are building an emulator-style save state. A cartridge RPG save is usually closer to a durable game-state record. FireRed has its own save infrastructure, and we are not diving into that today, but conceptually your clone should restore by setting destination-like state and then running the load sequence. The player should arrive in a valid overworld, not in a half-restored copy of yesterday’s task list.

Let’s make that concrete. If a player saves outside the Pallet Town lab and quits, then reloads, the clone should not need to remember that the grass animation task was on frame 3 and an NPC movement task had a temporary counter of 12 unless that movement is story-relevant and deliberately persisted. Instead, it reloads the map, recreates the NPCs according to event state, places the player, and resumes overworld control. That is what makes saves robust across code changes. It also makes debugging easier because you can inspect durable state separately from frame-local runtime machinery.

Now, because you are advanced learners, I want to point out the tradeoff. If you persist too little, you cannot restore important context. If you persist too much, your save format becomes brittle. The architectural question is: what is the smallest set of durable facts required to reconstruct the intended experience? For party data, that is more than species. You need level, experience, moves, current HP, status, maybe personality or individual values depending on the fidelity of the clone. For inventory, item IDs and counts may be enough. For scripts, flags and variables may be enough for many cases, but if you allow saving in the middle of a multi-step cutscene, then you need either to forbid saving there, checkpoint the cutscene, or persist enough script continuation state to resume safely.

FireRed solves many of these problems with mature conventions. Your minimal clone should solve them deliberately, even if simply. For example, you can say, “Saving is allowed only in stable overworld control, not in the middle of battle, text boxes, or scripted movement.” That is a perfectly reasonable early clone rule. It reduces the amount of continuation state you must persist. Later, if you want richer saving, you can expand the model.

Let me bring battle turns into this same architecture. A battle turn is another runtime construction around persistent creature data. The party owns the creatures. The battle runtime borrows or mirrors the relevant combatants, calculates choices, order, damage, status effects, fainting, experience, and result. When the battle ends, it writes back durable consequences. In a minimal clone, a turn loop can be extremely small: collect player action, choose enemy action, determine order, apply move effects, check fainting, check victory, repeat. But the important boundary is that battle state is not the same as party storage. Temporary modifiers, turn order, selected move this turn, and animation state do not all belong permanently in the party record.

This is where Pokemon Showdown can be useful later in the course as a comparison, especially for deterministic battle tests and data modeling, but for this week FireRed remains our authority because we are studying cartridge runtime handoff. The research packet notes Pokemon Showdown battle scripts, for example pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, as useful for move damage and battle logic comparison. That is not our primary source today. Today, the primary pattern is FireRed’s mode architecture: overworld callback, map load, warp state, and handoff.

So, if we assemble the minimal clone from today’s objectives, here is the spoken version of the design. The map loader takes a destination and produces an active map runtime. The script engine coordinates events and can yield to other modes. The party system owns creature records. The battle system owns battle turns and writes back results. The inventory system owns item counts and delegates effects to domain systems like party healing or field usage. The save system owns durable persistence and restores by rebuilding runtime state. The callback or main mode system decides who owns the frame.

That is the clone architecture in one paragraph. It is small, but it is not naive.

Now, before we move to the next segment, I want to test the boundaries with a slightly messy scenario. Imagine an NPC in Pallet Town gives the player a Potion, then immediately walks two tiles, then triggers a rival battle, and after the battle sets a flag so the event never repeats. What systems are involved? Script execution coordinates the sequence. Inventory receives the Potion. Object movement handles the NPC walking. Battle setup transitions out of the overworld. Battle turn logic runs the fight. Event flags record completion. Overworld resumes after battle. Save persistence later records the item count, party consequences, map position, and completion flag.

Where do students accidentally put all of that? In the NPC object. They make the NPC own the item, the battle, the movement, the story flag, and sometimes the save write. That works for one NPC. It fails for a game. FireRed’s architecture is valuable to study precisely because it shows how much behavior emerges from cooperation among specialized systems, with callbacks and data tables controlling when each system gets to act.

So the recap for this pass is this: destination resolution answers where the next map should be. Map loading answers what must be built before the map can run. CB2_Overworld, from pokefirered/src/overworld.c, chunk_7ca71901368c46f7, is the runtime callback that should receive control only after those assumptions are satisfied. LoadMapFromWarp, from pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, sits in the control path from pending warp state toward an active map. The map data loading area, pokefirered/src/overworld.c, chunk_f00e575dedd069ed, is where destination becomes usable runtime context. Pallet Town’s map data, pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf, gives us a compact reference case for that process.

Hold onto that distinction, because next we are going to press into the exact boundary between “I know the destination” and “the destination is ready.” That boundary is where many clone engines accidentally create hidden coupling. We are going to slow it down, name the intermediate states, and then turn it into pseudocode you can use in the lab trace.

Good. So take that boundary seriously. I am going to draw three boxes on the board, left to right. The first box says pending warp intent. The second says resolved destination. The third says active map runtime. Those are not synonyms. A surprisingly large number of beginner clone engines collapse all three into one variable called currentMap, and that feels fine until the first door, cutscene warp, forced respawn, or scripted teleport breaks it.

Pending warp intent means the game has been told, somehow, that the player should leave the current spatial context. Maybe the player stepped on a warp tile. Maybe a script requested a teleport. Maybe a battle ended and the player blacked out. At this point, we may have a map group, a map number, maybe a warp ID, maybe explicit coordinates, maybe a facing direction, and maybe a callback to run after loading. But the world has not changed yet. The destination is still a promise.

Resolved destination means the promise has been interpreted. The engine has decided which map header, which layout, which entrance coordinates, and which initial player position are intended. This is where the warp destination state from pokefirered/src/overworld.c, chunk_9b2a7fa129935fc9, matters architecturally. It is not just a convenience variable. It is the separation layer between the event that requested movement and the loader that can construct a playable map.

Active map runtime means enough data has been loaded and initialized that CB2_Overworld can safely run. And that phrase “safely run” is doing a lot of work. CB2_Overworld, in pokefirered/src/overworld.c, chunk_7ca71901368c46f7, should not be responsible for discovering that the current map header is missing, or that the player coordinates have not been initialized, or that the object event list has not been instantiated. By the time the callback receives control, the frame loop should be able to ask ordinary runtime questions: what tile is under the player, what scripts are pending, what objects need movement updates, what input happened this frame, and what visual effects need stepping?

Now, let me give you the clone-engine version, because this is what I want you to build in miniature before you admire the full FireRed machinery. Your minimal clone should not start by saying, “Load map Pallet Town and draw it.” It should start by saying, “There is a requested transition.” Then your loader resolves the request into a map identity. Then it loads the map record. Then it loads the layout or tile collision model. Then it places the player. Then it installs the runtime callback. In other words, your clone needs a pipeline, not a giant function called enterMap that mutates everything in one breath.

Concrete example. Suppose the player is standing at the door of the player’s house in Pallet Town. The source map event says, in effect, this warp leads to a destination map and a destination warp slot. We are not re-teaching map records here; you already worked with map header fields and object events last week. But now we care about the control consequence. The source event should not directly know the destination coordinates unless the design requires that. It should point into destination data. Pallet Town’s map data in pokefirered/data/maps/PalletTown/map.json, chunk_05643f6bfaa3b9bf, is useful as a compact reference because it keeps the example small enough that we can trace the chain without drowning in city-scale content.

So on the board, I am writing this as a verbal pseudocode sequence. Step one, receive warp trigger. Step two, store warp destination request. Step three, fade out or otherwise suspend player control. Step four, call LoadMapFromWarp. Step five, load destination map data. Step six, initialize map runtime state. Step seven, hand off to CB2_Overworld. Notice the key thing: the trigger does not draw the destination map, and CB2_Overworld does not decide where the warp goes. The middle is doing real architectural work.

LoadMapFromWarp, from pokefirered/src/overworld.c, chunk_7ea6afd1253d6817, is exactly the sort of function name I want you to learn to read as an architectural signal. It is not merely “some code that changes maps.” It is positioned at the transition between pending warp state and loaded map. It tells us that FireRed treats warp loading as a specific control path, not just as a generic map assignment. Then the map data load sequence in pokefirered/src/overworld.c, chunk_f00e575dedd069ed, is where the destination becomes usable runtime context. Again, that distinction is the lesson.

Check your understanding for a second. If I ask, “Where should a clone engine validate that a destination map ID actually exists?” the answer is not “inside the player movement code.” The movement code may detect that a warp tile was stepped on. It may submit the request. But validation belongs near resolution or loading, because that is where map identity becomes actual map data. If I ask, “Where should the engine poll directional input after a warp?” the answer is after the runtime callback has resumed, not while the destination is still half-constructed. Good? You should be able to feel those boundaries now.

And let me connect this to the rest of the course, because week 4 is where the architecture starts to become a running game instead of a catalog of files. In week 1, we decomposed the creature RPG into runtime shell, overworld maps, scripting, party data, battle, inventory, UI, save, and link systems. In week 2, we learned to trace from data records through globals, callbacks, tasks, scripts, structs, and concrete source functions. In week 3, you identified the files and state variables that implement the major systems. Now, in week 4, we are making those pieces move through time.

That phrase matters: through time. A map record is static. A callback is dynamic. A warp is a temporal bridge. The game is not merely “on Pallet Town.” It is entering Pallet Town, running Pallet Town, suspending Pallet Town, leaving Pallet Town, and later reconstructing enough state to resume somewhere else. That is why callbacks and task systems from earlier lectures are not trivia. SetMainCallback2 from pokefirered/src/main.c, chunk_6cfd90f6d0136799, and RunTasks from pokefirered/src/task.c, chunk_3ffebc0341225936, gave us the frame-level language. Today’s overworld path gives us a specific gameplay use of that language.

Now widen the lens. Minimal map loading is only one of the clone systems in this course objective. Script execution follows the same principle. A script trigger is not the same as a script VM being in a stable execution state. You detect an event, you enqueue or start a script, you suspend conflicting controls, and then the script interpreter advances in controlled steps. Party data follows a similar boundary. A party slot is not merely a displayed icon. It is persistent creature state, temporary battle state, UI selection state, and sometimes script-facing state. Battle turns follow it too. Choosing a move is not the same as executing damage. You collect commands, order actions, resolve effects, update party state, then return control. Item use is another version: selecting a potion in the bag is not the same as mutating HP; the engine must validate target, apply effect, show message, update inventory, and return to the proper caller. Save persistence is the most unforgiving case. The save function must serialize stable game state, not half-transition state, unless you explicitly design recovery rules for that.

This is also why I keep using FireRed as the primary authority and Pokemon Showdown only as a comparison when we discuss battle modeling later. The research packet points us toward FireRed’s runtime source for this section, especially pokefirered/src/overworld.c chunks for CB2_Overworld, map loading, warp state, and LoadMapFromWarp. Showdown’s battle scripts, such as pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, are valuable when we compare deterministic battle effect modeling. But Showdown does not teach us GBA overworld callback architecture. For this week’s question, FireRed is the ground truth.

Let’s do one more board pass. I am writing “clone invariant” next to the three boxes. The invariant is this: no runtime loop should execute against partially initialized world state. Say that back in your head. No runtime loop should execute against partially initialized world state. If your overworld update can run while currentMap is changed but collision is old, you have a bug waiting to happen. If your script engine can run while the map object list belongs to the previous map, you have hidden coupling. If your save system can write after the warp destination has changed but before the player position has been committed, you have a corruptible save model.

In a minimal clone, you can enforce this with a simple state enum. You do not need FireRed’s full machinery. You can have states like overworldRunning, transitionOut, resolvingWarp, loadingMap, transitionIn. The names are yours, but the separation is not optional if you want a clean engine. During resolvingWarp, you do not accept player movement. During loadingMap, you do not run NPC behavior. During transitionIn, you may draw and animate, but you still may withhold player control until the handoff is complete. Then, and only then, your overworld callback resumes normal frame updates.

Let me ask a sharper check for understanding. Suppose your clone has a bug where entering a house sometimes places the player at coordinate zero, zero. Where do you inspect first? Not the renderer. Not the input system. You inspect the warp resolution path. Did the source warp supply a destination warp ID? Did the destination map contain that warp slot? Did your loader translate that warp slot into player coordinates? Did you accidentally overwrite explicit coordinates with default coordinates? That is the diagnostic discipline we are practicing.

And suppose the bug is that an NPC from the previous map appears for one frame after a warp. That points to the active map runtime boundary. Your object events or entity instances are being cleared too late, or your callback resumes too early, or your renderer is allowed to draw from stale runtime arrays during transition. Again, the cure is not “add an if statement somewhere.” The cure is to respect the loading sequence.

Now, as we close this lecture, I want to bring the whole day into one synthesis. CB2_Overworld is not just a function to find in pokefirered/src/overworld.c, chunk_7ca71901368c46f7. It represents the stable running mode of the map experience. LoadMapFromWarp, in chunk_7ea6afd1253d6817, represents a transition path into that mode. The map data load sequence, chunk_f00e575dedd069ed, represents the construction of usable runtime context. The warp destination state, chunk_9b2a7fa129935fc9, represents the memory of where we intend to go before the world is safe to run. Pallet Town’s map JSON, chunk_05643f6bfaa3b9bf, gives us a small enough data case to trace without losing the architecture.

For the lab, your job is not to reproduce FireRed line for line. Your job is to trace the shape of the control flow and then implement the smallest honest version of it. Start from a source warp event. Resolve map group, map number, warp ID, and player coordinates. Load the destination map record. Prepare the runtime structures. Then hand off to your overworld update loop. If your pseudocode has one variable named currentMap and everything mutates it directly, revise it. If your pseudocode has a destination request, a resolution step, a loading step, and a runtime handoff, you are thinking like an engine programmer.

That is the bridge from today into the next lecture. Today we entered the overworld runtime by asking when CB2_Overworld is allowed to run. Next time, we will move more deliberately from warp state to loaded map, and we will trace the destination path with tighter attention to the exact data dependencies. Keep the distinction in your notes: knowing where to go is not the same as being ready to be there. That one sentence will save you from a large class of clone-engine bugs.

## 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
- pokefirered/src/battle_util.c
