Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba2faace15 |
No files matched your search
@@ -76,7 +76,7 @@ and take the copied Page from the output. A Notebook can hold and transfer a
|
|||||||
larger ordered collection of Pages between desks and the binder.
|
larger ordered collection of Pages between desks and the binder.
|
||||||
|
|
||||||
With JEI installed, Rivencraft displays the Ink Mixer operation in its own
|
With JEI installed, Rivencraft displays the Ink Mixer operation in its own
|
||||||
category and the hand-use, copying, and binding flows in a Writing category.
|
category and the hand-use, copying, binding, and Linking Book crafting flows in a Writing category.
|
||||||
JEI is optional; vanilla crafting
|
JEI is optional; vanilla crafting
|
||||||
recipes and all writing interactions work without it. For a development client
|
recipes and all writing interactions work without it. For a development client
|
||||||
with JEI, run `./gradlew runClient -PwithJei=true`.
|
with JEI, run `./gradlew runClient -PwithJei=true`.
|
||||||
@@ -104,8 +104,8 @@ font.
|
|||||||
|
|
||||||
## Linking Book
|
## Linking Book
|
||||||
|
|
||||||
Craft a Linking Book from a vanilla book and an ender pearl, then use it in the
|
Craft an unwritten Linking Book from one Link Panel Page and one leather in any
|
||||||
air. An unbound book opens a screen where it can be named and bound to the
|
two crafting-grid positions. An unbound book opens a screen where it can be named and bound to the
|
||||||
current location. A bound book opens its destination screen and travels when
|
current location. A bound book opens its destination screen and travels when
|
||||||
its preview panel is clicked. The destination is stored on the item and
|
its preview panel is clicked. The destination is stored on the item and
|
||||||
survives inventory and world saves. Travel only succeeds if the target
|
survives inventory and world saves. Travel only succeeds if the target
|
||||||
@@ -113,6 +113,7 @@ dimension is loaded and a non-fluid, passable landing position can be found
|
|||||||
nearby.
|
nearby.
|
||||||
The Linking Book uses the same Legacy page layout and bottom-corner page turns
|
The Linking Book uses the same Legacy page layout and bottom-corner page turns
|
||||||
as the Descriptive Book, but without the Descriptive Book's gold cover edges.
|
as the Descriptive Book, but without the Descriptive Book's gold cover edges.
|
||||||
|
Both screens play Minecraft's book-page-turn sound when a page is turned.
|
||||||
|
|
||||||
Successful book travel plays Mystcraft Legacy's original linking sound at both
|
Successful book travel plays Mystcraft Legacy's original linking sound at both
|
||||||
the departure and arrival locations.
|
the departure and arrival locations.
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ mod_name=Rivencraft
|
|||||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
mod_license=LGPL-3.0-or-later
|
mod_license=LGPL-3.0-or-later
|
||||||
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
||||||
mod_version=0.3.9.1
|
mod_version=0.3.9.2
|
||||||
immersive_portals_version=6.0.7
|
immersive_portals_version=6.0.7
|
||||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||||
# This should match the base package used for the mod sources.
|
# This should match the base package used for the mod sources.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import net.dnorberg.rivencraft.registry.ModCreativeTabs;
|
|||||||
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||||
import net.dnorberg.rivencraft.registry.ModItems;
|
import net.dnorberg.rivencraft.registry.ModItems;
|
||||||
import net.dnorberg.rivencraft.registry.ModMenus;
|
import net.dnorberg.rivencraft.registry.ModMenus;
|
||||||
|
import net.dnorberg.rivencraft.registry.ModRecipeSerializers;
|
||||||
import net.dnorberg.rivencraft.registry.ModSounds;
|
import net.dnorberg.rivencraft.registry.ModSounds;
|
||||||
import net.dnorberg.rivencraft.registry.ModBlocks;
|
import net.dnorberg.rivencraft.registry.ModBlocks;
|
||||||
import net.dnorberg.rivencraft.registry.ModBlockEntities;
|
import net.dnorberg.rivencraft.registry.ModBlockEntities;
|
||||||
@@ -43,6 +44,7 @@ public final class Rivencraft {
|
|||||||
ModBlockEntities.register(modEventBus);
|
ModBlockEntities.register(modEventBus);
|
||||||
ModItems.register(modEventBus);
|
ModItems.register(modEventBus);
|
||||||
ModMenus.register(modEventBus);
|
ModMenus.register(modEventBus);
|
||||||
|
ModRecipeSerializers.register(modEventBus);
|
||||||
ModSounds.register(modEventBus);
|
ModSounds.register(modEventBus);
|
||||||
ModCreativeTabs.register(modEventBus);
|
ModCreativeTabs.register(modEventBus);
|
||||||
NeoForge.EVENT_BUS.addListener(BookStandConversionHandler::onRightClickBlock);
|
NeoForge.EVENT_BUS.addListener(BookStandConversionHandler::onRightClickBlock);
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ import net.dnorberg.rivencraft.client.preview.DestinationPreviewRenderers;
|
|||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
|
||||||
import net.minecraft.client.gui.components.Button;
|
import net.minecraft.client.gui.components.Button;
|
||||||
import net.minecraft.client.gui.components.EditBox;
|
import net.minecraft.client.gui.components.EditBox;
|
||||||
import net.minecraft.client.gui.components.Tooltip;
|
import net.minecraft.client.gui.components.Tooltip;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.sounds.SoundEvents;
|
||||||
import net.minecraft.util.FormattedCharSequence;
|
import net.minecraft.util.FormattedCharSequence;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
@@ -250,7 +252,10 @@ public final class DescriptiveBookScreen extends Screen implements ExplorableBoo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void turnPage(int direction) {
|
private void turnPage(int direction) {
|
||||||
pageIndex = Math.max(0, Math.min(pageCount(), pageIndex + direction));
|
int nextPage = Math.max(0, Math.min(pageCount(), pageIndex + direction));
|
||||||
|
if (nextPage == pageIndex) return;
|
||||||
|
pageIndex = nextPage;
|
||||||
|
minecraft.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.BOOK_PAGE_TURN, 1.0F, 0.65F));
|
||||||
if (titleInput != null) titleInput.visible = pageIndex == 0;
|
if (titleInput != null) titleInput.visible = pageIndex == 0;
|
||||||
if (createButton != null) createButton.visible = pageIndex == 0;
|
if (createButton != null) createButton.visible = pageIndex == 0;
|
||||||
if (cancelButton != null) cancelButton.visible = pageIndex == 0;
|
if (cancelButton != null) cancelButton.visible = pageIndex == 0;
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ import net.minecraft.client.gui.components.Button;
|
|||||||
import net.minecraft.client.gui.components.EditBox;
|
import net.minecraft.client.gui.components.EditBox;
|
||||||
import net.minecraft.client.gui.components.Tooltip;
|
import net.minecraft.client.gui.components.Tooltip;
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.chat.contents.TranslatableContents;
|
import net.minecraft.network.chat.contents.TranslatableContents;
|
||||||
|
import net.minecraft.sounds.SoundEvents;
|
||||||
import net.minecraft.util.FormattedCharSequence;
|
import net.minecraft.util.FormattedCharSequence;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
@@ -215,7 +217,9 @@ public final class LinkingBookScreen extends Screen implements ExplorableBookScr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void turnPage(int page) {
|
private void turnPage(int page) {
|
||||||
|
if (pageIndex == page) return;
|
||||||
pageIndex = page;
|
pageIndex = page;
|
||||||
|
minecraft.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.BOOK_PAGE_TURN, 1.0F, 0.65F));
|
||||||
if (titleInput != null) titleInput.visible = pageIndex == 0;
|
if (titleInput != null) titleInput.visible = pageIndex == 0;
|
||||||
if (bindButton != null) bindButton.visible = pageIndex == 0;
|
if (bindButton != null) bindButton.visible = pageIndex == 0;
|
||||||
if (cancelButton != null) cancelButton.visible = pageIndex == 0;
|
if (cancelButton != null) cancelButton.visible = pageIndex == 0;
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ public final class RivencraftJeiPlugin implements IModPlugin {
|
|||||||
new WritingGuide(List.of(panel, new ItemStack(Items.PAPER), new ItemStack(ModItems.INK_VIAL.get())),
|
new WritingGuide(List.of(panel, new ItemStack(Items.PAPER), new ItemStack(ModItems.INK_VIAL.get())),
|
||||||
panel, "jei.rivencraft.writing.copy"),
|
panel, "jei.rivencraft.writing.copy"),
|
||||||
new WritingGuide(List.of(new ItemStack(Items.LEATHER), panel),
|
new WritingGuide(List.of(new ItemStack(Items.LEATHER), panel),
|
||||||
new ItemStack(ModItems.DESCRIPTIVE_BOOK.get()), "jei.rivencraft.writing.bind")
|
new ItemStack(ModItems.DESCRIPTIVE_BOOK.get()), "jei.rivencraft.writing.bind"),
|
||||||
|
new WritingGuide(List.of(panel, new ItemStack(Items.LEATHER)),
|
||||||
|
new ItemStack(ModItems.LINKING_BOOK.get()), "jei.rivencraft.writing.linking_book")
|
||||||
));
|
));
|
||||||
registration.addRecipes(INK_MIXER, List.of(new InkMixerGuide(panel)));
|
registration.addRecipes(INK_MIXER, List.of(new InkMixerGuide(panel)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package net.dnorberg.rivencraft.gametest;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import net.dnorberg.rivencraft.Rivencraft;
|
||||||
|
import net.dnorberg.rivencraft.item.PageItem;
|
||||||
|
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||||
|
import net.dnorberg.rivencraft.registry.ModItems;
|
||||||
|
import net.dnorberg.rivencraft.writing.LinkingBookRecipe;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.gametest.framework.GameTest;
|
||||||
|
import net.minecraft.gametest.framework.GameTestHelper;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||||
|
import net.minecraft.world.item.crafting.CraftingInput;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeType;
|
||||||
|
import net.neoforged.neoforge.gametest.GameTestHolder;
|
||||||
|
import net.neoforged.neoforge.gametest.PrefixGameTestTemplate;
|
||||||
|
|
||||||
|
@GameTestHolder(Rivencraft.MOD_ID)
|
||||||
|
@PrefixGameTestTemplate(false)
|
||||||
|
public final class LinkingBookRecipeGameTests {
|
||||||
|
private LinkingBookRecipeGameTests() {}
|
||||||
|
|
||||||
|
@GameTest(template = "empty")
|
||||||
|
public static void legacyRecipeRequiresExactlyLeatherAndLinkPanel(GameTestHelper helper) {
|
||||||
|
LinkingBookRecipe recipe = new LinkingBookRecipe(CraftingBookCategory.MISC);
|
||||||
|
ItemStack panel = PageItem.createLinkPanelPage();
|
||||||
|
CraftingInput valid = CraftingInput.of(2, 1, List.of(panel, new ItemStack(Items.LEATHER)));
|
||||||
|
helper.assertTrue(recipe.matches(valid, helper.getLevel()), "Link Panel and leather must match");
|
||||||
|
helper.assertTrue(recipe.matches(CraftingInput.of(2, 1,
|
||||||
|
List.of(new ItemStack(Items.LEATHER), panel)), helper.getLevel()),
|
||||||
|
"The Legacy recipe must be shapeless");
|
||||||
|
helper.assertFalse(recipe.matches(CraftingInput.of(2, 1,
|
||||||
|
List.of(new ItemStack(Items.BOOK), new ItemStack(Items.ENDER_PEARL))), helper.getLevel()),
|
||||||
|
"The old placeholder recipe must no longer match");
|
||||||
|
helper.assertFalse(recipe.matches(CraftingInput.of(2, 1,
|
||||||
|
List.of(new ItemStack(ModItems.PAGE.get()), new ItemStack(Items.LEATHER))), helper.getLevel()),
|
||||||
|
"A blank Page must not substitute for a Link Panel");
|
||||||
|
helper.assertFalse(recipe.matches(CraftingInput.of(3, 1,
|
||||||
|
List.of(panel, new ItemStack(Items.LEATHER), new ItemStack(Items.PAPER))), helper.getLevel()),
|
||||||
|
"An extra ingredient must invalidate the recipe");
|
||||||
|
helper.succeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "empty")
|
||||||
|
public static void outputIsUnwrittenAndRegisteredRecipeLoads(GameTestHelper helper) {
|
||||||
|
ItemStack panel = PageItem.createLinkPanelPage();
|
||||||
|
panel.set(DataComponents.CUSTOM_NAME, Component.literal("Home Panel"));
|
||||||
|
CraftingInput input = CraftingInput.of(2, 1, List.of(panel, new ItemStack(Items.LEATHER)));
|
||||||
|
var holder = helper.getLevel().getRecipeManager()
|
||||||
|
.getRecipeFor(RecipeType.CRAFTING, input, helper.getLevel()).orElseThrow();
|
||||||
|
helper.assertValueEqual(holder.id(), ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, "linking_book"),
|
||||||
|
"The registered recipe must replace the old placeholder");
|
||||||
|
ItemStack result = holder.value().assemble(input, helper.getLevel().registryAccess());
|
||||||
|
helper.assertTrue(result.is(ModItems.LINKING_BOOK.get()), "Crafting must produce a Linking Book");
|
||||||
|
helper.assertValueEqual(result.getCount(), 1, "Crafting must produce one book");
|
||||||
|
helper.assertFalse(result.has(ModDataComponents.LINK_TARGET.get()), "The book must be unwritten/unbound");
|
||||||
|
helper.assertFalse(result.has(ModDataComponents.PAGE_CONTENT.get()), "Page identity must not leak onto the book");
|
||||||
|
helper.assertValueEqual(result.get(DataComponents.CUSTOM_NAME), Component.literal("Home Panel"),
|
||||||
|
"The panel's custom name must survive crafting");
|
||||||
|
helper.succeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package net.dnorberg.rivencraft.registry;
|
||||||
|
|
||||||
|
import net.dnorberg.rivencraft.Rivencraft;
|
||||||
|
import net.dnorberg.rivencraft.writing.LinkingBookRecipe;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
|
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||||
|
import net.neoforged.bus.api.IEventBus;
|
||||||
|
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||||
|
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
|
|
||||||
|
public final class ModRecipeSerializers {
|
||||||
|
private static final DeferredRegister<RecipeSerializer<?>> SERIALIZERS =
|
||||||
|
DeferredRegister.create(Registries.RECIPE_SERIALIZER, Rivencraft.MOD_ID);
|
||||||
|
|
||||||
|
public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<LinkingBookRecipe>> LINKING_BOOK =
|
||||||
|
SERIALIZERS.register("linking_book", () -> new SimpleCraftingRecipeSerializer<>(LinkingBookRecipe::new));
|
||||||
|
|
||||||
|
private ModRecipeSerializers() {}
|
||||||
|
|
||||||
|
public static void register(IEventBus modEventBus) {
|
||||||
|
SERIALIZERS.register(modEventBus);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package net.dnorberg.rivencraft.writing;
|
||||||
|
|
||||||
|
import net.dnorberg.rivencraft.item.PageItem;
|
||||||
|
import net.dnorberg.rivencraft.registry.ModItems;
|
||||||
|
import net.dnorberg.rivencraft.registry.ModRecipeSerializers;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.core.NonNullList;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||||
|
import net.minecraft.world.item.crafting.CraftingInput;
|
||||||
|
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.Ingredient;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.neoforged.neoforge.common.crafting.DataComponentIngredient;
|
||||||
|
|
||||||
|
/** Legacy's shapeless leather + Link Panel recipe, adapted to page components. */
|
||||||
|
public final class LinkingBookRecipe extends CustomRecipe {
|
||||||
|
public LinkingBookRecipe(CraftingBookCategory category) {
|
||||||
|
super(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ItemStack findPanel(CraftingInput input) {
|
||||||
|
if (input.ingredientCount() != 2) return ItemStack.EMPTY;
|
||||||
|
ItemStack panel = ItemStack.EMPTY;
|
||||||
|
boolean leather = false;
|
||||||
|
for (ItemStack stack : input.items()) {
|
||||||
|
if (stack.isEmpty()) continue;
|
||||||
|
if (stack.is(ModItems.PAGE.get()) && PageItem.content(stack).kind() == PageContent.Kind.LINK_PANEL
|
||||||
|
&& panel.isEmpty()) {
|
||||||
|
panel = stack;
|
||||||
|
} else if (stack.is(Items.LEATHER) && !leather) {
|
||||||
|
leather = true;
|
||||||
|
} else {
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return leather ? panel : ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(CraftingInput input, Level level) {
|
||||||
|
return !findPanel(input).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack assemble(CraftingInput input, HolderLookup.Provider registries) {
|
||||||
|
ItemStack panel = findPanel(input);
|
||||||
|
if (panel.isEmpty()) return ItemStack.EMPTY;
|
||||||
|
ItemStack book = new ItemStack(ModItems.LINKING_BOOK.get());
|
||||||
|
// Legacy copied the panel's data. Only its custom name is meaningful to a
|
||||||
|
// 1.21.1 Linking Book today; page-only components must not reach the book.
|
||||||
|
if (panel.has(DataComponents.CUSTOM_NAME)) {
|
||||||
|
book.set(DataComponents.CUSTOM_NAME, panel.get(DataComponents.CUSTOM_NAME));
|
||||||
|
}
|
||||||
|
return book;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canCraftInDimensions(int width, int height) {
|
||||||
|
return width * height >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonNullList<Ingredient> getIngredients() {
|
||||||
|
NonNullList<Ingredient> ingredients = NonNullList.create();
|
||||||
|
ingredients.add(DataComponentIngredient.of(false,
|
||||||
|
net.dnorberg.rivencraft.registry.ModDataComponents.PAGE_CONTENT.get(),
|
||||||
|
PageContent.LINK_PANEL, ModItems.PAGE.get()));
|
||||||
|
ingredients.add(Ingredient.of(Items.LEATHER));
|
||||||
|
return ingredients;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecipeSerializer<?> getSerializer() {
|
||||||
|
return ModRecipeSerializers.LINKING_BOOK.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -136,6 +136,7 @@
|
|||||||
"jei.rivencraft.ink_mixer.usage": "Paper + ink makes a Link Panel. One vial fills 16 ink; each panel uses the configured ink cost. The empty vial returns as a glass bottle.",
|
"jei.rivencraft.ink_mixer.usage": "Paper + ink makes a Link Panel. One vial fills 16 ink; each panel uses the configured ink cost. The empty vial returns as a glass bottle.",
|
||||||
"jei.rivencraft.writing.copy": "At a Writing Desk, select a stored Page, add paper and ink, then take the copy. The source Page stays.",
|
"jei.rivencraft.writing.copy": "At a Writing Desk, select a stored Page, add paper and ink, then take the copy. The source Page stays.",
|
||||||
"jei.rivencraft.writing.bind": "At a Book Binder, name the Age and add a cover plus Link Panel. Optional symbol Pages follow the panel.",
|
"jei.rivencraft.writing.bind": "At a Book Binder, name the Age and add a cover plus Link Panel. Optional symbol Pages follow the panel.",
|
||||||
|
"jei.rivencraft.writing.linking_book": "Craft one Link Panel Page with leather to make an unwritten Linking Book. Open it to name and bind it.",
|
||||||
"itemGroup.rivencraft": "Rivencraft",
|
"itemGroup.rivencraft": "Rivencraft",
|
||||||
"itemGroup.rivencraft.pages": "Rivencraft Pages"
|
"itemGroup.rivencraft.pages": "Rivencraft Pages"
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,4 @@
|
|||||||
{
|
{
|
||||||
"type": "minecraft:crafting_shapeless",
|
"type": "rivencraft:linking_book",
|
||||||
"category": "misc",
|
"category": "misc"
|
||||||
"ingredients": [
|
|
||||||
{
|
|
||||||
"item": "minecraft:book"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"item": "minecraft:ender_pearl"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"result": {
|
|
||||||
"count": 1,
|
|
||||||
"id": "rivencraft:linking_book"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user