incanto 0.1.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/LICENSE +30 -0
- package/README.md +36 -0
- package/THIRD-PARTY-NOTICES.md +88 -0
- package/assets/audio/attacked.mp3 +0 -0
- package/assets/audio/explosion.mp3 +0 -0
- package/assets/audio/gold_loot.mp3 +0 -0
- package/assets/audio/heal.mp3 +0 -0
- package/assets/audio/hit_metal_bang.mp3 +0 -0
- package/assets/audio/ice_spear.mp3 +0 -0
- package/assets/audio/monster_died.mp3 +0 -0
- package/assets/audio/slash.mp3 +0 -0
- package/assets/audio/smite.mp3 +0 -0
- package/assets/audio/spells_cast.mp3 +0 -0
- package/assets/audio/ui_click.wav +0 -0
- package/assets/audio/walk.mp3 +0 -0
- package/assets/catalog.json +390 -0
- package/assets/characters/2dbasic.json +41 -0
- package/assets/characters/2dbasic.png +0 -0
- package/assets/characters/ghost.json +46 -0
- package/assets/characters/ghost.png +0 -0
- package/assets/characters/goblin.json +40 -0
- package/assets/characters/goblin.png +0 -0
- package/assets/characters/medieval-knight.json +41 -0
- package/assets/characters/medieval-knight.png +0 -0
- package/assets/effects/swoosh.png +0 -0
- package/assets/items/box.png +0 -0
- package/assets/items/buff_potion.png +0 -0
- package/assets/items/coin.png +0 -0
- package/assets/items/gem.png +0 -0
- package/assets/items/gold.png +0 -0
- package/assets/items/hp_potion.png +0 -0
- package/assets/items/locked_item_box.png +0 -0
- package/assets/items/map.png +0 -0
- package/assets/items/resurrection_potion.png +0 -0
- package/assets/items/super_box.png +0 -0
- package/assets/items/trap.png +0 -0
- package/assets/tiles/floor00.jpg +0 -0
- package/assets/tiles/minecraft-tiles.png +0 -0
- package/assets/tiles/wall00.jpg +0 -0
- package/assets/vegetation/ash_color.png +0 -0
- package/assets/vegetation/aspen_color.png +0 -0
- package/assets/vegetation/bark/birch_color_1k.jpg +0 -0
- package/assets/vegetation/bark/birch_normal_1k.jpg +0 -0
- package/assets/vegetation/bark/birch_roughness_1k.jpg +0 -0
- package/assets/vegetation/bark/oak_color_1k.jpg +0 -0
- package/assets/vegetation/bark/oak_normal_1k.jpg +0 -0
- package/assets/vegetation/bark/oak_roughness_1k.jpg +0 -0
- package/assets/vegetation/bark/pine_color_1k.jpg +0 -0
- package/assets/vegetation/bark/pine_normal_1k.jpg +0 -0
- package/assets/vegetation/bark/pine_roughness_1k.jpg +0 -0
- package/assets/vegetation/ground/dirt_color.jpg +0 -0
- package/assets/vegetation/ground/dirt_normal.jpg +0 -0
- package/assets/vegetation/ground/grass.jpg +0 -0
- package/assets/vegetation/oak_color.png +0 -0
- package/assets/vegetation/pine_color.png +0 -0
- package/bin/incanto-assets.mjs +107 -0
- package/bin/incanto-check.mjs +107 -0
- package/bin/incanto-editor.mjs +343 -0
- package/bin/incanto-env.mjs +144 -0
- package/bin/incanto-model.mjs +296 -0
- package/bin/incanto-play.mjs +219 -0
- package/bin/incanto-skills.mjs +71 -0
- package/dist/2d.d.ts +642 -0
- package/dist/2d.js +44 -0
- package/dist/3d.d.ts +1860 -0
- package/dist/3d.js +5 -0
- package/dist/agent8-DzU2fFyH.js +129 -0
- package/dist/audio-player-DqUR3XFs.d.ts +110 -0
- package/dist/behavior-BAQq7HGM.d.ts +851 -0
- package/dist/create-game-BdjpTHrW.js +1725 -0
- package/dist/create-game-CZHROKcT.js +527 -0
- package/dist/debug-draw-CZmOYjL2.js +13 -0
- package/dist/debug.d.ts +66 -0
- package/dist/debug.js +658 -0
- package/dist/duplicate-DP2WPYom.js +22 -0
- package/dist/env.d.ts +430 -0
- package/dist/env.js +3152 -0
- package/dist/errors-BMFaY68Q.d.ts +33 -0
- package/dist/errors-BpWbnbb_.js +13 -0
- package/dist/gameplay-Ccruc3Wd.js +1501 -0
- package/dist/gameplay.d.ts +543 -0
- package/dist/gameplay.js +2 -0
- package/dist/heightmap-CroQPEER.js +185 -0
- package/dist/index.d.ts +305 -0
- package/dist/index.js +62 -0
- package/dist/json-BLk7H2Qa.js +30 -0
- package/dist/loader-CGs_G-r0.js +919 -0
- package/dist/loader-Mo0KghCv.d.ts +41 -0
- package/dist/net.d.ts +427 -0
- package/dist/net.js +772 -0
- package/dist/noise-CGUMx44x.js +82 -0
- package/dist/particle-sim-CbN4YUuH.d.ts +63 -0
- package/dist/particle-sim-DYuSUxvK.js +1319 -0
- package/dist/physics-2d-KuMWPTf6.js +288 -0
- package/dist/physics-3d-Dl67vOLT.js +434 -0
- package/dist/react.d.ts +65 -0
- package/dist/react.js +209 -0
- package/dist/register-BuUV1_KB.js +561 -0
- package/dist/register-CNlYAS1_.js +10634 -0
- package/dist/register-DPEV9_9t.js +851 -0
- package/dist/register-Dasmnurl.js +374 -0
- package/dist/registry-BVJ2HbCn.js +132 -0
- package/dist/rng-DP-SR7eg.js +38 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/schema-CcoWb32N.d.ts +104 -0
- package/dist/test.d.ts +158 -0
- package/dist/test.js +275 -0
- package/dist/touch-031PxtCR.js +208 -0
- package/dist/vite.d.ts +26 -0
- package/dist/vite.js +57 -0
- package/editor/assets/GameServer-C56iOUgF.js +1 -0
- package/editor/assets/agent8-Bp7QFI7v.js +1 -0
- package/editor/assets/index-DF3tMeKJ.css +1 -0
- package/editor/assets/index-Dl2pjA8e.js +7365 -0
- package/editor/assets/rapier-CEuLKeCu.js +1 -0
- package/editor/assets/rapier-DE6a0vmv.js +1 -0
- package/editor/index.html +169 -0
- package/package.json +97 -0
- package/schemas/scene.schema.json +4254 -0
- package/skills/README.md +9 -0
- package/skills/incanto-3d-character.md +229 -0
- package/skills/incanto-3d-models.md +151 -0
- package/skills/incanto-assets.md +118 -0
- package/skills/incanto-audio.md +309 -0
- package/skills/incanto-behaviors-and-scripts.md +169 -0
- package/skills/incanto-building-2d-games.md +242 -0
- package/skills/incanto-building-3d-games.md +245 -0
- package/skills/incanto-editor.md +163 -0
- package/skills/incanto-environment.md +743 -0
- package/skills/incanto-gameplay-behaviors.md +707 -0
- package/skills/incanto-multiplayer.md +264 -0
- package/skills/incanto-node-reference.md +797 -0
- package/skills/incanto-physics-and-input.md +164 -0
- package/skills/incanto-scene-json-authoring.md +325 -0
- package/skills/incanto-verifying-your-game.md +191 -0
- package/skills/incanto-web-integration.md +96 -0
- package/templates/agent8-server.js +84 -0
- package/templates/agent8-server.ts +138 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
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";
|
|
4
|
+
import { n as registerDebugSource } from "./debug-draw-CZmOYjL2.js";
|
|
5
|
+
//#region src/2d/physics/physics-2d.ts
|
|
6
|
+
var physics_2d_exports = /* @__PURE__ */ __exportAll({
|
|
7
|
+
Physics2D: () => Physics2D,
|
|
8
|
+
enablePhysics2D: () => enablePhysics2D
|
|
9
|
+
});
|
|
10
|
+
/** Internal unit scale: Rapier's solver is tuned for meters; we speak pixels. */
|
|
11
|
+
const SCALE = 100;
|
|
12
|
+
const DEG = Math.PI / 180;
|
|
13
|
+
/**
|
|
14
|
+
* Enable 2D physics for an engine. Dynamically imports Rapier (compat build,
|
|
15
|
+
* wasm inlined) so games without physics never pay its bundle cost.
|
|
16
|
+
*/
|
|
17
|
+
async function enablePhysics2D(engine, opts) {
|
|
18
|
+
const R = await import("@dimforge/rapier2d-compat");
|
|
19
|
+
await R.init();
|
|
20
|
+
return new Physics2D(R, engine, opts);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Per-engine 2D physics world (y-down pixel space, consistent with Node2D).
|
|
24
|
+
* Each fixedUpdate: sync tree → step world → write back → drain trigger events.
|
|
25
|
+
*/
|
|
26
|
+
var Physics2D = class {
|
|
27
|
+
R;
|
|
28
|
+
engine;
|
|
29
|
+
/** Render collider outlines in the GAME view (renderers pick this up). */
|
|
30
|
+
debugDraw = false;
|
|
31
|
+
dimension = "2d";
|
|
32
|
+
unregisterDebug;
|
|
33
|
+
warnedNoCollider = /* @__PURE__ */ new WeakSet();
|
|
34
|
+
entries = /* @__PURE__ */ new Map();
|
|
35
|
+
byColliderHandle = /* @__PURE__ */ new Map();
|
|
36
|
+
world;
|
|
37
|
+
events;
|
|
38
|
+
kcc;
|
|
39
|
+
disconnect;
|
|
40
|
+
lastDt = 1 / 60;
|
|
41
|
+
optsGravity;
|
|
42
|
+
lastScene = null;
|
|
43
|
+
constructor(R, engine, opts) {
|
|
44
|
+
this.R = R;
|
|
45
|
+
this.engine = engine;
|
|
46
|
+
this.optsGravity = opts?.gravity;
|
|
47
|
+
this.lastScene = engine.scene;
|
|
48
|
+
const declared = engine.scene?.physics?.gravity;
|
|
49
|
+
const g = opts?.gravity ?? declared ?? [0, 980];
|
|
50
|
+
this.world = new R.World({
|
|
51
|
+
x: (g[0] ?? 0) / SCALE,
|
|
52
|
+
y: (g[1] ?? 0) / SCALE
|
|
53
|
+
});
|
|
54
|
+
this.kcc = this.world.createCharacterController(.01);
|
|
55
|
+
this.kcc.setUp({
|
|
56
|
+
x: 0,
|
|
57
|
+
y: -1
|
|
58
|
+
});
|
|
59
|
+
this.events = new R.EventQueue(true);
|
|
60
|
+
this.unregisterDebug = registerDebugSource(this);
|
|
61
|
+
this.disconnect = engine.fixedUpdated.connect((dt) => this.step(dt));
|
|
62
|
+
this.syncBodies();
|
|
63
|
+
}
|
|
64
|
+
/** @internal Driven by engine.fixedUpdated. */
|
|
65
|
+
step(dt) {
|
|
66
|
+
this.lastDt = dt;
|
|
67
|
+
if (this.engine.scene !== this.lastScene) {
|
|
68
|
+
this.lastScene = this.engine.scene;
|
|
69
|
+
if (!this.optsGravity) {
|
|
70
|
+
const g = this.engine.scene?.physics?.gravity ?? [0, 980];
|
|
71
|
+
this.world.gravity = {
|
|
72
|
+
x: (g[0] ?? 0) / SCALE,
|
|
73
|
+
y: (g[1] ?? 0) / SCALE
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
this.syncBodies();
|
|
78
|
+
this.world.timestep = dt;
|
|
79
|
+
this.world.step(this.events);
|
|
80
|
+
for (const [node, e] of this.entries) {
|
|
81
|
+
const t = e.body.translation();
|
|
82
|
+
if (!e.body.isFixed()) {
|
|
83
|
+
const [ox, oy] = parentWorldOffset2D(node);
|
|
84
|
+
node.position = [t.x * SCALE - ox, t.y * SCALE - oy];
|
|
85
|
+
}
|
|
86
|
+
if (node instanceof RigidBody2D) {
|
|
87
|
+
if (!node.fixedRotation) node.rotation = e.body.rotation() / DEG;
|
|
88
|
+
const lv = e.body.linvel();
|
|
89
|
+
node.linearVelocity = [lv.x * SCALE, lv.y * SCALE];
|
|
90
|
+
e.lastVX = node.linearVelocity[0] ?? 0;
|
|
91
|
+
e.lastVY = node.linearVelocity[1] ?? 0;
|
|
92
|
+
}
|
|
93
|
+
const [wx, wy] = worldPosition2D(node);
|
|
94
|
+
e.lastX = wx;
|
|
95
|
+
e.lastY = wy;
|
|
96
|
+
}
|
|
97
|
+
this.events.drainCollisionEvents((h1, h2, started) => {
|
|
98
|
+
const a = this.byColliderHandle.get(h1);
|
|
99
|
+
const b = this.byColliderHandle.get(h2);
|
|
100
|
+
if (!a || !b) return;
|
|
101
|
+
const sig = started ? "triggerEnter" : "triggerExit";
|
|
102
|
+
a.emit(sig, b);
|
|
103
|
+
b.emit(sig, a);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** @internal Called by CharacterBody2D.moveAndSlide (during tree fixedUpdate). */
|
|
107
|
+
moveAndSlide(node) {
|
|
108
|
+
const e = this.ensureEntry(node);
|
|
109
|
+
const vy = node.velocity[1] ?? 0;
|
|
110
|
+
if (node.snapToGround && vy >= 0) this.kcc.enableSnapToGround(.05);
|
|
111
|
+
else this.kcc.disableSnapToGround();
|
|
112
|
+
this.kcc.setMaxSlopeClimbAngle(node.slopeLimitDeg * DEG);
|
|
113
|
+
const desired = {
|
|
114
|
+
x: (node.velocity[0] ?? 0) * this.lastDt / SCALE,
|
|
115
|
+
y: vy * this.lastDt / SCALE
|
|
116
|
+
};
|
|
117
|
+
this.kcc.computeColliderMovement(e.collider, desired, this.R.QueryFilterFlags.EXCLUDE_SENSORS);
|
|
118
|
+
const corrected = this.kcc.computedMovement();
|
|
119
|
+
const t = e.body.translation();
|
|
120
|
+
e.body.setNextKinematicTranslation({
|
|
121
|
+
x: t.x + corrected.x,
|
|
122
|
+
y: t.y + corrected.y
|
|
123
|
+
});
|
|
124
|
+
node._grounded = this.kcc.computedGrounded();
|
|
125
|
+
}
|
|
126
|
+
/** Rapier debug segments, scaled back to pixels. Null while debugDraw is off. */
|
|
127
|
+
debugLines() {
|
|
128
|
+
if (!this.debugDraw) return null;
|
|
129
|
+
const raw = this.world.debugRender().vertices;
|
|
130
|
+
const px = new Float32Array(raw.length);
|
|
131
|
+
for (let i = 0; i < raw.length; i++) px[i] = raw[i] * SCALE;
|
|
132
|
+
return px;
|
|
133
|
+
}
|
|
134
|
+
dispose() {
|
|
135
|
+
this.disconnect();
|
|
136
|
+
this.unregisterDebug();
|
|
137
|
+
this.world.free();
|
|
138
|
+
}
|
|
139
|
+
syncBodies() {
|
|
140
|
+
const scene = this.engine.scene;
|
|
141
|
+
if (!scene) return;
|
|
142
|
+
const seen = /* @__PURE__ */ new Set();
|
|
143
|
+
collectBodies(scene.root, seen);
|
|
144
|
+
for (const node of seen) {
|
|
145
|
+
if (node.collider.shape === void 0) {
|
|
146
|
+
const stale = this.entries.get(node);
|
|
147
|
+
if (stale) {
|
|
148
|
+
this.byColliderHandle.delete(stale.collider.handle);
|
|
149
|
+
this.world.removeRigidBody(stale.body);
|
|
150
|
+
this.entries.delete(node);
|
|
151
|
+
node._physics = null;
|
|
152
|
+
}
|
|
153
|
+
if (!this.warnedNoCollider.has(node)) {
|
|
154
|
+
this.warnedNoCollider.add(node);
|
|
155
|
+
console.warn(`[incanto] '${node.name}' has no collider — physics skips it.`);
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const existing = this.entries.get(node);
|
|
160
|
+
if (existing && existing.colliderKey !== JSON.stringify(node.collider)) {
|
|
161
|
+
this.byColliderHandle.delete(existing.collider.handle);
|
|
162
|
+
this.world.removeRigidBody(existing.body);
|
|
163
|
+
this.entries.delete(node);
|
|
164
|
+
node._physics = null;
|
|
165
|
+
}
|
|
166
|
+
this.ensureEntry(node);
|
|
167
|
+
}
|
|
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;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
ensureEntry(node) {
|
|
176
|
+
let e = this.entries.get(node);
|
|
177
|
+
const R = this.R;
|
|
178
|
+
if (!e) {
|
|
179
|
+
const [wx, wy] = worldPosition2D(node);
|
|
180
|
+
const x = wx / SCALE;
|
|
181
|
+
const y = wy / SCALE;
|
|
182
|
+
let desc;
|
|
183
|
+
if (node instanceof RigidBody2D) desc = R.RigidBodyDesc.dynamic();
|
|
184
|
+
else if (node instanceof CharacterBody2D || node instanceof Area2D) desc = R.RigidBodyDesc.kinematicPositionBased();
|
|
185
|
+
else desc = R.RigidBodyDesc.fixed();
|
|
186
|
+
desc.setTranslation(x, y).setRotation(node.rotation * DEG);
|
|
187
|
+
if (node instanceof RigidBody2D) {
|
|
188
|
+
desc.setGravityScale(node.gravityScale);
|
|
189
|
+
desc.setLinvel((node.linearVelocity[0] ?? 0) / SCALE, (node.linearVelocity[1] ?? 0) / SCALE);
|
|
190
|
+
if (node.fixedRotation) desc.lockRotations();
|
|
191
|
+
}
|
|
192
|
+
const body = this.world.createRigidBody(desc);
|
|
193
|
+
const colDesc = parseCollider2D(R, node.collider, node.name);
|
|
194
|
+
colDesc.setActiveEvents(R.ActiveEvents.COLLISION_EVENTS);
|
|
195
|
+
colDesc.setActiveCollisionTypes(R.ActiveCollisionTypes.ALL);
|
|
196
|
+
if (node instanceof Area2D) colDesc.setSensor(true);
|
|
197
|
+
if (node instanceof RigidBody2D) colDesc.setMass(node.mass).setFriction(node.friction).setRestitution(node.restitution);
|
|
198
|
+
const collider = this.world.createCollider(colDesc, body);
|
|
199
|
+
e = {
|
|
200
|
+
body,
|
|
201
|
+
collider,
|
|
202
|
+
colliderKey: JSON.stringify(node.collider),
|
|
203
|
+
lastX: wx,
|
|
204
|
+
lastY: wy,
|
|
205
|
+
lastVX: 0,
|
|
206
|
+
lastVY: 0
|
|
207
|
+
};
|
|
208
|
+
this.entries.set(node, e);
|
|
209
|
+
this.byColliderHandle.set(collider.handle, node);
|
|
210
|
+
node._physics = this;
|
|
211
|
+
return e;
|
|
212
|
+
}
|
|
213
|
+
const [px, py] = worldPosition2D(node);
|
|
214
|
+
if (px !== e.lastX || py !== e.lastY) {
|
|
215
|
+
e.body.setTranslation({
|
|
216
|
+
x: px / SCALE,
|
|
217
|
+
y: py / SCALE
|
|
218
|
+
}, true);
|
|
219
|
+
e.lastX = px;
|
|
220
|
+
e.lastY = py;
|
|
221
|
+
}
|
|
222
|
+
if (node instanceof RigidBody2D) {
|
|
223
|
+
const vx = node.linearVelocity[0] ?? 0;
|
|
224
|
+
const vy = node.linearVelocity[1] ?? 0;
|
|
225
|
+
if (vx !== e.lastVX || vy !== e.lastVY) {
|
|
226
|
+
e.body.setLinvel({
|
|
227
|
+
x: vx / SCALE,
|
|
228
|
+
y: vy / SCALE
|
|
229
|
+
}, true);
|
|
230
|
+
e.lastVX = vx;
|
|
231
|
+
e.lastVY = vy;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return e;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
function collectBodies(node, out) {
|
|
238
|
+
if (node instanceof PhysicsBody2D) out.add(node);
|
|
239
|
+
for (const c of node.children) collectBodies(c, out);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Composed world position (translation only — ancestor rotation/scale are NOT
|
|
243
|
+
* supported for physics bodies; keep body ancestors untransformed or offset-only).
|
|
244
|
+
*/
|
|
245
|
+
function worldPosition2D(node) {
|
|
246
|
+
let x = 0;
|
|
247
|
+
let y = 0;
|
|
248
|
+
for (let n = node; n; n = n.parent) if (n instanceof Node2D) {
|
|
249
|
+
x += n.position[0] ?? 0;
|
|
250
|
+
y += n.position[1] ?? 0;
|
|
251
|
+
}
|
|
252
|
+
return [x, y];
|
|
253
|
+
}
|
|
254
|
+
function parentWorldOffset2D(node) {
|
|
255
|
+
let x = 0;
|
|
256
|
+
let y = 0;
|
|
257
|
+
for (let n = node.parent; n; n = n.parent) if (n instanceof Node2D) {
|
|
258
|
+
x += n.position[0] ?? 0;
|
|
259
|
+
y += n.position[1] ?? 0;
|
|
260
|
+
}
|
|
261
|
+
return [x, y];
|
|
262
|
+
}
|
|
263
|
+
function parseCollider2D(R, collider, nodeName) {
|
|
264
|
+
validateCollider2D(collider, nodeName);
|
|
265
|
+
const desc = buildColliderDesc(R, collider, nodeName);
|
|
266
|
+
const offset = collider.offset;
|
|
267
|
+
if (Array.isArray(offset)) desc.setTranslation((offset[0] ?? 0) / SCALE, (offset[1] ?? 0) / SCALE);
|
|
268
|
+
return desc;
|
|
269
|
+
}
|
|
270
|
+
function buildColliderDesc(R, collider, nodeName) {
|
|
271
|
+
const shape = collider.shape;
|
|
272
|
+
if (shape === "rect") {
|
|
273
|
+
const size = collider.size;
|
|
274
|
+
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].`);
|
|
275
|
+
return R.ColliderDesc.cuboid(size[0] / 2 / SCALE, size[1] / 2 / SCALE);
|
|
276
|
+
}
|
|
277
|
+
if (shape === "circle") {
|
|
278
|
+
if (typeof collider.radius !== "number") throw new IncantoError("BAD_FORMAT", `Collider on '${nodeName}': circle needs "radius".`);
|
|
279
|
+
return R.ColliderDesc.ball(collider.radius / SCALE);
|
|
280
|
+
}
|
|
281
|
+
if (shape === "capsule") {
|
|
282
|
+
if (typeof collider.radius !== "number" || typeof collider.height !== "number") throw new IncantoError("BAD_FORMAT", `Collider on '${nodeName}': capsule needs "radius" and "height".`);
|
|
283
|
+
return R.ColliderDesc.capsule(collider.height / 2 / SCALE, collider.radius / SCALE);
|
|
284
|
+
}
|
|
285
|
+
throw new IncantoError("BAD_FORMAT", `Collider on '${nodeName}': "shape" must be 'rect', 'circle', or 'capsule', got ${JSON.stringify(shape)}.`);
|
|
286
|
+
}
|
|
287
|
+
//#endregion
|
|
288
|
+
export { enablePhysics2D as n, physics_2d_exports as r, Physics2D as t };
|