Refine goal HUD animations

This commit is contained in:
Daddy32
2025-12-16 21:15:14 +01:00
parent e3dfeb2e70
commit 9d554c8805
5 changed files with 195 additions and 6 deletions

View File

@@ -56,6 +56,14 @@
},
},
messages: { ...defaultMessageConfig },
goalEffects: {
enabled: true,
nearThreshold: 0.7,
completeThreshold: 0.98,
pulseSpeedMs: 900,
glowStrength: 0.55,
gradientBlend: true,
},
};
return {

View File

@@ -65,8 +65,16 @@
},
});
const normalizeGoalEffects = (
goalEffects = {},
defaults = baseConfig.goalEffects || {},
) => ({
...defaults,
...goalEffects,
});
const normalizeSceneConfig = (sceneConfig = {}, defaults = baseConfig) => {
const { link = {}, messages = {}, ...rest } = sceneConfig;
const { link = {}, messages = {}, goalEffects = {}, ...rest } = sceneConfig;
const base = defaults || {};
return {
...base,
@@ -76,6 +84,7 @@
messages,
base.messages || defaultMessageConfig,
),
goalEffects: normalizeGoalEffects(goalEffects, base.goalEffects),
};
};
@@ -403,7 +412,7 @@
const checkWinCondition = () => {
if (state.levelWon) return;
const goal = goals.getGoalState();
ui.setGoal(goal || { label: "—", progress: 0 });
ui.setGoal(goal || { label: "—", progress: 0 }, config.goalEffects);
if (!goal || !goal.met) return;
applyWinEffects();
state.levelWon = true;
@@ -424,7 +433,7 @@
activeColor: chain.color,
});
const goal = goals.getGoalState();
ui.setGoal(goal || { label: "—", progress: 0 });
ui.setGoal(goal || { label: "—", progress: 0 }, config.goalEffects);
goals.maybeAnnounceGoalProgress(goal);
};

View File

@@ -141,6 +141,14 @@
// Scoring/persistence
noGameOver: false, // true disables game-over-on-blocked-entry checks
goalEffects: {
enabled: true, // set false to disable HUD goal bling
nearThreshold: 0.7, // fraction (0-1) that triggers “near” pulse
completeThreshold: 0.98, // fraction (0-1) for “almost there” glow
pulseSpeedMs: 900, // pulse speed for near/complete states
glowStrength: 0.55, // opacity multiplier for glow
gradientBlend: true, // when colors are provided, blend them into the bar
},
},
createBodies: (w, h) => {
// Return an array of Matter bodies that make up scene obstacles/boundaries.

View File

@@ -108,7 +108,7 @@
}
};
const setGoal = ({ label, progress, colors }) => {
const setGoal = ({ label, progress, colors }, effects = {}) => {
if (goalLabelEl) {
goalLabelEl.innerHTML = "";
if (Array.isArray(colors) && colors.length > 0) {
@@ -135,6 +135,47 @@
0,
Math.min(100, progress ?? 0),
)}%`;
if (goalProgressEl) {
const parentProgress = goalProgressEl.parentElement;
const targetEls = [goalProgressEl, parentProgress].filter(Boolean);
targetEls.forEach((el) => {
el.classList.remove("goal-near", "goal-complete", "goal-gradient");
});
goalProgressEl.style.removeProperty("background-image");
goalProgressEl.style.removeProperty("--goal-glow-alpha");
const fraction = Math.max(0, Math.min(1, (progress ?? 0) / 100));
if (effects?.enabled) {
const near = effects.nearThreshold ?? 0.7;
const complete = effects.completeThreshold ?? 0.98;
const pulseSpeed = Math.max(200, effects.pulseSpeedMs ?? 900);
goalProgressEl.style.setProperty(
"--goal-pulse-speed",
`${pulseSpeed}ms`,
);
goalProgressEl.style.setProperty(
"--goal-glow-alpha",
effects.glowStrength ?? 0.55,
);
if (fraction >= complete) {
targetEls.forEach((el) => el.classList.add("goal-complete"));
} else if (fraction >= near) {
targetEls.forEach((el) => el.classList.add("goal-near"));
}
const useGradient =
effects.gradientBlend !== false &&
Array.isArray(colors) &&
colors.length > 0;
if (useGradient) {
goalProgressEl.classList.add("goal-gradient");
const denom = Math.max(1, colors.length - 1);
const stops = colors
.map((c, idx) => `${c} ${(idx / denom) * 100}%`)
.join(", ");
goalProgressEl.style.backgroundImage = `linear-gradient(90deg, ${stops})`;
}
}
}
};
const showWin = (message) => {