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