/* ExtraHardMode 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 <http://www.gnu.org/licenses/>. */ package me.ryanhamshire.ExtraHardMode; import java.util.List; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.EnderDragon; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerBucketEmptyEvent; import org.bukkit.event.player.PlayerBucketFillEvent; import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerPickupItemEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; class PlayerEventHandler implements Listener { //typical constructor, yawn PlayerEventHandler() { } //FEATURE: respawning players start without full health or food @EventHandler(ignoreCancelled = true) public void onPlayerRespawn(PlayerRespawnEvent respawnEvent) { Player player = respawnEvent.getPlayer(); World world = respawnEvent.getPlayer().getWorld(); if(!ExtraHardMode.instance.config_enabled_worlds.contains(world) || player.hasPermission("extrahardmode.bypass")) return; SetPlayerHealthAndFoodTask task = new SetPlayerHealthAndFoodTask(player, ExtraHardMode.instance.config_playerRespawnHealth, ExtraHardMode.instance.config_playerRespawnFoodLevel); ExtraHardMode.instance.getServer().getScheduler().scheduleSyncDelayedTask(ExtraHardMode.instance, task, 10L); //half-second delay //FEATURE: players can't swim when they're carrying a lot of weight PlayerData playerData = ExtraHardMode.instance.dataStore.getPlayerData(player.getName()); playerData.cachedWeightStatus = null; } //when a player interacts with the world @EventHandler(priority = EventPriority.LOWEST) void onPlayerInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); World world = event.getPlayer().getWorld(); if(!ExtraHardMode.instance.config_enabled_worlds.contains(world) || player.hasPermission("extrahardmode.bypass")) return; Action action = event.getAction(); //FEATURE: bonemeal doesn't work on mushrooms if(ExtraHardMode.instance.config_noBonemealOnMushrooms && action == Action.RIGHT_CLICK_BLOCK) { Block block = event.getClickedBlock(); if(block.getType() == Material.RED_MUSHROOM || block.getType() == Material.BROWN_MUSHROOM) { //what's the player holding? Material materialInHand = player.getItemInHand().getType(); //if bonemeal, cancel the event if(materialInHand == Material.INK_SACK) //bukkit bug labels bone meal as ink sack { event.setCancelled(true); } } } //FEATURE: seed reduction. some plants die even when a player uses bonemeal. if(ExtraHardMode.instance.config_weakFoodCrops && action == Action.RIGHT_CLICK_BLOCK) { Block block = event.getClickedBlock(); Material materialInHand = player.getItemInHand().getType(); if(materialInHand == Material.INK_SACK && ExtraHardMode.instance.plantDies(block, Byte.MAX_VALUE)) { event.setCancelled(true); block.setType(Material.LONG_GRASS); //dead shrub } } //FEATURE: putting out fire up close catches the player on fire Block block = event.getClickedBlock(); if(ExtraHardMode.instance.config_extinguishingFireIgnitesPlayers && block != null && block.getType() != Material.AIR) { if(block.getRelative(event.getBlockFace()).getType() == Material.FIRE) { player.setFireTicks(100); //20L ~ 1 seconds; 100L ~ 5 seconds } } } //when a player fills a bucket... @EventHandler(priority = EventPriority.LOW) void onPlayerFillBucket(PlayerBucketFillEvent event) { //FEATURE: can't move water source blocks if(ExtraHardMode.instance.config_dontMoveWaterSourceBlocks) { Player player = event.getPlayer(); World world = event.getPlayer().getWorld(); if(!ExtraHardMode.instance.config_enabled_worlds.contains(world) || player.hasPermission("extrahardmode.bypass")) return; //only care about stationary (source) water Block block = event.getBlockClicked(); if(block.getType() == Material.STATIONARY_WATER) { //cancel the event so that the water doesn't get removed event.setCancelled(true); //fill the player's bucket anyway //(beware, player may have a stack of empty buckets, and filled buckets DON'T stack) int extraBuckets = player.getItemInHand().getAmount() - 1; player.getItemInHand().setType(Material.WATER_BUCKET); player.getItemInHand().setAmount(1); if(extraBuckets > 0) { player.getInventory().addItem(new ItemStack(Material.BUCKET, extraBuckets)); } //send the player data so that his client doesn't incorrectly show the water as missing player.sendBlockChange(block.getLocation(), block.getTypeId(), block.getData()); } } } //when a player empties a bucket... @EventHandler(priority = EventPriority.NORMAL) void onPlayerEmptyBucket(PlayerBucketEmptyEvent event) { //FEATURE: can't move water source blocks if(ExtraHardMode.instance.config_dontMoveWaterSourceBlocks) { Player player = event.getPlayer(); World world = event.getPlayer().getWorld(); if(!ExtraHardMode.instance.config_enabled_worlds.contains(world) || player.hasPermission("extrahardmode.bypass")) return; //only care about water buckets if(player.getItemInHand().getType() == Material.WATER_BUCKET) { //plan to change this block into a non-source block on the next tick Block block = event.getBlockClicked().getRelative(event.getBlockFace()); EvaporateWaterTask task = new EvaporateWaterTask(block); ExtraHardMode.instance.getServer().getScheduler().scheduleSyncDelayedTask(ExtraHardMode.instance, task, 15L); } } } //when a player dumps a bucket... //block use of buckets within other players' claims @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onPlayerBucketEmpty (PlayerBucketEmptyEvent bucketEvent) { Player player = bucketEvent.getPlayer(); World world = player.getWorld(); if(!ExtraHardMode.instance.config_enabled_worlds.contains(world) || player.hasPermission("extrahardmode.bypass")) return; //FEATURE: very limited building in the end //players are allowed to break only end stone, and only to create a stair up to ground level if(ExtraHardMode.instance.config_enderDragonNoBuilding && world.getEnvironment() == Environment.THE_END) { bucketEvent.setCancelled(true); ExtraHardMode.sendMessage(player, TextMode.Err, Messages.LimitedEndBuilding); return; } } //when a player changes worlds... @EventHandler(priority = EventPriority.MONITOR) void onPlayerChangeWorld(PlayerChangedWorldEvent event) { World world = event.getFrom(); if(!ExtraHardMode.instance.config_enabled_worlds.contains(world)) return; //FEATURE: respawn the ender dragon when the last player leaves the end if(world.getEnvironment() != Environment.THE_END) return; if(world.getPlayers().size() > 0) return; //look for an ender dragon List<Entity> entities = world.getEntities(); EnderDragon enderDragon = null; for(int i = 0; i < entities.size(); i++) { Entity entity = entities.get(i); if(entity instanceof EnderDragon) { enderDragon = (EnderDragon)entities.get(i); break; } //clean up any summoned minions if(entity.getType() == EntityType.ZOMBIE || entity.getType() == EntityType.BLAZE || entity.getType() == EntityType.VILLAGER) { entity.remove(); } } //if he's there, full health if(enderDragon != null) { enderDragon.setHealth(enderDragon.getMaxHealth()); } //otherwise, spawn one else { world.spawnEntity(new Location(world, 0, world.getMaxHeight() - 1, 0), EntityType.ENDER_DRAGON); } } //when a player moves... @EventHandler(priority = EventPriority.NORMAL) void onPlayerMove(PlayerMoveEvent event) { //FEATURE: no swimming while heavy if(!ExtraHardMode.instance.config_noSwimmingInArmor) return; //only care about moving up Location from = event.getFrom(); Location to = event.getTo(); if(to.getY() <= from.getY()) return; //only when in water Block fromBlock = from.getBlock(); if(!fromBlock.isLiquid()) return; Block toBlock = to.getBlock(); if(!toBlock.isLiquid()) return; //only when in deep water Block underFromBlock = fromBlock.getRelative(BlockFace.DOWN).getRelative(BlockFace.DOWN); if(!underFromBlock.isLiquid()) return; //only enabled worlds, and players without bypass permission Player player = event.getPlayer(); World world = player.getWorld(); if(!ExtraHardMode.instance.config_enabled_worlds.contains(world) || player.hasPermission("extrahardmode.bypass")) return; PlayerData playerData = ExtraHardMode.instance.dataStore.getPlayerData(player.getName()); //if no cached value, calculate if(playerData.cachedWeightStatus == null) { //count worn clothing (counts double) PlayerInventory inventory = player.getInventory(); int weight = 0; ItemStack [] armor = inventory.getArmorContents(); for(ItemStack armorPiece : armor) { if(armorPiece != null && armorPiece.getType() != Material.AIR) { weight += 2; } } //count contents for(ItemStack itemStack : inventory.getContents()) { if(itemStack != null && itemStack.getType() != Material.AIR) { weight++; if(weight > 18) { break; } } } playerData.cachedWeightStatus = weight > 18; } //if too heavy, not allowed to swim if(playerData.cachedWeightStatus == true) { event.setCancelled(true); ExtraHardMode.sendMessage(player, TextMode.Warn, Messages.NoSwimmingInArmor); } } //when a player drops an item @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) void onPlayerDropItem(PlayerDropItemEvent event) { //FEATURE: players can't swim when they're carrying a lot of weight Player player = event.getPlayer(); PlayerData playerData = ExtraHardMode.instance.dataStore.getPlayerData(player.getName()); playerData.cachedWeightStatus = null; } //when a player picks up an item @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) void onPlayerPickupItem(PlayerPickupItemEvent event) { //FEATURE: players can't swim when they're carrying a lot of weight Player player = event.getPlayer(); PlayerData playerData = ExtraHardMode.instance.dataStore.getPlayerData(player.getName()); playerData.cachedWeightStatus = null; } //when a player interacts with an inventory @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) void onPlayerInventoryClick(InventoryClickEvent event) { //FEATURE: players can't swim when they're carrying a lot of weight HumanEntity humanEntity = event.getWhoClicked(); if(humanEntity instanceof Player) { Player player = (Player)humanEntity; PlayerData playerData = ExtraHardMode.instance.dataStore.getPlayerData(player.getName()); playerData.cachedWeightStatus = null; } } }