mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-18 23:22:44 +00:00
update header ads (#2266)
## Description: 1. Remove SpawnAds and replace it with AdTimer which will delete the in-game ad after the first minute. 2. remove login blocker UI, we don't use it anymore 3. convert TerritoryPatternsModal & GutterAds to use event based when checking for flares 4. remove window.PageOS.session.newPageView(); because it was throwing an exception 5. Convert SpawnTimer to a lit element to give it a higher z-index to stay above the header ad ## 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: evan
This commit is contained in:
@@ -5,6 +5,7 @@ import { GameStartingModal } from "../GameStartingModal";
|
||||
import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler";
|
||||
import { TransformHandler } from "./TransformHandler";
|
||||
import { UIState } from "./UIState";
|
||||
import { AdTimer } from "./layers/AdTimer";
|
||||
import { AlertFrame } from "./layers/AlertFrame";
|
||||
import { BuildMenu } from "./layers/BuildMenu";
|
||||
import { ChatDisplay } from "./layers/ChatDisplay";
|
||||
@@ -27,7 +28,6 @@ import { PlayerPanel } from "./layers/PlayerPanel";
|
||||
import { RailroadLayer } from "./layers/RailroadLayer";
|
||||
import { ReplayPanel } from "./layers/ReplayPanel";
|
||||
import { SettingsModal } from "./layers/SettingsModal";
|
||||
import { SpawnAd } from "./layers/SpawnAd";
|
||||
import { SpawnTimer } from "./layers/SpawnTimer";
|
||||
import { StructureIconsLayer } from "./layers/StructureIconsLayer";
|
||||
import { StructureLayer } from "./layers/StructureLayer";
|
||||
@@ -209,18 +209,19 @@ export function createRenderer(
|
||||
fpsDisplay.eventBus = eventBus;
|
||||
fpsDisplay.userSettings = userSettings;
|
||||
|
||||
const spawnAd = document.querySelector("spawn-ad") as SpawnAd;
|
||||
if (!(spawnAd instanceof SpawnAd)) {
|
||||
console.error("spawn ad not found");
|
||||
}
|
||||
spawnAd.g = game;
|
||||
|
||||
const alertFrame = document.querySelector("alert-frame") as AlertFrame;
|
||||
if (!(alertFrame instanceof AlertFrame)) {
|
||||
console.error("alert frame not found");
|
||||
}
|
||||
alertFrame.game = game;
|
||||
|
||||
const spawnTimer = document.querySelector("spawn-timer") as SpawnTimer;
|
||||
if (!(spawnTimer instanceof SpawnTimer)) {
|
||||
console.error("spawn timer not found");
|
||||
}
|
||||
spawnTimer.game = game;
|
||||
spawnTimer.transformHandler = transformHandler;
|
||||
|
||||
// When updating these layers please be mindful of the order.
|
||||
// Try to group layers by the return value of shouldTransform.
|
||||
// Not grouping the layers may cause excessive calls to context.save() and context.restore().
|
||||
@@ -246,7 +247,7 @@ export function createRenderer(
|
||||
uiState,
|
||||
playerPanel,
|
||||
),
|
||||
new SpawnTimer(game, transformHandler),
|
||||
spawnTimer,
|
||||
leaderboard,
|
||||
gameLeftSidebar,
|
||||
unitDisplay,
|
||||
@@ -260,7 +261,7 @@ export function createRenderer(
|
||||
playerPanel,
|
||||
headsUpMessage,
|
||||
multiTabModal,
|
||||
spawnAd,
|
||||
new AdTimer(game),
|
||||
alertFrame,
|
||||
fpsDisplay,
|
||||
];
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
const AD_SHOW_TICKS = 60 * 10; // 1 minute
|
||||
|
||||
export class AdTimer implements Layer {
|
||||
private isHidden: boolean = false;
|
||||
|
||||
constructor(private g: GameView) {}
|
||||
|
||||
init() {}
|
||||
|
||||
public async tick() {
|
||||
if (this.isHidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gameTicks = this.g.ticks() - this.g.config().numSpawnPhaseTurns();
|
||||
if (gameTicks > AD_SHOW_TICKS) {
|
||||
console.log("destroying sticky ads");
|
||||
window.fusetag?.que?.push(() => {
|
||||
window.fusetag?.destroySticky?.();
|
||||
});
|
||||
this.isHidden = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { getGamesPlayed } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
const AD_TYPE = "bottom_rail";
|
||||
const AD_CONTAINER_ID = "bottom-rail-ad-container";
|
||||
|
||||
@customElement("spawn-ad")
|
||||
export class SpawnAd extends LitElement implements Layer {
|
||||
public g: GameView;
|
||||
|
||||
@state()
|
||||
private isVisible: boolean = false;
|
||||
|
||||
@state()
|
||||
private adLoaded: boolean = false;
|
||||
|
||||
private gamesPlayed: number = 0;
|
||||
|
||||
// Override createRenderRoot to disable shadow DOM
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
static styles = css``;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.gamesPlayed = getGamesPlayed();
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
this.isVisible = true;
|
||||
this.loadAd();
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
// Destroy the ad when hiding
|
||||
this.destroyAd();
|
||||
this.isVisible = false;
|
||||
this.adLoaded = false;
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
public async tick() {
|
||||
if (
|
||||
!this.isVisible &&
|
||||
this.g.inSpawnPhase() &&
|
||||
this.g.ticks() > 10 &&
|
||||
this.gamesPlayed > 5
|
||||
) {
|
||||
console.log("not showing spawn ad");
|
||||
// this.show();
|
||||
}
|
||||
if (this.isVisible && !this.g.inSpawnPhase()) {
|
||||
console.log("hiding bottom left ad");
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private loadAd(): void {
|
||||
if (!window.ramp) {
|
||||
console.warn("Playwire RAMP not available");
|
||||
return;
|
||||
}
|
||||
if (this.adLoaded) {
|
||||
console.log("Ad already loaded, skipping");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
window.ramp.que.push(() => {
|
||||
window.ramp.spaAddAds([
|
||||
{
|
||||
type: AD_TYPE,
|
||||
selectorId: AD_CONTAINER_ID,
|
||||
},
|
||||
]);
|
||||
this.adLoaded = true;
|
||||
console.log("Playwire ad loaded:", AD_TYPE);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to load Playwire ad:", error);
|
||||
}
|
||||
}
|
||||
|
||||
private destroyAd(): void {
|
||||
if (!window.ramp || !this.adLoaded) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
window.ramp.que.push(() => {
|
||||
window.ramp.destroyUnits("all");
|
||||
console.log("Playwire spawn ad destroyed");
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to destroy Playwire ad:", error);
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
// Clean up ad when component is removed
|
||||
this.destroyAd();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.isVisible) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="fixed bottom-0 left-0 w-full min-h-[100px] bg-gray-900 border border-gray-600 flex items-center justify-center z-50"
|
||||
>
|
||||
<div
|
||||
id="${AD_CONTAINER_ID}"
|
||||
class="w-full h-full flex items-center justify-center"
|
||||
>
|
||||
${!this.adLoaded
|
||||
? html`<span class="text-white text-sm"
|
||||
>${translateText("spawn_ad.loading")}</span
|
||||
>`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,34 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { GameMode, Team } from "../../../core/game/Game";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
export class SpawnTimer implements Layer {
|
||||
@customElement("spawn-timer")
|
||||
export class SpawnTimer extends LitElement implements Layer {
|
||||
public game: GameView;
|
||||
public transformHandler: TransformHandler;
|
||||
|
||||
private ratios = [0];
|
||||
private colors = ["rgba(0, 128, 255, 0.7)", "rgba(0, 0, 0, 0.5)"];
|
||||
|
||||
constructor(
|
||||
private game: GameView,
|
||||
private transformHandler: TransformHandler,
|
||||
) {}
|
||||
private isVisible = false;
|
||||
|
||||
init() {}
|
||||
createRenderRoot() {
|
||||
this.style.position = "fixed";
|
||||
this.style.top = "0";
|
||||
this.style.left = "0";
|
||||
this.style.width = "100%";
|
||||
this.style.height = "7px";
|
||||
this.style.zIndex = "1000";
|
||||
this.style.pointerEvents = "none";
|
||||
return this;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.isVisible = true;
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (this.game.inSpawnPhase()) {
|
||||
@@ -21,6 +37,7 @@ export class SpawnTimer implements Layer {
|
||||
this.game.ticks() / this.game.config().numSpawnPhaseTurns(),
|
||||
];
|
||||
this.colors = ["rgba(0, 128, 255, 0.7)"];
|
||||
this.requestUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,6 +45,7 @@ export class SpawnTimer implements Layer {
|
||||
this.colors = [];
|
||||
|
||||
if (this.game.config().gameConfig().gameMode !== GameMode.Team) {
|
||||
this.requestUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -41,44 +59,52 @@ export class SpawnTimer implements Layer {
|
||||
|
||||
const theme = this.game.config().theme();
|
||||
const total = sumIterator(teamTiles.values());
|
||||
if (total === 0) return;
|
||||
if (total === 0) {
|
||||
this.requestUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [team, count] of teamTiles) {
|
||||
const ratio = count / total;
|
||||
this.ratios.push(ratio);
|
||||
this.colors.push(theme.teamColor(team).toRgbString());
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
shouldTransform(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
renderLayer(context: CanvasRenderingContext2D) {
|
||||
if (this.ratios.length === 0 || this.colors.length === 0) return;
|
||||
render() {
|
||||
if (!this.isVisible) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const barHeight = 10;
|
||||
const barWidth = this.transformHandler.width();
|
||||
if (this.ratios.length === 0 || this.colors.length === 0) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.game.inSpawnPhase() &&
|
||||
this.game.config().gameConfig().gameMode !== GameMode.Team
|
||||
) {
|
||||
return;
|
||||
return html``;
|
||||
}
|
||||
|
||||
let x = 0;
|
||||
let filledRatio = 0;
|
||||
for (let i = 0; i < this.ratios.length && i < this.colors.length; i++) {
|
||||
const ratio = this.ratios[i] ?? 1 - filledRatio;
|
||||
const segmentWidth = barWidth * ratio;
|
||||
|
||||
context.fillStyle = this.colors[i];
|
||||
context.fillRect(x, 0, segmentWidth, barHeight);
|
||||
|
||||
x += segmentWidth;
|
||||
filledRatio += ratio;
|
||||
}
|
||||
return html`
|
||||
<div class="w-full h-full flex z-[999]">
|
||||
${this.ratios.map((ratio, i) => {
|
||||
const color = this.colors[i] || "rgba(0, 0, 0, 0.5)";
|
||||
return html`
|
||||
<div
|
||||
class="h-full transition-all duration-100 ease-in-out"
|
||||
style="width: ${ratio * 100}%; background-color: ${color};"
|
||||
></div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,12 +132,7 @@ export class WinModal extends LitElement implements Layer {
|
||||
for (const pattern of Object.values(patterns?.patterns ?? {})) {
|
||||
for (const colorPalette of pattern.colorPalettes ?? []) {
|
||||
if (
|
||||
patternRelationship(
|
||||
pattern,
|
||||
colorPalette,
|
||||
me !== false ? me : null,
|
||||
null,
|
||||
) === "purchasable"
|
||||
patternRelationship(pattern, colorPalette, me, null) === "purchasable"
|
||||
) {
|
||||
const palette = patterns?.colorPalettes?.[colorPalette.name];
|
||||
if (palette) {
|
||||
|
||||
Reference in New Issue
Block a user