mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 15:17:59 +01:00
Adjust tree spawn rate and add a sweet apple orchard biome
This commit is contained in:
parent
9599d0044b
commit
ded8499e9f
14 changed files with 592 additions and 50 deletions
|
@ -66,7 +66,7 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka
|
|||
return true;
|
||||
}
|
||||
|
||||
protected BlockState getPlacedFruitState(Random random) {
|
||||
public BlockState getPlacedFruitState(Random random) {
|
||||
return fruit.get().getDefaultState();
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka
|
|||
|
||||
BlockState fruitState = world.getBlockState(fruitPosition);
|
||||
|
||||
if (stage == Stage.WITHERING && fruitState.isOf(fruit.get())) {
|
||||
if (stage == Stage.WITHERING && fruitState.isOf(fruit.get()) && world.isPlayerInRange(pos.getX(), pos.getY(), pos.getZ(), 25)) {
|
||||
if (world.random.nextInt(2) == 0) {
|
||||
Block.dropStack(world, fruitPosition, rottenFruitSupplier.get());
|
||||
} else {
|
||||
|
@ -141,7 +141,7 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka
|
|||
return TintedBlock.blend(foliageColor, overlay);
|
||||
}
|
||||
|
||||
private boolean isPositionValidForFruit(BlockState state, BlockPos pos) {
|
||||
public boolean isPositionValidForFruit(BlockState state, BlockPos pos) {
|
||||
return state.getRenderingSeed(pos) % 3 == 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ public class GoldenOakLeavesBlock extends FruitBearingBlock {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected BlockState getPlacedFruitState(Random random) {
|
||||
public BlockState getPlacedFruitState(Random random) {
|
||||
return super.getPlacedFruitState(random).with(EnchantedFruitBlock.ENCHANTED, random.nextInt(1000) == 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package com.minelittlepony.unicopia.mixin;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
import com.minelittlepony.unicopia.server.world.gen.BiomeSelectionInjector;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
|
||||
import net.minecraft.world.biome.source.util.VanillaBiomeParameters;
|
||||
|
||||
@Mixin(VanillaBiomeParameters.class)
|
||||
abstract class MixinVanillaBiomeParameters {
|
||||
@ModifyVariable(method = "writeOverworldBiomeParameters", at = @At("HEAD"))
|
||||
private Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> onWriteOverworldBiomeParameters(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> parametersCollector) {
|
||||
return new BiomeSelectionInjector(parametersCollector);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import java.util.*;
|
|||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.minelittlepony.unicopia.block.UBlocks;
|
||||
|
||||
|
@ -17,10 +18,11 @@ import net.minecraft.util.Identifier;
|
|||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.registry.*;
|
||||
import net.minecraft.registry.tag.BiomeTags;
|
||||
import net.minecraft.world.gen.GenerationStep;
|
||||
import net.minecraft.world.biome.BiomeKeys;
|
||||
import net.minecraft.world.gen.feature.*;
|
||||
import net.minecraft.world.gen.feature.size.TwoLayersFeatureSize;
|
||||
import net.minecraft.world.gen.foliage.FoliagePlacer;
|
||||
import net.minecraft.world.gen.GenerationStep;
|
||||
import net.minecraft.world.gen.placementmodifier.PlacementModifier;
|
||||
import net.minecraft.world.gen.stateprovider.BlockStateProvider;
|
||||
import net.minecraft.world.gen.trunk.TrunkPlacer;
|
||||
|
@ -29,9 +31,8 @@ public record Tree (
|
|||
Identifier id,
|
||||
TreeFeatureConfig.Builder config,
|
||||
RegistryKey<ConfiguredFeature<?, ?>> configuredFeatureId,
|
||||
Optional<RegistryKey<PlacedFeature>> placedFeatureId,
|
||||
Optional<Block> sapling,
|
||||
Optional<PlacementModifier> placement
|
||||
Set<Placement> placements,
|
||||
Optional<Block> sapling
|
||||
) {
|
||||
public static final List<Tree> REGISTRY = new ArrayList<>();
|
||||
|
||||
|
@ -44,20 +45,22 @@ public record Tree (
|
|||
});
|
||||
registries.getOptional(RegistryKeys.PLACED_FEATURE).ifPresent(registry -> {
|
||||
var reg = registries.asDynamicRegistryManager().createRegistryLookup().getOrThrow(RegistryKeys.CONFIGURED_FEATURE);
|
||||
REGISTRY.stream().filter(tree -> tree.placedFeatureId().isPresent()).forEach(tree -> {
|
||||
var placedFeature = new PlacedFeature(reg.getOrThrow(tree.configuredFeatureId()),
|
||||
VegetationPlacedFeatures.treeModifiersWithWouldSurvive(tree.placement().orElseThrow(), tree.sapling().orElse(Blocks.OAK_SAPLING))
|
||||
);
|
||||
|
||||
Registry.register(registry, tree.id, placedFeature);
|
||||
REGISTRY.stream().forEach(tree -> {
|
||||
tree.placements().forEach(placement -> {
|
||||
Registry.register(registry, placement.id(), new PlacedFeature(reg.getOrThrow(tree.configuredFeatureId()),
|
||||
VegetationPlacedFeatures.treeModifiersWithWouldSurvive(placement.count(), tree.sapling().orElse(Blocks.OAK_SAPLING))
|
||||
));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
public static final Predicate<BiomeSelectionContext> IS_FOREST = BiomeSelectors.foundInOverworld().and(BiomeSelectors.tag(BiomeTags.IS_FOREST));
|
||||
public static final Predicate<BiomeSelectionContext> IS_OAK_FOREST = IS_FOREST
|
||||
.and(BiomeSelectors.excludeByKey(BiomeKeys.BIRCH_FOREST, BiomeKeys.OLD_GROWTH_BIRCH_FOREST, BiomeKeys.DARK_FOREST))
|
||||
.and(BiomeSelectors.tag(BiomeTags.IS_TAIGA).negate());
|
||||
|
||||
public static Builder create(Identifier id, TrunkPlacer trunkPlacer, FoliagePlacer foliagePlacer) {
|
||||
return new Builder(id, trunkPlacer, foliagePlacer);
|
||||
|
@ -73,8 +76,7 @@ public record Tree (
|
|||
|
||||
private final Identifier id;
|
||||
|
||||
private Optional<Predicate<BiomeSelectionContext>> selector = Optional.empty();
|
||||
private Optional<PlacementModifier> countModifier = Optional.empty();
|
||||
private Map<Identifier, Placement> placements = new HashMap<>();
|
||||
private Function<TreeFeatureConfig.Builder, TreeFeatureConfig.Builder> configParameters = Function.identity();
|
||||
private Optional<TwoLayersFeatureSize> size = Optional.empty();
|
||||
|
||||
|
@ -104,13 +106,18 @@ public record Tree (
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder count(int count, float extraChance, int extraCount) {
|
||||
countModifier = Optional.of(PlacedFeatures.createCountExtraModifier(count, extraChance, extraCount));
|
||||
return this;
|
||||
public Builder placement(int count, float extraChance, int extraCount, Predicate<BiomeSelectionContext> selector) {
|
||||
return placement("", count, extraChance, extraCount, selector);
|
||||
}
|
||||
|
||||
public Builder biomes(Predicate<BiomeSelectionContext> selector) {
|
||||
this.selector = Optional.of(selector);
|
||||
public Builder placement(String suffex, int count, float extraChance, int extraCount, Predicate<BiomeSelectionContext> selector) {
|
||||
Identifier id = this.id.withSuffixedPath("/placed" + (suffex.isEmpty() ? "" : "/") + suffex);
|
||||
placements.put(id, new Placement(
|
||||
id,
|
||||
PlacedFeatures.createCountExtraModifier(count, extraChance, extraCount),
|
||||
RegistryKey.of(RegistryKeys.PLACED_FEATURE, id),
|
||||
selector
|
||||
));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -119,7 +126,7 @@ public record Tree (
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder farmingCondition(int yLevel, int sizeBelowY, int sizeAboveY) {
|
||||
public Builder dimensions(int yLevel, int sizeBelowY, int sizeAboveY) {
|
||||
this.size = Optional.of(new TwoLayersFeatureSize(yLevel, Math.max(0, sizeBelowY), Math.max(0, sizeAboveY)));
|
||||
return this;
|
||||
}
|
||||
|
@ -132,23 +139,28 @@ public record Tree (
|
|||
BlockStateProvider.of(leavesType),
|
||||
foliagePlacer,
|
||||
size.get()
|
||||
)), configuredFeatureId, selector.map(selector -> {
|
||||
RegistryKey<PlacedFeature> i = RegistryKey.of(RegistryKeys.PLACED_FEATURE, id);
|
||||
BiomeModifications.addFeature(selector, GenerationStep.Feature.VEGETAL_DECORATION, i);
|
||||
return i;
|
||||
}), saplingId.map(id -> UBlocks.register(id, saplingConstructor.apply(new SaplingGenerator() {
|
||||
)), configuredFeatureId, placements.values().stream()
|
||||
.collect(Collectors.toUnmodifiableSet()),
|
||||
saplingId.map(id -> UBlocks.register(id, saplingConstructor.apply(new SaplingGenerator() {
|
||||
@Override
|
||||
protected RegistryKey<ConfiguredFeature<?, ?>> getTreeFeature(Random rng, boolean flowersNearby) {
|
||||
return configuredFeatureId;
|
||||
}
|
||||
}, FabricBlockSettings.copy(Blocks.OAK_SAPLING)), ItemGroups.NATURAL)), countModifier);
|
||||
}, FabricBlockSettings.copy(Blocks.OAK_SAPLING)), ItemGroups.NATURAL)));
|
||||
|
||||
if (REGISTRY.isEmpty()) {
|
||||
bootstrap();
|
||||
}
|
||||
|
||||
REGISTRY.add(tree);
|
||||
tree.placements().forEach(placement -> {
|
||||
BiomeModifications.addFeature(placement.selector(), GenerationStep.Feature.VEGETAL_DECORATION, placement.feature());
|
||||
});
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
|
||||
public record Placement(Identifier id, PlacementModifier count, RegistryKey<PlacedFeature> feature, Predicate<BiomeSelectionContext> selector) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ package com.minelittlepony.unicopia.server.world;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.block.UBlocks;
|
||||
import com.minelittlepony.unicopia.server.world.gen.FruitBlobFoliagePlacer;
|
||||
|
||||
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.intprovider.ConstantIntProvider;
|
||||
|
@ -39,19 +41,20 @@ public interface UTreeGen {
|
|||
.log(UBlocks.ZAP_LOG)
|
||||
.leaves(UBlocks.ZAP_LEAVES)
|
||||
.sapling(Unicopia.id("zapling"))
|
||||
.biomes(Tree.Builder.IS_FOREST)
|
||||
.count(0, 0.01F, 1)
|
||||
.farmingCondition(6, 0, 8)
|
||||
.placement(0, 0.01F, 1, Tree.Builder.IS_FOREST)
|
||||
.dimensions(6, 0, 8)
|
||||
.build();
|
||||
Tree GREEN_APPLE_TREE = createAppleTree("green_apple", UBlocks.GREEN_APPLE_LEAVES, 2);
|
||||
Tree SWEET_APPLE_TREE = createAppleTree("sweet_apple", UBlocks.SWEET_APPLE_LEAVES, 3);
|
||||
Tree SOUR_APPLE_TREE = createAppleTree("sour_apple", UBlocks.SOUR_APPLE_LEAVES, 5);
|
||||
Tree GREEN_APPLE_TREE = createAppleTree("green_apple", UBlocks.GREEN_APPLE_LEAVES, 2, 0.2F).build();
|
||||
Tree SWEET_APPLE_TREE = createAppleTree("sweet_apple", UBlocks.SWEET_APPLE_LEAVES, 3, 0.1F)
|
||||
.placement("orchard", 6, 0.1F, 3, BiomeSelectors.includeByKey(UWorldGen.SWEET_APPLE_ORCHARD))
|
||||
.build();
|
||||
Tree SOUR_APPLE_TREE = createAppleTree("sour_apple", UBlocks.SOUR_APPLE_LEAVES, 5, 0.2F).build();
|
||||
Tree GOLDEN_APPLE_TREE = Tree.Builder.create(Unicopia.id("golden_oak_tree"),
|
||||
new StraightTrunkPlacer(6, 1, 3),
|
||||
new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3)
|
||||
)
|
||||
.configure(TreeFeatureConfig.Builder::forceDirt)
|
||||
.farmingCondition(1, 3, 5)
|
||||
.dimensions(1, 3, 5)
|
||||
.log(UBlocks.GOLDEN_OAK_LOG)
|
||||
.leaves(UBlocks.GOLDEN_OAK_LEAVES)
|
||||
.sapling(Unicopia.id("golden_oak_sapling"))
|
||||
|
@ -60,7 +63,7 @@ public interface UTreeGen {
|
|||
new StraightTrunkPlacer(4, 5, 3),
|
||||
new FernFoliagePlacer(ConstantIntProvider.create(4), ConstantIntProvider.create(0))
|
||||
)
|
||||
.farmingCondition(6, 0, 8)
|
||||
.dimensions(6, 0, 8)
|
||||
.log(UBlocks.PALM_LOG)
|
||||
.leaves(UBlocks.PALM_LEAVES)
|
||||
.sapling(Unicopia.id("palm_sapling")).sapling((generator, settings) -> {
|
||||
|
@ -72,36 +75,33 @@ public interface UTreeGen {
|
|||
};
|
||||
})
|
||||
.configure(builder -> builder.dirtProvider(BlockStateProvider.of(Blocks.SAND)))
|
||||
.biomes(selector -> selector.hasTag(BiomeTags.IS_BEACH) || selector.hasTag(BiomeTags.IS_JUNGLE))
|
||||
.count(2, 0.01F, 1)
|
||||
.placement(2, 0.01F, 1, selector -> selector.hasTag(BiomeTags.IS_BEACH) || selector.hasTag(BiomeTags.IS_JUNGLE))
|
||||
.build();
|
||||
Tree MANGO_TREE = Tree.Builder.create(Unicopia.id("mango_tree"),
|
||||
new StraightTrunkPlacer(4, 7, 3),
|
||||
new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3)
|
||||
)
|
||||
.farmingCondition(9, 0, 4)
|
||||
.dimensions(9, 0, 4)
|
||||
.log(Blocks.JUNGLE_LOG)
|
||||
.leaves(UBlocks.MANGO_LEAVES)
|
||||
.biomes(selector -> selector.hasTag(BiomeTags.IS_JUNGLE) && selector.getBiomeKey() != BiomeKeys.SPARSE_JUNGLE)
|
||||
.sapling(Unicopia.id("mango_sapling"))
|
||||
.count(1, 1, 2)
|
||||
.placement(1, 1, 2, selector -> selector.hasTag(BiomeTags.IS_JUNGLE) && selector.getBiomeKey() != BiomeKeys.SPARSE_JUNGLE)
|
||||
.configure(builder -> builder.decorators(ImmutableList.of(TrunkVineTreeDecorator.INSTANCE, new LeavesVineTreeDecorator(0.25f))))
|
||||
.build();
|
||||
|
||||
static Tree createAppleTree(String name, Block leaves, int preferredDensity) {
|
||||
static Tree.Builder createAppleTree(String name, Block leaves, int preferredDensity, float spawnRate) {
|
||||
return Tree.Builder.create(Unicopia.id(name + "_tree"),
|
||||
new StraightTrunkPlacer(4, 3, 2),
|
||||
new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3)
|
||||
new FruitBlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3)
|
||||
)
|
||||
.configure(TreeFeatureConfig.Builder::forceDirt)
|
||||
.biomes(selector -> selector.hasTag(BiomeTags.IS_FOREST))
|
||||
.count(2, 0.01F, 1)
|
||||
.farmingCondition(1, preferredDensity - 2, preferredDensity)
|
||||
.placement(0, spawnRate, 4, Tree.Builder.IS_OAK_FOREST)
|
||||
.dimensions(1, preferredDensity - 2, preferredDensity)
|
||||
.log(Blocks.OAK_LOG)
|
||||
.leaves(leaves)
|
||||
.sapling(Unicopia.id(name + "_sapling"))
|
||||
.build();
|
||||
.sapling(Unicopia.id(name + "_sapling"));
|
||||
}
|
||||
|
||||
static void bootstrap() { }
|
||||
static void bootstrap() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.util.List;
|
|||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.block.ShellsBlock;
|
||||
import com.minelittlepony.unicopia.block.UBlocks;
|
||||
import com.minelittlepony.unicopia.server.world.gen.OverworldBiomeSelectionCallback;
|
||||
|
||||
import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
|
||||
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
|
||||
|
@ -13,11 +14,13 @@ import net.minecraft.block.Blocks;
|
|||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.tag.BiomeTags;
|
||||
import net.minecraft.util.collection.DataPool;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Vec3i;
|
||||
import net.minecraft.util.math.intprovider.UniformIntProvider;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeKeys;
|
||||
import net.minecraft.world.gen.GenerationStep;
|
||||
import net.minecraft.world.gen.blockpredicate.BlockPredicate;
|
||||
|
@ -66,6 +69,8 @@ public interface UWorldGen {
|
|||
BiomePlacementModifier.of()
|
||||
));
|
||||
|
||||
RegistryKey<Biome> SWEET_APPLE_ORCHARD = RegistryKey.of(RegistryKeys.BIOME, Unicopia.id("sweet_apple_orchard"));
|
||||
|
||||
static void bootstrap() {
|
||||
BiomeModifications.addFeature(BiomeSelectors.tag(BiomeTags.IS_JUNGLE), GenerationStep.Feature.VEGETAL_DECORATION, PINEAPPLE_PLANT_PLACED_FEATURE);
|
||||
BiomeModifications.addFeature(
|
||||
|
@ -75,5 +80,11 @@ public interface UWorldGen {
|
|||
.or(BiomeSelectors.includeByKey(BiomeKeys.STONY_SHORE))
|
||||
), GenerationStep.Feature.VEGETAL_DECORATION, SHELLS_PLACED_FEATURE);
|
||||
UTreeGen.bootstrap();
|
||||
|
||||
OverworldBiomeSelectionCallback.EVENT.register(context -> {
|
||||
if (context.biomeKey() == BiomeKeys.FOREST) {
|
||||
context.addOverride(context.referenceFrame().temperature().splitAbove(0.9F), SWEET_APPLE_ORCHARD);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package com.minelittlepony.unicopia.server.world.gen;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
|
||||
public interface BiomeSelectionContext {
|
||||
RegistryKey<Biome> biomeKey();
|
||||
|
||||
SplittableBiomeCoordinate referenceFrame();
|
||||
|
||||
@Nullable
|
||||
RegistryKey<Biome> addOverride(SplittableBiomeCoordinate coordinate, RegistryKey<Biome> biome);
|
||||
|
||||
public record SplittableBiomeCoordinate(
|
||||
SplitableParameterRange temperature,
|
||||
SplitableParameterRange humidity,
|
||||
SplitableParameterRange continentalness,
|
||||
SplitableParameterRange erosion,
|
||||
SplitableParameterRange depth,
|
||||
SplitableParameterRange weirdness,
|
||||
long offset) {
|
||||
}
|
||||
|
||||
public interface SplitableParameterRange {
|
||||
SplittableBiomeCoordinate splitAbove(float midpoint);
|
||||
|
||||
SplittableBiomeCoordinate splitBelow(float midpoint);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
package com.minelittlepony.unicopia.server.world.gen;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.include.com.google.common.base.Objects;
|
||||
|
||||
import com.minelittlepony.unicopia.server.world.gen.BiomeSelectionContext.SplittableBiomeCoordinate;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
|
||||
import net.minecraft.world.biome.source.util.MultiNoiseUtil.NoiseHypercube;
|
||||
import net.minecraft.world.biome.source.util.MultiNoiseUtil.ParameterRange;
|
||||
|
||||
public final class BiomeSelectionInjector implements Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> {
|
||||
|
||||
private final Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> parameterConsumer;
|
||||
|
||||
public BiomeSelectionInjector(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> parameterConsumer) {
|
||||
this.parameterConsumer = parameterConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Pair<NoiseHypercube, RegistryKey<Biome>> parameter) {
|
||||
Context context = new Context(parameter);
|
||||
OverworldBiomeSelectionCallback.EVENT.invoker().onSelectingBiome(context);
|
||||
if (!context.overrides.isEmpty()) {
|
||||
List<Pair<NoiseHypercube, RegistryKey<Biome>>> subSections = List.of(parameter);
|
||||
// sort splits from largest to smallest
|
||||
var divisions = context.overrides.entrySet()
|
||||
.stream()
|
||||
.sorted(Comparator.<Map.Entry<SplittableBiomeCoordinate, RegistryKey<Biome>>, Float>comparing(entry -> Context.getVolume(entry.getKey())).reversed())
|
||||
.toList();
|
||||
// recursively sub-divide into parts so every split gets a (kinda) fair share
|
||||
for (Map.Entry<SplittableBiomeCoordinate, RegistryKey<Biome>> division : divisions) {
|
||||
subSections = subSections.stream().flatMap(par -> Stream.of(
|
||||
Pair.of(Context.write(division.getKey(), par.getFirst()), division.getValue()),
|
||||
par
|
||||
)).toList();
|
||||
}
|
||||
// output the result
|
||||
subSections.forEach(parameterConsumer);
|
||||
} else {
|
||||
parameterConsumer.accept(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
class Context implements BiomeSelectionContext {
|
||||
private final Pair<NoiseHypercube, RegistryKey<Biome>> parameter;
|
||||
private final Map<SplittableBiomeCoordinate, RegistryKey<Biome>> overrides = new LinkedHashMap<>();
|
||||
|
||||
public Context(Pair<NoiseHypercube, RegistryKey<Biome>> parameter) {
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryKey<Biome> biomeKey() {
|
||||
return parameter.getSecond();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SplittableBiomeCoordinate referenceFrame() {
|
||||
return createStart(parameter.getFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public RegistryKey<Biome> addOverride(SplittableBiomeCoordinate coordinate, RegistryKey<Biome> biome) {
|
||||
return overrides.put(coordinate, biome);
|
||||
}
|
||||
|
||||
static float getVolume(SplittableBiomeCoordinate coordinate) {
|
||||
return ((SplitableParameterRangeImpl)coordinate.temperature()).length()
|
||||
* ((SplitableParameterRangeImpl)coordinate.humidity()).length()
|
||||
* ((SplitableParameterRangeImpl)coordinate.continentalness()).length()
|
||||
* ((SplitableParameterRangeImpl)coordinate.erosion()).length()
|
||||
* ((SplitableParameterRangeImpl)coordinate.depth()).length()
|
||||
* ((SplitableParameterRangeImpl)coordinate.weirdness()).length();
|
||||
}
|
||||
|
||||
static NoiseHypercube write(SplittableBiomeCoordinate coordinate, NoiseHypercube referenceFrame) {
|
||||
return new NoiseHypercube(
|
||||
((SplitableParameterRangeImpl)coordinate.temperature()).write(referenceFrame.temperature()),
|
||||
((SplitableParameterRangeImpl)coordinate.humidity()).write(referenceFrame.humidity()),
|
||||
((SplitableParameterRangeImpl)coordinate.continentalness()).write(referenceFrame.continentalness()),
|
||||
((SplitableParameterRangeImpl)coordinate.erosion()).write(referenceFrame.erosion()),
|
||||
((SplitableParameterRangeImpl)coordinate.depth()).write(referenceFrame.depth()),
|
||||
((SplitableParameterRangeImpl)coordinate.weirdness()).write(referenceFrame.weirdness()),
|
||||
coordinate.offset()
|
||||
);
|
||||
}
|
||||
|
||||
static SplittableBiomeCoordinate createStart(NoiseHypercube referenceFrame) {
|
||||
final SplittableBiomeCoordinate[] self = new SplittableBiomeCoordinate[1];
|
||||
return self[0] = new SplittableBiomeCoordinate(
|
||||
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::temperature, 0, 1),
|
||||
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::humidity, 0, 1),
|
||||
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::continentalness, 0, 1),
|
||||
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::erosion, 0, 1),
|
||||
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::depth, 0, 1),
|
||||
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::weirdness, 0, 1),
|
||||
referenceFrame.offset()
|
||||
);
|
||||
}
|
||||
|
||||
static SplittableBiomeCoordinate createCopy(SplittableBiomeCoordinate old) {
|
||||
final SplittableBiomeCoordinate[] self = new SplittableBiomeCoordinate[1];
|
||||
return self[0] = new SplittableBiomeCoordinate(
|
||||
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.temperature()),
|
||||
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.humidity()),
|
||||
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.continentalness()),
|
||||
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.erosion()),
|
||||
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.depth()),
|
||||
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.weirdness()),
|
||||
old.offset()
|
||||
);
|
||||
}
|
||||
|
||||
private static final class SplitableParameterRangeImpl implements SplitableParameterRange {
|
||||
private final SplittableBiomeCoordinate[] coordinate;
|
||||
private final Function<SplittableBiomeCoordinate, SplitableParameterRange> dimension;
|
||||
private float min;
|
||||
private float max;
|
||||
|
||||
SplitableParameterRangeImpl(SplittableBiomeCoordinate[] coordinate, SplitableParameterRangeImpl original) {
|
||||
this(coordinate, original.dimension, original.min, original.max);
|
||||
}
|
||||
|
||||
SplitableParameterRangeImpl(SplittableBiomeCoordinate[] coordinate, Function<SplittableBiomeCoordinate, SplitableParameterRange> dimension, float min, float max) {
|
||||
this.coordinate = coordinate;
|
||||
this.dimension = dimension;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SplittableBiomeCoordinate splitAbove(float midpoint) {
|
||||
return copyWithDifference(o -> o.min = MathHelper.lerp(midpoint, o.min, o.max));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SplittableBiomeCoordinate splitBelow(float midpoint) {
|
||||
return copyWithDifference(o -> o.max = MathHelper.lerp(midpoint, o.min, o.max));
|
||||
}
|
||||
|
||||
private SplittableBiomeCoordinate copyWithDifference(Consumer<SplitableParameterRangeImpl> mutator) {
|
||||
SplittableBiomeCoordinate copy = createCopy(coordinate[0]);
|
||||
mutator.accept((SplitableParameterRangeImpl)dimension.apply(copy));
|
||||
return copy;
|
||||
}
|
||||
|
||||
public ParameterRange write(ParameterRange range) {
|
||||
return ParameterRange.of(
|
||||
MultiNoiseUtil.toFloat(range.min()) * (1 + min),
|
||||
MultiNoiseUtil.toFloat(range.max()) * max
|
||||
);
|
||||
}
|
||||
|
||||
public float length() {
|
||||
return max - min;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof SplitableParameterRangeImpl i
|
||||
&& MathHelper.approximatelyEquals(i.min, min)
|
||||
&& MathHelper.approximatelyEquals(i.max, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(min, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.minelittlepony.unicopia.server.world.gen;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.joml.Vector2i;
|
||||
|
||||
import com.minelittlepony.unicopia.block.FruitBearingBlock;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.intprovider.IntProvider;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.TestableWorld;
|
||||
import net.minecraft.world.gen.feature.TreeFeatureConfig;
|
||||
import net.minecraft.world.gen.foliage.BlobFoliagePlacer;
|
||||
|
||||
public class FruitBlobFoliagePlacer extends BlobFoliagePlacer {
|
||||
|
||||
public FruitBlobFoliagePlacer(IntProvider radius, IntProvider offset, int height) {
|
||||
super(radius, offset, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(TestableWorld world, BlockPlacer placer, Random random, TreeFeatureConfig config, int trunkHeight, TreeNode treeNode, int foliageHeight, int radius) {
|
||||
final Map<Vector2i, Pair<FruitBearingBlock, Integer>> leafPositions = new HashMap<>();
|
||||
super.generate(world, new BlockPlacer() {
|
||||
@Override
|
||||
public void placeBlock(BlockPos pos, BlockState state) {
|
||||
placer.placeBlock(pos, state);
|
||||
if (state.getBlock() instanceof FruitBearingBlock block
|
||||
&& block.isPositionValidForFruit(state, pos)) {
|
||||
leafPositions.compute(new Vector2i(pos.getX(), pos.getZ()), (col, original) -> original == null || original.getSecond() > pos.getY() ? Pair.of(block, pos.getY()) : original);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPlacedBlock(BlockPos pos) {
|
||||
return placer.hasPlacedBlock(pos);
|
||||
}
|
||||
}, random, config, trunkHeight, treeNode, foliageHeight, radius);
|
||||
BlockPos.Mutable mutable = new BlockPos.Mutable();
|
||||
leafPositions.entrySet().forEach(pos -> {
|
||||
mutable.set(pos.getKey().x(), pos.getValue().getSecond() - 1, pos.getKey().y());
|
||||
if (!placer.hasPlacedBlock(mutable) && random.nextInt(12) == 0) {
|
||||
placer.placeBlock(mutable, pos.getValue().getFirst().getPlacedFruitState(random));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.minelittlepony.unicopia.server.world.gen;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* Provides a basic event for mods to inject their own biomes in place of vanilla overworld biomes.
|
||||
* <p>
|
||||
* Mods are able to insert entries for their biomes into the ranges for existing ones by
|
||||
* sub-dividing the provided section into pieces and assigning a new biome to one of the create
|
||||
* segments.
|
||||
*/
|
||||
public interface OverworldBiomeSelectionCallback {
|
||||
Event<OverworldBiomeSelectionCallback> EVENT = EventFactory.createArrayBacked(OverworldBiomeSelectionCallback.class, delegates -> {
|
||||
return context -> {
|
||||
for (OverworldBiomeSelectionCallback delegate : delegates) {
|
||||
delegate.onSelectingBiome(context);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
void onSelectingBiome(BiomeSelectionContext context);
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 6.9 KiB |
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"unicopia:sweet_apple_orchard"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
{
|
||||
"carvers": {
|
||||
"air": [
|
||||
"minecraft:cave",
|
||||
"minecraft:cave_extra_underground",
|
||||
"minecraft:canyon"
|
||||
]
|
||||
},
|
||||
"downfall": 0.8,
|
||||
"effects": {
|
||||
"fog_color": 12638463,
|
||||
"mood_sound": {
|
||||
"block_search_extent": 8,
|
||||
"offset": 2.0,
|
||||
"sound": "minecraft:ambient.cave",
|
||||
"tick_delay": 6000
|
||||
},
|
||||
"music": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"replace_current_music": false,
|
||||
"sound": "minecraft:music.overworld.forest"
|
||||
},
|
||||
"sky_color": 7972607,
|
||||
"water_color": 4159204,
|
||||
"water_fog_color": 329011
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[
|
||||
"minecraft:lake_lava_underground",
|
||||
"minecraft:lake_lava_surface"
|
||||
],
|
||||
[
|
||||
"minecraft:amethyst_geode"
|
||||
],
|
||||
[
|
||||
"minecraft:monster_room",
|
||||
"minecraft:monster_room_deep"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:ore_dirt",
|
||||
"minecraft:ore_gravel",
|
||||
"minecraft:ore_granite_upper",
|
||||
"minecraft:ore_granite_lower",
|
||||
"minecraft:ore_diorite_upper",
|
||||
"minecraft:ore_diorite_lower",
|
||||
"minecraft:ore_andesite_upper",
|
||||
"minecraft:ore_andesite_lower",
|
||||
"minecraft:ore_tuff",
|
||||
"minecraft:ore_coal_upper",
|
||||
"minecraft:ore_coal_lower",
|
||||
"minecraft:ore_iron_upper",
|
||||
"minecraft:ore_iron_middle",
|
||||
"minecraft:ore_iron_small",
|
||||
"minecraft:ore_gold",
|
||||
"minecraft:ore_gold_lower",
|
||||
"minecraft:ore_redstone",
|
||||
"minecraft:ore_redstone_lower",
|
||||
"minecraft:ore_diamond",
|
||||
"minecraft:ore_diamond_large",
|
||||
"minecraft:ore_diamond_buried",
|
||||
"minecraft:ore_lapis",
|
||||
"minecraft:ore_lapis_buried",
|
||||
"minecraft:ore_copper",
|
||||
"minecraft:underwater_magma",
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[
|
||||
"minecraft:spring_water",
|
||||
"minecraft:spring_lava"
|
||||
],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:forest_flowers",
|
||||
"minecraft:flower_default",
|
||||
"minecraft:patch_grass_forest",
|
||||
"minecraft:brown_mushroom_normal",
|
||||
"minecraft:red_mushroom_normal",
|
||||
"minecraft:patch_sugar_cane",
|
||||
"minecraft:patch_pumpkin"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"spawn_costs": {},
|
||||
"spawners": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"maxCount": 8,
|
||||
"minCount": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"axolotls": [],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"maxCount": 4,
|
||||
"minCount": 4,
|
||||
"weight": 12
|
||||
},
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"maxCount": 4,
|
||||
"minCount": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:chicken",
|
||||
"maxCount": 4,
|
||||
"minCount": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:cow",
|
||||
"maxCount": 4,
|
||||
"minCount": 4,
|
||||
"weight": 8
|
||||
},
|
||||
{
|
||||
"type": "minecraft:wolf",
|
||||
"maxCount": 4,
|
||||
"minCount": 4,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"misc": [],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"maxCount": 4,
|
||||
"minCount": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"maxCount": 4,
|
||||
"minCount": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"maxCount": 1,
|
||||
"minCount": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"maxCount": 4,
|
||||
"minCount": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"maxCount": 4,
|
||||
"minCount": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"maxCount": 4,
|
||||
"minCount": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"maxCount": 4,
|
||||
"minCount": 1,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"maxCount": 1,
|
||||
"minCount": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"maxCount": 6,
|
||||
"minCount": 4,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"water_ambient": [],
|
||||
"water_creature": []
|
||||
},
|
||||
"temperature": 0.8
|
||||
}
|
|
@ -48,6 +48,7 @@
|
|||
"MixinStateManagerBuilder",
|
||||
"MixinBlockState",
|
||||
"MixinTargetPredicate",
|
||||
"MixinVanillaBiomeParameters",
|
||||
"MixinWardenEntity",
|
||||
"MixinWorld",
|
||||
"MixinWorldChunk",
|
||||
|
|
Loading…
Reference in a new issue