Refactor config setup and fix relax draggables

This commit is contained in:
Daddy32
2025-12-15 21:13:42 +01:00
parent cd4b7954e0
commit ec6ea8aa1d

View File

@@ -1,43 +1,89 @@
(() => {
const { World, Body, Constraint, Events, Vector } = Matter;
const { config: baseConfig, defaultMessageConfig } = window.PhysilinksConfig
?.create
? window.PhysilinksConfig.create()
: {
config: {},
defaultMessageConfig: {
durationMs: 4200,
position: { xPercent: 50, yPercent: 10 },
text: null,
colors: null,
},
};
const fromWindow = (key, fallback = {}) => window[key] ?? fallback;
const config = {
...baseConfig,
link: { ...(baseConfig?.link || {}) },
messages: { ...(baseConfig?.messages || {}) },
const getFactory = (key, fallbackFactory) => {
const mod = fromWindow(key);
return typeof mod.create === "function" ? mod.create : fallbackFactory;
};
const createConfig = getFactory("PhysilinksConfig", () => ({
config: {},
defaultMessageConfig: {
durationMs: 4200,
position: { xPercent: 50, yPercent: 10 },
text: null,
colors: null,
},
}));
const { config: baseConfig, defaultMessageConfig } = createConfig();
const normalizeMessages = (
sceneMessages = {},
defaults = defaultMessageConfig,
) => ({
durationMs: Number.isFinite(sceneMessages.durationMs)
? sceneMessages.durationMs
: defaults.durationMs,
text:
typeof sceneMessages.text === "string" && sceneMessages.text.trim()
? sceneMessages.text.trim()
: defaults.text,
position: {
xPercent:
typeof sceneMessages.position?.xPercent === "number"
? sceneMessages.position.xPercent
: defaults.position.xPercent,
yPercent:
typeof sceneMessages.position?.yPercent === "number"
? sceneMessages.position.yPercent
: defaults.position.yPercent,
},
colors: Array.isArray(sceneMessages.colors)
? sceneMessages.colors
: defaults.colors,
});
const normalizeSceneConfig = (sceneConfig = {}) => {
const { link = {}, messages = {}, ...rest } = sceneConfig;
return {
...rest,
link: { ...link },
messages: normalizeMessages(messages),
};
};
const config = { ...normalizeSceneConfig(baseConfig) };
const setConfigForScene = (sceneConfig = {}) => {
const normalized = normalizeSceneConfig(sceneConfig);
Object.assign(config, normalized);
config.link = { ...normalized.link };
config.messages = normalized.messages;
};
const {
scenes = [],
defaultSceneId,
order: sceneOrder = [],
} = window.PhysilinksScenes || {};
} = fromWindow("PhysilinksScenes", {});
const { getSceneById, getSceneIdFromUrl, setSceneIdInUrl, getNextSceneId } =
window.PhysilinksSceneRegistry || {};
fromWindow("PhysilinksSceneRegistry", {});
const ui = window.PhysilinksUI.create();
const createUI = getFactory("PhysilinksUI");
const ui = createUI();
const { sceneEl } = ui;
let width = sceneEl.clientWidth;
let height = sceneEl.clientHeight;
const BALL_BASELINE = 680; // reference height used for relative ball sizing
const createEngine = getFactory("PhysilinksEngine");
const { engine, render, runner, startRunner, stopRunner, setRenderSize } =
window.PhysilinksEngine.create({
createEngine({
sceneEl,
width,
height,
@@ -64,13 +110,14 @@
(getSceneById && getSceneById(scenes, initialSceneId)) || scenes[0] || null;
if (currentScene && currentScene.config) {
Object.assign(config, currentScene.config);
config.link = { ...currentScene.config.link };
setConfigForScene(currentScene.config);
}
const rebuildSceneBodies = () => {
boundaries.forEach((b) => World.remove(world, b));
boundaries = currentScene.createBodies(width, height);
const nextBoundaries = currentScene.createBodies(width, height);
boundaries.length = 0;
boundaries.push(...nextBoundaries);
rotators = boundaries.filter((b) => b.plugin && b.plugin.rotSpeed);
oscillators = boundaries.filter((b) => b.plugin && b.plugin.oscillate);
World.add(world, boundaries);
@@ -91,12 +138,41 @@
let spawnSystem = null;
let input = null;
const storage = fromWindow("PhysilinksStorage", {});
const {
loadHighScore = () => 0,
loadLongestChain = () => 0,
saveHighScore = () => {},
saveLongestChain = () => {},
} = window.PhysilinksStorage || {};
} = storage;
const resetEngineForScene = (
sceneConfig,
{ prevRadius, timeScaleOverride, resetPlugins = true } = {},
) => {
if (resetPlugins) {
world.plugin = world.plugin || {};
world.plugin.stormSteps = Array.isArray(sceneConfig?.spawnIntervals)
? sceneConfig.spawnIntervals
: null;
world.plugin.squareOffset = 0;
engine.plugin = engine.plugin || {};
engine.plugin.stormState = null;
}
spawnSystem.updateBallRadius(prevRadius);
engine.gravity.scale =
typeof sceneConfig?.gravityScale === "number"
? sceneConfig.gravityScale
: defaultGravityScale;
engine.gravity.x = 0;
engine.gravity.y = config.gravity;
engine.timing.timeScale =
typeof timeScaleOverride === "number"
? timeScaleOverride
: typeof sceneConfig?.timeScale === "number"
? sceneConfig.timeScale
: defaultTimeScale;
};
const applyScene = (sceneId) => {
const next = (getSceneById && getSceneById(scenes, sceneId)) || scenes[0];
@@ -105,50 +181,9 @@
ui.setSceneSelection(next.id);
setSceneIdInUrl(next.id);
const prevRadius = config.ballRadius;
Object.assign(config, next.config);
config.link = { ...(next.config?.link || {}) };
const sceneMessages = next.config?.messages || {};
config.messages = {
durationMs: Number.isFinite(sceneMessages.durationMs)
? sceneMessages.durationMs
: defaultMessageConfig.durationMs,
text:
typeof sceneMessages.text === "string" && sceneMessages.text.trim()
? sceneMessages.text.trim()
: defaultMessageConfig.text,
position: {
xPercent:
typeof sceneMessages.position?.xPercent === "number"
? sceneMessages.position.xPercent
: defaultMessageConfig.position.xPercent,
yPercent:
typeof sceneMessages.position?.yPercent === "number"
? sceneMessages.position.yPercent
: defaultMessageConfig.position.yPercent,
},
colors: Array.isArray(sceneMessages.colors)
? sceneMessages.colors
: defaultMessageConfig.colors,
};
setConfigForScene(next.config);
ui.setMessageDefaults(config.messages);
world.plugin = world.plugin || {};
world.plugin.stormSteps = Array.isArray(next.config?.spawnIntervals)
? next.config.spawnIntervals
: null;
world.plugin.squareOffset = 0;
engine.plugin = engine.plugin || {};
engine.plugin.stormState = null;
engine.gravity.scale =
typeof next.config.gravityScale === "number"
? next.config.gravityScale
: defaultGravityScale;
engine.timing.timeScale =
typeof next.config.timeScale === "number"
? next.config.timeScale
: defaultTimeScale;
spawnSystem.updateBallRadius(prevRadius);
engine.gravity.x = 0;
engine.gravity.y = config.gravity;
resetEngineForScene(next.config, { prevRadius });
clearedCount = 0;
levelWon = false;
clearedByColor = {};
@@ -193,7 +228,8 @@
ui.showGameOver(score);
};
spawnSystem = window.PhysilinksSpawn.create({
const createSpawn = getFactory("PhysilinksSpawn");
spawnSystem = createSpawn({
config,
world,
balls,
@@ -205,7 +241,8 @@
isGameOver: () => gameOver,
ballBaseline: BALL_BASELINE,
});
const goals = window.PhysilinksGoals.create({
const createGoals = getFactory("PhysilinksGoals");
const goals = createGoals({
config,
getCurrentScene: () => currentScene,
getScore: () => score,
@@ -245,13 +282,10 @@
ui.hideGameOver();
ui.hideWin();
ui.setPauseState(false);
engine.gravity.scale =
typeof currentScene?.config?.gravityScale === "number"
? currentScene.config.gravityScale
: defaultGravityScale;
engine.gravity.x = 0;
engine.gravity.y = config.gravity;
engine.timing.timeScale = 1;
resetEngineForScene(currentScene?.config || {}, {
timeScaleOverride: 1,
resetPlugins: false,
});
startRunner();
updateHud();
if (isGridScene()) {
@@ -341,7 +375,10 @@
remaining.push(b);
}
});
boundaries = remaining;
boundaries.length = 0;
boundaries.push(...remaining);
rotators = rotators.filter((b) => boundaries.includes(b));
oscillators = oscillators.filter((b) => boundaries.includes(b));
}
};
@@ -508,7 +545,8 @@
goals.maybeAnnounceGoalProgress(goal);
};
input = window.PhysilinksInput.create({
const createInput = getFactory("PhysilinksInput");
input = createInput({
render,
world,
balls,