mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-18 17:53:29 +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:
@@ -74,10 +74,11 @@ export async function fetchCosmetics(): Promise<Cosmetics | null> {
|
|||||||
export function patternRelationship(
|
export function patternRelationship(
|
||||||
pattern: Pattern,
|
pattern: Pattern,
|
||||||
colorPalette: { name: string; isArchived?: boolean } | null,
|
colorPalette: { name: string; isArchived?: boolean } | null,
|
||||||
userMeResponse: UserMeResponse | null,
|
userMeResponse: UserMeResponse | false,
|
||||||
affiliateCode: string | null,
|
affiliateCode: string | null,
|
||||||
): "owned" | "purchasable" | "blocked" {
|
): "owned" | "purchasable" | "blocked" {
|
||||||
const flares = userMeResponse?.player.flares ?? [];
|
const flares =
|
||||||
|
userMeResponse === false ? [] : (userMeResponse.player.flares ?? []);
|
||||||
if (flares.includes("pattern:*")) {
|
if (flares.includes("pattern:*")) {
|
||||||
return "owned";
|
return "owned";
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-1
@@ -1,5 +1,6 @@
|
|||||||
import { LitElement, html } from "lit";
|
import { LitElement, html } from "lit";
|
||||||
import { customElement, state } from "lit/decorators.js";
|
import { customElement, state } from "lit/decorators.js";
|
||||||
|
import { UserMeResponse } from "../core/ApiSchemas";
|
||||||
import { isInIframe } from "./Utils";
|
import { isInIframe } from "./Utils";
|
||||||
|
|
||||||
const LEFT_FUSE = "gutter-ad-container-left";
|
const LEFT_FUSE = "gutter-ad-container-left";
|
||||||
@@ -17,6 +18,31 @@ export class GutterAds extends LitElement {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly boundUserMeHandler = (event: Event) =>
|
||||||
|
this.onUserMe((event as CustomEvent<UserMeResponse | false>).detail);
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
document.addEventListener(
|
||||||
|
"userMeResponse",
|
||||||
|
this.boundUserMeHandler as EventListener,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onUserMe(userMeResponse: UserMeResponse | false): void {
|
||||||
|
const flares =
|
||||||
|
userMeResponse === false ? [] : (userMeResponse.player.flares ?? []);
|
||||||
|
const hasFlare = flares.some((flare) => flare.startsWith("pattern:"));
|
||||||
|
if (hasFlare) {
|
||||||
|
console.log("No ads because you have patterns");
|
||||||
|
window.enableAds = false;
|
||||||
|
} else {
|
||||||
|
console.log("No flares, showing ads");
|
||||||
|
this.show();
|
||||||
|
window.enableAds = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private isScreenLargeEnough(): boolean {
|
private isScreenLargeEnough(): boolean {
|
||||||
return window.innerWidth >= MIN_SCREEN_WIDTH;
|
return window.innerWidth >= MIN_SCREEN_WIDTH;
|
||||||
}
|
}
|
||||||
@@ -52,6 +78,10 @@ export class GutterAds extends LitElement {
|
|||||||
this.isVisible = false;
|
this.isVisible = false;
|
||||||
console.log("hiding GutterAds");
|
console.log("hiding GutterAds");
|
||||||
this.destroyAds();
|
this.destroyAds();
|
||||||
|
document.removeEventListener(
|
||||||
|
"userMeResponse",
|
||||||
|
this.boundUserMeHandler as EventListener,
|
||||||
|
);
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +125,7 @@ export class GutterAds extends LitElement {
|
|||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this.destroyAds();
|
this.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
+4
-109
@@ -2,7 +2,6 @@ import version from "../../resources/version.txt";
|
|||||||
import { UserMeResponse } from "../core/ApiSchemas";
|
import { UserMeResponse } from "../core/ApiSchemas";
|
||||||
import { EventBus } from "../core/EventBus";
|
import { EventBus } from "../core/EventBus";
|
||||||
import { GameRecord, GameStartInfo, ID } from "../core/Schemas";
|
import { GameRecord, GameStartInfo, ID } from "../core/Schemas";
|
||||||
import { ServerConfig } from "../core/configuration/Config";
|
|
||||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||||
import { UserSettings } from "../core/game/UserSettings";
|
import { UserSettings } from "../core/game/UserSettings";
|
||||||
import "./AccountModal";
|
import "./AccountModal";
|
||||||
@@ -36,17 +35,17 @@ import {
|
|||||||
generateCryptoRandomUUID,
|
generateCryptoRandomUUID,
|
||||||
incrementGamesPlayed,
|
incrementGamesPlayed,
|
||||||
isInIframe,
|
isInIframe,
|
||||||
translateText,
|
|
||||||
} from "./Utils";
|
} from "./Utils";
|
||||||
import "./components/NewsButton";
|
import "./components/NewsButton";
|
||||||
import { NewsButton } from "./components/NewsButton";
|
import { NewsButton } from "./components/NewsButton";
|
||||||
import "./components/baseComponents/Button";
|
import "./components/baseComponents/Button";
|
||||||
import "./components/baseComponents/Modal";
|
import "./components/baseComponents/Modal";
|
||||||
import { discordLogin, getUserMe, isLoggedIn } from "./jwt";
|
import { getUserMe, isLoggedIn } from "./jwt";
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
enableAds: boolean;
|
||||||
PageOS: {
|
PageOS: {
|
||||||
session: {
|
session: {
|
||||||
newPageView: () => void;
|
newPageView: () => void;
|
||||||
@@ -57,6 +56,7 @@ declare global {
|
|||||||
destroyZone: (id: string) => void;
|
destroyZone: (id: string) => void;
|
||||||
pageInit: (options?: any) => void;
|
pageInit: (options?: any) => void;
|
||||||
que: Array<() => void>;
|
que: Array<() => void>;
|
||||||
|
destroySticky: () => void;
|
||||||
};
|
};
|
||||||
ramp: {
|
ramp: {
|
||||||
que: Array<() => void>;
|
que: Array<() => void>;
|
||||||
@@ -265,98 +265,12 @@ class Client {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const config = await getServerConfigFromClient();
|
if (userMeResponse !== false) {
|
||||||
if (!hasAllowedFlare(userMeResponse, config)) {
|
|
||||||
if (userMeResponse === false) {
|
|
||||||
// Login is required
|
|
||||||
document.body.innerHTML = `
|
|
||||||
<div style="
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
font-family: sans-serif;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
">
|
|
||||||
<div style="
|
|
||||||
background-color: rgba(0, 0, 0, 0.7);
|
|
||||||
color: white;
|
|
||||||
padding: 2em;
|
|
||||||
margin: 5em;
|
|
||||||
border-radius: 12px;
|
|
||||||
text-align: center;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
|
||||||
">
|
|
||||||
<p style="margin-bottom: 1em;">${translateText("auth.login_required")}</p>
|
|
||||||
<p style="margin-bottom: 1.5em;">${translateText("auth.redirecting")}</p>
|
|
||||||
<div style="width: 100%; height: 8px; background-color: #444; border-radius: 4px; overflow: hidden;">
|
|
||||||
<div style="
|
|
||||||
height: 100%;
|
|
||||||
width: 0%;
|
|
||||||
background-color: #4caf50;
|
|
||||||
animation: fillBar 5s linear forwards;
|
|
||||||
"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bg-image"></div>
|
|
||||||
<style>
|
|
||||||
@keyframes fillBar {
|
|
||||||
from { width: 0%; }
|
|
||||||
to { width: 100%; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
`;
|
|
||||||
setTimeout(discordLogin, 5000);
|
|
||||||
} else {
|
|
||||||
// Unauthorized
|
|
||||||
document.body.innerHTML = `
|
|
||||||
<div style="
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
font-family: sans-serif;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
">
|
|
||||||
<div style="
|
|
||||||
background-color: rgba(0, 0, 0, 0.7);
|
|
||||||
color: white;
|
|
||||||
padding: 2em;
|
|
||||||
margin: 5em;
|
|
||||||
border-radius: 12px;
|
|
||||||
text-align: center;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
|
||||||
">
|
|
||||||
<p style="margin-bottom: 1em;">${translateText("auth.not_authorized")}</p>
|
|
||||||
<p>${translateText("auth.contact_admin")}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bg-image"></div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else if (userMeResponse === false) {
|
|
||||||
// Not logged in
|
|
||||||
this.patternsModal.onUserMe(null);
|
|
||||||
} else {
|
|
||||||
// Authorized
|
// Authorized
|
||||||
console.log(
|
console.log(
|
||||||
`Your player ID is ${userMeResponse.player.publicId}\n` +
|
`Your player ID is ${userMeResponse.player.publicId}\n` +
|
||||||
"Sharing this ID will allow others to view your game history and stats.",
|
"Sharing this ID will allow others to view your game history and stats.",
|
||||||
);
|
);
|
||||||
this.patternsModal.onUserMe(userMeResponse);
|
|
||||||
const flares = (userMeResponse.player.flares ?? []).filter((flare) =>
|
|
||||||
flare.startsWith("pattern:"),
|
|
||||||
);
|
|
||||||
if (flares.length > 0) {
|
|
||||||
console.log("Hiding gutter ads because you have patterns");
|
|
||||||
this.gutterAds.hide();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -628,12 +542,6 @@ class Client {
|
|||||||
this.publicLobby.stop();
|
this.publicLobby.stop();
|
||||||
incrementGamesPlayed();
|
incrementGamesPlayed();
|
||||||
|
|
||||||
try {
|
|
||||||
window.PageOS.session.newPageView();
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error calling newPageView", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelectorAll(".ad").forEach((ad) => {
|
document.querySelectorAll(".ad").forEach((ad) => {
|
||||||
(ad as HTMLElement).style.display = "none";
|
(ad as HTMLElement).style.display = "none";
|
||||||
});
|
});
|
||||||
@@ -675,7 +583,6 @@ class Client {
|
|||||||
window.fusetag.pageInit({
|
window.fusetag.pageInit({
|
||||||
blockingFuseIds: ["lhs_sticky_vrec", "rhs_sticky_vrec"],
|
blockingFuseIds: ["lhs_sticky_vrec", "rhs_sticky_vrec"],
|
||||||
});
|
});
|
||||||
this.gutterAds.show();
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -735,15 +642,3 @@ function getPersistentIDFromCookie(): string {
|
|||||||
|
|
||||||
return newID;
|
return newID;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasAllowedFlare(
|
|
||||||
userMeResponse: UserMeResponse | false,
|
|
||||||
config: ServerConfig,
|
|
||||||
) {
|
|
||||||
const allowed = config.allowedFlares();
|
|
||||||
if (allowed === undefined) return true;
|
|
||||||
if (userMeResponse === false) return false;
|
|
||||||
const flares = userMeResponse.player.flares;
|
|
||||||
if (flares === undefined) return false;
|
|
||||||
return allowed.length === 0 || allowed.some((f) => flares.includes(f));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -37,14 +37,24 @@ export class TerritoryPatternsModal extends LitElement {
|
|||||||
|
|
||||||
private affiliateCode: string | null = null;
|
private affiliateCode: string | null = null;
|
||||||
|
|
||||||
private userMeResponse: UserMeResponse | null = null;
|
private userMeResponse: UserMeResponse | false = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserMe(userMeResponse: UserMeResponse | null) {
|
connectedCallback() {
|
||||||
if (userMeResponse === null) {
|
super.connectedCallback();
|
||||||
|
document.addEventListener(
|
||||||
|
"userMeResponse",
|
||||||
|
(event: CustomEvent<UserMeResponse | false>) => {
|
||||||
|
this.onUserMe(event.detail);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onUserMe(userMeResponse: UserMeResponse | false) {
|
||||||
|
if (userMeResponse === false) {
|
||||||
this.userSettings.setSelectedPatternName(undefined);
|
this.userSettings.setSelectedPatternName(undefined);
|
||||||
this.selectedPattern = null;
|
this.selectedPattern = null;
|
||||||
this.selectedColor = null;
|
this.selectedColor = null;
|
||||||
@@ -136,7 +146,11 @@ export class TerritoryPatternsModal extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderColorSwatchGrid(): TemplateResult {
|
private renderColorSwatchGrid(): TemplateResult {
|
||||||
const hexCodes = (this.userMeResponse?.player.flares ?? [])
|
const hexCodes = (
|
||||||
|
this.userMeResponse === false
|
||||||
|
? []
|
||||||
|
: (this.userMeResponse.player.flares ?? [])
|
||||||
|
)
|
||||||
.filter((flare) => flare.startsWith("color:"))
|
.filter((flare) => flare.startsWith("color:"))
|
||||||
.map((flare) => "#" + flare.split(":")[1]);
|
.map((flare) => "#" + flare.split(":")[1]);
|
||||||
return html`
|
return html`
|
||||||
|
|||||||
@@ -137,7 +137,6 @@ export function renderPatternPreview(
|
|||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
): TemplateResult {
|
): TemplateResult {
|
||||||
console.log("renderPatternPreview", pattern);
|
|
||||||
if (pattern === null) {
|
if (pattern === null) {
|
||||||
return renderBlankPreview(width, height);
|
return renderBlankPreview(width, height);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { GameStartingModal } from "../GameStartingModal";
|
|||||||
import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler";
|
import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler";
|
||||||
import { TransformHandler } from "./TransformHandler";
|
import { TransformHandler } from "./TransformHandler";
|
||||||
import { UIState } from "./UIState";
|
import { UIState } from "./UIState";
|
||||||
|
import { AdTimer } from "./layers/AdTimer";
|
||||||
import { AlertFrame } from "./layers/AlertFrame";
|
import { AlertFrame } from "./layers/AlertFrame";
|
||||||
import { BuildMenu } from "./layers/BuildMenu";
|
import { BuildMenu } from "./layers/BuildMenu";
|
||||||
import { ChatDisplay } from "./layers/ChatDisplay";
|
import { ChatDisplay } from "./layers/ChatDisplay";
|
||||||
@@ -27,7 +28,6 @@ import { PlayerPanel } from "./layers/PlayerPanel";
|
|||||||
import { RailroadLayer } from "./layers/RailroadLayer";
|
import { RailroadLayer } from "./layers/RailroadLayer";
|
||||||
import { ReplayPanel } from "./layers/ReplayPanel";
|
import { ReplayPanel } from "./layers/ReplayPanel";
|
||||||
import { SettingsModal } from "./layers/SettingsModal";
|
import { SettingsModal } from "./layers/SettingsModal";
|
||||||
import { SpawnAd } from "./layers/SpawnAd";
|
|
||||||
import { SpawnTimer } from "./layers/SpawnTimer";
|
import { SpawnTimer } from "./layers/SpawnTimer";
|
||||||
import { StructureIconsLayer } from "./layers/StructureIconsLayer";
|
import { StructureIconsLayer } from "./layers/StructureIconsLayer";
|
||||||
import { StructureLayer } from "./layers/StructureLayer";
|
import { StructureLayer } from "./layers/StructureLayer";
|
||||||
@@ -209,18 +209,19 @@ export function createRenderer(
|
|||||||
fpsDisplay.eventBus = eventBus;
|
fpsDisplay.eventBus = eventBus;
|
||||||
fpsDisplay.userSettings = userSettings;
|
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;
|
const alertFrame = document.querySelector("alert-frame") as AlertFrame;
|
||||||
if (!(alertFrame instanceof AlertFrame)) {
|
if (!(alertFrame instanceof AlertFrame)) {
|
||||||
console.error("alert frame not found");
|
console.error("alert frame not found");
|
||||||
}
|
}
|
||||||
alertFrame.game = game;
|
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.
|
// When updating these layers please be mindful of the order.
|
||||||
// Try to group layers by the return value of shouldTransform.
|
// Try to group layers by the return value of shouldTransform.
|
||||||
// Not grouping the layers may cause excessive calls to context.save() and context.restore().
|
// Not grouping the layers may cause excessive calls to context.save() and context.restore().
|
||||||
@@ -246,7 +247,7 @@ export function createRenderer(
|
|||||||
uiState,
|
uiState,
|
||||||
playerPanel,
|
playerPanel,
|
||||||
),
|
),
|
||||||
new SpawnTimer(game, transformHandler),
|
spawnTimer,
|
||||||
leaderboard,
|
leaderboard,
|
||||||
gameLeftSidebar,
|
gameLeftSidebar,
|
||||||
unitDisplay,
|
unitDisplay,
|
||||||
@@ -260,7 +261,7 @@ export function createRenderer(
|
|||||||
playerPanel,
|
playerPanel,
|
||||||
headsUpMessage,
|
headsUpMessage,
|
||||||
multiTabModal,
|
multiTabModal,
|
||||||
spawnAd,
|
new AdTimer(game),
|
||||||
alertFrame,
|
alertFrame,
|
||||||
fpsDisplay,
|
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 { GameMode, Team } from "../../../core/game/Game";
|
||||||
import { GameView } from "../../../core/game/GameView";
|
import { GameView } from "../../../core/game/GameView";
|
||||||
import { TransformHandler } from "../TransformHandler";
|
import { TransformHandler } from "../TransformHandler";
|
||||||
import { Layer } from "./Layer";
|
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 ratios = [0];
|
||||||
private colors = ["rgba(0, 128, 255, 0.7)", "rgba(0, 0, 0, 0.5)"];
|
private colors = ["rgba(0, 128, 255, 0.7)", "rgba(0, 0, 0, 0.5)"];
|
||||||
|
|
||||||
constructor(
|
private isVisible = false;
|
||||||
private game: GameView,
|
|
||||||
private transformHandler: TransformHandler,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
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() {
|
tick() {
|
||||||
if (this.game.inSpawnPhase()) {
|
if (this.game.inSpawnPhase()) {
|
||||||
@@ -21,6 +37,7 @@ export class SpawnTimer implements Layer {
|
|||||||
this.game.ticks() / this.game.config().numSpawnPhaseTurns(),
|
this.game.ticks() / this.game.config().numSpawnPhaseTurns(),
|
||||||
];
|
];
|
||||||
this.colors = ["rgba(0, 128, 255, 0.7)"];
|
this.colors = ["rgba(0, 128, 255, 0.7)"];
|
||||||
|
this.requestUpdate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +45,7 @@ export class SpawnTimer implements Layer {
|
|||||||
this.colors = [];
|
this.colors = [];
|
||||||
|
|
||||||
if (this.game.config().gameConfig().gameMode !== GameMode.Team) {
|
if (this.game.config().gameConfig().gameMode !== GameMode.Team) {
|
||||||
|
this.requestUpdate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,44 +59,52 @@ export class SpawnTimer implements Layer {
|
|||||||
|
|
||||||
const theme = this.game.config().theme();
|
const theme = this.game.config().theme();
|
||||||
const total = sumIterator(teamTiles.values());
|
const total = sumIterator(teamTiles.values());
|
||||||
if (total === 0) return;
|
if (total === 0) {
|
||||||
|
this.requestUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const [team, count] of teamTiles) {
|
for (const [team, count] of teamTiles) {
|
||||||
const ratio = count / total;
|
const ratio = count / total;
|
||||||
this.ratios.push(ratio);
|
this.ratios.push(ratio);
|
||||||
this.colors.push(theme.teamColor(team).toRgbString());
|
this.colors.push(theme.teamColor(team).toRgbString());
|
||||||
}
|
}
|
||||||
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldTransform(): boolean {
|
shouldTransform(): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLayer(context: CanvasRenderingContext2D) {
|
render() {
|
||||||
if (this.ratios.length === 0 || this.colors.length === 0) return;
|
if (!this.isVisible) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
const barHeight = 10;
|
if (this.ratios.length === 0 || this.colors.length === 0) {
|
||||||
const barWidth = this.transformHandler.width();
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!this.game.inSpawnPhase() &&
|
!this.game.inSpawnPhase() &&
|
||||||
this.game.config().gameConfig().gameMode !== GameMode.Team
|
this.game.config().gameConfig().gameMode !== GameMode.Team
|
||||||
) {
|
) {
|
||||||
return;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = 0;
|
return html`
|
||||||
let filledRatio = 0;
|
<div class="w-full h-full flex z-[999]">
|
||||||
for (let i = 0; i < this.ratios.length && i < this.colors.length; i++) {
|
${this.ratios.map((ratio, i) => {
|
||||||
const ratio = this.ratios[i] ?? 1 - filledRatio;
|
const color = this.colors[i] || "rgba(0, 0, 0, 0.5)";
|
||||||
const segmentWidth = barWidth * ratio;
|
return html`
|
||||||
|
<div
|
||||||
context.fillStyle = this.colors[i];
|
class="h-full transition-all duration-100 ease-in-out"
|
||||||
context.fillRect(x, 0, segmentWidth, barHeight);
|
style="width: ${ratio * 100}%; background-color: ${color};"
|
||||||
|
></div>
|
||||||
x += segmentWidth;
|
`;
|
||||||
filledRatio += ratio;
|
})}
|
||||||
}
|
</div>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,12 +132,7 @@ export class WinModal extends LitElement implements Layer {
|
|||||||
for (const pattern of Object.values(patterns?.patterns ?? {})) {
|
for (const pattern of Object.values(patterns?.patterns ?? {})) {
|
||||||
for (const colorPalette of pattern.colorPalettes ?? []) {
|
for (const colorPalette of pattern.colorPalettes ?? []) {
|
||||||
if (
|
if (
|
||||||
patternRelationship(
|
patternRelationship(pattern, colorPalette, me, null) === "purchasable"
|
||||||
pattern,
|
|
||||||
colorPalette,
|
|
||||||
me !== false ? me : null,
|
|
||||||
null,
|
|
||||||
) === "purchasable"
|
|
||||||
) {
|
) {
|
||||||
const palette = patterns?.colorPalettes?.[colorPalette.name];
|
const palette = patterns?.colorPalettes?.[colorPalette.name];
|
||||||
if (palette) {
|
if (palette) {
|
||||||
|
|||||||
@@ -97,6 +97,13 @@
|
|||||||
src="https://cdn.fuseplatform.net/publift/tags/2/4121/fuse.js"
|
src="https://cdn.fuseplatform.net/publift/tags/2/4121/fuse.js"
|
||||||
></script>
|
></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.googletag = window.googletag || { cmd: [] };
|
||||||
|
googletag.cmd.push(function () {
|
||||||
|
googletag.pubads().set("page_url", "http://openfront.io ");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Analytics -->
|
<!-- Analytics -->
|
||||||
<script
|
<script
|
||||||
async
|
async
|
||||||
@@ -388,6 +395,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<settings-modal></settings-modal>
|
<settings-modal></settings-modal>
|
||||||
<player-panel></player-panel>
|
<player-panel></player-panel>
|
||||||
|
<spawn-timer></spawn-timer>
|
||||||
<help-modal></help-modal>
|
<help-modal></help-modal>
|
||||||
<dark-mode-button></dark-mode-button>
|
<dark-mode-button></dark-mode-button>
|
||||||
<alert-frame></alert-frame>
|
<alert-frame></alert-frame>
|
||||||
@@ -396,7 +404,6 @@
|
|||||||
<multi-tab-modal></multi-tab-modal>
|
<multi-tab-modal></multi-tab-modal>
|
||||||
<news-modal></news-modal>
|
<news-modal></news-modal>
|
||||||
<game-left-sidebar></game-left-sidebar>
|
<game-left-sidebar></game-left-sidebar>
|
||||||
<spawn-ad></spawn-ad>
|
|
||||||
<flag-input-modal></flag-input-modal>
|
<flag-input-modal></flag-input-modal>
|
||||||
<fps-display></fps-display>
|
<fps-display></fps-display>
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user