const Terrain = Object.freeze({ PLAINS: 0, FOREST: 1, MOUNTAIN: 2, WATER: 3, DESERT: 4 }); const terrainInfo = [ { 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 } ]; const WEEKS_PER_MONTH = 4; const MONTHS_PER_YEAR = 12; const WEEKS_PER_YEAR = MONTHS_PER_YEAR * WEEKS_PER_MONTH; const SimConfig = Object.freeze({ world: Object.freeze({ initialPopulation: 50000, defaultSize: 160 }), population: Object.freeze({ populationScale: 10, carryingCapacityBase: 2.4, carryingCapacityFertility: 7.8, carryingCapacityMineral: 1.8, carryingCapacityFarmland: 5.5, territoryExitPopulationLossRate: 0.12, humanDataMinPopulation: 1.0 }), city: Object.freeze({ 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, }), 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, logisticsDistance: 34, charismaMin: 0.5, charismaMax: 1.5, charismaAverage: 1.0, leaderTenureMinYears: 22, 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 }), 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, independentCityPeacefulShare: 0.4, baseDurationYears: 8, maxDurationYears: 25, claimRadius: 8, maxTargetDistance: 48 }), 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 }), polityAccess: Object.freeze({ enabled: true, maxSearchDepth: 8, indirectPenalty: 0.012, perHopPenalty: 0.006, noAccessPenalty: 0.045, lowLoyaltyTransitPenalty: 0.012, foreignTransitPenalty: 0.008 }), culture: Object.freeze({ minimumInfluence: 0.18, divergenceRateMultiplier: 0.25, absorptionPopulationThreshold: 30, urbanSpreadRadius: 5, cityBlendRate: 0.045, routeBlendRate: 0.035, routeNeighborFactor: 0.35, cityTradeBlendRate: 0.015 }), route: Object.freeze({ maxRouteLength: 42, adaptiveMaxRouteLength: 72, isolatedCityBootstrap: true, isolatedRouteSearchLimit: 8, isolatedRouteRadius: 4, newRoutePheromoneSeed: 2.5, newRouteGraceBoost: 18, isolatedRouteMinStrength: 0.12, pheromoneDecay: 0.996, pheromoneDiffusion: 0.045, maxPheromone: 18, routePheromoneBuild: 0.18, routePheromoneMaintain: 0.12, routeUpkeepPerTile: 0.018, routeWeakThreshold: 8, routeUnsupportedDecay: 3, routeMaintainedBoost: 10, manualProtectionYears: 25, manualProtectedStrength: 32, manualRouteMaxLength: 120, desertTraversalCostFactor: 0.68, desertRouteLengthBonus: 30, desertAdaptiveMaxRouteLength: 96, desertPheromoneThresholdFactor: 0.45, desertConstructionCostFactor: 0.72 }), war: Object.freeze({ damageMultiplier: 0.65, tickWeeks: 4, startChanceMultiplier: 3, maxActiveWars: 12, maxStartsPerTick: 4, minDurationWeeks: 12, idleEndWeeks: 24, cooldownWeeks: 24, decisiveScore: 6, pressureMemoryDecay: 0.88, captureChanceMultiplier: 0.50, occupationPopulationLossRadius: 4, occupationPopulationLossRate: 0.08, cityDamageMinRate: 0.025, cityDamageMaxRate: 0.30, captureLossBase: 0.22, captureLossMax: 0.40, exhaustionPopulationLossBase: 0.003, exhaustionPopulationLossMax: 0.03, warZoneTileLossRate: 0.025, majorSettlementThreshold: 1.25 }), disaster: Object.freeze({ damageScale: 1.5, majorLossRate: 0.05, regionalPopulationLossRate: 0.42, regionalPopulationLossMax: 0.65, 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, minPopulation: 60, maxPopulation: 140, edgeBand: 4 }) }); function years(value) { return value * WEEKS_PER_YEAR; }