diff --git a/src/main/java/de/ellpeck/actuallyadditions/mod/event/ClientEvents.java b/src/main/java/de/ellpeck/actuallyadditions/mod/event/ClientEvents.java index 11e6cbe30..13ae0741d 100644 --- a/src/main/java/de/ellpeck/actuallyadditions/mod/event/ClientEvents.java +++ b/src/main/java/de/ellpeck/actuallyadditions/mod/event/ClientEvents.java @@ -12,34 +12,46 @@ package de.ellpeck.actuallyadditions.mod.event; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; import de.ellpeck.actuallyadditions.mod.ActuallyAdditions; import de.ellpeck.actuallyadditions.mod.blocks.IHudDisplay; import de.ellpeck.actuallyadditions.mod.config.CommonConfig; import de.ellpeck.actuallyadditions.mod.data.WorldData; import de.ellpeck.actuallyadditions.mod.inventory.gui.EnergyDisplay; +import de.ellpeck.actuallyadditions.mod.items.DrillItem; +import de.ellpeck.actuallyadditions.mod.items.ItemDrillUpgrade; import de.ellpeck.actuallyadditions.mod.tile.IEnergyDisplay; import de.ellpeck.actuallyadditions.mod.tile.TileEntityBase; +import de.ellpeck.actuallyadditions.mod.util.AssetUtil; import de.ellpeck.actuallyadditions.mod.util.StackUtil; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.client.event.RenderGuiOverlayEvent; +import net.neoforged.neoforge.client.event.RenderHighlightEvent; import net.neoforged.neoforge.client.gui.overlay.GuiOverlayManager; import net.neoforged.neoforge.event.TickEvent; import net.neoforged.neoforge.event.entity.player.ItemTooltipEvent; +import java.util.List; + @OnlyIn(Dist.CLIENT) public class ClientEvents { @@ -227,6 +239,46 @@ public class ClientEvents { } } + @SubscribeEvent + public void renderBlockHighlight(RenderHighlightEvent.Block event) { + Minecraft mc = Minecraft.getInstance(); + Player player = mc.player; + if (mc.player == null) + return; + + ItemStack stack = player.getMainHandItem(); + if (stack.getItem() instanceof DrillItem drillItem) { + if (!player.isShiftKeyDown() && drillItem.getHasUpgrade(stack, ItemDrillUpgrade.UpgradeType.THREE_BY_THREE)) { + Level level = player.level(); + Vec3 vec3 = event.getCamera().getPosition(); + double d0 = vec3.x(); + double d1 = vec3.y(); + double d2 = vec3.z(); + BlockHitResult blockHitResult = event.getTarget(); + BlockPos targetPos = blockHitResult.getBlockPos(); + BlockState blockState = level.getBlockState(targetPos); + VertexConsumer lineConsumer = event.getMultiBufferSource().getBuffer(RenderType.lines()); + if (stack.isCorrectToolForDrops(blockState)) { + int radius = 0; + if (drillItem.getHasUpgrade(stack, ItemDrillUpgrade.UpgradeType.FIVE_BY_FIVE)) { + radius = 2; + } else if (drillItem.getHasUpgrade(stack, ItemDrillUpgrade.UpgradeType.THREE_BY_THREE)) { + radius = 1; + } + if (radius == 0) return; //No radius, no need to render extra hitboxes + + List coords = drillItem.gatherBreakingPositions(stack, radius, level, targetPos, blockHitResult.getDirection(), player); + for (BlockPos blockPos : coords) { + if (blockPos.equals(targetPos)) continue; //Let the original event draw this one! + AssetUtil.renderHitOutline(event.getPoseStack(), lineConsumer, player, d0, d1, d2, level, blockPos, level.getBlockState(blockPos)); + } + } + } + } + } + + + /* @SubscribeEvent //TODO someday move the laser rendering to a new system public void onRenderStage(final RenderLevelStageEvent event) { if(event.getStage() == RenderLevelStageEvent.Stage.AFTER_SOLID_BLOCKS) { diff --git a/src/main/java/de/ellpeck/actuallyadditions/mod/items/DrillItem.java b/src/main/java/de/ellpeck/actuallyadditions/mod/items/DrillItem.java index 66cd3a042..c9bf7c02e 100644 --- a/src/main/java/de/ellpeck/actuallyadditions/mod/items/DrillItem.java +++ b/src/main/java/de/ellpeck/actuallyadditions/mod/items/DrillItem.java @@ -54,6 +54,7 @@ import net.neoforged.neoforge.items.IItemHandler; import net.neoforged.neoforge.items.IItemHandlerModifiable; import javax.annotation.Nonnull; +import java.util.ArrayList; import java.util.List; public class DrillItem extends ItemEnergy { @@ -457,6 +458,77 @@ public class DrillItem extends ItemEnergy { return true; } + /** + * Generate a list of block positions that can be broken taking radius, poker and side into account + * @param stack The Drill + * @param radius The Radius to break Blocks in (0 means only 1 Block will be broken!) + * @param world The World + * @param aPos The position of the block being broken + * @param side The side of the block being broken + * @param player The Player who breaks the Blocks + * @return A list of block positions that can be broken + */ + public List gatherBreakingPositions(ItemStack stack, int radius, Level world, BlockPos aPos, Direction side, Player player) { + int energyStored = this.getEnergyStored(stack); + List positions = new ArrayList<>(); + + int xRange = radius; + int yRange = radius; + int zRange = 0; + + //Corrects Blocks to hit depending on Side of original Block hit + if (side.getAxis() == Direction.Axis.Y) { + zRange = radius; + yRange = 0; + } + if (side.getAxis() == Direction.Axis.X) { + xRange = 0; + zRange = radius; + } + + //Not defined later because main Block is getting broken below + BlockState state = world.getBlockState(aPos); + float mainHardness = state.getDestroySpeed(world, aPos); + + //Break Middle Block first + int use = this.getEnergyUsePerBlock(stack); + if (energyStored < use) { + return positions; + } + + if (radius == 2 && side.getAxis() != Direction.Axis.Y) { + aPos = aPos.above(); + BlockState theState = world.getBlockState(aPos); + if (theState.getDestroySpeed(world, aPos) <= mainHardness + 5.0F) { + positions.add(aPos.immutable()); + } + } + + //Break Blocks around + if (radius > 0 && mainHardness >= 0.2F) { + for (int xPos = aPos.getX() - xRange; xPos <= aPos.getX() + xRange; xPos++) { + for (int yPos = aPos.getY() - yRange; yPos <= aPos.getY() + yRange; yPos++) { + for (int zPos = aPos.getZ() - zRange; zPos <= aPos.getZ() + zRange; zPos++) { + if (!(aPos.getX() == xPos && aPos.getY() == yPos && aPos.getZ() == zPos)) { + if (energyStored >= use) { + //Only break Blocks around that are (about) as hard or softer + BlockPos thePos = new BlockPos(xPos, yPos, zPos); + BlockState theState = world.getBlockState(thePos); + if (theState.getDestroySpeed(world, thePos) <= mainHardness + 5.0F) { + energyStored -= use; + positions.add(thePos.immutable()); + } + } else { + return positions; + } + } + } + } + } + } + return positions; + } + /** * Tries to harvest a certain Block * Breaks the Block, drops Particles etc. diff --git a/src/main/java/de/ellpeck/actuallyadditions/mod/util/AssetUtil.java b/src/main/java/de/ellpeck/actuallyadditions/mod/util/AssetUtil.java index ac45975f7..8aee13317 100644 --- a/src/main/java/de/ellpeck/actuallyadditions/mod/util/AssetUtil.java +++ b/src/main/java/de/ellpeck/actuallyadditions/mod/util/AssetUtil.java @@ -36,13 +36,19 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.resources.language.I18n; import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; import net.neoforged.neoforge.client.ClientHooks; @@ -423,4 +429,43 @@ public final class AssetUtil { } return new float[]{0.0f, (pos -= 170.0f) * 3.0f, 255.0f - pos * 3.0f}; } + + @OnlyIn(Dist.CLIENT) + public static void renderHitOutline(PoseStack poseStack, VertexConsumer consumer, Entity entity, + double camX, double camY, double camZ, Level level, BlockPos pos, BlockState state) { + renderShape(poseStack, consumer, state.getShape(level, pos, CollisionContext.of(entity)), + (double) pos.getX() - camX, + (double) pos.getY() - camY, + (double) pos.getZ() - camZ, + 0.0F, + 0.0F, + 0.0F, + 0.4F + ); + } + + @OnlyIn(Dist.CLIENT) + private static void renderShape(PoseStack poseStack, VertexConsumer consumer, VoxelShape shape, + double x, double y, double z, float red, float green, float blue, float alpha) { + PoseStack.Pose posestack$pose = poseStack.last(); + shape.forAllEdges( + (minX, minY, minZ, maxX, maxY, maxZ) -> { + float f = (float) (maxX - minX); + float f1 = (float) (maxY - minY); + float f2 = (float) (maxZ - minZ); + float f3 = Mth.sqrt(f * f + f1 * f1 + f2 * f2); + f /= f3; + f1 /= f3; + f2 /= f3; + consumer.vertex(posestack$pose.pose(), (float) (minX + x), (float) (minY + y), (float) (minZ + z)) + .color(red, green, blue, alpha) + .normal(posestack$pose.normal(), f, f1, f2) + .endVertex(); + consumer.vertex(posestack$pose.pose(), (float) (maxX + x), (float) (maxY + y), (float) (maxZ + z)) + .color(red, green, blue, alpha) + .normal(posestack$pose.normal(), f, f1, f2) + .endVertex(); + } + ); + } }