From cdd9441aa587ef7ecb2ac285350f59846b433ff9 Mon Sep 17 00:00:00 2001 From: Emirlol <81419447+Emirlol@users.noreply.github.com> Date: Sun, 7 Jan 2024 02:06:28 +0300 Subject: [PATCH 01/27] Add dungeon score calculation on client-side --- .../skyblocker/config/SkyblockerConfig.java | 14 + .../config/categories/DungeonsCategory.java | 30 +- .../categories/MessageFilterCategory.java | 8 + .../skyblock/dungeon/DungeonScore.java | 290 +++++++++++++++--- .../skyblock/filters/MimicFilter.java | 26 ++ .../java/de/hysky/skyblocker/utils/Utils.java | 27 ++ .../utils/chat/ChatMessageListener.java | 7 +- .../assets/skyblocker/lang/en_us.json | 7 + 8 files changed, 363 insertions(+), 46 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index aa6e5d24a3..e07a7588da 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -605,6 +605,9 @@ public static class Dungeons { @SerialEntry public DungeonChestProfit dungeonChestProfit = new DungeonChestProfit(); + @SerialEntry + public MimicMessages mimicMessages = new MimicMessages(); + @SerialEntry public boolean croesusHelper = true; @@ -785,6 +788,14 @@ public static class DungeonChestProfit { public Formatting incompleteColor = Formatting.BLUE; } + public static class MimicMessages { + @SerialEntry + public boolean sendMimicMessages = true; + + @SerialEntry + public String mimicMessage = "Mimic dead!"; + } + public static class LividColor { @SerialEntry public boolean enableLividColorGlow = true; @@ -969,6 +980,9 @@ public static class Messages { @SerialEntry public ChatFilterResult hideToggleSkyMall = ChatFilterResult.PASS; + @SerialEntry + public ChatFilterResult hideMimicKill = ChatFilterResult.PASS; + @SerialEntry public boolean hideMana = false; } diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index 583bc166e8..06133afc5e 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -2,17 +2,12 @@ import de.hysky.skyblocker.config.ConfigUtils; import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.skyblock.dungeon.DungeonMapConfigScreen; import de.hysky.skyblocker.utils.waypoint.Waypoint.Type; -import dev.isxander.yacl3.api.ButtonOption; -import dev.isxander.yacl3.api.ConfigCategory; -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.OptionDescription; -import dev.isxander.yacl3.api.OptionFlag; -import dev.isxander.yacl3.api.OptionGroup; +import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; import dev.isxander.yacl3.api.controller.StringControllerBuilder; -import de.hysky.skyblocker.skyblock.dungeon.DungeonMapConfigScreen; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; import net.minecraft.util.Formatting; @@ -418,6 +413,27 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.locations.dungeons.allowDroppingProtectedItems = newValue) .controller(ConfigUtils::createBooleanController) .build()) + //Mimic Messages + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.sendMimicMessages")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.sendMimicMessages.@Tooltip"))) + .binding(defaults.locations.dungeons.mimicMessages.sendMimicMessages, + () -> config.locations.dungeons.mimicMessages.sendMimicMessages, + newValue -> config.locations.dungeons.mimicMessages.sendMimicMessages = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.mimicMessage")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.mimicMessage.@Tooltip"))) + .binding(defaults.locations.dungeons.mimicMessages.mimicMessage, + () -> config.locations.dungeons.mimicMessages.mimicMessage, + newValue -> config.locations.dungeons.mimicMessages.mimicMessage = newValue) + .controller(StringControllerBuilder::create) + .build()) + .build()) // Livid Color .group(OptionGroup.createBuilder() diff --git a/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java index 37f24d8c61..3fe285de2e 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java @@ -102,6 +102,14 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.messages.hideMana = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideMimicKill")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.messages.hideMimicKill.@Tooltip"))) + .binding(defaults.messages.hideMimicKill, + () -> config.messages.hideMimicKill, + newValue -> config.messages.hideMimicKill = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index d67d6988ed..ebec99dc5c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -1,28 +1,81 @@ package de.hysky.skyblocker.skyblock.dungeon; + import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.scheduler.MessageScheduler; import de.hysky.skyblocker.utils.scheduler.Scheduler; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents; import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.Entity; +import net.minecraft.entity.mob.ZombieEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; import net.minecraft.sound.SoundEvents; +import net.minecraft.util.collection.DefaultedList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; public class DungeonScore { - private static final SkyblockerConfig.DungeonScore CONFIG = SkyblockerConfigManager.get().locations.dungeons.dungeonScore; - private static final Pattern DUNGEON_CLEARED_PATTERN = Pattern.compile("Cleared: (?\\d+)% \\((?\\d+)\\)"); + private static final SkyblockerConfig.DungeonScore SCORE_CONFIG = SkyblockerConfigManager.get().locations.dungeons.dungeonScore; + private static final SkyblockerConfig.MimicMessages MIMIC_MESSAGES_CONFIG = SkyblockerConfigManager.get().locations.dungeons.mimicMessages; + private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Dungeon Score"); + private static final Pattern CLEARED_PATTERN = Pattern.compile("Cleared: (?\\d+)%.*"); + private static final Pattern SECRETS_PATTERN = Pattern.compile("Secrets Found: (?\\d+\\.?\\d*)%"); + private static final Pattern PUZZLES_PATTERN = Pattern.compile(".+?(?=:): \\[(?.)](?: \\(\\w+\\))?"); + private static final Pattern PUZZLE_COUNT_PATTERN = Pattern.compile("Puzzles: \\((?\\d+)\\)"); + private static final Pattern TIME_PATTERN = Pattern.compile("Time: (?:(?\\d+(?=h))?h? ?(?\\d+(?=m))?m? ?(?\\d+(?=s))s|Soon!)"); + private static final Pattern CRYPTS_PATTERN = Pattern.compile("Crypts: (?\\d+)"); + private static final Pattern COMPLETED_ROOMS_PATTERN = Pattern.compile(" *Completed Rooms: (?\\d+)"); + private static final Pattern DUNGEON_START_PATTERN = Pattern.compile("(?:Auto-closing|Starting) in: \\d:\\d+"); + private static final Pattern FLOOR_PATTERN = Pattern.compile(".*?(?=T)The Catacombs \\((?[EFM]\\D*\\d*)\\)"); + private static final Pattern DEATHS_PATTERN = Pattern.compile("Team Deaths: (?\\d+)"); + private static String currentFloor; private static boolean sent270; private static boolean sent300; + private static boolean isMimicKilled; + private static int puzzleCount; + //Caching the dungeon start state to prevent unnecessary scoreboard pattern matching after dungeon starts + private static boolean isDungeonStarted; + private static boolean isMayorPaul; + private static final HashMap floorRequirements = new HashMap<>(Map.ofEntries( + Map.entry("E", new FloorRequirement(30, 1200)), + Map.entry("F1", new FloorRequirement(30, 600)), + Map.entry("F2", new FloorRequirement(40, 600)), + Map.entry("F3", new FloorRequirement(50, 600)), + Map.entry("F4", new FloorRequirement(60, 720)), + Map.entry("F5", new FloorRequirement(70, 600)), + Map.entry("F6", new FloorRequirement(85, 720)), + Map.entry("F7", new FloorRequirement(100, 840)), + Map.entry("M1", new FloorRequirement(100, 480)), + Map.entry("M2", new FloorRequirement(100, 480)), + Map.entry("M3", new FloorRequirement(100, 480)), + Map.entry("M4", new FloorRequirement(100, 480)), + Map.entry("M5", new FloorRequirement(100, 480)), + Map.entry("M6", new FloorRequirement(100, 600)), + Map.entry("M7", new FloorRequirement(100, 840)) + )); public static void init() { Scheduler.INSTANCE.scheduleCyclic(DungeonScore::tick, 20); ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset()); + ServerLivingEntityEvents.AFTER_DEATH.register((entity, source) -> { + if (isEntityMimic(entity)) { + if (MIMIC_MESSAGES_CONFIG.sendMimicMessages) MessageScheduler.INSTANCE.sendMessageAfterCooldown(MIMIC_MESSAGES_CONFIG.mimicMessage); + setMimicKilled(true); + } + }); } public static void tick() { @@ -31,46 +84,211 @@ public static void tick() { reset(); return; } + if (!isDungeonStarted) { + if (checkIfDungeonStarted()) onDungeonStart(); + return; + } + int score = calculateScore(); + if (SCORE_CONFIG.enableDungeonScore270 && !sent270 && score >= 270 && score < 300) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/pc " + Constants.PREFIX.get().getString() + SCORE_CONFIG.dungeonScore270Message.replaceAll("\\[score]", "270")); + client.player.playSound(SoundEvents.BLOCK_NOTE_BLOCK_PLING.value(), 1f, 0.1f); + sent270 = true; + } + if (SCORE_CONFIG.enableDungeonScore300 && !sent300 && score >= 300) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/pc " + Constants.PREFIX.get().getString() + SCORE_CONFIG.dungeonScore300Message.replaceAll("\\[score]", "300")); + client.player.playSound(SoundEvents.BLOCK_NOTE_BLOCK_PLING.value(), 1f, 1f); + sent300 = true; + } + } + + public static boolean isEntityMimic(Entity entity) { + if (!(entity instanceof ZombieEntity zombie)) return false; + if (!zombie.isBaby()) return false; + try { + DefaultedList armor = (DefaultedList) zombie.getArmorItems(); + if (armor.isEmpty()) return false; + NbtCompound helmetNbt = armor.get(3).getNbt(); + if (helmetNbt == null) return false; + return helmetNbt.getCompound("SkullOwner") + .getCompound("Properties") + .getList("textures", NbtElement.COMPOUND_TYPE) + .getCompound(0).getString("Value") + .equals("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTE5YzEyNTQzYmM3NzkyNjA1ZWY2OGUxZjg3NDlhZThmMmEzODFkOTA4NWQ0ZDRiNzgwYmExMjgyZDM1OTdhMCJ9fX0K"); + } catch (NullPointerException e) { + return false; + } catch (ClassCastException f) { + f.printStackTrace(); + return false; + } + } + private static boolean checkIfDungeonStarted() { for (String sidebarLine : Utils.STRING_SCOREBOARD) { - Matcher dungeonClearedMatcher = DUNGEON_CLEARED_PATTERN.matcher(sidebarLine); - if (!dungeonClearedMatcher.matches()) { - continue; - } - int score = Integer.parseInt(dungeonClearedMatcher.group("score")); - if (!DungeonManager.isInBoss()) score += 28; - if (!sent270 && score >= 270 && score < 300) { - if (CONFIG.enableDungeonScore270Message) { - MessageScheduler.INSTANCE.sendMessageAfterCooldown(Constants.PREFIX.get().getString() + CONFIG.dungeonScore270Message.replaceAll("\\[score]", "270")); - } - if (CONFIG.enableDungeonScore270Title) { - client.inGameHud.setDefaultTitleFade(); - client.inGameHud.setTitle(Constants.PREFIX.get().append(CONFIG.dungeonScore270Message.replaceAll("\\[score]", "270"))); - } - if (CONFIG.enableDungeonScore270Sound) { - client.player.playSound(SoundEvents.BLOCK_NOTE_BLOCK_PLING.value(), 100f, 0.1f); - } - sent270 = true; - } - if (!sent300 && score >= 300) { - if (CONFIG.enableDungeonScore300Message) { - MessageScheduler.INSTANCE.sendMessageAfterCooldown(Constants.PREFIX.get().getString() + CONFIG.dungeonScore300Message.replaceAll("\\[score]", "300")); - } - if (CONFIG.enableDungeonScore300Title) { - client.inGameHud.setDefaultTitleFade(); - client.inGameHud.setTitle(Constants.PREFIX.get().append(CONFIG.dungeonScore300Message.replaceAll("\\[score]", "300"))); - } - if (CONFIG.enableDungeonScore300Sound) { - client.player.playSound(SoundEvents.BLOCK_NOTE_BLOCK_PLING.value(), 100f, 0.1f); - } - sent300 = true; - } - break; + Matcher matcher = DUNGEON_START_PATTERN.matcher(sidebarLine); + if (matcher.matches()) return false; } + return true; + } + + private static void onDungeonStart() { + setCurrentFloor(); + isDungeonStarted = true; + puzzleCount = getPuzzleCount(); + isMayorPaul = Utils.getMayor().equals("Paul"); + } + + private static int calculateScore() { + int timeScore = calculateTimeScore(); + int exploreScore = calculateExploreScore(); + int skillScore = calculateSkillScore(); + int paulScore = isMayorPaul ? 10 : 0; + int cryptsScore = Math.min(getCrypts(), 5); + int mimicScore = isMimicKilled ? 2 : 0; + int totalScore = timeScore + exploreScore + skillScore + paulScore + cryptsScore + mimicScore; + //Will be this way until ready for pr, so it's easy to debug. + LOGGER.info("Total Score: {} (Time: {}, Explore: {}, Skill: {}, Paul: {}, Crypts: {}, Mimic: {})", totalScore, timeScore, exploreScore, skillScore, paulScore, cryptsScore, mimicScore); + return totalScore; + } + + private static int calculateExploreScore() { + int completedRoomScore = (int) Math.floor(60.0 * getCompletedRooms() / getTotalRooms()); + int percentageRequirement = floorRequirements.get(currentFloor).percentage; + int secretsScore = (int) Math.floor(40 * Math.min(percentageRequirement, getSecretsPercentage()) / percentageRequirement); + return completedRoomScore + secretsScore; + } + + private static int calculateTimeScore() { + Matcher timeMatcher = PlayerListMgr.regexAt(45, TIME_PATTERN); + if (timeMatcher == null) { + LOGGER.error("Time pattern doesn't match"); + return 0; + } + int score = 100; + int hours = Optional.ofNullable(timeMatcher.group("hours")).map(Integer::parseInt).orElse(0); + int minutes = Optional.ofNullable(timeMatcher.group("minutes")).map(Integer::parseInt).orElse(0); + int seconds = Optional.ofNullable(timeMatcher.group("seconds")).map(Integer::parseInt).orElse(0); + int timeSpent = hours * 3600 + minutes * 60 + seconds; + int timeRequirement = floorRequirements.get(currentFloor).timeLimit; + if (timeSpent < timeRequirement) return score; + + double timePastRequirement = ((double) (timeSpent - timeRequirement) / timeRequirement) * 100; + if (timePastRequirement >= 0 && timePastRequirement < 20) { + score -= (int) timePastRequirement / 2; + } else if (timePastRequirement >= 20 && timePastRequirement < 40) { + score -= (int) (10 + (timePastRequirement - 20) / 4); + } else if (timePastRequirement >= 40 && timePastRequirement < 50) { + score -= (int) (15 + (timePastRequirement - 40) / 5); + } else if (timePastRequirement >= 50 && timePastRequirement < 60) { + score -= (int) (17 + (timePastRequirement - 50) / 6); + } else if (timePastRequirement >= 60) { + score -= (int) (18 + (2.0 / 3.0) + (timePastRequirement - 60) / 7); + } + return score; + } + + private static int calculateSkillScore() { + return 20 + (int) Math.floor(80.0 * getCompletedRooms() / getTotalRooms()) - (2 * getDeathCount()) - (10 * getFailedPuzzles()); } private static void reset() { sent270 = false; sent300 = false; + isDungeonStarted = false; + isMimicKilled = false; + isMayorPaul = false; + puzzleCount = 0; + currentFloor = ""; + } + + public static void setMimicKilled(boolean killed) { + isMimicKilled = killed; + } + + private static int getTotalRooms() { + return (int) Math.round((getCompletedRooms()) / getClearPercentage()); //Clear% rounds to the closest integer so it can be off by 0.5% at most, this should be accurate enough + } + + private static int getCompletedRooms() { + Matcher completedRoomsMatcher = PlayerListMgr.regexAt(43, COMPLETED_ROOMS_PATTERN); + if (completedRoomsMatcher == null) { + LOGGER.error("Completed rooms pattern doesn't match"); + return 0; + } + return Integer.parseInt(completedRoomsMatcher.group("rooms")); + } + + private static double getClearPercentage() { + for (String sidebarLine : Utils.STRING_SCOREBOARD) { + Matcher clearMatcher = CLEARED_PATTERN.matcher(sidebarLine); + if (!clearMatcher.matches()) continue; + return Double.parseDouble(clearMatcher.group("cleared")) / 100; + } + LOGGER.error("Clear pattern doesn't match"); + return 0; + } + + private static int getDeathCount() { + Matcher matcher = PlayerListMgr.regexAt(25, DEATHS_PATTERN); + if (matcher == null) { + LOGGER.error("Death count pattern doesn't match"); + return 0; + } + //TODO: Turn this into a map of players and their deathcounts, get party members' pets, check if they have spirit pet, if they have it reduce their death count by 0.5 + return Integer.parseInt(matcher.group("deaths")); + } + + private static int getPuzzleCount() { + Matcher matcher = PlayerListMgr.regexAt(47, PUZZLE_COUNT_PATTERN); + if (matcher == null) { + LOGGER.error("Puzzle count pattern doesn't match"); + return 0; + } + return Integer.parseInt(matcher.group("count")); + } + + //Might be replaced to look for puzzle fail messages on chat instead of playerlist + private static int getFailedPuzzles() { + int failedPuzzles = 0; + for (int index = 0; index < puzzleCount; index++) { + Matcher puzzleMatcher = PlayerListMgr.regexAt(48 + index, PUZZLES_PATTERN); + if (puzzleMatcher == null) { + LOGGER.error("Puzzle pattern doesn't match"); + return 0; + } + if (puzzleMatcher.group("state").equals("✖")) failedPuzzles++; + } + return failedPuzzles; + } + + private static double getSecretsPercentage() { + Matcher matcher = PlayerListMgr.regexAt(44, SECRETS_PATTERN); + if (matcher == null) { + LOGGER.error("Secrets pattern doesn't match"); + return 0; + } + return Double.parseDouble(matcher.group("secper")); + } + + private static int getCrypts() { + Matcher matcher = PlayerListMgr.regexAt(33, CRYPTS_PATTERN); + if (matcher == null) { + LOGGER.error("Crypts pattern doesn't match"); + return 0; + } + return Integer.parseInt(matcher.group("crypts")); + } + + public static void setCurrentFloor() { + for (String sidebarLine : Utils.STRING_SCOREBOARD) { + Matcher floorMatcher = FLOOR_PATTERN.matcher(sidebarLine); + if (!floorMatcher.matches()) continue; + currentFloor = floorMatcher.group("floor"); + return; + } + LOGGER.error("Floor pattern doesn't match"); + } + + record FloorRequirement(int percentage, int timeLimit) { } } + diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java new file mode 100644 index 0000000000..0fce5b2cb4 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java @@ -0,0 +1,26 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.text.Text; + +import java.util.regex.Matcher; + +public class MimicFilter extends ChatPatternListener { + public MimicFilter() { + super("(?:Mimic dead!|Mimic Killed!|\\$SKYTILS-DUNGEON-SCORE-MIMIC\\$|\\Q" + SkyblockerConfigManager.get().locations.dungeons.mimicMessages.mimicMessage + "\\E)$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideMimicKill; + } + + @Override + protected boolean onMatch(Text message, Matcher matcher) { + DungeonScore.setMimicKilled(true); + return false; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 45ace08536..1fc718be19 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -24,6 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.Collections; import java.util.List; @@ -64,6 +65,8 @@ public class Utils { private static boolean sentLocRaw = false; private static boolean canSendLocRaw = false; + private static String mayor = ""; + /** * @implNote The parent text will always be empty, the actual text content is inside the text's siblings. */ @@ -135,7 +138,16 @@ public static String getMap() { return map; } + /** + * @return the current mayor as cached on skyblock join. + */ + @NotNull + public static String getMayor() { + return mayor; + } + public static void init() { + SkyblockEvents.JOIN.register(Utils::initializeMayorCache); ClientPlayConnectionEvents.JOIN.register(Utils::onClientWorldJoin); ClientReceiveMessageEvents.ALLOW_GAME.register(Utils::onChatMessage); ClientReceiveMessageEvents.GAME_CANCELED.register(Utils::onChatMessage); // Somehow this works even though onChatMessage returns a boolean @@ -381,4 +393,19 @@ private static void resetLocRawInfo() { locationRaw = ""; map = ""; } + + private static void initializeMayorCache() { + if (!mayor.isEmpty()) return; + try { + JsonObject json = JsonParser.parseString(Http.sendGetRequest("https://api.hypixel.net/v2/resources/skyblock/election")).getAsJsonObject(); + if (json.get("success").getAsBoolean()) { + mayor = json.get("mayor").getAsJsonObject().get("name").getAsString(); + System.out.println(mayor); + } else { + throw new IOException("API call for mayor failed: " + json.get("cause").getAsString()); + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java b/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java index ebdb6f0979..9739862519 100644 --- a/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java +++ b/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java @@ -1,7 +1,5 @@ package de.hysky.skyblocker.utils.chat; -import de.hysky.skyblocker.skyblock.filters.*; -import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.skyblock.barn.HungryHiker; import de.hysky.skyblocker.skyblock.barn.TreasureHunter; import de.hysky.skyblocker.skyblock.dungeon.Reparty; @@ -9,6 +7,8 @@ import de.hysky.skyblocker.skyblock.dungeon.puzzle.Trivia; import de.hysky.skyblocker.skyblock.dwarven.Fetchur; import de.hysky.skyblocker.skyblock.dwarven.Puzzler; +import de.hysky.skyblocker.skyblock.filters.*; +import de.hysky.skyblocker.utils.Utils; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.EventFactory; @@ -54,7 +54,8 @@ static void init() { new TeleportPadFilter(), new AutopetFilter(), new ShowOffFilter(), - new ToggleSkyMallFilter() + new ToggleSkyMallFilter(), + new MimicFilter() }; // Register all listeners to EVENT for (ChatMessageListener listener : listeners) { diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index dfc0c0352e..cd1d9673be 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -231,6 +231,11 @@ "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.solveWaterboard": "Solve Waterboard Puzzle", "text.autoconfig.skyblocker.option.locations.dungeons.solveWaterboard.@Tooltip": "Click the levers with green boxes to solve the puzzle.", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages": "Mimic Messages", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.sendMimicMessages": "Enable Mimic Messages", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.sendMimicMessages.@Tooltip": "Sends a message in chat when a mimic is killed for score calculation mods.", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.mimicMessage": "Mimic Message", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.mimicMessage.@Tooltip": "Message which will be sent in the chat when a mimic is killed. Recommended to keep as \"Mimic dead!\"", "text.autoconfig.skyblocker.option.locations.dungeons.lividColor": "Livid Color", "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorGlow": "Enable Livid Color Glow", "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorGlow.@Tooltip": "Applies the glowing effect to the correct Livid in F5/M5.", @@ -294,6 +299,8 @@ "text.autoconfig.skyblocker.option.messages.hideShowOff.@Tooltip": "Filters messages from the /show command", "text.autoconfig.skyblocker.option.messages.hideToggleSkyMall": "Hide Toggle Sky Mall Messages", "text.autoconfig.skyblocker.option.messages.hideToggleSkyMall.@Tooltip": "Hides those pesky messages telling you to disable the Sky Mall HOTM perk when you want it enabled!", + "text.autoconfig.skyblocker.option.messages.hideMimicKill": "Hide Mimic Kill Messages", + "text.autoconfig.skyblocker.option.messages.hideMimicKill.@Tooltip": "Filters the \"Mimic dead!\" and \"Mimic killed!\" messages from chat.", "text.autoconfig.skyblocker.category.slayer": "Slayers", "text.autoconfig.skyblocker.option.slayer.vampireSlayer": "Vampire Slayer", "text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableEffigyWaypoints": "Enable Effigy Waypoints", From 2b1239950626b4480b0ac7120f4c8429dfaf8b68 Mon Sep 17 00:00:00 2001 From: Emirlol <81419447+Emirlol@users.noreply.github.com> Date: Sun, 7 Jan 2024 02:58:21 +0300 Subject: [PATCH 02/27] Improved time score calculation --- .../skyblocker/skyblock/dungeon/DungeonScore.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index ebec99dc5c..6016813c07 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -35,7 +35,6 @@ public class DungeonScore { private static final Pattern SECRETS_PATTERN = Pattern.compile("Secrets Found: (?\\d+\\.?\\d*)%"); private static final Pattern PUZZLES_PATTERN = Pattern.compile(".+?(?=:): \\[(?.)](?: \\(\\w+\\))?"); private static final Pattern PUZZLE_COUNT_PATTERN = Pattern.compile("Puzzles: \\((?\\d+)\\)"); - private static final Pattern TIME_PATTERN = Pattern.compile("Time: (?:(?\\d+(?=h))?h? ?(?\\d+(?=m))?m? ?(?\\d+(?=s))s|Soon!)"); private static final Pattern CRYPTS_PATTERN = Pattern.compile("Crypts: (?\\d+)"); private static final Pattern COMPLETED_ROOMS_PATTERN = Pattern.compile(" *Completed Rooms: (?\\d+)"); private static final Pattern DUNGEON_START_PATTERN = Pattern.compile("(?:Auto-closing|Starting) in: \\d:\\d+"); @@ -49,6 +48,7 @@ public class DungeonScore { //Caching the dungeon start state to prevent unnecessary scoreboard pattern matching after dungeon starts private static boolean isDungeonStarted; private static boolean isMayorPaul; + private static long startingTime; private static final HashMap floorRequirements = new HashMap<>(Map.ofEntries( Map.entry("E", new FloorRequirement(30, 1200)), Map.entry("F1", new FloorRequirement(30, 600)), @@ -135,6 +135,7 @@ private static void onDungeonStart() { isDungeonStarted = true; puzzleCount = getPuzzleCount(); isMayorPaul = Utils.getMayor().equals("Paul"); + startingTime = System.currentTimeMillis(); } private static int calculateScore() { @@ -158,16 +159,8 @@ private static int calculateExploreScore() { } private static int calculateTimeScore() { - Matcher timeMatcher = PlayerListMgr.regexAt(45, TIME_PATTERN); - if (timeMatcher == null) { - LOGGER.error("Time pattern doesn't match"); - return 0; - } int score = 100; - int hours = Optional.ofNullable(timeMatcher.group("hours")).map(Integer::parseInt).orElse(0); - int minutes = Optional.ofNullable(timeMatcher.group("minutes")).map(Integer::parseInt).orElse(0); - int seconds = Optional.ofNullable(timeMatcher.group("seconds")).map(Integer::parseInt).orElse(0); - int timeSpent = hours * 3600 + minutes * 60 + seconds; + int timeSpent = (int) (System.currentTimeMillis() - startingTime) / 1000; int timeRequirement = floorRequirements.get(currentFloor).timeLimit; if (timeSpent < timeRequirement) return score; From 6a350f931609432bd93362b2dbdc26e23450e2b8 Mon Sep 17 00:00:00 2001 From: Emirlol <81419447+Emirlol@users.noreply.github.com> Date: Sun, 14 Jan 2024 18:21:20 +0300 Subject: [PATCH 03/27] Fixed most things and cleaned up code --- .../mixin/ClientPlayNetworkHandlerMixin.java | 19 +- .../skyblock/dungeon/DungeonScore.java | 596 ++++++++++-------- .../java/de/hysky/skyblocker/utils/Utils.java | 1 - 3 files changed, 353 insertions(+), 263 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java index 4015dfa5b6..b3fc871b16 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java @@ -1,19 +1,25 @@ package de.hysky.skyblocker.mixin; import com.llamalad7.mixinextras.injector.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import de.hysky.skyblocker.skyblock.FishingHelper; +import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; import de.hysky.skyblocker.skyblock.waypoint.MythologicalRitual; import de.hysky.skyblocker.utils.Utils; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityStatuses; import net.minecraft.entity.ItemEntity; import net.minecraft.entity.LivingEntity; +import net.minecraft.network.packet.s2c.play.EntityStatusS2CPacket; import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; import net.minecraft.util.Identifier; - +import net.minecraft.world.World; import org.slf4j.Logger; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -49,12 +55,12 @@ public abstract class ClientPlayNetworkHandlerMixin { private boolean skyblocker$cancelTeamWarning(Logger instance, String format, Object... arg) { return !Utils.isOnHypixel(); } - + @WrapWithCondition(method = { "onScoreboardScoreUpdate", "onScoreboardScoreReset" }, at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) private boolean skyblocker$cancelUnknownScoreboardObjectiveWarnings(Logger instance, String message, Object objectiveName) { return !Utils.isOnHypixel(); } - + @WrapWithCondition(method = "warnOnUnknownPayload", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) private boolean skyblocker$dropBadlionPacketWarnings(Logger instance, String message, Object identifier) { return !(Utils.isOnHypixel() && ((Identifier) identifier).getNamespace().equals("badlion")); @@ -64,4 +70,11 @@ public abstract class ClientPlayNetworkHandlerMixin { private void skyblocker$onParticle(ParticleS2CPacket packet, CallbackInfo ci) { MythologicalRitual.onParticle(packet); } + + @WrapOperation(method = "onEntityStatus", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/EntityStatusS2CPacket;getEntity(Lnet/minecraft/world/World;)Lnet/minecraft/entity/Entity;")) + private Entity skyblocker$onEntityDeath(EntityStatusS2CPacket packet, World world, Operation original) { + Entity entity = original.call(packet, world); + if (packet.getStatus() == EntityStatuses.PLAY_DEATH_SOUND_OR_ADD_PROJECTILE_HIT_PARTICLES) DungeonScore.handleEntityDeath(entity); + return entity; + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index 6016813c07..0334290d40 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -1,21 +1,26 @@ package de.hysky.skyblocker.skyblock.dungeon; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.utils.ApiUtils; import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.Http; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.scheduler.MessageScheduler; import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; -import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents; import net.minecraft.client.MinecraftClient; import net.minecraft.entity.Entity; import net.minecraft.entity.mob.ZombieEntity; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtElement; import net.minecraft.sound.SoundEvents; import net.minecraft.util.collection.DefaultedList; import org.slf4j.Logger; @@ -23,265 +28,338 @@ import java.util.HashMap; import java.util.Map; -import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.StreamSupport; public class DungeonScore { - private static final SkyblockerConfig.DungeonScore SCORE_CONFIG = SkyblockerConfigManager.get().locations.dungeons.dungeonScore; - private static final SkyblockerConfig.MimicMessages MIMIC_MESSAGES_CONFIG = SkyblockerConfigManager.get().locations.dungeons.mimicMessages; - private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Dungeon Score"); - private static final Pattern CLEARED_PATTERN = Pattern.compile("Cleared: (?\\d+)%.*"); - private static final Pattern SECRETS_PATTERN = Pattern.compile("Secrets Found: (?\\d+\\.?\\d*)%"); - private static final Pattern PUZZLES_PATTERN = Pattern.compile(".+?(?=:): \\[(?.)](?: \\(\\w+\\))?"); - private static final Pattern PUZZLE_COUNT_PATTERN = Pattern.compile("Puzzles: \\((?\\d+)\\)"); - private static final Pattern CRYPTS_PATTERN = Pattern.compile("Crypts: (?\\d+)"); - private static final Pattern COMPLETED_ROOMS_PATTERN = Pattern.compile(" *Completed Rooms: (?\\d+)"); - private static final Pattern DUNGEON_START_PATTERN = Pattern.compile("(?:Auto-closing|Starting) in: \\d:\\d+"); - private static final Pattern FLOOR_PATTERN = Pattern.compile(".*?(?=T)The Catacombs \\((?[EFM]\\D*\\d*)\\)"); - private static final Pattern DEATHS_PATTERN = Pattern.compile("Team Deaths: (?\\d+)"); - private static String currentFloor; - private static boolean sent270; - private static boolean sent300; - private static boolean isMimicKilled; - private static int puzzleCount; - //Caching the dungeon start state to prevent unnecessary scoreboard pattern matching after dungeon starts - private static boolean isDungeonStarted; - private static boolean isMayorPaul; - private static long startingTime; - private static final HashMap floorRequirements = new HashMap<>(Map.ofEntries( - Map.entry("E", new FloorRequirement(30, 1200)), - Map.entry("F1", new FloorRequirement(30, 600)), - Map.entry("F2", new FloorRequirement(40, 600)), - Map.entry("F3", new FloorRequirement(50, 600)), - Map.entry("F4", new FloorRequirement(60, 720)), - Map.entry("F5", new FloorRequirement(70, 600)), - Map.entry("F6", new FloorRequirement(85, 720)), - Map.entry("F7", new FloorRequirement(100, 840)), - Map.entry("M1", new FloorRequirement(100, 480)), - Map.entry("M2", new FloorRequirement(100, 480)), - Map.entry("M3", new FloorRequirement(100, 480)), - Map.entry("M4", new FloorRequirement(100, 480)), - Map.entry("M5", new FloorRequirement(100, 480)), - Map.entry("M6", new FloorRequirement(100, 600)), - Map.entry("M7", new FloorRequirement(100, 840)) - )); - - public static void init() { - Scheduler.INSTANCE.scheduleCyclic(DungeonScore::tick, 20); - ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset()); - ServerLivingEntityEvents.AFTER_DEATH.register((entity, source) -> { - if (isEntityMimic(entity)) { - if (MIMIC_MESSAGES_CONFIG.sendMimicMessages) MessageScheduler.INSTANCE.sendMessageAfterCooldown(MIMIC_MESSAGES_CONFIG.mimicMessage); - setMimicKilled(true); - } - }); - } - - public static void tick() { - MinecraftClient client = MinecraftClient.getInstance(); - if (!Utils.isInDungeons() || client.player == null) { - reset(); - return; - } - if (!isDungeonStarted) { - if (checkIfDungeonStarted()) onDungeonStart(); - return; - } - int score = calculateScore(); - if (SCORE_CONFIG.enableDungeonScore270 && !sent270 && score >= 270 && score < 300) { - MessageScheduler.INSTANCE.sendMessageAfterCooldown("/pc " + Constants.PREFIX.get().getString() + SCORE_CONFIG.dungeonScore270Message.replaceAll("\\[score]", "270")); - client.player.playSound(SoundEvents.BLOCK_NOTE_BLOCK_PLING.value(), 1f, 0.1f); - sent270 = true; - } - if (SCORE_CONFIG.enableDungeonScore300 && !sent300 && score >= 300) { - MessageScheduler.INSTANCE.sendMessageAfterCooldown("/pc " + Constants.PREFIX.get().getString() + SCORE_CONFIG.dungeonScore300Message.replaceAll("\\[score]", "300")); - client.player.playSound(SoundEvents.BLOCK_NOTE_BLOCK_PLING.value(), 1f, 1f); - sent300 = true; - } - } - - public static boolean isEntityMimic(Entity entity) { - if (!(entity instanceof ZombieEntity zombie)) return false; - if (!zombie.isBaby()) return false; - try { - DefaultedList armor = (DefaultedList) zombie.getArmorItems(); - if (armor.isEmpty()) return false; - NbtCompound helmetNbt = armor.get(3).getNbt(); - if (helmetNbt == null) return false; - return helmetNbt.getCompound("SkullOwner") - .getCompound("Properties") - .getList("textures", NbtElement.COMPOUND_TYPE) - .getCompound(0).getString("Value") - .equals("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTE5YzEyNTQzYmM3NzkyNjA1ZWY2OGUxZjg3NDlhZThmMmEzODFkOTA4NWQ0ZDRiNzgwYmExMjgyZDM1OTdhMCJ9fX0K"); - } catch (NullPointerException e) { - return false; - } catch (ClassCastException f) { - f.printStackTrace(); - return false; - } - } - - private static boolean checkIfDungeonStarted() { - for (String sidebarLine : Utils.STRING_SCOREBOARD) { - Matcher matcher = DUNGEON_START_PATTERN.matcher(sidebarLine); - if (matcher.matches()) return false; - } - return true; - } - - private static void onDungeonStart() { - setCurrentFloor(); - isDungeonStarted = true; - puzzleCount = getPuzzleCount(); - isMayorPaul = Utils.getMayor().equals("Paul"); - startingTime = System.currentTimeMillis(); - } - - private static int calculateScore() { - int timeScore = calculateTimeScore(); - int exploreScore = calculateExploreScore(); - int skillScore = calculateSkillScore(); - int paulScore = isMayorPaul ? 10 : 0; - int cryptsScore = Math.min(getCrypts(), 5); - int mimicScore = isMimicKilled ? 2 : 0; - int totalScore = timeScore + exploreScore + skillScore + paulScore + cryptsScore + mimicScore; - //Will be this way until ready for pr, so it's easy to debug. - LOGGER.info("Total Score: {} (Time: {}, Explore: {}, Skill: {}, Paul: {}, Crypts: {}, Mimic: {})", totalScore, timeScore, exploreScore, skillScore, paulScore, cryptsScore, mimicScore); - return totalScore; - } - - private static int calculateExploreScore() { - int completedRoomScore = (int) Math.floor(60.0 * getCompletedRooms() / getTotalRooms()); - int percentageRequirement = floorRequirements.get(currentFloor).percentage; - int secretsScore = (int) Math.floor(40 * Math.min(percentageRequirement, getSecretsPercentage()) / percentageRequirement); - return completedRoomScore + secretsScore; - } - - private static int calculateTimeScore() { - int score = 100; - int timeSpent = (int) (System.currentTimeMillis() - startingTime) / 1000; - int timeRequirement = floorRequirements.get(currentFloor).timeLimit; - if (timeSpent < timeRequirement) return score; - - double timePastRequirement = ((double) (timeSpent - timeRequirement) / timeRequirement) * 100; - if (timePastRequirement >= 0 && timePastRequirement < 20) { - score -= (int) timePastRequirement / 2; - } else if (timePastRequirement >= 20 && timePastRequirement < 40) { - score -= (int) (10 + (timePastRequirement - 20) / 4); - } else if (timePastRequirement >= 40 && timePastRequirement < 50) { - score -= (int) (15 + (timePastRequirement - 40) / 5); - } else if (timePastRequirement >= 50 && timePastRequirement < 60) { - score -= (int) (17 + (timePastRequirement - 50) / 6); - } else if (timePastRequirement >= 60) { - score -= (int) (18 + (2.0 / 3.0) + (timePastRequirement - 60) / 7); - } - return score; - } - - private static int calculateSkillScore() { - return 20 + (int) Math.floor(80.0 * getCompletedRooms() / getTotalRooms()) - (2 * getDeathCount()) - (10 * getFailedPuzzles()); - } - - private static void reset() { - sent270 = false; - sent300 = false; - isDungeonStarted = false; - isMimicKilled = false; - isMayorPaul = false; - puzzleCount = 0; - currentFloor = ""; - } - - public static void setMimicKilled(boolean killed) { - isMimicKilled = killed; - } - - private static int getTotalRooms() { - return (int) Math.round((getCompletedRooms()) / getClearPercentage()); //Clear% rounds to the closest integer so it can be off by 0.5% at most, this should be accurate enough - } - - private static int getCompletedRooms() { - Matcher completedRoomsMatcher = PlayerListMgr.regexAt(43, COMPLETED_ROOMS_PATTERN); - if (completedRoomsMatcher == null) { - LOGGER.error("Completed rooms pattern doesn't match"); - return 0; - } - return Integer.parseInt(completedRoomsMatcher.group("rooms")); - } - - private static double getClearPercentage() { - for (String sidebarLine : Utils.STRING_SCOREBOARD) { - Matcher clearMatcher = CLEARED_PATTERN.matcher(sidebarLine); - if (!clearMatcher.matches()) continue; - return Double.parseDouble(clearMatcher.group("cleared")) / 100; - } - LOGGER.error("Clear pattern doesn't match"); - return 0; - } - - private static int getDeathCount() { - Matcher matcher = PlayerListMgr.regexAt(25, DEATHS_PATTERN); - if (matcher == null) { - LOGGER.error("Death count pattern doesn't match"); - return 0; - } - //TODO: Turn this into a map of players and their deathcounts, get party members' pets, check if they have spirit pet, if they have it reduce their death count by 0.5 - return Integer.parseInt(matcher.group("deaths")); - } - - private static int getPuzzleCount() { - Matcher matcher = PlayerListMgr.regexAt(47, PUZZLE_COUNT_PATTERN); - if (matcher == null) { - LOGGER.error("Puzzle count pattern doesn't match"); - return 0; - } - return Integer.parseInt(matcher.group("count")); - } - - //Might be replaced to look for puzzle fail messages on chat instead of playerlist - private static int getFailedPuzzles() { - int failedPuzzles = 0; - for (int index = 0; index < puzzleCount; index++) { - Matcher puzzleMatcher = PlayerListMgr.regexAt(48 + index, PUZZLES_PATTERN); - if (puzzleMatcher == null) { - LOGGER.error("Puzzle pattern doesn't match"); - return 0; - } - if (puzzleMatcher.group("state").equals("✖")) failedPuzzles++; - } - return failedPuzzles; - } - - private static double getSecretsPercentage() { - Matcher matcher = PlayerListMgr.regexAt(44, SECRETS_PATTERN); - if (matcher == null) { - LOGGER.error("Secrets pattern doesn't match"); - return 0; - } - return Double.parseDouble(matcher.group("secper")); - } - - private static int getCrypts() { - Matcher matcher = PlayerListMgr.regexAt(33, CRYPTS_PATTERN); - if (matcher == null) { - LOGGER.error("Crypts pattern doesn't match"); - return 0; - } - return Integer.parseInt(matcher.group("crypts")); - } - - public static void setCurrentFloor() { - for (String sidebarLine : Utils.STRING_SCOREBOARD) { - Matcher floorMatcher = FLOOR_PATTERN.matcher(sidebarLine); - if (!floorMatcher.matches()) continue; - currentFloor = floorMatcher.group("floor"); - return; - } - LOGGER.error("Floor pattern doesn't match"); - } - - record FloorRequirement(int percentage, int timeLimit) { - } + private static final SkyblockerConfig.DungeonScore SCORE_CONFIG = SkyblockerConfigManager.get().locations.dungeons.dungeonScore; + private static final SkyblockerConfig.MimicMessages MIMIC_MESSAGES_CONFIG = SkyblockerConfigManager.get().locations.dungeons.mimicMessages; + private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Dungeon Score"); + //Scoreboard patterns + private static final Pattern CLEARED_PATTERN = Pattern.compile("Cleared: (?\\d+)%.*"); + private static final Pattern DUNGEON_START_PATTERN = Pattern.compile("(?:Auto-closing|Starting) in: \\d:\\d+"); + private static final Pattern FLOOR_PATTERN = Pattern.compile(".*?(?=T)The Catacombs \\((?[EFM]\\D*\\d*)\\)"); + //Playerlist patterns + private static final Pattern SECRETS_PATTERN = Pattern.compile("Secrets Found: (?\\d+\\.?\\d*)%"); + private static final Pattern PUZZLES_PATTERN = Pattern.compile(".+?(?=:): \\[(?.)](?: \\(\\w+\\))?"); + private static final Pattern PUZZLE_COUNT_PATTERN = Pattern.compile("Puzzles: \\((?\\d+)\\)"); + private static final Pattern CRYPTS_PATTERN = Pattern.compile("Crypts: (?\\d+)"); + private static final Pattern COMPLETED_ROOMS_PATTERN = Pattern.compile(" *Completed Rooms: (?\\d+)"); + //Chat patterns + private static final Pattern DEATHS_PATTERN = Pattern.compile(".*?\u2620 (?\\S+) .*"); + //Other patterns + private static final Pattern MIMIC_FLOOR_FILTER_PATTERN = Pattern.compile("[EFM][12345]?"); + + private static String currentFloor; + private static boolean sent270; + private static boolean sent300; + private static boolean isMimicKilled; + private static int puzzleCount; + private static boolean isDungeonStarted; + private static boolean isMayorPaul; + private static long startingTime; + private static int deathCount; + private static boolean firstDeathHasSpiritPet; + private static boolean bloodRoomCompleted; + private static final Map SpiritPetCache = new HashMap<>(); + + public static void init() { + Scheduler.INSTANCE.scheduleCyclic(DungeonScore::tick, 20); + SkyblockEvents.LEAVE.register(SpiritPetCache::clear); + ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset()); + ClientReceiveMessageEvents.GAME.register((message, overlay) -> { + String str = message.getString(); + checkMessageForDeaths(str); + checkMessageForWatcher(str); + }); + } + + public static void tick() { + MinecraftClient client = MinecraftClient.getInstance(); + if (!Utils.isInDungeons() || client.player == null) { + reset(); + return; + } + if (!isDungeonStarted) { + if (checkIfDungeonStarted()) onDungeonStart(); + return; + } + int score = calculateScore(); + if (!sent270 && score >= 270 && score < 300) { + if (SCORE_CONFIG.enableDungeonScore270Message) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown(SCORE_CONFIG.dungeonScore270Message.replaceAll("\\[score]", "270")); + } + if (SCORE_CONFIG.enableDungeonScore270Title) { + client.inGameHud.setDefaultTitleFade(); + client.inGameHud.setTitle(Constants.PREFIX.get().append(SCORE_CONFIG.dungeonScore270Message.replaceAll("\\[score]", "270"))); + } + if (SCORE_CONFIG.enableDungeonScore270Sound) { + client.player.playSound(SoundEvents.BLOCK_NOTE_BLOCK_PLING.value(), 100f, 0.1f); + } + sent270 = true; + } + if (!sent300 && score >= 300) { + if (SCORE_CONFIG.enableDungeonScore300Message) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown(SCORE_CONFIG.dungeonScore300Message.replaceAll("\\[score]", "300")); + } + if (SCORE_CONFIG.enableDungeonScore300Title) { + client.inGameHud.setDefaultTitleFade(); + client.inGameHud.setTitle(Constants.PREFIX.get().append(SCORE_CONFIG.dungeonScore300Message.replaceAll("\\[score]", "300"))); + } + if (SCORE_CONFIG.enableDungeonScore300Sound) { + client.player.playSound(SoundEvents.BLOCK_NOTE_BLOCK_PLING.value(), 100f, 0.1f); + } + sent300 = true; + } + } + + private static void reset() { + sent270 = false; + sent300 = false; + isDungeonStarted = false; + isMimicKilled = false; + isMayorPaul = false; + firstDeathHasSpiritPet = false; + deathCount = 0; + currentFloor = ""; + } + + private static void onDungeonStart() { + setCurrentFloor(); + isDungeonStarted = true; + puzzleCount = getPuzzleCount(); + isMayorPaul = Utils.getMayor().equals("Paul"); + startingTime = System.currentTimeMillis(); + } + + private static int calculateScore() { + int timeScore = calculateTimeScore(); + int exploreScore = calculateExploreScore(); + int skillScore = calculateSkillScore(); + int bonusScore = calculateBonusScore(); + int totalScore = timeScore + exploreScore + skillScore + bonusScore; + if (currentFloor.equals("E")) totalScore = (int) (totalScore * 0.7); + //Will be this way until ready for pr, so it's easy to debug. + LOGGER.info("Total Score: {} (Time: {}, Explore: {}, Skill: {}, Bonus: {})", totalScore, timeScore, exploreScore, skillScore, bonusScore); + return totalScore; + } + + private static int calculateSkillScore() { + int extraCompletedRooms = 0; //This is needed for calculating the score before going in, so we have the result sooner + if (!DungeonManager.isInBoss()) extraCompletedRooms = bloodRoomCompleted ? 1 : 2; + return 20 + (int) Math.floor(80.0 * (getCompletedRooms() + extraCompletedRooms) / getTotalRooms()) - getPuzzlePenalty() - getDeathScorePenalty(); + } + + private static int calculateExploreScore() { + int extraCompletedRooms = 0; + if (!DungeonManager.isInBoss()) extraCompletedRooms = bloodRoomCompleted ? 1 : 2; + int completedRoomScore = (int) Math.floor(60.0 * (getCompletedRooms() + extraCompletedRooms) / getTotalRooms()); + int percentageRequirement = FloorRequirement.valueOf(currentFloor).percentage; + int secretsScore = (int) Math.floor(40 * Math.min(percentageRequirement, getSecretsPercentage()) / percentageRequirement); + return completedRoomScore + secretsScore; + } + + private static int calculateTimeScore() { + int score = 100; + int timeSpent = (int) (System.currentTimeMillis() - startingTime) / 1000; + int timeRequirement = FloorRequirement.valueOf(currentFloor).timeLimit; + if (timeSpent < timeRequirement) return score; + + double timePastRequirement = ((double) (timeSpent - timeRequirement) / timeRequirement) * 100; + if (timePastRequirement >= 0 && timePastRequirement < 20) { + score -= (int) timePastRequirement / 2; + } else if (timePastRequirement >= 20 && timePastRequirement < 40) { + score -= (int) (10 + (timePastRequirement - 20) / 4); + } else if (timePastRequirement >= 40 && timePastRequirement < 50) { + score -= (int) (15 + (timePastRequirement - 40) / 5); + } else if (timePastRequirement >= 50 && timePastRequirement < 60) { + score -= (int) (17 + (timePastRequirement - 50) / 6); + } else if (timePastRequirement >= 60) { + score -= (int) (18 + (2.0 / 3.0) + (timePastRequirement - 60) / 7); + } + return score; + } + + private static int calculateBonusScore() { + int paulScore = isMayorPaul ? 10 : 0; + int cryptsScore = Math.min(getCrypts(), 5); + int mimicScore = isMimicKilled ? 2 : 0; + if (getSecretsPercentage() >= 100 && !MIMIC_FLOOR_FILTER_PATTERN.matcher(currentFloor).matches()) mimicScore = 2; //If mimic kill is not announced but all secrets are found, mimic must've been killed + return paulScore + cryptsScore + mimicScore; + } + + private static boolean checkIfDungeonStarted() { + return Utils.STRING_SCOREBOARD.stream().anyMatch(s -> DUNGEON_START_PATTERN.matcher(s).matches()); + } + + public static boolean isEntityMimic(Entity entity) { + if (!Utils.isInDungeons()) return false; + if (MIMIC_FLOOR_FILTER_PATTERN.matcher(currentFloor).matches()) return false; + if (entity == null) return false; + if (!(entity instanceof ZombieEntity zombie)) return false; + if (!zombie.isBaby()) return false; + try { + DefaultedList armor = (DefaultedList) zombie.getArmorItems(); + return armor.stream().allMatch(ItemStack::isEmpty); + } catch (NullPointerException e) { + return false; + } catch (ClassCastException f) { + f.printStackTrace(); + return false; + } + } + + public static void handleEntityDeath(Entity entity) { + if (isMimicKilled) return; + if (!isEntityMimic(entity)) return; + isMimicKilled = true; + } + + public static void setMimicKilled(boolean state) { + isMimicKilled = state; + } + + private static int getTotalRooms() { + int completedRooms = getCompletedRooms(); + return (int) Math.round(completedRooms / getClearPercentage()); + } + + private static int getCompletedRooms() { + Matcher matcher = PlayerListMgr.regexAt(43, COMPLETED_ROOMS_PATTERN); + return matcher != null ? Integer.parseInt(matcher.group("rooms")) : 0; + } + + private static double getClearPercentage() { + for (String sidebarLine : Utils.STRING_SCOREBOARD) { + Matcher clearMatcher = CLEARED_PATTERN.matcher(sidebarLine); + if (!clearMatcher.matches()) continue; + return Double.parseDouble(clearMatcher.group("cleared")) / 100.0; + } + LOGGER.error("Clear pattern doesn't match"); + return 0; + } + + private static int getDeathScorePenalty() { + return deathCount * 2 - (firstDeathHasSpiritPet ? 1 : 0); + } + + private static int getPuzzleCount() { + Matcher matcher = PlayerListMgr.regexAt(47, PUZZLE_COUNT_PATTERN); + return matcher != null ? Integer.parseInt(matcher.group("count")) : 0; + } + + //Possible states: ✖, ✦, ✔ + private static int getPuzzlePenalty() { + int incompletePuzzles = 0; + for (int index = 0; index < puzzleCount; index++) { + Matcher puzzleMatcher = PlayerListMgr.regexAt(48 + index, PUZZLES_PATTERN); + if (puzzleMatcher == null) break; + if (puzzleMatcher.group("state").matches("[✖✦]")) incompletePuzzles++; + } + return incompletePuzzles * 10; + } + + private static double getSecretsPercentage() { + Matcher matcher = PlayerListMgr.regexAt(44, SECRETS_PATTERN); + return matcher != null ? Double.parseDouble(matcher.group("secper")) : 0; + } + + private static int getCrypts() { + Matcher matcher = PlayerListMgr.regexAt(33, CRYPTS_PATTERN); + if (matcher == null) matcher = PlayerListMgr.regexAt(32, CRYPTS_PATTERN); //If class milestone 9 is reached, crypts goes up by 1 + return matcher != null ? Integer.parseInt(matcher.group("crypts")) : 0; + } + + public static boolean hasSpiritPet(String name) { + return SpiritPetCache.computeIfAbsent(name, k -> { + String playeruuid = ApiUtils.name2Uuid(name); + try (Http.ApiResponse response = Http.sendHypixelRequest("skyblock/profiles", "?uuid=" + playeruuid)) { + if (!response.ok()) throw new IllegalStateException("Failed to get profile uuid for player " + name + "! Response: " + response.content()); + JsonObject responseJson = JsonParser.parseString(response.content()).getAsJsonObject(); + + JsonObject player = StreamSupport.stream(responseJson.getAsJsonArray("profiles").spliterator(), false) + .map(JsonElement::getAsJsonObject) + .filter(profile -> profile.getAsJsonPrimitive("selected").getAsBoolean()) + .findFirst() + .orElseThrow(() -> new IllegalStateException("No selected profile found!?")) + .getAsJsonObject("members").entrySet().stream() + .filter(entry -> entry.getKey().equals(playeruuid)) + .map(Map.Entry::getValue) + .map(JsonElement::getAsJsonObject) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Player somehow not found inside their own profile!")); + + for (JsonElement element : player.getAsJsonObject("pets_data").getAsJsonArray("pets")) { + if (!element.getAsJsonObject().get("type").getAsString().equals("SPIRIT")) continue; + if (!element.getAsJsonObject().get("tier").getAsString().equals("LEGENDARY")) continue; + + return true; + } + } catch (Exception e) { + e.printStackTrace(); + LOGGER.error("[Skyblocker] Spirit pet lookup by name failed! Name: {} - Cause: {}", name, e.getMessage()); + } + return false; + }); + } + + private static void checkMessageForDeaths(String message) { + if (!Utils.isInDungeons()) return; + if (!message.startsWith("\u2620", 1)) return; + Matcher matcher = DEATHS_PATTERN.matcher(message); + if (!matcher.matches()) return; + deathCount++; + if (deathCount > 1) return; + final String whoDied = matcher.group("whodied").transform(s -> { + if (s.equals("You")) return MinecraftClient.getInstance().player.getName().getString(); //This will be wrong if the dead player is called 'You' but that's unlikely + else return s; + }); + CompletableFuture.supplyAsync(() -> hasSpiritPet(whoDied)) + .thenAccept(hasSpiritPet -> { + firstDeathHasSpiritPet = hasSpiritPet; + }); + } + + private static void checkMessageForWatcher(String message) { + if (message.equals("[BOSS] The Watcher: You have proven yourself. You may pass.")) bloodRoomCompleted = true; + } + + public static void setCurrentFloor() { + for (String sidebarLine : Utils.STRING_SCOREBOARD) { + Matcher floorMatcher = FLOOR_PATTERN.matcher(sidebarLine); + if (!floorMatcher.matches()) continue; + currentFloor = floorMatcher.group("floor"); + return; + } + LOGGER.error("Floor pattern doesn't match"); + } + + enum FloorRequirement { + E(30, 1200), + F1(30, 600), + F2(40, 600), + F3(50, 600), + F4(60, 720), + F5(70, 600), + F6(85, 720), + F7(100, 840), + M1(100, 480), + M2(100, 480), + M3(100, 480), + M4(100, 480), + M5(100, 480), + M6(100, 600), + M7(100, 840); + + private final int percentage; + private final int timeLimit; + + FloorRequirement(int percentage, int timeLimit) { + this.percentage = percentage; + this.timeLimit = timeLimit; + } + } } diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 1fc718be19..3336a0a714 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -400,7 +400,6 @@ private static void initializeMayorCache() { JsonObject json = JsonParser.parseString(Http.sendGetRequest("https://api.hypixel.net/v2/resources/skyblock/election")).getAsJsonObject(); if (json.get("success").getAsBoolean()) { mayor = json.get("mayor").getAsJsonObject().get("name").getAsString(); - System.out.println(mayor); } else { throw new IOException("API call for mayor failed: " + json.get("cause").getAsString()); } From cbdaa7a2b69ed2b17e36548b707400f416154c4f Mon Sep 17 00:00:00 2001 From: Emirlol <81419447+Emirlol@users.noreply.github.com> Date: Mon, 15 Jan 2024 19:05:55 +0300 Subject: [PATCH 04/27] Added dungeon score to the hud and cleaned up code --- .../de/hysky/skyblocker/SkyblockerMod.java | 1 + .../skyblock/dungeon/DungeonScore.java | 73 +++++++++++-------- .../skyblock/dungeon/DungeonScoreHUD.java | 38 ++++++++++ .../skyblock/filters/MimicFilter.java | 6 +- 4 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 83f41c0b52..4a2ff49763 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -110,6 +110,7 @@ public void onInitializeClient() { Waterboard.init(); DungeonScore.init(); PartyFinderScreen.initClass(); + DungeonScoreHUD.init(); ChestValue.init(); FireFreezeStaffTimer.init(); GuardianHealth.init(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index 0334290d40..0a9aec5025 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -56,13 +56,14 @@ public class DungeonScore { private static boolean sent270; private static boolean sent300; private static boolean isMimicKilled; - private static int puzzleCount; - private static boolean isDungeonStarted; + private static boolean dungeonStarted; private static boolean isMayorPaul; - private static long startingTime; - private static int deathCount; private static boolean firstDeathHasSpiritPet; private static boolean bloodRoomCompleted; + private static long startingTime; + private static int puzzleCount; + private static int deathCount; + private static int score; private static final Map SpiritPetCache = new HashMap<>(); public static void init() { @@ -70,6 +71,7 @@ public static void init() { SkyblockEvents.LEAVE.register(SpiritPetCache::clear); ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset()); ClientReceiveMessageEvents.GAME.register((message, overlay) -> { + if (!Utils.isInDungeons() || !dungeonStarted) return; String str = message.getString(); checkMessageForDeaths(str); checkMessageForWatcher(str); @@ -82,11 +84,11 @@ public static void tick() { reset(); return; } - if (!isDungeonStarted) { + if (!dungeonStarted) { if (checkIfDungeonStarted()) onDungeonStart(); return; } - int score = calculateScore(); + score = calculateScore(); if (!sent270 && score >= 270 && score < 300) { if (SCORE_CONFIG.enableDungeonScore270Message) { MessageScheduler.INSTANCE.sendMessageAfterCooldown(SCORE_CONFIG.dungeonScore270Message.replaceAll("\\[score]", "270")); @@ -116,19 +118,23 @@ public static void tick() { } private static void reset() { + currentFloor = ""; sent270 = false; sent300 = false; - isDungeonStarted = false; isMimicKilled = false; + dungeonStarted = false; isMayorPaul = false; firstDeathHasSpiritPet = false; + bloodRoomCompleted = false; + startingTime = 0L; + puzzleCount = 0; deathCount = 0; - currentFloor = ""; + score = 0; } private static void onDungeonStart() { setCurrentFloor(); - isDungeonStarted = true; + dungeonStarted = true; puzzleCount = getPuzzleCount(); isMayorPaul = Utils.getMayor().equals("Paul"); startingTime = System.currentTimeMillis(); @@ -147,15 +153,11 @@ private static int calculateScore() { } private static int calculateSkillScore() { - int extraCompletedRooms = 0; //This is needed for calculating the score before going in, so we have the result sooner - if (!DungeonManager.isInBoss()) extraCompletedRooms = bloodRoomCompleted ? 1 : 2; - return 20 + (int) Math.floor(80.0 * (getCompletedRooms() + extraCompletedRooms) / getTotalRooms()) - getPuzzlePenalty() - getDeathScorePenalty(); + return 20 + (int) Math.floor(80.0 * (getCompletedRooms() + getExtraCompletedRooms()) / getTotalRooms()) - getPuzzlePenalty() - getDeathScorePenalty(); } private static int calculateExploreScore() { - int extraCompletedRooms = 0; - if (!DungeonManager.isInBoss()) extraCompletedRooms = bloodRoomCompleted ? 1 : 2; - int completedRoomScore = (int) Math.floor(60.0 * (getCompletedRooms() + extraCompletedRooms) / getTotalRooms()); + int completedRoomScore = (int) Math.floor(60.0 * (getCompletedRooms() + getExtraCompletedRooms()) / getTotalRooms()); int percentageRequirement = FloorRequirement.valueOf(currentFloor).percentage; int secretsScore = (int) Math.floor(40 * Math.min(percentageRequirement, getSecretsPercentage()) / percentageRequirement); return completedRoomScore + secretsScore; @@ -168,18 +170,11 @@ private static int calculateTimeScore() { if (timeSpent < timeRequirement) return score; double timePastRequirement = ((double) (timeSpent - timeRequirement) / timeRequirement) * 100; - if (timePastRequirement >= 0 && timePastRequirement < 20) { - score -= (int) timePastRequirement / 2; - } else if (timePastRequirement >= 20 && timePastRequirement < 40) { - score -= (int) (10 + (timePastRequirement - 20) / 4); - } else if (timePastRequirement >= 40 && timePastRequirement < 50) { - score -= (int) (15 + (timePastRequirement - 40) / 5); - } else if (timePastRequirement >= 50 && timePastRequirement < 60) { - score -= (int) (17 + (timePastRequirement - 50) / 6); - } else if (timePastRequirement >= 60) { - score -= (int) (18 + (2.0 / 3.0) + (timePastRequirement - 60) / 7); - } - return score; + if (timePastRequirement < 20) return score - (int) timePastRequirement / 2; + if (timePastRequirement < 40) return score - (int) (10 + (timePastRequirement - 20) / 4); + if (timePastRequirement < 50) return score - (int) (15 + (timePastRequirement - 40) / 5); + if (timePastRequirement < 60) return score - (int) (17 + (timePastRequirement - 50) / 6); + return score - (int) (18 + (2.0 / 3.0) + (timePastRequirement - 60) / 7); } private static int calculateBonusScore() { @@ -191,7 +186,7 @@ private static int calculateBonusScore() { } private static boolean checkIfDungeonStarted() { - return Utils.STRING_SCOREBOARD.stream().anyMatch(s -> DUNGEON_START_PATTERN.matcher(s).matches()); + return Utils.STRING_SCOREBOARD.stream().noneMatch(s -> DUNGEON_START_PATTERN.matcher(s).matches()); } public static boolean isEntityMimic(Entity entity) { @@ -214,6 +209,7 @@ public static boolean isEntityMimic(Entity entity) { public static void handleEntityDeath(Entity entity) { if (isMimicKilled) return; if (!isEntityMimic(entity)) return; + if (MIMIC_MESSAGES_CONFIG.sendMimicMessages) MessageScheduler.INSTANCE.sendMessageAfterCooldown(MIMIC_MESSAGES_CONFIG.mimicMessage); isMimicKilled = true; } @@ -221,6 +217,8 @@ public static void setMimicKilled(boolean state) { isMimicKilled = state; } + //This is not very accurate at the beginning of the dungeon since clear percentage is rounded to the closest integer, so at lower percentages its effect on the result is quite high. + //For example: If clear percentage is 7% with a single room completed, it can be rounded from 6.5 or 7.49. In that range, the actual total room count can be either 14 or 15 while our result is 14. private static int getTotalRooms() { int completedRooms = getCompletedRooms(); return (int) Math.round(completedRooms / getClearPercentage()); @@ -231,6 +229,12 @@ private static int getCompletedRooms() { return matcher != null ? Integer.parseInt(matcher.group("rooms")) : 0; } + private static int getExtraCompletedRooms() { //This is needed for calculating the score before going in the boss room & completing the blood room, so we have the result sooner + if (!bloodRoomCompleted) return 2; + if (!DungeonManager.isInBoss()) return 1; + return 0; + } + private static double getClearPercentage() { for (String sidebarLine : Utils.STRING_SCOREBOARD) { Matcher clearMatcher = CLEARED_PATTERN.matcher(sidebarLine); @@ -306,7 +310,6 @@ public static boolean hasSpiritPet(String name) { } private static void checkMessageForDeaths(String message) { - if (!Utils.isInDungeons()) return; if (!message.startsWith("\u2620", 1)) return; Matcher matcher = DEATHS_PATTERN.matcher(message); if (!matcher.matches()) return; @@ -317,9 +320,7 @@ private static void checkMessageForDeaths(String message) { else return s; }); CompletableFuture.supplyAsync(() -> hasSpiritPet(whoDied)) - .thenAccept(hasSpiritPet -> { - firstDeathHasSpiritPet = hasSpiritPet; - }); + .thenAccept(hasSpiritPet -> firstDeathHasSpiritPet = hasSpiritPet); } private static void checkMessageForWatcher(String message) { @@ -336,6 +337,14 @@ public static void setCurrentFloor() { LOGGER.error("Floor pattern doesn't match"); } + public static int getScore() { + return score; + } + + public static boolean isDungeonStarted() { + return dungeonStarted; + } + enum FloorRequirement { E(30, 1200), F1(30, 600), diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java new file mode 100644 index 0000000000..9da1242678 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java @@ -0,0 +1,38 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class DungeonScoreHUD { + + public static void init() { + HudRenderCallback.EVENT.register(DungeonScoreHUD::onHudRender); + } + + private static void onHudRender(DrawContext context, float tickDelta) { + if (!Utils.isInDungeons() || !DungeonScore.isDungeonStarted()) return; + + int x = SkyblockerConfigManager.get().locations.dungeons.mapX; + int y = SkyblockerConfigManager.get().locations.dungeons.mapY; + int size = (int) (128 * SkyblockerConfigManager.get().locations.dungeons.mapScaling); + context.drawCenteredTextWithShadow(MinecraftClient.getInstance().textRenderer, + Text.literal("Score: ").append(formatScore(DungeonScore.getScore())), + x + (size >> 1), + y + size + 5, + 0x00FFFFFF); + } + + private static Text formatScore(int score) { + if (score < 100) return Text.literal(String.valueOf(score)).withColor(0xDC1A1A).append(Text.literal(" (D)").formatted(Formatting.GRAY)); + if (score < 160) return Text.literal(String.valueOf(score)).withColor(0x4141FF).append(Text.literal(" (C)").formatted(Formatting.GRAY)); + if (score < 230) return Text.literal(String.valueOf(score)).withColor(0x7FCC19).append(Text.literal(" (B)").formatted(Formatting.GRAY)); + if (score < 270) return Text.literal(String.valueOf(score)).withColor(0x7F3FB2).append(Text.literal(" (A)").formatted(Formatting.GRAY)); + if (score < 300) return Text.literal(String.valueOf(score)).withColor(0xF1E252).append(Text.literal(" (S)").formatted(Formatting.GRAY)); + return Text.literal(String.valueOf(score)).withColor(0xF1E252).formatted(Formatting.BOLD).append(Text.literal(" (S+)").formatted(Formatting.GRAY)); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java index 0fce5b2cb4..51482794f9 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java @@ -2,6 +2,7 @@ import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; +import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.chat.ChatFilterResult; import de.hysky.skyblocker.utils.chat.ChatPatternListener; import net.minecraft.text.Text; @@ -10,7 +11,7 @@ public class MimicFilter extends ChatPatternListener { public MimicFilter() { - super("(?:Mimic dead!|Mimic Killed!|\\$SKYTILS-DUNGEON-SCORE-MIMIC\\$|\\Q" + SkyblockerConfigManager.get().locations.dungeons.mimicMessages.mimicMessage + "\\E)$"); + super("(?:Mimic dead!?|Mimic Killed!|\\$SKYTILS-DUNGEON-SCORE-MIMIC\\$|\\Q" + SkyblockerConfigManager.get().locations.dungeons.mimicMessages.mimicMessage + "\\E)$"); } @Override @@ -20,7 +21,8 @@ public ChatFilterResult state() { @Override protected boolean onMatch(Text message, Matcher matcher) { + if (!Utils.isInDungeons()) return false; DungeonScore.setMimicKilled(true); - return false; + return true; } } From 52eb325a71664cd48b527dde13ba1df64a3b4777 Mon Sep 17 00:00:00 2001 From: Emirlol <81419447+Emirlol@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:12:39 +0300 Subject: [PATCH 05/27] Made the score hud element's location and size configurable --- .../skyblocker/config/SkyblockerConfig.java | 12 +++++ .../config/categories/DungeonsCategory.java | 18 +++++++ .../skyblocker/mixin/InGameHudMixin.java | 10 ++-- .../skyblock/dungeon/DungeonMap.java | 12 +---- .../dungeon/DungeonMapConfigScreen.java | 48 ++++++++++++++----- .../skyblock/dungeon/DungeonScoreHUD.java | 41 ++++++++-------- .../assets/skyblocker/lang/en_us.json | 4 +- 7 files changed, 97 insertions(+), 48 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index e07a7588da..ffd6aa4dcb 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -623,6 +623,18 @@ public static class Dungeons { @SerialEntry public int mapY = 2; + @SerialEntry + public boolean enableScore = true; + + @SerialEntry + public int scoreX = 29; + + @SerialEntry + public int scoreY = 134; + + @SerialEntry + public float scoreScaling = 1f; + @SerialEntry public boolean playerSecretsTracker = false; diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index 06133afc5e..8cd697e580 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -315,6 +315,13 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.locations.dungeons.enableMap = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.enableScore")) + .binding(defaults.locations.dungeons.enableScore, + () -> config.locations.dungeons.enableScore, + newValue -> config.locations.dungeons.enableScore = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) .option(ButtonOption.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mapScreen")) .text(Text.translatable("text.skyblocker.open")) @@ -327,6 +334,17 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.locations.dungeons.mapScaling = newValue) .controller(FloatFieldControllerBuilder::create) .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.scoreScaling")) + .binding(defaults.locations.dungeons.scoreScaling, + () -> config.locations.dungeons.scoreScaling, + newValue -> { + config.locations.dungeons.scoreX = config.locations.dungeons.scoreX + (int) ((config.locations.dungeons.scoreScaling - newValue) * 38.0); + config.locations.dungeons.scoreY = config.locations.dungeons.scoreY + (int) ((config.locations.dungeons.scoreScaling - newValue) * MinecraftClient.getInstance().textRenderer.fontHeight / 2.0); + config.locations.dungeons.scoreScaling = newValue; + }) + .controller(FloatFieldControllerBuilder::create) + .build()) .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.playerSecretsTracker")) .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.playerSecretsTracker.@Tooltip"))) diff --git a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java index 0ee7b528fb..396bf89354 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java @@ -5,9 +5,11 @@ import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.FancyStatusBars; +import de.hysky.skyblocker.skyblock.dungeon.DungeonMap; +import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; +import de.hysky.skyblocker.skyblock.dungeon.DungeonScoreHUD; import de.hysky.skyblocker.skyblock.item.HotbarSlotLock; import de.hysky.skyblocker.skyblock.item.ItemCooldowns; -import de.hysky.skyblocker.skyblock.dungeon.DungeonMap; import de.hysky.skyblocker.skyblock.item.ItemRarityBackgrounds; import de.hysky.skyblocker.utils.Utils; import net.fabricmc.api.EnvType; @@ -64,8 +66,10 @@ public abstract class InGameHudMixin { if (statusBars.render(context, scaledWidth, scaledHeight)) ci.cancel(); - if (Utils.isInDungeons() && SkyblockerConfigManager.get().locations.dungeons.enableMap) - DungeonMap.render(context.getMatrices()); + if (Utils.isInDungeons() && DungeonScore.isDungeonStarted()) { + if (SkyblockerConfigManager.get().locations.dungeons.enableMap) DungeonMap.render(context.getMatrices()); + if (SkyblockerConfigManager.get().locations.dungeons.enableScore) DungeonScoreHUD.render(context); + } } @Inject(method = "renderMountHealth", at = @At("HEAD"), cancellable = true) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java index e1af85ea5b..293d301f91 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java @@ -5,7 +5,6 @@ import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; import net.minecraft.client.render.MapRenderer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.util.math.MatrixStack; @@ -13,12 +12,9 @@ import net.minecraft.item.ItemStack; import net.minecraft.item.map.MapState; import net.minecraft.nbt.NbtCompound; -import net.minecraft.util.Identifier; import org.apache.commons.lang3.StringUtils; public class DungeonMap { - private static final Identifier MAP_BACKGROUND = new Identifier("textures/map/map_background.png"); - public static void render(MatrixStack matrices) { MinecraftClient client = MinecraftClient.getInstance(); if (client.player == null || client.world == null) return; @@ -46,13 +42,7 @@ public static void render(MatrixStack matrices) { } } - public static void renderHUDMap(DrawContext context, int x, int y) { - float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling; - int size = (int) (128 * scaling); - context.drawTexture(MAP_BACKGROUND, x, y, 0, 0, size, size, size, size); - } - - public static void init() { + public static void init() { //Todo: consider renaming the command to a more general name since it'll also have dungeon score and maybe other stuff in the future ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker") .then(ClientCommandManager.literal("hud") .then(ClientCommandManager.literal("dungeonmap") diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java index 02b0825415..00a956e115 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java @@ -5,13 +5,17 @@ import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.Screen; import net.minecraft.text.Text; +import net.minecraft.util.Identifier; import java.awt.*; public class DungeonMapConfigScreen extends Screen { - private int hudX = SkyblockerConfigManager.get().locations.dungeons.mapX; - private int hudY = SkyblockerConfigManager.get().locations.dungeons.mapY; + private int mapX = SkyblockerConfigManager.get().locations.dungeons.mapX; + private int mapY = SkyblockerConfigManager.get().locations.dungeons.mapY; + private int scoreX = SkyblockerConfigManager.get().locations.dungeons.scoreX; + private int scoreY = SkyblockerConfigManager.get().locations.dungeons.scoreY; + private static final Identifier MAP_BACKGROUND = new Identifier("textures/map/map_background.png"); private final Screen parent; protected DungeonMapConfigScreen() { @@ -27,17 +31,23 @@ public DungeonMapConfigScreen(Screen parent) { public void render(DrawContext context, int mouseX, int mouseY, float delta) { super.render(context, mouseX, mouseY, delta); renderBackground(context, mouseX, mouseY, delta); - DungeonMap.renderHUDMap(context, hudX, hudY); + renderHUDMap(context, mapX, mapY); + renderHUDScore(context, scoreX, scoreY); context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width >> 1, height >> 1, Color.GRAY.getRGB()); } @Override public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { - float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling; - int size = (int) (128 * scaling); - if (RenderHelper.pointIsInArea(mouseX, mouseY, hudX, hudY, hudX + size, hudY + size) && button == 0) { - hudX = (int) Math.max(Math.min(mouseX - (size >> 1), this.width - size), 0); - hudY = (int) Math.max(Math.min(mouseY - (size >> 1), this.height - size), 0); + int mapSize = (int) (128 * SkyblockerConfigManager.get().locations.dungeons.mapScaling); + float scoreScaling = SkyblockerConfigManager.get().locations.dungeons.scoreScaling; + int scoreWidth = (int) (textRenderer.getWidth("Score: 300 (S+)") * scoreScaling); + int scoreHeight = (int) (textRenderer.fontHeight * scoreScaling); + if (RenderHelper.pointIsInArea(mouseX, mouseY, mapX, mapY, mapX + mapSize, mapY + mapSize) && button == 0) { + mapX = (int) Math.max(Math.min(mouseX - (mapSize >> 1), this.width - mapSize), 0); + mapY = (int) Math.max(Math.min(mouseY - (mapSize >> 1), this.height - mapSize), 0); + } else if (RenderHelper.pointIsInArea(mouseX, mouseY, scoreX, scoreY, scoreX + scoreWidth, scoreY + scoreHeight) && button == 0) { + scoreX = (int) Math.max(Math.min(mouseX - (scoreWidth >> 1), this.width - scoreWidth), 0); + scoreY = (int) Math.max(Math.min(mouseY - (scoreHeight >> 1), this.height - scoreHeight), 0); } return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); } @@ -45,8 +55,10 @@ public boolean mouseDragged(double mouseX, double mouseY, int button, double del @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { if (button == 1) { - hudX = 2; - hudY = 2; + mapX = 2; + mapY = 2; + scoreX = Math.max((int) ((mapX + (64 * SkyblockerConfigManager.get().locations.dungeons.mapScaling)) - textRenderer.getWidth("Score: 300 (S+)") * SkyblockerConfigManager.get().locations.dungeons.scoreScaling / 2), 0); + scoreY = (int) (mapY + (128 * SkyblockerConfigManager.get().locations.dungeons.mapScaling) + 4); } return super.mouseClicked(mouseX, mouseY, button); @@ -54,10 +66,22 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { @Override public void close() { - SkyblockerConfigManager.get().locations.dungeons.mapX = hudX; - SkyblockerConfigManager.get().locations.dungeons.mapY = hudY; + SkyblockerConfigManager.get().locations.dungeons.mapX = mapX; + SkyblockerConfigManager.get().locations.dungeons.mapY = mapY; + SkyblockerConfigManager.get().locations.dungeons.scoreX = scoreX; + SkyblockerConfigManager.get().locations.dungeons.scoreY = scoreY; SkyblockerConfigManager.save(); this.client.setScreen(parent); } + + public void renderHUDMap(DrawContext context, int x, int y) { + float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling; + int size = (int) (128 * scaling); + context.drawTexture(MAP_BACKGROUND, x, y, 0, 0, size, size, size, size); + } + + public void renderHUDScore(DrawContext context, int x, int y) { + DungeonScoreHUD.render(context, x, y); + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java index 9da1242678..18038ccd3b 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java @@ -1,38 +1,37 @@ package de.hysky.skyblocker.skyblock.dungeon; import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Text; import net.minecraft.util.Formatting; public class DungeonScoreHUD { - - public static void init() { - HudRenderCallback.EVENT.register(DungeonScoreHUD::onHudRender); + private DungeonScoreHUD() { } - private static void onHudRender(DrawContext context, float tickDelta) { - if (!Utils.isInDungeons() || !DungeonScore.isDungeonStarted()) return; + public static void render(DrawContext context) { + int x = SkyblockerConfigManager.get().locations.dungeons.scoreX; + int y = SkyblockerConfigManager.get().locations.dungeons.scoreY; + render(context, x, y); + } - int x = SkyblockerConfigManager.get().locations.dungeons.mapX; - int y = SkyblockerConfigManager.get().locations.dungeons.mapY; - int size = (int) (128 * SkyblockerConfigManager.get().locations.dungeons.mapScaling); - context.drawCenteredTextWithShadow(MinecraftClient.getInstance().textRenderer, - Text.literal("Score: ").append(formatScore(DungeonScore.getScore())), - x + (size >> 1), - y + size + 5, - 0x00FFFFFF); + public static void render(DrawContext context, int x, int y) { + float scale = SkyblockerConfigManager.get().locations.dungeons.scoreScaling; + MatrixStack matrixStack = context.getMatrices(); + matrixStack.push(); + matrixStack.scale(scale, scale, 0); + context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, Text.literal("Score: ").append(formatScore(DungeonScore.getScore())), (int) (x / scale), (int) (y / scale), 0xFFFFFFFF); + matrixStack.pop(); } private static Text formatScore(int score) { - if (score < 100) return Text.literal(String.valueOf(score)).withColor(0xDC1A1A).append(Text.literal(" (D)").formatted(Formatting.GRAY)); - if (score < 160) return Text.literal(String.valueOf(score)).withColor(0x4141FF).append(Text.literal(" (C)").formatted(Formatting.GRAY)); - if (score < 230) return Text.literal(String.valueOf(score)).withColor(0x7FCC19).append(Text.literal(" (B)").formatted(Formatting.GRAY)); - if (score < 270) return Text.literal(String.valueOf(score)).withColor(0x7F3FB2).append(Text.literal(" (A)").formatted(Formatting.GRAY)); - if (score < 300) return Text.literal(String.valueOf(score)).withColor(0xF1E252).append(Text.literal(" (S)").formatted(Formatting.GRAY)); - return Text.literal(String.valueOf(score)).withColor(0xF1E252).formatted(Formatting.BOLD).append(Text.literal(" (S+)").formatted(Formatting.GRAY)); + if (score < 100) return Text.literal(String.format("%03d", score)).withColor(0xDC1A1A).append(Text.literal(" (D) ").formatted(Formatting.GRAY)); + if (score < 160) return Text.literal(String.format("%03d", score)).withColor(0x4141FF).append(Text.literal(" (C) ").formatted(Formatting.GRAY)); + if (score < 230) return Text.literal(String.format("%03d", score)).withColor(0x7FCC19).append(Text.literal(" (B) ").formatted(Formatting.GRAY)); + if (score < 270) return Text.literal(String.format("%03d", score)).withColor(0x7F3FB2).append(Text.literal(" (A) ").formatted(Formatting.GRAY)); + if (score < 300) return Text.literal(String.format("%03d", score)).withColor(0xF1E252).append(Text.literal(" (S) ").formatted(Formatting.GRAY)); + return Text.literal(String.format("%03d", score)).withColor(0xF1E252).formatted(Formatting.BOLD).append(Text.literal(" (S+)").formatted(Formatting.GRAY)); } } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index cd1d9673be..e66fbfcd3a 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -215,8 +215,10 @@ "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper": "Croesus Helper", "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "Gray out chests that have already been opened.", "text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "Enable Map", - "text.autoconfig.skyblocker.option.locations.dungeons.mapScreen": "Dungeon Map Placement Config...", + "text.autoconfig.skyblocker.option.locations.dungeons.enableScore": "Enable Score HUD", + "text.autoconfig.skyblocker.option.locations.dungeons.mapScreen": "Dungeon Map & Score Placement Config...", "text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "Map Scaling", + "text.autoconfig.skyblocker.option.locations.dungeons.scoreScaling": "Score Scaling", "text.autoconfig.skyblocker.option.locations.dungeons.playerSecretsTracker": "Player Secrets Tracker", "text.autoconfig.skyblocker.option.locations.dungeons.playerSecretsTracker.@Tooltip": "Tracks the amount of secrets people in your dungeon run are doing.", "text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow": "Starred Mob Glow", From 1382f6ce7f4dc9aade35f7d877e6b9ba6832ce4a Mon Sep 17 00:00:00 2001 From: Emirlol <81419447+Emirlol@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:52:31 +0300 Subject: [PATCH 06/27] Cleaned up some sections and added comments on some critical points --- .../skyblock/dungeon/DungeonScore.java | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index 0a9aec5025..44c19ff5ab 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -50,7 +50,7 @@ public class DungeonScore { //Chat patterns private static final Pattern DEATHS_PATTERN = Pattern.compile(".*?\u2620 (?\\S+) .*"); //Other patterns - private static final Pattern MIMIC_FLOOR_FILTER_PATTERN = Pattern.compile("[EFM][12345]?"); + private static final Pattern MIMICLESS_FLOORS_PATTERN = Pattern.compile("[EFM][12345]?"); private static String currentFloor; private static boolean sent270; @@ -91,7 +91,7 @@ public static void tick() { score = calculateScore(); if (!sent270 && score >= 270 && score < 300) { if (SCORE_CONFIG.enableDungeonScore270Message) { - MessageScheduler.INSTANCE.sendMessageAfterCooldown(SCORE_CONFIG.dungeonScore270Message.replaceAll("\\[score]", "270")); + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/pc " + Constants.PREFIX.get().getString() + SCORE_CONFIG.dungeonScore270Message.replaceAll("\\[score]", "270")); } if (SCORE_CONFIG.enableDungeonScore270Title) { client.inGameHud.setDefaultTitleFade(); @@ -104,7 +104,7 @@ public static void tick() { } if (!sent300 && score >= 300) { if (SCORE_CONFIG.enableDungeonScore300Message) { - MessageScheduler.INSTANCE.sendMessageAfterCooldown(SCORE_CONFIG.dungeonScore300Message.replaceAll("\\[score]", "300")); + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/pc " + Constants.PREFIX.get().getString() + SCORE_CONFIG.dungeonScore300Message.replaceAll("\\[score]", "300")); } if (SCORE_CONFIG.enableDungeonScore300Title) { client.inGameHud.setDefaultTitleFade(); @@ -141,14 +141,8 @@ private static void onDungeonStart() { } private static int calculateScore() { - int timeScore = calculateTimeScore(); - int exploreScore = calculateExploreScore(); - int skillScore = calculateSkillScore(); - int bonusScore = calculateBonusScore(); - int totalScore = timeScore + exploreScore + skillScore + bonusScore; - if (currentFloor.equals("E")) totalScore = (int) (totalScore * 0.7); - //Will be this way until ready for pr, so it's easy to debug. - LOGGER.info("Total Score: {} (Time: {}, Explore: {}, Skill: {}, Bonus: {})", totalScore, timeScore, exploreScore, skillScore, bonusScore); + int totalScore = calculateTimeScore() + calculateExploreScore() + calculateSkillScore() + calculateBonusScore(); + if (currentFloor.equals("E")) return (int) (totalScore * 0.7); return totalScore; } @@ -181,7 +175,7 @@ private static int calculateBonusScore() { int paulScore = isMayorPaul ? 10 : 0; int cryptsScore = Math.min(getCrypts(), 5); int mimicScore = isMimicKilled ? 2 : 0; - if (getSecretsPercentage() >= 100 && !MIMIC_FLOOR_FILTER_PATTERN.matcher(currentFloor).matches()) mimicScore = 2; //If mimic kill is not announced but all secrets are found, mimic must've been killed + if (getSecretsPercentage() >= 100 && !MIMICLESS_FLOORS_PATTERN.matcher(currentFloor).matches()) mimicScore = 2; //If mimic kill is not announced but all secrets are found, mimic must've been killed return paulScore + cryptsScore + mimicScore; } @@ -191,7 +185,7 @@ private static boolean checkIfDungeonStarted() { public static boolean isEntityMimic(Entity entity) { if (!Utils.isInDungeons()) return false; - if (MIMIC_FLOOR_FILTER_PATTERN.matcher(currentFloor).matches()) return false; + if (MIMICLESS_FLOORS_PATTERN.matcher(currentFloor).matches()) return false; if (entity == null) return false; if (!(entity instanceof ZombieEntity zombie)) return false; if (!zombie.isBaby()) return false; @@ -219,9 +213,9 @@ public static void setMimicKilled(boolean state) { //This is not very accurate at the beginning of the dungeon since clear percentage is rounded to the closest integer, so at lower percentages its effect on the result is quite high. //For example: If clear percentage is 7% with a single room completed, it can be rounded from 6.5 or 7.49. In that range, the actual total room count can be either 14 or 15 while our result is 14. + //Score might fluctuate at first if the total room amount calculated changes as it gets more accurate with each room completed. private static int getTotalRooms() { - int completedRooms = getCompletedRooms(); - return (int) Math.round(completedRooms / getClearPercentage()); + return (int) Math.round(getCompletedRooms() / getClearPercentage()); } private static int getCompletedRooms() { @@ -229,7 +223,9 @@ private static int getCompletedRooms() { return matcher != null ? Integer.parseInt(matcher.group("rooms")) : 0; } - private static int getExtraCompletedRooms() { //This is needed for calculating the score before going in the boss room & completing the blood room, so we have the result sooner + //This is needed for calculating the score before going in the boss room & completing the blood room, so we have the result sooner + //It might cause score to fluctuate when completing the blood room or entering the boss room as there's a slight delay between the room being completed (boolean set to true) and the scoreboard updating + private static int getExtraCompletedRooms() { if (!bloodRoomCompleted) return 2; if (!DungeonManager.isInBoss()) return 1; return 0; @@ -241,10 +237,11 @@ private static double getClearPercentage() { if (!clearMatcher.matches()) continue; return Double.parseDouble(clearMatcher.group("cleared")) / 100.0; } - LOGGER.error("Clear pattern doesn't match"); + LOGGER.error("[Skyblocker] Clear pattern doesn't match!"); return 0; } + //Score might fluctuate when the first death has spirit pet as the boolean will be set to true after getting a response from the api, which might take a while private static int getDeathScorePenalty() { return deathCount * 2 - (firstDeathHasSpiritPet ? 1 : 0); } @@ -254,7 +251,6 @@ private static int getPuzzleCount() { return matcher != null ? Integer.parseInt(matcher.group("count")) : 0; } - //Possible states: ✖, ✦, ✔ private static int getPuzzlePenalty() { int incompletePuzzles = 0; for (int index = 0; index < puzzleCount; index++) { @@ -302,7 +298,6 @@ public static boolean hasSpiritPet(String name) { return true; } } catch (Exception e) { - e.printStackTrace(); LOGGER.error("[Skyblocker] Spirit pet lookup by name failed! Name: {} - Cause: {}", name, e.getMessage()); } return false; @@ -334,7 +329,7 @@ public static void setCurrentFloor() { currentFloor = floorMatcher.group("floor"); return; } - LOGGER.error("Floor pattern doesn't match"); + LOGGER.error("[Skyblocker] Floor pattern doesn't match!"); } public static int getScore() { From 8feae9fc42e894431bc16474c4476ee0f7e58fa4 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 18 Jan 2024 08:23:05 +0300 Subject: [PATCH 07/27] Wrapped initializeMayorCache in a CompletableFuture --- .../java/de/hysky/skyblocker/utils/Utils.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 3336a0a714..31157bfc7c 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.concurrent.CompletableFuture; /** * Utility variables and methods for retrieving Skyblock related information. @@ -396,15 +397,18 @@ private static void resetLocRawInfo() { private static void initializeMayorCache() { if (!mayor.isEmpty()) return; - try { - JsonObject json = JsonParser.parseString(Http.sendGetRequest("https://api.hypixel.net/v2/resources/skyblock/election")).getAsJsonObject(); - if (json.get("success").getAsBoolean()) { - mayor = json.get("mayor").getAsJsonObject().get("name").getAsString(); - } else { - throw new IOException("API call for mayor failed: " + json.get("cause").getAsString()); + CompletableFuture.supplyAsync(() -> { + try { + JsonObject json = JsonParser.parseString(Http.sendGetRequest("https://api.hypixel.net/v2/resources/skyblock/election")).getAsJsonObject(); + if (json.get("success").getAsBoolean()) return json.get("mayor").getAsJsonObject().get("name").getAsString(); + throw new IOException("API call for mayor status failed: " + json.get("cause").getAsString()); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); } - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - } + return ""; + }).thenAccept(s -> { + if (!s.isEmpty()) mayor = s; + }); + } } From 9a86e3fa1ff130285a998f089939da24c3b5b7b9 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 18 Jan 2024 08:39:14 +0300 Subject: [PATCH 08/27] Refactored score hud configs from Dungeons to DungeonScore --- .../skyblocker/config/SkyblockerConfig.java | 24 +++++++++---------- .../config/categories/DungeonsCategory.java | 16 ++++++------- .../skyblocker/mixin/InGameHudMixin.java | 2 +- .../dungeon/DungeonMapConfigScreen.java | 12 +++++----- .../skyblock/dungeon/DungeonScoreHUD.java | 6 ++--- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index ffd6aa4dcb..e47b008de4 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -623,18 +623,6 @@ public static class Dungeons { @SerialEntry public int mapY = 2; - @SerialEntry - public boolean enableScore = true; - - @SerialEntry - public int scoreX = 29; - - @SerialEntry - public int scoreY = 134; - - @SerialEntry - public float scoreScaling = 1f; - @SerialEntry public boolean playerSecretsTracker = false; @@ -772,6 +760,18 @@ public static class DungeonScore { @SerialEntry public String dungeonScore300Message = "300 Score Reached!"; + + @SerialEntry + public boolean enableScoreHUD = true; + + @SerialEntry + public int scoreX = 29; + + @SerialEntry + public int scoreY = 134; + + @SerialEntry + public float scoreScaling = 1f; } public static class DungeonChestProfit { diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index 8cd697e580..8546382f53 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -317,9 +317,9 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .build()) .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.enableScore")) - .binding(defaults.locations.dungeons.enableScore, - () -> config.locations.dungeons.enableScore, - newValue -> config.locations.dungeons.enableScore = newValue) + .binding(defaults.locations.dungeons.dungeonScore.enableScoreHUD, + () -> config.locations.dungeons.dungeonScore.enableScoreHUD, + newValue -> config.locations.dungeons.dungeonScore.enableScoreHUD = newValue) .controller(ConfigUtils::createBooleanController) .build()) .option(ButtonOption.createBuilder() @@ -336,12 +336,12 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .build()) .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.scoreScaling")) - .binding(defaults.locations.dungeons.scoreScaling, - () -> config.locations.dungeons.scoreScaling, + .binding(defaults.locations.dungeons.dungeonScore.scoreScaling, + () -> config.locations.dungeons.dungeonScore.scoreScaling, newValue -> { - config.locations.dungeons.scoreX = config.locations.dungeons.scoreX + (int) ((config.locations.dungeons.scoreScaling - newValue) * 38.0); - config.locations.dungeons.scoreY = config.locations.dungeons.scoreY + (int) ((config.locations.dungeons.scoreScaling - newValue) * MinecraftClient.getInstance().textRenderer.fontHeight / 2.0); - config.locations.dungeons.scoreScaling = newValue; + config.locations.dungeons.dungeonScore.scoreX = config.locations.dungeons.dungeonScore.scoreX + (int) ((config.locations.dungeons.dungeonScore.scoreScaling - newValue) * 38.0); + config.locations.dungeons.dungeonScore.scoreY = config.locations.dungeons.dungeonScore.scoreY + (int) ((config.locations.dungeons.dungeonScore.scoreScaling - newValue) * MinecraftClient.getInstance().textRenderer.fontHeight / 2.0); + config.locations.dungeons.dungeonScore.scoreScaling = newValue; }) .controller(FloatFieldControllerBuilder::create) .build()) diff --git a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java index 396bf89354..df7cbdea2e 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java @@ -68,7 +68,7 @@ public abstract class InGameHudMixin { if (Utils.isInDungeons() && DungeonScore.isDungeonStarted()) { if (SkyblockerConfigManager.get().locations.dungeons.enableMap) DungeonMap.render(context.getMatrices()); - if (SkyblockerConfigManager.get().locations.dungeons.enableScore) DungeonScoreHUD.render(context); + if (SkyblockerConfigManager.get().locations.dungeons.dungeonScore.enableScoreHUD) DungeonScoreHUD.render(context); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java index 00a956e115..b832f12dec 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java @@ -13,8 +13,8 @@ public class DungeonMapConfigScreen extends Screen { private int mapX = SkyblockerConfigManager.get().locations.dungeons.mapX; private int mapY = SkyblockerConfigManager.get().locations.dungeons.mapY; - private int scoreX = SkyblockerConfigManager.get().locations.dungeons.scoreX; - private int scoreY = SkyblockerConfigManager.get().locations.dungeons.scoreY; + private int scoreX = SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreX; + private int scoreY = SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreY; private static final Identifier MAP_BACKGROUND = new Identifier("textures/map/map_background.png"); private final Screen parent; @@ -39,7 +39,7 @@ public void render(DrawContext context, int mouseX, int mouseY, float delta) { @Override public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { int mapSize = (int) (128 * SkyblockerConfigManager.get().locations.dungeons.mapScaling); - float scoreScaling = SkyblockerConfigManager.get().locations.dungeons.scoreScaling; + float scoreScaling = SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreScaling; int scoreWidth = (int) (textRenderer.getWidth("Score: 300 (S+)") * scoreScaling); int scoreHeight = (int) (textRenderer.fontHeight * scoreScaling); if (RenderHelper.pointIsInArea(mouseX, mouseY, mapX, mapY, mapX + mapSize, mapY + mapSize) && button == 0) { @@ -57,7 +57,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { if (button == 1) { mapX = 2; mapY = 2; - scoreX = Math.max((int) ((mapX + (64 * SkyblockerConfigManager.get().locations.dungeons.mapScaling)) - textRenderer.getWidth("Score: 300 (S+)") * SkyblockerConfigManager.get().locations.dungeons.scoreScaling / 2), 0); + scoreX = Math.max((int) ((mapX + (64 * SkyblockerConfigManager.get().locations.dungeons.mapScaling)) - textRenderer.getWidth("Score: 300 (S+)") * SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreScaling / 2), 0); scoreY = (int) (mapY + (128 * SkyblockerConfigManager.get().locations.dungeons.mapScaling) + 4); } @@ -68,8 +68,8 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { public void close() { SkyblockerConfigManager.get().locations.dungeons.mapX = mapX; SkyblockerConfigManager.get().locations.dungeons.mapY = mapY; - SkyblockerConfigManager.get().locations.dungeons.scoreX = scoreX; - SkyblockerConfigManager.get().locations.dungeons.scoreY = scoreY; + SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreX = scoreX; + SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreY = scoreY; SkyblockerConfigManager.save(); this.client.setScreen(parent); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java index 18038ccd3b..eedbf02076 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java @@ -12,13 +12,13 @@ private DungeonScoreHUD() { } public static void render(DrawContext context) { - int x = SkyblockerConfigManager.get().locations.dungeons.scoreX; - int y = SkyblockerConfigManager.get().locations.dungeons.scoreY; + int x = SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreX; + int y = SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreY; render(context, x, y); } public static void render(DrawContext context, int x, int y) { - float scale = SkyblockerConfigManager.get().locations.dungeons.scoreScaling; + float scale = SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreScaling; MatrixStack matrixStack = context.getMatrices(); matrixStack.push(); matrixStack.scale(scale, scale, 0); From b52f1e67c417266599d23dff22c7c2febfad95ed Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 18 Jan 2024 10:07:00 +0300 Subject: [PATCH 09/27] Prevent sending 270 message if 300 message is already sent This can happen if the player leaves the dungeon and rejoins while score is above 300, then fall below 300 from player deaths etc. --- .../java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index 44c19ff5ab..2094293582 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -89,7 +89,7 @@ public static void tick() { return; } score = calculateScore(); - if (!sent270 && score >= 270 && score < 300) { + if (!sent270 && !sent300 && score >= 270 && score < 300) { if (SCORE_CONFIG.enableDungeonScore270Message) { MessageScheduler.INSTANCE.sendMessageAfterCooldown("/pc " + Constants.PREFIX.get().getString() + SCORE_CONFIG.dungeonScore270Message.replaceAll("\\[score]", "270")); } From d9c69b47127c6aae71333831654402f1924924c8 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:24:56 +0300 Subject: [PATCH 10/27] Reduce mimic floor pattern matching to once per run and refactor isMimicKilled boolean --- .../skyblock/dungeon/DungeonScore.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index 2094293582..b27ca660b5 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -50,12 +50,13 @@ public class DungeonScore { //Chat patterns private static final Pattern DEATHS_PATTERN = Pattern.compile(".*?\u2620 (?\\S+) .*"); //Other patterns - private static final Pattern MIMICLESS_FLOORS_PATTERN = Pattern.compile("[EFM][12345]?"); + private static final Pattern MIMIC_FLOORS_PATTERN = Pattern.compile("[FM][67]]"); private static String currentFloor; + private static boolean floorHasMimics; private static boolean sent270; private static boolean sent300; - private static boolean isMimicKilled; + private static boolean mimicKilled; private static boolean dungeonStarted; private static boolean isMayorPaul; private static boolean firstDeathHasSpiritPet; @@ -119,9 +120,10 @@ public static void tick() { private static void reset() { currentFloor = ""; + floorHasMimics = false; sent270 = false; sent300 = false; - isMimicKilled = false; + mimicKilled = false; dungeonStarted = false; isMayorPaul = false; firstDeathHasSpiritPet = false; @@ -138,6 +140,7 @@ private static void onDungeonStart() { puzzleCount = getPuzzleCount(); isMayorPaul = Utils.getMayor().equals("Paul"); startingTime = System.currentTimeMillis(); + floorHasMimics = MIMIC_FLOORS_PATTERN.matcher(currentFloor).matches(); } private static int calculateScore() { @@ -174,8 +177,8 @@ private static int calculateTimeScore() { private static int calculateBonusScore() { int paulScore = isMayorPaul ? 10 : 0; int cryptsScore = Math.min(getCrypts(), 5); - int mimicScore = isMimicKilled ? 2 : 0; - if (getSecretsPercentage() >= 100 && !MIMICLESS_FLOORS_PATTERN.matcher(currentFloor).matches()) mimicScore = 2; //If mimic kill is not announced but all secrets are found, mimic must've been killed + int mimicScore = mimicKilled ? 2 : 0; + if (getSecretsPercentage() >= 100 && floorHasMimics) mimicScore = 2; //If mimic kill is not announced but all secrets are found, mimic must've been killed return paulScore + cryptsScore + mimicScore; } @@ -185,7 +188,7 @@ private static boolean checkIfDungeonStarted() { public static boolean isEntityMimic(Entity entity) { if (!Utils.isInDungeons()) return false; - if (MIMICLESS_FLOORS_PATTERN.matcher(currentFloor).matches()) return false; + if (!floorHasMimics) return false; if (entity == null) return false; if (!(entity instanceof ZombieEntity zombie)) return false; if (!zombie.isBaby()) return false; @@ -201,14 +204,14 @@ public static boolean isEntityMimic(Entity entity) { } public static void handleEntityDeath(Entity entity) { - if (isMimicKilled) return; + if (mimicKilled) return; if (!isEntityMimic(entity)) return; if (MIMIC_MESSAGES_CONFIG.sendMimicMessages) MessageScheduler.INSTANCE.sendMessageAfterCooldown(MIMIC_MESSAGES_CONFIG.mimicMessage); - isMimicKilled = true; + mimicKilled = true; } public static void setMimicKilled(boolean state) { - isMimicKilled = state; + mimicKilled = state; } //This is not very accurate at the beginning of the dungeon since clear percentage is rounded to the closest integer, so at lower percentages its effect on the result is quite high. From 2bfe8c5d5d9c39f270984e1f2d511c67cdc917f9 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:28:04 +0300 Subject: [PATCH 11/27] Check chat messages for dungeon start instead of scoreboard --- .../skyblock/dungeon/DungeonScore.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index b27ca660b5..98d3d79b7d 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -39,7 +39,6 @@ public class DungeonScore { private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Dungeon Score"); //Scoreboard patterns private static final Pattern CLEARED_PATTERN = Pattern.compile("Cleared: (?\\d+)%.*"); - private static final Pattern DUNGEON_START_PATTERN = Pattern.compile("(?:Auto-closing|Starting) in: \\d:\\d+"); private static final Pattern FLOOR_PATTERN = Pattern.compile(".*?(?=T)The Catacombs \\((?[EFM]\\D*\\d*)\\)"); //Playerlist patterns private static final Pattern SECRETS_PATTERN = Pattern.compile("Secrets Found: (?\\d+\\.?\\d*)%"); @@ -72,10 +71,14 @@ public static void init() { SkyblockEvents.LEAVE.register(SpiritPetCache::clear); ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset()); ClientReceiveMessageEvents.GAME.register((message, overlay) -> { - if (!Utils.isInDungeons() || !dungeonStarted) return; + if (overlay || !Utils.isInDungeons()) return; String str = message.getString(); - checkMessageForDeaths(str); - checkMessageForWatcher(str); + if (!dungeonStarted) { + checkMessageForMort(str); + } else { + checkMessageForDeaths(str); + checkMessageForWatcher(str); + } }); } @@ -85,10 +88,8 @@ public static void tick() { reset(); return; } - if (!dungeonStarted) { - if (checkIfDungeonStarted()) onDungeonStart(); - return; - } + if (!dungeonStarted) return; + score = calculateScore(); if (!sent270 && !sent300 && score >= 270 && score < 300) { if (SCORE_CONFIG.enableDungeonScore270Message) { @@ -182,10 +183,6 @@ private static int calculateBonusScore() { return paulScore + cryptsScore + mimicScore; } - private static boolean checkIfDungeonStarted() { - return Utils.STRING_SCOREBOARD.stream().noneMatch(s -> DUNGEON_START_PATTERN.matcher(s).matches()); - } - public static boolean isEntityMimic(Entity entity) { if (!Utils.isInDungeons()) return false; if (!floorHasMimics) return false; @@ -325,6 +322,11 @@ private static void checkMessageForWatcher(String message) { if (message.equals("[BOSS] The Watcher: You have proven yourself. You may pass.")) bloodRoomCompleted = true; } + private static void checkMessageForMort(String message) { + if (!message.equals("§e[NPC] §bMort§f: You should find it useful if you get lost.")) return; + onDungeonStart(); + } + public static void setCurrentFloor() { for (String sidebarLine : Utils.STRING_SCOREBOARD) { Matcher floorMatcher = FLOOR_PATTERN.matcher(sidebarLine); From a074292c9ce56daa060c02f8abaf78857998fffd Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:31:25 +0300 Subject: [PATCH 12/27] Reduce Enum#valueOf calls and prevent infinite or negative score and division by 0 --- .../skyblock/dungeon/DungeonScore.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index 98d3d79b7d..6059f9d5d1 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -51,6 +51,7 @@ public class DungeonScore { //Other patterns private static final Pattern MIMIC_FLOORS_PATTERN = Pattern.compile("[FM][67]]"); + private static FloorRequirement floorRequirement; private static String currentFloor; private static boolean floorHasMimics; private static boolean sent270; @@ -120,6 +121,7 @@ public static void tick() { } private static void reset() { + floorRequirement = null; currentFloor = ""; floorHasMimics = false; sent270 = false; @@ -141,6 +143,7 @@ private static void onDungeonStart() { puzzleCount = getPuzzleCount(); isMayorPaul = Utils.getMayor().equals("Paul"); startingTime = System.currentTimeMillis(); + floorRequirement = FloorRequirement.valueOf(currentFloor); floorHasMimics = MIMIC_FLOORS_PATTERN.matcher(currentFloor).matches(); } @@ -151,28 +154,28 @@ private static int calculateScore() { } private static int calculateSkillScore() { - return 20 + (int) Math.floor(80.0 * (getCompletedRooms() + getExtraCompletedRooms()) / getTotalRooms()) - getPuzzlePenalty() - getDeathScorePenalty(); + int totalRooms = getTotalRooms(); //This is necessary to avoid division by 0 at the start of dungeons, which results in infinite score + return 20 + Math.max((totalRooms != 0 ? (int) (80.0 * (getCompletedRooms() + getExtraCompletedRooms()) / totalRooms) : 0) - getPuzzlePenalty() - getDeathScorePenalty(), 0); //Can't go below 20 skill score } private static int calculateExploreScore() { - int completedRoomScore = (int) Math.floor(60.0 * (getCompletedRooms() + getExtraCompletedRooms()) / getTotalRooms()); - int percentageRequirement = FloorRequirement.valueOf(currentFloor).percentage; - int secretsScore = (int) Math.floor(40 * Math.min(percentageRequirement, getSecretsPercentage()) / percentageRequirement); - return completedRoomScore + secretsScore; + int totalRooms = getTotalRooms(); //This is necessary to avoid division by 0 at the start of dungeons, which results in infinite score + int completedRoomScore = totalRooms != 0 ? (int) (60.0 * (getCompletedRooms() + getExtraCompletedRooms()) / totalRooms) : 0; + int secretsScore = (int) (40 * Math.min(floorRequirement.percentage, getSecretsPercentage()) / floorRequirement.percentage); + return Math.max(completedRoomScore + secretsScore, 0); } private static int calculateTimeScore() { int score = 100; int timeSpent = (int) (System.currentTimeMillis() - startingTime) / 1000; - int timeRequirement = FloorRequirement.valueOf(currentFloor).timeLimit; - if (timeSpent < timeRequirement) return score; + if (timeSpent < floorRequirement.timeLimit) return score; - double timePastRequirement = ((double) (timeSpent - timeRequirement) / timeRequirement) * 100; + double timePastRequirement = ((double) (timeSpent - floorRequirement.timeLimit) / floorRequirement.timeLimit) * 100; if (timePastRequirement < 20) return score - (int) timePastRequirement / 2; if (timePastRequirement < 40) return score - (int) (10 + (timePastRequirement - 20) / 4); if (timePastRequirement < 50) return score - (int) (15 + (timePastRequirement - 40) / 5); if (timePastRequirement < 60) return score - (int) (17 + (timePastRequirement - 50) / 6); - return score - (int) (18 + (2.0 / 3.0) + (timePastRequirement - 60) / 7); + return Math.max(score - (int) (18 + (2.0 / 3.0) + (timePastRequirement - 60) / 7), 0); //This can theoretically go down to -20 if the time limit is one of the lower ones like 480, but individual score categories can't go below 0 } private static int calculateBonusScore() { From b765b3f2fe69c1e10a3eafec75c6bd17201b9196 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:32:21 +0300 Subject: [PATCH 13/27] Fix entrance floor score calculation --- .../skyblocker/skyblock/dungeon/DungeonScore.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index 6059f9d5d1..fde255d7ad 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -53,6 +53,7 @@ public class DungeonScore { private static FloorRequirement floorRequirement; private static String currentFloor; + private static boolean isCurrentFloorEntrance; private static boolean floorHasMimics; private static boolean sent270; private static boolean sent300; @@ -123,6 +124,7 @@ public static void tick() { private static void reset() { floorRequirement = null; currentFloor = ""; + isCurrentFloorEntrance = false; floorHasMimics = false; sent270 = false; sent300 = false; @@ -145,12 +147,12 @@ private static void onDungeonStart() { startingTime = System.currentTimeMillis(); floorRequirement = FloorRequirement.valueOf(currentFloor); floorHasMimics = MIMIC_FLOORS_PATTERN.matcher(currentFloor).matches(); + if (currentFloor.equals("E")) isCurrentFloorEntrance = true; } private static int calculateScore() { - int totalScore = calculateTimeScore() + calculateExploreScore() + calculateSkillScore() + calculateBonusScore(); - if (currentFloor.equals("E")) return (int) (totalScore * 0.7); - return totalScore; + if (isCurrentFloorEntrance) return Math.round(calculateTimeScore() * 0.7f) + Math.round(calculateExploreScore() * 0.7f) + Math.round(calculateSkillScore() * 0.7f) + Math.round(calculateBonusScore() * 0.7f); + return calculateTimeScore() + calculateExploreScore() + calculateSkillScore() + calculateBonusScore(); } private static int calculateSkillScore() { @@ -229,8 +231,8 @@ private static int getCompletedRooms() { //This is needed for calculating the score before going in the boss room & completing the blood room, so we have the result sooner //It might cause score to fluctuate when completing the blood room or entering the boss room as there's a slight delay between the room being completed (boolean set to true) and the scoreboard updating private static int getExtraCompletedRooms() { - if (!bloodRoomCompleted) return 2; - if (!DungeonManager.isInBoss()) return 1; + if (!bloodRoomCompleted) return isCurrentFloorEntrance ? 1 : 2; + if (!DungeonManager.isInBoss() && !isCurrentFloorEntrance) return 1; return 0; } From 4afcb477f933dc496827b4936f18685b2360c776 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 19 Jan 2024 08:58:19 +0300 Subject: [PATCH 14/27] Changed exception handling to use a logger in Utils and DungeonScore --- .../de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java | 6 ++---- src/main/java/de/hysky/skyblocker/utils/Utils.java | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index fde255d7ad..48a87c3668 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -197,10 +197,8 @@ public static boolean isEntityMimic(Entity entity) { try { DefaultedList armor = (DefaultedList) zombie.getArmorItems(); return armor.stream().allMatch(ItemStack::isEmpty); - } catch (NullPointerException e) { - return false; - } catch (ClassCastException f) { - f.printStackTrace(); + } catch (Exception f) { + LOGGER.error("[Skyblocker] Failed to check if entity is a mimic! Cause: {}", f.getMessage()); return false; } } diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 31157bfc7c..eaa07ff288 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -401,9 +401,9 @@ private static void initializeMayorCache() { try { JsonObject json = JsonParser.parseString(Http.sendGetRequest("https://api.hypixel.net/v2/resources/skyblock/election")).getAsJsonObject(); if (json.get("success").getAsBoolean()) return json.get("mayor").getAsJsonObject().get("name").getAsString(); - throw new IOException("API call for mayor status failed: " + json.get("cause").getAsString()); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); + throw new IOException(json.get("cause").getAsString()); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to get mayor status! Cause: {}", e.getMessage()); } return ""; }).thenAccept(s -> { From adce676848e3fb15cacfaeb8426f7f86bc5efaa4 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 19 Jan 2024 09:04:28 +0300 Subject: [PATCH 15/27] Match translation key to the path in the config --- .../hysky/skyblocker/config/categories/DungeonsCategory.java | 4 ++-- src/main/resources/assets/skyblocker/lang/en_us.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index 8546382f53..d939a4f0f1 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -316,7 +316,7 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .controller(ConfigUtils::createBooleanController) .build()) .option(Option.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.enableScore")) + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD")) .binding(defaults.locations.dungeons.dungeonScore.enableScoreHUD, () -> config.locations.dungeons.dungeonScore.enableScoreHUD, newValue -> config.locations.dungeons.dungeonScore.enableScoreHUD = newValue) @@ -335,7 +335,7 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .controller(FloatFieldControllerBuilder::create) .build()) .option(Option.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.scoreScaling")) + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.scoreScaling")) .binding(defaults.locations.dungeons.dungeonScore.scoreScaling, () -> config.locations.dungeons.dungeonScore.scoreScaling, newValue -> { diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index e66fbfcd3a..c151c77ba9 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -200,6 +200,8 @@ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableDungeonScoreSound.@Tooltip": "Plays a sound when reaching %d score in dungeons.", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.dungeonScoreMessage": "Dungeon Score %d Message", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.dungeonScoreMessage.@Tooltip": "Message which will be sent in the chat when reaching %d score in dungeons. The string \"[score]\" will be replaced with the dungeon score (%d).", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD": "Enable Score HUD", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.scoreScaling": "Score Scaling", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit": "Dungeon Chest Profit Calculator", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator": "Enable Profit Calculator", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator.@Tooltip": "Displays the profit of a dungeon chest in the chest screen's title.\nGreen if there's profit.\nRed if there isn't profit.\nGray if you don't gain or lose anything.\nBlue if calculations were based on incomplete data.", @@ -215,10 +217,8 @@ "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper": "Croesus Helper", "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "Gray out chests that have already been opened.", "text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "Enable Map", - "text.autoconfig.skyblocker.option.locations.dungeons.enableScore": "Enable Score HUD", "text.autoconfig.skyblocker.option.locations.dungeons.mapScreen": "Dungeon Map & Score Placement Config...", "text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "Map Scaling", - "text.autoconfig.skyblocker.option.locations.dungeons.scoreScaling": "Score Scaling", "text.autoconfig.skyblocker.option.locations.dungeons.playerSecretsTracker": "Player Secrets Tracker", "text.autoconfig.skyblocker.option.locations.dungeons.playerSecretsTracker.@Tooltip": "Tracks the amount of secrets people in your dungeon run are doing.", "text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow": "Starred Mob Glow", From 05518642cfdff4defe45a09c3fd52381c7b23c11 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:04:28 +0300 Subject: [PATCH 16/27] Change onEntityStatus mixin to use ModifyExpressionValue --- .../skyblocker/mixin/ClientPlayNetworkHandlerMixin.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java index b3fc871b16..e1eb6a2ddc 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java @@ -1,8 +1,7 @@ package de.hysky.skyblocker.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.WrapWithCondition; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import de.hysky.skyblocker.skyblock.FishingHelper; import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; @@ -19,7 +18,6 @@ import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; import net.minecraft.util.Identifier; -import net.minecraft.world.World; import org.slf4j.Logger; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -71,9 +69,8 @@ public abstract class ClientPlayNetworkHandlerMixin { MythologicalRitual.onParticle(packet); } - @WrapOperation(method = "onEntityStatus", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/EntityStatusS2CPacket;getEntity(Lnet/minecraft/world/World;)Lnet/minecraft/entity/Entity;")) - private Entity skyblocker$onEntityDeath(EntityStatusS2CPacket packet, World world, Operation original) { - Entity entity = original.call(packet, world); + @ModifyExpressionValue(method = "onEntityStatus", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/EntityStatusS2CPacket;getEntity(Lnet/minecraft/world/World;)Lnet/minecraft/entity/Entity;")) + private Entity skyblocker$onEntityDeath(Entity entity, @Local EntityStatusS2CPacket packet) { if (packet.getStatus() == EntityStatuses.PLAY_DEATH_SOUND_OR_ADD_PROJECTILE_HIT_PARTICLES) DungeonScore.handleEntityDeath(entity); return entity; } From c0358d05116de2a90eee72ea64177a336a2cfbf3 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:45:35 +0300 Subject: [PATCH 17/27] Changed Score text into a translatable and fixed score going off screen borders Cleaned up the formatScore method to make sure all scores and letter rankings are equally wide, thereby making the text not move as score changes or go off screen borders when the location is set on a narrower score and a wider score is to be displayed. --- .../dungeon/DungeonMapConfigScreen.java | 4 ++-- .../skyblock/dungeon/DungeonScoreHUD.java | 21 ++++++++++++------- .../assets/skyblocker/lang/en_us.json | 1 + 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java index b832f12dec..0f42c4956e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java @@ -40,7 +40,7 @@ public void render(DrawContext context, int mouseX, int mouseY, float delta) { public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { int mapSize = (int) (128 * SkyblockerConfigManager.get().locations.dungeons.mapScaling); float scoreScaling = SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreScaling; - int scoreWidth = (int) (textRenderer.getWidth("Score: 300 (S+)") * scoreScaling); + int scoreWidth = (int) (textRenderer.getWidth(DungeonScoreHUD.getFormattedScoreText()) * scoreScaling); int scoreHeight = (int) (textRenderer.fontHeight * scoreScaling); if (RenderHelper.pointIsInArea(mouseX, mouseY, mapX, mapY, mapX + mapSize, mapY + mapSize) && button == 0) { mapX = (int) Math.max(Math.min(mouseX - (mapSize >> 1), this.width - mapSize), 0); @@ -57,7 +57,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { if (button == 1) { mapX = 2; mapY = 2; - scoreX = Math.max((int) ((mapX + (64 * SkyblockerConfigManager.get().locations.dungeons.mapScaling)) - textRenderer.getWidth("Score: 300 (S+)") * SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreScaling / 2), 0); + scoreX = Math.max((int) ((mapX + (64 * SkyblockerConfigManager.get().locations.dungeons.mapScaling)) - textRenderer.getWidth(DungeonScoreHUD.getFormattedScoreText()) * SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreScaling / 2), 0); scoreY = (int) (mapY + (128 * SkyblockerConfigManager.get().locations.dungeons.mapScaling) + 4); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java index eedbf02076..1dfb1b9571 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScoreHUD.java @@ -11,6 +11,9 @@ public class DungeonScoreHUD { private DungeonScoreHUD() { } + //This is 4+5 wide, needed to offset the extra width from bold numbers (3×1 wide) in S+ and the "+" (6 wide) so that it doesn't go off the screen if the score is S+ and the hud element is at the right edge of the screen + private static final Text extraSpace = Text.literal(" ").append(Text.literal(" ").formatted(Formatting.BOLD)); + public static void render(DrawContext context) { int x = SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreX; int y = SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreY; @@ -22,16 +25,20 @@ public static void render(DrawContext context, int x, int y) { MatrixStack matrixStack = context.getMatrices(); matrixStack.push(); matrixStack.scale(scale, scale, 0); - context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, Text.literal("Score: ").append(formatScore(DungeonScore.getScore())), (int) (x / scale), (int) (y / scale), 0xFFFFFFFF); + context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, getFormattedScoreText(), (int) (x / scale), (int) (y / scale), 0xFFFFFFFF); matrixStack.pop(); } + public static Text getFormattedScoreText() { + return Text.translatable("skyblocker.dungeons.dungeonScore.scoreText", formatScore(DungeonScore.getScore())); + } + private static Text formatScore(int score) { - if (score < 100) return Text.literal(String.format("%03d", score)).withColor(0xDC1A1A).append(Text.literal(" (D) ").formatted(Formatting.GRAY)); - if (score < 160) return Text.literal(String.format("%03d", score)).withColor(0x4141FF).append(Text.literal(" (C) ").formatted(Formatting.GRAY)); - if (score < 230) return Text.literal(String.format("%03d", score)).withColor(0x7FCC19).append(Text.literal(" (B) ").formatted(Formatting.GRAY)); - if (score < 270) return Text.literal(String.format("%03d", score)).withColor(0x7F3FB2).append(Text.literal(" (A) ").formatted(Formatting.GRAY)); - if (score < 300) return Text.literal(String.format("%03d", score)).withColor(0xF1E252).append(Text.literal(" (S) ").formatted(Formatting.GRAY)); - return Text.literal(String.format("%03d", score)).withColor(0xF1E252).formatted(Formatting.BOLD).append(Text.literal(" (S+)").formatted(Formatting.GRAY)); + if (score < 100) return Text.literal(String.format("%03d", score)).withColor(0xDC1A1A).append(Text.literal(" (D)").formatted(Formatting.GRAY)).append(extraSpace); + if (score < 160) return Text.literal(String.format("%03d", score)).withColor(0x4141FF).append(Text.literal(" (C)").formatted(Formatting.GRAY)).append(extraSpace); + if (score < 230) return Text.literal(String.format("%03d", score)).withColor(0x7FCC19).append(Text.literal(" (B)").formatted(Formatting.GRAY)).append(extraSpace); + if (score < 270) return Text.literal(String.format("%03d", score)).withColor(0x7F3FB2).append(Text.literal(" (A)").formatted(Formatting.GRAY)).append(extraSpace); + if (score < 300) return Text.literal(String.format("%03d", score)).withColor(0xF1E252).append(Text.literal(" (S)").formatted(Formatting.GRAY)).append(extraSpace); + return Text.literal("").append(Text.literal(String.format("%03d", score)).withColor(0xF1E252).formatted(Formatting.BOLD)).append(Text.literal(" (S+)").formatted(Formatting.GRAY)); } } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index c151c77ba9..38b14170ea 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -340,6 +340,7 @@ "skyblocker.dungeons.secrets.customWaypointAdded": "§rAdded a custom waypoint at X: %d, Y: %d, Z: %d for room %s secret #%d of category %s with name '%s'.", "skyblocker.dungeons.secrets.customWaypointRemoved": "§rRemoved custom waypoint at X: %d, Y: %d, Z: %d for room %s secret #%d of category %s with name '%s'.", "skyblocker.dungeons.secrets.customWaypointNotFound": "§cNo custom waypoint found at X: %d, Y: %d, Z: %d for room %s.", + "skyblocker.dungeons.dungeonScore.scoreText": "Score: %s", "skyblocker.dungeons.secretsTracker.feedback": "%s§f found %s§f secrets. %s", "skyblocker.dungeons.secretsTracker.failFeedback": "§cUnable to calculate the amount of secrets everybody did this run!", From de4c4e1de62ec95cf4563f597a58c0ce02629042 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:57:00 +0300 Subject: [PATCH 18/27] Fix mimic floors pattern regex There was a lingering ] that broke the whole mimic thing, and I spent 2 hours trying to figure out why... --- .../java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index 48a87c3668..187ef43b1f 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -49,7 +49,7 @@ public class DungeonScore { //Chat patterns private static final Pattern DEATHS_PATTERN = Pattern.compile(".*?\u2620 (?\\S+) .*"); //Other patterns - private static final Pattern MIMIC_FLOORS_PATTERN = Pattern.compile("[FM][67]]"); + private static final Pattern MIMIC_FLOORS_PATTERN = Pattern.compile("[FM][67]"); private static FloorRequirement floorRequirement; private static String currentFloor; From cf55639859eda114efce3e91f762a534d0076b17 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 19 Jan 2024 18:16:38 +0300 Subject: [PATCH 19/27] Improve mimic filter regex and refactor some code --- .../skyblocker/skyblock/dungeon/DungeonScore.java | 11 ++++++----- .../skyblocker/skyblock/filters/MimicFilter.java | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index 187ef43b1f..4dfbf62a89 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -189,11 +189,7 @@ private static int calculateBonusScore() { } public static boolean isEntityMimic(Entity entity) { - if (!Utils.isInDungeons()) return false; - if (!floorHasMimics) return false; - if (entity == null) return false; - if (!(entity instanceof ZombieEntity zombie)) return false; - if (!zombie.isBaby()) return false; + if (!Utils.isInDungeons() || !floorHasMimics || !(entity instanceof ZombieEntity zombie) || !zombie.isBaby()) return false; try { DefaultedList armor = (DefaultedList) zombie.getArmorItems(); return armor.stream().allMatch(ItemStack::isEmpty); @@ -348,6 +344,11 @@ public static boolean isDungeonStarted() { return dungeonStarted; } + //Feel free to refactor this if you can think of a better name. + public static boolean isMimicOnCurrentFloor() { + return floorHasMimics; + } + enum FloorRequirement { E(30, 1200), F1(30, 600), diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java index 51482794f9..31f8318797 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java @@ -11,7 +11,7 @@ public class MimicFilter extends ChatPatternListener { public MimicFilter() { - super("(?:Mimic dead!?|Mimic Killed!|\\$SKYTILS-DUNGEON-SCORE-MIMIC\\$|\\Q" + SkyblockerConfigManager.get().locations.dungeons.mimicMessages.mimicMessage + "\\E)$"); + super(".*?(?:Mimic dead!?|Mimic Killed!|\\$SKYTILS-DUNGEON-SCORE-MIMIC\\$|\\Q" + SkyblockerConfigManager.get().locations.dungeons.mimicMessages.mimicMessage + "\\E)$"); } @Override @@ -21,7 +21,7 @@ public ChatFilterResult state() { @Override protected boolean onMatch(Text message, Matcher matcher) { - if (!Utils.isInDungeons()) return false; + if (!Utils.isInDungeons() || !DungeonScore.isDungeonStarted() || !DungeonScore.isMimicOnCurrentFloor()) return false; DungeonScore.setMimicKilled(true); return true; } From 044f700a3c5c1a54eb39b2210f5dfdf18f528d7b Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 19 Jan 2024 19:03:20 +0300 Subject: [PATCH 20/27] Add a description for score config --- .../de/hysky/skyblocker/config/categories/DungeonsCategory.java | 1 + src/main/resources/assets/skyblocker/lang/en_us.json | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index d939a4f0f1..9c311471b3 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -317,6 +317,7 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .build()) .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.@Tooltip"), Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.deathMessagesNote"))) .binding(defaults.locations.dungeons.dungeonScore.enableScoreHUD, () -> config.locations.dungeons.dungeonScore.enableScoreHUD, newValue -> config.locations.dungeons.dungeonScore.enableScoreHUD = newValue) diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 38b14170ea..c38cc7ffab 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -201,6 +201,8 @@ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.dungeonScoreMessage": "Dungeon Score %d Message", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.dungeonScoreMessage.@Tooltip": "Message which will be sent in the chat when reaching %d score in dungeons. The string \"[score]\" will be replaced with the dungeon score (%d).", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD": "Enable Score HUD", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.@Tooltip": "Displays the dungeon score in the HUD.", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.deathMessagesNote": "\n\n\nNote: This only works correctly if death messages are enabled in your skyblock settings.", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.scoreScaling": "Score Scaling", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit": "Dungeon Chest Profit Calculator", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator": "Enable Profit Calculator", From f7f8e5f0b8f9d638566e8528e1a64219b8bf40ad Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:31:31 -0500 Subject: [PATCH 21/27] Small changes --- .../skyblocker/config/categories/DungeonsCategory.java | 2 +- .../skyblocker/mixin/ClientPlayNetworkHandlerMixin.java | 2 +- .../de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java | 6 +++--- src/main/java/de/hysky/skyblocker/utils/Utils.java | 2 +- src/main/resources/assets/skyblocker/lang/en_us.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index 9c311471b3..1d8a7e8cc5 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -317,7 +317,7 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .build()) .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.@Tooltip"), Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.deathMessagesNote"))) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.@Tooltip"), Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.deathMessagesNote"))) .binding(defaults.locations.dungeons.dungeonScore.enableScoreHUD, () -> config.locations.dungeons.dungeonScore.enableScoreHUD, newValue -> config.locations.dungeons.dungeonScore.enableScoreHUD = newValue) diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java index e1eb6a2ddc..a853708881 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java @@ -70,7 +70,7 @@ public abstract class ClientPlayNetworkHandlerMixin { } @ModifyExpressionValue(method = "onEntityStatus", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/EntityStatusS2CPacket;getEntity(Lnet/minecraft/world/World;)Lnet/minecraft/entity/Entity;")) - private Entity skyblocker$onEntityDeath(Entity entity, @Local EntityStatusS2CPacket packet) { + private Entity skyblocker$onEntityDeath(Entity entity, @Local(argsOnly = true) EntityStatusS2CPacket packet) { if (packet.getStatus() == EntityStatuses.PLAY_DEATH_SOUND_OR_ADD_PROJECTILE_HIT_PARTICLES) DungeonScore.handleEntityDeath(entity); return entity; } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index 4dfbf62a89..c6dbf8dcdf 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -193,8 +193,8 @@ public static boolean isEntityMimic(Entity entity) { try { DefaultedList armor = (DefaultedList) zombie.getArmorItems(); return armor.stream().allMatch(ItemStack::isEmpty); - } catch (Exception f) { - LOGGER.error("[Skyblocker] Failed to check if entity is a mimic! Cause: {}", f.getMessage()); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to check if entity is a mimic!", e); return false; } } @@ -297,7 +297,7 @@ public static boolean hasSpiritPet(String name) { return true; } } catch (Exception e) { - LOGGER.error("[Skyblocker] Spirit pet lookup by name failed! Name: {} - Cause: {}", name, e.getMessage()); + LOGGER.error("[Skyblocker] Spirit pet lookup by name failed! Name: {}", name, e); } return false; }); diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index eaa07ff288..53c0ff4a2d 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -403,7 +403,7 @@ private static void initializeMayorCache() { if (json.get("success").getAsBoolean()) return json.get("mayor").getAsJsonObject().get("name").getAsString(); throw new IOException(json.get("cause").getAsString()); } catch (Exception e) { - LOGGER.error("[Skyblocker] Failed to get mayor status! Cause: {}", e.getMessage()); + LOGGER.error("[Skyblocker] Failed to get mayor status!", e); } return ""; }).thenAccept(s -> { diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index c38cc7ffab..3381e67a03 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -202,7 +202,7 @@ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.dungeonScoreMessage.@Tooltip": "Message which will be sent in the chat when reaching %d score in dungeons. The string \"[score]\" will be replaced with the dungeon score (%d).", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD": "Enable Score HUD", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.@Tooltip": "Displays the dungeon score in the HUD.", - "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.deathMessagesNote": "\n\n\nNote: This only works correctly if death messages are enabled in your skyblock settings.", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.deathMessagesNote": "\n\n\nNote: This only works correctly if death messages are enabled in your skyblock settings.", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.scoreScaling": "Score Scaling", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit": "Dungeon Chest Profit Calculator", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator": "Enable Profit Calculator", From b1bfa5e0009b2a969c913e701a56b812517a0700 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sat, 20 Jan 2024 13:25:04 +0300 Subject: [PATCH 22/27] Add death message filter and improve death regex --- .../skyblocker/config/SkyblockerConfig.java | 3 +++ .../categories/MessageFilterCategory.java | 8 ++++++ .../skyblock/dungeon/DungeonScore.java | 6 ++++- .../skyblock/filters/DeathFilter.java | 25 +++++++++++++++++++ .../utils/chat/ChatMessageListener.java | 3 ++- .../assets/skyblocker/lang/en_us.json | 4 ++- 6 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/DeathFilter.java diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index e47b008de4..4e7f980205 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -995,6 +995,9 @@ public static class Messages { @SerialEntry public ChatFilterResult hideMimicKill = ChatFilterResult.PASS; + @SerialEntry + public ChatFilterResult hideDeath = ChatFilterResult.PASS; + @SerialEntry public boolean hideMana = false; } diff --git a/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java index 3fe285de2e..ce34904979 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java @@ -110,6 +110,14 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.messages.hideMimicKill = newValue) .controller(ConfigUtils::createEnumCyclingListController) .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideDeath")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.messages.hideDeath.@Tooltip"))) + .binding(defaults.messages.hideDeath, + () -> config.messages.hideDeath, + newValue -> config.messages.hideDeath = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index c6dbf8dcdf..a59d56159d 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -47,7 +47,7 @@ public class DungeonScore { private static final Pattern CRYPTS_PATTERN = Pattern.compile("Crypts: (?\\d+)"); private static final Pattern COMPLETED_ROOMS_PATTERN = Pattern.compile(" *Completed Rooms: (?\\d+)"); //Chat patterns - private static final Pattern DEATHS_PATTERN = Pattern.compile(".*?\u2620 (?\\S+) .*"); + private static final Pattern DEATHS_PATTERN = Pattern.compile(" \\u2620 (?\\S+) .*"); //Other patterns private static final Pattern MIMIC_FLOORS_PATTERN = Pattern.compile("[FM][67]"); @@ -82,6 +82,10 @@ public static void init() { checkMessageForWatcher(str); } }); + ClientReceiveMessageEvents.GAME_CANCELED.register((message, overlay) -> { + if (overlay || !Utils.isInDungeons() || !dungeonStarted) return; + checkMessageForDeaths(message.getString()); + }); } public static void tick() { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/DeathFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/DeathFilter.java new file mode 100644 index 0000000000..f2b9e7c56d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/DeathFilter.java @@ -0,0 +1,25 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.text.Text; + +import java.util.regex.Matcher; + +public class DeathFilter extends ChatPatternListener { + + public DeathFilter() { + super(" \\u2620 .*"); + } + + @Override + protected ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideDeath; + } + + @Override + protected boolean onMatch(Text message, Matcher matcher) { + return true; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java b/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java index 9739862519..ee43bc4cf0 100644 --- a/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java +++ b/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java @@ -55,7 +55,8 @@ static void init() { new AutopetFilter(), new ShowOffFilter(), new ToggleSkyMallFilter(), - new MimicFilter() + new MimicFilter(), + new DeathFilter() }; // Register all listeners to EVENT for (ChatMessageListener listener : listeners) { diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 3381e67a03..91354ae77c 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -202,7 +202,7 @@ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.dungeonScoreMessage.@Tooltip": "Message which will be sent in the chat when reaching %d score in dungeons. The string \"[score]\" will be replaced with the dungeon score (%d).", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD": "Enable Score HUD", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.@Tooltip": "Displays the dungeon score in the HUD.", - "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.deathMessagesNote": "\n\n\nNote: This only works correctly if death messages are enabled in your skyblock settings.", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.deathMessagesNote": "\n\n\nNote: This only works correctly if death messages are enabled in your skyblock settings. If you want to hide death messages, use this mod's Hide Player Death Messages setting instead to allow further processing of death messages.", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.scoreScaling": "Score Scaling", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit": "Dungeon Chest Profit Calculator", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator": "Enable Profit Calculator", @@ -305,6 +305,8 @@ "text.autoconfig.skyblocker.option.messages.hideToggleSkyMall.@Tooltip": "Hides those pesky messages telling you to disable the Sky Mall HOTM perk when you want it enabled!", "text.autoconfig.skyblocker.option.messages.hideMimicKill": "Hide Mimic Kill Messages", "text.autoconfig.skyblocker.option.messages.hideMimicKill.@Tooltip": "Filters the \"Mimic dead!\" and \"Mimic killed!\" messages from chat.", + "text.autoconfig.skyblocker.option.messages.hideDeath": "Hide Player Death Messages", + "text.autoconfig.skyblocker.option.messages.hideDeath.@Tooltip": "Filters the player death messages from chat.", "text.autoconfig.skyblocker.category.slayer": "Slayers", "text.autoconfig.skyblocker.option.slayer.vampireSlayer": "Vampire Slayer", "text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableEffigyWaypoints": "Enable Effigy Waypoints", From ac6ac5096bf60c39ddf52bad41a6123afa51b097 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sat, 20 Jan 2024 18:31:06 +0300 Subject: [PATCH 23/27] Singularize MimicMessages --- .../skyblocker/config/SkyblockerConfig.java | 6 ++--- .../config/categories/DungeonsCategory.java | 22 +++++++++---------- .../skyblock/dungeon/DungeonScore.java | 4 ++-- .../skyblock/filters/MimicFilter.java | 2 +- .../assets/skyblocker/lang/en_us.json | 10 ++++----- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index 4e7f980205..9477fbeda8 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -606,7 +606,7 @@ public static class Dungeons { public DungeonChestProfit dungeonChestProfit = new DungeonChestProfit(); @SerialEntry - public MimicMessages mimicMessages = new MimicMessages(); + public MimicMessage mimicMessage = new MimicMessage(); @SerialEntry public boolean croesusHelper = true; @@ -800,9 +800,9 @@ public static class DungeonChestProfit { public Formatting incompleteColor = Formatting.BLUE; } - public static class MimicMessages { + public static class MimicMessage { @SerialEntry - public boolean sendMimicMessages = true; + public boolean sendMimicMessage = true; @SerialEntry public String mimicMessage = "Mimic dead!"; diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index 1d8a7e8cc5..c39eb05cb0 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -434,22 +434,22 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .build()) //Mimic Messages .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages")) + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage")) .collapsed(true) .option(Option.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.sendMimicMessages")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.sendMimicMessages.@Tooltip"))) - .binding(defaults.locations.dungeons.mimicMessages.sendMimicMessages, - () -> config.locations.dungeons.mimicMessages.sendMimicMessages, - newValue -> config.locations.dungeons.mimicMessages.sendMimicMessages = newValue) + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage.@Tooltip"))) + .binding(defaults.locations.dungeons.mimicMessage.sendMimicMessage, + () -> config.locations.dungeons.mimicMessage.sendMimicMessage, + newValue -> config.locations.dungeons.mimicMessage.sendMimicMessage = newValue) .controller(ConfigUtils::createBooleanController) .build()) .option(Option.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.mimicMessage")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.mimicMessage.@Tooltip"))) - .binding(defaults.locations.dungeons.mimicMessages.mimicMessage, - () -> config.locations.dungeons.mimicMessages.mimicMessage, - newValue -> config.locations.dungeons.mimicMessages.mimicMessage = newValue) + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.mimicMessage")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.mimicMessage.@Tooltip"))) + .binding(defaults.locations.dungeons.mimicMessage.mimicMessage, + () -> config.locations.dungeons.mimicMessage.mimicMessage, + newValue -> config.locations.dungeons.mimicMessage.mimicMessage = newValue) .controller(StringControllerBuilder::create) .build()) .build()) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index a59d56159d..cb834e7f23 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -35,7 +35,7 @@ public class DungeonScore { private static final SkyblockerConfig.DungeonScore SCORE_CONFIG = SkyblockerConfigManager.get().locations.dungeons.dungeonScore; - private static final SkyblockerConfig.MimicMessages MIMIC_MESSAGES_CONFIG = SkyblockerConfigManager.get().locations.dungeons.mimicMessages; + private static final SkyblockerConfig.MimicMessage MIMIC_MESSAGE_CONFIG = SkyblockerConfigManager.get().locations.dungeons.mimicMessage; private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Dungeon Score"); //Scoreboard patterns private static final Pattern CLEARED_PATTERN = Pattern.compile("Cleared: (?\\d+)%.*"); @@ -206,7 +206,7 @@ public static boolean isEntityMimic(Entity entity) { public static void handleEntityDeath(Entity entity) { if (mimicKilled) return; if (!isEntityMimic(entity)) return; - if (MIMIC_MESSAGES_CONFIG.sendMimicMessages) MessageScheduler.INSTANCE.sendMessageAfterCooldown(MIMIC_MESSAGES_CONFIG.mimicMessage); + if (MIMIC_MESSAGE_CONFIG.sendMimicMessage) MessageScheduler.INSTANCE.sendMessageAfterCooldown(MIMIC_MESSAGE_CONFIG.mimicMessage); mimicKilled = true; } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java index 31f8318797..367e393c79 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java @@ -11,7 +11,7 @@ public class MimicFilter extends ChatPatternListener { public MimicFilter() { - super(".*?(?:Mimic dead!?|Mimic Killed!|\\$SKYTILS-DUNGEON-SCORE-MIMIC\\$|\\Q" + SkyblockerConfigManager.get().locations.dungeons.mimicMessages.mimicMessage + "\\E)$"); + super(".*?(?:Mimic dead!?|Mimic Killed!|\\$SKYTILS-DUNGEON-SCORE-MIMIC\\$|\\Q" + SkyblockerConfigManager.get().locations.dungeons.mimicMessage.mimicMessage + "\\E)$"); } @Override diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 91354ae77c..1360f5f120 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -235,11 +235,11 @@ "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.solveWaterboard": "Solve Waterboard Puzzle", "text.autoconfig.skyblocker.option.locations.dungeons.solveWaterboard.@Tooltip": "Click the levers with green boxes to solve the puzzle.", - "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages": "Mimic Messages", - "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.sendMimicMessages": "Enable Mimic Messages", - "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.sendMimicMessages.@Tooltip": "Sends a message in chat when a mimic is killed for score calculation mods.", - "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.mimicMessage": "Mimic Message", - "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessages.mimicMessage.@Tooltip": "Message which will be sent in the chat when a mimic is killed. Recommended to keep as \"Mimic dead!\"", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage": "Mimic Message", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage": "Enable Mimic Message", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage.@Tooltip": "Sends a message in chat upon killing a mimic for other players' score calculation mods.", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.mimicMessage": "Mimic Message", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.mimicMessage.@Tooltip": "Message which will be sent in the chat upon killing a mimic. Recommended to retain the default value.", "text.autoconfig.skyblocker.option.locations.dungeons.lividColor": "Livid Color", "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorGlow": "Enable Livid Color Glow", "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorGlow.@Tooltip": "Applies the glowing effect to the correct Livid in F5/M5.", From 4fd8d4f727ce611eb28218e6a47080894a78b7b1 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sat, 20 Jan 2024 18:57:08 +0300 Subject: [PATCH 24/27] Fix mimic messages not increasing score when mimic filter is disabled --- .../skyblocker/skyblock/dungeon/DungeonScore.java | 11 +++++++++-- .../skyblocker/skyblock/filters/MimicFilter.java | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index cb834e7f23..10605d8b53 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -48,6 +48,7 @@ public class DungeonScore { private static final Pattern COMPLETED_ROOMS_PATTERN = Pattern.compile(" *Completed Rooms: (?\\d+)"); //Chat patterns private static final Pattern DEATHS_PATTERN = Pattern.compile(" \\u2620 (?\\S+) .*"); + private static final Pattern MIMIC_PATTERN = Pattern.compile(".*?(?:Mimic dead!?|Mimic Killed!|\\$SKYTILS-DUNGEON-SCORE-MIMIC\\$|\\Q" + MIMIC_MESSAGE_CONFIG.mimicMessage + "\\E)$"); //Other patterns private static final Pattern MIMIC_FLOORS_PATTERN = Pattern.compile("[FM][67]"); @@ -80,6 +81,7 @@ public static void init() { } else { checkMessageForDeaths(str); checkMessageForWatcher(str); + if (floorHasMimics) checkMessageForMimic(str); //Only called when the message is not cancelled & isn't on the action bar, complementing MimicFilter } }); ClientReceiveMessageEvents.GAME_CANCELED.register((message, overlay) -> { @@ -210,8 +212,8 @@ public static void handleEntityDeath(Entity entity) { mimicKilled = true; } - public static void setMimicKilled(boolean state) { - mimicKilled = state; + public static void onMimicKill() { + mimicKilled = true; } //This is not very accurate at the beginning of the dungeon since clear percentage is rounded to the closest integer, so at lower percentages its effect on the result is quite high. @@ -330,6 +332,11 @@ private static void checkMessageForMort(String message) { onDungeonStart(); } + private static void checkMessageForMimic(String message) { + if (!MIMIC_PATTERN.matcher(message).matches()) return; + onMimicKill(); + } + public static void setCurrentFloor() { for (String sidebarLine : Utils.STRING_SCOREBOARD) { Matcher floorMatcher = FLOOR_PATTERN.matcher(sidebarLine); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java index 367e393c79..cb84525458 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java @@ -22,7 +22,7 @@ public ChatFilterResult state() { @Override protected boolean onMatch(Text message, Matcher matcher) { if (!Utils.isInDungeons() || !DungeonScore.isDungeonStarted() || !DungeonScore.isMimicOnCurrentFloor()) return false; - DungeonScore.setMimicKilled(true); + DungeonScore.onMimicKill(); //Only called when the message is cancelled | sent to action bar, complementing DungeonScore#checkMessageForMimic return true; } } From 6120f55ac3161ebf4c0858ab674a5cbffa7879a9 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sat, 20 Jan 2024 19:09:17 +0300 Subject: [PATCH 25/27] Refactor score hud settings --- .../config/categories/DungeonsCategory.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index c39eb05cb0..6ec06e7adb 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -230,6 +230,25 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.locations.dungeons.dungeonScore.dungeonScore300Message = newValue) .controller(StringControllerBuilder::create) .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.@Tooltip"), Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.deathMessagesNote"))) + .binding(defaults.locations.dungeons.dungeonScore.enableScoreHUD, + () -> config.locations.dungeons.dungeonScore.enableScoreHUD, + newValue -> config.locations.dungeons.dungeonScore.enableScoreHUD = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.scoreScaling")) + .binding(defaults.locations.dungeons.dungeonScore.scoreScaling, + () -> config.locations.dungeons.dungeonScore.scoreScaling, + newValue -> { + config.locations.dungeons.dungeonScore.scoreX = config.locations.dungeons.dungeonScore.scoreX + (int) ((config.locations.dungeons.dungeonScore.scoreScaling - newValue) * 38.0); + config.locations.dungeons.dungeonScore.scoreY = config.locations.dungeons.dungeonScore.scoreY + (int) ((config.locations.dungeons.dungeonScore.scoreScaling - newValue) * MinecraftClient.getInstance().textRenderer.fontHeight / 2.0); + config.locations.dungeons.dungeonScore.scoreScaling = newValue; + }) + .controller(FloatFieldControllerBuilder::create) + .build()) .build()) //Dungeon Chest Profit @@ -315,14 +334,6 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.locations.dungeons.enableMap = newValue) .controller(ConfigUtils::createBooleanController) .build()) - .option(Option.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.@Tooltip"), Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.deathMessagesNote"))) - .binding(defaults.locations.dungeons.dungeonScore.enableScoreHUD, - () -> config.locations.dungeons.dungeonScore.enableScoreHUD, - newValue -> config.locations.dungeons.dungeonScore.enableScoreHUD = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) .option(ButtonOption.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mapScreen")) .text(Text.translatable("text.skyblocker.open")) @@ -335,17 +346,6 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.locations.dungeons.mapScaling = newValue) .controller(FloatFieldControllerBuilder::create) .build()) - .option(Option.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.scoreScaling")) - .binding(defaults.locations.dungeons.dungeonScore.scoreScaling, - () -> config.locations.dungeons.dungeonScore.scoreScaling, - newValue -> { - config.locations.dungeons.dungeonScore.scoreX = config.locations.dungeons.dungeonScore.scoreX + (int) ((config.locations.dungeons.dungeonScore.scoreScaling - newValue) * 38.0); - config.locations.dungeons.dungeonScore.scoreY = config.locations.dungeons.dungeonScore.scoreY + (int) ((config.locations.dungeons.dungeonScore.scoreScaling - newValue) * MinecraftClient.getInstance().textRenderer.fontHeight / 2.0); - config.locations.dungeons.dungeonScore.scoreScaling = newValue; - }) - .controller(FloatFieldControllerBuilder::create) - .build()) .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.playerSecretsTracker")) .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.playerSecretsTracker.@Tooltip"))) From 9bc2efcb66a3dbf0c0ab882ef05fdae551ab5912 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sun, 21 Jan 2024 09:45:02 +0300 Subject: [PATCH 26/27] Fix incorrect merge leftovers --- src/main/java/de/hysky/skyblocker/SkyblockerMod.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 4a2ff49763..83f41c0b52 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -110,7 +110,6 @@ public void onInitializeClient() { Waterboard.init(); DungeonScore.init(); PartyFinderScreen.initClass(); - DungeonScoreHUD.init(); ChestValue.init(); FireFreezeStaffTimer.init(); GuardianHealth.init(); From cbf7f80faf78764f7787122be194e8d26e8b1f9e Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sun, 21 Jan 2024 12:25:38 -0500 Subject: [PATCH 27/27] Fix DungeonsCategory --- .../hysky/skyblocker/config/categories/DungeonsCategory.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index 6ec06e7adb..3b685f9afb 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -432,7 +432,8 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.locations.dungeons.allowDroppingProtectedItems = newValue) .controller(ConfigUtils::createBooleanController) .build()) - //Mimic Messages + + //Mimic Message .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage")) .collapsed(true) @@ -454,7 +455,7 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .build()) .build()) - // Livid Color + //Livid Color .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor")) .collapsed(true)