Skip to content

Commit

Permalink
[Slayers] Adds Bossbars
Browse files Browse the repository at this point in the history
@
  • Loading branch information
BigloBot committed Sep 7, 2024
1 parent 04afe1a commit f7dd132
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig
newValue -> config.slayers.highlightBosses = newValue)
.controller(ConfigUtils::createEnumCyclingListController)
.build())
.option(Option.<Boolean>createBuilder()
.name(Text.translatable("skyblocker.config.slayer.bossbar"))
.description(OptionDescription.of(
Text.translatable("skyblocker.config.slayer.bossbar.@Tooltip")))
.binding(defaults.slayers.displayBossbar,
() -> config.slayers.displayBossbar,
newValue -> config.slayers.displayBossbar = newValue)
.controller(ConfigUtils::createBooleanController)
.build())

//Enderman Slayer
.group(OptionGroup.createBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public class SlayersConfig {
@SerialEntry
public HighlightSlayerEntities highlightBosses = HighlightSlayerEntities.OFF;

@SerialEntry
public boolean displayBossbar = false;

public enum HighlightSlayerEntities {
OFF, GLOW, HITBOX;

Expand Down
43 changes: 43 additions & 0 deletions src/main/java/de/hysky/skyblocker/mixins/BossBarHudMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package de.hysky.skyblocker.mixins;

import de.hysky.skyblocker.config.SkyblockerConfig;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.skyblock.slayers.SlayerBossBars;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.BossBarHud;
import net.minecraft.client.gui.hud.ClientBossBar;
import net.minecraft.entity.boss.BossBar;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(BossBarHud.class)
public abstract class BossBarHudMixin {

@Final
@Shadow
private MinecraftClient client;

@Shadow
protected abstract void renderBossBar(DrawContext context, int x, int y, BossBar bossBar);

@Inject(method = "render", at = @At("HEAD"), cancellable = true)
private void onRender(DrawContext context, CallbackInfo ci) {

if (SkyblockerConfigManager.get().slayers.displayBossbar && SlayerBossBars.shouldRenderBossBar()) {
ClientBossBar bar = SlayerBossBars.getBossBar();

int textWidth = this.client.textRenderer.getWidth(bar.getName());
context.drawTextWithShadow(this.client.textRenderer, bar.getName(), context.getScaledWindowWidth() / 2 - textWidth / 2, 3, 16777215);

this.renderBossBar(context, (context.getScaledWindowWidth() / 2) - 91, 12, bar);

ci.cancel();
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class AttunementColors {
*/
public static int getColor(LivingEntity e) {
if (!SkyblockerConfigManager.get().slayers.blazeSlayer.attunementHighlights) return 0xf57738;
for (Entity entity : SlayerUtils.getEntityArmorStands(e)) {
for (Entity entity : SlayerUtils.getEntityArmorStands(e, 2.5f)) {
Matcher matcher = COLOR_PATTERN.matcher(entity.getDisplayName().getString());
if (matcher.find()) {
String matchedColour = matcher.group();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
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;
Expand Down Expand Up @@ -42,7 +40,7 @@ public static void checkFirePillar(Entity entity) {

// 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();
Entity referenceEntity = SlayerUtils.getSlayerArmorstandEntity();
if (!(referenceEntity != null ? referenceEntity : MinecraftClient.getInstance().player).getBlockPos().isWithinDistance(entity.getPos(), 22)) return;
announceFirePillarDetails(entityName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ protected static void updateMania() {
return;
}

Entity slayerEntity = SlayerUtils.getSlayerEntity();
Entity slayerEntity = SlayerUtils.getSlayerArmorstandEntity();
if (slayerEntity == null) return;

boolean anyMania = false;
for (Entity entity : SlayerUtils.getEntityArmorStands(slayerEntity)) {
for (Entity entity : SlayerUtils.getEntityArmorStands(slayerEntity, 2.5f)) {
if (entity.getDisplayName().toString().contains("MANIA")) {
anyMania = true;
BlockPos pos = MinecraftClient.getInstance().player.getBlockPos().down();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected static void updateStake() {
TitleContainer.removeTitle(title);
return;
}
Entity slayerEntity = SlayerUtils.getSlayerEntity();
Entity slayerEntity = SlayerUtils.getSlayerArmorstandEntity();
if (slayerEntity != null && slayerEntity.getDisplayName().toString().contains("҉")) {
RenderHelper.displayInTitleContainerAndPlaySound(title);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ protected static void updateIce() {
return;
}

Entity slayerEntity = SlayerUtils.getSlayerEntity();
Entity slayerEntity = SlayerUtils.getSlayerArmorstandEntity();
if (slayerEntity == null) return;

boolean anyClaws = false;
for (Entity entity : SlayerUtils.getEntityArmorStands(slayerEntity)) {
for (Entity entity : SlayerUtils.getEntityArmorStands(slayerEntity, 2.5f)) {
if (entity.getDisplayName().toString().contains("TWINCLAWS")) {
anyClaws = true;
if (!TitleContainer.containsTitle(title) && !scheduled) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package de.hysky.skyblocker.skyblock.slayers;

import de.hysky.skyblocker.utils.SlayerUtils;
import net.minecraft.client.gui.hud.ClientBossBar;
import net.minecraft.entity.boss.BossBar;
import net.minecraft.entity.decoration.ArmorStandEntity;
import net.minecraft.text.Text;

import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SlayerBossBars {

private static final Pattern HEALTH_PATTERN = Pattern.compile("(\\d+(?:\\.\\d+)?[kM]?)(?=❤)");
private static int bossMaxHealth = -1;
private static long lastUpdateTime = 0;
private static final long UPDATE_INTERVAL = 400;
private static ClientBossBar bossBar;
public static final UUID uuid = UUID.randomUUID();

public static boolean shouldRenderBossBar() {
long currentTime = System.currentTimeMillis();
if (currentTime - lastUpdateTime < UPDATE_INTERVAL) {
return bossBar != null;
}

lastUpdateTime = currentTime;

if (!SlayerUtils.isInSlayer()) {
bossMaxHealth = -1;
bossBar = null;
return false;
}

if (SlayerUtils.getSlayerArmorstandEntity() != null && bossMaxHealth == -1) {
Matcher maxHealthMatcher = HEALTH_PATTERN.matcher(SlayerUtils.getSlayerArmorstandEntity().getDisplayName().getString());
if (maxHealthMatcher.find()) bossMaxHealth = convertToInt(maxHealthMatcher.group(0));
}

return bossBar != null || SlayerUtils.getSlayerArmorstandEntity() != null;
}

public static ClientBossBar getBossBar() {
ArmorStandEntity slayer = SlayerUtils.getSlayerArmorstandEntity();
if (bossBar == null) bossBar = new ClientBossBar(uuid, slayer != null ? slayer.getDisplayName() : Text.of("Attempting to Locate Slayer..."), 1f, BossBar.Color.PURPLE, BossBar.Style.PROGRESS, false, false, false);

if (slayer == null) {
if (SlayerUtils.isInSlayer()){
bossBar.setStyle(BossBar.Style.PROGRESS);
bossBar.setColor(BossBar.Color.RED);
}
return bossBar;
}

Matcher healthMatcher = HEALTH_PATTERN.matcher(slayer.getCustomName().getString());
if (healthMatcher.find() && slayer.isAlive()) {
bossBar.setPercent(bossMaxHealth == -1 ? 1f : (float) convertToInt(healthMatcher.group(1)) / bossMaxHealth);
bossBar.setColor(BossBar.Color.PINK);
bossBar.setName(slayer.getDisplayName());
bossBar.setStyle(BossBar.Style.NOTCHED_10);
} else {
bossBar.setColor(BossBar.Color.RED);
bossBar.setStyle(BossBar.Style.PROGRESS);
bossBar.setName(slayer.getDisplayName());
}

return bossBar;
}

private static int convertToInt(String value) {
if (value == null || value.isEmpty()) {
return 0;
}

value = value.trim().toLowerCase();
double multiplier = 1.0;

if (value.endsWith("m")) {
multiplier = 1_000_000;
value = value.substring(0, value.length() - 1);
} else if (value.endsWith("k")) {
multiplier = 1_000;
value = value.substring(0, value.length() - 1);
}

try {
double numericValue = Double.parseDouble(value);
return (int) (numericValue * multiplier);
} catch (NumberFormatException e) {
return 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public static boolean shouldGlow(UUID entityUUID) {
}

public static boolean isSlayer(LivingEntity e) {
return SlayerUtils.isInSlayer() && SlayerUtils.getEntityArmorStands(e).stream().anyMatch(entity ->
return SlayerUtils.isInSlayer() && SlayerUtils.getEntityArmorStands(e, 2.5f).stream().anyMatch(entity ->
entity.getDisplayName().getString().contains(MinecraftClient.getInstance().getSession().getUsername()));
}

Expand Down
27 changes: 19 additions & 8 deletions src/main/java/de/hysky/skyblocker/utils/SlayerUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,56 @@

//TODO Slayer Packet system that can provide information about the current slayer boss, abstract so that different bosses can have different info
public class SlayerUtils {
private static ArmorStandEntity slayerArmorstandEntity;
public static final String REVENANT = "Revenant Horror";
public static final String TARA = "Tarantula Broodfather";
public static final String SVEN = "Sven Packmaster";
public static final String VOIDGLOOM = "Voidgloom Seraph";
public static final String VAMPIRE = "Riftstalker Bloodfiend";
public static final String DEMONLORD = "Inferno Demonlord";
private static final Logger LOGGER = LoggerFactory.getLogger(SlayerUtils.class);
private static final Pattern SLAYER_PATTERN = Pattern.compile("Revenant Horror|Tarantula Broodfather|Sven Packmaster|Voidgloom Seraph|Inferno Demonlord|Riftstalker Bloodfiend");
public static final Pattern SLAYER_PATTERN = Pattern.compile("Revenant Horror|Tarantula Broodfather|Sven Packmaster|Voidgloom Seraph|Inferno Demonlord|Riftstalker Bloodfiend");

//TODO: Cache this, probably included in Packet system
public static List<Entity> getEntityArmorStands(Entity entity) {
return entity.getEntityWorld().getOtherEntities(entity, entity.getBoundingBox().expand(0.3F, 2.5F, 0.3F), x -> x instanceof ArmorStandEntity && x.hasCustomName());
public static List<Entity> getEntityArmorStands(Entity entity, float scanHeight) {
return entity.getEntityWorld().getOtherEntities(entity, entity.getBoundingBox().expand(0.3F, scanHeight, 0.3F), x -> x instanceof ArmorStandEntity && x.hasCustomName());
}

//Eventually this should be modified so that if you hit a slayer boss all slayer features will work on that boss.
public static Entity getSlayerEntity() {
public static ArmorStandEntity getSlayerArmorstandEntity() {
// TODO: This should be set when the system to detect isInSlayer is made event-driven
if (slayerArmorstandEntity != null && slayerArmorstandEntity.isAlive()) {
return slayerArmorstandEntity;
}

if (MinecraftClient.getInstance().world != null) {
for (Entity entity : MinecraftClient.getInstance().world.getEntities()) {
if (entity.hasCustomName()) {
String entityName = entity.getCustomName().getString();
Matcher matcher = SLAYER_PATTERN.matcher(entityName);
if (matcher.find()) {
String username = MinecraftClient.getInstance().getSession().getUsername();
for (Entity armorStand : getEntityArmorStands(entity)) {
for (Entity armorStand : getEntityArmorStands(entity, 1.5f)) {
if (armorStand.getDisplayName().getString().contains(username)) {
return entity;
slayerArmorstandEntity = (ArmorStandEntity) entity;
return slayerArmorstandEntity;
}
}
}
}
}
}

slayerArmorstandEntity = null;
return null;
}

public static boolean isInSlayer() {
try {
for (String line : Utils.STRING_SCOREBOARD) {
if (line.contains("Slay the boss!")) return true;
if (line.contains("Slay the boss!")) {
return true;
}
}
} catch (NullPointerException e) {
LOGGER.error("[Skyblocker] Error while checking if player is in slayer", e);
Expand Down Expand Up @@ -102,4 +113,4 @@ public static String getSlayerType() {
}
return "";
}
}
}
3 changes: 3 additions & 0 deletions src/main/resources/assets/skyblocker/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,9 @@
"skyblocker.config.slayer.blazeSlayer.attunementHighlights" : "Attunement Highlights",
"skyblocker.config.slayer.blazeSlayer.attunementHighlights.@Tooltip": "If Boss Highlighting is on, applies a color matching the current attunement to the selected effect.",

"skyblocker.config.slayer.bossbar": "Slayer Bossbar",
"skyblocker.config.slayer.bossbar.@Tooltip": "Displays a bossbar for your active slayer boss",

"skyblocker.config.slayer.endermanSlayer": "Enderman Slayer",
"skyblocker.config.slayer.endermanSlayer.enableYangGlyphsNotification": "Enable Yang Glyphs notification",
"skyblocker.config.slayer.endermanSlayer.highlightBeacons": "Beacon Highlighting",
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/skyblocker.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"AbstractInventoryScreenMixin",
"BackgroundRendererMixin",
"BatEntityMixin",
"BossBarHudMixin",
"ClientPlayerEntityMixin",
"ClientPlayNetworkHandlerMixin",
"ClientWorldMixin",
Expand Down

0 comments on commit f7dd132

Please sign in to comment.