package tc.oc.pgm.shield; import java.util.logging.Logger; import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.PotionEffectRemoveEvent; import org.bukkit.potion.PotionEffectType; import tc.oc.commons.core.logging.ClassLogger; import tc.oc.commons.core.util.TimeUtils; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; public class ShieldPlayerModule { private final ShieldMatchModule smm; private final Logger logger; private final MatchPlayer player; private final Player bukkit; private final ShieldParameters parameters; // Current shield strength, which may be less than the player's absorption // if they are getting some from a potion effect. private double shieldHealth; // Ticks until next shield recharge. private long rechargeTicks; public ShieldPlayerModule(ShieldMatchModule smm, MatchPlayer player, ShieldParameters parameters) { this.smm = smm; this.logger = ClassLogger.get(smm.getLogger(), getClass(), player.getName()); this.player = player; this.bukkit = player.getBukkit(); this.parameters = parameters; this.shieldHealth = parameters.maxHealth; } double getAbsorption() { return bukkit.getAbsorption(); } void setAbsorption(double absorption) { bukkit.setAbsorption((float) absorption); } void addAbsorption(double delta) { if(delta != 0d) setAbsorption(getAbsorption() + delta); } public void apply() { addAbsorption(shieldHealth); } public void remove() { addAbsorption(-shieldHealth); } /** * Recharge the shield to its maximum health. If the player has more * absorption than the current shield strength, the excess is preserved. */ void recharge() { if(shieldHealth < parameters.maxHealth) { double delta = parameters.maxHealth - shieldHealth; logger.fine("Recharging shield: shield=" + shieldHealth + " delta=" + delta); shieldHealth = parameters.maxHealth; addAbsorption(delta); bukkit.playSound(bukkit.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 2); } } void damage() { rechargeTicks = TimeUtils.toTicks(parameters.rechargeDelay); } public void tick(Match match) { if(rechargeTicks > 0) { if(--rechargeTicks == 0) { recharge(); } } else { // Detect shield damage, in case it somehow happens without firing an event. double absorption = getAbsorption(); if(shieldHealth > absorption) { logger.fine("Detected unexpected shield damage: shield=" + shieldHealth + " absorption=" + absorption); shieldHealth = absorption; damage(); } } } void onEvent(EntityDamageEvent event) { if(event.getFinalDamage() > 0) { // Absorbed damage is applied to the shield before any potion effect double shieldDamage = -event.getDamage(EntityDamageEvent.DamageModifier.ABSORPTION); logger.fine("Absorbing damage with shield: shield=" + shieldHealth + " shieldDamage=" + shieldDamage); shieldHealth = Math.max(0, shieldHealth - shieldDamage); damage(); } } void onEvent(PotionEffectRemoveEvent event) { // The NMS code assumes that a potion effect is the only way to get // absorption hearts, so when the effect is removed, it simply removes // the same amount of absorption that it added initially. If any of that // eats into the shield, we refund the difference. if(PotionEffectType.ABSORPTION.equals(event.getEffect().getType())) { double newAbsorption = Math.max(0, getAbsorption() - 4 * (1 + event.getEffect().getAmplifier())); if(newAbsorption < shieldHealth) { logger.fine("Compensating for removal of absorption " + event.getEffect().getAmplifier() + " effect, which will reduce absorption hearts to " + newAbsorption + ", which is below shield health of " + shieldHealth); addAbsorption(shieldHealth - newAbsorption); } } } }