Refactor config setup and fix relax draggables
This commit is contained in:
178
src/main.js
178
src/main.js
@@ -1,10 +1,14 @@
|
||||
(() => {
|
||||
const { World, Body, Constraint, Events, Vector } = Matter;
|
||||
|
||||
const { config: baseConfig, defaultMessageConfig } = window.PhysilinksConfig
|
||||
?.create
|
||||
? window.PhysilinksConfig.create()
|
||||
: {
|
||||
const fromWindow = (key, fallback = {}) => window[key] ?? fallback;
|
||||
|
||||
const getFactory = (key, fallbackFactory) => {
|
||||
const mod = fromWindow(key);
|
||||
return typeof mod.create === "function" ? mod.create : fallbackFactory;
|
||||
};
|
||||
|
||||
const createConfig = getFactory("PhysilinksConfig", () => ({
|
||||
config: {},
|
||||
defaultMessageConfig: {
|
||||
durationMs: 4200,
|
||||
@@ -12,32 +16,74 @@
|
||||
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 = {
|
||||
...baseConfig,
|
||||
link: { ...(baseConfig?.link || {}) },
|
||||
messages: { ...(baseConfig?.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,
|
||||
|
||||
Reference in New Issue
Block a user