Movement & needs¶
Movement speed¶
Tile factors¶
Per-tile speed factor (1 = normal; < 1 = slower; > 1 = faster). From MovementSpeed.ts:
| Tile type | Factor |
|---|---|
| grass | 1 |
| sand | 0.5 |
| stone | 1.25 |
| shallow_water | 0.2 |
| mountain | 0.3 |
| deep_water | 0 (unwalkable) |
Walkable objects¶
Whether a tile object can be walked through is defined on the object (sim definition), not in the movement handler:
- AbstractObject has
isWalkable(): boolean(defaultfalse). - Objects that can be walked through override it to return
true(e.g. PineTreeObject, StoneChunkObject, WoodLogObject, StoneBrickObject).
The MovementSpeed handler is generic: it calls object.isWalkable() and, for speed, treats stack-like objects (with getQuantity/getMaxStack) as full speed and other walkable objects as 0.3× (PASS_THROUGH_SPEED_FACTOR). Pathfinding and blocking use isObjectWalkable(obj) from the same module.
Pathfinding¶
- PathFinder uses A* with 8-direction movement
- Step cost =
1 / speedFactor(higher cost = slower) - Unwalkable tiles get
Infinitycost getStepCostAt(x, y, deps)uses both tile and object factors
Step timing¶
- MOVE_INTERVAL_MS = 420 ms (base step duration)
- Step duration =
MOVE_INTERVAL_MS / speedFactor - Faster terrain → shorter steps; slower → longer steps
- JobMovement uses
getSpeedFactor(next.x, next.y)for the next tile
Needs¶
Need types¶
| Need | Decay | Fulfilled by |
|---|---|---|
| sleeping | 0.35/sec | SleepJob |
| drinking | 0.35/sec | DrinkJob |
| eating | 0.35/sec | (not yet) |
| recreation | 0.35/sec | (not yet) |
Decay¶
- Each need: 0–100, decays at 0.35 per real second
ColonistObject.tickNeeds(deltaTimeMs)called each frame- Returns
{ drinkingCrossed, sleepingCrossed }when need drops below threshold
Threshold¶
- LOW_NEED_THRESHOLD = 10
- When need crosses from >10% to ≤10%, emit
needFulfillmentRequired - Threshold flag prevents repeated events until need is fulfilled
Fulfillment¶
- Drinking:
fulfillDrinking()– set to 90%, reset threshold - Sleeping:
fulfillSleep()– set to 90%, reset threshold - Called by DrinkJob/SleepJob on completion
Need-driven job creation¶
Need decay and job creation are driven by colonist behaviors (see Behaviors). A needCheck behavior runs every tick: it calls colonist.tickNeeds(deltaTime) and, when drinking or sleeping crosses to ≤10%, calls context.requestNeedJob(colonistId, 'drinking'|'sleeping'). The client context implementation then calls NeedJobScheduler.createJobForColonist(id, needType). NeedJobScheduler creates a DrinkJob or SleepJob if the colonist is free and need ≤10% (or when forced from the UI).
Drink job¶
- Destination: nearest tile adjacent to shallow_water or deep_water
findNearestWaterAdjacentTile(fromX, fromY)– scans map for water, then adjacent walkable tiles
Sleep job¶
- In zone: destination = nearest tile in sleeping zone; duration 15 s
- Outside zone: sleep at current tile; duration 30 s
Force from UI (Needs tab)¶
- Needs tab in the citizen panel has "Go drink" and "Go sleep" buttons.
- Clicking either cancels the colonist's current job (if any), then creates a DrinkJob or SleepJob for that colonist (with
force: true). - Force-created jobs bypass the low-need threshold and
DEBUG_HAUL_ONLYin NeedJobScheduler; JobAssigner still assigns them when they are in the queue.
Batch mode (legacy)¶
createDrinkJobs()/createSleepJobs()– scan all colonists, create jobs for thirsty/sleepy- Prefer event-driven flow; batch kept for fallback