diff --git a/script.js b/script.js
index 51ce9ac..2dad4bc 100644
--- a/script.js
+++ b/script.js
@@ -1747,6 +1747,9 @@ class Simulation {
founded: this.year,
legitimacy: this.rng.range(0.68, 0.94),
cohesion: this.rng.range(0.58, 0.9),
+ charisma: this.rng.range(0.5, 1.5),
+ leaderStarted: this.year,
+ leaderTenureYears: this.rng.range(24, 68),
crisis: 0,
lastCrisisYear: this.year
};
@@ -1811,6 +1814,45 @@ class Simulation {
for (const polity of this.polities) this.samplePolityHistory(polity);
}
+ selectNewLeader(polity, forced = false) {
+ if (!polity) return;
+ const previousCharisma = clamp(polity.charisma ?? 1, 0.5, 1.5);
+ const center = this.getCityById(polity.centerCityId);
+ const centerStability = center ? clamp(center.loyalty ?? 0.5, 0, 1) : 0.5;
+ const institutionalBias = ((polity.legitimacy ?? 0.7) + (polity.cohesion ?? 0.6) + centerStability) / 3;
+ const randomLeader = this.rng.range(0.5, 1.5);
+ const continuity = forced ? 0.18 : 0.34;
+ const institutionalPull = 0.82 + institutionalBias * 0.36;
+ const nextCharisma = clamp(
+ previousCharisma * continuity + randomLeader * (1 - continuity) * institutionalPull,
+ 0.5,
+ 1.5
+ );
+ polity.charisma = nextCharisma;
+ polity.leaderStarted = this.year;
+ polity.leaderTenureYears = this.rng.range(22, 72);
+
+ const change = nextCharisma - previousCharisma;
+ polity.legitimacy = clamp((polity.legitimacy ?? 0.7) + change * 0.08 - (forced ? 0.035 : 0), 0, 1);
+ polity.cohesion = clamp((polity.cohesion ?? 0.6) + change * 0.045 - (forced ? 0.020 : 0), 0, 1);
+ if (forced) polity.crisis = clamp((polity.crisis || 0) + 0.04, 0, 1.5);
+ }
+
+ updatePolityLeaders() {
+ for (const polity of this.polities) {
+ polity.charisma = clamp(polity.charisma ?? 1, 0.5, 1.5);
+ polity.leaderStarted ??= polity.founded ?? this.year;
+ polity.leaderTenureYears ??= this.rng.range(24, 68);
+ const tenureYears = (this.year - polity.leaderStarted) / MONTHS_PER_YEAR;
+ const oldLeader = Math.max(0, tenureYears - polity.leaderTenureYears);
+ const crisisPressure = clamp((polity.crisis || 0) * 0.045, 0, 0.09);
+ const successionChance = clamp(oldLeader * 0.018 + crisisPressure, 0, 0.36);
+ if (successionChance > 0 && this.rng.next() < successionChance) {
+ this.selectNewLeader(polity, (polity.crisis || 0) > 0.85);
+ }
+ }
+ }
+
markPolityEnded(polity, reason = "dissolved") {
if (!polity) return;
const history = this.ensurePolityHistory(polity);
@@ -1916,7 +1958,7 @@ class Simulation {
const instability = this.polityInstability ? this.polityInstability(polity) : 0;
const agePressure = this.polityAgePressure ? this.polityAgePressure(polity) : 0;
- return Math.max(0,
+ const basePower = Math.max(0,
Math.sqrt(population) * 1.35 +
Math.sqrt(treasury) * 1.15 +
avgLoyalty * 16 +
@@ -1924,6 +1966,7 @@ class Simulation {
instability * 12 -
agePressure * 6
);
+ return basePower * clamp(polity.charisma ?? 1, 0.5, 1.5);
}
polityInfluenceRange(polity, wartime = false) {
@@ -2656,6 +2699,7 @@ class Simulation {
if (this.erodePolityLegitimacy) this.erodePolityLegitimacy();
if (this.triggerPolityCrises) this.triggerPolityCrises();
if (this.applyOldStateStress) this.applyOldStateStress();
+ if (this.updatePolityLeaders) this.updatePolityLeaders();
this.updateCityLoyalty();
this.absorbIndependentCities();
@@ -3059,6 +3103,9 @@ class Simulation {
founded: p.founded,
legitimacy: p.legitimacy,
cohesion: p.cohesion,
+ charisma: p.charisma,
+ leaderStarted: p.leaderStarted,
+ leaderTenureYears: p.leaderTenureYears,
crisis: p.crisis,
lastCrisisYear: p.lastCrisisYear
})),
@@ -3145,6 +3192,9 @@ class Simulation {
founded: p.founded || 0,
legitimacy: p.legitimacy ?? 0.7,
cohesion: p.cohesion ?? 0.6,
+ charisma: clamp(p.charisma ?? 1, 0.5, 1.5),
+ leaderStarted: p.leaderStarted ?? p.founded ?? sim.year,
+ leaderTenureYears: clamp(p.leaderTenureYears ?? 48, 12, 96),
crisis: p.crisis ?? 0,
lastCrisisYear: p.lastCrisisYear ?? sim.year
}));
@@ -3439,6 +3489,8 @@ function renderTooltip() {
${cityEthnicity ? `City majorityE${cityEthnicity}` : ""}
${city ? `State${polity ? `#${polity.id}` : "Independent"}` : ""}
${city ? `Loyalty${city.loyalty.toFixed(2)}` : ""}
+ ${polity ? `Charisma${clamp(polity.charisma ?? 1, 0.5, 1.5).toFixed(2)}` : ""}
+ ${polity ? `Leader tenure${Math.floor((sim.year - (polity.leaderStarted ?? polity.founded ?? sim.year)) / MONTHS_PER_YEAR)}y` : ""}
${polity ? `Treasury${polity.treasury.toFixed(1)}` : ""}
${polity ? `Center${polity.centerCityId === city.id ? "yes" : "no"}` : ""}
${agent ? `EthnicityE${agent.ethnicity}` : ""}