diff --git a/plugin-modules/Core/resources-json/bosses.json b/plugin-modules/Core/resources-json/bosses.json index 872c582..55580fc 100644 --- a/plugin-modules/Core/resources-json/bosses.json +++ b/plugin-modules/Core/resources-json/bosses.json @@ -5,6 +5,10 @@ "editing": true, "buyable": true, "price": 500000.0, + "healthBar": { + "radius": 50, + "text": "&6&lSkeleton King &8&m &r &a%currentHealth%&7/&2%maxHealth%" + }, "entityStats": [ { "mainStats": { diff --git a/plugin-modules/Core/src/com/songoda/epicbosses/EpicBosses.java b/plugin-modules/Core/src/com/songoda/epicbosses/EpicBosses.java index 3d46155..22968e8 100644 --- a/plugin-modules/Core/src/com/songoda/epicbosses/EpicBosses.java +++ b/plugin-modules/Core/src/com/songoda/epicbosses/EpicBosses.java @@ -58,6 +58,7 @@ public class EpicBosses extends SongodaPlugin implements IReloadable { private BossSkillManager bossSkillManager; private BossTauntManager bossTauntManager; private BossHookManager bossHookManager; + private BossHealthBarManager bossHealthBarManager; private AutoSpawnManager autoSpawnManager; @@ -119,6 +120,7 @@ public class EpicBosses extends SongodaPlugin implements IReloadable { this.bossSkillManager = new BossSkillManager(this); this.bossHookManager = new BossHookManager(this); this.bossTauntManager = new BossTauntManager(this); + this.bossHealthBarManager = new BossHealthBarManager(this); this.bossTargetManager = new BossTargetManager(this); this.bossEntityContainer = new BossEntityContainer(); this.minionEntityContainer = new MinionEntityContainer(); @@ -317,6 +319,10 @@ public class EpicBosses extends SongodaPlugin implements IReloadable { return this.bossEntityContainer; } + public BossHealthBarManager getBossHealthBarManager() { + return bossHealthBarManager; + } + public BossMechanicManager getBossMechanicManager() { return this.bossMechanicManager; } diff --git a/plugin-modules/Core/src/com/songoda/epicbosses/entity/BossEntity.java b/plugin-modules/Core/src/com/songoda/epicbosses/entity/BossEntity.java index 54fd6be..e70f786 100644 --- a/plugin-modules/Core/src/com/songoda/epicbosses/entity/BossEntity.java +++ b/plugin-modules/Core/src/com/songoda/epicbosses/entity/BossEntity.java @@ -24,19 +24,22 @@ public class BossEntity { @Expose private final DropsElement drops; @Expose + private final HealthBarElement healthBar; + @Expose private String spawnItem, targeting; @Expose private boolean editing, buyable; @Expose private Double price; - public BossEntity(boolean editing, String spawnItem, String targeting, boolean buyable, Double price, List entityStats, SkillsElement skills, DropsElement drops, MessagesElement messages, CommandsElement commands) { + public BossEntity(boolean editing, String spawnItem, String targeting, boolean buyable, Double price, List entityStats, SkillsElement skills, DropsElement drops, MessagesElement messages, CommandsElement commands, HealthBarElement healthBar) { this.editing = editing; this.entityStats = entityStats; this.targeting = targeting; this.spawnItem = spawnItem; this.skills = skills; this.drops = drops; + this.healthBar = healthBar; this.messages = messages; this.commands = commands; this.buyable = buyable; @@ -164,4 +167,8 @@ public class BossEntity { public DropsElement getDrops() { return this.drops; } + + public HealthBarElement getHealthBar() { + return healthBar; + } } diff --git a/plugin-modules/Core/src/com/songoda/epicbosses/entity/elements/HealthBarElement.java b/plugin-modules/Core/src/com/songoda/epicbosses/entity/elements/HealthBarElement.java new file mode 100644 index 0000000..8c59a3c --- /dev/null +++ b/plugin-modules/Core/src/com/songoda/epicbosses/entity/elements/HealthBarElement.java @@ -0,0 +1,33 @@ +package com.songoda.epicbosses.entity.elements; + +import com.google.gson.annotations.Expose; + +public class HealthBarElement { + + @Expose + private String text; + + @Expose + private Integer radius; + + public HealthBarElement(String text, Integer radius) { + this.text = text; + this.radius = radius; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Integer getRadius() { + return radius; + } + + public void setRadius(Integer radius) { + this.radius = radius; + } +} \ No newline at end of file diff --git a/plugin-modules/Core/src/com/songoda/epicbosses/listeners/pre/BossSpawnListener.java b/plugin-modules/Core/src/com/songoda/epicbosses/listeners/pre/BossSpawnListener.java index 3b5c0ea..400e426 100644 --- a/plugin-modules/Core/src/com/songoda/epicbosses/listeners/pre/BossSpawnListener.java +++ b/plugin-modules/Core/src/com/songoda/epicbosses/listeners/pre/BossSpawnListener.java @@ -8,6 +8,7 @@ import com.songoda.epicbosses.events.PreBossSpawnEvent; import com.songoda.epicbosses.events.PreBossSpawnItemEvent; import com.songoda.epicbosses.holder.ActiveBossHolder; import com.songoda.epicbosses.managers.BossEntityManager; +import com.songoda.epicbosses.managers.BossHealthBarManager; import com.songoda.epicbosses.managers.BossLocationManager; import com.songoda.epicbosses.managers.BossTauntManager; import com.songoda.epicbosses.utils.*; @@ -38,11 +39,13 @@ public class BossSpawnListener implements Listener { private BossLocationManager bossLocationManager; private BossEntityManager bossEntityManager; private BossTauntManager bossTauntManager; + private BossHealthBarManager bossHealthBarManager; public BossSpawnListener(EpicBosses epicBosses) { this.bossTauntManager = epicBosses.getBossTauntManager(); this.bossEntityManager = epicBosses.getBossEntityManager(); this.bossLocationManager = epicBosses.getBossLocationManager(); + this.bossHealthBarManager = epicBosses.getBossHealthBarManager(); } @EventHandler @@ -120,6 +123,7 @@ public class BossSpawnListener implements Listener { List commands = new ArrayList(this.bossEntityManager.getOnSpawnCommands(bossEntity)); List messages = new ArrayList(this.bossEntityManager.getOnSpawnMessage(bossEntity)); int messageRadius = this.bossEntityManager.getOnSpawnMessageRadius(bossEntity); + ServerUtils serverUtils = ServerUtils.get(); if (event instanceof PreBossSpawnItemEvent) { @@ -155,6 +159,9 @@ public class BossSpawnListener implements Listener { MessageUtils.get().sendMessage(location, NumberUtils.get().getSquared(messageRadius), messages); } + // Health bar in action bar ;) + this.bossHealthBarManager.handleHealthBar(activeBossHolder); + activeBossHolder.getTargetHandler().runTargetCycle(); this.bossTauntManager.handleTauntSystem(activeBossHolder); @@ -162,5 +169,4 @@ public class BossSpawnListener implements Listener { ServerUtils.get().callEvent(bossSpawnEvent); } - -} +} \ No newline at end of file diff --git a/plugin-modules/Core/src/com/songoda/epicbosses/managers/BossHealthBarManager.java b/plugin-modules/Core/src/com/songoda/epicbosses/managers/BossHealthBarManager.java new file mode 100644 index 0000000..d08c153 --- /dev/null +++ b/plugin-modules/Core/src/com/songoda/epicbosses/managers/BossHealthBarManager.java @@ -0,0 +1,57 @@ +package com.songoda.epicbosses.managers; + +import com.songoda.core.compatibility.ServerVersion; +import com.songoda.epicbosses.EpicBosses; +import com.songoda.epicbosses.entity.elements.HealthBarElement; +import com.songoda.epicbosses.holder.ActiveBossHolder; +import com.songoda.epicbosses.utils.MessageUtils; +import com.songoda.epicbosses.utils.NumberUtils; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.LivingEntity; +import org.bukkit.scheduler.BukkitRunnable; + +public class BossHealthBarManager { + + private final EpicBosses epicBosses; + + public BossHealthBarManager(EpicBosses epicBosses) { + this.epicBosses = epicBosses; + } + + public void handleHealthBar(ActiveBossHolder activeBossHolder) { + if (activeBossHolder.getBossEntity().getHealthBar() == null || activeBossHolder.getBossEntity().getHealthBar().getText() == null) + return; + + createNewRunnable(activeBossHolder); + } + + public void createNewRunnable(ActiveBossHolder activeBossHolder) { + HealthBarElement healthBarElement = activeBossHolder.getBossEntity().getHealthBar(); + + new BukkitRunnable() { + @Override + public void run() { + if (activeBossHolder.isDead()) { + cancel(); + return; + } + + if (activeBossHolder.getLivingEntity() == null) return; + + // Parse placeholders + String formattedText = healthBarElement.getText().replaceAll("(?i)%currenthealth%", String.valueOf(NumberUtils.get().formatDouble(activeBossHolder.getLivingEntity().getHealth()))) + .replaceAll("(?i)%maxhealth%", String.valueOf(NumberUtils.get().formatDouble(getMaxHealth(activeBossHolder.getLivingEntity())))); + + MessageUtils.get().sendActionBar(activeBossHolder.getLocation(), NumberUtils.get().getSquared(healthBarElement.getRadius()), formattedText); + } + }.runTaskTimer(epicBosses, 10, 20); + } + + private double getMaxHealth(LivingEntity entity) { + if (ServerVersion.isServerVersionAbove(ServerVersion.V1_8)) { + return entity.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue(); + } else { + return entity.getMaxHealth(); + } + } +} \ No newline at end of file diff --git a/plugin-modules/Core/src/com/songoda/epicbosses/utils/MessageUtils.java b/plugin-modules/Core/src/com/songoda/epicbosses/utils/MessageUtils.java index 36b5b06..6cfa997 100644 --- a/plugin-modules/Core/src/com/songoda/epicbosses/utils/MessageUtils.java +++ b/plugin-modules/Core/src/com/songoda/epicbosses/utils/MessageUtils.java @@ -1,9 +1,16 @@ package com.songoda.epicbosses.utils; +import com.songoda.core.compatibility.ServerVersion; +import com.songoda.core.utils.NMSUtils; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; @@ -42,10 +49,52 @@ public class MessageUtils { } else { Bukkit.getOnlinePlayers().forEach(player -> { if ((player.getWorld().equals(center.getWorld())) && (player.getLocation().distanceSquared(center) <= radius)) { - messages.forEach(string -> player.sendMessage(string)); + messages.forEach(player::sendMessage); } }); } } -} + public void sendActionBar(Location center, int radius, String message) { + if (radius == -1) { + if (center.getWorld() == null) return; + center.getWorld().getPlayers().forEach(p -> sendActionBar(p, message)); + } else { + Bukkit.getOnlinePlayers().forEach(player -> { + if ((player.getWorld().equals(center.getWorld())) && (player.getLocation().distanceSquared(center) <= radius)) { + sendActionBar(player, message); + } + }); + } + } + + private Class clazzPacketPlayOutChat, clazzChatSerializer, clazzIChatBaseComponent; + private Method methodA; + + public void sendActionBar(Player player, String message) { + message = ChatColor.translateAlternateColorCodes('&', message); + + if (ServerVersion.isServerVersionAbove(ServerVersion.V1_8)) { + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(message)); + } else { + try { + // Cache nms + if (clazzPacketPlayOutChat == null) { + clazzPacketPlayOutChat = NMSUtils.getNMSClass("PacketPlayOutChat"); + + clazzChatSerializer = NMSUtils.getNMSClass("IChatBaseComponent$ChatSerializer"); + clazzIChatBaseComponent = NMSUtils.getNMSClass("IChatBaseComponent"); + + methodA = clazzChatSerializer.getDeclaredMethod("a", String.class); + } + + Object chatBaseComponent = clazzIChatBaseComponent.cast(methodA.invoke(clazzChatSerializer, "{\"text\": \"" + message + "\"}")); + Object packetPlayOutChat = clazzPacketPlayOutChat.getConstructor(new Class[]{clazzIChatBaseComponent, byte.class}).newInstance(chatBaseComponent, (byte) 2); + + NMSUtils.sendPacket(player, packetPlayOutChat); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file