From 48a350b63a00b0d65a14be60b6f5a56878686144 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 15 May 2020 22:52:32 +0100 Subject: [PATCH] Allow any book to be placed in a bookshelf - Modifies the bookshelf model and blockstate to choose a texture dynamically based on the book item - Changes bookshelf slots to accept all books from wizardry and vanilla minecraft by default - Adds a couple of hooks to allow addons to register their own book items and model textures - Adds a config option to allow modpack makers to add more book items --- .../java/electroblob/wizardry/Settings.java | 7 ++ .../java/electroblob/wizardry/Wizardry.java | 5 + .../wizardry/block/BlockBookshelf.java | 103 +++++++++++++++--- .../client/model/BakedModelBookshelf.java | 10 +- .../wizardry/client/model/ModelBookshelf.java | 20 +++- .../inventory/ContainerBookshelf.java | 46 +++++++- .../wizardry/packet/PacketSyncSettings.java | 30 +++-- .../wizardry/registry/WizardryBlocks.java | 2 + .../assets/ebwizardry/lang/en_gb.lang | 2 + .../assets/ebwizardry/lang/en_us.lang | 2 + .../ebwizardry/textures/blocks/books_blue.png | Bin 0 -> 1977 bytes .../textures/blocks/books_brown.png | Bin 0 -> 1926 bytes .../textures/blocks/books_enchanted.png | Bin 0 -> 1991 bytes .../textures/blocks/books_purple.png | Bin 0 -> 1944 bytes .../blocks/{books.png => books_red.png} | Bin 15 files changed, 186 insertions(+), 41 deletions(-) create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/books_blue.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/books_brown.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/books_enchanted.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/books_purple.png rename src/main/resources/assets/ebwizardry/textures/blocks/{books.png => books_red.png} (100%) diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index cf3db7a4..89b1cf32 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -296,6 +296,8 @@ public final class Settings { Wizardry.MODID + ":acacia_bookshelf", Wizardry.MODID + ":dark_oak_bookshelf" ); + /** [Synchronised] List of registry names of items that can be placed in a bookshelf. */ + public Pair[] bookItems = parseItemMetaStrings(); // Client-only settings. These settings only affect client-side code and hence are not synced. Each client obeys // its own values for these, and changing them on a dedicated server will have no effect. @@ -735,6 +737,11 @@ public final class Settings { bookshelfBlocks = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); + property = config.get(TWEAKS_CATEGORY, "bookItems", new String[0], "List of registry names of items which can be placed in a bookshelf, in addition to the defaults. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. thaumcraft:thaumonomicon)."); + property.setLanguageKey("config." + Wizardry.MODID + ".book_items"); + bookItems = parseItemMetaStrings(property.getStringList()); + propOrder.add(property.getName()); + property = config.get(TWEAKS_CATEGORY, "bookshelfSearchRadius", 4, "The maximum number of blocks a bookshelf can be from an arcane workbench or lectern to be able to link to it.", 1, 10); diff --git a/src/main/java/electroblob/wizardry/Wizardry.java b/src/main/java/electroblob/wizardry/Wizardry.java index 0c9d7b50..db2444c0 100644 --- a/src/main/java/electroblob/wizardry/Wizardry.java +++ b/src/main/java/electroblob/wizardry/Wizardry.java @@ -1,5 +1,6 @@ package electroblob.wizardry; +import electroblob.wizardry.block.BlockBookshelf; import electroblob.wizardry.command.CommandCastSpell; import electroblob.wizardry.command.CommandDiscoverSpell; import electroblob.wizardry.command.CommandSetAlly; @@ -8,6 +9,7 @@ import electroblob.wizardry.data.DispenserCastingData; import electroblob.wizardry.data.WizardData; import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration; import electroblob.wizardry.integration.baubles.WizardryBaublesIntegration; +import electroblob.wizardry.inventory.ContainerBookshelf; import electroblob.wizardry.misc.Forfeit; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.*; @@ -123,6 +125,7 @@ public class Wizardry { WizardryLoot.register(); WizardryAdvancementTriggers.register(); Forfeit.register(); + BlockBookshelf.registerStandardBookModelTextures(); // Client-side stuff (via proxies) proxy.registerRenderers(); @@ -159,6 +162,8 @@ public class Wizardry { WizardryPacketHandler.initPackets(); // Post-registry extras + BlockBookshelf.compileBookModelTextures(); + ContainerBookshelf.initDefaultBookItems(); WizardryItems.populateWandMap(); WizardryItems.populateArmourMap(); WizardryItems.registerDispenseBehaviours(); diff --git a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java index 267cc4b1..53935b17 100644 --- a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java +++ b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java @@ -1,28 +1,29 @@ package electroblob.wizardry.block; +import com.google.common.collect.ImmutableList; import electroblob.wizardry.Settings; import electroblob.wizardry.Wizardry; import electroblob.wizardry.WizardryGuiHandler; +import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryTabs; import electroblob.wizardry.tileentity.TileEntityBookshelf; import net.minecraft.block.BlockHorizontal; import net.minecraft.block.ITileEntityProvider; import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; -import net.minecraft.block.properties.PropertyBool; +import net.minecraft.block.properties.PropertyInteger; import net.minecraft.block.state.BlockFaceShape; import net.minecraft.block.state.BlockStateContainer; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.InventoryHelper; +import net.minecraft.item.Item; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.SoundEvent; +import net.minecraft.util.*; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; import net.minecraft.world.IWorldEventListener; @@ -31,12 +32,17 @@ import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.Properties; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import org.apache.commons.lang3.ArrayUtils; import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.function.Supplier; @Mod.EventBusSubscriber public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvider { @@ -46,13 +52,11 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid public static final double PLAYER_NOTIFY_RANGE = 32; public static final int SLOT_COUNT = 12; - public static final UnlistedPropertyBool[] BOOKS = new UnlistedPropertyBool[SLOT_COUNT]; + public static final UnlistedPropertyInt[] BOOKS = new UnlistedPropertyInt[SLOT_COUNT]; - static { - for(int i=0; i, ResourceLocation> BOOK_TEXTURE_MAP = new HashMap<>(); + private static ImmutableList bookItems; + private static ImmutableList bookTextures; public BlockBookshelf(){ super(Material.WOOD); @@ -115,9 +119,19 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid IExtendedBlockState s = (IExtendedBlockState)super.getExtendedState(state, world, pos); if(world.getTileEntity(pos) instanceof TileEntityBookshelf){ + TileEntityBookshelf tileentity = ((TileEntityBookshelf)world.getTileEntity(pos)); + for(int i = 0; i < tileentity.getSizeInventory(); i++){ - s = s.withProperty(BOOKS[i], !tileentity.getStackInSlot(i).isEmpty()); + + if(tileentity.getStackInSlot(i).isEmpty()){ + s = s.withProperty(BOOKS[i], bookItems.size()); + + }else{ + Item item = tileentity.getStackInSlot(i).getItem(); + // Default to the standard spell book texture, which will always be the first item in the list + s = s.withProperty(BOOKS[i], bookItems.contains(item) ? bookItems.indexOf(item) : 0); + } } } @@ -176,14 +190,71 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid return tileentity != null && tileentity.receiveClientEvent(id, param); } - // Copied from BlockFluidBase, only reason it exists is because of java's weird restrictions on generics - private static final class UnlistedPropertyBool extends Properties.PropertyAdapter { + /** Returns the list of book textures, in ID order. */ + public static ImmutableList getBookTextures(){ + // ModelBookshelf calls this before init() so we need to return the map values as a fallback + return bookTextures == null ? ImmutableList.copyOf(BOOK_TEXTURE_MAP.values()) : bookTextures; + } - public UnlistedPropertyBool(String name){ - super(PropertyBool.create(name)); + /** Called during block registration to initialise the block properties for each book. */ + public static void initBookProperties(){ + for(int i=0; i { + + public UnlistedPropertyInt(String name){ + // Max is inclusive here, but we're using the largest value for no book so there's an extra one (can't use -1) + super(PropertyInteger.create(name, 0, BOOK_TEXTURE_MAP.size())); + } + } + + /** Called from {@link Wizardry#init(FMLInitializationEvent)} to retrieve the actual book items and compile them + * and their textures into immutable lists. */ + public static void compileBookModelTextures(){ + + ImmutableList.Builder itemListBuider = ImmutableList.builder(); + ImmutableList.Builder textureListBuilder = ImmutableList.builder(); + + for(Map.Entry, ResourceLocation> entry : BOOK_TEXTURE_MAP.entrySet()){ + itemListBuider.add(entry.getKey().get()); + textureListBuilder.add(entry.getValue()); + } + + bookItems = itemListBuider.build(); + bookTextures = textureListBuilder.build(); + } + + /** Called from {@link Wizardry#preInit(FMLPreInitializationEvent)} to register the default set of book textures. */ + public static void registerStandardBookModelTextures(){ + // Vanilla Minecraft books + // Regular brown books are the default so they're registered first + registerBookModelTexture(() -> Items.BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_brown")); + registerBookModelTexture(() -> Items.WRITABLE_BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_brown")); + registerBookModelTexture(() -> Items.WRITTEN_BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_brown")); + registerBookModelTexture(() -> Items.ENCHANTED_BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_enchanted")); + // Wizardry books + registerBookModelTexture(() -> WizardryItems.spell_book, new ResourceLocation(Wizardry.MODID, "blocks/books_red")); + registerBookModelTexture(() -> WizardryItems.wizard_handbook, new ResourceLocation(Wizardry.MODID, "blocks/books_blue")); + registerBookModelTexture(() -> WizardryItems.arcane_tome, new ResourceLocation(Wizardry.MODID, "blocks/books_purple")); + } + + /** + * Registers a book texture to be used for the book model when the given item is placed in a bookshelf. This method + * must be called from {@code preInit} as the registered item count is required during block registration. + * This also necessitates the use of a supplier to fetch the item, since the items will not yet be registered. + * @param itemFactory A {@link Supplier} that returns the book item to link the texture to. This item need not + * be an instance of {@link electroblob.wizardry.item.ItemSpellBook ItemSpellBook}. Duplicate + * items will result in an {@link IllegalArgumentException} being thrown later. + * @param texture The texture to apply to the book model. + */ + public static void registerBookModelTexture(Supplier itemFactory, ResourceLocation texture){ + BOOK_TEXTURE_MAP.put(itemFactory, texture); + } + /** * Returns a list of nearby bookshelves' inventories, where 'bookshelves' are any tile entities with inventories * whose blocks are specified in the config file under the {@code bookshelfBlocks} option. diff --git a/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java b/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java index c100b369..af18c319 100644 --- a/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java +++ b/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java @@ -17,9 +17,9 @@ import java.util.List; public class BakedModelBookshelf implements IBakedModel { private final IBakedModel bookshelf; - private final IBakedModel[] books; + private final IBakedModel[][] books; - public BakedModelBookshelf(IBakedModel bookshelf, IBakedModel... books){ + public BakedModelBookshelf(IBakedModel bookshelf, IBakedModel[][] books){ this.bookshelf = bookshelf; this.books = books; } @@ -36,9 +36,9 @@ public class BakedModelBookshelf implements IBakedModel { IExtendedBlockState extendedState = (IExtendedBlockState)state; for(int i = 0; i bookModelLocations = new ArrayList<>(); @@ -48,11 +49,18 @@ public class ModelBookshelf implements IModel { // I don't know why default state works here (surely it ought to be the state param?), but it works so who cares IBakedModel bookshelf = bookshelfModel.bake(bookshelfModel.getDefaultState(), format, bakedTextureGetter); - IBakedModel[] books = new IBakedModel[BlockBookshelf.SLOT_COUNT]; + ImmutableList textures = BlockBookshelf.getBookTextures(); - for(int i = 0; i < BlockBookshelf.SLOT_COUNT; i++){ - IModel bookModel = ModelLoaderRegistry.getModel(new ModelResourceLocation(bookModelLocations.get(i), variant)); - books[i] = bookModel.bake(bookModel.getDefaultState(), format, bakedTextureGetter); // Same here! + IBakedModel[][] books = new IBakedModel[textures.size()][BlockBookshelf.SLOT_COUNT]; + + for(int i = 0; i < textures.size(); i++){ + + ImmutableMap retexturer = ImmutableMap.of("books", textures.get(i).toString()); + + for(int j = 0; j < BlockBookshelf.SLOT_COUNT; j++){ + IModel bookModel = ModelLoaderRegistry.getModel(new ModelResourceLocation(bookModelLocations.get(j), variant)).retexture(retexturer); + books[i][j] = bookModel.bake(bookModel.getDefaultState(), format, bakedTextureGetter); // Same here! + } } // To clarify: the whole point of doing this was to avoid having to bake 4 * 2^12 models per bookshelf type @@ -85,7 +93,7 @@ public class ModelBookshelf implements IModel { // 3. Is it correct to use getModel() to get the dependency models upon construction of this class so I can // access their textures properly? Or is that too early? // 4. If so, what's the point of getDependencies, and why should I override it? - return ImmutableList.of(BOOK_TEXTURE); + return BlockBookshelf.getBookTextures();// ImmutableList.of(DEFAULT_BOOK_TEXTURE); } } diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java b/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java index 5ece23a0..a32b7d2b 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java @@ -1,17 +1,29 @@ package electroblob.wizardry.inventory; +import electroblob.wizardry.Settings; +import electroblob.wizardry.Wizardry; import electroblob.wizardry.block.BlockBookshelf; import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.tileentity.TileEntityBookshelf; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; import net.minecraft.inventory.Container; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.Slot; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; public class ContainerBookshelf extends Container { + private static final Set validItems = new HashSet<>(); + /** The bookshelf tile entity associated with this container. */ public TileEntityBookshelf tileentity; @@ -21,7 +33,7 @@ public class ContainerBookshelf extends Container { for(int y = 0; y < 2; y++){ for(int x = 0; x < BlockBookshelf.SLOT_COUNT / 2; x++){ - this.addSlotToContainer(new SlotBookshelf(tileentity, x + BlockBookshelf.SLOT_COUNT / 2 * y, 35 + x * 18, 17 + y * 18, 64, ItemSpellBook.class)); + this.addSlotToContainer(new SlotBookshelf(tileentity, x + BlockBookshelf.SLOT_COUNT / 2 * y, 35 + x * 18, 17 + y * 18)); } } @@ -94,11 +106,31 @@ public class ContainerBookshelf extends Container { return remainder; } - public class SlotBookshelf extends SlotItemClassList { + /** + * Adds the given item to the set of items that can be put in a bookshelf. This method should be called from the + * {@code init()} phase. + * @param item The item to register + * @see BlockBookshelf#registerBookModelTexture(Supplier, ResourceLocation) + */ + public static void registerBookItem(Item item){ + validItems.add(item); + } - @SafeVarargs - public SlotBookshelf(IInventory inventory, int index, int x, int y, int stackLimit, Class... allowedItemClasses){ - super(inventory, index, x, y, stackLimit, allowedItemClasses); + /** Called from {@link Wizardry#init(FMLInitializationEvent)} to register the default book items. */ + public static void initDefaultBookItems(){ + registerBookItem(WizardryItems.spell_book); + registerBookItem(WizardryItems.arcane_tome); + registerBookItem(WizardryItems.wizard_handbook); + registerBookItem(Items.BOOK); + registerBookItem(Items.WRITTEN_BOOK); + registerBookItem(Items.WRITABLE_BOOK); + registerBookItem(Items.ENCHANTED_BOOK); + } + + public class SlotBookshelf extends Slot { + + public SlotBookshelf(IInventory inventory, int index, int x, int y){ + super(inventory, index, x, y); } @Override @@ -115,6 +147,10 @@ public class ContainerBookshelf extends Container { return result; } + @Override + public boolean isItemValid(ItemStack stack){ + return validItems.contains(stack.getItem()) || Settings.containsMetaItem(Wizardry.settings.bookItems, stack); + } } } diff --git a/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java b/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java index 55958461..24b0be25 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java +++ b/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java @@ -41,6 +41,7 @@ public class PacketSyncSettings implements IMessageHandler { Wizardry.settings.forfeitChance = message.settings.forfeitChance; Wizardry.settings.bookshelfSearchRadius = message.settings.bookshelfSearchRadius; Wizardry.settings.bookshelfBlocks = message.settings.bookshelfBlocks; + Wizardry.settings.bookItems = message.settings.bookItems; } public static class Message implements IMessage { @@ -57,7 +58,6 @@ public class PacketSyncSettings implements IMessageHandler { } @Override - @SuppressWarnings("unchecked") public void fromBytes(ByteBuf buf){ // I'm guessing the settings field will be null here, so it needs initialising. // This is also a great reason to have the settings as an actual object. @@ -69,12 +69,8 @@ public class PacketSyncSettings implements IMessageHandler { settings.replaceVanillaFireballs = buf.readBoolean(); settings.forfeitChance = buf.readFloat(); settings.bookshelfSearchRadius = buf.readInt(); - int length = buf.readInt(); - List> entries = new ArrayList<>(); - for(int i=0; i { buf.writeBoolean(settings.replaceVanillaFireballs); buf.writeFloat((float)settings.forfeitChance); // Configs don't have floats but this can only be 0-1 anyway buf.writeInt(settings.bookshelfSearchRadius); - buf.writeInt(settings.bookshelfBlocks.length); - for(Pair entry : settings.bookshelfBlocks){ + writeMetaItems(buf, settings.bookshelfBlocks); + writeMetaItems(buf, settings.bookItems); + } + + @SuppressWarnings("unchecked") + private static Pair[] readMetaItems(ByteBuf buf){ + int length = buf.readInt(); + List> entries = new ArrayList<>(); + for(int i=0; i[] items){ + buf.writeInt(items.length); + for(Pair entry : items){ ByteBufUtils.writeUTF8String(buf, entry.getLeft().toString()); buf.writeShort(entry.getRight()); } } + } } diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java index b126b19c..3d63775f 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java @@ -96,6 +96,8 @@ public final class WizardryBlocks { @SubscribeEvent public static void register(RegistryEvent.Register event){ + BlockBookshelf.initBookProperties(); + IForgeRegistry registry = event.getRegistry(); registerBlock(registry, "arcane_workbench", new BlockArcaneWorkbench().setHardness(1.0F).setCreativeTab(WizardryTabs.WIZARDRY)); diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index cee7452f..adcf6990 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -1238,6 +1238,8 @@ config.ebwizardry.bow_item_whitelist=Bow Item Whitelist config.ebwizardry.bow_item_whitelist.tooltip=List of registry names of items which should count as bows for imbuement spells. Most bows should work automatically, but those that don't can be added manually here. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. tconstruct:shortbow). config.ebwizardry.bookshelf_blocks=Bookshelf Blocks config.ebwizardry.bookshelf_blocks.tooltip=List of registry names of blocks that count as bookshelves for the arcane workbench and lectern. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. ebwizardry:oak_bookshelf). +config.ebwizardry.book_items=Book Items +config.ebwizardry.book_items.tooltip=List of registry names of items which can be placed in a bookshelf, in addition to the defaults. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. thaumcraft:thaumonomicon). config.ebwizardry.bookshelf_search_radius=Bookshelf Search Radius config.ebwizardry.bookshelf_search_radius.tooltip=The maximum number of blocks a bookshelf can be from an arcane workbench or lectern to be able to link to it. config.ebwizardry.currency_items=Currency Items diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index f3e9bde0..fc4a1010 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -1238,6 +1238,8 @@ config.ebwizardry.bow_item_whitelist=Bow Item Whitelist config.ebwizardry.bow_item_whitelist.tooltip=List of registry names of items which should count as bows for imbuement spells. Most bows should work automatically, but those that don't can be added manually here. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. tconstruct:shortbow). config.ebwizardry.bookshelf_blocks=Bookshelf Blocks config.ebwizardry.bookshelf_blocks.tooltip=List of registry names of blocks that count as bookshelves for the arcane workbench and lectern. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. ebwizardry:oak_bookshelf). +config.ebwizardry.book_items=Book Items +config.ebwizardry.book_items.tooltip=List of registry names of items which can be placed in a bookshelf, in addition to the defaults. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. thaumcraft:thaumonomicon). config.ebwizardry.bookshelf_search_radius=Bookshelf Search Radius config.ebwizardry.bookshelf_search_radius.tooltip=The maximum number of blocks a bookshelf can be from an arcane workbench or lectern to be able to link to it. config.ebwizardry.currency_items=Currency Items diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/books_blue.png b/src/main/resources/assets/ebwizardry/textures/blocks/books_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..07235139f0fc93bbe2df1a4ff7b77eb3ad6cc2b1 GIT binary patch literal 1977 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-k5q+3lmsP~D-;yvr)B1( zDwI?fq$;FVWTr7NRNOin9=_?d8PDJ6TANsy5*Vi+wwf%P^3zghQf==2>9aoxX6kq; zbe08VT>k%~cElg-oM)W0hQ zFJw!ORoQ*1`jK+sukhk4=UskUP2^X;ru<-11q){`<2M7dp9Vs9lkWc0yR9QV=~%k( zt4RBEe~Osv((LBA@Yw~FR9I~@Y-=e`eXaE8+OkE9i)Q_!X74wg)pv1w=}p!dGJS#5HDB|-KK13= zEztt^KQ=)}j+Lt4>6)^lV{w~;_6}{;qyrWw0vG!<|B})UUGZW^@sSfTkt?JGL=#lj zHB3Hob~c;4=Oq`$Vpj`}6*aG&ymO)+X=$>U2Q|M?G*Hg@8NU7J$wh`~PJ)qpYgzYJ zq)Ba=aY9hiJA%z6LuUD}rw?A&?NwXH#PYXj^PvMdr=KwV=w9w#aFQu%!-eEKoIV?6 z*$Mmy->ZDz+#gr_$FTYubJNP8*srEnBDOA@ zC2gL0;;~p%&aI%+bFG|K?tWMOVq>_zuHe&iPzGc^6@esZSe9s7yBkH2wloff`$a_a@3!!JX$PuLxp6KOoXZP%ls zqInk{tEO7Vr}6J`+x&da+R8QN%dT+0~Duy(e0$K0esX zus|lBq1kjf}dFHU*{mfx8! zr||S`vishgw@S+X{E}N0x4u2}u*&D|ZdUs`^v^H0oc2D-JWkB*QM+W(yJrQop z}Z->e;leZuRxWJGx(be_1TE_sEJ${!8vT+k{iR z?&keJwB*0wpXsGVI{ps-Z(Q%5v1sR-sq^euzPNv=wJqX$a^>v)8E*nIo=Qw8J+b!9 z{mWNvoSV9KFa6e7_qpkM`Qj(4#}%4aXZ)QiU%zD5PrKfETP9EQvz&bE%VR0$_p5Hj zU;lmj_10(0_on6lTh_p5RTz?0I{oO6c#*!U56-%`8QtP9izY@KUilzH@B6>H8uOaB z^Zeac+P?Ugtmk~~7sr{rjwiHlFZ+|4cYVR`{kq=P(+ln1zS+L{!r}LuTAyv` z+^Tx{V#$kD$3OcY&}5z$;Q#JjQ45u)g*4e`52M$V1VQyUv=_-*t{}sp09Io9(~)-_ERO&)@#-+uYBXeNXnw zU5>SWTRBUszSYdNN}0!X(f)EZzkx!g~fick2#WBR<^wvp+9!!BU z$Ljr$y!I68oR{ppIoY?@$=h^?tFvAc>!nG~-p$cBrvx;6duKbFSedc#=JH+=a0#gL zSa|1W-vg;9dj6lcEX=Qd|NH;_-~ac&7yisG-Iu%Xk&}hls_RQP?Ch8lIH}WO*StqH z|9u!V6klk#Zrjk6IQn>Ih(sprGPQ)h*q9anbDkbJu_rh{{X zj`0Nk`kV9bIey)x%$gt0|H$=g_-hSO8%LL87t_*eSx;BLK6mcqI%a{(CP}lN%~z0_ zdAKxjPKRFIY=xMp-Opd{JpD9RE8xiPyyNBNHC48Axu>NnrHV8p*)>lITwkdrxO(d@ zh80ta?^zwKY0FxBuRbBd#k^Bb>*<`uuQeGi8r@P6E{r3g0aWl}9(&85k#Qlalz>&la>y{U6^4-p$@iuV@un&17I;VDNPHb6Mw<&;$Uw CroAr! literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/books_brown.png b/src/main/resources/assets/ebwizardry/textures/blocks/books_brown.png new file mode 100644 index 0000000000000000000000000000000000000000..bfc03dc340c3d85293a427927fea1247cb2e3e0c GIT binary patch literal 1926 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-d#gesN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHWfh@SLj1>c`^(akI>0v%5ihbCUe50P(~4Ch;DqxdakQl(LI}Sx9IFWqL%lxe}DZQb$gRy zX3agPn~#6Lo1b`ckq( zyzS86zhQo#Cd^LvQqT9-ZZBB>$5k#>;>k;^HNDHfuiCqP>+Uzq+ho2iI~SJ5{disC zT1)K*eHBMu8GPFzoagEx@*}Wya_kOn)uanIP6jXanS1Mxm{!A!orOoH@7%CBNg-sy z#2pP0MRj+sSnhhVD)>U|13tc>eH$;tn4Nvfr75;<3EMsA0Jj;R>hC_A?6|kTL1|Of zZLS>^E>ZzJ&pLWkd6<>2Y!6J8`f+Fd^#@lQ9cQM60VT-)S{YSm3l|Qxck*kV_$W#@}9R^A|EfQ{SXD_HG+Iwg5VCWi_nu3EXomm6+Q+ZPgD zm}g?D96$A{)YszLt6OfKdQ<$#$)M}%yW6wo=1ee|?V0(#ddsUEMT2hM;;PfC%oGZGM(m|uV}Jf;J>cZ zXW`rCuKXAE9cS^s>9ClyXWFWzYZy-)C{t|r_M7T9Vb$`yjo;0mSy(S!;52R5w6^K` zIn#G^EtsElMlbc7^qI+TJuTArRsDM|aaQ<*^c%Tl9wyu~)33Zw-SUjH{pHi2W^d}Y z9gHcOaM*tNyEVK&vw3+R=7b;WQ^?sfzqdWQr?@OK$v-^EJiWC|r*#he+3+Cu zvQ6agn^W?&vu}BCIk0~2v(OI*ytDOnm9Dbwu8T4F)_Lsi`@dg2j9oUpncNv=)p=&x z>9;N?W=C$_&f!^?zwq&laN*ZI|K2@_yw6|t)v)~Kch5U_0{46qxw1cf%KLvMh9kJle9-_Ecow^dOz$y>XMK9C)yia+I{hsk*mupUk;O{!On#S_l{4R zesBLvd>Snl>Fp>e&0OgpYMi#1_lPUByV>YhW`wg7*?dJ zZ!2eDVBjq9h%9Dc&{GCs#)_r(Wef}q>?NMQuIx`(`GrmSy0YhWF)%PlmbgZgIOpf) zrskC}I2WZRmZYXAlxLP?D7bt2281{Ai8C-ThI+aQNDrnGNanSRTQUcqrzw^>#v$ET2UuH3f zAO8Jzam7`2TiMv}^AyB5H|!CakriQm$Lzb_H{XU0I>OVQzT{N$?vBtqBlK}O=aOH~ zZOuN>PYie73{y5V;Qqy(hJq3#UBsmJv`I5b*p{Dl&)VDdFQ7ed3IkvEL0n_OV3=9kmp00i_>zopr0DnB3n*aa+ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/books_enchanted.png b/src/main/resources/assets/ebwizardry/textures/blocks/books_enchanted.png new file mode 100644 index 0000000000000000000000000000000000000000..fe1be1ab3b2e187db4a5aec75f72cb3de19b01f4 GIT binary patch literal 1991 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 MboFyt=akR{06dbsQUCw| literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/books_purple.png b/src/main/resources/assets/ebwizardry/textures/blocks/books_purple.png new file mode 100644 index 0000000000000000000000000000000000000000..af1439faa07aac5676dcd3770aefb25ddb125b25 GIT binary patch literal 1944 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-msN#ClmsP~D-;yvr)B1( zDwI?fq$;FVWTr7NRNPt{9y#fa2G5`8BE>8!8#^8+OzS9bw*05k9h+Sqe%!|TR6?Y} zLLbgkTk8LpeCF2^TYc>33dPoh!ktfq(o3$a(>|{rX+Hn=(Rp*(ckZ+^aA^v=wz^F> ze}eIs%G0OksXM=WujZ9oZy{&@#8&XNSwYmDww7s(yVcDq{Z;np?*3Z0CiUh7lY9CL zbN=qzf8O9h`mw{FPaZJ4wl7bXPWE`A9W5@n-Ad+d>t%`O{k9h`@3st;v?~-A>Q0xC zJM}B|kIg)ldH$Wpqs7#>*swDy_Lf)y5q%;LYLDq5i2A(L=#l> z7^X33=Dm_p^%P~j!&)NX@$*ybvYRGnMS~>PEn$13=;J2wX@B15&Y<_o3p>`tZET9S zG2)HmdD79Nx`I*gmGz1`wjX=uuWww<;Anq%O+>=B<1-jP$j{4rGKq7G!^Oq1oIV@h zaSA9^Jb1{%DX@XD(z!7w#cU!+V+g~PhgRK>nKw-M9`jJedLl!?1lA3`h7*@*RmUZX zI59j_mFPU6=E)XuuJVJr6>Iw@@%MYaA9P=D_ftlthlg9SEikZn%8Nz)ipN$M9?c24 zvhwXe8r zOD1WDuRQTsDr-$t@Y!Ce6<1u$NRW$85Q71x&TepmfsW48aY=iFO)U!VTVeI;Y@ zG2xX9CG@W-^nRSvG8=QDci=PCDDhJHDbT39M zT$HnGtw+=;#bCzNL_d~zhgm-@BQ@(X%@!=mvz}J6mi5S~)7kF4jnbD+Buq8;=F`gm zywOQj{q6Fg!sYRsnx5Xty`~yib~hn<{z0t+E#7wxWB%P-{kBGD>zkit780SzlI9h!b3DyvT=_^Uhq!^%}ZG zXE)B(+!3er)owv-P@3_RI4zEC*ZO65+USSfce%ga^!bef<2h!^wrf`C6?$*8j{a4( zc~aaSuG9Rkf>F~7SL9i)Tk5KNqHBxN(kYj{tiHSw4cV>H+)^PQ{Mk9ce%Z>(^OxUe zMosrNxT2Z2Xx61$JEP{T@iR{Uo%wF_to@UJCokHq)!wzc>9Wen+l2RW47SG;hV+pFXB4| zO?02XWa3ZF{*LKCG>*pZs=U0pJ52X@SKzg7`PnBtW(t{EotU0oWBsjwfAg;??#~vx z{8H#Yv+N*?(a-R$Tb3OZ{A%|jd`m#Z^b1pF1~;GSR{xx~uK!$|-~R4tmDj2yL$}WjTXA*0 z_N1R#&uh+~@6ObpV%k6F@5;BoV@ebrvqiq$`TtS=w0CEBG=5($ZPB%_<$tZ_F0KBb zzAr21);!PgZ=aY|zA?fq}im z)7O>#2`j&_vDO2lyLTBF7$i$vBTAg}b8}PkN*J7rQWHy3QxwWGOEMJPJ$(bh8~MZ; z7#RILT^vI!PHzpe^*HPxQ5)Xueq_NH^;nrXrNspTyFz0^TnY*|#BznM(0ldbg751T z$96qGS?e9Q&K~t_pA?(Hp6e7KB)yKKkVAY@#=5c(zU6z~@BV%7x7xaFv9c@k zm~Gw=H=|w0UvaXZQ|RU$miyG?{1Xh@-hPsjNpCoEDvsTIwe5aOuDb>o-|ufUcUR0j zE3@T1pUVYa<7wUrEHdc|PnPztFaMup{M!9s_UoA?Hw73H4Atf-n9e+A66(Zy+FknH zzo(7d3|vbCGcB)kybw8!96Il~XOHCN_MI+wd+5d#AQ NgQu&X%Q~loCIG{bgJ%E$ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/books.png b/src/main/resources/assets/ebwizardry/textures/blocks/books_red.png similarity index 100% rename from src/main/resources/assets/ebwizardry/textures/blocks/books.png rename to src/main/resources/assets/ebwizardry/textures/blocks/books_red.png