@jgengine/core 0.5.0 → 0.7.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 +191 -2
- package/dist/ai/crowd.d.ts +36 -0
- package/dist/ai/crowd.js +213 -0
- package/dist/ai/jobBoard.d.ts +49 -0
- package/dist/ai/jobBoard.js +155 -0
- package/dist/ai/spawnDirector.d.ts +56 -0
- package/dist/ai/spawnDirector.js +150 -0
- package/dist/ai/threat.d.ts +25 -0
- package/dist/ai/threat.js +67 -0
- package/dist/audio/audioFalloff.d.ts +38 -0
- package/dist/audio/audioFalloff.js +32 -0
- package/dist/board/laneBoard.d.ts +63 -0
- package/dist/board/laneBoard.js +114 -0
- package/dist/board/timelineBoard.d.ts +36 -0
- package/dist/board/timelineBoard.js +70 -0
- package/dist/cards/cardPile.d.ts +63 -0
- package/dist/cards/cardPile.js +166 -0
- package/dist/cards/modifierPipeline.d.ts +33 -0
- package/dist/cards/modifierPipeline.js +48 -0
- package/dist/combat/abilityKit.d.ts +42 -0
- package/dist/combat/abilityKit.js +129 -0
- package/dist/combat/animationState.d.ts +47 -0
- package/dist/combat/animationState.js +101 -0
- package/dist/combat/attackTags.d.ts +14 -0
- package/dist/combat/attackTags.js +26 -0
- package/dist/combat/breakMeters.d.ts +32 -0
- package/dist/combat/breakMeters.js +51 -0
- package/dist/combat/comboString.d.ts +35 -0
- package/dist/combat/comboString.js +59 -0
- package/dist/combat/death.d.ts +8 -0
- package/dist/combat/death.js +5 -2
- package/dist/combat/defensiveWindow.d.ts +34 -0
- package/dist/combat/defensiveWindow.js +66 -0
- package/dist/combat/downed.d.ts +35 -0
- package/dist/combat/downed.js +104 -0
- package/dist/combat/effects.d.ts +11 -0
- package/dist/combat/effects.js +22 -13
- package/dist/combat/hitReaction.d.ts +23 -0
- package/dist/combat/hitReaction.js +20 -0
- package/dist/combat/resistance.d.ts +13 -0
- package/dist/combat/resistance.js +46 -0
- package/dist/combat/telegraph.d.ts +27 -0
- package/dist/combat/telegraph.js +37 -0
- package/dist/crafting/crop.d.ts +59 -0
- package/dist/crafting/crop.js +181 -0
- package/dist/crafting/production.d.ts +64 -0
- package/dist/crafting/production.js +130 -0
- package/dist/crafting/recipe.d.ts +62 -0
- package/dist/crafting/recipe.js +101 -0
- package/dist/economy/sharedWallet.d.ts +28 -0
- package/dist/economy/sharedWallet.js +62 -0
- package/dist/economy/techTree.d.ts +40 -0
- package/dist/economy/techTree.js +66 -0
- package/dist/game/cosmetics.d.ts +25 -0
- package/dist/game/cosmetics.js +51 -0
- package/dist/game/defineGame.d.ts +5 -0
- package/dist/game/events.d.ts +62 -0
- package/dist/game/lootFilter.d.ts +33 -0
- package/dist/game/lootFilter.js +48 -0
- package/dist/game/ping.d.ts +67 -0
- package/dist/game/ping.js +86 -0
- package/dist/game/playableGame.d.ts +291 -5
- package/dist/game/race.d.ts +95 -0
- package/dist/game/race.js +216 -0
- package/dist/game/runDraft.d.ts +34 -0
- package/dist/game/runDraft.js +97 -0
- package/dist/game/social.d.ts +27 -0
- package/dist/game/social.js +16 -0
- package/dist/game/worldItem.d.ts +92 -0
- package/dist/game/worldItem.js +132 -0
- package/dist/input/axisInput.d.ts +43 -0
- package/dist/input/axisInput.js +70 -0
- package/dist/input/pointer.d.ts +24 -0
- package/dist/input/pointer.js +17 -0
- package/dist/interaction/contextMenu.d.ts +30 -0
- package/dist/interaction/contextMenu.js +22 -0
- package/dist/interaction/qte.d.ts +20 -0
- package/dist/interaction/qte.js +24 -0
- package/dist/interaction/skillCheck.d.ts +20 -0
- package/dist/interaction/skillCheck.js +26 -0
- package/dist/inventory/shapedGrid.d.ts +56 -0
- package/dist/inventory/shapedGrid.js +176 -0
- package/dist/inventory/storageTier.d.ts +52 -0
- package/dist/inventory/storageTier.js +77 -0
- package/dist/item/affix.d.ts +54 -0
- package/dist/item/affix.js +133 -0
- package/dist/item/durability.d.ts +51 -0
- package/dist/item/durability.js +78 -0
- package/dist/item/modularItem.d.ts +46 -0
- package/dist/item/modularItem.js +75 -0
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +53 -1
- package/dist/movement/dash.d.ts +37 -0
- package/dist/movement/dash.js +65 -0
- package/dist/multiplayer/combatSnapshot.d.ts +45 -0
- package/dist/multiplayer/combatSnapshot.js +89 -0
- package/dist/multiplayer/lagCompensation.d.ts +43 -0
- package/dist/multiplayer/lagCompensation.js +154 -0
- package/dist/multiplayer/matchmaking.d.ts +33 -0
- package/dist/multiplayer/matchmaking.js +60 -0
- package/dist/multiplayer/simultaneousCommit.d.ts +34 -0
- package/dist/multiplayer/simultaneousCommit.js +53 -0
- package/dist/nav/navGrid.d.ts +46 -0
- package/dist/nav/navGrid.js +248 -0
- package/dist/nav/pathFollow.d.ts +29 -0
- package/dist/nav/pathFollow.js +67 -0
- package/dist/physics/buoyancy.d.ts +49 -0
- package/dist/physics/buoyancy.js +112 -0
- package/dist/physics/carryable.d.ts +46 -0
- package/dist/physics/carryable.js +110 -0
- package/dist/physics/damageZones.d.ts +56 -0
- package/dist/physics/damageZones.js +107 -0
- package/dist/physics/forceVolume.d.ts +48 -0
- package/dist/physics/forceVolume.js +119 -0
- package/dist/physics/physicsWorld.d.ts +219 -0
- package/dist/physics/physicsWorld.js +749 -0
- package/dist/physics/ragdoll.d.ts +49 -0
- package/dist/physics/ragdoll.js +103 -0
- package/dist/physics/spatialGrid.d.ts +41 -0
- package/dist/physics/spatialGrid.js +138 -0
- package/dist/physics/structure.d.ts +83 -0
- package/dist/physics/structure.js +0 -0
- package/dist/physics/traversal.d.ts +75 -0
- package/dist/physics/traversal.js +138 -0
- package/dist/physics/vehicleBody.d.ts +93 -0
- package/dist/physics/vehicleBody.js +234 -0
- package/dist/runtime/gameContext.d.ts +66 -2
- package/dist/runtime/gameContext.js +157 -4
- package/dist/runtime/hostPersistence.d.ts +18 -0
- package/dist/runtime/hostPersistence.js +4 -0
- package/dist/runtime/persistenceScope.d.ts +32 -0
- package/dist/runtime/persistenceScope.js +64 -0
- package/dist/scene/assetCatalog.d.ts +13 -0
- package/dist/scene/autoTarget.d.ts +15 -0
- package/dist/scene/autoTarget.js +54 -0
- package/dist/scene/behaviors.d.ts +13 -1
- package/dist/scene/behaviors.js +3 -0
- package/dist/scene/captureCheck.d.ts +7 -0
- package/dist/scene/captureCheck.js +11 -0
- package/dist/scene/entityStore.d.ts +7 -1
- package/dist/scene/entityStore.js +15 -1
- package/dist/scene/form.d.ts +40 -0
- package/dist/scene/form.js +56 -0
- package/dist/scene/mount.d.ts +62 -0
- package/dist/scene/mount.js +107 -0
- package/dist/scene/possession.d.ts +30 -0
- package/dist/scene/possession.js +51 -0
- package/dist/scene/roster.d.ts +25 -0
- package/dist/scene/roster.js +57 -0
- package/dist/scene/selection.d.ts +30 -0
- package/dist/scene/selection.js +38 -0
- package/dist/scene/stationClaim.d.ts +43 -0
- package/dist/scene/stationClaim.js +109 -0
- package/dist/sensor/frustumSensor.d.ts +60 -0
- package/dist/sensor/frustumSensor.js +104 -0
- package/dist/sensor/hiddenStateProbe.d.ts +31 -0
- package/dist/sensor/hiddenStateProbe.js +40 -0
- package/dist/sensor/recordingBuffer.d.ts +29 -0
- package/dist/sensor/recordingBuffer.js +61 -0
- package/dist/sensor/revealQuery.d.ts +22 -0
- package/dist/sensor/revealQuery.js +36 -0
- package/dist/session/contestedChannel.d.ts +36 -0
- package/dist/session/contestedChannel.js +100 -0
- package/dist/session/extraction.d.ts +54 -0
- package/dist/session/extraction.js +108 -0
- package/dist/session/ring.d.ts +36 -0
- package/dist/session/ring.js +88 -0
- package/dist/session/roundState.d.ts +55 -0
- package/dist/session/roundState.js +129 -0
- package/dist/stats/accumulatorMeter.d.ts +31 -0
- package/dist/stats/accumulatorMeter.js +81 -0
- package/dist/stats/eventMeter.d.ts +34 -0
- package/dist/stats/eventMeter.js +76 -0
- package/dist/stats/rollCheck.d.ts +15 -0
- package/dist/stats/rollCheck.js +15 -0
- package/dist/survival/decayMeter.d.ts +59 -0
- package/dist/survival/decayMeter.js +95 -0
- package/dist/survival/moodle.d.ts +48 -0
- package/dist/survival/moodle.js +91 -0
- package/dist/survival/regionHealth.d.ts +72 -0
- package/dist/survival/regionHealth.js +129 -0
- package/dist/tactics/predictiveQuery.d.ts +34 -0
- package/dist/tactics/predictiveQuery.js +44 -0
- package/dist/tactics/snapshot.d.ts +18 -0
- package/dist/tactics/snapshot.js +65 -0
- package/dist/tactics/surface.d.ts +52 -0
- package/dist/tactics/surface.js +135 -0
- package/dist/tactics/tacticalGrid.d.ts +57 -0
- package/dist/tactics/tacticalGrid.js +244 -0
- package/dist/time/beatClock.d.ts +40 -0
- package/dist/time/beatClock.js +73 -0
- package/dist/time/simClock.d.ts +62 -0
- package/dist/time/simClock.js +132 -0
- package/dist/turn/commit.d.ts +27 -0
- package/dist/turn/commit.js +72 -0
- package/dist/turn/turnLoop.d.ts +58 -0
- package/dist/turn/turnLoop.js +216 -0
- package/dist/world/buildPermissions.d.ts +47 -0
- package/dist/world/buildPermissions.js +134 -0
- package/dist/world/buildingIndex.d.ts +16 -0
- package/dist/world/buildingIndex.js +63 -0
- package/dist/world/buildings.d.ts +98 -0
- package/dist/world/buildings.js +320 -0
- package/dist/world/carve.d.ts +105 -0
- package/dist/world/carve.js +175 -0
- package/dist/world/connectors.d.ts +40 -0
- package/dist/world/connectors.js +67 -0
- package/dist/world/envField.d.ts +71 -0
- package/dist/world/envField.js +96 -0
- package/dist/world/features.d.ts +105 -1
- package/dist/world/features.js +95 -0
- package/dist/world/fog.d.ts +40 -0
- package/dist/world/fog.js +137 -0
- package/dist/world/markers.d.ts +52 -0
- package/dist/world/markers.js +96 -0
- package/dist/world/minimap.d.ts +42 -0
- package/dist/world/minimap.js +72 -0
- package/dist/world/placedStructureStore.d.ts +39 -0
- package/dist/world/placedStructureStore.js +95 -0
- package/dist/world/placementController.d.ts +48 -0
- package/dist/world/placementController.js +97 -0
- package/dist/world/realm.d.ts +61 -0
- package/dist/world/realm.js +66 -0
- package/dist/world/regions.d.ts +66 -0
- package/dist/world/regions.js +158 -0
- package/dist/world/scatter.d.ts +24 -0
- package/dist/world/scatter.js +126 -0
- package/dist/world/scatterItems.d.ts +38 -0
- package/dist/world/scatterItems.js +85 -0
- package/dist/world/support.d.ts +27 -0
- package/dist/world/support.js +75 -0
- package/dist/world/terraform.d.ts +54 -0
- package/dist/world/terraform.js +186 -0
- package/dist/world/terrain.d.ts +54 -0
- package/dist/world/terrain.js +181 -0
- package/dist/world/walls.d.ts +61 -0
- package/dist/world/walls.js +221 -0
- package/dist/world/water.d.ts +27 -0
- package/dist/world/water.js +125 -0
- package/dist/world/weather.d.ts +92 -0
- package/dist/world/weather.js +169 -0
- package/dist/world/wind.d.ts +17 -0
- package/dist/world/wind.js +40 -0
- package/package.json +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { PhysicsWorld } from "./physicsWorld.js";
|
|
2
|
+
import type { AxisInput } from "../input/axisInput.js";
|
|
3
|
+
export interface GripCurve {
|
|
4
|
+
points: readonly (readonly [number, number])[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Piecewise-linear tire-grip curve: normalized lateral slip → available grip (0..1). Grip peaks near
|
|
8
|
+
* the breakaway slip then falls off as the tire slides — the shape that separates a planted corner
|
|
9
|
+
* from a drift. Points are read in ascending slip order; ends clamp.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sampleGripCurve(curve: GripCurve, slip: number): number;
|
|
12
|
+
export declare const DEFAULT_GRIP_CURVE: GripCurve;
|
|
13
|
+
export interface WheelSpec {
|
|
14
|
+
offset: readonly [number, number, number];
|
|
15
|
+
radius?: number;
|
|
16
|
+
restLength?: number;
|
|
17
|
+
steered?: boolean;
|
|
18
|
+
powered?: boolean;
|
|
19
|
+
handbrakeLocks?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface WheelState {
|
|
22
|
+
worldX: number;
|
|
23
|
+
worldY: number;
|
|
24
|
+
worldZ: number;
|
|
25
|
+
grounded: boolean;
|
|
26
|
+
compression: number;
|
|
27
|
+
steerAngle: number;
|
|
28
|
+
}
|
|
29
|
+
export interface VehicleBodyConfig {
|
|
30
|
+
position: readonly [number, number, number];
|
|
31
|
+
heading?: number;
|
|
32
|
+
chassisHalfExtents?: readonly [number, number, number];
|
|
33
|
+
mass?: number;
|
|
34
|
+
wheels?: readonly WheelSpec[];
|
|
35
|
+
engineAccel?: number;
|
|
36
|
+
brakeAccel?: number;
|
|
37
|
+
topSpeed?: number;
|
|
38
|
+
reverseSpeed?: number;
|
|
39
|
+
turnRate?: number;
|
|
40
|
+
turnSpeedRef?: number;
|
|
41
|
+
grip?: GripCurve;
|
|
42
|
+
gripStrength?: number;
|
|
43
|
+
handbrakeGrip?: number;
|
|
44
|
+
rollingResistance?: number;
|
|
45
|
+
suspensionRest?: number;
|
|
46
|
+
suspensionStiffness?: number;
|
|
47
|
+
suspensionDamping?: number;
|
|
48
|
+
groundHeight?: (x: number, z: number) => number;
|
|
49
|
+
surfaceFriction?: (x: number, z: number) => number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Arcade vehicle over the {@link PhysicsWorld} rigid-body sim. The chassis is a single box body driven
|
|
53
|
+
* by an {@link AxisInput}: per-wheel suspension is a spring-damper held with G3's `springJoint` against
|
|
54
|
+
* the sampled ground, drive/brake push along the heading, and a tire-grip curve bleeds lateral velocity
|
|
55
|
+
* (cornering, and drift under handbrake). Because the chassis is a physics body it still collides — that
|
|
56
|
+
* contact feeds crash damage. Call `update(dt, input)` before the shared `world.step(dt)`.
|
|
57
|
+
*/
|
|
58
|
+
export declare class VehicleBody {
|
|
59
|
+
heading: number;
|
|
60
|
+
readonly chassis: number;
|
|
61
|
+
private readonly world;
|
|
62
|
+
private readonly wheels;
|
|
63
|
+
private readonly springs;
|
|
64
|
+
private readonly wheelStates;
|
|
65
|
+
private readonly engineAccel;
|
|
66
|
+
private readonly brakeAccel;
|
|
67
|
+
private readonly topSpeed;
|
|
68
|
+
private readonly reverseSpeed;
|
|
69
|
+
private readonly turnRate;
|
|
70
|
+
private readonly turnSpeedRef;
|
|
71
|
+
private readonly grip;
|
|
72
|
+
private readonly gripStrength;
|
|
73
|
+
private readonly handbrakeGrip;
|
|
74
|
+
private readonly rollingResistance;
|
|
75
|
+
private readonly suspensionRest;
|
|
76
|
+
private readonly groundHeight;
|
|
77
|
+
private readonly surfaceFriction;
|
|
78
|
+
private groundedCount;
|
|
79
|
+
constructor(world: PhysicsWorld, config: VehicleBodyConfig);
|
|
80
|
+
private wheelMount;
|
|
81
|
+
get position(): [number, number, number];
|
|
82
|
+
get forward(): [number, number];
|
|
83
|
+
/** Signed forward speed along the heading (negative = reversing). */
|
|
84
|
+
get speed(): number;
|
|
85
|
+
get grounded(): boolean;
|
|
86
|
+
wheelState(index: number): WheelState | null;
|
|
87
|
+
get wheelCount(): number;
|
|
88
|
+
resetTo(position: readonly [number, number, number], heading: number): void;
|
|
89
|
+
update(dt: number, input: AxisInput): void;
|
|
90
|
+
private updateSuspension;
|
|
91
|
+
remove(): void;
|
|
92
|
+
}
|
|
93
|
+
export declare function createVehicleBody(world: PhysicsWorld, config: VehicleBodyConfig): VehicleBody;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Piecewise-linear tire-grip curve: normalized lateral slip → available grip (0..1). Grip peaks near
|
|
3
|
+
* the breakaway slip then falls off as the tire slides — the shape that separates a planted corner
|
|
4
|
+
* from a drift. Points are read in ascending slip order; ends clamp.
|
|
5
|
+
*/
|
|
6
|
+
export function sampleGripCurve(curve, slip) {
|
|
7
|
+
const pts = curve.points;
|
|
8
|
+
if (pts.length === 0)
|
|
9
|
+
return 1;
|
|
10
|
+
const s = Math.abs(slip);
|
|
11
|
+
if (s <= pts[0][0])
|
|
12
|
+
return pts[0][1];
|
|
13
|
+
for (let i = 1; i < pts.length; i += 1) {
|
|
14
|
+
const [x1, y1] = pts[i];
|
|
15
|
+
if (s <= x1) {
|
|
16
|
+
const [x0, y0] = pts[i - 1];
|
|
17
|
+
const span = x1 - x0;
|
|
18
|
+
const t = span <= 0 ? 0 : (s - x0) / span;
|
|
19
|
+
return y0 + (y1 - y0) * t;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return pts[pts.length - 1][1];
|
|
23
|
+
}
|
|
24
|
+
export const DEFAULT_GRIP_CURVE = {
|
|
25
|
+
points: [
|
|
26
|
+
[0, 1],
|
|
27
|
+
[0.2, 1],
|
|
28
|
+
[0.45, 0.78],
|
|
29
|
+
[1, 0.55],
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
function cornerWheels(half, rest, radius) {
|
|
33
|
+
const x = half[0];
|
|
34
|
+
const z = half[2];
|
|
35
|
+
const y = -half[1];
|
|
36
|
+
return [
|
|
37
|
+
{ offset: [-x, y, z], steered: true, restLength: rest, radius },
|
|
38
|
+
{ offset: [x, y, z], steered: true, restLength: rest, radius },
|
|
39
|
+
{ offset: [-x, y, -z], powered: true, handbrakeLocks: true, restLength: rest, radius },
|
|
40
|
+
{ offset: [x, y, -z], powered: true, handbrakeLocks: true, restLength: rest, radius },
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Arcade vehicle over the {@link PhysicsWorld} rigid-body sim. The chassis is a single box body driven
|
|
45
|
+
* by an {@link AxisInput}: per-wheel suspension is a spring-damper held with G3's `springJoint` against
|
|
46
|
+
* the sampled ground, drive/brake push along the heading, and a tire-grip curve bleeds lateral velocity
|
|
47
|
+
* (cornering, and drift under handbrake). Because the chassis is a physics body it still collides — that
|
|
48
|
+
* contact feeds crash damage. Call `update(dt, input)` before the shared `world.step(dt)`.
|
|
49
|
+
*/
|
|
50
|
+
export class VehicleBody {
|
|
51
|
+
heading;
|
|
52
|
+
chassis;
|
|
53
|
+
world;
|
|
54
|
+
wheels;
|
|
55
|
+
springs;
|
|
56
|
+
wheelStates;
|
|
57
|
+
engineAccel;
|
|
58
|
+
brakeAccel;
|
|
59
|
+
topSpeed;
|
|
60
|
+
reverseSpeed;
|
|
61
|
+
turnRate;
|
|
62
|
+
turnSpeedRef;
|
|
63
|
+
grip;
|
|
64
|
+
gripStrength;
|
|
65
|
+
handbrakeGrip;
|
|
66
|
+
rollingResistance;
|
|
67
|
+
suspensionRest;
|
|
68
|
+
groundHeight;
|
|
69
|
+
surfaceFriction;
|
|
70
|
+
groundedCount = 0;
|
|
71
|
+
constructor(world, config) {
|
|
72
|
+
this.world = world;
|
|
73
|
+
this.heading = config.heading ?? 0;
|
|
74
|
+
const half = config.chassisHalfExtents ?? [0.9, 0.35, 1.8];
|
|
75
|
+
this.suspensionRest = config.suspensionRest ?? 0.55;
|
|
76
|
+
const radius = 0.35;
|
|
77
|
+
this.wheels = config.wheels ?? cornerWheels(half, this.suspensionRest, radius);
|
|
78
|
+
this.engineAccel = config.engineAccel ?? 18;
|
|
79
|
+
this.brakeAccel = config.brakeAccel ?? 26;
|
|
80
|
+
this.topSpeed = config.topSpeed ?? 28;
|
|
81
|
+
this.reverseSpeed = config.reverseSpeed ?? 10;
|
|
82
|
+
this.turnRate = config.turnRate ?? 1.6;
|
|
83
|
+
this.turnSpeedRef = config.turnSpeedRef ?? 8;
|
|
84
|
+
this.grip = config.grip ?? DEFAULT_GRIP_CURVE;
|
|
85
|
+
this.gripStrength = config.gripStrength ?? 8;
|
|
86
|
+
this.handbrakeGrip = config.handbrakeGrip ?? 0.35;
|
|
87
|
+
this.rollingResistance = config.rollingResistance ?? 0.6;
|
|
88
|
+
const stiffness = config.suspensionStiffness ?? 60;
|
|
89
|
+
const damping = config.suspensionDamping ?? 8;
|
|
90
|
+
this.groundHeight = config.groundHeight ?? (() => 0);
|
|
91
|
+
this.surfaceFriction = config.surfaceFriction ?? (() => 1);
|
|
92
|
+
this.chassis = world.addBody({
|
|
93
|
+
position: config.position,
|
|
94
|
+
halfExtents: half,
|
|
95
|
+
mass: config.mass ?? 4,
|
|
96
|
+
});
|
|
97
|
+
this.springs = [];
|
|
98
|
+
this.wheelStates = [];
|
|
99
|
+
for (const wheel of this.wheels) {
|
|
100
|
+
const worldPos = this.wheelMount(wheel);
|
|
101
|
+
const rest = wheel.restLength ?? this.suspensionRest;
|
|
102
|
+
const id = world.springJoint({
|
|
103
|
+
bodyA: this.chassis,
|
|
104
|
+
anchorA: [wheel.offset[0], wheel.offset[1], wheel.offset[2]],
|
|
105
|
+
anchorB: [worldPos[0], worldPos[1] - rest, worldPos[2]],
|
|
106
|
+
restLength: rest,
|
|
107
|
+
stiffness,
|
|
108
|
+
damping,
|
|
109
|
+
});
|
|
110
|
+
this.springs.push(id);
|
|
111
|
+
this.wheelStates.push({
|
|
112
|
+
worldX: worldPos[0],
|
|
113
|
+
worldY: worldPos[1],
|
|
114
|
+
worldZ: worldPos[2],
|
|
115
|
+
grounded: false,
|
|
116
|
+
compression: 0,
|
|
117
|
+
steerAngle: 0,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
wheelMount(wheel) {
|
|
122
|
+
const sin = Math.sin(this.heading);
|
|
123
|
+
const cos = Math.cos(this.heading);
|
|
124
|
+
const [ox, oy, oz] = wheel.offset;
|
|
125
|
+
return [
|
|
126
|
+
this.world.posX[this.chassis] + ox * cos + oz * sin,
|
|
127
|
+
this.world.posY[this.chassis] + oy,
|
|
128
|
+
this.world.posZ[this.chassis] - ox * sin + oz * cos,
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
get position() {
|
|
132
|
+
return [this.world.posX[this.chassis], this.world.posY[this.chassis], this.world.posZ[this.chassis]];
|
|
133
|
+
}
|
|
134
|
+
get forward() {
|
|
135
|
+
return [Math.sin(this.heading), Math.cos(this.heading)];
|
|
136
|
+
}
|
|
137
|
+
/** Signed forward speed along the heading (negative = reversing). */
|
|
138
|
+
get speed() {
|
|
139
|
+
const [fx, fz] = this.forward;
|
|
140
|
+
return this.world.velX[this.chassis] * fx + this.world.velZ[this.chassis] * fz;
|
|
141
|
+
}
|
|
142
|
+
get grounded() {
|
|
143
|
+
return this.groundedCount > 0;
|
|
144
|
+
}
|
|
145
|
+
wheelState(index) {
|
|
146
|
+
return this.wheelStates[index] ?? null;
|
|
147
|
+
}
|
|
148
|
+
get wheelCount() {
|
|
149
|
+
return this.wheels.length;
|
|
150
|
+
}
|
|
151
|
+
resetTo(position, heading) {
|
|
152
|
+
const w = this.world;
|
|
153
|
+
const c = this.chassis;
|
|
154
|
+
w.posX[c] = position[0];
|
|
155
|
+
w.posY[c] = position[1];
|
|
156
|
+
w.posZ[c] = position[2];
|
|
157
|
+
w.velX[c] = 0;
|
|
158
|
+
w.velY[c] = 0;
|
|
159
|
+
w.velZ[c] = 0;
|
|
160
|
+
this.heading = heading;
|
|
161
|
+
w.wake(c);
|
|
162
|
+
}
|
|
163
|
+
update(dt, input) {
|
|
164
|
+
const w = this.world;
|
|
165
|
+
const c = this.chassis;
|
|
166
|
+
this.updateSuspension(input.steer);
|
|
167
|
+
const speed = this.speed;
|
|
168
|
+
const grounded = this.groundedCount > 0;
|
|
169
|
+
if (grounded) {
|
|
170
|
+
const steerScale = Math.min(1, Math.abs(speed) / this.turnSpeedRef);
|
|
171
|
+
const dir = speed >= 0 ? 1 : -1;
|
|
172
|
+
this.heading += input.steer * this.turnRate * steerScale * dir * dt;
|
|
173
|
+
}
|
|
174
|
+
const [fx, fz] = this.forward;
|
|
175
|
+
if (grounded) {
|
|
176
|
+
let accel = 0;
|
|
177
|
+
if (input.throttle > 0 && speed < this.topSpeed)
|
|
178
|
+
accel += input.throttle * this.engineAccel;
|
|
179
|
+
if (input.brake > 0) {
|
|
180
|
+
if (speed > 0.2)
|
|
181
|
+
accel -= input.brake * this.brakeAccel;
|
|
182
|
+
else if (speed > -this.reverseSpeed)
|
|
183
|
+
accel -= input.brake * this.engineAccel;
|
|
184
|
+
}
|
|
185
|
+
w.velX[c] += fx * accel * dt;
|
|
186
|
+
w.velZ[c] += fz * accel * dt;
|
|
187
|
+
const roll = Math.max(0, 1 - this.rollingResistance * dt * (input.throttle > 0 ? 0 : 1));
|
|
188
|
+
const forwardSpeed = w.velX[c] * fx + w.velZ[c] * fz;
|
|
189
|
+
const rolled = forwardSpeed * roll;
|
|
190
|
+
const lateralSpeed = -w.velX[c] * fz + w.velZ[c] * fx;
|
|
191
|
+
const surface = this.surfaceFriction(w.posX[c], w.posZ[c]);
|
|
192
|
+
const slip = Math.abs(lateralSpeed) / (Math.abs(forwardSpeed) + 1);
|
|
193
|
+
const handbrake = 1 - input.handbrake * (1 - this.handbrakeGrip);
|
|
194
|
+
const grip = sampleGripCurve(this.grip, slip) * surface * handbrake;
|
|
195
|
+
const keep = Math.max(0, 1 - grip * this.gripStrength * dt);
|
|
196
|
+
const newLateral = lateralSpeed * keep;
|
|
197
|
+
w.velX[c] = fx * rolled - fz * newLateral;
|
|
198
|
+
w.velZ[c] = fz * rolled + fx * newLateral;
|
|
199
|
+
w.wake(c);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
updateSuspension(steer) {
|
|
203
|
+
this.groundedCount = 0;
|
|
204
|
+
const w = this.world;
|
|
205
|
+
for (let i = 0; i < this.wheels.length; i += 1) {
|
|
206
|
+
const wheel = this.wheels[i];
|
|
207
|
+
const state = this.wheelStates[i];
|
|
208
|
+
const mount = this.wheelMount(wheel);
|
|
209
|
+
const radius = wheel.radius ?? 0.35;
|
|
210
|
+
const rest = wheel.restLength ?? this.suspensionRest;
|
|
211
|
+
const groundY = this.groundHeight(mount[0], mount[2]);
|
|
212
|
+
const gap = mount[1] - (groundY + radius);
|
|
213
|
+
const maxTravel = rest + radius;
|
|
214
|
+
const grounded = gap <= maxTravel;
|
|
215
|
+
w.setJointAnchor(this.springs[i], mount[0], groundY + radius, mount[2]);
|
|
216
|
+
state.worldX = mount[0];
|
|
217
|
+
state.worldY = groundY + radius;
|
|
218
|
+
state.worldZ = mount[2];
|
|
219
|
+
state.grounded = grounded;
|
|
220
|
+
state.compression = Math.max(0, rest - gap);
|
|
221
|
+
state.steerAngle = wheel.steered === true ? steer : 0;
|
|
222
|
+
if (grounded)
|
|
223
|
+
this.groundedCount += 1;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
remove() {
|
|
227
|
+
for (const id of this.springs)
|
|
228
|
+
this.world.removeJoint(id);
|
|
229
|
+
this.springs.length = 0;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
export function createVehicleBody(world, config) {
|
|
233
|
+
return new VehicleBody(world, config);
|
|
234
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { type OnDeathSpec } from "../combat/death.js";
|
|
2
|
-
import { type EffectInput, type EffectResult, type ReceiveMap, type SingleTargetEffectInput } from "../combat/effects.js";
|
|
2
|
+
import { type EffectInput, type EffectResult, type EffectVia, type ReceiveMap, type SingleTargetEffectInput } from "../combat/effects.js";
|
|
3
3
|
import { type ProjectileSystem } from "../combat/projectiles.js";
|
|
4
|
+
import { type HitReaction, type HitReactionConfig } from "../combat/hitReaction.js";
|
|
5
|
+
import { type TelegraphShape } from "../combat/telegraph.js";
|
|
4
6
|
import { type CommandDefinition, type CommandResult } from "../commands/commandRegistry.js";
|
|
7
|
+
import { type Cosmetics } from "../game/cosmetics.js";
|
|
5
8
|
import type { GameDefinition } from "../game/defineGame.js";
|
|
6
9
|
import { type GameEventMap, type GameEvents } from "../game/events.js";
|
|
7
10
|
import { type GameFeed } from "../game/feed.js";
|
|
@@ -9,25 +12,35 @@ import { type Leaderboard } from "../game/leaderboard.js";
|
|
|
9
12
|
import { type Loadouts } from "../game/loadout.js";
|
|
10
13
|
import { type Drop, type LootTableDef } from "../game/lootTable.js";
|
|
11
14
|
import { type QuestJournal } from "../game/quest.js";
|
|
15
|
+
import { type WorldItemRecord, type WorldItemSpawnInput } from "../game/worldItem.js";
|
|
12
16
|
import { type Social } from "../game/social.js";
|
|
13
17
|
import { type TradeField, type TradeSystem } from "../game/trade.js";
|
|
14
18
|
import { type Unlocks } from "../game/unlocks.js";
|
|
15
19
|
import { type InventoryLayout, type InventorySet } from "../inventory/inventoryModel.js";
|
|
20
|
+
import type { ContextVerb } from "../interaction/contextMenu.js";
|
|
16
21
|
import type { ProximityPrompt } from "../interaction/proximityPrompt.js";
|
|
17
22
|
import { type ItemUseHandler, type ItemUseInput, type ItemUseRejection, type ItemUseResult } from "../item/use.js";
|
|
18
23
|
import { type WeaponStats } from "../item/weapon.js";
|
|
19
24
|
import { type PoseAllowedStates, type PoseState } from "../movement/poseState.js";
|
|
20
25
|
import type { ModelAssetRef } from "../scene/assetCatalog.js";
|
|
21
26
|
import { type EntityStatsApi, type StatCatalog } from "../scene/entityStats.js";
|
|
22
|
-
import type { EntityPose, SceneEntity, SpawnOptions } from "../scene/entityStore.js";
|
|
27
|
+
import type { EntityPose, EntityPosition, SceneEntity, SpawnOptions } from "../scene/entityStore.js";
|
|
28
|
+
import { type Forms } from "../scene/form.js";
|
|
23
29
|
import { type ObjectStore } from "../scene/objectStore.js";
|
|
30
|
+
import { type Roster } from "../scene/roster.js";
|
|
31
|
+
import { type Possession } from "../scene/possession.js";
|
|
24
32
|
import { type SpatialApi } from "../scene/spatial.js";
|
|
25
33
|
import { type CycleTargetOptions } from "../scene/targeting.js";
|
|
26
34
|
import { type Stats } from "../stats/statModifiers.js";
|
|
35
|
+
import { type SimClock } from "../time/simClock.js";
|
|
27
36
|
export interface GameContextItemEntry {
|
|
28
37
|
use?: string;
|
|
29
38
|
weapon?: Record<string, unknown>;
|
|
30
39
|
trade?: TradeField;
|
|
40
|
+
/** Rarity id read by the `worldItem` loot-filter/render binding when this item drops to the ground (#32/#33). */
|
|
41
|
+
rarity?: string;
|
|
42
|
+
/** Base item type read by the loot-filter rule evaluator (#33); defaults to the item id when absent. */
|
|
43
|
+
baseType?: string;
|
|
31
44
|
}
|
|
32
45
|
export type CatalogEntityRole = "player" | "enemy" | "hostile" | "npc" | "vehicle";
|
|
33
46
|
export interface GameContextEntityEntry {
|
|
@@ -38,6 +51,8 @@ export interface GameContextEntityEntry {
|
|
|
38
51
|
walkSpeed?: number;
|
|
39
52
|
};
|
|
40
53
|
role?: CatalogEntityRole;
|
|
54
|
+
/** Right-click context-menu verbs for this entity (#31). */
|
|
55
|
+
verbs?: readonly ContextVerb[];
|
|
41
56
|
}
|
|
42
57
|
export interface GameContextObjectEntry {
|
|
43
58
|
proximityPrompt?: ProximityPrompt;
|
|
@@ -45,6 +60,8 @@ export interface GameContextObjectEntry {
|
|
|
45
60
|
baseBreakTime: number;
|
|
46
61
|
};
|
|
47
62
|
slotInventory?: InventoryLayout;
|
|
63
|
+
/** Right-click context-menu verbs for this object (#31). */
|
|
64
|
+
verbs?: readonly ContextVerb[];
|
|
48
65
|
}
|
|
49
66
|
export interface GameContextContent {
|
|
50
67
|
itemById?(itemId: string): GameContextItemEntry | null | undefined;
|
|
@@ -69,6 +86,31 @@ export interface FloatTextInput {
|
|
|
69
86
|
text?: string;
|
|
70
87
|
kind?: string;
|
|
71
88
|
amount?: number;
|
|
89
|
+
hitType?: string;
|
|
90
|
+
element?: string;
|
|
91
|
+
crit?: boolean;
|
|
92
|
+
scale?: number;
|
|
93
|
+
}
|
|
94
|
+
export interface TelegraphInput {
|
|
95
|
+
from: string;
|
|
96
|
+
shape: TelegraphShape;
|
|
97
|
+
at: [number, number, number];
|
|
98
|
+
dir?: number;
|
|
99
|
+
windupMs: number;
|
|
100
|
+
kind?: string;
|
|
101
|
+
effect?: {
|
|
102
|
+
effect: string;
|
|
103
|
+
via?: EffectVia;
|
|
104
|
+
radius?: number;
|
|
105
|
+
falloff?: "linear" | "none";
|
|
106
|
+
los?: boolean;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export interface HitReactionInput {
|
|
110
|
+
from: string;
|
|
111
|
+
to: string;
|
|
112
|
+
config: HitReactionConfig;
|
|
113
|
+
power?: number;
|
|
72
114
|
}
|
|
73
115
|
export interface SceneEntityContext {
|
|
74
116
|
spawn(name: string, options?: SpawnOptions): string;
|
|
@@ -78,6 +120,8 @@ export interface SceneEntityContext {
|
|
|
78
120
|
list(): readonly SceneEntity[];
|
|
79
121
|
stats: EntityStatsApi;
|
|
80
122
|
floatText(input: FloatTextInput): void;
|
|
123
|
+
telegraph(input: TelegraphInput): () => void;
|
|
124
|
+
hitReaction(input: HitReactionInput): HitReaction | null;
|
|
81
125
|
setTarget(fromId: string, toId: string | null): void;
|
|
82
126
|
getTarget(fromId: string): string | null;
|
|
83
127
|
cycleTarget(fromId: string, options?: CycleTargetOptions): string | null;
|
|
@@ -92,6 +136,21 @@ export interface SceneEntityContext {
|
|
|
92
136
|
hasLineOfSight: SpatialApi["hasLineOfSight"];
|
|
93
137
|
queryArc: SpatialApi["queryArc"];
|
|
94
138
|
moveToward: SpatialApi["moveToward"];
|
|
139
|
+
form: Forms;
|
|
140
|
+
}
|
|
141
|
+
export type WorldItemPickupResult = {
|
|
142
|
+
status: "ok";
|
|
143
|
+
record: WorldItemRecord;
|
|
144
|
+
} | {
|
|
145
|
+
status: "rejected";
|
|
146
|
+
reason: string;
|
|
147
|
+
};
|
|
148
|
+
export interface SceneWorldItemContext {
|
|
149
|
+
spawn(input: WorldItemSpawnInput): WorldItemRecord;
|
|
150
|
+
get(instanceId: string): WorldItemRecord | null;
|
|
151
|
+
list(): readonly WorldItemRecord[];
|
|
152
|
+
nearestInRadius(from: EntityPosition, radius: number, filter?: (record: WorldItemRecord) => boolean): string | null;
|
|
153
|
+
pickup(instanceId: string, userId: string): WorldItemPickupResult;
|
|
95
154
|
}
|
|
96
155
|
export interface GameContextCommands {
|
|
97
156
|
define<TInput>(name: string, definition: CommandDefinition<GameContext, TInput>): void;
|
|
@@ -125,6 +184,7 @@ export interface GameContext {
|
|
|
125
184
|
scene: {
|
|
126
185
|
object: SceneObjectContext;
|
|
127
186
|
entity: SceneEntityContext;
|
|
187
|
+
worldItem: SceneWorldItemContext;
|
|
128
188
|
};
|
|
129
189
|
game: {
|
|
130
190
|
commands: GameContextCommands;
|
|
@@ -137,6 +197,7 @@ export interface GameContext {
|
|
|
137
197
|
unlocks: Unlocks;
|
|
138
198
|
economy: GameContextEconomy;
|
|
139
199
|
leaderboard: Leaderboard;
|
|
200
|
+
roster: Roster;
|
|
140
201
|
};
|
|
141
202
|
player: {
|
|
142
203
|
userId: string;
|
|
@@ -148,11 +209,14 @@ export interface GameContext {
|
|
|
148
209
|
reason: string;
|
|
149
210
|
} | null;
|
|
150
211
|
movement: PoseState;
|
|
212
|
+
possession: Possession;
|
|
213
|
+
cosmetics: Cosmetics;
|
|
151
214
|
};
|
|
152
215
|
item: {
|
|
153
216
|
use: GameContextItemUse;
|
|
154
217
|
weapon: WeaponStats;
|
|
155
218
|
};
|
|
219
|
+
time: SimClock;
|
|
156
220
|
subscribe(listener: () => void): () => void;
|
|
157
221
|
version(): number;
|
|
158
222
|
}
|