// Catalogue maths shared by the server (which charges costs) and the browser // (which displays and disables buttons) so the numbers always agree. import { effectValue, CITY_DEFENSE_BONUS } from "./data.js"; export function buildingBuildCost(proto, gdp, level) { return (proto.baseCost + proto.costGdpFraction * Math.max(gdp, 0)) * Math.pow(proto.costAlpha, Math.max(level, 0)); } export function buildingRefund(proto, gdp, level) { return buildingBuildCost(proto, gdp, level - 1) * proto.refundFraction; } export function buildingUpkeep(proto, gdp, level) { if (level <= 0) return 0; const base = proto.baseUpkeep + proto.upkeepGdpFraction * Math.max(gdp, 0); return base * Math.pow(proto.upkeepAlpha, level); } export function buildingEffectValue(proto, stat, level) { let total = 0; for (const e of proto.effects) { if (e.stat === stat) total += effectValue(e, level); } return total; } export function governmentChangeCost(proto, gdp) { return proto.baseCost + proto.costGdpFraction * Math.max(gdp, 0); } export function governmentEffects(proto) { const totals = {}; for (const e of proto.effects) { totals[e.stat] = (totals[e.stat] || 0) + effectValue(e, 1); } return totals; } export function technologyEffects(proto) { const totals = {}; for (const e of proto.effects) { totals[e.stat] = (totals[e.stat] || 0) + effectValue(e, 1); } return totals; } // True when every prerequisite technology is in the researched set. The set // holds catalogue indices, so each prerequisite id is first resolved against // the catalogue. export function prerequisitesMet(technology, researchedSet, catalogue) { for (const id of technology.prerequisites) { const index = catalogue ? catalogue.findIndex((t) => t.id === id) : -1; if (index < 0 || !researchedSet.has(index)) return false; } return true; } // Every defense modifier of the tile a unit stands on, in display order. The // tile carries its own terrain bonus and a city adds another; each entry is a // fraction of the unit's base defense. Shared by the server (which fights with // them) and the browser (which shows them) so the numbers always agree. export function defenseFactors(tile, hasCity) { const factors = []; if (tile && tile.defenseBonus) { factors.push({ label: tile.terrainType, bonus: tile.defenseBonus }); } if (hasCity) factors.push({ label: "City", bonus: CITY_DEFENSE_BONUS }); return factors; } // Multiplier applied to a unit's base defense for the tile it occupies. export function defenseMultiplier(tile, hasCity) { let total = 0; for (const factor of defenseFactors(tile, hasCity)) total += factor.bonus; return 1 + total; } // A unit's defense on a tile, before any role bonus (the attacker/defender // distinction is a combat rule, not part of the stat shown to the player). export function unitDefense(baseDefense, tile, hasCity) { return baseDefense * defenseMultiplier(tile, hasCity); }