aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java3
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java6
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java75
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java33
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java153
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java51
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java138
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java7
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java12
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java104
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json4
-rw-r--r--src/main/resources/skyblocker.mixins.json1
14 files changed, 504 insertions, 89 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
index fe05e793..c9705a3b 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
@@ -10,6 +10,7 @@ import me.xmrvizzy.skyblocker.skyblock.*;
import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonBlaze;
import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap;
import me.xmrvizzy.skyblocker.skyblock.dungeon.LividColor;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.TicTacToe;
import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud;
import me.xmrvizzy.skyblocker.skyblock.item.CustomArmorDyeColors;
import me.xmrvizzy.skyblocker.skyblock.item.CustomArmorTrims;
@@ -96,10 +97,12 @@ public class SkyblockerMod implements ClientModInitializer {
CustomItemNames.init();
CustomArmorDyeColors.init();
CustomArmorTrims.init();
+ TicTacToe.init();
containerSolverManager.init();
scheduler.scheduleCyclic(Utils::update, 20);
scheduler.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 100);
scheduler.scheduleCyclic(DungeonBlaze::update, 4);
+ scheduler.scheduleCyclic(TicTacToe::tick, 4);
scheduler.scheduleCyclic(LividColor::update, 10);
scheduler.scheduleCyclic(BackpackPreview::tick, 50);
scheduler.scheduleCyclic(DwarvenHud::update, 40);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
index 104c9c7a..e6961819 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
@@ -79,7 +79,7 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Category("button6")
@ConfigEntry.Gui.CollapsibleObject()
- public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"), "Storage", "/storage");
+ public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"), "(?:Rift )?Storage(?: \\(1/2\\))?", "/storage");
@ConfigEntry.Category("button7")
@ConfigEntry.Gui.CollapsibleObject()
@@ -413,10 +413,14 @@ public class SkyblockerConfig implements ConfigData {
public float mapScaling = 1f;
public int mapX = 2;
public int mapY = 2;
+ @ConfigEntry.Gui.Tooltip
+ public boolean starredMobGlow = false;
public boolean solveThreeWeirdos = true;
@ConfigEntry.Gui.Tooltip
public boolean blazesolver = true;
public boolean solveTrivia = true;
+ @ConfigEntry.Gui.Tooltip
+ public boolean solveTicTacToe = true;
@ConfigEntry.Gui.CollapsibleObject
public LividColor lividColor = new LividColor();
@ConfigEntry.Gui.CollapsibleObject()
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
index cd0ccefd..6c059741 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
@@ -12,12 +12,14 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
+import net.minecraft.util.Formatting;
import net.minecraft.util.math.ColorHelper;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
-import java.awt.*;
+
+import java.awt.Color;
import java.util.regex.Pattern;
@Mixin(DrawContext.class)
@@ -31,39 +33,58 @@ public abstract class DrawContextMixin {
@Inject(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD"))
public void skyblocker$renderItemBar(@Arg ItemStack stack, @Arg(ordinal = 0) int x, @Arg(ordinal = 1) int y) {
- if (Utils.isOnSkyblock() && SkyblockerConfig.get().locations.dwarvenMines.enableDrillFuel) {
- if (!stack.isEmpty()) {
- NbtCompound tag = stack.getNbt();
- if (tag != null && tag.contains("ExtraAttributes")) {
- if (tag.getCompound("ExtraAttributes").contains("drill_fuel")) {
- float current = 3000.0F;
- float max = 3000.0F;
+ if (!Utils.isOnSkyblock() || !SkyblockerConfig.get().locations.dwarvenMines.enableDrillFuel || stack.isEmpty()) {
+ return;
+ }
- for (String line : ItemUtils.getTooltipStrings(stack)) {
- if (line.contains("Fuel: ")) {
- String clear = Pattern.compile("[^0-9 /]").matcher(line).replaceAll("").trim();
- String[] split = clear.split("/");
- current = Integer.parseInt(split[0]);
- max = Integer.parseInt(split[1]) * 1000;
- break;
- }
- }
+ NbtCompound tag = stack.getNbt();
+ if (tag == null || !tag.contains("ExtraAttributes")) {
+ return;
+ }
- matrices.push();
- matrices.translate(0f, 0f, 200f);
- RenderSystem.disableDepthTest();
+ NbtCompound extraAttributes = tag.getCompound("ExtraAttributes");
+ if (!extraAttributes.contains("drill_fuel") && !extraAttributes.getString("id").equals("PICKONIMBUS")) {
+ return;
+ }
+ matrices.push();
+ matrices.translate(0f, 0f, 200f);
+ RenderSystem.disableDepthTest();
- float hue = Math.max(0.0F, 1.0F - (max - current) / max);
- int width = Math.round(current / max * 13.0F);
- Color color = Color.getHSBColor(hue / 3.0F, 1.0F, 1.0F);
- this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 15, y + 15, 0xFF000000);
- this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 2 + width, y + 14, ColorHelper.Argb.getArgb(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue()));
+ float current = 0.0F;
+ float max = 0.0F;
+ String clearFormatting = "";
- matrices.pop();
- RenderSystem.enableDepthTest();
+ for (String line : ItemUtils.getTooltipStrings(stack)) {
+ clearFormatting = Formatting.strip(line);
+ if (line.contains("Fuel: ")) {
+ if (clearFormatting != null) {
+ String clear = Pattern.compile("[^0-9 /]").matcher(clearFormatting).replaceAll("").trim();
+ String[] split = clear.split("/");
+ current = Integer.parseInt(split[0]);
+ max = Integer.parseInt(split[1]) * 1000;
+ }
+ break;
+ } else if (line.contains("uses.")) {
+ if (clearFormatting != null) {
+ int startIndex = clearFormatting.lastIndexOf("after") + 6;
+ int endIndex = clearFormatting.indexOf("uses", startIndex);
+ if (startIndex >= 0 && endIndex > startIndex) {
+ String usesString = clearFormatting.substring(startIndex, endIndex).trim();
+ current = Integer.parseInt(usesString);
+ max = 5000;
}
}
+ break;
}
}
+
+ float hue = Math.max(0.0F, 1.0F - (max - current) / max);
+ int width = Math.round(current / max * 13.0F);
+ Color color = Color.getHSBColor(hue / 3.0F, 1.0F, 1.0F);
+ this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 15, y + 15, 0xFF000000);
+ this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 2 + width, y + 14, ColorHelper.Argb.getArgb(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue()));
+
+ matrices.pop();
+ RenderSystem.enableDepthTest();
}
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java
new file mode 100644
index 00000000..3b796c38
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java
@@ -0,0 +1,33 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import com.llamalad7.mixinextras.sugar.Local;
+import com.llamalad7.mixinextras.sugar.Share;
+import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.StarredMobGlow;
+import net.minecraft.client.render.WorldRenderer;
+import net.minecraft.entity.Entity;
+
+@Mixin(WorldRenderer.class)
+public class WorldRendererMixin {
+
+ @ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;hasOutline(Lnet/minecraft/entity/Entity;)Z"))
+ private boolean skyblocker$shouldStarredMobGlow(boolean original, @Local Entity entity, @Share("isGlowingStarredMob") LocalBooleanRef isGlowingStarredMob) {
+ boolean isAStarredMobThatShouldGlow = SkyblockerConfig.get().locations.dungeons.starredMobGlow && StarredMobGlow.shouldMobGlow(entity);
+
+ isGlowingStarredMob.set(isAStarredMobThatShouldGlow);
+
+ return original || isAStarredMobThatShouldGlow;
+ }
+
+ @ModifyVariable(method = "render", at = @At("STORE"), ordinal = 0)
+ private int skyblocker$modifyGlowColor(int color, @Local Entity entity, @Share("isGlowingStarredMob") LocalBooleanRef isGlowingStarredMob) {
+ return isGlowingStarredMob.get() ? StarredMobGlow.getGlowColor(entity) : color;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
index 44c8803f..def1eb76 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
@@ -25,7 +25,7 @@ public class CroesusHelper extends ContainerSolver {
List<ColorHighlight> highlights = new ArrayList<>();
for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) {
ItemStack stack = entry.getValue();
- if (stack != null && stack.getNbt() != null && stack.getNbt().toString().contains("No more Chests to open!")) {
+ if (stack != null && stack.getNbt() != null && stack.getNbt().toString().contains("Opened Chest:")) {
highlights.add(ColorHighlight.gray(entry.getKey()));
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
index d0dcf1e1..c3e12c3d 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
@@ -9,8 +9,10 @@ import me.xmrvizzy.skyblocker.utils.color.QuadColor;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.Entity;
+import net.minecraft.entity.decoration.ArmorStandEntity;
+import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import org.slf4j.Logger;
@@ -20,92 +22,133 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
+/**
+ * This class provides functionality to render outlines around Blaze entities
+ */
public class DungeonBlaze {
private static final Logger LOGGER = LoggerFactory.getLogger(DungeonBlaze.class.getName());
- private static final float[] WHITE_COLOR_COMPONENTS = { 1.0f, 1.0f, 1.0f };
- static Entity highestBlaze = null;
- static Entity lowestBlaze = null;
- static Entity nextHighestBlaze = null;
- static Entity nextLowestBlaze = null;
- static boolean renderHooked = false;
-
+ private static final float[] WHITE_COLOR_COMPONENTS = {1.0f, 1.0f, 1.0f};
+ private static final QuadColor outlineColorGreen = QuadColor.single(0.0F, 1.0f, 0.0f, 1f);
+ private static final QuadColor outlineColorWhite = QuadColor.single(1.0f, 1.0f, 1.0f, 1.0f);
+
+ private static ArmorStandEntity highestBlaze = null;
+ private static ArmorStandEntity lowestBlaze = null;
+ private static ArmorStandEntity nextHighestBlaze = null;
+ private static ArmorStandEntity nextLowestBlaze = null;
+ private static boolean renderHooked = false;
+
+ /**
+ * Updates the state of Blaze entities and triggers the rendering process if necessary.
+ */
public static void update() {
ClientWorld world = MinecraftClient.getInstance().world;
- if (world == null || !Utils.isInDungeons()) return;
- if (!renderHooked){
-
+ ClientPlayerEntity player = MinecraftClient.getInstance().player;
+ if (world == null || player == null || !Utils.isInDungeons()) return;
+ if (!renderHooked) {
WorldRenderEvents.BEFORE_DEBUG_RENDER.register(DungeonBlaze::blazeRenderer);
renderHooked = true;
}
- Iterable<Entity> entities = world.getEntities();
- List<ObjectIntPair<Entity>> blazes = new ArrayList<>();
+ List<ObjectIntPair<ArmorStandEntity>> blazes = getBlazesInWorld(world, player);
+ sortBlazes(blazes);
+ updateBlazeEntities(blazes);
+ }
- for (Entity entity : entities) {
- String blazeName = entity.getName().getString();
-
+ /**
+ * Retrieves Blaze entities in the world and parses their health information.
+ *
+ * @param world The client world to search for Blaze entities.
+ * @return A list of Blaze entities and their associated health.
+ */
+ private static List<ObjectIntPair<ArmorStandEntity>> getBlazesInWorld(ClientWorld world, ClientPlayerEntity player) {
+ List<ObjectIntPair<ArmorStandEntity>> blazes = new ArrayList<>();
+ for (ArmorStandEntity blaze : world.getEntitiesByClass(ArmorStandEntity.class, player.getBoundingBox().expand(500D), EntityPredicates.NOT_MOUNTED)) {
+ String blazeName = blaze.getName().getString();
if (blazeName.contains("Blaze") && blazeName.contains("/")) {
try {
int health = Integer.parseInt(blazeName.substring(blazeName.indexOf("/") + 1, blazeName.length() - 1));
-
- blazes.add(ObjectIntPair.of(entity, health));
- } catch (NumberFormatException ex) {
- ex.printStackTrace();
+ blazes.add(ObjectIntPair.of(blaze, health));
+ } catch (NumberFormatException e) {
+ handleException(e);
}
}
}
+ return blazes;
+ }
- // Order the blazes in the list from the lowest health to the highest health
+ /**
+ * Sorts the Blaze entities based on their health values.
+ *
+ * @param blazes The list of Blaze entities to be sorted.
+ */
+ private static void sortBlazes(List<ObjectIntPair<ArmorStandEntity>> blazes) {
blazes.sort(Comparator.comparingInt(ObjectIntPair::rightInt));
+ }
- // Ensure that there are blazes in the list
+ /**
+ * Updates information about Blaze entities based on sorted list.
+ *
+ * @param blazes The sorted list of Blaze entities with associated health values.
+ */
+ private static void updateBlazeEntities(List<ObjectIntPair<ArmorStandEntity>> blazes) {
if (!blazes.isEmpty()) {
lowestBlaze = blazes.get(0).left();
-
int highestIndex = blazes.size() - 1;
highestBlaze = blazes.get(highestIndex).left();
-
- // If there's more than 1 blaze
if (blazes.size() > 1) {
- nextLowestBlaze = blazes.get(1).left();
- nextHighestBlaze = blazes.get(highestIndex - 1).left();
+ nextLowestBlaze = blazes.get(1).left();
+ nextHighestBlaze = blazes.get(highestIndex - 1).left();
}
}
-
}
+
+ /**
+ * Renders outlines for Blaze entities based on health and position.
+ *
+ * @param wrc The WorldRenderContext used for rendering.
+ */
public static void blazeRenderer(WorldRenderContext wrc) {
- QuadColor outlineColorRed = QuadColor.single( 0.0F, 1.0F, 0.0F, 1f);
- QuadColor outlineColorGreen = QuadColor.single(1.0F, 0.0F, 0.0F, 1f);
- QuadColor outlineColorWhite = QuadColor.single(1.0f, 1.0f, 1.0f, 1.0f);
-
try {
- if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfig.get().locations.dungeons.blazesolver){
- /* Outline */
+ if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfig.get().locations.dungeons.blazesolver) {
if (highestBlaze.getY() < 69) {
- Box blaze = highestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderUtils.drawBoxOutline(blaze, outlineColorRed, 5f);
-
- if (nextHighestBlaze != null && nextHighestBlaze.isAlive() && nextHighestBlaze != highestBlaze) {
- Box nextBlaze = nextHighestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderUtils.drawBoxOutline(nextBlaze, outlineColorWhite, 5f);
- RenderHelper.renderLinesFromPoints(wrc, new Vec3d[] { blaze.getCenter(), nextBlaze.getCenter() }, WHITE_COLOR_COMPONENTS, 1f, 5f);
- }
+ renderBlazeOutline(highestBlaze, nextHighestBlaze, wrc);
}
-
- /* Outline */
if (lowestBlaze.getY() > 69) {
- Box blaze = lowestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderUtils.drawBoxOutline(blaze, outlineColorRed, 5f);
-
- if (nextLowestBlaze != null && nextLowestBlaze.isAlive() && nextLowestBlaze != lowestBlaze) {
- Box nextBlaze = nextLowestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderUtils.drawBoxOutline(nextBlaze, outlineColorWhite, 5f);
- RenderHelper.renderLinesFromPoints(wrc, new Vec3d[] { blaze.getCenter(), nextBlaze.getCenter() }, WHITE_COLOR_COMPONENTS, 1f, 5f);
- }
+ renderBlazeOutline(lowestBlaze, nextLowestBlaze, wrc);
}
}
} catch (Exception e) {
- LOGGER.warn("[Skyblocker BlazeRenderer] " + e);
- e.printStackTrace();
+ handleException(e);
}
}
-} \ No newline at end of file
+
+ /**
+ * Renders outlines for Blaze entities and connections between them.
+ *
+ * @param blaze The Blaze entity for which to render an outline.
+ * @param nextBlaze The next Blaze entity for connection rendering.
+ * @param wrc The WorldRenderContext used for rendering.
+ */
+ private static void renderBlazeOutline(ArmorStandEntity blaze, ArmorStandEntity nextBlaze, WorldRenderContext wrc) {
+ Box blazeBox = blaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
+ RenderUtils.drawBoxOutline(blazeBox, DungeonBlaze.outlineColorGreen, 5f);
+
+ if (nextBlaze != null && nextBlaze.isAlive() && nextBlaze != blaze) {
+ Box nextBlazeBox = nextBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
+ RenderUtils.drawBoxOutline(nextBlazeBox, DungeonBlaze.outlineColorWhite, 5f);
+
+ Vec3d blazeCenter = blazeBox.getCenter();
+ Vec3d nextBlazeCenter = nextBlazeBox.getCenter();
+
+ RenderHelper.renderLinesFromPoints(wrc, new Vec3d[]{blazeCenter, nextBlazeCenter}, WHITE_COLOR_COMPONENTS, 1f, 5f);
+ }
+ }
+
+ /**
+ * Handles exceptions by logging and printing stack traces.
+ *
+ * @param e The exception to handle.
+ */
+ private static void handleException(Exception e) {
+ LOGGER.warn("[Skyblocker BlazeRenderer] Encountered an unknown exception", e);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java
new file mode 100644
index 00000000..b45242f9
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java
@@ -0,0 +1,51 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.culling.OcclusionCulling;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.decoration.ArmorStandEntity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.predicate.entity.EntityPredicates;
+import net.minecraft.util.math.Box;
+
+public class StarredMobGlow {
+
+ public static boolean shouldMobGlow(Entity entity) {
+ Box box = entity.getBoundingBox();
+
+ if (Utils.isInDungeons() && !entity.isInvisible() && OcclusionCulling.isVisible(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)) {
+ //Minibosses
+ if (entity instanceof PlayerEntity) {
+ switch (entity.getName().getString()) {
+ case "Lost Adventurer": return true;
+ case "Shadow Assassin": return true;
+ case "Diamond Guy": return true;
+ }
+ }
+
+ //Regular Mobs
+ if (!(entity instanceof ArmorStandEntity)) {
+ Box searchBox = box.expand(0, 2, 0);
+ List<ArmorStandEntity> armorStands = entity.getWorld().getEntitiesByClass(ArmorStandEntity.class, searchBox, EntityPredicates.NOT_MOUNTED);
+
+ if (!armorStands.isEmpty() && armorStands.get(0).getName().getString().contains("✯")) return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static int getGlowColor(Entity entity) {
+ if (entity instanceof PlayerEntity) {
+ switch (entity.getName().getString()) {
+ case "Lost Adventurer": return 0xfee15c;
+ case "Shadow Assassin": return 0x5b2cb2;
+ case "Diamond Guy": return 0x57c2f7;
+ }
+ }
+
+ return 0xf57738;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java
new file mode 100644
index 00000000..f5d55d2c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java
@@ -0,0 +1,138 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.RenderUtils;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.color.QuadColor;
+import me.xmrvizzy.skyblocker.utils.tictactoe.TicTacToeUtils;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
+import net.minecraft.block.Block;
+import net.minecraft.block.Blocks;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.entity.decoration.ItemFrameEntity;
+import net.minecraft.item.FilledMapItem;
+import net.minecraft.item.map.MapState;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Box;
+import net.minecraft.util.math.Direction;
+
+/**
+ * Thanks to Danker for a reference implementation!
+ */
+public class TicTacToe {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TicTacToe.class);
+ private static final QuadColor RED = QuadColor.single(1.0f, 0.0f, 0.0f, 1f);
+ private static Box nextBestMoveToMake = null;
+
+ public static void init() {
+ WorldRenderEvents.BEFORE_DEBUG_RENDER.register(TicTacToe::solutionRenderer);
+ }
+
+ public static void tick() {
+ MinecraftClient client = MinecraftClient.getInstance();
+ ClientWorld world = client.world;
+ ClientPlayerEntity player = client.player;
+
+ nextBestMoveToMake = null;
+
+ if (world == null || player == null || !Utils.isInDungeons()) return;
+
+ //Search within 21 blocks for item frames that contain maps
+ Box searchBox = new Box(player.getX() - 21, player.getY() - 21, player.getZ() - 21, player.getX() + 21, player.getY() + 21, player.getZ() + 21);
+ List<ItemFrameEntity> itemFramesThatHoldMaps = world.getEntitiesByClass(ItemFrameEntity.class, searchBox, ItemFrameEntity::containsMap);
+
+ try {
+ //Only attempt to solve if its the player's turn
+ if (itemFramesThatHoldMaps.size() != 9 && itemFramesThatHoldMaps.size() % 2 == 1) {
+ char[][] board = new char[3][3];
+ BlockPos leftmostRow = null;
+ int sign = 1;
+ char facing = 'X';
+
+ for (ItemFrameEntity itemFrame : itemFramesThatHoldMaps) {
+ MapState mapState = world.getMapState(FilledMapItem.getMapName(itemFrame.getMapId().getAsInt()));
+
+ if (mapState == null) continue;
+
+ int column = 0, row;
+ sign = 1;
+
+ //Find position of the item frame relative to where it is on the tic tac toe board
+ if (itemFrame.getHorizontalFacing() == Direction.SOUTH || itemFrame.getHorizontalFacing() == Direction.WEST) sign = -1;
+ BlockPos itemFramePos = BlockPos.ofFloored(itemFrame.getX(), itemFrame.getY(), itemFrame.getZ());
+
+ for (int i = 2; i >= 0; i--) {
+ int realI = i * sign;
+ BlockPos blockPos = itemFramePos;
+
+ if (itemFrame.getX() % 0.5 == 0) {
+ blockPos = itemFramePos.add(realI, 0, 0);
+ } else if (itemFrame.getZ() % 0.5 == 0) {
+ blockPos = itemFramePos.add(0, 0, realI);
+ facing = 'Z';
+ }
+
+ Block block = world.getBlockState(blockPos).getBlock();
+ if (block == Blocks.AIR || block == Blocks.STONE_BUTTON) {
+ leftmostRow = blockPos;
+ column = i;
+
+ break;
+ }
+ }
+
+ //Determine the row of the item frame
+ if (itemFrame.getY() == 72.5) {
+ row = 0;
+ } else if (itemFrame.getY() == 71.5) {
+ row = 1;
+ } else if (itemFrame.getY() == 70.5) {
+ row = 2;
+ } else {
+ continue;
+ }
+
+
+ //Get the color of the middle pixel of the map which determines whether its X or O
+ int middleColor = mapState.colors[8256] & 255;
+
+ if (middleColor == 114) {
+ board[row][column] = 'X';
+ } else if (middleColor == 33) {
+ board[row][column] = 'O';
+ }
+
+ int bestMove = TicTacToeUtils.getBestMove(board) - 1;
+
+ if (leftmostRow != null) {
+ double drawX = facing == 'X' ? leftmostRow.getX() - sign * (bestMove % 3) : leftmostRow.getX();
+ double drawY = 72 - (double) (bestMove / 3);
+ double drawZ = facing == 'Z' ? leftmostRow.getZ() - sign * (bestMove % 3) : leftmostRow.getZ();
+
+ nextBestMoveToMake = new Box(drawX, drawY, drawZ, drawX + 1, drawY + 1, drawZ + 1);
+ }
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while determining a tic tac toe solution!", e);
+ }
+ }
+
+ private static void solutionRenderer(WorldRenderContext context) {
+ try {
+ if (SkyblockerConfig.get().locations.dungeons.solveTicTacToe && nextBestMoveToMake != null) {
+ RenderUtils.drawBoxOutline(nextBestMoveToMake, RED, 5);
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while rendering the tic tac toe solution!", e);
+ }
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java
index ffc3f67d..6c648da9 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java
@@ -42,7 +42,10 @@ public class CustomArmorTrims {
private static void initializeTrimCache() {
ClientPlayerEntity player = MinecraftClient.getInstance().player;
- if (!trimsInitialized && player != null) {
+ if (trimsInitialized || player == null) {
+ return;
+ }
+ try {
TRIMS_CACHE.clear();
DynamicRegistryManager registryManager = player.networkHandler.getRegistryManager();
for (Identifier material : registryManager.get(RegistryKeys.TRIM_MATERIAL).getIds()) {
@@ -62,6 +65,8 @@ public class CustomArmorTrims {
LOGGER.info("[Skyblocker] Successfully cached all armor trims!");
trimsInitialized = true;
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker] Encountered an exception while caching armor trims", e);
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
index 1d58435e..351cef48 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
@@ -18,8 +18,8 @@ public class QuickNav {
private static final String dungeonHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}}";
public static void init() {
- ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
- if (Utils.isOnSkyblock() && SkyblockerConfig.get().quickNav.enableQuickNav && screen instanceof HandledScreen<?>) {
+ ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().quickNav.enableQuickNav && screen instanceof HandledScreen<?> && client.player != null && !client.player.isCreative()) {
String screenTitle = screen.getTitle().getString().trim();
List<QuickNavButton> buttons = QuickNav.init(screenTitle);
for (QuickNavButton button : buttons) Screens.getButtons(screen).add(button);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
index e85020aa..839e0dae 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
@@ -28,6 +28,7 @@ import java.util.List;
* Utility variables and methods for retrieving Skyblock related information.
*/
public class Utils {
+ private static final String ALTERNATE_HYPIXEL_ADDRESS = System.getProperty("skyblocker.alternateHypixelAddress", "");
private static final String PROFILE_PREFIX = "Profile: ";
private static boolean isOnHypixel = false;
private static boolean isOnSkyblock = false;
@@ -129,10 +130,9 @@ public class Utils {
return;
}
String string = sidebar.toString();
- String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : "";
if (sidebar.isEmpty()) return;
- if (serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io")) {
+ if (isConnectedToHypixel(client)) {
if (!isOnHypixel) {
isOnHypixel = true;
}
@@ -154,6 +154,14 @@ public class Utils {
leaveSkyblock();
}
}
+
+ private static boolean isConnectedToHypixel(MinecraftClient client) {
+ String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : "";
+ String serverBrand = (client.player != null && client.player.getServerBrand() != null) ? client.player.getServerBrand() : "";
+ boolean isOnHypixel = (serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord"));
+
+ return isOnHypixel;
+ }
private static void leaveSkyblock() {
if (isOnSkyblock) {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java
new file mode 100644
index 00000000..5e5c8f89
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java
@@ -0,0 +1,104 @@
+package me.xmrvizzy.skyblocker.utils.tictactoe;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class TicTacToeUtils {
+
+ public static int getBestMove(char[][] board) {
+ HashMap<Integer, Integer> moves = new HashMap<>();
+ for (int row = 0; row < board.length; row++) {
+ for (int col = 0; col < board[row].length; col++) {
+ if (board[row][col] != '\0') continue;
+ board[row][col] = 'O';
+ int score = alphabeta(board, Integer.MIN_VALUE, Integer.MAX_VALUE, false, 0);
+ board[row][col] = '\0';
+ moves.put(row * 3 + col + 1, score);
+ }
+ }
+ return Collections.max(moves.entrySet(), Map.Entry.comparingByValue()).getKey();
+ }
+
+ public static boolean hasMovesLeft(char[][] board) {
+ for (char[] rows : board) {
+ for (char col : rows) {
+ if (col == '\0') return true;
+ }
+ }
+ return false;
+ }
+
+ public static int getBoardRanking(char[][] board) {
+ for (int row = 0; row < 3; row++) {
+ if (board[row][0] == board[row][1] && board[row][0] == board[row][2]) {
+ if (board[row][0] == 'X') {
+ return -10;
+ } else if (board[row][0] == 'O') {
+ return 10;
+ }
+ }
+ }
+
+ for (int col = 0; col < 3; col++) {
+ if (board[0][col] == board[1][col] && board[0][col] == board[2][col]) {
+ if (board[0][col] == 'X') {
+ return -10;
+ } else if (board[0][col] == 'O') {
+ return 10;
+ }
+ }
+ }
+
+ if (board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
+ if (board[0][0] == 'X') {
+ return -10;
+ } else if (board[0][0] == 'O') {
+ return 10;
+ }
+ } else if (board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
+ if (board[0][2] == 'X') {
+ return -10;
+ } else if (board[0][2] == 'O') {
+ return 10;
+ }
+ }
+
+ return 0;
+ }
+ public static int alphabeta(char[][] board, int alpha, int beta, boolean max, int depth) {
+ int score = getBoardRanking(board);
+ if (score == 10 || score == -10) return score;
+ if (!hasMovesLeft(board)) return 0;
+
+ if (max) {
+ int bestScore = Integer.MIN_VALUE;
+ for (int row = 0; row < 3; row++) {
+ for (int col = 0; col < 3; col++) {
+ if (board[row][col] == '\0') {
+ board[row][col] = 'O';
+ bestScore = Math.max(bestScore, alphabeta(board, alpha, beta, false, depth + 1));
+ board[row][col] = '\0';
+ alpha = Math.max(alpha, bestScore);
+ if (beta <= alpha) break; // Pruning
+ }
+ }
+ }
+ return bestScore - depth;
+ } else {
+ int bestScore = Integer.MAX_VALUE;
+ for (int row = 0; row < 3; row++) {
+ for (int col = 0; col < 3; col++) {
+ if (board[row][col] == '\0') {
+ board[row][col] = 'X';
+ bestScore = Math.min(bestScore, alphabeta(board, alpha, beta, true, depth + 1));
+ board[row][col] = '\0';
+ beta = Math.min(beta, bestScore);
+ if (beta <= alpha) break; // Pruning
+ }
+ }
+ }
+ return bestScore + depth;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index 578917e2..87fa9b3a 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -204,10 +204,14 @@
"text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "Map Scaling",
"text.autoconfig.skyblocker.option.locations.dungeons.mapX": "Map X",
"text.autoconfig.skyblocker.option.locations.dungeons.mapY": "Map Y",
+ "text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow": "Starred Mob Glow",
+ "text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow.@Tooltip": "Applies the glowing effect to starred mobs that are visible.",
"text.autoconfig.skyblocker.option.locations.dungeons.solveThreeWeirdos": "Solve Three Weirdos Puzzle",
"text.autoconfig.skyblocker.option.locations.dungeons.blazesolver": "Solve Blaze Puzzle",
"text.autoconfig.skyblocker.option.locations.dungeons.blazesolver.@Tooltip": "Boxes the correct blaze in green, also draws a line to and boxes the next blaze to kill in white.",
"text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia": "Solve Trivia Puzzle",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe": "Solve Tic Tac Toe Puzzle",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe.@Tooltip": "Puts a red box around the next best move for you to make!",
"text.autoconfig.skyblocker.option.locations.dungeons.lividColor": "Livid Color",
"text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor": "Enable Livid Color",
"text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor.@Tooltip": "Send the livid color in the chat during the Livid boss fight.",
diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json
index 266a653b..6ba7dc22 100644
--- a/src/main/resources/skyblocker.mixins.json
+++ b/src/main/resources/skyblocker.mixins.json
@@ -19,6 +19,7 @@
"PlayerListHudMixin",
"PlayerSkinProviderMixin",
"ScoreboardMixin",
+ "WorldRendererMixin",
"accessor.BeaconBlockEntityRendererInvoker",
"accessor.FrustumInvoker",
"accessor.HandledScreenAccessor",