aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorEmirlol <81419447+Emirlol@users.noreply.github.com>2024-01-07 02:06:28 +0300
committerRime <81419447+Emirlol@users.noreply.github.com>2024-01-21 09:36:50 +0300
commitcdd9441aa587ef7ecb2ac285350f59846b433ff9 (patch)
treee6233d1d31fd42156ae5498d4d51c3e8188073ec /src/main
parent08444dc32b39b8cdf8bf6a1e47248fc4d871d2d7 (diff)
downloadSkyblocker-cdd9441aa587ef7ecb2ac285350f59846b433ff9.tar.gz
Skyblocker-cdd9441aa587ef7ecb2ac285350f59846b433ff9.tar.bz2
Skyblocker-cdd9441aa587ef7ecb2ac285350f59846b433ff9.zip
Add dungeon score calculation on client-side
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java14
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java30
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java8
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java290
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/filters/MimicFilter.java26
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Utils.java27
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java7
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json7
8 files changed, 363 insertions, 46 deletions
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index aa6e5d24..e07a7588 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -606,6 +606,9 @@ public class SkyblockerConfig {
public DungeonChestProfit dungeonChestProfit = new DungeonChestProfit();
@SerialEntry
+ public MimicMessages mimicMessages = new MimicMessages();
+
+ @SerialEntry
public boolean croesusHelper = true;
@SerialEntry
@@ -785,6 +788,14 @@ public class SkyblockerConfig {
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;
@@ -970,6 +981,9 @@ public class SkyblockerConfig {
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 583bc166..06133afc 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 @@ package de.hysky.skyblocker.config.categories;
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 class DungeonsCategory {
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.<Boolean>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.<String>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 37f24d8c..3fe285de 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 class MessageFilterCategory {
newValue -> config.messages.hideMana = newValue)
.controller(ConfigUtils::createBooleanController)
.build())
+ .option(Option.<ChatFilterResult>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 d67d6988..ebec99dc 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: (?<cleared>\\d+)% \\((?<score>\\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: (?<cleared>\\d+)%.*");
+ private static final Pattern SECRETS_PATTERN = Pattern.compile("Secrets Found: (?<secper>\\d+\\.?\\d*)%");
+ private static final Pattern PUZZLES_PATTERN = Pattern.compile(".+?(?=:): \\[(?<state>.)](?: \\(\\w+\\))?");
+ private static final Pattern PUZZLE_COUNT_PATTERN = Pattern.compile("Puzzles: \\((?<count>\\d+)\\)");
+ private static final Pattern TIME_PATTERN = Pattern.compile("Time: (?:(?<hours>\\d+(?=h))?h? ?(?<minutes>\\d+(?=m))?m? ?(?<seconds>\\d+(?=s))s|Soon!)");
+ private static final Pattern CRYPTS_PATTERN = Pattern.compile("Crypts: (?<crypts>\\d+)");
+ private static final Pattern COMPLETED_ROOMS_PATTERN = Pattern.compile(" *Completed Rooms: (?<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 \\((?<floor>[EFM]\\D*\\d*)\\)");
+ private static final Pattern DEATHS_PATTERN = Pattern.compile("Team Deaths: (?<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<String, FloorRequirement> 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 class DungeonScore {
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<ItemStack> armor = (DefaultedList<ItemStack>) 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 00000000..0fce5b2c
--- /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 45ace085..1fc718be 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.jetbrains.annotations.NotNull;
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 class Utils {
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 @@ public class Utils {
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 ebdb6f09..97398625 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.ThreeWeirdos;
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 @@ public interface ChatMessageListener {
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 dfc0c035..cd1d9673 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",