diff --git a/Xplat/src/main/java/vazkii/botania/common/block/block_entity/CellularBlockEntity.java b/Xplat/src/main/java/vazkii/botania/common/block/block_entity/CellularBlockEntity.java index fdce1d4732..9a0ff9610c 100644 --- a/Xplat/src/main/java/vazkii/botania/common/block/block_entity/CellularBlockEntity.java +++ b/Xplat/src/main/java/vazkii/botania/common/block/block_entity/CellularBlockEntity.java @@ -68,7 +68,7 @@ public void update(Level level) { } public boolean hasActiveParent(DandelifeonBlockEntity dandie) { - return flowerCoords != null && dandie.getLevel().getBlockEntity(flowerCoords) instanceof DandelifeonBlockEntity parent && dandie.getLevel().hasNeighborSignal(flowerCoords) && parent.isOnSpecialSoil() == dandie.overgrowthBoost; + return flowerCoords != null && dandie.getLevel().getBlockEntity(flowerCoords) instanceof DandelifeonBlockEntity parent && dandie.getLevel().hasNeighborSignal(flowerCoords) && (!dandie.overgrowthBoost || parent.isOnSpecialSoil()); } public int getGeneration() { diff --git a/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/OredupeBlockEntity.java b/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/OredupeBlockEntity.java new file mode 100644 index 0000000000..43bf8ea5ab --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/OredupeBlockEntity.java @@ -0,0 +1,179 @@ +/* + * This class is distributed as part of the Botania Mod. + * Get the Source Code in github: + * https://github.com/Vazkii/Botania + * + * Botania is Open Source and distributed under the + * Botania License: http://botaniamod.net/license.php + */ +package vazkii.botania.common.block.flower.functional; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.random.WeightedEntry; +import net.minecraft.util.random.WeightedRandom; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.LevelEvent; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; + +import org.jetbrains.annotations.Nullable; + +import vazkii.botania.api.block_entity.FunctionalFlowerBlockEntity; +import vazkii.botania.api.block_entity.RadiusDescriptor; +import vazkii.botania.api.recipe.OrechidRecipe; +import vazkii.botania.common.block.BotaniaFlowerBlocks; +import vazkii.botania.common.crafting.BotaniaRecipeTypes; +import vazkii.botania.common.handler.BotaniaSounds; +import vazkii.botania.common.handler.OrechidManager; +import vazkii.botania.xplat.BotaniaConfig; +import vazkii.botania.xplat.XplatAbstractions; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public class OrechidBlockEntity extends FunctionalFlowerBlockEntity { + private static final int COST = 20000; + private static final int DELAY = 200; + private static final int RANGE = 1; + + protected OrechidBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + public OrechidBlockEntity(BlockPos pos, BlockState state) { + this(BotaniaFlowerBlocks.ORECHID, pos, state); + } + + @Override + public void tickFlower() { + super.tickFlower(); + + if (getLevel().isClientSide || !canOperate()) { + return; + } + + if (getMana() >= getCost() && ticksExisted % getDelay() == 0) { + BlockPos coords = getCoordsToPut(); + if (coords != null) { + trySetRecipe(coords, findMatchingRecipe(coords)); + } + } + } + + protected void playSound(BlockPos coords) { + getLevel().playSound(null, coords, BotaniaSounds.orechid, SoundSource.BLOCKS, 1F, 1F); + } + + @Nullable + private OrechidRecipe findMatchingRecipe(BlockPos coords) { + BlockState input = level.getBlockState(coords); + List> values = new ArrayList<>(); + for (OrechidRecipe recipe : OrechidManager.getMatchingRecipes(getLevel().getRecipeManager(), getRecipeType(), input)) { + values.add(WeightedEntry.wrap(recipe, recipe.getWeight(getLevel(), coords))); + } + return WeightedRandom.getRandomItem(getLevel().random, values) + .map(WeightedEntry.Wrapper::getData) + .orElse(null); + } + + private void trySetRecipe(BlockPos coords, @Nullable OrechidRecipe recipe) { + if (recipe == null) { + return; + } + + BlockState state = recipe.getOutput(level, coords).pick(level.random); + if (getLevel().setBlockAndUpdate(coords, state)) { + if (BotaniaConfig.common().blockBreakParticles()) { + getLevel().levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, coords, Block.getId(state)); + } + playSound(coords); + addMana(-getCost()); + + var serverLevel = (ServerLevel) this.level; + var server = serverLevel.getServer(); + recipe.getSuccessFunction().get(server.getFunctions()).ifPresent(command -> { + var context = server.getFunctions().getGameLoopSender() + .withLevel(serverLevel) + .withPosition(Vec3.atBottomCenterOf(coords)); + server.getFunctions().execute(command, context); + }); + + sync(); + } + } + + private BlockPos getCoordsToPut() { + List possibleCoords = new ArrayList<>(); + var matcher = getReplaceMatcher(); + for (BlockPos pos : BlockPos.betweenClosed(getEffectivePos().offset(-getRange(), -getRangeY(), -getRange()), + getEffectivePos().offset(getRange(), getRangeY(), getRange()))) { + BlockState state = getLevel().getBlockState(pos); + if (matcher.test(state)) { + possibleCoords.add(pos.immutable()); + } + } + + if (possibleCoords.isEmpty()) { + return null; + } + return possibleCoords.get(getLevel().random.nextInt(possibleCoords.size())); + } + + public boolean canOperate() { + return true; + } + + public RecipeType getRecipeType() { + return BotaniaRecipeTypes.ORECHID_TYPE; + } + + public Predicate getReplaceMatcher() { + return state -> !OrechidManager.getMatchingRecipes( + this.getLevel().getRecipeManager(), + this.getRecipeType(), + state + ).isEmpty(); + } + + public int getCost() { + return XplatAbstractions.INSTANCE.gogLoaded() ? COST_GOG : COST; + } + + public int getDelay() { + return XplatAbstractions.INSTANCE.gogLoaded() ? DELAY_GOG : DELAY; + } + + @Override + public RadiusDescriptor getRadius() { + return RadiusDescriptor.Rectangle.square(getEffectivePos(), getRange()); + } + + @Override + public boolean acceptsRedstone() { + return true; + } + + public int getRange() { + return RANGE; + } + + public int getRangeY() { + return RANGE_Y; + } + + @Override + public int getColor() { + return 0x818181; + } + + @Override + public int getMaxMana() { + return getCost(); + } + +}