Improve color goal display with swatches

This commit is contained in:
Daddy32
2025-12-13 20:20:08 +01:00
parent 854f19503a
commit 37607ef148
2 changed files with 87 additions and 12 deletions

75
main.js
View File

@@ -96,10 +96,12 @@
}; };
const balls = []; const balls = [];
const blobConstraints = new Map();
let spawnTimer = null; let spawnTimer = null;
let score = 0; let score = 0;
let highScore = 0; let highScore = 0;
let clearedCount = 0; let clearedCount = 0;
let clearedByColor = {};
let gameOver = false; let gameOver = false;
let isPaused = false; let isPaused = false;
let levelWon = false; let levelWon = false;
@@ -135,6 +137,7 @@
engine.gravity.y = config.gravity; engine.gravity.y = config.gravity;
clearedCount = 0; clearedCount = 0;
levelWon = false; levelWon = false;
clearedByColor = {};
highScore = loadHighScore(next.id); highScore = loadHighScore(next.id);
rebuildSceneBodies(); rebuildSceneBodies();
buildLegend(); buildLegend();
@@ -309,6 +312,7 @@
levelWon = false; levelWon = false;
score = 0; score = 0;
clearedCount = 0; clearedCount = 0;
clearedByColor = {};
resetChainVisuals(); resetChainVisuals();
balls.forEach((ball) => { balls.forEach((ball) => {
cleanupBall(ball); cleanupBall(ball);
@@ -379,6 +383,17 @@
Body.applyForce(ball, ball.position, force); Body.applyForce(ball, ball.position, force);
}); });
} }
if (winCond.onWin.swirlBalls) {
balls.forEach((ball) => {
const angle = Math.random() * Math.PI * 2;
const mag = 0.06;
Body.applyForce(ball, ball.position, {
x: Math.cos(angle) * mag,
y: -Math.abs(Math.sin(angle)) * mag * 1.5,
});
Body.setAngularVelocity(ball, (Math.random() - 0.5) * 0.3);
});
}
if (winCond.onWin.removeCurves) { if (winCond.onWin.removeCurves) {
const remaining = []; const remaining = [];
boundaries.forEach((b) => { boundaries.forEach((b) => {
@@ -457,13 +472,36 @@
} }
ui.spawnScorePopup(releasePoint || chain.pointer, gain, chain.color); ui.spawnScorePopup(releasePoint || chain.pointer, gain, chain.color);
chain.constraints.forEach((c) => World.remove(world, c)); chain.constraints.forEach((c) => World.remove(world, c));
const blobIds = new Set();
chain.bodies.forEach((body) => { chain.bodies.forEach((body) => {
cleanupBall(body); if (body.plugin?.blobId) {
World.remove(world, body); blobIds.add(body.plugin.blobId);
} else {
if (body.plugin?.color) {
const key = normalizeColor(body.plugin.color);
clearedByColor[key] = (clearedByColor[key] || 0) + 1;
}
cleanupBall(body);
World.remove(world, body);
}
});
blobIds.forEach((id) => {
balls
.filter((b) => b.plugin?.blobId === id)
.forEach((b) => {
if (b.plugin?.color) {
const key = normalizeColor(b.plugin.color);
clearedByColor[key] = (clearedByColor[key] || 0) + 1;
}
});
removeBlob(id);
}); });
// Remove cleared balls from tracking list. // Remove cleared balls from tracking list.
for (let i = balls.length - 1; i >= 0; i -= 1) { for (let i = balls.length - 1; i >= 0; i -= 1) {
if (chain.bodies.includes(balls[i])) { if (
chain.bodies.includes(balls[i]) ||
(balls[i].plugin?.blobId && blobIds.has(balls[i].plugin?.blobId))
) {
balls.splice(i, 1); balls.splice(i, 1);
} }
} }
@@ -644,6 +682,8 @@
return Bodies.circle(x, y, config.ballRadius, commonOpts); return Bodies.circle(x, y, config.ballRadius, commonOpts);
}; };
const normalizeColor = (c) => (c || "").trim().toLowerCase();
const getGoalState = () => { const getGoalState = () => {
const winCond = currentScene?.config?.winCondition; const winCond = currentScene?.config?.winCondition;
if (!winCond) return null; if (!winCond) return null;
@@ -666,14 +706,29 @@
}; };
} }
if (winCond.type === "colorClear" && Array.isArray(winCond.targets)) { if (winCond.type === "colorClear" && Array.isArray(winCond.targets)) {
const target = winCond.targets.reduce( const targets = winCond.targets.map((t) => ({
(sum, t) => sum + (t.count || 0), color: normalizeColor(t.color),
0, count: t.count || 0,
); }));
const totalTarget = targets.reduce((sum, t) => sum + t.count, 0);
let totalAchieved = 0;
const parts = targets.map((t) => {
const got = Math.min(
t.count,
clearedByColor[normalizeColor(t.color)] || 0,
);
totalAchieved += got;
const remaining = Math.max(0, t.count - got);
return `${got}/${t.count} (${remaining} left)`;
});
return { return {
label: "Clear target colors", label: parts.join(" • "),
progress: target > 0 ? (100 * clearedCount) / target : 0, progress: totalTarget > 0 ? (100 * totalAchieved) / totalTarget : 0,
met: false, met: targets.every(
(t) =>
(clearedByColor[normalizeColor(t.color)] || 0) >= (t.count || 0),
),
colors: targets.map((t) => t.color),
}; };
} }
return null; return null;

24
ui.js
View File

@@ -97,8 +97,28 @@
} }
}; };
const setGoal = ({ label, progress }) => { const setGoal = ({ label, progress, colors }) => {
if (goalLabelEl) goalLabelEl.textContent = label || "—"; if (goalLabelEl) {
goalLabelEl.innerHTML = "";
if (Array.isArray(colors) && colors.length > 0) {
colors.forEach((color) => {
const swatch = document.createElement("span");
swatch.style.background = color;
swatch.style.display = "inline-block";
swatch.style.width = "14px";
swatch.style.height = "14px";
swatch.style.borderRadius = "50%";
swatch.style.border = "1px solid rgba(255,255,255,0.2)";
swatch.style.marginRight = "6px";
goalLabelEl.appendChild(swatch);
});
const text = document.createElement("span");
text.textContent = label || "";
goalLabelEl.appendChild(text);
} else {
goalLabelEl.textContent = label || "—";
}
}
if (goalProgressEl) if (goalProgressEl)
goalProgressEl.style.width = `${Math.max( goalProgressEl.style.width = `${Math.max(
0, 0,