Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion resources/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@
"warship": "Captures trade ships, destroys ships and boats"
},
"not_enough_money": "Not enough money",
"select_upgrade_amount": "Select Upgrade Amount",
"upgrade_amount": "x{amount}",
"warship_shift_hint": "Hold Shift and drag to select multiple warships at once"
},
"chat": {
Expand Down Expand Up @@ -1339,7 +1341,8 @@
},
"radial_menu": {
"delete_unit_description": "Click to delete the nearest unit",
"delete_unit_title": "Delete Unit"
"delete_unit_title": "Delete Unit",
"upgrade_x": "Upgrade x{amount}"
},
"relation": {
"default": "Default",
Expand Down
13 changes: 12 additions & 1 deletion src/client/InputHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,18 @@ export class InputHandler {
}

private setGhostStructure(ghostStructure: PlayerBuildableUnitType | null) {
this.uiState.ghostStructure = ghostStructure;
if (
this.uiState.ghostStructure === ghostStructure &&
ghostStructure !== null
) {
const multipliers = [1, 5, 10, 25, 50];
const idx = multipliers.indexOf(this.uiState.upgradeMultiplier || 1);
this.uiState.upgradeMultiplier =
multipliers[(idx + 1) % multipliers.length];
} else {
this.uiState.upgradeMultiplier = 1;
this.uiState.ghostStructure = ghostStructure;
}
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/client/Transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export class SendUpgradeStructureIntentEvent implements GameEvent {
constructor(
public readonly unitId: number,
public readonly unitType: UnitType,
public readonly amount: number = 1,
) {}
}

Expand Down Expand Up @@ -518,6 +519,7 @@ export class Transport {
type: "upgrade_structure",
unit: event.unitType,
unitId: event.unitId,
amount: event.amount,
});
}

Expand Down
1 change: 1 addition & 0 deletions src/client/UIState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export interface UIState {
attackRatio: number;
ghostStructure: PlayerBuildableUnitType | null;
rocketDirectionUp: boolean;
upgradeMultiplier: number;
}
6 changes: 5 additions & 1 deletion src/client/controllers/BuildPreviewController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,9 @@ export class BuildPreviewController implements Controller {
radiusTileY = this.game.y(upgradeTargetTile);
}

const cost = u.cost;
const multiplier =
u.canUpgrade !== false ? this.uiState.upgradeMultiplier || 1 : 1;
const cost = u.cost * BigInt(multiplier);
return {
ghostType: u.type,
tileX: this.game.x(tileRef),
Expand All @@ -467,6 +469,7 @@ export class BuildPreviewController implements Controller {
canBuild: u.canBuild !== false,
canUpgrade: u.canUpgrade !== false,
cost: Number(cost),
multiplier: multiplier,
showCost: this.userSettings.cursorCostLabel(),
canAfford: myPlayer.gold() >= cost,
ghostRailPaths: u.ghostRailPaths,
Expand Down Expand Up @@ -508,6 +511,7 @@ export class BuildPreviewController implements Controller {
new SendUpgradeStructureIntentEvent(
this.ghostUnit.buildableUnit.canUpgrade,
this.ghostUnit.buildableUnit.type,
this.uiState.upgradeMultiplier || 1,
),
);
this.removeGhostStructure();
Expand Down
1 change: 1 addition & 0 deletions src/client/hud/GameRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export function createRenderer(
attackRatio: 20,
ghostStructure: null,
rocketDirectionUp: true,
upgradeMultiplier: 1,
};

//hide when the game renders
Expand Down
218 changes: 152 additions & 66 deletions src/client/hud/layers/BuildMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ export class BuildMenu extends LitElement implements Controller {
@state()
private _hidden = true;

@state()
private _selectedUpgradeUnitType: UnitType | null = null;

public canBuildOrUpgrade(item: BuildItemDisplay): boolean {
if (this.game?.myPlayer() === null || this.playerBuildables === null) {
return false;
Expand All @@ -382,14 +385,10 @@ export class BuildMenu extends LitElement implements Controller {
return player.totalUnitLevels(item.unitType).toString();
}

public sendBuildOrUpgrade(buildableUnit: BuildableUnit, tile: TileRef): void {
public handleBuildClick(buildableUnit: BuildableUnit, tile: TileRef): void {
if (buildableUnit.canUpgrade !== false) {
this.eventBus.emit(
new SendUpgradeStructureIntentEvent(
buildableUnit.canUpgrade,
buildableUnit.type,
),
);
this._selectedUpgradeUnitType = buildableUnit.type;
this.requestUpdate();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
} else if (buildableUnit.canBuild) {
const rocketDirectionUp =
buildableUnit.type === UnitType.AtomBomb ||
Expand All @@ -399,81 +398,168 @@ export class BuildMenu extends LitElement implements Controller {
this.eventBus.emit(
new BuildUnitIntentEvent(buildableUnit.type, tile, rocketDirectionUp),
);
this.hideMenu();
}
}

public confirmUpgrade(amount: number): void {
if (!this._selectedUpgradeUnitType) {
this.hideMenu();
return;
}
const bu = this.playerBuildables?.find(
(u) => u.type === this._selectedUpgradeUnitType,
);
if (!bu || bu.canUpgrade === false) {
this.hideMenu();
return;
}
this.eventBus.emit(
new SendUpgradeStructureIntentEvent(bu.canUpgrade, bu.type, amount),
);
this.hideMenu();
}

renderAmountPanel() {
if (!this._selectedUpgradeUnitType) return html``;
const bu = this.playerBuildables?.find(
(u) => u.type === this._selectedUpgradeUnitType,
);
if (!bu) return html``;
const baseCost = bu.cost;
const playerGold = this.game?.myPlayer()?.gold() ?? 0n;

return html`
<div
style="display: flex; flex-direction: column; align-items: center; color: white; padding: 10px;"
>
<h3 style="margin-bottom: 15px; font-weight: bold; font-size: 16px;">
${translateText("build_menu.select_upgrade_amount")}
</h3>
<div
style="display: flex; gap: 10px; justify-content: center; flex-wrap: wrap;"
>
${[1, 5, 10, 25, 50].map((amount) => {
const cost = baseCost * BigInt(amount);
const canAfford = playerGold >= cost;
return html`
<button
class="build-button"
style="height: 100px; width: 80px;"
@click=${() => canAfford && this.confirmUpgrade(amount)}
?disabled=${!canAfford}
title=${!canAfford
? translateText("build_menu.not_enough_money")
: ""}
>
<span style="font-size: 20px; font-weight: bold;"
>${translateText("build_menu.upgrade_amount", {
amount: amount.toString(),
})}</span
>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<span
class="build-cost"
translate="no"
style="margin-top: 10px;"
>
${renderNumber(cost)}
<img
src=${goldCoinIcon}
alt="gold"
width="12"
height="12"
class="align-middle"
/>
</span>
</button>
`;
})}
</div>
</div>
`;
}

render() {
return html`
<div
class="build-menu ${this._hidden ? "hidden" : ""}"
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
${this.filteredBuildTable.map(
(row) => html`
<div class="build-row">
${row.map((item) => {
const buildableUnit = this.playerBuildables?.find(
(bu) => bu.type === item.unitType,
);
if (buildableUnit === undefined) {
return html``;
}
const enabled =
buildableUnit.canBuild !== false ||
buildableUnit.canUpgrade !== false;
return html`
<button
class="build-button"
@click=${() =>
this.sendBuildOrUpgrade(buildableUnit, this.clickedTile)}
?disabled=${!enabled}
title=${!enabled
? translateText("build_menu.not_enough_money")
: ""}
>
<img
src=${item.icon}
alt="${item.unitType}"
width="40"
height="40"
/>
<span class="build-name"
>${item.key && translateText(item.key)}</span
>
<span class="build-description"
>${item.description &&
translateText(item.description)}</span
>
<span class="build-cost" translate="no">
${renderNumber(
this.game && this.game.myPlayer() ? this.cost(item) : 0,
)}
<img
src=${goldCoinIcon}
alt="gold"
width="12"
height="12"
class="align-middle"
/>
</span>
${item.countable
? html`<div class="build-count-chip">
<span class="build-count">${this.count(item)}</span>
</div>`
: ""}
</button>
`;
})}
</div>
`,
)}
${this._selectedUpgradeUnitType
? this.renderAmountPanel()
: this.filteredBuildTable.map(
(row) => html`
<div class="build-row">
${row.map((item) => {
const buildableUnit = this.playerBuildables?.find(
(bu) => bu.type === item.unitType,
);
if (buildableUnit === undefined) {
return html``;
}
const enabled =
buildableUnit.canBuild !== false ||
buildableUnit.canUpgrade !== false;
return html`
<button
class="build-button"
@click=${() =>
this.handleBuildClick(
buildableUnit,
this.clickedTile,
)}
?disabled=${!enabled}
title=${!enabled
? translateText("build_menu.not_enough_money")
: ""}
>
<img
src=${item.icon}
alt="${item.unitType}"
width="40"
height="40"
/>
<span class="build-name"
>${item.key && translateText(item.key)}</span
>
<span class="build-description"
>${item.description &&
translateText(item.description)}</span
>
<span class="build-cost" translate="no">
${renderNumber(
this.game && this.game.myPlayer()
? this.cost(item)
: 0,
)}
<img
src=${goldCoinIcon}
alt="gold"
width="12"
height="12"
class="align-middle"
/>
</span>
${item.countable
? html`<div class="build-count-chip">
<span class="build-count"
>${this.count(item)}</span
>
</div>`
: ""}
</button>
`;
})}
</div>
`,
)}
</div>
`;
}

hideMenu() {
this._hidden = true;
this._selectedUpgradeUnitType = null;
this.requestUpdate();
}

Expand Down
Loading
Loading