/** Copyright (C) <2015> <coolAlias> This file is part of coolAlias' Zelda Sword Skills Minecraft Mod; as such, 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 zeldaswordskills.handler; import java.util.HashMap; import java.util.Map; import net.minecraft.block.Block; import net.minecraft.block.BlockBreakable; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.boss.IBossDisplayData; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.monster.EntityBlaze; import net.minecraft.entity.monster.EntityCaveSpider; import net.minecraft.entity.monster.EntityCreeper; import net.minecraft.entity.monster.EntityEnderman; import net.minecraft.entity.monster.EntityGhast; import net.minecraft.entity.monster.EntityIronGolem; import net.minecraft.entity.monster.EntityMagmaCube; import net.minecraft.entity.monster.EntityPigZombie; import net.minecraft.entity.monster.EntitySilverfish; import net.minecraft.entity.monster.EntitySkeleton; import net.minecraft.entity.monster.EntitySpider; import net.minecraft.entity.monster.EntityWitch; import net.minecraft.entity.monster.EntityZombie; import net.minecraft.entity.monster.IMob; import net.minecraft.entity.passive.EntityHorse; import net.minecraft.entity.passive.EntityOcelot; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemEnchantedBook; import net.minecraft.item.ItemStack; import net.minecraft.util.BlockPos; import net.minecraft.util.EnumFacing; import net.minecraft.util.MathHelper; import net.minecraft.world.World; import net.minecraftforge.event.AnvilUpdateEvent; import net.minecraftforge.event.entity.item.ItemTossEvent; import net.minecraftforge.event.entity.living.LivingDropsEvent; import net.minecraftforge.event.entity.player.EntityItemPickupEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.event.world.BlockEvent.HarvestDropsEvent; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.eventhandler.Event.Result; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import zeldaswordskills.ZSSAchievements; import zeldaswordskills.api.block.BlockWeight; import zeldaswordskills.api.block.ILiftable; import zeldaswordskills.api.block.ISmashable; import zeldaswordskills.api.item.ArmorIndex; import zeldaswordskills.api.item.IFairyUpgrade; import zeldaswordskills.api.item.IHandlePickup; import zeldaswordskills.api.item.IHandleToss; import zeldaswordskills.api.item.ILiftBlock; import zeldaswordskills.api.item.ISmashBlock; import zeldaswordskills.api.item.IUnenchantable; import zeldaswordskills.block.tileentity.TileEntityDungeonCore; import zeldaswordskills.entity.mobs.EntityDarknut; import zeldaswordskills.entity.mobs.EntityKeese; import zeldaswordskills.entity.mobs.EntityOctorok; import zeldaswordskills.entity.mobs.EntityWizzrobe; import zeldaswordskills.entity.player.ZSSPlayerInfo; import zeldaswordskills.entity.player.ZSSPlayerSkills; import zeldaswordskills.item.ItemHeldBlock; import zeldaswordskills.item.ZSSItems; import zeldaswordskills.network.PacketDispatcher; import zeldaswordskills.network.client.UnpressKeyPacket; import zeldaswordskills.network.server.HeldBlockColorPacket; import zeldaswordskills.ref.Config; import zeldaswordskills.ref.Sounds; import zeldaswordskills.skills.SkillBase; import zeldaswordskills.util.PlayerUtils; import zeldaswordskills.util.WorldUtils; /** * * Event Handler for various item- and block-related events * */ public class ZSSItemEvents { /** Mapping of mobs to skill orb drops */ private static final Map<Class<? extends EntityLivingBase>, ItemStack> dropsList = new HashMap<Class<? extends EntityLivingBase>, ItemStack>(); public ZSSItemEvents() { if (dropsList.isEmpty()) { ZSSItemEvents.init(); } } /** Adds a mob-class to skill orb mapping */ private static void addDrop(Class<? extends EntityLivingBase> mobClass, SkillBase skill) { ItemStack stack = new ItemStack(ZSSItems.skillOrb, 1, skill.getId()); dropsList.put(mobClass, stack); } /** * Returns the type of skill orb that the mob will drop this time; * this is not always the same as the stack stored in dropsList */ private static ItemStack getOrbDrop(EntityLivingBase mob, boolean isBoss) { if (dropsList.get(mob.getClass()) != null && mob.worldObj.rand.nextFloat() > Config.getChanceForRandomDrop()) { return dropsList.get(mob.getClass()); } else { ItemStack orb = null; int id = mob.worldObj.rand.nextInt(SkillBase.getNumSkills()); if (SkillBase.doesSkillExist(id) && SkillBase.getSkill(id).canDrop()) { if (dropsList.get(mob.getClass()) != null || isBoss || mob.worldObj.rand.nextFloat() < Config.getRandomMobDropChance()) { orb = (id == SkillBase.bonusHeart.getId() ? new ItemStack(ZSSItems.heartPiece) : new ItemStack(ZSSItems.skillOrb, 1, id)); } } return orb; } } @SubscribeEvent public void onLivingDrops(LivingDropsEvent event) { if (event.source.getEntity() instanceof EntityPlayer) { EntityPlayer player = (EntityPlayer) event.source.getEntity(); EntityLivingBase mob = event.entityLiving; boolean isBoss = mob instanceof IBossDisplayData; boolean flag = ZSSPlayerSkills.get(player).getSkillLevel(SkillBase.mortalDraw) == SkillBase.mortalDraw.getMaxLevel(); ItemStack orb = (isBoss && !flag ? new ItemStack(ZSSItems.skillOrb,1,SkillBase.mortalDraw.getId()) : getOrbDrop(mob, isBoss)); if (orb != null && Config.areOrbDropsEnabled()) { ItemStack helm = (player).getCurrentArmor(ArmorIndex.WORN_HELM); float f = (helm != null && helm.getItem() == ZSSItems.maskTruth ? 0.01F : 0.0F); float baseChance = Config.getDropChance(orb.getItem() == ZSSItems.heartPiece ? SkillBase.bonusHeart.getId() : orb.getItemDamage()); if (baseChance > 0.0F && (isBoss || mob.worldObj.rand.nextFloat() < (baseChance + f + (0.005F * event.lootingLevel)))) { event.drops.add(new EntityItem(mob.worldObj, mob.posX, mob.posY, mob.posZ, orb.copy())); mob.worldObj.playSoundEffect(mob.posX, mob.posY, mob.posZ, Sounds.SPECIAL_DROP, 1.0F, 1.0F); player.triggerAchievement(ZSSAchievements.skillGain); if (isBoss) { player.triggerAchievement(ZSSAchievements.skillMortal); } } } if (mob instanceof EntityCreeper && mob.worldObj.rand.nextFloat() < Config.getCreeperDropChance()) { event.drops.add(new EntityItem(mob.worldObj, mob.posX, mob.posY, mob.posZ, new ItemStack(ZSSItems.bomb))); } if (mob instanceof IMob && mob.worldObj.rand.nextInt(Config.getPowerDropRate()) == 0) { event.drops.add(new EntityItem(mob.worldObj, mob.posX, mob.posY, mob.posZ, new ItemStack(ZSSItems.powerPiece))); } // Check for heart and magic jar drops if (mob instanceof IMob) { // High-HP mobs have a better chance of dropping a Large Magic Jar; // as a base line, Dark Nuts (50 HP) should have a 5-10% drop rate (caps at 25%) float hp = mob.getMaxHealth(); float chance = MathHelper.clamp_float(((hp - 40F) / 100F), 0F, 0.25F); if (hp > 100) { // add 5% per additional 100 HP to a max of 50% for a 500+ HP critter chance = MathHelper.clamp_float(chance + (hp - 100F) / 2000F, 0F, 0.5F); } if (mob.worldObj.rand.nextFloat() < chance) { event.drops.add(new EntityItem(mob.worldObj, mob.posX, mob.posY, mob.posZ, new ItemStack(ZSSItems.magicJarBig))); } int consumable_chance = Config.getMobConsumableFrequency(); if (consumable_chance > 0 && mob.worldObj.rand.nextInt((event.drops.size() + 1) * (12 - consumable_chance)) == 0) { ItemStack stack = new ItemStack(mob.worldObj.rand.nextInt(4) == 0 ? ZSSItems.magicJar : ZSSItems.smallHeart); event.drops.add(new EntityItem(mob.worldObj, mob.posX, mob.posY, mob.posZ, stack)); } } } } @SubscribeEvent public void onAnvilUpdate(AnvilUpdateEvent event) { // Don't allow unenchantable items to be enchanted in the anvil: boolean left = (event.left.getItem() instanceof ItemEnchantedBook && event.right.getItem().getItemEnchantability() < 1 && (Config.areUnenchantablesDisabled() || event.right.getItem() instanceof IUnenchantable)); boolean right = (event.right.getItem() instanceof ItemEnchantedBook && event.left.getItem().getItemEnchantability() < 1 && (Config.areUnenchantablesDisabled() || event.left.getItem() instanceof IUnenchantable)); event.setCanceled(left || right); } @SubscribeEvent public void onBlockHarvest(HarvestDropsEvent event) { if (event.harvester != null) { Block block = event.state.getBlock(); if (block == Blocks.dirt || block == Blocks.grass) { if (event.harvester.getCurrentArmor(ArmorIndex.WORN_HELM) != null && event.harvester.getCurrentArmor(ArmorIndex.WORN_HELM).getItem() == ZSSItems.maskScents && event.world.rand.nextInt(32) == 0) { event.drops.add(event.world.rand.nextInt(4) == 0 ? new ItemStack(Blocks.red_mushroom) : new ItemStack(Blocks.brown_mushroom)); } } else if (block == Blocks.tallgrass) { if (PlayerUtils.isSword(event.harvester.getHeldItem()) && event.world.rand.nextFloat() < Config.getGrassDropChance()) { event.drops.add(ZSSItems.getRandomGrassDrop(event.world.rand)); } } else if (block == Blocks.iron_ore) { if (event.world.rand.nextFloat() < (0.005F * event.fortuneLevel)) { event.drops.add(new ItemStack(ZSSItems.masterOre)); event.harvester.worldObj.playSoundEffect(event.harvester.posX, event.harvester.posY, event.harvester.posZ, Sounds.SPECIAL_DROP, 1.0F, 1.0F); } } } } @SubscribeEvent public void onItemToss(ItemTossEvent event) { EntityItem item = event.entityItem; ItemStack stack = item.getEntityItem(); if (stack != null) { if (stack.getItem() instanceof IHandleToss) { ((IHandleToss) stack.getItem()).onItemTossed(item, event.player); } if (!item.isDead && (stack.getItem() == Items.emerald || (stack.getItem() instanceof IFairyUpgrade) && ((IFairyUpgrade) stack.getItem()).hasFairyUpgrade(stack))) { TileEntityDungeonCore core = WorldUtils.getNearbyFairySpawner(item.worldObj, item.posX, item.posY, item.posZ, true); if (core != null) { core.scheduleItemUpdate(event.player); } } } event.setCanceled(item.isDead); } @SubscribeEvent public void onItemPickup(EntityItemPickupEvent event) { ItemStack stack = event.item.getEntityItem(); EntityPlayer player = event.entityPlayer; if (stack != null && stack.getItem() instanceof IHandlePickup) { int size = stack.stackSize; if (((IHandlePickup) stack.getItem()).onPickupItem(stack, player)) { if (stack.stackSize < size) { FMLCommonHandler.instance().firePlayerItemPickupEvent(player, event.item); event.item.playSound(Sounds.POP, 0.2F, ((event.item.worldObj.rand.nextFloat() - event.item.worldObj.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F); player.onItemPickup(event.item, size - stack.stackSize); } if (stack.stackSize <= 0) { event.item.setDead(); event.setCanceled(true); } } else { event.setCanceled(true); } } } /** * LEFT_CLICK_BLOCK is only called on the server * RIGHT_CLICK_BLOCK is called on both sides... weird. */ @SubscribeEvent public void onInteract(PlayerInteractEvent event) { ItemStack stack = event.entityPlayer.getHeldItem(); switch(event.action) { case LEFT_CLICK_BLOCK: if (stack != null && stack.getItem() instanceof ISmashBlock && ZSSPlayerInfo.get(event.entityPlayer).getAttackTime() == 0) { if (blockWasSmashed(event.entityPlayer.worldObj, event.entityPlayer, stack, event.pos, event.face)) { if (event.entityPlayer instanceof EntityPlayerMP) { ZSSCombatEvents.setPlayerAttackTime(event.entityPlayer); PacketDispatcher.sendTo(new UnpressKeyPacket(UnpressKeyPacket.LMB), (EntityPlayerMP) event.entityPlayer); } event.useBlock = Result.DENY; } } break; case RIGHT_CLICK_BLOCK: if (stack != null && stack.getItem() instanceof ILiftBlock) { if (blockWasLifted(event.entityPlayer.worldObj, event.entityPlayer, stack, event.pos, event.face)) { event.useBlock = Result.DENY; } } break; default: } } /** * Returns true if the ILiftBlock itemstack was able to pick up the block clicked * and the useBlock result should be denied */ private boolean blockWasLifted(World world, EntityPlayer player, ItemStack stack, BlockPos pos, EnumFacing face) { IBlockState state = world.getBlockState(pos); Block block = state.getBlock(); if (player.canPlayerEdit(pos, face, stack) || block instanceof ILiftable) { boolean isLiftable = block instanceof ILiftable; boolean isValidBlock = (block.isOpaqueCube() || block instanceof BlockBreakable) && Item.getItemFromBlock(block) != null; BlockWeight weight = (isLiftable ? ((ILiftable) block).getLiftWeight(player, stack, state, face) : (Config.canLiftVanilla() ? null : BlockWeight.IMPOSSIBLE)); float strength = ((ILiftBlock) stack.getItem()).getLiftStrength(player, stack, state).weight; float resistance = (weight != null ? weight.weight : (block.getExplosionResistance(world, pos, null, null) * 5.0F/3.0F)); if (isValidBlock && weight != BlockWeight.IMPOSSIBLE && strength >= resistance && (isLiftable || !block.hasTileEntity(state))) { if (world.isRemote) { // Send block's render color to server so held block can render correctly PacketDispatcher.sendToServer(new HeldBlockColorPacket(block.colorMultiplier(world, pos))); } else { ItemStack returnStack = ((ILiftBlock) stack.getItem()).onLiftBlock(player, stack, state); if (returnStack != null && returnStack.stackSize <= 0) { returnStack = null; } ItemStack heldBlock = ItemHeldBlock.getBlockStack(state, returnStack); if (isLiftable) { ((ILiftable) block).onLifted(world, player, heldBlock, pos, state); } player.setCurrentItemOrArmor(0, heldBlock); world.playSoundEffect(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, block.stepSound.getBreakSound(), (block.stepSound.getVolume() + 1.0F) / 2.0F, block.stepSound.getFrequency() * 0.8F); world.setBlockToAir(pos); } return true; } else { WorldUtils.playSoundAtEntity(player, Sounds.GRUNT, 0.3F, 0.8F); } } return false; } /** * Returns true if the ISmashBlock itemstack was able to smash up the block clicked * and the useBlock result should be denied */ private boolean blockWasSmashed(World world, EntityPlayer player, ItemStack stack, BlockPos pos, EnumFacing face) { IBlockState state = world.getBlockState(pos); Block block = state.getBlock(); boolean isSmashable = block instanceof ISmashable; Result smashResult = Result.DEFAULT; boolean wasDestroyed = false; if (player.canPlayerEdit(pos, face, stack) || isSmashable) { BlockWeight weight = (isSmashable ? ((ISmashable) block).getSmashWeight(player, stack, state, face) : (Config.canSmashVanilla() || isVanillaBlockSmashable(block) ? null : BlockWeight.IMPOSSIBLE)); float strength = ((ISmashBlock) stack.getItem()).getSmashStrength(player, stack, state, face).weight; float resistance = (weight != null ? weight.weight : (block.getExplosionResistance(world, pos, null, null) * 5.0F/3.0F)); smashResult = (isSmashable ? ((ISmashable) block).onSmashed(world, player, stack, pos, state, face) : smashResult); if (smashResult == Result.DEFAULT) { boolean isValidBlock = block.isOpaqueCube() || block instanceof BlockBreakable; if (isValidBlock && weight != BlockWeight.IMPOSSIBLE && strength >= resistance && (!block.hasTileEntity(state) || isSmashable)) { if (!(block instanceof BlockBreakable)) { world.playSoundAtEntity(player, Sounds.ROCK_FALL, 1.0F, 1.0F); } if (!world.isRemote) { world.destroyBlock(pos, false); } wasDestroyed = true; } } ((ISmashBlock) stack.getItem()).onBlockSmashed(player, stack, state, face, (smashResult == Result.ALLOW || wasDestroyed)); } return (smashResult == Result.ALLOW || wasDestroyed); } private boolean isVanillaBlockSmashable(Block block) { return block.getMaterial() == Material.glass || block.getMaterial() == Material.ice; } private static void init() { addDrop(EntityCreeper.class, SkillBase.armorBreak); addDrop(EntityIronGolem.class, SkillBase.armorBreak); addDrop(EntitySilverfish.class, SkillBase.dash); addDrop(EntityHorse.class, SkillBase.dash); addDrop(EntityEnderman.class, SkillBase.dodge); addDrop(EntityKeese.class, SkillBase.dodge); addDrop(EntitySpider.class, SkillBase.endingBlow); addDrop(EntityCaveSpider.class, SkillBase.leapingBlow); addDrop(EntityMagmaCube.class, SkillBase.leapingBlow); addDrop(EntityPigZombie.class, SkillBase.parry); addDrop(EntityOcelot.class, SkillBase.parry); addDrop(EntityOctorok.class, SkillBase.risingCut); addDrop(EntityBlaze.class, SkillBase.spinAttack); addDrop(EntityDarknut.class, SkillBase.spinAttack); addDrop(EntityZombie.class, SkillBase.swordBasic); addDrop(EntitySkeleton.class, SkillBase.swordBasic); addDrop(EntityGhast.class, SkillBase.swordBeam); addDrop(EntityWitch.class, SkillBase.swordBeam); addDrop(EntityWizzrobe.class, SkillBase.swordBreak); } }