2026-05-14 20:15:23 +09:00
|
|
|
const Terrain = Object.freeze({
|
|
|
|
|
PLAINS: 0,
|
|
|
|
|
FOREST: 1,
|
|
|
|
|
MOUNTAIN: 2,
|
|
|
|
|
WATER: 3,
|
|
|
|
|
DESERT: 4
|
|
|
|
|
});
|
|
|
|
|
const terrainInfo = [
|
|
|
|
|
{ name: "Plains", color: [82, 102, 74], move: 1.0, fertility: 0.55, mineral: 0.05, regen: 0.025 },
|
|
|
|
|
{ name: "Forest", color: [58, 91, 68], move: 1.3, fertility: 0.62, mineral: 0.08, regen: 0.038 },
|
|
|
|
|
{ name: "Mountains", color: [104, 105, 101], move: 2.6, fertility: 0.18, mineral: 0.78, regen: 0.012 },
|
|
|
|
|
{ name: "Water", color: [52, 78, 103], move: 7.0, fertility: 0.04, mineral: 0.02, regen: 0.004 },
|
|
|
|
|
{ name: "Desert", color: [139, 123, 86], move: 1.55, fertility: 0.16, mineral: 0.16, regen: 0.014 }
|
|
|
|
|
];
|
|
|
|
|
const WEEKS_PER_MONTH = 4;
|
|
|
|
|
const MONTHS_PER_YEAR = 12;
|
|
|
|
|
const WEEKS_PER_YEAR = MONTHS_PER_YEAR * WEEKS_PER_MONTH;
|
|
|
|
|
const SimConfig = Object.freeze({
|
|
|
|
|
population: Object.freeze({
|
|
|
|
|
softCapStart: 0.85,
|
|
|
|
|
hardCapScale: 1.35,
|
|
|
|
|
carryingCapacityBase: 2.4,
|
|
|
|
|
carryingCapacityFertility: 7.8,
|
|
|
|
|
carryingCapacityMineral: 1.8,
|
|
|
|
|
carryingCapacityFarmland: 5.5,
|
|
|
|
|
pressurePenaltyBase: 0.2,
|
|
|
|
|
pressureReproductionPenalty: 0.16
|
|
|
|
|
}),
|
|
|
|
|
city: Object.freeze({
|
2026-05-16 13:48:08 +09:00
|
|
|
maxCities: 300,
|
|
|
|
|
agingStartYears: 180,
|
|
|
|
|
agingDecayPerYear: 0.002,
|
|
|
|
|
agingOverCapAttrition: 0.035,
|
2026-05-17 21:08:03 +09:00
|
|
|
agingRestoreMinWear: 0.08,
|
2026-05-16 13:48:08 +09:00
|
|
|
agingRestoreCostPerWear: 70,
|
2026-05-17 21:08:03 +09:00
|
|
|
agingRestoreBudgetShare: 0.18
|
2026-05-14 20:15:23 +09:00
|
|
|
}),
|
|
|
|
|
render: Object.freeze({
|
|
|
|
|
graphThrottleMs: 1400,
|
|
|
|
|
statsThrottleMs: 900,
|
|
|
|
|
historyWindowYears: 2000,
|
|
|
|
|
historySamplePaddingYears: 240
|
|
|
|
|
}),
|
|
|
|
|
technology: Object.freeze({
|
|
|
|
|
cityDiffusion: 0.0022,
|
|
|
|
|
tradeDiffusion: 0.0032,
|
|
|
|
|
cityInnovation: 0.00045
|
|
|
|
|
}),
|
|
|
|
|
polity: Object.freeze({
|
|
|
|
|
minimumPerCapitaFood: 0.06,
|
2026-05-16 13:48:08 +09:00
|
|
|
logisticsDistance: 34,
|
|
|
|
|
charismaMin: 0.5,
|
2026-05-17 21:08:03 +09:00
|
|
|
charismaMax: 1.5,
|
|
|
|
|
charismaAverage: 1.0,
|
|
|
|
|
leaderTenureMinYears: 22,
|
|
|
|
|
leaderTenureMaxYears: 72
|
|
|
|
|
}),
|
|
|
|
|
campaign: Object.freeze({
|
|
|
|
|
checkIntervalYears: 3,
|
|
|
|
|
baseChance: 0.18,
|
|
|
|
|
minCooldownYears: 12,
|
|
|
|
|
maxCooldownYears: 45,
|
|
|
|
|
maxActiveCampaigns: 12,
|
|
|
|
|
frontierTargetChance: 0.35,
|
|
|
|
|
nonStateTargetChance: 0.30,
|
|
|
|
|
independentCityTargetChance: 0.20,
|
|
|
|
|
polityTargetChance: 0.15,
|
|
|
|
|
baseDurationYears: 8,
|
|
|
|
|
maxDurationYears: 25,
|
|
|
|
|
claimRadius: 8,
|
|
|
|
|
maxTargetDistance: 48
|
2026-05-14 20:15:23 +09:00
|
|
|
}),
|
2026-09-16 13:51:43 +09:00
|
|
|
nomad: Object.freeze({
|
|
|
|
|
enabled: true,
|
|
|
|
|
maxBands: 24,
|
|
|
|
|
enabledAfterYears: 120,
|
|
|
|
|
spawnCheckIntervalYears: 25,
|
|
|
|
|
baseSpawnChance: 0.28,
|
|
|
|
|
minPopulation: 80,
|
|
|
|
|
maxPopulation: 420,
|
|
|
|
|
minRadius: 5,
|
|
|
|
|
maxRadius: 14,
|
|
|
|
|
movementIntervalYears: 2,
|
|
|
|
|
maxMoveDistance: 9,
|
|
|
|
|
grazingGain: 0.035,
|
|
|
|
|
overgrazingDamage: 0.018,
|
|
|
|
|
resourceConsumption: 0.012,
|
|
|
|
|
herdSoftCapPerPop: 2.4,
|
|
|
|
|
herdHardCapPerPop: 4.0,
|
|
|
|
|
resourceSoftCapPerPop: 1.2,
|
|
|
|
|
resourceHardCapPerPop: 2.5,
|
|
|
|
|
grazingDiminishingPower: 1.35,
|
|
|
|
|
urbanAbsorptionControl: 0.55,
|
|
|
|
|
cityInteractionRadius: 8,
|
|
|
|
|
tradeChance: 0.22,
|
|
|
|
|
raidChance: 0.14,
|
|
|
|
|
invasionChance: 0.025,
|
|
|
|
|
confederationPopulation: 900,
|
|
|
|
|
confederationPrestige: 1.8,
|
|
|
|
|
splitPopulation: 1200,
|
|
|
|
|
sedentarizationChance: 0.035,
|
|
|
|
|
collapseCohesion: 0.12,
|
|
|
|
|
influenceCulturalTrace: 0.006,
|
|
|
|
|
influenceMobilePopulationTrace: 0.004,
|
|
|
|
|
visualDurationYears: 20
|
|
|
|
|
}),
|
2026-05-14 20:15:23 +09:00
|
|
|
polityAccess: Object.freeze({
|
|
|
|
|
enabled: true,
|
2026-05-17 21:08:03 +09:00
|
|
|
maxSearchDepth: 8,
|
|
|
|
|
indirectPenalty: 0.012,
|
|
|
|
|
perHopPenalty: 0.006,
|
|
|
|
|
noAccessPenalty: 0.045,
|
|
|
|
|
lowLoyaltyTransitPenalty: 0.012,
|
|
|
|
|
foreignTransitPenalty: 0.008
|
2026-05-14 20:15:23 +09:00
|
|
|
}),
|
|
|
|
|
culture: Object.freeze({
|
|
|
|
|
spreadRadius: 3,
|
|
|
|
|
cityWeight: 0.035,
|
|
|
|
|
routeWeight: 1.35,
|
|
|
|
|
minimumInfluence: 0.18
|
|
|
|
|
}),
|
|
|
|
|
route: Object.freeze({
|
|
|
|
|
maxRouteLength: 42,
|
2026-09-16 13:51:43 +09:00
|
|
|
adaptiveMaxRouteLength: 72,
|
|
|
|
|
isolatedCityBootstrap: true,
|
|
|
|
|
isolatedRouteSearchLimit: 8,
|
|
|
|
|
isolatedRouteRadius: 4,
|
|
|
|
|
newRoutePheromoneSeed: 2.5,
|
|
|
|
|
newRouteGraceBoost: 18,
|
|
|
|
|
isolatedRouteMinStrength: 0.12,
|
2026-05-14 20:15:23 +09:00
|
|
|
pheromoneDecay: 0.996,
|
|
|
|
|
pheromoneDiffusion: 0.045,
|
|
|
|
|
maxPheromone: 18,
|
|
|
|
|
routePheromoneBuild: 0.18,
|
|
|
|
|
routePheromoneMaintain: 0.12,
|
|
|
|
|
routeUpkeepPerTile: 0.018,
|
|
|
|
|
routeWeakThreshold: 8,
|
2026-09-16 13:51:43 +09:00
|
|
|
routeUnsupportedDecay: 3,
|
2026-05-14 20:15:23 +09:00
|
|
|
routeMaintainedBoost: 10
|
|
|
|
|
}),
|
2026-09-16 13:51:43 +09:00
|
|
|
war: Object.freeze({
|
|
|
|
|
pressureMemoryDecay: 0.88,
|
|
|
|
|
cityDamageMinRate: 0.003,
|
|
|
|
|
cityDamageMaxRate: 0.08,
|
|
|
|
|
captureLossBase: 0.08,
|
|
|
|
|
captureLossMax: 0.32,
|
|
|
|
|
exhaustionPopulationLossBase: 0.0007,
|
|
|
|
|
exhaustionPopulationLossMax: 0.012,
|
|
|
|
|
warZoneTileLossRate: 0.006,
|
|
|
|
|
majorSettlementThreshold: 1.25
|
|
|
|
|
}),
|
2026-05-14 20:15:23 +09:00
|
|
|
disaster: Object.freeze({
|
|
|
|
|
damageScale: 1.5,
|
|
|
|
|
majorLossRate: 0.05,
|
|
|
|
|
checkIntervalYears: 8,
|
|
|
|
|
baseChance: 0.76,
|
|
|
|
|
minRadius: 6,
|
|
|
|
|
maxRadius: 34,
|
|
|
|
|
rareLargeChance: 0.10,
|
|
|
|
|
largeRadiusMin: 28,
|
|
|
|
|
largeRadiusMax: 54,
|
|
|
|
|
minIntensity: 0.25,
|
|
|
|
|
maxIntensity: 0.95,
|
|
|
|
|
visualDurationYears: 18,
|
|
|
|
|
maxActiveVisuals: 12,
|
|
|
|
|
maxHistory: 160
|
|
|
|
|
}),
|
|
|
|
|
frontierWave: Object.freeze({
|
|
|
|
|
enabledAfterYears: 300,
|
|
|
|
|
intervalYears: 80,
|
|
|
|
|
chance: 0.45,
|
2026-09-16 13:51:43 +09:00
|
|
|
minPopulation: 60,
|
|
|
|
|
maxPopulation: 140,
|
|
|
|
|
edgeBand: 4
|
2026-05-14 20:15:23 +09:00
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
function years(value) {
|
|
|
|
|
return value * WEEKS_PER_YEAR;
|
|
|
|
|
}
|