MobArena/src/main/java/com/garbagemule/MobArena/ArenaListener.java

1373 lines
48 KiB
Java

package com.garbagemule.MobArena;
import com.garbagemule.MobArena.events.ArenaKillEvent;
import com.garbagemule.MobArena.framework.Arena;
import com.garbagemule.MobArena.leaderboards.Leaderboard;
import com.garbagemule.MobArena.listeners.MAGlobalListener.TeleportResponse;
import com.garbagemule.MobArena.region.ArenaRegion;
import com.garbagemule.MobArena.region.RegionPoint;
import com.garbagemule.MobArena.repairable.Repairable;
import com.garbagemule.MobArena.repairable.RepairableAttachable;
import com.garbagemule.MobArena.repairable.RepairableBed;
import com.garbagemule.MobArena.repairable.RepairableBlock;
import com.garbagemule.MobArena.repairable.RepairableContainer;
import com.garbagemule.MobArena.repairable.RepairableDoor;
import com.garbagemule.MobArena.repairable.RepairableSign;
import com.garbagemule.MobArena.things.ExperienceThing;
import com.garbagemule.MobArena.things.Thing;
import com.garbagemule.MobArena.things.ThingPicker;
import com.garbagemule.MobArena.util.ClassChests;
import com.garbagemule.MobArena.util.Slugs;
import com.garbagemule.MobArena.waves.MABoss;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.Sign;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.AnimalTamer;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Horse;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Mob;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.Slime;
import org.bukkit.entity.Snowman;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.entity.ThrownPotion;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.Event.Result;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockBurnEvent;
import org.bukkit.event.block.BlockEvent;
import org.bukkit.event.block.BlockFadeEvent;
import org.bukkit.event.block.BlockFormEvent;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityCombustByBlockEvent;
import org.bukkit.event.entity.EntityCombustByEntityEvent;
import org.bukkit.event.entity.EntityCombustEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
import org.bukkit.event.entity.EntityTargetEvent;
import org.bukkit.event.entity.EntityTeleportEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.entity.PotionSplashEvent;
import org.bukkit.event.hanging.HangingBreakEvent;
import org.bukkit.event.player.PlayerAnimationEvent;
import org.bukkit.event.player.PlayerArmorStandManipulateEvent;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.vehicle.VehicleEnterEvent;
import org.bukkit.event.vehicle.VehicleExitEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Attachable;
import org.bukkit.material.Bed;
import org.bukkit.material.Door;
import org.bukkit.material.Redstone;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.projectiles.ProjectileSource;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
public class ArenaListener
{
private MobArena plugin;
private Arena arena;
private ArenaRegion region;
private MonsterManager monsters;
private ClassLimitManager classLimits;
private boolean softRestore,
softRestoreDrops,
protect;
private boolean monsterExp,
monsterInfight,
pvpOn, // pvp-enabled in config
pvpEnabled = false, // activated on first wave
foodRegen,
lockFoodLevel,
useClassChests;
private boolean allowTeleport,
canShare,
autoIgniteTNT;
private Set<Player> banned;
private EnumSet<EntityType> excludeFromRetargeting;
public ArenaListener(Arena arena, MobArena plugin) {
this.plugin = plugin;
this.arena = arena;
this.region = arena.getRegion();
this.monsters = arena.getMonsterManager();
ConfigurationSection s = arena.getSettings();
this.softRestore = s.getBoolean("soft-restore", false);
this.softRestoreDrops = s.getBoolean("soft-restore-drops", false);
this.protect = s.getBoolean("protect", true);
this.monsterExp = s.getBoolean("monster-exp", false);
this.monsterInfight = s.getBoolean("monster-infight", false);
this.pvpOn = s.getBoolean("pvp-enabled", false);
this.foodRegen = s.getBoolean("food-regen", false);
this.lockFoodLevel = s.getBoolean("lock-food-level", true);
this.allowTeleport = s.getBoolean("allow-teleporting", false);
this.canShare = s.getBoolean("share-items-in-arena", true);
this.autoIgniteTNT = s.getBoolean("auto-ignite-tnt", false);
this.useClassChests = s.getBoolean("use-class-chests", false);
this.classLimits = arena.getClassLimitManager();
this.banned = new HashSet<>();
this.excludeFromRetargeting = EnumSet.of(
EntityType.ELDER_GUARDIAN,
EntityType.GUARDIAN
);
}
void pvpActivate() {
if (arena.isRunning() && !arena.getPlayersInArena().isEmpty()) {
pvpEnabled = pvpOn;
}
}
void pvpDeactivate() {
if (pvpOn) pvpEnabled = false;
}
public void onBlockBreak(BlockBreakEvent event) {
// Check if the block is a sign, it might be a leaderboard
if (event.getBlock() instanceof Sign) {
// If the sign is the leaderboard sign, null out the config
if (event.getBlock().getLocation().equals(arena.getRegion().getLeaderboard())) {
arena.getRegion().set("leaderboard", null);
}
}
// If the arena isn't protected, care
if (!protect) return;
if (!arena.getRegion().contains(event.getBlock().getLocation()))
return;
if (!arena.inArena(event.getPlayer())) {
if (arena.inEditMode())
return;
else
event.setCancelled(true);
}
if (onBlockDestroy(event))
return;
event.setCancelled(true);
}
public void onHangingBreak(HangingBreakEvent event) {
// If the arena isn't protected, care
if (!protect) return;
Location l = event.getEntity().getLocation();
if (!arena.getRegion().contains(l)) {
return;
}
if (arena.inEditMode()) {
return;
}
event.setCancelled(true);
}
public void onBlockBurn(BlockBurnEvent event) {
// If the arena isn't protected, care
if (!protect) return;
if (!arena.getRegion().contains(event.getBlock().getLocation()) || onBlockDestroy(event))
return;
event.setCancelled(true);
}
private boolean onBlockDestroy(BlockEvent event) {
if (arena.inEditMode())
return true;
if (!arena.isRunning())
return false;
Block b = event.getBlock();
if (arena.removeBlock(b) || b.getType() == Material.TNT)
return true;
if (softRestore) {
BlockState state = b.getState();
Repairable r = null;
if (state instanceof InventoryHolder)
r = new RepairableContainer(state);
else if (state instanceof Sign)
r = new RepairableSign(state);
else if (state.getData() instanceof Attachable)
r = new RepairableAttachable(state);
else
r = new RepairableBlock(state);
arena.addRepairable(r);
if (!softRestoreDrops)
b.setType(Material.AIR);
return true;
}
return false;
}
public void onBlockPlace(BlockPlaceEvent event) {
Block b = event.getBlock();
// If the event didn't happen in the region, or if in edit mode, ignore
if (!arena.getRegion().contains(b.getLocation()) || arena.inEditMode()) {
return;
}
// If the arena isn't running, or if the player isn't in the arena, cancel.
if (!arena.isRunning() || !arena.inArena(event.getPlayer())) {
// But only if we're protecting the region
if (protect) {
event.setCancelled(true);
}
return;
}
// If the block is TNT, set its planter
if (b.getType() == Material.TNT) {
// If auto-igniting, set the planter of the primed TNT instead
if (autoIgniteTNT) {
event.setCancelled(true);
ItemStack stack = event.getItemInHand();
if (stack == null || stack.getType() != Material.TNT) {
plugin.getLogger().warning("Player " + event.getPlayer().getDisplayName() + " just placed TNT without holding a TNT block");
return;
}
stack.setAmount(stack.getAmount() - 1);
TNTPrimed tnt = b.getWorld().spawn(b.getRelative(BlockFace.UP).getLocation(), TNTPrimed.class);
tnt.setSource(event.getPlayer());
return;
}
}
// Any other block we don't care about if we're not protecting
if (!protect) {
return;
}
// Otherwise, block was placed during a session.
arena.addBlock(b);
if (b.getType().name().endsWith("_DOOR")) {
// For doors, add the block just above (so we get both halves)
arena.addBlock(b.getRelative(0, 1, 0));
}
}
public void onBlockForm(BlockFormEvent event) {
// If the arena isn't protected, care
if (!protect) return;
if (!arena.getRegion().contains(event.getBlock().getLocation()))
return;
// If a snowman forms some snow on its path, add the block
if (event.getNewState().getType() == Material.SNOW)
arena.addBlock(event.getBlock());
}
public void onBlockFade(BlockFadeEvent event) {
if (!protect) {
return;
}
if (!arena.getRegion().contains(event.getBlock().getLocation())) {
return;
}
switch (event.getBlock().getType()) {
case ICE:
case SNOW:
event.setCancelled(true);
break;
}
}
public void onBlockIgnite(BlockIgniteEvent event) {
// If the arena isn't protected, care
if (!protect) return;
Block b = event.getBlock();
if (!arena.getRegion().contains(b.getLocation()))
return;
switch (event.getCause()) {
case FLINT_AND_STEEL:
if (arena.inEditMode()) return;
if (arena.isRunning()) {
arena.addBlock(b.getRelative(BlockFace.UP));
break;
}
case LIGHTNING:
case SPREAD:
case FIREBALL:
case EXPLOSION:
case LAVA:
event.setCancelled(true);
break;
}
}
public void onSignChange(SignChangeEvent event) {
arena.setLeaderboard(new Leaderboard(plugin, arena, event.getBlock().getLocation()));
arena.getRegion().set(RegionPoint.LEADERBOARD, event.getBlock().getLocation());
arena.getPlugin().getGlobalMessenger().tell(event.getPlayer(), "Leaderboard made. Now set up the stat signs!");
}
public void onCreatureSpawn(CreatureSpawnEvent event) {
if (!arena.getRegion().contains(event.getLocation())) {
return;
}
if (arena.inEditMode() && event.getEntityType() == EntityType.ARMOR_STAND) {
return;
}
if (!arena.isRunning()) {
// Just block everything if we're not running
event.setCancelled(true);
return;
}
SpawnReason reason = event.getSpawnReason();
// Allow player-made iron golems and snowmen
if (reason == SpawnReason.BUILD_IRONGOLEM || reason == SpawnReason.BUILD_SNOWMAN) {
event.setCancelled(false);
monsters.addGolem(event.getEntity());
return;
}
/*
* We normally want to block all "default" spawns, because this
* reason means MobArena didn't trigger the event. However, we
* make an exception for certain mobs that spawn as results of
* other entities spawning them, e.g. when Evokers summon Vexes.
*/
if (reason == SpawnReason.DEFAULT) {
if (event.getEntityType() == EntityType.VEX) {
event.setCancelled(false);
monsters.addMonster(event.getEntity());
} else {
event.setCancelled(true);
}
return;
}
// If not custom, we probably don't want it, so get rid of it
if (reason != SpawnReason.CUSTOM) {
event.setCancelled(true);
return;
}
// Otherwise, we probably want it, so uncancel just in case
event.setCancelled(false);
/*
* Because MACreature works with the Creature interface rather
* than the Mob interface, it doesn't catch Slimes and Magma
* Cubes, so we catch them here.
*/
LivingEntity entity = event.getEntity();
if (entity instanceof Slime) {
monsters.addMonster(entity);
}
}
public void onEntityExplode(EntityExplodeEvent event) {
if (!monsters.getMonsters().contains(event.getEntity()) && !arena.getRegion().contains(event.getLocation(), 10))
return;
// The generic remove method removes bosses as well
monsters.remove(event.getEntity());
// Cancel if the arena isn't running
if (!arena.isRunning()) {
event.setCancelled(true);
return;
}
// Uncancel, just in case.
event.setCancelled(false);
// If the arena isn't destructible, just clear the blocklist.
if (!softRestore && protect) {
List<Block> blocks = new LinkedList<>(arena.getBlocks());
event.blockList().retainAll(blocks);
return;
}
if (!softRestoreDrops)
event.setYield(0);
// Handle all the blocks in the block list.
for (Block b : event.blockList()) {
BlockState state = b.getState();
if (state.getData() instanceof Door && ((Door) state.getData()).isTopHalf()) {
state = b.getRelative(BlockFace.DOWN).getState();
}
else if (state.getData() instanceof Bed && ((Bed) state.getData()).isHeadOfBed()) {
state = b.getRelative(((Bed) state.getData()).getFacing().getOppositeFace()).getState();
}
// Create a Repairable from the block.
Repairable r = null;
if (state instanceof InventoryHolder)
r = new RepairableContainer(state);
else if (state instanceof Sign)
r = new RepairableSign(state);
else if (state.getData() instanceof Bed)
r = new RepairableBed(state);
else if (state.getData() instanceof Door)
r = new RepairableDoor(state);
else if (state.getData() instanceof Attachable || state.getData() instanceof Redstone)
r = new RepairableAttachable(state);
else
r = new RepairableBlock(state);
// Cakes and liquids should just get removed. If player-placed block, drop as item.
Material mat = state.getType();
if (mat == Material.CAKE || mat == Material.WATER || mat == Material.LAVA)
arena.removeBlock(b);
else if (arena.removeBlock(b))
arena.getWorld().dropItemNaturally(b.getLocation(), new ItemStack(state.getType(), 1));
else if (softRestore)
arena.addRepairable(r);
else
arena.queueRepairable(r);
}
}
/******************************************************
*
* DEATH LISTENERS
*
******************************************************/
public void onEntityDeath(EntityDeathEvent event) {
if (event instanceof PlayerDeathEvent) {
onPlayerDeath((PlayerDeathEvent) event, (Player) event.getEntity());
}
else if (monsters.hasPet(event.getEntity())) {
monsters.removePet(event.getEntity());
}
else if (monsters.removeMonster(event.getEntity())) {
onMonsterDeath(event);
}
else if (monsters.removeMount(event.getEntity())) {
onMountDeath(event);
}
else if (monsters.removeGolem(event.getEntity())) {
arena.announce(Msg.GOLEM_DIED);
}
}
private void onPlayerDeath(PlayerDeathEvent event, Player player) {
if (arena.inArena(player) || arena.inLobby(player)) {
event.getDrops().clear();
event.setDroppedExp(0);
event.setKeepLevel(true);
if (player.getKiller() != null) {
callKillEvent(player.getKiller(), player);
}
if (arena.getSettings().getBoolean("show-death-messages", true)) {
arena.announce(event.getDeathMessage());
}
if (arena.getSettings().getBoolean("keep-exp", false)) {
arena.getRewardManager().addReward(player, new ExperienceThing(player.getTotalExperience()));
}
event.setDeathMessage(null);
arena.getScoreboard().death(player);
arena.playerDeath(player);
} else if (arena.inSpec(player)) {
event.getDrops().clear();
event.setDroppedExp(0);
arena.getScoreboard().death(player);
arena.playerLeave(player);
}
}
public boolean onPlayerRespawn(PlayerRespawnEvent event) {
Player p = event.getPlayer();
if (!arena.isDead(p)) {
return false;
}
Location loc = arena.getRespawnLocation(p);
event.setRespawnLocation(loc);
arena.playerRespawn(p);
return true;
}
private void onMountDeath(EntityDeathEvent event) {
// Shouldn't ever happen
}
private void onMonsterDeath(EntityDeathEvent event) {
EntityDamageEvent e1 = event.getEntity().getLastDamageCause();
EntityDamageByEntityEvent e2 = (e1 instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) e1 : null;
Entity damager = (e2 != null) ? e2.getDamager() : null;
LivingEntity damagee = event.getEntity();
// Make sure to grab the owner of a projectile/pet
if (damager instanceof Projectile) {
ProjectileSource shooter = ((Projectile) damager).getShooter();
if (shooter instanceof Entity) {
damager = (Entity) shooter;
}
} else {
Player owner = arena.getMonsterManager().getOwner(damager);
if (owner != null) {
damager = owner;
}
}
// If the damager was a player, add to kills.
if (damager instanceof Player) {
Player p = (Player) damager;
ArenaPlayer ap = arena.getArenaPlayer(p);
if (ap != null) {
ArenaPlayerStatistics stats = ap.getStats();
if (stats != null) {
ap.getStats().inc("kills");
arena.getScoreboard().addKill(p);
}
MABoss boss = monsters.getBoss(damagee);
if (boss != null) {
for (Player q : arena.getPlayersInArena()) {
arena.getMessenger().tell(q, Msg.WAVE_BOSS_KILLED, p.getName());
}
ThingPicker picker = boss.getReward();
if (picker != null) {
Thing reward = picker.pick();
if (reward != null) {
arena.getRewardManager().addReward(p, reward);
arena.getMessenger().tell(damager, Msg.WAVE_BOSS_REWARD_EARNED, reward.toString());
}
}
}
}
callKillEvent(p, damagee);
}
if (!monsterExp) {
event.setDroppedExp(0);
}
event.getDrops().clear();
MABoss boss = monsters.removeBoss(damagee);
if (boss != null) {
List<ItemStack> drops = boss.getDrops();
if (drops != null && !drops.isEmpty()) {
event.getDrops().addAll(drops);
}
boss.setDead(true);
boss.getHealthBar().setProgress(0);
}
List<ItemStack> loot = monsters.getLoot(damagee);
if (loot != null && !loot.isEmpty()) {
event.getDrops().add(getRandomItem(loot));
}
}
private void callKillEvent(Player killer, Entity victim) {
ArenaKillEvent event = new ArenaKillEvent(arena, killer, victim);
plugin.getServer().getPluginManager().callEvent(event);
}
private ItemStack getRandomItem(List<ItemStack> stacks) {
return stacks.get((new Random()).nextInt(stacks.size()));
}
/******************************************************
*
* DAMAGE LISTENERS
*
******************************************************/
public void onEntityDamage(EntityDamageEvent event) {
Entity damagee = event.getEntity();
EntityDamageByEntityEvent edbe = (event instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) event : null;
Entity damager = null;
if (edbe != null) {
damager = edbe.getDamager();
if (damager instanceof Projectile) {
ProjectileSource shooter = ((Projectile) damager).getShooter();
if (shooter instanceof Entity) {
damager = (Entity) shooter;
}
}
if (damager instanceof TNTPrimed) {
damager = ((TNTPrimed) damager).getSource();
}
}
MABoss boss = (damagee instanceof LivingEntity)
? monsters.getBoss((LivingEntity) damagee)
: null;
// Pets
if (arena.hasPet(damagee)) {
onPetDamage(event, damagee, damager);
}
else if (damagee instanceof ArmorStand) {
onArmorStandDamage(event);
}
// Mount
else if (damagee instanceof AbstractHorse && monsters.hasMount(damagee)) {
onMountDamage(event, (Horse) damagee, damager);
}
// Player
else if (damagee instanceof Player) {
onPlayerDamage(event, (Player) damagee, damager);
}
// Snowmen melting
else if (damagee instanceof Snowman && event.getCause() == DamageCause.MELTING) {
if (arena.isRunning() && arena.getRegion().contains(damagee.getLocation())) {
event.setCancelled(true);
}
}
// Boss monster
else if (boss != null) {
onBossDamage(event, boss, damager);
}
// Regular monster
else if (monsters.getMonsters().contains(damagee)) {
onMonsterDamage(event, damagee, damager);
}
// Player made golems
else if (monsters.getGolems().contains(damagee)) {
onGolemDamage(event, damagee, damager);
}
}
private void onPlayerDamage(EntityDamageEvent event, Player player, Entity damager) {
// Cancel all damage in the lobby and spec area
if (arena.inLobby(player) || arena.inSpec(player)) {
event.setCancelled(true);
return;
}
if (arena.inArena(player)) {
// Cancel damage from pets (and their projectiles)
if (monsters.hasPet(damager)) {
event.setCancelled(true);
return;
}
// Cancel PvP damage if disabled
if (!pvpEnabled && damager instanceof Player && !damager.equals(player)) {
event.setCancelled(true);
return;
}
event.setCancelled(false);
arena.getArenaPlayer(player).getStats().add("dmgTaken", event.getDamage());
// Redirect pet aggro (but not at players)
if (damager instanceof LivingEntity && !(damager instanceof Player)) {
LivingEntity target = (LivingEntity) damager;
monsters.getPets(player).forEach(pet -> {
if (pet instanceof Mob) {
Mob mob = (Mob) pet;
if (mob.getTarget() == null) {
mob.setTarget(target);
}
}
});
}
}
}
private void onPetDamage(EntityDamageEvent event, Entity pet, Entity damager) {
event.setCancelled(true);
}
private void onArmorStandDamage(EntityDamageEvent event) {
if (protect && !arena.inEditMode() && region.contains(event.getEntity().getLocation())) {
event.setCancelled(true);
}
}
private void onMountDamage(EntityDamageEvent event, Horse mount, Entity damager) {
event.setCancelled(true);
}
private void onBossDamage(EntityDamageEvent event, MABoss boss, Entity damager) {
onMonsterDamage(event, boss.getEntity(), damager);
if (event.isCancelled()) {
return;
}
double progress = boss.getHealth() / boss.getMaxHealth();
boss.getHealthBar().setProgress(progress);
}
private void onMonsterDamage(EntityDamageEvent event, Entity monster, Entity damager) {
if (damager instanceof Player) {
Player p = (Player) damager;
if (!arena.inArena(p)) {
event.setCancelled(true);
return;
}
ArenaPlayerStatistics aps = arena.getArenaPlayer(p).getStats();
aps.add("dmgDone", event.getDamage());
aps.inc("hits");
}
else if (arena.hasPet(damager)) {
Player owner = arena.getMonsterManager().getOwner(damager);
if (owner != null) {
ArenaPlayerStatistics aps = arena.getArenaPlayer(owner).getStats();
aps.add("dmgDone", event.getDamage());
}
}
else if (monsters.getMonsters().contains(damager)) {
if (!monsterInfight)
event.setCancelled(true);
}
}
private void onGolemDamage(EntityDamageEvent event, Entity golem, Entity damager) {
if (damager instanceof Player) {
Player p = (Player) damager;
if (!arena.inArena(p)) {
event.setCancelled(true);
return;
}
if (!pvpEnabled) {
event.setCancelled(true);
}
}
}
public void onEntityCombust(EntityCombustEvent event) {
if (monsters.getMonsters().contains(event.getEntity())) {
if (event instanceof EntityCombustByBlockEvent || event instanceof EntityCombustByEntityEvent) {
return;
}
event.setCancelled(true);
}
}
public void onEntityTarget(EntityTargetEvent event) {
if (!arena.isRunning() || event.isCancelled())
return;
Entity entity = event.getEntity();
Entity target = event.getTarget();
if (isArenaPet(entity)) {
onPetTarget(event, target);
} else if (isArenaMonster(entity)) {
onMonsterTarget(event, entity, target);
} else {
onForeignTarget(event, target);
}
}
private void onPetTarget(EntityTargetEvent event, Entity target) {
// If the target is null, do nothing
if (target == null) {
return;
}
// Pets should only attack monsters in the arena, nothing else
if (!isArenaMonster(target)) {
event.setCancelled(true);
}
}
private void onMonsterTarget(EntityTargetEvent event, Entity monster, Entity target) {
// Null means we lost our target or the target died, so find a new one
if (target == null) {
// ... unless the monster is excluded from retargeting
if (excludeFromRetargeting.contains(monster.getType())) {
return;
}
event.setTarget(MAUtils.getClosestPlayer(plugin, monster, arena));
return;
}
// If monster infighting is on and target is monster, let it happen
if (monsterInfight && isArenaMonster(target)) {
return;
}
// At this point, if the target is _not_ an arena player, we can just
// cancel, because this check also covers the target being a pet, an
// arena monster when infighting is off (due to the check above), or
// any foreign entity.
if (!isArenaPlayer(target)) {
event.setCancelled(true);
}
}
private void onForeignTarget(EntityTargetEvent event, Entity target) {
// If null, just bail
if (target == null) {
return;
}
// Foreign entities can't target arena pets, -players, or -monsters.
if (isArenaPet(target) || isArenaPlayer(target) || isArenaMonster(target)) {
event.setCancelled(true);
}
}
@SuppressWarnings("SuspiciousMethodCalls")
private boolean isArenaPlayer(Entity entity) {
return arena.getPlayersInArena().contains(entity);
}
@SuppressWarnings("SuspiciousMethodCalls")
private boolean isArenaMonster(Entity entity) {
return monsters.getMonsters().contains(entity);
}
private boolean isArenaPet(Entity entity) {
return arena.hasPet(entity);
}
public void onEntityTeleport(EntityTeleportEvent event) {
if (monsters.hasPet(event.getEntity()) && region.contains(event.getTo())) {
return;
}
if (region.contains(event.getFrom()) || region.contains(event.getTo())) {
event.setCancelled(true);
}
}
public void onPotionSplash(PotionSplashEvent event) {
ThrownPotion potion = event.getPotion();
if (!region.contains(potion.getLocation())) {
return;
}
if (potion.getShooter() instanceof Player) {
// Check for PvP stuff if the shooter is a player
if (!pvpEnabled) {
// If a potion has harmful effects, remove all players.
for (PotionEffect effect : potion.getEffects()) {
PotionEffectType type = effect.getType();
if (type.equals(PotionEffectType.HARM) || type.equals(PotionEffectType.POISON)) {
for (LivingEntity le : event.getAffectedEntities()) {
if (le instanceof Player) {
event.setIntensity(le, 0.0);
}
}
break;
}
}
}
} else if (!monsterInfight) {
// Otherwise, check for monster infighting
for (PotionEffect effect : potion.getEffects()) {
PotionEffectType type = effect.getType();
if (type.equals(PotionEffectType.HARM) || type.equals(PotionEffectType.POISON)) {
for (LivingEntity le : event.getAffectedEntities()) {
if (!(le instanceof Player)) {
event.setIntensity(le, 0.0);
}
}
break;
}
}
}
}
public void onEntityChangeBlock(EntityChangeBlockEvent event) {
if (!protect) {
return;
}
Block block = event.getBlock();
if (!arena.getRegion().contains(block.getLocation())) {
return;
}
if (arena.isRunning()) {
if (block.getType() == Material.TNT) {
Entity entity = event.getEntity();
if (entity instanceof Arrow) {
Arrow arrow = (Arrow) entity;
ProjectileSource shooter = arrow.getShooter();
if (shooter instanceof Player) {
Player player = (Player) shooter;
if (arena.inArena(player)) {
return;
}
}
}
}
}
event.setCancelled(true);
}
public void onEntityRegainHealth(EntityRegainHealthEvent event) {
if (!arena.isRunning())
return;
if (!(event.getEntity() instanceof Player) || !arena.inArena((Player) event.getEntity()))
return;
if (!foodRegen && event.getRegainReason() == RegainReason.SATIATED) {
event.setCancelled(true);
}
}
public void onFoodLevelChange(FoodLevelChangeEvent event) {
if (!(event.getEntity() instanceof Player)) {
return;
}
Player p = (Player) event.getEntity();
if (arena.isRunning()) {
if (arena.inArena(p) && lockFoodLevel) {
event.setCancelled(true);
}
} else {
// Always locked in lobby/spec
if (arena.inLobby(p) || arena.inSpec(p)) {
event.setCancelled(true);
}
}
}
public void onPlayerAnimation(PlayerAnimationEvent event) {
if (!arena.isRunning() || !arena.inArena(event.getPlayer()))
return;
arena.getArenaPlayer(event.getPlayer()).getStats().inc("swings");
}
public void onPlayerDropItem(PlayerDropItemEvent event) {
Player p = event.getPlayer();
/*
* If the player "drops an item" while in the process of leaving the
* arena, it has to be due to something that happens in the leaving
* process because most of the event system is single-threaded. This
* doesn't make much sense, but it can happen if the player earns a
* command reward of /give, which drops the item with a drop delay of
* 0 from the player, causing the player to immediately pick it up.
* Cancelling this event causes the player to somehow pick up the item
* twice, so we don't want to do that. This early return should guard
* against this specific case, and hopefully not break anything else.
*/
if (arena.isLeaving(p)) {
return;
}
// If the player is active in the arena, only cancel if sharing is not allowed
if (arena.inArena(p)) {
if (!canShare) {
arena.getMessenger().tell(p, Msg.LOBBY_DROP_ITEM);
event.setCancelled(true);
}
}
// If the player is in the lobby, just cancel
else if (arena.inLobby(p)) {
arena.getMessenger().tell(p, Msg.LOBBY_DROP_ITEM);
event.setCancelled(true);
}
// Same if it's a spectator, but...
else if (arena.inSpec(p)) {
arena.getMessenger().tell(p, Msg.LOBBY_DROP_ITEM);
event.setCancelled(true);
// If the spectator isn't in the region, force them to leave
if (!region.contains(p.getLocation())) {
arena.getMessenger().tell(p, Msg.MISC_MA_LEAVE_REMINDER);
arena.playerLeave(p);
}
}
/*
* If the player is not in the arena in any way (as arena player, lobby
* player or a spectator), but they -are- in the region, it must mean
* they are trying to drop items when not allowed
*/
else if (region.contains(p.getLocation())) {
arena.getMessenger().tell(p, Msg.LOBBY_DROP_ITEM);
event.setCancelled(true);
}
/*
* If the player is in the banned set, it means they got kicked or
* disconnected during a session, meaning they are more than likely
* trying to steal items, if a PlayerDropItemEvent is fired.
*/
else if (banned.contains(p)) {
plugin.getLogger().warning("Player " + p.getName() + " tried to steal class items!");
event.setCancelled(true);
}
}
public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) {
if (!arena.getReadyPlayersInLobby().contains(event.getPlayer()) && !arena.inArena(event.getPlayer()))
return;
if (!arena.isRunning()) {
event.getBlockClicked().getRelative(event.getBlockFace()).setType(Material.AIR);
event.setCancelled(true);
return;
}
Block liquid = event.getBlockClicked().getRelative(event.getBlockFace());
arena.addBlock(liquid);
}
public void onPlayerInteract(PlayerInteractEvent event) {
Player p = event.getPlayer();
if (!arena.inLobby(p)) return;
// Prevent placing blocks and using held items
if (event.hasItem()) {
event.setUseItemInHand(Result.DENY);
}
// Bail if off-hand or if there's no block involved.
if (event.getHand() == EquipmentSlot.OFF_HAND || !event.hasBlock())
return;
// Iron block
if (event.getClickedBlock().getType() == Material.IRON_BLOCK) {
handleReadyBlock(p);
}
// Sign
else if (event.getClickedBlock().getState() instanceof Sign) {
Sign sign = (Sign) event.getClickedBlock().getState();
handleSign(sign, p);
}
}
public void onPlayerArmorStandManipulate(PlayerArmorStandManipulateEvent event) {
if (protect && !arena.inEditMode() && region.contains(event.getRightClicked().getLocation())) {
event.setCancelled(true);
}
}
private void handleReadyBlock(Player p) {
if (arena.getArenaPlayer(p).getArenaClass() != null) {
arena.getMessenger().tell(p, Msg.LOBBY_PLAYER_READY);
arena.playerReady(p);
}
else {
arena.getMessenger().tell(p, Msg.LOBBY_PICK_CLASS);
}
}
private void handleSign(Sign sign, Player p) {
// Check if the first line is a class name.
String className = ChatColor.stripColor(sign.getLine(0));
String slug = Slugs.create(className);
if (!arena.getClasses().containsKey(slug) && !slug.equals("random"))
return;
ArenaClass newAC = arena.getClasses().get(slug);
// Check for permission.
if (!newAC.hasPermission(p) && !slug.equals("random")) {
arena.getMessenger().tell(p, Msg.LOBBY_CLASS_PERMISSION);
return;
}
ArenaClass oldAC = arena.getArenaPlayer(p).getArenaClass();
// Same class, do nothing.
if (newAC.equals(oldAC)) {
return;
}
// If the new class is full, inform the player.
if (!classLimits.canPlayerJoinClass(newAC)) {
arena.getMessenger().tell(p, Msg.LOBBY_CLASS_FULL);
return;
}
// Check price, balance, and inform
Thing price = newAC.getPrice();
if (price != null) {
if (!price.heldBy(p)) {
arena.getMessenger().tell(p, Msg.LOBBY_CLASS_TOO_EXPENSIVE, price.toString());
return;
}
}
// Otherwise, leave the old class, and pick the new!
classLimits.playerLeftClass(oldAC, p);
classLimits.playerPickedClass(newAC, p);
// Delay the inventory stuff to ensure that right-clicking works.
delayAssignClass(p, slug, price, sign);
}
/*private boolean cansPlayerJoinClass(ArenaClass ac, Player p) {
// If they can not join the class, deny them
if (!classLimits.canPlayerJoinClass(ac)) {
Messenger.tell(p, Msg.LOBBY_CLASS_FULL);
return false;
}
// Increment the "in use" in the Class Limit Manager
classLimits.playerPickedClass(ac);
return true;
}*/
private void delayAssignClass(final Player p, final String slug, final Thing price, final Sign sign) {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin,new Runnable() {
public void run() {
if (!slug.equalsIgnoreCase("random")) {
if (useClassChests) {
ArenaClass ac = plugin.getArenaMaster().getClasses().get(slug);
if (ClassChests.assignClassFromStoredClassChest(arena, p, ac)) {
return;
}
if (ClassChests.assignClassFromClassChestSearch(arena, p, ac, sign)) {
return;
}
// Otherwise just fall through and use the items from the config-file
}
arena.assignClass(p, slug);
arena.getMessenger().tell(p, Msg.LOBBY_CLASS_PICKED, arena.getClasses().get(slug).getConfigName());
if (price != null) {
arena.getMessenger().tell(p, Msg.LOBBY_CLASS_PRICE, price.toString());
}
}
else {
arena.addRandomPlayer(p);
arena.getMessenger().tell(p, Msg.LOBBY_CLASS_RANDOM);
}
}
});
}
public void onPlayerQuit(PlayerQuitEvent event) {
Player p = event.getPlayer();
if (!arena.isEnabled() || (!arena.inArena(p) && !arena.inLobby(p) && !arena.inSpec(p))) {
return;
}
arena.playerLeave(p);
banned.add(p);
scheduleUnban(p, 20);
}
public void onPlayerKick(PlayerKickEvent event) {
Player p = event.getPlayer();
if (!arena.isEnabled() || (!arena.inArena(p) && !arena.inLobby(p) && !arena.inSpec(p))) {
return;
}
arena.playerLeave(p);
banned.add(p);
scheduleUnban(p, 20);
}
private void scheduleUnban(final Player p, int ticks) {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
public void run() {
banned.remove(p);
}
}, ticks);
}
public TeleportResponse onPlayerTeleport(PlayerTeleportEvent event) {
if (!arena.isEnabled() || !region.isSetup() || arena.inEditMode() || allowTeleport) {
return TeleportResponse.IDGAF;
}
Player p = event.getPlayer();
/*
* Players that are being moved around by the arena are always allowed
* to teleport. They stay in this "moving" state only during the actual
* transition and are removed from that state as soon as the transition
* is complete.
*/
if (arena.isMoving(p)) {
return TeleportResponse.ALLOW;
}
Location to = event.getTo();
Location from = event.getFrom();
/*
* At this point we're looking at warping of players that are either in
* the arena - but not in the "moving" state - or not in the arena. The
* tricky bit here is to figure out the edge cases. This is essentially
* a matter of players "teleporting on their own" (or with assistance
* from other plugins).
*/
if (region.contains(from)) {
if (region.contains(to)) {
// Inside -> inside
if (!(arena.inArena(p) || arena.inLobby(p))) {
return reject(p, Msg.WARP_TO_ARENA);
}
return TeleportResponse.ALLOW;
} else {
// Inside -> outside
if (arena.getAllPlayers().contains(p)) {
return reject(p, Msg.WARP_FROM_ARENA);
}
return TeleportResponse.IDGAF;
}
} else {
if (region.contains(to)) {
// Outside -> inside
return reject(p, Msg.WARP_TO_ARENA);
} else {
// Outside -> outside
return TeleportResponse.IDGAF;
}
}
}
private TeleportResponse reject(Player p, Msg message) {
if (p.hasPermission("mobarena.admin.teleport")) {
return TeleportResponse.IDGAF;
}
arena.getMessenger().tell(p, message);
return TeleportResponse.REJECT;
}
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
Player p = event.getPlayer();
if (event.isCancelled() || (!arena.inArena(p) && !arena.inSpec(p) && !arena.inLobby(p))) {
return;
}
// This is safe, because commands will always have at least one element.
String base = event.getMessage().split(" ")[0].toLowerCase();
// Check if the entire base command is allowed.
if (plugin.getArenaMaster().isAllowed(base)) {
return;
}
// If not, check if the specific command is allowed.
String noslash = event.getMessage().substring(1).toLowerCase();
if (plugin.getArenaMaster().isAllowed(noslash)) {
return;
}
// This is dirty, but it ensures that commands are indeed blocked.
event.setMessage("/");
// Cancel the event regardless.
event.setCancelled(true);
arena.getMessenger().tell(p, Msg.MISC_COMMAND_NOT_ALLOWED);
}
public void onPlayerPreLogin(PlayerLoginEvent event) {
Player p = event.getPlayer();
if (p == null || !p.isOnline()) return;
Arena arena = plugin.getArenaMaster().getArenaWithPlayer(p);
if (arena == null) return;
arena.playerLeave(p);
}
public void onVehicleEnter(VehicleEnterEvent event) {
Entity entity = event.getEntered();
if (!(entity instanceof Player)) return;
Player p = (Player) entity;
if (!arena.inArena(p)) return;
Vehicle vehicle = event.getVehicle();
if (!(vehicle instanceof Horse)) return;
Horse horse = (Horse) vehicle;
if (!monsters.hasMount(horse)) return;
AnimalTamer tamer = horse.getOwner();
if (tamer.equals(p)) {
horse.setAI(true);
} else {
event.setCancelled(true);
}
}
public void onVehicleExit(VehicleExitEvent event) {
LivingEntity entity = event.getExited();
if (!(entity instanceof Player)) return;
Player p = (Player) entity;
if (!arena.inArena(p)) return;
Vehicle vehicle = event.getVehicle();
if (!(vehicle instanceof Horse)) return;
Horse horse = (Horse) vehicle;
if (!monsters.hasMount(horse)) return;
horse.setAI(false);
}
}