@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.
Files changed (303) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/ai/flock.d.ts +36 -0
  3. package/dist/ai/flock.js +182 -0
  4. package/dist/ai/groupAssist.d.ts +19 -0
  5. package/dist/ai/groupAssist.js +59 -0
  6. package/dist/ai/laneSelect.d.ts +21 -0
  7. package/dist/ai/laneSelect.js +37 -0
  8. package/dist/ai/mobBrain.d.ts +68 -0
  9. package/dist/ai/mobBrain.js +115 -0
  10. package/dist/ai/spawnDirector.d.ts +13 -0
  11. package/dist/ai/spawnDirector.js +41 -17
  12. package/dist/ai/spawnPoint.d.ts +5 -0
  13. package/dist/ai/spawnPoint.js +5 -0
  14. package/dist/ai/threat.d.ts +2 -0
  15. package/dist/ai/threat.js +34 -0
  16. package/dist/audio/audioFalloff.d.ts +5 -2
  17. package/dist/audio/music.d.ts +53 -0
  18. package/dist/audio/music.js +34 -0
  19. package/dist/audio/synth.d.ts +60 -0
  20. package/dist/audio/synth.js +8 -0
  21. package/dist/authoring.d.ts +14 -0
  22. package/dist/authoring.js +14 -0
  23. package/dist/cartridge/runtime.d.ts +15 -0
  24. package/dist/cartridge/runtime.js +556 -0
  25. package/dist/cartridge/spec.d.ts +188 -0
  26. package/dist/cartridge/spec.js +19 -0
  27. package/dist/cartridge/validate.d.ts +2 -0
  28. package/dist/cartridge/validate.js +78 -0
  29. package/dist/combat/abilityKit.d.ts +17 -2
  30. package/dist/combat/abilityKit.js +62 -13
  31. package/dist/combat/castRunner.d.ts +40 -0
  32. package/dist/combat/castRunner.js +56 -0
  33. package/dist/combat/comboPoints.d.ts +14 -0
  34. package/dist/combat/comboPoints.js +52 -0
  35. package/dist/combat/defensiveWindow.d.ts +1 -1
  36. package/dist/combat/defensiveWindow.js +5 -3
  37. package/dist/combat/dotField.d.ts +43 -0
  38. package/dist/combat/dotField.js +48 -0
  39. package/dist/combat/projectiles.d.ts +33 -12
  40. package/dist/combat/projectiles.js +214 -129
  41. package/dist/combat/regenShield.d.ts +35 -0
  42. package/dist/combat/regenShield.js +45 -0
  43. package/dist/combat/resistance.d.ts +16 -4
  44. package/dist/combat/resistance.js +43 -7
  45. package/dist/combat/resourcePool.d.ts +20 -0
  46. package/dist/combat/resourcePool.js +55 -0
  47. package/dist/combat/shotOrigin.d.ts +52 -0
  48. package/dist/combat/shotOrigin.js +86 -0
  49. package/dist/combat/telegraph.d.ts +25 -0
  50. package/dist/combat/telegraph.js +47 -0
  51. package/dist/crafting/recipe.js +4 -1
  52. package/dist/devtools/devtools.d.ts +72 -24
  53. package/dist/devtools/devtools.js +449 -101
  54. package/dist/devtools/transformTunables.js +3 -3
  55. package/dist/devtools/tunableSchema.d.ts +140 -0
  56. package/dist/devtools/tunableSchema.js +476 -0
  57. package/dist/editor/commands.d.ts +65 -0
  58. package/dist/editor/commands.js +193 -0
  59. package/dist/editor/document.d.ts +40 -0
  60. package/dist/editor/document.js +153 -0
  61. package/dist/editor/index.d.ts +30 -0
  62. package/dist/editor/index.js +6 -0
  63. package/dist/editor/types.d.ts +69 -0
  64. package/dist/editor/types.js +29 -0
  65. package/dist/faction/factions.d.ts +33 -0
  66. package/dist/faction/factions.js +76 -0
  67. package/dist/faction/reputation.d.ts +32 -0
  68. package/dist/faction/reputation.js +82 -0
  69. package/dist/game/chat.d.ts +2 -0
  70. package/dist/game/chat.js +9 -1
  71. package/dist/game/chatFilter.d.ts +15 -0
  72. package/dist/game/chatFilter.js +55 -0
  73. package/dist/game/connectedPlayers.d.ts +28 -0
  74. package/dist/game/connectedPlayers.js +21 -0
  75. package/dist/game/controlGate.d.ts +4 -0
  76. package/dist/game/controlGate.js +7 -0
  77. package/dist/game/defineGame.d.ts +44 -1
  78. package/dist/game/defineGame.js +1 -0
  79. package/dist/game/events.d.ts +25 -0
  80. package/dist/game/gamePhase.d.ts +20 -0
  81. package/dist/game/gamePhase.js +21 -0
  82. package/dist/game/keyValueStore.d.ts +25 -0
  83. package/dist/game/keyValueStore.js +61 -0
  84. package/dist/game/levelSequence.d.ts +24 -2
  85. package/dist/game/levelSequence.js +51 -2
  86. package/dist/game/lootTable.d.ts +14 -1
  87. package/dist/game/lootTable.js +28 -7
  88. package/dist/game/modelAnimation.d.ts +8 -0
  89. package/dist/game/modelAnimation.js +18 -0
  90. package/dist/game/playableGame.d.ts +116 -4
  91. package/dist/game/playableGame.js +1 -1
  92. package/dist/game/quest.d.ts +7 -3
  93. package/dist/game/quest.js +17 -8
  94. package/dist/game/race.d.ts +39 -1
  95. package/dist/game/race.js +96 -11
  96. package/dist/game/recordBook.d.ts +27 -0
  97. package/dist/game/recordBook.js +86 -0
  98. package/dist/game/talents.d.ts +47 -0
  99. package/dist/game/talents.js +140 -0
  100. package/dist/index.d.ts +8 -0
  101. package/dist/index.js +8 -0
  102. package/dist/input/actionBindings.d.ts +2 -0
  103. package/dist/input/actionBindings.js +1 -0
  104. package/dist/input/axisInput.d.ts +33 -3
  105. package/dist/input/axisInput.js +60 -4
  106. package/dist/input/bindingOverrides.d.ts +25 -0
  107. package/dist/input/bindingOverrides.js +95 -0
  108. package/dist/input/pointerAxis.d.ts +30 -0
  109. package/dist/input/pointerAxis.js +31 -0
  110. package/dist/input/touchScheme.d.ts +6 -0
  111. package/dist/input/touchScheme.js +45 -7
  112. package/dist/interaction/contextMenu.d.ts +1 -0
  113. package/dist/interaction/contextMenu.js +1 -0
  114. package/dist/interaction/proximityPrompt.d.ts +3 -1
  115. package/dist/interaction/proximityPrompt.js +3 -3
  116. package/dist/meta/changelog.d.ts +1 -1
  117. package/dist/meta/changelog.js +44 -1
  118. package/dist/movement/dash.d.ts +3 -0
  119. package/dist/movement/dash.js +19 -5
  120. package/dist/movement/glideModel.d.ts +44 -0
  121. package/dist/movement/glideModel.js +54 -0
  122. package/dist/movement/grappleSwing.d.ts +34 -0
  123. package/dist/movement/grappleSwing.js +56 -0
  124. package/dist/movement/leaderTrail.d.ts +29 -0
  125. package/dist/movement/leaderTrail.js +67 -0
  126. package/dist/movement/movementModel.d.ts +4 -0
  127. package/dist/movement/movementModel.js +13 -0
  128. package/dist/movement/playerMovement.d.ts +41 -0
  129. package/dist/movement/playerMovement.js +181 -0
  130. package/dist/movement/steering.d.ts +27 -0
  131. package/dist/movement/steering.js +40 -0
  132. package/dist/multiplayer/feedWriteGate.d.ts +10 -0
  133. package/dist/multiplayer/feedWriteGate.js +12 -0
  134. package/dist/multiplayer/presenceModel.d.ts +14 -2
  135. package/dist/multiplayer/presenceModel.js +15 -3
  136. package/dist/nav/corridors.d.ts +30 -0
  137. package/dist/nav/corridors.js +69 -0
  138. package/dist/nav/navGrid.js +30 -4
  139. package/dist/nav/railGraph.d.ts +66 -0
  140. package/dist/nav/railGraph.js +129 -0
  141. package/dist/nav/timetable.d.ts +41 -0
  142. package/dist/nav/timetable.js +94 -0
  143. package/dist/physics/buoyancy.d.ts +6 -0
  144. package/dist/physics/buoyancy.js +14 -0
  145. package/dist/physics/flowTube.d.ts +32 -0
  146. package/dist/physics/flowTube.js +64 -0
  147. package/dist/physics/forceVolume.d.ts +27 -0
  148. package/dist/physics/forceVolume.js +38 -0
  149. package/dist/physics/kinematicVehicle.d.ts +52 -0
  150. package/dist/physics/kinematicVehicle.js +81 -0
  151. package/dist/physics/physicsWorld.d.ts +13 -6
  152. package/dist/physics/physicsWorld.js +169 -40
  153. package/dist/physics/ragdoll.js +1 -1
  154. package/dist/physics/vehicleBody.js +2 -1
  155. package/dist/random/rng.d.ts +2 -0
  156. package/dist/random/rng.js +2 -0
  157. package/dist/random/seedLink.d.ts +5 -0
  158. package/dist/random/seedLink.js +35 -0
  159. package/dist/render/postProcessing.d.ts +56 -0
  160. package/dist/render/postProcessing.js +1 -0
  161. package/dist/runtime/adapter.d.ts +19 -0
  162. package/dist/runtime/adapter.js +11 -5
  163. package/dist/runtime/cameraDirector.d.ts +6 -1
  164. package/dist/runtime/cameraDirector.js +6 -0
  165. package/dist/runtime/commandRunner.d.ts +2 -2
  166. package/dist/runtime/commandRunner.js +2 -2
  167. package/dist/runtime/gameContext.d.ts +72 -8
  168. package/dist/runtime/gameContext.js +374 -61
  169. package/dist/runtime/gameRuntime.d.ts +5 -1
  170. package/dist/runtime/gameRuntime.js +13 -12
  171. package/dist/runtime/hostPersistence.d.ts +9 -1
  172. package/dist/runtime/hostPersistence.js +23 -8
  173. package/dist/runtime/hostedGameRunner.d.ts +49 -0
  174. package/dist/runtime/hostedGameRunner.js +62 -0
  175. package/dist/runtime/hostedWorldSession.d.ts +65 -0
  176. package/dist/runtime/hostedWorldSession.js +57 -0
  177. package/dist/runtime/inputSnapshot.d.ts +18 -0
  178. package/dist/runtime/inputSnapshot.js +7 -0
  179. package/dist/runtime/motionIntents.d.ts +16 -6
  180. package/dist/runtime/motionIntents.js +31 -2
  181. package/dist/runtime/snapshot.d.ts +1 -0
  182. package/dist/runtime/snapshot.js +1 -1
  183. package/dist/runtime/visibility.d.ts +74 -0
  184. package/dist/runtime/visibility.js +127 -0
  185. package/dist/runtime/worldChannel.d.ts +58 -0
  186. package/dist/runtime/worldChannel.js +69 -0
  187. package/dist/runtime/worldMirror.d.ts +24 -0
  188. package/dist/runtime/worldMirror.js +31 -0
  189. package/dist/runtime/worldReplication.d.ts +46 -0
  190. package/dist/runtime/worldReplication.js +185 -0
  191. package/dist/runtime/worldSnapshot.d.ts +17 -0
  192. package/dist/runtime/worldSnapshot.js +15 -0
  193. package/dist/scene/assetCatalog.d.ts +1 -0
  194. package/dist/scene/colliders.d.ts +50 -0
  195. package/dist/scene/colliders.js +101 -0
  196. package/dist/scene/entityStats.d.ts +4 -0
  197. package/dist/scene/entityStats.js +21 -0
  198. package/dist/scene/entityStore.d.ts +51 -5
  199. package/dist/scene/entityStore.js +119 -12
  200. package/dist/scene/objectQuery.d.ts +13 -2
  201. package/dist/scene/objectQuery.js +34 -2
  202. package/dist/scene/objectStore.d.ts +2 -0
  203. package/dist/scene/objectStore.js +31 -12
  204. package/dist/scene/sceneRaycast.d.ts +80 -0
  205. package/dist/scene/sceneRaycast.js +284 -0
  206. package/dist/scene/spatial.d.ts +11 -16
  207. package/dist/scene/spatial.js +99 -38
  208. package/dist/sensor/recordingBuffer.d.ts +6 -0
  209. package/dist/sensor/recordingBuffer.js +7 -0
  210. package/dist/sensor/replayLoop.d.ts +39 -0
  211. package/dist/sensor/replayLoop.js +64 -0
  212. package/dist/sensor/visionCone.d.ts +31 -0
  213. package/dist/sensor/visionCone.js +72 -0
  214. package/dist/session/extraction.d.ts +2 -2
  215. package/dist/session/extraction.js +13 -2
  216. package/dist/settings/settingsModel.d.ts +97 -0
  217. package/dist/settings/settingsModel.js +100 -0
  218. package/dist/stats/statModifiers.d.ts +5 -2
  219. package/dist/stats/statModifiers.js +32 -14
  220. package/dist/store/observableKeyedStore.d.ts +4 -0
  221. package/dist/store/observableKeyedStore.js +17 -2
  222. package/dist/survival/decayMeter.d.ts +1 -1
  223. package/dist/survival/decayMeter.js +0 -1
  224. package/dist/tactics/fallingGrid.d.ts +2 -2
  225. package/dist/tactics/fallingGrid.js +2 -2
  226. package/dist/time/beatClock.d.ts +18 -0
  227. package/dist/time/beatClock.js +24 -0
  228. package/dist/time/idleProgress.d.ts +29 -0
  229. package/dist/time/idleProgress.js +31 -0
  230. package/dist/time/serverTick.d.ts +7 -1
  231. package/dist/time/serverTick.js +27 -4
  232. package/dist/time/stateSchedule.d.ts +57 -0
  233. package/dist/time/stateSchedule.js +121 -0
  234. package/dist/turn/commit.d.ts +2 -2
  235. package/dist/turn/commit.js +2 -2
  236. package/dist/ui/gameLayout.d.ts +125 -0
  237. package/dist/ui/gameLayout.js +129 -0
  238. package/dist/ui/hudLayout.d.ts +58 -0
  239. package/dist/ui/hudLayout.js +207 -0
  240. package/dist/ui/hudScale.d.ts +44 -0
  241. package/dist/ui/hudScale.js +55 -0
  242. package/dist/ui/orientation.d.ts +39 -0
  243. package/dist/ui/orientation.js +49 -0
  244. package/dist/ui/swingTimer.d.ts +42 -0
  245. package/dist/ui/swingTimer.js +38 -0
  246. package/dist/visibility/assetStreaming.d.ts +66 -0
  247. package/dist/visibility/assetStreaming.js +201 -0
  248. package/dist/visibility/bounds.d.ts +66 -0
  249. package/dist/visibility/bounds.js +113 -0
  250. package/dist/visibility/camera.d.ts +21 -0
  251. package/dist/visibility/camera.js +3 -0
  252. package/dist/visibility/config.d.ts +21 -0
  253. package/dist/visibility/config.js +1 -0
  254. package/dist/visibility/diagnostics.d.ts +26 -0
  255. package/dist/visibility/diagnostics.js +36 -0
  256. package/dist/visibility/distance.d.ts +11 -0
  257. package/dist/visibility/distance.js +30 -0
  258. package/dist/visibility/frustum.d.ts +47 -0
  259. package/dist/visibility/frustum.js +186 -0
  260. package/dist/visibility/occlusion.d.ts +36 -0
  261. package/dist/visibility/occlusion.js +45 -0
  262. package/dist/visibility/settings.d.ts +75 -0
  263. package/dist/visibility/settings.js +49 -0
  264. package/dist/visibility/simulationCulling.d.ts +34 -0
  265. package/dist/visibility/simulationCulling.js +36 -0
  266. package/dist/visibility/spatialIndex.d.ts +45 -0
  267. package/dist/visibility/spatialIndex.js +212 -0
  268. package/dist/visibility/visibilitySystem.d.ts +90 -0
  269. package/dist/visibility/visibilitySystem.js +257 -0
  270. package/dist/world/buildings.d.ts +6 -0
  271. package/dist/world/buildings.js +152 -0
  272. package/dist/world/carve.d.ts +7 -2
  273. package/dist/world/carve.js +17 -7
  274. package/dist/world/cellStates.d.ts +45 -0
  275. package/dist/world/cellStates.js +103 -0
  276. package/dist/world/environmentSummary.d.ts +21 -4
  277. package/dist/world/environmentSummary.js +22 -3
  278. package/dist/world/features.d.ts +185 -8
  279. package/dist/world/features.js +64 -6
  280. package/dist/world/gridInstances.d.ts +7 -8
  281. package/dist/world/gridInstances.js +145 -7
  282. package/dist/world/lod.d.ts +17 -0
  283. package/dist/world/lod.js +66 -0
  284. package/dist/world/mapLayers.d.ts +60 -0
  285. package/dist/world/mapLayers.js +44 -0
  286. package/dist/world/massing.d.ts +45 -0
  287. package/dist/world/massing.js +581 -0
  288. package/dist/world/minimap.d.ts +13 -1
  289. package/dist/world/minimap.js +21 -2
  290. package/dist/world/roads.d.ts +47 -0
  291. package/dist/world/roads.js +146 -0
  292. package/dist/world/streets.d.ts +80 -0
  293. package/dist/world/streets.js +144 -0
  294. package/dist/world/terrain.d.ts +69 -3
  295. package/dist/world/terrain.js +309 -16
  296. package/dist/world/water.d.ts +7 -0
  297. package/dist/world/water.js +19 -4
  298. package/dist/world/weather.d.ts +3 -18
  299. package/dist/world/weather.js +4 -17
  300. package/dist/world/windZones.d.ts +47 -0
  301. package/dist/world/windZones.js +59 -0
  302. package/llms.txt +3013 -1726
  303. package/package.json +12 -2
@@ -33,6 +33,158 @@ export const DEFAULT_BUILDING_CONFIG = {
33
33
  probabilities: DEFAULT_PROBABILITIES,
34
34
  variants: DEFAULT_VARIANTS,
35
35
  };
36
+ export const DEFAULT_BUILDING_STYLE = "generic";
37
+ export const BUILDING_STYLE_PALETTES = {
38
+ generic: {
39
+ wall: "#83766a",
40
+ window: "#8ecae6",
41
+ awning: "#c2410c",
42
+ airConditioner: "#d4d4d8",
43
+ clothesline: "#facc15",
44
+ storefront: "#3f3f46",
45
+ shutter: "#52525b",
46
+ storeSign: "#f97316",
47
+ roof: "#57534e",
48
+ roofProp: "#14b8a6",
49
+ guardrail: "#a8a29e",
50
+ corner: "#6b6258",
51
+ },
52
+ capital: {
53
+ wall: "#cfc5ad",
54
+ window: "#33506e",
55
+ awning: "#7a1f2b",
56
+ airConditioner: "#b9b3a4",
57
+ clothesline: "#d9cfae",
58
+ storefront: "#4a3f33",
59
+ shutter: "#5d5647",
60
+ storeSign: "#d4af37",
61
+ roof: "#3b4252",
62
+ roofProp: "#8c7a4e",
63
+ guardrail: "#a89f88",
64
+ corner: "#b3a98d",
65
+ },
66
+ village: {
67
+ wall: "#b59e7a",
68
+ window: "#e9c46a",
69
+ awning: "#7f5539",
70
+ airConditioner: "#a8a29e",
71
+ clothesline: "#e07a5f",
72
+ storefront: "#5c4a36",
73
+ shutter: "#6e5a40",
74
+ storeSign: "#c8894a",
75
+ roof: "#8a5a3b",
76
+ roofProp: "#97724d",
77
+ guardrail: "#8f8271",
78
+ corner: "#8d7354",
79
+ },
80
+ desert: {
81
+ wall: "#c9a877",
82
+ window: "#3f6f6b",
83
+ awning: "#a3512e",
84
+ airConditioner: "#c9bfa8",
85
+ clothesline: "#d9b16f",
86
+ storefront: "#6e5233",
87
+ shutter: "#7d6140",
88
+ storeSign: "#b8742f",
89
+ roof: "#b5946a",
90
+ roofProp: "#9a7648",
91
+ guardrail: "#b09b77",
92
+ corner: "#a98c5d",
93
+ },
94
+ industrial: {
95
+ wall: "#6b7280",
96
+ window: "#9fb3c8",
97
+ awning: "#f97316",
98
+ airConditioner: "#9ca3af",
99
+ clothesline: "#eab308",
100
+ storefront: "#374151",
101
+ shutter: "#4b5563",
102
+ storeSign: "#f59e0b",
103
+ roof: "#374151",
104
+ roofProp: "#ef4444",
105
+ guardrail: "#d1d5db",
106
+ corner: "#52596b",
107
+ },
108
+ coastal: {
109
+ wall: "#e8e2d2",
110
+ window: "#7cc3d8",
111
+ awning: "#e76f51",
112
+ airConditioner: "#cfd8dc",
113
+ clothesline: "#f2cc8f",
114
+ storefront: "#35526b",
115
+ shutter: "#3a7ca5",
116
+ storeSign: "#ee6c4d",
117
+ roof: "#4f6d7a",
118
+ roofProp: "#62b6cb",
119
+ guardrail: "#c5ccc2",
120
+ corner: "#cfc5ab",
121
+ },
122
+ neon: {
123
+ wall: "#1b1b26",
124
+ window: "#22d3ee",
125
+ awning: "#ff2d95",
126
+ airConditioner: "#3f3f50",
127
+ clothesline: "#a78bfa",
128
+ storefront: "#12121a",
129
+ shutter: "#2a2a3a",
130
+ storeSign: "#f0abfc",
131
+ roof: "#14141d",
132
+ roofProp: "#34d399",
133
+ guardrail: "#52525f",
134
+ corner: "#24242f",
135
+ },
136
+ ruin: {
137
+ wall: "#6b6156",
138
+ window: "#1c1917",
139
+ awning: "#4a4238",
140
+ airConditioner: "#57534e",
141
+ clothesline: "#6b7f4f",
142
+ storefront: "#292524",
143
+ shutter: "#44403c",
144
+ storeSign: "#78716c",
145
+ roof: "#4a443e",
146
+ roofProp: "#6b7f4f",
147
+ guardrail: "#7c7268",
148
+ corner: "#57503f",
149
+ },
150
+ frontier: {
151
+ wall: "#7f5539",
152
+ window: "#d9ae62",
153
+ awning: "#6e3f1f",
154
+ airConditioner: "#8d8072",
155
+ clothesline: "#c98a5e",
156
+ storefront: "#4a3220",
157
+ shutter: "#5c3d26",
158
+ storeSign: "#b3702f",
159
+ roof: "#9c4a1a",
160
+ roofProp: "#7d6b52",
161
+ guardrail: "#937b5f",
162
+ corner: "#66492f",
163
+ },
164
+ aerial: {
165
+ wall: "#dfe7ec",
166
+ window: "#67c3cc",
167
+ awning: "#4d7ea8",
168
+ airConditioner: "#b8c4cc",
169
+ clothesline: "#9fd8df",
170
+ storefront: "#5c7684",
171
+ shutter: "#7b95a3",
172
+ storeSign: "#3aa7b8",
173
+ roof: "#aebcc4",
174
+ roofProp: "#e0f2f5",
175
+ guardrail: "#98a8b0",
176
+ corner: "#c3d0d7",
177
+ },
178
+ };
179
+ export function resolveBuildingPalette(style = DEFAULT_BUILDING_STYLE, overrides) {
180
+ const preset = BUILDING_STYLE_PALETTES[style];
181
+ if (preset === undefined) {
182
+ throw new Error(`Unknown building style "${style}". Valid styles: ${Object.keys(BUILDING_STYLE_PALETTES).join(", ")}. Use palette: { wall, window, ... } for a custom look.`);
183
+ }
184
+ if (overrides === undefined)
185
+ return preset;
186
+ return { ...preset, ...overrides };
187
+ }
36
188
  function hashU32(input) {
37
189
  let hash = 2166136261;
38
190
  for (let index = 0; index < input.length; index += 1) {
@@ -95,11 +95,16 @@ export declare class CarvableField implements TerrainField {
95
95
  readonly sampleNormal: TerrainField["sampleNormal"];
96
96
  private readonly base;
97
97
  private readonly edits;
98
+ private nextEditId;
98
99
  constructor(base: TerrainField);
99
100
  get editCount(): number;
100
101
  sampleHeight(x: number, z: number): number;
101
- carve(op: CraterOp): void;
102
- deposit(op: MoundOp): void;
102
+ /** Digs a bowl; the returned id undoes just this crater via `removeEdit` (#284.7). */
103
+ carve(op: CraterOp): number;
104
+ /** Raises a mound; the returned id undoes just this mound via `removeEdit` (#284.7). */
105
+ deposit(op: MoundOp): number;
106
+ /** Removes one edit by the id `carve`/`deposit` returned — capped-history destructibles evict without rebuilding the field. */
107
+ removeEdit(editId: number): boolean;
103
108
  clear(): void;
104
109
  }
105
110
  export declare function carvableTerrain(base: TerrainField): CarvableField;
@@ -135,7 +135,8 @@ export class CarvableField {
135
135
  waterLevel;
136
136
  sampleNormal;
137
137
  base;
138
- edits = [];
138
+ edits = new Map();
139
+ nextEditId = 1;
139
140
  constructor(base) {
140
141
  this.base = base;
141
142
  if (base.bounds !== undefined)
@@ -145,12 +146,11 @@ export class CarvableField {
145
146
  this.sampleNormal = withNormal((x, z) => this.sampleHeight(x, z));
146
147
  }
147
148
  get editCount() {
148
- return this.edits.length;
149
+ return this.edits.size;
149
150
  }
150
151
  sampleHeight(x, z) {
151
152
  let h = this.base.sampleHeight(x, z);
152
- for (let i = 0; i < this.edits.length; i += 1) {
153
- const e = this.edits[i];
153
+ for (const e of this.edits.values()) {
154
154
  const dx = x - e.x;
155
155
  const dz = z - e.z;
156
156
  const d = Math.sqrt(dx * dx + dz * dz);
@@ -160,14 +160,24 @@ export class CarvableField {
160
160
  }
161
161
  return h;
162
162
  }
163
+ /** Digs a bowl; the returned id undoes just this crater via `removeEdit` (#284.7). */
163
164
  carve(op) {
164
- this.edits.push({ x: op.x, z: op.z, radius: op.radius, delta: -op.depth });
165
+ const id = this.nextEditId++;
166
+ this.edits.set(id, { x: op.x, z: op.z, radius: op.radius, delta: -op.depth });
167
+ return id;
165
168
  }
169
+ /** Raises a mound; the returned id undoes just this mound via `removeEdit` (#284.7). */
166
170
  deposit(op) {
167
- this.edits.push({ x: op.x, z: op.z, radius: op.radius, delta: op.height });
171
+ const id = this.nextEditId++;
172
+ this.edits.set(id, { x: op.x, z: op.z, radius: op.radius, delta: op.height });
173
+ return id;
174
+ }
175
+ /** Removes one edit by the id `carve`/`deposit` returned — capped-history destructibles evict without rebuilding the field. */
176
+ removeEdit(editId) {
177
+ return this.edits.delete(editId);
168
178
  }
169
179
  clear() {
170
- this.edits.length = 0;
180
+ this.edits.clear();
171
181
  }
172
182
  }
173
183
  export function carvableTerrain(base) {
@@ -0,0 +1,45 @@
1
+ export interface CellStateGridConfig<TState extends string> {
2
+ cols: number;
3
+ rows: number;
4
+ /** The ordered escalation ladder; every cell starts at `states[0]`. */
5
+ states: readonly TState[];
6
+ /** World-space center of cell (0, 0), for `cellOf` world→cell mapping. Default `[0, 0]`. */
7
+ origin?: readonly [number, number];
8
+ /** World units per cell, for `cellOf`. Default `1`. */
9
+ cellSize?: number;
10
+ }
11
+ export interface CellRef {
12
+ col: number;
13
+ row: number;
14
+ }
15
+ /**
16
+ * A uniform world grid whose cells step through a discrete state ladder — pristine → cracked →
17
+ * burning → ruined — driven by whatever world events the game routes in (#284.8). Pure storage plus
18
+ * ladder mechanics; the game decides *when* to escalate, the grid guarantees ordered, clamped steps
19
+ * and cheap queries. `version()` bumps on every change for dirty-checked rendering.
20
+ */
21
+ export interface CellStateGrid<TState extends string> {
22
+ readonly cols: number;
23
+ readonly rows: number;
24
+ readonly states: readonly TState[];
25
+ stateAt(col: number, row: number): TState | null;
26
+ /** Ladder index at a cell; `-1` outside the grid. */
27
+ indexAt(col: number, row: number): number;
28
+ /** Step a cell up the ladder (clamped at the last state); returns the new state or `null` outside the grid. */
29
+ escalate(col: number, row: number, steps?: number): TState | null;
30
+ /** Step a cell back down (clamped at the first state). */
31
+ regress(col: number, row: number, steps?: number): TState | null;
32
+ set(col: number, row: number, state: TState): boolean;
33
+ /** Escalate every cell the predicate matches; returns how many changed. */
34
+ escalateWhere(predicate: (col: number, row: number, state: TState) => boolean, steps?: number): number;
35
+ cellsIn(state: TState): CellRef[];
36
+ counts(): Record<TState, number>;
37
+ /** World position → cell, using `origin`/`cellSize`; `null` outside the grid. */
38
+ cellOf(x: number, z: number): CellRef | null;
39
+ /** World-space center of a cell. */
40
+ centerOf(col: number, row: number): readonly [number, number];
41
+ /** Bumps on every state change — key re-renders/dirty checks off it. */
42
+ version(): number;
43
+ reset(): void;
44
+ }
45
+ export declare function createCellStateGrid<TState extends string>(config: CellStateGridConfig<TState>): CellStateGrid<TState>;
@@ -0,0 +1,103 @@
1
+ export function createCellStateGrid(config) {
2
+ if (config.states.length === 0)
3
+ throw new Error("cell state grid needs at least one state");
4
+ if (!(config.cols > 0) || !(config.rows > 0)) {
5
+ throw new Error(`cell state grid needs positive dimensions, got ${config.cols}x${config.rows}`);
6
+ }
7
+ const stateIndex = new Map();
8
+ config.states.forEach((state, index) => {
9
+ if (stateIndex.has(state))
10
+ throw new Error(`duplicate cell state: ${state}`);
11
+ stateIndex.set(state, index);
12
+ });
13
+ const [originX, originZ] = config.origin ?? [0, 0];
14
+ const cellSize = config.cellSize ?? 1;
15
+ const cells = new Int32Array(config.cols * config.rows);
16
+ let version = 0;
17
+ function inBounds(col, row) {
18
+ return Number.isInteger(col) && Number.isInteger(row) && col >= 0 && col < config.cols && row >= 0 && row < config.rows;
19
+ }
20
+ function indexOf(col, row) {
21
+ return row * config.cols + col;
22
+ }
23
+ function step(col, row, steps) {
24
+ if (!inBounds(col, row))
25
+ return null;
26
+ const at = indexOf(col, row);
27
+ const next = Math.max(0, Math.min(config.states.length - 1, cells[at] + steps));
28
+ if (next !== cells[at]) {
29
+ cells[at] = next;
30
+ version += 1;
31
+ }
32
+ return config.states[next];
33
+ }
34
+ return {
35
+ cols: config.cols,
36
+ rows: config.rows,
37
+ states: config.states,
38
+ stateAt: (col, row) => (inBounds(col, row) ? config.states[cells[indexOf(col, row)]] : null),
39
+ indexAt: (col, row) => (inBounds(col, row) ? cells[indexOf(col, row)] : -1),
40
+ escalate: (col, row, steps = 1) => step(col, row, Math.max(0, steps)),
41
+ regress: (col, row, steps = 1) => step(col, row, -Math.max(0, steps)),
42
+ set(col, row, state) {
43
+ const target = stateIndex.get(state);
44
+ if (target === undefined || !inBounds(col, row))
45
+ return false;
46
+ const at = indexOf(col, row);
47
+ if (cells[at] !== target) {
48
+ cells[at] = target;
49
+ version += 1;
50
+ }
51
+ return true;
52
+ },
53
+ escalateWhere(predicate, steps = 1) {
54
+ let changed = 0;
55
+ for (let row = 0; row < config.rows; row += 1) {
56
+ for (let col = 0; col < config.cols; col += 1) {
57
+ const at = indexOf(col, row);
58
+ if (!predicate(col, row, config.states[cells[at]]))
59
+ continue;
60
+ const next = Math.min(config.states.length - 1, cells[at] + Math.max(0, steps));
61
+ if (next !== cells[at]) {
62
+ cells[at] = next;
63
+ changed += 1;
64
+ }
65
+ }
66
+ }
67
+ if (changed > 0)
68
+ version += 1;
69
+ return changed;
70
+ },
71
+ cellsIn(state) {
72
+ const target = stateIndex.get(state);
73
+ const refs = [];
74
+ if (target === undefined)
75
+ return refs;
76
+ for (let row = 0; row < config.rows; row += 1) {
77
+ for (let col = 0; col < config.cols; col += 1) {
78
+ if (cells[indexOf(col, row)] === target)
79
+ refs.push({ col, row });
80
+ }
81
+ }
82
+ return refs;
83
+ },
84
+ counts() {
85
+ const result = Object.fromEntries(config.states.map((state) => [state, 0]));
86
+ for (let at = 0; at < cells.length; at += 1) {
87
+ result[config.states[cells[at]]] += 1;
88
+ }
89
+ return result;
90
+ },
91
+ cellOf(x, z) {
92
+ const col = Math.round((x - originX) / cellSize);
93
+ const row = Math.round((z - originZ) / cellSize);
94
+ return inBounds(col, row) ? { col, row } : null;
95
+ },
96
+ centerOf: (col, row) => [originX + col * cellSize, originZ + row * cellSize],
97
+ version: () => version,
98
+ reset() {
99
+ cells.fill(0);
100
+ version += 1;
101
+ },
102
+ };
103
+ }
@@ -1,6 +1,7 @@
1
- import { type GeneratedBuilding } from "./buildings.js";
2
- import type { BuildingEnvironmentDescriptor, EnvironmentWorldFeature, WorldBounds } from "./features.js";
1
+ import { type BuildingPalette, type BuildingStyle, type GeneratedBuilding } from "./buildings.js";
2
+ import type { BuildingEnvironmentDescriptor, EnvironmentArea, EnvironmentWorldFeature, WorldBounds } from "./features.js";
3
3
  import type { Aabb } from "./geometry.js";
4
+ import { type TerrainPalette } from "./terrain.js";
4
5
  export declare function resolveStructureBuildings(descriptor: BuildingEnvironmentDescriptor): GeneratedBuilding[];
5
6
  export interface TerrainHeightStats {
6
7
  min: number;
@@ -11,17 +12,19 @@ export interface TerrainHeightStats {
11
12
  export interface TerrainSummary {
12
13
  bounds: WorldBounds;
13
14
  height: TerrainHeightStats;
15
+ palette: TerrainPalette;
14
16
  waterLevel?: number;
15
17
  }
16
18
  export interface StructureSummary {
17
- style: string;
19
+ style: BuildingStyle;
20
+ palette: BuildingPalette;
18
21
  requested: number;
19
22
  buildings: number;
20
23
  parts: number;
21
24
  bounds: Aabb;
22
25
  }
23
26
  export interface VegetationSummary {
24
- area: WorldBounds;
27
+ area: EnvironmentArea;
25
28
  density: number;
26
29
  }
27
30
  export interface WaterSummary {
@@ -30,23 +33,37 @@ export interface WaterSummary {
30
33
  }
31
34
  export interface WeatherSummary {
32
35
  kind: "rain" | "snow";
36
+ area: EnvironmentArea;
33
37
  density: number;
34
38
  }
39
+ /** One road descriptor's resolved footprint: vertex count, width, and total centerline length. */
40
+ export interface RoadSummary {
41
+ points: number;
42
+ width: number;
43
+ length: number;
44
+ }
35
45
  export interface EnvironmentCounts {
36
46
  terrain: number;
47
+ islands: number;
37
48
  structureGroups: number;
38
49
  buildings: number;
39
50
  buildingParts: number;
40
51
  vegetationFields: number;
41
52
  waterBodies: number;
42
53
  weatherSystems: number;
54
+ roads: number;
55
+ }
56
+ export interface IslandSummary extends TerrainSummary {
57
+ origin: readonly [number, number];
43
58
  }
44
59
  export interface EnvironmentSummary {
45
60
  terrain?: TerrainSummary;
61
+ islands: readonly IslandSummary[];
46
62
  structures: readonly StructureSummary[];
47
63
  vegetation: readonly VegetationSummary[];
48
64
  water: readonly WaterSummary[];
49
65
  weather: readonly WeatherSummary[];
66
+ roads: readonly RoadSummary[];
50
67
  counts: EnvironmentCounts;
51
68
  isEmpty: boolean;
52
69
  }
@@ -1,5 +1,6 @@
1
- import { generateBuildingDistrict } from "./buildings.js";
2
- import { resolveTerrainField } from "./terrain.js";
1
+ import { generateBuildingDistrict, resolveBuildingPalette, } from "./buildings.js";
2
+ import { pathLength } from "./roads.js";
3
+ import { resolveTerrainField, resolveTerrainPalette } from "./terrain.js";
3
4
  export function resolveStructureBuildings(descriptor) {
4
5
  const columns = Math.max(1, Math.ceil(Math.sqrt(descriptor.count)));
5
6
  const rows = Math.max(1, Math.ceil(descriptor.count / columns));
@@ -74,6 +75,7 @@ function summarizeStructures(descriptor) {
74
75
  const buildings = resolveStructureBuildings(descriptor);
75
76
  return {
76
77
  style: descriptor.style,
78
+ palette: resolveBuildingPalette(descriptor.style, descriptor.palette),
77
79
  requested: descriptor.count,
78
80
  buildings: buildings.length,
79
81
  parts: buildings.reduce((sum, building) => sum + building.parts.length, 0),
@@ -84,11 +86,16 @@ function summarizeTerrain(descriptor) {
84
86
  return {
85
87
  bounds: descriptor.bounds,
86
88
  height: sampleTerrainHeights(descriptor),
89
+ palette: resolveTerrainPalette(descriptor),
87
90
  ...(descriptor.waterLevel === undefined ? {} : { waterLevel: descriptor.waterLevel }),
88
91
  };
89
92
  }
90
93
  export function summarizeEnvironment(feature) {
91
94
  const terrain = feature.terrain === undefined ? undefined : summarizeTerrain(feature.terrain);
95
+ const islands = (feature.islands ?? []).map((entry) => ({
96
+ ...summarizeTerrain({ ...entry, kind: "terrain" }),
97
+ origin: entry.origin,
98
+ }));
92
99
  const structures = (feature.structures ?? []).map(summarizeStructures);
93
100
  const vegetation = (feature.vegetation ?? []).map((entry) => ({
94
101
  area: entry.area,
@@ -100,29 +107,41 @@ export function summarizeEnvironment(feature) {
100
107
  }));
101
108
  const weather = (feature.weather ?? []).map((entry) => ({
102
109
  kind: entry.kind,
110
+ area: entry.area,
103
111
  density: entry.density,
104
112
  }));
113
+ const roads = (feature.roads ?? []).map((entry) => ({
114
+ points: entry.path.length,
115
+ width: entry.width,
116
+ length: pathLength(entry.path),
117
+ }));
105
118
  const buildings = structures.reduce((sum, entry) => sum + entry.buildings, 0);
106
119
  const counts = {
107
120
  terrain: terrain === undefined ? 0 : 1,
121
+ islands: islands.length,
108
122
  structureGroups: structures.length,
109
123
  buildings,
110
124
  buildingParts: structures.reduce((sum, entry) => sum + entry.parts, 0),
111
125
  vegetationFields: vegetation.length,
112
126
  waterBodies: water.length,
113
127
  weatherSystems: weather.length,
128
+ roads: roads.length,
114
129
  };
115
130
  const isEmpty = counts.terrain === 0 &&
131
+ counts.islands === 0 &&
116
132
  buildings === 0 &&
117
133
  counts.vegetationFields === 0 &&
118
134
  counts.waterBodies === 0 &&
119
- counts.weatherSystems === 0;
135
+ counts.weatherSystems === 0 &&
136
+ counts.roads === 0;
120
137
  return {
121
138
  ...(terrain === undefined ? {} : { terrain }),
139
+ islands,
122
140
  structures,
123
141
  vegetation,
124
142
  water,
125
143
  weather,
144
+ roads,
126
145
  counts,
127
146
  isEmpty,
128
147
  };