Unbound exploration: let towns genuinely migrate

Reverts the home-range bound on exploration and the homestead anchor-clamp.
A town that must range far to eat now relocates for real - the South
drifting inland after a hard winter, an East scout wandering across the
map and homesteading where they land (some homes now 648 units from the
anchor). Kept the identity-hash explore direction (no southward bias) and
the centroid-relative communal cohesion clamp (that follows migration, it
doesn't fight it). Towns still hold distinct cultures and stable
populations - migration is emergent, not collapse.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 11:34:23 -04:00
parent f4e55feff7
commit e6fae4f305
+15 -30
View File
@@ -101,33 +101,21 @@ public class Npc
Vector2 fromHome = Position - anchor; Vector2 fromHome = Position - anchor;
float distHome = fromHome.Length(); float distHome = fromHome.Length();
// Stay in the home range. A person forages OUT from their village and // Venture outward, unbounded — a town that must range far to eat
// back, they don't march to the horizon — so past a search radius they // genuinely relocates over time (the South drifting inland after a
// turn homeward. This keeps a strip-happy town (the East) from slowly // hard winter, an East scout wandering into a neighbor). Direction is
// migrating into its neighbors instead of settling its own ground. // outward from home, or — when right at the anchor — a per-soul angle
const float SearchRadius = 140f; // from identity (NOT a fixed vector; the old default leaned hard +Y,
// i.e. south, which funnelled every town's explorers into one corner).
Vector2 dir; Vector2 dir;
if (distHome > SearchRadius) if (distHome > 1f)
{ dir = fromHome.Normalized();
dir = (-fromHome).Normalized(); // head back toward home
dir = dir.Rotated(PersonalityModifier * 0.6f); // slight veer, still homeward
}
else else
{ {
// Outward from home in a per-soul direction. When right at the float ang = (Name.GetHashCode() & 0xFFFF) / 65535f * Mathf.Tau;
// anchor there's no outward vector, so pick an angle from the dir = new Vector2(Mathf.Cos(ang), Mathf.Sin(ang));
// soul's identity — NOT a fixed vector (the old default leaned
// hard +Y, i.e. south, quietly funnelling every town's explorers
// into the southern corner).
if (distHome > 1f)
dir = fromHome.Normalized();
else
{
float ang = (Name.GetHashCode() & 0xFFFF) / 65535f * Mathf.Tau;
dir = new Vector2(Mathf.Cos(ang), Mathf.Sin(ang));
}
dir = dir.Rotated(PersonalityModifier * 1.4f + 0.6f); // circle the home range
} }
dir = dir.Rotated(PersonalityModifier * 1.2f); // personality veer, so explorers fan out
Position += dir * MoveSpeed; Position += dir * MoveSpeed;
SenseNodes(gm.World); SenseNodes(gm.World);
} }
@@ -745,14 +733,11 @@ public class Npc
Enter(NpcState.Gathering); // no stone yet — go get it Enter(NpcState.Gathering); // no stone yet — go get it
return; return;
} }
// Homestead on your OWN town's land — selfish souls sprawl (a // Homestead where you actually live — no snapping back to the
// wide range, no clustering to the shared center) but they build // town anchor. If a people have migrated (following food inland
// in the east if they're an East soul, not wherever they drifted // after a hard winter), their homes rise where they now are; the
// into a neighbor's territory. // town genuinely moves.
Vector2 anchor = gm.TownAnchor(HomeTown);
Vector2 spot = _hasHomeSpot ? _homeSpot : Position; Vector2 spot = _hasHomeSpot ? _homeSpot : Position;
Vector2 off = spot - anchor;
if (off.Length() > 120f) spot = anchor + off.Normalized() * 120f;
mine = Building.NewShelter(spot); mine = Building.NewShelter(spot);
mine.Town = HomeTown; mine.Town = HomeTown;
gm.Buildings.Add(mine); gm.Buildings.Add(mine);