@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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Tuning for `createRegenShield`: pool size, refill rate, and the post-damage grace period. */
|
|
2
|
+
export interface RegenShieldConfig {
|
|
3
|
+
max: number;
|
|
4
|
+
/** Points restored per second once the post-damage grace period has elapsed. */
|
|
5
|
+
regenPerSecond: number;
|
|
6
|
+
/** Grace period after the last damage before regen resumes. */
|
|
7
|
+
regenDelayMs: number;
|
|
8
|
+
/** Starting value; defaults to `max` (full). */
|
|
9
|
+
current?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A shield pool that stops regenerating for `regenDelayMs` after every hit, then refills at
|
|
13
|
+
* `regenPerSecond` — the delayed-regen primitive that replaces snapshot-comparing stat values per
|
|
14
|
+
* tick to detect "damage taken" (#536.3). `damage` resets the grace timer; `tick` counts it down and
|
|
15
|
+
* regenerates once it elapses.
|
|
16
|
+
*/
|
|
17
|
+
export interface RegenShield {
|
|
18
|
+
current(): number;
|
|
19
|
+
max(): number;
|
|
20
|
+
setMax(value: number): void;
|
|
21
|
+
fraction(): number;
|
|
22
|
+
isFull(): boolean;
|
|
23
|
+
isBroken(): boolean;
|
|
24
|
+
/** True while the post-damage grace period is suppressing regen. */
|
|
25
|
+
suppressed(): boolean;
|
|
26
|
+
/** Absorb `amount` and reset the regen grace timer; returns damage that got through (0 while the shield holds). */
|
|
27
|
+
damage(amount: number): number;
|
|
28
|
+
/** Add `amount` without touching the regen timer (a pickup or scripted refill). */
|
|
29
|
+
restore(amount: number): void;
|
|
30
|
+
set(value: number): void;
|
|
31
|
+
/** Advance time: count down the grace period, then regenerate once it has elapsed. */
|
|
32
|
+
tick(dtSeconds: number): void;
|
|
33
|
+
}
|
|
34
|
+
/** Builds a {@link RegenShield} that suppresses regen for `regenDelayMs` after each hit. */
|
|
35
|
+
export declare function createRegenShield(config: RegenShieldConfig): RegenShield;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Builds a {@link RegenShield} that suppresses regen for `regenDelayMs` after each hit. */
|
|
2
|
+
export function createRegenShield(config) {
|
|
3
|
+
let max = Math.max(0, config.max);
|
|
4
|
+
const regenPerSecond = Math.max(0, config.regenPerSecond);
|
|
5
|
+
const regenDelayMs = Math.max(0, config.regenDelayMs);
|
|
6
|
+
function clamp(value, ceiling) {
|
|
7
|
+
return Math.max(0, Math.min(ceiling, value));
|
|
8
|
+
}
|
|
9
|
+
let current = clamp(config.current ?? max, max);
|
|
10
|
+
let sinceDamageMs = regenDelayMs;
|
|
11
|
+
return {
|
|
12
|
+
current: () => current,
|
|
13
|
+
max: () => max,
|
|
14
|
+
setMax(value) {
|
|
15
|
+
max = Math.max(0, value);
|
|
16
|
+
current = clamp(current, max);
|
|
17
|
+
},
|
|
18
|
+
fraction: () => (max <= 0 ? 0 : current / max),
|
|
19
|
+
isFull: () => current >= max,
|
|
20
|
+
isBroken: () => current <= 0,
|
|
21
|
+
suppressed: () => sinceDamageMs < regenDelayMs,
|
|
22
|
+
damage(amount) {
|
|
23
|
+
if (amount <= 0)
|
|
24
|
+
return 0;
|
|
25
|
+
sinceDamageMs = 0;
|
|
26
|
+
const overflow = Math.max(0, amount - current);
|
|
27
|
+
current = clamp(current - amount, max);
|
|
28
|
+
return overflow;
|
|
29
|
+
},
|
|
30
|
+
restore(amount) {
|
|
31
|
+
if (amount > 0)
|
|
32
|
+
current = clamp(current + amount, max);
|
|
33
|
+
},
|
|
34
|
+
set(value) {
|
|
35
|
+
current = clamp(value, max);
|
|
36
|
+
},
|
|
37
|
+
tick(dtSeconds) {
|
|
38
|
+
if (dtSeconds <= 0)
|
|
39
|
+
return;
|
|
40
|
+
sinceDamageMs += dtSeconds * 1000;
|
|
41
|
+
if (sinceDamageMs >= regenDelayMs)
|
|
42
|
+
current = clamp(current + regenPerSecond * dtSeconds, max);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
export type ResistVerdict = "immune" | "resist" | "normal" | "vulnerable";
|
|
2
|
-
export
|
|
3
|
-
|
|
2
|
+
export declare class UnknownResistanceCategoryError extends Error {
|
|
3
|
+
readonly category: string;
|
|
4
|
+
constructor(category: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class UnknownResistancePropertyError extends Error {
|
|
7
|
+
readonly category: string;
|
|
8
|
+
readonly property: string;
|
|
9
|
+
constructor(category: string, property: string);
|
|
10
|
+
}
|
|
11
|
+
export interface ResistanceMatrix<TCategory extends string = string, TProperty extends string = string> {
|
|
12
|
+
categories: Partial<Record<TCategory, Partial<Record<TProperty, ResistVerdict>>>>;
|
|
13
|
+
categoryIds?: readonly TCategory[];
|
|
14
|
+
propertyIds?: readonly TProperty[];
|
|
4
15
|
multipliers?: Partial<Record<ResistVerdict, number>>;
|
|
5
16
|
default?: ResistVerdict;
|
|
17
|
+
unknownCategory?: ResistVerdict;
|
|
6
18
|
}
|
|
7
19
|
export interface ResistanceResult {
|
|
8
20
|
verdict: ResistVerdict;
|
|
9
21
|
multiplier: number;
|
|
10
22
|
immune: boolean;
|
|
11
23
|
}
|
|
12
|
-
export declare function resolveResistance(matrix: ResistanceMatrix, category: string, targetProperties: readonly string[]): ResistanceResult;
|
|
13
|
-
export declare function resistanceScale(matrix: ResistanceMatrix, category: string, targetProperties: readonly string[]): number;
|
|
24
|
+
export declare function resolveResistance<TCategory extends string = string, TProperty extends string = string>(matrix: ResistanceMatrix<TCategory, TProperty>, category: TCategory | string, targetProperties: readonly (TProperty | string)[]): ResistanceResult;
|
|
25
|
+
export declare function resistanceScale<TCategory extends string = string, TProperty extends string = string>(matrix: ResistanceMatrix<TCategory, TProperty>, category: TCategory | string, targetProperties: readonly (TProperty | string)[]): number;
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
export class UnknownResistanceCategoryError extends Error {
|
|
2
|
+
category;
|
|
3
|
+
constructor(category) {
|
|
4
|
+
super(`Unknown resistance category: ${category}`);
|
|
5
|
+
this.name = "UnknownResistanceCategoryError";
|
|
6
|
+
this.category = category;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class UnknownResistancePropertyError extends Error {
|
|
10
|
+
category;
|
|
11
|
+
property;
|
|
12
|
+
constructor(category, property) {
|
|
13
|
+
super(`Unknown resistance property "${property}" for category "${category}"`);
|
|
14
|
+
this.name = "UnknownResistancePropertyError";
|
|
15
|
+
this.category = category;
|
|
16
|
+
this.property = property;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
1
19
|
const DEFAULT_MULTIPLIERS = {
|
|
2
20
|
immune: 0,
|
|
3
21
|
resist: 0.5,
|
|
@@ -17,13 +35,35 @@ function summaryVerdict(matrix, immune, net) {
|
|
|
17
35
|
return "resist";
|
|
18
36
|
return "normal";
|
|
19
37
|
}
|
|
38
|
+
function fromVerdict(matrix, verdict) {
|
|
39
|
+
if (verdict === "immune")
|
|
40
|
+
return { verdict: "immune", multiplier: 0, immune: true };
|
|
41
|
+
const multiplier = multiplierFor(matrix, verdict);
|
|
42
|
+
return { verdict, multiplier, immune: false };
|
|
43
|
+
}
|
|
44
|
+
function categoryTable(matrix, category) {
|
|
45
|
+
return matrix.categories[category];
|
|
46
|
+
}
|
|
20
47
|
export function resolveResistance(matrix, category, targetProperties) {
|
|
21
|
-
const byProperty = matrix
|
|
48
|
+
const byProperty = categoryTable(matrix, category);
|
|
49
|
+
if (byProperty === undefined) {
|
|
50
|
+
if (matrix.unknownCategory !== undefined)
|
|
51
|
+
return fromVerdict(matrix, matrix.unknownCategory);
|
|
52
|
+
const catalog = matrix.categoryIds ?? Object.keys(matrix.categories);
|
|
53
|
+
if (catalog.length === 0 && matrix.default !== undefined) {
|
|
54
|
+
return fromVerdict(matrix, matrix.default);
|
|
55
|
+
}
|
|
56
|
+
throw new UnknownResistanceCategoryError(category);
|
|
57
|
+
}
|
|
58
|
+
const propertyCatalog = matrix.propertyIds !== undefined ? new Set(matrix.propertyIds) : null;
|
|
22
59
|
const fallback = matrix.default ?? "normal";
|
|
23
60
|
let net = 1;
|
|
24
61
|
let matched = false;
|
|
25
62
|
let immune = false;
|
|
26
63
|
for (const property of targetProperties) {
|
|
64
|
+
if (propertyCatalog !== null && !propertyCatalog.has(property)) {
|
|
65
|
+
throw new UnknownResistancePropertyError(category, property);
|
|
66
|
+
}
|
|
27
67
|
const verdict = byProperty[property];
|
|
28
68
|
if (verdict === undefined)
|
|
29
69
|
continue;
|
|
@@ -32,12 +72,8 @@ export function resolveResistance(matrix, category, targetProperties) {
|
|
|
32
72
|
immune = true;
|
|
33
73
|
net *= multiplierFor(matrix, verdict);
|
|
34
74
|
}
|
|
35
|
-
if (!matched)
|
|
36
|
-
|
|
37
|
-
return { verdict: "immune", multiplier: 0, immune: true };
|
|
38
|
-
const multiplier = multiplierFor(matrix, fallback);
|
|
39
|
-
return { verdict: fallback, multiplier, immune: false };
|
|
40
|
-
}
|
|
75
|
+
if (!matched)
|
|
76
|
+
return fromVerdict(matrix, fallback);
|
|
41
77
|
const multiplier = immune ? 0 : net;
|
|
42
78
|
return { verdict: summaryVerdict(matrix, immune, multiplier), multiplier, immune };
|
|
43
79
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ResourcePoolConfig {
|
|
2
|
+
max: number;
|
|
3
|
+
initial?: number;
|
|
4
|
+
regenPerSecond?: number;
|
|
5
|
+
decayPerSecond?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ResourcePool {
|
|
8
|
+
current(): number;
|
|
9
|
+
max(): number;
|
|
10
|
+
setMax(value: number): void;
|
|
11
|
+
fraction(): number;
|
|
12
|
+
canSpend(amount: number): boolean;
|
|
13
|
+
spend(amount: number): boolean;
|
|
14
|
+
gain(amount: number): number;
|
|
15
|
+
set(value: number): void;
|
|
16
|
+
tick(dtSeconds: number): void;
|
|
17
|
+
isFull(): boolean;
|
|
18
|
+
isEmpty(): boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function createResourcePool(config: ResourcePoolConfig): ResourcePool;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export function createResourcePool(config) {
|
|
2
|
+
let max = Math.max(0, config.max);
|
|
3
|
+
const regenPerSecond = Math.max(0, config.regenPerSecond ?? 0);
|
|
4
|
+
const decayPerSecond = Math.max(0, config.decayPerSecond ?? 0);
|
|
5
|
+
let current = clamp(config.initial ?? max, max);
|
|
6
|
+
function clamp(value, ceiling) {
|
|
7
|
+
return Math.max(0, Math.min(ceiling, value));
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
current() {
|
|
11
|
+
return current;
|
|
12
|
+
},
|
|
13
|
+
max() {
|
|
14
|
+
return max;
|
|
15
|
+
},
|
|
16
|
+
setMax(value) {
|
|
17
|
+
max = Math.max(0, value);
|
|
18
|
+
current = clamp(current, max);
|
|
19
|
+
},
|
|
20
|
+
fraction() {
|
|
21
|
+
return max <= 0 ? 0 : current / max;
|
|
22
|
+
},
|
|
23
|
+
canSpend(amount) {
|
|
24
|
+
return amount <= 0 || amount <= current;
|
|
25
|
+
},
|
|
26
|
+
spend(amount) {
|
|
27
|
+
if (amount <= 0)
|
|
28
|
+
return true;
|
|
29
|
+
if (amount > current)
|
|
30
|
+
return false;
|
|
31
|
+
current = clamp(current - amount, max);
|
|
32
|
+
return true;
|
|
33
|
+
},
|
|
34
|
+
gain(amount) {
|
|
35
|
+
if (amount > 0)
|
|
36
|
+
current = clamp(current + amount, max);
|
|
37
|
+
return current;
|
|
38
|
+
},
|
|
39
|
+
set(value) {
|
|
40
|
+
current = clamp(value, max);
|
|
41
|
+
},
|
|
42
|
+
tick(dtSeconds) {
|
|
43
|
+
if (dtSeconds <= 0)
|
|
44
|
+
return;
|
|
45
|
+
const net = (regenPerSecond - decayPerSecond) * dtSeconds;
|
|
46
|
+
current = clamp(current + net, max);
|
|
47
|
+
},
|
|
48
|
+
isFull() {
|
|
49
|
+
return current >= max;
|
|
50
|
+
},
|
|
51
|
+
isEmpty() {
|
|
52
|
+
return current <= 0;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { EntityPosition } from "../scene/entityStore.js";
|
|
2
|
+
import { type EntityColliderSet } from "../scene/colliders.js";
|
|
3
|
+
import type { Aim } from "../scene/spatial.js";
|
|
4
|
+
/**
|
|
5
|
+
* How a shot's world-space origin (and optional direction) is resolved before prediction/settlement.
|
|
6
|
+
* - `eye` — `aim.origin` when present, else the shooter's entity position raised to eye height; the
|
|
7
|
+
* shot traces the shooter's sightline, so what the crosshair covers is what gets hit (the default).
|
|
8
|
+
* - `legacy` — `aim.origin` when present, else the shooter's raw entity position (feet).
|
|
9
|
+
* - `entity` — always the shooter's entity position.
|
|
10
|
+
* - `entityOffset` / `muzzle` — entity-local offset rotated by the shooter's yaw (muzzle on a weapon model).
|
|
11
|
+
* - `camera` — explicit camera/reticle world origin (and optional direction override).
|
|
12
|
+
* - `world` — absolute world origin.
|
|
13
|
+
*/
|
|
14
|
+
export type ShotOriginPolicy = {
|
|
15
|
+
kind: "eye";
|
|
16
|
+
height?: number;
|
|
17
|
+
} | {
|
|
18
|
+
kind: "legacy";
|
|
19
|
+
} | {
|
|
20
|
+
kind: "entity";
|
|
21
|
+
} | {
|
|
22
|
+
kind: "entityOffset";
|
|
23
|
+
offset: EntityPosition;
|
|
24
|
+
} | {
|
|
25
|
+
kind: "muzzle";
|
|
26
|
+
offset?: EntityPosition;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "camera";
|
|
29
|
+
origin: EntityPosition;
|
|
30
|
+
direction?: EntityPosition;
|
|
31
|
+
} | {
|
|
32
|
+
kind: "world";
|
|
33
|
+
origin: EntityPosition;
|
|
34
|
+
direction?: EntityPosition;
|
|
35
|
+
};
|
|
36
|
+
export interface ResolvedShot {
|
|
37
|
+
origin: EntityPosition;
|
|
38
|
+
direction: EntityPosition;
|
|
39
|
+
}
|
|
40
|
+
export interface ShotOriginDeps {
|
|
41
|
+
positionOf(instanceId: string): EntityPosition | undefined;
|
|
42
|
+
rotationYOf?(instanceId: string): number | undefined;
|
|
43
|
+
/** When provided, the `eye` policy sizes its height from the shooter's own hitbox instead of the humanoid default. */
|
|
44
|
+
collidersOf?(instanceId: string): EntityColliderSet | null | undefined;
|
|
45
|
+
}
|
|
46
|
+
/** Shot-origin and first-person camera eye height above an entity's position: 90% of the default 1.8m hitbox top. */
|
|
47
|
+
export declare const DEFAULT_EYE_HEIGHT: number;
|
|
48
|
+
/** Eye height derived from a collider set: 90% of the tallest hitbox top, or the humanoid default when unknown. */
|
|
49
|
+
export declare function eyeHeightFromColliders(set: EntityColliderSet | null | undefined): number;
|
|
50
|
+
export declare function aimDirection(aim: Aim): EntityPosition | null;
|
|
51
|
+
export declare function aimSpreadDeg(aim: Aim): number;
|
|
52
|
+
export declare function resolveShot(deps: ShotOriginDeps, from: string, aim: Aim, policy?: ShotOriginPolicy): ResolvedShot | null;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { resolveColliders, worldOffset } from "../scene/colliders.js";
|
|
2
|
+
const DEFAULT_MUZZLE_OFFSET = [0, 1.4, 0.35];
|
|
3
|
+
const EYE_HEIGHT_RATIO = 0.9;
|
|
4
|
+
/** Shot-origin and first-person camera eye height above an entity's position: 90% of the default 1.8m hitbox top. */
|
|
5
|
+
export const DEFAULT_EYE_HEIGHT = EYE_HEIGHT_RATIO * 1.8;
|
|
6
|
+
/** Eye height derived from a collider set: 90% of the tallest hitbox top, or the humanoid default when unknown. */
|
|
7
|
+
export function eyeHeightFromColliders(set) {
|
|
8
|
+
const colliders = resolveColliders(set);
|
|
9
|
+
let top = 0;
|
|
10
|
+
for (const collider of colliders) {
|
|
11
|
+
const offsetY = collider.shape.offset?.[1] ?? 0;
|
|
12
|
+
const halfHeight = collider.shape.kind === "sphere" ? collider.shape.radius : collider.shape.halfExtents[1];
|
|
13
|
+
top = Math.max(top, offsetY + halfHeight);
|
|
14
|
+
}
|
|
15
|
+
return top > 0 ? top * EYE_HEIGHT_RATIO : DEFAULT_EYE_HEIGHT;
|
|
16
|
+
}
|
|
17
|
+
function normalize(vector) {
|
|
18
|
+
const length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
|
|
19
|
+
if (length === 0)
|
|
20
|
+
return null;
|
|
21
|
+
return [vector[0] / length, vector[1] / length, vector[2] / length];
|
|
22
|
+
}
|
|
23
|
+
export function aimDirection(aim) {
|
|
24
|
+
if ("origin" in aim)
|
|
25
|
+
return normalize(aim.direction);
|
|
26
|
+
const cosPitch = Math.cos(aim.pitch);
|
|
27
|
+
return [Math.sin(aim.yaw) * cosPitch, Math.sin(aim.pitch), Math.cos(aim.yaw) * cosPitch];
|
|
28
|
+
}
|
|
29
|
+
export function aimSpreadDeg(aim) {
|
|
30
|
+
return "origin" in aim ? 0 : aim.spread ?? 0;
|
|
31
|
+
}
|
|
32
|
+
export function resolveShot(deps, from, aim, policy = { kind: "eye" }) {
|
|
33
|
+
const aimDir = aimDirection(aim);
|
|
34
|
+
if (aimDir === null && policy.kind !== "camera" && policy.kind !== "world")
|
|
35
|
+
return null;
|
|
36
|
+
switch (policy.kind) {
|
|
37
|
+
case "eye": {
|
|
38
|
+
if ("origin" in aim) {
|
|
39
|
+
if (aimDir === null)
|
|
40
|
+
return null;
|
|
41
|
+
return { origin: aim.origin, direction: aimDir };
|
|
42
|
+
}
|
|
43
|
+
const position = deps.positionOf(from);
|
|
44
|
+
if (position === undefined || aimDir === null)
|
|
45
|
+
return null;
|
|
46
|
+
const height = policy.height ??
|
|
47
|
+
(deps.collidersOf !== undefined
|
|
48
|
+
? eyeHeightFromColliders(deps.collidersOf(from))
|
|
49
|
+
: DEFAULT_EYE_HEIGHT);
|
|
50
|
+
return { origin: [position[0], position[1] + height, position[2]], direction: aimDir };
|
|
51
|
+
}
|
|
52
|
+
case "legacy": {
|
|
53
|
+
const origin = "origin" in aim ? aim.origin : deps.positionOf(from);
|
|
54
|
+
if (origin === undefined || aimDir === null)
|
|
55
|
+
return null;
|
|
56
|
+
return { origin, direction: aimDir };
|
|
57
|
+
}
|
|
58
|
+
case "entity": {
|
|
59
|
+
const origin = deps.positionOf(from);
|
|
60
|
+
if (origin === undefined || aimDir === null)
|
|
61
|
+
return null;
|
|
62
|
+
return { origin, direction: aimDir };
|
|
63
|
+
}
|
|
64
|
+
case "entityOffset":
|
|
65
|
+
case "muzzle": {
|
|
66
|
+
const position = deps.positionOf(from);
|
|
67
|
+
if (position === undefined || aimDir === null)
|
|
68
|
+
return null;
|
|
69
|
+
const rotationY = deps.rotationYOf?.(from) ?? 0;
|
|
70
|
+
const offset = policy.kind === "muzzle" ? (policy.offset ?? DEFAULT_MUZZLE_OFFSET) : policy.offset;
|
|
71
|
+
return { origin: worldOffset(offset, position, rotationY), direction: aimDir };
|
|
72
|
+
}
|
|
73
|
+
case "camera": {
|
|
74
|
+
const direction = policy.direction !== undefined ? normalize(policy.direction) : aimDir;
|
|
75
|
+
if (direction === null)
|
|
76
|
+
return null;
|
|
77
|
+
return { origin: policy.origin, direction };
|
|
78
|
+
}
|
|
79
|
+
case "world": {
|
|
80
|
+
const direction = policy.direction !== undefined ? normalize(policy.direction) : aimDir;
|
|
81
|
+
if (direction === null)
|
|
82
|
+
return null;
|
|
83
|
+
return { origin: policy.origin, direction };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -24,6 +24,31 @@ export interface TelegraphConfig {
|
|
|
24
24
|
kind?: string;
|
|
25
25
|
}
|
|
26
26
|
export declare function telegraphProgress(windupMs: number, startedAtMs: number, nowMs: number): number;
|
|
27
|
+
export interface HazardCycleConfig {
|
|
28
|
+
windupMs: number;
|
|
29
|
+
activeMs: number;
|
|
30
|
+
/** Rest between one activation's end and the next windup; default `0` (back-to-back). */
|
|
31
|
+
cooldownMs?: number;
|
|
32
|
+
/** Shifts the whole cycle — staggers identical hazards. */
|
|
33
|
+
offsetMs?: number;
|
|
34
|
+
}
|
|
35
|
+
export type HazardPhase = "windup" | "active" | "cooldown";
|
|
36
|
+
export interface HazardCycleSample {
|
|
37
|
+
phase: HazardPhase;
|
|
38
|
+
/** Elapsed fraction of the current phase in `[0, 1]` — the windup fraction is the decal fill. */
|
|
39
|
+
fraction: number;
|
|
40
|
+
remainingMs: number;
|
|
41
|
+
/** How many full cycles have completed before this one. */
|
|
42
|
+
cycleIndex: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Deterministic repeating hazard timing — windup → active → cooldown as a pure function of absolute
|
|
46
|
+
* time, for recurring flame vents, crushers, lightning rings. Sample `hazardCycleAt` each tick: draw
|
|
47
|
+
* the telegraph during `windup`, apply the effect during `active`, rest through `cooldown`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function hazardCycleAt(config: HazardCycleConfig, nowMs: number): HazardCycleSample;
|
|
50
|
+
/** Absolute time the hazard's next `active` phase begins at or after `nowMs` — the countdown seam. */
|
|
51
|
+
export declare function nextHazardActiveAt(config: HazardCycleConfig, nowMs: number): number;
|
|
27
52
|
export declare function telegraphFired(windupMs: number, startedAtMs: number, nowMs: number): boolean;
|
|
28
53
|
export declare function telegraphTurnProgress(config: TelegraphConfig, startedTurn: number, currentTurn: number): number;
|
|
29
54
|
export declare function telegraphFiredAtTurn(config: TelegraphConfig, startedTurn: number, currentTurn: number): boolean;
|
package/dist/combat/telegraph.js
CHANGED
|
@@ -3,6 +3,53 @@ export function telegraphProgress(windupMs, startedAtMs, nowMs) {
|
|
|
3
3
|
return 1;
|
|
4
4
|
return Math.max(0, Math.min(1, (nowMs - startedAtMs) / windupMs));
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Deterministic repeating hazard timing — windup → active → cooldown as a pure function of absolute
|
|
8
|
+
* time, for recurring flame vents, crushers, lightning rings. Sample `hazardCycleAt` each tick: draw
|
|
9
|
+
* the telegraph during `windup`, apply the effect during `active`, rest through `cooldown`.
|
|
10
|
+
*/
|
|
11
|
+
export function hazardCycleAt(config, nowMs) {
|
|
12
|
+
const cooldownMs = Math.max(0, config.cooldownMs ?? 0);
|
|
13
|
+
const cycleMs = config.windupMs + config.activeMs + cooldownMs;
|
|
14
|
+
if (cycleMs <= 0)
|
|
15
|
+
return { phase: "active", fraction: 1, remainingMs: 0, cycleIndex: 0 };
|
|
16
|
+
const shifted = nowMs + (config.offsetMs ?? 0);
|
|
17
|
+
const local = ((shifted % cycleMs) + cycleMs) % cycleMs;
|
|
18
|
+
const cycleIndex = Math.floor((shifted - local) / cycleMs);
|
|
19
|
+
if (local < config.windupMs) {
|
|
20
|
+
return {
|
|
21
|
+
phase: "windup",
|
|
22
|
+
fraction: config.windupMs <= 0 ? 1 : local / config.windupMs,
|
|
23
|
+
remainingMs: config.windupMs - local,
|
|
24
|
+
cycleIndex,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const intoActive = local - config.windupMs;
|
|
28
|
+
if (intoActive < config.activeMs) {
|
|
29
|
+
return {
|
|
30
|
+
phase: "active",
|
|
31
|
+
fraction: config.activeMs <= 0 ? 1 : intoActive / config.activeMs,
|
|
32
|
+
remainingMs: config.activeMs - intoActive,
|
|
33
|
+
cycleIndex,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const intoCooldown = intoActive - config.activeMs;
|
|
37
|
+
return {
|
|
38
|
+
phase: "cooldown",
|
|
39
|
+
fraction: cooldownMs <= 0 ? 1 : intoCooldown / cooldownMs,
|
|
40
|
+
remainingMs: cooldownMs - intoCooldown,
|
|
41
|
+
cycleIndex,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Absolute time the hazard's next `active` phase begins at or after `nowMs` — the countdown seam. */
|
|
45
|
+
export function nextHazardActiveAt(config, nowMs) {
|
|
46
|
+
const sample = hazardCycleAt(config, nowMs);
|
|
47
|
+
if (sample.phase === "windup")
|
|
48
|
+
return nowMs + sample.remainingMs;
|
|
49
|
+
const cooldownMs = Math.max(0, config.cooldownMs ?? 0);
|
|
50
|
+
const restMs = sample.phase === "active" ? sample.remainingMs + cooldownMs : sample.remainingMs;
|
|
51
|
+
return nowMs + restMs + config.windupMs;
|
|
52
|
+
}
|
|
6
53
|
export function telegraphFired(windupMs, startedAtMs, nowMs) {
|
|
7
54
|
return nowMs - startedAtMs >= windupMs;
|
|
8
55
|
}
|
package/dist/crafting/recipe.js
CHANGED
|
@@ -19,6 +19,9 @@ export function stationSatisfied(recipe, context) {
|
|
|
19
19
|
return true;
|
|
20
20
|
const range = recipe.stationRange !== undefined && recipe.stationRange > 0 ? recipe.stationRange : Infinity;
|
|
21
21
|
const origin = context.origin;
|
|
22
|
+
if (range !== Infinity && origin === undefined) {
|
|
23
|
+
throw new Error(`recipe "${recipe.id}" sets stationRange=${recipe.stationRange} but CraftContext.origin is missing`);
|
|
24
|
+
}
|
|
22
25
|
const stations = context.stations ?? [];
|
|
23
26
|
for (const station of stations) {
|
|
24
27
|
if (station.catalogId !== recipe.station)
|
|
@@ -37,7 +40,7 @@ function lockedRequirements(recipe, context) {
|
|
|
37
40
|
return [];
|
|
38
41
|
const unlocked = context.unlocked;
|
|
39
42
|
if (unlocked === undefined)
|
|
40
|
-
return
|
|
43
|
+
return recipe.requires;
|
|
41
44
|
return recipe.requires.filter((id) => !unlocked(id));
|
|
42
45
|
}
|
|
43
46
|
function outputsFit(state, layout, traits, recipe) {
|