@jgengine/shell 0.10.0 → 0.11.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 (234) hide show
  1. package/CHANGELOG.md +13 -2
  2. package/LICENSE +203 -661
  3. package/README.md +1 -1
  4. package/dist/GamePhaseStamp.d.ts +1 -0
  5. package/dist/GamePhaseStamp.js +9 -0
  6. package/dist/GamePlayerShell.d.ts +0 -12
  7. package/dist/GamePlayerShell.js +88 -1043
  8. package/dist/GameUiPreview.js +0 -2
  9. package/dist/audio/audioEngine.js +30 -8
  10. package/dist/audio/audioWire.d.ts +22 -0
  11. package/dist/audio/audioWire.js +19 -0
  12. package/dist/behaviour.d.ts +2 -1
  13. package/dist/behaviour.js +2 -1
  14. package/dist/behaviourAttach.d.ts +1 -0
  15. package/dist/behaviourAttach.js +1 -0
  16. package/dist/behaviourDriver.d.ts +1 -0
  17. package/dist/behaviourDriver.js +1 -0
  18. package/dist/boundActionDispatch.d.ts +13 -0
  19. package/dist/boundActionDispatch.js +37 -0
  20. package/dist/camera/GameCameraRig.d.ts +5 -2
  21. package/dist/camera/GameCameraRig.js +2 -2
  22. package/dist/camera/GameFirstPersonCamera.d.ts +9 -2
  23. package/dist/camera/GameFirstPersonCamera.js +8 -3
  24. package/dist/camera/GameInspectionCamera.d.ts +1 -1
  25. package/dist/camera/GameInspectionCamera.js +2 -2
  26. package/dist/camera/GameOrbitCamera.js +24 -9
  27. package/dist/camera/cameraBlendMath.d.ts +3 -0
  28. package/dist/camera/cameraBlendMath.js +3 -0
  29. package/dist/camera/fovPreference.d.ts +6 -1
  30. package/dist/camera/fovPreference.js +6 -1
  31. package/dist/camera/index.d.ts +4 -7
  32. package/dist/camera/index.js +4 -7
  33. package/dist/camera/inspectionCameraMath.d.ts +7 -2
  34. package/dist/camera/inspectionCameraMath.js +7 -2
  35. package/dist/camera/orbitCameraMath.d.ts +36 -6
  36. package/dist/camera/orbitCameraMath.js +56 -6
  37. package/dist/camera/rigMath.d.ts +94 -24
  38. package/dist/camera/rigMath.js +100 -24
  39. package/dist/camera/rigResolve.d.ts +4 -2
  40. package/dist/camera/rigResolve.js +4 -2
  41. package/dist/camera/shakeChannel.d.ts +6 -2
  42. package/dist/camera/shakeChannel.js +6 -2
  43. package/dist/camera/shakeChannelMath.d.ts +1 -0
  44. package/dist/camera/shakeChannelMath.js +1 -0
  45. package/dist/cartridge/runtime.d.ts +15 -0
  46. package/dist/cartridge/runtime.js +556 -0
  47. package/dist/cartridge/spec.d.ts +188 -0
  48. package/dist/cartridge/spec.js +19 -0
  49. package/dist/cartridge/validate.d.ts +2 -0
  50. package/dist/cartridge/validate.js +78 -0
  51. package/dist/cartridge.d.ts +1 -1
  52. package/dist/cartridge.js +3 -3
  53. package/dist/commandSink.d.ts +8 -3
  54. package/dist/commandSink.js +8 -3
  55. package/dist/defineGame.js +25 -6
  56. package/dist/devtools/ColPanel.d.ts +1 -0
  57. package/dist/devtools/ColPanel.js +14 -0
  58. package/dist/devtools/DevtoolsOverlay.d.ts +7 -3
  59. package/dist/devtools/DevtoolsOverlay.js +19 -450
  60. package/dist/devtools/KeysPanel.d.ts +4 -0
  61. package/dist/devtools/KeysPanel.js +31 -0
  62. package/dist/devtools/LogsPanel.d.ts +1 -0
  63. package/dist/devtools/LogsPanel.js +12 -0
  64. package/dist/devtools/NetPanel.d.ts +4 -0
  65. package/dist/devtools/NetPanel.js +10 -0
  66. package/dist/devtools/PerfPanel.d.ts +4 -0
  67. package/dist/devtools/PerfPanel.js +53 -0
  68. package/dist/devtools/TunePanel.d.ts +3 -0
  69. package/dist/devtools/TunePanel.js +309 -0
  70. package/dist/devtools/agentBridge.d.ts +41 -0
  71. package/dist/devtools/agentBridge.js +262 -0
  72. package/dist/devtools/collisionDebug.d.ts +6 -0
  73. package/dist/devtools/collisionDebug.js +6 -0
  74. package/dist/devtools/collisionDebugMath.d.ts +8 -2
  75. package/dist/devtools/collisionDebugMath.js +24 -5
  76. package/dist/devtools/devtoolsOverrides.d.ts +6 -0
  77. package/dist/devtools/devtoolsOverrides.js +48 -0
  78. package/dist/devtools/panelAtoms.d.ts +9 -0
  79. package/dist/devtools/panelAtoms.js +10 -0
  80. package/dist/devtools/perfDiagnose.d.ts +2 -0
  81. package/dist/devtools/perfDiagnose.js +43 -0
  82. package/dist/diagnostics/RuntimeDiagnostics.d.ts +29 -0
  83. package/dist/diagnostics/RuntimeDiagnostics.js +69 -0
  84. package/dist/drivers/FrameDriver.d.ts +37 -0
  85. package/dist/drivers/FrameDriver.js +232 -0
  86. package/dist/drivers/HudOnlyDriver.d.ts +16 -0
  87. package/dist/drivers/HudOnlyDriver.js +60 -0
  88. package/dist/environment/Daylight.d.ts +23 -7
  89. package/dist/environment/Daylight.js +106 -13
  90. package/dist/environment/EnvironmentScene.js +32 -6
  91. package/dist/environment/VolumetricClouds.d.ts +17 -0
  92. package/dist/environment/VolumetricClouds.js +202 -0
  93. package/dist/environment/daylightCycle.d.ts +3 -1
  94. package/dist/environment/daylightCycle.js +3 -1
  95. package/dist/environment/groundPadMath.d.ts +3 -0
  96. package/dist/environment/groundPadMath.js +3 -0
  97. package/dist/environment/index.d.ts +1 -0
  98. package/dist/environment/index.js +1 -0
  99. package/dist/environment/skyLightingPolicy.d.ts +2 -0
  100. package/dist/environment/skyLightingPolicy.js +2 -0
  101. package/dist/hotbarActions.d.ts +26 -0
  102. package/dist/hotbarActions.js +27 -0
  103. package/dist/inputSink.d.ts +16 -4
  104. package/dist/inputSink.js +57 -7
  105. package/dist/materialOverride.d.ts +10 -0
  106. package/dist/materialOverride.js +15 -3
  107. package/dist/multiplayer.js +8 -2
  108. package/dist/postfx/PostProcessing.js +47 -9
  109. package/dist/postfx/gradeShader.js +9 -1
  110. package/dist/registry.d.ts +3 -2
  111. package/dist/render/EnvironmentLighting.d.ts +10 -0
  112. package/dist/render/EnvironmentLighting.js +34 -0
  113. package/dist/render/SceneLighting.d.ts +7 -0
  114. package/dist/render/SceneLighting.js +18 -0
  115. package/dist/render/SceneModels.d.ts +14 -0
  116. package/dist/render/SceneModels.js +337 -0
  117. package/dist/render/defaultSurface.d.ts +29 -0
  118. package/dist/render/defaultSurface.js +101 -0
  119. package/dist/render/modelLoad.d.ts +24 -0
  120. package/dist/render/modelLoad.js +73 -0
  121. package/dist/render/modelRender.d.ts +11 -0
  122. package/dist/render/modelRender.js +15 -0
  123. package/dist/render/resolveModel.d.ts +39 -3
  124. package/dist/render/resolveModel.js +79 -2
  125. package/dist/render/useEntityRenderCues.d.ts +13 -0
  126. package/dist/render/useEntityRenderCues.js +62 -0
  127. package/dist/scatter/InstancedScatter.d.ts +24 -0
  128. package/dist/scatter/InstancedScatter.js +130 -0
  129. package/dist/scatter/scatterModels.d.ts +25 -0
  130. package/dist/scatter/scatterModels.js +61 -0
  131. package/dist/scatter/scatterProxies.d.ts +6 -0
  132. package/dist/scatter/scatterProxies.js +94 -0
  133. package/dist/scatter.d.ts +1 -0
  134. package/dist/scatter.js +1 -0
  135. package/dist/scene/AuthoredScene.d.ts +82 -0
  136. package/dist/scene/AuthoredScene.js +173 -0
  137. package/dist/scene/GeneratedAssetRenderer.d.ts +14 -0
  138. package/dist/scene/GeneratedAssetRenderer.js +59 -0
  139. package/dist/scene/GrassFieldRenderer.d.ts +2 -0
  140. package/dist/scene/GrassFieldRenderer.js +25 -0
  141. package/dist/scene/SoilPatchRenderer.d.ts +2 -0
  142. package/dist/scene/SoilPatchRenderer.js +21 -0
  143. package/dist/scene/StudioStage.d.ts +37 -0
  144. package/dist/scene/StudioStage.js +42 -0
  145. package/dist/scene/WaterRenderer.d.ts +2 -0
  146. package/dist/scene/WaterRenderer.js +37 -0
  147. package/dist/scene/builtinSceneKindRenderers.d.ts +2 -0
  148. package/dist/scene/builtinSceneKindRenderers.js +22 -0
  149. package/dist/scene/sceneKindRenderers.d.ts +28 -0
  150. package/dist/scene/sceneKindRenderers.js +17 -0
  151. package/dist/scene.d.ts +1 -0
  152. package/dist/scene.js +1 -0
  153. package/dist/settings/appliedSettings.d.ts +5 -0
  154. package/dist/settings/appliedSettings.js +9 -0
  155. package/dist/settings/settingsController.d.ts +2 -0
  156. package/dist/settings/settingsController.js +22 -1
  157. package/dist/shellConstants.d.ts +4 -0
  158. package/dist/shellConstants.js +4 -0
  159. package/dist/shellMovement.d.ts +8 -0
  160. package/dist/shellMovement.js +11 -0
  161. package/dist/shellPointer.d.ts +20 -0
  162. package/dist/shellPointer.js +28 -0
  163. package/dist/structures/PlacementGhost.d.ts +4 -0
  164. package/dist/structures/PlacementGhost.js +4 -0
  165. package/dist/structures/TransformGizmo.d.ts +43 -0
  166. package/dist/structures/TransformGizmo.js +55 -0
  167. package/dist/structures/index.d.ts +1 -0
  168. package/dist/structures/index.js +1 -0
  169. package/dist/terrain/GrassField.js +1 -1
  170. package/dist/terrain/TerraformBrushCursor.js +3 -0
  171. package/dist/terrain/grassBudget.d.ts +11 -2
  172. package/dist/terrain/grassBudget.js +20 -4
  173. package/dist/terrain/grassGeometry.d.ts +3 -0
  174. package/dist/terrain/grassGeometry.js +3 -0
  175. package/dist/terrain/grassMaterial.d.ts +2 -0
  176. package/dist/terrain/grassMaterial.js +2 -0
  177. package/dist/terrain/random.d.ts +3 -0
  178. package/dist/terrain/random.js +3 -0
  179. package/dist/terrain/soilPatchMaterial.d.ts +10 -0
  180. package/dist/terrain/soilPatchMaterial.js +109 -0
  181. package/dist/terrain/terrainDetailMaterial.d.ts +19 -2
  182. package/dist/terrain/terrainDetailMaterial.js +72 -5
  183. package/dist/terrain/terrainMath.d.ts +8 -1
  184. package/dist/terrain/terrainMath.js +8 -1
  185. package/dist/touch/TouchControlsOverlay.d.ts +9 -5
  186. package/dist/touch/TouchControlsOverlay.js +201 -22
  187. package/dist/useShellMultiplayerSync.d.ts +9 -0
  188. package/dist/useShellMultiplayerSync.js +98 -0
  189. package/dist/visibility/CullingProvider.js +1 -1
  190. package/dist/water/OceanConfig.d.ts +2 -0
  191. package/dist/water/OceanConfig.js +2 -0
  192. package/dist/weather/fireSpreadPose.d.ts +1 -0
  193. package/dist/weather/fireSpreadPose.js +1 -0
  194. package/dist/weather/weatherGeometry.d.ts +1 -0
  195. package/dist/weather/weatherGeometry.js +1 -0
  196. package/dist/weather/weatherMath.d.ts +3 -0
  197. package/dist/weather/weatherMath.js +3 -0
  198. package/dist/weather/weatherUniforms.d.ts +3 -0
  199. package/dist/weather/weatherUniforms.js +3 -0
  200. package/dist/world/RetainedVfx.d.ts +10 -0
  201. package/dist/world/RetainedVfx.js +136 -0
  202. package/dist/world/WorldHud.d.ts +49 -2
  203. package/dist/world/WorldHud.js +87 -2
  204. package/dist/world/WorldScene.d.ts +21 -0
  205. package/dist/world/WorldScene.js +159 -0
  206. package/dist/world/WorldVfx.d.ts +9 -0
  207. package/dist/world/WorldVfx.js +184 -0
  208. package/dist/world/entityPose.d.ts +2 -0
  209. package/dist/world/entityPose.js +2 -0
  210. package/dist/world/floatTextStyle.d.ts +1 -0
  211. package/dist/world/floatTextStyle.js +1 -0
  212. package/dist/world/telegraphPulse.d.ts +1 -0
  213. package/dist/world/telegraphPulse.js +1 -0
  214. package/dist/world/worldBarSamples.d.ts +25 -0
  215. package/dist/world/worldBarSamples.js +38 -0
  216. package/dist/worldSky.d.ts +5 -0
  217. package/dist/worldSky.js +11 -0
  218. package/dist/worldSync.d.ts +2 -1
  219. package/dist/worldSync.js +2 -1
  220. package/package.json +6 -6
  221. package/skills/jgengine/SKILL.md +51 -0
  222. package/skills/jgengine/api.md +434 -0
  223. package/skills/jgengine/capabilities.md +29 -0
  224. package/skills/jgengine/reference-cartridge.md +72 -0
  225. package/skills/jgengine-ui/SKILL.md +36 -0
  226. package/skills/jgengine-ui/api.md +1852 -0
  227. package/skills/jgengine-ui/capabilities.md +142 -0
  228. package/skills/jgengine-ui/reference.md +500 -0
  229. package/skills/jgengine-ui/references/visual-scorecard.md +73 -0
  230. package/skills/jgengine-world/SKILL.md +38 -0
  231. package/skills/jgengine-world/api.md +2652 -0
  232. package/skills/jgengine-world/capabilities.md +370 -0
  233. package/skills/jgengine-world/reference.md +222 -0
  234. package/llms.txt +0 -2761
@@ -0,0 +1,184 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useFrame } from "@react-three/fiber";
3
+ import { useEffect, useMemo, useRef, useState } from "react";
4
+ import * as THREE from "three";
5
+ import { useGameContext } from "@jgengine/react/provider";
6
+ const PARTICLE_COUNT = {
7
+ projectile: 7,
8
+ beam: 6,
9
+ nova: 14,
10
+ glow: 10,
11
+ spark: 12,
12
+ };
13
+ function planarDir() {
14
+ const angle = Math.random() * Math.PI * 2;
15
+ return new THREE.Vector3(Math.cos(angle), 0, Math.sin(angle));
16
+ }
17
+ function sphereDir() {
18
+ return new THREE.Vector3(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1)
19
+ .normalize();
20
+ }
21
+ function makeParticles(kind) {
22
+ const count = PARTICLE_COUNT[kind];
23
+ const out = [];
24
+ for (let i = 0; i < count; i += 1) {
25
+ if (kind === "nova") {
26
+ out.push({ offset: new THREE.Vector3(), dir: planarDir(), speed: 1, size: 0.5 + Math.random() * 0.4, delay: 0 });
27
+ }
28
+ else if (kind === "spark") {
29
+ const dir = sphereDir();
30
+ dir.y = Math.abs(dir.y) * 0.6 + 0.2;
31
+ out.push({ offset: new THREE.Vector3(), dir, speed: 2.4 + Math.random() * 1.8, size: 0.28 + Math.random() * 0.22, delay: 0 });
32
+ }
33
+ else if (kind === "glow") {
34
+ out.push({
35
+ offset: planarDir().multiplyScalar(0.35 + Math.random() * 0.5),
36
+ dir: new THREE.Vector3(0, 1, 0),
37
+ speed: 0.5 + Math.random() * 0.6,
38
+ size: 0.55 + Math.random() * 0.5,
39
+ delay: Math.random() * 0.3,
40
+ });
41
+ }
42
+ else if (kind === "beam") {
43
+ out.push({ offset: new THREE.Vector3(), dir: new THREE.Vector3(), speed: 0, size: 0.45 + Math.random() * 0.3, delay: 0 });
44
+ }
45
+ else {
46
+ out.push({
47
+ offset: sphereDir().multiplyScalar(0.12),
48
+ dir: new THREE.Vector3(),
49
+ speed: 0,
50
+ size: 0.4 + Math.random() * 0.25,
51
+ delay: i * 0.05,
52
+ });
53
+ }
54
+ }
55
+ return out;
56
+ }
57
+ function useRadialTexture() {
58
+ return useMemo(() => {
59
+ const size = 64;
60
+ const canvas = document.createElement("canvas");
61
+ canvas.width = size;
62
+ canvas.height = size;
63
+ const c = canvas.getContext("2d");
64
+ if (c !== null) {
65
+ const gradient = c.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
66
+ gradient.addColorStop(0, "rgba(255,255,255,1)");
67
+ gradient.addColorStop(0.35, "rgba(255,255,255,0.65)");
68
+ gradient.addColorStop(1, "rgba(255,255,255,0)");
69
+ c.fillStyle = gradient;
70
+ c.fillRect(0, 0, size, size);
71
+ }
72
+ const texture = new THREE.CanvasTexture(canvas);
73
+ texture.needsUpdate = true;
74
+ return texture;
75
+ }, []);
76
+ }
77
+ function clamp01(value) {
78
+ return value < 0 ? 0 : value > 1 ? 1 : value;
79
+ }
80
+ function VfxItem({ event, texture }) {
81
+ const born = useRef(performance.now());
82
+ const sprites = useRef([]);
83
+ const ringRef = useRef(null);
84
+ const ringMat = useRef(null);
85
+ const beamMat = useRef(null);
86
+ const color = useMemo(() => new THREE.Color(event.color), [event.color]);
87
+ const from = useMemo(() => new THREE.Vector3(event.from[0], event.from[1] + 1, event.from[2]), [event.from]);
88
+ const to = useMemo(() => (event.to === undefined ? from.clone() : new THREE.Vector3(event.to[0], event.to[1] + 1, event.to[2])), [event.to, from]);
89
+ const radius = event.radius ?? 2.5;
90
+ const beam = useMemo(() => {
91
+ const delta = new THREE.Vector3().subVectors(to, from);
92
+ const length = Math.max(0.001, delta.length());
93
+ const mid = new THREE.Vector3().addVectors(from, to).multiplyScalar(0.5);
94
+ const quat = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), delta.clone().normalize());
95
+ return { length, mid, quat };
96
+ }, [from, to]);
97
+ const particles = useMemo(() => makeParticles(event.kind), [event.kind]);
98
+ const scratch = useRef(new THREE.Vector3());
99
+ useFrame(() => {
100
+ const p = clamp01((performance.now() - born.current) / event.durationMs);
101
+ const kind = event.kind;
102
+ for (let i = 0; i < particles.length; i += 1) {
103
+ const sprite = sprites.current[i];
104
+ const particle = particles[i];
105
+ if (sprite === undefined || sprite === null || particle === undefined)
106
+ continue;
107
+ const pos = scratch.current;
108
+ let opacity = 0;
109
+ let scale = particle.size;
110
+ if (kind === "projectile") {
111
+ const travel = clamp01(p * 1.15 - particle.delay);
112
+ pos.copy(from).lerp(to, travel).add(particle.offset);
113
+ opacity = (1 - p) * (i === 0 ? 1 : 0.55);
114
+ scale = particle.size * (i === 0 ? 1 : 0.7);
115
+ }
116
+ else if (kind === "beam") {
117
+ pos.copy(from).lerp(to, i / Math.max(1, particles.length - 1));
118
+ opacity = (1 - p) * 0.8;
119
+ }
120
+ else if (kind === "nova") {
121
+ pos.set(event.from[0], event.from[1] + 0.4, event.from[2]).addScaledVector(particle.dir, radius * p);
122
+ opacity = (1 - p) * 0.9;
123
+ }
124
+ else if (kind === "glow") {
125
+ const local = clamp01(p - particle.delay);
126
+ pos.copy(from).add(particle.offset).addScaledVector(particle.dir, particle.speed * local);
127
+ opacity = Math.sin(clamp01(p) * Math.PI) * 0.9;
128
+ scale = particle.size * (0.8 + 0.3 * Math.sin(local * Math.PI));
129
+ }
130
+ else {
131
+ pos.copy(to).addScaledVector(particle.dir, particle.speed * p);
132
+ pos.y -= p * p * 1.4;
133
+ opacity = (1 - p) * 0.95;
134
+ }
135
+ sprite.position.copy(pos);
136
+ sprite.scale.set(scale, scale, 1);
137
+ const material = sprite.material;
138
+ material.opacity = opacity;
139
+ }
140
+ if (ringRef.current !== null && ringMat.current !== null) {
141
+ const s = Math.max(0.001, radius * p);
142
+ ringRef.current.scale.set(s, s, s);
143
+ ringMat.current.opacity = (1 - p) * 0.7;
144
+ }
145
+ if (beamMat.current !== null) {
146
+ beamMat.current.opacity = (1 - p) * 0.85;
147
+ }
148
+ });
149
+ return (_jsxs("group", { children: [particles.map((_, i) => (_jsx("sprite", { ref: (node) => {
150
+ sprites.current[i] = node;
151
+ }, children: _jsx("spriteMaterial", { map: texture, color: color, transparent: true, depthWrite: false, depthTest: false, blending: THREE.AdditiveBlending, toneMapped: false, opacity: 0 }) }, i))), event.kind === "nova" && (_jsxs("mesh", { ref: ringRef, position: [event.from[0], event.from[1] + 0.06, event.from[2]], rotation: [-Math.PI / 2, 0, 0], renderOrder: 998, children: [_jsx("ringGeometry", { args: [0.82, 1, 48] }), _jsx("meshBasicMaterial", { ref: ringMat, color: color, transparent: true, side: THREE.DoubleSide, depthWrite: false, depthTest: false, blending: THREE.AdditiveBlending, toneMapped: false, opacity: 0 })] })), event.kind === "beam" && (_jsxs("mesh", { position: [beam.mid.x, beam.mid.y, beam.mid.z], quaternion: [beam.quat.x, beam.quat.y, beam.quat.z, beam.quat.w], renderOrder: 998, children: [_jsx("cylinderGeometry", { args: [0.12, 0.12, beam.length, 8, 1, true] }), _jsx("meshBasicMaterial", { ref: beamMat, color: color, transparent: true, side: THREE.DoubleSide, depthWrite: false, blending: THREE.AdditiveBlending, toneMapped: false, opacity: 0 })] }))] }));
152
+ }
153
+ /**
154
+ * Renders `combat.vfx` bursts as additive sprite-particle effects in the world scene. Auto-mounted by the
155
+ * player shell alongside the other world overlays; games emit effects via `ctx.scene.entity.vfx(...)`.
156
+ *
157
+ * @internal
158
+ */
159
+ export function WorldSpellVfx({ tailMs = 200 }) {
160
+ const ctx = useGameContext();
161
+ const texture = useRadialTexture();
162
+ const [active, setActive] = useState([]);
163
+ const key = useRef(0);
164
+ const timers = useRef(new Set());
165
+ useEffect(() => {
166
+ const pending = timers.current;
167
+ const off = ctx.game.events.on("combat.vfx", (event) => {
168
+ const entry = { key: key.current++, event };
169
+ setActive((current) => [...current, entry]);
170
+ const timer = setTimeout(() => {
171
+ pending.delete(timer);
172
+ setActive((current) => current.filter((item) => item.key !== entry.key));
173
+ }, event.durationMs + tailMs);
174
+ pending.add(timer);
175
+ });
176
+ return () => {
177
+ off();
178
+ for (const timer of pending)
179
+ clearTimeout(timer);
180
+ pending.clear();
181
+ };
182
+ }, [ctx, tailMs]);
183
+ return (_jsx(_Fragment, { children: active.map((item) => (_jsx(VfxItem, { event: item.event, texture: texture }, item.key))) }));
184
+ }
@@ -10,5 +10,7 @@ export interface PoseSource {
10
10
  position: readonly [number, number, number];
11
11
  rotationY: number;
12
12
  }
13
+ /** @internal */
13
14
  export declare function writeEntityPose(target: PoseWritable, source: PoseSource): void;
15
+ /** @internal */
14
16
  export declare function posesEqual(a: PoseSource, b: PoseSource): boolean;
@@ -1,7 +1,9 @@
1
+ /** @internal */
1
2
  export function writeEntityPose(target, source) {
2
3
  target.position.set(source.position[0], source.position[1], source.position[2]);
3
4
  target.rotation.y = source.rotationY;
4
5
  }
6
+ /** @internal */
5
7
  export function posesEqual(a, b) {
6
8
  return (a.position[0] === b.position[0] &&
7
9
  a.position[1] === b.position[1] &&
@@ -11,4 +11,5 @@ export interface FloatTextStyle {
11
11
  fontWeight: number;
12
12
  glow: string;
13
13
  }
14
+ /** @internal */
14
15
  export declare function resolveFloatTextStyle(info: FloatTextInfo): FloatTextStyle;
@@ -12,6 +12,7 @@ const ELEMENT_COLORS = {
12
12
  holy: "#fef08a",
13
13
  void: "#c084fc",
14
14
  };
15
+ /** @internal */
15
16
  export function resolveFloatTextStyle(info) {
16
17
  const crit = info.crit === true || info.hitType === "crit";
17
18
  let color;
@@ -1 +1,2 @@
1
+ /** @internal */
1
2
  export declare function telegraphPulseOpacity(bornMs: number, windupMs: number, nowMs: number): number;
@@ -1,3 +1,4 @@
1
+ /** @internal */
1
2
  export function telegraphPulseOpacity(bornMs, windupMs, nowMs) {
2
3
  const progress = Math.max(0, Math.min(1, (nowMs - bornMs) / Math.max(1, windupMs)));
3
4
  return 0.45 + 0.5 * progress;
@@ -16,6 +16,7 @@ export interface Projectable {
16
16
  y: number;
17
17
  z: number;
18
18
  }
19
+ /** @internal */
19
20
  export declare function collectWorldBarSamples(ctx: GameContext, statId: string, height: number, roles: readonly CatalogEntityRole[] | undefined, resolveRole: ((entity: SceneEntity) => CatalogEntityRole | undefined) | undefined, camera: {
20
21
  matrixWorldInverse: unknown;
21
22
  projectionMatrix: unknown;
@@ -23,6 +24,30 @@ export declare function collectWorldBarSamples(ctx: GameContext, statId: string,
23
24
  width: number;
24
25
  height: number;
25
26
  }, into: WorldBarSample[], project: Projectable, maxDistance?: number): number;
27
+ /** One entity's projected nameplate: screen `x`/`y`, display `name`, health `percent` (or `null` when statless), and world `distance` from the player. */
28
+ export interface NameplateSample {
29
+ id: string;
30
+ name: string;
31
+ x: number;
32
+ y: number;
33
+ /** Health-stat fraction 0..1, or `null` when the entity carries no `statId` stat (name-only nameplate). */
34
+ percent: number | null;
35
+ distance: number;
36
+ }
37
+ /**
38
+ * Projects every non-local, role/distance-filtered entity into `into` as a
39
+ * `NameplateSample`. Pure data step powering `WorldNameplates` — swap in your
40
+ * own renderer by calling this directly instead of the component.
41
+ * @internal
42
+ */
43
+ export declare function collectNameplateSamples(ctx: GameContext, statId: string, height: number, roles: readonly CatalogEntityRole[] | undefined, resolveRole: ((entity: SceneEntity) => CatalogEntityRole | undefined) | undefined, camera: {
44
+ matrixWorldInverse: unknown;
45
+ projectionMatrix: unknown;
46
+ }, viewport: {
47
+ width: number;
48
+ height: number;
49
+ }, into: NameplateSample[], project: Projectable, maxDistance?: number): number;
50
+ /** @internal */
26
51
  export declare function paintWorldBarSamples(canvas: {
27
52
  width: number;
28
53
  height: number;
@@ -1,4 +1,5 @@
1
1
  import { worldHealthBarAllowsRole } from "@jgengine/core/game/playableGame";
2
+ /** @internal */
2
3
  export function collectWorldBarSamples(ctx, statId, height, roles, resolveRole, camera, viewport, into, project, maxDistance = 60) {
3
4
  into.length = 0;
4
5
  const playerId = ctx.player.userId;
@@ -28,6 +29,43 @@ export function collectWorldBarSamples(ctx, statId, height, roles, resolveRole,
28
29
  }
29
30
  return into.length;
30
31
  }
32
+ /**
33
+ * Projects every non-local, role/distance-filtered entity into `into` as a
34
+ * `NameplateSample`. Pure data step powering `WorldNameplates` — swap in your
35
+ * own renderer by calling this directly instead of the component.
36
+ * @internal
37
+ */
38
+ export function collectNameplateSamples(ctx, statId, height, roles, resolveRole, camera, viewport, into, project, maxDistance = 40) {
39
+ into.length = 0;
40
+ const playerId = ctx.player.userId;
41
+ const player = ctx.scene.entity.get(playerId);
42
+ for (const entity of ctx.scene.entity.list()) {
43
+ if (entity.id === playerId)
44
+ continue;
45
+ if (!worldHealthBarAllowsRole(roles, resolveRole?.(entity)))
46
+ continue;
47
+ const distance = player === null
48
+ ? 0
49
+ : Math.hypot(entity.position[0] - player.position[0], entity.position[2] - player.position[2]);
50
+ if (player !== null && distance > maxDistance)
51
+ continue;
52
+ const stat = ctx.scene.entity.stats.get(entity.id, statId);
53
+ let percent = null;
54
+ if (stat !== null) {
55
+ const range = stat.max - stat.min;
56
+ percent = range <= 0 ? 0 : Math.max(0, Math.min(1, (stat.current - stat.min) / range));
57
+ }
58
+ project.set(entity.position[0], entity.position[1] + height, entity.position[2]);
59
+ project.project(camera);
60
+ if (project.z < -1 || project.z > 1)
61
+ continue;
62
+ const x = (project.x * 0.5 + 0.5) * viewport.width;
63
+ const y = (-project.y * 0.5 + 0.5) * viewport.height;
64
+ into.push({ id: entity.id, name: entity.name, x, y, percent, distance });
65
+ }
66
+ return into.length;
67
+ }
68
+ /** @internal */
31
69
  export function paintWorldBarSamples(canvas, samples, dpr, barWidthPx = 112, barHeightPx = 10) {
32
70
  const g = canvas.getContext("2d");
33
71
  if (g === null)
@@ -0,0 +1,5 @@
1
+ import type { SkyEnvironmentDescriptor, WorldFeature } from "@jgengine/core/world/features";
2
+ /** The world's declared sky, when its world feature is an environment with one (#196.1). @internal */
3
+ export declare function resolveWorldSky(world: WorldFeature | undefined): SkyEnvironmentDescriptor | undefined;
4
+ /** Deterministic HSL color from a stable instance id (default entity/object tint). @internal */
5
+ export declare function colorFromId(id: string): string;
@@ -0,0 +1,11 @@
1
+ /** The world's declared sky, when its world feature is an environment with one (#196.1). @internal */
2
+ export function resolveWorldSky(world) {
3
+ return world?.kind === "environment" ? world.sky : undefined;
4
+ }
5
+ /** Deterministic HSL color from a stable instance id (default entity/object tint). @internal */
6
+ export function colorFromId(id) {
7
+ let hash = 0;
8
+ for (let index = 0; index < id.length; index += 1)
9
+ hash = (hash * 31 + id.charCodeAt(index)) >>> 0;
10
+ return `hsl(${hash % 360}, 65%, 55%)`;
11
+ }
@@ -6,5 +6,6 @@ import type { GameRuntimeFeeds } from "@jgengine/core/runtime/transport";
6
6
  * of a locally-simulated one. Pure and transport-agnostic — the backend's `feeds.subscribeServer` is the only
7
7
  * dependency; returns the unsubscribe. The shell attaches this (and gates its local sim) when the game's adapter
8
8
  * opts into `authority: "server"`.
9
- */
9
+ * @internal
10
+ */
10
11
  export declare function attachWorldSync(feeds: Pick<GameRuntimeFeeds, "subscribeServer">, serverId: string, ctx: Pick<GameContext, "hydrate">): () => void;
package/dist/worldSync.js CHANGED
@@ -5,7 +5,8 @@ import { createWorldMirror } from "@jgengine/core/runtime/worldMirror";
5
5
  * of a locally-simulated one. Pure and transport-agnostic — the backend's `feeds.subscribeServer` is the only
6
6
  * dependency; returns the unsubscribe. The shell attaches this (and gates its local sim) when the game's adapter
7
7
  * opts into `authority: "server"`.
8
- */
8
+ * @internal
9
+ */
9
10
  export function attachWorldSync(feeds, serverId, ctx) {
10
11
  const mirror = createWorldMirror(ctx);
11
12
  return feeds.subscribeServer(serverId, (view) => {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@jgengine/shell",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Game player shell for JGengine: React Three Fiber canvas, orbit camera, input tracking, HUD mounting, GameUiPreview, and a demo game. Consumers supply a GameRegistry.",
5
- "license": "AGPL-3.0-only",
5
+ "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "sideEffects": false,
8
8
  "repository": {
@@ -13,7 +13,7 @@
13
13
  "files": [
14
14
  "dist",
15
15
  "CHANGELOG.md",
16
- "llms.txt"
16
+ "skills"
17
17
  ],
18
18
  "exports": {
19
19
  "./weather": {
@@ -35,9 +35,9 @@
35
35
  "test": "bun test src"
36
36
  },
37
37
  "dependencies": {
38
- "@jgengine/core": "^0.10.0",
39
- "@jgengine/react": "^0.10.0",
40
- "@jgengine/ws": "^0.10.0"
38
+ "@jgengine/core": "^0.11.0",
39
+ "@jgengine/react": "^0.11.0",
40
+ "@jgengine/ws": "^0.11.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@react-three/drei": "^10.0.0",
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: jgengine
3
+ description: Route game work to the smallest useful JGengine guidance set.
4
+ ---
5
+
6
+ # JGengine intake and router
7
+
8
+ Read the repository [README](../../../README.md) for package truth, commands, layering, and license. This skill decides what to load; it is not an engine manual.
9
+
10
+ ## 1. Establish the target
11
+
12
+ Capture only decisions that change architecture:
13
+
14
+ - player point of view and controls
15
+ - world shape and authored content
16
+ - core loop, interaction, combat, and progression
17
+ - solo, local, hosted, or persistent multiplayer
18
+ - HUD/menu states and art direction
19
+ - one observable completion scenario
20
+
21
+ For a new in-repo game, scaffold with `bun run new:game <id> --name "Title"`. Outside this monorepo, start with `npx jgengine create`; never copy `Games/*`.
22
+
23
+ ## 2. Select domains
24
+
25
+ Load only rows the target needs. Do not preload every domain.
26
+
27
+ | Need | Skill | Owner |
28
+ | --- | --- | --- |
29
+ | scene placement, terrain painting, paths, asset authoring | `jgengine-editor` | editor document and authoring tools |
30
+ | runtime world, movement, input, interaction, AI, navigation, audio | `jgengine-world` | spatial simulation and world runtime |
31
+ | game state, systems, inventory, crafting, quests, economy, turns | `jgengine-gameplay` | non-combat rules and state |
32
+ | targeting, stats, damage, effects, abilities, projectiles, loot | `jgengine-combat` | combat resolution |
33
+ | HUD, menus, layout, accessibility, touch presentation | `jgengine-ui` | player-facing interface |
34
+ | models, sprites, materials, audio files, source licensing | `jgengine-assets` | asset discovery and catalogs |
35
+ | authority, transport, replication, sessions, host persistence | `jgengine-multiplayer` | network topology and persistence adapters |
36
+
37
+ Scenes normally require both `jgengine-editor` for authoring and `jgengine-world` for runtime consumption. Combat and multiplayer are opt-in, not default intake.
38
+
39
+ ## 3. Discover before designing
40
+
41
+ Within each selected domain:
42
+
43
+ 1. Search `capabilities.md` by intent; it maps needs to imports.
44
+ 2. Open `api.md` only when exact signatures or export inventory are needed.
45
+ 3. Open the linked reference only for a deeper recipe or trap.
46
+
47
+ If no capability fits, identify the upstream package seam before writing game-local code. In this repository, implement a reusable primitive with its first adopter; if editor-owned content cannot be expressed, file the editor gap first.
48
+
49
+ ## 4. Build and finish
50
+
51
+ Keep scene coordinates in the editor document, state serializable, randomness injected, and hot-path work bounded. Use `jgengine-verify` for evidence appropriate to the change. Inside this monorepo, `workflow` owns issue/PR completion. Visual polish work also uses the `jgengine-ui` quality reference.