diff options
| author | Aaron <51387595+AzureAaron@users.noreply.github.com> | 2024-08-12 19:28:30 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-12 19:28:30 -0400 |
| commit | 3830cea067dd2d0cdb2910dd624e0d9f0e46b7c3 (patch) | |
| tree | c934a3a23b854b935fb54ca6afc541a23bf99fb0 /src/main/java | |
| parent | 11fc3c22ffb57834a3b95dcacafa252dd67a658e (diff) | |
| parent | 507e962ee8adc0b04539aa1794208e7a0d68579b (diff) | |
| download | Skyblocker-3830cea067dd2d0cdb2910dd624e0d9f0e46b7c3.tar.gz Skyblocker-3830cea067dd2d0cdb2910dd624e0d9f0e46b7c3.tar.bz2 Skyblocker-3830cea067dd2d0cdb2910dd624e0d9f0e46b7c3.zip | |
Merge pull request #888 from BigloBot/new-slayer-highlighting-pr
[Slayer Helpers] Slayer Highlights, Blaze Helpers and Glow Caching
Diffstat (limited to 'src/main/java')
11 files changed, 500 insertions, 50 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 5e573598..405827e0 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -31,6 +31,7 @@ import de.hysky.skyblocker.skyblock.end.BeaconHighlighter; import de.hysky.skyblocker.skyblock.end.EnderNodes; import de.hysky.skyblocker.skyblock.end.TheEnd; import de.hysky.skyblocker.skyblock.entity.MobBoundingBoxes; +import de.hysky.skyblocker.skyblock.entity.MobGlow; import de.hysky.skyblocker.skyblock.events.EventNotifications; import de.hysky.skyblocker.skyblock.fancybars.FancyStatusBars; import de.hysky.skyblocker.skyblock.garden.FarmingHud; @@ -48,6 +49,7 @@ import de.hysky.skyblocker.skyblock.profileviewer.ProfileViewerScreen; import de.hysky.skyblocker.skyblock.rift.TheRift; import de.hysky.skyblocker.skyblock.searchoverlay.SearchOverManager; import de.hysky.skyblocker.skyblock.shortcut.Shortcuts; +import de.hysky.skyblocker.skyblock.slayers.SlayerEntitiesGlow; import de.hysky.skyblocker.skyblock.special.SpecialEffects; import de.hysky.skyblocker.skyblock.tabhud.TabHud; import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenMaster; @@ -212,6 +214,8 @@ public class SkyblockerMod implements ClientModInitializer { TooltipManager.init(); SlotTextManager.init(); BazaarHelper.init(); + MobGlow.init(); + SlayerEntitiesGlow.init(); Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20); Scheduler.INSTANCE.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 200); diff --git a/src/main/java/de/hysky/skyblocker/config/categories/SlayersCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/SlayersCategory.java index 0a65aa3b..f0e256a7 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/SlayersCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/SlayersCategory.java @@ -2,6 +2,7 @@ package de.hysky.skyblocker.config.categories; import de.hysky.skyblocker.config.ConfigUtils; import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.configs.SlayersConfig; import dev.isxander.yacl3.api.ConfigCategory; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.OptionDescription; @@ -17,6 +18,31 @@ public class SlayersCategory { return ConfigCategory.createBuilder() .name(Text.translatable("skyblocker.config.slayer")) + //General Slayers Options + .option(Option.<SlayersConfig.HighlightSlayerEntities>createBuilder() + .name(Text.translatable("skyblocker.config.slayer.highlightMinis")) + .description(OptionDescription.of( + Text.translatable("skyblocker.config.slayer.highlightMinis.@Tooltip[0]"), + Text.translatable("skyblocker.config.slayer.highlightMinis.@Tooltip[1]"), + Text.translatable("skyblocker.config.slayer.highlightMinis.@Tooltip[2]"))) + .binding(defaults.slayers.highlightMinis, + () -> config.slayers.highlightMinis, + newValue -> config.slayers.highlightMinis = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.<SlayersConfig.HighlightSlayerEntities>createBuilder() + .name(Text.translatable("skyblocker.config.slayer.highlightBosses")) + .description(OptionDescription.of( + Text.translatable("skyblocker.config.slayer.highlightBosses.@Tooltip[0]"), + Text.translatable("skyblocker.config.slayer.highlightBosses.@Tooltip[1]"), + Text.translatable("skyblocker.config.slayer.highlightBosses.@Tooltip[2]"), + Text.translatable("skyblocker.config.slayer.highlightBosses.@Tooltip[3]"))) + .binding(defaults.slayers.highlightBosses, + () -> config.slayers.highlightBosses, + newValue -> config.slayers.highlightBosses = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + //Enderman Slayer .group(OptionGroup.createBuilder() .name(Text.translatable("skyblocker.config.slayer.endermanSlayer")) @@ -138,6 +164,28 @@ public class SlayersCategory { .build()) .build()) + //Blaze Slayer + .group(OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.slayer.blazeSlayer")) + .collapsed(true) + .option(Option.<SlayersConfig.BlazeSlayer.FirePillar>createBuilder() + .name(Text.translatable("skyblocker.config.slayer.blazeSlayer.enableFirePillarAnnouncer")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.slayer.blazeSlayer.enableFirePillarAnnouncer.@Tooltip"))) + .binding(defaults.slayers.blazeSlayer.firePillarCountdown, + () -> config.slayers.blazeSlayer.firePillarCountdown, + newValue -> config.slayers.blazeSlayer.firePillarCountdown = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.slayer.blazeSlayer.attunementHighlights")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.slayer.blazeSlayer.attunementHighlights.@Tooltip"))) + .binding(defaults.slayers.blazeSlayer.attunementHighlights, + () -> config.slayers.blazeSlayer.attunementHighlights, + newValue -> config.slayers.blazeSlayer.attunementHighlights = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/config/configs/SlayersConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/SlayersConfig.java index ff6c2275..82713e5d 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/SlayersConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/SlayersConfig.java @@ -4,11 +4,24 @@ import dev.isxander.yacl3.config.v2.api.SerialEntry; public class SlayersConfig { @SerialEntry + public HighlightSlayerEntities highlightMinis = HighlightSlayerEntities.OFF; + + @SerialEntry + public HighlightSlayerEntities highlightBosses = HighlightSlayerEntities.OFF; + + public enum HighlightSlayerEntities { + OFF, GLOW, HITBOX + } + + @SerialEntry public EndermanSlayer endermanSlayer = new EndermanSlayer(); @SerialEntry public VampireSlayer vampireSlayer = new VampireSlayer(); + @SerialEntry + public BlazeSlayer blazeSlayer = new BlazeSlayer(); + public static class EndermanSlayer { @SerialEntry public boolean enableYangGlyphsNotification = true; @@ -57,4 +70,29 @@ public class SlayersConfig { @SerialEntry public int maniaUpdateFrequency = 5; } + + public static class BlazeSlayer { + @SerialEntry + public FirePillar firePillarCountdown = FirePillar.SOUND_AND_VISUAL; + + @SerialEntry + public Boolean attunementHighlights = true; + + public enum FirePillar { + OFF("Off"), + VISUAL("Visual Indicator"), + SOUND_AND_VISUAL("Sound and Visual Indicator"); + + private final String description; + + FirePillar(String description) { + this.description = description; + } + + @Override + public String toString() { + return description; + } + } + } } diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index 4e263015..8cafb601 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -3,10 +3,13 @@ package de.hysky.skyblocker.mixins; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import com.llamalad7.mixinextras.sugar.Local; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.config.configs.SlayersConfig; import de.hysky.skyblocker.skyblock.CompactDamage; import de.hysky.skyblocker.skyblock.FishingHelper; import de.hysky.skyblocker.skyblock.chocolatefactory.EggFinder; import de.hysky.skyblocker.skyblock.crimson.dojo.DojoManager; +import de.hysky.skyblocker.skyblock.crimson.slayer.FirePillarAnnouncer; import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; import de.hysky.skyblocker.skyblock.dwarven.WishingCompassSolver; @@ -14,6 +17,7 @@ import de.hysky.skyblocker.skyblock.dwarven.CrystalsChestHighlighter; import de.hysky.skyblocker.skyblock.end.BeaconHighlighter; import de.hysky.skyblocker.skyblock.end.EnderNodes; import de.hysky.skyblocker.skyblock.end.TheEnd; +import de.hysky.skyblocker.skyblock.slayers.SlayerEntitiesGlow; import de.hysky.skyblocker.skyblock.waypoint.MythologicalRitual; import de.hysky.skyblocker.utils.SlayerUtils; import de.hysky.skyblocker.utils.Utils; @@ -112,6 +116,7 @@ public abstract class ClientPlayNetworkHandlerMixin { if (packet.getStatus() == EntityStatuses.PLAY_DEATH_SOUND_OR_ADD_PROJECTILE_HIT_PARTICLES) { DungeonScore.handleEntityDeath(entity); TheEnd.onEntityDeath(entity); + SlayerEntitiesGlow.onEntityDeath(entity); } return entity; } @@ -120,6 +125,13 @@ public abstract class ClientPlayNetworkHandlerMixin { private void skyblocker$onEntityTrackerUpdate(EntityTrackerUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { if (!(entity instanceof ArmorStandEntity armorStandEntity)) return; + if (SkyblockerConfigManager.get().slayers.highlightMinis == SlayersConfig.HighlightSlayerEntities.GLOW && SlayerEntitiesGlow.isSlayerMiniMob(armorStandEntity) + || SkyblockerConfigManager.get().slayers.highlightBosses == SlayersConfig.HighlightSlayerEntities.GLOW && SlayerEntitiesGlow.isSlayer(armorStandEntity)) { + SlayerEntitiesGlow.setSlayerMobGlow(armorStandEntity); + } + + if (SkyblockerConfigManager.get().slayers.blazeSlayer.firePillarCountdown != SlayersConfig.BlazeSlayer.FirePillar.OFF) FirePillarAnnouncer.checkFirePillar(entity); + EggFinder.checkIfEgg(armorStandEntity); try { //Prevent packet handling fails if something goes wrong so that entity trackers still update, just without compact damage numbers CompactDamage.compactDamage(armorStandEntity); diff --git a/src/main/java/de/hysky/skyblocker/mixins/WorldRendererMixin.java b/src/main/java/de/hysky/skyblocker/mixins/WorldRendererMixin.java index 2959d4b5..7126cbad 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/WorldRendererMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/WorldRendererMixin.java @@ -1,6 +1,8 @@ package de.hysky.skyblocker.mixins; import de.hysky.skyblocker.skyblock.dungeon.LividColor; +import de.hysky.skyblocker.skyblock.slayers.SlayerEntitiesGlow; +import net.minecraft.entity.decoration.ArmorStandEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -42,7 +44,10 @@ public class WorldRendererMixin { boolean shouldShowBoundingBox = MobBoundingBoxes.shouldDrawMobBoundingBox(entity); if (shouldShowBoundingBox) { - MobBoundingBoxes.submitBox2BeRendered(entity.getBoundingBox(), MobBoundingBoxes.getBoxColor(entity)); + MobBoundingBoxes.submitBox2BeRendered( + entity instanceof ArmorStandEntity e ? SlayerEntitiesGlow.getSlayerMobBoundingBox(e) : entity.getBoundingBox(), + MobBoundingBoxes.getBoxColor(entity) + ); } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/crimson/slayer/AttunementColors.java b/src/main/java/de/hysky/skyblocker/skyblock/crimson/slayer/AttunementColors.java new file mode 100644 index 00000000..ba94812a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/crimson/slayer/AttunementColors.java @@ -0,0 +1,35 @@ +package de.hysky.skyblocker.skyblock.crimson.slayer; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.SlayerUtils; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; + +import java.awt.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class AttunementColors { + private static final Pattern COLOR_PATTERN = Pattern.compile("ASHEN|SPIRIT|CRYSTAL|AURIC"); + + /** + * Fetches highlight colour based on the Inferno Demonlord, or its demons', Hellion Shield Attunement + */ + public static int getColor(LivingEntity e) { + if (!SkyblockerConfigManager.get().slayers.blazeSlayer.attunementHighlights) return 0xf57738; + for (Entity entity : SlayerUtils.getEntityArmorStands(e)) { + Matcher matcher = COLOR_PATTERN.matcher(entity.getDisplayName().getString()); + if (matcher.find()) { + String matchedColour = matcher.group(); + return switch (matchedColour) { + case "ASHEN" -> Color.DARK_GRAY.getRGB(); + case "SPIRIT" -> Color.WHITE.getRGB(); + case "CRYSTAL" -> Color.CYAN.getRGB(); + case "AURIC" -> Color.YELLOW.getRGB(); + default -> Color.RED.getRGB(); + }; + } + } + return Color.RED.getRGB(); + } +}
\ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/crimson/slayer/FirePillarAnnouncer.java b/src/main/java/de/hysky/skyblocker/skyblock/crimson/slayer/FirePillarAnnouncer.java new file mode 100644 index 00000000..d2328096 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/crimson/slayer/FirePillarAnnouncer.java @@ -0,0 +1,61 @@ +package de.hysky.skyblocker.skyblock.crimson.slayer; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.config.configs.SlayersConfig; +import de.hysky.skyblocker.utils.SlayerUtils; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.render.title.Title; +import de.hysky.skyblocker.utils.render.title.TitleContainer; +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.Entity; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.text.MutableText; +import net.minecraft.text.PlainTextContent; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class FirePillarAnnouncer { + + private static final Pattern FIRE_PILLAR_PATTERN = Pattern.compile("(\\d+)s \\d+ hits"); + + /** + * Checks if an entity is the fire pillar when it has been updated (i.e. name change). This triggers twice on + * seven seconds remaining, so it's been reduced down to only announce the last 5 seconds until explosion. + * <p> + * There's also not a great way to detect ownership of the fire pillar, so a crude range calculation is used to try and + * prevent another player's FirePillar appearing on the HUD. + * + * @param entity The updated entity that is checked to be a fire pillar + */ + public static void checkFirePillar(Entity entity) { + if (Utils.isInCrimson() && SlayerUtils.isInSlayer() && entity instanceof ArmorStandEntity) { + + String entityName = entity.getName().getString(); + Matcher matcher = FIRE_PILLAR_PATTERN.matcher(entityName); + + if (matcher.matches()) { + int seconds = Integer.parseInt(matcher.group(1)); + if (seconds > 5) return; + + // There is an edge case where the slayer has entered demon phase and temporarily despawned with + // an active fire pillar in play, So fallback to the player + Entity referenceEntity = SlayerUtils.getSlayerEntity(); + if (!(referenceEntity != null ? referenceEntity : MinecraftClient.getInstance().player).getBlockPos().isWithinDistance(entity.getPos(), 22)) return; + announceFirePillarDetails(entityName); + } + } + } + + private static void announceFirePillarDetails(String entityName) { + Title title = new Title(Text.literal(entityName).formatted(Formatting.BOLD, Formatting.DARK_PURPLE)); + + if (SkyblockerConfigManager.get().slayers.blazeSlayer.firePillarCountdown == SlayersConfig.BlazeSlayer.FirePillar.SOUND_AND_VISUAL) { + RenderHelper.displayInTitleContainerAndPlaySound(title, 15); + } else { + TitleContainer.addTitle(title, 15); + } + } +}
\ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobBoundingBoxes.java b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobBoundingBoxes.java index f005fc06..ad2b9b8c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobBoundingBoxes.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobBoundingBoxes.java @@ -1,7 +1,9 @@ package de.hysky.skyblocker.skyblock.entity; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.config.configs.SlayersConfig; import de.hysky.skyblocker.skyblock.dungeon.LividColor; +import de.hysky.skyblocker.skyblock.slayers.SlayerEntitiesGlow; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.render.FrustumUtils; import de.hysky.skyblocker.utils.render.RenderHelper; @@ -9,6 +11,7 @@ import de.hysky.skyblocker.utils.render.Renderable; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.client.MinecraftClient; import net.minecraft.entity.Entity; import net.minecraft.entity.decoration.ArmorStandEntity; import net.minecraft.entity.player.PlayerEntity; @@ -40,6 +43,17 @@ public class MobBoundingBoxes { }; } + if (SkyblockerConfigManager.get().slayers.highlightMinis == SlayersConfig.HighlightSlayerEntities.HITBOX + && entity instanceof ArmorStandEntity le && SlayerEntitiesGlow.isSlayerMiniMob(le)) { + return true; + } + + if (SkyblockerConfigManager.get().slayers.highlightBosses == SlayersConfig.HighlightSlayerEntities.HITBOX + && entity instanceof ArmorStandEntity le) { + return le.getDisplayName().getString().contains(MinecraftClient.getInstance().getSession().getUsername()) || + entity.getDisplayName().getString().contains("Ⓣ") || entity.getDisplayName().getString().contains("Ⓐ"); + } + return false; } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java index 22474cf8..b0436dd1 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java @@ -2,20 +2,23 @@ package de.hysky.skyblocker.skyblock.entity; import com.google.common.collect.Streams; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.config.configs.SlayersConfig; import de.hysky.skyblocker.skyblock.crimson.dojo.DojoManager; import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra; +import de.hysky.skyblocker.skyblock.crimson.slayer.AttunementColors; import de.hysky.skyblocker.skyblock.dungeon.LividColor; import de.hysky.skyblocker.skyblock.end.TheEnd; +import de.hysky.skyblocker.skyblock.slayers.SlayerEntitiesGlow; import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.SlayerUtils; import de.hysky.skyblocker.utils.Utils; -import de.hysky.skyblocker.utils.render.culling.OcclusionCulling; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.minecraft.client.MinecraftClient; import net.minecraft.entity.Entity; import net.minecraft.entity.decoration.ArmorStandEntity; -import net.minecraft.entity.mob.EndermanEntity; -import net.minecraft.entity.mob.MagmaCubeEntity; -import net.minecraft.entity.mob.ZombieEntity; +import net.minecraft.entity.mob.*; import net.minecraft.entity.passive.BatEntity; +import net.minecraft.entity.passive.WolfEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.util.Formatting; @@ -23,60 +26,103 @@ import net.minecraft.util.math.Box; import net.minecraft.world.World; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; public class MobGlow { + /** * The Nukekubi head texture id is eb07594e2df273921a77c101d0bfdfa1115abed5b9b2029eb496ceba9bdbb4b3. */ public static final String NUKEKUBI_HEAD_TEXTURE = "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWIwNzU5NGUyZGYyNzM5MjFhNzdjMTAxZDBiZmRmYTExMTVhYmVkNWI5YjIwMjllYjQ5NmNlYmE5YmRiYjRiMyJ9fX0="; + private static final long GLOW_CACHE_DURATION = 50; + private static final long PLAYER_CAN_SEE_CACHE_DURATION = 100; + private static final ConcurrentHashMap<Entity, CacheEntry> glowCache = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap<Entity, CacheEntry> canSeeCache = new ConcurrentHashMap<>(); + + public static void init() { + Scheduler.INSTANCE.scheduleCyclic(MobGlow::clearCache, 300 * 20); + } public static boolean shouldMobGlow(Entity entity) { - Box box = entity.getBoundingBox(); - if (OcclusionCulling.getReducedCuller().isVisible(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)) { - String name = entity.getName().getString(); + long currentTime = System.currentTimeMillis(); + CacheEntry cachedGlow = glowCache.get(entity); + if (cachedGlow == null || (currentTime - cachedGlow.timestamp) > GLOW_CACHE_DURATION) { + boolean shouldGlow = computeShouldMobGlow(entity); + glowCache.put(entity, new CacheEntry(shouldGlow, currentTime)); + cachedGlow = glowCache.get(entity); + } - // Dungeons - if (Utils.isInDungeons() && !entity.isInvisible()) { - return switch (entity) { - // Minibosses - case PlayerEntity p when name.equals("Lost Adventurer") || name.equals("Shadow Assassin") || name.equals("Diamond Guy") -> SkyblockerConfigManager.get().dungeons.starredMobGlow; - case PlayerEntity p when entity.getId() == LividColor.getCorrectLividId() -> LividColor.shouldGlow(name); + return cachedGlow.value && playerCanSee(entity, currentTime); + } - // Bats - case BatEntity b -> SkyblockerConfigManager.get().dungeons.starredMobGlow || SkyblockerConfigManager.get().dungeons.starredMobBoundingBoxes; - // Armor Stands - case ArmorStandEntity _armorStand -> false; + /** + * Checks if the player can see the entity. + * Has "True sight" within a certain aura, but since name tags exist I think this is fine... + */ + private static boolean playerCanSee(Entity entity, long currentTime) { - // Regular Mobs - default -> SkyblockerConfigManager.get().dungeons.starredMobGlow && isStarred(entity); - }; - } + CacheEntry canSee = canSeeCache.get(entity); + if (canSee == null || (currentTime - canSee.timestamp) > PLAYER_CAN_SEE_CACHE_DURATION) { + boolean playerCanSee = entity.distanceTo(MinecraftClient.getInstance().player) <= 20 || MinecraftClient.getInstance().player.canSee(entity); + canSeeCache.put(entity, new CacheEntry(playerCanSee, currentTime)); + return playerCanSee; + } - return switch (entity) { - // Rift - case PlayerEntity p when Utils.isInTheRift() && !entity.isInvisible() && name.equals("Blobbercyst ") -> SkyblockerConfigManager.get().otherLocations.rift.blobbercystGlow; + return canSee.value; + } - // Enderman Slayer - // Highlights Nukekubi Heads - case ArmorStandEntity armorStand when Utils.isInTheEnd() && SlayerUtils.isInSlayer() && isNukekubiHead(armorStand) -> SkyblockerConfigManager.get().slayers.endermanSlayer.highlightNukekubiHeads; + private static boolean computeShouldMobGlow(Entity entity) { + String name = entity.getName().getString(); + + // Dungeons + if (Utils.isInDungeons()) { + return switch (entity) { - // Special Zelot - case EndermanEntity enderman when Utils.isInTheEnd() && !entity.isInvisible() -> TheEnd.isSpecialZealot(enderman); + // Minibosses + case PlayerEntity p when name.equals("Lost Adventurer") || name.equals("Shadow Assassin") || name.equals("Diamond Guy") -> SkyblockerConfigManager.get().dungeons.starredMobGlow; + case PlayerEntity p when entity.getId() == LividColor.getCorrectLividId() -> LividColor.shouldGlow(name); - //dojo - case ZombieEntity zombie when Utils.isInCrimson() && DojoManager.inArena -> DojoManager.shouldGlow(getArmorStandName(zombie)); + // Bats + case BatEntity b -> SkyblockerConfigManager.get().dungeons.starredMobGlow || SkyblockerConfigManager.get().dungeons.starredMobBoundingBoxes; - //Kuudra - case MagmaCubeEntity magmaCube when Utils.isInKuudra() -> SkyblockerConfigManager.get().crimsonIsle.kuudra.kuudraGlow && magmaCube.getSize() == Kuudra.KUUDRA_MAGMA_CUBE_SIZE; + // Armor Stands + case ArmorStandEntity _armorStand -> false; - default -> false; + // Regular Mobs + default -> SkyblockerConfigManager.get().dungeons.starredMobGlow && isStarred(entity); }; } - return false; + return switch (entity) { + + // Rift Blobbercyst + case PlayerEntity p when Utils.isInTheRift() && name.equals("Blobbercyst ") -> SkyblockerConfigManager.get().otherLocations.rift.blobbercystGlow; + + // Dojo Helpers + case ZombieEntity zombie when Utils.isInCrimson() && DojoManager.inArena -> DojoManager.shouldGlow(getArmorStandName(zombie)); + + //Kuudra + case MagmaCubeEntity magmaCube when Utils.isInKuudra() -> SkyblockerConfigManager.get().crimsonIsle.kuudra.kuudraGlow && magmaCube.getSize() == Kuudra.KUUDRA_MAGMA_CUBE_SIZE; + + // Special Zealot && Slayer (Mini)Boss + case EndermanEntity enderman when Utils.isInTheEnd() -> TheEnd.isSpecialZealot(enderman) || SlayerEntitiesGlow.shouldGlow(enderman.getUuid()); + case ZombieEntity zombie when !(zombie instanceof ZombifiedPiglinEntity) && SlayerUtils.isInSlayerQuestType(SlayerUtils.REVENANT) -> SlayerEntitiesGlow.shouldGlow(zombie.getUuid()); + case SpiderEntity spider when SlayerUtils.isInSlayerQuestType(SlayerUtils.TARA) -> SlayerEntitiesGlow.shouldGlow(spider.getUuid()); + case WolfEntity wolf when SlayerUtils.isInSlayerQuestType(SlayerUtils.SVEN) -> SlayerEntitiesGlow.shouldGlow(wolf.getUuid()); + case BlazeEntity blaze when SlayerUtils.isInSlayerQuestType(SlayerUtils.DEMONLORD) -> SlayerEntitiesGlow.shouldGlow(blaze.getUuid()); + + // Enderman Slayer's Nukekubi Skulls + case ArmorStandEntity armorStand when Utils.isInTheEnd() && SlayerUtils.isInSlayer() && isNukekubiHead(armorStand) -> SkyblockerConfigManager.get().slayers.endermanSlayer.highlightNukekubiHeads; + + // Blaze Slayer's Demonic minions + case WitherSkeletonEntity e when SkyblockerConfigManager.get().slayers.highlightBosses == SlayersConfig.HighlightSlayerEntities.GLOW -> SlayerUtils.isInSlayerType(SlayerUtils.DEMONLORD) && e.distanceTo(MinecraftClient.getInstance().player) <= 15; + case ZombifiedPiglinEntity e when SkyblockerConfigManager.get().slayers.highlightBosses == SlayersConfig.HighlightSlayerEntities.GLOW -> SlayerUtils.isInSlayerType(SlayerUtils.DEMONLORD) && e.distanceTo(MinecraftClient.getInstance().player) <= 15; + + default -> false; + }; } /** @@ -123,10 +169,16 @@ public class MobGlow { case PlayerEntity p when name.equals("Blobbercyst ") -> Formatting.GREEN.getColorValue(); case EndermanEntity enderman when TheEnd.isSpecialZealot(enderman) -> Formatting.RED.getColorValue(); - case ArmorStandEntity armorStand when isNukekubiHead(armorStand) -> Formatting.GREEN.getColorValue(); + case ArmorStandEntity armorStand when isNukekubiHead(armorStand) -> 0x990099; case ZombieEntity zombie when Utils.isInCrimson() && DojoManager.inArena -> DojoManager.getColor(); case MagmaCubeEntity magmaCube when Utils.isInKuudra() -> 0xf7510f; + // Blaze Slayer Attunement Colours + case ArmorStandEntity armorStand when SlayerUtils.isInSlayerQuestType(SlayerUtils.DEMONLORD) -> AttunementColors.getColor(armorStand); + case BlazeEntity blaze when SlayerUtils.isInSlayer() -> AttunementColors.getColor(blaze); + case ZombifiedPiglinEntity piglin when SlayerUtils.isInSlayer() -> AttunementColors.getColor(piglin); + case WitherSkeletonEntity wSkelly when SlayerUtils.isInSlayer() -> AttunementColors.getColor(wSkelly); + default -> 0xf57738; }; } @@ -137,4 +189,11 @@ public class MobGlow { private static boolean isNukekubiHead(ArmorStandEntity entity) { return Streams.stream(entity.getArmorItems()).map(ItemUtils::getHeadTexture).anyMatch(headTexture -> headTexture.contains(NUKEKUBI_HEAD_TEXTURE)); } -} + + private record CacheEntry(boolean value, long timestamp) {} + + private static void clearCache() { + canSeeCache.clear(); + glowCache.clear(); + } +}
\ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/slayers/SlayerEntitiesGlow.java b/src/main/java/de/hysky/skyblocker/skyblock/slayers/SlayerEntitiesGlow.java new file mode 100644 index 00000000..79f91bfb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/slayers/SlayerEntitiesGlow.java @@ -0,0 +1,122 @@ +package de.hysky.skyblocker.skyblock.slayers; + +import de.hysky.skyblocker.utils.SlayerUtils; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.entity.mob.*; +import net.minecraft.entity.passive.WolfEntity; +import net.minecraft.util.math.Box; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class SlayerEntitiesGlow { + private static final Map<String, String> SLAYER_MINI_NAMES = Map.ofEntries( + Map.entry("Revenant Sycophant", SlayerUtils.REVENANT), + Map.entry("Revenant Champion", SlayerUtils.REVENANT), + Map.entry("Deformed Revenant", SlayerUtils.REVENANT), + Map.entry("Atoned Champion", SlayerUtils.REVENANT), + Map.entry("Atoned Revenant", SlayerUtils.REVENANT), + Map.entry("Tarantula Vermin", SlayerUtils.TARA), + Map.entry("Tarantula Beast", SlayerUtils.TARA), + Map.entry("Mutant Tarantula", SlayerUtils.TARA), + Map.entry("Pack Enforcer", SlayerUtils.SVEN), + Map.entry("Sven Follower", SlayerUtils.SVEN), + Map.entry("Sven Alpha", SlayerUtils.SVEN), + Map.entry("Voidling Devotee", SlayerUtils.VOIDGLOOM), + Map.entry("Voidling Radical", SlayerUtils.VOIDGLOOM), + Map.entry("Voidcrazed Maniac", SlayerUtils.VOIDGLOOM), + Map.entry("Flare Demon", SlayerUtils.DEMONLORD), + Map.entry("Kindleheart Demon", SlayerUtils.DEMONLORD), + Map.entry("Burningsoul Demon", SlayerUtils.DEMONLORD) + ); + + public static void init() { + ClientPlayConnectionEvents.JOIN.register((ignore, ignore2, ignore3) -> clearGlow()); + } + private static final Map<String, Class<? extends MobEntity>> SLAYER_MOB_TYPE = Map.of( + SlayerUtils.REVENANT, ZombieEntity.class, + SlayerUtils.TARA, SpiderEntity.class, + SlayerUtils.SVEN, WolfEntity.class, + SlayerUtils.VOIDGLOOM, EndermanEntity.class, + SlayerUtils.DEMONLORD, BlazeEntity.class + ); + + private static final Set<UUID> MOBS_TO_GLOW = new HashSet<>(); + + public static boolean shouldGlow(UUID entityUUID) { + return MOBS_TO_GLOW.contains(entityUUID); + } + + public static boolean isSlayer(LivingEntity e) { + return SlayerUtils.isInSlayer() && SlayerUtils.getEntityArmorStands(e).stream().anyMatch(entity -> + entity.getDisplayName().getString().contains(MinecraftClient.getInstance().getSession().getUsername())); + } + + public static boolean isSlayerMiniMob(LivingEntity entity) { + if (entity.getCustomName() == null) return false; + String entityName = entity.getCustomName().getString(); + return SLAYER_MINI_NAMES.keySet().stream().anyMatch(slayerMobName -> entityName.contains(slayerMobName) && SlayerUtils.isInSlayerQuestType(SLAYER_MINI_NAMES.get(slayerMobName))); + } + + public static Box getSlayerMobBoundingBox(LivingEntity entity) { + return switch (SlayerUtils.getSlayerType()) { + case SlayerUtils.REVENANT -> new Box(entity.getX() - 0.4, entity.getY() - 0.1, entity.getZ() - 0.4, entity.getX() + 0.4, entity.getY() - 2.2, entity.getZ() + 0.4); + case SlayerUtils.TARA -> new Box(entity.getX() - 0.9, entity.getY() - 0.2, entity.getZ() - 0.9, entity.getX() + 0.9, entity.getY() - 1.2, entity.getZ() + 0.9); + case SlayerUtils.VOIDGLOOM -> new Box(entity.getX() - 0.4, entity.getY() - 0.2, entity.getZ() - 0.4, entity.getX() + 0.4, entity.getY() - 3, entity.getZ() + 0.4); |
