package net.mcft.copy.backpacks.item;
import java.util.List;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.ISpecialArmor;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.mcft.copy.backpacks.WearableBackpacks;
import net.mcft.copy.backpacks.api.BackpackHelper;
import net.mcft.copy.backpacks.api.IBackpack;
import net.mcft.copy.backpacks.api.IBackpackData;
import net.mcft.copy.backpacks.api.IBackpackType;
import net.mcft.copy.backpacks.client.KeyBindingHandler;
import net.mcft.copy.backpacks.container.ContainerBackpack;
import net.mcft.copy.backpacks.item.IDyeableItem;
import net.mcft.copy.backpacks.misc.BackpackDataItems;
import net.mcft.copy.backpacks.misc.BackpackSize;
import net.mcft.copy.backpacks.misc.util.LangUtils;
import net.mcft.copy.backpacks.misc.util.NbtUtils;
import net.mcft.copy.backpacks.misc.util.WorldUtils;
// TODO: Support armor enchantments like on BetterStorage backpacks? (Delayed to 1.11 version due to lack of enchantment hooks.)
// TODO: Implement additional enchantments?
// - Holding: Increases backpack size (dungeon loot only?)
// - Supply I: Automatically fills up stackable items from backpack
// - Supply II: Automatically replaces broken items (and allow middle click to pull from backpack?)
// - Demand: If a picked up item is stackable and would occupy a new stack in the player's inventory, see
// if there's already a non-full stack of it in the backpack, if so pick it up into the backpack.
public class ItemBackpack extends Item implements IBackpackType, IDyeableItem, ISpecialArmor {
public static final int DEFAULT_COLOR = 0xA06540;
public static final String[] TAG_CUSTOM_ARMOR = { "backpack", "armor" };
public static final String[] TAG_CUSTOM_SIZE = { "backpack", "size" };
public ItemBackpack() {
setUnlocalizedName("wearablebackpacks.backpack");
setMaxStackSize(1);
setCreativeTab(CreativeTabs.TOOLS); // TODO: Use our own creative tab?
}
/** Returns the damage reduction amount. Functions identically to the Vanilla ItemArmor value. */
public int getDamageReductionAmount(ItemStack stack) {
int defaultArmor = WearableBackpacks.CONFIG.backpack.armor.get();
return NbtUtils.get(stack, defaultArmor, TAG_CUSTOM_ARMOR);
}
// Item properties
@Override
@SideOnly(Side.CLIENT)
public void addInformation(ItemStack stack, EntityPlayer playerIn, List<String> tooltip, boolean advanced) {
IBackpack backpack = BackpackHelper.getBackpack(playerIn);
boolean isEquipped = ((backpack != null) && (backpack.getStack() == stack));
// If the shift key is held down, display equip / unequip hints,
// otherwise just display "Hold SHIFT for more info" message.
if (LangUtils.tooltipIsShiftKeyDown(tooltip)) {
boolean equipAsChestArmor = WearableBackpacks.CONFIG.equipAsChestArmor.get();
boolean enableSelfInteraction = WearableBackpacks.CONFIG.enableSelfInteraction.get();
// If own backpacks can be interacted with while equipped and one is either
// currently equipped or won't be equipped as chest armor, display open hint.
// Does not display anything if key is unbound.
if (enableSelfInteraction && (isEquipped || !equipAsChestArmor))
LangUtils.formatTooltipKey(tooltip, "openHint", KeyBindingHandler.openBackpack);
// If the backpack is the player's currently equipped backpack, display unequip hint.
if (isEquipped) LangUtils.formatTooltip(tooltip, "unequipHint");
// If not equipped, display the equip hint. If equipAsChestArmor is off,
// use extended tooltip, which also explains how to unequip the backpack.
else LangUtils.formatTooltip(tooltip, "equipHint" + (!equipAsChestArmor ? ".extended" : ""));
}
// If someone's using the player's backpack right now, display it in the tooltip.
if (isEquipped && (backpack.getPlayersUsing() > 0))
LangUtils.formatTooltipPrepend(tooltip, "\u00A8o", "used");
// Only display the following information if advanced tooltips are enabled.
if (!advanced) return;
NBTBase customSize = NbtUtils.get(stack, TAG_CUSTOM_SIZE);
if (customSize != null)
try { tooltip.add("Custom Size: " + BackpackSize.parse(customSize)); }
catch (Exception ex) { } // Ignore NBT parse exceptions - they're already logged in createBackpackData.
}
// Item events
@Override
public EnumActionResult onItemUse(EntityPlayer player, World worldIn, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) {
IBlockState state = worldIn.getBlockState(pos);
// If the block is replaceable, keep the placing position
// the same but check the block below for solidity.
if (state.getBlock().isReplaceable(worldIn, pos))
state = worldIn.getBlockState(pos.offset(EnumFacing.DOWN));
// Otherwise make sure the top side is used, and
// change the placing position to the block above.
else if (facing == EnumFacing.UP)
pos = pos.offset(EnumFacing.UP);
else return EnumActionResult.FAIL;
// Check if the side is solid and try to place the backpack.
return (state.isSideSolid(worldIn, pos, EnumFacing.UP) &&
BackpackHelper.placeBackpack(worldIn, pos, player.getHeldItem(hand), player, false))
? EnumActionResult.SUCCESS : EnumActionResult.FAIL;
}
// IBackpackType implementation
@Override
public void onSpawnedWith(EntityLivingBase entity, IBackpack backpack) {
// TODO: Fill backpack with random items.
}
@Override
public void onEquip(EntityLivingBase entity, TileEntity tileEntity, IBackpack backpack) { }
@Override
public void onUnequip(EntityLivingBase entity, TileEntity tileEntity, IBackpack backpack) { }
@Override
public void onPlacedInteract(EntityPlayer player, TileEntity tileEntity, IBackpack backpack) {
if (player.world.isRemote) return;
new ContainerBackpack(player, backpack) {
@Override public boolean canInteractWith(EntityPlayer player) {
return (player.isEntityAlive() && !tileEntity.isInvalid() &&
(player.world.getTileEntity(tileEntity.getPos()) == tileEntity) &&
(player.getDistanceSq(tileEntity.getPos()) <= 64));
}
}.open();
}
@Override
public void onEquippedInteract(EntityPlayer player, EntityLivingBase target, IBackpack backpack) {
if (player.world.isRemote) return;
new ContainerBackpack(player, backpack) {
@Override public boolean canInteractWith(EntityPlayer player) {
return BackpackHelper.canInteractWithEquippedBackpack(player, target);
}
}.open();
}
@Override
public void onEquippedTick(EntityLivingBase entity, IBackpack backpack) { }
@Override
public void onDeath(EntityLivingBase entity, IBackpack backpack) {
if (!(backpack.getData() instanceof BackpackDataItems)) return;
BackpackDataItems dataItems = (BackpackDataItems)backpack.getData();
WorldUtils.dropStacksFromEntity(entity, dataItems.items, 4.0F);
}
@Override
public void onEquippedBroken(EntityLivingBase entity, IBackpack backpack) {
onDeath(entity, backpack);
}
@Override
public void onFaultyRemoval(EntityLivingBase entity, IBackpack backpack) {
onDeath(entity, backpack);
}
@Override
public void onBlockBreak(TileEntity tileEntity, IBackpack backpack) {
if (!(backpack.getData() instanceof BackpackDataItems)) return;
BackpackDataItems dataItems = (BackpackDataItems)backpack.getData();
WorldUtils.dropStacksFromBlock(tileEntity, dataItems.items);
}
@Override
public IBackpackData createBackpackData(ItemStack stack) {
BackpackSize size = WearableBackpacks.CONFIG.backpack.size.get();
// Custom size can be specified in stack's NBT data.
NBTBase customSize = NbtUtils.get(stack, TAG_CUSTOM_SIZE);
if (customSize != null)
try { size = BackpackSize.parse(customSize); }
catch (Exception ex) { WearableBackpacks.LOG.error(
"Error trying to deserialize backpack size from custom size NBT tag.", ex); }
return new BackpackDataItems(size);
}
// ISpecialArmor implementation
public ArmorProperties getProperties(EntityLivingBase player, ItemStack armor, DamageSource source, double damage, int slot) {
if (source.isUnblockable()) return new ArmorProperties(0, 0.0, 0);
int reductionAmount = ((ItemBackpack)armor.getItem()).getDamageReductionAmount(armor);
int maxDamage = armor.getMaxDamage() + 1 - armor.getItemDamage();
return new ArmorProperties(0, reductionAmount / 25.0, maxDamage);
}
public int getArmorDisplay(EntityPlayer player, ItemStack armor, int slot) {
return ((ItemBackpack)armor.getItem()).getDamageReductionAmount(armor);
}
public void damageArmor(EntityLivingBase entity, ItemStack stack,
DamageSource source, int damage, int slot) {
// TODO: There's still a lack of sound / particles when armor breaks in 1.11..?
stack.damageItem(damage, entity);
if (!stack.isEmpty()) return;
// If backpack breaks while equipped, call onEquippedBroken.
IBackpack backpack = BackpackHelper.getBackpack(entity);
if (backpack == null) return;
backpack.getType().onEquippedBroken(entity, backpack);
}
}