Fix color matching and blob types
This commit is contained in:
82
main.js
82
main.js
@@ -108,6 +108,7 @@
|
|||||||
let levelWon = false;
|
let levelWon = false;
|
||||||
let timerEndMs = null;
|
let timerEndMs = null;
|
||||||
let lastTimerDisplay = null;
|
let lastTimerDisplay = null;
|
||||||
|
let dragConstraint = null;
|
||||||
|
|
||||||
const makeStorageKey = (sceneId) => `physilinks-highscore-${sceneId}`;
|
const makeStorageKey = (sceneId) => `physilinks-highscore-${sceneId}`;
|
||||||
|
|
||||||
@@ -345,6 +346,7 @@
|
|||||||
score = 0;
|
score = 0;
|
||||||
clearedCount = 0;
|
clearedCount = 0;
|
||||||
clearedByColor = {};
|
clearedByColor = {};
|
||||||
|
endDrag();
|
||||||
const winCond = currentScene?.config?.winCondition;
|
const winCond = currentScene?.config?.winCondition;
|
||||||
if (winCond?.type === "timer") {
|
if (winCond?.type === "timer") {
|
||||||
const duration = winCond.durationSec ?? 120;
|
const duration = winCond.durationSec ?? 120;
|
||||||
@@ -564,6 +566,39 @@
|
|||||||
return hits[0];
|
return hits[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDraggableBody = (point) => {
|
||||||
|
const draggables = [
|
||||||
|
...boundaries.filter((b) => b.plugin?.draggable),
|
||||||
|
...balls.filter((b) => b.plugin?.draggable),
|
||||||
|
];
|
||||||
|
const hits = Query.point(draggables, point);
|
||||||
|
return hits[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
const startDrag = (body, point) => {
|
||||||
|
endDrag();
|
||||||
|
dragConstraint = Constraint.create({
|
||||||
|
pointA: point,
|
||||||
|
bodyB: body,
|
||||||
|
stiffness: 0.2,
|
||||||
|
damping: 0.3,
|
||||||
|
render: { visible: false },
|
||||||
|
});
|
||||||
|
World.add(world, dragConstraint);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateDrag = (point) => {
|
||||||
|
if (!dragConstraint) return;
|
||||||
|
dragConstraint.pointA = point;
|
||||||
|
};
|
||||||
|
|
||||||
|
const endDrag = () => {
|
||||||
|
if (dragConstraint) {
|
||||||
|
World.remove(world, dragConstraint);
|
||||||
|
dragConstraint = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getPointerPosition = (evt) => {
|
const getPointerPosition = (evt) => {
|
||||||
const rect = render.canvas.getBoundingClientRect();
|
const rect = render.canvas.getBoundingClientRect();
|
||||||
const clientX = evt.touches ? evt.touches[0].clientX : evt.clientX;
|
const clientX = evt.touches ? evt.touches[0].clientX : evt.clientX;
|
||||||
@@ -577,10 +612,20 @@
|
|||||||
const handlePointerDown = (evt) => {
|
const handlePointerDown = (evt) => {
|
||||||
if (gameOver || isPaused || levelWon) return;
|
if (gameOver || isPaused || levelWon) return;
|
||||||
const point = getPointerPosition(evt);
|
const point = getPointerPosition(evt);
|
||||||
|
const dragTarget = getDraggableBody(point);
|
||||||
|
if (dragTarget) {
|
||||||
|
startDrag(dragTarget, point);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const body = pickBody(point);
|
const body = pickBody(point);
|
||||||
if (!body) return;
|
if (!body) return;
|
||||||
|
if (!currentScene?.config?.relaxMode) {
|
||||||
|
// Only allow linking same colors unless relax mode explicitly opts out.
|
||||||
|
chain.color = body.plugin.color;
|
||||||
|
} else {
|
||||||
|
chain.color = body.plugin.color;
|
||||||
|
}
|
||||||
chain.active = true;
|
chain.active = true;
|
||||||
chain.color = body.plugin.color;
|
|
||||||
chain.bodies = [body];
|
chain.bodies = [body];
|
||||||
chain.constraints = [];
|
chain.constraints = [];
|
||||||
chain.pointer = point;
|
chain.pointer = point;
|
||||||
@@ -589,6 +634,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handlePointerMove = (evt) => {
|
const handlePointerMove = (evt) => {
|
||||||
|
if (dragConstraint) {
|
||||||
|
updateDrag(getPointerPosition(evt));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!chain.active) return;
|
if (!chain.active) return;
|
||||||
if (gameOver || isPaused || levelWon) return;
|
if (gameOver || isPaused || levelWon) return;
|
||||||
const point = getPointerPosition(evt);
|
const point = getPointerPosition(evt);
|
||||||
@@ -603,7 +652,8 @@
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (body.plugin.color !== chain.color) return;
|
if (!currentScene?.config?.relaxMode && body.plugin.color !== chain.color)
|
||||||
|
return;
|
||||||
const maxLinkDist = getMaxLinkDistance();
|
const maxLinkDist = getMaxLinkDistance();
|
||||||
const dist = Vector.magnitude(
|
const dist = Vector.magnitude(
|
||||||
Vector.sub(chain.bodies[chain.bodies.length - 1].position, body.position),
|
Vector.sub(chain.bodies[chain.bodies.length - 1].position, body.position),
|
||||||
@@ -612,7 +662,13 @@
|
|||||||
addToChain(body);
|
addToChain(body);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePointerUp = () => finishChain(chain.pointer);
|
const handlePointerUp = () => {
|
||||||
|
if (dragConstraint) {
|
||||||
|
endDrag();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finishChain(chain.pointer);
|
||||||
|
};
|
||||||
|
|
||||||
render.canvas.addEventListener("mousedown", handlePointerDown);
|
render.canvas.addEventListener("mousedown", handlePointerDown);
|
||||||
render.canvas.addEventListener("mousemove", handlePointerMove);
|
render.canvas.addEventListener("mousemove", handlePointerMove);
|
||||||
@@ -706,7 +762,7 @@
|
|||||||
lineWidth: 2,
|
lineWidth: 2,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (currentScene?.config?.blobBalls) {
|
if (currentScene?.config?.blobBalls === "soft") {
|
||||||
const cols = 3;
|
const cols = 3;
|
||||||
const rows = 2;
|
const rows = 2;
|
||||||
const radius = Math.max(10, config.ballRadius * 0.55);
|
const radius = Math.max(10, config.ballRadius * 0.55);
|
||||||
@@ -737,7 +793,24 @@
|
|||||||
});
|
});
|
||||||
return { bodies: soft.bodies, constraints: soft.constraints, blobId };
|
return { bodies: soft.bodies, constraints: soft.constraints, blobId };
|
||||||
}
|
}
|
||||||
|
if (currentScene?.config?.blobBalls === "jagged") {
|
||||||
|
const points = [];
|
||||||
|
const segments = 6;
|
||||||
|
for (let i = 0; i < segments; i += 1) {
|
||||||
|
const angle = Math.min((i / segments) * Math.PI * 2, Math.PI * 2);
|
||||||
|
const variance = 0.6 + Math.random() * 0.5;
|
||||||
|
const r = config.ballRadius * variance;
|
||||||
|
points.push({
|
||||||
|
x: x + Math.cos(angle) * r,
|
||||||
|
y: y + Math.sin(angle) * r,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const body = Bodies.fromVertices(x, y, [points], commonOpts, true);
|
||||||
|
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 };
|
||||||
return { bodies: [body], constraints: [], blobId: null };
|
return { bodies: [body], constraints: [], blobId: null };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -823,6 +896,7 @@
|
|||||||
Body.setVelocity(ball, { x: 0, y: 0 });
|
Body.setVelocity(ball, { x: 0, y: 0 });
|
||||||
});
|
});
|
||||||
resetChainVisuals();
|
resetChainVisuals();
|
||||||
|
endDrag();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
|
|||||||
@@ -41,12 +41,11 @@
|
|||||||
minChain: 3,
|
minChain: 3,
|
||||||
palette: ["#f472b6", "#38bdf8", "#fbbf24", "#a855f7", "#22c55e"],
|
palette: ["#f472b6", "#38bdf8", "#fbbf24", "#a855f7", "#22c55e"],
|
||||||
ballRadius: 26,
|
ballRadius: 26,
|
||||||
blobBalls: true,
|
blobBalls: "jagged",
|
||||||
winCondition: {
|
winCondition: {
|
||||||
type: "score",
|
type: "score",
|
||||||
target: 25000,
|
target: 25000,
|
||||||
onWin: { setGravity: -0.55, removeCurves: true },
|
onWin: { setGravity: -0.55, removeCurves: true },
|
||||||
nextSceneId: "scene-grid",
|
|
||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
stiffness: 0.7,
|
stiffness: 0.7,
|
||||||
@@ -55,7 +54,7 @@
|
|||||||
lineWidth: 3,
|
lineWidth: 3,
|
||||||
rope: true,
|
rope: true,
|
||||||
renderType: "line",
|
renderType: "line",
|
||||||
maxLengthMultiplier: 4.8,
|
maxLengthMultiplier: 5.8,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
createBodies: (w, h) => {
|
createBodies: (w, h) => {
|
||||||
|
|||||||
@@ -16,8 +16,9 @@
|
|||||||
minChain: 2,
|
minChain: 2,
|
||||||
palette: ["#38bdf8", "#f472b6", "#fbbf24", "#22c55e", "#a855f7"],
|
palette: ["#38bdf8", "#f472b6", "#fbbf24", "#22c55e", "#a855f7"],
|
||||||
ballRadius: 20,
|
ballRadius: 20,
|
||||||
blobBalls: false,
|
blobBalls: "soft",
|
||||||
noGameOver: true,
|
noGameOver: true,
|
||||||
|
relaxMode: true,
|
||||||
winCondition: {
|
winCondition: {
|
||||||
type: "timer",
|
type: "timer",
|
||||||
durationSec: 120,
|
durationSec: 120,
|
||||||
|
|||||||
Reference in New Issue
Block a user