// The building / improvement card. The city buildings tab and the HUD's tile
// improvement panel draw one card per type: an icon, a name and a status up
// top, then an action button carrying only the estimated price and the build
// time. What the work produces, consumes and costs lives in a hover tooltip, so
// the card itself stays bare. Shared by both panels so they read the same.
import { resourceById } from "../../../shared/data.js";
import { formatResourceAmount } from "../../../shared/resources.js";
import { groupDigits } from "../../../shared/text_format.js";
// The materials a construction bills in, in reading order.
export const CARD_RESOURCES = ["steel", "energy", "hightech"];
// A commodity's icon as an image node, for a row that names a resource with a
// picture rather than the word.
export function resourceIcon(id) {
const proto = resourceById(id);
const $img = $("");
if (proto) $img.attr("src", `assets/${proto.icon}`).attr("alt", proto.name);
return $img;
}
// A resource amount with its commodity icon. `sign` marks the direction of the
// flow: "+" for output, "−" for a cost.
function resourceLine(id, amount, sign) {
const proto = resourceById(id);
const $span = $("");
if (proto) {
$span.append($("
").attr("src", `assets/${proto.icon}`).attr("alt", proto.name));
}
$span.append(document.createTextNode(`${sign}${formatResourceAmount(id, amount)}`));
return $span;
}
// A research-point figure with its erlenmeyer icon. The icon stands in for the
// word "research", so no unit text follows: the science buildings, the
// technology cards and the research rate all read the same way.
export function researchNode(amount, { sign = "", decimals = 0 } = {}) {
const proto = resourceById("research");
const $span = $("");
if (proto) {
$span.append($("
").attr("src", `assets/${proto.icon}`).attr("alt", proto.name));
}
const value = Number(amount) || 0;
const text = decimals > 0 ? value.toFixed(decimals) : groupDigits(Math.round(value));
$span.append(document.createTextNode(`${sign}${text}`));
return $span;
}
// An inline resource bill for a card: a muted label, then one icon-and-amount
// per material, then an optional suffix. Used for a unit's daily upkeep, which
// is paid in materials rather than money.
export function resourceBill(items, { label = "", sign = "−", suffix = "" } = {}) {
const $bill = $("");
if (label) $bill.append($("").text(label));
for (const item of items) $bill.append(resourceLine(item.id, item.amount, sign));
if (suffix) $bill.append($("").text(suffix));
return $bill;
}
// The three tooltip sections from a work's flows. Each entry is a resource
// { id, amount } or free text { text }; free text is shown exactly as written.
// `desc` is an optional prose line shown first, with no section heading.
export function tooltipSections({
desc = "",
produce = [],
produceSuffix = "",
consume = [],
consumeSuffix = "",
cost = [],
} = {}) {
const sections = [
{ label: "Produces", cls: "tip-produce", sign: "+", suffix: produceSuffix, items: produce },
{ label: "Consumes", cls: "tip-consume", sign: "−", suffix: consumeSuffix, items: consume },
{ label: "Costs", cls: "tip-cost", sign: "−", suffix: "", items: cost },
];
if (desc) sections.unshift({ desc });
return sections;
}
let $tooltip = null;
// Removes any open card tooltip. Called on mouseleave and whenever the panel
// rebuilds, so a stale tooltip never lingers over fresh content.
export function hideCardTooltip() {
if ($tooltip) {
$tooltip.remove();
$tooltip = null;
}
}
// Opens the tooltip beside `$anchor`, flipping left or up when it would run off
// the viewport. It is fixed to the body, so a scrolling card list never clips
// it.
export function showCardTooltip($anchor, sections) {
hideCardTooltip();
if (!$anchor || !$anchor.length) return;
if (!sections || !sections.length) return;
$tooltip = $("