/*
GriefPrevention Server Plugin for Minecraft
Copyright (C) 2012 Ryan Hamshire
This program 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.
This program 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 this program. If not, see .
*/
package me.ryanhamshire.GriefPrevention;
import java.util.Calendar;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Creeper;
import org.bukkit.entity.Enderman;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.ThrownPotion;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.painting.PaintingBreakByEntityEvent;
import org.bukkit.event.painting.PaintingBreakEvent;
import org.bukkit.event.painting.PaintingPlaceEvent;
//handles events related to entities
class EntityEventHandler implements Listener
{
//convenience reference for the singleton datastore
private DataStore dataStore;
public EntityEventHandler(DataStore dataStore)
{
this.dataStore = dataStore;
}
//when an entity explodes...
@EventHandler(ignoreCancelled = true)
public void onEntityExplode(EntityExplodeEvent explodeEvent)
{
List blocks = explodeEvent.blockList();
Entity entity = explodeEvent.getEntity();
//FEATURE: creepers don't destroy blocks when they explode near or above sea level
if(GriefPrevention.instance.config_creepersDontDestroySurface && entity instanceof Creeper)
{
if(entity.getLocation().getBlockY() > entity.getLocation().getWorld().getSeaLevel() - 7)
{
blocks.clear(); //explosion still happens, can damage creatures/players, but no blocks will be destroyed
return;
}
}
//FEATURE: creating an explosion near a claim doesn't damage any of the claimed blocks
Claim claim = null;
for(int i = 0; i < blocks.size(); i++) //for each destroyed block
{
Block block = blocks.get(i);
if(block.getType() == Material.AIR) continue; //if it's air, we don't care
claim = this.dataStore.getClaimAt(block.getLocation(), false, claim);
//if the block is claimed, remove it from the list of destroyed blocks
if(claim != null)
{
blocks.remove(i--);
}
//if the block is not claimed and is a log, trigger the anti-tree-top code
else if(block.getType() == Material.LOG)
{
GriefPrevention.instance.handleLogBroken(block);
}
}
}
//when an entity dies...
@EventHandler
public void onEntityDeath(EntityDeathEvent event)
{
//FEATURE: when a player is involved in a siege (attacker or defender role)
//his death will end the siege
LivingEntity entity = event.getEntity();
if(!(entity instanceof Player)) return; //only tracking players
Player player = (Player)entity;
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
//if involved in a siege
if(playerData.siegeData != null)
{
//end it, with the dieing player being the loser
this.dataStore.endSiege(playerData.siegeData, null, player.getName());
}
}
//when an entity picks up an item
@EventHandler
public void onEntityPickup(EntityChangeBlockEvent event)
{
//FEATURE: endermen don't steal claimed blocks
//if its an enderman
if(event.getEntity() instanceof Enderman)
{
//and the block is claimed
if(this.dataStore.getClaimAt(event.getBlock().getLocation(), false, null) != null)
{
//he doesn't get to steal it
event.setCancelled(true);
}
}
}
//when a painting is broken
@EventHandler(ignoreCancelled = true)
public void onPaintingBreak(PaintingBreakEvent event)
{
//FEATURE: claimed paintings are protected from breakage
//only allow players to break paintings, not anything else (like water and explosions)
if(!(event instanceof PaintingBreakByEntityEvent))
{
event.setCancelled(true);
return;
}
PaintingBreakByEntityEvent entityEvent = (PaintingBreakByEntityEvent)event;
//which claim is the painting in?
Claim claim = this.dataStore.getClaimAt(event.getPainting().getLocation(), false, null);
if(claim == null) return;
//who is removing it?
Entity remover = entityEvent.getRemover();
//again, making sure the breaker is a player
if(!(remover instanceof Player))
{
event.setCancelled(true);
return;
}
//if the player doesn't have build permission, don't allow the breakage
Player playerRemover = (Player)entityEvent.getRemover();
String noBuildReason = claim.allowBuild(playerRemover);
if(noBuildReason != null)
{
event.setCancelled(true);
GriefPrevention.sendMessage(playerRemover, TextMode.Err, noBuildReason);
}
}
//when a painting is placed...
@EventHandler(ignoreCancelled = true)
public void onPaintingPlace(PaintingPlaceEvent event)
{
//FEATURE: similar to above, placing a painting requires build permission in the claim
//which claim is the painting in?
Claim claim = this.dataStore.getClaimAt(event.getBlock().getLocation(), false, null);
if(claim == null) return;
//if the player doesn't have permission, don't allow the placement
String noBuildReason = claim.allowBuild(event.getPlayer());
if(noBuildReason != null)
{
event.setCancelled(true);
GriefPrevention.sendMessage(event.getPlayer(), TextMode.Err, noBuildReason);
}
}
//when an entity is damaged
@EventHandler(ignoreCancelled = true)
public void onEntityDamage (EntityDamageEvent event)
{
//only actually interested in entities damaging entities (ignoring environmental damage)
if(!(event instanceof EntityDamageByEntityEvent)) return;
EntityDamageByEntityEvent subEvent = (EntityDamageByEntityEvent) event;
//determine which player is attacking, if any
Player attacker = null;
Entity damageSource = subEvent.getDamager();
if(damageSource instanceof Player)
{
attacker = (Player)damageSource;
}
else if(damageSource instanceof Arrow)
{
Arrow arrow = (Arrow)damageSource;
if(arrow.getShooter() instanceof Player)
{
attacker = (Player)arrow.getShooter();
}
}
else if(damageSource instanceof ThrownPotion)
{
ThrownPotion potion = (ThrownPotion)damageSource;
if(potion.getShooter() instanceof Player)
{
attacker = (Player)potion.getShooter();
}
}
//if the attacker is a player and defender is a player (pvp combat)
if(attacker != null && event.getEntity() instanceof Player)
{
//if pvp is disabled, cancel the event
if(!event.getEntity().getWorld().getPVP())
{
event.setCancelled(true);
return;
}
//FEATURE: prevent pvp in the first minute after spawn, and prevent pvp when one or both players have no inventory
Player defender = (Player)(event.getEntity());
PlayerData defenderData = this.dataStore.getPlayerData(((Player)event.getEntity()).getName());
PlayerData attackerData = this.dataStore.getPlayerData(attacker.getName());
//otherwise if protecting spawning players
if(GriefPrevention.instance.config_pvp_protectFreshSpawns)
{
if(defenderData.pvpImmune)
{
event.setCancelled(true);
GriefPrevention.sendMessage(attacker, TextMode.Err, "You can't injure defenseless players.");
return;
}
if(attackerData.pvpImmune)
{
event.setCancelled(true);
GriefPrevention.sendMessage(attacker, TextMode.Err, "You can't fight someone while you're protected from PvP.");
return;
}
}
//FEATURE: prevent players who very recently participated in pvp combat from hiding inventory to protect it from looting
//FEATURE: prevent players who are in pvp combat from logging out to avoid being defeated
long now = Calendar.getInstance().getTimeInMillis();
defenderData.lastPvpTimestamp = now;
defenderData.lastPvpPlayer = attacker.getName();
attackerData.lastPvpTimestamp = now;
attackerData.lastPvpPlayer = defender.getName();
}
//FEATURE: protect claimed animals, boats, minecarts
//NOTE: animals can be lead with wheat, vehicles can be pushed around.
//so unless precautions are taken by the owner, a resourceful thief might find ways to steal anyway
//if theft protection is enabled
if(GriefPrevention.instance.config_claims_preventTheft && event instanceof EntityDamageByEntityEvent)
{
//if the entity is an animal or a vehicle
if (subEvent.getEntity() instanceof Animals || subEvent.getEntity() instanceof Vehicle)
{
Claim claim = this.dataStore.getClaimAt(event.getEntity().getLocation(), false, null);
//if it's claimed
if(claim != null)
{
//if damaged by anything other than a player, cancel the event
if(attacker == null)
{
event.setCancelled(true);
}
//otherwise the player damaging the entity must have permission
else
{
String noContainersReason = claim.allowContainers(attacker);
if(noContainersReason != null)
{
event.setCancelled(true);
GriefPrevention.sendMessage(attacker, TextMode.Err, "That belongs to " + claim.getOwnerName() + ".");
}
}
}
}
}
}
}