diff options
| author | Kevin <92656833+kevinthegreat1@users.noreply.github.com> | 2024-01-21 13:05:51 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-21 13:05:51 -0500 |
| commit | a092f33ab7e52006cbca75adb6974ccf67f7e890 (patch) | |
| tree | 07412faeffd41b7d2aea72c8d75269db373ce194 /src | |
| parent | 08444dc32b39b8cdf8bf6a1e47248fc4d871d2d7 (diff) | |
| parent | cbf7f80faf78764f7787122be194e8d26e8b1f9e (diff) | |
| download | Skyblocker-a092f33ab7e52006cbca75adb6974ccf67f7e890.tar.gz Skyblocker-a092f33ab7e52006cbca75adb6974ccf67f7e890.tar.bz2 Skyblocker-a092f33ab7e52006cbca75adb6974ccf67f7e890.zip | |
Merge pull request #500 from Emirlol/clientside-dungeon-score
Add dungeon score calculation on client-side
Diffstat (limited to 'src')
14 files changed, 664 insertions, 99 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..9477fbed 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 MimicMessage mimicMessage = new MimicMessage(); + + @SerialEntry public boolean croesusHelper = true; @SerialEntry @@ -757,6 +760,18 @@ public class SkyblockerConfig { @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 { @@ -785,6 +800,14 @@ public class SkyblockerConfig { public Formatting incompleteColor = Formatting.BLUE; } + public static class MimicMessage { + @SerialEntry + public boolean sendMimicMessage = true; + + @SerialEntry + public String mimicMessage = "Mimic dead!"; + } + public static class LividColor { @SerialEntry public boolean enableLividColorGlow = true; @@ -970,6 +993,12 @@ public class SkyblockerConfig { public ChatFilterResult hideToggleSkyMall = ChatFilterResult.PASS; @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/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index 583bc166..3b685f9a 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; @@ -235,6 +230,25 @@ public class DungeonsCategory { newValue -> config.locations.dungeons.dungeonScore.dungeonScore300Message = newValue) .controller(StringControllerBuilder::create) .build()) + .option(Option.<Boolean>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.<Float>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 @@ -419,7 +433,29 @@ public class DungeonsCategory { .controller(ConfigUtils::createBooleanController) .build()) - // Livid Color + //Mimic Message + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage")) + .collapsed(true) + .option(Option.<Boolean>createBuilder() + .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.<String>createBuilder() + .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()) + + //Livid Color .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor")) .collapsed(true) 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..ce349049 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,22 @@ 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()) + .option(Option.<ChatFilterResult>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/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java index 4015dfa5..a8537088 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java @@ -1,19 +1,23 @@ package de.hysky.skyblocker.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.WrapWithCondition; 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 org.slf4j.Logger; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -49,12 +53,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 +68,10 @@ public abstract class ClientPlayNetworkHandlerMixin { private void skyblocker$onParticle(ParticleS2CPacket packet, CallbackInfo ci) { MythologicalRitual.onParticle(packet); } + + @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(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/mixin/InGameHudMixin.java b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java index 0ee7b528..df7cbdea 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 com.llamalad7.mixinextras.sugar.Local; 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.dungeonScore.enableScoreHUD) 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 e1af85ea..293d301f 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 de.hysky.skyblocker.utils.scheduler.Scheduler; 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.FilledMapItem; 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 class DungeonMap { } } - 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 02b08254..0f42c495 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 de.hysky.skyblocker.utils.render.RenderHelper; 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.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; protected DungeonMapConfigScreen() { @@ -27,17 +31,23 @@ public class DungeonMapConfigScreen extends Screen { 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.dungeonScore.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); + 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 class DungeonMapConfigScreen extends Screen { @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(DungeonScoreHUD.getFormattedScoreText()) * SkyblockerConfigManager.get().locations.dungeons.dungeonScore.scoreScaling / 2), 0); + scoreY = (int) (mapY + (128 * SkyblockerConfigManager.get().locations.dungeons.mapScaling) + 4); } return super.mouseClicked(mouseX, mouseY, button); @@ -54,10 +66,22 @@ public class DungeonMapConfigScreen extends Screen { @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.dungeonScore.scoreX = scoreX; + SkyblockerConfigManager.get().locations.dungeons.dungeonScore.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/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index d67d6988..10605d8b 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -1,76 +1,389 @@ 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.minecraft.client.MinecraftClient; +import net.minecraft.entity.Entity; +import net.minecraft.entity.mob.ZombieEntity; +import net.minecraft.item.ItemStack; 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.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 CONFIG = SkyblockerConfigManager.get().locations.dungeons.dungeonScore; - private static final Pattern DUNGEON_CLEARED_PATTERN = Pattern.compile("Cleared: (?<cleared>\\d+)% \\((?<score>\\d+)\\)"); - private static boolean sent270; - private static boolean sent300; - - public static void init() { - Scheduler.INSTANCE.scheduleCyclic(DungeonScore::tick, 20); - ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset()); - } - - public static void tick() { - MinecraftClient client = MinecraftClient.getInstance(); - if (!Utils.isInDungeons() || client.player == null) { - reset(); - return; - } - - 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; - } - } - - private static void reset() { - sent270 = false; - sent300 = false; - } + private static final SkyblockerConfig.DungeonScore SCORE_CONFIG = SkyblockerConfigManager.get().locations.dungeons.dungeonScore; + 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: (?<cleared>\\d+)%.*"); + private static final Pattern FLOOR_PATTERN = Pattern.compile(".*?(?=T)The Catacombs \\((?<floor>[EFM]\\D*\\d*)\\)"); + //Playerlist patterns + 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 CRYPTS_PATTERN = Pattern.compile("Crypts: (?<crypts>\\d+)"); + private static final Pattern COMPLETED_ROOMS_PATTERN = Pattern.compile(" *Completed Rooms: (?<rooms>\\d+)"); + //Chat patterns + private static final Pattern DEATHS_PATTERN = Pattern.compile(" \\u2620 (?<whodied>\\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]"); + + private static FloorRequirement floorRequirement; + private static String currentFloor; + private static boolean isCurrentFloorEntrance; + private static boolean floorHasMimics; + private static boolean sent270; + private static boolean sent300; + private static boolean mimicKilled; + private static boolean dungeonStarted; + private static boolean isMayorPaul; + 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<String, Boolean> 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) -> { + if (overlay || !Utils.isInDungeons()) return; + String str = message.getString(); + if (!dungeonStarted) { + checkMessageForMort(str); + } 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) -> { + if (overlay || !Utils.isInDungeons() || !dungeonStarted) return; + checkMessageForDeaths(message.getString()); + }); + } + + public static void tick() { + MinecraftClient client = MinecraftClient.getInstance(); + if (!Utils.isInDungeons() || client.player == null) { + reset(); + return; + } + if (!dungeonStarted) return; + + score = calculateScore(); + 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")); + } + 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("/pc " + Constants.PREFIX.get().getString() + 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() { + floorRequirement = null; + currentFloor = ""; + isCurrentFloorEntrance = false; + floorHasMimics = false; + sent270 = false; + sent300 = false; + mimicKilled = false; + dungeonStarted = false; + isMayorPaul = false; + firstDeathHasSpiritPet = false; + bloodRoomCompleted = false; + startingTime = 0L; + puzzleCount = 0; + deathCount = 0; + score = 0; + } + + private static void onDungeonStart() { + setCurrentFloor(); + dungeonStarted = true; + puzzleCount = getPuzzleCount(); + isMayorPaul = Utils.getMayor().equals("Paul"); + startingTime = System.currentTimeMillis(); + floorRequirement = FloorRequirement.valueOf(currentFloor); + floorHasMimics = MIMIC_FLOORS_PATTERN.matcher(currentFloor).matches(); + if (currentFloor.equals("E")) isCurrentFloorEntrance = true; + } + + private static int calculateScore() { + 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() { + 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 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; + if (timeSpent < floorRequirement.timeLimit) return score; + + 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 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() { + int paulScore = isMayorPaul ? 10 : 0; + int cryptsScore = Math.min(getCrypts(), 5); + 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; + } + + public static boolean isEntityMimic(Entity entity) { + if (!Utils.isInDungeons() || !floorHasMimics || !(entity instanceof ZombieEntity zombie) || !zombie.isBaby()) return false; + try { + DefaultedList<ItemStack> armor = (DefaultedList<ItemStack>) zombie.getArmorItems(); + return armor.stream().allMatch(ItemStack::isEmpty); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to check if entity is a mimic!", e); + return false; + } + } + + public static void handleEntityDeath(Entity entity) { + if (mimicKilled) return; + if (!isEntityMimic(entity)) return; + if (MIMIC_MESSAGE_CONFIG.sendMimicMessage) MessageScheduler.INSTANCE.sendMessageAfterCooldown(MIMIC_MESSAGE_CONFIG.mimicMessage); + mimicKilled = true; + } + + public static void onMimicKill() { + mimicKilled = true; + } + + //This is not very accurate at the beginning of the dungeon since clear |
