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