@react-shimeji/core 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1058 -339
- 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 +1058 -339
- 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,61 +603,71 @@ 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 = 0.999999;
|
|
600
658
|
function clamp(value, minimum, maximum) {
|
|
601
659
|
return Math.min(Math.max(value, minimum), maximum);
|
|
602
660
|
}
|
|
603
|
-
function isOnTop(point, rectangle, tolerance =
|
|
661
|
+
function isOnTop(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
604
662
|
return point.x >= rectangle.x - tolerance && point.x <= rectangle.x + rectangle.width + tolerance && Math.abs(point.y - rectangle.y) <= tolerance;
|
|
605
663
|
}
|
|
606
|
-
function isOnBottom(point, rectangle, tolerance =
|
|
664
|
+
function isOnBottom(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
607
665
|
return point.x >= rectangle.x - tolerance && point.x <= rectangle.x + rectangle.width + tolerance && Math.abs(point.y - rectangle.y - rectangle.height) <= tolerance;
|
|
608
666
|
}
|
|
609
|
-
function isOnLeft(point, rectangle, tolerance =
|
|
667
|
+
function isOnLeft(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
610
668
|
return point.y >= rectangle.y - tolerance && point.y <= rectangle.y + rectangle.height + tolerance && Math.abs(point.x - rectangle.x) <= tolerance;
|
|
611
669
|
}
|
|
612
|
-
function isOnRight(point, rectangle, tolerance =
|
|
670
|
+
function isOnRight(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
613
671
|
return point.y >= rectangle.y - tolerance && point.y <= rectangle.y + rectangle.height + tolerance && Math.abs(point.x - rectangle.x - rectangle.width) <= tolerance;
|
|
614
672
|
}
|
|
615
673
|
function isOnBorder(state, bounds, border, platform) {
|
|
@@ -618,29 +676,46 @@ function isOnBorder(state, bounds, border, platform) {
|
|
|
618
676
|
if (border === "Ceiling") return isOnTop(state, bounds) || platform !== void 0 && isOnBottom(state, platform);
|
|
619
677
|
return isOnLeft(state, bounds) || isOnRight(state, bounds) || platform !== void 0 && (isOnLeft(state, platform) || isOnRight(state, platform));
|
|
620
678
|
}
|
|
621
|
-
function
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
679
|
+
function isOnFloor(point, bounds, platforms = []) {
|
|
680
|
+
return platforms.some((platform) => isOnTop(point, platform)) || isOnBottom(point, bounds);
|
|
681
|
+
}
|
|
682
|
+
function isOnWall(point, bounds, lookRight, platforms = []) {
|
|
683
|
+
return lookRight ? platforms.some((platform) => isOnLeft(point, platform)) || isOnRight(point, bounds) : platforms.some((platform) => isOnRight(point, platform)) || isOnLeft(point, bounds);
|
|
684
|
+
}
|
|
685
|
+
function applyGravity(state, bounds, frameScale, gravity, resistanceX = 0.05, resistanceY = 0.1, platform) {
|
|
686
|
+
const platforms = platform ? [platform] : [];
|
|
687
|
+
const steps = Math.max(1, Math.round(frameScale));
|
|
688
|
+
let stopped = false;
|
|
689
|
+
for (let frame = 0; frame < steps && !stopped; frame += 1) {
|
|
690
|
+
state.vx -= state.vx * resistanceX;
|
|
691
|
+
state.vy = state.vy - state.vy * resistanceY + gravity;
|
|
692
|
+
const dx = Math.trunc(state.vx);
|
|
693
|
+
const dy = Math.trunc(state.vy);
|
|
694
|
+
const divisions = Math.max(1, Math.abs(dx), Math.abs(dy));
|
|
695
|
+
const start = { x: state.x, y: state.y };
|
|
696
|
+
for (let index = 0; index <= divisions; index += 1) {
|
|
697
|
+
const x = start.x + Math.trunc(dx * index / divisions);
|
|
698
|
+
const y = start.y + Math.trunc(dy * index / divisions);
|
|
699
|
+
state.x = x;
|
|
700
|
+
state.y = y;
|
|
701
|
+
if (dy > 0) {
|
|
702
|
+
for (let offset = -80; offset <= 0; offset += 1) {
|
|
703
|
+
state.y = y + offset;
|
|
704
|
+
if (isOnFloor(state, bounds, platforms)) {
|
|
705
|
+
stopped = true;
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (stopped) break;
|
|
710
|
+
state.y = y;
|
|
711
|
+
}
|
|
712
|
+
if (isOnWall(state, bounds, state.lookRight, platforms)) {
|
|
713
|
+
stopped = true;
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
642
717
|
}
|
|
643
|
-
return
|
|
718
|
+
return stopped;
|
|
644
719
|
}
|
|
645
720
|
function moveToward(state, target, speed, frameScale) {
|
|
646
721
|
const dx = target.x - state.x;
|
|
@@ -657,251 +732,832 @@ function moveToward(state, target, speed, frameScale) {
|
|
|
657
732
|
}
|
|
658
733
|
|
|
659
734
|
// src/action.ts
|
|
660
|
-
function
|
|
661
|
-
return value ===
|
|
735
|
+
function expressionIsPerFrame(value) {
|
|
736
|
+
return typeof value === "string" && value.trimStart().startsWith("#{");
|
|
662
737
|
}
|
|
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
|
-
|
|
738
|
+
var ActionValues = class {
|
|
739
|
+
constructor(random) {
|
|
740
|
+
this.random = random;
|
|
741
|
+
}
|
|
742
|
+
random;
|
|
743
|
+
actionCache = /* @__PURE__ */ new Map();
|
|
744
|
+
frameCache = /* @__PURE__ */ new Map();
|
|
745
|
+
init() {
|
|
746
|
+
this.actionCache.clear();
|
|
747
|
+
this.frameCache.clear();
|
|
748
|
+
}
|
|
749
|
+
initFrame() {
|
|
750
|
+
this.frameCache.clear();
|
|
751
|
+
}
|
|
752
|
+
number(key, value, environment, fallback) {
|
|
753
|
+
if (value === void 0) return fallback;
|
|
754
|
+
if (typeof value === "number") return value;
|
|
755
|
+
const cache = expressionIsPerFrame(value) ? this.frameCache : this.actionCache;
|
|
756
|
+
const cached = cache.get(key);
|
|
757
|
+
if (typeof cached === "number") return cached;
|
|
758
|
+
const result = evaluateExpression(value, environment, fallback, this.random);
|
|
759
|
+
cache.set(key, result);
|
|
760
|
+
return result;
|
|
761
|
+
}
|
|
762
|
+
boolean(key, value, environment, fallback) {
|
|
763
|
+
if (value === void 0) return fallback;
|
|
764
|
+
if (typeof value === "boolean") return value;
|
|
765
|
+
const cache = expressionIsPerFrame(value) ? this.frameCache : this.actionCache;
|
|
766
|
+
const cached = cache.get(key);
|
|
767
|
+
if (typeof cached === "boolean") return cached;
|
|
768
|
+
const result = evaluateExpression(value, environment, fallback, this.random);
|
|
769
|
+
cache.set(key, result);
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
var RuntimeBase = class {
|
|
774
|
+
constructor(definition, random) {
|
|
775
|
+
this.definition = definition;
|
|
776
|
+
this.values = new ActionValues(random);
|
|
777
|
+
}
|
|
778
|
+
definition;
|
|
779
|
+
time = 0;
|
|
780
|
+
values;
|
|
781
|
+
init(context) {
|
|
782
|
+
this.time = 0;
|
|
783
|
+
this.values.init();
|
|
784
|
+
this.onInit(context);
|
|
785
|
+
}
|
|
786
|
+
hasNext(context) {
|
|
787
|
+
return this.baseHasNext(context) && this.hasMore(context);
|
|
788
|
+
}
|
|
789
|
+
step(context) {
|
|
790
|
+
this.values.initFrame();
|
|
791
|
+
const result = this.tick(context);
|
|
792
|
+
this.time += 1;
|
|
793
|
+
return result;
|
|
794
|
+
}
|
|
795
|
+
onInit(_context) {
|
|
796
|
+
}
|
|
797
|
+
hasMore(_context) {
|
|
798
|
+
return true;
|
|
799
|
+
}
|
|
800
|
+
baseHasNext(context) {
|
|
801
|
+
const condition = this.values.boolean("condition", this.definition.condition, context.environment, true);
|
|
802
|
+
const duration = Math.trunc(this.values.number("duration", this.definition.duration, context.environment, Number.POSITIVE_INFINITY));
|
|
803
|
+
return condition && this.time < duration;
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
var TrackedBorder = class {
|
|
807
|
+
constructor(side, source, context) {
|
|
808
|
+
this.side = side;
|
|
809
|
+
this.source = source;
|
|
810
|
+
this.previous = this.rectangle(context);
|
|
811
|
+
}
|
|
812
|
+
side;
|
|
813
|
+
source;
|
|
814
|
+
previous;
|
|
815
|
+
move(point, context) {
|
|
816
|
+
const current = this.rectangle(context);
|
|
817
|
+
const previous = this.previous;
|
|
818
|
+
this.previous = current;
|
|
819
|
+
if (!current || !previous) return point;
|
|
820
|
+
if (this.side === "left" || this.side === "right") {
|
|
821
|
+
if (previous.height === 0) return point;
|
|
822
|
+
const next2 = {
|
|
823
|
+
x: point.x + this.coordinate(current) - this.coordinate(previous),
|
|
824
|
+
y: Math.trunc((point.y - previous.y) * current.height / previous.height + current.y)
|
|
825
|
+
};
|
|
826
|
+
return Math.abs(next2.x - point.x) >= 80 || Math.abs(next2.y - point.y) >= 80 ? point : next2;
|
|
827
|
+
}
|
|
828
|
+
if (previous.width === 0) return point;
|
|
829
|
+
const next = {
|
|
830
|
+
// FloorCeiling.java performs integer division before applying the
|
|
831
|
+
// mascot's relative offset along a resized border.
|
|
832
|
+
x: (point.x - previous.x) * Math.trunc(current.width / previous.width) + current.x,
|
|
833
|
+
y: point.y + this.coordinate(current) - this.coordinate(previous)
|
|
834
|
+
};
|
|
835
|
+
return Math.abs(next.x - point.x) >= 80 || next.y - point.y > 20 || next.y - point.y < -80 ? point : next;
|
|
836
|
+
}
|
|
837
|
+
isOn(point, context) {
|
|
838
|
+
const rectangle = this.rectangle(context);
|
|
839
|
+
if (!rectangle) return false;
|
|
840
|
+
switch (this.side) {
|
|
841
|
+
case "top":
|
|
842
|
+
return isOnTop(point, rectangle);
|
|
843
|
+
case "bottom":
|
|
844
|
+
return isOnBottom(point, rectangle);
|
|
845
|
+
case "left":
|
|
846
|
+
return isOnLeft(point, rectangle);
|
|
847
|
+
case "right":
|
|
848
|
+
return isOnRight(point, rectangle);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
coordinate(rectangle) {
|
|
852
|
+
switch (this.side) {
|
|
853
|
+
case "top":
|
|
854
|
+
return rectangle.y;
|
|
855
|
+
case "bottom":
|
|
856
|
+
return rectangle.y + rectangle.height;
|
|
857
|
+
case "left":
|
|
858
|
+
return rectangle.x;
|
|
859
|
+
case "right":
|
|
860
|
+
return rectangle.x + rectangle.width;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
rectangle(context) {
|
|
864
|
+
if (this.source === "work-area") return context.bounds;
|
|
865
|
+
if (!this.source) return void 0;
|
|
866
|
+
return context.platforms.find((platform) => platform.element === this.source);
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
function selectBorder(type, state, context) {
|
|
870
|
+
if (type === "Floor") {
|
|
871
|
+
const platform2 = context.platforms.find((candidate) => isOnTop(state, candidate));
|
|
872
|
+
if (platform2) return new TrackedBorder("top", platform2.element, context);
|
|
873
|
+
if (isOnBottom(state, context.bounds)) return new TrackedBorder("bottom", "work-area", context);
|
|
874
|
+
return new TrackedBorder("bottom", void 0, context);
|
|
875
|
+
}
|
|
876
|
+
if (type === "Ceiling") {
|
|
877
|
+
const platform2 = context.platforms.find((candidate) => isOnBottom(state, candidate));
|
|
878
|
+
if (platform2) return new TrackedBorder("bottom", platform2.element, context);
|
|
879
|
+
if (isOnTop(state, context.bounds)) return new TrackedBorder("top", "work-area", context);
|
|
880
|
+
return new TrackedBorder("top", void 0, context);
|
|
881
|
+
}
|
|
882
|
+
if (state.lookRight) {
|
|
883
|
+
const platform2 = context.platforms.find((candidate) => isOnLeft(state, candidate));
|
|
884
|
+
if (platform2) return new TrackedBorder("left", platform2.element, context);
|
|
885
|
+
if (isOnRight(state, context.bounds)) return new TrackedBorder("right", "work-area", context);
|
|
886
|
+
return new TrackedBorder("right", void 0, context);
|
|
887
|
+
}
|
|
888
|
+
const platform = context.platforms.find((candidate) => isOnRight(state, candidate));
|
|
889
|
+
if (platform) return new TrackedBorder("right", platform.element, context);
|
|
890
|
+
if (isOnLeft(state, context.bounds)) return new TrackedBorder("left", "work-area", context);
|
|
891
|
+
return new TrackedBorder("left", void 0, context);
|
|
707
892
|
}
|
|
708
|
-
var
|
|
709
|
-
constructor(
|
|
710
|
-
|
|
711
|
-
this.
|
|
712
|
-
this.loop = loop;
|
|
893
|
+
var AnimatedRuntime = class extends RuntimeBase {
|
|
894
|
+
constructor(definition, state, random) {
|
|
895
|
+
super(definition, random);
|
|
896
|
+
this.state = state;
|
|
713
897
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
898
|
+
state;
|
|
899
|
+
border;
|
|
900
|
+
onInit(context) {
|
|
901
|
+
this.border = this.definition.borderType ? selectBorder(this.definition.borderType, this.state, context) : void 0;
|
|
902
|
+
}
|
|
903
|
+
animation(context, turn) {
|
|
904
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
905
|
+
return this.definition.animations?.find((animation, index) => (turn === void 0 || Boolean(animation.turn) === turn) && this.values.boolean(`animation-${index}`, animation.condition, scoped, true));
|
|
906
|
+
}
|
|
907
|
+
animationDuration(context, turn) {
|
|
908
|
+
return this.animation(context, turn)?.poses.reduce((sum, pose) => sum + Math.max(0, pose.duration), 0) ?? 0;
|
|
909
|
+
}
|
|
910
|
+
applyBorder(context) {
|
|
911
|
+
if (!this.border) return "running";
|
|
912
|
+
const moved = this.border.move(this.state, context);
|
|
913
|
+
this.state.x = moved.x;
|
|
914
|
+
this.state.y = moved.y;
|
|
915
|
+
return this.border.isOn(this.state, context) ? "running" : "lost-ground";
|
|
916
|
+
}
|
|
917
|
+
applyAnimation(context, turn) {
|
|
918
|
+
const animation = this.animation(context, turn);
|
|
919
|
+
const pose = animation && poseAt(animation, this.time);
|
|
920
|
+
if (pose) applyPose(this.state, pose);
|
|
921
|
+
}
|
|
922
|
+
scopedEnvironment(environment) {
|
|
923
|
+
const gap = this.values.number("gap", this.definition.gap, environment, 0);
|
|
924
|
+
const withGap = { ...environment, gap };
|
|
925
|
+
const targetX = this.definition.targetX === void 0 ? void 0 : this.values.number("targetX", this.definition.targetX, withGap, 0);
|
|
926
|
+
const targetY = this.definition.targetY === void 0 ? void 0 : this.values.number("targetY", this.definition.targetY, withGap, 0);
|
|
927
|
+
return { ...withGap, ...targetX !== void 0 && { targetX }, ...targetY !== void 0 && { targetY } };
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
function poseAt(animation, time) {
|
|
931
|
+
const duration = animation.poses.reduce((sum, pose) => sum + Math.max(0, pose.duration), 0);
|
|
932
|
+
if (duration <= 0) return void 0;
|
|
933
|
+
let cursor = time % duration;
|
|
934
|
+
for (const pose of animation.poses) {
|
|
935
|
+
cursor -= Math.max(0, pose.duration);
|
|
936
|
+
if (cursor < 0) return pose;
|
|
937
|
+
}
|
|
938
|
+
return animation.poses.at(-1);
|
|
939
|
+
}
|
|
940
|
+
function applyPose(state, pose) {
|
|
941
|
+
state.sprite = pose.sprite;
|
|
942
|
+
state.anchorX = pose.anchor.x;
|
|
943
|
+
state.anchorY = pose.anchor.y;
|
|
944
|
+
state.x += (state.lookRight ? -1 : 1) * pose.velocity.x;
|
|
945
|
+
state.y += pose.velocity.y;
|
|
946
|
+
}
|
|
947
|
+
var StayRuntime = class extends AnimatedRuntime {
|
|
948
|
+
tick(context) {
|
|
949
|
+
const border = this.applyBorder(context);
|
|
950
|
+
if (border === "lost-ground") return border;
|
|
951
|
+
this.applyAnimation(context);
|
|
952
|
+
return "running";
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
var AnimateRuntime = class extends StayRuntime {
|
|
956
|
+
hasMore(context) {
|
|
957
|
+
return this.time < this.animationDuration(context);
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
var MoveRuntime = class extends AnimatedRuntime {
|
|
961
|
+
turning = false;
|
|
962
|
+
hasTurningAnimation = false;
|
|
963
|
+
onInit(context) {
|
|
964
|
+
super.onInit(context);
|
|
965
|
+
this.turning = false;
|
|
966
|
+
this.hasTurningAnimation = this.definition.animations?.some((animation) => animation.turn) ?? false;
|
|
967
|
+
}
|
|
968
|
+
hasMore(context) {
|
|
969
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
970
|
+
const targetX = this.targetX(scoped);
|
|
971
|
+
const targetY = this.targetY(scoped);
|
|
972
|
+
const reached = targetX !== void 0 && this.state.x === targetX || targetY !== void 0 && this.state.y === targetY;
|
|
973
|
+
return !reached || this.turning;
|
|
974
|
+
}
|
|
975
|
+
tick(context) {
|
|
976
|
+
const border = this.applyBorder(context);
|
|
977
|
+
if (border === "lost-ground") return border;
|
|
978
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
979
|
+
const targetX = this.targetX(scoped);
|
|
980
|
+
const targetY = this.targetY(scoped);
|
|
981
|
+
let down = false;
|
|
982
|
+
if (targetX !== void 0 && this.state.x !== targetX) {
|
|
983
|
+
const nextLookRight = this.state.x < targetX;
|
|
984
|
+
this.turning = this.hasTurningAnimation && (this.turning || nextLookRight !== this.state.lookRight);
|
|
985
|
+
this.state.lookRight = nextLookRight;
|
|
732
986
|
}
|
|
733
|
-
|
|
987
|
+
if (targetY !== void 0) down = this.state.y < targetY;
|
|
988
|
+
if (this.turning && this.time >= this.animationDuration(context, true)) this.turning = false;
|
|
989
|
+
this.applyAnimation(context, this.turning);
|
|
990
|
+
if (targetX !== void 0 && (this.state.lookRight && this.state.x >= targetX || !this.state.lookRight && this.state.x <= targetX)) this.state.x = targetX;
|
|
991
|
+
if (targetY !== void 0 && (down && this.state.y >= targetY || !down && this.state.y <= targetY)) this.state.y = targetY;
|
|
992
|
+
return "running";
|
|
993
|
+
}
|
|
994
|
+
targetX(environment) {
|
|
995
|
+
return this.definition.targetX === void 0 ? void 0 : Math.trunc(this.values.number("targetX", this.definition.targetX, environment, 0));
|
|
996
|
+
}
|
|
997
|
+
targetY(environment) {
|
|
998
|
+
return this.definition.targetY === void 0 ? void 0 : Math.trunc(this.values.number("targetY", this.definition.targetY, environment, 0));
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
var MoveWithTurnRuntime = class extends MoveRuntime {
|
|
1002
|
+
onInit(context) {
|
|
1003
|
+
super.onInit(context);
|
|
1004
|
+
this.hasTurningAnimation = (this.definition.animations?.length ?? 0) >= 2;
|
|
1005
|
+
}
|
|
1006
|
+
animation(context, turn) {
|
|
1007
|
+
const animations = this.definition.animations ?? [];
|
|
1008
|
+
if (turn) return animations.at(-1);
|
|
1009
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
1010
|
+
return animations.slice(0, -1).find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, scoped, true));
|
|
734
1011
|
}
|
|
735
1012
|
};
|
|
736
|
-
var
|
|
1013
|
+
var TurnRuntime = class extends AnimatedRuntime {
|
|
1014
|
+
turning = false;
|
|
1015
|
+
hasMore(context) {
|
|
1016
|
+
const desired = this.values.boolean("lookRight", this.definition.lookRight, context.environment, !this.state.lookRight);
|
|
1017
|
+
this.turning ||= desired !== this.state.lookRight;
|
|
1018
|
+
return this.turning && this.time < this.animationDuration(context);
|
|
1019
|
+
}
|
|
1020
|
+
tick(context) {
|
|
1021
|
+
this.state.lookRight = this.values.boolean("lookRight", this.definition.lookRight, context.environment, !this.state.lookRight);
|
|
1022
|
+
const border = this.applyBorder(context);
|
|
1023
|
+
if (border === "lost-ground") return border;
|
|
1024
|
+
this.applyAnimation(context);
|
|
1025
|
+
return "running";
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
var InstantRuntime = class extends RuntimeBase {
|
|
1029
|
+
constructor(definition, state, random, operation) {
|
|
1030
|
+
super(definition, random);
|
|
1031
|
+
this.state = state;
|
|
1032
|
+
this.operation = operation;
|
|
1033
|
+
}
|
|
1034
|
+
state;
|
|
1035
|
+
operation;
|
|
1036
|
+
onInit(context) {
|
|
1037
|
+
if (!this.baseHasNext(context)) return;
|
|
1038
|
+
if (this.operation === "look") {
|
|
1039
|
+
this.state.lookRight = this.values.boolean("lookRight", this.definition.lookRight, context.environment, !this.state.lookRight);
|
|
1040
|
+
} else if (this.operation === "offset") {
|
|
1041
|
+
this.state.x += Math.trunc(this.values.number("x", this.definition.x, context.environment, 0));
|
|
1042
|
+
this.state.y += Math.trunc(this.values.number("y", this.definition.y, context.environment, 0));
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
hasMore() {
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
737
1048
|
tick() {
|
|
738
|
-
return
|
|
1049
|
+
return "running";
|
|
739
1050
|
}
|
|
740
1051
|
};
|
|
741
|
-
var
|
|
742
|
-
constructor(
|
|
743
|
-
|
|
1052
|
+
var JumpRuntime = class extends RuntimeBase {
|
|
1053
|
+
constructor(definition, state, random) {
|
|
1054
|
+
super(definition, random);
|
|
744
1055
|
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
1056
|
}
|
|
751
|
-
action;
|
|
752
1057
|
state;
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
this.
|
|
764
|
-
this.state.
|
|
765
|
-
|
|
1058
|
+
hasMore(context) {
|
|
1059
|
+
return this.distance(context).distance !== 0;
|
|
1060
|
+
}
|
|
1061
|
+
tick(context) {
|
|
1062
|
+
const { targetX, targetY, distanceX, distanceY, distance } = this.distance(context);
|
|
1063
|
+
this.state.lookRight = this.state.x < targetX;
|
|
1064
|
+
const velocity = this.values.number("velocity", this.definition.velocity, context.environment, 20);
|
|
1065
|
+
if (distance !== 0) {
|
|
1066
|
+
this.state.vx = velocity * distanceX / distance;
|
|
1067
|
+
this.state.vy = velocity * distanceY / distance;
|
|
1068
|
+
this.state.x += Math.trunc(this.state.vx);
|
|
1069
|
+
this.state.y += Math.trunc(this.state.vy);
|
|
1070
|
+
const environment = { ...context.environment, targetX, targetY };
|
|
1071
|
+
const animation = this.definition.animations?.find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, environment, true));
|
|
1072
|
+
const pose = animation && poseAt(animation, this.time);
|
|
1073
|
+
if (pose) applyPose(this.state, pose);
|
|
766
1074
|
}
|
|
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;
|
|
1075
|
+
if (distance <= velocity) {
|
|
1076
|
+
this.state.x = targetX;
|
|
1077
|
+
this.state.y = targetY;
|
|
776
1078
|
}
|
|
777
|
-
return
|
|
1079
|
+
return "running";
|
|
778
1080
|
}
|
|
779
|
-
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1081
|
+
distance(context) {
|
|
1082
|
+
const targetX = Math.trunc(this.values.number("targetX", this.definition.targetX, context.environment, 0));
|
|
1083
|
+
const targetY = Math.trunc(this.values.number("targetY", this.definition.targetY, context.environment, 0));
|
|
1084
|
+
const distanceX = targetX - this.state.x;
|
|
1085
|
+
const distanceY = targetY - this.state.y - Math.abs(distanceX) / 2;
|
|
1086
|
+
return { targetX, targetY, distanceX, distanceY, distance: Math.hypot(distanceX, distanceY) };
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
var FallRuntime = class extends RuntimeBase {
|
|
1090
|
+
constructor(definition, state, random, defaultGravity) {
|
|
1091
|
+
super(definition, random);
|
|
1092
|
+
this.state = state;
|
|
1093
|
+
this.defaultGravity = defaultGravity;
|
|
1094
|
+
}
|
|
1095
|
+
state;
|
|
1096
|
+
defaultGravity;
|
|
1097
|
+
modX = 0;
|
|
1098
|
+
modY = 0;
|
|
1099
|
+
onInit(context) {
|
|
1100
|
+
this.modX = 0;
|
|
1101
|
+
this.modY = 0;
|
|
1102
|
+
this.state.vx = Math.trunc(this.values.number("initialVx", this.definition.initialVx, context.environment, 0));
|
|
1103
|
+
this.state.vy = Math.trunc(this.values.number("initialVy", this.definition.initialVy, context.environment, 0));
|
|
1104
|
+
}
|
|
1105
|
+
hasMore(context) {
|
|
1106
|
+
return !isOnFloor(this.state, context.bounds, context.platforms) && !isOnWall(this.state, context.bounds, this.state.lookRight, context.platforms);
|
|
1107
|
+
}
|
|
1108
|
+
tick(context) {
|
|
1109
|
+
if (this.state.vx !== 0) this.state.lookRight = this.state.vx > 0;
|
|
1110
|
+
const resistanceX = this.values.number("resistanceX", this.definition.resistanceX, context.environment, 0.05);
|
|
1111
|
+
const resistanceY = this.values.number("resistanceY", this.definition.resistanceY, context.environment, 0.1);
|
|
1112
|
+
const gravity = this.values.number("gravity", this.definition.gravity, context.environment, this.defaultGravity);
|
|
1113
|
+
this.state.vx -= this.state.vx * resistanceX;
|
|
1114
|
+
this.state.vy = this.state.vy - this.state.vy * resistanceY + gravity;
|
|
1115
|
+
this.modX += this.state.vx % 1;
|
|
1116
|
+
this.modY += this.state.vy % 1;
|
|
1117
|
+
const dx = Math.trunc(this.state.vx) + Math.trunc(this.modX);
|
|
1118
|
+
const dy = Math.trunc(this.state.vy) + Math.trunc(this.modY);
|
|
1119
|
+
this.modX %= 1;
|
|
1120
|
+
this.modY %= 1;
|
|
1121
|
+
const divisions = Math.max(1, Math.abs(dx), Math.abs(dy));
|
|
1122
|
+
const start = { x: this.state.x, y: this.state.y };
|
|
1123
|
+
let stopped = false;
|
|
1124
|
+
for (let index = 0; index <= divisions; index += 1) {
|
|
1125
|
+
const x = start.x + Math.trunc(dx * index / divisions);
|
|
1126
|
+
const y = start.y + Math.trunc(dy * index / divisions);
|
|
1127
|
+
this.state.x = x;
|
|
1128
|
+
this.state.y = y;
|
|
1129
|
+
if (dy > 0) {
|
|
1130
|
+
for (let offset = -80; offset <= 0; offset += 1) {
|
|
1131
|
+
this.state.y = y + offset;
|
|
1132
|
+
if (isOnFloor(this.state, context.bounds, context.platforms)) {
|
|
1133
|
+
stopped = true;
|
|
1134
|
+
break;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
if (stopped) break;
|
|
1138
|
+
this.state.y = y;
|
|
1139
|
+
}
|
|
1140
|
+
if (isOnWall(this.state, context.bounds, this.state.lookRight, context.platforms)) break;
|
|
788
1141
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
1142
|
+
const fallEnvironment = { ...context.environment, velocityX: this.state.vx, velocityY: this.state.vy };
|
|
1143
|
+
const animation = this.definition.animations?.find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, fallEnvironment, true));
|
|
1144
|
+
const pose = animation && poseAt(animation, this.time);
|
|
1145
|
+
if (pose) applyPose(this.state, pose);
|
|
1146
|
+
return "running";
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
function matchingActivePlatform(context) {
|
|
1150
|
+
const active = context.environment.mascot.environment.activeIE;
|
|
1151
|
+
if (!active.visible) return void 0;
|
|
1152
|
+
return context.platforms.find((platform) => Math.abs(platform.x - active.x) < 1e-3 && Math.abs(platform.y - active.y) < 1e-3 && Math.abs(platform.width - active.width) < 1e-3 && Math.abs(platform.height - active.height) < 1e-3);
|
|
1153
|
+
}
|
|
1154
|
+
function carryRelation(state, platform, offsetX, offsetY) {
|
|
1155
|
+
const grip = {
|
|
1156
|
+
x: state.x + (state.lookRight ? -offsetX : offsetX),
|
|
1157
|
+
y: state.y + offsetY
|
|
1158
|
+
};
|
|
1159
|
+
return isOnBottom(grip, platform) && (state.lookRight ? isOnLeft(grip, platform) : isOnRight(grip, platform));
|
|
1160
|
+
}
|
|
1161
|
+
function carriedPlatformPosition(state, platform, offsetX, offsetY) {
|
|
1162
|
+
return state.lookRight ? { x: state.x - offsetX, y: state.y + offsetY - platform.height } : { x: state.x + offsetX - platform.width, y: state.y + offsetY - platform.height };
|
|
1163
|
+
}
|
|
1164
|
+
var CarryFallRuntime = class extends FallRuntime {
|
|
1165
|
+
constructor(definition, state, random, gravity, callbacks) {
|
|
1166
|
+
super(definition, state, random, gravity);
|
|
1167
|
+
this.callbacks = callbacks;
|
|
1168
|
+
}
|
|
1169
|
+
callbacks;
|
|
1170
|
+
element;
|
|
1171
|
+
onInit(context) {
|
|
1172
|
+
super.onInit(context);
|
|
1173
|
+
this.element = matchingActivePlatform(context)?.element;
|
|
1174
|
+
}
|
|
1175
|
+
tick(context) {
|
|
1176
|
+
const platform = context.platforms.find((candidate) => candidate.element === this.element);
|
|
1177
|
+
const offsetX = Math.trunc(this.values.number("ieOffsetX", this.definition.ieOffsetX, context.environment, 0));
|
|
1178
|
+
const offsetY = Math.trunc(this.values.number("ieOffsetY", this.definition.ieOffsetY, context.environment, 0));
|
|
1179
|
+
if (!platform || !carryRelation(this.state, platform, offsetX, offsetY)) return "lost-ground";
|
|
1180
|
+
const result = super.tick(context);
|
|
1181
|
+
this.callbacks.movePlatform?.(platform.element, carriedPlatformPosition(this.state, platform, offsetX, offsetY));
|
|
1182
|
+
return result;
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
var CarryMoveRuntime = class extends MoveRuntime {
|
|
1186
|
+
constructor(definition, state, random, callbacks) {
|
|
1187
|
+
super(definition, state, random);
|
|
1188
|
+
this.callbacks = callbacks;
|
|
1189
|
+
}
|
|
1190
|
+
callbacks;
|
|
1191
|
+
element;
|
|
1192
|
+
onInit(context) {
|
|
1193
|
+
super.onInit(context);
|
|
1194
|
+
this.element = matchingActivePlatform(context)?.element;
|
|
1195
|
+
}
|
|
1196
|
+
tick(context) {
|
|
1197
|
+
const platform = context.platforms.find((candidate) => candidate.element === this.element);
|
|
1198
|
+
const offsetX = Math.trunc(this.values.number("ieOffsetX", this.definition.ieOffsetX, context.environment, 0));
|
|
1199
|
+
const offsetY = Math.trunc(this.values.number("ieOffsetY", this.definition.ieOffsetY, context.environment, 0));
|
|
1200
|
+
if (!platform || !carryRelation(this.state, platform, offsetX, offsetY)) return "lost-ground";
|
|
1201
|
+
const result = super.tick(context);
|
|
1202
|
+
this.callbacks.movePlatform?.(platform.element, carriedPlatformPosition(this.state, platform, offsetX, offsetY));
|
|
1203
|
+
return result;
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
var ThrowPlatformRuntime = class extends AnimateRuntime {
|
|
1207
|
+
constructor(definition, state, random, callbacks) {
|
|
1208
|
+
super(definition, state, random);
|
|
1209
|
+
this.callbacks = callbacks;
|
|
1210
|
+
}
|
|
1211
|
+
callbacks;
|
|
1212
|
+
element;
|
|
1213
|
+
onInit(context) {
|
|
1214
|
+
super.onInit(context);
|
|
1215
|
+
this.element = matchingActivePlatform(context)?.element;
|
|
1216
|
+
}
|
|
1217
|
+
tick(context) {
|
|
1218
|
+
const result = super.tick(context);
|
|
1219
|
+
const platform = context.platforms.find((candidate) => candidate.element === this.element);
|
|
1220
|
+
if (platform) {
|
|
1221
|
+
const vx = Math.trunc(this.values.number("initialVx", this.definition.initialVx, context.environment, 32));
|
|
1222
|
+
const vy = Math.trunc(this.values.number("initialVy", this.definition.initialVy, context.environment, -10));
|
|
1223
|
+
const gravity = this.values.number("gravity", this.definition.gravity, context.environment, 0.5);
|
|
1224
|
+
this.callbacks.movePlatform?.(platform.element, {
|
|
1225
|
+
x: platform.x + (this.state.lookRight ? vx : -vx),
|
|
1226
|
+
y: platform.y + vy + Math.trunc(this.time * gravity)
|
|
1227
|
+
});
|
|
792
1228
|
}
|
|
1229
|
+
return result;
|
|
793
1230
|
}
|
|
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();
|
|
1231
|
+
};
|
|
1232
|
+
function breed(definition, state, values, context, callbacks) {
|
|
1233
|
+
const bornX = Math.trunc(values.number("bornX", definition.bornX, context.environment, 0));
|
|
1234
|
+
const bornY = Math.trunc(values.number("bornY", definition.bornY, context.environment, 0));
|
|
1235
|
+
const count = Math.trunc(values.number("bornCount", definition.bornCount, context.environment, 1));
|
|
1236
|
+
if (count < 1) throw new RangeError("BornCount must be positive");
|
|
1237
|
+
for (let index = 0; index < count; index += 1) {
|
|
1238
|
+
callbacks.spawn({
|
|
1239
|
+
x: state.x + (state.lookRight ? -bornX : bornX),
|
|
1240
|
+
y: state.y + bornY,
|
|
1241
|
+
lookRight: state.lookRight,
|
|
1242
|
+
...definition.bornBehavior && { behaviorName: definition.bornBehavior }
|
|
1243
|
+
}, definition.bornMascot);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
var BreedRuntime = class extends AnimateRuntime {
|
|
1247
|
+
constructor(definition, state, random, callbacks) {
|
|
1248
|
+
super(definition, state, random);
|
|
1249
|
+
this.callbacks = callbacks;
|
|
1250
|
+
}
|
|
1251
|
+
callbacks;
|
|
1252
|
+
spawned = false;
|
|
1253
|
+
tick(context) {
|
|
1254
|
+
const result = super.tick(context);
|
|
1255
|
+
if (result === "lost-ground") return result;
|
|
1256
|
+
const duration = this.animationDuration(context);
|
|
1257
|
+
if (!this.spawned && this.time === duration - 1) {
|
|
1258
|
+
this.spawned = true;
|
|
1259
|
+
breed(this.definition, this.state, this.values, context, this.callbacks);
|
|
834
1260
|
}
|
|
1261
|
+
return result;
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
var BreedMoveRuntime = class extends MoveRuntime {
|
|
1265
|
+
constructor(definition, state, random, callbacks) {
|
|
1266
|
+
super(definition, state, random);
|
|
1267
|
+
this.callbacks = callbacks;
|
|
1268
|
+
}
|
|
1269
|
+
callbacks;
|
|
1270
|
+
tick(context) {
|
|
1271
|
+
const result = super.tick(context);
|
|
1272
|
+
if (result === "lost-ground") return result;
|
|
1273
|
+
const interval = Math.trunc(this.values.number("bornInterval", this.definition.bornInterval, context.environment, 1));
|
|
1274
|
+
if (interval < 1) throw new RangeError("BornInterval must be positive");
|
|
1275
|
+
if (this.time % interval === 0 && !this.turning) breed(this.definition, this.state, this.values, context, this.callbacks);
|
|
1276
|
+
return result;
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
var BreedJumpRuntime = class extends JumpRuntime {
|
|
1280
|
+
constructor(definition, breedState, random, callbacks) {
|
|
1281
|
+
super(definition, breedState, random);
|
|
1282
|
+
this.breedState = breedState;
|
|
1283
|
+
this.callbacks = callbacks;
|
|
835
1284
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1285
|
+
breedState;
|
|
1286
|
+
callbacks;
|
|
1287
|
+
tick(context) {
|
|
1288
|
+
const result = super.tick(context);
|
|
1289
|
+
const interval = Math.trunc(this.values.number("bornInterval", this.definition.bornInterval, context.environment, 1));
|
|
1290
|
+
if (interval < 1) throw new RangeError("BornInterval must be positive");
|
|
1291
|
+
if (this.time % interval === 0) breed(this.definition, this.breedState, this.values, context, this.callbacks);
|
|
1292
|
+
return result;
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
var DraggedRuntime = class extends RuntimeBase {
|
|
1296
|
+
constructor(definition, state, random, spec) {
|
|
1297
|
+
super(definition, random);
|
|
1298
|
+
this.state = state;
|
|
1299
|
+
this.spec = spec;
|
|
1300
|
+
}
|
|
1301
|
+
state;
|
|
1302
|
+
spec;
|
|
1303
|
+
footX = 0;
|
|
1304
|
+
footDx = 0;
|
|
1305
|
+
timeToRegist = 250;
|
|
1306
|
+
onInit(context) {
|
|
1307
|
+
this.footDx = 0;
|
|
1308
|
+
this.timeToRegist = 250;
|
|
1309
|
+
this.footX = context.environment.mascot.environment.cursor.x + this.offsetX(context);
|
|
1310
|
+
}
|
|
1311
|
+
hasMore() {
|
|
1312
|
+
return this.time < this.timeToRegist;
|
|
1313
|
+
}
|
|
1314
|
+
tick(context) {
|
|
1315
|
+
this.state.lookRight = false;
|
|
1316
|
+
this.state.dragging = true;
|
|
1317
|
+
const cursor = context.environment.mascot.environment.cursor;
|
|
1318
|
+
const offsetX = this.offsetX(context);
|
|
1319
|
+
const offsetY = this.offsetY(context);
|
|
1320
|
+
if (Math.abs(cursor.x - this.state.x + offsetX) >= 5) this.time = 0;
|
|
1321
|
+
this.footDx = (this.footDx + (cursor.x - this.footX) * 0.1) * 0.8;
|
|
1322
|
+
this.footX += this.footDx;
|
|
1323
|
+
const environment = { ...context.environment, footX: this.footX };
|
|
1324
|
+
const animation = this.definition.animations?.find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, environment, true));
|
|
1325
|
+
const pose = animation && poseAt(animation, this.time);
|
|
1326
|
+
if (pose) applyPose(this.state, pose);
|
|
1327
|
+
this.state.x = cursor.x + offsetX;
|
|
1328
|
+
this.state.y = cursor.y + offsetY;
|
|
1329
|
+
if (this.time === this.timeToRegist - 1 && this.values.number(`regist-${this.time}`, "#{Math.random()}", environment, 0) >= 0.1) this.timeToRegist += 1;
|
|
1330
|
+
return "running";
|
|
1331
|
+
}
|
|
1332
|
+
offsetX(context) {
|
|
1333
|
+
const offset = Math.trunc(this.values.number("offsetX", this.definition.offsetX, context.environment, 0));
|
|
1334
|
+
return this.definition.offsetType === "Origin" ? -offset + this.spriteCenter().x : offset;
|
|
1335
|
+
}
|
|
1336
|
+
offsetY(context) {
|
|
1337
|
+
const offset = Math.trunc(this.values.number("offsetY", this.definition.offsetY, context.environment, 120));
|
|
1338
|
+
return this.definition.offsetType === "Origin" ? -offset + this.spriteCenter().y : offset;
|
|
1339
|
+
}
|
|
1340
|
+
spriteCenter() {
|
|
1341
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1342
|
+
const width = typeof sprite === "object" ? sprite.width ?? 128 : 128;
|
|
1343
|
+
return { x: this.state.lookRight ? width - this.state.anchorX : this.state.anchorX, y: this.state.anchorY };
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
var RegistRuntime = class extends AnimatedRuntime {
|
|
1347
|
+
constructor(definition, state, random, spec) {
|
|
1348
|
+
super(definition, state, random);
|
|
1349
|
+
this.spec = spec;
|
|
1350
|
+
}
|
|
1351
|
+
spec;
|
|
1352
|
+
hasMore(context) {
|
|
1353
|
+
const cursor = context.environment.mascot.environment.cursor;
|
|
1354
|
+
const rawOffset = Math.trunc(this.values.number("offsetX", this.definition.offsetX, context.environment, 0));
|
|
1355
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1356
|
+
const width = typeof sprite === "object" ? sprite.width ?? 128 : 128;
|
|
1357
|
+
const centerX = this.state.lookRight ? width - this.state.anchorX : this.state.anchorX;
|
|
1358
|
+
const offsetX = this.definition.offsetType === "Origin" ? -rawOffset + centerX : rawOffset;
|
|
1359
|
+
return Math.abs(cursor.x - this.state.x + offsetX) < 5;
|
|
1360
|
+
}
|
|
1361
|
+
tick(context) {
|
|
1362
|
+
this.state.dragging = true;
|
|
1363
|
+
this.applyAnimation(context);
|
|
1364
|
+
if (this.time + 1 >= this.animationDuration(context)) {
|
|
1365
|
+
this.state.lookRight = this.values.number(`look-${this.time}`, "#{Math.random()}", context.environment, 0) < 0.5;
|
|
1366
|
+
return "lost-ground";
|
|
842
1367
|
}
|
|
843
|
-
return
|
|
1368
|
+
return "running";
|
|
844
1369
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
1370
|
+
};
|
|
1371
|
+
var SelfDestructRuntime = class extends AnimateRuntime {
|
|
1372
|
+
constructor(definition, state, random, callbacks) {
|
|
1373
|
+
super(definition, state, random);
|
|
1374
|
+
this.callbacks = callbacks;
|
|
1375
|
+
}
|
|
1376
|
+
callbacks;
|
|
1377
|
+
tick(context) {
|
|
1378
|
+
const result = super.tick(context);
|
|
1379
|
+
if (this.time === this.animationDuration(context) - 1) this.callbacks.remove();
|
|
1380
|
+
return result;
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
var ComplexRuntime = class extends RuntimeBase {
|
|
1384
|
+
constructor(definition, random, factory, selectOnly) {
|
|
1385
|
+
super(definition, random);
|
|
1386
|
+
this.factory = factory;
|
|
1387
|
+
this.selectOnly = selectOnly;
|
|
852
1388
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1389
|
+
factory;
|
|
1390
|
+
selectOnly;
|
|
1391
|
+
index = 0;
|
|
1392
|
+
child;
|
|
1393
|
+
selectionMade = false;
|
|
1394
|
+
onInit(context) {
|
|
1395
|
+
this.index = 0;
|
|
1396
|
+
this.child = void 0;
|
|
1397
|
+
this.selectionMade = false;
|
|
1398
|
+
if (this.baseHasNext(context)) this.seek(context);
|
|
1399
|
+
}
|
|
1400
|
+
hasMore(context) {
|
|
1401
|
+
if (!this.selectOnly) this.seek(context);
|
|
1402
|
+
return this.child?.hasNext(context) ?? false;
|
|
1403
|
+
}
|
|
1404
|
+
tick(context) {
|
|
1405
|
+
return this.child?.hasNext(context) ? this.child.step(context) : "running";
|
|
1406
|
+
}
|
|
1407
|
+
seek(context) {
|
|
1408
|
+
const definitions = this.definition.actions ?? [];
|
|
1409
|
+
if (definitions.length === 0) return;
|
|
1410
|
+
for (let guard = 0; guard <= definitions.length; guard += 1) {
|
|
1411
|
+
if (this.child?.hasNext(context)) {
|
|
1412
|
+
this.selectionMade = true;
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
if (this.selectOnly && this.selectionMade) {
|
|
1416
|
+
this.child = void 0;
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
if (this.index >= definitions.length) {
|
|
1420
|
+
if (this.definition.loop !== true) {
|
|
1421
|
+
this.child = void 0;
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
this.index = 0;
|
|
1425
|
+
}
|
|
1426
|
+
const definition = definitions[this.index++];
|
|
1427
|
+
if (!definition) {
|
|
1428
|
+
this.child = void 0;
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
this.child = this.factory(definition, context);
|
|
1432
|
+
this.child.init(context);
|
|
1433
|
+
}
|
|
1434
|
+
this.child = void 0;
|
|
856
1435
|
}
|
|
857
1436
|
};
|
|
858
1437
|
var ActionExecutor = class {
|
|
859
|
-
/** Creates an executor bound to one mascot's mutable internal state. */
|
|
860
1438
|
constructor(spec, state, options, callbacks) {
|
|
861
1439
|
this.spec = spec;
|
|
862
1440
|
this.state = state;
|
|
863
1441
|
this.options = options;
|
|
864
1442
|
this.callbacks = callbacks;
|
|
1443
|
+
this.random = options.random ?? Math.random;
|
|
865
1444
|
}
|
|
866
1445
|
spec;
|
|
867
1446
|
state;
|
|
868
1447
|
options;
|
|
869
1448
|
callbacks;
|
|
870
1449
|
runtime;
|
|
1450
|
+
accumulator = 0;
|
|
1451
|
+
random;
|
|
871
1452
|
/** Starts the action whose name matches a selected behavior. */
|
|
872
|
-
start(actionName, environment) {
|
|
1453
|
+
start(actionName, environment, _preserveLookRight = false, bounds = environment.mascot.environment.workArea, platforms = []) {
|
|
873
1454
|
const definition = this.spec.actions.find((action) => action.name === actionName);
|
|
874
|
-
this.
|
|
875
|
-
|
|
1455
|
+
this.accumulator = 0;
|
|
1456
|
+
if (!definition) {
|
|
1457
|
+
this.runtime = void 0;
|
|
1458
|
+
return false;
|
|
1459
|
+
}
|
|
1460
|
+
const context = { environment, bounds, platforms };
|
|
1461
|
+
this.runtime = this.createRuntime(definition, context, /* @__PURE__ */ new Set());
|
|
1462
|
+
this.runtime.init(context);
|
|
1463
|
+
return true;
|
|
876
1464
|
}
|
|
877
|
-
/** Advances
|
|
878
|
-
tick(deltaMs, environment, bounds) {
|
|
879
|
-
|
|
1465
|
+
/** Advances by elapsed milliseconds and reports completion. */
|
|
1466
|
+
tick(deltaMs, environment, bounds, platforms = []) {
|
|
1467
|
+
this.accumulator += Math.max(0, deltaMs);
|
|
1468
|
+
let result = this.runtime?.hasNext({ environment, bounds, platforms }) ? "running" : "complete";
|
|
1469
|
+
while (this.accumulator >= this.options.frameDuration && result === "running") {
|
|
1470
|
+
this.accumulator -= this.options.frameDuration;
|
|
1471
|
+
result = this.step(environment, bounds, platforms);
|
|
1472
|
+
}
|
|
1473
|
+
return result !== "running";
|
|
1474
|
+
}
|
|
1475
|
+
/** Advances exactly one legacy frame. */
|
|
1476
|
+
step(environment, bounds, platforms = []) {
|
|
1477
|
+
const context = { environment, bounds, platforms };
|
|
1478
|
+
if (!this.runtime?.hasNext(context)) return "complete";
|
|
1479
|
+
const result = this.runtime.step(context);
|
|
1480
|
+
if (result === "lost-ground") return result;
|
|
1481
|
+
return this.runtime.hasNext(context) ? "running" : "complete";
|
|
1482
|
+
}
|
|
1483
|
+
/** Returns whether the current action can execute another legacy frame. */
|
|
1484
|
+
hasNext(environment, bounds, platforms = []) {
|
|
1485
|
+
return this.runtime?.hasNext({ environment, bounds, platforms }) ?? false;
|
|
1486
|
+
}
|
|
1487
|
+
/** Retained for source compatibility with the previous collision API. */
|
|
1488
|
+
consumeViewportWallCollision() {
|
|
1489
|
+
return false;
|
|
880
1490
|
}
|
|
881
1491
|
/** Cancels the current action tree. */
|
|
882
1492
|
cancel() {
|
|
883
1493
|
this.runtime = void 0;
|
|
1494
|
+
this.accumulator = 0;
|
|
884
1495
|
}
|
|
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();
|
|
1496
|
+
createRuntime(definition, context, references) {
|
|
890
1497
|
if (definition.type === "Reference") {
|
|
891
|
-
if (!definition.name || references.has(definition.name)) return new
|
|
1498
|
+
if (!definition.name || references.has(definition.name)) return new InstantRuntime(definition, this.state, this.random, "noop");
|
|
892
1499
|
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);
|
|
1500
|
+
if (!referenced) return new InstantRuntime(definition, this.state, this.random, "noop");
|
|
1501
|
+
return this.createRuntime({ ...referenced, ...definition, type: referenced.type, name: definition.name }, context, new Set(references).add(definition.name));
|
|
896
1502
|
}
|
|
897
|
-
if (definition.type === "Sequence") {
|
|
898
|
-
return new
|
|
1503
|
+
if (definition.type === "Sequence" || definition.type === "Select") {
|
|
1504
|
+
return new ComplexRuntime(definition, this.random, (child, nextContext) => this.createRuntime(child, nextContext, new Set(references)), definition.type === "Select");
|
|
899
1505
|
}
|
|
900
|
-
if (definition.type === "
|
|
901
|
-
|
|
902
|
-
|
|
1506
|
+
if (definition.type === "Stay") return new StayRuntime(definition, this.state, this.random);
|
|
1507
|
+
if (definition.type === "Animate") return new AnimateRuntime(definition, this.state, this.random);
|
|
1508
|
+
if (definition.type === "Move") return new MoveRuntime(definition, this.state, this.random);
|
|
1509
|
+
switch (definition.embedType) {
|
|
1510
|
+
case "Fall":
|
|
1511
|
+
return new FallRuntime(definition, this.state, this.random, this.options.gravity);
|
|
1512
|
+
case "FallWithIE":
|
|
1513
|
+
return new CarryFallRuntime(definition, this.state, this.random, this.options.gravity, this.callbacks);
|
|
1514
|
+
case "Jump":
|
|
1515
|
+
case "ComplexJump":
|
|
1516
|
+
case "ScanJump":
|
|
1517
|
+
case "BroadcastJump":
|
|
1518
|
+
return new JumpRuntime(definition, this.state, this.random);
|
|
1519
|
+
case "WalkWithIE":
|
|
1520
|
+
return new CarryMoveRuntime(definition, this.state, this.random, this.callbacks);
|
|
1521
|
+
case "MoveWithTurn":
|
|
1522
|
+
return new MoveWithTurnRuntime(definition, this.state, this.random);
|
|
1523
|
+
case "ComplexMove":
|
|
1524
|
+
case "ScanMove":
|
|
1525
|
+
case "BroadcastMove":
|
|
1526
|
+
return new MoveRuntime(definition, this.state, this.random);
|
|
1527
|
+
case "Turn":
|
|
1528
|
+
return new TurnRuntime(definition, this.state, this.random);
|
|
1529
|
+
case "Look":
|
|
1530
|
+
return new InstantRuntime(definition, this.state, this.random, "look");
|
|
1531
|
+
case "Offset":
|
|
1532
|
+
return new InstantRuntime(definition, this.state, this.random, "offset");
|
|
1533
|
+
case "Mute":
|
|
1534
|
+
case "Reboot":
|
|
1535
|
+
return new InstantRuntime(definition, this.state, this.random, "noop");
|
|
1536
|
+
case "Breed":
|
|
1537
|
+
return new BreedRuntime(definition, this.state, this.random, this.callbacks);
|
|
1538
|
+
case "BreedMove":
|
|
1539
|
+
return new BreedMoveRuntime(definition, this.state, this.random, this.callbacks);
|
|
1540
|
+
case "BreedJump":
|
|
1541
|
+
return new BreedJumpRuntime(definition, this.state, this.random, this.callbacks);
|
|
1542
|
+
case "ThrowIE":
|
|
1543
|
+
return new ThrowPlatformRuntime(definition, this.state, this.random, this.callbacks);
|
|
1544
|
+
case "SelfDestruct":
|
|
1545
|
+
case "Exit":
|
|
1546
|
+
return new SelfDestructRuntime(definition, this.state, this.random, this.callbacks);
|
|
1547
|
+
case "Dragged":
|
|
1548
|
+
return new DraggedRuntime(definition, this.state, this.random, this.spec);
|
|
1549
|
+
case "Regist":
|
|
1550
|
+
return new RegistRuntime(definition, this.state, this.random, this.spec);
|
|
1551
|
+
case "Broadcast":
|
|
1552
|
+
case "Interact":
|
|
1553
|
+
case "ScanInteract":
|
|
1554
|
+
case "Transform":
|
|
1555
|
+
return new AnimateRuntime(definition, this.state, this.random);
|
|
1556
|
+
case "BroadcastStay":
|
|
1557
|
+
return new StayRuntime(definition, this.state, this.random);
|
|
1558
|
+
default:
|
|
1559
|
+
return new StayRuntime(definition, this.state, this.random);
|
|
903
1560
|
}
|
|
904
|
-
return new LeafRuntime(evaluateAction(definition, environment), this.state, this.options, this.callbacks, environment);
|
|
905
1561
|
}
|
|
906
1562
|
};
|
|
907
1563
|
|
|
@@ -930,7 +1586,6 @@ function environmentRectangle(bounds) {
|
|
|
930
1586
|
};
|
|
931
1587
|
}
|
|
932
1588
|
var PLATFORM_NEARBY_DISTANCE = 400;
|
|
933
|
-
var PLATFORM_EDGE_TOLERANCE = 16;
|
|
934
1589
|
function distanceToRectangle(point, rectangle) {
|
|
935
1590
|
const dx = Math.max(rectangle.x - point.x, 0, point.x - rectangle.x - rectangle.width);
|
|
936
1591
|
const dy = Math.max(rectangle.y - point.y, 0, point.y - rectangle.y - rectangle.height);
|
|
@@ -959,10 +1614,13 @@ var Mascot = class {
|
|
|
959
1614
|
dragging: false
|
|
960
1615
|
};
|
|
961
1616
|
this.domHandle = dom.createMascot(spec, id, options.mascotClassName || void 0);
|
|
1617
|
+
this.frameDuration = options.frameDuration;
|
|
1618
|
+
this.random = options.random ?? Math.random;
|
|
962
1619
|
this.behavior = new BehaviorController(spec, options.random);
|
|
963
1620
|
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)
|
|
1621
|
+
spawn: (position, characterId) => this.callbacks.spawn(characterId ?? this.spec.id, position),
|
|
1622
|
+
remove: () => this.callbacks.remove(this.id),
|
|
1623
|
+
...this.callbacks.movePlatform && { movePlatform: this.callbacks.movePlatform }
|
|
966
1624
|
});
|
|
967
1625
|
this.installPointerHandlers();
|
|
968
1626
|
}
|
|
@@ -984,44 +1642,19 @@ var Mascot = class {
|
|
|
984
1642
|
lastPointer;
|
|
985
1643
|
activePlatformElement;
|
|
986
1644
|
platforms = [];
|
|
1645
|
+
accumulatedMs = 0;
|
|
1646
|
+
frameDuration;
|
|
1647
|
+
random;
|
|
987
1648
|
/** Advances behavior, animation, physics, and rendering by one clock tick. */
|
|
988
1649
|
tick(deltaMs, bounds, platforms = []) {
|
|
989
1650
|
if (this.destroyed) return;
|
|
990
1651
|
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
1652
|
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;
|
|
1653
|
+
this.ensureBehavior(bounds, platforms, true);
|
|
1654
|
+
this.accumulatedMs += Math.max(0, deltaMs);
|
|
1655
|
+
while (this.accumulatedMs >= this.frameDuration && !this.destroyed) {
|
|
1656
|
+
this.accumulatedMs -= this.frameDuration;
|
|
1657
|
+
this.legacyTick(bounds, platforms);
|
|
1025
1658
|
}
|
|
1026
1659
|
} catch (error) {
|
|
1027
1660
|
this.callbacks.error(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -1042,14 +1675,68 @@ var Mascot = class {
|
|
|
1042
1675
|
for (const dispose of this.disposers.splice(0)) dispose();
|
|
1043
1676
|
this.dom.removeMascot(this.domHandle);
|
|
1044
1677
|
}
|
|
1045
|
-
startBehavior(environment) {
|
|
1678
|
+
startBehavior(environment, bounds, platforms) {
|
|
1046
1679
|
if (!this.currentBehavior) return false;
|
|
1047
1680
|
this.state.behaviorName = this.currentBehavior.name;
|
|
1048
|
-
return this.actions.start(this.currentBehavior.name, environment);
|
|
1681
|
+
return this.actions.start(this.currentBehavior.actionName ?? this.currentBehavior.name, environment, false, bounds, platforms);
|
|
1682
|
+
}
|
|
1683
|
+
ensureBehavior(bounds, platforms, initial) {
|
|
1684
|
+
for (let guard = 0; guard < 32 && !this.destroyed; guard += 1) {
|
|
1685
|
+
const environment = this.createEnvironment(bounds, platforms);
|
|
1686
|
+
if (!this.currentBehavior) {
|
|
1687
|
+
this.currentBehavior = initial ? this.behavior.selectInitial(environment, this.state.behaviorName) : this.selectNextBehavior(environment, bounds);
|
|
1688
|
+
initial = false;
|
|
1689
|
+
if (!this.currentBehavior) this.currentBehavior = this.findFallBehavior();
|
|
1690
|
+
if (!this.currentBehavior || !this.startBehavior(environment, bounds, platforms)) return;
|
|
1691
|
+
}
|
|
1692
|
+
if (this.actions.hasNext(environment, bounds, platforms)) return;
|
|
1693
|
+
this.currentBehavior = this.selectNextBehavior(environment, bounds) ?? this.findFallBehavior();
|
|
1694
|
+
if (!this.currentBehavior) return;
|
|
1695
|
+
if (!this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms)) return;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
legacyTick(bounds, platforms) {
|
|
1699
|
+
this.ensureBehavior(bounds, platforms, false);
|
|
1700
|
+
if (!this.currentBehavior) return;
|
|
1701
|
+
const result = this.actions.step(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1702
|
+
if (this.destroyed) return;
|
|
1703
|
+
if (result === "lost-ground") {
|
|
1704
|
+
this.state.dragging = false;
|
|
1705
|
+
this.actions.cancel();
|
|
1706
|
+
this.currentBehavior = this.findFallBehavior();
|
|
1707
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1708
|
+
} else if (result === "complete") {
|
|
1709
|
+
this.currentBehavior = this.selectNextBehavior(this.createEnvironment(bounds, platforms), bounds);
|
|
1710
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1711
|
+
this.ensureBehavior(bounds, platforms, false);
|
|
1712
|
+
} else if (this.isOutsideVisibleBounds(bounds)) {
|
|
1713
|
+
this.state.x = Math.trunc(bounds.x + this.random() * bounds.width);
|
|
1714
|
+
this.state.y = bounds.y - 256;
|
|
1715
|
+
this.actions.cancel();
|
|
1716
|
+
this.currentBehavior = this.findFallBehavior();
|
|
1717
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
isOutsideVisibleBounds(bounds) {
|
|
1721
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1722
|
+
const width = typeof sprite === "object" && "width" in sprite && sprite.width !== void 0 ? sprite.width : 128;
|
|
1723
|
+
const height = typeof sprite === "object" && "height" in sprite && sprite.height !== void 0 ? sprite.height : 128;
|
|
1724
|
+
const anchorX = this.state.lookRight ? width - this.state.anchorX : this.state.anchorX;
|
|
1725
|
+
const left = this.state.x - anchorX;
|
|
1726
|
+
const top = this.state.y - this.state.anchorY;
|
|
1727
|
+
return left + width <= bounds.x || bounds.x + bounds.width <= left || bounds.y + bounds.height <= top;
|
|
1049
1728
|
}
|
|
1050
1729
|
findFallBehavior() {
|
|
1051
1730
|
return this.behavior.force("Fall") ?? this.behavior.force("\u843D\u4E0B\u3059\u308B");
|
|
1052
1731
|
}
|
|
1732
|
+
selectNextBehavior(environment, bounds) {
|
|
1733
|
+
const selected = this.behavior.selectNext(environment);
|
|
1734
|
+
if (this.behavior.usedFallback()) {
|
|
1735
|
+
this.state.x = Math.trunc(bounds.x + this.random() * bounds.width);
|
|
1736
|
+
this.state.y = bounds.y - 256;
|
|
1737
|
+
}
|
|
1738
|
+
return selected;
|
|
1739
|
+
}
|
|
1053
1740
|
createEnvironment(bounds, platforms = this.platforms) {
|
|
1054
1741
|
const workArea = environmentRectangle(bounds);
|
|
1055
1742
|
const inactive = environmentRectangle({ x: -100, y: -100, width: 0, height: 0 });
|
|
@@ -1064,10 +1751,10 @@ var Mascot = class {
|
|
|
1064
1751
|
lookRight: this.state.lookRight,
|
|
1065
1752
|
environment: {
|
|
1066
1753
|
cursor: this.callbacks.pointer(),
|
|
1067
|
-
screen:
|
|
1754
|
+
screen: workArea,
|
|
1068
1755
|
workArea,
|
|
1069
|
-
floor:
|
|
1070
|
-
ceiling:
|
|
1756
|
+
floor: edge((point) => isOnFloor(point, bounds, platforms)),
|
|
1757
|
+
ceiling: edge((point) => isOnTop(point, bounds) || platforms.some((candidate) => isOnBottom(point, candidate))),
|
|
1071
1758
|
activeIE
|
|
1072
1759
|
}
|
|
1073
1760
|
}
|
|
@@ -1094,6 +1781,7 @@ var Mascot = class {
|
|
|
1094
1781
|
}
|
|
1095
1782
|
installPointerHandlers() {
|
|
1096
1783
|
const element = this.domHandle.spriteElement;
|
|
1784
|
+
const document2 = element.ownerDocument;
|
|
1097
1785
|
const listen = (target, type, listener) => {
|
|
1098
1786
|
target.addEventListener(type, listener);
|
|
1099
1787
|
this.disposers.push(() => target.removeEventListener(type, listener));
|
|
@@ -1102,29 +1790,29 @@ var Mascot = class {
|
|
|
1102
1790
|
const pointerEvent = event;
|
|
1103
1791
|
if (pointerEvent.button !== 0) return;
|
|
1104
1792
|
event.preventDefault();
|
|
1105
|
-
const point =
|
|
1793
|
+
const point = this.dom.toLocalPoint(pointerEvent.clientX, pointerEvent.clientY);
|
|
1106
1794
|
this.pointerId = pointerEvent.pointerId;
|
|
1107
1795
|
this.pointerDown = point;
|
|
1108
1796
|
this.lastPointer = point;
|
|
1109
1797
|
this.dragOffset = { x: this.state.x - point.x, y: this.state.y - point.y };
|
|
1110
1798
|
this.state.dragging = true;
|
|
1111
|
-
this.actions.cancel();
|
|
1112
1799
|
this.currentBehavior = this.behavior.force("Dragged") ?? this.behavior.force("\u30C9\u30E9\u30C3\u30B0\u3055\u308C\u308B");
|
|
1113
|
-
if (this.currentBehavior) this.
|
|
1800
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(this.dom.getBounds()), this.dom.getBounds(), this.platforms);
|
|
1114
1801
|
element.setPointerCapture?.(pointerEvent.pointerId);
|
|
1115
1802
|
});
|
|
1116
|
-
listen(
|
|
1803
|
+
listen(document2, "pointermove", (event) => {
|
|
1117
1804
|
const pointerEvent = event;
|
|
1118
1805
|
if (!this.state.dragging || pointerEvent.pointerId !== this.pointerId) return;
|
|
1119
|
-
const point =
|
|
1806
|
+
const point = this.dom.toLocalPoint(pointerEvent.clientX, pointerEvent.clientY);
|
|
1120
1807
|
const previous = this.lastPointer ?? point;
|
|
1808
|
+
const bounds = this.dom.getBounds();
|
|
1121
1809
|
this.state.vx = (point.x - previous.x) * 0.8;
|
|
1122
1810
|
this.state.vy = (point.y - previous.y) * 0.8;
|
|
1123
1811
|
this.state.x = point.x + this.dragOffset.x;
|
|
1124
1812
|
this.state.y = point.y + this.dragOffset.y;
|
|
1125
1813
|
this.lastPointer = point;
|
|
1126
1814
|
});
|
|
1127
|
-
listen(
|
|
1815
|
+
listen(document2, "pointerup", (event) => {
|
|
1128
1816
|
const pointerEvent = event;
|
|
1129
1817
|
if (!this.state.dragging || pointerEvent.pointerId !== this.pointerId) return;
|
|
1130
1818
|
this.state.dragging = false;
|
|
@@ -1132,8 +1820,8 @@ var Mascot = class {
|
|
|
1132
1820
|
this.pointerId = void 0;
|
|
1133
1821
|
this.currentBehavior = this.behavior.force("Thrown") ?? this.behavior.force("\u6295\u3052\u3089\u308C\u308B") ?? this.findFallBehavior();
|
|
1134
1822
|
if (this.currentBehavior) {
|
|
1135
|
-
|
|
1136
|
-
this.
|
|
1823
|
+
const bounds = this.dom.getBounds();
|
|
1824
|
+
this.startBehavior(this.createEnvironment(bounds), bounds, this.platforms);
|
|
1137
1825
|
}
|
|
1138
1826
|
if (moved < 4) this.callbacks.click(this.snapshot());
|
|
1139
1827
|
});
|
|
@@ -1141,21 +1829,23 @@ var Mascot = class {
|
|
|
1141
1829
|
};
|
|
1142
1830
|
|
|
1143
1831
|
// src/platform.ts
|
|
1144
|
-
function resolvePlatformElements(source,
|
|
1832
|
+
function resolvePlatformElements(source, root, excludedRoot) {
|
|
1145
1833
|
let elements;
|
|
1146
1834
|
if (typeof source === "string") {
|
|
1147
1835
|
try {
|
|
1148
|
-
elements = [...
|
|
1836
|
+
elements = [...root.querySelectorAll(source)];
|
|
1149
1837
|
} catch {
|
|
1150
1838
|
return [];
|
|
1151
1839
|
}
|
|
1152
1840
|
} else {
|
|
1153
1841
|
elements = source;
|
|
1154
1842
|
}
|
|
1843
|
+
const document2 = root.nodeType === 9 ? root : root.ownerDocument;
|
|
1844
|
+
if (!document2) return [];
|
|
1155
1845
|
const HTMLElementConstructor = document2.defaultView?.HTMLElement;
|
|
1156
1846
|
if (!HTMLElementConstructor) return [];
|
|
1157
1847
|
return [...new Set(elements)].filter(
|
|
1158
|
-
(element) => element instanceof HTMLElementConstructor && element.isConnected && (!excludedRoot || !excludedRoot.contains(element))
|
|
1848
|
+
(element) => element instanceof HTMLElementConstructor && element.isConnected && root.contains(element) && (!excludedRoot || !excludedRoot.contains(element))
|
|
1159
1849
|
);
|
|
1160
1850
|
}
|
|
1161
1851
|
function readPlatformRectangles(elements, workAreaRectangle) {
|
|
@@ -1283,21 +1973,26 @@ var ShimejiEngine = class {
|
|
|
1283
1973
|
destroyed = false;
|
|
1284
1974
|
initialized = false;
|
|
1285
1975
|
platformSource;
|
|
1286
|
-
|
|
1976
|
+
additionalPlatformElements = [];
|
|
1977
|
+
movedPlatforms = /* @__PURE__ */ new Map();
|
|
1978
|
+
platformRectangles = /* @__PURE__ */ new Map();
|
|
1979
|
+
/** Starts the clock and container-aware listeners. Calling this method more than once is harmless. */
|
|
1287
1980
|
initialize() {
|
|
1288
1981
|
this.assertAlive();
|
|
1289
1982
|
if (this.initialized) return;
|
|
1290
1983
|
this.initialized = true;
|
|
1291
|
-
|
|
1984
|
+
const document2 = this.container.ownerDocument;
|
|
1985
|
+
const view = document2.defaultView;
|
|
1986
|
+
if (!view) throw new Error("ShimejiEngine requires a container connected to a window");
|
|
1987
|
+
this.listen(document2, "pointermove", (event) => {
|
|
1292
1988
|
const pointerEvent = event;
|
|
1293
|
-
const x = pointerEvent.clientX;
|
|
1294
|
-
const y = pointerEvent.clientY;
|
|
1989
|
+
const { x, y } = this.dom.toLocalPoint(pointerEvent.clientX, pointerEvent.clientY);
|
|
1295
1990
|
this.pointer = { x, y, dx: x - this.pointer.x, dy: y - this.pointer.y };
|
|
1296
1991
|
});
|
|
1297
|
-
this.listen(
|
|
1298
|
-
const maintenance =
|
|
1992
|
+
this.listen(view, "resize", () => this.renderAll());
|
|
1993
|
+
const maintenance = view.setInterval(() => this.dom.ensureMounted(), 2e3);
|
|
1299
1994
|
this.intervals.add(maintenance);
|
|
1300
|
-
this.animationFrame = requestAnimationFrame(this.onAnimationFrame);
|
|
1995
|
+
this.animationFrame = view.requestAnimationFrame(this.onAnimationFrame);
|
|
1301
1996
|
}
|
|
1302
1997
|
/** Registers or replaces a parsed or legacy character specification. */
|
|
1303
1998
|
registerCharacter(spec) {
|
|
@@ -1327,8 +2022,8 @@ var ShimejiEngine = class {
|
|
|
1327
2022
|
const random = this.options.random ?? Math.random;
|
|
1328
2023
|
const spawnOptions = {
|
|
1329
2024
|
...position,
|
|
1330
|
-
x: position.x ?? bounds.x + random() * bounds.width,
|
|
1331
|
-
y: position.y ?? bounds.y
|
|
2025
|
+
x: position.x ?? Math.trunc(bounds.x + random() * bounds.width),
|
|
2026
|
+
y: position.y ?? bounds.y + 2
|
|
1332
2027
|
};
|
|
1333
2028
|
const id = `shimeji-${this.nextMascotId++}`;
|
|
1334
2029
|
const mascot = new Mascot(id, spec, this.dom, this.options, spawnOptions, {
|
|
@@ -1340,6 +2035,7 @@ var ShimejiEngine = class {
|
|
|
1340
2035
|
remove: (mascotId) => {
|
|
1341
2036
|
if (!this.destroyed) this.remove(mascotId);
|
|
1342
2037
|
},
|
|
2038
|
+
movePlatform: (element, point) => this.movePlatform(element, point),
|
|
1343
2039
|
click: (state2) => this.events.emit("click", state2),
|
|
1344
2040
|
error: (error) => this.events.emit("error", error)
|
|
1345
2041
|
});
|
|
@@ -1376,22 +2072,27 @@ var ShimejiEngine = class {
|
|
|
1376
2072
|
this.assertAlive();
|
|
1377
2073
|
return this.events.on(event, listener);
|
|
1378
2074
|
}
|
|
1379
|
-
/** Replaces the
|
|
1380
|
-
setPlatforms(platforms) {
|
|
2075
|
+
/** Replaces the primary platform source and any additional registered elements. */
|
|
2076
|
+
setPlatforms(platforms, additionalPlatforms = []) {
|
|
1381
2077
|
this.assertAlive();
|
|
1382
2078
|
this.platformSource = platforms;
|
|
2079
|
+
this.additionalPlatformElements = additionalPlatforms;
|
|
1383
2080
|
}
|
|
1384
2081
|
/** Stops animation and timers, removes listeners and DOM, and revokes all object URLs. */
|
|
1385
2082
|
destroy() {
|
|
1386
2083
|
if (this.destroyed) return;
|
|
1387
2084
|
for (const mascot of this.mascots.values()) mascot.destroy();
|
|
1388
2085
|
this.mascots.clear();
|
|
1389
|
-
|
|
2086
|
+
const view = this.container.ownerDocument.defaultView;
|
|
2087
|
+
if (this.animationFrame !== void 0) view?.cancelAnimationFrame(this.animationFrame);
|
|
1390
2088
|
this.animationFrame = void 0;
|
|
1391
|
-
for (const interval of this.intervals)
|
|
2089
|
+
for (const interval of this.intervals) view?.clearInterval(interval);
|
|
1392
2090
|
this.intervals.clear();
|
|
1393
2091
|
for (const dispose of this.disposers.splice(0)) dispose();
|
|
1394
2092
|
this.dom.destroy();
|
|
2093
|
+
for (const [element, movement] of this.movedPlatforms) element.style.transform = movement.originalTransform;
|
|
2094
|
+
this.movedPlatforms.clear();
|
|
2095
|
+
this.platformRectangles.clear();
|
|
1395
2096
|
this.sprites.destroy();
|
|
1396
2097
|
this.specs.clear();
|
|
1397
2098
|
this.events.clear();
|
|
@@ -1410,7 +2111,7 @@ var ShimejiEngine = class {
|
|
|
1410
2111
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
1411
2112
|
for (const mascot of [...this.mascots.values()]) mascot.tick(delta, bounds, platforms);
|
|
1412
2113
|
this.emitState();
|
|
1413
|
-
this.animationFrame = requestAnimationFrame(this.onAnimationFrame);
|
|
2114
|
+
this.animationFrame = this.container.ownerDocument.defaultView?.requestAnimationFrame(this.onAnimationFrame);
|
|
1414
2115
|
};
|
|
1415
2116
|
renderAll() {
|
|
1416
2117
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
@@ -1418,8 +2119,26 @@ var ShimejiEngine = class {
|
|
|
1418
2119
|
}
|
|
1419
2120
|
readFrameGeometry() {
|
|
1420
2121
|
const bounds = this.dom.getBounds();
|
|
1421
|
-
const elements =
|
|
1422
|
-
|
|
2122
|
+
const elements = [.../* @__PURE__ */ new Set([
|
|
2123
|
+
...resolvePlatformElements(this.platformSource, this.container),
|
|
2124
|
+
...resolvePlatformElements(this.additionalPlatformElements, this.container)
|
|
2125
|
+
])].filter((element) => !this.dom.owns(element));
|
|
2126
|
+
const platforms = readPlatformRectangles(elements, this.container.getBoundingClientRect());
|
|
2127
|
+
this.platformRectangles.clear();
|
|
2128
|
+
for (const platform of platforms) this.platformRectangles.set(platform.element, platform);
|
|
2129
|
+
return { bounds, platforms };
|
|
2130
|
+
}
|
|
2131
|
+
movePlatform(element, point) {
|
|
2132
|
+
const rectangle = this.platformRectangles.get(element);
|
|
2133
|
+
if (!rectangle) return;
|
|
2134
|
+
const movement = this.movedPlatforms.get(element) ?? { originalTransform: element.style.transform, x: 0, y: 0 };
|
|
2135
|
+
movement.x += point.x - rectangle.x;
|
|
2136
|
+
movement.y += point.y - rectangle.y;
|
|
2137
|
+
const translate = `translate(${movement.x}px, ${movement.y}px)`;
|
|
2138
|
+
element.style.transform = movement.originalTransform ? `${movement.originalTransform} ${translate}` : translate;
|
|
2139
|
+
rectangle.x = point.x;
|
|
2140
|
+
rectangle.y = point.y;
|
|
2141
|
+
this.movedPlatforms.set(element, movement);
|
|
1423
2142
|
}
|
|
1424
2143
|
emitState() {
|
|
1425
2144
|
this.events.emit("statechange", this.getState());
|