Compare commits
3
Commits
9fc94a8e00
...
575f08b64e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
575f08b64e | ||
|
|
a98cde4e48 | ||
|
|
da11e1d97d |
@@ -0,0 +1 @@
|
||||
<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M2 2h508v508H2z" fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="2"></path><g class="" style="" transform="translate(0,0)"><path d="m419.682 26.2-8.66 2.452L32.915 135.81 55.27 214.7l386.77-109.608L419.683 26.2zm-12.41 22.224 9.074 32.014-41.086-22.942 32.013-9.072zM350.77 64.438l56.8 31.714-37.084 10.51-56.8-31.715 37.084-10.51zm-61.577 17.45 56.803 31.716-37.084 10.51-56.8-31.718 37.08-10.51zm-61.574 17.45 56.802 31.715-37.084 10.51-56.803-31.715 37.084-10.51zm-61.577 17.45 56.803 31.716-37.084 10.51-56.8-31.717 37.08-10.51zm-61.574 17.45 56.8 31.715-37.083 10.51-56.802-31.715 37.084-10.51zm-45.86 26.227 41.085 22.94-32.01 9.072-9.074-32.012zM55 215v274h402V215H55zm18 18h33.273L73 266.273V233zm58.727 0h38.546l-46 46H85.727l46-46zm64 0h38.546l-46 46h-38.546l46-46zm64 0h38.546l-46 46h-38.546l46-46zm64 0h38.546l-46 46h-38.546l46-46zm64 0h38.546l-46 46h-38.546l46-46zM439 245.727V279h-33.273L439 245.727zM73 297h366v174H73V297zm248.635 46.57-192.44.703.067 18 192.44-.703-.067-18zM130.7 391.33l-.134 17.998 92.707.703.137-18-92.71-.7zm127.155.7-.2 18 63.913.702.2-17.998-63.913-.703z" fill="#9013fe" fill-opacity="1"></path></g></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -1,5 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height:512px;width:512px">
|
||||
<rect x="8" y="8" width="496" height="496" rx="24" fill="#2b3550" stroke="#ffffff" stroke-width="12"/>
|
||||
<text x="256" y="266" fill="#ffffff" font-family="sans-serif" font-size="40" font-weight="700" text-anchor="middle">dummy icon</text>
|
||||
<text x="256" y="316" fill="#ffffff" font-family="sans-serif" font-size="40" font-weight="700" text-anchor="middle">submarine</text>
|
||||
</svg>
|
||||
<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g class="" style="" transform="translate(0,0)"><path d="M315 144v21.75L292 160l-16 80H164c-49.154 16.385-81.254 27.1-102.578 34.846L52 256l-16-16v44.918C20.047 292.31 20 296.316 20 304c0 7.712.05 11.717 16 19.162V368l16-16 9.352-18.703c21.094 7.734 52.752 18.418 101.072 34.703H372c160-16 160-128 0-128h-16v-64l-23-5.75V144h-18zM148 263h256v18H148v-18z" fill="#9b9b9b" fill-opacity="1" stroke="#4a4a4a" stroke-opacity="1" stroke-width="5"></path></g></svg>
|
||||
|
Before Width: | Height: | Size: 479 B After Width: | Height: | Size: 556 B |
@@ -1200,10 +1200,11 @@ input:focus, select:focus { border-color: var(--accent); }
|
||||
/* The tri-state group tick: selects or excludes the whole type in one click and
|
||||
doubles as the group's readout (all / some / none). */
|
||||
.stack-group-check {
|
||||
width: 16px; height: 16px; flex: 0 0 auto; padding: 0;
|
||||
width: 42px; height: 42px;
|
||||
flex: 0 0 auto; padding: 0; box-sizing: border-box;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
border: 1px solid #46566e; border-radius: 4px; background: #101720;
|
||||
color: #eaf2fc; font-size: 11px; line-height: 1; cursor: pointer;
|
||||
border: 1px solid #46566e; border-radius: 6px; background: #101720;
|
||||
color: #eaf2fc; font-size: 26px; line-height: 1; cursor: pointer;
|
||||
}
|
||||
.stack-group-check:hover { border-color: #6eafff; }
|
||||
.stack-group-check.all { background: #3f6fa8; border-color: #6eafff; }
|
||||
@@ -2263,6 +2264,9 @@ input:focus, select:focus { border-color: var(--accent); }
|
||||
padding: 4px 6px;
|
||||
}
|
||||
.politics-readout { color: var(--muted); font-size: 12px; margin-left: 8px; }
|
||||
.politics-card .res { display: inline; white-space: nowrap; }
|
||||
.politics-card .res img { width: 13px; height: 13px; vertical-align: -2px; margin-right: 3px; }
|
||||
.culture-readout { margin-left: 0; font-size: 13px; }
|
||||
.politics-policy {
|
||||
padding: 8px 0;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.07);
|
||||
|
||||
+18
-18
@@ -1,6 +1,24 @@
|
||||
// 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": "a98cde4",
|
||||
"date": "2026-09-28",
|
||||
"subject": "Replaced the placeholder submarine icon",
|
||||
"body": ""
|
||||
},
|
||||
{
|
||||
"hash": "da11e1d",
|
||||
"date": "2026-09-28",
|
||||
"subject": "Showed the culture a propaganda campaign drains",
|
||||
"body": "The simple economy pays for propaganda out of the nation's culture stock and lapses a campaign it can no longer fund, but the tab quoted money and never named the store. Give culture the clapperboard icon as a non-tradeable resource like research, read the stock and the campaigns' daily drain out on the Propaganda tab, and price every campaign figure in culture when the simple economy runs."
|
||||
},
|
||||
{
|
||||
"hash": "9fc94a8",
|
||||
"date": "2026-09-28",
|
||||
"subject": "Rebuilt the technology panel as one lane-routed draggable graph",
|
||||
"body": ""
|
||||
},
|
||||
{
|
||||
"hash": "3c737e9",
|
||||
"date": "2026-09-28",
|
||||
@@ -42,23 +60,5 @@ export const DEVLOG = [
|
||||
"date": "2026-09-25",
|
||||
"subject": "Showed the nation's resource pool in the simple economy's top panel",
|
||||
"body": "The top bar now carries a two-by-two grid of the four storable goods, each a big icon, the grouped pool and an arrow reading production against consumption (purchases excluded); hovering a chip names both daily figures. The quantity column reserves room for 99 999 999 999 so the layout never shifts as stocks grow.\n\nformatResourceAmount paired each prefix with a divisor a thousand times too large, so a million tonnes printed as 1 kt; the steps now move together as kt, Mt, Gt and Tt."
|
||||
},
|
||||
{
|
||||
"hash": "d5305da",
|
||||
"date": "2026-09-25",
|
||||
"subject": "Gave the world a simple economy: one pool per nation and a global market",
|
||||
"body": "The simple economy is now the default and the old simulation is frozen as the hard model, selectable per game. A nation keeps one resource pool available in every region, fed by public production works rather than private per-region stores, and construction and training begin at once, paced by the region's spare power instead of gathering materials over days.\n\nMoney is euro only: a per-inhabitant daily tax funds a treasury spent in a global market, where buy and sell lots trade against the world stock and move its price. The hard economy keeps its per-nation currencies, central banks and exchange rates.\n\nSimple games skip the price-settling warmup and economic migration; the snapshot carries the economy model, the market and the pool figures the client draws. Added the matching test suites and the design briefs under docs/simple_economy."
|
||||
},
|
||||
{
|
||||
"hash": "49613f2",
|
||||
"date": "2026-09-24",
|
||||
"subject": "Made training gather materials, fixed the trade graph, and banked money gifts",
|
||||
"body": "Unit orders now gather steel and high-tech over the days before training, exactly as buildings do, and clicking a queued order reveals what it is gathering, its reserved budget and the soldiers it will draw.\n\nThe trade graph cached a producer-less node list under a stamp that already reflected the producers' stores, so every seeded mine stayed invisible to buyers and rare-good prices settled at a huge multiple of base; the storage nodes are now re-read after production.\n\nMoney gifts draw the giver's central-bank reserves in the denomination first, buy the rest from the issuing bank at the market rate, convert into the receiver's currency, and cannot buy from a bank their nation is at war with.\n\nPruned the finished design briefs (intelligence, stacks, air movement, politics performance, economy balance, training UI and FIXME) and refreshed the index and cross-references."
|
||||
},
|
||||
{
|
||||
"hash": "afb23df",
|
||||
"date": "2026-09-24",
|
||||
"subject": "Cut the migration sweep's repeated lookups and population churn",
|
||||
"body": ""
|
||||
}
|
||||
];
|
||||
|
||||
@@ -2744,6 +2744,10 @@ export const panelMethods = {
|
||||
localCiv,
|
||||
totalHours: snapshot.totalHours || 0,
|
||||
campaigns: snapshot.campaigns || [],
|
||||
// The nation's culture stock, which the simple economy spends on the
|
||||
// campaigns, and the model that decides which currency they are paid in.
|
||||
culture: snapshot.culture || [],
|
||||
economyModel: this.economyModel,
|
||||
opinions: snapshot.opinions || { ethnic: [], government: [] },
|
||||
spies: (snapshot.units || [])
|
||||
.filter((unit) => unit.civ === localCiv && this._isSpy(unit))
|
||||
|
||||
+106
-28
@@ -16,6 +16,7 @@ import {
|
||||
MARKET_LOTS,
|
||||
resourceById,
|
||||
PRICE_INDEX_WINDOW_DAYS,
|
||||
SIMPLE_ECONOMY,
|
||||
TREATIES,
|
||||
treatyById,
|
||||
} from "../../../shared/data.js";
|
||||
@@ -24,7 +25,7 @@ import { renderMonthlyBudget } from "./budget.js";
|
||||
import { effectNodes, money, quotedMoney } from "./format.js";
|
||||
import { bindTabs } from "./tabs.js";
|
||||
import { ethnicityChart } from "../ui/ethnicity.js";
|
||||
import { researchNode } from "../ui/card.js";
|
||||
import { cultureNode, researchNode } from "../ui/card.js";
|
||||
import { TechnologyTreeView } from "../ui/tech_tree_view.js";
|
||||
import { resourceNode, valueNode } from "../ui/value.js";
|
||||
import { lineChart } from "../ui/line_chart.js";
|
||||
@@ -2097,6 +2098,7 @@ export class NationModal {
|
||||
this._spyRows = new Map();
|
||||
this._reportsSignature = null;
|
||||
this.$intelligenceSpies = $("<div class='politics-card'></div>");
|
||||
this.$intelligenceCulture = $("<div class='politics-card'></div>");
|
||||
this.$intelligenceCampaigns = $("<div class='politics-card'></div>");
|
||||
this.$intelligenceSpyView.append(
|
||||
this._buildSpiesCard(),
|
||||
@@ -2104,6 +2106,7 @@ export class NationModal {
|
||||
this._buildSatellitesCard()
|
||||
);
|
||||
this.$intelligencePropagandaView.append(
|
||||
this._buildCultureCard(),
|
||||
this.$intelligenceCampaigns,
|
||||
this._buildCampaignForm(civs)
|
||||
);
|
||||
@@ -2114,6 +2117,7 @@ export class NationModal {
|
||||
this.$intelligenceSub.text(
|
||||
"Your spies and the propaganda campaigns they serve."
|
||||
);
|
||||
this._fillCulture(info, localCiv);
|
||||
this._fillSpies(info, localCiv);
|
||||
this._fillReports(info);
|
||||
this._fillSatellites(info);
|
||||
@@ -2122,6 +2126,42 @@ export class NationModal {
|
||||
this._syncCampaignForm();
|
||||
}
|
||||
|
||||
// The culture readout: the simple economy funds propaganda from the culture
|
||||
// stock, so the tab names the store and what the running campaigns drain from
|
||||
// it each day. The hard economy pays campaigns from the treasury instead, so
|
||||
// only the stock (soft power) is worth naming there.
|
||||
_buildCultureCard() {
|
||||
const $card = this.$intelligenceCulture;
|
||||
$card.empty();
|
||||
$card.append($("<div class='name'></div>").text("Culture"));
|
||||
const $desc = $("<div class='desc'></div>").text(
|
||||
this._campaignPaidInCulture()
|
||||
? "Propaganda is paid for in culture, drained from this store each day; a " +
|
||||
"campaign lapses when there is too little left to fund it."
|
||||
: "Your stock of soft power. Campaigns are funded from the treasury."
|
||||
);
|
||||
this.$cultureReadout = $("<div class='politics-readout culture-readout'></div>");
|
||||
$card.append($desc, this.$cultureReadout);
|
||||
return $card;
|
||||
}
|
||||
|
||||
_fillCulture(info, localCiv) {
|
||||
if (!this.$cultureReadout || !this.$cultureReadout.length) return;
|
||||
const culture = (info.culture && info.culture[localCiv]) || 0;
|
||||
const upkeep = (info.campaigns || [])
|
||||
.filter((campaign) => campaign.civ === localCiv)
|
||||
.reduce((sum, campaign) => sum + (campaign.hourlyCost || 0), 0);
|
||||
const $readout = this.$cultureReadout.empty().append(cultureNode(culture));
|
||||
if (this._campaignPaidInCulture()) {
|
||||
$readout
|
||||
.append(document.createTextNode(" culture · Campaigns consume "))
|
||||
.append(cultureNode(upkeep * HOURS_PER_DAY))
|
||||
.append(document.createTextNode("/day"));
|
||||
} else {
|
||||
$readout.append(document.createTextNode(" culture"));
|
||||
}
|
||||
}
|
||||
|
||||
// The spy roster: one row per spy in the field, with a Locate button that
|
||||
// selects it and brings the map to it. Rows are only rebuilt when the roster
|
||||
// (or a spy's whereabouts) changes, so an arriving snapshot does not yank a
|
||||
@@ -2336,19 +2376,51 @@ export class NationModal {
|
||||
}
|
||||
for (const campaign of campaigns) {
|
||||
const row = this._campaignRows.get(campaign.id);
|
||||
if (row) row.$cost.text(this._campaignCostText(info, campaign));
|
||||
if (row) row.$cost.empty().append(this._campaignCostNode(info, campaign));
|
||||
}
|
||||
}
|
||||
|
||||
_campaignCostText(info, campaign) {
|
||||
// Which currency propaganda is funded from: the simple economy drains the
|
||||
// culture stock, the hard economy the treasury. The two models also price a
|
||||
// campaign with their own figures, so the slider bounds come from here too.
|
||||
_campaignPaidInCulture() {
|
||||
return this.economyModel === "simple";
|
||||
}
|
||||
|
||||
_campaignDefaultCost() {
|
||||
return this._campaignPaidInCulture()
|
||||
? SIMPLE_ECONOMY.campaign.defaultCulturePerHour
|
||||
: PROPAGANDA.defaultHourlyCost;
|
||||
}
|
||||
|
||||
_campaignMaxCost() {
|
||||
return this._campaignPaidInCulture()
|
||||
? SIMPLE_ECONOMY.campaign.maxCulturePerHour
|
||||
: PROPAGANDA.maxHourlyCost;
|
||||
}
|
||||
|
||||
// A campaign figure in its own currency: culture points with the clapperboard
|
||||
// icon in the simple economy, quoted euros in the hard one. `amount` is
|
||||
// already scaled to the period being shown.
|
||||
_campaignAmountNode(amount) {
|
||||
if (this._campaignPaidInCulture()) return cultureNode(amount);
|
||||
return document.createTextNode(
|
||||
`${currencySymbol()}${groupDigits(Math.round(this._quoted(amount)))}`
|
||||
);
|
||||
}
|
||||
|
||||
_campaignCostNode(info, campaign) {
|
||||
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 / HOURS_PER_DAY);
|
||||
return (
|
||||
`${currencySymbol()}${groupDigits(Math.round(this._quoted(hourly) * HOURS_PER_DAY))}/day · ${currencySymbol()}${groupDigits(Math.round(spent))} spent over ` +
|
||||
`${days} day${days === 1 ? "" : "s"}`
|
||||
);
|
||||
const $cost = $("<span class='politics-campaign-cost'></span>");
|
||||
$cost
|
||||
.append(this._campaignAmountNode(hourly * HOURS_PER_DAY))
|
||||
.append(document.createTextNode("/day · "))
|
||||
.append(this._campaignAmountNode(spent))
|
||||
.append(document.createTextNode(` spent over ${days} day${days === 1 ? "" : "s"}`));
|
||||
return $cost;
|
||||
}
|
||||
|
||||
_buildCampaignRow(info, localCiv, campaign) {
|
||||
@@ -2369,30 +2441,32 @@ export class NationModal {
|
||||
if (campaign.civ === localCiv) {
|
||||
const $slider = $("<input type='range' class='politics-slider'>").attr({
|
||||
min: 0,
|
||||
max: PROPAGANDA.maxHourlyCost,
|
||||
step: 1000,
|
||||
max: this._campaignMaxCost(),
|
||||
step: this._campaignPaidInCulture() ? 1 : 1000,
|
||||
value: campaign.hourlyCost,
|
||||
});
|
||||
const $spend = $("<span class='politics-readout'></span>").text(
|
||||
`${currencySymbol()}${groupDigits(Math.round(this._quoted(campaign.hourlyCost) * HOURS_PER_DAY))}/day`
|
||||
);
|
||||
$slider.on("input", () =>
|
||||
$spend.text(
|
||||
`${currencySymbol()}${groupDigits(Math.round(this._quoted(Number($slider.val())) * HOURS_PER_DAY))}/day`
|
||||
)
|
||||
);
|
||||
const spendNode = (hourly) =>
|
||||
$("<span class='politics-readout'></span>")
|
||||
.append(this._campaignAmountNode(hourly * HOURS_PER_DAY))
|
||||
.append(document.createTextNode("/day"));
|
||||
let $spend = spendNode(campaign.hourlyCost);
|
||||
$slider.on("input", () => {
|
||||
const next = spendNode(Number($slider.val()));
|
||||
$spend.replaceWith(next);
|
||||
$spend = next;
|
||||
});
|
||||
$slider.on("change", () => {
|
||||
const amount = Number($slider.val());
|
||||
if (amount <= 0) {
|
||||
// Reverting to the last known spend keeps the control honest until
|
||||
// the player confirms the stop.
|
||||
$slider.val(campaign.hourlyCost);
|
||||
$spend.text(
|
||||
`${currencySymbol()}${groupDigits(Math.round(this._quoted(campaign.hourlyCost) * HOURS_PER_DAY))}/day`
|
||||
);
|
||||
const next = spendNode(campaign.hourlyCost);
|
||||
$spend.replaceWith(next);
|
||||
$spend = next;
|
||||
this.onConfirm(
|
||||
"Stop this campaign?",
|
||||
`Cutting the spending to ${currencySymbol()}0 will stop the campaign.`,
|
||||
`Cutting the spending to ${this._campaignPaidInCulture() ? "0 culture" : `${currencySymbol()}0`} will stop the campaign.`,
|
||||
() => this.onCancelCampaign(campaign.id)
|
||||
);
|
||||
} else {
|
||||
@@ -2422,17 +2496,21 @@ export class NationModal {
|
||||
$card.append($("<div class='name'></div>").text("Launch a propaganda campaign"));
|
||||
$card.append(
|
||||
$("<div class='desc'></div>").text(
|
||||
"Spend money to warm or cool a population's view of another people or government. " +
|
||||
"Your cultural impact decides how strongly the message lands, and the last " +
|
||||
"few popularity points are the hardest to win."
|
||||
`Spend ${this._campaignPaidInCulture() ? "culture" : "money"} to warm or cool a ` +
|
||||
"population's view of another people or government. Your cultural impact " +
|
||||
"decides how strongly the message lands, and the last few popularity points " +
|
||||
"are the hardest to win."
|
||||
)
|
||||
);
|
||||
$card.append(
|
||||
$("<div class='desc politics-cost'></div>").text(
|
||||
`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."
|
||||
const $beginCost = $("<div class='desc politics-cost'></div>").append(
|
||||
document.createTextNode("Costs "),
|
||||
this._campaignAmountNode(this._campaignDefaultCost() * HOURS_PER_DAY),
|
||||
document.createTextNode(
|
||||
"/day to begin; adjust the spending on an active campaign to make it land " +
|
||||
"harder or softer."
|
||||
)
|
||||
);
|
||||
$card.append($beginCost);
|
||||
const $observer = this._civSelect(civs, campaign.observer);
|
||||
const $kind = $("<select class='politics-select'></select>").append(
|
||||
$("<option value='government'></option>").text("their government"),
|
||||
|
||||
@@ -47,6 +47,22 @@ export function researchNode(amount, { sign = "", decimals = 0 } = {}) {
|
||||
return $span;
|
||||
}
|
||||
|
||||
// A culture-point figure with its clapperboard icon, standing in for the word
|
||||
// "culture" the way the erlenmeyer stands in for research. The simple economy
|
||||
// spends culture on propaganda, so the campaign panels read with the same
|
||||
// grammar as the research figures.
|
||||
export function cultureNode(amount, { sign = "", decimals = 0 } = {}) {
|
||||
const proto = resourceById("culture");
|
||||
const $span = $("<span class='res culture-points'></span>");
|
||||
if (proto) {
|
||||
$span.append($("<img>").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.
|
||||
|
||||
@@ -108,8 +108,22 @@ export const RESEARCH = {
|
||||
colour: "#a142b6",
|
||||
};
|
||||
|
||||
// Culture is the nation's store of soft power: produced by its cultural
|
||||
// buildings and spent, in the simple economy, on propaganda campaigns. Like
|
||||
// research it is a special resource -- it has no market, no stockpile tile and
|
||||
// never moves -- so it is kept out of the tradeable catalogue and shown only
|
||||
// with its clapperboard icon.
|
||||
export const CULTURE = {
|
||||
id: "culture",
|
||||
name: "Culture",
|
||||
unit: "",
|
||||
icon: "icon_culture.svg",
|
||||
colour: "#9a7bff",
|
||||
};
|
||||
|
||||
export function resourceById(id) {
|
||||
if (id === RESEARCH.id) return RESEARCH;
|
||||
if (id === CULTURE.id) return CULTURE;
|
||||
return RESOURCES[RESOURCE_INDEX[id]] || null;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { compact, groupDigits } from "../shared/text_format.js";
|
||||
import { tileImprovementResourceCost } from "../shared/resources.js";
|
||||
import { quotedBuildCost, marketPricesFrom } from "../client/js/modals/format.js";
|
||||
import { parseKey } from "../shared/hex.js";
|
||||
import { smallState, adjacentLand, grantBuilding, stripPorts } from "./framework/helpers.js";
|
||||
import { smallState, simpleState, adjacentLand, grantBuilding, stripPorts } from "./framework/helpers.js";
|
||||
|
||||
function stubNetwork() {
|
||||
return {
|
||||
@@ -1598,6 +1598,37 @@ export class GameScreenTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
async test_propaganda_tab_shows_the_culture_drain() {
|
||||
const env = await setupDom();
|
||||
try {
|
||||
const state = simpleState();
|
||||
state.culture.set(0, 12_345);
|
||||
state.requestCampaign(0, 0, "government", 1, 1);
|
||||
const screen = new GameScreen(stubNetwork(), makeConfig());
|
||||
screen.enter();
|
||||
screen.onState(state.snapshot(0));
|
||||
|
||||
env.$("#civ-flag").click();
|
||||
env.$("#modal-nation .tab[data-tab='intelligence']").click();
|
||||
showIntelligenceSubtab(env, "Propaganda");
|
||||
|
||||
const $culture = env.$("#intelligence-propaganda-view .culture-readout");
|
||||
this.assertSize($culture, 1, "the propaganda tab reads out the culture stock");
|
||||
this.assertTrue($culture.text().includes("12\u00a0345"), "it names the current culture");
|
||||
this.assertTrue($culture.text().includes("192"), "and what the campaigns consume");
|
||||
const src = $culture.find("img").first().attr("src");
|
||||
this.assertTrue(/icon_culture\.svg$/.test(src || ""), "with the culture icon");
|
||||
|
||||
const $cost = env.$("#intelligence-propaganda-view .politics-campaign-cost");
|
||||
this.assertSize($cost, 1, "the running campaign reports its cost");
|
||||
this.assertGreater($cost.find("img").length, 0, "in culture points");
|
||||
this.assertFalse($cost.text().includes("\u20ac"), "not in money");
|
||||
screen.leave();
|
||||
} finally {
|
||||
teardownDom(env);
|
||||
}
|
||||
}
|
||||
|
||||
async test_politics_tab_sends_campaign_and_policy_orders() {
|
||||
const env = await setupDom();
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user