@react-shimeji/core 0.2.2 → 0.3.0
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 +1056 -336
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -27
- package/dist/index.d.ts +73 -27
- package/dist/index.js +1056 -336
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,32 +1,44 @@
|
|
|
1
1
|
// src/dom.ts
|
|
2
2
|
var DomManager = class {
|
|
3
|
-
/** Uses the supplied element
|
|
3
|
+
/** Uses the supplied element as the containing block and clipping boundary. */
|
|
4
4
|
constructor(container, sprites) {
|
|
5
5
|
this.container = container;
|
|
6
6
|
this.sprites = sprites;
|
|
7
|
+
const view = container.ownerDocument.defaultView;
|
|
8
|
+
const computedStyle = view?.getComputedStyle(container);
|
|
9
|
+
if (!computedStyle?.position || computedStyle.position === "static") this.applyContainerStyle("position", "relative");
|
|
10
|
+
if (computedStyle?.overflowX === "visible" || !computedStyle?.overflowX) {
|
|
11
|
+
this.applyContainerStyle("overflowX", "clip");
|
|
12
|
+
}
|
|
7
13
|
}
|
|
8
14
|
container;
|
|
9
15
|
sprites;
|
|
10
16
|
handles = /* @__PURE__ */ new Set();
|
|
17
|
+
restoreContainerStyles = [];
|
|
11
18
|
/** Reattaches mascot elements if application code temporarily removed them. */
|
|
12
19
|
ensureMounted() {
|
|
13
20
|
for (const handle of this.handles) {
|
|
14
21
|
if (handle.element.parentElement !== this.container) this.container.appendChild(handle.element);
|
|
15
22
|
}
|
|
16
23
|
}
|
|
17
|
-
/** Returns
|
|
24
|
+
/** Returns bounds in the container-local coordinate system. */
|
|
18
25
|
getBounds() {
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
return { x: 0, y: 0, width: this.container.clientWidth, height: this.container.clientHeight };
|
|
27
|
+
}
|
|
28
|
+
/** Converts a viewport client coordinate into container-local coordinates. */
|
|
29
|
+
toLocalPoint(clientX, clientY) {
|
|
30
|
+
const rectangle = this.container.getBoundingClientRect();
|
|
31
|
+
return { x: clientX - rectangle.left, y: clientY - rectangle.top };
|
|
21
32
|
}
|
|
22
33
|
/** Creates a mascot node and acquires its spritesheet resource. */
|
|
23
34
|
createMascot(spec, mascotId, mascotClassName) {
|
|
24
35
|
const spriteLease = this.sprites.acquire(spec.spritesheet);
|
|
25
|
-
const element =
|
|
36
|
+
const element = this.container.ownerDocument.createElement("div");
|
|
26
37
|
element.dataset.shimejiId = mascotId;
|
|
38
|
+
element.setAttribute("aria-hidden", "true");
|
|
27
39
|
if (mascotClassName) element.className = mascotClassName;
|
|
28
|
-
Object.assign(element.style, { position: "
|
|
29
|
-
const spriteElement =
|
|
40
|
+
Object.assign(element.style, { position: "absolute", left: "0", top: "0", width: "0", height: "0", pointerEvents: "none", zIndex: "9999", userSelect: "none", willChange: "transform" });
|
|
41
|
+
const spriteElement = this.container.ownerDocument.createElement("div");
|
|
30
42
|
Object.assign(spriteElement.style, { position: "absolute", left: "0", top: "0", backgroundRepeat: "no-repeat", transformOrigin: "center center", pointerEvents: "auto", touchAction: "none", userSelect: "none" });
|
|
31
43
|
element.appendChild(spriteElement);
|
|
32
44
|
const handle = { element, spriteElement, spriteLease };
|
|
@@ -37,30 +49,34 @@ var DomManager = class {
|
|
|
37
49
|
/** Paints one mascot state into its existing DOM nodes. */
|
|
38
50
|
render(handle, spec, state) {
|
|
39
51
|
const sprite = this.sprites.resolve(spec, handle.spriteLease, state.sprite);
|
|
40
|
-
handle.element.style.transform = `translate3d(${state.x - state.anchorX}px, ${state.y - state.anchorY}px, 0)`;
|
|
41
52
|
handle.spriteElement.style.left = "0";
|
|
42
53
|
handle.spriteElement.style.top = "0";
|
|
43
54
|
handle.spriteElement.style.transform = `scaleX(${state.lookRight ? -1 : 1})`;
|
|
44
|
-
if (!sprite)
|
|
55
|
+
if (!sprite) {
|
|
56
|
+
handle.element.style.transform = `translate3d(${state.x - state.anchorX}px, ${state.y - state.anchorY}px, 0)`;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
let width = 128;
|
|
60
|
+
let height = 128;
|
|
45
61
|
handle.spriteElement.style.backgroundImage = `url("${sprite.url.replaceAll('"', '\\"')}")`;
|
|
46
62
|
if (sprite.rectangle) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
handle.element.style.width = `${sprite.rectangle.width}px`;
|
|
50
|
-
handle.element.style.height = `${sprite.rectangle.height}px`;
|
|
63
|
+
width = sprite.rectangle.width;
|
|
64
|
+
height = sprite.rectangle.height;
|
|
51
65
|
handle.spriteElement.style.backgroundPosition = `${-sprite.rectangle.x}px ${-sprite.rectangle.y}px`;
|
|
52
66
|
handle.spriteElement.style.backgroundSize = "auto";
|
|
53
67
|
} else {
|
|
54
68
|
handle.spriteElement.style.backgroundPosition = "0 0";
|
|
55
69
|
handle.spriteElement.style.backgroundSize = "contain";
|
|
56
|
-
handle.spriteElement.style.width = "128px";
|
|
57
|
-
handle.spriteElement.style.height = "128px";
|
|
58
70
|
const definition = Object.values(spec.sprites).find((candidate) => typeof candidate === "object" && "url" in candidate && candidate.url === sprite.url);
|
|
59
|
-
if (typeof definition === "object" && "width" in definition && definition.width !== void 0)
|
|
60
|
-
if (typeof definition === "object" && "height" in definition && definition.height !== void 0)
|
|
61
|
-
handle.element.style.width = handle.spriteElement.style.width;
|
|
62
|
-
handle.element.style.height = handle.spriteElement.style.height;
|
|
71
|
+
if (typeof definition === "object" && "width" in definition && definition.width !== void 0) width = definition.width;
|
|
72
|
+
if (typeof definition === "object" && "height" in definition && definition.height !== void 0) height = definition.height;
|
|
63
73
|
}
|
|
74
|
+
handle.spriteElement.style.width = `${width}px`;
|
|
75
|
+
handle.spriteElement.style.height = `${height}px`;
|
|
76
|
+
handle.element.style.width = `${width}px`;
|
|
77
|
+
handle.element.style.height = `${height}px`;
|
|
78
|
+
const anchorX = state.lookRight ? width - state.anchorX : state.anchorX;
|
|
79
|
+
handle.element.style.transform = `translate3d(${state.x - anchorX}px, ${state.y - state.anchorY}px, 0)`;
|
|
64
80
|
}
|
|
65
81
|
/** Removes one mascot node and releases its temporary image URL. */
|
|
66
82
|
removeMascot(handle) {
|
|
@@ -76,6 +92,14 @@ var DomManager = class {
|
|
|
76
92
|
/** Removes every mascot element and releases its temporary image URL. */
|
|
77
93
|
destroy() {
|
|
78
94
|
for (const handle of [...this.handles]) this.removeMascot(handle);
|
|
95
|
+
for (const restore of this.restoreContainerStyles.splice(0).reverse()) restore();
|
|
96
|
+
}
|
|
97
|
+
applyContainerStyle(property, value) {
|
|
98
|
+
const previous = this.container.style[property];
|
|
99
|
+
this.container.style[property] = value;
|
|
100
|
+
this.restoreContainerStyles.push(() => {
|
|
101
|
+
if (this.container.style[property] === value) this.container.style[property] = previous;
|
|
102
|
+
});
|
|
79
103
|
}
|
|
80
104
|
};
|
|
81
105
|
|
|
@@ -88,6 +112,9 @@ var actionTypeNames = {
|
|
|
88
112
|
Animate: "Animate",
|
|
89
113
|
Move: "Move",
|
|
90
114
|
Embedded: "Embedded",
|
|
115
|
+
Composite: "Sequence",
|
|
116
|
+
Fixed: "Animate",
|
|
117
|
+
Pause: "Stay",
|
|
91
118
|
\u8907\u5408: "Sequence",
|
|
92
119
|
\u9078\u629E: "Select",
|
|
93
120
|
\u53C2\u7167: "Reference",
|
|
@@ -137,12 +164,13 @@ function actionProperty(element, ...names) {
|
|
|
137
164
|
function parseAnimation(element) {
|
|
138
165
|
const poses = directChildren(element, "Pose", "\u30DD\u30FC\u30BA").map((pose) => ({
|
|
139
166
|
sprite: attribute(pose, "Image", "\u753B\u50CF") ?? "/shime1.png",
|
|
140
|
-
anchor: parsePoint(attribute(pose, "Anchor", "\u57FA\u6E96\u5EA7\u6A19"), { x: 64, y: 128 }),
|
|
167
|
+
anchor: parsePoint(attribute(pose, "ImageAnchor", "Anchor", "\u57FA\u6E96\u5EA7\u6A19"), { x: 64, y: 128 }),
|
|
141
168
|
velocity: parsePoint(attribute(pose, "Velocity", "\u79FB\u52D5\u901F\u5EA6")),
|
|
142
169
|
duration: Number(attribute(pose, "Duration", "\u9577\u3055") ?? 1)
|
|
143
170
|
}));
|
|
144
171
|
const condition = attribute(element, "Condition", "\u6761\u4EF6");
|
|
145
|
-
|
|
172
|
+
const turn = (attribute(element, "IsTurn", "Turn") ?? "false").toLowerCase() === "true";
|
|
173
|
+
return { poses, ...condition !== void 0 && { condition }, ...turn && { turn } };
|
|
146
174
|
}
|
|
147
175
|
function parseActionElement(element) {
|
|
148
176
|
const isReference = element.localName === "ActionReference" || element.localName === "\u52D5\u4F5C\u53C2\u7167";
|
|
@@ -166,20 +194,26 @@ function parseActionElement(element) {
|
|
|
166
194
|
};
|
|
167
195
|
const properties = [
|
|
168
196
|
["duration", actionProperty(element, "Duration", "\u9577\u3055")],
|
|
169
|
-
["gap", actionProperty(element, "Gap", "\u9593\u9694")],
|
|
197
|
+
["gap", actionProperty(element, "Gap", "\u9593\u9694", "\u305A\u308C")],
|
|
170
198
|
["targetX", actionProperty(element, "TargetX", "\u76EE\u7684\u5730X")],
|
|
171
199
|
["targetY", actionProperty(element, "TargetY", "\u76EE\u7684\u5730Y")],
|
|
172
|
-
["velocity", actionProperty(element, "Velocity", "\u901F\u5EA6")],
|
|
200
|
+
["velocity", actionProperty(element, "VelocityParam", "Velocity", "\u901F\u5EA6")],
|
|
173
201
|
["x", actionProperty(element, "X", "\u5909\u4F4DX")],
|
|
174
202
|
["y", actionProperty(element, "Y", "\u5909\u4F4DY")],
|
|
203
|
+
["offsetX", actionProperty(element, "OffsetX", "\u7AEFX")],
|
|
204
|
+
["offsetY", actionProperty(element, "OffsetY", "\u7AEFY")],
|
|
205
|
+
["offsetType", actionProperty(element, "OffsetType")],
|
|
175
206
|
["initialVx", actionProperty(element, "InitialVX", "InitialVx", "\u521D\u901FX")],
|
|
176
207
|
["initialVy", actionProperty(element, "InitialVY", "InitialVy", "\u521D\u901FY")],
|
|
177
|
-
["resistanceX", actionProperty(element, "ResistanceX", "\u7A7A\u6C17\u62B5\u6297X")],
|
|
178
|
-
["resistanceY", actionProperty(element, "ResistanceY", "\u7A7A\u6C17\u62B5\u6297Y")],
|
|
208
|
+
["resistanceX", actionProperty(element, "RegistanceX", "ResistanceX", "\u7A7A\u6C17\u62B5\u6297X")],
|
|
209
|
+
["resistanceY", actionProperty(element, "RegistanceY", "ResistanceY", "\u7A7A\u6C17\u62B5\u6297Y")],
|
|
179
210
|
["gravity", actionProperty(element, "Gravity", "\u91CD\u529B")],
|
|
180
|
-
["bornX", actionProperty(element, "BornX", "\u8A95\u751FX")],
|
|
181
|
-
["bornY", actionProperty(element, "BornY", "\u8A95\u751FY")],
|
|
182
|
-
["bornBehavior", actionProperty(element, "BornBehavior", "\u8A95\u751F\u6642\u306E\u884C\u52D5")],
|
|
211
|
+
["bornX", actionProperty(element, "BornX", "\u8A95\u751FX", "\u751F\u307E\u308C\u308B\u5834\u6240X")],
|
|
212
|
+
["bornY", actionProperty(element, "BornY", "\u8A95\u751FY", "\u751F\u307E\u308C\u308B\u5834\u6240Y")],
|
|
213
|
+
["bornBehavior", actionProperty(element, "BornBehavior", "BornBehaviour", "\u8A95\u751F\u6642\u306E\u884C\u52D5", "\u751F\u307E\u308C\u305F\u6642\u306E\u884C\u52D5")],
|
|
214
|
+
["bornMascot", actionProperty(element, "BornMascot")],
|
|
215
|
+
["bornCount", actionProperty(element, "BornCount")],
|
|
216
|
+
["bornInterval", actionProperty(element, "BornInterval")],
|
|
183
217
|
["ieOffsetX", actionProperty(element, "IEOffsetX", "IE\u306E\u7AEFX")],
|
|
184
218
|
["ieOffsetY", actionProperty(element, "IEOffsetY", "IE\u306E\u7AEFY")],
|
|
185
219
|
["lookRight", actionProperty(element, "LookRight", "\u53F3\u5411\u304D")]
|
|
@@ -195,18 +229,33 @@ function parseActionsXml(xml) {
|
|
|
195
229
|
const roots = lists.length ? lists : [document2.documentElement];
|
|
196
230
|
return roots.flatMap((list) => directChildren(list, "Action", "\u52D5\u4F5C").map(parseActionElement));
|
|
197
231
|
}
|
|
232
|
+
function parseNextBehaviors(element, inheritedConditions) {
|
|
233
|
+
const behaviors = [];
|
|
234
|
+
for (const child of Array.from(element.children)) {
|
|
235
|
+
if (child.localName === "Condition" || child.localName === "\u6761\u4EF6") {
|
|
236
|
+
const condition = attribute(child, "Condition", "\u6761\u4EF6");
|
|
237
|
+
behaviors.push(...parseNextBehaviors(child, [...inheritedConditions, ...condition ? [condition] : []]));
|
|
238
|
+
} else if (["Behavior", "\u884C\u52D5", "BehaviorReference", "BehaviorReferance", "\u884C\u52D5\u53C2\u7167"].includes(child.localName)) {
|
|
239
|
+
behaviors.push(parseBehaviorElement(child, inheritedConditions, 0));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return behaviors;
|
|
243
|
+
}
|
|
198
244
|
function parseBehaviorElement(element, inheritedConditions, groupIndex) {
|
|
199
245
|
const condition = attribute(element, "Condition", "\u6761\u4EF6");
|
|
200
246
|
const conditions = [...inheritedConditions, ...condition ? [condition] : []];
|
|
201
247
|
const nextList = directChildren(element, "NextBehaviorList", "NextBehavior", "\u6B21\u306E\u884C\u52D5\u30EA\u30B9\u30C8")[0];
|
|
202
|
-
const nextBehaviors = nextList ?
|
|
248
|
+
const nextBehaviors = nextList ? parseNextBehaviors(nextList, []) : [];
|
|
203
249
|
const reference = element.localName === "BehaviorReference" || element.localName === "BehaviorReferance" || element.localName === "\u884C\u52D5\u53C2\u7167";
|
|
250
|
+
const actionName = attribute(element, "Action", "\u52D5\u4F5C");
|
|
204
251
|
return {
|
|
205
252
|
type: reference ? "Reference" : "Behavior",
|
|
206
253
|
name: attribute(element, "Name", "\u540D\u524D") ?? "",
|
|
207
254
|
frequency: Number(attribute(element, "Frequency", "\u983B\u5EA6") ?? 0),
|
|
208
255
|
conditions,
|
|
209
256
|
nextBehaviors,
|
|
257
|
+
...nextList && { nextAdditive: (attribute(nextList, "Add", "\u8FFD\u52A0") ?? "true").toLowerCase() === "true" },
|
|
258
|
+
...actionName !== void 0 && { actionName },
|
|
210
259
|
groupIndex,
|
|
211
260
|
hidden: (attribute(element, "Hidden", "\u975E\u8868\u793A") ?? "false").toLowerCase() === "true"
|
|
212
261
|
};
|
|
@@ -312,7 +361,7 @@ var functions = {
|
|
|
312
361
|
};
|
|
313
362
|
var constants = { E: Math.E, PI: Math.PI };
|
|
314
363
|
function normalizeExpression(source) {
|
|
315
|
-
return source.trim().replace(/^(?:#|\$)\{/, "").replace(/\}$/, "").replace(/Math\.(random|min|max|abs|floor|ceil|round|sqrt|pow|sin|cos|tan|asin|acos|atan|sinh|cosh|tanh|asinh|acosh|atanh|cbrt|log|log2|log10|exp|expm1|log1p|trunc|sign|hypot|atan2)/g, "$1").replace(/Math\.(PI|E)\b/g, "$1").replace(/Mascot\./gi, "mascot.").replace(/TargetX|目的地X/gi, "targetX").replace(/TargetY|目的地Y/gi, "targetY").replace(/FootX|足X/gi, "footX").replace(/FootY|足Y/gi, "footY").replace(/MaxCount/gi, "maxCount").replace(/Gap
|
|
364
|
+
return source.trim().replace(/^(?:#|\$)\{/, "").replace(/\}$/, "").replace(/Math\.(random|min|max|abs|floor|ceil|round|sqrt|pow|sin|cos|tan|asin|acos|atan|sinh|cosh|tanh|asinh|acosh|atanh|cbrt|log|log2|log10|exp|expm1|log1p|trunc|sign|hypot|atan2)/g, "$1").replace(/Math\.(PI|E)\b/g, "$1").replace(/Mascot\./gi, "mascot.").replace(/TargetX|目的地X/gi, "targetX").replace(/TargetY|目的地Y/gi, "targetY").replace(/FootX|足X/gi, "footX").replace(/FootY|足Y/gi, "footY").replace(/VelocityX|速度X/gi, "velocityX").replace(/VelocityY|速度Y/gi, "velocityY").replace(/MaxCount/gi, "maxCount").replace(/Gap|ずれ/gi, "gap").replace(/\band\b/gi, "&&").replace(/\bor\b/gi, "||").replace(/\bnot\b/gi, "!");
|
|
316
365
|
}
|
|
317
366
|
function tokenize(source) {
|
|
318
367
|
const tokens = [];
|
|
@@ -513,7 +562,7 @@ function evaluateNode(node, scope) {
|
|
|
513
562
|
}
|
|
514
563
|
}
|
|
515
564
|
var expressionCache = /* @__PURE__ */ new Map();
|
|
516
|
-
function evaluateExpression(expression, environment, fallback) {
|
|
565
|
+
function evaluateExpression(expression, environment, fallback, random = Math.random) {
|
|
517
566
|
if (expression === void 0) return fallback;
|
|
518
567
|
if (typeof expression !== "string") return expression;
|
|
519
568
|
try {
|
|
@@ -523,7 +572,7 @@ function evaluateExpression(expression, environment, fallback) {
|
|
|
523
572
|
ast = new Parser(tokenize(normalized)).parse();
|
|
524
573
|
expressionCache.set(normalized, ast);
|
|
525
574
|
}
|
|
526
|
-
const result = evaluateNode(ast, environment);
|
|
575
|
+
const result = evaluateNode(ast, { ...environment, random: (maximum = 1) => random() * Number(maximum) });
|
|
527
576
|
if (typeof fallback === "boolean") return Boolean(result);
|
|
528
577
|
const numericResult = Number(result);
|
|
529
578
|
return Number.isNaN(numericResult) ? fallback : numericResult;
|
|
@@ -531,8 +580,8 @@ function evaluateExpression(expression, environment, fallback) {
|
|
|
531
580
|
return fallback;
|
|
532
581
|
}
|
|
533
582
|
}
|
|
534
|
-
function conditionsMatch(conditions, environment) {
|
|
535
|
-
return conditions.every((condition) => evaluateExpression(condition, environment, false));
|
|
583
|
+
function conditionsMatch(conditions, environment, random = Math.random) {
|
|
584
|
+
return conditions.every((condition) => evaluateExpression(condition, environment, false, random));
|
|
536
585
|
}
|
|
537
586
|
function selectWeighted(items, weight, random = Math.random) {
|
|
538
587
|
const weighted = items.map((item) => ({ item, weight: Math.max(0, weight(item)) }));
|
|
@@ -554,61 +603,71 @@ var BehaviorController = class {
|
|
|
554
603
|
spec;
|
|
555
604
|
random;
|
|
556
605
|
previous;
|
|
606
|
+
fallbackSelected = false;
|
|
557
607
|
/** Selects an initial behavior, honoring an explicit requested name when possible. */
|
|
558
608
|
selectInitial(environment, requestedName) {
|
|
609
|
+
this.fallbackSelected = false;
|
|
559
610
|
if (requestedName) {
|
|
560
611
|
const requested = this.spec.behaviors.find((behavior) => behavior.name === requestedName);
|
|
561
|
-
if (requested
|
|
612
|
+
if (requested) return this.previous = this.resolve(requested);
|
|
562
613
|
}
|
|
563
|
-
const fall = this.findFallBehavior();
|
|
564
|
-
if (fall && !this.isOnAnyBoundary(environment)) return this.previous = fall;
|
|
565
614
|
return this.previous = this.choose(this.spec.behaviors, environment);
|
|
566
615
|
}
|
|
567
616
|
/** Selects the weighted transition following the current behavior. */
|
|
568
617
|
selectNext(environment) {
|
|
569
|
-
const
|
|
570
|
-
|
|
618
|
+
const next = this.previous?.nextBehaviors ?? [];
|
|
619
|
+
const pool = this.previous && this.previous.nextAdditive === false ? next : [...this.spec.behaviors, ...next];
|
|
620
|
+
const selected = this.choose(pool, environment);
|
|
621
|
+
this.fallbackSelected = selected === void 0;
|
|
622
|
+
return this.previous = selected ?? this.findFallBehavior();
|
|
623
|
+
}
|
|
624
|
+
/** Whether the most recent transition had no effective weighted candidate. */
|
|
625
|
+
usedFallback() {
|
|
626
|
+
return this.fallbackSelected;
|
|
571
627
|
}
|
|
572
628
|
/** Replaces selection history so an external interaction can force a behavior. */
|
|
573
629
|
force(name) {
|
|
630
|
+
this.fallbackSelected = false;
|
|
574
631
|
const behavior = this.spec.behaviors.find((candidate) => candidate.name === name);
|
|
575
632
|
return this.previous = behavior ? this.resolve(behavior) : void 0;
|
|
576
633
|
}
|
|
577
634
|
choose(pool, environment) {
|
|
578
|
-
const applicable = pool.filter((behavior) => conditionsMatch(behavior.conditions, environment));
|
|
635
|
+
const applicable = pool.filter((behavior) => conditionsMatch(behavior.conditions, environment, this.random));
|
|
579
636
|
const chosen = selectWeighted(applicable, (behavior) => behavior.frequency, this.random);
|
|
580
637
|
return chosen ? this.resolve(chosen) : void 0;
|
|
581
638
|
}
|
|
582
639
|
resolve(behavior) {
|
|
583
640
|
if (behavior.type !== "Reference") return behavior;
|
|
584
641
|
const target = this.spec.behaviors.find((candidate) => candidate.type === "Behavior" && candidate.name === behavior.name);
|
|
585
|
-
return target ? {
|
|
642
|
+
return target ? {
|
|
643
|
+
...target,
|
|
644
|
+
...behavior,
|
|
645
|
+
type: "Behavior",
|
|
646
|
+
nextBehaviors: target.nextBehaviors,
|
|
647
|
+
...behavior.actionName !== void 0 ? { actionName: behavior.actionName } : target.actionName !== void 0 ? { actionName: target.actionName } : {},
|
|
648
|
+
...target.nextAdditive !== void 0 && { nextAdditive: target.nextAdditive }
|
|
649
|
+
} : { ...behavior, type: "Behavior" };
|
|
586
650
|
}
|
|
587
651
|
findFallBehavior() {
|
|
588
652
|
return this.spec.behaviors.find((behavior) => behavior.name === "Fall" || behavior.name === "\u843D\u4E0B\u3059\u308B");
|
|
589
653
|
}
|
|
590
|
-
isOnAnyBoundary(environment) {
|
|
591
|
-
const anchor = environment.mascot.anchor;
|
|
592
|
-
const area = environment.mascot.environment.workArea;
|
|
593
|
-
const activeIE = environment.mascot.environment.activeIE;
|
|
594
|
-
return area.topBorder.isOn(anchor) || area.leftBorder.isOn(anchor) || area.rightBorder.isOn(anchor) || area.bottomBorder.isOn(anchor) || activeIE.visible && (activeIE.topBorder.isOn(anchor) || activeIE.leftBorder.isOn(anchor) || activeIE.rightBorder.isOn(anchor) || activeIE.bottomBorder.isOn(anchor));
|
|
595
|
-
}
|
|
596
654
|
};
|
|
597
655
|
|
|
598
656
|
// src/physics.ts
|
|
657
|
+
var BORDER_TOLERANCE = 0.999999;
|
|
599
658
|
function clamp(value, minimum, maximum) {
|
|
600
659
|
return Math.min(Math.max(value, minimum), maximum);
|
|
601
660
|
}
|
|
602
|
-
function isOnTop(point, rectangle, tolerance =
|
|
661
|
+
function isOnTop(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
603
662
|
return point.x >= rectangle.x - tolerance && point.x <= rectangle.x + rectangle.width + tolerance && Math.abs(point.y - rectangle.y) <= tolerance;
|
|
604
663
|
}
|
|
605
|
-
function isOnBottom(point, rectangle, tolerance =
|
|
664
|
+
function isOnBottom(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
606
665
|
return point.x >= rectangle.x - tolerance && point.x <= rectangle.x + rectangle.width + tolerance && Math.abs(point.y - rectangle.y - rectangle.height) <= tolerance;
|
|
607
666
|
}
|
|
608
|
-
function isOnLeft(point, rectangle, tolerance =
|
|
667
|
+
function isOnLeft(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
609
668
|
return point.y >= rectangle.y - tolerance && point.y <= rectangle.y + rectangle.height + tolerance && Math.abs(point.x - rectangle.x) <= tolerance;
|
|
610
669
|
}
|
|
611
|
-
function isOnRight(point, rectangle, tolerance =
|
|
670
|
+
function isOnRight(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
612
671
|
return point.y >= rectangle.y - tolerance && point.y <= rectangle.y + rectangle.height + tolerance && Math.abs(point.x - rectangle.x - rectangle.width) <= tolerance;
|
|
613
672
|
}
|
|
614
673
|
function isOnBorder(state, bounds, border, platform) {
|
|
@@ -617,29 +676,46 @@ function isOnBorder(state, bounds, border, platform) {
|
|
|
617
676
|
if (border === "Ceiling") return isOnTop(state, bounds) || platform !== void 0 && isOnBottom(state, platform);
|
|
618
677
|
return isOnLeft(state, bounds) || isOnRight(state, bounds) || platform !== void 0 && (isOnLeft(state, platform) || isOnRight(state, platform));
|
|
619
678
|
}
|
|
620
|
-
function
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
679
|
+
function isOnFloor(point, bounds, platforms = []) {
|
|
680
|
+
return platforms.some((platform) => isOnTop(point, platform)) || isOnBottom(point, bounds);
|
|
681
|
+
}
|
|
682
|
+
function isOnWall(point, bounds, lookRight, platforms = []) {
|
|
683
|
+
return lookRight ? platforms.some((platform) => isOnLeft(point, platform)) || isOnRight(point, bounds) : platforms.some((platform) => isOnRight(point, platform)) || isOnLeft(point, bounds);
|
|
684
|
+
}
|
|
685
|
+
function applyGravity(state, bounds, frameScale, gravity, resistanceX = 0.05, resistanceY = 0.1, platform) {
|
|
686
|
+
const platforms = platform ? [platform] : [];
|
|
687
|
+
const steps = Math.max(1, Math.round(frameScale));
|
|
688
|
+
let stopped = false;
|
|
689
|
+
for (let frame = 0; frame < steps && !stopped; frame += 1) {
|
|
690
|
+
state.vx -= state.vx * resistanceX;
|
|
691
|
+
state.vy = state.vy - state.vy * resistanceY + gravity;
|
|
692
|
+
const dx = Math.trunc(state.vx);
|
|
693
|
+
const dy = Math.trunc(state.vy);
|
|
694
|
+
const divisions = Math.max(1, Math.abs(dx), Math.abs(dy));
|
|
695
|
+
const start = { x: state.x, y: state.y };
|
|
696
|
+
for (let index = 0; index <= divisions; index += 1) {
|
|
697
|
+
const x = start.x + Math.trunc(dx * index / divisions);
|
|
698
|
+
const y = start.y + Math.trunc(dy * index / divisions);
|
|
699
|
+
state.x = x;
|
|
700
|
+
state.y = y;
|
|
701
|
+
if (dy > 0) {
|
|
702
|
+
for (let offset = -80; offset <= 0; offset += 1) {
|
|
703
|
+
state.y = y + offset;
|
|
704
|
+
if (isOnFloor(state, bounds, platforms)) {
|
|
705
|
+
stopped = true;
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (stopped) break;
|
|
710
|
+
state.y = y;
|
|
711
|
+
}
|
|
712
|
+
if (isOnWall(state, bounds, state.lookRight, platforms)) {
|
|
713
|
+
stopped = true;
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
641
717
|
}
|
|
642
|
-
return
|
|
718
|
+
return stopped;
|
|
643
719
|
}
|
|
644
720
|
function moveToward(state, target, speed, frameScale) {
|
|
645
721
|
const dx = target.x - state.x;
|
|
@@ -656,251 +732,832 @@ function moveToward(state, target, speed, frameScale) {
|
|
|
656
732
|
}
|
|
657
733
|
|
|
658
734
|
// src/action.ts
|
|
659
|
-
function
|
|
660
|
-
return value ===
|
|
735
|
+
function expressionIsPerFrame(value) {
|
|
736
|
+
return typeof value === "string" && value.trimStart().startsWith("#{");
|
|
661
737
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
738
|
+
var ActionValues = class {
|
|
739
|
+
constructor(random) {
|
|
740
|
+
this.random = random;
|
|
741
|
+
}
|
|
742
|
+
random;
|
|
743
|
+
actionCache = /* @__PURE__ */ new Map();
|
|
744
|
+
frameCache = /* @__PURE__ */ new Map();
|
|
745
|
+
init() {
|
|
746
|
+
this.actionCache.clear();
|
|
747
|
+
this.frameCache.clear();
|
|
748
|
+
}
|
|
749
|
+
initFrame() {
|
|
750
|
+
this.frameCache.clear();
|
|
751
|
+
}
|
|
752
|
+
number(key, value, environment, fallback) {
|
|
753
|
+
if (value === void 0) return fallback;
|
|
754
|
+
if (typeof value === "number") return value;
|
|
755
|
+
const cache = expressionIsPerFrame(value) ? this.frameCache : this.actionCache;
|
|
756
|
+
const cached = cache.get(key);
|
|
757
|
+
if (typeof cached === "number") return cached;
|
|
758
|
+
const result = evaluateExpression(value, environment, fallback, this.random);
|
|
759
|
+
cache.set(key, result);
|
|
760
|
+
return result;
|
|
761
|
+
}
|
|
762
|
+
boolean(key, value, environment, fallback) {
|
|
763
|
+
if (value === void 0) return fallback;
|
|
764
|
+
if (typeof value === "boolean") return value;
|
|
765
|
+
const cache = expressionIsPerFrame(value) ? this.frameCache : this.actionCache;
|
|
766
|
+
const cached = cache.get(key);
|
|
767
|
+
if (typeof cached === "boolean") return cached;
|
|
768
|
+
const result = evaluateExpression(value, environment, fallback, this.random);
|
|
769
|
+
cache.set(key, result);
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
var RuntimeBase = class {
|
|
774
|
+
constructor(definition, random) {
|
|
775
|
+
this.definition = definition;
|
|
776
|
+
this.values = new ActionValues(random);
|
|
777
|
+
}
|
|
778
|
+
definition;
|
|
779
|
+
time = 0;
|
|
780
|
+
values;
|
|
781
|
+
init(context) {
|
|
782
|
+
this.time = 0;
|
|
783
|
+
this.values.init();
|
|
784
|
+
this.onInit(context);
|
|
785
|
+
}
|
|
786
|
+
hasNext(context) {
|
|
787
|
+
return this.baseHasNext(context) && this.hasMore(context);
|
|
788
|
+
}
|
|
789
|
+
step(context) {
|
|
790
|
+
this.values.initFrame();
|
|
791
|
+
const result = this.tick(context);
|
|
792
|
+
this.time += 1;
|
|
793
|
+
return result;
|
|
794
|
+
}
|
|
795
|
+
onInit(_context) {
|
|
796
|
+
}
|
|
797
|
+
hasMore(_context) {
|
|
798
|
+
return true;
|
|
799
|
+
}
|
|
800
|
+
baseHasNext(context) {
|
|
801
|
+
const condition = this.values.boolean("condition", this.definition.condition, context.environment, true);
|
|
802
|
+
const duration = Math.trunc(this.values.number("duration", this.definition.duration, context.environment, Number.POSITIVE_INFINITY));
|
|
803
|
+
return condition && this.time < duration;
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
var TrackedBorder = class {
|
|
807
|
+
constructor(side, source, context) {
|
|
808
|
+
this.side = side;
|
|
809
|
+
this.source = source;
|
|
810
|
+
this.previous = this.rectangle(context);
|
|
811
|
+
}
|
|
812
|
+
side;
|
|
813
|
+
source;
|
|
814
|
+
previous;
|
|
815
|
+
move(point, context) {
|
|
816
|
+
const current = this.rectangle(context);
|
|
817
|
+
const previous = this.previous;
|
|
818
|
+
this.previous = current;
|
|
819
|
+
if (!current || !previous) return point;
|
|
820
|
+
if (this.side === "left" || this.side === "right") {
|
|
821
|
+
if (previous.height === 0) return point;
|
|
822
|
+
const next2 = {
|
|
823
|
+
x: point.x + this.coordinate(current) - this.coordinate(previous),
|
|
824
|
+
y: Math.trunc((point.y - previous.y) * current.height / previous.height + current.y)
|
|
825
|
+
};
|
|
826
|
+
return Math.abs(next2.x - point.x) >= 80 || Math.abs(next2.y - point.y) >= 80 ? point : next2;
|
|
827
|
+
}
|
|
828
|
+
if (previous.width === 0) return point;
|
|
829
|
+
const next = {
|
|
830
|
+
// FloorCeiling.java performs integer division before applying the
|
|
831
|
+
// mascot's relative offset along a resized border.
|
|
832
|
+
x: (point.x - previous.x) * Math.trunc(current.width / previous.width) + current.x,
|
|
833
|
+
y: point.y + this.coordinate(current) - this.coordinate(previous)
|
|
834
|
+
};
|
|
835
|
+
return Math.abs(next.x - point.x) >= 80 || next.y - point.y > 20 || next.y - point.y < -80 ? point : next;
|
|
836
|
+
}
|
|
837
|
+
isOn(point, context) {
|
|
838
|
+
const rectangle = this.rectangle(context);
|
|
839
|
+
if (!rectangle) return false;
|
|
840
|
+
switch (this.side) {
|
|
841
|
+
case "top":
|
|
842
|
+
return isOnTop(point, rectangle);
|
|
843
|
+
case "bottom":
|
|
844
|
+
return isOnBottom(point, rectangle);
|
|
845
|
+
case "left":
|
|
846
|
+
return isOnLeft(point, rectangle);
|
|
847
|
+
case "right":
|
|
848
|
+
return isOnRight(point, rectangle);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
coordinate(rectangle) {
|
|
852
|
+
switch (this.side) {
|
|
853
|
+
case "top":
|
|
854
|
+
return rectangle.y;
|
|
855
|
+
case "bottom":
|
|
856
|
+
return rectangle.y + rectangle.height;
|
|
857
|
+
case "left":
|
|
858
|
+
return rectangle.x;
|
|
859
|
+
case "right":
|
|
860
|
+
return rectangle.x + rectangle.width;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
rectangle(context) {
|
|
864
|
+
if (this.source === "work-area") return context.bounds;
|
|
865
|
+
if (!this.source) return void 0;
|
|
866
|
+
return context.platforms.find((platform) => platform.element === this.source);
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
function selectBorder(type, state, context) {
|
|
870
|
+
if (type === "Floor") {
|
|
871
|
+
const platform2 = context.platforms.find((candidate) => isOnTop(state, candidate));
|
|
872
|
+
if (platform2) return new TrackedBorder("top", platform2.element, context);
|
|
873
|
+
if (isOnBottom(state, context.bounds)) return new TrackedBorder("bottom", "work-area", context);
|
|
874
|
+
return new TrackedBorder("bottom", void 0, context);
|
|
875
|
+
}
|
|
876
|
+
if (type === "Ceiling") {
|
|
877
|
+
const platform2 = context.platforms.find((candidate) => isOnBottom(state, candidate));
|
|
878
|
+
if (platform2) return new TrackedBorder("bottom", platform2.element, context);
|
|
879
|
+
if (isOnTop(state, context.bounds)) return new TrackedBorder("top", "work-area", context);
|
|
880
|
+
return new TrackedBorder("top", void 0, context);
|
|
881
|
+
}
|
|
882
|
+
if (state.lookRight) {
|
|
883
|
+
const platform2 = context.platforms.find((candidate) => isOnLeft(state, candidate));
|
|
884
|
+
if (platform2) return new TrackedBorder("left", platform2.element, context);
|
|
885
|
+
if (isOnRight(state, context.bounds)) return new TrackedBorder("right", "work-area", context);
|
|
886
|
+
return new TrackedBorder("right", void 0, context);
|
|
887
|
+
}
|
|
888
|
+
const platform = context.platforms.find((candidate) => isOnRight(state, candidate));
|
|
889
|
+
if (platform) return new TrackedBorder("right", platform.element, context);
|
|
890
|
+
if (isOnLeft(state, context.bounds)) return new TrackedBorder("left", "work-area", context);
|
|
891
|
+
return new TrackedBorder("left", void 0, context);
|
|
706
892
|
}
|
|
707
|
-
var
|
|
708
|
-
constructor(
|
|
709
|
-
|
|
710
|
-
this.
|
|
711
|
-
this.loop = loop;
|
|
893
|
+
var AnimatedRuntime = class extends RuntimeBase {
|
|
894
|
+
constructor(definition, state, random) {
|
|
895
|
+
super(definition, random);
|
|
896
|
+
this.state = state;
|
|
712
897
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
898
|
+
state;
|
|
899
|
+
border;
|
|
900
|
+
onInit(context) {
|
|
901
|
+
this.border = this.definition.borderType ? selectBorder(this.definition.borderType, this.state, context) : void 0;
|
|
902
|
+
}
|
|
903
|
+
animation(context, turn) {
|
|
904
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
905
|
+
return this.definition.animations?.find((animation, index) => (turn === void 0 || Boolean(animation.turn) === turn) && this.values.boolean(`animation-${index}`, animation.condition, scoped, true));
|
|
906
|
+
}
|
|
907
|
+
animationDuration(context, turn) {
|
|
908
|
+
return this.animation(context, turn)?.poses.reduce((sum, pose) => sum + Math.max(0, pose.duration), 0) ?? 0;
|
|
909
|
+
}
|
|
910
|
+
applyBorder(context) {
|
|
911
|
+
if (!this.border) return "running";
|
|
912
|
+
const moved = this.border.move(this.state, context);
|
|
913
|
+
this.state.x = moved.x;
|
|
914
|
+
this.state.y = moved.y;
|
|
915
|
+
return this.border.isOn(this.state, context) ? "running" : "lost-ground";
|
|
916
|
+
}
|
|
917
|
+
applyAnimation(context, turn) {
|
|
918
|
+
const animation = this.animation(context, turn);
|
|
919
|
+
const pose = animation && poseAt(animation, this.time);
|
|
920
|
+
if (pose) applyPose(this.state, pose);
|
|
921
|
+
}
|
|
922
|
+
scopedEnvironment(environment) {
|
|
923
|
+
const gap = this.values.number("gap", this.definition.gap, environment, 0);
|
|
924
|
+
const withGap = { ...environment, gap };
|
|
925
|
+
const targetX = this.definition.targetX === void 0 ? void 0 : this.values.number("targetX", this.definition.targetX, withGap, 0);
|
|
926
|
+
const targetY = this.definition.targetY === void 0 ? void 0 : this.values.number("targetY", this.definition.targetY, withGap, 0);
|
|
927
|
+
return { ...withGap, ...targetX !== void 0 && { targetX }, ...targetY !== void 0 && { targetY } };
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
function poseAt(animation, time) {
|
|
931
|
+
const duration = animation.poses.reduce((sum, pose) => sum + Math.max(0, pose.duration), 0);
|
|
932
|
+
if (duration <= 0) return void 0;
|
|
933
|
+
let cursor = time % duration;
|
|
934
|
+
for (const pose of animation.poses) {
|
|
935
|
+
cursor -= Math.max(0, pose.duration);
|
|
936
|
+
if (cursor < 0) return pose;
|
|
937
|
+
}
|
|
938
|
+
return animation.poses.at(-1);
|
|
939
|
+
}
|
|
940
|
+
function applyPose(state, pose) {
|
|
941
|
+
state.sprite = pose.sprite;
|
|
942
|
+
state.anchorX = pose.anchor.x;
|
|
943
|
+
state.anchorY = pose.anchor.y;
|
|
944
|
+
state.x += (state.lookRight ? -1 : 1) * pose.velocity.x;
|
|
945
|
+
state.y += pose.velocity.y;
|
|
946
|
+
}
|
|
947
|
+
var StayRuntime = class extends AnimatedRuntime {
|
|
948
|
+
tick(context) {
|
|
949
|
+
const border = this.applyBorder(context);
|
|
950
|
+
if (border === "lost-ground") return border;
|
|
951
|
+
this.applyAnimation(context);
|
|
952
|
+
return "running";
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
var AnimateRuntime = class extends StayRuntime {
|
|
956
|
+
hasMore(context) {
|
|
957
|
+
return this.time < this.animationDuration(context);
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
var MoveRuntime = class extends AnimatedRuntime {
|
|
961
|
+
turning = false;
|
|
962
|
+
hasTurningAnimation = false;
|
|
963
|
+
onInit(context) {
|
|
964
|
+
super.onInit(context);
|
|
965
|
+
this.turning = false;
|
|
966
|
+
this.hasTurningAnimation = this.definition.animations?.some((animation) => animation.turn) ?? false;
|
|
967
|
+
}
|
|
968
|
+
hasMore(context) {
|
|
969
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
970
|
+
const targetX = this.targetX(scoped);
|
|
971
|
+
const targetY = this.targetY(scoped);
|
|
972
|
+
const reached = targetX !== void 0 && this.state.x === targetX || targetY !== void 0 && this.state.y === targetY;
|
|
973
|
+
return !reached || this.turning;
|
|
974
|
+
}
|
|
975
|
+
tick(context) {
|
|
976
|
+
const border = this.applyBorder(context);
|
|
977
|
+
if (border === "lost-ground") return border;
|
|
978
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
979
|
+
const targetX = this.targetX(scoped);
|
|
980
|
+
const targetY = this.targetY(scoped);
|
|
981
|
+
let down = false;
|
|
982
|
+
if (targetX !== void 0 && this.state.x !== targetX) {
|
|
983
|
+
const nextLookRight = this.state.x < targetX;
|
|
984
|
+
this.turning = this.hasTurningAnimation && (this.turning || nextLookRight !== this.state.lookRight);
|
|
985
|
+
this.state.lookRight = nextLookRight;
|
|
731
986
|
}
|
|
732
|
-
|
|
987
|
+
if (targetY !== void 0) down = this.state.y < targetY;
|
|
988
|
+
if (this.turning && this.time >= this.animationDuration(context, true)) this.turning = false;
|
|
989
|
+
this.applyAnimation(context, this.turning);
|
|
990
|
+
if (targetX !== void 0 && (this.state.lookRight && this.state.x >= targetX || !this.state.lookRight && this.state.x <= targetX)) this.state.x = targetX;
|
|
991
|
+
if (targetY !== void 0 && (down && this.state.y >= targetY || !down && this.state.y <= targetY)) this.state.y = targetY;
|
|
992
|
+
return "running";
|
|
993
|
+
}
|
|
994
|
+
targetX(environment) {
|
|
995
|
+
return this.definition.targetX === void 0 ? void 0 : Math.trunc(this.values.number("targetX", this.definition.targetX, environment, 0));
|
|
996
|
+
}
|
|
997
|
+
targetY(environment) {
|
|
998
|
+
return this.definition.targetY === void 0 ? void 0 : Math.trunc(this.values.number("targetY", this.definition.targetY, environment, 0));
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
var MoveWithTurnRuntime = class extends MoveRuntime {
|
|
1002
|
+
onInit(context) {
|
|
1003
|
+
super.onInit(context);
|
|
1004
|
+
this.hasTurningAnimation = (this.definition.animations?.length ?? 0) >= 2;
|
|
1005
|
+
}
|
|
1006
|
+
animation(context, turn) {
|
|
1007
|
+
const animations = this.definition.animations ?? [];
|
|
1008
|
+
if (turn) return animations.at(-1);
|
|
1009
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
1010
|
+
return animations.slice(0, -1).find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, scoped, true));
|
|
733
1011
|
}
|
|
734
1012
|
};
|
|
735
|
-
var
|
|
1013
|
+
var TurnRuntime = class extends AnimatedRuntime {
|
|
1014
|
+
turning = false;
|
|
1015
|
+
hasMore(context) {
|
|
1016
|
+
const desired = this.values.boolean("lookRight", this.definition.lookRight, context.environment, !this.state.lookRight);
|
|
1017
|
+
this.turning ||= desired !== this.state.lookRight;
|
|
1018
|
+
return this.turning && this.time < this.animationDuration(context);
|
|
1019
|
+
}
|
|
1020
|
+
tick(context) {
|
|
1021
|
+
this.state.lookRight = this.values.boolean("lookRight", this.definition.lookRight, context.environment, !this.state.lookRight);
|
|
1022
|
+
const border = this.applyBorder(context);
|
|
1023
|
+
if (border === "lost-ground") return border;
|
|
1024
|
+
this.applyAnimation(context);
|
|
1025
|
+
return "running";
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
var InstantRuntime = class extends RuntimeBase {
|
|
1029
|
+
constructor(definition, state, random, operation) {
|
|
1030
|
+
super(definition, random);
|
|
1031
|
+
this.state = state;
|
|
1032
|
+
this.operation = operation;
|
|
1033
|
+
}
|
|
1034
|
+
state;
|
|
1035
|
+
operation;
|
|
1036
|
+
onInit(context) {
|
|
1037
|
+
if (!this.baseHasNext(context)) return;
|
|
1038
|
+
if (this.operation === "look") {
|
|
1039
|
+
this.state.lookRight = this.values.boolean("lookRight", this.definition.lookRight, context.environment, !this.state.lookRight);
|
|
1040
|
+
} else if (this.operation === "offset") {
|
|
1041
|
+
this.state.x += Math.trunc(this.values.number("x", this.definition.x, context.environment, 0));
|
|
1042
|
+
this.state.y += Math.trunc(this.values.number("y", this.definition.y, context.environment, 0));
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
hasMore() {
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
736
1048
|
tick() {
|
|
737
|
-
return
|
|
1049
|
+
return "running";
|
|
738
1050
|
}
|
|
739
1051
|
};
|
|
740
|
-
var
|
|
741
|
-
constructor(
|
|
742
|
-
|
|
1052
|
+
var JumpRuntime = class extends RuntimeBase {
|
|
1053
|
+
constructor(definition, state, random) {
|
|
1054
|
+
super(definition, random);
|
|
743
1055
|
this.state = state;
|
|
744
|
-
this.options = options;
|
|
745
|
-
this.callbacks = callbacks;
|
|
746
|
-
const animationEnvironment = { ...environment, ...action.targetX !== void 0 && { targetX: action.targetX }, ...action.targetY !== void 0 && { targetY: action.targetY } };
|
|
747
|
-
this.animation = action.definition.animations?.find((animation) => !animation.condition || evaluateExpression(animation.condition, animationEnvironment, false)) ?? action.definition.animations?.find((animation) => !animation.condition) ?? action.definition.animations?.[0];
|
|
748
|
-
this.poseDuration = this.animation?.poses.reduce((sum, pose) => sum + Math.max(0, pose.duration), 0) ?? 0;
|
|
749
1056
|
}
|
|
750
|
-
action;
|
|
751
1057
|
state;
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
this.
|
|
763
|
-
this.state.
|
|
764
|
-
|
|
1058
|
+
hasMore(context) {
|
|
1059
|
+
return this.distance(context).distance !== 0;
|
|
1060
|
+
}
|
|
1061
|
+
tick(context) {
|
|
1062
|
+
const { targetX, targetY, distanceX, distanceY, distance } = this.distance(context);
|
|
1063
|
+
this.state.lookRight = this.state.x < targetX;
|
|
1064
|
+
const velocity = this.values.number("velocity", this.definition.velocity, context.environment, 20);
|
|
1065
|
+
if (distance !== 0) {
|
|
1066
|
+
this.state.vx = velocity * distanceX / distance;
|
|
1067
|
+
this.state.vy = velocity * distanceY / distance;
|
|
1068
|
+
this.state.x += Math.trunc(this.state.vx);
|
|
1069
|
+
this.state.y += Math.trunc(this.state.vy);
|
|
1070
|
+
const environment = { ...context.environment, targetX, targetY };
|
|
1071
|
+
const animation = this.definition.animations?.find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, environment, true));
|
|
1072
|
+
const pose = animation && poseAt(animation, this.time);
|
|
1073
|
+
if (pose) applyPose(this.state, pose);
|
|
765
1074
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
const { definition } = this.action;
|
|
770
|
-
if (definition.type === "Embedded") return this.tickEmbedded(frameScale, environment, bounds);
|
|
771
|
-
if (definition.type === "Move" && (this.action.targetX !== void 0 || this.action.targetY !== void 0)) {
|
|
772
|
-
const reachedX = this.action.targetX === void 0 || Math.abs(this.state.x - this.action.targetX) < 0.5;
|
|
773
|
-
const reachedY = this.action.targetY === void 0 || Math.abs(this.state.y - this.action.targetY) < 0.5;
|
|
774
|
-
return reachedX && reachedY;
|
|
1075
|
+
if (distance <= velocity) {
|
|
1076
|
+
this.state.x = targetX;
|
|
1077
|
+
this.state.y = targetY;
|
|
775
1078
|
}
|
|
776
|
-
return
|
|
1079
|
+
return "running";
|
|
777
1080
|
}
|
|
778
|
-
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
1081
|
+
distance(context) {
|
|
1082
|
+
const targetX = Math.trunc(this.values.number("targetX", this.definition.targetX, context.environment, 0));
|
|
1083
|
+
const targetY = Math.trunc(this.values.number("targetY", this.definition.targetY, context.environment, 0));
|
|
1084
|
+
const distanceX = targetX - this.state.x;
|
|
1085
|
+
const distanceY = targetY - this.state.y - Math.abs(distanceX) / 2;
|
|
1086
|
+
return { targetX, targetY, distanceX, distanceY, distance: Math.hypot(distanceX, distanceY) };
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
var FallRuntime = class extends RuntimeBase {
|
|
1090
|
+
constructor(definition, state, random, defaultGravity) {
|
|
1091
|
+
super(definition, random);
|
|
1092
|
+
this.state = state;
|
|
1093
|
+
this.defaultGravity = defaultGravity;
|
|
1094
|
+
}
|
|
1095
|
+
state;
|
|
1096
|
+
defaultGravity;
|
|
1097
|
+
modX = 0;
|
|
1098
|
+
modY = 0;
|
|
1099
|
+
onInit(context) {
|
|
1100
|
+
this.modX = 0;
|
|
1101
|
+
this.modY = 0;
|
|
1102
|
+
this.state.vx = Math.trunc(this.values.number("initialVx", this.definition.initialVx, context.environment, 0));
|
|
1103
|
+
this.state.vy = Math.trunc(this.values.number("initialVy", this.definition.initialVy, context.environment, 0));
|
|
1104
|
+
}
|
|
1105
|
+
hasMore(context) {
|
|
1106
|
+
return !isOnFloor(this.state, context.bounds, context.platforms) && !isOnWall(this.state, context.bounds, this.state.lookRight, context.platforms);
|
|
1107
|
+
}
|
|
1108
|
+
tick(context) {
|
|
1109
|
+
if (this.state.vx !== 0) this.state.lookRight = this.state.vx > 0;
|
|
1110
|
+
const resistanceX = this.values.number("resistanceX", this.definition.resistanceX, context.environment, 0.05);
|
|
1111
|
+
const resistanceY = this.values.number("resistanceY", this.definition.resistanceY, context.environment, 0.1);
|
|
1112
|
+
const gravity = this.values.number("gravity", this.definition.gravity, context.environment, this.defaultGravity);
|
|
1113
|
+
this.state.vx -= this.state.vx * resistanceX;
|
|
1114
|
+
this.state.vy = this.state.vy - this.state.vy * resistanceY + gravity;
|
|
1115
|
+
this.modX += this.state.vx % 1;
|
|
1116
|
+
this.modY += this.state.vy % 1;
|
|
1117
|
+
const dx = Math.trunc(this.state.vx) + Math.trunc(this.modX);
|
|
1118
|
+
const dy = Math.trunc(this.state.vy) + Math.trunc(this.modY);
|
|
1119
|
+
this.modX %= 1;
|
|
1120
|
+
this.modY %= 1;
|
|
1121
|
+
const divisions = Math.max(1, Math.abs(dx), Math.abs(dy));
|
|
1122
|
+
const start = { x: this.state.x, y: this.state.y };
|
|
1123
|
+
let stopped = false;
|
|
1124
|
+
for (let index = 0; index <= divisions; index += 1) {
|
|
1125
|
+
const x = start.x + Math.trunc(dx * index / divisions);
|
|
1126
|
+
const y = start.y + Math.trunc(dy * index / divisions);
|
|
1127
|
+
this.state.x = x;
|
|
1128
|
+
this.state.y = y;
|
|
1129
|
+
if (dy > 0) {
|
|
1130
|
+
for (let offset = -80; offset <= 0; offset += 1) {
|
|
1131
|
+
this.state.y = y + offset;
|
|
1132
|
+
if (isOnFloor(this.state, context.bounds, context.platforms)) {
|
|
1133
|
+
stopped = true;
|
|
1134
|
+
break;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
if (stopped) break;
|
|
1138
|
+
this.state.y = y;
|
|
1139
|
+
}
|
|
1140
|
+
if (isOnWall(this.state, context.bounds, this.state.lookRight, context.platforms)) break;
|
|
787
1141
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
1142
|
+
const fallEnvironment = { ...context.environment, velocityX: this.state.vx, velocityY: this.state.vy };
|
|
1143
|
+
const animation = this.definition.animations?.find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, fallEnvironment, true));
|
|
1144
|
+
const pose = animation && poseAt(animation, this.time);
|
|
1145
|
+
if (pose) applyPose(this.state, pose);
|
|
1146
|
+
return "running";
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
function matchingActivePlatform(context) {
|
|
1150
|
+
const active = context.environment.mascot.environment.activeIE;
|
|
1151
|
+
if (!active.visible) return void 0;
|
|
1152
|
+
return context.platforms.find((platform) => Math.abs(platform.x - active.x) < 1e-3 && Math.abs(platform.y - active.y) < 1e-3 && Math.abs(platform.width - active.width) < 1e-3 && Math.abs(platform.height - active.height) < 1e-3);
|
|
1153
|
+
}
|
|
1154
|
+
function carryRelation(state, platform, offsetX, offsetY) {
|
|
1155
|
+
const grip = {
|
|
1156
|
+
x: state.x + (state.lookRight ? -offsetX : offsetX),
|
|
1157
|
+
y: state.y + offsetY
|
|
1158
|
+
};
|
|
1159
|
+
return isOnBottom(grip, platform) && (state.lookRight ? isOnLeft(grip, platform) : isOnRight(grip, platform));
|
|
1160
|
+
}
|
|
1161
|
+
function carriedPlatformPosition(state, platform, offsetX, offsetY) {
|
|
1162
|
+
return state.lookRight ? { x: state.x - offsetX, y: state.y + offsetY - platform.height } : { x: state.x + offsetX - platform.width, y: state.y + offsetY - platform.height };
|
|
1163
|
+
}
|
|
1164
|
+
var CarryFallRuntime = class extends FallRuntime {
|
|
1165
|
+
constructor(definition, state, random, gravity, callbacks) {
|
|
1166
|
+
super(definition, state, random, gravity);
|
|
1167
|
+
this.callbacks = callbacks;
|
|
1168
|
+
}
|
|
1169
|
+
callbacks;
|
|
1170
|
+
element;
|
|
1171
|
+
onInit(context) {
|
|
1172
|
+
super.onInit(context);
|
|
1173
|
+
this.element = matchingActivePlatform(context)?.element;
|
|
1174
|
+
}
|
|
1175
|
+
tick(context) {
|
|
1176
|
+
const platform = context.platforms.find((candidate) => candidate.element === this.element);
|
|
1177
|
+
const offsetX = Math.trunc(this.values.number("ieOffsetX", this.definition.ieOffsetX, context.environment, 0));
|
|
1178
|
+
const offsetY = Math.trunc(this.values.number("ieOffsetY", this.definition.ieOffsetY, context.environment, 0));
|
|
1179
|
+
if (!platform || !carryRelation(this.state, platform, offsetX, offsetY)) return "lost-ground";
|
|
1180
|
+
const result = super.tick(context);
|
|
1181
|
+
this.callbacks.movePlatform?.(platform.element, carriedPlatformPosition(this.state, platform, offsetX, offsetY));
|
|
1182
|
+
return result;
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
var CarryMoveRuntime = class extends MoveRuntime {
|
|
1186
|
+
constructor(definition, state, random, callbacks) {
|
|
1187
|
+
super(definition, state, random);
|
|
1188
|
+
this.callbacks = callbacks;
|
|
1189
|
+
}
|
|
1190
|
+
callbacks;
|
|
1191
|
+
element;
|
|
1192
|
+
onInit(context) {
|
|
1193
|
+
super.onInit(context);
|
|
1194
|
+
this.element = matchingActivePlatform(context)?.element;
|
|
1195
|
+
}
|
|
1196
|
+
tick(context) {
|
|
1197
|
+
const platform = context.platforms.find((candidate) => candidate.element === this.element);
|
|
1198
|
+
const offsetX = Math.trunc(this.values.number("ieOffsetX", this.definition.ieOffsetX, context.environment, 0));
|
|
1199
|
+
const offsetY = Math.trunc(this.values.number("ieOffsetY", this.definition.ieOffsetY, context.environment, 0));
|
|
1200
|
+
if (!platform || !carryRelation(this.state, platform, offsetX, offsetY)) return "lost-ground";
|
|
1201
|
+
const result = super.tick(context);
|
|
1202
|
+
this.callbacks.movePlatform?.(platform.element, carriedPlatformPosition(this.state, platform, offsetX, offsetY));
|
|
1203
|
+
return result;
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
var ThrowPlatformRuntime = class extends AnimateRuntime {
|
|
1207
|
+
constructor(definition, state, random, callbacks) {
|
|
1208
|
+
super(definition, state, random);
|
|
1209
|
+
this.callbacks = callbacks;
|
|
1210
|
+
}
|
|
1211
|
+
callbacks;
|
|
1212
|
+
element;
|
|
1213
|
+
onInit(context) {
|
|
1214
|
+
super.onInit(context);
|
|
1215
|
+
this.element = matchingActivePlatform(context)?.element;
|
|
1216
|
+
}
|
|
1217
|
+
tick(context) {
|
|
1218
|
+
const result = super.tick(context);
|
|
1219
|
+
const platform = context.platforms.find((candidate) => candidate.element === this.element);
|
|
1220
|
+
if (platform) {
|
|
1221
|
+
const vx = Math.trunc(this.values.number("initialVx", this.definition.initialVx, context.environment, 32));
|
|
1222
|
+
const vy = Math.trunc(this.values.number("initialVy", this.definition.initialVy, context.environment, -10));
|
|
1223
|
+
const gravity = this.values.number("gravity", this.definition.gravity, context.environment, 0.5);
|
|
1224
|
+
this.callbacks.movePlatform?.(platform.element, {
|
|
1225
|
+
x: platform.x + (this.state.lookRight ? vx : -vx),
|
|
1226
|
+
y: platform.y + vy + Math.trunc(this.time * gravity)
|
|
1227
|
+
});
|
|
791
1228
|
}
|
|
1229
|
+
return result;
|
|
792
1230
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
case "Reboot":
|
|
823
|
-
this.state.x = bounds.x + 128 + Math.max(0, bounds.width - 256) * Math.random();
|
|
824
|
-
this.state.y = bounds.y + 128 + Math.max(0, bounds.height - 256) * Math.random();
|
|
825
|
-
return true;
|
|
826
|
-
case "Offset":
|
|
827
|
-
case "Look":
|
|
828
|
-
return true;
|
|
829
|
-
case "Dragged":
|
|
830
|
-
return false;
|
|
831
|
-
default:
|
|
832
|
-
return this.elapsedMs >= this.durationMs();
|
|
1231
|
+
};
|
|
1232
|
+
function breed(definition, state, values, context, callbacks) {
|
|
1233
|
+
const bornX = Math.trunc(values.number("bornX", definition.bornX, context.environment, 0));
|
|
1234
|
+
const bornY = Math.trunc(values.number("bornY", definition.bornY, context.environment, 0));
|
|
1235
|
+
const count = Math.trunc(values.number("bornCount", definition.bornCount, context.environment, 1));
|
|
1236
|
+
if (count < 1) throw new RangeError("BornCount must be positive");
|
|
1237
|
+
for (let index = 0; index < count; index += 1) {
|
|
1238
|
+
callbacks.spawn({
|
|
1239
|
+
x: state.x + (state.lookRight ? -bornX : bornX),
|
|
1240
|
+
y: state.y + bornY,
|
|
1241
|
+
lookRight: state.lookRight,
|
|
1242
|
+
...definition.bornBehavior && { behaviorName: definition.bornBehavior }
|
|
1243
|
+
}, definition.bornMascot);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
var BreedRuntime = class extends AnimateRuntime {
|
|
1247
|
+
constructor(definition, state, random, callbacks) {
|
|
1248
|
+
super(definition, state, random);
|
|
1249
|
+
this.callbacks = callbacks;
|
|
1250
|
+
}
|
|
1251
|
+
callbacks;
|
|
1252
|
+
spawned = false;
|
|
1253
|
+
tick(context) {
|
|
1254
|
+
const result = super.tick(context);
|
|
1255
|
+
if (result === "lost-ground") return result;
|
|
1256
|
+
const duration = this.animationDuration(context);
|
|
1257
|
+
if (!this.spawned && this.time === duration - 1) {
|
|
1258
|
+
this.spawned = true;
|
|
1259
|
+
breed(this.definition, this.state, this.values, context, this.callbacks);
|
|
833
1260
|
}
|
|
1261
|
+
return result;
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
var BreedMoveRuntime = class extends MoveRuntime {
|
|
1265
|
+
constructor(definition, state, random, callbacks) {
|
|
1266
|
+
super(definition, state, random);
|
|
1267
|
+
this.callbacks = callbacks;
|
|
1268
|
+
}
|
|
1269
|
+
callbacks;
|
|
1270
|
+
tick(context) {
|
|
1271
|
+
const result = super.tick(context);
|
|
1272
|
+
if (result === "lost-ground") return result;
|
|
1273
|
+
const interval = Math.trunc(this.values.number("bornInterval", this.definition.bornInterval, context.environment, 1));
|
|
1274
|
+
if (interval < 1) throw new RangeError("BornInterval must be positive");
|
|
1275
|
+
if (this.time % interval === 0 && !this.turning) breed(this.definition, this.state, this.values, context, this.callbacks);
|
|
1276
|
+
return result;
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
var BreedJumpRuntime = class extends JumpRuntime {
|
|
1280
|
+
constructor(definition, breedState, random, callbacks) {
|
|
1281
|
+
super(definition, breedState, random);
|
|
1282
|
+
this.breedState = breedState;
|
|
1283
|
+
this.callbacks = callbacks;
|
|
834
1284
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1285
|
+
breedState;
|
|
1286
|
+
callbacks;
|
|
1287
|
+
tick(context) {
|
|
1288
|
+
const result = super.tick(context);
|
|
1289
|
+
const interval = Math.trunc(this.values.number("bornInterval", this.definition.bornInterval, context.environment, 1));
|
|
1290
|
+
if (interval < 1) throw new RangeError("BornInterval must be positive");
|
|
1291
|
+
if (this.time % interval === 0) breed(this.definition, this.breedState, this.values, context, this.callbacks);
|
|
1292
|
+
return result;
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
var DraggedRuntime = class extends RuntimeBase {
|
|
1296
|
+
constructor(definition, state, random, spec) {
|
|
1297
|
+
super(definition, random);
|
|
1298
|
+
this.state = state;
|
|
1299
|
+
this.spec = spec;
|
|
1300
|
+
}
|
|
1301
|
+
state;
|
|
1302
|
+
spec;
|
|
1303
|
+
footX = 0;
|
|
1304
|
+
footDx = 0;
|
|
1305
|
+
timeToRegist = 250;
|
|
1306
|
+
onInit(context) {
|
|
1307
|
+
this.footDx = 0;
|
|
1308
|
+
this.timeToRegist = 250;
|
|
1309
|
+
this.footX = context.environment.mascot.environment.cursor.x + this.offsetX(context);
|
|
1310
|
+
}
|
|
1311
|
+
hasMore() {
|
|
1312
|
+
return this.time < this.timeToRegist;
|
|
1313
|
+
}
|
|
1314
|
+
tick(context) {
|
|
1315
|
+
this.state.lookRight = false;
|
|
1316
|
+
this.state.dragging = true;
|
|
1317
|
+
const cursor = context.environment.mascot.environment.cursor;
|
|
1318
|
+
const offsetX = this.offsetX(context);
|
|
1319
|
+
const offsetY = this.offsetY(context);
|
|
1320
|
+
if (Math.abs(cursor.x - this.state.x + offsetX) >= 5) this.time = 0;
|
|
1321
|
+
this.footDx = (this.footDx + (cursor.x - this.footX) * 0.1) * 0.8;
|
|
1322
|
+
this.footX += this.footDx;
|
|
1323
|
+
const environment = { ...context.environment, footX: this.footX };
|
|
1324
|
+
const animation = this.definition.animations?.find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, environment, true));
|
|
1325
|
+
const pose = animation && poseAt(animation, this.time);
|
|
1326
|
+
if (pose) applyPose(this.state, pose);
|
|
1327
|
+
this.state.x = cursor.x + offsetX;
|
|
1328
|
+
this.state.y = cursor.y + offsetY;
|
|
1329
|
+
if (this.time === this.timeToRegist - 1 && this.values.number(`regist-${this.time}`, "#{Math.random()}", environment, 0) >= 0.1) this.timeToRegist += 1;
|
|
1330
|
+
return "running";
|
|
1331
|
+
}
|
|
1332
|
+
offsetX(context) {
|
|
1333
|
+
const offset = Math.trunc(this.values.number("offsetX", this.definition.offsetX, context.environment, 0));
|
|
1334
|
+
return this.definition.offsetType === "Origin" ? -offset + this.spriteCenter().x : offset;
|
|
1335
|
+
}
|
|
1336
|
+
offsetY(context) {
|
|
1337
|
+
const offset = Math.trunc(this.values.number("offsetY", this.definition.offsetY, context.environment, 120));
|
|
1338
|
+
return this.definition.offsetType === "Origin" ? -offset + this.spriteCenter().y : offset;
|
|
1339
|
+
}
|
|
1340
|
+
spriteCenter() {
|
|
1341
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1342
|
+
const width = typeof sprite === "object" ? sprite.width ?? 128 : 128;
|
|
1343
|
+
return { x: this.state.lookRight ? width - this.state.anchorX : this.state.anchorX, y: this.state.anchorY };
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
var RegistRuntime = class extends AnimatedRuntime {
|
|
1347
|
+
constructor(definition, state, random, spec) {
|
|
1348
|
+
super(definition, state, random);
|
|
1349
|
+
this.spec = spec;
|
|
1350
|
+
}
|
|
1351
|
+
spec;
|
|
1352
|
+
hasMore(context) {
|
|
1353
|
+
const cursor = context.environment.mascot.environment.cursor;
|
|
1354
|
+
const rawOffset = Math.trunc(this.values.number("offsetX", this.definition.offsetX, context.environment, 0));
|
|
1355
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1356
|
+
const width = typeof sprite === "object" ? sprite.width ?? 128 : 128;
|
|
1357
|
+
const centerX = this.state.lookRight ? width - this.state.anchorX : this.state.anchorX;
|
|
1358
|
+
const offsetX = this.definition.offsetType === "Origin" ? -rawOffset + centerX : rawOffset;
|
|
1359
|
+
return Math.abs(cursor.x - this.state.x + offsetX) < 5;
|
|
1360
|
+
}
|
|
1361
|
+
tick(context) {
|
|
1362
|
+
this.state.dragging = true;
|
|
1363
|
+
this.applyAnimation(context);
|
|
1364
|
+
if (this.time + 1 >= this.animationDuration(context)) {
|
|
1365
|
+
this.state.lookRight = this.values.number(`look-${this.time}`, "#{Math.random()}", context.environment, 0) < 0.5;
|
|
1366
|
+
return "lost-ground";
|
|
841
1367
|
}
|
|
842
|
-
return
|
|
1368
|
+
return "running";
|
|
843
1369
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1370
|
+
};
|
|
1371
|
+
var SelfDestructRuntime = class extends AnimateRuntime {
|
|
1372
|
+
constructor(definition, state, random, callbacks) {
|
|
1373
|
+
super(definition, state, random);
|
|
1374
|
+
this.callbacks = callbacks;
|
|
1375
|
+
}
|
|
1376
|
+
callbacks;
|
|
1377
|
+
tick(context) {
|
|
1378
|
+
const result = super.tick(context);
|
|
1379
|
+
if (this.time === this.animationDuration(context) - 1) this.callbacks.remove();
|
|
1380
|
+
return result;
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
var ComplexRuntime = class extends RuntimeBase {
|
|
1384
|
+
constructor(definition, random, factory, selectOnly) {
|
|
1385
|
+
super(definition, random);
|
|
1386
|
+
this.factory = factory;
|
|
1387
|
+
this.selectOnly = selectOnly;
|
|
851
1388
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1389
|
+
factory;
|
|
1390
|
+
selectOnly;
|
|
1391
|
+
index = 0;
|
|
1392
|
+
child;
|
|
1393
|
+
selectionMade = false;
|
|
1394
|
+
onInit(context) {
|
|
1395
|
+
this.index = 0;
|
|
1396
|
+
this.child = void 0;
|
|
1397
|
+
this.selectionMade = false;
|
|
1398
|
+
if (this.baseHasNext(context)) this.seek(context);
|
|
1399
|
+
}
|
|
1400
|
+
hasMore(context) {
|
|
1401
|
+
if (!this.selectOnly) this.seek(context);
|
|
1402
|
+
return this.child?.hasNext(context) ?? false;
|
|
1403
|
+
}
|
|
1404
|
+
tick(context) {
|
|
1405
|
+
return this.child?.hasNext(context) ? this.child.step(context) : "running";
|
|
1406
|
+
}
|
|
1407
|
+
seek(context) {
|
|
1408
|
+
const definitions = this.definition.actions ?? [];
|
|
1409
|
+
if (definitions.length === 0) return;
|
|
1410
|
+
for (let guard = 0; guard <= definitions.length; guard += 1) {
|
|
1411
|
+
if (this.child?.hasNext(context)) {
|
|
1412
|
+
this.selectionMade = true;
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
if (this.selectOnly && this.selectionMade) {
|
|
1416
|
+
this.child = void 0;
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
if (this.index >= definitions.length) {
|
|
1420
|
+
if (this.definition.loop !== true) {
|
|
1421
|
+
this.child = void 0;
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
this.index = 0;
|
|
1425
|
+
}
|
|
1426
|
+
const definition = definitions[this.index++];
|
|
1427
|
+
if (!definition) {
|
|
1428
|
+
this.child = void 0;
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
this.child = this.factory(definition, context);
|
|
1432
|
+
this.child.init(context);
|
|
1433
|
+
}
|
|
1434
|
+
this.child = void 0;
|
|
855
1435
|
}
|
|
856
1436
|
};
|
|
857
1437
|
var ActionExecutor = class {
|
|
858
|
-
/** Creates an executor bound to one mascot's mutable internal state. */
|
|
859
1438
|
constructor(spec, state, options, callbacks) {
|
|
860
1439
|
this.spec = spec;
|
|
861
1440
|
this.state = state;
|
|
862
1441
|
this.options = options;
|
|
863
1442
|
this.callbacks = callbacks;
|
|
1443
|
+
this.random = options.random ?? Math.random;
|
|
864
1444
|
}
|
|
865
1445
|
spec;
|
|
866
1446
|
state;
|
|
867
1447
|
options;
|
|
868
1448
|
callbacks;
|
|
869
1449
|
runtime;
|
|
1450
|
+
accumulator = 0;
|
|
1451
|
+
random;
|
|
870
1452
|
/** Starts the action whose name matches a selected behavior. */
|
|
871
|
-
start(actionName, environment) {
|
|
1453
|
+
start(actionName, environment, _preserveLookRight = false, bounds = environment.mascot.environment.workArea, platforms = []) {
|
|
872
1454
|
const definition = this.spec.actions.find((action) => action.name === actionName);
|
|
873
|
-
this.
|
|
874
|
-
|
|
1455
|
+
this.accumulator = 0;
|
|
1456
|
+
if (!definition) {
|
|
1457
|
+
this.runtime = void 0;
|
|
1458
|
+
return false;
|
|
1459
|
+
}
|
|
1460
|
+
const context = { environment, bounds, platforms };
|
|
1461
|
+
this.runtime = this.createRuntime(definition, context, /* @__PURE__ */ new Set());
|
|
1462
|
+
this.runtime.init(context);
|
|
1463
|
+
return true;
|
|
875
1464
|
}
|
|
876
|
-
/** Advances
|
|
877
|
-
tick(deltaMs, environment, bounds) {
|
|
878
|
-
|
|
1465
|
+
/** Advances by elapsed milliseconds and reports completion. */
|
|
1466
|
+
tick(deltaMs, environment, bounds, platforms = []) {
|
|
1467
|
+
this.accumulator += Math.max(0, deltaMs);
|
|
1468
|
+
let result = this.runtime?.hasNext({ environment, bounds, platforms }) ? "running" : "complete";
|
|
1469
|
+
while (this.accumulator >= this.options.frameDuration && result === "running") {
|
|
1470
|
+
this.accumulator -= this.options.frameDuration;
|
|
1471
|
+
result = this.step(environment, bounds, platforms);
|
|
1472
|
+
}
|
|
1473
|
+
return result !== "running";
|
|
1474
|
+
}
|
|
1475
|
+
/** Advances exactly one legacy frame. */
|
|
1476
|
+
step(environment, bounds, platforms = []) {
|
|
1477
|
+
const context = { environment, bounds, platforms };
|
|
1478
|
+
if (!this.runtime?.hasNext(context)) return "complete";
|
|
1479
|
+
const result = this.runtime.step(context);
|
|
1480
|
+
if (result === "lost-ground") return result;
|
|
1481
|
+
return this.runtime.hasNext(context) ? "running" : "complete";
|
|
1482
|
+
}
|
|
1483
|
+
/** Returns whether the current action can execute another legacy frame. */
|
|
1484
|
+
hasNext(environment, bounds, platforms = []) {
|
|
1485
|
+
return this.runtime?.hasNext({ environment, bounds, platforms }) ?? false;
|
|
1486
|
+
}
|
|
1487
|
+
/** Retained for source compatibility with the previous collision API. */
|
|
1488
|
+
consumeViewportWallCollision() {
|
|
1489
|
+
return false;
|
|
879
1490
|
}
|
|
880
1491
|
/** Cancels the current action tree. */
|
|
881
1492
|
cancel() {
|
|
882
1493
|
this.runtime = void 0;
|
|
1494
|
+
this.accumulator = 0;
|
|
883
1495
|
}
|
|
884
|
-
createRuntime(definition,
|
|
885
|
-
if (definition.condition && !evaluateExpression(definition.condition, environment, false)) return new CompleteRuntime();
|
|
886
|
-
const bounds = environment.mascot.environment.workArea;
|
|
887
|
-
const activeIE = environment.mascot.environment.activeIE;
|
|
888
|
-
if (!isOnBorder(this.state, bounds, definition.borderType, activeIE.visible ? activeIE : void 0)) return new CompleteRuntime();
|
|
1496
|
+
createRuntime(definition, context, references) {
|
|
889
1497
|
if (definition.type === "Reference") {
|
|
890
|
-
if (!definition.name || references.has(definition.name)) return new
|
|
1498
|
+
if (!definition.name || references.has(definition.name)) return new InstantRuntime(definition, this.state, this.random, "noop");
|
|
891
1499
|
const referenced = this.spec.actions.find((action) => action.name === definition.name);
|
|
892
|
-
if (!referenced) return new
|
|
893
|
-
|
|
894
|
-
return this.createRuntime({ ...referenced, ...definition, type: referenced.type }, environment, nextReferences);
|
|
1500
|
+
if (!referenced) return new InstantRuntime(definition, this.state, this.random, "noop");
|
|
1501
|
+
return this.createRuntime({ ...referenced, ...definition, type: referenced.type, name: definition.name }, context, new Set(references).add(definition.name));
|
|
895
1502
|
}
|
|
896
|
-
if (definition.type === "Sequence") {
|
|
897
|
-
return new
|
|
1503
|
+
if (definition.type === "Sequence" || definition.type === "Select") {
|
|
1504
|
+
return new ComplexRuntime(definition, this.random, (child, nextContext) => this.createRuntime(child, nextContext, new Set(references)), definition.type === "Select");
|
|
898
1505
|
}
|
|
899
|
-
if (definition.type === "
|
|
900
|
-
|
|
901
|
-
|
|
1506
|
+
if (definition.type === "Stay") return new StayRuntime(definition, this.state, this.random);
|
|
1507
|
+
if (definition.type === "Animate") return new AnimateRuntime(definition, this.state, this.random);
|
|
1508
|
+
if (definition.type === "Move") return new MoveRuntime(definition, this.state, this.random);
|
|
1509
|
+
switch (definition.embedType) {
|
|
1510
|
+
case "Fall":
|
|
1511
|
+
return new FallRuntime(definition, this.state, this.random, this.options.gravity);
|
|
1512
|
+
case "FallWithIE":
|
|
1513
|
+
return new CarryFallRuntime(definition, this.state, this.random, this.options.gravity, this.callbacks);
|
|
1514
|
+
case "Jump":
|
|
1515
|
+
case "ComplexJump":
|
|
1516
|
+
case "ScanJump":
|
|
1517
|
+
case "BroadcastJump":
|
|
1518
|
+
return new JumpRuntime(definition, this.state, this.random);
|
|
1519
|
+
case "WalkWithIE":
|
|
1520
|
+
return new CarryMoveRuntime(definition, this.state, this.random, this.callbacks);
|
|
1521
|
+
case "MoveWithTurn":
|
|
1522
|
+
return new MoveWithTurnRuntime(definition, this.state, this.random);
|
|
1523
|
+
case "ComplexMove":
|
|
1524
|
+
case "ScanMove":
|
|
1525
|
+
case "BroadcastMove":
|
|
1526
|
+
return new MoveRuntime(definition, this.state, this.random);
|
|
1527
|
+
case "Turn":
|
|
1528
|
+
return new TurnRuntime(definition, this.state, this.random);
|
|
1529
|
+
case "Look":
|
|
1530
|
+
return new InstantRuntime(definition, this.state, this.random, "look");
|
|
1531
|
+
case "Offset":
|
|
1532
|
+
return new InstantRuntime(definition, this.state, this.random, "offset");
|
|
1533
|
+
case "Mute":
|
|
1534
|
+
case "Reboot":
|
|
1535
|
+
return new InstantRuntime(definition, this.state, this.random, "noop");
|
|
1536
|
+
case "Breed":
|
|
1537
|
+
return new BreedRuntime(definition, this.state, this.random, this.callbacks);
|
|
1538
|
+
case "BreedMove":
|
|
1539
|
+
return new BreedMoveRuntime(definition, this.state, this.random, this.callbacks);
|
|
1540
|
+
case "BreedJump":
|
|
1541
|
+
return new BreedJumpRuntime(definition, this.state, this.random, this.callbacks);
|
|
1542
|
+
case "ThrowIE":
|
|
1543
|
+
return new ThrowPlatformRuntime(definition, this.state, this.random, this.callbacks);
|
|
1544
|
+
case "SelfDestruct":
|
|
1545
|
+
case "Exit":
|
|
1546
|
+
return new SelfDestructRuntime(definition, this.state, this.random, this.callbacks);
|
|
1547
|
+
case "Dragged":
|
|
1548
|
+
return new DraggedRuntime(definition, this.state, this.random, this.spec);
|
|
1549
|
+
case "Regist":
|
|
1550
|
+
return new RegistRuntime(definition, this.state, this.random, this.spec);
|
|
1551
|
+
case "Broadcast":
|
|
1552
|
+
case "Interact":
|
|
1553
|
+
case "ScanInteract":
|
|
1554
|
+
case "Transform":
|
|
1555
|
+
return new AnimateRuntime(definition, this.state, this.random);
|
|
1556
|
+
case "BroadcastStay":
|
|
1557
|
+
return new StayRuntime(definition, this.state, this.random);
|
|
1558
|
+
default:
|
|
1559
|
+
return new StayRuntime(definition, this.state, this.random);
|
|
902
1560
|
}
|
|
903
|
-
return new LeafRuntime(evaluateAction(definition, environment), this.state, this.options, this.callbacks, environment);
|
|
904
1561
|
}
|
|
905
1562
|
};
|
|
906
1563
|
|
|
@@ -929,7 +1586,6 @@ function environmentRectangle(bounds) {
|
|
|
929
1586
|
};
|
|
930
1587
|
}
|
|
931
1588
|
var PLATFORM_NEARBY_DISTANCE = 400;
|
|
932
|
-
var PLATFORM_EDGE_TOLERANCE = 16;
|
|
933
1589
|
function distanceToRectangle(point, rectangle) {
|
|
934
1590
|
const dx = Math.max(rectangle.x - point.x, 0, point.x - rectangle.x - rectangle.width);
|
|
935
1591
|
const dy = Math.max(rectangle.y - point.y, 0, point.y - rectangle.y - rectangle.height);
|
|
@@ -958,10 +1614,13 @@ var Mascot = class {
|
|
|
958
1614
|
dragging: false
|
|
959
1615
|
};
|
|
960
1616
|
this.domHandle = dom.createMascot(spec, id, options.mascotClassName || void 0);
|
|
1617
|
+
this.frameDuration = options.frameDuration;
|
|
1618
|
+
this.random = options.random ?? Math.random;
|
|
961
1619
|
this.behavior = new BehaviorController(spec, options.random);
|
|
962
1620
|
this.actions = new ActionExecutor(spec, this.state, options, {
|
|
963
|
-
spawn: (position) => this.callbacks.spawn(this.spec.id, position),
|
|
964
|
-
remove: () => this.callbacks.remove(this.id)
|
|
1621
|
+
spawn: (position, characterId) => this.callbacks.spawn(characterId ?? this.spec.id, position),
|
|
1622
|
+
remove: () => this.callbacks.remove(this.id),
|
|
1623
|
+
...this.callbacks.movePlatform && { movePlatform: this.callbacks.movePlatform }
|
|
965
1624
|
});
|
|
966
1625
|
this.installPointerHandlers();
|
|
967
1626
|
}
|
|
@@ -983,44 +1642,19 @@ var Mascot = class {
|
|
|
983
1642
|
lastPointer;
|
|
984
1643
|
activePlatformElement;
|
|
985
1644
|
platforms = [];
|
|
1645
|
+
accumulatedMs = 0;
|
|
1646
|
+
frameDuration;
|
|
1647
|
+
random;
|
|
986
1648
|
/** Advances behavior, animation, physics, and rendering by one clock tick. */
|
|
987
1649
|
tick(deltaMs, bounds, platforms = []) {
|
|
988
1650
|
if (this.destroyed) return;
|
|
989
1651
|
this.platforms = platforms;
|
|
990
|
-
const environment = this.createEnvironment(bounds, platforms);
|
|
991
|
-
if (this.state.dragging) {
|
|
992
|
-
this.dom.render(this.domHandle, this.spec, this.state);
|
|
993
|
-
return;
|
|
994
|
-
}
|
|
995
1652
|
try {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
this.currentBehavior = this.findFallBehavior();
|
|
1002
|
-
started = this.startBehavior(environment);
|
|
1003
|
-
}
|
|
1004
|
-
if (!started) break;
|
|
1005
|
-
}
|
|
1006
|
-
const activeIE = environment.mascot.environment.activeIE;
|
|
1007
|
-
const wasOnPlatformTop = activeIE.visible && activeIE.topBorder.isOn(this.state);
|
|
1008
|
-
const completed = this.actions.tick(deltaMs, environment, bounds);
|
|
1009
|
-
const remainedNearPlatform = this.state.x >= activeIE.left - PLATFORM_EDGE_TOLERANCE && this.state.x <= activeIE.right + PLATFORM_EDGE_TOLERANCE;
|
|
1010
|
-
if (wasOnPlatformTop && !remainedNearPlatform && !platforms.some((platform) => isOnTop(this.state, platform))) {
|
|
1011
|
-
this.actions.cancel();
|
|
1012
|
-
this.currentBehavior = this.findFallBehavior();
|
|
1013
|
-
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms));
|
|
1014
|
-
break;
|
|
1015
|
-
}
|
|
1016
|
-
if (!completed) break;
|
|
1017
|
-
if (this.destroyed) return;
|
|
1018
|
-
this.currentBehavior = this.behavior.selectNext(this.createEnvironment(bounds, platforms));
|
|
1019
|
-
if (!this.currentBehavior || !this.startBehavior(this.createEnvironment(bounds, platforms))) {
|
|
1020
|
-
this.currentBehavior = this.findFallBehavior();
|
|
1021
|
-
if (!this.currentBehavior || !this.startBehavior(this.createEnvironment(bounds, platforms))) break;
|
|
1022
|
-
}
|
|
1023
|
-
deltaMs = 0;
|
|
1653
|
+
this.ensureBehavior(bounds, platforms, true);
|
|
1654
|
+
this.accumulatedMs += Math.max(0, deltaMs);
|
|
1655
|
+
while (this.accumulatedMs >= this.frameDuration && !this.destroyed) {
|
|
1656
|
+
this.accumulatedMs -= this.frameDuration;
|
|
1657
|
+
this.legacyTick(bounds, platforms);
|
|
1024
1658
|
}
|
|
1025
1659
|
} catch (error) {
|
|
1026
1660
|
this.callbacks.error(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -1041,14 +1675,68 @@ var Mascot = class {
|
|
|
1041
1675
|
for (const dispose of this.disposers.splice(0)) dispose();
|
|
1042
1676
|
this.dom.removeMascot(this.domHandle);
|
|
1043
1677
|
}
|
|
1044
|
-
startBehavior(environment) {
|
|
1678
|
+
startBehavior(environment, bounds, platforms) {
|
|
1045
1679
|
if (!this.currentBehavior) return false;
|
|
1046
1680
|
this.state.behaviorName = this.currentBehavior.name;
|
|
1047
|
-
return this.actions.start(this.currentBehavior.name, environment);
|
|
1681
|
+
return this.actions.start(this.currentBehavior.actionName ?? this.currentBehavior.name, environment, false, bounds, platforms);
|
|
1682
|
+
}
|
|
1683
|
+
ensureBehavior(bounds, platforms, initial) {
|
|
1684
|
+
for (let guard = 0; guard < 32 && !this.destroyed; guard += 1) {
|
|
1685
|
+
const environment = this.createEnvironment(bounds, platforms);
|
|
1686
|
+
if (!this.currentBehavior) {
|
|
1687
|
+
this.currentBehavior = initial ? this.behavior.selectInitial(environment, this.state.behaviorName) : this.selectNextBehavior(environment, bounds);
|
|
1688
|
+
initial = false;
|
|
1689
|
+
if (!this.currentBehavior) this.currentBehavior = this.findFallBehavior();
|
|
1690
|
+
if (!this.currentBehavior || !this.startBehavior(environment, bounds, platforms)) return;
|
|
1691
|
+
}
|
|
1692
|
+
if (this.actions.hasNext(environment, bounds, platforms)) return;
|
|
1693
|
+
this.currentBehavior = this.selectNextBehavior(environment, bounds) ?? this.findFallBehavior();
|
|
1694
|
+
if (!this.currentBehavior) return;
|
|
1695
|
+
if (!this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms)) return;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
legacyTick(bounds, platforms) {
|
|
1699
|
+
this.ensureBehavior(bounds, platforms, false);
|
|
1700
|
+
if (!this.currentBehavior) return;
|
|
1701
|
+
const result = this.actions.step(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1702
|
+
if (this.destroyed) return;
|
|
1703
|
+
if (result === "lost-ground") {
|
|
1704
|
+
this.state.dragging = false;
|
|
1705
|
+
this.actions.cancel();
|
|
1706
|
+
this.currentBehavior = this.findFallBehavior();
|
|
1707
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1708
|
+
} else if (result === "complete") {
|
|
1709
|
+
this.currentBehavior = this.selectNextBehavior(this.createEnvironment(bounds, platforms), bounds);
|
|
1710
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1711
|
+
this.ensureBehavior(bounds, platforms, false);
|
|
1712
|
+
} else if (this.isOutsideVisibleBounds(bounds)) {
|
|
1713
|
+
this.state.x = Math.trunc(bounds.x + this.random() * bounds.width);
|
|
1714
|
+
this.state.y = bounds.y - 256;
|
|
1715
|
+
this.actions.cancel();
|
|
1716
|
+
this.currentBehavior = this.findFallBehavior();
|
|
1717
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
isOutsideVisibleBounds(bounds) {
|
|
1721
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1722
|
+
const width = typeof sprite === "object" && "width" in sprite && sprite.width !== void 0 ? sprite.width : 128;
|
|
1723
|
+
const height = typeof sprite === "object" && "height" in sprite && sprite.height !== void 0 ? sprite.height : 128;
|
|
1724
|
+
const anchorX = this.state.lookRight ? width - this.state.anchorX : this.state.anchorX;
|
|
1725
|
+
const left = this.state.x - anchorX;
|
|
1726
|
+
const top = this.state.y - this.state.anchorY;
|
|
1727
|
+
return left + width <= bounds.x || bounds.x + bounds.width <= left || bounds.y + bounds.height <= top;
|
|
1048
1728
|
}
|
|
1049
1729
|
findFallBehavior() {
|
|
1050
1730
|
return this.behavior.force("Fall") ?? this.behavior.force("\u843D\u4E0B\u3059\u308B");
|
|
1051
1731
|
}
|
|
1732
|
+
selectNextBehavior(environment, bounds) {
|
|
1733
|
+
const selected = this.behavior.selectNext(environment);
|
|
1734
|
+
if (this.behavior.usedFallback()) {
|
|
1735
|
+
this.state.x = Math.trunc(bounds.x + this.random() * bounds.width);
|
|
1736
|
+
this.state.y = bounds.y - 256;
|
|
1737
|
+
}
|
|
1738
|
+
return selected;
|
|
1739
|
+
}
|
|
1052
1740
|
createEnvironment(bounds, platforms = this.platforms) {
|
|
1053
1741
|
const workArea = environmentRectangle(bounds);
|
|
1054
1742
|
const inactive = environmentRectangle({ x: -100, y: -100, width: 0, height: 0 });
|
|
@@ -1063,10 +1751,10 @@ var Mascot = class {
|
|
|
1063
1751
|
lookRight: this.state.lookRight,
|
|
1064
1752
|
environment: {
|
|
1065
1753
|
cursor: this.callbacks.pointer(),
|
|
1066
|
-
screen:
|
|
1754
|
+
screen: workArea,
|
|
1067
1755
|
workArea,
|
|
1068
|
-
floor:
|
|
1069
|
-
ceiling:
|
|
1756
|
+
floor: edge((point) => isOnFloor(point, bounds, platforms)),
|
|
1757
|
+
ceiling: edge((point) => isOnTop(point, bounds) || platforms.some((candidate) => isOnBottom(point, candidate))),
|
|
1070
1758
|
activeIE
|
|
1071
1759
|
}
|
|
1072
1760
|
}
|
|
@@ -1093,6 +1781,7 @@ var Mascot = class {
|
|
|
1093
1781
|
}
|
|
1094
1782
|
installPointerHandlers() {
|
|
1095
1783
|
const element = this.domHandle.spriteElement;
|
|
1784
|
+
const document2 = element.ownerDocument;
|
|
1096
1785
|
const listen = (target, type, listener) => {
|
|
1097
1786
|
target.addEventListener(type, listener);
|
|
1098
1787
|
this.disposers.push(() => target.removeEventListener(type, listener));
|
|
@@ -1101,29 +1790,29 @@ var Mascot = class {
|
|
|
1101
1790
|
const pointerEvent = event;
|
|
1102
1791
|
if (pointerEvent.button !== 0) return;
|
|
1103
1792
|
event.preventDefault();
|
|
1104
|
-
const point =
|
|
1793
|
+
const point = this.dom.toLocalPoint(pointerEvent.clientX, pointerEvent.clientY);
|
|
1105
1794
|
this.pointerId = pointerEvent.pointerId;
|
|
1106
1795
|
this.pointerDown = point;
|
|
1107
1796
|
this.lastPointer = point;
|
|
1108
1797
|
this.dragOffset = { x: this.state.x - point.x, y: this.state.y - point.y };
|
|
1109
1798
|
this.state.dragging = true;
|
|
1110
|
-
this.actions.cancel();
|
|
1111
1799
|
this.currentBehavior = this.behavior.force("Dragged") ?? this.behavior.force("\u30C9\u30E9\u30C3\u30B0\u3055\u308C\u308B");
|
|
1112
|
-
if (this.currentBehavior) this.
|
|
1800
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(this.dom.getBounds()), this.dom.getBounds(), this.platforms);
|
|
1113
1801
|
element.setPointerCapture?.(pointerEvent.pointerId);
|
|
1114
1802
|
});
|
|
1115
|
-
listen(
|
|
1803
|
+
listen(document2, "pointermove", (event) => {
|
|
1116
1804
|
const pointerEvent = event;
|
|
1117
1805
|
if (!this.state.dragging || pointerEvent.pointerId !== this.pointerId) return;
|
|
1118
|
-
const point =
|
|
1806
|
+
const point = this.dom.toLocalPoint(pointerEvent.clientX, pointerEvent.clientY);
|
|
1119
1807
|
const previous = this.lastPointer ?? point;
|
|
1808
|
+
const bounds = this.dom.getBounds();
|
|
1120
1809
|
this.state.vx = (point.x - previous.x) * 0.8;
|
|
1121
1810
|
this.state.vy = (point.y - previous.y) * 0.8;
|
|
1122
1811
|
this.state.x = point.x + this.dragOffset.x;
|
|
1123
1812
|
this.state.y = point.y + this.dragOffset.y;
|
|
1124
1813
|
this.lastPointer = point;
|
|
1125
1814
|
});
|
|
1126
|
-
listen(
|
|
1815
|
+
listen(document2, "pointerup", (event) => {
|
|
1127
1816
|
const pointerEvent = event;
|
|
1128
1817
|
if (!this.state.dragging || pointerEvent.pointerId !== this.pointerId) return;
|
|
1129
1818
|
this.state.dragging = false;
|
|
@@ -1131,8 +1820,8 @@ var Mascot = class {
|
|
|
1131
1820
|
this.pointerId = void 0;
|
|
1132
1821
|
this.currentBehavior = this.behavior.force("Thrown") ?? this.behavior.force("\u6295\u3052\u3089\u308C\u308B") ?? this.findFallBehavior();
|
|
1133
1822
|
if (this.currentBehavior) {
|
|
1134
|
-
|
|
1135
|
-
this.
|
|
1823
|
+
const bounds = this.dom.getBounds();
|
|
1824
|
+
this.startBehavior(this.createEnvironment(bounds), bounds, this.platforms);
|
|
1136
1825
|
}
|
|
1137
1826
|
if (moved < 4) this.callbacks.click(this.snapshot());
|
|
1138
1827
|
});
|
|
@@ -1140,21 +1829,23 @@ var Mascot = class {
|
|
|
1140
1829
|
};
|
|
1141
1830
|
|
|
1142
1831
|
// src/platform.ts
|
|
1143
|
-
function resolvePlatformElements(source,
|
|
1832
|
+
function resolvePlatformElements(source, root, excludedRoot) {
|
|
1144
1833
|
let elements;
|
|
1145
1834
|
if (typeof source === "string") {
|
|
1146
1835
|
try {
|
|
1147
|
-
elements = [...
|
|
1836
|
+
elements = [...root.querySelectorAll(source)];
|
|
1148
1837
|
} catch {
|
|
1149
1838
|
return [];
|
|
1150
1839
|
}
|
|
1151
1840
|
} else {
|
|
1152
1841
|
elements = source;
|
|
1153
1842
|
}
|
|
1843
|
+
const document2 = root.nodeType === 9 ? root : root.ownerDocument;
|
|
1844
|
+
if (!document2) return [];
|
|
1154
1845
|
const HTMLElementConstructor = document2.defaultView?.HTMLElement;
|
|
1155
1846
|
if (!HTMLElementConstructor) return [];
|
|
1156
1847
|
return [...new Set(elements)].filter(
|
|
1157
|
-
(element) => element instanceof HTMLElementConstructor && element.isConnected && (!excludedRoot || !excludedRoot.contains(element))
|
|
1848
|
+
(element) => element instanceof HTMLElementConstructor && element.isConnected && root.contains(element) && (!excludedRoot || !excludedRoot.contains(element))
|
|
1158
1849
|
);
|
|
1159
1850
|
}
|
|
1160
1851
|
function readPlatformRectangles(elements, workAreaRectangle) {
|
|
@@ -1282,21 +1973,26 @@ var ShimejiEngine = class {
|
|
|
1282
1973
|
destroyed = false;
|
|
1283
1974
|
initialized = false;
|
|
1284
1975
|
platformSource;
|
|
1285
|
-
|
|
1976
|
+
additionalPlatformElements = [];
|
|
1977
|
+
movedPlatforms = /* @__PURE__ */ new Map();
|
|
1978
|
+
platformRectangles = /* @__PURE__ */ new Map();
|
|
1979
|
+
/** Starts the clock and container-aware listeners. Calling this method more than once is harmless. */
|
|
1286
1980
|
initialize() {
|
|
1287
1981
|
this.assertAlive();
|
|
1288
1982
|
if (this.initialized) return;
|
|
1289
1983
|
this.initialized = true;
|
|
1290
|
-
|
|
1984
|
+
const document2 = this.container.ownerDocument;
|
|
1985
|
+
const view = document2.defaultView;
|
|
1986
|
+
if (!view) throw new Error("ShimejiEngine requires a container connected to a window");
|
|
1987
|
+
this.listen(document2, "pointermove", (event) => {
|
|
1291
1988
|
const pointerEvent = event;
|
|
1292
|
-
const x = pointerEvent.clientX;
|
|
1293
|
-
const y = pointerEvent.clientY;
|
|
1989
|
+
const { x, y } = this.dom.toLocalPoint(pointerEvent.clientX, pointerEvent.clientY);
|
|
1294
1990
|
this.pointer = { x, y, dx: x - this.pointer.x, dy: y - this.pointer.y };
|
|
1295
1991
|
});
|
|
1296
|
-
this.listen(
|
|
1297
|
-
const maintenance =
|
|
1992
|
+
this.listen(view, "resize", () => this.renderAll());
|
|
1993
|
+
const maintenance = view.setInterval(() => this.dom.ensureMounted(), 2e3);
|
|
1298
1994
|
this.intervals.add(maintenance);
|
|
1299
|
-
this.animationFrame = requestAnimationFrame(this.onAnimationFrame);
|
|
1995
|
+
this.animationFrame = view.requestAnimationFrame(this.onAnimationFrame);
|
|
1300
1996
|
}
|
|
1301
1997
|
/** Registers or replaces a parsed or legacy character specification. */
|
|
1302
1998
|
registerCharacter(spec) {
|
|
@@ -1326,8 +2022,8 @@ var ShimejiEngine = class {
|
|
|
1326
2022
|
const random = this.options.random ?? Math.random;
|
|
1327
2023
|
const spawnOptions = {
|
|
1328
2024
|
...position,
|
|
1329
|
-
x: position.x ?? bounds.x + random() * bounds.width,
|
|
1330
|
-
y: position.y ?? bounds.y
|
|
2025
|
+
x: position.x ?? Math.trunc(bounds.x + random() * bounds.width),
|
|
2026
|
+
y: position.y ?? bounds.y + 2
|
|
1331
2027
|
};
|
|
1332
2028
|
const id = `shimeji-${this.nextMascotId++}`;
|
|
1333
2029
|
const mascot = new Mascot(id, spec, this.dom, this.options, spawnOptions, {
|
|
@@ -1339,6 +2035,7 @@ var ShimejiEngine = class {
|
|
|
1339
2035
|
remove: (mascotId) => {
|
|
1340
2036
|
if (!this.destroyed) this.remove(mascotId);
|
|
1341
2037
|
},
|
|
2038
|
+
movePlatform: (element, point) => this.movePlatform(element, point),
|
|
1342
2039
|
click: (state2) => this.events.emit("click", state2),
|
|
1343
2040
|
error: (error) => this.events.emit("error", error)
|
|
1344
2041
|
});
|
|
@@ -1375,22 +2072,27 @@ var ShimejiEngine = class {
|
|
|
1375
2072
|
this.assertAlive();
|
|
1376
2073
|
return this.events.on(event, listener);
|
|
1377
2074
|
}
|
|
1378
|
-
/** Replaces the
|
|
1379
|
-
setPlatforms(platforms) {
|
|
2075
|
+
/** Replaces the primary platform source and any additional registered elements. */
|
|
2076
|
+
setPlatforms(platforms, additionalPlatforms = []) {
|
|
1380
2077
|
this.assertAlive();
|
|
1381
2078
|
this.platformSource = platforms;
|
|
2079
|
+
this.additionalPlatformElements = additionalPlatforms;
|
|
1382
2080
|
}
|
|
1383
2081
|
/** Stops animation and timers, removes listeners and DOM, and revokes all object URLs. */
|
|
1384
2082
|
destroy() {
|
|
1385
2083
|
if (this.destroyed) return;
|
|
1386
2084
|
for (const mascot of this.mascots.values()) mascot.destroy();
|
|
1387
2085
|
this.mascots.clear();
|
|
1388
|
-
|
|
2086
|
+
const view = this.container.ownerDocument.defaultView;
|
|
2087
|
+
if (this.animationFrame !== void 0) view?.cancelAnimationFrame(this.animationFrame);
|
|
1389
2088
|
this.animationFrame = void 0;
|
|
1390
|
-
for (const interval of this.intervals)
|
|
2089
|
+
for (const interval of this.intervals) view?.clearInterval(interval);
|
|
1391
2090
|
this.intervals.clear();
|
|
1392
2091
|
for (const dispose of this.disposers.splice(0)) dispose();
|
|
1393
2092
|
this.dom.destroy();
|
|
2093
|
+
for (const [element, movement] of this.movedPlatforms) element.style.transform = movement.originalTransform;
|
|
2094
|
+
this.movedPlatforms.clear();
|
|
2095
|
+
this.platformRectangles.clear();
|
|
1394
2096
|
this.sprites.destroy();
|
|
1395
2097
|
this.specs.clear();
|
|
1396
2098
|
this.events.clear();
|
|
@@ -1409,7 +2111,7 @@ var ShimejiEngine = class {
|
|
|
1409
2111
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
1410
2112
|
for (const mascot of [...this.mascots.values()]) mascot.tick(delta, bounds, platforms);
|
|
1411
2113
|
this.emitState();
|
|
1412
|
-
this.animationFrame = requestAnimationFrame(this.onAnimationFrame);
|
|
2114
|
+
this.animationFrame = this.container.ownerDocument.defaultView?.requestAnimationFrame(this.onAnimationFrame);
|
|
1413
2115
|
};
|
|
1414
2116
|
renderAll() {
|
|
1415
2117
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
@@ -1417,8 +2119,26 @@ var ShimejiEngine = class {
|
|
|
1417
2119
|
}
|
|
1418
2120
|
readFrameGeometry() {
|
|
1419
2121
|
const bounds = this.dom.getBounds();
|
|
1420
|
-
const elements =
|
|
1421
|
-
|
|
2122
|
+
const elements = [.../* @__PURE__ */ new Set([
|
|
2123
|
+
...resolvePlatformElements(this.platformSource, this.container),
|
|
2124
|
+
...resolvePlatformElements(this.additionalPlatformElements, this.container)
|
|
2125
|
+
])].filter((element) => !this.dom.owns(element));
|
|
2126
|
+
const platforms = readPlatformRectangles(elements, this.container.getBoundingClientRect());
|
|
2127
|
+
this.platformRectangles.clear();
|
|
2128
|
+
for (const platform of platforms) this.platformRectangles.set(platform.element, platform);
|
|
2129
|
+
return { bounds, platforms };
|
|
2130
|
+
}
|
|
2131
|
+
movePlatform(element, point) {
|
|
2132
|
+
const rectangle = this.platformRectangles.get(element);
|
|
2133
|
+
if (!rectangle) return;
|
|
2134
|
+
const movement = this.movedPlatforms.get(element) ?? { originalTransform: element.style.transform, x: 0, y: 0 };
|
|
2135
|
+
movement.x += point.x - rectangle.x;
|
|
2136
|
+
movement.y += point.y - rectangle.y;
|
|
2137
|
+
const translate = `translate(${movement.x}px, ${movement.y}px)`;
|
|
2138
|
+
element.style.transform = movement.originalTransform ? `${movement.originalTransform} ${translate}` : translate;
|
|
2139
|
+
rectangle.x = point.x;
|
|
2140
|
+
rectangle.y = point.y;
|
|
2141
|
+
this.movedPlatforms.set(element, movement);
|
|
1422
2142
|
}
|
|
1423
2143
|
emitState() {
|
|
1424
2144
|
this.events.emit("statechange", this.getState());
|