# Interaction Checks and Field Control

Lecture ID: lecture_w06_d02
Week 6, Day 2

## Objectives
- Trace FireRed gameplay from data records through globals, callbacks, tasks, scripts, structs, and concrete source functions.
- Connect week 6 material to the overall course plan.

## Transcript
All right, let’s pick up exactly where yesterday’s lecture left us. Yesterday we followed input far enough to see that the D-pad is not, in FireRed, a direct command to move a sprite. That is the tempting beginner model: button pressed, avatar moves. But the real architecture is more defensive and more interesting. The button becomes a small record of intent, that intent is interpreted against avatar state, script state, map state, tile behavior, object events, and only then does the game decide whether we are walking, bumping, talking, surfing, reading, warping, opening a menu, or doing nothing.

Today we are going to slow down inside that decision layer. Not at the renderer, not at map loading, not at CB2_Overworld itself, because we already used CB2_Overworld as the runtime foundation. Today we are inside field control: the portion of the overworld that answers a very specific question every frame. Given the current map and the current avatar, what is the player allowed to cause?

Let me put a short phrase on the board: “input is not action; input is evidence.” That is the right mental model for FireRed. The raw hardware keys are evidence. The avatar’s transition state is evidence. The tile behavior underfoot is evidence. The object event in front of the player is evidence. Then the field controller adjudicates. It is basically a priority-ordered reducer, and that is exactly why this week’s lab asks you to write a controller-to-avatar reducer that decides whether input causes movement, collision rejection, warp trigger, object interaction, or script dispatch.

Now, if you look at the context research packet for today, the main file is pokefirered/src/field_control_avatar.c, especially chunk_636f753078bf545a for avatar field control, chunk_79332c416dfb6ac9 for player interaction handling, and chunk_4488cb84aee4d6dd for field-control branching. The companion source is pokefirered/src/fieldmap.c, chunk_eeb0180842f7ece9, which gives us the tile behavior support. Those are not four unrelated excerpts. They form a chain. Field control asks, “What did the player try to do?” Field map answers, “What kind of world cell is being targeted?” The script system then answers, “Is there a scripted response to that world cell, object, or coordinate?”

Let’s walk that chain carefully.

Start with FieldGetPlayerInput in pokefirered/src/field_control_avatar.c, chunk_636f753078bf545a. This function receives newKeys and heldKeys, but it does not simply copy them into gameplay state. First it looks at gPlayerAvatar.runningState and gPlayerAvatar.tileTransitionState. That second one matters a lot. FireRed generally wants major field interactions to happen when the avatar is stable enough to reason about. If the player is mid-tile, the coordinate in front of the player, the coordinate under the player, and the tile transition animation can disagree in subtle ways. So FieldGetPlayerInput gates button interpretation around states like T_TILE_CENTER and T_NOT_MOVING, with special handling for forced movement tiles.

This is one of those small source-level details that reveals a big architectural principle. The map is grid-based, but the visible avatar is animated between grid centers. So the system needs a policy for when input is accepted as a new field action. FireRed’s policy is not “whenever a key is physically pressed.” It is closer to “when avatar state and field state are coherent enough to accept another command.”

Notice also the treatment of forced movement. FieldGetPlayerInput computes forcedMove using MetatileBehavior_IsForcedMovementTile on the player’s current metatile behavior. That is already a merge of two systems. The avatar controller is asking a tile-behavior classifier whether the ground itself is currently in charge. Ice slides, currents, directional movement tiles, and similar behaviors can take priority over ordinary player agency. So if you are designing your own engine, this is the moment where you should resist putting all movement rules into the input module. The input module should not own all movement rules. It should consult field state.

Now, check for understanding before we move down the call chain. Suppose the player presses A while the avatar is being carried by a forced movement tile. Should the game immediately treat that as a talk or inspect command? No, not under the ordinary gating policy. The A press may be physically real, but it is not necessarily accepted as a field interaction. The field controller is allowed to say, “Not now; the avatar is under a movement constraint.” That distinction, physical input versus accepted input, is essential.

A second important function is ProcessPlayerFieldInput, same file, and this is where today’s lecture really lives. If FieldGetPlayerInput builds the intent record, ProcessPlayerFieldInput consumes it. At the top it resets facing NPC or signpost variables, gets the player direction with GetPlayerFacingDirection, gets the player’s current position with GetPlayerPosition, and asks the map for the metatile attributes and behavior at that position. Here is where pokefirered/src/fieldmap.c, chunk_eeb0180842f7ece9, enters the lecture. MapGridGetMetatileAttributeAt gets a metatile id through MapGridGetMetatileIdAt and then extracts a requested attribute from the primary or secondary tileset’s metatileAttributes. MapGridGetMetatileBehaviorAt is a thin, important convenience: it asks for METATILE_ATTRIBUTE_BEHAVIOR.

So, on the board, I’m drawing a pipeline. Raw keys become FieldInput. FieldInput plus gPlayerAvatar tells us which intentions are admissible. Then ProcessPlayerFieldInput combines that intent with MapPosition and metatile behavior. Then, depending on priority, the system starts a script, starts a warp, opens a menu, uses a registered item, or returns no handled action. This is the data-record to global-state to function-branch to visible-behavior trace that we have been practicing since week one.

Let’s be concrete. Imagine the player is standing in front of an NPC behind a counter in a Poké Mart. The player presses A. What does FireRed need to know? It needs the player’s facing direction. It needs the position immediately in front of the player. It needs the metatile behavior of that position, because a counter changes how interaction distance works. It needs to ask whether an object event exists at the target coordinate. If no object is directly at the counter tile, the code may need to look one tile beyond the counter. Then, once it identifies an object event, it needs to store which event was selected, store the local id into gSpecialVar_LastTalked, store the facing direction into gSpecialVar_Facing, retrieve the script pointer, and possibly pass that script through GetRamScript for dynamic replacement.

That is not hypothetical. In pokefirered/src/field_control_avatar.c, chunk_79332c416dfb6ac9, GetInteractedObjectEventScript does exactly this kind of work. It first calls GetObjectEventIdByPosition on the target position and elevation. If that fails, and if the metatile behavior is a counter, it looks one step farther using gDirectionToVectors. That is why you can talk to a clerk across a counter instead of needing the clerk on the same tile you are inspecting. The map tile is not just art. Its behavior modifies the interaction query.

This is a pattern you should carry into your own architecture. You do not want one-off code that says, “If map is mart and player is facing north and NPC is clerk.” Instead, you want a general interaction lookup: target position, elevation, behavior classifier, object event lookup, script pointer. Then the data does the specializing. The counter tile says, “Look beyond me.” The object event says, “Here is my local id and script.” The script system says, “Here is the actual behavior once selected.”

Now let’s examine the priority order, because this is where many clone projects become subtly wrong. In ProcessPlayerFieldInput, FireRed does not process A-button interaction first. It first checks CheckForTrainersWantingBattle. Then it checks TryRunOnFrameMapScript. Then, if a step was taken, it increments step-related counters and tries step-based scripts. Inside TryStartStepBasedScript, from chunk_4488cb84aee4d6dd, the order is coordinate events, warp events, miscellaneous walking scripts, step-count scripts, and repel counter update. That means stepping onto a coordinate event or warp is not the same kind of interaction as pressing A. It is a movement consequence.

Let me repeat that because it will appear on the quiz this week in some form. Pressing A asks the world in front of the player, “Do you have an interaction script?” Taking a step asks the world under the player, “Did entering this coordinate trigger anything?” Those are different lookup problems. They use overlapping data, yes, but they are not the same control path.

In the step-based path, TryStartCoordEventScript calls GetCoordEventScriptAtPosition using the current map header, adjusted by MAP_OFFSET, and elevation. If a script exists, ScriptContext_SetupScript is called. That is field control handing authority to the script engine. Then TryStartWarpEventScript checks for a warp event at the player’s current map position and checks whether the current metatile behavior is actually a warp behavior. The data record alone is not enough. A warp event record and a compatible metatile behavior must agree.

This is one of the best examples of defensive architecture in the overworld. Why require both? Because the event list says, “There is a warp here,” while the metatile behavior says, “This kind of tile should behave as a warp.” If those disagree, you probably have a map authoring error, and the runtime has a chance to avoid nonsensical behavior. A coordinate with a warp event under an ordinary floor tile should not necessarily rip the player into another map. Conversely, a door-looking tile without a warp event has no destination. FireRed’s runtime logic joins authored event data with tile semantics.

Now, someone might ask, “Professor, is that really data-driven, or is it code-driven?” Good. The answer is both, and that is the point of this course. The map headers, object events, background events, coordinate events, warp events, and metatile attributes are data records. But the meaning of those records is implemented by concrete C functions. Data-driven does not mean “no code.” It means the code is written as an interpreter of structured data. In FireRed, field_control_avatar.c is one interpreter of overworld interaction data.

Let’s go back to the A button. ProcessPlayerFieldInput gets the position in front of the player, gets that position’s metatile behavior through MapGridGetMetatileBehaviorAt, and then checks input->pressedAButton. If true, it calls TryStartInteractionScript. TryStartInteractionScript calls GetInteractionScript. And GetInteractionScript has one of the most important priority orders in today’s lecture: object event first, background event second, metatile script third, water script fourth.

I’ll write those four on the board in order: object, background, metatile, water. This is not just an implementation detail. It determines what players perceive as “the thing I interacted with.” If there is an NPC in front of you, the NPC wins before the tile’s generic behavior. If there is a background event, such as a signpost or hidden item, that wins before generic metatile text like bookshelf or PC behavior. If neither object nor background event handles the interaction, the metatile behavior may still provide a generic script. And finally, water-specific checks can produce Surf, waterfall, or current messages depending on flags, party state, and facing conditions.

Consider a PC in the player’s room. GetInteractedMetatileScript checks MetatileBehavior_IsPC and returns EventScript_PC. TryStartInteractionScript then sets up that script, but with a small exception: it does not play the ordinary interaction sound for PalletTown_PlayersHouse_2F_EventScript_PC or EventScript_PC. That tiny conditional is useful pedagogically because it shows that even once the script pointer has been chosen, field control may still apply presentation policy before handing off. The architecture is layered, but not sterile. Real games often have small presentation exceptions at the boundary.

Now let’s connect this to globals, because advanced students should be tracing not only calls, but also state writes. In GetInteractedObjectEventScript, after resolving the object event id, the function writes gSelectedObjectEvent, gSpecialVar_LastTalked, and gSpecialVar_Facing. Those globals become part of the script context. A script that runs after talking to an NPC can refer to “last talked” without being passed a neat modern object reference. This is a GBA C codebase; the contract is global state plus script variables. That may make some of you uncomfortable from a modern software-engineering perspective, but it is consistent and economical for the platform and engine style.

So the trace is: object event data gives a local id and script pointer; runtime lookup selects an object event id; globals record selection and facing; ScriptContext_SetupScript schedules the script; the overworld callback and script engine then advance behavior over subsequent frames. That is the exact kind of trace your homework should contain. Not “the NPC script runs,” but “GetInteractedObjectEventScript selects object event id, writes gSelectedObjectEvent and gSpecialVar_LastTalked, resolves GetObjectEventScriptPointerByObjectEventId, possibly GetRamScript, then TryStartInteractionScript calls ScriptContext_SetupScript.”

Let’s pause and do a quick verbal exercise. Suppose you press A facing a bookshelf tile with no object event and no background event. Which resolver returns the script? Object event returns null. Background event returns null. GetInteractedMetatileScript sees MetatileBehavior_IsBookshelf and returns EventScript_Bookshelf. Now suppose there is an NPC standing on that same target tile. Which wins? Object event wins, because GetInteractionScript asks GetInteractedObjectEventScript first. If you got that, you understand the priority model.

Now I want to fold in the movement side without re-teaching yesterday. Yesterday we followed the avatar movement attempt flow and collision rejection. Today we are looking at what happens around movement, not just whether movement succeeds. In ProcessPlayerFieldInput, after step handling and wild encounter handling, FireRed checks held-direction behavior for arrow warps and walk-into signposts. That is subtle. Some interactions are not A-button interactions. A signpost can be read by walking into it from the correct direction. A door warp may require holding direction toward a north-facing door. Arrow warps are checked when the held direction matches the player’s facing direction.

TryArrowWarp, in field_control_avatar.c chunk_4488cb84aee4d6dd, first looks up a warp event at the current map position. If one exists, it checks whether the metatile behavior matches an arrow warp for the direction, using IsArrowWarpMetatileBehavior. For directional stair warps, it can also change bike state back to on foot and introduce a delay before DoStairWarp. This is field control doing more than script dispatch. It is managing avatar mode, transition feel, and map transition setup.

TryDoorWarp is even more specific. It only proceeds if the direction is north, the metatile behavior is a warp door, a warp event exists at the target position, and the behavior passes IsWarpMetatileBehavior. Then it stores initial avatar state, sets up the warp, and calls DoDoorWarp. Again, the authored map event and the tile behavior must cooperate.

Now, where do the behavior values come from? Back to pokefirered/src/fieldmap.c, chunk_eeb0180842f7ece9. MapGridGetMetatileIdAt extracts the metatile id from the virtual map grid, using the border block if the coordinate is outside the defined grid. MapGridGetMetatileAttributeAt then calls GetAttributeByMetatileIdAndMapLayout. That helper chooses between the primary tileset’s metatileAttributes and the secondary tileset’s metatileAttributes based on the metatile id range. This is the bridge between map layout data and runtime interaction. The tile you see is not enough; the behavior attribute is what the field controller consumes.

And this is why we spent time in previous weeks on generated data, map headers, layouts, and tilesets. At the time, those may have felt like static resources. Today they are active participants in control flow. A metatile behavior value becomes a branch condition inside FieldGetPlayerInput, ProcessPlayerFieldInput, GetInteractedMetatileScript, TryStartWarpEventScript, TrySetUpWalkIntoSignpostScript, and TryDoorWarp. Data becomes behavior through repeated classification.

Let’s do one more concrete example, this time water. The player faces surfable water and presses A. The system gets the in-front position, reads metatile behavior, and GetInteractionScript eventually reaches GetInteractedWaterScript if object, background, and generic metatile scripts do not handle it. GetInteractedWaterScript checks whether the tile is fast water and whether the party has Surf, returning EventScript_CurrentTooFast if appropriate. It checks FLAG_BADGE05_GET, PartyHasMonWithSurf, and IsPlayerFacingSurfableFishableWater before returning EventScript_UseSurf. For waterfall, it checks the waterfall behavior, the seventh badge flag, and whether the player is surfing north. This is a nice example because the interaction is not stored solely in the tile. The tile says “water” or “waterfall,” the party model says whether a move is available, event flags say whether the player is authorized, and avatar state says whether the orientation is valid.

That is the architectural pattern for the whole course. Battle will have its own version of this, where move data, party data, volatile battle state, ability hooks, item hooks, and scripts combine. Inventory has its own version, where item records, field context, party context, and menu state combine. Save has its own version, where global state is serialized into persistent blocks. Week 6 is not an isolated input week. It is where you learn to see the overworld as a set of cooperating state machines that translate player intent into controlled changes.

Before we stop this first segment, let me recap the high-order idea. FireRed’s field control is not a monolithic “handle button” function. It is a layered reducer. FieldGetPlayerInput filters hardware input through avatar and script gating. ProcessPlayerFieldInput orders possible consequences: trainer attention, frame scripts, step scripts, wild encounters, held-direction warps and signs, A-button interactions, door warps, menus, and registered items. Interaction lookup then prioritizes object events, background events, metatile scripts, and water scripts. Fieldmap supplies the behavior attributes that make these branches data-sensitive. The result is a system where authored map data, runtime globals, callbacks, scripts, and concrete source functions all meet in the same per-frame decision.

Hold that model. In the next segment, we are going to zoom in on why this priority order prevents ambiguous field actions, and we will compare two cases that look similar to the player, talking across a counter and reading a sign, but are implemented through different lookup paths.

Good. Let’s take those two cases slowly, because this is where a lot of advanced students accidentally flatten the architecture too much. From the player’s point of view, pressing A while facing a person behind a counter and pressing A while facing a sign both feel like the same operation: “inspect the thing in front of me.” But the engine does not treat those as one generic thing. It treats them as a prioritized search across different authored surfaces.

So, imagine I am standing in a Poké Mart, facing the clerk. I press A. The first interesting question is not, “What text should appear?” The first question is, “What class of target did the player intend?” And FireRed answers that question by looking for an object event before it falls through to background or metatile interaction. That matters because the clerk is not just a tile. The clerk is a runtime object event with coordinates, facing, local id, movement state, and an attached script pointer. The sign, by contrast, is usually not a live object. It is commonly represented as a background event or sometimes as a tile behavior that implies a script. So the same button press travels through different lookup paths.

On the board, I’m going to write this as a priority chain, not as a menu of equal options. First: can the avatar currently accept interaction? Second: is there an object event in the faced position, including special cases such as talking across a counter? Third: is there a background event at the relevant position? Fourth: does the metatile behavior imply a script, such as a sign, bookshelf, PC, or water interaction? That is the important shape. It is not merely “A means talk.” A means “ask the field-control reducer to resolve an interaction according to ordered rules.”

This is why the source focus for today is specifically pokefirered/src/field_control_avatar.c, especially chunk_79332c416dfb6ac9 for player interaction handling, chunk_4488cb84aee4d6dd for field-control branching, and chunk_636f753078bf545a for avatar field control. Those chunks are the place where our abstract reducer becomes concrete C code. And when the interaction needs to ask, “What kind of tile am I facing?” we lean on pokefirered/src/fieldmap.c, chunk_eeb0180842f7ece9, because fieldmap is part of the support layer that turns map blocks and metatile behavior into usable runtime answers.

Now, let’s make the distinction more precise. A counter conversation looks like a spatial exception. The player is not occupying the same tile as the clerk, and often the player is not even immediately adjacent in the ordinary “face tile, find object” sense if the counter tile is between them. But the authored experience says, “This is still a conversation.” The field-control code therefore cannot be satisfied with a naive adjacency check. It needs a policy that understands facing direction, the tile in front of the player, and sometimes the tile beyond that. This is not just collision anymore. Collision asks, “Can I move there?” Interaction asks, “What meaningful target does this facing relation imply?”

A sign is different. When the player faces a sign and presses A, there may be no object event at all. The target may be resolved from a background event placed in the map data, or from metatile behavior. In that case, the map itself is speaking through data. The player sees a sign graphic. The engine sees a block with a behavior value, or a background event record at a coordinate, and then dispatches a script. So if you are designing the lab reducer this week, this is the exact distinction I want represented. Do not make your reducer return only “interact.” Make it return something like “object interaction,” “background interaction,” “metatile script,” or “no interaction,” because those are different architectural claims.

Let me pause for a check. If I press A facing a clerk across a counter, why should the object-event path usually have priority over a metatile sign-like path? Think it through. The counter tile might itself have behavior. The floor might have behavior. The map may contain background events nearby. But the visible intent is to talk to the clerk. If the engine checked the tile behavior first and treated the counter as the final target, the player could be blocked from talking to the object behind it. Priority order is therefore a design mechanism. It is not only an implementation detail.

And this gives us a broader principle for week 6: field control is ambiguity management. The Game Boy Advance does not know what the player “meant.” The engine infers intent by ordering checks. Movement has one set of priority rules. Interaction has another. Warps have another. Menus and registered items have still another. What makes the game feel clean is that most frames have exactly one winning consequence.

Now I want to connect this to the “data records through globals, callbacks, tasks, scripts, structs, and concrete source functions” trace that we have been using since week 1. Start with data records. The map contains object events, background events, warp events, coordinate events, layout references, block data, and metatile behavior. We covered the loading side earlier, so I am not going to re-teach LoadMapFromWarp or the whole map-header sequence. Just remember the result: by the time CB2_Overworld is running as the map runtime callback, the current map has been loaded into runtime memory, and the player avatar exists as field state. The per-frame callback does not parse the whole map from scratch. It consults prepared runtime structures and globals.

Then we move to globals and structs. The avatar has position and facing. The map has event records. Object events have active runtime slots. The script system has state about whether a script is already running. The field control logic asks these structures questions. Can the player move? Is the avatar locked? Is a script active? Is there a forced movement? Is the player currently in a state where A-button interaction should be accepted? That gating is why chunk_636f753078bf545a matters for avatar field control. The code is not merely sampling buttons; it is sampling buttons under movement state constraints.

Then callbacks and tasks enter the story. We already know from earlier weeks that FireRed’s runtime shell is driven through main callback slots and per-frame task dispatch. We cited pokefirered/src/main.c, chunk_6cfd90f6d0136799, for SetMainCallback2, and pokefirered/src/task.c, chunk_3ffebc0341225936, for RunTasks, when we built the callback-and-task model. Today’s point is that field control lives inside that same architectural rhythm. It is not a separate operating system. It is a per-frame decision layer inside the overworld runtime.

Then scripts. Once the interaction resolver finds an object event or background event or metatile script, the result is not “print text” directly. It is usually “start or dispatch a script.” That script may display dialogue, open a shop menu, check a flag, set a variable, begin a battle, give an item, or transfer control to another field effect. This is why pressing A at a clerk can eventually open a shop interface, while pressing A at a sign can simply display a message. Both begin as interaction resolution, but the downstream script decides the higher-level behavior.

Let’s do a concrete trace. Suppose the player is in front of a town sign. The player releases movement, faces the sign, and presses A. The hardware input has already been sampled into the field input path. FieldGetPlayerInput, as we discussed in the previous segment, filters that input through avatar and script gating. ProcessPlayerFieldInput then evaluates possible consequences in a deliberate order. By the time A-button interaction is considered, higher-priority conditions such as certain trainer attention, coordinate scripts, or immediate movement consequences may already have had a chance to win. If A-button interaction is still valid, the interaction code in pokefirered/src/field_control_avatar.c, chunk_79332c416dfb6ac9, asks what is in front of the avatar. If no object event claims the interaction, it can fall through to background or metatile logic. The metatile support side, grounded in pokefirered/src/fieldmap.c, chunk_eeb0180842f7ece9, supplies the behavior classification needed for tile-sensitive decisions. Finally, a script runs, and the visible result is text.

Now compare that to a clerk. Same A button. Same initial filtering. But the lookup hits an object event path. The object event record points to a script. The script may contain shop behavior. The visible result is not just a line of text; it may be a menu backed by item tables and money checks. We are now several systems away from the A button, but the first fork was field-control interaction priority.

This is also why I keep warning you not to describe FireRed as “data-driven” in a vague way. Yes, it is data-driven, but the data does not execute itself. Data becomes behavior only when some runtime function decides to interpret it. Object event data becomes a person you can talk to because the object event system instantiates it, the field-control code finds it, and the script engine dispatches its script. Metatile behavior becomes a readable sign or surfable water because fieldmap exposes behavior queries and field-control code gives those queries a place in the input decision order. Data-driven architecture is data plus interpreters plus scheduling.

Quick check for understanding: if a student’s clone stores signs as tiles only, and NPCs as entities only, where should the A-button resolver live? It should not live inside the tile class alone, and it should not live inside the NPC class alone. It should live in a field-control layer that can ask both systems in priority order. That is the reducer pattern for the lab. The reducer receives current input and current field state, then returns the consequence: move, collide, warp, talk, inspect, run script, open menu, use registered item, or do nothing.

Now, there is a subtle design pressure here. The more interaction surfaces you support, the more tempting it becomes to let every subsystem poll input for itself. The NPC system checks A. The sign system checks A. The door system checks A. The menu system checks Start. The item system checks Select. That seems easy at first, but it creates ambiguous ownership. What if two systems both think they should respond this frame? What if a script is running? What if the player is sliding, biking, surfing, or locked in a forced movement? FireRed’s field-control structure avoids that by centralizing the question of “what does this field input mean right now?” The subsystems provide data and helper queries, but field control arbitrates.

That arbitration is especially important around movement state gating. The player avatar is not a free cursor. It is an actor in a tile-based world with animation commitments. If a step animation is in progress, the next input cannot simply teleport into effect. If a script has locked the avatar, directional input should not begin walking. If a trainer has spotted the player, the A button should not open the menu while the trainer approach script is taking over. The gating checks are what preserve the fiction that the world is responding coherently rather than racing against the input device.

Let’s put this in course context. In weeks 1 and 2, we talked about the runtime shell, callbacks, generated data, headers as contracts, and the difference between compile-time assets and runtime state. In weeks 3 and 4, we widened the system: maps, event scripts, party data, battle entry, inventory, UI, save persistence. In week 5, we used Pokémon Showdown only as a comparison point for battle data hooks and deterministic test culture, not as a replacement model for FireRed. Now, in week 6, we are narrowing the lens again. We are looking at one second of ordinary walking around and showing that it crosses the whole architecture. That is the sophistication jump. The basic student says, “The player pressed A.” The advanced student says, “A hardware input bit was admitted through avatar state gates, interpreted by field-control branching, resolved against object/background/metatile data, and converted into script dispatch or another field consequence.”

Notice also how this prepares us for weeks 7 and 8. Story progression is not just a list of flags. A flag matters when a script branch reads it. A script matters when field control dispatches it. A map event matters when the overworld callback gives it a frame in which to be checked. So when we move into story progression, you should already be thinking: what runtime path causes this authored event to become active? Is it a coordinate event? An A-button object event? A background event? A trainer sight event? A warp? A menu action? The answer determines the shape of the engine code you need.

Let me add one more example, because it is a common source of bugs in student clones. Suppose there is a door tile in front of the player. The player presses Up and moves into it. That may be a warp consequence from movement, not an A-button interaction. But suppose there is a sign on a wall. The player presses Up and cannot move into the wall; then presses A to read it. Those look visually similar, because both involve facing something. But the first is movement attempting to enter a special tile, and the second is interaction inspecting a faced tile. If your clone handles both as “press button near tile,” you will blur an important distinction. FireRed keeps that distinction visible in the branching order in pokefirered/src/field_control_avatar.c, chunk_4488cb84aee4d6dd, where field-control consequences are selected rather than treated as one generic collision callback.

Now, I want you to listen for the phrase “winning consequence.” It is useful. Each frame, the field-control layer is choosing a winning consequence for the player’s input under current world state. Sometimes the winner is movement. Sometimes it is collision rejection. Sometimes it is a warp. Sometimes it is an object script. Sometimes it is opening the Start menu. Sometimes it is nothing, because state gates reject the input. A clean reducer has exactly this property: it can explain why one outcome won and the others did not.

Check yourself here. If an NPC is standing on the tile in front of the player, and the metatile under that NPC has a behavior that could also be meaningful, which layer should win when A is pressed? In FireRed’s style, the object interaction should generally win, because the object event is the more specific authored target. Specific runtime entities beat generic tile behavior. That is a priority principle you can carry into your own engine designs.

But, and this is important, specificity does not mean hard-coding every case. The code should not say, “If map is Celadon Mart and x equals this and y equals that, open shop.” The object event carries a script pointer or script association. The map data carries event records. The metatile carries behavior. The field-control code supplies the policy for choosing among them. This is the separation we have been building all semester: data records describe authored world content, runtime structures make them available, callbacks and tasks schedule the frame, field-control functions choose an interpretation, and scripts perform authored consequences.

So when you work on the lab reducer, model at least four inputs to the decision, even if your implementation is smaller than FireRed. First, the raw controls for the frame. Second, the avatar state: position, facing, movement lock, animation progress, and maybe mode such as walking, biking, or surfing. Third, the map query interface: collision, tile behavior, warp lookup, background event lookup. Fourth, the object query interface: object at faced position, object across counter, trainer attention, or other entity-driven triggers. Your reducer should not know how to draw the map, and it should not know how to render text. It should return a consequence that another layer can execute.

And if you want a testable version of that, make fixtures. One fixture: player faces an NPC, presses A, expects object script. Second fixture: player faces a sign tile, presses A, expects metatile or background script. Third fixture: player presses a direction into a wall, expects collision rejection. Fourth fixture: player steps onto a warp tile, expects warp consequence. Fifth fixture: player presses Start while locked by script, expects no menu. Those tests are not just checking gameplay. They are checking architecture. They verify that your priority chain is explicit.

This is also why the recent homework and quiz expectations are higher now. Earlier, it was enough to name the major systems. Then it was enough to design minimal clone versions of map loading, battle turns, item use, and save persistence. Now I expect you to trace the live path. If I give you a player-visible event, you should be able to say what data record participates, what global or struct stores the current state, what callback or task gives it execution time, what concrete field-control function chooses the branch, and what script or subsystem receives control afterward. That is the advanced-level move.

Let’s stop on one last intermediate synthesis before we continue. The counter and the sign teach the same lesson from opposite sides. The counter conversation proves that interaction cannot be reduced to the tile directly in front of the player, because runtime object events may be the intended target through a spatial rule. The sign proves that interaction cannot be reduced to objects, because static map data and metatile behavior can also be the intended target. Field control sits between those facts. It is the layer that turns “the player pressed A while facing north” into a single, authored, world-sensitive action. Keep that in your notes, because next we are going to use that model to analyze movement state gating more aggressively: not just what interaction wins, but when the engine refuses to ask the interaction question at all.

Good. So let’s make that refusal explicit. The engine is not asking, every frame, “Did the player press A, and what is in front of them?” It is asking a narrower question: “Given the avatar’s current movement state, is this frame even allowed to become an interaction frame?” That distinction is the heart of movement state gating.

On the board, I’m going to write three layers: raw keys, normalized field input, and winning field action. Raw keys are the hardware-facing state: A is newly pressed, Start is newly pressed, the D-pad is held north, and so on. Normalized field input is the `struct FieldInput` layer populated by `FieldGetPlayerInput`. Winning field action is what `ProcessPlayerFieldInput` decides to start, if anything. That middle layer matters because it is where FireRed says, “Yes, the A button physically changed, but no, that does not mean the avatar may talk right now.”

Look at `pokefirered/src/field_control_avatar.c`, chunk `chunk_636f753078bf545a`, where the field-control input path is concentrated. `FieldClearPlayerInput` wipes the semantic flags: `pressedAButton`, `pressedStartButton`, `pressedSelectButton`, `heldDirection`, `heldDirection2`, `tookStep`, `checkStandardWildEncounter`, and the D-pad direction field. Then `FieldGetPlayerInput` repopulates those fields from `newKeys` and `heldKeys`, but only after consulting runtime state like `gPlayerAvatar.runningState`, `gPlayerAvatar.tileTransitionState`, and whether the current tile behavior is a forced-movement tile. So even before we get to NPCs, signs, warps, or scripts, the input has already been interpreted through avatar state.

This is why, in your reducer lab, I do not want a function that says, “if A then interact.” That is too flat. Your reducer needs a gate in front of the interaction check. For example, suppose the player is halfway between tiles because a walking animation has begun. They press A during that in-between frame. In a naive implementation, you might raycast one tile forward and launch a script. FireRed generally avoids that ambiguity by checking the tile transition state. The semantic A press is only recorded when the avatar is at a stable enough movement state, such as tile center with no forced movement, or not moving. The visible design outcome is simple: the player talks when they are standing, not while the engine is resolving a tile transition.

Now, notice the sophistication here. This is not just polish. It protects the data model. We have already built, in prior lectures, the idea that map events are authored against tile coordinates and elevations. If the player is between tiles, what coordinate should interaction use? The previous tile? The next tile? The facing tile after the movement commits? FireRed’s answer is architectural: don’t let most interactions enter the script lookup phase until the avatar state has become coherent again.

Let’s trace this with a concrete example. Imagine the player is walking north toward a counter in a mart. On the frame where the player reaches tile center, `FieldGetPlayerInput` may set `tookStep` and `checkStandardWildEncounter`, and it may also observe held direction. Then `ProcessPlayerFieldInput` begins its arbitration. It resets facing NPC or signpost variables, gets the player’s facing direction, gets the player’s position with `GetPlayerPosition`, reads metatile attributes and behavior through the map grid, and clears `gFieldInputRecord` before copying over the D-pad direction. At that point, we are no longer talking about raw controls. We are in field policy.

And the policy is ordered. First, trainer attention can win through `CheckForTrainersWantingBattle`. Then an on-frame map script can win through `TryRunOnFrameMapScript`. Then, if the player took a step, step-based consequences can win through `TryStartStepBasedScript`. Only later does A-button interaction get its turn through `TryStartInteractionScript`. This is visible in `pokefirered/src/field_control_avatar.c`, chunk `chunk_4488cb84aee4d6dd`, which is the important field-control branching chunk for today. The order is not incidental. It encodes game rules.

Pause and test yourself here. Suppose the player presses A on the same frame that stepping onto a coordinate event should launch a story script. Which action should win? In FireRed’s actual branch structure, the step-based path is checked before the A-button interaction path. So the coordinate event or warp or step-count script can take control before A-button lookup happens. That is not an input bug. That is the engine preserving authored map control over opportunistic interaction.

Now let’s zoom into `TryStartStepBasedScript`, because it shows why movement state gating is broader than movement. It checks `TryStartCoordEventScript`, then `TryStartWarpEventScript`, then miscellaneous walking scripts, then step-count scripts, and then repel counter behavior under conditions that exclude forced avatar movement and forced movement tiles. That means a step is not just a displacement. A step is a transaction point. It is where the game says, “Now that position has changed cleanly, run the systems that are allowed to react to that change.” Creature happiness, poison, egg hatching, Safari Zone steps, Vs. Seeker charging, repel, wild encounters, warps, and coordinate events all hang off that concept.

This is one of the reasons I keep pushing you to trace data records through globals, callbacks, tasks, scripts, structs, and concrete source functions. A coordinate event begins as map event data. At runtime it is reachable through `gMapHeader.events->coordEvents`. The field-control layer queries it with `GetCoordEventScriptAtPosition`. If the trigger condition passes, `ScriptContext_SetupScript` transfers control to the script VM. The player sees a character stop, a message appear, a battle start, or a warp happen. That line from authored record to visible behavior is the course thesis in miniature.

Now, to connect this back to tile behavior without redoing last week’s tile lecture, the field layer depends on `pokefirered/src/fieldmap.c`, chunk `chunk_eeb0180842f7ece9`, for queries like `MapGridGetMetatileBehaviorAt` and `MapGridGetMetatileAttributeAt`. Field control does not manually decode tiles. It asks fieldmap for behavior and attributes. That is the contract. The control layer then treats behavior as meaning: “is this a forced movement tile,” “is this a counter,” “is this a warp,” “is this a sign,” “is this surfable water,” and so on. So the map grid gives semantic evidence; field control decides game action.

Let’s bring A-button interaction back in. `TryStartInteractionScript` calls `GetInteractionScript`. If no script is found, it returns false. If a script is found, it normally plays the select sound and then calls `ScriptContext_SetupScript`. There is even a small exception for PC scripts, such as `PalletTown_PlayersHouse_2F_EventScript_PC` and `EventScript_PC`, where the interaction sound is skipped. That tiny detail is useful because it reminds us that interaction handling is not merely lookup. It is also presentation policy.

Inside `GetInteractionScript`, the search order is object event, background event, metatile script, then water script. That order is in `pokefirered/src/field_control_avatar.c`, chunk `chunk_79332c416dfb6ac9`, the interaction-handling focus for today. Think about what that means. If an NPC object and a generic metatile affordance both appear to be available, the object event wins. If there is no object but there is a background event, the background event wins. If no authored background event exists, the engine can still fall back to metatile behavior such as PC, bookshelf, mart shelf, television, trash bin, sign, or wireless monitor. If none of those applies, water may provide Surf or Waterfall behavior.

Here is the check for understanding. A player faces a tile that is both visually a bookshelf and has a background event script at that same position. Which script should be returned? The background event script wins, because `GetInteractionScript` checks `GetInteractedBackgroundEventScript` before `GetInteractedMetatileScript`. That is a deliberate override mechanism. Authored map-specific behavior outranks generic tile behavior.

Now make the example more concrete. Hidden items are background events. `GetInteractedBackgroundEventScript` looks up a `BgEvent` at the player-facing map position, checks facing constraints for directional background events, and for hidden items it loads item, flag, and quantity data into special variables like `gSpecialVar_0x8005`, `gSpecialVar_0x8004`, and `gSpecialVar_0x8006`. Then it returns `EventScript_HiddenItemScript`, unless the hidden item flag is already set. So the player-visible sentence, “You found a Potion,” is not hard-coded into input. It is assembled from background event data, event flags, special variables, and a shared script. That is exactly the architecture pattern you have been tested on since week two: data record, runtime global, script dispatch, visible behavior.

Now, let’s compare that to object interaction. `GetInteractedObjectEventScript` first asks for an object event at the target position and elevation with `GetObjectEventIdByPosition`. If the result is empty, it does not immediately give up if the metatile behavior is a counter. Instead, it looks one more tile beyond the counter using `gDirectionToVectors[direction]`. That is the counter case we were discussing a few minutes ago. Once it finds a valid object, it writes `gSelectedObjectEvent`, writes `gSpecialVar_LastTalked` from the object event’s local id, writes `gSpecialVar_Facing`, obtains the object’s script pointer, and then passes that through `GetRamScript`. That last step matters because object behavior may be dynamically replaced by RAM script logic. Again, field control is a mediator, not merely a table lookup.

Let’s pause. If I ask, “Where is the identity of the talked-to NPC stored after interaction lookup?” the answer is not “in the script text.” The immediate runtime identity is stored in globals, especially `gSelectedObjectEvent` and `gSpecialVar_LastTalked`. The script can then use those variables. This is why the script system can be generic while still responding to a particular NPC. The script VM does not need to know how the A button was interpreted. Field control has already converted spatial intent into script state.

Now let’s return to movement state gating, because this is where many clone implementations become brittle. There are at least three places where FireRed refuses or redirects input. The first is semantic input capture in `FieldGetPlayerInput`. The second is branch priority in `ProcessPlayerFieldInput`. The third is script and control locking behavior around active scripts, signpost cancellation, and quest log playback. If you only implement the first, your game will still feel wrong. If you only implement the second, you will get weird mid-step interaction bugs. If you only implement script locking, you will still mis-prioritize warps, coordinate events, and interactions.

Consider `FieldInput_HandleCancelSignpost`. This function is not the normal A-button interaction path. It is a special path for what happens while a message or signpost-related script context is enabled. If the player can walk away to cancel a message box, and the player presses a direction that differs from the current facing direction, the function registers quest log input, sets up `EventScript_CancelMessageBox`, and locks player field controls. If Start is pressed in that same special state, it also sets up cancel behavior and can create `Task_QuestLogPlayback_OpenStartMenu`. So even the apparent act of “pressing away from a sign” is not ordinary movement. It is a script-cancellation gesture routed through script context and tasks.

This is a good place to connect back to the runtime shell, but only briefly. Earlier in the course we studied `AgbMain`, `SetMainCallback2`, task allocation, and `RunTasks`. We are not reopening that machinery today. We are using it. The point is that field control can decide, “start a script,” or “show the Start menu,” or “create a task,” and the engine shell will continue dispatching the appropriate callback and task work over later frames. So the field-control code is not trying to complete every consequence immediately. It often starts the next state machine.

Look at Start and Select in `ProcessPlayerFieldInput`. Start is checked late. If it wins, the engine sets `FLAG_OPENED_START_MENU`, plays `SE_WIN_OPEN`, and calls `ShowStartMenu`. Select is checked after Start and runs `UseRegisteredKeyItemOnField`. But both of these are after trainer detection, on-frame scripts, step consequences, wild encounter checks, arrow warps, walk-into signpost checks, A-button interactions, and door warps. That ordering tells you something subtle about field control: menu access is important, but it is not allowed to interrupt certain map-authored or movement-authored consequences that have already become due.

Here is another check. The player steps onto a warp tile and also presses Start on that frame. What should happen? In FireRed’s ordering, the step-based warp setup gets a chance before Start menu handling. `TryStartWarpEventScript` can call `SetupWarp`, which in turn writes the warp destination using functions such as `SetWarpDestinationToMapWarp` or dynamic-warp handling. The Start menu does not get to override that step consequence. This matches the player’s intuition: if you walk through a door, you go through the door. You do not open the menu inside the doorway transition unless the engine has reached a state where menu input is again allowed.

Now, one advanced detail: `heldDirection` and `heldDirection2` are both present, and they are used in different branches. You do not need to memorize the names as magic. Instead, notice the design pressure. Direction input may need to mean several related things: continuing movement, attempting arrow warps, walking into signposts, and door warps. FireRed stores enough semantic flags to let `ProcessPlayerFieldInput` distinguish these branch opportunities. Your lab reducer should mirror that idea conceptually, even if your names differ. Do not collapse all direction into a single boolean if your game has direction-sensitive warps and direction-sensitive interactions.

Let’s do one full trace, slowly. The player is standing in front of a Pokémon Center sign, facing north. The player holds north, not A. `FieldGetPlayerInput` sees D-pad input and, if movement state permits, marks held direction and records `dpadDirection` as north. `ProcessPlayerFieldInput` gets the player position and current metatile behavior. It first checks higher-priority systems. Then, for certain input states, it gets the position in front of the player through `GetInFrontOfPlayerPosition`, queries `MapGridGetMetatileBehaviorAt`, and calls `TrySetUpWalkIntoSignpostScript`. That function filters out left-right cases, checks the facing signpost type with `GetFacingSignpostType`, and can call `SetUpWalkIntoSignScript`, which writes `gSpecialVar_Facing`, calls `ScriptContext_SetupScript`, and marks the message as a signpost message with `MsgSetSignpost`. The player experiences this as bumping into or facing a sign and getting sign text. Architecturally, it is D-pad input becoming a script without A ever being pressed.

That is the key conceptual upgrade. Interaction is not synonymous with A. Field control owns a wider category: field action. A-button interaction is one field action. Step scripts are field actions. Walk-into signposts are field actions. Arrow warps and door warps are field actions. Start menu and registered item use are field actions. The field-control layer arbitrates among them.

Now, I want you to observe how this connects week six to weeks seven and eight. Week six is about input and avatar field control. Week seven will lean harder into event scripting and story progression. Week eight will consolidate how authored progression is persisted and resumed. This lecture is the bridge. If you understand today’s code, scripts stop looking like mysterious global events. They become the thing field control selects after enough runtime evidence has accumulated: player position, facing direction, elevation, metatile behavior, object event identity, background event record, flags, variables, and current movement state.

Let me give you one more example, this time with water. `GetInteractedWaterScript` is reached only after object, background, and generic metatile interaction fail. It checks fast water with Surf availability, badge and party requirements for Surf, and Waterfall with the relevant badge and surfing-north condition. So if the player presses A facing water, the decision is not “water tile equals Surf.” It is “water affordance plus party capability plus badge progression plus avatar state plus direction.” That is a field-control decision joined to party data and event flags. The creature system is now participating in overworld interaction, but only through a narrow query like `PartyHasMonWithSurf`.

This is also why `pokefirered/src/battle_setup.c`, chunk `chunk_494d132a59422c85`, includes field-facing headers such as `overworld.h`, `script.h`, `event_data.h`, `fieldmap.h`, `field_control_avatar.h`, and `field_player_avatar.h` around battle setup concerns. We are not studying battle setup in detail today, but the include pattern supports the broader course claim: gameplay features are connected through small state and script boundaries rather than through one monolithic “gameplay manager.” Trainers, wild encounters, scripts, field map queries, and avatar control must cooperate at the transition points.

So, recap this portion in one breath. FireRed does not let raw input directly mutate the world. It first normalizes input according to movement state. It then arbitrates possible field actions in a deliberate priority order. It resolves spatial intent through map position, elevation, object events, background events, and metatile behavior. When it finds a winner, it records the right globals and starts a script, task, menu, warp, or encounter path. If it does not find a winner, the input may simply vanish as a non-action for that frame.

That last sentence is important. “No action” is not failure. It is a valid result of field control. Pressing A while nothing interactable is available should not create a script. Holding a direction that does not match the facing-direction requirement for a door warp should not warp. Pressing Start while a higher-priority step script has just become due should not open the menu. A well-architected creature RPG needs this kind of refusal because the world is full of overlapping possibilities.

For the lab, when you write the controller-to-avatar reducer, I want you to annotate which inputs are raw and which are semantic. Then annotate which semantic actions are gated by movement state and which are gated by world state. Movement state answers, “May I ask the question?” World state answers, “What is the answer?” FireRed separates those concerns enough that we can trace them: `FieldGetPlayerInput` is largely about producing semantic input, while `ProcessPlayerFieldInput` is largely about choosing the winning field action. The separation is not perfect in a textbook sense, but it is strong enough to be architecturally useful.

And with that, we are ready to push one level deeper into control ownership. We have looked at when interaction is denied because the avatar is not in the right movement state, and when interaction loses because a higher-priority field action wins. Next, we will look at what happens after a script starts: how field controls become locked, how the script context prevents repeated dispatch, and how the engine eventually hands authority back to the avatar.

So let us stay exactly at that handoff boundary. The player presses A, the input has survived the movement-state gate, the field-control branch has decided that this is not a movement step, not a menu open, not a higher-priority system action, and now we are about to run some interaction script. At that instant, the most important architectural question is no longer, “What did the player press?” It becomes, “Who owns the overworld for the next few frames?”

I want to put that question on the board, because it is easy to miss. Board note: input ownership is not the same thing as avatar ownership. Input ownership means the engine is still sampling buttons every frame, because the hardware state does not stop changing. Avatar ownership means that those button presses are allowed to become walking, turning, talking, menu opening, warping, or script dispatch. FireRed has to keep polling input, because the runtime shell continues to tick. But it must stop letting ordinary field controls dispatch the same event again and again while a script is already active.

That is the bug you would create in your lab reducer if you only wrote, “A button plus facing NPC equals start conversation.” Imagine your reducer runs every frame. The player presses A in front of an NPC. Frame one: start conversation. Frame two: the physical A button may still be down. If your reducer has no ownership state, it starts the same conversation again. Frame three: again. You do not get one dialogue box; you get repeated script entry, corrupted control flow, or a lock because multiple systems believe they are responsible for the same text box. So the real rule is closer to this: A button plus facing NPC plus field controls currently accepting avatar interaction plus script context not already owning execution equals start conversation.

That is why this part of the lecture belongs with field control, not only with scripting. The script engine is not just a content interpreter. It is also part of the control regime of the overworld. The moment a field event dispatches into a script, input is still physically present, but the avatar no longer has normal authority.

Now, connect that back to the source focus. In pokefirered/src/field_control_avatar.c, chunk_636f753078bf545a, our concern is avatar field control: how raw field conditions and player input are reduced into candidate actions. In the same file, chunk_79332c416dfb6ac9 is the player interaction handling area, the portion we are leaning on for A-button event lookup and interaction dispatch. And chunk_4488cb84aee4d6dd is the field-control branching material, where the engine decides which kind of field action wins. Those chunks matter because they prevent us from telling a vague story like, “the game checks the button and runs a script.” No. The game organizes the frame as a decision pipeline, and each stage narrows authority.

Let me give you a concrete example. Suppose the player is standing in front of an NPC in Viridian City. We do not need to re-teach map loading here; we already used the map header, layout, and object event records earlier in the course. Just recall the prior chain. The map data gave us object events. The loaded map runtime has object event instances. The player avatar has a facing direction. The tile and object systems can answer, “What is in front of the player?” Now the player presses A.

The engine does not begin by saying, “NPC conversation.” It begins by asking whether the player avatar is in a state where A-button interaction can be considered. If the avatar is mid-step, locked by a forced movement, sliding, scripted, or otherwise busy, the semantic A-button intent may exist, but it does not become an interaction. That is movement state gating. Then the field-control branch asks whether some other field action has priority. For example, Start may open a menu in a clean idle state, directional input may attempt movement, special tile behavior may trigger a warp or board event, and A may query the facing location. Once the A path wins, the engine queries the world in front of the player: object, signpost, hidden item, special tile behavior, or script-bearing event.

Notice the phrase I just used: “script-bearing event.” That is not identical to “visible NPC.” A visible NPC can have a script. A signpost can have a script. A hidden item can behave like an interaction without presenting itself as a visible character. Some interaction checks are object-centered; others are tile-centered. That is where pokefirered/src/fieldmap.c, chunk_eeb0180842f7ece9, stays relevant for us. We are not re-opening the whole metatile lecture, but the field map and tile behavior support explain why the A button can be interpreted against a tile location, not merely against sprite collision. The player’s gaze is spatial, and the engine turns that gaze into a map query.

Check for understanding here: if I say, “The A button talks to NPCs,” what is missing from that sentence? You should be able to answer: the movement gate, the field-control priority branch, the facing-coordinate calculation, the object or tile lookup, the script dispatch rule, and the post-dispatch lock that prevents the event from being started repeatedly. Good. That whole sentence is what I mean by advanced source tracing. We are no longer satisfied with the player-facing description.

Now let us talk about script context as a lock. In many engines, you would see an explicit high-level enum: GAMEPLAY, DIALOGUE, CUTSCENE, MENU, BATTLE. FireRed has pieces that behave like that, but the ownership is spread across callbacks, tasks, script context state, avatar flags, and specialized helpers. That distributed ownership is one of the main architectural lessons of this course. The runtime is not a neat modern statechart in one file. It is a cooperating set of small state machines.

When an interaction script starts, the script engine will run commands over multiple frames. It may show a message box. It may wait for player acknowledgement. It may move an NPC. It may set a flag. It may start a battle. It may give an item. It may warp the player. Every one of those effects has consequences for field control. While text is open, walking should not happen. While a trainer approach sequence is active, normal A-button interaction should not happen. While a battle transition is being prepared, the overworld should not accept another field action as if nothing occurred.

This is why it is valuable that the research packet points us not only to field_control_avatar.c but also to pokefirered/src/battle_setup.c, chunk_494d132a59422c85. That chunk’s include surface ties battle setup to overworld, task, script, event data, metatile behavior, fieldmap, field_control_avatar, field_player_avatar, and transition systems. The important claim supported there is not “battle setup is field control.” It is subtler: a field interaction can become a battle setup workflow, and the source organization shows that battle entry is wired through the same family of overworld, script, task, and field-control modules we are studying. So when an NPC script starts a trainer battle, the A-button interaction pipeline has effectively handed authority to a different runtime subsystem.

Let us make that more precise. Board cue: I am going to draw five boxes left to right. First box: data records. Second: runtime globals and instances. Third: callback and task frame. Fourth: field-control branch. Fifth: script or subsystem ownership. The NPC’s script pointer, or signpost script, begins as data. Once the map is loaded, the relevant object event or map event exists in runtime-accessible state. During CB2_Overworld, which we already established as the map runtime callback, the task and callback frame keeps the overworld alive. Field control reads the avatar state and input. Then interaction dispatch starts a script. Once that happens, script context and related tasks dominate the next visible behavior.

You see how that fulfills the course’s repeated trace: data table to globals, callbacks, tasks, scripts, structs, concrete source functions, and finally visible behavior. We are not memorizing function names as trivia. We are learning how to follow ownership.

Now, there is an important nuance. Locking field control does not always mean freezing every visible thing. Text may animate. NPCs may keep stepping as part of a script. The camera may move. A battle transition may play. A field effect may run. So do not picture the overworld as “stopped.” Picture normal avatar control as suspended while another state machine is allowed to advance. That difference matters for your architecture design. If you build your clone by pausing the whole world whenever a script starts, you will quickly discover that cutscenes, follower movement, text printers, and transitions need time. A better mental model is cooperative control: the frame continues, but the authority to consume player input changes.

This is one place where FireRed’s task system from earlier weeks becomes relevant again without re-teaching it. We already covered that RunTasks gives per-frame dispatch, and CreateTask allocates small stateful workers; the research packet identifies pokefirered/src/task.c chunks chunk_cb0bb91a147bfb59 and chunk_3ffebc0341225936 for those mechanisms. Today we use that as prior foundation. A script can cause task-driven behavior. A transition can be task-driven. A message box can have its own progression. The avatar does not need to own every frame for the world to remain alive.

Let me pause and ask another check. In your lab reducer, what state variable or condition would you add to avoid repeated A-button dispatch while a message is open? There are several acceptable designs. You might have an interaction lock. You might have a mode field such as FIELD_MODE_SCRIPT. You might have a scriptContextActive predicate. You might gate avatar input on an avatar-control-enabled flag. What would not be acceptable is relying on key-repeat timing alone. Hardware input is not ownership. Button edge detection helps, but it does not replace script control. Even if you dispatch only on a new A press, you still need to prevent the player from walking away or opening the menu while the script expects control.

Now, return to the facing interaction. The A-button lookup is a fascinating example because it reveals a hierarchy of interpretations. The player experiences one verb: inspect or talk. Internally, that verb fans out. In front of the player there might be a person event. There might be a sign. There might be a hidden item. There might be a special tile with behavior. There might be nothing. The control code has to decide not only whether something exists, but which class of thing gets precedence when multiple interpretations are plausible.

For example, suppose the player is facing a counter tile with an NPC behind it. The game may allow talking across the counter. That is not the same as direct collision adjacency on an open tile. The interaction check must reason about facing direction, event object position, and possibly special tile behavior. Or suppose a hidden item sits on the tile in front of the player. The engine must treat the A button as a search action even though there is no visible object. Or suppose the player faces a signpost. The signpost is part of map event data, not a moving object event. The same A button verb crosses several data representations.

That is why, architecturally, “interaction” is a map query plus a dispatch policy. It is not a method on NPC. If you build it as NPC.talk(), you will immediately struggle with signs, hidden items, PCs, bookshelves, water tiles, cut trees, and counters. FireRed’s organization pushes us toward a more durable shape: take the player’s facing coordinate, ask world systems what interactable things occupy or relate to that coordinate, then dispatch the winning script or field action.

Now let us connect this to movement denial, because the symmetry is useful. For movement, directional input asks, “Can I enter that tile?” Collision, object occupancy, elevation, and tile behavior help answer. For A-button interaction, the question is, “Can I address that tile or object?” Some of the same map facts are involved, but the verb is different. A wall blocks movement, but a sign on a wall may accept interaction. A counter blocks movement, but an NPC across the counter may accept interaction. Water blocks walking, but may accept Surf under the correct party and flag conditions. So field control is not merely collision plus scripts. It is verb-sensitive spatial interpretation.

This is also where week 6 sits in the overall course plan. Weeks 1 through 5 gave us shell, data generation, maps, creature data, and battle comparison. Weeks 6 through 8 are the bridge from “the world is loaded” to “the world progresses.” Input is the first half of that bridge. Scripted events are the second half. Today’s field-control material is the hinge. It decides when player intention becomes world mutation.

Let me use an example that escalates into a battle, because it ties several systems together. The player presses A in front of a trainer NPC after defeating the Elite Four, or maybe before, depending on flags. The object event has a script. The script may check event flags or variables. If the battle is available, it starts trainer battle setup. At that point, battle_setup.c becomes relevant. In pokefirered/src/battle_setup.c, chunk_494d132a59422c85, the include list itself is a useful architectural signal: battle setup depends on event data, scripts, overworld, field control, player avatar, field map, and battle transition headers. Again, the point is not that includes prove behavior by themselves. The point is that the source boundary reflects the same cross-system path we observe at runtime: a field interaction can turn into battle entry through script-mediated setup.

If the player has already beaten that trainer, the same A button may produce different dialogue. Same input. Same facing direction. Same object. Different world flags. This is why I said earlier that movement state asks, “May I ask the question?” while world state asks, “What is the answer?” Event flags and variables are part of the answer. The A-button pipeline gets you to the event; the script and event data decide which branch of story state applies.

Board cue: I am adding a fork under “script dispatch.” Left branch: message-only interaction. Right branch: subsystem transition. Message-only interaction returns to field control after the text and script finish. Subsystem transition may replace the current control context with battle, shop, naming screen, party menu, move relearner, or another callback-driven interface. You saw the same architecture in pokefirered/src/learn_move.c, chunk_af3598df6f092723, where the research packet identifies a move relearner state machine and a callback initialization path. We are not studying the move relearner today, but it is a good comparison: a field script can hand the player to a specialized UI state machine, and then later the overworld must be restored.

That comparison should change how you think about field control. Field control is not the whole game. It is the default owner of player intention while standing in the overworld. It is one owner among many. The Start menu, the party menu, the battle engine, the naming screen, the move relearner, the trade system, and link features all have their own control loops or state machines. FireRed’s job is to hand control among them without losing persistent world state.

Now, let us examine the “hand back” side. A script ends. The message closes. The battle finishes. The shop exits. What has to be true before avatar control resumes? First, the active script or subsystem must report completion. Second, any visual transition or cleanup must be done. Third, avatar movement flags must be in a sane state. Fourth, the overworld callback and tasks must again allow field input to be processed normally. Fifth, the input edge state must not accidentally interpret the same physical button hold as a new command.

That last point is subtle. Suppose the player presses A to advance the last line of dialogue. The text box closes. On the very next frame, A may still be held. If field control immediately treats held A as a new interaction, the NPC may start talking again. Games solve this in different ways: require a new press edge, clear input state, impose a short lock, or wait for release. FireRed’s exact handling is distributed, but the architectural requirement is universal. Returning control is not just setting “script active” to false. It is returning to a clean input boundary.

Check for understanding: why does a script lock need to cover both movement and interaction? Because otherwise the player could walk away from a conversation, reopen a menu during a cutscene, or trigger a second event while the first event still owns assumptions about avatar position and map state. The lock protects invariants. It says, “This script is allowed to assume that the world will not be changed by ordinary avatar commands until it yields or finishes.”

Here is a clone-design version of the rule. Your reducer should not return only an action such as MOVE_NORTH or TALK. It should also read a control mode. If control mode is AVATAR_FREE, then input can produce movement, menu, A-button lookup, or registered button behavior. If control mode is SCRIPT_RUNNING, input may be routed to the text system or ignored by avatar movement. If control mode is TRANSITION, input is probably ignored. If control mode is BATTLE or MENU, the overworld reducer is not the owner at all. This is not overengineering; it is the minimum structure required to prevent input from leaking across subsystems.

And because you are advanced learners, I want to state the tradeoff. A single global mode is simple, but real games often need layered locks. You may have script active, message active, player movement frozen, NPC movement allowed, camera script allowed, menu blocked, and battle transition pending. FireRed’s style leans toward cooperating flags, callbacks, tasks, and contexts rather than one pristine enum. That can be harder to reason about, but it also lets small systems own their own frame progression. When reading FireRed, do not demand that all ownership be declared in one place. Trace it through the call path and the state predicates.

Let us return briefly to fieldmap.c, chunk_eeb0180842f7ece9, because tile behavior support gives us the other half of control handoff. A tile can cause a warp, a ledge hop, a current, a door animation, or a special field effect. Some of those happen from movement, not A-button interaction. But after they trigger, the same ownership rule applies: normal field controls must yield until the triggered behavior has finished or transferred the player to a new map state. We covered warp destination state and LoadMapFromWarp earlier, so I am not going to re-teach that. The forward connection is this: whether the trigger came from stepping onto a tile or pressing A toward an event, the engine must convert a local field decision into broader control ownership.

So at this point in week 6, we can name the recurring pattern. Player input is sampled every frame, but semantic input is conditionally accepted. Accepted input is interpreted against avatar state and map state. The winning field action either mutates the avatar immediately, such as turning or stepping, or dispatches to another state machine, such as script, transition, menu, or battle. That state machine holds control until it reaches a return boundary. Then field control resumes.

Let me do a quick local recap before we move on. We began this segment at the moment after an interaction starts. We asked who owns the overworld. We saw that script context acts like a control lock, not because the hardware stops polling, but because ordinary avatar interpretation must be suspended. We tied A-button lookup to object events, signposts, hidden items, and tile behavior, using pokefirered/src/field_control_avatar.c chunks chunk_636f753078bf545a, chunk_79332c416dfb6ac9, and chunk_4488cb84aee4d6dd as the source focus for avatar control, interaction handling, and field-control branching. We kept pokefirered/src/fieldmap.c chunk_eeb0180842f7ece9 in view for tile behavior support. And we used pokefirered/src/battle_setup.c chunk_494d132a59422c85 to show that field interaction can hand off into battle setup through script and event systems.

Now, the next step is to sharpen our notion of priority. We have said that higher-priority field actions can win before A-button lookup, and that scripts can own control after dispatch. But there is another question still sitting underneath that: when two possible interpretations are both valid, how does the engine choose one? That is where field control becomes policy, not just plumbing. In the next stretch, we will look at priority ordering as a design surface: why menu opening, movement attempts, forced movement, tile triggers, object interaction, and script dispatch cannot be treated as independent listeners all competing freely on the same frame.

So, suppose we draw the frame as a reducer. On the board I am writing: raw keys come in, field input flags are derived, then policy consumes those flags in an ordered sequence, and the first successful owner returns control. That is a different mental model from “the A button listener fires” or “the grass listener fires.” FireRed is not running a little public meeting where every subsystem gets to vote. It is running a narrow, deterministic adjudication path.

You can see that directly in pokefirered/src/field_control_avatar.c chunk_636f753078bf545a, where FieldGetPlayerInput does not immediately perform actions. It records facts about the frame: pressedAButton, pressedStartButton, heldDirection, tookStep, checkStandardWildEncounter, dpadDirection, and so on. That is already a design decision. The engine first translates hardware input plus avatar state into a field-specific input record. Only after that does another layer decide what the record means.

Now look at the policy layer in pokefirered/src/field_control_avatar.c chunk_4488cb84aee4d6dd. ProcessPlayerFieldInput begins by resetting facing NPC or signpost variables, reading player direction, reading player position, and querying the current tile behavior and tile attributes through fieldmap support. That is where our previous lecture on tiles becomes a dependency, not a separate topic. We are not re-teaching MapGrid here, but remember the crucial point from pokefirered/src/fieldmap.c chunk_eeb0180842f7ece9: the overworld does not treat the map as artwork. It treats the map as queryable behavioral data. ProcessPlayerFieldInput asks, “Where is the player? What behavior is under them? What attributes are attached to that block?” And then it runs a priority list.

Let’s make this concrete. Imagine the player has just stepped onto tall grass, is facing north, and is holding up. On that same frame, several interpretations might seem plausible. They took a step, so maybe step counters should increment. They are on grass, so maybe a wild encounter should be checked. They are holding a direction, so maybe movement or an arrow warp should be considered. If there is a signpost or ledge-like behavior in front of them, maybe a field script should trigger. If A was also pressed, maybe object interaction should be considered. If this were implemented as independent listeners, you would have nondeterministic or hard-to-reason-about behavior. FireRed avoids that by putting the interpretations in a sequence.

Notice the ordering. In ProcessPlayerFieldInput, trainer battle checks and on-frame map scripts are considered before ordinary step handling. That is not incidental. A trainer spotting you must be able to seize field control before you casually open a menu or continue movement interpretation. An on-frame script is another high-priority map-owned policy hook. Only after those does the function handle tookStep: game stat increments, Wonder News step counting, renewable hidden item counters, massage cooldown, Resort Gorgeous step counting, Birth Island rock step counting, and then step-based scripts. That sequence tells you something important about the architecture. “Taking a step” is not just animation. It is a commit point for multiple systems.

Check for understanding here: if a student says, “Wild encounters happen because the player presses a direction,” what is missing from that answer? Right, the commit boundary. The direction may initiate movement, but the encounter check is tied to the avatar reaching the tile center and the field input record saying checkStandardWildEncounter. That detail comes out of FieldGetPlayerInput in pokefirered/src/field_control_avatar.c chunk_636f753078bf545a, where tileTransitionState and forced movement determine whether tookStep and checkStandardWildEncounter are even set. In other words, the encounter system is not listening to the D-pad directly. It is listening to field-control facts after movement state has been normalized.

Now, let’s slow down on the phrase “tile center,” because this is one of those architecture details that separates a smooth overworld from a glitchy one. The avatar can be between tiles during movement. If you allow A-button interactions, Start menu opening, wild encounters, and warp triggers at arbitrary sub-tile positions, you create ambiguity. What tile is the player on? What tile is in front? Has a step happened yet? FireRed’s field input logic uses gPlayerAvatar.tileTransitionState and runningState to gate these flags. That is a movement-state gate. The game is saying, “Only certain field actions are meaningful at stable positions.” Advanced takeaway: this is not only collision prevention; it is semantic stabilization.

On the board, under “semantic stabilization,” I’m writing three phrases: current tile, facing tile, committed step. Current tile is used for behaviors under the player, like grass or warps. Facing tile is used for A-button interaction and certain signpost logic. Committed step is used for counters and step-based triggers. These are three different coordinate questions. Do not merge them in your own lab reducer. If you merge them, you will eventually create a bug where talking to a counter NPC behaves like standing on the counter, or where a wild encounter rolls before the player actually arrives on the grass tile.

The A-button path is particularly good evidence. In pokefirered/src/field_control_avatar.c chunk_79332c416dfb6ac9, TryStartInteractionScript delegates to GetInteractionScript. And GetInteractionScript itself checks categories in order: interacted object event script first, then background event script, then metatile script, then water script. Again, policy is embedded in ordering. The engine does not ask every possible target to respond and then merge the results. It asks, “Is there an object event interaction?” If yes, that wins. If no, it asks about background events. If no, it asks about metatile behavior. If no, it asks about water.

Why should object events win over background events? Think like a player. If an NPC is standing in front of a sign and you press A, you expect to talk to the NPC, not read the sign through their body. If a person is across a counter, you expect the counter logic to extend the interaction position to the object on the other side. That counter case is exactly why the object-event interaction code has to understand metatile behavior, not just object positions. The object layer and tile layer are not isolated. The tile behavior can modify how object lookup works.

Let me give you a more architectural example. In many engines, an object interaction system might take a ray cast from the avatar and return the first entity. FireRed does not quite have a modern physics ray cast, but it has a domain-specific equivalent: get the coordinate in front of the player, adjust elevation rules, inspect object events, inspect background events, inspect metatile behavior, and dispatch a script pointer. This is not a generic engine. It is a creature RPG overworld engine, and its lookup order reflects the expected affordances of towns, signs, counters, water, PCs, marts, centers, doors, and NPCs.

Now connect that to the script system. TryStartInteractionScript does not itself execute the whole conversation. It finds a script and calls ScriptContext_SetupScript. That call is the handoff from field control into story progression. This is the same course pattern we have been tracing since week 1: data records and globals feed callbacks and tasks, which dispatch scripts or state machines, which produce visible behavior. Here, the data record may be an object event in map data or a background event in map data. The globals include player position, direction, selected object event, and special variables like the last talked local ID. The callback shell is still the overworld frame. The script context becomes the owner after dispatch.

This is why the research packet’s course thesis keeps using the phrase “cooperating state machines plus data tables.” It is not decorative language. FireRed’s interaction path is literally that. The map contributes data. The avatar contributes state. The field-control function contributes ordered policy. The script VM receives the chosen script. And if that script leads to battle, the battle setup module becomes the next owner. We saw that bridge in pokefirered/src/battle_setup.c chunk_494d132a59422c85, where battle setup sits among dependencies like event data, script utilities, overworld, field control avatar, field player avatar, wild encounter, trainer systems, and battle transition. That include cluster is not proof of every runtime edge by itself, but it is strong architectural evidence that battle initiation is not a standalone battle-menu feature. It is integrated with overworld events.

Now, I want you to hear the word “priority” in two senses. First, there is temporal priority: what does the engine check first on this frame? Second, there is ownership priority: once a system successfully claims the frame, who owns control next? When CheckForTrainersWantingBattle returns true, ordinary field input does not continue. When TryRunOnFrameMapScript returns true, ordinary input does not continue. When TryStartStepBasedScript succeeds, the function records that the step caused control transfer and returns. These early returns are not just C style. They are control ownership.

Pause and test yourself. If Start is pressed on the same frame that a trainer sees the player, should the Start menu open? No. The field-control priority says trainer detection wins first. If A is pressed while a step-based script has just been triggered, should the interaction script also run? No. The step-based script claimed the frame. If Select is pressed for a registered item, but an A-button interaction already dispatched a script earlier in the order, should Select also fire? No. The reducer returns after the successful interaction. That is the whole point of reducing many possible observations to one chosen field action.

And this is also why the lab this week asks you to write a controller-to-avatar reducer, not just a movement function. If you return “move north” from raw up input, you have solved only the smallest slice. Your reducer needs to say, “Given input flags, avatar movement state, tile behavior, object event lookup, and script locks, what single field action, if any, owns this frame?” For an advanced implementation, I would expect your return type to distinguish at least no action, movement attempt, collision rejection, warp trigger, object interaction, background interaction, metatile interaction, wild encounter check, step script, and menu dispatch. You do not need FireRed’s exact names, but you need FireRed’s separation of concerns.

Let’s pull one subtle issue into the open: the input record is also used as memory of what action caused the handoff. ProcessPlayerFieldInput clears gFieldInputRecord, then copies dpadDirection, and later sets fields like tookStep, checkStandardWildEncounter, heldDirection, pressedAButton, pressedStartButton, or pressedSelectButton when a corresponding action succeeds. That means the global record is not merely “what buttons were down.” It becomes a record of the accepted field action. This is a useful distinction. Raw input says what the player asked for. Field input says what the field engine accepted as meaningful.

This matters for replay, quest logging, and cancellation behavior. The file includes Quest Log input override logic and a FieldInput_HandleCancelSignpost path. You do not need to memorize every quest log branch today, but notice the architecture: the game can substitute registered quest log input into the same field-control reducer. That is powerful because playback does not need to reinvent every interaction rule. It feeds controlled inputs through the same policy path, while respecting script context and field-control locks.

Here is a concrete example. A message box is open from a signpost. The player presses a direction away from the facing direction. FieldInput_HandleCancelSignpost can set up EventScript_CancelMessageBox and lock player field controls. If Start is pressed, it can set up cancellation and then create a task to open the Start menu after controls unlock. That task behavior ties back to our earlier runtime-shell material: tasks are per-frame continuations. We studied CreateTask and RunTasks in pokefirered/src/task.c chunk_cb0bb91a147bfb59 and chunk_3ffebc0341225936 earlier in the course. Here that machinery is not abstract. It solves a precise sequencing problem: cancel the message first, wait until field controls are available, then open the menu. You cannot do that cleanly if every button directly performs its final action immediately.

Let’s check understanding with a scenario. You are implementing your clone, and a student writes this rule: “If Start is pressed, open the menu, unless a script is active.” Is that enough? Not quite. It misses the signpost cancellation case, where Start during a cancellable message can be intentionally converted into a script cancellation plus deferred menu task. It also misses forced movement and fastest speed gating from FieldGetPlayerInput. So the better rule is not “Start opens menu.” The better rule is “Start may be admitted into the field input record only under movement and control conditions, then ProcessPlayerFieldInput may dispatch menu behavior if higher-priority field actions have not claimed the frame, with special script-context cancellation logic handled separately.” That is a mouthful, but it is the accurate mental model.

Now, about forced movement. Forced movement tiles are a perfect example of policy overriding ordinary agency. If the current metatile behavior is a forced movement tile, FieldGetPlayerInput suppresses some ordinary input interpretations. The engine is essentially saying, “The field, not the player, owns the avatar’s immediate motion.” This is how ice, currents, arrows, or other forced traversal behaviors remain coherent. If the player could open menus, turn, interact, and roll encounters at every sub-step during forced movement, the authored map behavior would collapse. Again, field control is not just plumbing. It is the enforcement layer for authored world rules.

Let me connect this to week 6 as a whole. In the first lecture this week, we traced input into avatar movement attempt flow. We were mostly concerned with how held direction becomes facing, stepping, collision rejection, and movement state. Today, we are adding the next layer: movement is only one possible interpretation of input. The field controller must arbitrate movement against script triggers, warps, interactions, menus, registered items, wild encounters, and forced states. That is why this week sits at the beginning of the “Input, Events, and Story Progression” module. If you do not understand this reducer, scripts look magical. If you do understand it, scripts become ordinary consequences of field policy.

A good way to design this in your own engine is to separate sampling from adjudication. Sampling asks, “What did the controller do this frame, and what stable avatar facts are true?” Adjudication asks, “Given those facts and the map, what single action wins?” Execution asks, “How do we transfer ownership to movement, script, battle, menu, or warp systems?” FireRed’s FieldGetPlayerInput, ProcessPlayerFieldInput, and interaction lookup path map very naturally onto those three responsibilities, even though the code is written in C and uses globals rather than modern dependency injection.

Let’s put a small table on the imaginary board, but I’ll speak it rather than make you copy a formal table. Raw A button is just hardware. pressedAButton in the FieldInput struct is an admitted field request. TryStartInteractionScript succeeding is a selected field action. ScriptContext_SetupScript is ownership transfer. The player seeing text, a shop menu, a PC interface, or a battle transition is visible behavior. That is the trace you should be able to narrate on the quiz: hardware, field input record, map and object queries, priority branch, script setup, task or callback continuation, visible outcome.

One more nuance before we move forward. The A-button lookup uses both position and direction, but position is not always just the tile immediately in front in a naive sense. GetInFrontOfPlayerPosition accounts for elevation rules. Object lookup also has special counter behavior. This is the sort of domain-specific detail that makes the architecture feel “Pokémon-like.” A clerk behind a counter is not on the same tile you are facing in a collision sense, but socially and interactively, they are reachable. The engine encodes that affordance through metatile behavior checks during interaction lookup. So when we say tiles are data, we mean they are also interaction grammar. A counter tile changes the meaning of pressing A.

That is a major design lesson: a tile behavior can participate in collision, movement, warps, encounters, and interaction reach. If your clone stores these as five unrelated boolean systems, you will probably duplicate edge cases. If instead you treat metatile behavior as a compact semantic tag queried by several subsystems, you get closer to FireRed’s architecture. The fieldmap support in pokefirered/src/fieldmap.c chunk_eeb0180842f7ece9 is the lower-level service; the field-control branches in pokefirered/src/field_control_avatar.c chunk_4488cb84aee4d6dd are the policy layer that decides how to use those answers.

Let me recap this segment before we extend it into field locks and script ownership. We sharpened priority from a vague idea into a reducer model. FieldGetPlayerInput admits only certain requests based on avatar state, tile transition state, forced movement, quest log playback, and buttons. ProcessPlayerFieldInput then orders possible meanings: trainer sight, on-frame scripts, step consequences, signpost and encounter logic, arrow warps, A-button interactions, door warps, Start menu, registered key items. Interaction lookup has its own internal priority: object events, background events, metatile scripts, water scripts. And every successful branch transfers ownership by returning early, often after setting up a script, menu, warp, battle, or deferred task.

Keep that reducer model active now, because the next question is what happens after ownership transfers. Once a script owns the field, normal input does not simply disappear; it is filtered, sometimes repurposed, sometimes deferred, and sometimes recorded. That is where field-control locks, script context, and tasks start cooperating in a way that looks small in code but is central to making the overworld feel stable.

Once a script owns the field, I want you to think of the player as still physically present, but no longer the highest-priority author of the next frame. That distinction matters. The avatar is not gone. The object events are not gone. The map is not gone. CB2_Overworld is still the broad runtime callback, and the task system is still dispatching per-frame work. But the meaning of input changes because ownership has moved from free exploration to a controlled interaction.

Let me put that on the board as a phrase: ownership is not the same as existence. The field still exists; free control is what has been suspended.

This is why FireRed’s interaction code is architecturally more interesting than it first appears. If you only read the A-button branch as “talk to NPC,” you miss the more general pattern. The branch is a gate into another state machine. Sometimes that state machine is a script. Sometimes it is a message box. Sometimes it is a warp setup. Sometimes it is a battle transition. Sometimes it is a menu. And sometimes, especially with field effects and deferred movement, it is a task that will keep running after the initiating input frame is long gone.

So, imagine the player standing in front of an NPC. The player presses A. In the reducer model from the last section, that input is first checked against field conditions: are we allowed to act, is movement already committed, is a forced state active, is a script already blocking control, and so on. Then the interaction lookup asks, “What is in front of the player?” The internal ordering matters: object event first, then background event, then metatile interaction, then water-specific behavior. The source focus for this is pokefirered/src/field_control_avatar.c, chunk_79332c416dfb6ac9, which is the interaction-handling chunk we have been using today. That chunk is important because it shows the A-button as a dispatch point, not as a behavior by itself.

Now, if the thing in front is an object event, the field system does not continue scanning for a signpost underneath that object, or a hidden metatile behavior behind it, as if all possibilities were equal. It claims the interaction. The object event wins because a visible person or object has semantic priority. And that is not merely game design taste; it is a consistency rule. If the player sees an NPC and presses A, the architecture must not surprise them by reading the tile under the NPC instead. So the priority order is part of the game’s feel.

Check your understanding here: if a player faces a counter tile that has a background event, but an NPC object is also occupying the interaction position, which layer should own the A press? The object event. Good. And why? Because player expectation follows visible interactables first, and the reducer’s early-return structure encodes that expectation.

Now let us continue past the moment of lookup. Suppose the object event has a script pointer. The field-control code does not itself print every line of dialogue, branch on every flag, give every item, or start every battle. That would make field_control_avatar.c an impossible god file. Instead, it identifies the interaction and hands off to the script machinery. This is the same course theme again: data record, global state, callback or task, script dispatch, visible result. The map object is data. Its local id, coordinates, graphics id, trainer type, movement type, and script pointer are records loaded from map data. The runtime object event is held in global/object-event state. The avatar-facing query finds the relevant record. Then a script context takes over.

That handoff is the bridge between week 6 and the weeks immediately ahead. We are in the Input, Events, and Story Progression module now. Today is still about input and field control, but what we are really building toward is script ownership of progression. Next week, when we talk more deeply about scripts and event data, this same A-button branch will become our entry point. The player presses one button; the engine may read a map script, check a flag, set a variable, queue text, give an item, start a battle, or change a map state. The button is small. The ownership transfer is large.

Let me draw a small frame timeline. Frame N: buttons are sampled. Frame N: ProcessPlayerFieldInput interprets the sample. Frame N: an object interaction is found. Frame N or N plus one: script context is started. After that, frames continue, but normal movement input is gated. So if the player keeps holding a direction while the NPC dialogue begins, the avatar should not slide away during the textbox. The held input is real electrically, but it is not accepted semantically.

That word, semantically, is doing work. The engine does not need to pretend the button is unpressed. It needs to decide that the button does not currently mean “walk north.” Under script or message control, a button might mean “advance text,” “choose a menu option,” or “confirm a yes/no box.” This is why input polling and input meaning are separate layers. We saw the polling foundation in the previous lecture, and now we are treating interpretation as ownership-sensitive.

The same idea appears when the interaction is not an NPC, but a signpost or background event. A sign might not create a visible object event at all. It may be encoded in map event data as a background event, and when the player presses A while facing the correct coordinate, the interaction handler dispatches that script. Again, field_control_avatar.c, chunk_79332c416dfb6ac9, supports the claim that interaction handling is layered by lookup type. The visible behavior is simple: “The sign says something.” The architecture is more precise: facing coordinate plus direction plus background event lookup plus script dispatch plus field-control lock plus message-box progression.

Now, the metatile branch is a little different, and this is where we connect back to last week without re-teaching it. We already covered metatile behavior queries and collision checks in fieldmap.c, especially the tile-behavior support noted in pokefirered/src/fieldmap.c, chunk_eeb0180842f7ece9. Today, I want you to focus not on how the tile behavior value is obtained, but on how the result participates in interaction control. A tile is not just drawn art. A tile can answer questions like, “Am I a PC?” “Am I a bookcase?” “Am I a television?” “Am I water?” “Am I a door?” The field-control layer asks these questions only when the higher-priority interaction layers have not already claimed the input.

So if there is no object event and no background event, the metatile script can become the owner. That is how the same A button can talk to a person, read a sign, inspect a bookshelf, surf on water, or trigger a special behavior. You are not looking at five unrelated input systems. You are looking at one ordered interpretation pipeline.

Here is a concrete example. The player stands in front of water and presses A. The water is not an NPC. It probably is not a background event in the ordinary signpost sense. So the lookup reaches the metatile or water-interaction branch. If the party has the right move access and the game state permits it, the interaction can dispatch the surf flow. That flow is not “move one tile.” It changes avatar state, field movement mode, and often starts a script or field effect sequence. So one A press has become a mode transition. The avatar may become mounted on a surf object or use a different movement state. The system that normally rejects movement into water now has a controlled path for entering water because a different interaction branch owns the transition.

Pause there. Why is Surf not just treated as ordinary directional movement into a water tile? Because movement collision and intentional interaction are different meanings. Walking into water without permission should be rejected. Pressing A on water with the right conditions should start a controlled state transition. Same map coordinate, different input meaning, different ownership branch.

This is the key to designing your lab reducer. Do not write your reducer as “if tile passable, move; else fail.” That is too shallow. You need a representation that can say “blocked for walking, but eligible for interaction,” or “not passable now, but dispatches a warp,” or “input consumed by active script,” or “button repurposed by menu.” FireRed’s organization pushes you toward this because field_control_avatar.c is not merely doing collision; it is choosing between mutually exclusive interpretations of the player’s intention.

Now let’s talk about gating. Movement state gating is the part students often under-model. They implement the happy path, where a direction key causes a movement attempt, and an A button causes an interaction. But the overworld is full of times when those inputs must be ignored or delayed: the avatar is already stepping, a forced movement is active, a tile transition is being resolved, a warp is in progress, a script is running, a menu is open, a message box is waiting, quest log playback is controlling behavior, or the game is waiting for an object event to finish turning.

In FireRed, you see this as a cluster of checks around field control and avatar state. The source focus includes pokefirered/src/field_control_avatar.c, chunk_636f753078bf545a for avatar field control and chunk_4488cb84aee4d6dd for field-control branching. Those chunks are valuable because they show that the input layer is guarded by runtime state. The game asks not only “what did the player press?” but “is this a frame where player control is meaningful?”

Let’s make that more concrete. When you press A to talk to an NPC, the NPC often turns to face you before dialogue appears. That turn may be a small movement action. During that tiny interval, the player cannot also begin walking away, open the Start menu, and trigger another A-button event. The field feels stable because the engine serializes ownership. It says, in effect, “This interaction has begun; finish its setup before accepting a new field command.”

And notice what that gives designers. Designers can author scripts under the assumption that the player is still in front of the NPC when the dialogue begins. They do not have to defensively script around the player escaping between the press and the first text line. Architecture protects content authoring. That is one of the deepest themes of this course: good runtime control makes data-driven content safe to write.

Now, let me connect this to trainer sight, because it is a sibling case. Trainer sight is not an A-button interaction. It is often an automatic interaction triggered by position, direction, and trainer range. But it participates in the same ownership economy. The player steps into a watched tile; the system detects trainer attention; ordinary control is suspended; the trainer may emote, walk toward the player, start dialogue, and then start battle. The research packet points us to pokefirered/src/battle_setup.c, chunk_494d132a59422c85, which includes battle setup dependencies on overworld, script, event data, field control avatar, field player avatar, battle transition, and related modules. That include pattern is not just trivia. It tells us battle entry is not isolated from the field. Battle setup is downstream of overworld event detection and script control.

So when a trainer sees you, the architecture path is not “battle engine randomly starts.” It is field state detects condition, field control transfers ownership, script or battle setup coordinates the transition, the battle transition system takes over visual flow, and only then does the battle runtime become the primary owner. That is why battle setup includes field and script modules in the first place. It sits at the boundary between walking on a map and entering a turn-based encounter.

Now I want to do a quick comprehension check. Suppose the player presses Start on the same frame that an automatic trainer-sight event becomes eligible. Which should win in a stable overworld reducer? The automatic field event should usually win, because the player has stepped into a state where the map is asserting control. If the Start menu opened first, the player could use menu timing to interrupt trainer engagement. Now, actual games may have exact frame nuances, but architecturally the priority is clear: once a field event claims the frame, menu input should not compete with it.

This is why the ordering inside ProcessPlayerFieldInput is not cosmetic. It encodes design law. Trainer sight and script-triggering checks appear before lower-priority voluntary conveniences like Start menu or registered item behavior. The player’s input does not have equal authority in every field condition. That is not unfair; it is the mechanism that makes the map coherent.

Let’s revisit early returns. In ordinary application code, early returns can be merely a style preference. Here, they are semantic. An early return says, “This frame has an owner.” If a warp branch succeeds, do not also open the Start menu. If an A-button interaction succeeds, do not also check the registered key item. If a step consequence is active, do not also treat the held button as a fresh free movement command. Early return is a control-transfer marker.

For your lab, this means your reducer should probably return a result type richer than a boolean. Do not return true or false for “handled.” Return something like movement attempt, collision rejection, script dispatch, warp dispatch, menu open, registered item action, interaction dispatch, or no-op. In a production engine, those would become calls into tasks, callbacks, scripts, or state transitions. In the lab, the return value is your proof that you understand the architecture.

Now, because you are advanced students, I also want to point out a subtle design consequence. The field-control layer must be deterministic enough for the player to build muscle memory. If two interactions are possible, the same one should win every time under the same conditions. That means the lookup priority cannot be an accidental result of array order unless that array order is itself part of the contract. FireRed’s explicit branch structure gives you a stable decision tree. Object event before background event before metatile behavior before water script is a contract players learn without ever seeing code.

Think about standing near a door. A door can be represented by tile behavior, warp metadata, scripted animation, and sometimes object-like visual state. If the player steps onto a warp tile, the movement/step consequence owns the transition. If the player presses A facing a closed door, a door warp branch may own the transition. The same physical doorway participates in multiple interaction forms depending on position and input. That is why field-control branching, the chunk_4488cb84aee4d6dd material in pokefirered/src/field_control_avatar.c, belongs in this lecture rather than only in a map-loading lecture. Door behavior is not just data. It is data interpreted through control state.

Let’s now connect this to global variables and structs, because our course objective is not merely to describe visible gameplay. We are tracing data records through globals, callbacks, tasks, scripts, structs, and concrete source functions. The map event data begins as structured records associated with the current map. During map load, which we covered earlier, that data is made available to the overworld runtime. Object events become runtime object event structures. The avatar has its own state, including facing direction and movement status. Field-control functions read those runtime structures. When a branch succeeds, the result is usually written into another runtime owner: script context, warp destination state, task data, battle setup state, or menu state.

So the A button is not a direct call to “do content.” It is a bridge across runtime representations. Coordinate state plus facing direction selects a candidate. Candidate data selects a script or behavior. The selected behavior mutates global state or creates a task. The callback/task shell keeps ticking until the new owner finishes. Then field control is released and the avatar can again interpret input as free exploration.

That last release step is essential. A lock without a release is a softlock. A script that starts but never returns field control freezes exploration. A task that waits on an animation but never observes completion traps the player. That is why you should think of field locks as contracts with exit conditions. Every branch that takes ownership needs a path back to a stable field state, unless it intentionally transitions to another callback such as battle, menu, or title-like flow.

We have seen this pattern in other modules too. The move relearner source, pokefirered/src/learn_move.c, chunk_af3598df6f092723, is described in the packet as a move relearner state machine with callback and menu-state flow. That is not today’s field-control source, but it is a useful comparison. The player enters a specialized UI, input is repurposed for menu navigation, and state advances through named phases. FireRed repeatedly uses small state machines to own input temporarily. Field control is simply the overworld-facing version of that broader architecture.

And later, when we return to battle, the same comparison will help. Battle input is not field input. Move selection, target selection, item use, and switching all reinterpret buttons under a different owner. Pokemon Showdown, in the research packet, is useful as a secondary comparison for deterministic battle data hooks and regression tests, but here FireRed remains our primary source. We are not replacing the overworld architecture with a simulator model. We are observing a shared principle: input becomes meaningful only inside a current state machine.

Now let’s do a final board pass for today’s complete flow. I’ll say it aloud as a trace.

Frame begins under CB2_Overworld, our established map runtime callback. Tasks run. Field state is updated. Input is sampled. The avatar-control layer checks whether free control is allowed. If movement is already committed, or a script owns the field, or a forced state is active, normal exploration input is gated. If free control is allowed, ProcessPlayerFieldInput interprets the frame in priority order. Automatic field consequences, trainer sight, step scripts, tile transitions, warps, A-button interactions, Start menu, and registered item actions compete through ordered checks. The first successful branch owns the frame and returns early. Ownership then passes to a script, task, menu, warp, battle transition, or movement action. That owner runs until it either restores field control or transitions to a new major runtime.

That is the reducer model, but expressed in FireRed terms.

One more check for understanding. If your clone engine has a bug where pressing A on a sign sometimes opens the Start menu instead, what architectural mistake should you suspect? You should suspect that the reducer is not treating successful interaction dispatch as ownership transfer. Maybe it continues evaluating lower-priority inputs after the sign interaction. Maybe it uses unordered checks. Maybe Start is handled globally before field ownership is decided. The fix is not “special-case signs.” The fix is to restore the decision hierarchy.

Another one. If pressing A on water with Surf available only produces a collision bump, what did you probably collapse? You collapsed movement collision and interaction eligibility into one passability check. The tile can be blocked for walking but valid for an A-button water interaction. The field-control layer needs both questions.

Now, to connect this back to the whole course plan: weeks 1 through 5 gave us the machine. Startup, callbacks, tasks, generated data, map headers, layouts, block access, tile behavior, object events, battle data, and data-driven comparison points. Week 6 is where the machine starts feeling like a game under the player’s thumb. Input is the surface, but control ownership is the architecture. Weeks 7 and 8 will keep following that ownership into story progression: scripts, flags, variables, events, and the ways the game remembers what has changed.

So, when you work on the lab reducer, do not try to imitate every FireRed helper name. I care more that your model preserves the architectural contracts. Polling is separate from interpretation. Interpretation is gated by movement and script state. Interactions are prioritized. A successful branch transfers ownership. The new owner has an exit path. And every player-visible behavior can be traced back through data records, runtime globals or structs, callbacks, tasks, scripts, and concrete functions.

That is the synthesis for today. Field control is not a pile of input if-statements. It is the overworld’s arbitration system. It decides when the player is driving, when the map is driving, when a script is driving, when a task is driving, and when control has left the field entirely for a menu, warp, or battle. Once you see that, the FireRed overworld stops looking like a collection of special cases and starts looking like a disciplined set of cooperating state machines.

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