@jgengine/core 0.8.0 → 0.10.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 +36 -0
- package/dist/ai/flock.d.ts +36 -0
- package/dist/ai/flock.js +182 -0
- package/dist/ai/groupAssist.d.ts +19 -0
- package/dist/ai/groupAssist.js +59 -0
- package/dist/ai/laneSelect.d.ts +21 -0
- package/dist/ai/laneSelect.js +37 -0
- package/dist/ai/mobBrain.d.ts +68 -0
- package/dist/ai/mobBrain.js +115 -0
- package/dist/ai/spawnDirector.d.ts +13 -0
- package/dist/ai/spawnDirector.js +41 -17
- package/dist/ai/spawnPoint.d.ts +5 -0
- package/dist/ai/spawnPoint.js +5 -0
- package/dist/ai/threat.d.ts +2 -0
- package/dist/ai/threat.js +34 -0
- package/dist/audio/audioFalloff.d.ts +5 -2
- package/dist/audio/music.d.ts +53 -0
- package/dist/audio/music.js +34 -0
- package/dist/audio/synth.d.ts +60 -0
- package/dist/audio/synth.js +8 -0
- package/dist/authoring.d.ts +14 -0
- package/dist/authoring.js +14 -0
- package/dist/cartridge/runtime.d.ts +15 -0
- package/dist/cartridge/runtime.js +556 -0
- package/dist/cartridge/spec.d.ts +188 -0
- package/dist/cartridge/spec.js +19 -0
- package/dist/cartridge/validate.d.ts +2 -0
- package/dist/cartridge/validate.js +78 -0
- package/dist/combat/abilityKit.d.ts +17 -2
- package/dist/combat/abilityKit.js +62 -13
- package/dist/combat/castRunner.d.ts +40 -0
- package/dist/combat/castRunner.js +56 -0
- package/dist/combat/comboPoints.d.ts +14 -0
- package/dist/combat/comboPoints.js +52 -0
- package/dist/combat/defensiveWindow.d.ts +1 -1
- package/dist/combat/defensiveWindow.js +5 -3
- package/dist/combat/dotField.d.ts +43 -0
- package/dist/combat/dotField.js +48 -0
- package/dist/combat/projectiles.d.ts +33 -12
- package/dist/combat/projectiles.js +214 -129
- package/dist/combat/regenShield.d.ts +35 -0
- package/dist/combat/regenShield.js +45 -0
- package/dist/combat/resistance.d.ts +16 -4
- package/dist/combat/resistance.js +43 -7
- package/dist/combat/resourcePool.d.ts +20 -0
- package/dist/combat/resourcePool.js +55 -0
- package/dist/combat/shotOrigin.d.ts +52 -0
- package/dist/combat/shotOrigin.js +86 -0
- package/dist/combat/telegraph.d.ts +25 -0
- package/dist/combat/telegraph.js +47 -0
- package/dist/crafting/recipe.js +4 -1
- package/dist/devtools/devtools.d.ts +72 -24
- package/dist/devtools/devtools.js +449 -101
- package/dist/devtools/transformTunables.js +3 -3
- package/dist/devtools/tunableSchema.d.ts +140 -0
- package/dist/devtools/tunableSchema.js +476 -0
- package/dist/editor/commands.d.ts +65 -0
- package/dist/editor/commands.js +193 -0
- package/dist/editor/document.d.ts +40 -0
- package/dist/editor/document.js +153 -0
- package/dist/editor/index.d.ts +30 -0
- package/dist/editor/index.js +6 -0
- package/dist/editor/types.d.ts +69 -0
- package/dist/editor/types.js +29 -0
- package/dist/faction/factions.d.ts +33 -0
- package/dist/faction/factions.js +76 -0
- package/dist/faction/reputation.d.ts +32 -0
- package/dist/faction/reputation.js +82 -0
- package/dist/game/chat.d.ts +2 -0
- package/dist/game/chat.js +9 -1
- package/dist/game/chatFilter.d.ts +15 -0
- package/dist/game/chatFilter.js +55 -0
- package/dist/game/connectedPlayers.d.ts +28 -0
- package/dist/game/connectedPlayers.js +21 -0
- package/dist/game/controlGate.d.ts +4 -0
- package/dist/game/controlGate.js +7 -0
- package/dist/game/defineGame.d.ts +44 -1
- package/dist/game/defineGame.js +1 -0
- package/dist/game/events.d.ts +25 -0
- package/dist/game/gamePhase.d.ts +20 -0
- package/dist/game/gamePhase.js +21 -0
- package/dist/game/keyValueStore.d.ts +25 -0
- package/dist/game/keyValueStore.js +61 -0
- package/dist/game/levelSequence.d.ts +24 -2
- package/dist/game/levelSequence.js +51 -2
- package/dist/game/lootTable.d.ts +14 -1
- package/dist/game/lootTable.js +28 -7
- package/dist/game/modelAnimation.d.ts +8 -0
- package/dist/game/modelAnimation.js +18 -0
- package/dist/game/playableGame.d.ts +116 -4
- package/dist/game/playableGame.js +1 -1
- package/dist/game/quest.d.ts +7 -3
- package/dist/game/quest.js +17 -8
- package/dist/game/race.d.ts +39 -1
- package/dist/game/race.js +96 -11
- package/dist/game/recordBook.d.ts +27 -0
- package/dist/game/recordBook.js +86 -0
- package/dist/game/talents.d.ts +47 -0
- package/dist/game/talents.js +140 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/input/actionBindings.d.ts +2 -0
- package/dist/input/actionBindings.js +1 -0
- package/dist/input/axisInput.d.ts +33 -3
- package/dist/input/axisInput.js +60 -4
- package/dist/input/bindingOverrides.d.ts +25 -0
- package/dist/input/bindingOverrides.js +95 -0
- package/dist/input/pointerAxis.d.ts +30 -0
- package/dist/input/pointerAxis.js +31 -0
- package/dist/input/touchScheme.d.ts +6 -0
- package/dist/input/touchScheme.js +45 -7
- package/dist/interaction/contextMenu.d.ts +1 -0
- package/dist/interaction/contextMenu.js +1 -0
- package/dist/interaction/proximityPrompt.d.ts +3 -1
- package/dist/interaction/proximityPrompt.js +3 -3
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +44 -1
- package/dist/movement/dash.d.ts +3 -0
- package/dist/movement/dash.js +19 -5
- package/dist/movement/glideModel.d.ts +44 -0
- package/dist/movement/glideModel.js +54 -0
- package/dist/movement/grappleSwing.d.ts +34 -0
- package/dist/movement/grappleSwing.js +56 -0
- package/dist/movement/leaderTrail.d.ts +29 -0
- package/dist/movement/leaderTrail.js +67 -0
- package/dist/movement/movementModel.d.ts +4 -0
- package/dist/movement/movementModel.js +13 -0
- package/dist/movement/playerMovement.d.ts +41 -0
- package/dist/movement/playerMovement.js +181 -0
- package/dist/movement/steering.d.ts +27 -0
- package/dist/movement/steering.js +40 -0
- package/dist/multiplayer/feedWriteGate.d.ts +10 -0
- package/dist/multiplayer/feedWriteGate.js +12 -0
- package/dist/multiplayer/presenceModel.d.ts +14 -2
- package/dist/multiplayer/presenceModel.js +15 -3
- package/dist/nav/corridors.d.ts +30 -0
- package/dist/nav/corridors.js +69 -0
- package/dist/nav/navGrid.js +30 -4
- package/dist/nav/railGraph.d.ts +66 -0
- package/dist/nav/railGraph.js +129 -0
- package/dist/nav/timetable.d.ts +41 -0
- package/dist/nav/timetable.js +94 -0
- package/dist/physics/buoyancy.d.ts +6 -0
- package/dist/physics/buoyancy.js +14 -0
- package/dist/physics/flowTube.d.ts +32 -0
- package/dist/physics/flowTube.js +64 -0
- package/dist/physics/forceVolume.d.ts +27 -0
- package/dist/physics/forceVolume.js +38 -0
- package/dist/physics/kinematicVehicle.d.ts +52 -0
- package/dist/physics/kinematicVehicle.js +81 -0
- package/dist/physics/physicsWorld.d.ts +13 -6
- package/dist/physics/physicsWorld.js +169 -40
- package/dist/physics/ragdoll.js +1 -1
- package/dist/physics/vehicleBody.js +2 -1
- package/dist/random/rng.d.ts +2 -0
- package/dist/random/rng.js +2 -0
- package/dist/random/seedLink.d.ts +5 -0
- package/dist/random/seedLink.js +35 -0
- package/dist/render/postProcessing.d.ts +56 -0
- package/dist/render/postProcessing.js +1 -0
- package/dist/runtime/adapter.d.ts +19 -0
- package/dist/runtime/adapter.js +11 -5
- package/dist/runtime/cameraDirector.d.ts +6 -1
- package/dist/runtime/cameraDirector.js +6 -0
- package/dist/runtime/commandRunner.d.ts +2 -2
- package/dist/runtime/commandRunner.js +2 -2
- package/dist/runtime/gameContext.d.ts +72 -8
- package/dist/runtime/gameContext.js +374 -61
- package/dist/runtime/gameRuntime.d.ts +5 -1
- package/dist/runtime/gameRuntime.js +13 -12
- package/dist/runtime/hostPersistence.d.ts +9 -1
- package/dist/runtime/hostPersistence.js +23 -8
- package/dist/runtime/hostedGameRunner.d.ts +49 -0
- package/dist/runtime/hostedGameRunner.js +62 -0
- package/dist/runtime/hostedWorldSession.d.ts +65 -0
- package/dist/runtime/hostedWorldSession.js +57 -0
- package/dist/runtime/inputSnapshot.d.ts +18 -0
- package/dist/runtime/inputSnapshot.js +7 -0
- package/dist/runtime/motionIntents.d.ts +16 -6
- package/dist/runtime/motionIntents.js +31 -2
- package/dist/runtime/snapshot.d.ts +1 -0
- package/dist/runtime/snapshot.js +1 -1
- package/dist/runtime/visibility.d.ts +74 -0
- package/dist/runtime/visibility.js +127 -0
- package/dist/runtime/worldChannel.d.ts +58 -0
- package/dist/runtime/worldChannel.js +69 -0
- package/dist/runtime/worldMirror.d.ts +24 -0
- package/dist/runtime/worldMirror.js +31 -0
- package/dist/runtime/worldReplication.d.ts +46 -0
- package/dist/runtime/worldReplication.js +185 -0
- package/dist/runtime/worldSnapshot.d.ts +17 -0
- package/dist/runtime/worldSnapshot.js +15 -0
- package/dist/scene/assetCatalog.d.ts +1 -0
- package/dist/scene/colliders.d.ts +50 -0
- package/dist/scene/colliders.js +101 -0
- package/dist/scene/entityStats.d.ts +4 -0
- package/dist/scene/entityStats.js +21 -0
- package/dist/scene/entityStore.d.ts +51 -5
- package/dist/scene/entityStore.js +119 -12
- package/dist/scene/objectQuery.d.ts +13 -2
- package/dist/scene/objectQuery.js +34 -2
- package/dist/scene/objectStore.d.ts +2 -0
- package/dist/scene/objectStore.js +31 -12
- package/dist/scene/sceneRaycast.d.ts +80 -0
- package/dist/scene/sceneRaycast.js +284 -0
- package/dist/scene/spatial.d.ts +11 -16
- package/dist/scene/spatial.js +99 -38
- package/dist/sensor/recordingBuffer.d.ts +6 -0
- package/dist/sensor/recordingBuffer.js +7 -0
- package/dist/sensor/replayLoop.d.ts +39 -0
- package/dist/sensor/replayLoop.js +64 -0
- package/dist/sensor/visionCone.d.ts +31 -0
- package/dist/sensor/visionCone.js +72 -0
- package/dist/session/extraction.d.ts +2 -2
- package/dist/session/extraction.js +13 -2
- package/dist/settings/settingsModel.d.ts +97 -0
- package/dist/settings/settingsModel.js +100 -0
- package/dist/stats/statModifiers.d.ts +5 -2
- package/dist/stats/statModifiers.js +32 -14
- package/dist/store/observableKeyedStore.d.ts +4 -0
- package/dist/store/observableKeyedStore.js +17 -2
- package/dist/survival/decayMeter.d.ts +1 -1
- package/dist/survival/decayMeter.js +0 -1
- package/dist/tactics/fallingGrid.d.ts +2 -2
- package/dist/tactics/fallingGrid.js +2 -2
- package/dist/time/beatClock.d.ts +18 -0
- package/dist/time/beatClock.js +24 -0
- package/dist/time/idleProgress.d.ts +29 -0
- package/dist/time/idleProgress.js +31 -0
- package/dist/time/serverTick.d.ts +7 -1
- package/dist/time/serverTick.js +27 -4
- package/dist/time/stateSchedule.d.ts +57 -0
- package/dist/time/stateSchedule.js +121 -0
- package/dist/turn/commit.d.ts +2 -2
- package/dist/turn/commit.js +2 -2
- package/dist/ui/gameLayout.d.ts +125 -0
- package/dist/ui/gameLayout.js +129 -0
- package/dist/ui/hudLayout.d.ts +58 -0
- package/dist/ui/hudLayout.js +207 -0
- package/dist/ui/hudScale.d.ts +44 -0
- package/dist/ui/hudScale.js +55 -0
- package/dist/ui/orientation.d.ts +39 -0
- package/dist/ui/orientation.js +49 -0
- package/dist/ui/swingTimer.d.ts +42 -0
- package/dist/ui/swingTimer.js +38 -0
- package/dist/visibility/assetStreaming.d.ts +66 -0
- package/dist/visibility/assetStreaming.js +201 -0
- package/dist/visibility/bounds.d.ts +66 -0
- package/dist/visibility/bounds.js +113 -0
- package/dist/visibility/camera.d.ts +21 -0
- package/dist/visibility/camera.js +3 -0
- package/dist/visibility/config.d.ts +21 -0
- package/dist/visibility/config.js +1 -0
- package/dist/visibility/diagnostics.d.ts +26 -0
- package/dist/visibility/diagnostics.js +36 -0
- package/dist/visibility/distance.d.ts +11 -0
- package/dist/visibility/distance.js +30 -0
- package/dist/visibility/frustum.d.ts +47 -0
- package/dist/visibility/frustum.js +186 -0
- package/dist/visibility/occlusion.d.ts +36 -0
- package/dist/visibility/occlusion.js +45 -0
- package/dist/visibility/settings.d.ts +75 -0
- package/dist/visibility/settings.js +49 -0
- package/dist/visibility/simulationCulling.d.ts +34 -0
- package/dist/visibility/simulationCulling.js +36 -0
- package/dist/visibility/spatialIndex.d.ts +45 -0
- package/dist/visibility/spatialIndex.js +212 -0
- package/dist/visibility/visibilitySystem.d.ts +90 -0
- package/dist/visibility/visibilitySystem.js +257 -0
- package/dist/world/buildings.d.ts +6 -0
- package/dist/world/buildings.js +152 -0
- package/dist/world/carve.d.ts +7 -2
- package/dist/world/carve.js +17 -7
- package/dist/world/cellStates.d.ts +45 -0
- package/dist/world/cellStates.js +103 -0
- package/dist/world/environmentSummary.d.ts +21 -4
- package/dist/world/environmentSummary.js +22 -3
- package/dist/world/features.d.ts +185 -8
- package/dist/world/features.js +64 -6
- package/dist/world/gridInstances.d.ts +7 -8
- package/dist/world/gridInstances.js +145 -7
- package/dist/world/lod.d.ts +17 -0
- package/dist/world/lod.js +66 -0
- package/dist/world/mapLayers.d.ts +60 -0
- package/dist/world/mapLayers.js +44 -0
- package/dist/world/massing.d.ts +45 -0
- package/dist/world/massing.js +581 -0
- package/dist/world/minimap.d.ts +13 -1
- package/dist/world/minimap.js +21 -2
- package/dist/world/roads.d.ts +47 -0
- package/dist/world/roads.js +146 -0
- package/dist/world/streets.d.ts +80 -0
- package/dist/world/streets.js +144 -0
- package/dist/world/terrain.d.ts +69 -3
- package/dist/world/terrain.js +309 -16
- package/dist/world/water.d.ts +7 -0
- package/dist/world/water.js +19 -4
- package/dist/world/weather.d.ts +3 -18
- package/dist/world/weather.js +4 -17
- package/dist/world/windZones.d.ts +47 -0
- package/dist/world/windZones.js +59 -0
- package/llms.txt +3013 -1726
- package/package.json +12 -2
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { cloneEditorDocument, findEditorMarker, findEditorVolume, importEditorDocumentJson, } from "./document.js";
|
|
2
|
+
function snapshotState(state) {
|
|
3
|
+
return {
|
|
4
|
+
document: cloneEditorDocument(state.document),
|
|
5
|
+
selection: [...state.selection],
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function removeById(doc, id) {
|
|
9
|
+
return {
|
|
10
|
+
version: 1,
|
|
11
|
+
markers: doc.markers.filter((marker) => marker.id !== id),
|
|
12
|
+
volumes: doc.volumes.filter((volume) => volume.id !== id),
|
|
13
|
+
paths: doc.paths.filter((path) => path.id !== id),
|
|
14
|
+
annotations: doc.annotations.filter((note) => note.id !== id),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function applyMutating(state, command) {
|
|
18
|
+
switch (command.type) {
|
|
19
|
+
case "select":
|
|
20
|
+
return { ...state, selection: [...command.ids] };
|
|
21
|
+
case "clearSelection":
|
|
22
|
+
return { ...state, selection: [] };
|
|
23
|
+
case "setTransform": {
|
|
24
|
+
const markers = state.document.markers.map((marker) => {
|
|
25
|
+
if (marker.id !== command.id)
|
|
26
|
+
return marker;
|
|
27
|
+
return {
|
|
28
|
+
...marker,
|
|
29
|
+
position: command.position === undefined ? marker.position : { ...command.position },
|
|
30
|
+
...(command.rotationY === undefined ? {} : { rotationY: command.rotationY }),
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
const volumes = state.document.volumes.map((volume) => {
|
|
34
|
+
if (volume.id !== command.id)
|
|
35
|
+
return volume;
|
|
36
|
+
if (command.position === undefined)
|
|
37
|
+
return volume;
|
|
38
|
+
return { ...volume, center: { ...command.position } };
|
|
39
|
+
});
|
|
40
|
+
const annotations = state.document.annotations.map((note) => {
|
|
41
|
+
if (note.id !== command.id)
|
|
42
|
+
return note;
|
|
43
|
+
if (command.position === undefined)
|
|
44
|
+
return note;
|
|
45
|
+
return { ...note, position: { ...command.position } };
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
...state,
|
|
49
|
+
document: { ...state.document, markers, volumes, annotations },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
case "addMarker":
|
|
53
|
+
return {
|
|
54
|
+
...state,
|
|
55
|
+
document: {
|
|
56
|
+
...state.document,
|
|
57
|
+
markers: [...state.document.markers.filter((m) => m.id !== command.marker.id), command.marker],
|
|
58
|
+
},
|
|
59
|
+
selection: [command.marker.id],
|
|
60
|
+
};
|
|
61
|
+
case "addVolume":
|
|
62
|
+
return {
|
|
63
|
+
...state,
|
|
64
|
+
document: {
|
|
65
|
+
...state.document,
|
|
66
|
+
volumes: [...state.document.volumes.filter((v) => v.id !== command.volume.id), command.volume],
|
|
67
|
+
},
|
|
68
|
+
selection: [command.volume.id],
|
|
69
|
+
};
|
|
70
|
+
case "setVolume": {
|
|
71
|
+
const volumes = state.document.volumes.map((volume) => {
|
|
72
|
+
if (volume.id !== command.id)
|
|
73
|
+
return volume;
|
|
74
|
+
return {
|
|
75
|
+
...volume,
|
|
76
|
+
...command.patch,
|
|
77
|
+
center: command.patch.center === undefined ? volume.center : { ...command.patch.center },
|
|
78
|
+
halfExtents: command.patch.halfExtents === undefined
|
|
79
|
+
? volume.halfExtents
|
|
80
|
+
: { ...command.patch.halfExtents },
|
|
81
|
+
meta: command.patch.meta === undefined ? volume.meta : { ...command.patch.meta },
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
return { ...state, document: { ...state.document, volumes } };
|
|
85
|
+
}
|
|
86
|
+
case "remove":
|
|
87
|
+
return {
|
|
88
|
+
document: removeById(state.document, command.id),
|
|
89
|
+
selection: state.selection.filter((id) => id !== command.id),
|
|
90
|
+
};
|
|
91
|
+
case "importDocument":
|
|
92
|
+
case "replaceDocument":
|
|
93
|
+
return {
|
|
94
|
+
document: cloneEditorDocument(command.document),
|
|
95
|
+
selection: [],
|
|
96
|
+
};
|
|
97
|
+
case "importJson":
|
|
98
|
+
return {
|
|
99
|
+
document: importEditorDocumentJson(command.json),
|
|
100
|
+
selection: [],
|
|
101
|
+
};
|
|
102
|
+
case "undo":
|
|
103
|
+
case "redo":
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function isStructural(command) {
|
|
108
|
+
return (command.type === "setTransform" ||
|
|
109
|
+
command.type === "addMarker" ||
|
|
110
|
+
command.type === "addVolume" ||
|
|
111
|
+
command.type === "setVolume" ||
|
|
112
|
+
command.type === "remove" ||
|
|
113
|
+
command.type === "importDocument" ||
|
|
114
|
+
command.type === "importJson" ||
|
|
115
|
+
command.type === "replaceDocument");
|
|
116
|
+
}
|
|
117
|
+
/** Creates an editor session with undo/redo history seeded from an initial document. */
|
|
118
|
+
export function createEditorSession(initial, historyLimit = 100) {
|
|
119
|
+
let state = {
|
|
120
|
+
document: cloneEditorDocument(initial),
|
|
121
|
+
selection: [],
|
|
122
|
+
};
|
|
123
|
+
const past = [];
|
|
124
|
+
const future = [];
|
|
125
|
+
const listeners = new Set();
|
|
126
|
+
const emit = () => {
|
|
127
|
+
for (const listener of listeners)
|
|
128
|
+
listener(state);
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
getState: () => state,
|
|
132
|
+
subscribe(listener) {
|
|
133
|
+
listeners.add(listener);
|
|
134
|
+
return () => {
|
|
135
|
+
listeners.delete(listener);
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
canUndo: () => past.length > 0,
|
|
139
|
+
canRedo: () => future.length > 0,
|
|
140
|
+
dispatch(command) {
|
|
141
|
+
if (command.type === "undo") {
|
|
142
|
+
const previous = past.pop();
|
|
143
|
+
if (previous === undefined)
|
|
144
|
+
return state;
|
|
145
|
+
future.push(snapshotState(state));
|
|
146
|
+
state = previous;
|
|
147
|
+
emit();
|
|
148
|
+
return state;
|
|
149
|
+
}
|
|
150
|
+
if (command.type === "redo") {
|
|
151
|
+
const next = future.pop();
|
|
152
|
+
if (next === undefined)
|
|
153
|
+
return state;
|
|
154
|
+
past.push(snapshotState(state));
|
|
155
|
+
state = next;
|
|
156
|
+
emit();
|
|
157
|
+
return state;
|
|
158
|
+
}
|
|
159
|
+
const next = applyMutating(state, command);
|
|
160
|
+
if (next === null)
|
|
161
|
+
return state;
|
|
162
|
+
if (isStructural(command)) {
|
|
163
|
+
past.push(snapshotState(state));
|
|
164
|
+
if (past.length > historyLimit)
|
|
165
|
+
past.shift();
|
|
166
|
+
future.length = 0;
|
|
167
|
+
}
|
|
168
|
+
state = next;
|
|
169
|
+
emit();
|
|
170
|
+
return state;
|
|
171
|
+
},
|
|
172
|
+
exportJson(pretty = true) {
|
|
173
|
+
return JSON.stringify(state.document, null, pretty ? 2 : undefined);
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** Compact snapshot of a session state — counts, selection, and the selected object. */
|
|
178
|
+
export function summarizeEditorSession(state) {
|
|
179
|
+
const selectedId = state.selection[0];
|
|
180
|
+
return {
|
|
181
|
+
markers: state.document.markers.length,
|
|
182
|
+
volumes: state.document.volumes.length,
|
|
183
|
+
paths: state.document.paths.length,
|
|
184
|
+
annotations: state.document.annotations.length,
|
|
185
|
+
selection: [...state.selection],
|
|
186
|
+
...(selectedId === undefined
|
|
187
|
+
? {}
|
|
188
|
+
: {
|
|
189
|
+
selectedMarker: findEditorMarker(state.document, selectedId),
|
|
190
|
+
selectedVolume: findEditorVolume(state.document, selectedId),
|
|
191
|
+
}),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { EditorDocument, EditorLayersInput, EditorMarker, EditorNote, EditorPath, EditorVolume } from "./types.js";
|
|
2
|
+
/** Builds a fresh, empty editor document to start authoring a scene from scratch. */
|
|
3
|
+
export declare function createEmptyEditorDocument(): EditorDocument;
|
|
4
|
+
/** Deep-copies an editor document so edits never mutate the source. */
|
|
5
|
+
export declare function cloneEditorDocument(doc: EditorDocument): EditorDocument;
|
|
6
|
+
/** Resolves a game's `editorLayers` export — document, partial data, or factory — into a full document. */
|
|
7
|
+
export declare function normalizeEditorLayers(input: EditorLayersInput | undefined | null): EditorDocument;
|
|
8
|
+
/** Combines multiple editor documents' markers, volumes, paths, and notes into one. */
|
|
9
|
+
export declare function mergeEditorDocuments(...docs: readonly EditorDocument[]): EditorDocument;
|
|
10
|
+
/** Looks up a marker by id in an editor document. */
|
|
11
|
+
export declare function findEditorMarker(doc: EditorDocument, id: string): EditorMarker | undefined;
|
|
12
|
+
/** Looks up a volume by id in an editor document. */
|
|
13
|
+
export declare function findEditorVolume(doc: EditorDocument, id: string): EditorVolume | undefined;
|
|
14
|
+
/** Looks up a path by id in an editor document. */
|
|
15
|
+
export declare function findEditorPath(doc: EditorDocument, id: string): EditorPath | undefined;
|
|
16
|
+
/** Looks up an annotation note by id in an editor document. */
|
|
17
|
+
export declare function findEditorNote(doc: EditorDocument, id: string): EditorNote | undefined;
|
|
18
|
+
/** Lists the distinct marker, volume, and path kinds authored in a document. */
|
|
19
|
+
export declare function listEditorKinds(doc: EditorDocument): {
|
|
20
|
+
markers: string[];
|
|
21
|
+
volumes: string[];
|
|
22
|
+
paths: string[];
|
|
23
|
+
};
|
|
24
|
+
/** Serializes an editor document to JSON text for saving or export. */
|
|
25
|
+
export declare function exportEditorDocumentJson(doc: EditorDocument, pretty?: boolean): string;
|
|
26
|
+
/** Parses JSON text back into a normalized editor document. */
|
|
27
|
+
export declare function importEditorDocumentJson(raw: string): EditorDocument;
|
|
28
|
+
/** Computes the world-space min/max bounds spanning every object in a document, or null if empty. */
|
|
29
|
+
export declare function editorDocumentBounds(doc: EditorDocument): {
|
|
30
|
+
min: {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
z: number;
|
|
34
|
+
};
|
|
35
|
+
max: {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
z: number;
|
|
39
|
+
};
|
|
40
|
+
} | null;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/** Builds a fresh, empty editor document to start authoring a scene from scratch. */
|
|
2
|
+
export function createEmptyEditorDocument() {
|
|
3
|
+
return {
|
|
4
|
+
version: 1,
|
|
5
|
+
markers: [],
|
|
6
|
+
volumes: [],
|
|
7
|
+
paths: [],
|
|
8
|
+
annotations: [],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/** Deep-copies an editor document so edits never mutate the source. */
|
|
12
|
+
export function cloneEditorDocument(doc) {
|
|
13
|
+
return {
|
|
14
|
+
version: 1,
|
|
15
|
+
markers: doc.markers.map((marker) => ({
|
|
16
|
+
...marker,
|
|
17
|
+
position: { ...marker.position },
|
|
18
|
+
...(marker.meta === undefined ? {} : { meta: { ...marker.meta } }),
|
|
19
|
+
})),
|
|
20
|
+
volumes: doc.volumes.map((volume) => ({
|
|
21
|
+
...volume,
|
|
22
|
+
center: { ...volume.center },
|
|
23
|
+
...(volume.halfExtents === undefined ? {} : { halfExtents: { ...volume.halfExtents } }),
|
|
24
|
+
...(volume.meta === undefined ? {} : { meta: { ...volume.meta } }),
|
|
25
|
+
})),
|
|
26
|
+
paths: doc.paths.map((path) => ({
|
|
27
|
+
...path,
|
|
28
|
+
points: path.points.map((point) => ({ ...point })),
|
|
29
|
+
...(path.meta === undefined ? {} : { meta: { ...path.meta } }),
|
|
30
|
+
})),
|
|
31
|
+
annotations: doc.annotations.map((note) => ({
|
|
32
|
+
...note,
|
|
33
|
+
position: { ...note.position },
|
|
34
|
+
...(note.meta === undefined ? {} : { meta: { ...note.meta } }),
|
|
35
|
+
})),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function asArray(value) {
|
|
39
|
+
return value === undefined ? [] : [...value];
|
|
40
|
+
}
|
|
41
|
+
/** Resolves a game's `editorLayers` export — document, partial data, or factory — into a full document. */
|
|
42
|
+
export function normalizeEditorLayers(input) {
|
|
43
|
+
if (input === undefined || input === null)
|
|
44
|
+
return createEmptyEditorDocument();
|
|
45
|
+
const resolved = typeof input === "function" ? input() : input;
|
|
46
|
+
return {
|
|
47
|
+
version: 1,
|
|
48
|
+
markers: asArray(resolved.markers),
|
|
49
|
+
volumes: asArray(resolved.volumes),
|
|
50
|
+
paths: asArray(resolved.paths),
|
|
51
|
+
annotations: asArray(resolved.annotations),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Combines multiple editor documents' markers, volumes, paths, and notes into one. */
|
|
55
|
+
export function mergeEditorDocuments(...docs) {
|
|
56
|
+
const out = createEmptyEditorDocument();
|
|
57
|
+
for (const doc of docs) {
|
|
58
|
+
out.markers.push(...doc.markers);
|
|
59
|
+
out.volumes.push(...doc.volumes);
|
|
60
|
+
out.paths.push(...doc.paths);
|
|
61
|
+
out.annotations.push(...doc.annotations);
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/** Looks up a marker by id in an editor document. */
|
|
66
|
+
export function findEditorMarker(doc, id) {
|
|
67
|
+
return doc.markers.find((marker) => marker.id === id);
|
|
68
|
+
}
|
|
69
|
+
/** Looks up a volume by id in an editor document. */
|
|
70
|
+
export function findEditorVolume(doc, id) {
|
|
71
|
+
return doc.volumes.find((volume) => volume.id === id);
|
|
72
|
+
}
|
|
73
|
+
/** Looks up a path by id in an editor document. */
|
|
74
|
+
export function findEditorPath(doc, id) {
|
|
75
|
+
return doc.paths.find((path) => path.id === id);
|
|
76
|
+
}
|
|
77
|
+
/** Looks up an annotation note by id in an editor document. */
|
|
78
|
+
export function findEditorNote(doc, id) {
|
|
79
|
+
return doc.annotations.find((note) => note.id === id);
|
|
80
|
+
}
|
|
81
|
+
/** Lists the distinct marker, volume, and path kinds authored in a document. */
|
|
82
|
+
export function listEditorKinds(doc) {
|
|
83
|
+
const markers = new Set();
|
|
84
|
+
const volumes = new Set();
|
|
85
|
+
const paths = new Set();
|
|
86
|
+
for (const marker of doc.markers)
|
|
87
|
+
markers.add(marker.kind);
|
|
88
|
+
for (const volume of doc.volumes)
|
|
89
|
+
volumes.add(volume.kind);
|
|
90
|
+
for (const path of doc.paths)
|
|
91
|
+
paths.add(path.kind);
|
|
92
|
+
return {
|
|
93
|
+
markers: [...markers].sort(),
|
|
94
|
+
volumes: [...volumes].sort(),
|
|
95
|
+
paths: [...paths].sort(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** Serializes an editor document to JSON text for saving or export. */
|
|
99
|
+
export function exportEditorDocumentJson(doc, pretty = true) {
|
|
100
|
+
return JSON.stringify(doc, null, pretty ? 2 : undefined);
|
|
101
|
+
}
|
|
102
|
+
/** Parses JSON text back into a normalized editor document. */
|
|
103
|
+
export function importEditorDocumentJson(raw) {
|
|
104
|
+
const parsed = JSON.parse(raw);
|
|
105
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
106
|
+
throw new Error("editor document must be an object");
|
|
107
|
+
}
|
|
108
|
+
return normalizeEditorLayers(parsed);
|
|
109
|
+
}
|
|
110
|
+
/** Computes the world-space min/max bounds spanning every object in a document, or null if empty. */
|
|
111
|
+
export function editorDocumentBounds(doc) {
|
|
112
|
+
let minX = Infinity;
|
|
113
|
+
let minY = Infinity;
|
|
114
|
+
let minZ = Infinity;
|
|
115
|
+
let maxX = -Infinity;
|
|
116
|
+
let maxY = -Infinity;
|
|
117
|
+
let maxZ = -Infinity;
|
|
118
|
+
let any = false;
|
|
119
|
+
const include = (x, y, z) => {
|
|
120
|
+
any = true;
|
|
121
|
+
if (x < minX)
|
|
122
|
+
minX = x;
|
|
123
|
+
if (y < minY)
|
|
124
|
+
minY = y;
|
|
125
|
+
if (z < minZ)
|
|
126
|
+
minZ = z;
|
|
127
|
+
if (x > maxX)
|
|
128
|
+
maxX = x;
|
|
129
|
+
if (y > maxY)
|
|
130
|
+
maxY = y;
|
|
131
|
+
if (z > maxZ)
|
|
132
|
+
maxZ = z;
|
|
133
|
+
};
|
|
134
|
+
for (const marker of doc.markers) {
|
|
135
|
+
include(marker.position.x, marker.position.y, marker.position.z);
|
|
136
|
+
}
|
|
137
|
+
for (const volume of doc.volumes) {
|
|
138
|
+
include(volume.center.x, volume.center.y, volume.center.z);
|
|
139
|
+
}
|
|
140
|
+
for (const path of doc.paths) {
|
|
141
|
+
for (const point of path.points)
|
|
142
|
+
include(point.x, point.y, point.z);
|
|
143
|
+
}
|
|
144
|
+
for (const note of doc.annotations) {
|
|
145
|
+
include(note.position.x, note.position.y, note.position.z);
|
|
146
|
+
}
|
|
147
|
+
if (!any)
|
|
148
|
+
return null;
|
|
149
|
+
return {
|
|
150
|
+
min: { x: minX, y: minY, z: minZ },
|
|
151
|
+
max: { x: maxX, y: maxY, z: maxZ },
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** The full authored scene: every marker, volume, path, and note for a game. */
|
|
2
|
+
export type { EditorDocument } from "./types.js";
|
|
3
|
+
/** Per-kind show/hide flags for the editor's layer panel. */
|
|
4
|
+
export type { EditorKindVisibility } from "./types.js";
|
|
5
|
+
/** Accepted shape for a game's `editorLayers` export: a document, partial data, or a factory. */
|
|
6
|
+
export type { EditorLayersInput } from "./types.js";
|
|
7
|
+
/** A placeable point object in the scene — spawn, mob, chest, POI, etc. */
|
|
8
|
+
export type { EditorMarker } from "./types.js";
|
|
9
|
+
/** A free-text annotation pinned to a world position for designers. */
|
|
10
|
+
export type { EditorNote } from "./types.js";
|
|
11
|
+
/** A polyline of points — road, corridor, patrol route — placed in the scene. */
|
|
12
|
+
export type { EditorPath } from "./types.js";
|
|
13
|
+
/** A world-space point used across editor markers, volumes, and paths. */
|
|
14
|
+
export type { EditorVec3 } from "./types.js";
|
|
15
|
+
/** A spatial region — zone, aggro range, capture area — placed in the scene. */
|
|
16
|
+
export type { EditorVolume } from "./types.js";
|
|
17
|
+
/** Collision shape a volume is rendered and tested as. */
|
|
18
|
+
export type { EditorVolumeShape } from "./types.js";
|
|
19
|
+
export { WELL_KNOWN_MARKER_KINDS, WELL_KNOWN_PATH_KINDS, WELL_KNOWN_VOLUME_KINDS, } from "./types.js";
|
|
20
|
+
export { cloneEditorDocument, createEmptyEditorDocument, editorDocumentBounds, exportEditorDocumentJson, findEditorMarker, findEditorNote, findEditorPath, findEditorVolume, importEditorDocumentJson, listEditorKinds, mergeEditorDocuments, normalizeEditorLayers, } from "./document.js";
|
|
21
|
+
/** Creates an editor session with undo/redo history seeded from an initial document. */
|
|
22
|
+
export { createEditorSession } from "./commands.js";
|
|
23
|
+
/** Compact snapshot of a session state — counts, selection, and the selected object. */
|
|
24
|
+
export { summarizeEditorSession } from "./commands.js";
|
|
25
|
+
/** A single editor mutation — select, move, add, remove, undo/redo — dispatched to a session. */
|
|
26
|
+
export type { EditorCommand } from "./commands.js";
|
|
27
|
+
/** Stateful, undoable handle for driving scene edits from UI or an MCP agent. */
|
|
28
|
+
export type { EditorSession } from "./commands.js";
|
|
29
|
+
/** The document plus current selection at a point in editor history. */
|
|
30
|
+
export type { EditorSessionState } from "./commands.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { WELL_KNOWN_MARKER_KINDS, WELL_KNOWN_PATH_KINDS, WELL_KNOWN_VOLUME_KINDS, } from "./types.js";
|
|
2
|
+
export { cloneEditorDocument, createEmptyEditorDocument, editorDocumentBounds, exportEditorDocumentJson, findEditorMarker, findEditorNote, findEditorPath, findEditorVolume, importEditorDocumentJson, listEditorKinds, mergeEditorDocuments, normalizeEditorLayers, } from "./document.js";
|
|
3
|
+
/** Creates an editor session with undo/redo history seeded from an initial document. */
|
|
4
|
+
export { createEditorSession } from "./commands.js";
|
|
5
|
+
/** Compact snapshot of a session state — counts, selection, and the selected object. */
|
|
6
|
+
export { summarizeEditorSession } from "./commands.js";
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** A world-space point used across editor markers, volumes, and paths. */
|
|
2
|
+
export type EditorVec3 = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
z: number;
|
|
6
|
+
};
|
|
7
|
+
/** Collision shape a volume is rendered and tested as. */
|
|
8
|
+
export type EditorVolumeShape = "sphere" | "cylinder" | "box";
|
|
9
|
+
/** A placeable point object in the scene — spawn, mob, chest, POI, etc. */
|
|
10
|
+
export interface EditorMarker {
|
|
11
|
+
id: string;
|
|
12
|
+
kind: string;
|
|
13
|
+
position: EditorVec3;
|
|
14
|
+
rotationY?: number;
|
|
15
|
+
color?: string;
|
|
16
|
+
label?: string;
|
|
17
|
+
meta?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
/** A spatial region — zone, aggro range, capture area — placed in the scene. */
|
|
20
|
+
export interface EditorVolume {
|
|
21
|
+
id: string;
|
|
22
|
+
kind: string;
|
|
23
|
+
shape: EditorVolumeShape;
|
|
24
|
+
center: EditorVec3;
|
|
25
|
+
radius?: number;
|
|
26
|
+
height?: number;
|
|
27
|
+
halfExtents?: EditorVec3;
|
|
28
|
+
color?: string;
|
|
29
|
+
label?: string;
|
|
30
|
+
meta?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
/** A polyline of points — road, corridor, patrol route — placed in the scene. */
|
|
33
|
+
export interface EditorPath {
|
|
34
|
+
id: string;
|
|
35
|
+
kind: string;
|
|
36
|
+
points: readonly EditorVec3[];
|
|
37
|
+
width?: number;
|
|
38
|
+
color?: string;
|
|
39
|
+
label?: string;
|
|
40
|
+
meta?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
/** A free-text annotation pinned to a world position for designers. */
|
|
43
|
+
export interface EditorNote {
|
|
44
|
+
id: string;
|
|
45
|
+
text: string;
|
|
46
|
+
position: EditorVec3;
|
|
47
|
+
color?: string;
|
|
48
|
+
meta?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
/** The full authored scene: every marker, volume, path, and note for a game. */
|
|
51
|
+
export interface EditorDocument {
|
|
52
|
+
version: 1;
|
|
53
|
+
markers: EditorMarker[];
|
|
54
|
+
volumes: EditorVolume[];
|
|
55
|
+
paths: EditorPath[];
|
|
56
|
+
annotations: EditorNote[];
|
|
57
|
+
}
|
|
58
|
+
/** Accepted shape for a game's `editorLayers` export: a document, partial data, or a factory. */
|
|
59
|
+
export type EditorLayersInput = EditorDocument | Partial<Omit<EditorDocument, "version">> | (() => EditorDocument | Partial<Omit<EditorDocument, "version">>);
|
|
60
|
+
/** Per-kind show/hide flags for the editor's layer panel. */
|
|
61
|
+
export interface EditorKindVisibility {
|
|
62
|
+
[kind: string]: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** Standard marker kinds recognized with default colors and behavior. */
|
|
65
|
+
export declare const WELL_KNOWN_MARKER_KINDS: readonly ["player_spawn", "mob", "boss", "vendor", "chest", "travel", "npc", "poi", "prop", "goal", "branch"];
|
|
66
|
+
/** Standard volume kinds recognized with default colors and behavior. */
|
|
67
|
+
export declare const WELL_KNOWN_VOLUME_KINDS: readonly ["zone", "flatten", "cluster", "aggro", "leash", "discover", "capture", "prompt", "poi", "respawn_skip"];
|
|
68
|
+
/** Standard path kinds recognized with default colors and behavior. */
|
|
69
|
+
export declare const WELL_KNOWN_PATH_KINDS: readonly ["road", "corridor", "branch", "route"];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Standard marker kinds recognized with default colors and behavior. */
|
|
2
|
+
export const WELL_KNOWN_MARKER_KINDS = [
|
|
3
|
+
"player_spawn",
|
|
4
|
+
"mob",
|
|
5
|
+
"boss",
|
|
6
|
+
"vendor",
|
|
7
|
+
"chest",
|
|
8
|
+
"travel",
|
|
9
|
+
"npc",
|
|
10
|
+
"poi",
|
|
11
|
+
"prop",
|
|
12
|
+
"goal",
|
|
13
|
+
"branch",
|
|
14
|
+
];
|
|
15
|
+
/** Standard volume kinds recognized with default colors and behavior. */
|
|
16
|
+
export const WELL_KNOWN_VOLUME_KINDS = [
|
|
17
|
+
"zone",
|
|
18
|
+
"flatten",
|
|
19
|
+
"cluster",
|
|
20
|
+
"aggro",
|
|
21
|
+
"leash",
|
|
22
|
+
"discover",
|
|
23
|
+
"capture",
|
|
24
|
+
"prompt",
|
|
25
|
+
"poi",
|
|
26
|
+
"respawn_skip",
|
|
27
|
+
];
|
|
28
|
+
/** Standard path kinds recognized with default colors and behavior. */
|
|
29
|
+
export const WELL_KNOWN_PATH_KINDS = ["road", "corridor", "branch", "route"];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type FactionRelation = "hostile" | "neutral" | "friendly";
|
|
2
|
+
export interface FactionDef {
|
|
3
|
+
id: string;
|
|
4
|
+
relations?: Readonly<Record<string, FactionRelation>>;
|
|
5
|
+
towardSelf?: FactionRelation;
|
|
6
|
+
towardOthers?: FactionRelation;
|
|
7
|
+
}
|
|
8
|
+
export interface FactionGraphConfig {
|
|
9
|
+
factions: readonly FactionDef[];
|
|
10
|
+
symmetric?: boolean;
|
|
11
|
+
unaligned?: FactionRelation;
|
|
12
|
+
}
|
|
13
|
+
export interface FactionGraph {
|
|
14
|
+
relationBetween(observer: string | null | undefined, other: string | null | undefined): FactionRelation;
|
|
15
|
+
isHostile(observer: string | null | undefined, other: string | null | undefined): boolean;
|
|
16
|
+
isFriendly(observer: string | null | undefined, other: string | null | undefined): boolean;
|
|
17
|
+
isNeutral(observer: string | null | undefined, other: string | null | undefined): boolean;
|
|
18
|
+
has(factionId: string): boolean;
|
|
19
|
+
ids(): string[];
|
|
20
|
+
}
|
|
21
|
+
export declare function createFactionGraph(config: FactionGraphConfig): FactionGraph;
|
|
22
|
+
export interface FactionRoster {
|
|
23
|
+
assign(entityId: string, factionId: string | null): void;
|
|
24
|
+
factionOf(entityId: string): string | null;
|
|
25
|
+
relationBetweenEntities(observer: string, other: string): FactionRelation;
|
|
26
|
+
isHostile(observer: string, other: string): boolean;
|
|
27
|
+
isFriendly(observer: string, other: string): boolean;
|
|
28
|
+
members(factionId: string): string[];
|
|
29
|
+
hostilesOf(observer: string, candidates: Iterable<string>): string[];
|
|
30
|
+
remove(entityId: string): void;
|
|
31
|
+
clear(): void;
|
|
32
|
+
}
|
|
33
|
+
export declare function createFactionRoster(graph: FactionGraph): FactionRoster;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export function createFactionGraph(config) {
|
|
2
|
+
const symmetric = config.symmetric ?? true;
|
|
3
|
+
const unaligned = config.unaligned ?? "neutral";
|
|
4
|
+
const defs = new Map();
|
|
5
|
+
for (const def of config.factions)
|
|
6
|
+
defs.set(def.id, def);
|
|
7
|
+
function relationBetween(observer, other) {
|
|
8
|
+
if (observer == null || other == null)
|
|
9
|
+
return unaligned;
|
|
10
|
+
const from = defs.get(observer);
|
|
11
|
+
const to = defs.get(other);
|
|
12
|
+
if (from === undefined || to === undefined)
|
|
13
|
+
return unaligned;
|
|
14
|
+
if (observer === other)
|
|
15
|
+
return from.towardSelf ?? "friendly";
|
|
16
|
+
const direct = from.relations?.[other];
|
|
17
|
+
if (direct !== undefined)
|
|
18
|
+
return direct;
|
|
19
|
+
if (symmetric) {
|
|
20
|
+
const mirrored = to.relations?.[observer];
|
|
21
|
+
if (mirrored !== undefined)
|
|
22
|
+
return mirrored;
|
|
23
|
+
}
|
|
24
|
+
return from.towardOthers ?? "neutral";
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
relationBetween,
|
|
28
|
+
isHostile: (observer, other) => relationBetween(observer, other) === "hostile",
|
|
29
|
+
isFriendly: (observer, other) => relationBetween(observer, other) === "friendly",
|
|
30
|
+
isNeutral: (observer, other) => relationBetween(observer, other) === "neutral",
|
|
31
|
+
has: (factionId) => defs.has(factionId),
|
|
32
|
+
ids: () => [...defs.keys()],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function createFactionRoster(graph) {
|
|
36
|
+
const membership = new Map();
|
|
37
|
+
function factionOf(entityId) {
|
|
38
|
+
return membership.get(entityId) ?? null;
|
|
39
|
+
}
|
|
40
|
+
function relationBetweenEntities(observer, other) {
|
|
41
|
+
return graph.relationBetween(factionOf(observer), factionOf(other));
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
assign(entityId, factionId) {
|
|
45
|
+
if (factionId === null)
|
|
46
|
+
membership.delete(entityId);
|
|
47
|
+
else
|
|
48
|
+
membership.set(entityId, factionId);
|
|
49
|
+
},
|
|
50
|
+
factionOf,
|
|
51
|
+
relationBetweenEntities,
|
|
52
|
+
isHostile: (observer, other) => relationBetweenEntities(observer, other) === "hostile",
|
|
53
|
+
isFriendly: (observer, other) => relationBetweenEntities(observer, other) === "friendly",
|
|
54
|
+
members(factionId) {
|
|
55
|
+
const out = [];
|
|
56
|
+
for (const [entityId, id] of membership)
|
|
57
|
+
if (id === factionId)
|
|
58
|
+
out.push(entityId);
|
|
59
|
+
return out;
|
|
60
|
+
},
|
|
61
|
+
hostilesOf(observer, candidates) {
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const id of candidates) {
|
|
64
|
+
if (id !== observer && relationBetweenEntities(observer, id) === "hostile")
|
|
65
|
+
out.push(id);
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
},
|
|
69
|
+
remove(entityId) {
|
|
70
|
+
membership.delete(entityId);
|
|
71
|
+
},
|
|
72
|
+
clear() {
|
|
73
|
+
membership.clear();
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { FactionRelation } from "./factions.js";
|
|
2
|
+
export interface ReputationTier {
|
|
3
|
+
id: string;
|
|
4
|
+
min: number;
|
|
5
|
+
relation: FactionRelation;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_REPUTATION_TIERS: readonly ReputationTier[];
|
|
8
|
+
export declare function tierForStanding(tiers: readonly ReputationTier[], standing: number): ReputationTier;
|
|
9
|
+
export interface ReputationLedgerConfig {
|
|
10
|
+
tiers?: readonly ReputationTier[];
|
|
11
|
+
initial?: Readonly<Record<string, number>>;
|
|
12
|
+
min?: number;
|
|
13
|
+
max?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ReputationLedger {
|
|
16
|
+
standing(actorId: string, factionId: string): number;
|
|
17
|
+
hasStanding(actorId: string, factionId: string): boolean;
|
|
18
|
+
gain(actorId: string, factionId: string, amount: number): number;
|
|
19
|
+
set(actorId: string, factionId: string, standing: number): number;
|
|
20
|
+
tier(actorId: string, factionId: string): ReputationTier;
|
|
21
|
+
relation(actorId: string, factionId: string): FactionRelation;
|
|
22
|
+
standings(actorId: string): Record<string, number>;
|
|
23
|
+
reset(actorId: string, factionId?: string): void;
|
|
24
|
+
}
|
|
25
|
+
export declare function createReputationLedger(config?: ReputationLedgerConfig): ReputationLedger;
|
|
26
|
+
export interface EffectiveRelationInput {
|
|
27
|
+
base: FactionRelation;
|
|
28
|
+
ledger: ReputationLedger;
|
|
29
|
+
actorId: string;
|
|
30
|
+
factionId: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function effectiveRelation(input: EffectiveRelationInput): FactionRelation;
|