204 lines
8.3 KiB
Java
204 lines
8.3 KiB
Java
package net.minecraft.world.level.block.grower;
|
|
|
|
import com.mojang.serialization.Codec;
|
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.core.registries.Registries;
|
|
import net.minecraft.data.worldgen.features.TreeFeatures;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.world.level.BlockGetter;
|
|
import net.minecraft.world.level.LevelAccessor;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public final class TreeGrower {
|
|
private static final Map<String, TreeGrower> GROWERS = new Object2ObjectArrayMap<>();
|
|
public static final Codec<TreeGrower> CODEC = Codec.stringResolver(treeGrower -> treeGrower.name, GROWERS::get);
|
|
public static final TreeGrower OAK = new TreeGrower(
|
|
"oak",
|
|
0.1F,
|
|
Optional.empty(),
|
|
Optional.empty(),
|
|
Optional.of(TreeFeatures.OAK),
|
|
Optional.of(TreeFeatures.FANCY_OAK),
|
|
Optional.of(TreeFeatures.OAK_BEES_005),
|
|
Optional.of(TreeFeatures.FANCY_OAK_BEES_005)
|
|
);
|
|
public static final TreeGrower SPRUCE = new TreeGrower(
|
|
"spruce",
|
|
0.5F,
|
|
Optional.of(TreeFeatures.MEGA_SPRUCE),
|
|
Optional.of(TreeFeatures.MEGA_PINE),
|
|
Optional.of(TreeFeatures.SPRUCE),
|
|
Optional.empty(),
|
|
Optional.empty(),
|
|
Optional.empty()
|
|
);
|
|
public static final TreeGrower MANGROVE = new TreeGrower(
|
|
"mangrove",
|
|
0.85F,
|
|
Optional.empty(),
|
|
Optional.empty(),
|
|
Optional.of(TreeFeatures.MANGROVE),
|
|
Optional.of(TreeFeatures.TALL_MANGROVE),
|
|
Optional.empty(),
|
|
Optional.empty()
|
|
);
|
|
public static final TreeGrower AZALEA = new TreeGrower("azalea", Optional.empty(), Optional.of(TreeFeatures.AZALEA_TREE), Optional.empty());
|
|
public static final TreeGrower BIRCH = new TreeGrower("birch", Optional.empty(), Optional.of(TreeFeatures.BIRCH), Optional.of(TreeFeatures.BIRCH_BEES_005));
|
|
public static final TreeGrower JUNGLE = new TreeGrower(
|
|
"jungle", Optional.of(TreeFeatures.MEGA_JUNGLE_TREE), Optional.of(TreeFeatures.JUNGLE_TREE_NO_VINE), Optional.empty()
|
|
);
|
|
public static final TreeGrower ACACIA = new TreeGrower("acacia", Optional.empty(), Optional.of(TreeFeatures.ACACIA), Optional.empty());
|
|
public static final TreeGrower CHERRY = new TreeGrower("cherry", Optional.empty(), Optional.of(TreeFeatures.CHERRY), Optional.of(TreeFeatures.CHERRY_BEES_005));
|
|
public static final TreeGrower DARK_OAK = new TreeGrower("dark_oak", Optional.of(TreeFeatures.DARK_OAK), Optional.empty(), Optional.empty());
|
|
public static final TreeGrower PALE_OAK = new TreeGrower("pale_oak", Optional.of(TreeFeatures.PALE_OAK), Optional.empty(), Optional.empty());
|
|
private final String name;
|
|
private final float secondaryChance;
|
|
private final Optional<ResourceKey<ConfiguredFeature<?, ?>>> megaTree;
|
|
private final Optional<ResourceKey<ConfiguredFeature<?, ?>>> secondaryMegaTree;
|
|
private final Optional<ResourceKey<ConfiguredFeature<?, ?>>> tree;
|
|
private final Optional<ResourceKey<ConfiguredFeature<?, ?>>> secondaryTree;
|
|
private final Optional<ResourceKey<ConfiguredFeature<?, ?>>> flowers;
|
|
private final Optional<ResourceKey<ConfiguredFeature<?, ?>>> secondaryFlowers;
|
|
|
|
public TreeGrower(
|
|
String name,
|
|
Optional<ResourceKey<ConfiguredFeature<?, ?>>> megaTree,
|
|
Optional<ResourceKey<ConfiguredFeature<?, ?>>> tree,
|
|
Optional<ResourceKey<ConfiguredFeature<?, ?>>> flowers
|
|
) {
|
|
this(name, 0.0F, megaTree, Optional.empty(), tree, Optional.empty(), flowers, Optional.empty());
|
|
}
|
|
|
|
public TreeGrower(
|
|
String name,
|
|
float secondaryChance,
|
|
Optional<ResourceKey<ConfiguredFeature<?, ?>>> megaTree,
|
|
Optional<ResourceKey<ConfiguredFeature<?, ?>>> secondaryMegaTree,
|
|
Optional<ResourceKey<ConfiguredFeature<?, ?>>> tree,
|
|
Optional<ResourceKey<ConfiguredFeature<?, ?>>> secondaryTree,
|
|
Optional<ResourceKey<ConfiguredFeature<?, ?>>> flowers,
|
|
Optional<ResourceKey<ConfiguredFeature<?, ?>>> secondaryFlowers
|
|
) {
|
|
this.name = name;
|
|
this.secondaryChance = secondaryChance;
|
|
this.megaTree = megaTree;
|
|
this.secondaryMegaTree = secondaryMegaTree;
|
|
this.tree = tree;
|
|
this.secondaryTree = secondaryTree;
|
|
this.flowers = flowers;
|
|
this.secondaryFlowers = secondaryFlowers;
|
|
GROWERS.put(name, this);
|
|
}
|
|
|
|
@Nullable
|
|
private ResourceKey<ConfiguredFeature<?, ?>> getConfiguredFeature(RandomSource random, boolean flowers) {
|
|
if (random.nextFloat() < this.secondaryChance) {
|
|
if (flowers && this.secondaryFlowers.isPresent()) {
|
|
return (ResourceKey<ConfiguredFeature<?, ?>>)this.secondaryFlowers.get();
|
|
}
|
|
|
|
if (this.secondaryTree.isPresent()) {
|
|
return (ResourceKey<ConfiguredFeature<?, ?>>)this.secondaryTree.get();
|
|
}
|
|
}
|
|
|
|
return flowers && this.flowers.isPresent() ? (ResourceKey)this.flowers.get() : (ResourceKey)this.tree.orElse(null);
|
|
}
|
|
|
|
@Nullable
|
|
private ResourceKey<ConfiguredFeature<?, ?>> getConfiguredMegaFeature(RandomSource random) {
|
|
return this.secondaryMegaTree.isPresent() && random.nextFloat() < this.secondaryChance
|
|
? (ResourceKey)this.secondaryMegaTree.get()
|
|
: (ResourceKey)this.megaTree.orElse(null);
|
|
}
|
|
|
|
public boolean growTree(ServerLevel level, ChunkGenerator chunkGenerator, BlockPos pos, BlockState state, RandomSource random) {
|
|
ResourceKey<ConfiguredFeature<?, ?>> resourceKey = this.getConfiguredMegaFeature(random);
|
|
if (resourceKey != null) {
|
|
Holder<ConfiguredFeature<?, ?>> holder = (Holder<ConfiguredFeature<?, ?>>)level.registryAccess()
|
|
.lookupOrThrow(Registries.CONFIGURED_FEATURE)
|
|
.get(resourceKey)
|
|
.orElse(null);
|
|
if (holder != null) {
|
|
for (int i = 0; i >= -1; i--) {
|
|
for (int j = 0; j >= -1; j--) {
|
|
if (isTwoByTwoSapling(state, level, pos, i, j)) {
|
|
ConfiguredFeature<?, ?> configuredFeature = holder.value();
|
|
BlockState blockState = Blocks.AIR.defaultBlockState();
|
|
level.setBlock(pos.offset(i, 0, j), blockState, 4);
|
|
level.setBlock(pos.offset(i + 1, 0, j), blockState, 4);
|
|
level.setBlock(pos.offset(i, 0, j + 1), blockState, 4);
|
|
level.setBlock(pos.offset(i + 1, 0, j + 1), blockState, 4);
|
|
if (configuredFeature.place(level, chunkGenerator, random, pos.offset(i, 0, j))) {
|
|
return true;
|
|
}
|
|
|
|
level.setBlock(pos.offset(i, 0, j), state, 4);
|
|
level.setBlock(pos.offset(i + 1, 0, j), state, 4);
|
|
level.setBlock(pos.offset(i, 0, j + 1), state, 4);
|
|
level.setBlock(pos.offset(i + 1, 0, j + 1), state, 4);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ResourceKey<ConfiguredFeature<?, ?>> resourceKey2 = this.getConfiguredFeature(random, this.hasFlowers(level, pos));
|
|
if (resourceKey2 == null) {
|
|
return false;
|
|
} else {
|
|
Holder<ConfiguredFeature<?, ?>> holder2 = (Holder<ConfiguredFeature<?, ?>>)level.registryAccess()
|
|
.lookupOrThrow(Registries.CONFIGURED_FEATURE)
|
|
.get(resourceKey2)
|
|
.orElse(null);
|
|
if (holder2 == null) {
|
|
return false;
|
|
} else {
|
|
ConfiguredFeature<?, ?> configuredFeature2 = holder2.value();
|
|
BlockState blockState2 = level.getFluidState(pos).createLegacyBlock();
|
|
level.setBlock(pos, blockState2, 4);
|
|
if (configuredFeature2.place(level, chunkGenerator, random, pos)) {
|
|
if (level.getBlockState(pos) == blockState2) {
|
|
level.sendBlockUpdated(pos, state, blockState2, 2);
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
level.setBlock(pos, state, 4);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static boolean isTwoByTwoSapling(BlockState state, BlockGetter level, BlockPos pos, int xOffset, int yOffset) {
|
|
Block block = state.getBlock();
|
|
return level.getBlockState(pos.offset(xOffset, 0, yOffset)).is(block)
|
|
&& level.getBlockState(pos.offset(xOffset + 1, 0, yOffset)).is(block)
|
|
&& level.getBlockState(pos.offset(xOffset, 0, yOffset + 1)).is(block)
|
|
&& level.getBlockState(pos.offset(xOffset + 1, 0, yOffset + 1)).is(block);
|
|
}
|
|
|
|
private boolean hasFlowers(LevelAccessor level, BlockPos pos) {
|
|
for (BlockPos blockPos : BlockPos.MutableBlockPos.betweenClosed(pos.below().north(2).west(2), pos.above().south(2).east(2))) {
|
|
if (level.getBlockState(blockPos).is(BlockTags.FLOWERS)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|