Villager Tasks and Projects

The village periodically asks the digger for help: bring coal for the hearths, patch the east wall, dig to a new depth. Each request is a "project" defined in data/projects.json. Below: how a project is authored, how the game picks which ones appear and how often, how asks and rewards scale with depth, and the one authoring rule you can't break. Every task in the file is listed with its exact numbers.

In plain terms

Each entry in projects.json is a template. To spawn a task, the game picks an eligible template, rolls the exact material amounts and reward inside the authored ranges, and scales both by how deep the player has dug. The player can fulfil it (hand over the materials for the reward), deny it (take a smaller penalty), or ignore it until it expires (a larger penalty). A few task types carry no material cost. They ask the player to do something — reach a depth, mine a new material, find a discovery — and the reward is claimed when the objective is met.

Ranges in the JSON are written [low, high]. A material ask of "coal": [4, 8] means "somewhere from 4 to 8 coal, before depth scaling."

Every field on a task template

Field Where it lives What it does
category projects.json → per task Which of the seven task families this belongs to. Sets the expiry window (below) and colours how the task reads.
title / flavour projects.json → per task The card's headline and one-line story. No mechanical effect.
materials projects.json → per task The delivery ask: one or more material_id: [low, high] ranges. Omitted on objective tasks that ask the player to do something instead.
reward_type projects.json → per task The single reward if there is no reward_pool. One of food, water, gold, population, safety_boost, or a material id.
reward_scale projects.json → per task [low, high] amount for the single reward, before depth scaling.
reward_pool projects.json → per task Optional list of weighted reward options { "type", "scale": [lo,hi], "weight" }; the game rolls one per spawn so the same task can pay differently each time. Overrides reward_type/reward_scale when present.
reward_bundle projects.json → per task Optional list that grants every listed reward at once (used by epic tasks). Each entry is { "type", "scale": [lo,hi] }.
deny_penalty_type projects.json → per task What denying/ignoring hurts: food, water, safety, or none.
deny_penalty_pct projects.json → per task [low, high] fraction lost on deny (e.g. [0.12, 0.22] = 12–22%).
min_depth projects.json → per task The task cannot appear until the player has dug at least this deep (in tiles).
max_depth projects.json → per task Optional. The task stops appearing once the player digs past this depth.
spawn_weight projects.json → per task Relative chance of being picked among eligible tasks. Default 1.0; lower = rarer.
objective_type projects.json → per task For non-delivery tasks: deliver (default), find_discovery, mine_material, or reach_depth.
objective projects.json → per task The target for a non-delivery task, e.g. { "depth_delta": 25 } or { "material_id": "copper_ore" }.

The seven categories

Category sets how long a task lives before it expires. The expiry windows themselves are a script constant in ProjectManager (_EXPIRY_DAYS), not in the JSON; the JSON category string only chooses which window applies.

Category Expiry window (in-game days) Role
emergency 0.30.7 Short-fuse crises. Harshest deny penalties; must be answered fast.
supply_run 1.02.0 Everyday "bring us X material" asks. The staple of the feed.
construction 1.52.5 Build/repair the village; usually pays safety.
craft_request 1.52.5 Bring ore/fuel; the village crafts and hands back a finished good.
exploration 2.03.5 Go do something down there (mine, dig deeper, find a discovery).
luxury 2.03.5 Optional feel-good asks; deny penalty is usually none.
epic 3.05.0 Long, expensive multi-material asks that pay a whole reward bundle. Rare (spawn_weight 0.3).

The rolled expiry is a random value inside the window. Ignoring a task until it lapses is worse than denying it: expiry applies the deny penalty multiplied by 1.75 (_EXPIRE_PENALTY_MULT, a script constant).

How the feed is shaped

ProjectManager constants govern the rhythm of tasks, not JSON. Knowing them lets you read what the JSON values will actually produce.

Lever Where it lives Default Effect
Base spawn interval ProjectManager._SPAWN_INTERVAL_BASE 0.5 days (±0.2 jitter) Roughly two tasks a day at baseline.
First task delay ProjectManager._FIRST_SPAWN_DAYS 1.5 days The opening task lands about halfway through day 2 so it does not crowd the start.
Depth speed-up ProjectManager._DEPTH_FACTOR_PER_10 / _DEPTH_FACTOR_CAP +10% per 10 tiles, capped Deeper digs get tasks faster, up to three times baseline.
Pile-up slowdown ProjectManager._PILE_UP_FACTOR 0.20 per open task Each task already waiting stretches the next interval, so the board does not flood.
Population gate ProjectManager._try_spawn pop < 5 blocks spawning No tasks until the village has at least five people.

Eligibility. A template can be picked only if the player's depth is at least its min_depth and not past its max_depth, and, for delivery tasks, every asked material has been reachable at the current depth. Objective tasks add their own checks. A reach_depth task only appears while a full descent of its depth_delta still fits above the dig floor; a mine_material task for a specific material stops offering once the player has already dug that material.

How spawn_weight shapes the mix

Among the eligible templates, the game does a weighted random pick using each template's spawn_weight (default 1.0 when omitted). A template at 0.5 surfaces about half as often as a default one; an epic at 0.3 is deliberately rare. A low weight never removes a task from the pool, it just comes up less. Use spawn_weight to keep demanding asks — iron-heavy, deep-material — from dominating the feed while still letting them appear. Raise a weight above 1.0 and the task shows up more often than baseline.

How ask budgets scale with depth

A depth scale multiplies both the material ask and the reward at spawn time: 1.0 + clamp(depth ÷ 100, 0, 3), i.e. from at the surface up to at depth 300 and below (_DEPTH_SCALE_MAX = 4.0). So "coal": [4, 8] on a surface task asks for 4–8 coal, but the same template deep down asks for up to four times that, and pays up to four times the reward to match. That's why the authored ranges look small: they're the shallow budget, and the game inflates them with depth. One exception: rewards of type reinforced_frame are a fixed count and never depth-scale — a frame is one upgrade no matter the depth.

Two reward conversions to know

The reward-must-differ-from-ask rule

Never give a delivery task a reward material that it also asks for. The ask and the reward both scale with the same depth multiplier, so rewarding an iron task with iron nets the player a loss on deep spawns: they hand over 4× iron and get back 4× a smaller iron number. This is authoring discipline, not something the code enforces. Nothing in ProjectManager checks it, so keeping every delivery task's reward types distinct from its materials keys is on the designer. Objective tasks that ask no materials are exempt — there's nothing to double up on.

Every task in projects.json

Ranges are the authored (shallow) budget before depth scaling. "Reward" shows the single reward_type/reward_scale, or "pool" / "bundle" where the task rolls or grants several.

supply_run

id Ask (range) Reward Deny min_depth spawn_weight
coal_supply coal [4,8] food [14,26] (pool: food/water/gold) food [0.12,0.22] 0 (1.0)
wood_supply wood [6,12] food [12,24] (pool: food/water/gold) food [0.10,0.18] 0 (1.0)
stone_supply stone [6,12] water [12,24] (pool: water/food/gold) water [0.10,0.18] 0 (1.0)
iron_supply iron [1,2] gold [14,22] (pool: gold/food) food [0.10,0.18] 77 0.5
deep_coal_supply deep_coal [2,4] food [22,40] (pool: food/gold) food [0.15,0.25] 80 0.7
sulfur_supply sulfur [3,6] water [16,30] (pool: water/gold) water [0.12,0.22] 60 (1.0)
quartz_supply quartz [1,2] gold [22,34] (pool: gold/food) food [0.10,0.18] 100 0.5
kiln_fuel coal [3,6], clay [5,10] food [16,28] (pool: food/water) food [0.10,0.20] 27 (1.0)
forge_run iron_ore [2,4], coal [3,6] iron [2,3] water [0.12,0.22] 77 0.8
charcoal_burn wood [6,12] coal [6,10] food [0.10,0.18] 20 0.8

construction

id Ask (range) Reward Deny min_depth spawn_weight
wall_repair stone [5,10], clay [3,6] safety_boost [14,26] safety [0.12,0.22] 0 (1.0)
longhouse_patch wood [6,12] water [14,26] (pool: water/food) water [0.10,0.18] 0 (1.0)
watchtower_brace iron [1,2], stone [4,8] safety_boost [16,30] (pool: safety/gold) safety [0.15,0.25] 40 0.6
deep_vault_work dense_stone [3,5], coal [3,6] safety_boost [18,34] (pool: safety/reinforced_frame) safety [0.18,0.30] 80 0.8
iron_gate iron [2,3] safety_boost [20,36] (pool: safety/gold) safety [0.18,0.30] 40 0.6
deep_reinforcement dense_stone [3,6], iron [1,2] safety_boost [20,36] (pool: safety/iron/steel) safety [0.15,0.28] 113 0.8
aqueduct_lining limestone [3,6], clay [4,8] water [22,40] (pool: water/gold) water [0.15,0.25] 201 0.9

craft_request

id Ask (range) Reward Deny min_depth spawn_weight
pickaxe_request iron [1,2], coal [2,4] water [16,28] (pool: water/gold) water [0.12,0.22] 40 0.7
bronze_fittings copper_ore [3,5], tin_ore [3,5] bronze [2,3] (pool: bronze/gold) food [0.12,0.22] 60 0.8
limestone_blocks limestone [4,8] water [16,28] (pool: water/gold) water [0.08,0.16] 20 0.8
copper_pipe copper_ore [3,5] water [16,28] (pool: water/gold) water [0.15,0.25] 50 0.8
iron_tools iron [1,2], coal [3,5] food [20,36] (pool: food/gold) food [0.15,0.25] 40 0.7
smithy_smelt iron_ore [4,6] iron [2,3] food [0.12,0.22] 77 0.7
smelter_charge coal [4,8], sulfur [2,4] food [18,32] (pool: food/crude_oil) food [0.12,0.22] 52 (1.0)
alloy_order copper_ore [2,4], iron [1,2] steel [1,2] (pool: steel/bronze/gold) safety [0.15,0.25] 141 0.7
mortar_mix limestone [4,7], clay [4,8] food [22,36] (pool: food/gold) food [0.10,0.18] 201 (1.0)
deep_fuel_blend deep_coal [2,4], sulfur [2,4] crude_oil [3,5] (pool: crude_oil/deep_coal/gold) food [0.15,0.25] 233 0.8

exploration

These use objective_type other than delivery where noted; they carry no material cost.

id Objective / Ask Reward Deny Depth gate spawn_weight
fossil_hunt deliver fossil [2,4] population [1,1] none min 30 (1.0)
crystal_collection deliver quartz [1,3] food [26,46] (pool: food/gold) none min 100 0.6
strange_root_study deliver strange_root [2,4] water [16,28] (pool: water/food) none min 20 (1.0)
chest_hunt find_discovery treasure_chest gold [18,30] (pool: gold/food) safety [0.05,0.10] min 40 (1.0)
prospect mine_material any_new gold [10,18] (pool: gold/food) food [0.15,0.25] min 0, max 60 (1.0)
copper_find mine_material copper_ore gold [14,24] (pool: gold/iron/bronze) safety [0.12,0.20] min 100, max 260 (1.0)
dig_deeper reach_depth +25 safety_boost [18,30] (pool: safety/gold) safety [0.18,0.28] min 0, max 120 (1.0)

emergency

id Ask (range) Reward Deny min_depth spawn_weight
maw_barrier stone [8,14], iron [1,2] safety_boost [24,42] safety [0.25,0.40] 40 0.7
flood_warning clay [6,12] water [18,32] water [0.25,0.40] 0 (1.0)
fire_risk clay [4,8], dirt [6,12] safety_boost [14,26] safety [0.30,0.40] 0 (1.0)
emergency_patch stone [8,14], iron [1,2] safety_boost [24,44] safety [0.28,0.42] 77 0.8
flood_seal clay [6,10], dense_stone [2,4] water [22,38] water [0.28,0.42] 113 0.9

luxury

id Ask (range) Reward Deny min_depth spawn_weight
elder_gift quartz [1,2] population [1,1] none 263 0.8
feast_supply coal [5,10] food [20,38] none 0 (1.0)
rare_minerals prismatic_shard [1,2] population [1,1] none 113 0.8

epic

Epic tasks grant a reward_bundle (every listed reward at once) and are rare (spawn_weight 0.3).

id Objective / Ask Reward bundle Deny min_depth
deep_bastion dense_stone [4,7], iron [2,3], limestone [3,5] safety_boost [30,50] + gold [20,32] + reinforced_frame [1,2] safety [0.20,0.35] 201
great_descent reach_depth +50 gold [24,38] + steel [1,2] + safety_boost [20,34] safety [0.18,0.30] 10
the_long_haul deep_coal [3,5], sulfur [3,5], iron [1,2] food [30,50] + gold [18,30] + crude_oil [3,5] food [0.18,0.30] 233
relic_recovery find_discovery crystal_pocket gold [22,36] + quartz [2,3] + population [1,1] safety [0.10,0.20] 201

Safe to change or handle with care

After editing projects.json, run the data validator and smoke test before shipping — see The Safe Change Workflow.