Home Workers & Factory Automation

Workers, Residences & Factory Automation


5.1 Worker entity

Workers are npc_instances (19) with employer_id — same stats, inventory, and death rules as other NPCs; plus job state (13).

worker:
  id: worker_ana
  name: Ana
  employer_character_id:...
  residence_id: residence_b
  lodging_bed_id: bed_2
  wage_account: employer_bank   # deducted per tick
  state: working | traveling | resting | idle | dismissed
  attributes:                   # same families as players (`11`)
    STR, DEX, STA, VIT,...
  skills:
    logging: 12
    hauling: 8
    carpentry: 5
    trading: 3
  pools:
    stamina: { current, max }
    hunger: { current, max }
  inventory: WorkerCarrier       # limited slots — mass/volume (`08`)
  equipment:                     # job outfit
    hands: iron_axe
    body: work_tunic
    pack: small_backpack
  active_job_id: job_lumber_loop

5.2 Hire & dismiss

Action Cost
Hire Up-front fee + a daily wage (employer sets; minimum floor per ruleset). First paycheck is the next game day
Dismiss Worker leaves only after backpay is settled. Firing does not erase debt or keep their level

Hire from labor broker NPCs in towns or recruit after quests. Worker cap per residence from building tier + CHA (optional).

5.3 Training

Employer pays training fee (coins from bank or residence treasury):

training_session(worker, skill, fee) → skill_xp += f(fee, worker.VIT, teacher_quality)

Training can be passive on-job (slow XP while doing related tasks) or active (paid courses at guild hall — faster). Skilled workers: faster harvest, shorter craft ticks, better yields, fewer failures.

5.4 Wages, effort & silver sink (implemented)

Workers are a deliberate coin sink: harder routes cost more payroll, and skilled workers charge more while doing more labor per day.

Payroll formula (once per game day)

Wages bill when the world-clock day rolls, on the same day the ledger uses. A worker hired today is first billed at the next rollover. wage_interval_ticks is unused.

due_cp = round(
  (base_cp + finished_step_cp + travel_cp) * level_wage_mult * army_wage_mult
).max(1)
Component Source
base_cp Employer-set wage.copper_per_interval (hire min from NPC def). Defenders use base × defender_wage_mult plus equipped gear value, even on a quiet day
finished_step_cp Effort for steps finished today × wage_copper_per_effort. A harvest route pays harvest_route per node completed, once per node per day. A second pass of the same step does not add again. Idle and companion with nothing finished pay the idle rate only — a suspended route is not billed. A job loop that finished nothing pays no loop effort
travel_cp Meters walked this interval × travel_copper_per_m
level_wage_mult 1 + (level − 1) × wage_per_level (default 0.03)
army_wage_mult 1 + (hired − 1) × army_wage_scale_per_extra

Fight and ability fees are added for defenders only when those actions happened. Strike days add 0; frozen backpay is unchanged.

Step costing rules (the content files → workers.step_costs):

Step kind Default effort Notes
harvest_route 2.2 per node 20-node route ≈ 44 effort units
harvest 2.0 Single-resource stop
craft 1.8 Per craft stop in the compiled loop
travel 0.7 Flat complexity; meters paid separately
broker_sell 0.8
deposit / withdraw / pickup ~0.45–0.55
rest / wait / idle 0.15–0.3 Cheap parking

A companion or idle worker with no finished steps pays the idle rate. The rates above are what a finished step of that kind adds.

Pay sources (treasury order)

  1. Employer bank ledger (automatic paycheck withdraw; works while offline while the region still tracks the character)
  2. Assigned lodging container contents
  3. Other employer-owned placed chests within 12 m of lodging (or employer if no lodging)
  4. Employer carried coins (root + worn bags)

Other player expenses (taxes, shipping fees, shop buys, hire/train fees, property upkeep) use the same bank-then-purse rule via try_spend_copper.

If the full amount cannot be paid, that day's wage is added to backpay and the worker enters Strike (job loop halted). They do not work, and they cannot be dismissed, until the entire backpay is paid. A short payroll does not fire them. While striking, collection retries about every 5 s so restocking lodging, chests, or the bank clears the debt without waiting for the next day. Further days on strike do not add another paycheck: pay stops when work stops, so time away cannot grow a debt the idle worker cannot earn down. The next worked day bills normally again after the frozen backpay is settled.

Hunger / thirst

Per-tick drain is multiplied by the current step’s effort rate (hunger_effort_scale / thirst_effort_scale). On lodging rest_if_needed, workers eat/drink one pantry consumable from the assigned bed container only when hunger or thirst is below 65% — stock the bed itself with soup/water (nearby chests are not scanned for meals; that path is wages-only).

Level bonuses (skilled workers)

Bonus Formula (capped) Effect
Wage 1 + (L−1)×wage_per_level (default 0.03) Higher daily payroll
Labor labor_per_day + (L−1)×labor_per_level (40 + 2 per level) More harvests and crafts that day
Harvest speed duration ÷ 1+(L−1)×0.03 (cap 1.75×) Shorter channels
Craft speed same shape Shorter craft ticks
Move speed × 1+(L−1)×0.02 (cap 1.4×) Faster travel

Level is worth keeping. A level-20 laborer costs about ×1.57 wages and gets 78 labor instead of 40 (about ×1.95 output), plus faster channels and walking so the larger pool still fits in the workday. Dismissing them to hire a fresh level-1 worker throws away that output and still requires the backpay to be paid first.

Worker XP pacing

Cumulative XP → level via xp_base × growth^(L−1) ceilings. Defaults: xp_base: 2000, growth: 1.65, awards 0.35 per harvest/craft/sell, 1.0 per route wrap, soft-capped at xp_max_per_route_loop: 8 so craft-until-exhausted cannot dump a level in one loop. Expect ~80+ gatherer loops to L2 and hundreds to mid levels.

Client UI

Workers dock shows today's accrued pay and, when the authored route would cost more, that quote (due→quote cp). The i ledger Labor section lists each worker's accrued daily pay, route sales estimate, net, and any backpay.

If the employer cannot pay → worker strikes until the full backpay is settled. Dismiss is refused while backpay remains.


7.1 Job definition

A job is a repeating cycle of steps. One active_job per worker.

job_id: job_chop_to_storage_a
worker_id: worker_ana
enabled: true
checkpoint: true              # resume mid-cycle after rest
steps:
  - step: travel
    target: { poi: forest_patch_7 }      # harvest node / landmark / coords

  - step: harvest
    resource: oak_log
    tool: iron_axe                       # must be equipped
    until: { inventory_full: true } | { qty: 20 }

  - step: travel
    target: { storage: storage_house_a }

  - step: deposit
    storage: storage_house_a
    filter: { items: [oak_log] }
    deposit_all: true

  - step: travel
    target: { lodging: lodging_b }       # optional: only if stamina low (condition)

  - step: rest_if_needed                 # implicit between cycles
job_id: job_mill_planks
worker_id: worker_ben
steps:
  - step: travel
    target: { storage: storage_house_a }

  - step: withdraw
    storage: storage_house_a
    items: [{ template: oak_log, qty: 10 }]

  - step: travel
    target: { device: wood_mill_1 }

  - step: craft
    device: wood_mill_1
    recipe: lumber_to_plank
    qty: 10                              # or until inputs exhausted

  - step: travel
    target: { storage: storage_house_b }

  - step: deposit
    storage: storage_house_b
    filter: { items: [hardwood_plank] }
job_id: job_sell_planks
worker_id: worker_cara
steps:
  - step: travel
    target: { storage: storage_house_b }

  - step: withdraw
    storage: storage_house_b
    items: [{ template: hardwood_plank, qty: 50 }]

  - step: travel
    target: { broker: market_c_broker }

  - step: broker_sell
    broker: market_c_broker
    items: [{ template: hardwood_plank, qty: all }]
    pricing: { mode: market | premium_pct: 5 | fixed_silver: 2 }
    currency: silver                     # proceeds → employer bank (`10`)

7.2 Step types

Step Behavior
travel Pathfind to target; time = distance / move_speed; stamina cost
harvest Gather from world node (lumber, ore, herb); skill + tool modify tick time & yield
withdraw Pull items from storage to worker inventory
deposit Push items from worker inventory to storage
craft Run recipe at device; uses worker skill for speed/failure
enchant Run enchant ritual at altar; mana/stamina from worker pools; partial reagent loss on fail (17)
broker_sell List or instant-sell at broker (10); proceeds to employer
stall_sell Optional: player stall instead of broker
rest_if_needed Branch: lodging if stamina/hunger thresholds
wait Rare: wait for input stock (mill blocked until logs arrive)
harvest_route Ordered harvest over specific node ids until carry threshold or nodes exhausted (compiled from route, see §7.3)

7.3 Harvest loops — route block (implemented)

A high-level route on the job compiles server-side into the travel/harvest/deposit step cycle — this is what the gfx route editor saves:

job_id: route_oak_loop
mode: job_loop
route:
  kind: harvest_loop
  lodging_container_id: bed-1
  outbound_waypoints: [{ x: 12.0, y: 10.0 }]
  harvest_nodes: [oak-1, oak-2]
  carry_return_ratio: 0.90      # head home when carry ≥ 90%
  loop_wait_ticks: 90           # pause at home between loops
steps: []                       # compiled from route

Compiled cycle: travel waypoints outbound → harvest_route over the listed nodes → travel waypoints reversed → travel to lodging bed → deposit all → rest_if_needed → wait → repeat.

Runtime reliability rules (harvest route):

  • Drop-centric collection — after a harvest channel completes, the worker walks to each scattered loot pile itself and picks it up by drop id. Loot scatters up to ~1.2 m past the fixed harvest stand position, so chasing the drop (not the node) is what keeps collection reliable; far-side scatter can never stall the loop.
  • Never abandon mid-collect — carry-full is only checked at node arrival and at pickup time (hard carry cap → head home to deposit); a node in progress is always finished and collected first.
  • Path-failure skip — a node or waypoint with no viable path for ~3s is skipped with a no path … worker error instead of stalling the route; leftover loot at a depleted node is re-collected on a later loop.
  • Rest rules — stamina only regens while idle, so the loop paces itself: a worker too tired to start a harvest channel rests in place and resumes the same node once recovered (never an insufficient stamina retry spin); rest_if_needed at the bed tops stamina up to 90% so every loop starts rested; with no active job workers rest in place like any worker.
  • Carry capacity — route carry thresholds and encumbrance use the shared agent carry snapshot (same source as the sim's pickup/encumbrance gates), so heavy loads (e.g. oak logs) never fake a "full" reading after one node.
  • Overburden redirect (≥ 95% carry) — a worker must not keep traveling to harvest / withdraw / pick up when already loaded. The job loop looks ahead for a step that reduces burden now (deposit, craft that shrinks mass with held recipe inputs even if a tool/station/stamina is blocking, or broker_sell with matching held items — not merely travel-to-broker). If one exists, the worker jumps to it and runs that step the same tick (sell/deposit/craft path themselves when out of range; craft surfaces tool/station blockers or rests for stamina instead of parking at bed). While Resting at lodging for a 90% bed top-up, an overburdened worker with a usable offload wakes at the mid-route recovery line (~40%) so they can saw/deposit/sell. If the plan has no usable offload action, the worker paths to their bed/lodging and surfaces status overburdened — holding …; no deposit/craft/sell step can lighten the pack.

7.4 Timing

tick_time(step) =
  travel:  path_distance / effective_move_speed(worker)
  harvest: base_harvest_ticks / (1 + logging_skill × coeff) × qty
  craft:   recipe.craft_time_ticks / (1 + relevant_skill × coeff)

Travel time is first-class — distant forest + far mill = hire haulers or build closer storage.

7.5 Conditions & branching (Phase 6b+)

- step: withdraw
  storage: storage_house_a
  items: [{ template: oak_log, qty: 10 }]
  on_fail: { wait_ticks: 60, retry: true }   # mill idle until supply

8. Example factory line

Player owns residence B with lodging (2 beds), wood mill, links to storage A (near forest) and storage B (near residence).

Worker Job cycle
Ana Forest → chop oak_log → deposit storage A → rest → repeat
Ben Withdraw logs from A → mill at residence B → planks to storage B → repeat
Cara Withdraw planks from B → travel to market C → broker sell for silver → bank proceeds to employer → repeat
  [Forest] ──Ana──► [Storage A] ──Ben──► [Mill @ Res B] ──► [Storage B] ──Cara──► [Broker @ Market C]
                         ▲                                              │
                         └──────────── rest / food @ lodging B ◄─────────┘

Player stocks lodging pantry, pays one wage per world-clock day (base plus finished steps plus distance), outfits Ana with axe, maintains mill durability, and monitors profit = broker revenue − wages − training − food − fuel.

A level-1 crew that does this correctly is meant to come out ahead. One labor-day of a two-yield gather (about 80 units) sells to the matching merchant at full price, and that revenue clears the minimum wage with room for the walk. Milling or smelting pays about half again what selling the raw stack would, which covers the miller's wage. A second crew the same day hits the warm price band. The merchant's purse and those bands are what keep a large crew from emptying the town.


9. Outfitting

Job type Typical equipment
Lumber axe, hands, backpack
Mining pick, lantern, heavy pack
Hauling large pack, cart (later)
Milling / forge tongs, apron (recipe tool reqs)
Trading light pack, market token

Missing required tool → step fails / worker idles with error to employer the terminal client. Tools have durability — employer must repair or replace.


10. Economy integration

Flow Handling
Wages Debit employer bank or residence treasury once per game day; one unpaid worked day becomes backpay, and further strike days do not add pay
Training One-time fee to guild / trainer NPC
Broker sales Proceeds to employer bank (config); physical coins not hauled unless withdraw_coins step
Currency Copper, silver, gold, platinum (08 §8.5)
Taxes Optional region sales tax on broker_sell (10)

Workers do not need player online — sim runs on region worker while chunk is active (see §12).

Job-loop workers keep a workday. They harvest, craft, broker-sell, and list only during workers.work_phases (default: dawn through evening). Night sends them home to rest. At dawn each pool refills to labor_per_day + (level−1)×labor_per_level (default 40, plus 2 per level above 1). A finished harvest node spends labor_per_harvest; a finished craft batch spends labor_per_craft. An empty pool rests the same way as night. Below meal_need_ratio they do not start harvest or craft; they eat from pack or lodging, or idle at the lodge. Stamina does not regenerate during a harvest or craft channel. Players are not on this clock.


11. the terminal client / agent

flatland residence list
flatland residence link storage residence_b storage_house_a
flatland worker hire --residence residence_b --name Ana --wage 5_copper_per_100_ticks
flatland worker assign-bed worker_ana bed_2
flatland worker equip worker_ana --slot hands iron_axe
flatland worker train worker_ana --skill logging --fee 2_silver
flatland worker job set worker_ana --file jobs/ana_chop.yaml
flatland worker job enable worker_ana
flatland worker status worker_ana    # step, stamina, location, errors
flatland factory status residence_b  # all workers, storage levels, device queues

Agent JSON: full job state, storage snapshots, P&L estimate.


12. Simulation & region lifecycle

Case Behavior
Region active (players nearby or policy keeps hot) Workers tick normally
Region cold / unloaded Workers pause; state frozen; no wages accrue (config) or reduced accrual
Employer offline Workers continue if region active

Anti-exploit: cap workers per account; audit broker_sell for wash trading; residence must be in valid claimed territory.


13. Phasing

Phase Scope
5 Craft residences (building shell), plot prep, fixture anchors (20)
6a Hire 1 worker, simple job: harvest → deposit (1 storage)
6b Multi-step jobs, withdraw/craft/deposit, lodging + rest
6c Wages per tick, training, skills on job
6d Broker_sell step, factory dashboard the terminal client
7 Multi-worker lines, job conditions, carts, cross-storage wait

15. Settlement workers (NPC towns)

Player-hired workers (§1–§14) and settlement workers share the same job engine (09, 18):

Player workers Settlement workers
Owner Player / guild Authored settlement profile
Output Any recipe player configures Basic only — short/long sword, shield, leather armor, hardtack
Risk PvP death, evil, wages No player-style permadeath; treasury-funded
Stock Employer storage / broker Shop shelf + food stand — depletes on purchase
Design Player-built residence World-build settlements/*.yaml (18 §6)

Settlement job cycles run when the region is loaded; ops can seed initial stock at realm launch. Players compete with — and buy from — these shelves while building superior unique items via skill, quality rolls, and enchants.


16. Cross-references

Topic Doc
Recipes, devices, stations 09 §7
Storage chests 08 §6
Broker sell 10 §4
Attributes, stamina, skills 11
Settlement worker pools 18 §6, 19 §9
NPC definitions & AI 19
Buildings & schematics 20
Player housing / residence chests 08 §6, 20 §9

Update 09 for device queue API; 10 for worker broker permissions.