Extract helpers for chain finish flow
This commit is contained in:
189
src/main.js
189
src/main.js
@@ -435,93 +435,93 @@
|
|||||||
updateHud();
|
updateHud();
|
||||||
};
|
};
|
||||||
|
|
||||||
const finishChain = (releasePoint) => {
|
const updateLongestChain = (chainLength) => {
|
||||||
if (!chain.active || gameOver || isPaused) return;
|
if (chainLength <= longestChainRecord) return;
|
||||||
if (chain.bodies.length >= config.minChain) {
|
longestChainRecord = chainLength;
|
||||||
const chainLength = chain.bodies.length;
|
saveLongestChain(currentScene.id, longestChainRecord);
|
||||||
if (chainLength > longestChainRecord) {
|
console.log(
|
||||||
longestChainRecord = chainLength;
|
"New longest chain record",
|
||||||
saveLongestChain(currentScene.id, longestChainRecord);
|
chainLength,
|
||||||
console.log(
|
"scene",
|
||||||
"New longest chain record",
|
currentScene?.id,
|
||||||
chainLength,
|
);
|
||||||
"scene",
|
ui.showFloatingMessage(`New chain record: ${chainLength}`, {
|
||||||
currentScene?.id,
|
durationMs: 3600,
|
||||||
);
|
position: config.messages.position,
|
||||||
ui.showFloatingMessage(`New chain record: ${chainLength}`, {
|
});
|
||||||
durationMs: 3600,
|
};
|
||||||
position: config.messages.position,
|
|
||||||
});
|
const getChainScoreState = () => {
|
||||||
|
const baseGain = 10 * Math.pow(chain.bodies.length, 2);
|
||||||
|
const negativeColors = (
|
||||||
|
currentScene?.config?.negativeScoreColors || []
|
||||||
|
).map(normalizeColor);
|
||||||
|
const negativeProgressColors = (
|
||||||
|
currentScene?.config?.negativeProgressColors || []
|
||||||
|
).map(normalizeColor);
|
||||||
|
const normalizedColor = chain.color
|
||||||
|
? normalizeColor(chain.color || "")
|
||||||
|
: null;
|
||||||
|
const isNegative =
|
||||||
|
normalizedColor && negativeColors.includes(normalizedColor);
|
||||||
|
const isNegativeProgress =
|
||||||
|
normalizedColor && negativeProgressColors.includes(normalizedColor);
|
||||||
|
const gain = isNegative ? -baseGain : baseGain;
|
||||||
|
return { gain, isNegativeProgress };
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeChainConstraints = () => {
|
||||||
|
chain.constraints.forEach((c) => World.remove(world, c));
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeChainBodies = () => {
|
||||||
|
const blobIds = new Set();
|
||||||
|
chain.bodies.forEach((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;
|
||||||
|
}
|
||||||
|
spawnSystem.cleanupBall(body);
|
||||||
|
World.remove(world, body);
|
||||||
}
|
}
|
||||||
const baseGain = 10 * Math.pow(chain.bodies.length, 2);
|
});
|
||||||
const negativeColors = (
|
blobIds.forEach((id) => {
|
||||||
currentScene?.config?.negativeScoreColors || []
|
balls
|
||||||
).map(normalizeColor);
|
.filter((b) => b.plugin?.blobId === id)
|
||||||
const negativeProgressColors = (
|
.forEach((b) => {
|
||||||
currentScene?.config?.negativeProgressColors || []
|
if (b.plugin?.color) {
|
||||||
).map(normalizeColor);
|
const key = normalizeColor(b.plugin.color);
|
||||||
const isNegative =
|
|
||||||
chain.color &&
|
|
||||||
negativeColors.includes(normalizeColor(chain.color || ""));
|
|
||||||
const isNegativeProgress =
|
|
||||||
chain.color &&
|
|
||||||
negativeProgressColors.includes(normalizeColor(chain.color || ""));
|
|
||||||
const gain = isNegative ? -baseGain : baseGain;
|
|
||||||
score += gain;
|
|
||||||
clearedCount += chain.bodies.length;
|
|
||||||
if (score > highScore) {
|
|
||||||
highScore = score;
|
|
||||||
saveHighScore(currentScene.id, highScore);
|
|
||||||
}
|
|
||||||
ui.spawnScorePopup(releasePoint || chain.pointer, gain, chain.color);
|
|
||||||
chain.constraints.forEach((c) => World.remove(world, c));
|
|
||||||
const blobIds = new Set();
|
|
||||||
chain.bodies.forEach((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;
|
clearedByColor[key] = (clearedByColor[key] || 0) + 1;
|
||||||
}
|
}
|
||||||
spawnSystem.cleanupBall(body);
|
});
|
||||||
World.remove(world, body);
|
spawnSystem.removeBlob(id);
|
||||||
}
|
});
|
||||||
});
|
for (let i = balls.length - 1; i >= 0; i -= 1) {
|
||||||
blobIds.forEach((id) => {
|
if (
|
||||||
balls
|
chain.bodies.includes(balls[i]) ||
|
||||||
.filter((b) => b.plugin?.blobId === id)
|
(balls[i].plugin?.blobId && blobIds.has(balls[i].plugin?.blobId))
|
||||||
.forEach((b) => {
|
) {
|
||||||
if (b.plugin?.color) {
|
balls.splice(i, 1);
|
||||||
const key = normalizeColor(b.plugin.color);
|
|
||||||
clearedByColor[key] = (clearedByColor[key] || 0) + 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
spawnSystem.removeBlob(id);
|
|
||||||
});
|
|
||||||
// Remove cleared balls from tracking list.
|
|
||||||
for (let i = balls.length - 1; i >= 0; i -= 1) {
|
|
||||||
if (
|
|
||||||
chain.bodies.includes(balls[i]) ||
|
|
||||||
(balls[i].plugin?.blobId && blobIds.has(balls[i].plugin?.blobId))
|
|
||||||
) {
|
|
||||||
balls.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (isNegativeProgress) {
|
|
||||||
const winCond = currentScene?.config?.winCondition;
|
|
||||||
if (winCond?.type === "colorClear" && Array.isArray(winCond.targets)) {
|
|
||||||
winCond.targets.forEach((target) => {
|
|
||||||
const key = normalizeColor(target.color);
|
|
||||||
const current = clearedByColor[key] || 0;
|
|
||||||
clearedByColor[key] = Math.max(0, current - chain.bodies.length);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
chain.constraints.forEach((c) => World.remove(world, c));
|
|
||||||
chain.bodies.forEach((b) => setHighlight(b, false));
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyNegativeProgressPenalty = (chainLength) => {
|
||||||
|
const winCond = currentScene?.config?.winCondition;
|
||||||
|
if (winCond?.type !== "colorClear" || !Array.isArray(winCond.targets)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
winCond.targets.forEach((target) => {
|
||||||
|
const key = normalizeColor(target.color);
|
||||||
|
const current = clearedByColor[key] || 0;
|
||||||
|
clearedByColor[key] = Math.max(0, current - chainLength);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetChainState = () => {
|
||||||
chain.active = false;
|
chain.active = false;
|
||||||
chain.color = null;
|
chain.color = null;
|
||||||
chain.bodies = [];
|
chain.bodies = [];
|
||||||
@@ -531,6 +531,31 @@
|
|||||||
checkWinCondition();
|
checkWinCondition();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const finishChain = (releasePoint) => {
|
||||||
|
if (!chain.active || gameOver || isPaused) return;
|
||||||
|
const chainLength = chain.bodies.length;
|
||||||
|
if (chainLength >= config.minChain) {
|
||||||
|
updateLongestChain(chainLength);
|
||||||
|
const { gain, isNegativeProgress } = getChainScoreState();
|
||||||
|
score += gain;
|
||||||
|
clearedCount += chainLength;
|
||||||
|
if (score > highScore) {
|
||||||
|
highScore = score;
|
||||||
|
saveHighScore(currentScene.id, highScore);
|
||||||
|
}
|
||||||
|
ui.spawnScorePopup(releasePoint || chain.pointer, gain, chain.color);
|
||||||
|
removeChainConstraints();
|
||||||
|
removeChainBodies();
|
||||||
|
if (isNegativeProgress) {
|
||||||
|
applyNegativeProgressPenalty(chainLength);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
removeChainConstraints();
|
||||||
|
chain.bodies.forEach((b) => setHighlight(b, false));
|
||||||
|
}
|
||||||
|
resetChainState();
|
||||||
|
};
|
||||||
|
|
||||||
const updateHud = () => {
|
const updateHud = () => {
|
||||||
ui.updateHud({
|
ui.updateHud({
|
||||||
spawnIntervalMs: config.spawnIntervalMs,
|
spawnIntervalMs: config.spawnIntervalMs,
|
||||||
|
|||||||
Reference in New Issue
Block a user