diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 58b298c13..e2b0e15f9 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -15,6 +15,7 @@ import { EventsDisplay } from "./layers/EventsDisplay"; import { FxLayer } from "./layers/FxLayer"; import { Layer } from "./layers/Layer"; import { Leaderboard } from "./layers/Leaderboard"; +import { LeftInGameAd } from "./layers/LeftInGameAd"; import { MultiTabModal } from "./layers/MultiTabModal"; import { NameLayer } from "./layers/NameLayer"; import { OptionsMenu } from "./layers/OptionsMenu"; @@ -171,6 +172,14 @@ export function createRenderer( } playerTeamLabel.game = game; + const leftInGameAd = document.querySelector( + "left-in-game-ad", + ) as LeftInGameAd; + if (!(leftInGameAd instanceof LeftInGameAd)) { + console.error("left in game ad not found"); + } + leftInGameAd.g = game; + const layers: Layer[] = [ new TerrainLayer(game, transformHandler), new TerritoryLayer(game, eventBus), @@ -204,6 +213,7 @@ export function createRenderer( playerPanel, playerTeamLabel, multiTabModal, + leftInGameAd, ]; return new GameRenderer( diff --git a/src/client/graphics/layers/LeftInGameAd.ts b/src/client/graphics/layers/LeftInGameAd.ts new file mode 100644 index 000000000..602fcb0dd --- /dev/null +++ b/src/client/graphics/layers/LeftInGameAd.ts @@ -0,0 +1,111 @@ +import { LitElement, css, html } from "lit"; +import { customElement, state } from "lit/decorators.js"; +import { GameView } from "../../../core/game/GameView"; +import { Layer } from "./Layer"; + +declare global { + interface Window { + aiptag: { + cmd: { + display: { + push: (callback: () => void) => void; + }; + }; + }; + aipDisplayTag: { + display: (id: string) => void; + }; + } +} + +const BREAKPOINT = { + width: 1000, + height: 800, +}; + +const AD_SIZE = "openfront-io_300x250_ingame"; + +@customElement("left-in-game-ad") +export class LeftInGameAd extends LitElement implements Layer { + public g: GameView; + + @state() + private isVisible: boolean = false; + + // Override createRenderRoot to disable shadow DOM + createRenderRoot() { + return this; + } + + static styles = css` + .ad-container { + position: fixed; + left: 0; + top: 50%; + transform: translateY(-50%); + z-index: 9000; + pointer-events: auto; + } + `; + + constructor() { + super(); + } + + public show(): void { + this.isVisible = true; + this.requestUpdate(); + // Refresh the ad when showing + setTimeout(() => this.refreshAd(), 100); + } + + public hide(): void { + this.isVisible = false; + this.requestUpdate(); + } + + public async tick() { + if (!this.isVisible && !this.g.inSpawnPhase() && this.screenLargeEnough()) { + console.log("showing left in game ad"); + this.show(); + } + if (this.isVisible && !this.screenLargeEnough()) { + console.log("hiding left in game ad"); + this.hide(); + } + } + + private screenLargeEnough(): boolean { + return ( + window.innerWidth > BREAKPOINT.width && + window.innerHeight > BREAKPOINT.height + ); + } + + private refreshAd(): void { + if (window.aiptag && window.aiptag.cmd && window.aiptag.cmd.display) { + window.aiptag.cmd.display.push( + function () { + if (window.aipDisplayTag) { + window.aipDisplayTag.display(AD_SIZE); + } + }.bind(this), + ); + } + } + + render() { + if (!this.isVisible) { + return html``; + } + + return html` +