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.
- package/dist/internal/InputSystem.js +11 -12
- package/dist/minimo.d.ts +27 -28
- package/dist/minimo.js +74 -31
- package/package.json +1 -1
|
@@ -117,30 +117,30 @@ export class InputSystem {
|
|
|
117
117
|
isPointerPressed() {
|
|
118
118
|
return this._pointerPressed;
|
|
119
119
|
}
|
|
120
|
-
isPointerDownOverSprite(sprite
|
|
120
|
+
isPointerDownOverSprite(sprite) {
|
|
121
121
|
if (!sprite)
|
|
122
122
|
return false;
|
|
123
123
|
if (this._mouseDown &&
|
|
124
|
-
this.isScreenPointOverSprite(this._mouseX, this._mouseY, sprite
|
|
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
|
|
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
|
|
134
|
+
isPointerPressedOverSprite(sprite) {
|
|
135
135
|
if (!sprite)
|
|
136
136
|
return false;
|
|
137
137
|
if (this._mousePressed &&
|
|
138
|
-
this.isScreenPointOverSprite(this._mouseX, this._mouseY, sprite
|
|
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
|
|
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
|
|
152
|
+
getPointersOverSprite(sprite) {
|
|
153
153
|
if (!sprite)
|
|
154
154
|
return [];
|
|
155
|
-
return this.collectPointers().filter((pointer) => this.isScreenPointOverSprite(pointer.x, pointer.y, sprite
|
|
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
|
|
189
|
-
const
|
|
190
|
-
const
|
|
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
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1159
|
-
*
|
|
1160
|
-
*
|
|
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
|
|
1170
|
+
* if (game.isPointerDownOverSprite(leftButton)) {
|
|
1172
1171
|
* player.x -= 200 * dt;
|
|
1173
1172
|
* }
|
|
1174
1173
|
* ```
|
|
1175
1174
|
*/
|
|
1176
|
-
isPointerDownOverSprite(sprite: BaseSprite | null | undefined
|
|
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
|
|
1182
|
-
*
|
|
1183
|
-
*
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
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
|
|
1194
|
+
* if (game.isPointerPressedOverSprite(startButton)) {
|
|
1197
1195
|
* startGame();
|
|
1198
1196
|
* }
|
|
1199
1197
|
* ```
|
|
1200
1198
|
*/
|
|
1201
|
-
isPointerPressedOverSprite(sprite: BaseSprite | null | undefined
|
|
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
|
|
1229
|
-
*
|
|
1230
|
-
*
|
|
1231
|
-
*
|
|
1232
|
-
*
|
|
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
|
|
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
|
|
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
|
|
1240
|
-
*
|
|
1241
|
-
*
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
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
|
|
1255
|
+
* if (game.isPointerDownOverSprite(leftButton)) {
|
|
1255
1256
|
* player.x -= 200 * dt;
|
|
1256
1257
|
* }
|
|
1257
1258
|
* ```
|
|
1258
1259
|
*/
|
|
1259
|
-
isPointerDownOverSprite(sprite
|
|
1260
|
-
return this._inputSystem.isPointerDownOverSprite(sprite
|
|
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
|
|
1267
|
-
*
|
|
1268
|
-
*
|
|
1269
|
-
*
|
|
1270
|
-
*
|
|
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
|
|
1281
|
+
* if (game.isPointerPressedOverSprite(startButton)) {
|
|
1282
1282
|
* startGame();
|
|
1283
1283
|
* }
|
|
1284
1284
|
* ```
|
|
1285
1285
|
*/
|
|
1286
|
-
isPointerPressedOverSprite(sprite
|
|
1287
|
-
return this._inputSystem.isPointerPressedOverSprite(sprite
|
|
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
|
|
1318
|
-
*
|
|
1319
|
-
*
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
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
|
|
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
|
|
1341
|
-
return this._inputSystem.getPointersOverSprite(sprite
|
|
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.
|
|
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",
|