@jgengine/core 0.18.1 → 0.19.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 +158 -7
- package/dist/ai/decisionGraph.d.ts +48 -4
- package/dist/ai/decisionGraph.js +153 -23
- package/dist/ai/groupAssist.d.ts +10 -0
- package/dist/ai/groupAssist.js +11 -0
- package/dist/ai/pursuit.d.ts +1 -1
- package/dist/ai/pursuit.js +1 -1
- package/dist/ai/threat.d.ts +8 -0
- package/dist/ai/threat.js +10 -0
- package/dist/anim/animGraph.d.ts +11 -0
- package/dist/anim/animGraph.js +128 -0
- package/dist/anim/boneTexture.d.ts +55 -0
- package/dist/anim/boneTexture.js +43 -0
- package/dist/anim/footPlacement.d.ts +57 -0
- package/dist/anim/footPlacement.js +67 -0
- package/dist/anim/locomotionGraph.d.ts +9 -0
- package/dist/anim/locomotionGraph.js +29 -0
- package/dist/audio/audioFalloff.d.ts +2 -0
- package/dist/audio/doppler.d.ts +21 -0
- package/dist/audio/doppler.js +32 -0
- package/dist/audio/engineLayers.d.ts +83 -0
- package/dist/audio/engineLayers.js +115 -0
- package/dist/combat/abilityKit.d.ts +1 -0
- package/dist/combat/abilityKit.js +1 -0
- package/dist/combat/death.d.ts +7 -0
- package/dist/combat/shotOrigin.d.ts +6 -0
- package/dist/combat/shotOrigin.js +6 -0
- package/dist/combat/weaponFeedback.d.ts +36 -0
- package/dist/combat/weaponFeedback.js +42 -0
- package/dist/combat/weaponHandling.d.ts +161 -0
- package/dist/combat/weaponHandling.js +202 -0
- package/dist/combat/weaponPresentation.d.ts +115 -0
- package/dist/combat/weaponPresentation.js +96 -0
- package/dist/devtools/warnOnce.d.ts +8 -0
- package/dist/devtools/warnOnce.js +18 -0
- package/dist/editor/document.js +3 -1
- package/dist/editor/environment.js +8 -0
- package/dist/editor/types.d.ts +4 -0
- package/dist/faction/reputation.d.ts +6 -0
- package/dist/faction/reputation.js +11 -0
- package/dist/game/cameraConfig.d.ts +51 -1
- package/dist/game/events.d.ts +6 -0
- package/dist/game/lootTable.d.ts +4 -0
- package/dist/game/lootTable.js +16 -0
- package/dist/game/playableGame.d.ts +84 -17
- package/dist/game/toasts.d.ts +8 -0
- package/dist/game/toasts.js +7 -0
- package/dist/game/vfxInstance.d.ts +9 -0
- package/dist/game/vfxInstance.js +12 -1
- package/dist/game/viewports.d.ts +43 -0
- package/dist/game/viewports.js +46 -0
- package/dist/game/worldItem.d.ts +5 -1
- package/dist/game/worldItem.js +0 -1
- package/dist/input/actionBindings.d.ts +15 -1
- package/dist/input/actionBindings.js +14 -3
- package/dist/input/actionContexts.d.ts +25 -1
- package/dist/input/actionContexts.js +82 -25
- package/dist/input/axisInput.d.ts +4 -0
- package/dist/input/axisInput.js +4 -0
- package/dist/input/axisShaper.d.ts +73 -0
- package/dist/input/axisShaper.js +105 -0
- package/dist/input/gamepadModel.d.ts +36 -2
- package/dist/input/gamepadModel.js +69 -30
- package/dist/input/haptics.d.ts +50 -0
- package/dist/input/haptics.js +74 -0
- package/dist/input/inputBuffer.d.ts +40 -0
- package/dist/input/inputBuffer.js +59 -0
- package/dist/input/lookChannel.d.ts +10 -0
- package/dist/input/lookChannel.js +6 -0
- package/dist/input/pointer.d.ts +2 -0
- package/dist/input/pointer.js +4 -0
- package/dist/item/affix.d.ts +6 -0
- package/dist/item/affix.js +6 -0
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +112 -1
- package/dist/movement/glideModel.d.ts +3 -0
- package/dist/movement/glideModel.js +6 -0
- package/dist/movement/grappleSwing.d.ts +2 -0
- package/dist/movement/grappleSwing.js +5 -1
- package/dist/movement/movementModel.d.ts +39 -0
- package/dist/movement/movementModel.js +62 -19
- package/dist/movement/movementProbe.d.ts +51 -0
- package/dist/movement/movementProbe.js +119 -0
- package/dist/movement/playerMovement.d.ts +21 -2
- package/dist/movement/playerMovement.js +89 -6
- package/dist/movement/solidObstacles.d.ts +9 -1
- package/dist/movement/solidObstacles.js +44 -2
- package/dist/movement/voxelController.js +7 -7
- package/dist/nav/navFromEnvironment.d.ts +9 -3
- package/dist/nav/navFromEnvironment.js +18 -12
- package/dist/nav/navMesh.d.ts +43 -2
- package/dist/nav/navMesh.js +534 -128
- package/dist/physics/aircraftDynamics.d.ts +390 -0
- package/dist/physics/aircraftDynamics.js +704 -0
- package/dist/physics/boatDynamics.d.ts +116 -0
- package/dist/physics/boatDynamics.js +160 -0
- package/dist/physics/drivableVehicle.d.ts +22 -8
- package/dist/physics/drivableVehicle.js +4 -2
- package/dist/physics/flightDynamics.d.ts +21 -4
- package/dist/physics/flightDynamics.js +28 -5
- package/dist/physics/handlingProbe.d.ts +249 -0
- package/dist/physics/handlingProbe.js +606 -0
- package/dist/physics/kinematicVehicle.d.ts +32 -2
- package/dist/physics/kinematicVehicle.js +15 -0
- package/dist/physics/physicsBackend.d.ts +12 -0
- package/dist/physics/physicsBackendConformance.js +34 -0
- package/dist/physics/physicsWorldBackend.d.ts +1 -0
- package/dist/physics/physicsWorldBackend.js +33 -0
- package/dist/physics/vehicleBackendLink.d.ts +49 -0
- package/dist/physics/vehicleBackendLink.js +102 -0
- package/dist/physics/vehicleDynamics.d.ts +374 -0
- package/dist/physics/vehicleDynamics.js +737 -0
- package/dist/physics/worldColliders.d.ts +1 -1
- package/dist/physics/worldColliders.js +35 -3
- package/dist/puzzle/cellGrid.d.ts +15 -9
- package/dist/puzzle/cellGrid.js +13 -9
- package/dist/render/lookPreset.d.ts +9 -2
- package/dist/render/lookPreset.js +29 -2
- package/dist/render/postProcessing.d.ts +30 -0
- package/dist/render/postProcessing.js +1 -1
- package/dist/runtime/cameraDirector.d.ts +17 -3
- package/dist/runtime/cameraDirector.js +25 -0
- package/dist/runtime/commandInput.d.ts +37 -0
- package/dist/runtime/commandInput.js +58 -0
- package/dist/runtime/commandRunner.d.ts +27 -0
- package/dist/runtime/commandRunner.js +31 -3
- package/dist/runtime/context/combat.d.ts +1 -0
- package/dist/runtime/context/combat.js +2 -1
- package/dist/runtime/context/deathLoot.d.ts +2 -0
- package/dist/runtime/context/deathLoot.js +1 -0
- package/dist/runtime/gameContext.js +7 -0
- package/dist/runtime/gameContextTypes.d.ts +19 -3
- package/dist/runtime/gameRuntime.d.ts +3 -1
- package/dist/runtime/gameRuntime.js +4 -0
- package/dist/runtime/inputSnapshot.d.ts +6 -0
- package/dist/runtime/inputSnapshot.js +10 -0
- package/dist/runtime/localPlayers.d.ts +73 -0
- package/dist/runtime/localPlayers.js +119 -0
- package/dist/runtime/transport.d.ts +9 -0
- package/dist/runtime/transport.js +7 -1
- package/dist/scene/behaviorRuntime.d.ts +12 -2
- package/dist/scene/behaviorRuntime.js +30 -14
- package/dist/scene/behaviors.d.ts +5 -1
- package/dist/scene/sceneKinds.d.ts +9 -0
- package/dist/scene/vehicleSeat.d.ts +5 -1
- package/dist/scene/vehicleSeat.js +5 -1
- package/dist/sensor/concealment.d.ts +6 -0
- package/dist/sensor/concealment.js +8 -0
- package/dist/sensor/freezeMonitor.d.ts +6 -0
- package/dist/sensor/freezeMonitor.js +8 -0
- package/dist/sensor/perception.d.ts +11 -1
- package/dist/sensor/perception.js +57 -10
- package/dist/sensor/recordingBuffer.d.ts +3 -0
- package/dist/sensor/recordingBuffer.js +8 -0
- package/dist/session/roundState.d.ts +4 -0
- package/dist/session/roundState.js +11 -0
- package/dist/settings/frameRateLimit.d.ts +16 -0
- package/dist/settings/frameRateLimit.js +37 -0
- package/dist/settings/graphicsProfile.d.ts +2 -0
- package/dist/settings/graphicsProfile.js +12 -0
- package/dist/settings/graphicsSettings.d.ts +40 -0
- package/dist/settings/graphicsSettings.js +81 -0
- package/dist/settings/settingsModel.d.ts +9 -1
- package/dist/settings/settingsModel.js +10 -0
- package/dist/stats/eventMeter.d.ts +1 -1
- package/dist/stats/eventMeter.js +1 -1
- package/dist/vfx/feedbackMixer.d.ts +61 -0
- package/dist/vfx/feedbackMixer.js +106 -0
- package/dist/visibility/simulationCulling.d.ts +8 -1
- package/dist/visibility/simulationCulling.js +7 -0
- package/dist/visibility/spatialIndex.d.ts +21 -0
- package/dist/visibility/spatialIndex.js +16 -0
- package/dist/world/authoredSolids.d.ts +46 -0
- package/dist/world/authoredSolids.js +75 -0
- package/dist/world/buildingGenerator.js +2 -2
- package/dist/world/buildingKit.d.ts +3 -1
- package/dist/world/buildings.d.ts +34 -2
- package/dist/world/buildings.js +11 -1
- package/dist/world/cityKind.d.ts +7 -0
- package/dist/world/cityKind.js +52 -1
- package/dist/world/features.d.ts +29 -2
- package/dist/world/features.js +6 -0
- package/dist/world/footprintGrid.d.ts +7 -0
- package/dist/world/footprintGrid.js +16 -0
- package/dist/world/interiors.d.ts +1 -0
- package/dist/world/interiors.js +5 -3
- package/dist/world/lod.d.ts +6 -0
- package/dist/world/lod.js +8 -0
- package/dist/world/terraform.d.ts +8 -0
- package/dist/world/terraform.js +7 -0
- package/dist/world/walls.d.ts +7 -0
- package/dist/world/walls.js +7 -0
- package/dist/world/worldSolids.d.ts +84 -0
- package/dist/world/worldSolids.js +240 -0
- package/dist/world.d.ts +2 -2
- package/dist/world.js +1 -1
- package/package.json +1 -1
- package/skills/ce-handoff/SKILL.md +1 -1
- package/skills/game-design/SKILL.md +2 -2
- package/skills/jgengine/SKILL.md +7 -5
- package/skills/jgengine/api.md +118 -89
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine/recipes/minimal-game.md +3 -0
- package/skills/jgengine-combat/SKILL.md +5 -0
- package/skills/jgengine-combat/api.md +138 -103
- package/skills/jgengine-combat/capabilities.md +29 -1
- package/skills/jgengine-combat/recipes/weapon-feel.md +50 -0
- package/skills/jgengine-gameplay/api.md +462 -436
- package/skills/jgengine-gameplay/capabilities.md +24 -0
- package/skills/jgengine-gameplay/reference-systems.md +4 -0
- package/skills/jgengine-world/SKILL.md +15 -3
- package/skills/jgengine-world/api.md +873 -626
- package/skills/jgengine-world/capabilities.md +130 -6
- package/skills/jgengine-world/recipes/character-animation.md +39 -0
- package/skills/jgengine-world/recipes/controls.md +44 -0
- package/skills/jgengine-world/recipes/flight-feel.md +76 -0
- package/skills/jgengine-world/recipes/npc-senses.md +43 -0
- package/skills/jgengine-world/recipes/vehicle-feel.md +85 -0
- package/skills/jgengine-world/reference.md +31 -2
- package/skills/jgengine-world/visibility.md +112 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { WeaponHandlingFrame } from "./weaponHandling.js";
|
|
2
|
+
/** Camera-space position `[x, y, z]` in metres: `+x` right, `+y` up, `-z` forward. */
|
|
3
|
+
export type ViewmodelOffset = readonly [number, number, number];
|
|
4
|
+
/**
|
|
5
|
+
* How one weapon sits in first person. Offsets are camera-space metres, angles are radians, FOVs are
|
|
6
|
+
* degrees. Every field is optional; two weapons differ by the ones they set.
|
|
7
|
+
*/
|
|
8
|
+
export interface WeaponPresentationTuning {
|
|
9
|
+
/** Viewmodel anchor at the hip (default `[0.34, -0.26, -0.72]`). */
|
|
10
|
+
hip?: ViewmodelOffset;
|
|
11
|
+
/** Viewmodel anchor fully aimed; put the sight on the camera axis (default `[0, -0.07, -0.55]`). */
|
|
12
|
+
ads?: ViewmodelOffset;
|
|
13
|
+
/** Viewmodel FOV in degrees, independent of the world FOV; omit to draw the viewmodel at the world FOV. */
|
|
14
|
+
viewmodelFov?: {
|
|
15
|
+
hip?: number;
|
|
16
|
+
ads?: number;
|
|
17
|
+
};
|
|
18
|
+
/** World FOV multiplier at full ADS, e.g. `0.8` for a light zoom (default `1`). */
|
|
19
|
+
adsZoom?: number;
|
|
20
|
+
/** Viewmodel lag behind look motion. */
|
|
21
|
+
sway?: {
|
|
22
|
+
/** Radians of lag per rad/s of look speed (default `0.02`). */
|
|
23
|
+
perRadPerSec?: number;
|
|
24
|
+
/** Largest lag, rad (default `0.08`). */
|
|
25
|
+
max?: number;
|
|
26
|
+
/** How fast lag follows its target, 1/s (default `12`). */
|
|
27
|
+
response?: number;
|
|
28
|
+
/** Lag multiplier at full ADS (default `0.3`). */
|
|
29
|
+
adsScale?: number;
|
|
30
|
+
};
|
|
31
|
+
/** Walk bob driven by the movement probe's speed and `bobPhase`. */
|
|
32
|
+
bob?: {
|
|
33
|
+
/** Side and vertical amplitude in metres at `fullSpeed` (default `[0.012, 0.01]`). */
|
|
34
|
+
amplitude?: readonly [number, number];
|
|
35
|
+
/** Ground speed that gives the full amplitude, m/s (default `4`). */
|
|
36
|
+
fullSpeed?: number;
|
|
37
|
+
/** Bob multiplier at full ADS (default `0.25`). */
|
|
38
|
+
adsScale?: number;
|
|
39
|
+
};
|
|
40
|
+
/** Viewmodel reaction to the handling's recoil offset. */
|
|
41
|
+
kick?: {
|
|
42
|
+
/** Metres pushed back per radian of recoil (default `1.2`). */
|
|
43
|
+
back?: number;
|
|
44
|
+
/** Viewmodel muzzle rise per radian of recoil (default `1.5`). */
|
|
45
|
+
rise?: number;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** One frame of inputs: look motion, the movement probe, and the weapon's handling readout. */
|
|
49
|
+
export interface WeaponPresentationInput {
|
|
50
|
+
/** Look yaw speed, rad/s, positive turning left (the same sign as a camera yaw that grows left). */
|
|
51
|
+
lookYawRate: number;
|
|
52
|
+
/** Look pitch speed, rad/s, positive looking up. */
|
|
53
|
+
lookPitchRate: number;
|
|
54
|
+
/** Horizontal ground speed, m/s (e.g. `EntityRenderCues.speed`). */
|
|
55
|
+
speed: number;
|
|
56
|
+
/** `0..1` walk-cycle phase (e.g. `EntityRenderCues.bobPhase`). */
|
|
57
|
+
bobPhase: number;
|
|
58
|
+
handling: WeaponHandlingFrame;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Where the viewmodel and camera go this frame. First person applies all of it; third person and
|
|
62
|
+
* over-the-shoulder read `lookPitch`/`lookYaw`, `aimPitch`/`aimYaw` and `adsProgress` from the same frame.
|
|
63
|
+
*/
|
|
64
|
+
export interface WeaponPose {
|
|
65
|
+
/** Viewmodel anchor, camera-space metres. */
|
|
66
|
+
offset: [number, number, number];
|
|
67
|
+
/** Viewmodel rotation about its anchor, rad: `pitch > 0` muzzle up, `yaw > 0` muzzle left, `roll > 0` tilts left. */
|
|
68
|
+
pitch: number;
|
|
69
|
+
yaw: number;
|
|
70
|
+
roll: number;
|
|
71
|
+
/** Viewmodel FOV in degrees, or `null` to use the world FOV. */
|
|
72
|
+
viewmodelFov: number | null;
|
|
73
|
+
/** Multiplier for the world FOV (ADS zoom). */
|
|
74
|
+
fovScale: number;
|
|
75
|
+
/** Offset to add to the camera look, rad: aim kick plus view punch. `lookYaw > 0` pulls right. */
|
|
76
|
+
lookPitch: number;
|
|
77
|
+
lookYaw: number;
|
|
78
|
+
/** Offset shots follow, rad; a third-person rig or upper-body aim reads this. */
|
|
79
|
+
aimPitch: number;
|
|
80
|
+
aimYaw: number;
|
|
81
|
+
/** `0` hip … `1` fully aimed. */
|
|
82
|
+
adsProgress: number;
|
|
83
|
+
}
|
|
84
|
+
/** Serializable presentation state: the current sway lag. */
|
|
85
|
+
export interface WeaponPresentationState {
|
|
86
|
+
swayPitch: number;
|
|
87
|
+
swayYaw: number;
|
|
88
|
+
}
|
|
89
|
+
/** Turns a {@link WeaponHandlingFrame} plus look and movement into a viewmodel and camera pose. */
|
|
90
|
+
export interface WeaponPresentation {
|
|
91
|
+
/** Advance sway by `dt` and return the pose. The returned object is reused between calls. */
|
|
92
|
+
update(dt: number, input: WeaponPresentationInput): WeaponPose;
|
|
93
|
+
pose(): WeaponPose;
|
|
94
|
+
retune(next: WeaponPresentationTuning): void;
|
|
95
|
+
snapshot(): WeaponPresentationState;
|
|
96
|
+
restore(state: WeaponPresentationState): void;
|
|
97
|
+
reset(): void;
|
|
98
|
+
}
|
|
99
|
+
/** Default hip anchor, matching the shell's built-in viewmodel. */
|
|
100
|
+
export declare const DEFAULT_VIEWMODEL_HIP: ViewmodelOffset;
|
|
101
|
+
/** Default ADS anchor: the built-in viewmodel's top rail on the camera axis. */
|
|
102
|
+
export declare const DEFAULT_VIEWMODEL_ADS: ViewmodelOffset;
|
|
103
|
+
/**
|
|
104
|
+
* Creates a {@link WeaponPresentation}: per-weapon viewmodel offset, ADS pose and viewmodel FOV, sway
|
|
105
|
+
* and inertia from look speed, bob from the movement probe, and a recoil kick, all driven by the same
|
|
106
|
+
* handling frame third-person and over-the-shoulder rigs read. Deterministic and allocation-free per update.
|
|
107
|
+
* @capability weapon-presentation per-weapon viewmodel offset, ADS pose, viewmodel FOV, sway, bob and recoil kick from a weapon handling frame
|
|
108
|
+
*/
|
|
109
|
+
export declare function createWeaponPresentation(initial?: WeaponPresentationTuning): WeaponPresentation;
|
|
110
|
+
/**
|
|
111
|
+
* Scale for a viewmodel drawn at `viewmodelFov` through a camera at `worldFov` (both degrees): multiply
|
|
112
|
+
* the viewmodel's camera-space x and y by this and it projects as if rendered with its own FOV, without
|
|
113
|
+
* a second camera or render pass.
|
|
114
|
+
*/
|
|
115
|
+
export declare function viewmodelFovScale(worldFov: number, viewmodelFov: number): number;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** Default hip anchor, matching the shell's built-in viewmodel. */
|
|
2
|
+
export const DEFAULT_VIEWMODEL_HIP = [0.34, -0.26, -0.72];
|
|
3
|
+
/** Default ADS anchor: the built-in viewmodel's top rail on the camera axis. */
|
|
4
|
+
export const DEFAULT_VIEWMODEL_ADS = [0, -0.07, -0.55];
|
|
5
|
+
const TAU = Math.PI * 2;
|
|
6
|
+
const DEFAULT_BOB = [0.012, 0.01];
|
|
7
|
+
function lerp(hip, aimed, ads) {
|
|
8
|
+
return hip * (1 - ads) + aimed * ads;
|
|
9
|
+
}
|
|
10
|
+
function clamp(value, limit) {
|
|
11
|
+
return Math.max(-limit, Math.min(limit, value));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a {@link WeaponPresentation}: per-weapon viewmodel offset, ADS pose and viewmodel FOV, sway
|
|
15
|
+
* and inertia from look speed, bob from the movement probe, and a recoil kick, all driven by the same
|
|
16
|
+
* handling frame third-person and over-the-shoulder rigs read. Deterministic and allocation-free per update.
|
|
17
|
+
* @capability weapon-presentation per-weapon viewmodel offset, ADS pose, viewmodel FOV, sway, bob and recoil kick from a weapon handling frame
|
|
18
|
+
*/
|
|
19
|
+
export function createWeaponPresentation(initial = {}) {
|
|
20
|
+
let tuning = initial;
|
|
21
|
+
const state = { swayPitch: 0, swayYaw: 0 };
|
|
22
|
+
const out = {
|
|
23
|
+
offset: [...(tuning.hip ?? DEFAULT_VIEWMODEL_HIP)],
|
|
24
|
+
pitch: 0,
|
|
25
|
+
yaw: 0,
|
|
26
|
+
roll: 0,
|
|
27
|
+
viewmodelFov: tuning.viewmodelFov?.hip ?? null,
|
|
28
|
+
fovScale: 1,
|
|
29
|
+
lookPitch: 0,
|
|
30
|
+
lookYaw: 0,
|
|
31
|
+
aimPitch: 0,
|
|
32
|
+
aimYaw: 0,
|
|
33
|
+
adsProgress: 0,
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
update(dt, input) {
|
|
37
|
+
const step = Math.max(0, dt);
|
|
38
|
+
const handling = input.handling;
|
|
39
|
+
const ads = Math.max(0, Math.min(1, handling.adsProgress));
|
|
40
|
+
const sway = tuning.sway;
|
|
41
|
+
const swayMax = sway?.max ?? 0.08;
|
|
42
|
+
const swayScale = lerp(1, sway?.adsScale ?? 0.3, ads);
|
|
43
|
+
const per = sway?.perRadPerSec ?? 0.02;
|
|
44
|
+
const targetYaw = clamp(-input.lookYawRate * per, swayMax) * swayScale;
|
|
45
|
+
const targetPitch = clamp(-input.lookPitchRate * per, swayMax) * swayScale;
|
|
46
|
+
const follow = 1 - Math.exp(-(sway?.response ?? 12) * step);
|
|
47
|
+
state.swayYaw += (targetYaw - state.swayYaw) * follow;
|
|
48
|
+
state.swayPitch += (targetPitch - state.swayPitch) * follow;
|
|
49
|
+
const bob = tuning.bob;
|
|
50
|
+
const [bobX, bobY] = bob?.amplitude ?? DEFAULT_BOB;
|
|
51
|
+
const bobScale = Math.min(1, Math.max(0, input.speed) / Math.max(1e-6, bob?.fullSpeed ?? 4)) * lerp(1, bob?.adsScale ?? 0.25, ads);
|
|
52
|
+
const phase = input.bobPhase * TAU;
|
|
53
|
+
const recoil = Math.hypot(handling.aimPitch + handling.cameraPitch, handling.aimYaw + handling.cameraYaw);
|
|
54
|
+
const hip = tuning.hip ?? DEFAULT_VIEWMODEL_HIP;
|
|
55
|
+
const aimed = tuning.ads ?? DEFAULT_VIEWMODEL_ADS;
|
|
56
|
+
out.offset[0] = lerp(hip[0], aimed[0], ads) + Math.sin(phase) * bobX * bobScale;
|
|
57
|
+
out.offset[1] = lerp(hip[1], aimed[1], ads) - Math.abs(Math.sin(phase)) * bobY * bobScale;
|
|
58
|
+
out.offset[2] = lerp(hip[2], aimed[2], ads) + recoil * (tuning.kick?.back ?? 1.2);
|
|
59
|
+
out.pitch = state.swayPitch + recoil * (tuning.kick?.rise ?? 1.5);
|
|
60
|
+
out.yaw = state.swayYaw;
|
|
61
|
+
out.roll = state.swayYaw * 0.5;
|
|
62
|
+
const hipFov = tuning.viewmodelFov?.hip ?? tuning.viewmodelFov?.ads;
|
|
63
|
+
const adsFov = tuning.viewmodelFov?.ads ?? hipFov;
|
|
64
|
+
out.viewmodelFov = hipFov === undefined || adsFov === undefined ? null : lerp(hipFov, adsFov, ads);
|
|
65
|
+
out.fovScale = lerp(1, tuning.adsZoom ?? 1, ads);
|
|
66
|
+
out.lookPitch = handling.aimPitch + handling.cameraPitch;
|
|
67
|
+
out.lookYaw = handling.aimYaw + handling.cameraYaw;
|
|
68
|
+
out.aimPitch = handling.aimPitch;
|
|
69
|
+
out.aimYaw = handling.aimYaw;
|
|
70
|
+
out.adsProgress = ads;
|
|
71
|
+
return out;
|
|
72
|
+
},
|
|
73
|
+
pose: () => out,
|
|
74
|
+
retune(next) {
|
|
75
|
+
tuning = next;
|
|
76
|
+
},
|
|
77
|
+
snapshot: () => ({ ...state }),
|
|
78
|
+
restore(next) {
|
|
79
|
+
state.swayPitch = next.swayPitch;
|
|
80
|
+
state.swayYaw = next.swayYaw;
|
|
81
|
+
},
|
|
82
|
+
reset() {
|
|
83
|
+
state.swayPitch = 0;
|
|
84
|
+
state.swayYaw = 0;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Scale for a viewmodel drawn at `viewmodelFov` through a camera at `worldFov` (both degrees): multiply
|
|
90
|
+
* the viewmodel's camera-space x and y by this and it projects as if rendered with its own FOV, without
|
|
91
|
+
* a second camera or render pass.
|
|
92
|
+
*/
|
|
93
|
+
export function viewmodelFovScale(worldFov, viewmodelFov) {
|
|
94
|
+
const toHalf = (deg) => Math.tan((Math.max(1, Math.min(179, deg)) * Math.PI) / 360);
|
|
95
|
+
return toHalf(worldFov) / toHalf(viewmodelFov);
|
|
96
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logs a dev-mode `console.warn` once per `site`, and never in a production build. Reach for it
|
|
3
|
+
* where a permissive config would otherwise fail silently — a clipped sky dome, an ignored tint.
|
|
4
|
+
* @capability warn-once emit a dev-only warning a single time per call site
|
|
5
|
+
*/
|
|
6
|
+
export declare function warnOnce(site: string, message: string): void;
|
|
7
|
+
/** Clears the once-per-site record so a test can observe the warning again. @internal */
|
|
8
|
+
export declare function resetWarnOnce(): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isProductionEnvironment } from "./saveEndpoint.js";
|
|
2
|
+
const warned = new Set();
|
|
3
|
+
/**
|
|
4
|
+
* Logs a dev-mode `console.warn` once per `site`, and never in a production build. Reach for it
|
|
5
|
+
* where a permissive config would otherwise fail silently — a clipped sky dome, an ignored tint.
|
|
6
|
+
* @capability warn-once emit a dev-only warning a single time per call site
|
|
7
|
+
*/
|
|
8
|
+
export function warnOnce(site, message) {
|
|
9
|
+
if (warned.has(site) || isProductionEnvironment())
|
|
10
|
+
return;
|
|
11
|
+
warned.add(site);
|
|
12
|
+
const host = globalThis;
|
|
13
|
+
host.console?.warn?.(message);
|
|
14
|
+
}
|
|
15
|
+
/** Clears the once-per-site record so a test can observe the warning again. @internal */
|
|
16
|
+
export function resetWarnOnce() {
|
|
17
|
+
warned.clear();
|
|
18
|
+
}
|
package/dist/editor/document.js
CHANGED
|
@@ -19,6 +19,8 @@ function cloneEnvironment(environment) {
|
|
|
19
19
|
...(environment.zenithColor === undefined ? {} : { zenithColor: environment.zenithColor }),
|
|
20
20
|
...(environment.sunIntensity === undefined ? {} : { sunIntensity: environment.sunIntensity }),
|
|
21
21
|
...(environment.ambientIntensity === undefined ? {} : { ambientIntensity: environment.ambientIntensity }),
|
|
22
|
+
...(environment.sunAzimuth === undefined ? {} : { sunAzimuth: environment.sunAzimuth }),
|
|
23
|
+
...(environment.sunElevation === undefined ? {} : { sunElevation: environment.sunElevation }),
|
|
22
24
|
...(environment.fog === undefined ? {} : { fog: { ...environment.fog } }),
|
|
23
25
|
};
|
|
24
26
|
}
|
|
@@ -1082,7 +1084,7 @@ function decodeEnvironment(value, path, errors) {
|
|
|
1082
1084
|
failed = true;
|
|
1083
1085
|
}
|
|
1084
1086
|
}
|
|
1085
|
-
for (const key of ["sunIntensity", "ambientIntensity"]) {
|
|
1087
|
+
for (const key of ["sunIntensity", "ambientIntensity", "sunAzimuth", "sunElevation"]) {
|
|
1086
1088
|
if (value[key] === undefined)
|
|
1087
1089
|
continue;
|
|
1088
1090
|
if (typeof value[key] === "number" && Number.isFinite(value[key]))
|
|
@@ -25,6 +25,14 @@ export function skyConfigFromEnvironment(env) {
|
|
|
25
25
|
...(env.zenithColor === undefined ? {} : { zenithColor: env.zenithColor }),
|
|
26
26
|
...(env.sunIntensity === undefined ? {} : { sunIntensity: env.sunIntensity }),
|
|
27
27
|
...(env.ambientIntensity === undefined ? {} : { ambientIntensity: env.ambientIntensity }),
|
|
28
|
+
...(env.sunAzimuth === undefined && env.sunElevation === undefined
|
|
29
|
+
? {}
|
|
30
|
+
: {
|
|
31
|
+
sun: {
|
|
32
|
+
...(env.sunAzimuth === undefined ? {} : { azimuth: env.sunAzimuth }),
|
|
33
|
+
...(env.sunElevation === undefined ? {} : { elevation: env.sunElevation }),
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
28
36
|
...(env.fog === undefined ? {} : { fog: { ...env.fog } }),
|
|
29
37
|
};
|
|
30
38
|
}
|
package/dist/editor/types.d.ts
CHANGED
|
@@ -219,6 +219,10 @@ export interface EditorEnvironment {
|
|
|
219
219
|
zenithColor?: string;
|
|
220
220
|
sunIntensity?: number;
|
|
221
221
|
ambientIntensity?: number;
|
|
222
|
+
/** Sun compass bearing in degrees (0 = -Z, 90 = +X); runtime `SkySunConfig.azimuth`. */
|
|
223
|
+
sunAzimuth?: number;
|
|
224
|
+
/** Sun height above the horizon in degrees; runtime `SkySunConfig.elevation`. */
|
|
225
|
+
sunElevation?: number;
|
|
222
226
|
fog?: EditorFogConfig;
|
|
223
227
|
}
|
|
224
228
|
/** The full authored scene: every marker, volume, path, note, and sculpted terrain for a game. */
|
|
@@ -17,6 +17,10 @@ export interface ReputationLedgerConfig {
|
|
|
17
17
|
min?: number;
|
|
18
18
|
max?: number;
|
|
19
19
|
}
|
|
20
|
+
/** Plain JSON standings of a {@link ReputationLedger}: stored values only, `initial` stays config. */
|
|
21
|
+
export interface ReputationLedgerState {
|
|
22
|
+
records: Record<string, Record<string, number>>;
|
|
23
|
+
}
|
|
20
24
|
export interface ReputationLedger {
|
|
21
25
|
standing(actorId: string, factionId: string): number;
|
|
22
26
|
hasStanding(actorId: string, factionId: string): boolean;
|
|
@@ -26,6 +30,8 @@ export interface ReputationLedger {
|
|
|
26
30
|
relation(actorId: string, factionId: string): FactionRelation;
|
|
27
31
|
standings(actorId: string): Record<string, number>;
|
|
28
32
|
reset(actorId: string, factionId?: string): void;
|
|
33
|
+
snapshot(): ReputationLedgerState;
|
|
34
|
+
restore(next: ReputationLedgerState): void;
|
|
29
35
|
}
|
|
30
36
|
export declare function createReputationLedger(config?: ReputationLedgerConfig): ReputationLedger;
|
|
31
37
|
export interface EffectiveRelationInput {
|
|
@@ -77,6 +77,17 @@ export function createReputationLedger(config = {}) {
|
|
|
77
77
|
else
|
|
78
78
|
records.get(actorId)?.delete(factionId);
|
|
79
79
|
},
|
|
80
|
+
snapshot() {
|
|
81
|
+
const out = {};
|
|
82
|
+
for (const [actorId, row] of records)
|
|
83
|
+
out[actorId] = Object.fromEntries(row);
|
|
84
|
+
return { records: out };
|
|
85
|
+
},
|
|
86
|
+
restore(next) {
|
|
87
|
+
records.clear();
|
|
88
|
+
for (const [actorId, row] of Object.entries(next.records))
|
|
89
|
+
records.set(actorId, new Map(Object.entries(row)));
|
|
90
|
+
},
|
|
80
91
|
};
|
|
81
92
|
}
|
|
82
93
|
export function effectiveRelation(input) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { WeaponHandlingFrame } from "../combat/weaponHandling.js";
|
|
2
|
+
import type { WeaponPresentationTuning } from "../combat/weaponPresentation.js";
|
|
1
3
|
export interface CameraFollowState {
|
|
2
4
|
entityId: string;
|
|
3
5
|
target: {
|
|
@@ -12,6 +14,11 @@ export interface CameraFollowState {
|
|
|
12
14
|
};
|
|
13
15
|
distance: number;
|
|
14
16
|
}
|
|
17
|
+
/** What a camera rig reads about the held weapon each frame; see `GameCameraConfig.weapon`. */
|
|
18
|
+
export interface CameraWeaponView {
|
|
19
|
+
handling: WeaponHandlingFrame;
|
|
20
|
+
presentation?: WeaponPresentationTuning;
|
|
21
|
+
}
|
|
15
22
|
export interface FirstPersonCameraConfig {
|
|
16
23
|
/** Camera height above the followed entity's origin. Default 1.6. */
|
|
17
24
|
eyeHeight?: number;
|
|
@@ -170,11 +177,47 @@ export interface ChaseCameraConfig {
|
|
|
170
177
|
lookHeight?: number;
|
|
171
178
|
/** Spring-arm damping (higher = stiffer, less lag). Default 6. */
|
|
172
179
|
springDamping?: number;
|
|
173
|
-
/**
|
|
180
|
+
/**
|
|
181
|
+
* Speed→FOV curve: FOV lerps from `base` to `max` as speed climbs to `speedForMax`. Speed is the
|
|
182
|
+
* followed entity's sim `velocity` when it publishes one. `response` (default 6) eases the FOV
|
|
183
|
+
* toward the curve with `1-exp(-response*dt)`; `Infinity` applies it unsmoothed.
|
|
184
|
+
*/
|
|
174
185
|
fov?: {
|
|
175
186
|
base?: number;
|
|
176
187
|
max?: number;
|
|
177
188
|
speedForMax?: number;
|
|
189
|
+
response?: number;
|
|
190
|
+
};
|
|
191
|
+
/** Pull the boom back by up to `extra` world units as speed climbs to `speedForMax` (default the FOV curve's `speedForMax`). */
|
|
192
|
+
distanceBySpeed?: {
|
|
193
|
+
extra: number;
|
|
194
|
+
speedForMax?: number;
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Tilt the boom with the target's body pitch (`rotationX`) so the camera stays behind it on
|
|
198
|
+
* slopes and ramps. `blend` (default 1) is the fraction of the pitch followed, clamped to `max`
|
|
199
|
+
* radians (default 0.6) and eased by `response` (default 5). Omit the block to keep a level boom.
|
|
200
|
+
*/
|
|
201
|
+
pitchFollow?: {
|
|
202
|
+
blend?: number;
|
|
203
|
+
max?: number;
|
|
204
|
+
response?: number;
|
|
205
|
+
};
|
|
206
|
+
/** How `ctx.camera.kickFov(degrees)` impulses feel: the kick decays at `decay` per second (default 7) and the summed kick is clamped to `max` degrees (default 20). */
|
|
207
|
+
fovKick?: {
|
|
208
|
+
decay?: number;
|
|
209
|
+
max?: number;
|
|
210
|
+
};
|
|
211
|
+
/** Input action that, while held, swings the chase view to look back at what is behind the target. */
|
|
212
|
+
lookBackAction?: string;
|
|
213
|
+
/**
|
|
214
|
+
* Pull the boom in past scene colliders, objects, terrain and walls via `ctx.scene.raycast`
|
|
215
|
+
* (entities never block). `radius` (default 0.3) keeps the lens off the surface; `minDistance`
|
|
216
|
+
* (default 0.8) is the closest the camera gets to the look pivot. Default on; `false` disables.
|
|
217
|
+
*/
|
|
218
|
+
collision?: boolean | {
|
|
219
|
+
radius?: number;
|
|
220
|
+
minDistance?: number;
|
|
178
221
|
};
|
|
179
222
|
/** Procedural shake amplitude per unit of speed (adds to the trauma channel). Default 0.0. */
|
|
180
223
|
shakePerSpeed?: number;
|
|
@@ -446,6 +489,13 @@ export interface GameCameraConfig {
|
|
|
446
489
|
followEntityId?: string | null;
|
|
447
490
|
/** Fired each frame after cameraFollow lock is applied. */
|
|
448
491
|
onCameraFollow?: (state: CameraFollowState) => void;
|
|
492
|
+
/**
|
|
493
|
+
* The followed entity's held weapon, read every frame by the `first` and `shoulder` rigs. Return the
|
|
494
|
+
* weapon's `createWeaponHandling` frame and its presentation tuning, or `null` for none. First person
|
|
495
|
+
* poses the viewmodel from it (offset, ADS pose, viewmodel FOV, sway, bob, kick) and adds the recoil to
|
|
496
|
+
* the look; over-the-shoulder takes its ADS blend from `adsProgress` and adds the same recoil.
|
|
497
|
+
*/
|
|
498
|
+
weapon?: (entityId: string) => CameraWeaponView | null;
|
|
449
499
|
rotateSpeed?: number;
|
|
450
500
|
zoomSpeed?: number;
|
|
451
501
|
dampingFactor?: number;
|
package/dist/game/events.d.ts
CHANGED
|
@@ -236,6 +236,12 @@ export interface AudioLoopSetEvent {
|
|
|
236
236
|
rate?: number;
|
|
237
237
|
gain?: number;
|
|
238
238
|
at?: readonly [number, number, number];
|
|
239
|
+
/** Emitter velocity (world units/s) for doppler on sounds that declare `doppler`. */
|
|
240
|
+
velocity?: readonly [number, number, number];
|
|
241
|
+
/** Lowpass cutoff in Hz. */
|
|
242
|
+
lowpass?: number;
|
|
243
|
+
/** Highpass cutoff in Hz. */
|
|
244
|
+
highpass?: number;
|
|
239
245
|
}
|
|
240
246
|
/** Stop and dispose the retained loop `id`; an unknown `id` is ignored (#1051). */
|
|
241
247
|
export interface AudioLoopStopEvent {
|
package/dist/game/lootTable.d.ts
CHANGED
|
@@ -38,6 +38,10 @@ export interface LootRegistry {
|
|
|
38
38
|
register(def: LootTableDef): void;
|
|
39
39
|
has(id: string): boolean;
|
|
40
40
|
roll(id: string, rng?: () => number): Drop[];
|
|
41
|
+
/** Registered tables in registration order; plain JSON unless an entry uses `generate`. */
|
|
42
|
+
snapshot(): LootTableDef[];
|
|
43
|
+
/** Replaces every registered table with `next`, validating each. */
|
|
44
|
+
restore(next: readonly LootTableDef[]): void;
|
|
41
45
|
}
|
|
42
46
|
/** Options for {@link createLootRegistry} — inject a default RNG so bare `roll(id)` uses the world stream. */
|
|
43
47
|
export interface LootRegistryOptions {
|
package/dist/game/lootTable.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { resolveRng } from "../random/resolveRng.js";
|
|
2
|
+
function copyDef(def) {
|
|
3
|
+
return {
|
|
4
|
+
...def,
|
|
5
|
+
entries: def.entries.map((entry) => ({ ...entry, count: typeof entry.count === "number" ? entry.count : [entry.count[0], entry.count[1]] })),
|
|
6
|
+
};
|
|
7
|
+
}
|
|
2
8
|
function assertValidEntry(entry, mode) {
|
|
3
9
|
const kinds = [entry.item !== undefined, entry.currency !== undefined, entry.generate !== undefined].filter(Boolean).length;
|
|
4
10
|
if (kinds !== 1) {
|
|
@@ -88,6 +94,16 @@ export function createLootRegistry(options = {}) {
|
|
|
88
94
|
}
|
|
89
95
|
return drops;
|
|
90
96
|
},
|
|
97
|
+
snapshot() {
|
|
98
|
+
return [...tables.values()].map(copyDef);
|
|
99
|
+
},
|
|
100
|
+
restore(next) {
|
|
101
|
+
for (const def of next)
|
|
102
|
+
assertValidDef(def);
|
|
103
|
+
tables.clear();
|
|
104
|
+
for (const def of next)
|
|
105
|
+
tables.set(def.id, copyDef(def));
|
|
106
|
+
},
|
|
91
107
|
};
|
|
92
108
|
}
|
|
93
109
|
/**
|
|
@@ -6,6 +6,8 @@ import type { PostProcessingConfig } from "../render/postProcessing.js";
|
|
|
6
6
|
import type { EnvironmentSource } from "../render/environment.js";
|
|
7
7
|
import type { SpriteAtlas } from "../assets/spriteAtlas.js";
|
|
8
8
|
import type { LookPreset } from "../render/lookPreset.js";
|
|
9
|
+
import type { GamepadFeelConfig } from "../input/gamepadModel.js";
|
|
10
|
+
import type { LocalPlayersConfig } from "../runtime/localPlayers.js";
|
|
9
11
|
import type { TouchControlsConfig } from "../input/touchScheme.js";
|
|
10
12
|
import type { GameSettingsConfig } from "../settings/settingsModel.js";
|
|
11
13
|
import type { GraphicsProfile } from "../settings/graphicsProfile.js";
|
|
@@ -22,14 +24,21 @@ import type { SkyEnvironmentConfig } from "../world/features.js";
|
|
|
22
24
|
import type { VisibilityConfig } from "../visibility/config.js";
|
|
23
25
|
import type { GameDefinition, GameLoop } from "./defineGame.js";
|
|
24
26
|
import type { GameCameraConfig } from "./cameraConfig.js";
|
|
27
|
+
import type { ViewportsConfig } from "./viewports.js";
|
|
25
28
|
import type { LootFilterRule } from "./lootFilter.js";
|
|
26
29
|
import type { RarityStyle } from "./worldItem.js";
|
|
27
|
-
export type { CameraFollowState, CameraKeyframe, CameraProjection, CameraRigKind, CameraShakeConfig, ChaseCameraConfig, ChaseView, CinematicCameraConfig, FirstPersonCameraConfig, GameCameraConfig, InspectionCameraConfig, InspectionZoomAnchor, LockOnCameraConfig, ObserverCameraConfig, PlayerFovConfig, RtsCameraConfig, ShoulderCameraConfig, SideScrollCameraConfig, TopDownCameraConfig, TurntableCameraConfig, } from "./cameraConfig.js";
|
|
30
|
+
export type { CameraFollowState, CameraKeyframe, CameraProjection, CameraRigKind, CameraWeaponView, CameraShakeConfig, ChaseCameraConfig, ChaseView, CinematicCameraConfig, FirstPersonCameraConfig, GameCameraConfig, InspectionCameraConfig, InspectionZoomAnchor, LockOnCameraConfig, ObserverCameraConfig, PlayerFovConfig, RtsCameraConfig, ShoulderCameraConfig, SideScrollCameraConfig, TopDownCameraConfig, TurntableCameraConfig, } from "./cameraConfig.js";
|
|
28
31
|
export { CAMERA_FRUSTUM_DEFAULTS } from "./cameraConfig.js";
|
|
32
|
+
/**
|
|
33
|
+
* Mouse/touch pointer bindings for `defineGame({ pointer })`.
|
|
34
|
+
*
|
|
35
|
+
* @capability pointer-commands click an entity or the ground to run a command (click-to-target, click-to-move, grab loot, marquee select)
|
|
36
|
+
*/
|
|
29
37
|
export interface PointerConfig {
|
|
30
38
|
/**
|
|
31
|
-
* Left-click
|
|
32
|
-
*
|
|
39
|
+
* Left-click runs this command with `{ point, entity, object }`; `entity` is the clicked entity id or
|
|
40
|
+
* null, so the same command covers click-to-move, ground-target and click-to-target
|
|
41
|
+
* (`ctx.scene.entity.setTarget`). Suppresses the default left-click hotbar fire.
|
|
33
42
|
*/
|
|
34
43
|
moveCommand?: string;
|
|
35
44
|
/** Enable left-drag marquee + single-click box-select of entities (RTS unit command). */
|
|
@@ -236,17 +245,14 @@ export interface ModelConfig {
|
|
|
236
245
|
* rig's bind pose.
|
|
237
246
|
*/
|
|
238
247
|
animation?: ModelAnimationConfig | "auto" | "none";
|
|
239
|
-
/**
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
bone: string;
|
|
248
|
-
};
|
|
249
|
-
};
|
|
248
|
+
/**
|
|
249
|
+
* Runtime inverse kinematics for a rigged model, applied after the animation mixer each frame.
|
|
250
|
+
* Feet follow slopes and steps while keeping each clip's swing lift, soles never sink below the
|
|
251
|
+
* ground, the pelvis drops so the lower leg can reach, and the correction fades out while airborne.
|
|
252
|
+
* `"auto"` finds the legs by bone name (`UpLeg`, `UpperLeg`, `Thigh`); bone names resolve on the
|
|
253
|
+
* loaded scene.
|
|
254
|
+
*/
|
|
255
|
+
ik?: "auto" | ModelIkConfig;
|
|
250
256
|
/** Props/weapons parented to named bones on this model's rig; each follows its bone through animation. */
|
|
251
257
|
attachments?: readonly ModelAttachment[];
|
|
252
258
|
/** Static kit-of-parts pieces stacked at fixed local offsets — no bone/rig involved. Use this for a compound entity assembled from several modular meshes (a castle keep from base + mid + roof pieces); use `attachments` for props parented to an animated rig's bones. Tag parts with a `role` to procedurally animate a rig-less character composition. */
|
|
@@ -254,6 +260,25 @@ export interface ModelConfig {
|
|
|
254
260
|
/** Tuning for the procedural part-motion driver when any part carries a `role`; omit for defaults. */
|
|
255
261
|
partMotion?: PartMotionParams;
|
|
256
262
|
}
|
|
263
|
+
/** Bone names and limits for {@link ModelConfig.ik}. */
|
|
264
|
+
export interface ModelIkConfig {
|
|
265
|
+
/** Thigh → shin → foot chains; omit to find them by bone name. `[]` turns foot IK off. */
|
|
266
|
+
feet?: readonly {
|
|
267
|
+
root: string;
|
|
268
|
+
mid: string;
|
|
269
|
+
tip: string;
|
|
270
|
+
}[];
|
|
271
|
+
/** Bone shifted down when a foot has to reach lower ground; defaults to the first thigh's parent. */
|
|
272
|
+
pelvis?: string;
|
|
273
|
+
/** Largest foot or pelvis correction, as a fraction of leg length. Default `0.4`. */
|
|
274
|
+
maxAdjust?: number;
|
|
275
|
+
/** Tilt planted feet to the ground normal, `0..1`. Default `1`. */
|
|
276
|
+
alignToGround?: number;
|
|
277
|
+
/** Turns this bone toward the camera. */
|
|
278
|
+
lookAt?: {
|
|
279
|
+
bone: string;
|
|
280
|
+
};
|
|
281
|
+
}
|
|
257
282
|
export interface ObjectStyle {
|
|
258
283
|
color?: string;
|
|
259
284
|
opacity?: number;
|
|
@@ -328,7 +353,7 @@ export interface BackdropConfig {
|
|
|
328
353
|
sky?: SkyEnvironmentConfig;
|
|
329
354
|
fog?: BackdropFogConfig;
|
|
330
355
|
}
|
|
331
|
-
/** Free-flight families moved into the walk controller so one seam covers ground + air — creative/spectator/noclip/hover are weightless, aircraft
|
|
356
|
+
/** Free-flight families moved into the walk controller so one seam covers ground + air — creative/spectator/noclip/hover are weightless, aircraft, rotorcraft and rockets use `physics/aircraftDynamics`. */
|
|
332
357
|
export interface FlightConfig {
|
|
333
358
|
/** Which free-flight family to use when flight is active. Default "creative". */
|
|
334
359
|
mode?: "creative" | "spectator" | "noclip" | "hover";
|
|
@@ -355,6 +380,35 @@ export interface FlightConfig {
|
|
|
355
380
|
/** Gate flight behind live game state — creative toggle, stamina check, etc. `false` stays walking. Default true when `flight` is present. */
|
|
356
381
|
canFly?: (ctx: GameContext) => boolean;
|
|
357
382
|
}
|
|
383
|
+
/** Feel knobs for the built-in walk controller; see {@link PlayerMovementConfig.feel}. */
|
|
384
|
+
export interface MovementFeelConfig {
|
|
385
|
+
/** Ground velocity response, 1/s (default 26). Lower drifts into and out of motion; higher is snappy. */
|
|
386
|
+
groundAcceleration?: number;
|
|
387
|
+
/** Air velocity response, 1/s (default 12). `0` commits to the jump arc; high values steer freely in the air. */
|
|
388
|
+
airAcceleration?: number;
|
|
389
|
+
/** Stopping response on the ground with no input, 1/s (default 18). Lower slides like ice. */
|
|
390
|
+
groundFriction?: number;
|
|
391
|
+
/** Sprint speed as a multiple of walk speed (default 2.25). */
|
|
392
|
+
runMultiplier?: number;
|
|
393
|
+
/** Crouch speed as a multiple of walk speed (default 0.45). */
|
|
394
|
+
crouchMultiplier?: number;
|
|
395
|
+
/** A jump pressed up to this many ms before landing fires on landing (default 0). */
|
|
396
|
+
jumpBufferMs?: number;
|
|
397
|
+
/** A jump pressed up to this many ms after walking off a ledge still fires (default 0). */
|
|
398
|
+
coyoteMs?: number;
|
|
399
|
+
/** Rising speed kept when jump is released early, 0..1 (default 1: no cut). Lower makes a tap a hop. */
|
|
400
|
+
jumpCutFactor?: number;
|
|
401
|
+
/** Gravity multiplier near the peak, while vertical speed is under `apexSpeed` (default 1). Below 1 hangs. */
|
|
402
|
+
apexGravityScale?: number;
|
|
403
|
+
/** Vertical speed under which `apexGravityScale` applies, m/s (default 1.5). */
|
|
404
|
+
apexSpeed?: number;
|
|
405
|
+
/** Gravity multiplier while falling (default 1). Above 1 drops faster than it rose. */
|
|
406
|
+
fallGravityScale?: number;
|
|
407
|
+
/** After landing, jumps wait and walk speed ramps back from `landingSpeedScale` over this many ms (default 0). */
|
|
408
|
+
landingRecoveryMs?: number;
|
|
409
|
+
/** Walk speed share right after landing when `landingRecoveryMs` is set (default 0.5). */
|
|
410
|
+
landingSpeedScale?: number;
|
|
411
|
+
}
|
|
358
412
|
/** Movement-control levers for the shell-driven local player walk controller. */
|
|
359
413
|
export interface PlayerMovementConfig {
|
|
360
414
|
/** "free" (default) moves camera-relative across the plane; "axis" locks travel to one world axis; "grid" snaps each committed position to cell centers. */
|
|
@@ -379,6 +433,13 @@ export interface PlayerMovementConfig {
|
|
|
379
433
|
canSprint?: (ctx: GameContext) => boolean;
|
|
380
434
|
/** Fraction of walk speed while backpedalling (holding `moveBack`). Overrides the engine default (0.65). */
|
|
381
435
|
backpedalMult?: number;
|
|
436
|
+
/**
|
|
437
|
+
* Walk-controller feel. Acceleration and friction are exponential response rates (1/s): higher reaches
|
|
438
|
+
* the target velocity or stops sooner. Omitted fields keep the engine defaults, so nothing changes until a
|
|
439
|
+
* game sets one. The jump fields apply to the kinematic walk; a `physics.backend` capsule or `collision.voxel`
|
|
440
|
+
* body jumps on the press frame with plain gravity. Tune against `measureMovement` (`@jgengine/core/movement/movementProbe`).
|
|
441
|
+
*/
|
|
442
|
+
feel?: MovementFeelConfig;
|
|
382
443
|
/** Radians/second the rendered body rotates toward its movement heading (shortest arc), so strafing/backpedalling read as a turn rather than an instant flip; also the rate the internally-integrated `turnLeft`/`turnRight` heading turns when the shell doesn't own yaw. Unset = body facing snaps instantly (no change to existing feel). */
|
|
383
444
|
turnSpeed?: number;
|
|
384
445
|
/** On-foot swimming when the terrain declares a `waterLevel` and the player is submerged: caps speed and floats them at the surface. `true` uses defaults; default off. */
|
|
@@ -393,8 +454,8 @@ export interface PlayerMovementConfig {
|
|
|
393
454
|
* Free-flight — creative/spectator/noclip/hover — folded into the same walk controller so a game
|
|
394
455
|
* doesn't hand-roll a second movement loop to get Minecraft-like flight. `true` uses creative defaults;
|
|
395
456
|
* an object tunes speed/vertical/sprint/collide/canFly. Horizontal is always yaw-relative strafe
|
|
396
|
-
* (A = left, D = right — never roll/bank), vertical is Space/Ctrl, and aircraft
|
|
397
|
-
*
|
|
457
|
+
* (A = left, D = right — never roll/bank), vertical is Space/Ctrl, and aircraft, rotorcraft and rockets
|
|
458
|
+
* use `physics/aircraftDynamics`. The shell drives this automatically when present; `canFly` toggles
|
|
398
459
|
* between walking and flying per tick without rebuilding the config.
|
|
399
460
|
*/
|
|
400
461
|
flight?: FlightConfig | boolean;
|
|
@@ -537,6 +598,12 @@ export interface PlayableGame<TUi = unknown, TWorldOverlay = unknown, TRenderEnt
|
|
|
537
598
|
pointer?: PointerConfig;
|
|
538
599
|
/** Touch controls on coarse-pointer devices. Unset derives a scheme from `input` (virtual joystick for movement actions, on-screen buttons for the rest); a config refines it with gestures and curated buttons; `false` opts out. */
|
|
539
600
|
touch?: TouchControlsConfig | false;
|
|
601
|
+
/** Gamepad stick deadzone, response curve and trigger deadzone the shell's pad poll applies before publishing analog values. Unset keeps the shell defaults (axial `0.12`/`0.95`, linear). */
|
|
602
|
+
gamepad?: GamepadFeelConfig;
|
|
603
|
+
/** Local seats on this screen for couch co-op. With `maxSlots > 1` each extra pad hot-joins its own seat on its first button press, the shell calls `loop.onNewPlayer(ctx, { userId, isNew: true })` for it, and its input lands on `localPlayers(ctx).local(slotId).input` instead of `ctx.input`. Unset keeps one seat: every device drives `ctx.player`. */
|
|
604
|
+
localPlayers?: LocalPlayersConfig;
|
|
605
|
+
/** Split-screen for local seats (`localPlayers`): one viewport per joined seat, each following its own entity. The primary seat keeps the main camera rig; post-processing is skipped while more than one viewport is drawn. */
|
|
606
|
+
viewports?: ViewportsConfig;
|
|
540
607
|
/** Phone orientation contract. Legacy `"landscape"`/`"portrait"` stays advisory (a dismissible rotate hint). The object form `{ mobile: "landscape-required" }` is strict — the shell shows an engine-owned rotate screen and blocks gameplay until the device is turned. See `GameOrientation`. */
|
|
541
608
|
orientation?: GameOrientation;
|
|
542
609
|
/** Where the game is meant to be played. Default `["web", "mobile"]` — design-resolution HUD fit is on for every game: `HudCanvas` auto-scales from `hudFit.designSize` down to the live viewport, so the desktop layout shrinks to fit a phone instead of overflowing it. Declare `["web"]` to opt a desktop-only game out (compact displays fall back to the legacy fixed 0.85 zoom). */
|
package/dist/game/toasts.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface Toast<T = string> {
|
|
|
15
15
|
export declare function appendToast<T>(toasts: readonly Toast<T>[], toast: Toast<T>, cap: number): readonly Toast<T>[];
|
|
16
16
|
/** Drop every toast whose `expiresAt` is at or before `now`. Returns the same array when nothing expired. */
|
|
17
17
|
export declare function pruneToasts<T>(toasts: readonly Toast<T>[], now: number): readonly Toast<T>[];
|
|
18
|
+
/** Plain JSON state of a {@link ToastQueue}: live toasts plus the id counter. */
|
|
19
|
+
export interface ToastQueueState<T = string> {
|
|
20
|
+
toasts: Toast<T>[];
|
|
21
|
+
counter: number;
|
|
22
|
+
}
|
|
18
23
|
/** Stateful transient-toast list with a size cap and time-to-live eviction. */
|
|
19
24
|
export interface ToastQueue<T = string> {
|
|
20
25
|
/** Raise a toast at `now` living `ttlSeconds` (falling back to the queue default); returns it. */
|
|
@@ -25,6 +30,9 @@ export interface ToastQueue<T = string> {
|
|
|
25
30
|
list(): readonly Toast<T>[];
|
|
26
31
|
/** Remove all toasts. */
|
|
27
32
|
clear(): void;
|
|
33
|
+
/** Copies the queue; each toast's `body` is shared, since the queue never mutates it. */
|
|
34
|
+
snapshot(): ToastQueueState<T>;
|
|
35
|
+
restore(next: ToastQueueState<T>): void;
|
|
28
36
|
}
|
|
29
37
|
/** Options for {@link createToastQueue}. */
|
|
30
38
|
export interface ToastQueueOptions {
|