85 lines
2.9 KiB
JavaScript
85 lines
2.9 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);
|
|
}
|
|
|
|
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);
|
|
}
|