From 078619fcc4bcc60662b874e45829ed407d013f90 Mon Sep 17 00:00:00 2001 From: q8gazy Date: Sun, 9 Feb 2025 09:25:01 +0300 Subject: [PATCH 01/14] Update PublicLobby button styling and layout --- src/client/PublicLobby.ts | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/client/PublicLobby.ts b/src/client/PublicLobby.ts index 1d497918b..3185b7a7c 100644 --- a/src/client/PublicLobby.ts +++ b/src/client/PublicLobby.ts @@ -60,6 +60,11 @@ export class PublicLobby extends LitElement { const lobby = this.lobbies[0]; const timeRemaining = Math.max(0, Math.floor(lobby.msUntilStart / 1000)); + // Format time to show minutes and seconds + const minutes = Math.floor(timeRemaining / 60); + const seconds = timeRemaining % 60; + const timeDisplay = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`; + return html` `; From 11a09ce6623689c1ef31e0623ca83002fa44586b Mon Sep 17 00:00:00 2001 From: q8gazy Date: Sun, 9 Feb 2025 12:01:44 +0300 Subject: [PATCH 02/14] new logo --- resources/images/OpenFrontLogo.png | Bin 0 -> 7312 bytes src/client/index.html | 19 ++++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 resources/images/OpenFrontLogo.png diff --git a/resources/images/OpenFrontLogo.png b/resources/images/OpenFrontLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..b19a1dd165d58c39fde29f9c74e05c81bbeedffa GIT binary patch literal 7312 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 K(#4NW0{#d655$K6 literal 0 HcmV?d00001 diff --git a/src/client/index.html b/src/client/index.html index 672d4b307..8b5f65360 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -82,16 +82,17 @@
- OpenFront.io -

+ OpenFront.io +

+
- (v0.15.0) - + v0.15.0 +
Date: Sun, 9 Feb 2025 09:24:11 -0800 Subject: [PATCH 03/14] make bots easier on medium --- src/core/configuration/DefaultConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 1d5a129dc..f210a4450 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -375,7 +375,7 @@ export class DefaultConfig implements Config { difficultyMultiplier = 0.3; break; case Difficulty.Medium: - difficultyMultiplier = 0.6; + difficultyMultiplier = 0.5; break; case Difficulty.Hard: difficultyMultiplier = 1; From 79bbd4def785bac4d5f0a580f643cf1adf9047f8 Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 9 Feb 2025 09:44:37 -0800 Subject: [PATCH 04/14] GameView: filter out inactive units. --- src/core/game/GameView.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/game/GameView.ts b/src/core/game/GameView.ts index bde421c9e..ff1416c7f 100644 --- a/src/core/game/GameView.ts +++ b/src/core/game/GameView.ts @@ -333,10 +333,10 @@ export class GameView implements GameMap { } units(...types: UnitType[]): UnitView[] { if (types.length == 0) { - return Array.from(this._units.values()); + return Array.from(this._units.values()).filter((u) => u.isActive()); } - return Array.from(this._units.values()).filter((u) => - types.includes(u.type()) + return Array.from(this._units.values()).filter( + (u) => u.isActive() && types.includes(u.type()) ); } unit(id: number): UnitView { From 1b6172ee4b79b910ceb068b955f376000eb89d95 Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 9 Feb 2025 10:35:03 -0800 Subject: [PATCH 05/14] bugfix: units not getting removed from screen when getting destroyed --- src/client/graphics/layers/UnitLayer.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 2afc300bd..b6eef957d 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -11,6 +11,7 @@ import { manhattanDistFN, TileRef, } from "../../../core/game/GameMap"; +import { GameUpdateType } from "../../../core/game/GameUpdates"; enum Relationship { Self, @@ -48,9 +49,9 @@ export class UnitLayer implements Layer { if (this.myPlayer == null) { this.myPlayer = this.game.playerByClientID(this.clientID); } - for (const unit of this.game.units()) { - if (unit.wasUpdated()) this.onUnitEvent(unit); - } + this.game.updatesSinceLastTick()?.[GameUpdateType.Unit]?.forEach((unit) => { + this.onUnitEvent(this.game.unit(unit.id)); + }); } init() { @@ -79,9 +80,11 @@ export class UnitLayer implements Layer { this.canvas.width = this.game.width(); this.canvas.height = this.game.height(); - for (const unit of this.game.units()) { - this.onUnitEvent(unit); - } + this.game + ?.updatesSinceLastTick() + ?.[GameUpdateType.Unit]?.forEach((unit) => { + this.onUnitEvent(this.game.unit(unit.id)); + }); } private relationship(unit: UnitView): Relationship { From 8d690c013090795da02cddd0f13436d85882004d Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 9 Feb 2025 10:37:10 -0800 Subject: [PATCH 06/14] NPCs break alliances less frequently --- src/core/execution/FakeHumanExecution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index ef9924265..eb7c7e93f 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -146,9 +146,9 @@ export class FakeHumanExecution implements Execution { private shouldAttack(other: Player): boolean { if (this.player.isAlliedWith(other)) { if (this.shouldDiscourageAttack(other)) { - return this.random.chance(100); + return this.random.chance(200); } - return this.random.chance(20); + return this.random.chance(50); } else { if (this.shouldDiscourageAttack(other)) { return this.random.chance(4); From 963daefdebb88ce69e0298110a7573426fdb4d00 Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 9 Feb 2025 11:16:08 -0800 Subject: [PATCH 07/14] allow brackets in names --- src/client/graphics/layers/NameLayer.ts | 38 +++++++++-------------- src/core/Schemas.ts | 4 +-- src/core/Util.ts | 40 ++++++++++++------------- src/core/validations/username.ts | 2 +- 4 files changed, 37 insertions(+), 47 deletions(-) diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts index 45ff67ed6..2161ff4be 100644 --- a/src/client/graphics/layers/NameLayer.ts +++ b/src/client/graphics/layers/NameLayer.ts @@ -25,7 +25,7 @@ class RenderInfo { public lastRenderCalc: number, public location: Cell, public fontSize: number, - public element: HTMLElement, + public element: HTMLElement ) {} } @@ -49,7 +49,7 @@ export class NameLayer implements Layer { private game: GameView, private theme: Theme, private transformHandler: TransformHandler, - private clientID: ClientID, + private clientID: ClientID ) { this.traitorIconImage = new Image(); this.traitorIconImage.src = traitorIcon; @@ -100,13 +100,7 @@ export class NameLayer implements Layer { if (!this.seenPlayers.has(player)) { this.seenPlayers.add(player); this.renders.push( - new RenderInfo( - player, - 0, - null, - 0, - this.createPlayerElement(player), - ), + new RenderInfo(player, 0, null, 0, this.createPlayerElement(player)) ); } } @@ -115,11 +109,11 @@ export class NameLayer implements Layer { public renderLayer(mainContex: CanvasRenderingContext2D) { const screenPosOld = this.transformHandler.worldToScreenCoordinates( - new Cell(0, 0), + new Cell(0, 0) ); const screenPos = new Cell( screenPosOld.x - window.innerWidth / 2, - screenPosOld.y - window.innerHeight / 2, + screenPosOld.y - window.innerHeight / 2 ); this.container.style.transform = `translate(${screenPos.x}px, ${screenPos.y}px) scale(${this.transformHandler.scale})`; @@ -136,7 +130,7 @@ export class NameLayer implements Layer { 0, 0, mainContex.canvas.width, - mainContex.canvas.height, + mainContex.canvas.height ); } @@ -191,7 +185,7 @@ export class NameLayer implements Layer { const oldLocation = render.location; render.location = new Cell( render.player.nameLocation().x, - render.player.nameLocation().y, + render.player.nameLocation().y ); // Calculate base size and scale @@ -230,7 +224,7 @@ export class NameLayer implements Layer { if (render.player === this.firstPlace) { if (!existingCrown) { iconsDiv.appendChild( - this.createIconElement(this.crownIconImage.src, iconSize, "crown"), + this.createIconElement(this.crownIconImage.src, iconSize, "crown") ); } } else if (existingCrown) { @@ -242,11 +236,7 @@ export class NameLayer implements Layer { if (render.player.isTraitor()) { if (!existingTraitor) { iconsDiv.appendChild( - this.createIconElement( - this.traitorIconImage.src, - iconSize, - "traitor", - ), + this.createIconElement(this.traitorIconImage.src, iconSize, "traitor") ); } } else if (existingTraitor) { @@ -261,8 +251,8 @@ export class NameLayer implements Layer { this.createIconElement( this.allianceIconImage.src, iconSize, - "alliance", - ), + "alliance" + ) ); } } else if (existingAlliance) { @@ -277,7 +267,7 @@ export class NameLayer implements Layer { ) { if (!existingTarget) { iconsDiv.appendChild( - this.createIconElement(this.targetIconImage.src, iconSize, "target"), + this.createIconElement(this.targetIconImage.src, iconSize, "target") ); } } else if (existingTarget) { @@ -291,7 +281,7 @@ export class NameLayer implements Layer { .filter( (emoji) => emoji.recipientID == AllPlayers || - emoji.recipientID == myPlayer?.smallID(), + emoji.recipientID == myPlayer?.smallID() ); if (emojis.length > 0) { @@ -324,7 +314,7 @@ export class NameLayer implements Layer { private createIconElement( src: string, size: number, - id: string, + id: string ): HTMLImageElement { const icon = document.createElement("img"); icon.src = src; diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 9abd1f3e4..c5ad90aca 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -96,7 +96,7 @@ const GameConfigSchema = z.object({ const SafeString = z .string() // Remove common dangerous characters and patterns - .regex(/^[a-zA-Z0-9\s.,!?@#$%&*()-_+=[\]{}|;:"'\/]+$/) + .regex(/^[a-zA-Z0-9\s.,!?@#$%&*()-_+=\[\]{}|;:"'\/]+$/) // Reasonable max length to prevent DOS .max(1000); @@ -106,7 +106,7 @@ const EmojiSchema = z.string().refine( }, { message: "Must contain at least one emoji character", - }, + } ); const ID = z .string() diff --git a/src/core/Util.ts b/src/core/Util.ts index 109c9d770..a31bdb7f5 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -16,7 +16,7 @@ import { andFN, GameMap, manhattanDistFN, TileRef } from "./game/GameMap"; export function manhattanDistWrapped( c1: Cell, c2: Cell, - width: number, + width: number ): number { // Calculate x distance let dx = Math.abs(c1.x - c2.x); @@ -36,7 +36,7 @@ export function within(value: number, min: number, max: number): number { export function distSort( gm: GameMap, - target: TileRef, + target: TileRef ): (a: TileRef, b: TileRef) => number { return (a: TileRef, b: TileRef) => { return gm.manhattanDist(a, target) - gm.manhattanDist(b, target); @@ -45,7 +45,7 @@ export function distSort( export function distSortUnit( gm: GameMap, - target: Unit | TileRef, + target: Unit | TileRef ): (a: Unit, b: Unit) => number { const targetRef = typeof target === "number" ? target : target.tile(); @@ -61,7 +61,7 @@ export function distSortUnit( export function sourceDstOceanShore( gm: Game, src: Player, - tile: TileRef, + tile: TileRef ): [TileRef | null, TileRef | null] { const dst = gm.owner(tile); let srcTile = closestOceanShoreFromPlayer(gm, src, tile); @@ -88,10 +88,10 @@ export function targetTransportTile(gm: Game, tile: TileRef): TileRef | null { export function closestOceanShoreFromPlayer( gm: GameMap, player: Player, - target: TileRef, + target: TileRef ): TileRef | null { const shoreTiles = Array.from(player.borderTiles()).filter((t) => - gm.isOceanShore(t), + gm.isOceanShore(t) ); if (shoreTiles.length == 0) { return null; @@ -101,12 +101,12 @@ export function closestOceanShoreFromPlayer( const closestDistance = manhattanDistWrapped( gm.cell(target), gm.cell(closest), - gm.width(), + gm.width() ); const currentDistance = manhattanDistWrapped( gm.cell(target), gm.cell(current), - gm.width(), + gm.width() ); return currentDistance < closestDistance ? current : closest; }); @@ -115,13 +115,13 @@ export function closestOceanShoreFromPlayer( function closestOceanShoreTN( gm: GameMap, tile: TileRef, - searchDist: number, + searchDist: number ): TileRef { const tn = Array.from( gm.bfs( tile, - andFN((_, t) => !gm.hasOwner(t), manhattanDistFN(tile, searchDist)), - ), + andFN((_, t) => !gm.hasOwner(t), manhattanDistFN(tile, searchDist)) + ) ) .filter((t) => gm.isOceanShore(t)) .sort((a, b) => gm.manhattanDist(tile, a) - gm.manhattanDist(tile, b)); @@ -143,7 +143,7 @@ export function simpleHash(str: string): number { export function calculateBoundingBox( gm: GameMap, - borderTiles: ReadonlySet, + borderTiles: ReadonlySet ): { min: Cell; max: Cell } { let minX = Infinity, minY = Infinity, @@ -163,18 +163,18 @@ export function calculateBoundingBox( export function calculateBoundingBoxCenter( gm: GameMap, - borderTiles: ReadonlySet, + borderTiles: ReadonlySet ): Cell { const { min, max } = calculateBoundingBox(gm, borderTiles); return new Cell( min.x + Math.floor((max.x - min.x) / 2), - min.y + Math.floor((max.y - min.y) / 2), + min.y + Math.floor((max.y - min.y) / 2) ); } export function inscribed( outer: { min: Cell; max: Cell }, - inner: { min: Cell; max: Cell }, + inner: { min: Cell; max: Cell } ): boolean { return ( outer.min.x <= inner.min.x && @@ -208,7 +208,7 @@ export function getMode(list: Set): number { export function sanitize(name: string): string { return Array.from(name) .join("") - .replace(/[^\p{L}\p{N}\s\p{Emoji}\p{Emoji_Component}]/gu, ""); + .replace(/[^\p{L}\p{N}\s\p{Emoji}\p{Emoji_Component}\[\]]/gu, ""); } export function processName(name: string): string { @@ -238,7 +238,7 @@ export function processName(name: string): string { // Add CSS for the emoji images const withEmojiStyles = styledHTML.replace( / Date: Sun, 9 Feb 2025 12:14:53 -0800 Subject: [PATCH 08/14] can only send MIRV to player owned land --- src/core/game/PlayerImpl.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 2407dbef1..921008f5c 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -569,6 +569,10 @@ export class PlayerImpl implements Player { } switch (unitType) { case UnitType.MIRV: + if (!this.mg.hasOwner(targetTile)) { + return false; + } + return this.nukeSpawn(targetTile); case UnitType.AtomBomb: case UnitType.HydrogenBomb: return this.nukeSpawn(targetTile); From 6ff4e10a7e2421b2af2f7725e15ef1b7892740f0 Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 9 Feb 2025 12:29:13 -0800 Subject: [PATCH 09/14] public games have random map --- src/server/GameManager.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/server/GameManager.ts b/src/server/GameManager.ts index 2cfcf4cf1..b4ff3f7c0 100644 --- a/src/server/GameManager.ts +++ b/src/server/GameManager.ts @@ -5,12 +5,15 @@ import { Client } from "./Client"; import { GamePhase, GameServer } from "./GameServer"; import { Difficulty, GameMapType, GameType } from "../core/game/Game"; import { generateID } from "../core/Util"; +import { PseudoRandom } from "../core/PseudoRandom"; export class GameManager { private lastNewLobby: number = 0; private games: GameServer[] = []; + private random = new PseudoRandom(123); + constructor(private config: ServerConfig) {} public game(id: GameID): GameServer | null { @@ -46,7 +49,7 @@ export class GameManager { gameMap: GameMapType.World, gameType: GameType.Private, difficulty: Difficulty.Medium, - }), + }) ); return id; } @@ -54,7 +57,7 @@ export class GameManager { hasActiveGame(gameID: GameID): boolean { const game = this.games .filter( - (g) => g.phase() == GamePhase.Lobby || g.phase() == GamePhase.Active, + (g) => g.phase() == GamePhase.Lobby || g.phase() == GamePhase.Active ) .find((g) => g.id == gameID); return game != null; @@ -81,10 +84,10 @@ export class GameManager { this.lastNewLobby = now; lobbies.push( new GameServer(generateID(), now, true, this.config, { - gameMap: GameMapType.World, + gameMap: this.random.randElement(Object.values(GameMapType)), gameType: GameType.Public, difficulty: Difficulty.Medium, - }), + }) ); } From b3498976fb9707e6a653cb7dfb36dccfa586266a Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 9 Feb 2025 13:15:06 -0800 Subject: [PATCH 10/14] show game map name in public lobby --- src/client/PublicLobby.ts | 11 ++++++++--- src/core/Schemas.ts | 1 + src/server/GameServer.ts | 2 +- src/server/Server.ts | 1 + 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/client/PublicLobby.ts b/src/client/PublicLobby.ts index 3185b7a7c..57fe25289 100644 --- a/src/client/PublicLobby.ts +++ b/src/client/PublicLobby.ts @@ -20,7 +20,7 @@ export class PublicLobby extends LitElement { this.fetchAndUpdateLobbies(); this.lobbiesInterval = window.setInterval( () => this.fetchAndUpdateLobbies(), - 1000, + 1000 ); } @@ -74,6 +74,11 @@ export class PublicLobby extends LitElement { >
Next Game
+
+
+ ${lobby.gameConfig.gameMap} +
+
${lobby.numClients} @@ -106,7 +111,7 @@ export class PublicLobby extends LitElement { }, bubbles: true, composed: true, - }), + }) ); } else { this.dispatchEvent( @@ -114,7 +119,7 @@ export class PublicLobby extends LitElement { detail: { lobby: this.currLobby }, bubbles: true, composed: true, - }), + }) ); this.currLobby = null; } diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index c5ad90aca..1c402ccf9 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -85,6 +85,7 @@ export interface Lobby { id: string; msUntilStart?: number; numClients?: number; + gameConfig?: GameConfig; } const GameConfigSchema = z.object({ diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index 03af445ee..df12a0013 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -47,7 +47,7 @@ export class GameServer { public readonly createdAt: number, public readonly isPublic: boolean, private config: ServerConfig, - private gameConfig: GameConfig, + public gameConfig: GameConfig, ) {} public updateGameConfig(gameConfig: GameConfig): void { diff --git a/src/server/Server.ts b/src/server/Server.ts index 583a5b60e..baa8030fc 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -195,6 +195,7 @@ function updateLobbies() { id: g.id, msUntilStart: g.startTime() - Date.now(), numClients: g.numClients(), + gameConfig: g.gameConfig, })) .sort((a, b) => a.msUntilStart - b.msUntilStart), }); From ca2b6127782ab1d6ceb403bfe0e2699e70d20cd3 Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 9 Feb 2025 13:18:10 -0800 Subject: [PATCH 11/14] give warship larger attack radius --- src/core/execution/WarshipExecution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/execution/WarshipExecution.ts b/src/core/execution/WarshipExecution.ts index 810d1ae48..9cd1d40a9 100644 --- a/src/core/execution/WarshipExecution.ts +++ b/src/core/execution/WarshipExecution.ts @@ -68,7 +68,7 @@ export class WarshipExecution implements Execution { const ships = this.mg .units(UnitType.TransportShip, UnitType.Warship, UnitType.TradeShip) .filter( - (u) => this.mg.manhattanDist(u.tile(), this.warship.tile()) < 100 + (u) => this.mg.manhattanDist(u.tile(), this.warship.tile()) < 130 ) .filter((u) => u.owner() != this.warship.owner()) .filter((u) => u != this.warship) From 2c77d5a1528ddac26a4be362d4d4a1c97d636b59 Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 9 Feb 2025 13:44:52 -0800 Subject: [PATCH 12/14] fix events display + control panel on mobile --- src/client/index.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/index.html b/src/client/index.html index 8b5f65360..2b6513aeb 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -190,15 +190,15 @@ class="bottom-0 w-full flex-col-reverse sm:flex-row z-50" style="position: fixed; pointer-events: none" > -
- -
+
+ +
From 921509a23b58ca70905e2af3c1b7daf7355f6ab6 Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 9 Feb 2025 13:51:29 -0800 Subject: [PATCH 13/14] fix sliders not getting input bug --- src/client/graphics/layers/ControlPanel.ts | 104 ++++++++++++--------- 1 file changed, 61 insertions(+), 43 deletions(-) diff --git a/src/client/graphics/layers/ControlPanel.ts b/src/client/graphics/layers/ControlPanel.ts index 596ece856..7e083e183 100644 --- a/src/client/graphics/layers/ControlPanel.ts +++ b/src/client/graphics/layers/ControlPanel.ts @@ -135,62 +135,80 @@ export class ControlPanel extends LitElement implements Layer {
-
+
-
-
- { - this.targetTroopRatio = - parseInt((e.target as HTMLInputElement).value) / 100; - this.onTroopChange(this.targetTroopRatio); - }} - class="absolute w-full top-3 m-0 opacity-0 cursor-pointer" - /> +
+ +
+ +
+ + { + this.targetTroopRatio = + parseInt((e.target as HTMLInputElement).value) / 100; + this.onTroopChange(this.targetTroopRatio); + }} + class="absolute left-0 right-0 top-2 m-0 h-4 opacity-0 cursor-pointer" + /> + +
+
-
+
-
-
-
- { - this.attackRatio = - parseInt((e.target as HTMLInputElement).value) / 100; - this.onAttackRatioChange(this.attackRatio); - }} - class="absolute w-full top-3 m-0 opacity-0 cursor-pointer" - /> +
+ +
+ +
+ + { + this.attackRatio = + parseInt((e.target as HTMLInputElement).value) / 100; + this.onAttackRatioChange(this.attackRatio); + }} + class="absolute left-0 right-0 top-2 m-0 h-4 opacity-0 cursor-pointer" + /> + +
+
`; } + createRenderRoot() { return this; // Disable shadow DOM to allow Tailwind styles } From f3a9378a5faffc728af7fbd6ee7b28d7b4236a67 Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 9 Feb 2025 14:20:39 -0800 Subject: [PATCH 14/14] google ads script --- src/client/index.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/client/index.html b/src/client/index.html index 2b6513aeb..ddbd035b4 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -74,6 +74,11 @@ gtag("js", new Date()); gtag("config", "AW-16702609763"); +