@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,129 @@
|
|
|
1
|
+
import { generateBuildingDistrict } from "./buildings.js";
|
|
2
|
+
import { resolveTerrainField } from "./terrain.js";
|
|
3
|
+
export function resolveStructureBuildings(descriptor) {
|
|
4
|
+
const columns = Math.max(1, Math.ceil(Math.sqrt(descriptor.count)));
|
|
5
|
+
const rows = Math.max(1, Math.ceil(descriptor.count / columns));
|
|
6
|
+
const spacing = descriptor.spacing;
|
|
7
|
+
const [centerX, centerZ] = descriptor.position ?? [0, 0];
|
|
8
|
+
const origin = [
|
|
9
|
+
centerX - ((columns - 1) * (descriptor.footprint.w + spacing)) / 2,
|
|
10
|
+
centerZ - ((rows - 1) * (descriptor.footprint.d + spacing)) / 2,
|
|
11
|
+
];
|
|
12
|
+
return generateBuildingDistrict({
|
|
13
|
+
rows,
|
|
14
|
+
columns,
|
|
15
|
+
origin,
|
|
16
|
+
lotSize: descriptor.footprint,
|
|
17
|
+
streetWidth: spacing,
|
|
18
|
+
floorRange: descriptor.stories,
|
|
19
|
+
base: { floorHeight: descriptor.storyHeight },
|
|
20
|
+
...(descriptor.seed === undefined ? {} : { seed: descriptor.seed }),
|
|
21
|
+
}).slice(0, descriptor.count);
|
|
22
|
+
}
|
|
23
|
+
const TERRAIN_SAMPLE_RESOLUTION = 16;
|
|
24
|
+
function sampleTerrainHeights(descriptor) {
|
|
25
|
+
const field = resolveTerrainField(descriptor);
|
|
26
|
+
const halfW = descriptor.bounds.w / 2;
|
|
27
|
+
const halfD = descriptor.bounds.d / 2;
|
|
28
|
+
const steps = TERRAIN_SAMPLE_RESOLUTION;
|
|
29
|
+
let min = Infinity;
|
|
30
|
+
let max = -Infinity;
|
|
31
|
+
let sum = 0;
|
|
32
|
+
let finite = true;
|
|
33
|
+
for (let ix = 0; ix <= steps; ix += 1) {
|
|
34
|
+
for (let iz = 0; iz <= steps; iz += 1) {
|
|
35
|
+
const x = -halfW + (descriptor.bounds.w * ix) / steps;
|
|
36
|
+
const z = -halfD + (descriptor.bounds.d * iz) / steps;
|
|
37
|
+
const height = field.sampleHeight(x, z);
|
|
38
|
+
if (!Number.isFinite(height)) {
|
|
39
|
+
finite = false;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (height < min)
|
|
43
|
+
min = height;
|
|
44
|
+
if (height > max)
|
|
45
|
+
max = height;
|
|
46
|
+
sum += height;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const count = (steps + 1) * (steps + 1);
|
|
50
|
+
if (!finite)
|
|
51
|
+
return { min: NaN, max: NaN, mean: NaN, finite: false };
|
|
52
|
+
return { min, max, mean: sum / count, finite: true };
|
|
53
|
+
}
|
|
54
|
+
function unionAabb(buildings) {
|
|
55
|
+
if (buildings.length === 0)
|
|
56
|
+
return { minX: 0, minZ: 0, maxX: 0, maxZ: 0 };
|
|
57
|
+
let minX = Infinity;
|
|
58
|
+
let minZ = Infinity;
|
|
59
|
+
let maxX = -Infinity;
|
|
60
|
+
let maxZ = -Infinity;
|
|
61
|
+
for (const building of buildings) {
|
|
62
|
+
if (building.bounds.minX < minX)
|
|
63
|
+
minX = building.bounds.minX;
|
|
64
|
+
if (building.bounds.minZ < minZ)
|
|
65
|
+
minZ = building.bounds.minZ;
|
|
66
|
+
if (building.bounds.maxX > maxX)
|
|
67
|
+
maxX = building.bounds.maxX;
|
|
68
|
+
if (building.bounds.maxZ > maxZ)
|
|
69
|
+
maxZ = building.bounds.maxZ;
|
|
70
|
+
}
|
|
71
|
+
return { minX, minZ, maxX, maxZ };
|
|
72
|
+
}
|
|
73
|
+
function summarizeStructures(descriptor) {
|
|
74
|
+
const buildings = resolveStructureBuildings(descriptor);
|
|
75
|
+
return {
|
|
76
|
+
style: descriptor.style,
|
|
77
|
+
requested: descriptor.count,
|
|
78
|
+
buildings: buildings.length,
|
|
79
|
+
parts: buildings.reduce((sum, building) => sum + building.parts.length, 0),
|
|
80
|
+
bounds: unionAabb(buildings),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function summarizeTerrain(descriptor) {
|
|
84
|
+
return {
|
|
85
|
+
bounds: descriptor.bounds,
|
|
86
|
+
height: sampleTerrainHeights(descriptor),
|
|
87
|
+
...(descriptor.waterLevel === undefined ? {} : { waterLevel: descriptor.waterLevel }),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function summarizeEnvironment(feature) {
|
|
91
|
+
const terrain = feature.terrain === undefined ? undefined : summarizeTerrain(feature.terrain);
|
|
92
|
+
const structures = (feature.structures ?? []).map(summarizeStructures);
|
|
93
|
+
const vegetation = (feature.vegetation ?? []).map((entry) => ({
|
|
94
|
+
area: entry.area,
|
|
95
|
+
density: entry.density,
|
|
96
|
+
}));
|
|
97
|
+
const water = (feature.water ?? []).map((entry) => ({
|
|
98
|
+
level: entry.level,
|
|
99
|
+
bounds: entry.bounds,
|
|
100
|
+
}));
|
|
101
|
+
const weather = (feature.weather ?? []).map((entry) => ({
|
|
102
|
+
kind: entry.kind,
|
|
103
|
+
density: entry.density,
|
|
104
|
+
}));
|
|
105
|
+
const buildings = structures.reduce((sum, entry) => sum + entry.buildings, 0);
|
|
106
|
+
const counts = {
|
|
107
|
+
terrain: terrain === undefined ? 0 : 1,
|
|
108
|
+
structureGroups: structures.length,
|
|
109
|
+
buildings,
|
|
110
|
+
buildingParts: structures.reduce((sum, entry) => sum + entry.parts, 0),
|
|
111
|
+
vegetationFields: vegetation.length,
|
|
112
|
+
waterBodies: water.length,
|
|
113
|
+
weatherSystems: weather.length,
|
|
114
|
+
};
|
|
115
|
+
const isEmpty = counts.terrain === 0 &&
|
|
116
|
+
buildings === 0 &&
|
|
117
|
+
counts.vegetationFields === 0 &&
|
|
118
|
+
counts.waterBodies === 0 &&
|
|
119
|
+
counts.weatherSystems === 0;
|
|
120
|
+
return {
|
|
121
|
+
...(terrain === undefined ? {} : { terrain }),
|
|
122
|
+
structures,
|
|
123
|
+
vegetation,
|
|
124
|
+
water,
|
|
125
|
+
weather,
|
|
126
|
+
counts,
|
|
127
|
+
isEmpty,
|
|
128
|
+
};
|
|
129
|
+
}
|
package/dist/world/features.d.ts
CHANGED
|
@@ -6,17 +6,36 @@ export type EnvironmentVec2 = readonly [number, number];
|
|
|
6
6
|
export interface EnvironmentArea extends WorldBounds {
|
|
7
7
|
h?: number;
|
|
8
8
|
}
|
|
9
|
+
export interface TerrainColors {
|
|
10
|
+
low?: string;
|
|
11
|
+
high?: string;
|
|
12
|
+
waterline?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface TerrainFlattenMask {
|
|
15
|
+
center: EnvironmentVec2;
|
|
16
|
+
radius: number;
|
|
17
|
+
/** Target flat height; defaults to the noise field's height at `center`. */
|
|
18
|
+
height?: number;
|
|
19
|
+
/** Extra blend-ring width outside `radius` where the flat height smoothsteps back to the noise value; default `radius * 0.5`. */
|
|
20
|
+
falloff?: number;
|
|
21
|
+
}
|
|
9
22
|
export interface TerrainEnvironmentConfig {
|
|
10
23
|
bounds?: WorldBounds;
|
|
11
24
|
height?: number;
|
|
12
25
|
heightMap?: string;
|
|
26
|
+
/** Named palette preset (see `TERRAIN_MATERIAL_PALETTES` in `world/terrain`); default "grass". Overridden field-by-field by `colors`. */
|
|
13
27
|
material?: string;
|
|
28
|
+
/** Explicit low/high/waterline hex colors; any field left unset falls back to the resolved `material` preset. */
|
|
29
|
+
colors?: TerrainColors;
|
|
30
|
+
segments?: number;
|
|
14
31
|
seed?: string;
|
|
15
32
|
frequency?: number;
|
|
16
33
|
octaves?: number;
|
|
17
34
|
ridged?: boolean;
|
|
18
35
|
baseHeight?: number;
|
|
19
36
|
waterLevel?: number;
|
|
37
|
+
/** Flat pads carved into the noise field, e.g. for building pads or spawn circles. */
|
|
38
|
+
flatten?: readonly TerrainFlattenMask[];
|
|
20
39
|
}
|
|
21
40
|
export interface RainEnvironmentConfig {
|
|
22
41
|
area?: EnvironmentArea;
|
|
@@ -46,14 +65,31 @@ export interface GrassEnvironmentConfig {
|
|
|
46
65
|
}
|
|
47
66
|
export interface OceanEnvironmentConfig {
|
|
48
67
|
bounds?: WorldBounds;
|
|
68
|
+
position?: EnvironmentVec2;
|
|
49
69
|
level?: number;
|
|
50
70
|
waveHeight?: number;
|
|
51
71
|
waveScale?: number;
|
|
52
72
|
waveSpeed?: number;
|
|
53
73
|
color?: string;
|
|
54
74
|
}
|
|
75
|
+
export interface SkyEnvironmentConfig {
|
|
76
|
+
/** Fixed look used when `timeOfDay` is off (or no clock is available); default "day". */
|
|
77
|
+
preset?: "day" | "dusk" | "night";
|
|
78
|
+
/** Drive sun/sky from the world clock's `calendar().dayFraction` instead of the fixed `preset`. */
|
|
79
|
+
timeOfDay?: boolean;
|
|
80
|
+
horizonColor?: string;
|
|
81
|
+
zenithColor?: string;
|
|
82
|
+
sunIntensity?: number;
|
|
83
|
+
ambientIntensity?: number;
|
|
84
|
+
fog?: {
|
|
85
|
+
color?: string;
|
|
86
|
+
near?: number;
|
|
87
|
+
far?: number;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
55
90
|
export interface BuildingEnvironmentConfig {
|
|
56
91
|
count?: number;
|
|
92
|
+
position?: EnvironmentVec2;
|
|
57
93
|
footprint?: WorldBounds;
|
|
58
94
|
stories?: readonly [number, number];
|
|
59
95
|
storyHeight?: number;
|
|
@@ -61,6 +97,18 @@ export interface BuildingEnvironmentConfig {
|
|
|
61
97
|
style?: string;
|
|
62
98
|
seed?: string;
|
|
63
99
|
}
|
|
100
|
+
export type PadSize = readonly [number, number] | {
|
|
101
|
+
radius: number;
|
|
102
|
+
};
|
|
103
|
+
export interface PadEnvironmentConfig {
|
|
104
|
+
center: EnvironmentVec2;
|
|
105
|
+
size: PadSize;
|
|
106
|
+
/** Surface offset above the terrain height at `center`; default `0.05` — just proud of the ground. */
|
|
107
|
+
height?: number;
|
|
108
|
+
color?: string;
|
|
109
|
+
/** Rotation applied to rectangular pads; ignored for circular pads. */
|
|
110
|
+
rotationY?: number;
|
|
111
|
+
}
|
|
64
112
|
export type TerrainEnvironmentDescriptor = {
|
|
65
113
|
kind: "terrain";
|
|
66
114
|
} & Required<Pick<TerrainEnvironmentConfig, "bounds" | "height">> & Omit<TerrainEnvironmentConfig, "bounds" | "height">;
|
|
@@ -75,10 +123,16 @@ export type GrassEnvironmentDescriptor = {
|
|
|
75
123
|
} & Required<Pick<GrassEnvironmentConfig, "area" | "density" | "bladeHeight" | "bladeWidth" | "windStrength" | "colors">> & Pick<GrassEnvironmentConfig, "seed">;
|
|
76
124
|
export type OceanEnvironmentDescriptor = {
|
|
77
125
|
kind: "ocean";
|
|
78
|
-
} & Required<Pick<OceanEnvironmentConfig, "bounds" | "level" | "waveHeight" | "waveScale" | "waveSpeed" | "color"
|
|
126
|
+
} & Required<Pick<OceanEnvironmentConfig, "bounds" | "level" | "waveHeight" | "waveScale" | "waveSpeed" | "color">> & Pick<OceanEnvironmentConfig, "position">;
|
|
79
127
|
export type BuildingEnvironmentDescriptor = {
|
|
80
128
|
kind: "building";
|
|
81
|
-
} & Required<Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style">> & Pick<BuildingEnvironmentConfig, "seed">;
|
|
129
|
+
} & Required<Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style">> & Pick<BuildingEnvironmentConfig, "seed" | "position">;
|
|
130
|
+
export type PadEnvironmentDescriptor = {
|
|
131
|
+
kind: "pad";
|
|
132
|
+
} & Required<Pick<PadEnvironmentConfig, "center" | "size" | "height" | "color">> & Pick<PadEnvironmentConfig, "rotationY">;
|
|
133
|
+
export type SkyEnvironmentDescriptor = {
|
|
134
|
+
kind: "sky";
|
|
135
|
+
} & Required<Pick<SkyEnvironmentConfig, "preset" | "timeOfDay">> & Omit<SkyEnvironmentConfig, "preset" | "timeOfDay">;
|
|
82
136
|
export type WeatherEnvironmentDescriptor = RainEnvironmentDescriptor | SnowEnvironmentDescriptor;
|
|
83
137
|
export type VegetationEnvironmentDescriptor = GrassEnvironmentDescriptor;
|
|
84
138
|
export type WaterEnvironmentDescriptor = OceanEnvironmentDescriptor;
|
|
@@ -86,36 +140,59 @@ export type StructureEnvironmentDescriptor = BuildingEnvironmentDescriptor;
|
|
|
86
140
|
export type EnvironmentDescriptorList<T> = T | readonly T[];
|
|
87
141
|
export interface EnvironmentWorldConfig {
|
|
88
142
|
terrain?: TerrainEnvironmentDescriptor;
|
|
143
|
+
sky?: SkyEnvironmentDescriptor;
|
|
89
144
|
weather?: EnvironmentDescriptorList<WeatherEnvironmentDescriptor>;
|
|
90
145
|
vegetation?: EnvironmentDescriptorList<VegetationEnvironmentDescriptor>;
|
|
91
146
|
water?: EnvironmentDescriptorList<WaterEnvironmentDescriptor>;
|
|
92
147
|
structures?: EnvironmentDescriptorList<StructureEnvironmentDescriptor>;
|
|
148
|
+
/** Ground pads, e.g. platforms or paved patches; each implicitly flattens the terrain beneath it. */
|
|
149
|
+
pads?: readonly PadEnvironmentDescriptor[];
|
|
93
150
|
}
|
|
94
151
|
export interface EnvironmentWorldFeature {
|
|
95
152
|
kind: "environment";
|
|
96
153
|
terrain?: TerrainEnvironmentDescriptor;
|
|
154
|
+
sky?: SkyEnvironmentDescriptor;
|
|
97
155
|
weather?: readonly WeatherEnvironmentDescriptor[];
|
|
98
156
|
vegetation?: readonly VegetationEnvironmentDescriptor[];
|
|
99
157
|
water?: readonly WaterEnvironmentDescriptor[];
|
|
100
158
|
structures?: readonly StructureEnvironmentDescriptor[];
|
|
159
|
+
pads?: readonly PadEnvironmentDescriptor[];
|
|
160
|
+
}
|
|
161
|
+
export interface WorldGridCell {
|
|
162
|
+
x: number;
|
|
163
|
+
z: number;
|
|
164
|
+
/** Extruded box height for this cell; falls back to the config's `baseHeight`, then `1`. */
|
|
165
|
+
height?: number;
|
|
166
|
+
/** Per-cell tint; falls back to the config's `defaultColor`. */
|
|
167
|
+
color?: string;
|
|
168
|
+
}
|
|
169
|
+
/** Shared by `biomes()`/`voxel()`/`plots()`/`tilemap()` so the shell can render their declared content as instanced boxes without a hand-written renderer. */
|
|
170
|
+
export interface WorldGridConfig {
|
|
171
|
+
cells?: readonly WorldGridCell[];
|
|
172
|
+
/** World-unit size of one grid cell. Default 1. */
|
|
173
|
+
cellSize?: number;
|
|
174
|
+
/** Height used for cells that omit their own `height`. Default 1. */
|
|
175
|
+
baseHeight?: number;
|
|
176
|
+
/** Color used for cells that omit their own `color`. */
|
|
177
|
+
defaultColor?: string;
|
|
101
178
|
}
|
|
102
|
-
export interface BiomesWorldConfig {
|
|
179
|
+
export interface BiomesWorldConfig extends WorldGridConfig {
|
|
103
180
|
map: string;
|
|
104
181
|
zones: string;
|
|
105
182
|
bounds?: WorldBounds;
|
|
106
183
|
}
|
|
107
|
-
export interface VoxelWorldConfig {
|
|
184
|
+
export interface VoxelWorldConfig extends WorldGridConfig {
|
|
108
185
|
seed: string;
|
|
109
186
|
generate?: string;
|
|
110
187
|
streaming?: {
|
|
111
188
|
radius: number;
|
|
112
189
|
};
|
|
113
190
|
}
|
|
114
|
-
export interface PlotsWorldConfig {
|
|
191
|
+
export interface PlotsWorldConfig extends WorldGridConfig {
|
|
115
192
|
city?: string;
|
|
116
193
|
interiors?: string;
|
|
117
194
|
}
|
|
118
|
-
export interface TilemapWorldConfig {
|
|
195
|
+
export interface TilemapWorldConfig extends WorldGridConfig {
|
|
119
196
|
map: string;
|
|
120
197
|
}
|
|
121
198
|
export type WorldFeature = ({
|
|
@@ -129,13 +206,17 @@ export type WorldFeature = ({
|
|
|
129
206
|
} & TilemapWorldConfig) | EnvironmentWorldFeature | {
|
|
130
207
|
kind: "flat";
|
|
131
208
|
};
|
|
209
|
+
/** Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it. */
|
|
210
|
+
export declare function padFlattenMasks(pads: readonly PadEnvironmentDescriptor[]): readonly TerrainFlattenMask[];
|
|
132
211
|
export declare function environment(config?: EnvironmentWorldConfig): EnvironmentWorldFeature;
|
|
133
212
|
export declare function terrain(config?: TerrainEnvironmentConfig): TerrainEnvironmentDescriptor;
|
|
213
|
+
export declare function sky(config?: SkyEnvironmentConfig): SkyEnvironmentDescriptor;
|
|
134
214
|
export declare function rain(config?: RainEnvironmentConfig): RainEnvironmentDescriptor;
|
|
135
215
|
export declare function snow(config?: SnowEnvironmentConfig): SnowEnvironmentDescriptor;
|
|
136
216
|
export declare function grass(config?: GrassEnvironmentConfig): GrassEnvironmentDescriptor;
|
|
137
217
|
export declare function ocean(config?: OceanEnvironmentConfig): OceanEnvironmentDescriptor;
|
|
138
218
|
export declare function building(config?: BuildingEnvironmentConfig): BuildingEnvironmentDescriptor;
|
|
219
|
+
export declare function pad(config: PadEnvironmentConfig): PadEnvironmentDescriptor;
|
|
139
220
|
export declare function biomes(config: BiomesWorldConfig): WorldFeature;
|
|
140
221
|
export declare function voxel(config: VoxelWorldConfig): WorldFeature;
|
|
141
222
|
export declare function plots(config?: PlotsWorldConfig): WorldFeature;
|
package/dist/world/features.js
CHANGED
|
@@ -7,18 +7,39 @@ function list(value) {
|
|
|
7
7
|
function withOptional(base, optional) {
|
|
8
8
|
return optional === undefined ? base : { ...base, ...optional };
|
|
9
9
|
}
|
|
10
|
+
/** Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it. */
|
|
11
|
+
export function padFlattenMasks(pads) {
|
|
12
|
+
return pads.map((padDescriptor) => ({
|
|
13
|
+
center: padDescriptor.center,
|
|
14
|
+
radius: padHalfExtent(padDescriptor.size) * 1.2,
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
function padHalfExtent(size) {
|
|
18
|
+
return "radius" in size ? size.radius : Math.max(size[0], size[1]) / 2;
|
|
19
|
+
}
|
|
20
|
+
function withPadFlatten(terrainDescriptor, pads) {
|
|
21
|
+
if (terrainDescriptor === undefined || pads === undefined || pads.length === 0)
|
|
22
|
+
return terrainDescriptor;
|
|
23
|
+
return {
|
|
24
|
+
...terrainDescriptor,
|
|
25
|
+
flatten: [...(terrainDescriptor.flatten ?? []), ...padFlattenMasks(pads)],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
10
28
|
export function environment(config = {}) {
|
|
11
29
|
const weather = list(config.weather);
|
|
12
30
|
const vegetation = list(config.vegetation);
|
|
13
31
|
const water = list(config.water);
|
|
14
32
|
const structures = list(config.structures);
|
|
33
|
+
const terrainDescriptor = withPadFlatten(config.terrain, config.pads);
|
|
15
34
|
return {
|
|
16
35
|
kind: "environment",
|
|
17
|
-
...(
|
|
36
|
+
...(terrainDescriptor === undefined ? {} : { terrain: terrainDescriptor }),
|
|
37
|
+
...(config.sky === undefined ? {} : { sky: config.sky }),
|
|
18
38
|
...(weather === undefined ? {} : { weather }),
|
|
19
39
|
...(vegetation === undefined ? {} : { vegetation }),
|
|
20
40
|
...(water === undefined ? {} : { water }),
|
|
21
41
|
...(structures === undefined ? {} : { structures }),
|
|
42
|
+
...(config.pads === undefined ? {} : { pads: config.pads }),
|
|
22
43
|
};
|
|
23
44
|
}
|
|
24
45
|
export function terrain(config = {}) {
|
|
@@ -29,12 +50,28 @@ export function terrain(config = {}) {
|
|
|
29
50
|
}, {
|
|
30
51
|
...(config.heightMap === undefined ? {} : { heightMap: config.heightMap }),
|
|
31
52
|
...(config.material === undefined ? {} : { material: config.material }),
|
|
53
|
+
...(config.colors === undefined ? {} : { colors: config.colors }),
|
|
54
|
+
...(config.segments === undefined ? {} : { segments: config.segments }),
|
|
32
55
|
...(config.seed === undefined ? {} : { seed: config.seed }),
|
|
33
56
|
...(config.frequency === undefined ? {} : { frequency: config.frequency }),
|
|
34
57
|
...(config.octaves === undefined ? {} : { octaves: config.octaves }),
|
|
35
58
|
...(config.ridged === undefined ? {} : { ridged: config.ridged }),
|
|
36
59
|
...(config.baseHeight === undefined ? {} : { baseHeight: config.baseHeight }),
|
|
37
60
|
...(config.waterLevel === undefined ? {} : { waterLevel: config.waterLevel }),
|
|
61
|
+
...(config.flatten === undefined ? {} : { flatten: config.flatten }),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export function sky(config = {}) {
|
|
65
|
+
return withOptional({
|
|
66
|
+
kind: "sky",
|
|
67
|
+
preset: config.preset ?? "day",
|
|
68
|
+
timeOfDay: config.timeOfDay ?? false,
|
|
69
|
+
}, {
|
|
70
|
+
...(config.horizonColor === undefined ? {} : { horizonColor: config.horizonColor }),
|
|
71
|
+
...(config.zenithColor === undefined ? {} : { zenithColor: config.zenithColor }),
|
|
72
|
+
...(config.sunIntensity === undefined ? {} : { sunIntensity: config.sunIntensity }),
|
|
73
|
+
...(config.ambientIntensity === undefined ? {} : { ambientIntensity: config.ambientIntensity }),
|
|
74
|
+
...(config.fog === undefined ? {} : { fog: config.fog }),
|
|
38
75
|
});
|
|
39
76
|
}
|
|
40
77
|
export function rain(config = {}) {
|
|
@@ -80,6 +117,7 @@ export function ocean(config = {}) {
|
|
|
80
117
|
waveScale: config.waveScale ?? 18,
|
|
81
118
|
waveSpeed: config.waveSpeed ?? 0.55,
|
|
82
119
|
color: config.color ?? "#1d7fa3",
|
|
120
|
+
...(config.position === undefined ? {} : { position: config.position }),
|
|
83
121
|
};
|
|
84
122
|
}
|
|
85
123
|
export function building(config = {}) {
|
|
@@ -91,7 +129,19 @@ export function building(config = {}) {
|
|
|
91
129
|
storyHeight: config.storyHeight ?? 3,
|
|
92
130
|
spacing: config.spacing ?? 2,
|
|
93
131
|
style: config.style ?? "generic",
|
|
94
|
-
},
|
|
132
|
+
}, {
|
|
133
|
+
...(config.seed === undefined ? {} : { seed: config.seed }),
|
|
134
|
+
...(config.position === undefined ? {} : { position: config.position }),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
export function pad(config) {
|
|
138
|
+
return withOptional({
|
|
139
|
+
kind: "pad",
|
|
140
|
+
center: config.center,
|
|
141
|
+
size: config.size,
|
|
142
|
+
height: config.height ?? 0.05,
|
|
143
|
+
color: config.color ?? "#8b8680",
|
|
144
|
+
}, config.rotationY === undefined ? undefined : { rotationY: config.rotationY });
|
|
95
145
|
}
|
|
96
146
|
export function biomes(config) {
|
|
97
147
|
return { kind: "biomes", ...config };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface FogBounds {
|
|
2
|
+
minX: number;
|
|
3
|
+
minZ: number;
|
|
4
|
+
maxX: number;
|
|
5
|
+
maxZ: number;
|
|
6
|
+
}
|
|
7
|
+
export interface FogConfig {
|
|
8
|
+
bounds: FogBounds;
|
|
9
|
+
cellSize: number;
|
|
10
|
+
/** Cells revealed on construction (start-of-run known area). */
|
|
11
|
+
revealed?: readonly number[];
|
|
12
|
+
}
|
|
13
|
+
export interface FogCells {
|
|
14
|
+
cols: number;
|
|
15
|
+
rows: number;
|
|
16
|
+
minX: number;
|
|
17
|
+
minZ: number;
|
|
18
|
+
cellSize: number;
|
|
19
|
+
/** Row-major flags; index = row * cols + col. */
|
|
20
|
+
revealed: readonly boolean[];
|
|
21
|
+
revealedCount: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Reveal-on-event fog of war over a fixed grid. Walking (`revealAlong`) and
|
|
25
|
+
* digging/acting (`reveal`) clear cells; once revealed a cell stays revealed.
|
|
26
|
+
* Pure and renderer-free — the shell/react map draws `cells()`.
|
|
27
|
+
*/
|
|
28
|
+
export interface FogField {
|
|
29
|
+
reveal(x: number, z: number, radius?: number): number;
|
|
30
|
+
revealAlong(from: readonly [number, number], to: readonly [number, number], radius?: number): number;
|
|
31
|
+
isRevealed(x: number, z: number): boolean;
|
|
32
|
+
isRevealedCell(col: number, row: number): boolean;
|
|
33
|
+
revealedCount(): number;
|
|
34
|
+
cellCount(): number;
|
|
35
|
+
fraction(): number;
|
|
36
|
+
cells(): FogCells;
|
|
37
|
+
reset(): void;
|
|
38
|
+
subscribe(listener: () => void): () => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function createFogField(config: FogConfig): FogField;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
function clampInt(value, min, max) {
|
|
2
|
+
const rounded = Math.floor(value);
|
|
3
|
+
return rounded < min ? min : rounded > max ? max : rounded;
|
|
4
|
+
}
|
|
5
|
+
export function createFogField(config) {
|
|
6
|
+
const { minX, minZ, maxX, maxZ } = config.bounds;
|
|
7
|
+
const cellSize = config.cellSize;
|
|
8
|
+
if (cellSize <= 0)
|
|
9
|
+
throw new Error("createFogField: cellSize must be positive");
|
|
10
|
+
const cols = Math.max(1, Math.ceil((maxX - minX) / cellSize));
|
|
11
|
+
const rows = Math.max(1, Math.ceil((maxZ - minZ) / cellSize));
|
|
12
|
+
const total = cols * rows;
|
|
13
|
+
const revealed = new Array(total).fill(false);
|
|
14
|
+
let revealedCount = 0;
|
|
15
|
+
const listeners = new Set();
|
|
16
|
+
let snapshotCache = null;
|
|
17
|
+
function colOf(x) {
|
|
18
|
+
return clampInt((x - minX) / cellSize, 0, cols - 1);
|
|
19
|
+
}
|
|
20
|
+
function rowOf(z) {
|
|
21
|
+
return clampInt((z - minZ) / cellSize, 0, rows - 1);
|
|
22
|
+
}
|
|
23
|
+
function markCell(col, row) {
|
|
24
|
+
if (col < 0 || col >= cols || row < 0 || row >= rows)
|
|
25
|
+
return false;
|
|
26
|
+
const index = row * cols + col;
|
|
27
|
+
if (revealed[index])
|
|
28
|
+
return false;
|
|
29
|
+
revealed[index] = true;
|
|
30
|
+
revealedCount += 1;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
function notify(changed) {
|
|
34
|
+
if (changed === 0)
|
|
35
|
+
return;
|
|
36
|
+
snapshotCache = null;
|
|
37
|
+
for (const listener of listeners)
|
|
38
|
+
listener();
|
|
39
|
+
}
|
|
40
|
+
function revealAt(x, z, radius) {
|
|
41
|
+
const centerCol = colOf(x);
|
|
42
|
+
const centerRow = rowOf(z);
|
|
43
|
+
let changed = markCell(centerCol, centerRow) ? 1 : 0;
|
|
44
|
+
if (radius <= 0)
|
|
45
|
+
return changed;
|
|
46
|
+
const span = Math.ceil(radius / cellSize);
|
|
47
|
+
const radiusSq = radius * radius;
|
|
48
|
+
for (let dr = -span; dr <= span; dr += 1) {
|
|
49
|
+
for (let dc = -span; dc <= span; dc += 1) {
|
|
50
|
+
if (dc === 0 && dr === 0)
|
|
51
|
+
continue;
|
|
52
|
+
const cellX = minX + (centerCol + dc + 0.5) * cellSize;
|
|
53
|
+
const cellZ = minZ + (centerRow + dr + 0.5) * cellSize;
|
|
54
|
+
const dx = cellX - x;
|
|
55
|
+
const dz = cellZ - z;
|
|
56
|
+
if (dx * dx + dz * dz > radiusSq)
|
|
57
|
+
continue;
|
|
58
|
+
if (markCell(centerCol + dc, centerRow + dr))
|
|
59
|
+
changed += 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return changed;
|
|
63
|
+
}
|
|
64
|
+
if (config.revealed !== undefined) {
|
|
65
|
+
for (const index of config.revealed) {
|
|
66
|
+
if (index >= 0 && index < total && !revealed[index]) {
|
|
67
|
+
revealed[index] = true;
|
|
68
|
+
revealedCount += 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
snapshotCache = null;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
reveal(x, z, radius = 0) {
|
|
75
|
+
const changed = revealAt(x, z, radius);
|
|
76
|
+
notify(changed);
|
|
77
|
+
return changed;
|
|
78
|
+
},
|
|
79
|
+
revealAlong(from, to, radius = cellSize) {
|
|
80
|
+
const dx = to[0] - from[0];
|
|
81
|
+
const dz = to[1] - from[1];
|
|
82
|
+
const distance = Math.hypot(dx, dz);
|
|
83
|
+
const steps = Math.max(1, Math.ceil(distance / cellSize));
|
|
84
|
+
let changed = 0;
|
|
85
|
+
for (let step = 0; step <= steps; step += 1) {
|
|
86
|
+
const t = step / steps;
|
|
87
|
+
changed += revealAt(from[0] + dx * t, from[1] + dz * t, radius);
|
|
88
|
+
}
|
|
89
|
+
notify(changed);
|
|
90
|
+
return changed;
|
|
91
|
+
},
|
|
92
|
+
isRevealed(x, z) {
|
|
93
|
+
return revealed[rowOf(z) * cols + colOf(x)] === true;
|
|
94
|
+
},
|
|
95
|
+
isRevealedCell(col, row) {
|
|
96
|
+
if (col < 0 || col >= cols || row < 0 || row >= rows)
|
|
97
|
+
return false;
|
|
98
|
+
return revealed[row * cols + col] === true;
|
|
99
|
+
},
|
|
100
|
+
revealedCount() {
|
|
101
|
+
return revealedCount;
|
|
102
|
+
},
|
|
103
|
+
cellCount() {
|
|
104
|
+
return total;
|
|
105
|
+
},
|
|
106
|
+
fraction() {
|
|
107
|
+
return total === 0 ? 0 : revealedCount / total;
|
|
108
|
+
},
|
|
109
|
+
cells() {
|
|
110
|
+
if (snapshotCache === null) {
|
|
111
|
+
snapshotCache = {
|
|
112
|
+
cols,
|
|
113
|
+
rows,
|
|
114
|
+
minX,
|
|
115
|
+
minZ,
|
|
116
|
+
cellSize,
|
|
117
|
+
revealed: revealed.slice(),
|
|
118
|
+
revealedCount,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return snapshotCache;
|
|
122
|
+
},
|
|
123
|
+
reset() {
|
|
124
|
+
if (revealedCount === 0)
|
|
125
|
+
return;
|
|
126
|
+
revealed.fill(false);
|
|
127
|
+
revealedCount = 0;
|
|
128
|
+
notify(1);
|
|
129
|
+
},
|
|
130
|
+
subscribe(listener) {
|
|
131
|
+
listeners.add(listener);
|
|
132
|
+
return () => {
|
|
133
|
+
listeners.delete(listener);
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WorldGridConfig } from "./features.js";
|
|
2
|
+
export interface GridInstanceTransform {
|
|
3
|
+
position: readonly [number, number, number];
|
|
4
|
+
scale: readonly [number, number, number];
|
|
5
|
+
color: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Pure cell→instance math for `biomes()`/`voxel()`/`plots()`/`tilemap()` world features: one
|
|
9
|
+
* extruded box per declared cell, grounded at y=0 and centered on its cell in x/z. Consumed by
|
|
10
|
+
* the shell's grid-world renderer so it can drive a single `THREE.InstancedMesh` without
|
|
11
|
+
* duplicating this math per game.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveGridInstances(config: WorldGridConfig): readonly GridInstanceTransform[];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const DEFAULT_CELL_SIZE = 1;
|
|
2
|
+
const DEFAULT_BASE_HEIGHT = 1;
|
|
3
|
+
const DEFAULT_GRID_COLOR = "#8a8f98";
|
|
4
|
+
/**
|
|
5
|
+
* Pure cell→instance math for `biomes()`/`voxel()`/`plots()`/`tilemap()` world features: one
|
|
6
|
+
* extruded box per declared cell, grounded at y=0 and centered on its cell in x/z. Consumed by
|
|
7
|
+
* the shell's grid-world renderer so it can drive a single `THREE.InstancedMesh` without
|
|
8
|
+
* duplicating this math per game.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveGridInstances(config) {
|
|
11
|
+
const cells = config.cells ?? [];
|
|
12
|
+
const cellSize = config.cellSize ?? DEFAULT_CELL_SIZE;
|
|
13
|
+
const defaultColor = config.defaultColor ?? DEFAULT_GRID_COLOR;
|
|
14
|
+
return cells.map((cell) => {
|
|
15
|
+
const height = cell.height ?? config.baseHeight ?? DEFAULT_BASE_HEIGHT;
|
|
16
|
+
return {
|
|
17
|
+
position: [cell.x * cellSize, height / 2, cell.z * cellSize],
|
|
18
|
+
scale: [cellSize, height, cellSize],
|
|
19
|
+
color: cell.color ?? defaultColor,
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type MarkerPosition = readonly [number, number, number];
|
|
2
|
+
export interface MapMarker<TMeta = unknown> {
|
|
3
|
+
id: string;
|
|
4
|
+
kind: string;
|
|
5
|
+
position: MarkerPosition;
|
|
6
|
+
label?: string;
|
|
7
|
+
owner?: string;
|
|
8
|
+
createdAt: number;
|
|
9
|
+
expiresAt?: number;
|
|
10
|
+
meta?: TMeta;
|
|
11
|
+
}
|
|
12
|
+
export interface MarkerInput<TMeta = unknown> {
|
|
13
|
+
id?: string;
|
|
14
|
+
kind: string;
|
|
15
|
+
position: MarkerPosition;
|
|
16
|
+
label?: string;
|
|
17
|
+
owner?: string;
|
|
18
|
+
createdAt?: number;
|
|
19
|
+
expiresAt?: number;
|
|
20
|
+
meta?: TMeta;
|
|
21
|
+
}
|
|
22
|
+
export interface MarkerQuery {
|
|
23
|
+
kind?: string;
|
|
24
|
+
owner?: string;
|
|
25
|
+
near?: MarkerPosition;
|
|
26
|
+
radius?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Visual descriptor for a marker kind. Games supply their own palette; the
|
|
30
|
+
* engine ships `DEFAULT_MARKER_KINDS` as a content-agnostic starting set that
|
|
31
|
+
* the react minimap/compass read for colors and glyphs.
|
|
32
|
+
*/
|
|
33
|
+
export interface MarkerKindStyle {
|
|
34
|
+
id: string;
|
|
35
|
+
color: string;
|
|
36
|
+
glyph: string;
|
|
37
|
+
priority: number;
|
|
38
|
+
}
|
|
39
|
+
export declare const DEFAULT_MARKER_KINDS: Record<string, MarkerKindStyle>;
|
|
40
|
+
export declare function markerKindStyle(kind: string, styles?: Record<string, MarkerKindStyle>): MarkerKindStyle;
|
|
41
|
+
export interface MarkerSet<TMeta = unknown> {
|
|
42
|
+
add(marker: MarkerInput<TMeta>): string;
|
|
43
|
+
remove(id: string): boolean;
|
|
44
|
+
get(id: string): MapMarker<TMeta> | null;
|
|
45
|
+
list(): readonly MapMarker<TMeta>[];
|
|
46
|
+
query(query: MarkerQuery): MapMarker<TMeta>[];
|
|
47
|
+
prune(now: number): number;
|
|
48
|
+
clear(): void;
|
|
49
|
+
subscribe(listener: () => void): () => void;
|
|
50
|
+
snapshot(): readonly MapMarker<TMeta>[];
|
|
51
|
+
}
|
|
52
|
+
export declare function createMarkerSet<TMeta = unknown>(now?: () => number): MarkerSet<TMeta>;
|