@react-shimeji/core 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1072 -343
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -27
- package/dist/index.d.ts +73 -27
- package/dist/index.js +1072 -343
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -47,68 +47,83 @@ module.exports = __toCommonJS(index_exports);
|
|
|
47
47
|
|
|
48
48
|
// src/dom.ts
|
|
49
49
|
var DomManager = class {
|
|
50
|
-
/** Uses the supplied element
|
|
50
|
+
/** Uses the supplied element as the containing block and clipping boundary. */
|
|
51
51
|
constructor(container, sprites) {
|
|
52
52
|
this.container = container;
|
|
53
53
|
this.sprites = sprites;
|
|
54
|
+
const view = container.ownerDocument.defaultView;
|
|
55
|
+
const computedStyle = view?.getComputedStyle(container);
|
|
56
|
+
if (!computedStyle?.position || computedStyle.position === "static") this.applyContainerStyle("position", "relative");
|
|
57
|
+
if (computedStyle?.overflowX === "visible" || !computedStyle?.overflowX) {
|
|
58
|
+
this.applyContainerStyle("overflowX", "clip");
|
|
59
|
+
}
|
|
54
60
|
}
|
|
55
61
|
container;
|
|
56
62
|
sprites;
|
|
57
63
|
handles = /* @__PURE__ */ new Set();
|
|
64
|
+
restoreContainerStyles = [];
|
|
58
65
|
/** Reattaches mascot elements if application code temporarily removed them. */
|
|
59
66
|
ensureMounted() {
|
|
60
|
-
const target = this.container.ownerDocument.body;
|
|
61
67
|
for (const handle of this.handles) {
|
|
62
|
-
if (handle.element.parentElement !==
|
|
68
|
+
if (handle.element.parentElement !== this.container) this.container.appendChild(handle.element);
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
|
-
/** Returns
|
|
71
|
+
/** Returns bounds in the container-local coordinate system. */
|
|
66
72
|
getBounds() {
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
return { x: 0, y: 0, width: this.container.clientWidth, height: this.container.clientHeight };
|
|
74
|
+
}
|
|
75
|
+
/** Converts a viewport client coordinate into container-local coordinates. */
|
|
76
|
+
toLocalPoint(clientX, clientY) {
|
|
77
|
+
const rectangle = this.container.getBoundingClientRect();
|
|
78
|
+
return { x: clientX - rectangle.left, y: clientY - rectangle.top };
|
|
69
79
|
}
|
|
70
80
|
/** Creates a mascot node and acquires its spritesheet resource. */
|
|
71
81
|
createMascot(spec, mascotId, mascotClassName) {
|
|
72
82
|
const spriteLease = this.sprites.acquire(spec.spritesheet);
|
|
73
|
-
const element =
|
|
83
|
+
const element = this.container.ownerDocument.createElement("div");
|
|
74
84
|
element.dataset.shimejiId = mascotId;
|
|
85
|
+
element.setAttribute("aria-hidden", "true");
|
|
75
86
|
if (mascotClassName) element.className = mascotClassName;
|
|
76
|
-
Object.assign(element.style, { position: "
|
|
77
|
-
const spriteElement =
|
|
87
|
+
Object.assign(element.style, { position: "absolute", left: "0", top: "0", width: "0", height: "0", pointerEvents: "none", zIndex: "9999", userSelect: "none", willChange: "transform" });
|
|
88
|
+
const spriteElement = this.container.ownerDocument.createElement("div");
|
|
78
89
|
Object.assign(spriteElement.style, { position: "absolute", left: "0", top: "0", backgroundRepeat: "no-repeat", transformOrigin: "center center", pointerEvents: "auto", touchAction: "none", userSelect: "none" });
|
|
79
90
|
element.appendChild(spriteElement);
|
|
80
91
|
const handle = { element, spriteElement, spriteLease };
|
|
81
92
|
this.handles.add(handle);
|
|
82
|
-
this.container.
|
|
93
|
+
this.container.appendChild(element);
|
|
83
94
|
return handle;
|
|
84
95
|
}
|
|
85
96
|
/** Paints one mascot state into its existing DOM nodes. */
|
|
86
97
|
render(handle, spec, state) {
|
|
87
98
|
const sprite = this.sprites.resolve(spec, handle.spriteLease, state.sprite);
|
|
88
|
-
handle.element.style.transform = `translate3d(${state.x - state.anchorX}px, ${state.y - state.anchorY}px, 0)`;
|
|
89
99
|
handle.spriteElement.style.left = "0";
|
|
90
100
|
handle.spriteElement.style.top = "0";
|
|
91
101
|
handle.spriteElement.style.transform = `scaleX(${state.lookRight ? -1 : 1})`;
|
|
92
|
-
if (!sprite)
|
|
102
|
+
if (!sprite) {
|
|
103
|
+
handle.element.style.transform = `translate3d(${state.x - state.anchorX}px, ${state.y - state.anchorY}px, 0)`;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
let width = 128;
|
|
107
|
+
let height = 128;
|
|
93
108
|
handle.spriteElement.style.backgroundImage = `url("${sprite.url.replaceAll('"', '\\"')}")`;
|
|
94
109
|
if (sprite.rectangle) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
handle.element.style.width = `${sprite.rectangle.width}px`;
|
|
98
|
-
handle.element.style.height = `${sprite.rectangle.height}px`;
|
|
110
|
+
width = sprite.rectangle.width;
|
|
111
|
+
height = sprite.rectangle.height;
|
|
99
112
|
handle.spriteElement.style.backgroundPosition = `${-sprite.rectangle.x}px ${-sprite.rectangle.y}px`;
|
|
100
113
|
handle.spriteElement.style.backgroundSize = "auto";
|
|
101
114
|
} else {
|
|
102
115
|
handle.spriteElement.style.backgroundPosition = "0 0";
|
|
103
116
|
handle.spriteElement.style.backgroundSize = "contain";
|
|
104
|
-
handle.spriteElement.style.width = "128px";
|
|
105
|
-
handle.spriteElement.style.height = "128px";
|
|
106
117
|
const definition = Object.values(spec.sprites).find((candidate) => typeof candidate === "object" && "url" in candidate && candidate.url === sprite.url);
|
|
107
|
-
if (typeof definition === "object" && "width" in definition && definition.width !== void 0)
|
|
108
|
-
if (typeof definition === "object" && "height" in definition && definition.height !== void 0)
|
|
109
|
-
handle.element.style.width = handle.spriteElement.style.width;
|
|
110
|
-
handle.element.style.height = handle.spriteElement.style.height;
|
|
118
|
+
if (typeof definition === "object" && "width" in definition && definition.width !== void 0) width = definition.width;
|
|
119
|
+
if (typeof definition === "object" && "height" in definition && definition.height !== void 0) height = definition.height;
|
|
111
120
|
}
|
|
121
|
+
handle.spriteElement.style.width = `${width}px`;
|
|
122
|
+
handle.spriteElement.style.height = `${height}px`;
|
|
123
|
+
handle.element.style.width = `${width}px`;
|
|
124
|
+
handle.element.style.height = `${height}px`;
|
|
125
|
+
const anchorX = state.lookRight ? width - state.anchorX : state.anchorX;
|
|
126
|
+
handle.element.style.transform = `translate3d(${state.x - anchorX}px, ${state.y - state.anchorY}px, 0)`;
|
|
112
127
|
}
|
|
113
128
|
/** Removes one mascot node and releases its temporary image URL. */
|
|
114
129
|
removeMascot(handle) {
|
|
@@ -124,6 +139,14 @@ var DomManager = class {
|
|
|
124
139
|
/** Removes every mascot element and releases its temporary image URL. */
|
|
125
140
|
destroy() {
|
|
126
141
|
for (const handle of [...this.handles]) this.removeMascot(handle);
|
|
142
|
+
for (const restore of this.restoreContainerStyles.splice(0).reverse()) restore();
|
|
143
|
+
}
|
|
144
|
+
applyContainerStyle(property, value) {
|
|
145
|
+
const previous = this.container.style[property];
|
|
146
|
+
this.container.style[property] = value;
|
|
147
|
+
this.restoreContainerStyles.push(() => {
|
|
148
|
+
if (this.container.style[property] === value) this.container.style[property] = previous;
|
|
149
|
+
});
|
|
127
150
|
}
|
|
128
151
|
};
|
|
129
152
|
|
|
@@ -136,6 +159,9 @@ var actionTypeNames = {
|
|
|
136
159
|
Animate: "Animate",
|
|
137
160
|
Move: "Move",
|
|
138
161
|
Embedded: "Embedded",
|
|
162
|
+
Composite: "Sequence",
|
|
163
|
+
Fixed: "Animate",
|
|
164
|
+
Pause: "Stay",
|
|
139
165
|
\u8907\u5408: "Sequence",
|
|
140
166
|
\u9078\u629E: "Select",
|
|
141
167
|
\u53C2\u7167: "Reference",
|
|
@@ -185,12 +211,13 @@ function actionProperty(element, ...names) {
|
|
|
185
211
|
function parseAnimation(element) {
|
|
186
212
|
const poses = directChildren(element, "Pose", "\u30DD\u30FC\u30BA").map((pose) => ({
|
|
187
213
|
sprite: attribute(pose, "Image", "\u753B\u50CF") ?? "/shime1.png",
|
|
188
|
-
anchor: parsePoint(attribute(pose, "Anchor", "\u57FA\u6E96\u5EA7\u6A19"), { x: 64, y: 128 }),
|
|
214
|
+
anchor: parsePoint(attribute(pose, "ImageAnchor", "Anchor", "\u57FA\u6E96\u5EA7\u6A19"), { x: 64, y: 128 }),
|
|
189
215
|
velocity: parsePoint(attribute(pose, "Velocity", "\u79FB\u52D5\u901F\u5EA6")),
|
|
190
216
|
duration: Number(attribute(pose, "Duration", "\u9577\u3055") ?? 1)
|
|
191
217
|
}));
|
|
192
218
|
const condition = attribute(element, "Condition", "\u6761\u4EF6");
|
|
193
|
-
|
|
219
|
+
const turn = (attribute(element, "IsTurn", "Turn") ?? "false").toLowerCase() === "true";
|
|
220
|
+
return { poses, ...condition !== void 0 && { condition }, ...turn && { turn } };
|
|
194
221
|
}
|
|
195
222
|
function parseActionElement(element) {
|
|
196
223
|
const isReference = element.localName === "ActionReference" || element.localName === "\u52D5\u4F5C\u53C2\u7167";
|
|
@@ -214,20 +241,26 @@ function parseActionElement(element) {
|
|
|
214
241
|
};
|
|
215
242
|
const properties = [
|
|
216
243
|
["duration", actionProperty(element, "Duration", "\u9577\u3055")],
|
|
217
|
-
["gap", actionProperty(element, "Gap", "\u9593\u9694")],
|
|
244
|
+
["gap", actionProperty(element, "Gap", "\u9593\u9694", "\u305A\u308C")],
|
|
218
245
|
["targetX", actionProperty(element, "TargetX", "\u76EE\u7684\u5730X")],
|
|
219
246
|
["targetY", actionProperty(element, "TargetY", "\u76EE\u7684\u5730Y")],
|
|
220
|
-
["velocity", actionProperty(element, "Velocity", "\u901F\u5EA6")],
|
|
247
|
+
["velocity", actionProperty(element, "VelocityParam", "Velocity", "\u901F\u5EA6")],
|
|
221
248
|
["x", actionProperty(element, "X", "\u5909\u4F4DX")],
|
|
222
249
|
["y", actionProperty(element, "Y", "\u5909\u4F4DY")],
|
|
250
|
+
["offsetX", actionProperty(element, "OffsetX", "\u7AEFX")],
|
|
251
|
+
["offsetY", actionProperty(element, "OffsetY", "\u7AEFY")],
|
|
252
|
+
["offsetType", actionProperty(element, "OffsetType")],
|
|
223
253
|
["initialVx", actionProperty(element, "InitialVX", "InitialVx", "\u521D\u901FX")],
|
|
224
254
|
["initialVy", actionProperty(element, "InitialVY", "InitialVy", "\u521D\u901FY")],
|
|
225
|
-
["resistanceX", actionProperty(element, "ResistanceX", "\u7A7A\u6C17\u62B5\u6297X")],
|
|
226
|
-
["resistanceY", actionProperty(element, "ResistanceY", "\u7A7A\u6C17\u62B5\u6297Y")],
|
|
255
|
+
["resistanceX", actionProperty(element, "RegistanceX", "ResistanceX", "\u7A7A\u6C17\u62B5\u6297X")],
|
|
256
|
+
["resistanceY", actionProperty(element, "RegistanceY", "ResistanceY", "\u7A7A\u6C17\u62B5\u6297Y")],
|
|
227
257
|
["gravity", actionProperty(element, "Gravity", "\u91CD\u529B")],
|
|
228
|
-
["bornX", actionProperty(element, "BornX", "\u8A95\u751FX")],
|
|
229
|
-
["bornY", actionProperty(element, "BornY", "\u8A95\u751FY")],
|
|
230
|
-
["bornBehavior", actionProperty(element, "BornBehavior", "\u8A95\u751F\u6642\u306E\u884C\u52D5")],
|
|
258
|
+
["bornX", actionProperty(element, "BornX", "\u8A95\u751FX", "\u751F\u307E\u308C\u308B\u5834\u6240X")],
|
|
259
|
+
["bornY", actionProperty(element, "BornY", "\u8A95\u751FY", "\u751F\u307E\u308C\u308B\u5834\u6240Y")],
|
|
260
|
+
["bornBehavior", actionProperty(element, "BornBehavior", "BornBehaviour", "\u8A95\u751F\u6642\u306E\u884C\u52D5", "\u751F\u307E\u308C\u305F\u6642\u306E\u884C\u52D5")],
|
|
261
|
+
["bornMascot", actionProperty(element, "BornMascot")],
|
|
262
|
+
["bornCount", actionProperty(element, "BornCount")],
|
|
263
|
+
["bornInterval", actionProperty(element, "BornInterval")],
|
|
231
264
|
["ieOffsetX", actionProperty(element, "IEOffsetX", "IE\u306E\u7AEFX")],
|
|
232
265
|
["ieOffsetY", actionProperty(element, "IEOffsetY", "IE\u306E\u7AEFY")],
|
|
233
266
|
["lookRight", actionProperty(element, "LookRight", "\u53F3\u5411\u304D")]
|
|
@@ -243,18 +276,33 @@ function parseActionsXml(xml) {
|
|
|
243
276
|
const roots = lists.length ? lists : [document2.documentElement];
|
|
244
277
|
return roots.flatMap((list) => directChildren(list, "Action", "\u52D5\u4F5C").map(parseActionElement));
|
|
245
278
|
}
|
|
279
|
+
function parseNextBehaviors(element, inheritedConditions) {
|
|
280
|
+
const behaviors = [];
|
|
281
|
+
for (const child of Array.from(element.children)) {
|
|
282
|
+
if (child.localName === "Condition" || child.localName === "\u6761\u4EF6") {
|
|
283
|
+
const condition = attribute(child, "Condition", "\u6761\u4EF6");
|
|
284
|
+
behaviors.push(...parseNextBehaviors(child, [...inheritedConditions, ...condition ? [condition] : []]));
|
|
285
|
+
} else if (["Behavior", "\u884C\u52D5", "BehaviorReference", "BehaviorReferance", "\u884C\u52D5\u53C2\u7167"].includes(child.localName)) {
|
|
286
|
+
behaviors.push(parseBehaviorElement(child, inheritedConditions, 0));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return behaviors;
|
|
290
|
+
}
|
|
246
291
|
function parseBehaviorElement(element, inheritedConditions, groupIndex) {
|
|
247
292
|
const condition = attribute(element, "Condition", "\u6761\u4EF6");
|
|
248
293
|
const conditions = [...inheritedConditions, ...condition ? [condition] : []];
|
|
249
294
|
const nextList = directChildren(element, "NextBehaviorList", "NextBehavior", "\u6B21\u306E\u884C\u52D5\u30EA\u30B9\u30C8")[0];
|
|
250
|
-
const nextBehaviors = nextList ?
|
|
295
|
+
const nextBehaviors = nextList ? parseNextBehaviors(nextList, []) : [];
|
|
251
296
|
const reference = element.localName === "BehaviorReference" || element.localName === "BehaviorReferance" || element.localName === "\u884C\u52D5\u53C2\u7167";
|
|
297
|
+
const actionName = attribute(element, "Action", "\u52D5\u4F5C");
|
|
252
298
|
return {
|
|
253
299
|
type: reference ? "Reference" : "Behavior",
|
|
254
300
|
name: attribute(element, "Name", "\u540D\u524D") ?? "",
|
|
255
301
|
frequency: Number(attribute(element, "Frequency", "\u983B\u5EA6") ?? 0),
|
|
256
302
|
conditions,
|
|
257
303
|
nextBehaviors,
|
|
304
|
+
...nextList && { nextAdditive: (attribute(nextList, "Add", "\u8FFD\u52A0") ?? "true").toLowerCase() === "true" },
|
|
305
|
+
...actionName !== void 0 && { actionName },
|
|
258
306
|
groupIndex,
|
|
259
307
|
hidden: (attribute(element, "Hidden", "\u975E\u8868\u793A") ?? "false").toLowerCase() === "true"
|
|
260
308
|
};
|
|
@@ -360,7 +408,7 @@ var functions = {
|
|
|
360
408
|
};
|
|
361
409
|
var constants = { E: Math.E, PI: Math.PI };
|
|
362
410
|
function normalizeExpression(source) {
|
|
363
|
-
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
|
|
411
|
+
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, "!");
|
|
364
412
|
}
|
|
365
413
|
function tokenize(source) {
|
|
366
414
|
const tokens = [];
|
|
@@ -561,7 +609,7 @@ function evaluateNode(node, scope) {
|
|
|
561
609
|
}
|
|
562
610
|
}
|
|
563
611
|
var expressionCache = /* @__PURE__ */ new Map();
|
|
564
|
-
function evaluateExpression(expression, environment, fallback) {
|
|
612
|
+
function evaluateExpression(expression, environment, fallback, random = Math.random) {
|
|
565
613
|
if (expression === void 0) return fallback;
|
|
566
614
|
if (typeof expression !== "string") return expression;
|
|
567
615
|
try {
|
|
@@ -571,7 +619,7 @@ function evaluateExpression(expression, environment, fallback) {
|
|
|
571
619
|
ast = new Parser(tokenize(normalized)).parse();
|
|
572
620
|
expressionCache.set(normalized, ast);
|
|
573
621
|
}
|
|
574
|
-
const result = evaluateNode(ast, environment);
|
|
622
|
+
const result = evaluateNode(ast, { ...environment, random: (maximum = 1) => random() * Number(maximum) });
|
|
575
623
|
if (typeof fallback === "boolean") return Boolean(result);
|
|
576
624
|
const numericResult = Number(result);
|
|
577
625
|
return Number.isNaN(numericResult) ? fallback : numericResult;
|
|
@@ -579,8 +627,8 @@ function evaluateExpression(expression, environment, fallback) {
|
|
|
579
627
|
return fallback;
|
|
580
628
|
}
|
|
581
629
|
}
|
|
582
|
-
function conditionsMatch(conditions, environment) {
|
|
583
|
-
return conditions.every((condition) => evaluateExpression(condition, environment, false));
|
|
630
|
+
function conditionsMatch(conditions, environment, random = Math.random) {
|
|
631
|
+
return conditions.every((condition) => evaluateExpression(condition, environment, false, random));
|
|
584
632
|
}
|
|
585
633
|
function selectWeighted(items, weight, random = Math.random) {
|
|
586
634
|
const weighted = items.map((item) => ({ item, weight: Math.max(0, weight(item)) }));
|
|
@@ -602,62 +650,76 @@ var BehaviorController = class {
|
|
|
602
650
|
spec;
|
|
603
651
|
random;
|
|
604
652
|
previous;
|
|
653
|
+
fallbackSelected = false;
|
|
605
654
|
/** Selects an initial behavior, honoring an explicit requested name when possible. */
|
|
606
655
|
selectInitial(environment, requestedName) {
|
|
656
|
+
this.fallbackSelected = false;
|
|
607
657
|
if (requestedName) {
|
|
608
658
|
const requested = this.spec.behaviors.find((behavior) => behavior.name === requestedName);
|
|
609
|
-
if (requested
|
|
659
|
+
if (requested) return this.previous = this.resolve(requested);
|
|
610
660
|
}
|
|
611
|
-
const fall = this.findFallBehavior();
|
|
612
|
-
if (fall && !this.isOnAnyBoundary(environment)) return this.previous = fall;
|
|
613
661
|
return this.previous = this.choose(this.spec.behaviors, environment);
|
|
614
662
|
}
|
|
615
663
|
/** Selects the weighted transition following the current behavior. */
|
|
616
664
|
selectNext(environment) {
|
|
617
|
-
const
|
|
618
|
-
|
|
665
|
+
const next = this.previous?.nextBehaviors ?? [];
|
|
666
|
+
const pool = this.previous && this.previous.nextAdditive === false ? next : [...this.spec.behaviors, ...next];
|
|
667
|
+
const selected = this.choose(pool, environment);
|
|
668
|
+
this.fallbackSelected = selected === void 0;
|
|
669
|
+
return this.previous = selected ?? this.findFallBehavior();
|
|
670
|
+
}
|
|
671
|
+
/** Whether the most recent transition had no effective weighted candidate. */
|
|
672
|
+
usedFallback() {
|
|
673
|
+
return this.fallbackSelected;
|
|
619
674
|
}
|
|
620
675
|
/** Replaces selection history so an external interaction can force a behavior. */
|
|
621
676
|
force(name) {
|
|
677
|
+
this.fallbackSelected = false;
|
|
622
678
|
const behavior = this.spec.behaviors.find((candidate) => candidate.name === name);
|
|
623
679
|
return this.previous = behavior ? this.resolve(behavior) : void 0;
|
|
624
680
|
}
|
|
625
681
|
choose(pool, environment) {
|
|
626
|
-
const applicable = pool.filter((behavior) => conditionsMatch(behavior.conditions, environment));
|
|
682
|
+
const applicable = pool.filter((behavior) => conditionsMatch(behavior.conditions, environment, this.random));
|
|
627
683
|
const chosen = selectWeighted(applicable, (behavior) => behavior.frequency, this.random);
|
|
628
684
|
return chosen ? this.resolve(chosen) : void 0;
|
|
629
685
|
}
|
|
630
686
|
resolve(behavior) {
|
|
631
687
|
if (behavior.type !== "Reference") return behavior;
|
|
632
688
|
const target = this.spec.behaviors.find((candidate) => candidate.type === "Behavior" && candidate.name === behavior.name);
|
|
633
|
-
return target ? {
|
|
689
|
+
return target ? {
|
|
690
|
+
...target,
|
|
691
|
+
...behavior,
|
|
692
|
+
type: "Behavior",
|
|
693
|
+
nextBehaviors: target.nextBehaviors,
|
|
694
|
+
...behavior.actionName !== void 0 ? { actionName: behavior.actionName } : target.actionName !== void 0 ? { actionName: target.actionName } : {},
|
|
695
|
+
...target.nextAdditive !== void 0 && { nextAdditive: target.nextAdditive }
|
|
696
|
+
} : { ...behavior, type: "Behavior" };
|
|
634
697
|
}
|
|
635
698
|
findFallBehavior() {
|
|
636
699
|
return this.spec.behaviors.find((behavior) => behavior.name === "Fall" || behavior.name === "\u843D\u4E0B\u3059\u308B");
|
|
637
700
|
}
|
|
638
|
-
isOnAnyBoundary(environment) {
|
|
639
|
-
const anchor = environment.mascot.anchor;
|
|
640
|
-
const area = environment.mascot.environment.workArea;
|
|
641
|
-
const activeIE = environment.mascot.environment.activeIE;
|
|
642
|
-
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));
|
|
643
|
-
}
|
|
644
701
|
};
|
|
645
702
|
|
|
646
703
|
// src/physics.ts
|
|
704
|
+
var BORDER_TOLERANCE = 1 - Number.EPSILON / 2;
|
|
705
|
+
function isWithinSpan(value, minimum, maximum, tolerance) {
|
|
706
|
+
const distance = value < minimum ? minimum - value : value > maximum ? value - maximum : 0;
|
|
707
|
+
return distance <= tolerance;
|
|
708
|
+
}
|
|
647
709
|
function clamp(value, minimum, maximum) {
|
|
648
710
|
return Math.min(Math.max(value, minimum), maximum);
|
|
649
711
|
}
|
|
650
|
-
function isOnTop(point, rectangle, tolerance =
|
|
651
|
-
return point.x
|
|
712
|
+
function isOnTop(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
713
|
+
return isWithinSpan(point.x, rectangle.x, rectangle.x + rectangle.width, tolerance) && Math.abs(point.y - rectangle.y) <= tolerance;
|
|
652
714
|
}
|
|
653
|
-
function isOnBottom(point, rectangle, tolerance =
|
|
654
|
-
return point.x
|
|
715
|
+
function isOnBottom(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
716
|
+
return isWithinSpan(point.x, rectangle.x, rectangle.x + rectangle.width, tolerance) && Math.abs(point.y - rectangle.y - rectangle.height) <= tolerance;
|
|
655
717
|
}
|
|
656
|
-
function isOnLeft(point, rectangle, tolerance =
|
|
657
|
-
return point.y
|
|
718
|
+
function isOnLeft(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
719
|
+
return isWithinSpan(point.y, rectangle.y, rectangle.y + rectangle.height, tolerance) && Math.abs(point.x - rectangle.x) <= tolerance;
|
|
658
720
|
}
|
|
659
|
-
function isOnRight(point, rectangle, tolerance =
|
|
660
|
-
return point.y
|
|
721
|
+
function isOnRight(point, rectangle, tolerance = BORDER_TOLERANCE) {
|
|
722
|
+
return isWithinSpan(point.y, rectangle.y, rectangle.y + rectangle.height, tolerance) && Math.abs(point.x - rectangle.x - rectangle.width) <= tolerance;
|
|
661
723
|
}
|
|
662
724
|
function isOnBorder(state, bounds, border, platform) {
|
|
663
725
|
if (!border) return true;
|
|
@@ -665,29 +727,46 @@ function isOnBorder(state, bounds, border, platform) {
|
|
|
665
727
|
if (border === "Ceiling") return isOnTop(state, bounds) || platform !== void 0 && isOnBottom(state, platform);
|
|
666
728
|
return isOnLeft(state, bounds) || isOnRight(state, bounds) || platform !== void 0 && (isOnLeft(state, platform) || isOnRight(state, platform));
|
|
667
729
|
}
|
|
668
|
-
function
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
730
|
+
function isOnFloor(point, bounds, platforms = []) {
|
|
731
|
+
return platforms.some((platform) => isOnTop(point, platform)) || isOnBottom(point, bounds);
|
|
732
|
+
}
|
|
733
|
+
function isOnWall(point, bounds, lookRight, platforms = []) {
|
|
734
|
+
return lookRight ? platforms.some((platform) => isOnLeft(point, platform)) || isOnRight(point, bounds) : platforms.some((platform) => isOnRight(point, platform)) || isOnLeft(point, bounds);
|
|
735
|
+
}
|
|
736
|
+
function applyGravity(state, bounds, frameScale, gravity, resistanceX = 0.05, resistanceY = 0.1, platform) {
|
|
737
|
+
const platforms = platform ? [platform] : [];
|
|
738
|
+
const steps = Math.max(1, Math.round(frameScale));
|
|
739
|
+
let stopped = false;
|
|
740
|
+
for (let frame = 0; frame < steps && !stopped; frame += 1) {
|
|
741
|
+
state.vx -= state.vx * resistanceX;
|
|
742
|
+
state.vy = state.vy - state.vy * resistanceY + gravity;
|
|
743
|
+
const dx = Math.trunc(state.vx);
|
|
744
|
+
const dy = Math.trunc(state.vy);
|
|
745
|
+
const divisions = Math.max(1, Math.abs(dx), Math.abs(dy));
|
|
746
|
+
const start = { x: state.x, y: state.y };
|
|
747
|
+
for (let index = 0; index <= divisions; index += 1) {
|
|
748
|
+
const x = start.x + Math.trunc(dx * index / divisions);
|
|
749
|
+
const y = start.y + Math.trunc(dy * index / divisions);
|
|
750
|
+
state.x = x;
|
|
751
|
+
state.y = y;
|
|
752
|
+
if (dy > 0) {
|
|
753
|
+
for (let offset = -80; offset <= 0; offset += 1) {
|
|
754
|
+
state.y = y + offset;
|
|
755
|
+
if (isOnFloor(state, bounds, platforms)) {
|
|
756
|
+
stopped = true;
|
|
757
|
+
break;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (stopped) break;
|
|
761
|
+
state.y = y;
|
|
762
|
+
}
|
|
763
|
+
if (isOnWall(state, bounds, state.lookRight, platforms)) {
|
|
764
|
+
stopped = true;
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
689
768
|
}
|
|
690
|
-
return
|
|
769
|
+
return stopped;
|
|
691
770
|
}
|
|
692
771
|
function moveToward(state, target, speed, frameScale) {
|
|
693
772
|
const dx = target.x - state.x;
|
|
@@ -704,251 +783,832 @@ function moveToward(state, target, speed, frameScale) {
|
|
|
704
783
|
}
|
|
705
784
|
|
|
706
785
|
// src/action.ts
|
|
707
|
-
function
|
|
708
|
-
return value ===
|
|
786
|
+
function expressionIsPerFrame(value) {
|
|
787
|
+
return typeof value === "string" && value.trimStart().startsWith("#{");
|
|
709
788
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
789
|
+
var ActionValues = class {
|
|
790
|
+
constructor(random) {
|
|
791
|
+
this.random = random;
|
|
792
|
+
}
|
|
793
|
+
random;
|
|
794
|
+
actionCache = /* @__PURE__ */ new Map();
|
|
795
|
+
frameCache = /* @__PURE__ */ new Map();
|
|
796
|
+
init() {
|
|
797
|
+
this.actionCache.clear();
|
|
798
|
+
this.frameCache.clear();
|
|
799
|
+
}
|
|
800
|
+
initFrame() {
|
|
801
|
+
this.frameCache.clear();
|
|
802
|
+
}
|
|
803
|
+
number(key, value, environment, fallback) {
|
|
804
|
+
if (value === void 0) return fallback;
|
|
805
|
+
if (typeof value === "number") return value;
|
|
806
|
+
const cache = expressionIsPerFrame(value) ? this.frameCache : this.actionCache;
|
|
807
|
+
const cached = cache.get(key);
|
|
808
|
+
if (typeof cached === "number") return cached;
|
|
809
|
+
const result = evaluateExpression(value, environment, fallback, this.random);
|
|
810
|
+
cache.set(key, result);
|
|
811
|
+
return result;
|
|
812
|
+
}
|
|
813
|
+
boolean(key, value, environment, fallback) {
|
|
814
|
+
if (value === void 0) return fallback;
|
|
815
|
+
if (typeof value === "boolean") return value;
|
|
816
|
+
const cache = expressionIsPerFrame(value) ? this.frameCache : this.actionCache;
|
|
817
|
+
const cached = cache.get(key);
|
|
818
|
+
if (typeof cached === "boolean") return cached;
|
|
819
|
+
const result = evaluateExpression(value, environment, fallback, this.random);
|
|
820
|
+
cache.set(key, result);
|
|
821
|
+
return result;
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
var RuntimeBase = class {
|
|
825
|
+
constructor(definition, random) {
|
|
826
|
+
this.definition = definition;
|
|
827
|
+
this.values = new ActionValues(random);
|
|
828
|
+
}
|
|
829
|
+
definition;
|
|
830
|
+
time = 0;
|
|
831
|
+
values;
|
|
832
|
+
init(context) {
|
|
833
|
+
this.time = 0;
|
|
834
|
+
this.values.init();
|
|
835
|
+
this.onInit(context);
|
|
836
|
+
}
|
|
837
|
+
hasNext(context) {
|
|
838
|
+
return this.baseHasNext(context) && this.hasMore(context);
|
|
839
|
+
}
|
|
840
|
+
step(context) {
|
|
841
|
+
this.values.initFrame();
|
|
842
|
+
const result = this.tick(context);
|
|
843
|
+
this.time += 1;
|
|
844
|
+
return result;
|
|
845
|
+
}
|
|
846
|
+
onInit(_context) {
|
|
847
|
+
}
|
|
848
|
+
hasMore(_context) {
|
|
849
|
+
return true;
|
|
850
|
+
}
|
|
851
|
+
baseHasNext(context) {
|
|
852
|
+
const condition = this.values.boolean("condition", this.definition.condition, context.environment, true);
|
|
853
|
+
const duration = Math.trunc(this.values.number("duration", this.definition.duration, context.environment, Number.POSITIVE_INFINITY));
|
|
854
|
+
return condition && this.time < duration;
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
var TrackedBorder = class {
|
|
858
|
+
constructor(side, source, context) {
|
|
859
|
+
this.side = side;
|
|
860
|
+
this.source = source;
|
|
861
|
+
this.previous = this.rectangle(context);
|
|
862
|
+
}
|
|
863
|
+
side;
|
|
864
|
+
source;
|
|
865
|
+
previous;
|
|
866
|
+
move(point, context) {
|
|
867
|
+
const current = this.rectangle(context);
|
|
868
|
+
const previous = this.previous;
|
|
869
|
+
this.previous = current;
|
|
870
|
+
if (!current || !previous) return point;
|
|
871
|
+
if (this.side === "left" || this.side === "right") {
|
|
872
|
+
if (previous.height === 0) return point;
|
|
873
|
+
const next2 = {
|
|
874
|
+
x: point.x + this.coordinate(current) - this.coordinate(previous),
|
|
875
|
+
y: Math.trunc((point.y - previous.y) * current.height / previous.height + current.y)
|
|
876
|
+
};
|
|
877
|
+
return Math.abs(next2.x - point.x) >= 80 || Math.abs(next2.y - point.y) >= 80 ? point : next2;
|
|
878
|
+
}
|
|
879
|
+
if (previous.width === 0) return point;
|
|
880
|
+
const next = {
|
|
881
|
+
// FloorCeiling.java performs integer division before applying the
|
|
882
|
+
// mascot's relative offset along a resized border.
|
|
883
|
+
x: (point.x - previous.x) * Math.trunc(current.width / previous.width) + current.x,
|
|
884
|
+
y: point.y + this.coordinate(current) - this.coordinate(previous)
|
|
885
|
+
};
|
|
886
|
+
return Math.abs(next.x - point.x) >= 80 || next.y - point.y > 20 || next.y - point.y < -80 ? point : next;
|
|
887
|
+
}
|
|
888
|
+
isOn(point, context) {
|
|
889
|
+
const rectangle = this.rectangle(context);
|
|
890
|
+
if (!rectangle) return false;
|
|
891
|
+
switch (this.side) {
|
|
892
|
+
case "top":
|
|
893
|
+
return isOnTop(point, rectangle);
|
|
894
|
+
case "bottom":
|
|
895
|
+
return isOnBottom(point, rectangle);
|
|
896
|
+
case "left":
|
|
897
|
+
return isOnLeft(point, rectangle);
|
|
898
|
+
case "right":
|
|
899
|
+
return isOnRight(point, rectangle);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
coordinate(rectangle) {
|
|
903
|
+
switch (this.side) {
|
|
904
|
+
case "top":
|
|
905
|
+
return rectangle.y;
|
|
906
|
+
case "bottom":
|
|
907
|
+
return rectangle.y + rectangle.height;
|
|
908
|
+
case "left":
|
|
909
|
+
return rectangle.x;
|
|
910
|
+
case "right":
|
|
911
|
+
return rectangle.x + rectangle.width;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
rectangle(context) {
|
|
915
|
+
if (this.source === "work-area") return context.bounds;
|
|
916
|
+
if (!this.source) return void 0;
|
|
917
|
+
return context.platforms.find((platform) => platform.element === this.source);
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
function selectBorder(type, state, context) {
|
|
921
|
+
if (type === "Floor") {
|
|
922
|
+
const platform2 = context.platforms.find((candidate) => isOnTop(state, candidate));
|
|
923
|
+
if (platform2) return new TrackedBorder("top", platform2.element, context);
|
|
924
|
+
if (isOnBottom(state, context.bounds)) return new TrackedBorder("bottom", "work-area", context);
|
|
925
|
+
return new TrackedBorder("bottom", void 0, context);
|
|
926
|
+
}
|
|
927
|
+
if (type === "Ceiling") {
|
|
928
|
+
const platform2 = context.platforms.find((candidate) => isOnBottom(state, candidate));
|
|
929
|
+
if (platform2) return new TrackedBorder("bottom", platform2.element, context);
|
|
930
|
+
if (isOnTop(state, context.bounds)) return new TrackedBorder("top", "work-area", context);
|
|
931
|
+
return new TrackedBorder("top", void 0, context);
|
|
932
|
+
}
|
|
933
|
+
if (state.lookRight) {
|
|
934
|
+
const platform2 = context.platforms.find((candidate) => isOnLeft(state, candidate));
|
|
935
|
+
if (platform2) return new TrackedBorder("left", platform2.element, context);
|
|
936
|
+
if (isOnRight(state, context.bounds)) return new TrackedBorder("right", "work-area", context);
|
|
937
|
+
return new TrackedBorder("right", void 0, context);
|
|
938
|
+
}
|
|
939
|
+
const platform = context.platforms.find((candidate) => isOnRight(state, candidate));
|
|
940
|
+
if (platform) return new TrackedBorder("right", platform.element, context);
|
|
941
|
+
if (isOnLeft(state, context.bounds)) return new TrackedBorder("left", "work-area", context);
|
|
942
|
+
return new TrackedBorder("left", void 0, context);
|
|
754
943
|
}
|
|
755
|
-
var
|
|
756
|
-
constructor(
|
|
757
|
-
|
|
758
|
-
this.
|
|
759
|
-
this.loop = loop;
|
|
944
|
+
var AnimatedRuntime = class extends RuntimeBase {
|
|
945
|
+
constructor(definition, state, random) {
|
|
946
|
+
super(definition, random);
|
|
947
|
+
this.state = state;
|
|
760
948
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
949
|
+
state;
|
|
950
|
+
border;
|
|
951
|
+
onInit(context) {
|
|
952
|
+
this.border = this.definition.borderType ? selectBorder(this.definition.borderType, this.state, context) : void 0;
|
|
953
|
+
}
|
|
954
|
+
animation(context, turn) {
|
|
955
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
956
|
+
return this.definition.animations?.find((animation, index) => (turn === void 0 || Boolean(animation.turn) === turn) && this.values.boolean(`animation-${index}`, animation.condition, scoped, true));
|
|
957
|
+
}
|
|
958
|
+
animationDuration(context, turn) {
|
|
959
|
+
return this.animation(context, turn)?.poses.reduce((sum, pose) => sum + Math.max(0, pose.duration), 0) ?? 0;
|
|
960
|
+
}
|
|
961
|
+
applyBorder(context) {
|
|
962
|
+
if (!this.border) return "running";
|
|
963
|
+
const moved = this.border.move(this.state, context);
|
|
964
|
+
this.state.x = moved.x;
|
|
965
|
+
this.state.y = moved.y;
|
|
966
|
+
return this.border.isOn(this.state, context) ? "running" : "lost-ground";
|
|
967
|
+
}
|
|
968
|
+
applyAnimation(context, turn) {
|
|
969
|
+
const animation = this.animation(context, turn);
|
|
970
|
+
const pose = animation && poseAt(animation, this.time);
|
|
971
|
+
if (pose) applyPose(this.state, pose);
|
|
972
|
+
}
|
|
973
|
+
scopedEnvironment(environment) {
|
|
974
|
+
const gap = this.values.number("gap", this.definition.gap, environment, 0);
|
|
975
|
+
const withGap = { ...environment, gap };
|
|
976
|
+
const targetX = this.definition.targetX === void 0 ? void 0 : this.values.number("targetX", this.definition.targetX, withGap, 0);
|
|
977
|
+
const targetY = this.definition.targetY === void 0 ? void 0 : this.values.number("targetY", this.definition.targetY, withGap, 0);
|
|
978
|
+
return { ...withGap, ...targetX !== void 0 && { targetX }, ...targetY !== void 0 && { targetY } };
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
function poseAt(animation, time) {
|
|
982
|
+
const duration = animation.poses.reduce((sum, pose) => sum + Math.max(0, pose.duration), 0);
|
|
983
|
+
if (duration <= 0) return void 0;
|
|
984
|
+
let cursor = time % duration;
|
|
985
|
+
for (const pose of animation.poses) {
|
|
986
|
+
cursor -= Math.max(0, pose.duration);
|
|
987
|
+
if (cursor < 0) return pose;
|
|
988
|
+
}
|
|
989
|
+
return animation.poses.at(-1);
|
|
990
|
+
}
|
|
991
|
+
function applyPose(state, pose) {
|
|
992
|
+
state.sprite = pose.sprite;
|
|
993
|
+
state.anchorX = pose.anchor.x;
|
|
994
|
+
state.anchorY = pose.anchor.y;
|
|
995
|
+
state.x += (state.lookRight ? -1 : 1) * pose.velocity.x;
|
|
996
|
+
state.y += pose.velocity.y;
|
|
997
|
+
}
|
|
998
|
+
var StayRuntime = class extends AnimatedRuntime {
|
|
999
|
+
tick(context) {
|
|
1000
|
+
const border = this.applyBorder(context);
|
|
1001
|
+
if (border === "lost-ground") return border;
|
|
1002
|
+
this.applyAnimation(context);
|
|
1003
|
+
return "running";
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1006
|
+
var AnimateRuntime = class extends StayRuntime {
|
|
1007
|
+
hasMore(context) {
|
|
1008
|
+
return this.time < this.animationDuration(context);
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
var MoveRuntime = class extends AnimatedRuntime {
|
|
1012
|
+
turning = false;
|
|
1013
|
+
hasTurningAnimation = false;
|
|
1014
|
+
onInit(context) {
|
|
1015
|
+
super.onInit(context);
|
|
1016
|
+
this.turning = false;
|
|
1017
|
+
this.hasTurningAnimation = this.definition.animations?.some((animation) => animation.turn) ?? false;
|
|
1018
|
+
}
|
|
1019
|
+
hasMore(context) {
|
|
1020
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
1021
|
+
const targetX = this.targetX(scoped);
|
|
1022
|
+
const targetY = this.targetY(scoped);
|
|
1023
|
+
const reached = targetX !== void 0 && this.state.x === targetX || targetY !== void 0 && this.state.y === targetY;
|
|
1024
|
+
return !reached || this.turning;
|
|
1025
|
+
}
|
|
1026
|
+
tick(context) {
|
|
1027
|
+
const border = this.applyBorder(context);
|
|
1028
|
+
if (border === "lost-ground") return border;
|
|
1029
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
1030
|
+
const targetX = this.targetX(scoped);
|
|
1031
|
+
const targetY = this.targetY(scoped);
|
|
1032
|
+
let down = false;
|
|
1033
|
+
if (targetX !== void 0 && this.state.x !== targetX) {
|
|
1034
|
+
const nextLookRight = this.state.x < targetX;
|
|
1035
|
+
this.turning = this.hasTurningAnimation && (this.turning || nextLookRight !== this.state.lookRight);
|
|
1036
|
+
this.state.lookRight = nextLookRight;
|
|
779
1037
|
}
|
|
780
|
-
|
|
1038
|
+
if (targetY !== void 0) down = this.state.y < targetY;
|
|
1039
|
+
if (this.turning && this.time >= this.animationDuration(context, true)) this.turning = false;
|
|
1040
|
+
this.applyAnimation(context, this.turning);
|
|
1041
|
+
if (targetX !== void 0 && (this.state.lookRight && this.state.x >= targetX || !this.state.lookRight && this.state.x <= targetX)) this.state.x = targetX;
|
|
1042
|
+
if (targetY !== void 0 && (down && this.state.y >= targetY || !down && this.state.y <= targetY)) this.state.y = targetY;
|
|
1043
|
+
return "running";
|
|
1044
|
+
}
|
|
1045
|
+
targetX(environment) {
|
|
1046
|
+
return this.definition.targetX === void 0 ? void 0 : Math.trunc(this.values.number("targetX", this.definition.targetX, environment, 0));
|
|
1047
|
+
}
|
|
1048
|
+
targetY(environment) {
|
|
1049
|
+
return this.definition.targetY === void 0 ? void 0 : Math.trunc(this.values.number("targetY", this.definition.targetY, environment, 0));
|
|
781
1050
|
}
|
|
782
1051
|
};
|
|
783
|
-
var
|
|
1052
|
+
var MoveWithTurnRuntime = class extends MoveRuntime {
|
|
1053
|
+
onInit(context) {
|
|
1054
|
+
super.onInit(context);
|
|
1055
|
+
this.hasTurningAnimation = (this.definition.animations?.length ?? 0) >= 2;
|
|
1056
|
+
}
|
|
1057
|
+
animation(context, turn) {
|
|
1058
|
+
const animations = this.definition.animations ?? [];
|
|
1059
|
+
if (turn) return animations.at(-1);
|
|
1060
|
+
const scoped = this.scopedEnvironment(context.environment);
|
|
1061
|
+
return animations.slice(0, -1).find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, scoped, true));
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
var TurnRuntime = class extends AnimatedRuntime {
|
|
1065
|
+
turning = false;
|
|
1066
|
+
hasMore(context) {
|
|
1067
|
+
const desired = this.values.boolean("lookRight", this.definition.lookRight, context.environment, !this.state.lookRight);
|
|
1068
|
+
this.turning ||= desired !== this.state.lookRight;
|
|
1069
|
+
return this.turning && this.time < this.animationDuration(context);
|
|
1070
|
+
}
|
|
1071
|
+
tick(context) {
|
|
1072
|
+
this.state.lookRight = this.values.boolean("lookRight", this.definition.lookRight, context.environment, !this.state.lookRight);
|
|
1073
|
+
const border = this.applyBorder(context);
|
|
1074
|
+
if (border === "lost-ground") return border;
|
|
1075
|
+
this.applyAnimation(context);
|
|
1076
|
+
return "running";
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
var InstantRuntime = class extends RuntimeBase {
|
|
1080
|
+
constructor(definition, state, random, operation) {
|
|
1081
|
+
super(definition, random);
|
|
1082
|
+
this.state = state;
|
|
1083
|
+
this.operation = operation;
|
|
1084
|
+
}
|
|
1085
|
+
state;
|
|
1086
|
+
operation;
|
|
1087
|
+
onInit(context) {
|
|
1088
|
+
if (!this.baseHasNext(context)) return;
|
|
1089
|
+
if (this.operation === "look") {
|
|
1090
|
+
this.state.lookRight = this.values.boolean("lookRight", this.definition.lookRight, context.environment, !this.state.lookRight);
|
|
1091
|
+
} else if (this.operation === "offset") {
|
|
1092
|
+
this.state.x += Math.trunc(this.values.number("x", this.definition.x, context.environment, 0));
|
|
1093
|
+
this.state.y += Math.trunc(this.values.number("y", this.definition.y, context.environment, 0));
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
hasMore() {
|
|
1097
|
+
return false;
|
|
1098
|
+
}
|
|
784
1099
|
tick() {
|
|
785
|
-
return
|
|
1100
|
+
return "running";
|
|
786
1101
|
}
|
|
787
1102
|
};
|
|
788
|
-
var
|
|
789
|
-
constructor(
|
|
790
|
-
|
|
1103
|
+
var JumpRuntime = class extends RuntimeBase {
|
|
1104
|
+
constructor(definition, state, random) {
|
|
1105
|
+
super(definition, random);
|
|
791
1106
|
this.state = state;
|
|
792
|
-
this.options = options;
|
|
793
|
-
this.callbacks = callbacks;
|
|
794
|
-
const animationEnvironment = { ...environment, ...action.targetX !== void 0 && { targetX: action.targetX }, ...action.targetY !== void 0 && { targetY: action.targetY } };
|
|
795
|
-
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];
|
|
796
|
-
this.poseDuration = this.animation?.poses.reduce((sum, pose) => sum + Math.max(0, pose.duration), 0) ?? 0;
|
|
797
1107
|
}
|
|
798
|
-
action;
|
|
799
1108
|
state;
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
this.
|
|
811
|
-
this.state.
|
|
812
|
-
|
|
1109
|
+
hasMore(context) {
|
|
1110
|
+
return this.distance(context).distance !== 0;
|
|
1111
|
+
}
|
|
1112
|
+
tick(context) {
|
|
1113
|
+
const { targetX, targetY, distanceX, distanceY, distance } = this.distance(context);
|
|
1114
|
+
this.state.lookRight = this.state.x < targetX;
|
|
1115
|
+
const velocity = this.values.number("velocity", this.definition.velocity, context.environment, 20);
|
|
1116
|
+
if (distance !== 0) {
|
|
1117
|
+
this.state.vx = velocity * distanceX / distance;
|
|
1118
|
+
this.state.vy = velocity * distanceY / distance;
|
|
1119
|
+
this.state.x += Math.trunc(this.state.vx);
|
|
1120
|
+
this.state.y += Math.trunc(this.state.vy);
|
|
1121
|
+
const environment = { ...context.environment, targetX, targetY };
|
|
1122
|
+
const animation = this.definition.animations?.find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, environment, true));
|
|
1123
|
+
const pose = animation && poseAt(animation, this.time);
|
|
1124
|
+
if (pose) applyPose(this.state, pose);
|
|
813
1125
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
const { definition } = this.action;
|
|
818
|
-
if (definition.type === "Embedded") return this.tickEmbedded(frameScale, environment, bounds);
|
|
819
|
-
if (definition.type === "Move" && (this.action.targetX !== void 0 || this.action.targetY !== void 0)) {
|
|
820
|
-
const reachedX = this.action.targetX === void 0 || Math.abs(this.state.x - this.action.targetX) < 0.5;
|
|
821
|
-
const reachedY = this.action.targetY === void 0 || Math.abs(this.state.y - this.action.targetY) < 0.5;
|
|
822
|
-
return reachedX && reachedY;
|
|
1126
|
+
if (distance <= velocity) {
|
|
1127
|
+
this.state.x = targetX;
|
|
1128
|
+
this.state.y = targetY;
|
|
823
1129
|
}
|
|
824
|
-
return
|
|
1130
|
+
return "running";
|
|
825
1131
|
}
|
|
826
|
-
|
|
827
|
-
const
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
1132
|
+
distance(context) {
|
|
1133
|
+
const targetX = Math.trunc(this.values.number("targetX", this.definition.targetX, context.environment, 0));
|
|
1134
|
+
const targetY = Math.trunc(this.values.number("targetY", this.definition.targetY, context.environment, 0));
|
|
1135
|
+
const distanceX = targetX - this.state.x;
|
|
1136
|
+
const distanceY = targetY - this.state.y - Math.abs(distanceX) / 2;
|
|
1137
|
+
return { targetX, targetY, distanceX, distanceY, distance: Math.hypot(distanceX, distanceY) };
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
var FallRuntime = class extends RuntimeBase {
|
|
1141
|
+
constructor(definition, state, random, defaultGravity) {
|
|
1142
|
+
super(definition, random);
|
|
1143
|
+
this.state = state;
|
|
1144
|
+
this.defaultGravity = defaultGravity;
|
|
1145
|
+
}
|
|
1146
|
+
state;
|
|
1147
|
+
defaultGravity;
|
|
1148
|
+
modX = 0;
|
|
1149
|
+
modY = 0;
|
|
1150
|
+
onInit(context) {
|
|
1151
|
+
this.modX = 0;
|
|
1152
|
+
this.modY = 0;
|
|
1153
|
+
this.state.vx = Math.trunc(this.values.number("initialVx", this.definition.initialVx, context.environment, 0));
|
|
1154
|
+
this.state.vy = Math.trunc(this.values.number("initialVy", this.definition.initialVy, context.environment, 0));
|
|
1155
|
+
}
|
|
1156
|
+
hasMore(context) {
|
|
1157
|
+
return !isOnFloor(this.state, context.bounds, context.platforms) && !isOnWall(this.state, context.bounds, this.state.lookRight, context.platforms);
|
|
1158
|
+
}
|
|
1159
|
+
tick(context) {
|
|
1160
|
+
if (this.state.vx !== 0) this.state.lookRight = this.state.vx > 0;
|
|
1161
|
+
const resistanceX = this.values.number("resistanceX", this.definition.resistanceX, context.environment, 0.05);
|
|
1162
|
+
const resistanceY = this.values.number("resistanceY", this.definition.resistanceY, context.environment, 0.1);
|
|
1163
|
+
const gravity = this.values.number("gravity", this.definition.gravity, context.environment, this.defaultGravity);
|
|
1164
|
+
this.state.vx -= this.state.vx * resistanceX;
|
|
1165
|
+
this.state.vy = this.state.vy - this.state.vy * resistanceY + gravity;
|
|
1166
|
+
this.modX += this.state.vx % 1;
|
|
1167
|
+
this.modY += this.state.vy % 1;
|
|
1168
|
+
const dx = Math.trunc(this.state.vx) + Math.trunc(this.modX);
|
|
1169
|
+
const dy = Math.trunc(this.state.vy) + Math.trunc(this.modY);
|
|
1170
|
+
this.modX %= 1;
|
|
1171
|
+
this.modY %= 1;
|
|
1172
|
+
const divisions = Math.max(1, Math.abs(dx), Math.abs(dy));
|
|
1173
|
+
const start = { x: this.state.x, y: this.state.y };
|
|
1174
|
+
let stopped = false;
|
|
1175
|
+
for (let index = 0; index <= divisions; index += 1) {
|
|
1176
|
+
const x = start.x + Math.trunc(dx * index / divisions);
|
|
1177
|
+
const y = start.y + Math.trunc(dy * index / divisions);
|
|
1178
|
+
this.state.x = x;
|
|
1179
|
+
this.state.y = y;
|
|
1180
|
+
if (dy > 0) {
|
|
1181
|
+
for (let offset = -80; offset <= 0; offset += 1) {
|
|
1182
|
+
this.state.y = y + offset;
|
|
1183
|
+
if (isOnFloor(this.state, context.bounds, context.platforms)) {
|
|
1184
|
+
stopped = true;
|
|
1185
|
+
break;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
if (stopped) break;
|
|
1189
|
+
this.state.y = y;
|
|
1190
|
+
}
|
|
1191
|
+
if (isOnWall(this.state, context.bounds, this.state.lookRight, context.platforms)) break;
|
|
835
1192
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1193
|
+
const fallEnvironment = { ...context.environment, velocityX: this.state.vx, velocityY: this.state.vy };
|
|
1194
|
+
const animation = this.definition.animations?.find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, fallEnvironment, true));
|
|
1195
|
+
const pose = animation && poseAt(animation, this.time);
|
|
1196
|
+
if (pose) applyPose(this.state, pose);
|
|
1197
|
+
return "running";
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
function matchingActivePlatform(context) {
|
|
1201
|
+
const active = context.environment.mascot.environment.activeIE;
|
|
1202
|
+
if (!active.visible) return void 0;
|
|
1203
|
+
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);
|
|
1204
|
+
}
|
|
1205
|
+
function carryRelation(state, platform, offsetX, offsetY) {
|
|
1206
|
+
const grip = {
|
|
1207
|
+
x: state.x + (state.lookRight ? -offsetX : offsetX),
|
|
1208
|
+
y: state.y + offsetY
|
|
1209
|
+
};
|
|
1210
|
+
return isOnBottom(grip, platform) && (state.lookRight ? isOnLeft(grip, platform) : isOnRight(grip, platform));
|
|
1211
|
+
}
|
|
1212
|
+
function carriedPlatformPosition(state, platform, offsetX, offsetY) {
|
|
1213
|
+
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 };
|
|
1214
|
+
}
|
|
1215
|
+
var CarryFallRuntime = class extends FallRuntime {
|
|
1216
|
+
constructor(definition, state, random, gravity, callbacks) {
|
|
1217
|
+
super(definition, state, random, gravity);
|
|
1218
|
+
this.callbacks = callbacks;
|
|
1219
|
+
}
|
|
1220
|
+
callbacks;
|
|
1221
|
+
element;
|
|
1222
|
+
onInit(context) {
|
|
1223
|
+
super.onInit(context);
|
|
1224
|
+
this.element = matchingActivePlatform(context)?.element;
|
|
1225
|
+
}
|
|
1226
|
+
tick(context) {
|
|
1227
|
+
const platform = context.platforms.find((candidate) => candidate.element === this.element);
|
|
1228
|
+
const offsetX = Math.trunc(this.values.number("ieOffsetX", this.definition.ieOffsetX, context.environment, 0));
|
|
1229
|
+
const offsetY = Math.trunc(this.values.number("ieOffsetY", this.definition.ieOffsetY, context.environment, 0));
|
|
1230
|
+
if (!platform || !carryRelation(this.state, platform, offsetX, offsetY)) return "lost-ground";
|
|
1231
|
+
const result = super.tick(context);
|
|
1232
|
+
this.callbacks.movePlatform?.(platform.element, carriedPlatformPosition(this.state, platform, offsetX, offsetY));
|
|
1233
|
+
return result;
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
var CarryMoveRuntime = class extends MoveRuntime {
|
|
1237
|
+
constructor(definition, state, random, callbacks) {
|
|
1238
|
+
super(definition, state, random);
|
|
1239
|
+
this.callbacks = callbacks;
|
|
1240
|
+
}
|
|
1241
|
+
callbacks;
|
|
1242
|
+
element;
|
|
1243
|
+
onInit(context) {
|
|
1244
|
+
super.onInit(context);
|
|
1245
|
+
this.element = matchingActivePlatform(context)?.element;
|
|
1246
|
+
}
|
|
1247
|
+
tick(context) {
|
|
1248
|
+
const platform = context.platforms.find((candidate) => candidate.element === this.element);
|
|
1249
|
+
const offsetX = Math.trunc(this.values.number("ieOffsetX", this.definition.ieOffsetX, context.environment, 0));
|
|
1250
|
+
const offsetY = Math.trunc(this.values.number("ieOffsetY", this.definition.ieOffsetY, context.environment, 0));
|
|
1251
|
+
if (!platform || !carryRelation(this.state, platform, offsetX, offsetY)) return "lost-ground";
|
|
1252
|
+
const result = super.tick(context);
|
|
1253
|
+
this.callbacks.movePlatform?.(platform.element, carriedPlatformPosition(this.state, platform, offsetX, offsetY));
|
|
1254
|
+
return result;
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
var ThrowPlatformRuntime = class extends AnimateRuntime {
|
|
1258
|
+
constructor(definition, state, random, callbacks) {
|
|
1259
|
+
super(definition, state, random);
|
|
1260
|
+
this.callbacks = callbacks;
|
|
1261
|
+
}
|
|
1262
|
+
callbacks;
|
|
1263
|
+
element;
|
|
1264
|
+
onInit(context) {
|
|
1265
|
+
super.onInit(context);
|
|
1266
|
+
this.element = matchingActivePlatform(context)?.element;
|
|
1267
|
+
}
|
|
1268
|
+
tick(context) {
|
|
1269
|
+
const result = super.tick(context);
|
|
1270
|
+
const platform = context.platforms.find((candidate) => candidate.element === this.element);
|
|
1271
|
+
if (platform) {
|
|
1272
|
+
const vx = Math.trunc(this.values.number("initialVx", this.definition.initialVx, context.environment, 32));
|
|
1273
|
+
const vy = Math.trunc(this.values.number("initialVy", this.definition.initialVy, context.environment, -10));
|
|
1274
|
+
const gravity = this.values.number("gravity", this.definition.gravity, context.environment, 0.5);
|
|
1275
|
+
this.callbacks.movePlatform?.(platform.element, {
|
|
1276
|
+
x: platform.x + (this.state.lookRight ? vx : -vx),
|
|
1277
|
+
y: platform.y + vy + Math.trunc(this.time * gravity)
|
|
1278
|
+
});
|
|
839
1279
|
}
|
|
1280
|
+
return result;
|
|
840
1281
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
case "Reboot":
|
|
871
|
-
this.state.x = bounds.x + 128 + Math.max(0, bounds.width - 256) * Math.random();
|
|
872
|
-
this.state.y = bounds.y + 128 + Math.max(0, bounds.height - 256) * Math.random();
|
|
873
|
-
return true;
|
|
874
|
-
case "Offset":
|
|
875
|
-
case "Look":
|
|
876
|
-
return true;
|
|
877
|
-
case "Dragged":
|
|
878
|
-
return false;
|
|
879
|
-
default:
|
|
880
|
-
return this.elapsedMs >= this.durationMs();
|
|
1282
|
+
};
|
|
1283
|
+
function breed(definition, state, values, context, callbacks) {
|
|
1284
|
+
const bornX = Math.trunc(values.number("bornX", definition.bornX, context.environment, 0));
|
|
1285
|
+
const bornY = Math.trunc(values.number("bornY", definition.bornY, context.environment, 0));
|
|
1286
|
+
const count = Math.trunc(values.number("bornCount", definition.bornCount, context.environment, 1));
|
|
1287
|
+
if (count < 1) throw new RangeError("BornCount must be positive");
|
|
1288
|
+
for (let index = 0; index < count; index += 1) {
|
|
1289
|
+
callbacks.spawn({
|
|
1290
|
+
x: state.x + (state.lookRight ? -bornX : bornX),
|
|
1291
|
+
y: state.y + bornY,
|
|
1292
|
+
lookRight: state.lookRight,
|
|
1293
|
+
...definition.bornBehavior && { behaviorName: definition.bornBehavior }
|
|
1294
|
+
}, definition.bornMascot);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
var BreedRuntime = class extends AnimateRuntime {
|
|
1298
|
+
constructor(definition, state, random, callbacks) {
|
|
1299
|
+
super(definition, state, random);
|
|
1300
|
+
this.callbacks = callbacks;
|
|
1301
|
+
}
|
|
1302
|
+
callbacks;
|
|
1303
|
+
spawned = false;
|
|
1304
|
+
tick(context) {
|
|
1305
|
+
const result = super.tick(context);
|
|
1306
|
+
if (result === "lost-ground") return result;
|
|
1307
|
+
const duration = this.animationDuration(context);
|
|
1308
|
+
if (!this.spawned && this.time === duration - 1) {
|
|
1309
|
+
this.spawned = true;
|
|
1310
|
+
breed(this.definition, this.state, this.values, context, this.callbacks);
|
|
881
1311
|
}
|
|
1312
|
+
return result;
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
var BreedMoveRuntime = class extends MoveRuntime {
|
|
1316
|
+
constructor(definition, state, random, callbacks) {
|
|
1317
|
+
super(definition, state, random);
|
|
1318
|
+
this.callbacks = callbacks;
|
|
1319
|
+
}
|
|
1320
|
+
callbacks;
|
|
1321
|
+
tick(context) {
|
|
1322
|
+
const result = super.tick(context);
|
|
1323
|
+
if (result === "lost-ground") return result;
|
|
1324
|
+
const interval = Math.trunc(this.values.number("bornInterval", this.definition.bornInterval, context.environment, 1));
|
|
1325
|
+
if (interval < 1) throw new RangeError("BornInterval must be positive");
|
|
1326
|
+
if (this.time % interval === 0 && !this.turning) breed(this.definition, this.state, this.values, context, this.callbacks);
|
|
1327
|
+
return result;
|
|
1328
|
+
}
|
|
1329
|
+
};
|
|
1330
|
+
var BreedJumpRuntime = class extends JumpRuntime {
|
|
1331
|
+
constructor(definition, breedState, random, callbacks) {
|
|
1332
|
+
super(definition, breedState, random);
|
|
1333
|
+
this.breedState = breedState;
|
|
1334
|
+
this.callbacks = callbacks;
|
|
882
1335
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1336
|
+
breedState;
|
|
1337
|
+
callbacks;
|
|
1338
|
+
tick(context) {
|
|
1339
|
+
const result = super.tick(context);
|
|
1340
|
+
const interval = Math.trunc(this.values.number("bornInterval", this.definition.bornInterval, context.environment, 1));
|
|
1341
|
+
if (interval < 1) throw new RangeError("BornInterval must be positive");
|
|
1342
|
+
if (this.time % interval === 0) breed(this.definition, this.breedState, this.values, context, this.callbacks);
|
|
1343
|
+
return result;
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
var DraggedRuntime = class extends RuntimeBase {
|
|
1347
|
+
constructor(definition, state, random, spec) {
|
|
1348
|
+
super(definition, random);
|
|
1349
|
+
this.state = state;
|
|
1350
|
+
this.spec = spec;
|
|
1351
|
+
}
|
|
1352
|
+
state;
|
|
1353
|
+
spec;
|
|
1354
|
+
footX = 0;
|
|
1355
|
+
footDx = 0;
|
|
1356
|
+
timeToRegist = 250;
|
|
1357
|
+
onInit(context) {
|
|
1358
|
+
this.footDx = 0;
|
|
1359
|
+
this.timeToRegist = 250;
|
|
1360
|
+
this.footX = context.environment.mascot.environment.cursor.x + this.offsetX(context);
|
|
1361
|
+
}
|
|
1362
|
+
hasMore() {
|
|
1363
|
+
return this.time < this.timeToRegist;
|
|
1364
|
+
}
|
|
1365
|
+
tick(context) {
|
|
1366
|
+
this.state.lookRight = false;
|
|
1367
|
+
this.state.dragging = true;
|
|
1368
|
+
const cursor = context.environment.mascot.environment.cursor;
|
|
1369
|
+
const offsetX = this.offsetX(context);
|
|
1370
|
+
const offsetY = this.offsetY(context);
|
|
1371
|
+
if (Math.abs(cursor.x - this.state.x + offsetX) >= 5) this.time = 0;
|
|
1372
|
+
this.footDx = (this.footDx + (cursor.x - this.footX) * 0.1) * 0.8;
|
|
1373
|
+
this.footX += this.footDx;
|
|
1374
|
+
const environment = { ...context.environment, footX: this.footX };
|
|
1375
|
+
const animation = this.definition.animations?.find((candidate, index) => this.values.boolean(`animation-${index}`, candidate.condition, environment, true));
|
|
1376
|
+
const pose = animation && poseAt(animation, this.time);
|
|
1377
|
+
if (pose) applyPose(this.state, pose);
|
|
1378
|
+
this.state.x = cursor.x + offsetX;
|
|
1379
|
+
this.state.y = cursor.y + offsetY;
|
|
1380
|
+
if (this.time === this.timeToRegist - 1 && this.values.number(`regist-${this.time}`, "#{Math.random()}", environment, 0) >= 0.1) this.timeToRegist += 1;
|
|
1381
|
+
return "running";
|
|
1382
|
+
}
|
|
1383
|
+
offsetX(context) {
|
|
1384
|
+
const offset = Math.trunc(this.values.number("offsetX", this.definition.offsetX, context.environment, 0));
|
|
1385
|
+
return this.definition.offsetType === "Origin" ? -offset + this.spriteCenter().x : offset;
|
|
1386
|
+
}
|
|
1387
|
+
offsetY(context) {
|
|
1388
|
+
const offset = Math.trunc(this.values.number("offsetY", this.definition.offsetY, context.environment, 120));
|
|
1389
|
+
return this.definition.offsetType === "Origin" ? -offset + this.spriteCenter().y : offset;
|
|
1390
|
+
}
|
|
1391
|
+
spriteCenter() {
|
|
1392
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1393
|
+
const width = typeof sprite === "object" ? sprite.width ?? 128 : 128;
|
|
1394
|
+
return { x: this.state.lookRight ? width - this.state.anchorX : this.state.anchorX, y: this.state.anchorY };
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
var RegistRuntime = class extends AnimatedRuntime {
|
|
1398
|
+
constructor(definition, state, random, spec) {
|
|
1399
|
+
super(definition, state, random);
|
|
1400
|
+
this.spec = spec;
|
|
1401
|
+
}
|
|
1402
|
+
spec;
|
|
1403
|
+
hasMore(context) {
|
|
1404
|
+
const cursor = context.environment.mascot.environment.cursor;
|
|
1405
|
+
const rawOffset = Math.trunc(this.values.number("offsetX", this.definition.offsetX, context.environment, 0));
|
|
1406
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1407
|
+
const width = typeof sprite === "object" ? sprite.width ?? 128 : 128;
|
|
1408
|
+
const centerX = this.state.lookRight ? width - this.state.anchorX : this.state.anchorX;
|
|
1409
|
+
const offsetX = this.definition.offsetType === "Origin" ? -rawOffset + centerX : rawOffset;
|
|
1410
|
+
return Math.abs(cursor.x - this.state.x + offsetX) < 5;
|
|
1411
|
+
}
|
|
1412
|
+
tick(context) {
|
|
1413
|
+
this.state.dragging = true;
|
|
1414
|
+
this.applyAnimation(context);
|
|
1415
|
+
if (this.time + 1 >= this.animationDuration(context)) {
|
|
1416
|
+
this.state.lookRight = this.values.number(`look-${this.time}`, "#{Math.random()}", context.environment, 0) < 0.5;
|
|
1417
|
+
return "lost-ground";
|
|
889
1418
|
}
|
|
890
|
-
return
|
|
1419
|
+
return "running";
|
|
891
1420
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1421
|
+
};
|
|
1422
|
+
var SelfDestructRuntime = class extends AnimateRuntime {
|
|
1423
|
+
constructor(definition, state, random, callbacks) {
|
|
1424
|
+
super(definition, state, random);
|
|
1425
|
+
this.callbacks = callbacks;
|
|
1426
|
+
}
|
|
1427
|
+
callbacks;
|
|
1428
|
+
tick(context) {
|
|
1429
|
+
const result = super.tick(context);
|
|
1430
|
+
if (this.time === this.animationDuration(context) - 1) this.callbacks.remove();
|
|
1431
|
+
return result;
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
var ComplexRuntime = class extends RuntimeBase {
|
|
1435
|
+
constructor(definition, random, factory, selectOnly) {
|
|
1436
|
+
super(definition, random);
|
|
1437
|
+
this.factory = factory;
|
|
1438
|
+
this.selectOnly = selectOnly;
|
|
899
1439
|
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1440
|
+
factory;
|
|
1441
|
+
selectOnly;
|
|
1442
|
+
index = 0;
|
|
1443
|
+
child;
|
|
1444
|
+
selectionMade = false;
|
|
1445
|
+
onInit(context) {
|
|
1446
|
+
this.index = 0;
|
|
1447
|
+
this.child = void 0;
|
|
1448
|
+
this.selectionMade = false;
|
|
1449
|
+
if (this.baseHasNext(context)) this.seek(context);
|
|
1450
|
+
}
|
|
1451
|
+
hasMore(context) {
|
|
1452
|
+
if (!this.selectOnly) this.seek(context);
|
|
1453
|
+
return this.child?.hasNext(context) ?? false;
|
|
1454
|
+
}
|
|
1455
|
+
tick(context) {
|
|
1456
|
+
return this.child?.hasNext(context) ? this.child.step(context) : "running";
|
|
1457
|
+
}
|
|
1458
|
+
seek(context) {
|
|
1459
|
+
const definitions = this.definition.actions ?? [];
|
|
1460
|
+
if (definitions.length === 0) return;
|
|
1461
|
+
for (let guard = 0; guard <= definitions.length; guard += 1) {
|
|
1462
|
+
if (this.child?.hasNext(context)) {
|
|
1463
|
+
this.selectionMade = true;
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
if (this.selectOnly && this.selectionMade) {
|
|
1467
|
+
this.child = void 0;
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
if (this.index >= definitions.length) {
|
|
1471
|
+
if (this.definition.loop !== true) {
|
|
1472
|
+
this.child = void 0;
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
this.index = 0;
|
|
1476
|
+
}
|
|
1477
|
+
const definition = definitions[this.index++];
|
|
1478
|
+
if (!definition) {
|
|
1479
|
+
this.child = void 0;
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
this.child = this.factory(definition, context);
|
|
1483
|
+
this.child.init(context);
|
|
1484
|
+
}
|
|
1485
|
+
this.child = void 0;
|
|
903
1486
|
}
|
|
904
1487
|
};
|
|
905
1488
|
var ActionExecutor = class {
|
|
906
|
-
/** Creates an executor bound to one mascot's mutable internal state. */
|
|
907
1489
|
constructor(spec, state, options, callbacks) {
|
|
908
1490
|
this.spec = spec;
|
|
909
1491
|
this.state = state;
|
|
910
1492
|
this.options = options;
|
|
911
1493
|
this.callbacks = callbacks;
|
|
1494
|
+
this.random = options.random ?? Math.random;
|
|
912
1495
|
}
|
|
913
1496
|
spec;
|
|
914
1497
|
state;
|
|
915
1498
|
options;
|
|
916
1499
|
callbacks;
|
|
917
1500
|
runtime;
|
|
1501
|
+
accumulator = 0;
|
|
1502
|
+
random;
|
|
918
1503
|
/** Starts the action whose name matches a selected behavior. */
|
|
919
|
-
start(actionName, environment) {
|
|
1504
|
+
start(actionName, environment, _preserveLookRight = false, bounds = environment.mascot.environment.workArea, platforms = []) {
|
|
920
1505
|
const definition = this.spec.actions.find((action) => action.name === actionName);
|
|
921
|
-
this.
|
|
922
|
-
|
|
1506
|
+
this.accumulator = 0;
|
|
1507
|
+
if (!definition) {
|
|
1508
|
+
this.runtime = void 0;
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
const context = { environment, bounds, platforms };
|
|
1512
|
+
this.runtime = this.createRuntime(definition, context, /* @__PURE__ */ new Set());
|
|
1513
|
+
this.runtime.init(context);
|
|
1514
|
+
return true;
|
|
923
1515
|
}
|
|
924
|
-
/** Advances
|
|
925
|
-
tick(deltaMs, environment, bounds) {
|
|
926
|
-
|
|
1516
|
+
/** Advances by elapsed milliseconds and reports completion. */
|
|
1517
|
+
tick(deltaMs, environment, bounds, platforms = []) {
|
|
1518
|
+
this.accumulator += Math.max(0, deltaMs);
|
|
1519
|
+
let result = this.runtime?.hasNext({ environment, bounds, platforms }) ? "running" : "complete";
|
|
1520
|
+
while (this.accumulator >= this.options.frameDuration && result === "running") {
|
|
1521
|
+
this.accumulator -= this.options.frameDuration;
|
|
1522
|
+
result = this.step(environment, bounds, platforms);
|
|
1523
|
+
}
|
|
1524
|
+
return result !== "running";
|
|
1525
|
+
}
|
|
1526
|
+
/** Advances exactly one legacy frame. */
|
|
1527
|
+
step(environment, bounds, platforms = []) {
|
|
1528
|
+
const context = { environment, bounds, platforms };
|
|
1529
|
+
if (!this.runtime?.hasNext(context)) return "complete";
|
|
1530
|
+
const result = this.runtime.step(context);
|
|
1531
|
+
if (result === "lost-ground") return result;
|
|
1532
|
+
return this.runtime.hasNext(context) ? "running" : "complete";
|
|
1533
|
+
}
|
|
1534
|
+
/** Returns whether the current action can execute another legacy frame. */
|
|
1535
|
+
hasNext(environment, bounds, platforms = []) {
|
|
1536
|
+
return this.runtime?.hasNext({ environment, bounds, platforms }) ?? false;
|
|
1537
|
+
}
|
|
1538
|
+
/** Retained for source compatibility with the previous collision API. */
|
|
1539
|
+
consumeViewportWallCollision() {
|
|
1540
|
+
return false;
|
|
927
1541
|
}
|
|
928
1542
|
/** Cancels the current action tree. */
|
|
929
1543
|
cancel() {
|
|
930
1544
|
this.runtime = void 0;
|
|
1545
|
+
this.accumulator = 0;
|
|
931
1546
|
}
|
|
932
|
-
createRuntime(definition,
|
|
933
|
-
if (definition.condition && !evaluateExpression(definition.condition, environment, false)) return new CompleteRuntime();
|
|
934
|
-
const bounds = environment.mascot.environment.workArea;
|
|
935
|
-
const activeIE = environment.mascot.environment.activeIE;
|
|
936
|
-
if (!isOnBorder(this.state, bounds, definition.borderType, activeIE.visible ? activeIE : void 0)) return new CompleteRuntime();
|
|
1547
|
+
createRuntime(definition, context, references) {
|
|
937
1548
|
if (definition.type === "Reference") {
|
|
938
|
-
if (!definition.name || references.has(definition.name)) return new
|
|
1549
|
+
if (!definition.name || references.has(definition.name)) return new InstantRuntime(definition, this.state, this.random, "noop");
|
|
939
1550
|
const referenced = this.spec.actions.find((action) => action.name === definition.name);
|
|
940
|
-
if (!referenced) return new
|
|
941
|
-
|
|
942
|
-
return this.createRuntime({ ...referenced, ...definition, type: referenced.type }, environment, nextReferences);
|
|
1551
|
+
if (!referenced) return new InstantRuntime(definition, this.state, this.random, "noop");
|
|
1552
|
+
return this.createRuntime({ ...referenced, ...definition, type: referenced.type, name: definition.name }, context, new Set(references).add(definition.name));
|
|
943
1553
|
}
|
|
944
|
-
if (definition.type === "Sequence") {
|
|
945
|
-
return new
|
|
1554
|
+
if (definition.type === "Sequence" || definition.type === "Select") {
|
|
1555
|
+
return new ComplexRuntime(definition, this.random, (child, nextContext) => this.createRuntime(child, nextContext, new Set(references)), definition.type === "Select");
|
|
946
1556
|
}
|
|
947
|
-
if (definition.type === "
|
|
948
|
-
|
|
949
|
-
|
|
1557
|
+
if (definition.type === "Stay") return new StayRuntime(definition, this.state, this.random);
|
|
1558
|
+
if (definition.type === "Animate") return new AnimateRuntime(definition, this.state, this.random);
|
|
1559
|
+
if (definition.type === "Move") return new MoveRuntime(definition, this.state, this.random);
|
|
1560
|
+
switch (definition.embedType) {
|
|
1561
|
+
case "Fall":
|
|
1562
|
+
return new FallRuntime(definition, this.state, this.random, this.options.gravity);
|
|
1563
|
+
case "FallWithIE":
|
|
1564
|
+
return new CarryFallRuntime(definition, this.state, this.random, this.options.gravity, this.callbacks);
|
|
1565
|
+
case "Jump":
|
|
1566
|
+
case "ComplexJump":
|
|
1567
|
+
case "ScanJump":
|
|
1568
|
+
case "BroadcastJump":
|
|
1569
|
+
return new JumpRuntime(definition, this.state, this.random);
|
|
1570
|
+
case "WalkWithIE":
|
|
1571
|
+
return new CarryMoveRuntime(definition, this.state, this.random, this.callbacks);
|
|
1572
|
+
case "MoveWithTurn":
|
|
1573
|
+
return new MoveWithTurnRuntime(definition, this.state, this.random);
|
|
1574
|
+
case "ComplexMove":
|
|
1575
|
+
case "ScanMove":
|
|
1576
|
+
case "BroadcastMove":
|
|
1577
|
+
return new MoveRuntime(definition, this.state, this.random);
|
|
1578
|
+
case "Turn":
|
|
1579
|
+
return new TurnRuntime(definition, this.state, this.random);
|
|
1580
|
+
case "Look":
|
|
1581
|
+
return new InstantRuntime(definition, this.state, this.random, "look");
|
|
1582
|
+
case "Offset":
|
|
1583
|
+
return new InstantRuntime(definition, this.state, this.random, "offset");
|
|
1584
|
+
case "Mute":
|
|
1585
|
+
case "Reboot":
|
|
1586
|
+
return new InstantRuntime(definition, this.state, this.random, "noop");
|
|
1587
|
+
case "Breed":
|
|
1588
|
+
return new BreedRuntime(definition, this.state, this.random, this.callbacks);
|
|
1589
|
+
case "BreedMove":
|
|
1590
|
+
return new BreedMoveRuntime(definition, this.state, this.random, this.callbacks);
|
|
1591
|
+
case "BreedJump":
|
|
1592
|
+
return new BreedJumpRuntime(definition, this.state, this.random, this.callbacks);
|
|
1593
|
+
case "ThrowIE":
|
|
1594
|
+
return new ThrowPlatformRuntime(definition, this.state, this.random, this.callbacks);
|
|
1595
|
+
case "SelfDestruct":
|
|
1596
|
+
case "Exit":
|
|
1597
|
+
return new SelfDestructRuntime(definition, this.state, this.random, this.callbacks);
|
|
1598
|
+
case "Dragged":
|
|
1599
|
+
return new DraggedRuntime(definition, this.state, this.random, this.spec);
|
|
1600
|
+
case "Regist":
|
|
1601
|
+
return new RegistRuntime(definition, this.state, this.random, this.spec);
|
|
1602
|
+
case "Broadcast":
|
|
1603
|
+
case "Interact":
|
|
1604
|
+
case "ScanInteract":
|
|
1605
|
+
case "Transform":
|
|
1606
|
+
return new AnimateRuntime(definition, this.state, this.random);
|
|
1607
|
+
case "BroadcastStay":
|
|
1608
|
+
return new StayRuntime(definition, this.state, this.random);
|
|
1609
|
+
default:
|
|
1610
|
+
return new StayRuntime(definition, this.state, this.random);
|
|
950
1611
|
}
|
|
951
|
-
return new LeafRuntime(evaluateAction(definition, environment), this.state, this.options, this.callbacks, environment);
|
|
952
1612
|
}
|
|
953
1613
|
};
|
|
954
1614
|
|
|
@@ -977,7 +1637,6 @@ function environmentRectangle(bounds) {
|
|
|
977
1637
|
};
|
|
978
1638
|
}
|
|
979
1639
|
var PLATFORM_NEARBY_DISTANCE = 400;
|
|
980
|
-
var PLATFORM_EDGE_TOLERANCE = 16;
|
|
981
1640
|
function distanceToRectangle(point, rectangle) {
|
|
982
1641
|
const dx = Math.max(rectangle.x - point.x, 0, point.x - rectangle.x - rectangle.width);
|
|
983
1642
|
const dy = Math.max(rectangle.y - point.y, 0, point.y - rectangle.y - rectangle.height);
|
|
@@ -1006,10 +1665,14 @@ var Mascot = class {
|
|
|
1006
1665
|
dragging: false
|
|
1007
1666
|
};
|
|
1008
1667
|
this.domHandle = dom.createMascot(spec, id, options.mascotClassName || void 0);
|
|
1668
|
+
this.frameDuration = options.frameDuration;
|
|
1669
|
+
this.random = options.random ?? Math.random;
|
|
1670
|
+
this.forceInitialFall = spawnOptions.behaviorName === void 0;
|
|
1009
1671
|
this.behavior = new BehaviorController(spec, options.random);
|
|
1010
1672
|
this.actions = new ActionExecutor(spec, this.state, options, {
|
|
1011
|
-
spawn: (position) => this.callbacks.spawn(this.spec.id, position),
|
|
1012
|
-
remove: () => this.callbacks.remove(this.id)
|
|
1673
|
+
spawn: (position, characterId) => this.callbacks.spawn(characterId ?? this.spec.id, position),
|
|
1674
|
+
remove: () => this.callbacks.remove(this.id),
|
|
1675
|
+
...this.callbacks.movePlatform && { movePlatform: this.callbacks.movePlatform }
|
|
1013
1676
|
});
|
|
1014
1677
|
this.installPointerHandlers();
|
|
1015
1678
|
}
|
|
@@ -1031,44 +1694,20 @@ var Mascot = class {
|
|
|
1031
1694
|
lastPointer;
|
|
1032
1695
|
activePlatformElement;
|
|
1033
1696
|
platforms = [];
|
|
1697
|
+
accumulatedMs = 0;
|
|
1698
|
+
frameDuration;
|
|
1699
|
+
random;
|
|
1700
|
+
forceInitialFall;
|
|
1034
1701
|
/** Advances behavior, animation, physics, and rendering by one clock tick. */
|
|
1035
1702
|
tick(deltaMs, bounds, platforms = []) {
|
|
1036
1703
|
if (this.destroyed) return;
|
|
1037
1704
|
this.platforms = platforms;
|
|
1038
|
-
const environment = this.createEnvironment(bounds, platforms);
|
|
1039
|
-
if (this.state.dragging) {
|
|
1040
|
-
this.dom.render(this.domHandle, this.spec, this.state);
|
|
1041
|
-
return;
|
|
1042
|
-
}
|
|
1043
1705
|
try {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
this.currentBehavior = this.findFallBehavior();
|
|
1050
|
-
started = this.startBehavior(environment);
|
|
1051
|
-
}
|
|
1052
|
-
if (!started) break;
|
|
1053
|
-
}
|
|
1054
|
-
const activeIE = environment.mascot.environment.activeIE;
|
|
1055
|
-
const wasOnPlatformTop = activeIE.visible && activeIE.topBorder.isOn(this.state);
|
|
1056
|
-
const completed = this.actions.tick(deltaMs, environment, bounds);
|
|
1057
|
-
const remainedNearPlatform = this.state.x >= activeIE.left - PLATFORM_EDGE_TOLERANCE && this.state.x <= activeIE.right + PLATFORM_EDGE_TOLERANCE;
|
|
1058
|
-
if (wasOnPlatformTop && !remainedNearPlatform && !platforms.some((platform) => isOnTop(this.state, platform))) {
|
|
1059
|
-
this.actions.cancel();
|
|
1060
|
-
this.currentBehavior = this.findFallBehavior();
|
|
1061
|
-
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms));
|
|
1062
|
-
break;
|
|
1063
|
-
}
|
|
1064
|
-
if (!completed) break;
|
|
1065
|
-
if (this.destroyed) return;
|
|
1066
|
-
this.currentBehavior = this.behavior.selectNext(this.createEnvironment(bounds, platforms));
|
|
1067
|
-
if (!this.currentBehavior || !this.startBehavior(this.createEnvironment(bounds, platforms))) {
|
|
1068
|
-
this.currentBehavior = this.findFallBehavior();
|
|
1069
|
-
if (!this.currentBehavior || !this.startBehavior(this.createEnvironment(bounds, platforms))) break;
|
|
1070
|
-
}
|
|
1071
|
-
deltaMs = 0;
|
|
1706
|
+
this.ensureBehavior(bounds, platforms, true);
|
|
1707
|
+
this.accumulatedMs += Math.max(0, deltaMs);
|
|
1708
|
+
while (this.accumulatedMs >= this.frameDuration && !this.destroyed) {
|
|
1709
|
+
this.accumulatedMs -= this.frameDuration;
|
|
1710
|
+
this.legacyTick(bounds, platforms);
|
|
1072
1711
|
}
|
|
1073
1712
|
} catch (error) {
|
|
1074
1713
|
this.callbacks.error(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -1089,14 +1728,68 @@ var Mascot = class {
|
|
|
1089
1728
|
for (const dispose of this.disposers.splice(0)) dispose();
|
|
1090
1729
|
this.dom.removeMascot(this.domHandle);
|
|
1091
1730
|
}
|
|
1092
|
-
startBehavior(environment) {
|
|
1731
|
+
startBehavior(environment, bounds, platforms) {
|
|
1093
1732
|
if (!this.currentBehavior) return false;
|
|
1094
1733
|
this.state.behaviorName = this.currentBehavior.name;
|
|
1095
|
-
return this.actions.start(this.currentBehavior.name, environment);
|
|
1734
|
+
return this.actions.start(this.currentBehavior.actionName ?? this.currentBehavior.name, environment, false, bounds, platforms);
|
|
1735
|
+
}
|
|
1736
|
+
ensureBehavior(bounds, platforms, initial) {
|
|
1737
|
+
for (let guard = 0; guard < 32 && !this.destroyed; guard += 1) {
|
|
1738
|
+
const environment = this.createEnvironment(bounds, platforms);
|
|
1739
|
+
if (!this.currentBehavior) {
|
|
1740
|
+
this.currentBehavior = initial ? this.forceInitialFall ? this.findFallBehavior() ?? this.behavior.selectInitial(environment) : this.behavior.selectInitial(environment, this.state.behaviorName) : this.selectNextBehavior(environment, bounds);
|
|
1741
|
+
initial = false;
|
|
1742
|
+
if (!this.currentBehavior) this.currentBehavior = this.findFallBehavior();
|
|
1743
|
+
if (!this.currentBehavior || !this.startBehavior(environment, bounds, platforms)) return;
|
|
1744
|
+
}
|
|
1745
|
+
if (this.actions.hasNext(environment, bounds, platforms)) return;
|
|
1746
|
+
this.currentBehavior = this.selectNextBehavior(environment, bounds) ?? this.findFallBehavior();
|
|
1747
|
+
if (!this.currentBehavior) return;
|
|
1748
|
+
if (!this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms)) return;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
legacyTick(bounds, platforms) {
|
|
1752
|
+
this.ensureBehavior(bounds, platforms, false);
|
|
1753
|
+
if (!this.currentBehavior) return;
|
|
1754
|
+
const result = this.actions.step(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1755
|
+
if (this.destroyed) return;
|
|
1756
|
+
if (result === "lost-ground") {
|
|
1757
|
+
this.state.dragging = false;
|
|
1758
|
+
this.actions.cancel();
|
|
1759
|
+
this.currentBehavior = this.findFallBehavior();
|
|
1760
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1761
|
+
} else if (result === "complete") {
|
|
1762
|
+
this.currentBehavior = this.selectNextBehavior(this.createEnvironment(bounds, platforms), bounds);
|
|
1763
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1764
|
+
this.ensureBehavior(bounds, platforms, false);
|
|
1765
|
+
} else if (this.isOutsideVisibleBounds(bounds)) {
|
|
1766
|
+
this.state.x = Math.trunc(bounds.x + this.random() * bounds.width);
|
|
1767
|
+
this.state.y = bounds.y - 256;
|
|
1768
|
+
this.actions.cancel();
|
|
1769
|
+
this.currentBehavior = this.findFallBehavior();
|
|
1770
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(bounds, platforms), bounds, platforms);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
isOutsideVisibleBounds(bounds) {
|
|
1774
|
+
const sprite = this.spec.sprites[this.state.sprite];
|
|
1775
|
+
const width = typeof sprite === "object" && "width" in sprite && sprite.width !== void 0 ? sprite.width : 128;
|
|
1776
|
+
const height = typeof sprite === "object" && "height" in sprite && sprite.height !== void 0 ? sprite.height : 128;
|
|
1777
|
+
const anchorX = this.state.lookRight ? width - this.state.anchorX : this.state.anchorX;
|
|
1778
|
+
const left = this.state.x - anchorX;
|
|
1779
|
+
const top = this.state.y - this.state.anchorY;
|
|
1780
|
+
return left + width <= bounds.x || bounds.x + bounds.width <= left || bounds.y + bounds.height <= top;
|
|
1096
1781
|
}
|
|
1097
1782
|
findFallBehavior() {
|
|
1098
1783
|
return this.behavior.force("Fall") ?? this.behavior.force("\u843D\u4E0B\u3059\u308B");
|
|
1099
1784
|
}
|
|
1785
|
+
selectNextBehavior(environment, bounds) {
|
|
1786
|
+
const selected = this.behavior.selectNext(environment);
|
|
1787
|
+
if (this.behavior.usedFallback()) {
|
|
1788
|
+
this.state.x = Math.trunc(bounds.x + this.random() * bounds.width);
|
|
1789
|
+
this.state.y = bounds.y - 256;
|
|
1790
|
+
}
|
|
1791
|
+
return selected;
|
|
1792
|
+
}
|
|
1100
1793
|
createEnvironment(bounds, platforms = this.platforms) {
|
|
1101
1794
|
const workArea = environmentRectangle(bounds);
|
|
1102
1795
|
const inactive = environmentRectangle({ x: -100, y: -100, width: 0, height: 0 });
|
|
@@ -1111,10 +1804,10 @@ var Mascot = class {
|
|
|
1111
1804
|
lookRight: this.state.lookRight,
|
|
1112
1805
|
environment: {
|
|
1113
1806
|
cursor: this.callbacks.pointer(),
|
|
1114
|
-
screen:
|
|
1807
|
+
screen: workArea,
|
|
1115
1808
|
workArea,
|
|
1116
|
-
floor:
|
|
1117
|
-
ceiling:
|
|
1809
|
+
floor: edge((point) => isOnFloor(point, bounds, platforms)),
|
|
1810
|
+
ceiling: edge((point) => isOnTop(point, bounds) || platforms.some((candidate) => isOnBottom(point, candidate))),
|
|
1118
1811
|
activeIE
|
|
1119
1812
|
}
|
|
1120
1813
|
}
|
|
@@ -1141,6 +1834,7 @@ var Mascot = class {
|
|
|
1141
1834
|
}
|
|
1142
1835
|
installPointerHandlers() {
|
|
1143
1836
|
const element = this.domHandle.spriteElement;
|
|
1837
|
+
const document2 = element.ownerDocument;
|
|
1144
1838
|
const listen = (target, type, listener) => {
|
|
1145
1839
|
target.addEventListener(type, listener);
|
|
1146
1840
|
this.disposers.push(() => target.removeEventListener(type, listener));
|
|
@@ -1149,29 +1843,29 @@ var Mascot = class {
|
|
|
1149
1843
|
const pointerEvent = event;
|
|
1150
1844
|
if (pointerEvent.button !== 0) return;
|
|
1151
1845
|
event.preventDefault();
|
|
1152
|
-
const point =
|
|
1846
|
+
const point = this.dom.toLocalPoint(pointerEvent.clientX, pointerEvent.clientY);
|
|
1153
1847
|
this.pointerId = pointerEvent.pointerId;
|
|
1154
1848
|
this.pointerDown = point;
|
|
1155
1849
|
this.lastPointer = point;
|
|
1156
1850
|
this.dragOffset = { x: this.state.x - point.x, y: this.state.y - point.y };
|
|
1157
1851
|
this.state.dragging = true;
|
|
1158
|
-
this.actions.cancel();
|
|
1159
1852
|
this.currentBehavior = this.behavior.force("Dragged") ?? this.behavior.force("\u30C9\u30E9\u30C3\u30B0\u3055\u308C\u308B");
|
|
1160
|
-
if (this.currentBehavior) this.
|
|
1853
|
+
if (this.currentBehavior) this.startBehavior(this.createEnvironment(this.dom.getBounds()), this.dom.getBounds(), this.platforms);
|
|
1161
1854
|
element.setPointerCapture?.(pointerEvent.pointerId);
|
|
1162
1855
|
});
|
|
1163
|
-
listen(
|
|
1856
|
+
listen(document2, "pointermove", (event) => {
|
|
1164
1857
|
const pointerEvent = event;
|
|
1165
1858
|
if (!this.state.dragging || pointerEvent.pointerId !== this.pointerId) return;
|
|
1166
|
-
const point =
|
|
1859
|
+
const point = this.dom.toLocalPoint(pointerEvent.clientX, pointerEvent.clientY);
|
|
1167
1860
|
const previous = this.lastPointer ?? point;
|
|
1861
|
+
const bounds = this.dom.getBounds();
|
|
1168
1862
|
this.state.vx = (point.x - previous.x) * 0.8;
|
|
1169
1863
|
this.state.vy = (point.y - previous.y) * 0.8;
|
|
1170
1864
|
this.state.x = point.x + this.dragOffset.x;
|
|
1171
1865
|
this.state.y = point.y + this.dragOffset.y;
|
|
1172
1866
|
this.lastPointer = point;
|
|
1173
1867
|
});
|
|
1174
|
-
listen(
|
|
1868
|
+
listen(document2, "pointerup", (event) => {
|
|
1175
1869
|
const pointerEvent = event;
|
|
1176
1870
|
if (!this.state.dragging || pointerEvent.pointerId !== this.pointerId) return;
|
|
1177
1871
|
this.state.dragging = false;
|
|
@@ -1179,8 +1873,8 @@ var Mascot = class {
|
|
|
1179
1873
|
this.pointerId = void 0;
|
|
1180
1874
|
this.currentBehavior = this.behavior.force("Thrown") ?? this.behavior.force("\u6295\u3052\u3089\u308C\u308B") ?? this.findFallBehavior();
|
|
1181
1875
|
if (this.currentBehavior) {
|
|
1182
|
-
|
|
1183
|
-
this.
|
|
1876
|
+
const bounds = this.dom.getBounds();
|
|
1877
|
+
this.startBehavior(this.createEnvironment(bounds), bounds, this.platforms);
|
|
1184
1878
|
}
|
|
1185
1879
|
if (moved < 4) this.callbacks.click(this.snapshot());
|
|
1186
1880
|
});
|
|
@@ -1188,21 +1882,23 @@ var Mascot = class {
|
|
|
1188
1882
|
};
|
|
1189
1883
|
|
|
1190
1884
|
// src/platform.ts
|
|
1191
|
-
function resolvePlatformElements(source,
|
|
1885
|
+
function resolvePlatformElements(source, root, excludedRoot) {
|
|
1192
1886
|
let elements;
|
|
1193
1887
|
if (typeof source === "string") {
|
|
1194
1888
|
try {
|
|
1195
|
-
elements = [...
|
|
1889
|
+
elements = [...root.querySelectorAll(source)];
|
|
1196
1890
|
} catch {
|
|
1197
1891
|
return [];
|
|
1198
1892
|
}
|
|
1199
1893
|
} else {
|
|
1200
1894
|
elements = source;
|
|
1201
1895
|
}
|
|
1896
|
+
const document2 = root.nodeType === 9 ? root : root.ownerDocument;
|
|
1897
|
+
if (!document2) return [];
|
|
1202
1898
|
const HTMLElementConstructor = document2.defaultView?.HTMLElement;
|
|
1203
1899
|
if (!HTMLElementConstructor) return [];
|
|
1204
1900
|
return [...new Set(elements)].filter(
|
|
1205
|
-
(element) => element instanceof HTMLElementConstructor && element.isConnected && (!excludedRoot || !excludedRoot.contains(element))
|
|
1901
|
+
(element) => element instanceof HTMLElementConstructor && element.isConnected && root.contains(element) && (!excludedRoot || !excludedRoot.contains(element))
|
|
1206
1902
|
);
|
|
1207
1903
|
}
|
|
1208
1904
|
function readPlatformRectangles(elements, workAreaRectangle) {
|
|
@@ -1330,21 +2026,26 @@ var ShimejiEngine = class {
|
|
|
1330
2026
|
destroyed = false;
|
|
1331
2027
|
initialized = false;
|
|
1332
2028
|
platformSource;
|
|
1333
|
-
|
|
2029
|
+
additionalPlatformElements = [];
|
|
2030
|
+
movedPlatforms = /* @__PURE__ */ new Map();
|
|
2031
|
+
platformRectangles = /* @__PURE__ */ new Map();
|
|
2032
|
+
/** Starts the clock and container-aware listeners. Calling this method more than once is harmless. */
|
|
1334
2033
|
initialize() {
|
|
1335
2034
|
this.assertAlive();
|
|
1336
2035
|
if (this.initialized) return;
|
|
1337
2036
|
this.initialized = true;
|
|
1338
|
-
|
|
2037
|
+
const document2 = this.container.ownerDocument;
|
|
2038
|
+
const view = document2.defaultView;
|
|
2039
|
+
if (!view) throw new Error("ShimejiEngine requires a container connected to a window");
|
|
2040
|
+
this.listen(document2, "pointermove", (event) => {
|
|
1339
2041
|
const pointerEvent = event;
|
|
1340
|
-
const x = pointerEvent.clientX;
|
|
1341
|
-
const y = pointerEvent.clientY;
|
|
2042
|
+
const { x, y } = this.dom.toLocalPoint(pointerEvent.clientX, pointerEvent.clientY);
|
|
1342
2043
|
this.pointer = { x, y, dx: x - this.pointer.x, dy: y - this.pointer.y };
|
|
1343
2044
|
});
|
|
1344
|
-
this.listen(
|
|
1345
|
-
const maintenance =
|
|
2045
|
+
this.listen(view, "resize", () => this.renderAll());
|
|
2046
|
+
const maintenance = view.setInterval(() => this.dom.ensureMounted(), 2e3);
|
|
1346
2047
|
this.intervals.add(maintenance);
|
|
1347
|
-
this.animationFrame = requestAnimationFrame(this.onAnimationFrame);
|
|
2048
|
+
this.animationFrame = view.requestAnimationFrame(this.onAnimationFrame);
|
|
1348
2049
|
}
|
|
1349
2050
|
/** Registers or replaces a parsed or legacy character specification. */
|
|
1350
2051
|
registerCharacter(spec) {
|
|
@@ -1372,10 +2073,14 @@ var ShimejiEngine = class {
|
|
|
1372
2073
|
if (!spec) throw new Error(`Character '${characterId}' is not registered`);
|
|
1373
2074
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
1374
2075
|
const random = this.options.random ?? Math.random;
|
|
2076
|
+
const randomX = Math.trunc(bounds.x + random() * bounds.width);
|
|
2077
|
+
const spawnInset = Math.min(2, bounds.width / 2);
|
|
1375
2078
|
const spawnOptions = {
|
|
1376
2079
|
...position,
|
|
1377
|
-
|
|
1378
|
-
|
|
2080
|
+
// Fall treats the wall in the facing direction as ground. Keep implicit
|
|
2081
|
+
// spawns clear of both side-wall tolerances so even random() === 0 falls.
|
|
2082
|
+
x: position.x ?? Math.min(Math.max(randomX, bounds.x + spawnInset), bounds.x + bounds.width - spawnInset),
|
|
2083
|
+
y: position.y ?? bounds.y + 2
|
|
1379
2084
|
};
|
|
1380
2085
|
const id = `shimeji-${this.nextMascotId++}`;
|
|
1381
2086
|
const mascot = new Mascot(id, spec, this.dom, this.options, spawnOptions, {
|
|
@@ -1387,6 +2092,7 @@ var ShimejiEngine = class {
|
|
|
1387
2092
|
remove: (mascotId) => {
|
|
1388
2093
|
if (!this.destroyed) this.remove(mascotId);
|
|
1389
2094
|
},
|
|
2095
|
+
movePlatform: (element, point) => this.movePlatform(element, point),
|
|
1390
2096
|
click: (state2) => this.events.emit("click", state2),
|
|
1391
2097
|
error: (error) => this.events.emit("error", error)
|
|
1392
2098
|
});
|
|
@@ -1423,22 +2129,27 @@ var ShimejiEngine = class {
|
|
|
1423
2129
|
this.assertAlive();
|
|
1424
2130
|
return this.events.on(event, listener);
|
|
1425
2131
|
}
|
|
1426
|
-
/** Replaces the
|
|
1427
|
-
setPlatforms(platforms) {
|
|
2132
|
+
/** Replaces the primary platform source and any additional registered elements. */
|
|
2133
|
+
setPlatforms(platforms, additionalPlatforms = []) {
|
|
1428
2134
|
this.assertAlive();
|
|
1429
2135
|
this.platformSource = platforms;
|
|
2136
|
+
this.additionalPlatformElements = additionalPlatforms;
|
|
1430
2137
|
}
|
|
1431
2138
|
/** Stops animation and timers, removes listeners and DOM, and revokes all object URLs. */
|
|
1432
2139
|
destroy() {
|
|
1433
2140
|
if (this.destroyed) return;
|
|
1434
2141
|
for (const mascot of this.mascots.values()) mascot.destroy();
|
|
1435
2142
|
this.mascots.clear();
|
|
1436
|
-
|
|
2143
|
+
const view = this.container.ownerDocument.defaultView;
|
|
2144
|
+
if (this.animationFrame !== void 0) view?.cancelAnimationFrame(this.animationFrame);
|
|
1437
2145
|
this.animationFrame = void 0;
|
|
1438
|
-
for (const interval of this.intervals)
|
|
2146
|
+
for (const interval of this.intervals) view?.clearInterval(interval);
|
|
1439
2147
|
this.intervals.clear();
|
|
1440
2148
|
for (const dispose of this.disposers.splice(0)) dispose();
|
|
1441
2149
|
this.dom.destroy();
|
|
2150
|
+
for (const [element, movement] of this.movedPlatforms) element.style.transform = movement.originalTransform;
|
|
2151
|
+
this.movedPlatforms.clear();
|
|
2152
|
+
this.platformRectangles.clear();
|
|
1442
2153
|
this.sprites.destroy();
|
|
1443
2154
|
this.specs.clear();
|
|
1444
2155
|
this.events.clear();
|
|
@@ -1457,7 +2168,7 @@ var ShimejiEngine = class {
|
|
|
1457
2168
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
1458
2169
|
for (const mascot of [...this.mascots.values()]) mascot.tick(delta, bounds, platforms);
|
|
1459
2170
|
this.emitState();
|
|
1460
|
-
this.animationFrame = requestAnimationFrame(this.onAnimationFrame);
|
|
2171
|
+
this.animationFrame = this.container.ownerDocument.defaultView?.requestAnimationFrame(this.onAnimationFrame);
|
|
1461
2172
|
};
|
|
1462
2173
|
renderAll() {
|
|
1463
2174
|
const { bounds, platforms } = this.readFrameGeometry();
|
|
@@ -1465,8 +2176,26 @@ var ShimejiEngine = class {
|
|
|
1465
2176
|
}
|
|
1466
2177
|
readFrameGeometry() {
|
|
1467
2178
|
const bounds = this.dom.getBounds();
|
|
1468
|
-
const elements =
|
|
1469
|
-
|
|
2179
|
+
const elements = [.../* @__PURE__ */ new Set([
|
|
2180
|
+
...resolvePlatformElements(this.platformSource, this.container),
|
|
2181
|
+
...resolvePlatformElements(this.additionalPlatformElements, this.container)
|
|
2182
|
+
])].filter((element) => !this.dom.owns(element));
|
|
2183
|
+
const platforms = readPlatformRectangles(elements, this.container.getBoundingClientRect());
|
|
2184
|
+
this.platformRectangles.clear();
|
|
2185
|
+
for (const platform of platforms) this.platformRectangles.set(platform.element, platform);
|
|
2186
|
+
return { bounds, platforms };
|
|
2187
|
+
}
|
|
2188
|
+
movePlatform(element, point) {
|
|
2189
|
+
const rectangle = this.platformRectangles.get(element);
|
|
2190
|
+
if (!rectangle) return;
|
|
2191
|
+
const movement = this.movedPlatforms.get(element) ?? { originalTransform: element.style.transform, x: 0, y: 0 };
|
|
2192
|
+
movement.x += point.x - rectangle.x;
|
|
2193
|
+
movement.y += point.y - rectangle.y;
|
|
2194
|
+
const translate = `translate(${movement.x}px, ${movement.y}px)`;
|
|
2195
|
+
element.style.transform = movement.originalTransform ? `${movement.originalTransform} ${translate}` : translate;
|
|
2196
|
+
rectangle.x = point.x;
|
|
2197
|
+
rectangle.y = point.y;
|
|
2198
|
+
this.movedPlatforms.set(element, movement);
|
|
1470
2199
|
}
|
|
1471
2200
|
emitState() {
|
|
1472
2201
|
this.events.emit("statechange", this.getState());
|