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