From 79e2e72a8b514168a9425094331ddad38268fe00 Mon Sep 17 00:00:00 2001 From: Yannick Marcotte-Gourde Date: Fri, 19 Jan 2024 13:45:49 -0500 Subject: [PATCH] Allow customizing Tinker's Construct manuals in dreamcraft (#794) * Apply spotless * Added unit testing * Include mod dependencies in tests * Copied TiCo manuals from assets/tinker/manuals/en_US in TinkersConstruct No changes made to the content yet to help content review * Added BookDataStoreProxy * Added BookDataReader * Added BookLoader interface as a facade for the com.dreammaster.mantle package * Load Tinker's Construct books from dreamcraft * Reduced visibility of MantleBookLoader members --- addon.gradle | 9 + dependencies.gradle | 9 + .../ItemFocusWardingTransformer.java | 6 +- .../com/dreammaster/main/MainRegistry.java | 11 +- .../dreammaster/mantle/BookDataReader.java | 50 + .../mantle/BookDataStoreProxy.java | 38 + .../com/dreammaster/mantle/BookLoader.java | 16 + .../dreammaster/mantle/MantleBookLoader.java | 79 ++ .../scripts/ScriptTinkersConstruct.java | 22 + .../dreamcraft/tinker/manuals/diary.xml | 167 +++ .../dreamcraft/tinker/manuals/firstday.xml | 407 ++++++ .../dreamcraft/tinker/manuals/materials.xml | 1107 +++++++++++++++++ .../dreamcraft/tinker/manuals/smeltery.xml | 296 +++++ .../dreamcraft/tinker/manuals/weaponry.xml | 144 +++ .../mantle/BookDataReaderTest.java | 51 + .../mantle/BookDataStoreProxyTest.java | 75 ++ .../mantle/MantleBookLoaderTest.java | 170 +++ .../assets/dreamcraft/mantle/blank-book.xml | 0 .../assets/dreamcraft/mantle/test-book.xml | 4 + 19 files changed, 2659 insertions(+), 2 deletions(-) create mode 100644 addon.gradle create mode 100644 src/main/java/com/dreammaster/mantle/BookDataReader.java create mode 100644 src/main/java/com/dreammaster/mantle/BookDataStoreProxy.java create mode 100644 src/main/java/com/dreammaster/mantle/BookLoader.java create mode 100644 src/main/java/com/dreammaster/mantle/MantleBookLoader.java create mode 100644 src/main/resources/assets/dreamcraft/tinker/manuals/diary.xml create mode 100644 src/main/resources/assets/dreamcraft/tinker/manuals/firstday.xml create mode 100644 src/main/resources/assets/dreamcraft/tinker/manuals/materials.xml create mode 100644 src/main/resources/assets/dreamcraft/tinker/manuals/smeltery.xml create mode 100644 src/main/resources/assets/dreamcraft/tinker/manuals/weaponry.xml create mode 100644 src/test/java/com/dreammaster/mantle/BookDataReaderTest.java create mode 100644 src/test/java/com/dreammaster/mantle/BookDataStoreProxyTest.java create mode 100644 src/test/java/com/dreammaster/mantle/MantleBookLoaderTest.java create mode 100644 src/test/resources/assets/dreamcraft/mantle/blank-book.xml create mode 100644 src/test/resources/assets/dreamcraft/mantle/test-book.xml diff --git a/addon.gradle b/addon.gradle new file mode 100644 index 000000000..70474015e --- /dev/null +++ b/addon.gradle @@ -0,0 +1,9 @@ +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } +} +configurations { + testImplementation.extendsFrom compileOnly +} diff --git a/dependencies.gradle b/dependencies.gradle index bba8fc113..a3c0f8619 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -27,4 +27,13 @@ dependencies { runtimeOnlyNonPublishable("curse.maven:biomes-o-plenty-220318:2499612") runtimeOnlyNonPublishable("com.github.GTNewHorizons:WailaHarvestability:1.2.0-GTNH:dev") + + // Core unit testing platform + testImplementation(platform('org.junit:junit-bom:5.9.2')) + testImplementation('org.junit.jupiter:junit-jupiter') + + // Extra unit testing libraries + testImplementation('org.assertj:assertj-core:3.+') + testImplementation('org.mockito:mockito-core:5.+') + testImplementation('org.mockito:mockito-junit-jupiter:5.+') } diff --git a/src/main/java/com/dreammaster/coremod/transformers/ItemFocusWardingTransformer.java b/src/main/java/com/dreammaster/coremod/transformers/ItemFocusWardingTransformer.java index cc4760409..da98660aa 100644 --- a/src/main/java/com/dreammaster/coremod/transformers/ItemFocusWardingTransformer.java +++ b/src/main/java/com/dreammaster/coremod/transformers/ItemFocusWardingTransformer.java @@ -1,6 +1,10 @@ package com.dreammaster.coremod.transformers; -import static org.objectweb.asm.Opcodes.*; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.ARETURN; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.POP; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.tree.AbstractInsnNode; diff --git a/src/main/java/com/dreammaster/main/MainRegistry.java b/src/main/java/com/dreammaster/main/MainRegistry.java index 424c64771..6a03ff956 100644 --- a/src/main/java/com/dreammaster/main/MainRegistry.java +++ b/src/main/java/com/dreammaster/main/MainRegistry.java @@ -1,7 +1,16 @@ package com.dreammaster.main; import static gregtech.api.enums.Dyes.MACHINE_METAL; -import static gregtech.api.enums.Mods.*; +import static gregtech.api.enums.Mods.Avaritia; +import static gregtech.api.enums.Mods.BartWorks; +import static gregtech.api.enums.Mods.BloodMagic; +import static gregtech.api.enums.Mods.GalactiGreg; +import static gregtech.api.enums.Mods.Railcraft; +import static gregtech.api.enums.Mods.SGCraft; +import static gregtech.api.enums.Mods.Thaumcraft; +import static gregtech.api.enums.Mods.TinkerConstruct; +import static gregtech.api.enums.Mods.TwilightForest; +import static gregtech.api.enums.Mods.Witchery; import static gregtech.api.recipe.RecipeMaps.compressorRecipes; import static gregtech.api.util.GT_RecipeBuilder.SECONDS; diff --git a/src/main/java/com/dreammaster/mantle/BookDataReader.java b/src/main/java/com/dreammaster/mantle/BookDataReader.java new file mode 100644 index 000000000..4d46ce5f3 --- /dev/null +++ b/src/main/java/com/dreammaster/mantle/BookDataReader.java @@ -0,0 +1,50 @@ +package com.dreammaster.mantle; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; + +import javax.annotation.Nonnull; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +/** + * This class doesn't support loading different XML documents based on Minecraft's currently configured language. Books + * are instead intended to be translated through lang files. + */ +class BookDataReader { + + private static final DocumentBuilderFactory DB_FACTORY = DocumentBuilderFactory.newInstance(); + + @Nonnull + Document readBook(String xmlDocumentLocation) { + try (InputStream stream = loadBook(xmlDocumentLocation)) { + return readBookDocument(stream); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new IllegalStateException( + "Failed to load book data from " + xmlDocumentLocation + ":\n" + e.getMessage(), + e); + } + } + + @Nonnull + private InputStream loadBook(String path) throws IOException { + InputStream inputStream = getClass().getResourceAsStream(path); + if (Objects.isNull(inputStream)) { + throw new IOException("File " + path + " does not exist."); + } + return inputStream; + } + + @Nonnull + private Document readBookDocument(InputStream stream) + throws ParserConfigurationException, SAXException, IOException { + Document doc = DB_FACTORY.newDocumentBuilder().parse(stream); + doc.getDocumentElement().normalize(); + return doc; + } + +} diff --git a/src/main/java/com/dreammaster/mantle/BookDataStoreProxy.java b/src/main/java/com/dreammaster/mantle/BookDataStoreProxy.java new file mode 100644 index 000000000..d499b8386 --- /dev/null +++ b/src/main/java/com/dreammaster/mantle/BookDataStoreProxy.java @@ -0,0 +1,38 @@ +package com.dreammaster.mantle; + +import java.util.Objects; + +import com.dreammaster.main.MainRegistry; + +import eu.usrv.yamcore.auxiliary.LogHelper; +import mantle.books.BookData; +import mantle.books.BookDataStore; + +/** + * This class helps us avoid issues with books already loaded by another mod. + */ +class BookDataStoreProxy { + + private static final BookDataStoreProxy INSTANCE = new BookDataStoreProxy(MainRegistry.Logger); + + static BookDataStoreProxy getInstance() { + return INSTANCE; + } + + private final LogHelper logger; + + BookDataStoreProxy(LogHelper logger) { + Objects.requireNonNull(logger); + this.logger = logger; + + } + + void addBook(BookData bookData) { + Objects.requireNonNull(bookData); + try { + BookDataStore.addBook(bookData); + } catch (IllegalArgumentException e) { + logger.error("Cannot override book " + bookData.unlocalizedName + " which is already defined elsewhere."); + } + } +} diff --git a/src/main/java/com/dreammaster/mantle/BookLoader.java b/src/main/java/com/dreammaster/mantle/BookLoader.java new file mode 100644 index 000000000..41c2c1a7c --- /dev/null +++ b/src/main/java/com/dreammaster/mantle/BookLoader.java @@ -0,0 +1,16 @@ +package com.dreammaster.mantle; + +public interface BookLoader { + + static BookLoader of(String unlocalizedName, String modId, String xmlDocumentPath) { + return MantleBookLoader.readBook(unlocalizedName, modId, xmlDocumentPath); + } + + BookLoader setTooltip(String tooltip); + + BookLoader setItemImage(String itemImage); + + BookLoader makeTranslatable(); + + void addToBookDataStore(); +} diff --git a/src/main/java/com/dreammaster/mantle/MantleBookLoader.java b/src/main/java/com/dreammaster/mantle/MantleBookLoader.java new file mode 100644 index 000000000..cbc400b77 --- /dev/null +++ b/src/main/java/com/dreammaster/mantle/MantleBookLoader.java @@ -0,0 +1,79 @@ +package com.dreammaster.mantle; + +import java.util.Objects; +import java.util.stream.Stream; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; + +import gregtech.GT_Mod; +import gregtech.api.interfaces.internal.IGT_Mod; +import mantle.books.BookData; + +final class MantleBookLoader implements BookLoader { + + private static final BookDataReader BOOK_DATA_READER = new BookDataReader(); + private static final BookDataStoreProxy BOOK_DATA_STORE_PROXY = BookDataStoreProxy.getInstance(); + private final BookDataStoreProxy bookDataStoreProxy; + private final BookData data; + private final IGT_Mod sideChecker; + private final BookDataReader bookDataReader; + + static BookLoader readBook(String unlocalizedName, String modId, String xmlDocumentPath) { + return new MantleBookLoader(BOOK_DATA_STORE_PROXY, BOOK_DATA_READER, GT_Mod.gregtechproxy) + .setRequiredData(unlocalizedName, modId, xmlDocumentPath); + } + + @VisibleForTesting + MantleBookLoader(BookDataStoreProxy bookDataStoreProxy, BookDataReader bookDataReader, IGT_Mod sideChecker) { + Stream.of(bookDataStoreProxy, bookDataReader, sideChecker).forEach(Objects::requireNonNull); + this.bookDataStoreProxy = bookDataStoreProxy; + this.bookDataReader = bookDataReader; + this.sideChecker = sideChecker; + this.data = new BookData(); + } + + @VisibleForTesting + BookLoader setRequiredData(String unlocalizedName, String modId, String xmlDocumentPath) { + Objects.requireNonNull(unlocalizedName); + Objects.requireNonNull(modId); + Objects.requireNonNull(xmlDocumentPath); + this.data.unlocalizedName = unlocalizedName; + this.data.modID = modId; + if (sideChecker.isClientSide()) { + data.doc = bookDataReader.readBook(xmlDocumentPath); + } + return this; + } + + @Override + public BookLoader setTooltip(String tooltip) { + Objects.requireNonNull(tooltip); + data.toolTip = "§o" + StatCollector.translateToLocal(tooltip); + return this; + } + + @Override + public BookLoader setItemImage(String itemImage) { + Objects.requireNonNull(itemImage); + data.itemImage = new ResourceLocation(data.modID, itemImage); + return this; + } + + @Override + public BookLoader makeTranslatable() { + data.isTranslatable = true; + return this; + } + + @Override + public void addToBookDataStore() { + if (Strings.isNullOrEmpty(data.unlocalizedName)) { + throw new IllegalStateException("You must call setRequiredData before addToBookDataStore."); + } + bookDataStoreProxy.addBook(data); + } +} diff --git a/src/main/java/com/dreammaster/scripts/ScriptTinkersConstruct.java b/src/main/java/com/dreammaster/scripts/ScriptTinkersConstruct.java index f58e40fcc..d218d7084 100644 --- a/src/main/java/com/dreammaster/scripts/ScriptTinkersConstruct.java +++ b/src/main/java/com/dreammaster/scripts/ScriptTinkersConstruct.java @@ -12,6 +12,7 @@ import static gregtech.api.enums.Mods.GregTech; import static gregtech.api.enums.Mods.IguanaTweaksTinkerConstruct; import static gregtech.api.enums.Mods.IndustrialCraft2; +import static gregtech.api.enums.Mods.Mantle; import static gregtech.api.enums.Mods.Minecraft; import static gregtech.api.enums.Mods.Natura; import static gregtech.api.enums.Mods.NewHorizonsCoreMod; @@ -33,6 +34,7 @@ import net.minecraftforge.oredict.OreDictionary; import com.dreammaster.gthandler.CustomItemList; +import com.dreammaster.mantle.BookLoader; import com.dreammaster.oredict.OreDictHelper; import com.dreammaster.tinkersConstruct.TConstructHelper; @@ -50,6 +52,15 @@ public class ScriptTinkersConstruct implements IScriptLoader { + /** + * Purposefully adding this static method here and keeping it private rather than adding onto TConstructHelper to + * avoid future proliferation of overloads of this method. + */ + private static void addBook(String bookName, String unlocalizedName, String tooltip, String itemImage) { + BookLoader.of(unlocalizedName, TinkerConstruct.ID, "/assets/dreamcraft/tinker/manuals/" + bookName + ".xml") + .setTooltip(tooltip).setItemImage(itemImage).makeTranslatable().addToBookDataStore(); + } + @Override public String getScriptName() { return "Tinkers Construct"; @@ -59,6 +70,7 @@ public String getScriptName() { public List getDependencies() { return Arrays.asList( TinkerConstruct.ID, + Mantle.ID, RandomThings.ID, TinkersMechworks.ID, BloodArsenal.ID, @@ -3499,5 +3511,15 @@ public void loadRecipes() { .itemOutputs(getModItem(TinkerConstruct.ID, "decoration.stoneladder", 4, 0, missing)) .duration(3 * SECONDS).eut(30).addTo(assemblerRecipes); + addManuals(); } + + private void addManuals() { + addBook("firstday", "tconstruct.manual.beginner", "manual1.tooltip", "tinker:tinkerbook_diary"); + addBook("materials", "tconstruct.manual.toolstation", "manual2.tooltip", "tinker:tinkerbook_toolstation"); + addBook("smeltery", "tconstruct.manual.smeltery", "manual3.tooltip", "tinker:tinkerbook_smeltery"); + addBook("diary", "tconstruct.manual.diary", "manual4.tooltip", "tinker:tinkerbook_blue"); + addBook("weaponry", "tconstruct.manual.weaponry", "manual5.tooltip", "tinker:tinkerbook_green"); + } + } diff --git a/src/main/resources/assets/dreamcraft/tinker/manuals/diary.xml b/src/main/resources/assets/dreamcraft/tinker/manuals/diary.xml new file mode 100644 index 000000000..01beed1c1 --- /dev/null +++ b/src/main/resources/assets/dreamcraft/tinker/manuals/diary.xml @@ -0,0 +1,167 @@ + + + + +Tinker's Log #1: + +Today is a new day. I finally left home and decided to wander around in the wilderness until I find a good place to call my home. + +Tinker's Log #2: + +I've decided to keep a log of my creations and machinations, as well as some insights into what I'm doing. I haven't actually built anything yet. There is a small cave I can board up for the night. I created a stone pickaxe affectionately called "Betsy". + + + +Tinker's Log #3: + +The night is scary. Creepers are everywhere, the zombies are banging on my door, bats block out the sky... it is not fun being alone out here. I want to hide in the corner and cry until they leave. This log doesn't help take my mind off of anything. + +Tinker's Log #4: + +Betsy didn't last long. She just crumpled into dust suddenly and the handle shattered. My parents told me this would happen and this was normal, but it still seems so wrong. If I could make the pickaxe a little better perhaps, maybe I could repair it. + + + +Tinker's Log #5: + +I've spent the past three days looking for paper and ink. Squids were easy enough to find, but the sugar cane was buried deep in a ravine next to a creeper nest. Eventually I dropped rocks on the creepers and watched them explode out of surprise. My eyebrow will grow back in time. + +Tinker's Log #6: + +Waiting on sugar cane to grow. Not much else today. + + + +Tinker's Log #7: + +I've sketched out plans for a new workbench. It's a little strange. The center of the workbench is missing, but sturdy, and there are grooves in the top for supplies. I think I will call it a "table". + +Tinker's Log #8: + +I made a few more tables with different patterns in them. They look useful for different things, so I put different materials and tools by each one. Perhaps I will come up with a use for them in time. + + + +Tinker's Log #9: + +To make tools, you need to know what you're making. The pickaxe and shovel are easy enough to make with practice, the best crafters in my village told me, but I can't seem to make any good ones. Out of frustration I punched a hole in a wooden board. And another. And a few more. By the end I had something that resembled an axe, and that gave me a few ideas... + + + +Tinker's Log #10: + +I punched holes in a few boards carefully in different shapes. I spent a lot of time making them, so they look really good. They all resemble the parts of tools that I can remember: heads, handles, bindings, pieces, and some others. They look really useful, so I placed them by a table for later. + + + +Tinker's Log #11: + +I grabbed a few of the boards and started to make tool parts using them as an outline. The parts themselves feel sturdy, and when I put them together the end result felt a lot better than Betsy did. They're made completely out of stone, something even the finest crafters could never manage. This feels like a great accomplishment. + +Tinker's Log #12: + +Perhaps making a tool completely out of stone wasn't the best idea. It didn't last very long. On the upside, instead of shattering into dust only the tip scratched off. I glued a new tip on the head and it seemed good as new. + + + +Tinker's Log #13: + +I wanted to make a few more tools, but the boards with tool shapes in them went everywhere! I ended up breaking a few out of frustration. They don't fit very well in my chest either. I took a few of them, added a few slots to the bottom of the chest, placed it by my table and called it good. + +I also named them "Patterns", the chest a "Pattern Chest". I'm still angry at them, and it took so long to make new ones... + + + +Tinker's Log #14 + +Today I learned just how hard it was to shape iron. First you have to dig it out of the earth. A wooden pickaxe will shatter the ore; this I found out the hard way. Once you do get that ore there isn't much to do with it besides throw it in the furnace. Melting the iron and the stone seems inefficient, but I do need the metal more than I care about the waste. + +I shaped a few of the bars into tools, and they felt strong. Much stronger, in fact, than the ones back at the village. I've named the pickaxe "Krug" after some of the stories Nana used to tell me at night. How I miss her... + + + +Tinker's Log #15: + +This place holds little for me now. I've packed up my things, grabbed what resources I can carry, and set off. The tables and patterns will have to stay behind. I can't possibly carry them with all the food I have. + +Tinker's Log #16: + +A week later and I've finally found some others to visit. Some of them seem nice, most don't look like me. A few of them have things I never could have dreamed of. They've pointed me at a patch of land that was quiet and secluded. There I should be able to work, for I have ideas from the journey. + + + +Tinker's Log #17: + +After three days and nights of fending off zombies, I've finally created a house. It has a few rooms, a basement, but most importantly a workshop. All of the tables and patterns I had before are here, as well as a few others I wanted to try out from the trip. + +There's a round one I can cook food with and one that's just as good as any hoe, but can chop trees or actually dig the ground up. The villagers tell me these are named "Frying Pan" and "Mattock". The Frying Pan seems heavy enough to smack creepers with. I have a sword named "Shimmer" that I want to alter somehow. + + + +Tinker's Log #18: + +One of the villagers was kind enough to give me some random things. Each one looks useful in their own right. I was lucky enough to find a few diamonds earlier, and I have a few more tools in storage. This should be fun. + + + +Tinker's Log #19: + +After some heavy experimentation, I've ended up with some things that look amazing and others that look less useful than they actually are. A diamond tip on a pickaxe makes for a wonderfully hard mining tool, and an emerald on a sword hilt somehow makes it stay together longer, but some things don't do anything. + +Adding flowers to a tool does nothing; they don't even stay on the thing. Mushrooms have a similar effect, and adding more material to a tool just makes it heavier. You wouldn't think redstone would do anything. I've coated a shovel so much that it looks deep red. However, it slides through dirt as if the dirt wasn't there. + + + +Moss is also a strange one. Somehow it seems to be healing the tool, even to the point where it looks like new. + +Tinker's Log #20: + +I tried to make some tools out of gold, like the crafters in my village used to, but instead it broke the table. Some of the villages laughed at me for such an idea... if only they knew. + + + +Tinker's Log #21: + +Some of these tools are more useful than I thought. Adding blaze powder to a weapon seems to give it some kind of burning effect. Lapis has this strange quality of being able to find more materials in the same area, even if I know exactly what I'm looking at. + +Tinker's Log #22: + +I have some ideas for creating tools, but for that I need a large place to create them. The furnace is inadequate, and I need a lot of stone. Combining lava with coal and netherrack seems to instantly cook stone as I mine it, up to the point where I can bypass the furnace entirely. + + + +Tinker's Log #23: + +Today I made something strange. It's round, wooden, and doesn't look like anything I've ever seen. Someone came by and said it was a "wheel" and that they were great fun to roll around. I wonder what else it could be used for? + +Tinker's Log #24: + +I made a few more of the wheels to get some practice in. I had some fun rolling them around, rolling them in pairs, and then one ended up beside my boat. It just looked so natural I had to start putting them together. + + + +Tinker's Log #25: + +I think I'm on to something here. Combining the boat with the wheel made it work something like a minecart, only I can pull it around places. It feels a little small though. + +Tinker's Log #26: + +I have all these tools laying around with little notes on what each one does. Instead of leaving them scattered about, I think I will create one place to store them. + + + +Tinker's Log #27: + +I went exploring and found some new ores today. They're colors I've never seen before, and they don't shatter on impact like iron does. I wonder what I can do with these? + +Tinker's Log #28: + +The normal furnace is nice for keeping warm, but it leaves something to be desired for processing metal. I've dug a pit and lined it with some clay, gathered lava nearby, and put ore in the center in an attempt to improve on the design. The results were quite interesting. + + + +The more lava I fed into the pit the hotter the material became. Eventually the whole thing liquified. I broke open the side of the pit with a cauldron in an attempt to catch the material, but it went everywhere. There were some remnants left in the pit as well. + +Raw ore still has parts of stone in it. There must be a way to remove the excess. + + \ No newline at end of file diff --git a/src/main/resources/assets/dreamcraft/tinker/manuals/firstday.xml b/src/main/resources/assets/dreamcraft/tinker/manuals/firstday.xml new file mode 100644 index 000000000..132ada2de --- /dev/null +++ b/src/main/resources/assets/dreamcraft/tinker/manuals/firstday.xml @@ -0,0 +1,407 @@ + + + + +Materials and You +Surviving the first day and beyond + +Volume 1 +By Skyla + + + +Table of Contents + + Getting Started + sapling + 3 + + + Recipes + workbench + 5 + + + Armor Modifiers + travelvest + 4 + + + + +Welcome to the first edition of Materials and You: Surviving the first day and beyond. Within these pages you will find the first steps to making the tools and materials you need to survive. + +This book is a magic copy; it updates whenever the original has been modified. Check back occasionally for information on new things. + + + +The first step in making tools is crafting a blank pattern. It is a blank slate to stamp a shape into, providing a reference for future creations. The patterns are shaped on the Stencil Table or are used as tabletops. + +You can then shape a material in a part builder with the pattern, then combine parts in the tool station. Patterns can be stored and accessed in the pattern chest. + +Together these make the Tool Workshop. It is recommended you keep all of these nearby when using any of them. + + + +Blank Pattern + + blankpattern + two + + + + +Stencil Table + + stenciltable + two + + + + +Part Crafter + + partcrafter + two + + + + +Pattern Chest + + patternchest + two + + + + +Tool Station + + toolstation + two + + + + +Tool Forge + + toolforge + three + + + + +Drying Rack + + dryingrack + three + + + + +Oreberry Bushes + +Sometime in your travels you will happen upon the bush known as the Oreberry. They grow underground in dark areas, and require close to complete darkness to produce anything. + +The berries can be melted down into nuggets and ingots, making them an invaluable source of metals. + + + +Oreberries in their natural environment +tinker:manuals/oreberries.png + + + +Slime Channels + +Slime channels are useful for moving items or entities around. They are used as a sort of easily placeable water and can be paired with bounce pads for best effect. + + + +Slime Channel + + slimechannel + two + + + + +Bounce Pad + + bouncepad + two + + + + +A few traps or blockades may help with your early survival experience + + + +Punji Stick + + punji + three + + + + +Barricade + + barricade + two + + + + +Once you're well established, you can begin to process metals in a more efficient manner. The scope of the smeltery is beyond this volume, but a few recipes will help you get started. + +Note: The Smeltery is required to process all metals, including iron. + + + +Grout + + grout + two + + + + +Seared Brick +searedbrick + + + +Seared Bricks + + searedbricks + two + + + + +Smeltery Controller + + smelterycontroller + three + + + + +If you ever find yourself without this book, a new one is simple to make. Other books can be made from this one as well. + + + +Book + + alternatebook + three + + + + +Materials and You: v1 + + patternbook1 + two + + + + +Materials and You: v2 + + patternbook2 + two + + + + +Mighty Smelting + + patternbook3 + two + + + + +Armor Modifiers + +The following armor modifiers seem to be missing names or in flux. No word is available on the accuracy or new home of these pages. + + + +Night Vision +Apparently carrots and juice are not only good for your eyes, but for your goggles as well. +The Night Vision Potion also helps. + +Effects: +- Night Vision + +Type: Single-use +Stackable: No + + + +travelgoggles +nightvision + + + +Perfect Dodge +Ninja ninja. + +Effects: +- You're harder to hit + +Type: Single-use +Stackable: yes + + + +travelvest +dodge + + + +Stealth +Travelling gear is not very protective. Therefore I mixed some magical items and an Invisibility Potion together, to make myself stealthier! + +Effects: +- Turn invisible while sneaking + +Type: Single-use +Stackable: No + + + +travelvest +stealth + + + +Feather Fall +You already got wings. If chicken can do it, so can you. +Any slime variant can be used. + +Effects: +- Allows you to glide down slowly in air + +Type: Single-use +Stackable: yes + + + +Tinker Table + + featherfall + three + + + + +Water Walk +If I put these things on my boots, I'm sure I can walk over water too. What could go wrong? + +Effects: +- Allows you to walk over water +- Sneak to sink +- Does not come with a halo + +Type: Single-use +Stackable: no + + + +travelboots +waterwalk + + + +Lead boots +This iron block has served me well. I want to take it with me, to the bottom of the ocean. +Perfect if you want to see where your pet-fish sleeps. + +Effects: +- Allows you to walk under water +- Prevents you from swimming +- Blub. + +Type: Single-use +Stackable: no + + + +travelboots +leadboots + + + +Slimy Soles +When I jump on slime, I bounce and don't take damage. I should be able to replicate that effect by putting the slime onto my soles. + +Effects: +- Dampens the fall impact +- Reduces fall damage taken + +Type: Single-use +Stackable: yes + + + +travelboots +slimysoles + + + +Speed +Putting redstone on my weapon makes it faster. So why don't I put it on my hands directly! + +Effects: +- Increases mining speed + +Type: Multi-use +Stackable: yes + + + +travelglove +glovehaste + + + +Auto-Repair +Attaching moss to an armor infuses it with life. The tool appears to be capable of regenerating wear and tear. + +Effects: +- The tool slowly repairs itself +- Sunlight speeds up the process +- Can be applied to all travellers gear + +Type: Single-use +Stackable: yes + + + +travelmulti +moss + + + +Double Jump +Infused with the power of a ghast tear and some slime and piston mechanics, I should be able to repel myself even further into the air. + +Effects: +- Jump during jumping to jump +- Can be applied to wings as well + +Type: Single-use +Stackable: yes + + + +travelmulti +doublejump + + + \ No newline at end of file diff --git a/src/main/resources/assets/dreamcraft/tinker/manuals/materials.xml b/src/main/resources/assets/dreamcraft/tinker/manuals/materials.xml new file mode 100644 index 000000000..7685bb539 --- /dev/null +++ b/src/main/resources/assets/dreamcraft/tinker/manuals/materials.xml @@ -0,0 +1,1107 @@ + + + + +Materials and You +A Guide to Tools and Abilities + +Volume 2 +By Skyla + + + +Table of Contents + + Tools + ironpick + 2 + + + Materials + woodplanks + 4 + + + Modifiers + lavacrystal + 4 + + + + +Welcome to the second edition of Materials and You: A Guide to Tools and Abilities. Within these pages you will details on every known tool, modifier, and material. A detailed breakdown of everything known, and a few unknowns, lie here. + +This book is a magic copy; it updates whenever the original has been modified. Check back occasionally for information on new things. + + + +Pickaxe +The Pickaxe is a basic mining tool. It is the simplest method of chewing through rock and harvesting ores. +Class: Precision Tool +Effective on: Stone or rock, ores, and metal. +pickicon + + Pickaxe Head + pickhead + + + Tool Rod + toolrod + + + Tool Binding + binding + + + + +Shovel +The Shovel is a basic digging tool. It is the most common way of moving earth around. +Class: Precision Tool +Effective on: Dirt, sand, gravel, and snow. +shovelicon + + Shovel Head + shovelhead + + + Tool Rod + toolrod + + + + +Hatchet +The Hatchet is a basic chopping tool. It is the simplest way to shape lumber. +Class: Precision Tool +Effective on: Wood, Leaves, and Trees. +axeicon + + Hatchet Head + axehead + + + Tool Rod + toolrod + + + + +Mattock +The Cutter Mattock is a versatile farming tool. Its primary purpose is to till soil. It also works somewhat like an axe and a shovel, but is not a replacement for either. +Class: Multitool +Effective on: Wood, dirt, and plants. +mattockicon + + Axe Head + axehead + + + Tool Rod + toolrod + + + Shovel Head + shovelhead + + + + +Broadsword +The Broadsword is a defensive weapon. It is a favorite of many Tinkers and is useful in a wide variety of situations. +Right-click: Block +- Blocking cuts a wide variety of damage types in half. + +Class: Melee Weapon +Effective on webs. +swordicon + + Sword Blade + swordblade + + + Tool Rod + toolrod + + + Wide Guard + wideguard + + + + +Longsword +The Longsword is an offensive weapon. It is used for full-speed charges and plowing through enemy lines +Right-click: Lunge +- When released, it sends you careening forward at breakneck speed. + +Natural Abilities: +- Charge Boost, does 1.5x damage and knockback while sprinting + +Class: Melee Weapon +Effective on webs. +longswordicon + + Sword Blade + swordblade + + + Tool Rod + toolrod + + + Hand Guard + handguard + + + + +Rapier +The Rapier is a special weapon. The dancing swordplay lets you get in and out of battle quickly. While the Rapier's damage is low, it makes up for this with its other abilities. +Right-click: Backpedal +- Take a small hop backwards + +Natural Abilities: +- Charge Boost, does 1.5x damage and knockback while sprinting. +- Armor Pierce, ignores armor and blocking. +- Quick Strike, the enemy is stunned for less time. + +Class: Melee Weapon +rapiericon + + Sword Blade + swordblade + + + Tool Rod + toolrod + + + Crossbar + crossbar + + + + +Dagger +The Dagger is a short weapon. It is light and usable as both a striking and throwing weapon. +Right-click: Throw Dagger +- Toss the item for a ranged attack + +Class: Ranged/Melee Weapon +daggerIcon + + Knife Blade + knifeblade + + + Tool Rod + toolrod + + + Crossbar + crossbar + + + + +Frying Pan +The Frying is a heavy weapon that uses sheer weight to stun foes or a place to cook your food. It is common to hunt pigs and cook them with the same pan. +Right-click: Block +- Blocking cuts a wide variety of damage types in half. + +Shift+Right-click: Place Item +- The frying pan cooks a large amount of food at a time. + +Natural Abilities: +- Bash, does extra knockback and stuns the foe. +frypanicon + + Pan + pan + + + Tool Rod + toolrod + + + + +Battlesign +The Battlesign is an advance in weapon technology worthy of Zombie Pigmen everywhere. +Right-click: Block +- Blocking cuts a wide variety of damage types in half. + +Shift+Right-click: Place Item +- The battlesign can show up to five lines of text. + +Class: Lethal Joke Weapon +battlesignicon + + Board + board + + + Tool Rod + toolrod + + + + +Chisel +The Chisel is an advanced detailing tool for carving shapes into blocks. +Right-click: Chisel +- Transforms various blocks into bricks. +Crafting grid: Chisel +- Some items can only be chiseled in the crafting grid. + +Class: Utility Tool +chiselicon + + Chisel Head + chiselhead + + + Tool Rod + toolrod + + + + +Hammer +The Hammer is a heavy mining tool meant for digging out large areas. It is also effective on undead. +Natural Abilities: +- Area of Effect, Mines a 3x3 area. The direction depends on which face is pounded. +- Smite II, Does 2-4 extra hearts of damage against undead. +- Huge Durability, this tool has very high durability to make up for the constant beating it will receive. + +Class: Broad Mining Tool +hammericon + + Hammer Head + hammerhead + + + Large Plate + largeplate + + + Tough Rod + toughrod + + + Large Plate + largeplate + + + + +Lumber Axe +The Lumber Axe is capable of bringing down whole trees or clearing large swaths of food in a single swing. +Natural Abilities: +Fell Trees +- Harvests entire trees. It has a max height of 30. Trees are defined as Leaves on top of Logs. +Area of Effect +- Mines a 3x3x3 area. This is a fallback if a tree is not found. + +Class: Broad Mining Tool +lumbericon + + Broad Axe Head + broadaxehead + + + Large Plate + largeplate + + + Tough Rod + toughrod + + + Tough Binding + toughbinding + + + + +Excavator +The Excavator is a large digging tool used for clearing out large swaths of land, gravel, or snow. +Natural Abilities: +Area of Effect +- Mines a 3x3 area. The direction depends on which face is scooped. + +Class: Broad Mining Tool +excavatoricon + + Excavator Head + excavatorhead + + + Large Plate + largeplate + + + Tough Rod + toughrod + + + Tough Binding + toughbinding + + + + +Scythe +The Scythe is well known for its large swinging area. It can be used to harvest crops, leaves, and is a decent weapon. +Natural Abilities: +Area of Effect +- Attacks mobs in a 3x3x3. +- Harvests a 3x3x3 area. + +Class: Broad Melee Weapon +scytheicon + + Scythe Head + scythehead + + + Tough Rod + toughrod + + + Tough Binding + toughbinding + + + Tough Rod + toughrod + + + + +Cleaver +The Cleaver is a heavy defensive weapon. The sword is unwieldy, but the sheer weight of the tool crushes foes. +Right-click: Block +- Blocking cuts a wide variety of damage types in half. + +Natural Abilities: +- Beheading II, Beheads mobs. Their head is dropped as loot. +- Heavy, Base attack is increased at the cost of swing time + +Class: Heavy Melee Weapon +cleavericon + + Large Sword Blade + largeswordblade + + + Large Plate + largeplate + + + Tough Rod + toughrod + + + Tough Rod + toughrod + + + + +Battleaxe +The battleaxe is an offensive melee weapon that can also bring down small trees. +Right-click: TBD +- This ability is incomplete + +Natural Abilities: +- Area of Effect, harvests wood blocks in a 1x9 tower. +- Bash, knocks the enemy further away than normal. + +Class: Heavy Melee Weapon +battleaxeicon + + Broad Axe Head + broadaxehead + + + Broad Axe Head + broadaxehead + + + Tough Rod + toughrod + + + Tough Binding + toughbinding + + + + +Shortbow +The Shortbow is a weapon used to fire arrows. It has a quick, short range. +Right-click: Draw bow +- The further a bow is drawn, the further an arrow flies and the more damage it does. + +Class: Ranged Projectile Launcher +shortbowIcon + + Tool Rod + toolrod + + + Bowstring + bowstring + + + Tool Rod + toolrod + + + + +Arrow +Arrows are fired from bows as projectiles. They can also be used as stabbing weapons in a pinch. +Class: Ammunition +arrowIcon + + Arrowhead + arrowhead + + + Tool Rod + toolrod + + + Fletching + fletching + + + + +Material Traits +Some materials have traits that augment their natural uses. + +- Reinforced: 10% chance per level of not using durability +- Stonebound: The tool mines faster as it wears out, but does less damage +- Jagged: The tool attacks with does more damage as it wears out, but mines slower +- Writable: One extra modifier per piece +- Tasty: Sometimes drops food items + + + +Wood +woodaxe +It seems to grow everywhere. + +
woodplanks
+
+
+ + +Stone +stonepick +One of the most common materials in the world. + +
stoneblock
+
+
+ + +Iron +ironpick +A staple for tools, it can be refined and processed. +- Requires the Smeltery + +
ironingot
+
+
+ + +Flint +flintshovel +More durable than stone. The material of choice for cavemen. + +
flint
+
+
+ + +Cactus +cactussword +A form of wood found only in the desert. + +
cactus
+
+
+ + +Bone +bonerapier +A material with many uses, dead or alive. + +
bone
+
+
+ + +Obsidian +bonefrypan +Tough as nails, but fragile when shaped. + +
obsidian
+
+
+ + +Alumite +alumitepick +An uncommon alloy. It seems to be quite durable. +- Requires the Smeltery + +
alumiteingot
+
+
+ + +Netherrack +netherrackmattock +This is made of what?! + +
netherrack
+
+
+ + +Blue Slime +blueslimebattlesign +Converting it from food has made it stronger. +BlueSlime + +
blueslimecrystal
+
+
+ + +Green Slime +slimebattlesign +Soft and springy, it seems to last forever. +Slime + +
slimecrystal
+
+
+ + +Slimy Mud + + slimymud + three + + + + +Slime Crystal +slimecrystal + + + +Paper +paperpick +It's very weak, but can take a lot of modification. + +
paperstack
+
+
+ + +Paper Stack + + paperstack + two + + + + +Cobalt +cobaltlongsword +One of the nether materials. It's bright blue. +- Requires the Smeltery + +
cobaltingot
+
+
+ + +Ardite +arditelongsword +One of the nether materials. It seems to mine faster as it wears out. +- Requires the Smeltery + +
arditeingot
+
+
+ + +Manyullyn +manyullynlongsword +The nether alloy. It's stronger than diamond. +- Requires the Smeltery + +
manyullyningot
+
+
+ + +Copper +copperaxe +A common metal found in the ground. +- Requires the Smeltery + +
copperingot
+
+
+ + +Bronze +bronzeaxe +A common alloy. It's a bit better than iron. +- Requires the Smeltery + +
bronzeingot
+
+
+ + +Steel +steelsword +An Iron alloy. The method for obtaining this is unknown. +- Requires the Smeltery + +
steelingot
+
+
+ + +Pig Iron +pigironsword +PigIron +An Iron alloy. It seems to be made of blood, iron, and emeralds. +- Requires the Smeltery + +
pigironingot
+
+
+ + + + + +Diamond +Adding a diamond to the edge of a tool seems to make it more resilient. + +Effects: +- 500 extra durability +- Mining level increased to level 3 + +Type: Single-use +Stackable: No + + + +tool +diamondmod + + + +Emerald +Affixing an emerald to a tool's weakest point appears to make it more resilient. + +Effects: +- 50% more durability +- Mining level increased to level 2 + +Type: Single-use +Stackable: No + + + +tool +emeraldmod + + + +Speed +Adding redstone to a tool seems to increase its mining speed. + +Effects: +- Each redstone dust increases mining speed by 0.08. +- At 50/50 the boost is 4.0, making a wood pickaxe equivalent in speed to iron. +- Not useful on weapons + +Type: Multi-use +Stackable: Yes + + + +tool +redstonemod + + + +Auto-Repair +Attaching moss to a tool infuses it with life. The tool appears to be capable of regenerating wear and tear. + +Effects: +- The tool slowly repairs itself +- Sunlight speeds up the process + +Type: Single-use +Stackable: Yes + + + +tool +mossmod + + + +Auto-Smelt +Melting a lava crystal onto the end of a tool gives it the power of a furnace. + +Effects: +- Smelts blocks as they're harvested +- Sets mobs on fire for 3 seconds +- Stacks with Luck +- Not compatible with Silky + +Type: Multi-use +Stackable: No + + + +tool +lavacrystalmod + + + +Ball of Moss + + mossball + three + + + + +Lava Crystal + + lavacrystal + three + + + + +Luck +Encrusting lapis on tools is a gift to the gods of luck. They will bless your tool with great fortune and loot. + +Effects: +- Adds fortune or looting, depends on tool type +- Increases level at a maximum threshold, but has a chance of increasing before +- Sometimes adds extra luck to the tool +- Not compatible with Silky + +Type: Single-use +Stackable: No + + + +tool +lapismod + + + +Sharpness +Adding quartz to the edge of a tool seems to make it sharper. + +Effects: +- Increases attack damage +- Has less of an effect on tools with piercing properties + +Type: Multi-use +Stackable: Yes + + + +weapon +quartzmod + + + +Fiery +Adding blaze powder to a tool gives it a fiery tinge. + +Effects: +- Sets enemies on fire +- Every 5 powder adds another second to the flame. + +Type: Multi-use +Stackable: Yes + + + +weapon +blazemod + + + +Necrotic +Placing the bone of a Wither Skeleton on a tool gives it nefarious life-stealing powers. + +Effects: +- Heals the player every time a monster is attacked +- Heals one heart per bone + +Type: Single-use +Stackable: Yes + + + +weapon +necroticmod + + + +Silky +Adding a large glob of aluminum brass or gold and a bunch of string seems to give the tool silky-smooth properties. + +Effects: +- Allows blocks to be harvested directly +- Scythes modified with Silky act as shears on blocks +- Not compatible with Luck or Auto-Smelt + +Type: Single-use +Stackable: No + + + +tool +silkymod + + + +Silky Cloth + + silkycloth + three + + + + +Silky Jewel + + silkyjewel + three + + + + +Reinforced +Adding a reinforced plate to the tool seems to help with its durability. + +Effects: +- Adds the material trait Reinforced to the tool +- Stacks with previous levels of Reinforced + +Type: Single-use +Stackable: Yes + + + +tool +reinforcedmod + + + +Knockback +Attaching a piston to the tool and activating it at the right time seems to throw mobs further away. + +Effects: +- Adds extra knockback to the tool. + +Type: Multi-use +Stackable: Yes + + + +weapon +pistonmod + + + +Beheading +Working an ender pearl and some obsidian on a weapon has the curious effect of separating the target's head from its body. + +Effects: +- Beheads mobs. Enemies drop their heads as a result. + +Type: Single-use +Stackable: Yes + + + +weapon +beheadingmod + + + +Bane of Arthropods +Striking a spider with its own eyeball causes it to recoil in fear. + +Effects: +- Does extra damage to spiders. +- 1-2 hearts per level. + +Type: Multi-use +Stackable: Yes + + + +weapon +spidermod + + + +Smite +The raw power of consecrated soil empowers your weapon, smiting enemies from on high. + +Effects: +- Does extra damage to undead. +- 1-2 hearts per level. + +Type: Multi-use +Stackable: Yes + + + +weapon +smitemod + + + +Graveyard Soil + + graveyardsoil + two + + + + +Consecrated Soil +consecratedsoil + + + +Flux +Adding a Hardened Flux Capacitor or Leadstone Energy Cell gives a tool energy. The tool still functions properly when the Cell/Capacitor has no charge. + +Effects: +- Allows the tool to be charged with Redstone Flux +- Uses energy instead of durability while charged +- Available from Thermal Expansion +- Tools must have at least 1/1000th of the batteries capacity in durability + +Type: Single-use +Stackable: No + + + +tool +fluxmod + + + +Flux +Adding a Hardened Flux Capacitor or Leadstone Energy Cell gives a tool energy. The tool still functions properly when the Cell/Capacitor has no charge. + +Effects: +- Allows the tool to be charged with Redstone Flux +- Uses energy instead of durability while charged +- Available from Thermal Expansion + +Type: Single-use +Stackable: No + + + +tool +fluxmod2 + + + +Additional Modifiers +Working the tool with a block of gold and a diamond makes the tool more malleable, allowing for more modification. + +Effects: +- Adds an additional modifier slot to the tool + +Type: Single-use +Stackable: No + + + +tool +tier1free + + + +Additional Modifiers +Attaching a solid gold apple and encrusting the tool with a block of diamond warps the tool in places you did not notice before. + +Effects: +- Adds an additional modifier slot to the tool + +Type: Single-use +Stackable: No + + + +tool +tier1.5free + + + +Additional Modifiers +Working the tool with a nether star gives it the ability to attach parts they would not otherwise stay. + +Effects: +- Adds an additional modifier slot to the tool + +Type: Single-use +Stackable: No + + + +tool +tier2free + + + +Additional Modifiers +For the creative types or the map makers in the world, this item is for you. + +Effects: +- Adds an additional modifier slot to the tool +- If it has a target lock, it will only work on that tool. + +Type: Single-use +Stackable: Yes + + + +tool +creativefree + + +
diff --git a/src/main/resources/assets/dreamcraft/tinker/manuals/smeltery.xml b/src/main/resources/assets/dreamcraft/tinker/manuals/smeltery.xml new file mode 100644 index 000000000..756a09f40 --- /dev/null +++ b/src/main/resources/assets/dreamcraft/tinker/manuals/smeltery.xml @@ -0,0 +1,296 @@ + + + + +Mighty Smelting +Make the most of your metals + +By Thruul M'gon + + + +Table of Contents + + Introduction + smelterybook + 3 + + + Casting + castingtable + 8 + + + Construction + smeltery + 5 + + + Alloys + liquidiron + 9 + + + Recipes + blankcast + 9 + + + + +Welcome to the world of metalcraft. Within these pages you will learn the secrets of crafting rare and precious metals into usable materials. + +You will learn how to cast everything from the softest Iron to the hardest Manyullyn into whatever shape you desire and create alloy mixes from obscure materials. As always, experimentation is key! + +The main structure for melting metals is called the Smeltery. It is larger than structures you may be familiar with, but the method for construction is relatively simple. It operates much like a furnace with a drain. + + + +When working with the Smeltery, there are a few things to keep in mind: + +* The Smeltery functions as a large liquid tank. It is capable of melting metals and mixing various things. + +* Drains function as input/output for the tank. If you need to get liquids back into the structure, simply pour them into a drain. + +* Ores naturally have more material than processed versions, such as ingots. The Smeltery will preserve the full value of the ores - roughly twice as much as a regular furnace would provide. + + + +* Mixing two or more metals in the Smeltery may result in an alloy. The process happens automatically. + +* If you need more space to process metal, add more layers. + +* Casts are made differently from Patterns. Details are found later. + + + +The Smeltery is a multi-block structure. You will need the following materials to get started. + + 1 Smeltery Controller + smeltery + + + 1 Seared Tank + lavatank + + + 9 Seared Bricks + searedbrick + + + Any combination of 10 Seared Bricks, Seared Tanks, or Drains + + drain + + + 1 Faucet + faucet + + + 1 Casting Table + castingtable + + + + +Begin construction by laying a 3x3 bed of seared bricks. Leave any space above these bricks open; you will be constructing a shell around a 3x3 hollow inside. + +Place the Controller one layer up and the lava tank anywhere on the same level. Fill in the rest of the space with seared bricks, lava tanks, or drains as you like. + +The small end of drains should be facing the outside. If you're successful, the Controller will light up and start working. Fill the tank with lava, the Smeltery with metal, and watch it go. + +The structure must be a 5x5 shell due to the amount of heat lava gives off. There are no vertical restrictions to the size of the Smeltery. + + + +Larger Smelteries will hold more metal, of course, and the Controller can be on any level so long as there is a lava tank to match. + +When you are ready to pour liquid metal from the Smeltery, put a faucet on a drain and a casting table below it. + +Other liquid removal objects will work, but are frowned upon. An example picture is provided for your convenience. + + + +Figure 1a +tinker:manuals/smelterysmall.png + + + +Alloys are made when two or more metals are mixed in precise proportions. Currently known alloys are: + + Bronze, made from Copper(3) and Tin(1) + bronzeingot + + + Aluminum Brass, made from Aluminum(3) and Copper(1) + alubrassingot + + + Manyullyn, made from Cobalt(2) and Ardite(2) + manyullyningot + + + Alumite, made from Aluminum(5), Iron(2), and Obsidian(2) + alumiteingot + + + Pig Iron, made from Iron(1), Blood(1), and Emeralds(1) + pigironingot + + + + +There exist a few liquids that are gained through strange means. Currently known liquids are: + + Blood, damaging entities in the Smeltery + bloodbucket + + + Liquified Emeralds, melting down emeralds or villagers + emeraldbucket + + + Glue, melting down horses in the Smeltery + gluebucket + + + Slime, found on slime islands. It functions as a spawner + slimebucket + + + Ender, either melting down ender pearls or endermen + enderbucket + + + + +Blank casts are made by pouring aluminum brass or gold into an empty casting table. These are used in crafting recipes and can be re-melted. + +Part casts must be created by pouring metal around existing parts, like a pickaxe head. + + + +Forming a Cast +tinker:manuals/casting.png + + + +Grout + + grout + two + + + + +Seared Brick +searedbrick + + + +Seared Bricks + + searedbricks + two + + + + +Smeltery Controller + + smelterycontroller + three + + + + +Smeltery Drain + + smelterydrain + three + + + + +Seared Tank + + smelterytank1 + three + + + + +Seared Glass + + smelterytank2 + three + + + + +Seared Window + + smelterytank3 + three + + + + +Seared Faucet + + smelteryfaucet + three + + + + +Casting Channel + + castingchannel + three + + + + +Casting Table + + smelterytable + three + + + + +Casting Basin + + smelterybasin + three + + + + +Brownstone +brownstone + + + +Clear Glass +clearglass + + + +Seared Stone +searedstone + + + +Endstone +endstone + + + +Glueball +glueball + + + + \ No newline at end of file diff --git a/src/main/resources/assets/dreamcraft/tinker/manuals/weaponry.xml b/src/main/resources/assets/dreamcraft/tinker/manuals/weaponry.xml new file mode 100644 index 000000000..820ed18ea --- /dev/null +++ b/src/main/resources/assets/dreamcraft/tinker/manuals/weaponry.xml @@ -0,0 +1,144 @@ + + + + +Tinkers' Weaponry +Brings the hurt whole new places + +By Sheriff Bownana + + + +Table of Contents + + Introduction + weaponrybook + 3 + + + Throwing Weapons + throwingknifeIcon + 5 + + + Projectile Weapons + shortbowIcon + 7 + + + Projectiles + arrowIcon + 10 + + + + +Why walk up to hit stuff, when you can do that while standing here! + +A tinkerer knows how to create varied ranged weapons. There are two big categories: Thrown weapons and weapons that shoot projectiles. + +Both types utilize an ammo system. Instead of having several items, these ranged weapons consist of one item that contains many of it. Why carry around 2 stacks of arrows, when you can have 200 in one stack! +These Ammo-Items don't have a durability, but use up their ammo for each projectile thrown/shot. + + + +Throwing Weapons: +* Throwing Knife +* Shuriken +* Javelin + +Projectile Weapons: +* Shortbow +* Longbow +* Crossbow + +Projectiles: +* Arrows +* Bolts + + + +Throwing Weapons + +Throwing Knifes: +These knifes are crafted from a knife blade and a handle. +Hold right-click to take aim, and release it to throw a knife. +Throwing knifes stack up to a moderate amount, and deal moderate damage. + +Shuriken: +Small delicate projectiles put together from 4 parts, that are thrown from the wrist. Because of this you don't need to take aim and can throw them directly. +Since they're so small you can carry many of them, however they don't deal much damage. + + + + + +Javelin: +This is a hybrid between a melee and a ranged weapon, consisting of an arrow head on two tough tool rods. It deals moderate damage as a melee weapon, but to unleash its true potential you have to throw it at your enemy. +The throwing has a small windup. Because of their size you can only carry a few, but they pack a punch. + + + +Projectile Weapons + +Shortbow: +Put together from two bow limbs and a bowstring, this weapon is a fast and handy bow. Keep in mind that inflexible materials like stone can hardly be drawn back and can't accelerate projectiles well. Organic materials are favoured for quick bows, while metals require lots of strength to draw back, but provide more power. +The bow requires arrows as ammo. +Offensive modifiers on bows do not count for arrows. The only modifiers affecting its ranged performance are Redstone and lapis. + + + + + +Longbow: +The big brother. This bow is much bigger, requiring more time to pull it back, but boy oh boy are those arrows fast. +Because of this the longbow performs slightly better against armored targets. Furthermore this makes the longbow less accurate with very light arrows. +The longbow is constructed from two bow limbs, a bowstring and a large plate. + + + + + +Crossbow: +Those pesky metals are such a pain to pull back. Luckily a tinkerer knows how to help himself! The crossbow allows you to pre-load it with a bolt, and have it ready instantly to fire. Since this allows for much more power, it can shoot heavier projectiles, resulting in more damage and armor penetration. +To load a crossbow simply right-click and wait until the animation is done. The loading process can be stopped by pressing right-click while sneaking. The crossbow can be unloaded in the same way. + + + +Projectiles +For these projectiles goes: The heavier the projectile, the more damage carries through armor. + +Arrows: +The bread and butter of every ranger. Arrows can be built from many different materials. It needs an arrow head, a shaft and a fletching. +The choice of materials allows a balance between damage, arrow-count, accuracy and fragility. The head material is crucial for its weight. +A fragile arrow has a chance to break on impact with terrain. Arrows always break when they hit a target, however the reinforced trait gives a chance for the arrow to survive the impact. + + + + + +Valid Shaft Materials: +* Stick +* Sugarcane +* Bone +* Blaze Rod + +Valid Fletching Materials: +* Leaves +* Slimeleaves +* Feathers +* Slime + + + + + +Bolts: +Crafting bolts is a delicate process. First you need a core in the form of a tool rod. +Take this tool rod to a smeltery and put it into a Casting Table. Pour some metal onto it to coat the tip with a more damaging material. +After this process, add a fletching and your bolts are ready to be used. + +Since the bolts consist of a harder core and tip they carry more weight than regular arrows, making them perfect to fight armored targets. + + + \ No newline at end of file diff --git a/src/test/java/com/dreammaster/mantle/BookDataReaderTest.java b/src/test/java/com/dreammaster/mantle/BookDataReaderTest.java new file mode 100644 index 000000000..842efd57c --- /dev/null +++ b/src/test/java/com/dreammaster/mantle/BookDataReaderTest.java @@ -0,0 +1,51 @@ +package com.dreammaster.mantle; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +class BookDataReaderTest { + + private static final String TEST_BOOKS_LOCATION = "/assets/dreamcraft/mantle/"; + + @Test + void instanciate() { + assertThatCode(BookDataReader::new).doesNotThrowAnyException(); + } + + @Test + void readValidBook() { + BookDataReader bookDataReader = new BookDataReader(); + String bookName = TEST_BOOKS_LOCATION + "test-book.xml"; + + Document document = bookDataReader.readBook(bookName); + + assertThat(document).extracting(Node::getFirstChild).extracting(Node::getNodeName).isEqualTo("book"); + + } + + @Test + void readMissingBook() { + BookDataReader bookDataReader = new BookDataReader(); + String bookName = TEST_BOOKS_LOCATION + "missing-book.xml"; + + assertThatThrownBy(() -> bookDataReader.readBook(bookName)).isInstanceOf(IllegalStateException.class) + .hasCauseInstanceOf(IOException.class); + } + + @Test + void readInvalidBook() { + BookDataReader bookDataReader = new BookDataReader(); + String bookName = TEST_BOOKS_LOCATION + "blank-book.xml"; + + assertThatThrownBy(() -> bookDataReader.readBook(bookName)).isInstanceOf(IllegalStateException.class) + .hasCauseInstanceOf(SAXException.class); + } +} diff --git a/src/test/java/com/dreammaster/mantle/BookDataStoreProxyTest.java b/src/test/java/com/dreammaster/mantle/BookDataStoreProxyTest.java new file mode 100644 index 000000000..53ff29b8f --- /dev/null +++ b/src/test/java/com/dreammaster/mantle/BookDataStoreProxyTest.java @@ -0,0 +1,75 @@ +package com.dreammaster.mantle; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; + +import eu.usrv.yamcore.auxiliary.LogHelper; +import mantle.books.BookData; +import mantle.books.BookDataStore; + +@ExtendWith(MockitoExtension.class) +class BookDataStoreProxyTest { + + @Mock + LogHelper LOGGER; + + @Mock + BookData BOOKDATA; + + @Test + void instanciateWithNull() { + assertThatThrownBy(() -> new BookDataStoreProxy(null)).isInstanceOf(NullPointerException.class); + } + + @Test + void instanciate() { + assertThatCode(() -> new BookDataStoreProxy(LOGGER)).doesNotThrowAnyException(); + } + + @Test + void addNullBook() { + BookDataStoreProxy bookDataStoreProxy = new BookDataStoreProxy(LOGGER); + + assertThatThrownBy(() -> bookDataStoreProxy.addBook(null)).isInstanceOf(NullPointerException.class); + verify(LOGGER, never()).error(anyString()); + } + + @Test + void addValidBook() { + try (MockedStatic bookDataStore = mockStatic(BookDataStore.class)) { + BookDataStoreProxy bookDataStoreProxy = new BookDataStoreProxy(LOGGER); + + bookDataStoreProxy.addBook(BOOKDATA); + + bookDataStore.verify(() -> BookDataStore.addBook(BOOKDATA)); + verify(LOGGER, never()).error(anyString()); + } + } + + /** + * BookDataStore will throw an IllegalArgumentException if it already contains a book. We catch and log it to + * increase interoperability. + */ + @Test + void addDuplicateBook() { + try (MockedStatic bookDataStore = mockStatic(BookDataStore.class)) { + bookDataStore.when(() -> BookDataStore.addBook(BOOKDATA)).thenThrow(new IllegalArgumentException()); + BookDataStoreProxy bookDataStoreProxy = new BookDataStoreProxy(LOGGER); + + bookDataStoreProxy.addBook(BOOKDATA); + + bookDataStore.verify(() -> BookDataStore.addBook(BOOKDATA)); + verify(LOGGER).error(anyString()); + } + } +} diff --git a/src/test/java/com/dreammaster/mantle/MantleBookLoaderTest.java b/src/test/java/com/dreammaster/mantle/MantleBookLoaderTest.java new file mode 100644 index 000000000..e51e98ed1 --- /dev/null +++ b/src/test/java/com/dreammaster/mantle/MantleBookLoaderTest.java @@ -0,0 +1,170 @@ +package com.dreammaster.mantle; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.function.Consumer; +import java.util.stream.Stream; + +import net.minecraft.util.ResourceLocation; + +import org.assertj.core.api.ObjectAssert; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.w3c.dom.Document; + +import gregtech.api.interfaces.internal.IGT_Mod; +import mantle.books.BookData; + +@ExtendWith(MockitoExtension.class) +class MantleBookLoaderTest { + + private static final String PATH = "/somewhere/over/the/rainbow"; + private static final String MOD_ID = "modid"; + private static final String UNLOCALIZED_NAME = "testBook"; + private static final String TOOLTIP = "tooltip"; + private static final String ITEM_IMAGE = "itemImage.png"; + @Mock + BookDataStoreProxy BOOK_DATA_STORE_PROXY; + + @Mock + BookDataReader BOOK_DATA_READER; + + @Mock + IGT_Mod SIDE_CHECKER; + + @Mock + Document DOCUMENT; + + @Captor + ArgumentCaptor bookDataArgumentCaptor; + + private MantleBookLoader fixture; + + @BeforeEach + void BeforeEach() { + fixture = new MantleBookLoader(BOOK_DATA_STORE_PROXY, BOOK_DATA_READER, SIDE_CHECKER); + } + + private void buildBookAndAddToBookDataStore(Stream> bookBuilderOperation) { + fixture.setRequiredData(UNLOCALIZED_NAME, MOD_ID, PATH); + bookBuilderOperation.forEach(operation -> operation.accept(fixture)); + fixture.addToBookDataStore(); + } + + @Test + void instanciateWithNullBookDataStoreProxy() { + ThrowingCallable code = () -> new MantleBookLoader(null, BOOK_DATA_READER, SIDE_CHECKER); + + assertThatThrownBy(code).isInstanceOf(NullPointerException.class); + } + + @Test + void instanciateWithNullBookDataReader() { + ThrowingCallable code = () -> new MantleBookLoader(BOOK_DATA_STORE_PROXY, null, SIDE_CHECKER); + + assertThatThrownBy(code).isInstanceOf(NullPointerException.class); + } + + @Test + void instanciateWithNullSideChecker() { + ThrowingCallable code = () -> new MantleBookLoader(BOOK_DATA_STORE_PROXY, BOOK_DATA_READER, null); + + assertThatThrownBy(code).isInstanceOf(NullPointerException.class); + } + + @Test + void instanciateProperly() { + ThrowingCallable code = () -> new MantleBookLoader(BOOK_DATA_STORE_PROXY, BOOK_DATA_READER, SIDE_CHECKER); + + assertThatCode(code).doesNotThrowAnyException(); + } + + /** + * This test case shouldn't be possible under the current implementation. + */ + @Test + void addToBookDataStorePrematurely() { + ThrowingCallable code = fixture::addToBookDataStore; + + assertThatThrownBy(code).isInstanceOf(IllegalStateException.class); + verify(BOOK_DATA_STORE_PROXY, never()).addBook(any()); + } + + @Test + void addToBookDataStoreWithDefaultsServerside() { + when(SIDE_CHECKER.isClientSide()).thenReturn(false); + + buildBookAndAddToBookDataStore(Stream.empty()); + + verify(BOOK_DATA_READER, never()).readBook(PATH); + assertBaseAddedBookData(); + } + + private ObjectAssert assertBaseAddedBookData() { + verify(BOOK_DATA_STORE_PROXY).addBook(bookDataArgumentCaptor.capture()); + return assertThat(bookDataArgumentCaptor.getValue()) + .hasFieldOrPropertyWithValue("fullUnlocalizedName", MOD_ID + ":" + UNLOCALIZED_NAME); + } + + @Test + void addToBookDataStoreWithDefaultsClientside() { + when(SIDE_CHECKER.isClientSide()).thenReturn(true); + when(BOOK_DATA_READER.readBook(PATH)).thenReturn(DOCUMENT); + + buildBookAndAddToBookDataStore(Stream.empty()); + + assertBaseAddedBookData().hasFieldOrPropertyWithValue("doc", DOCUMENT); + } + + @Test + void addToBookDataStoreWithToolTip() { + buildBookAndAddToBookDataStore(Stream.of((bookLoader) -> bookLoader.setTooltip(TOOLTIP))); + + assertBaseAddedBookData().hasFieldOrPropertyWithValue("toolTip", "§o" + TOOLTIP); + } + + @Test + void addToBookDataStoreWithNullToolTip() { + ThrowingCallable code = () -> buildBookAndAddToBookDataStore( + Stream.of((bookLoader) -> bookLoader.setTooltip(null))); + + assertThatThrownBy(code).isInstanceOf(NullPointerException.class); + } + + // Not mocking ResourceLocation, API assumed stable + @Test + void addToBookDataStoreWithItemImage() { + buildBookAndAddToBookDataStore(Stream.of((bookLoader) -> bookLoader.setItemImage(ITEM_IMAGE))); + + assertBaseAddedBookData().extracting((bookData -> bookData.itemImage)) + .isEqualTo(new ResourceLocation(MOD_ID, ITEM_IMAGE)); + } + + @Test + void addToBookDataStoreWithNullItemImage() { + ThrowingCallable code = () -> buildBookAndAddToBookDataStore( + Stream.of((bookLoader) -> bookLoader.setItemImage(null))); + + assertThatThrownBy(code).isInstanceOf(NullPointerException.class); + } + + @Test + void addToBookDataStoreAsTranslatable() { + buildBookAndAddToBookDataStore(Stream.of(BookLoader::makeTranslatable)); + + assertBaseAddedBookData().hasFieldOrPropertyWithValue("isTranslatable", true); + } + +} diff --git a/src/test/resources/assets/dreamcraft/mantle/blank-book.xml b/src/test/resources/assets/dreamcraft/mantle/blank-book.xml new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/assets/dreamcraft/mantle/test-book.xml b/src/test/resources/assets/dreamcraft/mantle/test-book.xml new file mode 100644 index 000000000..717e14067 --- /dev/null +++ b/src/test/resources/assets/dreamcraft/mantle/test-book.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file