/** 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.util; import java.util.UUID; import mods.battlegear2.api.core.IBattlePlayer; import mods.battlegear2.api.shield.IShield; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemSword; import net.minecraft.util.ChatComponentTranslation; import zeldaswordskills.ZSSMain; import zeldaswordskills.api.item.IWeapon; import zeldaswordskills.api.item.WeaponRegistry; import zeldaswordskills.item.ItemZeldaShield; import zeldaswordskills.item.ItemZeldaSword; import zeldaswordskills.network.PacketDispatcher; import zeldaswordskills.network.bidirectional.PlaySoundPacket; /** * * A collection of utility methods related to the player * */ public class PlayerUtils { /** Copy of Item#field_111210_e which is the UUID used to store weapon damage modifiers */ public static final UUID itemDamageUUID = UUID.fromString("CB3F55D3-645C-4F38-A497-9C13A33DB5CF"); /** * Returns whether the player is blocking, accounting for possibility of Battlegear2 shield item use */ public static boolean isBlocking(EntityPlayer player) { if (player.isBlocking()) { return true; } else if (ZSSMain.isBG2Enabled) { return ((IBattlePlayer) player).isBattlemode() && ((IBattlePlayer) player).isBlockingWithShield(); } return false; } /** * Returns whether the ItemStack (possibly null) is some kind of shield */ public static boolean isShield(ItemStack stack) { if (stack == null) { return false; } else if (ZSSMain.isBG2Enabled && stack.getItem() instanceof IShield) { return true; } return stack.getItem() instanceof ItemZeldaShield; } /** * Returns true if the player is holding any type of item which grants a damage bonus, * including from tools such as axes, shovels, etc. */ public static boolean isHoldingWeapon(EntityPlayer player) { AttributeModifier itemDamage = player.getAttributeMap().getAttributeInstance(SharedMonsterAttributes.attackDamage).getModifier(itemDamageUUID); return itemDamage != null && itemDamage.getAmount() > 0; } /** * Returns true if the item is a sword: i.e. if it is an {@link ItemSword}, * an {@link IWeapon} (returns {@link IWeapon#isSword(ItemStack)}), * or registered to the {@link WeaponRegistry} as a sword */ public static boolean isSword(ItemStack stack) { if (stack == null) { return false; } else if (stack.getItem() instanceof IWeapon) { return ((IWeapon) stack.getItem()).isSword(stack); } return WeaponRegistry.INSTANCE.isSword(stack.getItem()); } /** * Returns true if the item is any kind of weapon: a {@link #isSword(ItemStack) sword}, * an {@link IWeapon}, or registered to the {@link WeaponRegistry} as a weapon */ public static boolean isWeapon(ItemStack stack) { if (stack == null) { return false; } else if (stack.getItem() instanceof IWeapon) { return ((IWeapon) stack.getItem()).isWeapon(stack); } return (isSword(stack) || WeaponRegistry.INSTANCE.isWeapon(stack.getItem())); } /** Returns true if the entity is currently holding a Zelda-specific sword */ public static boolean isHoldingZeldaSword(EntityLivingBase entity) { return (entity.getHeldItem() != null && entity.getHeldItem().getItem() instanceof ItemZeldaSword); } /** Returns true if the entity is currently holding a Master sword */ public static boolean isHoldingMasterSword(EntityLivingBase entity) { return (isHoldingZeldaSword(entity) && ((ItemZeldaSword) entity.getHeldItem().getItem()).isMasterSword()); } /** * Returns true if the player has any type of master sword somewhere in the inventory */ public static boolean hasMasterSword(EntityPlayer player) { for (ItemStack stack : player.inventory.mainInventory) { if (stack != null && stack.getItem() instanceof ItemZeldaSword && ((ItemZeldaSword) stack.getItem()).isMasterSword()) { return true; } } return false; } /** * Returns true if the player has the Item somewhere in the inventory, * ignoring the stack's damage value */ public static boolean hasItem(EntityPlayer player, Item item) { return hasItem(player, item, -1); } /** * Subtype sensitive version of {@link #hasItem(EntityPlayer, Item) hasItem}, * checks Item and damage values, ignoring stack size and NBT. */ public static boolean hasItem(EntityPlayer player, ItemStack stack) { return hasItem(player, stack.getItem(), stack.getItemDamage()); } /** * Returns true if the player has the Item somewhere in the inventory, with * optional metadata for subtyped items * @param meta use -1 to ignore the stack's damage value */ public static boolean hasItem(EntityPlayer player, Item item, int meta) { for (ItemStack stack : player.inventory.mainInventory) { if (stack != null && stack.getItem() == item) { if (meta == -1 || stack.getItemDamage() == meta) { return true; } } } return false; } /** Returns the difference between player's max and current health */ public static float getHealthMissing(EntityPlayer player) { return player.capabilities.isCreativeMode ? 0.0F : (player.getMaxHealth() - player.getHealth()); } /** * Adds the stack to the player's inventory or, failing that, drops it as an EntityItem */ public static void addItemToInventory(EntityPlayer player, ItemStack stack) { if (!player.inventory.addItemStackToInventory(stack)) { player.dropPlayerItemWithRandomChoice(stack, false); } } /** * Attempts to consume the amount given from the player's held item stack only; * does not check stack damage. In Creative, acts like hasItem (does not consume). */ public static boolean consumeHeldItem(EntityPlayer player, Item item, int amount) { return consumeHeldItem(player, item, -1, amount); } /** * Attempts to consume the amount given from the player's held item stack only. * In Creative, acts like hasItem (does not consume). * @param damage Required stack damage to match, or -1 to not check damage */ public static boolean consumeHeldItem(EntityPlayer player, Item item, int damage, int amount) { if (amount < 1) { return false; } ItemStack stack = player.getHeldItem(); if (stack == null || stack.getItem() != item || stack.stackSize < amount) { return false; } else if (damage > -1 && stack.getItemDamage() != damage) { return false; } else if (player.capabilities.isCreativeMode) { return true; } else { stack.stackSize -= amount; if (stack.stackSize < 1) { stack = null; player.setCurrentItemOrArmor(0, null); } return true; } } /** * Returns true if the required number of item were removed from the player's inventory; * if the entire quantity is not present, then no items are removed. * In Creative, acts like hasItem (does not consume). */ public static boolean consumeInventoryItem(EntityPlayer player, Item item, int required) { return consumeInventoryItem(player, item, 0, required); } /** * Calls {@link #consumeInventoryItem} with the stack's item and damage value */ public static boolean consumeInventoryItem(EntityPlayer player, ItemStack stack, int required) { return consumeInventoryItem(player, stack.getItem(), stack.getItemDamage(), required); } /** * A metadata-sensitive version of {@link InventoryPlayer#consumeInventoryItem(int)} * In Creative, acts like hasItem (does not consume). * @param item The type of item to consume * @param meta The required damage value of the stack * @param required The number of such items to consume * @return True if the entire amount was consumed; if this is not possible, no items are consumed and it returns false */ public static boolean consumeInventoryItem(EntityPlayer player, Item item, int meta, int required) { if (required < 1) { return false; } // decremented until it reaches zero, meaning the entire required amount was consumed int consumed = required; for (int i = 0; i < player.inventory.getSizeInventory() && consumed > 0; ++i) { ItemStack invStack = player.inventory.getStackInSlot(i); if (invStack != null && invStack.getItem() == item && invStack.getItemDamage() == meta) { if (invStack.stackSize <= consumed) { consumed -= invStack.stackSize; if (!player.capabilities.isCreativeMode) { player.inventory.setInventorySlotContents(i, null); } } else { if (!player.capabilities.isCreativeMode) { invStack = invStack.splitStack(invStack.stackSize - consumed); player.inventory.setInventorySlotContents(i, invStack); } consumed = 0; } } } if (consumed > 0 && !player.capabilities.isCreativeMode) { player.inventory.addItemStackToInventory(new ItemStack(item, required - consumed, meta)); } return consumed == 0; } /** Sends a translated chat message with optional arguments to the player */ public static void sendTranslatedChat(EntityPlayer player, String message, Object... args) { player.addChatMessage(new ChatComponentTranslation(message, args)); } /** * Sends a packet to the client to play a sound on the client side only, or * sends a packet to the server to play a sound on the server for all to hear. * To avoid playing a sound twice, only call the method from one side or the other, not both. */ public static void playSound(EntityPlayer player, String sound, float volume, float pitch) { if (player.worldObj.isRemote) { PacketDispatcher.sendToServer(new PlaySoundPacket(sound, volume, pitch, player)); } else if (player instanceof EntityPlayerMP) { PacketDispatcher.sendTo(new PlaySoundPacket(sound, volume, pitch), (EntityPlayerMP) player); } } /** * Plays a sound at the player's position with randomized volume and pitch. * Sends a packet to the client to play a sound on the client side only, or * sends a packet to the server to play a sound on the server for all to hear. * * To avoid playing a sound twice, only call the method from one side or the * other, not both. To play a sound directly on the server, use * {@link WorldUtils#playSoundAtEntity} instead. * * @param f Volume: nextFloat() * f + add * @param add Pitch: 1.0F / (nextFloat() * f + add) */ public static void playRandomizedSound(EntityPlayer player, String sound, float f, float add) { float volume = player.worldObj.rand.nextFloat() * f + add; float pitch = 1.0F / (player.worldObj.rand.nextFloat() * f + add); playSound(player, sound, volume, pitch); } }