From b181c6c15d5ae13d69205cc56d0b6165b01ba97c Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 25 Jun 2025 17:21:54 +0200 Subject: [PATCH] Keybind Ground Attack (#1258) ## Description: - Implement ground attack logic in ClientGameRunner - Move cursor location logic from doBoatAttackUnderCursor to getTileUnderCursor for reusability. - New keybind G for Ground Attack - Add translations for ground attack labels in English ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory* - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [X] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors image *The getTileUnderCursor() and doGroundAttackUnderCursor() methods are private with many dependencies. I did not do isolated unit testing and thus zero tests where added. ## Please put your Discord username so you can be contacted if a bug or regression is found: eng.la --------- Co-authored-by: evanpelle --- resources/lang/en.json | 2 + src/client/ClientGameRunner.ts | 70 ++++++++++++++++++++++++++-------- src/client/InputHandler.ts | 8 ++++ src/client/UserSettingModal.ts | 9 +++++ 4 files changed, 74 insertions(+), 15 deletions(-) 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} > + +
${translateText("user_setting.zoom_controls")}