@jgengine/shell 0.7.0 → 0.8.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 (82) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +26 -1
  3. package/dist/GameHost.d.ts +12 -0
  4. package/dist/GameHost.js +33 -0
  5. package/dist/GamePlayer.d.ts +11 -0
  6. package/dist/GamePlayer.js +24 -0
  7. package/dist/GamePlayerShell.d.ts +33 -1
  8. package/dist/GamePlayerShell.js +439 -28
  9. package/dist/behaviour.d.ts +31 -0
  10. package/dist/behaviour.js +63 -0
  11. package/dist/camera/GameCameraRig.d.ts +8 -3
  12. package/dist/camera/GameCameraRig.js +28 -14
  13. package/dist/camera/GameFirstPersonCamera.js +2 -0
  14. package/dist/camera/GameInspectionCamera.d.ts +6 -0
  15. package/dist/camera/GameInspectionCamera.js +24 -0
  16. package/dist/camera/cameraRigs.d.ts +5 -1
  17. package/dist/camera/cameraRigs.js +43 -6
  18. package/dist/camera/index.d.ts +4 -2
  19. package/dist/camera/index.js +4 -2
  20. package/dist/camera/inspectionCameraMath.d.ts +25 -0
  21. package/dist/camera/inspectionCameraMath.js +44 -0
  22. package/dist/camera/rigMath.d.ts +40 -1
  23. package/dist/camera/rigMath.js +55 -0
  24. package/dist/camera/rigResolve.d.ts +15 -0
  25. package/dist/camera/rigResolve.js +52 -0
  26. package/dist/defineGame.d.ts +16 -0
  27. package/dist/defineGame.js +51 -0
  28. package/dist/devtools/DevtoolsOverlay.d.ts +14 -0
  29. package/dist/devtools/DevtoolsOverlay.js +334 -0
  30. package/dist/environment/Daylight.d.ts +50 -0
  31. package/dist/environment/Daylight.js +100 -0
  32. package/dist/environment/EnvironmentScene.js +18 -26
  33. package/dist/environment/GroundPad.d.ts +7 -0
  34. package/dist/environment/GroundPad.js +12 -0
  35. package/dist/environment/daylightCycle.d.ts +26 -0
  36. package/dist/environment/daylightCycle.js +116 -0
  37. package/dist/environment/groundPadMath.d.ts +13 -0
  38. package/dist/environment/groundPadMath.js +10 -0
  39. package/dist/environment/index.d.ts +2 -0
  40. package/dist/environment/index.js +2 -0
  41. package/dist/materialOverride.d.ts +8 -0
  42. package/dist/materialOverride.js +32 -0
  43. package/dist/multiplayer.d.ts +16 -9
  44. package/dist/multiplayer.js +62 -12
  45. package/dist/pointer/PointerProbe.js +14 -1
  46. package/dist/pointer/pointerService.d.ts +2 -0
  47. package/dist/pointer/pointerService.js +45 -24
  48. package/dist/registry.d.ts +4 -1
  49. package/dist/registry.js +3 -1
  50. package/dist/render/modelRender.d.ts +14 -0
  51. package/dist/render/modelRender.js +79 -0
  52. package/dist/terrain/index.d.ts +1 -1
  53. package/dist/terrain/index.js +1 -1
  54. package/dist/terrain/terrainMath.d.ts +1 -0
  55. package/dist/terrain/terrainMath.js +8 -2
  56. package/dist/touch/TouchControlsOverlay.d.ts +18 -0
  57. package/dist/touch/TouchControlsOverlay.js +173 -0
  58. package/dist/water/OceanShader.d.ts +1 -1
  59. package/dist/water/OceanShader.js +3 -3
  60. package/dist/world/DataObjects.d.ts +44 -0
  61. package/dist/world/DataObjects.js +75 -0
  62. package/dist/world/GridWorldScene.d.ts +10 -0
  63. package/dist/world/GridWorldScene.js +54 -0
  64. package/dist/world/WorldHud.d.ts +5 -1
  65. package/dist/world/WorldHud.js +6 -2
  66. package/dist/world/WorldItems.js +21 -2
  67. package/llms.txt +2382 -0
  68. package/package.json +6 -5
  69. package/dist/demo/builderDemo.d.ts +0 -2
  70. package/dist/demo/builderDemo.js +0 -189
  71. package/dist/demo/demoGame.d.ts +0 -2
  72. package/dist/demo/demoGame.js +0 -208
  73. package/dist/demo/environmentShowcase.d.ts +0 -2
  74. package/dist/demo/environmentShowcase.js +0 -15
  75. package/dist/demo/mapDemo.d.ts +0 -2
  76. package/dist/demo/mapDemo.js +0 -206
  77. package/dist/demo/pointerDemo.d.ts +0 -2
  78. package/dist/demo/pointerDemo.js +0 -151
  79. package/dist/demo/sensorShowcase.d.ts +0 -2
  80. package/dist/demo/sensorShowcase.js +0 -131
  81. package/dist/demo/survivalDemo.d.ts +0 -2
  82. package/dist/demo/survivalDemo.js +0 -291
@@ -1,291 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { defineGame } from "@jgengine/core/game/defineGame";
3
- import { createAssetCatalog } from "@jgengine/core/scene/assetCatalog";
4
- import { createDecayMeterSet, } from "@jgengine/core/survival/decayMeter";
5
- import { createMoodleStack, stackMoodles, } from "@jgengine/core/survival/moodle";
6
- import { createMultiRegionHealth, } from "@jgengine/core/survival/regionHealth";
7
- import { environment, rain, terrain } from "@jgengine/core/world/features";
8
- import { createEnvironmentField, } from "@jgengine/core/world/envField";
9
- import { resolveTerrainField } from "@jgengine/core/world/terrain";
10
- import { createFireGrid, resolveWeather, } from "@jgengine/core/world/weather";
11
- import { useEngineState } from "@jgengine/react/engineStore";
12
- import { EnvironmentScene } from "../environment/EnvironmentScene.js";
13
- import { FireSpreadLayer } from "../weather/FireSpreadLayer.js";
14
- const HERO = "survivor";
15
- const HOTBAR = "hotbar";
16
- const DAY_LENGTH = 24 * 3600;
17
- const FIRE_ORIGIN = [-16, -16];
18
- const FIRE_CELL = 2;
19
- const CAMPFIRE = { x: 8, z: 0, radius: 9, strength: 26 };
20
- const weatherTable = {
21
- clear: {},
22
- rain: { grip: 0.65, visibility: 0.7, spread: 0.35, chill: -4 },
23
- storm: { grip: 0.45, visibility: 0.4, structureDamage: 4, ignition: 0.15, spread: 0.25, chill: -8 },
24
- };
25
- function createSurvivalSim() {
26
- const meters = createDecayMeterSet([
27
- {
28
- id: "hunger",
29
- max: 100,
30
- rate: 0.9,
31
- thresholds: [
32
- { id: "peckish", label: "Peckish", at: 55, when: "below", severity: "neutral" },
33
- { id: "hungry", label: "Hungry", at: 30, when: "below", severity: "warning" },
34
- { id: "starving", label: "Starving", at: 12, when: "below", severity: "critical" },
35
- ],
36
- },
37
- {
38
- id: "thirst",
39
- max: 100,
40
- rate: 1.3,
41
- thresholds: [
42
- { id: "thirsty", label: "Thirsty", at: 35, when: "below", severity: "warning" },
43
- { id: "dehydrated", label: "Dehydrated", at: 12, when: "below", severity: "critical" },
44
- ],
45
- },
46
- {
47
- id: "warmth",
48
- max: 100,
49
- start: 72,
50
- rate: 0.6,
51
- thresholds: [
52
- { id: "chilly", label: "Chilly", at: 45, when: "below", severity: "neutral" },
53
- { id: "freezing", label: "Freezing", at: 20, when: "below", severity: "critical" },
54
- ],
55
- },
56
- {
57
- id: "stamina",
58
- max: 100,
59
- rate: 0,
60
- thresholds: [{ id: "winded", label: "Winded", at: 20, when: "below", severity: "warning" }],
61
- },
62
- ]);
63
- const health = createMultiRegionHealth({
64
- regions: [
65
- { id: "head", label: "Head", max: 35, vital: true, vulnerability: 2 },
66
- { id: "torso", label: "Torso", max: 85, vital: true },
67
- { id: "leftArm", label: "L. Arm", max: 55 },
68
- { id: "rightArm", label: "R. Arm", max: 55 },
69
- { id: "legs", label: "Legs", max: 70 },
70
- ],
71
- ailments: {
72
- bleed: {
73
- id: "bleed",
74
- label: "Bleeding",
75
- region: "torso",
76
- severity: "critical",
77
- drainPerSecond: 1.4,
78
- stacking: true,
79
- treatedBy: ["bandage"],
80
- },
81
- fracture: {
82
- id: "fracture",
83
- label: "Fractured Leg",
84
- region: "legs",
85
- severity: "warning",
86
- treatedBy: ["splint"],
87
- },
88
- },
89
- });
90
- const buffs = createMoodleStack();
91
- const fire = createFireGrid({
92
- cols: 16,
93
- rows: 16,
94
- cellSize: FIRE_CELL,
95
- origin: FIRE_ORIGIN,
96
- spreadRate: 0.9,
97
- burnRate: 0.05,
98
- wind: [1, 0.35],
99
- windBias: 0.7,
100
- });
101
- let weather = { kind: "rain", intensity: 0.55, wind: [1, 0.35] };
102
- const field = createEnvironmentField({
103
- dayLength: DAY_LENGTH,
104
- baseTemperature: 14,
105
- nightDrop: 14,
106
- heatSources: [CAMPFIRE],
107
- rain: () => (weather.kind === "clear" ? 0 : weather.intensity),
108
- occluders: [{ x: 20, z: 18, w: 10, d: 10, shade: 1 }],
109
- });
110
- let version = 0;
111
- let currentTime = 0;
112
- const listeners = new Set();
113
- function computeSnapshot() {
114
- const resolved = resolveWeather(weather, weatherTable);
115
- const temperature = field.temperature(0, 0, currentTime, 0);
116
- return {
117
- version,
118
- meters: meters.snapshot(),
119
- moodles: stackMoodles(buffs.list(), meters.moodles(), health.ailmentMoodles()),
120
- regions: health.regions(),
121
- overall: health.overall(),
122
- temperature,
123
- wetness: field.wetness(0, 0, currentTime),
124
- weather,
125
- visibility: resolved.visibility,
126
- grip: resolved.grip,
127
- burning: fire.burning,
128
- };
129
- }
130
- let snapshot = computeSnapshot();
131
- function notify() {
132
- version += 1;
133
- snapshot = computeSnapshot();
134
- for (const listener of listeners)
135
- listener(snapshot);
136
- }
137
- return {
138
- store: {
139
- getState: () => snapshot,
140
- subscribe: (listener) => {
141
- listeners.add(listener);
142
- return () => listeners.delete(listener);
143
- },
144
- },
145
- fire,
146
- field,
147
- bootstrap() {
148
- health.damage("head", 9);
149
- health.damage("legs", 26);
150
- health.applyAilment("bleed");
151
- health.applyAilment("bleed");
152
- health.applyAilment("fracture");
153
- buffs.add({ id: "cookedMeat", label: "Cooked Meat", duration: 320, severity: "good", note: "+HP regen" });
154
- buffs.add({ id: "berries", label: "Berries", duration: 180, severity: "good" });
155
- meters.refill("hunger", -58);
156
- meters.refill("thirst", -66);
157
- meters.refill("warmth", -34);
158
- meters.refill("stamina", -70);
159
- fire.ignite(-4, 4);
160
- fire.ignite(-2, 4);
161
- fire.ignite(-4, 6);
162
- fire.ignite(0, 6);
163
- notify();
164
- },
165
- tick(_ctx, dt, gameTime) {
166
- currentTime = gameTime;
167
- const sample = field.sample(0, 0, gameTime, 0);
168
- const coldPenalty = sample.temperature < 8 ? 2.4 : sample.temperature < 16 ? 1.4 : 0.6;
169
- meters.setRateModifier("warmth", coldPenalty);
170
- meters.tick(dt);
171
- buffs.tick(dt);
172
- health.tick(dt);
173
- const resolved = resolveWeather(weather, weatherTable);
174
- const wetnessAt = (col, row) => field.wetness(FIRE_ORIGIN[0] + col * FIRE_CELL, FIRE_ORIGIN[1] + row * FIRE_CELL, gameTime);
175
- fire.step(dt, { spread: resolved.spread, wetnessAt });
176
- notify();
177
- },
178
- treat(itemId) {
179
- health.treat(itemId);
180
- notify();
181
- },
182
- };
183
- }
184
- let sim = createSurvivalSim();
185
- const entityCatalog = {
186
- [HERO]: {
187
- stats: { health: { max: 100 }, stamina: { max: 100 } },
188
- receive: { damage: { order: ["health"] } },
189
- movement: { poses: ["standing", "running", "crouch"], walkSpeed: 2.4 },
190
- },
191
- };
192
- const game = defineGame({
193
- name: "survival-demo",
194
- assets: createAssetCatalog(),
195
- multiplayer: null,
196
- inventories: { [HOTBAR]: { slots: 4, hud: "hotbar" } },
197
- time: { start: 6 * 3600, dayLength: DAY_LENGTH, speeds: [1, 2, 3, 4] },
198
- input: {
199
- moveForward: ["KeyW"],
200
- moveBack: ["KeyS"],
201
- moveLeft: ["KeyA"],
202
- moveRight: ["KeyD"],
203
- jump: ["Space"],
204
- sprint: ["Shift"],
205
- slot1: ["Digit1"],
206
- slot2: ["Digit2"],
207
- },
208
- });
209
- function onInit(ctx) {
210
- sim = createSurvivalSim();
211
- sim.bootstrap();
212
- ctx.item.use.register({
213
- treatWound: {
214
- apply(state, input) {
215
- sim.treat(input.itemId);
216
- return { state };
217
- },
218
- },
219
- });
220
- }
221
- function onNewPlayer(ctx) {
222
- ctx.scene.entity.spawn(HERO, { id: ctx.player.userId, position: [0, 0, 0], role: "player" });
223
- ctx.player.inventory.put(HOTBAR, "bandage", 3);
224
- ctx.player.inventory.put(HOTBAR, "splint", 1);
225
- }
226
- function onTick(ctx, dt) {
227
- sim.tick(ctx, dt, ctx.time.now());
228
- }
229
- const survivalWorld = environment({
230
- terrain: terrain({ bounds: { w: 160, d: 160 }, height: 3, frequency: 0.03, seed: "survival" }),
231
- weather: rain({ area: { w: 160, d: 120, h: 60 }, density: 0.5, wind: [1.2, 0.3] }),
232
- });
233
- const survivalField = resolveTerrainField(survivalWorld.terrain);
234
- function SurvivalWorld() {
235
- return (_jsxs(_Fragment, { children: [_jsx(EnvironmentScene, { feature: survivalWorld }), _jsx(FireSpreadLayer, { grid: sim.fire, cellSize: FIRE_CELL, origin: FIRE_ORIGIN, heightAt: survivalField.sampleHeight })] }));
236
- }
237
- const SEVERITY_STYLE = {
238
- good: "border-emerald-400/70 bg-emerald-500/15 text-emerald-200",
239
- neutral: "border-sky-400/60 bg-sky-500/10 text-sky-200",
240
- warning: "border-amber-400/70 bg-amber-500/15 text-amber-200",
241
- critical: "border-rose-500/80 bg-rose-600/20 text-rose-200",
242
- };
243
- function MoodleChip({ moodle }) {
244
- return (_jsxs("div", { className: `flex min-w-[86px] items-center gap-2 rounded-md border px-2 py-1.5 shadow-lg shadow-black/40 backdrop-blur-sm ${SEVERITY_STYLE[moodle.severity]}`, children: [_jsxs("div", { className: "relative flex h-9 w-9 shrink-0 items-center justify-center rounded bg-black/40 text-base font-bold", children: [moodle.icon ?? moodle.label.slice(0, 1), moodle.stacks > 1 ? (_jsx("span", { className: "absolute -bottom-1 -right-1 rounded-full bg-black/80 px-1 text-[10px] font-bold text-white", children: moodle.stacks })) : null] }), _jsxs("div", { className: "leading-tight", children: [_jsx("p", { className: "text-xs font-semibold", children: moodle.label }), moodle.note !== undefined ? _jsx("p", { className: "text-[10px] opacity-80", children: moodle.note }) : null, moodle.fraction !== undefined ? (_jsx("div", { className: "mt-1 h-1 w-16 overflow-hidden rounded bg-black/50", children: _jsx("div", { className: "h-full bg-current", style: { width: `${Math.round(moodle.fraction * 100)}%` } }) })) : null] })] }));
245
- }
246
- function MoodleStackPanel() {
247
- const state = useEngineState(sim.store);
248
- return (_jsx("div", { className: "flex max-w-[220px] flex-wrap gap-1.5", children: state.moodles.map((moodle) => (_jsx(MoodleChip, { moodle: moodle }, moodle.id))) }));
249
- }
250
- const METER_STYLE = {
251
- hunger: { label: "Hunger", fill: "bg-orange-400" },
252
- thirst: { label: "Thirst", fill: "bg-sky-400" },
253
- warmth: { label: "Warmth", fill: "bg-rose-400" },
254
- stamina: { label: "Stamina", fill: "bg-lime-400" },
255
- };
256
- function MetersPanel() {
257
- const state = useEngineState(sim.store);
258
- return (_jsxs("div", { className: "w-60 rounded-lg border border-white/15 bg-black/70 p-3 shadow-xl shadow-black/50", children: [_jsx("p", { className: "mb-2 text-[11px] font-bold uppercase tracking-widest text-white/60", children: "Condition" }), _jsx("div", { className: "space-y-2", children: Object.entries(METER_STYLE).map(([id, style]) => {
259
- const meter = state.meters[id];
260
- if (meter === undefined)
261
- return null;
262
- return (_jsxs("div", { children: [_jsxs("div", { className: "mb-0.5 flex justify-between text-[11px] text-white/70", children: [_jsx("span", { children: style.label }), _jsx("span", { className: "tabular-nums", children: Math.round(meter.value) })] }), _jsx("div", { className: "h-2.5 overflow-hidden rounded bg-white/10", children: _jsx("div", { className: `h-full rounded ${style.fill}`, style: { width: `${Math.round(meter.fraction * 100)}%` } }) })] }, id));
263
- }) })] }));
264
- }
265
- function RegionHealthPanel() {
266
- const state = useEngineState(sim.store);
267
- const barColor = (fraction) => fraction > 0.6 ? "bg-emerald-400" : fraction > 0.3 ? "bg-amber-400" : "bg-rose-500";
268
- return (_jsxs("div", { className: "w-56 rounded-lg border border-white/15 bg-black/70 p-3 shadow-xl shadow-black/50", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsx("p", { className: "text-[11px] font-bold uppercase tracking-widest text-white/60", children: "Body" }), _jsxs("span", { className: "text-[11px] tabular-nums text-white/70", children: [Math.round(state.overall * 100), "%"] })] }), _jsx("div", { className: "space-y-1.5", children: state.regions.map((region) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: `w-14 shrink-0 text-[11px] ${region.vital ? "text-white" : "text-white/70"}`, children: region.label }), _jsx("div", { className: "h-2 flex-1 overflow-hidden rounded bg-white/10", children: _jsx("div", { className: `h-full rounded ${barColor(region.fraction)}`, style: { width: `${Math.round(region.fraction * 100)}%` } }) }), _jsx("span", { className: "w-8 shrink-0 text-right text-[10px] tabular-nums text-white/60", children: Math.round(region.current) })] }, region.id))) })] }));
269
- }
270
- function EnvironmentReadout() {
271
- const state = useEngineState(sim.store);
272
- return (_jsxs("div", { className: "rounded-lg border border-white/15 bg-black/70 px-3 py-2 text-right shadow-xl shadow-black/50", children: [_jsx("p", { className: "text-[11px] font-bold uppercase tracking-widest text-white/60", children: "Weather" }), _jsxs("p", { className: "mt-1 text-sm font-semibold capitalize text-sky-200", children: [state.weather.kind, " \u00B7 ", Math.round(state.weather.intensity * 100), "%"] }), _jsxs("div", { className: "mt-1 space-y-0.5 text-[11px] tabular-nums text-white/70", children: [_jsxs("p", { children: ["Temp ", state.temperature.toFixed(1), "\u00B0"] }), _jsxs("p", { children: ["Wet ", Math.round(state.wetness * 100), "%"] }), _jsxs("p", { children: ["Grip ", Math.round(state.grip * 100), "% \u00B7 Vis ", Math.round(state.visibility * 100), "%"] }), state.burning > 0 ? _jsxs("p", { className: "text-orange-300", children: [state.burning, " cells ablaze"] }) : null] })] }));
273
- }
274
- function SurvivalGameUI() {
275
- return (_jsxs("div", { className: "pointer-events-none absolute inset-0 font-sans text-white", children: [_jsx("div", { className: "absolute left-4 top-4", children: _jsx(MoodleStackPanel, {}) }), _jsx("div", { className: "absolute right-4 top-4", children: _jsx(EnvironmentReadout, {}) }), _jsx("div", { className: "absolute bottom-4 left-4", children: _jsx(MetersPanel, {}) }), _jsx("div", { className: "absolute bottom-4 right-4", children: _jsx(RegionHealthPanel, {}) })] }));
276
- }
277
- const itemCatalog = {
278
- bandage: { use: "treatWound" },
279
- splint: { use: "treatWound" },
280
- };
281
- export const survivalDemoGame = {
282
- game,
283
- content: {
284
- entityById: (catalogId) => entityCatalog[catalogId] ?? null,
285
- itemById: (itemId) => (itemId in itemCatalog ? itemCatalog[itemId] : null),
286
- },
287
- loop: { onInit, onNewPlayer, onTick },
288
- GameUI: SurvivalGameUI,
289
- environment: SurvivalWorld,
290
- camera: { minDistance: 8, maxDistance: 40, initialDistance: 26, targetHeight: 1.4 },
291
- };