Stagger floating goal reminders
This commit is contained in:
25
src/main.js
25
src/main.js
@@ -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,8 +1085,12 @@
|
|||||||
return pieces.join(" • ");
|
return pieces.join(" • ");
|
||||||
};
|
};
|
||||||
|
|
||||||
const announceGoalMessage = () => {
|
const maybeAnnounceGoalProgress = (goal) => {
|
||||||
const goal = getGoalState();
|
if (!goal || !Number.isFinite(goal.progress)) return;
|
||||||
|
const fraction = Math.max(0, Math.min(1, goal.progress / 100));
|
||||||
|
for (const threshold of goalMilestoneThresholds) {
|
||||||
|
if (fraction >= threshold && !announcedGoalMilestones.has(threshold)) {
|
||||||
|
announcedGoalMilestones.add(threshold);
|
||||||
const text = config.messages?.text || formatGoalMessage(goal);
|
const text = config.messages?.text || formatGoalMessage(goal);
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
const colors =
|
const colors =
|
||||||
@@ -1092,6 +1104,9 @@
|
|||||||
position: config.messages.position,
|
position: config.messages.position,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const clampBodiesIntoView = (prevWidth, prevHeight) => {
|
const clampBodiesIntoView = (prevWidth, prevHeight) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user