11<#--
22 # MCreator (https://mcreator.net/)
33 # Copyright (C) 2012-2020, Pylo
4- # Copyright (C) 2020-2024 , Pylo, opensource contributors
4+ # Copyright (C) 2020-2025 , Pylo, opensource contributors
55 #
66 # This program is free software: you can redistribute it and/or modify
77 # it under the terms of the GNU General Public License as published by
3030
3131<#-- @formatter:off -->
3232<#include "../procedures.java.ftl" >
33+ <#include "../mcitems.ftl" >
3334package ${package} .world.features;
3435
3536<#assign configuration = generator.map(featuretype, "features", 1) >
@@ -43,21 +44,35 @@ package ${package}.world.features;
4344 </#if >
4445 </#list >
4546</#if >
47+ <#assign placementPattern = r'\$([^$ ]+)\$'>
48+ <#assign placementMatches = placementcode?matches(placementPattern) >
49+ <#assign placementHardcodedElements = [ ]>
50+ <#list placementMatches as match >
51+ <#assign placementHardcodedElements = placementHardcodedElements + [match?groups[1 ]]>
52+ </#list >
53+ <#assign nonHardcodedPlacement = placementcode?replace(placementPattern, "", "r") >
54+ <#assign configurationMatches = configurationcode?matches(placementPattern) >
55+ <#assign configurationHardcodedElements = [ ]>
56+ <#list configurationMatches as match >
57+ <#assign configurationHardcodedElements = configurationHardcodedElements + [match?groups[1 ]]>
58+ </#list >
59+ <#assign nonHardcodedConfiguration = configurationcode?replace(placementPattern, "", "r") >
60+ <#assign allHardcodedElements = placementHardcodedElements + configurationHardcodedElements >
4661<#compress >
4762public class ${name} Feature extends ${generator.map(featuretype, "features") } {
48- private static ${name} Feature INSTANCE = null;
49- private static ConfiguredFeature<?, ?> CONFIGURED_FEATURE = null;
50- private static final Random random = new Random();
63+ private static ${name} Feature FEATURE = null;
64+ private static ConfiguredFeature<?, ?> CONFIGURED_FEATURE = null;
5165
5266 public ${name} Feature() {
5367 super(${configuration} ::deserialize);
5468 }
5569
5670 public static Feature<?> feature() {
57- INSTANCE = new ${name} Feature();
58- CONFIGURED_FEATURE = INSTANCE.withConfiguration(${configurationcode} );
71+ Random random = new Random();
72+ FEATURE = new ${name} Feature();
73+ CONFIGURED_FEATURE = <#if featuretype == "configured_feature_reference" >${nonHardcodedConfiguration} <#else >FEATURE.withConfiguration(<#if nonHardcodedConfiguration == "" >NoFeatureConfig.NO_FEATURE_CONFIG<#else >${nonHardcodedConfiguration} </#if >)</#if ><#if data.hasPlacedFeature() >${nonHardcodedPlacement} </#if >;
5974
60- return INSTANCE ;
75+ return FEATURE ;
6176 }
6277
6378 public static ConfiguredFeature<?, ?> configuredFeature() {
@@ -67,81 +82,6 @@ public class ${name}Feature extends ${generator.map(featuretype, "features")} {
6782 return CONFIGURED_FEATURE;
6883 }
6984
70- <#if configuration != "BaseTreeFeatureConfig" >
71- @Override public boolean place(IWorld world, ChunkGenerator generator, Random random, BlockPos pos, ${configuration} config) {
72- BlockPos placePos = pos;
73- <#if data.restrictionBiomes?has_content && cond >
74- DimensionType dimensionType = world.getDimension().getType();
75- boolean dimensionCriteria = false;
76- <#list w.filterBrokenReferences(data.restrictionBiomes) as restrictionBiome >
77- <#assign biomeName = fixNamespace(restrictionBiome) >
78- <#if biomeName == "#minecraft:is_overworld" >
79- if(dimensionType == World.OVERWORLD)
80- dimensionCriteria = true;
81- <#elseif biomeName == "#minecraft:is_nether" >
82- if(dimensionType == World.THE_NETHER)
83- dimensionCriteria = true;
84- <#else >
85- if(dimensionType == World.THE_END)
86- dimensionCriteria = true;
87- </#if >
88- </#list >
89-
90- if(!dimensionCriteria)
91- return false;
92- </#if >
93-
94- <#if data.hasPlacedFeature() >
95- <#if hasProcedure(data.generateCondition) >
96- int x = placePos.getX();
97- int y = placePos.getY();
98- int z = placePos.getZ();
99- if (!<@procedureOBJToConditionCode data.generateCondition/ >)
100- return false;
101- </#if >
102-
103- <#if placementcode.contains("Rarity") >
104- if(random.nextFloat() < 1.0F / (float) ${placementcode?keep_after("Rarity(")?keep_before(")") } ) {
105- </#if >
106- <#if placementcode.contains("Count") >
107- int count = ${placementcode?keep_after("Count(")?keep_before_last("^") } ;
108- for(int a = 0; a < count; a++) {
109- </#if >
110-
111- <#if placementcode != "" >
112- ${removeStrings(placementcode)}
113- </#if >
114-
115- <#if featuretype == "feature_random_patch_simple" >
116- if(!(${configurationcode?keep_after_last(".withCondition(")?keep_before_last(")")?replace("random.", name + "Feature.random.") } ))
117- return false;
118- </#if >
119-
120- <#if featuretype == "feature_simple_block" >
121- BlockState state = config.state;
122- if (state.isValidPosition(world, placePos)) {
123- if (state.getBlock() instanceof DoublePlantBlock) {
124- if (!world.isAirBlock(placePos.up()))
125- return false;
126- ((DoublePlantBlock) state.getBlock()).placeAt(world, placePos, 2);
127- } else
128- world.setBlockState(placePos, config.state, 2);
129- return true;
130- }
131- return false;
132- </#if >
133-
134- return super.place(world, generator, random, placePos, config);
135-
136- <#if placementcode.contains("Count") >}</#if >
137- <#if placementcode.contains("Rarity") >}</#if >
138- <#if placementcode.contains("Rarity") || placementcode.contains("Count") >return false;</#if >
139- <#else >
140- return super.place(world, generator, random, placePos, config);
141- </#if >
142- }
143- </#if >
144-
14585 public static final Set<ResourceLocation > GENERATE_BIOMES =
14686 <#if data.restrictionBiomes?has_content && !cond >
14787 ImmutableSet.of(
@@ -151,29 +91,68 @@ public class ${name}Feature extends ${generator.map(featuretype, "features")} {
15191 new ResourceLocation("${expandedBiome} ")<#sep >,
15292 </#list ><#sep >,
15393 </#list >
154- );
94+ )
15595 <#else >
156- null;
96+ null
97+ </#if >;
98+
99+ <#if data.restrictionBiomes?has_content && cond >
100+ private final Set<DimensionType > generateDimensions = ImmutableSet.of(
101+ <#list w.filterBrokenReferences(data.restrictionBiomes) as restrictionBiome >
102+ <#assign biomeName = fixNamespace(restrictionBiome) >
103+ <#if biomeName == "#minecraft:is_overworld" >
104+ DimensionType.OVERWORLD
105+ <#elseif biomeName == "#minecraft:is_nether" >
106+ DimensionType.THE_NETHER
107+ <#else >
108+ DimensionType.THE_END
109+ </#if ><#sep >,
110+ </#list >
111+ );
112+ </#if >
113+
114+ <#if featuretype == "feature_simple_block" || (data.hasPlacedFeature() && ((data.restrictionBiomes?has_content && cond) || data.hasGenerationConditions() || (allHardcodedElements?size > 0)))>
115+ @Override public boolean place(IWorld world, ChunkGenerator generator, Random random, BlockPos pos, ${configuration} config) {
116+ <#-- #4781 - we need to use WorldGenLevel instead of Level, or one can run incompatible procedures in condition -->
117+ BlockPos origin = pos;
118+ <#if data.restrictionBiomes?has_content && cond >
119+ if (!generateDimensions.contains(world.getDimension().getType()))
120+ return false;
121+ </#if >
122+
123+ <#if hasProcedure(data.generateCondition) >
124+ int x = origin.getX();
125+ int y = origin.getY();
126+ int z = origin.getZ();
127+ if (!<@procedureOBJToConditionCode data.generateCondition/ >)
128+ return false;
129+ </#if >
130+
131+ <#if data.hasPlacedFeature() && (allHardcodedElements?size > 0)>
132+ <#list allHardcodedElements as element >
133+ ${element}
134+ </#list >
135+ </#if >
136+
137+ <#if featuretype == "feature_simple_block" >
138+ BlockState state = config.state;
139+ if (state.isValidPosition(world, origin)) {
140+ if (state.getBlock() instanceof DoublePlantBlock) {
141+ if (!world.isAirBlock(origin.up()))
142+ return false;
143+ ((DoublePlantBlock) state.getBlock()).placeAt(world, origin, 2);
144+ } else
145+ world.setBlockState(origin, config.state, 2);
146+ return true;
147+ }
148+ return false;
149+ <#else >
150+ return super.place(world, generator, random, origin, config);
151+ </#if >
152+ }
157153 </#if >
158154}</#compress >
159155<#-- @formatter:on -->
160- <#function removeStrings str >
161- <#assign result = str >
162- <#list 1..countOccurrencesOfSlash(result) as i >
163- <#assign result_str = "/" + result?keep_after("/")?keep_before("/") + "/" >
164- <#assign result = result?replace(result_str, "") >
165- </#list >
166- <#return result >
167- </#function >
168- <#function countOccurrencesOfSlash input >
169- <#local count = 0 >
170- <#list 0..(input?length - 1) as i >
171- <#if input[i ] == "/">
172- <#assign count = count + 1 >
173- </#if >
174- </#list >
175- <#return count >
176- </#function >
177156<#function expandBiomeTag biomeTag >
178157 <#local result = [ ]>
179158
0 commit comments