/* * This file is part of HoloAPI. * * HoloAPI is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HoloAPI is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HoloAPI. If not, see <http://www.gnu.org/licenses/>. */ package com.dsh105.holoapi.listeners; import com.dsh105.commodus.RomanNumeral; import com.dsh105.commodus.StringUtil; import com.dsh105.holoapi.HoloAPI; import com.dsh105.holoapi.api.Hologram; import com.dsh105.holoapi.config.Settings; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityRegainHealthEvent; import org.bukkit.event.entity.PotionSplashEvent; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerExpChangeEvent; import org.bukkit.event.player.PlayerItemConsumeEvent; import org.bukkit.potion.Potion; import org.bukkit.potion.PotionEffect; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; import java.text.DecimalFormat; import java.util.*; public class IndicatorListener implements Listener { private static final char HEART_CHARACTER = '\u2764'; private static final DecimalFormat DAMAGE_FORMAT = new DecimalFormat("#.0"); private static final HashMap<String, ArrayList<String>> CHAT_BUBBLES = new HashMap<>(); private static final List<EntityDamageEvent.DamageCause> SUPPORTED_DAMAGE_TYPES = Arrays.asList(EntityDamageEvent.DamageCause.DROWNING, EntityDamageEvent.DamageCause.LAVA, EntityDamageEvent.DamageCause.MAGIC, EntityDamageEvent.DamageCause.POISON, EntityDamageEvent.DamageCause.THORNS, EntityDamageEvent.DamageCause.WITHER); @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onDamage(EntityDamageEvent event) { // Check if damage indicators are enabled if (!Settings.INDICATOR_ENABLE.getValue("damage")) { return; } // Don't show damage indicators for void damage if (event.getCause() == EntityDamageEvent.DamageCause.VOID) { return; } // Make sure that indicators are enabled for this entity type if (event.getEntity().getType() == EntityType.PLAYER) { if (!Settings.INDICATOR_SHOW_FOR_PLAYERS.getValue("damage")) { return; } } else if (event.getEntity() instanceof LivingEntity) { if (!Settings.INDICATOR_SHOW_FOR_MOBS.getValue("damage")) { return; } } else { return; // We only show indicators for players and mobs. } final LivingEntity entity = (LivingEntity) event.getEntity(); if (entity.getNoDamageTicks() > entity.getMaximumNoDamageTicks() / 2.0F) { return; } String damagePrefix = Settings.INDICATOR_FORMAT.getValue("damage", "default"); // Get our DamageCause-specific damagePrefix, if any if (SUPPORTED_DAMAGE_TYPES.contains(event.getCause())) { String type = event.getCause().toString().toLowerCase(); if (event.getCause() == EntityDamageEvent.DamageCause.LAVA) { type = "fire"; } damagePrefix = Settings.INDICATOR_FORMAT.getValue("damage", type); if (!Settings.INDICATOR_ENABLE_TYPE.getValue("damage", type)) { return; // This type of indicator is disabled } } // Build the message prefix and suffix (i.e. the portions without the damage) final String indicatorPrefix = damagePrefix + "-"; final String indicatorSuffix = " " + HEART_CHARACTER; final double healthBefore = entity.getHealth(); Bukkit.getScheduler().runTask(HoloAPI.getCore(), new Runnable() { @Override public void run() { double damageTaken = healthBefore - entity.getHealth(); if (damageTaken > 0) { // Round to the nearest .5 damageTaken = Math.round(damageTaken * 2.0D) / 2.0D; String text = indicatorPrefix + DAMAGE_FORMAT.format(damageTaken) + indicatorSuffix; Location loc = entity.getLocation(); loc.setY(loc.getY() + Settings.INDICATOR_Y_OFFSET.getValue("damage")); HoloAPI.getManager().createSimpleHologram(loc, Settings.INDICATOR_TIME_VISIBLE.getValue("damage"), true, text); } } }); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onExpGain(PlayerExpChangeEvent event) { if (Settings.INDICATOR_ENABLE.getValue("exp")) { Location l = event.getPlayer().getLocation().clone(); l.setY(l.getY() + Settings.INDICATOR_Y_OFFSET.getValue("exp")); HoloAPI.getManager().createSimpleHologram(l, Settings.INDICATOR_TIME_VISIBLE.getValue("exp"), true, Settings.INDICATOR_FORMAT.getValue("exp", "default") + "+" + event.getAmount() + " exp"); } } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onEntityRegainHealth(EntityRegainHealthEvent event) { if (Settings.INDICATOR_ENABLE.getValue("gainHealth")) { if ((event.getEntity() instanceof Player && Settings.INDICATOR_SHOW_FOR_PLAYERS.getValue("gainHealth")) || Settings.INDICATOR_SHOW_FOR_MOBS.getValue("gainHealth")) { Location l = event.getEntity().getLocation().clone(); l.setY(l.getY() + Settings.INDICATOR_Y_OFFSET.getValue("gainHealth")); HoloAPI.getManager().createSimpleHologram(l, Settings.INDICATOR_TIME_VISIBLE.getValue("gainHealth"), true, Settings.INDICATOR_FORMAT.getValue("gainHealth", "default") + "+" + event.getAmount() + " " + HEART_CHARACTER); } } } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onConsumePotion(PlayerItemConsumeEvent event) { if (Settings.INDICATOR_ENABLE.getValue("potion")) { if (event.getItem().getType() == Material.POTION) { Potion potion = Potion.fromItemStack(event.getItem()); if (potion != null) { this.showPotionHologram(event.getPlayer(), potion.getEffects()); } } else if (event.getItem().getType() == Material.GOLDEN_APPLE) { String msg = Settings.INDICATOR_FORMAT.getValue("potion", "goldenapple"); if (event.getItem().getDurability() == 1) { msg = Settings.INDICATOR_FORMAT.getValue("potion", "godapple"); } Location l = event.getPlayer().getLocation().clone(); l.setY(l.getY() + Settings.INDICATOR_Y_OFFSET.getValue("potion")); HoloAPI.getManager().createSimpleHologram(l, Settings.INDICATOR_TIME_VISIBLE.getValue("potion"), true, msg.replace("%effect%", "Golden Apple")); } } } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onSplashPotion(PotionSplashEvent event) { if (Settings.INDICATOR_ENABLE.getValue("potion")) { for (Entity e : event.getAffectedEntities()) { if ((event.getEntity() instanceof Player && Settings.INDICATOR_SHOW_FOR_PLAYERS.getValue("potion")) || Settings.INDICATOR_SHOW_FOR_MOBS.getValue("potion")) { this.showPotionHologram(e, event.getPotion().getEffects()); } } } } private void showPotionHologram(Entity e, Collection<PotionEffect> effects) { for (PotionEffect effect : effects) { int amp = (effect.getAmplifier() < 1 ? 1 : effect.getAmplifier()) + 1; String content = Settings.INDICATOR_FORMAT.getValue("potion", effect.getType().getName().toLowerCase()); content = content.replace("%effect%", StringUtil.capitalise(effect.getType().getName().replace("_", " "))).replace("%amp%", "" + new RomanNumeral(amp)); Location l = e.getLocation().clone(); l.setY(l.getY() + Settings.INDICATOR_Y_OFFSET.getValue("potion")); HoloAPI.getManager().createSimpleHologram(l, Settings.INDICATOR_TIME_VISIBLE.getValue("potion"), true, content); } } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onAsyncPlayerChat(AsyncPlayerChatEvent event) { if (!event.isCancelled()) { final Player p = event.getPlayer(); if (!HoloAPI.getVanishProvider().isVanished(p)) { final String msg = event.getMessage(); if (event.isAsynchronous()) { HoloAPI.getCore().getServer().getScheduler().scheduleSyncDelayedTask(HoloAPI.getCore(), new BukkitRunnable() { @Override public void run() { showChatHologram(p, msg); } }); } else { this.showChatHologram(p, msg); } } } } private void showChatHologram(final Player p, String msg) { //YAMLConfig config = HoloAPI.getConfig(HoloAPI.ConfigType.MAIN); if (Settings.CHATBUBBLES_SHOW.getValue()) { Location loc = p.getEyeLocation().clone(); loc.add(0D, Settings.CHATBUBBLES_DISTANCE_ABOVE.getValue(), 0D); final int duration = Settings.CHATBUBBLES_DISPLAY_DURATION.getValue(); final boolean rise = Settings.CHATBUBBLES_RISE.getValue(); final boolean followPlayer = Settings.CHATBUBBLES_FOLLOW_PLAYER.getValue(); int charsPerLine = Settings.CHATBUBBLES_CHARACTERS_PER_LINE.getValue(); ArrayList<String> lines = new ArrayList<>(); if (Settings.CHATBUBBLES_SHOW_PLAYER_NAME.getValue()) { lines.add(Settings.CHATBUBBLES_NAME_FORMAT.getValue() + p.getName() + ":"); } int index = 0; while (index < msg.length()) { lines.add(ChatColor.WHITE + msg.substring(index, Math.min(index + charsPerLine, msg.length()))); index += charsPerLine; } if (CHAT_BUBBLES.containsKey(p.getName())) { ArrayList<String> hologramIds = CHAT_BUBBLES.get(p.getName()); if (!hologramIds.isEmpty()) { Hologram last = null; // Iterate from bottom to top for (int j = hologramIds.size() - 1; j >= 0; j--) { Hologram h = HoloAPI.getManager().getHologram(hologramIds.get(j)); if (h != null) { double totalSize = (h.getLines().length * Settings.VERTICAL_LINE_SPACING.getValue()); double minY = h.getDefaultY() - totalSize; if (last != null && minY < last.getDefaultY()) { h.move(new Vector(h.getDefaultX(), h.getDefaultY() + (last.getLines().length * Settings.VERTICAL_LINE_SPACING.getValue()), h.getDefaultZ())); } else { if (minY < loc.getY()) { h.move(new Vector(h.getDefaultX(), h.getDefaultY() + totalSize, h.getDefaultZ())); last = h; } } } } } } final Hologram hologram = HoloAPI.getManager().createSimpleHologram(loc, duration, !followPlayer, lines); ArrayList<String> list; if (CHAT_BUBBLES.containsKey(p.getName())) { list = CHAT_BUBBLES.get(p.getName()); } else { list = new ArrayList<>(); } list.add(hologram.getSaveId()); CHAT_BUBBLES.put(p.getName(), list); new BukkitRunnable() { @Override public void run() { ArrayList<String> list = CHAT_BUBBLES.get(p.getName()); if (list != null) { list.remove(hologram.getSaveId()); } CHAT_BUBBLES.put(p.getName(), list); } }.runTaskLater(HoloAPI.getCore(), duration * 20); if (followPlayer) { class FollowPlayer extends BukkitRunnable { private int i; private double riseDiff = 0.0D; @Override public void run() { if (p == null || ++i >= ((duration * 20) - 1)) { this.cancel(); } Location l = p.getEyeLocation(); if (rise) { riseDiff += 0.02D; } hologram.move(new Vector(l.getX(), l.getY() + 0.5D + riseDiff, l.getZ())); } } new FollowPlayer().runTaskTimer(HoloAPI.getCore(), 10L, 10L); } } } }