@react-shimeji/core 0.3.2 → 0.3.4
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 +73 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +73 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -662,12 +662,17 @@ var BehaviorController = class {
|
|
|
662
662
|
}
|
|
663
663
|
/** Selects the weighted transition following the current behavior. */
|
|
664
664
|
selectNext(environment) {
|
|
665
|
-
const
|
|
666
|
-
const pool = this.previous && this.previous.nextAdditive === false ? next : [...this.spec.behaviors, ...next];
|
|
667
|
-
const selected = this.choose(pool, environment);
|
|
665
|
+
const selected = this.choose(this.nextPool(), environment);
|
|
668
666
|
this.fallbackSelected = selected === void 0;
|
|
669
667
|
return this.previous = selected ?? this.findFallBehavior();
|
|
670
668
|
}
|
|
669
|
+
/** Tries a weighted transition from a subset without changing history when none applies. */
|
|
670
|
+
trySelectNext(environment, predicate) {
|
|
671
|
+
const selected = this.choose(this.nextPool().filter(predicate), environment);
|
|
672
|
+
if (!selected) return void 0;
|
|
673
|
+
this.fallbackSelected = false;
|
|
674
|
+
return this.previous = selected;
|
|
675
|
+
}
|
|
671
676
|
/** Whether the most recent transition had no effective weighted candidate. */
|
|
672
677
|
usedFallback() {
|
|
673
678
|
return this.fallbackSelected;
|
|
@@ -683,6 +688,10 @@ var BehaviorController = class {
|
|
|
683
688
|
const chosen = selectWeighted(applicable, (behavior) => behavior.frequency, this.random);
|
|
684
689
|
return chosen ? this.resolve(chosen) : void 0;
|
|
685
690
|
}
|
|
691
|
+
nextPool() {
|
|
692
|
+
const next = this.previous?.nextBehaviors ?? [];
|
|
693
|
+
return this.previous && this.previous.nextAdditive === false ? next : [...this.spec.behaviors, ...next];
|
|
694
|
+
}
|
|
686
695
|
resolve(behavior) {
|
|
687
696
|
if (behavior.type !== "Reference") return behavior;
|
|
688
697
|
const target = this.spec.behaviors.find((candidate) => candidate.type === "Behavior" && candidate.name === behavior.name);
|
|
@@ -1638,6 +1647,13 @@ function environmentRectangle(bounds) {
|
|
|
1638
1647
|
}
|
|
1639
1648
|
var PLATFORM_NEARBY_DISTANCE = 400;
|
|
1640
1649
|
var PLATFORM_EDGE_TOLERANCE = 2;
|
|
1650
|
+
var MASCOT_HITBOX_MAX_WIDTH = 32;
|
|
1651
|
+
var MASCOT_HITBOX_MAX_HEIGHT = 64;
|
|
1652
|
+
var MASCOT_COLLISION_LOOKAHEAD = 4;
|
|
1653
|
+
var IE_BEHAVIOR_NAME_PATTERN = /wall|climb|crawl|壁|登|よじ/i;
|
|
1654
|
+
function isIEBehavior(behavior) {
|
|
1655
|
+
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));
|
|
1656
|
+
}
|
|
1641
1657
|
function distanceToRectangle(point, rectangle) {
|
|
1642
1658
|
const dx = Math.max(rectangle.x - point.x, 0, point.x - rectangle.x - rectangle.width);
|
|
1643
1659
|
const dy = Math.max(rectangle.y - point.y, 0, point.y - rectangle.y - rectangle.height);
|
|
@@ -1700,7 +1716,7 @@ var Mascot = class {
|
|
|
1700
1716
|
random;
|
|
1701
1717
|
forceInitialFall;
|
|
1702
1718
|
/** Advances behavior, animation, physics, and rendering by one clock tick. */
|
|
1703
|
-
tick(deltaMs, bounds, platforms = []) {
|
|
1719
|
+
tick(deltaMs, bounds, platforms = [], siblings = []) {
|
|
1704
1720
|
if (this.destroyed) return;
|
|
1705
1721
|
this.platforms = platforms;
|
|
1706
1722
|
try {
|
|
@@ -1708,7 +1724,7 @@ var Mascot = class {
|
|
|
1708
1724
|
this.accumulatedMs += Math.max(0, deltaMs);
|
|
1709
1725
|
while (this.accumulatedMs >= this.frameDuration && !this.destroyed) {
|
|
1710
1726
|
this.accumulatedMs -= this.frameDuration;
|
|
1711
|
-
this.legacyTick(bounds, platforms);
|
|
1727
|
+
this.legacyTick(bounds, platforms, siblings);
|
|
1712
1728
|
}
|
|
1713
1729
|
} catch (error) {
|
|
1714
1730
|
this.callbacks.error(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -1721,6 +1737,24 @@ var Mascot = class {
|
|
|
1721
1737
|
snapshot() {
|
|
1722
1738
|
return { ...this.state };
|
|
1723
1739
|
}
|
|
1740
|
+
/** Returns the mascot's compact, feet-aligned behavioral collision box. */
|
|
1741
|
+
collisionBox() {
|
|
1742
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1743
|
+
const spriteWidth = typeof sprite === "object" && sprite.width !== void 0 ? sprite.width : 128;
|
|
1744
|
+
const spriteHeight = typeof sprite === "object" && sprite.height !== void 0 ? sprite.height : 128;
|
|
1745
|
+
const width = Math.min(spriteWidth, MASCOT_HITBOX_MAX_WIDTH);
|
|
1746
|
+
const height = Math.min(spriteHeight, MASCOT_HITBOX_MAX_HEIGHT);
|
|
1747
|
+
const anchorX = this.state.lookRight ? spriteWidth - this.state.anchorX : this.state.anchorX;
|
|
1748
|
+
const visualLeft = this.state.x - anchorX;
|
|
1749
|
+
const visualTop = this.state.y - this.state.anchorY;
|
|
1750
|
+
return {
|
|
1751
|
+
id: this.id,
|
|
1752
|
+
x: visualLeft + (spriteWidth - width) / 2,
|
|
1753
|
+
y: visualTop + spriteHeight - height,
|
|
1754
|
+
width,
|
|
1755
|
+
height
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1724
1758
|
/** Removes listeners, DOM nodes, and object URLs owned by this mascot. */
|
|
1725
1759
|
destroy() {
|
|
1726
1760
|
if (this.destroyed) return;
|
|
@@ -1749,11 +1783,13 @@ var Mascot = class {
|
|
|
1749
1783
|
if (!this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms)) return;
|
|
1750
1784
|
}
|
|
1751
1785
|
}
|
|
1752
|
-
legacyTick(bounds, platforms) {
|
|
1786
|
+
legacyTick(bounds, platforms, siblings) {
|
|
1753
1787
|
this.ensureBehavior(bounds, platforms, false);
|
|
1754
1788
|
if (!this.currentBehavior) return;
|
|
1789
|
+
const previous = { x: this.state.x, y: this.state.y };
|
|
1755
1790
|
const result = this.actions.step(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1756
1791
|
if (this.destroyed) return;
|
|
1792
|
+
this.avoidSiblingCollision(previous, bounds, platforms, siblings);
|
|
1757
1793
|
if (result === "lost-ground") {
|
|
1758
1794
|
this.state.dragging = false;
|
|
1759
1795
|
this.actions.cancel();
|
|
@@ -1771,6 +1807,31 @@ var Mascot = class {
|
|
|
1771
1807
|
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1772
1808
|
}
|
|
1773
1809
|
}
|
|
1810
|
+
avoidSiblingCollision(previous, bounds, platforms, siblings) {
|
|
1811
|
+
const dx = this.state.x - previous.x;
|
|
1812
|
+
if (this.state.dragging || dx === 0 || this.state.y !== previous.y) return;
|
|
1813
|
+
const onHorizontalSurface = isOnFloor(previous, bounds, platforms) || isOnTop(previous, bounds) || platforms.some((platform) => isOnBottom(previous, platform));
|
|
1814
|
+
if (!onHorizontalSurface) return;
|
|
1815
|
+
const currentBox = this.collisionBox();
|
|
1816
|
+
const offsetX = currentBox.x - this.state.x;
|
|
1817
|
+
const previousBox = { ...currentBox, x: previous.x + offsetX };
|
|
1818
|
+
const movingRight = dx > 0;
|
|
1819
|
+
const sweptLeft = Math.min(previousBox.x, currentBox.x) - (movingRight ? 0 : MASCOT_COLLISION_LOOKAHEAD);
|
|
1820
|
+
const sweptRight = Math.max(previousBox.x + previousBox.width, currentBox.x + currentBox.width) + (movingRight ? MASCOT_COLLISION_LOOKAHEAD : 0);
|
|
1821
|
+
const previousCenterX = previousBox.x + previousBox.width / 2;
|
|
1822
|
+
const collision = siblings.some((sibling) => {
|
|
1823
|
+
if (sibling.id === this.id) return false;
|
|
1824
|
+
const siblingCenterX = sibling.x + sibling.width / 2;
|
|
1825
|
+
const isAhead = movingRight ? siblingCenterX >= previousCenterX : siblingCenterX <= previousCenterX;
|
|
1826
|
+
const overlapsVertically = currentBox.y < sibling.y + sibling.height && sibling.y < currentBox.y + currentBox.height;
|
|
1827
|
+
const crossesHorizontally = sweptLeft <= sibling.x + sibling.width && sibling.x <= sweptRight;
|
|
1828
|
+
return isAhead && overlapsVertically && crossesHorizontally;
|
|
1829
|
+
});
|
|
1830
|
+
if (!collision) return;
|
|
1831
|
+
this.state.x = previous.x;
|
|
1832
|
+
this.state.vx = 0;
|
|
1833
|
+
this.state.lookRight = !this.state.lookRight;
|
|
1834
|
+
}
|
|
1774
1835
|
isOutsideVisibleBounds(bounds) {
|
|
1775
1836
|
const sprite = this.spec.sprites[this.state.sprite];
|
|
1776
1837
|
const width = typeof sprite === "object" && "width" in sprite && sprite.width !== void 0 ? sprite.width : 128;
|
|
@@ -1807,7 +1868,9 @@ var Mascot = class {
|
|
|
1807
1868
|
else if (this.state.y > bottom) this.state.y = bottom;
|
|
1808
1869
|
}
|
|
1809
1870
|
}
|
|
1810
|
-
const
|
|
1871
|
+
const environment = this.createEnvironment(bounds, platforms);
|
|
1872
|
+
const interruptedBehavior = this.currentBehavior;
|
|
1873
|
+
const selected = this.behavior.trySelectNext(environment, (candidate) => candidate.name !== interruptedBehavior?.name && isIEBehavior(candidate)) ?? this.selectNextBehavior(environment, bounds, false);
|
|
1811
1874
|
if (this.behavior.usedFallback() || selected?.name === "Fall" || selected?.name === "\u843D\u4E0B\u3059\u308B") {
|
|
1812
1875
|
this.state.x = original.x;
|
|
1813
1876
|
this.state.y = original.y;
|
|
@@ -2190,7 +2253,9 @@ var ShimejiEngine = class {
|
|
|
2190
2253
|
this.lastFrameTime = timestamp;
|
|
2191
2254
|
const delta = Math.max(0, Math.min(rawDelta, this.options.maxDeltaTime));
|
|
2192
2255
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
2193
|
-
|
|
2256
|
+
const mascots = [...this.mascots.values()];
|
|
2257
|
+
const collisionBoxes = mascots.map((mascot) => mascot.collisionBox());
|
|
2258
|
+
for (const mascot of mascots) mascot.tick(delta, bounds, platforms, collisionBoxes);
|
|
2194
2259
|
this.emitState();
|
|
2195
2260
|
this.animationFrame = this.container.ownerDocument.defaultView?.requestAnimationFrame(this.onAnimationFrame);
|
|
2196
2261
|
};
|