package slimeknights.toolleveling.capability; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.EnumFacing; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ICapabilitySerializable; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; import javax.annotation.Nullable; import slimeknights.tconstruct.library.tools.ToolCore; import slimeknights.toolleveling.TinkerToolLeveling; public class DamageXpHandler implements IDamageXp, ICapabilitySerializable<NBTTagList> { private static String TAG_PLAYER_UUID = "player_uuid"; private static String TAG_DAMAGE_LIST = "damage_data"; private static String TAG_ITEM = "item"; private static String TAG_DAMAGE = "damage"; private Map<UUID, Map<ItemStack, Float>> playerToDamageMap = new HashMap<>(); @Override public void addDamageFromTool(float damage, ItemStack tool, EntityPlayer player) { Map<ItemStack, Float> damageMap = playerToDamageMap.getOrDefault(player.getUniqueID(), new HashMap<>()); damage += getDamageDealtByTool(tool, player); damageMap.put(tool, damage); playerToDamageMap.put(player.getUniqueID(), damageMap); } @Override public float getDamageDealtByTool(ItemStack tool, EntityPlayer player) { Map<ItemStack, Float> damageMap = playerToDamageMap.getOrDefault(player.getUniqueID(), new HashMap<>()); return damageMap.entrySet().stream() .filter(itemStackFloatEntry -> ToolCore.isEqualTinkersItem(tool, itemStackFloatEntry.getKey())) .findFirst() .map(Map.Entry::getValue) .orElse(0f); } @Override public void distributeXpToTools(EntityLivingBase deadEntity) { playerToDamageMap.forEach((uuid, itemStackFloatMap) -> distributeXpForPlayer(deadEntity.getEntityWorld(), uuid, itemStackFloatMap)); } private void distributeXpForPlayer(World world, UUID playerUuid, Map<ItemStack, Float> damageMap) { Optional.ofNullable(world.getPlayerEntityByUUID(playerUuid)) .ifPresent( player -> damageMap.forEach( (itemStack, damage) -> distributeXpToPlayerForTool(player, itemStack, damage) ) ); } private void distributeXpToPlayerForTool(EntityPlayer player, ItemStack tool, float damage) { if(!tool.isEmpty() && player.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null)) { IItemHandler itemHandler = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); // check for identity. should work in most cases because the entity was killed without loading/unloading for(int i = 0; i < itemHandler.getSlots(); i++) { if(itemHandler.getStackInSlot(i) == tool) { TinkerToolLeveling.modToolLeveling.addXp(tool, Math.round(damage), player); return; } } // check for equal stack in case instance equality didn't find it for(int i = 0; i < itemHandler.getSlots(); i++) { if(ToolCore.isEqualTinkersItem(itemHandler.getStackInSlot(i), tool)) { TinkerToolLeveling.modToolLeveling.addXp(itemHandler.getStackInSlot(i), Math.round(damage), player); return; } } } } @Override public NBTTagList serializeNBT() { NBTTagList playerList = new NBTTagList(); playerToDamageMap.forEach((uuid, itemStackFloatMap) -> playerList.appendTag(convertPlayerDataToTag(uuid, itemStackFloatMap))); return playerList; } private NBTTagCompound convertPlayerDataToTag(UUID uuid, Map<ItemStack, Float> itemStackFloatMap) { NBTTagCompound tag = new NBTTagCompound(); tag.setUniqueId(TAG_PLAYER_UUID, uuid); NBTTagList damageTag = new NBTTagList(); itemStackFloatMap.forEach((itemStack, damage) -> damageTag.appendTag(convertItemDamageDataToTag(itemStack, damage))); tag.setTag(TAG_DAMAGE_LIST, damageTag); return tag; } private NBTTagCompound convertItemDamageDataToTag(ItemStack stack, Float damage) { NBTTagCompound tag = new NBTTagCompound(); NBTTagCompound itemTag = stack.writeToNBT(new NBTTagCompound()); tag.setTag(TAG_ITEM, itemTag.copy()); tag.setFloat(TAG_DAMAGE, damage); return tag; } @Override public void deserializeNBT(NBTTagList nbt) { playerToDamageMap = new HashMap<>(); for(int i = 0; i < nbt.tagCount(); i++) { NBTTagCompound tag = nbt.getCompoundTagAt(i); UUID playerUuid = tag.getUniqueId(TAG_PLAYER_UUID); NBTTagList data = tag.getTagList(TAG_DAMAGE_LIST, 10); Map<ItemStack, Float> damageMap = new HashMap<>(); for(int j = 0; j < data.tagCount(); j++) { deserializeTagToMapEntry(damageMap, data.getCompoundTagAt(j)); } playerToDamageMap.put(playerUuid, damageMap); } } private void deserializeTagToMapEntry(Map<ItemStack, Float> damageMap, NBTTagCompound tag) { ItemStack stack = new ItemStack(tag.getCompoundTag(TAG_ITEM)); if(!stack.isEmpty()) { damageMap.put(stack, tag.getFloat(TAG_DAMAGE)); } } @Override public boolean hasCapability(Capability<?> capability, @Nullable EnumFacing facing) { return capability == CapabilityDamageXp.CAPABILITY; } @Override public <T> T getCapability(Capability<T> capability, @Nullable EnumFacing facing) { if(capability == CapabilityDamageXp.CAPABILITY) { return (T) this; } return null; } }