Improve color goal display with swatches
This commit is contained in:
75
main.js
75
main.js
@@ -96,10 +96,12 @@
|
||||
};
|
||||
|
||||
const balls = [];
|
||||
const blobConstraints = new Map();
|
||||
let spawnTimer = null;
|
||||
let score = 0;
|
||||
let highScore = 0;
|
||||
let clearedCount = 0;
|
||||
let clearedByColor = {};
|
||||
let gameOver = false;
|
||||
let isPaused = false;
|
||||
let levelWon = false;
|
||||
@@ -135,6 +137,7 @@
|
||||
engine.gravity.y = config.gravity;
|
||||
clearedCount = 0;
|
||||
levelWon = false;
|
||||
clearedByColor = {};
|
||||
highScore = loadHighScore(next.id);
|
||||
rebuildSceneBodies();
|
||||
buildLegend();
|
||||
@@ -309,6 +312,7 @@
|
||||
levelWon = false;
|
||||
score = 0;
|
||||
clearedCount = 0;
|
||||
clearedByColor = {};
|
||||
resetChainVisuals();
|
||||
balls.forEach((ball) => {
|
||||
cleanupBall(ball);
|
||||
@@ -379,6 +383,17 @@
|
||||
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) {
|
||||
const remaining = [];
|
||||
boundaries.forEach((b) => {
|
||||
@@ -457,13 +472,36 @@
|
||||
}
|
||||
ui.spawnScorePopup(releasePoint || chain.pointer, gain, chain.color);
|
||||
chain.constraints.forEach((c) => World.remove(world, c));
|
||||
const blobIds = new Set();
|
||||
chain.bodies.forEach((body) => {
|
||||
cleanupBall(body);
|
||||
World.remove(world, body);
|
||||
if (body.plugin?.blobId) {
|
||||
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.
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -644,6 +682,8 @@
|
||||
return Bodies.circle(x, y, config.ballRadius, commonOpts);
|
||||
};
|
||||
|
||||
const normalizeColor = (c) => (c || "").trim().toLowerCase();
|
||||
|
||||
const getGoalState = () => {
|
||||
const winCond = currentScene?.config?.winCondition;
|
||||
if (!winCond) return null;
|
||||
@@ -666,14 +706,29 @@
|
||||
};
|
||||
}
|
||||
if (winCond.type === "colorClear" && Array.isArray(winCond.targets)) {
|
||||
const target = winCond.targets.reduce(
|
||||
(sum, t) => sum + (t.count || 0),
|
||||
0,
|
||||
);
|
||||
const targets = winCond.targets.map((t) => ({
|
||||
color: normalizeColor(t.color),
|
||||
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 {
|
||||
label: "Clear target colors",
|
||||
progress: target > 0 ? (100 * clearedCount) / target : 0,
|
||||
met: false,
|
||||
label: parts.join(" • "),
|
||||
progress: totalTarget > 0 ? (100 * totalAchieved) / totalTarget : 0,
|
||||
met: targets.every(
|
||||
(t) =>
|
||||
(clearedByColor[normalizeColor(t.color)] || 0) >= (t.count || 0),
|
||||
),
|
||||
colors: targets.map((t) => t.color),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
|
||||
24
ui.js
24
ui.js
@@ -97,8 +97,28 @@
|
||||
}
|
||||
};
|
||||
|
||||
const setGoal = ({ label, progress }) => {
|
||||
if (goalLabelEl) goalLabelEl.textContent = label || "—";
|
||||
const setGoal = ({ label, progress, colors }) => {
|
||||
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)
|
||||
goalProgressEl.style.width = `${Math.max(
|
||||
0,
|
||||
|
||||
Reference in New Issue
Block a user