Back to Powder Tool V600Billion
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.

source/src/ui/frame.js
/**
 * 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 };
    },
  };
}