Skip to content

Movement & Time

Every entity’s movement and the game’s calendar are driven by the same function, ProcessMovement, called once per game tick to advance exactly one entity. Full byte/instruction-level detail: docs/wime/amiga/movement-system.md and docs/wime/amiga/timer-system.md in the main repo (not part of this site).

Time in War in Middle Earth has three nested levels, and time only advances once every entity has had a turn — there’s no independent real-time or per-frame clock:

1 call to ProcessMovement = try to move 1 entity
151 calls (the full roster) = 1 sub-day tick (15 game-minutes)
96 sub-day ticks = 1 day

The calendar spans 8 months matching the War of the Ring timeline, September 3018 through April 3019 of the Third Age. One quirk survives into the original release: the calendar never advances past March in the shipped game — the month index clamps at March while the day count keeps climbing, producing dates like “March 102nd.” (This project’s own reimplementation fixes the day/month rollover; the original game’s bug is documented here for accuracy.)

Day/night doesn’t change movement speed — it changes whether units move

Section titled “Day/night doesn’t change movement speed — it changes whether units move”

There’s no direct day/night speed penalty. Instead, during the “night” window (roughly the middle third of each day), every entity currently moving has a 1-in-10 chance, each time it’s processed, of switching to a camped/resting state. This is why units don’t all stop simultaneously at a fixed hour — camping is gradual and probabilistic, one unit at a time.

Movement is not a global pathfinding search — there’s no “plan a route across the whole map” step. Instead, each entity’s turn does local, greedy terrain scoring:

  1. Compute a preferred compass heading straight at the entity’s current destination.
  2. 90% of the time, sample all 8 neighboring directions and terrain-score each one (scores come from the movement speed tables, 13 tables keyed by the destination tile’s low nibble and the mover’s movement class); the other 10%, just check the straight-ahead tile and commit to it if its score clears a low bar.
  3. Pick whichever direction scores best, with the exact-reverse direction penalized so units don’t flip-flop in place.

This local scoring is the entire explanation for behavior that looks much smarter than it is:

  • Units snap onto and follow roads simply because road tiles score better than open terrain in the local comparison — there’s no explicit “prefer roads” rule, just better numbers.
  • Units avoid water and use bridges the same way: a direct heading into water scores badly, so the local search steers along the bank until a bridge tile appears in the 8-direction neighborhood, at which point it wins the comparison and gets taken.
  • Units route around mountains identically — poor terrain scores force local sidestepping until a pass or road tile becomes visible.

There is genuinely no global “find the nearest bridge” or “search the mountain range for a pass” algorithm anywhere in this code path. The illusion of intelligent routing comes from combining this local scoring with a second, separate layer:

Above the tile-by-tile steering sits a waypoint queue: each moving entity that’s following a route consumes queued destinations one at a time from an 8-byte slot table, loading the next (destX, destY) once the current one is reached. This is the same 86-route, DATA+0x3B00 table described in Combat & Events § “Routes” — so the full picture is: pre-authored waypoint routes provide the long-range plan, and per-tick local terrain scoring keeps the entity on passable ground between waypoints.

Field Role
posX/posY, destX/destY Current position and current-leg destination
facing Current compass heading (0–7); also drives the compass direction shown in status text
moveState 0 = inactive, 1 = camped/resting, 2 = locked/busy, 3 = actively moving
moveClass Which of 13 terrain-speed tables this entity uses
moveAccum A fractional speed accumulator — terrain score adds to it each tick, and only once it crosses a whole-tile threshold does the entity actually move a tile, so faster units/terrain combinations can move multiple tiles in one tick
linkedEntity Non-zero means “following another entity” — this entity is skipped by the normal movement logic entirely

The status line the game shows for a selected entity (e.g. “Frodo is going to Rivendell.”, “Aragorn is heading north.”, “Sam is resting.”) is assembled directly from moveState plus whether the destination is near a named location:

moveState Status shown
0 (not displayed — inactive)
1 “resting.”
2 (context-dependent — locked/busy)
3, destination near a named location “going to <location>”
3, no named location nearby “heading <direction>”

The game’s actual movement vocabulary is “following”, “heading”, “going to”, “passing by”, and “resting” — notably, the word “traveling” never appears anywhere in the original game’s strings.