diff options
Diffstat (limited to 'src/main/java/lance5057/tDefense/util')
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); + } +} |
