@react-shimeji/core 0.3.0 → 0.3.2
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 +45 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +45 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -654,21 +654,25 @@ var BehaviorController = class {
|
|
|
654
654
|
};
|
|
655
655
|
|
|
656
656
|
// src/physics.ts
|
|
657
|
-
var BORDER_TOLERANCE =
|
|
657
|
+
var BORDER_TOLERANCE = 1 - Number.EPSILON / 2;
|
|
658
|
+
function isWithinSpan(value, minimum, maximum, tolerance) {
|
|
659
|
+
const distance = value < minimum ? minimum - value : value > maximum ? value - maximum : 0;
|
|
660
|
+
return distance <= tolerance;
|
|
661
|
+
}
|
|
658
662
|
function clamp(value, minimum, maximum) {
|
|
659
663
|
return Math.min(Math.max(value, minimum), maximum);
|
|
660
664
|
}
|
|
661
665
|
function isOnTop(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
662
|
-
return point.x
|
|
666
|
+
return isWithinSpan(point.x, rectangle.x, rectangle.x + rectangle.width, tolerance) && Math.abs(point.y - rectangle.y) <= tolerance;
|
|
663
667
|
}
|
|
664
668
|
function isOnBottom(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
665
|
-
return point.x
|
|
669
|
+
return isWithinSpan(point.x, rectangle.x, rectangle.x + rectangle.width, tolerance) && Math.abs(point.y - rectangle.y - rectangle.height) <= tolerance;
|
|
666
670
|
}
|
|
667
671
|
function isOnLeft(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
668
|
-
return point.y
|
|
672
|
+
return isWithinSpan(point.y, rectangle.y, rectangle.y + rectangle.height, tolerance) && Math.abs(point.x - rectangle.x) <= tolerance;
|
|
669
673
|
}
|
|
670
674
|
function isOnRight(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
671
|
-
return point.y
|
|
675
|
+
return isWithinSpan(point.y, rectangle.y, rectangle.y + rectangle.height, tolerance) && Math.abs(point.x - rectangle.x - rectangle.width) <= tolerance;
|
|
672
676
|
}
|
|
673
677
|
function isOnBorder(state, bounds, border, platform) {
|
|
674
678
|
if (!border) return true;
|
|
@@ -1586,6 +1590,7 @@ function environmentRectangle(bounds) {
|
|
|
1586
1590
|
};
|
|
1587
1591
|
}
|
|
1588
1592
|
var PLATFORM_NEARBY_DISTANCE = 400;
|
|
1593
|
+
var PLATFORM_EDGE_TOLERANCE = 2;
|
|
1589
1594
|
function distanceToRectangle(point, rectangle) {
|
|
1590
1595
|
const dx = Math.max(rectangle.x - point.x, 0, point.x - rectangle.x - rectangle.width);
|
|
1591
1596
|
const dy = Math.max(rectangle.y - point.y, 0, point.y - rectangle.y - rectangle.height);
|
|
@@ -1616,6 +1621,7 @@ var Mascot = class {
|
|
|
1616
1621
|
this.domHandle = dom.createMascot(spec, id, options.mascotClassName || void 0);
|
|
1617
1622
|
this.frameDuration = options.frameDuration;
|
|
1618
1623
|
this.random = options.random ?? Math.random;
|
|
1624
|
+
this.forceInitialFall = spawnOptions.behaviorName === void 0;
|
|
1619
1625
|
this.behavior = new BehaviorController(spec, options.random);
|
|
1620
1626
|
this.actions = new ActionExecutor(spec, this.state, options, {
|
|
1621
1627
|
spawn: (position, characterId) => this.callbacks.spawn(characterId ?? this.spec.id, position),
|
|
@@ -1645,6 +1651,7 @@ var Mascot = class {
|
|
|
1645
1651
|
accumulatedMs = 0;
|
|
1646
1652
|
frameDuration;
|
|
1647
1653
|
random;
|
|
1654
|
+
forceInitialFall;
|
|
1648
1655
|
/** Advances behavior, animation, physics, and rendering by one clock tick. */
|
|
1649
1656
|
tick(deltaMs, bounds, platforms = []) {
|
|
1650
1657
|
if (this.destroyed) return;
|
|
@@ -1684,7 +1691,7 @@ var Mascot = class {
|
|
|
1684
1691
|
for (let guard = 0; guard < 32 && !this.destroyed; guard += 1) {
|
|
1685
1692
|
const environment = this.createEnvironment(bounds, platforms);
|
|
1686
1693
|
if (!this.currentBehavior) {
|
|
1687
|
-
this.currentBehavior = initial ? this.behavior.selectInitial(environment, this.state.behaviorName) : this.selectNextBehavior(environment, bounds);
|
|
1694
|
+
this.currentBehavior = initial ? this.forceInitialFall ? this.findFallBehavior() ?? this.behavior.selectInitial(environment) : this.behavior.selectInitial(environment, this.state.behaviorName) : this.selectNextBehavior(environment, bounds);
|
|
1688
1695
|
initial = false;
|
|
1689
1696
|
if (!this.currentBehavior) this.currentBehavior = this.findFallBehavior();
|
|
1690
1697
|
if (!this.currentBehavior || !this.startBehavior(environment, bounds, platforms)) return;
|
|
@@ -1703,7 +1710,7 @@ var Mascot = class {
|
|
|
1703
1710
|
if (result === "lost-ground") {
|
|
1704
1711
|
this.state.dragging = false;
|
|
1705
1712
|
this.actions.cancel();
|
|
1706
|
-
this.currentBehavior = this.
|
|
1713
|
+
this.currentBehavior = this.selectEdgeBehavior(bounds, platforms);
|
|
1707
1714
|
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1708
1715
|
} else if (result === "complete") {
|
|
1709
1716
|
this.currentBehavior = this.selectNextBehavior(this.createEnvironment(bounds, platforms), bounds);
|
|
@@ -1729,14 +1736,37 @@ var Mascot = class {
|
|
|
1729
1736
|
findFallBehavior() {
|
|
1730
1737
|
return this.behavior.force("Fall") ?? this.behavior.force("\u843D\u4E0B\u3059\u308B");
|
|
1731
1738
|
}
|
|
1732
|
-
selectNextBehavior(environment, bounds) {
|
|
1739
|
+
selectNextBehavior(environment, bounds, relocateOnFallback = true) {
|
|
1733
1740
|
const selected = this.behavior.selectNext(environment);
|
|
1734
|
-
if (this.behavior.usedFallback()) {
|
|
1741
|
+
if (relocateOnFallback && this.behavior.usedFallback()) {
|
|
1735
1742
|
this.state.x = Math.trunc(bounds.x + this.random() * bounds.width);
|
|
1736
1743
|
this.state.y = bounds.y - 256;
|
|
1737
1744
|
}
|
|
1738
1745
|
return selected;
|
|
1739
1746
|
}
|
|
1747
|
+
selectEdgeBehavior(bounds, platforms) {
|
|
1748
|
+
const original = { x: this.state.x, y: this.state.y };
|
|
1749
|
+
const platform = platforms.find((candidate) => candidate.element === this.activePlatformElement);
|
|
1750
|
+
if (platform) {
|
|
1751
|
+
const left = platform.x;
|
|
1752
|
+
const right = platform.x + platform.width;
|
|
1753
|
+
const top = platform.y;
|
|
1754
|
+
const bottom = platform.y + platform.height;
|
|
1755
|
+
if (Math.abs(this.state.y - top) <= PLATFORM_EDGE_TOLERANCE || Math.abs(this.state.y - bottom) <= PLATFORM_EDGE_TOLERANCE) {
|
|
1756
|
+
if (this.state.x < left) this.state.x = left;
|
|
1757
|
+
else if (this.state.x > right) this.state.x = right;
|
|
1758
|
+
} else if (Math.abs(this.state.x - left) <= PLATFORM_EDGE_TOLERANCE || Math.abs(this.state.x - right) <= PLATFORM_EDGE_TOLERANCE) {
|
|
1759
|
+
if (this.state.y < top) this.state.y = top;
|
|
1760
|
+
else if (this.state.y > bottom) this.state.y = bottom;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
const selected = this.selectNextBehavior(this.createEnvironment(bounds, platforms), bounds, false);
|
|
1764
|
+
if (this.behavior.usedFallback() || selected?.name === "Fall" || selected?.name === "\u843D\u4E0B\u3059\u308B") {
|
|
1765
|
+
this.state.x = original.x;
|
|
1766
|
+
this.state.y = original.y;
|
|
1767
|
+
}
|
|
1768
|
+
return selected ?? this.findFallBehavior();
|
|
1769
|
+
}
|
|
1740
1770
|
createEnvironment(bounds, platforms = this.platforms) {
|
|
1741
1771
|
const workArea = environmentRectangle(bounds);
|
|
1742
1772
|
const inactive = environmentRectangle({ x: -100, y: -100, width: 0, height: 0 });
|
|
@@ -1767,7 +1797,7 @@ var Mascot = class {
|
|
|
1767
1797
|
}
|
|
1768
1798
|
const anchor = this.state;
|
|
1769
1799
|
const current = platforms.find((platform) => platform.element === this.activePlatformElement);
|
|
1770
|
-
if (current && (isOnTop(anchor, current,
|
|
1800
|
+
if (current && (isOnTop(anchor, current, PLATFORM_EDGE_TOLERANCE) || isOnBottom(anchor, current, PLATFORM_EDGE_TOLERANCE) || isOnLeft(anchor, current, PLATFORM_EDGE_TOLERANCE) || isOnRight(anchor, current, PLATFORM_EDGE_TOLERANCE))) return current;
|
|
1771
1801
|
if (this.state.vy >= 0) {
|
|
1772
1802
|
const landingPlatform = platforms.filter((platform) => anchor.x >= platform.x && anchor.x <= platform.x + platform.width && platform.y >= anchor.y - 1).sort((left, right) => left.y - right.y)[0];
|
|
1773
1803
|
if (landingPlatform) {
|
|
@@ -2020,9 +2050,13 @@ var ShimejiEngine = class {
|
|
|
2020
2050
|
if (!spec) throw new Error(`Character '${characterId}' is not registered`);
|
|
2021
2051
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
2022
2052
|
const random = this.options.random ?? Math.random;
|
|
2053
|
+
const randomX = Math.trunc(bounds.x + random() * bounds.width);
|
|
2054
|
+
const spawnInset = Math.min(2, bounds.width / 2);
|
|
2023
2055
|
const spawnOptions = {
|
|
2024
2056
|
...position,
|
|
2025
|
-
|
|
2057
|
+
// Fall treats the wall in the facing direction as ground. Keep implicit
|
|
2058
|
+
// spawns clear of both side-wall tolerances so even random() === 0 falls.
|
|
2059
|
+
x: position.x ?? Math.min(Math.max(randomX, bounds.x + spawnInset), bounds.x + bounds.width - spawnInset),
|
|
2026
2060
|
y: position.y ?? bounds.y + 2
|
|
2027
2061
|
};
|
|
2028
2062
|
const id = `shimeji-${this.nextMascotId++}`;
|