/* * Copyright (C) 2023 NotEnoughUpdates contributors * * This file is part of NotEnoughUpdates. * * NotEnoughUpdates is free software: you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * NotEnoughUpdates is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with NotEnoughUpdates. If not, see . */ package io.github.moulberry.notenoughupdates.miscfeatures.world; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe; import io.github.moulberry.notenoughupdates.core.util.render.RenderUtils; import io.github.moulberry.notenoughupdates.util.SBInfo; import io.github.moulberry.notenoughupdates.util.SpecialColour; import lombok.val; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.init.Blocks; import net.minecraft.network.play.server.S22PacketMultiBlockChange; import net.minecraft.network.play.server.S23PacketBlockChange; import net.minecraft.util.BlockPos; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @NEUAutoSubscribe public class CrystalHollowChestHighlighter extends GenericBlockHighlighter { // Because ConcurrentModificationException is the bane of me public static CopyOnWriteArrayList markedBlocks = new CopyOnWriteArrayList<>(); public static void processBlockChangePacket(S23PacketBlockChange packetIn) { BlockPos pos = packetIn.getBlockPosition(); if (packetIn.blockState == null) return; checkForChest(pos, packetIn.blockState); } public static void processMultiBlockChangePacket(S22PacketMultiBlockChange packetIn) { for (S22PacketMultiBlockChange.BlockUpdateData blockChanged : packetIn.getChangedBlocks()) { BlockPos pos = blockChanged.getPos(); checkForChest(pos, blockChanged.getBlockState()); } } public static void checkForChest(BlockPos pos, IBlockState blockState) { val world = Minecraft.getMinecraft().theWorld; val player = Minecraft.getMinecraft().thePlayer; if (world == null || player == null) return; IBlockState oldState = world.getBlockState(pos); if ((oldState.getBlock() == Blocks.air || oldState.getBlock() == Blocks.stone) && blockState.getBlock() == Blocks.chest) { // Only add if in a 10x10x10 area. Minimises other players' chests being caught if (player.getEntityBoundingBox().expand(10, 10, 10).isVecInside(new Vec3(pos))) { markedBlocks.add(pos); } } } @SubscribeEvent public void onTick(TickEvent.ClientTickEvent event) { if (!isEnabled()) return; markedBlocks.forEach(this::tryRegisterInterest); // Here to catch chests that get highlighted by other people after they open them, and // any highlighted blocks in which the chest despawned in List blockToRemove = new ArrayList<>(); highlightedBlocks.forEach(it -> { if (Minecraft.getMinecraft().theWorld.getBlockState(it).getBlock() != Blocks.chest) { blockToRemove.add(it); } }); blockToRemove.forEach(highlightedBlocks::remove); blockToRemove.forEach(markedBlocks::remove); } @SubscribeEvent public void onBlockInteraction(PlayerInteractEvent event) { if (event.action == PlayerInteractEvent.Action.RIGHT_CLICK_BLOCK) { markedBlocks.remove(event.pos); highlightedBlocks.remove(event.pos); } } @Override public void onWorldRenderLast(RenderWorldLastEvent event) { if (!isEnabled()) return; World w = Minecraft.getMinecraft().theWorld; if (w == null) return; for (BlockPos blockPos : highlightedBlocks) { RenderUtils.renderBoundingBox(blockPos, getColor(blockPos), event.partialTicks, false); } } @Override protected boolean isEnabled() { return "crystal_hollows".equals(SBInfo.getInstance().getLocation()) && NotEnoughUpdates.INSTANCE.config.world.highlightCrystalHollowChests; } @Override protected boolean isValidHighlightSpot(BlockPos key) { World w = Minecraft.getMinecraft().theWorld; if (w == null) return false; Block b = w.getBlockState(key).getBlock(); return b == Blocks.chest; } @Override protected int getColor(BlockPos blockPos) { return SpecialColour.specialToChromaRGB(NotEnoughUpdates.INSTANCE.config.world.crystalHollowChestColor); } }