mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 13:40:46 +00:00
Disable Radial Menu during spawn phase, just left click or tap to pick spawn point (#3611)
## Description: During spawn phase, disable Radial Menu further. Its options where already greyed out or non-responding on purpose, except for the attack button (middle button) which could only be used to select a spawn point but two clicks for that is convoluted. It was mostly a nuisance, especially on mobile where you where forced to go through the Radial Menu, so tap and then tap again to pick a spawn point. - Now, left click directly places a spawn point. Even if "Left click opens menu" is enabled. - And right click does not open Radial menu anymore. Which had no use anyway. And also makes touch screen and mouse players more alike in that they now both have no access to the Radial Menu (which didn't have a purpose anyway except picking spawn point in a convoluted way with two clicks). - On touch screen during spawn phase, the Radial Menu also doesn't open anymore. Instead of two taps (open Radial Menu > tap attack button), now one tap suffices to pick a spawn point just like one left mouse click now does. Fixes https://github.com/openfrontio/OpenFrontIO/issues/3609 Also from UnitLayer > onTouch: - remove redundant isValidRef check. Since isValidCoord check was added in PR 3226 above it, we know it is a correct coord and with that correct ref, already. - remove redundant comment about isValidCoord/Ref not being checked there yet intentionally, because it is actually being checked there since PR 3226. ## 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 ## Please put your Discord username so you can be contacted if a bug or regression is found: tryout33 --------- Co-authored-by: iamlewis <lewismmmm@gmail.com>
This commit is contained in:
@@ -270,7 +270,7 @@ async function createClientGame(
|
||||
clientID,
|
||||
eventBus,
|
||||
gameRenderer,
|
||||
new InputHandler(gameRenderer.uiState, canvas, eventBus),
|
||||
new InputHandler(gameView, gameRenderer.uiState, canvas, eventBus),
|
||||
transport,
|
||||
worker,
|
||||
gameView,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EventBus, GameEvent } from "../core/EventBus";
|
||||
import { PlayerBuildableUnitType, UnitType } from "../core/game/Game";
|
||||
import { UnitView } from "../core/game/GameView";
|
||||
import { GameView, UnitView } from "../core/game/GameView";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { UIState } from "./graphics/UIState";
|
||||
import { Platform } from "./Platform";
|
||||
@@ -177,6 +177,7 @@ export class InputHandler {
|
||||
private readonly userSettings: UserSettings = new UserSettings();
|
||||
|
||||
constructor(
|
||||
private gameView: GameView,
|
||||
public uiState: UIState,
|
||||
private canvas: HTMLCanvasElement,
|
||||
private eventBus: EventBus,
|
||||
@@ -571,7 +572,11 @@ export class InputHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.userSettings.leftClickOpensMenu() || event.shiftKey) {
|
||||
if (
|
||||
!this.userSettings.leftClickOpensMenu() ||
|
||||
event.shiftKey ||
|
||||
this.gameView.inSpawnPhase() // No Radial Menu during spawn phase, only spawn point selection
|
||||
) {
|
||||
this.eventBus.emit(new MouseUpEvent(event.x, event.y));
|
||||
} else {
|
||||
this.eventBus.emit(new ContextMenuEvent(event.clientX, event.clientY));
|
||||
@@ -658,6 +663,9 @@ export class InputHandler {
|
||||
|
||||
private onContextMenu(event: MouseEvent) {
|
||||
event.preventDefault();
|
||||
if (this.gameView.inSpawnPhase()) {
|
||||
return;
|
||||
}
|
||||
if (this.uiState.ghostStructure !== null) {
|
||||
this.setGhostStructure(null);
|
||||
return;
|
||||
|
||||
@@ -167,14 +167,17 @@ export class UnitLayer implements Layer {
|
||||
}
|
||||
|
||||
const clickRef = this.game.ref(cell.x, cell.y);
|
||||
if (!this.game.isOcean(clickRef)) {
|
||||
// No isValidCoord/Ref check yet, that is done for ContextMenuEvent later
|
||||
// No warship to find because no Ocean tile, open Radial Menu
|
||||
this.eventBus.emit(new ContextMenuEvent(event.x, event.y));
|
||||
if (this.game.inSpawnPhase()) {
|
||||
// No Radial Menu during spawn phase, only spawn point selection
|
||||
if (!this.game.isOcean(clickRef)) {
|
||||
this.eventBus.emit(new MouseUpEvent(event.x, event.y));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.game.isValidRef(clickRef)) {
|
||||
if (!this.game.isOcean(clickRef)) {
|
||||
// No warship to find because no Ocean tile, open Radial Menu
|
||||
this.eventBus.emit(new ContextMenuEvent(event.x, event.y));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,17 @@ import {
|
||||
import { UIState } from "../src/client/graphics/UIState";
|
||||
import { EventBus } from "../src/core/EventBus";
|
||||
import { UnitType } from "../src/core/game/Game";
|
||||
import { GameView } from "../src/core/game/GameView";
|
||||
|
||||
class MockPointerEvent {
|
||||
button: number;
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
x: number;
|
||||
y: number;
|
||||
pointerId: number;
|
||||
type: string;
|
||||
pointerType: string;
|
||||
preventDefault: () => void;
|
||||
|
||||
constructor(type: string, init: any) {
|
||||
@@ -20,7 +24,10 @@ class MockPointerEvent {
|
||||
this.button = init.button;
|
||||
this.clientX = init.clientX;
|
||||
this.clientY = init.clientY;
|
||||
this.x = init.x ?? init.clientX;
|
||||
this.y = init.y ?? init.clientY;
|
||||
this.pointerId = init.pointerId;
|
||||
this.pointerType = init.pointerType ?? "mouse";
|
||||
this.preventDefault = vi.fn();
|
||||
}
|
||||
}
|
||||
@@ -29,10 +36,12 @@ global.PointerEvent = MockPointerEvent as any;
|
||||
|
||||
describe("InputHandler AutoUpgrade", () => {
|
||||
let inputHandler: InputHandler;
|
||||
let mockGameView: GameView;
|
||||
let eventBus: EventBus;
|
||||
let mockCanvas: HTMLCanvasElement;
|
||||
|
||||
beforeEach(() => {
|
||||
mockGameView = { inSpawnPhase: () => false } as GameView;
|
||||
mockCanvas = document.createElement("canvas");
|
||||
mockCanvas.width = 800;
|
||||
mockCanvas.height = 600;
|
||||
@@ -40,6 +49,7 @@ describe("InputHandler AutoUpgrade", () => {
|
||||
eventBus = new EventBus();
|
||||
|
||||
inputHandler = new InputHandler(
|
||||
mockGameView,
|
||||
{
|
||||
attackRatio: 20,
|
||||
ghostStructure: null,
|
||||
@@ -218,6 +228,56 @@ describe("InputHandler AutoUpgrade", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Spawn Phase Handling", () => {
|
||||
test("should emit MouseUpEvent and not ContextMenuEvent on left click release during spawn phase", () => {
|
||||
mockGameView.inSpawnPhase = () => true;
|
||||
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||
|
||||
inputHandler["userSettings"].leftClickOpensMenu = () => true;
|
||||
|
||||
const pointerEvent = new PointerEvent("pointerup", {
|
||||
button: 0,
|
||||
clientX: 150,
|
||||
clientY: 250,
|
||||
});
|
||||
inputHandler["lastPointerDownX"] = 149;
|
||||
inputHandler["lastPointerDownY"] = 249;
|
||||
|
||||
inputHandler["onPointerUp"](pointerEvent);
|
||||
|
||||
expect(mockEmit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
x: 150,
|
||||
y: 250,
|
||||
}),
|
||||
);
|
||||
const emittedTypes = mockEmit.mock.calls.map(
|
||||
(call) => call[0].constructor.name,
|
||||
);
|
||||
expect(emittedTypes).toContain("MouseUpEvent");
|
||||
expect(emittedTypes).not.toContain("ContextMenuEvent");
|
||||
});
|
||||
|
||||
test("should suppress/ignore context menu events during spawn phase", () => {
|
||||
mockGameView.inSpawnPhase = () => true;
|
||||
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||
|
||||
const mouseEvent = new MouseEvent("contextmenu", {
|
||||
clientX: 150,
|
||||
clientY: 250,
|
||||
});
|
||||
const preventDefaultSpy = vi.spyOn(mouseEvent, "preventDefault");
|
||||
|
||||
inputHandler["onContextMenu"](mouseEvent);
|
||||
|
||||
expect(preventDefaultSpy).toHaveBeenCalled();
|
||||
const emittedTypes = mockEmit.mock.calls.map(
|
||||
(call) => call[0].constructor.name,
|
||||
);
|
||||
expect(emittedTypes).not.toContain("ContextMenuEvent");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Pointer Event Handling", () => {
|
||||
test("should handle pointer events with different pointer IDs", () => {
|
||||
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||
@@ -481,7 +541,12 @@ describe("InputHandler AutoUpgrade", () => {
|
||||
overlappingRailroads: [],
|
||||
ghostRailPaths: [],
|
||||
} as UIState;
|
||||
inputHandler = new InputHandler(uiState, mockCanvas, eventBus);
|
||||
inputHandler = new InputHandler(
|
||||
mockGameView,
|
||||
uiState,
|
||||
mockCanvas,
|
||||
eventBus,
|
||||
);
|
||||
inputHandler.initialize();
|
||||
});
|
||||
|
||||
@@ -533,7 +598,12 @@ describe("InputHandler AutoUpgrade", () => {
|
||||
overlappingRailroads: [],
|
||||
ghostRailPaths: [],
|
||||
} as UIState;
|
||||
inputHandler = new InputHandler(uiState, mockCanvas, eventBus);
|
||||
inputHandler = new InputHandler(
|
||||
mockGameView,
|
||||
uiState,
|
||||
mockCanvas,
|
||||
eventBus,
|
||||
);
|
||||
inputHandler.initialize();
|
||||
});
|
||||
|
||||
@@ -570,7 +640,12 @@ describe("InputHandler AutoUpgrade", () => {
|
||||
overlappingRailroads: [],
|
||||
ghostRailPaths: [],
|
||||
} as UIState;
|
||||
inputHandler = new InputHandler(uiState, mockCanvas, eventBus);
|
||||
inputHandler = new InputHandler(
|
||||
mockGameView,
|
||||
uiState,
|
||||
mockCanvas,
|
||||
eventBus,
|
||||
);
|
||||
inputHandler.initialize();
|
||||
});
|
||||
|
||||
@@ -590,7 +665,12 @@ describe("InputHandler AutoUpgrade", () => {
|
||||
overlappingRailroads: [],
|
||||
ghostRailPaths: [],
|
||||
} as UIState;
|
||||
inputHandler = new InputHandler(uiState, mockCanvas, eventBus);
|
||||
inputHandler = new InputHandler(
|
||||
mockGameView,
|
||||
uiState,
|
||||
mockCanvas,
|
||||
eventBus,
|
||||
);
|
||||
inputHandler.initialize();
|
||||
|
||||
window.dispatchEvent(
|
||||
@@ -616,7 +696,12 @@ describe("InputHandler AutoUpgrade", () => {
|
||||
overlappingRailroads: [],
|
||||
ghostRailPaths: [],
|
||||
} as UIState;
|
||||
inputHandler = new InputHandler(uiState, mockCanvas, eventBus);
|
||||
inputHandler = new InputHandler(
|
||||
mockGameView,
|
||||
uiState,
|
||||
mockCanvas,
|
||||
eventBus,
|
||||
);
|
||||
inputHandler.initialize();
|
||||
|
||||
window.dispatchEvent(
|
||||
@@ -639,7 +724,12 @@ describe("InputHandler AutoUpgrade", () => {
|
||||
overlappingRailroads: [],
|
||||
ghostRailPaths: [],
|
||||
} as UIState;
|
||||
inputHandler = new InputHandler(uiState, mockCanvas, eventBus);
|
||||
inputHandler = new InputHandler(
|
||||
mockGameView,
|
||||
uiState,
|
||||
mockCanvas,
|
||||
eventBus,
|
||||
);
|
||||
inputHandler.initialize();
|
||||
|
||||
window.dispatchEvent(
|
||||
|
||||
Reference in New Issue
Block a user