1use super::prelude::*;
2use super::runner::FeatureDecorationRunner;
3use crate::worldgen::template::{
4 StructurePlaceSettings, StructureProcessorRandom, StructureTemplate,
5};
6use glam::IVec3;
7use steel_registry::structure::LiquidSettingsData;
8use steel_utils::BoundingBox;
9use steel_worldgen::structure::{StructureBlockIgnore, StructureMirror};
10
11struct ConfiguredFeaturePlaceContext<'a, 'region> {
12 region: &'a mut WorldGenRegion<'region>,
13 registry: &'a Registry,
14 random: &'a mut WorldgenRandom,
15 origin: BlockPos,
16 biome_zoom_seed: i64,
17}
18
19type ConfiguredFeaturePlacer = for<'a, 'region> fn(
20 &mut ConfiguredFeaturePlaceContext<'a, 'region>,
21 &ConfiguredFeatureKind,
22) -> bool;
23
24impl FeatureDecorationRunner {
25 pub(super) fn place_configured_feature(
26 region: &mut WorldGenRegion<'_>,
27 registry: &Registry,
28 random: &mut WorldgenRandom,
29 feature: &ConfiguredFeatureRef,
30 origin: BlockPos,
31 biome_zoom_seed: i64,
32 ) -> bool {
33 let kind = Self::configured_feature_kind(feature);
34 Self::place_configured_feature_kind(region, registry, random, kind, origin, biome_zoom_seed)
35 }
36
37 pub(super) fn place_configured_feature_kind(
38 region: &mut WorldGenRegion<'_>,
39 registry: &Registry,
40 random: &mut WorldgenRandom,
41 kind: &ConfiguredFeatureKind,
42 origin: BlockPos,
43 biome_zoom_seed: i64,
44 ) -> bool {
45 if !region.can_write_to_chunk(
46 SectionPos::block_to_section_coord(origin.x()),
47 SectionPos::block_to_section_coord(origin.z()),
48 ) {
49 return false;
50 }
51
52 let placer = Self::configured_feature_placer(kind);
53 let mut context = ConfiguredFeaturePlaceContext {
54 region,
55 registry,
56 random,
57 origin,
58 biome_zoom_seed,
59 };
60 placer(&mut context, kind)
61 }
62
63 pub(super) fn configured_feature_kind(
64 feature: &ConfiguredFeatureRef,
65 ) -> &ConfiguredFeatureKind {
66 match feature {
67 ConfiguredFeatureRef::Reference(configured_feature) => &configured_feature.kind,
68 ConfiguredFeatureRef::Inline(configured_feature) => configured_feature,
69 }
70 }
71
72 fn configured_feature_placer(kind: &ConfiguredFeatureKind) -> ConfiguredFeaturePlacer {
73 match kind {
74 ConfiguredFeatureKind::Bamboo(_) => place_bamboo,
75 ConfiguredFeatureKind::BasaltColumns(_) => place_basalt_columns,
76 ConfiguredFeatureKind::BasaltPillar => place_basalt_pillar,
77 ConfiguredFeatureKind::BlockBlob(_) => place_block_blob,
78 ConfiguredFeatureKind::BlockColumn(_) => place_block_column,
79 ConfiguredFeatureKind::BlockPile(_) => place_block_pile,
80 ConfiguredFeatureKind::BlueIce => place_blue_ice,
81 ConfiguredFeatureKind::BonusChest => place_bonus_chest,
82 ConfiguredFeatureKind::ChorusPlant => place_chorus_plant,
83 ConfiguredFeatureKind::CoralClaw => place_coral_claw,
84 ConfiguredFeatureKind::CoralMushroom => place_coral_mushroom,
85 ConfiguredFeatureKind::CoralTree => place_coral_tree,
86 ConfiguredFeatureKind::DeltaFeature(_) => place_delta_feature,
87 ConfiguredFeatureKind::DesertWell => place_desert_well,
88 ConfiguredFeatureKind::Disk(_) => place_disk,
89 ConfiguredFeatureKind::DripstoneCluster(_) => place_dripstone_cluster,
90 ConfiguredFeatureKind::EndGateway(_) => place_end_gateway,
91 ConfiguredFeatureKind::EndIsland => place_end_island,
92 ConfiguredFeatureKind::EndPlatform => place_end_platform,
93 ConfiguredFeatureKind::EndSpike(_) => place_end_spike,
94 ConfiguredFeatureKind::FallenTree(_) => place_fallen_tree,
95 ConfiguredFeatureKind::Fossil(_) => place_fossil,
96 ConfiguredFeatureKind::FreezeTopLayer => place_freeze_top_layer,
97 ConfiguredFeatureKind::Geode(_) => place_geode,
98 ConfiguredFeatureKind::GlowstoneBlob => place_glowstone_blob,
99 ConfiguredFeatureKind::HugeBrownMushroom(_) => place_huge_brown_mushroom,
100 ConfiguredFeatureKind::HugeFungus(_) => place_huge_fungus,
101 ConfiguredFeatureKind::HugeRedMushroom(_) => place_huge_red_mushroom,
102 ConfiguredFeatureKind::Iceberg(_) => place_iceberg,
103 ConfiguredFeatureKind::Kelp => place_kelp,
104 ConfiguredFeatureKind::Lake(_) => place_lake,
105 ConfiguredFeatureKind::LargeDripstone(_) => place_large_dripstone,
106 ConfiguredFeatureKind::MonsterRoom => place_monster_room,
107 ConfiguredFeatureKind::MultifaceGrowth(_) => place_multiface_growth,
108 ConfiguredFeatureKind::NetherForestVegetation(_) => place_nether_forest_vegetation,
109 ConfiguredFeatureKind::NetherrackReplaceBlobs(_) => place_netherrack_replace_blobs,
110 ConfiguredFeatureKind::Ore(_) => place_ore,
111 ConfiguredFeatureKind::PointedDripstone(_) => place_pointed_dripstone,
112 ConfiguredFeatureKind::RandomBooleanSelector(_) => place_random_boolean_selector,
113 ConfiguredFeatureKind::RandomSelector(_) => place_random_selector,
114 ConfiguredFeatureKind::WeightedRandomSelector(_) => place_weighted_random_selector,
115 ConfiguredFeatureKind::RootSystem(_) => place_root_system,
116 ConfiguredFeatureKind::ScatteredOre(_) => place_scattered_ore,
117 ConfiguredFeatureKind::SculkPatch(_) => place_sculk_patch,
118 ConfiguredFeatureKind::SeaPickle(_) => place_sea_pickle,
119 ConfiguredFeatureKind::Seagrass(_) => place_seagrass,
120 ConfiguredFeatureKind::Sequence(_) => place_sequence,
121 ConfiguredFeatureKind::SimpleBlock(_) => place_simple_block,
122 ConfiguredFeatureKind::SimpleRandomSelector(_) => place_simple_random_selector,
123 ConfiguredFeatureKind::Speleothem(_) => place_speleothem,
124 ConfiguredFeatureKind::SpeleothemCluster(_) => place_speleothem_cluster,
125 ConfiguredFeatureKind::Spike(_) => place_spike,
126 ConfiguredFeatureKind::SpringFeature(_) => place_spring_feature,
127 ConfiguredFeatureKind::Template(_) => place_template,
128 ConfiguredFeatureKind::Tree(_) => place_tree,
129 ConfiguredFeatureKind::TwistingVines(_) => place_twisting_vines,
130 ConfiguredFeatureKind::UnderwaterMagma(_) => place_underwater_magma,
131 ConfiguredFeatureKind::VegetationPatch(_) => place_vegetation_patch,
132 ConfiguredFeatureKind::Vines => place_vines,
133 ConfiguredFeatureKind::VoidStartPlatform => place_void_start_platform,
134 ConfiguredFeatureKind::WaterloggedVegetationPatch(_) => {
135 place_waterlogged_vegetation_patch
136 }
137 ConfiguredFeatureKind::WeepingVines => place_weeping_vines,
138 }
139 }
140}
141
142fn place_random_boolean_selector(
143 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
144 kind: &ConfiguredFeatureKind,
145) -> bool {
146 let ConfiguredFeatureKind::RandomBooleanSelector(config) = kind else {
147 panic!("random_boolean_selector placer received wrong configured feature kind");
148 };
149 let selected_feature = if context.random.next_bool() {
150 &config.feature_true
151 } else {
152 &config.feature_false
153 };
154 FeatureDecorationRunner::place_placed_feature_ref(
155 context.region,
156 context.registry,
157 context.random,
158 context.origin,
159 selected_feature,
160 context.biome_zoom_seed,
161 )
162}
163
164fn place_random_selector(
165 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
166 kind: &ConfiguredFeatureKind,
167) -> bool {
168 let ConfiguredFeatureKind::RandomSelector(config) = kind else {
169 panic!("random_selector placer received wrong configured feature kind");
170 };
171 for weighted_feature in &config.features {
172 let roll = context.random.next_f32();
173 if roll < weighted_feature.chance {
174 return FeatureDecorationRunner::place_placed_feature_ref(
175 context.region,
176 context.registry,
177 context.random,
178 context.origin,
179 &weighted_feature.feature,
180 context.biome_zoom_seed,
181 );
182 }
183 }
184
185 FeatureDecorationRunner::place_placed_feature_ref(
186 context.region,
187 context.registry,
188 context.random,
189 context.origin,
190 &config.default,
191 context.biome_zoom_seed,
192 )
193}
194
195fn place_weighted_random_selector(
196 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
197 kind: &ConfiguredFeatureKind,
198) -> bool {
199 let ConfiguredFeatureKind::WeightedRandomSelector(config) = kind else {
200 panic!("weighted_random_selector placer received wrong configured feature kind");
201 };
202 let Some(feature_index) = weighted_index(
203 config.features.len(),
204 |index| config.features[index].weight,
205 context.random,
206 ) else {
207 return false;
208 };
209 FeatureDecorationRunner::place_placed_feature_ref(
210 context.region,
211 context.registry,
212 context.random,
213 context.origin,
214 &config.features[feature_index].data,
215 context.biome_zoom_seed,
216 )
217}
218
219fn place_simple_random_selector(
220 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
221 kind: &ConfiguredFeatureKind,
222) -> bool {
223 let ConfiguredFeatureKind::SimpleRandomSelector(config) = kind else {
224 panic!("simple_random_selector placer received wrong configured feature kind");
225 };
226 assert!(
227 !config.features.is_empty(),
228 "simple random selector feature list must not be empty"
229 );
230 let Ok(feature_count) = i32::try_from(config.features.len()) else {
231 panic!(
232 "simple random selector feature count {} exceeds i32 range",
233 config.features.len()
234 );
235 };
236 let feature_index = context.random.next_i32_bounded(feature_count) as usize;
237 FeatureDecorationRunner::place_placed_feature_ref(
238 context.region,
239 context.registry,
240 context.random,
241 context.origin,
242 &config.features[feature_index],
243 context.biome_zoom_seed,
244 )
245}
246
247fn place_sequence(
248 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
249 kind: &ConfiguredFeatureKind,
250) -> bool {
251 let ConfiguredFeatureKind::Sequence(config) = kind else {
252 panic!("sequence placer received wrong configured feature kind");
253 };
254 for feature in &config.features {
255 if !FeatureDecorationRunner::place_placed_feature_ref(
256 context.region,
257 context.registry,
258 context.random,
259 context.origin,
260 feature,
261 context.biome_zoom_seed,
262 ) {
263 return false;
264 }
265 }
266 true
267}
268
269fn place_template(
270 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
271 kind: &ConfiguredFeatureKind,
272) -> bool {
273 let ConfiguredFeatureKind::Template(config) = kind else {
274 panic!("template placer received wrong configured feature kind");
275 };
276 let Some(template_index) = weighted_index(
277 config.templates.len(),
278 |index| config.templates[index].weight,
279 context.random,
280 ) else {
281 panic!("template feature has no selectable templates");
282 };
283 let entry = &config.templates[template_index].data;
284 let Ok(rotation_count) = i32::try_from(entry.rotations.len()) else {
285 panic!(
286 "template feature rotation count {} exceeds i32 range",
287 entry.rotations.len()
288 );
289 };
290 assert!(
291 rotation_count != 0,
292 "template feature entry {} has no rotations",
293 entry.id
294 );
295 let rotation = entry.rotations[context.random.next_i32_bounded(rotation_count) as usize];
296 let template = match StructureTemplate::load_vanilla(context.registry, &entry.id) {
297 Ok(template) => template,
298 Err(err) => panic!("{err}"),
299 };
300 let size = template.size(Rotation::None);
301 let position = template_feature_position(context.origin, rotation, size);
302 let settings = StructurePlaceSettings {
303 mirror: StructureMirror::None,
304 rotation,
305 rotation_pivot: BlockPos::ZERO,
306 bounding_box: template_feature_bounding_box(context.region),
307 processors: &[],
308 block_ignore: StructureBlockIgnore::None,
309 late_block_ignore: StructureBlockIgnore::None,
310 replace_jigsaws: false,
311 projection: None,
312 processor_random: StructureProcessorRandom::Placement,
313 liquid_settings: LiquidSettingsData::ApplyWaterlogging,
314 };
315
316 template.place_in_world(
317 context.region,
318 context.registry,
319 position,
320 position,
321 &settings,
322 context.random,
323 UpdateFlags::UPDATE_ALL,
324 )
325}
326
327fn weighted_index(
328 len: usize,
329 weight_at: impl Fn(usize) -> u32,
330 random: &mut WorldgenRandom,
331) -> Option<usize> {
332 let total_weight = (0..len)
333 .map(|index| u64::from(weight_at(index)))
334 .sum::<u64>();
335 if total_weight == 0 {
336 return None;
337 }
338 let Ok(total_weight_i32) = i32::try_from(total_weight) else {
339 panic!("weighted feature total weight {total_weight} exceeds i32 range");
340 };
341 let mut selection = random.next_i32_bounded(total_weight_i32) as u64;
342 for index in 0..len {
343 let weight = u64::from(weight_at(index));
344 if selection < weight {
345 return Some(index);
346 }
347 selection -= weight;
348 }
349 None
350}
351
352const fn template_feature_position(origin: BlockPos, rotation: Rotation, size: IVec3) -> BlockPos {
353 let west_offset = rotation.rotate(Direction::West).offset();
354 let north_offset = rotation.rotate(Direction::North).offset();
355 origin.offset(
356 west_offset.0 * (size.x / 2) + north_offset.0 * (size.z / 2),
357 0,
358 west_offset.2 * (size.x / 2) + north_offset.2 * (size.z / 2),
359 )
360}
361
362const fn template_feature_bounding_box(region: &WorldGenRegion<'_>) -> BoundingBox {
363 BoundingBox::new(
364 IVec3::new(i32::MIN, region.min_y(), i32::MIN),
365 IVec3::new(i32::MAX, region.max_y_exclusive() - 1, i32::MAX),
366 )
367}
368
369fn place_bamboo(
370 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
371 kind: &ConfiguredFeatureKind,
372) -> bool {
373 let ConfiguredFeatureKind::Bamboo(config) = kind else {
374 panic!("bamboo placer received wrong configured feature kind");
375 };
376 FeatureDecorationRunner::place_bamboo_feature(
377 context.region,
378 context.random,
379 config,
380 context.origin,
381 )
382}
383
384fn place_simple_block(
385 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
386 kind: &ConfiguredFeatureKind,
387) -> bool {
388 let ConfiguredFeatureKind::SimpleBlock(config) = kind else {
389 panic!("simple_block placer received wrong configured feature kind");
390 };
391 FeatureDecorationRunner::place_simple_block_feature(
392 context.region,
393 context.registry,
394 context.random,
395 config,
396 context.origin,
397 )
398}
399
400fn place_block_blob(
401 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
402 kind: &ConfiguredFeatureKind,
403) -> bool {
404 let ConfiguredFeatureKind::BlockBlob(config) = kind else {
405 panic!("block_blob placer received wrong configured feature kind");
406 };
407 FeatureDecorationRunner::place_block_blob_feature(
408 context.region,
409 context.registry,
410 context.random,
411 config,
412 context.origin,
413 )
414}
415
416fn place_vegetation_patch(
417 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
418 kind: &ConfiguredFeatureKind,
419) -> bool {
420 let ConfiguredFeatureKind::VegetationPatch(config) = kind else {
421 panic!("vegetation_patch placer received wrong configured feature kind");
422 };
423 FeatureDecorationRunner::place_vegetation_patch_feature(
424 context.region,
425 context.registry,
426 context.random,
427 config,
428 context.origin,
429 context.biome_zoom_seed,
430 )
431}
432
433fn place_waterlogged_vegetation_patch(
434 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
435 kind: &ConfiguredFeatureKind,
436) -> bool {
437 let ConfiguredFeatureKind::WaterloggedVegetationPatch(config) = kind else {
438 panic!("waterlogged_vegetation_patch placer received wrong configured feature kind");
439 };
440 FeatureDecorationRunner::place_waterlogged_vegetation_patch_feature(
441 context.region,
442 context.registry,
443 context.random,
444 config,
445 context.origin,
446 context.biome_zoom_seed,
447 )
448}
449
450fn place_block_column(
451 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
452 kind: &ConfiguredFeatureKind,
453) -> bool {
454 let ConfiguredFeatureKind::BlockColumn(config) = kind else {
455 panic!("block_column placer received wrong configured feature kind");
456 };
457 FeatureDecorationRunner::place_block_column_feature(
458 context.region,
459 context.registry,
460 context.random,
461 config,
462 context.origin,
463 )
464}
465
466fn place_block_pile(
467 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
468 kind: &ConfiguredFeatureKind,
469) -> bool {
470 let ConfiguredFeatureKind::BlockPile(config) = kind else {
471 panic!("block_pile placer received wrong configured feature kind");
472 };
473 FeatureDecorationRunner::place_block_pile_feature(
474 context.region,
475 context.registry,
476 context.random,
477 config,
478 context.origin,
479 )
480}
481
482fn place_disk(
483 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
484 kind: &ConfiguredFeatureKind,
485) -> bool {
486 let ConfiguredFeatureKind::Disk(config) = kind else {
487 panic!("disk placer received wrong configured feature kind");
488 };
489 FeatureDecorationRunner::place_disk_feature(
490 context.region,
491 context.registry,
492 context.random,
493 config,
494 context.origin,
495 )
496}
497
498fn place_basalt_pillar(
499 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
500 kind: &ConfiguredFeatureKind,
501) -> bool {
502 let ConfiguredFeatureKind::BasaltPillar = kind else {
503 panic!("basalt_pillar placer received wrong configured feature kind");
504 };
505 FeatureDecorationRunner::place_basalt_pillar_feature(
506 context.region,
507 context.random,
508 context.origin,
509 )
510}
511
512fn place_basalt_columns(
513 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
514 kind: &ConfiguredFeatureKind,
515) -> bool {
516 let ConfiguredFeatureKind::BasaltColumns(config) = kind else {
517 panic!("basalt_columns placer received wrong configured feature kind");
518 };
519 FeatureDecorationRunner::place_basalt_columns_feature(
520 context.region,
521 context.random,
522 config,
523 context.origin,
524 )
525}
526
527fn place_blue_ice(
528 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
529 kind: &ConfiguredFeatureKind,
530) -> bool {
531 let ConfiguredFeatureKind::BlueIce = kind else {
532 panic!("blue_ice placer received wrong configured feature kind");
533 };
534 FeatureDecorationRunner::place_blue_ice_feature(context.region, context.random, context.origin)
535}
536
537fn place_bonus_chest(
538 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
539 kind: &ConfiguredFeatureKind,
540) -> bool {
541 let ConfiguredFeatureKind::BonusChest = kind else {
542 panic!("bonus_chest placer received wrong configured feature kind");
543 };
544 FeatureDecorationRunner::place_bonus_chest_feature(
545 context.region,
546 context.random,
547 context.origin,
548 )
549}
550
551fn place_chorus_plant(
552 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
553 kind: &ConfiguredFeatureKind,
554) -> bool {
555 let ConfiguredFeatureKind::ChorusPlant = kind else {
556 panic!("chorus_plant placer received wrong configured feature kind");
557 };
558 FeatureDecorationRunner::place_chorus_plant_feature(
559 context.region,
560 context.random,
561 context.origin,
562 )
563}
564
565fn place_coral_claw(
566 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
567 kind: &ConfiguredFeatureKind,
568) -> bool {
569 let ConfiguredFeatureKind::CoralClaw = kind else {
570 panic!("coral_claw placer received wrong configured feature kind");
571 };
572 FeatureDecorationRunner::place_coral_claw_feature(
573 context.region,
574 context.registry,
575 context.random,
576 context.origin,
577 )
578}
579
580fn place_coral_mushroom(
581 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
582 kind: &ConfiguredFeatureKind,
583) -> bool {
584 let ConfiguredFeatureKind::CoralMushroom = kind else {
585 panic!("coral_mushroom placer received wrong configured feature kind");
586 };
587 FeatureDecorationRunner::place_coral_mushroom_feature(
588 context.region,
589 context.registry,
590 context.random,
591 context.origin,
592 )
593}
594
595fn place_coral_tree(
596 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
597 kind: &ConfiguredFeatureKind,
598) -> bool {
599 let ConfiguredFeatureKind::CoralTree = kind else {
600 panic!("coral_tree placer received wrong configured feature kind");
601 };
602 FeatureDecorationRunner::place_coral_tree_feature(
603 context.region,
604 context.registry,
605 context.random,
606 context.origin,
607 )
608}
609
610fn place_delta_feature(
611 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
612 kind: &ConfiguredFeatureKind,
613) -> bool {
614 let ConfiguredFeatureKind::DeltaFeature(config) = kind else {
615 panic!("delta_feature placer received wrong configured feature kind");
616 };
617 FeatureDecorationRunner::place_delta_feature(
618 context.region,
619 context.registry,
620 context.random,
621 config,
622 context.origin,
623 )
624}
625
626fn place_desert_well(
627 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
628 kind: &ConfiguredFeatureKind,
629) -> bool {
630 let ConfiguredFeatureKind::DesertWell = kind else {
631 panic!("desert_well placer received wrong configured feature kind");
632 };
633 FeatureDecorationRunner::place_desert_well_feature(
634 context.region,
635 context.random,
636 context.origin,
637 )
638}
639
640fn place_end_gateway(
641 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
642 kind: &ConfiguredFeatureKind,
643) -> bool {
644 let ConfiguredFeatureKind::EndGateway(config) = kind else {
645 panic!("end_gateway placer received wrong configured feature kind");
646 };
647 FeatureDecorationRunner::place_end_gateway_feature(context.region, config, context.origin)
648}
649
650fn place_end_island(
651 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
652 kind: &ConfiguredFeatureKind,
653) -> bool {
654 let ConfiguredFeatureKind::EndIsland = kind else {
655 panic!("end_island placer received wrong configured feature kind");
656 };
657 FeatureDecorationRunner::place_end_island_feature(
658 context.region,
659 context.random,
660 context.origin,
661 )
662}
663
664fn place_end_platform(
665 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
666 kind: &ConfiguredFeatureKind,
667) -> bool {
668 let ConfiguredFeatureKind::EndPlatform = kind else {
669 panic!("end_platform placer received wrong configured feature kind");
670 };
671 FeatureDecorationRunner::place_end_platform_feature(context.region, context.origin)
672}
673
674fn place_end_spike(
675 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
676 kind: &ConfiguredFeatureKind,
677) -> bool {
678 let ConfiguredFeatureKind::EndSpike(config) = kind else {
679 panic!("end_spike placer received wrong configured feature kind");
680 };
681 FeatureDecorationRunner::place_end_spike_feature(
682 context.region,
683 context.random,
684 config,
685 context.origin,
686 )
687}
688
689fn place_geode(
690 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
691 kind: &ConfiguredFeatureKind,
692) -> bool {
693 let ConfiguredFeatureKind::Geode(config) = kind else {
694 panic!("geode placer received wrong configured feature kind");
695 };
696 FeatureDecorationRunner::place_geode_feature(
697 context.region,
698 context.registry,
699 context.random,
700 config,
701 context.origin,
702 )
703}
704
705fn place_glowstone_blob(
706 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
707 kind: &ConfiguredFeatureKind,
708) -> bool {
709 let ConfiguredFeatureKind::GlowstoneBlob = kind else {
710 panic!("glowstone_blob placer received wrong configured feature kind");
711 };
712 FeatureDecorationRunner::place_glowstone_blob_feature(
713 context.region,
714 context.random,
715 context.origin,
716 )
717}
718
719fn place_huge_brown_mushroom(
720 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
721 kind: &ConfiguredFeatureKind,
722) -> bool {
723 let ConfiguredFeatureKind::HugeBrownMushroom(config) = kind else {
724 panic!("huge_brown_mushroom placer received wrong configured feature kind");
725 };
726 FeatureDecorationRunner::place_huge_brown_mushroom_feature(
727 context.region,
728 context.registry,
729 context.random,
730 config,
731 context.origin,
732 )
733}
734
735fn place_huge_red_mushroom(
736 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
737 kind: &ConfiguredFeatureKind,
738) -> bool {
739 let ConfiguredFeatureKind::HugeRedMushroom(config) = kind else {
740 panic!("huge_red_mushroom placer received wrong configured feature kind");
741 };
742 FeatureDecorationRunner::place_huge_red_mushroom_feature(
743 context.region,
744 context.registry,
745 context.random,
746 config,
747 context.origin,
748 )
749}
750
751fn place_huge_fungus(
752 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
753 kind: &ConfiguredFeatureKind,
754) -> bool {
755 let ConfiguredFeatureKind::HugeFungus(config) = kind else {
756 panic!("huge_fungus placer received wrong configured feature kind");
757 };
758 FeatureDecorationRunner::place_huge_fungus_feature(
759 context.region,
760 context.registry,
761 context.random,
762 config,
763 context.origin,
764 )
765}
766
767fn place_iceberg(
768 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
769 kind: &ConfiguredFeatureKind,
770) -> bool {
771 let ConfiguredFeatureKind::Iceberg(config) = kind else {
772 panic!("iceberg placer received wrong configured feature kind");
773 };
774 FeatureDecorationRunner::place_iceberg_feature(
775 context.region,
776 context.registry,
777 context.random,
778 config,
779 context.origin,
780 )
781}
782
783fn place_netherrack_replace_blobs(
784 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
785 kind: &ConfiguredFeatureKind,
786) -> bool {
787 let ConfiguredFeatureKind::NetherrackReplaceBlobs(config) = kind else {
788 panic!("netherrack_replace_blobs placer received wrong configured feature kind");
789 };
790 FeatureDecorationRunner::place_netherrack_replace_blobs_feature(
791 context.region,
792 context.registry,
793 context.random,
794 config,
795 context.origin,
796 )
797}
798
799fn place_nether_forest_vegetation(
800 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
801 kind: &ConfiguredFeatureKind,
802) -> bool {
803 let ConfiguredFeatureKind::NetherForestVegetation(config) = kind else {
804 panic!("nether_forest_vegetation placer received wrong configured feature kind");
805 };
806 FeatureDecorationRunner::place_nether_forest_vegetation_feature(
807 context.region,
808 context.registry,
809 context.random,
810 config,
811 context.origin,
812 )
813}
814
815fn place_twisting_vines(
816 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
817 kind: &ConfiguredFeatureKind,
818) -> bool {
819 let ConfiguredFeatureKind::TwistingVines(config) = kind else {
820 panic!("twisting_vines placer received wrong configured feature kind");
821 };
822 FeatureDecorationRunner::place_twisting_vines_feature(
823 context.region,
824 context.random,
825 config,
826 context.origin,
827 )
828}
829
830fn place_vines(
831 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
832 kind: &ConfiguredFeatureKind,
833) -> bool {
834 let ConfiguredFeatureKind::Vines = kind else {
835 panic!("vines placer received wrong configured feature kind");
836 };
837 FeatureDecorationRunner::place_vines_feature(context.region, context.origin)
838}
839
840fn place_void_start_platform(
841 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
842 kind: &ConfiguredFeatureKind,
843) -> bool {
844 let ConfiguredFeatureKind::VoidStartPlatform = kind else {
845 panic!("void_start_platform placer received wrong configured feature kind");
846 };
847 FeatureDecorationRunner::place_void_start_platform_feature(context.region, context.origin)
848}
849
850fn place_weeping_vines(
851 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
852 kind: &ConfiguredFeatureKind,
853) -> bool {
854 let ConfiguredFeatureKind::WeepingVines = kind else {
855 panic!("weeping_vines placer received wrong configured feature kind");
856 };
857 FeatureDecorationRunner::place_weeping_vines_feature(
858 context.region,
859 context.random,
860 context.origin,
861 )
862}
863
864fn place_spring_feature(
865 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
866 kind: &ConfiguredFeatureKind,
867) -> bool {
868 let ConfiguredFeatureKind::SpringFeature(config) = kind else {
869 panic!("spring_feature placer received wrong configured feature kind");
870 };
871 FeatureDecorationRunner::place_spring_feature(
872 context.region,
873 context.registry,
874 config,
875 context.origin,
876 )
877}
878
879fn place_kelp(
880 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
881 kind: &ConfiguredFeatureKind,
882) -> bool {
883 let ConfiguredFeatureKind::Kelp = kind else {
884 panic!("kelp placer received wrong configured feature kind");
885 };
886 FeatureDecorationRunner::place_kelp_feature(context.region, context.random, context.origin)
887}
888
889fn place_lake(
890 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
891 kind: &ConfiguredFeatureKind,
892) -> bool {
893 let ConfiguredFeatureKind::Lake(config) = kind else {
894 panic!("lake placer received wrong configured feature kind");
895 };
896 FeatureDecorationRunner::place_lake_feature(
897 context.region,
898 context.registry,
899 context.random,
900 config,
901 context.origin,
902 context.biome_zoom_seed,
903 )
904}
905
906fn place_monster_room(
907 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
908 kind: &ConfiguredFeatureKind,
909) -> bool {
910 let ConfiguredFeatureKind::MonsterRoom = kind else {
911 panic!("monster_room placer received wrong configured feature kind");
912 };
913 FeatureDecorationRunner::place_monster_room_feature(
914 context.region,
915 context.random,
916 context.origin,
917 )
918}
919
920fn place_freeze_top_layer(
921 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
922 kind: &ConfiguredFeatureKind,
923) -> bool {
924 let ConfiguredFeatureKind::FreezeTopLayer = kind else {
925 panic!("freeze_top_layer placer received wrong configured feature kind");
926 };
927 FeatureDecorationRunner::place_freeze_top_layer_feature(
928 context.region,
929 context.registry,
930 context.origin,
931 context.biome_zoom_seed,
932 )
933}
934
935fn place_multiface_growth(
936 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
937 kind: &ConfiguredFeatureKind,
938) -> bool {
939 let ConfiguredFeatureKind::MultifaceGrowth(config) = kind else {
940 panic!("multiface_growth placer received wrong configured feature kind");
941 };
942 FeatureDecorationRunner::place_multiface_growth_feature(
943 context.region,
944 context.registry,
945 context.random,
946 config,
947 context.origin,
948 )
949}
950
951fn place_sea_pickle(
952 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
953 kind: &ConfiguredFeatureKind,
954) -> bool {
955 let ConfiguredFeatureKind::SeaPickle(config) = kind else {
956 panic!("sea_pickle placer received wrong configured feature kind");
957 };
958 FeatureDecorationRunner::place_sea_pickle_feature(
959 context.region,
960 context.random,
961 config,
962 context.origin,
963 )
964}
965
966fn place_seagrass(
967 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
968 kind: &ConfiguredFeatureKind,
969) -> bool {
970 let ConfiguredFeatureKind::Seagrass(config) = kind else {
971 panic!("seagrass placer received wrong configured feature kind");
972 };
973 FeatureDecorationRunner::place_seagrass_feature(
974 context.region,
975 context.random,
976 config,
977 context.origin,
978 )
979}
980
981fn place_underwater_magma(
982 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
983 kind: &ConfiguredFeatureKind,
984) -> bool {
985 let ConfiguredFeatureKind::UnderwaterMagma(config) = kind else {
986 panic!("underwater_magma placer received wrong configured feature kind");
987 };
988 FeatureDecorationRunner::place_underwater_magma_feature(
989 context.region,
990 context.random,
991 config,
992 context.origin,
993 )
994}
995
996fn place_pointed_dripstone(
997 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
998 kind: &ConfiguredFeatureKind,
999) -> bool {
1000 let ConfiguredFeatureKind::PointedDripstone(config) = kind else {
1001 panic!("pointed_dripstone placer received wrong configured feature kind");
1002 };
1003 FeatureDecorationRunner::place_pointed_dripstone_feature(
1004 context.region,
1005 context.random,
1006 config,
1007 context.origin,
1008 )
1009}
1010
1011fn place_dripstone_cluster(
1012 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1013 kind: &ConfiguredFeatureKind,
1014) -> bool {
1015 let ConfiguredFeatureKind::DripstoneCluster(config) = kind else {
1016 panic!("dripstone_cluster placer received wrong configured feature kind");
1017 };
1018 FeatureDecorationRunner::place_dripstone_cluster_feature(
1019 context.region,
1020 context.random,
1021 config,
1022 context.origin,
1023 )
1024}
1025
1026fn place_speleothem(
1027 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1028 kind: &ConfiguredFeatureKind,
1029) -> bool {
1030 let ConfiguredFeatureKind::Speleothem(config) = kind else {
1031 panic!("speleothem placer received wrong configured feature kind");
1032 };
1033 FeatureDecorationRunner::place_speleothem_feature(
1034 context.region,
1035 context.random,
1036 config,
1037 context.origin,
1038 )
1039}
1040
1041fn place_speleothem_cluster(
1042 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1043 kind: &ConfiguredFeatureKind,
1044) -> bool {
1045 let ConfiguredFeatureKind::SpeleothemCluster(config) = kind else {
1046 panic!("speleothem_cluster placer received wrong configured feature kind");
1047 };
1048 FeatureDecorationRunner::place_speleothem_cluster_feature(
1049 context.region,
1050 context.random,
1051 config,
1052 context.origin,
1053 )
1054}
1055
1056fn place_large_dripstone(
1057 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1058 kind: &ConfiguredFeatureKind,
1059) -> bool {
1060 let ConfiguredFeatureKind::LargeDripstone(config) = kind else {
1061 panic!("large_dripstone placer received wrong configured feature kind");
1062 };
1063 FeatureDecorationRunner::place_large_dripstone_feature(
1064 context.region,
1065 context.random,
1066 config,
1067 context.origin,
1068 )
1069}
1070
1071fn place_spike(
1072 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1073 kind: &ConfiguredFeatureKind,
1074) -> bool {
1075 let ConfiguredFeatureKind::Spike(config) = kind else {
1076 panic!("spike placer received wrong configured feature kind");
1077 };
1078 FeatureDecorationRunner::place_spike_feature(
1079 context.region,
1080 context.registry,
1081 context.random,
1082 config,
1083 context.origin,
1084 )
1085}
1086
1087fn place_ore(
1088 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1089 kind: &ConfiguredFeatureKind,
1090) -> bool {
1091 let ConfiguredFeatureKind::Ore(config) = kind else {
1092 panic!("ore placer received wrong configured feature kind");
1093 };
1094 FeatureDecorationRunner::place_ore_feature(
1095 context.region,
1096 context.registry,
1097 context.random,
1098 config,
1099 context.origin,
1100 )
1101}
1102
1103fn place_scattered_ore(
1104 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1105 kind: &ConfiguredFeatureKind,
1106) -> bool {
1107 let ConfiguredFeatureKind::ScatteredOre(config) = kind else {
1108 panic!("scattered_ore placer received wrong configured feature kind");
1109 };
1110 FeatureDecorationRunner::place_scattered_ore_feature(
1111 context.region,
1112 context.registry,
1113 context.random,
1114 config,
1115 context.origin,
1116 )
1117}
1118
1119fn place_sculk_patch(
1120 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1121 kind: &ConfiguredFeatureKind,
1122) -> bool {
1123 let ConfiguredFeatureKind::SculkPatch(config) = kind else {
1124 panic!("sculk_patch placer received wrong configured feature kind");
1125 };
1126 FeatureDecorationRunner::place_sculk_patch_feature(
1127 context.region,
1128 context.registry,
1129 context.random,
1130 config,
1131 context.origin,
1132 )
1133}
1134
1135fn place_tree(
1136 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1137 kind: &ConfiguredFeatureKind,
1138) -> bool {
1139 let ConfiguredFeatureKind::Tree(config) = kind else {
1140 panic!("tree placer received wrong configured feature kind");
1141 };
1142 FeatureDecorationRunner::place_tree_feature(
1143 context.region,
1144 context.registry,
1145 context.random,
1146 config,
1147 context.origin,
1148 context.biome_zoom_seed,
1149 )
1150}
1151
1152fn place_fallen_tree(
1153 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1154 kind: &ConfiguredFeatureKind,
1155) -> bool {
1156 let ConfiguredFeatureKind::FallenTree(config) = kind else {
1157 panic!("fallen_tree placer received wrong configured feature kind");
1158 };
1159 FeatureDecorationRunner::place_fallen_tree_feature(
1160 context.region,
1161 context.registry,
1162 context.random,
1163 config,
1164 context.origin,
1165 context.biome_zoom_seed,
1166 )
1167}
1168
1169fn place_fossil(
1170 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1171 kind: &ConfiguredFeatureKind,
1172) -> bool {
1173 let ConfiguredFeatureKind::Fossil(config) = kind else {
1174 panic!("fossil placer received wrong configured feature kind");
1175 };
1176 FeatureDecorationRunner::place_fossil_feature(
1177 context.region,
1178 context.registry,
1179 context.random,
1180 config,
1181 context.origin,
1182 )
1183}
1184
1185fn place_root_system(
1186 context: &mut ConfiguredFeaturePlaceContext<'_, '_>,
1187 kind: &ConfiguredFeatureKind,
1188) -> bool {
1189 let ConfiguredFeatureKind::RootSystem(config) = kind else {
1190 panic!("root_system placer received wrong configured feature kind");
1191 };
1192 FeatureDecorationRunner::place_root_system_feature(
1193 context.region,
1194 context.registry,
1195 context.random,
1196 config,
1197 context.origin,
1198 context.biome_zoom_seed,
1199 )
1200}
1201
1202#[cfg(test)]
1203mod tests {
1204 use super::*;
1205
1206 #[test]
1207 fn template_feature_position_uses_template_depth_for_north_offset() {
1208 assert_eq!(
1209 template_feature_position(
1210 BlockPos::new(100, 64, 200),
1211 Rotation::None,
1212 IVec3::new(10, 30, 6),
1213 ),
1214 BlockPos::new(95, 64, 197)
1215 );
1216 }
1217}