# Collision, Camera, and Object Events

Lecture ID: lecture_w05_d02
Week 5, Day 2

## Objectives
- Decompose a complete creature-collector RPG into runtime shell, overworld maps, event scripting, creature data, battle, inventory, UI, save, and link systems.
- Connect week 5 material to the overall course plan.

## Transcript
All right, let’s pick up from where we left off, not by restarting the map system, but by tightening the loop around the part that actually makes the overworld feel playable. Last time, we treated tiles as runtime queries. That phrase matters. We are no longer thinking of the map as a picture. We are thinking of the map as a compact database that the engine interrogates every frame: what block is under this coordinate, what behavior does that block advertise, does it allow entry, does it cause a side effect, and how should the camera reinterpret the same coordinate for drawing?

So today, part one of our lecture is going to sit right at the intersection of collision, camera, and object events. If week three gave us the callback and task shell, and week four gave us the loaded map as a structured runtime state, then week five is where the loaded map starts pushing back. The player presses the D-pad, and the world says, “Yes, you may enter that cell,” or, “No, there is a wall there,” or, “You can enter, but this tile behaves like tall grass,” or, “You cannot enter because an object event is occupying it.” That last phrase is important because collision is not only terrain collision. In a creature-collector RPG like FireRed, collision is a composite answer.

Let me put a little board sketch up. Imagine I draw a grid, say ten columns by eight rows. I mark the player at coordinate 4, 3. To the right, at 5, 3, I draw a tree tile. Above, at 4, 2, I draw an NPC. Below, at 4, 4, I draw tall grass. Now, if the player attempts to move right, the terrain system should block. If the player attempts to move up, the object-event system should block, unless the NPC is moving away or has a special pass-through condition. If the player attempts to move down, collision should allow the movement, but tile behavior should say, “This is grass, so now overworld movement may feed into wild encounter logic.” That is the model I want you to keep in your head for the first half hour: one movement attempt, several queries, one final decision.

The FireRed source organization reinforces that separation. The week packet points us to pokefirered/src/fieldmap.c, especially chunk_642df7a31e75f54c for fieldmap runtime support, chunk_eeb0180842f7ece9 for map block and tile behavior flow, and chunk_77ef7f3f8bff9777 for helper flow. Those chunks matter because they show the map layer doing service work. It is not the battle engine. It is not the party model. It is not the script VM. It is the map runtime answering questions for other systems. Then the packet calls out pokefirered/src/event_object_movement.c as the high-signal source for object-event movement claims, and that is exactly the boundary we care about today: fieldmap answers questions about blocks and tile behavior; object movement code participates in whether a creature, NPC, or player-shaped event can occupy or traverse a coordinate.

Now, quick check for understanding. If I ask you, “Is a tile passable?” what is the advanced answer? It is not simply “look up a collision bit.” The better answer is, “Passability is the result of querying the map block at a coordinate, interpreting its metatile behavior, considering elevation and movement rules where applicable, and then checking dynamic occupancy from object events.” Good. That distinction is the difference between a toy tile renderer and an RPG overworld runtime.

Let’s connect that to the overall course architecture. In week one, we decomposed the whole game into runtime shell, overworld maps, event scripting, creature data, battle, inventory, UI, save, and link systems. We are still inside the overworld maps box, but notice how porous that box is. Collision can trigger script. Tile behavior can trigger battle setup. Object events can become conversation targets. Camera movement can determine what graphics need to be copied into VRAM. And save state remembers where the player is standing. So when I say “fieldmap,” do not hear “just map drawing.” Hear “the spatial query service that other systems depend on.”

There is a nice cross-module hint in pokefirered/src/battle_setup.c, chunk_494d132a59422c85. The excerpt in the research packet shows battle setup including headers such as fieldmap.h, event_object_movement.h, metatile_behavior.h, overworld.h, script.h, and battle.h. That is a compact architectural clue. Wild battle entry is not born in the battle engine alone. The battle engine receives control after overworld state, metatile behavior, event data, and transition logic have already cooperated. This is why I keep asking you to trace data table to runtime global to callback or task to visible behavior. A wild encounter is visible as a battle screen, but its preconditions are spatial.

Let’s walk a concrete movement attempt. I’ll narrate it slowly first, then we will refine it. The player is at 10, 12 and presses right. The desired coordinate is 11, 12. The movement code does not ask the renderer, “What picture is at 11, 12?” It asks the map runtime for the block or metatile information at 11, 12. The fieldmap helper flow, represented in our packet by pokefirered/src/fieldmap.c chunk_77ef7f3f8bff9777, is the kind of layer that keeps this from becoming ad hoc pointer arithmetic everywhere. Then the engine interprets the block’s tile behavior. The map block and tile behavior flow called out in pokefirered/src/fieldmap.c chunk_eeb0180842f7ece9 supports that idea: block access is upstream of behavior interpretation. If the behavior says this is an impassable tile, the movement attempt fails, and the avatar may play a bump animation or simply remain facing that direction. If the terrain permits movement, object occupancy is checked. If an NPC object event occupies 11, 12, the player still cannot enter. If no object blocks, the movement may begin, and only then do camera and drawing concerns update as the avatar advances.

Notice the order. We are not moving the sprite and then cleaning up the logical coordinate afterward. In a grid-based RPG, the logic coordinate is authoritative. The sprite animation is a visual interpolation over a decision that has already been made. That is one of the biggest design lessons in this module. If you build your lab map by letting the sprite’s pixel rectangle collide with other pixel rectangles, you will create a platformer-flavored system. That is not what FireRed is doing. FireRed’s overworld is tile-first, behavior-first, and object-event-aware.

Let me write the lab version on the board in pseudocode, spoken rather than copied. We have getBlockAt x y. We have getBehaviorForBlock block. We have isTerrainBlocked behavior direction maybe elevation. We have isObjectOccupying x y. Then attemptMove returns blockedByTerrain, blockedByObject, or allowedWithBehavior. If allowed, update the actor’s intended destination and possibly update camera offset. That is the lab architecture. I am deliberately not saying “just store a two-dimensional array of booleans,” because by now, after the week four homework and quiz, you should be past that. A two-dimensional boolean collision map is fine for an introductory clone, but it loses the reason FireRed’s metatile behavior system exists.

Here is the deeper reason: one tile can answer multiple questions. A ledge tile may block entry from one direction but allow a jump from another. A water tile may be impassable to walking but passable while surfing. A grass tile may be passable but semantically important. A warp tile may be passable and also initiate a map transition. A counter tile may prevent movement but allow interaction across it. So the question is not “wall or not wall?” The question is “what does this tile mean under the current movement mode and interaction context?” Fieldmap code gives the engine a way to retrieve the tile-side facts; behavior-specific code gives those facts gameplay meaning.

Now, let’s pause and check. Suppose I show you a tile that looks like a flower. The player can walk over it. Does that mean its tile behavior is irrelevant? No. It may still matter for footstep effects, encounter rules, bike permissions, or scripted constraints, depending on the behavior table and the calling system. Visual sameness and behavioral sameness are not equivalent. Conversely, two visually different tiles might share a behavior. That is why metatile behavior is such a strong design pattern for this era of RPG: it compresses art variation while preserving a smaller set of gameplay queries.

Let’s bring the camera into this. The camera is the second place where students often make the wrong abstraction. They think the camera is simply “the player’s x and y.” But in a tile engine, the camera is a mapping from world coordinates to screen coordinates, and that mapping has both tile and pixel components. The world coordinate says where the player or object is in the map. The camera coordinate says which portion of the map is currently being drawn. During movement, you may have a fractional or pixel offset as the screen scrolls from one tile alignment to the next. So for a brief period, the logical player coordinate and the visual scroll offset are both meaningful, but they answer different questions.

If I ask, “What tile is under the player for encounter checks?” use the logical coordinate. If I ask, “Which metatile graphics should be drawn into the newly exposed strip at the edge of the screen?” use the camera-relative drawing calculation. The week packet explicitly includes camera-relative map drawing as part of this material, and pokefirered/src/fieldmap.c chunk_642df7a31e75f54c is identified as fieldmap runtime support. That is the natural home for this kind of service: given a map and a camera position, determine what blocks need to be fetched and drawn.

On the board, I’ll draw another diagram. The map is large: maybe 40 by 30 blocks. The screen shows only a window, maybe something like 15 by 10 tiles, plus margins or buffers because the engine wants scrolling to be smooth. The camera origin is not the map origin. If the camera origin is 20, 12, then screen cell 0, 0 corresponds to world cell 20, 12. Screen cell 1, 0 corresponds to 21, 12. That transformation seems trivial, but it is everywhere. Rendering uses it. Object visibility uses it. Off-screen movement culling may use it. The player’s sprite placement uses it. And when the camera scrolls, the engine does not want to redraw the entire world; it wants to update the newly visible edge and preserve as much as possible.

This is where old hardware architecture shapes software architecture. On the Game Boy Advance, you are managing tilemaps, backgrounds, object sprites, VRAM, and per-frame budgets. FireRed’s architecture is not merely “clean” because somebody liked separation of concerns. It is clean because the hardware punishes waste. A camera-relative renderer can keep work bounded. A metatile query system can convert world data into the small pieces needed for the visible background. An object movement system can decide which sprites are active and where to put them on screen relative to camera scroll. The runtime shell from week three, with callbacks and tasks, gives this work a frame-by-frame schedule.

Let’s do a recap moment before we go further. We have three coordinate interpretations active at once. First, map coordinates, which identify logical cells in the loaded map. Second, behavior coordinates, which are usually the same logical cells but interpreted through metatile behavior and movement rules. Third, screen coordinates, which are camera-relative and used for drawing. Most bugs in student overworld engines come from mixing those three. You block movement based on a screen coordinate. Or you draw based on a player-relative coordinate without adding the camera origin. Or you check object occupancy after visually moving the sprite, which creates one-frame overlaps. Advanced architecture is largely about refusing to let those meanings collapse into one integer pair.

Now, let’s connect object events carefully, without re-teaching the object-event data definitions from the previous map lectures. We already know the map has object events as part of its gameplay data. Today we care about their runtime occupancy. An object event is not just an NPC script waiting to be talked to. It is also a moving participant in the grid. It has a current position, a facing direction, movement type, possibly a local movement boundary, and flags or states that determine whether it is active. The week focus specifically names object-event movement boundaries, and pokefirered/src/event_object_movement.c is the source the packet tells us to use for runtime movement claims after chunk review. So our architectural claim is: dynamic map collision must include object-event movement state, not just static map blocks.

Think about the NPC pacing left and right in a town. Its object event may have an initial coordinate in the map data, but at runtime it is not frozen there. The movement system decides when it steps, where it may step, and whether it remains within a boundary. That means collision with that NPC cannot be answered purely from map.json or static layout data. It must be answered from runtime object state. This is one reason the map system becomes a live simulation rather than a loaded file.

Here is a concrete example. Imagine an NPC whose home coordinate is 8, 6 and whose movement range is one tile horizontally. At time A, the NPC occupies 8, 6. At time B, it occupies 9, 6. The terrain at 9, 6 has not changed. The metatile behavior at 9, 6 has not changed. But the player’s movement result into 9, 6 has changed. At time A, the player may enter 9, 6 if the terrain permits. At time B, the player is blocked because the NPC is there. So if your lab implementation stores only terrain collision and ignores object occupancy, it will pass trivial wall tests but fail the actual RPG problem.

Check for understanding. If a movement attempt fails because an NPC is standing on the target tile, should we modify the map block to become blocked? No. That would corrupt the distinction between static terrain and dynamic occupancy. The map block remains what it is. Object-event state contributes an additional blocking layer. This is the same reasoning we will later use in battle and inventory systems: do not rewrite base species data when a particular party member gains experience; do not rewrite item definitions when the bag count changes. Base data and runtime state are separate.

Now, camera and object events have their own interaction. An object’s world coordinate is not enough to draw it. To place its sprite, the engine needs world coordinate minus camera origin, plus any pixel movement offset. If the camera scrolls while the object is animating, both offsets participate. That is why object movement and camera drawing cannot be totally ignorant of each other. They should not be tangled into one monolithic function, but they must agree on coordinate conventions. In FireRed terms, fieldmap runtime helpers, object movement, and overworld callback scheduling together create the visible illusion of a continuous world.

This is a good moment to mention the phrase “runtime shell” again. Back in week three, we studied AgbMain and the main callback slots, and we emphasized SetMainCallback2 and task dispatch through RunTasks. I am not going to reteach that machinery. But I want you to see how today’s material plugs into it. Collision is not an offline computation. Camera scrolling is not an offline computation. Object movement is not an offline computation. These are per-frame responsibilities scheduled under the overworld runtime callback. So when you read fieldmap.c in isolation, do not imagine it as a library used only during map load. It is part of the live overworld loop.

And this is also why the source packet’s battle_setup.c clue is useful. In pokefirered/src/battle_setup.c, chunk_494d132a59422c85, the include list ties together overworld, fieldmap, event object movement, metatile behavior, field control, player avatar, and battle transition. That does not mean every include is equally important to our topic, and we should not overread an include list as a call graph. But it does show that battle setup lives downstream from field state. When the player steps into grass, the system that eventually starts a battle is already depending on map behavior and overworld state having been interpreted correctly.

Let’s refine our movement attempt into a pipeline. Step one: gather input or scripted movement command. Step two: determine intended direction and target coordinate. Step three: query static map block and metatile behavior. Step four: apply movement-mode rules. Step five: query object occupancy and movement boundaries. Step six: if blocked, update facing and perhaps run bump or interaction logic. Step seven: if allowed, commit movement state and animate over frames. Step eight: during or after movement, update camera-relative drawing as needed. Step nine: once the step resolves, evaluate tile-triggered effects such as encounters or scripts. I am saying “pipeline,” but remember that in the actual engine these responsibilities are distributed across modules and callbacks. The pipeline is our mental model, not necessarily a single function body.

Let’s test that model with the tall grass example. Player at 4, 3, presses down to 4, 4. The terrain query says passable. The object occupancy query says empty. Movement begins. The camera may or may not scroll, depending on whether the player is near the screen center and the map edge constraints. Once the movement is accepted or completed, the tile behavior can be interpreted as grass for encounter purposes. If an encounter is selected, the battle setup path starts to matter. Again, chunk_494d132a59422c85 from pokefirered/src/battle_setup.c supports the idea that battle setup is connected to fieldmap and metatile behavior headers, but the important architectural point is broader: a battle can be the consequence of a movement decision.

Now test it with a wall. Player presses right into a tree. Static metatile behavior blocks. We do not ask encounter logic. We do not scroll the camera. We do not move the player’s logical coordinate. We may update facing direction, because facing and position are separate pieces of state. That distinction matters for interaction. If a player bumps into a counter or faces an NPC, pressing A may interact with the tile or object in front of the player. So even failed movement can update meaningful state.

Now test it with an NPC. Static terrain allows. Object occupancy blocks. Again, the player does not enter the tile. But the engine may still set facing direction toward the NPC, and the interaction button may now start the object event’s script. This is why object collision and event scripting are neighbors in the architecture. Occupancy says, “You cannot walk here.” Interaction says, “Because you are facing this object, you may talk to it.” Those are different decisions, but they use adjacent spatial facts.

Let me pause for a second and ask a diagnostic question. In your lab, if the player presses toward an NPC, which coordinate should be used to find the NPC for interaction: the player’s current coordinate or the coordinate in front of the player? It should be the coordinate in front of the player, derived from current coordinate plus facing direction. If you use the current coordinate, you will only talk to objects you are standing on, which should almost never happen. If you use the target coordinate after committing movement, you will accidentally require movement to succeed before interaction can be detected. The right model is: failed or non-movement-facing state still defines a front tile.

That front-tile idea also previews why collision and scripting cannot be separated too naively. The map runtime says what is in front. The object system says whether an object event is there. The script system says what should happen when activated. The UI system may show a message box. The task system advances the message box and returns control. The save system may later remember flags changed by that script. We are still in one tiny player action, but the full creature RPG architecture is already present.

I want you to notice how this increases the sophistication expected for homework_w05. Earlier in the course, if I asked you to build a minimal map loader, I accepted a grid, a player coordinate, and maybe one warp. This week, that is not enough. Your grid map needs metatile lookup, collision checks, camera offset, and object-event occupancy. The lab description says exactly that: implement a grid map that supports those features and run sample movement attempts against it. The “sample movement attempts” part is there because architecture becomes visible in edge cases. I want to see move into wall, move into grass, move into NPC, move while camera scrolls, move near map edge, and move when an object boundary prevents an NPC from stepping outward.

Here is a clean way to structure that lab without overengineering it. Represent your static map as block IDs. Represent your block table as block ID to behavior. Represent behaviors as a small enum or table, not just booleans. Represent runtime objects separately from the static map. Give each object a current coordinate, maybe a home coordinate, a movement range, and an active flag. Then write movement attempt functions that return structured results. Not just true or false, but something like allowed, blocked terrain, blocked object, and entered behavior. That structured return value forces you to preserve information that later systems need.

Now, to be very clear, I am not requiring you to reproduce FireRed’s exact C structures in the lab. The goal is not mimicry. The goal is architectural fidelity. FireRed’s fieldmap.c chunks in the packet, particularly chunk_eeb0180842f7ece9 for map block and tile behavior flow, tell us that block lookup and behavior interpretation are part of the real engine’s runtime pathway. Your lab should preserve that separation even if your language is different and your data structures are smaller.

Let’s end this first segment by tying the camera back to player experience. The player does not perceive “I queried a metatile behavior and subtracted a camera origin.” The player perceives a world that is solid, readable, and responsive. They press against a wall; the avatar does not pass through. They walk into grass; a battle may happen. They approach an NPC; the NPC occupies space. They move through a town; the camera follows smoothly enough that the world feels larger than the screen. That player-visible coherence is produced by boring, rigorous agreements between systems: map coordinates mean one thing, screen coordinates mean another, static terrain is separate from dynamic objects, and behavior is not identical to graphics.

So, hold onto this sentence as we move into the next part: collision is not a property of a tile; collision is a runtime answer assembled from tile behavior, movement context, and object state. Camera is not a picture; camera is the transformation that lets a large logical map become a small visible window. And object events are not just scripts; they are spatial actors whose occupancy participates in the map simulation. We will continue from that sentence into the more detailed flow of block access, camera-relative drawing, and movement boundaries.

Good. So let’s put that sentence on the board and make it operational. I’m going to draw three columns: logical map, visible screen, and actors. The logical map is measured in metatile coordinates. The visible screen is measured in background tile positions and pixel offsets. The actors, meaning the player and object events, live in logical map space but are rendered through the camera transform. If you mix those columns casually, your engine becomes brittle very quickly.

In FireRed terms, the fieldmap code is one of the places where that agreement is maintained. The research packet points us specifically to pokefirered/src/fieldmap.c, chunk_642df7a31e75f54c, as fieldmap runtime support; chunk_eeb0180842f7ece9, as the map block and tile behavior flow; and chunk_77ef7f3f8bff9777, as helper flow. I want you to hear that phrase, “helper flow,” not as “unimportant utility code,” but as “the glue that prevents every higher-level system from making its own private interpretation of the map.”

Suppose the player presses right. At the design level, that sounds like a small event: move one tile east. At runtime, it is a chain of questions. What is the player’s current logical coordinate? What coordinate is one step east? Is that coordinate inside the active map area? Which metatile block is stored there? What behavior is associated with that metatile? Does the current movement mode care about that behavior? Is another object event occupying the target coordinate? Is there an elevation mismatch? If movement is allowed, how should the actor’s sprite animate, and how should the camera respond if the actor approaches the screen edge?

Notice the sequence. We do not begin by drawing. We begin by asking whether the world permits the attempted state transition. Rendering comes after the simulation answer. This is one of the architectural habits I want you to carry into your own clone design: input does not directly change pixels; input proposes a state transition, and the field runtime accepts, rejects, or modifies that proposal.

Now, let me make this concrete. Imagine a small grid map. At coordinate 10, 5 there is normal ground. At 11, 5 there is a ledge metatile whose graphic shows a little cliff edge. At 12, 5 there is still ground. If the player is at 10, 5 and presses right, you cannot decide collision just by asking, “Is 11, 5 blocked?” A ledge may be passable from one side and blocked from another. Its graphic alone does not answer the movement question. Its behavior must be queried with movement direction and actor context. That is why last time I emphasized tile behavior queries as runtime questions, not labels you paste onto art.

Here is the check for understanding. If two tiles use the same ground graphic, can they have different movement behavior? Yes. And if two tiles use different graphics, can they share the same behavior? Also yes. The visual layer and behavior layer correlate because designers choose them that way, but the engine should not confuse correlation with identity.

Now I’m going to add a fourth word to the board: cache. Fieldmap systems often cache or stage map blocks so the engine can draw and query efficiently. On the Game Boy Advance, this is not an aesthetic choice; it is a hardware-era necessity. You cannot treat the entire map as if it were a free, infinitely addressable canvas every frame. You maintain a visible region, update the background tilemap as the camera crosses boundaries, and query map blocks through controlled helpers. That is why the fieldmap runtime support in pokefirered/src/fieldmap.c, chunk_642df7a31e75f54c, matters architecturally. It is part of the layer that turns authored map data into fast runtime answers.

Let’s connect that to camera-relative drawing. The player thinks, “I am walking east through Viridian City.” The engine thinks in offsets. The map has a coordinate system. The camera has a coordinate system. The background hardware has scroll registers and tilemaps. The sprite system has object coordinates. When the camera moves, most of the world does not move logically. The player’s map coordinate may increment from 10, 5 to 11, 5. The camera may shift a few pixels per frame. The background scroll may change. Object sprites may be redrawn at different screen positions even if their map coordinates are unchanged.

So, board cue: I’m writing this as a small equation. Screen position equals world position minus camera origin, plus rendering offset. That is not exact source code; it is the mental model. If an NPC stands at map coordinate 14, 5 and the camera origin is 8, 2, then the NPC appears six tiles from the left and three tiles from the top, adjusted by pixel animation offsets. When the camera origin becomes 9, 2, the NPC appears five tiles from the left. The NPC did not move. The camera did.

This distinction becomes important when debugging. If an NPC seems to “slide” when the player walks, the first question is not, “Did the NPC movement code run?” The first question is, “Did the camera transform change?” Advanced debugging starts by identifying which coordinate space changed. Logical map coordinate? Camera origin? Sprite animation offset? Background scroll? If you cannot name the coordinate space, you cannot localize the bug.

Now let’s bring object events back in, but only at the level we need today. We already treated object events as map-authored actors in earlier lectures, so I’m not going to re-teach their placement or scripting role. Today we care about their occupancy. An object event is not merely a script trigger waiting to be talked to. In movement logic, it is part of the collision field. That means the target tile may be walkable according to terrain behavior and still unavailable because another actor occupies it.

The research packet specifically flags pokefirered/src/event_object_movement.c as the high-signal movement source to use for runtime movement claims after reviewing the fieldmap chunks. That pairing is important. Fieldmap answers questions about blocks and behaviors; event object movement participates in actor boundaries, occupancy, and movement execution. In a clean architecture, those are separate responsibilities. Terrain says, “Here is what this coordinate means as map data.” The object movement layer says, “Here is who is currently there, who is moving there, and what actor rules apply.”

Let’s test that with a little scenario. The player is at 5, 5. An NPC is standing at 6, 5. The tile at 6, 5 is ordinary floor. The player presses right. If your collision function only checks metatile behavior, it returns passable. The player then steps into the NPC. That is wrong. If your collision function only checks object occupancy, then a wall tile with no NPC returns passable. Also wrong. The final movement answer is assembled from both. The correct answer is, “Terrain permits the step, but dynamic occupancy rejects it.”

Now, a subtle point: occupancy is not always just “current tile.” In a tile-based engine with walking animations, an actor may be between tiles. If an NPC has begun moving from 6, 5 to 7, 5, should 6, 5 be considered occupied? Should 7, 5 be reserved? Different engines choose different answers, but the important architectural idea is reservation. Without reservation, two actors can legally decide to move into the same tile during the same frame or adjacent frames. So a robust movement system often distinguishes current position, previous position, target position, and movement-in-progress state.

Check yourself here. Why is that more than a visual concern? Because scripts, collision, camera following, and interaction all depend on actor positions. If a moving NPC is visually halfway across a tile but logically already at the destination, the player’s attempt to talk to that NPC may resolve differently than if the NPC remains logically at the source until the animation finishes. Neither choice is automatically wrong, but the engine must be consistent.

FireRed’s broader architecture repeatedly makes this kind of consistency visible. Earlier in the course, when we followed callback and task flow through pokefirered/src/main.c, chunk_b35676322b169f85 for AgbMain and chunk_6cfd90f6d0136799 for SetMainCallback2, and pokefirered/src/task.c, chunk_cb0bb91a147bfb59 for CreateTask and chunk_3ffebc0341225936 for RunTasks, the same lesson appeared: visible behavior is produced by staged runtime systems, not by one giant loop doing everything informally. Week 5 is the map-runtime version of that idea. Movement, collision, drawing, and object updates are staged so each frame can produce a coherent world.

Let’s return to the lab you are building this week. You are implementing a grid map that supports metatile lookup, collision checks, camera offset, and object-event occupancy. If you implement that as one function called movePlayer that directly indexes a 2D array, changes player x, scrolls the camera, and redraws everything, it may pass one trivial test. It will not teach you the architecture. I want your lab to have explicit query points. One function should answer, “What block is at this coordinate?” Another should answer, “What behavior does this block imply?” Another should answer, “Does movement into this coordinate succeed for this actor and direction?” Another should project world coordinates into screen coordinates.

Let me say that again in more professor-like terms: you are not just coding movement; you are creating the interfaces between map data, simulation, and presentation. Those interfaces are the real learning target.

Now, imagine your test map has a wall at row 2, a door at row 3, water at row 4, and a wandering NPC near the door. The player attempts four moves: north into the wall, east into ground, south into water, and east into the NPC. The expected log should not simply say true, true, false, false. It should tell you why. “Rejected: impassable metatile behavior.” “Accepted: normal ground.” “Rejected: actor cannot enter water.” “Rejected: occupied by object event.” That reason field is not just nice debugging. It mirrors the separation of causes inside the engine.

Here is another check for understanding. If a player cannot walk onto a tile, should the engine always play the same bump behavior? No. The reason matters. A wall collision, an NPC collision, a water boundary, a ledge from the wrong direction, and a scripted forced stop may all reject movement, but they can produce different animation, sound, script checks, or follow-up behavior. Advanced architecture preserves cause long enough for later systems to respond appropriately.

Let’s talk about camera boundaries, because this is where many clone implementations become visibly amateur. A camera that follows the player in the center of the screen works in the middle of a map. At the edge of a map, centering the player would reveal beyond the authored world. So the camera needs constraints. It follows the player, but only within the legal camera range. The player can stand near the western edge of the map; the camera cannot scroll further west than the map’s left boundary. As a result, the player’s screen position changes near edges even if your usual rule is “keep player centered.”

That means screen position is not always constant for the player. In the middle region, the camera absorbs movement, and the player stays near the center. Near the edge, the camera stops, and the player moves across the screen. This is a simple idea, but it is an essential polish line. If your camera blindly centers the player everywhere, you draw void. If your camera never follows, the map feels like disconnected screens. If your camera follows but does not clamp correctly, objects will jitter or appear offset near boundaries.

The fieldmap helper flow cited in pokefirered/src/fieldmap.c, chunk_77ef7f3f8bff9777, is relevant here because helpers are where these transformations and bounded queries tend to be centralized. Again, I am not asking you to memorize a helper name from a slide. I am asking you to recognize the architectural function: centralize the math that many systems depend on, because the cost of inconsistent coordinate conversion is enormous.

Let’s do a concrete board example. Map width is 30 tiles. Screen width is 15 tiles. If the player’s x coordinate is 2, the camera x origin should be 0. If the player’s x coordinate is 10, the camera x origin might be 3, assuming the player is held around screen column 7. If the player’s x coordinate is 28, the camera x origin should be 15, because 30 minus 15 equals 15. That is the maximum origin that still shows valid map columns 15 through 29. So the camera origin is not just player x minus half screen width. It is that value clamped between 0 and map width minus screen width.

Now, if an NPC stands at x equals 20 and the camera origin is 15, the NPC appears at screen x equals 5. If the camera origin is 3, that same NPC appears at screen x equals 17, which may be off screen. You do not delete the NPC because it is off screen. You decide whether to update it, whether to draw it, and whether its sprite resources are active according to the object system’s visibility and range rules.

This gives us an important design separation: simulation radius and render radius are related, but not identical. A creature RPG overworld may keep nearby NPCs active even when just off screen, because they can walk into view. It may not need to animate or draw far-away NPCs. It may keep global script state for an NPC regardless of whether the object is currently instantiated as a visible sprite. This is exactly the broader decomposition from the course objective: runtime shell, overworld maps, event scripting, creature data, battle, inventory, UI, save, and link systems are not separate because programmers like folders. They are separate because each system has its own state lifetime and update rules.

Now, let’s tie this briefly to battle, since the research packet reminds us to keep the full creature-collector architecture in view. pokefirered/src/battle_setup.c, chunk_494d132a59422c85, includes dependencies on overworld, script, event_object_movement, metatile_behavior, fieldmap, and battle transition systems. That is a source-backed clue about how entering battle is not isolated from the field. A wild encounter or trainer encounter begins in the overworld, using map position, tile behavior, event state, and movement context, then transitions into the battle engine. So the collision and movement layer is not “just walking.” It is upstream of encounters, scripts, transitions, and progression.

Think of tall grass. The metatile behavior says more than “walkable.” It can participate in encounter logic. A movement step into grass may be physically allowed, but it also creates a chance for a field-to-battle transition. So when we say tile behavior is queried at runtime, we mean for multiple purposes: collision, effects, encounter checks, animation cues, sound, and script interactions. One authored tile can feed several runtime systems.

Pause there and check your mental model. If you are implementing tall grass in the lab extension, should “grass” be a subclass of “walkable floor” hard-coded into player movement? I would advise against that. Better: terrain behavior includes walkability and tags or behavior identifiers. Movement asks whether entry is allowed. Encounter logic asks whether this behavior can trigger an encounter. Rendering asks what graphics to draw. Those are separate questions over shared data.

Now let’s revisit dynamic objects from the camera side. Suppose an NPC is at map coordinate 12, 10 and the camera origin is 8, 6. The NPC draws at screen tile 4, 4. If the NPC moves east to 13, 10 while the player stands still, its screen tile changes to 5, 4 through animation. If the player moves west at the same time and the camera origin changes from 8, 6 to 7, 6, the NPC’s screen position changes even if its logical position did not. In the renderer, both effects combine. This is where beginner engines often double-apply movement. They subtract camera once when placing the sprite and again when scrolling the container. The result is that NPCs drift twice as fast as the background.

The cure is discipline. Choose where the camera transform is applied. If your background layer is scrolled by camera offset, then sprites need their own world-to-screen conversion consistent with that scroll. If your whole scene graph is moved as a parent, then child sprites should not also manually subtract the full camera in the same way. FireRed’s hardware-specific path is different from a modern browser canvas or Unity scene graph, but the architectural invariant is the same: one camera transform, applied coherently.

Now, I’m going to say something that sounds simple but prevents a lot of bugs: collision should usually be computed in world coordinates, not screen coordinates. If an NPC is visually at screen column 4, that does not tell you whether the player can step into map coordinate 12, 10 unless you know the camera origin. Collision lives in the logical map. Rendering lives in the camera-relative projection. If your collision code depends on where something appears on the screen, scrolling the camera can change gameplay. That is almost always a bug.

Let’s make the check explicit. The player stands still. The camera scrolls because of a scripted pan. Should collision around the player change? No, not because of the camera itself. The player’s screen position changed; the player’s world position did not. If your collision changes during a pure camera pan, you have confused spaces.

Now, object-event movement boundaries add one more layer. Many NPCs are not allowed to roam freely over the whole map. They may have a movement range around their spawn point or a scripted path. This boundary is not the same as terrain collision. An NPC may be physically able to walk onto a tile but prohibited by its local movement range. Conversely, a boundary cannot allow an NPC to walk through a wall. So the movement answer for an NPC is also assembled: terrain behavior, object occupancy, movement range, elevation, script lock state, and current animation state.

This is why pokefirered/src/event_object_movement.c matters for today’s lecture. It is the source focus for claims about runtime object-event movement, while pokefirered/src/fieldmap.c, chunk_eeb0180842f7ece9, grounds the map block and tile behavior side. Together they show the division we want to model in our own architecture: the map answers “what is there,” and the object movement system answers “what may this actor do now.”

For your lab, I want you to include at least one NPC with a movement boundary. Give it a home coordinate and a radius. Then run a sample attempt where the NPC tries to step outside that radius onto an otherwise walkable tile. The expected result should be rejection by movement boundary, not rejection by terrain. Then place a wall inside the boundary and reject by terrain. Then place the player inside the boundary and reject by occupancy. Those three tests prove you are not flattening every failure into “blocked.”

Now, quick recap before we push further. A metatile lookup retrieves authored map content. A tile behavior query interprets that content for a runtime purpose. A collision check combines terrain behavior with actor context and dynamic occupancy. The camera converts world space into screen space under boundary constraints. Object events are actors with position, movement state, and often local movement limits. Each of those pieces can be tested separately, but the player experiences them as one smooth step across the map.

That “one smooth step” is the point. Good architecture does not exist so the code looks impressive. It exists so that when the player taps the D-pad, twenty small systems agree quickly enough to preserve the illusion of a coherent place. Keep that in mind as we now move from single-step movement into the per-frame sequence: when the input is sampled, when the movement decision is made, when the object animation advances, when the camera updates, and when the newly visible edge of the map has to be drawn.

So, let us slow that sequence down, because this is where many student clone engines become subtly wrong. They implement collision, and they implement animation, and they implement a camera, but they do not decide which one owns the frame. In FireRed-style architecture, the answer is not that one system owns everything. The answer is that the runtime shell gives you a repeated frame opportunity, and the overworld code uses that opportunity to coordinate several smaller state machines.

Imagine the player is standing at world coordinate ten, six, facing east. On one frame, the D-pad is sampled and says, “east is held.” That does not immediately mean the player’s sprite teleports to eleven, six. It means the player avatar logic asks whether a movement should begin. That question expands into several others. Is the avatar currently locked by a script? Is the target tile in bounds? What block is at eleven, six? What behavior does that metatile report? Is there an object event already occupying that position? Is the movement type allowed to enter that behavior? Only after those questions produce a yes does the actor begin a movement state.

Notice the word begin. This is the part I want on the board. I am writing: “input decision is discrete; movement presentation is temporal.” The collision decision often happens at the tile step boundary, but the visible motion is spread over multiple frames. That separation is what lets a tile-based RPG feel responsive without becoming visually jerky. The map says yes or no at the grid level. The actor system animates the yes over time. The camera then follows the animation, either by moving the screen offset or by shifting which map blocks are drawn at the edge.

This is why the fieldmap helpers matter beyond mere lookup. In the source focus for today, pokefirered/src/fieldmap.c chunk_642df7a31e75f54c is useful as fieldmap runtime support, while chunk_eeb0180842f7ece9 is the one I want you to associate with map block and tile behavior flow, and chunk_77ef7f3f8bff9777 with helper flow. I am not asking you to memorize every helper name as trivia. I am asking you to recognize the architectural pattern: the engine keeps map representation behind query functions so that movement, scripts, encounters, and rendering can ask consistent questions about the same world.

Let me give a concrete example. Suppose tile eleven, six visually looks like tall grass. The rendered metatile and the gameplay behavior are related, but not identical concepts. The visual block says what graphics to draw. The behavior says what the tile means to runtime systems. It may mean passable, encounter-producing, surfable, ledge-like, blocked, door-like, or something else. If your clone stores only a sprite index in the grid, then your collision code starts growing visual assumptions: “if tile index equals 42, block movement.” That works until you reuse graphics for different semantics, or until the same behavior appears under multiple graphics. FireRed’s architecture avoids that trap by routing through block access and behavior queries, which is exactly the flow emphasized in pokefirered/src/fieldmap.c chunk_eeb0180842f7ece9.

Now, check your understanding here. If I change the artwork for a cliff tile but keep the same metatile behavior, should the collision result change? No. The player-facing pixels changed, but the behavioral contract did not. Conversely, if I keep the same artwork and change the behavior from ordinary ground to impassable, should collision change? Yes. That is the reason we keep saying “tiles as runtime queries,” not “tiles as pictures.”

Now let us add the camera. The camera is not just a rendering offset; it is a participant in map streaming. If the player walks east and the player is not near the right boundary of the map, the usual illusion is that the player remains near the center of the screen while the world scrolls west. But if the player is near the map edge, the camera cannot scroll past valid map data. In that case, the player’s sprite moves across the screen instead. So the same accepted movement can produce different screen-space effects depending on camera boundary constraints.

This is one of the most important distinctions for today’s lab. World coordinate and screen coordinate are not the same thing. World coordinate answers, “Where is this actor in the map grid?” Screen coordinate answers, “Where is this actor drawn relative to the current camera?” Camera-relative map drawing is the bridge. The fieldmap side supplies the blocks that should be visible. The object system supplies actors whose screen positions are derived from world position plus movement offsets plus camera offset. If those calculations are inconsistent by even one tile, you get classic bugs: the player collides with something visually offset, an NPC appears to stand on the wrong tile, or the newly exposed row at the edge of the screen draws one frame late.

Let us walk through the per-frame sequence with a sample movement. I will narrate it like a trace. Frame zero: player is idle at ten, six, camera origin is maybe three, two, meaning the visible screen begins around world coordinate three, two. Input says east. The avatar movement code asks for the target coordinate, eleven, six. The fieldmap query reads the block for eleven, six and gets its behavior. The object occupancy check asks whether an object event occupies eleven, six. The answer is clear, so the player’s movement state becomes “walking east.”

Frame one: the player is no longer making a new tile decision. They are mid-step. The engine advances the movement animation by some pixel increment. If the camera is allowed to scroll, camera offset also advances. The world coordinate may already be logically committed to eleven, six, or it may remain paired with a previous-plus-delta representation depending on the specific actor model, but architecturally the key is that the visible state is between tiles while the grid decision is already settled. The map renderer checks whether camera movement exposed a new column at the east edge. If yes, it must draw the corresponding blocks into the background tilemap.

Frame two and onward: the same movement state continues. No new collision decision should be made for another east step until the actor is ready to accept a new movement. That is another common clone bug. Students sample input every frame and run collision every frame even while the sprite is halfway through a tile. Then holding right can queue or double-apply motion in a way that breaks object alignment. In a FireRed-like system, the actor’s movement state is a gate. Input matters when the actor is eligible to begin a step; animation frames matter after the decision.

Here is the board cue: I am drawing four boxes in a row. Input, movement decision, animation advance, camera and draw update. Under movement decision, I am writing “fieldmap behavior plus occupancy.” Under animation advance, I am writing “object state.” Under camera and draw, I am writing “world to screen plus edge redraw.” Those are not four separate games. They are four responsibilities inside one frame loop.

Now connect this back to our broader course plan. In week one, you decomposed the creature RPG into runtime shell, overworld maps, event scripting, creature data, battle, inventory, UI, save, and link systems. By week five, you should be able to see that this decomposition is not a diagram for a slide. It predicts where bugs belong. If a tile is visually wrong, suspect map drawing or block data. If a tile is visually correct but passability is wrong, suspect metatile behavior or collision query. If the player can walk through an NPC, suspect object occupancy or movement boundary logic. If the player collides correctly but the screen scrolls incorrectly, suspect camera conversion and map redraw. That diagnostic separation is the professional value of the architecture.

The research packet also shows why this module becomes a bridge to later systems. For example, pokefirered/src/battle_setup.c chunk_494d132a59422c85 includes overworld, event object movement, metatile behavior, fieldmap, player avatar, screen effects, and battle transition headers in the same source area. That does not mean battle setup owns all those systems. It means entering battle from the field is a cross-system handoff. The player steps on or through an overworld condition, the map and event context decide that a battle can begin, and then the runtime transitions into a battle state. Collision and tile behavior are therefore not isolated “map homework.” They are upstream of encounters, scripts, transitions, and eventually save-visible progression.

Let us make that concrete with tall grass. The movement system allows the player to enter the tile. The behavior query identifies the tile as grass-like. The player animation and camera proceed. At the appropriate point in the step or after the step resolves, encounter logic may ask whether this behavior can trigger a wild encounter. If it does, battle setup takes over. This is why I keep insisting that “passable” is not the only question. A tile can be passable and still semantically rich. It can be passable water for surfing, passable grass for encounters, passable floor for nothing special, or passable warp-like space that triggers a transition. We covered warp state earlier, so I am not going to reteach it now, but I do want you to see the continuity: the same map runtime that lets you move also supplies the facts that let the rest of the game react.

Now, object events complicate this because they are not just extra collision tiles. An object event has identity. It may face a direction, run a script when spoken to, wander inside a local rectangle, or be hidden by a flag. If you model NPCs as blocked cells only, you lose the difference between “there is a rock here” and “there is a trainer here who can see you, turn, walk, and start a script.” The source focus names pokefirered/src/event_object_movement.c as the high-signal object-event movement source for these runtime movement claims, and we use that in combination with the fieldmap chunks rather than replacing fieldmap queries with actor code. The map answers what the terrain permits. The object-event system answers what actors are doing and occupying.

Here is a subtle point. Object-event movement boundaries are local constraints layered on top of global map collision. An NPC might be allowed to wander only two tiles left and right from its home position. That does not mean those two tiles are automatically passable. The movement attempt still has to respect terrain and occupancy. Likewise, a passable tile outside the NPC’s local range is still forbidden for that NPC’s autonomous movement. So the decision for an NPC step is a conjunction: is this movement legal for the actor’s local movement rule, legal for the terrain, and legal with respect to other occupants?

Check for understanding. If an NPC’s wander rectangle includes a pond tile, should the NPC walk into the pond just because it is inside the rectangle? No. The rectangle is not terrain permission. It is a boundary on possible attempts. The fieldmap behavior query still matters. If a perfectly walkable ground tile is outside the rectangle, should the NPC wander there? Also no. Terrain permission is not actor permission. Both must agree.

This is where advanced design discipline comes in. Do not collapse all of those checks into a single Boolean called canMove without preserving why it failed. For a small lab, yes, a Boolean is enough to move the avatar. But for a serious engine, failure reason matters. The player bumping into a wall may play a bump sound. Trying to surf onto water may open a prompt. Walking into an NPC may initiate interaction if the A button is pressed, but block passive movement otherwise. A trainer seeing the player may override normal input and start a scripted approach. All of those begin from “movement not simply accepted,” but they do not mean the same thing.

Let me phrase that another way. Collision is not merely negation. Collision is information. A blocked tile may be blocked because of terrain, because of an object, because of map boundary, because a script has locked movement, because a ledge allows movement only in one direction, or because the actor’s movement type cannot enter that behavior. The engine’s architecture benefits when those cases can be distinguished close to the point of query, even if the final avatar movement function receives a compact result.

Now, in today’s lab, when you implement your grid map, I want you to model at least four layers. First, a block grid, where each coordinate gives you a metatile or block id. Second, a behavior table, where the block id can be translated into a behavior. Third, an object occupancy layer, where dynamic actors reserve or occupy coordinates. Fourth, a camera state, where world coordinates are converted to screen coordinates under map bounds. If you do only the first layer, you have a tile renderer. If you do the first two, you have terrain semantics. If you add the third, you have an overworld with actors. If you add the fourth, you have the beginning of a FireRed-like field runtime.

For a sample movement attempt, use this trace. The player at four, four attempts north to four, three. Your code should ask for the target block. It should resolve the block behavior. It should ask whether that behavior blocks the player’s movement type. It should ask whether an object occupies four, three. Then it should either begin a movement state or return a failure reason. If movement begins, the camera does not need to jump; it should update over the movement frames. If the camera moves enough to expose new map area, draw the new strip from the block grid. You do not need GBA background hardware for the lab, but you do need the conceptual equivalent: only the visible region is drawn, and it is drawn from world data through camera offset.

This is also why we spent time earlier on callbacks and tasks. The per-frame field runtime is not a while loop hidden inside the player. It is part of the broader runtime shell. We already covered AgbMain, the main callback slots, SetMainCallback2, task allocation, task IDs, and RunTasks, so I will only bring that forward briefly. The overworld gets time each frame because the shell calls into the current callback and task machinery. Inside that time slice, the field systems progress. That is how a script can lock player input, an object can keep walking, a camera can keep scrolling, and a text box can eventually return control without the whole program becoming a pile of blocking calls.

You can see the same broad callback-state-machine idiom outside the overworld too. The research packet points to pokefirered/src/learn_move.c chunk_af3598df6f092723, which explicitly describes a move relearner state machine and includes script, task, overworld, menu, and summary-screen dependencies. Different subsystem, same architectural habit: state progresses in stages through callbacks and tasks rather than by freezing the entire engine in one long synchronous procedure. That comparison is useful because it keeps us from treating the overworld as special magic. It is one member of a family of runtime state machines.

Now, let us return to the camera one more time, because camera bugs are very easy to misdiagnose as collision bugs. Suppose the player walks east and the screen scrolls, but the collision probe for the next tile uses screen coordinate instead of world coordinate. At first, near the origin, the bug may be invisible because screen and world are close. After scrolling, the player tries to move into what appears to be open ground and gets blocked, or walks into what appears to be a wall. The actual mistake is coordinate-space confusion. The fieldmap query must receive world coordinates. The renderer receives camera-relative coordinates. Object sprites are drawn by converting their world or local movement state through the camera. Keep those spaces named in your code. Do not call everything x and y once the project grows.

A good advanced implementation often has names like mapX and mapY, cameraX and cameraY, screenX and screenY, and maybe pixelOffsetX for mid-step animation. The names are not cosmetic. They prevent invalid arithmetic from looking plausible. If I see screenX passed into a metatile lookup, I want an alarm bell to go off in your head. Fieldmap lookup is about the map. Screen placement is about presentation.

Let me give you a final check before we pause for the next segment. A player begins a step east. Halfway through the animation, the camera has shifted four pixels. The player presses north during that half-step. Should the engine immediately run north collision from the half-step screen position? No. The actor is mid-movement. The next grid decision waits until the movement state allows another step, and when it does, the target is computed from the actor’s map coordinate and facing or input direction, not from an incidental screen pixel. You may buffer input if you choose, but buffering is not the same as running collision out of the wrong coordinate space.

So the synthesis for this section is this: fieldmap code provides stable world queries; tile behavior gives semantic meaning to blocks; object-event movement adds dynamic actors and local constraints; the camera converts world changes into screen changes; and the runtime shell gives all of that a frame-by-frame heartbeat. When those responsibilities remain separate but coordinated, the player experiences a single clean action. When they blur together, the first symptom is usually not a compiler error. It is a one-tile lie: a wall that is not a wall, an NPC that is not really where it appears, or a camera that shows a place before the map runtime can honestly support it. We will use that diagnostic lens next as we move from the player’s step into event contact, object facing, and the moment where movement becomes interaction.

Now, let’s pick up exactly there: the moment where movement becomes interaction. Up to this point, a step has mostly been a spatial question. Can I move from this world coordinate to that world coordinate? What metatile is there? What behavior does that tile report? Is there a dynamic object occupying the destination? If yes, is it solid, passable, hidden, moving, or in some intermediate state? But once the player presses A, or once the player’s attempted step touches a script boundary, the map runtime has to reinterpret space as intention.

Let me put that on the board as a sentence, because it is one of the more important architecture sentences in this week: collision asks, “may the actor enter this cell?” Interaction asks, “what does this adjacent or contacted cell mean?” Those are related, but they are not the same query.

In a simpler engine, students often combine them. You see code like, “if tile is wall, block movement; if tile is sign, read sign; if tile is NPC, talk.” That works for a toy prototype, and it is exactly why the week 5 lab starts with a small grid map. But FireRed has to support ledges, doors, counters, scripts, trainers, warps, hidden items, surfable water, object events, and camera-relative drawing under the same overworld shell. So the engine benefits from splitting the problem into layers. The fieldmap layer gives stable access to map blocks and tile behavior; the object movement layer controls dynamic actors; the script and field-control layer interpret what a player-facing or player-contact query should do.

That separation is visible in the source focus for today. The fieldmap runtime support in pokefirered/src/fieldmap.c, especially chunk_642df7a31e75f54c, is part of the stable world-query side. The map block and tile behavior flow in pokefirered/src/fieldmap.c, chunk_eeb0180842f7ece9, is where we ground the claim that the engine is not treating the screen as the truth; it is treating map data and block lookup as the truth. And the helper flow in pokefirered/src/fieldmap.c, chunk_77ef7f3f8bff9777, matters because helper functions are how this architecture avoids every caller becoming its own half-correct map interpreter. For movement claims involving dynamic actors, the research packet points us to pokefirered/src/event_object_movement.c as the high-signal movement source, so when we talk about NPC occupancy and local movement boundaries, keep that module mentally paired with the fieldmap queries rather than folded into them.

Now, imagine the player is standing one tile south of an NPC. The player presses A. The engine should not ask, “can the player move north?” That would be the wrong question. In fact, if the NPC is solid, the movement answer should be no. But the interaction answer should be yes: the player can address the object in front of them. So we have a deliberately interesting contradiction: the same occupied cell blocks entry and permits interaction. Advanced architecture often looks like that. A cell is not simply “available” or “unavailable.” It is available to some subsystems for some purposes, and unavailable to others.

Here is the concrete example I want you to track. Suppose our lab grid has the player at world coordinate 10, 12, facing north. The NPC is at 10, 11. The camera is currently offset so that world coordinate 10, 12 appears near the center of the screen, perhaps screen tile 8, 6. The player presses A. The input handler does not care where on the screen the NPC appears. It cares about the player’s world coordinate and facing direction. It computes the facing coordinate, 10, 11, then asks the map-and-object runtime, “what is interactable there?” The rendering system may later draw a text box and freeze movement, but that is downstream. The interaction begins as a world query.

Check yourself here: if the camera scrolls one tile while the player remains at 10, 12, should the identity of the NPC in front of the player change? No. The screen position changes; the world relationship does not. That is why we keep insisting that camera-relative drawing is a projection, not a source of gameplay truth.

Let’s take this one level deeper. The player pressing A usually involves a facing query. A movement attempt involves a destination query. A trainer seeing the player involves a line-of-sight query. A coordinate trigger involves a player-position query after movement completes. These are all map queries, but they are not interchangeable. If you design your clone so there is only one function called check_tile, you will rapidly overload it with unrelated meanings. By the time you add ledges or counters, you will no longer know whether check_tile means “can enter,” “can activate,” “can see across,” “can surf on,” or “should run an event after landing.”

In FireRed terms, the source packet’s fieldmap chunks support the lower part of that stack: block access and tile behavior lookup. The behavior value itself is not the whole event system. It is a semantic tag that other systems can use. That distinction matters. A tile behavior query might tell us, “this is a counter,” or “this is water,” or “this is a special surface.” But the player-facing control logic decides whether pressing A across a counter should reach an NPC, whether a water tile should ask about Surf, or whether the current state allows that action. The map does not need to know the entire story of the party, item inventory, badges, script flags, or UI mode. It only needs to answer map-shaped questions reliably.

Now, let me pause and ask a quick understanding check. If your player cannot walk into a shop counter, but can talk across that counter to the clerk, which layer is probably responsible for the difference? The collision layer blocks entry because the tile behavior or object occupancy says entry is not allowed. The interaction layer performs a separate facing query and interprets the counter as allowing communication across an adjacent boundary. If you answered, “the counter is both solid and interactable depending on the query,” that is the architectural answer I want.

This is also why object facing is not a cosmetic detail. In a creature-collector RPG, facing direction is a small piece of state with surprisingly large consequences. It controls which adjacent tile the player addresses. It controls which direction an NPC turns before dialogue. It controls which sprite frame is drawn. It may control whether a trainer sees the player. And it participates in movement animation, because an actor can face a direction without successfully stepping in that direction.

Think about the familiar case: you press into a wall. The player sprite turns toward the wall, but does not move. That is not a failed frame. That is correct behavior. The facing state changed; the position state did not. A less mature engine often binds facing and movement too tightly: if movement fails, nothing changes. Then the player cannot turn toward a sign unless they can enter the sign’s tile, which is nonsense. So a good movement system has to represent at least three related but separable facts: current coordinate, current facing direction, and current movement animation state.

Board cue: I’m writing three columns now: position, facing, animation. Position is the authoritative world cell. Facing is the actor’s directional intent or orientation. Animation is the visual transition over frames. When the player taps a direction into an obstacle, position stays the same, facing updates, and animation may play a bump or turn frame. When the player takes a valid step, position may be reserved or updated according to the engine’s movement model, facing updates, and animation runs until the actor visually reaches the next cell. When the player presses A, position stays the same, facing selects the target coordinate, and animation may be idle while the script system takes over.

That “reserved or updated” phrase deserves attention. In a tile RPG, movement is visually continuous but logically grid-based. During a step, an actor may be between two screen tile positions. The engine must still know which logical cells are occupied or reserved so another object does not walk through the actor halfway through the animation. This is one reason object-event movement code tends to be more complicated than student prototypes expect. The dynamic occupancy question is not only, “where is the NPC standing this instant?” It is also, “where is the NPC going, and should that destination be treated as unavailable?”

When the research packet points us to pokefirered/src/event_object_movement.c for object-event movement boundaries, this is the kind of runtime concern we should expect from that module. The object system is not just a sprite list. It is an actor-state system with local movement rules, occupancy implications, facing, animation, and script-visible identity. Then fieldmap.c gives those actors a stable terrain to query. Again, separate responsibilities, coordinated per frame.

Now let’s connect this to camera. During an object movement, the camera may be stationary, following the player, or being moved by a special script. The object’s world coordinate and movement state should remain coherent regardless of camera offset. If the player walks north and the camera scrolls north, the player might remain visually near the center of the screen while the map background shifts. That can trick you, while debugging, into thinking the player is not moving. But the world coordinate changed. The camera projection changed in the opposite visual direction. The sprite’s screen position might be held steady. Three things happened, and if you only inspect pixels, you will misunderstand the runtime.

This is why, in the lab, I want your sample movement attempts to log world coordinates, not just redraw the grid. For each attempted step, print something like: actor id, starting world coordinate, facing direction, target world coordinate, terrain behavior, object occupancy result, movement accepted or rejected, resulting camera offset. That log is not busywork. It trains you to diagnose the one-tile lie I mentioned earlier. If the rendered grid says the player is blocked but the log says the terrain is passable and there is no object, then your bug is probably in projection or rendering. If the log says the target is blocked but the drawing looks open, your bug is probably in block lookup, behavior mapping, or stale object occupancy. If both say blocked but the player should be able to interact, then your bug may be that you reused movement collision for interaction.

Let’s do a small spoken trace. Player at 5, 5, facing east. Tile at 6, 5 is a counter. NPC clerk at 7, 5, behind the counter. Player presses right. The movement subsystem computes destination 6, 5. Fieldmap lookup returns the metatile behavior for the counter. The collision interpretation says entry is blocked. The player’s position remains 5, 5, facing becomes east. Now player presses A. The interaction subsystem first checks the facing tile, 6, 5. The counter behavior says this is a tile that may allow interaction across it, so the query may continue one more tile to 7, 5, or may use a specific counter-facing rule depending on how the engine expresses it. Object lookup finds the clerk object event. Script dispatch begins dialogue. Same geometry, different question, different result.

Now, I’m not claiming that exact counter flow from a specific FireRed function name here, because our packet for today emphasizes fieldmap chunks and names event_object_movement.c broadly rather than giving us a full field-control excerpt. But the architectural reasoning is grounded in the source organization we do have: fieldmap.c chunks for map block and behavior access, and event_object_movement.c as the high-signal object movement source. That is how you should make claims from partial source context: anchor the low-level mechanism where the evidence is strong, and label the higher-level flow as architectural inference unless you have the exact function in front of you.

That source discipline is part of the course, too. In week 2, we practiced tracing data table to callback to visible behavior. In week 3 and week 4, you identified files, structs, functions, constants, data tables, and state variables across major systems. By week 5, I expect you to stop saying “the game just checks the tile” as if that is a complete explanation. Which tile? In which coordinate space? Through which accessor? Interpreted as collision, behavior, contact, or interaction? With object occupancy included or excluded? Before or after the movement animation commits? Those questions are how an advanced engineer reads an engine instead of narrating it.

Let me also connect this to event scripting, because we are right at the boundary. A script should not need to know how the camera redraws metatiles. It should be able to say, in effect, “start this dialogue,” “move this object,” “set this flag,” “give this item,” or “warp the player.” The script layer operates in gameplay concepts. But those gameplay concepts depend on reliable map runtime services. If a script tells an NPC to walk left three steps, the object movement system must resolve each step against local constraints. If a script hides an object, occupancy and drawing must eventually reflect that. If a script warps the player, we are back to the prior foundation: warp destination state and map load sequence, which we covered earlier, and then the new map’s fieldmap data becomes the query authority.

So the overworld is not a single state machine. It is a stack of cooperating state machines. The runtime shell calls the overworld callback each frame. The overworld callback services tasks, input, scripts, movement, camera, and drawing in an order that preserves consistency. The fieldmap layer answers block and behavior questions. The object layer answers actor questions. The script layer answers progression questions. The UI layer may temporarily capture input and draw text boxes. Save and link systems sit nearby because they need snapshots or synchronization of these states. That is the full course objective showing up inside one step on a grid.

Now, another check for understanding. Suppose an NPC appears to overlap the player for one frame during a scripted movement. Give me two plausible categories of bug. First, the object occupancy or reservation state may be updated too late, so both actors believe the same cell is available. Second, the logical state may be correct, but the camera-relative drawing or sprite screen coordinate calculation is using a stale offset. There are others, of course, but those two categories separate gameplay truth from projection truth, and that separation is the diagnostic habit I want.

Let’s spend a minute on boundaries. Object-event movement boundaries are not only the edges of the whole map. NPCs often have local roaming areas. A character may pace left and right, wander within a small rectangle, turn in place, or remain fixed. In data, you can think of an object event as having a home coordinate plus movement type and range. At runtime, that becomes a constraint system. The object cannot simply select any passable adjacent tile on the entire map. It must pass terrain collision, dynamic occupancy, and its own movement boundary. That gives designers expressive control without writing a script for every idle step.

Here is a practical clone design. Give each object an origin coordinate, current coordinate, facing, movement kind, movement radius, solid flag, script id, and active movement state. When the NPC’s idle movement timer fires, choose a candidate direction according to movement kind. Compute target coordinate. Reject it if it falls outside origin plus radius. Reject it if fieldmap behavior blocks entry. Reject it if another solid object occupies or reserves it. If accepted, reserve the target and start animation. When animation completes, commit current coordinate and clear the reservation. If rejected, optionally turn in place or wait. That is enough to approximate the architectural shape without copying FireRed’s full implementation.

Notice how this design uses the same world-query discipline as the player movement, but with object-specific constraints. That reuse is important. Do not create one collision system for the player and a completely separate one for NPCs unless the engine genuinely needs different physics. You can have different policy layered over shared queries. The player may be allowed to trigger ledge jumping; an NPC may not. The player may initiate Surf; an NPC may not. But both should ask the fieldmap for behavior and ask the object layer about occupancy in a consistent coordinate space.

The camera, meanwhile, should follow the subject according to its own policy. Most of the time that subject is the player, but scripts may lock, pan, or delay the camera. The fieldmap drawing code has to know which blocks to draw for the current camera origin. That is where pokefirered/src/fieldmap.c chunk_77ef7f3f8bff9777, the helper flow, is relevant to our mental model: helper functions keep camera-relative block access from being reinvented incorrectly at every draw call. And chunk_eeb0180842f7ece9 matters because the block and behavior flow is the bridge between stored map data and runtime interpretation. You should read those chunks with this question in mind: “Which functions return world facts, and which functions prepare those facts for the screen?”

Let me say that again because it is a good annotation to put in your source notes. World facts and screen preparation are different. A world fact is “metatile at world coordinate x,y has behavior b.” Screen preparation is “draw the block corresponding to world coordinate x,y at screen tile position u,v given camera offset c.” If those two ideas are collapsed, scrolling becomes dangerous. If they are separated, scrolling becomes mostly a projection update.

Now we can see why week 5 sits where it does in the course. In weeks 1 and 2, we established that FireRed is organized around a runtime shell: callbacks, tasks, data tables, and visible behavior. In weeks 3 and 4, we moved into maps as authored data and into the overworld load path, including map headers, layout references, object events, and warp destination state. Today we are inside the loaded map, after the header and layout have done their job. This is the moment where data becomes continuous play. The player does not experience “a layout registry reference.” The player experiences walking one tile, seeing the camera scroll, bumping into an NPC, pressing A, and getting a script. The runtime has to make all of those feel atomic even though they cross several modules.

That is also why the week 5 homework is more demanding than the earlier map-data exercises. At this point, I expect your implementation to distinguish metatile lookup from tile behavior, collision from interaction, world coordinate from screen coordinate, and static terrain from dynamic occupancy. If you only draw a grid and move a marker, that is not enough for this week. The lab asks for sample movement attempts because the behavior of rejected movement is just as important as accepted movement. A creature RPG spends a lot of time saying no gracefully: no, you cannot walk through this wall; no, you cannot step onto this water yet; no, this NPC occupies that tile; no, the camera should not reveal unloaded space; no, this object cannot wander outside its local range. A polished overworld is a machine for graceful refusal.

And with that, we are ready to move into the next subproblem: once the facing query or contact query identifies an event target, how does the engine cross from movement state into script state without losing the frame-by-frame discipline of the overworld? Keep the board columns in mind: position, facing, animation, camera, script. In the next segment, we will treat a player interaction not as a magical dialogue popup, but as a handoff between cooperating runtime systems that all still need to agree on where the player is, what they are facing, and what the map can honestly support.

Now, that handoff is where a lot of student clone projects accidentally smear responsibilities together. The player presses A, a text box appears, and because the visible behavior is simple, the code becomes simple in the wrong way. The input handler looks at the tile ahead, decides there is an NPC, immediately opens a message box, maybe freezes movement by setting a boolean, maybe changes the NPC’s facing direction directly, and now the map loop, UI system, script system, and object animation system are tangled in one place. That can work for a ten-minute prototype. It does not scale to a FireRed-style overworld, because interaction is not just “show text.” Interaction may start a script. That script may turn an NPC. It may move the player. It may check flags. It may give an item. It may start a battle. It may warp. It may open a shop menu. It may temporarily own the camera. It may return control only after several asynchronous visual actions have finished.

So, let’s be precise. When I say “handoff,” I mean that the overworld runtime detects that an interaction is valid, identifies the event source, and then transfers authority over player control to another state machine without stopping the frame loop. The frame loop keeps breathing. Tasks still run. Animation still advances. OAM still needs updates. Backgrounds still need to be drawn when something changes. What changes is not that the engine exits the runtime shell. What changes is which subsystem is allowed to interpret input and issue gameplay commands.

Put your eyes back on our board columns: position, facing, animation, camera, script. A player interaction touches all five, but it should not collapse them into one variable. Position says, “where is the player, and what tile is in front of them?” Facing says, “which direction is the query projected?” Animation says, “is the player idle enough to interact, and should the NPC turn?” Camera says, “if this script moves something, what portion of the map remains visible?” Script says, “what semantic event are we now executing?” Those are separate questions. They cooperate, but they are not the same question.

This is why the fieldmap chunks matter so much. In the packet, pokefirered/src/fieldmap.c chunk_642df7a31e75f54c is identified as fieldmap runtime support, chunk_eeb0180842f7ece9 as map block and tile behavior flow, and chunk_77ef7f3f8bff9777 as helper flow. That tells us something architectural, not just textual. The engine has a vocabulary for asking map questions. It does not need every consumer to reinterpret raw map data. The player controller, object event movement, encounter checks, and script-triggered movement can all stand on the same basic kind of question: given coordinates, what block is there, what behavior does it expose, and what does that imply for movement or interaction?

Now here is the subtle point. The tile ahead of the player is not always the event target, and the event target is not always the same kind of target. If the player is facing an NPC, the target is an object event occupying that tile. If the player is facing a sign, the target may be a background event attached to a coordinate. If the player is facing a counter with a clerk behind it, the interaction may be intentionally projected farther than one tile, depending on the engine’s interaction rules. If the player is standing on a coordinate trigger, the target may not be in front of them at all. We covered the data categories earlier, so I am not going to re-teach map headers, object events, warp events, coordinate triggers, and background events. But now you should see why that earlier separation pays off. At runtime, “interaction” is a query over several event layers, not a single if statement called talkToNpc.

Let’s do a concrete example. Imagine the player is in a Pokémon Center, one tile below the nurse, facing up. The player presses A. The engine first needs to know whether the player is in a state where A can be interpreted as field interaction. If the avatar is mid-step, no. If a script already owns input, no. If a menu is open, the menu gets the button, not the overworld. Assuming the player is idle, the facing direction gives us a target coordinate: player x, player y minus one, using the map’s coordinate convention. Then the engine asks, “Is there an object event occupying that coordinate?” If yes, it can identify that object event’s local id and associated script pointer or script label, based on the event data that came in through the map load sequence we discussed in the previous lecture.

But notice what the engine should not do yet. It should not immediately mutate every visible thing in the room inside the button handler. It should initiate the appropriate script or special field interaction, then let the script and object systems perform their own steps across frames. The nurse may turn down. The text box may open. The healing sequence may fade or play sound. Control may be returned after the script terminates. In FireRed architectural terms, this fits the larger source pattern we have been tracing since week one: visible behavior is downstream of data records, globals, callbacks, tasks, scripts, structs, and concrete source functions. That exact course thesis is echoed in the research synthesis: trace “data table to runtime globals to callback/task/script to player-visible behavior,” with FireRed as the primary authority.

Quick check for understanding. If I ask you, “Why not just put the nurse’s entire healing interaction in the A-button branch?” the answer is not “because that is ugly,” although it is. The deeper answer is that the A-button branch is a detector and dispatcher. It should decide whether the world supports an interaction now, and if so, which event should receive control. It should not become the interpreter for every possible event consequence.

Let me write a small model on the board. Not code from the repo, just a mental model. First line: input says A pressed. Second line: avatar state says idle and facing up. Third line: map query says target coordinate is x, y minus one. Fourth line: occupancy query says object event id 3 is there. Fifth line: event lookup says object event id 3 has script NurseScript. Sixth line: runtime says lock player control, turn object if required, start script. Seventh line: frame loop continues. That last line is the one people forget. “Start script” does not mean “freeze the machine.” It means “change the active authority while the machine continues to tick.”

This is also where collision and event scripting meet in a way that is easy to miss. Occupancy is a collision concern when the player wants to move. Occupancy is an interaction concern when the player wants to talk. Same fact, different question. If an NPC occupies the tile ahead, movement says, “blocked.” Interaction says, “candidate target.” That dual use is one reason object-event movement has to be designed carefully. The packet names pokefirered/src/event_object_movement.c as the high-signal source for object-event movement claims, and we use it alongside the fieldmap chunks because object events are not decorations placed after the map is done. They participate in the same runtime space. They occupy coordinates, animate through movement states, and need boundaries so that their autonomous motion does not invalidate map logic.

Now, let’s connect that to the lab you are about to do. Your lab grid will have metatile lookup, collision checks, camera offset, and object-event occupancy. If you build it well, your movement function will not know about “Professor Oak” or “Nurse Joy” or “a sign with text.” It will know about attempted movement from one coordinate to another. It will ask the map whether the destination metatile blocks movement. It will ask the object layer whether another object occupies the destination. It will either approve the move, reject the move, or report why. Separately, your interaction function will use the same coordinate projection but interpret an occupied tile as a possible event target. That separation is small in the lab and enormous in a real engine.

Let’s test the idea with a deliberately annoying case. Suppose an NPC is walking left and right on a two-tile path. The player presses A on the same frame the NPC begins stepping away. Is the NPC still interactable? Different engines can make different choices, but the architecture must make one choice consistently. You need some notion of current tile, previous tile, movement-in-progress, and maybe reserved destination. If the object has started moving, does it still occupy the old tile until the animation completes? Does it reserve the new tile immediately to prevent the player from stepping into it? Can the player talk to it mid-step? FireRed-style engines tend to treat movement as a tile-based state with animation layered over it, not free-floating physics. That means the object-event system has to bridge logical occupancy and visual interpolation.

And now the fieldmap helper flow becomes important again. The fieldmap is not merely a renderer. In the source focus, pokefirered/src/fieldmap.c chunk_77ef7f3f8bff9777 is called out for helper flow, while chunk_eeb0180842f7ece9 is specifically about map block and tile behavior flow. That means our mental model should include small, reusable helpers for coordinate-to-block and block-to-behavior queries. The interaction system should not decode the map layout by hand. The camera should not guess where blocks are. Object movement should not carry its own private copy of tile behavior semantics. When these systems disagree, you get bugs that feel impossible to reproduce: the player cannot walk somewhere the camera can draw, or an NPC can wander into a tile the player cannot enter, or a script moves an object through a wall because script movement bypassed normal collision.

Pause there. That last example is not hypothetical in engine design. If you have one collision path for player input and a separate collision path for scripted movement, they will eventually diverge. Maybe player movement checks water but scripted movement only checks walls. Maybe player movement respects object occupancy but scripted movement does not. Maybe NPC random walking respects boundaries but forced movement ignores them. Some exceptions are intentional. A cutscene may move the player through a door animation or onto a scripted tile. But intentional exceptions should be explicit. If bypassing collision is a feature, it should be named as a special mode, not accidentally caused by copying half of the movement code.

Let’s make the camera part concrete too, because the camera is easy to treat as a visual afterthought. Suppose the player interacts with an NPC who takes two steps backward during a script. The script says, “move NPC up, move NPC up.” If the NPC is near the top edge of the visible area, the camera may not move because the player did not move. But the object still needs to remain within the loaded and drawable portion of the map, or at least within the engine’s buffered region. FireRed’s camera-relative drawing model, which we discussed from the fieldmap source focus last segment, is built on the idea that screen tiles are a window over a larger map. The camera offset decides which map coordinates correspond to which background positions. Scripts that move objects do not get to ignore that. They may not directly scroll the camera, but their results are interpreted through the same camera-relative world.

Think about ledges for a moment. A ledge is a tile behavior question, not just a collision boolean. From below, maybe it blocks. From above, maybe it permits a jump action. That is why the packet’s emphasis on tile behavior queries in pokefirered/src/fieldmap.c chunk_eeb0180842f7ece9 is so important. “Blocked or not blocked” is too small as the universal interface. For simple movement, yes, you can reduce the result to pass or fail. But the richer architecture asks, “what behavior does this tile advertise?” Tall grass may be passable but trigger encounters. Water may be impassable unless surfing state is active. A door tile may be passable in the sense that stepping into it starts a transition rather than leaving you standing there. A counter tile may not be passable but may still support interaction across it. A sign tile may not block movement at all in the same way, but its coordinate can carry a background event.

So, the advanced version of your lab should avoid naming the central function isWall. Call it something like getTileBehavior or queryMetatileBehavior. Then have movement policy interpret behavior in context. Player walking context. NPC wandering context. Surfing context. Scripted forced movement context. Interaction context. The same metatile can yield different decisions under different policies. That is not overengineering; that is the essence of a data-driven overworld.

Now let’s bring in battle_setup.c briefly, because it shows why these boundaries matter beyond maps. The research packet cites pokefirered/src/battle_setup.c chunk_494d132a59422c85 and the excerpt shows it including overworld.h, event_object_movement.h, metatile_behavior.h, fieldmap.h, field_control_avatar.h, field_player_avatar.h, script.h, event_data.h, battle.h, and battle_transition.h. That include list is a clue. Battle setup is not an isolated battle-only island. It has to ask field questions. Where is the player? What tile are they on? Is this tall grass? Is this a trainer encounter? What script or transition should begin? But the fact that battle setup includes these contracts does not mean battle setup owns the map. It consumes field state through interfaces. That is exactly the architectural discipline we want: battle is entered from overworld conditions, but battle does not become the overworld renderer, the camera, or the object mover.

This is where week five connects forward to the broader course plan. In weeks three through five, we have been building the overworld map runtime. Starting next, when we lean harder into event scripting and then battle integration, we will keep returning to this handoff pattern. A script that starts a battle is not magic. It is a transition from one state machine to another, prepared by field conditions, mediated by callbacks and tasks, and eventually returned from. A shop script opening inventory UI follows the same shape. A party menu opened from an NPC gift follows the same shape. Save persistence has to capture enough of this world state to resume coherently, but not so much transient animation state that a save file becomes a frame dump. Link systems have to synchronize selected states without pretending every local rendering detail is network gameplay state.

Let me ask a diagnostic question. If your clone saves the camera pixel offset but not the player’s map coordinate, is that enough? No. The camera is derived presentation state. It can often be reconstructed from the map, player coordinate, viewport size, and scroll rules. If your clone saves the player coordinate but not object-event local state, is that enough? Sometimes, if all objects are stateless decorations or reset on reload. But if an NPC has moved because of a script, disappeared because of a flag, or is mid-cutscene, then you need a progression representation. FireRed’s real system has richer event flags, variables, object templates, and runtime object state, but the lesson for us is conceptual: persistent semantic state and transient frame state must not be confused.

Now, watch the chain from input to visible result in one complete example. The player stands outside a house door. Pressing up attempts movement, not A interaction. The tile ahead has door behavior. The movement policy may allow entry not by placing the avatar permanently on the door tile, but by starting a transition flow. That flow can set warp destination state, trigger fade, load the new map, initialize objects, and place the player at the destination. We already covered LoadMapFromWarp and warp destination state in the previous lecture summaries, so we will not redo them now. What matters today is that the door began as a tile behavior query. The same fieldmap machinery that helps answer “is this blocked?” also helps answer “does this tile initiate a transition?” This is the bridge from collision to event.

Now a different example. The player stands in front of a sign and presses A. The target coordinate has no object event. Movement occupancy is irrelevant. But the background event layer at that coordinate may provide a script. The engine does not say, “there is no NPC, therefore nothing happens.” It continues the interaction query through the relevant event layers. If it finds a sign script, it starts the script. The script opens a message box. The UI system handles text drawing and input progression. The overworld remains suspended as player-controlled movement but not dead as a runtime. When the message box closes and the script finishes, control returns. That is the same handoff shape as the nurse, but the target source differs.

Here is another check for understanding. In your lab, if the player presses A facing a blocked wall tile that has no background event and no object event, what should happen? Nothing visible, usually. But internally, that “nothing” should still be the result of a clean query. You projected a coordinate, checked object occupancy, checked background events, perhaps checked special tile behavior, found no interaction target, and returned control. A clean no-op is still designed behavior. A polished overworld is full of clean no-ops.

Let’s talk briefly about object movement boundaries, because this is the other half of preventing chaos. Object events on a map often have a home coordinate and movement range. A wandering NPC is not free to roam the entire loaded map. That boundary is part design, part performance, and part correctness. Design, because the mapper expects the NPC to stay in a local area. Performance, because local movement avoids expensive or surprising queries across the entire map. Correctness, because wandering into doorways, cutscene paths, or other NPC zones can break scripts. The event_object_movement.c source named in the packet is the place to keep in mind for these runtime movement claims, while our fieldmap.c chunks provide the map-query foundation underneath.

In your own engine, represent that boundary explicitly. Do not implement wandering by saying, “choose a random adjacent passable tile forever.” That sounds reasonable until a character slowly migrates across the whole town. Instead, choose from adjacent directions, then reject candidates outside the object’s allowed rectangle or radius, then reject candidates whose tile behavior forbids that object’s movement mode, then reject candidates occupied or reserved by another object, then begin a movement animation if one remains. Again, refusal is part of the engine. Most candidate moves by random walkers should be rejected quietly. That is not failure; that is how authored space remains authored.

Now, if we connect this back to camera-relative drawing, we get an important invariant: logical coordinates should remain stable even when screen coordinates shift. An NPC’s map x and y do not change because the camera scrolls. The object’s sprite screen position changes because screen position equals map position minus camera origin, plus animation offset. That formula, conceptually, is the whole bridge. Map coordinate gives identity in the world. Camera origin gives viewport. Animation offset gives in-progress movement. If you mix these together too early, you will suffer. You will try to use sprite pixels for collision. You will let camera scroll change object identity. You will discover that talking to an NPC works only when the camera is in one position. That is the signature of a system that confused world space with screen space.

I want you to notice how often the same architectural sentence repeats: keep semantic state separate from presentation state, and connect them through clear queries. Metatile id is not yet behavior. Behavior is not yet movement policy. Movement policy is not yet animation. Animation is not yet camera. Camera is not yet script. Script may command movement, but it should do so through the movement system. Script may open UI, but it should do so through the UI system. Battle setup may be triggered by tile behavior, but battle should enter through a transition boundary.

That is the level of rigor I expect now, because by week five you have already had homework and quiz work on decomposing the RPG into runtime shell, overworld maps, event scripting, creature data, battle, inventory, UI, save, and link systems. You have also already designed minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence. So a week-five answer that says “collision checks if there is a wall” is no longer advanced enough. I want to hear: collision is a context-sensitive policy over map block lookup, metatile behavior, object occupancy, movement state, and sometimes script authority. That is the answer at our current level.

Let me finish this segment with a compact synthesis before we move into the next board pass. The overworld is not one loop that moves a sprite around. It is a negotiated runtime among map data, tile behavior, object events, camera rules, input state, animation state, and scripts. Fieldmap support, as highlighted in pokefirered/src/fieldmap.c chunk_642df7a31e75f54c, gives the runtime a way to talk about map space. Map block and tile behavior flow, highlighted in chunk_eeb0180842f7ece9, lets systems ask what the world permits or implies. Helper flow, highlighted in chunk_77ef7f3f8bff9777, is what keeps those questions reusable rather than copied into every feature. Object-event movement then sits on top of those questions and adds occupancy, local boundaries, animation, and interaction targets.

So keep this in your head as we continue: pressing a direction, pressing A, an NPC taking a random step, a script moving the player, a door transition, a grass encounter, and a camera scroll are all different surface behaviors over the same underlying discipline. They ask structured questions about map coordinates and runtime state, then hand authority to the subsystem responsible for the next phase. Next, we are going to tighten that into the sample movement-attempt algorithm for the workshop, because I want your lab implementation to expose the same separations. We will write it as a sequence of runtime queries rather than as one giant movement function, and then we will run several attempts through it: ordinary walking, blocked wall, occupied tile, sign interaction, wandering NPC boundary, and a camera edge case.

All right, let’s put the movement-attempt algorithm on the board. I’m going to write it in deliberately boring language, because boring is the correct shape here: read current position, compute candidate position, query map block, query tile behavior, query object occupancy, decide movement result, then maybe update camera and maybe trigger a script. Notice the word “maybe.” That is where architecture lives. The player does not “just move.” The player attempts movement, and the runtime classifies the attempt.

So, suppose the player is standing at local coordinate 12, 8, facing east. The input system gives us direction east. The movement layer computes candidate coordinate 13, 8. At that point, if your lab code immediately changes player.x to 13, you have already collapsed three systems into one assignment. The FireRed-style version asks the map system, “What is at 13, 8?” That is the metatile lookup path we discussed last time, grounded in pokefirered/src/fieldmap.c, especially chunk_642df7a31e75f54c for fieldmap runtime support and chunk_eeb0180842f7ece9 for the map block and tile behavior flow. The important thing is not merely that there is an array. The important thing is that the array is accessed through a runtime query boundary.

Now, next board line: “candidate block gives metatile id plus collision/behavior interpretation.” If the candidate tile is a wall, the movement result is blocked-by-terrain. The avatar may turn to face east, the bump animation may play, but the position does not change. If the tile is floor, the result is not yet success. We still ask whether some object event occupies the candidate coordinate. This is where the source packet’s note about pokefirered/src/event_object_movement.c matters. We are not using object events as decorative sprites. They are active participants in movement authority. A wandering NPC, a trainer, a cutscene actor, or the player avatar itself can reserve space in the same coordinate world that the tilemap uses.

Here is the concrete example I want you to run in the workshop. Build a 10 by 8 grid. Put a wall metatile at 4, 3. Put a sign behavior at 2, 2. Put an NPC object at 5, 5 with a movement rectangle from x equals 4 through 6 and y equals 4 through 6. Put the camera viewport at world origin 0, 0 with a visible width and height smaller than the map. Now run six attempts. Player at 3, 3 moves east into 4, 3; terrain blocks. Player at 5, 4 moves south into 5, 5; occupancy blocks. Player at 2, 3 faces north and presses A; movement does not happen, but interaction queries 2, 2 and finds sign behavior. NPC at 6, 5 attempts east; the tile may be open, but the NPC’s local boundary blocks. Player at 7, 4 moves east; movement succeeds, and camera offset may scroll if the player crosses the camera’s comfort zone. Finally, player near the map edge moves toward the edge; movement may succeed while the camera remains clamped because drawing cannot reveal outside-map space.

Pause there. Check your understanding: why do I want blocked wall, occupied tile, and boundary violation to be three different outcomes if all three mean “the actor did not enter the tile”? Right, because the response is different. Terrain collision can play a bump and maybe suppress scripts. Object occupancy can turn the player toward an NPC, or let the A-button interaction target that object. Boundary violation for an NPC may simply choose another random movement direction next frame. Same surface result, different architectural meaning.

Let me connect that to camera-relative drawing. The camera is not the map. The camera is a viewport over the map. That is why pokefirered/src/fieldmap.c chunk_77ef7f3f8bff9777 is useful as a helper-flow reference: it reinforces that fieldmap support is a set of transformations and queries, not one monolithic “world” object that owns every decision. When your lab renderer draws, it should not ask, “Where is tile 0, 0 on screen forever?” It should ask, “Given camera offset cx, cy, which map coordinate appears at this screen cell?” Then, when movement changes the player’s world coordinate, the camera may update its offset, and only then does drawing reinterpret the visible region.

This is also the reason we spent so much time earlier on runtime callbacks and tasks. We are not re-teaching CB2_Overworld today, but keep it as the frame around this work. The overworld callback is the per-frame stage on which input, movement, scripts, object events, and drawing cooperate. The same course thesis still holds: data table to runtime globals to callback, task, or script to visible behavior. In week one, that may have sounded abstract. By week five, you can now point to a coordinate, a metatile id, a behavior query, an object slot, and a camera offset and say exactly where visible behavior came from.

One more source-backed bridge before we close. In pokefirered/src/battle_setup.c, chunk_494d132a59422c85, the include list itself is a useful architectural clue: battle setup touches overworld, script, event object movement, metatile behavior, and fieldmap concerns. That does not mean collision code runs the battle engine. It means the transition from walking to encounter to battle is a handoff across systems. Grass behavior, trainer sight, scripted events, and map position all prepare the condition under which battle setup can take authority. That is the creature-collector architecture in miniature: no subsystem owns the whole game; each subsystem owns a phase change.

So for your homework, when you implement the movement attempt, do not give me a heroic function called moveEverything. Give me small named queries. “Get block.” “Get tile behavior.” “Is terrain passable.” “Is occupied.” “Is inside movement range.” “Apply movement.” “Update camera.” “Check interaction or trigger.” If those are separate, you can test them separately. If they are separate, you can later attach scripts, encounters, ledges, surfing, doors, and trainer vision without rewriting the whole loop.

Let’s recap the week in one pass. Tiles are not just art; at runtime, they are queried data. Collision is not just a yes-or-no property; it is a decision assembled from terrain, behavior, actors, elevation-like constraints, and movement context. The camera is not the player; it is a constrained view over world coordinates. Object events are not just sprites; they are coordinate-owning runtime actors with their own movement rules. And the overworld, as a system, is not a bag of special cases. It is a disciplined set of state machines asking structured questions in a stable order.

That closes our fieldmap week. Next time, we begin using this same discipline to reason about event scripting more aggressively: how a coordinate result becomes a script condition, how a script temporarily takes control away from free movement, and how the game returns authority to the overworld loop afterward. Keep today’s algorithm handy, because it is going to become the hinge between map mechanics and story behavior.

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