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);
}
}
}
}