2026-05-14 20:15:23 +09:00
|
|
|
const Terrain = Object.freeze({
|
|
|
|
|
PLAINS: 0,
|
|
|
|
|
FOREST: 1,
|
|
|
|
|
MOUNTAIN: 2,
|
|
|
|
|
WATER: 3,
|
|
|
|
|
DESERT: 4
|
|
|
|
|
});
|
|
|
|
|
const terrainInfo = [
|
2026-09-16 20:53:17 +09:00
|
|
|
{ name: "平原", color: [82, 102, 74], move: 1.0, fertility: 0.55, mineral: 0.05, regen: 0.025 },
|
|
|
|
|
{ name: "森林", color: [58, 91, 68], move: 1.3, fertility: 0.62, mineral: 0.08, regen: 0.038 },
|
|
|
|
|
{ name: "山地", color: [104, 105, 101], move: 2.6, fertility: 0.18, mineral: 0.78, regen: 0.012 },
|
|
|
|
|
{ name: "水域", color: [52, 78, 103], move: 7.0, fertility: 0, mineral: 0.02, regen: 0.004 },
|
|
|
|
|
{ name: "砂漠", color: [139, 123, 86], move: 1.55, fertility: 0.16, mineral: 0.16, regen: 0.014 }
|
2026-05-14 20:15:23 +09:00
|
|
|
];
|
|
|
|
|
const WEEKS_PER_MONTH = 4;
|
|
|
|
|
const MONTHS_PER_YEAR = 12;
|
|
|
|
|
const WEEKS_PER_YEAR = MONTHS_PER_YEAR * WEEKS_PER_MONTH;
|
|
|
|
|
const SimConfig = Object.freeze({
|
2026-09-16 20:53:17 +09:00
|
|
|
world: Object.freeze({
|
|
|
|
|
initialPopulation: 50000,
|
|
|
|
|
defaultSize: 160
|
|
|
|
|
}),
|
2026-05-14 20:15:23 +09:00
|
|
|
population: Object.freeze({
|
2026-09-16 20:53:17 +09:00
|
|
|
populationScale: 10,
|
2026-05-14 20:15:23 +09:00
|
|
|
carryingCapacityBase: 2.4,
|
|
|
|
|
carryingCapacityFertility: 7.8,
|
|
|
|
|
carryingCapacityMineral: 1.8,
|
|
|
|
|
carryingCapacityFarmland: 5.5,
|
2026-09-17 22:32:25 +09:00
|
|
|
territoryExitPopulationLossRate: 0.12,
|
|
|
|
|
humanDataMinPopulation: 1.0
|
2026-05-14 20:15:23 +09:00
|
|
|
}),
|
|
|
|
|
city: Object.freeze({
|
2026-09-16 20:53:17 +09:00
|
|
|
maxCities: 1200,
|
|
|
|
|
initialPopulation: 400,
|
|
|
|
|
abandonmentPopulation: 200,
|
|
|
|
|
populationScale: 10,
|
|
|
|
|
foodProductionScale: 10,
|
|
|
|
|
postShockRecoveryGrowth: 0.045,
|
|
|
|
|
overCapacityAttrition: 0.020,
|
|
|
|
|
naturalFoundingMinDistance: 6,
|
|
|
|
|
naturalFoundingStartYears: 160,
|
|
|
|
|
naturalFoundingIntervalYears: 5,
|
|
|
|
|
naturalFoundingReferenceArea: 160 * 160,
|
|
|
|
|
naturalFoundingMaxPerCycle: 8,
|
|
|
|
|
foundingCompetitionRadius: 9,
|
2026-05-14 20:15:23 +09:00
|
|
|
}),
|
|
|
|
|
render: Object.freeze({
|
|
|
|
|
graphThrottleMs: 1400,
|
|
|
|
|
statsThrottleMs: 900,
|
|
|
|
|
historyWindowYears: 2000,
|
|
|
|
|
historySamplePaddingYears: 240
|
|
|
|
|
}),
|
|
|
|
|
technology: Object.freeze({
|
|
|
|
|
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,
|
2026-09-16 20:53:17 +09:00
|
|
|
leaderTenureMaxYears: 72,
|
|
|
|
|
foundingProtectionYears: 12,
|
|
|
|
|
warCapitalDistanceStart: 18,
|
|
|
|
|
warCapitalDistanceScale: 42,
|
|
|
|
|
warDisconnectedTerritoryFactor: 0.42,
|
|
|
|
|
institutionalAgingAnnualIncrease: 0.0015,
|
|
|
|
|
institutionalReformThreshold: 0.30,
|
|
|
|
|
institutionalReformBaseChance: 0.008,
|
|
|
|
|
institutionalReformChanceScale: 0.10,
|
|
|
|
|
institutionalReformMaxChance: 0.12,
|
|
|
|
|
institutionalReformCooldownYears: 50,
|
|
|
|
|
institutionalReformMinChange: -0.50,
|
|
|
|
|
institutionalReformMaxChange: 0.30,
|
|
|
|
|
treasuryCapPerPopulation: 0.30,
|
|
|
|
|
treasuryCityFoundingChance: 0.14,
|
|
|
|
|
treasuryCityFoundingCostRatio: 0.35,
|
|
|
|
|
treasuryCityPopulationPerCost: 0.18,
|
|
|
|
|
treasuryCityReservePerCost: 0.45,
|
|
|
|
|
treasuryCityFoundingSearchRadius: 20,
|
|
|
|
|
treasuryCitySiteScoreWindow: 6.0
|
|
|
|
|
}),
|
|
|
|
|
territory: Object.freeze({
|
|
|
|
|
baseCityReach: 9,
|
|
|
|
|
radiusScale: 0.5,
|
|
|
|
|
cityPowerWeight: 1.8,
|
|
|
|
|
capitalReachBonus: 1.5,
|
|
|
|
|
ownershipSwitchMargin: 1.5,
|
|
|
|
|
minimumInfluence: 2.4,
|
|
|
|
|
fullMaintenanceCityPopulation: 2400,
|
|
|
|
|
minimumPopulationSupport: 0.28,
|
|
|
|
|
maintenancePressure: 4,
|
|
|
|
|
supportSmoothing: 0.18,
|
|
|
|
|
smallStateSwitchPenalty: 2.5
|
|
|
|
|
}),
|
|
|
|
|
rebellion: Object.freeze({
|
|
|
|
|
participationLoyalty: 0.50,
|
|
|
|
|
automaticLoyalty: 0.30,
|
|
|
|
|
chanceScale: 0.20,
|
|
|
|
|
maxAnnualChance: 0.16,
|
|
|
|
|
conquestGraceYears: 20,
|
|
|
|
|
treasuryTransferRate: 0.65
|
2026-05-17 21:08:03 +09:00
|
|
|
}),
|
|
|
|
|
campaign: Object.freeze({
|
|
|
|
|
checkIntervalYears: 3,
|
|
|
|
|
baseChance: 0.18,
|
|
|
|
|
minCooldownYears: 12,
|
|
|
|
|
maxCooldownYears: 45,
|
|
|
|
|
maxActiveCampaigns: 12,
|
|
|
|
|
frontierTargetChance: 0.35,
|
|
|
|
|
nonStateTargetChance: 0.30,
|
|
|
|
|
independentCityTargetChance: 0.20,
|
2026-09-16 20:53:17 +09:00
|
|
|
polityTargetChance: 0,
|
|
|
|
|
independentCityPeacefulShare: 0.4,
|
2026-05-17 21:08:03 +09:00
|
|
|
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({
|
2026-09-16 20:53:17 +09:00
|
|
|
minimumInfluence: 0.18,
|
|
|
|
|
divergenceRateMultiplier: 0.25,
|
|
|
|
|
absorptionPopulationThreshold: 30,
|
|
|
|
|
urbanSpreadRadius: 5,
|
|
|
|
|
cityBlendRate: 0.045,
|
|
|
|
|
routeBlendRate: 0.035,
|
|
|
|
|
routeNeighborFactor: 0.35,
|
|
|
|
|
cityTradeBlendRate: 0.015
|
2026-05-14 20:15:23 +09:00
|
|
|
}),
|
|
|
|
|
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-09-16 20:53:17 +09:00
|
|
|
routeMaintainedBoost: 10,
|
|
|
|
|
manualProtectionYears: 25,
|
|
|
|
|
manualProtectedStrength: 32,
|
|
|
|
|
manualRouteMaxLength: 120,
|
|
|
|
|
desertTraversalCostFactor: 0.68,
|
|
|
|
|
desertRouteLengthBonus: 30,
|
|
|
|
|
desertAdaptiveMaxRouteLength: 96,
|
|
|
|
|
desertPheromoneThresholdFactor: 0.45,
|
|
|
|
|
desertConstructionCostFactor: 0.72
|
2026-05-14 20:15:23 +09:00
|
|
|
}),
|
2026-09-16 13:51:43 +09:00
|
|
|
war: Object.freeze({
|
2026-09-16 20:53:17 +09:00
|
|
|
damageMultiplier: 0.65,
|
|
|
|
|
tickWeeks: 4,
|
|
|
|
|
startChanceMultiplier: 3,
|
|
|
|
|
maxActiveWars: 12,
|
|
|
|
|
maxStartsPerTick: 4,
|
|
|
|
|
minDurationWeeks: 12,
|
|
|
|
|
idleEndWeeks: 24,
|
|
|
|
|
cooldownWeeks: 24,
|
|
|
|
|
decisiveScore: 6,
|
2026-09-16 13:51:43 +09:00
|
|
|
pressureMemoryDecay: 0.88,
|
2026-09-17 22:32:25 +09:00
|
|
|
captureChanceMultiplier: 0.50,
|
|
|
|
|
occupationPopulationLossRadius: 4,
|
|
|
|
|
occupationPopulationLossRate: 0.08,
|
2026-09-16 20:53:17 +09:00
|
|
|
cityDamageMinRate: 0.025,
|
|
|
|
|
cityDamageMaxRate: 0.30,
|
|
|
|
|
captureLossBase: 0.22,
|
|
|
|
|
captureLossMax: 0.40,
|
|
|
|
|
exhaustionPopulationLossBase: 0.003,
|
|
|
|
|
exhaustionPopulationLossMax: 0.03,
|
|
|
|
|
warZoneTileLossRate: 0.025,
|
2026-09-16 13:51:43 +09:00
|
|
|
majorSettlementThreshold: 1.25
|
|
|
|
|
}),
|
2026-05-14 20:15:23 +09:00
|
|
|
disaster: Object.freeze({
|
|
|
|
|
damageScale: 1.5,
|
|
|
|
|
majorLossRate: 0.05,
|
2026-09-17 22:32:25 +09:00
|
|
|
regionalPopulationLossRate: 0.42,
|
|
|
|
|
regionalPopulationLossMax: 0.65,
|
2026-05-14 20:15:23 +09:00
|
|
|
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;
|
|
|
|
|
}
|