@react-shimeji/core 0.3.3 → 0.3.5
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/index.cjs +55 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +55 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1600,6 +1600,9 @@ function environmentRectangle(bounds) {
|
|
|
1600
1600
|
}
|
|
1601
1601
|
var PLATFORM_NEARBY_DISTANCE = 400;
|
|
1602
1602
|
var PLATFORM_EDGE_TOLERANCE = 2;
|
|
1603
|
+
var MASCOT_HITBOX_MAX_WIDTH = 32;
|
|
1604
|
+
var MASCOT_HITBOX_MAX_HEIGHT = 64;
|
|
1605
|
+
var MASCOT_COLLISION_LOOKAHEAD = 4;
|
|
1603
1606
|
var IE_BEHAVIOR_NAME_PATTERN = /wall|climb|crawl|壁|登|よじ/i;
|
|
1604
1607
|
function isIEBehavior(behavior) {
|
|
1605
1608
|
return behavior.conditions.some((condition) => /activeIE/i.test(condition)) || behavior.name.includes("IE") || behavior.name.includes("\uFF29\uFF25") || IE_BEHAVIOR_NAME_PATTERN.test(behavior.name) || behavior.actionName !== void 0 && (behavior.actionName.includes("IE") || behavior.actionName.includes("\uFF29\uFF25") || IE_BEHAVIOR_NAME_PATTERN.test(behavior.actionName));
|
|
@@ -1666,7 +1669,7 @@ var Mascot = class {
|
|
|
1666
1669
|
random;
|
|
1667
1670
|
forceInitialFall;
|
|
1668
1671
|
/** Advances behavior, animation, physics, and rendering by one clock tick. */
|
|
1669
|
-
tick(deltaMs, bounds, platforms = []) {
|
|
1672
|
+
tick(deltaMs, bounds, platforms = [], siblings = []) {
|
|
1670
1673
|
if (this.destroyed) return;
|
|
1671
1674
|
this.platforms = platforms;
|
|
1672
1675
|
try {
|
|
@@ -1674,7 +1677,7 @@ var Mascot = class {
|
|
|
1674
1677
|
this.accumulatedMs += Math.max(0, deltaMs);
|
|
1675
1678
|
while (this.accumulatedMs >= this.frameDuration && !this.destroyed) {
|
|
1676
1679
|
this.accumulatedMs -= this.frameDuration;
|
|
1677
|
-
this.legacyTick(bounds, platforms);
|
|
1680
|
+
this.legacyTick(bounds, platforms, siblings);
|
|
1678
1681
|
}
|
|
1679
1682
|
} catch (error) {
|
|
1680
1683
|
this.callbacks.error(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -1687,6 +1690,24 @@ var Mascot = class {
|
|
|
1687
1690
|
snapshot() {
|
|
1688
1691
|
return { ...this.state };
|
|
1689
1692
|
}
|
|
1693
|
+
/** Returns the mascot's compact, feet-aligned behavioral collision box. */
|
|
1694
|
+
collisionBox() {
|
|
1695
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1696
|
+
const spriteWidth = typeof sprite === "object" && sprite.width !== void 0 ? sprite.width : 128;
|
|
1697
|
+
const spriteHeight = typeof sprite === "object" && sprite.height !== void 0 ? sprite.height : 128;
|
|
1698
|
+
const width = Math.min(spriteWidth, MASCOT_HITBOX_MAX_WIDTH);
|
|
1699
|
+
const height = Math.min(spriteHeight, MASCOT_HITBOX_MAX_HEIGHT);
|
|
1700
|
+
const anchorX = this.state.lookRight ? spriteWidth - this.state.anchorX : this.state.anchorX;
|
|
1701
|
+
const visualLeft = this.state.x - anchorX;
|
|
1702
|
+
const visualTop = this.state.y - this.state.anchorY;
|
|
1703
|
+
return {
|
|
1704
|
+
id: this.id,
|
|
1705
|
+
x: visualLeft + (spriteWidth - width) / 2,
|
|
1706
|
+
y: visualTop + spriteHeight - height,
|
|
1707
|
+
width,
|
|
1708
|
+
height
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1690
1711
|
/** Removes listeners, DOM nodes, and object URLs owned by this mascot. */
|
|
1691
1712
|
destroy() {
|
|
1692
1713
|
if (this.destroyed) return;
|
|
@@ -1715,11 +1736,13 @@ var Mascot = class {
|
|
|
1715
1736
|
if (!this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms)) return;
|
|
1716
1737
|
}
|
|
1717
1738
|
}
|
|
1718
|
-
legacyTick(bounds, platforms) {
|
|
1739
|
+
legacyTick(bounds, platforms, siblings) {
|
|
1719
1740
|
this.ensureBehavior(bounds, platforms, false);
|
|
1720
1741
|
if (!this.currentBehavior) return;
|
|
1742
|
+
const previous = { x: this.state.x, y: this.state.y };
|
|
1721
1743
|
const result = this.actions.step(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1722
1744
|
if (this.destroyed) return;
|
|
1745
|
+
this.avoidSiblingCollision(previous, bounds, platforms, siblings);
|
|
1723
1746
|
if (result === "lost-ground") {
|
|
1724
1747
|
this.state.dragging = false;
|
|
1725
1748
|
this.actions.cancel();
|
|
@@ -1737,6 +1760,32 @@ var Mascot = class {
|
|
|
1737
1760
|
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1738
1761
|
}
|
|
1739
1762
|
}
|
|
1763
|
+
avoidSiblingCollision(previous, bounds, platforms, siblings) {
|
|
1764
|
+
const dx = this.state.x - previous.x;
|
|
1765
|
+
if (this.state.dragging || dx === 0 || this.state.y !== previous.y) return;
|
|
1766
|
+
const onHorizontalSurface = isOnFloor(previous, bounds, platforms) || isOnTop(previous, bounds) || platforms.some((platform) => isOnBottom(previous, platform));
|
|
1767
|
+
if (!onHorizontalSurface) return;
|
|
1768
|
+
const currentBox = this.collisionBox();
|
|
1769
|
+
const offsetX = currentBox.x - this.state.x;
|
|
1770
|
+
const previousBox = { ...currentBox, x: previous.x + offsetX };
|
|
1771
|
+
const movingRight = dx > 0;
|
|
1772
|
+
const sweptLeft = Math.min(previousBox.x, currentBox.x) - (movingRight ? 0 : MASCOT_COLLISION_LOOKAHEAD);
|
|
1773
|
+
const sweptRight = Math.max(previousBox.x + previousBox.width, currentBox.x + currentBox.width) + (movingRight ? MASCOT_COLLISION_LOOKAHEAD : 0);
|
|
1774
|
+
const previousCenterX = previousBox.x + previousBox.width / 2;
|
|
1775
|
+
const collision = siblings.some((sibling) => {
|
|
1776
|
+
if (sibling.id === this.id) return false;
|
|
1777
|
+
const siblingCenterX = sibling.x + sibling.width / 2;
|
|
1778
|
+
const isAhead = movingRight ? siblingCenterX >= previousCenterX : siblingCenterX <= previousCenterX;
|
|
1779
|
+
const overlapsVertically = currentBox.y < sibling.y + sibling.height && sibling.y < currentBox.y + currentBox.height;
|
|
1780
|
+
const crossesHorizontally = sweptLeft <= sibling.x + sibling.width && sibling.x <= sweptRight;
|
|
1781
|
+
const wasAlreadyOverlapping = overlapsVertically && previousBox.x < sibling.x + sibling.width && sibling.x < previousBox.x + previousBox.width;
|
|
1782
|
+
return isAhead && overlapsVertically && crossesHorizontally && !wasAlreadyOverlapping;
|
|
1783
|
+
});
|
|
1784
|
+
if (!collision) return;
|
|
1785
|
+
this.state.x = previous.x;
|
|
1786
|
+
this.state.vx = 0;
|
|
1787
|
+
this.state.lookRight = !this.state.lookRight;
|
|
1788
|
+
}
|
|
1740
1789
|
isOutsideVisibleBounds(bounds) {
|
|
1741
1790
|
const sprite = this.spec.sprites[this.state.sprite];
|
|
1742
1791
|
const width = typeof sprite === "object" && "width" in sprite && sprite.width !== void 0 ? sprite.width : 128;
|
|
@@ -2158,7 +2207,9 @@ var ShimejiEngine = class {
|
|
|
2158
2207
|
this.lastFrameTime = timestamp;
|
|
2159
2208
|
const delta = Math.max(0, Math.min(rawDelta, this.options.maxDeltaTime));
|
|
2160
2209
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
2161
|
-
|
|
2210
|
+
const mascots = [...this.mascots.values()];
|
|
2211
|
+
const collisionBoxes = mascots.map((mascot) => mascot.collisionBox());
|
|
2212
|
+
for (const mascot of mascots) mascot.tick(delta, bounds, platforms, collisionBoxes);
|
|
2162
2213
|
this.emitState();
|
|
2163
2214
|
this.animationFrame = this.container.ownerDocument.defaultView?.requestAnimationFrame(this.onAnimationFrame);
|
|
2164
2215
|
};
|