diff --git a/AGENTS.md b/AGENTS.md index c1d0c45..d6f81f2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -78,6 +78,18 @@ There is no linter, formatter, or CI. Do not invent commands beyond these. fragmented and large frames (`server/websocket.js`). Do not lower the buffer limits without checking the snapshot path. - Indent `.js` files with 2 spaces. `.editorconfig` only enforces UTF-8. +- The simulation ticks hourly, but the player reads rates per day: money income + and upkeep, research and culture effects, campaign spending and the opinion + drift are all scaled by `HOURS_PER_DAY` where they are shown + (`client/js/modals/format.js` `signedMoney`/`effectNodes`, `budget.js`, + `nation.js`, and the approval tooltip in `game_screen/panels.js`). Write new + rate copy per day, and leave durations (build time, cooldowns, endurance) in + hours. +- `description` fields under `shared/data/` are player-facing and must match the + simulation: they surface in tooltips — city building cards and resource + producers (`client/js/ui/card.js` `tooltipSections`), spy operations and + minority policies (`game_screen/panels.js`, `modals/nation.js`) — so correct + them when the behaviour they describe changes. - Keep aircraft out of the melee: `_battleTiles`/`_combatTarget` skip them and their strikes are routed explicitly. Mirror the air rules (airport-only rebasing, strike radius, ferry fuel) on both client and server, or prediction diff --git a/client/css/style.css b/client/css/style.css index f83f529..0de05c7 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -1106,6 +1106,7 @@ input:focus, select:focus { border-color: var(--accent); } .card-tooltip .tip-consume, .card-tooltip .tip-cost { color: #e2c08a; } .card-tooltip .tip-none { color: var(--muted); } +.card-tooltip .tip-desc { color: var(--text); line-height: 1.35; } .card-tooltip .res { display: inline; white-space: nowrap; } .card-tooltip .res img { width: 13px; height: 13px; vertical-align: -2px; margin-right: 3px; } @@ -2107,6 +2108,7 @@ input:focus, select:focus { border-color: var(--accent); } } .politics-estimate { margin-top: 8px; font-size: 13px; } .politics-cost { color: #f0b060; } +.politics-desc { color: var(--muted); font-size: 12px; } .politics-wizard .politics-controls { margin-top: 0; } .ethnic-trend { font-weight: 700; margin-left: 2px; } .trend-up { color: #6ee787; } diff --git a/client/js/devlog_data.js b/client/js/devlog_data.js index 166b95d..af45862 100644 --- a/client/js/devlog_data.js +++ b/client/js/devlog_data.js @@ -1,6 +1,12 @@ // Generated by scripts/generate-devlog.js from `git log`; do not edit. // The pre-commit hook refreshes it so the main menu shows the latest commits. export const DEVLOG = [ + { + "hash": "26e5141", + "date": "2026-09-25", + "subject": "Split the resource tooltip into aligned production and consumption lines", + "body": "" + }, { "hash": "0a1c2d0", "date": "2026-09-25", @@ -54,11 +60,5 @@ export const DEVLOG = [ "date": "2026-09-24", "subject": "Gave the world spies, treaties and nuclear weapons", "body": "" - }, - { - "hash": "54efd66", - "date": "2026-09-24", - "subject": "Grouped the unit stack by type and let ranged units pick a target", - "body": "" } ]; diff --git a/client/js/game_screen.js b/client/js/game_screen.js index 6109551..e520ced 100644 --- a/client/js/game_screen.js +++ b/client/js/game_screen.js @@ -448,7 +448,7 @@ export class GameScreen { !this._isFriendlyAirport(coords)) { this.confirmModal.show( "No runway", - "Aircraft may only fly to one of your cities with an airport, or attack an enemy unit.", + "Aircraft may only fly to one of your cities with an airport, or strike a hostile unit or city.", null ); return; diff --git a/client/js/game_screen/panels.js b/client/js/game_screen/panels.js index b8dddcc..b2b0fe2 100644 --- a/client/js/game_screen/panels.js +++ b/client/js/game_screen/panels.js @@ -25,6 +25,7 @@ import { ECONOMY, EFFECT_PRODUCTION, APPROVAL, + POPULARITY, effectValue, pillageSteps, ROADS, @@ -47,7 +48,7 @@ import { import { defenseFactors, unitDefense, productionFactors, productionMultiplier, popularityDisplay } from "../../../shared/rules.js"; import { formatResourceAmount, foodNeedPerDay, protoUpkeep, tileImprovementResourceCost } from "../../../shared/resources.js"; import { dateString, hoursInNextMonth } from "../../../shared/game_clock.js"; -import { DEFAULT_UNIT_SPEED } from "../../../shared/game_state/constants.js"; +import { DEFAULT_UNIT_SPEED, HOURS_PER_DAY } from "../../../shared/game_state/constants.js"; import { flightDistanceTiles } from "../../../shared/hex.js"; import { ethnicityChart } from "../ui/ethnicity.js"; import { isEconomicMode } from "../map_view/economic.js"; @@ -518,19 +519,13 @@ export const panelMethods = { let hint = "Not yet available"; if (operation.id === SPY_SABOTAGE) { enabled = this._canSabotage(units); - hint = enabled - ? "Cripple this tile's production for a week (spends the spies)" - : "Move the spy onto foreign land"; + hint = enabled ? operation.description : "Move the spy onto foreign land"; } else if (operation.id === SPY_CITY) { enabled = this._spyActionTarget(SPY_CITY, units); - hint = enabled - ? "Read a neighbouring foreign city (spends the spies)" - : "Move the spy beside a foreign city"; + hint = enabled ? operation.description : "Move the spy beside a foreign city"; } else if (operation.id === SPY_COMMS) { enabled = this._spyActionTarget(SPY_COMMS, units); - hint = enabled - ? "Intercept a neighbouring capital's messages (spends the spies)" - : "Move the spy beside a foreign capital"; + hint = enabled ? operation.description : "Move the spy beside a foreign capital"; } const active = armed === operation.id; $button @@ -1001,15 +996,17 @@ export const panelMethods = { const popularityPoints = stats.popularity || 0; const popularity = popularityDisplay(popularityPoints); this._updateApprovalStat(popularity); - // The people drift one point an hour back to neutral, so the estimate is - // simply how many points away they are; zero is already there. + // The people drift `POPULARITY.driftPerHour` points back to neutral every + // hour, so the estimate is how many points away they are spread over that + // daily rate; zero is already there. + const driftPerDay = POPULARITY.driftPerHour * HOURS_PER_DAY; const pointsAway = Math.abs(popularityPoints); this._setStatTooltip( "stat-approval", pointsAway <= 0 ? "Popularity is exactly neutral" - : `Neutrality in about ${pointsAway} h (~${Math.ceil(pointsAway / 24)} d) ` + - "at the current drift of one point an hour" + : `Neutrality in about ${Math.ceil(pointsAway / driftPerDay)} d ` + + `at the current drift of ${driftPerDay} points a day` ); // Popularity doubles its arrow past ten percentage points. this._updateStatArrow( @@ -1677,7 +1674,7 @@ export const panelMethods = { $el.append( $("
").text( "The market is above the budgeted prices. " + - "The budget rises 1% a day, doubling each day, until the materials can be bought." + "The budget is raised to meet them, then the materials are bought." ) ); } else { @@ -1829,7 +1826,11 @@ export const panelMethods = { const materials = tileImprovementResourceCost(improvement); const bill = this._resourceItems(materials); const price = quotedBuildCost(improvement.buildCost, materials, prices); + // A resource producer's reference text sits above its output; the military + // works carry theirs as the produce line. + const desc = improvement.resource ? improvement.description || "" : ""; return tooltipSections({ + desc, produce, produceSuffix: improvement.resource ? "/day" : "", consume: this._improvementUpkeepItems(improvement), diff --git a/client/js/modals/budget.js b/client/js/modals/budget.js index a3d7792..f82bbbb 100644 --- a/client/js/modals/budget.js +++ b/client/js/modals/budget.js @@ -1,5 +1,5 @@ // The budget table shared by the nation and city panels. Every row is an item -// and its hourly amount, income in green and every cost in red, with the +// and its daily amount, income in green and every cost in red, with the // projected total on the last row. Building types are rows that expand to their // per-city cost. `breakdown` mirrors the shape both the national and the // regional economy use, so one renderer serves them both. @@ -88,7 +88,8 @@ export function renderBudget($list, breakdown = {}, { expanded = new Set(), onTo } // What the nation spent buying each resource, and earned selling it to other - // countries, over the last day, shown as an hourly average. + // countries, over the last day, shown as a daily figure. The record holds the + // day's total, so it is divided back to an hourly rate for `signedMoney`. if (resourceMoney.length > 0) { $body.append(sectionRow("Resource trade")); for (const entry of resourceMoney) { @@ -103,7 +104,7 @@ export function renderBudget($list, breakdown = {}, { expanded = new Set(), onTo } } - // The real resource bills of the last day, as hourly averages, in their own + // The real resource bills of the last day, as daily figures, in their own // section so they are not confused with the forecast upkeep rows above. const resourceCosts = breakdown.resourceCosts || []; if (resourceCosts.length > 0) { diff --git a/client/js/modals/city.js b/client/js/modals/city.js index 0b4c786..65c299f 100644 --- a/client/js/modals/city.js +++ b/client/js/modals/city.js @@ -138,7 +138,7 @@ export class CityModal { ); for (const row of this.buildingRows) this.$buildingGrid.append(row.card.$card); this.refreshBuildings(view.budget || 0, view.levels || {}, this.gdp, {}); - this.$budgetSub.text(`Region net +${currencySymbol()}0/h`); + this.$budgetSub.text(`Region net +${currencySymbol()}0/day`); this.renderBudget(null); this.renderPeople( view.ethnicMakeup || [], @@ -804,6 +804,7 @@ export class CityModal { const bill = this._resourceItems(materials); const price = this._quotedCost(cost, row.proto, row.level + row.pending); return tooltipSections({ + desc: row.proto.description || "", produce: effect && effect !== "No effect" ? [{ node: buildingEffectNodes(row.proto, shownLevel) }] : [], consume: this._resourceItems(upkeep), consumeSuffix: "/day", diff --git a/client/js/modals/format.js b/client/js/modals/format.js index 908acb2..080e166 100644 --- a/client/js/modals/format.js +++ b/client/js/modals/format.js @@ -1,6 +1,7 @@ // Shared formatting helpers for the modal panels. import { groupDigits } from "../../../shared/text_format.js"; +import { HOURS_PER_DAY } from "../../../shared/game_clock.js"; import { policyById, resourceById, treatyById } from "../../../shared/data.js"; import { marketPriceIndex, resourceMarketValue } from "../../../shared/resources.js"; import { formatMoneySigned, currencySymbol } from "../currency.js"; @@ -34,9 +35,9 @@ export function formatEffect(effect, level) { case "production": return `${value >= 0 ? "+" : ""}${(value * 100).toFixed(1)}% production`; case "research": - return `${value >= 0 ? "+" : ""}${value.toFixed(2)} research/h`; + return `${value >= 0 ? "+" : ""}${(value * HOURS_PER_DAY).toFixed(2)} research/day`; case "culture": - return `${value >= 0 ? "+" : ""}${value.toFixed(2)} culture/h`; + return `${value >= 0 ? "+" : ""}${(value * HOURS_PER_DAY).toFixed(2)} culture/day`; case "infantry_attack": return `${value >= 0 ? "+" : ""}${(value * 100).toFixed(1)}% infantry attack`; case "infantry_defense": @@ -81,7 +82,7 @@ export function buildingEffectText(proto, level) { return `−${saved}% civilian bombing losses · very hard to destroy`; } case "air_defense": - return "Fires one volley at hostile aircraft over the city"; + return "Fires on hostile aircraft and missiles within two tiles"; case "port_production": { const per = (proto.productionPerSeaTilePerLevel || 0) * level; const food = (proto.foodPerSeaTilePerLevel || 0) * level * 100; @@ -104,8 +105,8 @@ export function effectNodes(effects, level) { if (index > 0) $frag.append(document.createTextNode(" · ")); if (effect.stat === "research") { const value = level <= 0 ? 0 : effect.baseValue * Math.pow(level, effect.exponent); - $frag.append(researchNode(value, { sign: value >= 0 ? "+" : "", decimals: 2 })); - $frag.append(document.createTextNode("/h")); + $frag.append(researchNode(value * HOURS_PER_DAY, { sign: value >= 0 ? "+" : "", decimals: 2 })); + $frag.append(document.createTextNode("/day")); } else { $frag.append(document.createTextNode(formatEffect(effect, level))); } @@ -122,8 +123,9 @@ export function buildingEffectNodes(proto, level) { export const SOURCE_LABELS = { building: "building", government: "government", technology: "research" }; +// A signed money rate, given per hour by the model and shown per day. export function signedMoney(value) { - return `${formatMoneySigned(value)}/h`; + return `${formatMoneySigned(value * HOURS_PER_DAY)}/day`; } // A signed money amount with no period suffix, for the monthly budget report. diff --git a/client/js/modals/nation.js b/client/js/modals/nation.js index 4c0ecd8..71615c6 100644 --- a/client/js/modals/nation.js +++ b/client/js/modals/nation.js @@ -1600,8 +1600,8 @@ export class NationModal { this.$technologySub .empty() .append(document.createTextNode("Research: ")) - .append(researchNode(researchRate || 0, { sign: "+", decimals: 1 })) - .append(document.createTextNode("/h")); + .append(researchNode((researchRate || 0) * HOURS_PER_DAY, { sign: "+", decimals: 1 })) + .append(document.createTextNode("/day")); const fin = finances || {}; this._treasury = budget; @@ -2482,9 +2482,9 @@ export class NationModal { const hourly = campaign.hourlyCost || 0; const activeHours = Math.max(0, (info.totalHours || 0) - (campaign.startedHours || 0)); const spent = hourly * activeHours; - const days = Math.floor(activeHours / 24); + const days = Math.floor(activeHours / HOURS_PER_DAY); return ( - `${currencySymbol()}${groupDigits(this._quoted(hourly))}/h · ${currencySymbol()}${groupDigits(Math.round(spent))} spent over ` + + `${currencySymbol()}${groupDigits(Math.round(this._quoted(hourly) * HOURS_PER_DAY))}/day · ${currencySymbol()}${groupDigits(Math.round(spent))} spent over ` + `${days} day${days === 1 ? "" : "s"}` ); } @@ -2512,10 +2512,12 @@ export class NationModal { value: campaign.hourlyCost, }); const $spend = $("").text( - `${currencySymbol()}${groupDigits(this._quoted(campaign.hourlyCost))}/h` + `${currencySymbol()}${groupDigits(Math.round(this._quoted(campaign.hourlyCost) * HOURS_PER_DAY))}/day` ); $slider.on("input", () => - $spend.text(`${currencySymbol()}${groupDigits(this._quoted(Number($slider.val())))}/h`) + $spend.text( + `${currencySymbol()}${groupDigits(Math.round(this._quoted(Number($slider.val())) * HOURS_PER_DAY))}/day` + ) ); $slider.on("change", () => { const amount = Number($slider.val()); @@ -2523,7 +2525,9 @@ export class NationModal { // Reverting to the last known spend keeps the control honest until // the player confirms the stop. $slider.val(campaign.hourlyCost); - $spend.text(`${currencySymbol()}${groupDigits(this._quoted(campaign.hourlyCost))}/h`); + $spend.text( + `${currencySymbol()}${groupDigits(Math.round(this._quoted(campaign.hourlyCost) * HOURS_PER_DAY))}/day` + ); this.onConfirm( "Stop this campaign?", `Cutting the spending to ${currencySymbol()}0 will stop the campaign.`, @@ -2563,7 +2567,7 @@ export class NationModal { ); $card.append( $("").text( - `Costs ${currencySymbol()}${groupDigits(this._quoted(PROPAGANDA.defaultHourlyCost))}/h to begin; ` + + `Costs ${currencySymbol()}${groupDigits(Math.round(this._quoted(PROPAGANDA.defaultHourlyCost) * HOURS_PER_DAY))}/day to begin; ` + "adjust the spending on an active campaign to make it land harder or softer." ) ); @@ -2677,6 +2681,7 @@ export class NationModal { const $eth = this._civSelect(civs, policy.ethnicity); this.$policyType = $type; this.$policyEth = $eth; + this.$policyDesc = $(""); this.$policyTarget = $(""); this.$policyCost = $(""); this.$policyDuplicate = $(""); @@ -2701,6 +2706,7 @@ export class NationModal { $controls.append($("").append(this.$policyEnact)); this.$politicsWizard.append( $controls, + this.$policyDesc, this.$policyTarget, this.$policyCost, this.$policyDuplicate @@ -2719,6 +2725,7 @@ export class NationModal { const type = String(this.$policyType.val()); const ethnicity = Number(this.$policyEth.val()); const proto = policyById(type); + this.$policyDesc.text(proto ? proto.description : ""); this.$policyTarget.text(proto ? `Target: ${proto.targetEffect}` : ""); this.$policyCost.text(proto ? `Political cost: ${proto.politicalCost}` : ""); const duplicate = active.some( diff --git a/client/js/ui/card.js b/client/js/ui/card.js index c6ca646..22328c0 100644 --- a/client/js/ui/card.js +++ b/client/js/ui/card.js @@ -60,18 +60,22 @@ export function resourceBill(items, { label = "", sign = "−", suffix = "" } = // 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 = [], } = {}) { - return [ + 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; @@ -93,6 +97,10 @@ export function showCardTooltip($anchor, sections) { if (!$anchor || !$anchor.length) return; $tooltip = $(""); for (const section of sections) { + if (section.desc) { + $tooltip.append($("").text(section.desc)); + continue; + } const $section = $(""); $section.append($("").text(section.label)); if (!section.items || section.items.length === 0) { diff --git a/shared/data/buildings.js b/shared/data/buildings.js index f02e0ad..5f4c9b6 100644 --- a/shared/data/buildings.js +++ b/shared/data/buildings.js @@ -13,7 +13,7 @@ export const BUILDING_MECHANIC = { PARALLEL_BUILDING: "parallel_building", // Cuts civilian deaths from bombing, and is very hard to knock down. CIVILIAN_SHELTER: "civilian_shelter", - // Fires one volley at hostile aircraft passing over the city. + // Fires on hostile aircraft and missiles that enter a two-tile corridor. AIR_DEFENSE: "air_defense", // Flat production per head for the city's region, per adjacent sea tile. PORT_PRODUCTION: "port_production", @@ -40,7 +40,7 @@ export const BUILDINGS = [ { id: "shelters", name: "Wartime shelters", - description: "Each level reduces civilian life loss from bombing by 10%. Extremely hard to destroy. Does not protect other buildings.", + description: "Each level shelters another 10% of the city's people from bombing, up to all of them. Extremely hard to destroy. Does not protect other buildings.", baseCost: 100_000_000, costAlpha: 2, refundFraction: 0.5, @@ -58,7 +58,7 @@ export const BUILDINGS = [ id: "aa_building", advanced: true, name: "Air defense", - description: "Damages hostile aircraft that fly above this city.", + description: "Fires on hostile aircraft and missiles that come within two tiles of this city.", baseCost: 1_000_000_000, costAlpha: 2, refundFraction: 0.5, diff --git a/shared/data/governments.js b/shared/data/governments.js index 840cc2d..145fd68 100644 --- a/shared/data/governments.js +++ b/shared/data/governments.js @@ -7,7 +7,7 @@ export const GOVERNMENTS = [ { id: "republic", name: "Republic", - description: "Rule by elected representatives. A balanced government that spreads its support across the economy, trade and the sciences.", + description: "Rule by elected representatives. A balanced government that spreads its support across the economy and the sciences.", baseCost: 0, costGdpFraction: 0.02, effects: [effect(EFFECT_PRODUCTION, 0.05, 1.0), effect(EFFECT_RESEARCH, 1.0, 1.0)], diff --git a/shared/data/improvements.js b/shared/data/improvements.js index bef93da..bc5ebdc 100644 --- a/shared/data/improvements.js +++ b/shared/data/improvements.js @@ -77,7 +77,7 @@ export const TILE_IMPROVEMENTS = [ advanced: true, name: "Coastal cannon", description: - "Automatically fires on hostile warships within two tiles. Only a coastal tile can carry one.", + "Automatically fires on hostile naval units within two tiles. Only a coastal tile can carry one.", buildCost: 120_000_000, maintenance: 2000, range: 2, diff --git a/shared/data/resources.js b/shared/data/resources.js index 65fa94e..04d3919 100644 --- a/shared/data/resources.js +++ b/shared/data/resources.js @@ -311,7 +311,7 @@ export const RESOURCE_BUILDINGS = [ id: "coal_power_plant", name: "Coal power plant", description: - "Burns coal to make 250 GWh of electricity a day. Cheap to raise, but the fuel bill never stops.", + "Burns coal to make 2,500 GWh of electricity a day. Cheap to raise, but the fuel bill never stops.", resource: "energy", outputPerDay: 250 * KWH_PER_GWH, fuelEnergyPerDay: 250 * KWH_PER_GWH * 0.25, @@ -324,7 +324,7 @@ export const RESOURCE_BUILDINGS = [ id: "natural_gas_power_plant", name: "Natural gas power plant", description: - "The cleanest fossil plant: 300 GWh a day for a higher fuel bill.", + "The cleanest fossil plant: 3,000 GWh a day for a higher fuel bill.", resource: "energy", outputPerDay: 300 * KWH_PER_GWH, fuelEnergyPerDay: 300 * KWH_PER_GWH * 0.25, @@ -336,7 +336,7 @@ export const RESOURCE_BUILDINGS = [ { id: "oil_power_plant", name: "Oil power plant", - description: "Burns oil for 270 GWh of electricity a day.", + description: "Burns oil for 2,700 GWh of electricity a day.", resource: "energy", outputPerDay: 270 * KWH_PER_GWH, fuelEnergyPerDay: 270 * KWH_PER_GWH * 0.25, @@ -349,7 +349,7 @@ export const RESOURCE_BUILDINGS = [ id: "solar_power_plant", name: "Solar power plant", description: - "A field of panels producing a steady 30 GWh a day with no fuel and no upkeep.", + "A field of panels producing a steady 300 GWh a day with no fuel and no upkeep.", resource: "energy", outputPerDay: 30 * KWH_PER_GWH, energyPerDay: 0, @@ -361,7 +361,7 @@ export const RESOURCE_BUILDINGS = [ { id: "geothermal_plant", name: "Geothermal plant", - description: "Taps the earth's heat for 60 GWh a day of free electricity.", + description: "Taps the earth's heat for 600 GWh a day of free electricity.", resource: "energy", outputPerDay: 60 * KWH_PER_GWH, energyPerDay: 0, @@ -373,7 +373,7 @@ export const RESOURCE_BUILDINGS = [ { id: "wind_turbines", name: "Wind turbines", - description: "A hilltop wind farm making 25 GWh a day for free.", + description: "A hilltop wind farm making 250 GWh a day for free.", resource: "energy", outputPerDay: 25 * KWH_PER_GWH, energyPerDay: 0, @@ -385,7 +385,7 @@ export const RESOURCE_BUILDINGS = [ { id: "offshore_wind_turbines", name: "Offshore wind turbines", - description: "Turbines out at sea making 50 GWh a day. Needs a coastal tile.", + description: "Turbines out at sea making 500 GWh a day. Needs a coastal tile.", resource: "energy", outputPerDay: 50 * KWH_PER_GWH, energyPerDay: 0, @@ -399,7 +399,7 @@ export const RESOURCE_BUILDINGS = [ id: "fusion_power_plant", advanced: true, name: "Fusion power plant", - description: "A late-game reactor producing 4 TWh of electricity a day.", + description: "A late-game reactor producing 40 TWh of electricity a day.", resource: "energy", outputPerDay: 4_000 * KWH_PER_GWH, energyPerDay: 0, @@ -417,7 +417,7 @@ export const RESOURCE_BUILDINGS = [ id: "steel_mill", name: "Steel mill", description: - "Smelts iron into 20,000 t of steel a day, spending 20 MWh of energy per tonne.", + "Smelts iron into 200,000 t of steel a day, spending 20 MWh of energy per tonne.", resource: "steel", outputPerDay: 20_000, energyPerDay: 2_000 * RESOURCE_RULES.energyPerSteelTonne, @@ -428,7 +428,7 @@ export const RESOURCE_BUILDINGS = [ { id: "gold_mine", name: "Gold mine", - description: "Produces 20,000 carats of luxury a day, at 10 MWh a carat.", + description: "Produces 200,000 carats of luxury a day, at 10 MWh a carat.", resource: "luxury", outputPerDay: 20_000, energyPerDay: 20_000 * RESOURCE_RULES.energyPerLuxuryCarat.gold_mine, @@ -439,7 +439,7 @@ export const RESOURCE_BUILDINGS = [ { id: "diamond_mine", name: "Diamond mine", - description: "Produces 20,000 carats of luxury a day, at 5 MWh a carat.", + description: "Produces 200,000 carats of luxury a day, at 5 MWh a carat.", resource: "luxury", outputPerDay: 20_000, energyPerDay: 20_000 * RESOURCE_RULES.energyPerLuxuryCarat.diamond_mine, @@ -452,7 +452,7 @@ export const RESOURCE_BUILDINGS = [ advanced: true, name: "Chip foundry", description: - "Fabricates 5000 units of high-tech a day, spending 100 MWh of energy per unit.", + "Fabricates 50,000 units of high-tech a day, spending 10 MWh of energy per unit.", resource: "hightech", outputPerDay: 5_000, energyPerDay: 5_000 * RESOURCE_RULES.energyPerHighTechUnit, diff --git a/shared/data/statuses.js b/shared/data/statuses.js index 6a22472..4a62f89 100644 --- a/shared/data/statuses.js +++ b/shared/data/statuses.js @@ -16,7 +16,7 @@ export const STATUSES = [ id: STATUS_ENCIRCLED, label: "Encircled", icon: "encirclement.svg", - description: "Cut off from every allied city while a hostile unit is adjacent. Loses 10% of its maximum hit points each day, or 2% inside a city.", + description: "Cut off: every adjacent land tile is held by a hostile unit and no friendly port supplies it by sea. Loses 10% of its maximum hit points each day, or 2% inside a city.", hpLossPerDay: 0.10, cityHpLossPerDay: 0.02, }, @@ -24,7 +24,7 @@ export const STATUSES = [ id: STATUS_BESIEGED, label: "Besieged", icon: "encirclement.svg", - description: "Cut off from every other allied city while a hostile unit is adjacent.", + description: "Cut off: every adjacent land tile is held by a hostile unit and no friendly port supplies it by sea.", }, { id: STATUS_BATTLE, diff --git a/shared/data/technologies.js b/shared/data/technologies.js index b6a16c2..248e4bf 100644 --- a/shared/data/technologies.js +++ b/shared/data/technologies.js @@ -74,7 +74,7 @@ export const TECHNOLOGIES = [ { id: "space_program", name: "Space Program", - description: "Reaching orbit inspires the nation and spins off technology across the economy.", + description: "Reaching orbit inspires the nation, lifting its culture and its industry.", theme: "science", cost: 300, prerequisites: ["industrial_automation", "quantum_computing"], diff --git a/shared/data/treaties.js b/shared/data/treaties.js index 4168a59..947060e 100644 --- a/shared/data/treaties.js +++ b/shared/data/treaties.js @@ -26,7 +26,7 @@ export const TREATIES = [ id: TREATY_ALLIANCE, name: "Alliance", description: - "The signatories cannot go to war with each other; attacking one declares war on all.", + "The signatories spare each other's forces; attacking one breaks the pact and draws its allies into the war.", }, { id: TREATY_FREE_TRADE, @@ -48,7 +48,7 @@ export const TREATIES = [ { id: TREATY_GIVE_RESOURCES, name: "Give resources", - description: "Transfers stores of steel, high-tech, luxury, food or energy.", + description: "Transfers stores of steel, high-tech, luxury or food.", payload: "resources", }, { diff --git a/tests/game_screen_test.js b/tests/game_screen_test.js index 3670e88..3d7e61f 100644 --- a/tests/game_screen_test.js +++ b/tests/game_screen_test.js @@ -2291,7 +2291,7 @@ export class GameScreenTest extends TestCase { .map((_, el) => env.$(el).text()) .get() .join("|"); - this.assertTrue(labels.includes("doubling"), `the automatic rise is explained: ${labels}`); + this.assertTrue(labels.includes("raised to meet them"), `the automatic rise is explained: ${labels}`); this.assertFalse(labels.includes("Increase budget"), `no raise is offered: ${labels}`); this.assertEqual( env.$("#tile-works .site-warning").length, diff --git a/tests/modals_test.js b/tests/modals_test.js index 8e797af..a28c191 100644 --- a/tests/modals_test.js +++ b/tests/modals_test.js @@ -34,7 +34,10 @@ export class CityModalTest extends TestCase { 1, "the lab's science output carries the erlenmeyer icon" ); - this.assertFalse($tip.text().includes("research"), "the unit word is replaced by the icon"); + this.assertFalse( + $tip.find(".tip-produce").text().includes("research"), + "the unit word is replaced by the icon" + ); modal.close(); } finally { teardownDom(env); @@ -287,13 +290,13 @@ export class CityModalTest extends TestCase { }; modal.show({ id: 1, name: "Paris" }, { budget: 1.0e15, buildings: BUILDINGS, levels: {} }); modal.renderBudget(breakdown); - this.assertTrue(env.$("#city-budget-sub").text().includes("+¤635/h"), "the region net is shown"); + this.assertTrue(env.$("#city-budget-sub").text().includes("+¤15 240/day"), "the region net is shown"); const text = env.$("#city-budget-list").text(); - this.assertTrue(text.includes("+¤1 035/h"), "region tax income is green"); + this.assertTrue(text.includes("+¤24 840/day"), "region tax income is green"); this.assertTrue(text.includes("Bank in Paris (building) +5.0%"), "a building bonus is listed"); - this.assertTrue(text.includes("−¤400/h"), "the building upkeep is listed"); + this.assertTrue(text.includes("−¤9 600/day"), "the building upkeep is listed"); this.assertTrue(text.includes("TOTAL"), "a total row is present"); - this.assertTrue(text.includes("+¤635/h"), "the total carries the region net"); + this.assertTrue(text.includes("+¤15 240/day"), "the total carries the region net"); this.assertFalse(text.includes("No units"), "unit upkeep is national, not regional"); env.$("#city-budget-list tr.budget-group .budget-name").click(); @@ -664,7 +667,8 @@ export class NationModalTest extends TestCase { 1, "the rate carries the erlenmeyer icon" ); - this.assertTrue($sub.text().includes("2.5"), "the rate is shown"); + this.assertTrue($sub.text().includes("60.0"), "the per-day rate is shown"); + this.assertTrue($sub.text().includes("/day"), "the rate is stated per day"); this.assertFalse($sub.text().includes("RP"), "no text 'RP' unit is left"); modal.close(); } finally {