diff options
author | Walker Selby <git@walkerselby.com> | 2022-03-27 12:13:08 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-27 13:13:08 -0400 |
commit | 5e7a8a02fe836c1e635faf067fbe8913549bc0e0 (patch) | |
tree | dc99a2544bd81af6d70c7bd07ba53142ff38d057 /src/main/java | |
parent | 47efdd4f84cc9a29738fe16d631eb33ee716db61 (diff) | |
download | NotEnoughUpdates-5e7a8a02fe836c1e635faf067fbe8913549bc0e0.tar.gz NotEnoughUpdates-5e7a8a02fe836c1e635faf067fbe8913549bc0e0.tar.bz2 NotEnoughUpdates-5e7a8a02fe836c1e635faf067fbe8913549bc0e0.zip |
Add NBT Data to Profile Viewer Tabs and XP Bars (#94)
* Fixed what hypxiel broke in dungeons
* Added Longswords to Tools Tab (Itemlist)
* Set max cata level to 70
* ups
* why is it now E
* YEP the cap is 99 not 70
* long cata xp BatChest + remove crash check because hypixel profile is null and i havent fixed that yet
* Added checks for chat messages for dungeon win overlay - i think ive got them all but 4Shrug
* Added an option to alert you if you put something for too much onto ah (default 50%) + Lowest bin alert triggers if lowest bin isnt found
* IMPORTANT
* fix dungeon page in /pv
* show minions from coop in /pv
* (BREAKING CHANGE) remove whitespace in changelog
* fix crash with minion check
* initial draft
* tooltip
* important changes
* progress related things
progress bar for community goals
total collected points and personal goals display
* only show bingo tab on Bingo profiles
* make goals display gold when completed
* Added MM7 button to dungeon preset
* wart go not stonks
* fix percents and added stack size
* use code nopo in fortnite shop — Today at 17:29
The capitalisation
* config option for always showing bingo page
* 🙂
* movble pv tabs
* 2.1.md 🙂
* oops
* der Kabel
* import
* Added powder amount to level up perk
* Made it only show if the perk isnt maxed / level 0
* fix /pv crash when pet is not in repo
* 2.1.md 🙂
* web editor my beloved
* Add NBT Data to Profile Viewer Tabs and XP Bars
Add NBT Data Support for Resources in Tabs
Add NBT Data Support for Resources in XP Bars
Optimize renderTabs function
Optimize mouseClicked Function
Optimize ProfileViewerPage Enum
* Update Style
Co-authored-by: NopoTheGamer <40329022+NopoTheGamer@users.noreply.github.com>
* Update GuiProfileViewer
Rebased on pvbingo
Updated formatting with IntelliJ reformatting tool (hopefully that matches now, let me know if you want me to try again but I think I did it right)
* dungeons and pv bingo tab (#93)
* add custom skull loading back (#96)
* Refactor Hollows solvers, add tests, add Vec3Comparable, fix bugs (#95)
* Rebase on master
* Fix conflicts and rebase on master
Add changes to change log
* Oops
Co-authored-by: nopo <noahogno@gmail.com>
Co-authored-by: jani270 <jani270@gmx.de>
Co-authored-by: Lulonaut <lulonaut@tutanota.de>
Co-authored-by: NopoTheGamer <40329022+NopoTheGamer@users.noreply.github.com>
Co-authored-by: Lulonaut <67191924+Lulonaut@users.noreply.github.com>
Co-authored-by: CraftyOldMiner <85420839+CraftyOldMiner@users.noreply.github.com>
Diffstat (limited to 'src/main/java')
16 files changed, 522 insertions, 1403 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java index 951e7335..0ed68e06 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java @@ -1,5 +1,17 @@ package io.github.moulberry.notenoughupdates; +import java.awt.Color; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Set; + import com.google.common.collect.Sets; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -12,9 +24,22 @@ import io.github.moulberry.notenoughupdates.listener.ChatListener; 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.*; +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; +import io.github.moulberry.notenoughupdates.miscfeatures.FairySouls; +import io.github.moulberry.notenoughupdates.miscfeatures.FishingHelper; +import io.github.moulberry.notenoughupdates.miscfeatures.ItemCooldowns; +import io.github.moulberry.notenoughupdates.miscfeatures.ItemCustomizeManager; +import io.github.moulberry.notenoughupdates.miscfeatures.MiningStuff; +import io.github.moulberry.notenoughupdates.miscfeatures.NullzeeSphere; +import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay; +import io.github.moulberry.notenoughupdates.miscfeatures.SlotLocking; +import io.github.moulberry.notenoughupdates.miscfeatures.StorageManager; +import io.github.moulberry.notenoughupdates.miscfeatures.SunTzu; import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBiomes; -import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBlockSounds; import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.DwarvenMinesTextures; import io.github.moulberry.notenoughupdates.miscgui.CalendarOverlay; import io.github.moulberry.notenoughupdates.miscgui.InventoryStorageSelector; @@ -28,14 +53,17 @@ import io.github.moulberry.notenoughupdates.util.Utils; import io.github.moulberry.notenoughupdates.util.XPInformation; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.resources.IReloadableResourceManager; import net.minecraft.client.settings.KeyBinding; import net.minecraft.event.ClickEvent; import net.minecraft.scoreboard.ScoreObjective; import net.minecraft.scoreboard.Scoreboard; import net.minecraft.util.ChatComponentText; import net.minecraft.util.EnumChatFormatting; -import net.minecraft.world.biome.*; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraft.world.biome.BiomeGenHell; +import net.minecraft.world.biome.BiomeGenJungle; +import net.minecraft.world.biome.BiomeGenMesa; +import net.minecraft.world.biome.BiomeGenSnow; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.common.Mod; @@ -44,12 +72,6 @@ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; -import java.awt.*; -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Set; - @Mod(modid = NotEnoughUpdates.MODID, version = NotEnoughUpdates.VERSION, clientSideOnly = true) public class NotEnoughUpdates { public static final String MODID = "notenoughupdates"; @@ -57,6 +79,60 @@ public class NotEnoughUpdates { public static final String PRE_VERSION = "0.0"; public static final int VERSION_ID = 20100; public static final int PRE_VERSION_ID = 0; + + public static NotEnoughUpdates INSTANCE = null; + + public NEUManager manager; + public NEUOverlay overlay; + public NEUConfig config; + + private File configFile; + + public File getConfigFile() { + return this.configFile; + } + + public void newConfigFile() { + this.configFile = new File(NotEnoughUpdates.INSTANCE.getNeuDir(), "configNew.json"); + } + + private static final long CHAT_MSG_COOLDOWN = 200; + private long lastChatMessage = 0; + private long secondLastChatMessage = 0; + private String currChatMessage = null; + + //Stolen from Biscut and used for detecting whether in skyblock + private static final Set<String> SKYBLOCK_IN_ALL_LANGUAGES = + Sets.newHashSet("SKYBLOCK", "\u7A7A\u5C9B\u751F\u5B58", "\u7A7A\u5CF6\u751F\u5B58"); + + public GuiScreen openGui = null; + public long lastOpenedGui = 0; + + public Commands commands; + + public static HashMap<String, String> petRarityToColourMap = new HashMap<String, String>() {{ + put("UNKNOWN", EnumChatFormatting.RED.toString()); + put("COMMON", EnumChatFormatting.WHITE.toString()); + put("UNCOMMON", EnumChatFormatting.GREEN.toString()); + put("RARE", EnumChatFormatting.BLUE.toString()); + put("EPIC", EnumChatFormatting.DARK_PURPLE.toString()); + put("LEGENDARY", EnumChatFormatting.GOLD.toString()); + put("MYTHIC", EnumChatFormatting.LIGHT_PURPLE.toString()); + }}; + + public static ProfileViewer profileViewer; + + public boolean packDevEnabled = false; + + private final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); + private File neuDir; + + public File getNeuDir() { + return this.neuDir; + } + + public Color[][] colourMap = null; + /** * Registers the biomes for the crystal hollows here so optifine knows they exists */ @@ -90,48 +166,6 @@ public class NotEnoughUpdates { .setBiomeName("NeuCrystalHollowsCrystalNucleus") .setFillerBlockMetadata(5470985) .setTemperatureRainfall(0.95F, 0.9F); - private static final long CHAT_MSG_COOLDOWN = 200; - //Stolen from Biscut and used for detecting whether in skyblock - private static final Set<String> SKYBLOCK_IN_ALL_LANGUAGES = - Sets.newHashSet("SKYBLOCK", "\u7A7A\u5C9B\u751F\u5B58", "\u7A7A\u5CF6\u751F\u5B58"); - public static NotEnoughUpdates INSTANCE = null; - public static HashMap<String, String> petRarityToColourMap = new HashMap<String, String>() {{ - put("UNKNOWN", EnumChatFormatting.RED.toString()); - put("COMMON", EnumChatFormatting.WHITE.toString()); - put("UNCOMMON", EnumChatFormatting.GREEN.toString()); - put("RARE", EnumChatFormatting.BLUE.toString()); - put("EPIC", EnumChatFormatting.DARK_PURPLE.toString()); - put("LEGENDARY", EnumChatFormatting.GOLD.toString()); - put("MYTHIC", EnumChatFormatting.LIGHT_PURPLE.toString()); - }}; - public static ProfileViewer profileViewer; - private final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); - public NEUManager manager; - public NEUOverlay overlay; - public NEUConfig config; - public GuiScreen openGui = null; - public long lastOpenedGui = 0; - public Commands commands; - public boolean packDevEnabled = false; - public Color[][] colourMap = null; - private File configFile; - private long lastChatMessage = 0; - private long secondLastChatMessage = 0; - private String currChatMessage = null; - private File neuDir; - private boolean hasSkyblockScoreboard; - - public File getConfigFile() { - return this.configFile; - } - - public void newConfigFile() { - this.configFile = new File(NotEnoughUpdates.INSTANCE.getNeuDir(), "configNew.json"); - } - - public File getNeuDir() { - return this.neuDir; - } /** * Instantiates NEUIo, NEUManager and NEUOverlay instances. Registers keybinds and adds a shutdown hook to clear tmp folder. @@ -215,14 +249,6 @@ public class NotEnoughUpdates { MinecraftForge.EVENT_BUS.register(new CrystalOverlay()); MinecraftForge.EVENT_BUS.register(new ItemCooldowns()); - if (Minecraft.getMinecraft().getResourceManager() instanceof IReloadableResourceManager) { - IReloadableResourceManager manager = (IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager(); - manager.registerReloadListener(CustomSkulls.getInstance()); - manager.registerReloadListener(NPCRetexturing.getInstance()); - manager.registerReloadListener(new ItemCustomizeManager.ReloadListener()); - manager.registerReloadListener(new CustomBlockSounds.ReloaderListener()); - } - this.commands = new Commands(); BackgroundBlur.registerListener(); @@ -384,6 +410,8 @@ public class NotEnoughUpdates { return hasSkyblockScoreboard(); } + private boolean hasSkyblockScoreboard; + public boolean hasSkyblockScoreboard() { return hasSkyblockScoreboard; } 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 index dab99698..88264538 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DiagCommand.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DiagCommand.java @@ -17,10 +17,8 @@ public class DiagCommand extends ClientCommandBase { private static final String USAGE_TEXT = EnumChatFormatting.WHITE + "Usage: /neudiag <metal | wishing | debug>\n\n" + - "/neudiag metal Metal Detector Solver diagnostics\n" + - " <no sub-command> Show current solution diags\n" + - " center=<off | on> Disable / enable using center\n" + - "/neudiag wishing Wishing Compass Solver diagnostics\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"; @@ -39,26 +37,7 @@ public class DiagCommand extends ClientCommandBase { String command = args[0].toLowerCase(); switch (command) { case "metal": - if (args.length == 1) { - CrystalMetalDetectorSolver.logDiagnosticData(true); - return; - } - - String subCommand = args[1].toLowerCase(); - if (subCommand.equals("center=off")) { - CrystalMetalDetectorSolver.setDebugDoNotUseCenter(true); - sender.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "Center coordinates-based solutions disabled")); - } - else if (subCommand.equals("center=on")) { - CrystalMetalDetectorSolver.setDebugDoNotUseCenter(false); - sender.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "Center coordinates-based solutions enabled")); - } else { - showUsage(sender); - return; - } - + CrystalMetalDetectorSolver.logDiagnosticData(true); break; case "wishing": CrystalWishingCompassSolver.getInstance().logDiagnosticData(true); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/core/config/annotations/ConfigEditorDraggableList.java b/src/main/java/io/github/moulberry/notenoughupdates/core/config/annotations/ConfigEditorDraggableList.java index 5063d543..61e923f5 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/core/config/annotations/ConfigEditorDraggableList.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/core/config/annotations/ConfigEditorDraggableList.java @@ -9,5 +9,5 @@ import java.lang.annotation.Target; @Target(ElementType.FIELD) public @interface ConfigEditorDraggableList { String[] exampleText(); - boolean allowRemovingElements() default true; + boolean allowDeleting() default true; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/core/config/gui/GuiOptionEditorDraggableList.java b/src/main/java/io/github/moulberry/notenoughupdates/core/config/gui/GuiOptionEditorDraggableList.java index df373dbf..4dbde24b 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/core/config/gui/GuiOptionEditorDraggableList.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/core/config/gui/GuiOptionEditorDraggableList.java @@ -24,7 +24,7 @@ public class GuiOptionEditorDraggableList extends GuiOptionEditor { private static final ResourceLocation DELETE = new ResourceLocation("notenoughupdates:core/delete.png"); private final String[] exampleText; - private final boolean allowRemovingElements; + private final boolean enableDeleting; private final List<Integer> activeText; private int currentDragging = -1; private int dragStartIndex = -1; @@ -39,11 +39,11 @@ public class GuiOptionEditorDraggableList extends GuiOptionEditor { public GuiOptionEditorDraggableList( ConfigProcessor.ProcessedOption option, String[] exampleText, - boolean allowRemovingElements + boolean disableDeleting ) { super(option); - this.allowRemovingElements = allowRemovingElements; + this.enableDeleting = disableDeleting; this.exampleText = exampleText; this.activeText = (List<Integer>) option.get(); } @@ -84,7 +84,7 @@ public class GuiOptionEditorDraggableList extends GuiOptionEditor { GlStateManager.color(1, greenBlue, greenBlue, 1); } - if (allowRemovingElements) { + if (enableDeleting) { Minecraft.getMinecraft().getTextureManager().bindTexture(DELETE); Utils.drawTexturedRect(x + width / 6 + 27, y + 45 - 7 - 13, 11, 14, GL11.GL_NEAREST); } @@ -215,7 +215,7 @@ public class GuiOptionEditorDraggableList extends GuiOptionEditor { dragStartIndex >= 0 && Mouse.getEventButton() == 0 && mouseX >= x + width / 6 + 27 - 3 && mouseX <= x + width / 6 + 27 + 11 + 3 && mouseY >= y + 45 - 7 - 13 - 3 && mouseY <= y + 45 - 7 - 13 + 14 + 3) { - if (allowRemovingElements) { + if (enableDeleting) { activeText.remove(dragStartIndex); } currentDragging = -1; @@ -226,13 +226,13 @@ public class GuiOptionEditorDraggableList extends GuiOptionEditor { if (!Mouse.isButtonDown(0) || dropdownOpen) { currentDragging = -1; dragStartIndex = -1; - if (trashHoverTime > 0 && allowRemovingElements) trashHoverTime = -System.currentTimeMillis(); + if (trashHoverTime > 0 && enableDeleting) trashHoverTime = -System.currentTimeMillis(); } else if (currentDragging >= 0 && mouseX >= x + width / 6 + 27 - 3 && mouseX <= x + width / 6 + 27 + 11 + 3 && mouseY >= y + 45 - 7 - 13 - 3 && mouseY <= y + 45 - 7 - 13 + 14 + 3) { - if (trashHoverTime < 0 && allowRemovingElements) trashHoverTime = System.currentTimeMillis(); + if (trashHoverTime < 0 && enableDeleting) trashHoverTime = System.currentTimeMillis(); } else { - if (trashHoverTime > 0 && allowRemovingElements) trashHoverTime = -System.currentTimeMillis(); + if (trashHoverTime > 0 && enableDeleting) trashHoverTime = -System.currentTimeMillis(); } if (Mouse.getEventButtonState()) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/core/config/struct/ConfigProcessor.java b/src/main/java/io/github/moulberry/notenoughupdates/core/config/struct/ConfigProcessor.java index 3eb8a2d1..0d06980a 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/core/config/struct/ConfigProcessor.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/core/config/struct/ConfigProcessor.java @@ -147,7 +147,7 @@ public class ConfigProcessor { if (optionField.isAnnotationPresent(ConfigEditorDraggableList.class)) { ConfigEditorDraggableList configEditorAnnotation = optionField.getAnnotation(ConfigEditorDraggableList.class); - editor = new GuiOptionEditorDraggableList(option, configEditorAnnotation.exampleText(), configEditorAnnotation.allowRemovingElements()); + editor = new GuiOptionEditorDraggableList(option, configEditorAnnotation.exampleText(), configEditorAnnotation.allowDeleting()); } } if (optionType.isAssignableFrom(String.class)) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/core/util/Vec3Comparable.java b/src/main/java/io/github/moulberry/notenoughupdates/core/util/Vec3Comparable.java deleted file mode 100644 index cd144c21..00000000 --- a/src/main/java/io/github/moulberry/notenoughupdates/core/util/Vec3Comparable.java +++ /dev/null @@ -1,129 +0,0 @@ -package io.github.moulberry.notenoughupdates.core.util; - -import net.minecraft.util.BlockPos; -import net.minecraft.util.Vec3; -import net.minecraft.util.Vec3i; - -public class Vec3Comparable extends Vec3 implements Comparable<Vec3Comparable> { - public static final Vec3Comparable NULL_VECTOR = new Vec3Comparable(0.0, 0.0, 0.0); - - public Vec3Comparable(double x, double y, double z) { - super(x, y, z); - } - - public Vec3Comparable(Vec3i sourceVec) { - super(sourceVec); - } - - public Vec3Comparable(Vec3 source) { - super(source.xCoord, source.yCoord, source.zCoord); - } - - public Vec3Comparable(BlockPos source) { - - super(source.getX(), source.getY(), source.getZ()); - } - - public Vec3Comparable(Vec3Comparable source) { - super(source.xCoord, source.yCoord, source.zCoord); - } - - @Override - public Vec3Comparable subtractReverse(Vec3 vec) { - return new Vec3Comparable(super.subtractReverse(vec)); - } - - @Override - public Vec3Comparable normalize() { - return new Vec3Comparable(super.normalize()); - } - - @Override - public Vec3Comparable crossProduct(Vec3 vec) { - return new Vec3Comparable(super.crossProduct(vec)); - } - - @Override - public Vec3Comparable subtract(Vec3 vec) { - return new Vec3Comparable(super.subtract(vec)); - } - - @Override - public Vec3Comparable subtract(double x, double y, double z) { - return new Vec3Comparable(super.subtract(x, y, z)); - } - - @Override - public Vec3Comparable add(Vec3 other) { - return new Vec3Comparable(super.add(other)); - } - - @Override - public Vec3Comparable addVector(double x, double y, double z) { - return new Vec3Comparable(super.addVector(x, y, z)); - } - - @Override - public Vec3Comparable getIntermediateWithXValue(Vec3 vec, double x) { - return new Vec3Comparable(super.getIntermediateWithXValue(vec, x)); - } - - @Override - public Vec3Comparable getIntermediateWithYValue(Vec3 vec, double y) { - return new Vec3Comparable(super.getIntermediateWithYValue(vec, y)); - } - - @Override - public Vec3Comparable getIntermediateWithZValue(Vec3 vec, double z) { - return new Vec3Comparable(super.getIntermediateWithZValue(vec, z)); - } - - @Override - public Vec3Comparable rotatePitch(float pitch) { - return new Vec3Comparable(super.rotatePitch(pitch)); - } - - @Override - public Vec3Comparable rotateYaw(float yaw) { - return new Vec3Comparable(super.rotateYaw(yaw)); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (!(other instanceof Vec3Comparable)) { - return false; - } else { - Vec3Comparable vec3c = (Vec3Comparable) other; - return this.xCoord == vec3c.xCoord && this.yCoord == vec3c.yCoord && this.zCoord == vec3c.zCoord; - } - } - - @Override - public int hashCode() { - long bits = 1L; - bits = 31L * bits + doubleToLongBits(xCoord); - bits = 31L * bits + doubleToLongBits(yCoord); - bits = 31L * bits + doubleToLongBits(zCoord); - return (int) (bits ^ (bits >> 32)); - } - - public int compareTo(Vec3Comparable other) { - return this.yCoord == other.yCoord ? - (this.zCoord == other.zCoord ? - (int)(this.xCoord - other.xCoord) - : (int)(this.zCoord - other.zCoord)) - : (int)(this.yCoord - other.yCoord); - } - - public boolean signumEquals(Vec3 other) { - return Math.signum(xCoord) == Math.signum(other.xCoord) && - Math.signum(yCoord) == Math.signum(other.yCoord) && - Math.signum(zCoord) == Math.signum(other.zCoord); - } - - private static long doubleToLongBits(double d) { - return d == 0.0 ? 0L : Double.doubleToLongBits(d); - } -} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/AuctionBINWarning.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/AuctionBINWarning.java index c7bcc0e1..a787ca99 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/AuctionBINWarning.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/AuctionBINWarning.java @@ -37,7 +37,9 @@ public class AuctionBINWarning extends GuiElement { private String sellingName; private int sellingPrice; private int lowestPrice; + private int buyPercentage; private int sellStackAmount; + private boolean isALoss = true; private boolean shouldPerformCheck() { if (!NotEnoughUpdates.INSTANCE.config.ahTweaks.enableBINWarning || @@ -110,14 +112,13 @@ public class AuctionBINWarning extends GuiElement { float undercutFactor = 1 - NotEnoughUpdates.INSTANCE.config.ahTweaks.warningThreshold / 100; if (undercutFactor < 0) undercutFactor = 0; if (undercutFactor > 1) undercutFactor = 1; - float overcutFactor = 1 + NotEnoughUpdates.INSTANCE.config.ahTweaks.overcutWarningThreshold / 100; + float overcutFactor = 1 - NotEnoughUpdates.INSTANCE.config.ahTweaks.overcutWarningThreshold / 100; if (overcutFactor < 0) overcutFactor = 0; + if (overcutFactor > 1) overcutFactor = 1; - if ( - (sellingPrice > 0 && lowestPrice > 0 && - ((sellingPrice > sellStackAmount * lowestPrice * overcutFactor) || - sellingPrice < sellStackAmount * lowestPrice * undercutFactor)) - || lowestPrice == -1) { + if ((sellingPrice > 0 && lowestPrice > 0 && sellingPrice < sellStackAmount * lowestPrice * undercutFactor) + || (sellingPrice > 0 && lowestPrice > 0 && sellingPrice > sellStackAmount * lowestPrice * (overcutFactor + 1)) + || lowestPrice == -1) { showWarning = true; return true; } else { @@ -180,9 +181,7 @@ public class AuctionBINWarning extends GuiElement { width / 2, height / 2 - 45 + 25, false, 170, 0xffffffff ); TextRenderUtils.drawStringCenteredScaledMaxWidth( - (lowestPrice > 0 - ? "has a lowest BIN of \u00a76" + lowestPriceStr + "\u00a7r coins" - : "\u00a7cWarning: No lowest BIN found!"), + (lowestPrice > 0 ? "has a lowest BIN of \u00a76" + lowestPriceStr + "\u00a7r coins" : "\u00a7cWarning: No lowest BIN found!"), Minecraft.getMinecraft().fontRendererObj, width / 2, height / 2 - 45 + 34, @@ -191,16 +190,16 @@ public class AuctionBINWarning extends GuiElement { 0xffa0a0a0 ); - boolean isALoss = false; - int buyPercentage = 0; if (sellingPrice > lowestPrice * sellStackAmount) { - buyPercentage = sellingPrice * 100 / (lowestPrice * sellStackAmount) - 100; + buyPercentage = sellingPrice * 100 / (lowestPrice * sellStackAmount); isALoss = false; } else if (sellingPrice < lowestPrice * sellStackAmount) { - buyPercentage = 100 - sellingPrice * 100 / (lowestPrice * sellStackAmount); + buyPercentage = 100 - sellingPrice * 100 / (lowestPrice * sellStackAmount); + if (buyPercentage <= 0) buyPercentage = 1; isALoss = true; } + TextRenderUtils.drawStringCenteredScaledMaxWidth( "Continue selling it for", Minecraft.getMinecraft().fontRendererObj, @@ -211,10 +210,7 @@ public class AuctionBINWarning extends GuiElement { 0xffa0a0a0 ); TextRenderUtils.drawStringCenteredScaledMaxWidth( - "\u00a76" + sellingPriceStr + "\u00a7r coins?" + - ((lowestPrice > 0 && buyPercentage > 0) - ? "(\u00a7" + (isALoss ? "c-" : "a+") + buyPercentage + "%\u00a7r)" - : ""), + "\u00a76" + sellingPriceStr + "\u00a7r coins?" + (lowestPrice > 0 ? "(\u00a7" + (isALoss ? "c-" : "a+") + buyPercentage + "%\u00a7r)" : ""), Minecraft.getMinecraft().fontRendererObj, width / 2, height / 2 - 45 + 59, 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 f43ebda2..27c334ad 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java @@ -1,7 +1,6 @@ package io.github.moulberry.notenoughupdates.miscfeatures; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; -import io.github.moulberry.notenoughupdates.core.util.Vec3Comparable; import io.github.moulberry.notenoughupdates.core.util.render.RenderUtils; import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; import io.github.moulberry.notenoughupdates.util.NEUDebugLogger; @@ -12,6 +11,7 @@ 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.Arrays; @@ -22,35 +22,25 @@ import java.util.List; import java.util.stream.Collectors; public class CrystalMetalDetectorSolver { - enum SolutionState { - NOT_STARTED, - MULTIPLE, - MULTIPLE_KNOWN, - FOUND, - FOUND_KNOWN, - FAILED, - INVALID, - } - private static final Minecraft mc = Minecraft.getMinecraft(); - private static Vec3Comparable prevPlayerPos; - private static double prevDistToTreasure; + private static BlockPos prevPlayerPos; + private static double prevDistToTreasure = 0; private static HashSet<BlockPos> possibleBlocks = new HashSet<>(); - private static final HashMap<Vec3Comparable, Double> evaluatedPlayerPositions = new HashMap<>(); - private static boolean chestRecentlyFound; - private static long chestLastFoundMillis; + 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 debugDoNotUseCenter = false; - private static boolean visitKeeperMessagePrinted; - private static final String KEEPER_OF_STRING = "Keeper of "; - private static final String DIAMOND_STRING = "diamond"; - private static final String LAPIS_STRING = "lapis"; - private static final String EMERALD_STRING = "emerald"; - private static final String GOLD_STRING = "gold"; + 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)); @@ -104,14 +94,6 @@ public class CrystalMetalDetectorSolver { -9896946827286L // x=-37, y=-21, z=-22 )); - static Predicate<BlockPos> treasureAllowedPredicate = CrystalMetalDetectorSolver::treasureAllowed; - static SolutionState currentState = SolutionState.NOT_STARTED; - static SolutionState previousState = SolutionState.NOT_STARTED; - - public interface Predicate<BlockPos> { - boolean check(BlockPos blockPos); - } - public static void process(IChatComponent message) { if (SBInfo.getInstance().getLocation() == null || !NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled || @@ -120,7 +102,7 @@ public class CrystalMetalDetectorSolver { return; } - boolean centerNewlyDiscovered = locateMinesCenterIfNeeded(); + locateMinesCenterIfNeeded(); double distToTreasure = Double.parseDouble(message .getUnformattedText() @@ -140,64 +122,20 @@ public class CrystalMetalDetectorSolver { chestRecentlyFound = false; } - SolutionState originalState = currentState; - int originalCount = possibleBlocks.size(); - Vec3Comparable adjustedPlayerPos = getPlayerPosAdjustedForEyeHeight(); - findPossibleSolutions(distToTreasure, adjustedPlayerPos, centerNewlyDiscovered); - if (currentState != originalState || originalCount != possibleBlocks.size()) { - switch (currentState) { - case FOUND_KNOWN: - NEUDebugLogger.log(NEUDebugFlag.METAL, "Known location identified."); - // falls through - case FOUND: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + "[NEU] Found solution.")); - if (NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.METAL) && - (previousState == SolutionState.INVALID || previousState == SolutionState.FAILED)) { - NEUDebugLogger.log(NEUDebugFlag.METAL, - EnumChatFormatting.AQUA + "Solution coordinates: " + - EnumChatFormatting.WHITE + possibleBlocks.iterator().next().toString()); - } - break; - case INVALID: - mc.thePlayer.addChatMessage(new ChatComponentText( - EnumChatFormatting.RED + "[NEU] Previous solution is invalid.")); - logDiagnosticData(false); - resetSolution(false); - break; - case FAILED: - mc.thePlayer.addChatMessage(new ChatComponentText( - EnumChatFormatting.RED + "[NEU] Failed to find a solution.")); - logDiagnosticData(false); - resetSolution(false); - break; - case MULTIPLE_KNOWN: - NEUDebugLogger.log(NEUDebugFlag.METAL, "Multiple known locations identified:"); - // falls through - case MULTIPLE: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "[NEU] Need another position to find solution. Possible blocks: " + possibleBlocks.size())); - break; - default: - throw new IllegalStateException("Metal detector is in invalid state"); - } - } - } - - static void findPossibleSolutions(double distToTreasure, Vec3Comparable playerPos, boolean centerNewlyDiscovered) { - if (prevDistToTreasure == distToTreasure && prevPlayerPos.equals(playerPos) && - !evaluatedPlayerPositions.containsKey(playerPos)) { - evaluatedPlayerPositions.put(playerPos, distToTreasure); + 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(playerPos.xCoord) + xOffset, - y, Math.floor(playerPos.zCoord) + zOffset - ); - calculatedDist = playerPos.distanceTo(new Vec3Comparable(pos).addVector(0D, 1D, 0D)); - if (round(calculatedDist, 1) == distToTreasure && treasureAllowedPredicate.check(pos)) { + 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++; @@ -205,11 +143,10 @@ public class CrystalMetalDetectorSolver { xOffset = 0; calculatedDist = 0; while (calculatedDist < distToTreasure) { - BlockPos pos = new BlockPos(Math.floor(playerPos.xCoord) - xOffset, - y, Math.floor(playerPos.zCoord) + zOffset - ); - calculatedDist = playerPos.distanceTo(new Vec3Comparable(pos).addVector(0D, 1D, 0D)); - if (round(calculatedDist, 1) == distToTreasure && treasureAllowedPredicate.check(pos)) { + 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++; @@ -217,33 +154,48 @@ public class CrystalMetalDetectorSolver { } } - updateSolutionState(); + checkAndDisplaySolutionState(); } else if (possibleBlocks.size() != 1) { + evaluatedPlayerPositions.put(mc.thePlayer.getPosition().getImmutable(), distToTreasure); HashSet<BlockPos> temp = new HashSet<>(); for (BlockPos pos : possibleBlocks) { - if (round(playerPos.distanceTo(new Vec3Comparable(pos).addVector(0D, 1D, 0D)), 1) == distToTreasure) { + if (round(getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)), 1) == distToTreasure) { temp.add(pos); } } possibleBlocks = temp; - updateSolutionState(); + checkAndDisplaySolutionState(); } else { BlockPos pos = possibleBlocks.iterator().next(); - if (Math.abs(distToTreasure - (playerPos.distanceTo(new Vec3Comparable(pos)))) > 5) { - currentState = SolutionState.INVALID; + 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); } } - } else if (centerNewlyDiscovered && possibleBlocks.size() > 1) { - updateSolutionState(); } - prevPlayerPos = playerPos; + prevPlayerPos = mc.thePlayer.getPosition().getImmutable(); prevDistToTreasure = distToTreasure; } - public static void setDebugDoNotUseCenter(boolean val) { - debugDoNotUseCenter = val; + private static void checkForSingleKnownLocationMatch() { + if (minesCenter == BlockPos.NULL_VECTOR || possibleBlocks.size() < 2) { + return; + } + + 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:"); + } } private static String getFriendlyBlockPositions(Collection<BlockPos> positions) { @@ -254,13 +206,10 @@ public class CrystalMetalDetectorSolver { StringBuilder sb = new StringBuilder(); sb.append("\n"); for (BlockPos blockPos : positions) { - sb.append("Absolute: "); - sb.append(blockPos.toString()); + sb.append("Absolute: " + blockPos.toString()); if (minesCenter != Vec3i.NULL_VECTOR) { BlockPos relativeOffset = blockPos.subtract(minesCenter); - sb.append(", Relative: "); - sb.append(relativeOffset.toString() ); - sb.append(" (" + relativeOffset.toLong() + ")"); + sb.append(", Relative: " + relativeOffset.toString() + " (" + relativeOffset.toLong() + ")"); } sb.append("\n"); } @@ -268,23 +217,22 @@ public class CrystalMetalDetectorSolver { return sb.toString(); } - private static String getFriendlyEvaluatedPositions() { - if (!NEUDebugLogger.isFlagEnabled(NEUDebugFlag.METAL) || evaluatedPlayerPositions.size() == 0) { + 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 (Vec3Comparable vec : evaluatedPlayerPositions.keySet()) { - sb.append("Absolute: " + vec.toString()); + for (BlockPos blockPos : positions.keySet()) { + sb.append("Absolute: " + blockPos.toString()); if (minesCenter != Vec3i.NULL_VECTOR) { - BlockPos positionBlockPos = new BlockPos(vec); - BlockPos relativeOffset = positionBlockPos.subtract(minesCenter); + BlockPos relativeOffset = blockPos.subtract(minesCenter); sb.append(", Relative: " + relativeOffset.toString() + " (" + relativeOffset.toLong() + ")"); } sb.append(" Distance: "); - sb.append(evaluatedPlayerPositions.get(vec)); + sb.append(positions.get(blockPos)); sb.append("\n"); } @@ -292,8 +240,101 @@ public class CrystalMetalDetectorSolver { 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) { @@ -304,18 +345,15 @@ public class CrystalMetalDetectorSolver { chestRecentlyFound = chestFound; possibleBlocks.clear(); evaluatedPlayerPositions.clear(); - previousState = currentState; - currentState = SolutionState.NOT_STARTED; } public static void initWorld() { minesCenter = Vec3i.NULL_VECTOR; visitKeeperMessagePrinted = false; + blockPosIfLastSolutionInvalid = null; openedChestPositions.clear(); - chestLastFoundMillis = 0; prevDistToTreasure = 0; prevPlayerPos = null; - currentState = SolutionState.NOT_STARTED; resetSolution(false); } @@ -339,14 +377,15 @@ public class CrystalMetalDetectorSolver { } } - private static boolean locateMinesCenterIfNeeded() { + private static void locateMinesCenterIfNeeded() { if (minesCenter != Vec3i.NULL_VECTOR) { - return false; + return; } List<EntityArmorStand> keeperEntities = mc.theWorld.getEntities(EntityArmorStand.class, (entity) -> { if (!entity.hasCustomName()) return false; - return entity.getCustomNameTag().contains(KEEPER_OF_STRING); + if (entity.getCustomNameTag().contains(KEEPER_OF_STRING)) return true; + return false; }); if (keeperEntities.size() == 0) { @@ -355,7 +394,7 @@ public class CrystalMetalDetectorSolver { "[NEU] Approach a Keeper while holding the metal detector to enable faster treasure hunting.")); visitKeeperMessagePrinted = true; } - return false; + return; } EntityArmorStand keeperEntity = keeperEntities.get(0); @@ -368,11 +407,6 @@ public class CrystalMetalDetectorSolver { EnumChatFormatting.WHITE + minesCenter.toString()); mc.thePlayer.addChatMessage(new ChatComponentText( EnumChatFormatting.YELLOW + "[NEU] Faster treasure hunting is now enabled based on Keeper location.")); - return true; - } - - public static void setMinesCenter(BlockPos center) { - minesCenter = center; } private static double round(double value, int precision) { @@ -380,174 +414,42 @@ public class CrystalMetalDetectorSolver { return (double) Math.round(value * scale) / scale; } - private static void updateSolutionState() { - previousState = currentState; - + private static void checkAndDisplaySolutionState() { if (possibleBlocks.size() == 0) { - currentState = SolutionState.FAILED; - return; - } - - if (possibleBlocks.size() == 1) { - currentState = SolutionState.FOUND; + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[NEU] Failed to find a solution.")); + logDiagnosticData(false); + resetSolution(false); return; } - // Narrow solutions using known locations if the mines center is known - if (minesCenter.equals(BlockPos.NULL_VECTOR) || debugDoNotUseCenter) { - currentState = SolutionState.MULTIPLE; - return; - } - - HashSet<BlockPos> temp = - possibleBlocks.stream() - .filter(block -> knownChestOffsets.contains(block.subtract(minesCenter).toLong())) - .collect(Collectors.toCollection(HashSet::new)); - if (temp.size() == 0) { - currentState = SolutionState.MULTIPLE; - return; - } - - if (temp.size() == 1) { - possibleBlocks = temp; - currentState = SolutionState.FOUND_KNOWN; - return; - - } - - currentState = SolutionState.MULTIPLE_KNOWN; - } - - public static BlockPos getSolution() { - if (CrystalMetalDetectorSolver.possibleBlocks.size() != 1) { - return BlockPos.ORIGIN; + checkForSingleKnownLocationMatch(); + if (possibleBlocks.size() > 1) { + 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.")); } - - return CrystalMetalDetectorSolver.possibleBlocks.stream().iterator().next(); } - private static Vec3Comparable getPlayerPosAdjustedForEyeHeight() { - return new Vec3Comparable( + private static Vec3 getPlayerPos() { + return new Vec3( mc.thePlayer.posX, mc.thePlayer.posY + (mc.thePlayer.getEyeHeight() - mc.thePlayer.getDefaultEyeHeight()), mc.thePlayer.posZ ); } - static boolean isKnownOffset(BlockPos pos) { - return knownChestOffsets.contains(pos.subtract(minesCenter).toLong()); - } - - static boolean isAllowedBlockType(BlockPos pos) { - return mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:gold_block") || + private static boolean treasureAllowed(BlockPos pos) { + 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"); - } - - static boolean isAirAbove(BlockPos pos) { - return mc.theWorld. - getBlockState(pos.add(0, 1, 0)).getBlock().getRegistryName().equals("minecraft:air"); - } - - private static boolean treasureAllowed(BlockPos pos) { - boolean airAbove = isAirAbove(pos); - boolean allowedBlockType = isAllowedBlockType(pos); - return isKnownOffset(pos) || (airAbove && allowedBlockType); - } - - static private String getDiagnosticMessage() { - StringBuilder diagsMessage = new StringBuilder(); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Mines Center: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append((minesCenter.equals(Vec3i.NULL_VECTOR)) ? "<NOT DISCOVERED>" : minesCenter.toString()); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Current Solution State: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(currentState.name()); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Previous Solution State: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(previousState.name()); - 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("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()); - 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>"); - } - - return diagsMessage.toString(); - } - - public static void logDiagnosticData(boolean outputAlways) { - if (!SBInfo.getInstance().checkForSkyblockLocation()) { - return; - } - - if (!NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled) - { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] Metal Detector Solver is not enabled.")); - return; - } - - boolean metalDebugFlagSet = NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.METAL); - if (outputAlways || metalDebugFlagSet) { - NEUDebugLogger.logAlways(getDiagnosticMessage()); - } + 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 index 9a950e7f..25f39c3a 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java @@ -2,67 +2,27 @@ 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.core.util.Vec3Comparable; -import io.github.moulberry.notenoughupdates.options.NEUConfig; import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; import io.github.moulberry.notenoughupdates.util.NEUDebugLogger; import io.github.moulberry.notenoughupdates.util.SBInfo; -import io.github.moulberry.notenoughupdates.util.Utils; 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.Vec3i; -import net.minecraftforge.client.ClientCommandHandler; +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; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.function.BooleanSupplier; -import java.util.function.LongSupplier; - public class CrystalWishingCompassSolver { - enum SolverState { - NOT_STARTED, - PROCESSING_FIRST_USE, - NEED_SECOND_COMPASS, - PROCESSING_SECOND_USE, - SOLVED, - FAILED_EXCEPTION, - FAILED_TIMEOUT_NO_REPEATING, - FAILED_TIMEOUT_NO_PARTICLES, - FAILED_INTERSECTION_CALCULATION, - FAILED_INVALID_SOLUTION, - } - - enum CompassTarget { - GOBLIN_QUEEN, - GOBLIN_KING, - BAL, - JUNGLE_TEMPLE, - ODAWA, - PRECURSOR_CITY, - MINES_OF_DIVAN, - CRYSTAL_NUCLEUS, - } - - enum Crystal { - AMBER, - AMETHYST, - JADE, - SAPPHIRE, - TOPAZ, - } - private static final CrystalWishingCompassSolver INSTANCE = new CrystalWishingCompassSolver(); public static CrystalWishingCompassSolver getInstance() { return INSTANCE; @@ -71,72 +31,40 @@ public class CrystalWishingCompassSolver { private static final Minecraft mc = Minecraft.getMinecraft(); private static boolean isSkytilsPresent = false; - // NOTE: There is a small set of breakable blocks above the nucleus at Y > 181. While this zone is reported - // as the Crystal Nucleus by Hypixel, for wishing compass purposes it is in the appropriate quadrant. - private static final AxisAlignedBB NUCLEUS_BB = new AxisAlignedBB(462, 63, 461, 564, 181, 565); - private static final AxisAlignedBB HOLLOWS_BB = new AxisAlignedBB(201, 30, 201, 824, 189, 824); - private static final AxisAlignedBB PRECURSOR_REMNANTS_BB = new AxisAlignedBB(513, 64, 513, 824, 189, 824); - private static final AxisAlignedBB MITHRIL_DEPOSITS_BB = new AxisAlignedBB(513, 64, 201, 824, 189, 512); - private static final AxisAlignedBB GOBLIN_HOLDOUT_BB = new AxisAlignedBB(201, 64, 513, 512, 189, 824); - private static final AxisAlignedBB JUNGLE_BB = new AxisAlignedBB(201, 64, 201, 512, 189, 512); - private static final AxisAlignedBB MAGMA_FIELDS_BB = new AxisAlignedBB(201, 30, 201, 824, 63, 824); - private static final double MAX_DISTANCE_BETWEEN_PARTICLES = 0.6; - private static final double MAX_DISTANCE_FROM_USE_TO_FIRST_PARTICLE = 9.0; - - // 64.0 is an arbitrary value but seems to work well - private static final double MINIMUM_DISTANCE_SQ_BETWEEN_COMPASSES = 64.0; - - // All particles typically arrive in < 3500, so 5000 should be enough buffer - public static final long ALL_PARTICLES_MAX_MILLIS = 5000L; - - public LongSupplier currentTimeMillis = System::currentTimeMillis; - public BooleanSupplier kingsScentPresent = this::isKingsScentPresent; - public BooleanSupplier keyInInventory = this::isKeyInInventory; - public interface CrystalEnumSetSupplier { - EnumSet<Crystal> getAsCrystalEnumSet(); - } - public CrystalEnumSetSupplier foundCrystals = this::getFoundCrystals; - - private SolverState solverState; - private Compass firstCompass; - private Compass secondCompass; - private Line solutionIntersectionLine; - private EnumSet<CompassTarget> possibleTargets; - private Vec3Comparable solution; - private Vec3Comparable originalSolution; - private EnumSet<CompassTarget> solutionPossibleTargets; - - public SolverState getSolverState() { - return solverState; - } - - public Vec3i getSolutionCoords() { - return new Vec3i(solution.xCoord, solution.yCoord, solution.zCoord); - } - - public EnumSet<CompassTarget> getPossibleTargets() { - return possibleTargets; + // 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"); - solverState = SolverState.NOT_STARTED; - firstCompass = null; - secondCompass = null; + resetForNewCompass(); + firstCompassLine = null; + secondCompassLine = null; solutionIntersectionLine = null; - possibleTargets = null; + prevPlayerPos = null; solution = null; - originalSolution = null; - solutionPossibleTargets = null; - } - - public void initWorld() { - resetForNewTarget(); } @SubscribeEvent public void onWorldLoad(WorldEvent.Unload event) { - initWorld(); + resetForNewTarget(); isSkytilsPresent = Loader.isModLoaded("skytils"); } @@ -162,101 +90,28 @@ public class CrystalWishingCompassSolver { return; } - BlockPos playerPos = mc.thePlayer.getPosition().getImmutable(); - try { - HandleCompassResult result = handleCompassUse(playerPos); - switch (result) { - case SUCCESS: - return; - case STILL_PROCESSING_PRIOR_USE: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "[NEU] Wait a little longer before using the wishing compass again.")); - event.setCanceled(true); - break; - case LOCATION_TOO_CLOSE: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "[NEU] Move a little further before using the wishing compass again.")); - event.setCanceled(true); - break; - case POSSIBLE_TARGETS_CHANGED: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "[NEU] Possible wishing compass targets have changed. Solver has been reset.")); - event.setCanceled(true); - break; - case NO_PARTICLES_FOR_PREVIOUS_COMPASS: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "[NEU] No particles detected for prior compass use. Need another position to solve.")); - break; - case PLAYER_IN_NUCLEUS: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "[NEU] Wishing compass must be used outside the nucleus for accurate results.")); - event.setCanceled(true); - break; - default: - throw new IllegalStateException("Unexpected wishing compass solver state: \n" + getDiagnosticMessage()); + 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(); - event.setCanceled(true); - solverState = SolverState.FAILED_EXCEPTION; } } - public HandleCompassResult handleCompassUse(BlockPos playerPos) { - long lastCompassUsedMillis = 0; - switch (solverState) { - case PROCESSING_SECOND_USE: - if (secondCompass != null) { - lastCompassUsedMillis = secondCompass.whenUsedMillis; - } - case PROCESSING_FIRST_USE: - if (lastCompassUsedMillis == 0 && firstCompass != null) { - lastCompassUsedMillis = firstCompass.whenUsedMillis; - } - if (lastCompassUsedMillis != 0 && - (currentTimeMillis.getAsLong() > lastCompassUsedMillis + ALL_PARTICLES_MAX_MILLIS)) { - return HandleCompassResult.NO_PARTICLES_FOR_PREVIOUS_COMPASS; - } - - return HandleCompassResult.STILL_PROCESSING_PRIOR_USE; - case SOLVED: - case FAILED_EXCEPTION: - case FAILED_TIMEOUT_NO_REPEATING: - case FAILED_TIMEOUT_NO_PARTICLES: - case FAILED_INTERSECTION_CALCULATION: - case FAILED_INVALID_SOLUTION: - resetForNewTarget(); - // falls through, NOT_STARTED is the state when resetForNewTarget returns - case NOT_STARTED: - if (NUCLEUS_BB.isVecInside(new Vec3Comparable(playerPos.getX(), playerPos.getY(), playerPos.getZ()))) { - return HandleCompassResult.PLAYER_IN_NUCLEUS; - } - - firstCompass = new Compass(playerPos, currentTimeMillis.getAsLong()); - solverState = SolverState.PROCESSING_FIRST_USE; - possibleTargets = calculatePossibleTargets(playerPos); - return HandleCompassResult.SUCCESS; - case NEED_SECOND_COMPASS: - if (firstCompass.whereUsed.distanceSq(playerPos) < MINIMUM_DISTANCE_SQ_BETWEEN_COMPASSES) { - return HandleCompassResult.LOCATION_TOO_CLOSE; - } - - if (!possibleTargets.equals(calculatePossibleTargets(playerPos))) { - resetForNewTarget(); - return HandleCompassResult.POSSIBLE_TARGETS_CHANGED; - } - - secondCompass = new Compass(playerPos, currentTimeMillis.getAsLong()); - solverState = SolverState.PROCESSING_SECOND_USE; - return HandleCompassResult.SUCCESS; - } - - throw new IllegalStateException("Unexpected compass state" ); - } - /* * Processes particles if the wishing compass was used within the last 5 seconds. * @@ -287,701 +142,188 @@ public class CrystalWishingCompassSolver { ) { if (!NotEnoughUpdates.INSTANCE.config.mining.wishingCompassSolver || particleType != EnumParticleTypes.VILLAGER_HAPPY || - !SBInfo.getInstance().getLocation().equals("crystal_hollows")) { + !SBInfo.getInstance().getLocation().equals("crystal_hollows") || + isSolved() || + System.currentTimeMillis() - compassUsedMillis > 5000) { return; } try { - SolverState originalSolverState = solverState; - solveUsingParticle(x, y, z, currentTimeMillis.getAsLong()); - if (solverState != originalSolverState) { - switch (solverState) { - case SOLVED: - showSolution(); - break; - case FAILED_EXCEPTION: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] Unable to determine wishing compass target.")); - logDiagnosticData(false); - break; - case FAILED_TIMEOUT_NO_REPEATING: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] Timed out waiting for repeat set of compass particles.")); - logDiagnosticData(false); - break; - case FAILED_TIMEOUT_NO_PARTICLES: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] Timed out waiting for compass particles.")); - logDiagnosticData(false); - break; - case FAILED_INTERSECTION_CALCULATION: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] Unable to determine intersection of wishing compasses.")); - logDiagnosticData(false); - break; - case FAILED_INVALID_SOLUTION: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] Failed to find solution.")); - logDiagnosticData(false); - break; - case NEED_SECOND_COMPASS: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "[NEU] Need another position to determine wishing compass target.")); - break; - } - } - } catch (Exception e) { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] Exception while calculating wishing compass solution - see log for details")); - e.printStackTrace(); - } - } - - /** - * - * @param x Particle x coordinate - * @param y Particle y coordinate - * @param z Particle z coordinate - */ - public void solveUsingParticle(double x, double y, double z, long currentTimeMillis) { - Compass currentCompass; - switch (solverState) { - case PROCESSING_FIRST_USE: - currentCompass = firstCompass; - break; - case PROCESSING_SECOND_USE: - currentCompass = secondCompass; - break; - default: - return; - } - - currentCompass.processParticle(x, y, z, currentTimeMillis); - switch (currentCompass.compassState) { - case FAILED_TIMEOUT_NO_PARTICLES: - solverState = SolverState.FAILED_TIMEOUT_NO_PARTICLES; + Vec3 particleVec = new Vec3(x, y, z); + if (firstParticle == null) { + firstParticle = particleVec; return; - case FAILED_TIMEOUT_NO_REPEATING: - solverState = SolverState.FAILED_TIMEOUT_NO_REPEATING; - return; - case WAITING_FOR_FIRST_PARTICLE: - case COMPUTING_LAST_PARTICLE: - return; - case COMPLETED: - if (solverState == SolverState.NEED_SECOND_COMPASS) { - return; - } - if (solverState == SolverState.PROCESSING_FIRST_USE) { - solverState = SolverState.NEED_SECOND_COMPASS; - return; - } - break; - } - - // First and Second compasses have completed - solutionIntersectionLine = firstCompass.line.getIntersectionLineSegment(secondCompass.line); - - if (solutionIntersectionLine == null) { - solverState = SolverState.FAILED_INTERSECTION_CALCULATION; - return; - } - - solution = new Vec3Comparable(solutionIntersectionLine.getMidpoint()); - - Vec3Comparable firstDirection = firstCompass.getDirection(); - Vec3Comparable firstSolutionDirection = firstCompass.getDirectionTo(solution); - Vec3Comparable secondDirection = secondCompass.getDirection(); - Vec3Comparable secondSolutionDirection = secondCompass.getDirectionTo(solution); - if (!firstDirection.signumEquals(firstSolutionDirection) || - !secondDirection.signumEquals(secondSolutionDirection) || - !HOLLOWS_BB.isVecInside(solution)) { - solverState = SolverState.FAILED_INVALID_SOLUTION; - return; - } - - solutionPossibleTargets = getSolutionTargets(possibleTargets, solution); - - // Adjust the Jungle Temple solution coordinates - if (solutionPossibleTargets.size() == 1 && - solutionPossibleTargets.contains(CompassTarget.JUNGLE_TEMPLE)) { - originalSolution = solution; - solution = solution.addVector(-57, 36, -21); - } - - solverState = SolverState.SOLVED; - } - - private boolean isKeyInInventory() - { - for (ItemStack item : mc.thePlayer.inventory.mainInventory){ - if (item != null && item.getDisplayName().contains("Jungle Key")) { - return true; } - } - return false; - } - - private boolean isKingsScentPresent() - { - return SBInfo.getInstance().footer.getUnformattedText().contains("King's Scent I"); - } - - private EnumSet<Crystal> getFoundCrystals() { - EnumSet<Crystal> foundCrystals = EnumSet.noneOf(Crystal.class); - NEUConfig.HiddenProfileSpecific perProfileConfig = NotEnoughUpdates.INSTANCE.config.getProfileSpecific(); - if (perProfileConfig == null) return foundCrystals; - HashMap<String, Integer> crystals = perProfileConfig.crystals; - for (String crystalName : crystals.keySet()) { - Integer crystalState = crystals.get(crystalName); - if (crystalState != null && crystalState > 0) { - foundCrystals.add(Crystal.valueOf(crystalName.toUpperCase())); - } - } - - return foundCrystals; - } - - // Returns candidates based on seen Y coordinates and quadrants that - // are not adjacent to the solution's quadrant. If the solution is - // the nucleus then a copy of the original possible targets is - // returned. - // - // NOTE: Adjacent quadrant filtering could be improved based on - // structure sizes in the future to only allow a certain - // distance into the adjacent quadrant. - // - // |----------|------------| - // | Jungle | Mithril | - // | | Deposits | - // |----------|----------- | - // | Goblin | Precursor | - // | Holdout | Deposits | - // |----------|------------| - static public EnumSet<CompassTarget> getSolutionTargets( - EnumSet<CompassTarget> possibleTargets, - Vec3Comparable solution) { - EnumSet<CompassTarget> solutionPossibleTargets; - solutionPossibleTargets = possibleTargets.clone(); - - if (NUCLEUS_BB.isVecInside(solution)) { - return solutionPossibleTargets; - } - - solutionPossibleTargets.remove(CompassTarget.CRYSTAL_NUCLEUS); - - // Eliminate non-adjacent zones first - if (MITHRIL_DEPOSITS_BB.isVecInside(solution)) { - solutionPossibleTargets.remove(CompassTarget.GOBLIN_KING); - solutionPossibleTargets.remove(CompassTarget.GOBLIN_QUEEN); - } else if (PRECURSOR_REMNANTS_BB.isVecInside(solution)) { - solutionPossibleTargets.remove(CompassTarget.ODAWA); - solutionPossibleTargets.remove(CompassTarget.JUNGLE_TEMPLE); - } else if (GOBLIN_HOLDOUT_BB.isVecInside(solution)) { - solutionPossibleTargets.remove(CompassTarget.MINES_OF_DIVAN); - } else if (JUNGLE_BB.isVecInside(solution)) { - solutionPossibleTargets.remove(CompassTarget.PRECURSOR_CITY); - } - // If there's only 1 possible target then don't remove based - // on Y coordinates since assumptions about Y coordinates could - // be wrong. - if (solutionPossibleTargets.size() > 1) { - // Y coordinates are 43-70 from 11 samples - if (solutionPossibleTargets.contains(CompassTarget.BAL) && - solution.yCoord > 72) { - solutionPossibleTargets.remove(CompassTarget.BAL); + double distanceFromFirst = particleVec.distanceTo(firstParticle); + if (distanceFromFirst > MAX_COMPASS_PARTICLE_SPREAD) { + return; } - // Y coordinates are 93-157 from 10 samples, may be able to filter - // more based on the offset of the King within the structure - if (solutionPossibleTargets.contains(CompassTarget.GOBLIN_KING) && - solution.yCoord < 64) { - solutionPossibleTargets.remove(CompassTarget.GOBLIN_KING); + if (distanceFromFirst >= lastParticleDistanceFromFirst) { + lastParticleDistanceFromFirst = distanceFromFirst; + lastParticle = particleVec; + return; } - // Y coordinates are 129-139 from 10 samples - if (solutionPossibleTargets.contains(CompassTarget.GOBLIN_QUEEN) && - (solution.yCoord < 127 || solution.yCoord > 141)) { - solutionPossibleTargets.remove(CompassTarget.GOBLIN_QUEEN); + // 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; } - // Y coordinates are 72-80 from 10 samples - if (solutionPossibleTargets.contains(CompassTarget.JUNGLE_TEMPLE) && - (solution.yCoord < 70 || solution.yCoord > 82)) { - solutionPossibleTargets.remove(CompassTarget.JUNGLE_TEMPLE); + 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; } - // Y coordinates are 110-128 from 3 samples, not enough data to use - if (solutionPossibleTargets.contains(CompassTarget.ODAWA) && - solution.yCoord < 64) { - solutionPossibleTargets.remove(CompassTarget.ODAWA); - } + solution = solutionIntersectionLine.getMidpoint(); - // Y coordinates are 122-129 from 8 samples - if (solutionPossibleTargets.contains(CompassTarget.PRECURSOR_CITY) && - (solution.yCoord < 119 || solution.yCoord > 132)) { - solutionPossibleTargets.remove(CompassTarget.PRECURSOR_CITY); + if (solution.distanceTo(firstParticle) < 8) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] WARNING: Solution is likely incorrect.")); + logDiagnosticData(false); + return; } - // Y coordinates are 98-102 from 15 samples - if (solutionPossibleTargets.contains(CompassTarget.MINES_OF_DIVAN) && - (solution.yCoord < 96 || solution.yCoord > 104)) { - solutionPossibleTargets.remove(CompassTarget.MINES_OF_DIVAN); - } + showSolution(); + } catch (Exception e) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Exception while calculating wishing compass solution - see log for details")); + e.printStackTrace(); } - - return solutionPossibleTargets; } - private EnumSet<CompassTarget> calculatePossibleTargets(BlockPos playerPos) { - boolean targetsBasedOnZoneWithoutCrystal = false; - EnumSet<CompassTarget> candidateTargets = EnumSet.allOf(CompassTarget.class); - EnumSet<Crystal> foundCrystals = this.foundCrystals.getAsCrystalEnumSet(); - Vec3Comparable playerPosVec = new Vec3Comparable(playerPos); - - // If the current zone's crystal hasn't been found then remove all non-nucleus candidates other - // than the ones in the current zone. The one exception is that the king is kept when in the jungle - // since the compass can point to the king if odawa is missing (which often happens). - // The nucleus is kept since it can be returned if the structure for the current zone is missing. - if (GOBLIN_HOLDOUT_BB.isVecInside(playerPosVec) && !foundCrystals.contains(Crystal.AMBER)) { - candidateTargets.clear(); - candidateTargets.add(CompassTarget.CRYSTAL_NUCLEUS); - candidateTargets.add(CompassTarget.GOBLIN_KING); - candidateTargets.add(CompassTarget.GOBLIN_QUEEN); - targetsBasedOnZoneWithoutCrystal = true; - } - - if (JUNGLE_BB.isVecInside(playerPosVec) && !foundCrystals.contains(Crystal.AMETHYST)) { - candidateTargets.clear(); - candidateTargets.add(CompassTarget.CRYSTAL_NUCLEUS); - candidateTargets.add(CompassTarget.ODAWA); - candidateTargets.add(CompassTarget.JUNGLE_TEMPLE); - if (!keyInInventory.getAsBoolean() && !kingsScentPresent.getAsBoolean()) { - // If Odawa is missing then the king may be returned - candidateTargets.add(CompassTarget.GOBLIN_KING); - } - targetsBasedOnZoneWithoutCrystal = true; - } - - if (MITHRIL_DEPOSITS_BB.isVecInside(playerPosVec) && !foundCrystals.contains(Crystal.JADE)) { - candidateTargets.clear(); - candidateTargets.add(CompassTarget.CRYSTAL_NUCLEUS); - candidateTargets.add(CompassTarget.MINES_OF_DIVAN); - targetsBasedOnZoneWithoutCrystal = true; - } - - if (PRECURSOR_REMNANTS_BB.isVecInside(playerPosVec) && !foundCrystals.contains(Crystal.SAPPHIRE)) { - candidateTargets.clear(); - candidateTargets.add(CompassTarget.CRYSTAL_NUCLEUS); - candidateTargets.add(CompassTarget.PRECURSOR_CITY); - targetsBasedOnZoneWithoutCrystal = true; - } - - if (MAGMA_FIELDS_BB.isVecInside(playerPosVec) && !foundCrystals.contains(Crystal.TOPAZ)) { - candidateTargets.clear(); - candidateTargets.add(CompassTarget.CRYSTAL_NUCLEUS); - candidateTargets.add(CompassTarget.BAL); - targetsBasedOnZoneWithoutCrystal = true; - } - - if (!targetsBasedOnZoneWithoutCrystal) { - // Filter out crystal-based targets outside the current zone - if (foundCrystals.contains(Crystal.AMBER)) { - candidateTargets.remove(CompassTarget.GOBLIN_KING); - candidateTargets.remove(CompassTarget.GOBLIN_QUEEN); - } - - if (foundCrystals.contains(Crystal.AMETHYST)) { - candidateTargets.remove(CompassTarget.ODAWA); - candidateTargets.remove(CompassTarget.JUNGLE_TEMPLE); - } - - if (foundCrystals.contains(Crystal.JADE)) { - candidateTargets.remove(CompassTarget.MINES_OF_DIVAN); - } - - if (foundCrystals.contains(Crystal.TOPAZ)) { - candidateTargets.remove(CompassTarget.BAL); - } - - if (foundCrystals.contains(Crystal.SAPPHIRE)) { - candidateTargets.remove(CompassTarget.PRECURSOR_CITY); - } - } - - candidateTargets.remove(kingsScentPresent.getAsBoolean() ? CompassTarget.GOBLIN_KING : CompassTarget.GOBLIN_QUEEN); - candidateTargets.remove(keyInInventory.getAsBoolean() ? CompassTarget.ODAWA : CompassTarget.JUNGLE_TEMPLE); - - return candidateTargets; + private boolean isSolved() { + return solution != null; } - private String getFriendlyNameForCompassTarget(CompassTarget compassTarget) { - switch (compassTarget) { - case BAL: return EnumChatFormatting.RED + "Bal"; - case ODAWA: return EnumChatFormatting.GREEN + "Odawa"; - case JUNGLE_TEMPLE: return EnumChatFormatting.AQUA + "the " + - EnumChatFormatting.GREEN + "Jungle Temple"; - case GOBLIN_KING: return EnumChatFormatting.GOLD + "King Yolkar"; - case GOBLIN_QUEEN: return EnumChatFormatting.AQUA + "the " + - EnumChatFormatting.YELLOW + "Goblin Queen"; - case PRECURSOR_CITY: return EnumChatFormatting.AQUA + "the " + - EnumChatFormatting.WHITE + "Precursor City"; - case MINES_OF_DIVAN: return EnumChatFormatting.AQUA + "the " + - EnumChatFormatting.BLUE + "Mines of Divan"; - default: return EnumChatFormatting.WHITE + "an undetermined location"; - } - } + 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); - private String getNameForCompassTarget(CompassTarget compassTarget) { - boolean useSkytilsNames = (NotEnoughUpdates.INSTANCE.config.mining.wishingCompassWaypointNameType == 1); - switch (compassTarget) { - case BAL: return useSkytilsNames ? "internal_bal" : "Bal"; - case ODAWA: return "Odawa"; - case JUNGLE_TEMPLE: return useSkytilsNames ? "internal_temple" : "Temple"; - case GOBLIN_KING: return useSkytilsNames ? "internal_king" : "King"; - case GOBLIN_QUEEN: return useSkytilsNames ? "internal_den" : "Queen"; - case PRECURSOR_CITY: return useSkytilsNames ? "internal_city" : "City"; - case MINES_OF_DIVAN: return useSkytilsNames ? "internal_mines" : "Mines"; - default: return "WishingTarget"; + if (NUCLEUS_BB.isVecInside(solution)) { + description += "Crystal Nucleus (" + coordsText + ")"; + } else { + description += coordsText; } - } - private String getSolutionCoordsText() { - return solution == null ? "" : - String.format("%.0f %.0f %.0f", solution.xCoord, solution.yCoord, solution.zCoord); - } + ChatComponentText message = new ChatComponentText(EnumChatFormatting.YELLOW + description); - private String getWishingCompassDestinationsMessage() { - StringBuilder sb = new StringBuilder(); - sb.append(EnumChatFormatting.YELLOW); - sb.append("[NEU] "); - sb.append(EnumChatFormatting.AQUA); - sb.append("Wishing compass points to "); - int index = 1; - for (CompassTarget target : solutionPossibleTargets) { - if (index > 1) { - sb.append(EnumChatFormatting.AQUA); - if (index == solutionPossibleTargets.size()) { - sb.append(" or "); - } else { - sb.append(", "); - } - } - sb.append(getFriendlyNameForCompassTarget(target)); - index++; + 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); } - sb.append(EnumChatFormatting.AQUA); - sb.append(" ("); - sb.append(getSolutionCoordsText()); - sb.append(")"); - return sb.toString(); + Minecraft.getMinecraft().thePlayer.addChatMessage(message); } - private void showSolution() { - if (solution == null) return; - - if (NUCLEUS_BB.isVecInside(solution)) { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + "[NEU] " + - EnumChatFormatting.AQUA + "Wishing compass target is the Crystal Nucleus")); + 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; } - String destinationMessage = getWishingCompassDestinationsMessage(); - - if (!isSkytilsPresent) { - mc.thePlayer.addChatMessage(new ChatComponentText(destinationMessage)); + if (!NotEnoughUpdates.INSTANCE.config.mining.wishingCompassSolver) + { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Wishing Compass Solver is not enabled.")); return; } - String targetNameForSkytils = solutionPossibleTargets.size() == 1 ? - getNameForCompassTarget(solutionPossibleTargets.iterator().next()) : - "WishingTarget"; - String skytilsCommand = String.format("/sthw add %s %s", targetNameForSkytils, getSolutionCoordsText()); - if (NotEnoughUpdates.INSTANCE.config.mining.wishingCompassAutocreateKnownWaypoints && - solutionPossibleTargets.size() == 1) { - mc.thePlayer.addChatMessage(new ChatComponentText(destinationMessage)); - int commandResult = ClientCommandHandler.instance.executeCommand(mc.thePlayer, skytilsCommand); - if (commandResult == 1) - { - return; - } - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[NEU] Failed to automatically run /sthw")); + if (!outputAlways && !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.WISHING)) { + return; } - destinationMessage += EnumChatFormatting.YELLOW + " [Add Skytils Waypoint]"; - ChatComponentText chatMessage = new ChatComponentText(destinationMessage); - chatMessage.setChatStyle(Utils.createClickStyle(ClickEvent.Action.RUN_COMMAND, - skytilsCommand, - EnumChatFormatting.YELLOW + "Set waypoint for wishing target\n")); - mc.thePlayer.addChatMessage(chatMessage); - } + boolean originalDebugFlag = !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.add(NEUDebugFlag.WISHING); - private String getDiagnosticMessage() { StringBuilder diagsMessage = new StringBuilder(); + diagsMessage.append("\n"); diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Solver State: "); + diagsMessage.append("Skytils Present: "); diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(solverState.name()); + diagsMessage.append(isSkytilsPresent); diagsMessage.append("\n"); - if (firstCompass == null) { - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("First Compass: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append("<NONE>"); - diagsMessage.append("\n"); - } else { - firstCompass.appendCompassDiagnostics(diagsMessage, "First Compass"); - } - - if (secondCompass == null) { - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Second Compass: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append("<NONE>"); - diagsMessage.append("\n"); - } else { - secondCompass.appendCompassDiagnostics(diagsMessage, "Second Compass"); - } - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Intersection Line: "); + diagsMessage.append("Compass Used Millis: "); diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append((solutionIntersectionLine == null) ? "<NONE>" : solutionIntersectionLine); + diagsMessage.append(compassUsedMillis); diagsMessage.append("\n"); diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Jungle Key in Inventory: "); + diagsMessage.append("Compass Used Position: "); diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(isKeyInInventory()); + diagsMessage.append((prevPlayerPos == null) ? "<NONE>" : prevPlayerPos.toString()); diagsMessage.append("\n"); diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("King's Scent Present: "); + diagsMessage.append("First Particle: "); diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(isKingsScentPresent()); + diagsMessage.append((firstParticle == null) ? "<NONE>" : firstParticle.toString()); diagsMessage.append("\n"); diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("First Compass Targets: "); + diagsMessage.append("Last Particle: "); diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(possibleTargets == null ? "<NONE>" : possibleTargets.toString()); + diagsMessage.append((lastParticle == null) ? "<NONE>" : lastParticle.toString()); diagsMessage.append("\n"); diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Current Calculated Targets: "); + diagsMessage.append("Last Particle Distance From First: "); diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(calculatePossibleTargets(mc.thePlayer.getPosition())); + diagsMessage.append(lastParticleDistanceFromFirst); diagsMessage.append("\n"); diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Found Crystals: "); + diagsMessage.append("First Compass Line: "); diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(getFoundCrystals()); + diagsMessage.append((firstCompassLine == null) ? "<NONE>" : firstCompassLine.toString()); diagsMessage.append("\n"); - if (originalSolution != null) { - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Original Solution: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(originalSolution); - diagsMessage.append("\n"); - } - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Solution: "); + diagsMessage.append("Second Compass Line: "); diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append((solution == null) ? "<NONE>" : solution.toString()); + diagsMessage.append((secondCompassLine == null) ? "<NONE>" : secondCompassLine.toString()); diagsMessage.append("\n"); diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Solution Targets: "); + diagsMessage.append("Intersection Line: "); diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append((solutionPossibleTargets == null) ? "<NONE>" : solutionPossibleTargets.toString()); + diagsMessage.append((secondCompassLine == null) ? "<NONE>" : solutionIntersectionLine); diagsMessage.append("\n"); - return diagsMessage.toString(); - } - - public void logDiagnosticData(boolean outputAlways) { - if (!SBInfo.getInstance().checkForSkyblockLocation()) { - return; - } - - if (!NotEnoughUpdates.INSTANCE.config.mining.wishingCompassSolver) - { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] Wishing Compass Solver is not enabled.")); - return; - } - - boolean wishingDebugFlagSet = NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.WISHING); - if (outputAlways || wishingDebugFlagSet) { - NEUDebugLogger.logAlways(getDiagnosticMessage()); - } - } - - enum CompassState { - WAITING_FOR_FIRST_PARTICLE, - COMPUTING_LAST_PARTICLE, - COMPLETED, - FAILED_TIMEOUT_NO_REPEATING, - FAILED_TIMEOUT_NO_PARTICLES, - } - - enum HandleCompassResult { - SUCCESS, - LOCATION_TOO_CLOSE, - STILL_PROCESSING_PRIOR_USE, - POSSIBLE_TARGETS_CHANGED, - NO_PARTICLES_FOR_PREVIOUS_COMPASS, - PLAYER_IN_NUCLEUS - } - - static class Compass { - public CompassState compassState; - public Line line = null; - - private final BlockPos whereUsed; - private final long whenUsedMillis; - private Vec3Comparable firstParticle = null; - private Vec3Comparable previousParticle = null; - private Vec3Comparable lastParticle = null; - private final ArrayList<ProcessedParticle> processedParticles; - - Compass(BlockPos whereUsed, long whenUsedMillis) { - this.whereUsed = whereUsed; - this.whenUsedMillis = whenUsedMillis; - compassState = CompassState.WAITING_FOR_FIRST_PARTICLE; - processedParticles = new ArrayList<>(); - } - - public Vec3Comparable getDirection() { - if (firstParticle == null || lastParticle == null) { - return null; - } - - return new Vec3Comparable(firstParticle.subtractReverse(lastParticle).normalize()); - } - - public Vec3Comparable getDirectionTo(Vec3Comparable target) { - if (firstParticle == null || target == null) { - return null; - } - - return new Vec3Comparable(firstParticle.subtractReverse(target).normalize()); - } - - public double particleSpread() { - if (firstParticle == null || lastParticle == null) { - return 0.0; - } - return firstParticle.distanceTo(lastParticle); - } - - public void processParticle(double x, double y, double z, long particleTimeMillis) { - if (compassState == CompassState.FAILED_TIMEOUT_NO_REPEATING || - compassState == CompassState.FAILED_TIMEOUT_NO_PARTICLES || - compassState == CompassState.COMPLETED) { - throw new UnsupportedOperationException("processParticle should not be called in a failed or completed state"); - } - - if (particleTimeMillis - this.whenUsedMillis > ALL_PARTICLES_MAX_MILLIS) { - // Assume we have failed if we're still trying to process particles - compassState = CompassState.FAILED_TIMEOUT_NO_REPEATING; - return; - } - - Vec3Comparable currentParticle = new Vec3Comparable(x, y, z); - if (compassState == CompassState.WAITING_FOR_FIRST_PARTICLE) { - if (currentParticle.distanceTo(new Vec3Comparable(whereUsed)) < MAX_DISTANCE_FROM_USE_TO_FIRST_PARTICLE) { - processedParticles.add(new ProcessedParticle(currentParticle, particleTimeMillis)); - firstParticle = currentParticle; - previousParticle = currentParticle; - compassState = CompassState.COMPUTING_LAST_PARTICLE; - } - return; - } - - // State is COMPUTING_LAST_PARTICLE, keep updating the previousParticle until - // the first particle in the second sequence is seen. - if (currentParticle.distanceTo(previousParticle) <= MAX_DISTANCE_BETWEEN_PARTICLES) { - processedParticles.add(new ProcessedParticle(currentParticle, particleTimeMillis)); - previousParticle = currentParticle; - return; - } - - if (currentParticle.distanceTo(firstParticle) > MAX_DISTANCE_BETWEEN_PARTICLES) { - return; - } - - // It's a repeating particle - processedParticles.add(new ProcessedParticle(currentParticle, particleTimeMillis)); - lastParticle = previousParticle; - line = new Line(firstParticle, lastParticle); - compassState = CompassState.COMPLETED; - } - - public void appendCompassDiagnostics(StringBuilder diagsMessage, String compassName) { - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Compass State: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(compassState.name()); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append(compassName); - diagsMessage.append(" Used Millis: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(whenUsedMillis); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append(compassName); - diagsMessage.append(" Used Position: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append((whereUsed == null) ? "<NONE>" : whereUsed.toString()); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append(compassName); - diagsMessage.append(" All Seen Particles: \n"); - diagsMessage.append(EnumChatFormatting.WHITE); - for (ProcessedParticle particle : processedParticles) { - diagsMessage.append(particle.toString()); - diagsMessage.append("\n"); - } - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append(compassName); - diagsMessage.append(" Particle Spread: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(particleSpread()); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append(compassName); - diagsMessage.append(" Compass Line: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append((line == null) ? "<NONE>" : line.toString()); - diagsMessage.append("\n"); - } - - static class ProcessedParticle { - Vec3Comparable coords; - long particleTimeMillis; + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Solution: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((solution == null) ? "<NONE>" : solution.toString()); + diagsMessage.append("\n"); - ProcessedParticle(Vec3Comparable coords, long particleTimeMillis) { - this.coords = coords; - this.particleTimeMillis = particleTimeMillis; - } + NEUDebugLogger.log(NEUDebugFlag.WISHING, diagsMessage.toString()); - @Override - public String toString() { - return coords.toString() + " " + particleTimeMillis; - } + if (!originalDebugFlag) { + NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.remove(NEUDebugFlag.WISHING); } } } 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 f793baf0..6cfb8c04 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 @@ -641,42 +641,6 @@ public class Mining { @ConfigAccordionId(id = 6) public int crystalHollowNoneColor = 12; - @ConfigOption( - name = "Wishing Compass Solver", - desc = "" - ) - @ConfigEditorAccordion(id = 7) - public boolean wishingCompassSolverAccordion = false; - - @Expose - @ConfigOption( - name = "Enable Solver", - desc = "Show wishing compass target coordinates based on two samples" - ) - @ConfigAccordionId(id = 7) - @ConfigEditorBoolean - public boolean wishingCompassSolver = true; - - @Expose - @ConfigOption( - name = "Skytils Waypoints", - desc = "Automatically create Skytils waypoints for well-known targets" - ) - @ConfigAccordionId(id = 7) - @ConfigEditorBoolean - public boolean wishingCompassAutocreateKnownWaypoints = false; - - @Expose - @ConfigOption( - name = "Waypoint Type", - desc = "Skytils Waypoint name type. Skytils Built-in will be overwritten by Skytils when the waypoint is nearby." - ) - @ConfigAccordionId(id = 7) - @ConfigEditorDropdown( - values = {"NEU", "Skytils"} - ) - public int wishingCompassWaypointNameType = 0; - @Expose @ConfigOption( name = "Puzzler Solver", @@ -701,11 +665,19 @@ 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 = "" ) - @ConfigEditorAccordion(id = 8) + @ConfigEditorAccordion(id = 7) public boolean texturesAccordion = false; @Expose @@ -713,7 +685,7 @@ public class Mining { name = "Dwarven Mines Textures", desc = "Allows texture packs to retexture blocks in the Dwarven Mines. If you don't have a texture pack that does this, you should leave this off" ) - @ConfigAccordionId(id = 8) + @ConfigAccordionId(id = 7) @ConfigEditorBoolean public boolean dwarvenTextures = false; @Expose @@ -721,7 +693,7 @@ public class Mining { name = "Crystal Hollows Textures", desc = "Allows texture packs to retexture blocks in the Crystal Hollows. If you don't have a texture pack that does this, you should leave this off" ) - @ConfigAccordionId(id = 8) + @ConfigAccordionId(id = 7) @ConfigEditorBoolean public boolean crystalHollowTextures = false; @@ -730,7 +702,7 @@ public class Mining { name = "Replace Gemstone sounds", desc = "Replace the break sounds of crystals in the Crystal Hollows. Requires a texture pack with this feature" ) - @ConfigAccordionId(id = 8) + @ConfigAccordionId(id = 7) @ConfigEditorBoolean public boolean gemstoneSounds = false; @@ -739,7 +711,7 @@ public class Mining { name = "Replace Mithril sounds", desc = "Replace the break sounds of mithril and titanium in the Dwarven mines. Requires a texture pack with this feature" ) - @ConfigAccordionId(id = 8) + @ConfigAccordionId(id = 7) @ConfigEditorBoolean public boolean mithrilSounds = false; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java index dafbe202..48410404 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java @@ -107,6 +107,7 @@ public class Misc { @Expose @ConfigOption( + name = "Edit Enchant Colours", desc = "Change the colours of certain skyblock enchants (/neuec)" ) diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/ProfileViewer.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/ProfileViewer.java index 2e1d0619..04fe6e6b 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/ProfileViewer.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/ProfileViewer.java @@ -49,7 +49,7 @@ public class ProfileViewer { "\u00a7eMining", "\u00a7eBingo", }, - allowRemovingElements = false + allowDeleting = false ) public List<Integer> pageLayout = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7)); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BingoPage.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BingoPage.java index 60d1e513..6108901c 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BingoPage.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BingoPage.java @@ -298,12 +298,14 @@ public class BingoPage { NotEnoughUpdates.INSTANCE.manager.hypixelApi.getHypixelApiAsync( NotEnoughUpdates.INSTANCE.config.apiKey.apiKey, "resources/skyblock/bingo", - args - ).thenAccept(jsonObject -> { - if (jsonObject.has("success") && jsonObject.get("success").getAsBoolean()) { - bingoGoals = jsonArrayToJsonObjectList(jsonObject.get("goals").getAsJsonArray()); - currentEventId = jsonObject.get("id").getAsInt(); - } - }); + args, + jsonObject -> { + if (jsonObject.has("success") && jsonObject.get("success").getAsBoolean()) { + bingoGoals = jsonArrayToJsonObjectList(jsonObject.get("goals").getAsJsonArray()); + currentEventId = jsonObject.get("id").getAsInt(); + } + }, + () -> {} + ); } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java index 898f9e11..11c10ea0 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java @@ -67,6 +67,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.TimeZone; import java.util.TreeMap; @@ -1956,6 +1957,12 @@ public class GuiProfileViewer extends GuiScreen { } } + NBTTagCompound nbt = new NBTTagCompound(); //Adding NBT Data for Custom Resource Packs + NBTTagCompound display = new NBTTagCompound(); + display.setString("Name", skillName); + nbt.setTag("display", display); + stack.setTagCompound(nbt); + GL11.glTranslatef((x), (y - 6f), 0); GL11.glScalef(0.7f, 0.7f, 1); Utils.drawItemStackLinear(stack, 0, 0); @@ -1964,11 +1971,32 @@ public class GuiProfileViewer extends GuiScreen { } private ItemStack getQuestionmarkSkull() { - return Utils.createSkull( - EnumChatFormatting.RED + "Unknown Pet", - "Unknown Pet", - "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmM4ZWExZjUxZjI1M2ZmNTE0MmNhMTFhZTQ1MTkzYTRhZDhjM2FiNWU5YzZlZWM4YmE3YTRmY2I3YmFjNDAifX19" - ); + String textureLink = "bc8ea1f51f253ff5142ca11ae45193a4ad8c3ab5e9c6eec8ba7a4fcb7bac40"; + + String b64Decoded = + "{\"textures\":{\"SKIN\":{\"url\":\"http://textures.minecraft.net/texture/" + textureLink + "\"}}}"; + String b64Encoded = new String(Base64.getEncoder().encode(b64Decoded.getBytes())); + + ItemStack stack = new ItemStack(Items.skull, 1, 3); + NBTTagCompound nbt = new NBTTagCompound(); + NBTTagCompound skullOwner = new NBTTagCompound(); + NBTTagCompound properties = new NBTTagCompound(); + NBTTagList textures = new NBTTagList(); + NBTTagCompound textures_0 = new NBTTagCompound(); + + String uuid = UUID.nameUUIDFromBytes(b64Encoded.getBytes()).toString(); + skullOwner.setString("Id", uuid); + skullOwner.setString("Name", uuid); + + textures_0.setString("Value", b64Encoded); + textures.appendTag(textures_0); + + properties.setTag("textures", textures); + skullOwner.setTag("Properties", properties); + nbt.setTag("SkullOwner", skullOwner); + stack.setTagCompound(nbt); + stack.setStackDisplayName(EnumChatFormatting.RED + "Unknown Pet"); + return stack; } private void drawPetsPage(int mouseX, int mouseY, float partialTicks) { @@ -5303,24 +5331,37 @@ public class GuiProfileViewer extends GuiScreen { } public enum ProfileViewerPage { - LOADING(-1, null), - INVALID_NAME(-1, null), - NO_SKYBLOCK(-1, null), - BASIC(0, new ItemStack(Items.paper)), - DUNGEON(1, new ItemStack(Item.getItemFromBlock(Blocks.deadbush))), - EXTRA(2, new ItemStack(Items.book)), - INVENTORIES(3, new ItemStack(Item.getItemFromBlock(Blocks.ender_chest))), - COLLECTIONS(4, new ItemStack(Items.painting)), - PETS(5, new ItemStack(Items.bone)), - MINING(6, new ItemStack(Items.iron_pickaxe)), - BINGO(7, new ItemStack(Items.filled_map)); + LOADING(), + INVALID_NAME(), + NO_SKYBLOCK(), + BASIC(0, Items.paper, "Your Skills"), + DUNGEON(1, Item.getItemFromBlock(Blocks.deadbush), "Dungeoneering"), + EXTRA(2, Items.book, "Profile Stats"), + INVENTORIES(3, Item.getItemFromBlock(Blocks.ender_chest), "Storage"), + COLLECTIONS(4, Items.painting, "Collections"), + PETS(5, Items.bone, "Pets"), + MINING(6, Items.iron_pickaxe, "Heart of the Mountain"), + BINGO(7, Items.filled_map, "Bingo"); public final ItemStack stack; public final int id; - ProfileViewerPage(int id, ItemStack stack) { + ProfileViewerPage() { + this(-1, null, null); + } + + ProfileViewerPage(int id, Item item, String name) { this.id = id; - this.stack = stack; + if (item == null) { + stack = null; + } else { + stack = new ItemStack(item); + NBTTagCompound nbt = new NBTTagCompound(); //Adding NBT Data for Custom Resource Packs + NBTTagCompound display = new NBTTagCompound(); + display.setString("Name", name); + nbt.setTag("display", display); + stack.setTagCompound(nbt); + } } public static ProfileViewerPage getById(int id) { @@ -5331,6 +5372,11 @@ public class GuiProfileViewer extends GuiScreen { } return null; } + + public Optional<ItemStack> getItem() { + return Optional.ofNullable(stack); + } + } public static class PetLevel { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/NEUDebugLogger.java b/src/main/java/io/github/moulberry/notenoughupdates/util/NEUDebugLogger.java index 827a3e97..d662a872 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/NEUDebugLogger.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/NEUDebugLogger.java @@ -13,8 +13,6 @@ import java.util.function.Consumer; public class NEUDebugLogger { private static final Minecraft mc = Minecraft.getMinecraft(); public static Consumer<String> logMethod = NEUDebugLogger::chatLogger; - // Used to prevent accessing NEUConfig in unit tests - public static boolean allFlagsEnabled = false; private static void chatLogger(String message) { mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + "[NEU DEBUG] " + message)); @@ -25,13 +23,7 @@ public class NEUDebugLogger { } public static void log(NEUDebugFlag flag, String message) { - if (logMethod != null && (allFlagsEnabled || isFlagEnabled(flag))) { - logAlways(message); - } - } - - public static void logAlways(String message) { - if (logMethod != null) { + if (logMethod != null && isFlagEnabled(flag)) { logMethod.accept(message); } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java b/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java index a557eb12..5b7bd055 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java @@ -18,8 +18,6 @@ import net.minecraft.scoreboard.Score; import net.minecraft.scoreboard.ScoreObjective; import net.minecraft.scoreboard.ScorePlayerTeam; import net.minecraft.scoreboard.Scoreboard; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; import net.minecraftforge.client.event.ClientChatReceivedEvent; import net.minecraftforge.client.event.GuiOpenEvent; @@ -59,7 +57,7 @@ public class SBInfo { public String slayer = ""; public boolean stranded = false; - public String mode = null; + public String mode = ""; public Date currentTimeDate = null; @@ -122,16 +120,6 @@ public class SBInfo { } } - public boolean checkForSkyblockLocation() { - if (!NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard() || getLocation() == null) { - Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] This command is not available outside SkyBlock")); - return false; - } - - return true; - } - private static final Pattern PROFILE_PATTERN = Pattern.compile("(?<type>(♲ Ironman)|(☀ Stranded)|()) *Profile: (?<name>[^ ]+)"); |