Config Format — houses.js
Complete annotated reference for the housing config file at src/scripts/houses.js.
File Structure
houses.js is a JavaScript file executed by the embedded MuJS VM at startup. It calls building_config.add() once per housing tier, passing a config object. The 20 calls appear in tier order (Crude Hut first, Palatial Estate last).
// src/scripts/houses.js (structure overview)
building_config.add({
type: "BUILDING_HOUSE_CRUDE_HUT",
// ... (outer fields)
model: {
// ... (per-difficulty arrays, 5 elements each)
}
});
building_config.add({
type: "BUILDING_HOUSE_STURDY_HUT",
// ...
});
// ... 18 more entries
Annotated Example
The example below is based on Ordinary Cottage (tier 6) and covers every field present in the config.
building_config.add({
type: "BUILDING_HOUSE_ORDINARY_COTTAGE",
// ── Visuals ──────────────────────────────────────────────────────
animations: {
base: { pack: 18, id: 5, offset: 10 }, // spritesheet location
preview: { pack: 18, id: 5, offset: 0 }, // shown while placing
house: { pack: 18, id: 5, offset: 10 }, // in-game sprite
minimap: { pack: 18, id: 5, offset: 0 } // minimap dot sprite
},
variants: {
// animation variant sets for different merge states
single: { ... }, // 1×1 unmerged
merged: { ... } // 2×2 merged
},
// ── Placement ─────────────────────────────────────────────────────
building_size: 1, // base footprint in tiles (always 1 here; size
// grows via merge/expand, not config)
can_merge: true, // allows 4 adjacent same-tier plots to merge
// into one 2×2 block; false for manors/estates
// ── Neighbourhood influence ───────────────────────────────────────
desirability: {
value: -1, // base desirability contribution to neighbours
step: 1, // change per tile of distance
step_size: 1, // apply step every N tiles
range: 2 // maximum influence radius (tiles)
},
// ── Crime ─────────────────────────────────────────────────────────
crime: {
base: 5, // base crime-generation value
value: 0 // additional scaling factor
},
// ── Per-difficulty model ───────────────────────────────────────────
// All arrays have 5 elements: [Very Easy, Easy, Normal, Hard, Very Hard]
model: {
// Desirability thresholds
devolve_desirability: [-20, -15, -10, -5, 0],
evolve_desirability: [ -5, 0, 5, 10, 15],
// Population
max_people: [ 18, 17, 15, 13, 10],
// (per tile; multiply by footprint area for total)
// Tax
tax_multiplier: [ 9, 8, 8, 7, 7],
prosperity: [ 5, 5, 5, 5, 5],
// prosperity points added to city score each year this house exists
// Food
food_types: [ 1, 1, 1, 1, 1],
// number of distinct food types required
// Services — all boolean (0 or 1)
water: [ 1, 1, 1, 1, 1],
// 1 = Water Supply required (not just well)
religion: [ 0, 0, 0, 0, 1],
// number of gods required (0–5)
education: [ 0, 0, 0, 0, 0],
// education level required (0–3)
physician: [ 0, 0, 0, 0, 0],
dentist: [ 0, 0, 0, 0, 0],
health: [ 0, 0, 0, 0, 0],
// Entertainment threshold (0–100)
entertainment: [ 0, 0, 10, 15, 20],
// Entertainment type divisors (higher = harder to reach threshold)
entertainment_juggler_divider: [ 4, 4, 4, 5, 5],
entertainment_musician_divider: [ 3, 3, 3, 4, 4],
entertainment_dancer_divider: [ 3, 3, 3, 3, 3],
entertainment_senet_divider: [ 2, 2, 2, 2, 2],
// Goods (0 = not required, 1 = required)
pottery: [0, 0, 0, 0, 0],
beer: [0, 0, 0, 0, 0],
linen: [0, 0, 0, 0, 0],
jewelry: [0, 0, 0, 0, 0],
// Risk values (0–100; higher = more frequent incidents)
fire_risk: [15, 16, 18, 20, 22],
// NOTE: fire_risk peaks at Ordinary Cottage, then decreases at higher tiers
damage_risk: [ 5, 5, 5, 5, 5],
disease_risk: [ 8, 8, 8, 8, 8],
malaria_risk: [ 5, 5, 5, 5, 5],
crime_risk: [12, 12, 12, 12, 12],
// Food / goods consumption rates
food_consumption_percentage: [100, 100, 100, 100, 100],
// % of max_people / month consumed as food
food_storage_multiplier: [3, 3, 3, 3, 3],
// house stores up to (multiplier × monthly consumption)
// Devolution stability
devolve_delay: [2, 2, 3, 3, 4],
// months of consecutive unmet requirements before devolution applies
unreachable_ticks_devolve_threshold: [12, 10, 8, 6, 4],
// ticks with no walker access before forced devolution
unreachable_ticks_block_evolve_threshold: [4, 4, 4, 4, 4],
// ticks with no walker access that block evolution (even if other needs met)
days_without_food_devolve_threshold: [30, 28, 25, 20, 15]
// days without food delivery before devolution
}
});
Difficulty Indexing
Every array in model{} has exactly 5 elements, indexed by the current difficulty setting at runtime:
| Index | Difficulty | Effect on housing |
|---|---|---|
| 0 | Very Easy | Lowest desirability thresholds, most forgiving food/service requirements. |
| 1 | Easy | Slightly tighter than Very Easy. |
| 2 | Normal | Baseline. Values in the player-facing wiki are shown for this index. |
| 3 | Hard | Higher entertainment and desirability thresholds. |
| 4 | Very Hard | Strictest requirements, highest risks. |
In C++ the difficulty index is read via game_settings().difficulty and passed into every model.field[difficulty] lookup inside check_requirements().
model{} Field Reference
| Field | Type | Description |
|---|---|---|
evolve_desirability | int[5] | Minimum desirability to evolve to the next tier. |
devolve_desirability | int[5] | Desirability below which devolution is triggered. |
max_people | int[5] | Maximum residents per tile. Multiply by footprint area for total. |
tax_multiplier | int[5] | Scales the base tax rate for this tier. |
prosperity | int[5] | Prosperity points contributed to the city score annually. |
food_types | int[5] | Number of distinct food types required (1 or 2 in base game). |
water | int[5] | 0 = well acceptable, 1 = Water Supply building required. |
religion | int[5] | Number of distinct gods required (0–5). |
education | int[5] | Education score required (0–3). |
health | int[5] | Health score required (0–2). |
physician | int[5] | 1 = physician walker coverage required. |
dentist | int[5] | 1 = dentist walker coverage required. |
entertainment | int[5] | Entertainment score threshold (0–100). |
entertainment_juggler_divider | int[5] | Juggler contribution divisor. Higher = less score from jugglers. |
entertainment_musician_divider | int[5] | Musician contribution divisor. |
entertainment_dancer_divider | int[5] | Dancer contribution divisor. |
entertainment_senet_divider | int[5] | Senet player contribution divisor. |
pottery | int[5] | 1 = pottery required in inventory. |
beer | int[5] | 1 = beer required. |
linen | int[5] | 1 = linen required. |
jewelry | int[5] | 1 = jewelry required (luxury good). |
fire_risk | int[5] | Monthly probability of a fire starting (0–100). Peaks at tier 6, then falls. |
damage_risk | int[5] | Monthly probability of structural damage. |
disease_risk | int[5] | Monthly probability of a disease outbreak. |
malaria_risk | int[5] | Monthly probability of malaria — higher near floodplains. |
crime_risk | int[5] | Monthly probability of criminal activity spawning. |
food_consumption_percentage | int[5] | Fraction of max population worth of food consumed per month (%). |
food_storage_multiplier | int[5] | How many months of food stock the house holds. |
devolve_delay | int[5] | Months of consecutive failed requirements before devolution applies. |
unreachable_ticks_devolve_threshold | int[5] | Ticks with no road-walker access before forced devolution. |
unreachable_ticks_block_evolve_threshold | int[5] | Ticks with no road-walker access that block evolution. |
days_without_food_devolve_threshold | int[5] | Days without food delivery before devolution triggers. |
Outer Field Reference
| Field | Description |
|---|---|
type | Building type enum name. Must match exactly the enum value in building_type.h. |
animations | Sprite sheet references for each view state. |
variants | Animation variant sets for single and merged states. |
building_size | Base footprint (always 1 for housing; size grows via merge/expand at runtime). |
can_merge | true for tiers 1–14 (those that merge into 2×2). false for manors and estates, which only expand via expand_to_*(). |
desirability.value | Base contribution to neighbouring tiles' desirability. Negative for low tiers, positive for high tiers. |
desirability.step | How much the contribution changes per tile of distance. |
desirability.step_size | Apply the step every N tiles of distance. |
desirability.range | Maximum radius of influence in tiles. |
crime.base | Base crime rate added to the neighbourhood crime overlay. |