package choonster.testmod3.item;
import choonster.testmod3.Logger;
import choonster.testmod3.util.Constants;
import choonster.testmod3.util.InventoryUtils;
import choonster.testmod3.util.InventoryUtils.EntityInventoryType;
import choonster.testmod3.util.ItemStackUtils;
import com.google.common.collect.ImmutableSet;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static net.minecraftforge.common.util.Constants.NBT;
/**
* An armour item that replaces your other armour when equipped and restores it when unequipped.
* <p>
* Test for this thread:
* http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/modification-development/2595100-persistent-variables-for-armor
*
* @author Choonster
*/
public class ItemArmourReplacement extends ItemArmourTestMod3 {
// NBT keys
private static final String KEY_REPLACED_ARMOUR = "ReplacedArmour";
private static final String KEY_SLOT = "Slot";
private static final String KEY_STACK = "Stack";
/**
* The items to replace the other armour with.
*/
private Set<ItemStack> replacementItems;
public ItemArmourReplacement(final ArmorMaterial material, final EntityEquipmentSlot equipmentSlot, final String armourName) {
super(material, equipmentSlot, armourName);
}
/**
* Get the items to replace the other armour with.
*
* @return The items to replace the other armour with
*/
public Set<ItemStack> getReplacementItems() {
return replacementItems;
}
/**
* Set the items to replace the other armour with.
*
* @param replacements The items to replace the other armour with
*/
public void setReplacementItems(final ItemStack... replacements) {
if (replacements.length > 3) {
throw new IllegalArgumentException("Must supply at most 3 replacement items");
}
replacementItems = ImmutableSet.copyOf(replacements);
}
/**
* Has this item replaced the other armour?
*
* @param stack The ItemStack of this item
* @return Has this item replaced the other armour?
*/
public boolean hasReplacedArmour(final ItemStack stack) {
return stack.hasTagCompound() && stack.getTagCompound().hasKey(KEY_REPLACED_ARMOUR, NBT.TAG_LIST);
}
/**
* Save the entity's armour and replace it with the replacements defined for this item.
*
* @param stack The ItemStack of this item
* @param entity The entity
*/
private void replaceArmour(final ItemStack stack, final EntityLivingBase entity) {
final NBTTagCompound stackTagCompound = ItemStackUtils.getOrCreateTagCompound(stack);
final NBTTagList replacedArmour = new NBTTagList();
// Create a mutable copy of the replacements
final Set<ItemStack> replacements = new HashSet<>(replacementItems);
Constants.ARMOUR_SLOTS.stream() // For each armour type,
.filter(equipmentSlot -> equipmentSlot != armorType)
.forEach(equipmentSlot -> { // If it's not this item's armour type,
final Optional<ItemStack> optionalReplacement = replacements.stream()
.filter(replacementStack -> replacementStack.getItem().isValidArmor(replacementStack, equipmentSlot, entity))
.findFirst();
if (optionalReplacement.isPresent()) { // If there's a replacement for this armour type,
final ItemStack replacement = optionalReplacement.get(); // Get it
replacements.remove(replacement); // Don't use it for any other armour type
// Create a compound tag to store the original and add it to the list of replaced armour
final NBTTagCompound tagCompound = new NBTTagCompound();
replacedArmour.appendTag(tagCompound);
tagCompound.setByte(KEY_SLOT, (byte) equipmentSlot.getSlotIndex());
// If the original item exists, add it to the compound tag
final ItemStack original = entity.getItemStackFromSlot(equipmentSlot);
if (!original.isEmpty()) {
tagCompound.setTag(KEY_STACK, original.serializeNBT());
}
entity.setItemStackToSlot(equipmentSlot, replacement.copy()); // Equip a copy of the replacement
Logger.info("Equipped replacement %s to %s, replacing %s", replacement, equipmentSlot, original);
}
});
stackTagCompound.setTag(KEY_REPLACED_ARMOUR, replacedArmour); // Save the replaced armour to the ItemStack
}
/**
* Restore the entity's saved armour from this item's ItemStack NBT.
*
* @param stack The ItemStack of this item
* @param entity The entity
*/
private void restoreArmour(final ItemStack stack, final EntityLivingBase entity) {
final NBTTagCompound stackTagCompound = ItemStackUtils.getOrCreateTagCompound(stack);
final NBTTagList replacedArmour = stackTagCompound.getTagList(KEY_REPLACED_ARMOUR, NBT.TAG_COMPOUND);
for (int i = 0; i < replacedArmour.tagCount(); i++) { // For each saved armour item,
final NBTTagCompound replacedTagCompound = replacedArmour.getCompoundTagAt(i);
final ItemStack original = new ItemStack(replacedTagCompound.getCompoundTag(KEY_STACK)); // Load the original ItemStack from the NBT
final EntityEquipmentSlot equipmentSlot = InventoryUtils.getEquipmentSlotFromIndex(replacedTagCompound.getByte(KEY_SLOT)); // Get the armour slot
final ItemStack current = entity.getItemStackFromSlot(equipmentSlot);
// Is the item currently in the slot one of the replacements defined for this item?
final boolean isReplacement = replacementItems.stream().anyMatch(replacement -> ItemStack.areItemStacksEqual(replacement, current));
if (original.isEmpty()) { // If the original item is empty,
if (isReplacement) { // If the current item is a replacement,
Logger.info("Original item for %s is null, clearing replacement", equipmentSlot);
entity.setItemStackToSlot(equipmentSlot, ItemStack.EMPTY); // Delete it
} else { // Else do nothing
Logger.info("Original item for %s is null, leaving current item", equipmentSlot);
}
} else {
Logger.info("Restoring original %s to %s, replacing %s", original, equipmentSlot, current);
// If the current item isn't a replacement and the entity is a player, try to add it to their inventory or drop it on the ground
if (!isReplacement && entity instanceof EntityPlayer) {
ItemHandlerHelper.giveItemToPlayer((EntityPlayer) entity, current);
}
entity.setItemStackToSlot(equipmentSlot, original); // Equip the original item
}
}
stackTagCompound.removeTag(KEY_REPLACED_ARMOUR);
if (stackTagCompound.hasNoTags()) {
stack.setTagCompound(null);
}
}
/**
* Restore the entity's saved armour if the ItemStack is in the specified inventory slot.
*
* @param inventory The inventory
* @param slot The inventory slot
* @param stack The ItemStack of this item
* @param entity The entity
* @return Was the armour restored?
*/
private boolean tryRestoreArmour(final IItemHandler inventory, final int slot, final ItemStack stack, final EntityLivingBase entity) {
if (slot < inventory.getSlots() && inventory.getStackInSlot(slot) == stack) {
restoreArmour(stack, entity); // Restore the entity's armour
return true;
}
return false;
}
/**
* Called every tick while the item is worn by a player.
*
* @param world The player's world
* @param player The player
* @param itemStack The ItemStack of this item
*/
@Override
public void onArmorTick(final World world, final EntityPlayer player, final ItemStack itemStack) {
if (!world.isRemote && !hasReplacedArmour(itemStack)) { // If this is the server and the player's armour hasn't been replaced,
replaceArmour(itemStack, player); // Replace the player's armour
player.inventoryContainer.detectAndSendChanges(); // Sync the player's inventory with the client
}
}
/**
* Called every tick while the item is in a player's inventory (including while worn).
*
* @param stack The ItemStack of this item
* @param worldIn The entity's world
* @param entity The entity
* @param itemSlot The slot containing this item
* @param isSelected Is the entity holding this item?
*/
@Override
public void onUpdate(final ItemStack stack, final World worldIn, final Entity entity, final int itemSlot, final boolean isSelected) {
// If this is the server, the entity is living and the entity's armour has been replaced,
if (!worldIn.isRemote && entity instanceof EntityLivingBase && hasReplacedArmour(stack)) {
// Try to restore the entity's armour
final EntityInventoryType successfulInventoryType = InventoryUtils.forEachEntityInventory(
entity,
inventory -> tryRestoreArmour(inventory, itemSlot, stack, (EntityLivingBase) entity),
EntityInventoryType.MAIN, EntityInventoryType.HAND
);
if (successfulInventoryType != null) {
Logger.info("Restored saved armour for slot %d of %s's %s inventory", itemSlot, entity.getName(), successfulInventoryType);
}
}
}
@SideOnly(Side.CLIENT)
@Override
public void addInformation(final ItemStack stack, final EntityPlayer playerIn, final List<String> tooltip, final boolean advanced) {
tooltip.add(I18n.format("item.testmod3:armour_replacement.equip.desc"));
tooltip.add(I18n.format("item.testmod3:armour_replacement.unequip.desc"));
}
}