Expansion phase: settlements — the North joins the world
Runs 5-6 logged in FINDINGS.md (soul layer validated: nature debris 0.000 vs selfish 0.225 after two years; the ledger works). Towns are now real spatial structure: TownDef seeds (South at the bottom of the map, North at the top), NPCs affiliate with a home town, communal duty serves your own settlement (buildings are town-tagged, founding uses the town centroid and per-town shelter caps), and each town raises its own shelters and granary. Generational (North) soul profile added — temperament only until the knowledge system lands. Map shows town labels; rosters and CSVs carry town columns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+26
-15
@@ -22,6 +22,7 @@ public class Npc
|
||||
{
|
||||
// --- Identity & soul -------------------------------------------------
|
||||
public string Name = "";
|
||||
public string HomeTown = ""; // settlement affiliation — home, not a cage
|
||||
public SoulProfile Soul = SoulProfile.NatureSoul();
|
||||
public SoulImprint Imprint = new(); // triad — see SPEC §2 amendment
|
||||
public float PersonalityModifier; // per-NPC variance within a soul type
|
||||
@@ -193,24 +194,28 @@ public class Npc
|
||||
|
||||
if (IsCommunal)
|
||||
{
|
||||
// Communal duty is to your own settlement — you heat your own
|
||||
// town's hearths. (Helping a neighbor town is a later, deliberate
|
||||
// system, not an accident of the work queue.)
|
||||
|
||||
// 2. Construction sites.
|
||||
foreach (var b in gm.Buildings)
|
||||
if (!b.IsComplete && b.NeededMaterial is MaterialKind needed)
|
||||
if (b.Town == HomeTown && !b.IsComplete && b.NeededMaterial is MaterialKind needed)
|
||||
return needed;
|
||||
|
||||
// 3. Firewood for shelters running low.
|
||||
foreach (var b in gm.Buildings)
|
||||
if (b.WantsUpkeepWood)
|
||||
if (b.Town == HomeTown && b.WantsUpkeepWood)
|
||||
return MaterialKind.Wood;
|
||||
|
||||
// 4. Winter insurance: stock the granary while the land still gives.
|
||||
foreach (var b in gm.Buildings)
|
||||
if (b.WantsFood)
|
||||
if (b.Town == HomeTown && b.WantsFood)
|
||||
return MaterialKind.Food;
|
||||
|
||||
// 5. Ambition: expansion modules for sound, stocked shelters.
|
||||
foreach (var b in gm.Buildings)
|
||||
if (b.WantsExpansion)
|
||||
if (b.Town == HomeTown && b.WantsExpansion)
|
||||
return MaterialKind.Wood;
|
||||
}
|
||||
|
||||
@@ -381,12 +386,13 @@ public class Npc
|
||||
// shelters and eat from the granary — freeriding is the whole point.
|
||||
if (!IsCommunal) { Enter(NpcState.Socializing); return; }
|
||||
|
||||
// Priority 1: construction sites. Priority 2: shelters low on firewood.
|
||||
// Serve your own settlement's buildings, nearest first:
|
||||
// construction → firewood → granary deposits → expansion.
|
||||
Building? site = null;
|
||||
float bestDist = float.MaxValue;
|
||||
foreach (var b in gm.Buildings)
|
||||
{
|
||||
if (b.IsComplete) continue;
|
||||
if (b.Town != HomeTown || b.IsComplete) continue;
|
||||
float d = Position.DistanceSquaredTo(b.Site);
|
||||
if (d < bestDist) { bestDist = d; site = b; }
|
||||
}
|
||||
@@ -394,7 +400,7 @@ public class Npc
|
||||
{
|
||||
foreach (var b in gm.Buildings)
|
||||
{
|
||||
if (!b.WantsUpkeepWood) continue;
|
||||
if (b.Town != HomeTown || !b.WantsUpkeepWood) continue;
|
||||
float d = Position.DistanceSquaredTo(b.Site);
|
||||
if (d < bestDist) { bestDist = d; site = b; }
|
||||
}
|
||||
@@ -407,7 +413,7 @@ public class Npc
|
||||
{
|
||||
foreach (var b in gm.Buildings)
|
||||
{
|
||||
if (!b.WantsFood) continue;
|
||||
if (b.Town != HomeTown || !b.WantsFood) continue;
|
||||
float d = Position.DistanceSquaredTo(b.Site);
|
||||
if (d < bestDist) { bestDist = d; site = b; }
|
||||
}
|
||||
@@ -417,7 +423,7 @@ public class Npc
|
||||
{
|
||||
foreach (var b in gm.Buildings)
|
||||
{
|
||||
if (!b.WantsExpansion) continue;
|
||||
if (b.Town != HomeTown || !b.WantsExpansion) continue;
|
||||
float d = Position.DistanceSquaredTo(b.Site);
|
||||
if (d < bestDist) { bestDist = d; site = b; }
|
||||
}
|
||||
@@ -425,26 +431,31 @@ public class Npc
|
||||
|
||||
if (site == null)
|
||||
{
|
||||
// Communal-first founding, near where the community actually
|
||||
// gathers. Shelters first; once the village stands, the granary.
|
||||
// Communal-first founding, near where this settlement's people
|
||||
// actually gather. Shelters first; once the village stands, the
|
||||
// granary.
|
||||
bool carriesStone = Inventory.TryGetValue(MaterialKind.Stone, out float stone) && stone > 0f;
|
||||
int shelters = 0;
|
||||
bool granaryExists = false;
|
||||
foreach (var b in gm.Buildings)
|
||||
{
|
||||
if (b.Town != HomeTown) continue;
|
||||
if (b.Kind == BuildingKind.Granary) granaryExists = true;
|
||||
else shelters++;
|
||||
}
|
||||
|
||||
int shelterCap = gm.TownPopulation(HomeTown) / 3;
|
||||
var jitter = new Vector2(PersonalityModifier * 25f, -PersonalityModifier * 25f);
|
||||
if (shelters < gm.NpcCount / 3 && carriesStone)
|
||||
if (shelters < shelterCap && carriesStone)
|
||||
{
|
||||
site = Building.NewShelter(gm.CommunityCentroid() + jitter);
|
||||
site = Building.NewShelter(gm.TownCentroid(HomeTown) + jitter);
|
||||
site.Town = HomeTown;
|
||||
gm.Buildings.Add(site);
|
||||
}
|
||||
else if (!granaryExists && shelters >= gm.NpcCount / 3 && carriesStone)
|
||||
else if (!granaryExists && shelters >= shelterCap && carriesStone)
|
||||
{
|
||||
site = Building.NewGranary(gm.CommunityCentroid() + jitter);
|
||||
site = Building.NewGranary(gm.TownCentroid(HomeTown) + jitter);
|
||||
site.Town = HomeTown;
|
||||
gm.Buildings.Add(site);
|
||||
}
|
||||
else { Enter(NpcState.Socializing); return; }
|
||||
|
||||
Reference in New Issue
Block a user