@jgengine/core 0.14.0 → 0.15.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 +500 -1
- package/dist/ai/difficulty.d.ts +154 -0
- package/dist/ai/difficulty.js +194 -0
- package/dist/ai/driver.d.ts +107 -0
- package/dist/ai/driver.js +175 -0
- package/dist/ai/flock.js +13 -23
- package/dist/ai/laneSelect.js +2 -1
- package/dist/ai/mobBrain.js +2 -1
- package/dist/ai/waveRunner.d.ts +81 -0
- package/dist/ai/waveRunner.js +104 -0
- package/dist/audio/musicState.d.ts +47 -0
- package/dist/audio/musicState.js +38 -0
- package/dist/audio/stepCadence.d.ts +47 -0
- package/dist/audio/stepCadence.js +29 -0
- package/dist/audio/synth.d.ts +12 -0
- package/dist/authoring.d.ts +6 -4
- package/dist/authoring.js +6 -4
- package/dist/combat/castRunner.d.ts +13 -1
- package/dist/combat/castRunner.js +5 -1
- package/dist/combat/comboMeter.d.ts +140 -0
- package/dist/combat/comboMeter.js +215 -0
- package/dist/combat/comboPoints.d.ts +11 -0
- package/dist/combat/comboPoints.js +11 -2
- package/dist/combat/damageResolution.d.ts +1 -1
- package/dist/combat/defensiveWindow.d.ts +5 -0
- package/dist/combat/effects.d.ts +24 -0
- package/dist/combat/effects.js +6 -1
- package/dist/combat/projectiles.d.ts +6 -0
- package/dist/combat/projectiles.js +2 -2
- package/dist/combat/regenShield.d.ts +40 -2
- package/dist/combat/regenShield.js +66 -18
- package/dist/combat/resourcePool.d.ts +19 -0
- package/dist/combat/resourcePool.js +20 -2
- package/dist/combat/shotOrigin.d.ts +5 -0
- package/dist/combat/statusApplication.d.ts +1 -1
- package/dist/combat/statusApplication.js +2 -1
- package/dist/combat/statusEffectView.d.ts +56 -0
- package/dist/combat/statusEffectView.js +52 -0
- package/dist/combat.d.ts +4 -2
- package/dist/combat.js +2 -0
- package/dist/devtools/fallbackSeams.d.ts +3 -2
- package/dist/devtools/fallbackSeams.js +1 -1
- package/dist/devtools/textureErrors.d.ts +40 -0
- package/dist/devtools/textureErrors.js +68 -0
- package/dist/economy/shopStock.d.ts +110 -0
- package/dist/economy/shopStock.js +157 -0
- package/dist/economy/wallet.d.ts +6 -0
- package/dist/economy/wallet.js +1 -0
- package/dist/game/achievements.d.ts +80 -0
- package/dist/game/achievements.js +124 -0
- package/dist/game/cameraConfig.d.ts +7 -0
- package/dist/game/codex.d.ts +60 -0
- package/dist/game/codex.js +89 -0
- package/dist/game/credits.d.ts +36 -0
- package/dist/game/credits.js +0 -0
- package/dist/game/defineGame.d.ts +28 -3
- package/dist/game/defineGame.js +5 -0
- package/dist/game/dialogueGraph.d.ts +116 -0
- package/dist/game/dialogueGraph.js +103 -0
- package/dist/game/eventTicker.d.ts +95 -0
- package/dist/game/eventTicker.js +91 -0
- package/dist/game/events.d.ts +24 -5
- package/dist/game/events.js +18 -0
- package/dist/game/gamePhase.d.ts +1 -1
- package/dist/game/gamePhase.js +1 -1
- package/dist/game/leaderboardRank.d.ts +71 -0
- package/dist/game/leaderboardRank.js +76 -0
- package/dist/game/lootTable.d.ts +10 -1
- package/dist/game/lootTable.js +4 -2
- package/dist/game/notifications.d.ts +57 -0
- package/dist/game/notifications.js +107 -0
- package/dist/game/playableGame.d.ts +39 -2
- package/dist/game/quest.d.ts +26 -0
- package/dist/game/quest.js +31 -0
- package/dist/game/runDraft.js +2 -1
- package/dist/game/saveSlots.d.ts +102 -0
- package/dist/game/saveSlots.js +136 -0
- package/dist/game/talentTreeView.d.ts +106 -0
- package/dist/game/talentTreeView.js +114 -0
- package/dist/game/worldItem.js +2 -1
- package/dist/gameplay.d.ts +15 -4
- package/dist/gameplay.js +13 -2
- package/dist/i18n/i18n.d.ts +51 -0
- package/dist/i18n/i18n.js +82 -0
- package/dist/input/axisInput.d.ts +1 -1
- package/dist/input/axisInput.js +18 -2
- package/dist/input/rebindSession.d.ts +141 -0
- package/dist/input/rebindSession.js +235 -0
- package/dist/input/touchControlsMode.d.ts +16 -0
- package/dist/input/touchControlsMode.js +23 -0
- package/dist/input/touchScheme.d.ts +30 -1
- package/dist/input/touchScheme.js +31 -1
- package/dist/interaction/promptRegistry.d.ts +78 -0
- package/dist/interaction/promptRegistry.js +130 -0
- package/dist/inventory/storageTier.js +3 -2
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +145 -1
- package/dist/movement/movementModel.d.ts +26 -3
- package/dist/movement/movementModel.js +121 -15
- package/dist/movement/playerMovement.js +161 -25
- package/dist/physics/drivableVehicle.js +3 -1
- package/dist/physics/kinematicVehicle.d.ts +56 -1
- package/dist/physics/kinematicVehicle.js +99 -10
- package/dist/physics/vehicleObstacles.js +19 -0
- package/dist/random/resolveRng.d.ts +14 -0
- package/dist/random/resolveRng.js +31 -0
- package/dist/render/postProcessing.d.ts +12 -1
- package/dist/runtime/cameraDirector.d.ts +2 -2
- package/dist/runtime/context/combat.js +8 -0
- package/dist/runtime/gameContext.js +6 -1
- package/dist/runtime/gameContextTypes.d.ts +18 -0
- package/dist/runtime/inputSnapshot.d.ts +10 -1
- package/dist/runtime/inputSnapshot.js +13 -1
- package/dist/runtime/runtimeSave.d.ts +3 -3
- package/dist/runtime/runtimeSave.js +11 -2
- package/dist/scene/assetDiagnostics.js +2 -2
- package/dist/scene/autoTarget.js +2 -1
- package/dist/scene/captureCheck.js +3 -2
- package/dist/scene/entityStore.d.ts +4 -1
- package/dist/scene/entityStore.js +3 -0
- package/dist/scene/objectStore.d.ts +10 -0
- package/dist/scene/objectStore.js +29 -6
- package/dist/scene/sceneKinds.d.ts +18 -0
- package/dist/scene/sceneKinds.js +19 -0
- package/dist/scene/sequenceDirector.d.ts +110 -0
- package/dist/scene/sequenceDirector.js +146 -0
- package/dist/session/roles.js +3 -2
- package/dist/settings/settingsModel.d.ts +1 -0
- package/dist/settings/settingsModel.js +1 -0
- package/dist/stats/rollCheck.js +4 -2
- package/dist/time/dayNightCycle.d.ts +114 -0
- package/dist/time/dayNightCycle.js +208 -0
- package/dist/time/timerSet.d.ts +132 -0
- package/dist/time/timerSet.js +243 -0
- package/dist/ui/accessibility.d.ts +51 -0
- package/dist/ui/accessibility.js +83 -0
- package/dist/ui/coachMarks.d.ts +99 -0
- package/dist/ui/coachMarks.js +126 -0
- package/dist/ui/floatingText.d.ts +114 -0
- package/dist/ui/floatingText.js +203 -0
- package/dist/ui/modalStack.d.ts +143 -0
- package/dist/ui/modalStack.js +160 -0
- package/dist/ui/objectiveBanner.d.ts +123 -0
- package/dist/ui/objectiveBanner.js +162 -0
- package/dist/ui/photoMode.d.ts +32 -0
- package/dist/ui/photoMode.js +51 -0
- package/dist/ui/radialMenu.d.ts +47 -0
- package/dist/ui/radialMenu.js +74 -0
- package/dist/ui/screenMarkers.d.ts +128 -0
- package/dist/ui/screenMarkers.js +118 -0
- package/dist/ui.d.ts +8 -0
- package/dist/ui.js +8 -0
- package/dist/vfx/cameraShake.d.ts +97 -0
- package/dist/vfx/cameraShake.js +136 -0
- package/dist/vfx/damageDirection.d.ts +102 -0
- package/dist/vfx/damageDirection.js +188 -0
- package/dist/vfx/particles.d.ts +103 -0
- package/dist/vfx/particles.js +256 -0
- package/dist/vfx/screenEffects.d.ts +149 -0
- package/dist/vfx/screenEffects.js +191 -0
- package/dist/world/authoredObjects.d.ts +18 -0
- package/dist/world/authoredObjects.js +21 -0
- package/dist/world/authoredSpawn.js +19 -0
- package/dist/world/buildingLots.d.ts +54 -11
- package/dist/world/buildingLots.js +116 -30
- package/dist/world/cityBlocks.d.ts +45 -0
- package/dist/world/cityBlocks.js +159 -0
- package/dist/world/cityContent.d.ts +46 -2
- package/dist/world/cityContent.js +160 -2
- package/dist/world/cityGenerator.d.ts +261 -23
- package/dist/world/cityGenerator.js +643 -33
- package/dist/world/cityGeometry.d.ts +20 -0
- package/dist/world/cityGeometry.js +91 -0
- package/dist/world/cityKind.js +1 -44
- package/dist/world/fastTravel.d.ts +64 -0
- package/dist/world/fastTravel.js +108 -0
- package/dist/world/features.d.ts +16 -0
- package/dist/world/features.js +2 -0
- package/dist/world/grounded.d.ts +11 -0
- package/dist/world/grounded.js +12 -0
- package/dist/world/mapAnnotations.d.ts +75 -0
- package/dist/world/mapAnnotations.js +139 -0
- package/dist/world/markers.js +1 -0
- package/dist/world/resourceNode.d.ts +1 -1
- package/dist/world/resourceNode.js +2 -1
- package/dist/world/roads.d.ts +294 -5
- package/dist/world/roads.js +1262 -21
- package/dist/world/sharedAnnotations.d.ts +84 -0
- package/dist/world/sharedAnnotations.js +76 -0
- package/dist/world/spawnOverride.d.ts +37 -0
- package/dist/world/spawnOverride.js +50 -0
- package/dist/world/streetGenerator.d.ts +60 -11
- package/dist/world/streetGenerator.js +1786 -129
- package/dist/world/terrain.d.ts +1 -0
- package/dist/world/terrain.js +2 -0
- package/dist/world/waypoints.d.ts +63 -0
- package/dist/world/waypoints.js +137 -0
- package/dist/world.d.ts +17 -2
- package/dist/world.js +17 -2
- package/package.json +1 -1
- package/skills/ce-handoff/SKILL.md +149 -0
- package/skills/game-design/SKILL.md +2 -0
- package/skills/jgengine/SKILL.md +3 -1
- package/skills/jgengine/api.md +17 -7
- package/skills/jgengine/capabilities.md +12 -0
- package/skills/jgengine/recipes/minimal-game.md +7 -4
- package/skills/jgengine-combat/api.md +40 -4
- package/skills/jgengine-combat/capabilities.md +12 -0
- package/skills/jgengine-combat/reference.md +2 -0
- package/skills/jgengine-gameplay/api.md +212 -11
- package/skills/jgengine-gameplay/capabilities.md +66 -1
- package/skills/jgengine-world/api.md +397 -11
- package/skills/jgengine-world/capabilities.md +123 -2
- package/skills/jgengine-world/reference.md +15 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { AnnotationLayer } from "./mapAnnotations.js";
|
|
2
|
+
import type { MapLayerTone, MapZoneShape } from "./mapLayers.js";
|
|
3
|
+
import type { WorldXZ } from "./minimap.js";
|
|
4
|
+
/** Feed action shared drawings ride by default (pair with the party feed, like pings). */
|
|
5
|
+
export declare const ANNOTATION_FEED_ACTION = "map.annotation";
|
|
6
|
+
/** A serializable map-annotation edit broadcast to other players. */
|
|
7
|
+
export type AnnotationBroadcast = {
|
|
8
|
+
op: "stroke";
|
|
9
|
+
from: string;
|
|
10
|
+
id: string;
|
|
11
|
+
points: readonly WorldXZ[];
|
|
12
|
+
tone?: MapLayerTone;
|
|
13
|
+
width?: number;
|
|
14
|
+
} | {
|
|
15
|
+
op: "shape";
|
|
16
|
+
from: string;
|
|
17
|
+
id: string;
|
|
18
|
+
shape: MapZoneShape;
|
|
19
|
+
tone?: MapLayerTone;
|
|
20
|
+
label?: string;
|
|
21
|
+
} | {
|
|
22
|
+
op: "note";
|
|
23
|
+
from: string;
|
|
24
|
+
id: string;
|
|
25
|
+
position: WorldXZ;
|
|
26
|
+
text: string;
|
|
27
|
+
tone?: MapLayerTone;
|
|
28
|
+
} | {
|
|
29
|
+
op: "remove";
|
|
30
|
+
from: string;
|
|
31
|
+
id: string;
|
|
32
|
+
} | {
|
|
33
|
+
op: "clear";
|
|
34
|
+
from: string;
|
|
35
|
+
};
|
|
36
|
+
/** The feed sink shared annotations push to — an alias of the ping/feed `push` seam. */
|
|
37
|
+
export interface AnnotationFeedSink {
|
|
38
|
+
push(action: string, entry: unknown): void;
|
|
39
|
+
}
|
|
40
|
+
/** Construction options for {@link createSharedAnnotations}. */
|
|
41
|
+
export interface SharedAnnotationsDeps {
|
|
42
|
+
/** The local annotation layer edits apply to (and inbound edits mirror into). */
|
|
43
|
+
layer: AnnotationLayer;
|
|
44
|
+
/** Feed to broadcast local edits on (typically `ctx.game.feed`). */
|
|
45
|
+
feed: AnnotationFeedSink;
|
|
46
|
+
/** This player's id — stamped on broadcasts and used to drop our own echoes. */
|
|
47
|
+
from: string;
|
|
48
|
+
/** Feed action to publish/consume. Default {@link ANNOTATION_FEED_ACTION}. */
|
|
49
|
+
action?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Local annotation edits that also broadcast, plus `apply` for inbound edits.
|
|
53
|
+
* `add*`/`remove`/`clear` mirror {@link AnnotationLayer} but return globally-unique
|
|
54
|
+
* ids so two clients never collide.
|
|
55
|
+
*/
|
|
56
|
+
export interface SharedAnnotations {
|
|
57
|
+
addStroke(points: readonly WorldXZ[], options?: {
|
|
58
|
+
tone?: MapLayerTone;
|
|
59
|
+
width?: number;
|
|
60
|
+
}): string;
|
|
61
|
+
addShape(shape: MapZoneShape, options?: {
|
|
62
|
+
tone?: MapLayerTone;
|
|
63
|
+
label?: string;
|
|
64
|
+
}): string;
|
|
65
|
+
addNote(position: WorldXZ, text: string, options?: {
|
|
66
|
+
tone?: MapLayerTone;
|
|
67
|
+
}): string;
|
|
68
|
+
remove(id: string): boolean;
|
|
69
|
+
clear(): void;
|
|
70
|
+
/** Mirror an inbound broadcast from another player; own echoes (matching `from`) are ignored. */
|
|
71
|
+
apply(payload: AnnotationBroadcast): void;
|
|
72
|
+
feedAction: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Make a map annotation layer collaborative: local `addStroke`/`addShape`/
|
|
76
|
+
* `addNote`/`remove`/`clear` apply then broadcast a serializable edit over the
|
|
77
|
+
* party feed (the same seam `createPingSystem` uses), and `apply` mirrors inbound
|
|
78
|
+
* edits from other players — dropping our own echoes. Ids are globally unique per
|
|
79
|
+
* client so two players' strokes never collide. Transport-agnostic: wire `feed`
|
|
80
|
+
* to any replicated feed and route received entries into `apply`.
|
|
81
|
+
*
|
|
82
|
+
* @capability shared-annotations broadcast local map-drawing edits over the party feed and mirror inbound edits — collaborative annotations over any replicated feed
|
|
83
|
+
*/
|
|
84
|
+
export declare function createSharedAnnotations(deps: SharedAnnotationsDeps): SharedAnnotations;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Feed action shared drawings ride by default (pair with the party feed, like pings). */
|
|
2
|
+
export const ANNOTATION_FEED_ACTION = "map.annotation";
|
|
3
|
+
/**
|
|
4
|
+
* Make a map annotation layer collaborative: local `addStroke`/`addShape`/
|
|
5
|
+
* `addNote`/`remove`/`clear` apply then broadcast a serializable edit over the
|
|
6
|
+
* party feed (the same seam `createPingSystem` uses), and `apply` mirrors inbound
|
|
7
|
+
* edits from other players — dropping our own echoes. Ids are globally unique per
|
|
8
|
+
* client so two players' strokes never collide. Transport-agnostic: wire `feed`
|
|
9
|
+
* to any replicated feed and route received entries into `apply`.
|
|
10
|
+
*
|
|
11
|
+
* @capability shared-annotations broadcast local map-drawing edits over the party feed and mirror inbound edits — collaborative annotations over any replicated feed
|
|
12
|
+
*/
|
|
13
|
+
export function createSharedAnnotations(deps) {
|
|
14
|
+
const { layer, feed, from } = deps;
|
|
15
|
+
const action = deps.action ?? ANNOTATION_FEED_ACTION;
|
|
16
|
+
let counter = 0;
|
|
17
|
+
function nextId() {
|
|
18
|
+
counter += 1;
|
|
19
|
+
return `${from}:${counter}`;
|
|
20
|
+
}
|
|
21
|
+
function broadcast(payload) {
|
|
22
|
+
feed.push(action, payload);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
feedAction: action,
|
|
26
|
+
addStroke(points, options = {}) {
|
|
27
|
+
const id = nextId();
|
|
28
|
+
layer.addStroke(points, { id, ...options });
|
|
29
|
+
broadcast({ op: "stroke", from, id, points: points.map((p) => [p[0], p[1]]), ...options });
|
|
30
|
+
return id;
|
|
31
|
+
},
|
|
32
|
+
addShape(shape, options = {}) {
|
|
33
|
+
const id = nextId();
|
|
34
|
+
layer.addShape(shape, { id, ...options });
|
|
35
|
+
broadcast({ op: "shape", from, id, shape, ...options });
|
|
36
|
+
return id;
|
|
37
|
+
},
|
|
38
|
+
addNote(position, text, options = {}) {
|
|
39
|
+
const id = nextId();
|
|
40
|
+
layer.addNote(position, text, { id, ...options });
|
|
41
|
+
broadcast({ op: "note", from, id, position: [position[0], position[1]], text, ...options });
|
|
42
|
+
return id;
|
|
43
|
+
},
|
|
44
|
+
remove(id) {
|
|
45
|
+
const existed = layer.remove(id);
|
|
46
|
+
if (existed)
|
|
47
|
+
broadcast({ op: "remove", from, id });
|
|
48
|
+
return existed;
|
|
49
|
+
},
|
|
50
|
+
clear() {
|
|
51
|
+
layer.clear();
|
|
52
|
+
broadcast({ op: "clear", from });
|
|
53
|
+
},
|
|
54
|
+
apply(payload) {
|
|
55
|
+
if (payload.from === from)
|
|
56
|
+
return; // ignore our own echo
|
|
57
|
+
switch (payload.op) {
|
|
58
|
+
case "stroke":
|
|
59
|
+
layer.addStroke(payload.points, { id: payload.id, tone: payload.tone, width: payload.width });
|
|
60
|
+
return;
|
|
61
|
+
case "shape":
|
|
62
|
+
layer.addShape(payload.shape, { id: payload.id, tone: payload.tone, label: payload.label });
|
|
63
|
+
return;
|
|
64
|
+
case "note":
|
|
65
|
+
layer.addNote(payload.position, payload.text, { id: payload.id, tone: payload.tone });
|
|
66
|
+
return;
|
|
67
|
+
case "remove":
|
|
68
|
+
layer.remove(payload.id);
|
|
69
|
+
return;
|
|
70
|
+
case "clear":
|
|
71
|
+
layer.clear();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-session player-spawn override — a capture-harness ergonomics seam that lets a single
|
|
3
|
+
* screenshot or drive run place the player somewhere other than the authored `player_spawn`
|
|
4
|
+
* marker *without mutating `editor.scene.json`*. The runner installs one from the `?spawn=` URL
|
|
5
|
+
* overlay (mirroring `?cam=`); {@link authoredSpawnPosition} consults it for the default
|
|
6
|
+
* player-spawn resolution only, so authored content is untouched and the override evaporates the
|
|
7
|
+
* moment the page reloads without the param.
|
|
8
|
+
*
|
|
9
|
+
* Genre-agnostic: it moves whatever a game treats as its player spawn. It never changes markers of
|
|
10
|
+
* other kinds or an explicit marker `id`, so an override taken for a close-up cannot perturb enemy
|
|
11
|
+
* or prop placement queries that share {@link authoredSpawnPosition}.
|
|
12
|
+
*
|
|
13
|
+
* Install-style module state is the established runner pattern here (`installAssetBase`,
|
|
14
|
+
* `installEditorHost`, `installSaveEndpoint`); the default is `null`, so nothing changes and every
|
|
15
|
+
* pure query stays deterministic until a runner explicitly installs an override.
|
|
16
|
+
*/
|
|
17
|
+
/** A spawn point that replaces the authored player spawn for one capture session. */
|
|
18
|
+
export interface SpawnOverride {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
z: number;
|
|
22
|
+
/** Optional facing (yaw radians); when omitted the authored/`0` rotation is kept. */
|
|
23
|
+
rotationY?: number;
|
|
24
|
+
}
|
|
25
|
+
/** Install (or, with `null`, clear) the active player-spawn override. Idempotent; last write wins. */
|
|
26
|
+
export declare function installSpawnOverride(override: SpawnOverride | null): void;
|
|
27
|
+
/** Clear any installed player-spawn override. */
|
|
28
|
+
export declare function clearSpawnOverride(): void;
|
|
29
|
+
/** The currently installed player-spawn override, or `null` when none is active. */
|
|
30
|
+
export declare function readSpawnOverride(): SpawnOverride | null;
|
|
31
|
+
/**
|
|
32
|
+
* Parse a `?spawn=` / `--spawn` value into a {@link SpawnOverride}, or `null` when it is absent or
|
|
33
|
+
* malformed. Accepts `"x,y,z"` and `"x,y,z,yaw"` (yaw in radians); whitespace around components is
|
|
34
|
+
* tolerated. A non-finite or short tuple yields `null` so a typo never silently teleports the
|
|
35
|
+
* player to `NaN`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseSpawnOverride(raw: string | null | undefined): SpawnOverride | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-session player-spawn override — a capture-harness ergonomics seam that lets a single
|
|
3
|
+
* screenshot or drive run place the player somewhere other than the authored `player_spawn`
|
|
4
|
+
* marker *without mutating `editor.scene.json`*. The runner installs one from the `?spawn=` URL
|
|
5
|
+
* overlay (mirroring `?cam=`); {@link authoredSpawnPosition} consults it for the default
|
|
6
|
+
* player-spawn resolution only, so authored content is untouched and the override evaporates the
|
|
7
|
+
* moment the page reloads without the param.
|
|
8
|
+
*
|
|
9
|
+
* Genre-agnostic: it moves whatever a game treats as its player spawn. It never changes markers of
|
|
10
|
+
* other kinds or an explicit marker `id`, so an override taken for a close-up cannot perturb enemy
|
|
11
|
+
* or prop placement queries that share {@link authoredSpawnPosition}.
|
|
12
|
+
*
|
|
13
|
+
* Install-style module state is the established runner pattern here (`installAssetBase`,
|
|
14
|
+
* `installEditorHost`, `installSaveEndpoint`); the default is `null`, so nothing changes and every
|
|
15
|
+
* pure query stays deterministic until a runner explicitly installs an override.
|
|
16
|
+
*/
|
|
17
|
+
let installed = null;
|
|
18
|
+
/** Install (or, with `null`, clear) the active player-spawn override. Idempotent; last write wins. */
|
|
19
|
+
export function installSpawnOverride(override) {
|
|
20
|
+
installed = override;
|
|
21
|
+
}
|
|
22
|
+
/** Clear any installed player-spawn override. */
|
|
23
|
+
export function clearSpawnOverride() {
|
|
24
|
+
installed = null;
|
|
25
|
+
}
|
|
26
|
+
/** The currently installed player-spawn override, or `null` when none is active. */
|
|
27
|
+
export function readSpawnOverride() {
|
|
28
|
+
return installed;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Parse a `?spawn=` / `--spawn` value into a {@link SpawnOverride}, or `null` when it is absent or
|
|
32
|
+
* malformed. Accepts `"x,y,z"` and `"x,y,z,yaw"` (yaw in radians); whitespace around components is
|
|
33
|
+
* tolerated. A non-finite or short tuple yields `null` so a typo never silently teleports the
|
|
34
|
+
* player to `NaN`.
|
|
35
|
+
*/
|
|
36
|
+
export function parseSpawnOverride(raw) {
|
|
37
|
+
if (raw === null || raw === undefined)
|
|
38
|
+
return null;
|
|
39
|
+
const parts = raw
|
|
40
|
+
.split(",")
|
|
41
|
+
.map((part) => part.trim())
|
|
42
|
+
.filter((part) => part.length > 0);
|
|
43
|
+
if (parts.length < 3 || parts.length > 4)
|
|
44
|
+
return null;
|
|
45
|
+
const nums = parts.map((part) => Number(part));
|
|
46
|
+
if (nums.some((value) => !Number.isFinite(value)))
|
|
47
|
+
return null;
|
|
48
|
+
const [x, y, z, rotationY] = nums;
|
|
49
|
+
return rotationY === undefined ? { x, y, z } : { x, y, z, rotationY };
|
|
50
|
+
}
|
|
@@ -25,6 +25,10 @@ export interface StreetEdge {
|
|
|
25
25
|
level: StreetLevel;
|
|
26
26
|
/** True when this edge belongs to the main closed loop (circuit mode). */
|
|
27
27
|
loop: boolean;
|
|
28
|
+
/** Per-point road-surface height (one per `points`), present only when `elevation > 0`. Raw
|
|
29
|
+
* (uncapped) samples of {@link StreetNetwork.elevationAt}; the grade-limited profile lives on the
|
|
30
|
+
* owning {@link Street.heights}. */
|
|
31
|
+
heights?: number[];
|
|
28
32
|
}
|
|
29
33
|
/** A feature span carried by a street: a `[from, to]` index window into the street's `points`. */
|
|
30
34
|
export interface StreetFeatureSpan {
|
|
@@ -34,12 +38,19 @@ export interface StreetFeatureSpan {
|
|
|
34
38
|
/** Ground height at each bank/portal — the deck/floor reference the renderer drapes to. */
|
|
35
39
|
bankHeight: number;
|
|
36
40
|
}
|
|
41
|
+
/** Left/right pedestrian bands flanking a street's asphalt, as offset polylines. */
|
|
42
|
+
export interface StreetSidewalks {
|
|
43
|
+
/** Polyline offset to the left of travel (parallel to `points`). */
|
|
44
|
+
left: StreetVec2[];
|
|
45
|
+
/** Polyline offset to the right of travel. */
|
|
46
|
+
right: StreetVec2[];
|
|
47
|
+
}
|
|
37
48
|
/** One chained through-street: a maximal run of edges through degree-2 nodes, for rendering + furniture. */
|
|
38
49
|
export interface Street {
|
|
39
50
|
id: number;
|
|
40
51
|
/** Ordered node ids the chain visits; first === last when `loop`. */
|
|
41
52
|
nodes: number[];
|
|
42
|
-
/** Smoothed,
|
|
53
|
+
/** Smoothed, arc-filleted centerline. */
|
|
43
54
|
points: StreetVec2[];
|
|
44
55
|
width: number;
|
|
45
56
|
level: StreetLevel;
|
|
@@ -49,6 +60,12 @@ export interface Street {
|
|
|
49
60
|
bulb?: StreetVec2;
|
|
50
61
|
/** Bridge/tunnel spans along this street, if any. */
|
|
51
62
|
features: StreetFeatureSpan[];
|
|
63
|
+
/** Flanking pedestrian bands, present on boulevard/avenue/street levels (not lanes). */
|
|
64
|
+
sidewalks?: StreetSidewalks;
|
|
65
|
+
/** Per-point road-surface height (one per `points`), present only when `elevation > 0`. Grade-limited
|
|
66
|
+
* so no consecutive pair exceeds `maxGrade`; a loop stays continuous (`heights[0] === heights.at(-1)`).
|
|
67
|
+
* This is ROAD elevation, not the `context.heightAt` terrain that drives bridges/tunnels. */
|
|
68
|
+
heights?: number[];
|
|
52
69
|
}
|
|
53
70
|
/** One crossing of three or more streets: patch center/radius plus outgoing arm directions. */
|
|
54
71
|
export interface StreetJunction {
|
|
@@ -92,36 +109,60 @@ export interface StreetNetwork {
|
|
|
92
109
|
tunnels: StreetFeature[];
|
|
93
110
|
/** Independent cycle count (E − V + components): 0 = pure tree, ≥1 = has loops. */
|
|
94
111
|
loops: number;
|
|
112
|
+
/** The shared road-surface height FIELD (volume-local), present only when `elevation > 0`. Sample it
|
|
113
|
+
* to drape junction welds, sidewalks, and building bases so every surface meets the road continuously
|
|
114
|
+
* — the streets/edges sample this exact field. Distinct from the `context.heightAt` terrain sampler. */
|
|
115
|
+
elevationAt?: (x: number, z: number) => number;
|
|
95
116
|
}
|
|
96
117
|
/** Fully-defaulted slider set the generator reads. */
|
|
97
118
|
export interface StreetNetworkRules {
|
|
98
119
|
seed: string;
|
|
120
|
+
/** Organic boundary clipping amount, when supported by the caller. */
|
|
121
|
+
outline?: number;
|
|
99
122
|
/** 1 = regular lattice nodes; 0 = organic scatter. */
|
|
100
123
|
gridness: number;
|
|
101
124
|
/** 0 = tree (dead-ends); 1 = a single closed circuit. */
|
|
102
125
|
loopiness: number;
|
|
103
126
|
/** Extra chord edges between neighbors → mesh density. */
|
|
104
127
|
connectivity: number;
|
|
105
|
-
/** Spur-lane density forking off the mains. */
|
|
128
|
+
/** Spur-lane density forking off the mains (and, in a circuit, the pit-lane trigger). */
|
|
106
129
|
branching: number;
|
|
130
|
+
/** Share of branch streets that use residential side-street rules. */
|
|
131
|
+
residentialBranches?: number;
|
|
107
132
|
/** Fraction of dangling ends kept as cul-de-sacs (vs reconnected). */
|
|
108
133
|
deadEnds: number;
|
|
109
134
|
/** Target node spacing / block size, world units. */
|
|
110
135
|
segmentLength: number;
|
|
111
136
|
/** Cross-axis spacing multiplier (≥1 = long skinny Manhattan blocks). */
|
|
112
137
|
aspect: number;
|
|
113
|
-
/** Sideways wander amplitude, 0..1. */
|
|
138
|
+
/** Sideways wander amplitude, 0..1 (and circuit corner-template density). */
|
|
114
139
|
winding: number;
|
|
115
|
-
/** Minimum curve radius (m) — caps wander curvature and corner
|
|
140
|
+
/** Minimum curve radius (m) — caps wander curvature and sets the corner-fillet radius. */
|
|
116
141
|
minCurveRadius: number;
|
|
117
142
|
/** Shallowest corner (degrees) a street keeps — gentler bends are straightened. */
|
|
118
143
|
minTurnAngle: number;
|
|
119
|
-
/** Sharpest corner (degrees)
|
|
144
|
+
/** Sharpest corner (degrees) between consecutive centerline samples — sharper corners are filleted. */
|
|
120
145
|
maxTurnAngle: number;
|
|
121
146
|
/** Base street width; the hierarchy scales off it. */
|
|
122
147
|
width: number;
|
|
123
148
|
/** Share of avenues upgraded to boulevards, 0..1. */
|
|
124
149
|
boulevards: number;
|
|
150
|
+
/** Pedestrian band width paved on each side of boulevard/avenue/street levels. Default ~2. */
|
|
151
|
+
sidewalkWidth?: number;
|
|
152
|
+
/** Road-surface relief amplitude, 0..1. 0 (default) = dead flat and byte-identical to a pre-elevation
|
|
153
|
+
* network; 1 ≈ 22 m of relief across a ~520 m volume (scaled to the footprint). */
|
|
154
|
+
elevation?: number;
|
|
155
|
+
/** Maximum road slope (rise/run) the grade cap enforces on every street's height sequence. Default 0.07. */
|
|
156
|
+
maxGrade?: number;
|
|
157
|
+
/** SPACE-FILLING circuit layout dial, 0..1. 0 (default) = the hull construction (a flowing loop around
|
|
158
|
+
* an empty middle), BYTE-IDENTICAL to a pre-compactness network. Rising values switch the circuit
|
|
159
|
+
* LAYOUT stage to a grid spanning-tree CYCLE that folds back through its own interior: parallel
|
|
160
|
+
* adjacent runs one corridor pitch apart, switchback esses, consecutive hairpins, and long straights
|
|
161
|
+
* from straight tree branches, with footprint coverage rising with the dial (~45% of grid cells at
|
|
162
|
+
* 0.5, ~85% at 1). Everything downstream — spline fit, curvature floor, straight designation,
|
|
163
|
+
* self-clearance, edge distribution, elevation, pit lane — is reused unchanged. Only meaningful in
|
|
164
|
+
* circuit mode; ignored by the street net. */
|
|
165
|
+
compactness?: number;
|
|
125
166
|
}
|
|
126
167
|
/** Ground sampler + feature toggles enabling bridges/tunnels; omit for a flat, feature-free network. */
|
|
127
168
|
export interface StreetNetworkContext {
|
|
@@ -137,13 +178,21 @@ export interface StreetNetworkContext {
|
|
|
137
178
|
tunnelClearance?: number;
|
|
138
179
|
}
|
|
139
180
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
181
|
+
* Replace every real corner of a polyline with a sampled **circular-arc fillet** so bends read as
|
|
182
|
+
* smooth curves instead of polygonal notches, while enforcing two hard limits with fixed endpoints:
|
|
183
|
+
* interior corners gentler than `minRad` are straightened away (deliberate winding only, no
|
|
184
|
+
* micro-wiggle), and each remaining corner is rounded with an arc of radius `curveRadius` (clamped to
|
|
185
|
+
* half the shorter adjacent leg) sampled finely enough that no discrete turn between consecutive
|
|
186
|
+
* samples exceeds `maxRad` — a corner sharper than the ceiling is still reduced, now to a fillet
|
|
187
|
+
* rather than a bevel. Open-chain endpoints never move (a graph node stays welded); pass `closed` for
|
|
188
|
+
* a ring (first === last) so its wrap corner is filleted too and the loop stays closed.
|
|
189
|
+
*
|
|
190
|
+
* Pass `radiusFor` to vary the fillet radius PER CORNER (returning the target radius from the corner's
|
|
191
|
+
* deflection and adjacent leg lengths) instead of the single `curveRadius` — the circuit loop uses this
|
|
192
|
+
* to mix hairpins, standard corners, and sweepers; every other caller keeps the scalar radius.
|
|
144
193
|
* @internal
|
|
145
194
|
*/
|
|
146
|
-
export declare function clampTurns(points: readonly StreetVec2[], minRad: number, maxRad: number): StreetVec2[];
|
|
195
|
+
export declare function clampTurns(points: readonly StreetVec2[], minRad: number, maxRad: number, curveRadius?: number, closed?: boolean, radiusFor?: (deflection: number, la: number, lb: number) => number): StreetVec2[];
|
|
147
196
|
/** Decide which topology family the sliders call for. Circuit wins when loops dominate and both
|
|
148
197
|
* branching and mesh connectivity are low — exactly the "race track" corner of the slider space.
|
|
149
198
|
* @internal */
|
|
@@ -154,6 +203,6 @@ export declare function streetNetworkMode(rules: StreetNetworkRules): StreetNetw
|
|
|
154
203
|
* water gaps into bridges and ridges into tunnels. Coordinates are volume-local; the caller maps to
|
|
155
204
|
* world space.
|
|
156
205
|
*
|
|
157
|
-
* @capability street-generator build a deterministic street/track graph (nodes, edges, streets, junctions, bridges, tunnels) from sliders
|
|
206
|
+
* @capability street-generator build a deterministic street/track graph (nodes, edges, streets, junctions, sidewalks, bridges, tunnels) from sliders
|
|
158
207
|
*/
|
|
159
208
|
export declare function generateStreets(rules: StreetNetworkRules, hx: number, hz: number, context?: StreetNetworkContext): StreetNetwork;
|