bentobox/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java

264 lines
11 KiB
Java

package world.bentobox.bentobox.listeners.flags.settings;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.WeakHashMap;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Firework;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.AreaEffectCloudApplyEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.entity.EntityShootBowEvent;
import org.bukkit.event.entity.LingeringPotionSplashEvent;
import org.bukkit.event.entity.PotionSplashEvent;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.api.events.flags.FlagSettingChangeEvent;
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.flags.FlagListener;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.RanksManager;
/**
* Handles PVP
* @author tastybento
*
*/
public class PVPListener extends FlagListener {
private final Map<Integer, UUID> thrownPotions = new HashMap<>();
private final Map<Entity, Player> firedFireworks = new WeakHashMap<>();
/**
* This method protects players from PVP if it is not allowed and from
* arrows fired by other players
*
* @param e - event
*/
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onEntityDamage(EntityDamageByEntityEvent e) {
if (e.getEntity() instanceof Player && getPlugin().getIWM().inWorld(e.getEntity().getWorld())) {
// Allow self damage or NPC attack because Citizens handles its own PVP
if (e.getEntity().equals(e.getDamager()) || e.getEntity().hasMetadata("NPC")) {
return;
}
// Is PVP allowed here?
if (this.PVPAllowed(e.getEntity().getLocation())) {
return;
}
// Protect visitors
if (e.getCause().equals(DamageCause.ENTITY_ATTACK) && protectedVisitor((Player)e.getEntity())) {
if (e.getDamager() instanceof Player p && p != null) {
User.getInstance(p).notify(Flags.INVINCIBLE_VISITORS.getHintReference());
} else if (e.getDamager() instanceof Projectile pr && pr.getShooter() instanceof Player sh && sh != null) {
User.getInstance(sh).notify(Flags.INVINCIBLE_VISITORS.getHintReference());
}
e.setCancelled(true);
} else {
// PVP check
respond(e, e.getDamager(), e.getEntity(), getFlag(e.getEntity().getWorld()));
}
}
}
/**
* Checks how to respond to an attack
* @param e - event
* @param damager - entity doing the damaging
* @param flag - flag
*/
private void respond(Cancellable e, Entity damager, Entity hurtEntity, Flag flag) {
// Get the attacker
if (damager instanceof Player) {
User user = User.getInstance(damager);
if (!checkIsland((Event)e, (Player)damager, damager.getLocation(), flag)) {
user.notify(getFlag(damager.getWorld()).getHintReference());
e.setCancelled(true);
}
} else if (damager instanceof Projectile p && ((Projectile)damager).getShooter() instanceof Player shooter) {
// Find out who fired the arrow
processDamage(e, damager, shooter, hurtEntity, flag);
} else if (damager instanceof Firework && firedFireworks.containsKey(damager)) {
Player shooter = firedFireworks.get(damager);
processDamage(e, damager, shooter, hurtEntity, flag);
}
}
private void processDamage(Cancellable e, Entity damager, Player shooter, Entity hurtEntity, Flag flag) {
// Allow self damage
if (hurtEntity.equals(shooter) || shooter == null) {
return;
}
User user = User.getInstance(shooter);
if (!checkIsland((Event)e, shooter, damager.getLocation(), flag)) {
damager.setFireTicks(0);
hurtEntity.setFireTicks(0);
user.notify(getFlag(damager.getWorld()).getHintReference());
e.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onFishing(PlayerFishEvent e) {
if (e.getCaught() instanceof Player c && getPlugin().getIWM().inWorld(c.getLocation())) {
// Allow self-inflicted damage or NPC damage
if (c.equals(e.getPlayer()) || c.hasMetadata("NPC")) {
return;
}
// Is PVP allowed here?
if (this.PVPAllowed(c.getLocation())) {
return;
}
// Protect visitors
if (protectedVisitor(c)) {
User.getInstance(e.getPlayer()).notify(Flags.INVINCIBLE_VISITORS.getHintReference());
e.setCancelled(true);
} else if (!checkIsland(e, e.getPlayer(), c.getLocation(), getFlag(c.getWorld()))) {
e.getHook().remove();
User.getInstance(e.getPlayer()).notify(getFlag(c.getWorld()).getHintReference());
e.setCancelled(true);
}
}
}
/**
* Checks for splash damage. Remove damage if it should not affect.
* @param e - event
*/
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
public void onSplashPotionSplash(final PotionSplashEvent e) {
if (e.getEntity().getShooter() instanceof Player p && p != null && getPlugin().getIWM().inWorld(e.getEntity().getWorld())) {
User user = User.getInstance(p);
// Is PVP allowed here?
if (this.PVPAllowed(e.getEntity().getLocation())) {
return;
}
// Run through affected entities and cancel the splash for protected players
for (LivingEntity le : e.getAffectedEntities()) {
if (!le.getUniqueId().equals(user.getUniqueId()) && blockPVP(user, le, e, getFlag(e.getEntity().getWorld()))) {
e.setIntensity(le, 0);
}
}
}
}
/**
* Check if PVP should be blocked or not
* @param user - user who is initiating the action
* @param le - Living entity involved
* @param e - event driving
* @param flag - flag to check
* @return true if PVP should be blocked otherwise false
*/
private boolean blockPVP(User user, LivingEntity le, Event e, Flag flag) {
// Check for self-inflicted damage or Citizen NPCs
if (le.equals(user.getPlayer()) || le.hasMetadata("NPC")) {
return false;
}
if (le instanceof Player) {
// Protect visitors
if (protectedVisitor(le)) {
user.notify(Flags.INVINCIBLE_VISITORS.getHintReference());
return true;
}
// Check if PVP is allowed or not
if (!checkIsland(e, user.getPlayer(), le.getLocation(), flag)) {
user.notify(getFlag(le.getWorld()).getHintReference());
return true;
}
}
return false;
}
private boolean protectedVisitor(LivingEntity entity) {
return getPlugin().getIWM().getIvSettings(entity.getWorld()).contains(DamageCause.ENTITY_ATTACK.name())
&& !getIslands().userIsOnIsland(entity.getWorld(), User.getInstance(entity));
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
public void onLingeringPotionSplash(final LingeringPotionSplashEvent e) {
// Try to get the shooter
if (e.getEntity().getShooter() instanceof Player && getPlugin().getIWM().inWorld(e.getEntity().getWorld())) {
// Store it and remove it when the effect is gone (Entity ID, UUID of throwing player)
thrownPotions.put(e.getAreaEffectCloud().getEntityId(), ((Player)e.getEntity().getShooter()).getUniqueId());
Bukkit.getScheduler().runTaskLater(getPlugin(), () -> thrownPotions.remove(e.getAreaEffectCloud().getEntityId()), e.getAreaEffectCloud().getDuration());
}
}
@EventHandler(priority = EventPriority.NORMAL)
public void onLingeringPotionDamage(AreaEffectCloudApplyEvent e) {
if (thrownPotions.containsKey(e.getEntity().getEntityId())) {
User user = User.getInstance(thrownPotions.get(e.getEntity().getEntityId()));
// Run through affected entities and delete them if they are safe
e.getAffectedEntities().removeIf(le -> !le.getUniqueId().equals(user.getUniqueId()) && blockPVP(user, le, e, getFlag(e.getEntity().getWorld())));
}
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled=true)
public void onPlayerShootFireworkEvent(final EntityShootBowEvent e) {
// Only care about players shooting fireworks
if (e.getEntity() instanceof Player && (e.getProjectile() instanceof Firework)) {
firedFireworks.put(e.getProjectile(), (Player)e.getEntity());
}
}
@EventHandler(priority = EventPriority.NORMAL)
public void onPVPFlagToggle(final FlagSettingChangeEvent e) {
Flag flag = e.getEditedFlag();
// Only care about PVP Flags
if (Flags.PVP_OVERWORLD.equals(flag) || Flags.PVP_NETHER.equals(flag) || Flags.PVP_END.equals(flag)) {
String message = "protection.flags." + flag.getID() + "." + (e.isSetTo() ? "enabled" : "disabled");
// Send the message to visitors
e.getIsland().getVisitors().forEach(visitor -> User.getInstance(visitor).sendMessage(message));
// Send the message to island members (and coops and trusted)
e.getIsland().getMemberSet(RanksManager.COOP_RANK).forEach(member -> User.getInstance(member).sendMessage(message));
}
}
/**
* Warn visitors if the island they are teleporting to has PVP on
* @param e teleport event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled=true)
public void onPlayerTeleport(PlayerTeleportEvent e) {
if (e.getTo() == null) {
return;
}
getIslands().getIslandAt(e.getTo()).ifPresent(island -> {
if (island.getMemberSet(RanksManager.COOP_RANK).contains(e.getPlayer().getUniqueId())) {
return;
}
if (island.isAllowed(Flags.PVP_OVERWORLD)) {
alertUser(e.getPlayer(), Flags.PVP_OVERWORLD);
}
if (island.isAllowed(Flags.PVP_NETHER)) {
alertUser(e.getPlayer(), Flags.PVP_NETHER);
}
if (island.isAllowed(Flags.PVP_END)) {
alertUser(e.getPlayer(), Flags.PVP_END);
}
});
}
private void alertUser(@NonNull Player player, Flag flag) {
String message = "protection.flags." + flag.getID() + ".enabled";
User.getInstance(player).sendMessage(message);
player.playSound(player.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER,2F, 1F);
}
}