@jgengine/core 0.6.0 → 0.8.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 +214 -2
- package/README.md +2 -0
- 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 +61 -0
- package/dist/ai/spawnDirector.js +162 -0
- package/dist/ai/threat.d.ts +25 -0
- package/dist/ai/threat.js +67 -0
- package/dist/anim/easing.d.ts +14 -0
- package/dist/anim/easing.js +55 -0
- package/dist/audio/audioFalloff.d.ts +38 -0
- package/dist/audio/audioFalloff.js +32 -0
- package/dist/behaviour/behaviour.d.ts +136 -0
- package/dist/behaviour/behaviour.js +388 -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 +64 -0
- package/dist/cards/cardPile.js +175 -0
- package/dist/cards/modifierPipeline.d.ts +33 -0
- package/dist/cards/modifierPipeline.js +48 -0
- package/dist/combat/abilityKit.d.ts +47 -0
- package/dist/combat/abilityKit.js +139 -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 +12 -1
- package/dist/combat/effects.js +31 -19
- package/dist/combat/hitReaction.d.ts +23 -0
- package/dist/combat/hitReaction.js +20 -0
- package/dist/combat/projectiles.d.ts +42 -5
- package/dist/combat/projectiles.js +98 -12
- package/dist/combat/resistance.d.ts +13 -0
- package/dist/combat/resistance.js +46 -0
- package/dist/combat/telegraph.d.ts +31 -0
- package/dist/combat/telegraph.js +51 -0
- package/dist/commands/commandRegistry.d.ts +1 -1
- package/dist/commands/commandRegistry.js +2 -1
- 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/data/dataSource.d.ts +26 -0
- package/dist/data/dataSource.js +93 -0
- package/dist/data/devProxy.d.ts +11 -0
- package/dist/data/devProxy.js +38 -0
- package/dist/data/fetchJson.d.ts +20 -0
- package/dist/data/fetchJson.js +43 -0
- package/dist/data/jsonDataSource.d.ts +4 -0
- package/dist/data/jsonDataSource.js +6 -0
- package/dist/devtools/devtools.d.ts +157 -0
- package/dist/devtools/devtools.js +484 -0
- package/dist/devtools/transformTunables.d.ts +14 -0
- package/dist/devtools/transformTunables.js +38 -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/chat.d.ts +66 -0
- package/dist/game/chat.js +165 -0
- package/dist/game/cosmetics.d.ts +25 -0
- package/dist/game/cosmetics.js +51 -0
- package/dist/game/defineGame.d.ts +10 -2
- package/dist/game/defineGame.js +2 -1
- package/dist/game/events.d.ts +87 -0
- package/dist/game/levelSequence.d.ts +44 -0
- package/dist/game/levelSequence.js +78 -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 +516 -8
- package/dist/game/playableGame.js +6 -1
- package/dist/game/progression.d.ts +1 -0
- package/dist/game/progression.js +23 -2
- package/dist/game/race.d.ts +100 -0
- package/dist/game/race.js +236 -0
- package/dist/game/runDraft.d.ts +34 -0
- package/dist/game/runDraft.js +97 -0
- package/dist/game/social.d.ts +69 -0
- package/dist/game/social.js +138 -0
- package/dist/game/spawnPoints.d.ts +19 -0
- package/dist/game/spawnPoints.js +23 -0
- package/dist/game/worldItem.d.ts +95 -0
- package/dist/game/worldItem.js +138 -0
- package/dist/input/actionBindings.d.ts +10 -0
- package/dist/input/actionBindings.js +14 -0
- package/dist/input/axisInput.d.ts +43 -0
- package/dist/input/axisInput.js +70 -0
- package/dist/input/gestureSurface.d.ts +31 -0
- package/dist/input/gestureSurface.js +120 -0
- package/dist/input/pointer.d.ts +69 -0
- package/dist/input/pointer.js +65 -0
- package/dist/input/touchScheme.d.ts +83 -0
- package/dist/input/touchScheme.js +115 -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 +115 -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 +71 -1
- package/dist/movement/dash.d.ts +37 -0
- package/dist/movement/dash.js +65 -0
- package/dist/movement/movementModel.d.ts +36 -3
- package/dist/movement/movementModel.js +81 -5
- package/dist/movement/voxelController.d.ts +61 -0
- package/dist/movement/voxelController.js +229 -0
- package/dist/multiplayer/chatContract.d.ts +42 -0
- package/dist/multiplayer/chatContract.js +57 -0
- package/dist/multiplayer/combatSnapshot.d.ts +45 -0
- package/dist/multiplayer/combatSnapshot.js +89 -0
- package/dist/multiplayer/identity.d.ts +13 -0
- package/dist/multiplayer/identity.js +20 -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/poseSyncGate.d.ts +1 -0
- package/dist/multiplayer/poseSyncGate.js +13 -1
- package/dist/multiplayer/presenceModel.d.ts +3 -0
- package/dist/multiplayer/presenceModel.js +15 -1
- package/dist/multiplayer/simultaneousCommit.d.ts +34 -0
- package/dist/multiplayer/simultaneousCommit.js +53 -0
- package/dist/multiplayer/voiceContract.d.ts +44 -0
- package/dist/multiplayer/voiceContract.js +108 -0
- package/dist/nav/navConstrain.d.ts +14 -0
- package/dist/nav/navConstrain.js +26 -0
- package/dist/nav/navFromEnvironment.d.ts +11 -0
- package/dist/nav/navFromEnvironment.js +16 -0
- package/dist/nav/navGrid.d.ts +56 -0
- package/dist/nav/navGrid.js +265 -0
- package/dist/nav/pathFollow.d.ts +31 -0
- package/dist/nav/pathFollow.js +69 -0
- package/dist/physics/ballisticSweep.d.ts +18 -0
- package/dist/physics/ballisticSweep.js +49 -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 +124 -2
- package/dist/physics/physicsWorld.js +493 -41
- 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 +143 -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 +99 -0
- package/dist/physics/vehicleBody.js +233 -0
- package/dist/puzzle/cellGrid.d.ts +26 -0
- package/dist/puzzle/cellGrid.js +110 -0
- package/dist/puzzle/fallingPiece.d.ts +24 -0
- package/dist/puzzle/fallingPiece.js +51 -0
- package/dist/random/nameGen.d.ts +18 -0
- package/dist/random/nameGen.js +35 -0
- package/dist/random/rng.d.ts +2 -0
- package/dist/random/rng.js +23 -0
- package/dist/runtime/adapter.d.ts +35 -0
- package/dist/runtime/adapter.js +37 -1
- package/dist/runtime/cameraDirector.d.ts +10 -0
- package/dist/runtime/cameraDirector.js +25 -0
- package/dist/runtime/gameContext.d.ts +107 -3
- package/dist/runtime/gameContext.js +253 -7
- package/dist/runtime/hostPersistence.d.ts +39 -0
- package/dist/runtime/hostPersistence.js +54 -0
- package/dist/runtime/inputSnapshot.d.ts +7 -0
- package/dist/runtime/inputSnapshot.js +12 -0
- package/dist/runtime/motionIntents.d.ts +25 -0
- package/dist/runtime/motionIntents.js +25 -0
- package/dist/runtime/persistenceScope.d.ts +32 -0
- package/dist/runtime/persistenceScope.js +64 -0
- package/dist/runtime/transport.d.ts +32 -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 +9 -1
- package/dist/scene/entityStore.js +25 -2
- 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/movementSpeed.d.ts +35 -0
- package/dist/scene/movementSpeed.js +20 -0
- package/dist/scene/objectQuery.d.ts +18 -0
- package/dist/scene/objectQuery.js +108 -0
- package/dist/scene/objectStore.d.ts +11 -0
- package/dist/scene/objectStore.js +109 -4
- package/dist/scene/paintLayer.d.ts +15 -0
- package/dist/scene/paintLayer.js +53 -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/spatial.d.ts +21 -0
- package/dist/scene/spatial.js +63 -1
- package/dist/scene/stationClaim.d.ts +43 -0
- package/dist/scene/stationClaim.js +109 -0
- package/dist/sensor/concealment.d.ts +21 -0
- package/dist/sensor/concealment.js +63 -0
- package/dist/sensor/freezeMonitor.d.ts +17 -0
- package/dist/sensor/freezeMonitor.js +35 -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/roles.d.ts +6 -0
- package/dist/session/roles.js +32 -0
- package/dist/session/roundState.d.ts +79 -0
- package/dist/session/roundState.js +158 -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/fallingGrid.d.ts +62 -0
- package/dist/tactics/fallingGrid.js +158 -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 +68 -0
- package/dist/tactics/tacticalGrid.js +261 -0
- package/dist/time/beatClock.d.ts +40 -0
- package/dist/time/beatClock.js +73 -0
- package/dist/time/simClock.d.ts +25 -1
- package/dist/time/simClock.js +28 -3
- package/dist/turn/commit.d.ts +27 -0
- package/dist/turn/commit.js +72 -0
- package/dist/turn/intent.d.ts +14 -0
- package/dist/turn/intent.js +29 -0
- package/dist/turn/turnLoop.d.ts +60 -0
- package/dist/turn/turnLoop.js +223 -0
- package/dist/world/buildPermissions.d.ts +47 -0
- package/dist/world/buildPermissions.js +134 -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/environmentSummary.d.ts +53 -0
- package/dist/world/environmentSummary.js +129 -0
- package/dist/world/features.d.ts +87 -6
- package/dist/world/features.js +52 -2
- package/dist/world/fog.d.ts +40 -0
- package/dist/world/fog.js +137 -0
- package/dist/world/gridInstances.d.ts +13 -0
- package/dist/world/gridInstances.js +22 -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 +69 -0
- package/dist/world/placementController.js +153 -0
- package/dist/world/realm.d.ts +61 -0
- package/dist/world/realm.js +66 -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 +23 -1
- package/dist/world/terrain.js +64 -1
- package/dist/world/voxelField.d.ts +54 -0
- package/dist/world/voxelField.js +241 -0
- package/dist/world/walls.d.ts +61 -0
- package/dist/world/walls.js +221 -0
- package/dist/world/weather.d.ts +92 -0
- package/dist/world/weather.js +169 -0
- package/llms.txt +4102 -0
- package/package.json +3 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type SessionVisibility = "public" | "private";
|
|
2
|
+
export type SessionStatus = "open" | "running" | "closed";
|
|
3
|
+
export interface SessionListing {
|
|
4
|
+
serverId: string;
|
|
5
|
+
gameId: string;
|
|
6
|
+
status: SessionStatus;
|
|
7
|
+
visibility: SessionVisibility;
|
|
8
|
+
memberCount: number;
|
|
9
|
+
slotsPerServer: number;
|
|
10
|
+
label?: string;
|
|
11
|
+
mode?: string;
|
|
12
|
+
joinCode?: string;
|
|
13
|
+
tags?: readonly string[];
|
|
14
|
+
updatedAt: number;
|
|
15
|
+
}
|
|
16
|
+
export interface MatchFilter {
|
|
17
|
+
mode?: string;
|
|
18
|
+
tags?: readonly string[];
|
|
19
|
+
query?: string;
|
|
20
|
+
notFull?: boolean;
|
|
21
|
+
status?: readonly SessionStatus[];
|
|
22
|
+
includePrivate?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface BrowseOptions {
|
|
25
|
+
limit?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function normalizeJoinCode(code: string): string;
|
|
28
|
+
export declare function generateJoinCode(random: () => number, length?: number): string;
|
|
29
|
+
export declare function hasSpace(listing: SessionListing): boolean;
|
|
30
|
+
export declare function matchesFilter(listing: SessionListing, filter: MatchFilter): boolean;
|
|
31
|
+
export declare function browseSessions(listings: readonly SessionListing[], filter?: MatchFilter, options?: BrowseOptions): SessionListing[];
|
|
32
|
+
export declare function findByJoinCode(listings: readonly SessionListing[], code: string): SessionListing | null;
|
|
33
|
+
export declare function quickMatch(listings: readonly SessionListing[], filter?: MatchFilter): SessionListing | null;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export function normalizeJoinCode(code) {
|
|
2
|
+
return code.toUpperCase().replace(/[^A-Z0-9]/g, "");
|
|
3
|
+
}
|
|
4
|
+
const CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
5
|
+
export function generateJoinCode(random, length = 6) {
|
|
6
|
+
let code = "";
|
|
7
|
+
for (let i = 0; i < length; i += 1) {
|
|
8
|
+
const index = Math.min(CODE_ALPHABET.length - 1, Math.floor(random() * CODE_ALPHABET.length));
|
|
9
|
+
code += CODE_ALPHABET[index];
|
|
10
|
+
}
|
|
11
|
+
return code;
|
|
12
|
+
}
|
|
13
|
+
export function hasSpace(listing) {
|
|
14
|
+
return listing.memberCount < listing.slotsPerServer;
|
|
15
|
+
}
|
|
16
|
+
export function matchesFilter(listing, filter) {
|
|
17
|
+
if (listing.visibility === "private" && filter.includePrivate !== true)
|
|
18
|
+
return false;
|
|
19
|
+
const statuses = filter.status ?? ["open", "running"];
|
|
20
|
+
if (!statuses.includes(listing.status))
|
|
21
|
+
return false;
|
|
22
|
+
if (filter.mode !== undefined && listing.mode !== filter.mode)
|
|
23
|
+
return false;
|
|
24
|
+
if (filter.notFull === true && !hasSpace(listing))
|
|
25
|
+
return false;
|
|
26
|
+
if (filter.tags !== undefined && filter.tags.length > 0) {
|
|
27
|
+
const owned = new Set(listing.tags ?? []);
|
|
28
|
+
if (!filter.tags.every((tag) => owned.has(tag)))
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (filter.query !== undefined && filter.query.length > 0) {
|
|
32
|
+
const haystack = (listing.label ?? "").toLowerCase();
|
|
33
|
+
if (!haystack.includes(filter.query.toLowerCase()))
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
export function browseSessions(listings, filter = {}, options = {}) {
|
|
39
|
+
const matched = listings
|
|
40
|
+
.filter((listing) => matchesFilter(listing, filter))
|
|
41
|
+
.sort((a, b) => b.memberCount - a.memberCount || b.updatedAt - a.updatedAt);
|
|
42
|
+
return options.limit !== undefined ? matched.slice(0, options.limit) : matched;
|
|
43
|
+
}
|
|
44
|
+
export function findByJoinCode(listings, code) {
|
|
45
|
+
const normalized = normalizeJoinCode(code);
|
|
46
|
+
if (normalized.length === 0)
|
|
47
|
+
return null;
|
|
48
|
+
for (const listing of listings) {
|
|
49
|
+
if (listing.joinCode !== undefined && normalizeJoinCode(listing.joinCode) === normalized) {
|
|
50
|
+
return listing;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
export function quickMatch(listings, filter = {}) {
|
|
56
|
+
const candidates = listings
|
|
57
|
+
.filter((listing) => matchesFilter(listing, { ...filter, notFull: true }))
|
|
58
|
+
.sort((a, b) => b.memberCount - a.memberCount || a.updatedAt - b.updatedAt);
|
|
59
|
+
return candidates[0] ?? null;
|
|
60
|
+
}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
function appearanceChanged(a, b) {
|
|
2
|
+
if (a === b)
|
|
3
|
+
return false;
|
|
4
|
+
if (a === undefined || b === undefined)
|
|
5
|
+
return true;
|
|
6
|
+
const aKeys = Object.keys(a);
|
|
7
|
+
const bKeys = Object.keys(b);
|
|
8
|
+
if (aKeys.length !== bKeys.length)
|
|
9
|
+
return true;
|
|
10
|
+
return aKeys.some((key) => a[key] !== b[key]);
|
|
11
|
+
}
|
|
1
12
|
function poseChanged(a, b, tuning) {
|
|
2
13
|
return (Math.abs(a.x - b.x) > tuning.positionEpsilon
|
|
3
14
|
|| Math.abs(a.y - b.y) > tuning.verticalEpsilon
|
|
4
15
|
|| Math.abs(a.z - b.z) > tuning.positionEpsilon
|
|
5
16
|
|| Math.abs(a.rotationY - b.rotationY) > tuning.rotationEpsilon
|
|
6
|
-
|| Math.abs(a.rotationPitch - b.rotationPitch) > tuning.rotationEpsilon
|
|
17
|
+
|| Math.abs(a.rotationPitch - b.rotationPitch) > tuning.rotationEpsilon
|
|
18
|
+
|| appearanceChanged(a.appearance, b.appearance));
|
|
7
19
|
}
|
|
8
20
|
export function createPoseSyncGate(tuning) {
|
|
9
21
|
let lastSentPose = null;
|
|
@@ -7,6 +7,7 @@ export interface PresencePoseState {
|
|
|
7
7
|
rotationY: number;
|
|
8
8
|
rotationPitch?: number;
|
|
9
9
|
lastSeenAtMs?: number;
|
|
10
|
+
appearance?: Record<string, string>;
|
|
10
11
|
}
|
|
11
12
|
export interface IncomingPose {
|
|
12
13
|
position: {
|
|
@@ -16,6 +17,7 @@ export interface IncomingPose {
|
|
|
16
17
|
};
|
|
17
18
|
rotationY?: number;
|
|
18
19
|
rotationPitch?: number;
|
|
20
|
+
appearance?: Record<string, string>;
|
|
19
21
|
}
|
|
20
22
|
export interface PoseSyncRules {
|
|
21
23
|
/** Speed cap (units/sec) for client-authoritative movement. */
|
|
@@ -36,6 +38,7 @@ export interface PoseSyncDecision {
|
|
|
36
38
|
};
|
|
37
39
|
rotationY: number;
|
|
38
40
|
rotationPitch: number;
|
|
41
|
+
appearance?: Record<string, string>;
|
|
39
42
|
/** True when the pose differs and a pose write is needed. */
|
|
40
43
|
changed: boolean;
|
|
41
44
|
/** True when only the keep-alive stamp should be written. */
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
function appearanceEqual(a, b) {
|
|
2
|
+
if (a === b)
|
|
3
|
+
return true;
|
|
4
|
+
if (a === undefined || b === undefined)
|
|
5
|
+
return false;
|
|
6
|
+
const aKeys = Object.keys(a);
|
|
7
|
+
const bKeys = Object.keys(b);
|
|
8
|
+
if (aKeys.length !== bKeys.length)
|
|
9
|
+
return false;
|
|
10
|
+
return aKeys.every((key) => a[key] === b[key]);
|
|
11
|
+
}
|
|
1
12
|
export function decidePoseSync(current, incoming, rules, nowMs) {
|
|
2
13
|
const elapsedSec = Math.max(rules.minElapsedSec, Math.min(rules.maxElapsedSec, (nowMs - (current.lastSeenAtMs ?? nowMs)) / 1000));
|
|
3
14
|
const maxDist = rules.maxSpeed * elapsedSec;
|
|
@@ -17,14 +28,17 @@ export function decidePoseSync(current, incoming, rules, nowMs) {
|
|
|
17
28
|
: Math.max(0, Math.min(rules.maxVerticalOffset, incoming.position.y));
|
|
18
29
|
const nextRotationY = incoming.rotationY ?? current.rotationY;
|
|
19
30
|
const nextRotationPitch = incoming.rotationPitch ?? current.rotationPitch ?? 0;
|
|
31
|
+
const nextAppearance = incoming.appearance ?? current.appearance;
|
|
32
|
+
const appearanceChanged = incoming.appearance !== undefined && !appearanceEqual(incoming.appearance, current.appearance);
|
|
20
33
|
const moved = targetX !== current.position.x || targetZ !== current.position.z;
|
|
21
34
|
const jumped = nextY !== current.position.y;
|
|
22
35
|
const rotated = nextRotationY !== current.rotationY || nextRotationPitch !== (current.rotationPitch ?? 0);
|
|
23
|
-
const changed = moved || jumped || rotated;
|
|
36
|
+
const changed = moved || jumped || rotated || appearanceChanged;
|
|
24
37
|
return {
|
|
25
38
|
position: { x: targetX, y: nextY, z: targetZ },
|
|
26
39
|
rotationY: nextRotationY,
|
|
27
40
|
rotationPitch: nextRotationPitch,
|
|
41
|
+
appearance: nextAppearance,
|
|
28
42
|
changed,
|
|
29
43
|
refreshKeepAlive: !changed && shouldRefreshKeepAlive(current.lastSeenAtMs, nowMs, rules),
|
|
30
44
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type CommitPhase = "collecting" | "revealed";
|
|
2
|
+
export interface SealedCommit<TAction> {
|
|
3
|
+
playerId: string;
|
|
4
|
+
action: TAction;
|
|
5
|
+
sealedAt: number;
|
|
6
|
+
}
|
|
7
|
+
export interface CommitRoundConfig {
|
|
8
|
+
participants: readonly string[];
|
|
9
|
+
allowReseal?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export type SealResult = {
|
|
12
|
+
ok: true;
|
|
13
|
+
allSealed: boolean;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
reason: "unknown_participant" | "already_sealed" | "already_revealed";
|
|
17
|
+
};
|
|
18
|
+
export declare class CommitRound<TAction> {
|
|
19
|
+
private readonly order;
|
|
20
|
+
private readonly participants;
|
|
21
|
+
private readonly allowReseal;
|
|
22
|
+
private readonly sealed;
|
|
23
|
+
private revealedFlag;
|
|
24
|
+
constructor(config: CommitRoundConfig);
|
|
25
|
+
seal(playerId: string, action: TAction, at: number): SealResult;
|
|
26
|
+
hasSealed(playerId: string): boolean;
|
|
27
|
+
sealedCount(): number;
|
|
28
|
+
pending(): string[];
|
|
29
|
+
allSealed(): boolean;
|
|
30
|
+
get phase(): CommitPhase;
|
|
31
|
+
reveal(): readonly SealedCommit<TAction>[] | null;
|
|
32
|
+
}
|
|
33
|
+
export declare function createCommitRound<TAction>(config: CommitRoundConfig): CommitRound<TAction>;
|
|
34
|
+
export declare function resolveCommits<TAction, TResult>(reveals: readonly SealedCommit<TAction>[], resolver: (ordered: readonly SealedCommit<TAction>[]) => TResult): TResult;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export class CommitRound {
|
|
2
|
+
order;
|
|
3
|
+
participants;
|
|
4
|
+
allowReseal;
|
|
5
|
+
sealed = new Map();
|
|
6
|
+
revealedFlag = false;
|
|
7
|
+
constructor(config) {
|
|
8
|
+
if (config.participants.length === 0) {
|
|
9
|
+
throw new RangeError("a commit round needs at least one participant");
|
|
10
|
+
}
|
|
11
|
+
this.order = [...config.participants];
|
|
12
|
+
this.participants = new Set(config.participants);
|
|
13
|
+
this.allowReseal = config.allowReseal ?? false;
|
|
14
|
+
}
|
|
15
|
+
seal(playerId, action, at) {
|
|
16
|
+
if (this.revealedFlag)
|
|
17
|
+
return { ok: false, reason: "already_revealed" };
|
|
18
|
+
if (!this.participants.has(playerId))
|
|
19
|
+
return { ok: false, reason: "unknown_participant" };
|
|
20
|
+
if (this.sealed.has(playerId) && !this.allowReseal) {
|
|
21
|
+
return { ok: false, reason: "already_sealed" };
|
|
22
|
+
}
|
|
23
|
+
this.sealed.set(playerId, { playerId, action, sealedAt: at });
|
|
24
|
+
return { ok: true, allSealed: this.allSealed() };
|
|
25
|
+
}
|
|
26
|
+
hasSealed(playerId) {
|
|
27
|
+
return this.sealed.has(playerId);
|
|
28
|
+
}
|
|
29
|
+
sealedCount() {
|
|
30
|
+
return this.sealed.size;
|
|
31
|
+
}
|
|
32
|
+
pending() {
|
|
33
|
+
return this.order.filter((id) => !this.sealed.has(id));
|
|
34
|
+
}
|
|
35
|
+
allSealed() {
|
|
36
|
+
return this.sealed.size === this.participants.size;
|
|
37
|
+
}
|
|
38
|
+
get phase() {
|
|
39
|
+
return this.revealedFlag ? "revealed" : "collecting";
|
|
40
|
+
}
|
|
41
|
+
reveal() {
|
|
42
|
+
if (!this.allSealed())
|
|
43
|
+
return null;
|
|
44
|
+
this.revealedFlag = true;
|
|
45
|
+
return this.order.map((id) => this.sealed.get(id));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function createCommitRound(config) {
|
|
49
|
+
return new CommitRound(config);
|
|
50
|
+
}
|
|
51
|
+
export function resolveCommits(reveals, resolver) {
|
|
52
|
+
return resolver(reveals);
|
|
53
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface VoiceParticipant {
|
|
2
|
+
userId: string;
|
|
3
|
+
streamId?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface VoiceRoute {
|
|
6
|
+
fromUserId: string;
|
|
7
|
+
channelId: string;
|
|
8
|
+
gain: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Signaling seam for voice: who is in a channel and which media stream
|
|
12
|
+
* descriptor they published. The media plane (WebRTC, SFU, or anything else
|
|
13
|
+
* that moves audio bytes) stays behind this seam, host-supplied — the engine
|
|
14
|
+
* never touches it. subscribers delivers the channel roster on every change,
|
|
15
|
+
* starting with the current roster.
|
|
16
|
+
*/
|
|
17
|
+
export interface VoiceTransport {
|
|
18
|
+
join(channelId: string, streamId?: string): Promise<void>;
|
|
19
|
+
leave(channelId: string): Promise<void>;
|
|
20
|
+
publish(channelId: string, streamId: string): Promise<void>;
|
|
21
|
+
subscribers(channelId: string, onChange: (participants: readonly VoiceParticipant[]) => void): () => void;
|
|
22
|
+
}
|
|
23
|
+
export declare function createLocalVoiceTransport(options?: {
|
|
24
|
+
userId?: string;
|
|
25
|
+
}): {
|
|
26
|
+
transport: VoiceTransport;
|
|
27
|
+
participants(channelId: string): readonly VoiceParticipant[];
|
|
28
|
+
};
|
|
29
|
+
export type PushToTalkMode = "hold" | "toggle" | "openMic";
|
|
30
|
+
export type PushToTalkStatus = "idle" | "keyed" | "open";
|
|
31
|
+
export interface PushToTalk {
|
|
32
|
+
mode(): PushToTalkMode;
|
|
33
|
+
setMode(mode: PushToTalkMode): void;
|
|
34
|
+
keyDown(): void;
|
|
35
|
+
keyUp(): void;
|
|
36
|
+
muted(): boolean;
|
|
37
|
+
setMuted(muted: boolean): void;
|
|
38
|
+
status(): PushToTalkStatus;
|
|
39
|
+
transmitting(): boolean;
|
|
40
|
+
}
|
|
41
|
+
export declare function createPushToTalk(config?: {
|
|
42
|
+
mode?: PushToTalkMode;
|
|
43
|
+
onChange?: (transmitting: boolean) => void;
|
|
44
|
+
}): PushToTalk;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export function createLocalVoiceTransport(options) {
|
|
2
|
+
const userId = options?.userId ?? "local";
|
|
3
|
+
const rosters = new Map();
|
|
4
|
+
const listeners = new Map();
|
|
5
|
+
function roster(channelId) {
|
|
6
|
+
let entries = rosters.get(channelId);
|
|
7
|
+
if (entries === undefined) {
|
|
8
|
+
entries = new Map();
|
|
9
|
+
rosters.set(channelId, entries);
|
|
10
|
+
}
|
|
11
|
+
return entries;
|
|
12
|
+
}
|
|
13
|
+
function snapshot(channelId) {
|
|
14
|
+
return [...roster(channelId).values()].map((participant) => ({ ...participant }));
|
|
15
|
+
}
|
|
16
|
+
function notify(channelId) {
|
|
17
|
+
const current = snapshot(channelId);
|
|
18
|
+
for (const listener of listeners.get(channelId) ?? [])
|
|
19
|
+
listener(current);
|
|
20
|
+
}
|
|
21
|
+
const transport = {
|
|
22
|
+
join(channelId, streamId) {
|
|
23
|
+
const participant = { userId };
|
|
24
|
+
if (streamId !== undefined)
|
|
25
|
+
participant.streamId = streamId;
|
|
26
|
+
roster(channelId).set(userId, participant);
|
|
27
|
+
notify(channelId);
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
},
|
|
30
|
+
leave(channelId) {
|
|
31
|
+
if (roster(channelId).delete(userId))
|
|
32
|
+
notify(channelId);
|
|
33
|
+
return Promise.resolve();
|
|
34
|
+
},
|
|
35
|
+
publish(channelId, streamId) {
|
|
36
|
+
const participant = roster(channelId).get(userId);
|
|
37
|
+
if (participant !== undefined) {
|
|
38
|
+
participant.streamId = streamId;
|
|
39
|
+
notify(channelId);
|
|
40
|
+
}
|
|
41
|
+
return Promise.resolve();
|
|
42
|
+
},
|
|
43
|
+
subscribers(channelId, onChange) {
|
|
44
|
+
let set = listeners.get(channelId);
|
|
45
|
+
if (set === undefined) {
|
|
46
|
+
set = new Set();
|
|
47
|
+
listeners.set(channelId, set);
|
|
48
|
+
}
|
|
49
|
+
set.add(onChange);
|
|
50
|
+
onChange(snapshot(channelId));
|
|
51
|
+
return () => {
|
|
52
|
+
set.delete(onChange);
|
|
53
|
+
if (set.size === 0)
|
|
54
|
+
listeners.delete(channelId);
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
return { transport, participants: snapshot };
|
|
59
|
+
}
|
|
60
|
+
export function createPushToTalk(config) {
|
|
61
|
+
let mode = config?.mode ?? "hold";
|
|
62
|
+
let keyed = false;
|
|
63
|
+
let muted = false;
|
|
64
|
+
let lastTransmitting = false;
|
|
65
|
+
function transmitting() {
|
|
66
|
+
if (muted)
|
|
67
|
+
return false;
|
|
68
|
+
return mode === "openMic" ? true : keyed;
|
|
69
|
+
}
|
|
70
|
+
function emit() {
|
|
71
|
+
const current = transmitting();
|
|
72
|
+
if (current === lastTransmitting)
|
|
73
|
+
return;
|
|
74
|
+
lastTransmitting = current;
|
|
75
|
+
config?.onChange?.(current);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
mode: () => mode,
|
|
79
|
+
setMode(next) {
|
|
80
|
+
mode = next;
|
|
81
|
+
keyed = false;
|
|
82
|
+
emit();
|
|
83
|
+
},
|
|
84
|
+
keyDown() {
|
|
85
|
+
if (mode === "hold")
|
|
86
|
+
keyed = true;
|
|
87
|
+
else if (mode === "toggle")
|
|
88
|
+
keyed = !keyed;
|
|
89
|
+
emit();
|
|
90
|
+
},
|
|
91
|
+
keyUp() {
|
|
92
|
+
if (mode === "hold")
|
|
93
|
+
keyed = false;
|
|
94
|
+
emit();
|
|
95
|
+
},
|
|
96
|
+
muted: () => muted,
|
|
97
|
+
setMuted(next) {
|
|
98
|
+
muted = next;
|
|
99
|
+
emit();
|
|
100
|
+
},
|
|
101
|
+
status() {
|
|
102
|
+
if (mode === "openMic")
|
|
103
|
+
return "open";
|
|
104
|
+
return keyed ? "keyed" : "idle";
|
|
105
|
+
},
|
|
106
|
+
transmitting,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EntityPosition } from "../scene/entityStore.js";
|
|
2
|
+
import type { NavGrid, NavPoint } from "./navGrid.js";
|
|
3
|
+
export interface NavConstrainProposed {
|
|
4
|
+
position: EntityPosition;
|
|
5
|
+
rotationY: number;
|
|
6
|
+
grounded: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface NavConstrainEntity {
|
|
9
|
+
position: EntityPosition;
|
|
10
|
+
}
|
|
11
|
+
export interface NavConstrainOptions {
|
|
12
|
+
y?: (point: NavPoint) => number;
|
|
13
|
+
}
|
|
14
|
+
export declare function constrainToNavGrid(grid: NavGrid, options?: NavConstrainOptions): (proposed: NavConstrainProposed, entity: NavConstrainEntity) => NavConstrainProposed | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function withYRemap(proposed, options) {
|
|
2
|
+
if (options?.y === undefined)
|
|
3
|
+
return proposed;
|
|
4
|
+
const y = options.y([proposed.position[0], proposed.position[2]]);
|
|
5
|
+
return { ...proposed, position: [proposed.position[0], y, proposed.position[2]] };
|
|
6
|
+
}
|
|
7
|
+
function walkable(grid, point) {
|
|
8
|
+
const cell = grid.cellAt(point);
|
|
9
|
+
return grid.isWalkable(cell.col, cell.row);
|
|
10
|
+
}
|
|
11
|
+
export function constrainToNavGrid(grid, options) {
|
|
12
|
+
return (proposed, entity) => {
|
|
13
|
+
const target = [proposed.position[0], proposed.position[2]];
|
|
14
|
+
if (walkable(grid, target))
|
|
15
|
+
return withYRemap(proposed, options);
|
|
16
|
+
const slideX = [entity.position[0], proposed.position[2]];
|
|
17
|
+
if (walkable(grid, slideX)) {
|
|
18
|
+
return withYRemap({ ...proposed, position: [entity.position[0], proposed.position[1], proposed.position[2]] }, options);
|
|
19
|
+
}
|
|
20
|
+
const slideZ = [proposed.position[0], entity.position[2]];
|
|
21
|
+
if (walkable(grid, slideZ)) {
|
|
22
|
+
return withYRemap({ ...proposed, position: [proposed.position[0], proposed.position[1], entity.position[2]] }, options);
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EnvironmentWorldFeature } from "../world/features.js";
|
|
2
|
+
import type { Aabb } from "../world/geometry.js";
|
|
3
|
+
export interface NavObstacleGrid {
|
|
4
|
+
blockAabb(aabb: Aabb): void;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Expands every structure descriptor on an environment world feature into its
|
|
8
|
+
* generated buildings and blocks their footprints on `grid`. Returns the
|
|
9
|
+
* number of buildings blocked.
|
|
10
|
+
*/
|
|
11
|
+
export declare function populateNavGridFromEnvironment(grid: NavObstacleGrid, world: EnvironmentWorldFeature): number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { resolveStructureBuildings } from "../world/environmentSummary.js";
|
|
2
|
+
/**
|
|
3
|
+
* Expands every structure descriptor on an environment world feature into its
|
|
4
|
+
* generated buildings and blocks their footprints on `grid`. Returns the
|
|
5
|
+
* number of buildings blocked.
|
|
6
|
+
*/
|
|
7
|
+
export function populateNavGridFromEnvironment(grid, world) {
|
|
8
|
+
let blocked = 0;
|
|
9
|
+
for (const descriptor of world.structures ?? []) {
|
|
10
|
+
for (const building of resolveStructureBuildings(descriptor)) {
|
|
11
|
+
grid.blockAabb(building.bounds);
|
|
12
|
+
blocked += 1;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return blocked;
|
|
16
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Aabb } from "../world/geometry.js";
|
|
2
|
+
export type NavPoint = readonly [number, number];
|
|
3
|
+
export interface NavGridConfig {
|
|
4
|
+
bounds: Aabb;
|
|
5
|
+
cellSize: number;
|
|
6
|
+
/** Allow 8-neighbour movement (no corner cutting). Default true. */
|
|
7
|
+
diagonal?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface NavCell {
|
|
10
|
+
col: number;
|
|
11
|
+
row: number;
|
|
12
|
+
}
|
|
13
|
+
export interface FindPathOptions {
|
|
14
|
+
/** String-pull the raw grid path into straight runs where line-of-sight is clear. Default true. */
|
|
15
|
+
smooth?: boolean;
|
|
16
|
+
/** Diameter of the mover in world units — blocks cells within this margin of an obstacle. Default 0. */
|
|
17
|
+
clearance?: number;
|
|
18
|
+
/** Multiplier (>=1) applied to the base cost of stepping from one cell center to a neighbour's. Use to penalize slopes, terrain, etc. */
|
|
19
|
+
stepCost?: (from: NavPoint, to: NavPoint) => number;
|
|
20
|
+
}
|
|
21
|
+
export interface NavGrid {
|
|
22
|
+
readonly cols: number;
|
|
23
|
+
readonly rows: number;
|
|
24
|
+
readonly cellSize: number;
|
|
25
|
+
readonly bounds: Aabb;
|
|
26
|
+
readonly diagonal: boolean;
|
|
27
|
+
isWalkable(col: number, row: number): boolean;
|
|
28
|
+
setWalkable(col: number, row: number, walkable: boolean): void;
|
|
29
|
+
blockAabb(aabb: Aabb): void;
|
|
30
|
+
clearAabb(aabb: Aabb): void;
|
|
31
|
+
reset(walkable: boolean): void;
|
|
32
|
+
inBounds(col: number, row: number): boolean;
|
|
33
|
+
cellAt(point: NavPoint): NavCell;
|
|
34
|
+
center(col: number, row: number): NavPoint;
|
|
35
|
+
/** Nearest walkable cell to a point (spiral search); null if the whole grid is blocked. */
|
|
36
|
+
nearestWalkable(point: NavPoint): NavCell | null;
|
|
37
|
+
/** Unobstructed straight line between two world points (supercover cell walk). */
|
|
38
|
+
lineOfSight(from: NavPoint, to: NavPoint): boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function createNavGrid(config: NavGridConfig): NavGrid;
|
|
41
|
+
/**
|
|
42
|
+
* A* over the walkable grid. Returns a polyline of world-space `[x, z]` waypoints
|
|
43
|
+
* from `from` to `to`, or `null` when no route exists. Blocked start/goal snap to
|
|
44
|
+
* the nearest walkable cell so a click on an obstacle still routes to its edge.
|
|
45
|
+
*/
|
|
46
|
+
export declare function findPath(grid: NavGrid, from: NavPoint, to: NavPoint, options?: FindPathOptions): NavPoint[] | null;
|
|
47
|
+
/**
|
|
48
|
+
* `FindPathOptions.stepCost` factory that penalizes steep terrain: cost is
|
|
49
|
+
* `1 + weight * |Δheight| / horizontalDistance`, so with the default weight a
|
|
50
|
+
* 45° slope roughly doubles the step cost.
|
|
51
|
+
*/
|
|
52
|
+
export declare function slopeStepCost(field: {
|
|
53
|
+
sampleHeight(x: number, z: number): number;
|
|
54
|
+
}, weight?: number): (from: NavPoint, to: NavPoint) => number;
|
|
55
|
+
/** Remove waypoints the mover can skip because it has clear line-of-sight past them. */
|
|
56
|
+
export declare function smoothPath(grid: NavGrid, points: readonly NavPoint[]): NavPoint[];
|