@onimaxxing/worldgen-node 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1034 @@
1
+ // Type surface for the worldgen editor snapshot. Imported and
2
+ // re-exported by `crates/oni-wasm/index.d.ts`.
3
+ //
4
+ // Usage: the active settings live in a `{ path → JSON-string }` map.
5
+ // Parse the entry you want to edit, mutate, write back, then call
6
+ // `worldgen.snapshot.load(files)`.
7
+ //
8
+ // import init, { worldgen, type World } from
9
+ // '@onimaxxing/worldgen';
10
+ //
11
+ // await init();
12
+ // const files = worldgen.snapshot.files();
13
+ // const world: World = JSON.parse(files['worlds/SandstoneDefault']);
14
+ // world.iconScale = 2.0;
15
+ // files['worlds/SandstoneDefault'] = JSON.stringify(world);
16
+ // worldgen.snapshot.load(files);
17
+ // // Cluster cache invalidated; next worldgen.generate(...) uses mutated settings.
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Bundle aggregates.
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /**
24
+ * Worldgen settings: world/subworld/cluster defs, noise trees, biome
25
+ * configs, trait pool, feature settings, and the three mixing tables.
26
+ */
27
+ export interface WorldgenBundle {
28
+ /** Global defaults applied when a world doesn't override them. */
29
+ defaults: DefaultSettings;
30
+ /**
31
+ * World definitions keyed by config path (e.g.
32
+ * `"worlds/SandstoneDefault"` for basegame,
33
+ * `"expansion1::worlds/VanillaSandstoneDefault"` for SpacedOut).
34
+ */
35
+ worlds: Record<string, World>;
36
+ /**
37
+ * Subworld (biome/zone) definitions keyed by config path. Worlds
38
+ * reference these via `subworldFiles`.
39
+ */
40
+ subworlds: Record<string, Subworld>;
41
+ /**
42
+ * Cluster layouts keyed by config path. Defines which worlds appear
43
+ * at which ring positions on the starmap.
44
+ */
45
+ clusters: Record<string, ClusterLayout>;
46
+ /**
47
+ * Noise-tree definitions keyed by config path. Drives terrain shape
48
+ * (base noise), biome borders (override noise), and density fields.
49
+ */
50
+ noise: Record<string, NoiseTree>;
51
+ /** Biome definitions (elemental banding, noise thresholds). */
52
+ biomes: Record<string, BiomeSettings>;
53
+ /**
54
+ * World trait pool. Each entry is a rollable trait (e.g.
55
+ * `GeoActive`, `Volcanoes`) and the modifiers it applies.
56
+ */
57
+ traits: Record<string, WorldTrait>;
58
+ /**
59
+ * Story trait pool. Traits applied when the coordinate's story code
60
+ * (`SNDST-C-42-0-4A-*`) names them.
61
+ */
62
+ story_traits: Record<string, WorldTrait>;
63
+ /** Per-feature configuration (geyser tables, ruins tables, etc.). */
64
+ feature_settings: Record<string, FeatureSettings>;
65
+ /**
66
+ * DLC-level mixing settings keyed by DLC id (e.g. `"expansion1"`).
67
+ * Controls which mixing options are available per cluster.
68
+ */
69
+ dlc_mixing: Record<string, DlcMixingSettings>;
70
+ /**
71
+ * Whole-world mixing options: substitute one asteroid type for
72
+ * another (e.g. a Ceres world slotted in for Sandstone).
73
+ */
74
+ world_mixing: Record<string, WorldMixingSettings>;
75
+ /**
76
+ * Subworld mixing options: substitute one biome for another within
77
+ * an asteroid.
78
+ */
79
+ subworld_mixing: Record<string, SubworldMixingSettings>;
80
+ }
81
+
82
+ /** Lookup tables referenced by name from `WorldgenBundle`. */
83
+ export interface LookupsBundle {
84
+ /** Mob spawn configs (critter placement rules) keyed by mob name. */
85
+ mobs: Record<string, MobConfig>;
86
+ /** Room placement configs keyed by room name. */
87
+ rooms: Record<string, RoomConfig>;
88
+ /** Named `[minKelvin, maxKelvin]` ranges. Subworlds reference these via `temperatureRange`. */
89
+ temperatures: Record<string, [number, number]>;
90
+ /** Pre-authored templates (bases, geyser rooms, POIs) keyed by template path. */
91
+ templates: Record<string, TemplateContainer>;
92
+ }
93
+
94
+ /** Full snapshot: worldgen and lookups in one blob. */
95
+ export interface FullBundle extends WorldgenBundle, LookupsBundle {}
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // Primitives and shared shapes.
99
+ // ---------------------------------------------------------------------------
100
+
101
+ /**
102
+ * Float range. Used for densities, starting-base positions,
103
+ * border sizes, and many other `{min, max}` fields. Min and max
104
+ * are both inclusive; `{min: 0.5, max: 0.5}` means "always 0.5".
105
+ */
106
+ export interface MinMax {
107
+ /** Lower bound (inclusive). */
108
+ min: number;
109
+ /** Upper bound (inclusive). */
110
+ max: number;
111
+ }
112
+
113
+ /**
114
+ * Integer 2D vector. The Rust struct accepts uppercase `X/Y` from
115
+ * YAML but serializes lowercase `x/y`, which is the shape you'll
116
+ * always see from the WASM API.
117
+ */
118
+ export interface Vector2I {
119
+ x: number;
120
+ y: number;
121
+ }
122
+
123
+ /** Integer `{x, y}` world dimensions in cells. */
124
+ export type WorldSize = Vector2I;
125
+
126
+ /**
127
+ * Overworld-cell layout algorithm.
128
+ * - `Default`: standard Voronoi-tree layout (used by every stock world).
129
+ * - `PowerTree`: weighted Voronoi via MIConvexHull, used by a handful of
130
+ * special worlds.
131
+ */
132
+ export type LayoutMethod = 'Default' | 'PowerTree';
133
+
134
+ /**
135
+ * Display category. `Asteroid` is the usual; `Moon` is used for
136
+ * orbiting micro-worlds in SpacedOut.
137
+ */
138
+ export type WorldCategory = 'Asteroid' | 'Moon';
139
+
140
+ /**
141
+ * How aggressively the template-spawning pass tries to satisfy a
142
+ * `TemplateSpawnRules` entry:
143
+ * - `GuaranteeOne`: fail worldgen if at least one can't be placed.
144
+ * - `GuaranteeSome`: guarantee `someCount`, fail otherwise.
145
+ * - `GuaranteeSomeTryMore`: `someCount` guaranteed, plus `moreCount`
146
+ * best-effort.
147
+ * - `GuaranteeAll`: fail if any of the listed names can't be placed.
148
+ * - `GuaranteeRange`: guarantee a count chosen uniformly in
149
+ * `[range.x, range.y]`.
150
+ * - `TryOne` / `TrySome` / `TryRange` / `TryAll`: best-effort variants
151
+ * that never fail worldgen.
152
+ */
153
+ export type ListRule =
154
+ | 'GuaranteeOne'
155
+ | 'GuaranteeSome'
156
+ | 'GuaranteeSomeTryMore'
157
+ | 'GuaranteeAll'
158
+ | 'GuaranteeRange'
159
+ | 'TryOne'
160
+ | 'TrySome'
161
+ | 'TryRange'
162
+ | 'TryAll';
163
+
164
+ /**
165
+ * How an `AllowedCellsFilter` treats its `tag` reference.
166
+ * - `Default`: no tag filter.
167
+ * - `AtTag`: only cells with the tag.
168
+ * - `NotAtTag`: only cells without the tag.
169
+ * - `DistanceFromTag`: filter by `minDistance`/`maxDistance` cells
170
+ * away from any cell carrying the tag.
171
+ */
172
+ export type TagCommand = 'Default' | 'AtTag' | 'NotAtTag' | 'DistanceFromTag';
173
+
174
+ /**
175
+ * Set operation applied to the candidate-cell list when a filter is
176
+ * evaluated. `Replace` is the default (filter results become the new
177
+ * candidate set).
178
+ */
179
+ export type FilterCommand =
180
+ | 'Clear'
181
+ | 'Replace'
182
+ | 'UnionWith'
183
+ | 'IntersectWith'
184
+ | 'ExceptWith'
185
+ | 'SymmetricExceptWith'
186
+ | 'All';
187
+
188
+ /**
189
+ * Point-sampling strategy used when placing subworld centroids and
190
+ * mob/room sample points. `PoissonDisk` (the default) produces
191
+ * evenly-spaced points; `UniformSquare` is a dense grid used by a
192
+ * few special cases.
193
+ */
194
+ export type SampleBehaviour = 'PoissonDisk' | 'UniformSquare';
195
+
196
+ /**
197
+ * Subworld zone type. Drives the game's zone colour, the
198
+ * `StateTransition` background element, and some gameplay systems
199
+ * (e.g. Radioactive exposure). Ceres, Prehistoric, and DLC5 (Aquatic)
200
+ * add the later entries in the list.
201
+ */
202
+ export type ZoneType =
203
+ | 'FrozenWastes'
204
+ | 'CrystalCaverns'
205
+ | 'BoggyMarsh'
206
+ | 'Sandstone'
207
+ | 'ToxicJungle'
208
+ | 'MagmaCore'
209
+ | 'OilField'
210
+ | 'Space'
211
+ | 'Ocean'
212
+ | 'Rust'
213
+ | 'Forest'
214
+ | 'Radioactive'
215
+ | 'Swamp'
216
+ | 'Wasteland'
217
+ | 'RocketInterior'
218
+ | 'Metallic'
219
+ | 'Barren'
220
+ | 'Moo'
221
+ | 'IceCaves'
222
+ | 'CarrotQuarry'
223
+ | 'SugarWoods'
224
+ | 'PrehistoricGarden'
225
+ | 'PrehistoricRaptor'
226
+ | 'PrehistoricWetlands'
227
+ | 'Abyss'
228
+ | 'Beach'
229
+ | 'KelpForest'
230
+ | 'Reef';
231
+
232
+ /**
233
+ * Named temperature band that subworlds reference. The actual Kelvin
234
+ * range for each name lives in `LookupsBundle.temperatures`. Stock
235
+ * ONI defines 13 bands spanning extremely cold → extremely hot; the
236
+ * last few aliases are kept for older configs.
237
+ */
238
+ export type TemperatureRange =
239
+ | 'ExtremelyCold'
240
+ | 'VeryVeryCold'
241
+ | 'VeryCold'
242
+ | 'Cold'
243
+ | 'Chilly'
244
+ | 'Cool'
245
+ | 'Mild'
246
+ | 'Room'
247
+ | 'HumanWarm'
248
+ | 'HumanHot'
249
+ | 'Hot'
250
+ | 'VeryHot'
251
+ | 'ExtremelyHot'
252
+ | 'HumanIdeal'
253
+ | 'HumanComfortable'
254
+ | 'HotMarginal'
255
+ | 'Warm'
256
+ | 'CoolMarginal';
257
+
258
+ /**
259
+ * A constraint on which overworld cells a subworld/template is
260
+ * allowed to occupy. Multiple filters chain via `command`; the
261
+ * allowed-cells set is reset/replaced/unioned/intersected in order
262
+ * by `sortOrder`.
263
+ */
264
+ export interface AllowedCellsFilter {
265
+ /** How to interpret the `tag` reference (see {@link TagCommand}). */
266
+ tagcommand?: TagCommand;
267
+ /** Layout tag this filter keys off. Omitted when `tagcommand` is `Default`. */
268
+ tag?: string | null;
269
+ /** Minimum cell distance when `tagcommand` is `DistanceFromTag`. */
270
+ minDistance?: number;
271
+ /** Maximum cell distance when `tagcommand` is `DistanceFromTag`. */
272
+ maxDistance?: number;
273
+ /** Set operation applied to the running candidate set. */
274
+ command?: FilterCommand;
275
+ /** Only allow cells with a temperature-range matching one of these. */
276
+ temperatureRanges?: TemperatureRange[];
277
+ /** Only allow cells whose subworld zone-type matches one of these. */
278
+ zoneTypes?: ZoneType[];
279
+ /** Only allow cells whose subworld config path is in this list. */
280
+ subworldNames?: string[];
281
+ /** Evaluation order; lower values apply earlier in the chain. */
282
+ sortOrder?: number;
283
+ /**
284
+ * If `tag` is set but no cell in the world has it, drop this filter
285
+ * silently rather than failing the pass.
286
+ */
287
+ ignoreIfMissingTag?: boolean;
288
+ }
289
+
290
+ /**
291
+ * One entry in `World.worldTraitRules`. The roll picks between `min`
292
+ * and `max` traits for the world; `specificTraits` narrows the pool
293
+ * to a specific list; `required*`/`forbidden*` filter on tags.
294
+ */
295
+ export interface TraitRule {
296
+ /** Minimum number of traits to pick (inclusive). */
297
+ min?: number;
298
+ /** Maximum number of traits to pick (inclusive). */
299
+ max?: number;
300
+ /** Only pick traits that carry every one of these tags. */
301
+ requiredTags?: string[] | null;
302
+ /** Restrict the pool to exactly these trait names. */
303
+ specificTraits?: string[] | null;
304
+ /** Skip traits carrying any of these tags. */
305
+ forbiddenTags?: string[] | null;
306
+ /** Skip these specific trait names. */
307
+ forbiddenTraits?: string[] | null;
308
+ }
309
+
310
+ /**
311
+ * One entry in `World.subworldMixingRules`. Controls how many copies
312
+ * of a given mixing-subworld can appear in the layout, gated by tag
313
+ * filters.
314
+ */
315
+ export interface SubworldMixingRule {
316
+ /** Name of the mixing-subworld this rule targets. */
317
+ name?: string;
318
+ /** Minimum copies to place. Default 0. */
319
+ minCount?: number;
320
+ /** Maximum copies to place. Default `i32::MAX`. */
321
+ maxCount?: number;
322
+ /** Skip if any of these tags are present. */
323
+ forbiddenTags?: string[];
324
+ /** Skip if any of these tags are absent. */
325
+ requiredTags?: string[];
326
+ }
327
+
328
+ /**
329
+ * Rule applied post-layout to add/remove layout tags on cells that
330
+ * pass `allowedCellsFilter`. Used to inject things like `"WarpWorld"`
331
+ * or `"StartWorld"` markers that downstream passes consume.
332
+ */
333
+ export interface ModifyLayoutTagsRule {
334
+ /** Tags to add on matching cells. */
335
+ addTags?: string[];
336
+ /** Tags to remove from matching cells. */
337
+ removeTags?: string[];
338
+ /** Filter chain selecting which cells the tag edits apply to. */
339
+ allowedCellsFilter?: AllowedCellsFilter[];
340
+ }
341
+
342
+ /**
343
+ * A template-spawn rule: place one of these templates at an overworld
344
+ * cell satisfying this filter. Collected on `World` or `Subworld` and
345
+ * evaluated in priority order.
346
+ */
347
+ export interface TemplateSpawnRules {
348
+ /** Human-readable identifier for this rule. Optional. */
349
+ ruleId?: string | null;
350
+ /** Template paths this rule picks from (e.g. `["geysers/generic_hot"]`). */
351
+ names?: string[];
352
+ /** How aggressively to satisfy the rule (see {@link ListRule}). */
353
+ listRule?: ListRule;
354
+ /** Used by `GuaranteeSome` / `GuaranteeSomeTryMore`: the guaranteed count. */
355
+ someCount?: number;
356
+ /** Used by `GuaranteeSomeTryMore`: additional best-effort count. */
357
+ moreCount?: number;
358
+ /** Total placements to attempt. Default 1. */
359
+ times?: number;
360
+ /**
361
+ * Priority for template-placement scheduling. Higher-priority rules
362
+ * are placed before lower ones, so hand-authored POIs land before
363
+ * generic fills.
364
+ */
365
+ priority?: number;
366
+ /** Allow the same template name to be picked more than once. */
367
+ allowDuplicates?: boolean;
368
+ /**
369
+ * Allow this template in `ExtremelyCold` / `ExtremelyHot` cells
370
+ * (normally skipped).
371
+ */
372
+ allowExtremeTemperatureOverlap?: boolean;
373
+ /**
374
+ * Allow placement inside the ring surrounding the starting base
375
+ * cell (normally excluded).
376
+ */
377
+ allowNearStart?: boolean;
378
+ /**
379
+ * Skip the usual "don't overlap prior template cells" check. Used
380
+ * sparingly for decorative overlays.
381
+ */
382
+ useRelaxedFiltering?: boolean;
383
+ /** With `GuaranteeRange`/`TryRange`: roll a uniform count in `[x, y]`. */
384
+ range?: Vector2I;
385
+ /** Relative offset from the centroid of the matched cell. Default `(0, 0)`. */
386
+ overrideOffset?: Vector2I;
387
+ /**
388
+ * Absolute placement override. `(-1, -1)` (the default) means "no
389
+ * override"; the rule picks a cell via the filter.
390
+ */
391
+ overridePlacement?: Vector2I;
392
+ /** Filter chain selecting candidate cells for placement. */
393
+ allowedCellsFilter?: AllowedCellsFilter[];
394
+ /**
395
+ * Extension knob: when `true` and the world placement has
396
+ * `sizeMultiplier < 1`, this rule's `times` is scaled by the
397
+ * multiplier with a per-rule KRandom roll for the fractional
398
+ * remainder. Lets shrunk worlds keep the same geyser density as
399
+ * full-size worlds.
400
+ *
401
+ * Mirrors the behaviour CGM applies to its geyser rules, but as a
402
+ * per-rule opt-in (no name-pattern matching).
403
+ */
404
+ scaleCountWithWorldSize?: boolean;
405
+ }
406
+
407
+ /**
408
+ * A single world's full config (ProcGen.World). Editing the worldsize
409
+ * of the starting world changes the grid dimensions of the asteroid
410
+ * you spawn on; editing `subworldFiles` changes which biomes appear
411
+ * in the Voronoi layout. `file_path` is stripped from JSON.
412
+ */
413
+ export interface World {
414
+ /** Internal identifier; usually matches the config path's last segment. */
415
+ name: string;
416
+ /** Loc-key or inline description shown in the world-select UI. */
417
+ description?: string | null;
418
+ /** Name-table paths used to generate a display name for this world. */
419
+ nameTables?: string[] | null;
420
+ /** Sprite shown for this asteroid in the starmap / cluster UI. */
421
+ asteroidIcon?: string | null;
422
+ /** Size multiplier on the starmap icon. Default 1.0. */
423
+ iconScale?: number;
424
+ /**
425
+ * Tags used by cluster/mixing/trait filters (e.g. `"RocketInterior"`,
426
+ * `"ForbiddenClusterCategory_Ceres"`). Matching rules in
427
+ * `ClusterLayout`/`WorldTrait`/`*MixingSettings` key off these.
428
+ */
429
+ worldTags?: string[];
430
+ /** Turn the world-traits pass off entirely for this world. */
431
+ disableWorldTraits?: boolean;
432
+ /**
433
+ * One or more rules describing how many traits to roll. Multiple
434
+ * rules stack, e.g. `[{min: 2, max: 4}, {specificTraits: ["GeoActive"]}]`.
435
+ */
436
+ worldTraitRules?: TraitRule[];
437
+ /**
438
+ * Scalar multiplier applied to trait impact (spawn counts, etc.).
439
+ * Default 1.0.
440
+ */
441
+ worldTraitScale?: number;
442
+ /** UI category: `Asteroid` or `Moon`. */
443
+ category?: WorldCategory;
444
+ /** Cell dimensions of this world's grid. Canonical sizes are 256×384 etc. */
445
+ worldsize?: WorldSize;
446
+ /**
447
+ * Number of rows at the top of the world hidden behind the
448
+ * starting-region fog of war until scanned. Default 0.
449
+ */
450
+ hiddenY?: number;
451
+ /**
452
+ * Per-world overrides for global `DefaultSettings`. Example:
453
+ * setting `defaultsOverrides.data.OverworldDensityMin` to a
454
+ * different value changes subworld placement density for this
455
+ * world only, without mutating the cluster-wide defaults.
456
+ */
457
+ defaultsOverrides?: DefaultSettings | null;
458
+ /** Overworld layout algorithm (default Voronoi tree). */
459
+ layoutMethod?: LayoutMethod;
460
+ /**
461
+ * Weighted list of subworld config paths this world is built from.
462
+ * Each subworld gets dropped into the layout according to its
463
+ * weight; counts are driven by `Subworld.density`.
464
+ */
465
+ subworldFiles?: WeightedSubworldName[];
466
+ /**
467
+ * Filter chain used when the layout lands a cell that no
468
+ * `subworldFile` claims; picks a fallback zone.
469
+ */
470
+ unknownCellsAllowedSubworlds?: AllowedCellsFilter[];
471
+ /**
472
+ * Per-world subworld-mixing rules. Populated when a mixing code is
473
+ * active (e.g. `MUWF1` in the coordinate) to inject biomes from
474
+ * other worlds.
475
+ */
476
+ subworldMixingRules?: SubworldMixingRule[];
477
+ /** Rules that add/remove layout tags after the layout finishes. */
478
+ modifyLayoutTags?: ModifyLayoutTagsRule[];
479
+ /** Name of the subworld that hosts the starting base. */
480
+ startSubworldName?: string | null;
481
+ /** Template stamped at the starting-base position (usually `bases/sandstoneBase`). */
482
+ startingBaseTemplate?: string | null;
483
+ /** Horizontal fraction of the world where the starting base lands, `[0, 1]`. */
484
+ startingBasePositionHorizontal?: MinMax;
485
+ /** Vertical fraction of the world where the starting base lands, `[0, 1]`. */
486
+ startingBasePositionVertical?: MinMax;
487
+ /**
488
+ * Insertion-ordered map of feature-name → priority. Iteration order
489
+ * is load-bearing; the game iterates this map in insertion order
490
+ * during feature distribution.
491
+ */
492
+ globalFeatures?: Record<string, number>;
493
+ /** World-level template rules (POIs placed by the world, not by subworlds). */
494
+ worldTemplateRules?: TemplateSpawnRules[];
495
+ /**
496
+ * Seasonal-event tags attached to this world (e.g. winter/holiday
497
+ * seasons in SpacedOut).
498
+ */
499
+ seasons?: string[];
500
+ /**
501
+ * World traits that always apply regardless of rolling (the "fixed"
502
+ * part of the worldTraitRules roll).
503
+ */
504
+ fixedTraits?: string[];
505
+ /**
506
+ * True if this world should be spawned adjacent to a Temporal Tear
507
+ * node in the SpacedOut starmap. Only honoured for SpacedOut.
508
+ */
509
+ adjacentTemporalTear?: boolean;
510
+ }
511
+
512
+ /**
513
+ * Weighted entry in a subworld pool. Total subworld count is driven
514
+ * by density; `weight` controls the proportional share each subworld
515
+ * claims. The `minCount` / `maxCount` pair clamps how many copies
516
+ * of this subworld can land in the layout.
517
+ */
518
+ export interface WeightedSubworldName {
519
+ /** Subworld config path. */
520
+ name: string;
521
+ /**
522
+ * Minimum copies to place (floor on the weighted roll).
523
+ * C# default 0.
524
+ */
525
+ minCount?: number;
526
+ /**
527
+ * Maximum copies to place (cap on the weighted roll).
528
+ * C# default `int.MaxValue`.
529
+ */
530
+ maxCount?: number;
531
+ /** Relative weight; higher means more copies placed. C# default 1.0. */
532
+ weight?: number;
533
+ /**
534
+ * Priority. Higher-priority subworlds are placed before lower-priority
535
+ * ones when the count roll contends across the pool. Default 0.
536
+ */
537
+ priority?: number;
538
+ /**
539
+ * Power-tree weighting override. Only consulted when the hosting
540
+ * world uses `layoutMethod: PowerTree`. Default 0.0.
541
+ */
542
+ overridePower?: number;
543
+ }
544
+
545
+ /**
546
+ * Point-sampling layer used for additional mob/room sample point
547
+ * generation within a Subworld. Each subworld can stack multiple
548
+ * samplers (e.g. one for Hatches, another for sweetle nests).
549
+ */
550
+ export interface Sampler {
551
+ /** Point-count range for this layer. */
552
+ density?: MinMax;
553
+ /** Minimum cell distance between accepted points. */
554
+ avoidRadius?: number;
555
+ /**
556
+ * If true, reject candidates that are too close to points from
557
+ * prior samplers (used to keep layers from overlapping).
558
+ */
559
+ doAvoidPoints?: boolean;
560
+ /** Point-sampler algorithm. */
561
+ sampleBehaviour?: SampleBehaviour;
562
+ }
563
+
564
+ /**
565
+ * A biome/zone definition (ProcGen.Subworld). Subworlds are the
566
+ * building blocks of a world's Voronoi layout: each overworld cell
567
+ * is assigned a subworld, and the subworld drives that cell's
568
+ * temperature, elemental banding, feature pool, and template rules.
569
+ */
570
+ export interface Subworld {
571
+ /** Internal identifier (often null for subworlds loaded by path). */
572
+ name?: string | null;
573
+ /** Loc-key for display name. */
574
+ nameKey?: string | null;
575
+ /** Loc-key for description. */
576
+ descriptionKey?: string | null;
577
+ /** Loc-key for utility/tooltip string. */
578
+ utilityKey?: string | null;
579
+ /** Path into `noise`: the primary biome noise. */
580
+ biomeNoise?: string | null;
581
+ /** Path into `noise`: used for biome-to-biome border transitions. */
582
+ overrideNoise?: string | null;
583
+ /** Path into `noise`: optional density modulation. */
584
+ densityNoise?: string | null;
585
+ /** Name of the border-biome config applied around this subworld. */
586
+ borderOverride?: string | null;
587
+ /** Higher wins when two neighbouring subworlds both claim a border. */
588
+ borderOverridePriority?: number;
589
+ /** Border thickness range in cells. C# default is `{min: 1.0, max: 2.5}`. */
590
+ borderSizeOverride?: MinMax;
591
+ /** Named temperature band (entries in `LookupsBundle.temperatures`). */
592
+ temperatureRange?: TemperatureRange;
593
+ /**
594
+ * Single guaranteed feature placed near the centre of this
595
+ * subworld's region (e.g. a geyser). Null means no central feature.
596
+ */
597
+ centralFeature?: Feature | null;
598
+ /** Additional features that may spawn scattered through the subworld. */
599
+ features?: Feature[];
600
+ /** Layout tags attached to every cell of this subworld. */
601
+ tags?: string[];
602
+ /** Subworld-count density in the Voronoi layout. */
603
+ density?: MinMax;
604
+ /** Minimum cell distance between centroids. */
605
+ avoidRadius?: number;
606
+ /** Respect the avoid-radius when placing relative to prior subworlds. */
607
+ doAvoidPoints?: boolean;
608
+ /**
609
+ * When true, child-cell Voronoi relaxation is skipped, producing
610
+ * jaggier edges. Used by a handful of biomes (Sandstone included).
611
+ */
612
+ dontRelaxChildren?: boolean;
613
+ /** Centroid-sampling algorithm. */
614
+ sampleBehaviour?: SampleBehaviour;
615
+ /** Extra point-sample layers for mob/room placement. */
616
+ samplers?: Sampler[];
617
+ /** Minimum child-cell count before the subworld is considered valid. */
618
+ minChildCount?: number;
619
+ /** Force every instance to have exactly one child cell. */
620
+ singleChildCount?: boolean;
621
+ /** Additional children to add on top of the density roll. */
622
+ extraBiomeChildren?: number;
623
+ /** Weighted pool of biome configs used to paint this subworld's cells. */
624
+ biomes?: WeightedBiome[];
625
+ /** Zone type (affects colour, StateTransition background, gameplay). */
626
+ zoneType?: ZoneType;
627
+ /** Weight used by `PowerTree` layouts. Ignored for `Default` layout. */
628
+ pdWeight?: number;
629
+ /** Relaxation iteration count. Advanced; rarely edited. */
630
+ iterations?: number;
631
+ /** Minimum Voronoi-energy threshold before stopping relaxation. */
632
+ minEnergy?: number;
633
+ /** Template-spawn rules scoped to this subworld's cells. */
634
+ subworldTemplateRules?: TemplateSpawnRules[] | null;
635
+ /** Catch-all for Subworld fields not enumerated above. */
636
+ [key: string]: unknown;
637
+ }
638
+
639
+ /**
640
+ * A geological feature (geyser room, dormant volcano, ancient ruins)
641
+ * that can spawn inside a subworld. Matches `ProcGen.Feature`.
642
+ */
643
+ export interface Feature {
644
+ /**
645
+ * Feature type: either a direct prefab name (e.g. `"GeyserGeneric"`)
646
+ * or a feature-settings path.
647
+ */
648
+ type?: string | null;
649
+ /** Layout tags attached to cells this feature occupies. */
650
+ tags?: string[];
651
+ /**
652
+ * Mobs spawned inside the feature when it generates (e.g. Hatches
653
+ * in a geyser room).
654
+ */
655
+ internalMobs?: InternalMob[] | null;
656
+ }
657
+
658
+ /**
659
+ * A mob entry inside a `Feature`. Rolls `count` copies of the named
660
+ * mob prefab at generation time.
661
+ */
662
+ export interface InternalMob {
663
+ /** Integer count range rolled uniformly. */
664
+ count?: CountRange | null;
665
+ /** Mob prefab name (e.g. `"Hatch"`). */
666
+ mob?: string | null;
667
+ }
668
+
669
+ /** Integer range used for internal-mob counts. */
670
+ export interface CountRange {
671
+ min: number;
672
+ max: number;
673
+ }
674
+
675
+ /**
676
+ * Weighted biome entry in `Subworld.biomes`. The worldgen picks
677
+ * one biome per subworld-cell weighted by these entries.
678
+ */
679
+ export interface WeightedBiome {
680
+ /** Biome config path. */
681
+ name: string;
682
+ /** Relative weight. */
683
+ weight?: number;
684
+ /**
685
+ * Layout tags attached to cells painted with this biome choice
686
+ * (on top of the subworld-level `tags`). Used by downstream
687
+ * filter chains.
688
+ */
689
+ tags?: string[];
690
+ }
691
+
692
+ /**
693
+ * Normalized-coordinate bounding box for the overworld base region.
694
+ * Lives under `DefaultSettings.baseData`. Units are arbitrary game
695
+ * space (not cells); the game scales this at placement time.
696
+ */
697
+ export interface BaseLocation {
698
+ left: number;
699
+ right: number;
700
+ bottom: number;
701
+ top: number;
702
+ }
703
+
704
+ /**
705
+ * One starting-inventory element to drop around the printing pod.
706
+ * Lives under `DefaultSettings.startingWorldElements`.
707
+ */
708
+ export interface StartingWorldElement {
709
+ /** Element name (e.g. `"Algae"`, `"Water"`). */
710
+ element: string;
711
+ /** Total mass in kilograms. */
712
+ amount: number;
713
+ }
714
+
715
+ /**
716
+ * Global default settings (ProcGen.DefaultSettings). The free-form
717
+ * `data` field carries key-value pairs (e.g. `OverworldDensityMin:
718
+ * 600`). Per-world overrides live on `World.defaultsOverrides` and
719
+ * layer on top of these at lookup time.
720
+ */
721
+ export interface DefaultSettings {
722
+ /** Bounding box used when the overworld renderer positions the base region. */
723
+ baseData?: BaseLocation | null;
724
+ /**
725
+ * Free-form key-value map. Stock keys include `OverworldDensityMin`,
726
+ * `OverworldAvoidRadius`, `OverworldMinNodes`, `DrawWorldBorder`,
727
+ * `OverworldSampleBehaviour`.
728
+ */
729
+ data?: Record<string, unknown> | null;
730
+ /** Initial resources placed around the printing pod. */
731
+ startingWorldElements?: StartingWorldElement[] | null;
732
+ /** Layout tags added to every overworld cell at the start of generation. */
733
+ overworldAddTags?: string[] | null;
734
+ /** Default `moveTags` applied when populating a subworld with cells. */
735
+ defaultMoveTags?: string[] | null;
736
+ }
737
+
738
+ // ---------------------------------------------------------------------------
739
+ // Mob & room configs.
740
+ // ---------------------------------------------------------------------------
741
+
742
+ /** Integer count range used by mob/room `density`. */
743
+ export interface MinMaxConfig {
744
+ min: number;
745
+ max: number;
746
+ }
747
+
748
+ /**
749
+ * Mob placement region (ProcGen.Mob.Location). Drives where a mob's
750
+ * sample points are valid: `Floor` requires a solid floor below the
751
+ * cell, `Air` requires an empty gas cell, `Liquid` requires a liquid
752
+ * cell, etc. The default is `Floor`.
753
+ */
754
+ export type MobLocation =
755
+ | 'Floor'
756
+ | 'Ceiling'
757
+ | 'Air'
758
+ | 'BackWall'
759
+ | 'NearWater'
760
+ | 'NearLiquid'
761
+ | 'Solid'
762
+ | 'Water'
763
+ | 'ShallowLiquid'
764
+ | 'Surface'
765
+ | 'LiquidFloor'
766
+ | 'AnyFloor'
767
+ | 'LiquidCeiling'
768
+ | 'Liquid'
769
+ | 'EntombedFloorPeek';
770
+
771
+ /**
772
+ * Mob spawn configuration (ProcGen.Mob). Controls how many copies of
773
+ * a mob prefab get scattered across the grid after layout.
774
+ */
775
+ export interface MobConfig {
776
+ /** Prefab-selection strategy name (e.g. `"WeightedRandom"`). */
777
+ selectMethod?: string;
778
+ /** Roll range for the count of this mob placed per world. */
779
+ density?: MinMaxConfig;
780
+ /** Point-sampling strategy name for placement. */
781
+ sampleBehaviour?: string;
782
+ /** Cell-type region where sample points are accepted. */
783
+ location?: MobLocation;
784
+ /** Prefab name spawned by this entry. */
785
+ prefabName?: string | null;
786
+ /** Footprint width in cells. */
787
+ width?: number;
788
+ /** Footprint height in cells. */
789
+ height?: number;
790
+ /** Horizontal spacing to enforce between copies. */
791
+ paddingX?: number;
792
+ }
793
+
794
+ /**
795
+ * Room placement configuration (ProcGen.Room). Same mechanics as
796
+ * `MobConfig` but for carved rooms rather than creatures.
797
+ */
798
+ export interface RoomConfig {
799
+ /** Room-selection strategy name. */
800
+ selectMethod?: string;
801
+ /** Roll range for the number of rooms. */
802
+ density?: MinMaxConfig;
803
+ /** Point-sampling strategy name. */
804
+ sampleBehaviour?: string;
805
+ }
806
+
807
+ // ---------------------------------------------------------------------------
808
+ // Template containers (POIs, geysers, bases).
809
+ // ---------------------------------------------------------------------------
810
+
811
+ /**
812
+ * Template coordinate vector. Uppercase `X/Y` (matches the on-disk
813
+ * YAML), distinct from the lowercase {@link Vector2I} used by
814
+ * world/subworld configs.
815
+ */
816
+ export interface TemplateVec2I {
817
+ X: number;
818
+ Y: number;
819
+ }
820
+
821
+ /**
822
+ * Template metadata. Populated by Rust's `refresh_info()` when the
823
+ * loaded YAML's `min` is `(0, 0)`.
824
+ */
825
+ export interface TemplateInfo {
826
+ /** Bounding-box dimensions in cells. */
827
+ size: TemplateVec2I;
828
+ /** Total cell count (`cells.length` in practice). */
829
+ area: number;
830
+ /** Lower-left cell of the bounding box, relative to the template origin. */
831
+ min: TemplateVec2I;
832
+ /** Upper-right cell of the bounding box. */
833
+ max: TemplateVec2I;
834
+ }
835
+
836
+ /**
837
+ * A single cell in a template: one element of mass at a relative
838
+ * grid position. Templates are stamped onto the world grid by
839
+ * translating these positions by the template's spawn point.
840
+ */
841
+ export interface TemplateCell {
842
+ /** Element name (e.g. `"SandStone"`, `"Oxygen"`). Null for vacuum. */
843
+ element?: string | null;
844
+ /** Element mass in kg. */
845
+ mass: number;
846
+ /** Temperature in Kelvin. */
847
+ temperature: number;
848
+ /** X offset from template origin. */
849
+ location_x: number;
850
+ /** Y offset from template origin. */
851
+ location_y: number;
852
+ /** Disease name (e.g. `"SlimeLung"`). Null for sterile. */
853
+ diseaseName?: string | null;
854
+ /** Germ count. */
855
+ diseaseCount: number;
856
+ /**
857
+ * If true, this cell stays un-revealed (fog-of-war) even when
858
+ * templates are normally pre-revealed. Used by puzzle/POI cells.
859
+ */
860
+ preventFoWReveal?: boolean | null;
861
+ }
862
+
863
+ /**
864
+ * One item stored inside a container building (e.g. a RationBox
865
+ * pre-filled with meal-lice). Lives under `TemplatePrefab.storage`.
866
+ */
867
+ export interface TemplateStorageItem {
868
+ /** Prefab id of the stored item. */
869
+ id?: string | null;
870
+ /** Element override, if the item is an element-carrier. */
871
+ element?: string | null;
872
+ /** Quantity (mass for elements, count for creatures/items). */
873
+ units: number;
874
+ /** Temperature in Kelvin. */
875
+ temperature: number;
876
+ /** Free-form rottable state; carried through verbatim. */
877
+ rottable?: unknown;
878
+ }
879
+
880
+ /**
881
+ * A prefab placed by a template (building, pickupable, ore deposit,
882
+ * or other entity). Which `TemplateContainer` array it lives in
883
+ * determines how the game spawns it.
884
+ */
885
+ export interface TemplatePrefab {
886
+ /** Prefab id (e.g. `"Tile"`, `"Headquarters"`, `"Hatch"`). */
887
+ id?: string | null;
888
+ /** X offset from template origin (can be fractional). */
889
+ location_x: number;
890
+ /** Y offset from template origin (can be fractional). */
891
+ location_y: number;
892
+ /** Element override (for ore-like prefabs). */
893
+ element?: string | null;
894
+ /** Spawn temperature in Kelvin. */
895
+ temperature: number;
896
+ /** Count or mass; interpreted per prefab type. */
897
+ units: number;
898
+ /** Prefab type hint (e.g. `"Pickupable"`, `"Ore"`). */
899
+ type?: string | null;
900
+ /** Container contents (for storage buildings like RationBox). */
901
+ storage?: TemplateStorageItem[];
902
+ /** Rotation hint for orientable buildings. */
903
+ rotationOrientation?: string | null;
904
+ /** Connections bitfield for connectable prefabs (pipes, wires). */
905
+ connections?: number | null;
906
+ }
907
+
908
+ /**
909
+ * A pre-authored template (starting base, geyser room, artifact POI,
910
+ * etc.). Templates are stamped onto the world grid by a
911
+ * `TemplateSpawnRule` that picks this container's path. Matches
912
+ * `ProcGen.TemplateContainer`.
913
+ */
914
+ export interface TemplateContainer {
915
+ /** Internal template name. Usually matches the config path's last segment. */
916
+ name?: string | null;
917
+ /** Bounding-box metadata. */
918
+ info: TemplateInfo;
919
+ /** Element/mass/temperature per cell. */
920
+ cells: TemplateCell[];
921
+ /** Building prefabs (tiles, machines). */
922
+ buildings: TemplatePrefab[];
923
+ /** Pickupable prefabs (items, creatures). */
924
+ pickupables: TemplatePrefab[];
925
+ /** Elemental-ore deposits. */
926
+ elementalOres: TemplatePrefab[];
927
+ /** Other entities (lights, furniture, decor). */
928
+ otherEntities: TemplatePrefab[];
929
+ }
930
+
931
+ // ---------------------------------------------------------------------------
932
+ // Categories surfaced as opaque records. Editors can round-trip them
933
+ // safely; field-level typing can be added later.
934
+ // ---------------------------------------------------------------------------
935
+
936
+ /** Which worlds appear at which ring positions on the starmap, plus starmap POIs. */
937
+ export interface ClusterLayout { [key: string]: unknown }
938
+
939
+ /**
940
+ * Noise-tree definition (base / override / density). Editing primitive
941
+ * frequencies and seeds changes every cell of every world that
942
+ * references the tree.
943
+ */
944
+ export interface NoiseTree { [key: string]: unknown }
945
+
946
+ /** Element/mass bands keyed on noise-field thresholds. */
947
+ export interface BiomeSettings { [key: string]: unknown }
948
+
949
+ /** Trait definition: names, tags, and modifiers applied when a roll picks it. */
950
+ export interface WorldTrait { [key: string]: unknown }
951
+
952
+ /** Per-feature configuration (geyser type pool, ruins content tables, etc.). */
953
+ export interface FeatureSettings { [key: string]: unknown }
954
+
955
+ /** DLC-level mixing settings: which asteroid/subworld substitutions are eligible. */
956
+ export interface DlcMixingSettings { [key: string]: unknown }
957
+
958
+ /** Whole-world mixing option (substitute asteroid type A for B). */
959
+ export interface WorldMixingSettings { [key: string]: unknown }
960
+
961
+ /** Subworld mixing option (substitute biome A for B inside an asteroid). */
962
+ export interface SubworldMixingSettings { [key: string]: unknown }
963
+
964
+
965
+ // ---------------------------------------------------------------------------
966
+ // WorldgenSnapshot: the snapshot shape returned by the JSON-in/out
967
+ // snapshot helpers (`snapshot_load_vanilla`, `snapshot_from_files`,
968
+ // `snapshot_merge_files`, `snapshot_to_files`).
969
+ // ---------------------------------------------------------------------------
970
+
971
+ /**
972
+ * - `Strict` (default): closed Klei-parity surface; rejects custom
973
+ * `TemperatureRange` names at load time.
974
+ * - `Extensions`: opens `TemperatureRange` to `Custom(string)`
975
+ * provided the name resolves to a `tables.temperatures.add` entry.
976
+ */
977
+ export type SnapshotMode = 'Strict' | 'Extensions';
978
+
979
+ /**
980
+ * Klei-style file path key (e.g. `worldgen/biomes/Forest.yaml`, or
981
+ * DLC-qualified `expansion1::worldgen/biomes/Forest.yaml`).
982
+ */
983
+ export type FilePath = string;
984
+
985
+ /**
986
+ * Add/remove pair for table-shaped YAML files (mobs, rooms,
987
+ * temperatures, borders, rivers). Two snapshots compose by merging
988
+ * the `add` maps (last-wins) and unioning `remove`.
989
+ */
990
+ export interface ComposableDict<V> {
991
+ add: Record<string, V>;
992
+ remove: string[];
993
+ }
994
+
995
+ /** Whole-file-replace categories, keyed by the on-disk Klei path. */
996
+ export interface SnapshotFiles {
997
+ clusters: Record<FilePath, ClusterLayout>;
998
+ worlds: Record<FilePath, World>;
999
+ subworlds: Record<FilePath, Subworld>;
1000
+ traits: Record<FilePath, WorldTrait>;
1001
+ story_traits: Record<FilePath, WorldTrait>;
1002
+ feature_settings: Record<FilePath, FeatureSettings>;
1003
+ noise: Record<FilePath, NoiseTree>;
1004
+ templates: Record<FilePath, TemplateContainer>;
1005
+ }
1006
+
1007
+ /**
1008
+ * Table-shaped categories: Klei's single-file `add: {…}` /
1009
+ * `remove: […]` layout.
1010
+ */
1011
+ export interface SnapshotTables {
1012
+ biomes: ComposableDict<BiomeSettings>;
1013
+ mobs: ComposableDict<MobConfig>;
1014
+ rivers: ComposableDict<unknown>;
1015
+ rooms: ComposableDict<RoomConfig>;
1016
+ temperatures: ComposableDict<[number, number]>;
1017
+ borders: ComposableDict<Array<unknown>>;
1018
+ }
1019
+
1020
+ /** Wholesale-replace singletons: defaults + DLC/world/subworld mixing. */
1021
+ export interface SnapshotMixing {
1022
+ defaults: DefaultSettings;
1023
+ dlc_mixing: Record<string, DlcMixingSettings>;
1024
+ world_mixing: Record<string, WorldMixingSettings>;
1025
+ subworld_mixing: Record<string, SubworldMixingSettings>;
1026
+ }
1027
+
1028
+ /** The mod-ingestion snapshot returned and accepted by the snapshot helpers. */
1029
+ export interface WorldgenSnapshot {
1030
+ mode: SnapshotMode;
1031
+ files: SnapshotFiles;
1032
+ tables: SnapshotTables;
1033
+ mixing: SnapshotMixing;
1034
+ }