@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.d.cts
CHANGED
|
@@ -422,11 +422,14 @@ declare class BehaviorController {
|
|
|
422
422
|
selectInitial(environment: MascotEnvironment, requestedName?: string): BehaviorDefinition | undefined;
|
|
423
423
|
/** Selects the weighted transition following the current behavior. */
|
|
424
424
|
selectNext(environment: MascotEnvironment): BehaviorDefinition | undefined;
|
|
425
|
+
/** Tries a weighted transition from a subset without changing history when none applies. */
|
|
426
|
+
trySelectNext(environment: MascotEnvironment, predicate: (behavior: BehaviorDefinition) => boolean): BehaviorDefinition | undefined;
|
|
425
427
|
/** Whether the most recent transition had no effective weighted candidate. */
|
|
426
428
|
usedFallback(): boolean;
|
|
427
429
|
/** Replaces selection history so an external interaction can force a behavior. */
|
|
428
430
|
force(name: string): BehaviorDefinition | undefined;
|
|
429
431
|
private choose;
|
|
432
|
+
private nextPool;
|
|
430
433
|
private resolve;
|
|
431
434
|
private findFallBehavior;
|
|
432
435
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -422,11 +422,14 @@ declare class BehaviorController {
|
|
|
422
422
|
selectInitial(environment: MascotEnvironment, requestedName?: string): BehaviorDefinition | undefined;
|
|
423
423
|
/** Selects the weighted transition following the current behavior. */
|
|
424
424
|
selectNext(environment: MascotEnvironment): BehaviorDefinition | undefined;
|
|
425
|
+
/** Tries a weighted transition from a subset without changing history when none applies. */
|
|
426
|
+
trySelectNext(environment: MascotEnvironment, predicate: (behavior: BehaviorDefinition) => boolean): BehaviorDefinition | undefined;
|
|
425
427
|
/** Whether the most recent transition had no effective weighted candidate. */
|
|
426
428
|
usedFallback(): boolean;
|
|
427
429
|
/** Replaces selection history so an external interaction can force a behavior. */
|
|
428
430
|
force(name: string): BehaviorDefinition | undefined;
|
|
429
431
|
private choose;
|
|
432
|
+
private nextPool;
|
|
430
433
|
private resolve;
|
|
431
434
|
private findFallBehavior;
|
|
432
435
|
}
|
package/dist/index.js
CHANGED
|
@@ -615,12 +615,17 @@ var BehaviorController = class {
|
|
|
615
615
|
}
|
|
616
616
|
/** Selects the weighted transition following the current behavior. */
|
|
617
617
|
selectNext(environment) {
|
|
618
|
-
const
|
|
619
|
-
const pool = this.previous && this.previous.nextAdditive === false ? next : [...this.spec.behaviors, ...next];
|
|
620
|
-
const selected = this.choose(pool, environment);
|
|
618
|
+
const selected = this.choose(this.nextPool(), environment);
|
|
621
619
|
this.fallbackSelected = selected === void 0;
|
|
622
620
|
return this.previous = selected ?? this.findFallBehavior();
|
|
623
621
|
}
|
|
622
|
+
/** Tries a weighted transition from a subset without changing history when none applies. */
|
|
623
|
+
trySelectNext(environment, predicate) {
|
|
624
|
+
const selected = this.choose(this.nextPool().filter(predicate), environment);
|
|
625
|
+
if (!selected) return void 0;
|
|
626
|
+
this.fallbackSelected = false;
|
|
627
|
+
return this.previous = selected;
|
|
628
|
+
}
|
|
624
629
|
/** Whether the most recent transition had no effective weighted candidate. */
|
|
625
630
|
usedFallback() {
|
|
626
631
|
return this.fallbackSelected;
|
|
@@ -636,6 +641,10 @@ var BehaviorController = class {
|
|
|
636
641
|
const chosen = selectWeighted(applicable, (behavior) => behavior.frequency, this.random);
|
|
637
642
|
return chosen ? this.resolve(chosen) : void 0;
|
|
638
643
|
}
|
|
644
|
+
nextPool() {
|
|
645
|
+
const next = this.previous?.nextBehaviors ?? [];
|
|
646
|
+
return this.previous && this.previous.nextAdditive === false ? next : [...this.spec.behaviors, ...next];
|
|
647
|
+
}
|
|
639
648
|
resolve(behavior) {
|
|
640
649
|
if (behavior.type !== "Reference") return behavior;
|
|
641
650
|
const target = this.spec.behaviors.find((candidate) => candidate.type === "Behavior" && candidate.name === behavior.name);
|
|
@@ -1591,6 +1600,13 @@ function environmentRectangle(bounds) {
|
|
|
1591
1600
|
}
|
|
1592
1601
|
var PLATFORM_NEARBY_DISTANCE = 400;
|
|
1593
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;
|
|
1606
|
+
var IE_BEHAVIOR_NAME_PATTERN = /wall|climb|crawl|壁|登|よじ/i;
|
|
1607
|
+
function isIEBehavior(behavior) {
|
|
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));
|
|
1609
|
+
}
|
|
1594
1610
|
function distanceToRectangle(point, rectangle) {
|
|
1595
1611
|
const dx = Math.max(rectangle.x - point.x, 0, point.x - rectangle.x - rectangle.width);
|
|
1596
1612
|
const dy = Math.max(rectangle.y - point.y, 0, point.y - rectangle.y - rectangle.height);
|
|
@@ -1653,7 +1669,7 @@ var Mascot = class {
|
|
|
1653
1669
|
random;
|
|
1654
1670
|
forceInitialFall;
|
|
1655
1671
|
/** Advances behavior, animation, physics, and rendering by one clock tick. */
|
|
1656
|
-
tick(deltaMs, bounds, platforms = []) {
|
|
1672
|
+
tick(deltaMs, bounds, platforms = [], siblings = []) {
|
|
1657
1673
|
if (this.destroyed) return;
|
|
1658
1674
|
this.platforms = platforms;
|
|
1659
1675
|
try {
|
|
@@ -1661,7 +1677,7 @@ var Mascot = class {
|
|
|
1661
1677
|
this.accumulatedMs += Math.max(0, deltaMs);
|
|
1662
1678
|
while (this.accumulatedMs >= this.frameDuration && !this.destroyed) {
|
|
1663
1679
|
this.accumulatedMs -= this.frameDuration;
|
|
1664
|
-
this.legacyTick(bounds, platforms);
|
|
1680
|
+
this.legacyTick(bounds, platforms, siblings);
|
|
1665
1681
|
}
|
|
1666
1682
|
} catch (error) {
|
|
1667
1683
|
this.callbacks.error(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -1674,6 +1690,24 @@ var Mascot = class {
|
|
|
1674
1690
|
snapshot() {
|
|
1675
1691
|
return { ...this.state };
|
|
1676
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
|
+
}
|
|
1677
1711
|
/** Removes listeners, DOM nodes, and object URLs owned by this mascot. */
|
|
1678
1712
|
destroy() {
|
|
1679
1713
|
if (this.destroyed) return;
|
|
@@ -1702,11 +1736,13 @@ var Mascot = class {
|
|
|
1702
1736
|
if (!this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms)) return;
|
|
1703
1737
|
}
|
|
1704
1738
|
}
|
|
1705
|
-
legacyTick(bounds, platforms) {
|
|
1739
|
+
legacyTick(bounds, platforms, siblings) {
|
|
1706
1740
|
this.ensureBehavior(bounds, platforms, false);
|
|
1707
1741
|
if (!this.currentBehavior) return;
|
|
1742
|
+
const previous = { x: this.state.x, y: this.state.y };
|
|
1708
1743
|
const result = this.actions.step(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1709
1744
|
if (this.destroyed) return;
|
|
1745
|
+
this.avoidSiblingCollision(previous, bounds, platforms, siblings);
|
|
1710
1746
|
if (result === "lost-ground") {
|
|
1711
1747
|
this.state.dragging = false;
|
|
1712
1748
|
this.actions.cancel();
|
|
@@ -1724,6 +1760,31 @@ var Mascot = class {
|
|
|
1724
1760
|
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1725
1761
|
}
|
|
1726
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
|
+
return isAhead && overlapsVertically && crossesHorizontally;
|
|
1782
|
+
});
|
|
1783
|
+
if (!collision) return;
|
|
1784
|
+
this.state.x = previous.x;
|
|
1785
|
+
this.state.vx = 0;
|
|
1786
|
+
this.state.lookRight = !this.state.lookRight;
|
|
1787
|
+
}
|
|
1727
1788
|
isOutsideVisibleBounds(bounds) {
|
|
1728
1789
|
const sprite = this.spec.sprites[this.state.sprite];
|
|
1729
1790
|
const width = typeof sprite === "object" && "width" in sprite && sprite.width !== void 0 ? sprite.width : 128;
|
|
@@ -1760,7 +1821,9 @@ var Mascot = class {
|
|
|
1760
1821
|
else if (this.state.y > bottom) this.state.y = bottom;
|
|
1761
1822
|
}
|
|
1762
1823
|
}
|
|
1763
|
-
const
|
|
1824
|
+
const environment = this.createEnvironment(bounds, platforms);
|
|
1825
|
+
const interruptedBehavior = this.currentBehavior;
|
|
1826
|
+
const selected = this.behavior.trySelectNext(environment, (candidate) => candidate.name !== interruptedBehavior?.name && isIEBehavior(candidate)) ?? this.selectNextBehavior(environment, bounds, false);
|
|
1764
1827
|
if (this.behavior.usedFallback() || selected?.name === "Fall" || selected?.name === "\u843D\u4E0B\u3059\u308B") {
|
|
1765
1828
|
this.state.x = original.x;
|
|
1766
1829
|
this.state.y = original.y;
|
|
@@ -2143,7 +2206,9 @@ var ShimejiEngine = class {
|
|
|
2143
2206
|
this.lastFrameTime = timestamp;
|
|
2144
2207
|
const delta = Math.max(0, Math.min(rawDelta, this.options.maxDeltaTime));
|
|
2145
2208
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
2146
|
-
|
|
2209
|
+
const mascots = [...this.mascots.values()];
|
|
2210
|
+
const collisionBoxes = mascots.map((mascot) => mascot.collisionBox());
|
|
2211
|
+
for (const mascot of mascots) mascot.tick(delta, bounds, platforms, collisionBoxes);
|
|
2147
2212
|
this.emitState();
|
|
2148
2213
|
this.animationFrame = this.container.ownerDocument.defaultView?.requestAnimationFrame(this.onAnimationFrame);
|
|
2149
2214
|
};
|