Town-colored homes, split diagonally for mixed-culture households
Shelters now wear their town's culture color on the map (North cool-blue, South green, West gold, East red), dimmed by wear; granaries stay green, ruins dark. And bonds can now cross town lines: usually you pair within your own people (that's who you're near), but a wanderer who warms to someone from another town shacks up across cultures - the first quiet thread of contact between two peoples. When two towns share a roof, the house is drawn split diagonally, half each culture, visible across the map. Mechanics: buildings track their top-two resident home-towns (recomputed with occupancy, throttled to every 30 ticks since it's map-only); the bonding rule dropped its same-town requirement. A 240-day run produced 126 bonds, 40 of them cross-town (East+North, West+South, all combos), with populations still balanced - inter-cultural contact without destabilizing the towns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -164,6 +164,14 @@ public partial class GameManager : Node2D
|
||||
return new Vector2(World.GridSize / 2f, World.GridSize / 2f);
|
||||
}
|
||||
|
||||
/// <summary>The soul culture of a named town (for map coloring).</summary>
|
||||
public SoulType TownSoul(string town)
|
||||
{
|
||||
foreach (var t in _towns)
|
||||
if (t.Name == town) return t.Soul;
|
||||
return SoulType.Nature;
|
||||
}
|
||||
|
||||
public Vector2 TownCentroid(string town)
|
||||
{
|
||||
Vector2 sum = Vector2.Zero;
|
||||
@@ -325,6 +333,48 @@ public partial class GameManager : Node2D
|
||||
Npcs.AddRange(newborns);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tally which home-towns sleep under each shelter, storing the top two.
|
||||
/// A house with residents from two towns is a mixed household — someone
|
||||
/// shacked up across cultures — and gets drawn split on the map.
|
||||
/// </summary>
|
||||
private void RecomputeHouseholds()
|
||||
{
|
||||
var counts = new Dictionary<Building, Dictionary<string, int>>();
|
||||
|
||||
foreach (var npc in Npcs)
|
||||
{
|
||||
if (npc.State != NpcState.Resting) continue;
|
||||
Building? at = null; float best = 6.25f;
|
||||
foreach (var b in Buildings)
|
||||
{
|
||||
if (b.RestCapacity <= 0) continue;
|
||||
float d = npc.Position.DistanceSquaredTo(b.Site);
|
||||
if (d < best) { best = d; at = b; }
|
||||
}
|
||||
if (at == null) continue;
|
||||
if (!counts.TryGetValue(at, out var byTown))
|
||||
counts[at] = byTown = new Dictionary<string, int>();
|
||||
byTown[npc.HomeTown] = byTown.GetValueOrDefault(npc.HomeTown) + 1;
|
||||
}
|
||||
|
||||
foreach (var b in Buildings)
|
||||
{
|
||||
b.ResidentTownA = "";
|
||||
b.ResidentTownB = "";
|
||||
if (!counts.TryGetValue(b, out var byTown)) continue;
|
||||
|
||||
string a = "", bTown = ""; int ca = 0, cb = 0;
|
||||
foreach (var kv in byTown)
|
||||
{
|
||||
if (kv.Value > ca) { bTown = a; cb = ca; a = kv.Key; ca = kv.Value; }
|
||||
else if (kv.Value > cb) { bTown = kv.Key; cb = kv.Value; }
|
||||
}
|
||||
b.ResidentTownA = a;
|
||||
b.ResidentTownB = cb > 0 ? bTown : "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Where the community's feet actually are — new communal
|
||||
/// buildings are founded here, not at a scripted town center.</summary>
|
||||
public Vector2 CommunityCentroid()
|
||||
@@ -356,6 +406,11 @@ public partial class GameManager : Node2D
|
||||
if (at != null) at.Occupancy++;
|
||||
}
|
||||
|
||||
// Household composition (which cultures share each roof) — for the
|
||||
// map only, so it's cheap and refreshed occasionally, not every tick.
|
||||
if (TotalTicks % 30 == 0)
|
||||
RecomputeHouseholds();
|
||||
|
||||
TotalTicks++;
|
||||
|
||||
foreach (var npc in Npcs)
|
||||
|
||||
Reference in New Issue
Block a user