NPCs & Population
Every sector has a simulated population of real NPCs — not abstract numbers, but named individuals with jobs, homes, family ties, and schedules. The city has 10k–40k NPCs who get born, die, quit, emigrate, and witness what you do every day. Ignoring this system means losing control of your empire.
NPC Roles
From engine/internal/population/types.go:
| Role | Meaning |
|---|---|
| Owner | One NPC per non-landmark building. Eigentümer / proprietor. |
| Staff | Employees in commercial/industrial buildings. With wage. |
| Resident | Lives in residential, no job. Birth pool for new owners/staff. |
| Unemployed | Actively jobless (e.g. after quit, before replacement). |
Phase-6+ specialties (server roadmap, partly reserved in pool): Auditor, Judge, Doctor, Teacher, Cop, CorpSec, Journalist, Lawyer, Guard.
NPC Attributes (0–100)
Each NPC has six psychological + economic values:
| Attribute | Meaning |
|---|---|
| Wealth | 0–100+ (can exceed). Determines wealth tier (low/mid/high). |
| Corruption | 0–100. Bribe susceptibility — high = easy to buy. |
| Fear | 0–100. Reaction to intimidation. High fear = quits faster. |
| Loyalty | 0–100. Loyalty to employer/family. < 20 = informant flag. |
| Observance | 0–100. Witness clarity. < 20 = witnesses nothing. |
| Productivity | 0–100. Job output modifier. |
Schedule (StateAt)
Every NPC has a deterministic schedule as a pure function:
ShiftStart = minute-of-day, default 540 (09:00)
ShiftEnd = default 1020 (17:00)
Commute = 10 (same sector) | 30 (adjacent) | 60 (far)
States: Home → CommutingOut → Working → CommutingIn → Home → OffDuty | Sick | Imprisoned | Grieving.
StateAt(tick) is O(1) — at any time-of-day you can ask “where is NPC X right now?”. Critical for the witness mechanic (below).
Witness System
NPCs at the crime scene observe events. Per NPC a ring buffer of 10 events:
WitnessEvent = {
Day, EventType, ActorPlayerIdx, NodeID, SectorID,
Fidelity (0-100, decay -10/day)
}
Gates:
- NPC must have Observance ≥ 20
- NPC must be at the crime node or in 2-hop radius (Phase 4)
- Same-location bonus: +30 fidelity
Decay: −10 fidelity/day. At 0 → event pruned from buffer.
Fog-of-war: Other players see ActorPlayerIdx as −1 (unknown) — only you (or building owner) see who committed the crime.
Dedup: Same (EventType, Actor, Node) → existing entry refreshed to fidelity 100, no duplicate.
Player counter-actions against witnesses (full entries in Actions):
- WITNESS_INTIMIDATION — STR/CHA-driven, 45 min, $500. Slows Evidence accrual in the sector. Best deployed early in an investigation before the case escalates beyond Beat-Cop stage.
- CLEAN_UP (server v1.62.0) — STL/TCH-driven, 45 min. Physical sweep at the crime node — pulls fidelity off witness events directly, retroactive heat reduction on the hood.
- DATA_WIPE (server v1.68.0) — TCH hack against the police record system. Doesn’t touch the witnesses themselves but cleans the downstream paper trail; pairs with intimidation when the case has already been opened.
Informant Mechanic
If Staff.Loyalty ≤ 20:
Staff.Informant = true (sticky — does not flip back on Loyalty recovery)
Informants leak witness events to the police — a primary path for your crimes ending up in investigations.
Reset: Only via Phase-3.F action. Otherwise: fire or replace NPC.
Visible: Building owner sees the informant flag in the exact-attribute view, others don’t.
Bribe Effect on NPCs
bribe-action on NPC:
- Corruption +5..10 (depending on success)
- BribedMask bit for your player index set (sticky)
- Re-bribe → repeat-bribe discount (UI signal)
- Above 60% corruption (cops): permanent DirtyCop relation possible (Layer 6)
Daily Lifecycle (End-of-Day)
Every tick runs the following population phases:
Mortality (age-driven death)
< 360 months (30 yrs) → 0% chance
360-720 months (30-60 yrs) → linear 0.001%/month-excess
> 720 months (60+ yrs) → linear 0.003%/month-excess (3× multiplier)
Birth
- Daily chance: 0.0005 per adult Resident (240–540 months old)
- Penalties: Violence-deaths (−50% at 20+), emigration (−40% at 30+), sector heat (−40% at heat=100)
- Newborn spawns in parent’s HomeNode
Emigration
- Triggers in sectors with DeathsTotal > 5
- Base: 0.005/day per resident, +0.002 per excess death
- Heat boost: linear ×1.0 → ×2.0 (heat 0 → 100)
- Cause:
EmigrationCauseDeathSpiralorEmigrationCauseHeat
Quit (voluntary job loss for staff/owners)
Three independent channels:
- Loyalty channel: Per point below 30 → +0.001/day quit chance
- Fear channel: Per point above 70 → +0.0005/day
- Wage channel: Very low wage → +0.01/day base
- Tenure inertia: Veterans quit slower
Retirement (age-driven)
- Trigger: Owner/Staff ≥ 540 months (45 yrs)
- Probability: 0.05/day at 540, ramped to 1.0/day at 900+ months
- Replacement spawns immediately
Replacement (death/quit → new NPC)
- Promotion: Local adult resident (20–40 yrs, matching wealth tier) → staff/owner
- Fresh spawn: If no candidate → fresh NPC with baseline-50 attributes
Immigration
- Trigger: LaborDemand/LaborSupply > 1.3 (sector understaffed)
- Inflow: ~5% PopulationTotal/day
- Wealth tier matched to wage offer
Loyalty regen
- Daily +1 loyalty for staff under baseline (50)
- One-sided — staff ≥ 50 not touched
- But: informant flag stays sticky even on loyalty recovery
Sector Aggregates
Each sector tracks 20+ counters (births, deaths-by-cause, quits, hires, etc.) as lifetime + today values plus 7-day rolling history (for UI sparklines).
Health Tier (demographic)
| Tier | Trigger |
|---|---|
| Thriving | Births − Emigration > 5 |
| Stable | Default |
| Declining | Emigration − Births > 5 |
| Collapsing | DeathsByViolence ≥ 10 OR (Emigration ≥ 10 AND Births/Emigration < 0.33) |
Economy Tier (economic)
| Tier | Trigger |
|---|---|
| Booming | WealthInflow ≥ 200 + Inflow/Outflow ≥ 3:1 |
| Stable | Default |
| Stagnating | LaborSupply ≥ 5 + Supply/Demand ≥ 2:1 |
| Recession | Outflow ≥ 200 + Outflow/Inflow ≥ 3:1 |
The two tiers are independent — a sector can be Thriving (demographically) AND in Recession (economically) at the same time. “Brain drain” marker.
What you see as a player (fog-of-war)
| Position | Visible |
|---|---|
| Unscouted Sector | Aggregates only (population, labor, migration, avg wealth). No individual NPCs. |
| Scouted Sector | Owner/Staff lists with hinted attributes (“very_corrupt”, “very_loyal”). No exact values. |
| Own Building | Staff with exact 0–100 values + informant flag + witness events (own crimes with ActorPlayerIdx, others’ as −1). |
| Admin | No fog. |
NPCDTO fields
From engine/internal/fixture/population.go:
NPCDTO {
ID, Name, Surname, AgeYears
Role // "owner" | "staff" | "resident" | "unemployed"
HomeBuildingID, WorkBuildingID
WealthTier // "low" | "mid" | "high"
CorruptionHint, FearHint, LoyaltyHint // hinted, categorical
Wage, TenureDays
State, CurrentNode, ShiftStart, ShiftEnd, NextTransition
Exact?: { // owner/admin only
Corruption, Fear, Loyalty, Observance, Productivity, Wealth
Informant, BribedByRequester
WitnessedEvents[]
}
}
Sector Population DTO
SectorPopulationDTO {
Total, Cap // Population + housing
LaborDemand, LaborSupply, MigrationFlag // +1 inflow / -1 outflow / 0 stable
AvgWealth, AvgOwnerFear, AvgStaffLoyalty
InformantCount // Count without identities
AvgStaffProductivity
}
Strategic Implications
Deep dive: Once you start unionizing buildings, the Workforce & Strikes labor system applies — chronic-pattern detection, federal-heat penalties, crisis tiers, and bot adaptation all kick in.
- Keep sector health under control: Heat → emigration → less staff available → buildings unproductive
- Keep manager heat low: > 30 = synergy modifier reduces wash cap (see Economy)
- Loyalty as long-term investment: Staff < 20 = informant. Either nurture loyalty or replace the NPC
- Violence = population crash: Frequent murder/arson generates
DeathsByViolence→ sector → Collapsing → buildings lose workforce - Wealth inflow attracts wealthy NPCs: Booming sectors bring more wealth inflow + better owner/staff recruitment
- Witness decay tactics: Crimes during low-traffic time windows (NPCs on shift elsewhere) reduces witnesses
- Bribe-mask sticky: Bribed NPCs stay marked permanently — repeat bribes cheaper
Sources
engine/internal/population/— 115+ Go files, full lifecycle engineengine/internal/fixture/population.go— DTO mapping for APIengine/internal/government/institutional_effects.go— contact effects (NPCs are the basis of contacts)