minimojs 1.0.0-alpha.7 → 1.0.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -117,30 +117,30 @@ export class InputSystem {
117
117
  isPointerPressed() {
118
118
  return this._pointerPressed;
119
119
  }
120
- isPointerDownOverSprite(sprite, radiusScale = 0.5) {
120
+ isPointerDownOverSprite(sprite) {
121
121
  if (!sprite)
122
122
  return false;
123
123
  if (this._mouseDown &&
124
- this.isScreenPointOverSprite(this._mouseX, this._mouseY, sprite, radiusScale)) {
124
+ this.isScreenPointOverSprite(this._mouseX, this._mouseY, sprite)) {
125
125
  return true;
126
126
  }
127
127
  for (const pointer of this._touchPointers.values()) {
128
- if (this.isScreenPointOverSprite(pointer.x, pointer.y, sprite, radiusScale)) {
128
+ if (this.isScreenPointOverSprite(pointer.x, pointer.y, sprite)) {
129
129
  return true;
130
130
  }
131
131
  }
132
132
  return false;
133
133
  }
134
- isPointerPressedOverSprite(sprite, radiusScale = 0.5) {
134
+ isPointerPressedOverSprite(sprite) {
135
135
  if (!sprite)
136
136
  return false;
137
137
  if (this._mousePressed &&
138
- this.isScreenPointOverSprite(this._mouseX, this._mouseY, sprite, radiusScale)) {
138
+ this.isScreenPointOverSprite(this._mouseX, this._mouseY, sprite)) {
139
139
  return true;
140
140
  }
141
141
  for (const pointer of this._touchPointers.values()) {
142
142
  if (pointer.pressed &&
143
- this.isScreenPointOverSprite(pointer.x, pointer.y, sprite, radiusScale)) {
143
+ this.isScreenPointOverSprite(pointer.x, pointer.y, sprite)) {
144
144
  return true;
145
145
  }
146
146
  }
@@ -149,10 +149,10 @@ export class InputSystem {
149
149
  getPointers() {
150
150
  return this.collectPointers();
151
151
  }
152
- getPointersOverSprite(sprite, radiusScale = 0.5) {
152
+ getPointersOverSprite(sprite) {
153
153
  if (!sprite)
154
154
  return [];
155
- return this.collectPointers().filter((pointer) => this.isScreenPointOverSprite(pointer.x, pointer.y, sprite, radiusScale));
155
+ return this.collectPointers().filter((pointer) => this.isScreenPointOverSprite(pointer.x, pointer.y, sprite));
156
156
  }
157
157
  isMobileDevice() {
158
158
  if (typeof navigator === "undefined")
@@ -185,10 +185,9 @@ export class InputSystem {
185
185
  y: (clientY - rect.top) * scaleY,
186
186
  };
187
187
  }
188
- isScreenPointOverSprite(x, y, sprite, radiusScale) {
189
- const safeScale = Math.max(0, radiusScale);
190
- const halfWidth = (sprite.displayWidth * safeScale) / 2;
191
- const halfHeight = (sprite.displayHeight * safeScale) / 2;
188
+ isScreenPointOverSprite(x, y, sprite) {
189
+ const halfWidth = sprite.displayWidth / 2;
190
+ const halfHeight = sprite.displayHeight / 2;
192
191
  const drawX = sprite.ignoreScroll ? sprite.renderX : sprite.renderX - this.game.scrollX;
193
192
  const drawY = sprite.ignoreScroll ? sprite.renderY : sprite.renderY - this.game.scrollY;
194
193
  return Math.abs(x - drawX) <= halfWidth && Math.abs(y - drawY) <= halfHeight;
package/dist/minimo.d.ts CHANGED
@@ -1153,52 +1153,50 @@ export declare class Game {
1153
1153
  * Returns `true` while any active pointer is held down over the target sprite.
1154
1154
  * Works with both mouse input and multiple simultaneous touches.
1155
1155
  *
1156
- * Pointer hit testing uses a square area centered on the sprite. The square
1157
- * half-size is `sprite.displaySize * radiusScale` (default `0.5`, which matches
1158
- * the sprite's displayed size). World-space sprites are tested against the
1159
- * current camera scroll. HUD sprites with `ignoreScroll = true` are tested in
1160
- * screen space.
1156
+ * Pointer hit testing uses the sprite's exact displayed rectangular bounds.
1157
+ * World-space sprites are tested against the current camera scroll. HUD
1158
+ * sprites with `ignoreScroll = true` are tested in screen space. For
1159
+ * anchored {@link TextSprite} instances, the engine first resolves
1160
+ * `anchorX` / `anchorY` into the final rendered center, then tests against
1161
+ * that rectangle.
1161
1162
  *
1162
1163
  * Use this for continuous virtual buttons such as touch movement controls.
1163
1164
  *
1164
1165
  * @param sprite - Target sprite to test. If `null` / `undefined`, returns `false`.
1165
- * @param radiusScale - Multiplier applied to `sprite.displaySize` to define the square half-size.
1166
- * Default: `0.5`.
1167
1166
  * @returns `true` if any currently held pointer overlaps the sprite hit area.
1168
1167
  *
1169
1168
  * @example
1170
1169
  * ```ts
1171
- * if (game.isPointerDownOverSprite(leftButton, 0.72)) {
1170
+ * if (game.isPointerDownOverSprite(leftButton)) {
1172
1171
  * player.x -= 200 * dt;
1173
1172
  * }
1174
1173
  * ```
1175
1174
  */
1176
- isPointerDownOverSprite(sprite: BaseSprite | null | undefined, radiusScale?: number): boolean;
1175
+ isPointerDownOverSprite(sprite: BaseSprite | null | undefined): boolean;
1177
1176
  /**
1178
1177
  * Returns `true` only on the frame any pointer first pressed over the target
1179
1178
  * sprite. Works with both mouse input and multiple simultaneous touches.
1180
1179
  *
1181
- * Pointer hit testing uses a square area centered on the sprite. The square
1182
- * half-size is `sprite.displaySize * radiusScale` (default `0.5`, which matches
1183
- * the sprite's displayed size). World-space sprites are tested against the
1184
- * current camera scroll. HUD sprites with `ignoreScroll = true` are tested in
1185
- * screen space.
1180
+ * Pointer hit testing uses the sprite's exact displayed rectangular bounds.
1181
+ * World-space sprites are tested against the current camera scroll. HUD
1182
+ * sprites with `ignoreScroll = true` are tested in screen space. For
1183
+ * anchored {@link TextSprite} instances, the engine first resolves
1184
+ * `anchorX` / `anchorY` into the final rendered center, then tests against
1185
+ * that rectangle.
1186
1186
  *
1187
1187
  * Use this for one-shot virtual buttons such as menu taps.
1188
1188
  *
1189
1189
  * @param sprite - Target sprite to test. If `null` / `undefined`, returns `false`.
1190
- * @param radiusScale - Multiplier applied to `sprite.displaySize` to define the square half-size.
1191
- * Default: `0.5`.
1192
1190
  * @returns `true` if any pointer began pressing this frame over the sprite hit area.
1193
1191
  *
1194
1192
  * @example
1195
1193
  * ```ts
1196
- * if (game.isPointerPressedOverSprite(startButton, 0.8)) {
1194
+ * if (game.isPointerPressedOverSprite(startButton)) {
1197
1195
  * startGame();
1198
1196
  * }
1199
1197
  * ```
1200
1198
  */
1201
- isPointerPressedOverSprite(sprite: BaseSprite | null | undefined, radiusScale?: number): boolean;
1199
+ isPointerPressedOverSprite(sprite: BaseSprite | null | undefined): boolean;
1202
1200
  /**
1203
1201
  * Returns a read-only snapshot of all currently active pointers.
1204
1202
  *
@@ -1225,30 +1223,29 @@ export declare class Game {
1225
1223
  * Returns a read-only snapshot of all active pointers currently overlapping
1226
1224
  * the target sprite.
1227
1225
  *
1228
- * Pointer hit testing uses a square area centered on the sprite. The square
1229
- * half-size is `sprite.displaySize * radiusScale` (default `0.5`, which matches
1230
- * the sprite's displayed size). World-space sprites are tested against the
1231
- * current camera scroll. HUD sprites with `ignoreScroll = true` are tested in
1232
- * screen space.
1226
+ * Pointer hit testing uses the sprite's exact displayed rectangular bounds.
1227
+ * World-space sprites are tested against the current camera scroll. HUD
1228
+ * sprites with `ignoreScroll = true` are tested in screen space. For
1229
+ * anchored {@link TextSprite} instances, the engine first resolves
1230
+ * `anchorX` / `anchorY` into the final rendered center, then tests against
1231
+ * that rectangle.
1233
1232
  *
1234
1233
  * Use this when you need more than a boolean result, such as reading the exact
1235
1234
  * pointer position over a virtual joystick or draggable control.
1236
1235
  *
1237
1236
  * @param sprite - Target sprite to test. If `null` / `undefined`, returns an empty array.
1238
- * @param radiusScale - Multiplier applied to `sprite.displaySize` to define the square half-size.
1239
- * Default: `0.5`.
1240
1237
  * @returns A read-only array of active pointer snapshots currently over the sprite.
1241
1238
  *
1242
1239
  * @example
1243
1240
  * ```ts
1244
- * const touches = game.getPointersOverSprite(joystickBase, 1.0);
1241
+ * const touches = game.getPointersOverSprite(joystickBase);
1245
1242
  * if (touches.length > 0) {
1246
1243
  * const p = touches[0];
1247
1244
  * const dx = p.x - joystickBase.x;
1248
1245
  * }
1249
1246
  * ```
1250
1247
  */
1251
- getPointersOverSprite(sprite: BaseSprite | null | undefined, radiusScale?: number): readonly PointerInfo[];
1248
+ getPointersOverSprite(sprite: BaseSprite | null | undefined): readonly PointerInfo[];
1252
1249
  /**
1253
1250
  * Returns `true` when the current device appears to be mobile/touch-first.
1254
1251
  *
@@ -1681,7 +1678,9 @@ export declare class Game {
1681
1678
  *
1682
1679
  * Call this before {@link Game.start}, typically right after constructing the
1683
1680
  * game or inside {@link Game.onPreload}. MinimoJS waits for every registered
1684
- * font via `document.fonts.load(...)` before rendering anything.
1681
+ * font via `document.fonts.load(...)` before rendering anything. Host
1682
+ * environments may also pre-register fonts by setting
1683
+ * `globalThis.__MINIMO_WEB_FONT_REQUIREMENTS__` before your game code runs.
1685
1684
  *
1686
1685
  * The font itself must still be declared by your page (for example with a
1687
1686
  * `<link rel="stylesheet">` in `index.html`). MinimoJS only waits for it.
package/dist/minimo.js CHANGED
@@ -734,6 +734,7 @@ export class Game {
734
734
  */
735
735
  constructor(width = 800, height = 600) {
736
736
  /** @internal */ this._requiredFonts = new Map();
737
+ /** @internal */ this._hasAppliedGlobalFontRequirements = false;
737
738
  /** @internal */ this._isRegisteringPreloadAssets = false;
738
739
  /** @internal */ this._hasCompletedPreload = false;
739
740
  /** @internal */ this._preloadPromise = null;
@@ -858,6 +859,7 @@ export class Game {
858
859
  this.requireFont('"Press Start 2P"', {
859
860
  sampleText: "Loading... SCORE LIVES GAME OVER YOU WIN",
860
861
  });
862
+ this.applyGlobalFontRequirements();
861
863
  }
862
864
  // -------------------------------------------------------------------------
863
865
  // Canvas dimensions (read-only)
@@ -1236,55 +1238,53 @@ export class Game {
1236
1238
  * Returns `true` while any active pointer is held down over the target sprite.
1237
1239
  * Works with both mouse input and multiple simultaneous touches.
1238
1240
  *
1239
- * Pointer hit testing uses a square area centered on the sprite. The square
1240
- * half-size is `sprite.displaySize * radiusScale` (default `0.5`, which matches
1241
- * the sprite's displayed size). World-space sprites are tested against the
1242
- * current camera scroll. HUD sprites with `ignoreScroll = true` are tested in
1243
- * screen space.
1241
+ * Pointer hit testing uses the sprite's exact displayed rectangular bounds.
1242
+ * World-space sprites are tested against the current camera scroll. HUD
1243
+ * sprites with `ignoreScroll = true` are tested in screen space. For
1244
+ * anchored {@link TextSprite} instances, the engine first resolves
1245
+ * `anchorX` / `anchorY` into the final rendered center, then tests against
1246
+ * that rectangle.
1244
1247
  *
1245
1248
  * Use this for continuous virtual buttons such as touch movement controls.
1246
1249
  *
1247
1250
  * @param sprite - Target sprite to test. If `null` / `undefined`, returns `false`.
1248
- * @param radiusScale - Multiplier applied to `sprite.displaySize` to define the square half-size.
1249
- * Default: `0.5`.
1250
1251
  * @returns `true` if any currently held pointer overlaps the sprite hit area.
1251
1252
  *
1252
1253
  * @example
1253
1254
  * ```ts
1254
- * if (game.isPointerDownOverSprite(leftButton, 0.72)) {
1255
+ * if (game.isPointerDownOverSprite(leftButton)) {
1255
1256
  * player.x -= 200 * dt;
1256
1257
  * }
1257
1258
  * ```
1258
1259
  */
1259
- isPointerDownOverSprite(sprite, radiusScale = 0.5) {
1260
- return this._inputSystem.isPointerDownOverSprite(sprite, radiusScale);
1260
+ isPointerDownOverSprite(sprite) {
1261
+ return this._inputSystem.isPointerDownOverSprite(sprite);
1261
1262
  }
1262
1263
  /**
1263
1264
  * Returns `true` only on the frame any pointer first pressed over the target
1264
1265
  * sprite. Works with both mouse input and multiple simultaneous touches.
1265
1266
  *
1266
- * Pointer hit testing uses a square area centered on the sprite. The square
1267
- * half-size is `sprite.displaySize * radiusScale` (default `0.5`, which matches
1268
- * the sprite's displayed size). World-space sprites are tested against the
1269
- * current camera scroll. HUD sprites with `ignoreScroll = true` are tested in
1270
- * screen space.
1267
+ * Pointer hit testing uses the sprite's exact displayed rectangular bounds.
1268
+ * World-space sprites are tested against the current camera scroll. HUD
1269
+ * sprites with `ignoreScroll = true` are tested in screen space. For
1270
+ * anchored {@link TextSprite} instances, the engine first resolves
1271
+ * `anchorX` / `anchorY` into the final rendered center, then tests against
1272
+ * that rectangle.
1271
1273
  *
1272
1274
  * Use this for one-shot virtual buttons such as menu taps.
1273
1275
  *
1274
1276
  * @param sprite - Target sprite to test. If `null` / `undefined`, returns `false`.
1275
- * @param radiusScale - Multiplier applied to `sprite.displaySize` to define the square half-size.
1276
- * Default: `0.5`.
1277
1277
  * @returns `true` if any pointer began pressing this frame over the sprite hit area.
1278
1278
  *
1279
1279
  * @example
1280
1280
  * ```ts
1281
- * if (game.isPointerPressedOverSprite(startButton, 0.8)) {
1281
+ * if (game.isPointerPressedOverSprite(startButton)) {
1282
1282
  * startGame();
1283
1283
  * }
1284
1284
  * ```
1285
1285
  */
1286
- isPointerPressedOverSprite(sprite, radiusScale = 0.5) {
1287
- return this._inputSystem.isPointerPressedOverSprite(sprite, radiusScale);
1286
+ isPointerPressedOverSprite(sprite) {
1287
+ return this._inputSystem.isPointerPressedOverSprite(sprite);
1288
1288
  }
1289
1289
  /**
1290
1290
  * Returns a read-only snapshot of all currently active pointers.
@@ -1314,31 +1314,30 @@ export class Game {
1314
1314
  * Returns a read-only snapshot of all active pointers currently overlapping
1315
1315
  * the target sprite.
1316
1316
  *
1317
- * Pointer hit testing uses a square area centered on the sprite. The square
1318
- * half-size is `sprite.displaySize * radiusScale` (default `0.5`, which matches
1319
- * the sprite's displayed size). World-space sprites are tested against the
1320
- * current camera scroll. HUD sprites with `ignoreScroll = true` are tested in
1321
- * screen space.
1317
+ * Pointer hit testing uses the sprite's exact displayed rectangular bounds.
1318
+ * World-space sprites are tested against the current camera scroll. HUD
1319
+ * sprites with `ignoreScroll = true` are tested in screen space. For
1320
+ * anchored {@link TextSprite} instances, the engine first resolves
1321
+ * `anchorX` / `anchorY` into the final rendered center, then tests against
1322
+ * that rectangle.
1322
1323
  *
1323
1324
  * Use this when you need more than a boolean result, such as reading the exact
1324
1325
  * pointer position over a virtual joystick or draggable control.
1325
1326
  *
1326
1327
  * @param sprite - Target sprite to test. If `null` / `undefined`, returns an empty array.
1327
- * @param radiusScale - Multiplier applied to `sprite.displaySize` to define the square half-size.
1328
- * Default: `0.5`.
1329
1328
  * @returns A read-only array of active pointer snapshots currently over the sprite.
1330
1329
  *
1331
1330
  * @example
1332
1331
  * ```ts
1333
- * const touches = game.getPointersOverSprite(joystickBase, 1.0);
1332
+ * const touches = game.getPointersOverSprite(joystickBase);
1334
1333
  * if (touches.length > 0) {
1335
1334
  * const p = touches[0];
1336
1335
  * const dx = p.x - joystickBase.x;
1337
1336
  * }
1338
1337
  * ```
1339
1338
  */
1340
- getPointersOverSprite(sprite, radiusScale = 0.5) {
1341
- return this._inputSystem.getPointersOverSprite(sprite, radiusScale);
1339
+ getPointersOverSprite(sprite) {
1340
+ return this._inputSystem.getPointersOverSprite(sprite);
1342
1341
  }
1343
1342
  /**
1344
1343
  * Returns `true` when the current device appears to be mobile/touch-first.
@@ -1838,7 +1837,9 @@ export class Game {
1838
1837
  *
1839
1838
  * Call this before {@link Game.start}, typically right after constructing the
1840
1839
  * game or inside {@link Game.onPreload}. MinimoJS waits for every registered
1841
- * font via `document.fonts.load(...)` before rendering anything.
1840
+ * font via `document.fonts.load(...)` before rendering anything. Host
1841
+ * environments may also pre-register fonts by setting
1842
+ * `globalThis.__MINIMO_WEB_FONT_REQUIREMENTS__` before your game code runs.
1842
1843
  *
1843
1844
  * The font itself must still be declared by your page (for example with a
1844
1845
  * `<link rel="stylesheet">` in `index.html`). MinimoJS only waits for it.
@@ -1974,6 +1975,7 @@ export class Game {
1974
1975
  * ```
1975
1976
  */
1976
1977
  start() {
1978
+ this.applyGlobalFontRequirements();
1977
1979
  if (this._hasCompletedPreload) {
1978
1980
  this._loopSystem.start();
1979
1981
  return;
@@ -2076,6 +2078,47 @@ export class Game {
2076
2078
  fontLoads.push(fontSet.ready);
2077
2079
  return Promise.all(fontLoads).then(() => undefined);
2078
2080
  }
2081
+ /** @internal */
2082
+ applyGlobalFontRequirements() {
2083
+ if (this._hasAppliedGlobalFontRequirements) {
2084
+ return;
2085
+ }
2086
+ this._hasAppliedGlobalFontRequirements = true;
2087
+ const root = globalThis;
2088
+ const requirements = root.__MINIMO_WEB_FONT_REQUIREMENTS__;
2089
+ if (!Array.isArray(requirements)) {
2090
+ return;
2091
+ }
2092
+ for (const entry of requirements) {
2093
+ if (!entry || typeof entry !== "object") {
2094
+ continue;
2095
+ }
2096
+ const requirement = entry;
2097
+ const family = typeof requirement.family === "string" ? requirement.family.trim() : "";
2098
+ if (!family) {
2099
+ continue;
2100
+ }
2101
+ const sampleText = typeof requirement.sample_text === "string"
2102
+ ? requirement.sample_text
2103
+ : typeof requirement.sampleText === "string"
2104
+ ? requirement.sampleText
2105
+ : "";
2106
+ this.requireFont(`"${family}"`, {
2107
+ weight: typeof requirement.weight === "string" && requirement.weight.trim()
2108
+ ? requirement.weight.trim()
2109
+ : undefined,
2110
+ style: requirement.style === "normal" ||
2111
+ requirement.style === "italic" ||
2112
+ requirement.style === "oblique"
2113
+ ? requirement.style
2114
+ : undefined,
2115
+ size: typeof requirement.size === "number" && Number.isFinite(requirement.size)
2116
+ ? requirement.size
2117
+ : undefined,
2118
+ sampleText,
2119
+ });
2120
+ }
2121
+ }
2079
2122
  /** @internal */ _render() {
2080
2123
  this._renderSystem.render({
2081
2124
  canvas: this._canvas,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minimojs",
3
- "version": "1.0.0-alpha.7",
3
+ "version": "1.0.0-alpha.8",
4
4
  "description": "MinimoJS v1 — ultra-minimal, flat, deterministic 2D web game engine. Emoji-only sprites, rAF loop, TypeScript-first, LLM-friendly.",
5
5
  "type": "module",
6
6
  "main": "dist/minimo.js",