@jgengine/shell 0.6.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 (118) hide show
  1. package/CHANGELOG.md +214 -2
  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 +641 -59
  9. package/dist/audio/AudioComponents.d.ts +12 -0
  10. package/dist/audio/AudioComponents.js +53 -0
  11. package/dist/audio/audioEngine.d.ts +23 -0
  12. package/dist/audio/audioEngine.js +110 -0
  13. package/dist/behaviour.d.ts +31 -0
  14. package/dist/behaviour.js +63 -0
  15. package/dist/camera/GameCameraRig.d.ts +16 -0
  16. package/dist/camera/GameCameraRig.js +56 -0
  17. package/dist/camera/GameFirstPersonCamera.js +2 -0
  18. package/dist/camera/GameInspectionCamera.d.ts +6 -0
  19. package/dist/camera/GameInspectionCamera.js +24 -0
  20. package/dist/camera/GameOrbitCamera.d.ts +3 -1
  21. package/dist/camera/GameOrbitCamera.js +4 -2
  22. package/dist/camera/cameraRigs.d.ts +26 -0
  23. package/dist/camera/cameraRigs.js +522 -0
  24. package/dist/camera/index.d.ts +7 -0
  25. package/dist/camera/index.js +7 -0
  26. package/dist/camera/inspectionCameraMath.d.ts +25 -0
  27. package/dist/camera/inspectionCameraMath.js +44 -0
  28. package/dist/camera/rigMath.d.ts +169 -0
  29. package/dist/camera/rigMath.js +393 -0
  30. package/dist/camera/rigResolve.d.ts +15 -0
  31. package/dist/camera/rigResolve.js +52 -0
  32. package/dist/camera/shakeChannel.d.ts +29 -0
  33. package/dist/camera/shakeChannel.js +38 -0
  34. package/dist/defineGame.d.ts +16 -0
  35. package/dist/defineGame.js +51 -0
  36. package/dist/devtools/DevtoolsOverlay.d.ts +14 -0
  37. package/dist/devtools/DevtoolsOverlay.js +334 -0
  38. package/dist/environment/Daylight.d.ts +50 -0
  39. package/dist/environment/Daylight.js +100 -0
  40. package/dist/environment/EnvironmentScene.js +18 -26
  41. package/dist/environment/GroundPad.d.ts +7 -0
  42. package/dist/environment/GroundPad.js +12 -0
  43. package/dist/environment/daylightCycle.d.ts +26 -0
  44. package/dist/environment/daylightCycle.js +116 -0
  45. package/dist/environment/groundPadMath.d.ts +13 -0
  46. package/dist/environment/groundPadMath.js +10 -0
  47. package/dist/environment/index.d.ts +2 -0
  48. package/dist/environment/index.js +2 -0
  49. package/dist/map/MapMarkerBeacons.d.ts +12 -0
  50. package/dist/map/MapMarkerBeacons.js +16 -0
  51. package/dist/map/index.d.ts +2 -0
  52. package/dist/map/index.js +2 -0
  53. package/dist/map/terrainMap.d.ts +23 -0
  54. package/dist/map/terrainMap.js +79 -0
  55. package/dist/materialOverride.d.ts +8 -0
  56. package/dist/materialOverride.js +32 -0
  57. package/dist/multiplayer.d.ts +16 -9
  58. package/dist/multiplayer.js +62 -12
  59. package/dist/pointer/PointerOverlays.d.ts +12 -0
  60. package/dist/pointer/PointerOverlays.js +17 -0
  61. package/dist/pointer/PointerProbe.d.ts +4 -0
  62. package/dist/pointer/PointerProbe.js +40 -0
  63. package/dist/pointer/pointerService.d.ts +26 -0
  64. package/dist/pointer/pointerService.js +89 -0
  65. package/dist/registry.d.ts +4 -1
  66. package/dist/registry.js +3 -1
  67. package/dist/render/modelRender.d.ts +14 -0
  68. package/dist/render/modelRender.js +79 -0
  69. package/dist/replay/useSessionRecorder.d.ts +14 -0
  70. package/dist/replay/useSessionRecorder.js +22 -0
  71. package/dist/structures/PlacementGhost.d.ts +8 -0
  72. package/dist/structures/PlacementGhost.js +11 -0
  73. package/dist/structures/index.d.ts +2 -0
  74. package/dist/structures/index.js +2 -0
  75. package/dist/structures.d.ts +1 -0
  76. package/dist/structures.js +1 -0
  77. package/dist/terrain/CarvedTerrain.d.ts +22 -0
  78. package/dist/terrain/CarvedTerrain.js +19 -0
  79. package/dist/terrain/EditableGround.d.ts +11 -0
  80. package/dist/terrain/EditableGround.js +38 -0
  81. package/dist/terrain/TerraformBrushCursor.d.ts +8 -0
  82. package/dist/terrain/TerraformBrushCursor.js +13 -0
  83. package/dist/terrain/index.d.ts +4 -1
  84. package/dist/terrain/index.js +4 -1
  85. package/dist/terrain/terrainMath.d.ts +15 -1
  86. package/dist/terrain/terrainMath.js +87 -1
  87. package/dist/touch/TouchControlsOverlay.d.ts +18 -0
  88. package/dist/touch/TouchControlsOverlay.js +173 -0
  89. package/dist/vision/FrustumSensorHud.d.ts +21 -0
  90. package/dist/vision/FrustumSensorHud.js +67 -0
  91. package/dist/vision/HiddenStateProbeHud.d.ts +11 -0
  92. package/dist/vision/HiddenStateProbeHud.js +11 -0
  93. package/dist/vision/RevealVision.d.ts +28 -0
  94. package/dist/vision/RevealVision.js +53 -0
  95. package/dist/water/OceanShader.d.ts +1 -1
  96. package/dist/water/OceanShader.js +3 -3
  97. package/dist/weather/FireSpreadLayer.d.ts +14 -0
  98. package/dist/weather/FireSpreadLayer.js +68 -0
  99. package/dist/weather/index.d.ts +1 -0
  100. package/dist/weather/index.js +1 -0
  101. package/dist/world/DataObjects.d.ts +44 -0
  102. package/dist/world/DataObjects.js +75 -0
  103. package/dist/world/GridWorldScene.d.ts +10 -0
  104. package/dist/world/GridWorldScene.js +54 -0
  105. package/dist/world/InstancedJoints.d.ts +14 -0
  106. package/dist/world/InstancedJoints.js +32 -0
  107. package/dist/world/WorldHud.d.ts +7 -1
  108. package/dist/world/WorldHud.js +76 -14
  109. package/dist/world/WorldItems.d.ts +5 -0
  110. package/dist/world/WorldItems.js +50 -0
  111. package/dist/world/floatTextStyle.d.ts +14 -0
  112. package/dist/world/floatTextStyle.js +39 -0
  113. package/llms.txt +2382 -0
  114. package/package.json +10 -5
  115. package/dist/demo/demoGame.d.ts +0 -2
  116. package/dist/demo/demoGame.js +0 -208
  117. package/dist/demo/environmentShowcase.d.ts +0 -2
  118. package/dist/demo/environmentShowcase.js +0 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jgengine/shell",
3
- "version": "0.6.0",
3
+ "version": "0.8.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
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
@@ -12,13 +12,18 @@
12
12
  },
13
13
  "files": [
14
14
  "dist",
15
- "CHANGELOG.md"
15
+ "CHANGELOG.md",
16
+ "llms.txt"
16
17
  ],
17
18
  "exports": {
18
19
  "./weather": {
19
20
  "types": "./dist/weather/index.d.ts",
20
21
  "default": "./dist/weather/index.js"
21
22
  },
23
+ "./camera": {
24
+ "types": "./dist/camera/index.d.ts",
25
+ "default": "./dist/camera/index.js"
26
+ },
22
27
  "./*": {
23
28
  "types": "./dist/*.d.ts",
24
29
  "default": "./dist/*.js"
@@ -30,9 +35,9 @@
30
35
  "test": "bun test src"
31
36
  },
32
37
  "dependencies": {
33
- "@jgengine/core": "^0.6.0",
34
- "@jgengine/react": "^0.6.0",
35
- "@jgengine/ws": "^0.6.0"
38
+ "@jgengine/core": "^0.8.0",
39
+ "@jgengine/react": "^0.8.0",
40
+ "@jgengine/ws": "^0.8.0"
36
41
  },
37
42
  "peerDependencies": {
38
43
  "@react-three/drei": "^10.0.0",
@@ -1,2 +0,0 @@
1
- import type { PlayableGame } from "../registry.js";
2
- export declare const demoGame: PlayableGame;
@@ -1,208 +0,0 @@
1
- import { 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 { CurrencyPill, HealthBar, SlotGrid } from "@jgengine/react/components";
5
- import { useFeed, useGameClock, useGameStore, usePlayer, useTarget } from "@jgengine/react/hooks";
6
- const HERO = "hero";
7
- const MOB = "gloomSlime";
8
- const LOOT_TABLE = "slime-drops";
9
- const GOLD = "gold";
10
- const HOTBAR = "hotbar";
11
- const MELEE_DAMAGE = 6;
12
- const MELEE_RANGE = 1.9;
13
- const MELEE_COOLDOWN = 1.2;
14
- const AGGRO_RADIUS = 11;
15
- const MOB_SPAWNS = [
16
- [7, 0, -6],
17
- [-8, 0, 5],
18
- [6, 0, 8],
19
- ];
20
- const entityCatalog = {
21
- [HERO]: {
22
- stats: { health: { max: 100 }, mana: { max: 60 } },
23
- receive: { damage: { order: ["health"] } },
24
- movement: { poses: ["standing", "running", "crouch"], walkSpeed: 2.4 },
25
- },
26
- [MOB]: {
27
- stats: { health: { max: 60 } },
28
- receive: { damage: { order: ["health"] } },
29
- onDeath: { drops: LOOT_TABLE },
30
- role: "enemy",
31
- },
32
- };
33
- const itemCatalog = {
34
- sword: { use: "swingSword", weapon: { damage: 22 } },
35
- zap: { use: "castZap", weapon: { damage: 35, manaCost: 12, range: 18 } },
36
- };
37
- const game = defineGame({
38
- name: "gameplayer-demo",
39
- assets: createAssetCatalog(),
40
- multiplayer: null,
41
- inventories: { [HOTBAR]: { slots: 4, hud: "hotbar" } },
42
- time: { start: 8 * 3600, speeds: [1, 2, 3, 4] },
43
- input: {
44
- moveForward: ["KeyW"],
45
- moveBack: ["KeyS"],
46
- moveLeft: ["KeyA"],
47
- moveRight: ["KeyD"],
48
- jump: ["Space"],
49
- sprint: ["Shift"],
50
- turnLeft: ["KeyQ"],
51
- turnRight: ["KeyE"],
52
- tabTarget: ["Tab"],
53
- clearTarget: ["Escape"],
54
- slot1: ["Digit1"],
55
- slot2: ["Digit2"],
56
- },
57
- });
58
- let elapsed = 0;
59
- let nextSpawnIndex = 0;
60
- const meleeReadyAt = new Map();
61
- function spawnMob(ctx) {
62
- const at = MOB_SPAWNS[nextSpawnIndex % MOB_SPAWNS.length];
63
- nextSpawnIndex += 1;
64
- ctx.scene.entity.spawn(MOB, { position: at, role: "npc" });
65
- }
66
- function spawnHero(ctx) {
67
- ctx.scene.entity.spawn(HERO, {
68
- id: ctx.player.userId,
69
- position: [0, 0, 0],
70
- role: "player",
71
- });
72
- }
73
- function onInit(ctx) {
74
- elapsed = 0;
75
- nextSpawnIndex = 0;
76
- meleeReadyAt.clear();
77
- ctx.game.loot.register({ id: LOOT_TABLE, entries: [{ currency: GOLD, count: [2, 5], weight: 1 }] });
78
- ctx.game.feed.bind("entity.died");
79
- ctx.game.events.on("entity.died", (event) => {
80
- if (event.catalogId === MOB) {
81
- meleeReadyAt.delete(event.instanceId);
82
- spawnMob(ctx);
83
- }
84
- });
85
- ctx.item.use.register({
86
- swingSword: {
87
- apply(state, input) {
88
- const hits = state.scene.entity
89
- .queryArc({ from: input.from, aim: input.aim ?? { yaw: 0, pitch: 0 }, radius: 3.2, halfAngleDeg: 70 })
90
- .filter((id) => state.scene.entity.get(id)?.name === MOB);
91
- for (const id of hits) {
92
- state.scene.entity.effect({ from: input.from, to: id, effect: "damage", via: { item: input.itemId } });
93
- }
94
- return { state };
95
- },
96
- },
97
- castZap: {
98
- can(state, input) {
99
- const targetId = state.scene.entity.getTarget(input.from);
100
- if (targetId === null)
101
- return { reason: "no-target" };
102
- const range = state.item.weapon.getStat(input.itemId, "range") ?? Number.POSITIVE_INFINITY;
103
- const distance = state.scene.entity.distance(input.from, targetId);
104
- if (distance === null || distance > range)
105
- return { reason: "out-of-range" };
106
- const cost = state.item.weapon.getStat(input.itemId, "manaCost") ?? 0;
107
- const mana = state.scene.entity.stats.get(input.from, "mana");
108
- if (mana === null || mana.current < cost)
109
- return { reason: "no-mana" };
110
- return null;
111
- },
112
- apply(state, input) {
113
- const targetId = state.scene.entity.getTarget(input.from);
114
- if (targetId === null)
115
- return { state, error: "no-target" };
116
- const cost = state.item.weapon.getStat(input.itemId, "manaCost") ?? 0;
117
- state.scene.entity.stats.delta(input.from, "mana", -cost);
118
- state.scene.entity.effect({ from: input.from, to: targetId, effect: "damage", via: { item: input.itemId } });
119
- return { state };
120
- },
121
- },
122
- });
123
- }
124
- function onNewPlayer(ctx) {
125
- spawnHero(ctx);
126
- ctx.player.inventory.put(HOTBAR, "sword", 1);
127
- ctx.player.inventory.put(HOTBAR, "zap", 1);
128
- ctx.game.economy.grant(ctx.player.userId, GOLD, 10);
129
- for (let index = 0; index < MOB_SPAWNS.length; index += 1)
130
- spawnMob(ctx);
131
- }
132
- function onTick(ctx, dt) {
133
- elapsed += dt;
134
- const playerId = ctx.player.userId;
135
- const player = ctx.scene.entity.get(playerId);
136
- if (player === null) {
137
- spawnHero(ctx);
138
- return;
139
- }
140
- const mana = ctx.scene.entity.stats.get(playerId, "mana");
141
- if (mana !== null && mana.current < mana.max)
142
- ctx.scene.entity.stats.delta(playerId, "mana", 5 * dt);
143
- for (const mob of ctx.scene.entity.list()) {
144
- if (mob.name !== MOB)
145
- continue;
146
- const distance = ctx.scene.entity.distance(mob.id, playerId);
147
- if (distance === null || distance > AGGRO_RADIUS)
148
- continue;
149
- if (distance > MELEE_RANGE) {
150
- const next = ctx.scene.entity.moveToward(mob.id, playerId, {
151
- speed: 2,
152
- stopDistance: MELEE_RANGE * 0.8,
153
- dt,
154
- });
155
- if (next !== null) {
156
- ctx.scene.entity.setPose(mob.id, {
157
- position: next,
158
- rotationY: Math.atan2(player.position[0] - next[0], player.position[2] - next[2]),
159
- });
160
- }
161
- }
162
- else if ((meleeReadyAt.get(mob.id) ?? 0) <= elapsed) {
163
- meleeReadyAt.set(mob.id, elapsed + MELEE_COOLDOWN);
164
- ctx.scene.entity.effect({ from: mob.id, to: playerId, effect: "damage", via: { amount: MELEE_DAMAGE } });
165
- }
166
- }
167
- }
168
- function VitalsPanel({ userId }) {
169
- return (_jsxs("div", { className: "rounded bg-black/60 p-2", children: [_jsx(HealthBar, { instanceId: userId, statId: "health", className: "h-2.5 overflow-hidden rounded bg-white/15", fillClassName: "bg-emerald-400" }), _jsx(HealthBar, { instanceId: userId, statId: "mana", className: "mt-1 h-1.5 overflow-hidden rounded bg-white/15", fillClassName: "bg-sky-400" })] }));
170
- }
171
- function TargetPanel({ userId }) {
172
- const targetId = useTarget(userId);
173
- const targetName = useGameStore((ctx) => targetId === null ? null : (ctx.scene.entity.get(targetId)?.name ?? null));
174
- if (targetId === null || targetName === null) {
175
- return _jsx("p", { className: "rounded bg-black/50 px-2 py-1 text-center text-xs text-white/50", children: "Tab: target a mob" });
176
- }
177
- return (_jsxs("div", { className: "rounded bg-black/60 p-2", children: [_jsx("p", { className: "text-xs uppercase tracking-wide text-red-300", children: targetName }), _jsx(HealthBar, { instanceId: targetId, statId: "health", className: "mt-1 h-2 overflow-hidden rounded bg-white/15", fillClassName: "bg-red-400" })] }));
178
- }
179
- function KillFeedPanel() {
180
- const kills = useFeed({ action: "entity.died", limit: 5 });
181
- return (_jsx("ul", { className: "space-y-0.5 text-right text-xs text-white/70", children: kills
182
- .slice()
183
- .reverse()
184
- .map((entry, index) => (_jsxs("li", { children: [entry.data.catalogId ?? "?", " down"] }, `${entry.at}-${index}`))) }));
185
- }
186
- function HotbarPanel() {
187
- return (_jsx(SlotGrid, { inventoryId: HOTBAR, className: "flex gap-1.5", renderSlot: (slot, index) => (_jsxs("div", { className: "w-16 rounded border border-white/20 bg-black/60 px-1.5 py-1 text-center text-[11px]", children: [_jsx("span", { className: "text-white/50", children: index + 1 }), _jsx("p", { className: "truncate text-white", children: slot === null ? "—" : slot.itemId })] })) }));
188
- }
189
- function ClockPanel() {
190
- const { paused, playSpeed, speeds, calendar, controls } = useGameClock();
191
- const pad = (value) => value.toString().padStart(2, "0");
192
- return (_jsxs("div", { className: "pointer-events-auto flex items-center gap-2 rounded bg-black/60 px-2 py-1 text-xs", children: [_jsxs("span", { className: "tabular-nums text-white", children: ["Day ", calendar.day + 1, " \u00B7 ", pad(calendar.hour), ":", pad(calendar.minute)] }), _jsx("button", { onClick: () => controls.toggle(), className: "rounded border border-white/25 px-1.5 py-0.5 text-white/80 hover:bg-white/10", children: paused ? "▶" : "⏸" }), _jsx("div", { className: "flex gap-0.5", children: speeds.map((speed) => (_jsxs("button", { onClick: () => controls.setSpeed(speed), className: `rounded border px-1.5 py-0.5 ${!paused && speed === playSpeed
193
- ? "border-amber-300 bg-amber-300/20 text-amber-200"
194
- : "border-white/25 text-white/70 hover:bg-white/10"}`, children: [speed, "\u00D7"] }, speed))) })] }));
195
- }
196
- function DemoGameUI() {
197
- const player = usePlayer();
198
- return (_jsxs("div", { className: "pointer-events-none absolute inset-0 font-mono text-white", children: [_jsx("div", { className: "absolute left-4 top-4", children: _jsx(ClockPanel, {}) }), _jsx("div", { className: "absolute left-1/2 top-4 w-56 -translate-x-1/2", children: _jsx(TargetPanel, { userId: player.userId }) }), _jsxs("div", { className: "absolute right-4 top-4 flex flex-col items-end gap-2", children: [_jsx(CurrencyPill, { currencyId: GOLD, className: "rounded bg-black/60 px-2 py-1 text-sm text-amber-300" }), _jsx(KillFeedPanel, {})] }), _jsx("div", { className: "absolute bottom-4 left-4 w-64", children: _jsx(VitalsPanel, { userId: player.userId }) }), _jsx("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2", children: _jsx(HotbarPanel, {}) }), _jsx("div", { className: "absolute bottom-4 right-4 max-w-[220px] text-right text-[11px] leading-4 text-white/40", children: "WASD move \u00B7 Space jump \u00B7 Shift sprint \u00B7 Q/E turn \u00B7 Tab target \u00B7 Esc clear \u00B7 1 sword \u00B7 2 zap" })] }));
199
- }
200
- export const demoGame = {
201
- game,
202
- content: {
203
- itemById: (itemId) => itemCatalog[itemId] ?? null,
204
- entityById: (catalogId) => entityCatalog[catalogId] ?? null,
205
- },
206
- loop: { onInit, onNewPlayer, onTick },
207
- GameUI: DemoGameUI,
208
- };
@@ -1,2 +0,0 @@
1
- import type { PlayableGame } from "../registry.js";
2
- export declare const environmentShowcaseGame: PlayableGame;
@@ -1,15 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { building, environment, grass, ocean, rain, terrain, } from "@jgengine/core/world/features";
3
- import { EnvironmentScene } from "../environment/EnvironmentScene.js";
4
- import { demoGame } from "./demoGame.js";
5
- const showcase = environment({
6
- terrain: terrain({ bounds: { w: 220, d: 220 }, height: 4, frequency: 0.035, seed: "showcase", waterLevel: -1 }),
7
- vegetation: grass({ area: { w: 120, d: 120 }, density: 5, colors: ["#31531f", "#8fbf4a"], seed: "showcase" }),
8
- water: ocean({ bounds: { w: 520, d: 520 }, level: -1.4, waveHeight: 0.9, waveSpeed: 0.5, color: "#1f6f92" }),
9
- weather: rain({ area: { w: 160, d: 160, h: 70 }, density: 0.5, wind: [1.4, 0.4] }),
10
- structures: building({ count: 6, footprint: { w: 12, d: 9 }, stories: [3, 7], storyHeight: 2.8, spacing: 6, seed: "showcase" }),
11
- });
12
- export const environmentShowcaseGame = {
13
- ...demoGame,
14
- environment: () => _jsx(EnvironmentScene, { feature: showcase }),
15
- };