@jgengine/core 0.11.0 → 0.12.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 +26 -0
- package/dist/ai/populationDirector.d.ts +103 -0
- package/dist/ai/populationDirector.js +190 -0
- package/dist/ai/pursuit.d.ts +56 -0
- package/dist/ai/pursuit.js +53 -0
- package/dist/authoring.d.ts +1 -1
- package/dist/authoring.js +1 -1
- package/dist/combat.d.ts +1 -0
- package/dist/combat.js +1 -0
- package/dist/devtools/controls.d.ts +38 -0
- package/dist/devtools/controls.js +181 -0
- package/dist/devtools/devtools.d.ts +5 -197
- package/dist/devtools/devtools.js +38 -774
- package/dist/devtools/discover.d.ts +32 -0
- package/dist/devtools/discover.js +240 -0
- package/dist/devtools/fallbackSeams.d.ts +50 -0
- package/dist/devtools/fallbackSeams.js +111 -0
- package/dist/devtools/frame.d.ts +15 -0
- package/dist/devtools/frame.js +247 -0
- package/dist/devtools/logs.d.ts +15 -0
- package/dist/devtools/logs.js +86 -0
- package/dist/devtools/overrides.d.ts +16 -0
- package/dist/devtools/overrides.js +66 -0
- package/dist/devtools/types.d.ts +212 -0
- package/dist/devtools/types.js +1 -0
- package/dist/economy/auctionBook.d.ts +122 -0
- package/dist/economy/auctionBook.js +213 -0
- package/dist/economy/priceHistory.d.ts +44 -0
- package/dist/economy/priceHistory.js +89 -0
- package/dist/economy/unlockPoints.d.ts +92 -0
- package/dist/economy/unlockPoints.js +103 -0
- package/dist/editor/commandApply.d.ts +5 -0
- package/dist/editor/commandApply.js +755 -0
- package/dist/editor/commands.d.ts +48 -3
- package/dist/editor/commands.js +40 -754
- package/dist/editor/directives.d.ts +94 -0
- package/dist/editor/directives.js +206 -0
- package/dist/editor/document.d.ts +37 -2
- package/dist/editor/document.js +422 -6
- package/dist/editor/entityCatalog.d.ts +42 -0
- package/dist/editor/entityCatalog.js +101 -0
- package/dist/editor/environment.d.ts +74 -0
- package/dist/editor/environment.js +169 -0
- package/dist/editor/index.d.ts +26 -1
- package/dist/editor/index.js +8 -1
- package/dist/editor/minimap.d.ts +31 -0
- package/dist/editor/minimap.js +73 -0
- package/dist/editor/streamer.d.ts +66 -0
- package/dist/editor/streamer.js +68 -0
- package/dist/editor/types.d.ts +143 -0
- package/dist/editor/world.d.ts +136 -0
- package/dist/editor/world.js +303 -0
- package/dist/game/breeding.d.ts +161 -0
- package/dist/game/breeding.js +181 -0
- package/dist/game/cameraConfig.d.ts +451 -0
- package/dist/game/cameraConfig.js +1 -0
- package/dist/game/defineGame.d.ts +16 -9
- package/dist/game/defineGame.js +14 -6
- package/dist/game/defineSystem.d.ts +2 -0
- package/dist/game/defineSystem.js +2 -0
- package/dist/game/events.d.ts +28 -0
- package/dist/game/playableGame.d.ts +34 -435
- package/dist/game/playableGame.js +1 -1
- package/dist/game/progression.d.ts +35 -0
- package/dist/game/progression.js +19 -0
- package/dist/game/quest.js +1 -3
- package/dist/game/tribe.d.ts +184 -0
- package/dist/game/tribe.js +432 -0
- package/dist/gameplay.d.ts +3 -1
- package/dist/gameplay.js +3 -1
- package/dist/inventory/encumbrance.d.ts +69 -0
- package/dist/inventory/encumbrance.js +68 -0
- package/dist/item/durability.js +1 -3
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +24 -1
- package/dist/movement/movementModel.d.ts +15 -1
- package/dist/movement/movementModel.js +33 -15
- package/dist/movement/playerMovement.js +102 -2
- package/dist/multiplayer/combatSnapshot.js +13 -8
- package/dist/multiplayer/presenceModel.d.ts +2 -0
- package/dist/multiplayer/presenceModel.js +8 -0
- package/dist/physics/drivableVehicle.d.ts +2 -0
- package/dist/physics/drivableVehicle.js +1 -1
- package/dist/physics/flightDynamics.d.ts +82 -0
- package/dist/physics/flightDynamics.js +165 -0
- package/dist/physics/gravityField.d.ts +25 -0
- package/dist/physics/gravityField.js +41 -0
- package/dist/physics/kinematicVehicle.d.ts +87 -0
- package/dist/physics/kinematicVehicle.js +193 -11
- package/dist/physics/physicsWorld.d.ts +26 -0
- package/dist/physics/physicsWorld.js +109 -117
- package/dist/physics/vehicleObstacles.d.ts +44 -0
- package/dist/physics/vehicleObstacles.js +115 -0
- package/dist/random/rng.d.ts +4 -1
- package/dist/random/rng.js +4 -1
- package/dist/runtime/adapter.d.ts +13 -0
- package/dist/runtime/adapter.js +13 -0
- package/dist/runtime/context/combat.d.ts +51 -0
- package/dist/runtime/context/combat.js +130 -0
- package/dist/runtime/context/deathLoot.d.ts +30 -0
- package/dist/runtime/context/deathLoot.js +39 -0
- package/dist/runtime/context/player.d.ts +48 -0
- package/dist/runtime/context/player.js +125 -0
- package/dist/runtime/context/scene.d.ts +59 -0
- package/dist/runtime/context/scene.js +291 -0
- package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
- package/dist/runtime/context/snapshotCodecs.js +26 -0
- package/dist/runtime/descriptors/baseline.js +26 -3
- package/dist/runtime/descriptors/features.d.ts +5 -0
- package/dist/runtime/descriptors/features.js +20 -2
- package/dist/runtime/gameContext.d.ts +2 -428
- package/dist/runtime/gameContext.js +88 -501
- package/dist/runtime/gameContextTypes.d.ts +467 -0
- package/dist/runtime/gameContextTypes.js +1 -0
- package/dist/runtime/headlessRunner.d.ts +4 -2
- package/dist/runtime/headlessRunner.js +1 -0
- package/dist/runtime/hostPolicy.d.ts +51 -0
- package/dist/runtime/hostPolicy.js +68 -0
- package/dist/runtime/hostedGameRunner.d.ts +3 -1
- package/dist/runtime/hostedGameRunner.js +1 -0
- package/dist/runtime/hostedWorldSession.d.ts +3 -1
- package/dist/runtime/hostedWorldSession.js +1 -0
- package/dist/runtime/worldSnapshot.d.ts +13 -1
- package/dist/runtime/worldSnapshot.js +15 -3
- package/dist/scene/assetCatalog.d.ts +5 -0
- package/dist/scene/assetSpace.js +1 -3
- package/dist/scene/authoredTriggers.d.ts +50 -0
- package/dist/scene/authoredTriggers.js +114 -0
- package/dist/scene/builtinSceneKinds.js +8 -3
- package/dist/scene/colliders.d.ts +58 -0
- package/dist/scene/colliders.js +108 -0
- package/dist/scene/collisionMesh.d.ts +94 -0
- package/dist/scene/collisionMesh.js +699 -0
- package/dist/scene/companion.d.ts +147 -0
- package/dist/scene/companion.js +193 -0
- package/dist/scene/entityStats.d.ts +7 -9
- package/dist/scene/entityStats.js +10 -14
- package/dist/scene/sceneKinds.d.ts +16 -0
- package/dist/scene/sceneKinds.js +4 -0
- package/dist/scene/sceneRaycast.js +15 -0
- package/dist/stats/spawnLevelStats.d.ts +110 -0
- package/dist/stats/spawnLevelStats.js +140 -0
- package/dist/stats/statPool.d.ts +70 -0
- package/dist/stats/statPool.js +59 -0
- package/dist/store/changeSignal.d.ts +3 -0
- package/dist/store/changeSignal.js +2 -0
- package/dist/store/observableKeyedStore.d.ts +2 -0
- package/dist/store/observableKeyedStore.js +1 -0
- package/dist/survival/decayMeter.js +1 -3
- package/dist/survival/regionHealth.js +1 -3
- package/dist/ui/hudLayout.d.ts +5 -0
- package/dist/ui/hudLayout.js +17 -1
- package/dist/world/authoredEntities.d.ts +29 -0
- package/dist/world/authoredEntities.js +29 -0
- package/dist/world/authoredObjects.d.ts +15 -1
- package/dist/world/authoredObjects.js +13 -1
- package/dist/world/authoredSpawn.d.ts +39 -0
- package/dist/world/authoredSpawn.js +37 -0
- package/dist/world/buildSockets.d.ts +152 -0
- package/dist/world/buildSockets.js +188 -0
- package/dist/world/cityBlocks.d.ts +152 -0
- package/dist/world/cityBlocks.js +670 -0
- package/dist/world/cityContent.d.ts +77 -0
- package/dist/world/cityContent.js +261 -0
- package/dist/world/cityGeometry.d.ts +114 -0
- package/dist/world/cityGeometry.js +573 -0
- package/dist/world/cityKind.d.ts +314 -0
- package/dist/world/cityKind.js +1578 -0
- package/dist/world/features.d.ts +18 -3
- package/dist/world/features.js +10 -1
- package/dist/world/grassKind.js +2 -2
- package/dist/world/markers.d.ts +50 -4
- package/dist/world/markers.js +49 -0
- package/dist/world/massing.js +1 -1
- package/dist/world/minimapBake.d.ts +78 -0
- package/dist/world/minimapBake.js +208 -0
- package/dist/world/minimapTrack.d.ts +17 -0
- package/dist/world/minimapTrack.js +22 -0
- package/dist/world/pathNetwork.d.ts +159 -0
- package/dist/world/pathNetwork.js +684 -0
- package/dist/world/place.d.ts +166 -0
- package/dist/world/place.js +96 -0
- package/dist/world/placeAsset.js +3 -1
- package/dist/world/poleLineKind.d.ts +40 -0
- package/dist/world/poleLineKind.js +97 -0
- package/dist/world/polyline.js +9 -2
- package/dist/world/resourceNode.d.ts +109 -0
- package/dist/world/resourceNode.js +140 -0
- package/dist/world/scatter.js +2 -20
- package/dist/world/streets.d.ts +29 -0
- package/dist/world/streets.js +55 -0
- package/dist/world/terraform.js +58 -88
- package/dist/world/terrain.d.ts +45 -0
- package/dist/world/terrain.js +78 -0
- package/dist/world.d.ts +14 -5
- package/dist/world.js +12 -3
- package/package.json +1 -1
- package/skills/game-design/SKILL.md +28 -0
- package/skills/game-design/agents/openai.yaml +4 -0
- package/skills/game-design/references/application-playbook.md +170 -0
- package/skills/game-design/references/field-guide.md +288 -0
- package/skills/jgengine/SKILL.md +7 -4
- package/skills/jgengine/api.md +172 -79
- package/skills/jgengine/capabilities.md +104 -2
- package/skills/jgengine/recipes/minimal-game.md +88 -0
- package/skills/jgengine-combat/SKILL.md +5 -1
- package/skills/jgengine-combat/api.md +36 -0
- package/skills/jgengine-combat/capabilities.md +44 -0
- package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
- package/skills/jgengine-combat/reference.md +12 -2
- package/skills/jgengine-gameplay/api.md +152 -26
- package/skills/jgengine-gameplay/capabilities.md +78 -2
- package/skills/jgengine-gameplay/reference-systems.md +1 -5
- package/skills/jgengine-world/SKILL.md +13 -0
- package/skills/jgengine-world/api.md +419 -15
- package/skills/jgengine-world/capabilities.md +108 -2
- package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
- package/skills/jgengine-world/reference.md +43 -4
- package/skills/jgengine/reference-cartridge.md +0 -72
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** One carried stack: an item id and how many of it the carrier holds. */
|
|
2
|
+
export interface LoadEntry {
|
|
3
|
+
itemId: string;
|
|
4
|
+
quantity: number;
|
|
5
|
+
}
|
|
6
|
+
/** Resolves the per-unit mass of an item id. Injected so mass tables stay decoupled from this module. */
|
|
7
|
+
export type MassResolver = (itemId: string) => number;
|
|
8
|
+
/**
|
|
9
|
+
* Tuning for where the encumbrance bands sit and how hard the carrier is slowed.
|
|
10
|
+
* Both fields are load *fractions* / multipliers in `0..1`, never absolute mass.
|
|
11
|
+
*/
|
|
12
|
+
export interface EncumbranceConfig {
|
|
13
|
+
/**
|
|
14
|
+
* Load fraction at/above which the carrier becomes encumbered (loses sprint & jump and
|
|
15
|
+
* starts slowing). Below it the carrier is unencumbered. Default `0.85`.
|
|
16
|
+
*/
|
|
17
|
+
soft?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Move-speed multiplier reached as load approaches full capacity (fraction → 1), the floor
|
|
20
|
+
* the progressive slow curve bottoms out at just before immobility. Default `0.5`.
|
|
21
|
+
*/
|
|
22
|
+
floor?: number;
|
|
23
|
+
}
|
|
24
|
+
/** Coarse carry state: free-moving, slowed, or pinned in place. */
|
|
25
|
+
export type EncumbranceTier = "unencumbered" | "encumbered" | "immobile";
|
|
26
|
+
/** Serializable snapshot of a carrier's load versus capacity and the movement consequences. */
|
|
27
|
+
export interface EncumbranceState {
|
|
28
|
+
/** Total carried mass (never negative). */
|
|
29
|
+
mass: number;
|
|
30
|
+
/** Carrying capacity this load was measured against. */
|
|
31
|
+
capacity: number;
|
|
32
|
+
/** `mass / capacity`, clamped to be finite and non-negative; may exceed 1 when overloaded. */
|
|
33
|
+
fraction: number;
|
|
34
|
+
tier: EncumbranceTier;
|
|
35
|
+
/** Movement speed scalar in `0..1`: 1 when unencumbered, decays toward `floor`, 0 when immobile. */
|
|
36
|
+
moveMultiplier: number;
|
|
37
|
+
canSprint: boolean;
|
|
38
|
+
canJump: boolean;
|
|
39
|
+
immobile: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Sum the mass of every carried stack using an injected per-unit mass resolver. Bounded by the
|
|
43
|
+
* number of entries; allocates nothing. Missing or negative unit masses are treated as 0.
|
|
44
|
+
*
|
|
45
|
+
* @param entries carried stacks (`{ itemId, quantity }`, compatible with any inventory listing).
|
|
46
|
+
* @param massOf per-unit mass lookup for an item id.
|
|
47
|
+
* @capability carry-load sum carried mass from item stacks via an injected mass resolver
|
|
48
|
+
*/
|
|
49
|
+
export declare function totalLoad(entries: readonly LoadEntry[], massOf: MassResolver): number;
|
|
50
|
+
/**
|
|
51
|
+
* Progressive move-speed multiplier for a raw load fraction: 1 while at/below `soft`, a linear
|
|
52
|
+
* decay from 1 down toward `floor` across the `soft..1` band, and 0 once at/over capacity.
|
|
53
|
+
* Pure and side-effect free — the curve `resolveEncumbrance` reads for `moveMultiplier`.
|
|
54
|
+
*
|
|
55
|
+
* @capability encumbrance-move-curve progressive move-speed multiplier from a carry-load fraction
|
|
56
|
+
*/
|
|
57
|
+
export declare function encumbranceMoveMultiplier(fraction: number, config?: EncumbranceConfig): number;
|
|
58
|
+
/**
|
|
59
|
+
* Resolve carried mass against carrying capacity into a serializable encumbrance state: load
|
|
60
|
+
* fraction, tier, and the movement consequences (`canSprint`/`canJump`/`immobile` gates plus a
|
|
61
|
+
* progressive `moveMultiplier`). Below `soft` the carrier is unencumbered and unhindered; from
|
|
62
|
+
* `soft` up to capacity it is encumbered — no sprint or jump, and speed decays from 1 toward
|
|
63
|
+
* `floor`; at or above capacity it is immobile (multiplier 0). Deterministic and allocation-light;
|
|
64
|
+
* pair with {@link totalLoad} to derive `mass` from an inventory and a mass table. A capacity of 0
|
|
65
|
+
* or less pins any positive load as immobile (`fraction` 1) so the state stays finite/serializable.
|
|
66
|
+
*
|
|
67
|
+
* @capability carry-encumbrance carried mass vs capacity into movement penalty tiers and a speed multiplier
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveEncumbrance(mass: number, capacity: number, config?: EncumbranceConfig): EncumbranceState;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const DEFAULT_SOFT = 0.85;
|
|
2
|
+
const DEFAULT_FLOOR = 0.5;
|
|
3
|
+
/**
|
|
4
|
+
* Sum the mass of every carried stack using an injected per-unit mass resolver. Bounded by the
|
|
5
|
+
* number of entries; allocates nothing. Missing or negative unit masses are treated as 0.
|
|
6
|
+
*
|
|
7
|
+
* @param entries carried stacks (`{ itemId, quantity }`, compatible with any inventory listing).
|
|
8
|
+
* @param massOf per-unit mass lookup for an item id.
|
|
9
|
+
* @capability carry-load sum carried mass from item stacks via an injected mass resolver
|
|
10
|
+
*/
|
|
11
|
+
export function totalLoad(entries, massOf) {
|
|
12
|
+
let total = 0;
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
if (entry.quantity <= 0)
|
|
15
|
+
continue;
|
|
16
|
+
const unit = massOf(entry.itemId);
|
|
17
|
+
if (unit > 0)
|
|
18
|
+
total += unit * entry.quantity;
|
|
19
|
+
}
|
|
20
|
+
return total;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Progressive move-speed multiplier for a raw load fraction: 1 while at/below `soft`, a linear
|
|
24
|
+
* decay from 1 down toward `floor` across the `soft..1` band, and 0 once at/over capacity.
|
|
25
|
+
* Pure and side-effect free — the curve `resolveEncumbrance` reads for `moveMultiplier`.
|
|
26
|
+
*
|
|
27
|
+
* @capability encumbrance-move-curve progressive move-speed multiplier from a carry-load fraction
|
|
28
|
+
*/
|
|
29
|
+
export function encumbranceMoveMultiplier(fraction, config) {
|
|
30
|
+
const soft = config?.soft ?? DEFAULT_SOFT;
|
|
31
|
+
const floor = config?.floor ?? DEFAULT_FLOOR;
|
|
32
|
+
if (fraction >= 1)
|
|
33
|
+
return 0;
|
|
34
|
+
if (fraction <= soft)
|
|
35
|
+
return 1;
|
|
36
|
+
const span = 1 - soft;
|
|
37
|
+
const t = span > 0 ? (fraction - soft) / span : 1;
|
|
38
|
+
return 1 - (1 - floor) * t;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve carried mass against carrying capacity into a serializable encumbrance state: load
|
|
42
|
+
* fraction, tier, and the movement consequences (`canSprint`/`canJump`/`immobile` gates plus a
|
|
43
|
+
* progressive `moveMultiplier`). Below `soft` the carrier is unencumbered and unhindered; from
|
|
44
|
+
* `soft` up to capacity it is encumbered — no sprint or jump, and speed decays from 1 toward
|
|
45
|
+
* `floor`; at or above capacity it is immobile (multiplier 0). Deterministic and allocation-light;
|
|
46
|
+
* pair with {@link totalLoad} to derive `mass` from an inventory and a mass table. A capacity of 0
|
|
47
|
+
* or less pins any positive load as immobile (`fraction` 1) so the state stays finite/serializable.
|
|
48
|
+
*
|
|
49
|
+
* @capability carry-encumbrance carried mass vs capacity into movement penalty tiers and a speed multiplier
|
|
50
|
+
*/
|
|
51
|
+
export function resolveEncumbrance(mass, capacity, config) {
|
|
52
|
+
const soft = config?.soft ?? DEFAULT_SOFT;
|
|
53
|
+
const safeMass = mass > 0 ? mass : 0;
|
|
54
|
+
const fraction = capacity > 0 ? safeMass / capacity : safeMass > 0 ? 1 : 0;
|
|
55
|
+
const immobile = fraction >= 1;
|
|
56
|
+
const tier = immobile ? "immobile" : fraction >= soft ? "encumbered" : "unencumbered";
|
|
57
|
+
const unencumbered = tier === "unencumbered";
|
|
58
|
+
return {
|
|
59
|
+
mass: safeMass,
|
|
60
|
+
capacity,
|
|
61
|
+
fraction,
|
|
62
|
+
tier,
|
|
63
|
+
moveMultiplier: encumbranceMoveMultiplier(fraction, config),
|
|
64
|
+
canSprint: unencumbered,
|
|
65
|
+
canJump: unencumbered,
|
|
66
|
+
immobile,
|
|
67
|
+
};
|
|
68
|
+
}
|
package/dist/item/durability.js
CHANGED
package/dist/meta/changelog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating. */
|
|
2
|
-
export declare const VERSION = "0.
|
|
2
|
+
export declare const VERSION = "0.12.0";
|
|
3
3
|
/** One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md). */
|
|
4
4
|
export interface ChangelogEntry {
|
|
5
5
|
migrate: readonly string[];
|
package/dist/meta/changelog.js
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
/** Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating. */
|
|
2
|
-
export const VERSION = "0.
|
|
2
|
+
export const VERSION = "0.12.0";
|
|
3
3
|
/** Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`). */
|
|
4
4
|
export const CHANGELOG = {
|
|
5
|
+
"0.12.0": {
|
|
6
|
+
migrate: [
|
|
7
|
+
"Bump lockstep SDK packages to ^0.12.0: @jgengine/{core,react,ws,node,sql,convex,shell,editor,assets}. CLI jgengine is 0.9.0; @jgengine/github is unchanged.",
|
|
8
|
+
"Author games through defineGame from @jgengine/shell — the single public authoring entry. Core's constructor was renamed defineGame → defineGameDefinition (@jgengine/core/game/defineGame); only hosts and tooling call it. GamePlayer/GamePlayerShell are @internal; GameHost is the documented mount and owns the editor summon (?mode=editor, F2+E, dev save endpoint) — delete per-game editor bootstraps.",
|
|
9
|
+
"Declare worlds with world({ id, ground: { mode, size, surface? }, physics? }) from @jgengine/core/world/place (flat/round/voxel/board). environment() still works as the legacy consumption path for editor-written scene data — nothing required for existing games; author dressing in the editor.",
|
|
10
|
+
],
|
|
11
|
+
added: [
|
|
12
|
+
"world() place API (#1178) — @jgengine/core/world/place: ground mode/size discriminated at type and runtime (flat {x,z} with Infinity, round {radius}, voxel generator domain, board {x,y} cells); ground.surface matter/feel laws; per-place physics over the game default; multiple worlds per game; shell PlaceScene substrate rendering; seeds via seedForPlace.",
|
|
13
|
+
"@jgengine/shell/gameKit — happy-path authoring surface: authoring, mount, stores, systems, authored-scene helpers, HUD primitives, seeded rng.",
|
|
14
|
+
"Portable generic stat pools — @jgengine/core/stats/statPool (bounded named pools: health, energy, heat, ammo…); entity stats rebuilt on them; recipe in skill jgengine-combat.",
|
|
15
|
+
"Auction-house economy primitives (#1086) — @jgengine/core/economy/auctionBook: timed bid auctions, escrowed bids, outbid refunds, optional buyout, anti-snipe extension, settlement with house cut; plus market price history.",
|
|
16
|
+
"Portable minimap marker sources — core world seam for game minimaps; recipe in skill jgengine-ui.",
|
|
17
|
+
"Unified seed-driven path network + city generation v2 (#1103, #1106) — street/block/building dials, zone bands, presets, junction geometry, road-derived parcels; shell renders real intersections, curbs, lane lines, tunnels, bridge decks.",
|
|
18
|
+
"Mesh-accurate hitboxes (#1075), auto-fit colliders from model bounds (#1072), movement obstruction from fitted boxes + compound-box openings (#1077).",
|
|
19
|
+
"Editor overhaul (#1110) — materials/lighting/scripting/animation/multiplayer workspaces, command palette, minimap bake (#1036), GLB thumbnails, hierarchy DnD/keys/context/visibility-lock, multi-select inspector, console RPC, asset drag-place, ortho projection, pivot modes, play chrome.",
|
|
20
|
+
"Editor authoring pipeline — entity definitions and starter catalogs (#1012), Data-tab catalogs/schema fields (#1043), promote authored scene folder to runnable game (#1011), shared trigger primitive with goal/win rules (#1013), asset import rewrites game/assets.ts (#1042).",
|
|
21
|
+
],
|
|
22
|
+
changed: [
|
|
23
|
+
"npx jgengine create scaffolds a thin infinite flat world() place (no more 96m meadow); editor blankWorld drops pre-baked seeded grass — dressing belongs in editor.scene.json.",
|
|
24
|
+
"Environment studio visual-quality pass; grass fields no longer rebuild geometry per frame (#1090).",
|
|
25
|
+
],
|
|
26
|
+
removed: [],
|
|
27
|
+
},
|
|
5
28
|
"0.11.0": {
|
|
6
29
|
migrate: [
|
|
7
30
|
"Bump lockstep SDK packages to ^0.11.0: @jgengine/{core,react,ws,node,sql,convex,shell,editor,assets}. CLI jgengine and @jgengine/github may lag on their own version lines.",
|
|
@@ -121,9 +121,23 @@ export declare function advancePlayerMotion(motion: PlayerMotionState, intent: M
|
|
|
121
121
|
/** A placed scene object the walking player collides against as a circle-vs-AABB obstacle. */
|
|
122
122
|
export interface CollisionObstacle {
|
|
123
123
|
position: readonly [number, number, number];
|
|
124
|
-
/** Box half-size on each axis. Default `[0.5, 0.5, 0.5]` (unit-box scene objects). */
|
|
124
|
+
/** Box half-size on each axis. Default `[0.5, 0.5, 0.5]` (unit-box scene objects). Ignored when `boxes` is set. */
|
|
125
125
|
halfExtents?: readonly [number, number, number];
|
|
126
|
+
/** Local center shift added to `position` for the single-AABB path (the collider's yaw-rotated offset). Ignored when `boxes` is set. */
|
|
127
|
+
offset?: readonly [number, number, number];
|
|
128
|
+
/**
|
|
129
|
+
* Optional compound solid: axis-aligned sub-boxes as `min`/`max` **relative to `position`** (add
|
|
130
|
+
* `position` for world space), already yaw-rotated and conservatively expanded by the caller. When
|
|
131
|
+
* present the capsule slides against EACH sub-box instead of the single `halfExtents` AABB, so a
|
|
132
|
+
* concave opening (the gap between an archway's pillars/lintel) lets the capsule pass through.
|
|
133
|
+
*/
|
|
134
|
+
boxes?: readonly {
|
|
135
|
+
min: readonly [number, number, number];
|
|
136
|
+
max: readonly [number, number, number];
|
|
137
|
+
}[];
|
|
126
138
|
}
|
|
139
|
+
/** @internal Player radius the walking obstruction inflates obstacles by; callers gather within reach of it. */
|
|
140
|
+
export declare const DEFAULT_OBSTACLE_PLAYER_RADIUS = 0.3;
|
|
127
141
|
/**
|
|
128
142
|
* Resolve a horizontal step against nearby placed objects with classic
|
|
129
143
|
* axis-separated sliding (try X, then Z against the post-X position) so
|
|
@@ -187,7 +187,8 @@ export function advancePlayerMotion(motion, intent, forwardX, forwardZ, baseSpee
|
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
189
|
const DEFAULT_OBSTACLE_HALF_EXTENTS = [0.5, 0.5, 0.5];
|
|
190
|
-
|
|
190
|
+
/** @internal Player radius the walking obstruction inflates obstacles by; callers gather within reach of it. */
|
|
191
|
+
export const DEFAULT_OBSTACLE_PLAYER_RADIUS = 0.3;
|
|
191
192
|
/** Feet-to-head span used for the obstacle's vertical overlap test; matches DEFAULT_VOXEL_DIMS.height. */
|
|
192
193
|
const OBSTACLE_PLAYER_HEIGHT = 1.8;
|
|
193
194
|
function clampAxisStep(originalCoord, step, min, max) {
|
|
@@ -216,22 +217,39 @@ export function resolveObstacleStep(current, stepX, stepZ, obstacles, playerRadi
|
|
|
216
217
|
const reachX = Math.abs(stepX) + playerRadius;
|
|
217
218
|
const reachZ = Math.abs(stepZ) + playerRadius;
|
|
218
219
|
const nearby = [];
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
if (Math.abs(
|
|
228
|
-
|
|
220
|
+
const considerBox = (minX, maxX, minY, maxY, minZ, maxZ) => {
|
|
221
|
+
// Vertical-span cull: a sub-box entirely above the head (walk under the lintel) or below the feet is skipped.
|
|
222
|
+
if (maxY <= feetY || minY >= headY)
|
|
223
|
+
return;
|
|
224
|
+
const centerX = (minX + maxX) / 2;
|
|
225
|
+
const centerZ = (minZ + maxZ) / 2;
|
|
226
|
+
const halfX = (maxX - minX) / 2;
|
|
227
|
+
const halfZ = (maxZ - minZ) / 2;
|
|
228
|
+
if (Math.abs(centerX - current[0]) > halfX + reachX || Math.abs(centerZ - current[2]) > halfZ + reachZ)
|
|
229
|
+
return;
|
|
229
230
|
nearby.push({
|
|
230
|
-
minX:
|
|
231
|
-
maxX:
|
|
232
|
-
minZ:
|
|
233
|
-
maxZ:
|
|
231
|
+
minX: minX - playerRadius,
|
|
232
|
+
maxX: maxX + playerRadius,
|
|
233
|
+
minZ: minZ - playerRadius,
|
|
234
|
+
maxZ: maxZ + playerRadius,
|
|
234
235
|
});
|
|
236
|
+
};
|
|
237
|
+
for (const obstacle of obstacles) {
|
|
238
|
+
const px = obstacle.position[0];
|
|
239
|
+
const py = obstacle.position[1];
|
|
240
|
+
const pz = obstacle.position[2];
|
|
241
|
+
if (obstacle.boxes !== undefined && obstacle.boxes.length > 0) {
|
|
242
|
+
for (const box of obstacle.boxes) {
|
|
243
|
+
considerBox(px + box.min[0], px + box.max[0], py + box.min[1], py + box.max[1], pz + box.min[2], pz + box.max[2]);
|
|
244
|
+
}
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const halfExtents = obstacle.halfExtents ?? DEFAULT_OBSTACLE_HALF_EXTENTS;
|
|
248
|
+
const offset = obstacle.offset;
|
|
249
|
+
const cx = px + (offset !== undefined ? offset[0] : 0);
|
|
250
|
+
const cy = py + (offset !== undefined ? offset[1] : 0);
|
|
251
|
+
const cz = pz + (offset !== undefined ? offset[2] : 0);
|
|
252
|
+
considerBox(cx - halfExtents[0], cx + halfExtents[0], cy - halfExtents[1], cy + halfExtents[1], cz - halfExtents[2], cz + halfExtents[2]);
|
|
235
253
|
}
|
|
236
254
|
let resultX = stepX;
|
|
237
255
|
for (const box of nearby) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { applyHorizontalImpulses, applyMotionImpulses } from "../runtime/motionIntents.js";
|
|
2
2
|
import { groundFieldFor, hasEnvironmentTerrain, sampleSlope } from "../world/terrain.js";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveColliders } from "../scene/colliders.js";
|
|
4
|
+
import { advancePlayerMotion, constrainStepToAxis, createEmptyMovementKeys, createPlayerMotionState, DEFAULT_OBSTACLE_PLAYER_RADIUS, resolveMovementIntent, resolveObstacleStep, snapPositionToGrid, } from "./movementModel.js";
|
|
4
5
|
import { approachYaw, steerYaw } from "./steering.js";
|
|
5
6
|
import { advanceVoxelPlayer, createVoxelPlayerBody, } from "./voxelController.js";
|
|
6
7
|
const DEFAULT_TURN_SPEED = 2.4;
|
|
@@ -180,7 +181,7 @@ export function stepPlayerMovement(ctx, userId, input, dt, tuning, heading) {
|
|
|
180
181
|
stepZ = constrained.stepZ;
|
|
181
182
|
}
|
|
182
183
|
if (tuning.movement?.collideObjects === true) {
|
|
183
|
-
const obstacles =
|
|
184
|
+
const obstacles = gatherMovementObstacles(ctx, player.position, stepX, stepZ);
|
|
184
185
|
const resolved = resolveObstacleStep(player.position, stepX, stepZ, obstacles);
|
|
185
186
|
stepX = resolved.stepX;
|
|
186
187
|
stepZ = resolved.stepZ;
|
|
@@ -234,3 +235,102 @@ export function stepPlayerMovement(ctx, userId, input, dt, tuning, heading) {
|
|
|
234
235
|
dt,
|
|
235
236
|
});
|
|
236
237
|
}
|
|
238
|
+
/** Feet-to-head span the obstruction test uses; matches `movementModel`'s `OBSTACLE_PLAYER_HEIGHT`. */
|
|
239
|
+
const OBSTACLE_PLAYER_HEIGHT = 1.8;
|
|
240
|
+
/**
|
|
241
|
+
* Largest collider half-extent the bounded broadphase assumes, so an object whose blocking box reaches
|
|
242
|
+
* the player still has its center inside the `inBox` query — a ~4m wall (half-extent ~2m) is caught. An
|
|
243
|
+
* object wider than this on an axis could be missed at its far edge; raise it if a wider blocker exists.
|
|
244
|
+
*/
|
|
245
|
+
const OBSTACLE_MAX_HALF_EXTENT = 4;
|
|
246
|
+
/**
|
|
247
|
+
* Gather the player's nearby blocking obstacles from resolved colliders — the mesh-accurate replacement
|
|
248
|
+
* for the old `nearbyObstacles(list(), …)` which emitted a default 1×1×1 box per object. Bounded by an
|
|
249
|
+
* `inBox` broadphase (reach ≥ player radius + max half-extent so wide walls can't be missed), then, per
|
|
250
|
+
* candidate, keeps only blocking physical colliders and emits their real half-extents/offset (or, for a
|
|
251
|
+
* mesh collider carrying `boxes`, the compound sub-boxes), conservatively yaw-rotated by the object's
|
|
252
|
+
* `rotationY`. Objects with no resolved collider fall back to the default box; objects whose colliders
|
|
253
|
+
* don't block stop obstructing. Runs for both the shell's local player and a host, so parity is automatic.
|
|
254
|
+
*/
|
|
255
|
+
function gatherMovementObstacles(ctx, position, stepX, stepZ) {
|
|
256
|
+
const reachX = Math.abs(stepX) + DEFAULT_OBSTACLE_PLAYER_RADIUS + OBSTACLE_MAX_HALF_EXTENT;
|
|
257
|
+
const reachZ = Math.abs(stepZ) + DEFAULT_OBSTACLE_PLAYER_RADIUS + OBSTACLE_MAX_HALF_EXTENT;
|
|
258
|
+
const min = [
|
|
259
|
+
position[0] - reachX,
|
|
260
|
+
position[1] - OBSTACLE_MAX_HALF_EXTENT,
|
|
261
|
+
position[2] - reachZ,
|
|
262
|
+
];
|
|
263
|
+
const max = [
|
|
264
|
+
position[0] + reachX,
|
|
265
|
+
position[1] + OBSTACLE_PLAYER_HEIGHT + OBSTACLE_MAX_HALF_EXTENT,
|
|
266
|
+
position[2] + reachZ,
|
|
267
|
+
];
|
|
268
|
+
const obstacles = [];
|
|
269
|
+
for (const object of ctx.scene.object.inBox(min, max)) {
|
|
270
|
+
const set = ctx.scene.object.collidersOf(object.instanceId);
|
|
271
|
+
if (set === null) {
|
|
272
|
+
// No resolved collider: preserve today's default 1×1×1 box.
|
|
273
|
+
obstacles.push({ position: object.position });
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
for (const collider of resolveColliders(set)) {
|
|
277
|
+
if (!collider.blocks || collider.purpose !== "physical")
|
|
278
|
+
continue;
|
|
279
|
+
obstacles.push(obstacleFromCollider(collider, object.position, object.rotationY));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return obstacles;
|
|
283
|
+
}
|
|
284
|
+
/** One blocking collider as a {@link CollisionObstacle}: a compound `boxes` obstacle for a mesh collider
|
|
285
|
+
* carrying sub-boxes, else a single half-extents/offset AABB. Yaw is applied by conservatively expanding
|
|
286
|
+
* each AABB to its rotated extent (mirroring `worldOffset`), so the obstruction never under-covers. */
|
|
287
|
+
function obstacleFromCollider(collider, position, rotationY) {
|
|
288
|
+
const cos = Math.cos(rotationY);
|
|
289
|
+
const sin = Math.sin(rotationY);
|
|
290
|
+
const shape = collider.shape;
|
|
291
|
+
if (shape.kind === "mesh" && shape.boxes !== undefined && shape.boxes.length > 0) {
|
|
292
|
+
return {
|
|
293
|
+
position,
|
|
294
|
+
boxes: shape.boxes.map((b) => yawExpandLocalBox(b.min, b.max, cos, sin)),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
let hx;
|
|
298
|
+
let hy;
|
|
299
|
+
let hz;
|
|
300
|
+
if (shape.kind === "sphere") {
|
|
301
|
+
hx = hy = hz = shape.radius;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
hx = shape.halfExtents[0];
|
|
305
|
+
hy = shape.halfExtents[1];
|
|
306
|
+
hz = shape.halfExtents[2];
|
|
307
|
+
}
|
|
308
|
+
const offset = shape.offset;
|
|
309
|
+
const ox = offset !== undefined ? offset[0] : 0;
|
|
310
|
+
const oy = offset !== undefined ? offset[1] : 0;
|
|
311
|
+
const oz = offset !== undefined ? offset[2] : 0;
|
|
312
|
+
return {
|
|
313
|
+
position,
|
|
314
|
+
// Rotate the offset by yaw (mirror of colliders.worldOffset), expand the extents to the rotated AABB.
|
|
315
|
+
offset: [ox * cos + oz * sin, oy, -ox * sin + oz * cos],
|
|
316
|
+
halfExtents: [Math.abs(hx * cos) + Math.abs(hz * sin), hy, Math.abs(hx * sin) + Math.abs(hz * cos)],
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
/** Yaw-rotate an entity-local AABB about the object origin and re-fit it to an axis-aligned box, expressed
|
|
320
|
+
* as `min`/`max` relative to `position`. Conservative: the rotated box is grown to its enclosing AABB. */
|
|
321
|
+
function yawExpandLocalBox(boxMin, boxMax, cos, sin) {
|
|
322
|
+
const centerX = (boxMin[0] + boxMax[0]) / 2;
|
|
323
|
+
const centerY = (boxMin[1] + boxMax[1]) / 2;
|
|
324
|
+
const centerZ = (boxMin[2] + boxMax[2]) / 2;
|
|
325
|
+
const halfX = (boxMax[0] - boxMin[0]) / 2;
|
|
326
|
+
const halfY = (boxMax[1] - boxMin[1]) / 2;
|
|
327
|
+
const halfZ = (boxMax[2] - boxMin[2]) / 2;
|
|
328
|
+
const rcX = centerX * cos + centerZ * sin;
|
|
329
|
+
const rcZ = -centerX * sin + centerZ * cos;
|
|
330
|
+
const rhX = Math.abs(halfX * cos) + Math.abs(halfZ * sin);
|
|
331
|
+
const rhZ = Math.abs(halfX * sin) + Math.abs(halfZ * cos);
|
|
332
|
+
return {
|
|
333
|
+
min: [rcX - rhX, centerY - halfY, rcZ - rhZ],
|
|
334
|
+
max: [rcX + rhX, centerY + halfY, rcZ + rhZ],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomSeedFrom, stepRandomSeed } from "../random/rng.js";
|
|
1
2
|
function cloneStats(stats) {
|
|
2
3
|
return { ...stats };
|
|
3
4
|
}
|
|
@@ -21,14 +22,18 @@ export function cloneSnapshot(snapshot) {
|
|
|
21
22
|
capturedAt: snapshot.capturedAt,
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Deterministic per-replay PRNG built from the shared {@link randomSeedFrom} /
|
|
27
|
+
* {@link stepRandomSeed} primitives. `randomSeedFrom` takes the raw integer seed
|
|
28
|
+
* without string hashing, so the emitted sequence is byte-identical to the former
|
|
29
|
+
* inline mulberry32 — replay determinism across peers is preserved.
|
|
30
|
+
*/
|
|
31
|
+
function seededPrng(seed) {
|
|
32
|
+
let cursor = randomSeedFrom(seed);
|
|
26
33
|
return () => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
31
|
-
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
34
|
+
const [value, next] = stepRandomSeed(cursor);
|
|
35
|
+
cursor = next;
|
|
36
|
+
return value;
|
|
32
37
|
};
|
|
33
38
|
}
|
|
34
39
|
function toLive(snapshot, rules) {
|
|
@@ -43,7 +48,7 @@ export function replayCombat(a, b, rules) {
|
|
|
43
48
|
const maxRounds = rules.maxRounds ?? 100;
|
|
44
49
|
const critChance = rules.critChance ?? 0;
|
|
45
50
|
const critMultiplier = rules.critMultiplier ?? 2;
|
|
46
|
-
const prng =
|
|
51
|
+
const prng = seededPrng((a.seed ^ (b.seed * 0x9e3779b1)) >>> 0);
|
|
47
52
|
const liveA = toLive(a, rules);
|
|
48
53
|
const liveB = toLive(b, rules);
|
|
49
54
|
const blows = [];
|
|
@@ -32,6 +32,8 @@ export interface PoseSyncRules {
|
|
|
32
32
|
/** Unchanged poses refresh the keep-alive stamp at most this often. */
|
|
33
33
|
keepAliveRefreshMs: number;
|
|
34
34
|
}
|
|
35
|
+
/** Canonical client-authoritative pose-sync tuning shared by every host transport (WS, Convex). */
|
|
36
|
+
export declare const DEFAULT_POSE_SYNC_RULES: PoseSyncRules;
|
|
35
37
|
export interface PoseSyncDecision {
|
|
36
38
|
position: {
|
|
37
39
|
x: number;
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/** Canonical client-authoritative pose-sync tuning shared by every host transport (WS, Convex). */
|
|
2
|
+
export const DEFAULT_POSE_SYNC_RULES = {
|
|
3
|
+
maxSpeed: 12,
|
|
4
|
+
maxVerticalOffset: 3,
|
|
5
|
+
minElapsedSec: 0.05,
|
|
6
|
+
maxElapsedSec: 0.5,
|
|
7
|
+
keepAliveRefreshMs: 10_000,
|
|
8
|
+
};
|
|
1
9
|
function appearanceEqual(a, b) {
|
|
2
10
|
if (a === b)
|
|
3
11
|
return true;
|
|
@@ -12,6 +12,8 @@ export interface DrivableVehicleOptions {
|
|
|
12
12
|
export interface DrivableVehiclePose {
|
|
13
13
|
position: DrivableVehiclePosition;
|
|
14
14
|
rotationY: number;
|
|
15
|
+
rotationX: number;
|
|
16
|
+
rotationZ: number;
|
|
15
17
|
dt: number;
|
|
16
18
|
}
|
|
17
19
|
/** {@link tickDrivableVehicle}'s result — the ready-to-apply pose patch plus the raw sim step for HUD/telemetry reads. */
|
|
@@ -10,7 +10,7 @@ export function tickDrivableVehicle(vehicle, dt, axis, options = {}) {
|
|
|
10
10
|
const [x, y, z] = step.position;
|
|
11
11
|
const resolvedY = options.groundHeight === undefined ? y : options.groundHeight(x, z);
|
|
12
12
|
return {
|
|
13
|
-
pose: { position: [x, resolvedY, z], rotationY: step.heading, dt },
|
|
13
|
+
pose: { position: [x, resolvedY, z], rotationX: step.bodyPitch, rotationY: step.heading, rotationZ: step.bodyRoll, dt },
|
|
14
14
|
step,
|
|
15
15
|
};
|
|
16
16
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type GravityField } from "./gravityField.js";
|
|
2
|
+
/** Three-dimensional world-space vector used by the flight model. */
|
|
3
|
+
export type FlightVector = readonly [number, number, number];
|
|
4
|
+
/** Supported aerodynamic/propulsion families. */
|
|
5
|
+
export type AircraftKind = "fixedWing" | "rotorcraft" | "vtol";
|
|
6
|
+
/** Normalized pilot inputs for one flight-simulation tick. */
|
|
7
|
+
export interface FlightControlInput {
|
|
8
|
+
throttle: number;
|
|
9
|
+
pitch: number;
|
|
10
|
+
roll: number;
|
|
11
|
+
yaw: number;
|
|
12
|
+
collective?: number;
|
|
13
|
+
airbrake?: number;
|
|
14
|
+
afterburner?: number;
|
|
15
|
+
vectoring?: number;
|
|
16
|
+
}
|
|
17
|
+
/** Angular authority, response, damping, and self-leveling configuration. */
|
|
18
|
+
export interface FlightControlRates {
|
|
19
|
+
pitch: number;
|
|
20
|
+
roll: number;
|
|
21
|
+
yaw: number;
|
|
22
|
+
response: number;
|
|
23
|
+
damping: number;
|
|
24
|
+
stability: number;
|
|
25
|
+
}
|
|
26
|
+
/** Data-first physical tuning shared by all aircraft instances of one catalog type. */
|
|
27
|
+
export interface AircraftTuning {
|
|
28
|
+
kind: AircraftKind;
|
|
29
|
+
mass: number;
|
|
30
|
+
gravity?: number;
|
|
31
|
+
maxThrust: number;
|
|
32
|
+
maxSpeed: number;
|
|
33
|
+
drag: number;
|
|
34
|
+
sideDrag: number;
|
|
35
|
+
lift: number;
|
|
36
|
+
stallSpeed: number;
|
|
37
|
+
controls: FlightControlRates;
|
|
38
|
+
afterburnerMultiplier?: number;
|
|
39
|
+
hoverThrust?: number;
|
|
40
|
+
rotorResponse?: number;
|
|
41
|
+
groundEffectHeight?: number;
|
|
42
|
+
vtolTransitionSpeed?: number;
|
|
43
|
+
groundClearance?: number;
|
|
44
|
+
}
|
|
45
|
+
/** Spawn state and injectable world-field samplers for an aircraft instance. */
|
|
46
|
+
export interface AircraftOptions {
|
|
47
|
+
position?: FlightVector;
|
|
48
|
+
velocity?: FlightVector;
|
|
49
|
+
rotation?: FlightVector;
|
|
50
|
+
wind?: (position: FlightVector) => FlightVector;
|
|
51
|
+
groundHeight?: (x: number, z: number) => number;
|
|
52
|
+
gravityField?: GravityField;
|
|
53
|
+
}
|
|
54
|
+
/** Pose and aerodynamic telemetry returned after one flight tick. */
|
|
55
|
+
export interface AircraftStep {
|
|
56
|
+
position: FlightVector;
|
|
57
|
+
velocity: FlightVector;
|
|
58
|
+
rotation: FlightVector;
|
|
59
|
+
forwardSpeed: number;
|
|
60
|
+
airspeed: number;
|
|
61
|
+
verticalSpeed: number;
|
|
62
|
+
angleOfAttack: number;
|
|
63
|
+
liftFraction: number;
|
|
64
|
+
stalled: boolean;
|
|
65
|
+
grounded: boolean;
|
|
66
|
+
gForce: number;
|
|
67
|
+
}
|
|
68
|
+
/** Stateful six-degree-of-freedom aircraft simulation. */
|
|
69
|
+
export interface AircraftDynamics {
|
|
70
|
+
tick(dt: number, input: FlightControlInput): AircraftStep;
|
|
71
|
+
pose(): {
|
|
72
|
+
position: FlightVector;
|
|
73
|
+
rotation: FlightVector;
|
|
74
|
+
};
|
|
75
|
+
velocity(): FlightVector;
|
|
76
|
+
resetTo(position: FlightVector, rotation?: FlightVector): void;
|
|
77
|
+
setVelocity(velocity: FlightVector): void;
|
|
78
|
+
}
|
|
79
|
+
/** Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
|
|
80
|
+
* @capability flight-dynamics simulate fixed-wing, helicopter, and VTOL aircraft
|
|
81
|
+
*/
|
|
82
|
+
export declare function createAircraftDynamics(tuning: AircraftTuning, options?: AircraftOptions): AircraftDynamics;
|