Stagger floating goal reminders

This commit is contained in:
Daddy32
2025-12-14 13:28:04 +01:00
parent 17d11d062c
commit d7f9fe9af3

View File

@@ -139,6 +139,8 @@
let score = 0; let score = 0;
let highScore = 0; let highScore = 0;
let longestChainRecord = 0; let longestChainRecord = 0;
const goalMilestoneThresholds = [0.5, 0.75, 0.9];
let announcedGoalMilestones = new Set();
let clearedCount = 0; let clearedCount = 0;
let clearedByColor = {}; let clearedByColor = {};
let gameOver = false; let gameOver = false;
@@ -483,6 +485,7 @@
score = 0; score = 0;
clearedCount = 0; clearedCount = 0;
clearedByColor = {}; clearedByColor = {};
announcedGoalMilestones.clear();
endDrag(); endDrag();
const winCond = currentScene?.config?.winCondition; const winCond = currentScene?.config?.winCondition;
if (winCond?.type === "timer") { if (winCond?.type === "timer") {
@@ -517,7 +520,6 @@
spawnInitialBurst(); spawnInitialBurst();
startSpawner(); startSpawner();
} }
announceGoalMessage();
}; };
const setHighlight = (body, on) => { const setHighlight = (body, on) => {
@@ -896,6 +898,7 @@
}); });
const goal = getGoalState(); const goal = getGoalState();
ui.setGoal(goal || { label: "—", progress: 0 }); ui.setGoal(goal || { label: "—", progress: 0 });
maybeAnnounceGoalProgress(goal);
}; };
const buildLegend = () => { const buildLegend = () => {
@@ -1017,6 +1020,7 @@
label: `${String(Math.floor(remainingSec / 60)).padStart(2, "0")}:${String(remainingSec % 60).padStart(2, "0")}`, label: `${String(Math.floor(remainingSec / 60)).padStart(2, "0")}:${String(remainingSec % 60).padStart(2, "0")}`,
progress: duration > 0 ? (100 * elapsed) / duration : 0, progress: duration > 0 ? (100 * elapsed) / duration : 0,
met: remainingMs <= 0, met: remainingMs <= 0,
hint: "Survive until the timer ends",
}; };
} }
if (winCond.type === "clearCount") { if (winCond.type === "clearCount") {
@@ -1026,6 +1030,7 @@
label: `Clear ${target} balls (${remaining} left)`, label: `Clear ${target} balls (${remaining} left)`,
progress: target > 0 ? (100 * clearedCount) / target : 0, progress: target > 0 ? (100 * clearedCount) / target : 0,
met: clearedCount >= target, met: clearedCount >= target,
hint: "Clear the required balls",
}; };
} }
if (winCond.type === "score") { if (winCond.type === "score") {
@@ -1035,6 +1040,7 @@
label: `Score ${target} (${remaining} left)`, label: `Score ${target} (${remaining} left)`,
progress: target > 0 ? (100 * score) / target : 0, progress: target > 0 ? (100 * score) / target : 0,
met: score >= target, met: score >= target,
hint: "Reach the score target",
}; };
} }
if (winCond.type === "colorClear" && Array.isArray(winCond.targets)) { if (winCond.type === "colorClear" && Array.isArray(winCond.targets)) {
@@ -1062,14 +1068,16 @@
(clearedByColor[normalizeColor(t.color)] || 0) >= (t.count || 0), (clearedByColor[normalizeColor(t.color)] || 0) >= (t.count || 0),
), ),
colors: targets.map((t) => t.color), colors: targets.map((t) => t.color),
hint: "Clear the target colors",
}; };
} }
return null; return null;
}; };
const formatGoalMessage = (goal) => { const formatGoalMessage = (goal) => {
if (!goal || !goal.label || goal.label === "—") return null; if (!goal) return null;
const pieces = [`Goal: ${goal.label}`]; const title = goal.hint || goal.label || "Goal";
const pieces = [`Goal: ${title}`];
if (Number.isFinite(goal.progress)) { if (Number.isFinite(goal.progress)) {
const pct = Math.max(0, Math.min(100, Math.round(goal.progress))); const pct = Math.max(0, Math.min(100, Math.round(goal.progress)));
pieces.push(`${pct}% complete`); pieces.push(`${pct}% complete`);
@@ -1077,21 +1085,28 @@
return pieces.join(" • "); return pieces.join(" • ");
}; };
const announceGoalMessage = () => { const maybeAnnounceGoalProgress = (goal) => {
const goal = getGoalState(); if (!goal || !Number.isFinite(goal.progress)) return;
const text = config.messages?.text || formatGoalMessage(goal); const fraction = Math.max(0, Math.min(1, goal.progress / 100));
if (!text) return; for (const threshold of goalMilestoneThresholds) {
const colors = if (fraction >= threshold && !announcedGoalMilestones.has(threshold)) {
(Array.isArray(config.messages?.colors) && config.messages.colors) || announcedGoalMilestones.add(threshold);
goal?.colors || const text = config.messages?.text || formatGoalMessage(goal);
null; if (!text) return;
ui.showFloatingMessage( const colors =
{ text, colors }, (Array.isArray(config.messages?.colors) && config.messages.colors) ||
{ goal?.colors ||
durationMs: config.messages.durationMs, null;
position: config.messages.position, ui.showFloatingMessage(
}, { text, colors },
); {
durationMs: config.messages.durationMs,
position: config.messages.position,
},
);
break;
}
}
}; };
const clampBodiesIntoView = (prevWidth, prevHeight) => { const clampBodiesIntoView = (prevWidth, prevHeight) => {