From 8d884c0ee36e33c249dff08bf96904502be38643 Mon Sep 17 00:00:00 2001 From: maxanier Date: Sun, 30 Jul 2023 17:26:40 +0200 Subject: [PATCH] Rework mother tree structure code. Remove versatile connected block and replace with version specifically tailored to the mother --- .../blockentity/MotherBlockEntity.java | 177 +++++++++++------ .../VulnerableRemainsBlockEntity.java | 39 ++-- .../blocks/connected/ConnectedBlock.java | 98 ---------- .../blocks/connected/IConnectedBlock.java | 26 --- .../blocks/mother/IRemainsBlock.java | 5 +- .../vampirism/blocks/mother/MotherBlock.java | 19 +- .../blocks/mother/MotherTreeStructure.java | 178 ++++++++++++++++++ .../vampirism/blocks/mother/RemainsBlock.java | 51 +++-- .../blocks/mother/RemainsConnector.java | 37 ---- .../teamlapen/vampirism/core/ModBlocks.java | 8 +- .../vampirism/data/LootTablesGenerator.java | 4 - .../entity/player/ModPlayerEventHandler.java | 12 +- .../gen/structure/mother/MotherPiece.java | 67 ++++--- 13 files changed, 395 insertions(+), 326 deletions(-) delete mode 100644 src/main/java/de/teamlapen/vampirism/blocks/connected/ConnectedBlock.java delete mode 100644 src/main/java/de/teamlapen/vampirism/blocks/connected/IConnectedBlock.java create mode 100644 src/main/java/de/teamlapen/vampirism/blocks/mother/MotherTreeStructure.java delete mode 100644 src/main/java/de/teamlapen/vampirism/blocks/mother/RemainsConnector.java diff --git a/src/main/java/de/teamlapen/vampirism/blockentity/MotherBlockEntity.java b/src/main/java/de/teamlapen/vampirism/blockentity/MotherBlockEntity.java index 899e2542d0..4659e61ea9 100644 --- a/src/main/java/de/teamlapen/vampirism/blockentity/MotherBlockEntity.java +++ b/src/main/java/de/teamlapen/vampirism/blockentity/MotherBlockEntity.java @@ -1,17 +1,15 @@ package de.teamlapen.vampirism.blockentity; import de.teamlapen.vampirism.VampirismMod; -import de.teamlapen.vampirism.blocks.DarkSpruceLogs; -import de.teamlapen.vampirism.blocks.connected.ConnectedBlock; import de.teamlapen.vampirism.blocks.mother.IRemainsBlock; -import de.teamlapen.vampirism.blocks.mother.MotherBlock; +import de.teamlapen.vampirism.blocks.mother.MotherTreeStructure; import de.teamlapen.vampirism.core.ModParticles; import de.teamlapen.vampirism.core.ModSounds; import de.teamlapen.vampirism.core.ModTiles; import de.teamlapen.vampirism.network.ClientboundPlayEventPacket; import de.teamlapen.vampirism.particle.FlyingBloodParticleOptions; import net.minecraft.core.BlockPos; -import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.core.particles.DustParticleOptions; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.Packet; @@ -26,33 +24,27 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; import java.util.List; +import java.util.Optional; +import java.util.Set; + public class MotherBlockEntity extends BlockEntity { private final ServerBossEvent bossEvent = new ServerBossEvent(Component.translatable("block.vampirism.mother"), BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.NOTCHED_10); - private boolean canBeDestroyed = false; + private boolean isFrozen = false; private int freezeTimer = 0; - private final ConnectedBlock.Connector connector = new ConnectedBlock.Connector<>(DarkSpruceLogs.class); - /** - * Indicate whether a mother block is loaded in the world. - * Should be acceptably accurate as there is only every one mother nearby. But don't use for anything important for gameplay - */ - public static boolean IS_A_MOTHER_LOADED_UNRELIABLE = false; - - public MotherBlockEntity(BlockPos pos, BlockState state) { - super(ModTiles.MOTHER.get(), pos, state); - bossEvent.setProgress(1); - bossEvent.setPlayBossMusic(true); - } public static void serverTick(Level level, BlockPos blockPos, BlockState blockState, MotherBlockEntity e) { + IS_A_MOTHER_LOADED_UNRELIABLE = true; + //Handle fight --------------------------------- if (e.isFrozen && e.freezeTimer-- <= 0) { e.unFreezeFight(level, blockPos, blockState); } @@ -60,10 +52,11 @@ public static void serverTick(Level level, BlockPos blockPos, BlockState blockSt if (e.level.getRandom().nextInt(50) == 0) { e.updateFightStatus(); if (!e.bossEvent.getPlayers().isEmpty()) { - List> vul = ((IRemainsBlock) e.getBlockState().getBlock()).getConnector().getVulnerabilities(level, blockPos).filter(state -> ((IRemainsBlock) (state.getRight().getBlock())).isVulnerable(state.getRight())).toList(); - if (!vul.isEmpty()) { + + List> vuls = e.getTreeStructure(false).getVerifiedVulnerabilities(level).filter(t -> t.getRight().isVulnerable(t.getMiddle())).toList(); + if (!vuls.isEmpty()) { for (ServerPlayer player : e.bossEvent.getPlayers()) { - BlockPos p = vul.get(e.level.getRandom().nextInt(vul.size())).getLeft(); + BlockPos p = vuls.get(e.level.getRandom().nextInt(vuls.size())).getLeft(); ModParticles.spawnParticlesServer(player.level(), new FlyingBloodParticleOptions(100, false, p.getX() + 0.5, p.getY() + 0.5, p.getZ() + 0.5), player.getX(), player.getY() + player.getEyeHeight() / 2, player.getZ(), 10, 0.1f, 0.1f, 0.1f, 0); } } @@ -71,32 +64,71 @@ public static void serverTick(Level level, BlockPos blockPos, BlockState blockSt } } + //Handle destruction -------------------------------- + if (e.level != null && e.destructionTimer > 0) { + if (e.destructionTimer++ % 3 == 0) { + MotherTreeStructure structure = e.getTreeStructure(false); + Optional> hierarchy = structure.popHierarchy(); + if (hierarchy.isPresent()) { + for (BlockPos p : hierarchy.get()) { + if (level.getBlockState(p).getBlock() instanceof IRemainsBlock) { + level.setBlock(p, Blocks.AIR.defaultBlockState(), 3); + ModParticles.spawnParticlesServer(level, new DustParticleOptions(new Vector3f(0.7f, 0.7f, 0.7f), 1), p.getX() + 0.5, p.getY() + 0.5, p.getZ() + 0.5f, 20, 0.3, 0.3, 0.3, 0.01); + } + } + } else { + //Destruction complete + e.destructionTimer = -1; + } + } + } } - public void updateFightStatus() { - List> vulnerabilities = ((IRemainsBlock) this.getBlockState().getBlock()).getConnector().getVulnerabilities(this.level, this.worldPosition).toList(); - long remainingVulnerabilities = vulnerabilities.stream().filter(state -> ((IRemainsBlock) (state.getRight().getBlock())).isVulnerable(state.getRight())).count(); - if (remainingVulnerabilities > 0) { - this.bossEvent.setProgress(remainingVulnerabilities / (float) vulnerabilities.size()); - } else { - this.bossEvent.setProgress(0); - this.endFight(); - } + + //Destruction + /** + * Caches structure. Should be mostly valid because blocks cannot be destroyed by survival player. Is (unreliably) invalidated when a block is destroyed nonetheless. + */ + private MotherTreeStructure cachedStructure; + + /** + * Indicate whether a mother block is loaded in the world. + * Should be acceptably accurate as there is only every one mother nearby. But don't use for anything important for gameplay + */ + public static boolean IS_A_MOTHER_LOADED_UNRELIABLE = false; + + public MotherBlockEntity(BlockPos pos, BlockState state) { + super(ModTiles.MOTHER.get(), pos, state); + bossEvent.setProgress(1); + bossEvent.setPlayBossMusic(true); } + /** + * Describes destruction process + * == 0: Intact + * > 0: Increasing, destruction in progress + * == -1: Structure, destroyed, mother is vulnerable + */ + private int destructionTimer = 0; - private void endFight() { - this.bossEvent.getPlayers().forEach(p -> VampirismMod.dispatcher.sendTo(new ClientboundPlayEventPacket(2, getBlockPos(), 0), p)); - this.bossEvent.removeAllPlayers(); - this.canBeDestroyed = true; - this.connector.foreachFacing(this.level, this.worldPosition, (level, pos, state) -> { - level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3); - ModParticles.spawnParticlesServer(level, ParticleTypes.POOF, pos.getX(), pos.getY(), pos.getZ(), 5, 0.5, 0.5, 0.5, 0.05); - }); - this.setChanged(); - if (this.level != null) { - this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3); - this.level.playSound(null, worldPosition, ModSounds.MOTHER_DEATH.get(), SoundSource.BLOCKS, 1f, 1f); - } + public boolean isCanBeBroken() { + return this.destructionTimer == -1; + } + + public boolean isIntact() { + return destructionTimer == 0; + } + + @Override + public void load(@NotNull CompoundTag tag) { + super.load(tag); + this.destructionTimer = tag.getInt("destruction_timer"); + } + + /** + * Call this when a block that belongs to this structure is removed + */ + public void onStructureBlockRemoved() { + this.cachedStructure = null; } @Override @@ -123,47 +155,70 @@ private void addPlayer(Player player) { return saveWithoutMetadata(); } + @Nullable @Override public Packet getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); } + public void onVulnerabilityHit(ServerPlayer p, boolean destroyed) { + addPlayer(p); + updateFightStatus(); + if (destroyed && isIntact()) { + freezeFight(); + } + } + + public void updateFightStatus() { + List> vuls = getTreeStructure(false).getVerifiedVulnerabilities(level).toList(); + long remainingVulnerabilities = vuls.stream().filter(vul -> vul.getRight().isVulnerable(vul.getMiddle())).count(); + if (remainingVulnerabilities > 0) { + this.bossEvent.setProgress(remainingVulnerabilities / (float) vuls.size()); + } else { + this.bossEvent.setProgress(0); + this.endFight(); + } + } + @Override protected void saveAdditional(@NotNull CompoundTag tag) { super.saveAdditional(tag); - tag.putBoolean("isVulnerable", this.canBeDestroyed); + tag.putInt("destruction_timer", this.destructionTimer); } - @Override - public void load(@NotNull CompoundTag tag) { - super.load(tag); - this.canBeDestroyed = tag.getBoolean("isVulnerable"); + private void endFight() { + this.bossEvent.getPlayers().forEach(p -> VampirismMod.dispatcher.sendTo(new ClientboundPlayEventPacket(2, getBlockPos(), 0), p)); + this.bossEvent.removeAllPlayers(); + this.initiateDestruction(); } - - public boolean isCanBeDestroyed() { - return this.canBeDestroyed; + private void freezeFight() { + this.isFrozen = true; + getTreeStructure(false).getVerifiedVulnerabilities(this.level).forEach(vul -> vul.getRight().freeze(level, vul.getLeft(), vul.getMiddle())); + this.bossEvent.setColor(BossEvent.BossBarColor.WHITE); } - public void onVulnerabilityHit(ServerPlayer p, boolean destroyed) { - addPlayer(p); - updateFightStatus(); - if (destroyed && !this.canBeDestroyed) { - freezeFight(); + @NotNull + private MotherTreeStructure getTreeStructure(boolean forceRefresh) { + if (forceRefresh || this.cachedStructure == null) { + this.cachedStructure = MotherTreeStructure.getTreeView(this.level, this.worldPosition); } + return cachedStructure; } - private void freezeFight() { - this.isFrozen = true; - ((MotherBlock) getBlockState().getBlock()).getConnector().foreach(this.level, this.worldPosition, (level, pos, state) -> ((IRemainsBlock) state.getBlock()).freeze(level, pos, state)); - this.bossEvent.setColor(BossEvent.BossBarColor.WHITE); + private void initiateDestruction() { + this.getTreeStructure(true); + this.destructionTimer = 1; + if (this.level != null) { + this.level.playSound(null, worldPosition, ModSounds.MOTHER_DEATH.get(), SoundSource.BLOCKS, 1f, 1f); + } } private void unFreezeFight(Level level, BlockPos blockPos, BlockState blockState) { this.isFrozen = false; this.freezeTimer = 20 * 10; - ((MotherBlock) getBlockState().getBlock()).getConnector().foreach(level, blockPos, (level1, pos, state) -> ((IRemainsBlock) state.getBlock()).unFreeze(level1, pos, state)); + getTreeStructure(false).getVerifiedVulnerabilities(this.level).forEach(vul -> vul.getRight().unFreeze(level, vul.getLeft(), vul.getMiddle())); this.bossEvent.setColor(BossEvent.BossBarColor.RED); } diff --git a/src/main/java/de/teamlapen/vampirism/blockentity/VulnerableRemainsBlockEntity.java b/src/main/java/de/teamlapen/vampirism/blockentity/VulnerableRemainsBlockEntity.java index da917b4add..92fdb30a64 100644 --- a/src/main/java/de/teamlapen/vampirism/blockentity/VulnerableRemainsBlockEntity.java +++ b/src/main/java/de/teamlapen/vampirism/blockentity/VulnerableRemainsBlockEntity.java @@ -1,6 +1,6 @@ package de.teamlapen.vampirism.blockentity; -import de.teamlapen.vampirism.blocks.mother.ActiveVulnerableRemainsBlock; +import de.teamlapen.vampirism.blocks.mother.MotherTreeStructure; import de.teamlapen.vampirism.core.ModBlocks; import de.teamlapen.vampirism.core.ModEntities; import de.teamlapen.vampirism.core.ModSounds; @@ -31,30 +31,23 @@ public VulnerableRemainsBlockEntity(BlockPos pos, BlockState state) { } - private Optional getMother() { - if (motherPos == null) { - ((ActiveVulnerableRemainsBlock) getBlockState().getBlock()).getConnector().getMother(this.level, this.worldPosition).ifPresentOrElse(pos -> { - motherPos = pos.getKey(); - }, () -> { - this.level.setBlockAndUpdate(this.worldPosition, ModBlocks.REMAINS.get().defaultBlockState()); + public static void serverTick(Level level, BlockPos blockPos, BlockState blockState, VulnerableRemainsBlockEntity e) { + if (e.firstTick) { + e.firstTick = false; + e.getMother().ifPresent(mother -> { + mother.updateFightStatus(); + e.checkDummyEntity(); }); + } else if (level.getRandom().nextInt(100) == 3) { + e.checkDummyEntity(); } - return Optional.ofNullable(motherPos).map(pos -> { - var blockEntity = level.getBlockEntity(pos); - if (blockEntity instanceof MotherBlockEntity mother) { - return mother; - } - return null; - }); } private void destroyVulnerability() { this.level.setBlockAndUpdate(this.worldPosition, ModBlocks.INCAPACITATED_VULNERABLE_REMAINS.get().defaultBlockState()); } - public void attacked(@NotNull BlockState state, @NotNull ServerPlayer player) { - } public void onDamageDealt(DamageSource src, double damage) { this.health -= damage; @@ -109,17 +102,11 @@ private VulnerableRemainsDummyEntity checkDummyEntity() { private boolean firstTick = true; - public static void serverTick(Level level, BlockPos blockPos, BlockState blockState, VulnerableRemainsBlockEntity e) { - if (e.firstTick) { - e.firstTick = false; - Optional motherOpt = e.getMother(); - //motherOpt.ifPresent(MotherBlockEntity::updateFightStatus); - motherOpt.ifPresent(mother -> { - mother.updateFightStatus(); - Entity e2 = e.checkDummyEntity(); - //level.getNearbyPlayers(TargetingConditions.DEFAULT, e2, AABB.ofSize(blockPos.getCenter(), 5, 5, 5)).forEach(mother::addPlayer); - }); + private Optional getMother() { + if (this.level != null) { + return MotherTreeStructure.findMother(this.level, this.getBlockPos()).map(p -> this.level.getBlockEntity(p.getKey())).filter(e -> e instanceof MotherBlockEntity).map(e -> (MotherBlockEntity) e); } + return Optional.empty(); } } diff --git a/src/main/java/de/teamlapen/vampirism/blocks/connected/ConnectedBlock.java b/src/main/java/de/teamlapen/vampirism/blocks/connected/ConnectedBlock.java deleted file mode 100644 index b3de0b863c..0000000000 --- a/src/main/java/de/teamlapen/vampirism/blocks/connected/ConnectedBlock.java +++ /dev/null @@ -1,98 +0,0 @@ -package de.teamlapen.vampirism.blocks.connected; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.logging.log4j.util.TriConsumer; -import org.jetbrains.annotations.NotNull; - -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Stream; - -public abstract class ConnectedBlock extends Block implements IConnectedBlock { - - - public ConnectedBlock(Properties pProperties) { - super(pProperties); - } - - @Override - public abstract Connector getConnector(); - - - public static class Connector implements IConnectedBlock.IConnector { - - @NotNull - private final Class connectedBlock; - - public Connector(@NotNull Class connectedBlock) { - - this.connectedBlock = connectedBlock; - } - - @Override - public Stream> collectConnectedBlocks(Level level, BlockPos pos) { - return this.innerCollect(level, pos, new HashSet<>(), state -> true); - } - - @Override - public Stream> collectConnectedBlocks(Level level, BlockPos pos, Predicate predicate) { - return this.innerCollect(level, pos, new HashSet<>(), predicate); - } - - @Override - public Optional> find(Level level, BlockPos pos, Predicate predicate) { - return this.innerFind(level, pos, new HashSet<>(), predicate); - } - - @Override - public void foreach(Level level, BlockPos pos, TriConsumer consumer) { - innerForEach(level, pos, new HashSet<>(), consumer); - } - - public void foreachFacing(Level level, BlockPos pos, TriConsumer consumer) { - HashSet objects = new HashSet<>(); - Direction.stream().map(pos::relative).forEach(newPos -> this.innerForEach(level, newPos, objects, consumer)); - } - - private Stream> innerCollect(Level level, BlockPos pos, Set collected, Predicate predicate) { - if (collected.contains(pos)) return Stream.empty(); - collected.add(pos); - BlockState blockState = level.getBlockState(pos); - if (!this.connectedBlock.isAssignableFrom(blockState.getBlock().getClass())) return Stream.empty(); - Stream> stream = Direction.stream().map(pos::relative).flatMap(newPos -> this.innerCollect(level, newPos, collected, predicate)); - if (predicate.test(blockState)) { - return Stream.concat(Stream.of(Pair.of(pos, blockState)), stream); - } else { - return stream; - } - } - - private void innerForEach(Level level, BlockPos pos, Set collected, TriConsumer consumer) { - if (collected.contains(pos)) return; - collected.add(pos); - BlockState blockState = level.getBlockState(pos); - if (!this.connectedBlock.isAssignableFrom(blockState.getBlock().getClass())) return; - consumer.accept(level, pos, blockState); - Direction.stream().map(pos::relative).forEach(newPos -> this.innerForEach(level, newPos, collected, consumer)); - } - - private Optional> innerFind(Level level, BlockPos pos, Set visited, Predicate predicate) { - if (visited.contains(pos)) return Optional.empty(); - visited.add(pos); - BlockState blockState = level.getBlockState(pos); - if (!this.connectedBlock.isAssignableFrom(blockState.getBlock().getClass())) return Optional.empty(); - if (predicate.test(blockState)) { - return Optional.of(Pair.of(pos, blockState)); - } else { - return Direction.stream().map(pos::relative).map(newPos -> this.innerFind(level, newPos, visited, predicate)).filter(Optional::isPresent).findFirst().orElse(Optional.empty()); - } - } - } -} diff --git a/src/main/java/de/teamlapen/vampirism/blocks/connected/IConnectedBlock.java b/src/main/java/de/teamlapen/vampirism/blocks/connected/IConnectedBlock.java deleted file mode 100644 index 73ea4de381..0000000000 --- a/src/main/java/de/teamlapen/vampirism/blocks/connected/IConnectedBlock.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.teamlapen.vampirism.blocks.connected; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.state.BlockState; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.logging.log4j.util.TriConsumer; - -import java.util.Optional; -import java.util.function.Predicate; -import java.util.stream.Stream; - -public interface IConnectedBlock { - - IConnector getConnector(); - - interface IConnector { - Stream> collectConnectedBlocks(Level level, BlockPos pos); - - Stream> collectConnectedBlocks(Level level, BlockPos pos, Predicate predicate); - - Optional> find(Level level, BlockPos pos, Predicate predicate); - - void foreach(Level level, BlockPos pos, TriConsumer consumer); - } -} diff --git a/src/main/java/de/teamlapen/vampirism/blocks/mother/IRemainsBlock.java b/src/main/java/de/teamlapen/vampirism/blocks/mother/IRemainsBlock.java index 8e7e3205c0..79acd3cc95 100644 --- a/src/main/java/de/teamlapen/vampirism/blocks/mother/IRemainsBlock.java +++ b/src/main/java/de/teamlapen/vampirism/blocks/mother/IRemainsBlock.java @@ -1,11 +1,10 @@ package de.teamlapen.vampirism.blocks.mother; -import de.teamlapen.vampirism.blocks.connected.IConnectedBlock; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; -public interface IRemainsBlock extends IConnectedBlock { +public interface IRemainsBlock { boolean isVulnerable(BlockState state); @@ -19,6 +18,4 @@ default void freeze(Level level, BlockPos pos, BlockState state) { default void unFreeze(Level level, BlockPos pos, BlockState state) { } - @Override - RemainsConnector getConnector(); } diff --git a/src/main/java/de/teamlapen/vampirism/blocks/mother/MotherBlock.java b/src/main/java/de/teamlapen/vampirism/blocks/mother/MotherBlock.java index 3d767a8f39..6e27bfe70c 100644 --- a/src/main/java/de/teamlapen/vampirism/blocks/mother/MotherBlock.java +++ b/src/main/java/de/teamlapen/vampirism/blocks/mother/MotherBlock.java @@ -1,9 +1,10 @@ package de.teamlapen.vampirism.blocks.mother; import de.teamlapen.vampirism.blockentity.MotherBlockEntity; -import de.teamlapen.vampirism.blocks.connected.ConnectedBlock; +import de.teamlapen.vampirism.blocks.VampirismBlock; import de.teamlapen.vampirism.core.ModTiles; import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.EntityBlock; @@ -14,6 +15,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.MapColor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -22,9 +24,8 @@ import static de.teamlapen.vampirism.blocks.HorizontalContainerBlock.createTickerHelper; -public class MotherBlock extends ConnectedBlock implements EntityBlock, IRemainsBlock { +public class MotherBlock extends VampirismBlock implements EntityBlock, IRemainsBlock { - private final RemainsConnector connector = new RemainsConnector(); public MotherBlock() { super(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BROWN).strength(5, 3600000.0F).sound(SoundType.CHAIN)); @@ -65,14 +66,18 @@ public BlockEntity newBlockEntity(@NotNull BlockPos pos, @NotNull BlockState sta return new MotherBlockEntity(pos, state); } + @Override + public boolean onDestroyedByPlayer(BlockState state, Level level, BlockPos pos, Player player, boolean willHarvest, FluidState fluid) { + if (getBlockEntity(level, pos).map(MotherBlockEntity::isCanBeBroken).orElse(Boolean.TRUE)) { + return super.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluid); + } + return false; + } + @Nullable @Override public BlockEntityTicker getTicker(Level level, @NotNull BlockState state, @NotNull BlockEntityType type) { return level.isClientSide() ? null : createTickerHelper(type, ModTiles.MOTHER.get(), MotherBlockEntity::serverTick); } - @Override - public RemainsConnector getConnector() { - return this.connector; - } } diff --git a/src/main/java/de/teamlapen/vampirism/blocks/mother/MotherTreeStructure.java b/src/main/java/de/teamlapen/vampirism/blocks/mother/MotherTreeStructure.java new file mode 100644 index 0000000000..1ef8ae0f6d --- /dev/null +++ b/src/main/java/de/teamlapen/vampirism/blocks/mother/MotherTreeStructure.java @@ -0,0 +1,178 @@ +package de.teamlapen.vampirism.blocks.mother; + +import de.teamlapen.vampirism.core.ModBlocks; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.state.BlockState; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; + +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * Utility class to build and temporarily store the physical structure of the mother roots/tree + */ +public class MotherTreeStructure { + /** + * @param start Position of the mother block + * @return A newly created representation of the structure in the world + */ + public static MotherTreeStructure getTreeView(Level level, BlockPos start) { + Builder builder = new Builder(); + BlockState mother = level.getBlockState(start); + assert mother.getBlock() == ModBlocks.MOTHER.get(); + builder.addBlock(start, (IRemainsBlock) mother.getBlock(), mother, 0); + Queue> queue = new LinkedList<>(); + queue.add(Pair.of(0, start)); + + while (!queue.isEmpty()) { + Pair p = queue.poll(); + nextTreeStep(level, p.getRight(), p.getLeft() + 1, queue, builder); + } + return builder.build(); + } + + /** + * Analyzes the adjacent blocks to the current level and stores and queues the respective blocks + * + * @param currentPos The current, already processed, position + * @param nextDepth The current depth + 1 + * @param queue Queue to add next block positions to, if any + * @param builder Structure builder to add processed information to + */ + private static void nextTreeStep(Level level, BlockPos currentPos, int nextDepth, Queue> queue, Builder builder) { + Direction.stream().forEach(dir -> { + BlockPos next = currentPos.relative(dir); + if (!builder.allBlocks.contains(next)) { + BlockState state = level.getBlockState(next); + if (state.getBlock() instanceof IRemainsBlock b) { + builder.addBlock(next, b, state, nextDepth); + queue.add(Pair.of(nextDepth, next)); + } + } + }); + + } + + public static Optional> findMother(LevelAccessor level, BlockPos pos) { + return innerFindMother(level, pos, new HashSet<>(), blockState -> blockState.getBlock() == ModBlocks.MOTHER.get()); + } + + private static Optional> innerFindMother(LevelAccessor level, BlockPos pos, Set visited, Predicate predicate) { + if (visited.contains(pos)) return Optional.empty(); + visited.add(pos); + BlockState blockState = level.getBlockState(pos); + if (!(blockState.getBlock() instanceof IRemainsBlock)) return Optional.empty(); + if (predicate.test(blockState)) { + return Optional.of(Pair.of(pos, blockState)); + } else { + return Direction.stream().map(pos::relative).map(newPos -> innerFindMother(level, newPos, visited, predicate)).filter(Optional::isPresent).findFirst().orElse(Optional.empty()); + } + } + + /** + * Set of all blocks belonging to the structure (at initial scan, may contain invalid blocks later) + */ + private final Set allBlocks; + /** + * Set of all vulnerabilities belonging to the structure (at initial scan, may contain invalid blocks later) + */ + private final Set vulnerabilities; + /** + * All blocks sorted by depth (distance from mother core). Only valid up to {@link MotherTreeStructure#validHierarchy} (and potentially invalid due to external changes) + */ + private final Set[] hierarchy; + private int validHierarchy; + + public MotherTreeStructure(Set allBlocks, Set vulnerabilities, Set[] hierarchy) { + this.allBlocks = allBlocks; + this.vulnerabilities = vulnerabilities; + this.hierarchy = hierarchy; + this.validHierarchy = hierarchy.length - 1; + } + + /** + * Set of all blocks belonging to the structure. + * May contain now invalid positions + */ + public Set getCachedBlocks() { + return allBlocks; + } + + /** + * Set of all vulnerabilities belonging to the structure. + * May contain now invalid positions + */ + public Set getCachedVulnerabilities() { + return vulnerabilities; + } + + + /** + * @return Stream of details about all verified (they still exist in the level) vulnerabilities of the structure + */ + public Stream> getVerifiedVulnerabilities(Level level) { + return getCachedVulnerabilities().stream().map(pos -> { + BlockState bs = level.getBlockState(pos); + if (bs.getBlock() instanceof IRemainsBlock b) { + if (b.isVulnerability(bs)) { + return Triple.of(pos, bs, b); + } + } + return null; + } + ).filter(Objects::nonNull); + } + + + /** + * Removes highest depth blocks from hierarchy + * + * @return Set of all blocks of highest depth + */ + public Optional> popHierarchy() { + if (validHierarchy > 0) { + Set set = hierarchy[validHierarchy]; + allBlocks.removeAll(set); //Probably not needed + vulnerabilities.removeAll(set); //Probably not needed + validHierarchy--; + return Optional.of(set); + } + return Optional.empty(); + } + + private static class Builder { + public final Set allBlocks = new HashSet<>(); + + private final Set vulnerabilities = new HashSet<>(); + + private final ArrayList> hierarchy = new ArrayList<>(); + + public void addBlock(BlockPos pos, IRemainsBlock block, BlockState state, int depth) { + allBlocks.add(pos); + if (block.isVulnerability(state)) { + vulnerabilities.add(pos); + } + if (depth < hierarchy.size()) { + hierarchy.get(depth).add(pos); + + } else if (depth == hierarchy.size()) { + Set p = new HashSet<>(); + p.add(pos); + hierarchy.add(p); + } else { + throw new IllegalStateException("Max fucked up"); + } + } + + public MotherTreeStructure build() { + return new MotherTreeStructure(allBlocks, vulnerabilities, hierarchy.toArray(new Set[0])); + } + + + } +} diff --git a/src/main/java/de/teamlapen/vampirism/blocks/mother/RemainsBlock.java b/src/main/java/de/teamlapen/vampirism/blocks/mother/RemainsBlock.java index 7cfa490978..cdf0e7b543 100644 --- a/src/main/java/de/teamlapen/vampirism/blocks/mother/RemainsBlock.java +++ b/src/main/java/de/teamlapen/vampirism/blocks/mother/RemainsBlock.java @@ -1,6 +1,7 @@ package de.teamlapen.vampirism.blocks.mother; -import de.teamlapen.vampirism.blocks.connected.ConnectedBlock; +import de.teamlapen.vampirism.blockentity.MotherBlockEntity; +import de.teamlapen.vampirism.blocks.VampirismBlock; import de.teamlapen.vampirism.core.ModBlocks; import de.teamlapen.vampirism.core.ModTags; import net.minecraft.core.BlockPos; @@ -10,18 +11,21 @@ import net.minecraft.util.RandomSource; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.BonemealableBlock; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; import org.jetbrains.annotations.NotNull; import java.util.Arrays; +import java.util.Optional; -public class RemainsBlock extends ConnectedBlock implements BonemealableBlock, IRemainsBlock { +public class RemainsBlock extends VampirismBlock implements BonemealableBlock, IRemainsBlock { private final boolean vulnerable; private final boolean isVulnerability; - private final RemainsConnector connector = new RemainsConnector(); public RemainsBlock(Properties properties, boolean vulnerable, boolean isVulnerability) { super(properties); @@ -60,27 +64,30 @@ public void performBonemeal(ServerLevel level, @NotNull RandomSource random, Blo } @Override - public void tick(@NotNull BlockState state, @NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull RandomSource random) { - if (this.getConnector().getMotherEntity(level, pos).isEmpty()) { - level.setBlockAndUpdate(pos, ModBlocks.CURSED_EARTH.get().defaultBlockState()); - } - if (this == ModBlocks.VULNERABLE_REMAINS.get()) { - if (Arrays.stream(Direction.values()).allMatch(d -> level.getBlockState(pos.relative(d)).is(ModTags.Blocks.REMAINS))) { - level.setBlockAndUpdate(pos, ModBlocks.CURSED_EARTH.get().defaultBlockState()); - } + public void attack(@NotNull BlockState state, @NotNull Level level, @NotNull BlockPos pos, @NotNull Player player) { + if (player instanceof ServerPlayer serverPlayer) { + getMotherEntity(level, pos).ifPresent(a -> a.informAboutAttacker(serverPlayer)); } } @Override - public void attack(@NotNull BlockState state, @NotNull Level level, @NotNull BlockPos pos, @NotNull Player player) { - if (player instanceof ServerPlayer serverPlayer) { - getConnector().getMotherEntity(level, pos).ifPresent(a -> a.informAboutAttacker(serverPlayer)); - } + public boolean onDestroyedByPlayer(BlockState state, Level level, BlockPos pos, Player player, boolean willHarvest, FluidState fluid) { + getMotherEntity(level, pos).ifPresent(MotherBlockEntity::onStructureBlockRemoved); + return super.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluid); } @Override - public RemainsConnector getConnector() { - return this.connector; + public void tick(@NotNull BlockState state, @NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull RandomSource random) { + if (random.nextInt(100) == 0) { + if (MotherTreeStructure.findMother(level, pos).isEmpty()) { + level.setBlockAndUpdate(pos, ModBlocks.CURSED_EARTH.get().defaultBlockState()); + } + if (this == ModBlocks.VULNERABLE_REMAINS.get()) { + if (Arrays.stream(Direction.values()).allMatch(d -> level.getBlockState(pos.relative(d)).is(ModTags.Blocks.REMAINS))) { + level.setBlockAndUpdate(pos, ModBlocks.CURSED_EARTH.get().defaultBlockState()); + } + } + } } @Override @@ -89,4 +96,14 @@ public void unFreeze(Level level, BlockPos pos, BlockState state) { level.setBlock(pos, ModBlocks.ACTIVE_VULNERABLE_REMAINS.get().defaultBlockState(), 3); } } + + private Optional getMotherEntity(@NotNull LevelAccessor level, @NotNull BlockPos pos) { + return MotherTreeStructure.findMother(level, pos).map(pair -> { + BlockEntity blockEntity = level.getBlockEntity(pair.getLeft()); + if (blockEntity instanceof MotherBlockEntity mother) { + return mother; + } + return null; + }); + } } diff --git a/src/main/java/de/teamlapen/vampirism/blocks/mother/RemainsConnector.java b/src/main/java/de/teamlapen/vampirism/blocks/mother/RemainsConnector.java deleted file mode 100644 index 50c7f6432c..0000000000 --- a/src/main/java/de/teamlapen/vampirism/blocks/mother/RemainsConnector.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.teamlapen.vampirism.blocks.mother; - -import de.teamlapen.vampirism.blockentity.MotherBlockEntity; -import de.teamlapen.vampirism.blocks.connected.ConnectedBlock; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import org.apache.commons.lang3.tuple.Pair; - -import java.util.Optional; -import java.util.stream.Stream; - -public class RemainsConnector extends ConnectedBlock.Connector { - - public RemainsConnector() { - super(IRemainsBlock.class); - } - - public Optional> getMother(Level level, BlockPos pos) { - return this.find(level, pos, state -> ((IRemainsBlock) state.getBlock()).isMother(state)); - } - - public Optional getMotherEntity(Level level, BlockPos pos) { - return getMother(level, pos).map(pair -> { - BlockEntity blockEntity = level.getBlockEntity(pair.getLeft()); - if (blockEntity instanceof MotherBlockEntity mother) { - return mother; - } - return null; - }); - } - - public Stream> getVulnerabilities(Level level, BlockPos pos) { - return this.collectConnectedBlocks(level, pos, state -> ((IRemainsBlock) state.getBlock()).isVulnerability(state)); - } -} diff --git a/src/main/java/de/teamlapen/vampirism/core/ModBlocks.java b/src/main/java/de/teamlapen/vampirism/core/ModBlocks.java index 7e820533dc..9302e8684a 100755 --- a/src/main/java/de/teamlapen/vampirism/core/ModBlocks.java +++ b/src/main/java/de/teamlapen/vampirism/core/ModBlocks.java @@ -162,11 +162,11 @@ public class ModBlocks { public static final RegistryObject COFFIN_RED = registerWithItem("coffin_red", () -> new CoffinBlock(DyeColor.RED)); public static final RegistryObject COFFIN_BLACK = registerWithItem("coffin_black", () -> new CoffinBlock(DyeColor.BLACK)); public static final RegistryObject ALCHEMY_TABLE = registerWithItem("alchemy_table", AlchemyTableBlock::new); - public static final RegistryObject REMAINS = registerWithItem("remains", () -> new RemainsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BROWN).strength(0.5F, 3600000.0F).sound(SoundType.ROOTED_DIRT).randomTicks(), false, false)); - public static final RegistryObject VULNERABLE_REMAINS = registerWithItem("vulnerable_remains", () -> new RemainsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BROWN).strength(0.5F, 3600000.0F).sound(SoundType.ROOTED_DIRT).randomTicks(), true, true)); - public static final RegistryObject INCAPACITATED_VULNERABLE_REMAINS = registerWithItem("incapacitated_vulnerable_remains", () -> new RemainsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BROWN).strength(0.5F, 3600000.0F).sound(SoundType.ROOTED_DIRT).randomTicks(), false, true)); + public static final RegistryObject REMAINS = registerWithItem("remains", () -> new RemainsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BROWN).strength(-1, 3600000.0F).sound(SoundType.ROOTED_DIRT).randomTicks().noLootTable(), false, false)); + public static final RegistryObject VULNERABLE_REMAINS = registerWithItem("vulnerable_remains", () -> new RemainsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BROWN).strength(-1, 3600000.0F).sound(SoundType.ROOTED_DIRT).randomTicks().noLootTable(), true, true)); + public static final RegistryObject INCAPACITATED_VULNERABLE_REMAINS = registerWithItem("incapacitated_vulnerable_remains", () -> new RemainsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BROWN).strength(-1.0F, 3600000.0F).sound(SoundType.ROOTED_DIRT).randomTicks().noLootTable(), false, true)); - public static final RegistryObject ACTIVE_VULNERABLE_REMAINS = BLOCKS.register("active_vulnerable_remains", () -> new ActiveVulnerableRemainsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BROWN).strength(0.5F, 3600000.0F).sound(SoundType.ROOTED_DIRT))); + public static final RegistryObject ACTIVE_VULNERABLE_REMAINS = BLOCKS.register("active_vulnerable_remains", () -> new ActiveVulnerableRemainsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BROWN).strength(-1, 3600000.0F).sound(SoundType.ROOTED_DIRT).noLootTable())); public static final RegistryObject CURSED_HANGING_ROOTS = registerWithItem("cursed_hanging_roots", () -> { var block = new HangingRootsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BROWN).noCollission().instabreak().sound(SoundType.GRASS).offsetType(BlockBehaviour.OffsetType.XZ).ignitedByLava().pushReaction(PushReaction.DESTROY)); diff --git a/src/main/java/de/teamlapen/vampirism/data/LootTablesGenerator.java b/src/main/java/de/teamlapen/vampirism/data/LootTablesGenerator.java index 6fcde8bf2f..c128c7b6fc 100644 --- a/src/main/java/de/teamlapen/vampirism/data/LootTablesGenerator.java +++ b/src/main/java/de/teamlapen/vampirism/data/LootTablesGenerator.java @@ -324,10 +324,6 @@ protected void generate() { this.add(ModBlocks.THRONE.get(), (p_218567_0_) -> createSinglePropConditionTable(p_218567_0_, VampirismSplitBlock.PART, VampirismSplitBlock.Part.MAIN)); this.dropSelf(ModBlocks.ALCHEMY_TABLE.get()); this.add(ModBlocks.DIAGONAL_CURSED_BARK.get(), noDrop()); - this.add(ModBlocks.REMAINS.get(), noDrop()); - this.add(ModBlocks.ACTIVE_VULNERABLE_REMAINS.get(), noDrop()); - this.add(ModBlocks.INCAPACITATED_VULNERABLE_REMAINS.get(), noDrop()); - this.add(ModBlocks.VULNERABLE_REMAINS.get(), noDrop()); this.add(ModBlocks.CURSED_HANGING_ROOTS.get(), ModBlockLootTables::createShearsOnlyDrop); this.add(ModBlocks.MOTHER.get(), createSingleItemTable(ModItems.MOTHER_CORE.get()) .withPool(LootPool.lootPool().name("bonus").setRolls(UniformGenerator.between(1,4)) diff --git a/src/main/java/de/teamlapen/vampirism/entity/player/ModPlayerEventHandler.java b/src/main/java/de/teamlapen/vampirism/entity/player/ModPlayerEventHandler.java index 3f06039418..0b80765612 100755 --- a/src/main/java/de/teamlapen/vampirism/entity/player/ModPlayerEventHandler.java +++ b/src/main/java/de/teamlapen/vampirism/entity/player/ModPlayerEventHandler.java @@ -11,11 +11,9 @@ import de.teamlapen.vampirism.api.entity.vampire.IVampire; import de.teamlapen.vampirism.api.items.IFactionLevelItem; import de.teamlapen.vampirism.api.items.IFactionSlayerItem; -import de.teamlapen.vampirism.blockentity.MotherBlockEntity; import de.teamlapen.vampirism.blockentity.TotemBlockEntity; import de.teamlapen.vampirism.blocks.*; import de.teamlapen.vampirism.blocks.mother.MotherBlock; -import de.teamlapen.vampirism.blocks.mother.RemainsBlock; import de.teamlapen.vampirism.config.VampirismConfig; import de.teamlapen.vampirism.core.ModBlocks; import de.teamlapen.vampirism.core.ModEffects; @@ -424,13 +422,11 @@ public void onPlayerLeftClickedBlock(PlayerInteractEvent.@NotNull LeftClickBlock event.setCanceled(true); } else if ((ModBlocks.GARLIC_DIFFUSER_NORMAL.get() == state.getBlock() || ModBlocks.GARLIC_DIFFUSER_WEAK.get() == state.getBlock() || ModBlocks.GARLIC_DIFFUSER_IMPROVED.get() == state.getBlock()) && Helper.isVampire(event.getEntity())) { event.getEntity().addEffect(new MobEffectInstance(ModEffects.GARLIC.get())); - } else if (state.getBlock() instanceof RemainsBlock) { - event.setUseItem(Event.Result.DENY); } else if (state.getBlock() instanceof MotherBlock) { - BlockEntity blockEntity = event.getEntity().level().getBlockEntity(pos); - if (blockEntity instanceof MotherBlockEntity mother && !mother.isCanBeDestroyed()) { - event.setUseItem(Event.Result.DENY); - } + //BlockEntity blockEntity = event.getEntity().level().getBlockEntity(pos); + //if (blockEntity instanceof MotherBlockEntity mother && !mother.isCanBeBroken()) { + // event.setUseItem(Event.Result.DENY); + //} } else if (state.getBlock() == ModBlocks.DARK_SPRUCE_LOG.get()) { if (state.getValue(DarkSpruceLogs.INVULNERABLE)) { event.setUseItem(Event.Result.DENY); diff --git a/src/main/java/de/teamlapen/vampirism/world/gen/structure/mother/MotherPiece.java b/src/main/java/de/teamlapen/vampirism/world/gen/structure/mother/MotherPiece.java index 992cef24cf..04c1159ab7 100644 --- a/src/main/java/de/teamlapen/vampirism/world/gen/structure/mother/MotherPiece.java +++ b/src/main/java/de/teamlapen/vampirism/world/gen/structure/mother/MotherPiece.java @@ -1,6 +1,5 @@ package de.teamlapen.vampirism.world.gen.structure.mother; -import de.teamlapen.vampirism.blocks.DarkSpruceLogs; import de.teamlapen.vampirism.core.ModBlocks; import de.teamlapen.vampirism.world.gen.VampirismFeatures; import net.minecraft.core.BlockPos; @@ -41,14 +40,13 @@ public void postProcess(@NotNull WorldGenLevel level, @NotNull StructureManager } } - protected void placeLogs(WorldGenLevel level, BoundingBox box) { - for (int i = 5; i < 11; i++) { - generateLogSlate(level, box, i); - } - BlockState log = ModBlocks.DARK_SPRUCE_LOG.get().defaultBlockState().setValue(DarkSpruceLogs.INVULNERABLE, true); - generateBox(level, box, 2, 11, 0, 4, 11, 0, log, log, false); - generateBox(level, box, 1, 11, 1, 2, 11, 1, log, log, false); - placeAir(level, box); + protected void generateLogSlate(WorldGenLevel level, BoundingBox box, int yHeight) { + BlockState log = ModBlocks.REMAINS.get().defaultBlockState(); + generateBox(level, box, 2, yHeight, 0, 5, yHeight, 0, log, log, false); + generateBox(level, box, 1, yHeight, 1, 6, yHeight, 1, log, log, false); + generateBox(level, box, 0, yHeight, 2, 7, yHeight, 5, log, log, false); + generateBox(level, box, 1, yHeight, 6, 6, yHeight, 6, log, log, false); + generateBox(level, box, 2, yHeight, 7, 5, yHeight, 7, log, log, false); } protected void placeAir(WorldGenLevel level, BoundingBox box) { @@ -64,13 +62,14 @@ protected void placeAir(WorldGenLevel level, BoundingBox box) { generateBox(level, box, 3, 8, 4, 4, 8, 4, air, air, false); } - protected void generateLogSlate(WorldGenLevel level, BoundingBox box, int yHeight) { - BlockState log = ModBlocks.DARK_SPRUCE_LOG.get().defaultBlockState().setValue(DarkSpruceLogs.INVULNERABLE, true); - generateBox(level, box, 2, yHeight, 0, 5, yHeight, 0, log, log, false); - generateBox(level, box, 1, yHeight, 1, 6, yHeight, 1, log, log, false); - generateBox(level, box, 0, yHeight, 2, 7, yHeight, 5, log, log, false); - generateBox(level, box, 1, yHeight, 6, 6, yHeight, 6, log, log, false); - generateBox(level, box, 2, yHeight, 7, 5, yHeight, 7, log, log, false); + protected void placeLogs(WorldGenLevel level, BoundingBox box) { + for (int i = 5; i < 11; i++) { + generateLogSlate(level, box, i); + } + BlockState log = ModBlocks.REMAINS.get().defaultBlockState(); + generateBox(level, box, 2, 11, 0, 4, 11, 0, log, log, false); + generateBox(level, box, 1, 11, 1, 2, 11, 1, log, log, false); + placeAir(level, box); } protected void placeMother(@NotNull WorldGenLevel level, @NotNull BoundingBox box) { @@ -96,20 +95,20 @@ protected void placeRoots(@NotNull WorldGenLevel level, @NotNull BoundingBox box int y = 0; int x = -5; int z = -7; - placeBlock(level, remains, x + 0, y + 0, z + 14, box); - placeBlock(level, remains, x + 1, y + 0, z + 14, box); - placeBlock(level, remains, x + 2, y + 0, z + 2, box); - placeBlock(level, remains, x + 2, y + 0, z + 14, box); - placeBlock(level, remains, x + 3, y + 0, z + 2, box); - placeBlock(level, remains, x + 4, y + 0, z + 8, box); - placeBlock(level, remains, x + 5, y + 0, z + 8, box); - placeBlock(level, remains, x + 6, y + 0, z + 11, box); - placeBlock(level, remains, x + 7, y + 0, z + 4, box); - placeBlock(level, active, x + 8, y + 0, z + 9, box); - placeBlock(level, remains, x + 12, y + 0, z + 16, box); - placeBlock(level, remains, x + 12, y + 0, z + 17, box); - placeBlock(level, remains, x + 13, y + 0, z + 17, box); - placeBlock(level, remains, x + 14, y + 0, z + 17, box); + placeBlock(level, remains, x, y, z + 14, box); + placeBlock(level, remains, x + 1, y, z + 14, box); + placeBlock(level, remains, x + 2, y, z + 2, box); + placeBlock(level, remains, x + 2, y, z + 14, box); + placeBlock(level, remains, x + 3, y, z + 2, box); + placeBlock(level, remains, x + 4, y, z + 8, box); + placeBlock(level, remains, x + 5, y, z + 8, box); + placeBlock(level, remains, x + 6, y, z + 11, box); + placeBlock(level, remains, x + 7, y, z + 4, box); + placeBlock(level, active, x + 8, y, z + 9, box); + placeBlock(level, remains, x + 12, y, z + 16, box); + placeBlock(level, remains, x + 12, y, z + 17, box); + placeBlock(level, remains, x + 13, y, z + 17, box); + placeBlock(level, remains, x + 14, y, z + 17, box); placeBlock(level, remains, x + 1, y + 1, z + 10, box); placeBlock(level, remains, x + 2, y + 1, z + 10, box); @@ -125,11 +124,11 @@ protected void placeRoots(@NotNull WorldGenLevel level, @NotNull BoundingBox box placeBlock(level, remains, x + 7, y + 1, z + 4, box); placeBlock(level, remains, x + 7, y + 1, z + 5, box); placeBlock(level, remains, x + 7, y + 1, z + 11, box); - placeBlock(level, remains, x + 8, y + 1, z + 0, box); + placeBlock(level, remains, x + 8, y + 1, z, box); placeBlock(level, remains, x + 8, y + 1, z + 8, box); placeBlock(level, remains, x + 8, y + 1, z + 9, box); placeBlock(level, remains, x + 8, y + 1, z + 10, box); - placeBlock(level, remains, x + 9, y + 1, z + 0, box); + placeBlock(level, remains, x + 9, y + 1, z, box); placeBlock(level, remains, x + 9, y + 1, z + 16, box); placeBlock(level, remains, x + 9, y + 1, z + 17, box); placeBlock(level, remains, x + 11, y + 1, z + 4, box); @@ -164,7 +163,7 @@ protected void placeRoots(@NotNull WorldGenLevel level, @NotNull BoundingBox box placeBlock(level, remains, x + 5, y + 2, z + 17, box); placeBlock(level, remains, x + 6, y + 2, z + 2, box); placeBlock(level, remains, x + 6, y + 2, z + 8, box); - placeBlock(level, remains, x + 7, y + 2, z + 0, box); + placeBlock(level, remains, x + 7, y + 2, z, box); placeBlock(level, active, x + 7, y + 2, z + 1, box); placeBlock(level, remains, x + 7, y + 2, z + 2, box); placeBlock(level, remains, x + 7, y + 2, z + 3, box); @@ -174,7 +173,7 @@ protected void placeRoots(@NotNull WorldGenLevel level, @NotNull BoundingBox box placeBlock(level, remains, x + 7, y + 2, z + 11, box); placeBlock(level, remains, x + 7, y + 2, z + 13, box); placeBlock(level, remains, x + 7, y + 2, z + 14, box); - placeBlock(level, remains, x + 8, y + 2, z + 0, box); + placeBlock(level, remains, x + 8, y + 2, z, box); placeBlock(level, remains, x + 8, y + 2, z + 9, box); placeBlock(level, remains, x + 8, y + 2, z + 10, box); placeBlock(level, remains, x + 8, y + 2, z + 13, box);