package slimeknights.tconstruct.library.modifiers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.util.DamageSource;
import net.minecraft.util.text.translation.I18n;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.Set;
import javax.annotation.Nullable;
import slimeknights.mantle.util.RecipeMatchRegistry;
import slimeknights.tconstruct.library.TinkerRegistry;
import slimeknights.tconstruct.library.Util;
import slimeknights.tconstruct.library.traits.ITrait;
import slimeknights.tconstruct.library.utils.TagUtil;
import slimeknights.tconstruct.library.utils.TinkerUtil;
public abstract class Modifier extends RecipeMatchRegistry implements IModifier {
public static final String LOC_Name = "modifier.%s.name";
public static final String LOC_Desc = "modifier.%s.desc";
public static final String LOC_Extra = "modifier.%s.extra";
protected static final Random random = new Random();
public final String identifier;
protected final List<ModifierAspect> aspects = Lists.newLinkedList();
public Modifier(String identifier) {
this.identifier = Util.sanitizeLocalizationString(identifier);
TinkerRegistry.registerModifier(this);
}
@Override
public String getIdentifier() {
return identifier;
}
@Override
public boolean isHidden() {
return false;
}
protected void addAspects(ModifierAspect... aspects) {
this.aspects.addAll(Arrays.asList(aspects));
}
@Override
public final boolean canApply(ItemStack stack, ItemStack original) throws TinkerGuiException {
Set<Enchantment> enchantments = EnchantmentHelper.getEnchantments(stack).keySet();
NBTTagList traits = TagUtil.getTraitsTagList(stack);
for(int i = 0; i < traits.tagCount(); i++) {
String id = traits.getStringTagAt(i);
ITrait trait = TinkerRegistry.getTrait(id);
if(trait != null) {
if(!canApplyTogether(trait) || !trait.canApplyTogether(this)) {
throw new TinkerGuiException(Util.translateFormatted("gui.error.incompatible_trait", this.getLocalizedName(), trait.getLocalizedName()));
}
canApplyWithEnchantment(trait, enchantments);
}
}
NBTTagList modifiers = TagUtil.getBaseModifiersTagList(stack);
for(int i = 0; i < modifiers.tagCount(); i++) {
String id = modifiers.getStringTagAt(i);
IModifier mod = TinkerRegistry.getModifier(id);
if(mod != null) {
if(!canApplyTogether(mod) || !mod.canApplyTogether(this)) {
throw new TinkerGuiException(Util.translateFormatted("gui.error.incompatible_modifiers", this.getLocalizedName(), mod.getLocalizedName()));
}
canApplyWithEnchantment(mod, enchantments);
}
}
canApplyWithEnchantment(this, enchantments);
// aspects
for(ModifierAspect aspect : aspects) {
if(!aspect.canApply(stack, original)) {
return false;
}
}
return canApplyCustom(stack);
}
private static void canApplyWithEnchantment(IToolMod iToolMod, Set<Enchantment> enchantments)
throws TinkerGuiException {
for(Enchantment enchantment : enchantments) {
if(!iToolMod.canApplyTogether(enchantment)) {
String enchName = I18n.translateToLocal(enchantment.getName());
throw new TinkerGuiException(Util.translateFormatted("gui.error.incompatible_enchantments", iToolMod.getLocalizedName(), enchName));
}
}
}
@Override
public boolean canApplyTogether(Enchantment enchantment) {
return true;
}
@Override
public boolean canApplyTogether(IToolMod otherModifier) {
return true;
}
protected boolean canApplyCustom(ItemStack stack) throws TinkerGuiException {
return true;
}
@Override
public void updateNBT(NBTTagCompound modifierTag) {
// nothing to do in most cases, aspects handle the updating for most modifier
}
@Override
public void apply(ItemStack stack) {
NBTTagCompound root = TagUtil.getTagSafe(stack);
apply(root);
stack.setTagCompound(root);
}
@Override
public void apply(NBTTagCompound root) {
// add the modifier to its data
NBTTagList tagList;
// if the modifier wasn't present before, add it and safe it to the tool
if(!TinkerUtil.hasModifier(root, getIdentifier())) {
tagList = TagUtil.getBaseModifiersTagList(root);
tagList.appendTag(new NBTTagString(getIdentifier()));
TagUtil.setBaseModifiersTagList(root, tagList);
}
// have the modifier itself save its data
NBTTagCompound modifierTag = new NBTTagCompound();
tagList = TagUtil.getModifiersTagList(root);
int index = TinkerUtil.getIndexInList(tagList, identifier);
if(index >= 0) {
modifierTag = tagList.getCompoundTagAt(index);
}
// update NBT through aspects
for(ModifierAspect aspect : aspects) {
aspect.updateNBT(root, modifierTag);
}
updateNBT(modifierTag);
// some modifiers might not save data, don't save them
if(!modifierTag.hasNoTags()) {
// but if they do, ensure that the identifier is correct
ModifierNBT data = ModifierNBT.readTag(modifierTag);
if(!identifier.equals(data.identifier)) {
data.identifier = identifier;
data.write(modifierTag);
}
}
// update the tools NBT
if(index >= 0) {
tagList.set(index, modifierTag);
}
else {
tagList.appendTag(modifierTag);
}
TagUtil.setModifiersTagList(root, tagList);
applyEffect(root, modifierTag);
}
@Override
public String getTooltip(NBTTagCompound modifierTag, boolean detailed) {
StringBuilder sb = new StringBuilder();
ModifierNBT data = ModifierNBT.readTag(modifierTag);
sb.append(getLocalizedName());
if(data.level > 1) {
sb.append(" ");
sb.append(TinkerUtil.getRomanNumeral(data.level));
}
return sb.toString();
}
public String getLeveledTooltip(NBTTagCompound modifierTag, boolean detailed) {
ModifierNBT data = ModifierNBT.readInteger(modifierTag);
return getLeveledTooltip(data.level, detailed ? " " + data.extraInfo : "");
}
public String getLeveledTooltip(int level, @Nullable String suffix) {
// the most important function in the whole file!
String basic = getLocalizedName(); // backup
if(level == 0) {
return basic;
}
else if(level > 1) {
basic += " " + TinkerUtil.getRomanNumeral(level);
}
for(int i = level; i > 1; i--) {
if(I18n.canTranslate(String.format(LOC_Name + i, getIdentifier()))) {
basic = I18n.translateToLocal(String.format(LOC_Name + i, getIdentifier()));
break;
}
}
if(suffix != null) {
basic += suffix;
}
return basic;
}
@Override
public String getLocalizedName() {
return Util.translate(LOC_Name, getIdentifier());
}
@Override
public String getLocalizedDesc() {
return Util.translate(LOC_Desc, getIdentifier());
}
@Override
public List<String> getExtraInfo(ItemStack tool, NBTTagCompound modifierTag) {
return ImmutableList.of();
}
@Override
public boolean equalModifier(NBTTagCompound modifierTag1, NBTTagCompound modifierTag2) {
ModifierNBT data1 = ModifierNBT.readTag(modifierTag1);
ModifierNBT data2 = ModifierNBT.readTag(modifierTag2);
return data1.identifier.equals(data2.identifier) && data1.level == data2.level;
}
@Override
public boolean hasTexturePerMaterial() {
return false;
}
protected static boolean attackEntitySecondary(DamageSource source, float damage, Entity entity, boolean ignoreInvulv, boolean resetInvulv) {
return attackEntitySecondary(source, damage, entity, ignoreInvulv, resetInvulv, true);
}
protected static boolean attackEntitySecondary(DamageSource source, float damage, Entity entity, boolean ignoreInvulv, boolean resetInvulv, boolean noKnockback) {
IAttributeInstance knockbackAttribute = null;
float oldLastDamage = 0;
if(entity instanceof EntityLivingBase) {
oldLastDamage = ((EntityLivingBase) entity).lastDamage;
if(noKnockback) {
knockbackAttribute = ((EntityLivingBase) entity).getEntityAttribute(SharedMonsterAttributes.KNOCKBACK_RESISTANCE);
}
}
if(knockbackAttribute != null) {
knockbackAttribute.applyModifier(ANTI_KNOCKBACK_MOD);
}
// set hurt resistance time to 0 because we always want to deal damage in traits
if(ignoreInvulv) {
entity.hurtResistantTime = 0;
}
boolean hit = entity.attackEntityFrom(source, damage);
if(entity instanceof EntityLivingBase) {
((EntityLivingBase) entity).lastDamage += oldLastDamage;
}
// reset hurt resistance time if desired
if(hit && resetInvulv) {
entity.hurtResistantTime = 0;
}
if(knockbackAttribute != null) {
knockbackAttribute.removeModifier(ANTI_KNOCKBACK_MOD);
}
return hit;
}
@Override
public boolean hasItemsToApplyWith() {
return !items.isEmpty();
}
private static final AttributeModifier ANTI_KNOCKBACK_MOD = new AttributeModifier("Anti Modifier Knockback", 1f, 0);
}