minimojs 1.0.0-alpha.3 → 1.0.0-alpha.31

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.
Files changed (63) hide show
  1. package/README.md +87 -284
  2. package/dist/internal/AnimationSystem.js +345 -0
  3. package/dist/internal/AssetSystem.d.ts +6 -0
  4. package/dist/internal/AssetSystem.js +273 -0
  5. package/dist/internal/BackgroundSystem.d.ts +1 -0
  6. package/dist/internal/BackgroundSystem.js +26 -0
  7. package/dist/internal/CanvasSystem.d.ts +1 -0
  8. package/dist/internal/CanvasSystem.js +51 -0
  9. package/dist/internal/DesignNamingCheck.d.ts +1 -0
  10. package/dist/internal/DesignNamingCheck.js +7 -0
  11. package/dist/internal/ExplosionSystem.d.ts +1 -0
  12. package/dist/internal/ExplosionSystem.js +540 -0
  13. package/dist/internal/InputSystem.d.ts +1 -0
  14. package/dist/internal/InputSystem.js +292 -0
  15. package/dist/internal/LoopSystem.d.ts +1 -0
  16. package/dist/internal/LoopSystem.js +61 -0
  17. package/dist/internal/PhysicsSystem.d.ts +1 -0
  18. package/dist/internal/PhysicsSystem.js +174 -0
  19. package/dist/internal/RenderSystem.d.ts +1 -0
  20. package/dist/internal/RenderSystem.js +1021 -0
  21. package/dist/internal/SoundSystem.d.ts +39 -0
  22. package/dist/internal/SoundSystem.js +292 -0
  23. package/dist/internal/SpriteSystem.d.ts +1 -0
  24. package/dist/internal/SpriteSystem.js +32 -0
  25. package/dist/internal/TextSystem.d.ts +1 -0
  26. package/dist/internal/TextSystem.js +16 -0
  27. package/dist/internal/TimerSystem.d.ts +1 -0
  28. package/dist/internal/TimerSystem.js +43 -0
  29. package/dist/internal/TrailSystem.d.ts +1 -0
  30. package/dist/internal/TrailSystem.js +116 -0
  31. package/dist/internal/TransitionSystem.d.ts +1 -0
  32. package/dist/internal/TransitionSystem.js +74 -0
  33. package/dist/internal/arcade-racer/ArcadeRacerCollisionSystem.d.ts +1 -0
  34. package/dist/internal/arcade-racer/ArcadeRacerCollisionSystem.js +198 -0
  35. package/dist/internal/arcade-racer/ArcadeRacerLaneSystem.d.ts +1 -0
  36. package/dist/internal/arcade-racer/ArcadeRacerLaneSystem.js +120 -0
  37. package/dist/internal/arcade-racer/ArcadeRacerRenderSystem.d.ts +1 -0
  38. package/dist/internal/arcade-racer/ArcadeRacerRenderSystem.js +599 -0
  39. package/dist/internal/arcade-racer/ArcadeRacerRoadSprite.d.ts +1 -0
  40. package/dist/internal/arcade-racer/ArcadeRacerRoadSprite.js +13 -0
  41. package/dist/internal/arcade-racer/ArcadeRacerTrackSystem.d.ts +1 -0
  42. package/dist/internal/arcade-racer/ArcadeRacerTrackSystem.js +447 -0
  43. package/dist/minimo-ambient.d.ts +299 -0
  44. package/dist/minimo-ambient.js +1445 -0
  45. package/dist/minimo-arcaderacer.d.ts +1431 -0
  46. package/dist/minimo-arcaderacer.js +2060 -0
  47. package/dist/minimo-cards.d.ts +359 -0
  48. package/dist/minimo-cards.js +819 -0
  49. package/dist/minimo-format.d.ts +429 -0
  50. package/dist/minimo-format.js +447 -0
  51. package/dist/minimo.d.ts +1815 -209
  52. package/dist/minimo.js +2660 -783
  53. package/package.json +10 -3
  54. package/dist/animations.js +0 -30
  55. package/dist/audio.js +0 -17
  56. package/dist/game.js +0 -1105
  57. package/dist/input.js +0 -185
  58. package/dist/internal-types.js +0 -4
  59. package/dist/physics.js +0 -10
  60. package/dist/render.js +0 -75
  61. package/dist/sprite.js +0 -149
  62. package/dist/timers.js +0 -23
  63. /package/dist/{pointer-info.js → internal/AnimationSystem.d.ts} +0 -0
@@ -0,0 +1,292 @@
1
+ /** @internal */
2
+ export class InputSystem {
3
+ constructor(
4
+ /** @internal */ game,
5
+ /** @internal */ canvas) {
6
+ this.game = game;
7
+ this.canvas = canvas;
8
+ /** @internal */
9
+ this._keysDown = new Set();
10
+ /** @internal */
11
+ this._keysPressed = new Set();
12
+ /** @internal */
13
+ this._pointerDown = false;
14
+ /** @internal */
15
+ this._pointerPressed = false;
16
+ /** @internal */
17
+ this._pointerX = 0;
18
+ /** @internal */
19
+ this._pointerY = 0;
20
+ /** @internal */
21
+ this._mouseDown = false;
22
+ /** @internal */
23
+ this._mousePressed = false;
24
+ /** @internal */
25
+ this._mouseX = 0;
26
+ /** @internal */
27
+ this._mouseY = 0;
28
+ /** @internal */
29
+ this._touchPointers = new Map();
30
+ /** @internal */
31
+ this._primaryTouchId = null;
32
+ /**
33
+ * The primary touch after it lifted, kept until the mouse or a new touch
34
+ * takes over, so the pointer stays where the finger left the screen instead
35
+ * of jumping to the mouse's last position.
36
+ *
37
+ * @internal
38
+ */
39
+ this._releasedTouch = null;
40
+ /** @internal True on the frame the primary touch ended in a `touchcancel`. */
41
+ this._pointerCancelled = false;
42
+ }
43
+ get pointerX() {
44
+ return this._pointerX;
45
+ }
46
+ get pointerY() {
47
+ return this._pointerY;
48
+ }
49
+ bindInputEvents() {
50
+ window.addEventListener("keydown", (e) => {
51
+ if (!this._keysDown.has(e.key)) {
52
+ this._keysPressed.add(e.key);
53
+ }
54
+ this._keysDown.add(e.key);
55
+ });
56
+ window.addEventListener("keyup", (e) => {
57
+ this._keysDown.delete(e.key);
58
+ });
59
+ this.canvas.addEventListener("mousedown", (e) => {
60
+ const p = this.getCanvasPoint(e.clientX, e.clientY);
61
+ this._mouseX = p.x;
62
+ this._mouseY = p.y;
63
+ this._mouseDown = true;
64
+ this._mousePressed = true;
65
+ this._releasedTouch = null;
66
+ this.syncPrimaryPointer();
67
+ });
68
+ this.canvas.addEventListener("mouseup", () => {
69
+ this._mouseDown = false;
70
+ this._releasedTouch = null;
71
+ this.syncPrimaryPointer();
72
+ });
73
+ this.canvas.addEventListener("mousemove", (e) => {
74
+ const p = this.getCanvasPoint(e.clientX, e.clientY);
75
+ this._mouseX = p.x;
76
+ this._mouseY = p.y;
77
+ this._releasedTouch = null;
78
+ this.syncPrimaryPointer();
79
+ });
80
+ this.canvas.addEventListener("touchstart", (e) => {
81
+ for (const touch of Array.from(e.changedTouches)) {
82
+ const p = this.getCanvasPoint(touch.clientX, touch.clientY);
83
+ this._touchPointers.set(touch.identifier, {
84
+ x: p.x,
85
+ y: p.y,
86
+ pressed: true,
87
+ });
88
+ // A finger that touches while none is followed becomes the pointer,
89
+ // even when other fingers are still down.
90
+ if (this._primaryTouchId === null) {
91
+ this._primaryTouchId = touch.identifier;
92
+ }
93
+ }
94
+ this._releasedTouch = null;
95
+ this._pointerCancelled = false;
96
+ this.syncPrimaryPointer();
97
+ e.preventDefault();
98
+ }, { passive: false });
99
+ this.canvas.addEventListener("touchend", (e) => {
100
+ this.endTouches(e, false);
101
+ e.preventDefault();
102
+ }, { passive: false });
103
+ this.canvas.addEventListener("touchcancel", (e) => {
104
+ this.endTouches(e, true);
105
+ e.preventDefault();
106
+ }, { passive: false });
107
+ this.canvas.addEventListener("touchmove", (e) => {
108
+ for (const touch of Array.from(e.changedTouches)) {
109
+ const p = this.getCanvasPoint(touch.clientX, touch.clientY);
110
+ const existing = this._touchPointers.get(touch.identifier);
111
+ if (existing) {
112
+ existing.x = p.x;
113
+ existing.y = p.y;
114
+ }
115
+ else {
116
+ this._touchPointers.set(touch.identifier, {
117
+ x: p.x,
118
+ y: p.y,
119
+ pressed: false,
120
+ });
121
+ }
122
+ }
123
+ this.syncPrimaryPointer();
124
+ e.preventDefault();
125
+ }, { passive: false });
126
+ }
127
+ isKeyDown(key) {
128
+ return this._keysDown.has(key);
129
+ }
130
+ isKeyPressed(key) {
131
+ return this._keysPressed.has(key);
132
+ }
133
+ isPointerDown() {
134
+ return this._pointerDown;
135
+ }
136
+ isPointerPressed() {
137
+ return this._pointerPressed;
138
+ }
139
+ isPointerCancelled() {
140
+ return this._pointerCancelled;
141
+ }
142
+ isPointerDownOverSprite(sprite) {
143
+ if (!sprite)
144
+ return false;
145
+ if (this._mouseDown &&
146
+ this.isScreenPointOverSprite(this._mouseX, this._mouseY, sprite)) {
147
+ return true;
148
+ }
149
+ for (const pointer of this._touchPointers.values()) {
150
+ if (this.isScreenPointOverSprite(pointer.x, pointer.y, sprite)) {
151
+ return true;
152
+ }
153
+ }
154
+ return false;
155
+ }
156
+ isPointerPressedOverSprite(sprite) {
157
+ if (!sprite)
158
+ return false;
159
+ if (this._mousePressed &&
160
+ this.isScreenPointOverSprite(this._mouseX, this._mouseY, sprite)) {
161
+ return true;
162
+ }
163
+ for (const pointer of this._touchPointers.values()) {
164
+ if (pointer.pressed &&
165
+ this.isScreenPointOverSprite(pointer.x, pointer.y, sprite)) {
166
+ return true;
167
+ }
168
+ }
169
+ return false;
170
+ }
171
+ getPointers() {
172
+ return this.collectPointers();
173
+ }
174
+ getPointersOverSprite(sprite) {
175
+ if (!sprite)
176
+ return [];
177
+ return this.collectPointers().filter((pointer) => this.isScreenPointOverSprite(pointer.x, pointer.y, sprite));
178
+ }
179
+ isMobileDevice() {
180
+ if (typeof navigator === "undefined")
181
+ return false;
182
+ const ua = navigator.userAgent ?? "";
183
+ const mobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile/i.test(ua);
184
+ const iPadOS = navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
185
+ const coarsePointer = typeof window !== "undefined" &&
186
+ typeof window.matchMedia === "function" &&
187
+ window.matchMedia("(pointer: coarse)").matches;
188
+ return mobileUA || iPadOS || coarsePointer;
189
+ }
190
+ clearFramePressedState() {
191
+ this._keysPressed.clear();
192
+ this._mousePressed = false;
193
+ for (const pointer of this._touchPointers.values()) {
194
+ pointer.pressed = false;
195
+ }
196
+ if (this._releasedTouch)
197
+ this._releasedTouch.pressed = false;
198
+ this._pointerCancelled = false;
199
+ this.syncPrimaryPointer();
200
+ }
201
+ resetPressedState() {
202
+ this.clearFramePressedState();
203
+ }
204
+ /**
205
+ * Removes the touches an event ended. When the primary one lifts, it is kept
206
+ * as the released touch at the point it lifted, carrying its frame-press flag
207
+ * so a tap that starts and ends between two frames still reads as pressed.
208
+ *
209
+ * @internal
210
+ */
211
+ endTouches(e, cancelled) {
212
+ for (const touch of Array.from(e.changedTouches)) {
213
+ const pointer = this._touchPointers.get(touch.identifier);
214
+ this._touchPointers.delete(touch.identifier);
215
+ if (this._primaryTouchId !== touch.identifier || !pointer)
216
+ continue;
217
+ this._primaryTouchId = null;
218
+ if (!cancelled) {
219
+ const p = this.getCanvasPoint(touch.clientX, touch.clientY);
220
+ pointer.x = p.x;
221
+ pointer.y = p.y;
222
+ }
223
+ this._releasedTouch = pointer;
224
+ this._pointerCancelled = cancelled;
225
+ }
226
+ this.syncPrimaryPointer();
227
+ }
228
+ /** @internal */
229
+ getCanvasPoint(clientX, clientY) {
230
+ const rect = this.canvas.getBoundingClientRect();
231
+ const scaleX = this.canvas.width / rect.width;
232
+ const scaleY = this.canvas.height / rect.height;
233
+ return {
234
+ x: (clientX - rect.left) * scaleX,
235
+ y: (clientY - rect.top) * scaleY,
236
+ };
237
+ }
238
+ /** @internal */
239
+ isScreenPointOverSprite(x, y, sprite) {
240
+ const halfWidth = sprite.displayWidth / 2;
241
+ const halfHeight = sprite.displayHeight / 2;
242
+ const drawX = sprite.ignoreScroll ? sprite.renderX : sprite.renderX - this.game.scrollX;
243
+ const drawY = sprite.ignoreScroll ? sprite.renderY : sprite.renderY - this.game.scrollY;
244
+ return Math.abs(x - drawX) <= halfWidth && Math.abs(y - drawY) <= halfHeight;
245
+ }
246
+ /** @internal */
247
+ collectPointers() {
248
+ const pointers = [];
249
+ if (this._mouseDown) {
250
+ pointers.push({
251
+ id: "mouse",
252
+ kind: "mouse",
253
+ x: this._mouseX,
254
+ y: this._mouseY,
255
+ pressed: this._mousePressed,
256
+ });
257
+ }
258
+ for (const [id, pointer] of this._touchPointers.entries()) {
259
+ pointers.push({
260
+ id,
261
+ kind: "touch",
262
+ x: pointer.x,
263
+ y: pointer.y,
264
+ pressed: pointer.pressed,
265
+ });
266
+ }
267
+ return pointers;
268
+ }
269
+ /** @internal */
270
+ syncPrimaryPointer() {
271
+ if (this._primaryTouchId !== null) {
272
+ const primaryTouch = this._touchPointers.get(this._primaryTouchId);
273
+ if (primaryTouch) {
274
+ this._pointerX = primaryTouch.x;
275
+ this._pointerY = primaryTouch.y;
276
+ this._pointerDown = true;
277
+ this._pointerPressed = primaryTouch.pressed;
278
+ return;
279
+ }
280
+ this._primaryTouchId = null;
281
+ }
282
+ // No finger is followed. Fingers still down are not adopted: the pointer
283
+ // reports the followed finger's release, and the next finger to touch
284
+ // is followed instead.
285
+ this._primaryTouchId = null;
286
+ const released = this._releasedTouch;
287
+ this._pointerX = released ? released.x : this._mouseX;
288
+ this._pointerY = released ? released.y : this._mouseY;
289
+ this._pointerDown = this._mouseDown;
290
+ this._pointerPressed = this._mousePressed || (released?.pressed ?? false);
291
+ }
292
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,61 @@
1
+ /** @internal */
2
+ export class LoopSystem {
3
+ constructor(/** @internal */ onFrame) {
4
+ this.onFrame = onFrame;
5
+ /** @internal */
6
+ this._rafId = null;
7
+ /** @internal */
8
+ this._lastTimestamp = null;
9
+ /** @internal */
10
+ this._running = false;
11
+ /** @internal */
12
+ this._hasCreated = false;
13
+ /** @internal */
14
+ this._onCreate = null;
15
+ /** @internal */
16
+ this.loop = (timestamp) => {
17
+ if (!this._running)
18
+ return;
19
+ if (this._lastTimestamp === null) {
20
+ this._lastTimestamp = timestamp;
21
+ }
22
+ let dt = (timestamp - this._lastTimestamp) / 1000;
23
+ this._lastTimestamp = timestamp;
24
+ if (dt > 0.1)
25
+ dt = 0.1;
26
+ this.onFrame(dt, dt * 1000);
27
+ this._rafId = requestAnimationFrame(this.loop);
28
+ };
29
+ }
30
+ get onCreate() {
31
+ return this._onCreate;
32
+ }
33
+ set onCreate(callback) {
34
+ this._onCreate = callback;
35
+ }
36
+ get isRunning() {
37
+ return this._running;
38
+ }
39
+ start() {
40
+ if (this._running)
41
+ return;
42
+ if (!this._hasCreated) {
43
+ this.invokeCreate();
44
+ }
45
+ this._running = true;
46
+ this._lastTimestamp = null;
47
+ this._rafId = requestAnimationFrame(this.loop);
48
+ }
49
+ stop() {
50
+ this._running = false;
51
+ if (this._rafId !== null) {
52
+ cancelAnimationFrame(this._rafId);
53
+ this._rafId = null;
54
+ }
55
+ this._lastTimestamp = null;
56
+ }
57
+ invokeCreate() {
58
+ this._hasCreated = true;
59
+ this._onCreate?.();
60
+ }
61
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,174 @@
1
+ /** @internal */
2
+ export class PhysicsSystem {
3
+ constructor() {
4
+ /** @internal */
5
+ this._gravityX = 0;
6
+ /** @internal */
7
+ this._gravityY = 0;
8
+ /** @internal */
9
+ this._enabled = false;
10
+ }
11
+ get gravityX() {
12
+ return this._gravityX;
13
+ }
14
+ set gravityX(value) {
15
+ this._gravityX = value;
16
+ }
17
+ get gravityY() {
18
+ return this._gravityY;
19
+ }
20
+ set gravityY(value) {
21
+ this._gravityY = value;
22
+ }
23
+ get enabled() {
24
+ return this._enabled;
25
+ }
26
+ set enabled(value) {
27
+ this._enabled = value;
28
+ }
29
+ update(sprites, dt) {
30
+ for (const sprite of sprites) {
31
+ if (sprite.isStatic) {
32
+ continue;
33
+ }
34
+ if (sprite.gravityScale !== 0) {
35
+ sprite.vx += this._gravityX * sprite.gravityScale * dt;
36
+ sprite.vy += this._gravityY * sprite.gravityScale * dt;
37
+ }
38
+ sprite.x += sprite.vx * dt;
39
+ sprite.y += sprite.vy * dt;
40
+ }
41
+ }
42
+ overlap(a, b) {
43
+ const halfWidthA = a.bodyDisplayWidth / 2;
44
+ const halfHeightA = a.bodyDisplayHeight / 2;
45
+ const halfWidthB = b.bodyDisplayWidth / 2;
46
+ const halfHeightB = b.bodyDisplayHeight / 2;
47
+ return (Math.abs(a.bodyCenterX - b.bodyCenterX) < halfWidthA + halfWidthB &&
48
+ Math.abs(a.bodyCenterY - b.bodyCenterY) < halfHeightA + halfHeightB);
49
+ }
50
+ overlapAny(listA, listB) {
51
+ for (const a of listA) {
52
+ for (const b of listB) {
53
+ if (this.overlap(a, b)) {
54
+ return [a, b];
55
+ }
56
+ }
57
+ }
58
+ return null;
59
+ }
60
+ collide(a, b) {
61
+ this.assertEnabled();
62
+ if (a === b)
63
+ return null;
64
+ const collision = this.getCollisionResolution(a, b);
65
+ if (!collision)
66
+ return null;
67
+ if (!a.isStatic) {
68
+ a.x += collision.separationX;
69
+ a.y += collision.separationY;
70
+ if (collision.axis === "x") {
71
+ a.vx = 0;
72
+ }
73
+ else {
74
+ a.vy = 0;
75
+ }
76
+ }
77
+ else if (!b.isStatic) {
78
+ b.x -= collision.separationX;
79
+ b.y -= collision.separationY;
80
+ if (collision.axis === "x") {
81
+ b.vx = 0;
82
+ }
83
+ else {
84
+ b.vy = 0;
85
+ }
86
+ }
87
+ return collision.info;
88
+ }
89
+ collideAny(listA, listB) {
90
+ this.assertEnabled();
91
+ for (const a of listA) {
92
+ for (const b of listB) {
93
+ const info = this.collide(a, b);
94
+ if (info)
95
+ return [a, b, info];
96
+ }
97
+ }
98
+ return null;
99
+ }
100
+ /** @internal */
101
+ assertEnabled() {
102
+ if (!this._enabled) {
103
+ throw new Error("MinimoJS: collide() and collideAny() require game.physics = true.");
104
+ }
105
+ }
106
+ /** @internal */
107
+ getCollisionResolution(a, b) {
108
+ const halfWidthA = a.bodyDisplayWidth / 2;
109
+ const halfHeightA = a.bodyDisplayHeight / 2;
110
+ const halfWidthB = b.bodyDisplayWidth / 2;
111
+ const halfHeightB = b.bodyDisplayHeight / 2;
112
+ const dx = b.bodyCenterX - a.bodyCenterX;
113
+ const dy = b.bodyCenterY - a.bodyCenterY;
114
+ const overlapX = halfWidthA + halfWidthB - Math.abs(dx);
115
+ const overlapY = halfHeightA + halfHeightB - Math.abs(dy);
116
+ if (overlapX <= 0 || overlapY <= 0) {
117
+ return null;
118
+ }
119
+ let axis;
120
+ if (overlapX < overlapY) {
121
+ axis = "x";
122
+ }
123
+ else if (overlapY < overlapX) {
124
+ axis = "y";
125
+ }
126
+ else {
127
+ const relVX = Math.abs(a.vx - b.vx);
128
+ const relVY = Math.abs(a.vy - b.vy);
129
+ axis = relVX > relVY ? "x" : "y";
130
+ }
131
+ const info = {
132
+ left: false,
133
+ right: false,
134
+ top: false,
135
+ bottom: false,
136
+ grounded: false,
137
+ };
138
+ if (axis === "x") {
139
+ if (dx >= 0) {
140
+ info.right = true;
141
+ return {
142
+ info,
143
+ axis,
144
+ separationX: -overlapX,
145
+ separationY: 0,
146
+ };
147
+ }
148
+ info.left = true;
149
+ return {
150
+ info,
151
+ axis,
152
+ separationX: overlapX,
153
+ separationY: 0,
154
+ };
155
+ }
156
+ if (dy >= 0) {
157
+ info.bottom = true;
158
+ info.grounded = true;
159
+ return {
160
+ info,
161
+ axis,
162
+ separationX: 0,
163
+ separationY: -overlapY,
164
+ };
165
+ }
166
+ info.top = true;
167
+ return {
168
+ info,
169
+ axis,
170
+ separationX: 0,
171
+ separationY: overlapY,
172
+ };
173
+ }
174
+ }
@@ -0,0 +1 @@
1
+ export {};