diff --git a/scripts/NPC.cs b/scripts/NPC.cs index f717762..cd6ca89 100644 --- a/scripts/NPC.cs +++ b/scripts/NPC.cs @@ -370,6 +370,13 @@ public class Npc if (foodCarried < reserveTarget && (Nourishment < 0.8f || Soul.Accumulation > 0.5f)) return MaterialKind.Food; + // Homeless too long → gather stone for a roof, whatever your soul. + // Even the selfish build private shelter; they just won't build it + // for anyone else. (Communal souls also queue this below, guarded by + // whether the town already has a shelter under construction.) + if (!IsCommunal && _unshelteredRests >= 3) + return MaterialKind.Stone; + if (IsCommunal) { // Communal duty is to your own settlement — you heat your own @@ -508,8 +515,16 @@ public class Npc if (TotalCarried() >= EffectiveCapacity) { // A full pack means different things to different souls: - // communal souls take it to the village; hoarders sit on it. - if (!IsCommunal) { Enter(NpcState.Socializing); return; } + // communal souls take it to the village; hoarders sit on it — + // unless they're homeless and carrying stone, in which case even + // a selfish soul goes to raise their own roof. + if (!IsCommunal) + { + bool hasStone = Inventory.TryGetValue(MaterialKind.Stone, out float s) && s > 0f; + if (_unshelteredRests >= 3 && hasStone) { Enter(NpcState.Building); return; } + Enter(NpcState.Socializing); + return; + } Inventory.TryGetValue(demanded, out float haveDemanded); if (haveDemanded >= EffectiveCapacity * 0.25f) { Enter(NpcState.Building); return; } @@ -592,9 +607,14 @@ public class Npc Inventory.TryGetValue(_targetNode.Kind, out float have); Inventory[_targetNode.Kind] = have + taken; - // Enough of what's needed? Communal souls go deliver it. + // Enough of what's needed? Communal souls go deliver it; a + // homeless selfish soul with enough stone goes to build its own. Inventory.TryGetValue(PickGatherTarget(gm), out float demandedHave); - if (IsCommunal && demandedHave >= EffectiveCapacity * 0.5f) Enter(NpcState.Building); + if (IsCommunal && demandedHave >= EffectiveCapacity * 0.5f) + Enter(NpcState.Building); + else if (!IsCommunal && _unshelteredRests >= 3 && + Inventory.TryGetValue(MaterialKind.Stone, out float st) && st >= 10f) + Enter(NpcState.Building); } } @@ -628,11 +648,65 @@ public class Npc } } + /// + /// A self-interested soul raising a roof for itself — private property, + /// not communal service. Contributes to their own in-progress shelter, + /// or founds one at their spot if carrying stone. This is why an + /// all-selfish town (the East) has homes at all: not cooperation, just + /// each person eventually housing themselves. + /// + private void FoundOwnShelter(GameManager gm) + { + // Contribute to an unfinished shelter near me (likely my own). + Building? mine = null; + float best = 900f; // within 30 units + foreach (var b in gm.Buildings) + { + if (b.Town != HomeTown || b.IsComplete || b.Kind != BuildingKind.Shelter) continue; + float d = Position.DistanceSquaredTo(b.Site); + if (d < best) { best = d; mine = b; } + } + + if (mine == null) + { + // Found one where I sleep — the selfish homestead where they + // please, no clustering toward the town's shared center. + if (!Inventory.TryGetValue(MaterialKind.Stone, out float stone) || stone <= 0f) + { + Enter(NpcState.Gathering); // no stone yet — go get it + return; + } + Vector2 spot = _hasHomeSpot ? _homeSpot : Position; + mine = Building.NewShelter(spot); + mine.Town = HomeTown; + gm.Buildings.Add(mine); + _unshelteredRests = 0; + Godot.GD.Print($"[Homestead] day {gm.Day}: {Name} ({HomeTown}) raises a private roof."); + } + + Activity = "building my own shelter"; + MoveToward(mine.Site); + if (Position.DistanceTo(mine.Site) < ArriveDist) + { + bool delivered = mine.Deliver(this); + if (!delivered || mine.IsComplete) Enter(NpcState.Socializing); + } + } + private void TickBuilding(GameManager gm) { - // Communal work is for communal souls. The others still sleep in the - // shelters and eat from the granary — freeriding is the whole point. - if (!IsCommunal) { Enter(NpcState.Socializing); return; } + // Communal work is for communal souls — the others don't tend the + // village's hearths or fill its granary. But even a selfish soul + // builds a roof for ITSELF: a shelter is private property, and after + // enough rough nights they'll raise one (the East's "personal wealth + // infrastructure"). So non-communal souls pass only when personally + // homeless, and only to found their own shelter — not to serve. + if (!IsCommunal) + { + if (_unshelteredRests < 3) { Enter(NpcState.Socializing); return; } + FoundOwnShelter(gm); + return; + } // Serve your own settlement's buildings, nearest first: // construction → firewood → granary deposits → expansion.