Wiki / Developer Docs / Housing System / Class Hierarchy

Class Hierarchy

How the 20 housing tier classes are organised, what the base class owns, and what lives in runtime_data_t.

Hierarchy

building_impl                          (src/building/building_impl.h)
  └─ building_house                    (src/building/building_house.h)
       ├─ building_house_crude_hut          tier  1
       ├─ building_house_sturdy_hut         tier  2
       ├─ building_house_meager_shanty      tier  3
       ├─ building_house_common_shanty      tier  4
       ├─ building_house_rough_cottage      tier  5
       ├─ building_house_ordinary_cottage   tier  6
       ├─ building_house_modest_homestead   tier  7
       ├─ building_house_spacious_homestead tier  8
       ├─ building_house_modest_apartment   tier  9
       ├─ building_house_spacious_apartment tier 10
       ├─ building_house_common_residence   tier 11  ← labor stops here
       ├─ building_house_spacious_residence tier 12
       ├─ building_house_elegant_residence  tier 13
       ├─ building_house_fancy_residence    tier 14
       ├─ building_house_common_manor       tier 15  ← 3×3 footprint
       ├─ building_house_spacious_manor     tier 16
       ├─ building_house_elegant_manor      tier 17
       ├─ building_house_stately_manor      tier 18
       ├─ building_house_modest_estate      tier 19  ← 4×4 footprint
       └─ building_house_palatial_estate    tier 20

Base Class — building_house

The base class owns the shared interface and helpers used by all tier classes. It does not implement evolution itself — that is delegated to each tier via the virtual evolve() method.

MethodDescription
virtual evolve(house_demands*)Called monthly. Checks merge eligibility, runs requirements check, applies tier change. Each of the 20 tier classes overrides this.
check_requirements(house_demands*)Returns e_house_progress (EVOLVE / NONE / DECAY). Compares current service state against model thresholds.
has_required_goods_and_services(int for_upgrade, house_demands*)Validates water, food, entertainment, education, religion, health, and all goods (pottery / beer / linen / jewelry). Sets demand flags for any unmet requirement.
check_evolve_desirability()Reads desirability from the grid, compares to model.evolve_desirability and model.devolve_desirability.
merge() / merge_impl()Attempts to merge a 1×1 plot into a 2×2 block with three adjacent matching tiles. Returns true if merge succeeded.
split(int new_type)Undoes a merge, placing four 1×1 plots in place of one large footprint.
decay_services()Called monthly. Decrements all service counters so that coverage must be renewed each month.
consume_food_weekly() / consume_goods_weekly()Deducts food and goods from runtime_data.foods[] and runtime_data.inventory[].
dcast_house()Convenience downcast shorthand (see Downcasting).

runtime_data_t

Each house instance carries a 186-byte runtime_data_t buffer (enforced by static_assert). All fields are declared in building_house.h.

Population & food

FieldTypeMeaning
populationu16Current occupants.
highest_populationu16Peak population recorded (used for prosperity score).
days_without_foodu8Counter incremented each week food is absent. Triggers devolution after model.days_without_food_devolve_threshold.
foods[8]u8[]Stock of each food type delivered by the bazaar.
inventory[8]u8[]Stock of manufactured goods (pottery, beer, linen, jewelry).
hsizeu8Tile width: 1, 2, 3, or 4.
is_mergedboolTrue when four 1×1 plots have merged into one block.

Service flags (set by walkers each month)

FieldSource figure
bazaar_accessBazaar buyer
fancy_bazaar_accessBazaar buyer (high-tier route)
schoolScribal School teacher
libraryLibrary librarian
academyAcademy teacher
physicianPhysician
dentistDentist
apothecaryApothecary
mortuaryMortuary embalmer
magistrateCourthouse magistrate
shrine_accessShrine proximity
booth_jugglerJuggler booth entertainer
bandstand_juggler / bandstand_musicianBandstand walkers
pavillion_musician / pavillion_dancerPavilion walkers
senet_playerSenet house player
bullfighterBull pen fighter

Aggregated service scores (computed monthly)

FieldRangeComputed from
entertainment0–100Weighted sum of juggler / musician / dancer / senet coverage; see Service Coverage.
education0–3school=1, school+library=2, school+library+academy=3.
health0–2+1 apothecary, +1 physician.
num_gods0–5Count of distinct gods with temple walker coverage. Shrine counts as 1 if no temple present.
water_supply0–20=none, 1=well, 2=water supply building.

Tax & economy

FieldMeaning
tax_collector_idID of the tax collector figure last visiting this house.
tax_income_or_storageAccumulated tax since last collection.
tax_coverageBoolean — whether a tax collector reached this house this year.

Devolution & stability

FieldMeaning
devolve_delayCountdown before a triggered devolution is actually applied. Prevents single-tick fluctuations from instantly downgrading a house.
unreachable_ticksIncremented each tick that no road-walker can reach the house. Triggers devolution at model.unreachable_ticks_devolve_threshold.
no_space_to_expandSet when merge or size-upgrade is blocked by adjacent obstacles.
house_happinessGeneral satisfaction score used for crime and prosperity calculations.
criminal_activeFlag set when a criminal has spawned from this house.

Tier Classes

Each of the 20 tier classes follows the same pattern:

// building_house.h
struct building_house_ordinary_cottage : building_house {
    BUILDING_METAINFO(BUILDING_HOUSE_ORDINARY_COTTAGE,
                      building_house_ordinary_cottage,
                      building_house)

    virtual void evolve(house_demands* demands) override;
    // inherits all other methods from building_house
};

The BUILDING_METAINFO macro handles factory registration, type-safe casting, and static param lookup. Beyond evolve(), most tier classes add no new methods — all variation is driven by the model thresholds loaded from config.

Downcasting

Use dcast<T>() or the convenience shorthand. Never use static_cast.

building* b = ...;

// check and downcast to any housing tier
building_house* h = b->dcast_house();          // nullptr if not a house

// downcast to a specific tier
auto* hut = b->dcast<building_house_crude_hut>();  // nullptr if wrong tier

// safe pattern in an update loop
if (auto* house = b->dcast_house()) {
    // safe to call any building_house method
    house->decay_services();
}