Files
Battle-for-Tismo/shared/rules.js
T
2026-09-17 22:35:42 +02:00

87 lines
3.0 KiB
JavaScript

// 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);
}
// Combined effect totals for a catalogue entry whose effects do not scale
// (governments and technologies are always applied at level 1).
function effectsTotal(proto) {
const totals = {};
for (const e of proto.effects) {
totals[e.stat] = (totals[e.stat] || 0) + effectValue(e, 1);
}
return totals;
}
export function governmentEffects(proto) {
return effectsTotal(proto);
}
export function technologyEffects(proto) {
return effectsTotal(proto);
}
// 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);
}