incanto 0.4.2 → 0.5.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.
Files changed (43) hide show
  1. package/dist/2d.d.ts +74 -6
  2. package/dist/2d.js +4 -4
  3. package/dist/3d.d.ts +174 -16
  4. package/dist/3d.js +4 -4
  5. package/dist/{audio-player-DqUR3XFs.d.ts → audio-player-DkBqRTs4.d.ts} +1 -1
  6. package/dist/{behavior-BAQq7HGM.d.ts → behavior-CWhW3oa6.d.ts} +54 -0
  7. package/dist/{create-game-CZHROKcT.js → create-game-BFESHv1X.js} +37 -25
  8. package/dist/{create-game-CMS7DiPi.js → create-game-D8UvwXme.js} +231 -8
  9. package/dist/debug.d.ts +1 -1
  10. package/dist/{duplicate-DP2WPYom.js → duplicate-BEvGBtb_.js} +1 -1
  11. package/dist/{gameplay-Ccruc3Wd.js → gameplay-CDFgSG6z.js} +293 -24
  12. package/dist/gameplay.d.ts +113 -2
  13. package/dist/gameplay.js +2 -2
  14. package/dist/index.d.ts +139 -5
  15. package/dist/index.js +59 -6
  16. package/dist/{loader-CGs_G-r0.js → loader-B4OEXDZ8.js} +60 -0
  17. package/dist/{loader-Mo0KghCv.d.ts → loader-D2u0fVW2.d.ts} +1 -1
  18. package/dist/net.d.ts +1 -1
  19. package/dist/net.js +1 -1
  20. package/dist/{particle-sim-DYuSUxvK.js → particle-sim-CFkILGwh.js} +84 -3
  21. package/dist/{particle-sim-CbN4YUuH.d.ts → particle-sim-CwJ5rI_P.d.ts} +3 -0
  22. package/dist/{physics-2d-KuMWPTf6.js → physics-2d-KXn1N1jF.js} +95 -10
  23. package/dist/{physics-3d-CXOBOUKG.js → physics-3d-C58oQzTX.js} +121 -43
  24. package/dist/react.d.ts +1 -1
  25. package/dist/react.js +1 -1
  26. package/dist/{register-B0gq63VW.js → register-C35HDZpm.js} +2 -2
  27. package/dist/{register-DPEV9_9t.js → register-D71C4rDC.js} +76 -5
  28. package/dist/{register-BuUV1_KB.js → register-Dl_ixIJe.js} +275 -2
  29. package/dist/{register-02aSywx2.js → register-DvPHJdVj.js} +390 -59
  30. package/dist/test.d.ts +2 -2
  31. package/dist/test.js +10 -10
  32. package/editor/assets/{agent8-DUVZGcuO.js → agent8-DruKhR22.js} +1 -1
  33. package/editor/assets/index-D2qufqUo.js +7417 -0
  34. package/editor/index.html +1 -1
  35. package/package.json +1 -1
  36. package/schemas/scene.schema.json +847 -133
  37. package/skills/incanto-3d-models.md +24 -1
  38. package/skills/incanto-building-3d-games.md +35 -3
  39. package/skills/incanto-gameplay-behaviors.md +60 -0
  40. package/skills/incanto-hud.md +58 -0
  41. package/skills/incanto-node-reference.md +113 -0
  42. package/skills/incanto-physics-and-input.md +47 -0
  43. package/editor/assets/index-DsM2Yp9F.js +0 -7365
@@ -49,6 +49,9 @@ declare class ParticleSim {
49
49
  private spawnAccumulator;
50
50
  private everSpawned;
51
51
  constructor(config: ParticleSimConfig, rng: Rng);
52
+ /** Change the emission rate LIVE (particles/sec; 0 pauses emission) — lets an
53
+ * emitter toggle on/off at runtime (drift smoke, throttle flames). */
54
+ setRate(rate: number): void;
52
55
  get count(): number;
53
56
  /** True when nothing is alive and at least one particle has ever spawned. */
54
57
  get done(): boolean;
@@ -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 { d as CharacterBody2D, f as PhysicsBody2D, g as validateCollider2D, h as Node2D, p as RigidBody2D, u as Area2D } from "./register-DPEV9_9t.js";
3
+ import { _ as validateCollider2D, d as Area2D, f as CharacterBody2D, g as Node2D, m as RigidBody2D, p as PhysicsBody2D, u as Joint2D } from "./register-D71C4rDC.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({
@@ -131,16 +131,94 @@ var Physics2D = class {
131
131
  for (let i = 0; i < raw.length; i++) px[i] = raw[i] * SCALE;
132
132
  return px;
133
133
  }
134
+ /** Create newly-arrived joints; tear down departed ones. */
135
+ syncJoints(structureChanged) {
136
+ const R = this.R;
137
+ if (structureChanged) {
138
+ for (const [joint, impulse] of this.joints) if (!this.jointSet.has(joint)) {
139
+ this.world.removeImpulseJoint(impulse, true);
140
+ this.joints.delete(joint);
141
+ }
142
+ }
143
+ for (const joint of this.jointSet) {
144
+ if (this.joints.has(joint)) continue;
145
+ const { a, b } = joint._resolveBodies();
146
+ const ea = this.entries.get(a);
147
+ const eb = this.entries.get(b);
148
+ if (!ea || !eb) continue;
149
+ const a1 = {
150
+ x: (joint.anchor[0] ?? 0) / SCALE,
151
+ y: (joint.anchor[1] ?? 0) / SCALE
152
+ };
153
+ const a2 = {
154
+ x: (joint.targetAnchor[0] ?? 0) / SCALE,
155
+ y: (joint.targetAnchor[1] ?? 0) / SCALE
156
+ };
157
+ let length = joint.length / SCALE;
158
+ if (joint.length === 0 && (joint.type === "rope" || joint.type === "spring")) {
159
+ const ta = ea.body.translation();
160
+ const tb = eb.body.translation();
161
+ length = Math.hypot(tb.x - ta.x, tb.y - ta.y);
162
+ }
163
+ let data;
164
+ if (joint.type === "fixed") data = R.JointData.fixed(a1, 0, a2, 0);
165
+ else if (joint.type === "rope") data = R.JointData.rope(length, a1, a2);
166
+ else if (joint.type === "spring") data = R.JointData.spring(length, joint.stiffness, joint.damping, a1, a2);
167
+ else data = R.JointData.revolute(a1, a2);
168
+ this.joints.set(joint, this.world.createImpulseJoint(data, ea.body, eb.body, true));
169
+ }
170
+ }
171
+ /**
172
+ * World-space raycast in PIXELS (y-down). `exclude` skips that body — a
173
+ * shooter probing from inside its own collider needs it. Sensors (Area2D)
174
+ * never block rays. Returns distance (px), the surface normal, and the hit
175
+ * body — or null.
176
+ */
177
+ castRay(origin, dir, maxLen, exclude, opts) {
178
+ const len = Math.hypot(dir[0], dir[1]) || 1;
179
+ const ray = new this.R.Ray({
180
+ x: origin[0] / SCALE,
181
+ y: origin[1] / SCALE
182
+ }, {
183
+ x: dir[0] / len,
184
+ y: dir[1] / len
185
+ });
186
+ const excludeCollider = exclude ? this.entries.get(exclude)?.collider : void 0;
187
+ const flags = opts?.staticOnly ? this.R.QueryFilterFlags.EXCLUDE_DYNAMIC | this.R.QueryFilterFlags.EXCLUDE_KINEMATIC : void 0;
188
+ const hit = this.world.castRayAndGetNormal(ray, maxLen / SCALE, true, flags, void 0, excludeCollider, void 0, (collider) => !collider.isSensor());
189
+ if (!hit) return null;
190
+ return {
191
+ distance: hit.timeOfImpact * SCALE,
192
+ normal: [hit.normal.x, hit.normal.y],
193
+ node: this.byColliderHandle.get(hit.collider.handle) ?? null
194
+ };
195
+ }
134
196
  dispose() {
135
197
  this.disconnect();
136
198
  this.unregisterDebug();
137
199
  this.world.free();
138
200
  }
201
+ lastStructure = -1;
202
+ lastRoot = null;
203
+ jointSet = /* @__PURE__ */ new Set();
204
+ joints = /* @__PURE__ */ new Map();
205
+ bodySet = /* @__PURE__ */ new Set();
139
206
  syncBodies() {
140
207
  const scene = this.engine.scene;
141
208
  if (!scene) return;
142
- const seen = /* @__PURE__ */ new Set();
143
- collectBodies(scene.root, seen);
209
+ const version = scene.tree._structureVersion;
210
+ const structureChanged = version !== this.lastStructure || scene.root !== this.lastRoot;
211
+ if (structureChanged) {
212
+ this.lastStructure = version;
213
+ this.lastRoot = scene.root;
214
+ this.bodySet.clear();
215
+ collectBodies(scene.root, this.bodySet);
216
+ }
217
+ if (structureChanged) {
218
+ this.jointSet.clear();
219
+ collectJoints(scene.root, this.jointSet);
220
+ }
221
+ const seen = this.bodySet;
144
222
  for (const node of seen) {
145
223
  if (node.collider.shape === void 0) {
146
224
  const stale = this.entries.get(node);
@@ -157,7 +235,7 @@ var Physics2D = class {
157
235
  continue;
158
236
  }
159
237
  const existing = this.entries.get(node);
160
- if (existing && existing.colliderKey !== JSON.stringify(node.collider)) {
238
+ if (existing && node._colliderChanged) {
161
239
  this.byColliderHandle.delete(existing.collider.handle);
162
240
  this.world.removeRigidBody(existing.body);
163
241
  this.entries.delete(node);
@@ -165,11 +243,14 @@ var Physics2D = class {
165
243
  }
166
244
  this.ensureEntry(node);
167
245
  }
168
- for (const [node, e] of this.entries) if (!seen.has(node)) {
169
- this.byColliderHandle.delete(e.collider.handle);
170
- this.world.removeRigidBody(e.body);
171
- this.entries.delete(node);
172
- node._physics = null;
246
+ this.syncJoints(structureChanged);
247
+ if (structureChanged) {
248
+ for (const [node, e] of this.entries) if (!seen.has(node)) {
249
+ this.byColliderHandle.delete(e.collider.handle);
250
+ this.world.removeRigidBody(e.body);
251
+ this.entries.delete(node);
252
+ node._physics = null;
253
+ }
173
254
  }
174
255
  }
175
256
  ensureEntry(node) {
@@ -199,7 +280,6 @@ var Physics2D = class {
199
280
  e = {
200
281
  body,
201
282
  collider,
202
- colliderKey: JSON.stringify(node.collider),
203
283
  lastX: wx,
204
284
  lastY: wy,
205
285
  lastVX: 0,
@@ -207,6 +287,7 @@ var Physics2D = class {
207
287
  };
208
288
  this.entries.set(node, e);
209
289
  this.byColliderHandle.set(collider.handle, node);
290
+ node._colliderChanged = false;
210
291
  node._physics = this;
211
292
  return e;
212
293
  }
@@ -234,6 +315,10 @@ var Physics2D = class {
234
315
  return e;
235
316
  }
236
317
  };
318
+ function collectJoints(node, out) {
319
+ if (node instanceof Joint2D) out.add(node);
320
+ for (const c of node.children) collectJoints(c, out);
321
+ }
237
322
  function collectBodies(node, out) {
238
323
  if (node instanceof PhysicsBody2D) out.add(node);
239
324
  for (const c of node.children) collectBodies(c, out);
@@ -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 { A as validateCollider3D, D as RigidBody3D, E as PhysicsBody3D, T as CharacterBody3D, b as Terrain3D, k as Node3D, w as Area3D } from "./register-02aSywx2.js";
4
+ import { D as CharacterBody3D, E as Area3D, M as validateCollider3D, O as PhysicsBody3D, T as Joint3D, j as Node3D, k as RigidBody3D, x as Terrain3D } from "./register-DvPHJdVj.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({
@@ -91,35 +91,33 @@ var Physics3D = class {
91
91
  ];
92
92
  node._interpPrev = node._interpCurr ?? next;
93
93
  node._interpCurr = next;
94
- node.position = [
95
- next[0],
96
- next[1],
97
- next[2]
98
- ];
94
+ const pos = node.position;
95
+ pos[0] = next[0];
96
+ pos[1] = next[1];
97
+ pos[2] = next[2];
99
98
  }
100
99
  if (node instanceof RigidBody3D) {
101
100
  if (!node.fixedRotation) {
102
101
  const q = e.body.rotation();
103
102
  eulerScratch.setFromQuaternion(quatScratch.set(q.x, q.y, q.z, q.w), "XYZ");
104
- node.rotation = [
105
- eulerScratch.x / DEG,
106
- eulerScratch.y / DEG,
107
- eulerScratch.z / DEG
108
- ];
103
+ const rot = node.rotation;
104
+ rot[0] = eulerScratch.x / DEG;
105
+ rot[1] = eulerScratch.y / DEG;
106
+ rot[2] = eulerScratch.z / DEG;
109
107
  }
110
108
  const lv = e.body.linvel();
111
- node.linearVelocity = [
112
- lv.x,
113
- lv.y,
114
- lv.z
115
- ];
116
- e.lastV = [
117
- lv.x,
118
- lv.y,
119
- lv.z
120
- ];
109
+ const nv = node.linearVelocity;
110
+ nv[0] = lv.x;
111
+ nv[1] = lv.y;
112
+ nv[2] = lv.z;
113
+ e.lastV[0] = lv.x;
114
+ e.lastV[1] = lv.y;
115
+ e.lastV[2] = lv.z;
121
116
  }
122
- e.last = worldPosition3D(node);
117
+ const wp = worldPosition3D(node);
118
+ e.last[0] = wp[0];
119
+ e.last[1] = wp[1];
120
+ e.last[2] = wp[2];
123
121
  }
124
122
  this.events.drainCollisionEvents((h1, h2, started) => {
125
123
  const a = this.byColliderHandle.get(h1);
@@ -162,11 +160,27 @@ var Physics3D = class {
162
160
  this.unregisterDebug();
163
161
  this.world.free();
164
162
  }
163
+ lastStructure = -1;
164
+ lastRoot = null;
165
+ jointSet = /* @__PURE__ */ new Set();
166
+ joints = /* @__PURE__ */ new Map();
167
+ bodySet = /* @__PURE__ */ new Set();
165
168
  syncBodies() {
166
169
  const scene = this.engine.scene;
167
170
  if (!scene) return;
168
- const seen = /* @__PURE__ */ new Set();
169
- collectBodies(scene.root, seen);
171
+ const version = scene.tree._structureVersion;
172
+ const structureChanged = version !== this.lastStructure || scene.root !== this.lastRoot;
173
+ if (structureChanged) {
174
+ this.lastStructure = version;
175
+ this.lastRoot = scene.root;
176
+ this.bodySet.clear();
177
+ collectBodies(scene.root, this.bodySet);
178
+ }
179
+ if (structureChanged) {
180
+ this.jointSet.clear();
181
+ collectJoints(scene.root, this.jointSet);
182
+ }
183
+ const seen = this.bodySet;
170
184
  for (const node of seen) {
171
185
  if (node.collider.shape === void 0) {
172
186
  const stale = this.entries.get(node);
@@ -184,7 +198,7 @@ var Physics3D = class {
184
198
  continue;
185
199
  }
186
200
  const existing = this.entries.get(node);
187
- if (existing && existing.colliderKey !== JSON.stringify(node.collider)) {
201
+ if (existing && node._colliderChanged) {
188
202
  this.byColliderHandle.delete(existing.collider.handle);
189
203
  this.world.removeRigidBody(existing.body);
190
204
  this.entries.delete(node);
@@ -193,12 +207,15 @@ var Physics3D = class {
193
207
  }
194
208
  this.ensureEntry(node);
195
209
  }
196
- for (const [node, e] of this.entries) if (!seen.has(node)) {
197
- this.byColliderHandle.delete(e.collider.handle);
198
- this.world.removeRigidBody(e.body);
199
- this.entries.delete(node);
200
- node._physics = null;
201
- node._interpPrev = node._interpCurr = null;
210
+ this.syncJoints(structureChanged);
211
+ if (structureChanged) {
212
+ for (const [node, e] of this.entries) if (!seen.has(node)) {
213
+ this.byColliderHandle.delete(e.collider.handle);
214
+ this.world.removeRigidBody(e.body);
215
+ this.entries.delete(node);
216
+ node._physics = null;
217
+ node._interpPrev = node._interpCurr = null;
218
+ }
202
219
  }
203
220
  }
204
221
  ensureEntry(node) {
@@ -231,7 +248,6 @@ var Physics3D = class {
231
248
  if (node instanceof RigidBody3D) colDesc.setMass(node.mass).setFriction(node.friction).setRestitution(node.restitution);
232
249
  const collider = this.world.createCollider(colDesc, body);
233
250
  e = {
234
- colliderKey: JSON.stringify(node.collider),
235
251
  body,
236
252
  collider,
237
253
  last: [
@@ -247,6 +263,7 @@ var Physics3D = class {
247
263
  };
248
264
  this.entries.set(node, e);
249
265
  this.byColliderHandle.set(collider.handle, node);
266
+ node._colliderChanged = false;
250
267
  node._physics = this;
251
268
  if (node instanceof RigidBody3D) node._physics3d = this;
252
269
  return e;
@@ -258,7 +275,9 @@ var Physics3D = class {
258
275
  y: p[1],
259
276
  z: p[2]
260
277
  }, true);
261
- e.last = p;
278
+ e.last[0] = p[0];
279
+ e.last[1] = p[1];
280
+ e.last[2] = p[2];
262
281
  node._interpPrev = node._interpCurr = null;
263
282
  }
264
283
  if (node instanceof RigidBody3D) {
@@ -304,6 +323,51 @@ var Physics3D = class {
304
323
  0
305
324
  ];
306
325
  }
326
+ /** Create newly-arrived joints; tear down departed ones. */
327
+ syncJoints(structureChanged) {
328
+ const R = this.R;
329
+ if (structureChanged) {
330
+ for (const [joint, impulse] of this.joints) if (!this.jointSet.has(joint)) {
331
+ this.world.removeImpulseJoint(impulse, true);
332
+ this.joints.delete(joint);
333
+ }
334
+ }
335
+ for (const joint of this.jointSet) {
336
+ if (this.joints.has(joint)) continue;
337
+ const { a, b } = joint._resolveBodies();
338
+ const ea = this.entries.get(a);
339
+ const eb = this.entries.get(b);
340
+ if (!ea || !eb) continue;
341
+ const a1 = {
342
+ x: joint.anchor[0] ?? 0,
343
+ y: joint.anchor[1] ?? 0,
344
+ z: joint.anchor[2] ?? 0
345
+ };
346
+ const a2 = {
347
+ x: joint.targetAnchor[0] ?? 0,
348
+ y: joint.targetAnchor[1] ?? 0,
349
+ z: joint.targetAnchor[2] ?? 0
350
+ };
351
+ let length = joint.length;
352
+ if (joint.length === 0 && (joint.type === "rope" || joint.type === "spring")) {
353
+ const ta = ea.body.translation();
354
+ const tb = eb.body.translation();
355
+ length = Math.hypot(tb.x - ta.x, tb.y - ta.y, tb.z - ta.z);
356
+ }
357
+ const identity = {
358
+ x: 0,
359
+ y: 0,
360
+ z: 0,
361
+ w: 1
362
+ };
363
+ let data;
364
+ if (joint.type === "fixed") data = R.JointData.fixed(a1, identity, a2, identity);
365
+ else if (joint.type === "rope") data = R.JointData.rope(length, a1, a2);
366
+ else if (joint.type === "spring") data = R.JointData.spring(length, joint.stiffness, joint.damping, a1, a2);
367
+ else data = R.JointData.spherical(a1, a2);
368
+ this.joints.set(joint, this.world.createImpulseJoint(data, ea.body, eb.body, true));
369
+ }
370
+ }
307
371
  /**
308
372
  * World-space raycast (meters). `exclude` skips that body's collider —
309
373
  * ground probes from inside a capsule need it. `opts.staticOnly` hits ONLY the
@@ -338,6 +402,10 @@ var Physics3D = class {
338
402
  };
339
403
  const quatScratch = new Quaternion();
340
404
  const eulerScratch = new Euler();
405
+ function collectJoints(node, out) {
406
+ if (node instanceof Joint3D) out.add(node);
407
+ for (const c of node.children) collectJoints(c, out);
408
+ }
341
409
  function collectBodies(node, out) {
342
410
  if (node instanceof PhysicsBody3D) out.add(node);
343
411
  for (const c of node.children) collectBodies(c, out);
@@ -346,6 +414,12 @@ function collectBodies(node, out) {
346
414
  * Composed world position (translation only — ancestor rotation/scale are NOT
347
415
  * supported for physics bodies; keep body ancestors untransformed or offset-only).
348
416
  */
417
+ const posScratch = [
418
+ 0,
419
+ 0,
420
+ 0
421
+ ];
422
+ /** Writes into the shared scratch — consume before the next call. */
349
423
  function worldPosition3D(node) {
350
424
  let x = 0;
351
425
  let y = 0;
@@ -355,12 +429,17 @@ function worldPosition3D(node) {
355
429
  y += n.position[1] ?? 0;
356
430
  z += n.position[2] ?? 0;
357
431
  }
358
- return [
359
- x,
360
- y,
361
- z
362
- ];
432
+ posScratch[0] = x;
433
+ posScratch[1] = y;
434
+ posScratch[2] = z;
435
+ return posScratch;
363
436
  }
437
+ const offsetScratch = [
438
+ 0,
439
+ 0,
440
+ 0
441
+ ];
442
+ /** Writes into the shared scratch — consume before the next call. */
364
443
  function parentWorldOffset3D(node) {
365
444
  let x = 0;
366
445
  let y = 0;
@@ -370,11 +449,10 @@ function parentWorldOffset3D(node) {
370
449
  y += n.position[1] ?? 0;
371
450
  z += n.position[2] ?? 0;
372
451
  }
373
- return [
374
- x,
375
- y,
376
- z
377
- ];
452
+ offsetScratch[0] = x;
453
+ offsetScratch[1] = y;
454
+ offsetScratch[2] = z;
455
+ return offsetScratch;
378
456
  }
379
457
  function parseCollider3D(R, node) {
380
458
  const collider = node.collider;
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-BAQq7HGM.js";
2
+ import { n as BehaviorCtor, v as Engine, w as Scene } from "./behavior-CWhW3oa6.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-CMS7DiPi.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-CZHROKcT.js").then((n) => n.n)).createGame2D(o)))(opts);
159
+ const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-D8UvwXme.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-BFESHv1X.js").then((n) => n.n)).createGame2D(o)))(opts);
160
160
  if (disposed) {
161
161
  next.dispose();
162
162
  return;
@@ -1,6 +1,6 @@
1
- import { f as Node, t as buildNodeJson, y as Signal } from "./loader-CGs_G-r0.js";
1
+ import { f as Node, t as buildNodeJson, y as Signal } from "./loader-B4OEXDZ8.js";
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
- import { t as registerCoreNodes } from "./register-BuUV1_KB.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
@@ -1,8 +1,8 @@
1
- import { f as Node } from "./loader-CGs_G-r0.js";
2
- import { i as applyParticlePreset, r as PARTICLE_PRESET_NAMES, t as ParticleSim } from "./particle-sim-DYuSUxvK.js";
1
+ import { f as Node } from "./loader-B4OEXDZ8.js";
2
+ import { i as applyParticlePreset, r as PARTICLE_PRESET_NAMES, t as ParticleSim } from "./particle-sim-CFkILGwh.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-BuUV1_KB.js";
5
+ import { t as registerCoreNodes } from "./register-Dl_ixIJe.js";
6
6
  import { i as getNodeSchema, l as registerNode } from "./registry-BVJ2HbCn.js";
7
7
  import { AdditiveBlending, CanvasTexture, Color, DynamicDrawUsage, Group, InstancedMesh, MathUtils, Matrix4, Mesh, MeshBasicMaterial, NormalBlending, Object3D, PlaneGeometry } from "three";
8
8
  //#region src/2d/physics/collider-validate.ts
@@ -96,7 +96,21 @@ var PhysicsBody2D = class extends Node2D {
96
96
  static props = { collider: { default: {} } };
97
97
  /** The unified collision model: every collider participant can emit these. */
98
98
  static signals = ["triggerEnter", "triggerExit"];
99
- collider = {};
99
+ _collider = {};
100
+ /** @internal Physics resync flag — raised when `collider` is REPLACED. */
101
+ _colliderChanged = false;
102
+ /**
103
+ * Collider shape prop. Change it by REPLACING the object
104
+ * (`body.collider = { shape: 'box', size: [...] }`) — physics rebuilds the
105
+ * body on replacement; in-place mutation of the old object is not watched.
106
+ */
107
+ get collider() {
108
+ return this._collider;
109
+ }
110
+ set collider(value) {
111
+ this._collider = value;
112
+ this._colliderChanged = true;
113
+ }
100
114
  /** Loader hook: bad collider shapes fail at LOAD, not at physics start. */
101
115
  static validateJson(node) {
102
116
  validateCollider2D(node.collider, node.name);
@@ -163,6 +177,62 @@ var CharacterBody2D = class extends PhysicsBody2D {
163
177
  }
164
178
  };
165
179
  //#endregion
180
+ //#region src/2d/nodes/joint-2d.ts
181
+ /**
182
+ * A physics joint linking its PARENT body to `target` (a node path to the
183
+ * other body). Godot-style placement: the joint lives as a child of body A.
184
+ *
185
+ * { "name": "Hinge", "type": "Joint2D",
186
+ * "props": { "type": "revolute", "target": "%Anchor", "anchor": [0, -20] } }
187
+ *
188
+ * Types: `fixed` welds the bodies rigidly · `revolute` is a pin/hinge at the
189
+ * anchors · `rope` caps the anchor distance at `length` (px; 0 = measured at
190
+ * creation) · `spring` pulls toward `length` with `stiffness`/`damping`.
191
+ * Anchors are LOCAL pixel offsets on each body.
192
+ */
193
+ var Joint2D = class extends Node2D {
194
+ static typeName = "Joint2D";
195
+ static props = {
196
+ type: {
197
+ default: "revolute",
198
+ options: [
199
+ "fixed",
200
+ "revolute",
201
+ "rope",
202
+ "spring"
203
+ ]
204
+ },
205
+ target: { default: "" },
206
+ anchor: { default: [0, 0] },
207
+ targetAnchor: { default: [0, 0] },
208
+ length: { default: 0 },
209
+ stiffness: { default: 50 },
210
+ damping: { default: 5 }
211
+ };
212
+ type = "revolute";
213
+ target = "";
214
+ anchor = [0, 0];
215
+ targetAnchor = [0, 0];
216
+ /** rope/spring rest length in px; 0 = measure body distance at creation. */
217
+ length = 0;
218
+ stiffness = 50;
219
+ damping = 5;
220
+ onEnterTree() {
221
+ if (!(this.parent instanceof PhysicsBody2D)) throw new IncantoError("TREE_VIOLATION", `Joint2D '${this.name}' must be a child of a physics body (its body A); parent is '${this.parent?.name ?? "none"}'.`);
222
+ if (this.target === "") throw new IncantoError("BAD_FORMAT", `Joint2D '${this.name}' needs a "target" node path to the body it links to.`);
223
+ }
224
+ /** @internal The two bodies, resolved (throws on a bad target at step time). */
225
+ _resolveBodies() {
226
+ const a = this.parent;
227
+ const b = this.getNodeOrNull(this.target);
228
+ if (!(b instanceof PhysicsBody2D)) throw new IncantoError("NODE_NOT_FOUND", `Joint2D '${this.name}': target '${this.target}' is not a physics body.`);
229
+ return {
230
+ a,
231
+ b
232
+ };
233
+ }
234
+ };
235
+ //#endregion
166
236
  //#region src/2d/nodes/sprite-2d.ts
167
237
  /** Shared unit quad — sprites are sized via the quad's scale, never THREE.Sprite. */
168
238
  const UNIT_PLANE$2 = new PlaneGeometry(1, 1);
@@ -846,6 +916,7 @@ function registerNodes2D() {
846
916
  registerNode(Area2D);
847
917
  registerNode(CharacterBody2D);
848
918
  registerNode(CharacterController2D);
919
+ registerNode(Joint2D);
849
920
  }
850
921
  //#endregion
851
- export { ColorRect2D as a, AnimatedSprite2D as c, CharacterBody2D as d, PhysicsBody2D as f, validateCollider2D as g, Node2D as h, Label as i, Sprite2D as l, StaticBody2D as m, UILayer as n, CharacterController2D as o, RigidBody2D as p, Particles2D as r, Camera2D as s, registerNodes2D as t, Area2D as u };
922
+ export { validateCollider2D as _, ColorRect2D as a, AnimatedSprite2D as c, Area2D as d, CharacterBody2D as f, Node2D as g, StaticBody2D as h, Label as i, Sprite2D as l, RigidBody2D as m, UILayer as n, CharacterController2D as o, PhysicsBody2D as p, Particles2D as r, Camera2D as s, registerNodes2D as t, Joint2D as u };