# Survival and Village

Two systems keep the base alive. `SurvivalManager` drains the digger's food and water and the storage that buffers them; `VillageManager` owns population, the three support metrics, buildings, and the daily tick.

Both are autoloads. `World.gd`'s `_process` heartbeat calls `SurvivalManager.process_survival(delta)` and `VillageManager.process_village(delta)` every frame; `VillageManager` also does one-shot bookkeeping each in-game day in response to `EventBus.new_day`.

## SurvivalManager

`scripts/village/SurvivalManager.gd` holds the digger's two live meters and the storage that overflows into them.

### Meters, storage, and the well

| Field | Meaning |
|---|---|
| `food`, `water` | The live 0–100 meters. These are the source of truth for survival. |
| `stored_food`, `stored_water` | The village stores. Overflow past a full meter banks into them from day one; the matching building (`food_silo` / `water_tower` in `ToolState.owned_tools`) gates only the *reverse* flow — stores topping the digger's meter back up. |
| `well_water` | Reservoir filled by the pump machine; trickles into `water` at `WELL_TRICKLE_RATE`. |
| `well_built` | Set true once the well has ever held water. |
| `_dead` | Latch set on death; blocks further draining. |

Tuning is read from `DataRegistry.balance["survival"]` in `_ready()`, with hardcoded fallbacks: `FOOD_DRAIN_PER_SECOND` and `WATER_DRAIN_PER_SECOND` default to `0.04`, `STORAGE_CAP` to `500.0`, `WELL_TRICKLE_RATE` to `0.04`. Storage caps scale with upgrades: `food_storage_cap()` returns `STORAGE_CAP + ToolState.silo_upgrade * 500.0` (and the water tower mirror).

### The drain tick

`process_survival(delta)` computes the per-frame drain and applies it:

```gdscript
var work_mult := WORK_MULT if GameManager.is_working else 1.0
var idle_mult := get_idle_mult() if not DisplayServer.window_is_focused() else 1.0
var food_drain := FOOD_DRAIN_PER_SECOND * delta * work_mult * idle_mult \
    * CosmeticManager.get_effect_mult("food_drain_mult") \
    * ChallengeManager.food_drain_mult()
```

Four multipliers stack onto the base rate:

- `work_mult` — `WORK_MULT` (default `2.0`) while `GameManager.is_working`, else `1.0`. Active digging burns supplies twice as fast.
- `idle_mult` — when the window is **not** focused, drain slows to `get_idle_mult()`; focused play uses `1.0`.
- Cosmetic and challenge multipliers — traits like the Mourning Sash or The Dave slow drain, challenges can raise it.

`get_idle_mult()` returns `IDLE_MULT` (default `0.15`) unless `offline_mult_override` is set — the donate-gated Settings slider, range `0.01`–`0.30`, persisted in `settings.json` rather than the run save.

After draining, the well trickles into `water`, then storage tops the meters back up toward 100 when a silo/tower is owned. Emissions are **throttled**: `food_changed`, `water_changed`, and `well_changed` only fire when the value moves at least `_EMIT_THRESHOLD` (`0.5`) or hits zero, because per-frame emits drove a full HUD relayout every tick.

### Death

Death is evaluated on the totals, where `water_total` includes `well_water` (the trickle is rate-limited and can lag):

```gdscript
var food_total  := food
var water_total := water + well_water
```

Reaching zero on either emits `EventBus.player_starving` / `player_dehydrated`, sets `_dead`, builds a cause string (`"starvation"`, `"dehydration"`, or `"starvation and dehydration"`), and emits `EventBus.player_died(cause)`.

> Offline death is handled differently: `OfflineSimulator` calls `mark_dead()` (which sets `_dead` without emitting) so it can construct the "while away" cause string itself. Do not rely on `process_survival` to fire `player_died` for offline sessions.

### Adding, draining, and offline

- `add_supplies({"food": x, "water": y})` fills the meter to 100 and banks any overflow into the village stores (capped) — no building required. The stores are the village's larder (the daily tick eats from them regardless of buildings), and the elder chain's first task depends on gathering past full raising `food_support`. The Silo/Tower gate only the stores→meter top-up in `process_survival` (and add +500 cap each). `add_food` / `add_water` are thin wrappers; `add_well_water` fills the well.
- `drain_food_pct(pct)` / `drain_water_pct(pct)` drain a fraction of **total** stores (meter + silo), meter first — used by denied/expired project penalties so a silo can't trivially refill the loss.
- `simulate_offline(elapsed_seconds)` mirrors the live tick's modifiers (idle mult, cosmetic, challenge) but omits `work_mult`, drains the well at full rate, and returns `{food_consumed, water_consumed}`. It only drains the **digger's** share; the village's share is drawn separately in the offline sim.
- `is_alive()` reports the same total logic used in the tick; `revive()` clears `_dead` for the lineage carry-on; `reset(...)` restores a fresh run.

## VillageManager

`scripts/village/VillageManager.gd` owns the surface community: how many souls live there, how well supported they feel, which buildings exist, and the once-a-day resolution.

### Population and the support metrics

`population` starts at `BASE_POPULATION` (default `47`, from `DataRegistry.balance["village"]`). `peak_population` is a monotonic high-water mark that never dips when villagers are lost, so run-progress-scaled systems (e.g. the Scout's Wager ante) don't cheapen after a storm.

Three floats measure how well the village is provided for, all recomputed by `_refresh_metrics()`:

| Metric | Source | Notes |
|---|---|---|
| `food_support` | `_calc_food_support()` | `(food + stored_food) * FOOD_WEIGHT + (water + stored_water) * WATER_WEIGHT` plus the farm building bonus. |
| `shelter_support` | `_calc_shelter_support()` | "Days of fuel reserve" scaled to population, plus the longhouse bonus. |
| `safety_support` | `_calc_safety_support()` | Wall HP, dig rate, and depth rate components, plus the watchtower bonus. |

Shelter support is buffer-based: weighted fuel stock on hand (wood, coal, deep coal, crude oil, ethanol — each with its own `SHELTER_*_WEIGHT`) divided by daily need gives `coverage_days`; holding `SHELTER_TARGET_DAYS` (default `12`) worth scores exactly `population`, capped at `SHELTER_COVERAGE_CAP` (`2.0`) × population. Until the elder chain graduates the player (`village_chain_done`) or day 7, a smaller `SHELTER_TARGET_DAYS_APPRENTICE` (default `4`) applies.

Safety support sums three weighted components plus the watchtower bonus:

```gdscript
var wall_component  := (_cached_wall_hp / WALL_HP_PER_PERSON) * 0.60 * (1.0 - _safety_debuff_pct)
var tile_component  := minf(tile_rate / TILE_TARGET_RATE, 2.0) * population * 0.30
var depth_component := minf(depth_rate / DEPTH_TARGET_RATE, 2.0) * population * 0.10
```

`_cached_wall_hp` is only rescanned when `_wall_hp_dirty` is set (a wall built/destroyed/removed), because `inventory_changed` — the most frequent `_refresh_metrics()` trigger — never affects wall HP.

`_refresh_metrics()` is wired to many `EventBus` signals in `_ready()`: `wall_built`/`wall_destroyed`/`wall_removed` (which also dirty the wall cache), `storage_changed`, `inventory_changed`, and — importantly — `food_changed`/`water_changed`, so a task reward or penalty updates the food metric on the click rather than at end of day.

### Safety scars

Denied or expired safety tasks call `apply_safety_debuff(pct, days)`, appending a scar `{pct, remaining}` to `_safety_scars`. `_recompute_safety_debuff()` caches the summed magnitude, clamped to `SAFETY_DEBUFF_CAP` (`0.75`), into `_safety_debuff_pct` so the hot path reads one float. Scars heal on their own timer, ticked down in `process_village(delta)`; a healed scar triggers `_refresh_metrics()` to restore that much safety. Scars are persisted (with legacy single-scar back-compat in `load_from_save`).

### Villager visits

`process_village(delta)` also drives the automatic trade visit: when `SurvivalManager.food <= FOOD_THRESHOLD` (default `18`), `_visit_cooldown` has expired, and `_auto_visit_armed` is true, it calls `_trigger_visit()` (emits `villager_arrived`). Challenge runs start with `_auto_visit_armed = false` so an experienced player isn't interrupted on spawn; ringing the bell (`call_villager()`) re-arms it. `_on_villager_left()` starts the `VISIT_COOLDOWN` (default `120` s). The trade UI and economy constants live in `scenes/ui/VillagerVisitPopup.gd`, not here.

### Buildings

Three buildings exist — `farm` (food), `longhouse` (shelter), `watchtower` (safety) — defined canonically in `data/buildings.json` (loaded over the `BUILDING_DEFS` fallback in `_ready()`). Each has three levels; `costs[i]` is the material cost for level `i+1` and `bonus[i]` is the flat support added at that level.

The bell offers upgrades. `get_building_options()` returns building ids below level 3. `choose_building(id)` queues a build and calls `start_construction()`, which charges the cost immediately if affordable (announcing the spend via a toast — the only place a building's gold leaves the inventory) and sets `active_build` with `build_days_left = 1`. A build the player can't yet afford stays in `queued_build` and auto-starts on the daily tick once affordable.

### The daily tick

`_on_new_day()` (bound to `EventBus.new_day`) runs the day's resolution in order:

1. `_consume_village_resources()` — draws `population * FOOD_PER_PERSON_PER_DAY` (and water) from storage with ±15% variance, idle-scaled when unfocused.
2. `_consume_shelter_fuel()` — adds today's need to `_shelter_debt`, then `_flush_shelter_debt()` burns whole inventory units cheapest-first (wood → coal → crude oil → deep coal → ethanol). Fractional carry-over keeps the average burn exact; negative debt is a pre-payment credit carried forward.
3. `_consume_black_rot()` — Black Rot challenge only: the village needs one `strange_root` per soul per day from day 2 on; the unfed shortfall is culled and recorded as blighted graveyard crosses via `GameManager.record_black_rot_deaths`.
4. Construction: `start_construction()` then `_process_construction()` (only for builds already active at day start).
5. `_tick_population()` — the migration tier check (below).
6. Milestone/bell bookkeeping: `_check_building_milestone`, `_check_population_milestone`, `_check_fallback_bell`, `_try_ring_bell`.
7. `_emit_daily_journal(...)` — writes the elder journal lines (Maren's stores draw, Bjorn's fuel burn, Siv's Maw wall damage).

`_tick_population()` compares `min_metric` (the lowest of the three supports) against population bands: `≥ 1.30×` brings a family plus solo arrivals, `≥ 1.10×` a family, `≥ 0.80×` a drifter; below `0.60×` and `0.40×` villagers flee in growing numbers. A grace window (first 3 days, plus two days after `graduated_day`) suppresses decline only — growth is never graced. Population is floored at 1.

### Bells and milestones

`BUILDING_MILESTONES` (population thresholds, overridable from balance) each credit one bell into `_owed_bells` when crossed; `_check_fallback_bell` tops up `_owed_bells` after `FALLBACK_BELL_DAYS` (default `6`) of stalled growth. `_try_ring_bell()` surfaces one owed bell at a time — but holds if a growth cutscene fired this day (`_celebrated_today`) or a specialist arrival popup is active (`SpecialistManager.is_popup_active()`), so the two big modals never collide. `try_ring_pending_bell()` lets a finishing specialist popup surface a held bell promptly. `_check_population_milestone()` fires the 50-pop `population_milestone` cutscene (highest threshold only when several are crossed at once, e.g. offline catch-up).

### Offline and breach helpers

`VillageManager` exposes several helpers the offline sim and Maw call into:

- `simulate_shelter_offline(days_passed, idle_mult)` and `simulate_black_rot_offline(days_passed)` mirror the daily fuel burn and rot cull over the away window. The rot is a discrete daily tax and is **not** idle-multiplied.
- `simulate_hunger_offline(dry_days, idle_mult)` applies the `_tick_population` hunger tiers on the idle clock, silently, for the days stores sat empty.
- `idle_window_seconds(food_total, water_total)` returns how long food/water lasts the digger plus the village at idle drain — the single source for the supply-ETA readout (classic BottomBar; HearthCrest on the talisman HUD) and the storage-warning math.
- `apply_breach_losses(pct, floor_pop)` kills a fraction on a Maw breach (never below `floor_pop`, never increasing), and re-arms the growth cutscenes so survivors re-cross thresholds. `send_off_successor()` removes one villager for the lineage carry-on.

`get_save_data()` / `load_from_save()` serialize population, buildings, bell state, visit cooldown, and safety scars; keys must stay in sync between the two.

## Key files

- `scripts/village/SurvivalManager.gd` — digger food/water/storage/well drain, death check, offline drain.
- `scripts/village/VillageManager.gd` — population, support metrics, buildings, daily tick, bells, villager visits.
- `data/buildings.json` — canonical farm/longhouse/watchtower definitions (costs and bonuses).

## Related

- [Projects and Specialists](/docs/underroot/projects-and-specialists)
- [Idle and Offline Simulation](/docs/underroot/idle-and-offline-simulation)
- [The Maw](/docs/underroot/the-maw)
- [Machines](/docs/underroot/machines)
- [EventBus and the Signal Convention](/docs/underroot/eventbus-and-the-signal-convention)