@jgengine/core 0.8.0 → 0.10.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 +36 -0
- package/dist/ai/flock.d.ts +36 -0
- package/dist/ai/flock.js +182 -0
- package/dist/ai/groupAssist.d.ts +19 -0
- package/dist/ai/groupAssist.js +59 -0
- package/dist/ai/laneSelect.d.ts +21 -0
- package/dist/ai/laneSelect.js +37 -0
- package/dist/ai/mobBrain.d.ts +68 -0
- package/dist/ai/mobBrain.js +115 -0
- package/dist/ai/spawnDirector.d.ts +13 -0
- package/dist/ai/spawnDirector.js +41 -17
- package/dist/ai/spawnPoint.d.ts +5 -0
- package/dist/ai/spawnPoint.js +5 -0
- package/dist/ai/threat.d.ts +2 -0
- package/dist/ai/threat.js +34 -0
- package/dist/audio/audioFalloff.d.ts +5 -2
- package/dist/audio/music.d.ts +53 -0
- package/dist/audio/music.js +34 -0
- package/dist/audio/synth.d.ts +60 -0
- package/dist/audio/synth.js +8 -0
- package/dist/authoring.d.ts +14 -0
- package/dist/authoring.js +14 -0
- package/dist/cartridge/runtime.d.ts +15 -0
- package/dist/cartridge/runtime.js +556 -0
- package/dist/cartridge/spec.d.ts +188 -0
- package/dist/cartridge/spec.js +19 -0
- package/dist/cartridge/validate.d.ts +2 -0
- package/dist/cartridge/validate.js +78 -0
- package/dist/combat/abilityKit.d.ts +17 -2
- package/dist/combat/abilityKit.js +62 -13
- package/dist/combat/castRunner.d.ts +40 -0
- package/dist/combat/castRunner.js +56 -0
- package/dist/combat/comboPoints.d.ts +14 -0
- package/dist/combat/comboPoints.js +52 -0
- package/dist/combat/defensiveWindow.d.ts +1 -1
- package/dist/combat/defensiveWindow.js +5 -3
- package/dist/combat/dotField.d.ts +43 -0
- package/dist/combat/dotField.js +48 -0
- package/dist/combat/projectiles.d.ts +33 -12
- package/dist/combat/projectiles.js +214 -129
- package/dist/combat/regenShield.d.ts +35 -0
- package/dist/combat/regenShield.js +45 -0
- package/dist/combat/resistance.d.ts +16 -4
- package/dist/combat/resistance.js +43 -7
- package/dist/combat/resourcePool.d.ts +20 -0
- package/dist/combat/resourcePool.js +55 -0
- package/dist/combat/shotOrigin.d.ts +52 -0
- package/dist/combat/shotOrigin.js +86 -0
- package/dist/combat/telegraph.d.ts +25 -0
- package/dist/combat/telegraph.js +47 -0
- package/dist/crafting/recipe.js +4 -1
- package/dist/devtools/devtools.d.ts +72 -24
- package/dist/devtools/devtools.js +449 -101
- package/dist/devtools/transformTunables.js +3 -3
- package/dist/devtools/tunableSchema.d.ts +140 -0
- package/dist/devtools/tunableSchema.js +476 -0
- package/dist/editor/commands.d.ts +65 -0
- package/dist/editor/commands.js +193 -0
- package/dist/editor/document.d.ts +40 -0
- package/dist/editor/document.js +153 -0
- package/dist/editor/index.d.ts +30 -0
- package/dist/editor/index.js +6 -0
- package/dist/editor/types.d.ts +69 -0
- package/dist/editor/types.js +29 -0
- package/dist/faction/factions.d.ts +33 -0
- package/dist/faction/factions.js +76 -0
- package/dist/faction/reputation.d.ts +32 -0
- package/dist/faction/reputation.js +82 -0
- package/dist/game/chat.d.ts +2 -0
- package/dist/game/chat.js +9 -1
- package/dist/game/chatFilter.d.ts +15 -0
- package/dist/game/chatFilter.js +55 -0
- package/dist/game/connectedPlayers.d.ts +28 -0
- package/dist/game/connectedPlayers.js +21 -0
- package/dist/game/controlGate.d.ts +4 -0
- package/dist/game/controlGate.js +7 -0
- package/dist/game/defineGame.d.ts +44 -1
- package/dist/game/defineGame.js +1 -0
- package/dist/game/events.d.ts +25 -0
- package/dist/game/gamePhase.d.ts +20 -0
- package/dist/game/gamePhase.js +21 -0
- package/dist/game/keyValueStore.d.ts +25 -0
- package/dist/game/keyValueStore.js +61 -0
- package/dist/game/levelSequence.d.ts +24 -2
- package/dist/game/levelSequence.js +51 -2
- package/dist/game/lootTable.d.ts +14 -1
- package/dist/game/lootTable.js +28 -7
- package/dist/game/modelAnimation.d.ts +8 -0
- package/dist/game/modelAnimation.js +18 -0
- package/dist/game/playableGame.d.ts +116 -4
- package/dist/game/playableGame.js +1 -1
- package/dist/game/quest.d.ts +7 -3
- package/dist/game/quest.js +17 -8
- package/dist/game/race.d.ts +39 -1
- package/dist/game/race.js +96 -11
- package/dist/game/recordBook.d.ts +27 -0
- package/dist/game/recordBook.js +86 -0
- package/dist/game/talents.d.ts +47 -0
- package/dist/game/talents.js +140 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/input/actionBindings.d.ts +2 -0
- package/dist/input/actionBindings.js +1 -0
- package/dist/input/axisInput.d.ts +33 -3
- package/dist/input/axisInput.js +60 -4
- package/dist/input/bindingOverrides.d.ts +25 -0
- package/dist/input/bindingOverrides.js +95 -0
- package/dist/input/pointerAxis.d.ts +30 -0
- package/dist/input/pointerAxis.js +31 -0
- package/dist/input/touchScheme.d.ts +6 -0
- package/dist/input/touchScheme.js +45 -7
- package/dist/interaction/contextMenu.d.ts +1 -0
- package/dist/interaction/contextMenu.js +1 -0
- package/dist/interaction/proximityPrompt.d.ts +3 -1
- package/dist/interaction/proximityPrompt.js +3 -3
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +44 -1
- package/dist/movement/dash.d.ts +3 -0
- package/dist/movement/dash.js +19 -5
- package/dist/movement/glideModel.d.ts +44 -0
- package/dist/movement/glideModel.js +54 -0
- package/dist/movement/grappleSwing.d.ts +34 -0
- package/dist/movement/grappleSwing.js +56 -0
- package/dist/movement/leaderTrail.d.ts +29 -0
- package/dist/movement/leaderTrail.js +67 -0
- package/dist/movement/movementModel.d.ts +4 -0
- package/dist/movement/movementModel.js +13 -0
- package/dist/movement/playerMovement.d.ts +41 -0
- package/dist/movement/playerMovement.js +181 -0
- package/dist/movement/steering.d.ts +27 -0
- package/dist/movement/steering.js +40 -0
- package/dist/multiplayer/feedWriteGate.d.ts +10 -0
- package/dist/multiplayer/feedWriteGate.js +12 -0
- package/dist/multiplayer/presenceModel.d.ts +14 -2
- package/dist/multiplayer/presenceModel.js +15 -3
- package/dist/nav/corridors.d.ts +30 -0
- package/dist/nav/corridors.js +69 -0
- package/dist/nav/navGrid.js +30 -4
- package/dist/nav/railGraph.d.ts +66 -0
- package/dist/nav/railGraph.js +129 -0
- package/dist/nav/timetable.d.ts +41 -0
- package/dist/nav/timetable.js +94 -0
- package/dist/physics/buoyancy.d.ts +6 -0
- package/dist/physics/buoyancy.js +14 -0
- package/dist/physics/flowTube.d.ts +32 -0
- package/dist/physics/flowTube.js +64 -0
- package/dist/physics/forceVolume.d.ts +27 -0
- package/dist/physics/forceVolume.js +38 -0
- package/dist/physics/kinematicVehicle.d.ts +52 -0
- package/dist/physics/kinematicVehicle.js +81 -0
- package/dist/physics/physicsWorld.d.ts +13 -6
- package/dist/physics/physicsWorld.js +169 -40
- package/dist/physics/ragdoll.js +1 -1
- package/dist/physics/vehicleBody.js +2 -1
- package/dist/random/rng.d.ts +2 -0
- package/dist/random/rng.js +2 -0
- package/dist/random/seedLink.d.ts +5 -0
- package/dist/random/seedLink.js +35 -0
- package/dist/render/postProcessing.d.ts +56 -0
- package/dist/render/postProcessing.js +1 -0
- package/dist/runtime/adapter.d.ts +19 -0
- package/dist/runtime/adapter.js +11 -5
- package/dist/runtime/cameraDirector.d.ts +6 -1
- package/dist/runtime/cameraDirector.js +6 -0
- package/dist/runtime/commandRunner.d.ts +2 -2
- package/dist/runtime/commandRunner.js +2 -2
- package/dist/runtime/gameContext.d.ts +72 -8
- package/dist/runtime/gameContext.js +374 -61
- package/dist/runtime/gameRuntime.d.ts +5 -1
- package/dist/runtime/gameRuntime.js +13 -12
- package/dist/runtime/hostPersistence.d.ts +9 -1
- package/dist/runtime/hostPersistence.js +23 -8
- package/dist/runtime/hostedGameRunner.d.ts +49 -0
- package/dist/runtime/hostedGameRunner.js +62 -0
- package/dist/runtime/hostedWorldSession.d.ts +65 -0
- package/dist/runtime/hostedWorldSession.js +57 -0
- package/dist/runtime/inputSnapshot.d.ts +18 -0
- package/dist/runtime/inputSnapshot.js +7 -0
- package/dist/runtime/motionIntents.d.ts +16 -6
- package/dist/runtime/motionIntents.js +31 -2
- package/dist/runtime/snapshot.d.ts +1 -0
- package/dist/runtime/snapshot.js +1 -1
- package/dist/runtime/visibility.d.ts +74 -0
- package/dist/runtime/visibility.js +127 -0
- package/dist/runtime/worldChannel.d.ts +58 -0
- package/dist/runtime/worldChannel.js +69 -0
- package/dist/runtime/worldMirror.d.ts +24 -0
- package/dist/runtime/worldMirror.js +31 -0
- package/dist/runtime/worldReplication.d.ts +46 -0
- package/dist/runtime/worldReplication.js +185 -0
- package/dist/runtime/worldSnapshot.d.ts +17 -0
- package/dist/runtime/worldSnapshot.js +15 -0
- package/dist/scene/assetCatalog.d.ts +1 -0
- package/dist/scene/colliders.d.ts +50 -0
- package/dist/scene/colliders.js +101 -0
- package/dist/scene/entityStats.d.ts +4 -0
- package/dist/scene/entityStats.js +21 -0
- package/dist/scene/entityStore.d.ts +51 -5
- package/dist/scene/entityStore.js +119 -12
- package/dist/scene/objectQuery.d.ts +13 -2
- package/dist/scene/objectQuery.js +34 -2
- package/dist/scene/objectStore.d.ts +2 -0
- package/dist/scene/objectStore.js +31 -12
- package/dist/scene/sceneRaycast.d.ts +80 -0
- package/dist/scene/sceneRaycast.js +284 -0
- package/dist/scene/spatial.d.ts +11 -16
- package/dist/scene/spatial.js +99 -38
- package/dist/sensor/recordingBuffer.d.ts +6 -0
- package/dist/sensor/recordingBuffer.js +7 -0
- package/dist/sensor/replayLoop.d.ts +39 -0
- package/dist/sensor/replayLoop.js +64 -0
- package/dist/sensor/visionCone.d.ts +31 -0
- package/dist/sensor/visionCone.js +72 -0
- package/dist/session/extraction.d.ts +2 -2
- package/dist/session/extraction.js +13 -2
- package/dist/settings/settingsModel.d.ts +97 -0
- package/dist/settings/settingsModel.js +100 -0
- package/dist/stats/statModifiers.d.ts +5 -2
- package/dist/stats/statModifiers.js +32 -14
- package/dist/store/observableKeyedStore.d.ts +4 -0
- package/dist/store/observableKeyedStore.js +17 -2
- package/dist/survival/decayMeter.d.ts +1 -1
- package/dist/survival/decayMeter.js +0 -1
- package/dist/tactics/fallingGrid.d.ts +2 -2
- package/dist/tactics/fallingGrid.js +2 -2
- package/dist/time/beatClock.d.ts +18 -0
- package/dist/time/beatClock.js +24 -0
- package/dist/time/idleProgress.d.ts +29 -0
- package/dist/time/idleProgress.js +31 -0
- package/dist/time/serverTick.d.ts +7 -1
- package/dist/time/serverTick.js +27 -4
- package/dist/time/stateSchedule.d.ts +57 -0
- package/dist/time/stateSchedule.js +121 -0
- package/dist/turn/commit.d.ts +2 -2
- package/dist/turn/commit.js +2 -2
- package/dist/ui/gameLayout.d.ts +125 -0
- package/dist/ui/gameLayout.js +129 -0
- package/dist/ui/hudLayout.d.ts +58 -0
- package/dist/ui/hudLayout.js +207 -0
- package/dist/ui/hudScale.d.ts +44 -0
- package/dist/ui/hudScale.js +55 -0
- package/dist/ui/orientation.d.ts +39 -0
- package/dist/ui/orientation.js +49 -0
- package/dist/ui/swingTimer.d.ts +42 -0
- package/dist/ui/swingTimer.js +38 -0
- package/dist/visibility/assetStreaming.d.ts +66 -0
- package/dist/visibility/assetStreaming.js +201 -0
- package/dist/visibility/bounds.d.ts +66 -0
- package/dist/visibility/bounds.js +113 -0
- package/dist/visibility/camera.d.ts +21 -0
- package/dist/visibility/camera.js +3 -0
- package/dist/visibility/config.d.ts +21 -0
- package/dist/visibility/config.js +1 -0
- package/dist/visibility/diagnostics.d.ts +26 -0
- package/dist/visibility/diagnostics.js +36 -0
- package/dist/visibility/distance.d.ts +11 -0
- package/dist/visibility/distance.js +30 -0
- package/dist/visibility/frustum.d.ts +47 -0
- package/dist/visibility/frustum.js +186 -0
- package/dist/visibility/occlusion.d.ts +36 -0
- package/dist/visibility/occlusion.js +45 -0
- package/dist/visibility/settings.d.ts +75 -0
- package/dist/visibility/settings.js +49 -0
- package/dist/visibility/simulationCulling.d.ts +34 -0
- package/dist/visibility/simulationCulling.js +36 -0
- package/dist/visibility/spatialIndex.d.ts +45 -0
- package/dist/visibility/spatialIndex.js +212 -0
- package/dist/visibility/visibilitySystem.d.ts +90 -0
- package/dist/visibility/visibilitySystem.js +257 -0
- package/dist/world/buildings.d.ts +6 -0
- package/dist/world/buildings.js +152 -0
- package/dist/world/carve.d.ts +7 -2
- package/dist/world/carve.js +17 -7
- package/dist/world/cellStates.d.ts +45 -0
- package/dist/world/cellStates.js +103 -0
- package/dist/world/environmentSummary.d.ts +21 -4
- package/dist/world/environmentSummary.js +22 -3
- package/dist/world/features.d.ts +185 -8
- package/dist/world/features.js +64 -6
- package/dist/world/gridInstances.d.ts +7 -8
- package/dist/world/gridInstances.js +145 -7
- package/dist/world/lod.d.ts +17 -0
- package/dist/world/lod.js +66 -0
- package/dist/world/mapLayers.d.ts +60 -0
- package/dist/world/mapLayers.js +44 -0
- package/dist/world/massing.d.ts +45 -0
- package/dist/world/massing.js +581 -0
- package/dist/world/minimap.d.ts +13 -1
- package/dist/world/minimap.js +21 -2
- package/dist/world/roads.d.ts +47 -0
- package/dist/world/roads.js +146 -0
- package/dist/world/streets.d.ts +80 -0
- package/dist/world/streets.js +144 -0
- package/dist/world/terrain.d.ts +69 -3
- package/dist/world/terrain.js +309 -16
- package/dist/world/water.d.ts +7 -0
- package/dist/world/water.js +19 -4
- package/dist/world/weather.d.ts +3 -18
- package/dist/world/weather.js +4 -17
- package/dist/world/windZones.d.ts +47 -0
- package/dist/world/windZones.js +59 -0
- package/llms.txt +3013 -1726
- package/package.json +12 -2
package/dist/ai/spawnDirector.js
CHANGED
|
@@ -80,7 +80,7 @@ export function advanceSpawnDirector(config, state, dt, ctx) {
|
|
|
80
80
|
let done = state.done;
|
|
81
81
|
const alert = Math.max(0, state.alert - alertDecay * dt);
|
|
82
82
|
const trickle = config.waves[wave]?.budgetPerSecond ?? 0;
|
|
83
|
-
const escalation =
|
|
83
|
+
const escalation = config.escalationPerSecond ?? 0;
|
|
84
84
|
const alertBonus = (config.alertBudgetPerSecond ?? 0) * alert;
|
|
85
85
|
const playerBonus = (config.playerBudgetPerSecond ?? 0) * players;
|
|
86
86
|
budget += (trickle + escalation + alertBonus + playerBonus) * dt;
|
|
@@ -118,7 +118,14 @@ export function advanceSpawnDirector(config, state, dt, ctx) {
|
|
|
118
118
|
if (spawnPoints !== undefined && spawnPoints.length > 0) {
|
|
119
119
|
const [roll, nextRng] = nextRandom(rng);
|
|
120
120
|
rng = nextRng;
|
|
121
|
-
const
|
|
121
|
+
const bias = config.spawnPointBias ?? 1;
|
|
122
|
+
const point = pickSpawnPoint({
|
|
123
|
+
candidates: spawnPoints,
|
|
124
|
+
avoid: playerPositions,
|
|
125
|
+
random: () => roll,
|
|
126
|
+
distanceBias: bias < 0 ? "far" : bias > 0 ? "near" : "none",
|
|
127
|
+
biasStrength: Math.abs(bias),
|
|
128
|
+
});
|
|
122
129
|
if (point !== null) {
|
|
123
130
|
spawn.point = point;
|
|
124
131
|
spawn.laneId = spawnPoints.indexOf(point);
|
|
@@ -134,29 +141,46 @@ export function advanceSpawnDirector(config, state, dt, ctx) {
|
|
|
134
141
|
spawns,
|
|
135
142
|
};
|
|
136
143
|
}
|
|
137
|
-
export function pickSpawnPoint(
|
|
138
|
-
|
|
144
|
+
export function pickSpawnPoint(optionsOrPoints, legacyPlayers = [], legacyOptions = { roll: 0 }) {
|
|
145
|
+
let options;
|
|
146
|
+
if (Array.isArray(optionsOrPoints)) {
|
|
147
|
+
const legacyBias = legacyOptions.bias ?? 1;
|
|
148
|
+
options = {
|
|
149
|
+
candidates: optionsOrPoints,
|
|
150
|
+
avoid: legacyPlayers,
|
|
151
|
+
random: () => legacyOptions.roll,
|
|
152
|
+
distanceBias: legacyBias < 0 ? "far" : legacyBias > 0 ? "near" : "none",
|
|
153
|
+
biasStrength: Math.abs(legacyBias),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
options = optionsOrPoints;
|
|
158
|
+
}
|
|
159
|
+
const { candidates, avoid = [], random, distanceBias = "near", biasStrength = 1 } = options;
|
|
160
|
+
if (candidates.length === 0)
|
|
139
161
|
return null;
|
|
140
|
-
|
|
162
|
+
if (distanceBias === "none" || avoid.length === 0 || biasStrength === 0) {
|
|
163
|
+
return candidates[Math.min(candidates.length - 1, Math.floor(clamp01(random()) * candidates.length))];
|
|
164
|
+
}
|
|
165
|
+
const exponent = distanceBias === "far" ? biasStrength : -biasStrength;
|
|
141
166
|
const weights = [];
|
|
142
167
|
let total = 0;
|
|
143
|
-
for (const point of
|
|
168
|
+
for (const point of candidates) {
|
|
144
169
|
let nearest = Number.POSITIVE_INFINITY;
|
|
145
|
-
for (const
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
148
|
-
nearest =
|
|
170
|
+
for (const avoidedPoint of avoid) {
|
|
171
|
+
const distance = Math.hypot(point[0] - avoidedPoint[0], point[1] - avoidedPoint[1]);
|
|
172
|
+
if (distance < nearest)
|
|
173
|
+
nearest = distance;
|
|
149
174
|
}
|
|
150
|
-
const
|
|
151
|
-
const weight = Math.max(1e-6, (1 + distance) ** -bias);
|
|
175
|
+
const weight = Math.max(1e-6, (1 + nearest) ** exponent);
|
|
152
176
|
weights.push(weight);
|
|
153
177
|
total += weight;
|
|
154
178
|
}
|
|
155
|
-
let cursor = clamp01(
|
|
156
|
-
for (let
|
|
157
|
-
cursor -= weights[
|
|
179
|
+
let cursor = clamp01(random()) * total;
|
|
180
|
+
for (let index = 0; index < candidates.length; index += 1) {
|
|
181
|
+
cursor -= weights[index];
|
|
158
182
|
if (cursor <= 0)
|
|
159
|
-
return
|
|
183
|
+
return candidates[index];
|
|
160
184
|
}
|
|
161
|
-
return
|
|
185
|
+
return candidates[candidates.length - 1];
|
|
162
186
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { NavPoint } from "../nav/navGrid.js";
|
|
2
|
+
import { type SpawnPointDistanceBias, type SpawnPointSelectionOptions } from "./spawnDirector.js";
|
|
3
|
+
export type { SpawnPointDistanceBias, SpawnPointSelectionOptions };
|
|
4
|
+
/** Selects a spawn point by game intent while keeping weighting mechanics internal. */
|
|
5
|
+
export declare function selectSpawnPoint(options: SpawnPointSelectionOptions): NavPoint | null;
|
package/dist/ai/threat.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface ThreatTable {
|
|
|
18
18
|
decay(dt: number): void;
|
|
19
19
|
highest(options?: HighestThreatOptions): string | null;
|
|
20
20
|
ranked(): ThreatEntry[];
|
|
21
|
+
taunt(sourceId: string, durationSeconds: number): void;
|
|
22
|
+
forcedTarget(): string | null;
|
|
21
23
|
remove(sourceId: string): void;
|
|
22
24
|
clear(): void;
|
|
23
25
|
size(): number;
|
package/dist/ai/threat.js
CHANGED
|
@@ -4,6 +4,8 @@ export function createThreatTable(config = {}) {
|
|
|
4
4
|
const max = config.max ?? Number.POSITIVE_INFINITY;
|
|
5
5
|
const forgetBelow = config.forgetBelow ?? DEFAULT_FORGET_BELOW;
|
|
6
6
|
const table = new Map();
|
|
7
|
+
let forcedSource = null;
|
|
8
|
+
let forcedRemaining = 0;
|
|
7
9
|
function store(sourceId, value) {
|
|
8
10
|
const clamped = Math.min(max, value);
|
|
9
11
|
if (clamped <= forgetBelow) {
|
|
@@ -13,6 +15,16 @@ export function createThreatTable(config = {}) {
|
|
|
13
15
|
table.set(sourceId, clamped);
|
|
14
16
|
return clamped;
|
|
15
17
|
}
|
|
18
|
+
function topThreat() {
|
|
19
|
+
let best = 0;
|
|
20
|
+
for (const value of table.values())
|
|
21
|
+
if (value > best)
|
|
22
|
+
best = value;
|
|
23
|
+
return best;
|
|
24
|
+
}
|
|
25
|
+
function forcedTarget() {
|
|
26
|
+
return forcedRemaining > 0 ? forcedSource : null;
|
|
27
|
+
}
|
|
16
28
|
return {
|
|
17
29
|
add(sourceId, amount) {
|
|
18
30
|
return store(sourceId, (table.get(sourceId) ?? 0) + amount);
|
|
@@ -24,6 +36,13 @@ export function createThreatTable(config = {}) {
|
|
|
24
36
|
return table.get(sourceId) ?? 0;
|
|
25
37
|
},
|
|
26
38
|
decay(dt) {
|
|
39
|
+
if (dt > 0 && forcedRemaining > 0) {
|
|
40
|
+
forcedRemaining -= dt;
|
|
41
|
+
if (forcedRemaining <= 0) {
|
|
42
|
+
forcedRemaining = 0;
|
|
43
|
+
forcedSource = null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
27
46
|
if (decayPerSecond <= 0 || dt <= 0)
|
|
28
47
|
return;
|
|
29
48
|
const loss = decayPerSecond * dt;
|
|
@@ -31,6 +50,9 @@ export function createThreatTable(config = {}) {
|
|
|
31
50
|
store(sourceId, value - loss);
|
|
32
51
|
},
|
|
33
52
|
highest(options = {}) {
|
|
53
|
+
const forced = forcedTarget();
|
|
54
|
+
if (forced !== null)
|
|
55
|
+
return forced;
|
|
34
56
|
let bestId = null;
|
|
35
57
|
let bestThreat = Number.NEGATIVE_INFINITY;
|
|
36
58
|
for (const [sourceId, value] of table) {
|
|
@@ -54,11 +76,23 @@ export function createThreatTable(config = {}) {
|
|
|
54
76
|
.map(([sourceId, threat]) => ({ sourceId, threat }))
|
|
55
77
|
.sort((a, b) => b.threat - a.threat || a.sourceId.localeCompare(b.sourceId));
|
|
56
78
|
},
|
|
79
|
+
taunt(sourceId, durationSeconds) {
|
|
80
|
+
store(sourceId, Math.max(table.get(sourceId) ?? 0, topThreat()));
|
|
81
|
+
forcedSource = sourceId;
|
|
82
|
+
forcedRemaining = Math.max(0, durationSeconds);
|
|
83
|
+
},
|
|
84
|
+
forcedTarget,
|
|
57
85
|
remove(sourceId) {
|
|
58
86
|
table.delete(sourceId);
|
|
87
|
+
if (forcedSource === sourceId) {
|
|
88
|
+
forcedSource = null;
|
|
89
|
+
forcedRemaining = 0;
|
|
90
|
+
}
|
|
59
91
|
},
|
|
60
92
|
clear() {
|
|
61
93
|
table.clear();
|
|
94
|
+
forcedSource = null;
|
|
95
|
+
forcedRemaining = 0;
|
|
62
96
|
},
|
|
63
97
|
size() {
|
|
64
98
|
return table.size;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SynthPatch } from "./synth.js";
|
|
1
2
|
export type AudioBusId = string;
|
|
2
3
|
export type FalloffCurve = "linear" | "inverse" | "none";
|
|
3
4
|
export interface AudioFalloffConfig {
|
|
@@ -15,8 +16,10 @@ export interface AudioBusDef {
|
|
|
15
16
|
}
|
|
16
17
|
export interface SoundDef {
|
|
17
18
|
id: string;
|
|
18
|
-
/**
|
|
19
|
-
url
|
|
19
|
+
/** Sample source — a URL/data URI the shell's Web Audio glue fetches and decodes. Provide exactly one of `url` or `synth`. */
|
|
20
|
+
url?: string;
|
|
21
|
+
/** Procedural source — a pure synth patch the shell realises on Web Audio, no file fetch. Provide exactly one of `url` or `synth`. */
|
|
22
|
+
synth?: SynthPatch;
|
|
20
23
|
bus: AudioBusId;
|
|
21
24
|
/** Base gain before bus/falloff multipliers. Default 1. */
|
|
22
25
|
gain?: number;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named synthesised instrument. Each maps to a voice in the shell's instrument
|
|
3
|
+
* library (`@jgengine/shell/audio/musicVoices`); an unknown name falls back to a
|
|
4
|
+
* plain sine voice so a theme is never silent.
|
|
5
|
+
*/
|
|
6
|
+
export type MusicInstrument = "strings" | "flute" | "harp" | "horn" | "choir" | "bell" | "timpani" | "bass" | "stacc" | "pad" | "lute" | "dulcimer" | "frameDrum" | "warDrum" | "reed" | "pipe" | "squareLead" | "woodBlock" | "tinyBell" | "piano" | "shaker" | "brassStab" | "cymSwell" | "oboe";
|
|
7
|
+
/** One scheduled note in a theme, positioned on the loop's quarter-note grid. */
|
|
8
|
+
export interface NoteEvent {
|
|
9
|
+
/** Quarter-note position within the loop (0-based). */
|
|
10
|
+
beat: number;
|
|
11
|
+
/** MIDI note number (60 = middle C). */
|
|
12
|
+
midi: number;
|
|
13
|
+
/** Sounding length in quarter-note beats. */
|
|
14
|
+
dur: number;
|
|
15
|
+
/** Velocity 0..1. */
|
|
16
|
+
vel: number;
|
|
17
|
+
/** Which instrument voice plays it. */
|
|
18
|
+
inst: MusicInstrument;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A through-composed, looping music track. `events` need not be sorted; the
|
|
22
|
+
* director schedules them ahead against a fixed anchor so loops are seamless.
|
|
23
|
+
*/
|
|
24
|
+
export interface MusicTheme {
|
|
25
|
+
id: string;
|
|
26
|
+
/** Tempo in beats (quarter notes) per minute. */
|
|
27
|
+
bpm: number;
|
|
28
|
+
/** Loop length in 4/4 bars; the loop is `bars * 4` beats long. */
|
|
29
|
+
bars: number;
|
|
30
|
+
events: readonly NoteEvent[];
|
|
31
|
+
/**
|
|
32
|
+
* Per-theme loudness normalisation multiplied into the crossfade target, so
|
|
33
|
+
* dense and sparse themes sit at the same perceived level. Default 1.
|
|
34
|
+
*/
|
|
35
|
+
trim?: number;
|
|
36
|
+
}
|
|
37
|
+
/** A resolved note occurrence: the source note plus its absolute onset in seconds. */
|
|
38
|
+
export interface ScheduledNote {
|
|
39
|
+
event: NoteEvent;
|
|
40
|
+
/** Absolute onset in seconds, on the timeline of the `anchorSec`/window args. */
|
|
41
|
+
when: number;
|
|
42
|
+
}
|
|
43
|
+
/** Standard equal-temperament MIDI-to-frequency (A4 = 440 Hz at MIDI 69). */
|
|
44
|
+
export declare function mtof(midi: number): number;
|
|
45
|
+
/** Loop length of a theme in seconds. */
|
|
46
|
+
export declare function themeLoopSeconds(theme: MusicTheme): number;
|
|
47
|
+
/**
|
|
48
|
+
* Pure lookahead scheduler: every note occurrence of `theme` whose onset falls
|
|
49
|
+
* in the half-open window `(fromSec, toSec]`, given the theme's loop-zero at
|
|
50
|
+
* `anchorSec`. Handles any number of loop wraps, so a director calls it once per
|
|
51
|
+
* tick with a non-overlapping window and never double-schedules a note.
|
|
52
|
+
*/
|
|
53
|
+
export declare function notesInWindow(theme: MusicTheme, anchorSec: number, fromSec: number, toSec: number): ScheduledNote[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Standard equal-temperament MIDI-to-frequency (A4 = 440 Hz at MIDI 69). */
|
|
2
|
+
export function mtof(midi) {
|
|
3
|
+
return 440 * 2 ** ((midi - 69) / 12);
|
|
4
|
+
}
|
|
5
|
+
/** Loop length of a theme in seconds. */
|
|
6
|
+
export function themeLoopSeconds(theme) {
|
|
7
|
+
return (theme.bars * 4 * 60) / theme.bpm;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Pure lookahead scheduler: every note occurrence of `theme` whose onset falls
|
|
11
|
+
* in the half-open window `(fromSec, toSec]`, given the theme's loop-zero at
|
|
12
|
+
* `anchorSec`. Handles any number of loop wraps, so a director calls it once per
|
|
13
|
+
* tick with a non-overlapping window and never double-schedules a note.
|
|
14
|
+
*/
|
|
15
|
+
export function notesInWindow(theme, anchorSec, fromSec, toSec) {
|
|
16
|
+
const loop = themeLoopSeconds(theme);
|
|
17
|
+
if (loop <= 0 || toSec <= fromSec)
|
|
18
|
+
return [];
|
|
19
|
+
const spb = 60 / theme.bpm;
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const event of theme.events) {
|
|
22
|
+
const onset = event.beat * spb;
|
|
23
|
+
let k = Math.max(0, Math.floor((fromSec - anchorSec - onset) / loop));
|
|
24
|
+
for (;;) {
|
|
25
|
+
const when = anchorSec + onset + k * loop;
|
|
26
|
+
if (when > toSec)
|
|
27
|
+
break;
|
|
28
|
+
if (when > fromSec)
|
|
29
|
+
out.push({ event, when });
|
|
30
|
+
k += 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** Oscillator waveform available to a pitched synth voice. */
|
|
2
|
+
export type SynthWave = "sine" | "square" | "sawtooth" | "triangle";
|
|
3
|
+
/** Biquad filter shape a noise voice is coloured through. */
|
|
4
|
+
export type SynthFilter = "lowpass" | "highpass" | "bandpass";
|
|
5
|
+
/**
|
|
6
|
+
* A pitched oscillator voice: a 12ms linear attack to `gain`, then an
|
|
7
|
+
* exponential decay to silence across `duration`, with an optional
|
|
8
|
+
* exponential pitch slide from `freq` to `slideTo`.
|
|
9
|
+
*/
|
|
10
|
+
export interface ToneVoice {
|
|
11
|
+
kind: "tone";
|
|
12
|
+
/** Start frequency in Hz. */
|
|
13
|
+
freq: number;
|
|
14
|
+
/** Total voice length in seconds (the decay spans this). */
|
|
15
|
+
duration: number;
|
|
16
|
+
/** Peak gain before patch/bus scaling. Default 1. */
|
|
17
|
+
gain?: number;
|
|
18
|
+
/** Oscillator waveform. Default "sine". */
|
|
19
|
+
wave?: SynthWave;
|
|
20
|
+
/** Start offset in seconds from the cue trigger. Default 0. */
|
|
21
|
+
delay?: number;
|
|
22
|
+
/** When set, pitch ramps exponentially from `freq` to this Hz across `duration`. */
|
|
23
|
+
slideTo?: number;
|
|
24
|
+
/** Attack ramp in seconds. Default 0.012. */
|
|
25
|
+
attack?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A filtered white-noise burst — impacts, whooshes, breath, crackle. Realised
|
|
29
|
+
* from a shared 1s noise buffer at a randomised playback rate and start offset,
|
|
30
|
+
* decaying exponentially to silence at `duration * decay`.
|
|
31
|
+
*/
|
|
32
|
+
export interface NoiseVoice {
|
|
33
|
+
kind: "noise";
|
|
34
|
+
/** Total voice length in seconds. */
|
|
35
|
+
duration: number;
|
|
36
|
+
/** Biquad cutoff/centre in Hz. */
|
|
37
|
+
filterFreq: number;
|
|
38
|
+
/** Peak gain before patch/bus scaling. Default 1. */
|
|
39
|
+
gain?: number;
|
|
40
|
+
/** Filter shape. Default "lowpass". */
|
|
41
|
+
filterType?: SynthFilter;
|
|
42
|
+
/** Fraction of `duration` over which the burst decays to silence. Default 0.9. */
|
|
43
|
+
decay?: number;
|
|
44
|
+
/** Start offset in seconds from the cue trigger. Default 0. */
|
|
45
|
+
delay?: number;
|
|
46
|
+
}
|
|
47
|
+
/** One layered voice of a synth cue. */
|
|
48
|
+
export type SynthVoice = ToneVoice | NoiseVoice;
|
|
49
|
+
/**
|
|
50
|
+
* A procedural sound cue: a set of voices triggered together, each with its own
|
|
51
|
+
* `delay`, summed into one one-shot. Pure serialisable data — the shell realises
|
|
52
|
+
* it on Web Audio, so the same catalog runs headless in tests with no `AudioContext`.
|
|
53
|
+
*/
|
|
54
|
+
export interface SynthPatch {
|
|
55
|
+
voices: readonly SynthVoice[];
|
|
56
|
+
/** Overall cue gain multiplier applied to every voice. Default 1. */
|
|
57
|
+
gain?: number;
|
|
58
|
+
}
|
|
59
|
+
/** Total wall-clock length of a patch in seconds — the latest voice end across all voices. */
|
|
60
|
+
export declare function patchDuration(patch: SynthPatch): number;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Total wall-clock length of a patch in seconds — the latest voice end across all voices. */
|
|
2
|
+
export function patchDuration(patch) {
|
|
3
|
+
let end = 0;
|
|
4
|
+
for (const voice of patch.voices) {
|
|
5
|
+
end = Math.max(end, (voice.delay ?? 0) + voice.duration);
|
|
6
|
+
}
|
|
7
|
+
return end;
|
|
8
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task-first JGEngine authoring surface.
|
|
3
|
+
*
|
|
4
|
+
* Game code should begin here. Domain and runtime deep imports remain available for
|
|
5
|
+
* advanced composition, but this module intentionally exposes common authored
|
|
6
|
+
* concepts without requiring callers to understand the repository layout.
|
|
7
|
+
*/
|
|
8
|
+
export { defineGame, type GameDefinition, type GameDefinitionConfig, type GameLoop, type InventoryDeclaration, type PhysicsConfig, type GameServerConfig, } from "./game/defineGame.js";
|
|
9
|
+
export { lootTable, type LootTableDef, type LootEntry, type Drop, } from "./game/lootTable.js";
|
|
10
|
+
export { biomes, voxel, plots, tilemap, flat, environment, terrain, rain, snow, grass, ocean, building, road, type WorldFeature, } from "./world/features.js";
|
|
11
|
+
export { seededRng, seededStreams } from "./random/rng.js";
|
|
12
|
+
export { selectSpawnPoint, type SpawnPointSelectionOptions, type SpawnPointDistanceBias, } from "./ai/spawnPoint.js";
|
|
13
|
+
export { contextVerb } from "./interaction/contextMenu.js";
|
|
14
|
+
export { type ActionCodesMap } from "./input/actionBindings.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task-first JGEngine authoring surface.
|
|
3
|
+
*
|
|
4
|
+
* Game code should begin here. Domain and runtime deep imports remain available for
|
|
5
|
+
* advanced composition, but this module intentionally exposes common authored
|
|
6
|
+
* concepts without requiring callers to understand the repository layout.
|
|
7
|
+
*/
|
|
8
|
+
export { defineGame, } from "./game/defineGame.js";
|
|
9
|
+
export { lootTable, } from "./game/lootTable.js";
|
|
10
|
+
export { biomes, voxel, plots, tilemap, flat, environment, terrain, rain, snow, grass, ocean, building, road, } from "./world/features.js";
|
|
11
|
+
export { seededRng, seededStreams } from "./random/rng.js";
|
|
12
|
+
export { selectSpawnPoint, } from "./ai/spawnPoint.js";
|
|
13
|
+
export { contextVerb } from "./interaction/contextMenu.js";
|
|
14
|
+
export {} from "./input/actionBindings.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type AbilityKit } from "../combat/abilityKit.js";
|
|
2
|
+
import type { GameLoop } from "../game/defineGame.js";
|
|
3
|
+
import type { GameContext, GameContextContent } from "../runtime/gameContext.js";
|
|
4
|
+
import { type CartridgeRun, type CartridgeSpec } from "./spec.js";
|
|
5
|
+
export interface CartridgeRuntime {
|
|
6
|
+
content: GameContextContent;
|
|
7
|
+
loop: GameLoop<GameContext>;
|
|
8
|
+
run(ctx: GameContext): CartridgeRun;
|
|
9
|
+
weaponKit(ctx: GameContext): AbilityKit;
|
|
10
|
+
chooseUpgrade(ctx: GameContext, offerId: string): void;
|
|
11
|
+
begin(ctx: GameContext): void;
|
|
12
|
+
reset(ctx: GameContext): void;
|
|
13
|
+
spec: CartridgeSpec;
|
|
14
|
+
}
|
|
15
|
+
export declare function createCartridge(spec: CartridgeSpec): CartridgeRuntime;
|