Run 11: satiation and quarry return - clean two-year stability
Granary satiation: hunger scars motivate storage only until town capacity could carry a winter (pop x 45); sprawl of ~10 granaries falls to the sated level. Quarry return: packs stuck with undroppable stone after rebuilds made starving porters; unneeded stone goes back to the quarry (matter conserved, packs unclogged). Also corrects the record: run 10's zero-ruin year-2 claim came from CSV contamination by orphaned headless child processes (console wrapper kills didn't kill the tree). Clean verified result now: year-1 catastrophe and rebuild, then year-2 at 0.77 nourishment, zero new ruins, 100% condition, textbook granary sawtooth. Open in FINDINGS: accessible stone exhaustion plateaus housing below population - a Phase 3 economy question (deeper quarrying, stockpiles). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+38
-4
@@ -259,8 +259,10 @@ public class Npc
|
||||
return MaterialKind.Food;
|
||||
|
||||
// 4.5. Hunger remembered, and nowhere to store against it:
|
||||
// gather the surplus that will found the granary.
|
||||
if (_hungerMemory && !TownHasFoodStorage(gm))
|
||||
// gather the surplus that will found the granary — until the
|
||||
// town's storage could carry a winter. Scars motivate; they
|
||||
// shouldn't compel forever. Enough is a real quantity.
|
||||
if (_hungerMemory && !TownHasFoodStorage(gm) && !TownStorageSated(gm))
|
||||
return MaterialKind.Food;
|
||||
|
||||
// 5. Ambition: expansion modules for sound, stocked shelters.
|
||||
@@ -298,6 +300,20 @@ public class Npc
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enough is a real quantity: true when the town's total granary
|
||||
/// capacity (built or building) could carry its people through a
|
||||
/// winter (~45 food per resident). Past this, no new storage rises.
|
||||
/// </summary>
|
||||
private bool TownStorageSated(GameManager gm)
|
||||
{
|
||||
float capacity = 0f;
|
||||
foreach (var b in gm.Buildings)
|
||||
if (b.Town == HomeTown && b.Kind == BuildingKind.Granary && !b.IsRuined)
|
||||
capacity += Building.FoodStockCap;
|
||||
return capacity >= gm.TownPopulation(HomeTown) * 45f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When full but mostly carrying the wrong thing, set the excess down to
|
||||
/// make room for what's actually needed. Lossy for now —
|
||||
@@ -526,14 +542,32 @@ public class Npc
|
||||
}
|
||||
// Hunger remembered + a surplus with nowhere to put it → the
|
||||
// barn goes where the harvest is. Founded by scarcity survived,
|
||||
// not by abundance alone; another appears only when this fills.
|
||||
else if (_hungerMemory &&
|
||||
// not by abundance alone — and only until the town's storage
|
||||
// could carry a winter. Enough is enough.
|
||||
else if (_hungerMemory && !TownStorageSated(gm) &&
|
||||
Inventory.TryGetValue(MaterialKind.Food, out float surplus) && surplus > 8f)
|
||||
{
|
||||
site = Building.NewGranary(Position + jitter);
|
||||
site.Town = HomeTown;
|
||||
gm.Buildings.Add(site);
|
||||
}
|
||||
// Nothing needs your stone: take it back to the quarry rather
|
||||
// than carry a house on your back forever. Matter is conserved;
|
||||
// packs unclog; the quarry is the town's bank.
|
||||
else if (Inventory.TryGetValue(MaterialKind.Stone, out float stoneCarried) && stoneCarried > 5f)
|
||||
{
|
||||
var quarry = gm.World.FindNearestAny(MaterialKind.Stone, Position);
|
||||
if (quarry == null) { Enter(NpcState.Socializing); return; }
|
||||
Activity = "returning stone to the quarry";
|
||||
MoveToward(quarry.Cell);
|
||||
if (Position.DistanceTo(quarry.Cell) < ArriveDist)
|
||||
{
|
||||
quarry.Amount = Mathf.Min(quarry.MaxAmount, quarry.Amount + stoneCarried);
|
||||
Inventory[MaterialKind.Stone] = 0f;
|
||||
Enter(NpcState.Socializing);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else { Enter(NpcState.Socializing); return; }
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,21 @@ public class World
|
||||
return taken;
|
||||
}
|
||||
|
||||
/// <summary>Nearest node of a kind regardless of amount — for returning
|
||||
/// unneeded minerals to the quarry rather than carrying them forever.</summary>
|
||||
public ResourceNode? FindNearestAny(MaterialKind kind, Vector2 position)
|
||||
{
|
||||
ResourceNode? best = null;
|
||||
float bestDist = float.MaxValue;
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
if (node.Kind != kind) continue;
|
||||
float d = position.DistanceSquaredTo(node.Cell);
|
||||
if (d < bestDist) { best = node; bestDist = d; }
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called once per in-game day by GameManager. Season multipliers throttle
|
||||
/// regeneration — winter is when the granary earns its clay.
|
||||
|
||||
Reference in New Issue
Block a user