@jgengine/core 0.11.0 → 0.13.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 +52 -0
- package/dist/ai/populationDirector.d.ts +103 -0
- package/dist/ai/populationDirector.js +190 -0
- package/dist/ai/pursuit.d.ts +56 -0
- package/dist/ai/pursuit.js +53 -0
- package/dist/authoring.d.ts +1 -1
- package/dist/authoring.js +1 -1
- package/dist/combat/effects.d.ts +11 -4
- package/dist/combat/effects.js +24 -17
- package/dist/combat/magazine.d.ts +13 -0
- package/dist/combat/magazine.js +27 -0
- package/dist/combat/vfxPresets.d.ts +176 -0
- package/dist/combat/vfxPresets.js +74 -0
- package/dist/combat/weaponFire.d.ts +114 -0
- package/dist/combat/weaponFire.js +96 -0
- package/dist/combat.d.ts +5 -2
- package/dist/combat.js +4 -1
- package/dist/devtools/controls.d.ts +38 -0
- package/dist/devtools/controls.js +181 -0
- package/dist/devtools/devtools.d.ts +5 -197
- package/dist/devtools/devtools.js +38 -774
- package/dist/devtools/discover.d.ts +32 -0
- package/dist/devtools/discover.js +240 -0
- package/dist/devtools/fallbackSeams.d.ts +50 -0
- package/dist/devtools/fallbackSeams.js +111 -0
- package/dist/devtools/frame.d.ts +15 -0
- package/dist/devtools/frame.js +247 -0
- package/dist/devtools/logs.d.ts +15 -0
- package/dist/devtools/logs.js +86 -0
- package/dist/devtools/overrides.d.ts +16 -0
- package/dist/devtools/overrides.js +66 -0
- package/dist/devtools/types.d.ts +212 -0
- package/dist/devtools/types.js +1 -0
- package/dist/devtools/urlFlags.d.ts +22 -0
- package/dist/devtools/urlFlags.js +62 -0
- package/dist/economy/auctionBook.d.ts +122 -0
- package/dist/economy/auctionBook.js +213 -0
- package/dist/economy/priceHistory.d.ts +44 -0
- package/dist/economy/priceHistory.js +89 -0
- package/dist/economy/unlockPoints.d.ts +92 -0
- package/dist/economy/unlockPoints.js +103 -0
- package/dist/editor/commandApply.d.ts +5 -0
- package/dist/editor/commandApply.js +755 -0
- package/dist/editor/commands.d.ts +48 -3
- package/dist/editor/commands.js +40 -754
- package/dist/editor/directives.d.ts +94 -0
- package/dist/editor/directives.js +206 -0
- package/dist/editor/document.d.ts +37 -2
- package/dist/editor/document.js +422 -6
- package/dist/editor/entityCatalog.d.ts +42 -0
- package/dist/editor/entityCatalog.js +101 -0
- package/dist/editor/environment.d.ts +74 -0
- package/dist/editor/environment.js +169 -0
- package/dist/editor/index.d.ts +26 -1
- package/dist/editor/index.js +8 -1
- package/dist/editor/minimap.d.ts +31 -0
- package/dist/editor/minimap.js +73 -0
- package/dist/editor/streamer.d.ts +66 -0
- package/dist/editor/streamer.js +68 -0
- package/dist/editor/types.d.ts +143 -0
- package/dist/editor/world.d.ts +136 -0
- package/dist/editor/world.js +303 -0
- package/dist/game/breeding.d.ts +161 -0
- package/dist/game/breeding.js +181 -0
- package/dist/game/cameraConfig.d.ts +451 -0
- package/dist/game/cameraConfig.js +1 -0
- package/dist/game/clipRoles.d.ts +51 -0
- package/dist/game/clipRoles.js +142 -0
- package/dist/game/defineGame.d.ts +16 -9
- package/dist/game/defineGame.js +14 -6
- package/dist/game/defineSystem.d.ts +2 -0
- package/dist/game/defineSystem.js +2 -0
- package/dist/game/events.d.ts +28 -0
- package/dist/game/partAnimation.d.ts +78 -0
- package/dist/game/partAnimation.js +147 -0
- package/dist/game/playableGame.d.ts +72 -440
- package/dist/game/playableGame.js +1 -1
- package/dist/game/progression.d.ts +35 -0
- package/dist/game/progression.js +22 -1
- package/dist/game/quest.js +1 -3
- package/dist/game/tribe.d.ts +184 -0
- package/dist/game/tribe.js +432 -0
- package/dist/gameplay.d.ts +3 -1
- package/dist/gameplay.js +3 -1
- package/dist/inventory/encumbrance.d.ts +69 -0
- package/dist/inventory/encumbrance.js +68 -0
- package/dist/inventory/inventoryModel.d.ts +24 -8
- package/dist/inventory/inventoryModel.js +47 -8
- package/dist/item/durability.js +1 -3
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +48 -1
- package/dist/movement/movementModel.d.ts +15 -1
- package/dist/movement/movementModel.js +33 -15
- package/dist/movement/playerMovement.js +102 -2
- package/dist/multiplayer/combatSnapshot.js +13 -8
- package/dist/multiplayer/presenceModel.d.ts +2 -0
- package/dist/multiplayer/presenceModel.js +8 -0
- package/dist/physics/drivableVehicle.d.ts +2 -0
- package/dist/physics/drivableVehicle.js +1 -1
- package/dist/physics/flightDynamics.d.ts +82 -0
- package/dist/physics/flightDynamics.js +165 -0
- package/dist/physics/gravityField.d.ts +25 -0
- package/dist/physics/gravityField.js +41 -0
- package/dist/physics/kinematicVehicle.d.ts +87 -0
- package/dist/physics/kinematicVehicle.js +193 -11
- package/dist/physics/physicsWorld.d.ts +26 -0
- package/dist/physics/physicsWorld.js +109 -117
- package/dist/physics/vehicleObstacles.d.ts +44 -0
- package/dist/physics/vehicleObstacles.js +115 -0
- package/dist/random/rng.d.ts +4 -1
- package/dist/random/rng.js +4 -1
- package/dist/runtime/adapter.d.ts +13 -0
- package/dist/runtime/adapter.js +13 -0
- package/dist/runtime/context/combat.d.ts +51 -0
- package/dist/runtime/context/combat.js +131 -0
- package/dist/runtime/context/combatFx.js +15 -5
- package/dist/runtime/context/deathLoot.d.ts +30 -0
- package/dist/runtime/context/deathLoot.js +39 -0
- package/dist/runtime/context/player.d.ts +48 -0
- package/dist/runtime/context/player.js +125 -0
- package/dist/runtime/context/scene.d.ts +60 -0
- package/dist/runtime/context/scene.js +329 -0
- package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
- package/dist/runtime/context/snapshotCodecs.js +26 -0
- package/dist/runtime/descriptors/baseline.js +26 -3
- package/dist/runtime/descriptors/features.d.ts +5 -0
- package/dist/runtime/descriptors/features.js +20 -2
- package/dist/runtime/gameContext.d.ts +2 -428
- package/dist/runtime/gameContext.js +133 -500
- package/dist/runtime/gameContextTypes.d.ts +500 -0
- package/dist/runtime/gameContextTypes.js +1 -0
- package/dist/runtime/headlessRunner.d.ts +17 -2
- package/dist/runtime/headlessRunner.js +15 -0
- package/dist/runtime/hostPolicy.d.ts +51 -0
- package/dist/runtime/hostPolicy.js +68 -0
- package/dist/runtime/hostedGameRunner.d.ts +3 -1
- package/dist/runtime/hostedGameRunner.js +1 -0
- package/dist/runtime/hostedWorldSession.d.ts +3 -1
- package/dist/runtime/hostedWorldSession.js +1 -0
- package/dist/runtime/worldSnapshot.d.ts +13 -1
- package/dist/runtime/worldSnapshot.js +15 -3
- package/dist/scene/assetCatalog.d.ts +7 -0
- package/dist/scene/assetSpace.js +1 -3
- package/dist/scene/authoredTriggers.d.ts +50 -0
- package/dist/scene/authoredTriggers.js +114 -0
- package/dist/scene/builtinSceneKinds.js +8 -3
- package/dist/scene/colliders.d.ts +79 -0
- package/dist/scene/colliders.js +170 -0
- package/dist/scene/collisionMesh.d.ts +94 -0
- package/dist/scene/collisionMesh.js +699 -0
- package/dist/scene/companion.d.ts +147 -0
- package/dist/scene/companion.js +193 -0
- package/dist/scene/entityStats.d.ts +7 -9
- package/dist/scene/entityStats.js +19 -15
- package/dist/scene/sceneKinds.d.ts +16 -0
- package/dist/scene/sceneKinds.js +4 -0
- package/dist/scene/sceneRaycast.js +15 -0
- package/dist/stats/spawnLevelStats.d.ts +110 -0
- package/dist/stats/spawnLevelStats.js +140 -0
- package/dist/stats/statModifiers.d.ts +16 -0
- package/dist/stats/statModifiers.js +30 -1
- package/dist/stats/statPool.d.ts +70 -0
- package/dist/stats/statPool.js +62 -0
- package/dist/store/changeSignal.d.ts +3 -0
- package/dist/store/changeSignal.js +2 -0
- package/dist/store/observableKeyedStore.d.ts +2 -0
- package/dist/store/observableKeyedStore.js +1 -0
- package/dist/survival/decayMeter.js +1 -3
- package/dist/survival/regionHealth.js +1 -3
- package/dist/ui/hudLayout.d.ts +5 -0
- package/dist/ui/hudLayout.js +17 -1
- package/dist/ui/panelModel.d.ts +120 -0
- package/dist/ui/panelModel.js +168 -0
- package/dist/ui.d.ts +1 -0
- package/dist/ui.js +1 -0
- package/dist/world/authoredEntities.d.ts +29 -0
- package/dist/world/authoredEntities.js +29 -0
- package/dist/world/authoredObjects.d.ts +15 -1
- package/dist/world/authoredObjects.js +13 -1
- package/dist/world/authoredSpawn.d.ts +39 -0
- package/dist/world/authoredSpawn.js +37 -0
- package/dist/world/buildSockets.d.ts +152 -0
- package/dist/world/buildSockets.js +188 -0
- package/dist/world/buildingLots.d.ts +84 -0
- package/dist/world/buildingLots.js +119 -0
- package/dist/world/buildings.d.ts +6 -0
- package/dist/world/cityBlocks.d.ts +152 -0
- package/dist/world/cityBlocks.js +670 -0
- package/dist/world/cityContent.d.ts +77 -0
- package/dist/world/cityContent.js +261 -0
- package/dist/world/cityGenerator.d.ts +41 -0
- package/dist/world/cityGenerator.js +58 -0
- package/dist/world/cityGeometry.d.ts +114 -0
- package/dist/world/cityGeometry.js +573 -0
- package/dist/world/cityKind.d.ts +314 -0
- package/dist/world/cityKind.js +1578 -0
- package/dist/world/environmentSummary.js +57 -1
- package/dist/world/features.d.ts +53 -5
- package/dist/world/features.js +15 -3
- package/dist/world/grassKind.js +2 -2
- package/dist/world/markers.d.ts +50 -4
- package/dist/world/markers.js +49 -0
- package/dist/world/massing.js +1 -1
- package/dist/world/minimapBake.d.ts +78 -0
- package/dist/world/minimapBake.js +208 -0
- package/dist/world/minimapTrack.d.ts +17 -0
- package/dist/world/minimapTrack.js +22 -0
- package/dist/world/place.d.ts +166 -0
- package/dist/world/place.js +96 -0
- package/dist/world/placeAsset.js +3 -1
- package/dist/world/poleLineKind.d.ts +40 -0
- package/dist/world/poleLineKind.js +97 -0
- package/dist/world/polyline.js +9 -2
- package/dist/world/resourceNode.d.ts +109 -0
- package/dist/world/resourceNode.js +140 -0
- package/dist/world/roads.d.ts +19 -2
- package/dist/world/roads.js +60 -3
- package/dist/world/scatter.js +2 -20
- package/dist/world/streetGenerator.d.ts +159 -0
- package/dist/world/streetGenerator.js +684 -0
- package/dist/world/streets.d.ts +103 -1
- package/dist/world/streets.js +273 -0
- package/dist/world/terraform.js +58 -88
- package/dist/world/terrain.d.ts +65 -1
- package/dist/world/terrain.js +119 -2
- package/dist/world.d.ts +14 -5
- package/dist/world.js +12 -3
- package/package.json +1 -1
- package/skills/game-design/SKILL.md +28 -0
- package/skills/game-design/agents/openai.yaml +4 -0
- package/skills/game-design/references/application-playbook.md +170 -0
- package/skills/game-design/references/field-guide.md +288 -0
- package/skills/jgengine/SKILL.md +8 -4
- package/skills/jgengine/api.md +182 -81
- package/skills/jgengine/capabilities.md +104 -2
- package/skills/jgengine/recipes/minimal-game.md +88 -0
- package/skills/jgengine-combat/SKILL.md +19 -1
- package/skills/jgengine-combat/api.md +75 -2
- package/skills/jgengine-combat/capabilities.md +61 -1
- package/skills/jgengine-combat/recipes/portable-damage-effects.md +197 -0
- package/skills/jgengine-combat/recipes/portable-runtime-state.md +154 -0
- package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
- package/skills/jgengine-combat/recipes/portable-weapon-plumbing.md +156 -0
- package/skills/jgengine-combat/recipes/visible-attacks.md +81 -0
- package/skills/jgengine-combat/reference.md +33 -4
- package/skills/jgengine-gameplay/SKILL.md +8 -0
- package/skills/jgengine-gameplay/api.md +182 -26
- package/skills/jgengine-gameplay/capabilities.md +96 -2
- package/skills/jgengine-gameplay/recipes/portable-xp-leveling.md +168 -0
- package/skills/jgengine-gameplay/reference-systems.md +10 -5
- package/skills/jgengine-world/SKILL.md +13 -0
- package/skills/jgengine-world/api.md +453 -21
- package/skills/jgengine-world/capabilities.md +112 -2
- package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
- package/skills/jgengine-world/reference.md +43 -4
- package/skills/jgengine/reference-cartridge.md +0 -72
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { generateBuildingDistrict, resolveBuildingPalette, } from "./buildings.js";
|
|
1
|
+
import { DEFAULT_BUILDING_CONFIG, generateBuilding, generateBuildingDistrict, resolveBuildingPalette, } from "./buildings.js";
|
|
2
|
+
import { deriveBuildingLots } from "./buildingLots.js";
|
|
2
3
|
import { pathLength } from "./roads.js";
|
|
3
4
|
import { resolveTerrainField, resolveTerrainPalette } from "./terrain.js";
|
|
4
5
|
export function resolveStructureBuildings(descriptor) {
|
|
6
|
+
if (descriptor.along !== undefined) {
|
|
7
|
+
return resolveFrontageBuildings(descriptor, descriptor.along);
|
|
8
|
+
}
|
|
5
9
|
const columns = Math.max(1, Math.ceil(Math.sqrt(descriptor.count)));
|
|
6
10
|
const rows = Math.max(1, Math.ceil(descriptor.count / columns));
|
|
7
11
|
const spacing = descriptor.spacing;
|
|
@@ -21,6 +25,58 @@ export function resolveStructureBuildings(descriptor) {
|
|
|
21
25
|
...(descriptor.seed === undefined ? {} : { seed: descriptor.seed }),
|
|
22
26
|
}).slice(0, descriptor.count);
|
|
23
27
|
}
|
|
28
|
+
function hashU32(input) {
|
|
29
|
+
let hash = 2166136261;
|
|
30
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
31
|
+
hash ^= input.charCodeAt(i);
|
|
32
|
+
hash = Math.imul(hash, 16777619);
|
|
33
|
+
}
|
|
34
|
+
hash ^= hash >>> 16;
|
|
35
|
+
hash = Math.imul(hash, 2246822507);
|
|
36
|
+
hash ^= hash >>> 13;
|
|
37
|
+
hash = Math.imul(hash, 3266489909);
|
|
38
|
+
hash ^= hash >>> 16;
|
|
39
|
+
return hash >>> 0;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build one street-aware building per road-frontage lot: massed at the lot center, its `rotationY`
|
|
43
|
+
* set so the front faces the road (the renderer turns the whole building about `center`), floors
|
|
44
|
+
* rolled deterministically from the descriptor's `stories` and the lot's road/side/index.
|
|
45
|
+
*/
|
|
46
|
+
function resolveFrontageBuildings(descriptor, along) {
|
|
47
|
+
const seed = descriptor.seed ?? "buildings";
|
|
48
|
+
const lots = deriveBuildingLots({
|
|
49
|
+
roads: along.roads,
|
|
50
|
+
footprint: descriptor.footprint,
|
|
51
|
+
spacing: descriptor.spacing,
|
|
52
|
+
maxLots: descriptor.count,
|
|
53
|
+
seed: `${seed}:lots`,
|
|
54
|
+
...(along.setback === undefined ? {} : { setback: along.setback }),
|
|
55
|
+
...(along.bothSides === undefined ? {} : { bothSides: along.bothSides }),
|
|
56
|
+
...(along.area === undefined ? {} : { area: along.area }),
|
|
57
|
+
});
|
|
58
|
+
const [floorsMin, floorsMax] = descriptor.stories;
|
|
59
|
+
const bayWidth = DEFAULT_BUILDING_CONFIG.bayWidth;
|
|
60
|
+
return lots.map((lot, index) => buildLot(lot, index, seed, floorsMin, floorsMax, bayWidth, descriptor.storyHeight));
|
|
61
|
+
}
|
|
62
|
+
function buildLot(lot, index, seed, floorsMin, floorsMax, bayWidth, storyHeight) {
|
|
63
|
+
const lo = Math.min(floorsMin, floorsMax);
|
|
64
|
+
const hi = Math.max(floorsMin, floorsMax);
|
|
65
|
+
const floors = lo + Math.floor((hashU32(`${seed}:floors:${index}`) / 4294967296) * (hi - lo + 1));
|
|
66
|
+
const baysWide = Math.max(1, Math.floor(lot.footprint.w / bayWidth));
|
|
67
|
+
const baysDeep = Math.max(1, Math.floor(lot.footprint.d / bayWidth));
|
|
68
|
+
const building = generateBuilding({
|
|
69
|
+
id: `frontage-${index}`,
|
|
70
|
+
seed: `${seed}:${index}`,
|
|
71
|
+
center: lot.center,
|
|
72
|
+
floors,
|
|
73
|
+
baysWide,
|
|
74
|
+
baysDeep,
|
|
75
|
+
bayWidth,
|
|
76
|
+
floorHeight: storyHeight,
|
|
77
|
+
});
|
|
78
|
+
return { ...building, rotationY: lot.rotationY };
|
|
79
|
+
}
|
|
24
80
|
const TERRAIN_SAMPLE_RESOLUTION = 16;
|
|
25
81
|
function sampleTerrainHeights(descriptor) {
|
|
26
82
|
const field = resolveTerrainField(descriptor);
|
package/dist/world/features.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BuildingPaletteOverrides, BuildingStyle } from "./buildings.js";
|
|
2
2
|
import type { AvoidZone } from "./geometry.js";
|
|
3
3
|
import type { TerrainPathProfile } from "./pathTerrain.js";
|
|
4
|
+
import type { PlaceWorldFeature } from "./place.js";
|
|
4
5
|
import type { TerraformSnapshot } from "./terraform.js";
|
|
5
6
|
import type { VolumetricCloudsConfig } from "./volumetricClouds.js";
|
|
6
7
|
export interface WorldBounds {
|
|
@@ -290,6 +291,34 @@ export interface SkyEnvironmentConfig {
|
|
|
290
291
|
/** Raymarched volumetric cloud layer over the sky dome. Off by default — omit for no clouds. */
|
|
291
292
|
volumetricClouds?: VolumetricCloudsConfig;
|
|
292
293
|
}
|
|
294
|
+
/** One road a `building({ along })` frontage lines with buildings: centerline + full width. */
|
|
295
|
+
export interface BuildingFrontageRoad {
|
|
296
|
+
/** Road centerline vertices in world XZ; at least two points. */
|
|
297
|
+
path: readonly EnvironmentVec2[];
|
|
298
|
+
/** Road full width in world units; lots set back from `width / 2`. */
|
|
299
|
+
width: number;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Street-aware placement for `building()`: instead of a grid scattered around `position`, lots are
|
|
303
|
+
* stepped along each road's frontage, offset by a consistent setback (a curb + sidewalk strip), and
|
|
304
|
+
* turned so every building FRONT faces its road. `footprint` (`w` = frontage width, `d` = depth) and
|
|
305
|
+
* `spacing` (along-road gap) are shared with the grid mode. Deterministic and bounded by `maxLots`.
|
|
306
|
+
*/
|
|
307
|
+
export interface BuildingFrontageConfig {
|
|
308
|
+
/** Roads whose frontage is lined with lots. */
|
|
309
|
+
roads: readonly BuildingFrontageRoad[];
|
|
310
|
+
/** Sidewalk strip between the curb (`width / 2`) and the building front, world units. Default 3. */
|
|
311
|
+
setback?: number;
|
|
312
|
+
/** Place lots on both sides of each road. Default true. */
|
|
313
|
+
bothSides?: boolean;
|
|
314
|
+
/** Clip region; lots whose center falls outside are dropped. Omit to keep every lot. */
|
|
315
|
+
area?: {
|
|
316
|
+
center?: EnvironmentVec2;
|
|
317
|
+
halfExtents: EnvironmentVec2;
|
|
318
|
+
};
|
|
319
|
+
/** Hard cap on emitted lots so a long network stays bounded. Default 400. */
|
|
320
|
+
maxLots?: number;
|
|
321
|
+
}
|
|
293
322
|
export interface BuildingEnvironmentConfig {
|
|
294
323
|
count?: number;
|
|
295
324
|
position?: EnvironmentVec2;
|
|
@@ -302,6 +331,11 @@ export interface BuildingEnvironmentConfig {
|
|
|
302
331
|
/** Explicit per-part-kind hex colors; any part left unset falls back to the resolved `style` palette. */
|
|
303
332
|
palette?: BuildingPaletteOverrides;
|
|
304
333
|
seed?: string;
|
|
334
|
+
/**
|
|
335
|
+
* Street-aware placement: derive lots along the given roads' frontage (facing each street, set
|
|
336
|
+
* back by a sidewalk strip) instead of the grid around `position`. See {@link BuildingFrontageConfig}.
|
|
337
|
+
*/
|
|
338
|
+
along?: BuildingFrontageConfig;
|
|
305
339
|
}
|
|
306
340
|
export type PadSize = readonly [number, number] | {
|
|
307
341
|
radius: number;
|
|
@@ -343,7 +377,7 @@ export type OceanEnvironmentDescriptor = {
|
|
|
343
377
|
} & Required<Pick<OceanEnvironmentConfig, "bounds" | "level" | "waveHeight" | "waveScale" | "waveSpeed" | "color">> & Pick<OceanEnvironmentConfig, "position" | "levelAt">;
|
|
344
378
|
export type BuildingEnvironmentDescriptor = {
|
|
345
379
|
kind: "building";
|
|
346
|
-
} & Required<Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style">> & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette">;
|
|
380
|
+
} & Required<Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style">> & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette" | "along">;
|
|
347
381
|
export type PadEnvironmentDescriptor = {
|
|
348
382
|
kind: "pad";
|
|
349
383
|
} & Required<Pick<PadEnvironmentConfig, "center" | "size" | "height" | "color">> & Pick<PadEnvironmentConfig, "rotationY" | "elevation">;
|
|
@@ -464,8 +498,13 @@ export interface PlotsWorldConfig extends WorldGridConfig {
|
|
|
464
498
|
export interface TilemapWorldConfig extends WorldGridConfig {
|
|
465
499
|
map: string;
|
|
466
500
|
}
|
|
467
|
-
/**
|
|
468
|
-
|
|
501
|
+
/**
|
|
502
|
+
* A declared world shape passed to `defineGame`. The preferred model is the place feature from
|
|
503
|
+
* `world()` (`@jgengine/core/world/place`): substrate + laws, with all dressing authored in the
|
|
504
|
+
* editor. The remaining members — biomes, voxel grid, plots, tilemap, environment, flat — are the
|
|
505
|
+
* legacy code-declared shapes kept for existing games.
|
|
506
|
+
*/
|
|
507
|
+
export type WorldFeature = PlaceWorldFeature | ({
|
|
469
508
|
kind: "biomes";
|
|
470
509
|
} & BiomesWorldConfig) | ({
|
|
471
510
|
kind: "voxel";
|
|
@@ -478,7 +517,16 @@ export type WorldFeature = ({
|
|
|
478
517
|
};
|
|
479
518
|
/** Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it. Elevated pads (absolute `elevation`) float free and carve nothing. */
|
|
480
519
|
export declare function padFlattenMasks(pads: readonly PadEnvironmentDescriptor[]): readonly TerrainFlattenMask[];
|
|
481
|
-
/** Composes an `environment()`
|
|
520
|
+
/** Composes an `environment()` feature from terrain, sky, weather, vegetation, water, structures, roads, and pads.
|
|
521
|
+
*
|
|
522
|
+
* Legacy dressing-in-code path — not how a world is defined. A world is the place you play in
|
|
523
|
+
* (`world()` from `world/place`: ground + laws); sky look, foliage scatter, props, and sculpt are
|
|
524
|
+
* scene content authored in the editor. Reach for `environment()` only when consuming
|
|
525
|
+
* editor/preset-written scene data (`environmentContentFromDocument`) or maintaining an existing
|
|
526
|
+
* game; never scaffold new worlds from it.
|
|
527
|
+
*
|
|
528
|
+
* @capability world-environment compose editor/preset-derived outdoor dressing — terrain, weather, water, structures — into a legacy environment feature
|
|
529
|
+
*/
|
|
482
530
|
export declare function environment(config?: EnvironmentWorldConfig): EnvironmentWorldFeature;
|
|
483
531
|
/** Declares a heightfield terrain patch for `environment()` — bounds, noise, materials, and flatten masks. */
|
|
484
532
|
export declare function terrain(config?: TerrainEnvironmentConfig): TerrainEnvironmentDescriptor;
|
|
@@ -494,7 +542,7 @@ export declare function ocean(config?: OceanEnvironmentConfig): OceanEnvironment
|
|
|
494
542
|
/** Declare a road ribbon for an `environment()` world; the shell drapes and renders it over the terrain. */
|
|
495
543
|
export declare function road(config: RoadEnvironmentConfig): RoadEnvironmentDescriptor;
|
|
496
544
|
export declare function island(config: TerrainIslandConfig): TerrainIslandDescriptor;
|
|
497
|
-
/** Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style. */
|
|
545
|
+
/** Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style. Pass `along` to line road frontage instead of gridding around `position`. */
|
|
498
546
|
export declare function building(config?: BuildingEnvironmentConfig): BuildingEnvironmentDescriptor;
|
|
499
547
|
export declare function pad(config: PadEnvironmentConfig): PadEnvironmentDescriptor;
|
|
500
548
|
/** Declares a biome-painted world — the whole-world alternative to a single `environment()` terrain. */
|
package/dist/world/features.js
CHANGED
|
@@ -43,7 +43,16 @@ function bandWeatherDescriptors(terrainDescriptor) {
|
|
|
43
43
|
}
|
|
44
44
|
return out;
|
|
45
45
|
}
|
|
46
|
-
/** Composes an `environment()`
|
|
46
|
+
/** Composes an `environment()` feature from terrain, sky, weather, vegetation, water, structures, roads, and pads.
|
|
47
|
+
*
|
|
48
|
+
* Legacy dressing-in-code path — not how a world is defined. A world is the place you play in
|
|
49
|
+
* (`world()` from `world/place`: ground + laws); sky look, foliage scatter, props, and sculpt are
|
|
50
|
+
* scene content authored in the editor. Reach for `environment()` only when consuming
|
|
51
|
+
* editor/preset-written scene data (`environmentContentFromDocument`) or maintaining an existing
|
|
52
|
+
* game; never scaffold new worlds from it.
|
|
53
|
+
*
|
|
54
|
+
* @capability world-environment compose editor/preset-derived outdoor dressing — terrain, weather, water, structures — into a legacy environment feature
|
|
55
|
+
*/
|
|
47
56
|
export function environment(config = {}) {
|
|
48
57
|
const explicitWeather = list(config.weather);
|
|
49
58
|
const bandWeather = bandWeatherDescriptors(config.terrain);
|
|
@@ -219,11 +228,13 @@ export function island(config) {
|
|
|
219
228
|
const { kind: _kind, ...descriptor } = terrain(terrainConfig);
|
|
220
229
|
return { ...descriptor, kind: "island", origin };
|
|
221
230
|
}
|
|
222
|
-
/** Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style. */
|
|
231
|
+
/** Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style. Pass `along` to line road frontage instead of gridding around `position`. */
|
|
223
232
|
export function building(config = {}) {
|
|
224
233
|
return withOptional({
|
|
225
234
|
kind: "building",
|
|
226
|
-
|
|
235
|
+
// In frontage mode the road network sizes the district; `count` becomes an upper bound, so a
|
|
236
|
+
// caller that just says `along` gets a filled street rather than a single grid building.
|
|
237
|
+
count: config.count ?? (config.along === undefined ? 1 : config.along.maxLots ?? 400),
|
|
227
238
|
footprint: config.footprint ?? { w: 8, d: 8 },
|
|
228
239
|
stories: config.stories ?? [1, 4],
|
|
229
240
|
storyHeight: config.storyHeight ?? 3,
|
|
@@ -233,6 +244,7 @@ export function building(config = {}) {
|
|
|
233
244
|
...(config.seed === undefined ? {} : { seed: config.seed }),
|
|
234
245
|
...(config.position === undefined ? {} : { position: config.position }),
|
|
235
246
|
...(config.palette === undefined ? {} : { palette: config.palette }),
|
|
247
|
+
...(config.along === undefined ? {} : { along: config.along }),
|
|
236
248
|
});
|
|
237
249
|
}
|
|
238
250
|
export function pad(config) {
|
package/dist/world/grassKind.js
CHANGED
|
@@ -12,7 +12,7 @@ import { registerSceneKind } from "../scene/sceneKinds.js";
|
|
|
12
12
|
export const GRASS_FIELD_KIND = "grass_field";
|
|
13
13
|
/** Grass defaults: a lush, lightly-swaying meadow. */
|
|
14
14
|
export const GRASS_DEFAULTS = {
|
|
15
|
-
density:
|
|
15
|
+
density: 12,
|
|
16
16
|
bladeHeight: 0.6,
|
|
17
17
|
colorBase: "#3f6f37",
|
|
18
18
|
colorTip: "#9ac25a",
|
|
@@ -26,7 +26,7 @@ export const GRASS_DEFAULTS = {
|
|
|
26
26
|
/** The grass parameter schema — drives the inspector and `meta` parse via the studio seam. */
|
|
27
27
|
export const GRASS_SCHEMA = {
|
|
28
28
|
fields: [
|
|
29
|
-
{ type: "range", key: "density", label: "density", min: 0.5, max:
|
|
29
|
+
{ type: "range", key: "density", label: "density", min: 0.5, max: 160, step: 0.5, default: GRASS_DEFAULTS.density, unit: "/m²" },
|
|
30
30
|
{ type: "range", key: "bladeHeight", label: "blade height", min: 0.1, max: 2, step: 0.05, default: GRASS_DEFAULTS.bladeHeight, unit: "m" },
|
|
31
31
|
{ type: "color", key: "colorBase", label: "base color", default: GRASS_DEFAULTS.colorBase },
|
|
32
32
|
{ type: "color", key: "colorTip", label: "tip color", default: GRASS_DEFAULTS.colorTip },
|
package/dist/world/markers.d.ts
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
export type MarkerPosition = readonly [number, number, number];
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Small, display-only marker shape consumed by map renderers. Existing games
|
|
4
|
+
* can project their own entities to this view without adopting marker
|
|
5
|
+
* lifecycle storage or duplicating them into a {@link MarkerSet}.
|
|
6
|
+
*/
|
|
7
|
+
export interface MarkerView<TMeta = unknown> {
|
|
3
8
|
id: string;
|
|
4
|
-
kind: string;
|
|
5
9
|
position: MarkerPosition;
|
|
10
|
+
kind?: string;
|
|
6
11
|
label?: string;
|
|
12
|
+
/** Optional world heading in radians for presentations that draw directional markers. */
|
|
13
|
+
heading?: number;
|
|
14
|
+
meta?: TMeta;
|
|
15
|
+
}
|
|
16
|
+
/** A marker owned by {@link MarkerSet}, including its lifecycle and query fields. */
|
|
17
|
+
export interface MapMarker<TMeta = unknown> extends MarkerView<TMeta> {
|
|
18
|
+
kind: string;
|
|
7
19
|
owner?: string;
|
|
8
20
|
createdAt: number;
|
|
9
21
|
expiresAt?: number;
|
|
10
|
-
meta?: TMeta;
|
|
11
22
|
}
|
|
12
23
|
export interface MarkerInput<TMeta = unknown> {
|
|
13
24
|
id?: string;
|
|
14
25
|
kind: string;
|
|
15
26
|
position: MarkerPosition;
|
|
16
27
|
label?: string;
|
|
28
|
+
/** Optional world heading in radians for directional map presentations. */
|
|
29
|
+
heading?: number;
|
|
17
30
|
owner?: string;
|
|
18
31
|
createdAt?: number;
|
|
19
32
|
expiresAt?: number;
|
|
@@ -37,7 +50,7 @@ export interface MarkerKindStyle {
|
|
|
37
50
|
priority: number;
|
|
38
51
|
}
|
|
39
52
|
export declare const DEFAULT_MARKER_KINDS: Record<string, MarkerKindStyle>;
|
|
40
|
-
export declare function markerKindStyle(kind: string, styles?: Record<string, MarkerKindStyle>): MarkerKindStyle;
|
|
53
|
+
export declare function markerKindStyle(kind: string | undefined, styles?: Record<string, MarkerKindStyle>): MarkerKindStyle;
|
|
41
54
|
export interface MarkerSet<TMeta = unknown> {
|
|
42
55
|
add(marker: MarkerInput<TMeta>): string;
|
|
43
56
|
remove(id: string): boolean;
|
|
@@ -49,4 +62,37 @@ export interface MarkerSet<TMeta = unknown> {
|
|
|
49
62
|
subscribe(listener: () => void): () => void;
|
|
50
63
|
snapshot(): readonly MapMarker<TMeta>[];
|
|
51
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Observable marker snapshots owned by an external project. `getSnapshot`
|
|
67
|
+
* must return the same array identity until the source changes and calls its
|
|
68
|
+
* subscribers, matching React's external-store contract.
|
|
69
|
+
*/
|
|
70
|
+
export interface MarkerSource<TMarker extends MarkerView = MarkerView> {
|
|
71
|
+
subscribe(listener: () => void): () => void;
|
|
72
|
+
getSnapshot(): readonly TMarker[];
|
|
73
|
+
/** Optional hydration snapshot; omit when the client snapshot is also valid during SSR. */
|
|
74
|
+
getServerSnapshot?: () => readonly TMarker[];
|
|
75
|
+
}
|
|
76
|
+
/** Marker data accepted by portable consumers: static views, an external source, or a native set. */
|
|
77
|
+
export type MarkerCollection<TMarker extends MarkerView = MarkerView> = readonly TMarker[] | MarkerSource<TMarker> | MarkerSet;
|
|
78
|
+
/** Configuration for projecting a caller-owned collection into display-only markers. */
|
|
79
|
+
export interface MarkerSourceOptions<TEntity, TMarker extends MarkerView = MarkerView> {
|
|
80
|
+
/** Subscribe to changes in the caller-owned collection. Static collections may omit this. */
|
|
81
|
+
subscribe?: (listener: () => void) => () => void;
|
|
82
|
+
/** Read the current caller-owned collection. Keep its identity stable between changes. */
|
|
83
|
+
getSnapshot: () => readonly TEntity[];
|
|
84
|
+
/** Optional server collection used to keep hydration output deterministic. */
|
|
85
|
+
getServerSnapshot?: () => readonly TEntity[];
|
|
86
|
+
/** Project one caller-owned record into the minimal marker view. */
|
|
87
|
+
project: (value: TEntity, index: number) => TMarker;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Adapt a caller-owned array/store to an observable marker source. Projection
|
|
91
|
+
* is cached between source changes, so React reads do not copy the collection
|
|
92
|
+
* per frame. The caller retains ownership of entities, updates, persistence,
|
|
93
|
+
* and subscription scheduling.
|
|
94
|
+
*
|
|
95
|
+
* @capability portable-marker-source project caller-owned arrays or subscribable stores into cached minimap marker snapshots without a MarkerSet
|
|
96
|
+
*/
|
|
97
|
+
export declare function createMarkerSource<TEntity, TMarker extends MarkerView = MarkerView>(options: MarkerSourceOptions<TEntity, TMarker>): MarkerSource<TMarker>;
|
|
52
98
|
export declare function createMarkerSet<TMeta = unknown>(now?: () => number): MarkerSet<TMeta>;
|
package/dist/world/markers.js
CHANGED
|
@@ -10,8 +10,55 @@ export const DEFAULT_MARKER_KINDS = {
|
|
|
10
10
|
ping: { id: "ping", color: "#22d3ee", glyph: "◎", priority: 65 },
|
|
11
11
|
};
|
|
12
12
|
export function markerKindStyle(kind, styles = DEFAULT_MARKER_KINDS) {
|
|
13
|
+
if (kind === undefined)
|
|
14
|
+
return { id: "marker", color: "#e2e8f0", glyph: "\u2022", priority: 30 };
|
|
13
15
|
return styles[kind] ?? { id: kind, color: "#e2e8f0", glyph: "•", priority: 30 };
|
|
14
16
|
}
|
|
17
|
+
function projectedSnapshot(readSource, project) {
|
|
18
|
+
let dirty = true;
|
|
19
|
+
let source;
|
|
20
|
+
let snapshot = [];
|
|
21
|
+
return {
|
|
22
|
+
invalidate() {
|
|
23
|
+
dirty = true;
|
|
24
|
+
},
|
|
25
|
+
read() {
|
|
26
|
+
const next = readSource();
|
|
27
|
+
if (dirty || next !== source) {
|
|
28
|
+
source = next;
|
|
29
|
+
snapshot = next.map(project);
|
|
30
|
+
dirty = false;
|
|
31
|
+
}
|
|
32
|
+
return snapshot;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Adapt a caller-owned array/store to an observable marker source. Projection
|
|
38
|
+
* is cached between source changes, so React reads do not copy the collection
|
|
39
|
+
* per frame. The caller retains ownership of entities, updates, persistence,
|
|
40
|
+
* and subscription scheduling.
|
|
41
|
+
*
|
|
42
|
+
* @capability portable-marker-source project caller-owned arrays or subscribable stores into cached minimap marker snapshots without a MarkerSet
|
|
43
|
+
*/
|
|
44
|
+
export function createMarkerSource(options) {
|
|
45
|
+
const client = projectedSnapshot(options.getSnapshot, options.project);
|
|
46
|
+
const server = options.getServerSnapshot === undefined
|
|
47
|
+
? client
|
|
48
|
+
: projectedSnapshot(options.getServerSnapshot, options.project);
|
|
49
|
+
return {
|
|
50
|
+
subscribe(listener) {
|
|
51
|
+
if (options.subscribe === undefined)
|
|
52
|
+
return () => undefined;
|
|
53
|
+
return options.subscribe(() => {
|
|
54
|
+
client.invalidate();
|
|
55
|
+
listener();
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
getSnapshot: client.read,
|
|
59
|
+
getServerSnapshot: server.read,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
15
62
|
function distanceXZ(a, b) {
|
|
16
63
|
return Math.hypot(a[0] - b[0], a[2] - b[2]);
|
|
17
64
|
}
|
|
@@ -38,6 +85,8 @@ export function createMarkerSet(now = Date.now) {
|
|
|
38
85
|
};
|
|
39
86
|
if (input.label !== undefined)
|
|
40
87
|
marker.label = input.label;
|
|
88
|
+
if (input.heading !== undefined)
|
|
89
|
+
marker.heading = input.heading;
|
|
41
90
|
if (input.owner !== undefined)
|
|
42
91
|
marker.owner = input.owner;
|
|
43
92
|
if (input.expiresAt !== undefined)
|
package/dist/world/massing.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clamp } from "../math/scalar.js";
|
|
1
2
|
export const MASSING_COMPOSITIONS = [
|
|
2
3
|
"bar",
|
|
3
4
|
"split",
|
|
@@ -17,7 +18,6 @@ export const MASSING_PROFILES = [
|
|
|
17
18
|
"offset",
|
|
18
19
|
"twisted",
|
|
19
20
|
];
|
|
20
|
-
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
21
21
|
export const hashMassingSeed = (text) => [...text].reduce((sum, ch) => sum + ch.charCodeAt(0), 0);
|
|
22
22
|
export const massingBase = (spec, cap = 0.28) => Math.min(Math.max(0, spec.pilotis), Math.max(0, spec.height) * cap);
|
|
23
23
|
export const isVillageMassing = (spec) => spec.composition === "cluster" && spec.height < 36 && spec.articulation > 56 && spec.moduleDensity > 2;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Vec2 } from "./geometry.js";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic minimap terrain bake (#1036): rasterize an authored world's terrain (plus optional
|
|
4
|
+
* biome zones and water) into a top-down RGBA image and a matching world-bounds rectangle, then
|
|
5
|
+
* encode it as a PNG data URI. The editor runs this as a bake action and stores the result on the
|
|
6
|
+
* scene document; runtime feeds it straight into the existing `Minimap` / `WorldMap` `background` +
|
|
7
|
+
* `mapBounds` props — no new runtime render path, and the same authored world always bakes the same
|
|
8
|
+
* image (safe for verify/CI). Pure: no canvas, no DOM, `core` still imports nothing external.
|
|
9
|
+
*/
|
|
10
|
+
/** World-space rectangle the bake spans (matches the react `MapBounds` shape structurally). */
|
|
11
|
+
export interface MinimapBakeBounds {
|
|
12
|
+
minX: number;
|
|
13
|
+
minZ: number;
|
|
14
|
+
maxX: number;
|
|
15
|
+
maxZ: number;
|
|
16
|
+
}
|
|
17
|
+
/** One biome/region tint painted over the terrain shade where the ground falls inside its polygon. */
|
|
18
|
+
export interface MinimapBakeZone {
|
|
19
|
+
/** Closed XZ polygon in world space. */
|
|
20
|
+
polygon: readonly Vec2[];
|
|
21
|
+
/** `#rgb` / `#rrggbb` tint. */
|
|
22
|
+
color: string;
|
|
23
|
+
/** Blend strength 0..1. Default 0.5. */
|
|
24
|
+
alpha?: number;
|
|
25
|
+
}
|
|
26
|
+
/** The authored world a bake reads — a height sampler, its bounds, and optional zones/water. */
|
|
27
|
+
export interface MinimapBakeSource {
|
|
28
|
+
bounds: MinimapBakeBounds;
|
|
29
|
+
sampleHeight(x: number, z: number): number;
|
|
30
|
+
/** Optional surface normal for slope shading; flat shading when omitted. */
|
|
31
|
+
sampleNormal?(x: number, z: number): readonly [number, number, number];
|
|
32
|
+
zones?: readonly MinimapBakeZone[];
|
|
33
|
+
/** Ground at or below this height renders as water. */
|
|
34
|
+
waterLevel?: number;
|
|
35
|
+
}
|
|
36
|
+
/** The height→color ramp and water color used by the bake. */
|
|
37
|
+
export interface MinimapBakePalette {
|
|
38
|
+
low: string;
|
|
39
|
+
high: string;
|
|
40
|
+
water: string;
|
|
41
|
+
}
|
|
42
|
+
/** The built-in height→color ramp and water color a bake uses when no palette override is given. */
|
|
43
|
+
export declare const DEFAULT_MINIMAP_PALETTE: MinimapBakePalette;
|
|
44
|
+
/** Options for {@link bakeMinimapImage}. */
|
|
45
|
+
export interface MinimapBakeOptions {
|
|
46
|
+
/** Pixel resolution of the longer axis; the shorter axis keeps world aspect. Default 128. */
|
|
47
|
+
resolution?: number;
|
|
48
|
+
palette?: Partial<MinimapBakePalette>;
|
|
49
|
+
}
|
|
50
|
+
/** A baked minimap image: RGBA pixels plus the world bounds they span. */
|
|
51
|
+
export interface MinimapBake {
|
|
52
|
+
width: number;
|
|
53
|
+
height: number;
|
|
54
|
+
/** Row-major RGBA, length `width * height * 4`. */
|
|
55
|
+
pixels: Uint8ClampedArray;
|
|
56
|
+
bounds: MinimapBakeBounds;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Rasterizes a top-down terrain image: height maps onto a low→high color ramp (normalized over the
|
|
60
|
+
* sampled height range), slope darkens the shade, water fills below `waterLevel`, and each zone
|
|
61
|
+
* polygon tints the ground it covers. Deterministic for a fixed source. The bounds are preserved so
|
|
62
|
+
* a player-centered minimap pans the image correctly.
|
|
63
|
+
* @capability minimap-bake rasterize authored terrain + zones into a top-down minimap image
|
|
64
|
+
*/
|
|
65
|
+
export declare function bakeMinimapImage(source: MinimapBakeSource, options?: MinimapBakeOptions): MinimapBake;
|
|
66
|
+
/** Encodes an RGBA bake to raw PNG bytes (pure; stored DEFLATE, valid for any decoder). */
|
|
67
|
+
export declare function encodeBakePng(bake: MinimapBake): Uint8Array;
|
|
68
|
+
/**
|
|
69
|
+
* Encodes a bake as a `data:image/png;base64,...` URI — the exact string the `Minimap` /
|
|
70
|
+
* `WorldMap` `background` prop takes. Deterministic for a fixed bake.
|
|
71
|
+
* @capability minimap-bake encode a baked minimap as a PNG data URI for the Minimap background
|
|
72
|
+
*/
|
|
73
|
+
export declare function minimapBakeToPngDataUri(bake: MinimapBake): string;
|
|
74
|
+
/** One-call convenience: rasterize a source and return `{ background, mapBounds }` for a minimap. */
|
|
75
|
+
export declare function bakeMinimapBackground(source: MinimapBakeSource, options?: MinimapBakeOptions): {
|
|
76
|
+
background: string;
|
|
77
|
+
mapBounds: MinimapBakeBounds;
|
|
78
|
+
};
|