mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 10:18:11 +00:00
Merge branch 'main' into embeddedurlfix
This commit is contained in:
@@ -305,6 +305,7 @@ export function createRenderer(
|
||||
|
||||
export class GameRenderer {
|
||||
private context: CanvasRenderingContext2D;
|
||||
private layerTickState = new Map<Layer, { lastTickAtMs: number }>();
|
||||
|
||||
constructor(
|
||||
private game: GameView,
|
||||
@@ -416,7 +417,28 @@ export class GameRenderer {
|
||||
}
|
||||
|
||||
tick() {
|
||||
this.layers.forEach((l) => l.tick?.());
|
||||
const nowMs = performance.now();
|
||||
|
||||
for (const layer of this.layers) {
|
||||
if (!layer.tick) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const state = this.layerTickState.get(layer) ?? {
|
||||
lastTickAtMs: -Infinity,
|
||||
};
|
||||
|
||||
const intervalMs = layer.getTickIntervalMs?.() ?? 0;
|
||||
if (intervalMs > 0 && nowMs - state.lastTickAtMs < intervalMs) {
|
||||
this.layerTickState.set(layer, state);
|
||||
continue;
|
||||
}
|
||||
|
||||
state.lastTickAtMs = nowMs;
|
||||
this.layerTickState.set(layer, state);
|
||||
|
||||
layer.tick();
|
||||
}
|
||||
}
|
||||
|
||||
resize(width: number, height: number): void {
|
||||
|
||||
@@ -39,6 +39,10 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
|
||||
private _lastTroopIncreaseRate: number;
|
||||
|
||||
getTickIntervalMs() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.attackRatio = Number(
|
||||
localStorage.getItem("settings.attackRatio") ?? "0.2",
|
||||
@@ -81,9 +85,7 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.game.ticks() % 5 === 0) {
|
||||
this.updateTroopIncrease();
|
||||
}
|
||||
this.updateTroopIncrease();
|
||||
|
||||
this._maxTroops = this.game.config().maxTroops(player);
|
||||
this._gold = player.gold();
|
||||
|
||||
@@ -2,10 +2,9 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameType } from "../../../core/game/Game";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { crazyGamesSDK } from "../../CrazyGamesSDK";
|
||||
import { PauseGameIntentEvent } from "../../Transport";
|
||||
import { PauseGameIntentEvent, SendWinnerEvent } from "../../Transport";
|
||||
import { translateText } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
import { ShowReplayPanelEvent } from "./ReplayPanel";
|
||||
@@ -50,16 +49,20 @@ export class GameRightSidebar extends LitElement implements Layer {
|
||||
this._isVisible = true;
|
||||
this.game.inSpawnPhase();
|
||||
|
||||
this.eventBus.on(SendWinnerEvent, () => {
|
||||
this.hasWinner = true;
|
||||
this.requestUpdate();
|
||||
});
|
||||
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
getTickIntervalMs() {
|
||||
return 250;
|
||||
}
|
||||
|
||||
tick() {
|
||||
// Timer logic
|
||||
const updates = this.game.updatesSinceLastTick();
|
||||
if (updates) {
|
||||
this.hasWinner = this.hasWinner || updates[GameUpdateType.Win].length > 0;
|
||||
}
|
||||
|
||||
// Check if the player is the lobby creator
|
||||
if (!this.isLobbyCreator && this.game.myPlayer()?.isLobbyCreator()) {
|
||||
this.isLobbyCreator = true;
|
||||
@@ -67,18 +70,24 @@ export class GameRightSidebar extends LitElement implements Layer {
|
||||
}
|
||||
|
||||
const maxTimerValue = this.game.config().gameConfig().maxTimerValue;
|
||||
const spawnPhaseTurns = this.game.config().numSpawnPhaseTurns();
|
||||
const ticks = this.game.ticks();
|
||||
const gameTicks = Math.max(0, ticks - spawnPhaseTurns);
|
||||
const elapsedSeconds = Math.floor(gameTicks / 10); // 10 ticks per second
|
||||
|
||||
if (this.game.inSpawnPhase()) {
|
||||
this.timer = maxTimerValue !== undefined ? maxTimerValue * 60 : 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.hasWinner) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maxTimerValue !== undefined) {
|
||||
if (this.game.inSpawnPhase()) {
|
||||
this.timer = maxTimerValue * 60;
|
||||
} else if (!this.hasWinner && this.game.ticks() % 10 === 0) {
|
||||
this.timer = Math.max(0, this.timer - 1);
|
||||
}
|
||||
this.timer = Math.max(0, maxTimerValue * 60 - elapsedSeconds);
|
||||
} else {
|
||||
if (this.game.inSpawnPhase()) {
|
||||
this.timer = 0;
|
||||
} else if (!this.hasWinner && this.game.ticks() % 10 === 0) {
|
||||
this.timer++;
|
||||
}
|
||||
this.timer = elapsedSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
export interface Layer {
|
||||
init?: () => void;
|
||||
tick?: () => void;
|
||||
// Optional hint to throttle expensive ticks by wall-clock.
|
||||
// If omitted or <= 0, the layer ticks whenever GameRenderer ticks.
|
||||
getTickIntervalMs?: () => number;
|
||||
renderLayer?: (context: CanvasRenderingContext2D) => void;
|
||||
shouldTransform?: () => boolean;
|
||||
redraw?: () => void;
|
||||
|
||||
@@ -55,12 +55,14 @@ export class Leaderboard extends LitElement implements Layer {
|
||||
|
||||
init() {}
|
||||
|
||||
getTickIntervalMs() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (this.game === null) throw new Error("Not initialized");
|
||||
if (!this.visible) return;
|
||||
if (this.game.ticks() % 10 === 0) {
|
||||
this.updateLeaderboard();
|
||||
}
|
||||
this.updateLeaderboard();
|
||||
}
|
||||
|
||||
private setSort(key: "tiles" | "gold" | "maxtroops") {
|
||||
|
||||
@@ -33,6 +33,10 @@ export class MainRadialMenu extends LitElement implements Layer {
|
||||
|
||||
private clickedTile: TileRef | null = null;
|
||||
|
||||
getTickIntervalMs() {
|
||||
return 500;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private eventBus: EventBus,
|
||||
private game: GameView,
|
||||
@@ -156,18 +160,16 @@ export class MainRadialMenu extends LitElement implements Layer {
|
||||
|
||||
async tick() {
|
||||
if (!this.radialMenu.isMenuVisible() || this.clickedTile === null) return;
|
||||
if (this.game.ticks() % 5 === 0) {
|
||||
this.game
|
||||
.myPlayer()!
|
||||
.actions(this.clickedTile)
|
||||
.then((actions) => {
|
||||
this.updatePlayerActions(
|
||||
this.game.myPlayer()!,
|
||||
actions,
|
||||
this.clickedTile!,
|
||||
);
|
||||
});
|
||||
}
|
||||
this.game
|
||||
.myPlayer()!
|
||||
.actions(this.clickedTile)
|
||||
.then((actions) => {
|
||||
this.updatePlayerActions(
|
||||
this.game.myPlayer()!,
|
||||
actions,
|
||||
this.clickedTile!,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
renderLayer(context: CanvasRenderingContext2D) {
|
||||
|
||||
@@ -133,11 +133,11 @@ export class NameLayer implements Layer {
|
||||
}
|
||||
}
|
||||
|
||||
public tick() {
|
||||
if (this.game.ticks() % 10 !== 0) {
|
||||
return;
|
||||
}
|
||||
getTickIntervalMs() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
public tick() {
|
||||
// Precompute the first-place player for performance
|
||||
this.firstPlace = getFirstPlacePlayer(this.game);
|
||||
|
||||
|
||||
@@ -44,11 +44,13 @@ export class ReplayPanel extends LitElement implements Layer {
|
||||
}
|
||||
}
|
||||
|
||||
getTickIntervalMs() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (!this.visible) return;
|
||||
if (this.game!.ticks() % 10 === 0) {
|
||||
this.requestUpdate();
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
onReplaySpeedChange(value: ReplaySpeedMultiplier) {
|
||||
|
||||
@@ -42,6 +42,10 @@ export class TeamStats extends LitElement implements Layer {
|
||||
|
||||
init() {}
|
||||
|
||||
getTickIntervalMs() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (this.game.config().gameConfig().gameMode !== GameMode.Team) return;
|
||||
|
||||
@@ -52,9 +56,7 @@ export class TeamStats extends LitElement implements Layer {
|
||||
|
||||
if (!this.visible) return;
|
||||
|
||||
if (this.game.ticks() % 10 === 0) {
|
||||
this.updateTeamStats();
|
||||
}
|
||||
this.updateTeamStats();
|
||||
}
|
||||
|
||||
private updateTeamStats() {
|
||||
|
||||
@@ -40,6 +40,10 @@ describe("InputHandler AutoUpgrade", () => {
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
inputHandler.destroy();
|
||||
});
|
||||
|
||||
describe("Middle Mouse Button Handling", () => {
|
||||
test("should emit AutoUpgradeEvent on middle mouse button press", () => {
|
||||
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||
|
||||
Reference in New Issue
Block a user