diff options
Diffstat (limited to 'src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java')
| -rw-r--r-- | src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java new file mode 100644 index 00000000..25f39c3a --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java @@ -0,0 +1,329 @@ +package io.github.moulberry.notenoughupdates.miscfeatures; + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.core.util.Line; +import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; +import io.github.moulberry.notenoughupdates.util.NEUDebugLogger; +import io.github.moulberry.notenoughupdates.util.SBInfo; +import net.minecraft.client.Minecraft; +import net.minecraft.event.ClickEvent; +import net.minecraft.event.HoverEvent; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.BlockPos; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.Vec3; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +public class CrystalWishingCompassSolver { + private static final CrystalWishingCompassSolver INSTANCE = new CrystalWishingCompassSolver(); + public static CrystalWishingCompassSolver getInstance() { + return INSTANCE; + } + + private static final Minecraft mc = Minecraft.getMinecraft(); + private static boolean isSkytilsPresent = false; + + // Crystal Nucleus unbreakable blocks, area coordinates reported by Hypixel server are slightly different + private static final AxisAlignedBB NUCLEUS_BB = new AxisAlignedBB(463, 63, 460, 563, 181, 564); + private static final double MAX_COMPASS_PARTICLE_SPREAD = 16; + + private static BlockPos prevPlayerPos; + private long compassUsedMillis = 0; + private Vec3 firstParticle = null; + private Vec3 lastParticle = null; + private double lastParticleDistanceFromFirst = 0; + private Line firstCompassLine = null; + private Line secondCompassLine = null; + private Vec3 solution = null; + private Line solutionIntersectionLine = null; + + private void resetForNewCompass() { + compassUsedMillis = 0; + firstParticle = null; + lastParticle = null; + lastParticleDistanceFromFirst = 0; + } + + private void resetForNewTarget() { + NEUDebugLogger.log(NEUDebugFlag.WISHING,"Resetting for new target"); + resetForNewCompass(); + firstCompassLine = null; + secondCompassLine = null; + solutionIntersectionLine = null; + prevPlayerPos = null; + solution = null; + } + + @SubscribeEvent + public void onWorldLoad(WorldEvent.Unload event) { + resetForNewTarget(); + isSkytilsPresent = Loader.isModLoaded("skytils"); + } + + @SubscribeEvent + public void onPlayerInteract(PlayerInteractEvent event) { + if (!NotEnoughUpdates.INSTANCE.config.mining.wishingCompassSolver || + SBInfo.getInstance().getLocation() == null || + !SBInfo.getInstance().getLocation().equals("crystal_hollows") || + event.entityPlayer != mc.thePlayer || + (event.action != PlayerInteractEvent.Action.RIGHT_CLICK_AIR && + event.action != PlayerInteractEvent.Action.RIGHT_CLICK_BLOCK) + ) { + return; + } + + ItemStack heldItem = event.entityPlayer.getHeldItem(); + if (heldItem == null || heldItem.getItem() != Items.skull) { + return; + } + + String heldInternalName = NotEnoughUpdates.INSTANCE.manager.getInternalNameForItem(heldItem); + if (heldInternalName == null || !heldInternalName.equals("WISHING_COMPASS")) { + return; + } + + try { + if (isSolved()) { + resetForNewTarget(); + } + + // 64.0 is an arbitrary value but seems to work well + if (prevPlayerPos != null && prevPlayerPos.distanceSq(mc.thePlayer.getPosition()) < 64.0) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] Move a little further before using the wishing compass again.")); + event.setCanceled(true); + return; + } + + prevPlayerPos = mc.thePlayer.getPosition().getImmutable(); + compassUsedMillis = System.currentTimeMillis(); + } catch (Exception e) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Error processing wishing compass action - see log for details")); + e.printStackTrace(); + } + } + + /* + * Processes particles if the wishing compass was used within the last 5 seconds. + * + * The first and the last particles are used to create a line for each wishing compass + * use that is then used to calculate the target. + * + * Once two lines have been calculated, the shortest line between the two is calculated + * with the midpoint on that line being the wishing compass target. The accuracy of this + * seems to be very high. + * + * The target location varies based on various criteria, including, but not limited to: + * Topaz Crystal (Khazad-dûm) Magma Fields + * Odawa (Jungle Village) Jungle w/no Jungle Key in inventory + * Amethyst Crystal (Jungle Temple) Jungle w/Jungle Key in inventory + * Sapphire Crystal (Lost Precursor City) Precursor Remnants + * Jade Crystal (Mines of Divan) Mithril Deposits + * King Yolkar Goblin Holdout without "King's Scent I" effect + * Goblin Queen Goblin Holdout with "King's Scent I" effect + * Crystal Nucleus All Crystals found and none placed + * per-area structure missing, or because Hypixel. + * Always within 1 block of X=513 Y=106 Z=551. + */ + public void onSpawnParticle( + EnumParticleTypes particleType, + double x, + double y, + double z + ) { + if (!NotEnoughUpdates.INSTANCE.config.mining.wishingCompassSolver || + particleType != EnumParticleTypes.VILLAGER_HAPPY || + !SBInfo.getInstance().getLocation().equals("crystal_hollows") || + isSolved() || + System.currentTimeMillis() - compassUsedMillis > 5000) { + return; + } + + try { + Vec3 particleVec = new Vec3(x, y, z); + if (firstParticle == null) { + firstParticle = particleVec; + return; + } + + double distanceFromFirst = particleVec.distanceTo(firstParticle); + if (distanceFromFirst > MAX_COMPASS_PARTICLE_SPREAD) { + return; + } + + if (distanceFromFirst >= lastParticleDistanceFromFirst) { + lastParticleDistanceFromFirst = distanceFromFirst; + lastParticle = particleVec; + return; + } + + // We get here when the second repetition of particles begins. + // Since the second repetition overlaps with the last few particles + // of the first repetition, the last particle we capture isn't truly the last. + // But that's OK since subsequent particles will be on the same line. + Line line = new Line(firstParticle, lastParticle); + if (firstCompassLine == null) { + firstCompassLine = line; + resetForNewCompass(); + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] Need another position to determine wishing compass target.")); + return; + } + + secondCompassLine = line; + solutionIntersectionLine = firstCompassLine.getIntersectionLineSegment(secondCompassLine); + if (solutionIntersectionLine == null) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Unable to determine wishing compass target.")); + logDiagnosticData(false); + return; + } + + solution = solutionIntersectionLine.getMidpoint(); + + if (solution.distanceTo(firstParticle) < 8) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] WARNING: Solution is likely incorrect.")); + logDiagnosticData(false); + return; + } + + showSolution(); + } catch (Exception e) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Exception while calculating wishing compass solution - see log for details")); + e.printStackTrace(); + } + } + + private boolean isSolved() { + return solution != null; + } + + private void showSolution() { + if (solution == null) return; + String description = "[NEU] Wishing compass target: "; + String coordsText = String.format("%.0f %.0f %.0f", + solution.xCoord, + solution.yCoord, + solution.zCoord); + + if (NUCLEUS_BB.isVecInside(solution)) { + description += "Crystal Nucleus (" + coordsText + ")"; + } else { + description += coordsText; + } + + ChatComponentText message = new ChatComponentText(EnumChatFormatting.YELLOW + description); + + if (isSkytilsPresent) { + ChatStyle clickEvent = new ChatStyle().setChatClickEvent( + new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/sthw add WishingTarget " + coordsText)); + clickEvent.setChatHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ChatComponentText( + EnumChatFormatting.YELLOW + + "Add Skytils hollows waypoint"))); + message.setChatStyle(clickEvent); + } + + Minecraft.getMinecraft().thePlayer.addChatMessage(message); + } + + public void logDiagnosticData(boolean outputAlways) { + if (SBInfo.getInstance().getLocation() == null) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] This command is not available outside SkyBlock")); + return; + } + + if (!NotEnoughUpdates.INSTANCE.config.mining.wishingCompassSolver) + { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Wishing Compass Solver is not enabled.")); + return; + } + + if (!outputAlways && !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.WISHING)) { + return; + } + + boolean originalDebugFlag = !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.add(NEUDebugFlag.WISHING); + + StringBuilder diagsMessage = new StringBuilder(); + + diagsMessage.append("\n"); + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Skytils Present: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(isSkytilsPresent); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Compass Used Millis: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(compassUsedMillis); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Compass Used Position: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((prevPlayerPos == null) ? "<NONE>" : prevPlayerPos.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("First Particle: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((firstParticle == null) ? "<NONE>" : firstParticle.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Last Particle: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((lastParticle == null) ? "<NONE>" : lastParticle.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Last Particle Distance From First: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(lastParticleDistanceFromFirst); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("First Compass Line: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((firstCompassLine == null) ? "<NONE>" : firstCompassLine.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Second Compass Line: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((secondCompassLine == null) ? "<NONE>" : secondCompassLine.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Intersection Line: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((secondCompassLine == null) ? "<NONE>" : solutionIntersectionLine); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Solution: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((solution == null) ? "<NONE>" : solution.toString()); + diagsMessage.append("\n"); + + NEUDebugLogger.log(NEUDebugFlag.WISHING, diagsMessage.toString()); + + if (!originalDebugFlag) { + NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.remove(NEUDebugFlag.WISHING); + } + } +} |
