diff options
Diffstat (limited to 'src')
17 files changed, 1095 insertions, 204 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java index 5ad82d56..0ed68e06 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java @@ -25,6 +25,7 @@ import io.github.moulberry.notenoughupdates.listener.ItemTooltipListener; import io.github.moulberry.notenoughupdates.listener.NEUEventListener; import io.github.moulberry.notenoughupdates.listener.RenderListener; import io.github.moulberry.notenoughupdates.miscfeatures.CrystalOverlay; +import io.github.moulberry.notenoughupdates.miscfeatures.CrystalWishingCompassSolver; import io.github.moulberry.notenoughupdates.miscfeatures.CustomItemEffects; import io.github.moulberry.notenoughupdates.miscfeatures.DwarvenMinesWaypoints; import io.github.moulberry.notenoughupdates.miscfeatures.EnchantingSolvers; @@ -226,6 +227,7 @@ public class NotEnoughUpdates { MinecraftForge.EVENT_BUS.register(InventoryStorageSelector.getInstance()); MinecraftForge.EVENT_BUS.register(SlotLocking.getInstance()); MinecraftForge.EVENT_BUS.register(FishingHelper.getInstance()); + MinecraftForge.EVENT_BUS.register(CrystalWishingCompassSolver.getInstance()); MinecraftForge.EVENT_BUS.register(new DwarvenMinesTextures()); MinecraftForge.EVENT_BUS.register(CustomBiomes.INSTANCE); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java index de017cb5..5c2bf5c0 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java @@ -40,6 +40,7 @@ public class Commands { ClientCommandHandler.instance.registerCommand(new StatsCommand()); ClientCommandHandler.instance.registerCommand(new DevTestCommand()); ClientCommandHandler.instance.registerCommand(new NullzeeSphereCommand()); + ClientCommandHandler.instance.registerCommand(new DiagCommand()); // Repo Commands ClientCommandHandler.instance.registerCommand(new ResetRepoCommand()); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java index 7caf09cb..53a7894b 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java @@ -24,7 +24,7 @@ import java.util.List; public class DevTestCommand extends ClientCommandBase { private static final List<String> DEV_TESTERS = - Arrays.asList("moulberry", "lucycoconut", "ironm00n", "ariyio", "throwpo", "lrg89", "dediamondpro", "lulonaut"); + Arrays.asList("moulberry", "lucycoconut", "ironm00n", "ariyio", "throwpo", "lrg89", "dediamondpro", "lulonaut", "craftyoldminer"); private static final String[] DEV_FAIL_STRINGS = { "No.", diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DiagCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DiagCommand.java new file mode 100644 index 00000000..88264538 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DiagCommand.java @@ -0,0 +1,90 @@ +package io.github.moulberry.notenoughupdates.commands.dev; + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.commands.ClientCommandBase; +import io.github.moulberry.notenoughupdates.miscfeatures.CrystalMetalDetectorSolver; +import io.github.moulberry.notenoughupdates.miscfeatures.CrystalWishingCompassSolver; +import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; + +public class DiagCommand extends ClientCommandBase { + public DiagCommand() { + super("neudiag"); + } + + private static final String USAGE_TEXT = EnumChatFormatting.WHITE + + "Usage: /neudiag <metal | wishing | debug>\n\n" + + "/neudiag metal Metal Detector Solver diagnostics\n" + + "/neudiag wishing Wishing Compass Solver diagnostics\n" + + "/neudiag debug\n" + + " <no sub-command> Show current flags\n" + + " <enable | disable> <flag> Enable/disable flag\n"; + + private void showUsage(ICommandSender sender) { + sender.addChatMessage(new ChatComponentText(USAGE_TEXT)); + } + + @Override + public void processCommand(ICommandSender sender, String[] args) throws CommandException { + if (args.length == 0) { + showUsage(sender); + return; + } + + String command = args[0].toLowerCase(); + switch (command) { + case "metal": + CrystalMetalDetectorSolver.logDiagnosticData(true); + break; + case "wishing": + CrystalWishingCompassSolver.getInstance().logDiagnosticData(true); + break; + case "debug": + if (args.length > 1) { + boolean enablingFlag = true; + String action = args[1]; + switch (action) { + case "disable": + enablingFlag = false; + // falls through + case "enable": + if (args.length != 3) { + sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "You must specify a flag:\n" + + NEUDebugFlag.FLAG_LIST)); + return; + } + + String flagName = args[2].toUpperCase(); + try { + NEUDebugFlag debugFlag = NEUDebugFlag.valueOf(flagName); + if (enablingFlag) { + NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.add(debugFlag); + } else { + NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.remove(debugFlag); + } + } catch (IllegalArgumentException e) { + sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + flagName + " is invalid. Valid flags are:\n" + + NEUDebugFlag.FLAG_LIST)); + return; + } + break; + default: + showUsage(sender); + return; + } + } + + sender.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + "Effective debug flags: " + + NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.toString())); + break; + default: + showUsage(sender); + return; + } + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/core/util/Line.java b/src/main/java/io/github/moulberry/notenoughupdates/core/util/Line.java new file mode 100644 index 00000000..fb134e85 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/core/util/Line.java @@ -0,0 +1,90 @@ +package io.github.moulberry.notenoughupdates.core.util; + +import net.minecraft.util.Vec3; + +/** + * Represents a line using two points along the line or a segment with endpoints. + */ +public class Line { + private static final double DOUBLE_EPSILON = 4.94065645841247E-324; + public Vec3 point1; + public Vec3 point2; + + public Line(Vec3 first, Vec3 second) { + point1 = first; + point2 = second; + } + + public Vec3 getMidpoint() { + return new Vec3((point1.xCoord + point2.xCoord) / 2.0, + (point1.yCoord + point2.yCoord) / 2.0, + (point1.zCoord + point2.zCoord) / 2.0); + } + + /** + * Calculates the intersection line segment between 2 lines + * Based on http://paulbourke.net/geometry/pointlineplane/calclineline.cs + * + * @return The intersection {@link Line} or {@code null} if no solution found + */ + public Line getIntersectionLineSegment(Line other) + { + Vec3 p1 = this.point1; + Vec3 p2 = this.point2; + Vec3 p3 = other.point1; + Vec3 p4 = other.point2; + Vec3 p13 = p1.subtract(p3); + Vec3 p43 = p4.subtract(p3); + + if (lengthSquared(p43) < DOUBLE_EPSILON) { + return null; + } + + Vec3 p21 = p2.subtract(p1); + if (lengthSquared(p21) < DOUBLE_EPSILON) { + return null; + } + + double d1343 = p13.xCoord * p43.xCoord + p13.yCoord * p43.yCoord + p13.zCoord * p43.zCoord; + double d4321 = p43.xCoord * p21.xCoord + p43.yCoord * p21.yCoord + p43.zCoord * p21.zCoord; + double d1321 = p13.xCoord * p21.xCoord + p13.yCoord * p21.yCoord + p13.zCoord * p21.zCoord; + double d4343 = p43.xCoord * p43.xCoord + p43.yCoord * p43.yCoord + p43.zCoord * p43.zCoord; + double d2121 = p21.xCoord * p21.xCoord + p21.yCoord * p21.yCoord + p21.zCoord * p21.zCoord; + + double denom = d2121 * d4343 - d4321 * d4321; + if (Math.abs(denom) < DOUBLE_EPSILON) { + return null; + } + double numer = d1343 * d4321 - d1321 * d4343; + + double mua = numer / denom; + double mub = (d1343 + d4321 * (mua)) / d4343; + + Line resultSegment = new Line( + new Vec3 ( + (float)(p1.xCoord + mua * p21.xCoord), + (float)(p1.yCoord + mua * p21.yCoord), + (float)(p1.zCoord + mua * p21.zCoord)), + new Vec3 ( + (float)(p3.xCoord + mub * p43.xCoord), + (float)(p3.yCoord + mub * p43.yCoord), + (float)(p3.zCoord + mub * p43.zCoord))); + + return resultSegment; + } + + public Line getImmutable() { + return new Line(point1, point2); + } + + private static double lengthSquared(Vec3 vec) { + return vec.dotProduct(vec); + } + + public String toString() { + return String.format("point1 = %s, point2 = %s, midpoint = %s", + point1 == null ? "NULL" : point1.toString(), + point2 == null ? "NULL" : point2.toString(), + (point1 == null || point2 == null) ? "NULL" : getMidpoint()); + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/listener/ChatListener.java b/src/main/java/io/github/moulberry/notenoughupdates/listener/ChatListener.java index 633cd80f..ebb537dc 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/listener/ChatListener.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/ChatListener.java @@ -209,7 +209,7 @@ public class ChatListener { } if (unformatted.startsWith("You found ") && SBInfo.getInstance().getLocation() != null && SBInfo.getInstance().getLocation().equals("crystal_hollows")) { - CrystalMetalDetectorSolver.reset(true); + CrystalMetalDetectorSolver.resetSolution(true); } if (unformatted.startsWith("[NPC] Keeper of ") | unformatted.startsWith("[NPC] Professor Robot: ") || unformatted.startsWith(" ") || unformatted.startsWith("✦") || unformatted.equals( diff --git a/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java b/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java index ffebdd6b..eeb80abd 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java @@ -140,7 +140,7 @@ public class NEUEventListener { @SubscribeEvent public void onWorldLoad(WorldEvent.Unload event) { NotEnoughUpdates.INSTANCE.saveConfig(); - CrystalMetalDetectorSolver.reset(false); + CrystalMetalDetectorSolver.initWorld(); } @SubscribeEvent diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java index 4586d190..27c334ad 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java @@ -2,31 +2,119 @@ package io.github.moulberry.notenoughupdates.miscfeatures; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.core.util.render.RenderUtils; +import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; +import io.github.moulberry.notenoughupdates.util.NEUDebugLogger; import io.github.moulberry.notenoughupdates.util.SBInfo; import net.minecraft.client.Minecraft; -import net.minecraft.util.*; +import net.minecraft.entity.item.EntityArmorStand; +import net.minecraft.util.BlockPos; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.Vec3; +import net.minecraft.util.Vec3i; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.stream.Collectors; public class CrystalMetalDetectorSolver { private static final Minecraft mc = Minecraft.getMinecraft(); + private static BlockPos prevPlayerPos; private static double prevDistToTreasure = 0; - private static List<BlockPos> possibleBlocks = new ArrayList<>(); - private static final List<BlockPos> locations = new ArrayList<>(); - + private static HashSet<BlockPos> possibleBlocks = new HashSet<>(); + private static final HashMap<BlockPos, Double> evaluatedPlayerPositions = new HashMap<>(); + private static BlockPos blockPosIfLastSolutionInvalid; private static Boolean chestRecentlyFound = false; private static long chestLastFoundMillis = 0; + private static final HashSet<BlockPos> openedChestPositions = new HashSet<>(); + + // Keeper and Mines of Divan center location info + private static Vec3i minesCenter; + private static boolean visitKeeperMessagePrinted = false; + private static String KEEPER_OF_STRING = "Keeper of "; + private static String DIAMOND_STRING = "diamond"; + private static String LAPIS_STRING = "lapis"; + private static String EMERALD_STRING = "emerald"; + private static String GOLD_STRING = "gold"; + private static final HashMap<String, Vec3i> keeperOffsets = new HashMap<String, Vec3i>() {{ + put(DIAMOND_STRING, new Vec3i(33,0,3)); + put(LAPIS_STRING, new Vec3i(-33,0,-3)); + put(EMERALD_STRING, new Vec3i(-3,0,33)); + put(GOLD_STRING, new Vec3i(3,0,-33)); + }}; + + // Chest offsets from center + private static final HashSet<Long> knownChestOffsets = new HashSet<>(Arrays.asList( + -10171958951910L, // x=-38, y=-22, z=26 + 10718829084646L, // x=38, y=-22, z=-26 + -10721714765806L, // x=-40, y=-22, z=18 + -10996458455018L, // x=-41, y=-20, z=22 + -1100920913904L, // x=-5, y=-21, z=16 + 11268584898530L, // x=40, y=-22, z=-30 + -11271269253148L, // x=-42, y=-20, z=-28 + -11546281377832L, // x=-43, y=-22, z=-40 + 11818542038999L, // x=42, y=-19, z=-41 + 12093285728240L, // x=43, y=-21, z=-16 + -1409286164L, // x=-1, y=-22, z=-20 + 1922736062492L, // x=6, y=-21, z=28 + 2197613969419L, // x=7, y=-21, z=11 + 2197613969430L, // x=7, y=-21, z=22 + -3024999153708L, // x=-12, y=-21, z=-44 + 3571936395295L, // x=12, y=-22, z=31 + 3572003504106L, // x=12, y=-22, z=-22 + 3572003504135L, // x=12, y=-21, z=7 + 3572070612949L, // x=12, y=-21, z=-43 + -3574822076373L, // x=-14, y=-21, z=43 + -3574822076394L, // x=-14, y=-21, z=22 + -4399455797228L, // x=-17, y=-21, z=20 + -5224156626944L, // x=-20, y=-22, z=0 + 548346527764L, // x=1, y=-21, z=20 + 5496081743901L, // x=19, y=-22, z=29 + 5770959650816L, // x=20, y=-22, z=0 + 5771093868518L, // x=20, y=-21, z=-26 + -6048790347736L, // x=-23, y=-22, z=40 + 6320849682418L, // x=22, y=-21, z=-14 + -6323668254708L, // x=-24, y=-22, z=12 + 6595593371674L, // x=23, y=-22, z=26 + 6595660480473L, // x=23, y=-22, z=-39 + 6870471278619L, // x=24, y=-22, z=27 + 7145349185553L, // x=25, y=-22, z=17 + 8244995030996L, // x=29, y=-21, z=-44 + -8247679385612L, // x=-31, y=-21, z=-12 + -8247679385640L, // x=-31, y=-21, z=-40 + 8519872937959L, // x=30, y=-21, z=-25 + -8522557292584L, // x=-32, y=-21, z=-40 + -9622068920278L, // x=-36, y=-20, z=42 + -9896946827278L, // x=-37, y=-21, z=-14 + -9896946827286L // x=-37, y=-21, z=-22 + )); public static void process(IChatComponent message) { + if (SBInfo.getInstance().getLocation() == null || + !NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled || + !SBInfo.getInstance().getLocation().equals("crystal_hollows") || + !message.getUnformattedText().contains("TREASURE: ")) { + return; + } + + locateMinesCenterIfNeeded(); + + double distToTreasure = Double.parseDouble(message + .getUnformattedText() + .split("TREASURE: ")[1].split("m")[0].replaceAll("(?!\\.)\\D", "")); + // Delay to keep old chest location from being treated as the new chest location if (chestRecentlyFound) { long currentTimeMillis = System.currentTimeMillis(); if (chestLastFoundMillis == 0) { chestLastFoundMillis = currentTimeMillis; return; - } else if (currentTimeMillis - chestLastFoundMillis < 1000) { + } else if (currentTimeMillis - chestLastFoundMillis < 1000 && distToTreasure < 5.0) { return; } @@ -34,80 +122,239 @@ public class CrystalMetalDetectorSolver { chestRecentlyFound = false; } - if (SBInfo.getInstance().getLocation() != null && SBInfo.getInstance().getLocation().equals("crystal_hollows") - && message.getUnformattedText().contains("TREASURE: ")) { - double distToTreasure = Double.parseDouble(message - .getUnformattedText() - .split("TREASURE: ")[1].split("m")[0].replaceAll("(?!\\.)\\D", "")); - if (NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled && prevDistToTreasure == distToTreasure && - prevPlayerPos.getX() == mc.thePlayer.getPosition().getX() && - prevPlayerPos.getY() == mc.thePlayer.getPosition().getY() && - prevPlayerPos.getZ() == mc.thePlayer.getPosition().getZ() && !locations.contains(mc.thePlayer.getPosition())) { - if (possibleBlocks.size() == 0) { - locations.add(mc.thePlayer.getPosition()); - for (int zOffset = (int) Math.floor(-distToTreasure); zOffset <= Math.ceil(distToTreasure); zOffset++) { - for (int y = 65; y <= 75; y++) { - double calculatedDist = 0; - int xOffset = 0; - while (calculatedDist < distToTreasure) { - BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) + xOffset, - y, Math.floor(mc.thePlayer.posZ) + zOffset - ); - calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); - if (round(calculatedDist, 1) == distToTreasure && !possibleBlocks.contains(pos) && - treasureAllowed(pos) && mc.theWorld. - getBlockState(pos.add(0, 1, 0)).getBlock().getRegistryName().equals("minecraft:air")) { - possibleBlocks.add(pos); - } - xOffset++; - } - xOffset = 0; - calculatedDist = 0; - while (calculatedDist < distToTreasure) { - BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) - xOffset, - y, Math.floor(mc.thePlayer.posZ) + zOffset - ); - calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); - if (round(calculatedDist, 1) == distToTreasure && !possibleBlocks.contains(pos) && - treasureAllowed(pos) && mc.theWorld. - getBlockState(pos.add(0, 1, 0)).getBlock().getRegistryName().equals("minecraft:air")) { - possibleBlocks.add(pos); - } - xOffset++; + if (prevDistToTreasure == distToTreasure && + prevPlayerPos.equals(mc.thePlayer.getPosition()) && + !evaluatedPlayerPositions.keySet().contains(mc.thePlayer.getPosition())) { + if (possibleBlocks.size() == 0) { + evaluatedPlayerPositions.put(mc.thePlayer.getPosition(), distToTreasure); + for (int zOffset = (int) Math.floor(-distToTreasure); zOffset <= Math.ceil(distToTreasure); zOffset++) { + for (int y = 65; y <= 75; y++) { + double calculatedDist = 0; + int xOffset = 0; + while (calculatedDist < distToTreasure) { + BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) + xOffset, + y, Math.floor(mc.thePlayer.posZ) + zOffset); + calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); + if (round(calculatedDist, 1) == distToTreasure && treasureAllowed(pos)) { + possibleBlocks.add(pos); } + xOffset++; } - } - sendMessage(); - } else if (possibleBlocks.size() != 1) { - locations.add(mc.thePlayer.getPosition()); - List<BlockPos> temp = new ArrayList<>(); - for (BlockPos pos : possibleBlocks) { - if (round(getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)), 1) == distToTreasure) { - temp.add(pos); + xOffset = 0; + calculatedDist = 0; + while (calculatedDist < distToTreasure) { + BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) - xOffset, + y, Math.floor(mc.thePlayer.posZ) + zOffset); + calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); + if (round(calculatedDist, 1) == distToTreasure && treasureAllowed(pos)) { + possibleBlocks.add(pos); + } + xOffset++; } } - possibleBlocks = temp; - sendMessage(); - } else { - BlockPos pos = possibleBlocks.get(0); - if (Math.abs(distToTreasure - (getPlayerPos().distanceTo(new Vec3(pos)))) > 5) { - mc.thePlayer.addChatMessage(new ChatComponentText( - EnumChatFormatting.RED + "[NEU] Previous solution is invalid.")); - reset(false); + } + + checkAndDisplaySolutionState(); + } else if (possibleBlocks.size() != 1) { + evaluatedPlayerPositions.put(mc.thePlayer.getPosition().getImmutable(), distToTreasure); + HashSet<BlockPos> temp = new HashSet<>(); + for (BlockPos pos : possibleBlocks) { + if (round(getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)), 1) == distToTreasure) { + temp.add(pos); } } + possibleBlocks = temp; + checkAndDisplaySolutionState(); + } else { + BlockPos pos = possibleBlocks.iterator().next(); + if (Math.abs(distToTreasure - (getPlayerPos().distanceTo(new Vec3(pos)))) > 5) { + mc.thePlayer.addChatMessage(new ChatComponentText( + EnumChatFormatting.RED + "[NEU] Previous solution is invalid.")); + blockPosIfLastSolutionInvalid = pos.getImmutable(); + logDiagnosticData(false); + resetSolution(false); + } } + } + + prevPlayerPos = mc.thePlayer.getPosition().getImmutable(); + prevDistToTreasure = distToTreasure; + } + + private static void checkForSingleKnownLocationMatch() { + if (minesCenter == BlockPos.NULL_VECTOR || possibleBlocks.size() < 2) { + return; + } - prevPlayerPos = mc.thePlayer.getPosition(); - prevDistToTreasure = distToTreasure; + HashSet<BlockPos> temp = possibleBlocks.stream() + .filter(block -> knownChestOffsets.contains(block.subtract(minesCenter).toLong())) + .collect(Collectors.toCollection(HashSet::new)); + if (temp.size() == 1) { + possibleBlocks = temp; + NEUDebugLogger.log(NEUDebugFlag.METAL, "Known location identified."); + } else if (temp.size() > 1) { + NEUDebugLogger.log(NEUDebugFlag.METAL, temp.size() + " known locations identified:"); } } - public static void reset(Boolean chestFound) { + private static String getFriendlyBlockPositions(Collection<BlockPos> positions) { + if (!NEUDebugLogger.isFlagEnabled(NEUDebugFlag.METAL) || positions.size() == 0) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + for (BlockPos blockPos : positions) { + sb.append("Absolute: " + blockPos.toString()); + if (minesCenter != Vec3i.NULL_VECTOR) { + BlockPos relativeOffset = blockPos.subtract(minesCenter); + sb.append(", Relative: " + relativeOffset.toString() + " (" + relativeOffset.toLong() + ")"); + } + sb.append("\n"); + } + + return sb.toString(); + } + + private static String getFriendlyEvaluatedPositions(HashMap<BlockPos, Double> positions) { + if (!NEUDebugLogger.isFlagEnabled(NEUDebugFlag.METAL) || positions.size() == 0) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + for (BlockPos blockPos : positions.keySet()) { + sb.append("Absolute: " + blockPos.toString()); + if (minesCenter != Vec3i.NULL_VECTOR) { + BlockPos relativeOffset = blockPos.subtract(minesCenter); + sb.append(", Relative: " + relativeOffset.toString() + " (" + relativeOffset.toLong() + ")"); + } + + sb.append(" Distance: "); + sb.append(positions.get(blockPos)); + + sb.append("\n"); + } + + return sb.toString(); + } + + public static void logDiagnosticData(boolean outputAlways) { + if (SBInfo.getInstance().getLocation() == null) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] This command is not available outside SkyBlock")); + return; + } + + if (!NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled) + { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Metal Detector Solver is not enabled.")); + return; + } + + if (!outputAlways && !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.METAL)) { + return; + } + + boolean originalDebugFlag = !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.add(NEUDebugFlag.METAL); + + StringBuilder diagsMessage = new StringBuilder(); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Mines Center: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((minesCenter == Vec3i.NULL_VECTOR) ? "<NOT DISCOVERED>" : minesCenter.toString()); + diagsMessage.append("\n"); + + diagsMessage.append("\n"); + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Previous Player Position: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((prevPlayerPos == null) ? "<NONE>" : prevPlayerPos.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Previous Distance To Treasure: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((prevDistToTreasure == 0) ? "<NONE>" : prevDistToTreasure); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Last Solution Invalid Position: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((blockPosIfLastSolutionInvalid == null) ? "<NONE>" : blockPosIfLastSolutionInvalid.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Current Possible Blocks: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(possibleBlocks.size()); + diagsMessage.append(getFriendlyBlockPositions(possibleBlocks)); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Evaluated player positions: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(evaluatedPlayerPositions.size()); + diagsMessage.append(getFriendlyEvaluatedPositions(evaluatedPlayerPositions)); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Chest locations not on known list:\n"); + diagsMessage.append(EnumChatFormatting.WHITE); + if (minesCenter != Vec3i.NULL_VECTOR) { + HashSet<BlockPos> locationsNotOnKnownList = openedChestPositions + .stream() + .filter(block -> !knownChestOffsets.contains(block.subtract(minesCenter).toLong())) + .map(block -> block.subtract(minesCenter)) + .collect(Collectors.toCollection(HashSet::new)); + if (locationsNotOnKnownList.size() > 0) { + for (BlockPos blockPos : locationsNotOnKnownList) { + diagsMessage.append(String.format( + "%dL,\t\t// x=%d, y=%d, z=%d", + blockPos.toLong(), + blockPos.getX(), + blockPos.getY(), + blockPos.getZ() + )); + } + } + } else { + diagsMessage.append("<REQUIRES MINES CENTER>"); + } + + NEUDebugLogger.log(NEUDebugFlag.METAL, diagsMessage.toString()); + + if (!originalDebugFlag) { + NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.remove(NEUDebugFlag.METAL); + } + } + + public static void resetSolution(Boolean chestFound) { + if (chestFound) { + blockPosIfLastSolutionInvalid = null; + prevPlayerPos = null; + prevDistToTreasure = 0; + if (possibleBlocks.size() == 1) { + openedChestPositions.add(possibleBlocks.iterator().next().getImmutable()); + } + } + chestRecentlyFound = chestFound; possibleBlocks.clear(); - locations.clear(); + evaluatedPlayerPositions.clear(); + } + + public static void initWorld() { + minesCenter = Vec3i.NULL_VECTOR; + visitKeeperMessagePrinted = false; + blockPosIfLastSolutionInvalid = null; + openedChestPositions.clear(); + prevDistToTreasure = 0; + prevPlayerPos = null; + resetSolution(false); } public static void render(float partialTicks) { @@ -117,10 +364,10 @@ public class CrystalMetalDetectorSolver { SBInfo.getInstance().location.equals("Mines of Divan")) { if (possibleBlocks.size() == 1) { - BlockPos block = possibleBlocks.get(0); + BlockPos block = possibleBlocks.iterator().next(); RenderUtils.renderBeaconBeam(block.add(0, 1, 0), beaconRGB, 1.0f, partialTicks); - RenderUtils.renderWayPoint("Treasure", possibleBlocks.get(0).add(0, 2.5, 0), partialTicks); + RenderUtils.renderWayPoint("Treasure", possibleBlocks.iterator().next().add(0, 2.5, 0), partialTicks); } else if (possibleBlocks.size() > 1 && NotEnoughUpdates.INSTANCE.config.mining.metalDetectorShowPossible) { for (BlockPos block : possibleBlocks) { RenderUtils.renderBeaconBeam(block.add(0, 1, 0), beaconRGB, 1.0f, partialTicks); @@ -130,19 +377,55 @@ public class CrystalMetalDetectorSolver { } } + private static void locateMinesCenterIfNeeded() { + if (minesCenter != Vec3i.NULL_VECTOR) { + return; + } + + List<EntityArmorStand> keeperEntities = mc.theWorld.getEntities(EntityArmorStand.class, (entity) -> { + if (!entity.hasCustomName()) return false; + if (entity.getCustomNameTag().contains(KEEPER_OF_STRING)) return true; + return false; + }); + + if (keeperEntities.size() == 0) { + if (!visitKeeperMessagePrinted) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] Approach a Keeper while holding the metal detector to enable faster treasure hunting.")); + visitKeeperMessagePrinted = true; + } + return; + } + + EntityArmorStand keeperEntity = keeperEntities.get(0); + String keeperName = keeperEntity.getCustomNameTag(); + NEUDebugLogger.log(NEUDebugFlag.METAL,"Locating center using Keeper: " + + EnumChatFormatting.WHITE + keeperEntity); + String keeperType = keeperName.substring(keeperName.indexOf(KEEPER_OF_STRING) + KEEPER_OF_STRING.length()); + minesCenter = keeperEntity.getPosition().add(keeperOffsets.get(keeperType.toLowerCase())); + NEUDebugLogger.log(NEUDebugFlag.METAL,"Mines center: " + + EnumChatFormatting.WHITE + minesCenter.toString()); + mc.thePlayer.addChatMessage(new ChatComponentText( + EnumChatFormatting.YELLOW + "[NEU] Faster treasure hunting is now enabled based on Keeper location.")); + } + private static double round(double value, int precision) { int scale = (int) Math.pow(10, precision); return (double) Math.round(value * scale) / scale; } - private static void sendMessage() { + private static void checkAndDisplaySolutionState() { + if (possibleBlocks.size() == 0) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[NEU] Failed to find a solution.")); + logDiagnosticData(false); + resetSolution(false); + return; + } + + checkForSingleKnownLocationMatch(); if (possibleBlocks.size() > 1) { - mc.thePlayer.addChatMessage(new ChatComponentText( - EnumChatFormatting.YELLOW + "[NEU] Need another position to find solution. Possible blocks: " - + possibleBlocks.size())); - } else if (possibleBlocks.size() == 0) { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[NEU] Failed to find solution.")); - reset(false); + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] Need another position to find solution. Possible blocks: " + possibleBlocks.size())); } else { mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + "[NEU] Found solution.")); } @@ -157,12 +440,16 @@ public class CrystalMetalDetectorSolver { } private static boolean treasureAllowed(BlockPos pos) { - return mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:gold_block") || + boolean airAbove = mc.theWorld. + getBlockState(pos.add(0, 1, 0)).getBlock().getRegistryName().equals("minecraft:air"); + boolean allowedBlockType = mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:gold_block") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:prismarine") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:chest") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:stained_glass") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:stained_glass_pane") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:wool") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:stained_hardened_clay"); + boolean knownOffset = knownChestOffsets.contains(pos.subtract(minesCenter).toLong()); + return airAbove & (knownOffset | allowedBlockType); } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java new file mode 100644 index 00000000..25f39c3a --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java @@ -0,0 +1,329 @@ +package io.github.moulberry.notenoughupdates.miscfeatures; + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.core.util.Line; +import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; +import io.github.moulberry.notenoughupdates.util.NEUDebugLogger; +import io.github.moulberry.notenoughupdates.util.SBInfo; +import net.minecraft.client.Minecraft; +import net.minecraft.event.ClickEvent; +import net.minecraft.event.HoverEvent; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.BlockPos; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.Vec3; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +public class CrystalWishingCompassSolver { + private static final CrystalWishingCompassSolver INSTANCE = new CrystalWishingCompassSolver(); + public static CrystalWishingCompassSolver getInstance() { + return INSTANCE; + } + + private static final Minecraft mc = Minecraft.getMinecraft(); + private static boolean isSkytilsPresent = false; + + // Crystal Nucleus unbreakable blocks, area coordinates reported by Hypixel server are slightly different + private static final AxisAlignedBB NUCLEUS_BB = new AxisAlignedBB(463, 63, 460, 563, 181, 564); + private static final double MAX_COMPASS_PARTICLE_SPREAD = 16; + + private static BlockPos prevPlayerPos; + private long compassUsedMillis = 0; + private Vec3 firstParticle = null; + private Vec3 lastParticle = null; + private double lastParticleDistanceFromFirst = 0; + private Line firstCompassLine = null; + private Line secondCompassLine = null; + private Vec3 solution = null; + private Line solutionIntersectionLine = null; + + private void resetForNewCompass() { + compassUsedMillis = 0; + firstParticle = null; + lastParticle = null; + lastParticleDistanceFromFirst = 0; + } + + private void resetForNewTarget() { + NEUDebugLogger.log(NEUDebugFlag.WISHING,"Resetting for new target"); + resetForNewCompass(); + firstCompassLine = null; + secondCompassLine = null; + solutionIntersectionLine = null; + prevPlayerPos = null; + solution = null; + } + + @SubscribeEvent + public void onWorldLoad(WorldEvent.Unload event) { + resetForNewTarget(); + isSkytilsPresent = Loader.isModLoaded("skytils"); + } + + @SubscribeEvent + public void onPlayerInteract(PlayerInteractEvent event) { + if (!NotEnoughUpdates.INSTANCE.config.mining.wishingCompassSolver || + SBInfo.getInstance().getLocation() == null || + !SBInfo.getInstance().getLocation().equals("crystal_hollows") || + event.entityPlayer != mc.thePlayer || + (event.action != PlayerInteractEvent.Action.RIGHT_CLICK_AIR && + event.action != PlayerInteractEvent.Action.RIGHT_CLICK_BLOCK) + ) { + return; + } + + ItemStack heldItem = event.entityPlayer.getHeldItem(); + if (heldItem == null || heldItem.getItem() != Items.skull) { + return; + } + + String heldInternalName = NotEnoughUpdates.INSTANCE.manager.getInternalNameForItem(heldItem); + if (heldInternalName == null || !heldInternalName.equals("WISHING_COMPASS")) { + return; + } + + try { + if (isSolved()) { + resetForNewTarget(); + } + + // 64.0 is an arbitrary value but seems to work well + if (prevPlayerPos != null && prevPlayerPos.distanceSq(mc.thePlayer.getPosition()) < 64.0) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] Move a little further before using the wishing compass again.")); + event.setCanceled(true); + return; + } + + prevPlayerPos = mc.thePlayer.getPosition().getImmutable(); + compassUsedMillis = System.currentTimeMillis(); + } catch (Exception e) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Error processing wishing compass action - see log for details")); + e.printStackTrace(); + } + } + + /* + * Processes particles if the wishing compass was used within the last 5 seconds. + * + * The first and the last particles are used to create a line for each wishing compass + * use that is then used to calculate the target. + * + * Once two lines have been calculated, the shortest line between the two is calculated + * with the midpoint on that line being the wishing compass target. The accuracy of this + * seems to be very high. + * + * The target location varies based on various criteria, including, but not limited to: + * Topaz Crystal (Khazad-dûm) Magma Fields + * Odawa (Jungle Village) Jungle w/no Jungle Key in inventory + * Amethyst Crystal (Jungle Temple) Jungle w/Jungle Key in inventory + * Sapphire Crystal (Lost Precursor City) Precursor Remnants + * Jade Crystal (Mines of Divan) Mithril Deposits + * King Yolkar Goblin Holdout without "King's Scent I" effect + * Goblin Queen Goblin Holdout with "King's Scent I" effect + * Crystal Nucleus All Crystals found and none placed + * per-area structure missing, or because Hypixel. + * Always within 1 block of X=513 Y=106 Z=551. + */ + public void onSpawnParticle( + EnumParticleTypes particleType, + double x, + double y, + double z + ) { + if (!NotEnoughUpdates.INSTANCE.config.mining.wishingCompassSolver || + particleType != EnumParticleTypes.VILLAGER_HAPPY || + !SBInfo.getInstance().getLocation().equals("crystal_hollows") || + isSolved() || + System.currentTimeMillis() - compassUsedMillis > 5000) { + return; + } + + try { + Vec3 particleVec = new Vec3(x, y, z); + if (firstParticle == null) { + firstParticle = particleVec; + return; + } + + double distanceFromFirst = particleVec.distanceTo(firstParticle); + if (distanceFromFirst > MAX_COMPASS_PARTICLE_SPREAD) { + return; + } + + if (distanceFromFirst >= lastParticleDistanceFromFirst) { + lastParticleDistanceFromFirst = distanceFromFirst; + lastParticle = particleVec; + return; + } + + // We get here when the second repetition of particles begins. + // Since the second repetition overlaps with the last few particles + // of the first repetition, the last particle we capture isn't truly the last. + // But that's OK since subsequent particles will be on the same line. + Line line = new Line(firstParticle, lastParticle); + if (firstCompassLine == null) { + firstCompassLine = line; + resetForNewCompass(); + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] Need another position to determine wishing compass target.")); + return; + } + + secondCompassLine = line; + solutionIntersectionLine = firstCompassLine.getIntersectionLineSegment(secondCompassLine); + if (solutionIntersectionLine == null) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Unable to determine wishing compass target.")); + logDiagnosticData(false); + return; + } + + solution = solutionIntersectionLine.getMidpoint(); + + if (solution.distanceTo(firstParticle) < 8) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] WARNING: Solution is likely incorrect.")); + logDiagnosticData(false); + return; + } + + showSolution(); + } catch (Exception e) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Exception while calculating wishing compass solution - see log for details")); + e.printStackTrace(); + } + } + + private boolean isSolved() { + return solution != null; + } + + private void showSolution() { + if (solution == null) return; + String description = "[NEU] Wishing compass target: "; + String coordsText = String.format("%.0f %.0f %.0f", + solution.xCoord, + solution.yCoord, + solution.zCoord); + + if (NUCLEUS_BB.isVecInside(solution)) { + description += "Crystal Nucleus (" + coordsText + ")"; + } else { + description += coordsText; + } + + ChatComponentText message = new ChatComponentText(EnumChatFormatting.YELLOW + description); + + if (isSkytilsPresent) { + ChatStyle clickEvent = new ChatStyle().setChatClickEvent( + new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/sthw add WishingTarget " + coordsText)); + clickEvent.setChatHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ChatComponentText( + EnumChatFormatting.YELLOW + + "Add Skytils hollows waypoint"))); + message.setChatStyle(clickEvent); + } + + Minecraft.getMinecraft().thePlayer.addChatMessage(message); + } + + public void logDiagnosticData(boolean outputAlways) { + if (SBInfo.getInstance().getLocation() == null) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] This command is not available outside SkyBlock")); + return; + } + + if (!NotEnoughUpdates.INSTANCE.config.mining.wishingCompassSolver) + { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Wishing Compass Solver is not enabled.")); + return; + } + + if (!outputAlways && !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.WISHING)) { + return; + } + + boolean originalDebugFlag = !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.add(NEUDebugFlag.WISHING); + + StringBuilder diagsMessage = new StringBuilder(); + + diagsMessage.append("\n"); + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Skytils Present: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(isSkytilsPresent); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Compass Used Millis: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(compassUsedMillis); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Compass Used Position: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((prevPlayerPos == null) ? "<NONE>" : prevPlayerPos.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("First Particle: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((firstParticle == null) ? "<NONE>" : firstParticle.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Last Particle: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((lastParticle == null) ? "<NONE>" : lastParticle.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Last Particle Distance From First: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(lastParticleDistanceFromFirst); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("First Compass Line: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((firstCompassLine == null) ? "<NONE>" : firstCompassLine.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Second Compass Line: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((secondCompassLine == null) ? "<NONE>" : secondCompassLine.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Intersection Line: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((secondCompassLine == null) ? "<NONE>" : solutionIntersectionLine); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Solution: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((solution == null) ? "<NONE>" : solution.toString()); + diagsMessage.append("\n"); + + NEUDebugLogger.log(NEUDebugFlag.WISHING, diagsMessage.toString()); + + if (!originalDebugFlag) { + NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.remove(NEUDebugFlag.WISHING); + } + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinNetHandlerPlayClient.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinNetHandlerPlayClient.java index 4c2a0485..f7baffd6 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinNetHandlerPlayClient.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinNetHandlerPlayClient.java @@ -47,6 +47,11 @@ public class MixinNetHandlerPlayClient { double xCoord, double yCoord, double zCoord, double xOffset, double yOffset, double zOffset, int[] params ) { + CrystalWishingCompassSolver.getInstance().onSpawnParticle(particleTypes, + xCoord, + yCoord, + zCoord + ); boolean override = FishingHelper.getInstance().onSpawnParticle( particleTypes, xCoord, diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java index 470b0381..1d03a2d1 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java @@ -11,8 +11,35 @@ import io.github.moulberry.notenoughupdates.core.config.gui.GuiPositionEditor; import io.github.moulberry.notenoughupdates.miscgui.GuiEnchantColour; import io.github.moulberry.notenoughupdates.miscgui.GuiInvButtonEditor; import io.github.moulberry.notenoughupdates.miscgui.NEUOverlayPlacements; -import io.github.moulberry.notenoughupdates.options.seperateSections.*; +import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; +import io.github.moulberry.notenoughupdates.options.seperateSections.AccessoryBag; +import io.github.moulberry.notenoughupdates.options.seperateSections.AHGraph; +import io.github.moulberry.notenoughupdates.options.seperateSections.AHTweaks; +import io.github.moulberry.notenoughupdates.options.seperateSections.ApiKey; +import io.github.moulberry.notenoughupdates.options.seperateSections.Calendar; import io.github.moulberry.notenoughupdates.options.seperateSections.CustomArmour; +import io.github.moulberry.notenoughupdates.options.seperateSections.Dungeons; +import io.github.moulberry.notenoughupdates.options.seperateSections.DungeonMapConfig; +import io.github.moulberry.notenoughupdates.options.seperateSections.Enchanting; +import io.github.moulberry.notenoughupdates.options.seperateSections.Fishing; +import io.github.moulberry.notenoughupdates.options.seperateSections.ImprovedSBMenu; +import io.github.moulberry.notenoughupdates.options.seperateSections.InventoryButtons; +import io.github.moulberry.notenoughupdates.options.seperateSections.Itemlist; +import io.github.moulberry.notenoughupdates.options.seperateSections.ItemOverlays; +import io.github.moulberry.notenoughupdates.options.seperateSections.LocationEdit; +import io.github.moulberry.notenoughupdates.options.seperateSections.Mining; +import io.github.moulberry.notenoughupdates.options.seperateSections.Misc; +import io.github.moulberry.notenoughupdates.options.seperateSections.MiscOverlays; +import io.github.moulberry.notenoughupdates.options.seperateSections.NeuAuctionHouse; +import io.github.moulberry.notenoughupdates.options.seperateSections.Notifications; +import io.github.moulberry.notenoughupdates.options.seperateSections.PetOverlay; +import io.github.moulberry.notenoughupdates.options.seperateSections.SlayerOverlay; +import io.github.moulberry.notenoughupdates.options.seperateSections.SlotLocking; +import io.github.moulberry.notenoughupdates.options.seperateSections.SkillOverlays; +import io.github.moulberry.notenoughupdates.options.seperateSections.StorageGUI; +import io.github.moulberry.notenoughupdates.options.seperateSections.Toolbar; +import io.github.moulberry.notenoughupdates.options.seperateSections.TooltipTweaks; +import io.github.moulberry.notenoughupdates.options.seperateSections.TradeMenu; import io.github.moulberry.notenoughupdates.overlays.MiningOverlay; import io.github.moulberry.notenoughupdates.overlays.OverlayManager; import io.github.moulberry.notenoughupdates.overlays.TextOverlay; @@ -22,6 +49,7 @@ import net.minecraftforge.client.ClientCommandHandler; import org.lwjgl.util.vector.Vector2f; import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -319,6 +347,8 @@ public class NEUConfig extends Config { public List<NEUConfig.InventoryButton> inventoryButtons = createDefaultInventoryButtons(); @Expose + public EnumSet<NEUDebugFlag> debugFlags = EnumSet.noneOf(NEUDebugFlag.class); + @Expose public boolean enableItemEditing = false; @Expose public boolean cacheRenderedItempane = true; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java b/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java new file mode 100644 index 00000000..5b3188d6 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java @@ -0,0 +1,12 @@ +package io.github.moulberry.notenoughupdates.options.customtypes; + +public enum NEUDebugFlag { + // NOTE: Removing enum values causes gson to remove all debugFlags on load if any removed value is present + METAL, + WISHING, + ; + + public static final String FLAG_LIST = + "METAL - Metal Detector Solver\n" + + "WISHING - Wishing Compass Solver"; +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Mining.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Mining.java index 6ebd7f42..3af3347a 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Mining.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Mining.java @@ -665,6 +665,14 @@ public class Mining { @ConfigEditorBoolean
public boolean titaniumAlertMustBeVisible = false;
+ @Expose
+ @ConfigOption(
+ name = "Wishing Compass Solver",
+ desc = "Show wishing compass target coordinates based on two samples"
+ )
+ @ConfigEditorBoolean
+ public boolean wishingCompassSolver = true;
+
@ConfigOption(
name = "Custom Textures",
desc = ""
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/overlays/CrystalHollowOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/overlays/CrystalHollowOverlay.java index 529f132a..ea190323 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/overlays/CrystalHollowOverlay.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/overlays/CrystalHollowOverlay.java @@ -7,7 +7,10 @@ import io.github.moulberry.notenoughupdates.options.NEUConfig; import io.github.moulberry.notenoughupdates.util.SBInfo; import io.github.moulberry.notenoughupdates.util.Utils; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.inventory.GuiChest; import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.inventory.ContainerChest; +import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumChatFormatting; import org.lwjgl.util.vector.Vector2f; @@ -49,6 +52,57 @@ public class CrystalHollowOverlay extends TextOverlay { super(position, dummyStrings, styleSupplier); } + private final Pattern hotmCrystalNotFoundPattern = Pattern.compile("(?<crystal>[a-zA-Z]+) \\u2716 Not Found"); + private final Pattern hotmCrystalNotPlacedPattern = Pattern.compile("(?<crystal>[a-zA-Z]+) \\u2716 Not Placed"); + private final Pattern hotmCrystalPlacedPattern = Pattern.compile("(?<crystal>[a-zA-Z]+) \\u2714 Placed"); + + private void updateHotmCrystalState(IInventory lower) { + NEUConfig.HiddenProfileSpecific perProfileConfig = NotEnoughUpdates.INSTANCE.config.getProfileSpecific(); + if (perProfileConfig == null) return; + + ItemStack crystalStateStack = lower.getStackInSlot(50); + if (crystalStateStack == null || !crystalStateStack.hasTagCompound()) { + return; + } + + String name = Utils.cleanColour(crystalStateStack.getDisplayName()).trim(); + if (!name.equals("Crystal Hollows Crystals")) { + return; + } + + String[] lore = NotEnoughUpdates.INSTANCE.manager.getLoreFromNBT(crystalStateStack.getTagCompound()); + for (String line : lore) { + if (line == null) { + continue; + } + String cleanLine = Utils.cleanColour(line).trim(); + Matcher hotmCrystalNotPlacedMatcher = hotmCrystalNotPlacedPattern.matcher(cleanLine); + Matcher hotmCrystalNotFoundMatcher = hotmCrystalNotFoundPattern.matcher(cleanLine); + Matcher hotmCrystalPlacedMatcher = hotmCrystalPlacedPattern.matcher(cleanLine); + if (hotmCrystalNotFoundMatcher.matches() && perProfileConfig.crystals.containsKey(hotmCrystalNotFoundMatcher.group("crystal"))) { + perProfileConfig.crystals.put(hotmCrystalNotFoundMatcher.group("crystal"), 0); + } else if (hotmCrystalNotPlacedMatcher.matches() && perProfileConfig.crystals.containsKey(hotmCrystalNotPlacedMatcher.group("crystal"))) { + perProfileConfig.crystals.put(hotmCrystalNotPlacedMatcher.group("crystal"), 1); + } else if (hotmCrystalPlacedMatcher.matches() && perProfileConfig.crystals.containsKey(hotmCrystalPlacedMatcher.group("crystal"))) { + perProfileConfig.crystals.put(hotmCrystalPlacedMatcher.group("crystal"), 2); + } + } + } + + @Override + public void updateFrequent() { + if (Minecraft.getMinecraft().currentScreen instanceof GuiChest) { + GuiChest chest = (GuiChest) Minecraft.getMinecraft().currentScreen; + ContainerChest container = (ContainerChest) chest.inventorySlots; + IInventory lower = container.getLowerChestInventory(); + String containerName = lower.getDisplayName().getUnformattedText(); + + if (containerName.equals("Heart of the Mountain") && lower.getSizeInventory() >= 54) { + updateHotmCrystalState(lower); + } + } + } + @Override public void update() { overlayStrings = null; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/overlays/MiningOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/overlays/MiningOverlay.java index bfbe367f..96864391 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/overlays/MiningOverlay.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/overlays/MiningOverlay.java @@ -54,7 +54,7 @@ public class MiningOverlay extends TextOverlay { String containerName = lower.getDisplayName().getUnformattedText(); if (containerName.equals("Commissions") && lower.getSizeInventory() >= 27) { - UpdateCommissions(lower); + updateCommissions(lower); } else if (containerName.equals("Forge") && lower.getSizeInventory() >= 36) { updateForge(lower); } @@ -127,7 +127,7 @@ public class MiningOverlay extends TextOverlay { } } - private void UpdateCommissions(IInventory lower) { + private void updateCommissions(IInventory lower) { // Get the location (type) of the currently shown commissions ItemStack commTypeStack = lower.getStackInSlot(27); if (commTypeStack == null || !commTypeStack.hasTagCompound()) { @@ -202,93 +202,32 @@ public class MiningOverlay extends TextOverlay { "\\xA77Time Remaining: \\xA7a((?<Completed>Completed!)|(((?<days>[0-9]+)d)? ?((?<hours>[0-9]+)h)? ?((?<minutes>[0-9]+)m)? ?((?<seconds>[0-9]+)s)?))"); private static final Pattern timeRemainingTab = Pattern.compile( ".*[1-5]\\) (?<ItemName>.*): ((?<Ready>Ready!)|(((?<days>[0-9]+)d)? ?((?<hours>[0-9]+)h)? ?((?<minutes>[0-9]+)m)? ?((?<seconds>[0-9]+)s)?))"); + private static final Pattern forgesHeaderPattern = Pattern.compile( + "\\xa7r\\xa79\\xa7lForges \\xa7r(?:\\xa7f\\(\\+1 more\\)\\xa7r)?"); @Override public void update() { overlayStrings = null; - NEUConfig.HiddenProfileSpecific hidden = NotEnoughUpdates.INSTANCE.config.getProfileSpecific(); - - - /*if(Minecraft.getMinecraft().currentScreen instanceof GuiChest) { - GuiChest chest = (GuiChest) Minecraft.getMinecraft().currentScreen; - ContainerChest container = (ContainerChest) chest.inventorySlots; - String containerName = container.getLowerChestInventory().getDisplayName().getUnformattedText(); - - - long currentTime = System.currentTimeMillis(); - if(currentTime - lastSkymallSync > 60*1000) { - if(CapeManager.getInstance().lastJsonSync != null) { - JsonObject obj = CapeManager.getInstance().lastJsonSync; - if(obj.has("skymall") && obj.get("skymall").isJsonPrimitive()) { - activeSkymall = obj.get("skymall").getAsString(); - } - } - } - - if(containerName.equals("Heart of the Mountain") && container.getLowerChestInventory().getSizeInventory() > 10) { - System.out.println("HOTM Container"); - ItemStack stack = container.getLowerChestInventory().getStackInSlot(10); - if(stack != null && stack.getDisplayName().equals(GREEN+"Sky Mall")) { - NotEnoughUpdates.INSTANCE.config.hidden.skymallActive = false; - - String[] lines = NotEnoughUpdates.INSTANCE.manager.getLoreFromNBT(stack.getTagCompound()); - - for(String line : lines) { - if(line.equals("\u00a7aYour Current Effect")) { - System.out.println("Current effect"); - NotEnoughUpdates.INSTANCE.config.hidden.skymallActive = true; - } else if(NotEnoughUpdates.INSTANCE.config.hidden.skymallActive) { - String prevActiveSkymall = activeSkymall; - System.out.println("Setting"); - if(line.contains("Gain \u00a7a+100 \u00a76\u2E15 Mining Speed")) { - activeSkymall = "mining_speed"; - } else if(line.contains("Gain \u00a7a+50 \u00a76\u2618 Mining Fortune")) { - activeSkymall = "mining_fortune"; - } else if(line.contains("Gain \u00a7a+15% \u00a77Powder from mining")) { - activeSkymall = "powder"; - } else if(line.contains("Reduce Pickaxe Ability cooldown")) { - activeSkymall = "pickaxe_ability"; - } else if(line.contains("10x \u00a77chance to find Goblins")) { - activeSkymall = "goblin"; - } else if(line.contains("Gain \u00a7a5x \u00a79Titanium \u00a77drops")) { - activeSkymall = "titanium"; - } else { - System.out.println("Unknown"); - activeSkymall = "unknown"; - } - if(!activeSkymall.equals(prevActiveSkymall)) { - System.out.println("Maybe sending to server"); - if(currentTime - lastSkymallSync > 60*1000) { - lastSkymallSync = currentTime; - System.out.println("Sending to server"); - NotEnoughUpdates.INSTANCE.manager.hypixelApi.getMyApiAsync("skymall?"+activeSkymall, (jsonObject) -> { - System.out.println("Success!"); - }, () -> { - System.out.println("Error!"); - }); - } - } - break; - } - } - } - } - }*/ + NEUConfig.HiddenProfileSpecific profileConfig = NotEnoughUpdates.INSTANCE.config.getProfileSpecific(); if (!NotEnoughUpdates.INSTANCE.config.mining.dwarvenOverlay && NotEnoughUpdates.INSTANCE.config.mining.emissaryWaypoints == 0 && !NotEnoughUpdates.INSTANCE.config.mining.titaniumAlert && - NotEnoughUpdates.INSTANCE.config.mining.locWaypoints == 0) return; - //thanks to "Pure Genie#7250" for helping with this (makes tita alert and waypoints work without mine overlay) - if (SBInfo.getInstance().getLocation() == null) return; - if (SBInfo.getInstance().getLocation().equals("mining_3") || SBInfo.getInstance().getLocation().equals( - "crystal_hollows")) { + NotEnoughUpdates.INSTANCE.config.mining.locWaypoints == 0) { + return; + } - overlayStrings = new ArrayList<>(); + // Get commission and forge info even if the overlay isn't going to be rendered since it is used elsewhere + //thanks to "Pure Genie#7250" for helping with this (makes tita alert and waypoints work without mine overlay) + if(SBInfo.getInstance().getLocation() == null) return; + if (SBInfo.getInstance().getLocation().equals("mining_3") || + SBInfo.getInstance().getLocation().equals("crystal_hollows")) { commissionProgress.clear(); - String mithrilPowder = null; - String gemstonePowder = null; + // These strings will be displayed one after the other when the player list is disabled + String mithrilPowder = RED + "[NEU] Failed to get data from your tablist"; + String gemstonePowder = RED + "Please enable player list info in your skyblock settings"; + int forgeInt = 0; boolean commissions = false; boolean forges = false; @@ -298,48 +237,43 @@ public class MiningOverlay extends TextOverlay { for (NetworkPlayerInfo info : players) { String name = Minecraft.getMinecraft().ingameGUI.getTabList().getPlayerName(info); if (name.contains("Mithril Powder:")) { - mithrilPowder = DARK_AQUA + Utils.trimIgnoreColour(name).replaceAll("\u00a7[f|F|r]", ""); - continue; - } else if (mithrilPowder == null) { - mithrilPowder = RED + "[NEU] Failed to get data from your tablist"; + mithrilPowder = DARK_AQUA + Utils.trimWhitespaceAndFormatCodes(name).replaceAll("\u00a7[f|F|r]", ""); continue; } if (name.contains("Gemstone Powder:")) { - gemstonePowder = DARK_AQUA + Utils.trimIgnoreColour(name).replaceAll("\u00a7[f|F|r]", ""); - continue; - } else if (gemstonePowder == null) { - gemstonePowder = RED + "Please enable player list info in your skyblock settings"; + gemstonePowder = DARK_AQUA + Utils.trimWhitespaceAndFormatCodes(name).replaceAll("\u00a7[f|F|r]", ""); continue; } - if (name.matches("\\xa7r\\xa79\\xa7lForges \\xa7r(?:\\xa7f\\(\\+1 more\\)\\xa7r)?") && hidden != null) { + Matcher forgesMatcher = forgesHeaderPattern.matcher(name); + if (forgesMatcher.matches() && profileConfig != null) { commissions = false; forges = true; continue; - } else if (name.equals(RESET.toString() + BLUE + BOLD + "Commissions" + RESET) && hidden != null) { + } + + // Commissions appear after Forges, start enumerating Commissions instead of Forges + if (name.equals(RESET.toString() + BLUE + BOLD + "Commissions" + RESET) && profileConfig != null) { commissions = true; forges = false; continue; } - String clean = StringUtils.cleanColour(name); - if (forges && clean.startsWith(" ") && hidden != null) { - char firstChar = clean.trim().charAt(0); + String cleanName = StringUtils.cleanColour(name); + if (forges && cleanName.startsWith(" ") && profileConfig != null) { + char firstChar = cleanName.trim().charAt(0); if (firstChar < '0' || firstChar > '9') { forges = false; } else { if (name.contains("LOCKED")) { ForgeItem item = new ForgeItem(forgeInt, 1, true); - replaceForgeOrAdd(item, hidden.forgeItems, true); + replaceForgeOrAdd(item, profileConfig.forgeItems, true); } else if (name.contains("EMPTY")) { ForgeItem item = new ForgeItem(forgeInt, 0, true); - replaceForgeOrAdd(item, hidden.forgeItems, true); - //forgeStringsEmpty.add(DARK_AQUA+"Forge "+ Utils.trimIgnoreColour(name).replaceAll("\u00a7[f|F|r]", "")); + replaceForgeOrAdd(item, profileConfig.forgeItems, true); } else { - String cleanName = Utils.cleanColour(name); - Matcher matcher = timeRemainingTab.matcher(cleanName); if (matcher.matches()) { @@ -348,7 +282,7 @@ public class MiningOverlay extends TextOverlay { if (matcher.group("Ready") != null && !matcher.group("Ready").equals("")) { ForgeItem item = new ForgeItem(Utils.cleanColour(itemName), 0, forgeInt, true); - replaceForgeOrAdd(item, hidden.forgeItems, true); + replaceForgeOrAdd(item, profileConfig.forgeItems, true); } else { long duration = 0; try { @@ -374,15 +308,15 @@ public class MiningOverlay extends TextOverlay { forgeInt, true ); - replaceForgeOrAdd(item, hidden.forgeItems, false); + replaceForgeOrAdd(item, profileConfig.forgeItems, false); } } } } forgeInt++; } - } else if (commissions && clean.startsWith(" ") && hidden != null) { - String[] split = clean.trim().split(": "); + } else if (commissions && cleanName.startsWith(" ") && profileConfig != null) { + String[] split = cleanName.trim().split(": "); if (split.length == 2) { if (split[1].endsWith("%")) { try { @@ -400,8 +334,8 @@ public class MiningOverlay extends TextOverlay { forges = false; } } + if (!NotEnoughUpdates.INSTANCE.config.mining.dwarvenOverlay) { - overlayStrings = null; return; } @@ -430,22 +364,6 @@ public class MiningOverlay extends TextOverlay { } } } - /*boolean hasAny = false; - if(NotEnoughUpdates.INSTANCE.config.mining.dwarvenOverlay) { - overlayStrings.addAll(commissionsStrings); - hasAny = true; - } - if(NotEnoughUpdates.INSTANCE.config.mining.powderOverlay) { - if(mithrilPowder != null) { - if(hasAny) overlayStrings.add(null); - overlayStrings.add(DARK_AQUA+mithrilPowder); - hasAny = true; - } - } - if(NotEnoughUpdates.INSTANCE.config.mining.forgeOverlay) { - if(hasAny) overlayStrings.add(null); - overlayStrings.addAll(forgeStrings); - }*/ String pickaxeCooldown; if (ItemCooldowns.pickaxeUseCooldownMillisRemaining <= 0) { @@ -455,6 +373,7 @@ public class MiningOverlay extends TextOverlay { DARK_AQUA + "Pickaxe CD: \u00a7a" + (ItemCooldowns.pickaxeUseCooldownMillisRemaining / 1000) + "s"; } + overlayStrings = new ArrayList<>(); for (int index : NotEnoughUpdates.INSTANCE.config.mining.dwarvenText2) { switch (index) { case 0: @@ -467,8 +386,8 @@ public class MiningOverlay extends TextOverlay { overlayStrings.add(gemstonePowder); break; case 3: - if (hidden != null) { - overlayStrings.addAll(getForgeStrings(hidden.forgeItems)); + if (profileConfig != null) { + overlayStrings.addAll(getForgeStrings(profileConfig.forgeItems)); } break; case 4: @@ -479,8 +398,7 @@ public class MiningOverlay extends TextOverlay { } } } else { - overlayStrings = new ArrayList<>(); - if (hidden == null) { + if (profileConfig == null) { return; } boolean forgeDisplay = false; @@ -490,16 +408,17 @@ public class MiningOverlay extends TextOverlay { } } if (forgeDisplay) { + overlayStrings = new ArrayList<>(); if (NotEnoughUpdates.INSTANCE.config.mining.forgeDisplayEnabledLocations == 1 && !SBInfo.getInstance().isInDungeon) { - overlayStrings.addAll(getForgeStrings(hidden.forgeItems)); + overlayStrings.addAll(getForgeStrings(profileConfig.forgeItems)); } else if (NotEnoughUpdates.INSTANCE.config.mining.forgeDisplayEnabledLocations == 2) { - overlayStrings.addAll(getForgeStrings(hidden.forgeItems)); + overlayStrings.addAll(getForgeStrings(profileConfig.forgeItems)); } } } - if (overlayStrings.isEmpty()) overlayStrings = null; + if (overlayStrings != null && overlayStrings.isEmpty()) overlayStrings = null; } private static List<String> getForgeStrings(List<ForgeItem> forgeItems) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/NEUDebugLogger.java b/src/main/java/io/github/moulberry/notenoughupdates/util/NEUDebugLogger.java new file mode 100644 index 00000000..d662a872 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/NEUDebugLogger.java @@ -0,0 +1,30 @@ +package io.github.moulberry.notenoughupdates.util; + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; +import net.minecraft.client.Minecraft; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.function.Consumer; + +public class NEUDebugLogger { + private static final Minecraft mc = Minecraft.getMinecraft(); + public static Consumer<String> logMethod = NEUDebugLogger::chatLogger; + + private static void chatLogger(String message) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + "[NEU DEBUG] " + message)); + } + + public static boolean isFlagEnabled(NEUDebugFlag flag) { + return NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(flag); + } + + public static void log(NEUDebugFlag flag, String message) { + if (logMethod != null && isFlagEnabled(flag)) { + logMethod.accept(message); + } + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java index 9b5f62e3..dc88da2f 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java @@ -301,6 +301,40 @@ public class Utils { return ""; } + public static String trimWhitespaceAndFormatCodes(String str) { + int startIndex = indexOfFirstNonWhitespaceNonFormatCode(str); + int endIndex = lastIndexOfNonWhitespaceNonFormatCode(str); + if (startIndex == -1 || endIndex == -1) return ""; + return str.substring(startIndex, endIndex+1); + } + + private static int indexOfFirstNonWhitespaceNonFormatCode(String str) { + int len = str.length(); + for (int i = 0; i < len; i++) { + char ch = str.charAt(i); + if (Character.isWhitespace(ch)) { + continue; + } else if (ch == '\u00a7') { + i++; + continue; + } + return i; + } + return -1; + } + + private static int lastIndexOfNonWhitespaceNonFormatCode(String str) { + for (int i = str.length() - 1; i >= 0; i--) { + char ch = str.charAt(i); + if (Character.isWhitespace(ch) || ch == '\u00a7' || (i > 0 && str.charAt(i - 1) == '\u00a7')) { + continue; + } + return i; + } + + return -1; + } + public static List<String> getRawTooltip(ItemStack stack) { List<String> list = Lists.newArrayList(); String s = stack.getDisplayName(); |