3 Commits
Author SHA1 Message Date
tevansandClaude Fable 5 7dcbd96b13 Fix pause overlay showing GAME PAUSED while unpaused — v2.0.5
Root cause of the toggle cycle bug: the theme set display:flex on
#pause.dg2-pause unconditionally, out-specificity-ing core's hidden
(unpaused) state. After unpausing, the pause element stayed visible and
showed Foundry's default GAME PAUSED markup (the overlay injector
correctly skips unpaused renders). Scope the flex layout to
#pause.dg2-pause.paused and explicitly hide :not(.paused).

Verified three full pause/unpause cycles: paused = radar scope visible,
unpaused = element hidden. (Earlier tests misread visibility via
offsetParent, which is always null for position:fixed elements.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 22:39:52 +01:00
tevansandClaude Fable 5 7bbe36c29e Deterministic pause overlay via GamePause subclass — v2.0.4
The v2.0.3 pauseGame-hook re-injection raced Foundry's own pause re-render:
on slower clients the default GAME PAUSED markup landed after our overlay,
so pressing Space after an unpause showed the stock screen. Register a
DG2GamePause subclass (CONFIG.ui.pause) that applies the radar-scope
overlay inside _onRender - part of every render, no timing window. Hook
fallbacks retained (with retries) for cores lacking the AppV2 GamePause.
Verified with five rapid unpause/pause cycles at 150ms settle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 22:27:50 +01:00
tevansandClaude Fable 5 7d684ffdb2 Robust pause overlay + PNG NPC tokens — v2.0.3
The radar-scope pause overlay only applied via the renderGamePause hook,
which does not fire on every pause toggle path — pressing Space after an
unpause could show Foundry's default GAME PAUSED. The overlay injector is
now idempotent and wired to renderGamePause AND pauseGame (plus a ready
check), so the scope applies on every toggle; verified across repeated
unpause/pause cycles.

Also rasterize the three FALSE ALARM NPC tokens (Rostova, Changed Vance,
the Tear) to 1024px PNGs, matching the map fix in v2.0.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 22:16:11 +01:00
10 changed files with 73 additions and 22 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

+3 -3
View File
@@ -95,7 +95,7 @@ function npcActor({ key, name, type, img, token, stats, health, skills, notes, s
const aRostova = npcActor({
key: "rostova", name: "Dr. Elena Rostova", type: "npc",
img: `${A}/token-rostova.svg`, token: `${A}/token-rostova.svg`,
img: `${A}/token-rostova.png`, token: `${A}/token-rostova.png`,
stats: { str: 10, con: 10, dex: 11, int: 15, pow: 12, cha: 11 }, health: 10, san: 0,
skills: { computer_science: 70, science: 0, sigint: 40, first_aid: 30, alertness: 40 },
shortDescription: "FAA facility technician — deceased, flash-frozen.",
@@ -104,7 +104,7 @@ const aRostova = npcActor({
const aVance = npcActor({
key: "vance", name: "Deputy Marcus Vance (Changed)", type: "unnatural",
img: `${A}/token-vance.svg`, token: `${A}/token-vance.svg`,
img: `${A}/token-vance.png`, token: `${A}/token-vance.png`,
stats: { str: 17, con: 16, dex: 16, int: 6, pow: 15, cha: 3 }, health: 16,
san: { failed: "1D6", success: "1" },
skills: { alertness: 60, athletics: 70, dodge: 50, melee_weapons: 65, unarmed_combat: 55 },
@@ -115,7 +115,7 @@ const aVance = npcActor({
const aTear = npcActor({
key: "tear", name: "The Tear (Static Between Stations)", type: "unnatural",
img: `${A}/token-entity.svg`, token: `${A}/token-entity.svg`,
img: `${A}/token-entity.png`, token: `${A}/token-entity.png`,
stats: { str: 20, con: 30, dex: 20, int: 20, pow: 25, cha: 1 }, health: 30,
san: { failed: "1D20", success: "1D6" },
skills: { alertness: 90, dodge: 80 },
+9
View File
@@ -539,12 +539,21 @@
#pause.dg2-pause {
background: none;
}
/* Layout ONLY while actually paused - never override Foundry's hidden
(unpaused) state, or the stock GAME PAUSED markup shows after unpausing. */
#pause.dg2-pause.paused {
display: flex;
flex-direction: column;
align-items: center;
gap: 14px;
}
#pause.dg2-pause:not(.paused) {
display: none;
}
#pause.dg2-pause img { display: none; }
.dg2-pause-scope {
+52 -10
View File
@@ -37,6 +37,24 @@ import RitualItemData from "./data/item/ritual.js";
const { Actors, Items } = foundry.documents.collections;
Hooks.once("init", async () => {
// Own the pause UI: applying our overlay inside _onRender means it is part
// of EVERY render of the pause application - no hook/timing races.
try {
const BasePause = foundry.applications?.ui?.GamePause;
if (BasePause) {
class DG2GamePause extends BasePause {
/** @override */
async _onRender(context, options) {
await super._onRender?.(context, options);
applyDG2PauseOverlay(this.element);
}
}
CONFIG.ui.pause = DG2GamePause;
}
} catch (ex) {
console.warn("Delta Green 2 | could not register pause subclass", ex);
}
Object.assign(CONFIG.Actor.dataModels, {
// The keys are the types defined in system.json documentTypes.Actor
agent: AgentData,
@@ -221,16 +239,25 @@ Hooks.once("diceSoNiceReady", (dice3d) => {
}
});
Hooks.on("renderGamePause", function (_, html, options) {
if (options.cssClass !== "paused") return;
/**
* Replace the stock pause spinner with the DG2 radar-scope animation.
* Idempotent: safe to call on every render/toggle; skips if already applied.
* Wired to BOTH renderGamePause and pauseGame so the scope appears no matter
* which path Foundry takes when the pause state changes.
*/
function applyDG2PauseOverlay(element) {
try {
const caption = game.i18n.translations.DG?.MissionPaused ?? "Mission Paused";
const sub = game.i18n.translations.DG2?.PausedSub ?? "ALL STATIONS HOLD // AWAITING HANDLER";
const el = element ?? document.getElementById("pause");
if (!el || !game.paused) return;
if (el.querySelector(".dg2-pause-scope")) return; // already applied
// Replace the default spinner with a DG2 radar-scope pause animation.
html.classList.add("dg2-pause");
html.innerHTML = `
const caption = game.i18n.translations.DG?.MissionPaused ?? "Mission Paused";
const sub =
game.i18n.translations.DG2?.PausedSub ??
"All stations hold // awaiting Handler";
el.classList.add("dg2-pause");
el.innerHTML = `
<div class="dg2-pause-scope" aria-hidden="true">
<span class="dg2-pause-ring r1"></span>
<span class="dg2-pause-ring r2"></span>
@@ -246,11 +273,26 @@ Hooks.on("renderGamePause", function (_, html, options) {
<span class="dg2-pause-title">${caption}</span>
<span class="dg2-pause-sub">${sub}</span>
</figcaption>`;
} catch {
// noop
} catch (ex) {
console.warn("Delta Green 2 | pause overlay failed", ex);
}
}
Hooks.on("renderGamePause", (_app, html) => {
const el = html instanceof HTMLElement ? html : html?.[0];
applyDG2PauseOverlay(el);
});
Hooks.on("pauseGame", () => {
// Fallback for cores where the pause subclass isn't registered: re-apply
// after Foundry's own render, with retries to outlast a slow re-render.
for (const delay of [50, 250, 700]) {
setTimeout(() => applyDG2PauseOverlay(), delay);
}
});
Hooks.once("ready", () => applyDG2PauseOverlay());
function addEventListenerToChatMessage(element) {
element.addEventListener("click", (event) => {
const btnWithAction = event.target.closest("button[data-action]");
Binary file not shown.
+3 -3
View File
@@ -1,3 +1,3 @@
2026/07/16-15:09:13.184913 170e2b000 Recovering log #4
2026/07/16-15:09:13.186094 170e2b000 Delete type=0 #4
2026/07/16-15:09:13.186131 170e2b000 Delete type=3 #2
2026/07/16-22:12:46.376475 17235f000 Recovering log #4
2026/07/16-22:12:46.380006 17235f000 Delete type=0 #4
2026/07/16-22:12:46.380058 17235f000 Delete type=3 #2
+5 -5
View File
@@ -1,5 +1,5 @@
2026/07/16-15:09:13.093478 16f10f000 Delete type=3 #1
2026/07/16-15:09:13.095837 16f9a7000 Level-0 table #5: started
2026/07/16-15:09:13.096279 16f9a7000 Level-0 table #5: 19548 bytes OK
2026/07/16-15:09:13.096401 16f9a7000 Delete type=0 #3
2026/07/16-15:09:13.096523 16f9a7000 Manual compaction at level-0 from '!adventures!5zf355zpx5s1ux56' @ 72057594037927935 : 1 .. '!adventures!5zf355zpx5s1ux56' @ 0 : 0; will stop at (end)
2026/07/16-22:12:46.303554 1737b7000 Delete type=3 #1
2026/07/16-22:12:46.305623 17404f000 Level-0 table #5: started
2026/07/16-22:12:46.305975 17404f000 Level-0 table #5: 19548 bytes OK
2026/07/16-22:12:46.306072 17404f000 Delete type=0 #3
2026/07/16-22:12:46.306136 17404f000 Manual compaction at level-0 from '!adventures!5zf355zpx5s1ux56' @ 72057594037927935 : 1 .. '!adventures!5zf355zpx5s1ux56' @ 0 : 0; will stop at (end)
+1 -1
View File
@@ -15,7 +15,7 @@
"discord": "jalensailin"
}
],
"version": "2.0.2",
"version": "2.0.5",
"compatibility": {
"minimum": "13",
"verified": "14",