-
- ${tabs.map((cat) => {
- const isAllTab = cat === "Overview";
- const isPolicyTab = cat === "Policy Directives";
- const isActive = isAllTab
- ? isAllView
- : isPolicyTab
- ? isPolicyDirectivesView
- : cat === activeCategory;
- const tabTooltip = translateText(
- `research_tree.tab_tooltip.${cat.toLowerCase().replace(" ", "_")}`,
- );
- const showPolicyBadge =
- isPolicyTab && me?.hasUnseenPolicyDirectives?.();
- return html``;
- })}
-
-
${this.renderResearchSlider()}
+
+
+
-
-
- ${isPolicyDirectivesView
- ? this.renderPolicyDirectivesView()
- : isAllView
- ? this.renderAllView(
- levels,
- researched,
- categoryColors,
- percentByTechId,
- )
- : activeCategory
- ? html`
+ ${this.categories.map((cat) => {
+ const techs = techsByCategory.get(cat) ?? [];
+ const catClass = `cat-${cat.toLowerCase()}`;
+
+ const icons: Record
= {
+ Land: landIcon,
+ Sea: seaIcon,
+ Air: airIcon,
+ Nuclear: nuclearIcon,
+ };
+ const iconSrc = icons[cat];
+
+ // Check if all non-researched techs in category are prioritized
+ const nonResearchedTechs = techs.filter(
+ (t) => !researched.has(t.id),
+ );
+ const allPrioritized =
+ nonResearchedTechs.length > 0 &&
+ nonResearchedTechs.every((t) => priorities.has(t.id));
+
+ return html`
+
+
+ ${techs.map((tech) => {
+ const available = this.isAvailable(tech.id, researched);
+ const isResearched = researched.has(tech.id);
+ const pct = percentByTechId.get(tech.id) ?? 0;
+ const isPriority = priorities.has(tech.id);
+ const display = getTechDisplay(tech);
+ const tooltip = getDetailedTechTooltip(tech.id);
+
+ const rowClass = [
+ "tech-row",
+ isResearched ? "researched" : "",
+ !available && !isResearched ? "locked" : "",
+ ]
+ .filter(Boolean)
+ .join(" ");
+
+ const barClass = `bar-${cat.toLowerCase()}`;
+
+ // Locked techs show as prioritized (yellow) if they're set as priority
+ const btnClass = [
+ "priority-btn",
+ isPriority && available
+ ? "active"
+ : isPriority && !available
+ ? "locked-prioritized"
+ : "",
+ ]
+ .filter(Boolean)
+ .join(" ");
+
+ return html`
+
this.onTechClick(tech.id)}
+ title=${tooltip}
>
- ${levels
- .filter((lvl) =>
- this.techs.some(
- (t) =>
- t.level === lvl &&
- t.category === activeCategory,
- ),
- )
- .map((lvl) => {
- const techsForLevel = this.techs.filter(
- (t) =>
- t.level === lvl &&
- t.category === activeCategory,
- );
- return html`
-
Tech Level ${lvl}
-
- ${techsForLevel.map((tech) => {
- const available = this.isAvailable(
- tech.id,
- researched,
- );
- const isResearched = researched.has(tech.id);
- const clickable = !isResearched;
- const inHighlight = highlightTrail.has(
- tech.id,
- );
- const classes = [
- "tech",
- available ? "" : "locked",
- isResearched ? "researched" : "",
- inHighlight ? "priority" : "",
- ]
- .filter(Boolean)
- .join(" ");
- const action = this.renderScorchedEarthAction(
- tech,
- me ?? null,
- isResearched,
- );
- return html`
- ${(() => {
- const display = getTechDisplay(tech);
- return html`
`;
- })()}
- ${action}
-
`;
- })}
-
-
`;
- })}
-
`
- : html`
- No research categories found.
-
`}
- ${!isAllView && !isPolicyDirectivesView
- ? html`
`
- : ""}
-
+
+
${display.name}
+
+ ${display.shortDescription ?? ""}
+
+
+
+
+ ${pct > 0 && pct < 100
+ ? html`
${pct}%`
+ : ""}
+
+
+
+ `;
+ })}
+
+ `;
+ })}
diff --git a/src/client/StatisticsModal.ts b/src/client/StatisticsModal.ts
index b156e43db..b45c97ee1 100644
--- a/src/client/StatisticsModal.ts
+++ b/src/client/StatisticsModal.ts
@@ -250,13 +250,7 @@ export class StatisticsModal extends LitElement {
]
: [];
- const categories: Category[] = [
- "Land",
- "Sea",
- "Air",
- "Nuclear",
- "Economy",
- ];
+ const categories: Category[] = ["Land", "Sea", "Air", "Nuclear"];
const nodes = getTechNodes();
const techsByCategory: Array<[string, string]> = sel
? categories.map((cat) => {
diff --git a/src/client/TechTooltips.ts b/src/client/TechTooltips.ts
new file mode 100644
index 000000000..280a1875b
--- /dev/null
+++ b/src/client/TechTooltips.ts
@@ -0,0 +1,72 @@
+import {
+ BOMBER_UPGRADES,
+ FIGHTER_UPGRADES,
+ SUBMARINE_UPGRADES,
+ WARSHIP_UPGRADES,
+} from "../core/game/UnitUpgrades";
+import { RESEARCH_TECH_IDS } from "../core/tech/TechIds";
+
+export function getDetailedTechTooltip(techId: string): string {
+ switch (techId) {
+ // --- SEA ---
+ case RESEARCH_TECH_IDS.SEA_MISSILE_NAVY: {
+ const w2 = WARSHIP_UPGRADES[1];
+ const s1 = SUBMARINE_UPGRADES[0];
+ return `Unlocks:\nโข Gen 2 Warships (+25% health to ${w2.maxHealth}, +35% min damage to ${w2.damageMin}, +21.5% max damage to ${w2.damageMax})\nโข Gen 1 Submarines (${s1.maxHealth} health, ${s1.damageMin}-${s1.damageMax} damage, stealth)`;
+ }
+ case RESEARCH_TECH_IDS.SEA_ADVANCED_FLEET: {
+ const w3 = WARSHIP_UPGRADES[2];
+ const s2 = SUBMARINE_UPGRADES[1];
+ return `Unlocks:\nโข Gen 3 Warships (+20% health to ${w3.maxHealth}, +25.9% min damage to ${w3.damageMin}, +17.7% max damage to ${w3.damageMax})\nโข Gen 2 Submarines (+25% health to ${s2.maxHealth}, +35% min damage to ${s2.damageMin}, +21.5% max damage to ${s2.damageMax})`;
+ }
+ case RESEARCH_TECH_IDS.SEA_NUCLEAR_SUBMARINES: {
+ const s3 = SUBMARINE_UPGRADES[2];
+ return `Unlocks:\nโข Gen 3 Submarines (+20% health to ${s3.maxHealth}, +25.9% min damage to ${s3.damageMin}, +17.7% max damage to ${s3.damageMax})\nโข Ship Anti-Air: Warships engage and destroy aircraft within range`;
+ }
+ case RESEARCH_TECH_IDS.SEA_TBD_LEVEL4:
+ return `Unlocks:\nโข Nuclear Subs: Enables submarines to launch nuclear weapons while submerged and undetected (second-strike capability)`;
+
+ // --- LAND ---
+ case RESEARCH_TECH_IDS.LAND_ROADS_HOSPITALS:
+ return `Unlocks:\nโข Roads: Increases unit movement speed, generates passive trade income per connected tile\nโข Trade Routes: Trade ships establish international commerce routes for continuous gold income`;
+ case RESEARCH_TECH_IDS.LAND_MILITARY_ACADEMY:
+ return `Unlocks:\nโข City Anti-Air: Cities automatically engage enemy aircraft with AA batteries\nโข Improved SAM: +35% range to 94.5 pixels, improved accuracy vs bombers/fighters/missiles`;
+ case RESEARCH_TECH_IDS.LAND_SAM_SYSTEMS:
+ return `Unlocks:\nโข Advanced SAM: +82.25% range to 127.6 pixels (exceeds H-bomb radius), max interception success\nโข Hospitals: Increases city population growth rate (faster troop production & economy)`;
+ case RESEARCH_TECH_IDS.LAND_DOOMSDAY_DEVICE:
+ return `Unlocks:\nโข Military Academy: Unlocks Academy structure; each connected Academy increases enemy troop casualties you inflict in land battles (+10% with one, ~+15% with two, up to +20% cap; applies on attack and defense)`;
+
+ // --- AIR ---
+ case RESEARCH_TECH_IDS.AIR_PARATROOPERS: {
+ const f1 = FIGHTER_UPGRADES[0];
+ return `Unlocks:\nโข Gen 1 Fighters (${f1.maxHealth} health, ${f1.damageMin}-${f1.damageMax} damage, engages aircraft)\nโข Paratroopers: Airborne infantry deployed behind enemy lines for rapid expansion`;
+ }
+ case RESEARCH_TECH_IDS.AIR_ADVANCED_JETS: {
+ const f2 = FIGHTER_UPGRADES[1];
+ const b2 = BOMBER_UPGRADES[1];
+ return `Unlocks:\nโข Gen 2 Fighters (+33.3% health to ${f2.maxHealth}, +50% min damage to ${f2.damageMin}, +30.8% max damage to ${f2.damageMax})\nโข Heavy Bombers (+20% health to ${b2.maxHealth}, +20% damage to ${b2.damageMin}, +40% range to ${b2.targetRange}, +50% speed to 3)`;
+ }
+ case RESEARCH_TECH_IDS.AIR_NAVAL_STRIKE: {
+ const f3 = FIGHTER_UPGRADES[2];
+ return `Unlocks:\nโข Gen 3 Fighters (+25% health to ${f3.maxHealth}, +33.3% min damage to ${f3.damageMin}, +23.5% max damage to ${f3.damageMax})\nโข Naval Strike: Fighters can attack warships, transport ships, and trade ships`;
+ }
+ case RESEARCH_TECH_IDS.AIR_TBD_LEVEL4: {
+ const f4 = FIGHTER_UPGRADES[3];
+ const b3 = BOMBER_UPGRADES[2];
+ return `Unlocks:\nโข Gen 4 Fighters (+20% health to ${f4.maxHealth}, +25% min damage to ${f4.damageMin}, +19% max damage to ${f4.damageMax})\nโข Supersonic Bombers (+16.7% health to ${b3.maxHealth}, +16.7% damage to ${b3.damageMin}, +28.6% range to ${b3.targetRange}, +33.3% speed to 4)`;
+ }
+
+ // --- NUCLEAR ---
+ case RESEARCH_TECH_IDS.NUCLEAR_FISSION:
+ return `Unlocks:\nโข Atom Bomb: Basic fission weapon with large blast radius (inner: 12px, outer: 30px)\nโข Missile Silo: Required launch facility for deploying nuclear weapons`;
+ case RESEARCH_TECH_IDS.THERMONUCLEAR_STAGING:
+ return `Unlocks:\nโข Hydrogen Bomb: High-yield fusion weapon with massive blast radius (inner: 80px, outer: 100px) - devastates multi-tile areas`;
+ case RESEARCH_TECH_IDS.MIRV_TECHNOLOGY:
+ return `Unlocks:\nโข MIRV: Multiple Independent Reentry Vehicles - deploys multiple warheads per missile, significantly harder for SAMs to intercept (50% hit chance vs 100% for atom bombs)`;
+ case RESEARCH_TECH_IDS.NUCLEAR_TBD_LEVEL4:
+ return `Unlocks:\nโข Doomsday Device: Auto-triggers when any of your tiles are hit by a nuke; consumes the device and unleashes a global fallout wave that instantly deletes bombers/fighters/warships/trade ships, damages other structures by 80% of current health, relinquishes land, and spreads fallout across the world`;
+
+ default:
+ return "No detailed information available.";
+ }
+}
diff --git a/src/client/Transport.ts b/src/client/Transport.ts
index 009fcd9e3..c1c434104 100644
--- a/src/client/Transport.ts
+++ b/src/client/Transport.ts
@@ -11,11 +11,6 @@ import {
} from "../core/game/Game";
import { TileRef } from "../core/game/GameMap";
import { PlayerView } from "../core/game/GameView";
-import {
- isUpgradeableUnit,
- maxStructureLevel,
- maxUnitLevel,
-} from "../core/game/Upgradeables";
import {
AllPlayersStats,
ClientHashMessage,
@@ -124,21 +119,10 @@ export class BuildUnitIntentEvent implements GameEvent {
) {}
}
-export class SendScorchedEarthIntentEvent implements GameEvent {}
-
export class SendResearchTreeSelectIntentEvent implements GameEvent {
constructor(public readonly techId: string) {}
}
-export class SendPolicyDirectiveSelectIntentEvent implements GameEvent {
- constructor(
- public readonly directiveId: string,
- public readonly optionId: string,
- ) {}
-}
-
-export class SendMarkPolicyDirectivesSeenIntentEvent implements GameEvent {}
-
export class SendTargetPlayerIntentEvent implements GameEvent {
constructor(public readonly targetID: PlayerID) {}
}
@@ -351,9 +335,6 @@ export class Transport {
this.onSendSetAutoBombingEvent(e),
);
- this.eventBus.on(SendScorchedEarthIntentEvent, () =>
- this.onSendScorchedEarthIntent(),
- );
this.eventBus.on(SendUpgradeStructureIntentEvent, (e) =>
this.onSendUpgradeStructureIntent(e),
);
@@ -368,14 +349,6 @@ export class Transport {
this.onSendResearchTreeSelectIntent(e),
);
- this.eventBus.on(SendPolicyDirectiveSelectIntentEvent, (e) =>
- this.onSendPolicyDirectiveSelectIntent(e),
- );
-
- this.eventBus.on(SendMarkPolicyDirectivesSeenIntentEvent, () =>
- this.onSendMarkPolicyDirectivesSeenIntent(),
- );
-
this.eventBus.on(BuildUnitIntentEvent, (e) => this.onBuildUnitIntent(e));
this.eventBus.on(PauseGameEvent, (e) => this.onPauseGameEvent(e));
@@ -767,45 +740,20 @@ export class Transport {
this._lastBuildUnit = event.unit;
this._lastBuildAt = now;
- // Compute desired starting level for upgradeable structures or units from local settings.
- let targetLevel: number | undefined;
+ // Read stack count from localStorage (in-game communication)
+ let stackCount: number | undefined;
let bomberLevel: number | undefined;
try {
- const key = String(event.unit);
- if (isUpgradeableUnit(event.unit)) {
- const rawUnits = localStorage.getItem("unitUpgradeSettings.levels");
- if (rawUnits) {
- const obj = JSON.parse(rawUnits) as Record
;
- const val = obj?.[key];
- if (typeof val === "number" && val > 1) {
- // Server will clamp to player's researched max level
- targetLevel = Math.min(maxUnitLevel(event.unit), val);
- }
- }
- } else {
- const rawStruct = localStorage.getItem("buildSettings.levels");
- if (rawStruct) {
- const obj = JSON.parse(rawStruct) as Record;
- const val = obj?.[key];
- if (typeof val === "number" && val > 1) {
- targetLevel = Math.min(maxStructureLevel(event.unit), val);
- }
- }
- }
- // For airfields, also get bomber upgrade level from unit upgrade settings
- if (event.unit === UnitType.Airfield) {
- const rawUnits = localStorage.getItem("unitUpgradeSettings.levels");
- if (rawUnits) {
- const obj = JSON.parse(rawUnits) as Record;
- const val = obj?.[String(UnitType.Bomber)];
- if (typeof val === "number" && val > 1) {
- bomberLevel = Math.min(maxUnitLevel(UnitType.Bomber), val);
- }
+ const rawStack = localStorage.getItem("buildSettings.stackCount");
+ if (rawStack) {
+ const obj = JSON.parse(rawStack) as Record;
+ const val = obj?.[String(event.unit)];
+ if (typeof val === "number" && val > 1) {
+ stackCount = Math.min(25, val);
}
}
} catch {
- // Ignore malformed local storage.
- targetLevel = undefined;
+ stackCount = undefined;
bomberLevel = undefined;
}
@@ -814,18 +762,11 @@ export class Transport {
clientID: this.lobbyConfig.clientID,
unit: event.unit,
tile: event.tile,
- targetLevel,
+ targetLevel: stackCount, // Renamed semantically but keeping wire format for now
bomberLevel,
});
}
- private onSendScorchedEarthIntent() {
- this.sendIntent({
- type: "activate_scorched_earth",
- clientID: this.lobbyConfig.clientID,
- });
- }
-
private onSendUpgradeStructureIntent(event: SendUpgradeStructureIntentEvent) {
// Prefer new generic intent
this.sendIntent({
@@ -854,24 +795,6 @@ export class Transport {
});
}
- private onSendPolicyDirectiveSelectIntent(
- event: SendPolicyDirectiveSelectIntentEvent,
- ) {
- this.sendIntent({
- type: "policy_directive_select",
- clientID: this.lobbyConfig.clientID,
- directiveId: event.directiveId,
- optionId: event.optionId,
- });
- }
-
- private onSendMarkPolicyDirectivesSeenIntent() {
- this.sendIntent({
- type: "mark_policy_directives_seen",
- clientID: this.lobbyConfig.clientID,
- });
- }
-
private onPauseGameEvent(event: PauseGameEvent) {
if (!this.isLocal) {
console.log(`cannot pause multiplayer games`);
diff --git a/src/client/Utils.ts b/src/client/Utils.ts
index 890d1927f..8f5a96114 100644
--- a/src/client/Utils.ts
+++ b/src/client/Utils.ts
@@ -168,7 +168,6 @@ export function getMessageTypeClasses(type: MessageType): string {
case MessageType.TRADE_SHIP_CAPTURED_ENEMY:
case MessageType.RECEIVED_GOLD_FROM_TRADE:
case MessageType.CONQUERED_PLAYER:
- case MessageType.INSURANCE_REFUND:
return severityColors["success"];
case MessageType.ATTACK_FAILED:
case MessageType.ALLIANCE_REJECTED:
diff --git a/src/client/graphics/layers/BuildMenu.ts b/src/client/graphics/layers/BuildMenu.ts
index 98a78b66d..0e11105a6 100644
--- a/src/client/graphics/layers/BuildMenu.ts
+++ b/src/client/graphics/layers/BuildMenu.ts
@@ -27,12 +27,13 @@ import {
import { Gold, UnitType, UpgradeType } from "../../../core/game/Game";
import { GameView } from "../../../core/game/GameView";
import {
+ isStackableStructure,
+ isTechUpgradeableStructure,
isUnitAvailable,
- isUpgradeableStructure,
isUpgradeableUnit,
- maxStructureLevel,
+ maxStackCount,
maxUnitLevel,
- playerMaxStructureLevel,
+ playerMaxStructureTechLevel,
playerMaxUnitLevel,
} from "../../../core/game/Upgradeables";
import { ToggleBomberUpgradeModeEvent } from "../../events/ToggleBomberUpgradeModeEvent";
@@ -547,70 +548,79 @@ export class BuildMenu extends LitElement {
.config()
.unitInfo(item.unitType)
.cost(this.game.myPlayer()!);
- // Structures: use configured structure multiplier
- if (isUpgradeableStructure(item.unitType)) {
- const desired = this._desiredStructureLevel(item.unitType);
+ // Stackable structures: use stack count for cost calculation
+ if (isStackableStructure(item.unitType)) {
+ const stackCount = this._desiredStackCount(item.unitType);
let structureCost =
- desired <= 1
+ stackCount <= 1
? base
: aggregateStructureBuildCost(
this.game.config(),
this.game.myPlayer()!,
item.unitType,
- desired,
+ stackCount,
this.game.config().structureUpgradeCostMultiplier(item.unitType),
);
- // Add bomber upgrade cost for airfields
+ // Add bomber upgrade cost for airfields (based on tech level, not stack)
if (item.unitType === UnitType.Airfield) {
- const bomberLevel = this._desiredUnitLevel(UnitType.Bomber);
+ const bomberLevel = this._structureTechLevel(UnitType.Airfield);
structureCost += computeBomberUpgradeCost(
this.game.config(),
this.game.myPlayer()!,
bomberLevel,
- desired,
+ stackCount,
);
}
return structureCost;
}
// Units: use hardcoded costs from UnitUpgrades (aggregateStructureBuildCost handles this)
if (isUpgradeableUnit(item.unitType)) {
- const desired = this._desiredUnitLevel(item.unitType);
- if (desired <= 1) return base;
+ const techLevel = playerMaxUnitLevel(
+ this.game.myPlayer()!,
+ item.unitType,
+ );
+ if (techLevel <= 1) return base;
// aggregateStructureBuildCost detects upgradeable units and uses hardcoded costs
return aggregateStructureBuildCost(
this.game.config(),
this.game.myPlayer()!,
item.unitType,
- desired,
+ techLevel,
0, // multiplier ignored for upgradeable units
);
}
return base;
}
- private _desiredStructureLevel(type: UnitType): number {
- // If a specific level is requested via the UI prop, use that (clamped by max level)
+ // Get the desired stack count for stackable structures
+ private _desiredStackCount(type: UnitType): number {
+ // If a specific level is requested via the UI prop, use that (clamped by max)
const level = this.structureLevels[type];
if (level && level > 1) {
- return Math.min(maxStructureLevel(type), level);
+ return Math.min(maxStackCount(type), level);
}
+ // Read from localStorage (used for in-game communication, not persistence)
try {
- const raw = localStorage.getItem("buildSettings.levels");
+ const raw = localStorage.getItem("buildSettings.stackCount");
if (!raw) return 1;
const obj = JSON.parse(raw);
const key = String(type);
const val = obj?.[key];
if (typeof val !== "number" || val < 1) return 1;
- // Use player-specific max level based on researched techs
- const player = this.game?.myPlayer();
- const maxLevel = player ? playerMaxStructureLevel(player, type) : 1;
- return Math.min(maxLevel, val);
+ return Math.min(maxStackCount(type), val);
} catch (_) {
return 1;
}
}
+ // Get the tech level for tech-upgradeable structures (SAM, Airfield)
+ private _structureTechLevel(type: UnitType): number {
+ const player = this.game?.myPlayer();
+ if (!player) return 1;
+ return playerMaxStructureTechLevel(player, type);
+ }
+
private _desiredUnitLevel(type: UnitType): number {
try {
const raw = localStorage.getItem("unitUpgradeSettings.levels");
@@ -635,7 +645,94 @@ export class BuildMenu extends LitElement {
if (!player) {
return "?";
}
- return player.units(item.unitType).length.toString();
+ // Use unitsOwned() to get the correct count including stacked structures
+ return player.unitsOwned(item.unitType).toString();
+ }
+
+ private getUnitDisplayName(unitType: UnitType, baseName: string): string {
+ const player = this.game?.myPlayer();
+ if (!player) return baseName;
+
+ // Handle combat units with tech upgrades
+ if (isUpgradeableUnit(unitType)) {
+ const level = playerMaxUnitLevel(player, unitType);
+
+ // Only Fighters use "Gen X" naming
+ if (unitType === UnitType.FighterJet && level > 1) {
+ return `Gen ${level} ${baseName}`;
+ }
+
+ // Warships have specific names per level
+ if (unitType === UnitType.Warship) {
+ switch (level) {
+ case 1:
+ return baseName; // "Warship"
+ case 2:
+ return "Cruiser";
+ case 3:
+ return "Aegis Warship";
+ default:
+ return baseName;
+ }
+ }
+
+ // Submarines have specific names per level
+ if (unitType === UnitType.Submarine) {
+ switch (level) {
+ case 1:
+ return "Diesel Sub";
+ case 2:
+ return "Tactical Sub";
+ case 3:
+ return "Attack Sub";
+ default:
+ return baseName;
+ }
+ }
+
+ // Bombers have specific names per level
+ if (unitType === UnitType.Bomber) {
+ switch (level) {
+ case 1:
+ return baseName; // "Bomber"
+ case 2:
+ return "Heavy Bomber";
+ case 3:
+ return "Supersonic Bomber";
+ default:
+ return baseName;
+ }
+ }
+
+ return baseName;
+ }
+
+ // Handle tech-upgradeable structures (SAM, Airfield)
+ if (isTechUpgradeableStructure(unitType)) {
+ const techLevel = playerMaxStructureTechLevel(player, unitType);
+ const stackCount = this._desiredStackCount(unitType);
+
+ let name = baseName;
+ if (unitType === UnitType.SAMLauncher && techLevel > 1) {
+ name =
+ techLevel === 2
+ ? "Radar SAM"
+ : techLevel === 3
+ ? "Strategic SAM"
+ : baseName;
+ }
+
+ // Do not prefix stack count in the label; chip handles it
+ return name;
+ }
+
+ // Handle other stackable structures
+ if (isStackableStructure(unitType)) {
+ // Do not prefix stack count in the label; chip handles it
+ return baseName;
+ }
+
+ return baseName;
}
public onBuildSelected = (item: BuildItemDisplay) => {
@@ -674,16 +771,17 @@ export class BuildMenu extends LitElement {
(row) => html`
${row.map((item) => {
- const name = item.key
+ const baseName = item.key
? translateText(item.key)
: String(item.unitType);
const price =
this.game && this.game.myPlayer() ? this.cost(item) : 0;
- const desiredLevel = isUpgradeableStructure(item.unitType)
- ? this._desiredStructureLevel(item.unitType)
- : isUpgradeableUnit(item.unitType)
- ? this._desiredUnitLevel(item.unitType)
- : 1;
+
+ const displayName = this.getUnitDisplayName(
+ item.unitType,
+ baseName,
+ );
+ const desiredStack = this._desiredStackCount(item.unitType);
return html`