Roamers - pret/pokeemerald GitHub Wiki

Roamers

When the player loads the game after beating the Elite Four, they'll find themselves in their bedroom in Littleroot Town. When they go downstairs, a lengthy cutscene will play, ending in the player seeing a TV news report announcing the arrival of a roaming Pokemon.

Roamers are Wild Pokemon that don't appear in fixed locations; rather, at most one roamer exists at a time, wandering across different routes. If the player and the roamer happen to be on the same route, the player can encounter them at random. Roamers always try to flee from battle, but their health and status condition are tracked persistently and will still be present in the next battle.

Movement algorithm

When playing casually and tracking a roamer via the Pokedex, it's easy to see that roamers typically travel along the world map: when a roamer moves from its current route, it will move to an adjacent route. (If two routes are separated by a city, then the routes are considered adjacent: the roamer will skip past the city.) Roamers may also randomly teleport across the world map instead of pathing.

The pathing system doesn't dynamically examine map connections or any similar data. Rather, the sRoamerLocations variable acts as a precomputed table, mapping each route to a list of its adjacent routes. Because roamers only travel one node at a time, and don't travel toward any particular goal, no real pathfinding algorithm is needed; the roamer just looks up its current route in the table to find all adjacent routes to which it can travel, and it then tries those routes at random until it finds a suitable one.

Roamers remember their current location (sRoamerLocation, singular) and their previous two locations (sLocationHistory). When pathing, roamers won't revisit a route if they were there two moves ago. For example, if a roamer starts at Route 110 (south of Mauville), travels to Route 111 (north of Mauville), and then hops to Route 117 (west of Mauville), its next move may bring it to Route 111, but not to Route 110. In other words, roamers will never "close a triangle" when pathing. They can move back and forth between a pair of routes, however.

Every time a roamer moves, it has a 1 in 16 chance of teleporting to a random route, instead of pathing to an adjacent route. It's worth noting that it can teleport to an adjacent route, which to the player would be indistinguishable from pathing. It cannot, of course, teleport to the route it's already in.

Roamers will move (pathing or teleporting) whenever the player crosses over a map connection. They'll teleport whenever the player passes through a warp or reloads their save file. They also teleport after you battle them.

Implementation details

As mentioned above, roamers don't consult map connections, the region map data, or anything else for their pathing. They rely on a precomputed set of data indicating where and how they can move — a waypoint graph, where each route is a single node. The graph is stored in the sRoamerLocations variable as an adjacency list, formatted as a two-dimensional array of routes' map-nums. Each inner array lists a single route's map-num, followed the map-nums of all adjacent routes. Arrays are fixed-length; when a route has fewer than the maximum possible number of adjacent routes, a dummy value is used to fill the remainder of the inner array.

An adjacency list should contain every node in the graph. Simply put: if the roamer is capable of traveling from one route to another, then both routes should have a row in the sRoamerLocations table. If you break this rule, then the pathing algorithm will misbehave. For example, if you add route X to the list of routes adjacent to route Y, but you don't then add a list of all the routes adjacent to X, then it'll be possible for the roamer to travel from Y to X, but it will then be incapable of leaving X except by randomly teleporting.

An adjacency list should always be "symmetrical:" if X lists Y among its adjacent routes, then Y should list X among its adjacent routes. If you break this rule, all sorts of havoc can result. The roamer.c file calls out one possible risk of an infinite loop: if it's possible for the roamer to move from X to Y to Z, or from X to Z, but they can only travel from Z to X (as depicted below), then an infinite loop may occur. Roamers refuse to revisit a map they've been to two hops ago — they refuse to "close a triangle" — so if the roamer travels the path X to Y to Z, then their only movement option will be to return to X, which isn't allowed. The roamer code will continue rolling random numbers to try and find a suitable adjacent map to Z, forever.

pathing

The adjacency list must contain at least two routes. Specifically, there must be at least two inner arrays that don't start with the same map-num. Roamers teleport by rolling a random number, using it as an index into the adjacency list, and checking if the route they get is different from the route they're currently on. If there's only one row in the adjacency list, or if all rows are for the same route, then the roamer will enter an infinite loop if it tries to teleport out of that route.

⚠️ **GitHub.com Fallback** ⚠️