Add stack blocks scene with column spawning

This commit is contained in:
Daddy32
2025-12-14 21:05:25 +01:00
parent ec87d71e52
commit e500380a7e
3 changed files with 78 additions and 15 deletions

View File

@@ -102,6 +102,7 @@
<script src="./src/scenes/scene-lavalamp.js"></script> <script src="./src/scenes/scene-lavalamp.js"></script>
<script src="./src/scenes/scene-relax.js"></script> <script src="./src/scenes/scene-relax.js"></script>
<script src="./src/scenes/scene-swirl-arena.js"></script> <script src="./src/scenes/scene-swirl-arena.js"></script>
<script src="./src/scenes/scene-stack-blocks.js"></script>
<script src="./src/scenes/index.js"></script> <script src="./src/scenes/index.js"></script>
<script src="./src/ui.js"></script> <script src="./src/ui.js"></script>
<script src="./src/main.js"></script> <script src="./src/main.js"></script>

View File

@@ -52,6 +52,7 @@
minChain: 3, minChain: 3,
palette: ["#ff595e", "#ffca3a", "#8ac926", "#1982c4", "#6a4c93"], palette: ["#ff595e", "#ffca3a", "#8ac926", "#1982c4", "#6a4c93"],
ballRadius: 18, ballRadius: 18,
ballShape: "circle",
link: { link: {
stiffness: 0.85, stiffness: 0.85,
lengthScale: 1.05, // max stretch factor; slack below this lengthScale: 1.05, // max stretch factor; slack below this
@@ -320,12 +321,18 @@
(Math.random() - 0.5) * Math.max(spawnJitter, config.ballRadius), (Math.random() - 0.5) * Math.max(spawnJitter, config.ballRadius),
} }
: null; : null;
const x = const columnCount = currentScene?.config?.spawnColumns;
let x =
centerSpawn?.x ?? centerSpawn?.x ??
Math.max( Math.max(
config.ballRadius + 10, config.ballRadius + 10,
Math.min(width - config.ballRadius - 10, Math.random() * width), Math.min(width - config.ballRadius - 10, Math.random() * width),
); );
if (Number.isFinite(columnCount) && columnCount > 0) {
const columnWidth = width / columnCount;
const colIndex = Math.floor(Math.random() * columnCount);
x = columnWidth * (colIndex + 0.5);
}
const spawnFromBottom = currentScene?.config?.spawnFrom === "bottom"; const spawnFromBottom = currentScene?.config?.spawnFrom === "bottom";
const y = const y =
centerSpawn?.y ?? centerSpawn?.y ??
@@ -342,21 +349,30 @@
Math.floor(Math.random() * (batchMax - batchMin + 1)) + batchMin, Math.floor(Math.random() * (batchMax - batchMin + 1)) + batchMin,
); );
for (let i = 0; i < batchCount; i += 1) { for (let i = 0; i < batchCount; i += 1) {
const blob = createBallBodies( const targetX = Math.min(
Math.min(
Math.max( Math.max(
config.ballRadius + 10, config.ballRadius + 10,
x + (i - batchCount / 2) * config.ballRadius * 1.5, x + (i - batchCount / 2) * config.ballRadius * 1.5,
), ),
width - config.ballRadius - 10, width - config.ballRadius - 10,
), );
const targetY =
y + y +
i * i *
(spawnFromBottom (spawnFromBottom
? -config.ballRadius * 0.5 ? -config.ballRadius * 0.5
: config.ballRadius * 0.5), : config.ballRadius * 0.5);
color, const halfSize = config.ballRadius * 1.05;
); const region = {
min: { x: targetX - halfSize, y: targetY - halfSize },
max: { x: targetX + halfSize, y: targetY + halfSize },
};
const hits = Query.region(balls, region);
if (hits && hits.length > 0) {
triggerGameOver();
return;
}
const blob = createBallBodies(targetX, targetY, color);
if (blob.constraints.length > 0 && blob.blobId) { if (blob.constraints.length > 0 && blob.blobId) {
blobConstraints.set(blob.blobId, blob.constraints); blobConstraints.set(blob.blobId, blob.constraints);
} }
@@ -402,6 +418,39 @@
} }
}; };
const spawnInitialColumns = () => {
const rows = currentScene?.config?.initialRows;
const columns = currentScene?.config?.spawnColumns;
if (!Number.isFinite(rows) || rows <= 0) return false;
if (!Number.isFinite(columns) || columns <= 0) return false;
const columnWidth = width / columns;
const side = config.ballRadius * 2;
const rowGap = side * 1.05;
for (let r = 0; r < rows; r += 1) {
const y = config.ballRadius * 1.5 + r * rowGap;
for (let c = 0; c < columns; c += 1) {
const x = columnWidth * (c + 0.5);
const color =
config.palette[Math.floor(Math.random() * config.palette.length)];
const blob = createBallBodies(x, y, color);
blob.bodies.forEach((body) => {
body.plugin = body.plugin || {};
body.plugin.hasEntered = true;
balls.push(body);
World.add(world, body);
});
if (blob.constraints.length > 0) {
World.add(world, blob.constraints);
}
if (blob.constraints.length > 0 && blob.blobId) {
blobConstraints.set(blob.blobId, blob.constraints);
}
}
}
spawnCount += rows * columns;
return true;
};
const cleanupBall = (ball) => { const cleanupBall = (ball) => {
if (ball.plugin && ball.plugin.entryCheckId) { if (ball.plugin && ball.plugin.entryCheckId) {
clearTimeout(ball.plugin.entryCheckId); clearTimeout(ball.plugin.entryCheckId);
@@ -517,7 +566,10 @@
if (isGridScene()) { if (isGridScene()) {
spawnGridBalls(); spawnGridBalls();
} else { } else {
const spawnedGrid = spawnInitialColumns();
if (!spawnedGrid) {
spawnInitialBurst(); spawnInitialBurst();
}
startSpawner(); startSpawner();
} }
showGoalIntro(); showGoalIntro();
@@ -1000,6 +1052,15 @@
body.plugin = { color, hasEntered: false, entryCheckId: null }; body.plugin = { color, hasEntered: false, entryCheckId: null };
return { bodies: [body], constraints: [], blobId: null }; return { bodies: [body], constraints: [], blobId: null };
} }
if (currentScene?.config?.ballShape === "rect") {
const side = config.ballRadius * 2;
const body = Bodies.rectangle(x, y, side, side, {
...commonOpts,
chamfer: 4,
});
body.plugin = { color, hasEntered: false, entryCheckId: null };
return { bodies: [body], constraints: [], blobId: null };
}
const body = Bodies.circle(x, y, config.ballRadius, commonOpts); const body = Bodies.circle(x, y, config.ballRadius, commonOpts);
body.plugin = { color, hasEntered: false, entryCheckId: null }; body.plugin = { color, hasEntered: false, entryCheckId: null };
return { bodies: [body], constraints: [], blobId: null }; return { bodies: [body], constraints: [], blobId: null };

View File

@@ -8,6 +8,7 @@
"scene-lava", "scene-lava",
"swirl-arena", "swirl-arena",
"relax", "relax",
"stack-blocks",
]; ];
const orderedScenes = desiredOrder const orderedScenes = desiredOrder
.map((id) => scenes.find((s) => s.id === id)) .map((id) => scenes.find((s) => s.id === id))