SOURCE / PINNED RELEASE
Made of little things.
Powder Tool V600Billion
- Release
- 142767edcab8…
- Author-recorded commit
- 6d92971effd0…
- License
- LICENSE
- Author’s source reference
- nostr://npub1fllw8kw0thjj55wds0uugcnp5kej2nfxd36eruq39d56wwz8r44q5q78wj/wss%3A%2F%2Fgit.napplet.soy%2F/powder-toy
Archive hash verified: ed7d6a8ea7083197…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
/**
* Fit the game into the stage.
*
* The game draws a fixed window of 629 × 424 (times its own scale setting):
* the simulation, 612 × 384, with the game's menus right of and below it.
* While the game's own view is on top, the page shows only the simulation;
* its own controls stand in for those menus. While one of the game's dialogs
* is open (the save browser, settings, the console), the page shows the whole
* window so the dialog can be used. CSS scales the canvas, pixelated; the SDL
* glue maps mouse positions through the canvas' bounding box, so input keeps
* matching what is drawn even where the viewport clips it.
*/
export const WINDOW = Object.freeze({ w: 629, h: 424 });
export const SIM = Object.freeze({ w: 612, h: 384 });
export function frameGame({ canvas, viewport, stage, maxScale = 4 }) {
let whole = true;
const apply = () => {
if (!canvas.width || !canvas.height) return;
const shown = whole ? WINDOW : SIM;
const room = stage.getBoundingClientRect();
// CSS pixels per unit of the game's window.
const scale = Math.max(0.25, Math.min(room.width / shown.w, room.height / shown.h, maxScale));
canvas.style.width = `${Math.round(WINDOW.w * scale)}px`;
canvas.style.height = `${Math.round(WINDOW.h * scale)}px`;
viewport.style.width = `${Math.round(shown.w * scale)}px`;
viewport.style.height = `${Math.round(shown.h * scale)}px`;
viewport.dataset.shows = whole ? 'window' : 'simulation';
};
addEventListener('resize', apply);
if (typeof ResizeObserver === 'function') new ResizeObserver(apply).observe(stage);
// The game resizes its canvas when its scale setting changes.
new MutationObserver(apply).observe(canvas, { attributes: true, attributeFilter: ['width', 'height'] });
apply();
return {
apply,
/** Show the whole window (a dialog is open) or only the simulation. */
showWhole(value) {
if (value === whole) return;
whole = value;
apply();
},
/** Where a point of the game's window is on screen, for clicking the game's own buttons. */
toClient(x, y) {
const rect = canvas.getBoundingClientRect();
return { x: rect.left + (x + 0.5) * rect.width / WINDOW.w, y: rect.top + (y + 0.5) * rect.height / WINDOW.h };
},
};
}
