diff --git a/src/main/java/ru/bclib/api/biomes/SurfaceRuleBuilder.java b/src/main/java/ru/bclib/api/biomes/SurfaceRuleBuilder.java index c46e3704..bde5b093 100644 --- a/src/main/java/ru/bclib/api/biomes/SurfaceRuleBuilder.java +++ b/src/main/java/ru/bclib/api/biomes/SurfaceRuleBuilder.java @@ -1,21 +1,122 @@ package ru.bclib.api.biomes; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.SurfaceRules; import net.minecraft.world.level.levelgen.SurfaceRules.ConditionSource; +import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; +import net.minecraft.world.level.levelgen.placement.CaveSurface; +import ru.bclib.api.spawning.SpawnRuleBuilder; +import ru.bclib.api.spawning.SpawnRuleEntry; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.function.Supplier; public class SurfaceRuleBuilder { + private static final Map RULES_CACHE = Maps.newHashMap(); private static final SurfaceRuleBuilder INSTANCE = new SurfaceRuleBuilder(); - private List conditions; + private List rules = Lists.newArrayList(); + private SurfaceRuleEntry entryInstance; + private ResourceKey biomeKey; private SurfaceRuleBuilder() {} public static SurfaceRuleBuilder start() { + INSTANCE.biomeKey = null; + INSTANCE.rules.clear(); return INSTANCE; } - public static SurfaceRules.RuleSource build() { - return null; + /** + * Restricts surface to only one biome. + * @param biomeKey {@link ResourceKey} for the {@link Biome}. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder biome(ResourceKey biomeKey) { + this.biomeKey = biomeKey; + return this; + } + + /** + * Restricts surface to only one biome. + * @param biome {@link Biome}. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder biome(Biome biome) { + return biome(BiomeAPI.getBiomeKey(biome)); + } + + /** + * Set biome surface with specified {@link BlockState}. Example - block of grass in the Overworld biomes + * @param state {@link BlockState} for the ground cover. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder surface(BlockState state) { + entryInstance = getFromCache("surface_" + state.toString(), () -> { + RuleSource rule = SurfaceRules.state(state); + return new SurfaceRuleEntry(1, SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, rule)); + }); + return this; + } + + /** + * Set biome subsurface with specified {@link BlockState}. Example - dirt in the Overworld biomes. + * @param state {@link BlockState} for the subterrain layer. + * @param depth block layer depth. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder subsurface(BlockState state, int depth) { + entryInstance = getFromCache("subsurface_" + depth + "_" + state.toString(), () -> { + RuleSource rule = SurfaceRules.state(state); + rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(depth, false, false, CaveSurface.FLOOR), rule); + return new SurfaceRuleEntry(2, SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, rule)); + }); + return this; + } + + /** + * Set biome filler with specified {@link BlockState}. Example - stone in the Overworld biomes. + * @param state {@link BlockState} for filling. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder filler(BlockState state) { + entryInstance = getFromCache("fill_" + state.toString(), () -> { + return new SurfaceRuleEntry(3, SurfaceRules.state(state)); + }); + return this; + } + + /** + * Finalise rule building process. + * @return {@link SurfaceRules.RuleSource}. + */ + public SurfaceRules.RuleSource build() { + Collections.sort(rules); + SurfaceRules.RuleSource[] ruleArray = rules.toArray(new SurfaceRules.RuleSource[rules.size()]); + SurfaceRules.RuleSource rule = SurfaceRules.sequence(ruleArray); + if (biomeKey != null) { + rule = SurfaceRules.ifTrue(SurfaceRules.isBiome(biomeKey), rule); + } + return rule; + } + + /** + * Internal function, will take entry from cache or create it if necessary. + * @param name {@link String} entry internal name. + * @param supplier {@link Supplier} for {@link SurfaceRuleEntry}. + * @return new or existing {@link SurfaceRuleEntry}. + */ + private static SurfaceRuleEntry getFromCache(String name, Supplier supplier) { + SurfaceRuleEntry entry = RULES_CACHE.get(name); + if (entry == null) { + entry = supplier.get(); + RULES_CACHE.put(name, entry); + } + return entry; } } diff --git a/src/main/java/ru/bclib/api/biomes/SurfaceRuleEntry.java b/src/main/java/ru/bclib/api/biomes/SurfaceRuleEntry.java new file mode 100644 index 00000000..348dbbfb --- /dev/null +++ b/src/main/java/ru/bclib/api/biomes/SurfaceRuleEntry.java @@ -0,0 +1,24 @@ +package ru.bclib.api.biomes; + +import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.levelgen.SurfaceRules; +import org.jetbrains.annotations.NotNull; + +public class SurfaceRuleEntry implements Comparable { + private final SurfaceRules.RuleSource rule; + private final byte priority; + + public SurfaceRuleEntry(int priority, SurfaceRules.RuleSource rule) { + this.priority = (byte) priority; + this.rule = rule; + } + + protected SurfaceRules.RuleSource getRule() { + return rule; + } + + @Override + public int compareTo(@NotNull SurfaceRuleEntry entry) { + return Integer.compare(priority, entry.priority); + } +} diff --git a/src/main/java/ru/bclib/api/spawning/SpawnRuleEntry.java b/src/main/java/ru/bclib/api/spawning/SpawnRuleEntry.java index 710860c6..08461e44 100644 --- a/src/main/java/ru/bclib/api/spawning/SpawnRuleEntry.java +++ b/src/main/java/ru/bclib/api/spawning/SpawnRuleEntry.java @@ -19,7 +19,7 @@ public class SpawnRuleEntry implements Comparable this.rule = rule; } - boolean canSpawn(EntityType type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { + protected boolean canSpawn(EntityType type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return rule.canSpawn(type, world, spawnReason, pos, random); }