/**
Copyright (C) <2017> <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.item;
import java.util.Collection;
import java.util.List;
import com.google.common.collect.Lists;
import net.minecraft.client.resources.model.IBakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.entity.Entity;
import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.StatCollector;
import net.minecraft.village.MerchantRecipe;
import net.minecraft.village.MerchantRecipeList;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import zeldaswordskills.api.entity.BombType;
import zeldaswordskills.api.item.IUnenchantable;
import zeldaswordskills.client.ISwapModel;
import zeldaswordskills.client.render.item.ModelItemBombBag;
import zeldaswordskills.creativetab.ZSSCreativeTabs;
import zeldaswordskills.entity.projectile.EntityBomb;
import zeldaswordskills.ref.ModInfo;
import zeldaswordskills.util.MerchantRecipeHelper;
import zeldaswordskills.util.PlayerUtils;
import zeldaswordskills.util.WorldUtils;
/**
*
* A bag for holding bombs. The only means of obtaining this special bag is by finding it in
* the world, or by purchasing one from a Priest for a hefty fee.
*
* While held and so long as the bag is not full, any bombs in the player's inventory will be
* stored automatically. Right-clicking will remove a bomb from the bag and place it in the
* player's hands; 'b' can be pressed at any time to grab a bomb from the bag, even if it is
* not currently being held.
*
* If right-clicked while sneaking and there is another bomb bag with initial capacity
* in the inventory that is either empty or contains the same type of bombs, the two
* bags will combine resulting in a bag with increased carrying capacity.
*
* Left-clicking on a ticking bomb with the bag will turn the bomb into an item that can be
* picked up. Careful not to get blown up while trying!
*
* NOTE: If in creative mode with full inventory and the player right-clicks while holding the
* bag, it will simply be removed rather than dropping to the ground. This is vanilla behavior.
*
*/
public class ItemBombBag extends BaseModItem implements ISwapModel, IUnenchantable
{
private static final int BASE_CAPACITY = 10, MAX_CAPACITY = 50;
public ItemBombBag() {
super();
setMaxDamage(0);
setMaxStackSize(1);
setCreativeTab(ZSSCreativeTabs.tabTools);
}
@Override
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
if (player.capabilities.isCreativeMode || removeBomb(stack)) {
if (!player.inventory.addItemStackToInventory(stack)) {
player.dropPlayerItemWithRandomChoice(stack, false);
}
int type = getBagBombType(stack);
return new ItemStack(ZSSItems.bomb, 1, (type > 0 ? type : 0));
}
return stack;
}
@Override
public boolean onLeftClickEntity(ItemStack stack, EntityPlayer player, Entity entity) {
if (entity instanceof EntityBomb) {
return ((EntityBomb) entity).disarm(entity.worldObj);
} else if (entity instanceof EntityVillager && !player.worldObj.isRemote) {
EntityVillager villager = (EntityVillager) entity;
MerchantRecipeList trades = villager.getRecipes(player);
if (villager.getProfession() != 1 && trades != null) {
MerchantRecipe trade = new MerchantRecipe(stack.copy(), new ItemStack(Items.emerald, 16));
if (player.worldObj.rand.nextFloat() < 0.2F && MerchantRecipeHelper.addToListWithCheck(trades, trade)) {
PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.generic.sell.1");
} else {
PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.generic.sorry.1");
}
} else {
PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.generic.sorry.0");
}
}
return true;
}
/**
* While held, the bomb bag will scan the player's inventory for bombs and attempt to store them
* @param slot inventory slot at which the item resides
* @param isHeld true if the item is currently held
*/
@Override
public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean isHeld) {
if (!world.isRemote && isHeld && getBombsHeld(stack) < getCapacity(stack) && entity instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) entity;
for (int i = 0; i < player.inventory.getSizeInventory(); ++i) {
ItemStack invStack = player.inventory.getStackInSlot(i);
if (invStack != null && areMatchingTypes(stack, invStack, true)) {
if (addBombs(stack, invStack) < 1) {
player.inventory.setInventorySlotContents(i, null);
if (getBombsHeld(stack) == getCapacity(stack)) {
return;
}
}
}
}
}
}
@Override
@SideOnly(Side.CLIENT)
public void addInformation(ItemStack stack, EntityPlayer player, List<String> list, boolean advanced) {
list.add(EnumChatFormatting.ITALIC + StatCollector.translateToLocal("tooltip.zss.bomb_bag.desc.0"));
list.add(EnumChatFormatting.ITALIC + StatCollector.translateToLocal("tooltip.zss.bomb_bag.desc.1"));
list.add(EnumChatFormatting.ITALIC + StatCollector.translateToLocal("tooltip.zss.bomb_bag.desc.2"));
int held = getBombsHeld(stack);
int i = getBagBombType(stack);
BombType type = (held > 0 && i > 0) ? BombType.values()[i % BombType.values().length] : BombType.BOMB_STANDARD;
String bombName = StatCollector.translateToLocal("item.zss.bomb_" + type.unlocalizedName + ".name");
list.add(EnumChatFormatting.BOLD + StatCollector.translateToLocalFormatted("tooltip.zss.bomb_bag.desc.bombs", bombName, held, getCapacity(stack)));
}
/**
* Attempts to add an amount of bombs to the bag, returning any that wouldn't fit
* @param amount can be either positive or negative
* @return the number of bombs that could not be added / removed, if any
*/
public int addBombs(ItemStack stack, int amount) {
int bombs = getBombsHeld(stack) + amount;
if (amount < 0) {
stack.getTagCompound().setInteger("bombs", Math.max(0, bombs));
if (bombs < 1) {
setBagBombType(stack, -1);
}
return -Math.min(0, bombs);
}
int capacity = getCapacity(stack);
stack.getTagCompound().setInteger("bombs", Math.min(bombs, capacity));
return -Math.min(0, capacity - bombs);
}
/**
* ItemStack sensitive version for setting bag's type when adding bombs
* @return the number of bombs that wouldn't fit, if any (usually returns a negative value)
*/
public int addBombs(ItemStack bag, ItemStack bombs) {
if (ItemBomb.getType(bombs) == BombType.BOMB_FLOWER) {
return bombs.stackSize; // can't put bomb flowers in bomb bags
} else if (areMatchingTypes(bag, bombs, true)) {
int remaining = addBombs(bag, bombs.stackSize);
setBagBombType(bag, ItemBomb.getType(bombs).ordinal());
return remaining;
} else {
return bombs.stackSize;
}
}
/**
* Returns true if a bomb was removed from the bag
*/
public boolean removeBomb(ItemStack stack) {
if (getBombsHeld(stack) > 0) {
addBombs(stack, -1);
return true;
} else {
return false;
}
}
/**
* Empties the entire contents of the bomb bag into the player's inventory
* or onto the ground if there is no more room
*/
public void emptyBag(ItemStack stack, EntityPlayer player) {
int n = getBombsHeld(stack);
int type = getBagBombType(stack);
if (type < 0 || n < 1) { return; }
ItemStack newBag = new ItemStack(ZSSItems.bombBag);
setCapacity(newBag, getCapacity(stack));
if (player.inventory.addItemStackToInventory(newBag)) {
player.setCurrentItemOrArmor(0, null);
while (n-- > 0) {
ItemStack bomb = new ItemStack(ZSSItems.bomb, 1, type);
if (!player.inventory.addItemStackToInventory(bomb)) {
WorldUtils.spawnItemWithRandom(player.worldObj, bomb, player.posX, player.posY, player.posZ);
}
}
}
}
/**
* Returns max storage capacity for this bag
*/
public int getCapacity(ItemStack stack) {
return getCapacity(stack, false);
}
/**
* Returns either the true NBT capacity for this bag, or the adjusted max capacity
*/
public int getCapacity(ItemStack stack, boolean trueCapacity) {
int capacity = (stack.hasTagCompound() ? Math.max(BASE_CAPACITY, stack.getTagCompound().getInteger("capacity")) : BASE_CAPACITY);
int type = getBagBombType(stack);
return (trueCapacity || type == -1 || type == BombType.BOMB_STANDARD.ordinal()) ? capacity : capacity / 2;
}
/**
* Set's the stacks capacity to 'size' or MAX_CAPACITY, whichever is smaller
*/
public void setCapacity(ItemStack stack, int size) {
verifyNBT(stack);
stack.getTagCompound().setInteger("capacity", Math.min(size, MAX_CAPACITY));
}
/**
* Returns number of bombs held in this bag
*/
public int getBombsHeld(ItemStack stack) {
verifyNBT(stack); // fixes bags from creative tab not having nbt tag
// fix for reports of ArrayIndexOutOfBoundsException from bombs held < 0
int bombsHeld = (stack.hasTagCompound() ? stack.getTagCompound().getInteger("bombs") : 0);
if (bombsHeld < 0) {
stack.getTagCompound().setInteger("bombs", 0);
}
return (bombsHeld < 0 ? 0 : bombsHeld);
}
/**
* Returns the ordinal value of the type of bomb held, or -1 if no current type
*/
public int getBagBombType(ItemStack stack) {
return (stack.hasTagCompound() ? stack.getTagCompound().getInteger("type") : -1);
}
/**
* Sets the bags current type
*/
public void setBagBombType(ItemStack bag, int type) {
verifyNBT(bag);
bag.getTagCompound().setInteger("type", type);
}
/**
* Returns true if the stack is a bomb or a bomb bag and its type matches the
* type currently stored in the bag, or true if no bombs are currently stored
* @param isBomb true if searching for a bomb and not a bomb bag
*/
public boolean areMatchingTypes(ItemStack bag, ItemStack stack, boolean isBomb) {
int type = getBagBombType(bag);
if (isBomb && stack.getItem() instanceof ItemBomb) {
return getBombsHeld(bag) == 0 || type == -1 || type == ItemBomb.getType(stack).ordinal();
} else if (!isBomb && stack.getItem() instanceof ItemBombBag) {
int type2 = getBagBombType(stack);
return getBombsHeld(bag) == 0 || type == -1 || getBombsHeld(stack) == 0 || type == type2 || type2 == -1;
} else {
return false;
}
}
/**
* Returns true if the two stacks can be combined into one bomb bag with increased capacity
*/
public boolean canCombine(ItemStack bag, ItemStack stack) {
if (stack == null || stack == bag || !areMatchingTypes(bag, stack, false)) {
return false;
}
return (getCapacity(bag, true) + getCapacity(stack, true)) <= MAX_CAPACITY;
}
/**
* Ensures stack has correctly formatted NBT tag; if not, one is created
*/
private void verifyNBT(ItemStack stack) {
if (!stack.hasTagCompound()) {
stack.setTagCompound(new NBTTagCompound());
stack.getTagCompound().setInteger("bombs", 0);
stack.getTagCompound().setInteger("type", -1);
stack.getTagCompound().setInteger("capacity", BASE_CAPACITY);
}
}
@Override
@SideOnly(Side.CLIENT)
public Collection<ModelResourceLocation> getDefaultResources() {
String name = getUnlocalizedName();
name = ModInfo.ID + ":" + name.substring(name.lastIndexOf(".") + 1);
return Lists.newArrayList(new ModelResourceLocation(name, "inventory"));
}
@Override
@SideOnly(Side.CLIENT)
public Class<? extends IBakedModel> getNewModel() {
return ModelItemBombBag.class;
}
}