115 lines
4.8 KiB
JavaScript
115 lines
4.8 KiB
JavaScript
// Shared formatting helpers for the modal panels.
|
||
|
||
import { groupDigits } from "../../../shared/text_format.js";
|
||
import { policyById } from "../../../shared/data.js";
|
||
|
||
export function formatEffect(effect, level) {
|
||
const value = level <= 0 ? 0 : effect.baseValue * Math.pow(level, effect.exponent);
|
||
switch (effect.stat) {
|
||
case "gdp":
|
||
return `${value >= 0 ? "+" : ""}${(value * 100).toFixed(1)}% GDP/capita`;
|
||
case "budget_income":
|
||
return `${value >= 0 ? "+" : ""}${(value * 100).toFixed(1)}% tax income`;
|
||
case "research":
|
||
return `${value >= 0 ? "+" : ""}${value.toFixed(2)} research/h`;
|
||
case "culture":
|
||
return `${value >= 0 ? "+" : ""}${value.toFixed(2)} culture/h`;
|
||
case "infantry_attack":
|
||
return `${value >= 0 ? "+" : ""}${(value * 100).toFixed(1)}% infantry attack`;
|
||
case "infantry_defense":
|
||
return `${value >= 0 ? "+" : ""}${(value * 100).toFixed(1)}% infantry defense`;
|
||
case "artillery_attack":
|
||
return `${value >= 0 ? "+" : ""}${(value * 100).toFixed(1)}% artillery attack`;
|
||
case "construction_speed":
|
||
return `${value >= 0 ? "+" : ""}${(value * 100).toFixed(1)}% construction speed`;
|
||
case "rail_speed":
|
||
return `${value >= 0 ? "+" : ""}${(value * 100).toFixed(1)}% railway speed`;
|
||
default:
|
||
return `${effect.stat} ${value.toFixed(2)}`;
|
||
}
|
||
}
|
||
|
||
export function effectsText(effects, level) {
|
||
const parts = effects.map((e) => formatEffect(e, level));
|
||
return parts.length > 0 ? parts.join(" · ") : "No effect";
|
||
}
|
||
|
||
// The effect line for a building: its stat effects, or, for a utility building
|
||
// whose benefit is a rule, a short description of that rule at `level`.
|
||
export function buildingEffectText(proto, level) {
|
||
if (proto.effects && proto.effects.length > 0) return effectsText(proto.effects, level);
|
||
switch (proto.mechanic) {
|
||
case "parallel_training":
|
||
return `${1 + level} parallel training line${level === 0 ? "" : "s"}`;
|
||
case "parallel_building":
|
||
return `${1 + level} parallel building line${level === 0 ? "" : "s"}`;
|
||
case "civilian_shelter": {
|
||
const saved = Math.round((proto.civilianProtectionPerLevel || 0) * level * 100);
|
||
return `−${saved}% civilian bombing losses · very hard to destroy`;
|
||
}
|
||
case "air_defense":
|
||
return "Fires one volley at hostile aircraft over the city";
|
||
case "port_gdp": {
|
||
const per = (proto.gdpPerCapitaPerSeaTilePerLevel || 0) * level;
|
||
return `+${groupDigits(per)} GDP/capita per adjacent sea tile to the region`;
|
||
}
|
||
case "air_immigration":
|
||
return "Immigration from every other city with an airport";
|
||
default:
|
||
return "No effect";
|
||
}
|
||
}
|
||
|
||
export const SOURCE_LABELS = { building: "building", government: "government", technology: "research" };
|
||
|
||
export function signedMoney(value) {
|
||
return `${value < 0 ? "−" : "+"}€${groupDigits(Math.round(Math.abs(value)))}/h`;
|
||
}
|
||
|
||
export function percent(value) {
|
||
return `${value < 0 ? "−" : "+"}${(Math.abs(value) * 100).toFixed(1)}%`;
|
||
}
|
||
|
||
// Renders one news entry as text. Government actions name the specific actors
|
||
// doing them — "French troops", "French scientists" — rather than the nation as
|
||
// a whole, and the victim or target is named in the people's form ("the
|
||
// Vietnamese").
|
||
export function describeNews(entry, civs) {
|
||
const name = (index) => (civs[index] ? civs[index].name : `Nation ${index + 1}`);
|
||
// The attributive form of a nation, for putting before an actor noun. It
|
||
// falls back to the demonym, which doubles as an adjective for most of them;
|
||
// the few person-nouns (New Zealander, New Hampshirite) carry an explicit one.
|
||
const adjective = (index) => {
|
||
const civ = civs[index];
|
||
if (!civ) return `Nation ${index + 1}`;
|
||
return civ.adjective || civ.demonym || civ.name;
|
||
};
|
||
const people = (index) => {
|
||
const civ = civs[index];
|
||
return civ && civ.demonym ? `the ${civ.demonym}` : name(index);
|
||
};
|
||
switch (entry.type) {
|
||
case "war":
|
||
return `${adjective(entry.actor)} leaders have declared war on ${name(entry.target)}`;
|
||
case "capture":
|
||
return `${adjective(entry.to)} troops have captured ${entry.city} from ${people(entry.from)}`;
|
||
case "policy": {
|
||
const policy = policyById(entry.policy);
|
||
const policyName = policy ? policy.name : entry.policy;
|
||
const who =
|
||
civs[entry.ethnicity] && civs[entry.ethnicity].demonym
|
||
? `the ${civs[entry.ethnicity].demonym}`
|
||
: name(entry.ethnicity);
|
||
return `${adjective(entry.civ)} lawmakers have enacted ${policyName} for ${who}`;
|
||
}
|
||
case "technology":
|
||
return entry.repeatable
|
||
? `${adjective(entry.civ)} scientists have improved ${entry.name}`
|
||
: `${adjective(entry.civ)} scientists have discovered ${entry.name}`;
|
||
case "join":
|
||
return `${entry.player} took control of ${name(entry.civ)}`;
|
||
default:
|
||
return entry.type;
|
||
}
|
||
}
|