clone

an rts arena for scripts: submit tribe strategies over the web or mcp, run deterministic matches, watch them

main
tribes / packages / engine / src / constants.ts
constants.ts 27.4K
1/**2 * All tuning numbers for the v2 engine in one place. Every value here is an3 * integer; rates are per-mille (‰) unless stated otherwise. The README's4 * costs/rates tables are generated from the values in this file — keep them5 * in sync when tuning.6 */7import {8 BUILDINGS,9 PROFESSIONS,10 RESOURCES,11 TECHS,12 type Building,13 type Profession,14 type Resource,15 type Tech,16} from '@tribes/protocol';1718// ---------------------------------------------------------------------------19// Index maps (enum order = tie-break order everywhere)20// ---------------------------------------------------------------------------2122export const RES_IDX: Record<Resource, number> = Object.fromEntries(23 RESOURCES.map((r, i) => [r, i]),24) as Record<Resource, number>;25export const PROF_IDX: Record<Profession, number> = Object.fromEntries(26 PROFESSIONS.map((p, i) => [p, i]),27) as Record<Profession, number>;28export const BLD_IDX: Record<Building, number> = Object.fromEntries(29 BUILDINGS.map((b, i) => [b, i]),30) as Record<Building, number>;31export const TECH_IDX: Record<Tech, number> = Object.fromEntries(32 TECHS.map((t, i) => [t, i]),33) as Record<Tech, number>;3435export const R_N = RESOURCES.length; // 2036export const P_N = PROFESSIONS.length; // 1837export const B_N = BUILDINGS.length; // 253839// Frequently used resource indices.40export const RI = {41 grain: RES_IDX.grain,42 fish: RES_IDX.fish,43 meat: RES_IDX.meat,44 hides: RES_IDX.hides,45 wool: RES_IDX.wool,46 timber: RES_IDX.timber,47 stone: RES_IDX.stone,48 ore: RES_IDX.ore,49 coin: RES_IDX.coin,50 flour: RES_IDX.flour,51 planks: RES_IDX.planks,52 metal: RES_IDX.metal,53 leather: RES_IDX.leather,54 cloth: RES_IDX.cloth,55 bread: RES_IDX.bread,56 ale: RES_IDX.ale,57 tools: RES_IDX.tools,58 weapons: RES_IDX.weapons,59 armor: RES_IDX.armor,60 clothes: RES_IDX.clothes,61} as const;6263export const PI = {64 farmer: PROF_IDX.farmer,65 fisher: PROF_IDX.fisher,66 herder: PROF_IDX.herder,67 woodcutter: PROF_IDX.woodcutter,68 miner: PROF_IDX.miner,69 miller: PROF_IDX.miller,70 baker: PROF_IDX.baker,71 brewer: PROF_IDX.brewer,72 smelter: PROF_IDX.smelter,73 smith: PROF_IDX.smith,74 carpenter: PROF_IDX.carpenter,75 tanner: PROF_IDX.tanner,76 weaver: PROF_IDX.weaver,77 tailor: PROF_IDX.tailor,78 builder: PROF_IDX.builder,79 hauler: PROF_IDX.hauler,80 scholar: PROF_IDX.scholar,81 priest: PROF_IDX.priest,82} as const;8384export const BI = {85 hall: BLD_IDX.hall,86 house: BLD_IDX.house,87 granary: BLD_IDX.granary,88 warehouse: BLD_IDX.warehouse,89 farm: BLD_IDX.farm,90 dock: BLD_IDX.dock,91 pasture: BLD_IDX.pasture,92 lumber_camp: BLD_IDX.lumber_camp,93 mine: BLD_IDX.mine,94 mill: BLD_IDX.mill,95 bakery: BLD_IDX.bakery,96 brewery: BLD_IDX.brewery,97 smelter: BLD_IDX.smelter,98 smithy: BLD_IDX.smithy,99 tannery: BLD_IDX.tannery,100 weavers_hall: BLD_IDX.weavers_hall,101 tailor_shop: BLD_IDX.tailor_shop,102 market: BLD_IDX.market,103 shrine: BLD_IDX.shrine,104 church: BLD_IDX.church,105 library: BLD_IDX.library,106 academy: BLD_IDX.academy,107 barracks: BLD_IDX.barracks,108 walls: BLD_IDX.walls,109 tower: BLD_IDX.tower,110} as const;111112// ---------------------------------------------------------------------------113// Terrain114// ---------------------------------------------------------------------------115116/** Internal terrain codes; index into TERRAIN_CHARS gives the TerrainChar. */117export const TC = {118 PLAINS: 0,119 FOREST: 1,120 HILLS: 2,121 MOUNTAIN: 3,122 WATER: 4,123 RIVER: 5,124 SWAMP: 6,125 STEPPE: 7,126 ARCTIC: 8,127 VOLCANO: 9,128 ORE: 10,129 GOLD: 11,130} as const;131export const TERRAIN_CHARS = '.fhmwrstavOG';132133/** Ocean tiles are impassable; rivers are fordable (passable). */134export const IMPASSABLE = new Set<number>([TC.WATER]);135/** Hills-class terrain (mining, spawn fairness). Volcano is mountain-class. */136export const HILLS_CLASS = new Set<number>([TC.HILLS, TC.MOUNTAIN, TC.ORE, TC.GOLD, TC.VOLCANO]);137/** Cold terrain per spec: settlement on/adjacent t/a/m consumes clothes. */138export const COLD_CLASS = new Set<number>([TC.STEPPE, TC.ARCTIC, TC.MOUNTAIN]);139/** Fishing water: ocean and rivers. */140export const WATER_CLASS = new Set<number>([TC.WATER, TC.RIVER]);141142// ---------------------------------------------------------------------------143// Settlement tiers, housing, storage144// ---------------------------------------------------------------------------145146/** Pop thresholds: tier = highest i with pop >= TIER_POP[i]; never downgrades. */147export const TIER_POP = [0, 100, 400, 1000] as const;148/** Territory radius (Chebyshev) per tier. */149export const TIER_RADIUS = [4, 6, 9, 12] as const;150/**151 * Organic territory: settlements claim ground by multi-source cheapest-cost152 * expansion. Per-tile claim cost by terrain code (index = TC): plains 1,153 * forest 2, hills 2, mountain 5, water/river IMPASSABLE (0 = blocked), swamp154 * 3, steppe 1, arctic 2, volcano blocked, ore 2, gold 2. Water and rivers155 * hard-stop claims — natural borders and chokepoints are real.156 */157export const CLAIM_COST: ReadonlyArray<number> = [1, 2, 2, 5, 0, 0, 3, 1, 2, 0, 2, 2];158/** Max accumulated claim cost by settlement tier (expansion budget). */159export const TIER_CLAIM_BUDGET = [5, 8, 12, 16] as const;160/** Claim budget added per tribe age: borders keep moving after tier 3. */161export const AGE_CLAIM_BONUS = [0, 2, 3, 4] as const;162/** Everything beyond the base camp footprint (TIER_CLAIM_BUDGET[0]) is163 * scaled by the `expansion` decree (0-1000 permille, default 500): territory164 * is a strategy, not physics. Isolationists keep a hamlet's borders at any165 * size; expansionists claim the full tier + age + pop reach. */166/** Claim budget added by settlement pop: +1 per 25 of isqrt(pop), capped.167 * (pop 625 -> +1, 2500 -> +2, 5625 -> +3, 10000 -> +4, 15625 -> +5, 22500 -> +6) */168export const POP_CLAIM_DIV = 25;169export const POP_CLAIM_CAP = 6;170171// ---------------------------------------------------------------------------172// Ages (prestige ladder)173// ---------------------------------------------------------------------------174/** Hoard burned (drawn across your settlements, ascending id) to reach age175 * index i+1. Escalating and chain-deep: advancing PROVES the civilization. */176export const AGE_COSTS: ReadonlyArray<Partial<Record<import('@tribes/protocol').Resource, number>>> = [177 { grain: 800, timber: 400, hides: 100 }, // -> settled178 { bread: 400, planks: 300, metal: 150, cloth: 100, coin: 100 }, // -> crafting179 { bread: 800, metal: 300, armor: 60, ale: 300, coin: 400, clothes: 150 }, // -> high180];181/** Extraction & refinement rate bonus per age, ‰ added to every rate. */182export const AGE_RATE_BONUS = [0, 100, 200, 300] as const;183/** Warband power bonus per soldier per age. */184export const AGE_POWER_BONUS = 3;185/** Flat score term per age (the multiplier is gone: a total-score186 * multiplier made speculative advance() spam a free compounding lottery —187 * ages now pay through this flat term and the real rate bonuses). */188export const SCORE_PER_AGE = 150;189/** Score multiplier per age, ‰ added. RETIRED at 0 — see SCORE_PER_AGE. */190export const AGE_SCORE_MULT = 0;191192export const BASE_STORE_CAP = 2000;193/** Granary and warehouse each add to ONE shared per-resource cap (documented194 * simplification: each resource is individually capped at storeCap). */195export const GRANARY_CAP_BONUS = 2000;196export const WAREHOUSE_CAP_BONUS = 2000;197198export const HALL_HOUSING_BASE = 300;199export const HALL_HOUSING_PER_TIER = 300;200export const HOUSE_HOUSING = 60;201202// ---------------------------------------------------------------------------203// Labor: building slots & biome caps204// ---------------------------------------------------------------------------205206export const FARM_SLOTS = 40;207export const DOCK_SLOTS = 30;208export const PASTURE_HERDER_SLOTS = 10;209export const LUMBER_SLOTS = 30;210export const MINE_SLOTS = 30;211export const MILL_SLOTS = 20;212export const BAKERY_SLOTS = 20;213export const BREWERY_SLOTS = 15;214export const SMELTER_SLOTS = 20;215export const SMITHY_SLOTS = 20;216export const TANNERY_SLOTS = 15;217export const WEAVER_SLOTS = 15;218export const TAILOR_SLOTS = 15;219export const LIBRARY_SLOTS = 10;220export const ACADEMY_SLOTS = 25;221export const SHRINE_SLOTS = 10;222export const CHURCH_SLOTS = 25;223/** Carpenter slots come with lumber camps and the hall. */224export const CARPENTER_PER_LUMBER = 10;225export const CARPENTER_PER_HALL = 5;226/** Builders and haulers have no building requirement (capacity = uncapped). */227export const UNCAPPED_SLOTS = 100000;228229export const FARMERS_PER_PLAINS = 8;230export const WOODCUTTERS_PER_FOREST = 8;231export const MINERS_PER_HILL = 8;232export const FISHERS_PER_WATER = 10;233export const HERDS_PER_PASTURE = 20;234export const HORSES_PER_PASTURE = 10;235236// ---------------------------------------------------------------------------237// Extraction (per-mille output per worker-tick, at full tool coverage)238// ---------------------------------------------------------------------------239240// Calibration: demand is 2 satiation pts per capita per 10t. 40% food labor241// (480 of 1200) on grain alone covers it even without tools:242// 480 * 0.100 * 0.6 = 28.8 pts/tick vs 24 needed. Tools and the bread chain243// are the upside, not a survival requirement.244export const FARMER_RATE = 140; // grain (tuned up from 100: balance harness showed peaceful economies decaying to ~700 pop)245export const FISHER_RATE = 110; // fish (tuned with FARMER_RATE)246export const WOODCUTTER_RATE = 80; // timber247export const MINER_STONE_RATE = 60; // stone, always248export const MINER_ORE_RATE = 60; // ore, iff >=1 'O' vein in territory249/** Each additional claimed vein (up to +3) raises the per-miner ore rate —250 * prospecting for veins is a real mining strategy, not a checkbox. */251export const MINER_ORE_RATE_PER_EXTRA_VEIN = 30;252export const MINER_COIN_RATE = 15; // coin, iff >=1 'G' vein in territory253254/** Extractive professions work at this ‰ yield without tools. */255export const NO_TOOL_YIELD = 600;256/** Tools wear 1 per this many covered worker-ticks (fixed-point accumulator). */257export const TOOL_WEAR_TICKS = 200;258259/** One herder tends this many herds. */260export const HERDS_PER_HERDER = 5;261262// ---------------------------------------------------------------------------263// Refinement (per-mille output per worker-tick; recipes are per output unit)264// ---------------------------------------------------------------------------265266export const MILLER_RATE = 400; // grain -> flour, 1:1267export const BAKER_RATE = 300; // flour -> bread, 1:1268export const BREWER_RATE = 200; // 2 grain -> 1 ale269export const SMELTER_RATE = 150; // 2 ore + 1 timber -> 1 metal270export const SMITH_RATE = 100; // 1 metal + 1 timber -> 1 tool/weapon; +1 leather for armor271export const CARPENTER_RATE = 200; // 1 timber -> 1 plank272export const TANNER_RATE = 200; // 1 hide -> 1 leather273export const WEAVER_RATE = 150; // 2 wool -> 1 cloth274export const TAILOR_RATE = 100; // 1 cloth + 1 leather -> 1 clothes275276export const ALE_GRAIN_COST = 2;277export const METAL_ORE_COST = 2;278export const METAL_TIMBER_COST = 1;279export const CLOTH_WOOL_COST = 2;280281// ---------------------------------------------------------------------------282// Consumption & morale283// ---------------------------------------------------------------------------284285/**286 * Food demand: 2 satiation points per capita per SLOW_TICK_INTERVAL at287 * rations=1000 -> per-tick points = (pop * rations + acc) div 50_000.288 * "1 food per 10 ticks per capita" holds exactly for fish/meat (satiation 2).289 */290export const FOOD_DEMAND_DIV = 50000;291/** Satiation points per unit, eat order bread > fish > meat > grain. */292export const SATIATION: ReadonlyArray<readonly [number, number]> = [293 [RI.bread, 3],294 [RI.fish, 2],295 [RI.meat, 2],296 [RI.grain, 1],297];298/** Starvation deaths per tick capped at max(1, pop div 50). */299export const STARVATION_DEATH_CAP_DIV = 50;300export const STARVATION_MORALE_HIT = 30;301302/** Ale demand: 1 ale per capita per 10 ticks at ale_ration=1000. */303export const ALE_DEMAND_DIV = 10000;304/** Cold-biome clothes demand: (pop + acc) div 10_000 per tick. */305export const CLOTHES_DEMAND_DIV = 10000;306307/** Morale drifts 1/4 of the gap toward its target every slow tick. */308export const MORALE_BASE_TARGET = 500;309export const MORALE_VARIETY_BONUS = 100;310export const MORALE_ALE_BONUS = 100;311export const MORALE_CLOTHES_PENALTY = 150;312export const MORALE_STARVED_PENALTY = 300;313export const MORALE_BLESSING_BONUS = 150;314315// ---------------------------------------------------------------------------316// Population (slow tick)317// ---------------------------------------------------------------------------318319export const BIRTH_RATE = 8; // ‰ per 10t, x housing x morale x surplus factors320/** Frontier boom: extra birth rate (‰) scaled by FREE housing share. A young321 * camp with room to grow booms StarCraft-style; growth tapers to BIRTH_RATE322 * as housing fills. */323export const BIRTH_BOOM_MAX = 55;324/** Carpenters stop milling planks once this many sit unused in the325 * settlement — they don't eat every stick of construction timber. */326export const PLANKS_SOFT_CAP = 120;327/** No monster camps spawn before this tick: the StarCraft-style safety328 * window — ten souls get a fighting chance to establish before pressure. */329export const THREAT_GRACE_TICKS = 1200;330export const NATURAL_DEATH_RATE = 2; // ‰ per 10t331export const EMIGRATION_MORALE = 300; // below this, people leave332/** Hardship (starvation/emigration/natural death) never empties a settlement333 * below this floor — a remnant always hangs on and can recover. Only conquest334 * (capture) or disbandment removes a settlement outright. Prevents the335 * runaway starvation -> morale -> emigration extinction spiral. */336export const SETTLEMENT_POP_FLOOR = 40;337/** Monster raids pillage stores freely but only burn buildings while the338 * settlement is still substantial — they never raze a town to its hall.339 * (Tribal raids/sieges have no such mercy: conquest is the kill mechanism.) */340export const THREAT_ARSON_MIN_BUILDINGS = 6;341/** The hall's kitchen gardens: baseline farmer slots that survive any342 * pillaging, so a razed settlement can always feed a remnant and rebuild. */343export const HALL_GARDEN_SLOTS = 12;344/** Hall salvage yard: baseline woodcutter slots so wreckage can be rebuilt. */345export const HALL_YARD_SLOTS = 6;346/** The hall's herd yard: the founding flock survives without a pasture347 * (real herding — growth past this — still needs pastures and herders). */348export const HALL_HERD_SLOTS = 6;349/** Herder slots the hall provides for that small yard. */350export const HALL_HERDER_SLOTS = 2;351export const EMIGRATION_RATE = 10; // ‰ per 10t352/** Housing factor: 1000 below 80% occupancy, linear to 0 at 100%. */353export const HOUSING_SOFT_PERMILLE = 800;354/** Surplus factor saturates when stored food covers this many ticks. */355export const SURPLUS_TICKS = 50;356357// ---------------------------------------------------------------------------358// Herds (slow tick)359// ---------------------------------------------------------------------------360361export const HERD_GROWTH = 30; // ‰ per 10t of tended herds (x2 when fodder-fed)362export const HORSE_GROWTH = 20; // ‰ per 10t363export const WOOL_PER_HERD = 100; // ‰ per tended herd per 10t364export const MEAT_PER_SLAUGHTER = 3;365export const HIDES_PER_SLAUGHTER = 1;366export const FODDER_GRAIN_PER_HERD = 1; // grain per fed herd per 10t367368// ---------------------------------------------------------------------------369// Construction370// ---------------------------------------------------------------------------371372export interface BuildCost {373 timber?: number;374 planks?: number;375 stone?: number;376}377378export interface BuildingSpec {379 cost: BuildCost | null; // null = not buildable (hall)380 points: number;381 /** Tech index gate, or -1. */382 tech: number;383}384385export const BUILD_SPECS: Record<Building, BuildingSpec> = {386 hall: { cost: null, points: 0, tech: -1 },387 house: { cost: { timber: 20 }, points: 40, tech: -1 },388 granary: { cost: { timber: 40 }, points: 80, tech: -1 },389 warehouse: { cost: { timber: 40, stone: 20 }, points: 100, tech: -1 },390 farm: { cost: { timber: 30 }, points: 60, tech: -1 },391 dock: { cost: { timber: 40 }, points: 80, tech: -1 },392 pasture: { cost: { timber: 20 }, points: 50, tech: -1 },393 lumber_camp: { cost: { timber: 20 }, points: 50, tech: -1 },394 // timber-only: stone comes from miners, so the first mine must not need stone395 mine: { cost: { timber: 40 }, points: 100, tech: -1 },396 mill: { cost: { timber: 30, stone: 10 }, points: 100, tech: TECH_IDX.milling },397 bakery: { cost: { timber: 30, stone: 20 }, points: 100, tech: TECH_IDX.milling },398 brewery: { cost: { timber: 30, stone: 10 }, points: 100, tech: TECH_IDX.brewing },399 smelter: { cost: { timber: 20, stone: 40 }, points: 120, tech: TECH_IDX.smelting },400 smithy: { cost: { timber: 30, stone: 20 }, points: 120, tech: -1 },401 tannery: { cost: { timber: 20 }, points: 80, tech: -1 },402 weavers_hall: { cost: { timber: 30 }, points: 80, tech: -1 },403 tailor_shop: { cost: { timber: 20 }, points: 80, tech: -1 },404 market: { cost: { timber: 50, stone: 20 }, points: 120, tech: -1 },405 shrine: { cost: { timber: 20, stone: 20 }, points: 80, tech: -1 },406 church: { cost: { stone: 60, planks: 40 }, points: 200, tech: TECH_IDX.liturgy },407 library: { cost: { timber: 50, stone: 20 }, points: 120, tech: -1 },408 academy: { cost: { stone: 60, planks: 40 }, points: 200, tech: TECH_IDX.scholarship },409 barracks: { cost: { timber: 50, stone: 20 }, points: 120, tech: -1 },410 walls: { cost: { timber: 80 }, points: 150, tech: -1 }, // level 1; see WALL_LEVELS411 tower: { cost: { timber: 30, stone: 40 }, points: 120, tech: -1 },412};413414/** Wall upgrade path: cost/points per target level (index = level - 1). */415export const WALL_LEVELS: ReadonlyArray<{ cost: BuildCost; points: number; tech: number }> = [416 { cost: { timber: 80 }, points: 150, tech: -1 }, // L1 stockade417 { cost: { stone: 150 }, points: 250, tech: TECH_IDX.masonry }, // L2 stone418 { cost: { stone: 200, planks: 50 }, points: 400, tech: TECH_IDX.castellany }, // L3 castle419];420export const MAX_WALL_LEVEL = 3;421422/** One build point per builder-tick; one active site per 10 builders (min 1). */423export const BUILDERS_PER_SITE = 10;424export const BUILD_QUEUE_MAX = 12;425426// ---------------------------------------------------------------------------427// Tech & faith428// ---------------------------------------------------------------------------429430/** Knowledge/faith per worker per slow tick, ‰. */431export const SCHOLAR_RATE = 150;432export const PRIEST_RATE = 100;433434export const TECH_COSTS: Record<Tech, number> = {435 milling: 60,436 brewing: 60,437 smelting: 80,438 weaponsmithing: 150,439 plate_armor: 200,440 masonry: 80,441 castellany: 200,442 coinage: 100,443 crop_rotation: 100,444 horsemanship: 100,445 scholarship: 120,446 liturgy: 80,447 siegecraft: 250,448};449450/** Prereq DAG (tech -> prereq techs). */451export const TECH_PREREQS: Record<Tech, Tech[]> = {452 milling: [],453 brewing: [],454 smelting: [],455 weaponsmithing: ['smelting'],456 plate_armor: ['weaponsmithing'],457 masonry: [],458 castellany: ['masonry'],459 coinage: [],460 crop_rotation: [],461 horsemanship: [],462 scholarship: [],463 liturgy: [],464 siegecraft: ['weaponsmithing'],465};466467/** Escalation: effective cost = base * (4 + techsOwned) / 4. */468export const TECH_ESCALATION_DIV = 4;469470export const CROP_ROTATION_BONUS = 200; // ‰ extra farm yield471export const HARVEST_RITE_BONUS = 100; // ‰ extra farm yield while active472473export const RITE_COSTS = { blessing: 20, harvest: 25, warding: 40, war: 30 } as const;474export const RITE_DURATION = { blessing: 50, harvest: 50, warding: 100 } as const;475export const WAR_RITE_POWER_BONUS = 100; // ‰, one battle476477// ---------------------------------------------------------------------------478// Military479// ---------------------------------------------------------------------------480481export const MUSTER_MIN_SIZE = 10;482/** A settlement keeps at least this many civilians after muster/found. */483export const MIN_REMAINING_POP = 50;484export const SUPPLY_CAP_PER_SOLDIER = 3;485export const SUPPLY_PER_SOLDIER_10T = 1;486export const STARTING_WARBAND_MORALE = 800;487/** Fresh musters/reinforcements drill before campaigning: reduced power and488 * no raid/besiege until the counter runs out (see WarbandView.drilling). */489export const MUSTER_DRILL_TICKS = 250;490/** Power multiplier (per-mille) while a warband is still drilling. */491export const DRILLING_POWER_PERMILLE = 600;492/** Rams: siegecraft + these stores at the mustering settlement set siege=true. */493export const RAM_PLANKS = 20;494export const RAM_METAL = 5;495496/** power = size * (10 + armed(10) + armor*10/size + vet/100 + situational). */497export const POWER_BASE = 10;498export const POWER_ARMED = 10;499/** Defender terrain modifiers (per-soldier additive). */500export const TERRAIN_DEF_MOD: Readonly<Record<number, number>> = {501 [TC.FOREST]: 2,502 [TC.HILLS]: 3,503 [TC.ORE]: 3,504 [TC.GOLD]: 3,505 [TC.MOUNTAIN]: 4,506 [TC.VOLCANO]: 4,507 [TC.SWAMP]: -2,508};509export const WALL_DEF_PER_SOLDIER = 5; // x wallLevel, garrison defending a siege510511/** Battle variance: power * (950 + rng.int(101)) / 1000, attacker then defender. */512export const VARIANCE_BASE = 950;513export const VARIANCE_SPAN = 101;514515/** Casualty ‰: loser min(500, 150 + r/5); winner min(300, max(50, 350000/r));516 * r = winnerPower*1000/loserPower. */517export const LOSER_LOSS_BASE = 150;518export const LOSER_LOSS_CAP = 500;519export const WINNER_LOSS_CAP = 300;520export const WINNER_LOSS_MIN = 50;521522export const VET_WIN = 50;523export const VET_LOSS = 20;524export const ROUT_MORALE_HIT = 100;525526export const PATROL_RADIUS = 6;527/** Settlement passive defense: pop/10 militia + wallLevel*100 + towers*50. */528export const MILITIA_POP_DIV = 10;529/** Fortification combat power. Tuned up in v0.3.1: with the war economy530 * actually functioning, drilled+aged bands outscaled static defenses and531 * pure suppression dominated — a wall level must matter like ~25 drilled532 * soldiers, so defensive INVESTMENT is a real counter-build. */533export const WALL_DEFENSE = 250;534export const TOWER_DEFENSE = 120;535/** Raid carry capacity: units of loot per soldier. */536export const RAID_CARRY_PER_SOLDIER = 2;537/** A building is destroyed when attacker power >= 2x defense. */538export const RAID_DESTROY_RATIO = 2;539/** Siege: wall damage per slow tick = attacker power; a wall level falls at540 * wallLevel * WALL_HP damage. Without rams vs walls >= 2: quarter rate. */541export const WALL_HP = 2000;542export const NO_RAM_DIV = 4;543/** Desertion outside own territory with empty supply: 1‰ per 10t. */544export const DESERTION_RATE = 1;545export const NO_SUPPLY_MORALE_HIT = 20;546547// ---------------------------------------------------------------------------548// Threats549// ---------------------------------------------------------------------------550551export const THREAT_MIN_DIST = 10;552/** Camps only spawn within this distance of some settlement, which equals553 * THREAT_RAID_RANGE so every camp can actually raid. */554export const THREAT_MAX_DIST = 25;555export const THREAT_SPAWN_TRIES = 8;556/** maxCamps = min(6, 1 + tick div 1500). */557export const THREAT_MAX_CAMPS_CAP = 6;558export const THREAT_RAMP_TICKS = 1500;559export const THREAT_BASE_STRENGTH: readonly number[] = [20, 30, 50, 90]; // by kind idx560export const THREAT_STRENGTH_TICK_DIV = 250;561export const THREAT_WEALTH_DIV = 5000;562export const THREAT_WEALTH_RADIUS = 20;563export const THREAT_GROWTH_PER_10T = 1;564export const THREAT_STRENGTH_CAP_MULT = 4;565export const THREAT_RAID_RANGE = 25;566export const THREAT_RAID_COOLDOWN = 100;567export const THREAT_FIRST_RAID_DELAY = 100;568export const CAMP_POWER_MULT = 10;569export const THREAT_RAID_POWER_MULT = 8;570export const DEMON_GATE_MIN_TICK = 6000;571/** Loot for clearing a camp: coin = strength * 2 (credited to home settlement). */572export const CAMP_CLEAR_COIN_MULT = 2;573574// ---------------------------------------------------------------------------575// Logistics & trade576// ---------------------------------------------------------------------------577578/** Route/equalization throughput: units per hauler per slow tick. */579export const HAULER_THROUGHPUT = 20;580/** Routes only move surplus above this keep-level of the resource. */581export const ROUTE_KEEP_LEVEL = 100;582export const MIN_SETTLE_DIST = 6;583export const FOUND_MIN_SETTLERS = 50;584585/** Peddler: instant sale at ‰ of base value; coinage improves it. */586export const PEDDLER_RATE = 400;587export const PEDDLER_RATE_COINAGE = 600;588589/** Base coin value per unit (peddler & trade-volume accounting). */590export const RESOURCE_VALUE: Record<Resource, number> = {591 grain: 1,592 fish: 1,593 meat: 2,594 hides: 2,595 wool: 2,596 timber: 1,597 stone: 1,598 ore: 3,599 coin: 1,600 flour: 2,601 planks: 2,602 metal: 6,603 leather: 3,604 cloth: 4,605 bread: 2,606 ale: 3,607 tools: 8,608 weapons: 10,609 armor: 12,610 clothes: 8,611};612613// ---------------------------------------------------------------------------614// Vision & events615// ---------------------------------------------------------------------------616617/** Settlement vision = territoryRadius + this. */618export const SETTLEMENT_VISION_BONUS = 2;619export const UNIT_VISION = 6; // warbands & caravans620/** Glimpse band beyond vision: terrain CATEGORY only (TribeView.distant). */621export const DISTANT_VISION_BONUS = 5;622/** Terrain code -> DistantChar category ('.' open, 'f' vegetation, '^' highland, 'w' water). */623export const DISTANT_CHARS = '.f^^wwf..^^^';624export const SIGHT_COOLDOWN = 200;625/** A settlement's smoke column is visible far beyond terrain vision —626 * camp/village/town/city by tier. Civilizations cannot hide: tribe-level627 * awareness is near-inevitable as neighbors grow, while units stay fogged. */628export const SMOKE_RANGE = [12, 18, 26, 34] as const;629630// ---------------------------------------------------------------------------631// Score632// ---------------------------------------------------------------------------633634export const SCORE_POP_DIV = 10;635/** Settlements score by QUALITY: base + per-tier (a bare hall camp is 10,636 * a tier-3 city 40) — quantity alone no longer pays like quality. */637export const SCORE_SETTLEMENT_BASE = 10;638export const SCORE_SETTLEMENT_PER_TIER = 10;639export const SCORE_PER_TECH = 25;640export const SCORE_POWER_DIV = 20;641export const SCORE_COIN_DIV = 20;642/** Trade volume also earns (half the coin rate): a working market scores643 * even when the coin is spent again. */644export const SCORE_TRADE_DIV = 40;645export const SCORE_PER_BUILDING = 2;646/** Buildings score STANDING structures with a usefulness audit: houses only647 * while occupied; other types at full value up to this many per settlement,648 * half value beyond. Empty monuments to the formula stopped paying. */649export const SCORE_BUILDING_TYPE_CAP = 8;650/** Defense finally scores: wall levels and towers count as military. */651export const SCORE_WALL_LEVEL = 25;652export const SCORE_TOWER = 10;653654// ---------------------------------------------------------------------------655// Starting state (per tribe, single founding settlement)656// ---------------------------------------------------------------------------657658export const START_MORALE = 600;659export const START_HERDS = 4;660export const START_HORSES = 0;661662/** A founding CAMP, not a town: one hall (kitchen gardens + salvage yard663 * bootstrap food and timber). Everything else must be built — establishing664 * safety and an economy IS the early game. */665export const START_BUILDINGS: Partial<Record<Building, number>> = {666 hall: 1,667};668669/** Wagon-load supplies for ten souls: days of food, a little materiel, a few670 * spears. Armies, walls and workshops all come later — from the land. */671export const START_STORES: Partial<Record<Resource, number>> = {672 grain: 150,673 meat: 30,674 hides: 8,675 timber: 80,676 stone: 20,677 coin: 10,678 tools: 12,679 weapons: 4,680 clothes: 12,681};682683/** Default craft splits (tools/weapons/armor; mill/brew/fodder). */684export const DEFAULT_CRAFT: readonly number[] = [600, 250, 150, 500, 100, 200];685/** Default decrees (rations/slaughter/fodder/ale_ration). */686export const DEFAULT_DECREES: readonly number[] = [1000, 0, 0, 0, 500];