using System.Collections.Generic; using Godot; namespace WorldSim; public enum BuildingKind { Shelter, Granary } /// /// Modular buildings (SPEC §6): physically-sequential construction /// (foundation → walls → roof) with multi-NPC material delivery. /// /// Buildings are the world's metabolism: /// Shelters — burn firewood daily, decay into ruins if neglected, and /// accept expansion modules that permanently raise their burn. /// Granary — the village's winter insurance: stores food surplus in the /// abundant seasons, feeds the hungry when the land gives nothing. /// Its walls are clay — the first real use for it. /// public class Building { public BuildingKind Kind = BuildingKind.Shelter; public Vector2 Site; /// Which settlement this building belongs to. public string Town = ""; public static Building NewShelter(Vector2 site) => new() { Kind = BuildingKind.Shelter, Site = site, Stages = new() { new("foundation", MaterialKind.Stone, 10f), new("walls", MaterialKind.Wood, 20f), new("roof", MaterialKind.Wood, 10f), }, }; public static Building NewGranary(Vector2 site) => new() { Kind = BuildingKind.Granary, Site = site, Stages = new() { new("foundation", MaterialKind.Stone, 10f), new("walls", MaterialKind.Clay, 25f), new("roof", MaterialKind.Wood, 10f), }, }; // --- Construction ---------------------------------------------------- public record Stage(string Name, MaterialKind Material, float Cost); public List Stages = new(); public int CurrentStage; public float Delivered; // toward the current stage's cost public bool IsComplete => CurrentStage >= Stages.Count; public MaterialKind? NeededMaterial => IsComplete ? null : Stages[CurrentStage].Material; public float StageProgress => IsComplete ? 1f : Delivered / Stages[CurrentStage].Cost; // --- Shelter: upkeep ------------------------------------------------- public const float UpkeepStockCap = 10f; public float UpkeepStock; // delivered wood reserve public float Condition = 1f; // 1 = sound; 0 = ruin public bool IsRuined => Condition <= 0f; /// Base burn plus half a unit per expansion module, forever. public float DailyBurn => Kind == BuildingKind.Shelter ? 2f + Modules * 0.5f : 0f; /// True when this shelter wants a firewood delivery. public bool WantsUpkeepWood => Kind == BuildingKind.Shelter && IsComplete && !IsRuined && UpkeepStock < UpkeepStockCap * 0.7f; // --- Shelter: expansion modules -------------------------------------- public const int ModuleCap = 4; public const float ModuleCost = 15f; // wood per module public int Modules; public float ModuleDelivered; // toward the next module /// Sound, stocked shelters accept expansion work. public bool WantsExpansion => Kind == BuildingKind.Shelter && IsComplete && !IsRuined && Modules < ModuleCap && !WantsUpkeepWood; /// How many can sleep here. Expansion adds beds — growth has a /// human meaning, not just a bigger square. public int RestCapacity => Kind == BuildingKind.Shelter && IsComplete && !IsRuined ? 3 + Modules : 0; // --- Granary: food store --------------------------------------------- public const float FoodStockCap = 150f; public float FoodStock; /// True when the granary has room for more food. public bool WantsFood => Kind == BuildingKind.Granary && IsComplete && FoodStock < FoodStockCap; /// Take food out for a hungry villager. Returns amount granted. public float WithdrawFood(float requested) { float granted = Mathf.Min(requested, FoodStock); FoodStock -= granted; return granted; } // --- Daily tick ------------------------------------------------------- /// Called once per in-game day by GameManager. public void DailyUpkeep() { if (Kind != BuildingKind.Shelter || !IsComplete || IsRuined) return; if (UpkeepStock >= DailyBurn) { UpkeepStock -= DailyBurn; Condition = Mathf.Min(1f, Condition + 0.05f); // tended buildings mend } else { UpkeepStock = 0f; Condition -= 0.1f; // neglect shows in ~10 days } } // --- Delivery -------------------------------------------------------- /// /// The visitor delivers what the building currently needs — construction /// material while building; then firewood/expansion (shelter) or food /// (granary). Returns false if they had nothing useful to give. /// public bool Deliver(Npc npc) { if (IsRuined) return false; if (!IsComplete) { var stage = Stages[CurrentStage]; if (!npc.Inventory.TryGetValue(stage.Material, out float have) || have <= 0f) return false; float take = Mathf.Min(have, stage.Cost - Delivered); npc.Inventory[stage.Material] = have - take; Delivered += take; if (Delivered >= stage.Cost) { CurrentStage++; Delivered = 0f; } return take > 0f; } if (Kind == BuildingKind.Granary) { // Deposit real surplus only (keep 5) — and never redeposit what // was just withdrawn: depositors must arrive carrying > 8. if (!npc.Inventory.TryGetValue(MaterialKind.Food, out float carried) || carried <= 8f) return false; float space = FoodStockCap - FoodStock; float taken = Mathf.Min(carried - 5f, space); if (taken <= 0f) return false; npc.Inventory[MaterialKind.Food] = carried - taken; FoodStock += taken; return true; } // Shelter: reserve first — never expand a shelter you can't heat. if (!npc.Inventory.TryGetValue(MaterialKind.Wood, out float wood) || wood <= 0f) return false; float woodSpace = UpkeepStockCap - UpkeepStock; if (woodSpace > 0f) { float taken = Mathf.Min(wood, woodSpace); npc.Inventory[MaterialKind.Wood] = wood - taken; UpkeepStock += taken; return taken > 0f; } if (Modules < ModuleCap) { float take = Mathf.Min(wood, ModuleCost - ModuleDelivered); npc.Inventory[MaterialKind.Wood] = wood - take; ModuleDelivered += take; if (ModuleDelivered >= ModuleCost) { Modules++; ModuleDelivered = 0f; } return take > 0f; } return false; } // Ruins persist in the world — they are not removed. What a place was // remains visible in what it left behind. }