diff --git a/resources/lang/en.json b/resources/lang/en.json
index 5fb5babfc..859a18838 100644
--- a/resources/lang/en.json
+++ b/resources/lang/en.json
@@ -269,6 +269,8 @@
"attack_keybinds": "Attack Keybinds",
"boat_attack": "Boat Attack",
"boat_attack_desc": "Send a boat attack to the tile under your cursor.",
+ "ground_attack": "Ground Attack",
+ "ground_attack_desc": "Send a ground attack to the tile under your cursor.",
"zoom_controls": "Zoom Controls",
"zoom_out": "Zoom Out",
"zoom_out_desc": "Zoom out the map",
diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts
index 1df60c2f9..0614732cd 100644
--- a/src/client/ClientGameRunner.ts
+++ b/src/client/ClientGameRunner.ts
@@ -27,6 +27,7 @@ import { UserSettings } from "../core/game/UserSettings";
import { WorkerClient } from "../core/worker/WorkerClient";
import {
DoBoatAttackEvent,
+ DoGroundAttackEvent,
InputHandler,
MouseMoveEvent,
MouseUpEvent,
@@ -246,9 +247,16 @@ export class ClientGameRunner {
1000,
);
}, 20000);
- this.eventBus.on(MouseUpEvent, (e) => this.inputEvent(e));
- this.eventBus.on(MouseMoveEvent, (e) => this.onMouseMove(e));
- this.eventBus.on(DoBoatAttackEvent, (e) => this.doBoatAttackUnderCursor());
+ this.eventBus.on(MouseUpEvent, this.inputEvent.bind(this));
+ this.eventBus.on(MouseMoveEvent, this.onMouseMove.bind(this));
+ this.eventBus.on(
+ DoBoatAttackEvent,
+ this.doBoatAttackUnderCursor.bind(this),
+ );
+ this.eventBus.on(
+ DoGroundAttackEvent,
+ this.doGroundAttackUnderCursor.bind(this),
+ );
this.renderer.initialize();
this.input.initialize();
@@ -418,20 +426,10 @@ export class ClientGameRunner {
}
private doBoatAttackUnderCursor(): void {
- if (!this.isActive || !this.lastMousePosition) {
+ const tile = this.getTileUnderCursor();
+ if (tile === null) {
return;
}
- if (this.gameView.inSpawnPhase()) {
- return;
- }
- const cell = this.renderer.transformHandler.screenToWorldCoordinates(
- this.lastMousePosition.x,
- this.lastMousePosition.y,
- );
- if (!this.gameView.isValidCoord(cell.x, cell.y)) {
- return;
- }
- const tile = this.gameView.ref(cell.x, cell.y);
if (this.myPlayer === null) {
const myPlayer = this.gameView.playerByClientID(this.lobby.clientID);
@@ -446,6 +444,48 @@ export class ClientGameRunner {
});
}
+ private doGroundAttackUnderCursor(): void {
+ const tile = this.getTileUnderCursor();
+ if (tile === null) {
+ return;
+ }
+
+ if (this.myPlayer === null) {
+ const myPlayer = this.gameView.playerByClientID(this.lobby.clientID);
+ if (myPlayer === null) return;
+ this.myPlayer = myPlayer;
+ }
+
+ this.myPlayer.actions(tile).then((actions) => {
+ if (this.myPlayer === null) return;
+ if (actions.canAttack) {
+ this.eventBus.emit(
+ new SendAttackIntentEvent(
+ this.gameView.owner(tile).id(),
+ this.myPlayer.troops() * this.renderer.uiState.attackRatio,
+ ),
+ );
+ }
+ });
+ }
+
+ private getTileUnderCursor(): TileRef | null {
+ if (!this.isActive || !this.lastMousePosition) {
+ return null;
+ }
+ if (this.gameView.inSpawnPhase()) {
+ return null;
+ }
+ const cell = this.renderer.transformHandler.screenToWorldCoordinates(
+ this.lastMousePosition.x,
+ this.lastMousePosition.y,
+ );
+ if (!this.gameView.isValidCoord(cell.x, cell.y)) {
+ return null;
+ }
+ return this.gameView.ref(cell.x, cell.y);
+ }
+
private canBoatAttack(actions: PlayerActions, tile: TileRef): boolean {
const bu = actions.buildableUnits.find(
(bu) => bu.type === UnitType.TransportShip,
diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts
index 96fde5beb..367676364 100644
--- a/src/client/InputHandler.ts
+++ b/src/client/InputHandler.ts
@@ -79,6 +79,8 @@ export class ShowEmojiMenuEvent implements GameEvent {
export class DoBoatAttackEvent implements GameEvent {}
+export class DoGroundAttackEvent implements GameEvent {}
+
export class AttackRatioEvent implements GameEvent {
constructor(public readonly attackRatio: number) {}
}
@@ -133,6 +135,7 @@ export class InputHandler {
attackRatioDown: "Digit1",
attackRatioUp: "Digit2",
boatAttack: "KeyB",
+ groundAttack: "KeyG",
modifierKey: "ControlLeft",
altKey: "AltLeft",
...JSON.parse(localStorage.getItem("settings.keybinds") ?? "{}"),
@@ -265,6 +268,11 @@ export class InputHandler {
this.eventBus.emit(new DoBoatAttackEvent());
}
+ if (e.code === this.keybinds.groundAttack) {
+ e.preventDefault();
+ this.eventBus.emit(new DoGroundAttackEvent());
+ }
+
if (e.code === this.keybinds.attackRatioDown) {
e.preventDefault();
this.eventBus.emit(new AttackRatioEvent(-10));
diff --git a/src/client/UserSettingModal.ts b/src/client/UserSettingModal.ts
index b31a4dd5e..3278b5994 100644
--- a/src/client/UserSettingModal.ts
+++ b/src/client/UserSettingModal.ts
@@ -416,6 +416,15 @@ export class UserSettingModal extends LitElement {
@change=${this.handleKeybindChange}
>
+