package gr8pefish.ironbackpacks.util.helpers;
import gr8pefish.ironbackpacks.api.items.backpacks.interfaces.IBackpack;
import gr8pefish.ironbackpacks.api.items.backpacks.interfaces.IUpgradableBackpack;
import gr8pefish.ironbackpacks.api.register.ItemIUpgradeRegistry;
import gr8pefish.ironbackpacks.capabilities.player.PlayerDeathBackpackCapabilities;
import gr8pefish.ironbackpacks.capabilities.player.PlayerWearingBackpackCapabilities;
import gr8pefish.ironbackpacks.config.ConfigHandler;
import gr8pefish.ironbackpacks.items.upgrades.UpgradeMethods;
import gr8pefish.ironbackpacks.network.NetworkingHandler;
import gr8pefish.ironbackpacks.network.client.ClientEquippedPackMessage;
import gr8pefish.ironbackpacks.network.client.ClientEquippedPackPlayerSensitiveMessage;
import gr8pefish.ironbackpacks.registry.ItemRegistry;
import gr8pefish.ironbackpacks.util.IronBackpacksConstants;
import gr8pefish.ironbackpacks.util.NBTUtils;
import net.minecraft.entity.EntityTracker;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.EnumHand;
import net.minecraftforge.oredict.OreDictionary;
import java.util.ArrayList;
/**
* Houses helper methods used throughout the mod
*/
public class IronBackpacksHelper {
//======================================================= Gets the player's relevant backpack ===============================================
/**
* Gets the backpack to open. Checks for the current backpack first, then checks for an equipped backpack, and finally checks the player's inventory.
* @param player - the player with the backpack
* @return - null if it can't be found, the itemstack otherwise
*/
public static ItemStack getBackpack(EntityPlayer player) {
ItemStack backpack;
ItemStack currPack = PlayerWearingBackpackCapabilities.getCurrentBackpack(player);
if (currPack != null) {
backpack = currPack;
}else if(PlayerWearingBackpackCapabilities.getEquippedBackpack(player)!= null){
backpack = PlayerWearingBackpackCapabilities.getEquippedBackpack(player);
}else {
backpack = getBackpackFromPlayersInventory(player);
}
if (!player.worldObj.isRemote && backpack != null)
NBTUtils.setUUID(backpack);
return backpack;
}
/**
* Gets the backpack form the player's inventory. WARNING: won't get the equipped backpack.
* @param player - the player with the backpack
* @return - null if nothing can be found, the itemstack otherwise
*/
public static ItemStack getBackpackFromPlayersInventory(EntityPlayer player){
ItemStack backpack = null;
if (player.getHeldItemMainhand() != null && player.getHeldItemMainhand().getItem() instanceof IBackpack) {
backpack = player.getHeldItemMainhand();
} else {
for (int i = 0; i < player.inventory.getSizeInventory(); i++) {
ItemStack stack = player.inventory.getStackInSlot(i);
if (stack != null && stack.getItem() != null && stack.getItem() instanceof IBackpack) {
backpack = player.inventory.getStackInSlot(i);
}
}
}
if (!player.worldObj.isRemote && backpack != null) {
NBTUtils.setUUID(backpack);
}
return backpack;
}
//============================================ Methods relating to Upgrades Applied ======================================================
/**
* Get upgrades stored in the backpack's NBT data
// * @param stack - the backpack to check
* @return - an int[] of the upgrades applied (only contains what is applied, no empty values)
*/
public static ArrayList<ItemStack> getUpgradesAppliedFromNBT(ItemStack backpack) {
ArrayList<ItemStack> upgradesArrayList = new ArrayList<>();
if (backpack != null) {
NBTTagCompound nbtTagCompound = backpack.getTagCompound();
if (nbtTagCompound != null) {
if(nbtTagCompound.hasKey(IronBackpacksConstants.NBTKeys.UPGRADES)) {
NBTTagList tagList = nbtTagCompound.getTagList(IronBackpacksConstants.NBTKeys.UPGRADES, net.minecraftforge.common.util.Constants.NBT.TAG_COMPOUND);
for (int i = 0; i < tagList.tagCount(); i++) {
NBTTagCompound stackTag = tagList.getCompoundTagAt(i);
ItemStack upgrade = ItemStack.loadItemStackFromNBT(stackTag);
if (upgrade != null)
upgradesArrayList.add(upgrade);
}
}
}
}
return upgradesArrayList;
}
/**
* Get the number of upgrade points used.
* @param upgrades - the upgrades to check
* @return - integer value
*/
public static int getUpgradePointsUsed(ArrayList<ItemStack> upgrades){
int counter = 0;
for (ItemStack stack : upgrades){
counter += ItemIUpgradeRegistry.getItemUpgrade(stack).getUpgradeCost(stack);
}
return counter;
}
/**
* Returns the total possible upgrade points available.
* @param stack - the itemstack to check
* @return - integer value
*/
public static int getTotalUpgradePointsFromNBT(ItemStack stack){
IUpgradableBackpack backpack = (IUpgradableBackpack) stack.getItem();
int upgradeCount = backpack.getUpgradePoints(stack); //from initialization via config
int extraPoints = getAdditionalUpgradesUpgradeCount(stack);
return (upgradeCount + extraPoints);
}
/**
* Gets how many 'additional upgrade' points have been applied to the backpack.
* @param stack - the backpack
* @return - integer value
*/
public static int getAdditionalUpgradesUpgradeCount(ItemStack stack){
if (stack != null) {
NBTTagCompound nbtTagCompound = stack.getTagCompound();
if (nbtTagCompound != null) {
if (nbtTagCompound.hasKey(IronBackpacksConstants.NBTKeys.ADDITIONAL_POINTS)) {
return nbtTagCompound.getIntArray(IronBackpacksConstants.NBTKeys.ADDITIONAL_POINTS)[0]; //[pointsAdded, upgradesApplied]
}
}
}
return 0;
}
/**
* Gets how many 'additional upgrade' upgrades have been applied to the backpack.
* @param stack - the backpack
* @return - integer value
*/
public static int getAdditionalUpgradesTimesApplied(ItemStack stack){
if (stack != null) {
NBTTagCompound nbtTagCompound = stack.getTagCompound();
if (nbtTagCompound != null) {
if (nbtTagCompound.hasKey(IronBackpacksConstants.NBTKeys.ADDITIONAL_POINTS)) {
return nbtTagCompound.getIntArray(IronBackpacksConstants.NBTKeys.ADDITIONAL_POINTS)[1]; //[pointsAdded, upgradesApplied]
}
}
}
return 0;
}
//==============================================================================Equipping/Removing backpack========================================================
public static void equipBackpackFromKeybinding(EntityPlayer player) {
ItemStack backpack = PlayerWearingBackpackCapabilities.getEquippedBackpack(player);
if (backpack != null) { //need to unequip backpack
//if hotbar isn't full
//if currently selected slot is empty
//put backpack there
//else
//put backpack in first available empty slot
//else if hotbar is full
//if offhand is empty
//put item in offhand, put backpack in mainhand
//otherwise
//if have other empty slot in inventory
//move currently selected item to somewhere in inventory, put backpack in main hand
boolean hasEmptyHotbarSlot = false;
boolean hasEmptyOffhand = false;
boolean hasEmptyInventorySlot = false;
for (int i = 0; i < 9; i++) {
if (player.inventory.getStackInSlot(i) == null) hasEmptyHotbarSlot = true;
}
if (hasEmptyHotbarSlot) {
if (player.getHeldItemMainhand() == null) {
player.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, backpack);
} else {
//take off backpack and put in player's inventory
player.inventory.addItemStackToInventory(backpack);
}
} else {
if (player.getHeldItemOffhand() == null) hasEmptyOffhand = true;
if (hasEmptyOffhand) {
ItemStack selected = player.getHeldItem(EnumHand.MAIN_HAND);
//put item in offhand
player.setItemStackToSlot(EntityEquipmentSlot.OFFHAND, selected);
//put backpack in mainhand
player.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, backpack);
} else {
for (int i = 9; i < player.inventory.getSizeInventory() - 5; i++) { //don't care about armor slots or offhand
if (player.inventory.getStackInSlot(i) == null) hasEmptyInventorySlot = true;
}
if (hasEmptyInventorySlot) {
ItemStack selected = player.getHeldItem(EnumHand.MAIN_HAND).copy();
//put backpack in mainhand
player.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, backpack);
//put item in offhand
player.inventory.addItemStackToInventory(selected);
}
}
}
if (hasEmptyHotbarSlot || hasEmptyOffhand || hasEmptyInventorySlot) { //backpack was unequipped, need to "save"
//update equipped backpack to null
PlayerWearingBackpackCapabilities.setEquippedBackpack(player, null);
//update equipped backpack on client side, not ideal but it works
NetworkingHandler.network.sendTo(new ClientEquippedPackMessage(null), (EntityPlayerMP)player);
//update backpacks for multiplayer
EntityTracker tracker = ((EntityPlayerMP) player).worldObj.getMinecraftServer().worldServerForDimension(player.dimension).getEntityTracker();
tracker.sendToAllTrackingEntity(player, NetworkingHandler.network.getPacketFrom(new ClientEquippedPackPlayerSensitiveMessage(player.getEntityId(), null)));
}
}
else if (!ConfigHandler.disableEquipping && player.getHeldItemMainhand() != null && player.getHeldItemMainhand().getItem() instanceof IBackpack) { //need to equip backpack
ItemStack backpackStack = player.getHeldItemMainhand();
NBTUtils.setUUID(backpackStack);
//equip backpack from the backpack the player is holding
PlayerWearingBackpackCapabilities.setEquippedBackpack(player, backpackStack);
//update equipped backpack on client side, not ideal but it works
NetworkingHandler.network.sendTo(new ClientEquippedPackMessage(backpackStack), (EntityPlayerMP)player); //works on SSP
//delete the held items
player.inventory.setInventorySlotContents(player.inventory.currentItem, null);
//update backpacks for multiplayer
EntityTracker tracker = ((EntityPlayerMP) player).worldObj.getMinecraftServer().worldServerForDimension(player.dimension).getEntityTracker();
tracker.sendToAllTrackingEntity(player, NetworkingHandler.network.getPacketFrom(new ClientEquippedPackPlayerSensitiveMessage(player.getEntityId(), backpackStack)));
}
}
//==================================================== Handles the backpack persisting through death ==============================================================
/**
* Saves the equipped backpacks when the player dies. Does a bunch of logic, outlined below.
*
* Equipped backpacks:
* !Eternity
* !KeepInvTrue
* Add to drops
*
* @param player - the dying player
* @return null if no drop, the backpack as an itemstack otherwise
*/
public static EntityItem savePlayerDeathDrops(EntityPlayer player) {
EntityItem entityItem = null; //the equipped backpack to drop
boolean gameruleKeepInv = player.worldObj.getGameRules().getBoolean("keepInventory");
ArrayList<ItemStack> backpacks = new ArrayList<>(); //the backpacks to save
boolean shouldStorePack = false; //to store the pack in the inventory for a "normal death"
boolean stored = false; //the act of storing it
ItemStack packToStore = null;
//ToDo: Try with gravestone mods and get it working
//test with full inventories as well
//deal with dropping the equipped pack
ItemStack equippedPack = PlayerWearingBackpackCapabilities.getEquippedBackpack(player);
if (equippedPack != null) {
if (!UpgradeMethods.hasEternityUpgrade(getUpgradesAppliedFromNBT(equippedPack))) {
if (!gameruleKeepInv) {
ItemStack deathEquipped = PlayerDeathBackpackCapabilities.getEquippedBackpack(player);
if (deathEquipped == null || (!deathEquipped.equals(equippedPack))) { //for when an eternity backpack recently lost its upgrade and then this fires right after
entityItem = new EntityItem(player.worldObj, player.posX, player.posY, player.posZ, equippedPack); //works
PlayerDeathBackpackCapabilities.setEquippedBackpack(player, null); //removes backpack if it was present before
}
}
}
}
return entityItem;
}
/**
* Saves the inventory eternity backpack(s) to the player so that they aren't lost.
* Used because it fires before everything is dropped, which is necessary for iterating through inventory.
*
* Equipped backpacks:
* Eternity
* KeepInvTrue
* Save capEquipped
* !KeepInvTrue
* Remove Eternity upgrade
* Save capEquipped
* !Eternity
* KeepInvTrue
* Save capEquipped
*
* Normal backpacks:
* Eternity
* KeepInvTrue
* Nothing
* !KeepInvTrue
* Remove Eternity upgrade
* Save capDeath
* !Eternity
* KeepInvTrue
* Nothing
* !KeepInvTrue
* Nothing
*
* @param player - the player who died with the backpack
*/
public static void saveEternityBackpacksOnDeath(EntityPlayer player) {
ArrayList<ItemStack> backpacks = new ArrayList<>(); //the backpacks to save
boolean gameruleKeepInv = player.worldObj.getGameRules().getBoolean("keepInventory");
//deal with equipped packs (if you keep them through death)
ItemStack equippedPack = PlayerWearingBackpackCapabilities.getEquippedBackpack(player);
if (equippedPack != null) {
if (UpgradeMethods.hasEternityUpgrade(getUpgradesAppliedFromNBT(equippedPack))) {
if (gameruleKeepInv) { //works
PlayerDeathBackpackCapabilities.setEquippedBackpack(player, equippedPack.copy());
} else { //works
ItemStack updatedEquippedPack = removeEternityUpgrade(getUpgradesAppliedFromNBT(equippedPack), equippedPack); //remove upgrade
PlayerDeathBackpackCapabilities.setEquippedBackpack(player, updatedEquippedPack);
}
} else {
if (gameruleKeepInv) { //works
PlayerDeathBackpackCapabilities.setEquippedBackpack(player, equippedPack.copy());
}
}
}
//deal with storing other packs (only care if they have the eternity upgrade)
for (int i = 0; i < player.inventory.getSizeInventory(); i++) {
ItemStack tempStack = player.inventory.getStackInSlot(i);
if (tempStack != null) {
if (tempStack.getItem() instanceof IBackpack) {
if (UpgradeMethods.hasEternityUpgrade(getUpgradesAppliedFromNBT(tempStack))) {
if (!gameruleKeepInv) {
ItemStack stackToAdd = removeEternityUpgrade(getUpgradesAppliedFromNBT(tempStack), tempStack); //removes upgrade
backpacks.add(stackToAdd); //works
player.inventory.setInventorySlotContents(i, null); //set to null so it doesn't drop
}
}
}
}
}
PlayerDeathBackpackCapabilities.setEternityBackpacks(player, backpacks);
}
/**
* Loads the backpack(s) that need to be 'respawned' in.
* @param player - the player who lads the packs in
*/
public static void loadBackpackOnDeath(EntityPlayer player) {
//get equipped pack
ItemStack equipped = PlayerDeathBackpackCapabilities.getEquippedBackpack(player);
//respawn it
if (equipped != null) {
NetworkingHandler.network.sendTo(new ClientEquippedPackMessage(equipped), (EntityPlayerMP) player); //update client on correct pack
PlayerWearingBackpackCapabilities.setEquippedBackpack(player, equipped); //update server on correct pack
}
//get eternity packs and add them to inventory
ArrayList<ItemStack> packs = PlayerDeathBackpackCapabilities.getEternityBackpacks(player);
if (packs != null && !packs.isEmpty()) {
for (ItemStack stack : packs) {
boolean added = player.inventory.addItemStackToInventory(stack);
if (!added) { //if can't add to inventory
player.dropItem(stack, false); //just drop in world at that player's location
}
}
}
//after loading in, reset the death capabilities
PlayerDeathBackpackCapabilities.reset(player);
}
/**
* Helper method to remove the upgrade when the player dies so they have to craft it again to keep the functionality through their next death.
* @param upgrades - the upgrades of the backpack
* @param stack - the backpack to check
* @return - the itemstack with the 'keepOnDeath' upgrade removed (if valid/applicable)
*/
private static ItemStack removeEternityUpgrade(ArrayList<ItemStack> upgrades, ItemStack stack){
if (stack != null) {
NBTTagCompound nbtTagCompound = stack.getTagCompound();
NBTTagList tagList = new NBTTagList();
for (ItemStack upgrade : upgrades) {
if (!(ItemIUpgradeRegistry.isInstanceOfIUpgrade(upgrade) && ItemIUpgradeRegistry.getItemIUpgrade(upgrade.getItemDamage()).equals(ItemRegistry.eternityUpgrade))) {
tagList.appendTag(upgrade.writeToNBT(new NBTTagCompound()));
}
}
nbtTagCompound.setTag(IronBackpacksConstants.NBTKeys.UPGRADES, tagList);
return stack;
}
return null;
}
//===========================================================================Miscellaneous===========================================
/**
* Check if items are exactly the same.
* @param itemStack1
* @param itemStack2
* @return
*/
public static boolean areItemStacksTheSame(ItemStack itemStack1, ItemStack itemStack2){
return (ItemStack.areItemStacksEqual(itemStack1, itemStack2) && ItemStack.areItemStackTagsEqual(itemStack1, itemStack2));
}
/**
* Note - Doesn't check the stack size or isStackable of either, that must be done elsewhere
* This just checks if the items are equal enough to be stacked
* @param itemStack1
* @param itemStack2
* @return - boolean true if they are equal enough, false if they are not
*/
public static boolean areItemsEqualForStacking(ItemStack itemStack1, ItemStack itemStack2){
return (itemStack1.getItem().equals(itemStack2.getItem())) && (ItemStack.areItemStackTagsEqual(itemStack1, itemStack2) && (itemStack1.getItemDamage() == OreDictionary.WILDCARD_VALUE || itemStack2.getItemDamage() == OreDictionary.WILDCARD_VALUE || itemStack1.getItemDamage() == itemStack2.getItemDamage()));
}
/**
* Note - only checks the first itemStack for stackability, the other is assumed to be a filler amount and isn;t checked
* @param itemStack1
* @param itemStack2
* @return
*/
public static boolean areItemsEqualAndStackable(ItemStack itemStack1, ItemStack itemStack2){
return (itemStack1.isStackable() && itemStack1.stackSize < itemStack1.getMaxStackSize() && areItemsEqualForStacking(itemStack1, itemStack2));
}
}