aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java
diff options
context:
space:
mode:
authorWalker Selby <git@walkerselby.com>2022-03-27 12:13:08 -0500
committerGitHub <noreply@github.com>2022-03-27 13:13:08 -0400
commit5e7a8a02fe836c1e635faf067fbe8913549bc0e0 (patch)
treedc99a2544bd81af6d70c7bd07ba53142ff38d057 /src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java
parent47efdd4f84cc9a29738fe16d631eb33ee716db61 (diff)
downloadnotenoughupdates-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/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java')
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalWishingCompassSolver.java942
1 files changed, 142 insertions, 800 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
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);