castle-web-cli 0.4.129 → 0.4.131

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 (144) hide show
  1. package/dist/agent-prompts.js +4 -4
  2. package/dist/filesChanged.d.ts +2 -0
  3. package/dist/filesChanged.js +6 -2
  4. package/dist/ide.js +15 -4
  5. package/dist/importBrowse.js +1 -0
  6. package/dist/index.js +10 -4
  7. package/dist/init.js +63 -15
  8. package/dist/native/loop.js +1 -1
  9. package/dist/native/tools.js +1 -1
  10. package/dist/serve.d.ts +1 -0
  11. package/dist/serve.js +20 -1
  12. package/dist/shell/assets/index-BpOLUyVO.js +441 -0
  13. package/dist/shell/assets/{index-CAq6f9B5.css → index-D6K-0YDB.css} +1 -1
  14. package/dist/shell/index.html +2 -2
  15. package/kits/physics-2d/CLAUDE.md +9 -10
  16. package/kits/physics-2d/castle.json +1 -1
  17. package/kits/physics-2d/editors/PlayOnly.jsx +16 -6
  18. package/kits/physics-2d/editors/PxArtEditor.jsx +95 -31
  19. package/kits/physics-2d/editors/SceneEditor.jsx +10 -10
  20. package/kits/physics-2d/editors/SingleEditor.jsx +37 -8
  21. package/kits/physics-2d/editors/StyleEditor.jsx +7 -4
  22. package/kits/physics-2d/editors/pixelEditorChrome.jsx +7 -7
  23. package/kits/physics-2d/editors/pixelInspector.jsx +13 -7
  24. package/kits/physics-2d/editors/pxArtTimeline.jsx +29 -11
  25. package/kits/physics-2d/editors/pxArtTimeline.module.css +11 -0
  26. package/kits/physics-2d/engine/ScenePlayer.jsx +2 -2
  27. package/kits/physics-2d/engine/blueprint.js +23 -5
  28. package/kits/physics-2d/engine/liveReload.js +33 -21
  29. package/kits/physics-2d/engine/scene.js +17 -5
  30. package/kits/physics-2d/engine/systemRegistry.js +1 -1
  31. package/kits/physics-2d/engine/ui.jsx +2 -2
  32. package/kits/physics-2d/engine/ui.module.css +42 -0
  33. package/kits/physics-3d/CLAUDE.md +108 -0
  34. package/kits/physics-3d/behaviors/Body.jsx +51 -0
  35. package/kits/physics-3d/behaviors/Door.jsx +31 -0
  36. package/kits/physics-3d/behaviors/Lookable.jsx +11 -0
  37. package/kits/physics-3d/behaviors/Model.jsx +14 -0
  38. package/kits/physics-3d/behaviors/Pickup.jsx +39 -0
  39. package/kits/physics-3d/behaviors/Player.jsx +172 -0
  40. package/kits/physics-3d/behaviors/Shape.jsx +75 -0
  41. package/kits/physics-3d/behaviors/Solid.jsx +11 -0
  42. package/kits/physics-3d/behaviors/Transform.jsx +24 -0
  43. package/kits/physics-3d/blueprints/barrel.scene +15 -0
  44. package/kits/physics-3d/blueprints/crate.scene +29 -0
  45. package/kits/physics-3d/blueprints/door.scene +35 -0
  46. package/kits/physics-3d/blueprints/gem.scene +26 -0
  47. package/kits/physics-3d/blueprints/pillar.scene +25 -0
  48. package/kits/physics-3d/blueprints/platform.scene +23 -0
  49. package/kits/physics-3d/blueprints/player.scene +29 -0
  50. package/kits/physics-3d/blueprints/rock.scene +14 -0
  51. package/kits/physics-3d/blueprints/statue.scene +26 -0
  52. package/kits/physics-3d/blueprints/tree.scene +14 -0
  53. package/kits/physics-3d/blueprints/wall.scene +26 -0
  54. package/kits/{basic-2d → physics-3d}/castle.json +23 -5
  55. package/kits/physics-3d/docs/pxmodel-format.md +111 -0
  56. package/kits/physics-3d/drawings/crate.pxart +26 -0
  57. package/kits/physics-3d/drawings/door-edge.pxart +26 -0
  58. package/kits/physics-3d/drawings/door.pxart +27 -0
  59. package/kits/physics-3d/drawings/face.pxart +26 -0
  60. package/kits/physics-3d/drawings/floor.pxart +27 -0
  61. package/kits/physics-3d/drawings/platform.pxart +27 -0
  62. package/kits/physics-3d/drawings/statue.pxart +27 -0
  63. package/kits/physics-3d/drawings/wall.pxart +27 -0
  64. package/kits/physics-3d/engine3d/PlayOnly3D.jsx +36 -0
  65. package/kits/physics-3d/engine3d/PxModelEditor.jsx +619 -0
  66. package/kits/physics-3d/engine3d/Scene3DEditor.jsx +618 -0
  67. package/kits/physics-3d/engine3d/Scene3DPlayer.jsx +309 -0
  68. package/kits/physics-3d/engine3d/editor3dData.js +114 -0
  69. package/kits/physics-3d/engine3d/editor3dInspector.jsx +404 -0
  70. package/kits/physics-3d/engine3d/editorChrome.jsx +218 -0
  71. package/kits/physics-3d/engine3d/editorWorld.js +249 -0
  72. package/kits/physics-3d/engine3d/materials.js +174 -0
  73. package/kits/physics-3d/engine3d/meshops.js +123 -0
  74. package/kits/physics-3d/engine3d/modelEditorWorld.js +439 -0
  75. package/kits/physics-3d/engine3d/modelFiles.js +136 -0
  76. package/kits/physics-3d/engine3d/pxmodel.js +265 -0
  77. package/kits/physics-3d/engine3d/thumbnails.js +137 -0
  78. package/kits/physics-3d/engine3d/world3d.js +216 -0
  79. package/kits/{basic-2d → physics-3d}/eslint.config.js +10 -25
  80. package/kits/physics-3d/index.html +20 -0
  81. package/kits/physics-3d/main.jsx +30 -0
  82. package/kits/physics-3d/models/barrel.pxmodel +10 -0
  83. package/kits/physics-3d/models/player.pxmodel +222 -0
  84. package/kits/physics-3d/models/rock.pxmodel +30 -0
  85. package/kits/physics-3d/models/tree.pxmodel +10 -0
  86. package/kits/{basic-2d → physics-3d}/package-lock.json +140 -119
  87. package/kits/{basic-2d → physics-3d}/package.json +11 -9
  88. package/kits/{basic-2d → physics-3d}/pnpm-lock.yaml +162 -137
  89. package/kits/physics-3d/scenes/main.scene +398 -0
  90. package/kits/physics-3d/scenes/model-lab.scene +111 -0
  91. package/kits/physics-3d/systems/physics3d.js +356 -0
  92. package/package.json +5 -2
  93. package/dist/shell/assets/index-DKu9ejyh.js +0 -436
  94. package/kits/basic-2d/CLAUDE.md +0 -221
  95. package/kits/basic-2d/behaviors/Camera.jsx +0 -43
  96. package/kits/basic-2d/behaviors/Collider.jsx +0 -213
  97. package/kits/basic-2d/behaviors/Layout.jsx +0 -53
  98. package/kits/basic-2d/behaviors/Sprite.jsx +0 -357
  99. package/kits/basic-2d/behaviors/tint.js +0 -47
  100. package/kits/basic-2d/blueprints/cauldron.scene +0 -20
  101. package/kits/basic-2d/docs/pxart-format.md +0 -377
  102. package/kits/basic-2d/drawings/cauldron.pxart +0 -113
  103. package/kits/basic-2d/editors/BlueprintLibrary.jsx +0 -270
  104. package/kits/basic-2d/editors/ErrorBoundary.jsx +0 -59
  105. package/kits/basic-2d/editors/PlayOnly.jsx +0 -31
  106. package/kits/basic-2d/editors/PxArtEditor.jsx +0 -1092
  107. package/kits/basic-2d/editors/SceneEditor.jsx +0 -1780
  108. package/kits/basic-2d/editors/SelectionOverlay.jsx +0 -909
  109. package/kits/basic-2d/editors/SingleEditor.jsx +0 -122
  110. package/kits/basic-2d/editors/behaviorRegistry.js +0 -34
  111. package/kits/basic-2d/editors/editorHistory.js +0 -157
  112. package/kits/basic-2d/editors/inspectorSheet.js +0 -13
  113. package/kits/basic-2d/editors/pixelCanvas.js +0 -11
  114. package/kits/basic-2d/editors/pixelEditorChrome.jsx +0 -74
  115. package/kits/basic-2d/editors/pixelGeometry.js +0 -140
  116. package/kits/basic-2d/editors/pixelInspector.jsx +0 -633
  117. package/kits/basic-2d/editors/pxArtEditorModel.js +0 -732
  118. package/kits/basic-2d/editors/pxArtPlayback.js +0 -92
  119. package/kits/basic-2d/editors/pxArtTimeline.jsx +0 -752
  120. package/kits/basic-2d/editors/pxArtTimeline.module.css +0 -506
  121. package/kits/basic-2d/editors/pxArtTools.js +0 -232
  122. package/kits/basic-2d/editors/useArtboardFit.js +0 -105
  123. package/kits/basic-2d/engine/ScenePlayer.jsx +0 -209
  124. package/kits/basic-2d/engine/SceneUI.jsx +0 -59
  125. package/kits/basic-2d/engine/assets.js +0 -15
  126. package/kits/basic-2d/engine/autoInspector.jsx +0 -70
  127. package/kits/basic-2d/engine/behaviorExtensions.js +0 -32
  128. package/kits/basic-2d/engine/blueprint.js +0 -557
  129. package/kits/basic-2d/engine/collider.js +0 -200
  130. package/kits/basic-2d/engine/files.js +0 -141
  131. package/kits/basic-2d/engine/liveReload.js +0 -88
  132. package/kits/basic-2d/engine/pxart.js +0 -1032
  133. package/kits/basic-2d/engine/pxartSmooth.js +0 -222
  134. package/kits/basic-2d/engine/scene.js +0 -696
  135. package/kits/basic-2d/engine/spriteGeometry.js +0 -32
  136. package/kits/basic-2d/engine/systemRegistry.js +0 -16
  137. package/kits/basic-2d/engine/ui.jsx +0 -695
  138. package/kits/basic-2d/engine/ui.module.css +0 -2287
  139. package/kits/basic-2d/index.html +0 -24
  140. package/kits/basic-2d/main.jsx +0 -24
  141. package/kits/basic-2d/scenes/main.scene +0 -16
  142. package/kits/basic-2d/scripts/draw.mjs +0 -121
  143. /package/kits/{basic-2d → physics-3d}/.prettierrc +0 -0
  144. /package/kits/{basic-2d → physics-3d}/vite.config.js +0 -0
@@ -0,0 +1,356 @@
1
+ // The 3d physics system: one rapier world per scene runtime, registered as a
2
+ // runtime system (auto-discovered from `systems/*.js` by the imported engine's
3
+ // systemRegistry). Every actor with a Transform gets a rapier body -- fixed by
4
+ // default, dynamic or kinematic via a `Body` behavior. Colliders come from the
5
+ // actor's `Shape` and are sensors unless the actor is solid (a `Solid`
6
+ // behavior, or `Shape.solid`), so pickups and trigger zones still get overlap
7
+ // events. Behaviors get `onCollisionEnter` / `onCollisionExit` callbacks and
8
+ // drive motion through `scene.physics3d`.
9
+ //
10
+ // rapier's wasm loads async; the system installs immediately and starts
11
+ // simulating on the first frame after init resolves.
12
+ import * as THREE from 'three';
13
+ import * as RAPIER from '@dimforge/rapier3d-compat';
14
+
15
+ const FIXED_STEP = 1 / 60;
16
+ const MAX_STEPS_PER_FRAME = 4;
17
+ const DEG = Math.PI / 180;
18
+
19
+ let rapierReady = false;
20
+ let rapierInitPromise = null;
21
+
22
+ function ensureRapierInit() {
23
+ if (!rapierInitPromise) {
24
+ rapierInitPromise = RAPIER.init().then(() => {
25
+ rapierReady = true;
26
+ });
27
+ }
28
+ return rapierInitPromise;
29
+ }
30
+
31
+ export function installSystem(runtime) {
32
+ const system = new Physics3DSystem();
33
+ runtime.registerSystem(system);
34
+ system.attach(runtime);
35
+ return system;
36
+ }
37
+
38
+ export class Physics3DSystem {
39
+ constructor() {
40
+ this.world = null;
41
+ this.eventQueue = null;
42
+ this.bodies = new Map(); // actorId -> { body, collider, sig, pushedKey }
43
+ this.byCollider = new Map(); // collider handle -> actorId
44
+ this.acc = 0;
45
+ this.touching = new Set(); // "aId|bId" sorted pair keys
46
+ this.enter = [];
47
+ this.exit = [];
48
+ }
49
+
50
+ attach(runtime) {
51
+ runtime.physics3d = makeApi(this);
52
+ void ensureRapierInit();
53
+ }
54
+
55
+ reset() {
56
+ // Rapier objects live in wasm -- dropping the JS reference leaks them, so
57
+ // both the world and its event queue are freed explicitly. Runs on every
58
+ // scene load (the runtime resets registered systems) and on dispose.
59
+ if (this.world) this.world.free();
60
+ if (this.eventQueue) this.eventQueue.free();
61
+ this.world = null;
62
+ this.eventQueue = null;
63
+ this.bodies.clear();
64
+ this.byCollider.clear();
65
+ this.touching.clear();
66
+ this.enter.length = 0;
67
+ this.exit.length = 0;
68
+ this.acc = 0;
69
+ }
70
+
71
+ dispose() {
72
+ this.reset();
73
+ }
74
+
75
+ afterBehaviors(scene, dt) {
76
+ if (!rapierReady) return;
77
+ this.ensureWorld();
78
+ this.reconcileBodies(scene);
79
+ this.pushTransforms(scene);
80
+ this.step(dt);
81
+ this.writeBack(scene);
82
+ this.dispatchCallbacks(scene);
83
+ }
84
+
85
+ ensureWorld() {
86
+ if (this.world) return;
87
+ this.world = new RAPIER.World({ x: 0, y: -9.81, z: 0 });
88
+ this.eventQueue = new RAPIER.EventQueue(true);
89
+ // The ground everything stands on, matching the visual ground plane.
90
+ const groundBody = this.world.createRigidBody(
91
+ RAPIER.RigidBodyDesc.fixed().setTranslation(0, -0.5, 0)
92
+ );
93
+ this.world.createCollider(RAPIER.ColliderDesc.cuboid(100, 0.5, 100), groundBody);
94
+ }
95
+
96
+ reconcileBodies(scene) {
97
+ const seen = new Set();
98
+ for (const actor of scene.getActors()) {
99
+ if (!actor.components.Transform) continue;
100
+ seen.add(actor.id);
101
+ const sig = bodySignature(actor);
102
+ const existing = this.bodies.get(actor.id);
103
+ if (existing && existing.sig === sig) continue;
104
+ if (existing) this.removeBody(actor.id);
105
+ this.createBody(actor, sig);
106
+ }
107
+ for (const id of [...this.bodies.keys()]) {
108
+ if (!seen.has(id)) this.removeBody(id);
109
+ }
110
+ }
111
+
112
+ createBody(actor, sig) {
113
+ const t = actor.components.Transform;
114
+ const body = this.world.createRigidBody(bodyDescFor(actor, t));
115
+ body.userData = actor.id;
116
+ let collider = null;
117
+ const shape = actor.components.Shape;
118
+ if (shape) {
119
+ collider = this.world.createCollider(colliderDescFor(actor, shape), body);
120
+ this.byCollider.set(collider.handle, actor.id);
121
+ }
122
+ this.bodies.set(actor.id, { body, collider, sig, pushedKey: transformKey(t) });
123
+ }
124
+
125
+ removeBody(actorId) {
126
+ const entry = this.bodies.get(actorId);
127
+ if (!entry) return;
128
+ if (entry.collider) this.byCollider.delete(entry.collider.handle);
129
+ this.world.removeRigidBody(entry.body);
130
+ this.bodies.delete(actorId);
131
+ for (const key of [...this.touching]) {
132
+ if (key.startsWith(`${actorId}|`) || key.endsWith(`|${actorId}`)) this.touching.delete(key);
133
+ }
134
+ }
135
+
136
+ // Fixed bodies follow Transform edits (teleport); kinematic bodies follow
137
+ // with contact resolution. Dynamic bodies are the sim's to move.
138
+ pushTransforms(scene) {
139
+ for (const actor of scene.getActors()) {
140
+ const entry = this.bodies.get(actor.id);
141
+ const t = actor.components.Transform;
142
+ if (!entry || !t) continue;
143
+ const type = entry.body.bodyType();
144
+ if (type === RAPIER.RigidBodyType.KinematicPositionBased) {
145
+ entry.body.setNextKinematicTranslation({ x: t.x, y: t.y, z: t.z });
146
+ entry.body.setNextKinematicRotation(quatFromEuler(t));
147
+ } else if (type === RAPIER.RigidBodyType.Fixed) {
148
+ const key = transformKey(t);
149
+ if (key !== entry.pushedKey) {
150
+ entry.body.setTranslation({ x: t.x, y: t.y, z: t.z }, false);
151
+ entry.body.setRotation(quatFromEuler(t), false);
152
+ entry.pushedKey = key;
153
+ }
154
+ }
155
+ }
156
+ }
157
+
158
+ step(dt) {
159
+ this.enter.length = 0;
160
+ this.exit.length = 0;
161
+ this.acc += dt;
162
+ let steps = 0;
163
+ while (this.acc >= FIXED_STEP && steps < MAX_STEPS_PER_FRAME) {
164
+ // Snapshot dynamic bodies before each step so rendering can interpolate
165
+ // between the last two physics states -- without this, bodies advance
166
+ // only on step frames and stutter on displays faster than 60Hz.
167
+ for (const entry of this.bodies.values()) {
168
+ if (entry.body.bodyType() !== RAPIER.RigidBodyType.Dynamic) continue;
169
+ const p = entry.body.translation();
170
+ const q = entry.body.rotation();
171
+ entry.prev = { x: p.x, y: p.y, z: p.z, qx: q.x, qy: q.y, qz: q.z, qw: q.w };
172
+ }
173
+ this.world.timestep = FIXED_STEP;
174
+ this.world.step(this.eventQueue);
175
+ this.eventQueue.drainCollisionEvents((h1, h2, started) => this.recordPair(h1, h2, started));
176
+ this.acc -= FIXED_STEP;
177
+ steps += 1;
178
+ }
179
+ if (steps === MAX_STEPS_PER_FRAME) this.acc = 0;
180
+ }
181
+
182
+ recordPair(handle1, handle2, started) {
183
+ const a = this.byCollider.get(handle1);
184
+ const b = this.byCollider.get(handle2);
185
+ if (!a || !b) return; // the ground, or an already-removed collider
186
+ const key = a < b ? `${a}|${b}` : `${b}|${a}`;
187
+ if (started && !this.touching.has(key)) {
188
+ this.touching.add(key);
189
+ this.enter.push([a, b]);
190
+ } else if (!started && this.touching.has(key)) {
191
+ this.touching.delete(key);
192
+ this.exit.push([a, b]);
193
+ }
194
+ }
195
+
196
+ writeBack(scene) {
197
+ // Blend factor between the last two physics states: how far into the next
198
+ // fixed step this render frame sits.
199
+ const alpha = Math.max(0, Math.min(1, this.acc / FIXED_STEP));
200
+ for (const actor of scene.getActors()) {
201
+ const entry = this.bodies.get(actor.id);
202
+ if (!entry || entry.body.bodyType() !== RAPIER.RigidBodyType.Dynamic) continue;
203
+ const t = actor.components.Transform;
204
+ const p = entry.body.translation();
205
+ const prev = entry.prev;
206
+ if (prev) {
207
+ t.x = prev.x + (p.x - prev.x) * alpha;
208
+ t.y = prev.y + (p.y - prev.y) * alpha;
209
+ t.z = prev.z + (p.z - prev.z) * alpha;
210
+ } else {
211
+ t.x = p.x;
212
+ t.y = p.y;
213
+ t.z = p.z;
214
+ }
215
+ // Locked-rotation bodies (characters) keep Transform rotation under
216
+ // behavior control so they can face where they're going.
217
+ if (!actor.components.Body?.lockRotations) {
218
+ const q = entry.body.rotation();
219
+ if (prev) {
220
+ tmpQuat.set(prev.qx, prev.qy, prev.qz, prev.qw);
221
+ tmpQuat2.set(q.x, q.y, q.z, q.w);
222
+ tmpQuat.slerp(tmpQuat2, alpha);
223
+ actor.runtime.quat = { x: tmpQuat.x, y: tmpQuat.y, z: tmpQuat.z, w: tmpQuat.w };
224
+ setEulerFromQuat(t, actor.runtime.quat);
225
+ } else {
226
+ actor.runtime.quat = { x: q.x, y: q.y, z: q.z, w: q.w };
227
+ setEulerFromQuat(t, q);
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ dispatchCallbacks(scene) {
234
+ for (const [a, b] of this.enter) emitPair(scene, a, b, 'onCollisionEnter');
235
+ for (const [a, b] of this.exit) emitPair(scene, a, b, 'onCollisionExit');
236
+ }
237
+ }
238
+
239
+ function emitPair(scene, aId, bId, method) {
240
+ const a = scene.getActor(aId);
241
+ const b = scene.getActor(bId);
242
+ if (!a || !b) return;
243
+ scene.forEachBehavior(a, (instance) => instance[method]?.(a, scene, b));
244
+ scene.forEachBehavior(b, (instance) => instance[method]?.(b, scene, a));
245
+ }
246
+
247
+ function isSolid(actor) {
248
+ return Boolean(actor.components.Solid) || Boolean(actor.components.Shape?.solid);
249
+ }
250
+
251
+ function bodySignature(actor) {
252
+ return JSON.stringify([
253
+ actor.components.Body ?? null,
254
+ actor.components.Shape ?? null,
255
+ isSolid(actor),
256
+ ]);
257
+ }
258
+
259
+ function transformKey(t) {
260
+ return `${t.x},${t.y},${t.z},${t.rotationX},${t.rotationY},${t.rotationZ}`;
261
+ }
262
+
263
+ function bodyDescFor(actor, t) {
264
+ const opts = actor.components.Body;
265
+ let desc;
266
+ if (opts?.type === 'dynamic') desc = RAPIER.RigidBodyDesc.dynamic();
267
+ else if (opts?.type === 'kinematic') desc = RAPIER.RigidBodyDesc.kinematicPositionBased();
268
+ else desc = RAPIER.RigidBodyDesc.fixed();
269
+ desc.setTranslation(t.x, t.y, t.z);
270
+ desc.setRotation(quatFromEuler(t));
271
+ if (opts) {
272
+ desc.setGravityScale(opts.gravityScale);
273
+ if (opts.lockRotations) desc.lockRotations();
274
+ }
275
+ return desc;
276
+ }
277
+
278
+ function colliderDescFor(actor, shape) {
279
+ let desc;
280
+ if (shape.kind === 'sphere') desc = RAPIER.ColliderDesc.ball(shape.radius);
281
+ else if (shape.kind === 'capsule') {
282
+ desc = RAPIER.ColliderDesc.capsule(
283
+ Math.max(0.01, shape.height / 2 - shape.radius),
284
+ shape.radius
285
+ );
286
+ } else if (shape.kind === 'cylinder') {
287
+ desc = RAPIER.ColliderDesc.cylinder(shape.height / 2, shape.radius);
288
+ } else {
289
+ desc = RAPIER.ColliderDesc.cuboid(shape.width / 2, shape.height / 2, shape.depth / 2);
290
+ }
291
+ const opts = actor.components.Body;
292
+ desc.setFriction(opts?.friction ?? 0.6);
293
+ desc.setRestitution(opts?.restitution ?? 0);
294
+ if (!isSolid(actor)) desc.setSensor(true);
295
+ desc.setActiveEvents(RAPIER.ActiveEvents.COLLISION_EVENTS);
296
+ return desc;
297
+ }
298
+
299
+ const tmpEuler = new THREE.Euler();
300
+ const tmpQuat = new THREE.Quaternion();
301
+ const tmpQuat2 = new THREE.Quaternion();
302
+
303
+ function quatFromEuler(t) {
304
+ tmpEuler.set(t.rotationX * DEG, t.rotationY * DEG, t.rotationZ * DEG);
305
+ tmpQuat.setFromEuler(tmpEuler);
306
+ return { x: tmpQuat.x, y: tmpQuat.y, z: tmpQuat.z, w: tmpQuat.w };
307
+ }
308
+
309
+ function setEulerFromQuat(t, q) {
310
+ tmpQuat.set(q.x, q.y, q.z, q.w);
311
+ tmpEuler.setFromQuaternion(tmpQuat);
312
+ t.rotationX = tmpEuler.x / DEG;
313
+ t.rotationY = tmpEuler.y / DEG;
314
+ t.rotationZ = tmpEuler.z / DEG;
315
+ }
316
+
317
+ // The author-facing API behaviors reach through `scene.physics3d`. Motion goes
318
+ // through the body (never by writing a dynamic actor's Transform); rays
319
+ // exclude sensors so ground checks don't land on pickups.
320
+ function makeApi(system) {
321
+ const bodyOf = (actorOrId) => {
322
+ const id = typeof actorOrId === 'string' ? actorOrId : actorOrId?.id;
323
+ return system.bodies.get(id)?.body ?? null;
324
+ };
325
+ return {
326
+ ready: () => rapierReady && Boolean(system.world),
327
+ debugRender: () => (system.world ? system.world.debugRender() : null),
328
+ bodyOf,
329
+ mass: (actor) => bodyOf(actor)?.mass() ?? 0,
330
+ getVelocity: (actor) => {
331
+ const v = bodyOf(actor)?.linvel();
332
+ return v ? { x: v.x, y: v.y, z: v.z } : { x: 0, y: 0, z: 0 };
333
+ },
334
+ setVelocity: (actor, v) => bodyOf(actor)?.setLinvel(v, true),
335
+ applyImpulse: (actor, v) => bodyOf(actor)?.applyImpulse(v, true),
336
+ castRay: (origin, dir, maxToi, excludeActor) => {
337
+ if (!system.world) return null;
338
+ const ray = new RAPIER.Ray(origin, dir);
339
+ const exclude = excludeActor ? bodyOf(excludeActor) : undefined;
340
+ const hit = system.world.castRay(
341
+ ray,
342
+ maxToi,
343
+ true,
344
+ RAPIER.QueryFilterFlags.EXCLUDE_SENSORS,
345
+ undefined,
346
+ undefined,
347
+ exclude ?? undefined
348
+ );
349
+ if (!hit) return null;
350
+ return {
351
+ distance: hit.timeOfImpact,
352
+ actorId: system.byCollider.get(hit.collider.handle) ?? null,
353
+ };
354
+ },
355
+ };
356
+ }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "castle-web-cli",
3
- "version": "0.4.129",
3
+ "version": "0.4.131",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "castle-web": "./dist/index.js"
7
7
  },
8
8
  "scripts": {
9
- "build": "rm -rf dist && tsc && chmod +x dist/index.js && vite build && cp -R src/castle-host dist/castle-host && rm -rf kits && cp -R ../kits kits",
9
+ "build": "rm -rf dist && tsc && chmod +x dist/index.js && vite build && cp -R src/castle-host dist/castle-host && chmod -R u+w kits 2>/dev/null; rm -rf kits && cp -R ../kits kits && chmod -R u+w kits && rm -rf kits/*/imports",
10
10
  "dev": "tsc --watch",
11
11
  "check": "eslint . && jscpd && tsc --noEmit && tsc --noEmit -p src/shell/tsconfig.json"
12
12
  },
@@ -24,7 +24,10 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@codemirror/commands": "^6.10.4",
27
+ "@codemirror/lang-css": "^6.3.1",
28
+ "@codemirror/lang-html": "^6.4.12",
27
29
  "@codemirror/lang-javascript": "^6.2.5",
30
+ "@codemirror/lang-markdown": "^6.5.2",
28
31
  "@codemirror/language": "^6.12.3",
29
32
  "@codemirror/state": "^6.6.0",
30
33
  "@codemirror/view": "^6.41.1",