incanto 0.7.1 → 0.9.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/bin/incanto-check.mjs +6 -4
- package/dist/2d.d.ts +91 -3
- package/dist/2d.js +4 -4
- package/dist/3d.d.ts +43 -4
- package/dist/3d.js +4 -4
- package/dist/{audio-player-C3bH_eZ5.d.ts → audio-player-Dyjg5k92.d.ts} +1 -1
- package/dist/audit-C6rMyict.js +58 -0
- package/dist/{behavior-q3cejJgs.d.ts → behavior-Bod1AyJS.d.ts} +31 -1
- package/dist/{create-game-rFulaS3s.js → create-game-Bydey3CZ.js} +4 -5
- package/dist/{create-game-DuDkqnjl.js → create-game-C7Zsqbko.js} +4 -5
- package/dist/debug.d.ts +1 -1
- package/dist/{errors-BMFaY68Q.d.ts → errors-1dXlIwoR.d.ts} +7 -1
- package/dist/{gameplay-CDFgSG6z.js → gameplay-DPMgZk9W.js} +108 -1
- package/dist/gameplay.d.ts +44 -3
- package/dist/gameplay.js +2 -2
- package/dist/index.d.ts +151 -9
- package/dist/index.js +189 -4
- package/dist/{loader-DnLMMRy8.d.ts → loader-BcUDfNHn.d.ts} +1 -1
- package/dist/net.d.ts +46 -2
- package/dist/net.js +54 -2
- package/dist/particle-sim-CyUU7HVU.js +281 -0
- package/dist/{physics-2d-CZM5Y90X.js → physics-2d-DweTURFt.js} +45 -4
- package/dist/{physics-3d-DT5e8izo.js → physics-3d-Clv1uFzk.js} +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-Cl3pAxX-.js → register-BD6zgrv_.js} +254 -4
- package/dist/{register-C35HDZpm.js → register-Cwc-yCYx.js} +1 -1
- package/dist/{particle-sim-m3CdTGSl.js → register-DJZXxwAn.js} +1178 -278
- package/dist/{register-CpXcMiEk.js → register-iUPtts7T.js} +141 -4
- package/dist/test.d.ts +4 -10
- package/dist/test.js +11 -16
- package/editor/assets/{agent8-N4bwVH7C.js → agent8-B0ZGx-N1.js} +1 -1
- package/editor/assets/index-BLzp8moy.js +7417 -0
- package/editor/assets/{index-DF3tMeKJ.css → index-D8QvwvOm.css} +1 -1
- package/editor/index.html +2 -2
- package/package.json +1 -1
- package/schemas/scene.schema.json +427 -0
- package/skills/incanto-3d-character.md +14 -0
- package/skills/incanto-audio.md +23 -0
- package/skills/incanto-building-2d-games.md +31 -0
- package/skills/incanto-building-3d-games.md +21 -0
- package/skills/incanto-editor.md +13 -0
- package/skills/incanto-gameplay-behaviors.md +28 -0
- package/skills/incanto-hud.md +24 -0
- package/skills/incanto-multiplayer.md +27 -0
- package/skills/incanto-node-reference.md +73 -0
- package/skills/incanto-physics-and-input.md +9 -0
- package/skills/incanto-verifying-your-game.md +31 -0
- package/dist/register-Dl_ixIJe.js +0 -834
- package/editor/assets/index-CXFNanuR.js +0 -7417
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { n as jsonEquals } from "./json-BLk7H2Qa.js";
|
|
2
|
+
//#region src/core/order-groups.ts
|
|
3
|
+
const ORDER_GROUP_BASE = {
|
|
4
|
+
background: -2e3,
|
|
5
|
+
terrain: -1e3,
|
|
6
|
+
default: 0,
|
|
7
|
+
characters: 1e3,
|
|
8
|
+
effects: 2e3,
|
|
9
|
+
overlay: 3e3
|
|
10
|
+
};
|
|
11
|
+
/** base + fine offset (unknown group names fall back to `default`). */
|
|
12
|
+
function effectiveOrder(group, renderOrder) {
|
|
13
|
+
return (ORDER_GROUP_BASE[group] ?? 0) + renderOrder;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/core/particle-presets.ts
|
|
17
|
+
const PARTICLE_PRESETS = {
|
|
18
|
+
fire: {
|
|
19
|
+
rate: 60,
|
|
20
|
+
lifetime: [.4, .9],
|
|
21
|
+
speed: [30, 70],
|
|
22
|
+
directionDeg: -90,
|
|
23
|
+
spreadDeg: 35,
|
|
24
|
+
gravity: [0, -30],
|
|
25
|
+
sizeStart: 14,
|
|
26
|
+
sizeEnd: 3,
|
|
27
|
+
colorStart: "#ffcf5a",
|
|
28
|
+
colorEnd: "#e8401f",
|
|
29
|
+
alphaStart: .9,
|
|
30
|
+
alphaEnd: 0,
|
|
31
|
+
blend: "add"
|
|
32
|
+
},
|
|
33
|
+
smoke: {
|
|
34
|
+
rate: 20,
|
|
35
|
+
lifetime: [1.5, 2.5],
|
|
36
|
+
speed: [10, 25],
|
|
37
|
+
directionDeg: -90,
|
|
38
|
+
spreadDeg: 40,
|
|
39
|
+
gravity: [0, -12],
|
|
40
|
+
sizeStart: 10,
|
|
41
|
+
sizeEnd: 26,
|
|
42
|
+
colorStart: "#8a8a8a",
|
|
43
|
+
colorEnd: "#3a3a3a",
|
|
44
|
+
alphaStart: .35,
|
|
45
|
+
alphaEnd: 0,
|
|
46
|
+
blend: "normal"
|
|
47
|
+
},
|
|
48
|
+
sparks: {
|
|
49
|
+
rate: 80,
|
|
50
|
+
lifetime: [.3, .7],
|
|
51
|
+
speed: [120, 260],
|
|
52
|
+
spreadDeg: 360,
|
|
53
|
+
gravity: [0, 240],
|
|
54
|
+
drag: 1,
|
|
55
|
+
sizeStart: 4,
|
|
56
|
+
sizeEnd: 1,
|
|
57
|
+
colorStart: "#fff3b0",
|
|
58
|
+
colorEnd: "#ff9d2e",
|
|
59
|
+
blend: "add"
|
|
60
|
+
},
|
|
61
|
+
fireworks: {
|
|
62
|
+
rate: 0,
|
|
63
|
+
burst: 120,
|
|
64
|
+
lifetime: [.8, 1.6],
|
|
65
|
+
speed: [80, 240],
|
|
66
|
+
spreadDeg: 360,
|
|
67
|
+
gravity: [0, 90],
|
|
68
|
+
drag: .6,
|
|
69
|
+
sizeStart: 5,
|
|
70
|
+
sizeEnd: 1,
|
|
71
|
+
colorStart: "#9ad8ff",
|
|
72
|
+
colorEnd: "#ff5ad8",
|
|
73
|
+
blend: "add"
|
|
74
|
+
},
|
|
75
|
+
explosion: {
|
|
76
|
+
rate: 0,
|
|
77
|
+
burst: 60,
|
|
78
|
+
lifetime: [.25, .6],
|
|
79
|
+
speed: [150, 420],
|
|
80
|
+
spreadDeg: 360,
|
|
81
|
+
drag: 3,
|
|
82
|
+
sizeStart: 16,
|
|
83
|
+
sizeEnd: 2,
|
|
84
|
+
colorStart: "#ffe08a",
|
|
85
|
+
colorEnd: "#ff3b1f",
|
|
86
|
+
blend: "add"
|
|
87
|
+
},
|
|
88
|
+
flash: {
|
|
89
|
+
rate: 0,
|
|
90
|
+
burst: 8,
|
|
91
|
+
lifetime: [.08, .16],
|
|
92
|
+
speed: [0, 30],
|
|
93
|
+
spreadDeg: 360,
|
|
94
|
+
sizeStart: 64,
|
|
95
|
+
sizeEnd: 96,
|
|
96
|
+
colorStart: "#ffffff",
|
|
97
|
+
colorEnd: "#ffffff",
|
|
98
|
+
alphaStart: 1,
|
|
99
|
+
alphaEnd: 0,
|
|
100
|
+
blend: "add"
|
|
101
|
+
},
|
|
102
|
+
lightning: {
|
|
103
|
+
rate: 90,
|
|
104
|
+
lifetime: [.05, .12],
|
|
105
|
+
speed: [400, 900],
|
|
106
|
+
directionDeg: -90,
|
|
107
|
+
spreadDeg: 14,
|
|
108
|
+
sizeStart: 6,
|
|
109
|
+
sizeEnd: 2,
|
|
110
|
+
colorStart: "#eaf2ff",
|
|
111
|
+
colorEnd: "#7fb4ff",
|
|
112
|
+
blend: "add"
|
|
113
|
+
},
|
|
114
|
+
rain: {
|
|
115
|
+
rate: 160,
|
|
116
|
+
lifetime: [.8, 1.2],
|
|
117
|
+
speed: [300, 420],
|
|
118
|
+
directionDeg: 90,
|
|
119
|
+
spreadDeg: 6,
|
|
120
|
+
sizeStart: 3,
|
|
121
|
+
sizeEnd: 3,
|
|
122
|
+
colorStart: "#9ec8ff",
|
|
123
|
+
colorEnd: "#9ec8ff",
|
|
124
|
+
alphaStart: .5,
|
|
125
|
+
alphaEnd: .2,
|
|
126
|
+
blend: "normal"
|
|
127
|
+
},
|
|
128
|
+
snow: {
|
|
129
|
+
rate: 40,
|
|
130
|
+
lifetime: [3, 5],
|
|
131
|
+
speed: [20, 50],
|
|
132
|
+
directionDeg: 90,
|
|
133
|
+
spreadDeg: 30,
|
|
134
|
+
drag: .2,
|
|
135
|
+
sizeStart: 5,
|
|
136
|
+
sizeEnd: 4,
|
|
137
|
+
colorStart: "#ffffff",
|
|
138
|
+
colorEnd: "#ffffff",
|
|
139
|
+
alphaStart: .9,
|
|
140
|
+
alphaEnd: .6,
|
|
141
|
+
blend: "normal"
|
|
142
|
+
},
|
|
143
|
+
magic: {
|
|
144
|
+
rate: 50,
|
|
145
|
+
lifetime: [.6, 1.4],
|
|
146
|
+
speed: [20, 80],
|
|
147
|
+
spreadDeg: 360,
|
|
148
|
+
gravity: [0, -20],
|
|
149
|
+
sizeStart: 7,
|
|
150
|
+
sizeEnd: 1,
|
|
151
|
+
colorStart: "#9dffe8",
|
|
152
|
+
colorEnd: "#7a5cff",
|
|
153
|
+
blend: "add"
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const PARTICLE_PRESET_NAMES = Object.keys(PARTICLE_PRESETS);
|
|
157
|
+
/**
|
|
158
|
+
* Apply the delta rule across three layers: schema default < preset < the
|
|
159
|
+
* scene's explicit props. A prop still equal to its SCHEMA default is
|
|
160
|
+
* considered "unset" and takes the preset value.
|
|
161
|
+
*/
|
|
162
|
+
function applyParticlePreset(target, presetName, schema) {
|
|
163
|
+
if (presetName === "custom") return;
|
|
164
|
+
const preset = PARTICLE_PRESETS[presetName];
|
|
165
|
+
if (!preset) return;
|
|
166
|
+
for (const [key, presetValue] of Object.entries(preset)) {
|
|
167
|
+
const def = schema[key]?.default;
|
|
168
|
+
const current = target[key];
|
|
169
|
+
if (def !== void 0 && jsonEquals(current, def)) target[key] = Array.isArray(presetValue) ? [...presetValue] : presetValue;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/core/particle-sim.ts
|
|
174
|
+
const FIELDS = 9;
|
|
175
|
+
var ParticleSim = class {
|
|
176
|
+
config;
|
|
177
|
+
rng;
|
|
178
|
+
data;
|
|
179
|
+
alive = 0;
|
|
180
|
+
spawnAccumulator = 0;
|
|
181
|
+
everSpawned = false;
|
|
182
|
+
constructor(config, rng) {
|
|
183
|
+
this.config = config;
|
|
184
|
+
this.rng = rng;
|
|
185
|
+
this.data = new Float64Array(Math.max(1, config.maxParticles) * FIELDS);
|
|
186
|
+
}
|
|
187
|
+
/** Change the emission rate LIVE (particles/sec; 0 pauses emission) — lets an
|
|
188
|
+
* emitter toggle on/off at runtime (drift smoke, throttle flames). */
|
|
189
|
+
setRate(rate) {
|
|
190
|
+
this.config.rate = rate;
|
|
191
|
+
if (rate <= 0) this.spawnAccumulator = 0;
|
|
192
|
+
}
|
|
193
|
+
get count() {
|
|
194
|
+
return this.alive;
|
|
195
|
+
}
|
|
196
|
+
/** True when nothing is alive and at least one particle has ever spawned. */
|
|
197
|
+
get done() {
|
|
198
|
+
return this.everSpawned && this.alive === 0;
|
|
199
|
+
}
|
|
200
|
+
/** Spawn n particles immediately (fireworks, explosions, flashes). */
|
|
201
|
+
burst(n) {
|
|
202
|
+
for (let i = 0; i < n; i++) this.spawn();
|
|
203
|
+
}
|
|
204
|
+
update(dt) {
|
|
205
|
+
if (this.config.rate > 0) {
|
|
206
|
+
this.spawnAccumulator += this.config.rate * dt;
|
|
207
|
+
while (this.spawnAccumulator >= 1) {
|
|
208
|
+
this.spawnAccumulator -= 1;
|
|
209
|
+
this.spawn();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const d = this.data;
|
|
213
|
+
const [gx, gy, gz] = this.config.gravity;
|
|
214
|
+
const damp = this.config.drag > 0 ? Math.exp(-this.config.drag * dt) : 1;
|
|
215
|
+
let i = 0;
|
|
216
|
+
while (i < this.alive) {
|
|
217
|
+
const base = i * FIELDS;
|
|
218
|
+
const age = d[base + 6] + dt;
|
|
219
|
+
if (age >= d[base + 7]) {
|
|
220
|
+
this.kill(i);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
d[base + 6] = age;
|
|
224
|
+
d[base + 3] = (d[base + 3] + gx * dt) * damp;
|
|
225
|
+
d[base + 4] = (d[base + 4] + gy * dt) * damp;
|
|
226
|
+
d[base + 5] = (d[base + 5] + gz * dt) * damp;
|
|
227
|
+
d[base + 0] = d[base + 0] + d[base + 3] * dt;
|
|
228
|
+
d[base + 1] = d[base + 1] + d[base + 4] * dt;
|
|
229
|
+
d[base + 2] = d[base + 2] + d[base + 5] * dt;
|
|
230
|
+
i += 1;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
forEach(fn) {
|
|
234
|
+
const d = this.data;
|
|
235
|
+
for (let i = 0; i < this.alive; i++) {
|
|
236
|
+
const base = i * FIELDS;
|
|
237
|
+
const age = d[base + 6];
|
|
238
|
+
const life = d[base + 7];
|
|
239
|
+
fn({
|
|
240
|
+
x: d[base + 0],
|
|
241
|
+
y: d[base + 1],
|
|
242
|
+
z: d[base + 2],
|
|
243
|
+
vx: d[base + 3],
|
|
244
|
+
vy: d[base + 4],
|
|
245
|
+
vz: d[base + 5],
|
|
246
|
+
age,
|
|
247
|
+
life,
|
|
248
|
+
t: life > 0 ? age / life : 1,
|
|
249
|
+
seed: d[base + 8]
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
spawn() {
|
|
254
|
+
if (this.alive >= this.config.maxParticles) return;
|
|
255
|
+
const base = this.alive * FIELDS;
|
|
256
|
+
const d = this.data;
|
|
257
|
+
const c = this.config;
|
|
258
|
+
const angle = (c.directionDeg + (this.rng.next() - .5) * c.spreadDeg) * Math.PI / 180;
|
|
259
|
+
const speed = c.speed[0] + this.rng.next() * (c.speed[1] - c.speed[0]);
|
|
260
|
+
const zTilt = c.spreadZ ? (this.rng.next() - .5) * (c.spreadDeg * Math.PI / 180) : 0;
|
|
261
|
+
const planar = Math.cos(zTilt);
|
|
262
|
+
d[base + 0] = 0;
|
|
263
|
+
d[base + 1] = 0;
|
|
264
|
+
d[base + 2] = 0;
|
|
265
|
+
d[base + 3] = Math.cos(angle) * speed * planar;
|
|
266
|
+
d[base + 4] = Math.sin(angle) * speed * planar;
|
|
267
|
+
d[base + 5] = Math.sin(zTilt) * speed;
|
|
268
|
+
d[base + 6] = 0;
|
|
269
|
+
d[base + 7] = c.lifetime[0] + this.rng.next() * (c.lifetime[1] - c.lifetime[0]);
|
|
270
|
+
d[base + 8] = this.rng.next();
|
|
271
|
+
this.alive += 1;
|
|
272
|
+
this.everSpawned = true;
|
|
273
|
+
}
|
|
274
|
+
kill(index) {
|
|
275
|
+
const last = this.alive - 1;
|
|
276
|
+
if (index !== last) this.data.copyWithin(index * FIELDS, last * FIELDS, last * FIELDS + FIELDS);
|
|
277
|
+
this.alive = last;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
//#endregion
|
|
281
|
+
export { ORDER_GROUP_BASE as a, applyParticlePreset as i, PARTICLE_PRESETS as n, effectiveOrder as o, PARTICLE_PRESET_NAMES as r, ParticleSim as t };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
3
|
-
import { _ as
|
|
3
|
+
import { _ as RigidBody2D, b as validateCollider2D, g as PhysicsBody2D, h as CharacterBody2D, m as Area2D, p as Joint2D, y as Node2D } from "./register-BD6zgrv_.js";
|
|
4
4
|
import { n as registerDebugSource } from "./debug-draw-CZmOYjL2.js";
|
|
5
5
|
//#region src/2d/physics/physics-2d.ts
|
|
6
6
|
var physics_2d_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -110,11 +110,27 @@ var Physics2D = class {
|
|
|
110
110
|
if (node.snapToGround && vy >= 0) this.kcc.enableSnapToGround(.05);
|
|
111
111
|
else this.kcc.disableSnapToGround();
|
|
112
112
|
this.kcc.setMaxSlopeClimbAngle(node.slopeLimitDeg * DEG);
|
|
113
|
+
let carryX = 0;
|
|
114
|
+
let carryY = 0;
|
|
115
|
+
if (e.ground && e.ground._physics === this) {
|
|
116
|
+
const [gx, gy] = worldPosition2D(e.ground);
|
|
117
|
+
carryX = gx - (e.groundX ?? gx);
|
|
118
|
+
carryY = gy - (e.groundY ?? gy);
|
|
119
|
+
} else e.ground = null;
|
|
113
120
|
const desired = {
|
|
114
|
-
x: (node.velocity[0] ?? 0) * this.lastDt / SCALE,
|
|
115
|
-
y: vy * this.lastDt / SCALE
|
|
121
|
+
x: ((node.velocity[0] ?? 0) * this.lastDt + carryX) / SCALE * 1,
|
|
122
|
+
y: (vy * this.lastDt + carryY) / SCALE * 1
|
|
116
123
|
};
|
|
117
|
-
|
|
124
|
+
const charBottomPx = e.body.translation().y * SCALE + charHalfHeight(node);
|
|
125
|
+
const predicate = (collider) => {
|
|
126
|
+
const other = this.byColliderHandle.get(collider.handle);
|
|
127
|
+
if (other && other !== node && other.collider.oneWay === true) {
|
|
128
|
+
if (vy < 0) return false;
|
|
129
|
+
if (charBottomPx > platformTopPx(other) + ONE_WAY_TOLERANCE_PX) return false;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
};
|
|
133
|
+
this.kcc.computeColliderMovement(e.collider, desired, this.R.QueryFilterFlags.EXCLUDE_SENSORS, void 0, predicate);
|
|
118
134
|
const corrected = this.kcc.computedMovement();
|
|
119
135
|
const t = e.body.translation();
|
|
120
136
|
e.body.setNextKinematicTranslation({
|
|
@@ -122,6 +138,17 @@ var Physics2D = class {
|
|
|
122
138
|
y: t.y + corrected.y
|
|
123
139
|
});
|
|
124
140
|
node._grounded = this.kcc.computedGrounded();
|
|
141
|
+
if (node._grounded) {
|
|
142
|
+
const footX = (t.x + corrected.x) * SCALE;
|
|
143
|
+
const footY = (t.y + corrected.y) * SCALE + charHalfHeight(node) - 2;
|
|
144
|
+
const hit = this.castRay([footX, footY], [0, 1], 8, node);
|
|
145
|
+
if (hit?.node && !(hit.node instanceof CharacterBody2D)) {
|
|
146
|
+
e.ground = hit.node;
|
|
147
|
+
const [gx, gy] = worldPosition2D(hit.node);
|
|
148
|
+
e.groundX = gx;
|
|
149
|
+
e.groundY = gy;
|
|
150
|
+
} else e.ground = null;
|
|
151
|
+
} else e.ground = null;
|
|
125
152
|
}
|
|
126
153
|
/** Rapier debug segments, scaled back to pixels. Null while debugDraw is off. */
|
|
127
154
|
debugLines() {
|
|
@@ -327,6 +354,20 @@ function collectBodies(node, out) {
|
|
|
327
354
|
* Composed world position (translation only — ancestor rotation/scale are NOT
|
|
328
355
|
* supported for physics bodies; keep body ancestors untransformed or offset-only).
|
|
329
356
|
*/
|
|
357
|
+
const ONE_WAY_TOLERANCE_PX = 4;
|
|
358
|
+
/** Distance from a body's center to its lowest point, px. */
|
|
359
|
+
function charHalfHeight(node) {
|
|
360
|
+
const c = node.collider;
|
|
361
|
+
if (c.shape === "rect") return (c.size?.[1] ?? 32) / 2;
|
|
362
|
+
if (c.shape === "capsule") return (c.height ?? 32) / 2 + (c.radius ?? 8);
|
|
363
|
+
return c.radius ?? 16;
|
|
364
|
+
}
|
|
365
|
+
/** The stand-on surface of a platform (y-down: its SMALLEST y), px. */
|
|
366
|
+
function platformTopPx(node) {
|
|
367
|
+
const [, wy] = worldPosition2D(node);
|
|
368
|
+
const c = node.collider;
|
|
369
|
+
return wy - (c.shape === "rect" ? (c.size?.[1] ?? 32) / 2 : c.shape === "capsule" ? (c.height ?? 32) / 2 + (c.radius ?? 8) : c.radius ?? 16);
|
|
370
|
+
}
|
|
330
371
|
function worldPosition2D(node) {
|
|
331
372
|
let x = 0;
|
|
332
373
|
let y = 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
3
3
|
import { n as registerDebugSource } from "./debug-draw-CZmOYjL2.js";
|
|
4
|
-
import {
|
|
4
|
+
import { A as RigidBody3D, D as Area3D, E as Joint3D, M as Node3D, N as validateCollider3D, O as CharacterBody3D, S as Terrain3D, k as PhysicsBody3D } from "./register-iUPtts7T.js";
|
|
5
5
|
import { Euler, Quaternion } from "three";
|
|
6
6
|
//#region src/3d/physics/physics-3d.ts
|
|
7
7
|
var physics_3d_exports = /* @__PURE__ */ __exportAll({
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as JsonValue } from "./schema-CcoWb32N.js";
|
|
2
|
-
import { n as BehaviorCtor, v as Engine, w as Scene } from "./behavior-
|
|
2
|
+
import { n as BehaviorCtor, v as Engine, w as Scene } from "./behavior-Bod1AyJS.js";
|
|
3
3
|
import { CSSProperties, ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/react/index.d.ts
|
package/dist/react.js
CHANGED
|
@@ -156,7 +156,7 @@ function IncantoCanvas(props) {
|
|
|
156
156
|
pointer: latest.pointer,
|
|
157
157
|
...keyboard !== void 0 ? { keyboard } : {}
|
|
158
158
|
};
|
|
159
|
-
const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-
|
|
159
|
+
const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-Bydey3CZ.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-C7Zsqbko.js").then((n) => n.n)).createGame2D(o)))(opts);
|
|
160
160
|
if (disposed) {
|
|
161
161
|
next.dispose();
|
|
162
162
|
return;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { f as Node } from "./loader-B4OEXDZ8.js";
|
|
2
|
-
import {
|
|
2
|
+
import { t as registerCoreNodes } from "./register-DJZXxwAn.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
4
|
import { t as Rng } from "./rng-DP-SR7eg.js";
|
|
5
|
-
import { t as registerCoreNodes } from "./register-Dl_ixIJe.js";
|
|
6
5
|
import { i as getNodeSchema, l as registerNode } from "./registry-BVJ2HbCn.js";
|
|
7
|
-
import {
|
|
6
|
+
import { i as applyParticlePreset, o as effectiveOrder, r as PARTICLE_PRESET_NAMES, t as ParticleSim } from "./particle-sim-CyUU7HVU.js";
|
|
7
|
+
import { AdditiveBlending, BufferAttribute, BufferGeometry, CanvasTexture, Color, DynamicDrawUsage, Group, InstancedMesh, MathUtils, Matrix4, Mesh, MeshBasicMaterial, NearestFilter, NormalBlending, Object3D, PlaneGeometry } from "three";
|
|
8
8
|
//#region src/2d/physics/collider-validate.ts
|
|
9
9
|
/**
|
|
10
10
|
* Hard-validate a 2D collider prop — called at LOAD time (via the loader's
|
|
@@ -15,6 +15,7 @@ import { AdditiveBlending, CanvasTexture, Color, DynamicDrawUsage, Group, Instan
|
|
|
15
15
|
function validateCollider2D(collider, nodeName) {
|
|
16
16
|
const shape = collider.shape;
|
|
17
17
|
if (shape === void 0) return;
|
|
18
|
+
if (collider.oneWay !== void 0 && typeof collider.oneWay !== "boolean") throw new IncantoError("BAD_FORMAT", `Collider on '${nodeName}': "oneWay" must be a boolean (platforms you jump up through), got ${JSON.stringify(collider.oneWay)}.`);
|
|
18
19
|
if (shape === "rect") {
|
|
19
20
|
const size = collider.size;
|
|
20
21
|
if (!Array.isArray(size) || size.length !== 2 || size.some((v) => typeof v !== "number")) throw new IncantoError("BAD_FORMAT", `Collider on '${nodeName}': rect needs "size": [width, height].`);
|
|
@@ -874,6 +875,254 @@ const colorScratchA = new Color();
|
|
|
874
875
|
const colorScratchB = new Color();
|
|
875
876
|
const colorScratchC = new Color();
|
|
876
877
|
//#endregion
|
|
878
|
+
//#region src/2d/tilemap-grid.ts
|
|
879
|
+
/**
|
|
880
|
+
* Normalize the `cells` prop into an index grid. Two author formats:
|
|
881
|
+
* - string rows: `"." `/`" "` = empty, digits `0-9` = tile index, anything
|
|
882
|
+
* else must be in `legend` (char → index). Unknown chars HARD-fail.
|
|
883
|
+
* - number rows: passed through (`-1` = empty).
|
|
884
|
+
* Ragged rows are right-padded with empty.
|
|
885
|
+
*/
|
|
886
|
+
function parseCells(cells, legend) {
|
|
887
|
+
const rows = [];
|
|
888
|
+
let width = 0;
|
|
889
|
+
for (const row of cells) {
|
|
890
|
+
const out = [];
|
|
891
|
+
if (typeof row === "string") for (const ch of row) {
|
|
892
|
+
const mapped = legend[ch];
|
|
893
|
+
if (mapped !== void 0) out.push(mapped);
|
|
894
|
+
else if (ch === "." || ch === " ") out.push(-1);
|
|
895
|
+
else if (ch >= "0" && ch <= "9") out.push(ch.charCodeAt(0) - 48);
|
|
896
|
+
else throw new IncantoError("BAD_FORMAT", `TileMap2D: unknown tile char '${ch}' — add it to "legend" (char → tile index), or use '.'/' ' for empty and digits 0-9.`);
|
|
897
|
+
}
|
|
898
|
+
else for (const v of row) out.push(v);
|
|
899
|
+
width = Math.max(width, out.length);
|
|
900
|
+
rows.push(out);
|
|
901
|
+
}
|
|
902
|
+
for (const row of rows) while (row.length < width) row.push(-1);
|
|
903
|
+
return rows;
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Greedy rectangle merge over the solid cells: maximal horizontal runs,
|
|
907
|
+
* extended downward while the identical run repeats. A platform level's
|
|
908
|
+
* hundreds of solid tiles collapse to a handful of colliders.
|
|
909
|
+
*/
|
|
910
|
+
function mergeSolidRects(grid, solid) {
|
|
911
|
+
const h = grid.length;
|
|
912
|
+
const w = h > 0 ? grid[0].length : 0;
|
|
913
|
+
const used = grid.map((row) => row.map(() => false));
|
|
914
|
+
const isSolid = (x, y) => solid.has(grid[y][x]);
|
|
915
|
+
const rects = [];
|
|
916
|
+
for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
|
|
917
|
+
if (!isSolid(x, y) || used[y][x]) continue;
|
|
918
|
+
let runW = 1;
|
|
919
|
+
while (x + runW < w && isSolid(x + runW, y) && !used[y][x + runW]) runW++;
|
|
920
|
+
let runH = 1;
|
|
921
|
+
grow: while (y + runH < h) {
|
|
922
|
+
for (let i = 0; i < runW; i++) if (!isSolid(x + i, y + runH) || used[y + runH][x + i]) break grow;
|
|
923
|
+
runH++;
|
|
924
|
+
}
|
|
925
|
+
for (let dy = 0; dy < runH; dy++) for (let i = 0; i < runW; i++) used[y + dy][x + i] = true;
|
|
926
|
+
rects.push({
|
|
927
|
+
x,
|
|
928
|
+
y,
|
|
929
|
+
w: runW,
|
|
930
|
+
h: runH
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
return rects;
|
|
934
|
+
}
|
|
935
|
+
//#endregion
|
|
936
|
+
//#region src/2d/nodes/tile-map-2d.ts
|
|
937
|
+
/**
|
|
938
|
+
* A whole tile level as ONE node — grid render in a single draw call plus
|
|
939
|
+
* greedy-merged static colliders. Author the level as rows of characters:
|
|
940
|
+
*
|
|
941
|
+
* { "name": "Level", "type": "TileMap2D", "props": {
|
|
942
|
+
* "texture": "$tiles", "tileSize": 32,
|
|
943
|
+
* "cells": [
|
|
944
|
+
* "..................",
|
|
945
|
+
* "......111.........",
|
|
946
|
+
* "000000000000000000"
|
|
947
|
+
* ],
|
|
948
|
+
* "solid": [0, 1] } }
|
|
949
|
+
*
|
|
950
|
+
* `.` / space = empty, digits 0-9 = atlas tile index, other chars map through
|
|
951
|
+
* `legend` (e.g. `{"G": 12}`). The atlas is read left-to-right, top-to-bottom
|
|
952
|
+
* in `tileSize` squares. Tiles listed in `solid` become static colliders,
|
|
953
|
+
* greedy-merged into a few rectangles (hundreds of cells → a handful of
|
|
954
|
+
* bodies). Cell (0,0) renders with its TOP-LEFT on this node's origin.
|
|
955
|
+
*/
|
|
956
|
+
var TileMap2D = class extends Node2D {
|
|
957
|
+
static typeName = "TileMap2D";
|
|
958
|
+
static props = {
|
|
959
|
+
/** Atlas texture asset ref; tiles are tileSize×tileSize squares in it. */
|
|
960
|
+
texture: { default: "" },
|
|
961
|
+
tileSize: { default: 32 },
|
|
962
|
+
/** Atlas columns; 0 = derive from the texture width. */
|
|
963
|
+
columns: { default: 0 },
|
|
964
|
+
/** Rows of chars (or number arrays, -1 empty). */
|
|
965
|
+
cells: { default: [] },
|
|
966
|
+
/** char → atlas tile index for non-digit chars. */
|
|
967
|
+
legend: { default: {} },
|
|
968
|
+
/** Tile indices that get merged static colliders. */
|
|
969
|
+
solid: { default: [] },
|
|
970
|
+
opacity: { default: 1 }
|
|
971
|
+
};
|
|
972
|
+
texture = "";
|
|
973
|
+
tileSize = 32;
|
|
974
|
+
columns = 0;
|
|
975
|
+
_cells = [];
|
|
976
|
+
_legend = {};
|
|
977
|
+
_solid = [];
|
|
978
|
+
/** Rebuild flags — raised when the map-shaping props are REPLACED. */
|
|
979
|
+
geometryDirty = true;
|
|
980
|
+
collidersDirty = true;
|
|
981
|
+
/** Replace the whole array to change the map (mutations are not watched). */
|
|
982
|
+
get cells() {
|
|
983
|
+
return this._cells;
|
|
984
|
+
}
|
|
985
|
+
set cells(value) {
|
|
986
|
+
this._cells = value;
|
|
987
|
+
this.geometryDirty = true;
|
|
988
|
+
this.collidersDirty = true;
|
|
989
|
+
}
|
|
990
|
+
get legend() {
|
|
991
|
+
return this._legend;
|
|
992
|
+
}
|
|
993
|
+
set legend(value) {
|
|
994
|
+
this._legend = value;
|
|
995
|
+
this.geometryDirty = true;
|
|
996
|
+
this.collidersDirty = true;
|
|
997
|
+
}
|
|
998
|
+
get solid() {
|
|
999
|
+
return this._solid;
|
|
1000
|
+
}
|
|
1001
|
+
set solid(value) {
|
|
1002
|
+
this._solid = value;
|
|
1003
|
+
this.collidersDirty = true;
|
|
1004
|
+
}
|
|
1005
|
+
opacity = 1;
|
|
1006
|
+
tileMesh = null;
|
|
1007
|
+
builtColumns = 0;
|
|
1008
|
+
builtRows = 0;
|
|
1009
|
+
/** Loader hook: bad cells fail at LOAD (unknown chars), not at render. */
|
|
1010
|
+
static validateJson(node) {
|
|
1011
|
+
const map = node;
|
|
1012
|
+
parseCells(map._cells, map._legend);
|
|
1013
|
+
}
|
|
1014
|
+
/** The parsed index grid (test/tooling hook). */
|
|
1015
|
+
grid() {
|
|
1016
|
+
return parseCells(this._cells, this._legend);
|
|
1017
|
+
}
|
|
1018
|
+
onReady() {
|
|
1019
|
+
this.rebuildColliders();
|
|
1020
|
+
}
|
|
1021
|
+
update(_dt) {
|
|
1022
|
+
if (this.collidersDirty) this.rebuildColliders();
|
|
1023
|
+
}
|
|
1024
|
+
rebuildColliders() {
|
|
1025
|
+
this.collidersDirty = false;
|
|
1026
|
+
for (const child of [...this.children]) if (child.name.startsWith("__tileSolid")) child.free();
|
|
1027
|
+
if (this._solid.length === 0) return;
|
|
1028
|
+
const ts = this.tileSize;
|
|
1029
|
+
mergeSolidRects(this.grid(), new Set(this._solid)).forEach((r, i) => {
|
|
1030
|
+
const body = new StaticBody2D(`__tileSolid${i}`);
|
|
1031
|
+
body.position = [(r.x + r.w / 2) * ts, (r.y + r.h / 2) * ts];
|
|
1032
|
+
body.collider = {
|
|
1033
|
+
shape: "rect",
|
|
1034
|
+
size: [r.w * ts, r.h * ts]
|
|
1035
|
+
};
|
|
1036
|
+
this.addChild(body);
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
/** @internal The drawable mesh (lazily created under the backing object). */
|
|
1040
|
+
_mesh() {
|
|
1041
|
+
if (!this.tileMesh) {
|
|
1042
|
+
this.tileMesh = new Mesh(new BufferGeometry(), new MeshBasicMaterial({
|
|
1043
|
+
transparent: true,
|
|
1044
|
+
depthWrite: false
|
|
1045
|
+
}));
|
|
1046
|
+
this._ensureObject2D().add(this.tileMesh);
|
|
1047
|
+
}
|
|
1048
|
+
return this.tileMesh;
|
|
1049
|
+
}
|
|
1050
|
+
_syncObject2D(assets) {
|
|
1051
|
+
super._syncObject2D(assets);
|
|
1052
|
+
const mesh = this._mesh();
|
|
1053
|
+
const mat = mesh.material;
|
|
1054
|
+
if (!this.texture || !assets) {
|
|
1055
|
+
mesh.visible = false;
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
const texture = assets.getTexture(this.texture);
|
|
1059
|
+
const image = texture.image;
|
|
1060
|
+
if (!image?.width || !image.height) {
|
|
1061
|
+
mesh.visible = false;
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
mesh.visible = this.opacity > 0;
|
|
1065
|
+
mesh.renderOrder = this.effectiveRenderOrder;
|
|
1066
|
+
mat.opacity = this.opacity;
|
|
1067
|
+
if (mat.map !== texture) {
|
|
1068
|
+
texture.magFilter = NearestFilter;
|
|
1069
|
+
mat.map = texture;
|
|
1070
|
+
mat.needsUpdate = true;
|
|
1071
|
+
this.geometryDirty = true;
|
|
1072
|
+
}
|
|
1073
|
+
const columns = this.columns > 0 ? this.columns : Math.max(1, Math.floor(image.width / this.tileSize));
|
|
1074
|
+
const atlasRows = Math.max(1, Math.floor(image.height / this.tileSize));
|
|
1075
|
+
if (columns !== this.builtColumns || atlasRows !== this.builtRows) this.geometryDirty = true;
|
|
1076
|
+
if (!this.geometryDirty) return;
|
|
1077
|
+
this.geometryDirty = false;
|
|
1078
|
+
this.builtColumns = columns;
|
|
1079
|
+
this.builtRows = atlasRows;
|
|
1080
|
+
this.buildGeometry(mesh.geometry, columns, atlasRows);
|
|
1081
|
+
}
|
|
1082
|
+
/** One quad per visible tile, atlas UVs — a single draw call for the level. */
|
|
1083
|
+
buildGeometry(geometry, columns, atlasRows) {
|
|
1084
|
+
const grid = this.grid();
|
|
1085
|
+
const ts = this.tileSize;
|
|
1086
|
+
const positions = [];
|
|
1087
|
+
const uvs = [];
|
|
1088
|
+
const indices = [];
|
|
1089
|
+
for (let y = 0; y < grid.length; y++) {
|
|
1090
|
+
const row = grid[y];
|
|
1091
|
+
for (let x = 0; x < row.length; x++) {
|
|
1092
|
+
const tile = row[x];
|
|
1093
|
+
if (tile < 0) continue;
|
|
1094
|
+
const x0 = x * ts;
|
|
1095
|
+
const x1 = x0 + ts;
|
|
1096
|
+
const y0 = -(y * ts);
|
|
1097
|
+
const y1 = -((y + 1) * ts);
|
|
1098
|
+
const base = positions.length / 3;
|
|
1099
|
+
positions.push(x0, y0, 0, x1, y0, 0, x1, y1, 0, x0, y1, 0);
|
|
1100
|
+
const col = tile % columns;
|
|
1101
|
+
const rowIdx = Math.floor(tile / columns);
|
|
1102
|
+
const u0 = col / columns;
|
|
1103
|
+
const u1 = (col + 1) / columns;
|
|
1104
|
+
const v0 = 1 - rowIdx / atlasRows;
|
|
1105
|
+
const v1 = 1 - (rowIdx + 1) / atlasRows;
|
|
1106
|
+
uvs.push(u0, v0, u1, v0, u1, v1, u0, v1);
|
|
1107
|
+
indices.push(base, base + 2, base + 1, base, base + 3, base + 2);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
geometry.setAttribute("position", new BufferAttribute(new Float32Array(positions), 3));
|
|
1111
|
+
geometry.setAttribute("uv", new BufferAttribute(new Float32Array(uvs), 2));
|
|
1112
|
+
geometry.setIndex(indices);
|
|
1113
|
+
geometry.computeBoundingSphere();
|
|
1114
|
+
}
|
|
1115
|
+
/** Test hook: rendered tile count (quads in the built geometry). */
|
|
1116
|
+
get renderedTileCount() {
|
|
1117
|
+
const pos = this.tileMesh?.geometry.getAttribute("position");
|
|
1118
|
+
return pos ? pos.count / 4 : 0;
|
|
1119
|
+
}
|
|
1120
|
+
free() {
|
|
1121
|
+
this.tileMesh?.geometry.dispose();
|
|
1122
|
+
super.free();
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
//#endregion
|
|
877
1126
|
//#region src/2d/nodes/ui-layer.ts
|
|
878
1127
|
const ANCHORS = [
|
|
879
1128
|
"top-left",
|
|
@@ -949,6 +1198,7 @@ function registerNodes2D() {
|
|
|
949
1198
|
registerNode(CharacterBody2D);
|
|
950
1199
|
registerNode(CharacterController2D);
|
|
951
1200
|
registerNode(Joint2D);
|
|
1201
|
+
registerNode(TileMap2D);
|
|
952
1202
|
}
|
|
953
1203
|
//#endregion
|
|
954
|
-
export {
|
|
1204
|
+
export { RigidBody2D as _, parseCells as a, validateCollider2D as b, ColorRect2D as c, AnimatedSprite2D as d, Sprite2D as f, PhysicsBody2D as g, CharacterBody2D as h, mergeSolidRects as i, CharacterController2D as l, Area2D as m, UILayer as n, Particles2D as o, Joint2D as p, TileMap2D as r, Label as s, registerNodes2D as t, Camera2D as u, StaticBody2D as v, Node2D as y };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { f as Node, t as buildNodeJson, y as Signal } from "./loader-B4OEXDZ8.js";
|
|
2
|
+
import { t as registerCoreNodes } from "./register-DJZXxwAn.js";
|
|
2
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
3
|
-
import { t as registerCoreNodes } from "./register-Dl_ixIJe.js";
|
|
4
4
|
import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
|
|
5
5
|
import { l as registerNode } from "./registry-BVJ2HbCn.js";
|
|
6
6
|
//#region src/net/network-manager.ts
|