@jgengine/convex 0.45.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +809 -28
- package/README.md +1 -1
- package/dist/convexChatTransport.d.ts +2 -2
- package/dist/createConvexGameTransport.d.ts +3 -3
- package/dist/server.d.ts +6 -10
- package/dist/server.js +3 -5
- package/package.json +2 -2
- package/skills/jgengine/SKILL.md +2 -2
- package/skills/jgengine/api.md +158 -56
- package/skills/jgengine/capabilities.md +15 -3
- package/skills/jgengine/recipes/minimal-game.md +2 -2
- package/skills/jgengine-multiplayer/SKILL.md +1 -1
- package/skills/jgengine-multiplayer/api.md +79 -46
- package/skills/jgengine-multiplayer/capabilities.md +12 -0
- package/skills/jgengine-multiplayer/reference.md +12 -12
- package/skills/jgengine-multiplayer/references/peer-worlds.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -26,6 +26,787 @@ At publish, rename this heading to the new version and mirror the entries into
|
|
|
26
26
|
`packages/core/src/meta/changelog.ts` (the typed `CHANGELOG` export).
|
|
27
27
|
-->
|
|
28
28
|
|
|
29
|
+
## 0.46.0
|
|
30
|
+
|
|
31
|
+
### Migrate
|
|
32
|
+
|
|
33
|
+
- **Bump lockstep SDK packages to `^0.46.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.43.0`; `@jgengine/github` is `0.33.0`.
|
|
34
|
+
- `KitActionBar` (`@jgengine/react/hud/kitHud`) is gone. Use `ActionBar` from `@jgengine/react/hud/actionHud` with `look="kit"`: `actions` → `defs` (`ActionDef`s), `glyph` → `hotkey`, `variant: "primary"`/`pressed` → `active`, `variant: "ghost"` → `disabled`, `title` → `description`, each `onActivate` → one `onActivate(id)`, `kind` dropped (buttons carry `data-action-id`). `progress`, `trailing`, `prompt` and `wrapAt` keep their meaning. Pass `hotkeys={false}` when the game's own bindings fire the actions. `KitActionProgress` → `ActionProgress`.
|
|
35
|
+
- `hudBlockOverlaps`, `formatHudOverlapReport`, `HudBlockOverlap`, `HudBlockBounds` and `HudBlockOverlapOptions` (`@jgengine/react/hud/hudLanes`) are gone. Use `detectLayoutCollisions(blocks.map((b) => hudBlockRegion(b.id, b.rect)), { minArea: 1 })` and `formatLayoutCollisions(collisions, viewport)` from `@jgengine/core/hud/hudPriority`.
|
|
36
|
+
- `generateCity`, `resolveCityLotContent`, `GeneratedCity`, `CityPlot`, `ResolvedCityLot` and the rest of `@jgengine/core/city/cityGenerator` are gone. Use `resolveCityObject({ id, kind: "city", center, halfExtents, meta })`: street dials go in `meta` under their city-rule names (`segmentLength` → `blockSize`, `aspect` → `blockAspect`, `winding` → `curviness`, `width` → `streetWidth`); lot setback → `buildingRoadSetback`, spacing → `buildingSpacing`, `blockFill` → `roadsideOccupancy`, footprint → `lotScale`, `content.landmarks` → `landmarks`. The street graph is on `ResolvedCity.network`.
|
|
37
|
+
- `extractBlocks`, `FabricStreet`, `FabricParams` and `BlockRings` (`@jgengine/core/city/cityBlocks`) are gone. Use `extractGraphBlocks(network.nodes, network.edges, { sidewalkBase, curbMargin })`.
|
|
38
|
+
- `CityFillerClass` and `CITY_FILLER_CLASSES` are gone (nothing placed garages or depots any more).
|
|
39
|
+
- More features are now one folder in `@jgengine/core` (model `name.ts`, view `name.view.tsx`, draw `name.draw.tsx`). The `@jgengine/react` and `@jgengine/shell` root barrels still export the same names. Update deep imports:
|
|
40
|
+
- Menu: `@jgengine/react/menu/<file>` → `@jgengine/core/menu/<file>.view` for `gameFlowMenus`, `gameMenu`, `gameMenuSaves`, `gameMenuSound`, `menuNav`, `menuRouter`, `menuScreen`, `pauseMenu`, `quickMenu`, `radialMenu`.
|
|
41
|
+
- Settings: `@jgengine/react/settings/<file>` → `@jgengine/core/settings/<file>.view` for `accessibilitySettings`, `keybindingMenu`, `settings`, `settingsBlocks`; `@jgengine/shell/settings/<file>` → `@jgengine/core/settings/<file>.draw` for `QuickControls`, `SettingsChrome`, `SettingsMenu`, `SettingsRuntime`, `appliedSettings`, `settingsController`.
|
|
42
|
+
- Dialog: `@jgengine/react/dialog/{dialogBehavior,modals}` → `@jgengine/core/dialog/{dialogBehavior,modals}.view`.
|
|
43
|
+
- Inventory: `@jgengine/react/inventory/{inventoryGrid,shopGrid,slots}` → `@jgengine/core/inventory/{inventoryGrid,shopGrid,slots}.view`.
|
|
44
|
+
- Notifications: `@jgengine/react/notifications/{notices,notificationCenter,toastStack}` → `@jgengine/core/notifications/<file>.view`; `@jgengine/shell/notifications/ShellNotices` → `@jgengine/core/notifications/ShellNotices.view`.
|
|
45
|
+
- Haptics: `@jgengine/shell/haptics/padHaptics` → `@jgengine/core/haptics/padHaptics`; `@jgengine/shell/haptics/hapticsRoute` → `@jgengine/core/haptics/hapticsRoute.view`.
|
|
46
|
+
- Camera shake: `@jgengine/react/camera/shake/cameraShake` → `@jgengine/core/camera/shake/cameraShake.view`; `@jgengine/shell/camera/shake/<file>` → `@jgengine/core/camera/shake/<file>.draw` for `CameraShakeApplier`, `HitReactionShake`, `shakeChannel`.
|
|
47
|
+
- Combat: `@jgengine/shell/combat/ProjectileModels` → `@jgengine/core/combat/ProjectileModels.draw`.
|
|
48
|
+
- Quest tracker: `@jgengine/react/questTracker` → `@jgengine/core/mission/questTracker.view`.
|
|
49
|
+
- The remaining React-side features now live in `@jgengine/core` feature folders, model and view together (`name.ts` model, `name.view.tsx` view). Features that hid in `core/game/` got their own folders: `achievements/`, `codex/`, `dialogue/`, `chat/`, `social/`, `talents/`, `feed/`, `leaderboard/`, `cutscene/`. The `@jgengine/react` root barrel still exports the same names. Update deep imports:
|
|
50
|
+
- `@jgengine/core/game/achievements` → `@jgengine/core/achievements/achievements`
|
|
51
|
+
- `@jgengine/react/achievements` → `@jgengine/core/achievements/achievements.view`
|
|
52
|
+
- `@jgengine/react/tweenNumber` → `@jgengine/core/anim/tweenNumber.view`
|
|
53
|
+
- `@jgengine/react/cards` → `@jgengine/core/cards/cards.view`
|
|
54
|
+
- `@jgengine/core/game/chat` → `@jgengine/core/chat/chat`
|
|
55
|
+
- `@jgengine/react/chat` → `@jgengine/core/chat/chat.view`
|
|
56
|
+
- `@jgengine/react/chatBubbles` → `@jgengine/core/chat/chatBubbles.view`
|
|
57
|
+
- `@jgengine/core/game/chatFilter` → `@jgengine/core/chat/chatFilter`
|
|
58
|
+
- `@jgengine/react/standaloneChatPanel` → `@jgengine/core/chat/standaloneChatPanel.view`
|
|
59
|
+
- `@jgengine/core/game/codex` → `@jgengine/core/codex/codex`
|
|
60
|
+
- `@jgengine/react/codex` → `@jgengine/core/codex/codex.view`
|
|
61
|
+
- `@jgengine/core/vfx/damageDirection` → `@jgengine/core/combat/damage/damageDirection`
|
|
62
|
+
- `@jgengine/react/damageDirection` → `@jgengine/core/combat/damage/damageDirection.view`
|
|
63
|
+
- `@jgengine/react/cutscene` → `@jgengine/core/cutscene/cutscene.view`
|
|
64
|
+
- `@jgengine/core/scene/sequenceDirector` → `@jgengine/core/cutscene/sequenceDirector`
|
|
65
|
+
- `@jgengine/core/game/dialogue` → `@jgengine/core/dialogue/dialogue`
|
|
66
|
+
- `@jgengine/core/game/dialogueGraph` → `@jgengine/core/dialogue/dialogueGraph`
|
|
67
|
+
- `@jgengine/react/dialogueView` → `@jgengine/core/dialogue/dialogueView.view`
|
|
68
|
+
- `@jgengine/core/game/eventTicker` → `@jgengine/core/feed/eventTicker`
|
|
69
|
+
- `@jgengine/core/game/feed` → `@jgengine/core/feed/feed`
|
|
70
|
+
- `@jgengine/react/killFeed` → `@jgengine/core/feed/killFeed.view`
|
|
71
|
+
- `@jgengine/react/entityFrames` → `@jgengine/core/hud/entityFrames.view`
|
|
72
|
+
- `@jgengine/core/ui/floatingText` → `@jgengine/core/hud/floatingText`
|
|
73
|
+
- `@jgengine/react/floatingText` → `@jgengine/core/hud/floatingText.view`
|
|
74
|
+
- `@jgengine/react/i18n` → `@jgengine/core/i18n/i18n.view`
|
|
75
|
+
- `@jgengine/react/inputDevice` → `@jgengine/core/input/inputDevice.view`
|
|
76
|
+
- `@jgengine/react/keyHint` → `@jgengine/core/input/keyHint.view`
|
|
77
|
+
- `@jgengine/react/interactionPrompt` → `@jgengine/core/interaction/interactionPrompt.view`
|
|
78
|
+
- `@jgengine/react/skillCheckPaint` → `@jgengine/core/interaction/skillCheckPaint.view`
|
|
79
|
+
- `@jgengine/react/characterSheet` → `@jgengine/core/inventory/characterSheet.view`
|
|
80
|
+
- `@jgengine/react/dragLayer` → `@jgengine/core/inventory/dragLayer.view`
|
|
81
|
+
- `@jgengine/core/game/leaderboard` → `@jgengine/core/leaderboard/leaderboard`
|
|
82
|
+
- `@jgengine/core/game/leaderboardRank` → `@jgengine/core/leaderboard/leaderboardRank`
|
|
83
|
+
- `@jgengine/react/scoreboard` → `@jgengine/core/leaderboard/scoreboard.view`
|
|
84
|
+
- `@jgengine/core/game/credits` → `@jgengine/core/menu/credits`
|
|
85
|
+
- `@jgengine/react/creditsPanel` → `@jgengine/core/menu/creditsPanel.view`
|
|
86
|
+
- `@jgengine/react/creditsScreen` → `@jgengine/core/menu/creditsScreen.view`
|
|
87
|
+
- `@jgengine/react/loadingScreen` → `@jgengine/core/menu/loadingScreen.view`
|
|
88
|
+
- `@jgengine/react/startScreen` → `@jgengine/core/menu/startScreen.view`
|
|
89
|
+
- `@jgengine/core/ui/objectiveBanner` → `@jgengine/core/mission/objectiveBanner`
|
|
90
|
+
- `@jgengine/react/objectiveBanner` → `@jgengine/core/mission/objectiveBanner.view`
|
|
91
|
+
- `@jgengine/core/game/objectives` → `@jgengine/core/mission/objectives`
|
|
92
|
+
- `@jgengine/react/identity` → `@jgengine/core/multiplayer/identity.view`
|
|
93
|
+
- `@jgengine/react/voice` → `@jgengine/core/multiplayer/voice.view`
|
|
94
|
+
- `@jgengine/react/browserLifecycle` → `@jgengine/core/runtime/browserLifecycle.view`
|
|
95
|
+
- `@jgengine/react/useServerSession` → `@jgengine/core/session/useServerSession.view`
|
|
96
|
+
- `@jgengine/core/game/social` → `@jgengine/core/social/social`
|
|
97
|
+
- `@jgengine/react/social` → `@jgengine/core/social/social.view`
|
|
98
|
+
- `@jgengine/react/talentTree` → `@jgengine/core/talents/talentTree.view`
|
|
99
|
+
- `@jgengine/core/game/talentTreeView` → `@jgengine/core/talents/talentTreeView`
|
|
100
|
+
- `@jgengine/core/game/talents` → `@jgengine/core/talents/talents`
|
|
101
|
+
- `@jgengine/react/timerReadout` → `@jgengine/core/time/timerReadout.view`
|
|
102
|
+
- `@jgengine/react/accessibility` → `@jgengine/core/ui/accessibility.view`
|
|
103
|
+
- `@jgengine/react/canvasCover` → `@jgengine/core/ui/canvasCover.view`
|
|
104
|
+
- `@jgengine/react/coachMarks` → `@jgengine/core/ui/coachMarks.view`
|
|
105
|
+
- `@jgengine/react/components` → `@jgengine/core/ui/components.view`
|
|
106
|
+
- `@jgengine/react/gameCursor` → `@jgengine/core/ui/cursor.view`
|
|
107
|
+
- `@jgengine/react/display` → `@jgengine/core/ui/display.view`
|
|
108
|
+
- `@jgengine/react/gameFonts` → `@jgengine/core/ui/fonts.view`
|
|
109
|
+
- `@jgengine/react/gameIcons` → `@jgengine/core/ui/icons/gameIcons.view`
|
|
110
|
+
- `@jgengine/react/iconTreatment` → `@jgengine/core/ui/icons/iconTreatment.view`
|
|
111
|
+
- `@jgengine/react/iconsPreview` → `@jgengine/core/ui/icons/iconsPreview.view`
|
|
112
|
+
- `@jgengine/react/kitFlow` → `@jgengine/core/ui/kit/kitFlow.view`
|
|
113
|
+
- `@jgengine/react/kitFrame` → `@jgengine/core/ui/kit/kitFrame.view`
|
|
114
|
+
- `@jgengine/react/kitFramesPreview` → `@jgengine/core/ui/kit/kitFramesPreview.view`
|
|
115
|
+
- `@jgengine/react/uiKit` → `@jgengine/core/ui/kit/uiKit.view`
|
|
116
|
+
- `@jgengine/react/uiKitArcade` → `@jgengine/core/ui/kit/uiKitArcade.view`
|
|
117
|
+
- `@jgengine/react/uiKitBroadcast` → `@jgengine/core/ui/kit/uiKitBroadcast.view`
|
|
118
|
+
- `@jgengine/react/uiKitHolo` → `@jgengine/core/ui/kit/uiKitHolo.view`
|
|
119
|
+
- `@jgengine/react/uiKitInk` → `@jgengine/core/ui/kit/uiKitInk.view`
|
|
120
|
+
- `@jgengine/react/uiKitPaper` → `@jgengine/core/ui/kit/uiKitPaper.view`
|
|
121
|
+
- `@jgengine/react/uiKitParchment` → `@jgengine/core/ui/kit/uiKitParchment.view`
|
|
122
|
+
- `@jgengine/react/uiKitPlain` → `@jgengine/core/ui/kit/uiKitPlain.view`
|
|
123
|
+
- `@jgengine/react/uiKitRoot` → `@jgengine/core/ui/kit/uiKitRoot.view`
|
|
124
|
+
- `@jgengine/react/uiKitSignal` → `@jgengine/core/ui/kit/uiKitSignal.view`
|
|
125
|
+
- `@jgengine/react/uiKitTerminal` → `@jgengine/core/ui/kit/uiKitTerminal.view`
|
|
126
|
+
- `@jgengine/react/uiKits` → `@jgengine/core/ui/kit/uiKits.view`
|
|
127
|
+
- `@jgengine/react/uiKitsPreview` → `@jgengine/core/ui/kit/uiKitsPreview.view`
|
|
128
|
+
- `@jgengine/react/liveBind` → `@jgengine/core/ui/liveBind.view`
|
|
129
|
+
- `@jgengine/react/panels` → `@jgengine/core/ui/panels.view`
|
|
130
|
+
- `@jgengine/react/photoMode` → `@jgengine/core/ui/photoMode.view`
|
|
131
|
+
- `@jgengine/react/presence` → `@jgengine/core/ui/presence.view`
|
|
132
|
+
- `@jgengine/react/rotateDevice` → `@jgengine/core/ui/rotateDevice.view`
|
|
133
|
+
- `@jgengine/react/scrollRail` → `@jgengine/core/ui/scrollRail.view`
|
|
134
|
+
- `@jgengine/react/scrollRailPreview` → `@jgengine/core/ui/scrollRailPreview.view`
|
|
135
|
+
- `@jgengine/react/gameSlider` → `@jgengine/core/ui/slider.view`
|
|
136
|
+
- `@jgengine/core/audio/uiCues` → `@jgengine/core/ui/uiCues`
|
|
137
|
+
- `@jgengine/react/uiCues` → `@jgengine/core/ui/uiCues.view`
|
|
138
|
+
- `@jgengine/react/useDebouncedCommit` → `@jgengine/core/ui/useDebouncedCommit.view`
|
|
139
|
+
- `@jgengine/react/gameViewport` → `@jgengine/core/ui/viewport.view`
|
|
140
|
+
- The shell-side features now live in `@jgengine/core` feature folders with their models (`name.ts` model, `name.view.tsx` React, `name.draw.tsx` three.js): `audio/`, `devtools/` (was `diagnostics/` too), `input/` (was `pointer/` and `touch/` too), `replay/` (with the recording buffer and replay loop from `sensor/`), `play/`, `placement/`, `buildings/`, plus the loose shell files for input, commands, multiplayer, movement, behaviour and combat presentation. Command running and dispatch moved from `runtime/` to `commands/`, movement speed from `scene/` to `movement/`, the chat transport contract from `multiplayer/` to `chat/`. Update deep imports:
|
|
141
|
+
- `@jgengine/shell/play` → `@jgengine/core/play/playRunner.draw` (`runPlayScript`) and `@jgengine/core/play/script` (`createPlayScript`, `PlayScriptSpec`)
|
|
142
|
+
- `@jgengine/shell/placement` → `@jgengine/core/placement/PlacementGhost.draw` and `@jgengine/core/placement/TransformGizmo.draw`
|
|
143
|
+
- `@jgengine/shell/buildings` → `@jgengine/core/buildings/GeneratedBuilding.draw`
|
|
144
|
+
- `cameraShake`, `defaultCameraShake`, `rigShakeConfig` and `CALIBRATED_TRAUMA_DECAY_PER_SECOND` → `@jgengine/core/camera/shake/cameraShake` (the shake model); `@jgengine/core/camera/shake/shakeChannel.draw` keeps the rig hooks and `applyCameraShake`
|
|
145
|
+
- `@jgengine/core/multiplayer/chatContract` → `@jgengine/core/chat/chatContract`
|
|
146
|
+
- `@jgengine/core/runtime/commandDispatch` → `@jgengine/core/commands/commandDispatch`
|
|
147
|
+
- `@jgengine/core/runtime/commandInput` → `@jgengine/core/commands/commandInput`
|
|
148
|
+
- `@jgengine/core/runtime/commandRunner` → `@jgengine/core/commands/commandRunner`
|
|
149
|
+
- `@jgengine/core/scene/movementSpeed` → `@jgengine/core/movement/movementSpeed`
|
|
150
|
+
- `@jgengine/core/sensor/recordingBuffer` → `@jgengine/core/replay/recordingBuffer`
|
|
151
|
+
- `@jgengine/core/sensor/replayLoop` → `@jgengine/core/replay/replayLoop`
|
|
152
|
+
- `@jgengine/shell/CombatPresentation` → `@jgengine/core/combat/CombatPresentation.draw`
|
|
153
|
+
- `@jgengine/shell/JoinGate` → `@jgengine/core/multiplayer/JoinGate.view`
|
|
154
|
+
- `@jgengine/shell/audio/AudioComponents` → `@jgengine/core/audio/AudioComponents.draw`
|
|
155
|
+
- `@jgengine/shell/audio/CaptionOverlay` → `@jgengine/core/audio/CaptionOverlay.view`
|
|
156
|
+
- `@jgengine/shell/audio/FeedbackHost` → `@jgengine/core/audio/FeedbackHost.view`
|
|
157
|
+
- `@jgengine/shell/audio/adaptiveMusicWire` → `@jgengine/core/audio/adaptiveMusicWire`
|
|
158
|
+
- `@jgengine/shell/audio/audioEngine` → `@jgengine/core/audio/audioEngine.view`
|
|
159
|
+
- `@jgengine/shell/audio/audioProbe` → `@jgengine/core/audio/audioProbe.view`
|
|
160
|
+
- `@jgengine/shell/audio/audioWire` → `@jgengine/core/audio/audioWire.view`
|
|
161
|
+
- `@jgengine/shell/audio/busMix` → `@jgengine/core/audio/busMix`
|
|
162
|
+
- `@jgengine/shell/audio/captions` → `@jgengine/core/audio/captions`
|
|
163
|
+
- `@jgengine/shell/audio/emitterTracking` → `@jgengine/core/audio/emitterTracking.view`
|
|
164
|
+
- `@jgengine/shell/audio/eventFeedback` → `@jgengine/core/audio/eventFeedback`
|
|
165
|
+
- `@jgengine/shell/audio/gestureResume` → `@jgengine/core/audio/gestureResume.view`
|
|
166
|
+
- `@jgengine/shell/audio/listenerAnchor` → `@jgengine/core/audio/listenerAnchor`
|
|
167
|
+
- `@jgengine/shell/audio/loopParams` → `@jgengine/core/audio/loopParams`
|
|
168
|
+
- `@jgengine/shell/audio/musicDirector` → `@jgengine/core/audio/musicDirector.view`
|
|
169
|
+
- `@jgengine/shell/audio/musicVoices` → `@jgengine/core/audio/musicVoices.view`
|
|
170
|
+
- `@jgengine/shell/audio/occlusion` → `@jgengine/core/audio/occlusionRaycast`
|
|
171
|
+
- `@jgengine/shell/audio/playableAudio` → `@jgengine/core/audio/playableAudio.draw`
|
|
172
|
+
- `@jgengine/shell/audio/reverb` → `@jgengine/core/audio/reverbRack.view`
|
|
173
|
+
- `@jgengine/shell/audio/speech` → `@jgengine/core/audio/speech.view`
|
|
174
|
+
- `@jgengine/shell/audio/synthEngine` → `@jgengine/core/audio/synthEngine.view`
|
|
175
|
+
- `@jgengine/shell/behaviourAttach` → `@jgengine/core/behaviour/behaviourAttach.draw`
|
|
176
|
+
- `@jgengine/shell/behaviourDriver` → `@jgengine/core/behaviour/behaviourDriver`
|
|
177
|
+
- `@jgengine/shell/behaviour` → `@jgengine/core/behaviour/behaviour.draw`
|
|
178
|
+
- `@jgengine/shell/boundActionDispatch` → `@jgengine/core/input/boundActionDispatch`
|
|
179
|
+
- `@jgengine/shell/buildings/BuildingKitBatch` → `@jgengine/core/buildings/BuildingKitBatch.draw`
|
|
180
|
+
- `@jgengine/shell/buildings/BuildingRenderBudget` → `@jgengine/core/buildings/BuildingRenderBudget.draw`
|
|
181
|
+
- `@jgengine/shell/buildings/GeneratedBuilding` → `@jgengine/core/buildings/GeneratedBuilding.draw`
|
|
182
|
+
- `@jgengine/shell/buildings/buildingKitFit` → `@jgengine/core/buildings/buildingKitFit.draw`
|
|
183
|
+
- `@jgengine/shell/buildings/buildingKitMaterials` → `@jgengine/core/buildings/buildingKitMaterials.draw`
|
|
184
|
+
- `@jgengine/shell/buildings/buildingKitRegistry` → `@jgengine/core/buildings/buildingKitRegistry`
|
|
185
|
+
- `@jgengine/shell/buildings/buildingSpatialBatch` → `@jgengine/core/buildings/buildingSpatialBatch.draw`
|
|
186
|
+
- `@jgengine/shell/buildings/buildingSurface` → `@jgengine/core/buildings/buildingSurface.draw`
|
|
187
|
+
- `@jgengine/shell/commandSink` → `@jgengine/core/commands/commandSink`
|
|
188
|
+
- `@jgengine/shell/devtools/ColPanel` → `@jgengine/core/devtools/ColPanel.view`
|
|
189
|
+
- `@jgengine/shell/devtools/CollisionDebugWorld` → `@jgengine/core/devtools/CollisionDebugWorld.draw`
|
|
190
|
+
- `@jgengine/shell/devtools/DevtoolsOverlay` → `@jgengine/core/devtools/DevtoolsOverlay.draw`
|
|
191
|
+
- `@jgengine/shell/devtools/DevtoolsRuntime` → `@jgengine/core/devtools/DevtoolsRuntime.draw`
|
|
192
|
+
- `@jgengine/shell/devtools/FrameTelemetry` → `@jgengine/core/devtools/FrameTelemetry.draw`
|
|
193
|
+
- `@jgengine/shell/devtools/KeysPanel` → `@jgengine/core/devtools/KeysPanel.view`
|
|
194
|
+
- `@jgengine/shell/devtools/LogsPanel` → `@jgengine/core/devtools/LogsPanel.view`
|
|
195
|
+
- `@jgengine/shell/devtools/MissingModelsNotice` → `@jgengine/core/devtools/MissingModelsNotice.draw`
|
|
196
|
+
- `@jgengine/shell/devtools/NetPanel` → `@jgengine/core/devtools/NetPanel.draw`
|
|
197
|
+
- `@jgengine/shell/devtools/PerfPanel` → `@jgengine/core/devtools/PerfPanel.view`
|
|
198
|
+
- `@jgengine/shell/devtools/TunePanel` → `@jgengine/core/devtools/TunePanel.view`
|
|
199
|
+
- `@jgengine/shell/devtools/agentBridge` → `@jgengine/core/devtools/agentBridge.draw`
|
|
200
|
+
- `@jgengine/shell/devtools/collisionDebugMath` → `@jgengine/core/devtools/collisionDebugMath`
|
|
201
|
+
- `@jgengine/shell/devtools/collisionDebug` → `@jgengine/core/devtools/collisionDebug`
|
|
202
|
+
- `@jgengine/shell/devtools/devtoolsOverrides` → `@jgengine/core/devtools/devtoolsOverrides.view`
|
|
203
|
+
- `@jgengine/shell/devtools/devtoolsReports` → `@jgengine/core/devtools/devtoolsReports.draw`
|
|
204
|
+
- `@jgengine/shell/devtools/frameSampler` → `@jgengine/core/devtools/frameSampler.view`
|
|
205
|
+
- `@jgengine/shell/devtools/latencyInstrumentation` → `@jgengine/core/devtools/latencyInstrumentation.draw`
|
|
206
|
+
- `@jgengine/shell/devtools/panelAtoms` → `@jgengine/core/devtools/panelAtoms.view`
|
|
207
|
+
- `@jgengine/shell/devtools/perfDiagnose` → `@jgengine/core/devtools/perfDiagnose.view`
|
|
208
|
+
- `@jgengine/shell/devtools/playtestProbe` → `@jgengine/core/devtools/playtestProbe`
|
|
209
|
+
- `@jgengine/shell/devtools/simStepper` → `@jgengine/core/sim/simStepper`
|
|
210
|
+
- `@jgengine/shell/diagnostics/FpsCounter` → `@jgengine/core/devtools/FpsCounter.draw`
|
|
211
|
+
- `@jgengine/shell/diagnostics/RuntimeDiagnostics` → `@jgengine/core/devtools/diagnosticOverlay.view`
|
|
212
|
+
- `@jgengine/shell/hotbarActions` → `@jgengine/core/input/hotbarActions`
|
|
213
|
+
- `@jgengine/shell/input/gamepadMerge` → `@jgengine/core/input/gamepadMerge`
|
|
214
|
+
- `@jgengine/shell/input/gamepadPoll` → `@jgengine/core/input/gamepadPoll`
|
|
215
|
+
- `@jgengine/shell/input/gamepadSource` → `@jgengine/core/input/gamepadSource.draw`
|
|
216
|
+
- `@jgengine/shell/input/mouseLook` → `@jgengine/core/input/mouseLook.draw`
|
|
217
|
+
- `@jgengine/shell/input/pointerLock` → `@jgengine/core/input/pointerLock`
|
|
218
|
+
- `@jgengine/shell/inputSink` → `@jgengine/core/input/inputSink`
|
|
219
|
+
- `@jgengine/shell/multiplayer` → `@jgengine/core/multiplayer/multiplayer.draw`
|
|
220
|
+
- `@jgengine/shell/placement/PlacementGhost` → `@jgengine/core/placement/PlacementGhost.draw`
|
|
221
|
+
- `@jgengine/shell/placement/TransformGizmo` → `@jgengine/core/placement/TransformGizmo.draw`
|
|
222
|
+
- `@jgengine/shell/play/run` → `@jgengine/core/play/playRunner.draw`
|
|
223
|
+
- `@jgengine/shell/play/script` → `@jgengine/core/play/script`
|
|
224
|
+
- `@jgengine/shell/play/timeline` → `@jgengine/core/play/timeline`
|
|
225
|
+
- `@jgengine/shell/playSurfaceFocus` → `@jgengine/core/input/surfaceFocus.view`
|
|
226
|
+
- `@jgengine/shell/pointer/PointerOverlays` → `@jgengine/core/input/PointerOverlays.view`
|
|
227
|
+
- `@jgengine/shell/pointer/PointerProbe` → `@jgengine/core/input/PointerProbe.draw`
|
|
228
|
+
- `@jgengine/shell/pointer/contextMenu` → `@jgengine/core/input/secondaryPointer`
|
|
229
|
+
- `@jgengine/shell/pointer/controlGroupKeys` → `@jgengine/core/input/controlGroupKeys`
|
|
230
|
+
- `@jgengine/shell/pointer/pointerService` → `@jgengine/core/input/pointerService.draw`
|
|
231
|
+
- `@jgengine/shell/pointer/selectionGesture` → `@jgengine/core/input/selectionGesture`
|
|
232
|
+
- `@jgengine/shell/replay/useSessionRecorder` → `@jgengine/core/replay/useSessionRecorder.draw`
|
|
233
|
+
- `@jgengine/shell/shellControlSuspension` → `@jgengine/core/input/shellControlSuspension`
|
|
234
|
+
- `@jgengine/shell/shellInputPublication` → `@jgengine/core/input/shellInputPublication`
|
|
235
|
+
- `@jgengine/shell/shellMovement` → `@jgengine/core/movement/shellMovement`
|
|
236
|
+
- `@jgengine/shell/shellPointerInput` → `@jgengine/core/input/shellPointerInput`
|
|
237
|
+
- `@jgengine/shell/shellPointer` → `@jgengine/core/input/shellPointer.draw`
|
|
238
|
+
- `@jgengine/shell/touch/OrientationHint` → `@jgengine/core/input/OrientationHint.view`
|
|
239
|
+
- `@jgengine/shell/touch/TouchControlsOverlay` → `@jgengine/core/input/TouchControlsOverlay.view`
|
|
240
|
+
- `@jgengine/shell/touch/touchClusterLayout` → `@jgengine/core/input/touchClusterLayout`
|
|
241
|
+
- `@jgengine/shell/touch/touchIcons` → `@jgengine/core/input/touchIcons.view`
|
|
242
|
+
- `@jgengine/shell/useShellMultiplayerSync` → `@jgengine/core/multiplayer/useShellMultiplayerSync.draw`
|
|
243
|
+
- `@jgengine/shell/worldSync` → `@jgengine/core/multiplayer/authoritativeSync`
|
|
244
|
+
- The input and audio handles that came from the shell hand their state out and take it back, like every other stateful core handle: `MouseLookTracker.aim()`/`setAim(yaw, pitch)` → `state()`/`setState({ yaw, pitch })`; `PointerService.setCursor(x, y, present)`/`hasCursor()` → `setState({ x, y, present })`/`state().present`.
|
|
245
|
+
- Each engine feature is now one folder in `@jgengine/core` holding its model, React view and three.js draw files; the file name says the layer (`name.ts` model, `name.view.tsx` view, `name.draw.tsx` draw). The `@jgengine/react` root barrel still exports the same names. Update deep imports:
|
|
246
|
+
- HUD: `@jgengine/react/hud/<file>` → `@jgengine/core/hud/<file>.view` for `actionHud`, `comboMeter`, `hudButton`, `hudChromePreview`, `hudFrame`, `hudInteract`, `hudLanesPreview`, `hudLayout`, `hudLayoutPersist`, `hudReadouts`, `hudSkin`, `hudSkinPreview`, `hudSurface`, `hudTheme`, `hudThemePreview`, `hudThemeRoot`, `hudViewport`, `kitHud`, `selectionHud`, `statusEffectBar`, `waveHud`; `@jgengine/react/hud/hudLanes` → `@jgengine/core/hud/hudLanes`; `@jgengine/react/hud/hudTokens` → `@jgengine/core/hud/hudTokens`.
|
|
247
|
+
- HUD draw: `@jgengine/shell/hud/<file>` → `@jgengine/core/hud/<file>.draw` for `FrustumSensorHud`, `HudOnlyDriver`, `ShellHudPresentation`, `ViewportHuds`, `WorldHud`; `@jgengine/shell/hud/HiddenStateProbeHud` → `@jgengine/core/hud/HiddenStateProbeHud.view`.
|
|
248
|
+
- Save: `@jgengine/react/save/{saveSlots,saveStore,saveThumbnail}` → `@jgengine/core/save/{saveSlots,saveStore,saveThumbnail}.view`.
|
|
249
|
+
- `@jgengine/core` lists `react`, `react-dom`, `three`, `@react-three/fiber`, `@react-three/drei`, `three-stdlib`, `@jgengine/react` and `@jgengine/shell` as optional peers: a `.view` or `.draw` import needs them; model imports need nothing.
|
|
250
|
+
- One exported name, one definition. Renamed:
|
|
251
|
+
- `Mood` in `@jgengine/core/render/lookMood` → `LookMood`; `Mood` is the agent mood in `@jgengine/core/agents/mood`.
|
|
252
|
+
- `Footprint` in `@jgengine/core/inventory/shapedGrid` → `CellFootprint`; `Footprint` is the `{ w, d }` box in `@jgengine/core/world/geometry`.
|
|
253
|
+
- `PlayableGame` in `@jgengine/core/game/playableGame` → `PlayableGameContract`; `PlayableGame` is the shell type games use (`@jgengine/shell/registry`).
|
|
254
|
+
- `@jgengine/ws/host` and `@jgengine/node/host`: `GameHost` → `GameServerHost`, `createGameHost` → `createGameServerHost`, `GameHostOptions` → `GameServerHostOptions`. `GameHost` is the shell component.
|
|
255
|
+
- `pathLength` in `@jgengine/core/nav/pathFollow` → `followPathLength`; its `HeightSampler` → `HeightSource`.
|
|
256
|
+
- `Job` in `@jgengine/core/ai/jobBoard` → `StationJob`. `inGridBounds` in `@jgengine/core/editor/grid` → `inLayerBounds`. `SnapMode` in `@jgengine/editor` → `EditorSnapMode`.
|
|
257
|
+
- `AssetRef` in `@jgengine/core/game/tribe` → `TribeAssetRef`. `BufferedAction` in `@jgengine/core/time/beatClock` → `BeatBufferedAction`. `HarvestResult` in `@jgengine/core/crafting/crop` → `CropHarvest`.
|
|
258
|
+
- `ObjectRaycastHit` in `@jgengine/core/combat/projectiles` → `ProjectileObjectHit`. `WallSegment` in `@jgengine/core/scene/sceneRaycast` → `RaycastWall`. `PolylineHit` in `@jgengine/core/world/polylineIndex` → `PolylineIndexHit`.
|
|
259
|
+
- `HitReactionInput` in `@jgengine/core/runtime/gameContext` → `EntityHitReactionInput`. `ThresholdDirection` in `@jgengine/core/game/objectives` → `ObjectiveDirection`.
|
|
260
|
+
- `LeaderboardRow` in `@jgengine/core/runtime/hostPersistence` → `StoredLeaderboardRow` (the game `LeaderboardRow` plus `gameId` and `updatedAt`). `RunCommandArgs` in `@jgengine/convex/server` → `ConvexRunCommandArgs`.
|
|
261
|
+
- `FrameTelemetry` interface in `@jgengine/shell/devtools/frameSampler` → `FrameSampler`.
|
|
262
|
+
- `@jgengine/core/world/vec2` and `vec3` export one object each: `vec3.distance(a, b)` instead of `import { distance }`.
|
|
263
|
+
- Thresholds live in `@jgengine/core/threshold/`: `relation/thresholds` → `threshold/thresholds`; `combat/thresholdCrossings` → `threshold/thresholdTracker`, whose `ThresholdMark` is now `ThresholdBoundary` and whose emitted `ThresholdCrossing` is `ThresholdTrackerCrossing`. The accumulator meter's `tierAt` is gone; use `tierAt(tiers, value)?.id` from `threshold/thresholds`.
|
|
264
|
+
- One waypoint store: `createWaypointTracker` and its `Waypoint`/`WaypointTracker` types are removed from `@jgengine/core/ui/screenMarkers`. Use `createWaypointStore` (`@jgengine/core/world/waypoints`); `place([x, z], { height })` sets the screen-marker anchor, and `WaypointMarkers` takes the store.
|
|
265
|
+
- Moved: `MAX_SESSION_ID_LENGTH` → `@jgengine/core/runtime/hostPolicy`. `SessionVisibility` → `@jgengine/core/multiplayer/matchmaking` only. `Vec2` from `movement/formation`, `Vec3` from `anim/ikSolver` and `world/buildings` → `@jgengine/core/world/geometry`. The `{ x, y, z }` `Vec3` of `multiplayer/lagCompensation` and shell audio → `Xyz` (`@jgengine/core/math/xyz`).
|
|
266
|
+
- `CreatorConfig` is no longer re-exported by `@jgengine/editor`; use `CreatorConfig<PlayableGame>` from `@jgengine/core/editor/creatorStorage`.
|
|
267
|
+
- `HudPanel` no longer takes `lane` or `compact`. Every flowing panel is placed by one solver: drop `lane`, replace `compact="hide"` with `priority="tertiary"` (it folds into a toggle when there is no room), and drop `compact="keep"` and `compact="chip"`.
|
|
268
|
+
- `HudCanvas` no longer takes `compactScale`; the canvas picks the zoom.
|
|
269
|
+
- `resolveHudLanes`, `HudLaneSlot`, `HudLanePlacement`, `HudLaneConfig` and `HudLaneLayout` (`@jgengine/core/hud/hudLanes`) are gone. Use `arrangeHudPanels` (`@jgengine/core/hud/hudPanelLayout`).
|
|
270
|
+
- `HudLanesPreview` is now `HudAutoLayoutPreview` (`@jgengine/core/hud/hudPanelLayoutPreview.view`).
|
|
271
|
+
- Pick one `anchor` per panel. A game that switched anchors by screen size can drop the switch.
|
|
272
|
+
- `GameFrame` is gone: pass its `material`/`cut`/`depth`/`tint`/`edge`/`glow` props to `KitPanel` (a panel) or `KitButton` (a button) from `@jgengine/react/uiKitRoot`. `@jgengine/react/gameFrame` is now `@jgengine/react/kitFrame`: `GameMaterial` → `KitMaterial`, `GameCut` → `KitCut`, `GameMaterialLook` → `KitMaterialLook`, `GAME_MATERIALS` → `KIT_MATERIALS`, `gameCutPath` → `kitCutPath`, `gameFrameVars` → `kitFrameVars`; `GameFramesPreview` is `KitFramesPreview`.
|
|
273
|
+
- `HudFade` and `useHudAwake` moved from `@jgengine/react/hudFade` to `@jgengine/react/hud/hudLayout`.
|
|
274
|
+
- `StatBar`, `VitalBar`, `EntitySummary`, `StatMeter`, the `@jgengine/react/bars` bars (`HealthBar`, `ShieldBar`, `ManaBar`, `BossBar`, `AmmoCounter`, `barTokens`, …) and `@jgengine/react/hudPanel` (the old name of `HudSurface`; `HudPanel` now means only the `HudCanvas` slot) are removed. Draw every meter with `KitMeter`; a HUD's vitals card is `KitVitals`.
|
|
275
|
+
- `HudTheme.bar`, `HudSkin.bar` and the `--jg-bar-*` variables are removed; meters take the UI kit's tokens.
|
|
276
|
+
- `jgengine upgrade` is gone. Run `npx jgengine@latest sync` instead: at a game it brings the game to that engine; at a workspace root it moves the root catalog's `@jgengine/*` pins, then syncs every member game. It prints the release notes (Migrate first, then Adopt) for the versions it crossed.
|
|
277
|
+
- Template games no longer carry `scripts/browser.mjs`, `scripts/shoot.mjs` or `scripts/drive.mjs`. Run `jgengine sync` once: it deletes the old copies (unless listed in `owns`) and points the `shoot`/`drive` scripts at `jgengine shoot`/`jgengine drive`.
|
|
278
|
+
- `IN_REPO_TSCONFIG_PATHS` (`jgengine`) is gone, with `doctor`'s "in-repo tsconfig paths" finding: an in-repo game's `tsconfig.json` extends `games/tsconfig.base.json`, which takes the engine source paths from the repo's `tsconfig.base.json`.
|
|
279
|
+
- Settings, dialogs and inventory views each live in one folder per layer. Update deep imports:
|
|
280
|
+
- `@jgengine/react/{settings,settingsBlocks,accessibilitySettings,keybindingMenu}` → `@jgengine/react/settings/…`.
|
|
281
|
+
- `@jgengine/react/{modals,dialogBehavior}` → `@jgengine/react/dialog/…`; `@jgengine/core/ui/modalStack` → `@jgengine/core/dialog/modalStack`.
|
|
282
|
+
- `@jgengine/react/{inventoryGrid,slots,shopGrid}` → `@jgengine/react/inventory/…`.
|
|
283
|
+
- The HUD lives in one folder per layer. Update deep imports:
|
|
284
|
+
- `@jgengine/react/<file>` → `@jgengine/react/hud/<file>` for `actionHud`, `comboMeter`, `hudButton`, `hudChromePreview`, `hudFrame`, `hudInteract`, `hudLanes`, `hudLanesPreview`, `hudLayout`, `hudLayoutPersist`, `hudSkin`, `hudSkinPreview`, `hudSurface`, `hudTheme`, `hudThemePreview`, `hudThemeRoot`, `hudTokens`, `hudViewport`, `kitHud`, `selectionHud`, `statusEffectBar`, `waveHud`.
|
|
285
|
+
- `@jgengine/react/hud` → `@jgengine/react/hud/hudReadouts` (`Hotbar`, `Coins`, `Clock`, `Speedometer`, `Crosshair`).
|
|
286
|
+
- `@jgengine/core/ui/{hudDocument,hudLayout,hudScale}` → `@jgengine/core/hud/…`; `@jgengine/core/ui/gameLayout` → `@jgengine/core/hud/hudPriority`.
|
|
287
|
+
- `@jgengine/shell/ShellHudPresentation` → `@jgengine/shell/hud/ShellHudPresentation`; `@jgengine/shell/drivers/HudOnlyDriver` → `@jgengine/shell/hud/HudOnlyDriver`; `@jgengine/shell/hud/WorldHud` → `@jgengine/shell/hud/WorldHud`.
|
|
288
|
+
- `ViewportHuds` moved from `@jgengine/shell/camera/Viewports` to `@jgengine/shell/hud/ViewportHuds`.
|
|
289
|
+
- `@jgengine/shell/hud/FrustumSensorHud` → `@jgengine/shell/hud/FrustumSensorHud`; `@jgengine/shell/hud/HiddenStateProbeHud` → `@jgengine/shell/hud/HiddenStateProbeHud`.
|
|
290
|
+
- `WaveBanner` is removed; use `KitWaveClock` (`@jgengine/react/hud/kitHud`) with `text` and `detail`.
|
|
291
|
+
- `CurrencyPill` is removed; use `Coins` (`@jgengine/react/hud/hudReadouts`), `icon={null}` for the bare number.
|
|
292
|
+
- One pause menu: `GamePauseMenu` is now `PauseMenu` (`@jgengine/react/menu/pauseMenu`, also exported from `@jgengine/react`), the pause scale of `GameMenu`. The old `PauseMenu` in `@jgengine/react/dialog/modals` (Resume plus `{ id, label, onSelect, danger }` slots) is removed: pass `items: [{ id, label }]` and route them in `onSelect(id)`.
|
|
293
|
+
- `TitleMenu` (`@jgengine/react/titleMenu`) is removed; use `GameMenu` with `logo={{ text }}`.
|
|
294
|
+
- Menus live in one folder per layer. Update deep imports:
|
|
295
|
+
- `@jgengine/react/{gameMenu,gameMenuSaves,gameMenuSound,menuNav,menuRouter,menuScreen,quickMenu,radialMenu}` → `@jgengine/react/menu/…`.
|
|
296
|
+
- `@jgengine/core/ui/{menuNav,menuTransition,radialMenu}` → `@jgengine/core/menu/…`.
|
|
297
|
+
- One exported name, one definition. React views that shared a name with their core model are renamed:
|
|
298
|
+
- `Codex` component → `CodexPanel` (`@jgengine/react/codex`); `Codex` stays the core model type.
|
|
299
|
+
- `TalentTree` component → `TalentTreePanel` (`@jgengine/react/talentTree`).
|
|
300
|
+
- `DragState` in `@jgengine/react/dragLayer` → `DragLayerState`.
|
|
301
|
+
- `usePresence` in `@jgengine/react/presence` (mount/exit timing) → `useMountPresence`; `usePresence` is the social-presence hook in `@jgengine/react/hooks`.
|
|
302
|
+
- `SlotGrid` and `ProximityPrompt` are removed from `@jgengine/react/components`; use `InventoryGrid` or `Hotbar`, and `InteractionPrompt`.
|
|
303
|
+
- `easeOutCubic` is no longer exported from `@jgengine/react/tweenNumber`; import it from `@jgengine/core/anim/easing`.
|
|
304
|
+
- `@jgengine/shell/world/WorldHud` → `@jgengine/shell/hud/WorldHud` (same exports).
|
|
305
|
+
- `@jgengine/shell/vision/FrustumSensorHud` → `@jgengine/shell/hud/FrustumSensorHud` (same exports).
|
|
306
|
+
- `@jgengine/shell/vision/HiddenStateProbeHud` → `@jgengine/shell/hud/HiddenStateProbeHud` (same exports).
|
|
307
|
+
- `@jgengine/core/game/vfxInstance` → `@jgengine/core/vfx/vfxInstance`.
|
|
308
|
+
- Shell VFX moved from `world/` to `vfx/`: `@jgengine/shell/world/WorldParticles` → `@jgengine/shell/vfx/ParticleEmitters` (component `WorldParticles` → `ParticleEmitters`), `@jgengine/shell/world/WorldVfx` → `@jgengine/shell/vfx/SpellVfx` (component `WorldSpellVfx` → `SpellVfx`), `@jgengine/shell/world/RetainedVfx` → `@jgengine/shell/vfx/RetainedVfx`.
|
|
309
|
+
- `resolveParticleBudget` is gone. Pass the tier's per-effect cap to `planParticleBudget(entries, cap, { effectCap })` (`@jgengine/core/vfx/particleBudget`), which clamps each request to it before sharing the total out.
|
|
310
|
+
- Buildings, cities and placement each live in one folder per layer. Update deep imports:
|
|
311
|
+
- `@jgengine/core/world/{buildings,buildingGenerator,buildingIndex,buildingKit,buildingLots,massing}` → `@jgengine/core/buildings/…`; `@jgengine/shell/structures/{GeneratedBuilding,BuildingKitBatch,BuildingRenderBudget,buildingKitFit,buildingKitMaterials,buildingKitRegistry,buildingSpatialBatch,buildingSurface}` → `@jgengine/shell/buildings/…`. The `@jgengine/shell/structures` barrel is now `@jgengine/shell/buildings`.
|
|
312
|
+
- `@jgengine/core/world/{cityBlocks,cityContent,cityGenerator,cityKind,cityLayout}` → `@jgengine/core/city/…`; `@jgengine/shell/scene/{CityRenderer,cityJunctionSurface,cityWindowMaterial}` → `@jgengine/shell/city/…`.
|
|
313
|
+
- `@jgengine/core/world/{place,placeAsset,placement,placementController,placedStructureStore}` → `@jgengine/core/placement/…`; `PlacementGhost` and `TransformGizmo` → `@jgengine/shell/placement/…` (barrel `@jgengine/shell/placement`).
|
|
314
|
+
- Building part types have one definition, in `@jgengine/core/buildings/buildings`: the shell no longer exports its own `BuildingFacade`, `BuildingPartKind` or `BuildingPartPlacement`. Removed the shell `GeneratedBuilding` component, which nothing used (render buildings with `InstancedBuildings`); `GeneratedBuilding` now names only the core data.
|
|
315
|
+
- `CityLot` / `CityDriveway` in `cityLayout` are now `CityLayoutLot` / `CityLayoutDriveway`; `CityLot` and `CityDriveway` name the `city` scene kind's types.
|
|
316
|
+
- `@jgengine/shell/grass/GrassCover` → `@jgengine/core/grass/GrassCover.draw`
|
|
317
|
+
- `@jgengine/shell/scene/GrassFieldRenderer` → `@jgengine/core/grass/GrassFieldRenderer.draw`
|
|
318
|
+
- `@jgengine/shell/grass/GrassPatch` → `@jgengine/core/grass/GrassPatch.draw`
|
|
319
|
+
- `@jgengine/shell/grass/grassBake` → `@jgengine/core/grass/grassBake.draw`
|
|
320
|
+
- `@jgengine/shell/grass/grassCoverGeometry` → `@jgengine/core/grass/grassCoverGeometry.draw`
|
|
321
|
+
- `@jgengine/core/world/grassKind` → `@jgengine/core/grass/grassKind`
|
|
322
|
+
- `@jgengine/shell/grass/grassMaterial` → `@jgengine/core/grass/grassMaterial.draw`
|
|
323
|
+
- `@jgengine/shell/grass/grassStream` → `@jgengine/core/grass/grassStream.draw`
|
|
324
|
+
- `@jgengine/shell/sky/Daylight` → `@jgengine/core/sky/Daylight.draw`
|
|
325
|
+
- `@jgengine/shell/sky/VolumetricClouds` → `@jgengine/core/sky/VolumetricClouds.draw`
|
|
326
|
+
- `@jgengine/shell/sky/skyBands` → `@jgengine/core/sky/skyBands`
|
|
327
|
+
- `@jgengine/shell/sky/skyLightingPolicy` → `@jgengine/core/sky/skyLightingPolicy`
|
|
328
|
+
- `@jgengine/shell/sky/skySun` → `@jgengine/core/sky/skySun.draw`
|
|
329
|
+
- `@jgengine/shell/sky/sunShadowMath` → `@jgengine/core/sky/sunShadowMath`
|
|
330
|
+
- `@jgengine/shell/water/Ocean` → `@jgengine/core/water/Ocean.draw`
|
|
331
|
+
- `@jgengine/shell/water/OceanConfig` → `@jgengine/core/water/OceanConfig.draw`
|
|
332
|
+
- `@jgengine/shell/water/OceanMaterial` → `@jgengine/core/water/OceanMaterial.draw`
|
|
333
|
+
- `@jgengine/shell/water/OceanShader` → `@jgengine/core/water/OceanShader`
|
|
334
|
+
- `@jgengine/shell/water/WaterRenderer` → `@jgengine/core/water/WaterRenderer.draw`
|
|
335
|
+
- `@jgengine/shell/water/oceanColor` → `@jgengine/core/water/oceanColor`
|
|
336
|
+
- `@jgengine/shell/weather/AuthoredWeatherLayer` → `@jgengine/core/weather/AuthoredWeatherLayer.draw`
|
|
337
|
+
- `@jgengine/shell/weather/DustField` → `@jgengine/core/weather/DustField.draw`
|
|
338
|
+
- `@jgengine/shell/weather/FireSpreadLayer` → `@jgengine/core/weather/FireSpreadLayer.draw`
|
|
339
|
+
- `@jgengine/shell/weather/LightningStrike` → `@jgengine/core/weather/LightningStrike.draw`
|
|
340
|
+
- `@jgengine/shell/weather/RainField` → `@jgengine/core/weather/RainField.draw`
|
|
341
|
+
- `@jgengine/shell/weather/RainImpactField` → `@jgengine/core/weather/RainImpactField.draw`
|
|
342
|
+
- `@jgengine/shell/weather/SnowField` → `@jgengine/core/weather/SnowField.draw`
|
|
343
|
+
- `@jgengine/shell/weather/WeatherLayer` → `@jgengine/core/weather/WeatherLayer.draw`
|
|
344
|
+
- `@jgengine/core/world/authoredWeather` → `@jgengine/core/weather/authoredWeather`
|
|
345
|
+
- `@jgengine/shell/weather/fireSpreadPose` → `@jgengine/core/weather/fireSpreadPose.draw`
|
|
346
|
+
- `@jgengine/core/world/weather` → `@jgengine/core/weather/weather`
|
|
347
|
+
- `@jgengine/shell/weather/weatherGeometry` → `@jgengine/core/weather/weatherGeometry.draw`
|
|
348
|
+
- `@jgengine/shell/weather/weatherMath` → `@jgengine/core/weather/weatherMath`
|
|
349
|
+
- `@jgengine/shell/weather/weatherShelter` → `@jgengine/core/weather/weatherShelter.draw`
|
|
350
|
+
- `@jgengine/shell/weather/weatherUniforms` → `@jgengine/core/weather/weatherUniforms.draw`
|
|
351
|
+
|
|
352
|
+
- `@jgengine/shell/weather` and `@jgengine/shell/water` are gone: import each name from its core file above (`WeatherLayer` from `@jgengine/core/weather/WeatherLayer.draw`, `Ocean` from `@jgengine/core/water/Ocean.draw`).
|
|
353
|
+
- `@jgengine/shell/environment/EnvironmentScene` → `@jgengine/core/environment/EnvironmentScene.draw`
|
|
354
|
+
- `@jgengine/shell/environment/GroundPad` → `@jgengine/core/environment/GroundPad.draw`
|
|
355
|
+
- `@jgengine/shell/environment/groundPadMath` → `@jgengine/core/environment/groundPadMath`
|
|
356
|
+
- `@jgengine/shell/instancing/instanceChunks` → `@jgengine/core/instancing/instanceChunks.draw`
|
|
357
|
+
- `@jgengine/shell/instancing/instanceUpload` → `@jgengine/core/instancing/instanceUpload.draw`
|
|
358
|
+
- `@jgengine/shell/instancing/staticShapeInstances` → `@jgengine/core/instancing/staticShapeInstances.draw`
|
|
359
|
+
- `@jgengine/shell/lod/characterLod` → `@jgengine/core/lod/characterLod`
|
|
360
|
+
- `@jgengine/shell/lod/impostorCardPool` → `@jgengine/core/lod/impostorCardPool.draw`
|
|
361
|
+
- `@jgengine/shell/lod/impostor` → `@jgengine/core/lod/impostor.draw`
|
|
362
|
+
- `@jgengine/shell/lod/levelPass` → `@jgengine/core/lod/levelPass.draw`
|
|
363
|
+
- `@jgengine/shell/lod/singleVariantLod` → `@jgengine/core/lod/singleVariantLod.draw`
|
|
364
|
+
- `@jgengine/shell/scatter/InstancedScatter` → `@jgengine/core/scatter/InstancedScatter.draw`
|
|
365
|
+
- `@jgengine/shell/scatter/ScatterImpostorField` → `@jgengine/core/scatter/ScatterImpostorField.draw`
|
|
366
|
+
- `@jgengine/shell/scatter/scatterModels` → `@jgengine/core/scatter/scatterModels.draw`
|
|
367
|
+
- `@jgengine/shell/scatter/scatterProxies` → `@jgengine/core/scatter/scatterProxies.draw`
|
|
368
|
+
- `@jgengine/shell/scatter/scatterVariants` → `@jgengine/core/scatter/scatterVariants.draw`
|
|
369
|
+
- `@jgengine/shell/terrain/CarvedTerrain` → `@jgengine/core/terrain/CarvedTerrain.draw`
|
|
370
|
+
- `@jgengine/shell/terrain/EditableGround` → `@jgengine/core/terrain/EditableGround.draw`
|
|
371
|
+
- `@jgengine/shell/terrain/ProceduralGround` → `@jgengine/core/terrain/ProceduralGround.draw`
|
|
372
|
+
- `@jgengine/shell/terrain/TerraformBrushCursor` → `@jgengine/core/terrain/TerraformBrushCursor.draw`
|
|
373
|
+
- `@jgengine/shell/terrain/groundEdgeFade` → `@jgengine/core/terrain/groundEdgeFade.draw`
|
|
374
|
+
- `@jgengine/shell/terrain/heightfieldGeometry` → `@jgengine/core/terrain/heightfieldGeometry.draw`
|
|
375
|
+
- `@jgengine/shell/terrain/random` → `@jgengine/core/terrain/terrainSeed`
|
|
376
|
+
- `@jgengine/shell/terrain/soilPatchMaterial` → `@jgengine/core/terrain/soilPatchMaterial.draw`
|
|
377
|
+
- `@jgengine/shell/terrain/terrainChunks` → `@jgengine/core/terrain/terrainChunks.draw`
|
|
378
|
+
- `@jgengine/shell/terrain/terrainDetailMaterial` → `@jgengine/core/terrain/terrainDetailMaterial.draw`
|
|
379
|
+
- `@jgengine/shell/terrain/terrainGroundColor` → `@jgengine/core/terrain/terrainGroundColor.draw`
|
|
380
|
+
- `@jgengine/shell/terrain/terrainMath` → `@jgengine/core/terrain/terrainMath.draw`
|
|
381
|
+
- `@jgengine/shell/terrain/terrainPaintColor` → `@jgengine/core/terrain/terrainPaintColor.draw`
|
|
382
|
+
- `@jgengine/shell/terrain/terrainQuality` → `@jgengine/core/terrain/terrainQuality.draw`
|
|
383
|
+
- `@jgengine/shell/terrain/terrainSurfaceShader` → `@jgengine/core/terrain/terrainSurfaceShader.draw`
|
|
384
|
+
- `@jgengine/shell/terrain/terrainSurfaces` → `@jgengine/core/terrain/terrainSurfaces.draw`
|
|
385
|
+
- `@jgengine/shell/worldSky` → `@jgengine/core/sky/worldSky`
|
|
386
|
+
- The `@jgengine/shell/terrain`, `@jgengine/shell/scatter` and `@jgengine/shell/environment` barrels are gone: import each name from its file above (grass names from `@jgengine/core/grass/*`, `Sky` from `@jgengine/core/sky/Daylight.draw`, terrain fields from `@jgengine/core/terrain/terrain`).
|
|
387
|
+
- `colorFromId` is no longer exported; it was only used by the world scene.
|
|
388
|
+
- `@jgengine/core/world/fastTravel` → `@jgengine/core/map/fastTravel`
|
|
389
|
+
- `@jgengine/core/world/fog` → `@jgengine/core/map/fog`
|
|
390
|
+
- `@jgengine/core/world/mapAnnotations` → `@jgengine/core/map/mapAnnotations`
|
|
391
|
+
- `@jgengine/core/world/mapLayers` → `@jgengine/core/map/mapLayers`
|
|
392
|
+
- `@jgengine/core/world/markers` → `@jgengine/core/map/markers`
|
|
393
|
+
- `@jgengine/core/world/minimapBake` → `@jgengine/core/map/minimapBake`
|
|
394
|
+
- `@jgengine/core/world/minimapTrack` → `@jgengine/core/map/minimapTrack`
|
|
395
|
+
- `@jgengine/core/world/minimap` → `@jgengine/core/map/minimap`
|
|
396
|
+
- `@jgengine/core/world/sharedAnnotations` → `@jgengine/core/map/sharedAnnotations`
|
|
397
|
+
- `@jgengine/core/world/waypoints` → `@jgengine/core/map/waypoints`
|
|
398
|
+
- `@jgengine/react/fastTravel` → `@jgengine/core/map/fastTravel.view`
|
|
399
|
+
- `@jgengine/react/fogOverlay` → `@jgengine/core/map/fogOverlay.view`
|
|
400
|
+
- `@jgengine/react/map` → `@jgengine/core/map/map.view`
|
|
401
|
+
- `@jgengine/react/waypointMarkers` → `@jgengine/core/map/waypointMarkers.view`
|
|
402
|
+
- `@jgengine/shell/map/MapMarkerBeacons` → `@jgengine/core/map/MapMarkerBeacons.draw`
|
|
403
|
+
- `@jgengine/shell/map/terrainMap` → `@jgengine/core/map/terrainMap.draw`
|
|
404
|
+
- The `@jgengine/shell/map` barrel is gone: import `bakeTerrainMap` from `@jgengine/core/map/terrainMap.draw` and `MapMarkerBeacons` from `@jgengine/core/map/MapMarkerBeacons.draw`. The `@jgengine/react` root still exports the map components.
|
|
405
|
+
- `@jgengine/core/hud/hudPanelLayoutPreview.view` → `@jgengine/core/hud/hudLanesPreview.view`
|
|
406
|
+
- `@jgengine/react/roads/streetGeometryPreview` → `@jgengine/core/roads/streetGeometryPreview.view`
|
|
407
|
+
- `@jgengine/react/spriteClipPreview` → `@jgengine/core/render/spriteClipPreview.view`
|
|
408
|
+
- `@jgengine/react/tileLayerPreview` → `@jgengine/core/render/tileLayerPreview.view`
|
|
409
|
+
- `@jgengine/shell/city/CityRenderer` → `@jgengine/core/city/CityRenderer.draw`
|
|
410
|
+
- `@jgengine/shell/city/cityJunctionSurface` → `@jgengine/core/city/cityJunctionSurface`
|
|
411
|
+
- `@jgengine/shell/city/cityWindowMaterial` → `@jgengine/core/city/cityWindowMaterial.draw`
|
|
412
|
+
- `@jgengine/shell/roads/RoadJunctions` → `@jgengine/core/roads/RoadJunctions.draw`
|
|
413
|
+
- `@jgengine/shell/roads/RoadRibbons` → `@jgengine/core/roads/RoadRibbons.draw`
|
|
414
|
+
- `@jgengine/shell/roads/surfaceRibbonMaterial` → `@jgengine/core/roads/surfaceRibbonMaterial.draw`
|
|
415
|
+
- `@jgengine/shell/scene/AuthoredScene` → `@jgengine/core/scene/AuthoredScene.draw`
|
|
416
|
+
- `@jgengine/shell/scene/DecalRenderer` → `@jgengine/core/scene/DecalRenderer.draw`
|
|
417
|
+
- `@jgengine/shell/scene/GeneratedAssetRenderer` → `@jgengine/core/scene/GeneratedAssetRenderer.draw`
|
|
418
|
+
- `@jgengine/shell/scene/PoleLineRenderer` → `@jgengine/core/scene/PoleLineRenderer.draw`
|
|
419
|
+
- `@jgengine/shell/scene/SoilPatchRenderer` → `@jgengine/core/scene/SoilPatchRenderer.draw`
|
|
420
|
+
- `@jgengine/shell/scene/StudioStage` → `@jgengine/core/scene/StudioStage.draw`
|
|
421
|
+
- `@jgengine/shell/scene/builtinSceneKindRenderers` → `@jgengine/core/scene/builtinSceneKindRenderers.draw`
|
|
422
|
+
- `@jgengine/shell/scene/sceneKindRenderers` → `@jgengine/core/scene/sceneKindRenderers.draw`
|
|
423
|
+
- The `@jgengine/shell/scene` barrel is gone: import `AuthoredScene` from `@jgengine/core/scene/AuthoredScene.draw`.
|
|
424
|
+
- Level of detail lives in one folder per layer. Update deep imports:
|
|
425
|
+
- `@jgengine/core/world/{lod,modelLod}` → `@jgengine/core/lod/…`.
|
|
426
|
+
- `@jgengine/shell/render/{levelPass,singleVariantLod,characterLod,impostor,impostorCardPool}` → `@jgengine/shell/lod/…`.
|
|
427
|
+
- `ModelShadowMode` has one definition, in `@jgengine/core/lod/modelLod`; `@jgengine/shell/render/modelRender` no longer exports its copy.
|
|
428
|
+
- Removed `@jgengine/core/runtime/visibility` (`VisibilitySystem` class, `CameraVisibilityContext` and friends), which nothing used. Culling is `createVisibilitySystem` in `@jgengine/core/visibility/visibilitySystem`.
|
|
429
|
+
- Roads live in one folder per layer. Update deep imports:
|
|
430
|
+
- `@jgengine/core/world/{roads,pathRoads,streets,streetGenerator,routeCourse,raceCircuit}` → `@jgengine/core/roads/…`.
|
|
431
|
+
- `@jgengine/shell/environment/{RoadRibbons,RoadJunctions}` and `@jgengine/shell/terrain/surfaceRibbonMaterial` → `@jgengine/shell/roads/…`; the barrel is `@jgengine/shell/roads`.
|
|
432
|
+
- `@jgengine/react/streetGeometryPreview` → `@jgengine/react/roads/streetGeometryPreview`.
|
|
433
|
+
- One junction builder: `buildTrimmedIntersections` + `buildIntersectionMarkings`. Removed the older parallel paths:
|
|
434
|
+
- `buildJunctionPatch` (disc patch), `buildJunctionConnector`, `trimBandAtJunctions`/`BandTrimOptions`, `DashExclusion` and the `exclude` argument of `dashSegments`.
|
|
435
|
+
- From `streets`: `laneCenters`/`StreetLane`, `curbPaths`, `junctionExclusions`, `junctionMarkings` (use `IntersectionMarkingIntent.crosswalk` / `stopLine`). `offsetPath` now comes from `@jgengine/core/roads/roads`.
|
|
436
|
+
- `pathLength` in the roads module is now `polylineLength` (`@jgengine/core/world` `pathLength` is still the path-follow one). The shell `terrain` barrel no longer re-exports the surface-ribbon material; import it from `@jgengine/shell/roads/surfaceRibbonMaterial`.
|
|
437
|
+
- Scatter lives in one folder per layer: `@jgengine/core/world/{scatter,scatterRegion,scatterCoverage}` → `@jgengine/core/scatter/…` (the shell side is already `@jgengine/shell/scatter`).
|
|
438
|
+
- Removed `@jgengine/core/world/scatterItems` (`scatterItems`, `ScatterLayer` and its `ScatterInstance`), which nothing used. `ScatterInstance` is the scatter-region one.
|
|
439
|
+
- Polygon helpers have one home: `@jgengine/core/world/cityGeometry` → `@jgengine/core/world/polygon`. `scatterRegion` no longer exports `pointInPolygon`, `polygonArea`, `polygonBounds` or `distanceToPolygonEdge`; use `pointInPolygon(ring, x, z)`, `polygonArea`, `ringBounds` and `distanceToRing(ring, x, z)` from `@jgengine/core/world/polygon`.
|
|
440
|
+
- Scatter regions pick their palette item with the shared `pickWeighted` from `@jgengine/core/random/pick`.
|
|
441
|
+
- The sky lives in one folder per layer. Update deep imports:
|
|
442
|
+
- `@jgengine/core/render/{physicalSky,skyOwnedLighting}`, `@jgengine/core/world/volumetricClouds` and `@jgengine/core/time/dayNightCycle` → `@jgengine/core/sky/…`.
|
|
443
|
+
- `@jgengine/shell/environment/daylightCycle` → `@jgengine/core/sky/daylightCycle` (pure model, no three.js).
|
|
444
|
+
- `@jgengine/shell/environment/{Daylight,VolumetricClouds,skyBands,skyLightingPolicy,sunShadowMath}` → `@jgengine/shell/sky/…`. The `@jgengine/shell/environment` barrel still exports `Sky`, `SkyDome` and `VolumetricClouds`.
|
|
445
|
+
- `lerpHexColor` is gone; the day/night models mix colours with `mixHex` from `@jgengine/core/render/color`, which now also reads bare `rrggbb`.
|
|
446
|
+
- Camera and vector types have one definition each:
|
|
447
|
+
- `Vec3` from `@jgengine/core/vfx/particles` or `@jgengine/core/visibility/bounds`, and `Vec2` from `@jgengine/core/world/polygon` → `@jgengine/core/world/geometry`.
|
|
448
|
+
- The `{ x, y, z }` `Vec3` of `@jgengine/shell/camera/orbitCameraMath` and `@jgengine/editor/camera/orbitFraming` → `Xyz` from `@jgengine/core/math/xyz`.
|
|
449
|
+
- Camera config moved: `@jgengine/core/game/cameraConfig` → `@jgengine/core/camera/cameraConfig`. `CameraFollowState` is defined only there; `orbitCameraMath` no longer exports it or `distanceBetween`.
|
|
450
|
+
- `@jgengine/shell/camera/rigMath` no longer exports `clamp`, `lerp` or `smoothstep`; use `@jgengine/core/math/scalar` and `@jgengine/core/anim/easing`.
|
|
451
|
+
- `nowMs` is no longer exported from `@jgengine/core/visibility/diagnostics`; use `@jgengine/core/devtools/frame`.
|
|
452
|
+
- `@jgengine/core/world/water` → `@jgengine/core/water/waterSurface`; `@jgengine/core/world/waterKind` → `@jgengine/core/water/waterKind`.
|
|
453
|
+
- `@jgengine/shell/scene/WaterRenderer` → `@jgengine/shell/water/WaterRenderer`; `@jgengine/shell/environment/oceanColor` → `@jgengine/shell/water/oceanColor`.
|
|
454
|
+
- `OceanConfig` wave fields (`amplitude`, `speed`, `direction`, `steepness`, `waveScale`, custom `waves[]`) are gone. Pass `waves: WaterSurfaceConfig` (`waveHeight`, `waveScale`, `waveSpeed`, `waves`, `choppiness`, `direction`, `steepness`), the same config `waterSurface` takes. `DEFAULT_OCEAN_WAVE_SCALE`, `OceanWaveConfig`, `OceanDirectionVector`, `OceanWaveDirection` and `ResolvedOceanWaveConfig` are removed; `ResolvedOceanConfig.waves` is `GerstnerWave[]`.
|
|
455
|
+
|
|
456
|
+
### Added
|
|
457
|
+
|
|
458
|
+
- `ActionDef.detail` (`@jgengine/core/ui/actionModel`): the quieter text after the label, a cost line or unlock condition.
|
|
459
|
+
- `ActionBar` takes `look` (`slots` | `kit`), `progress`, `trailing`, `prompt` and `wrapAt`.
|
|
460
|
+
- `hudBlockRegion(id, rect)` (`@jgengine/core/hud/hudPriority`); `formatLayoutCollisions` takes the viewport and its header reads `JGENGINE LAYOUT COLLISION`.
|
|
461
|
+
- `landmarks` city rule (0..0.5, default 0): the share of buildable blocks given whole to one civic landmark (hall, arena, market or campus, picked by zone band). `CityLot.landmark` names it.
|
|
462
|
+
- `ResolvedCity.network`: the street graph the district grew from (plain data).
|
|
463
|
+
- `streetElevationField(rules, hx, hz)` (`@jgengine/core/roads/streetGenerator`): the road-surface field a network was draped over, rebuilt from seed, extents and the elevation dial.
|
|
464
|
+
- `GraphBlockRings.curb` and `GraphBlockEdge.sidewalk`.
|
|
465
|
+
- `AudioEngine.snapshot()`/`restore(next)` hand out and take back the player's mix (master and bus volume and mute, the heard space), and `EventFeedback.snapshot()` with `reset(next)` carries the cue cooldowns into a fresh seam.
|
|
466
|
+
- `arrangeHudPanels` (`@jgengine/core/hud/hudPanelLayout`) places HUD panels by anchor, order and priority so none overlap: stacked panels slide to the nearest free spot, lower priorities fold into a toggle, and the HUD shrinks only when nothing else fits.
|
|
467
|
+
- `HudPanel` `chip` names the toggle a panel folds into on any display. Opening the toggle shows the panel beside it.
|
|
468
|
+
- `useRegisterLayoutRegion` takes a `placement` key; a new key re-measures a region that moved without resizing.
|
|
469
|
+
- `uploadInstances(mesh, source, options)` (`@jgengine/shell/instancing/instanceUpload`): writes a list of matrices or a `{ count, at }` writer into a mesh, grows its buffers when needed, and reports the count to the draw probes. Knobs: `localMatrix` (a model part's own transform), `colors` (list or writer), `bounds` (`"sphere"`, `"boxes"` with `boundsPad`, a known `Box3`, or `"none"`), `dynamic` (per-frame buffers).
|
|
470
|
+
- `composeInstance(out, position, rotation, scale)`: one instance matrix from plain arrays.
|
|
471
|
+
- `partitionInstances(geometry, matrices, { localMatrix, cellSize })` (`@jgengine/shell/instancing/instanceChunks`): splits a batch into XZ cells that cull as units; a batch that fits one cell stays one draw.
|
|
472
|
+
- `groupInstancesByCell(items, cellSize, at)` (`@jgengine/core/instancing/instanceCells`): the plain cell grouping both scatter and buildings use.
|
|
473
|
+
- `bun run shoot --load <game> --guard`: builds only the /play runner (apps/dev reads package source, ~40 s), opens `/play/?game=<id>` in a fresh Chrome profile on the real GPU, and fails when the worst freeze or scene ready is worse than `scripts/load-baseline.json` beyond noise (worst freeze: 1.5x or +400 ms; scene ready: 1.3x or +1.5 s). A failing game gets one more cold run first. Baselines are per GPU; a game without one is skipped with a hint. `--save-baseline` records games. Never runs in CI (it skips when `CI` is set).
|
|
474
|
+
- `verify:fast`: for the games a change boots (render, boot or game changes, per the #2500 scope), a `load guard <game>` step runs alone on the machine, cached by the same inputs as the boot probe.
|
|
475
|
+
- `HudSlots` and `HUD_CORNER_WIDTH` (`@jgengine/react/hud/hudLayout`): a play HUD's eight edge slots on a `HudCanvas`, with the layout saved per game.
|
|
476
|
+
- `@jgengine/react/hud/kitHud`: `KitVitals` (meters plus the labeled objective), `KitWaveClock`, `KitHint`, `KitActionBar` (keyed build/action buttons with costs, progress and the interact prompt), `KitCount` and `KitCounts`.
|
|
477
|
+
- Nine games that were playable but missing from the site (Apex Nights, Briarcrown, Fernwild, Hold the Line, Orbit Watch, Outpost Zero, Red Frontier, Saltwick, The Sunken Crown) get a `games/README.md` row and a cover.
|
|
478
|
+
- Game cards show the one-line idea a game started from, read from its README.
|
|
479
|
+
- `readBuildEnv()` (`@jgengine/core/devtools/saveEndpoint`, internal): the one reader of the bundler's build flags (`DEV`, `PROD`, `NODE_ENV`, `VITE_JGENGINE_DEV_PROXY`), each read by its literal name.
|
|
480
|
+
- `createGameFlowSystem` (`@jgengine/core/game/gameFlow`): title, play, pause and autosave checkpoints as one system, with the `pause` and `start` commands. `openTitle`, `playGame`, `newGame`, `continueGame`, `togglePause` and `saveNow` drive it from menus and result screens.
|
|
481
|
+
- `GameFlowUI` (`@jgengine/react/menu/gameFlowMenus`): the title menu, the HUD while playing and the pause menu over it. The game passes its HUD and its menu words (`name`, `tagline`, `pitch`, `accent`, `controls`, `briefing`, `credits`). `startNewGame` starts a fresh run in a browser page, reloading when a run already played.
|
|
482
|
+
- `planParticleBudget` takes `{ effectCap }`: each request is clamped to it, an entry with no `requested` takes the whole effect cap, and every clamped request keeps at least one particle. An `Infinity` total cap admits every request.
|
|
483
|
+
- `buildTrimmedIntersections` nodes mid-span crossings itself, and takes per-street `elevation` and per-junction `elevation`.
|
|
484
|
+
- `IntersectionMarkingIntent.crosswalk` with `crosswalkBars`, `crosswalkBarWidth`, `crosswalkDepth` and `crosswalkGap` on `buildIntersectionMarkings`: zebra crossings beyond the stop line.
|
|
485
|
+
- `WaterSurfaceConfig.direction` (heading of the longest wave, degrees) and `steepness` (crest sharpness, 0..1 of the no-loop limit).
|
|
486
|
+
- `waterSurfaceConfigFromDescriptor`; `ResolvedWater.surface` carries an authored water volume's wave model.
|
|
487
|
+
|
|
488
|
+
### Changed
|
|
489
|
+
|
|
490
|
+
- fernwild, hold-the-line, briarcrown, orbit-watch, red-frontier and saltwick draw their build or seed strip with `ActionBar look="kit"`. In saltwick the seeds you have not picked read as normal buttons instead of greyed out (greyed now only means unavailable).
|
|
491
|
+
|
|
492
|
+
Verification:
|
|
493
|
+
- `bun test packages/react/src/hud packages/core/src/hud packages/core/src/ui games/deepward/src/game/ui/hudLanes.test.ts`: pass
|
|
494
|
+
- `bun scripts/check-types-all.ts` and the six games' `check-types`: pass
|
|
495
|
+
- fernwild in the browser: the build strip shows each structure with its cost and key, greyed when unaffordable, and clicking one starts building
|
|
496
|
+
|
|
497
|
+
## Bends
|
|
498
|
+
|
|
499
|
+
- `kitHud.test.tsx` lost its `KitActionBar` test with the component; the same behaviour (detail text, unaffordable as ghost, meters, prompt above) is asserted on `ActionBar look="kit"` in `actionHud.test.ts`.
|
|
500
|
+
- The `hudBlockOverlaps` tests in `hudLanes.test.ts` now run the same cases through `detectLayoutCollisions`; no case was dropped.
|
|
501
|
+
- City blocks are traced from the exact street graph instead of welding centerlines, so curved and filleted streets still close their blocks. Every city closes more blocks and places more buildings (the default district goes from 184 to 235 lots; Manhattan from 134 to 225). Apex Nights, Briarcrown, Ember Command, Hold the Line and Saltwick place city volumes and get the fuller fabric.
|
|
502
|
+
- Playground: "Lot frontage", "Lot depth" and "Plot variety" become one "Lot scale" dial (`?lotScale=`); "Landmarks" goes up to 0.5.
|
|
503
|
+
|
|
504
|
+
Verification:
|
|
505
|
+
- `bun test packages/core/src/city packages/core/src/roads packages/core/src/scatter packages/core/src/world apps/web/src/live apps/web/src/routes/-playground.test.ts`: pass
|
|
506
|
+
- `bun run check types`, `check concept-homes --all` (only the known TerrainMaterial): pass
|
|
507
|
+
- Playground in the browser: city and street race grow with buildings, an arena landmark, and the lap with kerbs and barriers
|
|
508
|
+
- `shoot apex-nights` and `shoot hold-the-line`: load and render normally
|
|
509
|
+
|
|
510
|
+
## Bends
|
|
511
|
+
|
|
512
|
+
- `cityKind.test.ts` pins the default district's lot counts; the pins move from 184/189 to 235/252 because the fabric now closes more blocks, which is the intended change.
|
|
513
|
+
- `cityLayout.ts` stays: it is Harbor Heat's authored road classes over the shared lot deriver, not a generator.
|
|
514
|
+
- Drop the i-have-adhd plugin from the repo settings; nothing on this PC uses it
|
|
515
|
+
- `check concept-homes` scans `games/*/src`, `games/*/packages/*/src` and `games/shared`. Each game is its own namespace (two games may each have a `Hud`); a clash with an engine export fails. The names a game's entry files hand the engine (`game`, `world`, `editorLayers`, `editorCatalogs`, `uiScenario` in top-level `src/` files) are the contract and pass.
|
|
516
|
+
- Games use the engine where theirs was a copy: `UiKitName` (`@jgengine/react/uiKits`) in the nine idea games, `FogBounds` in ember-command, `Vec2` in beacon-bastion, `clamp` in odd-orbit (`clampNeed` wraps it), and `openTitle` (`@jgengine/core/game/gameFlow`) inside beacon-bastion's `openKeepTitle` and deepward's `openMarrowTitle`.
|
|
517
|
+
- Game-specific things got their own names, e.g. harbor-heat `BeltHotbar`, scrap-signal `WeaponHotbar`, ember-command `TacticalMinimap`/`GlyphIcon`/`factionDamage`, deepward `VisorReticle`/`CommsLine`/`groundDistance`/`tickControls`, brightway-park `CoasterStation`/`tileAt`/`snapToTile`, the idea games' `campWorld`, and llmville's presence aliases (`WorldPresenceTransport`, …), economy (`playerCanAfford`, …) and settings (`GraphicsPreset`).
|
|
518
|
+
- `games/shared`: one `dirHasEntries` in `assetStore.ts`, used by `provisionAssets.ts` (was a private copy, and named like the assets CLI's `isPopulated`).
|
|
519
|
+
|
|
520
|
+
Verification:
|
|
521
|
+
- `bun scripts/check-concept-homes.ts --all`: no game entries; only the world/render names remain
|
|
522
|
+
- every game's `check-types`: pass
|
|
523
|
+
- `bun test` on the touched games, `games/shared` and `scripts/check-concept-homes.test.ts`: pass
|
|
524
|
+
|
|
525
|
+
## Bends
|
|
526
|
+
|
|
527
|
+
- None. The entry-export allowance covers only the five names the template writes into top-level `src/` files; the same name anywhere else in a game still fails (tested).
|
|
528
|
+
- A ground over 66k vertices splits into 128-cell tiles, each a `THREE.LOD` at strides 1/2/4/8/16. Scene load builds only the coarsest level of every tile plus what the camera needs nearby; finer levels build in the background as the camera reaches them. Each level drops a skirt below its edges, so neighbouring tiles at different detail show no crack. Normals come from the sampled heights, so a tile edge lights the same from both sides.
|
|
529
|
+
- A pulled terrain scan that may be missing no longer meshes the ground twice (once for the procedural stand-in, again when its maps load): the same mesh swaps material.
|
|
530
|
+
- A compatible prop alone in its 128 m cell joins the nearest batch of its model, and a model with no cell batch gathers its lone props into one. Drawn as a plain mesh it linked a second, non-instanced program for every material the batched copies already had (harbor-heat start: 112 programs, now 107).
|
|
531
|
+
|
|
532
|
+
Measured on this RTX 3070 (/play runner, cold, shared machine):
|
|
533
|
+
|
|
534
|
+
| | before | after |
|
|
535
|
+
|---|---:|---:|
|
|
536
|
+
| drift-foundry ground vertices built for scene ready | 438k (twice with a pulled scan) | ~7k + near tiles |
|
|
537
|
+
| drift-foundry height samples on the main thread | 1.7 s | 1.0 s |
|
|
538
|
+
| drift-foundry scene ready | 9.3–10.9 s | 9.3–10.0 s |
|
|
539
|
+
| harbor-heat programs at start | 112 | 107 |
|
|
540
|
+
|
|
541
|
+
Ground meshing no longer appears in drift-foundry's long tasks. Its scene ready is now held by shader warmup and model setup, plus the water depth plane and road ribbons sampling the yard's height field (about 1 s of early hitches).
|
|
542
|
+
- `check concept-homes` no longer counts `export const …` lines inside a generator's template literal (assets `indexGen`/`spriteIndexGen`) as definitions.
|
|
543
|
+
|
|
544
|
+
Verification:
|
|
545
|
+
- `bun scripts/check-concept-homes.ts --all`: 25 names left, all world/render
|
|
546
|
+
- `bun scripts/check-types-all.ts`: 23 dirs, 0 failing; every game's `check-types`: pass
|
|
547
|
+
- `bun test` on threshold, stats, vec2/vec3, waypoints, ui, movement, combat, nav, ws, node, convex, shell audio: 1693 pass
|
|
548
|
+
|
|
549
|
+
## Bends
|
|
550
|
+
|
|
551
|
+
- None. The concept-homes template-literal skip removes false positives only (text a generator prints is not a definition); it has its own test.
|
|
552
|
+
- `HudCanvas` places every panel with `arrangeHudPanels` instead of nine independent anchor regions, so panels in different anchors no longer run into each other on phone and tablet screens.
|
|
553
|
+
- Beacon Bastion, Deepward, Harbor Heat, Odd Orbit and Scrap Signal declare one anchor and a priority per panel; their per-size placements are gone.
|
|
554
|
+
- Ember Command draws the ledger, orders note, pause seal, survey map, Bram's card, command board, order hints and level-up writ as `HudCanvas` panels with one anchor and a priority each, instead of hand-placed CSS. The map folds into a toggle when there is no room for it rather than being hidden below 1180px, and the hand-registered layout regions are gone because every panel registers its own.
|
|
555
|
+
- Brightway Park and Drift Foundry draw their HUD as `HudCanvas` panels with one anchor and a priority each, instead of hand-placed CSS. At 800x450 Brightway's build sheet no longer covers the park card, and Drift Foundry's gate sign and compactor bar no longer run into the build rack and route strip. Their per-size position rules are gone.
|
|
556
|
+
- `StaticShapeInstances` moved to `@jgengine/shell/instancing/staticShapeInstances`; `placeAlongPath` moved to `@jgengine/core/instancing/pathInstances`.
|
|
557
|
+
- Scatter, impostor scatter, buildings, building kits, city furniture/trees/vehicles/signals, pole lines, interior decals, fire spread, the living-world flock, `DataObjects`, `GridWorldScene` and `TileLayerRenderer` draw through `uploadInstances`.
|
|
558
|
+
- Beacon Bastion, Deepward and LLMVille import the new homes; Deepward's station batches and LLMVille's endless datacenter upload through `uploadInstances`.
|
|
559
|
+
- Textures load as GPU-compressed KTX2 (Basis UASTC) when the server has a compressed twin. `bun run games:compress` encodes twins of every game's model and material textures into the shared asset store (`.asset-store/.ktx2/`). The step runs on its own, in the background at idle priority, when a game's dev server or the dev runner starts, and never runs in CI. A server without twins sends the original files, so nothing changes in CI or on a fresh clone.
|
|
560
|
+
- The Basis transcoder and the Draco decoder now ship with the game's own build instead of loading from a CDN. `configureModelLoaders()` with no paths uses the bundled copies; passing a folder path still works.
|
|
561
|
+
- Roads and junctions stay hidden until their scanned asphalt has loaded. Before, they linked a procedural program first and then relinked once the scan arrived. Shader warmup also re-applies the scene's shadow and wetness patches to a material swapped in after warmup was planned, so that material links once, already patched.
|
|
562
|
+
- Browser test fixtures understand `?url` imports, the way Vite does.
|
|
563
|
+
|
|
564
|
+
Measured on this RTX 3070 (/play runner, cold, best of 2):
|
|
565
|
+
|
|
566
|
+
| | without twins | with twins |
|
|
567
|
+
|---|---:|---:|
|
|
568
|
+
| scrap-signal scene ready | 9.5 s | 8.9 s |
|
|
569
|
+
| apex-nights scene ready | 6.2 s | 5.5 s |
|
|
570
|
+
| scrap-signal 2048² uploads | 16 decoded images on the main thread | 15 compressed uploads, 11 ms in total |
|
|
571
|
+
|
|
572
|
+
| harbor-heat roads | before | after |
|
|
573
|
+
|---|---:|---:|
|
|
574
|
+
| programs linked | 120–122 | 113 |
|
|
575
|
+
| scene ready | 11.3–16 s | 9.4–10.1 s |
|
|
576
|
+
|
|
577
|
+
The load guard baselines were tightened wherever a game got faster; none were loosened.
|
|
578
|
+
|
|
579
|
+
Handed to the world builder: drift-foundry's ground, and models drawn both instanced and plain (the duplicate programs).
|
|
580
|
+
- Models that point at the same texture file share one image: each model file used to decode and upload its own copy, so a kit of forty wall pieces on one trim sheet uploaded that sheet forty times (scrap-signal: 165 uploads of 23 images, about 2.4 s of main thread, now 16 uploads, about 0.16 s).
|
|
581
|
+
- Lifting a menu that covers the canvas (pressing Play) compiles what the scene gained while covered before the frame loop draws again.
|
|
582
|
+
- For a few seconds after the reveal or a lifted cover, a frame that would link a new program mid-draw (a level the Play press spawns from the game loop) is skipped instead: the canvas keeps its last image while the warmup links the new content off the frame, and the loading cover comes back only when that takes over 0.4 s.
|
|
583
|
+
- Ground ribbons and see-through double-sided model materials (window glass, leaf cards) draw in one pass, which drops their second back-face program and draw.
|
|
584
|
+
|
|
585
|
+
Measured on this RTX 3070 (/play runner, cold, busy shared machine):
|
|
586
|
+
|
|
587
|
+
| | before | after |
|
|
588
|
+
|---|---:|---:|
|
|
589
|
+
| scrap-signal 2048² texture uploads | 165 (2.4 s main thread) | 16 (0.16 s) |
|
|
590
|
+
| scrap-signal scene ready | 12.0 s | 9.0 s |
|
|
591
|
+
| briarcrown longest freeze after Play | 2.6 s | none over 100 ms (about 0.6 s "Preparing scene" cover) |
|
|
592
|
+
| red-frontier longest freeze after Play | 3.5 s | none over 100 ms |
|
|
593
|
+
| outpost-zero / saltwick / fernwild / orbit-watch after Play | 0.85 / 0.98 / 0.59 / 0.40 s | none over 100 ms |
|
|
594
|
+
| programs (saltwick / briarcrown / harbor-heat) | 89 / 83 / 127 | 82 / 78 / 123 |
|
|
595
|
+
- Terrain and road shaders compile about 5x faster on Direct3D: their noise reads a small shared lattice texture instead of an integer hash the HLSL compiler inlined hundreds of times.
|
|
596
|
+
- The ground mesh builds from a padded height grid sampled once per vertex (no repeated route projections for slopes), and the terrain mounts a shared stand-in until its geometry is ready, so the warmup no longer waits on it.
|
|
597
|
+
- Shader warmup: draws are grouped by the program three actually linked rather than by material, texture uploads happen under the cover for the first view (and only the cut-out maps of off-screen shadow casters), the rest upload one per frame afterwards, and a post-processing chain the covered frame loop never ran is linked under the cover too (`warmupDraws`).
|
|
598
|
+
- Image textures (surface scans, authored material maps, building surfaces, terrain detail maps) decode off the main thread through one `ImageTextureLoader`, so their upload no longer decodes a large JPEG inside `texImage2D`.
|
|
599
|
+
- Grass fills its view over a few frames (8 ms slices, nearest first) instead of baking the whole field in one frame.
|
|
600
|
+
- A cycling sky keeps its moon light mounted (dark by day). Mounting it as the moon crossed the horizon changed every lit program, so the whole scene relinked, an 8 s freeze in saltwick as play starts at dawn.
|
|
601
|
+
- Thousands of idle `useFrame` subscriptions (foot IK, glance, animation, paint) now subscribe only while they have work, through `useFrameWhile`.
|
|
602
|
+
- The surface-scan maps hold the warmup until they load, so it links the scanned road programs rather than the procedural ones they replace.
|
|
603
|
+
|
|
604
|
+
Cold loads on this RTX 3070 (/play runner, fresh profile, single runs on a shared machine, so about ±20% noise; ms):
|
|
605
|
+
|
|
606
|
+
| game | scene ready, first measured | before this change | after | worst click before | after |
|
|
607
|
+
|---|---:|---:|---:|---:|---:|
|
|
608
|
+
| apex-nights | 12343 | 16005 | 6153 | 1196 | 364 |
|
|
609
|
+
| beacon-bastion | 11513 | 19153 | 5288 | 1125 | 780 |
|
|
610
|
+
| briarcrown | 8801 | 12740 | 4817 | 973 | 529 |
|
|
611
|
+
| brightway-park | 13879 | 18148 | 5470 | 569 | 490 |
|
|
612
|
+
| deepward | 4467 | 5599 | 4180 | 627 | 536 |
|
|
613
|
+
| drift-foundry | never | 31243 | 9652 | 1945 | 815 |
|
|
614
|
+
| ember-command | 10283 | 15926 | 5214 | 849 | 555 |
|
|
615
|
+
| fernwild | 11172 | 9653 | 5818 | 1372 | 434 |
|
|
616
|
+
| harbor-heat | 29142 | 17006 | 9469 | 798 | 1072 |
|
|
617
|
+
| hold-the-line | 10362 | 13050 | 5743 | 1886 | 416 |
|
|
618
|
+
| odd-orbit | 11590 | 15429 | 6645 | 907 | 589 |
|
|
619
|
+
| orbit-watch | 11224 | 11007 | 4411 | 726 | 490 |
|
|
620
|
+
| outpost-zero | 10124 | 9598 | 4800 | 616 | 459 |
|
|
621
|
+
| red-frontier | 10898 | 10842 | 4149 | 926 | 477 |
|
|
622
|
+
| saltwick | 17563 | 10425 | 5531 | 820 | 362 |
|
|
623
|
+
| scrap-signal | 28709 | 21497 | 11928 | 1544 | 763 |
|
|
624
|
+
| the-sunken-crown | 12275 | 11295 | 6344 | 2019 | 460 |
|
|
625
|
+
|
|
626
|
+
Still over 6 s: drift-foundry (its ground is 438k vertices and the first view draws about 3,400 objects), scrap-signal (about 165 textures at 2048x2048 to upload, roughly 11 ms each; compressed or smaller textures are the fix) and harbor-heat (121 shader programs). After Play, briarcrown and red-frontier still link a few new materials on the first frame (about 1-2 s).
|
|
627
|
+
- Loading cover progress no longer re-renders the scene. The warmup reported progress through React state in the component that renders the canvas, so every report re-rendered the whole 3D tree (12,000+ components in drift-foundry, several times a second) and starved the warmup it was reporting on. Progress now goes through a small store the cover subscribes to.
|
|
628
|
+
- Placed objects measure their collision mesh once per catalog kind and model, not once per placed instance (drift-foundry walked about 2,000 clones of the same models on load). Batched static props measure each source once and keep their variant levels across membership changes instead of re-measuring every render (harbor-heat).
|
|
629
|
+
- Shader warmup: instanced, batched and skinned meshes are judged in or out of view by the same bounds the renderer culls with, so the first view's programs go first; shadow casters outside the view get their depth programs with the first view and their lit programs after.
|
|
630
|
+
- Ground that is already wet when it loads is patched before the warmup compiles it, instead of compiling dry and then again wet (drift-foundry compiled its 100 KB terrain shader twice, about 3 s each).
|
|
631
|
+
- `bun run shoot --load --guard` and `--save-baseline` stop at scene ready instead of pressing Play and waiting for playable. One game's guard run, build included, takes about 40 s; all 17 games measure in three batches of 1-2.5 minutes with `--no-build`.
|
|
632
|
+
|
|
633
|
+
Cold loads on this RTX 3070 (/play runner, fresh profile, NVIDIA DXCache cleared before each batch; ms, single runs on a shared machine, so about ±20% noise):
|
|
634
|
+
|
|
635
|
+
| game | scene ready before | after | worst freeze before | after |
|
|
636
|
+
|---|---:|---:|---:|---:|
|
|
637
|
+
| apex-nights | 12343 | 9960 | 928 | 904 |
|
|
638
|
+
| beacon-bastion | 11513 | 9625 | 739 | 760 |
|
|
639
|
+
| briarcrown | 8801 | 8111 | 531 | 525 |
|
|
640
|
+
| brightway-park | 13879 | 14648 | 543 | 540 |
|
|
641
|
+
| deepward | 4467 | 4477 | 552 | 561 |
|
|
642
|
+
| drift-foundry | never (timed out at 86 s) | 26700 | 2769 | 2182 |
|
|
643
|
+
| ember-command | 10283 | 10158 | 705 | 761 |
|
|
644
|
+
| fernwild | 11172 | 11505 | 1555 | 1340 |
|
|
645
|
+
| harbor-heat | 29142 | 16286 | 1738 | 778 |
|
|
646
|
+
| hold-the-line | 10362 | 10130 | 1558 | 1473 |
|
|
647
|
+
| odd-orbit | 11590 | 14110 | 748 | 675 |
|
|
648
|
+
| orbit-watch | 11224 | 10196 | 864 | 769 |
|
|
649
|
+
| outpost-zero | 10124 | 10373 | 787 | 645 |
|
|
650
|
+
| red-frontier | 10898 | 11194 | 729 | 728 |
|
|
651
|
+
| saltwick | 17563 | 10223 | 816 | 526 |
|
|
652
|
+
| scrap-signal | 28709 | 23176 | 871 | 1016 |
|
|
653
|
+
| the-sunken-crown | 12275 | 9201 | 1952 | 1782 |
|
|
654
|
+
|
|
655
|
+
Scene ready is still over 5 s for every game but deepward. What is left is the GPU compile itself: ANGLE on Direct3D 11 compiles 50-120 programs per game cold, about 0.3-0.8 s each with several in flight, and a procedural terrain program (about 100 KB of HLSL) takes about 3 s on its own. drift-foundry also waits for its terrain to finish building, about 14 s into the load.
|
|
656
|
+
- Terrain detail shader: each blended slot inlines the procedural surface function once instead of three times (rock, paint layer and base each had their own call), so the terrain program is a third the code for the HLSL compiler. Same pixels.
|
|
657
|
+
- Baselines recorded for deepward and harbor-heat on the RTX 3070.
|
|
658
|
+
|
|
659
|
+
Guard proof: planting a 2.5 s main-thread stall in deepward's room lights made `bun run shoot --load deepward --guard` exit 1:
|
|
660
|
+
`deepward: worst freeze 2541 ms, baseline 636 ms, limit 1036 ms` and `deepward: scene ready 6407 ms, baseline 4871 ms, limit 6371 ms`.
|
|
661
|
+
|
|
662
|
+
Cold loads on current beta (site build, fresh profile, NVIDIA DXCache cleared before each game; ms):
|
|
663
|
+
|
|
664
|
+
| game | worst freeze | scene ready |
|
|
665
|
+
|---|---:|---:|
|
|
666
|
+
| apex-nights | 839 | 10863 |
|
|
667
|
+
| beacon-bastion | 838 | 19448 |
|
|
668
|
+
| briarcrown | 576 | 18059 |
|
|
669
|
+
| brightway-park | 720 | 17949 |
|
|
670
|
+
| deepward | 701 | 5489 |
|
|
671
|
+
| ember-command* | 945 | 26008 |
|
|
672
|
+
| fernwild* | 1266 | 26825 |
|
|
673
|
+
| harbor-heat* | 2405 | 33325 |
|
|
674
|
+
| odd-orbit* | 1076 | 18293 |
|
|
675
|
+
| orbit-watch* | 726 | 10319 |
|
|
676
|
+
| outpost-zero* | 834 | 11332 |
|
|
677
|
+
| red-frontier* | 776 | 11059 |
|
|
678
|
+
|
|
679
|
+
\* measured while a second load run overlapped on the GPU, so these read slow.
|
|
680
|
+
After (runner build, cold): deepward 636 / 4871, harbor-heat 2168 / 42084 (other agents' test runs shared the machine).
|
|
681
|
+
|
|
682
|
+
Not done: scene ready is still 10–40 s for most games; the shader compile cost per program and the drift-foundry / harbor-heat main-thread freezes were not traced; drift-foundry, hold-the-line, saltwick, scrap-signal and the-sunken-crown have no clean measurement.
|
|
683
|
+
|
|
684
|
+
Verification:
|
|
685
|
+
- `bun test scripts/load-check.test.ts scripts/verify-fast.test.ts packages/shell/src/terrain/terrainDetailMaterial.test.ts`: pass
|
|
686
|
+
- `bun run shoot --load deepward --guard` with a planted stall: exit 1 (above)
|
|
687
|
+
|
|
688
|
+
## Bends
|
|
689
|
+
|
|
690
|
+
- None.
|
|
691
|
+
|
|
692
|
+
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
693
|
+
|
|
694
|
+
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
695
|
+
- `Ocean` (`@jgengine/shell/water`) animates its waves on the game clock (`ctx.time.advancedSeconds()`), so the sea stops when the game pauses, follows slow motion and lines up with the water surface buoyancy samples. Before any game time has passed (a title screen), or with no game (an editor preview), it runs on real time.
|
|
696
|
+
- Ocean glints follow the sun the mounted `Sky` placed, through the day-night cycle, and fade out once the sun sets; without a sky they keep the fixed day heading. `syncOceanMaterial` takes the sun direction as an optional fourth argument.
|
|
697
|
+
- Every game's HUD runs on the one kit: the nine starter games drop their own `HudFrame` copies for `HudSlots` and the `kitHud` blocks, and the seven frame games draw their frames through `KitPanel`/`KitButton`.
|
|
698
|
+
- Home is rebuilt around the builder: one-command start, a HUD that fits phones with touch controls, saves, walking/driving/flying, physics with an optional Rapier backend, systems with knobs, load numbers and agent tooling. Games are proof in the background, not the headline.
|
|
699
|
+
- The home page's code lives in `apps/web/src/snippets/` as real files that `apps/web` check-types compiles against the engine source, so a snippet that stops building fails CI.
|
|
700
|
+
- New theme: Archivo display type, an editor-style grid, orange accent, axis-tick eyebrows; both colour schemes and phone width reviewed. Bricolage Grotesque is replaced by Archivo.
|
|
701
|
+
- Every page's copy rewritten plainly and checked against the code: games, game page, playground, editor, capabilities (now with the blueprints), why, adopt, 404, site title and description.
|
|
702
|
+
- Load numbers on the site come from `scripts/load-baseline.json` (`apps/web/src/lib/siteFacts.ts`), not hand-typed.
|
|
703
|
+
- `isProductionEnvironment` and `proxiedUrl` read their flags through `readBuildEnv`.
|
|
704
|
+
|
|
705
|
+
Verification:
|
|
706
|
+
- In Vite's server module runner, the old `saveEndpoint.ts` throws "Dynamic access of import.meta.env is not supported"; the new one returns `isProductionEnvironment() === false` and `proxiedUrl` works.
|
|
707
|
+
- `bun test packages/core/src/devtools packages/core/src/data packages/core/src/random` and `bun run check types`: pass
|
|
708
|
+
- Playground and home page in the web dev server: rendered on the server, no page errors.
|
|
709
|
+
- `packages/core/src/devtools/buildEnv.test.ts`: fails on the old reader (`import.meta as … .env`), passes now; it scans core and shell source so no whole-object `import.meta` read comes back.
|
|
710
|
+
- `jgengine sync` takes over everything `upgrade` did: workspace catalog updates (keeping range prefixes, a held editor override and each game's `catalog:` declarations; refusing a downgrade), `--check` for CI, and release notes for the span crossed.
|
|
711
|
+
- `shoot` and `drive` always run the CLI's own bundled harness. When nothing is serving the game, the harness starts the project's vite through its JS entry with the current Node, so it no longer fails with `spawn EINVAL` on Windows.
|
|
712
|
+
- The in-repo template (`jgengine create --in-repo`) writes the games' shared wiring: `vite.config.ts` on `games/shared` (`engineSource`, `viteProvisionAssets`, `viteIdleExit`; it used to import a `devSavePlugin` from `apps/dev` that no longer exists), a `tsconfig.json` that extends `../tsconfig.base.json`, an `index.css` that scans the engine's source (the games scanned `node_modules/@jgengine/*/dist`, which is not built in the repo), and a `main.tsx` that passes `gameId="<id>"`. It no longer writes `.gitignore` or `AGENTS.md`: an in-repo game has the monorepo's.
|
|
713
|
+
- Every in-repo game but llmville sets `"template": "3d"` in `jgengine.json`, so the `games-sync` gate keeps its wiring current. A game lists in `owns` the files that are really its own: a richer `index.html` (description, social tags), `index.tsx` with more entry exports (`editorCatalogs`, `uiScenario`), brightway-park's evidence plugin and ember-command's fixed port in `vite.config.ts`, and deepward's own entry. llmville keeps every file (no template).
|
|
714
|
+
- Games gained the template's `drive` and `shoot` scripts. drift-foundry's two page rules moved from `index.css` to `style.css`.
|
|
715
|
+
|
|
716
|
+
Verification:
|
|
717
|
+
- `bun packages/jgengine/src/cli/index.ts sync --check games/*`: every game up to date
|
|
718
|
+
- `bun scripts/check-types-all.ts`: 23 dirs, 0 failing
|
|
719
|
+
- `bun test packages/jgengine scripts/scaffold-editor.test.ts`: 387 pass
|
|
720
|
+
- fernwild and odd-orbit run from their own folder (`bun run dev`): styled title menu, fernwild's HUD and build strip in play
|
|
721
|
+
|
|
722
|
+
## Bends
|
|
723
|
+
|
|
724
|
+
- `templates.test.ts`: the "in-repo tsconfig paths match the exact map doctor requires" test went with the map; it is replaced by a test that the in-repo wiring extends the base tsconfig, runs on `games/shared` and names the game by id. The AGENTS.md test now runs on the standalone template only, since the in-repo template no longer writes one.
|
|
725
|
+
- Moved to `@jgengine/core/terrain/`: `terrain` (was `world/terrain`), `terraform` (was `world/terraform`), `pathTerrain` (was `world/pathTerrain`), `terrainGuides` (was `world/terrainGuides`), `terrainGrade` (was `movement/terrainGrade`), and `terrainPaint` (was `editor/terrainMaterials`).
|
|
726
|
+
- `terrainGroundColorSampler` moved to `@jgengine/shell/terrain/terrainGroundColor` (was `environment/terrainGroundColor`).
|
|
727
|
+
- The editor's paint palette is `TerrainPaintPreset` / `TERRAIN_PAINT_PRESETS` / `TERRAIN_PAINT_COLORS` (was `TerrainMaterial` / `TERRAIN_MATERIALS` / `TERRAIN_MATERIAL_COLORS`), imported from `@jgengine/core/terrain/terrainPaint`. `@jgengine/core/editor` and `@jgengine/editor` no longer re-export it.
|
|
728
|
+
- Every importer in packages, apps and games points at the new paths. The terrain and road shader work from #2544 (shared noise texture, height-grid ground mesh, grouped warmup) is untouched; only import paths changed.
|
|
729
|
+
- The nine games that carried identical `flow.ts` and `Menus.tsx` copies now use `createGameFlowSystem` and `GameFlowUI`.
|
|
730
|
+
- The API orphan ratchet treats a symbol whose file moved folders as the same entry (package + name), so moving a module no longer needs `@internal` to pass.
|
|
731
|
+
- `bun run shoot --load --guard` reads the scene canvas back over the two seconds after scene ready and fails a scene that shows fewer than 40 coarse colours: `ember-command: the scene drew blank at scene ready (13 colours; 251 WebGL errors logged)`. Drawn scenes show 80-240; plain beta's ember-command and briarcrown showed 11-18 and fail. Each run also logs its colour count and WebGL error count.
|
|
732
|
+
- Baselines re-recorded for all 17 games now that every scene draws (a scene whose draws all failed loaded faster than a real one).
|
|
733
|
+
- Authored `road()` junctions in the shell now use the same builder as generated streets, so paved crossings get stop lines and zebra crossings from one place. A road authored with `markings: false` no longer gets stop lines or zebra bars painted at its crossings (Scrap Signal's dirt roads had them).
|
|
734
|
+
|
|
735
|
+
### Fixed
|
|
736
|
+
|
|
737
|
+
- A `HudPanel` that has not been moved takes its `anchor` on every render. It used to keep the anchor it first mounted with, so a game that picks its anchor per display profile stayed where it started after a resize.
|
|
738
|
+
- A layout region is re-measured when the game viewport's DOM changes. A panel that opens beside another one no longer leaves the neighbour's old rectangle in the overlap report.
|
|
739
|
+
- Drift Foundry's `play` and `mid-run` capture staging now hands the wheel to the course autopilot. The capture keeps the sim running while models stream, and the compactor caught the parked kart within seconds, so `shoot drift-foundry --mode play` filmed "caught you" every time.
|
|
740
|
+
- Shell play-surface focus: a key that unmounts the focused control (Escape closing the pause menu) now returns focus to the play surface, the same way a click on an unmounting "Play" button already did. Every game gets it.
|
|
741
|
+
|
|
742
|
+
Root cause: gameplay keys are read on the shell's focusable wrapper. Closing the pause menu with Escape removed its focused button, Chromium dropped focus to `<body>` without a `focusout`, and the focus reclaim only watched clicks.
|
|
743
|
+
|
|
744
|
+
Verification:
|
|
745
|
+
- `bun test packages/shell/src/playSurfaceFocus.test.ts`: pass (new test fails without the fix)
|
|
746
|
+
- fernwild in the browser: New Game, Escape opens pause, Escape closes it, focus is back on the play surface, Escape opens pause again (before: focus stayed on `<body>` and pause did not reopen).
|
|
747
|
+
|
|
748
|
+
## Bends
|
|
749
|
+
|
|
750
|
+
- None.
|
|
751
|
+
- `jgengine find <feature>` answers by folder and file names once a folder is named for the query: an export that only mentions the word (`MAX_OCEAN_WAVES` for "waves") no longer shows `shell/water` as a home for waves.
|
|
752
|
+
- `shoot` and `drive` `--size half` keep the page's full CSS viewport and halve only the pixel ratio, so a half-size shot lays out exactly like a full one. Before, the 800x450 viewport switched games into their compact HUD and the layout-collision check failed the run (Scrap Signal, Beacon Bastion).
|
|
753
|
+
- `EntityPreview` no longer throws "Cannot read properties of null (reading 'addEventListener')" when it unmounts while react-three-fiber is still creating its canvas (a dialogue or menu that re-mounts a portrait in one beat, e.g. Harbor Heat's Marco dialogue).
|
|
754
|
+
- Web Audio and saved devtools overrides are browser code, so they sit in the view layer (`musicDirector.view`, `synthEngine.view`, `reverbRack.view`, `devtoolsOverrides.view`) and the model type-check no longer needs DOM types.
|
|
755
|
+
- `jgengine find` indexes the checkout once and matches each query against that index, so asking many questions of one checkout costs one read.
|
|
756
|
+
- `GameMenu` (`@jgengine/react/menu/gameMenu`): its focus effect runs a frame after the highlight changes and used to grab focus whenever focus sat outside the menu, so a Tab to a text field (or hovering an item while typing) in that frame lost the field's focus. It now only takes focus when nothing has it. This was the cause of the `menuNav.test` Tab case failing now and then in CI (#2529): with a slow runner, the menu's frame landed after the Tab.
|
|
757
|
+
|
|
758
|
+
Verification:
|
|
759
|
+
- `bun test packages/react/src/menu/menuNav.test.ts`: 11 pass, three runs in a row
|
|
760
|
+
- the new "hovering the menu while a field has focus" test fails without the fix (focus lands on the body) and passes with it
|
|
761
|
+
- waits are `requestAnimationFrame` frames, not `waitForTimeout`
|
|
762
|
+
|
|
763
|
+
## Bends
|
|
764
|
+
|
|
765
|
+
- None. No assertion was relaxed; the timer waits became frame waits and one regression test was added.
|
|
766
|
+
- A shadow-casting light whose shadow map no frame had drawn yet left every lit draw failing (WebGL: "Mismatch between texture format and sampler type"), so the scene showed only its clear colour. The shader warmup holds the frame's draws behind the loading cover, and cascaded shadows took the first held frame as their one refresh, so far cascades never got a map. A cascade now refreshes until it has a map, or when something asks for a redraw.
|
|
767
|
+
- The warmup's own draws ran before any shadow map existed, so every lit warmup draw failed and finished nothing. The first warmup draw now makes the missing maps, and the next real frame redraws them with every caster. WebGL errors on a cold load drop from 230-250 per game to under 50.
|
|
768
|
+
- Ocean and authored water drew a different wave set from the CPU `waterSurface`, so floating bodies bobbed on waves that were not on screen. Both now come from `synthesizeWaves`.
|
|
769
|
+
- Ocean crest foam reads true crest sharpness, so calm ponds no longer foam over.
|
|
770
|
+
|
|
771
|
+
### Removed
|
|
772
|
+
|
|
773
|
+
- `buildingSpatialBatch` (replaced by `partitionInstances`), `chunkScatterInstances` / `ScatterChunk` (replaced by `groupInstancesByCell`).
|
|
774
|
+
|
|
775
|
+
Bends:
|
|
776
|
+
- Left on their own writers: `InstancedBodies` and `SpriteBatch` (per-frame packed-array hot loops over thousands of bodies), `staticObjectBatches` (skips the upload when nothing moved), `ImpostorCardPool` (packs many slices into one shared mesh), `crowdBatch` / `SkinnedInstances` (the crowd concept) and `ImpactFeedback` (combat feedback, another builder's area).
|
|
777
|
+
- `GridWorldScene` now computes a culling sphere where it had none.
|
|
778
|
+
|
|
779
|
+
Verification:
|
|
780
|
+
- `bun run check types`, `bun run gen` (all stages pass), `bun test packages/shell/src packages/core/src`.
|
|
781
|
+
- Play shots: Apex Nights and Hold the Line (city lamps, trees, cars), Beacon Bastion (authored plot shapes, forest), Deepward (station batches), Fernwild (scatter) all render as before.
|
|
782
|
+
- Load guard passes for Apex Nights, Hold the Line, Deepward, Fernwild and Beacon Bastion (Beacon Bastion failed once while other heavy jobs ran on the machine, then passed twice alone).
|
|
783
|
+
- Unverified: LLMVille's endless datacenter on screen (it needs its Convex backend to run); it type-checks.
|
|
784
|
+
- `jgengine upgrade` (with `--plan`, `--apply`, `--to`, `--json`), with no alias.
|
|
785
|
+
|
|
786
|
+
## Bends
|
|
787
|
+
|
|
788
|
+
- `upgrade.test.ts` and `upgradeCatalog.test.ts` are deleted. Their changelog tests moved to `releaseNotes.test.ts`; the catalog cases (bump, `--check`, `catalog:` kept, downgrade refused) are now sync tests.
|
|
789
|
+
- Sync always targets the CLI's own version (`npx jgengine@<version> sync` picks a version). The registry-publication check and the `--to` and `--json` flags from upgrade are dropped.
|
|
790
|
+
- The harness no longer runs a project's own `scripts/shoot.mjs`/`drive.mjs` copy, so `templates.test.ts` and `harness.test.ts` now expect no `scripts/` files and the bundled harness.
|
|
791
|
+
- The harness sources moved from `templates/gameFiles.ts` to `templates/harnessScripts.ts`; `check-scaffold-launch.ts` reads them there.
|
|
792
|
+
- The duplicate `TerrainMaterial` export in `editor/terrainMaterials`.
|
|
793
|
+
|
|
794
|
+
Bends:
|
|
795
|
+
- `map/terrainMap` (bakes a top-down map image) stays with the map concept; `shell/src/terrain.ts` stays as the package's `@jgengine/shell/terrain` entry.
|
|
796
|
+
- Moved files count as new exports, so 19 terrain and terraform symbols that had no doc line got one.
|
|
797
|
+
|
|
798
|
+
Verification:
|
|
799
|
+
- `bun run check types`, `bun run gen` (all stages), `bun run check concept-homes --all` (0 duplicates), `bun test packages/core/src packages/shell/src packages/editor/src apps/web/src scripts` (11223 pass, 0 fail).
|
|
800
|
+
- `jgengine find terrain` names one core folder (`packages/core/src/terrain/`) and one shell folder.
|
|
801
|
+
- Standalone editor: created a terrain, the Paint tab lists all eight presets, a grass stroke paints the ground.
|
|
802
|
+
- Play shots of Beacon Bastion, Harbor Heat, Odd Orbit, Scrap Signal and Fernwild render as before; load guard passes for all five (Harbor Heat failed once while the full test suite ran beside it, then passed alone).
|
|
803
|
+
|
|
804
|
+
### Bends
|
|
805
|
+
|
|
806
|
+
- Remaining instanced/plain pairs in harbor-heat are vehicles and characters: far ones draw through the baked crowd (its own shader), near ones on their full rig. That is two techniques, not one material compiled twice, so they stay.
|
|
807
|
+
- Making every lone prop a batch of one was tried and dropped: drift-foundry's scene ready went from about 10 s to 19–28 s (per-batch machinery on hundreds of one-prop batches).
|
|
808
|
+
- None.
|
|
809
|
+
|
|
29
810
|
## 0.45.0
|
|
30
811
|
|
|
31
812
|
### Migrate
|
|
@@ -41,7 +822,7 @@ At publish, rename this heading to the new version and mirror the entries into
|
|
|
41
822
|
- `@jgengine/core/runtime/save` → `@jgengine/core/save/saveConfig`; `@jgengine/core/runtime/hostedWorldStore` → `@jgengine/core/save/hostedWorldStore`
|
|
42
823
|
- `@jgengine/react/save` → `@jgengine/react/save/saveStore`; `@jgengine/react/saveSlots` → `@jgengine/react/save/saveSlots`; `@jgengine/react/saveThumbnail` → `@jgengine/react/save/saveThumbnail`
|
|
43
824
|
- `asyncMemoryWorldStore` is removed; wrap `memoryWorldStore()` when an async `HostedWorldStore` is needed (`memoryWorldPersistence` does).
|
|
44
|
-
- `SaveSlotMenu` is removed; use `SaveSlotsPane` (`@jgengine/react/gameMenuSaves`), with `heading` for the old `title`.
|
|
825
|
+
- `SaveSlotMenu` is removed; use `SaveSlotsPane` (`@jgengine/react/menu/gameMenuSaves`), with `heading` for the old `title`.
|
|
45
826
|
- `devSavePlugin` has one home, `@jgengine/node/devSavePlugin`.
|
|
46
827
|
- `pickWeighted` has one definition, `@jgengine/core/random/pick` (`pickWeighted(rng, items, weightOf)`). The `world/scatterItems` copy (`pickWeighted(entries, roll)`) is removed.
|
|
47
828
|
- `createSeededRandom` (`@jgengine/shell/terrain`) is removed; use `seededRng` from `@jgengine/core/random/rng`, which gives the same stream for a string seed.
|
|
@@ -627,8 +1408,8 @@ Menus are responsive throughout; how long the cover stays up depends on the driv
|
|
|
627
1408
|
|
|
628
1409
|
### Added
|
|
629
1410
|
|
|
630
|
-
- `GameMenu` and `GamePauseMenu` (`@jgengine/react/gameMenu`): a full-bleed menu surface over the live scene or drifting key art, with a token-sized logo, an animated navigable verb list, a generated selection tick and a detail pane per verb. The document scroll is locked while it is open, so the surface is exactly the viewport at any size.
|
|
631
|
-
- Panes for the detail slot: `ContinuePane`, `NewCharacterPane`, `SaveSlotsPane` (slot cards with a `meta.thumbnail`, playtime, progress, timestamp and a two-press delete) and `CreditsPane` (`@jgengine/react/gameMenuSaves`).
|
|
1411
|
+
- `GameMenu` and `GamePauseMenu` (`@jgengine/react/menu/gameMenu`): a full-bleed menu surface over the live scene or drifting key art, with a token-sized logo, an animated navigable verb list, a generated selection tick and a detail pane per verb. The document scroll is locked while it is open, so the surface is exactly the viewport at any size.
|
|
1412
|
+
- Panes for the detail slot: `ContinuePane`, `NewCharacterPane`, `SaveSlotsPane` (slot cards with a `meta.thumbnail`, playtime, progress, timestamp and a two-press delete) and `CreditsPane` (`@jgengine/react/menu/gameMenuSaves`).
|
|
632
1413
|
- `shrinkCapture` (`@jgengine/react/saveThumbnail`) turns a `captureCanvas` frame into a card-sized thumbnail data URL.
|
|
633
1414
|
- `createSessionLoop` (`@jgengine/core/run/sessionLoop`) is one session loop for every game: a session opens, may carry a quantity, closes at its boundary, and the game's own `settle` callback decides what happens to what it carried. `carries: false` for a session with nothing at stake, and an optional `offers` table (with `present`/`choose`) for a game whose next session opens with an authored change; without a table nothing is drawn. `installSessionProbe` reports the live session on `window.__jgengineRun`.
|
|
634
1415
|
|
|
@@ -661,7 +1442,7 @@ Verification:
|
|
|
661
1442
|
The CI reliability fix this lane also carried (a browser setup hook shorter than Playwright's launch default) is #2335, so either change can be reverted without the other.
|
|
662
1443
|
- The publish workflow's last step no longer reports a whole release as unpublished because it ran seconds after `npm publish`. `check:release-artifacts --wait <seconds>` re-checks while the only failures are absent packages (no metadata, version missing, tarball 404) and gives up when the budget runs out; a wrong version in the tarball, a missing subpath or a main entry that will not import still fails on the first check. The 0.37.0 publish run failed this step at 04:48 listing all 13 packages, then npm served every one of them. The step now passes `-- --wait 300`.
|
|
663
1444
|
- A Playwright test file's `beforeAll` now outlives the Chrome launch it waits on, and `browser-test-budget` rejects a file that leaves the hook at or under Playwright's 30 s launch default. On `test (g)` in #2326 the hook's 30 s budget and that default expired together, so CI reported only `(unnamed) hook timed out` beside `killed 1 dangling process` and nothing named the launch.
|
|
664
|
-
- `packages/react/src/dialogBehavior.test.ts` gives its browser navigation its own 20 s deadline and one retry inside a 60 s hook.
|
|
1445
|
+
- `packages/react/src/dialog/dialogBehavior.test.ts` gives its browser navigation its own 20 s deadline and one retry inside a 60 s hook.
|
|
665
1446
|
|
|
666
1447
|
Root cause: Bun prints a `beforeAll` timeout as an unnamed hook timeout, so a setup budget equal to Playwright's own 30 s launch default reports the anonymous timer instead of the launch that stalled.
|
|
667
1448
|
|
|
@@ -671,7 +1452,7 @@ After: a stall now surfaces as Playwright's own `Timeout ... exceeded`, and `bun
|
|
|
671
1452
|
Verification:
|
|
672
1453
|
- `bun scripts/browser-test-budget.ts` - pass, and the new rule names the unraised files on the previous tip
|
|
673
1454
|
- `bun test scripts/browser-test-budget.test.ts` - pass
|
|
674
|
-
- `bun test packages/react/src/dialogBehavior.test.ts packages/react/src/scrollRail.test.ts packages/shell/src/settingsBoundary.test.ts` - pass
|
|
1455
|
+
- `bun test packages/react/src/dialog/dialogBehavior.test.ts packages/react/src/scrollRail.test.ts packages/shell/src/settingsBoundary.test.ts` - pass
|
|
675
1456
|
- `bun run verify:fast` - pass
|
|
676
1457
|
- A prop placed at runtime honours its model's `variants` levels. `StaticObjectModels` applied the level switch only inside its batched branch, so a prop placed with `scene.object.place` — Harbor Heat's palm line — drew its full asset at every camera distance: 15 palms at 9,134 triangles each, and pointing the far level at a dispatch booth changed nothing on screen. `IsolatedEntityModel` now mounts `SingleVariantModel`, which asks the same `createModelVariantSelector` and `lodBandIndex` authority the batched path uses, at the prop's own camera distance, and keeps only the active level mounted so both levels never draw at once. The switch carries the selector's hysteresis, warms every level at mount, measures only the near level, and reads the placement from the scene store through scratch vectors, so a frame allocates nothing. One palm past 60 m draws 4,110 triangles instead of 9,134 (5,024 saved, 45% of the full level).
|
|
677
1458
|
|
|
@@ -795,7 +1576,7 @@ Play scripts prove play in seconds instead of minutes of browser play, and the t
|
|
|
795
1576
|
|
|
796
1577
|
### Performance
|
|
797
1578
|
|
|
798
|
-
- `createTerrainPaletteSampler` (`@jgengine/core/
|
|
1579
|
+
- `createTerrainPaletteSampler` (`@jgengine/core/terrain/terrain`) tests only the material regions whose painted core or blend ring covers a sample, so a world with hundreds of authored regions no longer pays for all of them at every vertex.
|
|
799
1580
|
|
|
800
1581
|
## 0.35.0
|
|
801
1582
|
|
|
@@ -841,7 +1622,7 @@ Play scripts prove play in seconds instead of minutes of browser play, and the t
|
|
|
841
1622
|
|
|
842
1623
|
- **Bump lockstep SDK packages to `^0.33.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.30.0`; `@jgengine/github` is `0.20.0`.
|
|
843
1624
|
- `useSceneObjects` (`@jgengine/react/hooks`) keeps one list identity across `move` and `rotate`, so a record's `position`/`rotationY` is what it was at the last render. Read the live placement with `ctx.scene.object.get(instanceId)` (a `useFrame` loop or a rAF-ticked HUD) when a mesh or readout must follow a moving object.
|
|
844
|
-
- A read of a mismatched stored value now converts losslessly where it can (a number as its digits, a boolean as `"true"`/`"false"`, those strings back) and throws where it cannot. No in-repo caller relied on the old fallback; `useSetting` (`@jgengine/react/settings`) is the generic path that could, so a game that stored a number under a key it reads with a string fallback now gets the digits.
|
|
1625
|
+
- A read of a mismatched stored value now converts losslessly where it can (a number as its digits, a boolean as `"true"`/`"false"`, those strings back) and throws where it cannot. No in-repo caller relied on the old fallback; `useSetting` (`@jgengine/react/settings/settings`) is the generic path that could, so a game that stored a number under a key it reads with a string fallback now gets the digits.
|
|
845
1626
|
|
|
846
1627
|
### Added
|
|
847
1628
|
|
|
@@ -943,7 +1724,7 @@ Play scripts prove play in seconds instead of minutes of browser play, and the t
|
|
|
943
1724
|
### Added
|
|
944
1725
|
|
|
945
1726
|
- `ColorblindFrame` (`@jgengine/react/accessibility`) wraps the 3D canvas and applies the player's colour-vision mode to that box alone, so HUD, menu and caption text keep their own contrast. `colorblindFrameFilter` (`@jgengine/core/ui/accessibility`) resolves a mode to the CSS filter, and `useColorblindMode` reads the mode from the settings store the settings rows write; the 3D shell does both for the mounted game.
|
|
946
|
-
- `SettingsSection` (`@jgengine/react/settingsBlocks`) is one `useMenuNav` menu: a d-pad or left stick moves between rows and left/right steps the focused value — slider by its step, toggle flipped, select to the next option, confirm arms a keybind — through the same `onAdjust` path the arrow keys use. `onAdjust` (`@jgengine/react/menuNav`) is the option that makes left/right adjust instead of move.
|
|
1727
|
+
- `SettingsSection` (`@jgengine/react/settings/settingsBlocks`) is one `useMenuNav` menu: a d-pad or left stick moves between rows and left/right steps the focused value — slider by its step, toggle flipped, select to the next option, confirm arms a keybind — through the same `onAdjust` path the arrow keys use. `onAdjust` (`@jgengine/react/menu/menuNav`) is the option that makes left/right adjust instead of move.
|
|
947
1728
|
- `createStanding` (`@jgengine/core/progression/standing`) holds per-faction or per-district standing over authored tracks and moves it from named outcomes, so an outcome that resolves to nothing throws at the call instead of silently moving nothing.
|
|
948
1729
|
- `createGateSet` (`@jgengine/core/progression/gates`) evaluates authored milestones against standing and money, and refuses to load when a gate's unlock id resolves to nothing.
|
|
949
1730
|
- `createCareerBank` (`@jgengine/core/progression/careerBank`) keeps persistent career money apart from a run's haul, so a failed run loses the haul and keeps the bank.
|
|
@@ -1033,11 +1814,11 @@ Play scripts prove play in seconds instead of minutes of browser play, and the t
|
|
|
1033
1814
|
- `standardBoneMask`, `UPPER_BODY_SLOTS`/`LOWER_BODY_SLOTS` and `UPPER_BODY_MASK`/`LOWER_BODY_MASK` (`@jgengine/core/anim/skeletonStandard`) derive a layer mask from the skeleton standard's own slots, so a rig mapped onto the standard is masked without hand-typing its bone names.
|
|
1034
1815
|
- `ModelLookAtConfig` (`@jgengine/core/game/playableGame`, `ModelConfig.ik.lookAt`) can aim at more than the camera: `target` takes `"camera"` (default), `{ entity }` offset by `entityEyeHeight` (default 1.5), or `{ point }`. `maxYaw`/`maxPitch` bound the turn in radians and `weight` (default 1) scales how much of the solved turn reaches the bone. An entity target that is not spawned keeps its last known direction instead of snapping the head back to the camera. `applyFootIk`'s sixth argument is now `lookAtTarget` and applies the limits and weight; `FootIkRig` carries `lookAtLimits` and `lookAtWeight`.
|
|
1035
1816
|
- The shell derives the directional locomotion parameters from the entity's own movement, so a rig with the `strafe`/`backward` states uses them with no game-side code: `direction` (the motion's share along the character's right axis), `facing` (its share along the facing axis) and `turnRate` (its yaw change per second) come from the entity's `position` and `rotationY` each visual sample and override conflicting authored extras. `readModelAnimationParams` gains an optional `ModelMotionStep` argument (`dx`, `dz`, `dyaw`, `seconds`); omitting it leaves direction to the authored extras, which is the previous behaviour. A step past the live `snapDistance` reports no direction and zero speed rather than inventing a lateral.
|
|
1036
|
-
- `createMenuTransition` (`@jgengine/core/
|
|
1037
|
-
- `@jgengine/core/
|
|
1038
|
-
- `MenuScreen` (`@jgengine/react/menuScreen`): animated enter/exit screen with a translucent scrim, a `backdrop` slot for a live 3D scene, reduced-motion collapse and exit unmount. Animates transform/opacity only.
|
|
1817
|
+
- `createMenuTransition` (`@jgengine/core/menu/menuTransition`): closed → enter → open → exit → closed timing over an injected clock, with reduced-motion collapse to zero and `snapshot`/`restore`.
|
|
1818
|
+
- `@jgengine/core/menu/menuNav`: `menuNavMove` focus movement over a vertical list, horizontal row or grid with disabled-item skipping, `menuIntentForKey`, `menuIntentForPadButton`, `menuIntentForPadAxis` and `shouldRepeatMenuNav` for held d-pad/stick repeat.
|
|
1819
|
+
- `MenuScreen` (`@jgengine/react/menu/menuScreen`): animated enter/exit screen with a translucent scrim, a `backdrop` slot for a live 3D scene, reduced-motion collapse and exit unmount. Animates transform/opacity only.
|
|
1039
1820
|
- `TitleMenu` (`@jgengine/react/titleMenu`): title-screen block — wordmark, eyebrow, tagline, navigable verb list, footer, control hint and backdrop slot. Games own the wording, verbs and art.
|
|
1040
|
-
- `useMenuNav` (`@jgengine/react/menuNav`): keyboard and standard-gamepad focus navigation for any menu — roving `tabIndex`, arrow/WASD/Home/End moves, Enter/Space/A confirm, Escape/Backspace/B/Start back, held-stick repeat, disabled-item skipping, no focus trap (Tab stays native, typing is never intercepted) and keys that work when focus starts outside the menu.
|
|
1821
|
+
- `useMenuNav` (`@jgengine/react/menu/menuNav`): keyboard and standard-gamepad focus navigation for any menu — roving `tabIndex`, arrow/WASD/Home/End moves, Enter/Space/A confirm, Escape/Backspace/B/Start back, held-stick repeat, disabled-item skipping, no focus trap (Tab stays native, typing is never intercepted) and keys that work when focus starts outside the menu.
|
|
1041
1822
|
- `PauseMenu` now navigates with the same keyboard/gamepad rules; Resume is the first item and Escape resumes.
|
|
1042
1823
|
- Engine demo `menu-framework` (`bun run shoot menu-framework --mode play`) renders a live 3D scene behind the title and pause screens.
|
|
1043
1824
|
- `credits-panel` — `CreditsPanel` (`@jgengine/react/creditsPanel`): a scrollable, HudTheme-skinned credits screen over the scene with one back affordance reachable by keyboard, gamepad and pointer.
|
|
@@ -1048,8 +1829,8 @@ Play scripts prove play in seconds instead of minutes of browser play, and the t
|
|
|
1048
1829
|
- `SaveSlotWrite.savedAt`: a save payload that knows when it was written can stamp that time instead of the index's clock.
|
|
1049
1830
|
- `@jgengine/core/settings/accessibilitySettings`: the persisted accessibility ids (`accessibility.textScale`, `accessibility.colorblind`, `accessibility.reducedMotion`, `accessibility.highContrast`, `accessibility.captions`, `controls.holdVsToggle`), `accessibilitySettingRows(store)` for a settings surface, `readAccessibilitySettings(store)`, and `holdToggleActive(previous, { pressed, held }, mode)` for hold-or-toggle actions.
|
|
1050
1831
|
- The shell's settings menu has an `accessibility` category (text size, colourblind palette, reduce motion, high contrast, captions, hold-or-toggle) unless a game hides it. Text size scales every `--jg-type-*` token through `--jg-text-scale`; reduce motion zeroes the engine's motion scale; high contrast, colourblind and captions are exposed as `data-*` hooks on the HUD root.
|
|
1051
|
-
- `SettingsSection`, `SettingsRowControl`, `SettingsSlider`, `SettingsToggle`, `SettingsSelect`, `SettingsKeybindControl` and `SettingsTabs` (`@jgengine/react/settingsBlocks`): the settings rows and click-to-capture controls as composable, HudTheme-tokenized blocks, so a game lays out its own settings screen instead of taking the stock dialog. Rebinding writes into the game's existing rebind/holdings store.
|
|
1052
|
-
- `useAccessibilitySettings`, `useHoldVsToggle` and `useAccessibilityRows` (`@jgengine/react/accessibilitySettings`) read the live preferences for any custom layout.
|
|
1832
|
+
- `SettingsSection`, `SettingsRowControl`, `SettingsSlider`, `SettingsToggle`, `SettingsSelect`, `SettingsKeybindControl` and `SettingsTabs` (`@jgengine/react/settings/settingsBlocks`): the settings rows and click-to-capture controls as composable, HudTheme-tokenized blocks, so a game lays out its own settings screen instead of taking the stock dialog. Rebinding writes into the game's existing rebind/holdings store.
|
|
1833
|
+
- `useAccessibilitySettings`, `useHoldVsToggle` and `useAccessibilityRows` (`@jgengine/react/settings/accessibilitySettings`) read the live preferences for any custom layout.
|
|
1053
1834
|
- `HudThemeRoot` takes `accessibility` and applies text scale and the preference data attributes at the HUD root; the shell passes the player's stored preferences there for both the 3D and HUD-only presentation paths.
|
|
1054
1835
|
- `MissionTickInput.events` (`@jgengine/core/gameplay`) carries the world events that happened this tick - `kill`, `collect`, `deliver` with a target, the actor, free-form `tags` and a count. An objective's `match` filter (`target`, `by`, `tags`, `withoutTags`) credits a constrained counter only from matching events, so a decoy, a wrong weapon or another killer's kill does not advance it.
|
|
1055
1836
|
- `failOn` accepts `event` (a matching reported event ends the run) and `left-area` (the watched actor is farther than `radius` from an authored marker), completing the reachable fail states: cargo lost, time out, escort died, left the area. `validateMissionCatalog` rejects a `left-area` fail with no actor or unknown marker and a `match` on a non-counter objective instead of ignoring it.
|
|
@@ -1124,7 +1905,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
1124
1905
|
- `KinematicVehicleTuning.weightTransfer` (`@jgengine/core/world`) adds load-transfer grip: longitudinal transfer under braking/traction and lateral transfer under cornering cut the tire grip curve (tire load sensitivity), so braking into a corner holds less slide than coasting through it. Each tick reports `weightTransfer` and `loadGrip`; omitting the block keeps the previous flat-grip model exactly.
|
|
1125
1906
|
- `KinematicVehicleModifiers.gripScale` scales the grip curve for one tick, mirroring `VehicleDynamicsModifiers.gripScale` so a damage layer drives either sim.
|
|
1126
1907
|
- `decal` scene kind (`@jgengine/core/world/decalKind`) and its renderer (`@jgengine/shell/scene/DecalRenderer`): dirt, cracks, paint and blood decals authored as placed markers, draped on the live terrain or standing on an authored tilt, with a deterministic procedural texture per style+seed.
|
|
1127
|
-
- `TerrainMaterialLayer.material` (`@jgengine/core/
|
|
1908
|
+
- `TerrainMaterialLayer.material` (`@jgengine/core/terrain/terraform`): a material library name whose scanned color map paints that terrain layer. The runtime samples up to four layer maps at each layer's `tiling` and blends them through the authored mask weights (`createTerrainDetailMaterial`'s `layerMaps`), so painted terrain blends between authored layers instead of tiling one flat map.
|
|
1128
1909
|
- `grass_field` volumes now paint a ground-cover mat under their blades (`groundCover` rule, `@jgengine/shell/terrain/GrassCover`), so an authored patch reads as turf rather than bare ground with stubble. `GrassEnvironmentConfig.groundCover` does the same for `grass()` patches.
|
|
1129
1910
|
- `ModelConfig.wind` (`@jgengine/core/game/playableGame`) and `applyModelWind` (`@jgengine/shell/render/modelWind`): per-model foliage sway with height-ramped travel, shared gust clock and per-model strength/speed/direction.
|
|
1130
1911
|
|
|
@@ -1209,7 +1990,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
1209
1990
|
### Migrate
|
|
1210
1991
|
|
|
1211
1992
|
- **Bump lockstep SDK packages to `^0.25.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.22.0`; `@jgengine/github` is `0.12.0`.
|
|
1212
|
-
- The frame panel from `@jgengine/react/hudPanel` is now `HudSurface` in `@jgengine/react/hudSurface` (`HudSurfaceProps`, `HudSurfaceElevation`). `HudPanel`, `HudPanelProps` and `HudPanelElevation` from `@jgengine/react/hudPanel` stay as deprecated aliases for this release. The `HudCanvas` layout slot keeps the name `HudPanel`.
|
|
1993
|
+
- The frame panel from `@jgengine/react/hudPanel` is now `HudSurface` in `@jgengine/react/hud/hudSurface` (`HudSurfaceProps`, `HudSurfaceElevation`). `HudPanel`, `HudPanelProps` and `HudPanelElevation` from `@jgengine/react/hudPanel` stay as deprecated aliases for this release. The `HudCanvas` layout slot keeps the name `HudPanel`.
|
|
1213
1994
|
- Engine HUD control sounds (button click/hover, panel and window open/close) no longer turn off with `notices: false` or `notices.cues: false`. Set `audio.hudCues: false` to silence them; `notices.cues` now covers notice sounds only.
|
|
1214
1995
|
- The notice stack now defaults to z-index 85 (was 45), above `ModalHost`. Pass `notices.zIndex` to put it back under a game overlay.
|
|
1215
1996
|
|
|
@@ -1276,7 +2057,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
1276
2057
|
- `createSurfaceRibbonMaterial`, `ribbonSurfaceFrame`, `resolvePathSurface` and `useSurfaceRibbonMaterial` (`@jgengine/shell/terrain`) shade ground ribbons with the terrain's procedural surface functions, plus wear and a noise-warped soft edge.
|
|
1277
2058
|
- `road({ surface })` (`@jgengine/core/world/features`) picks the carriageway surface; `false` keeps flat colour.
|
|
1278
2059
|
- `AuthoredScene`/`AuthoredPaths` `pathSurfaceDetail`/`surfaceDetail` props and path `meta.surface`, `meta.edgeSoftness`, `meta.wear`, `meta.surfaceDetail`.
|
|
1279
|
-
- `EditableTerrain.gridBounds` (`@jgengine/core/
|
|
2060
|
+
- `EditableTerrain.gridBounds` (`@jgengine/core/terrain/terraform`) exposes the world extent of the paint/height cell grid.
|
|
1280
2061
|
- Painted terrain renders each layer's `surface` as its own procedural ground (grass, dirt, gravel, rock, sand, mud, snow, road) with albedo variation, roughness and micro-normals, blended along noise-warped, height-aware edges, in play and in the editor viewport. No game config or assets needed.
|
|
1281
2062
|
- `TerrainDetailConfig.surfaceStrength` (0..1, default 1) scales the per-surface response; `detail: false` opts a terrain out to the flat vertex-colour ground.
|
|
1282
2063
|
- `@jgengine/shell/terrain` exports `packTerrainPaintWeights`, `createTerrainPaintTextures`, `terrainSurfaceClass`, `createTerrainDetailMaterial` and `TerrainQualityContext`/`useTerrainQuality`.
|
|
@@ -1286,7 +2067,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
1286
2067
|
- UI sound cues: `useUiCue()` / `playUiCue` (`@jgengine/react/uiCues`) play `click`, `hover`, `open`, `close`, `confirm`, `deny`, `notify`. Soft procedural defaults (`@jgengine/core/audio/uiCues`) mix on a new "ui" bus with a "UI volume" setting under master volume. A game sound named `ui.<cue>` replaces one; `audio.uiCues` overrides or silences cues (`false` for all).
|
|
1287
2068
|
- `notice_push` and `run_command` agent-bridge verbs for `drive --rpc`.
|
|
1288
2069
|
- `ui-feedback` dev probe (apps/dev) exercising refusals, the feed and cues.
|
|
1289
|
-
- `HudTheme` gains a type scale with a 12px floor, spacing, motion (durations and easings), elevation (layered shadows and a lit top rim), and hover/press/focus/disabled state tokens; `hudThemeVars` emits them as `--jg-type-*`, `--jg-space-*`, `--jg-motion-*`, `--jg-ease*`, `--jg-elevation-*`, `--jg-highlight`, `--jg-hover-*`, `--jg-press-*`, `--jg-focus-ring`, and `--jg-disabled-opacity`. `@jgengine/react/hudTokens` reads them in inline styles (`hudType`, `hudSpace`, `hudTransition`, `hudElevation`, `hudFont`, `HUD_FOCUS_RING`).
|
|
2070
|
+
- `HudTheme` gains a type scale with a 12px floor, spacing, motion (durations and easings), elevation (layered shadows and a lit top rim), and hover/press/focus/disabled state tokens; `hudThemeVars` emits them as `--jg-type-*`, `--jg-space-*`, `--jg-motion-*`, `--jg-ease*`, `--jg-elevation-*`, `--jg-highlight`, `--jg-hover-*`, `--jg-press-*`, `--jg-focus-ring`, and `--jg-disabled-opacity`. `@jgengine/react/hud/hudTokens` reads them in inline styles (`hudType`, `hudSpace`, `hudTransition`, `hudElevation`, `hudFont`, `HUD_FOCUS_RING`).
|
|
1290
2071
|
- Engine stylesheet `@jgengine/react/styles.css` (also `ENGINE_STYLESHEET` / `useEngineStyles` in `@jgengine/react/engineStyles`): every `jg-*` keyframe the shared blocks and registry use, `[data-jg-*]` interaction states, quiet hover/press/focus defaults for plain HUD buttons, and reduced-motion handling for `prefers-reduced-motion` and `[data-reduced-motion="true"]`. The `jgengine create` template imports it.
|
|
1291
2072
|
- `HudThemeRoot`: the shell wraps every `GameUI` in it, applying `defaultHudTheme` and the stylesheet. `defineGame({ hudTheme })` swaps the root theme; `hudTheme: false` opts out.
|
|
1292
2073
|
- Primitives `HudButton`, `HudPanel` (open/close transition via `usePresence`), and `TweenNumber` / `useTweenedValue` with an up/down delta flash; `useReducedMotion` in `@jgengine/react/accessibility`.
|
|
@@ -1440,7 +2221,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
1440
2221
|
- Relay Courtyard declares a persisted compact or detailed readout through the shared HUD settings framework.
|
|
1441
2222
|
- Core content reference and declared-source progression validators report located repair guidance across game-owned catalogs. Closed-source analysis detects unavailable prerequisites; intentional seeded cycles remain valid.
|
|
1442
2223
|
- Native recipe catalog validation checks item/station/unlock references, quantities and duplicate inputs without imposing economic or output-benefit policy.
|
|
1443
|
-
- `useDialogBehavior` (`@jgengine/react/dialogBehavior`) shares dialog focus entry, Tab cycling, Escape close requests, and restoration with caller-owned screens; `ModalHost` uses the same behavior.
|
|
2224
|
+
- `useDialogBehavior` (`@jgengine/react/dialog/dialogBehavior`) shares dialog focus entry, Tab cycling, Escape close requests, and restoration with caller-owned screens; `ModalHost` uses the same behavior.
|
|
1444
2225
|
- `resolvePeerShellMultiplayer` can host a playable authoritative GameContext world with injected persistence, signaling, peers and tick scheduling; `close()` awaits its save boundary.
|
|
1445
2226
|
- `createWorldGameHost` supports a serialized `slotsPerServer` player cap while allowing existing members and spectators.
|
|
1446
2227
|
- `bindCommandTransport` / `dispatchCommand` provide the shared UI command boundary; `possession.ownerOf` resolves unique recorded ownership.
|
|
@@ -2141,7 +2922,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
2141
2922
|
### Added
|
|
2142
2923
|
|
|
2143
2924
|
- **An `rng` that falls back to `Math.random` now says so (#1582)** — twelve core primitives (`createMobBrain`, `applyStatus`, `rollCheck`, `autoTarget`, `rollCapture`, `createLootRegistry`, `createRunDraft`, `createResourceNode`, `worldItem`, `selectLane`, `assignRoles`, `scheduleDelivery`) took an injectable `rng` and defaulted it silently, so a caller who forgot to pass `ctx.rng` got a simulation that could not replay, lockstep, roll back, or be pinned by a test — with no signal. They now route through `resolveRng` (`@jgengine/core/random/resolveRng`), which warns once per call site in dev and stays silent in production. The fallback itself is unchanged, so no behavior moves; four sites whose `rng` was a defaulted parameter now take it as optional, which is source-compatible.
|
|
2144
|
-
- **The two parts a game front-end could not reach for (#1577)** — `useMenuRouter(root)` (`@jgengine/react/menuRouter`) is the screen stack behind title → save select → settings → credits: `open`/`back`/`replace`/`reset`, `path`, and Escape-to-back on by default; `menuStackReducer` exposes the same transition as a pure function. For credits, `creditsForSourceIds(ids)` (`@jgengine/assets/credits`) turns the pack ids a game already declares to `buildCatalog` into licensed, attributed sections, `mergeCredits`/`groupCredits` (`@jgengine/core/game/credits`) fold and shape them, and `CreditsScreen` (`@jgengine/react/creditsScreen`) renders a `CreditsDocument` HudTheme-skinned. Attribution is now derived from what the game ships instead of hand-maintained.
|
|
2925
|
+
- **The two parts a game front-end could not reach for (#1577)** — `useMenuRouter(root)` (`@jgengine/react/menu/menuRouter`) is the screen stack behind title → save select → settings → credits: `open`/`back`/`replace`/`reset`, `path`, and Escape-to-back on by default; `menuStackReducer` exposes the same transition as a pure function. For credits, `creditsForSourceIds(ids)` (`@jgengine/assets/credits`) turns the pack ids a game already declares to `buildCatalog` into licensed, attributed sections, `mergeCredits`/`groupCredits` (`@jgengine/core/game/credits`) fold and shape them, and `CreditsScreen` (`@jgengine/react/creditsScreen`) renders a `CreditsDocument` HudTheme-skinned. Attribution is now derived from what the game ships instead of hand-maintained.
|
|
2145
2926
|
- **`ResourcePool` and `ComboPoints` can hand their state back (#1575)** — both gain `snapshot()` / `restore(state)` over a plain-data `ResourcePoolSnapshot` / `ComboPointsSnapshot`, so a save file, a server host, or a test can carry them across a rebuild. `ResourcePool` also gains `retune({ max, regenPerSecond, decayPerSecond })`, so a buff or difficulty change no longer needs a fresh pool (regen/decay were frozen at construction). Additive: existing calls are unchanged.
|
|
2146
2927
|
- **`HudTheme` now covers the whole `--jg-*` vocabulary (#1573)** — new `surface`, `status`, `font`, and `rarity` groups plus `-deep`/glow palette companions, so `hudThemeVars(theme)` emits all 53 tokens the shipped components read instead of the 28 the bars/frames/slots needed. One theme spread on a HUD ancestor now skins the `@jgengine/react` primitives and the `registry/jgengine` blocks together; previously a game had to author two disjoint token sets. New `hudTheme({ ... })` fills a theme from per-group partials, and `deriveHudTheme({ accent, surface })` builds a coherent full theme (including frames, troughs, and slots) from a seed pair. `ember` / `synthwave` / `fieldkit` join `HUD_THEME_PRESETS` with their registry values unchanged. Additive: existing themes and every rendered surface are unaffected.
|
|
2147
2928
|
- **Runtime-mutable arcade car tuning + hops (#1550)** — `KinematicVehicle.retune(tuning)` swaps a vehicle's whole tuning block while keeping position, heading, velocity and gear state, for vehicles whose stats change during play (installed parts, damage, debuffs). New tuning fields `hopGravity` (enables `vehicle.hop(impulse)`, reported as `step.airOffset` / `step.airborne` and applied on top of the ground sample by `tickDrivableVehicle`), `coastDeceleration` (flat coast road-load, versus `rollingResistance`'s proportional decay), and `reverseForceScale` (reverse push on the arcade path, previously reachable only via a full `chassis` block). New per-tick modifier `brakeScale`. All additive: omit them and every existing vehicle behaves exactly as before.
|
|
@@ -2237,7 +3018,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
2237
3018
|
that owns a `SpawnDirectorState`, ticks it from `update(dt, ctx?)`, forwards each `SpawnRequest` to an
|
|
2238
3019
|
optional `onSpawn` sink (so the model never instantiates entities), and exposes a pooled `view()`
|
|
2239
3020
|
readout (1-based `WAVE N`, wave progress `0..1`, budget/alert, spawned-this-wave/total, done) plus
|
|
2240
|
-
`forceNextWave`/`raiseAlert`/`subscribe`/`snapshot`/`restore`. `@jgengine/react/waveHud` adds
|
|
3021
|
+
`forceNextWave`/`raiseAlert`/`subscribe`/`snapshot`/`restore`. `@jgengine/react/hud/waveHud` adds
|
|
2241
3022
|
`WaveHud`/`useWaveRunner` — a theme-skinnable panel with a big WAVE N label, wave-progress bar, and
|
|
2242
3023
|
spawn/budget/alert readouts. Spawn-entry kinds stay free strings the runner never interprets.
|
|
2243
3024
|
- **Count-based combo / multiplier meter.** `@jgengine/core/combat/comboMeter` adds
|
|
@@ -2245,7 +3026,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
2245
3026
|
climbs on `hit(kind?)`, resets a decay window each hit, and drops (to 0, or by `dropStep`) when the
|
|
2246
3027
|
window elapses, driven by an injected `now` and/or `update(dt)`. Free-string `tiers` derive the active
|
|
2247
3028
|
`tier()` and a score `multiplier()`, with `peak()`, a pooled `view()`, `subscribe`, and
|
|
2248
|
-
`snapshot`/`restore`. React `@jgengine/react/comboMeter` ships `ComboMeterHud` (big live count, tier
|
|
3029
|
+
`snapshot`/`restore`. React `@jgengine/react/hud/comboMeter` ships `ComboMeterHud` (big live count, tier
|
|
2249
3030
|
label, draining window bar, multiplier — per-tier colored from a caller map) and a `useComboMeter` hook.
|
|
2250
3031
|
- **Off-screen objective / waypoint markers.** `@jgengine/core/ui/screenMarkers` adds a serializable,
|
|
2251
3032
|
observable `createWaypointTracker()` (`set`/`remove`/`clear`/`all`/`subscribe`/`snapshot`/`restore`,
|
|
@@ -2365,7 +3146,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
2365
3146
|
event without a genre kit. `@jgengine/react`'s `useSequenceDirector` runs the per-frame tick loop and exposes
|
|
2366
3147
|
playhead/progress + controls, and `CutsceneLetterbox` is a reskinnable cinematic bars + caption + Skip overlay.
|
|
2367
3148
|
See the `cutscene` dev demo.
|
|
2368
|
-
- **Modal / confirmation dialog system + reskinnable pause menu.** `@jgengine/core/
|
|
3149
|
+
- **Modal / confirmation dialog system + reskinnable pause menu.** `@jgengine/core/dialog/modalStack`'s
|
|
2369
3150
|
`createModalStack` is a genre-agnostic, serializable stack of opaque modal records (`push`/`pop`/`resolve` with a
|
|
2370
3151
|
free-form result, `top`/`isOpen`/`depth`, optional injected-clock auto-dismiss via `tick`/`timeRemaining`,
|
|
2371
3152
|
`subscribe`, `snapshot`/`restore`) that never interprets a modal's `kind` or result. `@jgengine/react`'s `ModalHost`
|
|
@@ -2420,7 +3201,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
2420
3201
|
- **Real curves and sidewalks in the street generator** (`@jgengine/core/world/streetGenerator`, #1364, #1368) — corners are now sampled circular-arc fillets honoring `minCurveRadius` instead of single bevels; street hierarchy comes from sampled betweenness centrality with an arterial-connectivity repair pass instead of a length percentile; and boulevard/avenue/street chains carry `sidewalks` offset polylines (new optional `sidewalkWidth` rule, default 2).
|
|
2421
3202
|
- **Race-track synthesis in circuit mode** (`@jgengine/core/world/streetGenerator`, #1365) — circuits are grown from seeded scatter → convex hull → inward midpoint displacement with chicane/ess/hairpin corner templates, an enforced start/finish straight, deterministic self-clearance retries, and a pit lane that leaves and rejoins the loop at two real junctions — an actual track layout instead of a wobbled ellipse.
|
|
2422
3203
|
- **City lot content resolution** (`@jgengine/core/world/cityGenerator`, #1367) — `resolveCityLotContent` (or `generateCity`'s new `content` option) enriches frontage lots with their zone band, building class from cityContent's weighted mixes (street-level-biased), floors, and massing pieces; a deterministic landmark pass merges clusters of adjacent lots into a few block-scale `hall`/`arena`/`market`/`campus` buildings (dial: `content.landmarks`, default 0.04). `GeneratedCity` gains an optional `lotContent`; the bare-lot path is unchanged.
|
|
2423
|
-
- **Quick menu takes many forms.** The radial menu grows into a full quick-menu family. `@jgengine/core/
|
|
3204
|
+
- **Quick menu takes many forms.** The radial menu grows into a full quick-menu family. `@jgengine/core/menu/radialMenu`
|
|
2424
3205
|
gains a `RadialArc` (`startAngle`/`sweep`) so the wheel can be a partial **arc** (bottom half, quarter, ping bar)
|
|
2425
3206
|
with bounds-aware selection. `@jgengine/react`'s `RadialMenu` now renders per-slice **hotkeys, count badges,
|
|
2426
3207
|
cooldowns, and submenu markers**, and takes an `arc`. New `@jgengine/react` **`QuickMenu`** presents one item
|
|
@@ -2430,7 +3211,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
2430
3211
|
- **Per-context touch control modes** (`@jgengine/core/input/touchScheme` `TouchControlsConfig.modes` + `@jgengine/core/input/touchControlsMode` `setTouchControlsMode`/`activeTouchControlsMode`, re-exported from `gameplay`, #1370) — a game whose verbs change at runtime (on foot vs driving vs flying) declares one named touch config per context; each mode's fields replace the matching base fields and gameplay switches with `setTouchControlsMode(ctx, "car")` / `null`. The shell re-derives the dock the same frame, so a runner on foot never sees flight or vehicle chrome. Blind derivation past 8 buttons now logs a dev warning naming the curation seams. Vice Isle (on-foot/car/aircraft) is the first adopter.
|
|
2431
3212
|
- **Analog action values through the input snapshot** (`@jgengine/core/runtime/inputSnapshot` `publishAnalog`/`value`/`analog`, `InputFrame.analog`, #1370) — a continuous source (the virtual joystick today, gamepad sticks tomorrow) publishes per-action magnitudes (0..1) alongside the digital held set; `ctx.input.value(action)` reads them, `ctx.input.axis(...)` samples them per bound action (so steer/pitch/roll bound to action names become proportional automatically), and the walk controller consumes them via `resolveMovementIntent`'s new analog path — a slight stick tilt walks slowly in that direction at speed scaled by deflection, instead of slamming a full-speed digital strafe. Serializable: analog rides `InputFrame` to an authoritative host unchanged.
|
|
2432
3213
|
- **Floating joystick variant + Settings selector** (`@jgengine/shell` touch dock, `@jgengine/core/input/touchScheme` `TouchJoystickVariant`, #1370) — two shipped stick behaviors, player-selectable under Settings → Controls → "Joystick": `floating` (default; the dock corner is a capture zone with a faint resting ring and the stick spawns centered under the thumb) and `fixed` (classic always-visible stick). Both share the analog vector math with a radial deadzone (travel rescaled from 0.16), replacing the old per-axis 0.42 digital thresholds as the movement path.
|
|
2433
|
-
- **Radial / quick menu (weapon/emote wheel).** `@jgengine/core/
|
|
3214
|
+
- **Radial / quick menu (weapon/emote wheel).** `@jgengine/core/menu/radialMenu` ships the pure geometry —
|
|
2434
3215
|
`radialSlices`, `radialIndexFromAngle`, `radialIndexFromVector` (pointer/stick vector → slice with a
|
|
2435
3216
|
neutral dead zone), `radialSlicePosition` — with slices centered from "up" clockwise. New `@jgengine/react`
|
|
2436
3217
|
`RadialMenu` renders the wheel: pointer angle drives the highlight, the neutral hub closes, click/confirm
|
|
@@ -2650,7 +3431,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
2650
3431
|
|
|
2651
3432
|
### Added
|
|
2652
3433
|
|
|
2653
|
-
- Shared mobile layout composition — `@jgengine/core/
|
|
3434
|
+
- Shared mobile layout composition — `@jgengine/core/hud/hudPriority` (pure geometry: `resolveLayoutMode`, `intersects`, `overlapArea`, `detectLayoutCollisions`, `computeGameplayRect`, `LayoutRegion`, `GameViewportLayout`) and `@jgengine/core/ui/orientation` (`resolveOrientationRequirement`, `orientationGateActive`, `MobileOrientationRule`). `@jgengine/react` adds `GameViewportProvider` (mounted by the shell), hooks (`useGameViewportLayout`, `useGameLayoutMode`, `useGameOrientation`, `useReservedControlZones`, `useLayoutCollisions`, `useRegisterLayoutRegion`), and a headless `RotateDeviceScreen`. The provider tracks `window.visualViewport`, publishes `--jg-viewport-*` / `--jg-visual-viewport-*` / `--jg-safe-*` CSS vars, resolves the explicit layout mode, and detects forbidden region overlaps (dev `data-jg-layout-collision` + console diagnostic).
|
|
2654
3435
|
- Mandatory orientation contract — `PlayableGame.orientation` accepts `{ mobile: <rule> }`; `landscape-required`/`portrait-required` render an engine-owned rotate gate that blocks gameplay (input suppressed, simulation frozen, HUD/controls unmounted), self-correcting when the device is turned. Reduced-motion and safe-area respected; `visualViewport`-sized for mobile Safari/PWA.
|
|
2655
3436
|
- Reserved touch-control zones — the touch dock registers its joystick / action-cluster / utility rectangles (runtime-measured) as `control` regions, so HUD placement and collision validation know exactly where controls sit.
|
|
2656
3437
|
- HUD placement metadata — `HudPanel` gains `priority`, `mobileBehavior`, `allowOverlapWith`, `collisionGroup`, and registers as a collision-tracked layout region.
|
|
@@ -2665,7 +3446,7 @@ Harbor Heat's shadow pass self time at `--quality full`: 2.88 → 1.37 ms per fr
|
|
|
2665
3446
|
|
|
2666
3447
|
### Added
|
|
2667
3448
|
|
|
2668
|
-
- Mobile HUD fit — design-resolution UI scaling now applies to every game by default: `HudCanvas` measures the live viewport and scales the whole HUD from `PlayableGame.hudFit.designSize` (default 1600×900, clamps `minScale`/`maxScale` 0.4–1) so desktop-authored layouts shrink to fit a phone instead of overflowing it; `hudFit.mobile` overrides tune the phone fit separately. Pure math lives in `@jgengine/core/
|
|
3449
|
+
- Mobile HUD fit — design-resolution UI scaling now applies to every game by default: `HudCanvas` measures the live viewport and scales the whole HUD from `PlayableGame.hudFit.designSize` (default 1600×900, clamps `minScale`/`maxScale` 0.4–1) so desktop-authored layouts shrink to fit a phone instead of overflowing it; `hudFit.mobile` overrides tune the phone fit separately. Pure math lives in `@jgengine/core/hud/hudScale` (`hudScaleForViewport`, `resolveHudFit`, `rectOverflow`, `overflowingPanels`); the shell mounts `@jgengine/react`'s `HudViewportProvider` around `GameUI` so games need no wiring.
|
|
2669
3450
|
- Graphics → UI scale — a player-facing `graphics.uiScale` slider (0.5–1.5, default 1) multiplies the computed HUD scale on every platform; the same resolution system drives desktop preference and mobile shrink.
|
|
2670
3451
|
- HUD overflow gate — `HudCanvas` measures every `HudPanel` against the viewport at runtime and reports offenders on a `data-hud-overflow` attribute (plus a console warning); `bun run shoot <game> --device mobile|both` now exits non-zero naming the panels that escape the viewport.
|
|
2671
3452
|
- Automatic visibility & streaming defaults — an engine-level `VisibilitySystem` (exported from `@jgengine/core`) gives every scene renderer-agnostic culling and asset-streaming policy with no per-object wiring: distance culling, preload margins, hysteresis, delayed unloading, multi-camera awareness (including cameras excluded from streaming), and per-object overrides (always-visible, never-unload, disabled culling/streaming, custom render distance and preload margin).
|
|
@@ -2805,7 +3586,7 @@ Pure, renderer-free primitives for card, board, and deckbuilder games — they s
|
|
|
2805
3586
|
- `@jgengine/core/world/support` — `solveSupport` walks the connector graph to ground (`supported`/`unsupported`/hop-`distance` for white→red decay); `toDebrisBodies` sinks collapsed pieces into the `PhysicsWorld`.
|
|
2806
3587
|
- `@jgengine/core/world/walls` — `createWallDrawTool` (drag walls → auto-enclose → `footprintFromWalls` → `autoRoof` hip/gable/flat) plus `createSurfacePaint` for per-tile floor/wall surfaces.
|
|
2807
3588
|
- `@jgengine/core/world/placedStructureStore` — `createPlacedStructureStore` save/load/select/move/delete with a `snapshot`↔`load` round-trip that survives reload.
|
|
2808
|
-
- `@jgengine/core/
|
|
3589
|
+
- `@jgengine/core/terrain/terraform` — `createEditableTerrain({ bounds, base, cellSize })` makes a `TerrainField` you can **write back to** via `apply(edit)` (raise/lower/flatten/paint), and `createTerraformBrush` is the cursor tool. This height-offset grid is the shared terrain-edit write-back pattern.
|
|
2809
3590
|
- `@jgengine/core/world/buildPermissions` — `createPlotPermissions` (per-plot/guild `BuildRole` edit authority) + `createContributionPool` (co-op pooled-resource contribution model).
|
|
2810
3591
|
- `@jgengine/shell` renderers: `structures/PlacementGhost` (tinted ghost), `terrain/EditableGround` (renders an `EditableTerrain` with surface paint), `terrain/TerraformBrushCursor` (brush ring); the `builder-sandbox` demo game wires them to `pointer.worldHit()`.
|
|
2811
3592
|
- **Map, fog of war & contextual ping.** Minimap/world-map/fog/compass + a squad ping verb, built on renderer-free core state, a shell terrain bake, and react HUD components. (Stacked on the pointer foundation above.)
|