Files
Battle-for-Tismo/shared/data/politics.js
T
adrien e7ee5dbf0e Added the resource economy, its works and their icons
- Five resources (energy, steel, food, luxury, high-tech) with per-city stores, a world market, delivery and material upkeep.
- Resource-producing tile works built outside cities; every nation opens with the works its economy needs.
- Real game-icons.net art replacing the dummy icons for the new resources and works, with the credits updated.
2026-09-22 05:27:11 +02:00

233 lines
9.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Politics tuning: public opinion, approval penalties, propaganda campaigns,
// minority policies, migration, war crimes, uprisings and cultural impact.
//
// Every population (an ethnicity, indexed like the civilisations) holds an
// opinion of every other ethnicity and of every government. Opinions run from 0
// (hatred) to 1 (devotion) and start at the neutral 50%. All of it lives in
// shared/game_state/politics.js so the server simulates it and the browser only
// reads it from the snapshot.
// The opinion scale. `driftPerDay` pulls every opinion back toward the neutral
// baseline; at this rate the full 0..1 span recovers over roughly 45 years, so
// a campaign's goodwill lingers for years while a war crime is not forgotten
// for a generation.
export const OPINION = {
baseline: 0.5,
driftPerDay: 1.0 / (365.25 * 45),
min: 0.0,
max: 1.0,
};
// A region whose people approve of their government by less than
// `rebellionThreshold` suffers the penalties below. Construction slows
// exponentially as approval falls and the tax take collapses toward
// `taxFloorFraction` of its normal value.
export const APPROVAL = {
rebellionThreshold: 0.1,
constructionExponent: 2.5,
taxFloorFraction: 0.1,
};
// Popularity is counted in integer points, centred on 0 (neutral). The whole
// 0..1 opinion scale maps to -100..+100 points, one point being half a percent.
// What the player sees is a logistic of those points: 0 points reads 50%, +100
// reads 75% and -100 reads 25%. Opinions drift one point an hour toward
// neutrality, and war, captures and casualties move them in points.
export const POPULARITY = {
opinionPerPoint: 0.005,
// Steepness chosen so display(0) = 0.5 and display(100) = 0.75.
logisticScale: Math.log(3) / 100,
// Points a population's opinion of its government moves toward neutral each
// hour, in either direction.
driftPerHour: 1,
// Declaring war costs the aggressor this many points with every population.
declareWar: 8,
// Being declared on grants the victim this many points with its own people,
// but only when the aggressor was already loathed there (below threshold).
declaredOnBoost: 15,
declaredOnThreshold: 0.15,
// Points won or lost per military unit destroyed, and per city taken.
unitLoss: 1,
unitKill: 1,
cityLoss: 10,
cityCapture: 10,
// Pillaging a tile costs the aggressor standing with the whole world, doubled
// among the populations directly affected.
pillage: 6,
};
// A campaign costs nothing to start: it charges `defaultHourlyCost` for as long
// as it runs, like any other upkeep and shown in the budget. The player may
// raise or lower the spending up to `maxHourlyCost`; the opinion shift scales
// with it. A campaign stays in force until cancelled. `dailyDelta` is the
// opinion shift per day at the default spend and balanced cultural impact;
// `culturalWeight` is how strongly the two nations' cultural impacts bend the
// result.
//
// Convincing the last few points is the hardest: the daily shift is scaled by
// how much room is left before the opinion reaches its maximum (warming) or
// minimum (cooling), raised to `difficultyExponent`. The rate is unchanged at
// or below the neutral midpoint, so only the final approach to a devoted or
// hateful population suffers the diminishing returns that make the last
// percentage points disproportionately expensive.
//
// Opinions otherwise drift one point an hour toward neutral (0.12 a day), so a
// campaign must out-spend that drift to change minds; `dailyDelta` is set well
// above it.
export const PROPAGANDA = {
defaultHourlyCost: 8_000,
maxHourlyCost: 40_000,
dailyDelta: 0.18,
culturalWeight: 0.6,
difficultyExponent: 2,
};
export const CAMPAIGN_ETHNICITY = "ethnicity";
export const CAMPAIGN_GOVERNMENT = "government";
export const CAMPAIGN_WARM = 1;
export const CAMPAIGN_COOL = -1;
// Minority policies a government may proclaim. Any number may be active at
// once, but the same policy may not be enacted twice for the same ethnicity.
// `targetEffect` and `politicalCost` are the estimates the wizard shows; the
// model applies them through POLICY_OPINION below. Effects are stated in
// popularity points (the integer unit the model works in); only the final
// approval figure is ever shown as a percentage.
export const POLICY_CHAMPION = "champion";
export const POLICY_EXPEL = "expel";
export const POLICY_PROTECT = "protect";
export const POLICY_IMMIGRATION = "immigration";
export const POLICIES = [
{
id: POLICY_CHAMPION,
name: "Champion a minority",
description:
"Proclaim the state the protector of an ethnicity. Its people everywhere warm to you, but your own other populations resent the favouritism.",
targetEffect: "+30 popularity among the chosen people",
politicalCost: "−16 popularity among every other population in your country",
},
{
id: POLICY_PROTECT,
name: "Protect a minority",
description:
"Shield an ethnicity inside your borders. It raises their view of you at home and abroad, at some cost with your other populations.",
targetEffect: "+24 popularity among the chosen people",
politicalCost: "−8 popularity among every other population in your country",
},
{
id: POLICY_EXPEL,
name: "Expel a minority",
description:
"Force an ethnicity out of the country. Its people, in and outside your borders, will hate you for a long time, and they will press into your neighbours.",
targetEffect: "The people begin to leave for neighbouring countries",
politicalCost: "−80 popularity among the expelled people, remembered for a generation",
},
{
id: POLICY_IMMIGRATION,
name: "Encourage immigration",
description:
"Invite an ethnicity in. They will come from lands where their government is unpopular, but every other population in your country dislikes the influx.",
targetEffect: "The chosen people migrate into your country",
politicalCost: "−10 popularity among every other population in your country",
},
];
export function policyById(id) {
return POLICIES.find((policy) => policy.id === id) || null;
}
// Immediate opinion shifts of proclaiming each policy, addressed to the target
// ethnicity and to the rest of the nation's people.
export const POLICY_OPINION = {
championTarget: 0.15,
championMajority: -0.08,
protectTarget: 0.12,
protectMajority: -0.04,
expelTarget: -0.4,
immigrationOthers: -0.05,
};
// Daily share of a minority that is expelled to neighbouring tiles, and the
// rate at which an encouraged ethnicity is drawn into the country. Immigration
// is scaled by how much the newcomer dislikes their current government:
// a 5% approval government sheds people fast, a 90% one holds them.
export const MIGRATION = {
expelSharePerDay: 0.004,
immigrationSharePerDay: 0.002,
// People fly from any other airport city into a city with an airport. The
// share is per destination airport level, per source city, per day.
airportSharePerDay: 0.0005,
// The approval at or above which nobody is inclined to leave.
comfortableApproval: 0.9,
// People move only to a region at least this much richer per head, and the
// bar rises with the travel time: a destination `time` hours away must be
// `incomeRatioBase * (1 + time * incomeTimePenalty)` times as rich. So the
// closer and richer a place is, the more attractive.
incomeRatioBase: 2,
incomeTimePenalty: 0.02,
// People travel the transport network; a tile of open terrain costs this many
// hours at walking pace, a road or railway less, open water a flat sea rate.
peopleSpeed: 1,
seaHoursPerTile: 1.5,
// Land routes are only traced this far; beyond it a destination is reachable
// only by air.
maxTravelHours: 120,
// Of the people who qualify, this share of the poorer region moves per day,
// scaled by how far the ratio clears the bar and capped.
incomeSharePerDay: 0.0015,
incomeGapCap: 1,
// No single day moves more than this share of a region's people by land.
incomeMaxSharePerDay: 0.005,
// An airport flies people over the distance at a flat time cost, but moves at
// most this many people per day for each airport level on the route.
airHours: 6,
airPeoplePerDayPerLevel: 1000,
// At generation, regions of a nation trade people until their production per
// head is level. `equalizeTolerance` is the residual spread accepted and
// `equalizePasses` bounds the refinement; no region is emptied below
// `minRegionPopulation`.
equalizeTolerance: 0.005,
equalizePasses: 30,
minRegionPopulation: 20000,
};
// A pillaged tile is a war crime. The people of the tile loathe the aggressor
// immediately; the stain spreads to neighbours, the region, the victim nation
// and the watching world, and is remembered for a generation.
export const WAR_CRIME = {
tileOpinion: 0.35,
tileEthnicOpinion: 0.2,
neighbourFactor: 0.5,
regionalFactor: 0.3,
nationalFactor: 0.2,
worldFactor: 0.08,
memoryYears: 25,
};
// A mistreated minority rises up when it hates its government and adores the
// one that shares its ethnicity, taking up arms for the latter.
export const REBELLION = {
approvalMax: 0.1,
championApprovalMin: 0.7,
populationShareMin: 0.12,
cooldownDays: 60,
maxUnitsPerEthnicity: 8,
};
// Declaring war on a government its people like costs the aggressor dear; a war
// on a loathed government can even be popular. The penalty scales with how far
// the onlookers' opinion of the victim strays from neutral.
export const WAR_DECLARATION = {
penalty: 0.3,
};
// Cultural impact is a nation's share of the world's cultural output, so it is
// zero-sum by construction. A nation above the average impact slowly wins the
// world over to its government.
export const CULTURE_IMPACT = {
dailyPopularityGain: 0.0004,
};