UNPKG

@dill-pixel/plugin-snap-physics

Version:

Snap Physics

1,190 lines (1,189 loc) 48.4 kB
import { Container as H, filterSet as I, Signal as T, Logger as w, Plugin as U } from "dill-pixel"; import { Point as f, Rectangle as M, Circle as R, Sprite as q, Bounds as X, Texture as Y, Graphics as G } from "pixi.js"; import { gsap as C } from "gsap"; const k = { /** * Adds `other` to `this` point and outputs into `outPoint` or a new Point. * * _Note: Only available with **pixi.js/math-extras**._ * @method add * @memberof Point# * @param {PointData} other - The point to add to `this`. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The `outPoint` reference or a new Point, with the result of the addition. */ /** * Adds `other` to `this` point and outputs into `outPoint` or a new Point. * * _Note: Only available with **pixi.js/math-extras**._ * @method add * @memberof ObservablePoint# * @param {PointData} other - The point to add to `this`. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The `outPoint` reference or a new Point, with the result of the addition. */ add(a, t) { return t || (t = new f()), t.x = this.x + a.x, t.y = this.y + a.y, t; }, /** * Subtracts `other` from `this` point and outputs into `outPoint` or a new Point. * * _Note: Only available with **pixi.js/math-extras**._ * @method subtract * @memberof Point# * @param {PointData} other - The point to subtract to `this`. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The `outPoint` reference or a new Point, with the result of the subtraction. */ /** * Subtracts `other` from `this` point and outputs into `outPoint` or a new Point. * * _Note: Only available with **pixi.js/math-extras**._ * @method subtract * @memberof ObservablePoint# * @param {PointData} other - The point to subtract to `this`. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The `outPoint` reference or a new Point, with the result of the subtraction. */ subtract(a, t) { return t || (t = new f()), t.x = this.x - a.x, t.y = this.y - a.y, t; }, /** * Multiplies component-wise `other` and `this` points and outputs into `outPoint` or a new Point. * * _Note: Only available with **pixi.js/math-extras**._ * @method multiply * @memberof Point# * @param {PointData} other - The point to multiply with `this`. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The `outPoint` reference or a new Point, with the component-wise multiplication. */ /** * Multiplies component-wise `other` and `this` points and outputs into `outPoint` or a new Point. * * _Note: Only available with **pixi.js/math-extras**._ * @method multiply * @memberof ObservablePoint# * @param {PointData} other - The point to multiply with `this`. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The `outPoint` reference or a new Point, with the component-wise multiplication. */ multiply(a, t) { return t || (t = new f()), t.x = this.x * a.x, t.y = this.y * a.y, t; }, /** * Multiplies each component of `this` point with the number `scalar` and outputs into `outPoint` or a new Point. * * _Note: Only available with **pixi.js/math-extras**._ * @method multiplyScalar * @memberof Point# * @param {number} scalar - The number to multiply both components of `this`. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The `outPoint` reference or a new Point, with the multiplication. */ /** * Multiplies each component of `this` point with the number `scalar` and outputs into `outPoint` or a new Point. * * _Note: Only available with **pixi.js/math-extras**._ * @method multiplyScalar * @memberof ObservablePoint# * @param {number} scalar - The number to multiply both components of `this`. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The `outPoint` reference or a new Point, with the multiplication. */ multiplyScalar(a, t) { return t || (t = new f()), t.x = this.x * a, t.y = this.y * a, t; }, /** * Computes the dot product of `other` with `this` point. * The dot product is the sum of the products of the corresponding components of two vectors. * * _Note: Only available with **pixi.js/math-extras**._ * @method dot * @memberof Point# * @param {PointData} other - The other point to calculate the dot product with `this`. * @returns {number} The result of the dot product. This is an scalar value. */ /** * Computes the dot product of `other` with `this` point. * The dot product is the sum of the products of the corresponding components of two vectors. * * _Note: Only available with **pixi.js/math-extras**._ * @method dot * @memberof ObservablePoint# * @param {PointData} other - The other point to calculate the dot product with `this`. * @returns {number} The result of the dot product. This is an scalar value. */ dot(a) { return this.x * a.x + this.y * a.y; }, /** * Computes the cross product of `other` with `this` point. * Given two linearly independent R3 vectors a and b, the cross product, a × b (read "a cross b"), * is a vector that is perpendicular to both a and b, and thus normal to the plane containing them. * While cross product only exists on 3D space, we can assume the z component of 2D to be zero and * the result becomes a vector that will only have magnitude on the z axis. * * This function returns the z component of the cross product of the two points. * * _Note: Only available with **pixi.js/math-extras**._ * @method cross * @memberof Point# * @param {PointData} other - The other point to calculate the cross product with `this`. * @returns {number} The z component of the result of the cross product. */ /** * Computes the cross product of `other` with `this` point. * Given two linearly independent R3 vectors a and b, the cross product, a × b (read "a cross b"), * is a vector that is perpendicular to both a and b, and thus normal to the plane containing them. * While cross product only exists on 3D space, we can assume the z component of 2D to be zero and * the result becomes a vector that will only have magnitude on the z axis. * * This function returns the z component of the cross product of the two points. * * _Note: Only available with **pixi.js/math-extras**._ * @method cross * @memberof ObservablePoint# * @param {PointData} other - The other point to calculate the cross product with `this`. * @returns {number} The z component of the result of the cross product. */ cross(a) { return this.x * a.y - this.y * a.x; }, /** * Computes a normalized version of `this` point. * * A normalized vector is a vector of magnitude (length) 1 * * _Note: Only available with **pixi.js/math-extras**._ * @method normalize * @memberof Point# * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The normalized point. */ /** * Computes a normalized version of `this` point. * * A normalized vector is a vector of magnitude (length) 1 * * _Note: Only available with **pixi.js/math-extras**._ * @method normalize * @memberof ObservablePoint# * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The normalized point. */ normalize(a) { a || (a = new f()); const t = Math.sqrt( this.x * this.x + this.y * this.y ); return a.x = this.x / t, a.y = this.y / t, a; }, /** * Computes the magnitude of this point (Euclidean distance from 0, 0). * * Defined as the square root of the sum of the squares of each component. * * _Note: Only available with **pixi.js/math-extras**._ * @method magnitude * @memberof Point# * @returns {number} The magnitude (length) of the vector. */ /** * Computes the magnitude of this point (Euclidean distance from 0, 0). * * Defined as the square root of the sum of the squares of each component. * * _Note: Only available with **pixi.js/math-extras**._ * @method magnitude * @memberof ObservablePoint# * @returns {number} The magnitude (length) of the vector. */ magnitude() { return Math.sqrt( this.x * this.x + this.y * this.y ); }, /** * Computes the square magnitude of this point. * If you are comparing the lengths of vectors, you should compare the length squared instead * as it is slightly more efficient to calculate. * * Defined as the sum of the squares of each component. * * _Note: Only available with **pixi.js/math-extras**._ * @method magnitudeSquared * @memberof Point# * @returns {number} The magnitude squared (length squared) of the vector. */ /** * Computes the square magnitude of this point. * If you are comparing the lengths of vectors, you should compare the length squared instead * as it is slightly more efficient to calculate. * * Defined as the sum of the squares of each component. * * _Note: Only available with **pixi.js/math-extras**._ * @method magnitudeSquared * @memberof ObservablePoint# * @returns {number} The magnitude squared (length squared) of the vector. */ magnitudeSquared() { return this.x * this.x + this.y * this.y; }, /** * Computes vector projection of `this` on `onto`. * * Imagine a light source, parallel to `onto`, above `this`. * The light would cast rays perpendicular to `onto`. * `(this as unknown as Point).project(onto)` is the shadow cast by `this` on the line defined by `onto` . * * _Note: Only available with **pixi.js/math-extras**._ * @method project * @memberof Point# * @param {PointData} onto - A non zero vector describing a line on which to project `this`. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The `this` on `onto` projection. */ /** * Computes vector projection of `this` on `onto`. * * Imagine a light source, parallel to `onto`, above `this`. * The light would cast rays perpendicular to `onto`. * `(this as unknown as Point).project(onto)` is the shadow cast by `this` on the line defined by `onto` . * * _Note: Only available with **pixi.js/math-extras**._ * @method project * @memberof ObservablePoint# * @param {PointData} onto - A non zero vector describing a line on which to project `this`. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The `this` on `onto` projection. */ project(a, t) { t || (t = new f()); const i = (this.x * a.x + this.y * a.y) / (a.x * a.x + a.y * a.y); return t.x = a.x * i, t.y = a.y * i, t; }, /** * Reflects `this` vector off of a plane orthogonal to `normal`. * `normal` is not normalized during this process. Consider normalizing your `normal` before use. * * Imagine a light source bouncing onto a mirror. * `this` vector is the light and `normal` is a vector perpendicular to the mirror. * `(this as unknown as Point).reflect(normal)` is the reflection of `this` on that mirror. * * _Note: Only available with **pixi.js/math-extras**._ * @method reflect * @memberof Point# * @param {PointData} normal - The normal vector of your reflecting plane. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The reflection of `this` on your reflecting plane. */ /** * Reflects `this` vector off of a plane orthogonal to `normal`. * `normal` is not normalized during this process. Consider normalizing your `normal` before use. * * Imagine a light source bouncing onto a mirror. * `this` vector is the light and `normal` is a vector perpendicular to the mirror. * `(this as unknown as Point).reflect(normal)` is the reflection of `this` on that mirror. * * _Note: Only available with **pixi.js/math-extras**._ * @method reflect * @memberof ObservablePoint# * @param {PointData} normal - The normal vector of your reflecting plane. * @param {PointData} [outPoint] - A Point-like object in which to store the value, * optional (otherwise will create a new Point). * @returns {PointData} The reflection of `this` on your reflecting plane. */ reflect(a, t) { t || (t = new f()); const i = this.x * a.x + this.y * a.y; return t.x = this.x - 2 * i * a.x, t.y = this.y - 2 * i * a.y, t; }, rotate(a, t) { t || (t = new f()); const i = Math.cos(a), n = Math.sin(a), e = this.x * i - this.y * n, o = this.x * n + this.y * i; return t.x = e, t.y = o, t; }, length() { return Math.sqrt( this.x * this.x + this.y * this.y ); } }; class D { constructor(t, i = !1) { this.cells = /* @__PURE__ */ new Map(), this._cellSize = t, i && c.all.forEach((n) => this.insert(n)); } get cellSize() { return this._cellSize; } set cellSize(t) { this._cellSize = t, this.cells.clear(), this.updateAll(); } destroy() { this.cells.clear(); } insert(t) { const i = t.boundingRect, n = Math.floor(i.x / this._cellSize), e = Math.floor(i.y / this._cellSize), o = Math.floor((i.x + i.width) / this._cellSize), h = Math.floor((i.y + i.height) / this._cellSize); for (let r = n; r <= o; r++) for (let d = e; d <= h; d++) { const l = this.getGridKey(r, d); this.cells.has(l) || this.cells.set(l, []); const u = this.cells.get(l); u.includes(t) || u.push(t); } } remove(t) { const i = []; this.cells.forEach((n, e) => { const o = n.indexOf(t); o !== -1 && (n.splice(o, 1), n.length === 0 && i.push(e)); }), i.forEach((n) => this.cells.delete(n)); } query(t, i, n = 0, e = 0, o, h) { let r = []; o && (Array.isArray(o) ? r = o.map((x) => x.uid) : r = [o.uid]); const d = new M( t.x - Math.abs(n), t.y - Math.abs(e), t.width + 2 * Math.abs(n), t.height + 2 * Math.abs(e) ), l = /* @__PURE__ */ new Set(), u = Math.floor(Math.min(d.x, d.x + d.width) / this._cellSize), g = Math.floor(Math.min(d.y, d.y + d.height) / this._cellSize), y = Math.floor(Math.max(d.x, d.x + d.width) / this._cellSize), p = Math.floor(Math.max(d.y, d.y + d.height) / this._cellSize); for (let x = u; x <= y; x++) for (let m = g; m <= p; m++) { const b = this.getGridKey(x, m), E = this.cells.get(b); E && E.forEach((S) => { r.includes(S.uid) || (i === void 0 || this.matchesFilter(S, i)) && l.add(S); }); } return l; } matchesFilter(t, i) { switch (typeof i) { case "string": return i === t.type || i === "solid" && t.isSolid || i === "actor" && t.isActor || i === "sensor" && t.isSensor; case "object": return Array.isArray(i) && i.includes(t.type); case "function": return i(t); default: return !1; } } updateAll() { c.all.forEach((t) => this.updateEntity(t)); } updateEntity(t) { this.remove(t), this.insert(t); } draw(t) { this._getDebugRects().forEach((n) => { t.rect(n.left, n.top, n.width, n.height), t.stroke({ color: 65280, pixelLine: !0 }); }); } _getDebugRects() { const t = []; return this.cells.forEach((i, n) => { const [e, o] = n.split(":").map(Number); i.length && t.push(new M(e * this._cellSize, o * this._cellSize, this._cellSize, this._cellSize)); }), t; } getGridKey(t, i) { return `${t}:${i}`; } } class z extends H { constructor(t) { super({ autoUpdate: !0 }), this.isActor = !1, this.isSolid = !1, this.isSensor = !1, this.debug = !1, this.debugColors = { bounds: 16711680, outerBounds: 65280 }, this.type = "Solid", this.isCircle = !1, this.isCollideable = !0, this.xRemainder = 0, this.yRemainder = 0, this.subpixelX = 0, this.subpixelY = 0, this.remainder = new f(0, 0), this._cachedBounds = null, this._dirtyBounds = !0, this.config = t; } get cachedBounds() { if (!this._cachedBounds || this._dirtyBounds) { const t = this.view.getBounds(); t.scale(1 / this.system.container.worldTransform.d), this.isCircle ? (t.width = t.height = Math.max(t.width, t.height), this._cachedBounds = new R( t.x + t.width * 0.5, t.y * t.width * 0.5, t.width * 0.5 )) : this._cachedBounds = t; } return this._cachedBounds ?? (this.isCircle ? new R() : new M()); } set cachedBounds(t) { this._cachedBounds = t; } get dirtyBounds() { return this._dirtyBounds; } set dirtyBounds(t) { this._dirtyBounds = t; } get boundingRect() { const t = this.getBoundingBox(); return this.isCircle ? t.getBounds() : t; } get top() { return this.boundingRect.top; } get bottom() { return this.boundingRect.bottom; } get left() { return this.boundingRect.left; } get right() { return this.boundingRect.right; } get system() { return c; } getCollideables(t = 0, i = 0) { return /* @__PURE__ */ new Set(); } preFixedUpdate() { } fixedUpdate(t) { } postFixedUpdate() { } getWorldBounds() { const t = this.system.container.toLocal(this.view.getGlobalPosition()), i = this.cachedBounds; return i.x = t.x, i.y = t.y, this.view instanceof q && this.view.anchor && (this.isCircle || (i.x -= this.view.width * this.view.anchor.x, i.y -= this.view.height * this.view.anchor.y)), i; } getBoundingBox() { const t = this.getWorldBounds(); return t instanceof X ? t.rectangle : t; } getOuterBoundingBox() { return null; } moveX(t) { this.remainder.x += t; const i = this.remainder.x; i !== 0 && (this.remainder.x -= i, this.x += i); } moveY(t) { this.remainder.y += t; const i = this.remainder.y; i !== 0 && (this.remainder.y -= i, this.y += i); } // Improved collision detection with subpixel precision collidesWith(t, i = 0, n = 0) { if (!t) return !1; const e = i + this.remainder.x, o = n + this.remainder.y; return this.isCircle ? t.isCircle ? c.getCircleToCircleIntersection(t, this, e, o) : c.getRectToCircletIntersection(t, this, e, o) : t.isCircle ? c.getRectToCircletIntersection(this, t, e, o) : c.getRectangleIntersection(t, this, e, o); } initialize() { } } class O extends z { constructor() { super(...arguments), this.type = "Solid", this.isSolid = !0, this.riding = /* @__PURE__ */ new Set(), this._animations = /* @__PURE__ */ new Set(), this._positionAnimation = null; } getCollideables(t = 0, i = 0) { return c.getNearbyEntities(this, "actor", t, i); } added() { c.addSolid(this), this.addSignalConnection(this.system.onSystemEnabledChanged.connect(this._handleSystemEnabledChanged)); } _handleSystemEnabledChanged(t) { var i, n; t ? ((i = this._animations) == null ? void 0 : i.size) > 0 && this._animations.forEach((e) => e == null ? void 0 : e.resume()) : ((n = this._animations) == null ? void 0 : n.size) > 0 && this._animations.forEach((e) => e == null ? void 0 : e.pause()); } removed() { var t; c.removeSolid(this), ((t = this._animations) == null ? void 0 : t.size) > 0 && this._animations.forEach((i) => i == null ? void 0 : i.kill()); } getAllRiding(t = 0, i = 0) { return I( this.getCollideables(t, i), (n) => n.isActor && n.isRiding(this) ); } move(t, i) { this.xRemainder += t, this.yRemainder += i; const n = Math.round(this.xRemainder), e = Math.round(this.yRemainder); if (n !== 0 || e !== 0) { const o = this.getAllRiding(n, e), h = this.boundingRect.clone(); e < 0 ? (h.y += e, h.height -= e) : h.height += e, n < 0 ? (h.x += n, h.width -= n) : h.width += n; const r = /* @__PURE__ */ new Set(); for (const d of this.getCollideables(n, e)) d.boundingRect.intersects(h) && r.add(d); for (const d of o) d.mostRiding === this && (d.moveY(e), d.moveX(n)); this.x += n, this.y += e, this.xRemainder -= n, this.yRemainder -= e, this.handleActorInteractions(n, e, o, r); } c.updateEntity(this); } animatePosition(t, i, n = {}) { var l; const e = this.position.clone(), o = Object.assign({ duration: 1, ease: "linear.none" }, n), h = t ?? e.x, r = i ?? e.y; this._positionAnimation = { targetX: h, targetY: r, startX: e.x, startY: e.y, duration: o.duration, elapsed: 0, ease: ((l = o.ease) == null ? void 0 : l.toString()) || "linear.none", repeat: o.repeat || 0, yoyo: o.yoyo || !1, repeatDelay: o.repeatDelay || 0, delayRemaining: 0, iteration: 0, isReversed: !1 }; const d = C.to({}, o); return this._animations.add(d), d; } fixedUpdate(t) { if (super.fixedUpdate(t), this._positionAnimation) { if (this._positionAnimation.delayRemaining > 0) { this._positionAnimation.delayRemaining -= t; return; } this._positionAnimation.elapsed += t; const i = Math.min(this._positionAnimation.elapsed / this._positionAnimation.duration, 1), n = C.parseEase(this._positionAnimation.ease)( this._positionAnimation.isReversed ? 1 - i : i ), e = this._positionAnimation.startX + (this._positionAnimation.targetX - this._positionAnimation.startX) * n, o = this._positionAnimation.startY + (this._positionAnimation.targetY - this._positionAnimation.startY) * n; this.move(e - this.x, o - this.y), i >= 1 && (this._positionAnimation.elapsed = 0, this._positionAnimation.repeat === -1 || this._positionAnimation.iteration < this._positionAnimation.repeat ? (this._positionAnimation.iteration++, this._positionAnimation.yoyo && (this._positionAnimation.isReversed = !this._positionAnimation.isReversed), this._positionAnimation.repeatDelay > 0 && (this._positionAnimation.delayRemaining = this._positionAnimation.repeatDelay)) : this._positionAnimation = null); } } handleActorInteractions(t, i, n = this.getAllRiding(), e = this.getCollideables(t, i)) { for (const o of e) if (!n.has(o) && !o.passThroughTypes.includes(this.type) && !o.isPassingThrough(this)) { const h = this.collidesWith(o, t, i), r = this.collidesWith(o, 0, 0); if (h || r) { const d = t !== 0 ? t > 0 ? this.boundingRect.right - o.boundingRect.left : this.boundingRect.left - o.boundingRect.right : 0, l = i !== 0 ? i > 0 ? this.boundingRect.bottom - o.boundingRect.top : this.boundingRect.top - o.boundingRect.bottom : 0; Math.abs(i) > Math.abs(t) ? (l !== 0 && o.moveY(l, o.squish, null, this), d !== 0 && o.moveX(d, o.squish, null, this)) : (d !== 0 && o.moveX(d, o.squish, null, this), l !== 0 && o.moveY(l, o.squish, null, this)); } } } // eslint-disable-next-line @typescript-eslint/no-unused-vars handleCollisionChange(t) { } } const W = { width: 10, height: 10, debugColor: 65535 }; class v extends O { constructor(t = {}) { super({ ...W, ...t }), this.type = "Wall", this.initialize(); } initialize() { this.view = this.add.sprite({ asset: Y.WHITE, width: this.config.width, height: this.config.height, tint: this.config.debugColor, anchor: 0.5 }); } } function it(a, t) { return a.x > t.left && a.x < t.right && a.y > t.top && a.y < t.bottom; } const B = 1e-10; function F(a, t) { const i = Math.max(0, Math.min(a.right, t.right) - Math.max(a.left, t.left)), n = Math.max(0, Math.min(a.bottom, t.bottom) - Math.max(a.top, t.top)), e = i * n; return { x: i, y: n, area: e }; } function L(a, t) { const i = Math.max(a.x, Math.min(a.x + a.width, t.x)), n = Math.max(a.y, Math.min(a.y + a.height, t.y)), e = t.x - i, o = t.y - n, h = e * e + o * o, r = Math.sqrt(h), l = Math.acos(r / t.radius) * t.radius * t.radius, u = r * Math.sqrt(t.radius * t.radius - h), g = l - u; return { x: 0, y: 0, area: Math.max(0, g) }; } function V(a, t) { const i = t.x - a.x, n = t.y - a.y, e = i * i + n * n, o = Math.sqrt(e), h = a.radius, r = t.radius, d = h + r; if (o >= d - B) return { x: 0, y: 0, area: 0 }; if (o <= Math.abs(h - r) + B) { const x = Math.min(h, r), m = Math.PI * x * x; return { x: a.x, y: a.y, area: m }; } const l = (h * h - r * r + e) / (2 * o), u = Math.sqrt(h * h - l * l), g = h * h * Math.acos(l / h) + r * r * Math.acos((o - l) / r) - o * u, y = a.x + l * i / o, p = a.y + l * n / o; return { x: y, y: p, area: Math.max(0, g) }; } function A(a, t, i, n) { const e = { type: `${i.type}|${n.type}`, entity1: i, entity2: n, top: 0, bottom: 0, left: 0, right: 0, area: 0, direction: void 0, overlap: { x: 0, y: 0 } }; let o = !1; if (i.isCircle && n.isCircle) { const h = a, r = t, d = r.x - h.x, l = r.y - h.y; Math.sqrt(d * d + l * l) < h.radius + r.radius && (o = !0, N(h, r, e)); } else if (i.isCircle !== n.isCircle) { const h = i.isCircle ? a : t, r = i.isCircle ? t : a, d = Math.max(r.x, Math.min(h.x, r.x + r.width)), l = Math.max(r.y, Math.min(h.y, r.y + r.height)), u = h.x - d, g = h.y - l; u * u + g * g <= h.radius * h.radius && (o = !0, j(r, h, d, l, e)); } else { const h = a, r = t; h.x < r.x + r.width && h.x + h.width > r.x && h.y < r.y + r.height && h.y + h.height > r.y && (o = !0, K(h, r, e)); } return o && e.area > c.collisionThreshold ? (e.direction = $(e), e) : !1; } function $(a) { let t, i = 0; return a.top > i && (i = a.top, t = "top"), a.bottom > i && (i = a.bottom, t = "bottom"), a.left > i && (i = a.left, t = "left"), a.right > i && (i = a.right, t = "right"), t; } function j(a, t, i, n, e) { const o = t.x - i, h = t.y - n, r = o * o + h * h; if (r >= t.radius * t.radius - B) return { x: 0, y: 0, area: 0 }; if (t.x >= a.x && t.x <= a.x + a.width && t.y >= a.y && t.y <= a.y + a.height) return { x: t.x, y: t.y, area: Math.PI * t.radius * t.radius }; const d = Math.sqrt(r), u = Math.acos(d / t.radius) * t.radius * t.radius, g = d * Math.sqrt(t.radius * t.radius - r), y = u - g; e.overlap = { x: i, y: n }, e.area = Math.max(0, y), d < t.radius && (e.top = 0, e.bottom = 0, e.left = 0, e.right = 0, o > 0 ? e.left = Math.abs(o) : e.right = Math.abs(o), h > 0 ? e.top = Math.abs(h) : e.bottom = Math.abs(h)); } function N(a, t, i) { const n = t.x - a.x, e = t.y - a.y, o = n * n + e * e, h = Math.sqrt(o), r = a.radius, d = t.radius, l = r + d; if (h >= l) return; const u = l - h, g = n / h, y = e / h; if (Math.abs(g) > 0.1 && (g > 0 ? i.left = u : i.right = u), Math.abs(y) > 0.1 && (y > 0 ? i.top = u : i.bottom = u), h <= Math.abs(r - d)) { const p = Math.min(r, d); i.area = Math.PI * p * p; } else { const p = (r * r - d * d + o) / (2 * h), x = Math.sqrt(r * r - p * p); i.area = r * r * Math.acos(p / r) + d * d * Math.acos((h - p) / d) - h * x; } i.overlap = { x: a.x + g * r, y: a.y + y * r }; } function K(a, t, i) { const n = t.x - a.x, e = t.y - a.y, o = a.width / 2, h = a.height / 2, r = t.width / 2, d = t.height / 2, l = a.x + o, u = a.y + h, g = t.x + r, y = t.y + d, p = Math.abs(g - l) - (o + r), x = Math.abs(y - u) - (h + d); if (i.overlap.x = Math.max(0, Math.min(a.x + a.width, t.x + t.width) - Math.max(a.x, t.x)), i.overlap.y = Math.max(0, Math.min(a.y + a.height, t.y + t.height) - Math.max(a.y, t.y)), i.area = i.overlap.x * i.overlap.y, p < 0 && x < 0) { const m = g - l, b = y - u; b > 0 ? i.bottom = Math.abs(b) : i.top = Math.abs(b), m > 0 ? i.right = Math.abs(m) : i.left = Math.abs(m); } else n > 0 ? i.right = Math.abs(n) : i.left = Math.abs(n), e > 0 ? i.bottom = Math.abs(e) : i.top = Math.abs(e); } const s = class s { static get enabled() { return s._enabled; } static set enabled(t) { !s._cleaningUp && t === s._enabled || (s._enabled = t, s._enabled ? s._fixedUpdateInterval = setInterval(() => { s.fixedUpdate(s._fixedTimeStep / 1e3); }, s._fixedTimeStep) : s._fixedUpdateInterval && (clearInterval(s._fixedUpdateInterval), s._fixedUpdateInterval = null), s._cleaningUp && s.onSystemEnabledChanged.emit(t)); } static set collisionResolver(t) { s._collisionResolver = t; } static get worldWidth() { var t; return (t = s.boundary) != null && t.width ? s.boundary.width + (s.boundary.padding ?? 0) : s.container.width; } static get worldHeight() { var t; return (t = s.boundary) != null && t.height ? s.boundary.height + (s.boundary.padding ?? 0) : s.container.height; } static get all() { return [...s.actors, ...s.solids, ...s.sensors]; } static get totalEntities() { return s.actors.length + s.solids.length + s.sensors.length; } static useSpatialHashGrid(t) { s.grid ? s.grid.cellSize = t : s.grid = new D(t, !0), s.plugin.options.useSpatialHashGrid = !0; } static removeSpatialHashGrid() { s.grid && (s.grid.destroy(), s.grid = null); } static resolveCollision(t) { return s._collisionResolver ? s._collisionResolver(t) : !0; } static addEntity(t) { s.typeMap.has(t.type) || s.typeMap.set(t.type, []), s.typeMap.get(t.type).push(t), s.grid && s.grid.insert(t); } static removeEntity(t) { if (s.grid && s.grid.remove(t), s.typeMap.has(t.type)) { const i = s.typeMap.get(t.type), n = i.indexOf(t); n !== -1 && i.splice(n, 1); } } static getEntitiesByType(...t) { return t.length === 0 ? s.typeMap.get(t[0]) || [] : t.reduce((i, n) => { const e = s.typeMap.get(n); return e != null && e.length ? [...i, ...e] : i; }, []); } static addActor(t) { s.actors.push(t), s.addEntity(t); } static addSolid(t) { s.solids.push(t), s.addEntity(t); } static addSensor(t) { s.sensors.push(t), s.addEntity(t); } static removeActor(t) { s.removeEntity(t); const i = s.actors.indexOf(t); i !== -1 && s.actors.splice(i, 1); } static removeSolid(t) { s.removeEntity(t); const i = s.solids.indexOf(t); i !== -1 && s.solids.splice(i, 1); } static removeSensor(t) { s.removeEntity(t); const i = s.sensors.indexOf(t); i !== -1 && s.sensors.splice(i, 1); } static getNearbyEntities(t, i, n = 0, e = 0, o) { if (s.grid) { const r = t.boundingRect; return s.grid.query(r, i, n, e, t, o); } const h = s.all.filter((r) => { if (r.uid === t.uid) return !1; if (i) switch (typeof i) { case "string": return i === r.type || i === "solid" && r.isSolid || i === "actor" && r.isActor || i === "sensor" && r.isSensor; case "object": return Array.isArray(i) && i.includes(r.type); case "function": return i(r); default: return !1; } return !0; }); return new Set(h); } static roundBoundingBox(t) { return t.x = Math.round(t.x), t.y = Math.round(t.y), t.width = Math.round(t.width), t.height = Math.round(t.height), t; } /** * @param entity1 * @param entity2 * @param dx * @param dy */ static getRectangleIntersection(t, i, n, e) { const o = t.getBoundingBox(), h = i.getBoundingBox().clone(); h.x += n, h.y += e; const r = F(o, h); return r.area > 0 && r.area > s.collisionThreshold; } /** * @param entity1 * @param entity2 * @param dx * @param dy */ static getCircleToCircleIntersection(t, i, n, e) { const o = t.getBoundingBox(), h = i.getBoundingBox().clone(); h.x += n, h.y += e; const r = V(o, h); return r.area > 0 && r.area > s.collisionThreshold; } /** * @param entity1 * @param entity2 * @param dx * @param dy */ static getRectToCircletIntersection(t, i, n, e) { const o = t.getBoundingBox(), h = i.getBoundingBox().clone(); h.x += n, h.y += e; const r = L(o, h); return r.area > 0 && r.area > s.collisionThreshold; } static fixedUpdate(t) { s.enabled && (s.container || w.error("SnapPhysicsPlugin: World container not set!"), s.all.forEach((i) => { i.preFixedUpdate(); }), s.updateHooks && s.updateHooks.forEach((i) => i(t)), s.solids.forEach((i) => { i.fixedUpdate(t); }), s.sensors.forEach((i) => { i.fixedUpdate(t); }), s.actors.forEach((i) => { i.fixedUpdate(t); }), s.all.forEach((i) => { i.postFixedUpdate(); }), s.camera && s.camera.update(), s.debug ? s.drawDebug() : s.gfx && s.gfx.clear()); } static addBoundary(t, i, n = 10, e = 5, o = ["top", "bottom", "left", "right"]) { if (!s.container) throw new Error("System container not set. Set World.container before calling System.addBoundary()."); s.worldBounds.length > 0 && (s.worldBounds.forEach((l) => { l.parent.removeChild(l), l.destroy(); }), s.worldBounds = []); const h = new f(0, 0), r = s.container; let d; o.includes("bottom") && (d = r.addChild(new v({ width: t, height: n })), d.position.set(h.x + t * 0.5, h.y + i + n * 0.5 - e), s.worldBounds.push(d)), o.includes("top") && (d = r.addChild(new v({ width: t, height: n })), d.position.set(h.x + t * 0.5, h.y - n * 0.5 + e), s.worldBounds.push(d)), o.includes("left") && (d = r.addChild(new v({ width: n, height: i })), d.position.set(h.x - n * 0.5 + e, h.y + i * 0.5), s.worldBounds.push(d)), o.includes("right") && (d = r.addChild(new v({ width: n, height: i })), d.position.set(h.x + t - e + n * 0.5, h.y + i * 0.5), s.worldBounds.push(d)), s.grid && s.worldBounds.forEach((l) => { var u, g; (u = s.grid) == null || u.remove(l), (g = s.grid) == null || g.insert(l); }); } static collide(t) { !t.type && t.entity1 && t.entity2 && (t.type = `${t.entity1.type}|${t.entity2.type}`), this.onCollision.emit(t); } static drawDebug() { s.container && (s.gfx || (s.gfx = new G(), s.container.addChild(s.gfx)), s.container.setChildIndex(s.gfx, s.container.children.length - 1), s.gfx.clear(), [...s.actors, ...s.solids, ...s.sensors].forEach((t) => { const i = t.getBoundingBox(), n = t.getOuterBoundingBox(); if (t.isCircle) { const e = i; if (s.gfx.circle(e.x, e.y, e.radius).stroke({ width: 1, color: t.debugColors.bounds, alignment: 0.5, pixelLine: !0 }), n) { const o = n; s.gfx.circle( o.x + o.radius, o.y + o.radius, o.radius ).stroke({ width: 1, color: t.debugColors.outerBounds, alignment: 0.5, pixelLine: !0 }); } } else { const e = i; if (s.gfx.rect(e.x, e.y, e.width, e.height).stroke({ width: 1, color: t.debugColors.bounds, alignment: 0.5 }), n) { const o = n; s.gfx.rect(o.x, o.y, o.width, o.height).stroke({ width: 1, color: t.debugColors.outerBounds, alignment: 0.5, pixelLine: !0 }); } } }), s.grid && s.grid.draw(s.gfx)); } static setContainer(t) { s.container = t; } static initialize(t) { t.gravity && (s.gravity = t.gravity), t.fps && (s.fps = t.fps, s._fixedTimeStep = 1e3 / t.fps), t.container && s.setContainer(t.container), t.debug !== void 0 && (s.debug = t.debug), t.collisionResolver && (s.collisionResolver = t.collisionResolver), t.boundary && (s.boundary = { width: t.boundary.width, height: t.boundary.height, padding: t.boundary.padding ?? 0 }, t.boundary.width && t.boundary.height ? s.addBoundary( t.boundary.width, t.boundary.height, t.boundary.thickness, t.boundary.padding, t.boundary.sides ) : w.error("SnapPhysicsPlugin System.initialize: Boundary width and height required.")), t.useSpatialHashGrid && s.useSpatialHashGrid(t.cellSize ?? 100); } static updateEntity(t) { s.grid && s.grid.updateEntity(t); } static cleanup() { s._cleaningUp = !0, s.worldBounds && (s.worldBounds.forEach((t) => { t.parent.removeChild(t), t.destroy(); }), s.worldBounds = []), s.container && (s.container.removeChildren(), s.container = null), s.gfx && (s.gfx.clear(), s.gfx = null), s.grid && (s.grid.destroy(), s.grid = null), s.camera && (s.camera = null), s.enabled = !1, s.solids = [], s.actors = [], s.sensors = [], s.typeMap.clear(), s.worldBounds = [], s._cleaningUp = !1; } }; s.DEFAULT_COLLISION_THRESHOLD = 0, s.fps = 60, s.debug = !0, s.typeMap = /* @__PURE__ */ new Map(), s.actors = [], s.solids = [], s.sensors = [], s.gravity = 10, s.onCollision = new T(), s.worldBounds = [], s.collisionThreshold = 8, s.updateHooks = /* @__PURE__ */ new Set(), s.postUpdateHooks = /* @__PURE__ */ new Set(), s._cleaningUp = !1, s._enabled = !1, s._fixedTimeStep = 1e3 / s.fps, s._fixedUpdateInterval = null, s.onSystemEnabledChanged = new T(), s._collisionResolver = null; let c = s; const P = "5.0.3", J = { useSpatialHashGrid: !1, gridCellSize: -1, fps: -1, debug: !1 }; class et extends U { constructor() { super(...arguments), this.id = "SnapPhysicsPlugin"; } get gridCellSize() { return this.options.gridCellSize; } set gridCellSize(t) { this.options.gridCellSize = t, this.options.useSpatialHashGrid && this.options.gridCellSize > 0 && c.useSpatialHashGrid(this.options.gridCellSize); } get useSpatialHashGrid() { return this.options.useSpatialHashGrid; } set useSpatialHashGrid(t) { this.options.useSpatialHashGrid = t, this.options.useSpatialHashGrid && this.options.gridCellSize > 0 ? c.useSpatialHashGrid(this.options.gridCellSize) : c.removeSpatialHashGrid(); } set fps(t) { this.options.fps = t, c.fps = t; } get system() { return c; } hello() { const t = `%c Dill Pixel Snap Physics Plugin v${P}`; console.log(t, "background: rgba(31, 41, 55, 1);color: #74b64c"), this.options.debug && w.log(this.options); } destroy() { w.log("SnapPhysicsPlugin:: destroy"), this.system.enabled = !1, c.cleanup(), super.destroy(); } async initialize(t, i) { this._addMathExtras(), this._options = { ...J, ...i }, this.system.app = t, this.system.plugin = this, this.options.useSpatialHashGrid && this.options.gridCellSize > 0 && this.system.useSpatialHashGrid(this.options.gridCellSize), this.options.fps > 0 && (c.fps = this.options.fps), this.hello(); } _addMathExtras() { Object.assign(f.prototype, k); } } class Q extends z { constructor() { super(...arguments), this.type = "Actor", this.isActor = !0, this.passThroughTypes = [], this.passingThrough = /* @__PURE__ */ new Set(), this.riding = /* @__PURE__ */ new Set(), this.mostRiding = null, this._animations = /* @__PURE__ */ new Set(), this._animationTargets = /* @__PURE__ */ new Map(); } get activeCollisions() { return this._activeCollisions; } set activeCollisions(t) { this._activeCollisions = t; } get ridingAllowed() { return !0; } getCollideables(t = 0, i = 0) { return c.getNearbyEntities(this, "solid", t, i); } added() { c.addActor(this); } removed() { this._animations && this._animations.forEach((t) => t == null ? void 0 : t.kill()), c.removeActor(this); } // eslint-disable-next-line @typescript-eslint/no-unused-vars squish(t, i, n) { } animateX(t, i = {}) { return this.animateTo("x", t, i); } animateY(t, i = {}) { return this.animateTo("y", t, i); } postFixedUpdate() { this.setAllRiding(); } animateTo(t, i, n = {}) { var g; const e = n.duration || 1, o = ((g = n.ease) == null ? void 0 : g.toString()) || "linear.none", h = n.repeat || 0, r = n.yoyo || !1, d = n.repeatDelay || 0, l = this[t]; this._animationTargets.set(t, { target: i, duration: e, elapsed: 0, start: l, ease: o, repeat: h, yoyo: r, repeatDelay: d, delayRemaining: 0, iteration: 0, isReversed: !1 }); const u = C.to({}, { duration: e, ...n }); return this._animations.add(u), u; } fixedUpdate(t) { super.fixedUpdate(t); for (const [i, n] of this._animationTargets.entries()) { if (n.delayRemaining > 0) { n.delayRemaining -= t; continue; } n.elapsed += t; const e = Math.min(n.elapsed / n.duration, 1), o = C.parseEase(n.ease)(n.isReversed ? 1 - e : e), r = n.start + (n.target - n.start) * o - this[i]; i === "x" ? this.moveX(r, null, null) : this.moveY(r, null, null), e >= 1 && (n.elapsed = 0, n.repeat === -1 || n.iteration < n.repeat ? (n.iteration++, n.yoyo && (n.isReversed = !n.isReversed), n.repeatDelay > 0 && (n.delayRemaining = n.repeatDelay)) : this._animationTargets.delete(i)); } } moveX(t, i, n, e) { this.xRemainder += t; let o = Math.round(this.xRemainder); const h = Math.sign(o); for (e && (e.isCollideable = !1); o !== 0; ) { const r = this.x + (o ? h : 0), d = this.collideAt(r - this.x, 0, this.getBoundingBox(), [ "left", "right" ]); if (d) { i && d.forEach((l) => { i(l, e, new f(r - this.x, 0)); }), this.xRemainder = 0; break; } else this.x = r, o -= h, this.xRemainder -= h, n && n(); c.updateEntity(this); } e && (e.isCollideable = !0); } moveY(t, i, n, e) { this.yRemainder += t; let o = Math.round(this.yRemainder); const h = Math.sign(o); for (e && (e.isCollideable = !1); o !== 0; ) { const r = this.y + (o ? h : 0), d = this.collideAt(0, r - this.y, this.getBoundingBox(), [ "top", "bottom" ]); if (d) { i && d.forEach((l) => i(l, e, new f(0, r - this.y))), this.yRemainder = 0; break; } else this.y = r, o -= h, this.yRemainder -= h, n && n(); c.updateEntity(this); } e && (e.isCollideable = !0); } // Simple bounding box collision check collideAt(t, i, n, e) { const o = this.isCircle ? new R(n.x + t, n.y + i, n.radius) : new M(n.x + t, n.y + i, n.width, n.height), h = []; for (const r of this.getCollideables()) { if (!r.isCollideable || this.passThroughTypes.includes(r.type)) continue; const d = r.getBoundingBox(); let l = A(o, d, this, r); e != null && e.length && l && (e.filter((g) => l[g]).length || (l = !1)), l && (c.collide(l), c.resolveCollision(l) && h.push(l)); } return h.length ? h : !1; } isRiding(t, i = 0, n = 0) { const e = this.boundingRect, o = t.boundingRect; return e.bottom <= o.top + n + 1 && Math.abs(e.bottom - o.top + n) <= 1 && e.left < o.right + i && e.right > o.left + i; } setPassingThrough(t) { this.passingThrough.add(t); } removePassingThrough(t) { this.passingThrough.delete(t); } isPassingThrough(t) { return this.passingThrough.has(t); } clearAllRiding() { this.mostRiding = null, this.riding.clear(); } setAllRiding(t = 0, i = 0) { this.clearAllRiding(), this.getCollideables(t, i).forEach((e) => { this.isRiding(e) && this.riding.add(e); }); let n = 0; for (const e of this.riding) { if (this.right > e.left && this.left < e.right) { this.mostRiding = e; break; } let o = 0; this.right > e.left && this.left < e.left ? (o = this.right - e.left, o > n && (n = o, this.mostRiding = e)) : this.left < e.right && this.right > e.right && (o = e.right - this.left, o > n && (n = o, this.mostRiding = e)); } } } const st = (a) => class extends a { constructor() { super(...arguments), this.velocity = new f(0, 0), this.previousVelocity = new f(0, 0), this.velocityRemainder = new f(0, 0), this.maxVelocity = new f(1e3, 1e3), this.friction = new f(0, 0), this.velocityState = { current: new f(0, 0), previous: new f(0, 0), remainder: new f(0, 0) }; } moveByVelocity(t, i, n) { this.velocityState.previous.copyFrom(this.velocityState.current), this.friction.x !== 0 && (this.velocity.x *= 1 - this.friction.x * t), this.friction.y !== 0 && (this.velocity.y *= 1 - this.friction.y * t), this.velocity.x = Math.min(Math.max(this.velocity.x, -this.maxVelocity.x), this.maxVelocity.x), this.velocity.y = Math.min(Math.max(this.velocity.y, -this.maxVelocity.y), this.maxVelocity.y), this.velocityRemainder.x += this.velocity.x * t, this.velocityRemainder.y += this.velocity.y * t; const e = Math.round(this.velocityRemainder.x), o = Math.round(this.velocityRemainder.y); this.velocityRemainder.x -= e, this.velocityRemainder.y -= o, this.velocityState.current.copyFrom(this.velocity), this.velocityState.remainder.copyFrom(this.velocityRemainder), this.isSolid ? this.move(e, o) : (e !== 0 && this.moveX(e, i, n), o !== 0 && this.moveY(o, i, n)); } reflect(t, i = 0, n = 0) { const e = new f( (t.left ? 1 : 0) + (t.right ? -1 : 0), (t.top ? 1 : 0) + (t.bottom ? -1 : 0) ); if (e.x === 0 && e.y === 0) return; const o = Math.sqrt(e.x * e.x + e.y * e.y); if (e.x /= o, e.y /= o, n > 0) { const d = Math.atan2(e.y, e.x) + (Math.random() - 0.5) * n; e.x = Math.cos(d), e.y = Math.sin(d); } const h = this.velocity.x * e.x + this.velocity.y * e.y, r = 1 - Math.min(Math.max(i, 0), 1); this.velocity.x = (this.velocity.x - 2 * h * e.x) * r, this.velocity.y = (this.velocity.y - 2 * h * e.y) * r, Math.abs(e.x) > 0.1 && (this.velocityRemainder.x = 0), Math.abs(e.y) > 0.1 && (this.velocityRemainder.y = 0); } // Helper method to get interpolated position for rendering getInterpolatedPosition(t) { return new f( this.x + (this.velocityState.current.x - this.velocityState.previous.x) * t, this.y + (this.velocityState.current.y - this.velocityState.previous.y) * t ); } setMaxVelocity(t, i) { this.maxVelocity.set(t, i); } setFriction(t, i) { this.friction.set(t, i); } }; class nt extends Q { constructor() { super(...arguments), this.type = "Sensor", this.isSensor = !0, this.isColliding = !1, this.passThroughTypes = ["Actor", "Player"]; } getCollideables() { return c.getNearbyEntities(this, "actor"); } added() { c.addSensor(this); } removed() { c.removeSensor(this); } // eslint-disable-next-line @typescript-eslint/no-unused-vars fixedUpdate(t) { this.activeCollisions = this.resolveAllCollisions() || [], this.isColliding = this.activeCollisions ? this.activeCollisions.length > 0 : !1; } /** * Resolve all collisions for this sensor * ignores passThroughTypes */ resolveAllCollisions() { const t = []; for (const i of this.getCollideables()) { if (!i.isCollideable) continue; const n = A(this.getBoundingBox(), i.getBoundingBox(), this, i); n && t.push(n), n && (c.collide(n), c.resolveCollision(n) && t.push(n)); } return t.length ? t : null; } getOuterCollisions(t = this.getCollideables()) { const i = this.getOuterBoundingBox(); if (!i) return []; const n = []; for (const e of t) { if (!e.isCollideable) continue; const o = A(i, e.getBoundingBox(), this, e); o && n.push(o); } return n; } } export { Q as Actor, z as Entity, nt as Sensor, O as Solid, c as System, v as Wall, st as WithVelocity, A as checkCollision, it as checkPointIntersection, et as default }; //# sourceMappingURL=dill-pixel-plugin-snap-physics.mjs.map