@jgengine/core 0.14.0 → 0.15.0-next.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 +483 -1
- package/dist/ai/difficulty.d.ts +154 -0
- package/dist/ai/difficulty.js +194 -0
- package/dist/ai/driver.d.ts +107 -0
- package/dist/ai/driver.js +175 -0
- package/dist/ai/flock.js +13 -23
- package/dist/ai/laneSelect.js +2 -1
- package/dist/ai/mobBrain.js +2 -1
- package/dist/ai/waveRunner.d.ts +81 -0
- package/dist/ai/waveRunner.js +104 -0
- package/dist/authoring.d.ts +6 -4
- package/dist/authoring.js +6 -4
- package/dist/combat/castRunner.d.ts +13 -1
- package/dist/combat/castRunner.js +5 -1
- package/dist/combat/comboMeter.d.ts +140 -0
- package/dist/combat/comboMeter.js +215 -0
- package/dist/combat/comboPoints.d.ts +11 -0
- package/dist/combat/comboPoints.js +11 -2
- package/dist/combat/damageResolution.d.ts +1 -1
- package/dist/combat/defensiveWindow.d.ts +5 -0
- package/dist/combat/effects.d.ts +24 -0
- package/dist/combat/effects.js +6 -1
- package/dist/combat/projectiles.d.ts +6 -0
- package/dist/combat/projectiles.js +2 -2
- package/dist/combat/regenShield.d.ts +40 -2
- package/dist/combat/regenShield.js +66 -18
- package/dist/combat/resourcePool.d.ts +19 -0
- package/dist/combat/resourcePool.js +20 -2
- package/dist/combat/shotOrigin.d.ts +5 -0
- package/dist/combat/statusApplication.d.ts +1 -1
- package/dist/combat/statusApplication.js +2 -1
- package/dist/combat/statusEffectView.d.ts +56 -0
- package/dist/combat/statusEffectView.js +52 -0
- package/dist/combat.d.ts +4 -2
- package/dist/combat.js +2 -0
- package/dist/devtools/fallbackSeams.d.ts +3 -2
- package/dist/devtools/fallbackSeams.js +1 -1
- package/dist/devtools/textureErrors.d.ts +40 -0
- package/dist/devtools/textureErrors.js +68 -0
- package/dist/economy/shopStock.d.ts +110 -0
- package/dist/economy/shopStock.js +157 -0
- package/dist/economy/wallet.d.ts +6 -0
- package/dist/economy/wallet.js +1 -0
- package/dist/game/achievements.d.ts +80 -0
- package/dist/game/achievements.js +124 -0
- package/dist/game/cameraConfig.d.ts +7 -0
- package/dist/game/codex.d.ts +60 -0
- package/dist/game/codex.js +89 -0
- package/dist/game/credits.d.ts +36 -0
- package/dist/game/credits.js +0 -0
- package/dist/game/defineGame.d.ts +28 -3
- package/dist/game/defineGame.js +5 -0
- package/dist/game/dialogueGraph.d.ts +116 -0
- package/dist/game/dialogueGraph.js +103 -0
- package/dist/game/eventTicker.d.ts +95 -0
- package/dist/game/eventTicker.js +91 -0
- package/dist/game/events.d.ts +24 -5
- package/dist/game/events.js +18 -0
- package/dist/game/gamePhase.d.ts +1 -1
- package/dist/game/gamePhase.js +1 -1
- package/dist/game/leaderboardRank.d.ts +71 -0
- package/dist/game/leaderboardRank.js +76 -0
- package/dist/game/lootTable.d.ts +10 -1
- package/dist/game/lootTable.js +4 -2
- package/dist/game/notifications.d.ts +57 -0
- package/dist/game/notifications.js +107 -0
- package/dist/game/playableGame.d.ts +39 -2
- package/dist/game/quest.d.ts +26 -0
- package/dist/game/quest.js +31 -0
- package/dist/game/runDraft.js +2 -1
- package/dist/game/saveSlots.d.ts +102 -0
- package/dist/game/saveSlots.js +136 -0
- package/dist/game/talentTreeView.d.ts +106 -0
- package/dist/game/talentTreeView.js +114 -0
- package/dist/game/worldItem.js +2 -1
- package/dist/gameplay.d.ts +15 -4
- package/dist/gameplay.js +13 -2
- package/dist/i18n/i18n.d.ts +51 -0
- package/dist/i18n/i18n.js +82 -0
- package/dist/input/axisInput.d.ts +1 -1
- package/dist/input/axisInput.js +18 -2
- package/dist/input/rebindSession.d.ts +141 -0
- package/dist/input/rebindSession.js +235 -0
- package/dist/input/touchControlsMode.d.ts +16 -0
- package/dist/input/touchControlsMode.js +23 -0
- package/dist/input/touchScheme.d.ts +30 -1
- package/dist/input/touchScheme.js +31 -1
- package/dist/interaction/promptRegistry.d.ts +78 -0
- package/dist/interaction/promptRegistry.js +130 -0
- package/dist/inventory/storageTier.js +3 -2
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +1 -1
- package/dist/movement/movementModel.d.ts +26 -3
- package/dist/movement/movementModel.js +121 -15
- package/dist/movement/playerMovement.js +161 -25
- package/dist/physics/drivableVehicle.js +3 -1
- package/dist/physics/kinematicVehicle.d.ts +56 -1
- package/dist/physics/kinematicVehicle.js +99 -10
- package/dist/physics/vehicleObstacles.js +19 -0
- package/dist/random/resolveRng.d.ts +14 -0
- package/dist/random/resolveRng.js +31 -0
- package/dist/render/postProcessing.d.ts +12 -1
- package/dist/runtime/cameraDirector.d.ts +2 -2
- package/dist/runtime/context/combat.js +8 -0
- package/dist/runtime/gameContext.js +6 -1
- package/dist/runtime/gameContextTypes.d.ts +18 -0
- package/dist/runtime/inputSnapshot.d.ts +10 -1
- package/dist/runtime/inputSnapshot.js +13 -1
- package/dist/runtime/runtimeSave.d.ts +3 -3
- package/dist/runtime/runtimeSave.js +11 -2
- package/dist/scene/assetDiagnostics.js +2 -2
- package/dist/scene/autoTarget.js +2 -1
- package/dist/scene/captureCheck.js +3 -2
- package/dist/scene/entityStore.d.ts +4 -1
- package/dist/scene/entityStore.js +3 -0
- package/dist/scene/objectStore.d.ts +10 -0
- package/dist/scene/objectStore.js +29 -6
- package/dist/scene/sceneKinds.d.ts +18 -0
- package/dist/scene/sceneKinds.js +19 -0
- package/dist/scene/sequenceDirector.d.ts +110 -0
- package/dist/scene/sequenceDirector.js +146 -0
- package/dist/session/roles.js +3 -2
- package/dist/settings/settingsModel.d.ts +1 -0
- package/dist/settings/settingsModel.js +1 -0
- package/dist/stats/rollCheck.js +4 -2
- package/dist/time/dayNightCycle.d.ts +114 -0
- package/dist/time/dayNightCycle.js +208 -0
- package/dist/time/timerSet.d.ts +132 -0
- package/dist/time/timerSet.js +243 -0
- package/dist/ui/accessibility.d.ts +51 -0
- package/dist/ui/accessibility.js +83 -0
- package/dist/ui/coachMarks.d.ts +99 -0
- package/dist/ui/coachMarks.js +126 -0
- package/dist/ui/floatingText.d.ts +114 -0
- package/dist/ui/floatingText.js +203 -0
- package/dist/ui/modalStack.d.ts +143 -0
- package/dist/ui/modalStack.js +160 -0
- package/dist/ui/objectiveBanner.d.ts +123 -0
- package/dist/ui/objectiveBanner.js +162 -0
- package/dist/ui/photoMode.d.ts +32 -0
- package/dist/ui/photoMode.js +51 -0
- package/dist/ui/radialMenu.d.ts +47 -0
- package/dist/ui/radialMenu.js +74 -0
- package/dist/ui/screenMarkers.d.ts +128 -0
- package/dist/ui/screenMarkers.js +118 -0
- package/dist/ui.d.ts +8 -0
- package/dist/ui.js +8 -0
- package/dist/vfx/cameraShake.d.ts +97 -0
- package/dist/vfx/cameraShake.js +136 -0
- package/dist/vfx/damageDirection.d.ts +102 -0
- package/dist/vfx/damageDirection.js +188 -0
- package/dist/vfx/particles.d.ts +103 -0
- package/dist/vfx/particles.js +256 -0
- package/dist/vfx/screenEffects.d.ts +149 -0
- package/dist/vfx/screenEffects.js +191 -0
- package/dist/world/authoredObjects.d.ts +18 -0
- package/dist/world/authoredObjects.js +21 -0
- package/dist/world/authoredSpawn.js +19 -0
- package/dist/world/buildingLots.d.ts +54 -11
- package/dist/world/buildingLots.js +116 -30
- package/dist/world/cityBlocks.d.ts +45 -0
- package/dist/world/cityBlocks.js +159 -0
- package/dist/world/cityContent.d.ts +46 -2
- package/dist/world/cityContent.js +160 -2
- package/dist/world/cityGenerator.d.ts +261 -23
- package/dist/world/cityGenerator.js +643 -33
- package/dist/world/cityGeometry.d.ts +20 -0
- package/dist/world/cityGeometry.js +91 -0
- package/dist/world/cityKind.js +1 -44
- package/dist/world/fastTravel.d.ts +64 -0
- package/dist/world/fastTravel.js +108 -0
- package/dist/world/features.d.ts +16 -0
- package/dist/world/features.js +2 -0
- package/dist/world/grounded.d.ts +11 -0
- package/dist/world/grounded.js +12 -0
- package/dist/world/mapAnnotations.d.ts +75 -0
- package/dist/world/mapAnnotations.js +139 -0
- package/dist/world/markers.js +1 -0
- package/dist/world/resourceNode.d.ts +1 -1
- package/dist/world/resourceNode.js +2 -1
- package/dist/world/roads.d.ts +294 -5
- package/dist/world/roads.js +1262 -21
- package/dist/world/sharedAnnotations.d.ts +84 -0
- package/dist/world/sharedAnnotations.js +76 -0
- package/dist/world/spawnOverride.d.ts +37 -0
- package/dist/world/spawnOverride.js +50 -0
- package/dist/world/streetGenerator.d.ts +60 -11
- package/dist/world/streetGenerator.js +1786 -129
- package/dist/world/terrain.d.ts +1 -0
- package/dist/world/terrain.js +2 -0
- package/dist/world/waypoints.d.ts +63 -0
- package/dist/world/waypoints.js +137 -0
- package/dist/world.d.ts +17 -2
- package/dist/world.js +17 -2
- package/package.json +1 -1
- package/skills/ce-handoff/SKILL.md +149 -0
- package/skills/game-design/SKILL.md +2 -0
- package/skills/jgengine/SKILL.md +3 -1
- package/skills/jgengine/api.md +17 -7
- package/skills/jgengine/capabilities.md +12 -0
- package/skills/jgengine/recipes/minimal-game.md +7 -4
- package/skills/jgengine-combat/api.md +40 -4
- package/skills/jgengine-combat/capabilities.md +12 -0
- package/skills/jgengine-combat/reference.md +2 -0
- package/skills/jgengine-gameplay/api.md +212 -11
- package/skills/jgengine-gameplay/capabilities.md +66 -1
- package/skills/jgengine-world/api.md +378 -11
- package/skills/jgengine-world/capabilities.md +123 -2
- package/skills/jgengine-world/reference.md +15 -1
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { bindingLabel, normalizeKeyCode, } from "./actionBindings.js";
|
|
2
|
+
import { applyBindingOverrides } from "./bindingOverrides.js";
|
|
3
|
+
/** All codes an {@link ActionCodes} entry binds (hold + toggle, or the plain array). */
|
|
4
|
+
function allCodes(codes) {
|
|
5
|
+
if (Array.isArray(codes))
|
|
6
|
+
return [...codes];
|
|
7
|
+
const modes = codes;
|
|
8
|
+
return [...(modes.hold ?? []), ...(modes.toggle ?? [])];
|
|
9
|
+
}
|
|
10
|
+
/** The first (primary) code of an {@link ActionCodes} entry, or `null` when unbound. */
|
|
11
|
+
function primaryCode(codes) {
|
|
12
|
+
return allCodes(codes)[0] ?? null;
|
|
13
|
+
}
|
|
14
|
+
/** Replace the primary code of `codes` with `code`, keeping the hold/toggle/repeat shape. */
|
|
15
|
+
function withPrimaryCode(codes, code) {
|
|
16
|
+
if (Array.isArray(codes)) {
|
|
17
|
+
return [code, ...codes.slice(1)];
|
|
18
|
+
}
|
|
19
|
+
const modes = codes;
|
|
20
|
+
if (modes.hold !== undefined && modes.hold.length > 0) {
|
|
21
|
+
return { ...modes, hold: [code, ...modes.hold.slice(1)] };
|
|
22
|
+
}
|
|
23
|
+
if (modes.toggle !== undefined && modes.toggle.length > 0) {
|
|
24
|
+
return { ...modes, toggle: [code, ...modes.toggle.slice(1)] };
|
|
25
|
+
}
|
|
26
|
+
return { ...modes, hold: [code] };
|
|
27
|
+
}
|
|
28
|
+
function normalizeConfig(config) {
|
|
29
|
+
if ("actions" in config)
|
|
30
|
+
return config.actions.map((a) => ({ ...a }));
|
|
31
|
+
const labels = config.labels ?? {};
|
|
32
|
+
return Object.keys(config.input).map((id) => ({
|
|
33
|
+
id,
|
|
34
|
+
label: labels[id] ?? id,
|
|
35
|
+
defaultCodes: config.input[id],
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A conflict-aware key-remap session over the action-binding model — the missing
|
|
40
|
+
* editor behind a controls-settings menu. It takes the game's authored actions
|
|
41
|
+
* ({@link RebindActionConfig}s, or an {@link ActionCodesMap} + labels) plus the
|
|
42
|
+
* player's saved {@link BindingOverrides}, and tracks the *effective* binding per
|
|
43
|
+
* action (default merged with the override via `applyBindingOverrides`). `rows()`
|
|
44
|
+
* exposes each action's label, current key glyph (via `bindingLabel`), whether it
|
|
45
|
+
* is still the default, and which other actions it collides with; `conflicts()`
|
|
46
|
+
* groups every action pair sharing a normalized code (the value-add a raw
|
|
47
|
+
* override map can't give you). `beginCapture`/`capture`/`cancelCapture` drive a
|
|
48
|
+
* "press a key" rebind (codes normalized via `normalizeKeyCode`), `reset`/
|
|
49
|
+
* `resetAll` return to defaults, and `overrides()`/`apply()` hand the caller the
|
|
50
|
+
* `BindingOverrides` to persist. Nothing here is genre-specific: action ids and
|
|
51
|
+
* labels are free strings the session never interprets, and hold/toggle/repeat
|
|
52
|
+
* binding shapes survive a rebind. `snapshot`/`restore` round-trips the editor
|
|
53
|
+
* (including the in-flight capture) through a save.
|
|
54
|
+
*
|
|
55
|
+
* @capability key-rebinding conflict-aware key-remap session over the action-binding model — capture/reassign, conflict detection, reset-to-defaults, persist overrides, snapshot/restore
|
|
56
|
+
*/
|
|
57
|
+
export function createRebindSession(config) {
|
|
58
|
+
const now = config.now ?? Date.now;
|
|
59
|
+
const actionList = normalizeConfig(config);
|
|
60
|
+
const defaultMap = {};
|
|
61
|
+
for (const action of actionList)
|
|
62
|
+
defaultMap[action.id] = action.defaultCodes;
|
|
63
|
+
const knownIds = new Set(actionList.map((a) => a.id));
|
|
64
|
+
const overrides = {};
|
|
65
|
+
const changedAt = new Map();
|
|
66
|
+
let capturingActionId = null;
|
|
67
|
+
const listeners = new Set();
|
|
68
|
+
function ingestOverrides(source) {
|
|
69
|
+
for (const id of Object.keys(source)) {
|
|
70
|
+
if (knownIds.has(id))
|
|
71
|
+
overrides[id] = source[id];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (config.overrides !== undefined)
|
|
75
|
+
ingestOverrides(config.overrides);
|
|
76
|
+
function notify() {
|
|
77
|
+
for (const listener of listeners)
|
|
78
|
+
listener();
|
|
79
|
+
}
|
|
80
|
+
/** Effective codes for an action: override merged over the authored default. */
|
|
81
|
+
function effectiveMap() {
|
|
82
|
+
return applyBindingOverrides(defaultMap, overrides);
|
|
83
|
+
}
|
|
84
|
+
/** Map of normalized code → action ids currently bound to it. */
|
|
85
|
+
function codeIndex() {
|
|
86
|
+
const effective = effectiveMap();
|
|
87
|
+
const index = new Map();
|
|
88
|
+
for (const action of actionList) {
|
|
89
|
+
for (const raw of allCodes(effective[action.id])) {
|
|
90
|
+
const norm = normalizeKeyCode(raw);
|
|
91
|
+
const bucket = index.get(norm);
|
|
92
|
+
if (bucket === undefined)
|
|
93
|
+
index.set(norm, [action.id]);
|
|
94
|
+
else
|
|
95
|
+
bucket.push(action.id);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return index;
|
|
99
|
+
}
|
|
100
|
+
function rows() {
|
|
101
|
+
const effective = effectiveMap();
|
|
102
|
+
const index = codeIndex();
|
|
103
|
+
return actionList.map((action) => {
|
|
104
|
+
const codes = effective[action.id];
|
|
105
|
+
const primaryRaw = primaryCode(codes);
|
|
106
|
+
const primary = primaryRaw === null ? null : normalizeKeyCode(primaryRaw);
|
|
107
|
+
const conflictWith = new Set();
|
|
108
|
+
for (const raw of allCodes(codes)) {
|
|
109
|
+
const bound = index.get(normalizeKeyCode(raw));
|
|
110
|
+
if (bound === undefined)
|
|
111
|
+
continue;
|
|
112
|
+
for (const other of bound)
|
|
113
|
+
if (other !== action.id)
|
|
114
|
+
conflictWith.add(other);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
actionId: action.id,
|
|
118
|
+
label: action.label,
|
|
119
|
+
codes,
|
|
120
|
+
code: primary,
|
|
121
|
+
bindingLabel: primary === null ? "" : bindingLabel(primary),
|
|
122
|
+
isDefault: overrides[action.id] === undefined,
|
|
123
|
+
conflictWith: [...conflictWith],
|
|
124
|
+
changedAt: changedAt.get(action.id) ?? null,
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
actions() {
|
|
130
|
+
return actionList;
|
|
131
|
+
},
|
|
132
|
+
rows,
|
|
133
|
+
row(actionId) {
|
|
134
|
+
return rows().find((r) => r.actionId === actionId) ?? null;
|
|
135
|
+
},
|
|
136
|
+
beginCapture(actionId) {
|
|
137
|
+
if (!knownIds.has(actionId) || capturingActionId === actionId)
|
|
138
|
+
return;
|
|
139
|
+
capturingActionId = actionId;
|
|
140
|
+
notify();
|
|
141
|
+
},
|
|
142
|
+
capturingActionId() {
|
|
143
|
+
return capturingActionId;
|
|
144
|
+
},
|
|
145
|
+
isCapturing(actionId) {
|
|
146
|
+
if (actionId === undefined)
|
|
147
|
+
return capturingActionId !== null;
|
|
148
|
+
return capturingActionId === actionId;
|
|
149
|
+
},
|
|
150
|
+
capture(code) {
|
|
151
|
+
const target = capturingActionId;
|
|
152
|
+
if (target === null)
|
|
153
|
+
return false;
|
|
154
|
+
const normalized = normalizeKeyCode(code);
|
|
155
|
+
const base = effectiveMap()[target];
|
|
156
|
+
overrides[target] = withPrimaryCode(base, normalized);
|
|
157
|
+
changedAt.set(target, now());
|
|
158
|
+
capturingActionId = null;
|
|
159
|
+
notify();
|
|
160
|
+
return true;
|
|
161
|
+
},
|
|
162
|
+
cancelCapture() {
|
|
163
|
+
if (capturingActionId === null)
|
|
164
|
+
return;
|
|
165
|
+
capturingActionId = null;
|
|
166
|
+
notify();
|
|
167
|
+
},
|
|
168
|
+
conflicts() {
|
|
169
|
+
const groups = [];
|
|
170
|
+
for (const [code, actionIds] of codeIndex()) {
|
|
171
|
+
if (actionIds.length >= 2)
|
|
172
|
+
groups.push({ code, actionIds: [...actionIds] });
|
|
173
|
+
}
|
|
174
|
+
return groups;
|
|
175
|
+
},
|
|
176
|
+
hasConflicts() {
|
|
177
|
+
for (const actionIds of codeIndex().values())
|
|
178
|
+
if (actionIds.length >= 2)
|
|
179
|
+
return true;
|
|
180
|
+
return false;
|
|
181
|
+
},
|
|
182
|
+
reset(actionId) {
|
|
183
|
+
if (overrides[actionId] === undefined)
|
|
184
|
+
return;
|
|
185
|
+
delete overrides[actionId];
|
|
186
|
+
changedAt.delete(actionId);
|
|
187
|
+
notify();
|
|
188
|
+
},
|
|
189
|
+
resetAll() {
|
|
190
|
+
if (Object.keys(overrides).length === 0 && changedAt.size === 0)
|
|
191
|
+
return;
|
|
192
|
+
for (const key of Object.keys(overrides))
|
|
193
|
+
delete overrides[key];
|
|
194
|
+
changedAt.clear();
|
|
195
|
+
notify();
|
|
196
|
+
},
|
|
197
|
+
isDefault(actionId) {
|
|
198
|
+
return overrides[actionId] === undefined;
|
|
199
|
+
},
|
|
200
|
+
overrides() {
|
|
201
|
+
return { ...overrides };
|
|
202
|
+
},
|
|
203
|
+
apply(persistFn) {
|
|
204
|
+
const snapshot = { ...overrides };
|
|
205
|
+
persistFn?.(snapshot);
|
|
206
|
+
return snapshot;
|
|
207
|
+
},
|
|
208
|
+
subscribe(listener) {
|
|
209
|
+
listeners.add(listener);
|
|
210
|
+
return () => listeners.delete(listener);
|
|
211
|
+
},
|
|
212
|
+
snapshot() {
|
|
213
|
+
return {
|
|
214
|
+
overrides: { ...overrides },
|
|
215
|
+
changedAt: Object.fromEntries(changedAt),
|
|
216
|
+
capturingActionId,
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
restore(snapshot) {
|
|
220
|
+
for (const key of Object.keys(overrides))
|
|
221
|
+
delete overrides[key];
|
|
222
|
+
changedAt.clear();
|
|
223
|
+
ingestOverrides(snapshot.overrides);
|
|
224
|
+
for (const [id, at] of Object.entries(snapshot.changedAt)) {
|
|
225
|
+
if (knownIds.has(id))
|
|
226
|
+
changedAt.set(id, at);
|
|
227
|
+
}
|
|
228
|
+
capturingActionId =
|
|
229
|
+
snapshot.capturingActionId !== null && knownIds.has(snapshot.capturingActionId)
|
|
230
|
+
? snapshot.capturingActionId
|
|
231
|
+
: null;
|
|
232
|
+
notify();
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime switch for the active touch control context (see `TouchControlsConfig.modes`): gameplay
|
|
3
|
+
* calls `setTouchControlsMode(ctx, "car")` when the player boards a vehicle and `null` on exit, and
|
|
4
|
+
* the shell re-derives the touch scheme so only that context's controls are on screen. Backed by the
|
|
5
|
+
* ctx keyed store, so writes notify `ctx.subscribe` (the shell re-renders) and the value rides along
|
|
6
|
+
* with whole-world saves exactly like the gameplay state that implies it.
|
|
7
|
+
*/
|
|
8
|
+
import type { GameContext } from "../runtime/gameContext.js";
|
|
9
|
+
/**
|
|
10
|
+
* Activate a named touch control mode, or `null` to return to the base config.
|
|
11
|
+
*
|
|
12
|
+
* @capability touch-controls switch the on-screen touch control set when gameplay context changes (enter/exit vehicle, mount, build mode)
|
|
13
|
+
*/
|
|
14
|
+
export declare function setTouchControlsMode(ctx: GameContext, mode: string | null): void;
|
|
15
|
+
/** The active touch control mode, `null` when the base config applies. */
|
|
16
|
+
export declare function activeTouchControlsMode(ctx: GameContext): string | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime switch for the active touch control context (see `TouchControlsConfig.modes`): gameplay
|
|
3
|
+
* calls `setTouchControlsMode(ctx, "car")` when the player boards a vehicle and `null` on exit, and
|
|
4
|
+
* the shell re-derives the touch scheme so only that context's controls are on screen. Backed by the
|
|
5
|
+
* ctx keyed store, so writes notify `ctx.subscribe` (the shell re-renders) and the value rides along
|
|
6
|
+
* with whole-world saves exactly like the gameplay state that implies it.
|
|
7
|
+
*/
|
|
8
|
+
import { defineStore } from "../store/defineStore.js";
|
|
9
|
+
const touchModeStore = defineStore("engine.touch.mode", null);
|
|
10
|
+
/**
|
|
11
|
+
* Activate a named touch control mode, or `null` to return to the base config.
|
|
12
|
+
*
|
|
13
|
+
* @capability touch-controls switch the on-screen touch control set when gameplay context changes (enter/exit vehicle, mount, build mode)
|
|
14
|
+
*/
|
|
15
|
+
export function setTouchControlsMode(ctx, mode) {
|
|
16
|
+
if (touchModeStore.read(ctx) === mode)
|
|
17
|
+
return;
|
|
18
|
+
touchModeStore.write(ctx, mode);
|
|
19
|
+
}
|
|
20
|
+
/** The active touch control mode, `null` when the base config applies. */
|
|
21
|
+
export function activeTouchControlsMode(ctx) {
|
|
22
|
+
return touchModeStore.read(ctx);
|
|
23
|
+
}
|
|
@@ -62,6 +62,22 @@ export declare const TOUCH_STYLE_OPTIONS: readonly {
|
|
|
62
62
|
}[];
|
|
63
63
|
/** Skin used when neither the game nor the player picks one. */
|
|
64
64
|
export declare const DEFAULT_TOUCH_STYLE: TouchStyle;
|
|
65
|
+
/**
|
|
66
|
+
* How the movement joystick behaves — the two shipped variants, player-selectable in
|
|
67
|
+
* Settings → Controls. `fixed` is the classic always-visible stick in its dock corner;
|
|
68
|
+
* `floating` keeps the corner as an empty capture zone and spawns the stick centered under
|
|
69
|
+
* wherever the thumb lands, so the hand never has to find a fixed target mid-play.
|
|
70
|
+
*/
|
|
71
|
+
export type TouchJoystickVariant = "fixed" | "floating";
|
|
72
|
+
/** Every joystick variant id, in menu order. */
|
|
73
|
+
export declare const TOUCH_JOYSTICK_VARIANTS: readonly TouchJoystickVariant[];
|
|
74
|
+
/** Joystick variants as `{ value, label }` rows for the Settings → Controls selector. */
|
|
75
|
+
export declare const TOUCH_JOYSTICK_VARIANT_OPTIONS: readonly {
|
|
76
|
+
value: TouchJoystickVariant;
|
|
77
|
+
label: string;
|
|
78
|
+
}[];
|
|
79
|
+
/** Variant used when the player has not picked one. */
|
|
80
|
+
export declare const DEFAULT_TOUCH_JOYSTICK_VARIANT: TouchJoystickVariant;
|
|
65
81
|
/** Where each touch cluster docks; unset falls back to the classic bottom layout. */
|
|
66
82
|
export interface TouchLayoutConfig {
|
|
67
83
|
/** Virtual joystick / movement zone. Default `bottom-left`. */
|
|
@@ -95,6 +111,9 @@ export interface TouchButtonSpec {
|
|
|
95
111
|
export interface TouchMovementConfig {
|
|
96
112
|
axis?: "both" | "horizontal" | "vertical";
|
|
97
113
|
}
|
|
114
|
+
/** One named control context's touch config — the same shape as the base config, minus nested modes. */
|
|
115
|
+
export type TouchControlsModeConfig = Omit<TouchControlsConfig, "modes">;
|
|
116
|
+
/** Game-authored refinement of the derived touch scheme on `defineGame({ touch })` — gestures, curated buttons, hidden actions, cluster layout, skin, and per-context `modes`. */
|
|
98
117
|
export interface TouchControlsConfig {
|
|
99
118
|
/** `false` removes the virtual joystick even when movement actions are bound; an object restricts it to one axis. */
|
|
100
119
|
movement?: false | TouchMovementConfig;
|
|
@@ -112,6 +131,14 @@ export interface TouchControlsConfig {
|
|
|
112
131
|
layout?: TouchLayoutConfig;
|
|
113
132
|
/** Suggested default skin; the player's Settings choice overrides it. Default `glass`. */
|
|
114
133
|
style?: TouchStyle;
|
|
134
|
+
/**
|
|
135
|
+
* Per-context control sets for games whose verbs change at runtime — on foot vs driving vs
|
|
136
|
+
* flying. Each key is a mode name whose fields *replace* the matching base fields while that
|
|
137
|
+
* mode is active (base config = the default mode). Gameplay switches with
|
|
138
|
+
* `setTouchControlsMode(ctx, "car")` and back with `null`; only the actions a context actually
|
|
139
|
+
* uses should be visible in it, so a runner on foot never sees flight or vehicle chrome.
|
|
140
|
+
*/
|
|
141
|
+
modes?: Record<string, TouchControlsModeConfig>;
|
|
115
142
|
}
|
|
116
143
|
export interface TouchJoystick {
|
|
117
144
|
up: string | null;
|
|
@@ -157,9 +184,11 @@ export interface DeriveTouchSchemeOptions {
|
|
|
157
184
|
reserved: ReadonlySet<string>;
|
|
158
185
|
firstPerson: boolean;
|
|
159
186
|
config?: TouchControlsConfig | false;
|
|
187
|
+
/** Active control context (see {@link TouchControlsConfig.modes}); null/unknown falls back to the base config. */
|
|
188
|
+
mode?: string | null;
|
|
160
189
|
}
|
|
161
190
|
/**
|
|
162
191
|
* Null means "render no touch controls" — either the game opted out or there
|
|
163
192
|
* is nothing to synthesize.
|
|
164
193
|
*/
|
|
165
|
-
export declare function deriveTouchScheme(input: ActionCodesMap | undefined, { reserved, firstPerson, config }: DeriveTouchSchemeOptions): TouchScheme | null;
|
|
194
|
+
export declare function deriveTouchScheme(input: ActionCodesMap | undefined, { reserved, firstPerson, config: rawConfig, mode }: DeriveTouchSchemeOptions): TouchScheme | null;
|
|
@@ -44,6 +44,15 @@ export const TOUCH_STYLE_OPTIONS = [
|
|
|
44
44
|
];
|
|
45
45
|
/** Skin used when neither the game nor the player picks one. */
|
|
46
46
|
export const DEFAULT_TOUCH_STYLE = "glass";
|
|
47
|
+
/** Every joystick variant id, in menu order. */
|
|
48
|
+
export const TOUCH_JOYSTICK_VARIANTS = ["fixed", "floating"];
|
|
49
|
+
/** Joystick variants as `{ value, label }` rows for the Settings → Controls selector. */
|
|
50
|
+
export const TOUCH_JOYSTICK_VARIANT_OPTIONS = [
|
|
51
|
+
{ value: "floating", label: "Floating (under your thumb)" },
|
|
52
|
+
{ value: "fixed", label: "Fixed" },
|
|
53
|
+
];
|
|
54
|
+
/** Variant used when the player has not picked one. */
|
|
55
|
+
export const DEFAULT_TOUCH_JOYSTICK_VARIANT = "floating";
|
|
47
56
|
const DEFAULT_LOOK_SENSITIVITY = 0.005;
|
|
48
57
|
const DEFAULT_TOUCH_LAYOUT = {
|
|
49
58
|
movement: "bottom-left",
|
|
@@ -175,11 +184,27 @@ function gestureActions(gestures) {
|
|
|
175
184
|
}
|
|
176
185
|
return used;
|
|
177
186
|
}
|
|
187
|
+
/** Resolve the effective config for a mode: mode fields replace base fields; the base is the default mode. */
|
|
188
|
+
function resolveModeConfig(config, mode) {
|
|
189
|
+
if (config === false || config === undefined || mode === null || mode === undefined)
|
|
190
|
+
return config;
|
|
191
|
+
const active = config.modes?.[mode];
|
|
192
|
+
if (active === undefined)
|
|
193
|
+
return config;
|
|
194
|
+
const { modes: _modes, ...base } = config;
|
|
195
|
+
return { ...base, ...active };
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Auto-derived button count past which the dock stops being playable — two full thumb arcs. A
|
|
199
|
+
* curated `touch.buttons` list may exceed it deliberately; blind derivation may not.
|
|
200
|
+
*/
|
|
201
|
+
const DERIVED_BUTTON_BUDGET = 8;
|
|
178
202
|
/**
|
|
179
203
|
* Null means "render no touch controls" — either the game opted out or there
|
|
180
204
|
* is nothing to synthesize.
|
|
181
205
|
*/
|
|
182
|
-
export function deriveTouchScheme(input, { reserved, firstPerson, config }) {
|
|
206
|
+
export function deriveTouchScheme(input, { reserved, firstPerson, config: rawConfig, mode }) {
|
|
207
|
+
const config = resolveModeConfig(rawConfig, mode);
|
|
183
208
|
if (config === false)
|
|
184
209
|
return null;
|
|
185
210
|
const actions = Object.keys(input ?? {});
|
|
@@ -246,6 +271,11 @@ export function deriveTouchScheme(input, { reserved, firstPerson, config }) {
|
|
|
246
271
|
anchor: null,
|
|
247
272
|
image: null,
|
|
248
273
|
}));
|
|
274
|
+
if (buttons.length > DERIVED_BUTTON_BUDGET) {
|
|
275
|
+
console.warn(`[jgengine:touch] derived ${buttons.length} on-screen buttons (${buttons
|
|
276
|
+
.map((button) => button.action)
|
|
277
|
+
.join(", ")}) — more than a thumb can reach. Curate touch.buttons, hide actions with touch.hidden, or split contexts with touch.modes.`);
|
|
278
|
+
}
|
|
249
279
|
}
|
|
250
280
|
const look = config?.look ?? firstPerson;
|
|
251
281
|
if (joystick === null && buttons.length === 0 && gestures === null && !look)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type PositionedPrompt, type PromptPoint } from "./proximityPrompt.js";
|
|
2
|
+
/**
|
|
3
|
+
* A partial edit applied to an already-registered {@link PositionedPrompt} by
|
|
4
|
+
* {@link PromptRegistry.update}. Any subset of the mutable fields — everything
|
|
5
|
+
* but the stable `id` — may be supplied; omitted fields keep their prior value.
|
|
6
|
+
*/
|
|
7
|
+
export type PositionedPromptPatch = Partial<Omit<PositionedPrompt, "id">>;
|
|
8
|
+
/**
|
|
9
|
+
* The serializable shape {@link PromptRegistry.snapshot} returns and
|
|
10
|
+
* {@link PromptRegistry.restore} consumes: the full set of positioned prompts
|
|
11
|
+
* (in registration order) plus the id of the prompt that was active when the
|
|
12
|
+
* snapshot was taken. Prompt display/command payloads are free strings and ids —
|
|
13
|
+
* keep them serializable and this round-trips through a save cleanly.
|
|
14
|
+
*/
|
|
15
|
+
export interface PromptRegistrySnapshot {
|
|
16
|
+
/** Every registered prompt, in registration order (tie-break order for resolve). */
|
|
17
|
+
prompts: readonly PositionedPrompt[];
|
|
18
|
+
/** Id of the active prompt at snapshot time, or `null` if none was active. */
|
|
19
|
+
activeId: string | null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A live, observable set of interaction prompts that OWNS the registered
|
|
23
|
+
* {@link PositionedPrompt}s and tracks which one is active for a player. It is a
|
|
24
|
+
* thin stateful layer over {@link resolveActivePrompt}: registration/order lives
|
|
25
|
+
* here, the nearest-within-radius / higher-priority selection stays in the pure
|
|
26
|
+
* resolver. {@link resolve} is the per-frame hot path — call it as the player
|
|
27
|
+
* moves; subscribers fire only when the active prompt actually CHANGES.
|
|
28
|
+
*/
|
|
29
|
+
export interface PromptRegistry {
|
|
30
|
+
/**
|
|
31
|
+
* Add (or replace, by id) a positioned prompt. Replacing an existing id keeps
|
|
32
|
+
* its registration slot so tie-break order is stable. Does not re-resolve on
|
|
33
|
+
* its own — the next {@link resolve} picks up the change.
|
|
34
|
+
*/
|
|
35
|
+
register(prompt: PositionedPrompt): void;
|
|
36
|
+
/**
|
|
37
|
+
* Patch a registered prompt in place. No-op if `id` is unknown. Only the fields
|
|
38
|
+
* present in `patch` change; the rest are preserved.
|
|
39
|
+
*/
|
|
40
|
+
update(id: string, patch: PositionedPromptPatch): void;
|
|
41
|
+
/** Remove the prompt with this id, if present. Returns whether one was removed. */
|
|
42
|
+
unregister(id: string): boolean;
|
|
43
|
+
/** Remove every prompt and clear the active selection. */
|
|
44
|
+
clear(): void;
|
|
45
|
+
/** All registered prompts in registration order (do not mutate the returned array). */
|
|
46
|
+
all(): readonly PositionedPrompt[];
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the active prompt for a player at `playerPosition` by delegating to
|
|
49
|
+
* {@link resolveActivePrompt}, cache its id, and notify subscribers only when
|
|
50
|
+
* the active prompt changed since the last call — so a HUD bound to this
|
|
51
|
+
* registry re-renders on transitions, not every frame. Returns the active
|
|
52
|
+
* prompt or `null`.
|
|
53
|
+
*/
|
|
54
|
+
resolve(playerPosition: PromptPoint): PositionedPrompt | null;
|
|
55
|
+
/** The prompt selected by the most recent {@link resolve}, or `null`. */
|
|
56
|
+
active(): PositionedPrompt | null;
|
|
57
|
+
/** Observe active-prompt changes (resolve transitions, clear, restore). Returns an unsubscribe fn. */
|
|
58
|
+
subscribe(listener: () => void): () => void;
|
|
59
|
+
/** Serializable state for a save. */
|
|
60
|
+
snapshot(): PromptRegistrySnapshot;
|
|
61
|
+
/** Restore prompts + active id from a {@link PromptRegistrySnapshot}. */
|
|
62
|
+
restore(snapshot: PromptRegistrySnapshot): void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create a live, observable interaction-prompt registry over the pure
|
|
66
|
+
* {@link resolveActivePrompt} resolver. Register positioned "press E" prompts,
|
|
67
|
+
* `resolve(playerPosition)` each frame as the hero moves, and read `active()` —
|
|
68
|
+
* the nearest strictly-in-range prompt (higher `priority` beats closer). The set
|
|
69
|
+
* is owned here (register/update/unregister/clear/all); selection stays pure.
|
|
70
|
+
* Subscribers are notified only when the active prompt *changes*, so a HUD does
|
|
71
|
+
* not thrash per frame, and `snapshot`/`restore` round-trip the prompts plus the
|
|
72
|
+
* active id through a save (display payloads are free strings/ids the game owns —
|
|
73
|
+
* the registry never branches on their meaning). Allocation-aware: `all()` and
|
|
74
|
+
* `resolve()` reuse a cached array instead of rebuilding per call.
|
|
75
|
+
*
|
|
76
|
+
* @capability interaction-prompt observable nearest-interactable prompt registry over the proximity-prompt resolver — register/resolve/active with priority+range and change-notified snapshot/restore
|
|
77
|
+
*/
|
|
78
|
+
export declare function createPromptRegistry(): PromptRegistry;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { resolveActivePrompt, } from "./proximityPrompt.js";
|
|
2
|
+
/** Structural copy of a positioned prompt so external mutation can't corrupt registry state. */
|
|
3
|
+
function clonePrompt(prompt) {
|
|
4
|
+
const copy = {
|
|
5
|
+
id: prompt.id,
|
|
6
|
+
position: { x: prompt.position.x, z: prompt.position.z },
|
|
7
|
+
prompt: {
|
|
8
|
+
radius: prompt.prompt.radius,
|
|
9
|
+
display: { ...prompt.prompt.display },
|
|
10
|
+
invoke: prompt.prompt.invoke === null ? null : { ...prompt.prompt.invoke },
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
if (prompt.priority !== undefined)
|
|
14
|
+
copy.priority = prompt.priority;
|
|
15
|
+
return copy;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create a live, observable interaction-prompt registry over the pure
|
|
19
|
+
* {@link resolveActivePrompt} resolver. Register positioned "press E" prompts,
|
|
20
|
+
* `resolve(playerPosition)` each frame as the hero moves, and read `active()` —
|
|
21
|
+
* the nearest strictly-in-range prompt (higher `priority` beats closer). The set
|
|
22
|
+
* is owned here (register/update/unregister/clear/all); selection stays pure.
|
|
23
|
+
* Subscribers are notified only when the active prompt *changes*, so a HUD does
|
|
24
|
+
* not thrash per frame, and `snapshot`/`restore` round-trip the prompts plus the
|
|
25
|
+
* active id through a save (display payloads are free strings/ids the game owns —
|
|
26
|
+
* the registry never branches on their meaning). Allocation-aware: `all()` and
|
|
27
|
+
* `resolve()` reuse a cached array instead of rebuilding per call.
|
|
28
|
+
*
|
|
29
|
+
* @capability interaction-prompt observable nearest-interactable prompt registry over the proximity-prompt resolver — register/resolve/active with priority+range and change-notified snapshot/restore
|
|
30
|
+
*/
|
|
31
|
+
export function createPromptRegistry() {
|
|
32
|
+
// Insertion-ordered store; Map preserves registration order for resolve tie-breaks.
|
|
33
|
+
const prompts = new Map();
|
|
34
|
+
const listeners = new Set();
|
|
35
|
+
// Cached view of prompts.values(), rebuilt lazily only after a mutation, so the
|
|
36
|
+
// per-frame resolve() does not allocate a fresh array each call.
|
|
37
|
+
let listCache = null;
|
|
38
|
+
let activeId = null;
|
|
39
|
+
function notify() {
|
|
40
|
+
for (const listener of listeners)
|
|
41
|
+
listener();
|
|
42
|
+
}
|
|
43
|
+
function invalidate() {
|
|
44
|
+
listCache = null;
|
|
45
|
+
}
|
|
46
|
+
function list() {
|
|
47
|
+
if (listCache === null)
|
|
48
|
+
listCache = [...prompts.values()];
|
|
49
|
+
return listCache;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
register(prompt) {
|
|
53
|
+
prompts.set(prompt.id, clonePrompt(prompt));
|
|
54
|
+
invalidate();
|
|
55
|
+
},
|
|
56
|
+
update(id, patch) {
|
|
57
|
+
const existing = prompts.get(id);
|
|
58
|
+
if (existing === undefined)
|
|
59
|
+
return;
|
|
60
|
+
const merged = clonePrompt(existing);
|
|
61
|
+
if (patch.position !== undefined)
|
|
62
|
+
merged.position = { x: patch.position.x, z: patch.position.z };
|
|
63
|
+
if (patch.priority !== undefined)
|
|
64
|
+
merged.priority = patch.priority;
|
|
65
|
+
if (patch.prompt !== undefined) {
|
|
66
|
+
merged.prompt = {
|
|
67
|
+
radius: patch.prompt.radius,
|
|
68
|
+
display: { ...patch.prompt.display },
|
|
69
|
+
invoke: patch.prompt.invoke === null ? null : { ...patch.prompt.invoke },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
prompts.set(id, merged);
|
|
73
|
+
invalidate();
|
|
74
|
+
},
|
|
75
|
+
unregister(id) {
|
|
76
|
+
const removed = prompts.delete(id);
|
|
77
|
+
if (removed) {
|
|
78
|
+
invalidate();
|
|
79
|
+
if (activeId === id) {
|
|
80
|
+
activeId = null;
|
|
81
|
+
notify();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return removed;
|
|
85
|
+
},
|
|
86
|
+
clear() {
|
|
87
|
+
if (prompts.size === 0 && activeId === null)
|
|
88
|
+
return;
|
|
89
|
+
prompts.clear();
|
|
90
|
+
invalidate();
|
|
91
|
+
if (activeId !== null) {
|
|
92
|
+
activeId = null;
|
|
93
|
+
notify();
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
all() {
|
|
97
|
+
return list();
|
|
98
|
+
},
|
|
99
|
+
resolve(playerPosition) {
|
|
100
|
+
const next = resolveActivePrompt(playerPosition, list());
|
|
101
|
+
const nextId = next === null ? null : next.id;
|
|
102
|
+
if (nextId !== activeId) {
|
|
103
|
+
activeId = nextId;
|
|
104
|
+
notify();
|
|
105
|
+
}
|
|
106
|
+
return next;
|
|
107
|
+
},
|
|
108
|
+
active() {
|
|
109
|
+
return activeId === null ? null : prompts.get(activeId) ?? null;
|
|
110
|
+
},
|
|
111
|
+
subscribe(listener) {
|
|
112
|
+
listeners.add(listener);
|
|
113
|
+
return () => listeners.delete(listener);
|
|
114
|
+
},
|
|
115
|
+
snapshot() {
|
|
116
|
+
return {
|
|
117
|
+
prompts: [...prompts.values()].map(clonePrompt),
|
|
118
|
+
activeId,
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
restore(snapshot) {
|
|
122
|
+
prompts.clear();
|
|
123
|
+
for (const prompt of snapshot.prompts)
|
|
124
|
+
prompts.set(prompt.id, clonePrompt(prompt));
|
|
125
|
+
invalidate();
|
|
126
|
+
activeId = snapshot.activeId !== null && prompts.has(snapshot.activeId) ? snapshot.activeId : null;
|
|
127
|
+
notify();
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveRng } from "../random/resolveRng.js";
|
|
1
2
|
export function tierOf(tiers, inventoryId) {
|
|
2
3
|
return tiers[inventoryId] ?? "carried";
|
|
3
4
|
}
|
|
@@ -59,7 +60,7 @@ function resolveDelay(delay, rng) {
|
|
|
59
60
|
const [min, max] = delay;
|
|
60
61
|
return min + rng() * (max - min);
|
|
61
62
|
}
|
|
62
|
-
export function insureLost(lost, policy, userId, now, rng
|
|
63
|
+
export function insureLost(lost, policy, userId, now, rng) {
|
|
63
64
|
const items = lost.filter((stack) => policy.isInsured(stack.itemId));
|
|
64
65
|
if (items.length === 0)
|
|
65
66
|
return null;
|
|
@@ -67,7 +68,7 @@ export function insureLost(lost, policy, userId, now, rng = Math.random) {
|
|
|
67
68
|
userId,
|
|
68
69
|
inventoryId: policy.returnInventoryId,
|
|
69
70
|
items,
|
|
70
|
-
deliverAt: now + resolveDelay(policy.delaySeconds, rng),
|
|
71
|
+
deliverAt: now + resolveDelay(policy.delaySeconds, resolveRng(rng, "scheduleDelivery")),
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
74
|
export function resolveConsolation(policy, partition) {
|
package/dist/meta/changelog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating. */
|
|
2
|
-
export declare const VERSION = "0.
|
|
2
|
+
export declare const VERSION = "0.15.0-next.0";
|
|
3
3
|
/** One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md). */
|
|
4
4
|
export interface ChangelogEntry {
|
|
5
5
|
migrate: readonly string[];
|
package/dist/meta/changelog.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating. */
|
|
2
|
-
export const VERSION = "0.
|
|
2
|
+
export const VERSION = "0.15.0-next.0";
|
|
3
3
|
/** Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`). */
|
|
4
4
|
export const CHANGELOG = {
|
|
5
5
|
"0.14.0": {
|