aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.idea/codeStyles/Project.xml4
-rw-r--r--Update Notes/2.0-Pre31-Release.md8
-rw-r--r--Update Notes/2.1.md5
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java1
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DiagCommand.java90
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/core/util/Line.java90
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/listener/ChatListener.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java439
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java329
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinNetHandlerPlayClient.java5
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java32
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java12
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Mining.java8
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/overlays/CrystalHollowOverlay.java54
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/overlays/MiningOverlay.java167
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/util/NEUDebugLogger.java30
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java34
20 files changed, 1108 insertions, 208 deletions
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 6cedf3ff..c2c50913 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,5 +1,9 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
+ <JavaCodeStyleSettings>
+ <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500" />
+ <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500" />
+ </JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
diff --git a/Update Notes/2.0-Pre31-Release.md b/Update Notes/2.0-Pre31-Release.md
index da4fa3f4..dec4e6a1 100644
--- a/Update Notes/2.0-Pre31-Release.md
+++ b/Update Notes/2.0-Pre31-Release.md
@@ -9,9 +9,9 @@
- Added toggle to disable showing the treecap cooldown in item durability. (Lulonaut)#
- Added text to inform the user to "/api new" when the /pv doesn't load.
- Added support for the new arrows to the PV.
-- Improved metal detector location detection logic. (Keebler408)
-- Improved metal detector wrong location handling. (Keebler408)
-- Added Beacons waypoints for metal detector waypoint locations. (Keebler408)
+- Improved metal detector location detection logic. (CraftyOldMiner)
+- Improved metal detector wrong location handling. (CraftyOldMiner)
+- Added Beacons waypoints for metal detector waypoint locations. (CraftyOldMiner)
- Made the PV command block in the right click player menu dynamically choose its position based on a empty slot. (DeDiamondPro)
- Added Tab auto completion to the AH search gui. (Lulonaut)
@@ -27,7 +27,7 @@
- Fixed not being able to press repeat keys in chat.
- Fixed gemstone gauntlet/Divan Drill not being recognised as mining tools.
- Fixed morph pets not being recognized by the extended pet info tooltip tweak.
-- Fixed wrong commission maxes values for goblin in dwarven mines and the crystal hollows. (Keebler408)
+- Fixed wrong commission max values for goblin in dwarven mines and the crystal hollows. (CraftyOldMiner)
- Fixed player right click menu command block text appearing on an inventory item. (DeDiamondPro)
diff --git a/Update Notes/2.1.md b/Update Notes/2.1.md
index 1a120dfb..f8130d92 100644
--- a/Update Notes/2.1.md
+++ b/Update Notes/2.1.md
@@ -13,6 +13,8 @@
- [Added an armor overlay for the new armor slots](https://cdn.discordapp.com/attachments/832652653292027904/922399046528794634/unknown.png)
- Added a pet overlay that shows your active pet in your inventory
- [Price graph for items on /ah and /bz](https://cdn.discordapp.com/attachments/896407218151366687/926968296929107999/unknown.png) - DeDiamondPro
+- Added wishing compass solver that shows target coordinates using two samples - CraftyOldMiner
+- Improved metal detector logic to solve using a single position in most cases using known locations based on Keeper coordinates - CraftyOldMiner
### **Minor Changes:**
- Add built-in recipes for forge crafts - nea89
- Add Stranded Villager Trades to the item list - nea89
@@ -56,6 +58,8 @@
- Added custom sounds for crystal hollow gemstones - nea89
- Added custom biomes for crystal hollow areas - nopo
- Added a config option to hide the reforge stats for Legendary items from Hypixel on reforge stones - Lulonaut
+- Crystal Hollows crystal states are now updated when the Heart of the Mountain menu is opened - CraftyOldMiner
+- Added /neudiag command to enable/disable debug logging and dump diagnostic data - CraftyOldMiner
### **Bug Fixes**
- Fix wiki pages freezing the entire game - nea89
- Made titanium overlay and waypoints work with dwarven overlay off
@@ -85,5 +89,6 @@
- Changed custom_enchant_gui.png to remove top right button
- Added 4 new textures for the on/off switches in /neu
- Code Cleanup - IRONM00N
+- Renamed Keebler408 to CraftyOldMiner
### **Previous change log**
https://github.com/NotEnoughUpdates/NotEnoughUpdates/blob/master/Update%20Notes/2.0-Pre31-Release.md
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();