summaryrefslogtreecommitdiff
path: root/src/main/java/lance5057/tDefense/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/lance5057/tDefense/util')
-rw-r--r--src/main/java/lance5057/tDefense/util/ArmorBuildGuiInfo.java48
-rw-r--r--src/main/java/lance5057/tDefense/util/ArmorBuilder.java596
-rw-r--r--src/main/java/lance5057/tDefense/util/ArmorEvent.java65
-rw-r--r--src/main/java/lance5057/tDefense/util/TDClientRegistry.java48
-rw-r--r--src/main/java/lance5057/tDefense/util/TDModelLoader.java204
-rw-r--r--src/main/java/lance5057/tDefense/util/TDModelRegistar.java62
-rw-r--r--src/main/java/lance5057/tDefense/util/TDRegistry.java58
7 files changed, 1081 insertions, 0 deletions
diff --git a/src/main/java/lance5057/tDefense/util/ArmorBuildGuiInfo.java b/src/main/java/lance5057/tDefense/util/ArmorBuildGuiInfo.java
new file mode 100644
index 0000000..a72b9ab
--- /dev/null
+++ b/src/main/java/lance5057/tDefense/util/ArmorBuildGuiInfo.java
@@ -0,0 +1,48 @@
+package lance5057.tDefense.util;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.lwjgl.util.Point;
+
+import com.google.common.collect.Lists;
+
+import lance5057.tDefense.core.tools.bases.ArmorBase;
+import net.minecraft.item.ItemStack;
+
+public class ArmorBuildGuiInfo {
+
+ @Nonnull
+ public final ItemStack armor;
+ // the positions where the slots are located
+ public final List<Point> positions = Lists.newArrayList();
+
+ public ArmorBuildGuiInfo() {
+ // for repairing
+ this.armor = ItemStack.EMPTY;
+ }
+
+ public ArmorBuildGuiInfo(@Nonnull ArmorBase tool) {
+ this.armor = tool.buildItemForRenderingInGui();
+ }
+
+ public static ArmorBuildGuiInfo default3Part(@Nonnull ArmorBase tool) {
+ ArmorBuildGuiInfo info = new ArmorBuildGuiInfo(tool);
+ info.addSlotPosition(33 - 20, 42 + 20);
+ info.addSlotPosition(33 + 20, 42 - 20);
+ info.addSlotPosition(33, 42);
+ return info;
+ }
+
+ /**
+ * Add another slot at the specified position for the tool.
+ * The positions are usually located between:
+ * X: 7 - 69
+ * Y: 18 - 64
+ */
+ public void addSlotPosition(int x, int y) {
+ positions.add(new Point(x, y));
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/lance5057/tDefense/util/ArmorBuilder.java b/src/main/java/lance5057/tDefense/util/ArmorBuilder.java
new file mode 100644
index 0000000..94014a6
--- /dev/null
+++ b/src/main/java/lance5057/tDefense/util/ArmorBuilder.java
@@ -0,0 +1,596 @@
+package lance5057.tDefense.util;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+import org.apache.logging.log4j.Logger;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+import gnu.trove.map.TIntIntMap;
+import gnu.trove.map.hash.TIntIntHashMap;
+import lance5057.tDefense.core.tools.bases.ArmorBase;
+import lance5057.tDefense.core.tools.bases.ArmorCore;
+import net.minecraft.enchantment.Enchantment;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.nbt.NBTTagString;
+import net.minecraft.util.NonNullList;
+import net.minecraft.util.text.translation.I18n;
+import slimeknights.mantle.util.ItemStackList;
+import slimeknights.mantle.util.RecipeMatch;
+import slimeknights.tconstruct.library.TinkerRegistry;
+import slimeknights.tconstruct.library.Util;
+import slimeknights.tconstruct.library.events.TinkerEvent;
+import slimeknights.tconstruct.library.materials.Material;
+import slimeknights.tconstruct.library.modifiers.IModifier;
+import slimeknights.tconstruct.library.modifiers.TinkerGuiException;
+import slimeknights.tconstruct.library.tinkering.IRepairable;
+import slimeknights.tconstruct.library.tinkering.MaterialItem;
+import slimeknights.tconstruct.library.tinkering.PartMaterialType;
+import slimeknights.tconstruct.library.tools.IToolPart;
+import slimeknights.tconstruct.library.tools.Pattern;
+import slimeknights.tconstruct.library.tools.ToolCore;
+import slimeknights.tconstruct.library.traits.AbstractTrait;
+import slimeknights.tconstruct.library.traits.ITrait;
+import slimeknights.tconstruct.library.utils.ListUtil;
+import slimeknights.tconstruct.library.utils.TagUtil;
+import slimeknights.tconstruct.library.utils.Tags;
+import slimeknights.tconstruct.library.utils.TinkerUtil;
+
+public final class ArmorBuilder {
+
+ private static Logger log = Util.getLogger("ArmorBuilder");
+
+ private ArmorBuilder() {
+ }
+
+ @Nonnull
+ public static ItemStack tryBuildTool(NonNullList<ItemStack> stacks, String name) {
+ return tryBuildArmor(stacks, name, TDRegistry.getArmor());
+ }
+
+ /**
+ * Takes an array of Itemstacks and tries to build a tool with it. Amount of itemstacks has to match exactly.
+ *
+ * @param stacks Input.
+ * @return The built tool or null if none could be built.
+ */
+ @Nonnull
+ public static ItemStack tryBuildArmor(NonNullList<ItemStack> stacks, String name, Collection<ArmorCore> possibleTools) {
+ int length = -1;
+ NonNullList<ItemStack> input;
+ // remove trailing empty slots
+ for(int i = 0; i < stacks.size(); i++) {
+ if(stacks.get(i).isEmpty()) {
+ if(length < 0) {
+ length = i;
+ }
+ }
+ else if(length >= 0) {
+ // incorrect input. gap with null in the stacks passed
+ return ItemStack.EMPTY;
+ }
+ }
+
+ if(length < 0) {
+ return ItemStack.EMPTY;
+ }
+
+ input = ItemStackList.of(stacks);
+
+ for(Item item : possibleTools) {
+ if(!(item instanceof ToolCore)) {
+ continue;
+ }
+ ItemStack output = ((ToolCore) item).buildItemFromStacks(input);
+ if(!output.isEmpty()) {
+ // name the item
+ if(name != null && !name.isEmpty()) {
+ output.setStackDisplayName(name);
+ }
+
+ return output;
+ }
+ }
+
+ return ItemStack.EMPTY;
+ }
+
+ /**
+ * Adds the trait to the tag, taking max-count and already existing traits into account.
+ *
+ * @param rootCompound The root compound of the item
+ * @param trait The trait to add.
+ * @param color The color used on the tooltip. Will not be used if the trait already exists on the tool.
+ */
+ public static void addTrait(NBTTagCompound rootCompound, ITrait trait, int color) {
+ // only registered traits allowed
+ if(TinkerRegistry.getTrait(trait.getIdentifier()) == null) {
+ log.error("addTrait: Trying to apply unregistered Trait {}", trait.getIdentifier());
+ return;
+ }
+
+ IModifier modifier = TinkerRegistry.getModifier(trait.getIdentifier());
+
+ if(modifier == null || !(modifier instanceof AbstractTrait)) {
+ log.error("addTrait: No matching modifier for the Trait {} present", trait.getIdentifier());
+ return;
+ }
+
+ AbstractTrait traitModifier = (AbstractTrait) modifier;
+
+ NBTTagCompound tag = new NBTTagCompound();
+ NBTTagList tagList = TagUtil.getModifiersTagList(rootCompound);
+ int index = TinkerUtil.getIndexInList(tagList, traitModifier.getModifierIdentifier());
+ if(index < 0) {
+ traitModifier.updateNBTforTrait(tag, color);
+ tagList.appendTag(tag);
+ TagUtil.setModifiersTagList(rootCompound, tagList);
+ }
+ else {
+ tag = tagList.getCompoundTagAt(index);
+ }
+
+ traitModifier.applyEffect(rootCompound, tag);
+ }
+
+ @Nonnull
+ public static ItemStack tryRepairTool(NonNullList<ItemStack> stacks, ItemStack toolStack, boolean removeItems) {
+ if(toolStack == null || !(toolStack.getItem() instanceof IRepairable)) {
+ return ItemStack.EMPTY;
+ }
+
+ // obtain a working copy of the items if the originals shouldn't be modified
+ if(!removeItems) {
+ stacks = Util.deepCopyFixedNonNullList(stacks);
+ }
+
+ return ((IRepairable) toolStack.getItem()).repair(toolStack, stacks);
+ }
+
+ /**
+ * Takes a tool and an array of itemstacks and tries to modify the tool with those.
+ * If removeItems is true, the items used in the process will be removed from the array.
+ *
+ * @param input Items to modify the tool with
+ * @param toolStack The tool
+ * @param removeItems If true the applied items will be removed from the array
+ * @return The modified tool or null if something went wrong or no modifier applied.
+ * @throws TinkerGuiException Thrown when not matching modifiers could be applied. Contains extra-information why the process failed.
+ */
+ @Nonnull
+ public static ItemStack tryModifyTool(NonNullList<ItemStack> input, ItemStack toolStack, boolean removeItems)
+ throws TinkerGuiException {
+ ItemStack copy = toolStack.copy();
+
+ // obtain a working copy of the items if the originals shouldn't be modified
+ NonNullList<ItemStack> stacks = Util.deepCopyFixedNonNullList(input);
+ NonNullList<ItemStack> usedStacks = Util.deepCopyFixedNonNullList(input);
+
+ Set<IModifier> appliedModifiers = Sets.newHashSet();
+ for(IModifier modifier : TinkerRegistry.getAllModifiers()) {
+ Optional<RecipeMatch.Match> matchOptional;
+ do {
+ matchOptional = modifier.matches(stacks);
+ ItemStack backup = copy.copy();
+
+ // found a modifier that is applicable. Try to apply the match
+ if(matchOptional.isPresent()) {
+ RecipeMatch.Match match = matchOptional.get();
+ // we need to apply the whole match
+ while(match.amount > 0) {
+ TinkerGuiException caughtException = null;
+ boolean canApply = false;
+ try {
+ canApply = modifier.canApply(copy, toolStack);
+ } catch(TinkerGuiException e) {
+ caughtException = e;
+ }
+
+ // but can it be applied?
+ if(canApply) {
+ modifier.apply(copy);
+
+ appliedModifiers.add(modifier);
+ match.amount--;
+ }
+ else {
+ // materials would allow another application, but modifier doesn't
+ // if we have already applied another modifier we cancel the whole thing to prevent situations where
+ // only a part of the modifiers gets applied. either all or none.
+ // if we have a reason, rather tell the player that
+ if(caughtException != null && !appliedModifiers.contains(modifier)) {
+ throw caughtException;
+ }
+
+ copy = backup;
+ RecipeMatch.removeMatch(stacks, match);
+ break;
+ }
+ }
+
+ if(match.amount == 0) {
+ RecipeMatch.removeMatch(stacks, match);
+ RecipeMatch.removeMatch(usedStacks, match);
+ }
+ }
+ } while(matchOptional.isPresent());
+ }
+
+ // check if all itemstacks were touched - otherwise there's an invalid item in the input
+ for(int i = 0; i < input.size(); i++) {
+ if(!input.get(i).isEmpty() && ItemStack.areItemStacksEqual(input.get(i), stacks.get(i))) {
+ if(!appliedModifiers.isEmpty()) {
+ String error = I18n.translateToLocalFormatted("gui.error.no_modifier_for_item", input.get(i).getDisplayName());
+ throw new TinkerGuiException(error);
+ }
+ return ItemStack.EMPTY;
+ }
+ }
+
+ // update output itemstacks
+ if(removeItems) {
+ for(int i = 0; i < input.size(); i++) {
+ // stacks might be null because stacksize got 0 during processing, we have to reflect that in the input
+ // so the caller can identify that
+ if(usedStacks.get(i).isEmpty()) {
+ input.get(i).setCount(0);
+ }
+ else {
+ input.get(i).setCount(usedStacks.get(i).getCount());
+ }
+ }
+ }
+
+ if(!appliedModifiers.isEmpty()) {
+ // always rebuild tinkers items to ensure consistency and find problems earlier
+ if(copy.getItem() instanceof ArmorBase) {
+ NBTTagCompound root = TagUtil.getTagSafe(copy);
+ rebuildArmor(root, (ArmorBase) copy.getItem());
+ copy.setTagCompound(root);
+ }
+ return copy;
+ }
+
+ return ItemStack.EMPTY;
+ }
+
+ /**
+ * Takes a tool and toolparts and replaces the parts inside the tool with the given ones.
+ * Toolparts have to be applicable to the tool. Toolparts must not be duplicates of currently used parts.
+ *
+ * @param toolStack The tool to replace the parts in
+ * @param toolPartsIn The toolparts.
+ * @param removeItems If true the applied items will be removed from the array
+ * @return The tool with the replaced parts or null if the conditions have not been met.
+ */
+ @Nonnull
+ public static ItemStack tryReplaceToolParts(ItemStack toolStack, final NonNullList<ItemStack> toolPartsIn, final boolean removeItems)
+ throws TinkerGuiException {
+ if(toolStack == null || !(toolStack.getItem() instanceof ArmorBase)) {
+ return ItemStack.EMPTY;
+ }
+
+ // we never modify the original. Caller can remove all of them if we return a result
+ NonNullList<ItemStack> inputItems = ItemStackList.of(Util.deepCopyFixedNonNullList(toolPartsIn));
+ if(!TinkerEvent.OnToolPartReplacement.fireEvent(inputItems, toolStack)) {
+ // event cancelled
+ return ItemStack.EMPTY;
+ }
+ // technically we don't need a deep copy here, but meh. less code.
+ final NonNullList<ItemStack> toolParts = Util.deepCopyFixedNonNullList(inputItems);
+
+ TIntIntMap assigned = new TIntIntHashMap();
+ ArmorBase tool = (ArmorBase) toolStack.getItem();
+ // materiallist has to be copied because it affects the actual NBT on the tool if it's changed
+ final NBTTagList materialList = TagUtil.getBaseMaterialsTagList(toolStack).copy();
+
+ // assing each toolpart to a slot in the tool
+ for(int i = 0; i < toolParts.size(); i++) {
+ ItemStack part = toolParts.get(i);
+ if(part.isEmpty()) {
+ continue;
+ }
+ if(!(part.getItem() instanceof IToolPart)) {
+ // invalid item for toolpart replacement
+ return ItemStack.EMPTY;
+ }
+
+ int candidate = -1;
+ // find an applicable slot in the tool structure corresponding to the toolparts position
+ List<PartMaterialType> pms = tool.getRequiredComponents();
+ for(int j = 0; j < pms.size(); j++) {
+ PartMaterialType pmt = pms.get(j);
+ String partMat = ((IToolPart) part.getItem()).getMaterial(part).getIdentifier();
+ String currentMat = materialList.getStringTagAt(j);
+ // is valid and not the same material?
+ if(pmt.isValid(part) && !partMat.equals(currentMat)) {
+ // part not taken up by previous part already?
+ if(!assigned.valueCollection().contains(j)) {
+ candidate = j;
+ // if a tool has multiple of the same parts we may want to replace another one as the currently selected
+ // for that purpose we only allow to overwrite the current selection if the input slot is a later one than the current one
+ if(i <= j) {
+ break;
+ }
+ }
+ }
+ }
+
+ // no assignment found for a part. Invalid input.
+ if(candidate < 0) {
+ return ItemStack.EMPTY;
+ }
+ assigned.put(i, candidate);
+ }
+
+ // did we assign nothing?
+ if(assigned.isEmpty()) {
+ return ItemStack.EMPTY;
+ }
+
+ // We now know which parts to replace with which inputs. Yay. Now we only have to do so.
+ // to do so we simply switch out the materials used and rebuild the tool
+ assigned.forEachEntry((i, j) -> {
+ String mat = ((IToolPart) toolParts.get(i).getItem()).getMaterial(toolParts.get(i)).getIdentifier();
+ materialList.set(j, new NBTTagString(mat));
+ if(removeItems) {
+ if(i < toolPartsIn.size() && !toolPartsIn.get(i).isEmpty()) {
+ toolPartsIn.get(i).shrink(1);
+ }
+ }
+ return true;
+ });
+
+ // check that each material is still compatible with each modifier
+ ArmorBase ArmorBase = (ArmorBase) toolStack.getItem();
+ ItemStack copyToCheck = ArmorBase.buildItem(TinkerUtil.getMaterialsFromTagList(materialList));
+ // this includes traits
+ NBTTagList modifiers = TagUtil.getBaseModifiersTagList(toolStack);
+ for(int i = 0; i < modifiers.tagCount(); i++) {
+ String id = modifiers.getStringTagAt(i);
+ IModifier mod = TinkerRegistry.getModifier(id);
+
+ // will throw an exception if it can't apply
+ if(mod != null && !mod.canApply(copyToCheck, copyToCheck)) {
+ throw new TinkerGuiException();
+ }
+ }
+
+ ItemStack output = toolStack.copy();
+ TagUtil.setBaseMaterialsTagList(output, materialList);
+ NBTTagCompound tag = TagUtil.getTagSafe(output);
+ rebuildArmor(tag, (ArmorBase) output.getItem());
+ output.setTagCompound(tag);
+
+ // check if the output has enough durability. we only allow it if the result would not be broken
+ if(output.getItemDamage() > output.getMaxDamage()) {
+ String error = I18n.translateToLocalFormatted("gui.error.not_enough_durability", output.getItemDamage() - output.getMaxDamage());
+ throw new TinkerGuiException(error);
+ }
+
+ return output;
+ }
+
+ /**
+ * Takes a pattern and itemstacks and crafts the materialitem of the pattern out of it.
+ * The output consists of an ItemStack[2] array that contains the part in the first slot and eventual leftover output in the 2nd one.
+ * The itemstacks have to match at least 1 material.
+ * If multiple materials match, matches with multiple items are preferred.
+ * Otherwise the first match will be taken.
+ *
+ * @param pattern Input-pattern. Has to be a Pattern.
+ * @param materialItems The Itemstacks to craft the item out of
+ * @param removeItems If true the match will be removed from the passed items
+ * @return ItemStack[2] Array containing the built item in the first slot and eventual secondary output in the second one. Null if no item could be built.
+ */
+ public static NonNullList<ItemStack> tryBuildToolPart(ItemStack pattern, NonNullList<ItemStack> materialItems, boolean removeItems)
+ throws TinkerGuiException {
+ Item itemPart = Pattern.getPartFromTag(pattern);
+ if(itemPart == null || !(itemPart instanceof MaterialItem) || !(itemPart instanceof IToolPart)) {
+ String error = I18n.translateToLocalFormatted("gui.error.invalid_pattern");
+ throw new TinkerGuiException(error);
+ }
+
+ IToolPart part = (IToolPart) itemPart;
+
+ if(!removeItems) {
+ materialItems = Util.deepCopyFixedNonNullList(materialItems);
+ }
+
+ // find the material from the input
+ Optional<RecipeMatch.Match> match = Optional.empty();
+ Material foundMaterial = null;
+ for(Material material : TinkerRegistry.getAllMaterials()) {
+ // craftable?
+ if(!material.isCraftable()) {
+ continue;
+ }
+ Optional<RecipeMatch.Match> newMatch = material.matches(materialItems, part.getCost());
+ if(!newMatch.isPresent()) {
+ continue;
+ }
+
+ // we found a match, yay
+ if(!match.isPresent()) {
+ match = newMatch;
+ foundMaterial = material;
+ // is it more complex than the old one?
+ }
+ }
+
+ // nope, no material
+ if(!match.isPresent()) {
+ return null;
+ }
+
+ ItemStack output = ((MaterialItem) itemPart).getItemstackWithMaterial(foundMaterial);
+ if(output.isEmpty()) {
+ return null;
+ }
+ if(output.getItem() instanceof IToolPart && !((IToolPart) output.getItem()).canUseMaterial(foundMaterial)) {
+ return null;
+ }
+
+ RecipeMatch.removeMatch(materialItems, match.get());
+
+ // check if we have secondary output
+ ItemStack secondary = ItemStack.EMPTY;
+ int leftover = (match.get().amount - part.getCost()) / Material.VALUE_Shard;
+ if(leftover > 0) {
+ secondary = TinkerRegistry.getShard(foundMaterial);
+ secondary.setCount(leftover);
+ }
+
+ // build an item with this
+ return ListUtil.getListFrom(output, secondary);
+ }
+
+ /**
+ * Rebuilds a tool from its raw data, material info and applied modifiers
+ *
+ * @param rootNBT The root NBT tag compound of the tool to to rebuild. The NBT will be modified, overwriting old
+ * data.
+ */
+ public static void rebuildArmor(NBTTagCompound rootNBT, ArmorBase armor) throws TinkerGuiException {
+ boolean broken = TagUtil.getToolTag(rootNBT).getBoolean(Tags.BROKEN);
+ // Recalculate tool base stats from material stats
+ NBTTagList materialTag = TagUtil.getBaseMaterialsTagList(rootNBT);
+ List<Material> materials = TinkerUtil.getMaterialsFromTagList(materialTag);
+ List<PartMaterialType> pms = armor.getRequiredComponents();
+
+ // ensure all needed Stats are present
+ while(materials.size() < pms.size()) {
+ materials.add(Material.UNKNOWN);
+ }
+ for(int i = 0; i < pms.size(); i++) {
+ if(!pms.get(i).isValidMaterial(materials.get(i))) {
+ materials.set(i, Material.UNKNOWN);
+ }
+ }
+
+ // the base stats of the tool
+ NBTTagCompound toolTag = armor.buildTag(materials);
+ TagUtil.setToolTag(rootNBT, toolTag);
+ // and its copy for reference
+ rootNBT.setTag(Tags.TOOL_DATA_ORIG, toolTag.copy());
+
+ // save the old modifiers list and clean up all tags that get set by modifiers/traits
+ NBTTagList modifiersTagOld = TagUtil.getModifiersTagList(rootNBT);
+ rootNBT.removeTag(Tags.TOOL_MODIFIERS); // the active-modifiers tag
+ rootNBT.setTag(Tags.TOOL_MODIFIERS, new NBTTagList());
+ rootNBT.removeTag("ench"); // and the enchantments tag
+ rootNBT.removeTag(Tags.ENCHANT_EFFECT); // enchant effect too, will be readded by a trait either way
+
+ // clean up traits
+ rootNBT.removeTag(Tags.TOOL_TRAITS);
+ armor.addMaterialTraits(rootNBT, materials);
+
+ // fire event
+ ArmorEvent.OnItemBuilding.fireEvent(rootNBT, ImmutableList.copyOf(materials), armor);
+
+ // reapply modifiers
+ NBTTagList modifiers = TagUtil.getBaseModifiersTagList(rootNBT);
+ NBTTagList modifiersTag = TagUtil.getModifiersTagList(rootNBT);
+ // copy over and reapply all relevant modifiers
+ for(int i = 0; i < modifiers.tagCount(); i++) {
+ String identifier = modifiers.getStringTagAt(i);
+ IModifier modifier = TinkerRegistry.getModifier(identifier);
+ if(modifier == null) {
+ log.debug("Missing modifier: {}", identifier);
+ continue;
+ }
+
+ NBTTagCompound tag;
+ int index = TinkerUtil.getIndexInList(modifiersTagOld, modifier.getIdentifier());
+
+ if(index >= 0) {
+ tag = modifiersTagOld.getCompoundTagAt(index);
+ }
+ else {
+ tag = new NBTTagCompound();
+ }
+
+ modifier.applyEffect(rootNBT, tag);
+ if(!tag.hasNoTags()) {
+ int indexNew = TinkerUtil.getIndexInList(modifiersTag, modifier.getIdentifier());
+ if(indexNew >= 0) {
+ modifiersTag.set(indexNew, tag);
+ }
+ else {
+ modifiersTag.appendTag(tag);
+ }
+ }
+ }
+
+ // remaining info, get updated toolTag
+ toolTag = TagUtil.getToolTag(rootNBT);
+ // adjust free modifiers
+ int freeModifiers = toolTag.getInteger(Tags.FREE_MODIFIERS);
+ freeModifiers -= TagUtil.getBaseModifiersUsed(rootNBT);
+ toolTag.setInteger(Tags.FREE_MODIFIERS, Math.max(0, freeModifiers));
+
+ // broken?
+ if(broken) {
+ toolTag.setBoolean(Tags.BROKEN, true);
+ }
+
+ TagUtil.setToolTag(rootNBT, toolTag);
+
+ if(freeModifiers < 0) {
+ throw new TinkerGuiException(Util.translateFormatted("gui.error.not_enough_modifiers", -freeModifiers));
+ }
+ }
+
+ public static short getEnchantmentLevel(NBTTagCompound rootTag, Enchantment enchantment) {
+ NBTTagList enchantments = rootTag.getTagList("ench", 10);
+
+ int id = Enchantment.getEnchantmentID(enchantment);
+
+ for(int i = 0; i < enchantments.tagCount(); i++) {
+ if(enchantments.getCompoundTagAt(i).getShort("id") == id) {
+ return enchantments.getCompoundTagAt(i).getShort("lvl");
+ }
+ }
+
+ return 0;
+ }
+
+ public static void addEnchantment(NBTTagCompound rootTag, Enchantment enchantment) {
+ NBTTagList enchantments = rootTag.getTagList("ench", 10);
+
+ NBTTagCompound enchTag = new NBTTagCompound();
+ int enchId = Enchantment.getEnchantmentID(enchantment);
+
+ int id = -1;
+ for(int i = 0; i < enchantments.tagCount(); i++) {
+ if(enchantments.getCompoundTagAt(i).getShort("id") == enchId) {
+ enchTag = enchantments.getCompoundTagAt(i);
+ id = i;
+ break;
+ }
+ }
+
+ int level = enchTag.getShort("lvl") + 1;
+ level = Math.min(level, enchantment.getMaxLevel());
+ enchTag.setShort("id", (short) enchId);
+ enchTag.setShort("lvl", (short) level);
+
+ if(id < 0) {
+ enchantments.appendTag(enchTag);
+ }
+ else {
+ enchantments.set(id, enchTag);
+ }
+
+ rootTag.setTag("ench", enchantments);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/lance5057/tDefense/util/ArmorEvent.java b/src/main/java/lance5057/tDefense/util/ArmorEvent.java
new file mode 100644
index 0000000..d83a930
--- /dev/null
+++ b/src/main/java/lance5057/tDefense/util/ArmorEvent.java
@@ -0,0 +1,65 @@
+package lance5057.tDefense.util;
+
+import com.google.common.collect.ImmutableList;
+
+import lance5057.tDefense.core.tools.bases.ArmorBase;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.NonNullList;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.eventhandler.Cancelable;
+import net.minecraftforge.fml.common.eventhandler.Event;
+import slimeknights.tconstruct.library.materials.Material;
+
+/**
+ * Base class for all tinkers events
+ */
+public abstract class ArmorEvent extends Event {
+
+ /**
+ * Fired when a tool is built.
+ * This happens every time a tool is loaded as well as when the player actually builds the tool.
+ * You can make changes to the tag compound and it'll land on the resulting tool, but its itemstack is not available.
+ */
+ public static class OnItemBuilding extends ArmorEvent {
+
+ public NBTTagCompound tag;
+ public final ImmutableList<Material> materials;
+ public final ArmorBase tool;
+
+ public OnItemBuilding(NBTTagCompound tag, ImmutableList<Material> materials, ArmorBase tool) {
+ this.tag = tag;
+ this.materials = materials;
+ this.tool = tool;
+ }
+
+ public static OnItemBuilding fireEvent(NBTTagCompound tag, ImmutableList<Material> materials, ArmorBase tool) {
+ OnItemBuilding event = new OnItemBuilding(tag, materials, tool);
+ MinecraftForge.EVENT_BUS.post(event);
+ return event;
+ }
+ }
+
+ /**
+ * Fired when the player tries to replace a toolpart.
+ * You can modify the input items to achieve different results, this will not modify the actual items in the game.
+ * If you're modifying the list itself, make sure to put new items into originally empty indices to prevent the usage of other items in the input. Just append to the list.
+ * You can not modify the tool that's getting modified
+ */
+ @Cancelable
+ public static class OnToolPartReplacement extends ArmorEvent {
+
+ /** The items in the tool station. Can be manipulated. */
+ public NonNullList<ItemStack> replacementParts;
+ public ItemStack toolStack;
+
+ public OnToolPartReplacement(NonNullList<ItemStack> replacementParts, ItemStack toolStack) {
+ this.replacementParts = replacementParts;
+ this.toolStack = toolStack.copy();
+ }
+
+ public static boolean fireEvent(NonNullList<ItemStack> replacementParts, ItemStack toolStack) {
+ return !MinecraftForge.EVENT_BUS.post(new OnToolPartReplacement(replacementParts, toolStack));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/lance5057/tDefense/util/TDClientRegistry.java b/src/main/java/lance5057/tDefense/util/TDClientRegistry.java
new file mode 100644
index 0000000..8fd47a1
--- /dev/null
+++ b/src/main/java/lance5057/tDefense/util/TDClientRegistry.java
@@ -0,0 +1,48 @@
+package lance5057.tDefense.util;
+
+import com.google.common.collect.Maps;
+
+import net.minecraft.item.Item;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+import org.apache.logging.log4j.Logger;
+
+import java.util.Map;
+
+import slimeknights.tconstruct.library.Util;
+import slimeknights.tconstruct.library.client.ToolBuildGuiInfo;
+import slimeknights.tconstruct.library.client.texture.AbstractColoredTexture;
+
+@SideOnly(Side.CLIENT)
+public final class TDClientRegistry {
+
+ // the logger for the library
+ public static final Logger log = Util.getLogger("API-Client");
+
+ private TDClientRegistry() {
+ }
+
+ /*---------------------------------------------------------------------------
+ | GUI & CRAFTING |
+ ---------------------------------------------------------------------------*/
+ private static final Map<Item, ArmorBuildGuiInfo> armorBuildInfo = Maps.newLinkedHashMap();
+
+ public static void addArmorBuilding(ArmorBuildGuiInfo info) {
+ armorBuildInfo.put(info.armor.getItem(), info);
+ }
+
+ public static ArmorBuildGuiInfo getArmorBuildInfoForArmor(Item armor) {
+ return armorBuildInfo.get(armor);
+ }
+
+ public static void clear() {
+ armorBuildInfo.clear();
+ }
+
+ /*---------------------------------------------------------------------------
+ | MATERIAL TEXTURE CREATION |
+ ---------------------------------------------------------------------------*/
+ private static final Map<String, AbstractColoredTexture> textureProcessors = Maps.newHashMap();
+
+} \ No newline at end of file
diff --git a/src/main/java/lance5057/tDefense/util/TDModelLoader.java b/src/main/java/lance5057/tDefense/util/TDModelLoader.java
new file mode 100644
index 0000000..5ecdacf
--- /dev/null
+++ b/src/main/java/lance5057/tDefense/util/TDModelLoader.java
@@ -0,0 +1,204 @@
+package lance5057.tDefense.util;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.apache.commons.io.FilenameUtils;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import gnu.trove.map.hash.TIntObjectHashMap;
+import lance5057.tDefense.core.tools.bases.ArmorCore;
+import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
+import net.minecraft.client.resources.IResourceManager;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.client.model.ICustomModelLoader;
+import net.minecraftforge.client.model.IModel;
+import net.minecraftforge.client.model.ModelLoaderRegistry;
+import net.minecraftforge.common.model.TRSRTransformation;
+import slimeknights.tconstruct.library.TinkerRegistry;
+import slimeknights.tconstruct.library.client.CustomTextureCreator;
+import slimeknights.tconstruct.library.client.model.MaterialModel;
+import slimeknights.tconstruct.library.client.model.MaterialModelLoader;
+import slimeknights.tconstruct.library.client.model.ModelHelper;
+import slimeknights.tconstruct.library.client.model.ModifierModel;
+import slimeknights.tconstruct.library.client.model.ModifierModelLoader;
+import slimeknights.tconstruct.library.client.model.ToolModel;
+import slimeknights.tconstruct.library.client.model.format.AmmoPosition;
+import slimeknights.tconstruct.library.client.model.format.ToolModelOverride;
+import slimeknights.tconstruct.library.tools.IToolPart;
+
+public class TDModelLoader implements ICustomModelLoader {
+
+ public static String EXTENSION = ".td";
+
+ // used to create only actually needed textures in the texturegenerator instead of ALL materials for all parts
+ private static final Map<ResourceLocation, ArmorCore> modelItemMap = Maps.newHashMap();
+
+ public static void addPartMapping(ResourceLocation resourceLocation, ArmorCore tool) {
+ modelItemMap.put(resourceLocation, tool);
+ }
+
+ @Override
+ public boolean accepts(ResourceLocation modelLocation) {
+ return modelLocation.getResourcePath().endsWith(EXTENSION); // tinkertoolmodel extension. Foo.tcon.json
+ }
+
+ @Override
+ public IModel loadModel(ResourceLocation modelLocation) {
+ try {
+ // Modelblock is used since our format is compatible to the vanilla format
+ // and we don't have to write our own json deserializer
+ // it also provides us with the textures
+ Map<String, String> textures = ModelHelper.loadTexturesFromJson(modelLocation);
+ ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms = ModelHelper.loadTransformFromJson(modelLocation);
+ ImmutableList<ToolModelOverride> overrides = ModelHelper.loadToolModelOverridesFromJson(modelLocation);
+ AmmoPosition ammoPosition = ModelHelper.loadAmmoPositionFromJson(modelLocation);
+ Float[] rotations = ModelHelper.loadLayerRotations(modelLocation);
+
+ if(rotations.length > 0 && textures.size() != rotations.length) {
+ TinkerRegistry.log.error("Toolmodel {} has invalid layerrotation entry: Size should be {} but is {}; Skipping rotations.", modelLocation, textures.size(), rotations.length);
+ rotations = new Float[0];
+ }
+
+ ImmutableList.Builder<ResourceLocation> defaultTextureListBuilder = ImmutableList.builder();
+ List<MaterialModel> parts = Lists.newArrayList();
+ List<MaterialModel> brokenParts = Lists.newArrayList();
+
+ ArmorCore ArmorCore = modelItemMap.get(MaterialModelLoader.getReducedPath(modelLocation));
+
+ for(Map.Entry<String, String> entry : textures.entrySet()) {
+ String name = entry.getKey();
+ try {
+ int i;
+ List<MaterialModel> listToAdd;
+
+ if(name.startsWith("layer")) {
+ i = Integer.valueOf(name.substring(5));
+ listToAdd = parts;
+ }
+ else if(name.startsWith("broken")) {
+ i = Integer.valueOf(name.substring(6));
+ listToAdd = brokenParts;
+ }
+ // invalid entry, ignore
+ else {
+ TinkerRegistry.log.warn("Toolmodel {} has invalid texture entry {}; Skipping layer.", modelLocation, name);
+ continue;
+ }
+
+ ResourceLocation location = new ResourceLocation(entry.getValue());
+ MaterialModel partModel = new MaterialModel(ImmutableList.of(location));
+ while(listToAdd.size() <= i) {
+ listToAdd.add(null);
+ }
+ listToAdd.set(i, partModel);
+
+ defaultTextureListBuilder.add(location);
+ registerCustomTextures(i, location, ArmorCore);
+ } catch(NumberFormatException e) {
+ TinkerRegistry.log.error("Toolmodel {} has invalid texture entry {}; Skipping layer.", modelLocation, name);
+ }
+ }
+
+ // create overrides
+ for(ToolModelOverride override : overrides) {
+ for(Map.Entry<String, String> entry : override.textures.entrySet()) {
+ String name = entry.getKey();
+ try {
+ int i;
+ TIntObjectHashMap<MaterialModel> mapToAdd;
+
+ if(name.startsWith("layer")) {
+ i = Integer.valueOf(name.substring(5));
+ mapToAdd = override.partModelReplacement;
+ }
+ else if(name.startsWith("broken")) {
+ i = Integer.valueOf(name.substring(6));
+ mapToAdd = override.brokenPartModelReplacement;
+ }
+ // invalid entry, ignore
+ else {
+ TinkerRegistry.log.warn("Toolmodel {} has invalid texture override entry {}; Skipping layer.", modelLocation, name);
+ continue;
+ }
+
+ ResourceLocation location = new ResourceLocation(entry.getValue());
+ MaterialModel partModel = new MaterialModel(ImmutableList.of(location));
+ mapToAdd.put(i, partModel);
+
+ registerCustomTextures(i, location, ArmorCore);
+ } catch(NumberFormatException e) {
+ TinkerRegistry.log.error("Toolmodel {} has invalid texture entry {}; Skipping layer.", modelLocation, name);
+ }
+ }
+ }
+
+ // remove models/item/ and .tcon
+ String toolName = FilenameUtils.removeExtension(modelLocation.getResourcePath().substring(12));
+ IModel mods;
+ ModifierModel modifiers = null;
+ try {
+ mods = ModelLoaderRegistry.getModel(ModifierModelLoader.getLocationForToolModifiers(modelLocation.getResourceDomain(), toolName));
+
+ if(mods == null || !(mods instanceof ModifierModel)) {
+ TinkerRegistry.log.trace(
+ "Toolmodel {} does not have any modifiers associated with it. Be sure that the Tools internal name, the Toolmodels filename and the name used inside the Modifier Model Definition match!",
+ modelLocation);
+ }
+ else {
+ modifiers = (ModifierModel) mods;
+
+ for(ToolModelOverride toolModelOverride : overrides) {
+ if(toolModelOverride.modifierSuffix != null) {
+ String modifierName = toolName + toolModelOverride.modifierSuffix;
+ IModel extraModel = ModelLoaderRegistry.getModel(ModifierModelLoader.getLocationForToolModifiers(modelLocation.getResourceDomain(), modifierName));
+ if(extraModel instanceof ModifierModel) {
+ ModifierModel overriddenModifierModel = new ModifierModel();
+ // fill in non-overridden modifiers
+ for(Map.Entry<String, String> entry : modifiers.getModels().entrySet()) {
+ overriddenModifierModel.addModelForModifier(entry.getKey(), entry.getValue());
+ }
+ // overwrite overridden modifiers
+ for(Map.Entry<String, String> entry : ((ModifierModel) extraModel).getModels().entrySet()) {
+ overriddenModifierModel.addModelForModifier(entry.getKey(), entry.getValue());
+ }
+ toolModelOverride.overrideModifierModel = overriddenModifierModel;
+ }
+ }
+ }
+ }
+ } catch(Exception e) {
+ TinkerRegistry.log.error(e);
+ modifiers = null;
+ }
+
+ return new ToolModel(defaultTextureListBuilder.build(), parts, brokenParts, rotations, modifiers, transforms, overrides, ammoPosition);
+ } catch(IOException e) {
+ TinkerRegistry.log.error("Could not load multimodel {}", modelLocation.toString());
+ }
+ return ModelLoaderRegistry.getMissingModel();
+ }
+
+ private void registerCustomTextures(int i, ResourceLocation resourceLocation, ArmorCore ArmorCore) {
+ if(ArmorCore == null) {
+ CustomTextureCreator.registerTexture(resourceLocation);
+ }
+ else {
+ for(IToolPart part : ArmorCore.getRequiredComponents().get(i).getPossibleParts()) {
+ CustomTextureCreator.registerTextureForPart(resourceLocation, part);
+ }
+ }
+ }
+
+ @Override
+ public void onResourceManagerReload(@Nonnull IResourceManager resourceManager) {
+
+ }
+} \ No newline at end of file
diff --git a/src/main/java/lance5057/tDefense/util/TDModelRegistar.java b/src/main/java/lance5057/tDefense/util/TDModelRegistar.java
new file mode 100644
index 0000000..ad628b0
--- /dev/null
+++ b/src/main/java/lance5057/tDefense/util/TDModelRegistar.java
@@ -0,0 +1,62 @@
+package lance5057.tDefense.util;
+
+import javax.annotation.Nonnull;
+
+import lance5057.tDefense.core.tools.bases.ArmorCore;
+import net.minecraft.client.renderer.ItemMeshDefinition;
+import net.minecraft.client.renderer.block.model.ModelResourceLocation;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.client.model.ModelLoader;
+import slimeknights.tconstruct.TConstruct;
+import slimeknights.tconstruct.library.client.model.ToolModelLoader;
+import slimeknights.tconstruct.library.tools.ToolCore;
+
+public class TDModelRegistar {
+ public static ResourceLocation registerToolModel(ArmorCore armor) {
+ if (armor == null || armor.getRegistryName() == null) {
+ return null;
+ }
+ ResourceLocation itemLocation = armor.getRegistryName();
+ String path = "tools/" + itemLocation.getResourcePath() + ToolModelLoader.EXTENSION;
+
+ ResourceLocation location = new ResourceLocation(itemLocation.getResourceDomain(), path);
+ TDModelLoader.addPartMapping(location, armor);
+
+ return registerToolModel(armor, location);
+ }
+
+ /**
+ * Manual registration of a tool model. You probably shouldn't be using this.
+ */
+ public static ResourceLocation registerToolModel(Item item, final ResourceLocation location) {
+ if (!location.getResourcePath().endsWith(ToolModelLoader.EXTENSION)) {
+ TConstruct.log.error("The material-model " + location.toString() + " does not end with '"
+ + ToolModelLoader.EXTENSION + "' and will therefore not be loaded by the custom model loader!");
+ }
+
+ return registerIt(item, location);
+ }
+
+ private static ResourceLocation registerIt(Item item, final ResourceLocation location) {
+ // plop it in.
+ // This here is needed for the model to be found ingame when the game looks for
+ // a model to render an Itemstack
+ // we use an ItemMeshDefinition because it allows us to do it no matter what
+ // metadata we use
+ ModelLoader.setCustomMeshDefinition(item, new ItemMeshDefinition() {
+ @Nonnull
+ @Override
+ public ModelResourceLocation getModelLocation(@Nonnull ItemStack stack) {
+ return new ModelResourceLocation(location, "inventory");
+ }
+ });
+
+ // We have to readd the default variant if we have custom variants, since it
+ // wont be added otherwise and therefore not loaded
+ ModelLoader.registerItemVariants(item, location);
+
+ return location;
+ }
+}
diff --git a/src/main/java/lance5057/tDefense/util/TDRegistry.java b/src/main/java/lance5057/tDefense/util/TDRegistry.java
new file mode 100644
index 0000000..87fed5b
--- /dev/null
+++ b/src/main/java/lance5057/tDefense/util/TDRegistry.java
@@ -0,0 +1,58 @@
+package lance5057.tDefense.util;
+
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+import gnu.trove.set.hash.TLinkedHashSet;
+import lance5057.tDefense.core.tools.bases.ArmorCore;
+import slimeknights.tconstruct.library.TinkerRegistry;
+import slimeknights.tconstruct.library.tinkering.PartMaterialType;
+import slimeknights.tconstruct.library.tools.IToolPart;
+import slimeknights.tconstruct.library.tools.ToolCore;
+
+public class TDRegistry {
+ private static final Set<ArmorCore> armor = new TLinkedHashSet<>();
+ private static final Set<IToolPart> armorParts = new TLinkedHashSet<>();
+ private static final Set<ArmorCore> armorStationCrafting = Sets.newLinkedHashSet();
+ private static final Set<ArmorCore> armorForgeCrafting = Sets.newLinkedHashSet();
+
+ public static void registerTool(ArmorCore tool) {
+ armor.add(tool);
+
+ for(PartMaterialType pmt : tool.getRequiredComponents()) {
+ for(IToolPart tp : pmt.getPossibleParts()) {
+ TinkerRegistry.registerToolPart(tp);
+ }
+ }
+ }
+
+ /** Adds a armor to the Crafting UI of both the armor Station as well as the armor Forge */
+ public static void registerArmorCrafting(ArmorCore armor) {
+ registerArmorStationCrafting(armor);
+ registerArmorForgeCrafting(armor);
+ }
+
+ /** Adds a armor to the Crafting UI of the armor Station */
+ public static void registerArmorStationCrafting(ArmorCore armor) {
+ armorStationCrafting.add(armor);
+ }
+
+ public static ImmutableSet<ArmorCore> getArmorStationCrafting() {
+ return ImmutableSet.copyOf(armorStationCrafting);
+ }
+
+ /** Adds a armor to the Crafting UI of the armor Forge */
+ public static void registerArmorForgeCrafting(ArmorCore armor) {
+ armorForgeCrafting.add(armor);
+ }
+
+ public static ImmutableSet<ArmorCore> getArmorForgeCrafting() {
+ return ImmutableSet.copyOf(armorForgeCrafting);
+ }
+
+ public static Set<ArmorCore> getArmor() {
+ return ImmutableSet.copyOf(armor);
+ }
+}