diff --git a/src/com/massivecraft/factions/listeners/FactionsListenerMain.java b/src/com/massivecraft/factions/listeners/FactionsListenerMain.java index f87be980..6bb5d1f0 100644 --- a/src/com/massivecraft/factions/listeners/FactionsListenerMain.java +++ b/src/com/massivecraft/factions/listeners/FactionsListenerMain.java @@ -1,5 +1,6 @@ package com.massivecraft.factions.listeners; +import java.text.MessageFormat; import java.util.Collection; import java.util.Iterator; @@ -9,7 +10,9 @@ import org.bukkit.block.Block; import org.bukkit.entity.Enderman; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; import org.bukkit.entity.Wither; import org.bukkit.event.Cancellable; import org.bukkit.event.EventHandler; @@ -26,10 +29,13 @@ import org.bukkit.event.block.BlockSpreadEvent; import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.EntityRegainHealthEvent; import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.event.entity.PotionSplashEvent; import org.bukkit.event.hanging.HangingBreakByEntityEvent; import org.bukkit.event.hanging.HangingBreakEvent; import org.bukkit.event.hanging.HangingPlaceEvent; @@ -77,6 +83,169 @@ public class FactionsListenerMain implements Listener { Bukkit.getPluginManager().registerEvents(this, Factions.get()); } + + // -------------------------------------------- // + // CAN COMBAT DAMAGE HAPPEN + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void canCombatDamageHappen(EntityDamageEvent event) + { + // TODO: Can't we just listen to the class type the sub is of? + if (!(event instanceof EntityDamageByEntityEvent)) return; + EntityDamageByEntityEvent sub = (EntityDamageByEntityEvent)event; + + if (this.canCombatDamageHappen(sub, true)) return; + event.setCancelled(true); + } + + // mainly for flaming arrows; don't want allies or people in safe zones to be ignited even after damage event is cancelled + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void canCombatDamageHappen(EntityCombustByEntityEvent event) + { + EntityDamageByEntityEvent sub = new EntityDamageByEntityEvent(event.getCombuster(), event.getEntity(), EntityDamageEvent.DamageCause.FIRE, 0); + if (this.canCombatDamageHappen(sub, false)) return; + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void canCombatDamageHappen(PotionSplashEvent event) + { + // If a harmful potion is splashing ... + if (!MUtil.isHarmfulPotion(event.getPotion())) return; + + Entity thrower = event.getPotion().getShooter(); + + // ... scan through affected entities to make sure they're all valid targets. + for (LivingEntity affectedEntity : event.getAffectedEntities()) + { + EntityDamageByEntityEvent sub = new EntityDamageByEntityEvent(thrower, affectedEntity, EntityDamageEvent.DamageCause.CUSTOM, 0); + if (this.canCombatDamageHappen(sub, true)) continue; + + // affected entity list doesn't accept modification (iter.remove() is a no-go), but this works + event.setIntensity(affectedEntity, 0.0); + } + } + + public boolean canCombatDamageHappen(EntityDamageByEntityEvent event, boolean notify) + { + // If the defender is a player ... + Entity edefender = event.getEntity(); + if (!(edefender instanceof Player)) return true; + Player defender = (Player)edefender; + FPlayer fdefender = FPlayerColl.get().get(edefender); + + // ... and the attacker is someone else ... + Entity eattacker = event.getDamager(); + if (eattacker instanceof Projectile) + { + eattacker = ((Projectile)eattacker).getShooter(); + } + if (eattacker.equals(edefender)) return true; + + // ... gather defender PS and faction information ... + PS defenderPs = PS.valueOf(defender); + Faction defenderPsFaction = BoardColl.get().getFactionAt(defenderPs); + + // ... PVP flag may cause a damage block ... + if (defenderPsFaction.getFlag(FFlag.PVP) == false) + { + if (eattacker instanceof Player) + { + if (notify) + { + FPlayer attacker = FPlayerColl.get().get((Player)eattacker); + attacker.msg("PVP is disabled in %s.", defenderPsFaction.describeTo(attacker)); + } + return false; + } + return defenderPsFaction.getFlag(FFlag.MONSTERS); + } + + // ... and if the attacker is a player ... + if (!(eattacker instanceof Player)) return true; + Player attacker = (Player)eattacker; + FPlayer fattacker = FPlayerColl.get().get(attacker); + + // ... does this player bypass all protection? ... + if (ConfServer.playersWhoBypassAllProtection.contains(attacker.getName())) return true; + + // ... gather attacker PS and faction information ... + PS attackerPs = PS.valueOf(attacker); + Faction attackerPsFaction = BoardColl.get().getFactionAt(attackerPs); + + // ... PVP flag may cause a damage block ... + // (just checking the defender as above isn't enough. What about the attacker? It could be in a no-pvp area) + // NOTE: This check is probably not that important but we could keep it anyways. + if (attackerPsFaction.getFlag(FFlag.PVP) == false) + { + if (notify) fattacker.msg("PVP is disabled in %s.", attackerPsFaction.describeTo(fattacker)); + return false; + } + + // ... are PVP rules completely ignored in this world? ... + if (ConfServer.worldsIgnorePvP.contains(defenderPs.getWorld())) return true; + + Faction defendFaction = fdefender.getFaction(); + Faction attackFaction = fattacker.getFaction(); + + if (attackFaction.isNone() && ConfServer.disablePVPForFactionlessPlayers) + { + if (notify) fattacker.msg("You can't hurt other players until you join a faction."); + return false; + } + else if (defendFaction.isNone()) + { + if (defenderPsFaction == attackFaction && ConfServer.enablePVPAgainstFactionlessInAttackersLand) + { + // Allow PVP vs. Factionless in attacker's faction territory + return true; + } + else if (ConfServer.disablePVPForFactionlessPlayers) + { + if (notify) fattacker.msg("You can't hurt players who are not currently in a faction."); + return false; + } + } + + Rel relation = defendFaction.getRelationTo(attackFaction); + + // Check the relation + if (fdefender.hasFaction() && relation.isAtLeast(ConfServer.friendlyFireFromRel) && defenderPsFaction.getFlag(FFlag.FRIENDLYFIRE) == false) + { + if (notify) fattacker.msg("You can't hurt %s.", relation.getDescPlayerMany()); + return false; + } + + // You can not hurt neutrals in their own territory. + boolean ownTerritory = fdefender.isInOwnTerritory(); + if (fdefender.hasFaction() && ownTerritory && relation == Rel.NEUTRAL) + { + if (notify) + { + fattacker.msg("You can't hurt %s in their own territory unless you declare them as an enemy.", fdefender.describeTo(fattacker)); + fdefender.msg("%s tried to hurt you.", fattacker.describeTo(fdefender, true)); + } + return false; + } + + // Damage will be dealt. However check if the damage should be reduced. + int damage = event.getDamage(); + if (damage > 0.0 && fdefender.hasFaction() && ownTerritory && ConfServer.territoryShieldFactor > 0) + { + int newDamage = (int)Math.ceil(damage * (1D - ConfServer.territoryShieldFactor)); + event.setDamage(newDamage); + + // Send message + if (notify) + { + String perc = MessageFormat.format("{0,number,#%}", (ConfServer.territoryShieldFactor)); // TODO does this display correctly?? + fdefender.msg("Enemy damage reduced by %s.", perc); + } + } + + return true; + } // -------------------------------------------- // // REMOVE PLAYER DATA WHEN BANNED diff --git a/src/com/massivecraft/factions/listeners/TodoFactionsEntityListener.java b/src/com/massivecraft/factions/listeners/TodoFactionsEntityListener.java index afacb400..6b382018 100644 --- a/src/com/massivecraft/factions/listeners/TodoFactionsEntityListener.java +++ b/src/com/massivecraft/factions/listeners/TodoFactionsEntityListener.java @@ -1,21 +1,11 @@ package com.massivecraft.factions.listeners; -import java.text.MessageFormat; - import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.entity.EntityCombustByEntityEvent; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.entity.PotionSplashEvent; import com.massivecraft.factions.BoardColl; import com.massivecraft.factions.ConfServer; @@ -23,10 +13,8 @@ import com.massivecraft.factions.FFlag; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.FPlayerColl; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Rel; import com.massivecraft.factions.event.PowerLossEvent; import com.massivecraft.mcore.ps.PS; -import com.massivecraft.mcore.util.MUtil; public class TodoFactionsEntityListener implements Listener @@ -83,179 +71,7 @@ public class TodoFactionsEntityListener implements Listener } } - // -------------------------------------------- // - // PVP STUFF?? - // -------------------------------------------- // - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onEntityDamage(EntityDamageEvent event) - { - // TODO: Can't we just listen to the class type the sub is of? - if (!(event instanceof EntityDamageByEntityEvent)) return; - EntityDamageByEntityEvent sub = (EntityDamageByEntityEvent)event; - - if ( ! this.canDamagerHurtDamagee(sub, true)) - { - event.setCancelled(true); - } - } - - // mainly for flaming arrows; don't want allies or people in safe zones to be ignited even after damage event is cancelled - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onEntityCombustByEntity(EntityCombustByEntityEvent event) - { - EntityDamageByEntityEvent sub = new EntityDamageByEntityEvent(event.getCombuster(), event.getEntity(), EntityDamageEvent.DamageCause.FIRE, 0); - if ( ! this.canDamagerHurtDamagee(sub, false)) - { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onPotionSplashEvent(PotionSplashEvent event) - { - // If a harmful potion is splashing ... - if (!MUtil.isHarmfulPotion(event.getPotion())) return; - - Entity thrower = event.getPotion().getShooter(); - - // scan through affected entities to make sure they're all valid targets - for (LivingEntity affectedEntity : event.getAffectedEntities()) - { - EntityDamageByEntityEvent sub = new EntityDamageByEntityEvent(thrower, affectedEntity, EntityDamageEvent.DamageCause.CUSTOM, 0); - if (this.canDamagerHurtDamagee(sub, true)) continue; - - // affected entity list doesn't accept modification (iter.remove() is a no-go), but this works - event.setIntensity(affectedEntity, 0.0); - } - } - - public boolean canDamagerHurtDamagee(EntityDamageByEntityEvent sub) - { - return canDamagerHurtDamagee(sub, true); - } - - public boolean canDamagerHurtDamagee(EntityDamageByEntityEvent sub, boolean notify) - { - Entity damager = sub.getDamager(); - Entity damagee = sub.getEntity(); - int damage = sub.getDamage(); - - if ( ! (damagee instanceof Player)) return true; - - FPlayer defender = FPlayerColl.get().get(damagee); - - if (defender == null || defender.getPlayer() == null) - return true; - - Location defenderLoc = defender.getPlayer().getLocation(); - - Faction defLocFaction = BoardColl.get().getFactionAt(PS.valueOf(defenderLoc)); - - // for damage caused by projectiles, getDamager() returns the projectile... what we need to know is the source - if (damager instanceof Projectile) - damager = ((Projectile)damager).getShooter(); - - if (damager == damagee) // ender pearl usage and other self-inflicted damage - return true; - - // Players can not take attack damage in a SafeZone, or possibly peaceful territory - - if (defLocFaction.getFlag(FFlag.PVP) == false) - { - if (damager instanceof Player) - { - if (notify) - { - FPlayer attacker = FPlayerColl.get().get((Player)damager); - attacker.msg("PVP is disabled in %s.", defLocFaction.describeTo(attacker)); - } - return false; - } - return defLocFaction.getFlag(FFlag.MONSTERS); - } - - if ( ! (damager instanceof Player)) - return true; - - FPlayer attacker = FPlayerColl.get().get((Player)damager); - - if (attacker == null || attacker.getPlayer() == null) - return true; - - if (ConfServer.playersWhoBypassAllProtection.contains(attacker.getName())) return true; - - Faction locFaction = BoardColl.get().getFactionAt(PS.valueOf(damager)); - - // so we know from above that the defender isn't in a safezone... what about the attacker, sneaky dog that he might be? - if (locFaction.getFlag(FFlag.PVP) == false) - { - if (notify) attacker.msg("PVP is disabled in %s.", locFaction.describeTo(attacker)); - return false; - } - - if (ConfServer.worldsIgnorePvP.contains(defenderLoc.getWorld().getName())) - return true; - - Faction defendFaction = defender.getFaction(); - Faction attackFaction = attacker.getFaction(); - - if (attackFaction.isNone() && ConfServer.disablePVPForFactionlessPlayers) - { - if (notify) attacker.msg("You can't hurt other players until you join a faction."); - return false; - } - else if (defendFaction.isNone()) - { - if (defLocFaction == attackFaction && ConfServer.enablePVPAgainstFactionlessInAttackersLand) - { - // Allow PVP vs. Factionless in attacker's faction territory - return true; - } - else if (ConfServer.disablePVPForFactionlessPlayers) - { - if (notify) attacker.msg("You can't hurt players who are not currently in a faction."); - return false; - } - } - - Rel relation = defendFaction.getRelationTo(attackFaction); - - // Check the relation - if (defender.hasFaction() && relation.isAtLeast(ConfServer.friendlyFireFromRel) && defLocFaction.getFlag(FFlag.FRIENDLYFIRE) == false) - { - if (notify) attacker.msg("You can't hurt %s.", relation.getDescPlayerMany()); - return false; - } - - // You can not hurt neutrals in their own territory. - boolean ownTerritory = defender.isInOwnTerritory(); - if (defender.hasFaction() && ownTerritory && relation == Rel.NEUTRAL) - { - if (notify) - { - attacker.msg("You can't hurt %s in their own territory unless you declare them as an enemy.", defender.describeTo(attacker)); - defender.msg("%s tried to hurt you.", attacker.describeTo(defender, true)); - } - return false; - } - - // Damage will be dealt. However check if the damage should be reduced. - if (damage > 0.0 && defender.hasFaction() && ownTerritory && ConfServer.territoryShieldFactor > 0) - { - int newDamage = (int)Math.ceil(damage * (1D - ConfServer.territoryShieldFactor)); - sub.setDamage(newDamage); - - // Send message - if (notify) - { - String perc = MessageFormat.format("{0,number,#%}", (ConfServer.territoryShieldFactor)); // TODO does this display correctly?? - defender.msg("Enemy damage reduced by %s.", perc); - } - } - - return true; - }