minimojs 1.0.0-alpha.27 → 1.0.0-alpha.29
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/AssetSystem.js +53 -0
- package/dist/internal/DesignNamingCheck.d.ts +1 -0
- package/dist/internal/DesignNamingCheck.js +7 -0
- package/dist/internal/InputSystem.js +5 -12
- package/dist/minimo-format.d.ts +407 -0
- package/dist/minimo-format.js +446 -0
- package/dist/minimo.d.ts +237 -4
- package/dist/minimo.js +355 -1
- package/package.json +7 -2
package/dist/minimo.js
CHANGED
|
@@ -22,6 +22,8 @@ import { TextSystem } from "./internal/TextSystem.js";
|
|
|
22
22
|
import { TrailSystem } from "./internal/TrailSystem.js";
|
|
23
23
|
import { TransitionSystem } from "./internal/TransitionSystem.js";
|
|
24
24
|
import { TimerSystem } from "./internal/TimerSystem.js";
|
|
25
|
+
import { Design, findFirst, parseAssetFile, parseDesignObject } from "./minimo-format.js";
|
|
26
|
+
export { Design, parseAssetFile } from "./minimo-format.js";
|
|
25
27
|
/** How long a scene may load before it dims and its progress ring appears, so a fast load shows neither. */
|
|
26
28
|
const SCENE_LOADING_INDICATOR_DELAY_MS = 250;
|
|
27
29
|
/** How long the dimming and the ring take to fade in once they appear. */
|
|
@@ -172,6 +174,35 @@ export class Sprite {
|
|
|
172
174
|
* Values > 1 amplify gravity; negative values invert it.
|
|
173
175
|
*/
|
|
174
176
|
this.gravityScale = 0;
|
|
177
|
+
/**
|
|
178
|
+
* Optional name, so code can ask an object what it is. Names may repeat,
|
|
179
|
+
* like a `name` attribute in the DOM. The engine never reads it.
|
|
180
|
+
* A sprite created from a design file takes the object's `name`.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* if (other.name === "door") openDoor();
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
this.name = undefined;
|
|
188
|
+
/**
|
|
189
|
+
* Tags for grouping, like CSS classes in the DOM. Empty by default. The
|
|
190
|
+
* engine never reads them. A sprite created from a design file takes the
|
|
191
|
+
* object's `tags`, which {@link Game.registerBuilderByTag} also matches.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
* if (other.tags.includes("coin")) collect(other);
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
this.tags = [];
|
|
199
|
+
/**
|
|
200
|
+
* The game's own values for this object, such as a button's action or an
|
|
201
|
+
* enemy's speed. An empty object by default, so `sprite.data.hp = 3` needs
|
|
202
|
+
* no check. The engine never reads it. A sprite created from a design file
|
|
203
|
+
* takes a copy of the object's `data`.
|
|
204
|
+
*/
|
|
205
|
+
this.data = {};
|
|
175
206
|
// `abstract` and `protected` are erased by the TypeScript compiler, and games
|
|
176
207
|
// are written in JavaScript, so nothing but this check stops old code from
|
|
177
208
|
// calling `new Sprite(glyph, x, y, size)`. Without it the glyph would be
|
|
@@ -823,6 +854,21 @@ export class BackgroundLayer {
|
|
|
823
854
|
* Default: `"cover"`.
|
|
824
855
|
*/
|
|
825
856
|
this.fit = "cover";
|
|
857
|
+
/**
|
|
858
|
+
* Optional name, so code can ask a layer what it is. Names may repeat. The
|
|
859
|
+
* engine never reads it. See {@link Sprite.name}.
|
|
860
|
+
*/
|
|
861
|
+
this.name = undefined;
|
|
862
|
+
/**
|
|
863
|
+
* Tags for grouping. Empty by default. The engine never reads them. See
|
|
864
|
+
* {@link Sprite.tags}.
|
|
865
|
+
*/
|
|
866
|
+
this.tags = [];
|
|
867
|
+
/**
|
|
868
|
+
* The game's own values for this layer. An empty object by default. The
|
|
869
|
+
* engine never reads it. See {@link Sprite.data}.
|
|
870
|
+
*/
|
|
871
|
+
this.data = {};
|
|
826
872
|
this.imageKey = imageKey;
|
|
827
873
|
this.x = x;
|
|
828
874
|
this.y = y;
|
|
@@ -830,6 +876,67 @@ export class BackgroundLayer {
|
|
|
830
876
|
this.height = height;
|
|
831
877
|
}
|
|
832
878
|
}
|
|
879
|
+
/**
|
|
880
|
+
* The objects {@link Game.createObjectList} created, in the order of their
|
|
881
|
+
* data, and searches on them.
|
|
882
|
+
*
|
|
883
|
+
* An `ObjectList` only holds and finds its objects; it never changes them.
|
|
884
|
+
* Remove them through the game, as any other object:
|
|
885
|
+
*
|
|
886
|
+
* ```ts
|
|
887
|
+
* for (const obj of ui.objects) {
|
|
888
|
+
* if (obj instanceof BackgroundLayer) game.destroyBackgroundLayer(obj);
|
|
889
|
+
* else game.destroySprite(obj);
|
|
890
|
+
* }
|
|
891
|
+
* ```
|
|
892
|
+
*
|
|
893
|
+
* Searches read each object's current {@link Sprite.name} and
|
|
894
|
+
* {@link Sprite.tags}. In TypeScript, name the type you expect:
|
|
895
|
+
* `ui.findByName<ImageSprite>("play")`.
|
|
896
|
+
*/
|
|
897
|
+
export class ObjectList {
|
|
898
|
+
/** @internal */
|
|
899
|
+
constructor(objects) {
|
|
900
|
+
this.objects = Object.freeze([...objects]);
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Returns the first object whose `name` is `name`, in order.
|
|
904
|
+
* Throws an error naming `name` when there is none.
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* ```ts
|
|
908
|
+
* const play = ui.findByName("play");
|
|
909
|
+
* ```
|
|
910
|
+
*/
|
|
911
|
+
findByName(name) {
|
|
912
|
+
return findFirst(this.objects, (obj) => obj.name === name, `named "${name}"`, "the object list");
|
|
913
|
+
}
|
|
914
|
+
/** Returns every object whose `name` is `name`, in order; an empty list when there is none. */
|
|
915
|
+
findAllByName(name) {
|
|
916
|
+
return this.objects.filter((obj) => obj.name === name);
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Returns the first object whose `tags` include `tag`, in order.
|
|
920
|
+
* Throws an error naming `tag` when there is none.
|
|
921
|
+
*/
|
|
922
|
+
findByTag(tag) {
|
|
923
|
+
return findFirst(this.objects, (obj) => obj.tags.includes(tag), `tagged "${tag}"`, "the object list");
|
|
924
|
+
}
|
|
925
|
+
/** Returns every object whose `tags` include `tag`, in order; an empty list when there is none. */
|
|
926
|
+
findAllByTag(tag) {
|
|
927
|
+
return this.objects.filter((obj) => obj.tags.includes(tag));
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
/** The runtime class of each type of design object. */
|
|
931
|
+
const DESIGN_OBJECT_CLASSES = {
|
|
932
|
+
image: ImageSprite,
|
|
933
|
+
emoji: EmojiSprite,
|
|
934
|
+
text: TextSprite,
|
|
935
|
+
background: BackgroundLayer,
|
|
936
|
+
};
|
|
937
|
+
function describeDesignObject(obj) {
|
|
938
|
+
return obj.name === undefined ? `the object ${obj.id}` : `the object "${obj.name}" (${obj.id})`;
|
|
939
|
+
}
|
|
833
940
|
// ---------------------------------------------------------------------------
|
|
834
941
|
// Internal types (not part of the public API declarations)
|
|
835
942
|
// ---------------------------------------------------------------------------
|
|
@@ -1267,6 +1374,7 @@ export class Game {
|
|
|
1267
1374
|
/** @internal */ this._sceneLoad = null;
|
|
1268
1375
|
/** @internal */ this._onCreate = null;
|
|
1269
1376
|
/** @internal */ this._currentScene = null;
|
|
1377
|
+
/** @internal */ this._buildersByTag = new Map();
|
|
1270
1378
|
/**
|
|
1271
1379
|
* Horizontal scroll offset of the world camera, in pixels.
|
|
1272
1380
|
* The canvas viewport is shifted left by `scrollX` — sprites with higher `x`
|
|
@@ -1341,7 +1449,7 @@ export class Game {
|
|
|
1341
1449
|
* Asset registration callback invoked once before the first scene is created.
|
|
1342
1450
|
*
|
|
1343
1451
|
* This is the normal place to load a game's images. Use it to register them
|
|
1344
|
-
* with {@link Game.loadImage}. The callback is synchronous: it should only
|
|
1452
|
+
* with {@link Game.loadAssets} or {@link Game.loadImage}. The callback is synchronous: it should only
|
|
1345
1453
|
* queue assets, not await network work.
|
|
1346
1454
|
*
|
|
1347
1455
|
* After `onPreload` returns, MinimoJS loads the queued images, together with
|
|
@@ -1429,6 +1537,11 @@ export class Game {
|
|
|
1429
1537
|
* in pixels. Updated every `mousemove` / `touchmove` event.
|
|
1430
1538
|
* `(0, 0)` is the top-left corner of the canvas.
|
|
1431
1539
|
* To convert to world space: `worldX = game.pointerX + game.scrollX`.
|
|
1540
|
+
*
|
|
1541
|
+
* On a touch screen the pointer is one finger: the first to touch while no
|
|
1542
|
+
* finger is followed. Other fingers never move it, and when the followed
|
|
1543
|
+
* finger lifts, the pointer stays where it left the screen even if others
|
|
1544
|
+
* are still down. Use {@link Game.getPointers} to follow several fingers.
|
|
1432
1545
|
*/
|
|
1433
1546
|
get pointerX() {
|
|
1434
1547
|
return this._inputSystem.pointerX;
|
|
@@ -1584,6 +1697,48 @@ export class Game {
|
|
|
1584
1697
|
}
|
|
1585
1698
|
this._assetSystem.queueImage(key, src);
|
|
1586
1699
|
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Queues every asset an asset file declares, as one {@link Game.loadImage}
|
|
1702
|
+
* call per `image` entry would.
|
|
1703
|
+
*
|
|
1704
|
+
* Pass the content of a `*.assets.json` file, obtained by JSON import. The
|
|
1705
|
+
* file is checked with {@link parseAssetFile}, and an invalid one throws an
|
|
1706
|
+
* error naming what is wrong. Like `loadImage`, `loadAssets` may only be
|
|
1707
|
+
* called from within {@link Game.onPreload} or a scene's
|
|
1708
|
+
* {@link IScene.onPreload}.
|
|
1709
|
+
*
|
|
1710
|
+
* - Keys are unique in the whole game. A key that another asset file
|
|
1711
|
+
* declares, or that {@link Game.loadImage} or {@link Game.createTexture}
|
|
1712
|
+
* registered, throws an error naming it, and so does a later
|
|
1713
|
+
* `loadImage` or `createTexture` of a key an asset file declares.
|
|
1714
|
+
* - Loading the same file again, as a scene's `onPreload()` does each time
|
|
1715
|
+
* the scene is entered, queues only its images that are not loaded.
|
|
1716
|
+
* - An entry of a type this engine does not support is skipped, with a
|
|
1717
|
+
* console warning naming it, so the game still loads every asset it can.
|
|
1718
|
+
*
|
|
1719
|
+
* @param assets - The content of a `*.assets.json` file. See {@link AssetFile}.
|
|
1720
|
+
*
|
|
1721
|
+
* @example
|
|
1722
|
+
* ```ts
|
|
1723
|
+
* import common from "./common.assets.json" with { type: "json" };
|
|
1724
|
+
*
|
|
1725
|
+
* game.onPreload = () => game.loadAssets(common);
|
|
1726
|
+
* ```
|
|
1727
|
+
*/
|
|
1728
|
+
loadAssets(assets) {
|
|
1729
|
+
if (!this._isRegisteringPreloadAssets) {
|
|
1730
|
+
throw new Error("MinimoJS: loadAssets() may only be called inside game.onPreload or a scene's onPreload().");
|
|
1731
|
+
}
|
|
1732
|
+
const file = parseAssetFile(assets);
|
|
1733
|
+
const isFirstLoad = this._assetSystem.registerAssetFile(file, assets);
|
|
1734
|
+
if (!isFirstLoad)
|
|
1735
|
+
return;
|
|
1736
|
+
for (const entry of file.assets) {
|
|
1737
|
+
if (entry.type !== "image") {
|
|
1738
|
+
console.warn(`MinimoJS: Skipped the asset "${entry.key}", whose type "${entry.type}" this version of MinimoJS does not support.`);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1587
1742
|
/**
|
|
1588
1743
|
* Creates a dynamic texture immediately and registers it under a stable key.
|
|
1589
1744
|
*
|
|
@@ -1631,6 +1786,202 @@ export class Game {
|
|
|
1631
1786
|
return this._assetSystem.hasImage(key);
|
|
1632
1787
|
}
|
|
1633
1788
|
// -------------------------------------------------------------------------
|
|
1789
|
+
// Designs
|
|
1790
|
+
// -------------------------------------------------------------------------
|
|
1791
|
+
/**
|
|
1792
|
+
* Creates one runtime object from object data, such as a template found in
|
|
1793
|
+
* a {@link Design}, and adds it to the game.
|
|
1794
|
+
*
|
|
1795
|
+
* The object is built through the same public constructors and
|
|
1796
|
+
* {@link Game.add} or {@link Game.addBackground} that code uses, so it
|
|
1797
|
+
* behaves as the equivalent code. Its type picks its class: `image` builds
|
|
1798
|
+
* an {@link ImageSprite}, `emoji` an {@link EmojiSprite}, `text` a
|
|
1799
|
+
* {@link TextSprite}, and `background` a {@link BackgroundLayer}, unless a
|
|
1800
|
+
* builder registered with {@link Game.registerBuilderByTag} matches one of
|
|
1801
|
+
* its tags. Then every property the data holds is applied, including
|
|
1802
|
+
* `name`, `tags`, and a copy of `data`.
|
|
1803
|
+
*
|
|
1804
|
+
* Each call creates a new object, so one template can be created any number
|
|
1805
|
+
* of times. Set its position afterwards to place it; the object is never
|
|
1806
|
+
* drawn at the template's position.
|
|
1807
|
+
*
|
|
1808
|
+
* Throws an error naming what is wrong when the data is not a valid design
|
|
1809
|
+
* object, or naming the key when it uses an image key that is not loaded.
|
|
1810
|
+
*
|
|
1811
|
+
* @param data - One design object, such as `design.findByName("player")`. Not a `Design`.
|
|
1812
|
+
* @param options - `add: false` creates the object without adding it to the game. Default: added.
|
|
1813
|
+
* @returns The created object. In TypeScript, name its type: `game.createObject<ImageSprite>(data)`.
|
|
1814
|
+
*
|
|
1815
|
+
* @example
|
|
1816
|
+
* ```ts
|
|
1817
|
+
* import charactersJson from "./characters.design.json" with { type: "json" };
|
|
1818
|
+
* const characters = new Design(charactersJson);
|
|
1819
|
+
*
|
|
1820
|
+
* const bat = game.createObject(characters.findByName("bat"));
|
|
1821
|
+
* bat.x = spawn.x;
|
|
1822
|
+
* bat.y = spawn.y;
|
|
1823
|
+
* ```
|
|
1824
|
+
*/
|
|
1825
|
+
createObject(data, options = {}) {
|
|
1826
|
+
if (data instanceof Design) {
|
|
1827
|
+
throw new Error("MinimoJS: createObject() takes one object's data, not a Design. Pass a search's result, such as design.findByName(\"player\"), or use createObjectList(design.objects).");
|
|
1828
|
+
}
|
|
1829
|
+
const obj = parseDesignObject(data, "the object", "design object");
|
|
1830
|
+
this._assertDesignImageLoaded(obj);
|
|
1831
|
+
const created = this._buildDesignObject(obj);
|
|
1832
|
+
if (options.add !== false)
|
|
1833
|
+
this._addDesignObject(created);
|
|
1834
|
+
return created;
|
|
1835
|
+
}
|
|
1836
|
+
/**
|
|
1837
|
+
* Creates a runtime object from each object data, in order, and adds them
|
|
1838
|
+
* to the game, as {@link Game.createObject} does for one. Objects in the
|
|
1839
|
+
* same layer draw in the order of the data.
|
|
1840
|
+
*
|
|
1841
|
+
* Every object is checked before any is created, so invalid data or an
|
|
1842
|
+
* image key that is not loaded creates nothing.
|
|
1843
|
+
*
|
|
1844
|
+
* @param objects - Object data, such as `design.objects` or `design.findAllByTag("enemy")`. Not a `Design`.
|
|
1845
|
+
* @param options - `add: false` creates the objects without adding them to the game. Default: added.
|
|
1846
|
+
* @returns An {@link ObjectList} of the created objects, to find them by name or tag.
|
|
1847
|
+
*
|
|
1848
|
+
* @example
|
|
1849
|
+
* ```ts
|
|
1850
|
+
* import menuJson from "./menu.design.json" with { type: "json" };
|
|
1851
|
+
* const menu = new Design(menuJson);
|
|
1852
|
+
*
|
|
1853
|
+
* class MenuScene {
|
|
1854
|
+
* onCreate() {
|
|
1855
|
+
* const ui = game.createObjectList(menu.objects);
|
|
1856
|
+
* this.play = ui.findByName("play");
|
|
1857
|
+
* }
|
|
1858
|
+
* onUpdate() {
|
|
1859
|
+
* if (game.isPointerPressedOverSprite(this.play)) start(this.play.data.action);
|
|
1860
|
+
* }
|
|
1861
|
+
* }
|
|
1862
|
+
* ```
|
|
1863
|
+
*/
|
|
1864
|
+
createObjectList(objects, options = {}) {
|
|
1865
|
+
if (!Array.isArray(objects)) {
|
|
1866
|
+
throw new Error("MinimoJS: createObjectList() takes a list of object data, such as design.objects or design.findAllByTag(\"enemy\").");
|
|
1867
|
+
}
|
|
1868
|
+
const parsed = objects.map((data, index) => parseDesignObject(data, `objects[${index}]`, "design object"));
|
|
1869
|
+
for (const obj of parsed)
|
|
1870
|
+
this._assertDesignImageLoaded(obj);
|
|
1871
|
+
const created = parsed.map((obj) => this._buildDesignObject(obj));
|
|
1872
|
+
if (options.add !== false) {
|
|
1873
|
+
for (const obj of created)
|
|
1874
|
+
this._addDesignObject(obj);
|
|
1875
|
+
}
|
|
1876
|
+
return new ObjectList(created);
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Makes {@link Game.createObject} and {@link Game.createObjectList} build
|
|
1880
|
+
* the design objects tagged `tag` with `builder`, such as an `Enemy` class
|
|
1881
|
+
* of the game's own.
|
|
1882
|
+
*
|
|
1883
|
+
* - The builder returns an instance of the object's type class or a
|
|
1884
|
+
* subclass of it: {@link ImageSprite} for `image`, {@link EmojiSprite} for
|
|
1885
|
+
* `emoji`, {@link TextSprite} for `text`, and {@link BackgroundLayer} for
|
|
1886
|
+
* `background`. Anything else throws an error naming the object.
|
|
1887
|
+
* - The engine then applies every property the data holds, so the file wins
|
|
1888
|
+
* over the constructor. Properties the data omits keep what the builder set.
|
|
1889
|
+
* An `emoji` object's `size` cannot be changed after construction, so a
|
|
1890
|
+
* builder must pass it on, or the creation throws.
|
|
1891
|
+
* - An object matching the builders of two of its tags throws an error
|
|
1892
|
+
* naming the object and both tags.
|
|
1893
|
+
* - Builders are scene state: {@link Game.reset} clears them, as it clears
|
|
1894
|
+
* timers, so each scene registers its own in `onCreate()`.
|
|
1895
|
+
*
|
|
1896
|
+
* @param tag - The tag whose objects the builder builds.
|
|
1897
|
+
* @param builder - Receives the object's data and the game, and returns the object, not yet added.
|
|
1898
|
+
*
|
|
1899
|
+
* @example
|
|
1900
|
+
* ```ts
|
|
1901
|
+
* class Enemy extends ImageSprite {
|
|
1902
|
+
* update(dt) { this.x += this.data.speed * dt; }
|
|
1903
|
+
* }
|
|
1904
|
+
*
|
|
1905
|
+
* class Level1Scene {
|
|
1906
|
+
* onCreate() {
|
|
1907
|
+
* game.registerBuilderByTag("enemy", (obj, game) => new Enemy(game, obj.imageKey));
|
|
1908
|
+
* this.enemies = game.createObjectList(level1.findAllByTag("enemy")).objects;
|
|
1909
|
+
* }
|
|
1910
|
+
* }
|
|
1911
|
+
* ```
|
|
1912
|
+
*/
|
|
1913
|
+
registerBuilderByTag(tag, builder) {
|
|
1914
|
+
if (typeof tag !== "string" || tag.length === 0) {
|
|
1915
|
+
throw new Error("MinimoJS: registerBuilderByTag() requires a non-empty tag.");
|
|
1916
|
+
}
|
|
1917
|
+
if (typeof builder !== "function") {
|
|
1918
|
+
throw new Error(`MinimoJS: registerBuilderByTag("${tag}") requires a builder function.`);
|
|
1919
|
+
}
|
|
1920
|
+
if (this._buildersByTag.has(tag)) {
|
|
1921
|
+
throw new Error(`MinimoJS: A builder is already registered for the tag "${tag}". game.reset() clears builders, so register each in a scene's onCreate().`);
|
|
1922
|
+
}
|
|
1923
|
+
this._buildersByTag.set(tag, builder);
|
|
1924
|
+
}
|
|
1925
|
+
/** @internal */
|
|
1926
|
+
_assertDesignImageLoaded(obj) {
|
|
1927
|
+
if ((obj.type === "image" || obj.type === "background") && !this.hasImage(obj.imageKey)) {
|
|
1928
|
+
throw new Error(`MinimoJS: ${describeDesignObject(obj)} uses the image key "${obj.imageKey}", which is not loaded. Declare it in an asset file and load that file with game.loadAssets() in an onPreload hook.`);
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
/** @internal */
|
|
1932
|
+
_buildDesignObject(obj) {
|
|
1933
|
+
const matchingTags = [...new Set(obj.tags ?? [])].filter((tag) => this._buildersByTag.has(tag));
|
|
1934
|
+
if (matchingTags.length > 1) {
|
|
1935
|
+
throw new Error(`MinimoJS: ${describeDesignObject(obj)} matches the builders of two of its tags, "${matchingTags[0]}" and "${matchingTags[1]}". An object may match at most one builder.`);
|
|
1936
|
+
}
|
|
1937
|
+
const tag = matchingTags[0];
|
|
1938
|
+
const builder = tag === undefined ? undefined : this._buildersByTag.get(tag);
|
|
1939
|
+
let created;
|
|
1940
|
+
if (builder === undefined) {
|
|
1941
|
+
created =
|
|
1942
|
+
obj.type === "image"
|
|
1943
|
+
? new ImageSprite(this, obj.imageKey)
|
|
1944
|
+
: obj.type === "emoji"
|
|
1945
|
+
? new EmojiSprite(obj.sprite, 0, 0, obj.size)
|
|
1946
|
+
: obj.type === "text"
|
|
1947
|
+
? new TextSprite(obj.text, 0, 0, obj.fontSize)
|
|
1948
|
+
: new BackgroundLayer(obj.imageKey);
|
|
1949
|
+
}
|
|
1950
|
+
else {
|
|
1951
|
+
created = builder(obj, this);
|
|
1952
|
+
const expected = DESIGN_OBJECT_CLASSES[obj.type];
|
|
1953
|
+
if (!(created instanceof expected)) {
|
|
1954
|
+
throw new Error(`MinimoJS: The builder for the tag "${tag}" did not return an instance of ${expected.name} or of a subclass of it for ${describeDesignObject(obj)}, whose type is "${obj.type}".`);
|
|
1955
|
+
}
|
|
1956
|
+
if (obj.type === "emoji" && obj.size !== undefined && created.size !== obj.size) {
|
|
1957
|
+
throw new Error(`MinimoJS: The builder for the tag "${tag}" built ${describeDesignObject(obj)} with the size ${created.size}, but its data says ${obj.size}. An EmojiSprite's size is set only by its constructor, so pass obj.size to it.`);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
const target = created;
|
|
1961
|
+
for (const [field, value] of Object.entries(obj)) {
|
|
1962
|
+
if (field === "id" || field === "type")
|
|
1963
|
+
continue;
|
|
1964
|
+
if (field === "size" && created instanceof EmojiSprite)
|
|
1965
|
+
continue;
|
|
1966
|
+
if (field === "imageKey" && created instanceof ImageSprite) {
|
|
1967
|
+
created.setTexture(value);
|
|
1968
|
+
continue;
|
|
1969
|
+
}
|
|
1970
|
+
target[field] = value;
|
|
1971
|
+
}
|
|
1972
|
+
return created;
|
|
1973
|
+
}
|
|
1974
|
+
/** @internal */
|
|
1975
|
+
_addDesignObject(obj) {
|
|
1976
|
+
if (obj instanceof BackgroundLayer) {
|
|
1977
|
+
if (!this._backgroundSystem.getMutableLayers().includes(obj))
|
|
1978
|
+
this.addBackground(obj);
|
|
1979
|
+
}
|
|
1980
|
+
else if (!this._spriteSystem.getMutableSprites().includes(obj)) {
|
|
1981
|
+
this.add(obj);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
// -------------------------------------------------------------------------
|
|
1634
1985
|
// Collision
|
|
1635
1986
|
// -------------------------------------------------------------------------
|
|
1636
1987
|
/**
|
|
@@ -1788,6 +2139,8 @@ export class Game {
|
|
|
1788
2139
|
* Use this for continuous pointer actions.
|
|
1789
2140
|
*
|
|
1790
2141
|
* **Hold behavior:** returns `true` every frame from press until release.
|
|
2142
|
+
* On a touch screen it follows one finger, as {@link Game.pointerX} does,
|
|
2143
|
+
* so it turns `false` when that finger lifts, even if another is down.
|
|
1791
2144
|
*
|
|
1792
2145
|
* @returns `true` if the pointer is currently pressed.
|
|
1793
2146
|
*
|
|
@@ -2649,6 +3002,7 @@ export class Game {
|
|
|
2649
3002
|
this._trailSystem.clearAll();
|
|
2650
3003
|
this._textSystem.clear();
|
|
2651
3004
|
this._inputSystem.resetPressedState();
|
|
3005
|
+
this._buildersByTag.clear();
|
|
2652
3006
|
this.scrollX = 0;
|
|
2653
3007
|
this.scrollY = 0;
|
|
2654
3008
|
this._loopSystem.invokeCreate();
|
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.29",
|
|
4
4
|
"description": "MinimoJS v1 — ultra-minimal, flat, deterministic 2D web game engine. Image-first sprites with emoji as a zero-asset alternative, rAF loop, TypeScript-first, LLM-friendly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/minimo.js",
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"import": "./dist/minimo.js",
|
|
11
11
|
"types": "./dist/minimo.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./format": {
|
|
14
|
+
"import": "./dist/minimo-format.js",
|
|
15
|
+
"types": "./dist/minimo-format.d.ts"
|
|
12
16
|
}
|
|
13
17
|
},
|
|
14
18
|
"files": [
|
|
@@ -21,7 +25,8 @@
|
|
|
21
25
|
"clean": "rm -rf dist",
|
|
22
26
|
"rebuild": "npm run clean && npm run build",
|
|
23
27
|
"prepack": "npm run build",
|
|
24
|
-
"pack:check": "npm pack --dry-run"
|
|
28
|
+
"pack:check": "npm pack --dry-run",
|
|
29
|
+
"test": "npm run build && node --test \"test/*.test.mjs\""
|
|
25
30
|
},
|
|
26
31
|
"keywords": [
|
|
27
32
|
"game",
|