package slimeknights.tconstruct.library.modifiers; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.text.TextFormatting; import net.minecraft.util.text.translation.I18n; import slimeknights.tconstruct.library.Util; import slimeknights.tconstruct.library.tinkering.Category; import slimeknights.tconstruct.library.utils.TagUtil; import slimeknights.tconstruct.library.utils.Tags; import slimeknights.tconstruct.library.utils.TinkerUtil; import slimeknights.tconstruct.library.utils.ToolHelper; /** * Have you ever wanted to create a simple modifier that is only allowed on tools * but needed something else too so you couldn't derive from a single base-modifier-class? * Well now you can! */ public abstract class ModifierAspect { public static final ModifierAspect freeModifier = new FreeModifierAspect(1); public static final ModifierAspect toolOnly = new CategoryAspect(Category.TOOL); public static final ModifierAspect harvestOnly = new CategoryAspect(Category.HARVEST); public static final ModifierAspect weaponOnly = new CategoryAspect(Category.WEAPON); public static final ModifierAspect projectileOnly = new CategoryAspect(Category.PROJECTILE); protected final IModifier parent; protected ModifierAspect() { this.parent = null; } public ModifierAspect(IModifier parent) { this.parent = parent; } public abstract boolean canApply(ItemStack stack, ItemStack original) throws TinkerGuiException; public abstract void updateNBT(NBTTagCompound root, NBTTagCompound modifierTag); /** * The modifier requires sufficient free modifier sto be present. */ public static class FreeModifierAspect extends ModifierAspect { private final int requiredModifiers; public FreeModifierAspect(int requiredModifiers) { this.requiredModifiers = requiredModifiers; } protected FreeModifierAspect(IModifier parent, int requiredModifiers) { super(parent); this.requiredModifiers = requiredModifiers; } @Override public boolean canApply(ItemStack stack, ItemStack original) throws TinkerGuiException { NBTTagCompound toolTag = TagUtil.getToolTag(stack); if(ToolHelper.getFreeModifiers(stack) < requiredModifiers) { String error = I18n.translateToLocalFormatted("gui.error.not_enough_modifiers", requiredModifiers); // also returns false if the tooltag is missing throw new TinkerGuiException(error); } return true; } @Override public void updateNBT(NBTTagCompound root, NBTTagCompound modifierTag) { // substract the modifiers NBTTagCompound toolTag = TagUtil.getToolTag(root); int modifiers = toolTag.getInteger(Tags.FREE_MODIFIERS) - requiredModifiers; toolTag.setInteger(Tags.FREE_MODIFIERS, Math.max(0, modifiers)); // and increase the count of used modifiers int usedModifiers = TagUtil.getBaseModifiersUsed(root); usedModifiers += requiredModifiers; TagUtil.setBaseModifiersUsed(root, usedModifiers); } } public static class FreeFirstModifierAspect extends FreeModifierAspect { public FreeFirstModifierAspect(IModifier parent, int requiredModifiers) { super(parent, requiredModifiers); } @Override public boolean canApply(ItemStack stack, ItemStack original) throws TinkerGuiException { // can always apply if the parent already has the modifier if(TinkerUtil.hasModifier(TagUtil.getTagSafe(stack), parent.getIdentifier())) { return true; } // otherwise he requires free modifiers return super.canApply(stack, original); } @Override public void updateNBT(NBTTagCompound root, NBTTagCompound modifierTag) { // same as above, if already present we don't need to reduce the free modifiers if(modifierTag.hasKey("modifierUsed")) { return; } super.updateNBT(root, modifierTag); modifierTag.setBoolean("modifierUsed", true); } } /** * Saves the base data of the modifier onto the tool. * Any modifier not having this has to take care of it itself. */ public static class DataAspect extends ModifierAspect { private final int color; public DataAspect(IModifier parent, TextFormatting color) { this(parent, Util.enumChatFormattingToColor(color)); } public DataAspect(IModifier parent, int color) { super(parent); this.color = color; } public <T extends IModifier & IModifierDisplay> DataAspect(T parent) { super(parent); this.color = parent.getColor(); } @Override public boolean canApply(ItemStack stack, ItemStack original) { // can always apply return true; } @Override public void updateNBT(NBTTagCompound root, NBTTagCompound modifierTag) { ModifierNBT data = ModifierNBT.readTag(modifierTag); data.identifier = parent.getIdentifier(); data.color = color; data.write(modifierTag); } } /** * The modifier can be applied several times per modifier used. */ public static class MultiAspect extends ModifierAspect { protected final int countPerLevel; protected DataAspect dataAspect; protected LevelAspect levelAspect; protected FreeModifierAspect freeModifierAspect; public <T extends IModifier & IModifierDisplay> MultiAspect(T parent, int maxLevel, int countPerLevel, int modifiersNeeded) { this(parent, parent.getColor(), maxLevel, countPerLevel, modifiersNeeded); } // multiple levels, once every time the maximum is reached public MultiAspect(IModifier parent, int color, int maxLevel, int countPerLevel, int modifiersNeeded) { super(parent); this.countPerLevel = countPerLevel; dataAspect = new DataAspect(parent, color); freeModifierAspect = new FreeModifierAspect(modifiersNeeded); levelAspect = new LevelAspect(parent, maxLevel); } // single-level public MultiAspect(IModifier parent, int color, int count) { this(parent, color, 1, count, 1); } protected int getMaxForLevel(int level) { return countPerLevel * level; } @Override public boolean canApply(ItemStack stack, ItemStack original) throws TinkerGuiException { // check if the threshold has been reached NBTTagCompound modifierTag = TinkerUtil.getModifierTag(stack, parent.getIdentifier()); ModifierNBT.IntegerNBT data = getData(modifierTag); // the current level is full / level is 0 if(data.current >= getMaxForLevel(data.level)) { // can we even apply a new level? if(!levelAspect.canApply(stack, original)) { return false; } // enough modifiers for another level? if(!freeModifierAspect.canApply(stack, original)) { return false; } } // we have not maxed out this level OR we have enough modifiers and can add a new level return true; } @Override public void updateNBT(NBTTagCompound root, NBTTagCompound modifierTag) { // simple data dataAspect.updateNBT(root, modifierTag); // increase the current level progress ModifierNBT.IntegerNBT data = getData(modifierTag); // new level? if(data.current >= getMaxForLevel(data.level)) { // remove modifiers freeModifierAspect.updateNBT(root, modifierTag); // add a level levelAspect.updateNBT(root, modifierTag); // update max. but to do so, we have to re-read the changed data again data = getData(modifierTag); } // always update max in case it changed since it got saved data.max = getMaxForLevel(data.level); // increase the level progress data.current++; data.write(modifierTag); } private ModifierNBT.IntegerNBT getData(NBTTagCompound tag) { ModifierNBT.IntegerNBT data = ModifierNBT.readInteger(tag); if(data.max == 0) { data.max = getMaxForLevel(data.level); } return data; } } /** * Only applicable on tools with the given categories. */ public static class CategoryAspect extends ModifierAspect { protected final Category[] category; public CategoryAspect(Category... category) { this.category = category; } @Override public boolean canApply(ItemStack stack, ItemStack original) { for(Category cat : category) { if(!ToolHelper.hasCategory(stack, cat)) { return false; } } return true; } @Override public void updateNBT(NBTTagCompound root, NBTTagCompound modifierTag) { // no extra information needed } } /** * Applicable on all tools that have at least one of those categories */ public static class CategoryAnyAspect extends CategoryAspect { public CategoryAnyAspect(Category... category) { super(category); } @Override public boolean canApply(ItemStack stack, ItemStack original) { for(Category cat : category) { if(ToolHelper.hasCategory(stack, cat)) { return true; } } return false; } } /** * Can only be applied once */ public static class SingleAspect extends ModifierAspect { public SingleAspect(IModifier parent) { super(parent); } @Override public boolean canApply(ItemStack stack, ItemStack original) throws TinkerGuiException { // check if the modifier is present in the base info. // this is not the same as checking if the modifier has data. But should be sufficient if(TinkerUtil.hasModifier(TagUtil.getTagSafe(stack), parent.getIdentifier())) { // check if original already had it too if(TinkerUtil.hasModifier(TagUtil.getTagSafe(original), parent.getIdentifier())) { // error, can't apply if it already had it throw new TinkerGuiException(I18n.translateToLocalFormatted("gui.error.single_modifier", parent.getLocalizedName())); } else { // original didn't have it, we can apply it once therefore, no error return false; } } // applicable if not found return true; } @Override public void updateNBT(NBTTagCompound root, NBTTagCompound modifierTag) { // no extra information needed, taken care of by base modifier } } /** * Allows the modifier to be applied multiple times up to a max level */ public static class LevelAspect extends ModifierAspect { private final int maxLevel; public LevelAspect(IModifier parent, int maxLevel) { super(parent); this.maxLevel = maxLevel; } @Override public boolean canApply(ItemStack stack, ItemStack original) throws TinkerGuiException { int levelNew = ModifierNBT.readTag(TinkerUtil.getModifierTag(stack, parent.getIdentifier())).level; int levelOld = ModifierNBT.readTag(TinkerUtil.getModifierTag(original, parent.getIdentifier())).level; // only 1 level per application // original and stack are equal for the first application, any multiple applications therefore yield >0 if(levelNew - levelOld > 0) { return false; } // new level would be above max level if(levelNew >= maxLevel) { throw new TinkerGuiException(I18n.translateToLocalFormatted("gui.error.max_level_modifier", parent.getLocalizedName())); } return true; } @Override public void updateNBT(NBTTagCompound root, NBTTagCompound modifierTag) { ModifierNBT data = ModifierNBT.readTag(modifierTag); data.level++; data.write(modifierTag); } } }