From 3f80c5616306eaf2449e42afacc58ec3036ffc9c Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 1 Mar 2020 13:04:15 +0000 Subject: [PATCH 001/415] Add gilded wood --- .../wizardry/block/BlockGildedWood.java | 13 + .../wizardry/client/model/WizardryModels.java | 14 +- .../wizardry/item/IMultiTexturedItem.java | 2 +- ...ental.java => ItemBlockMultiTextured.java} | 11 +- .../wizardry/registry/WizardryBlocks.java | 3 + .../wizardry/registry/WizardryItems.java | 17 +- .../blockstates/acacia_gilded_wood.json | 6 + .../blockstates/birch_gilded_wood.json | 6 + .../blockstates/dark_oak_gilded_wood.json | 6 + .../blockstates/jungle_gilded_wood.json | 6 + .../blockstates/oak_gilded_wood.json | 6 + .../blockstates/spruce_gilded_wood.json | 6 + .../assets/ebwizardry/lang/en_gb.lang | 7 + .../assets/ebwizardry/lang/en_us.lang | 7 + .../ebwizardry/models/block/bookshelf.json | 580 ++++++++++++++++++ .../models/block/gilded_acacia_wood.json | 6 + .../models/block/gilded_birch_wood.json | 6 + .../models/block/gilded_dark_oak_wood.json | 6 + .../models/block/gilded_jungle_wood.json | 6 + .../models/block/gilded_oak_wood.json | 6 + .../models/block/gilded_spruce_wood.json | 6 + .../models/item/gilded_acacia_wood.json | 3 + .../models/item/gilded_birch_wood.json | 3 + .../models/item/gilded_dark_oak_wood.json | 3 + .../models/item/gilded_jungle_wood.json | 3 + .../models/item/gilded_oak_wood.json | 3 + .../models/item/gilded_spruce_wood.json | 3 + .../recipes/gilded_acacia_wood.json | 23 + .../ebwizardry/recipes/gilded_birch_wood.json | 23 + .../recipes/gilded_dark_oak_wood.json | 23 + .../recipes/gilded_jungle_wood.json | 23 + .../ebwizardry/recipes/gilded_oak_wood.json | 23 + .../recipes/gilded_spruce_wood.json | 23 + .../ebwizardry/textures/blocks/books.png | Bin 0 -> 476 bytes .../textures/blocks/gilded_acacia_wood.png | Bin 0 -> 518 bytes .../textures/blocks/gilded_birch_wood.png | Bin 0 -> 6047 bytes .../textures/blocks/gilded_dark_oak_wood.png | Bin 0 -> 526 bytes .../textures/blocks/gilded_jungle_wood.png | Bin 0 -> 532 bytes .../textures/blocks/gilded_oak_wood.png | Bin 0 -> 533 bytes .../textures/blocks/gilded_spruce_wood.png | Bin 0 -> 6118 bytes .../blocks/spruce_bookshelf_inside.png | Bin 0 -> 250 bytes .../textures/blocks/spruce_bookshelf_side.png | Bin 0 -> 365 bytes .../textures/blocks/spruce_bookshelf_top.png | Bin 0 -> 356 bytes 43 files changed, 867 insertions(+), 15 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/block/BlockGildedWood.java rename src/main/java/electroblob/wizardry/item/{ItemBlockMultiTexturedElemental.java => ItemBlockMultiTextured.java} (71%) create mode 100644 src/main/resources/assets/ebwizardry/blockstates/acacia_gilded_wood.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/birch_gilded_wood.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/dark_oak_gilded_wood.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/jungle_gilded_wood.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/oak_gilded_wood.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/spruce_gilded_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/gilded_acacia_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/gilded_birch_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/gilded_dark_oak_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/gilded_jungle_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/gilded_oak_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/gilded_spruce_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/gilded_acacia_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/gilded_birch_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/gilded_dark_oak_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/gilded_jungle_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/gilded_oak_wood.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/gilded_spruce_wood.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/gilded_acacia_wood.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/gilded_birch_wood.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/gilded_dark_oak_wood.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/gilded_jungle_wood.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/gilded_oak_wood.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/gilded_spruce_wood.json create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/books.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/gilded_acacia_wood.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/gilded_birch_wood.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/gilded_dark_oak_wood.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/gilded_jungle_wood.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/gilded_oak_wood.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/gilded_spruce_wood.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/spruce_bookshelf_inside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/spruce_bookshelf_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/spruce_bookshelf_top.png diff --git a/src/main/java/electroblob/wizardry/block/BlockGildedWood.java b/src/main/java/electroblob/wizardry/block/BlockGildedWood.java new file mode 100644 index 00000000..f70ba642 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockGildedWood.java @@ -0,0 +1,13 @@ +package electroblob.wizardry.block; + +import electroblob.wizardry.registry.WizardryTabs; +import net.minecraft.block.BlockPlanks; + +public class BlockGildedWood extends BlockPlanks { + + public BlockGildedWood(){ + super(); + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java index 05a6ccc0..2363eb1b 100644 --- a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java +++ b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java @@ -5,10 +5,11 @@ import electroblob.wizardry.block.BlockCrystal; import electroblob.wizardry.block.BlockPedestal; import electroblob.wizardry.block.BlockRunestone; import electroblob.wizardry.item.IMultiTexturedItem; -import electroblob.wizardry.item.ItemBlockMultiTexturedElemental; +import electroblob.wizardry.item.ItemBlockMultiTextured; import electroblob.wizardry.item.ItemCrystal; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; +import net.minecraft.block.BlockPlanks; import net.minecraft.client.renderer.block.model.IBakedModel; import net.minecraft.client.renderer.block.model.ModelBakery; import net.minecraft.client.renderer.block.model.ModelResourceLocation; @@ -50,19 +51,24 @@ public final class WizardryModels { ModelLoader.setCustomStateMapper(WizardryBlocks.crystal_block, new StateMap.Builder() .withName(BlockCrystal.ELEMENT).withSuffix("_crystal_block").build()); // Yay unchecked casting! But we know it's always ok here, and it makes everything much neater. - ItemBlockMultiTexturedElemental crystalBlockItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.crystal_block); + ItemBlockMultiTextured crystalBlockItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.crystal_block); registerMultiTexturedModel(crystalBlockItem); ModelLoader.setCustomStateMapper(WizardryBlocks.runestone, new StateMap.Builder() .withName(BlockRunestone.ELEMENT).withSuffix("_runestone").build()); - ItemBlockMultiTexturedElemental runestoneItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.runestone); + ItemBlockMultiTextured runestoneItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.runestone); registerMultiTexturedModel(runestoneItem); ModelLoader.setCustomStateMapper(WizardryBlocks.runestone_pedestal, new StateMap.Builder() .withName(BlockPedestal.ELEMENT).ignore(BlockPedestal.NATURAL).withSuffix("_runestone_pedestal").build()); // Don't care about NATURAL property - ItemBlockMultiTexturedElemental pedestalItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.runestone_pedestal); + ItemBlockMultiTextured pedestalItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.runestone_pedestal); registerMultiTexturedModel(pedestalItem); + ModelLoader.setCustomStateMapper(WizardryBlocks.gilded_wood, new StateMap.Builder() + .withName(BlockPlanks.VARIANT).withSuffix("_gilded_wood").build()); + ItemBlockMultiTextured gildedWoodItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.gilded_wood); + registerMultiTexturedModel(gildedWoodItem); + // Items registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal); diff --git a/src/main/java/electroblob/wizardry/item/IMultiTexturedItem.java b/src/main/java/electroblob/wizardry/item/IMultiTexturedItem.java index 849f9ae8..45abaa42 100644 --- a/src/main/java/electroblob/wizardry/item/IMultiTexturedItem.java +++ b/src/main/java/electroblob/wizardry/item/IMultiTexturedItem.java @@ -9,7 +9,7 @@ import net.minecraft.util.ResourceLocation; * convenience method {@link electroblob.wizardry.client.model.WizardryModels#registerMultiTexturedModel(Item) WizardryModels.registerMultiTexturedModel(T)}. Also works well for {@code ItemBlock}s! * @author Electroblob * @since Wizardry 4.2 - * @see ItemBlockMultiTexturedElemental + * @see ItemBlockMultiTextured */ public interface IMultiTexturedItem { diff --git a/src/main/java/electroblob/wizardry/item/ItemBlockMultiTexturedElemental.java b/src/main/java/electroblob/wizardry/item/ItemBlockMultiTextured.java similarity index 71% rename from src/main/java/electroblob/wizardry/item/ItemBlockMultiTexturedElemental.java rename to src/main/java/electroblob/wizardry/item/ItemBlockMultiTextured.java index 0ed575cf..8084f853 100644 --- a/src/main/java/electroblob/wizardry/item/ItemBlockMultiTexturedElemental.java +++ b/src/main/java/electroblob/wizardry/item/ItemBlockMultiTextured.java @@ -1,32 +1,33 @@ package electroblob.wizardry.item; -import electroblob.wizardry.constants.Element; import net.minecraft.block.Block; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; -public class ItemBlockMultiTexturedElemental extends ItemBlock implements IMultiTexturedItem { +public class ItemBlockMultiTextured extends ItemBlock implements IMultiTexturedItem { private final boolean separateNames; + private final String[] prefixes; - public ItemBlockMultiTexturedElemental(Block block, boolean separateNames){ + public ItemBlockMultiTextured(Block block, boolean separateNames, String... prefixes){ super(block); this.setHasSubtypes(true); this.setMaxDamage(0); this.separateNames = separateNames; + this.prefixes = prefixes; } @Override public ResourceLocation getModelName(ItemStack stack){ int metadata = stack.getMetadata(); - if(metadata >= Element.values().length) metadata = 0; + if(metadata >= prefixes.length) metadata = 0; return getModelName(metadata); } public ResourceLocation getModelName(int metadata){ return new ResourceLocation(this.block.getRegistryName().getNamespace(), - Element.values()[metadata].getName() + "_" + this.block.getRegistryName().getPath()); + prefixes[metadata] + "_" + this.block.getRegistryName().getPath()); } @Override diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java index 0392545e..588fc193 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java @@ -4,6 +4,7 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.block.*; import electroblob.wizardry.tileentity.*; import net.minecraft.block.Block; +import net.minecraft.block.BlockPlanks; import net.minecraft.block.material.Material; import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; @@ -56,6 +57,7 @@ public final class WizardryBlocks { public static final Block thorns = placeholder(); public static final Block obsidian_crust = placeholder(); public static final Block dry_frosted_ice = placeholder(); + public static final Block gilded_wood = placeholder(); /** * Sets both the registry and unlocalised names of the given block, then registers it with the given registry. Use @@ -95,6 +97,7 @@ public final class WizardryBlocks { registerBlock(registry, "thorns", new BlockThorns()); registerBlock(registry, "obsidian_crust", new BlockObsidianCrust()); registerBlock(registry, "dry_frosted_ice", new BlockDryFrostedIce()); + registerBlock(registry, "gilded_wood", new BlockPlanks()); } diff --git a/src/main/java/electroblob/wizardry/registry/WizardryItems.java b/src/main/java/electroblob/wizardry/registry/WizardryItems.java index 6ae881c3..1d51c3cb 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryItems.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryItems.java @@ -13,6 +13,7 @@ import electroblob.wizardry.registry.WizardryTabs.CreativeTabListed; import electroblob.wizardry.registry.WizardryTabs.CreativeTabSorted; import net.minecraft.block.Block; import net.minecraft.block.BlockDispenser; +import net.minecraft.block.BlockPlanks; import net.minecraft.dispenser.BehaviorProjectileDispense; import net.minecraft.dispenser.IPosition; import net.minecraft.entity.IProjectile; @@ -35,6 +36,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nonnull; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -366,9 +368,9 @@ public final class WizardryItems { } } - private static void registerMultiTexturedItemBlock(IForgeRegistry registry, Block block, boolean separateNames){ + private static void registerMultiTexturedItemBlock(IForgeRegistry registry, Block block, boolean separateNames, String... prefixes){ // We don't need to keep a reference to the ItemBlock - Item itemblock = new ItemBlockMultiTexturedElemental(block, separateNames).setRegistryName(block.getRegistryName()); + Item itemblock = new ItemBlockMultiTextured(block, separateNames, prefixes).setRegistryName(block.getRegistryName()); registry.register(itemblock); if(block.getCreativeTab() instanceof CreativeTabListed){ @@ -389,9 +391,14 @@ public final class WizardryItems { registerItemBlock(registry, WizardryBlocks.crystal_ore); registerItemBlock(registry, WizardryBlocks.crystal_flower); registerItemBlock(registry, WizardryBlocks.transportation_stone); - registerMultiTexturedItemBlock(registry, WizardryBlocks.crystal_block, true); - registerMultiTexturedItemBlock(registry, WizardryBlocks.runestone, false); - registerMultiTexturedItemBlock(registry, WizardryBlocks.runestone_pedestal, false); + + String[] elements = Arrays.stream(Element.values()).map(Element::getName).toArray(String[]::new); + String[] woodTypes = Arrays.stream(BlockPlanks.EnumType.values()).map(BlockPlanks.EnumType::getName).toArray(String[]::new); + + registerMultiTexturedItemBlock(registry, WizardryBlocks.crystal_block, true, elements); + registerMultiTexturedItemBlock(registry, WizardryBlocks.runestone, false, elements); + registerMultiTexturedItemBlock(registry, WizardryBlocks.runestone_pedestal, false, elements); + registerMultiTexturedItemBlock(registry, WizardryBlocks.gilded_wood, true, woodTypes); // Items diff --git a/src/main/resources/assets/ebwizardry/blockstates/acacia_gilded_wood.json b/src/main/resources/assets/ebwizardry/blockstates/acacia_gilded_wood.json new file mode 100644 index 00000000..a5759461 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/acacia_gilded_wood.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "ebwizardry:gilded_acacia_wood" } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/birch_gilded_wood.json b/src/main/resources/assets/ebwizardry/blockstates/birch_gilded_wood.json new file mode 100644 index 00000000..f023d78f --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/birch_gilded_wood.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "ebwizardry:gilded_birch_wood" } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/dark_oak_gilded_wood.json b/src/main/resources/assets/ebwizardry/blockstates/dark_oak_gilded_wood.json new file mode 100644 index 00000000..da105e21 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/dark_oak_gilded_wood.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "ebwizardry:gilded_dark_oak_wood" } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/jungle_gilded_wood.json b/src/main/resources/assets/ebwizardry/blockstates/jungle_gilded_wood.json new file mode 100644 index 00000000..c5479ce0 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/jungle_gilded_wood.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "ebwizardry:gilded_jungle_wood" } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/oak_gilded_wood.json b/src/main/resources/assets/ebwizardry/blockstates/oak_gilded_wood.json new file mode 100644 index 00000000..99d49f6b --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/oak_gilded_wood.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "ebwizardry:gilded_oak_wood" } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/spruce_gilded_wood.json b/src/main/resources/assets/ebwizardry/blockstates/spruce_gilded_wood.json new file mode 100644 index 00000000..989ea5f6 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/spruce_gilded_wood.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "ebwizardry:gilded_spruce_wood" } + } +} diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index db9e697e..05ac7ba8 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -26,6 +26,13 @@ tile.ebwizardry\:earth_crystal_block.name=Block of Verdant Crystal tile.ebwizardry\:sorcery_crystal_block.name=Block of Mystical Crystal tile.ebwizardry\:healing_crystal_block.name=Block of Radiant Crystal +tile.ebwizardry\:oak_gilded_wood.name=Gilded Oak Wood +tile.ebwizardry\:spruce_gilded_wood.name=Gilded Spruce Wood +tile.ebwizardry\:birch_gilded_wood.name=Gilded Birch Wood +tile.ebwizardry\:jungle_gilded_wood.name=Gilded Jungle Wood +tile.ebwizardry\:dark_oak_gilded_wood.name=Gilded Dark Oak Wood +tile.ebwizardry\:acacia_gilded_wood.name=Gilded Acacia Wood + item.ebwizardry\:crystal_magic.name=Magic Crystal item.ebwizardry\:crystal_fire.name=Fiery Crystal item.ebwizardry\:crystal_ice.name=Icy Crystal diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index ca3d7a34..22826952 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -26,6 +26,13 @@ tile.ebwizardry\:earth_crystal_block.name=Block of Verdant Crystal tile.ebwizardry\:sorcery_crystal_block.name=Block of Mystical Crystal tile.ebwizardry\:healing_crystal_block.name=Block of Radiant Crystal +tile.ebwizardry\:oak_gilded_wood.name=Gilded Oak Wood +tile.ebwizardry\:spruce_gilded_wood.name=Gilded Spruce Wood +tile.ebwizardry\:birch_gilded_wood.name=Gilded Birch Wood +tile.ebwizardry\:jungle_gilded_wood.name=Gilded Jungle Wood +tile.ebwizardry\:dark_oak_gilded_wood.name=Gilded Dark Oak Wood +tile.ebwizardry\:acacia_gilded_wood.name=Gilded Acacia Wood + item.ebwizardry\:crystal_magic.name=Magic Crystal item.ebwizardry\:crystal_fire.name=Fiery Crystal item.ebwizardry\:crystal_ice.name=Icy Crystal diff --git a/src/main/resources/assets/ebwizardry/models/block/bookshelf.json b/src/main/resources/assets/ebwizardry/models/block/bookshelf.json new file mode 100644 index 00000000..9e1fb88f --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/bookshelf.json @@ -0,0 +1,580 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books", + "particle": "ebwizardry:blocks/spruce_bookshelf_side", + "side": "ebwizardry:blocks/spruce_bookshelf_side", + "inside": "ebwizardry:blocks/spruce_bookshelf_inside", + "top": "ebwizardry:blocks/spruce_bookshelf_top" + }, + "elements": [ + { + "name": "backboard", + "from": [1, 1, 7], + "to": [15, 15, 9], + "faces": { + "north": {"uv": [1, 1, 15, 15], "texture": "#inside"}, + "south": {"uv": [1, 1, 15, 15], "texture": "#inside"} + } + }, + { + "name": "base", + "from": [0, 0, 0], + "to": [16, 1, 16], + "faces": { + "north": {"uv": [0, 15, 16, 16], "texture": "#side", "cullface": "north"}, + "east": {"uv": [0, 15, 16, 16], "texture": "#side", "cullface": "east"}, + "south": {"uv": [0, 15, 16, 16], "texture": "#side", "cullface": "south"}, + "west": {"uv": [0, 15, 16, 16], "texture": "#side", "cullface": "west"}, + "up": {"uv": [0, 0, 16, 16], "texture": "#inside", "cullface": "up"}, + "down": {"uv": [0, 0, 16, 16], "texture": "#top", "cullface": "down"} + } + }, + { + "name": "side_west", + "from": [0, 1, 0], + "to": [1, 15, 16], + "faces": { + "north": {"uv": [15, 1, 16, 15], "texture": "#side", "cullface": "north"}, + "east": {"uv": [0, 1, 16, 15], "texture": "#inside"}, + "south": {"uv": [0, 1, 1, 15], "texture": "#side", "cullface": "south"}, + "west": {"uv": [0, 1, 16, 15], "texture": "#side", "cullface": "west"} + } + }, + { + "name": "side_east", + "from": [15, 1, 0], + "to": [16, 15, 16], + "faces": { + "north": {"uv": [0, 1, 1, 15], "texture": "#side", "cullface": "north"}, + "east": {"uv": [0, 1, 16, 15], "texture": "#side", "cullface": "east"}, + "south": {"uv": [15, 1, 16, 15], "texture": "#side", "cullface": "south"}, + "west": {"uv": [0, 1, 16, 15], "texture": "#inside", "cullface": "west"} + } + }, + { + "name": "top", + "from": [0, 15, 0], + "to": [16, 16, 16], + "faces": { + "north": {"uv": [0, 0, 16, 1], "texture": "#side", "cullface": "north"}, + "east": {"uv": [0, 0, 16, 1], "texture": "#side", "cullface": "east"}, + "south": {"uv": [0, 0, 16, 1], "texture": "#side", "cullface": "south"}, + "west": {"uv": [0, 0, 16, 1], "texture": "#side", "cullface": "west"}, + "up": {"uv": [0, 0, 16, 16], "texture": "#top", "cullface": "up"}, + "down": {"uv": [0, 0, 16, 16], "texture": "#inside"} + } + }, + { + "name": "shelf", + "from": [1, 7, 0], + "to": [15, 9, 16], + "faces": { + "north": {"uv": [1, 7, 15, 9], "texture": "#side", "cullface": "north"}, + "south": {"uv": [1, 7, 15, 9], "texture": "#side", "cullface": "south"}, + "up": {"uv": [1, 0, 15, 16], "texture": "#inside"}, + "down": {"uv": [1, 0, 15, 16], "texture": "#inside"} + } + }, + { + "name": "cornerUNET", + "from": [14, 14, 0], + "to": [15, 15, 1], + "faces": { + "north": {"uv": [1, 1, 2, 2], "texture": "#side", "cullface": "north"}, + "south": {"uv": [1, 1, 2, 2], "texture": "#side"}, + "west": {"uv": [1, 1, 2, 2], "texture": "#side"}, + "down": {"uv": [1, 1, 2, 2], "texture": "#side"} + } + }, + { + "name": "cornerUSWT", + "from": [1, 14, 15], + "to": [2, 15, 16], + "faces": { + "north": {"uv": [1, 1, 2, 2], "texture": "#side"}, + "east": {"uv": [1, 1, 2, 2], "texture": "#side"}, + "south": {"uv": [1, 1, 2, 2], "texture": "#side", "cullface": "south"}, + "down": {"uv": [1, 1, 2, 2], "rotation": 180, "texture": "#side"} + } + }, + { + "name": "cornerLNET", + "from": [14, 6, 0], + "to": [15, 7, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [1, 9, 2, 10], "texture": "#side", "cullface": "north"}, + "south": {"uv": [1, 9, 2, 10], "texture": "#side"}, + "west": {"uv": [1, 9, 2, 10], "texture": "#side"}, + "down": {"uv": [1, 9, 2, 10], "texture": "#side"} + } + }, + { + "name": "cornerLSWT", + "from": [1, 6, 15], + "to": [2, 7, 16], + "faces": { + "north": {"uv": [1, 9, 2, 10], "texture": "#side"}, + "east": {"uv": [1, 9, 2, 10], "texture": "#side"}, + "south": {"uv": [1, 9, 2, 10], "texture": "#side", "cullface": "south"}, + "down": {"uv": [1, 9, 2, 10], "rotation": 180, "texture": "#side"} + } + }, + { + "name": "cornerUNWT", + "from": [1, 14, 0], + "to": [2, 15, 1], + "faces": { + "north": {"uv": [14, 1, 15, 2], "texture": "#side", "cullface": "north"}, + "east": {"uv": [14, 1, 15, 2], "texture": "#side"}, + "south": {"uv": [14, 1, 15, 2], "texture": "#side"}, + "down": {"uv": [14, 1, 15, 2], "texture": "#side"} + } + }, + { + "name": "cornerUSET", + "from": [14, 14, 15], + "to": [15, 15, 16], + "faces": { + "north": {"uv": [14, 1, 15, 2], "texture": "#side"}, + "south": {"uv": [14, 1, 15, 2], "texture": "#side", "cullface": "south"}, + "west": {"uv": [14, 1, 15, 2], "texture": "#side"}, + "down": {"uv": [14, 1, 15, 2], "rotation": 180, "texture": "#side"} + } + }, + { + "name": "cornerLNWT", + "from": [1, 6, 0], + "to": [2, 7, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [14, 9, 15, 10], "texture": "#side", "cullface": "north"}, + "east": {"uv": [14, 9, 15, 10], "texture": "#side"}, + "south": {"uv": [14, 9, 15, 10], "texture": "#side"}, + "down": {"uv": [14, 9, 15, 10], "texture": "#side"} + } + }, + { + "name": "cornerLSET", + "from": [14, 6, 15], + "to": [15, 7, 16], + "faces": { + "north": {"uv": [14, 9, 15, 10], "texture": "#side"}, + "south": {"uv": [14, 9, 15, 10], "texture": "#side", "cullface": "south"}, + "west": {"uv": [14, 9, 15, 10], "texture": "#side"}, + "down": {"uv": [14, 9, 15, 10], "rotation": 180, "texture": "#side"} + } + }, + { + "name": "cornerUNEB", + "from": [14, 9, 0], + "to": [15, 10, 1], + "faces": { + "north": {"uv": [1, 6, 2, 7], "texture": "#side", "cullface": "north"}, + "south": {"uv": [1, 6, 2, 7], "texture": "#side"}, + "west": {"uv": [1, 6, 2, 7], "texture": "#side"}, + "up": {"uv": [1, 6, 2, 7], "texture": "#side"} + } + }, + { + "name": "cornerUSWB", + "from": [1, 9, 15], + "to": [2, 10, 16], + "faces": { + "north": {"uv": [1, 6, 2, 7], "texture": "#side"}, + "east": {"uv": [1, 6, 2, 7], "texture": "#side"}, + "south": {"uv": [1, 6, 2, 7], "texture": "#side", "cullface": "south"}, + "up": {"uv": [1, 6, 2, 7], "rotation": 180, "texture": "#side"} + } + }, + { + "name": "cornerLNEB", + "from": [14, 1, 0], + "to": [15, 2, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [1, 14, 2, 15], "texture": "#side", "cullface": "north"}, + "south": {"uv": [1, 14, 2, 15], "texture": "#side"}, + "west": {"uv": [1, 14, 2, 15], "texture": "#side"}, + "up": {"uv": [1, 14, 2, 15], "texture": "#side"} + } + }, + { + "name": "cornerLSWB", + "from": [1, 1, 15], + "to": [2, 2, 16], + "faces": { + "north": {"uv": [1, 14, 2, 15], "texture": "#side"}, + "east": {"uv": [1, 14, 2, 15], "texture": "#side"}, + "south": {"uv": [1, 14, 2, 15], "texture": "#side", "cullface": "south"}, + "up": {"uv": [1, 14, 2, 15], "rotation": 180, "texture": "#side"} + } + }, + { + "name": "cornerUNWB", + "from": [1, 9, 0], + "to": [2, 10, 1], + "faces": { + "north": {"uv": [14, 6, 15, 7], "texture": "#side", "cullface": "north"}, + "east": {"uv": [14, 6, 15, 7], "texture": "#side"}, + "south": {"uv": [14, 6, 15, 7], "texture": "#side"}, + "up": {"uv": [14, 6, 15, 7], "texture": "#side"} + } + }, + { + "name": "cornerUSEB", + "from": [14, 9, 15], + "to": [15, 10, 16], + "faces": { + "north": {"uv": [14, 6, 15, 7], "texture": "#side"}, + "south": {"uv": [14, 6, 15, 7], "texture": "#side", "cullface": "south"}, + "west": {"uv": [14, 6, 15, 7], "texture": "#side"}, + "up": {"uv": [14, 6, 15, 7], "rotation": 180, "texture": "#side"} + } + }, + { + "name": "cornerLNWB", + "from": [1, 1, 0], + "to": [2, 2, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [14, 14, 15, 15], "texture": "#side", "cullface": "north"}, + "east": {"uv": [14, 14, 15, 15], "texture": "#side"}, + "south": {"uv": [14, 14, 15, 15], "texture": "#side"}, + "up": {"uv": [14, 14, 15, 15], "texture": "#side"} + } + }, + { + "name": "cornerLSEB", + "from": [14, 1, 15], + "to": [15, 2, 16], + "faces": { + "north": {"uv": [14, 14, 15, 15], "texture": "#side"}, + "south": {"uv": [14, 14, 15, 15], "texture": "#side", "cullface": "south"}, + "west": {"uv": [14, 14, 15, 15], "texture": "#side"}, + "up": {"uv": [14, 14, 15, 15], "rotation": 180, "texture": "#side"} + } + }, + { + "name": "book1", + "from": [13, 9, 2], + "to": [15, 14, 6], + "faces": { + "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "texture": "#books"} + } + }, + { + "name": "book1", + "from": [1, 9, 10], + "to": [3, 14, 14], + "faces": { + "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book2", + "from": [11, 9, 2], + "to": [13, 14, 6], + "faces": { + "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "texture": "#books"} + } + }, + { + "name": "book2", + "from": [3, 9, 10], + "to": [5, 14, 14], + "faces": { + "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book3", + "from": [8, 9, 2], + "to": [10, 14, 6], + "faces": { + "north": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "east": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "south": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "west": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "texture": "#books"} + } + }, + { + "name": "book3", + "from": [6, 9, 10], + "to": [8, 14, 14], + "faces": { + "north": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "east": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "south": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "west": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book4", + "from": [5, 9, 2], + "to": [7, 14, 6], + "faces": { + "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "texture": "#books"} + } + }, + { + "name": "book4", + "from": [9, 9, 10], + "to": [11, 14, 14], + "faces": { + "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book5", + "from": [3, 9, 2], + "to": [5, 14, 6], + "faces": { + "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "texture": "#books"} + } + }, + { + "name": "book5", + "from": [11, 9, 10], + "to": [13, 14, 14], + "faces": { + "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book6", + "from": [1, 9, 2], + "to": [3, 14, 6], + "faces": { + "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "texture": "#books"} + } + }, + { + "name": "book6", + "from": [13, 9, 10], + "to": [15, 14, 14], + "faces": { + "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book7", + "from": [13, 1, 2], + "to": [15, 6, 6], + "faces": { + "north": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "east": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "south": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "west": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "texture": "#books"} + } + }, + { + "name": "book7", + "from": [1, 1, 10], + "to": [3, 6, 14], + "faces": { + "north": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "east": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "south": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "west": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book8", + "from": [11, 1, 2], + "to": [13, 6, 6], + "faces": { + "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "texture": "#books"} + } + }, + { + "name": "book8", + "from": [3, 1, 10], + "to": [5, 6, 14], + "faces": { + "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book9", + "from": [9, 1, 2], + "to": [11, 6, 6], + "faces": { + "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "texture": "#books"} + } + }, + { + "name": "book9", + "from": [5, 1, 10], + "to": [7, 6, 14], + "faces": { + "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book10", + "from": [6, 1, 2], + "to": [8, 6, 6], + "faces": { + "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "texture": "#books"} + } + }, + { + "name": "book10", + "from": [8, 1, 10], + "to": [10, 6, 14], + "faces": { + "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book11", + "from": [4, 1, 2], + "to": [6, 6, 6], + "faces": { + "north": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "east": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "south": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "west": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "texture": "#books"} + } + }, + { + "name": "book11", + "from": [10, 1, 10], + "to": [12, 6, 14], + "faces": { + "north": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "east": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "south": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "west": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "rotation": 180, "texture": "#books"} + } + }, + { + "name": "book12", + "from": [1, 1, 2], + "to": [3, 6, 6], + "faces": { + "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "texture": "#books"} + } + }, + { + "name": "book12", + "from": [13, 1, 10], + "to": [15, 6, 14], + "faces": { + "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "thirdperson_lefthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "firstperson_righthand": { + "rotation": [0, 45, 0], + "scale": [0.4, 0.4, 0.4] + }, + "firstperson_lefthand": { + "rotation": [0, 225, 0], + "scale": [0.4, 0.4, 0.4] + }, + "ground": { + "translation": [0, 3, 0], + "scale": [0.25, 0.25, 0.25] + }, + "gui": { + "rotation": [30, 135, 0], + "scale": [0.625, 0.625, 0.625] + }, + "fixed": { + "scale": [0.5, 0.5, 0.5] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/gilded_acacia_wood.json b/src/main/resources/assets/ebwizardry/models/block/gilded_acacia_wood.json new file mode 100644 index 00000000..4574dea5 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/gilded_acacia_wood.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "ebwizardry:blocks/gilded_acacia_wood" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/gilded_birch_wood.json b/src/main/resources/assets/ebwizardry/models/block/gilded_birch_wood.json new file mode 100644 index 00000000..f3369511 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/gilded_birch_wood.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "ebwizardry:blocks/gilded_birch_wood" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/gilded_dark_oak_wood.json b/src/main/resources/assets/ebwizardry/models/block/gilded_dark_oak_wood.json new file mode 100644 index 00000000..08934ce4 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/gilded_dark_oak_wood.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "ebwizardry:blocks/gilded_dark_oak_wood" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/gilded_jungle_wood.json b/src/main/resources/assets/ebwizardry/models/block/gilded_jungle_wood.json new file mode 100644 index 00000000..6b7e319c --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/gilded_jungle_wood.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "ebwizardry:blocks/gilded_jungle_wood" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/gilded_oak_wood.json b/src/main/resources/assets/ebwizardry/models/block/gilded_oak_wood.json new file mode 100644 index 00000000..59b4e00f --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/gilded_oak_wood.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "ebwizardry:blocks/gilded_oak_wood" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/gilded_spruce_wood.json b/src/main/resources/assets/ebwizardry/models/block/gilded_spruce_wood.json new file mode 100644 index 00000000..5d3d72ec --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/gilded_spruce_wood.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "ebwizardry:blocks/gilded_spruce_wood" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_acacia_wood.json b/src/main/resources/assets/ebwizardry/models/item/gilded_acacia_wood.json new file mode 100644 index 00000000..dde12e2e --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/gilded_acacia_wood.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/gilded_acacia_wood" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_birch_wood.json b/src/main/resources/assets/ebwizardry/models/item/gilded_birch_wood.json new file mode 100644 index 00000000..845dcc60 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/gilded_birch_wood.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/gilded_birch_wood" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_dark_oak_wood.json b/src/main/resources/assets/ebwizardry/models/item/gilded_dark_oak_wood.json new file mode 100644 index 00000000..4b8fdfe6 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/gilded_dark_oak_wood.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/gilded_dark_oak_wood" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_jungle_wood.json b/src/main/resources/assets/ebwizardry/models/item/gilded_jungle_wood.json new file mode 100644 index 00000000..54d2fbc0 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/gilded_jungle_wood.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/gilded_jungle_wood" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_oak_wood.json b/src/main/resources/assets/ebwizardry/models/item/gilded_oak_wood.json new file mode 100644 index 00000000..34674ded --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/gilded_oak_wood.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/gilded_oak_wood" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_spruce_wood.json b/src/main/resources/assets/ebwizardry/models/item/gilded_spruce_wood.json new file mode 100644 index 00000000..0a68aba2 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/gilded_spruce_wood.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/gilded_spruce_wood" +} diff --git a/src/main/resources/assets/ebwizardry/recipes/gilded_acacia_wood.json b/src/main/resources/assets/ebwizardry/recipes/gilded_acacia_wood.json new file mode 100644 index 00000000..fda2add0 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/gilded_acacia_wood.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + "wgw", + "www" + ], + "key": { + "g": { + "type": "forge:ore_dict", + "ore": "nuggetGold" + }, + "w": { + "item": "minecraft:planks", + "data": 4 + } + }, + "result": { + "item": "ebwizardry:gilded_wood", + "data": 4, + "count": 8 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/gilded_birch_wood.json b/src/main/resources/assets/ebwizardry/recipes/gilded_birch_wood.json new file mode 100644 index 00000000..0679b0eb --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/gilded_birch_wood.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + "wgw", + "www" + ], + "key": { + "g": { + "type": "forge:ore_dict", + "ore": "nuggetGold" + }, + "w": { + "item": "minecraft:planks", + "data": 2 + } + }, + "result": { + "item": "ebwizardry:gilded_wood", + "data": 2, + "count": 8 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/gilded_dark_oak_wood.json b/src/main/resources/assets/ebwizardry/recipes/gilded_dark_oak_wood.json new file mode 100644 index 00000000..6ea664e4 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/gilded_dark_oak_wood.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + "wgw", + "www" + ], + "key": { + "g": { + "type": "forge:ore_dict", + "ore": "nuggetGold" + }, + "w": { + "item": "minecraft:planks", + "data": 5 + } + }, + "result": { + "item": "ebwizardry:gilded_wood", + "data": 5, + "count": 8 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/gilded_jungle_wood.json b/src/main/resources/assets/ebwizardry/recipes/gilded_jungle_wood.json new file mode 100644 index 00000000..17497047 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/gilded_jungle_wood.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + "wgw", + "www" + ], + "key": { + "g": { + "type": "forge:ore_dict", + "ore": "nuggetGold" + }, + "w": { + "item": "minecraft:planks", + "data": 3 + } + }, + "result": { + "item": "ebwizardry:gilded_wood", + "data": 3, + "count": 8 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/gilded_oak_wood.json b/src/main/resources/assets/ebwizardry/recipes/gilded_oak_wood.json new file mode 100644 index 00000000..18da37be --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/gilded_oak_wood.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + "wgw", + "www" + ], + "key": { + "g": { + "type": "forge:ore_dict", + "ore": "nuggetGold" + }, + "w": { + "item": "minecraft:planks", + "data": 0 + } + }, + "result": { + "item": "ebwizardry:gilded_wood", + "data": 0, + "count": 8 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/gilded_spruce_wood.json b/src/main/resources/assets/ebwizardry/recipes/gilded_spruce_wood.json new file mode 100644 index 00000000..ec5c03e1 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/gilded_spruce_wood.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + "wgw", + "www" + ], + "key": { + "g": { + "type": "forge:ore_dict", + "ore": "nuggetGold" + }, + "w": { + "item": "minecraft:planks", + "data": 1 + } + }, + "result": { + "item": "ebwizardry:gilded_wood", + "data": 1, + "count": 8 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/books.png b/src/main/resources/assets/ebwizardry/textures/blocks/books.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b3ab2a6cacedb333a2ff4b484e27d9137fbfde GIT binary patch literal 476 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^J-8j2JlfwtshE zU|`@Z@Q5sCV9-+rVaAH3_GJtV4D2PIzOL*~m}GhMtYR0ly*hk|DgbpqxI)QW?gU5sE@L1 zTrHiKC-L|}(xXQVIw~1XF(su5!E;nDHXc2B@t~+(!)(qTJrzAJ7F7jbUQbz{+1nBh z9^8Dl{Jr(}d&T`X{8iqFzvgD2CcvZHJ@<}UP}Yl+7HtzBJ$n*W@wxhFu1|5#q;s#1 z+)|$Pm2sK?%f+`}Yo9o3oG*XB#qM~^L2scWLd6NQ9zHpBJmnFa^XXYy6?^N2`rI~u z+Q&I*B1d58SJ51q_qW=oyPG+Lgf9PdYSp3#B{S3?t>K)qwOL}(UZ2f&9HJi!O}^}n zjj`1!W4$b+yp+vhcf z_sieSJZXDB_Wa(+18Sxcz8CjIHf%U15!RS@d+{{`!&66nK0hhuaM=DfFh6W&4PW8B zeHR;8?&MbgXyk6tb9Fkx@09nhe`9oQ7O!xEqCh+2z5Q)(B#b5>(h}VeUHj^+R@v(P zA`w>Yd+K>DEj85c{8eB%@Uv7{>D*y$rUM${qBVsPK^x>-xUMZdpe4HE&9kf445^E~ z7bYD)Skm?7Zbymp9mA|1b+3Ha=%us7qDtkaZsYS%kDBh%{b&h)xay~7fnx7P-Up8q zPUc)>8(#LA$L(^@W<6H%-)i65XUi)7vUwXefBSsH<<+-mMxNDO`Czm5i_#-O3Qdod zK1THzyH0Ofe0oA$`p1&Er1F658D1)TwH(gM91MIamw*2Gx%Im*zW-y+`Gft@pVM3o U&+t<8 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/gilded_birch_wood.png b/src/main/resources/assets/ebwizardry/textures/blocks/gilded_birch_wood.png new file mode 100644 index 0000000000000000000000000000000000000000..1c2260835681f7dcac9e66562a9435f0036514d8 GIT binary patch literal 6047 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/gilded_dark_oak_wood.png b/src/main/resources/assets/ebwizardry/textures/blocks/gilded_dark_oak_wood.png new file mode 100644 index 0000000000000000000000000000000000000000..786dcc9b219b39bea2ba4b7643e51f6d4d6435e3 GIT binary patch literal 526 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^J-8j2JlfwtshE zU|`@Z@Q5sCV9-+rVaAH3_GJtV4D2PIzOL*~m}I%lzCc;yveE;B5c{^ZZT z&$Z9*NIYNO>vO8(zs>E~xJ)v6{P5q5y!TW6c5^lqmTj5*(ZiHUK8E3J2bFhQd*SEcC8OG(R-N# z>{?SbZo6)Hv&cqzb4Yyz>b_q6$5YQ8GP20HQZe64w<&VZJci8*hl+E`?si$tOV0lA_NbZv`GY|U z`y-p{xDzGMG>-qU(jtl6 d;HT5_jSnuH+`XZ=l7WGN!PC{xWt~$(695ej-3tH! literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/gilded_jungle_wood.png b/src/main/resources/assets/ebwizardry/textures/blocks/gilded_jungle_wood.png new file mode 100644 index 0000000000000000000000000000000000000000..b928ff6186d1d4f823fa0be0e5a148532176d779 GIT binary patch literal 532 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^J-8j2JlfwtshE zU|`@Z@Q5sCV9-+rVaAH3_GJtV4D2PIzOL*~m}I%F6uK4c+!z=bBuiW)N}Tg^b5rw5 z7@Uhz6H8K46v{J8G8EiBeFMT9`NSC*7;8LT978Nl&z-c_i`h}6b$>=(0h1?BnnoPbpMq14Y`vg#&SH0WYut^r z?{nVI+x=bbUEX@T=c_IMuRM4{-7@~{O+U{gar5HBFJ^FU%2QN}JFUvFcip2sPd82T zR^vCGqj?oHM+XIz|j|JpHzsD$z>PS$}X+9w4{1>Z}wx@iBLtvE~L-`S|gQF^{j z8Yl7=gg6++yKu;V-NmJTq1tYp6^~opn#}^4=U%bj`ObDfU-+T(rQ;uem^}S*qWf&r z8S&nof&Lfdm(?-c%5i>ktLL$@%dD!m&HoHs_kKH=lJ&RQEx1MROq^&!+O@RU&e^9Y j``0e2U;Fj8b$sdBQ)acD=Y$y;7#KWV{an^LB{Ts5>!{og literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/gilded_oak_wood.png b/src/main/resources/assets/ebwizardry/textures/blocks/gilded_oak_wood.png new file mode 100644 index 0000000000000000000000000000000000000000..29b2f19acdfde66cde2478d02e5fa8a376ec27c6 GIT binary patch literal 533 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/spruce_bookshelf_side.png b/src/main/resources/assets/ebwizardry/textures/blocks/spruce_bookshelf_side.png new file mode 100644 index 0000000000000000000000000000000000000000..821d3a6fa432e90474e6820258e4df6b73ebd6f5 GIT binary patch literal 365 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^J-8j2JlfwtshE zU|`@Z@Q5sCV9-+rVaAH3_GJtV4D2PIzOL*~m}GgyH5SH9&}3j>kSuYHC~?lu%}vcK zVQ?-=O)N=GQ7F$W$xv|j^bH7au4V&?*7-k{I}1$I({p|C zfU~Ogo!r5V$_zB)aZiwf)K8a~|iJ&RNe> zdil_^A9C)q3M_7)ty(c#tWYF^HKmgE{^JD~W4h#mzA-QyyJNm_%3l*{Qw9M&@dcI> z^-|85woTRw>Ro0~EBJCz;q0pU)t5vbRLd}|iSAg;;Ci$1Qp@9#jrj{TtYy9_dfb`M zAhBuBre_Zdh3%gm2w9lSJJFkSuYHC~?lu%}vcK zVQ?-=O)N=GQ7F$W$xv|j^bH7aWh6Tn#a4e76f^(veVKy`Fz!nUuT)V`@X%fdp(ziN7k|*T#_0$1x2Uq^6OMF zI@<2K%B5OSYk_E;tj^P#8R^xY5sT-~&wL^M=kr|or}Gx{*!jtLFfcGMc)I$ztaD0e F0st27fv^Ao literal 0 HcmV?d00001 From bb6bb7816372cb54a3d5f2820e65e893720b6981 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 1 Mar 2020 13:07:55 +0000 Subject: [PATCH 002/415] IntelliJ broke my formatting again >:( --- .../wizardry/registry/WizardryBlocks.java | 14 +-- .../wizardry/registry/WizardryEntities.java | 44 ++++----- .../wizardry/registry/WizardryItems.java | 98 +++++++++---------- 3 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java index 588fc193..c10c4daa 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java @@ -83,19 +83,19 @@ public final class WizardryBlocks { registerBlock(registry, "arcane_workbench", new BlockArcaneWorkbench().setHardness(1.0F).setCreativeTab(WizardryTabs.WIZARDRY)); registerBlock(registry, "crystal_ore", new BlockCrystalOre(Material.ROCK).setHardness(3.0F).setCreativeTab(WizardryTabs.WIZARDRY)); registerBlock(registry, "petrified_stone", new BlockStatue(Material.ROCK).setHardness(1.5F).setResistance(10.0F)); - registerBlock(registry, "ice_statue", new BlockStatue(Material.ICE).setHardness(0.5F).setLightOpacity(3)); + registerBlock(registry, "ice_statue", new BlockStatue(Material.ICE).setHardness(0.5F).setLightOpacity(3)); registerBlock(registry, "magic_light", new BlockMagicLight(Material.CIRCUITS)); - registerBlock(registry, "crystal_flower", new BlockCrystalFlower(Material.PLANTS).setHardness(0.0F).setCreativeTab(WizardryTabs.WIZARDRY)); + registerBlock(registry, "crystal_flower", new BlockCrystalFlower(Material.PLANTS).setHardness(0.0F).setCreativeTab(WizardryTabs.WIZARDRY)); registerBlock(registry, "snare", new BlockSnare(Material.PLANTS).setHardness(0.0F)); registerBlock(registry, "transportation_stone", new BlockTransportationStone(Material.ROCK).setHardness(0.3F).setLightLevel(0.5f).setLightOpacity(0).setCreativeTab(WizardryTabs.WIZARDRY)); - registerBlock(registry, "spectral_block", new BlockSpectral(Material.GLASS).setLightOpacity(1).setBlockUnbreakable().setResistance(6000000.0F)); + registerBlock(registry, "spectral_block", new BlockSpectral(Material.GLASS).setLightOpacity(1).setBlockUnbreakable().setResistance(6000000.0F)); registerBlock(registry, "crystal_block", new BlockCrystal(Material.IRON).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY)); - registerBlock(registry, "meteor", new Block(Material.ROCK).setLightLevel(1)); + registerBlock(registry, "meteor", new Block(Material.ROCK).setLightLevel(1)); registerBlock(registry, "vanishing_cobweb", new BlockVanishingCobweb(Material.WEB).setLightOpacity(1).setHardness(4.0F)); registerBlock(registry, "runestone", new BlockRunestone(Material.ROCK)); - registerBlock(registry, "runestone_pedestal", new BlockPedestal(Material.ROCK)); - registerBlock(registry, "thorns", new BlockThorns()); - registerBlock(registry, "obsidian_crust", new BlockObsidianCrust()); + registerBlock(registry, "runestone_pedestal", new BlockPedestal(Material.ROCK)); + registerBlock(registry, "thorns", new BlockThorns()); + registerBlock(registry, "obsidian_crust", new BlockObsidianCrust()); registerBlock(registry, "dry_frosted_ice", new BlockDryFrostedIce()); registerBlock(registry, "gilded_wood", new BlockPlanks()); diff --git a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java index b7bf9839..ddb374bb 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java @@ -61,18 +61,18 @@ public class WizardryEntities { IForgeRegistry registry = event.getRegistry(); // Vanilla summoned creatures - registry.register(createEntry(EntityZombieMinion.class, "zombie_minion", TrackingType.LIVING).build()); - registry.register(createEntry(EntityHuskMinion.class, "husk_minion", TrackingType.LIVING).build()); - registry.register(createEntry(EntitySkeletonMinion.class, "skeleton_minion", TrackingType.LIVING).build()); - registry.register(createEntry(EntityStrayMinion.class, "stray_minion", TrackingType.LIVING).build()); - registry.register(createEntry(EntitySpiderMinion.class, "spider_minion", TrackingType.LIVING).build()); - registry.register(createEntry(EntityBlazeMinion.class, "blaze_minion", TrackingType.LIVING).build()); + registry.register(createEntry(EntityZombieMinion.class, "zombie_minion", TrackingType.LIVING).build()); + registry.register(createEntry(EntityHuskMinion.class, "husk_minion", TrackingType.LIVING).build()); + registry.register(createEntry(EntitySkeletonMinion.class, "skeleton_minion", TrackingType.LIVING).build()); + registry.register(createEntry(EntityStrayMinion.class, "stray_minion", TrackingType.LIVING).build()); + registry.register(createEntry(EntitySpiderMinion.class, "spider_minion", TrackingType.LIVING).build()); + registry.register(createEntry(EntityBlazeMinion.class, "blaze_minion", TrackingType.LIVING).build()); registry.register(createEntry(EntityWitherSkeletonMinion.class, "wither_skeleton_minion", TrackingType.LIVING).build()); - registry.register(createEntry(EntitySilverfishMinion.class, "silverfish_minion", TrackingType.LIVING).build()); - registry.register(createEntry(EntityVexMinion.class, "vex_minion", TrackingType.LIVING).build()); + registry.register(createEntry(EntitySilverfishMinion.class, "silverfish_minion", TrackingType.LIVING).build()); + registry.register(createEntry(EntityVexMinion.class, "vex_minion", TrackingType.LIVING).build()); // Custom summoned creatures - registry.register(createEntry(EntityIceWraith.class, "ice_wraith", TrackingType.LIVING).egg(0xaafaff, 0x001ce1) + registry.register(createEntry(EntityIceWraith.class, "ice_wraith", TrackingType.LIVING).egg(0xaafaff, 0x001ce1) .spawn(EnumCreatureType.MONSTER, Wizardry.settings.iceWraithSpawnRate, 1, 1, ForgeRegistries.BIOMES.getValuesCollection().stream() .filter(b -> !Arrays.asList(Wizardry.settings.mobSpawnBiomeBlacklist).contains(b.getRegistryName()) && BiomeDictionary.hasType(b, BiomeDictionary.Type.SNOWY) @@ -94,10 +94,10 @@ public class WizardryEntities { // These two are only made of particles, so we can afford a lower update frequency registry.register(createEntry(EntityShadowWraith.class, "shadow_wraith") .tracker(80, 10, true).egg(0x11071c, 0x421384).build()); - registry.register(createEntry(EntityStormElemental.class, "storm_elemental") .tracker(80, 10, true).egg(0x162128, 0x135279).build()); + registry.register(createEntry(EntityStormElemental.class, "storm_elemental") .tracker(80, 10, true).egg(0x162128, 0x135279).build()); // Other living entities - registry.register(createEntry(EntityWizard.class, "wizard", TrackingType.LIVING).egg(0x19295e, 0xee9312).build()); + registry.register(createEntry(EntityWizard.class, "wizard", TrackingType.LIVING).egg(0x19295e, 0xee9312).build()); registry.register(createEntry(EntityEvilWizard.class, "evil_wizard", TrackingType.LIVING).egg(0x290404, 0xee9312) // For reference: 5, 1, 1 are the parameters for the witch in vanilla .spawn(EnumCreatureType.MONSTER, Wizardry.settings.evilWizardSpawnRate, 1, 1, ForgeRegistries.BIOMES.getValuesCollection().stream() @@ -115,17 +115,17 @@ public class WizardryEntities { // Directionless projectiles registry.register(createEntry(EntityFirebomb.class, "firebomb", TrackingType.PROJECTILE).build()); registry.register(createEntry(EntityPoisonBomb.class, "poison_bomb", TrackingType.PROJECTILE).build()); - registry.register(createEntry(EntitySparkBomb.class, "spark_bomb", TrackingType.PROJECTILE).build()); - registry.register(createEntry(EntitySmokeBomb.class, "smoke_bomb", TrackingType.PROJECTILE).build()); - registry.register(createEntry(EntityIceCharge.class, "ice_charge", TrackingType.PROJECTILE).build()); + registry.register(createEntry(EntitySparkBomb.class, "spark_bomb", TrackingType.PROJECTILE).build()); + registry.register(createEntry(EntitySmokeBomb.class, "smoke_bomb", TrackingType.PROJECTILE).build()); + registry.register(createEntry(EntityIceCharge.class, "ice_charge", TrackingType.PROJECTILE).build()); registry.register(createEntry(EntityForceOrb.class, "force_orb", TrackingType.PROJECTILE).build()); registry.register(createEntry(EntitySpark.class, "spark", TrackingType.PROJECTILE).build()); registry.register(createEntry(EntityDarknessOrb.class, "darkness_orb", TrackingType.PROJECTILE).build()); registry.register(createEntry(EntityFirebolt.class, "firebolt", TrackingType.PROJECTILE).build()); registry.register(createEntry(EntityThunderbolt.class, "thunderbolt", TrackingType.PROJECTILE).build()); - registry.register(createEntry(EntityLightningDisc.class, "lightning_disc", TrackingType.PROJECTILE).build()); + registry.register(createEntry(EntityLightningDisc.class, "lightning_disc", TrackingType.PROJECTILE).build()); registry.register(createEntry(EntityEmber.class, "ember", TrackingType.PROJECTILE).build()); - registry.register(createEntry(EntityMagicFireball.class, "magic_fireball", TrackingType.PROJECTILE).build()); + registry.register(createEntry(EntityMagicFireball.class, "magic_fireball", TrackingType.PROJECTILE).build()); registry.register(createEntry(EntityLargeMagicFireball.class, "large_magic_fireball", TrackingType.PROJECTILE).build()); registry.register(createEntry(EntityIceball.class, "iceball", TrackingType.PROJECTILE).build()); @@ -135,10 +135,10 @@ public class WizardryEntities { registry.register(createEntry(EntityLevitatingBlock.class, "levitating_block") .tracker(160, 3, true).build()); // Constructs - registry.register(createEntry(EntityBlackHole.class, "black_hole", TrackingType.CONSTRUCT).build()); + registry.register(createEntry(EntityBlackHole.class, "black_hole", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityBlizzard.class, "blizzard", TrackingType.CONSTRUCT).build()); - registry.register(createEntry(EntityForcefield.class, "forcefield", TrackingType.CONSTRUCT).build()); - registry.register(createEntry(EntityFireSigil.class, "fire_sigil", TrackingType.CONSTRUCT).build()); + registry.register(createEntry(EntityForcefield.class, "forcefield", TrackingType.CONSTRUCT).build()); + registry.register(createEntry(EntityFireSigil.class, "fire_sigil", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityFrostSigil.class, "frost_sigil", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityLightningSigil.class, "lightning_sigil", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityCombustionRune.class, "combustion_rune", TrackingType.CONSTRUCT).build()); @@ -147,14 +147,14 @@ public class WizardryEntities { registry.register(createEntry(EntityDecay.class, "decay", TrackingType.CONSTRUCT).build()); // These ones don't render, currently that makes no difference here but we might as well separate them - registry.register(createEntry(EntityArrowRain.class, "arrow_rain", TrackingType.CONSTRUCT).build()); - registry.register(createEntry(EntityEarthquake.class, "earthquake", TrackingType.CONSTRUCT).build()); + registry.register(createEntry(EntityArrowRain.class, "arrow_rain", TrackingType.CONSTRUCT).build()); + registry.register(createEntry(EntityEarthquake.class, "earthquake", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityHailstorm.class, "hailstorm", TrackingType.CONSTRUCT).build()); // These ones move, velocity updates are sent if that's not at constant velocity registry.register(createEntry(EntityShield.class, "shield") .tracker(160, 10, true).build()); registry.register(createEntry(EntityBubble.class, "bubble") .tracker(160, 3, false).build()); - registry.register(createEntry(EntityTornado.class, "tornado") .tracker(160, 3, false).build()); + registry.register(createEntry(EntityTornado.class, "tornado") .tracker(160, 3, false).build()); registry.register(createEntry(EntityIceSpike.class, "ice_spike") .tracker(160, 1, true).build()); } diff --git a/src/main/java/electroblob/wizardry/registry/WizardryItems.java b/src/main/java/electroblob/wizardry/registry/WizardryItems.java index 1d51c3cb..10ae105c 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryItems.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryItems.java @@ -407,29 +407,29 @@ public final class WizardryItems { registerItem(registry, "crystal_shard", new Item().setCreativeTab(WizardryTabs.WIZARDRY)); registerItem(registry, "grand_crystal", new Item().setCreativeTab(WizardryTabs.WIZARDRY)); - registerItem(registry, "wizard_handbook", new ItemWizardHandbook(), true); - registerItem(registry, "arcane_tome", new ItemArcaneTome()); + registerItem(registry, "wizard_handbook", new ItemWizardHandbook(), true); + registerItem(registry, "arcane_tome", new ItemArcaneTome()); registerItem(registry, "spell_book", new ItemSpellBook(), true); registerItem(registry, "scroll", new ItemScroll()); registerItem(registry, "magic_wand", new ItemWand(Tier.NOVICE, null)); - registerItem(registry, "apprentice_wand", new ItemWand(Tier.APPRENTICE, null)); + registerItem(registry, "apprentice_wand", new ItemWand(Tier.APPRENTICE, null)); registerItem(registry, "advanced_wand", new ItemWand(Tier.ADVANCED, null)); - registerItem(registry, "master_wand", new ItemWand(Tier.MASTER, null)); + registerItem(registry, "master_wand", new ItemWand(Tier.MASTER, null)); registerItem(registry, "novice_fire_wand", new ItemWand(Tier.NOVICE, Element.FIRE)); registerItem(registry, "apprentice_fire_wand", new ItemWand(Tier.APPRENTICE, Element.FIRE)); registerItem(registry, "advanced_fire_wand", new ItemWand(Tier.ADVANCED, Element.FIRE)); registerItem(registry, "master_fire_wand", new ItemWand(Tier.MASTER, Element.FIRE)); - registerItem(registry, "novice_ice_wand", new ItemWand(Tier.NOVICE, Element.ICE)); - registerItem(registry, "apprentice_ice_wand", new ItemWand(Tier.APPRENTICE, Element.ICE)); + registerItem(registry, "novice_ice_wand", new ItemWand(Tier.NOVICE, Element.ICE)); + registerItem(registry, "apprentice_ice_wand", new ItemWand(Tier.APPRENTICE, Element.ICE)); registerItem(registry, "advanced_ice_wand", new ItemWand(Tier.ADVANCED, Element.ICE)); - registerItem(registry, "master_ice_wand", new ItemWand(Tier.MASTER, Element.ICE)); + registerItem(registry, "master_ice_wand", new ItemWand(Tier.MASTER, Element.ICE)); registerItem(registry, "novice_lightning_wand", new ItemWand(Tier.NOVICE, Element.LIGHTNING)); registerItem(registry, "apprentice_lightning_wand", new ItemWand(Tier.APPRENTICE, Element.LIGHTNING)); - registerItem(registry, "advanced_lightning_wand", new ItemWand(Tier.ADVANCED, Element.LIGHTNING)); + registerItem(registry, "advanced_lightning_wand", new ItemWand(Tier.ADVANCED, Element.LIGHTNING)); registerItem(registry, "master_lightning_wand", new ItemWand(Tier.MASTER, Element.LIGHTNING)); registerItem(registry, "novice_necromancy_wand", new ItemWand(Tier.NOVICE, Element.NECROMANCY)); @@ -439,31 +439,31 @@ public final class WizardryItems { registerItem(registry, "novice_earth_wand", new ItemWand(Tier.NOVICE, Element.EARTH)); registerItem(registry, "apprentice_earth_wand", new ItemWand(Tier.APPRENTICE, Element.EARTH)); - registerItem(registry, "advanced_earth_wand", new ItemWand(Tier.ADVANCED, Element.EARTH)); + registerItem(registry, "advanced_earth_wand", new ItemWand(Tier.ADVANCED, Element.EARTH)); registerItem(registry, "master_earth_wand", new ItemWand(Tier.MASTER, Element.EARTH)); - registerItem(registry, "novice_sorcery_wand", new ItemWand(Tier.NOVICE, Element.SORCERY)); - registerItem(registry, "apprentice_sorcery_wand", new ItemWand(Tier.APPRENTICE, Element.SORCERY)); + registerItem(registry, "novice_sorcery_wand", new ItemWand(Tier.NOVICE, Element.SORCERY)); + registerItem(registry, "apprentice_sorcery_wand", new ItemWand(Tier.APPRENTICE, Element.SORCERY)); registerItem(registry, "advanced_sorcery_wand", new ItemWand(Tier.ADVANCED, Element.SORCERY)); - registerItem(registry, "master_sorcery_wand", new ItemWand(Tier.MASTER, Element.SORCERY)); + registerItem(registry, "master_sorcery_wand", new ItemWand(Tier.MASTER, Element.SORCERY)); - registerItem(registry, "novice_healing_wand", new ItemWand(Tier.NOVICE, Element.HEALING)); - registerItem(registry, "apprentice_healing_wand", new ItemWand(Tier.APPRENTICE, Element.HEALING)); + registerItem(registry, "novice_healing_wand", new ItemWand(Tier.NOVICE, Element.HEALING)); + registerItem(registry, "apprentice_healing_wand", new ItemWand(Tier.APPRENTICE, Element.HEALING)); registerItem(registry, "advanced_healing_wand", new ItemWand(Tier.ADVANCED, Element.HEALING)); - registerItem(registry, "master_healing_wand", new ItemWand(Tier.MASTER, Element.HEALING)); + registerItem(registry, "master_healing_wand", new ItemWand(Tier.MASTER, Element.HEALING)); registerItem(registry, "spectral_sword", new ItemSpectralSword(ToolMaterial.IRON)); registerItem(registry, "spectral_pickaxe", new ItemSpectralPickaxe(ToolMaterial.IRON)); registerItem(registry, "spectral_bow", new ItemSpectralBow()); - registerItem(registry, "blank_scroll", new ItemBlankScroll()); + registerItem(registry, "blank_scroll", new ItemBlankScroll()); registerItem(registry, "magic_silk", new Item().setCreativeTab(WizardryTabs.WIZARDRY)); registerItem(registry, "small_mana_flask", new ItemManaFlask(ItemManaFlask.Size.SMALL)); registerItem(registry, "medium_mana_flask", new ItemManaFlask(ItemManaFlask.Size.MEDIUM)); registerItem(registry, "large_mana_flask", new ItemManaFlask(ItemManaFlask.Size.LARGE)); - registerItem(registry, "storage_upgrade", new ItemWandUpgrade()); + registerItem(registry, "storage_upgrade", new ItemWandUpgrade()); registerItem(registry, "siphon_upgrade", new ItemWandUpgrade()); registerItem(registry, "condenser_upgrade", new ItemWandUpgrade()); registerItem(registry, "range_upgrade", new ItemWandUpgrade()); @@ -473,32 +473,32 @@ public final class WizardryItems { registerItem(registry, "attunement_upgrade", new ItemWandUpgrade()); registerItem(registry, "melee_upgrade", new ItemWandUpgrade()); - registerItem(registry, "flaming_axe", new ItemFlamingAxe(Materials.MAGICAL)); + registerItem(registry, "flaming_axe", new ItemFlamingAxe(Materials.MAGICAL)); registerItem(registry, "frost_axe", new ItemFrostAxe(Materials.MAGICAL)); registerItem(registry, "identification_scroll", new ItemIdentificationScroll()); registerItem(registry, "armour_upgrade", new ItemArmourUpgrade()); registerItem(registry, "astral_diamond", new Item(){ @Override public EnumRarity getRarity(ItemStack stack){ return EnumRarity.RARE; }}.setCreativeTab(WizardryTabs.WIZARDRY)); - registerItem(registry, "purifying_elixir", new ItemPurifyingElixir()); + registerItem(registry, "purifying_elixir", new ItemPurifyingElixir()); registerItem(registry, "firebomb", new ItemFirebomb()); - registerItem(registry, "poison_bomb", new ItemPoisonBomb()); + registerItem(registry, "poison_bomb", new ItemPoisonBomb()); registerItem(registry, "smoke_bomb", new ItemSmokeBomb()); registerItem(registry, "spark_bomb", new ItemSparkBomb()); registerItem(registry, "wizard_hat", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.HEAD, null), true); - registerItem(registry, "wizard_robe", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, null)); - registerItem(registry, "wizard_leggings", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, null)); + registerItem(registry, "wizard_robe", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, null)); + registerItem(registry, "wizard_leggings", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, null)); registerItem(registry, "wizard_boots", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.FEET, null)); - registerItem(registry, "wizard_hat_fire", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.HEAD, Element.FIRE)); + registerItem(registry, "wizard_hat_fire", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.HEAD, Element.FIRE)); registerItem(registry, "wizard_robe_fire", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, Element.FIRE)); registerItem(registry, "wizard_leggings_fire", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, Element.FIRE)); registerItem(registry, "wizard_boots_fire", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.FEET, Element.FIRE)); registerItem(registry, "wizard_hat_ice", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.HEAD, Element.ICE)); - registerItem(registry, "wizard_robe_ice", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, Element.ICE)); - registerItem(registry, "wizard_leggings_ice", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, Element.ICE)); + registerItem(registry, "wizard_robe_ice", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, Element.ICE)); + registerItem(registry, "wizard_leggings_ice", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, Element.ICE)); registerItem(registry, "wizard_boots_ice", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.FEET, Element.ICE)); registerItem(registry, "wizard_hat_lightning", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.HEAD, Element.LIGHTNING)); @@ -509,7 +509,7 @@ public final class WizardryItems { registerItem(registry, "wizard_hat_necromancy", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.HEAD, Element.NECROMANCY)); registerItem(registry, "wizard_robe_necromancy", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, Element.NECROMANCY)); registerItem(registry, "wizard_leggings_necromancy", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, Element.NECROMANCY)); - registerItem(registry, "wizard_boots_necromancy", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.FEET, Element.NECROMANCY)); + registerItem(registry, "wizard_boots_necromancy", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.FEET, Element.NECROMANCY)); registerItem(registry, "wizard_hat_earth", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.HEAD, Element.EARTH)); registerItem(registry, "wizard_robe_earth", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, Element.EARTH)); @@ -517,13 +517,13 @@ public final class WizardryItems { registerItem(registry, "wizard_boots_earth", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.FEET, Element.EARTH)); registerItem(registry, "wizard_hat_sorcery", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.HEAD, Element.SORCERY)); - registerItem(registry, "wizard_robe_sorcery", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, Element.SORCERY)); - registerItem(registry, "wizard_leggings_sorcery", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, Element.SORCERY)); + registerItem(registry, "wizard_robe_sorcery", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, Element.SORCERY)); + registerItem(registry, "wizard_leggings_sorcery", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, Element.SORCERY)); registerItem(registry, "wizard_boots_sorcery", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.FEET, Element.SORCERY)); registerItem(registry, "wizard_hat_healing", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.HEAD, Element.HEALING)); - registerItem(registry, "wizard_robe_healing", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, Element.HEALING)); - registerItem(registry, "wizard_leggings_healing", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, Element.HEALING)); + registerItem(registry, "wizard_robe_healing", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, Element.HEALING)); + registerItem(registry, "wizard_leggings_healing", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, Element.HEALING)); registerItem(registry, "wizard_boots_healing", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.FEET, Element.HEALING)); registerItem(registry, "spectral_helmet", new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.HEAD)); @@ -533,30 +533,30 @@ public final class WizardryItems { registerItem(registry, "lightning_hammer", new ItemLightningHammer()); - registerItem(registry, "ring_condensing", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); + registerItem(registry, "ring_condensing", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); registerItem(registry, "ring_siphoning", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); - registerItem(registry, "ring_battlemage", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); - registerItem(registry, "ring_combustion", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.RING)); - registerItem(registry, "ring_fire_melee", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); - registerItem(registry, "ring_fire_biome", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); - registerItem(registry, "ring_disintegration", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); + registerItem(registry, "ring_battlemage", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); + registerItem(registry, "ring_combustion", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.RING)); + registerItem(registry, "ring_fire_melee", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); + registerItem(registry, "ring_fire_biome", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); + registerItem(registry, "ring_disintegration", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); registerItem(registry, "ring_ice_melee", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); registerItem(registry, "ring_ice_biome", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); registerItem(registry, "ring_arcane_frost", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.RING)); - registerItem(registry, "ring_shattering", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); + registerItem(registry, "ring_shattering", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); registerItem(registry, "ring_lightning_melee", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); registerItem(registry, "ring_storm", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); registerItem(registry, "ring_seeking", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.RING)); - registerItem(registry, "ring_hammer", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.RING)); + registerItem(registry, "ring_hammer", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.RING)); registerItem(registry, "ring_soulbinding", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.RING)); registerItem(registry, "ring_leeching", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); registerItem(registry, "ring_necromancy_melee", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); registerItem(registry, "ring_mind_control", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); - registerItem(registry, "ring_poison", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); + registerItem(registry, "ring_poison", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); registerItem(registry, "ring_earth_melee", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); registerItem(registry, "ring_earth_biome", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); registerItem(registry, "ring_full_moon", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); - registerItem(registry, "ring_extraction", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); + registerItem(registry, "ring_extraction", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING)); registerItem(registry, "ring_mana_return", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.RING)); registerItem(registry, "ring_blockwrangler", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); registerItem(registry, "ring_conjurer", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING)); @@ -569,33 +569,33 @@ public final class WizardryItems { registerItem(registry, "amulet_wisdom", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_fire_protection", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_fire_cloaking", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.AMULET)); - registerItem(registry, "amulet_ice_immunity", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.AMULET)); + registerItem(registry, "amulet_ice_immunity", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_ice_protection", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_potential", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_channeling", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.AMULET)); - registerItem(registry, "amulet_lich", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.AMULET)); + registerItem(registry, "amulet_lich", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_wither_immunity", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_glide", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_banishing", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_anchoring", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.AMULET)); - registerItem(registry, "amulet_recovery", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.AMULET)); + registerItem(registry, "amulet_recovery", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_transience", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.AMULET)); - registerItem(registry, "amulet_resurrection", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.AMULET)); + registerItem(registry, "amulet_resurrection", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.AMULET)); registerItem(registry, "amulet_auto_shield", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.AMULET)); registerItem(registry, "charm_haggler", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_experience_tome", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_auto_smelt", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_lava_walking", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.CHARM)); - registerItem(registry, "charm_storm", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); - registerItem(registry, "charm_minion_health", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.CHARM)); + registerItem(registry, "charm_storm", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); + registerItem(registry, "charm_minion_health", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_minion_variants", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_flight", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_growth", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.CHARM)); - registerItem(registry, "charm_abseiling", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); + registerItem(registry, "charm_abseiling", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_silk_touch", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.CHARM)); - registerItem(registry, "charm_stop_time", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.CHARM)); - registerItem(registry, "charm_light", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); + registerItem(registry, "charm_stop_time", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.CHARM)); + registerItem(registry, "charm_light", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_transportation", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_feeding", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.CHARM)); From d9c3d15b9bb7d673405d48db2c9d6881b4ee6f6c Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 1 Mar 2020 13:39:59 +0000 Subject: [PATCH 003/415] Fix gilded wood models and move to wizardry tab --- src/main/java/electroblob/wizardry/registry/WizardryBlocks.java | 2 +- .../item/{gilded_acacia_wood.json => acacia_gilded_wood.json} | 0 .../item/{gilded_birch_wood.json => birch_gilded_wood.json} | 0 .../{gilded_dark_oak_wood.json => dark_oak_gilded_wood.json} | 0 .../item/{gilded_jungle_wood.json => jungle_gilded_wood.json} | 0 .../models/item/{gilded_oak_wood.json => oak_gilded_wood.json} | 0 .../item/{gilded_spruce_wood.json => spruce_gilded_wood.json} | 0 7 files changed, 1 insertion(+), 1 deletion(-) rename src/main/resources/assets/ebwizardry/models/item/{gilded_acacia_wood.json => acacia_gilded_wood.json} (100%) rename src/main/resources/assets/ebwizardry/models/item/{gilded_birch_wood.json => birch_gilded_wood.json} (100%) rename src/main/resources/assets/ebwizardry/models/item/{gilded_dark_oak_wood.json => dark_oak_gilded_wood.json} (100%) rename src/main/resources/assets/ebwizardry/models/item/{gilded_jungle_wood.json => jungle_gilded_wood.json} (100%) rename src/main/resources/assets/ebwizardry/models/item/{gilded_oak_wood.json => oak_gilded_wood.json} (100%) rename src/main/resources/assets/ebwizardry/models/item/{gilded_spruce_wood.json => spruce_gilded_wood.json} (100%) diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java index c10c4daa..2e4baa68 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java @@ -97,7 +97,7 @@ public final class WizardryBlocks { registerBlock(registry, "thorns", new BlockThorns()); registerBlock(registry, "obsidian_crust", new BlockObsidianCrust()); registerBlock(registry, "dry_frosted_ice", new BlockDryFrostedIce()); - registerBlock(registry, "gilded_wood", new BlockPlanks()); + registerBlock(registry, "gilded_wood", new BlockPlanks().setCreativeTab(WizardryTabs.WIZARDRY)); } diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_acacia_wood.json b/src/main/resources/assets/ebwizardry/models/item/acacia_gilded_wood.json similarity index 100% rename from src/main/resources/assets/ebwizardry/models/item/gilded_acacia_wood.json rename to src/main/resources/assets/ebwizardry/models/item/acacia_gilded_wood.json diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_birch_wood.json b/src/main/resources/assets/ebwizardry/models/item/birch_gilded_wood.json similarity index 100% rename from src/main/resources/assets/ebwizardry/models/item/gilded_birch_wood.json rename to src/main/resources/assets/ebwizardry/models/item/birch_gilded_wood.json diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_dark_oak_wood.json b/src/main/resources/assets/ebwizardry/models/item/dark_oak_gilded_wood.json similarity index 100% rename from src/main/resources/assets/ebwizardry/models/item/gilded_dark_oak_wood.json rename to src/main/resources/assets/ebwizardry/models/item/dark_oak_gilded_wood.json diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_jungle_wood.json b/src/main/resources/assets/ebwizardry/models/item/jungle_gilded_wood.json similarity index 100% rename from src/main/resources/assets/ebwizardry/models/item/gilded_jungle_wood.json rename to src/main/resources/assets/ebwizardry/models/item/jungle_gilded_wood.json diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_oak_wood.json b/src/main/resources/assets/ebwizardry/models/item/oak_gilded_wood.json similarity index 100% rename from src/main/resources/assets/ebwizardry/models/item/gilded_oak_wood.json rename to src/main/resources/assets/ebwizardry/models/item/oak_gilded_wood.json diff --git a/src/main/resources/assets/ebwizardry/models/item/gilded_spruce_wood.json b/src/main/resources/assets/ebwizardry/models/item/spruce_gilded_wood.json similarity index 100% rename from src/main/resources/assets/ebwizardry/models/item/gilded_spruce_wood.json rename to src/main/resources/assets/ebwizardry/models/item/spruce_gilded_wood.json From 004c4c09e30a2c7343c69eead9231af98fdd0560 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 25 Mar 2020 14:29:27 +0000 Subject: [PATCH 004/415] Add bookshelves --- .../wizardry/WizardryGuiHandler.java | 39 ++- .../wizardry/block/BlockBookshelf.java | 147 +++++++++ .../client/gui/GuiArcaneWorkbench.java | 2 +- .../wizardry/client/gui/GuiBookshelf.java | 54 ++++ .../client/model/BakedModelBookshelf.java | 72 +++++ .../wizardry/client/model/ModelBookshelf.java | 53 +++ .../client/model/ModelLoaderBookshelf.java | 28 ++ .../wizardry/client/model/WizardryModels.java | 11 + .../renderer/RenderArcaneWorkbench.java | 2 +- .../wizardry/event/SpellBindEvent.java | 2 +- .../ContainerArcaneWorkbench.java | 4 +- .../inventory/ContainerBookshelf.java | 122 +++++++ .../ContainerPortableWorkbench.java | 2 +- .../SlotItemClassList.java | 2 +- .../SlotItemList.java | 2 +- .../SlotWorkbenchItem.java | 5 +- .../wizardry/packet/PacketControlInput.java | 2 +- .../wizardry/registry/WizardryBlocks.java | 13 + .../wizardry/registry/WizardryItems.java | 7 + .../tileentity/TileEntityArcaneWorkbench.java | 1 + .../tileentity/TileEntityBookshelf.java | 221 +++++++++++++ .../blockstates/acacia_bookshelf.json | 8 + .../blockstates/birch_bookshelf.json | 8 + .../bookshelf_parts/acacia_bookshelf.json | 14 + .../bookshelf_parts/birch_bookshelf.json | 14 + .../blockstates/bookshelf_parts/books0.json | 14 + .../blockstates/bookshelf_parts/books1.json | 14 + .../blockstates/bookshelf_parts/books10.json | 14 + .../blockstates/bookshelf_parts/books11.json | 14 + .../blockstates/bookshelf_parts/books2.json | 14 + .../blockstates/bookshelf_parts/books3.json | 14 + .../blockstates/bookshelf_parts/books4.json | 14 + .../blockstates/bookshelf_parts/books5.json | 14 + .../blockstates/bookshelf_parts/books6.json | 14 + .../blockstates/bookshelf_parts/books7.json | 14 + .../blockstates/bookshelf_parts/books8.json | 14 + .../blockstates/bookshelf_parts/books9.json | 14 + .../bookshelf_parts/dark_oak_bookshelf.json | 14 + .../bookshelf_parts/jungle_bookshelf.json | 14 + .../bookshelf_parts/oak_bookshelf.json | 14 + .../bookshelf_parts/spruce_bookshelf.json | 14 + .../blockstates/dark_oak_bookshelf.json | 8 + .../blockstates/jungle_bookshelf.json | 8 + .../ebwizardry/blockstates/oak_bookshelf.json | 8 + .../blockstates/spruce_bookshelf.json | 8 + .../assets/ebwizardry/lang/en_gb.lang | 13 +- .../assets/ebwizardry/lang/en_us.lang | 13 +- .../models/block/acacia_bookshelf.json | 9 + .../models/block/birch_bookshelf.json | 9 + .../ebwizardry/models/block/books0.json | 33 ++ .../ebwizardry/models/block/books1.json | 33 ++ .../ebwizardry/models/block/books10.json | 33 ++ .../ebwizardry/models/block/books11.json | 33 ++ .../ebwizardry/models/block/books2.json | 33 ++ .../ebwizardry/models/block/books3.json | 33 ++ .../ebwizardry/models/block/books4.json | 33 ++ .../ebwizardry/models/block/books5.json | 33 ++ .../ebwizardry/models/block/books6.json | 33 ++ .../ebwizardry/models/block/books7.json | 33 ++ .../ebwizardry/models/block/books8.json | 33 ++ .../ebwizardry/models/block/books9.json | 33 ++ .../ebwizardry/models/block/bookshelf.json | 301 +----------------- .../models/block/dark_oak_bookshelf.json | 9 + .../models/block/jungle_bookshelf.json | 9 + .../models/block/oak_bookshelf.json | 9 + .../models/block/spruce_bookshelf.json | 9 + .../models/item/acacia_bookshelf.json | 3 + .../models/item/birch_bookshelf.json | 3 + .../models/item/dark_oak_bookshelf.json | 3 + .../models/item/jungle_bookshelf.json | 3 + .../ebwizardry/models/item/oak_bookshelf.json | 3 + .../models/item/spruce_bookshelf.json | 3 + .../blocks/acacia_bookshelf_inside.png | Bin 0 -> 5356 bytes .../textures/blocks/acacia_bookshelf_side.png | Bin 0 -> 5677 bytes .../textures/blocks/acacia_bookshelf_top.png | Bin 0 -> 5625 bytes .../blocks/birch_bookshelf_inside.png | Bin 0 -> 5346 bytes .../textures/blocks/birch_bookshelf_side.png | Bin 0 -> 5657 bytes .../textures/blocks/birch_bookshelf_top.png | Bin 0 -> 5606 bytes .../blocks/dark_oak_bookshelf_inside.png | Bin 0 -> 1234 bytes .../blocks/dark_oak_bookshelf_side.png | Bin 0 -> 1616 bytes .../blocks/dark_oak_bookshelf_top.png | Bin 0 -> 1560 bytes .../blocks/jungle_bookshelf_inside.png | Bin 0 -> 1306 bytes .../textures/blocks/jungle_bookshelf_side.png | Bin 0 -> 1564 bytes .../textures/blocks/jungle_bookshelf_top.png | Bin 0 -> 1561 bytes .../textures/blocks/oak_bookshelf_inside.png | Bin 0 -> 1301 bytes .../textures/blocks/oak_bookshelf_side.png | Bin 0 -> 1654 bytes .../textures/blocks/oak_bookshelf_top.png | Bin 0 -> 1573 bytes .../blocks/spruce_bookshelf_inside.png | Bin 250 -> 1236 bytes .../ebwizardry/textures/gui/bookshelf.png | Bin 0 -> 1009 bytes 89 files changed, 1572 insertions(+), 314 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/block/BlockBookshelf.java create mode 100644 src/main/java/electroblob/wizardry/client/gui/GuiBookshelf.java create mode 100644 src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java create mode 100644 src/main/java/electroblob/wizardry/client/model/ModelBookshelf.java create mode 100644 src/main/java/electroblob/wizardry/client/model/ModelLoaderBookshelf.java rename src/main/java/electroblob/wizardry/{tileentity => inventory}/ContainerArcaneWorkbench.java (98%) create mode 100644 src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java rename src/main/java/electroblob/wizardry/{tileentity => inventory}/ContainerPortableWorkbench.java (93%) rename src/main/java/electroblob/wizardry/{tileentity => inventory}/SlotItemClassList.java (95%) rename src/main/java/electroblob/wizardry/{tileentity => inventory}/SlotItemList.java (95%) rename src/main/java/electroblob/wizardry/{tileentity => inventory}/SlotWorkbenchItem.java (91%) create mode 100644 src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java create mode 100644 src/main/resources/assets/ebwizardry/blockstates/acacia_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/birch_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/acacia_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/birch_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books0.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books1.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books10.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books11.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books2.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books3.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books4.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books5.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books6.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books7.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books8.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books9.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/dark_oak_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/jungle_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/oak_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/spruce_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/dark_oak_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/jungle_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/oak_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/spruce_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/acacia_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/birch_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books0.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books1.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books10.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books11.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books2.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books3.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books4.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books5.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books6.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books7.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books8.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/books9.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/dark_oak_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/jungle_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/oak_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/spruce_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/acacia_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/birch_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/dark_oak_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/jungle_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/oak_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/spruce_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/acacia_bookshelf_inside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/acacia_bookshelf_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/acacia_bookshelf_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/birch_bookshelf_inside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/birch_bookshelf_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/birch_bookshelf_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_bookshelf_inside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_bookshelf_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_bookshelf_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/jungle_bookshelf_inside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/jungle_bookshelf_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/jungle_bookshelf_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/oak_bookshelf_inside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/oak_bookshelf_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/oak_bookshelf_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/gui/bookshelf.png diff --git a/src/main/java/electroblob/wizardry/WizardryGuiHandler.java b/src/main/java/electroblob/wizardry/WizardryGuiHandler.java index 69ffe1ab..69aa420f 100644 --- a/src/main/java/electroblob/wizardry/WizardryGuiHandler.java +++ b/src/main/java/electroblob/wizardry/WizardryGuiHandler.java @@ -1,10 +1,12 @@ package electroblob.wizardry; +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; +import electroblob.wizardry.inventory.ContainerBookshelf; +import electroblob.wizardry.inventory.ContainerPortableWorkbench; import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemWizardHandbook; -import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; -import electroblob.wizardry.tileentity.ContainerPortableWorkbench; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import electroblob.wizardry.tileentity.TileEntityBookshelf; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; @@ -20,40 +22,73 @@ public class WizardryGuiHandler implements IGuiHandler { public static final int ARCANE_WORKBENCH = nextGuiId++; public static final int WIZARD_HANDBOOK = nextGuiId++; public static final int PORTABLE_CRAFTING = nextGuiId++; + public static final int BOOKSHELF = nextGuiId++; @Override public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z){ + if(id == ARCANE_WORKBENCH){ + TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); + if(tileEntity instanceof TileEntityArcaneWorkbench){ return new ContainerArcaneWorkbench(player.inventory, (TileEntityArcaneWorkbench)tileEntity); } + }else if(id == PORTABLE_CRAFTING){ return new ContainerPortableWorkbench(player.inventory, world, new BlockPos(x, y, z)); + + }else if(id == BOOKSHELF){ + + TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); + + if(tileEntity instanceof TileEntityBookshelf){ + return new ContainerBookshelf(player.inventory, (TileEntityBookshelf)tileEntity); + } } + return null; } @Override public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z){ + if(id == ARCANE_WORKBENCH){ + TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); + if(tileEntity instanceof TileEntityArcaneWorkbench){ return new electroblob.wizardry.client.gui.GuiArcaneWorkbench(player.inventory, (TileEntityArcaneWorkbench)tileEntity); } + }else if(id == WIZARD_HANDBOOK && (player.getHeldItemMainhand().getItem() instanceof ItemWizardHandbook || player.getHeldItemOffhand().getItem() instanceof ItemWizardHandbook)){ + return new electroblob.wizardry.client.gui.handbook.GuiWizardHandbook(); + }else if(id == SPELL_BOOK){ + if(player.getHeldItemMainhand().getItem() instanceof ItemSpellBook){ return new electroblob.wizardry.client.gui.GuiSpellBook(player.getHeldItemMainhand()); }else if(player.getHeldItemOffhand().getItem() instanceof ItemSpellBook){ return new electroblob.wizardry.client.gui.GuiSpellBook(player.getHeldItemOffhand()); } + }else if(id == PORTABLE_CRAFTING){ return new electroblob.wizardry.client.gui.GuiPortableCrafting(player.inventory, world, new BlockPos(x, y, z)); + + }else if(id == BOOKSHELF){ + + TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); + + if(tileEntity instanceof TileEntityBookshelf){ + return new electroblob.wizardry.client.gui.GuiBookshelf(player.inventory, + (TileEntityBookshelf)tileEntity); + } + } + return null; } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java new file mode 100644 index 00000000..b4e23861 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java @@ -0,0 +1,147 @@ +package electroblob.wizardry.block; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryGuiHandler; +import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.tileentity.TileEntityBookshelf; +import net.minecraft.block.BlockHorizontal; +import net.minecraft.block.ITileEntityProvider; +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.PropertyBool; +import net.minecraft.block.state.BlockFaceShape; +import net.minecraft.block.state.BlockStateContainer; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.InventoryHelper; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.property.IExtendedBlockState; +import net.minecraftforge.common.property.Properties; + +import javax.annotation.Nullable; + +public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvider { + + public static final int SLOT_COUNT = 12; + + public static final UnlistedPropertyBool[] BOOKS = new UnlistedPropertyBool[SLOT_COUNT]; + + static { + for(int i=0; i[] properties = { FACING }; +// return new BlockStateContainer(this, ArrayUtils.addAll(properties, BOOKS)); + return new BlockStateContainer.Builder(this).add(FACING).add(BOOKS).build(); + } + + @Override + public boolean isOpaqueCube(IBlockState state){ + return false; + } + + @Override + public boolean isFullCube(IBlockState state){ + return false; + } + + @Override + public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){ + return state.getValue(FACING).getAxis() == face.getAxis() ? BlockFaceShape.UNDEFINED : BlockFaceShape.SOLID; + } + + @Override + public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer){ + return this.getDefaultState().withProperty(FACING, placer.getHorizontalFacing().getOpposite()); + } + + @Override + public void breakBlock(World world, BlockPos pos, IBlockState block){ + + TileEntity tileentity = world.getTileEntity(pos); + + if(tileentity instanceof TileEntityBookshelf){ + InventoryHelper.dropInventoryItems(world, pos, (TileEntityBookshelf)tileentity); + } + + super.breakBlock(world, pos, block); // For blocks that don't extend BlockContainer, this removes the TE + } + + @Override + public IBlockState getStateFromMeta(int meta){ + EnumFacing enumfacing = EnumFacing.byIndex(meta); + if(enumfacing.getAxis() == EnumFacing.Axis.Y) enumfacing = EnumFacing.NORTH; + return this.getDefaultState().withProperty(FACING, enumfacing); + } + + @Override + public int getMetaFromState(IBlockState state){ + return state.getValue(FACING).getIndex(); + } + + @Override + public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos){ + + 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()); + } + } + + return s; + } + + @Nullable + @Override + public TileEntity createNewTileEntity(World world, int meta){ + return new TileEntityBookshelf(); + } + + @Override + public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, EnumHand hand, + EnumFacing side, float hitX, float hitY, float hitZ){ + + TileEntity tileEntity = world.getTileEntity(pos); + + if(tileEntity == null || player.isSneaking()){ + return false; + } + + player.openGui(Wizardry.instance, WizardryGuiHandler.BOOKSHELF, world, pos.getX(), pos.getY(), pos.getZ()); + return true; + } + + @Override + public boolean eventReceived(IBlockState state, World world, BlockPos pos, int id, int param){ + super.eventReceived(state, world, pos, id, param); + TileEntity tileentity = world.getTileEntity(pos); + 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 { + + public UnlistedPropertyBool(String name){ + super(PropertyBool.create(name)); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index b4cf57a4..7d8be0a3 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -14,7 +14,7 @@ import electroblob.wizardry.packet.PacketControlInput; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import electroblob.wizardry.util.WandHelper; import net.minecraft.client.Minecraft; diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiBookshelf.java b/src/main/java/electroblob/wizardry/client/gui/GuiBookshelf.java new file mode 100644 index 00000000..67091424 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/gui/GuiBookshelf.java @@ -0,0 +1,54 @@ +package electroblob.wizardry.client.gui; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.inventory.ContainerBookshelf; +import electroblob.wizardry.tileentity.TileEntityBookshelf; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class GuiBookshelf extends GuiContainer { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/bookshelf.png"); + /** The player inventory bound to this GUI. */ + private final InventoryPlayer playerInventory; + /** The inventory contained within the corresponding bookshelf. */ + public IInventory bookshelfInventory; + + public GuiBookshelf(InventoryPlayer playerInv, TileEntityBookshelf bookshelfInv){ + super(new ContainerBookshelf(playerInv, bookshelfInv)); + this.playerInventory = playerInv; + this.bookshelfInventory = bookshelfInv; + this.allowUserInput = false; + this.ySize = 148; + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks){ + this.drawDefaultBackground(); + super.drawScreen(mouseX, mouseY, partialTicks); + this.renderHoveredToolTip(mouseX, mouseY); + } + + @Override + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){ + String s = this.bookshelfInventory.getDisplayName().getUnformattedText(); + this.fontRenderer.drawString(s, 8, 6, 4210752); + this.fontRenderer.drawString(this.playerInventory.getDisplayName().getUnformattedText(), 8, this.ySize - 96 + 2, 4210752); + } + + @Override + protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY){ + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + this.mc.getTextureManager().bindTexture(TEXTURE); + int i = (this.width - this.xSize) / 2; + int j = (this.height - this.ySize) / 2; + this.drawTexturedModalRect(i, j, 0, 0, this.xSize, this.ySize); + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java b/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java new file mode 100644 index 00000000..4e682688 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java @@ -0,0 +1,72 @@ +package electroblob.wizardry.client.model; + +import electroblob.wizardry.block.BlockBookshelf; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.client.renderer.block.model.ItemOverrideList; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.property.IExtendedBlockState; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class BakedModelBookshelf implements IBakedModel { + + private final IBakedModel bookshelf; + private final IBakedModel[] books; + + public BakedModelBookshelf(IBakedModel bookshelf, IBakedModel... books){ + this.bookshelf = bookshelf; + this.books = books; + } + + @Override + public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand){ + + IBakedModel fallback = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelManager().getMissingModel(); + if(state == null) return fallback.getQuads(null, side, rand); + + // It took me waaay too long to realise I needed a new list here + List quads = new ArrayList<>(bookshelf.getQuads(state, side, rand)); // Lists ain't immutable, chief + + IExtendedBlockState extendedState = (IExtendedBlockState)state; + + for(int i = 0; i bakedTextureGetter){ + + try { + + IModel bookshelfModel = ModelLoaderRegistry.getModel(bookshelfModelLocation); + // 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]; + + for(int i = 0; i < BlockBookshelf.SLOT_COUNT; i++){ + IModel bookModel = ModelLoaderRegistry.getModel(new ModelResourceLocation(new ResourceLocation(Wizardry.MODID, "bookshelf_parts/books" + i), variant)); + books[i] = bookModel.bake(bookModel.getDefaultState(), format, bakedTextureGetter); // Same here! + } + + return new BakedModelBookshelf(bookshelf, books); + + }catch(Exception exception){ + Wizardry.logger.error("Error baking bookshelf models: ", exception); + return ModelLoaderRegistry.getMissingModel().bake(state, format, bakedTextureGetter); + } + + } + +} diff --git a/src/main/java/electroblob/wizardry/client/model/ModelLoaderBookshelf.java b/src/main/java/electroblob/wizardry/client/model/ModelLoaderBookshelf.java new file mode 100644 index 00000000..0c7dcb79 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelLoaderBookshelf.java @@ -0,0 +1,28 @@ +package electroblob.wizardry.client.model; + +import electroblob.wizardry.Wizardry; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.ICustomModelLoader; +import net.minecraftforge.client.model.IModel; + +public class ModelLoaderBookshelf implements ICustomModelLoader { + + private static final String SMART_MODEL_PREFIX = "models/block/smart_model/"; + + @Override + public void onResourceManagerReload(IResourceManager resourceManager){ + // Do nothing + } + + @Override + public boolean accepts(ResourceLocation modelLocation){ + return modelLocation.getNamespace().equals(Wizardry.MODID) && modelLocation.getPath().startsWith(SMART_MODEL_PREFIX); + } + + @Override + public IModel loadModel(ResourceLocation modelLocation){ + return new ModelBookshelf(modelLocation.getPath().substring(SMART_MODEL_PREFIX.length())); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java index 2363eb1b..f2e2e6c3 100644 --- a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java +++ b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java @@ -21,6 +21,7 @@ import net.minecraft.util.NonNullList; import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.client.event.ModelRegistryEvent; import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.relauncher.Side; @@ -69,6 +70,16 @@ public final class WizardryModels { ItemBlockMultiTextured gildedWoodItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.gilded_wood); registerMultiTexturedModel(gildedWoodItem); + // Explanation for all this here -> https://github.com/TheGreyGhost/MinecraftByExample/tree/master/src/main/java/minecraftbyexample/mbe05_block_dynamic_block_model2 + ModelLoaderRegistry.registerLoader(new ModelLoaderBookshelf()); + + registerItemModel(Item.getItemFromBlock(WizardryBlocks.oak_bookshelf)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.spruce_bookshelf)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.birch_bookshelf)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.jungle_bookshelf)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.acacia_bookshelf)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.dark_oak_bookshelf)); + // Items registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal); diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java index ae9751ac..2f645301 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java @@ -1,7 +1,7 @@ package electroblob.wizardry.client.renderer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BufferBuilder; diff --git a/src/main/java/electroblob/wizardry/event/SpellBindEvent.java b/src/main/java/electroblob/wizardry/event/SpellBindEvent.java index 2e2de430..08b1d2b5 100644 --- a/src/main/java/electroblob/wizardry/event/SpellBindEvent.java +++ b/src/main/java/electroblob/wizardry/event/SpellBindEvent.java @@ -1,6 +1,6 @@ package electroblob.wizardry.event; -import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; import net.minecraft.entity.player.EntityPlayer; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerContainerEvent; diff --git a/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java similarity index 98% rename from src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java rename to src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index 7582ba0e..be8297c6 100644 --- a/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -1,4 +1,4 @@ -package electroblob.wizardry.tileentity; +package electroblob.wizardry.inventory; import electroblob.wizardry.Wizardry; import electroblob.wizardry.event.SpellBindEvent; @@ -6,6 +6,7 @@ import electroblob.wizardry.item.IWorkbenchItem; import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.registry.WizardryAdvancementTriggers; import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import electroblob.wizardry.util.WandHelper; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -161,6 +162,7 @@ public class ContainerArcaneWorkbench extends Container { // FIXME: It only seems to be syncing correctly when a stack is put into the slot, not taken out. // This is because markDirty isn't called in the tileentity, I think. + // You can simulate this using hoppers! this.tileentity.sync(); } diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java b/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java new file mode 100644 index 00000000..abe711df --- /dev/null +++ b/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java @@ -0,0 +1,122 @@ +package electroblob.wizardry.inventory; + +import electroblob.wizardry.block.BlockBookshelf; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.tileentity.TileEntityBookshelf; +import net.minecraft.entity.player.EntityPlayer; +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; + +public class ContainerBookshelf extends Container { + + /** The bookshelf tile entity associated with this container. */ + public TileEntityBookshelf tileentity; + + public ContainerBookshelf(IInventory inventory, TileEntityBookshelf tileentity){ + + this.tileentity = tileentity; + + 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)); + } + } + + for(int x = 0; x < 9; x++){ + this.addSlotToContainer(new Slot(inventory, x, 8 + x * 18, 124)); + } + + for(int y = 0; y < 3; y++){ + for(int x = 0; x < 9; x++){ + this.addSlotToContainer(new Slot(inventory, 9 + x + y * 9, 8 + x * 18, 66 + y * 18)); + } + } + + this.onSlotChanged(); + + } + + /** Called from individual slots when their item is changed or removed. */ + public void onSlotChanged(){ + this.tileentity.sync(); + } + + @Override + public boolean canInteractWith(EntityPlayer player){ + return this.tileentity.isUsableByPlayer(player); + } + + @Override + public ItemStack transferStackInSlot(EntityPlayer player, int clickedSlotId){ + + ItemStack remainder = ItemStack.EMPTY; + Slot slot = this.inventorySlots.get(clickedSlotId); + + if(slot != null && slot.getHasStack()){ + + ItemStack stack = slot.getStack(); // The stack that was there originally + remainder = stack.copy(); // A copy of that stack + + // Bookshelf -> inventory + if(clickedSlotId < BlockBookshelf.SLOT_COUNT){ + // Tries to move the stack into the player's inventory. If this fails... + if(!this.mergeItemStack(stack, BlockBookshelf.SLOT_COUNT, this.inventorySlots.size(), true)){ + return ItemStack.EMPTY; // ...nothing else happens. + } + } + // Inventory -> bookshelf + else{ + + int minSlotId = 0; + int maxSlotId = BlockBookshelf.SLOT_COUNT - 1; + + if(!(stack.getItem() instanceof ItemSpellBook)) return ItemStack.EMPTY; + + if(!this.mergeItemStack(stack, minSlotId, maxSlotId + 1, false)){ + return ItemStack.EMPTY; + } + } + + if(stack.getCount() == 0){ + slot.putStack(ItemStack.EMPTY); + }else{ + slot.onSlotChanged(); + } + + if(stack.getCount() == remainder.getCount()){ + return ItemStack.EMPTY; + } + + slot.onTake(player, stack); + } + + return remainder; + } + + public class SlotBookshelf extends SlotItemClassList { + + @SafeVarargs + public SlotBookshelf(IInventory inventory, int index, int x, int y, int stackLimit, Class... allowedItemClasses){ + super(inventory, index, x, y, stackLimit, allowedItemClasses); + } + + @Override + public void putStack(ItemStack stack){ + boolean statusChanged = this.getStack().isEmpty() != stack.isEmpty(); + super.putStack(stack); + if(statusChanged) ContainerBookshelf.this.onSlotChanged(); + } + + @Override + public ItemStack onTake(EntityPlayer player, ItemStack stack){ + ItemStack result = super.onTake(player, stack); + ContainerBookshelf.this.onSlotChanged(); + return result; + } + + } + +} diff --git a/src/main/java/electroblob/wizardry/tileentity/ContainerPortableWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerPortableWorkbench.java similarity index 93% rename from src/main/java/electroblob/wizardry/tileentity/ContainerPortableWorkbench.java rename to src/main/java/electroblob/wizardry/inventory/ContainerPortableWorkbench.java index 92b6f3c6..2111f1f7 100644 --- a/src/main/java/electroblob/wizardry/tileentity/ContainerPortableWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerPortableWorkbench.java @@ -1,4 +1,4 @@ -package electroblob.wizardry.tileentity; +package electroblob.wizardry.inventory; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.InventoryPlayer; diff --git a/src/main/java/electroblob/wizardry/tileentity/SlotItemClassList.java b/src/main/java/electroblob/wizardry/inventory/SlotItemClassList.java similarity index 95% rename from src/main/java/electroblob/wizardry/tileentity/SlotItemClassList.java rename to src/main/java/electroblob/wizardry/inventory/SlotItemClassList.java index 783249d9..bbdbd1b7 100644 --- a/src/main/java/electroblob/wizardry/tileentity/SlotItemClassList.java +++ b/src/main/java/electroblob/wizardry/inventory/SlotItemClassList.java @@ -1,4 +1,4 @@ -package electroblob.wizardry.tileentity; +package electroblob.wizardry.inventory; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.Slot; diff --git a/src/main/java/electroblob/wizardry/tileentity/SlotItemList.java b/src/main/java/electroblob/wizardry/inventory/SlotItemList.java similarity index 95% rename from src/main/java/electroblob/wizardry/tileentity/SlotItemList.java rename to src/main/java/electroblob/wizardry/inventory/SlotItemList.java index db95b5e0..bf1a82a9 100644 --- a/src/main/java/electroblob/wizardry/tileentity/SlotItemList.java +++ b/src/main/java/electroblob/wizardry/inventory/SlotItemList.java @@ -1,4 +1,4 @@ -package electroblob.wizardry.tileentity; +package electroblob.wizardry.inventory; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.Slot; diff --git a/src/main/java/electroblob/wizardry/tileentity/SlotWorkbenchItem.java b/src/main/java/electroblob/wizardry/inventory/SlotWorkbenchItem.java similarity index 91% rename from src/main/java/electroblob/wizardry/tileentity/SlotWorkbenchItem.java rename to src/main/java/electroblob/wizardry/inventory/SlotWorkbenchItem.java index 0e0a2f7d..83eb3659 100644 --- a/src/main/java/electroblob/wizardry/tileentity/SlotWorkbenchItem.java +++ b/src/main/java/electroblob/wizardry/inventory/SlotWorkbenchItem.java @@ -1,4 +1,4 @@ -package electroblob.wizardry.tileentity; +package electroblob.wizardry.inventory; import electroblob.wizardry.item.IWorkbenchItem; import net.minecraft.entity.player.EntityPlayer; @@ -29,8 +29,9 @@ public class SlotWorkbenchItem extends Slot { @Override public ItemStack onTake(EntityPlayer player, ItemStack stack){ + ItemStack result = super.onTake(player, stack); this.container.onSlotChanged(slotNumber, ItemStack.EMPTY, player); - return super.onTake(player, stack); + return result; } @Override diff --git a/src/main/java/electroblob/wizardry/packet/PacketControlInput.java b/src/main/java/electroblob/wizardry/packet/PacketControlInput.java index f2a43d39..837a0baf 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketControlInput.java +++ b/src/main/java/electroblob/wizardry/packet/PacketControlInput.java @@ -6,7 +6,7 @@ import electroblob.wizardry.packet.PacketControlInput.Message; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.spell.Possession; import electroblob.wizardry.spell.Resurrection; -import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import io.netty.buffer.ByteBuf; diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java index 2e4baa68..ed25dfd4 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java @@ -58,6 +58,12 @@ public final class WizardryBlocks { public static final Block obsidian_crust = placeholder(); public static final Block dry_frosted_ice = placeholder(); public static final Block gilded_wood = placeholder(); + public static final Block oak_bookshelf = placeholder(); + public static final Block spruce_bookshelf = placeholder(); + public static final Block birch_bookshelf = placeholder(); + public static final Block jungle_bookshelf = placeholder(); + public static final Block acacia_bookshelf = placeholder(); + public static final Block dark_oak_bookshelf = placeholder(); /** * Sets both the registry and unlocalised names of the given block, then registers it with the given registry. Use @@ -98,6 +104,12 @@ public final class WizardryBlocks { registerBlock(registry, "obsidian_crust", new BlockObsidianCrust()); registerBlock(registry, "dry_frosted_ice", new BlockDryFrostedIce()); registerBlock(registry, "gilded_wood", new BlockPlanks().setCreativeTab(WizardryTabs.WIZARDRY)); + registerBlock(registry, "oak_bookshelf", new BlockBookshelf()); + registerBlock(registry, "spruce_bookshelf", new BlockBookshelf()); + registerBlock(registry, "birch_bookshelf", new BlockBookshelf()); + registerBlock(registry, "jungle_bookshelf", new BlockBookshelf()); + registerBlock(registry, "acacia_bookshelf", new BlockBookshelf()); + registerBlock(registry, "dark_oak_bookshelf", new BlockBookshelf()); } @@ -111,5 +123,6 @@ public final class WizardryBlocks { GameRegistry.registerTileEntity(TileEntityPlayerSave.class, new ResourceLocation(Wizardry.MODID, "player_save")); GameRegistry.registerTileEntity(TileEntityPlayerSaveTimed.class, new ResourceLocation(Wizardry.MODID, "player_save_timed")); GameRegistry.registerTileEntity(TileEntityShrineCore.class, new ResourceLocation(Wizardry.MODID, "shrine_core")); + GameRegistry.registerTileEntity(TileEntityBookshelf.class, new ResourceLocation(Wizardry.MODID, "bookshelf")); } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryItems.java b/src/main/java/electroblob/wizardry/registry/WizardryItems.java index 10ae105c..b5c52052 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryItems.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryItems.java @@ -400,6 +400,13 @@ public final class WizardryItems { registerMultiTexturedItemBlock(registry, WizardryBlocks.runestone_pedestal, false, elements); registerMultiTexturedItemBlock(registry, WizardryBlocks.gilded_wood, true, woodTypes); + registerItemBlock(registry, WizardryBlocks.oak_bookshelf); + registerItemBlock(registry, WizardryBlocks.spruce_bookshelf); + registerItemBlock(registry, WizardryBlocks.birch_bookshelf); + registerItemBlock(registry, WizardryBlocks.jungle_bookshelf); + registerItemBlock(registry, WizardryBlocks.acacia_bookshelf); + registerItemBlock(registry, WizardryBlocks.dark_oak_bookshelf); + // Items registerItem(registry, "magic_crystal", new ItemCrystal()); diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java index cfc6dc09..37c4bf4c 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java @@ -1,6 +1,7 @@ package electroblob.wizardry.tileentity; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; import electroblob.wizardry.item.IManaStoringItem; import electroblob.wizardry.item.IWorkbenchItem; import electroblob.wizardry.item.ItemCrystal; diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java new file mode 100644 index 00000000..ce513635 --- /dev/null +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java @@ -0,0 +1,221 @@ +package electroblob.wizardry.tileentity; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.block.BlockBookshelf; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.util.NBTExtras; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.play.server.SPacketUpdateTileEntity; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ITickable; +import net.minecraft.util.NonNullList; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraftforge.common.util.Constants.NBT; + +public class TileEntityBookshelf extends TileEntity implements IInventory, ITickable { + + /** The inventory of the bookshelf. */ + private NonNullList inventory; + + public TileEntityBookshelf(){ + inventory = NonNullList.withSize(BlockBookshelf.SLOT_COUNT, ItemStack.EMPTY); + } + + /** Called to manually sync the tile entity with clients. */ + public void sync(){ + this.world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); + } + + @Override + public void update(){ + // Nothing here for now + } + + @Override + public int getSizeInventory(){ + return inventory.size(); + } + + @Override + public ItemStack getStackInSlot(int slot){ + return inventory.get(slot); + } + + @Override + public ItemStack decrStackSize(int slot, int amount){ + + ItemStack stack = getStackInSlot(slot); + + if(!stack.isEmpty()){ + if(stack.getCount() <= amount){ + setInventorySlotContents(slot, ItemStack.EMPTY); + }else{ + stack = stack.splitStack(amount); + if(stack.getCount() == 0){ + setInventorySlotContents(slot, ItemStack.EMPTY); + } + } + this.markDirty(); + } + + return stack; + } + + @Override + public ItemStack removeStackFromSlot(int slot){ + + ItemStack stack = getStackInSlot(slot); + + if(!stack.isEmpty()){ + setInventorySlotContents(slot, ItemStack.EMPTY); + } + + return stack; + } + + @Override + public void setInventorySlotContents(int slot, ItemStack stack){ + + inventory.set(slot, stack); + + //ItemStack previous = inventory.set(slot, stack); + + //if(previous.isEmpty() != stack.isEmpty()) this.sync(); + + if(!stack.isEmpty() && stack.getCount() > getInventoryStackLimit()){ + stack.setCount(getInventoryStackLimit()); + } + } + + @Override + public String getName(){ + return "container." + Wizardry.MODID + ":bookshelf"; + } + + @Override + public boolean hasCustomName(){ + return false; + } + + @Override + public ITextComponent getDisplayName(){ + return this.hasCustomName() ? new TextComponentString(this.getName()) : new TextComponentTranslation(this.getName()); + } + + @Override + public int getInventoryStackLimit(){ + return 64; + } + + @Override + public boolean isUsableByPlayer(EntityPlayer player){ + return world.getTileEntity(pos) == this && player.getDistanceSqToCenter(pos) < 64; + } + + @Override + public void openInventory(EntityPlayer player){ + + } + + @Override + public void closeInventory(EntityPlayer player){ + + } + + @Override + public boolean isItemValidForSlot(int slotNumber, ItemStack itemstack){ + return itemstack.isEmpty() || itemstack.getItem() instanceof ItemSpellBook; // TODO: Add a whitelist + } + + @Override + public void readFromNBT(NBTTagCompound tagCompound){ + + super.readFromNBT(tagCompound); + + NBTTagList tagList = tagCompound.getTagList("Inventory", NBT.TAG_COMPOUND); + for(int i = 0; i < tagList.tagCount(); i++){ + NBTTagCompound tag = tagList.getCompoundTagAt(i); + byte slot = tag.getByte("Slot"); + if(slot >= 0 && slot < getSizeInventory()){ + setInventorySlotContents(slot, new ItemStack(tag)); + } + } + } + + @Override + public NBTTagCompound writeToNBT(NBTTagCompound tagCompound){ + + super.writeToNBT(tagCompound); + + NBTTagList itemList = new NBTTagList(); + for(int i = 0; i < getSizeInventory(); i++){ + ItemStack stack = getStackInSlot(i); + if(!stack.isEmpty()){ + NBTTagCompound tag = new NBTTagCompound(); + tag.setByte("Slot", (byte)i); + stack.writeToNBT(tag); + itemList.appendTag(tag); + } + } + + NBTExtras.storeTagSafely(tagCompound, "Inventory", itemList); + return tagCompound; + } + + @Override + public final NBTTagCompound getUpdateTag(){ + return this.writeToNBT(new NBTTagCompound()); + } + + @Override + public SPacketUpdateTileEntity getUpdatePacket(){ + return new SPacketUpdateTileEntity(pos, getBlockMetadata(), this.getUpdateTag()); + } + + @Override + public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt){ + readFromNBT(pkt.getNbtCompound()); + } + + // What are all these for? + + @Override + public int getField(int id){ + return 0; + } + + @Override + public void setField(int id, int value){ + + } + + @Override + public int getFieldCount(){ + return 0; + } + + @Override + public void clear(){ + for(int i = 0; i < getSizeInventory(); i++){ + setInventorySlotContents(i, ItemStack.EMPTY); + } + } + + @Override + public boolean isEmpty(){ + for(int i = 0; i < getSizeInventory(); i++){ + if(!getStackInSlot(i).isEmpty()){ + return false; + } + } + return true; + } + +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/acacia_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/acacia_bookshelf.json new file mode 100644 index 00000000..4aa96488 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/acacia_bookshelf.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "ebwizardry:smart_model/acacia_bookshelf#facing=north" }, + "facing=east": { "model": "ebwizardry:smart_model/acacia_bookshelf#facing=east" }, + "facing=south": { "model": "ebwizardry:smart_model/acacia_bookshelf#facing=south" }, + "facing=west": { "model": "ebwizardry:smart_model/acacia_bookshelf#facing=west" } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/birch_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/birch_bookshelf.json new file mode 100644 index 00000000..656889cc --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/birch_bookshelf.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "ebwizardry:smart_model/birch_bookshelf#facing=north" }, + "facing=east": { "model": "ebwizardry:smart_model/birch_bookshelf#facing=east" }, + "facing=south": { "model": "ebwizardry:smart_model/birch_bookshelf#facing=south" }, + "facing=west": { "model": "ebwizardry:smart_model/birch_bookshelf#facing=west" } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/acacia_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/acacia_bookshelf.json new file mode 100644 index 00000000..c517daf9 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/acacia_bookshelf.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:acacia_bookshelf" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/birch_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/birch_bookshelf.json new file mode 100644 index 00000000..f86ffd00 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/birch_bookshelf.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:birch_bookshelf" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books0.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books0.json new file mode 100644 index 00000000..690e777d --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books0.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books0" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books1.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books1.json new file mode 100644 index 00000000..f0bf298d --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books1.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books1" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books10.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books10.json new file mode 100644 index 00000000..d0c3b676 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books10.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books10" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books11.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books11.json new file mode 100644 index 00000000..f0564d0e --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books11.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books11" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books2.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books2.json new file mode 100644 index 00000000..c6fbbb2d --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books2.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books2" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books3.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books3.json new file mode 100644 index 00000000..fe640e1a --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books3.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books3" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books4.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books4.json new file mode 100644 index 00000000..8f5f64c3 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books4.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books4" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books5.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books5.json new file mode 100644 index 00000000..8cbd4b8b --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books5.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books5" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books6.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books6.json new file mode 100644 index 00000000..23b29036 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books6.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books6" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books7.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books7.json new file mode 100644 index 00000000..e9cba50e --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books7.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books7" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books8.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books8.json new file mode 100644 index 00000000..981fdc32 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books8.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books8" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books9.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books9.json new file mode 100644 index 00000000..46bbd413 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/books9.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:books9" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/dark_oak_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/dark_oak_bookshelf.json new file mode 100644 index 00000000..bfb7e91e --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/dark_oak_bookshelf.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:dark_oak_bookshelf" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/jungle_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/jungle_bookshelf.json new file mode 100644 index 00000000..9a9bd237 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/jungle_bookshelf.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:jungle_bookshelf" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/oak_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/oak_bookshelf.json new file mode 100644 index 00000000..d13b08c1 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/oak_bookshelf.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:oak_bookshelf" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/spruce_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/spruce_bookshelf.json new file mode 100644 index 00000000..66cf20b1 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/bookshelf_parts/spruce_bookshelf.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:spruce_bookshelf" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/dark_oak_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/dark_oak_bookshelf.json new file mode 100644 index 00000000..8a838825 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/dark_oak_bookshelf.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "ebwizardry:smart_model/dark_oak_bookshelf#facing=north" }, + "facing=east": { "model": "ebwizardry:smart_model/dark_oak_bookshelf#facing=east" }, + "facing=south": { "model": "ebwizardry:smart_model/dark_oak_bookshelf#facing=south" }, + "facing=west": { "model": "ebwizardry:smart_model/dark_oak_bookshelf#facing=west" } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/jungle_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/jungle_bookshelf.json new file mode 100644 index 00000000..105b170f --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/jungle_bookshelf.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "ebwizardry:smart_model/jungle_bookshelf#facing=north" }, + "facing=east": { "model": "ebwizardry:smart_model/jungle_bookshelf#facing=east" }, + "facing=south": { "model": "ebwizardry:smart_model/jungle_bookshelf#facing=south" }, + "facing=west": { "model": "ebwizardry:smart_model/jungle_bookshelf#facing=west" } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/oak_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/oak_bookshelf.json new file mode 100644 index 00000000..dca53b64 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/oak_bookshelf.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "ebwizardry:smart_model/oak_bookshelf#facing=north" }, + "facing=east": { "model": "ebwizardry:smart_model/oak_bookshelf#facing=east" }, + "facing=south": { "model": "ebwizardry:smart_model/oak_bookshelf#facing=south" }, + "facing=west": { "model": "ebwizardry:smart_model/oak_bookshelf#facing=west" } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/spruce_bookshelf.json b/src/main/resources/assets/ebwizardry/blockstates/spruce_bookshelf.json new file mode 100644 index 00000000..3e52fe5b --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/spruce_bookshelf.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "ebwizardry:smart_model/spruce_bookshelf#facing=north" }, + "facing=east": { "model": "ebwizardry:smart_model/spruce_bookshelf#facing=east" }, + "facing=south": { "model": "ebwizardry:smart_model/spruce_bookshelf#facing=south" }, + "facing=west": { "model": "ebwizardry:smart_model/spruce_bookshelf#facing=west" } + } +} diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 05ac7ba8..8927fb28 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -1,4 +1,4 @@ -#PARSE_ESCAPES +# PARSE_ESCAPES tile.ebwizardry\:arcane_workbench.name=Arcane Workbench tile.ebwizardry\:crystal_ore.name=Crystal Ore tile.ebwizardry\:petrified_stone.name=Petrified Stone @@ -30,8 +30,15 @@ tile.ebwizardry\:oak_gilded_wood.name=Gilded Oak Wood tile.ebwizardry\:spruce_gilded_wood.name=Gilded Spruce Wood tile.ebwizardry\:birch_gilded_wood.name=Gilded Birch Wood tile.ebwizardry\:jungle_gilded_wood.name=Gilded Jungle Wood -tile.ebwizardry\:dark_oak_gilded_wood.name=Gilded Dark Oak Wood tile.ebwizardry\:acacia_gilded_wood.name=Gilded Acacia Wood +tile.ebwizardry\:dark_oak_gilded_wood.name=Gilded Dark Oak Wood + +tile.ebwizardry\:oak_bookshelf.name=Oak Bookshelf +tile.ebwizardry\:spruce_bookshelf.name=Spruce Bookshelf +tile.ebwizardry\:birch_bookshelf.name=Birch Bookshelf +tile.ebwizardry\:jungle_bookshelf.name=Jungle Bookshelf +tile.ebwizardry\:acacia_bookshelf.name=Acacia Bookshelf +tile.ebwizardry\:dark_oak_bookshelf.name=Dark Oak Bookshelf item.ebwizardry\:crystal_magic.name=Magic Crystal item.ebwizardry\:crystal_fire.name=Fiery Crystal @@ -510,6 +517,8 @@ container.ebwizardry\:arcane_workbench.apply=Apply container.ebwizardry\:arcane_workbench.mana=Mana\: container.ebwizardry\:arcane_workbench.upgrades=Applied Upgrades\: +container.ebwizardry\:bookshelf=Bookshelf + tier.novice=Novice tier.apprentice=Apprentice tier.advanced=Advanced diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 22826952..9a06a999 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -1,4 +1,4 @@ -#PARSE_ESCAPES +# PARSE_ESCAPES tile.ebwizardry\:arcane_workbench.name=Arcane Workbench tile.ebwizardry\:crystal_ore.name=Crystal Ore tile.ebwizardry\:petrified_stone.name=Petrified Stone @@ -30,8 +30,15 @@ tile.ebwizardry\:oak_gilded_wood.name=Gilded Oak Wood tile.ebwizardry\:spruce_gilded_wood.name=Gilded Spruce Wood tile.ebwizardry\:birch_gilded_wood.name=Gilded Birch Wood tile.ebwizardry\:jungle_gilded_wood.name=Gilded Jungle Wood -tile.ebwizardry\:dark_oak_gilded_wood.name=Gilded Dark Oak Wood tile.ebwizardry\:acacia_gilded_wood.name=Gilded Acacia Wood +tile.ebwizardry\:dark_oak_gilded_wood.name=Gilded Dark Oak Wood + +tile.ebwizardry\:oak_bookshelf.name=Oak Bookshelf +tile.ebwizardry\:spruce_bookshelf.name=Spruce Bookshelf +tile.ebwizardry\:birch_bookshelf.name=Birch Bookshelf +tile.ebwizardry\:jungle_bookshelf.name=Jungle Bookshelf +tile.ebwizardry\:acacia_bookshelf.name=Acacia Bookshelf +tile.ebwizardry\:dark_oak_bookshelf.name=Dark Oak Bookshelf item.ebwizardry\:crystal_magic.name=Magic Crystal item.ebwizardry\:crystal_fire.name=Fiery Crystal @@ -510,6 +517,8 @@ container.ebwizardry\:arcane_workbench.apply=Apply container.ebwizardry\:arcane_workbench.mana=Mana\: container.ebwizardry\:arcane_workbench.upgrades=Applied Upgrades\: +container.ebwizardry\:bookshelf=Bookshelf + tier.novice=Novice tier.apprentice=Apprentice tier.advanced=Advanced diff --git a/src/main/resources/assets/ebwizardry/models/block/acacia_bookshelf.json b/src/main/resources/assets/ebwizardry/models/block/acacia_bookshelf.json new file mode 100644 index 00000000..1908bdc0 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/acacia_bookshelf.json @@ -0,0 +1,9 @@ +{ + "parent": "ebwizardry:block/bookshelf", + "textures": { + "particle": "ebwizardry:blocks/acacia_bookshelf_side", + "side": "ebwizardry:blocks/acacia_bookshelf_side", + "inside": "ebwizardry:blocks/acacia_bookshelf_inside", + "top": "ebwizardry:blocks/acacia_bookshelf_top" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/birch_bookshelf.json b/src/main/resources/assets/ebwizardry/models/block/birch_bookshelf.json new file mode 100644 index 00000000..988c9506 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/birch_bookshelf.json @@ -0,0 +1,9 @@ +{ + "parent": "ebwizardry:block/bookshelf", + "textures": { + "particle": "ebwizardry:blocks/birch_bookshelf_side", + "side": "ebwizardry:blocks/birch_bookshelf_side", + "inside": "ebwizardry:blocks/birch_bookshelf_inside", + "top": "ebwizardry:blocks/birch_bookshelf_top" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/books0.json b/src/main/resources/assets/ebwizardry/models/block/books0.json new file mode 100644 index 00000000..e8a196f5 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books0.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book0", + "from": [13, 9, 2], + "to": [15, 14, 6], + "faces": { + "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "texture": "#books"} + } + }, + { + "name": "book0", + "from": [1, 9, 10], + "to": [3, 14, 14], + "faces": { + "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books1.json b/src/main/resources/assets/ebwizardry/models/block/books1.json new file mode 100644 index 00000000..bd5dedf6 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books1.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book1", + "from": [11, 9, 2], + "to": [13, 14, 6], + "faces": { + "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "texture": "#books"} + } + }, + { + "name": "book1", + "from": [3, 9, 10], + "to": [5, 14, 14], + "faces": { + "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books10.json b/src/main/resources/assets/ebwizardry/models/block/books10.json new file mode 100644 index 00000000..dc7871f7 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books10.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book10", + "from": [4, 1, 2], + "to": [6, 6, 6], + "faces": { + "north": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "east": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "south": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "west": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "texture": "#books"} + } + }, + { + "name": "book10", + "from": [10, 1, 10], + "to": [12, 6, 14], + "faces": { + "north": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "east": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "south": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "west": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books11.json b/src/main/resources/assets/ebwizardry/models/block/books11.json new file mode 100644 index 00000000..1597ac01 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books11.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book11", + "from": [1, 1, 2], + "to": [3, 6, 6], + "faces": { + "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "texture": "#books"} + } + }, + { + "name": "book11", + "from": [13, 1, 10], + "to": [15, 6, 14], + "faces": { + "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books2.json b/src/main/resources/assets/ebwizardry/models/block/books2.json new file mode 100644 index 00000000..39171d25 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books2.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book2", + "from": [8, 9, 2], + "to": [10, 14, 6], + "faces": { + "north": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "east": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "south": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "west": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "texture": "#books"} + } + }, + { + "name": "book2", + "from": [6, 9, 10], + "to": [8, 14, 14], + "faces": { + "north": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "east": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "south": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "west": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books3.json b/src/main/resources/assets/ebwizardry/models/block/books3.json new file mode 100644 index 00000000..e9b1be97 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books3.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book3", + "from": [5, 9, 2], + "to": [7, 14, 6], + "faces": { + "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "texture": "#books"} + } + }, + { + "name": "book3", + "from": [9, 9, 10], + "to": [11, 14, 14], + "faces": { + "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books4.json b/src/main/resources/assets/ebwizardry/models/block/books4.json new file mode 100644 index 00000000..41187df9 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books4.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book4", + "from": [3, 9, 2], + "to": [5, 14, 6], + "faces": { + "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "texture": "#books"} + } + }, + { + "name": "book4", + "from": [11, 9, 10], + "to": [13, 14, 14], + "faces": { + "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books5.json b/src/main/resources/assets/ebwizardry/models/block/books5.json new file mode 100644 index 00000000..62ea95d2 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books5.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book5", + "from": [1, 9, 2], + "to": [3, 14, 6], + "faces": { + "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "texture": "#books"} + } + }, + { + "name": "book5", + "from": [13, 9, 10], + "to": [15, 14, 14], + "faces": { + "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books6.json b/src/main/resources/assets/ebwizardry/models/block/books6.json new file mode 100644 index 00000000..255f5174 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books6.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book6", + "from": [13, 1, 2], + "to": [15, 6, 6], + "faces": { + "north": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "east": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "south": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "west": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "texture": "#books"} + } + }, + { + "name": "book6", + "from": [1, 1, 10], + "to": [3, 6, 14], + "faces": { + "north": {"uv": [6, 10, 8, 15], "texture": "#books"}, + "east": {"uv": [2, 10, 6, 15], "texture": "#books"}, + "south": {"uv": [0, 10, 2, 15], "texture": "#books"}, + "west": {"uv": [6, 10, 2, 15], "texture": "#books"}, + "up": {"uv": [8, 10, 10, 14], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books7.json b/src/main/resources/assets/ebwizardry/models/block/books7.json new file mode 100644 index 00000000..6a3fb74f --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books7.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book7", + "from": [11, 1, 2], + "to": [13, 6, 6], + "faces": { + "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "texture": "#books"} + } + }, + { + "name": "book7", + "from": [3, 1, 10], + "to": [5, 6, 14], + "faces": { + "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books8.json b/src/main/resources/assets/ebwizardry/models/block/books8.json new file mode 100644 index 00000000..0e3c5797 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books8.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book8", + "from": [9, 1, 2], + "to": [11, 6, 6], + "faces": { + "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "texture": "#books"} + } + }, + { + "name": "book8", + "from": [5, 1, 10], + "to": [7, 6, 14], + "faces": { + "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, + "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, + "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, + "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, + "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/books9.json b/src/main/resources/assets/ebwizardry/models/block/books9.json new file mode 100644 index 00000000..2b098b7a --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/books9.json @@ -0,0 +1,33 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "books": "ebwizardry:blocks/books" + }, + "elements": [ + { + "name": "book9", + "from": [6, 1, 2], + "to": [8, 6, 6], + "faces": { + "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "texture": "#books"} + } + }, + { + "name": "book9", + "from": [8, 1, 10], + "to": [10, 6, 14], + "faces": { + "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, + "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, + "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, + "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, + "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/bookshelf.json b/src/main/resources/assets/ebwizardry/models/block/bookshelf.json index 9e1fb88f..cccd9182 100644 --- a/src/main/resources/assets/ebwizardry/models/block/bookshelf.json +++ b/src/main/resources/assets/ebwizardry/models/block/bookshelf.json @@ -1,13 +1,6 @@ { "credit": "Made with Blockbench", "parent": "block/block", - "textures": { - "books": "ebwizardry:blocks/books", - "particle": "ebwizardry:blocks/spruce_bookshelf_side", - "side": "ebwizardry:blocks/spruce_bookshelf_side", - "inside": "ebwizardry:blocks/spruce_bookshelf_inside", - "top": "ebwizardry:blocks/spruce_bookshelf_top" - }, "elements": [ { "name": "backboard", @@ -27,7 +20,7 @@ "east": {"uv": [0, 15, 16, 16], "texture": "#side", "cullface": "east"}, "south": {"uv": [0, 15, 16, 16], "texture": "#side", "cullface": "south"}, "west": {"uv": [0, 15, 16, 16], "texture": "#side", "cullface": "west"}, - "up": {"uv": [0, 0, 16, 16], "texture": "#inside", "cullface": "up"}, + "up": {"uv": [0, 0, 16, 16], "texture": "#inside"}, "down": {"uv": [0, 0, 16, 16], "texture": "#top", "cullface": "down"} } }, @@ -50,7 +43,7 @@ "north": {"uv": [0, 1, 1, 15], "texture": "#side", "cullface": "north"}, "east": {"uv": [0, 1, 16, 15], "texture": "#side", "cullface": "east"}, "south": {"uv": [15, 1, 16, 15], "texture": "#side", "cullface": "south"}, - "west": {"uv": [0, 1, 16, 15], "texture": "#inside", "cullface": "west"} + "west": {"uv": [0, 1, 16, 15], "texture": "#inside"} } }, { @@ -256,294 +249,6 @@ "west": {"uv": [14, 14, 15, 15], "texture": "#side"}, "up": {"uv": [14, 14, 15, 15], "rotation": 180, "texture": "#side"} } - }, - { - "name": "book1", - "from": [13, 9, 2], - "to": [15, 14, 6], - "faces": { - "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, - "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, - "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, - "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, - "up": {"uv": [8, 0, 10, 4], "texture": "#books"} - } - }, - { - "name": "book1", - "from": [1, 9, 10], - "to": [3, 14, 14], - "faces": { - "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, - "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, - "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, - "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, - "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book2", - "from": [11, 9, 2], - "to": [13, 14, 6], - "faces": { - "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, - "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, - "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, - "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, - "up": {"uv": [8, 5, 10, 9], "texture": "#books"} - } - }, - { - "name": "book2", - "from": [3, 9, 10], - "to": [5, 14, 14], - "faces": { - "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, - "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, - "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, - "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, - "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book3", - "from": [8, 9, 2], - "to": [10, 14, 6], - "faces": { - "north": {"uv": [0, 10, 2, 15], "texture": "#books"}, - "east": {"uv": [6, 10, 2, 15], "texture": "#books"}, - "south": {"uv": [6, 10, 8, 15], "texture": "#books"}, - "west": {"uv": [2, 10, 6, 15], "texture": "#books"}, - "up": {"uv": [8, 10, 10, 14], "texture": "#books"} - } - }, - { - "name": "book3", - "from": [6, 9, 10], - "to": [8, 14, 14], - "faces": { - "north": {"uv": [6, 10, 8, 15], "texture": "#books"}, - "east": {"uv": [2, 10, 6, 15], "texture": "#books"}, - "south": {"uv": [0, 10, 2, 15], "texture": "#books"}, - "west": {"uv": [6, 10, 2, 15], "texture": "#books"}, - "up": {"uv": [8, 10, 10, 14], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book4", - "from": [5, 9, 2], - "to": [7, 14, 6], - "faces": { - "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, - "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, - "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, - "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, - "up": {"uv": [8, 0, 10, 4], "texture": "#books"} - } - }, - { - "name": "book4", - "from": [9, 9, 10], - "to": [11, 14, 14], - "faces": { - "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, - "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, - "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, - "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, - "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book5", - "from": [3, 9, 2], - "to": [5, 14, 6], - "faces": { - "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, - "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, - "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, - "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, - "up": {"uv": [8, 5, 10, 9], "texture": "#books"} - } - }, - { - "name": "book5", - "from": [11, 9, 10], - "to": [13, 14, 14], - "faces": { - "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, - "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, - "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, - "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, - "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book6", - "from": [1, 9, 2], - "to": [3, 14, 6], - "faces": { - "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, - "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, - "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, - "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, - "up": {"uv": [8, 5, 10, 9], "texture": "#books"} - } - }, - { - "name": "book6", - "from": [13, 9, 10], - "to": [15, 14, 14], - "faces": { - "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, - "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, - "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, - "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, - "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book7", - "from": [13, 1, 2], - "to": [15, 6, 6], - "faces": { - "north": {"uv": [0, 10, 2, 15], "texture": "#books"}, - "east": {"uv": [6, 10, 2, 15], "texture": "#books"}, - "south": {"uv": [6, 10, 8, 15], "texture": "#books"}, - "west": {"uv": [2, 10, 6, 15], "texture": "#books"}, - "up": {"uv": [8, 10, 10, 14], "texture": "#books"} - } - }, - { - "name": "book7", - "from": [1, 1, 10], - "to": [3, 6, 14], - "faces": { - "north": {"uv": [6, 10, 8, 15], "texture": "#books"}, - "east": {"uv": [2, 10, 6, 15], "texture": "#books"}, - "south": {"uv": [0, 10, 2, 15], "texture": "#books"}, - "west": {"uv": [6, 10, 2, 15], "texture": "#books"}, - "up": {"uv": [8, 10, 10, 14], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book8", - "from": [11, 1, 2], - "to": [13, 6, 6], - "faces": { - "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, - "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, - "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, - "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, - "up": {"uv": [8, 5, 10, 9], "texture": "#books"} - } - }, - { - "name": "book8", - "from": [3, 1, 10], - "to": [5, 6, 14], - "faces": { - "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, - "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, - "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, - "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, - "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book9", - "from": [9, 1, 2], - "to": [11, 6, 6], - "faces": { - "north": {"uv": [0, 5, 2, 10], "texture": "#books"}, - "east": {"uv": [6, 5, 2, 10], "texture": "#books"}, - "south": {"uv": [6, 5, 8, 10], "texture": "#books"}, - "west": {"uv": [2, 5, 6, 10], "texture": "#books"}, - "up": {"uv": [8, 5, 10, 9], "texture": "#books"} - } - }, - { - "name": "book9", - "from": [5, 1, 10], - "to": [7, 6, 14], - "faces": { - "north": {"uv": [6, 5, 8, 10], "texture": "#books"}, - "east": {"uv": [2, 5, 6, 10], "texture": "#books"}, - "south": {"uv": [0, 5, 2, 10], "texture": "#books"}, - "west": {"uv": [6, 5, 2, 10], "texture": "#books"}, - "up": {"uv": [8, 5, 10, 9], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book10", - "from": [6, 1, 2], - "to": [8, 6, 6], - "faces": { - "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, - "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, - "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, - "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, - "up": {"uv": [8, 0, 10, 4], "texture": "#books"} - } - }, - { - "name": "book10", - "from": [8, 1, 10], - "to": [10, 6, 14], - "faces": { - "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, - "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, - "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, - "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, - "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book11", - "from": [4, 1, 2], - "to": [6, 6, 6], - "faces": { - "north": {"uv": [0, 10, 2, 15], "texture": "#books"}, - "east": {"uv": [6, 10, 2, 15], "texture": "#books"}, - "south": {"uv": [6, 10, 8, 15], "texture": "#books"}, - "west": {"uv": [2, 10, 6, 15], "texture": "#books"}, - "up": {"uv": [8, 10, 10, 14], "texture": "#books"} - } - }, - { - "name": "book11", - "from": [10, 1, 10], - "to": [12, 6, 14], - "faces": { - "north": {"uv": [6, 10, 8, 15], "texture": "#books"}, - "east": {"uv": [2, 10, 6, 15], "texture": "#books"}, - "south": {"uv": [0, 10, 2, 15], "texture": "#books"}, - "west": {"uv": [6, 10, 2, 15], "texture": "#books"}, - "up": {"uv": [8, 10, 10, 14], "rotation": 180, "texture": "#books"} - } - }, - { - "name": "book12", - "from": [1, 1, 2], - "to": [3, 6, 6], - "faces": { - "north": {"uv": [0, 0, 2, 5], "texture": "#books"}, - "east": {"uv": [6, 0, 2, 5], "texture": "#books"}, - "south": {"uv": [6, 0, 8, 5], "texture": "#books"}, - "west": {"uv": [2, 0, 6, 5], "texture": "#books"}, - "up": {"uv": [8, 0, 10, 4], "texture": "#books"} - } - }, - { - "name": "book12", - "from": [13, 1, 10], - "to": [15, 6, 14], - "faces": { - "north": {"uv": [6, 0, 8, 5], "texture": "#books"}, - "east": {"uv": [2, 0, 6, 5], "texture": "#books"}, - "south": {"uv": [0, 0, 2, 5], "texture": "#books"}, - "west": {"uv": [6, 0, 2, 5], "texture": "#books"}, - "up": {"uv": [8, 0, 10, 4], "rotation": 180, "texture": "#books"} - } } ], "display": { @@ -570,7 +275,7 @@ "scale": [0.25, 0.25, 0.25] }, "gui": { - "rotation": [30, 135, 0], + "rotation": [30, 225, 0], "scale": [0.625, 0.625, 0.625] }, "fixed": { diff --git a/src/main/resources/assets/ebwizardry/models/block/dark_oak_bookshelf.json b/src/main/resources/assets/ebwizardry/models/block/dark_oak_bookshelf.json new file mode 100644 index 00000000..eccb39de --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/dark_oak_bookshelf.json @@ -0,0 +1,9 @@ +{ + "parent": "ebwizardry:block/bookshelf", + "textures": { + "particle": "ebwizardry:blocks/dark_oak_bookshelf_side", + "side": "ebwizardry:blocks/dark_oak_bookshelf_side", + "inside": "ebwizardry:blocks/dark_oak_bookshelf_inside", + "top": "ebwizardry:blocks/dark_oak_bookshelf_top" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/jungle_bookshelf.json b/src/main/resources/assets/ebwizardry/models/block/jungle_bookshelf.json new file mode 100644 index 00000000..53f5efa7 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/jungle_bookshelf.json @@ -0,0 +1,9 @@ +{ + "parent": "ebwizardry:block/bookshelf", + "textures": { + "particle": "ebwizardry:blocks/jungle_bookshelf_side", + "side": "ebwizardry:blocks/jungle_bookshelf_side", + "inside": "ebwizardry:blocks/jungle_bookshelf_inside", + "top": "ebwizardry:blocks/jungle_bookshelf_top" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/oak_bookshelf.json b/src/main/resources/assets/ebwizardry/models/block/oak_bookshelf.json new file mode 100644 index 00000000..36cc798d --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/oak_bookshelf.json @@ -0,0 +1,9 @@ +{ + "parent": "ebwizardry:block/bookshelf", + "textures": { + "particle": "ebwizardry:blocks/oak_bookshelf_side", + "side": "ebwizardry:blocks/oak_bookshelf_side", + "inside": "ebwizardry:blocks/oak_bookshelf_inside", + "top": "ebwizardry:blocks/oak_bookshelf_top" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/spruce_bookshelf.json b/src/main/resources/assets/ebwizardry/models/block/spruce_bookshelf.json new file mode 100644 index 00000000..d75b4d4e --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/spruce_bookshelf.json @@ -0,0 +1,9 @@ +{ + "parent": "ebwizardry:block/bookshelf", + "textures": { + "particle": "ebwizardry:blocks/spruce_bookshelf_side", + "side": "ebwizardry:blocks/spruce_bookshelf_side", + "inside": "ebwizardry:blocks/spruce_bookshelf_inside", + "top": "ebwizardry:blocks/spruce_bookshelf_top" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/item/acacia_bookshelf.json b/src/main/resources/assets/ebwizardry/models/item/acacia_bookshelf.json new file mode 100644 index 00000000..0adc3e0c --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/acacia_bookshelf.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/acacia_bookshelf" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/birch_bookshelf.json b/src/main/resources/assets/ebwizardry/models/item/birch_bookshelf.json new file mode 100644 index 00000000..a41498fd --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/birch_bookshelf.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/birch_bookshelf" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/dark_oak_bookshelf.json b/src/main/resources/assets/ebwizardry/models/item/dark_oak_bookshelf.json new file mode 100644 index 00000000..9818d2d5 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/dark_oak_bookshelf.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/dark_oak_bookshelf" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/jungle_bookshelf.json b/src/main/resources/assets/ebwizardry/models/item/jungle_bookshelf.json new file mode 100644 index 00000000..e65f05c7 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/jungle_bookshelf.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/jungle_bookshelf" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/oak_bookshelf.json b/src/main/resources/assets/ebwizardry/models/item/oak_bookshelf.json new file mode 100644 index 00000000..cbeffa68 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/oak_bookshelf.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/oak_bookshelf" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/spruce_bookshelf.json b/src/main/resources/assets/ebwizardry/models/item/spruce_bookshelf.json new file mode 100644 index 00000000..d29cc438 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/spruce_bookshelf.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/spruce_bookshelf" +} diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/acacia_bookshelf_inside.png b/src/main/resources/assets/ebwizardry/textures/blocks/acacia_bookshelf_inside.png new file mode 100644 index 0000000000000000000000000000000000000000..fa0d343d46e1c3b0992d6dc677e541f8f7e4c19c GIT binary patch literal 5356 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 R^~^wH51y`mF6*2UngH^fdP@KR literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/acacia_bookshelf_top.png b/src/main/resources/assets/ebwizardry/textures/blocks/acacia_bookshelf_top.png new file mode 100644 index 0000000000000000000000000000000000000000..1021d20ec72cded86f9743413ffdd70b989c3d9d GIT binary patch literal 5625 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 bEn#3VJtV<9rMs{WG$7#V>gTe~DWM4fGeNzS literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/birch_bookshelf_side.png b/src/main/resources/assets/ebwizardry/textures/blocks/birch_bookshelf_side.png new file mode 100644 index 0000000000000000000000000000000000000000..393ec084337f6baa45ae8e229b4f1391035a3526 GIT binary patch literal 5657 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_bookshelf_side.png b/src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_bookshelf_side.png new file mode 100644 index 0000000000000000000000000000000000000000..e7bc8f32056377a468877bcc21648471739c3005 GIT binary patch literal 1616 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-ZdHXulmsP~D-;yvr)B1( zDwI?fq$;FVWTr7NRNPt{mO1H*8qeSJnnf%s0;dZjRu?tP*!LfmE3dYBxw2+iPNL$3 z3zJz^{ry}XUjH!hlV9Y=gRaj_|?R3MeuhMs( znSYS{wC88)`pE~TpLW{#w(*bEgl|qV+nSsohoxj_UZ_r-`pv0rd)n@QpKnboGyL&d z?9{aP)it{q*GtJ~+gKMq(cL#~>vz3H@)6Pe`>!Tu=*t|pIa$4Mdwpq#QO={Z6B#vk zH(Xd_{?PRE>$Wp*qLqJ02~S(<&^`6ri{ppa+uPemuH(CG{zQMmJlm=V@e{%>FFf8* z>3m}DmuS0_;_t7hmv5j)!_ULS~SjDlaCrm@gxmw+Os!(axWHBv+i$%%&r($jd1SYUG2<~J^ zVA_yz<>u^+Rh$jG6=ZmJ)ZB6k&Ykm$i&bNth{&S$4HAi`{%5@sUU<*lN$_RCFRrqW zCrly?CnyKaQebmgk-z*@$%l*g<~OcpY%;nlv*F;9&k>9t(ig6OU?iNu?8q3)>9g-0 z>jR|-1%oyofeU>-6O`Z1oT}cW%*^0rFt_(9PlQK{c27$7grs>BSugZj&ReFzt#8Kl z@Qls!9(J>fDh;V?73%izb}UqJThn3jQAowB=vXgL@wwfBfs@VeRed~HEnoA+lT|op z!hnj{4?g)C zy<3yDWzB)^0{ckS$1YP2{;dC!s;D5`cIv+IqUi1-y#taF*?PRXa~&H|a)H8T!p?>Vr@VyA}bFGqPS1s1oBpO~JK7rO<}u!$b@79;kmDD+tC zkqeVnFTKG%d+{Xi0N?rR1$rK)tWoGG-w~&BwY)(t*mK&$Q1;EvYYyyrG+kAW{ng2^ z2j7eNyH|hxH2>(seb@A!pYVN@u%f)Pd7gKT__3Lf__iek9!_&ubiiZJkq6dx^Mk&| zGx4WwnUsEC{7%3m{?f(Q);d4*wLT-`^(bH1uU73xlF$0vGkipsZ=G3xiaWG@aZzji z>y4HZUVc3Di?ho*bUv5fuHrM3KF^%~E{`eiq;K!yq#Kc%ViOkY#4vwQ)_o=ZOy-Nw z%{_*#n;%bij+^vhT66!-im(&WL3SPg<}|<3LqUnN{*^>>4P38B|NPT`%- zy@CJv+$tLm`=z(JzUCe4-`?Kc`~Jd;`)ap@L&KA6tk;DuWNqe__~iDN`&sa(fCse+ zmJAFGY)RhkE({(FP7Fp`oC|I;FfecyctjR6Fz6|RFk{71`!WUw2KEw9Usv`g%xvPi z>ie5}JQ)}mBuiW)N}Tg^b5rw57@Uhz6H8K46v{J8G8EiBeFMT9`NSC*7%q9bIEGl9 zK00Y5*C7Lm7X2s7mxjE!!l!g2^vm8ak}|R`1tLf8x_@YW^&qJ|Dr@4kHjAxCZZt3+ z%=x+VciklGZS(t|yen~z hbjmp$jA$Rv}-+kK4 zq@h7ZVd1a)`+NT#^uJ+QB(=!@p7fuXB2Oc+i)Z5H%FXibTyM4%^DnWIWSVp|=Xvs% z?l?#B6Q>tVU#i}`PTX~+-m!0IPIyl|lKNo74xY$)Y-fUeDm6E4(%JoU&)HyoY_!n_2}2v_uA)Q7SuZaV|lsr5&swNOD0TJ25-2X z{>{06DN?>4R{qoH9-sKv>Pq31jZJs1EjBHy z>}l}!eRJ@ga-z!=7e~3b9-DX<7bG$E^vb@Uv$IgP_#u<{+z`$gu3Zs&EJ-X9o?Hyi z_*RzQQMr*)F3ni%Xmx|{$4hSh&6zW_WfCi%dz_GKGdecSUh0eN!fREIhO;YtQdFrzAr~034V$R@pJZi%|$5^SPV&#QF=B)BOkGn^`jT{D??!-iFF3WDaicCtE(9x(gHBDZLf(aDS! z2B)>^(ha_wBoi!`pP2tZsYqjW=<-!@&2GN4_I}-aXb=Cs@+&eq1&+=9ibp0mEz4Nk zEmg~7cub^tQp&|b(}i-uKAKN?7E8`DE=%Wlvgc%)`SzF3wr@D9o4b=kG&<(uBX5n3 zOD2`yeA{Pr@2}jq9m{RQ*Lf^`wJI#$x!3gSoe5cI&8~0xDHfH}8D$q3z2V+5{(#4= zvv`*5O0WoIHf?cc{a&_6)>uKUkFnwf#|G7jeKR{NOg-J#C8q`Dw`dgvSzB0_A8A;y zTIbe}BkvmLO!`{SZq6j|$$Y|MzPsiVc0c~cx7D^^e6B3VSEDzMreascr_9}C_EGV< zz~!nE)g`M8x&JwwO>o@NBRR!>_I#0(+xWZh72Y+!ezrX*sMo`DZvXD5Qj_FAb>!WA zevkLVbk8^WKka9}Jg1Q>bx_)1`@?p%&m0`T4o6Jibh2w^`LYWSmS?b?{Mz5_XrTVE zRPT6Sy63J<({!?B&CCsNFIeHMF2%j)^`kFJ4(+b-AFr(fCn z*tJP-jdR#t$tC)>K{E@rD-=(C;Fz!Md}dzf0pn9e-ZN)EF}$3+I>pxS-<;jCub5`W zs^9!AU8VYD<&(vG`{m{t@7+~%@}2uXInAg&s@3L+B>)ntSuwKXEdWWtZm3%ck=C-v`cr8QWgD z^;_Y-AD;T}_GCKbZ~Aqqqwk&5cC*j7>hpMC?Ny0&iF~?3PV=&gjPKkvuI?eaEDk57AVcI^6`d6}O1**|~3eKE!6_r?4z3)PFiq&#Q8TK0NE{Pi2V z_f&DjF)rBN@#mP0$lg-LoVuvIFekJ4x~?x0f2aCSRCk?k?<%!#fzV%m2fl~9ZLS-d zF)%Q&C3(BMFnBOHF&JraF1X3Sz`$AH5n0T@pr;JNj1^1m%NQ6K*h@TpUD=;7vx#fz z3W$H6z`(#DS>hT|;+&tGo0?a`;9QiNSdyBeP@Y+mq2TW68xY>eC(gjYaL&`kF~s8Z z)L?7ALk2vxxk^e?8W}ell{_u5WsnZyKOnh7Ts*3o0T&2`gHS~s7~3Un>lX)B;~@YBQBe>*1%F&vnc#wMrf`(Hv> z>7X@pG?S--aMqKsrYDZQI^vrwJFY(2C(^lkD&LZ;lUA?i^ZHgC{_~fz)4b?oGgtmA zQgDniz8amdK+L@~;MFdbpp&1DFVZ|2w}NlUq5b@)+dK|b8)TibZWiEtd!hVt!j&4E bbNXs;msH<#Pq1NNU|{fc^>bP0l+XkK2wu^- literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/jungle_bookshelf_inside.png b/src/main/resources/assets/ebwizardry/textures/blocks/jungle_bookshelf_inside.png new file mode 100644 index 0000000000000000000000000000000000000000..871a563acd98eb94f09580f3483f841632785fd0 GIT binary patch literal 1306 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-B&tFpN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsG*f9y#r`22b1ltzB_Vs)}t-4Q3R-cOT*Q64`d5~8~_^|_7@`c;)`8S4{&B7ed>+d8HFO1_sD zPG|hI?HWzxfA69sH}QhVJ0t-4UI_SH_PtFn9f z`h@8{KFy-dGRC<*nF@z$Ew3(X)_?lhbcYtlt&-v+Y@fs0*dFK~lRvmrcuUfvg|IJqg@-ssHl2gf^SHM5E9{19;B(M;)* zirw-exx;YTjL9lTJF_`|&PejoOxKyU(DL!i9!^Vf&8V1-kGwTDE}67C;OeVYq4CbW zrdRJQ$#j-8zwgwoyLHd!zJObI^LF1qEcboO^S*%Bw_>-ytA4Rj{ngJU+cw;rDj)FJ z)mLq$q2Zh>3cL>&H61TZYL@KiOlIJ3X3*~BNsCT-o-#rD%r=!1T*4BW%zlp@d06hA zOPcFnCjX@8%Kmm~#+E(JGQ1`C70S=Q72G=QdAFbX!ijB%SI$=Y{lTd-QYo&^N=Ef@ zTJ?=fn;YkETQ8F7vpL+z?CVsss%}f?iDLCSvmec?Pd$C{(*4^-Go0>kbj?amuaH$T za%Z1eeErhq#^but^Bx9#keemz;xSvPM!r(^;NB&h9;LOEUHrJgQl@9KvE96R+rC_F zXkk`Cp!KZpu#E4{>wL`Ooj1zx2fPon@sTYTqvS{rTJ_9*=|Gf48&mpOQJ*(*5H< zle(NK)-AmM`|O>)`(*cW&RWhK<)c3T4xinchw9Qx|14YIA?i(0|NtRfk$L91B0G22s2hJwJ&2}U|=ut^mS!_!Ym;y zZCKi(Aj`nOAX(xXQR1ARo12tX-4NAmmN&Y*p{`oHa0dMj0v9i#$kz{PS3LDh4c6r3jc^Xt@F7S!oa}5 N;OXk;vd$@?2>>NjQt$u( literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/jungle_bookshelf_side.png b/src/main/resources/assets/ebwizardry/textures/blocks/jungle_bookshelf_side.png new file mode 100644 index 0000000000000000000000000000000000000000..0acc2705cb5766d3b78065cf3463c745809cad47 GIT binary patch literal 1564 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-rdNeTlmsP~D-;yvr)B1( zDwI?fq$;FVWTr7NRNPt{7CC8&70;jVn$I|G<5=S`o_vb(lOF zEc9vC{cArKFiwAEt~_sU=@Z^}ZCk(VJ+jq9xj#cqn63{*MTScGs`gr+x#c=B{sy{t1J?_kV^(gY|rzbIM-Tf{+mlyivGvfMeUE$UbxjNe0tB}Nq3{J?3?njr7}7_ zd(YE?s+h2LzcPE}a}85Y+;C@|opX4mrjYY7w>B=-tPuCvn#_wHo|4loPFuJpfk!~O zXqw)-m+Ge9ChYWRxFC7kG2tmQckhP6meV2vI|2f3*s`Rzgx8gW!vQ89PLT_JM6HEt&U=wrg1xL5-b#8k8HS6as2X(Q`449 zwhA~!6nfcgaa3~BN!Iz+Z_Ze)5xMbL)ay0d54z2svtwT3xytL>S)U&DZogOgzPaCS zrNTiL>6j0SEfPUbG*;{|Q<6{aFkIHMfODSkJ--^qmR`LcknGDFz1GwCtl9M%ZrxSW_^wV{GySuE z$l|3^{7Zki)URM${e3t0tEWfG&MuIUZhEj!+hNg+-Dy@64DU>RJ?rI(6I`bmGIv|F z?n(-{5R!EJ#f5hZ=Y(p^lE3Kd$nmeGXqESO-lyw!?G0YL?fwoFGM~OWzc@9@M<*#q8G+qXv_3UBORVJUK5!0fN>9OgE;_p=ti-N5D^Sf5^b z)5nTu_ARx_s~(5eyT{LU%4be}u4o^xXE{6Cg5COhZAt6bTXqR?4^|5A+y234?%WQg zE$wF}2Vc9kHg}=klsuzDvp;yfY)!I0torAo?xxGywf+h!Yjwq)Io0o^vv}Rl+qFOY z;R`vR$jMTzDm%|kY>QtmeCE9JE#7p4GWNyA>+;@e2Y-$&4gTd^`sC4z_|>zep1x&X zab>80WEX-SLg{Pup~?ht-vw@B_s3hzw!w>!@6cAs~5 z)i;kf$-31|udVy6Y)TK6|Fzij+iXv&Z|!=k{ZkIFd&6D*YTmx>Z(<{E-wb-`9k%cM zE|4`>eQTGr*;xK6Ed6b<$Ub~~o4xgl^>xhARYtzse^&@IFfgzsdAqwX{0G4WdzVjU zU|`@Z@Q5sCV9-+rVaAH3_GJtV4D2PIzOL*~m?eazG&sKOoWsDtAX(xXQR1ARo12Jq135}WiYW|xZ-k!f-0t9yKxDg;y|N9%a9$WC? z0|yQytkXSvpvhNomy@2Jna|<-&KhDVt|z1lCEM}@xpyhe$URY$6W1iIpmpN-fddB` zI$FPHEc_~0t?=tchS8iozos-EJ?h+$c)oAB%JD-dSNlu;nJgj^v2X7Wj()MA%LlKg z%deQjSKz9sXV_EHAouFT#>UeIVqcCl`7&=xEa1ME?8$z@OknngfXfG~Z9e{==k}6; ZA>rR*jd*ok5e5bZ22WQ%mvv4FO#o#6)ye<> literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/jungle_bookshelf_top.png b/src/main/resources/assets/ebwizardry/textures/blocks/jungle_bookshelf_top.png new file mode 100644 index 0000000000000000000000000000000000000000..9d2cb6875ff0c4af041c11586a5c0b37771ad577 GIT binary patch literal 1561 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-W>kellmsP~D-;yvr)B1( zDwI?fq$;FVWTr7NRNPt{7CC8&70;jRn$I|M{4fZMh7>+3cK>6;!e zeNH;O-M?S@??FWw8Ohhp-RCrA)MjaL&Ry_q;<33?CBJ`N@a1pZz_><+8sf{c*gs__+8t>!q0jyZB$I zx8Aw9Z_DPrH($nGT6jA)<$2!;{<3v*C+toRUmJfr?C`d&J9nkXOnY-}(YK{Na?hTg zyH;`f;g=n2xaPj8bDP7c8aTmS)N@*)Sf9>>!sNqKh4b&YMekr(G*L}%t%u;*)&w2_ zWv`2|F_-=C9dUlPin&G4gPnJ^OhiRXuX4=8h67WX6{{uANv!|#Vu`<6ZnVBenke78 z2Jb2>CfD%3nIg$jA`f#XX0adq{j;Vd)>LFm^Rot4wW+(=Pt4c7KgpYap{ZKbmdy<} zuRmoFVQgfTxEA1Gd2O1kw!YImNf$PbDGYnXL)vt5)`m2QEZ*8|v4ZKyG3$B9LpBL(y z&)0a7(8R0vL%@kg)9cEO!dw^IV*<%OoQ}da>U-uIC@$5BOrM-vcJuj!WIxvxFOs@+ z*ZmOk(pl+sRkSoF_N(cYh~URLHd^^k0(2(a~ZFqxeXUW-+gybdK*G+>114P3ZE|nE( zl^zN<1^1V5c+?M_1$SxJeUu;)rGd$WXw^%J^;~}%&m7bm}jTvME z&i(U>ownj^jiFNi^@P)#uTNNRJ9nl1vy`r#Ers0c+a4EeVV7C3K!5tt#ET8PJj?jp ziUYEquTZ~xdW-L`BA$mr$8`T}wrdutYWRFd?!&$|SLW*tqUlGv&OYoAeSWIfYjfC} zNQdWcAzkWR{!^aqENI?8S@QIr23PZ4c4@Cwv%gGnPQQ3==kf_h7Ega?H1EyFW3R9L z*ZBN!vYu<*=YPDa`wnkbPg$jtdg-0da-%ARRcvp%H;XKKR=(0jy=dR$uIlcumpiv| zUwT(IyVO0|RG)M`SSrgPt-7Ggd6MFJoX}U@!6Xb!C6TEFmnVSg65h!N9;ES>hT|;+&tG zo0?a`;9QiNSdyBeP@Y+mq2TW68xY>eC(gjYaM#ntF~s8Z(%_9;hZRKjh@5<&U|}K3 zHP?E2-`@_ueM?IKt7 zw9ky!*>t>oeSjc3hW$~JbHdw@t7ckLh5hkzQUM)sy&|>-LyIQTpBw! zD186y9w4B*X5!Oc7e24MFJ^9@a;;BmL6BwWYA%bPJ6F}jN1Uh)w*KOGY8%f)?>gNx zPL6zQZm!gN5VYLI)Mz34_>E_o%{ksQyNesK-`Ffe$!`njxgN@xNAi3-@l literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/oak_bookshelf_inside.png b/src/main/resources/assets/ebwizardry/textures/blocks/oak_bookshelf_inside.png new file mode 100644 index 0000000000000000000000000000000000000000..d8626b0a202d42613e108d799ec0a0709306d936 GIT binary patch literal 1301 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-gsMU!N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsJ7K7Cq^W8c*B(tz9~doJUeEXLyH}O?dj8J=y>Km(pJ)PZgKt z7$kVUm~3$6-_PBb<98fUQCzP0>&f1OPbYa#y4aDn|5MGT!p}S7&RqO)&Hs7Bg42t( z{m%ZfdjG3wivq2{JN+6LYRPgPHxy7cDT_uTkt zA2r)ft4=Z9zy9M(wmT+r=h!~Jvb3<7J42wx$-1kMv2OL@3vnvF?@OYa>h@eV3{9A9 za{AhXm+h@(`;GIumq)BPP;u*ah1G5yR$WEm3x4hK^6~vT%MX8P{Ud#8^U?BO`W}}{ zUKl3S3w%ni-8}#6>iK#T{qn?T?X@?pf2|*{6oI?v6ySBwAaHUOg(dD!_VE>d3IMpW2fINmMcoGI^s_0 zEIlem`F7-qyc25Jd5hx$>%)YL+RJrqt}a`-uAt@7sfhx%Jc&L2|5jYko?Io*c{JzI zOYIw~%9j(4DjfG|GvsyIp}+i9$%nwYxaQTYj&t++A`;AQPhj{n`%`$qP0m|Mix%Az zp5YwJ^gtRxZJfWnokO^i7cIvSqNQL1a|?pU}cZ0@QmUMbVeZ`b;HKHL4+xcFH@Gq+R?hhm$E zk*jp~Pu-kC?`S=zqhjG( zeh7K#tbC$%HS9^@SJNvI!DoA=omTE#c6R%PByZjFmZUVvBdXu6?%i?M=^&B@YW$Z9K7W*#&*W@*T@( zTs*}8InDPwTV1rh5DVMpt*L)Lq=%c8XIGg$ygDoD{%qgeiqlgsqXIb~Q+EBgwomN#++l9WnT0Qy7Jo|;v&7znCm7gYDdVXBXKh{-$ zH=FuqMcc<;3Jz#m&q-P({41dC4?jyR+uQo>8*5cB%>A=dwewTE&!X)XTP}9mVZ0t zdzh~3Jp1NdyR~+1H~+7F{X?GXk@F3G98o1bSHB;cWH%+tdBR(Z8!ErV#lkKwYcBb2 z$-uzCmgMd3!tfsi7wla=nSp_Uv%n*=n1Ml08H5=tmfDvwFfg!}c>21sKVg;-me8?X zFVD`vz#v)T8d2h$pPQSSSHj?2l$uzQnxasiS(2gP?&%v4-pD7;z`zji>Eak-aXR@7 zkKw=b4vYzj2?+@|O5dnO&bqtiisTFl4HlgfGID-mi%e>N{$oFvrW9(tD~cz!X^%{W z>HhzJm|M@cPbGAS4R$ISV_Ek$-p4mCN_KNu50QH3=9mO Lu6{1-oD!M?$=BW8O z@Nm}pjP#!8SL3G5?J27_obu|`jd!w@l1Z_`GREsVTb{9$Nt#tk3dI@k{<-Ju^-YSI zd-gd+KEC}*KJP&4ud|*{9x%JM?@pFZ_E@1EE&g!(Uhdr1;Q#aQPg}S=y_hF89h}P=yuiNK>hZgjO8G4}Zgf;x{rUCtVvzUx zp45LX3uV3ht=7Beb1qj><$9FiJ|WkFTQw;m=SjwLpWas*+@c;OF_w!`^mIaaq(lvb z#9tP~+OI5Rc@`@&gZq%&kNg=uq}E!!=Ks9-3v}K=t{f@Jjb$Z zhdy(Gph!iBA|s=;hYjndi8o!8EP@y{oU8m-@J5`dzNa{8t_Oom3gfe5^-8@9-&C3< zE3$CtAM{|6R#8f@d@g8zQ8P)S`s2B3`I;}UCZBh7I=?|6>4kPypHS=XIPbMLTR`Q6L&v_o5hO0QlINWMBNE01HV+F8^4PTjuIM!NH_h+UihS%1aF zMaP;$5#F@FTV8UempF( zs^5D3%J_@!dHj&gV^jEHeqv_dJN^@Ui>lpo&nGXQ?6_iiV)-i3l=!@J!ew7K1+NW> zs#k~)US()`ttb9jpy90FdAFkL!tU|p7S~ls{XR3LY1Q&a-&d+U-n;j0vt{e+f*;)~*i2L*MgHgjR-rjhUx) zf0c1~rT(i5zvdY=E37BlUHE#a#B}+p=BLvimMhs^ob{Yzx4@d{xw3NB=R|)^-}&wC zCU3*{Ti+D>B(IGunQgoKcv1P(%3h7ndd&NR`JBy7&#rZG@vuB7XC=2MXWOxtXP$XY zo}{i+!_m{V)8ThurRkH~i{@`!qaPaaS(N)*$fJ!}o7cVAx!kLN@%+^~dH zm4{ZcLXUfndT>_1^xAosP37(6mHm_7m3r<_*<87O+m|(bM%h0jTVj<=?pS@O+_Pu< zZh?J%yQY_Z(KvgY`)g`q-G1iS@0^C)=cTS$b)t4+1^X+#+cm3xhc3IIvh%ij=`*eO z*EWlBtE}Clw}yWUyVv4x`eM(*tp$96fA0<8VcVZWto8`4IOL$gEM)kX$x#zdv$PHir_vE3sHxH&% ztZTMNQ3#yl(m2sE%;bc2gUiQ<6|B!~85#_$6`t++|Av#7A?KQ1hSw4<$<6N19-UO_ ze&RHD=B`fvPEQtd*z8f;G_rGDc5Av*$QPZubb5KcdgiAhv0d z;j;zS_tT#_h%d~`KhpT?b&-_S=BHN{&N%c&T6xLgukjn$WKPWd|0vOn?_SWoRmWQ9 T`zkRoFfe$!`njxgN@xNAoL&Wg literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/oak_bookshelf_top.png b/src/main/resources/assets/ebwizardry/textures/blocks/oak_bookshelf_top.png new file mode 100644 index 0000000000000000000000000000000000000000..fad076fed5c7e4f197ea4b204e2e18513735f0aa GIT binary patch literal 1573 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-)>ef?lmsP~D-;yvr)B1( zDwI?fq$;FVWTr7NRNPt{mVIfh2G5`Qp=X3VD)iX}R;96T{*x(JCHKnm{@UM}njJGt zUiwXVRsY}Y7khg_LCP9_J*(vfGcp#blr+t9mnsiSyK}wymbL!ll05=EnOmyt*tQ?j zKF0bvwRd`|dg2{B$*$79C)Vp7K5IGiR-WLfwks- zc3t#)cZq_){gQ8wMjpTSAkU@pz0xnoDIYs-Uu)c^w6Q+@v(cAtKEFd4)mF~hdgyLx zOH~}_^kcCmF%N2GY7frc>a!%l+oST!W4Y_+kE=h~;v7F^e?ZljA8CI&Go={s#=kIc zyK^zFZ*y(WW#gsI+ttsXlbsiTtnxKurB&IrUsrF5MXfF_J!zqHbFJ%JZ%NxTr){Gi zYqwv1yh-az!gu3GJRY4)vbh40$098@H5@zEX6|=qN8h#rMi=E!o2ZFOv$zut8hSKb z%Zq&NcXqO$oz1skrvsnV;-Z9y56$?>TbX6@WjJ0XN<3cJyWira`sAqBoJZFj64ZV$ zNzZ-3i!+9klYE$!?}P=s<@ivyPkwGVFWXy=;sZ>RPu*pDaekWoqf5;hy`5rjeG>zwbGDq!yl#BP@_m)7Z(Q}`wdMDIKUnN9 zQetqBO}vIfu}wrZD(v+$m>^nC$V~ht_lvp7w)lmQO8UN}FhUCAmQU@>9ntmp4zZ z`t{!G*78!@i+U@>Z=G*D$8f*llH^O(PmF(p!rB$qe`T%w%b1z<(zq^0EA%D{n>Ds#|yqBrx zTefXp_}i*VdbM$amC%FdrC(Q8*)H-g-+kt}QQ0-gy%+v%t+~rR?_SY+l`r}Zedk|B z<-c@_o1XesGjspSpY6JHSMF^JIQ(mW=im7sUwvr&$iH!oq>UY!PC{xWt~$(696Qz;b;H= literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/spruce_bookshelf_inside.png b/src/main/resources/assets/ebwizardry/textures/blocks/spruce_bookshelf_inside.png index fcd2326266f4325a620579ab07e106f0ee7fff77..2a0669290866706e510899e5ac9a1e7465213847 100644 GIT binary patch delta 1060 zcmeyxc!hI8f};Gi%$!t(lFEWqh1817GzNx>TUV#YmVMUXX?wr5 zE0SrFfc@!%3Fl_Yp852gRoFapa^1@-=E+&jtQ-s3PinsX|L5*Y{g_9K)bkBnWtt8@ z-F!aig2%fZr)oBR{=75p&f1R(%QrhK1YN1xvuwi?>nBYgH$5q>f2ld)_S{8#*7v>L zxLC(^(lw^-qSu{H9cRkZT((JM<(jE~Kkk#gU8K0YVt?zdW8bgZ-))$E>~Qv!gvQ0k zFCQ*7OxZHoLjK`)GoH7hpJt@r4*K$PZk{u%*~xWl_|6;XTzRawL)1S|HaYFK$-|SU z+Lta+eWG;h=d$K8;Pq=J*>EzqI zrvm4u%0F2;*L%*B7j5gmPpi7$e^*p~!_y6BrMFMzB;GOGW17x7rQ>#t)HylT#VRc> zM=i4~Ql!0}o?+^lYiq7|r*eMjQ!e4XZ-fH7Lju>aBr^5rh|Y-ot8qs)>l)jMMWV+Y z>lLS77Mpka+0K}!Ev?e(hh8RXB>VgfU0BZc)sCs_=F-dI8@%0?9PD!Jn<$sZC?#|h8@X@w~j6cjD_a9y=yd`PDLOZ4z$QXN}IAUgXj#sSE`kPmMqyF){Z+oWqExxw(cb@H+8^PzVoc7F3{2Nr~&?{~_bIGv-6+z7=$7XZM zy*$EZEFgTIL5`g^k4W~bJ@C=rrMw3zcsIAJ_cep` z7VDk8Zl#)9b1?qOm*tH4*LtV%3w-;3a>C|i@lr*%9Pdj_>Sq)BC;!*?eWT#q-Z_`T zqyKomy*Ew%#)pW@yP55CviyHu$c|Q#3!eCN@5B5*v#Mh+Z|*Y<{umi2=lMH_<)T2^ z>Sbk%R(HH?tUv6(O!%@PXOaUluioFMf8Je_PJV+s5`mJ2kTYr+3u}?Nbqd zDwrkybmD!rZI}5jb${ZR3dK92^hejqln47&`X}tx=nvSXk##?K$&aH=_2Dn$wbon^ z4e!5xj)8%JEy>&6h2cL4F4((#G6MqxXMsm#F$061G6RE@o-$*_Qu{In1_t&LPhVH| SC(PpFR@@1-A;%_0&j$ec%Lm5* delta 41 xcmcb@`HOLavH$~HlDE4HgCv6y1IOO>?+%lBS>!pHWO>o0&pZ$AGu(M!?%ujAcK!8^jvaUN&aq_8 zVf^u-nSp_cfrCMTp#hC?q1Rb^{|Sq}qsDEC9hLLWRvw)5`RAL<$_%BvhXcNRzHD;T z=KQlg34Qu~J6RZ57z7v`K%s!0kq~_L4YNd>;<8Mm<&!OI+VcC^>z_|zmu~zu>+`u@ z2RZ(Kjp+=p<-<5<*Tk70cT@O|+bF0lH>UJDJll|bHixC@f2#5XZ(O!BGJJdWayz5# zrrUGGp6&S@_+&TVfnRJ>KCH+s_Imzf+A5qHK^_rcXlGzyxUq2gZw4DBkO3R!F7IdG zE_h;&*fX0vmQNKQ#OE@u$#>wC&Of6$H(7Z?HAW~Rg{uMsA0r3D24^#-nV0{_Y)e+2 zV0rfZM!^@p=?t%>0}dUNF7|qUgX4)DmbieM!qMQ&z{K!oLa)O!6Z`hJHg`E#bTKcC+9Kkfn1) z1QV47Y%6bYv_K-|!Ds*fD<2C8P7LCbZcI3y&S1uLQP>=u+Zbp9ei+1uE!6v?Azt7D OGT+nH&t;ucLK6U+T`oTW literal 0 HcmV?d00001 From 02480db158a7d45859641d65a4904019d7559963 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 25 Mar 2020 15:05:32 +0000 Subject: [PATCH 005/415] Reorganise config file --- .../java/electroblob/wizardry/Settings.java | 213 ++++++++++-------- .../client/gui/config/GuiConfigWizardry.java | 24 +- .../assets/ebwizardry/lang/en_gb.lang | 52 +++-- .../assets/ebwizardry/lang/en_us.lang | 52 +++-- 4 files changed, 210 insertions(+), 131 deletions(-) diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index bafabf8f..e3d2afce 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -59,6 +59,10 @@ public final class Settings { // Category names /** The unlocalised name of the gameplay config category. */ public static final String GAMEPLAY_CATEGORY = "gameplay"; + /** The unlocalised name of the difficulty config category. */ + public static final String DIFFICULTY_CATEGORY = "difficulty"; + /** The unlocalised name of the tweaks config category. */ + public static final String TWEAKS_CATEGORY = "tweaks"; /** The unlocalised name of the spells config category. */ public static final String SPELLS_CATEGORY = "spells"; /** The unlocalised name of the resistances config category. */ @@ -97,37 +101,37 @@ public final class Settings { public int towerRarity = 700; /** [Server-only] List of structure file locations for wizard towers without loot chests. */ public ResourceLocation[] towerFiles = {new ResourceLocation(Wizardry.MODID, "wizard_tower_0"), - new ResourceLocation(Wizardry.MODID, "wizard_tower_1"), - new ResourceLocation(Wizardry.MODID, "wizard_tower_2"), - new ResourceLocation(Wizardry.MODID, "wizard_tower_3")}; + new ResourceLocation(Wizardry.MODID, "wizard_tower_1"), + new ResourceLocation(Wizardry.MODID, "wizard_tower_2"), + new ResourceLocation(Wizardry.MODID, "wizard_tower_3")}; /** [Server-only] List of structure file locations for wizard towers with loot chests. */ public ResourceLocation[] towerWithChestFiles = {new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_0"), - new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_1"), - new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_2"), - new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_3")}; + new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_1"), + new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_2"), + new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_3")}; /** [Server-only] List of dimension ids in which to generate obelisks. */ public int[] obeliskDimensions = {0, -1}; /** [Server-only] The rarity of obelisks, used by the world generator. Larger numbers are rarer. */ public int obeliskRarity = 600; /** [Server-only] List of structure file locations for obelisks. */ public ResourceLocation[] obeliskFiles = {new ResourceLocation(Wizardry.MODID, "obelisk_0"), - new ResourceLocation(Wizardry.MODID, "obelisk_1"), - new ResourceLocation(Wizardry.MODID, "obelisk_2"), - new ResourceLocation(Wizardry.MODID, "obelisk_3"), - new ResourceLocation(Wizardry.MODID, "obelisk_4")}; + new ResourceLocation(Wizardry.MODID, "obelisk_1"), + new ResourceLocation(Wizardry.MODID, "obelisk_2"), + new ResourceLocation(Wizardry.MODID, "obelisk_3"), + new ResourceLocation(Wizardry.MODID, "obelisk_4")}; /** [Server-only] List of dimension ids in which to generate shrines. */ public int[] shrineDimensions = {0, -1}; /** [Server-only] The rarity of shrines, used by the world generator. Larger numbers are rarer. */ public int shrineRarity = 1100; /** [Server-only] List of structure file locations for shrines. */ public ResourceLocation[] shrineFiles = {new ResourceLocation(Wizardry.MODID, "shrine_0"), - new ResourceLocation(Wizardry.MODID, "shrine_1"), - new ResourceLocation(Wizardry.MODID, "shrine_2"), - new ResourceLocation(Wizardry.MODID, "shrine_3"), - new ResourceLocation(Wizardry.MODID, "shrine_4"), - new ResourceLocation(Wizardry.MODID, "shrine_5"), - new ResourceLocation(Wizardry.MODID, "shrine_6"), - new ResourceLocation(Wizardry.MODID, "shrine_7")}; + new ResourceLocation(Wizardry.MODID, "shrine_1"), + new ResourceLocation(Wizardry.MODID, "shrine_2"), + new ResourceLocation(Wizardry.MODID, "shrine_3"), + new ResourceLocation(Wizardry.MODID, "shrine_4"), + new ResourceLocation(Wizardry.MODID, "shrine_5"), + new ResourceLocation(Wizardry.MODID, "shrine_6"), + new ResourceLocation(Wizardry.MODID, "shrine_7")}; /** [Server-only] List of solid blocks (usually trees) which are ignored by the structure generators. */ public Pair[] treeBlocks = parseItemMetaStrings(DEFAULT_TREE_BLOCKS); /** [Server-only] The chance for wizard towers to generate with an evil wizard and chest inside. */ @@ -147,8 +151,8 @@ public final class Settings { public ResourceLocation[] mobLootTableWhitelist = {}; /** [Server-only] Blacklist for loot tables to inject additional mob drops into. */ public ResourceLocation[] mobLootTableBlacklist = toResourceLocations("entities/vex", "entities/ender_dragon", - "entities/wither", "entities/silverfish", "entities/endermite", - Wizardry.MODID + "entities/evil_wizard"); + "entities/wither", "entities/silverfish", "entities/endermite", + Wizardry.MODID + "entities/evil_wizard"); /** * [Server-only] Whether or not players can teleport through unbreakable blocks (e.g. bedrock) using the * phase step spell. @@ -168,8 +172,10 @@ public final class Settings { public boolean playerBlockDamage = true; /** [Server-only] Whether to revert to the old wand upgrade system, which only requires tomes of arcana. */ public boolean legacyWandLevelling = false; - /** [Server-only] Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, - * velocity-based one. */ + /** + * [Server-only] Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, + * velocity-based one. + */ public boolean replaceVanillaFallDamage = true; /** [Server-only] Whether using bonemeal on grass blocks has a chance to grow crystal flowers. */ public boolean bonemealGrowsCrystalFlowers = true; @@ -322,7 +328,7 @@ public final class Settings { /** Constant array storing the names of each of the constants, in the order they are declared. */ public static final String[] names; - static { + static{ names = new String[values().length]; for(GuiPosition position : values()){ names[position.ordinal()] = position.name; @@ -377,7 +383,9 @@ public final class Settings { Wizardry.logger.info("Setting up main config"); setupGeneralConfig(); + setupDifficultyConfig(); setupWorldgenConfig(); + setupTweaksConfig(); if(event.getSide() == Side.CLIENT) setupClientConfig(); // Server has no spell HUD skins so this would crash it setupCommandsConfig(); setupCompatibilityConfig(); @@ -457,32 +465,18 @@ public final class Settings { config.addCustomCategoryComment(GAMEPLAY_CATEGORY, "Global settings that affect game mechanics. In multiplayer, the server/LAN host settings will apply. Please note that changing some of these settings may make the mod very difficult to play."); - property = config.get(GAMEPLAY_CATEGORY, "discoveryMode", true, - "For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false."); - property.setLanguageKey("config." + Wizardry.MODID + ".discovery_mode"); + property = config.get(GAMEPLAY_CATEGORY, "playerBlockDamage", true, + "Whether spells cast by players can destroy blocks in the world. Set to false to prevent griefing. To prevent non-players from destroying blocks with magic, use the mobGriefing gamerule."); + property.setLanguageKey("config." + Wizardry.MODID + ".player_block_damage"); Wizardry.proxy.setToNamedBooleanEntry(property); - property.setRequiresWorldRestart(true); - discoveryMode = property.getBoolean(); + playerBlockDamage = property.getBoolean(); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "legacyWandLevelling", false, - "Controls whether wands are required to gain progression before they can be upgraded to the next tier. Enable this option to revert to the pre-4.2 system, which only requires tomes of arcana. Wands will still gain progression even when this is enabled, so if you go back to the new system you won't lose any progress."); - property.setLanguageKey("config." + Wizardry.MODID + ".legacy_wand_levelling"); + property = config.get(GAMEPLAY_CATEGORY, "bonemealGrowsCrystalFlowers", true, + "Whether using bonemeal on grass blocks has a chance to grow crystal flowers."); + property.setLanguageKey("config." + Wizardry.MODID + ".bonemeal_grows_crystal_flowers"); Wizardry.proxy.setToNamedBooleanEntry(property); - legacyWandLevelling = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(GAMEPLAY_CATEGORY, "friendlyFire", FriendlyFire.ALL.name, "Controls which creatures may be damaged by your magic when allied to you. Your spells will not target your allies or creatures summoned/owned by them regardless of this setting, but this setting prevents all magic damage to allies.", FriendlyFire.names); - property.setLanguageKey("config." + Wizardry.MODID + ".friendly_fire"); - friendlyFire = FriendlyFire.fromName(property.getString()); - propOrder.add(property.getName()); - - property = config.get(GAMEPLAY_CATEGORY, "minionRevengeTargeting", true, - "Whether summoned creatures can revenge attack their owner if their owner attacks them."); - property.setLanguageKey("config." + Wizardry.MODID + ".minion_revenge_targeting"); - Wizardry.proxy.setToNamedBooleanEntry(property); - property.setRequiresWorldRestart(false); - minionRevengeTargeting = property.getBoolean(); + bonemealGrowsCrystalFlowers = property.getBoolean(); propOrder.add(property.getName()); property = config.get(GAMEPLAY_CATEGORY, "playersMoveEachOther", true, @@ -492,13 +486,6 @@ public final class Settings { playersMoveEachOther = property.getBoolean(); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "playerBlockDamage", true, - "Whether spells cast by players can destroy blocks in the world. Set to false to prevent griefing. To prevent non-players from destroying blocks with magic, use the mobGriefing gamerule."); - property.setLanguageKey("config." + Wizardry.MODID + ".player_block_damage"); - Wizardry.proxy.setToNamedBooleanEntry(property); - playerBlockDamage = property.getBoolean(); - propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "telekineticDisarmament", true, "Whether to allow players to disarm other players using the telekinesis spell. Set to false to prevent stealing of items."); property.setLanguageKey("config." + Wizardry.MODID + ".telekinetic_disarmament"); @@ -520,27 +507,6 @@ public final class Settings { worldTimeManipulation = property.getBoolean(); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "replaceVanillaFireballs", true, - "Whether to replace Minecraft's own fireballs with wizardry fireballs. If this is disabled, only wizardry spells will use the custom fireballs."); - property.setLanguageKey("config." + Wizardry.MODID + ".replace_vanilla_fireballs"); - Wizardry.proxy.setToNamedBooleanEntry(property); - replaceVanillaFireballs = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(GAMEPLAY_CATEGORY, "replaceVanillaFallDamage", true, - "Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells will use a more simplistic method of resetting the player's fall damage in certain cases."); - property.setLanguageKey("config." + Wizardry.MODID + ".replace_vanilla_fall_damage"); - Wizardry.proxy.setToNamedBooleanEntry(property); - replaceVanillaFallDamage = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(GAMEPLAY_CATEGORY, "creativeBypassesArcaneLock", true, - "Whether any player in creative mode can bypass arcane-locked blocks. If this is false, players must also be op in order to do so."); - property.setLanguageKey("config." + Wizardry.MODID + ".creative_bypasses_arcane_lock"); - Wizardry.proxy.setToNamedBooleanEntry(property); - creativeBypassesArcaneLock = property.getBoolean(); - propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "slowTimeAffectsPlayers", true, "Whether players are slowed when another nearby player uses the slow time spell. If this is false, mobs and projectiles will still be affected but players will move at normal speed."); property.setLanguageKey("config." + Wizardry.MODID + ".slow_time_affects_players"); @@ -548,11 +514,11 @@ public final class Settings { slowTimeAffectsPlayers = property.getBoolean(); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "bonemealGrowsCrystalFlowers", true, - "Whether using bonemeal on grass blocks has a chance to grow crystal flowers."); - property.setLanguageKey("config." + Wizardry.MODID + ".bonemeal_grows_crystal_flowers"); + property = config.get(GAMEPLAY_CATEGORY, "creativeBypassesArcaneLock", true, + "Whether any player in creative mode can bypass arcane-locked blocks. If this is false, players must also be op in order to do so."); + property.setLanguageKey("config." + Wizardry.MODID + ".creative_bypasses_arcane_lock"); Wizardry.proxy.setToNamedBooleanEntry(property); - bonemealGrowsCrystalFlowers = property.getBoolean(); + creativeBypassesArcaneLock = property.getBoolean(); propOrder.add(property.getName()); property = config.get(GAMEPLAY_CATEGORY, "mobLootTableWhitelist", new String[0], "Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them."); @@ -608,7 +574,48 @@ public final class Settings { lightningWraithSpawnRate = property.getInt(); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "forfeitChance", 0.2, + config.setCategoryPropertyOrder(GAMEPLAY_CATEGORY, propOrder); + + } + + private void setupDifficultyConfig(){ + + List propOrder = new ArrayList<>(); + + Property property; + + config.addCustomCategoryComment(DIFFICULTY_CATEGORY, "Settings that affect the mod's difficulty. In multiplayer, the server/LAN host settings will apply."); + + property = config.get(DIFFICULTY_CATEGORY, "discoveryMode", true, + "For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false."); + property.setLanguageKey("config." + Wizardry.MODID + ".discovery_mode"); + Wizardry.proxy.setToNamedBooleanEntry(property); + property.setRequiresWorldRestart(true); + discoveryMode = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(DIFFICULTY_CATEGORY, "legacyWandLevelling", false, + "Controls whether wands are required to gain progression before they can be upgraded to the next tier. Enable this option to revert to the pre-4.2 system, which only requires tomes of arcana. Wands will still gain progression even when this is enabled, so if you go back to the new system you won't lose any progress."); + property.setLanguageKey("config." + Wizardry.MODID + ".legacy_wand_levelling"); + Wizardry.proxy.setToNamedBooleanEntry(property); + legacyWandLevelling = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(DIFFICULTY_CATEGORY, "friendlyFire", FriendlyFire.ALL.name, + "Controls which creatures may be damaged by your magic when allied to you. Your spells will not target your allies or creatures summoned/owned by them regardless of this setting, but this setting prevents all magic damage to allies.", FriendlyFire.names); + property.setLanguageKey("config." + Wizardry.MODID + ".friendly_fire"); + friendlyFire = FriendlyFire.fromName(property.getString()); + propOrder.add(property.getName()); + + property = config.get(DIFFICULTY_CATEGORY, "minionRevengeTargeting", true, + "Whether summoned creatures can revenge attack their owner if their owner attacks them."); + property.setLanguageKey("config." + Wizardry.MODID + ".minion_revenge_targeting"); + Wizardry.proxy.setToNamedBooleanEntry(property); + property.setRequiresWorldRestart(false); + minionRevengeTargeting = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(DIFFICULTY_CATEGORY, "forfeitChance", 0.2, "The chance to 'misread' an undiscovered spell and trigger a forfeit instead. Setting this to 0 effectively disables the forfeit mechanic. Has no effect if discovery mode is disabled.", 0, 1); property.setLanguageKey("config." + Wizardry.MODID + ".forfeit_chance"); @@ -619,65 +626,91 @@ public final class Settings { // These two aren't sliders because using a slider makes it difficult to fine-tune the numbers; the nature of a // scaling factor means that 0.5 is as big a change as 2.0, so whilst a slider is fine for increasing the // damage, it doesn't give fine enough control for values less than 1. - property = config.get(GAMEPLAY_CATEGORY, "playerDamageScaling", 1.0, + property = config.get(DIFFICULTY_CATEGORY, "playerDamageScaling", 1.0, "Global damage scaling factor for the damage dealt by players casting spells, relative to 1.", 0, 255); property.setLanguageKey("config." + Wizardry.MODID + ".player_damage_scaling"); playerDamageScale = property.getDouble(); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "npcDamageScaling", 1.0, + property = config.get(DIFFICULTY_CATEGORY, "npcDamageScaling", 1.0, "Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1.", 0, 255); property.setLanguageKey("config." + Wizardry.MODID + ".npc_damage_scaling"); npcDamageScale = property.getDouble(); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "summonedCreatureTargetsWhitelist", new String[0], + config.setCategoryPropertyOrder(DIFFICULTY_CATEGORY, propOrder); + + } + + private void setupTweaksConfig(){ + + List propOrder = new ArrayList<>(); + + Property property; + + config.addCustomCategoryComment(TWEAKS_CATEGORY, "Assorted settings for tweaking the mod's behaviour. In multiplayer, the server/LAN host settings will apply."); + + property = config.get(TWEAKS_CATEGORY, "replaceVanillaFireballs", true, + "Whether to replace Minecraft's own fireballs with wizardry fireballs. If this is disabled, only wizardry spells will use the custom fireballs."); + property.setLanguageKey("config." + Wizardry.MODID + ".replace_vanilla_fireballs"); + Wizardry.proxy.setToNamedBooleanEntry(property); + replaceVanillaFireballs = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(TWEAKS_CATEGORY, "replaceVanillaFallDamage", true, + "Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells will use a more simplistic method of resetting the player's fall damage in certain cases."); + property.setLanguageKey("config." + Wizardry.MODID + ".replace_vanilla_fall_damage"); + Wizardry.proxy.setToNamedBooleanEntry(property); + replaceVanillaFallDamage = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(TWEAKS_CATEGORY, "summonedCreatureTargetsWhitelist", new String[0], "List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. SoundLoopSpellEntity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. " + Wizardry.MODID + ":wizard)."); property.setLanguageKey("config." + Wizardry.MODID + ".summoned_creature_targets_whitelist"); property.setRequiresWorldRestart(true); summonedCreatureTargetsWhitelist = getResourceLocationList(property); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "summonedCreatureTargetsBlacklist", new String[]{"creeper"}, + property = config.get(TWEAKS_CATEGORY, "summonedCreatureTargetsBlacklist", new String[]{"creeper"}, "List of names of entities which summoned creatures and wizards are specifically not allowed to attack, overriding the defaults and the whitelist. Add creatures to this list if allowing them to be attacked causes problems or is too destructive (removing creepers from this list is done at your own risk!). SoundLoopSpellEntity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. " + Wizardry.MODID + ":wizard)."); property.setLanguageKey("config." + Wizardry.MODID + ".summoned_creature_targets_blacklist"); property.setRequiresWorldRestart(true); summonedCreatureTargetsBlacklist = getResourceLocationList(property); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "mindControlTargetsBlacklist", new String[]{}, + property = config.get(TWEAKS_CATEGORY, "mindControlTargetsBlacklist", new String[]{}, "List of names of entities which cannot be mind controlled, in addition to the defaults. Add creatures to this list if allowing them to be mind-controlled causes problems or could be exploited. SoundLoopSpellEntity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. " + Wizardry.MODID + ":wizard)."); property.setLanguageKey("config." + Wizardry.MODID + ".mind_control_targets_blacklist"); property.setRequiresWorldRestart(true); mindControlTargetsBlacklist = getResourceLocationList(property); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "pocketFurnaceItemBlacklist", new String[]{"cobblestone", "netherrack"}, + property = config.get(TWEAKS_CATEGORY, "pocketFurnaceItemBlacklist", new String[]{"cobblestone", "netherrack"}, "List of registry names of blocks or items which cannot be smelted by the pocket furnace spell, in addition to armour, tools and weapons. Block/item names are not case sensitive. For mod items, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore)."); property.setLanguageKey("config." + Wizardry.MODID + ".pocket_furnace_item_blacklist"); property.setRequiresWorldRestart(true); pocketFurnaceItemBlacklist = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "divinationOreWhitelist", new String[0], "List of registry names of ore blocks which can be detected by the divination spell. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore)."); + property = config.get(TWEAKS_CATEGORY, "divinationOreWhitelist", new String[0], "List of registry names of ore blocks which can be detected by the divination spell. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore)."); property.setLanguageKey("config." + Wizardry.MODID + ".divination_ore_whitelist"); property.setRequiresWorldRestart(true); divinationOreWhitelist = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "swordItemWhitelist", new String[0], "List of registry names of items which should count as swords for imbuement spells. Most swords 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:broadsword)."); + property = config.get(TWEAKS_CATEGORY, "swordItemWhitelist", new String[0], "List of registry names of items which should count as swords for imbuement spells. Most swords 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:broadsword)."); property.setLanguageKey("config." + Wizardry.MODID + ".sword_item_whitelist"); property.setRequiresWorldRestart(true); swordItemWhitelist = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "bowItemWhitelist", new String[0], "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)."); + property = config.get(TWEAKS_CATEGORY, "bowItemWhitelist", new String[0], "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)."); property.setLanguageKey("config." + Wizardry.MODID + ".bow_item_whitelist"); property.setRequiresWorldRestart(true); bowItemWhitelist = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade.", + property = config.get(TWEAKS_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade.", Pattern.compile("[A-Za-z0-9:_]+ [0-9]+")); property.setLanguageKey("config." + Wizardry.MODID + ".currency_items"); property.setRequiresWorldRestart(true); @@ -690,14 +723,14 @@ public final class Settings { Wizardry.logger.warn("Invalid entry in currency items: {}", string); continue; // Ignore invalid entries, the pattern above should ensure this never happens } - try { + try{ currencyItems.put(parseItemMetaString(args[0]), Integer.parseInt(args[1])); }catch(NumberFormatException e){ Wizardry.logger.warn("Invalid integer in currency items: {}", args[1]); } } - config.setCategoryPropertyOrder(GAMEPLAY_CATEGORY, propOrder); + config.setCategoryPropertyOrder(TWEAKS_CATEGORY, propOrder); } @@ -793,7 +826,7 @@ public final class Settings { shrineFiles = getResourceLocationList(property); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "treeBlocks", DEFAULT_TREE_BLOCKS, "List of registry names of blocks which can be overwritten by wizardry's structure generators, affecting both fast and fancy structure generation. Most tree blocks and other foliage should work automatically, but those that don't can be added manually here. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. dynamictrees:oakbranch)."); + property = config.get(WORLDGEN_CATEGORY, "treeBlocks", DEFAULT_TREE_BLOCKS, "List of registry names of blocks which can be overwritten by wizardry's structure generators, affecting both fast and fancy structure generation. Most tree blocks and other foliage should work automatically, but those that don't can be added manually here. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. dynamictrees:oakbranch)."); property.setLanguageKey("config." + Wizardry.MODID + ".tree_blocks"); property.setRequiresWorldRestart(true); treeBlocks = parseItemMetaStrings(property.getStringList()); diff --git a/src/main/java/electroblob/wizardry/client/gui/config/GuiConfigWizardry.java b/src/main/java/electroblob/wizardry/client/gui/config/GuiConfigWizardry.java index 7f76ad1d..6108ae2c 100644 --- a/src/main/java/electroblob/wizardry/client/gui/config/GuiConfigWizardry.java +++ b/src/main/java/electroblob/wizardry/client/gui/config/GuiConfigWizardry.java @@ -25,10 +25,12 @@ public class GuiConfigWizardry extends GuiConfig { private static List getConfigEntries(){ - List configList = new ArrayList(1); + List configList = new ArrayList<>(1); configList.add(new DummyCategoryElement("gameplayConfig", "config." + Wizardry.MODID + ".category." + Settings.GAMEPLAY_CATEGORY, GameplayCategory.class)); + configList.add(new DummyCategoryElement("difficultyConfig", "config." + Wizardry.MODID + ".category." + Settings.DIFFICULTY_CATEGORY, DifficultyCategory.class)); configList.add(new DummyCategoryElement("worldgenConfig", "config." + Wizardry.MODID + ".category." + Settings.WORLDGEN_CATEGORY, WorldgenCategory.class)); + configList.add(new DummyCategoryElement("tweaksConfig", "config." + Wizardry.MODID + ".category." + Settings.TWEAKS_CATEGORY, TweaksCategory.class)); configList.add(new DummyCategoryElement("commandsConfig", "config." + Wizardry.MODID + ".category." + Settings.COMMANDS_CATEGORY, CommandsCategory.class)); configList.add(new DummyCategoryElement("clientConfig", "config." + Wizardry.MODID + ".category." + Settings.CLIENT_CATEGORY, ClientCategory.class)); configList.add(new DummyCategoryElement("spellsConfig", "config." + Wizardry.MODID + ".category." + Settings.SPELLS_CATEGORY, SpellsCategory.class)); @@ -79,6 +81,16 @@ public class GuiConfigWizardry extends GuiConfig { @Override protected String getCategory() { return Settings.GAMEPLAY_CATEGORY; } } + /** Difficulty category of the config gui. */ + public static class DifficultyCategory extends CategoryBase { + + public DifficultyCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){ + super(owningScreen, owningEntryList, prop); + } + + @Override protected String getCategory() { return Settings.DIFFICULTY_CATEGORY; } + } + /** Worldgen category of the config gui. */ public static class WorldgenCategory extends CategoryBase { @@ -89,6 +101,16 @@ public class GuiConfigWizardry extends GuiConfig { @Override protected String getCategory() { return Settings.WORLDGEN_CATEGORY; } } + /** Tweaks category of the config gui. */ + public static class TweaksCategory extends CategoryBase { + + public TweaksCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){ + super(owningScreen, owningEntryList, prop); + } + + @Override protected String getCategory() { return Settings.TWEAKS_CATEGORY; } + } + /** Commands category of the config gui. */ public static class CommandsCategory extends CategoryBase { diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 06f9bbb7..993845ae 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -1093,18 +1093,6 @@ config.ebwizardry.category.gameplay.tooltip=Configure wizardry's general gamepla config.ebwizardry.title.gameplay=Gameplay Settings config.ebwizardry.subtitle.gameplay=Global settings that affect game mechanics. -config.ebwizardry.discovery_mode=Discovery Mode -config.ebwizardry.discovery_mode.tooltip=For those who like a sense of mystery! When enabled, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is disabled. -config.ebwizardry.legacy_wand_levelling=Legacy Wand Levelling -config.ebwizardry.legacy_wand_levelling.tooltip=Controls whether wands are required to gain progression before they can be upgraded to the next tier. Enable this option to revert to the pre-4.2 system, which only requires tomes of arcana. Wands will still gain progression silently when this is enabled, so if you go back to the new system you won't lose any progress. -config.ebwizardry.legacy_wand_levelling.true=Yes - I liked it how it was! -config.ebwizardry.legacy_wand_levelling.false=No - Level me up! -config.ebwizardry.friendly_fire=Friendly Fire -config.ebwizardry.friendly_fire.tooltip=Controls which creatures may be damaged by your magic when allied to you. Your spells will not target your allies or creatures summoned/owned by them regardless of this setting, but this setting makes them completely immune if disabled. -config.ebwizardry.minion_revenge_targeting=Minion Revenge Targeting -config.ebwizardry.minion_revenge_targeting.tooltip=Whether summoned creatures can revenge-attack players or creatures that they would not otherwise be able to attack. -config.ebwizardry.minion_revenge_targeting.true=Yes - I'd never hurt them! -config.ebwizardry.minion_revenge_targeting.false=No - they must always obey me! config.ebwizardry.players_move_each_other=Players Move Each Other config.ebwizardry.players_move_each_other.tooltip=Whether to allow players to move other players around using magic. config.ebwizardry.players_move_each_other.true=Yes - let the games begin! @@ -1125,14 +1113,6 @@ config.ebwizardry.world_time_manipulation=World Time Manipulation config.ebwizardry.world_time_manipulation.tooltip=Whether players are allowed to change the world time with the speed time spell. If disabled, the speed time spell will not change the world time but will still speed up nearby block, entity and tile entity ticks. config.ebwizardry.world_time_manipulation.true=Yes - daytime, nighttime... config.ebwizardry.world_time_manipulation.false=No - I need my sleep! -config.ebwizardry.replace_vanilla_fireballs=Replace Vanilla Fireballs -config.ebwizardry.replace_vanilla_fireballs.tooltip=Whether to replace Minecraft's own fireballs with wizardry fireballs. If this is disabled, only wizardry spells will use the custom fireballs. -config.ebwizardry.replace_vanilla_fireballs.true=Yes - give me FIRE! -config.ebwizardry.replace_vanilla_fireballs.false=No - blazes are mean enough -config.ebwizardry.replace_vanilla_fall_damage=Replace Vanilla Fall Damage -config.ebwizardry.replace_vanilla_fall_damage.tooltip=Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells revert to a more simplistic method of resetting the player's fall damage in certain cases. -config.ebwizardry.replace_vanilla_fall_damage.true=Yes - it's parkour time -config.ebwizardry.replace_vanilla_fall_damage.false=No - break my legs normally config.ebwizardry.creative_bypasses_arcane_lock=Bypass Arcane Lock config.ebwizardry.creative_bypasses_arcane_lock.tooltip=Determines which players can bypass arcane locks. config.ebwizardry.creative_bypasses_arcane_lock.true=Anyone in creative mode @@ -1155,12 +1135,44 @@ config.ebwizardry.ice_wraith_spawn_rate=Ice Wraith Spawn Rate config.ebwizardry.ice_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned ice wraiths; higher numbers mean more ice wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable ice wraith spawning entirely. config.ebwizardry.lightning_wraith_spawn_rate=Lightning Wraith Spawn Rate config.ebwizardry.lightning_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned lightning wraiths; higher numbers mean more lightning wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable lightning wraith spawning entirely. + +config.ebwizardry.category.difficulty=Difficulty Settings +config.ebwizardry.category.difficulty.tooltip=Configure wizardry's difficulty +config.ebwizardry.title.difficulty=Difficulty Settings +config.ebwizardry.subtitle.difficulty=Settings that affect the mod's difficulty. + +config.ebwizardry.discovery_mode=Discovery Mode +config.ebwizardry.discovery_mode.tooltip=For those who like a sense of mystery! When enabled, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is disabled. +config.ebwizardry.legacy_wand_levelling=Legacy Wand Levelling +config.ebwizardry.legacy_wand_levelling.tooltip=Controls whether wands are required to gain progression before they can be upgraded to the next tier. Enable this option to revert to the pre-4.2 system, which only requires tomes of arcana. Wands will still gain progression silently when this is enabled, so if you go back to the new system you won't lose any progress. +config.ebwizardry.legacy_wand_levelling.true=Yes - I liked it how it was! +config.ebwizardry.legacy_wand_levelling.false=No - Level me up! +config.ebwizardry.friendly_fire=Friendly Fire +config.ebwizardry.friendly_fire.tooltip=Controls which creatures may be damaged by your magic when allied to you. Your spells will not target your allies or creatures summoned/owned by them regardless of this setting, but this setting makes them completely immune if disabled. +config.ebwizardry.minion_revenge_targeting=Minion Revenge Targeting +config.ebwizardry.minion_revenge_targeting.tooltip=Whether summoned creatures can revenge-attack players or creatures that they would not otherwise be able to attack. +config.ebwizardry.minion_revenge_targeting.true=Yes - I'd never hurt them! +config.ebwizardry.minion_revenge_targeting.false=No - they must always obey me! config.ebwizardry.forfeit_chance=Forfeit Chance config.ebwizardry.forfeit_chance.tooltip=The chance to 'misread' an undiscovered spell and trigger a forfeit instead. Setting this to 0 effectively disables the forfeit mechanic. Has no effect if discovery mode is disabled. config.ebwizardry.player_damage_scaling=Player Damage Scaling Factor config.ebwizardry.player_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by players casting spells, relative to 1. config.ebwizardry.npc_damage_scaling=NPC Damage Scaling Factor config.ebwizardry.npc_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1. + +config.ebwizardry.category.tweaks=Tweaks Settings +config.ebwizardry.category.tweaks.tooltip=Tweak wizardry's behaviour +config.ebwizardry.title.tweaks=Tweaks Settings +config.ebwizardry.subtitle.tweaks=Assorted settings for tweaking the mod's behaviour. + +config.ebwizardry.replace_vanilla_fireballs=Replace Vanilla Fireballs +config.ebwizardry.replace_vanilla_fireballs.tooltip=Whether to replace Minecraft's own fireballs with wizardry fireballs. If this is disabled, only wizardry spells will use the custom fireballs. +config.ebwizardry.replace_vanilla_fireballs.true=Yes - give me FIRE! +config.ebwizardry.replace_vanilla_fireballs.false=No - blazes are mean enough +config.ebwizardry.replace_vanilla_fall_damage=Replace Vanilla Fall Damage +config.ebwizardry.replace_vanilla_fall_damage.tooltip=Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells revert to a more simplistic method of resetting the player's fall damage in certain cases. +config.ebwizardry.replace_vanilla_fall_damage.true=Yes - it's parkour time +config.ebwizardry.replace_vanilla_fall_damage.false=No - break my legs normally config.ebwizardry.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist config.ebwizardry.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. ebwizardry\:wizard). config.ebwizardry.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 8714e6dd..4913dd6e 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -1093,18 +1093,6 @@ config.ebwizardry.category.gameplay.tooltip=Configure wizardry's general gamepla config.ebwizardry.title.gameplay=Gameplay Settings config.ebwizardry.subtitle.gameplay=Global settings that affect game mechanics. -config.ebwizardry.discovery_mode=Discovery Mode -config.ebwizardry.discovery_mode.tooltip=For those who like a sense of mystery! When enabled, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is disabled. -config.ebwizardry.legacy_wand_levelling=Legacy Wand Levelling -config.ebwizardry.legacy_wand_levelling.tooltip=Controls whether wands are required to gain progression before they can be upgraded to the next tier. Enable this option to revert to the pre-4.2 system, which only requires tomes of arcana. Wands will still gain progression silently when this is enabled, so if you go back to the new system you won't lose any progress. -config.ebwizardry.legacy_wand_levelling.true=Yes - I liked it how it was! -config.ebwizardry.legacy_wand_levelling.false=No - Level me up! -config.ebwizardry.friendly_fire=Friendly Fire -config.ebwizardry.friendly_fire.tooltip=Controls which creatures may be damaged by your magic when allied to you. Your spells will not target your allies or creatures summoned/owned by them regardless of this setting, but this setting makes them completely immune if disabled. -config.ebwizardry.minion_revenge_targeting=Minion Revenge Targeting -config.ebwizardry.minion_revenge_targeting.tooltip=Whether summoned creatures can revenge-attack players or creatures that they would not otherwise be able to attack. -config.ebwizardry.minion_revenge_targeting.true=Yes - I'd never hurt them! -config.ebwizardry.minion_revenge_targeting.false=No - they must always obey me! config.ebwizardry.players_move_each_other=Players Move Each Other config.ebwizardry.players_move_each_other.tooltip=Whether to allow players to move other players around using magic. config.ebwizardry.players_move_each_other.true=Yes - let the games begin! @@ -1125,14 +1113,6 @@ config.ebwizardry.world_time_manipulation=World Time Manipulation config.ebwizardry.world_time_manipulation.tooltip=Whether players are allowed to change the world time with the speed time spell. If disabled, the speed time spell will not change the world time but will still speed up nearby block, entity and tile entity ticks. config.ebwizardry.world_time_manipulation.true=Yes - daytime, nighttime... config.ebwizardry.world_time_manipulation.false=No - I need my sleep! -config.ebwizardry.replace_vanilla_fireballs=Replace Vanilla Fireballs -config.ebwizardry.replace_vanilla_fireballs.tooltip=Whether to replace Minecraft's own fireballs with wizardry fireballs. If this is disabled, only wizardry spells will use the custom fireballs. -config.ebwizardry.replace_vanilla_fireballs.true=Yes - give me FIRE! -config.ebwizardry.replace_vanilla_fireballs.false=No - blazes are mean enough -config.ebwizardry.replace_vanilla_fall_damage=Replace Vanilla Fall Damage -config.ebwizardry.replace_vanilla_fall_damage.tooltip=Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells revert to a more simplistic method of resetting the player's fall damage in certain cases. -config.ebwizardry.replace_vanilla_fall_damage.true=Yes - it's parkour time -config.ebwizardry.replace_vanilla_fall_damage.false=No - break my legs normally config.ebwizardry.creative_bypasses_arcane_lock=Bypass Arcane Lock config.ebwizardry.creative_bypasses_arcane_lock.tooltip=Determines which players can bypass arcane locks. config.ebwizardry.creative_bypasses_arcane_lock.true=Anyone in creative mode @@ -1155,12 +1135,44 @@ config.ebwizardry.ice_wraith_spawn_rate=Ice Wraith Spawn Rate config.ebwizardry.ice_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned ice wraiths; higher numbers mean more ice wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable ice wraith spawning entirely. config.ebwizardry.lightning_wraith_spawn_rate=Lightning Wraith Spawn Rate config.ebwizardry.lightning_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned lightning wraiths; higher numbers mean more lightning wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable lightning wraith spawning entirely. + +config.ebwizardry.category.difficulty=Difficulty Settings +config.ebwizardry.category.difficulty.tooltip=Configure wizardry's difficulty +config.ebwizardry.title.difficulty=Difficulty Settings +config.ebwizardry.subtitle.difficulty=Settings that affect the mod's difficulty. + +config.ebwizardry.discovery_mode=Discovery Mode +config.ebwizardry.discovery_mode.tooltip=For those who like a sense of mystery! When enabled, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is disabled. +config.ebwizardry.legacy_wand_levelling=Legacy Wand Levelling +config.ebwizardry.legacy_wand_levelling.tooltip=Controls whether wands are required to gain progression before they can be upgraded to the next tier. Enable this option to revert to the pre-4.2 system, which only requires tomes of arcana. Wands will still gain progression silently when this is enabled, so if you go back to the new system you won't lose any progress. +config.ebwizardry.legacy_wand_levelling.true=Yes - I liked it how it was! +config.ebwizardry.legacy_wand_levelling.false=No - Level me up! +config.ebwizardry.friendly_fire=Friendly Fire +config.ebwizardry.friendly_fire.tooltip=Controls which creatures may be damaged by your magic when allied to you. Your spells will not target your allies or creatures summoned/owned by them regardless of this setting, but this setting makes them completely immune if disabled. +config.ebwizardry.minion_revenge_targeting=Minion Revenge Targeting +config.ebwizardry.minion_revenge_targeting.tooltip=Whether summoned creatures can revenge-attack players or creatures that they would not otherwise be able to attack. +config.ebwizardry.minion_revenge_targeting.true=Yes - I'd never hurt them! +config.ebwizardry.minion_revenge_targeting.false=No - they must always obey me! config.ebwizardry.forfeit_chance=Forfeit Chance config.ebwizardry.forfeit_chance.tooltip=The chance to 'misread' an undiscovered spell and trigger a forfeit instead. Setting this to 0 effectively disables the forfeit mechanic. Has no effect if discovery mode is disabled. config.ebwizardry.player_damage_scaling=Player Damage Scaling Factor config.ebwizardry.player_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by players casting spells, relative to 1. config.ebwizardry.npc_damage_scaling=NPC Damage Scaling Factor config.ebwizardry.npc_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1. + +config.ebwizardry.category.tweaks=Tweaks Settings +config.ebwizardry.category.tweaks.tooltip=Tweak wizardry's behaviour +config.ebwizardry.title.tweaks=Tweaks Settings +config.ebwizardry.subtitle.tweaks=Assorted settings for tweaking the mod's behaviour. + +config.ebwizardry.replace_vanilla_fireballs=Replace Vanilla Fireballs +config.ebwizardry.replace_vanilla_fireballs.tooltip=Whether to replace Minecraft's own fireballs with wizardry fireballs. If this is disabled, only wizardry spells will use the custom fireballs. +config.ebwizardry.replace_vanilla_fireballs.true=Yes - give me FIRE! +config.ebwizardry.replace_vanilla_fireballs.false=No - blazes are mean enough +config.ebwizardry.replace_vanilla_fall_damage=Replace Vanilla Fall Damage +config.ebwizardry.replace_vanilla_fall_damage.tooltip=Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells revert to a more simplistic method of resetting the player's fall damage in certain cases. +config.ebwizardry.replace_vanilla_fall_damage.true=Yes - it's parkour time +config.ebwizardry.replace_vanilla_fall_damage.false=No - break my legs normally config.ebwizardry.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist config.ebwizardry.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. ebwizardry\:wizard). config.ebwizardry.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist From 97499eb523e94e95df5b7111e4a856b255554390 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 25 Mar 2020 15:08:15 +0000 Subject: [PATCH 006/415] Update version number --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/question.md | 2 +- CREDITS.md | 2 +- ISSUE_TEMPLATE.md | 2 +- build.gradle | 2 +- src/main/java/electroblob/wizardry/Wizardry.java | 2 +- src/main/resources/mcmod.info | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 182b1c1a..aa3ebc3f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -10,7 +10,7 @@ assignees: '' Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting. Minecraft version: 1.12.2 [change as necessary] -Wizardry version: 4.2.10 [change as necessary] +Wizardry version: 4.3 [change as necessary] Environment: Singleplayer/LAN game/Server [delete as necessary] Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.] diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index ff92dd20..536b7327 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -10,7 +10,7 @@ assignees: '' Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting. Minecraft version: 1.12.2 [change as necessary] -Wizardry version: 4.2.10 [change as necessary] +Wizardry version: 4.3 [change as necessary] Question details: [explain your question here] diff --git a/CREDITS.md b/CREDITS.md index fdbc9f24..8d526a39 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -1,7 +1,7 @@ # Credits Electroblob's Wizardry -Version 4.2.10 +Version 4.3 For Minecraft 1.12.2 Designed, coded and textured by Electroblob diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 3c47b45d..adeb1759 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,7 +1,7 @@ Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting. Minecraft version: 1.12.2 [change as necessary] -Wizardry version: 4.2.10 [change as necessary] +Wizardry version: 4.3 [change as necessary] Environment: Singleplayer/LAN game/Server [delete as necessary] Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.] diff --git a/build.gradle b/build.gradle index 7aadf522..8623ebcb 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ apply plugin: 'net.minecraftforge.gradle.forge' //Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. -version = "4.2.10" // There, it matches semver, happy now? +version = "4.3" // There, it matches semver, happy now? group= "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html archivesBaseName = "ElectroblobsWizardry" diff --git a/src/main/java/electroblob/wizardry/Wizardry.java b/src/main/java/electroblob/wizardry/Wizardry.java index 091ee65a..68e9b058 100644 --- a/src/main/java/electroblob/wizardry/Wizardry.java +++ b/src/main/java/electroblob/wizardry/Wizardry.java @@ -64,7 +64,7 @@ public class Wizardry { * 1.x.x represents Minecraft 1.7.x versions, 2.x.x represents Minecraft 1.10.x versions, 3.x.x represents Minecraft * 1.11.x versions, and so on. */ - public static final String VERSION = "4.2.10"; + public static final String VERSION = "4.3"; // IDEA: Triggering of inbuilt Forge events in relevant places? // IDEA: Abstract the vanilla particles behind the particle builder diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 741e2e8e..0e1e3aec 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -2,7 +2,7 @@ { "modid" : "ebwizardry", "name" : "Electroblob's Wizardry", - "version" : "4.2.10", + "version" : "4.3", "mcversion" : "1.12.2", "url" : "https://minecraft.curseforge.com/projects/electroblobs-wizardry", "credits" : "\nDesigned, coded and textured by Electroblob.\nCode contributed by: Corail31, 12foo, Shadows-of-Fire, Tora-B, Avatair, Aeronica, UltraHex, Azim-Palmer, raoulvdberge.\nTranslators: MadWrist (Spanish, Mexican Spanish), VilagVil, kellixon & bigenergy (Russian), Hahdrim (French), lorrampi (Brazilian Portuguese), ZHENGLOC & dragon-evol (Chinese), shejery & rewi_wire (Korean), Trozuu (Polish), chesterccj305 (Taiwanese).", From eb65b7d7b738f4cf0efedf3e95c63ca7e2f3c4c5 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 25 Mar 2020 15:14:58 +0000 Subject: [PATCH 007/415] Reorganise config again --- .../java/electroblob/wizardry/Settings.java | 116 +++++++++--------- .../assets/ebwizardry/lang/en_gb.lang | 28 ++--- .../assets/ebwizardry/lang/en_us.lang | 28 ++--- 3 files changed, 86 insertions(+), 86 deletions(-) diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index e3d2afce..c70459e2 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -472,13 +472,6 @@ public final class Settings { playerBlockDamage = property.getBoolean(); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "bonemealGrowsCrystalFlowers", true, - "Whether using bonemeal on grass blocks has a chance to grow crystal flowers."); - property.setLanguageKey("config." + Wizardry.MODID + ".bonemeal_grows_crystal_flowers"); - Wizardry.proxy.setToNamedBooleanEntry(property); - bonemealGrowsCrystalFlowers = property.getBoolean(); - propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "playersMoveEachOther", true, "Whether to allow players to move other players around using magic."); property.setLanguageKey("config." + Wizardry.MODID + ".players_move_each_other"); @@ -521,57 +514,11 @@ public final class Settings { creativeBypassesArcaneLock = property.getBoolean(); propOrder.add(property.getName()); - property = config.get(GAMEPLAY_CATEGORY, "mobLootTableWhitelist", new String[0], "Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them."); - property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_whitelist"); - property.setRequiresMcRestart(true); - mobLootTableWhitelist = getResourceLocationList(property); - propOrder.add(property.getName()); - - property = config.get(GAMEPLAY_CATEGORY, "mobLootTableBlacklist", new String[]{"entities/vex", "entities/ender_dragon", "entities/wither", Wizardry.MODID + ":entities/evil_wizard"}, "Blacklist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually exclude them."); - property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_blacklist"); - property.setRequiresMcRestart(true); - mobLootTableBlacklist = getResourceLocationList(property); - propOrder.add(property.getName()); - - property = config.get(GAMEPLAY_CATEGORY, "mobSpawnDimensions", new int[]{0}, - "List of dimension ids in which wizardry's hostile mobs can spawn."); - property.setLanguageKey("config." + Wizardry.MODID + ".mob_spawn_dimensions"); - property.setRequiresMcRestart(true); - mobSpawnDimensions = property.getIntList(); - propOrder.add(property.getName()); - - property = config.get(GAMEPLAY_CATEGORY, "mobSpawnBiomeBlacklist", new String[]{"mushroom_island", "mushroom_island_shore"}, - "List of names of biomes in which wizardry's hostile mobs cannot spawn. Biome names are not case-sensitive. For mod biomes, prefix with the mod ID (e.g. biomesoplenty:mystic_grove)."); - property.setLanguageKey("config." + Wizardry.MODID + ".mob_spawn_biome_blacklist"); - property.setRequiresMcRestart(true); - mobSpawnBiomeBlacklist = getResourceLocationList(property); - propOrder.add(property.getName()); - - property = config.get(GAMEPLAY_CATEGORY, "evilWizardSpawnRate", 3, - "Spawn rate for naturally-spawned evil wizards; higher numbers mean more evil wizards will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable evil wizard spawning entirely.", - 0, 100); - property.setLanguageKey("config." + Wizardry.MODID + ".evil_wizard_spawn_rate"); - Wizardry.proxy.setToNumberSliderEntry(property); - property.setRequiresMcRestart(true); - evilWizardSpawnRate = property.getInt(); - propOrder.add(property.getName()); - - property = config.get(GAMEPLAY_CATEGORY, "iceWraithSpawnRate", 3, - "Spawn rate for naturally-spawned ice wraiths; higher numbers mean more ice wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable ice wraith spawning entirely.", - 0, 100); - property.setLanguageKey("config." + Wizardry.MODID + ".ice_wraith_spawn_rate"); - Wizardry.proxy.setToNumberSliderEntry(property); - property.setRequiresMcRestart(true); - iceWraithSpawnRate = property.getInt(); - propOrder.add(property.getName()); - - property = config.get(GAMEPLAY_CATEGORY, "lightningWraithSpawnRate", 1, - "Spawn rate for naturally-spawned lightning wraiths; higher numbers mean more lightning wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable lightning wraith spawning entirely.", - 0, 100); - property.setLanguageKey("config." + Wizardry.MODID + ".lightning_wraith_spawn_rate"); - Wizardry.proxy.setToNumberSliderEntry(property); - property.setRequiresMcRestart(true); - lightningWraithSpawnRate = property.getInt(); + property = config.get(GAMEPLAY_CATEGORY, "bonemealGrowsCrystalFlowers", true, + "Whether using bonemeal on grass blocks has a chance to grow crystal flowers."); + property.setLanguageKey("config." + Wizardry.MODID + ".bonemeal_grows_crystal_flowers"); + Wizardry.proxy.setToNamedBooleanEntry(property); + bonemealGrowsCrystalFlowers = property.getBoolean(); propOrder.add(property.getName()); config.setCategoryPropertyOrder(GAMEPLAY_CATEGORY, propOrder); @@ -638,6 +585,33 @@ public final class Settings { npcDamageScale = property.getDouble(); propOrder.add(property.getName()); + property = config.get(DIFFICULTY_CATEGORY, "evilWizardSpawnRate", 3, + "Spawn rate for naturally-spawned evil wizards; higher numbers mean more evil wizards will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable evil wizard spawning entirely.", + 0, 100); + property.setLanguageKey("config." + Wizardry.MODID + ".evil_wizard_spawn_rate"); + Wizardry.proxy.setToNumberSliderEntry(property); + property.setRequiresMcRestart(true); + evilWizardSpawnRate = property.getInt(); + propOrder.add(property.getName()); + + property = config.get(DIFFICULTY_CATEGORY, "iceWraithSpawnRate", 3, + "Spawn rate for naturally-spawned ice wraiths; higher numbers mean more ice wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable ice wraith spawning entirely.", + 0, 100); + property.setLanguageKey("config." + Wizardry.MODID + ".ice_wraith_spawn_rate"); + Wizardry.proxy.setToNumberSliderEntry(property); + property.setRequiresMcRestart(true); + iceWraithSpawnRate = property.getInt(); + propOrder.add(property.getName()); + + property = config.get(DIFFICULTY_CATEGORY, "lightningWraithSpawnRate", 1, + "Spawn rate for naturally-spawned lightning wraiths; higher numbers mean more lightning wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable lightning wraith spawning entirely.", + 0, 100); + property.setLanguageKey("config." + Wizardry.MODID + ".lightning_wraith_spawn_rate"); + Wizardry.proxy.setToNumberSliderEntry(property); + property.setRequiresMcRestart(true); + lightningWraithSpawnRate = property.getInt(); + propOrder.add(property.getName()); + config.setCategoryPropertyOrder(DIFFICULTY_CATEGORY, propOrder); } @@ -664,6 +638,32 @@ public final class Settings { replaceVanillaFallDamage = property.getBoolean(); propOrder.add(property.getName()); + property = config.get(TWEAKS_CATEGORY, "mobLootTableWhitelist", new String[0], "Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them."); + property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_whitelist"); + property.setRequiresMcRestart(true); + mobLootTableWhitelist = getResourceLocationList(property); + propOrder.add(property.getName()); + + property = config.get(TWEAKS_CATEGORY, "mobLootTableBlacklist", new String[]{"entities/vex", "entities/ender_dragon", "entities/wither", Wizardry.MODID + ":entities/evil_wizard"}, "Blacklist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually exclude them."); + property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_blacklist"); + property.setRequiresMcRestart(true); + mobLootTableBlacklist = getResourceLocationList(property); + propOrder.add(property.getName()); + + property = config.get(TWEAKS_CATEGORY, "mobSpawnDimensions", new int[]{0}, + "List of dimension ids in which wizardry's hostile mobs can spawn."); + property.setLanguageKey("config." + Wizardry.MODID + ".mob_spawn_dimensions"); + property.setRequiresMcRestart(true); + mobSpawnDimensions = property.getIntList(); + propOrder.add(property.getName()); + + property = config.get(TWEAKS_CATEGORY, "mobSpawnBiomeBlacklist", new String[]{"mushroom_island", "mushroom_island_shore"}, + "List of names of biomes in which wizardry's hostile mobs cannot spawn. Biome names are not case-sensitive. For mod biomes, prefix with the mod ID (e.g. biomesoplenty:mystic_grove)."); + property.setLanguageKey("config." + Wizardry.MODID + ".mob_spawn_biome_blacklist"); + property.setRequiresMcRestart(true); + mobSpawnBiomeBlacklist = getResourceLocationList(property); + propOrder.add(property.getName()); + property = config.get(TWEAKS_CATEGORY, "summonedCreatureTargetsWhitelist", new String[0], "List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. SoundLoopSpellEntity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. " + Wizardry.MODID + ":wizard)."); property.setLanguageKey("config." + Wizardry.MODID + ".summoned_creature_targets_whitelist"); diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 993845ae..d738d907 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -1121,20 +1121,6 @@ config.ebwizardry.slow_time_affects_players=Slow Time Affects Players config.ebwizardry.slow_time_affects_players.tooltip=Whether players are slowed when another nearby player uses the slow time spell. If this is disabled, mobs and projectiles will still be affected but players will move at normal speed. config.ebwizardry.bonemeal_grows_crystal_flowers=Bonemeal Grows Crystal Flowers config.ebwizardry.bonemeal_grows_crystal_flowers.tooltip=Whether using bonemeal on grass blocks has a chance to grow crystal flowers. -config.ebwizardry.mob_loot_table_whitelist=Mob Loot Table Whitelist -config.ebwizardry.mob_loot_table_whitelist.tooltip=Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them. -config.ebwizardry.mob_loot_table_blacklist=Mob Loot Table Blacklist -config.ebwizardry.mob_loot_table_blacklist.tooltip=Blacklist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually exclude them. -config.ebwizardry.mob_spawn_dimensions=Mob Spawning Dimensions -config.ebwizardry.mob_spawn_dimensions.tooltip=List of ids of dimensions in which wizardry's hostile mobs can spawn. -config.ebwizardry.mob_spawn_biome_blacklist=Mob Spawning Biome Blacklist -config.ebwizardry.mob_spawn_biome_blaclist.tooltip=List of names of biomes in which wizardry's hostile mobs cannot spawn. Biome names are not case-sensitive. For mod biomes, prefix with the mod ID (e.g. biomesoplenty\:mystic_grove). -config.ebwizardry.evil_wizard_spawn_rate=Evil Wizard Spawn Rate -config.ebwizardry.evil_wizard_spawn_rate.tooltip=Spawn rate for naturally-spawned evil wizards; higher numbers mean more evil wizards will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable evil wizard spawning entirely. -config.ebwizardry.ice_wraith_spawn_rate=Ice Wraith Spawn Rate -config.ebwizardry.ice_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned ice wraiths; higher numbers mean more ice wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable ice wraith spawning entirely. -config.ebwizardry.lightning_wraith_spawn_rate=Lightning Wraith Spawn Rate -config.ebwizardry.lightning_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned lightning wraiths; higher numbers mean more lightning wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable lightning wraith spawning entirely. config.ebwizardry.category.difficulty=Difficulty Settings config.ebwizardry.category.difficulty.tooltip=Configure wizardry's difficulty @@ -1159,6 +1145,12 @@ config.ebwizardry.player_damage_scaling=Player Damage Scaling Factor config.ebwizardry.player_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by players casting spells, relative to 1. config.ebwizardry.npc_damage_scaling=NPC Damage Scaling Factor config.ebwizardry.npc_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1. +config.ebwizardry.evil_wizard_spawn_rate=Evil Wizard Spawn Rate +config.ebwizardry.evil_wizard_spawn_rate.tooltip=Spawn rate for naturally-spawned evil wizards; higher numbers mean more evil wizards will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable evil wizard spawning entirely. +config.ebwizardry.ice_wraith_spawn_rate=Ice Wraith Spawn Rate +config.ebwizardry.ice_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned ice wraiths; higher numbers mean more ice wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable ice wraith spawning entirely. +config.ebwizardry.lightning_wraith_spawn_rate=Lightning Wraith Spawn Rate +config.ebwizardry.lightning_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned lightning wraiths; higher numbers mean more lightning wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable lightning wraith spawning entirely. config.ebwizardry.category.tweaks=Tweaks Settings config.ebwizardry.category.tweaks.tooltip=Tweak wizardry's behaviour @@ -1173,6 +1165,14 @@ config.ebwizardry.replace_vanilla_fall_damage=Replace Vanilla Fall Damage config.ebwizardry.replace_vanilla_fall_damage.tooltip=Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells revert to a more simplistic method of resetting the player's fall damage in certain cases. config.ebwizardry.replace_vanilla_fall_damage.true=Yes - it's parkour time config.ebwizardry.replace_vanilla_fall_damage.false=No - break my legs normally +config.ebwizardry.mob_loot_table_whitelist=Mob Loot Table Whitelist +config.ebwizardry.mob_loot_table_whitelist.tooltip=Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them. +config.ebwizardry.mob_loot_table_blacklist=Mob Loot Table Blacklist +config.ebwizardry.mob_loot_table_blacklist.tooltip=Blacklist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually exclude them. +config.ebwizardry.mob_spawn_dimensions=Mob Spawning Dimensions +config.ebwizardry.mob_spawn_dimensions.tooltip=List of ids of dimensions in which wizardry's hostile mobs can spawn. +config.ebwizardry.mob_spawn_biome_blacklist=Mob Spawning Biome Blacklist +config.ebwizardry.mob_spawn_biome_blaclist.tooltip=List of names of biomes in which wizardry's hostile mobs cannot spawn. Biome names are not case-sensitive. For mod biomes, prefix with the mod ID (e.g. biomesoplenty\:mystic_grove). config.ebwizardry.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist config.ebwizardry.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. ebwizardry\:wizard). config.ebwizardry.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 4913dd6e..2aee9e28 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -1121,20 +1121,6 @@ config.ebwizardry.slow_time_affects_players=Slow Time Affects Players config.ebwizardry.slow_time_affects_players.tooltip=Whether players are slowed when another nearby player uses the slow time spell. If this is disabled, mobs and projectiles will still be affected but players will move at normal speed. config.ebwizardry.bonemeal_grows_crystal_flowers=Bonemeal Grows Crystal Flowers config.ebwizardry.bonemeal_grows_crystal_flowers.tooltip=Whether using bonemeal on grass blocks has a chance to grow crystal flowers. -config.ebwizardry.mob_loot_table_whitelist=Mob Loot Table Whitelist -config.ebwizardry.mob_loot_table_whitelist.tooltip=Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them. -config.ebwizardry.mob_loot_table_blacklist=Mob Loot Table Blacklist -config.ebwizardry.mob_loot_table_blacklist.tooltip=Blacklist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually exclude them. -config.ebwizardry.mob_spawn_dimensions=Mob Spawning Dimensions -config.ebwizardry.mob_spawn_dimensions.tooltip=List of ids of dimensions in which wizardry's hostile mobs can spawn. -config.ebwizardry.mob_spawn_biome_blacklist=Mob Spawning Biome Blacklist -config.ebwizardry.mob_spawn_biome_blaclist.tooltip=List of names of biomes in which wizardry's hostile mobs cannot spawn. Biome names are not case-sensitive. For mod biomes, prefix with the mod ID (e.g. biomesoplenty\:mystic_grove). -config.ebwizardry.evil_wizard_spawn_rate=Evil Wizard Spawn Rate -config.ebwizardry.evil_wizard_spawn_rate.tooltip=Spawn rate for naturally-spawned evil wizards; higher numbers mean more evil wizards will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable evil wizard spawning entirely. -config.ebwizardry.ice_wraith_spawn_rate=Ice Wraith Spawn Rate -config.ebwizardry.ice_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned ice wraiths; higher numbers mean more ice wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable ice wraith spawning entirely. -config.ebwizardry.lightning_wraith_spawn_rate=Lightning Wraith Spawn Rate -config.ebwizardry.lightning_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned lightning wraiths; higher numbers mean more lightning wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable lightning wraith spawning entirely. config.ebwizardry.category.difficulty=Difficulty Settings config.ebwizardry.category.difficulty.tooltip=Configure wizardry's difficulty @@ -1159,6 +1145,12 @@ config.ebwizardry.player_damage_scaling=Player Damage Scaling Factor config.ebwizardry.player_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by players casting spells, relative to 1. config.ebwizardry.npc_damage_scaling=NPC Damage Scaling Factor config.ebwizardry.npc_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1. +config.ebwizardry.evil_wizard_spawn_rate=Evil Wizard Spawn Rate +config.ebwizardry.evil_wizard_spawn_rate.tooltip=Spawn rate for naturally-spawned evil wizards; higher numbers mean more evil wizards will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable evil wizard spawning entirely. +config.ebwizardry.ice_wraith_spawn_rate=Ice Wraith Spawn Rate +config.ebwizardry.ice_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned ice wraiths; higher numbers mean more ice wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable ice wraith spawning entirely. +config.ebwizardry.lightning_wraith_spawn_rate=Lightning Wraith Spawn Rate +config.ebwizardry.lightning_wraith_spawn_rate.tooltip=Spawn rate for naturally-spawned lightning wraiths; higher numbers mean more lightning wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable lightning wraith spawning entirely. config.ebwizardry.category.tweaks=Tweaks Settings config.ebwizardry.category.tweaks.tooltip=Tweak wizardry's behaviour @@ -1173,6 +1165,14 @@ config.ebwizardry.replace_vanilla_fall_damage=Replace Vanilla Fall Damage config.ebwizardry.replace_vanilla_fall_damage.tooltip=Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells revert to a more simplistic method of resetting the player's fall damage in certain cases. config.ebwizardry.replace_vanilla_fall_damage.true=Yes - it's parkour time config.ebwizardry.replace_vanilla_fall_damage.false=No - break my legs normally +config.ebwizardry.mob_loot_table_whitelist=Mob Loot Table Whitelist +config.ebwizardry.mob_loot_table_whitelist.tooltip=Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them. +config.ebwizardry.mob_loot_table_blacklist=Mob Loot Table Blacklist +config.ebwizardry.mob_loot_table_blacklist.tooltip=Blacklist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually exclude them. +config.ebwizardry.mob_spawn_dimensions=Mob Spawning Dimensions +config.ebwizardry.mob_spawn_dimensions.tooltip=List of ids of dimensions in which wizardry's hostile mobs can spawn. +config.ebwizardry.mob_spawn_biome_blacklist=Mob Spawning Biome Blacklist +config.ebwizardry.mob_spawn_biome_blaclist.tooltip=List of names of biomes in which wizardry's hostile mobs cannot spawn. Biome names are not case-sensitive. For mod biomes, prefix with the mod ID (e.g. biomesoplenty\:mystic_grove). config.ebwizardry.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist config.ebwizardry.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. ebwizardry\:wizard). config.ebwizardry.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist From 849066de8ad9139d7b1381bd72064a9ccd98f71c Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 25 Mar 2020 15:20:24 +0000 Subject: [PATCH 008/415] Apparently we do need this after all --- .../wizardry/client/model/WizardryModels.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java index f2e2e6c3..11ad3990 100644 --- a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java +++ b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java @@ -10,16 +10,20 @@ import electroblob.wizardry.item.ItemCrystal; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import net.minecraft.block.BlockPlanks; +import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.block.model.IBakedModel; import net.minecraft.client.renderer.block.model.ModelBakery; import net.minecraft.client.renderer.block.model.ModelResourceLocation; import net.minecraft.client.renderer.block.statemap.StateMap; +import net.minecraft.client.renderer.block.statemap.StateMapperBase; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.fml.common.Mod; @@ -367,4 +371,9 @@ public final class WizardryModels { new ModelResourceLocation(item.getRegistryName(), variant)); } + @SubscribeEvent + public static void onTextureStitchEvent(TextureStitchEvent.Pre event){ + event.getMap().registerSprite(new ResourceLocation(Wizardry.MODID, "blocks/books")); + } + } From 885e58c85cbc70d01014e91e8d15c27e46fc859e Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 29 Mar 2020 16:55:04 +0100 Subject: [PATCH 009/415] Receive only RenderGameOverlayEvent.Post, not both --- .../java/electroblob/wizardry/client/gui/GuiSpellDisplay.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java b/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java index 85e2aa61..3b97ab9e 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java @@ -96,7 +96,7 @@ public class GuiSpellDisplay { // Normally when extending Gui, you'd have to have an instance to access its methods. However, we're not actually // using any of them, so this class may as well not bother and just be a static event handler. Neat! @SubscribeEvent - public static void draw(RenderGameOverlayEvent event){ + public static void draw(RenderGameOverlayEvent.Post event){ Minecraft mc = Minecraft.getMinecraft(); From 220bf15cfbdfb6040497bd592803fc4873eb217d Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 29 Mar 2020 16:56:52 +0100 Subject: [PATCH 010/415] Don't process unless the event type means it will actually draw something --- .../java/electroblob/wizardry/client/gui/GuiSpellDisplay.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java b/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java index 3b97ab9e..e511c6f1 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java @@ -97,6 +97,9 @@ public class GuiSpellDisplay { // using any of them, so this class may as well not bother and just be a static event handler. Neat! @SubscribeEvent public static void draw(RenderGameOverlayEvent.Post event){ + + if(event.getType() != RenderGameOverlayEvent.ElementType.TEXT + && event.getType() != RenderGameOverlayEvent.ElementType.HOTBAR) return; Minecraft mc = Minecraft.getMinecraft(); From 53a053bfffcc452cd0a8ad00cc16045e12bf3a07 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 29 Mar 2020 17:00:07 +0100 Subject: [PATCH 011/415] Make replenish hunger and satiety fill the correct amount of food, and make them say the correct number in their descriptions --- src/main/resources/assets/ebwizardry/lang/de_de.lang | 2 +- src/main/resources/assets/ebwizardry/lang/en_gb.lang | 2 +- src/main/resources/assets/ebwizardry/lang/en_us.lang | 2 +- .../resources/assets/ebwizardry/spells/replenish_hunger.json | 2 +- src/main/resources/assets/ebwizardry/spells/satiety.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/resources/assets/ebwizardry/lang/de_de.lang b/src/main/resources/assets/ebwizardry/lang/de_de.lang index a205ec43..ede0ab1a 100644 --- a/src/main/resources/assets/ebwizardry/lang/de_de.lang +++ b/src/main/resources/assets/ebwizardry/lang/de_de.lang @@ -836,7 +836,7 @@ spell.ebwizardry\:poison_bomb.desc=Schießt eine Giftbombe in die Richtung, in d spell.ebwizardry\:possession.desc="Werde dein Feind." spell.ebwizardry\:ray_of_purification.desc=Sendet einen blendenden Lichtstrahl aus, der seine Ziele verletzt und blendet. Untote Kreaturen erleiden doppelten Schaden und werden in Brand gesteckt. spell.ebwizardry\:remove_curse.desc=Entfernt alle Flüche, die den Magier derzeit betreffen. -spell.ebwizardry\:replenish_hunger.desc=Füllt den Hungerbalken des Magiers um 4 Hungerpunkte auf. +spell.ebwizardry\:replenish_hunger.desc=Füllt den Hungerbalken des Magiers um 3 Hungerpunkte auf. spell.ebwizardry\:resurrection.desc=Mit einem Meisterheiler an Ihrer Seite, ist tot zu sein... optional. spell.ebwizardry\:reversal.desc=Entfernt einen zufälligen negativen Trankeffekt und fügt ihn dem Ziel zu, das diesen Effekt für die verbleibende Dauer erleiden wird. Die Stufe erhöht die Anzahl der Effekte, die umgekehrt werden. spell.ebwizardry\:ring_of_fire.desc=Erzeugt einen Feuerring um den Magier, der alle Feinde in der Nähe schädigt und sie in Brand setzt. diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index d738d907..0ad6e088 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -859,7 +859,7 @@ spell.ebwizardry\:poison_bomb.desc=Lanches a poison bomb in the direction you ar spell.ebwizardry\:possession.desc="Become thy enemy." spell.ebwizardry\:ray_of_purification.desc=Emits a ray of blinding light that damages and blinds its targets. Undead creatures take double damage and are set on fire. spell.ebwizardry\:remove_curse.desc=Removes any curse currently affecting the caster. -spell.ebwizardry\:replenish_hunger.desc=Replenishes the caster's food level by 4 hunger points. +spell.ebwizardry\:replenish_hunger.desc=Replenishes the caster's food level by 3 hunger points. spell.ebwizardry\:resurrection.desc=With a master healer by your side, being dead is... optional. spell.ebwizardry\:reversal.desc=Removes a random negative potion effect and inflicts it upon the target, which will suffer that effect for the remaining duration. Potency increases the number of effects that are reversed. spell.ebwizardry\:ring_of_fire.desc=Creates a ring of fire around the caster, damaging all nearby enemies and setting them on fire. diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 2aee9e28..6649b52c 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -859,7 +859,7 @@ spell.ebwizardry\:poison_bomb.desc=Lanches a poison bomb in the direction you ar spell.ebwizardry\:possession.desc="Become thy enemy." spell.ebwizardry\:ray_of_purification.desc=Emits a ray of blinding light that damages and blinds its targets. Undead creatures take double damage and are set on fire. spell.ebwizardry\:remove_curse.desc=Removes any curse currently affecting the caster. -spell.ebwizardry\:replenish_hunger.desc=Replenishes the caster's food level by 4 hunger points. +spell.ebwizardry\:replenish_hunger.desc=Replenishes the caster's food level by 3 hunger points. spell.ebwizardry\:resurrection.desc=With a master healer by your side, being dead is... optional. spell.ebwizardry\:reversal.desc=Removes a random negative potion effect and inflicts it upon the target, which will suffer that effect for the remaining duration. Potency increases the number of effects that are reversed. spell.ebwizardry\:ring_of_fire.desc=Creates a ring of fire around the caster, damaging all nearby enemies and setting them on fire. diff --git a/src/main/resources/assets/ebwizardry/spells/replenish_hunger.json b/src/main/resources/assets/ebwizardry/spells/replenish_hunger.json index 7f5d82c4..1892f268 100644 --- a/src/main/resources/assets/ebwizardry/spells/replenish_hunger.json +++ b/src/main/resources/assets/ebwizardry/spells/replenish_hunger.json @@ -17,7 +17,7 @@ "chargeup": 0, "cooldown": 30, "base_properties": { - "hunger_points": 3, + "hunger_points": 6, "saturation_modifier": 0.1 } } \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/spells/satiety.json b/src/main/resources/assets/ebwizardry/spells/satiety.json index c8c9da91..06dc1140 100644 --- a/src/main/resources/assets/ebwizardry/spells/satiety.json +++ b/src/main/resources/assets/ebwizardry/spells/satiety.json @@ -17,7 +17,7 @@ "chargeup": 0, "cooldown": 50, "base_properties": { - "hunger_points": 8, + "hunger_points": 16, "saturation_modifier": 0.1 } } \ No newline at end of file From df1f737539cb57554100ae9b7dd72715f5df661e Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 31 Mar 2020 21:26:23 +0100 Subject: [PATCH 012/415] Fancy new advancement background --- .../textures/gui/advancement_background.png | Bin 2337 -> 1979 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/ebwizardry/textures/gui/advancement_background.png b/src/main/resources/assets/ebwizardry/textures/gui/advancement_background.png index c5de2e8897710ab98700065f45a577ba369c4a55..fc6cc3fe8e4f7315f22367df387c790a0a5f40f5 100644 GIT binary patch delta 1966 zcmZ1|w3~l|ay{$3s*s41pu}>8f};Gi%$!t(lFEWqh1817GzNx>TWiB2H$Bnd`SV<) zm_h+D3bHZ-1S8ecP7qgYnw~zWv#Ee7W?86(2Vp+bryU?@?}K^?RjX zja$Edh?aGzerR#c^IlinzN>~)8#`w!F8)}?yfD)LQ1XW~;bU`d9zV7$C0{aU<0ZMj z6+gay{>r)2YC-;~`5Ly>U)<{J5~FTLi2*KFbM`Um$<1UFX|_~a%6a(!$Je(ZGx_Y^UC;iI zbzp&yIeU)5OB<7he>z_{^o%>rW@u!DGfiF}%^1VN!eo$gprI_KMJDt78XG|wNrwQY z60>Q>8H$4I7*1Z3U@w}$x}n!}-m?&~xTJa!CjrG{nvW0qnsBB>YIn%zGbd?u3Ti9) z?P2MhG^bGc@SM+iAy=OCZjamZ`5wD`ZO}m$X_XxhoH-1aoVd8)k(Z0zF@arYN>^I9NGlCOCeKxvwY42+h@?I6=>nax>UdwBG?aB(pDYJ4f#eCM? zawEvSensi!-0AOjy^+f=2m24sk-GA? zVW)L|{Kl}$HZRVkWGs1Du*&SjzPy-66D-;DT$c1R zmpW)4+<#r*(H`p?Hn(Oke|u2v{>D}58*b0Pduv0@j+h&-?zN=eKFfFEPkoEeSJBgk zyTbY3+U|@_yI8~&Y>_0Axu8sLO5LQ!Oly;^pAGfow+L)Ky}JK`i(|y!rb}0C9=V>~ zAUm~S*7=@O)^8(=@)c{Zu2Nq!J!G#-*rKhqAHK;gtZJ%I5#xF~ai+j|xkc|2E?*C2 zx^s1QR#N?re9?A^bD^aV&!6Zvf4Kisebimy+iydXljLtR#dM3^3}MeH`<7D`eIxJ4 zy{?PX{bYVzF+Pz0;p)?`w%;ul%MiIinX^dgb2d6Dxkj z6_#x~qFXM~eVe11b&;`9{ejQzIzhY7?YO*5uX~EliUZM=()Ov3Q=*S<`f2S}%KUTU zg72O6Yw~8!5zk9!{j)Z&@cFYnCj`5Gx`PZIlU@>c-J4f@wJ~O*>%U)MnJ&6h2aUqY6gan zRv8Z&7#KJUJR*x381$4un6YB1eHjA-1AB?5uPgf#W*Kot@%iTyHZU+SNS3%plsM<- z=BDPAFgO?0rzV!9rYMwWmSiZnd-?{1H}Z)yFfdknx;TbdoSr*rum7O{k)!on`6q1C z>~Lehz?qyJt_>zXv*I3?t{3XC87ge1zhSc`7-FAdMQzx$D_(i3z(sp-UFg^CUbWz&6^S_sOUodsux7+fj)rV!?QeTUb4nI;~%oOs> oLegP%teEo7MfJ9`6`ru4o^}3~giGpP1_lNOPgg&ebxsLQ05Qd%OaK4? delta 2327 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 Date: Tue, 31 Mar 2020 21:33:10 +0100 Subject: [PATCH 013/415] Add bookshelf interface to arcane workbench Implements the main functionality: - Access bookshelves from within the workbench - Search by spell name, tier, element and type - Sort by tier, element or alphabetically, ascending or descending - Three-way shift-clicking support - Automatic return of books to bookshelves when wand is removed --- .../wizardry/block/BlockBookshelf.java | 10 - .../client/gui/GuiArcaneWorkbench.java | 223 +++++++++++-- .../inventory/ContainerArcaneWorkbench.java | 298 +++++++++++++++--- .../wizardry/inventory/SlotBookList.java | 70 ++++ .../wizardry/inventory/VirtualSlot.java | 79 +++++ .../electroblob/wizardry/spell/Spell.java | 29 ++ .../assets/ebwizardry/lang/en_gb.lang | 5 + .../assets/ebwizardry/lang/en_us.lang | 5 + .../textures/gui/arcane_workbench.png | Bin 31627 -> 40318 bytes 9 files changed, 646 insertions(+), 73 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/inventory/SlotBookList.java create mode 100644 src/main/java/electroblob/wizardry/inventory/VirtualSlot.java diff --git a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java index b4e23861..9fc4c90a 100644 --- a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java +++ b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java @@ -50,16 +50,6 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid return new BlockStateContainer.Builder(this).add(FACING).add(BOOKS).build(); } - @Override - public boolean isOpaqueCube(IBlockState state){ - return false; - } - - @Override - public boolean isFullCube(IBlockState state){ - return false; - } - @Override public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){ return state.getValue(FACING).getAxis() == face.getAxis() ? BlockFaceShape.UNDEFINED : BlockFaceShape.SOLID; diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index 7d8be0a3..28901598 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -6,6 +6,8 @@ import electroblob.wizardry.constants.Element; import electroblob.wizardry.constants.Tier; import electroblob.wizardry.data.SpellGlyphData; import electroblob.wizardry.data.WizardData; +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; +import electroblob.wizardry.inventory.SlotBookList; import electroblob.wizardry.item.IManaStoringItem; import electroblob.wizardry.item.ISpellCastingItem; import electroblob.wizardry.item.IWorkbenchItem; @@ -14,23 +16,25 @@ import electroblob.wizardry.packet.PacketControlInput; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.inventory.ContainerArcaneWorkbench; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import electroblob.wizardry.util.WandHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiTextField; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.GlStateManager.DestFactor; import net.minecraft.client.renderer.GlStateManager.SourceFactor; import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.ClickType; 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.minecraft.util.math.MathHelper; import net.minecraft.util.text.TextFormatting; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.fml.common.Mod; @@ -38,6 +42,10 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.relauncher.Side; import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; + +import java.io.IOException; +import java.util.Locale; @Mod.EventBusSubscriber(Side.CLIENT) public class GuiArcaneWorkbench extends GuiContainer { @@ -46,12 +54,15 @@ public class GuiArcaneWorkbench extends GuiContainer { public static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/arcane_workbench.png"); - private IInventory playerInventory; + private InventoryPlayer playerInventory; private IInventory arcaneWorkbenchInventory; + private ContainerArcaneWorkbench arcaneWorkbenchContainer; - private static final int TOOLTIP_WIDTH = 164; + private static final int TOOLTIP_WIDTH = 144; private static final int TOOLTIP_TEXT_INSET = 6; + private static final int BOOKSHELF_UI_WIDTH = 122; + /** We report the actual size of the GUI to Minecraft when a wand is in so JEI doesn't overdraw it. * For calculations, we use the size without the tooltip, which is stored in this constant. */ private static final int MAIN_GUI_WIDTH = 176; @@ -61,25 +72,69 @@ public class GuiArcaneWorkbench extends GuiContainer { private static final int RUNE_WIDTH = 100; private static final int RUNE_HEIGHT = 100; + private static final int SCROLL_BAR_LEFT = 102; + private static final int SCROLL_BAR_TOP = 34; + private static final int SCROLL_BAR_WIDTH = 12; + private static final int SCROLL_BAR_HEIGHT = 178; + private static final int SCROLL_HANDLE_HEIGHT = 15; + private static final int PROGRESSION_BAR_WIDTH = 152; private static final int PROGRESSION_BAR_HEIGHT = 3; private static final int HALO_DIAMETER = 156; private static final int TEXTURE_WIDTH = 512; - private static final int TEXTURE_HEIGHT = 256; + private static final int TEXTURE_HEIGHT = 512; private int animationTimer = 0; private static final int ANIMATION_DURATION = 20; + private GuiTextField searchField; + private boolean searchNeedsClearing; + + private float scroll = 0; + private boolean scrolling = false; + public GuiArcaneWorkbench(InventoryPlayer invPlayer, TileEntityArcaneWorkbench entity){ super(new ContainerArcaneWorkbench(invPlayer, entity)); + this.arcaneWorkbenchContainer = (ContainerArcaneWorkbench)inventorySlots; this.playerInventory = invPlayer; this.arcaneWorkbenchInventory = entity; xSize = MAIN_GUI_WIDTH; ySize = 220; } + @Override + public void initGui(){ + + this.mc.player.openContainer = this.inventorySlots; + this.guiLeft = (this.width - this.xSize) / 2; + this.guiTop = (this.height - this.ySize) / 2; + + Keyboard.enableRepeatEvents(true); + + this.buttonList.clear(); + this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width / 2 + 64, this.height / 2 + 3)); + this.buttonList.add(new GuiButtonSort(1, this.guiLeft - 44, this.guiTop + 8, ContainerArcaneWorkbench.SortType.TIER)); + this.buttonList.add(new GuiButtonSort(2, this.guiLeft - 31, this.guiTop + 8, ContainerArcaneWorkbench.SortType.ELEMENT)); + this.buttonList.add(new GuiButtonSort(3, this.guiLeft - 18, this.guiTop + 8, ContainerArcaneWorkbench.SortType.ALPHABETICAL)); + + this.searchField = new GuiTextField(0, this.fontRenderer, this.guiLeft - 113, this.guiTop + 22, 104, this.fontRenderer.FONT_HEIGHT); + this.searchField.setMaxStringLength(50); + this.searchField.setEnableBackgroundDrawing(false); + this.searchField.setVisible(true); + this.searchField.setTextColor(16777215); + this.searchField.setCanLoseFocus(false); + this.searchField.setFocused(true); + + } + + @Override + public void onGuiClosed(){ + super.onGuiClosed(); + Keyboard.enableRepeatEvents(false); + } + // Huh, didn't realise this method existed. Pretty neat. @Override public void updateScreen(){ @@ -93,18 +148,33 @@ public class GuiArcaneWorkbench extends GuiContainer { GlStateManager.color(1, 1, 1, 1); // Just in case + boolean mouseHeld = Mouse.isButtonDown(0); + + if(!scrolling && mouseHeld && getMaxScrollRows() > 0 && isPointInRegion(SCROLL_BAR_LEFT - BOOKSHELF_UI_WIDTH, + SCROLL_BAR_TOP, SCROLL_BAR_WIDTH, SCROLL_BAR_HEIGHT, mouseX, mouseY)){ + scrolling = true; + } + + if(!mouseHeld || getMaxScrollRows() == 0) scrolling = false; + + if(scrolling){ + scroll = MathHelper.clamp((float)(mouseY - SCROLL_BAR_TOP - SCROLL_HANDLE_HEIGHT/2 - guiTop) + /(SCROLL_BAR_HEIGHT - SCROLL_HANDLE_HEIGHT), 0, 1); + arcaneWorkbenchContainer.scrollTo((int)(getMaxScrollRows() * scroll + 0.5f)); + } + Slot slot = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT); // Tests if there is a wand in the workbench and edits the positioning accordingly if(slot.getHasStack() && slot.getStack().getItem() instanceof IWorkbenchItem && ((IWorkbenchItem)slot.getStack().getItem()).showTooltip(slot.getStack())){ xSize = MAIN_GUI_WIDTH + TOOLTIP_WIDTH; - guiLeft = (this.width - this.xSize) / 2; - this.applyBtn.x = (this.width - TOOLTIP_WIDTH) / 2 + 64; +// guiLeft = (this.width - this.xSize) / 2; +// this.applyBtn.x = (this.width - TOOLTIP_WIDTH) / 2 + 64; }else{ xSize = MAIN_GUI_WIDTH; - guiLeft = (this.width - this.xSize) / 2; - this.applyBtn.x = this.width / 2 + 64; +// guiLeft = (this.width - this.xSize) / 2; +// this.applyBtn.x = this.width / 2 + 64; } this.applyBtn.enabled = slot.getHasStack(); @@ -218,6 +288,15 @@ public class GuiArcaneWorkbench extends GuiContainer { } } + // Bookshelf interface + DrawingUtils.drawTexturedRect(guiLeft - BOOKSHELF_UI_WIDTH, guiTop, 0, 256, BOOKSHELF_UI_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT); + + // Scroll bar + DrawingUtils.drawTexturedRect(guiLeft - BOOKSHELF_UI_WIDTH + SCROLL_BAR_LEFT, + guiTop + SCROLL_BAR_TOP + (int)(scroll * (SCROLL_BAR_HEIGHT - SCROLL_HANDLE_HEIGHT) + 0.5f), + getMaxScrollRows() > 0 ? 30 : 30 + SCROLL_BAR_WIDTH, 476, + SCROLL_BAR_WIDTH, SCROLL_HANDLE_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); + // Tooltip only drawn if there is a wand if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack()){ @@ -302,11 +381,16 @@ public class GuiArcaneWorkbench extends GuiContainer { null); x += 18; GlStateManager.disableDepth(); + GlStateManager.disableLighting(); // Whyyyyyy? } } } } + this.searchField.drawTextBox(); // Easier to do this last, then we don't need to re-bind the GUI texture twice + + GlStateManager.color(1, 1, 1, 1); + Minecraft.getMinecraft().renderEngine.bindTexture(texture); // Fixes the bug that caused the slot highlight to render opaque. I don't know why it works, it just works! @@ -324,6 +408,7 @@ public class GuiArcaneWorkbench extends GuiContainer { : I18n.format(this.arcaneWorkbenchInventory.getName()), 8, 6, 4210752); this.fontRenderer.drawString(this.playerInventory.hasCustomName() ? this.playerInventory.getName() : I18n.format(this.playerInventory.getName()), 8, this.ySize - 96 + 2, 4210752); + this.fontRenderer.drawString(I18n.format("container." + Wizardry.MODID + ":arcane_workbench.bookshelves"), 8 - BOOKSHELF_UI_WIDTH, 6, 4210752); if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack()){ @@ -421,28 +506,18 @@ public class GuiArcaneWorkbench extends GuiContainer { } } } + + this.buttonList.forEach(b -> b.drawButtonForegroundLayer(mouseX, mouseY)); } - @Override - public void initGui(){ - this.mc.player.openContainer = this.inventorySlots; - this.guiLeft = (this.width - this.xSize) / 2; - this.guiTop = (this.height - this.ySize) / 2; - Keyboard.enableRepeatEvents(true); - this.buttonList.clear(); - this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width / 2 + 64, this.height / 2 + 3)); - } - - @Override - public void onGuiClosed(){ - super.onGuiClosed(); - Keyboard.enableRepeatEvents(false); - } + // Controls @Override protected void actionPerformed(GuiButton button){ + if(button.enabled){ - if(button.id == 0){ + + if(button == applyBtn){ // Packet building IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.APPLY_BUTTON); WizardryPacketHandler.net.sendToServer(msg); @@ -452,10 +527,106 @@ public class GuiArcaneWorkbench extends GuiContainer { // Animation animationTimer = 20; } + + if(button instanceof GuiButtonSort) this.arcaneWorkbenchContainer.setSortType(((GuiButtonSort)button).sortType); } } - private class GuiButtonApply extends GuiButton { + private int getMaxScrollRows(){ + return Math.max(0, MathHelper.ceil((float)arcaneWorkbenchContainer.getActiveBookshelfSlots().size() + / ContainerArcaneWorkbench.BOOKSHELF_SLOTS_X) - ContainerArcaneWorkbench.BOOKSHELF_SLOTS_Y); + } + + @Override + public void handleMouseInput() throws IOException{ + + super.handleMouseInput(); + int scrollDist = -Mouse.getEventDWheel(); + + if(scrollDist != 0 && getMaxScrollRows() > 0){ + + if(scrollDist > 0) this.scroll += 1f / getMaxScrollRows(); + if(scrollDist < 0) this.scroll -= 1f / getMaxScrollRows(); + + scroll = MathHelper.clamp(scroll, 0, 1); + + arcaneWorkbenchContainer.scrollTo((int)(scroll * getMaxScrollRows() + 0.5f)); + } + } + + @Override + protected void handleMouseClick(Slot slot, int slotId, int mouseButton, ClickType type){ + + searchNeedsClearing = true; + + // Click type behaves weirdly, don't use it! Query the item stack held by the cursor instead + if(slot instanceof SlotBookList && ((SlotBookList)slot).hasDelegate() && playerInventory.getItemStack().isEmpty()){ + // If no item is currently being moved, divert book list slots to send the virtual slot through instead + // See explanation in ContainerArcaneWorkbench + Slot virtualSlot = ((SlotBookList)slot).getDelegate(); + super.handleMouseClick(virtualSlot, virtualSlot.slotNumber, mouseButton, type); + }else{ + super.handleMouseClick(slot, slotId, mouseButton, type); + } + + arcaneWorkbenchContainer.updateActiveBookshelfSlots(); + } + + @Override + protected void keyTyped(char typedChar, int keyCode) throws IOException { + + if(this.searchNeedsClearing){ + this.searchNeedsClearing = false; + this.searchField.setText(""); + } + + if(this.searchField.textboxKeyTyped(typedChar, keyCode)){ + arcaneWorkbenchContainer.setSearchText(searchField.getText().toLowerCase(Locale.ROOT)); + }else{ + super.keyTyped(typedChar, keyCode); + } + } + + // Nested classes + + private class GuiButtonSort extends GuiButton { + + private final ContainerArcaneWorkbench.SortType sortType; + + public GuiButtonSort(int id, int x, int y, ContainerArcaneWorkbench.SortType sortType){ + super(id, x, y, 10, 10, I18n.format("container." + Wizardry.MODID + ":arcane_workbench.sort_" + sortType.name)); + this.sortType = sortType; + } + + @Override + public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){ + + if(this.visible){ + + // Whether the button is highlighted + this.hovered = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height; + + int k = 0; + int l = 476 + this.sortType.ordinal() * this.height; + + if(sortType == GuiArcaneWorkbench.this.arcaneWorkbenchContainer.getSortType()){ + k += this.width; + if(GuiArcaneWorkbench.this.arcaneWorkbenchContainer.isSortDescending()) k += this.width; + } + + DrawingUtils.drawTexturedRect(this.x, this.y, k, l, this.width, this.height, TEXTURE_WIDTH, TEXTURE_HEIGHT); + + } + } + + @Override + public void drawButtonForegroundLayer(int mouseX, int mouseY){ + if(hovered) drawHoveringText(this.displayString, mouseX - guiLeft, mouseY - guiTop); + } + + } + + private static class GuiButtonApply extends GuiButton { public GuiButtonApply(int id, int x, int y){ super(id, x, y, 16, 16, I18n.format("container." + Wizardry.MODID + ":arcane_workbench.apply")); @@ -481,7 +652,7 @@ public class GuiArcaneWorkbench extends GuiContainer { //colour = 10526880; } - DrawingUtils.drawTexturedRect(this.x, this.y, k, l, this.width, this.height, 512, 256); + DrawingUtils.drawTexturedRect(this.x, this.y, k, l, this.width, this.height, TEXTURE_WIDTH, TEXTURE_HEIGHT); //this.drawCenteredString(minecraft.fontRenderer, this.displayString, this.x + this.width / 2, // this.y + (this.height - 8) / 2, colour); } diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index be8297c6..43c4a0de 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -1,27 +1,52 @@ package electroblob.wizardry.inventory; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.block.BlockBookshelf; import electroblob.wizardry.event.SpellBindEvent; import electroblob.wizardry.item.IWorkbenchItem; import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.registry.WizardryAdvancementTriggers; import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.spell.Spell; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import electroblob.wizardry.util.WandHelper; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.ClickType; 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.tileentity.TileEntity; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraftforge.common.MinecraftForge; -import java.util.HashSet; -import java.util.Set; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; +/** + * The container for the arcane workbench GUI. + *

+ * The virtual slot system works as follows:

+ * - The container has two types of slots: {@link SlotBookList} and {@link VirtualSlot}.
+ * - The {@code SlotBookList}s are the ones that actually get displayed. They essentially delegate all their + * functions to the relevant {@code VirtualSlot}.
+ * - Each {@code VirtualSlot} refers to a specific slot in another {@link IInventory} nearby, but is not displayed + * directly on the GUI. They are sorted and filtered according to the GUI input via + * {@link ContainerArcaneWorkbench#getActiveBookshelfSlots()}.
+ * - When a stack is taken from a {@code SlotBookList} (or its current stack is queried), the {@code VirtualSlot} + * it delegates to depends on the current search term, sort order and state of the linked bookshelves.
+ * - When a stack is inserted into a {@code SlotBookList}, the {@code VirtualSlot} it delegates to depends + * instead on where the stack came from originally and which virtual slots are free.
+ * - Finally, the {@code SlotBookList}s are only really used on the client side (though they are included on the server + * for consistency, just in case). The bookshelf slot delegates to a virtual slot on the client side (which is where + * the search and sorting is done), and then the click is sent to the server. + */ public class ContainerArcaneWorkbench extends Container { /** The arcane workbench tile entity associated with this container. */ @@ -36,6 +61,19 @@ public class ContainerArcaneWorkbench extends Container { public static final int SLOT_RADIUS = 42; + public static final int BOOKSHELF_SLOTS_X = 5; + public static final int BOOKSHELF_SLOTS_Y = 10; + + public static final int PLAYER_INVENTORY_SIZE = 36; + + private List bookshelfSlots = new ArrayList<>(); + private List activeBookshelfSlots = new ArrayList<>(); + + private int scroll = 0; + private SortType sortType = SortType.TIER; + private boolean sortDescending = false; + private String searchText = ""; + public ContainerArcaneWorkbench(IInventory inventory, TileEntityArcaneWorkbench tileentity){ this.tileentity = tileentity; @@ -70,6 +108,15 @@ public class ContainerArcaneWorkbench extends Container { } } + for(int y = 0; y < BOOKSHELF_SLOTS_Y; y++){ + for(int x = 0; x < BOOKSHELF_SLOTS_X; x++){ + int index = x + y * BOOKSHELF_SLOTS_X; + this.addSlotToContainer(new SlotBookList(tileentity, UPGRADE_SLOT + 1 + index, -114 + x * 18, 34 + y * 18, this, index)); + } + } + + refreshBookshelfSlots(); // Must be done last + this.onSlotChanged(CENTRE_SLOT, wand, null); } @@ -181,38 +228,42 @@ public class ContainerArcaneWorkbench extends Container { ItemStack stack = slot.getStack(); // The stack that was there originally remainder = stack.copy(); // A copy of that stack - // Workbench -> inventory + // Workbench -> inventory/bookshelves if(clickedSlotId <= UPGRADE_SLOT){ - // Tries to move the stack into the player's inventory. If this fails... - if(!this.mergeItemStack(stack, UPGRADE_SLOT + 1, this.inventorySlots.size(), true)){ - return ItemStack.EMPTY; // ...nothing else happens. + // Try to move the stack into the bookshelves. If this fails... + if(getBookshelfSlots().isEmpty() || !this.mergeItemStack(stack, getBookshelfSlots().get(0).slotNumber, + getBookshelfSlots().get(getBookshelfSlots().size()-1).slotNumber + 1, false)){ + // ...try to move the stack into the player's inventory. If this fails... + if(!this.mergeItemStack(stack, UPGRADE_SLOT + 1, UPGRADE_SLOT + 1 + PLAYER_INVENTORY_SIZE, true)){ + return ItemStack.EMPTY; // ...nothing else happens. + } } } - // Inventory -> workbench - else{ - // The following logic prevents shift-clicking transferring the items to the wrong slot. - int minSlotId = 0; - int maxSlotId = UPGRADE_SLOT; + // Bookshelves -> workbench/inventory + else if(getSlot(clickedSlotId) instanceof VirtualSlot){ - if(stack.getItem() instanceof ItemSpellBook){ - minSlotId = 0; - maxSlotId = CRYSTAL_SLOT - 1; - }else if(getSlot(CRYSTAL_SLOT).isItemValid(stack)){ - minSlotId = CRYSTAL_SLOT; - maxSlotId = CRYSTAL_SLOT; - }else if(getSlot(CENTRE_SLOT).isItemValid(stack)){ - minSlotId = CENTRE_SLOT; - maxSlotId = CENTRE_SLOT; - }else if(getSlot(UPGRADE_SLOT).isItemValid(stack)){ - minSlotId = UPGRADE_SLOT; - maxSlotId = UPGRADE_SLOT; - }else{ - // If none of the above cases were true, then the item won't fit in the workbench. - return ItemStack.EMPTY; + int[] slotRange = findSlotRangeForItem(stack); + + // Try to move the stack into the workbench. If this fails... + if(slotRange == null || !this.mergeItemStack(stack, slotRange[0], slotRange[1] + 1, false)){ + // ...try to move the stack into the player's inventory. If this fails... + if(!this.mergeItemStack(stack, UPGRADE_SLOT + 1, UPGRADE_SLOT + 1 + PLAYER_INVENTORY_SIZE, true)){ + return ItemStack.EMPTY; // ...nothing else happens. + } } + } + // Inventory -> workbench/bookshelves + else{ - if(!this.mergeItemStack(stack, minSlotId, maxSlotId + 1, false)){ - return ItemStack.EMPTY; + int[] slotRange = findSlotRangeForItem(stack); + + // Try to move the stack into the workbench. If this fails... + if(slotRange == null || !this.mergeItemStack(stack, slotRange[0], slotRange[1] + 1, false)){ + // ...try to move the stack into the bookshelves. If this fails... + if(getBookshelfSlots().isEmpty() || !this.mergeItemStack(stack, getBookshelfSlots().get(0).slotNumber, + getBookshelfSlots().get(getBookshelfSlots().size()-1).slotNumber + 1, false)){ + return ItemStack.EMPTY; // ...nothing else happens. + } } } @@ -232,18 +283,59 @@ public class ContainerArcaneWorkbench extends Container { return remainder; } - // Overridden to stop stacks merging into 'removed' slots. - @Override - protected boolean mergeItemStack(ItemStack stack, int minSlotID, int maxSlotID, boolean p_75135_4_){ + /** + * Returns the minimum and maximum IDs (inclusive) of the workbench slots that are appropriate for the given stack, + * or null if no slots are appropriate. Note that this does mean the stack will fit, only that it is valid + * for all of the slots in the given range, and will fit if there is space for it. + * @param stack The stack to find a slot for + * @param excludeBookshelves Whether to exclude the bookshelf slots (useful when transferring from them) + * @return A 2-element int array of the minimum and maximum slot IDs respectively + */ + @Nullable + private int[] findSlotRangeForItem(ItemStack stack){ - for(int i = minSlotID; i < maxSlotID; i++){ - // System.out.println(this.getSlot(i).xDisplayPosition); - if(this.getSlot(i).xPos >= 0 && this.getSlot(i).yPos >= 0 && !this.getSlot(i).getHasStack()){ - return super.mergeItemStack(stack, minSlotID, maxSlotID, p_75135_4_); + if(this.getSlot(0).isItemValid(stack)){ // Spell books + + ItemStack centreStack = getSlot(CENTRE_SLOT).getStack(); + + if(centreStack.getItem() instanceof IWorkbenchItem){ + // Restrict the range to visible slots + // (How did I not think of this before? Why did I go to the trouble of overriding mergeItemStack? And + // how did that fix ever work in the first place?!) + int spellSlots = ((IWorkbenchItem)centreStack.getItem()).getSpellSlotCount(centreStack); + if(spellSlots > 0){ + return new int[]{0, spellSlots - 1}; + } + } + + }else if(getSlot(CRYSTAL_SLOT).isItemValid(stack)){ + return new int[]{CRYSTAL_SLOT, CRYSTAL_SLOT}; + + }else if(getSlot(CENTRE_SLOT).isItemValid(stack)){ + return new int[]{CENTRE_SLOT, CENTRE_SLOT}; + + }else if(getSlot(UPGRADE_SLOT).isItemValid(stack)){ + return new int[]{UPGRADE_SLOT, UPGRADE_SLOT}; + } + + return null; // It won't fit! + } + + @Override + public ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, EntityPlayer player){ + + // -999 is used for slots in the player inventory + if(slotId > 0 && getSlot(slotId) instanceof SlotBookList){ + + ItemStack stack = player.inventory.getItemStack(); + + if(!stack.isEmpty() && !getBookshelfSlots().isEmpty()){ + mergeItemStack(stack, getBookshelfSlots().get(0).slotNumber, getBookshelfSlots().get(getBookshelfSlots().size() - 1).slotNumber + 1, false); + return stack; } } - // Only returns false if none of the slots given are enabled and empty - return false; + + return super.slotClick(slotId, dragType, clickTypeIn, player); } /** @@ -272,4 +364,136 @@ public class ContainerArcaneWorkbench extends Container { } } + /** Scrolls to the given row number. */ + public void scrollTo(int row){ + this.scroll = row; + } + + /** Sets the sorting type to the given type, or toggles the sort direction if it is already that type. */ + public void setSortType(SortType sortType){ + + if(this.sortType == sortType){ + this.sortDescending = !this.sortDescending; + }else{ + this.sortType = sortType; + this.sortDescending = false; + } + + updateActiveBookshelfSlots(); + } + + /** Returns the current sorting type. */ + public SortType getSortType(){ + return sortType; + } + + /** Returns true if the current sorting is in descending order, false otherwise. */ + public boolean isSortDescending(){ + return sortDescending; + } + + /** Sets the search text for the bookshelf slots. */ + public void setSearchText(@Nonnull String searchText){ + this.searchText = searchText; + this.scrollTo(0); + updateActiveBookshelfSlots(); + } + + /** Returns all bookshelf slots currently linked to this container, including empty ones. The returned list + * is a copy of the internal virtual slot list, with any invalid slots removed. */ + public List getBookshelfSlots(){ + List validSlots = new ArrayList<>(bookshelfSlots); + validSlots.removeIf(s -> !s.isValid()); + return validSlots; + } + + /** Updates the active bookshelf slots with the current search term and sorting. Client-side only! */ + public void updateActiveBookshelfSlots(){ + activeBookshelfSlots = bookshelfSlots.stream().filter(s -> s.isValid() && !s.getStack().isEmpty() + // Slot 0 is a convenient way of testing if the item is a valid spell book + && this.getSlot(0).isItemValid(s.getStack()) + && Spell.byMetadata(s.getStack().getMetadata()).matches(searchText)) + // TODO: This doesn't account for non-spell book items at the moment + .sorted(Comparator.comparing(s -> Spell.byMetadata(s.getStack().getMetadata()), + sortDescending ? sortType.comparator.reversed() : sortType.comparator)) + .collect(Collectors.toList()); + } + + /** Returns all the {@link VirtualSlot}s that are currently active, sorted according to the current sort order. A + * virtual slot is active if it is not empty and its contents match the current search term (if any). */ + public List getActiveBookshelfSlots(){ + return activeBookshelfSlots; + } + + /** Returns all the {@link VirtualSlot}s that are currently visible on screen (and hence have an associated 'real' + * slot), accounting for search and scrolling, and sorted according to the current sort order. */ + public List getVisibleBookshelfSlots(){ + List activeSlots = getActiveBookshelfSlots(); + return activeSlots.subList(BOOKSHELF_SLOTS_X * scroll, activeSlots.size()); + } + + // The following operations are expensive so should not be done every tick! + // N.B. If we drop the requirement of it working with any container it could potentially be a lot easier since + // we then always have control over the bookshelf classes + + /** Called on initialisation, and whenever a bookshelf is added or removed. */ + private void refreshBookshelfSlots(){ + + this.inventorySlots.removeAll(bookshelfSlots); + // TESTME: May need to do this for inventoryItemStacks (probably not though, seems like MC handles it) + bookshelfSlots.clear(); + + for(IInventory bookshelf : findNearbyBookshelves()){ + for(int i=0; i findNearbyBookshelves(){ + + List bookshelves = new ArrayList<>(); + + int searchRadius = 4; // TODO: Config option for this + + for(int x = -searchRadius; x <= searchRadius; x++){ + for(int y = -searchRadius; y <= searchRadius; y++){ + for(int z = -searchRadius; z <= searchRadius; z++){ + + BlockPos pos = this.tileentity.getPos().add(x, y, z); + // TODO: Config option for allowed containers + if(this.tileentity.getWorld().getBlockState(pos).getBlock() instanceof BlockBookshelf){ + TileEntity te = this.tileentity.getWorld().getTileEntity(pos); + if(te instanceof IInventory && te != this.tileentity) bookshelves.add((IInventory)te); + } + + } + } + } + + return bookshelves; + + } + + public enum SortType { + + TIER("tier", Comparator.naturalOrder()), + ELEMENT("element", Comparator.comparing(Spell::getElement).thenComparing(Spell::getTier)), + ALPHABETICAL("alphabetical", Comparator.comparing(Spell::getUnlocalisedName)); + + public String name; + public Comparator comparator; + + SortType(String name, Comparator comparator){ + this.name = name; + this.comparator = comparator; + } + + } } diff --git a/src/main/java/electroblob/wizardry/inventory/SlotBookList.java b/src/main/java/electroblob/wizardry/inventory/SlotBookList.java new file mode 100644 index 00000000..8d7696bd --- /dev/null +++ b/src/main/java/electroblob/wizardry/inventory/SlotBookList.java @@ -0,0 +1,70 @@ +package electroblob.wizardry.inventory; + +import electroblob.wizardry.item.ItemSpellBook; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; + +import java.util.List; + +public class SlotBookList extends SlotItemClassList { + + private final ContainerArcaneWorkbench container; + /** The position in the book list this slot occupies (in other words: the slot index, but offset so the first book + * list slot starts at 0). */ + private final int listIndex; + + public SlotBookList(IInventory inventory, int index, int x, int y, ContainerArcaneWorkbench container, int listIndex){ + super(inventory, index, x, y, 64, ItemSpellBook.class); + this.container = container; + this.listIndex = listIndex; + } + + /** Returns the {@link VirtualSlot} this slot currently accesses, or null if it does not currently have one. */ + public VirtualSlot getDelegate(){ + return hasDelegate() ? container.getVisibleBookshelfSlots().get(listIndex) : null; + } + + /** Returns true if this slot currently accesses a {@link VirtualSlot}, false otherwise. */ + public boolean hasDelegate(){ + return listIndex < container.getVisibleBookshelfSlots().size(); + } + + @Override + public ItemStack getStack(){ + return hasDelegate() ? getDelegate().getStack() : ItemStack.EMPTY; // Delegate item lookup to virtual slot + } + + // This doesn't depend on the client-side search and sorting stuff so it can be done in here + @Override + public boolean isItemValid(ItemStack stack){ + // It's valid if there is a virtual slot that will accept it + return container.getBookshelfSlots().stream().anyMatch(s -> s.isItemValid(stack)); + } + + // Actual item interaction is delegated client-side before it ever gets here (in the GUI class) + // Therefore we only need to handle empty slot clicks + + @Override + public ItemStack decrStackSize(int amount){ + return ItemStack.EMPTY; + } + + @Override + public boolean canTakeStack(EntityPlayer player){ + return false; + } + + @Override + public ItemStack onTake(EntityPlayer player, ItemStack stack){ + return stack; + } + + @Override + public void putStack(ItemStack stack){ + // Can't handle this here either because it gets tangled up with the 348-line behemoth that is + // Container#slotClick, which has all sorts of side-effects + // Instead, we need to delegate client-side, as before + } + +} diff --git a/src/main/java/electroblob/wizardry/inventory/VirtualSlot.java b/src/main/java/electroblob/wizardry/inventory/VirtualSlot.java new file mode 100644 index 00000000..cf806246 --- /dev/null +++ b/src/main/java/electroblob/wizardry/inventory/VirtualSlot.java @@ -0,0 +1,79 @@ +package electroblob.wizardry.inventory; + +import electroblob.wizardry.tileentity.TileEntityBookshelf; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; + +/** + * A {@code VirtualSlot} represents a slot in an inventory other than the one that is currently open. Like regular slots, + * they hold a single {@link net.minecraft.item.ItemStack}, but unlike regular slots, they are not drawn to the screen + * and cannot be interacted with directly. A virtual slot is effectively a reference to a specific slot in some other + * inventory that allows the details of that inventory to be abstracted away from the current container. + *

+ * N.B. For normal slots, {@code slotIndex == slotNumber} (index is for the inventory, number is for the container). + * Importantly, for virtual slots this is not the case, since the {@code Container} they belong to is not the + * one associated with the virtual slot's {@code IInventory}. + */ +public class VirtualSlot extends Slot { + + private final TileEntity tileEntity; + + public VirtualSlot(IInventory inventory, int index){ + super(inventory, index, -999, -999); + if(!(inventory instanceof TileEntity)) throw new IllegalArgumentException("Inventory must be a tile entity!"); + this.tileEntity = (TileEntity)inventory; + } + + @Override + public boolean isEnabled(){ + return false; // Virtual slots are never displayed + } + + // We don't really want to be updating the bookshelves every single tick (let alone every frame!), so we're going + // to have to do some 'assuming things stay the same until told otherwise'. This means it's possible that virtual + // slots will remain even when their containers are gone, so we need a failsafe for when that happens. + + /** Returns true if this slot is valid, i.e. the tile entity still exists. */ + public boolean isValid(){ + return !tileEntity.isInvalid(); + } + + // Normally the container decides if a stack is valid, but that's not going to work here + @Override + public boolean isItemValid(ItemStack stack){ + // getSlotIndex() is required here, NOT slotNumber (which is only populated when the slot is added to a + // container - the javadoc is wrong, it has nothing to do with inventories) + return isValid() && inventory.isItemValidForSlot(getSlotIndex(), stack); + } + + @Override + public boolean canTakeStack(EntityPlayer playerIn){ + return isValid() && super.canTakeStack(playerIn); + } + + @Override + public ItemStack onTake(EntityPlayer player, ItemStack stack){ + return isValid() ? super.onTake(player, stack) : ItemStack.EMPTY; + } + + @Override + public ItemStack getStack(){ + return isValid() ? super.getStack() : ItemStack.EMPTY; + } + + @Override + public void putStack(ItemStack stack){ + if(isValid() && inventory instanceof TileEntityBookshelf) ((TileEntityBookshelf)inventory).sync(); + if(isValid()) super.putStack(stack); + } + + @Override + public ItemStack decrStackSize(int amount){ + if(isValid() && inventory instanceof TileEntityBookshelf) ((TileEntityBookshelf)inventory).sync(); + return isValid() ? super.decrStackSize(amount) : ItemStack.EMPTY; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java index 863bd31c..e817980f 100644 --- a/src/main/java/electroblob/wizardry/spell/Spell.java +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -117,6 +117,11 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C public static final String SPLASH_EFFECT_DURATION = "splash_effect_duration"; public static final String SPLASH_EFFECT_STRENGTH = "splash_effect_strength"; + // TODO: Translations for these? + public static final String TIER_MATCH_PREFIX = "tier="; + public static final String ELEMENT_MATCH_PREFIX = "element="; + public static final String TYPE_MATCH_PREFIX = "type="; + /** Forge registry-based replacement for the internal spells list. */ public static IForgeRegistry registry; @@ -675,6 +680,30 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C return net.minecraft.client.resources.I18n.format(getDescriptionTranslationKey()); } + /** + * Returns whether this spell matches the given string. Client-side only! A spell matches a particular string + * if any of the following conditions are true:

+ * - The spell's localised name contains the given string
+ * - The string starts with "tier:", and the localised name of the spell's tier contains the given string
+ * - The string starts with "element:", and the localised name of the spell's element contains the given string
+ * - The string starts with "type:", and the localised name of the spell's type contains the given string

+ * Matches are not case-sensitive. + * @param text The string to tested + * @return True if this spell matches the given string, false otherwise. + */ + public boolean matches(@Nonnull String text){ + + if(text.startsWith(TIER_MATCH_PREFIX)){ + return getTier().getDisplayName().toLowerCase(Locale.ROOT).contains(text.substring(TIER_MATCH_PREFIX.length())); + }else if(text.startsWith(ELEMENT_MATCH_PREFIX)){ + return getElement().getDisplayName().toLowerCase(Locale.ROOT).contains(text.substring(ELEMENT_MATCH_PREFIX.length())); + }else if(text.startsWith(TYPE_MATCH_PREFIX)){ + return getType().getDisplayName().toLowerCase(Locale.ROOT).contains(text.substring(TYPE_MATCH_PREFIX.length())); + }else{ + return getDisplayName().toLowerCase(Locale.ROOT).contains(text); + } + } + // ============================================ Sound methods ============================================== /** diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 0ad6e088..c51e35cf 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -524,6 +524,11 @@ container.ebwizardry\:arcane_workbench.apply=Apply container.ebwizardry\:arcane_workbench.mana=Mana\: container.ebwizardry\:arcane_workbench.upgrades=Applied Upgrades\: +container.ebwizardry\:arcane_workbench.bookshelves=Bookshelves +container.ebwizardry\:arcane_workbench.sort_tier=Sort by Tier +container.ebwizardry\:arcane_workbench.sort_element=Sort by Element +container.ebwizardry\:arcane_workbench.sort_alphabetical=Sort Alphabetically + container.ebwizardry\:bookshelf=Bookshelf tier.novice=Novice diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 6649b52c..6dee3005 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -524,6 +524,11 @@ container.ebwizardry\:arcane_workbench.apply=Apply container.ebwizardry\:arcane_workbench.mana=Mana\: container.ebwizardry\:arcane_workbench.upgrades=Applied Upgrades\: +container.ebwizardry\:arcane_workbench.bookshelves=Bookshelves +container.ebwizardry\:arcane_workbench.sort_tier=Sort by Tier +container.ebwizardry\:arcane_workbench.sort_element=Sort by Element +container.ebwizardry\:arcane_workbench.sort_alphabetical=Sort Alphabetically + container.ebwizardry\:bookshelf=Bookshelf tier.novice=Novice diff --git a/src/main/resources/assets/ebwizardry/textures/gui/arcane_workbench.png b/src/main/resources/assets/ebwizardry/textures/gui/arcane_workbench.png index 814ea32d5f16f547956eb97d2cc5f7516b6023bc..786a491b95d8b0de62fd33bb51933362fa83f6a3 100644 GIT binary patch literal 40318 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

4QJr#rRihi>^k^VrQN`*z)rQ+`qY;6HG?i*UB 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 literal 31627 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 Date: Tue, 31 Mar 2020 21:33:18 +0100 Subject: [PATCH 014/415] Add recipes for bookshelves --- .../ebwizardry/recipes/acacia_bookshelf.json | 20 +++++++++++++++++++ .../ebwizardry/recipes/birch_bookshelf.json | 20 +++++++++++++++++++ .../recipes/dark_oak_bookshelf.json | 20 +++++++++++++++++++ .../ebwizardry/recipes/jungle_bookshelf.json | 20 +++++++++++++++++++ .../ebwizardry/recipes/oak_bookshelf.json | 20 +++++++++++++++++++ .../ebwizardry/recipes/spruce_bookshelf.json | 20 +++++++++++++++++++ 6 files changed, 120 insertions(+) create mode 100644 src/main/resources/assets/ebwizardry/recipes/acacia_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/birch_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/dark_oak_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/jungle_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/oak_bookshelf.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/spruce_bookshelf.json diff --git a/src/main/resources/assets/ebwizardry/recipes/acacia_bookshelf.json b/src/main/resources/assets/ebwizardry/recipes/acacia_bookshelf.json new file mode 100644 index 00000000..510f7286 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/acacia_bookshelf.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + " c ", + "www" + ], + "key": { + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 4 + } + }, + "result": { + "item": "ebwizardry:acacia_bookshelf" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/birch_bookshelf.json b/src/main/resources/assets/ebwizardry/recipes/birch_bookshelf.json new file mode 100644 index 00000000..5a0ed340 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/birch_bookshelf.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + " c ", + "www" + ], + "key": { + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 2 + } + }, + "result": { + "item": "ebwizardry:birch_bookshelf" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/dark_oak_bookshelf.json b/src/main/resources/assets/ebwizardry/recipes/dark_oak_bookshelf.json new file mode 100644 index 00000000..ffad88f7 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/dark_oak_bookshelf.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + " c ", + "www" + ], + "key": { + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 5 + } + }, + "result": { + "item": "ebwizardry:dark_oak_bookshelf" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/jungle_bookshelf.json b/src/main/resources/assets/ebwizardry/recipes/jungle_bookshelf.json new file mode 100644 index 00000000..20676b56 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/jungle_bookshelf.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + " c ", + "www" + ], + "key": { + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 3 + } + }, + "result": { + "item": "ebwizardry:jungle_bookshelf" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/oak_bookshelf.json b/src/main/resources/assets/ebwizardry/recipes/oak_bookshelf.json new file mode 100644 index 00000000..be8a2963 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/oak_bookshelf.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + " c ", + "www" + ], + "key": { + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 0 + } + }, + "result": { + "item": "ebwizardry:oak_bookshelf" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/spruce_bookshelf.json b/src/main/resources/assets/ebwizardry/recipes/spruce_bookshelf.json new file mode 100644 index 00000000..291fb0c3 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/spruce_bookshelf.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "www", + " c ", + "www" + ], + "key": { + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 1 + } + }, + "result": { + "item": "ebwizardry:spruce_bookshelf" + } +} \ No newline at end of file From a67eb2381d0e28f56b7a59f1f310f40eb6c7457f Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 4 Apr 2020 21:32:57 +0100 Subject: [PATCH 015/415] Improve bookshelf search and account for undiscovered spells --- .../electroblob/wizardry/CommonProxy.java | 3 +- .../wizardry/client/ClientProxy.java | 3 +- .../inventory/ContainerArcaneWorkbench.java | 1 - .../electroblob/wizardry/spell/Spell.java | 74 +++++++++++++++---- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/src/main/java/electroblob/wizardry/CommonProxy.java b/src/main/java/electroblob/wizardry/CommonProxy.java index 63cf5ede..947a6e93 100644 --- a/src/main/java/electroblob/wizardry/CommonProxy.java +++ b/src/main/java/electroblob/wizardry/CommonProxy.java @@ -19,6 +19,7 @@ import net.minecraft.util.text.translation.I18n; import net.minecraft.world.World; import net.minecraftforge.common.config.Property; +import javax.annotation.Nullable; import java.util.List; import java.util.Set; @@ -74,7 +75,7 @@ public class CommonProxy { // SECTION Items // =============================================================================================================== - public boolean shouldDisplayDiscovered(Spell spell, ItemStack stack){ + public boolean shouldDisplayDiscovered(Spell spell, @Nullable ItemStack stack){ return false; } diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 7b4f2e6e..b81987be 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -79,6 +79,7 @@ import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.client.registry.RenderingRegistry; import org.lwjgl.input.Keyboard; +import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.List; @@ -208,7 +209,7 @@ public class ClientProxy extends CommonProxy { // =============================================================================================================== @Override - public boolean shouldDisplayDiscovered(Spell spell, ItemStack stack){ + public boolean shouldDisplayDiscovered(Spell spell, @Nullable ItemStack stack){ EntityPlayerSP player = Minecraft.getMinecraft().player; diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index 43c4a0de..a3dc3704 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -288,7 +288,6 @@ public class ContainerArcaneWorkbench extends Container { * or null if no slots are appropriate. Note that this does mean the stack will fit, only that it is valid * for all of the slots in the given range, and will fit if there is space for it. * @param stack The stack to find a slot for - * @param excludeBookshelves Whether to exclude the bookshelf slots (useful when transferring from them) * @return A 2-element int array of the minimum and maximum slot IDs respectively */ @Nullable diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java index e817980f..8db0c753 100644 --- a/src/main/java/electroblob/wizardry/spell/Spell.java +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -118,9 +118,15 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C public static final String SPLASH_EFFECT_STRENGTH = "splash_effect_strength"; // TODO: Translations for these? - public static final String TIER_MATCH_PREFIX = "tier="; - public static final String ELEMENT_MATCH_PREFIX = "element="; - public static final String TYPE_MATCH_PREFIX = "type="; + public static final String TIER_MATCH_PREFIX = "tier"; + public static final String ELEMENT_MATCH_PREFIX = "element"; + public static final String TYPE_MATCH_PREFIX = "type"; + public static final String DISCOVERED_MATCH_PREFIX = "discovered"; + public static final String MODID_MATCH_PREFIX = "modid"; + + public static final String MATCH_CONDITION_SEPARATOR = ";"; + public static final String MATCH_KEY_VALUE_SEPARATOR = "="; + public static final String MATCH_VALUE_SEPARATOR = ","; /** Forge registry-based replacement for the internal spells list. */ public static IForgeRegistry registry; @@ -682,26 +688,68 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C /** * Returns whether this spell matches the given string. Client-side only! A spell matches a particular string - * if any of the following conditions are true:

+ * if any of the following conditions are true: + *

* - The spell's localised name contains the given string
* - The string starts with "tier:", and the localised name of the spell's tier contains the given string
* - The string starts with "element:", and the localised name of the spell's element contains the given string
- * - The string starts with "type:", and the localised name of the spell's type contains the given string

+ * - The string starts with "type:", and the localised name of the spell's type contains the given string + *

* Matches are not case-sensitive. * @param text The string to tested * @return True if this spell matches the given string, false otherwise. */ public boolean matches(@Nonnull String text){ - if(text.startsWith(TIER_MATCH_PREFIX)){ - return getTier().getDisplayName().toLowerCase(Locale.ROOT).contains(text.substring(TIER_MATCH_PREFIX.length())); - }else if(text.startsWith(ELEMENT_MATCH_PREFIX)){ - return getElement().getDisplayName().toLowerCase(Locale.ROOT).contains(text.substring(ELEMENT_MATCH_PREFIX.length())); - }else if(text.startsWith(TYPE_MATCH_PREFIX)){ - return getType().getDisplayName().toLowerCase(Locale.ROOT).contains(text.substring(TYPE_MATCH_PREFIX.length())); - }else{ - return getDisplayName().toLowerCase(Locale.ROOT).contains(text); + if(text.isEmpty()) return true; + + boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(this, null); + + String[] conditions = text.split(MATCH_CONDITION_SEPARATOR); + + for(String condition : conditions){ + + String[] args = condition.split(MATCH_KEY_VALUE_SEPARATOR, 2); + + // Invalid condition, treat the whole lot as a spell name instead + if(args.length < 2) return discovered && getDisplayName().toLowerCase(Locale.ROOT).contains(text); + + String key = args[0]; + String[] values = args[1].split(MATCH_VALUE_SEPARATOR); + + String target; + + switch(key){ + case TIER_MATCH_PREFIX: + // Tier IS known for undiscovered spells so we can match it + target = getTier().getDisplayName().toLowerCase(Locale.ROOT); + break; + case ELEMENT_MATCH_PREFIX: + if(!discovered) return false; // Element is unknown so doesn't match + target = getElement().getDisplayName().toLowerCase(Locale.ROOT); + break; + case TYPE_MATCH_PREFIX: + if(!discovered) return false; // Type is unknown so doesn't match + target = getType().getDisplayName().toLowerCase(Locale.ROOT); + break; + case MODID_MATCH_PREFIX: + if(!discovered) return false; // Mod ID is unknown so doesn't match + target = getRegistryName().getNamespace().toLowerCase(Locale.ROOT); + break; + case DISCOVERED_MATCH_PREFIX: + target = Boolean.toString(discovered); + break; + default: + // Invalid condition, treat the whole lot as a spell name instead + return discovered && getDisplayName().toLowerCase(Locale.ROOT).contains(text); + } + + if(Arrays.stream(values).noneMatch(target::contains)) return false; // Didn't match + } + + return true; // Matched all the conditions, yay! + } // ============================================ Sound methods ============================================== From 525b7e13ddf4ac1833989cdd1cd0d5bc0b0d220b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 5 Apr 2020 15:33:40 +0100 Subject: [PATCH 016/415] Super method should do just fine! --- .../wizardry/client/particle/ParticleDust.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleDust.java b/src/main/java/electroblob/wizardry/client/particle/ParticleDust.java index cd925b81..069f6a40 100644 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleDust.java +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleDust.java @@ -17,15 +17,4 @@ public class ParticleDust extends ParticleWizardry { this.setRBGColorF(1, 1, 1); } - @Override - public void onUpdate(){ - this.prevPosX = this.posX; - this.prevPosY = this.posY; - this.prevPosZ = this.posZ; - this.move(this.motionX, this.motionY, this.motionZ); - - if(this.particleMaxAge-- <= 0){ - this.setExpired(); - } - } } From 938d0df95ccbbe4cd880901ea4d3d4bd623a44d0 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 5 Apr 2020 16:12:46 +0100 Subject: [PATCH 017/415] TODO: Convert magic light to a non-tileentity block --- .../electroblob/wizardry/tileentity/TileEntityMagicLight.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityMagicLight.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityMagicLight.java index 2fa0ab68..e7543b5d 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityMagicLight.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityMagicLight.java @@ -2,6 +2,8 @@ package electroblob.wizardry.tileentity; import net.minecraft.nbt.NBTTagCompound; +// TODO: Remove this class entirely, it really doesn't need to be a tile entity +@Deprecated public class TileEntityMagicLight extends TileEntityTimer { public int[] randomiser; From c5b517248118568707110d0b295fcb40c9c934e5 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 8 Apr 2020 00:16:12 +0100 Subject: [PATCH 018/415] Add lecterns --- .../wizardry/WizardryGuiHandler.java | 10 + .../wizardry/block/BlockBookshelf.java | 37 ++ .../wizardry/block/BlockLectern.java | 122 +++++ .../wizardry/client/ClientProxy.java | 7 +- .../client/gui/GuiArcaneWorkbench.java | 52 +- .../client/gui/GuiButtonSpellSort.java | 57 +++ .../gui/{handbook => }/GuiButtonTurnPage.java | 24 +- .../wizardry/client/gui/GuiLectern.java | 467 ++++++++++++++++++ .../wizardry/client/gui/GuiSpellBook.java | 110 +---- .../wizardry/client/gui/GuiSpellInfo.java | 137 +++++ .../gui/handbook/GuiWizardHandbook.java | 13 +- .../wizardry/client/model/WizardryModels.java | 7 + .../particle/ParticleBlockHighlight.java | 58 +++ .../client/renderer/RenderLectern.java | 53 ++ .../inventory/ContainerArcaneWorkbench.java | 59 +-- .../wizardry/packet/PacketLectern.java | 73 +++ .../packet/WizardryPacketHandler.java | 1 + .../wizardry/registry/WizardryBlocks.java | 13 + .../wizardry/registry/WizardryItems.java | 7 + .../electroblob/wizardry/spell/Spell.java | 5 +- .../tileentity/TileEntityLectern.java | 98 ++++ .../wizardry/util/ISpellSortable.java | 35 ++ .../wizardry/util/ParticleBuilder.java | 2 + .../blockstates/acacia_lectern.json | 14 + .../ebwizardry/blockstates/birch_lectern.json | 14 + .../blockstates/dark_oak_lectern.json | 14 + .../blockstates/jungle_lectern.json | 14 + .../ebwizardry/blockstates/oak_lectern.json | 14 + .../blockstates/spruce_lectern.json | 14 + .../assets/ebwizardry/lang/en_gb.lang | 9 + .../assets/ebwizardry/lang/en_us.lang | 9 + .../models/block/acacia_lectern.json | 10 + .../models/block/birch_lectern.json | 10 + .../models/block/dark_oak_lectern.json | 10 + .../models/block/jungle_lectern.json | 10 + .../ebwizardry/models/block/lectern.json | 116 +++++ .../ebwizardry/models/block/oak_lectern.json | 10 + .../models/block/spruce_lectern.json | 10 + .../models/item/acacia_lectern.json | 3 + .../ebwizardry/models/item/birch_lectern.json | 3 + .../models/item/dark_oak_lectern.json | 3 + .../models/item/jungle_lectern.json | 3 + .../ebwizardry/models/item/oak_lectern.json | 3 + .../models/item/spruce_lectern.json | 3 + .../ebwizardry/spells/arcane_jammer.json | 2 +- .../textures/blocks/acacia_lectern_base.png | Bin 0 -> 1801 bytes .../textures/blocks/acacia_lectern_side.png | Bin 0 -> 1902 bytes .../textures/blocks/acacia_lectern_top.png | Bin 0 -> 1990 bytes .../blocks/acacia_lectern_underside.png | Bin 0 -> 1528 bytes .../textures/blocks/birch_lectern_base.png | Bin 0 -> 1790 bytes .../textures/blocks/birch_lectern_side.png | Bin 0 -> 1808 bytes .../textures/blocks/birch_lectern_top.png | Bin 0 -> 1977 bytes .../blocks/birch_lectern_underside.png | Bin 0 -> 1593 bytes .../textures/blocks/dark_oak_lectern_base.png | Bin 0 -> 369 bytes .../textures/blocks/dark_oak_lectern_side.png | Bin 0 -> 1605 bytes .../textures/blocks/dark_oak_lectern_top.png | Bin 0 -> 521 bytes .../blocks/dark_oak_lectern_underside.png | Bin 0 -> 5532 bytes .../textures/blocks/gilded_dark_oak_wood.png | Bin 526 -> 2075 bytes .../textures/blocks/jungle_lectern_base.png | Bin 0 -> 1763 bytes .../textures/blocks/jungle_lectern_side.png | Bin 0 -> 1864 bytes .../textures/blocks/jungle_lectern_top.png | Bin 0 -> 1954 bytes .../blocks/jungle_lectern_underside.png | Bin 0 -> 1597 bytes .../textures/blocks/oak_lectern_base.png | Bin 0 -> 1844 bytes .../textures/blocks/oak_lectern_side.png | Bin 0 -> 1743 bytes .../textures/blocks/oak_lectern_top.png | Bin 0 -> 2001 bytes .../textures/blocks/oak_lectern_underside.png | Bin 0 -> 1532 bytes .../textures/blocks/spruce_lectern_base.png | Bin 0 -> 1721 bytes .../textures/blocks/spruce_lectern_side.png | Bin 0 -> 1621 bytes .../textures/blocks/spruce_lectern_top.png | Bin 0 -> 1947 bytes .../blocks/spruce_lectern_underside.png | Bin 0 -> 1556 bytes .../textures/entity/lectern_book.png | Bin 0 -> 4480 bytes .../textures/gui/arcane_workbench.png | Bin 40318 -> 40214 bytes .../ebwizardry/textures/gui/lectern.png | Bin 0 -> 43369 bytes .../textures/gui/spell_book_advanced.png | Bin 12024 -> 11179 bytes .../textures/gui/spell_book_apprentice.png | Bin 11927 -> 10989 bytes .../textures/gui/spell_book_master.png | Bin 12366 -> 11748 bytes .../textures/gui/spell_book_novice.png | Bin 11511 -> 10720 bytes .../textures/gui/spell_sort_buttons.png | Bin 0 -> 723 bytes .../textures/particle/block_highlight.png | Bin 0 -> 1465 bytes 79 files changed, 1534 insertions(+), 211 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/block/BlockLectern.java create mode 100644 src/main/java/electroblob/wizardry/client/gui/GuiButtonSpellSort.java rename src/main/java/electroblob/wizardry/client/gui/{handbook => }/GuiButtonTurnPage.java (69%) create mode 100644 src/main/java/electroblob/wizardry/client/gui/GuiLectern.java create mode 100644 src/main/java/electroblob/wizardry/client/gui/GuiSpellInfo.java create mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleBlockHighlight.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderLectern.java create mode 100644 src/main/java/electroblob/wizardry/packet/PacketLectern.java create mode 100644 src/main/java/electroblob/wizardry/tileentity/TileEntityLectern.java create mode 100644 src/main/java/electroblob/wizardry/util/ISpellSortable.java create mode 100644 src/main/resources/assets/ebwizardry/blockstates/acacia_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/birch_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/dark_oak_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/jungle_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/oak_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/blockstates/spruce_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/acacia_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/birch_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/dark_oak_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/jungle_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/oak_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/spruce_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/acacia_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/birch_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/dark_oak_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/jungle_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/oak_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/spruce_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_base.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_underside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_base.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_underside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_lectern_base.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_lectern_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_lectern_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_lectern_underside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/jungle_lectern_base.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/jungle_lectern_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/jungle_lectern_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/jungle_lectern_underside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/oak_lectern_base.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/oak_lectern_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/oak_lectern_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/oak_lectern_underside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/spruce_lectern_base.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/spruce_lectern_side.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/spruce_lectern_top.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/spruce_lectern_underside.png create mode 100644 src/main/resources/assets/ebwizardry/textures/entity/lectern_book.png create mode 100644 src/main/resources/assets/ebwizardry/textures/gui/lectern.png create mode 100644 src/main/resources/assets/ebwizardry/textures/gui/spell_sort_buttons.png create mode 100644 src/main/resources/assets/ebwizardry/textures/particle/block_highlight.png diff --git a/src/main/java/electroblob/wizardry/WizardryGuiHandler.java b/src/main/java/electroblob/wizardry/WizardryGuiHandler.java index 69aa420f..01d8d68a 100644 --- a/src/main/java/electroblob/wizardry/WizardryGuiHandler.java +++ b/src/main/java/electroblob/wizardry/WizardryGuiHandler.java @@ -7,6 +7,7 @@ import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemWizardHandbook; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import electroblob.wizardry.tileentity.TileEntityBookshelf; +import electroblob.wizardry.tileentity.TileEntityLectern; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; @@ -23,6 +24,7 @@ public class WizardryGuiHandler implements IGuiHandler { public static final int WIZARD_HANDBOOK = nextGuiId++; public static final int PORTABLE_CRAFTING = nextGuiId++; public static final int BOOKSHELF = nextGuiId++; + public static final int LECTERN = nextGuiId++; @Override public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z){ @@ -87,6 +89,14 @@ public class WizardryGuiHandler implements IGuiHandler { (TileEntityBookshelf)tileEntity); } + }else if(id == LECTERN){ + + TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); + + if(tileEntity instanceof TileEntityLectern){ + return new electroblob.wizardry.client.gui.GuiLectern((TileEntityLectern)tileEntity); + } + } return null; diff --git a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java index 9fc4c90a..ee4de190 100644 --- a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java +++ b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java @@ -13,6 +13,7 @@ import net.minecraft.block.state.BlockStateContainer; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; import net.minecraft.inventory.InventoryHelper; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; @@ -22,8 +23,11 @@ import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.Properties; +import org.apache.commons.lang3.ArrayUtils; import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvider { @@ -134,4 +138,37 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid } } + /** + * 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. + * @param world The world to search in + * @param centre The position to search around + * @param exclude Any tile entities that should be excluded from the returned list + * @return A list of nearby {@link IInventory} objects that count as valid bookshelves + */ + public static List findNearbyBookshelves(World world, BlockPos centre, TileEntity... exclude){ + + List bookshelves = new ArrayList<>(); + + int searchRadius = 4; // TODO: Config option for this + + for(int x = -searchRadius; x <= searchRadius; x++){ + for(int y = -searchRadius; y <= searchRadius; y++){ + for(int z = -searchRadius; z <= searchRadius; z++){ + + BlockPos pos = centre.add(x, y, z); + // TODO: Config option for allowed containers + if(world.getBlockState(pos).getBlock() instanceof BlockBookshelf){ + TileEntity te = world.getTileEntity(pos); + if(te instanceof IInventory && !ArrayUtils.contains(exclude, te)) bookshelves.add((IInventory)te); + } + + } + } + } + + return bookshelves; + + } + } diff --git a/src/main/java/electroblob/wizardry/block/BlockLectern.java b/src/main/java/electroblob/wizardry/block/BlockLectern.java new file mode 100644 index 00000000..922c4118 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockLectern.java @@ -0,0 +1,122 @@ +package electroblob.wizardry.block; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryGuiHandler; +import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.tileentity.TileEntityLectern; +import electroblob.wizardry.util.ParticleBuilder; +import electroblob.wizardry.util.ParticleBuilder.Type; +import net.minecraft.block.BlockHorizontal; +import net.minecraft.block.ITileEntityProvider; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.BlockFaceShape; +import net.minecraft.block.state.BlockStateContainer; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nullable; +import java.util.Random; + +public class BlockLectern extends BlockHorizontal implements ITileEntityProvider { + + public BlockLectern(){ + super(Material.WOOD); + this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH)); + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + + @Override + protected BlockStateContainer createBlockState(){ + return new BlockStateContainer.Builder(this).add(FACING).build(); + } + + @Override + public IBlockState getStateFromMeta(int meta){ + EnumFacing enumfacing = EnumFacing.byIndex(meta); + if(enumfacing.getAxis() == EnumFacing.Axis.Y) enumfacing = EnumFacing.NORTH; + return this.getDefaultState().withProperty(FACING, enumfacing); + } + + @Override + public int getMetaFromState(IBlockState state){ + return state.getValue(FACING).getIndex(); + } + + @Override + @SideOnly(Side.CLIENT) + public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random rand){ + + EntityPlayer entityplayer = world.getClosestPlayer(pos.getX() + 0.5, pos.getY() + 0.5, + pos.getZ() + 0.5, TileEntityLectern.BOOK_OPEN_DISTANCE, false); + + if(entityplayer != null){ + ParticleBuilder.create(Type.DUST).pos(pos.getX() + rand.nextFloat(), pos.getY() + 1, pos.getZ() + rand.nextFloat()) + .vel(0, 0.03, 0).clr(1, 1, 0.65f).fade(0.7f, 0, 1).shaded(false).spawn(world); + } + } + + @Override + public boolean isOpaqueCube(IBlockState state){ + return false; + } + + @Override + public boolean isFullCube(IBlockState state){ + return false; + } + + @Override + public boolean isFullBlock(IBlockState state){ + return false; + } + + @Override + public boolean isNormalCube(IBlockState state){ + return false; + } + + @Override + public boolean canPlaceTorchOnTop(IBlockState state, IBlockAccess world, BlockPos pos){ + return false; + } + + @Override + public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){ + return face == EnumFacing.DOWN ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED; + } + + @Override + public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer){ + return this.getDefaultState().withProperty(FACING, placer.getHorizontalFacing().getOpposite()); + } + + @Nullable + @Override + public TileEntity createNewTileEntity(World world, int meta){ + return new TileEntityLectern(); + } + + @Override + public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, EnumHand hand, + EnumFacing side, float hitX, float hitY, float hitZ){ + + TileEntity tileEntity = world.getTileEntity(pos); + + if(tileEntity == null || player.isSneaking()){ + return false; + } + + player.openGui(Wizardry.instance, WizardryGuiHandler.LECTERN, world, pos.getX(), pos.getY(), pos.getZ()); + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index b81987be..64e746d7 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -32,10 +32,7 @@ import electroblob.wizardry.packet.*; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.spell.*; -import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; -import electroblob.wizardry.tileentity.TileEntityMagicLight; -import electroblob.wizardry.tileentity.TileEntityShrineCore; -import electroblob.wizardry.tileentity.TileEntityStatue; +import electroblob.wizardry.tileentity.*; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.WandHelper; @@ -310,6 +307,7 @@ public class ClientProxy extends CommonProxy { public void registerParticles(){ // I'll be a good programmer and use the API method rather than the one above. Lead by example, as they say... ParticleWizardry.registerParticle(Type.BEAM, ParticleBeam::new); + ParticleWizardry.registerParticle(Type.BLOCK_HIGHLIGHT, ParticleBlockHighlight::new); ParticleWizardry.registerParticle(Type.BUFF, ParticleBuff::new); ParticleWizardry.registerParticle(Type.DARK_MAGIC, ParticleDarkMagic::new); ParticleWizardry.registerParticle(Type.DUST, ParticleDust::new); @@ -762,6 +760,7 @@ public class ClientProxy extends CommonProxy { ClientRegistry.bindTileEntitySpecialRenderer(TileEntityArcaneWorkbench.class, new RenderArcaneWorkbench()); ClientRegistry.bindTileEntitySpecialRenderer(TileEntityStatue.class, renderStatue = new RenderStatue()); ClientRegistry.bindTileEntitySpecialRenderer(TileEntityMagicLight.class, new RenderMagicLight()); + ClientRegistry.bindTileEntitySpecialRenderer(TileEntityLectern.class, new RenderLectern()); } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index 28901598..245390eb 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -17,6 +17,7 @@ import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.spell.Spell; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import electroblob.wizardry.util.ISpellSortable; import electroblob.wizardry.util.WandHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; @@ -115,9 +116,9 @@ public class GuiArcaneWorkbench extends GuiContainer { this.buttonList.clear(); this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width / 2 + 64, this.height / 2 + 3)); - this.buttonList.add(new GuiButtonSort(1, this.guiLeft - 44, this.guiTop + 8, ContainerArcaneWorkbench.SortType.TIER)); - this.buttonList.add(new GuiButtonSort(2, this.guiLeft - 31, this.guiTop + 8, ContainerArcaneWorkbench.SortType.ELEMENT)); - this.buttonList.add(new GuiButtonSort(3, this.guiLeft - 18, this.guiTop + 8, ContainerArcaneWorkbench.SortType.ALPHABETICAL)); + this.buttonList.add(new GuiButtonSpellSort(1, this.guiLeft - 44, this.guiTop + 8, ISpellSortable.SortType.TIER, arcaneWorkbenchContainer, this)); + this.buttonList.add(new GuiButtonSpellSort(2, this.guiLeft - 31, this.guiTop + 8, ISpellSortable.SortType.ELEMENT, arcaneWorkbenchContainer, this)); + this.buttonList.add(new GuiButtonSpellSort(3, this.guiLeft - 18, this.guiTop + 8, ISpellSortable.SortType.ALPHABETICAL, arcaneWorkbenchContainer, this)); this.searchField = new GuiTextField(0, this.fontRenderer, this.guiLeft - 113, this.guiTop + 22, 104, this.fontRenderer.FONT_HEIGHT); this.searchField.setMaxStringLength(50); @@ -294,7 +295,7 @@ public class GuiArcaneWorkbench extends GuiContainer { // Scroll bar DrawingUtils.drawTexturedRect(guiLeft - BOOKSHELF_UI_WIDTH + SCROLL_BAR_LEFT, guiTop + SCROLL_BAR_TOP + (int)(scroll * (SCROLL_BAR_HEIGHT - SCROLL_HANDLE_HEIGHT) + 0.5f), - getMaxScrollRows() > 0 ? 30 : 30 + SCROLL_BAR_WIDTH, 476, + getMaxScrollRows() > 0 ? 0 : SCROLL_BAR_WIDTH, 476, SCROLL_BAR_WIDTH, SCROLL_HANDLE_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); // Tooltip only drawn if there is a wand @@ -507,7 +508,7 @@ public class GuiArcaneWorkbench extends GuiContainer { } } - this.buttonList.forEach(b -> b.drawButtonForegroundLayer(mouseX, mouseY)); + this.buttonList.forEach(b -> b.drawButtonForegroundLayer(mouseX - guiLeft, mouseY - guiTop)); } // Controls @@ -528,7 +529,7 @@ public class GuiArcaneWorkbench extends GuiContainer { animationTimer = 20; } - if(button instanceof GuiButtonSort) this.arcaneWorkbenchContainer.setSortType(((GuiButtonSort)button).sortType); + if(button instanceof GuiButtonSpellSort) this.arcaneWorkbenchContainer.setSortType(((GuiButtonSpellSort)button).sortType); } } @@ -587,45 +588,6 @@ public class GuiArcaneWorkbench extends GuiContainer { } } - // Nested classes - - private class GuiButtonSort extends GuiButton { - - private final ContainerArcaneWorkbench.SortType sortType; - - public GuiButtonSort(int id, int x, int y, ContainerArcaneWorkbench.SortType sortType){ - super(id, x, y, 10, 10, I18n.format("container." + Wizardry.MODID + ":arcane_workbench.sort_" + sortType.name)); - this.sortType = sortType; - } - - @Override - public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){ - - if(this.visible){ - - // Whether the button is highlighted - this.hovered = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height; - - int k = 0; - int l = 476 + this.sortType.ordinal() * this.height; - - if(sortType == GuiArcaneWorkbench.this.arcaneWorkbenchContainer.getSortType()){ - k += this.width; - if(GuiArcaneWorkbench.this.arcaneWorkbenchContainer.isSortDescending()) k += this.width; - } - - DrawingUtils.drawTexturedRect(this.x, this.y, k, l, this.width, this.height, TEXTURE_WIDTH, TEXTURE_HEIGHT); - - } - } - - @Override - public void drawButtonForegroundLayer(int mouseX, int mouseY){ - if(hovered) drawHoveringText(this.displayString, mouseX - guiLeft, mouseY - guiTop); - } - - } - private static class GuiButtonApply extends GuiButton { public GuiButtonApply(int id, int x, int y){ diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiButtonSpellSort.java b/src/main/java/electroblob/wizardry/client/gui/GuiButtonSpellSort.java new file mode 100644 index 00000000..8e5c8439 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/gui/GuiButtonSpellSort.java @@ -0,0 +1,57 @@ +package electroblob.wizardry.client.gui; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.DrawingUtils; +import electroblob.wizardry.util.ISpellSortable; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.ResourceLocation; + +public class GuiButtonSpellSort extends GuiButton { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_sort_buttons.png"); + private static final int TEXTURE_WIDTH = 32; + private static final int TEXTURE_HEIGHT = 32; + + public final ISpellSortable.SortType sortType; + + private final ISpellSortable sortable; + private final GuiScreen parent; + + public GuiButtonSpellSort(int id, int x, int y, ISpellSortable.SortType sortType, ISpellSortable sortable, GuiScreen parent){ + super(id, x, y, 10, 10, I18n.format("container." + Wizardry.MODID + ":arcane_workbench.sort_" + sortType.name)); + this.sortType = sortType; + this.sortable = sortable; + this.parent = parent; + } + + @Override + public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){ + + if(this.visible){ + + // Whether the button is highlighted + this.hovered = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height; + + int k = 0; + int l = this.sortType.ordinal() * this.height; + + if(sortType == sortable.getSortType()){ + k += this.width; + if(sortable.isSortDescending()) k += this.width; + } + + parent.mc.getTextureManager().bindTexture(TEXTURE); + DrawingUtils.drawTexturedRect(this.x, this.y, k, l, this.width, this.height, TEXTURE_WIDTH, TEXTURE_HEIGHT); + + } + } + + @Override + public void drawButtonForegroundLayer(int mouseX, int mouseY){ + if(hovered) parent.drawHoveringText(this.displayString, mouseX, mouseY); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/gui/handbook/GuiButtonTurnPage.java b/src/main/java/electroblob/wizardry/client/gui/GuiButtonTurnPage.java similarity index 69% rename from src/main/java/electroblob/wizardry/client/gui/handbook/GuiButtonTurnPage.java rename to src/main/java/electroblob/wizardry/client/gui/GuiButtonTurnPage.java index 662e71a1..ef45cae1 100644 --- a/src/main/java/electroblob/wizardry/client/gui/handbook/GuiButtonTurnPage.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiButtonTurnPage.java @@ -1,6 +1,5 @@ -package electroblob.wizardry.client.gui.handbook; +package electroblob.wizardry.client.gui; -import electroblob.wizardry.Wizardry; import electroblob.wizardry.client.DrawingUtils; import electroblob.wizardry.registry.WizardrySounds; import net.minecraft.client.Minecraft; @@ -11,12 +10,12 @@ import net.minecraft.client.renderer.GlStateManager; import net.minecraft.util.ResourceLocation; //@SideOnly(Side.CLIENT) -class GuiButtonTurnPage extends GuiButton { +public class GuiButtonTurnPage extends GuiButton { - static final int WIDTH = 20; - static final int HEIGHT = 12; + public static final int WIDTH = 20; + public static final int HEIGHT = 12; - enum Type { + public enum Type { NEXT_PAGE(0, 196), PREVIOUS_PAGE(0, 208), @@ -34,11 +33,15 @@ class GuiButtonTurnPage extends GuiButton { public final Type type; - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png"); + private final ResourceLocation texture; + private final int textureWidth, textureHeight; - public GuiButtonTurnPage(int id, int x, int y, Type type){ + public GuiButtonTurnPage(int id, int x, int y, Type type, ResourceLocation texture, int textureWidth, int textureHeight){ super(id, x, y, WIDTH, HEIGHT, ""); this.type = type; + this.texture = texture; + this.textureWidth = textureWidth; + this.textureHeight = textureHeight; } @Override @@ -50,12 +53,13 @@ class GuiButtonTurnPage extends GuiButton { public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){ if(this.visible){ - + boolean flag = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height; GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); minecraft.getTextureManager().bindTexture(texture); - DrawingUtils.drawTexturedRect(this.x, this.y, flag ? type.u + width : type.u, type.v, width, height, 512, 256); + DrawingUtils.drawTexturedRect(this.x, this.y, flag ? type.u + width : type.u, type.v, width, height, textureWidth, textureHeight); } } + } diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java b/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java new file mode 100644 index 00000000..6c69b6f2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java @@ -0,0 +1,467 @@ +package electroblob.wizardry.client.gui; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.block.BlockBookshelf; +import electroblob.wizardry.client.DrawingUtils; +import electroblob.wizardry.client.gui.GuiButtonTurnPage.Type; +import electroblob.wizardry.data.SpellGlyphData; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.packet.PacketLectern; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.tileentity.TileEntityLectern; +import electroblob.wizardry.util.ISpellSortable; +import electroblob.wizardry.util.ParticleBuilder; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.audio.SoundHandler; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiTextField; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.resources.I18n; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.network.play.client.CPacketCloseWindow; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class GuiLectern extends GuiSpellInfo implements ISpellSortable { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/lectern.png"); + /** The distance of the page buttons from the bottom outside corners of the GUI. */ + private static final int PAGE_BUTTON_INSET_X = 22, PAGE_BUTTON_INSET_Y = 13; + /** The distance between adjacent page turn buttons. */ + private static final int PAGE_BUTTON_SPACING = 20; + /** The distance of the sort buttons from the top left corner of the GUI. */ + private static final int SORT_BUTTON_INSET_X = 96, SORT_BUTTON_INSET_Y = 20; + /** The distance between adjacent sort buttons. */ + private static final int SORT_BUTTON_SPACING = 13; + /** The distance of the spell buttons from the top outside corners of the GUI. */ + private static final int SPELL_BUTTON_INSET_X = 23, SPELL_BUTTON_INSET_Y = 44; + /** The distance between adjacent spell buttons (in both x and y). */ + private static final int SPELL_BUTTON_SPACING = 38; + + private static final int SPELL_ROWS = 3, SPELL_COLUMNS = 3; + public static final int SPELL_BUTTON_COUNT = SPELL_ROWS * SPELL_COLUMNS * 2; // x2 because there are 2 pages + + private final TileEntityLectern lectern; + + private GuiButton nextPageButton; + private GuiButton prevPageButton; + private GuiButton lastPageButton; + private GuiButton firstPageButton; + private GuiButton indexButton; + private GuiButton locateButton; + + private GuiButton[] sortButtons = new GuiButton[3]; + private GuiButtonSpell[] spellButtons = new GuiButtonSpell[SPELL_BUTTON_COUNT]; + + /** The spell currently being viewed, or null if the index is being viewed. */ + private Spell currentSpell; + /** The available spells in nearby bookshelves; should not contain duplicates. */ + private List availableSpells = new ArrayList<>(); + /** The spells matching the current search criteria, sorted according to the current sort settings. Will be a subset + * of {@link GuiLectern#availableSpells}. */ + private List matchingSpells; + + private ISpellSortable.SortType sortType = ISpellSortable.SortType.TIER; + private boolean sortDescending = false; + + private GuiTextField searchField; + private boolean searchNeedsClearing; + + private int currentPage = 0; + + public GuiLectern(TileEntityLectern lectern){ + super(288, 180); + this.lectern = lectern; + this.currentSpell = lectern.currentSpell; + this.setTextureSize(512, 512); + } + + @Override + public Spell getSpell(){ + return currentSpell; + } + + @Override + public ResourceLocation getTexture(){ + return TEXTURE; + } + + @Override + public SortType getSortType(){ + return sortType; + } + + @Override + public boolean isSortDescending(){ + return sortDescending; + } + + private int getPageCount(){ + return MathHelper.ceil((float)matchingSpells.size() / SPELL_BUTTON_COUNT); + } + + private Spell getSpellForButton(GuiButtonSpell button){ + return matchingSpells.get(currentPage * SPELL_BUTTON_COUNT + button.index); + } + + @Override + public void initGui(){ + + super.initGui(); + + updateAvailableSpells(); + + final int left = this.width / 2 - this.xSize / 2; + final int top = this.height / 2 - this.ySize / 2; + + int buttonID = 0; + + // Page buttons + this.buttonList.add(nextPageButton = new GuiButtonTurnPage(buttonID++, left + xSize - PAGE_BUTTON_INSET_X - GuiButtonTurnPage.WIDTH, + top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE, TEXTURE, textureWidth, textureHeight)); + + this.buttonList.add(prevPageButton = new GuiButtonTurnPage(buttonID++, left + PAGE_BUTTON_INSET_X, + top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE, TEXTURE, textureWidth, textureHeight)); + + this.buttonList.add(lastPageButton = new GuiButtonTurnPage(buttonID++, left + xSize - PAGE_BUTTON_INSET_X - GuiButtonTurnPage.WIDTH - PAGE_BUTTON_SPACING, + top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION, TEXTURE, textureWidth, textureHeight)); + + this.buttonList.add(firstPageButton = new GuiButtonTurnPage(buttonID++, left + PAGE_BUTTON_INSET_X + PAGE_BUTTON_SPACING, + top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION, TEXTURE, textureWidth, textureHeight)); + + this.buttonList.add(indexButton = new GuiButtonTurnPage(buttonID++, left + xSize/2 - 23, + top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS, TEXTURE, textureWidth, textureHeight)); + + this.buttonList.add(locateButton = new GuiButtonLocateBook(buttonID++, left + xSize/2 - 34, + top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT)); + + // Sort buttons + for(SortType sortType : SortType.values()){ + this.buttonList.add(sortButtons[sortType.ordinal()] = new GuiButtonSpellSort(buttonID++, + left + SORT_BUTTON_INSET_X + SORT_BUTTON_SPACING * sortType.ordinal(), + top + SORT_BUTTON_INSET_Y, sortType, this, this)); + } + + // Spell buttons + for(int i = 0; i < SPELL_BUTTON_COUNT; i++){ + + int row = i % SPELL_COLUMNS; + int column = (i / SPELL_COLUMNS) % SPELL_ROWS; + + int x = i < SPELL_BUTTON_COUNT/2 ? SPELL_BUTTON_INSET_X + row * SPELL_BUTTON_SPACING + : xSize - SPELL_BUTTON_INSET_X - GuiButtonSpell.WIDTH - (2-row) * SPELL_BUTTON_SPACING; + int y = SPELL_BUTTON_INSET_Y + column * SPELL_BUTTON_SPACING; + + this.buttonList.add(spellButtons[i] = new GuiButtonSpell(buttonID++, left + x, top + y, i)); + } + + this.searchField = new GuiTextField(0, this.fontRenderer, left + 157, top + 21, 106, this.fontRenderer.FONT_HEIGHT); + this.searchField.setMaxStringLength(50); + this.searchField.setEnableBackgroundDrawing(false); + this.searchField.setVisible(true); + this.searchField.setTextColor(16777215); + this.searchField.setCanLoseFocus(false); + this.searchField.setFocused(true); + + updateMatchingSpells(); + updateButtonVisiblity(); + + } + + @Override + public void onGuiClosed(){ + WizardryPacketHandler.net.sendToServer(new PacketLectern.Message(lectern.getPos(), currentSpell)); + super.onGuiClosed(); + } + + @Override + protected void drawBackgroundLayer(int left, int top, int mouseX, int mouseY){ + if(currentSpell == Spells.none){ + drawIndexPage(left, top); + }else{ + super.drawBackgroundLayer(left, top, mouseX, mouseY); + } + } + + @Override + protected void drawForegroundLayer(int left, int top, int mouseX, int mouseY){ + + if(currentSpell == Spells.none){ + this.fontRenderer.drawString(I18n.format("gui." + Wizardry.MODID + ":lectern.title"), + left + 20, top + SORT_BUTTON_INSET_Y, 0); + }else{ + super.drawForegroundLayer(left, top, mouseX, mouseY); + } + + this.buttonList.forEach(b -> b.drawButtonForegroundLayer(mouseX, mouseY)); + } + + private void drawIndexPage(int left, int top){ + + for(int i = 0; i < SPELL_BUTTON_COUNT; i++){ + + int index = currentPage * SPELL_BUTTON_COUNT + i; + Spell spell = index < matchingSpells.size() ? matchingSpells.get(index) : Spells.none; + boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(spell, null); + + Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon()); + + int row = i % SPELL_COLUMNS; + int column = (i / SPELL_COLUMNS) % SPELL_ROWS; + + int x = i < SPELL_BUTTON_COUNT/2 ? SPELL_BUTTON_INSET_X + row * SPELL_BUTTON_SPACING + : xSize - SPELL_BUTTON_INSET_X - GuiButtonSpell.WIDTH - (2-row) * SPELL_BUTTON_SPACING; + int y = SPELL_BUTTON_INSET_Y + column * SPELL_BUTTON_SPACING; + + DrawingUtils.drawTexturedRect(left + x + 1, top + y + 1, 0, 0, 32, 32, 32, 32); + } + + mc.renderEngine.bindTexture(getTexture()); + DrawingUtils.drawTexturedRect(left, top, 0, 256, xSize, ySize, textureWidth, textureHeight); + + this.searchField.drawTextBox(); + + GlStateManager.color(1, 1, 1, 1); + mc.renderEngine.bindTexture(getTexture()); + + } + + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { + super.mouseClicked(mouseX, mouseY, mouseButton); + searchNeedsClearing = true; + } + + @Override + protected void actionPerformed(GuiButton button){ + + int lastPage = getPageCount() - 1; + + if(button == indexButton){ + currentSpell = Spells.none; + currentPage = 0; + + }else if(button == locateButton){ + // Close the GUI + this.mc.player.connection.sendPacket(new CPacketCloseWindow(this.mc.player.openContainer.windowId)); + this.mc.displayGuiScreen(null); + + // Find the location(s) of the current spell's book(s) and highlight them + for(IInventory bookshelf : BlockBookshelf.findNearbyBookshelves(lectern.getWorld(), lectern.getPos())){ + + for(int i = 0; i < bookshelf.getSizeInventory(); i++){ + + ItemStack stack = bookshelf.getStackInSlot(i); + + if(stack.getItem() instanceof ItemSpellBook){ + + Spell spell = Spell.byMetadata(stack.getMetadata()); + + if(spell == this.currentSpell){ + BlockPos pos = (((TileEntity)bookshelf).getPos()); + for(EnumFacing side : EnumFacing.VALUES){ + ParticleBuilder.create(ParticleBuilder.Type.BLOCK_HIGHLIGHT).pos( + WizardryUtilities.getFaceCentre(pos, side) + .add(new Vec3d(side.getDirectionVec()) + .scale(WizardryUtilities.ANTI_Z_FIGHTING_OFFSET))) + .face(side).clr(0.9f, 0.5f, 0.8f).fade(0.7f, 0, 1).spawn(mc.world); + } + break; // This bookshelf has the spell, skip to the next bookshelf + } + } + } + } + + }else if(button == nextPageButton){ + if(currentPage < lastPage) currentPage++; + + }else if(button == prevPageButton){ + if(currentPage > 0) currentPage--; + + }else if(button == lastPageButton){ + currentPage = lastPage; + + }else if(button == firstPageButton){ + currentPage = 0; + + }else if(button instanceof GuiButtonSpell){ + currentSpell = getSpellForButton((GuiButtonSpell)button); + + }else if(button instanceof GuiButtonSpellSort){ + + SortType sortType = (((GuiButtonSpellSort)button).sortType); + + if(this.sortType == sortType){ + this.sortDescending = !this.sortDescending; + }else{ + this.sortType = sortType; + this.sortDescending = false; + } + + updateMatchingSpells(); + } + + updateButtonVisiblity(); + + } + + @Override + protected void keyTyped(char typedChar, int keyCode) throws IOException { + + if(this.searchNeedsClearing){ + this.searchNeedsClearing = false; + this.searchField.setText(""); + this.currentPage = 0; + } + + if(this.searchField.textboxKeyTyped(typedChar, keyCode)){ + this.currentPage = 0; + updateMatchingSpells(); + updateButtonVisiblity(); + }else{ + super.keyTyped(typedChar, keyCode); + } + } + + private void updateButtonVisiblity(){ + + if(currentSpell == Spells.none){ + + this.searchField.setVisible(true); + + int lastPage = getPageCount() - 1; + + prevPageButton.visible = currentPage > 0; + firstPageButton.visible = currentPage > 0; + nextPageButton.visible = currentPage < lastPage; + lastPageButton.visible = currentPage < lastPage; + + indexButton.visible = false; + locateButton.visible = false; + + for(GuiButton button : sortButtons) button.visible = true; + + for(GuiButtonSpell button : spellButtons){ + button.visible = currentPage * SPELL_BUTTON_COUNT + button.index < matchingSpells.size(); + } + + }else{ + this.searchField.setVisible(false); + this.buttonList.forEach(b -> b.visible = false); // Hide all buttons... + indexButton.visible = true; // ... except the index button and locate button + locateButton.visible = true; + } + } + + private void updateMatchingSpells(){ + matchingSpells = availableSpells.stream() + .filter(s -> s.matches(searchField.getText().toLowerCase(Locale.ROOT))) + .sorted(sortDescending ? sortType.comparator.reversed() : sortType.comparator) + .collect(Collectors.toList()); + } + + // TODO: Call this when a bookshelf is added or removed + // TODO: Config option to always display all spells when in creative + private void updateAvailableSpells(){ + + availableSpells.clear(); + + // No need to exclude the lectern TE because it's not an IInventory! + for(IInventory bookshelf : BlockBookshelf.findNearbyBookshelves(lectern.getWorld(), lectern.getPos())){ + + for(int i=0; i= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height; + GlStateManager.color(1, 1, 1, 1); + minecraft.getTextureManager().bindTexture(TEXTURE); + + DrawingUtils.drawTexturedRect(this.x, this.y, flag ? width : 0, 184, width, height, textureWidth, textureHeight); + } + } + + } + + private class GuiButtonSpell extends GuiButtonInvisible { + + private static final int WIDTH = 34, HEIGHT = 34; + + private final int index; + + public GuiButtonSpell(int id, int x, int y, int index){ + super(id, x, y, WIDTH, HEIGHT); + this.index = index; + } + + @Override + public void playPressSound(SoundHandler soundHandler){ + soundHandler.playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_PAGE_TURN, 1)); + } + + @Override + public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){ + if(visible){ + super.drawButton(minecraft, mouseX, mouseY, partialTicks); + if(hovered){ + mc.renderEngine.bindTexture(getTexture()); + DrawingUtils.drawTexturedRect(x, y, 40, 180, width, height, textureWidth, textureHeight); + } + } + } + + @Override + public void drawButtonForegroundLayer(int mouseX, int mouseY){ + if(visible && hovered){ + Spell spell = getSpellForButton(this); + if(Wizardry.proxy.shouldDisplayDiscovered(spell, null)){ + drawHoveringText(Collections.singletonList(spell.getDisplayName()), mouseX, mouseY, fontRenderer); + }else{ + drawHoveringText(Collections.singletonList(SpellGlyphData.getGlyphName(spell, mc.world)), + mouseX, mouseY, mc.standardGalacticFontRenderer); + } + } + } + + } + +} diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiSpellBook.java b/src/main/java/electroblob/wizardry/client/gui/GuiSpellBook.java index 3e26c194..d46a9886 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiSpellBook.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiSpellBook.java @@ -1,113 +1,35 @@ package electroblob.wizardry.client.gui; -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.client.DrawingUtils; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.data.SpellGlyphData; -import electroblob.wizardry.data.WizardData; import electroblob.wizardry.item.ItemSpellBook; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.spell.Spell; -import net.minecraft.client.Minecraft; -import net.minecraft.client.audio.PositionedSoundRecord; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.resources.I18n; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; -import org.lwjgl.input.Keyboard; +import net.minecraft.util.ResourceLocation; -public class GuiSpellBook extends GuiScreen { +public class GuiSpellBook extends GuiSpellInfo { - private int xSize, ySize; - private ItemSpellBook book; - private Spell spell; + private final ItemSpellBook book; + private final Spell spell; public GuiSpellBook(ItemStack stack){ - super(); - xSize = 288; - ySize = 180; - if(!(stack.getItem() instanceof ItemSpellBook)) throw new ClassCastException("Cannot create spell book GUI for item that does not extend ItemSpellBook!"); + + super(288, 180); + + if(!(stack.getItem() instanceof ItemSpellBook)){ + throw new ClassCastException("Cannot create spell book GUI for item that does not extend ItemSpellBook!"); + } + this.book = (ItemSpellBook)stack.getItem(); - this.spell = Spell.byMetadata(stack.getItemDamage()); + this.spell = (Spell.byMetadata(stack.getItemDamage())); } @Override - public void drawScreen(int par1, int par2, float par3){ - - int xPos = this.width / 2 - xSize / 2; - int yPos = this.height / 2 - this.ySize / 2; - - EntityPlayer player = Minecraft.getMinecraft().player; - - boolean discovered = true; - if(Wizardry.settings.discoveryMode && !player.isCreative() && WizardData.get(player) != null - && !WizardData.get(player).hasSpellBeenDiscovered(spell)){ - discovered = false; - } - - GlStateManager.color(1, 1, 1, 1); // Just in case - - // Draws spell illustration on opposite page, underneath the book so it shows through the hole. - Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon()); - DrawingUtils.drawTexturedRect(xPos + 146, yPos + 20, 0, 0, 128, 128, 128, 128); - - Minecraft.getMinecraft().renderEngine.bindTexture(book.getGuiTexture(spell)); - DrawingUtils.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256); - - super.drawScreen(par1, par2, par3); - - if(discovered){ - this.fontRenderer.drawString(spell.getDisplayName(), xPos + 17, yPos + 15, 0); - this.fontRenderer.drawString(spell.getType().getDisplayName(), xPos + 17, yPos + 26, 0x777777); - }else{ - this.mc.standardGalacticFontRenderer.drawString(SpellGlyphData.getGlyphName(spell, player.world), xPos + 17, - yPos + 15, 0); - this.mc.standardGalacticFontRenderer.drawString(spell.getType().getDisplayName(), xPos + 17, yPos + 26, - 0x777777); - } - - // Novice is usually white but this doesn't show up - String tier = I18n.format("gui.ebwizardry:spell_book.tier", spell.getTier() == Tier.NOVICE ? - "\u00A77" + spell.getTier().getDisplayName() : spell.getTier().getDisplayNameWithFormatting()); - this.fontRenderer.drawString(tier, xPos + 17, yPos + 45, 0); - - String element = I18n.format("gui.ebwizardry:spell_book.element", spell.getElement().getFormattingCode() + spell.getElement().getDisplayName()); - if(!discovered) element = I18n.format("gui.ebwizardry:spell_book.element_undiscovered"); - this.fontRenderer.drawString(element, xPos + 17, yPos + 57, 0); - - String manaCost = I18n.format("gui.ebwizardry:spell_book.mana_cost", spell.getCost()); - if(spell.isContinuous) manaCost = I18n.format("gui.ebwizardry:spell_book.mana_cost_continuous", spell.getCost()); - if(!discovered) manaCost = I18n.format("gui.ebwizardry:spell_book.mana_cost_undiscovered"); - this.fontRenderer.drawString(manaCost, xPos + 17, yPos + 69, 0); - - if(discovered){ - this.fontRenderer.drawSplitString(spell.getDescription(), xPos + 17, yPos + 83, 118, 0); - }else{ - this.mc.standardGalacticFontRenderer.drawSplitString( - SpellGlyphData.getGlyphDescription(spell, player.world), xPos + 17, yPos + 83, 118, 0); - } + public Spell getSpell(){ + return spell; } @Override - public void initGui(){ - super.initGui(); - Keyboard.enableRepeatEvents(true); - this.buttonList.clear(); - - this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1)); - } - - @Override - public void onGuiClosed(){ - super.onGuiClosed(); - Keyboard.enableRepeatEvents(false); - } - - @Override - public boolean doesGuiPauseGame(){ - return Wizardry.settings.booksPauseGame; + public ResourceLocation getTexture(){ + return book.getGuiTexture(spell); } } diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiSpellInfo.java b/src/main/java/electroblob/wizardry/client/gui/GuiSpellInfo.java new file mode 100644 index 00000000..0bb77c4f --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/gui/GuiSpellInfo.java @@ -0,0 +1,137 @@ +package electroblob.wizardry.client.gui; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.DrawingUtils; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.data.SpellGlyphData; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.spell.Spell; +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.input.Keyboard; + +/** Abstract base class for both {@link GuiSpellBook} and {@link GuiLectern}. Centralises code common to both + * those classes. */ +public abstract class GuiSpellInfo extends GuiScreen { + + protected static final String TRANSLATION_KEY_PREFIX = "gui." + Wizardry.MODID + ":spell_book"; + + protected final int xSize, ySize; + protected int textureWidth = 512; + protected int textureHeight = 256; + + public GuiSpellInfo(int xSize, int ySize){ + super(); + this.xSize = xSize; + this.ySize = ySize; + } + + protected void setTextureSize(int width, int height){ + this.textureWidth = width; + this.textureHeight = height; + } + + /** Returns the spell to be displayed by this GUI. */ + public abstract Spell getSpell(); + + /** Returns the main texture for the background of this GUI. */ + public abstract ResourceLocation getTexture(); + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks){ + int left = this.width/2 - xSize/2; + int top = this.height/2 - this.ySize/2; + this.drawBackgroundLayer(left, top, mouseX, mouseY); + super.drawScreen(mouseX, mouseY, partialTicks); // Just draws the buttons + this.drawForegroundLayer(left, top, mouseX, mouseY); + } + + /** + * Draws the background of the spell info GUI. This is called before buttons are drawn. + * @param left The x-coordinate of the left-hand edge of the GUI + * @param top The y-coordinate of the top edge of the GUI + * @param mouseX The current x position of the mouse pointer + * @param mouseY The current y position of the mouse pointer + */ + protected void drawBackgroundLayer(int left, int top, int mouseX, int mouseY){ + + boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(getSpell(), null); + + GlStateManager.color(1, 1, 1, 1); // Just in case + + // Draws spell illustration on opposite page, underneath the book so it shows through the hole. + Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? getSpell().getIcon() : Spells.none.getIcon()); + DrawingUtils.drawTexturedRect(left + 146, top + 20, 0, 0, 128, 128, 128, 128); + + Minecraft.getMinecraft().renderEngine.bindTexture(getTexture()); + DrawingUtils.drawTexturedRect(left, top, 0, 0, xSize, ySize, textureWidth, textureHeight); + } + + /** + * Draws the foreground of the spell info GUI. This is called after buttons are drawn. + * @param left The x-coordinate of the left-hand edge of the GUI + * @param top The y-coordinate of the top edge of the GUI + * @param mouseX The current x position of the mouse pointer + * @param mouseY The current y position of the mouse pointer + */ + protected void drawForegroundLayer(int left, int top, int mouseX, int mouseY){ + + boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(getSpell(), null); + + if(discovered){ + this.fontRenderer.drawString(getSpell().getDisplayName(), left + 17, top + 15, 0); + this.fontRenderer.drawString(getSpell().getType().getDisplayName(), left + 17, top + 26, 0x777777); + }else{ + this.mc.standardGalacticFontRenderer.drawString(SpellGlyphData.getGlyphName(getSpell(), mc.world), left + 17, + top + 15, 0); + this.mc.standardGalacticFontRenderer.drawString(getSpell().getType().getDisplayName(), left + 17, top + 26, + 0x777777); + } + + // Novice is usually white but this doesn't show up + String tier = I18n.format(TRANSLATION_KEY_PREFIX + ".tier", getSpell().getTier() == Tier.NOVICE ? + "\u00A77" + getSpell().getTier().getDisplayName() : getSpell().getTier().getDisplayNameWithFormatting()); + this.fontRenderer.drawString(tier, left + 17, top + 45, 0); + + String element = I18n.format(TRANSLATION_KEY_PREFIX + ".element", getSpell().getElement().getFormattingCode() + getSpell().getElement().getDisplayName()); + if(!discovered) element = I18n.format(TRANSLATION_KEY_PREFIX + ".element_undiscovered"); + this.fontRenderer.drawString(element, left + 17, top + 57, 0); + + String manaCost = I18n.format(TRANSLATION_KEY_PREFIX + ".mana_cost", getSpell().getCost()); + if(getSpell().isContinuous) manaCost = I18n.format(TRANSLATION_KEY_PREFIX + ".mana_cost_continuous", getSpell().getCost()); + if(!discovered) manaCost = I18n.format(TRANSLATION_KEY_PREFIX + ".mana_cost_undiscovered"); + this.fontRenderer.drawString(manaCost, left + 17, top + 69, 0); + + if(discovered){ + this.fontRenderer.drawSplitString(getSpell().getDescription(), left + 17, top + 83, 118, 0); + }else{ + this.mc.standardGalacticFontRenderer.drawSplitString( + SpellGlyphData.getGlyphDescription(getSpell(), mc.world), left + 17, top + 83, 118, 0); + } + } + + @Override + public void initGui(){ + super.initGui(); + Keyboard.enableRepeatEvents(true); + this.buttonList.clear(); + this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1)); + } + + @Override + public void onGuiClosed(){ + super.onGuiClosed(); + Keyboard.enableRepeatEvents(false); + } + + @Override + public boolean doesGuiPauseGame(){ + return Wizardry.settings.booksPauseGame; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java b/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java index 13fa335f..bf947a03 100644 --- a/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java +++ b/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java @@ -8,7 +8,8 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.client.ClientProxy; import electroblob.wizardry.client.DrawingUtils; import electroblob.wizardry.client.gui.GuiButtonInvisible; -import electroblob.wizardry.client.gui.handbook.GuiButtonTurnPage.Type; +import electroblob.wizardry.client.gui.GuiButtonTurnPage; +import electroblob.wizardry.client.gui.GuiButtonTurnPage.Type; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.constants.Element; import electroblob.wizardry.constants.Tier; @@ -348,19 +349,19 @@ public class GuiWizardHandbook extends GuiScreen { this.buttonList.clear(); this.buttonList.add(next = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH - BUTTON_INSET_X - GuiButtonTurnPage.WIDTH, - top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE)); + top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT)); this.buttonList.add(previous = new GuiButtonTurnPage(nextButtonId++, left + BUTTON_INSET_X, - top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE)); + top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT)); this.buttonList.add(nextSection = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH - BUTTON_INSET_X - GuiButtonTurnPage.WIDTH - BUTTON_SPACING, - top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION)); + top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT)); this.buttonList.add(previousSection = new GuiButtonTurnPage(nextButtonId++, left + BUTTON_INSET_X + BUTTON_SPACING, - top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION)); + top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT)); this.buttonList.add(menu = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH/2 - 28, - top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS)); + top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT)); this.buttonList.add(bookmark = new GuiButtonInvisible(nextButtonId++, left + 130, top + 172, 11, 19) { @Override diff --git a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java index 11ad3990..e03e22d5 100644 --- a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java +++ b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java @@ -84,6 +84,13 @@ public final class WizardryModels { registerItemModel(Item.getItemFromBlock(WizardryBlocks.acacia_bookshelf)); registerItemModel(Item.getItemFromBlock(WizardryBlocks.dark_oak_bookshelf)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.oak_lectern)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.spruce_lectern)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.birch_lectern)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.jungle_lectern)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.acacia_lectern)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.dark_oak_lectern)); + // Items registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal); diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleBlockHighlight.java b/src/main/java/electroblob/wizardry/client/particle/ParticleBlockHighlight.java new file mode 100644 index 00000000..8188fd45 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleBlockHighlight.java @@ -0,0 +1,58 @@ +package electroblob.wizardry.client.particle; + +import electroblob.wizardry.Wizardry; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.client.event.TextureStitchEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.Side; + +//@SideOnly(Side.CLIENT) +@Mod.EventBusSubscriber(Side.CLIENT) +public class ParticleBlockHighlight extends ParticleWizardry { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "particle/block_highlight"); + + public ParticleBlockHighlight(World world, double x, double y, double z){ + + super(world, x, y, z, TEXTURE); + + this.particleGravity = 0; + this.setMaxAge(160); + this.particleScale = 5; + this.shaded = false; + } + + @Override + public boolean shouldDisableDepth(){ + return true; + } + + @Override + public void onUpdate(){ + + super.onUpdate(); + + // Fading + if(this.particleAge > this.particleMaxAge/2){ + this.setAlphaF(1 - ((float)this.particleAge - this.particleMaxAge/2f) / (this.particleMaxAge/2f)); + } + + EnumFacing facing = EnumFacing.fromAngle(yaw); + if(pitch == 90) facing = EnumFacing.UP; + if(pitch == -90) facing = EnumFacing.DOWN; + + // Disappears if there is no block behind it (this is the same check used to spawn it) + if(!world.getBlockState(new BlockPos(posX, posY, posZ).offset(facing.getOpposite())).getMaterial().isSolid()){ + this.setExpired(); + } + } + + @SubscribeEvent + public static void onTextureStitchEvent(TextureStitchEvent.Pre event){ + event.getMap().registerSprite(TEXTURE); + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderLectern.java b/src/main/java/electroblob/wizardry/client/renderer/RenderLectern.java new file mode 100644 index 00000000..4872b09b --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderLectern.java @@ -0,0 +1,53 @@ +package electroblob.wizardry.client.renderer; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.tileentity.TileEntityLectern; +import net.minecraft.block.BlockHorizontal; +import net.minecraft.client.model.ModelBook; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.MathHelper; + +public class RenderLectern extends TileEntitySpecialRenderer { + + private static final ResourceLocation BOOK_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/lectern_book.png"); + private final ModelBook modelBook = new ModelBook(); + + @Override + public void render(TileEntityLectern te, double x, double y, double z, float partialTicks, int destroyStage, float alpha){ + + GlStateManager.pushMatrix(); + GlStateManager.translate((float)x + 0.5F, (float)y + 1, (float)z + 0.5F); + GlStateManager.rotate(90 - te.getWorld().getBlockState(te.getPos()).getValue(BlockHorizontal.FACING).getHorizontalAngle(), 0, 1, 0); + + float time = (float)te.ticksExisted + partialTicks; + + float spread = te.bookSpreadPrev + (te.bookSpread - te.bookSpreadPrev) * partialTicks; + + GlStateManager.translate(0, 0.12, 0); + if(spread > 0.3) GlStateManager.translate(0, MathHelper.sin(time * 0.1F) * 0.01F, 0); + + GlStateManager.rotate(112.5F, 0, 0, 1); + + GlStateManager.translate(0, 0.04 + (1 - spread) * 0.09, (1 - spread) * -0.1875); + + GlStateManager.rotate((1 - spread) * -90, 0, 1, 0); + + this.bindTexture(BOOK_TEXTURE); + + float f3 = te.pageFlipPrev + (te.pageFlip - te.pageFlipPrev) * partialTicks + 0.25F; + float f4 = te.pageFlipPrev + (te.pageFlip - te.pageFlipPrev) * partialTicks + 0.75F; + f3 = (f3 - (float)MathHelper.fastFloor(f3)) * 1.6F - 0.3F; + f4 = (f4 - (float)MathHelper.fastFloor(f4)) * 1.6F - 0.3F; + + f3 = MathHelper.clamp(f3, 0, 1); + f4 = MathHelper.clamp(f4, 0, 1); + + GlStateManager.enableCull(); + this.modelBook.render(null, time, f3, f4, spread, 0.0F, 0.0625F); + GlStateManager.popMatrix(); + + } + +} diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index a3dc3704..123fa1c0 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -9,6 +9,7 @@ import electroblob.wizardry.registry.WizardryAdvancementTriggers; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.spell.Spell; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import electroblob.wizardry.util.ISpellSortable; import electroblob.wizardry.util.WandHelper; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -18,9 +19,7 @@ import net.minecraft.inventory.IInventory; import net.minecraft.inventory.Slot; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraftforge.common.MinecraftForge; @@ -47,7 +46,7 @@ import java.util.stream.Collectors; * for consistency, just in case). The bookshelf slot delegates to a virtual slot on the client side (which is where * the search and sorting is done), and then the click is sent to the server. */ -public class ContainerArcaneWorkbench extends Container { +public class ContainerArcaneWorkbench extends Container implements ISpellSortable { /** The arcane workbench tile entity associated with this container. */ public TileEntityArcaneWorkbench tileentity; @@ -70,7 +69,7 @@ public class ContainerArcaneWorkbench extends Container { private List activeBookshelfSlots = new ArrayList<>(); private int scroll = 0; - private SortType sortType = SortType.TIER; + private ISpellSortable.SortType sortType = ISpellSortable.SortType.TIER; private boolean sortDescending = false; private String searchText = ""; @@ -369,7 +368,7 @@ public class ContainerArcaneWorkbench extends Container { } /** Sets the sorting type to the given type, or toggles the sort direction if it is already that type. */ - public void setSortType(SortType sortType){ + public void setSortType(ISpellSortable.SortType sortType){ if(this.sortType == sortType){ this.sortDescending = !this.sortDescending; @@ -381,12 +380,12 @@ public class ContainerArcaneWorkbench extends Container { updateActiveBookshelfSlots(); } - /** Returns the current sorting type. */ - public SortType getSortType(){ + @Override + public ISpellSortable.SortType getSortType(){ return sortType; } - /** Returns true if the current sorting is in descending order, false otherwise. */ + @Override public boolean isSortDescending(){ return sortDescending; } @@ -435,6 +434,7 @@ public class ContainerArcaneWorkbench extends Container { // N.B. If we drop the requirement of it working with any container it could potentially be a lot easier since // we then always have control over the bookshelf classes + // TODO: Call this when a bookshelf is added or removed /** Called on initialisation, and whenever a bookshelf is added or removed. */ private void refreshBookshelfSlots(){ @@ -442,7 +442,7 @@ public class ContainerArcaneWorkbench extends Container { // TESTME: May need to do this for inventoryItemStacks (probably not though, seems like MC handles it) bookshelfSlots.clear(); - for(IInventory bookshelf : findNearbyBookshelves()){ + for(IInventory bookshelf : BlockBookshelf.findNearbyBookshelves(tileentity.getWorld(), tileentity.getPos(), tileentity)){ for(int i=0; i findNearbyBookshelves(){ - - List bookshelves = new ArrayList<>(); - - int searchRadius = 4; // TODO: Config option for this - - for(int x = -searchRadius; x <= searchRadius; x++){ - for(int y = -searchRadius; y <= searchRadius; y++){ - for(int z = -searchRadius; z <= searchRadius; z++){ - - BlockPos pos = this.tileentity.getPos().add(x, y, z); - // TODO: Config option for allowed containers - if(this.tileentity.getWorld().getBlockState(pos).getBlock() instanceof BlockBookshelf){ - TileEntity te = this.tileentity.getWorld().getTileEntity(pos); - if(te instanceof IInventory && te != this.tileentity) bookshelves.add((IInventory)te); - } - - } - } - } - - return bookshelves; - - } - - public enum SortType { - - TIER("tier", Comparator.naturalOrder()), - ELEMENT("element", Comparator.comparing(Spell::getElement).thenComparing(Spell::getTier)), - ALPHABETICAL("alphabetical", Comparator.comparing(Spell::getUnlocalisedName)); - - public String name; - public Comparator comparator; - - SortType(String name, Comparator comparator){ - this.name = name; - this.comparator = comparator; - } - - } } diff --git a/src/main/java/electroblob/wizardry/packet/PacketLectern.java b/src/main/java/electroblob/wizardry/packet/PacketLectern.java new file mode 100644 index 00000000..c499aa6e --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/PacketLectern.java @@ -0,0 +1,73 @@ +package electroblob.wizardry.packet; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.tileentity.TileEntityLectern; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** [Client -> Server] This packet is sent when a player closes the lectern GUI to send the last-viewed spell to + * the server. */ +public class PacketLectern implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx){ + + // Just to make sure that the side is correct + if(ctx.side.isServer()){ + + final EntityPlayerMP player = ctx.getServerHandler().player; + + player.getServerWorld().addScheduledTask(() -> { + + TileEntity tileentity = player.world.getTileEntity(message.pos); + + if(tileentity instanceof TileEntityLectern){ + + ((TileEntityLectern)tileentity).currentSpell = message.spell; + ((TileEntityLectern)tileentity).sync(); // Update other clients with the new state + + }else{ + Wizardry.logger.warn("Received a PacketLectern, but no lectern existed at the position specified!"); + } + + }); + } + + return null; + } + + public static class Message implements IMessage { + + private BlockPos pos; + private Spell spell; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message(){} + + public Message(BlockPos pos, Spell spell){ + this.pos = pos; + this.spell = spell; + } + + @Override + public void fromBytes(ByteBuf buf){ + // The order is important + pos = new BlockPos(buf.readInt(), buf.readInt(), buf.readInt()); + spell = Spell.byNetworkID(buf.readInt()); + } + + @Override + public void toBytes(ByteBuf buf){ + buf.writeInt(pos.getX()); + buf.writeInt(pos.getY()); + buf.writeInt(pos.getZ()); + buf.writeInt(spell.networkID()); + } + } +} diff --git a/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java b/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java index 045205bf..8feabd56 100644 --- a/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java +++ b/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java @@ -31,6 +31,7 @@ public class WizardryPacketHandler { registerMessage(PacketEmitterData.class, PacketEmitterData.Message.class); registerMessage(PacketPossession.class, PacketPossession.Message.class); registerMessage(PacketConquerShrine.class, PacketConquerShrine.Message.class); + registerMessage(PacketLectern.class, PacketLectern.Message.class); } private static int nextPacketId = 0; diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java index ed25dfd4..244e5291 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java @@ -64,6 +64,12 @@ public final class WizardryBlocks { public static final Block jungle_bookshelf = placeholder(); public static final Block acacia_bookshelf = placeholder(); public static final Block dark_oak_bookshelf = placeholder(); + public static final Block oak_lectern = placeholder(); + public static final Block spruce_lectern = placeholder(); + public static final Block birch_lectern = placeholder(); + public static final Block jungle_lectern = placeholder(); + public static final Block acacia_lectern = placeholder(); + public static final Block dark_oak_lectern = placeholder(); /** * Sets both the registry and unlocalised names of the given block, then registers it with the given registry. Use @@ -110,6 +116,12 @@ public final class WizardryBlocks { registerBlock(registry, "jungle_bookshelf", new BlockBookshelf()); registerBlock(registry, "acacia_bookshelf", new BlockBookshelf()); registerBlock(registry, "dark_oak_bookshelf", new BlockBookshelf()); + registerBlock(registry, "oak_lectern", new BlockLectern()); + registerBlock(registry, "birch_lectern", new BlockLectern()); + registerBlock(registry, "spruce_lectern", new BlockLectern()); + registerBlock(registry, "jungle_lectern", new BlockLectern()); + registerBlock(registry, "acacia_lectern", new BlockLectern()); + registerBlock(registry, "dark_oak_lectern", new BlockLectern()); } @@ -124,5 +136,6 @@ public final class WizardryBlocks { GameRegistry.registerTileEntity(TileEntityPlayerSaveTimed.class, new ResourceLocation(Wizardry.MODID, "player_save_timed")); GameRegistry.registerTileEntity(TileEntityShrineCore.class, new ResourceLocation(Wizardry.MODID, "shrine_core")); GameRegistry.registerTileEntity(TileEntityBookshelf.class, new ResourceLocation(Wizardry.MODID, "bookshelf")); + GameRegistry.registerTileEntity(TileEntityLectern.class, new ResourceLocation(Wizardry.MODID, "lectern")); } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryItems.java b/src/main/java/electroblob/wizardry/registry/WizardryItems.java index b5c52052..e717d7f8 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryItems.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryItems.java @@ -407,6 +407,13 @@ public final class WizardryItems { registerItemBlock(registry, WizardryBlocks.acacia_bookshelf); registerItemBlock(registry, WizardryBlocks.dark_oak_bookshelf); + registerItemBlock(registry, WizardryBlocks.oak_lectern); + registerItemBlock(registry, WizardryBlocks.spruce_lectern); + registerItemBlock(registry, WizardryBlocks.birch_lectern); + registerItemBlock(registry, WizardryBlocks.jungle_lectern); + registerItemBlock(registry, WizardryBlocks.acacia_lectern); + registerItemBlock(registry, WizardryBlocks.dark_oak_lectern); + // Items registerItem(registry, "magic_crystal", new ItemCrystal()); diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java index 8db0c753..b93b35ff 100644 --- a/src/main/java/electroblob/wizardry/spell/Spell.java +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -552,6 +552,9 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C return ((ForgeRegistry)registry).getID(this); } + // N.B. You don't *have* to use the network ID for networking, metadata is consistent within a world and will work + // fine, *however* when syncing a list/array of spells it is much more convenient to use network IDs. + /** Returns this spell's network ID number, similar to mod-specific entity IDs.
*
* Unlike {@link Spell#metadata()}, this is guaranteed to be sequential so is suitable for indexed lookup. @@ -947,7 +950,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C return spell == null ? Spells.none : spell; } - /** Gets a spell instance from its network ID. Or the {@link None} spell if no such spell exists. */ + /** Gets a spell instance from its network ID, or the {@link None} spell if no such spell exists. */ public static Spell byNetworkID(int id){ if(id < 0 || id >= registry.getValuesCollection().size()){ return Spells.none; diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityLectern.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityLectern.java new file mode 100644 index 00000000..2cbdd8e6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityLectern.java @@ -0,0 +1,98 @@ +package electroblob.wizardry.tileentity; + +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.spell.Spell; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.play.server.SPacketUpdateTileEntity; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ITickable; +import net.minecraft.util.math.MathHelper; + +import java.util.Random; + +/** Controls the book animations and remembers the GUI state when not in use. */ +public class TileEntityLectern extends TileEntity implements ITickable { + + public static final double BOOK_OPEN_DISTANCE = 5; + + private static final Random rand = new Random(); + + public int ticksExisted; + public float pageFlip; + public float pageFlipPrev; + public float flipT; + public float flipA; + public float bookSpread; + public float bookSpreadPrev; + + public Spell currentSpell = Spells.none; + + @Override + public void update(){ + + this.bookSpreadPrev = this.bookSpread; + + EntityPlayer entityplayer = this.world.getClosestPlayer(this.pos.getX() + 0.5, this.pos.getY() + 0.5, + this.pos.getZ() + 0.5, BOOK_OPEN_DISTANCE, false); + + if(entityplayer != null){ + + this.bookSpread += 0.1f; + + if(this.bookSpread < 0.5f || rand.nextInt(40) == 0){ + float f1 = this.flipT; + while(f1 == flipT) this.flipT += (float)(rand.nextInt(4) - rand.nextInt(4)); + } + + }else{ + this.bookSpread -= 0.1f; + } + + this.bookSpread = MathHelper.clamp(this.bookSpread, 0.0f, 1.0f); + + this.ticksExisted++; + + this.pageFlipPrev = this.pageFlip; + float f = (this.flipT - this.pageFlip) * 0.4f; + f = MathHelper.clamp(f, -0.2f, 0.2f); + this.flipA += (f - this.flipA) * 0.9f; + this.pageFlip += this.flipA; + + } + + /** Called to manually sync the tile entity with clients. */ + public void sync(){ + this.world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); + } + + @Override + public NBTTagCompound writeToNBT(NBTTagCompound compound){ + super.writeToNBT(compound); // Confusingly, this method both writes to the supplied compound and returns it + compound.setInteger("spell", currentSpell.metadata()); + return compound; + } + + @Override + public void readFromNBT(NBTTagCompound compound){ + super.readFromNBT(compound); + currentSpell = Spell.byMetadata(compound.getInteger("spell")); + } + + @Override + public final NBTTagCompound getUpdateTag(){ + return this.writeToNBT(new NBTTagCompound()); + } + + @Override + public SPacketUpdateTileEntity getUpdatePacket(){ + return new SPacketUpdateTileEntity(pos, getBlockMetadata(), this.getUpdateTag()); + } + + @Override + public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt){ + readFromNBT(pkt.getNbtCompound()); + } + +} diff --git a/src/main/java/electroblob/wizardry/util/ISpellSortable.java b/src/main/java/electroblob/wizardry/util/ISpellSortable.java new file mode 100644 index 00000000..e6dd1ce7 --- /dev/null +++ b/src/main/java/electroblob/wizardry/util/ISpellSortable.java @@ -0,0 +1,35 @@ +package electroblob.wizardry.util; + +import electroblob.wizardry.spell.Spell; + +import java.util.Comparator; + +/** Interface for things that have a list of spells that can be sorted using {@link SortType}. This allows + * {@link electroblob.wizardry.client.gui.GuiButtonSpellSort GuiButtonSpellSort} to change its appearance based on the + * current sort setting. */ +public interface ISpellSortable { + + /** Returns the current sort type this {@code ISpellSortable} is set to sort by. */ + SortType getSortType(); + + /** Returns true if this {@code ISpellSortable} is currently set to sort in descending order, false if it is + * set to sort in ascending order. */ + boolean isSortDescending(); + + enum SortType { + + TIER("tier", Comparator.naturalOrder()), + ELEMENT("element", Comparator.comparing(Spell::getElement).thenComparing(Spell::getTier)), + ALPHABETICAL("alphabetical", Comparator.comparing(Spell::getUnlocalisedName)); + + public String name; + public Comparator comparator; + + SortType(String name, Comparator comparator){ + this.name = name; + this.comparator = comparator; + } + + } + +} diff --git a/src/main/java/electroblob/wizardry/util/ParticleBuilder.java b/src/main/java/electroblob/wizardry/util/ParticleBuilder.java index 273875b2..e0a1e8e2 100644 --- a/src/main/java/electroblob/wizardry/util/ParticleBuilder.java +++ b/src/main/java/electroblob/wizardry/util/ParticleBuilder.java @@ -93,6 +93,8 @@ public final class ParticleBuilder { public static class Type { /** 3D-rendered light-beam particle.

Defaults:
Lifetime: 1 tick
Colour: white */ public static final ResourceLocation BEAM = new ResourceLocation(Wizardry.MODID,"beam"); + /** Square block face highlight particle.

Defaults:
Lifetime: 160 ticks
Colour: white */ + public static final ResourceLocation BLOCK_HIGHLIGHT = new ResourceLocation(Wizardry.MODID,"block_highlight"); /** Helical animated 'buffing' particle.

Defaults:
Lifetime: 15 ticks *
Velocity: (0, 0.27, 0)
Colour: white */ public static final ResourceLocation BUFF = new ResourceLocation(Wizardry.MODID,"buff"); diff --git a/src/main/resources/assets/ebwizardry/blockstates/acacia_lectern.json b/src/main/resources/assets/ebwizardry/blockstates/acacia_lectern.json new file mode 100644 index 00000000..20fb6530 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/acacia_lectern.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:acacia_lectern" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/birch_lectern.json b/src/main/resources/assets/ebwizardry/blockstates/birch_lectern.json new file mode 100644 index 00000000..1acb6ae4 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/birch_lectern.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:birch_lectern" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/dark_oak_lectern.json b/src/main/resources/assets/ebwizardry/blockstates/dark_oak_lectern.json new file mode 100644 index 00000000..d193e745 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/dark_oak_lectern.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:dark_oak_lectern" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/jungle_lectern.json b/src/main/resources/assets/ebwizardry/blockstates/jungle_lectern.json new file mode 100644 index 00000000..e12d2b53 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/jungle_lectern.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:jungle_lectern" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/oak_lectern.json b/src/main/resources/assets/ebwizardry/blockstates/oak_lectern.json new file mode 100644 index 00000000..eb8141e5 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/oak_lectern.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:oak_lectern" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/spruce_lectern.json b/src/main/resources/assets/ebwizardry/blockstates/spruce_lectern.json new file mode 100644 index 00000000..dffef19a --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/spruce_lectern.json @@ -0,0 +1,14 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "ebwizardry:spruce_lectern" + }, + "variants": { + "facing": { + "north": {}, + "east": { "y": 90 }, + "south": { "y": 180 }, + "west": { "y": 270 } + } + } +} diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index c51e35cf..c2b51ac9 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -40,6 +40,13 @@ tile.ebwizardry\:jungle_bookshelf.name=Jungle Bookshelf tile.ebwizardry\:acacia_bookshelf.name=Acacia Bookshelf tile.ebwizardry\:dark_oak_bookshelf.name=Dark Oak Bookshelf +tile.ebwizardry\:oak_lectern.name=Oak Lectern +tile.ebwizardry\:spruce_lectern.name=Spruce Lectern +tile.ebwizardry\:birch_lectern.name=Birch Lectern +tile.ebwizardry\:jungle_lectern.name=Jungle Lectern +tile.ebwizardry\:acacia_lectern.name=Acacia Lectern +tile.ebwizardry\:dark_oak_lectern.name=Dark Oak Lectern + item.ebwizardry\:crystal_magic.name=Magic Crystal item.ebwizardry\:crystal_fire.name=Fiery Crystal item.ebwizardry\:crystal_ice.name=Icy Crystal @@ -519,6 +526,8 @@ gui.ebwizardry\:spell_book.mana_cost=Mana Cost: %s gui.ebwizardry\:spell_book.mana_cost_continuous=Mana Cost: %s/second gui.ebwizardry\:spell_book.mana_cost_undiscovered=Mana Cost: ? +gui.ebwizardry\:lectern.title=Spell Lookup + container.ebwizardry\:arcane_workbench=Arcane Workbench container.ebwizardry\:arcane_workbench.apply=Apply container.ebwizardry\:arcane_workbench.mana=Mana\: diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 6dee3005..ab54b962 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -40,6 +40,13 @@ tile.ebwizardry\:jungle_bookshelf.name=Jungle Bookshelf tile.ebwizardry\:acacia_bookshelf.name=Acacia Bookshelf tile.ebwizardry\:dark_oak_bookshelf.name=Dark Oak Bookshelf +tile.ebwizardry\:oak_lectern.name=Oak Lectern +tile.ebwizardry\:spruce_lectern.name=Spruce Lectern +tile.ebwizardry\:birch_lectern.name=Birch Lectern +tile.ebwizardry\:jungle_lectern.name=Jungle Lectern +tile.ebwizardry\:acacia_lectern.name=Acacia Lectern +tile.ebwizardry\:dark_oak_lectern.name=Dark Oak Lectern + item.ebwizardry\:crystal_magic.name=Magic Crystal item.ebwizardry\:crystal_fire.name=Fiery Crystal item.ebwizardry\:crystal_ice.name=Icy Crystal @@ -519,6 +526,8 @@ gui.ebwizardry\:spell_book.mana_cost=Mana Cost: %s gui.ebwizardry\:spell_book.mana_cost_continuous=Mana Cost: %s/second gui.ebwizardry\:spell_book.mana_cost_undiscovered=Mana Cost: ? +gui.ebwizardry\:lectern.title=Spell Lookup + container.ebwizardry\:arcane_workbench=Arcane Workbench container.ebwizardry\:arcane_workbench.apply=Apply container.ebwizardry\:arcane_workbench.mana=Mana\: diff --git a/src/main/resources/assets/ebwizardry/models/block/acacia_lectern.json b/src/main/resources/assets/ebwizardry/models/block/acacia_lectern.json new file mode 100644 index 00000000..1f7315c4 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/acacia_lectern.json @@ -0,0 +1,10 @@ +{ + "parent": "ebwizardry:block/lectern", + "textures": { + "particle": "ebwizardry:blocks/acacia_lectern_top", + "base": "ebwizardry:blocks/acacia_lectern_base", + "side": "ebwizardry:blocks/acacia_lectern_side", + "top": "ebwizardry:blocks/acacia_lectern_top", + "underside": "ebwizardry:blocks/acacia_lectern_underside" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/birch_lectern.json b/src/main/resources/assets/ebwizardry/models/block/birch_lectern.json new file mode 100644 index 00000000..5706382b --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/birch_lectern.json @@ -0,0 +1,10 @@ +{ + "parent": "ebwizardry:block/lectern", + "textures": { + "particle": "ebwizardry:blocks/birch_lectern_top", + "base": "ebwizardry:blocks/birch_lectern_base", + "side": "ebwizardry:blocks/birch_lectern_side", + "top": "ebwizardry:blocks/birch_lectern_top", + "underside": "ebwizardry:blocks/birch_lectern_underside" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/dark_oak_lectern.json b/src/main/resources/assets/ebwizardry/models/block/dark_oak_lectern.json new file mode 100644 index 00000000..fa7eb635 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/dark_oak_lectern.json @@ -0,0 +1,10 @@ +{ + "parent": "ebwizardry:block/lectern", + "textures": { + "particle": "ebwizardry:blocks/dark_oak_lectern_top", + "base": "ebwizardry:blocks/dark_oak_lectern_base", + "side": "ebwizardry:blocks/dark_oak_lectern_side", + "top": "ebwizardry:blocks/dark_oak_lectern_top", + "underside": "ebwizardry:blocks/dark_oak_lectern_underside" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/jungle_lectern.json b/src/main/resources/assets/ebwizardry/models/block/jungle_lectern.json new file mode 100644 index 00000000..0b560139 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/jungle_lectern.json @@ -0,0 +1,10 @@ +{ + "parent": "ebwizardry:block/lectern", + "textures": { + "particle": "ebwizardry:blocks/jungle_lectern_top", + "base": "ebwizardry:blocks/jungle_lectern_base", + "side": "ebwizardry:blocks/jungle_lectern_side", + "top": "ebwizardry:blocks/jungle_lectern_top", + "underside": "ebwizardry:blocks/jungle_lectern_underside" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/lectern.json b/src/main/resources/assets/ebwizardry/models/block/lectern.json new file mode 100644 index 00000000..03d694b3 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/lectern.json @@ -0,0 +1,116 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "elements": [ + { + "from": [2, 0, 2], + "to": [14, 1, 14], + "faces": { + "north": {"uv": [2, 15, 14, 16], "texture": "#side"}, + "east": {"uv": [2, 15, 14, 16], "texture": "#side"}, + "south": {"uv": [2, 15, 14, 16], "texture": "#side"}, + "west": {"uv": [2, 15, 14, 16], "texture": "#side"}, + "up": {"uv": [2, 2, 14, 14], "texture": "#base"}, + "down": {"uv": [2, 1, 14, 13], "texture": "#underside", "cullface": "down"} + } + }, + { + "from": [3, 1, 3], + "to": [13, 2, 13], + "faces": { + "north": {"uv": [3, 14, 13, 15], "texture": "#side"}, + "east": {"uv": [3, 14, 13, 15], "texture": "#side"}, + "south": {"uv": [3, 14, 13, 15], "texture": "#side"}, + "west": {"uv": [3, 14, 13, 15], "texture": "#side"}, + "up": {"uv": [3, 3, 13, 13], "texture": "#base"} + } + }, + { + "from": [4, 13, 4], + "to": [12, 15, 12], + "faces": { + "north": {"uv": [4, 1, 12, 3], "texture": "#side"}, + "east": {"uv": [4, 1, 12, 3], "texture": "#side"}, + "south": {"uv": [4, 1, 12, 3], "texture": "#side"}, + "west": {"uv": [4, 1, 12, 3], "texture": "#side"}, + "down": {"uv": [4, 3, 12, 11], "texture": "#underside"} + } + }, + { + "from": [5, 2, 5], + "to": [11, 13, 11], + "faces": { + "north": {"uv": [5, 3, 11, 14], "texture": "#side"}, + "east": {"uv": [5, 3, 11, 14], "texture": "#side"}, + "south": {"uv": [5, 3, 11, 14], "texture": "#side"}, + "west": {"uv": [5, 3, 11, 14], "texture": "#side"} + } + }, + { + "from": [0, 14.07612, 3.61732], + "to": [16, 16.07612, 17.61732], + "rotation": {"angle": -22.5, "axis": "x", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 14, 16, 16], "texture": "#top"}, + "east": {"uv": [1, 14, 15, 16], "texture": "#underside"}, + "south": {"uv": [0, 14, 16, 16], "texture": "#top"}, + "west": {"uv": [1, 14, 15, 16], "texture": "#underside"}, + "up": {"uv": [0, 0, 16, 14], "rotation": 180, "texture": "#top"}, + "down": {"uv": [0, 0, 16, 14], "texture": "#underside"} + } + }, + { + "from": [1, 16.07612, 3.61732], + "to": [15, 17.07612, 4.61732], + "rotation": {"angle": -22.5, "axis": "x", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [1, 13, 15, 14], "texture": "#top"}, + "east": {"uv": [1, 13, 2, 14], "texture": "#top"}, + "south": {"uv": [1, 13, 15, 14], "rotation": 180, "texture": "#top"}, + "west": {"uv": [14, 13, 15, 14], "texture": "#top"}, + "up": {"uv": [1, 13, 15, 14], "rotation": 180, "texture": "#top"} + } + }, + { + "from": [4, 15, 9], + "to": [12, 17, 12], + "faces": { + "east": {"uv": [9, 0, 12, 2], "rotation": 180, "texture": "#side"}, + "south": {"uv": [4, 0, 12, 2], "rotation": 180, "texture": "#side"}, + "west": {"uv": [4, 0, 7, 2], "rotation": 180, "texture": "#side"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "thirdperson_lefthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "firstperson_righthand": { + "rotation": [0, 45, 0], + "scale": [0.4, 0.4, 0.4] + }, + "firstperson_lefthand": { + "rotation": [0, 225, 0], + "scale": [0.4, 0.4, 0.4] + }, + "ground": { + "translation": [0, 3, 0], + "scale": [0.25, 0.25, 0.25] + }, + "gui": { + "rotation": [30, 225, 0], + "translation": [0, -1, 0], + "scale": [0.625, 0.625, 0.625] + }, + "fixed": { + "scale": [0.5, 0.5, 0.5] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/oak_lectern.json b/src/main/resources/assets/ebwizardry/models/block/oak_lectern.json new file mode 100644 index 00000000..bbd75a37 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/oak_lectern.json @@ -0,0 +1,10 @@ +{ + "parent": "ebwizardry:block/lectern", + "textures": { + "particle": "ebwizardry:blocks/oak_lectern_top", + "base": "ebwizardry:blocks/oak_lectern_base", + "side": "ebwizardry:blocks/oak_lectern_side", + "top": "ebwizardry:blocks/oak_lectern_top", + "underside": "ebwizardry:blocks/oak_lectern_underside" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/block/spruce_lectern.json b/src/main/resources/assets/ebwizardry/models/block/spruce_lectern.json new file mode 100644 index 00000000..396cdfed --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/spruce_lectern.json @@ -0,0 +1,10 @@ +{ + "parent": "ebwizardry:block/lectern", + "textures": { + "particle": "ebwizardry:blocks/spruce_lectern_top", + "base": "ebwizardry:blocks/spruce_lectern_base", + "side": "ebwizardry:blocks/spruce_lectern_side", + "top": "ebwizardry:blocks/spruce_lectern_top", + "underside": "ebwizardry:blocks/spruce_lectern_underside" + } +} diff --git a/src/main/resources/assets/ebwizardry/models/item/acacia_lectern.json b/src/main/resources/assets/ebwizardry/models/item/acacia_lectern.json new file mode 100644 index 00000000..59f077e4 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/acacia_lectern.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/acacia_lectern" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/birch_lectern.json b/src/main/resources/assets/ebwizardry/models/item/birch_lectern.json new file mode 100644 index 00000000..aaa107eb --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/birch_lectern.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/birch_lectern" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/dark_oak_lectern.json b/src/main/resources/assets/ebwizardry/models/item/dark_oak_lectern.json new file mode 100644 index 00000000..324650ba --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/dark_oak_lectern.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/dark_oak_lectern" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/jungle_lectern.json b/src/main/resources/assets/ebwizardry/models/item/jungle_lectern.json new file mode 100644 index 00000000..59a3dffd --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/jungle_lectern.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/jungle_lectern" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/oak_lectern.json b/src/main/resources/assets/ebwizardry/models/item/oak_lectern.json new file mode 100644 index 00000000..2ec35f3e --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/oak_lectern.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/oak_lectern" +} diff --git a/src/main/resources/assets/ebwizardry/models/item/spruce_lectern.json b/src/main/resources/assets/ebwizardry/models/item/spruce_lectern.json new file mode 100644 index 00000000..4aeb91ca --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/spruce_lectern.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/spruce_lectern" +} diff --git a/src/main/resources/assets/ebwizardry/spells/arcane_jammer.json b/src/main/resources/assets/ebwizardry/spells/arcane_jammer.json index bdd61d41..d6b4634d 100644 --- a/src/main/resources/assets/ebwizardry/spells/arcane_jammer.json +++ b/src/main/resources/assets/ebwizardry/spells/arcane_jammer.json @@ -12,7 +12,7 @@ }, "tier": "advanced", "element": "healing", - "type": "attack", + "type": "alteration", "cost": 30, "chargeup": 0, "cooldown": 50, diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_base.png b/src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_base.png new file mode 100644 index 0000000000000000000000000000000000000000..7769787524173dcdecfce13b2725467a0ff44d60 GIT binary patch literal 1801 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-<*Py>N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsEj3^PHrj$+PEkS0&RVhJ(csXMeGO{!=*Z#HlDF?bW;Wg(Fy6 z1o|d;H@y7+PwJVx&6$f*&rRDznvOo6jZ7n2=>e;9Kda3c=`V(W*)#|3~W5yFN%sU{LFqwbChnY9u{@*k`)w!!` ze@oSu<1gd$4rD(&-dXga@yf&3H&(T^l%>2Dsms26A<*({;XauK{QsW?rQSFa>!GtR zF-qh2*^0ZrUR`lKd7D_Q~8v8QC}SDj&F(N+iUfN z+4*5uy{7tqHS_ON^zOY&<9lfK*?Q--mlJkBPs-hwyv4%p{_4|u2`3-wyox%N<+$D_ ze{CS6>&GhT<(tzzH3T&S1>Jca!veHalP*}C2wd#b{A-nV=!_RTj4M)}opQSBAmQlt zh^r|xNUM)+wMwSK%%w^)EJ6EjI%VJ5vTU`8Vd3Ho0b3S>D8WzcNB1l;Ip(9GB%Qyn zDb~iAH_Yc*M~~`?MnS3LE9)$OXvxn%xw^q|{~ei)2XaokF#J7wIlADaQq+PMf$v$= za@iU5npowIO=w`0_OM~ybSh^GM_-SE#^DmNRVm*EtQXw5snV z<+Laq@-|rXK+Tga@?7PS{#_jsr{eZ}zQ?}a?$?vz2?oYGEWE|;XSH5k$qu->!gF({ zQ0x~&mtMnVGlbO-_PrMVWO#H+(772V%$CU|ZBFM}yG6olI93qv{GQsQQKw%on0)kt&*7IL+9&oMG%G$66O_Ak z>g8>RuP!<#Y~y(2Px|2r-K~Ew9oQ%FNn}$0$^*L#yM%)l6h^#N3SIJ>`G$>}40r$9 zhxe`paQA;%%ImZ$C$T?-S@&us+y3=iTh^NH$Tn15lIwD4s>*x0V{vdI@;svUT34HW;Trrj5Q3J7F;WFQ+*|6ZgB3!`t0QWfeY?^k1~nMb^3VY$%e@L zt7oZ8+&@?N=D1^{#jE4*qpZY)xkJ=P`S(d|(ccQ(f(VXP9vtLxM{uLSfZB^{4 zlvg*Fm@fCZD_@^daoz5T?Q~(;zF!tBGxs{(RlE`z%S;16tDw}sVd%M*qwRso9R*Fyi$#YMy`v%`y-My-}ZEv}k z&kXAlI^(~4fKaL$@kb^b+p+N|Z@{vC+A@QZ`K+HE?4I%Js>fS(*9Q5Cm!ub3{P_Hx>7dPrH?p^~3$ylpILdtB z`Lmhf9miMhuS)atd?IZq+5CdXXh+wb4X^B+gv)k4IiJWDw(hUM?4tcWzn(;VZr4=% zIU{A#>m3WzqNe->c4%;cjRZity*(>Qe59f;XLMZa+ZJQyivdTU-f6GUr>gu zrtMLq{f0mPOL!El_IYM++a9EN{@rR31_lPUByV>YhW{YAVDIwD3=9mM1s;*b3=De8 zAk0{?)V_>?fq}im)7O>#2@AWZkWS@vfoBX143Z_T5hc#~xw)x%B@E6*sfi`2DGKG8 zB^e6tp1uL$jeOz^42*7`E{-7R^#cr>q~oL+Q0KQ`_n7Z zmdx$t_?_6gL9HW7t=lQ>Zrq^|*6f+de&^Q*h^gc_CrD`VpU>yBZoc5GHN6vc+}v<+-zR zRy93Zef|FF=7V9&yJfzwG^=r{ZhCRjtDfav24m_Lr=?eBpRVNp(3bRcPy6c^&hu=z zJeIBPyl%haj@0L43{g6VY$iRoytB^yKgWv7&5Zrm^@SN7o0so%GRh4+SN*qb>ucXz zru|-=+sYqqHe*d#)fBd{`}K>OJNx-vN@A8z&sp^-z2Wr>XZxIM;s5M^v9X$j+TW46 Q0E!6)Pgg&ebxsLQ0INS(9{>OV literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_side.png b/src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_side.png new file mode 100644 index 0000000000000000000000000000000000000000..5dfc524ba47360ac030eb49a9436a1585e33f181 GIT binary patch literal 1902 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-tExgGN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHWfh@AFBgXhn4kzy7Vfu_eB_L-Z{cu_4Ke$vOVtI?}P2`OynmGN(0e-)!$GiNfXX z=N?HeYt5@Sva7Q#J@w$+gs#_>7uch7)-0N@7qfrgzD;*xmdT%*ud%oK%gGc4bdM3rPXhI_S>QCwCGU3)SNXB)=lYfJgeZ}YsC9%_1cicMbFPYm^f40dKFu< zV2a8*g&7Vec~WcicCt;n8M>qSu*Usu7lLL_+vO$LCGE=mUQx&GwDZ3QX7>f9lG#Ki z_L_%n=uTg9uLcyR-Mq)$boB)N@XWTj&Qdk@%mu>zW&?wOsN zKFGN#u?RkDD&)$WbXwtbb>sgP{T~+U`0eRn3GAGt!Y-cH(z+lq?aCUzyX~YM@0`GiodtzQ#WfdZbgE8`xfv2(J5_4c zwTR%ey|c4B9*fP|b|dKYT&tLszx8ZO-fmnzk3A-L$HVp>gOVc?&zvyNZogLkoj+pn zVJZD(%NE>QlNjz~WPgPib>=5LpP!s@`!r9zc(Uq3OET*ZXxl|J*PHJDm41yy(z^xd0oscLs^*7pe0t$f?3J-F25 zTozsXtnc92sh13_*UAMlkc7C*BYCYrG?Cf=Z z^68Q)zXiJU1SXV(rHhHjZ+H9SsD1WxP4d&2sq)ERtRBpYnQ?QWY3o_)#_n0`` z|9@?ptv_Q%^YV4Koz9B3J}58$d}S8D+JjHg+N<_GTz!d+Co!&h%Hgbiytm{s9yDq8 zs&+1V`@Qy5jVeRGeaN{WeT$YqmnI38`k7S;ihYdB*cE(NFDyNv=(hDoj(Fd%Z#F%6 zP~SW6_%$81s9TRjcd}*qUkRM{)|d14{RE2|^*}rCbXGzYUd(hFFcjv^&pA4!o_fCBXPF|wzA#kv?=Cb&i$;N*g^=I&Z zkUcKGW>K2uzJyd6_7gQC3lqM-ZPJqVdKbWFdw$;rLC1}i%NxGzG@kLk>YdTHerL{} zS93pk)?57Ev@^y)ZB9r#*T-4E(sw?%IKlJ!oJ~J8t4_+_Qdu@#w(hdw8UD#PmVcX> z`QV`1wA<;2+O{tas3@D^-!JufvGMb}g=?crS6V1kcYVFo$+X~YOQS&6_CrqXvVZt4 zGRGfyKi&PunfzzJ%GXx5$4~IhY;?cl&Od+ho%>nMZ+1pjoV{Rp|J$+idrNlS`8B=m z>NAZvmqm7aR=0E?>(`pKdq>2hXIoeOxVz|(jlm9%JM0OZefPrl7gTP3Rx0`9;^NBr zZ=OvLTU6omte$?k)`fL2$v|<&zm07&r?&B8wRq^pruEv0|xx z83O|Ydx@v7EBg}`c2Oa&)w8BbGcYhnmbgZgIOpf)rskC}I2WZRmZYXAlxLP?D7bt2 z281{Ai8C-T26(zShFF}Q8obf(uz^5r;p3>nscLI4d70?&xbmc~lsI)HLO@w4__Y6p z{)B`F{6Cufc>XXj8w)WfF}pgQ5bD*s=*G#*yyQrx%mdwN@9)~)wViL8V(@C`#^jl& ztrxR6?qFEVGVeoS-RsPkHBB-GUI$)?PZeQ*sH87iWo9t5wc$hYWcAGjs>`0L&ilIk zQ3*SP!k?F3zfTF?^6mSb&glO`;Gc-Ag4Q83jhN$2$ul#|cLq7!f4?QbP(o^}mW6(D z^I@mz1ZxGMSm3n>Nzxw56Q8X`OP za30v%A7y&z@)l>QEWD^Stm#h4L(%9u;KR!$+zc};Tn$8{}i)o#K=X)pLP&W}N Ws@@-U;tK-<1B0ilpUXO@geCyPe2t_4 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_top.png b/src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_top.png new file mode 100644 index 0000000000000000000000000000000000000000..4f588ada8aab2a2ebdaf1e55d6557e482b4e7414 GIT binary patch literal 1990 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 E0Am7_i~s-t literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_underside.png b/src/main/resources/assets/ebwizardry/textures/blocks/acacia_lectern_underside.png new file mode 100644 index 0000000000000000000000000000000000000000..8305f1707b9491ddd29870be023b9f8344f6c97a GIT binary patch literal 1528 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s->Z?K`N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHWvo;_`g22b1nbkWT$LLAedGMuv9+#LCDk3y8V?(V8*m%|L4 zgg&)I_`bHUH~+=mzQO#7iOc*w<`ywUo<>#|bKK?1&GPO%Z@y`*Ut|@>;Uc#7uH54J zpG1oeR?Pfl^)=J`g{xMs4)3F@#RWU84HbX1)QLQ1dbF*?J$mYu=b1CR?#;`T*ZSZYcKX_b zce~rmb{pxLzgyUUaHgSFZ}kluR$awrcE9SF_x;xAGs|n0>K&`T{Am8uY0}&9PXCRx zV*QrsMc3Nr%$vxU=Ra+)z0Up<7y0$G@2&j0daLTTZF#FtpG!VjrP2GA%ZpN* zZO%1#`@T8&F8z??Mpdm(8IF@g=S&smHZUvOQ}FEA>kRGi9WPQu3q&SrP1AOiU{v;G zO<>EIb4P{YLy~er_>$(<5We_|8NP>Oo^l-Kc6a(_Fy-Tk3I7W(nKwthmhQTFL^tZA(fvtz=NpDx!+?u<(lZVl zi7L2!aCpcQEnqNj?WS~L7YzgD34#qqG2*Y6FmSIv#iV{T@--v-Zia1!>K`Puv}Q}R z3MgEu+QgW%ftB&JrRzTy@rllnYpp`RN||I`db9Su^_kxfs$I_>WD_^}A>hQLnbKvp zYsJOm0?9sx>Yiev;lkgNyEK>1Ni=aTKK7=E(NbJ<+m1y?rw1f?s)jGjTD>}|me=%J z$Y-VQ*bV*S`&(4?GEZu+_gH#$?XF*@YYKg{^E|eu&Cb46!>zl`MOXfc+O^9+xwC9m zo>WLZzUWnyM`?p5r-k#IT{=uR*u7Bu#Bo@uiccX=QtS3tsl&-n#AH z^eG>{2LGA4eA+$M|66N=pDgan)48_pu=x9iqCKvfK6NLACiE6|dw2w8{;ilRbpGlBrg>-PteLa-?%pNb=JF2> zclUN1?7gY3Vd4L0<XrUt~F1mzj+@#Pp|0pv~$bvEW2L%f7OHi7Z&@^zVK(m_Iuh^e?L6@ zxhH%*_ifz?>!W_XZCRdQc;{M}*Aai_tf+0rr5E*0`BC`L_qBGOZT%W&S2aoi@ zhtH#?>Ry~Cm1e?muWbXX^#7c-s!PwR|M~qdt~M|FSDC`CIcZAW_wQUe+B(Jgu>KmC z|4a<8&n&jje!8E5fq^Z_+ueoXKL{?^yL>VO0|RG)M`SSrgPt-7Ggd6MFJoX}U@!6X zb!C6T!Y(Q-Xz?U>8v_G_WQl7;iF1B#Zfaf$gL6@8Vo7R>LV0FMhJw4NZ$Nk>pEv^p z!&Ofg#}JFtbAt`}4jJ(530t`|ea&xYp|hNeca$iMKp{ zru;Hc%S`>U;6y{?bd}Q8Yq$+2GERTk?P0;NWKy1|y}9uOhfJ?4$*oNMFJ>k=Bp(%C zvSi~&t-NU$S+!m-N`BJD#$)r~xvr$T-lZw*Hhf~4J)iXFYu?lmw|KWb_HS0Y?Y?_* q7O|&X7!T|$nelt+`?K30vRgi0q`GC!27d+y1_n=8KbLh*2~7abn#TwL literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_base.png b/src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_base.png new file mode 100644 index 0000000000000000000000000000000000000000..19b1e4824340363fdaea73467ca261f690d74fe1 GIT binary patch literal 1790 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-WvfCWN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsEj3kDm5egXeF$=w=obfdx-L*rb;koSuLB@X94tCqqKNpPz9f zL2*gw4>i@|-(~miGP)gsBmG|JIPp_sLOPzeDyMV0`|nfzPqX=c ztmFFnqW|*#Lk+jjw3kn8X6cgiKW}zyL5BJ^JEgo_CfTW_eUpoLYOBnrYcUI#yPta` zspho(eb4Rn>2s_!Hv2~Jo7kjlX0~W;c+CCy_Y-Dw|62H`@ltem+==%y%(NGTxBos8 zu;Z%#Qx*B5V*i(q-kvMwFhAe_$%474Ja*05>Yi!GUR0OeWcRV0;~5(qsrzE86)}bMeEZF#gklKs;X?g%{Hc|OjfMl6~yXte(phKs>wbe4Q>E^Ud#xLRv`=1nbXRy0E?iDvUTE?T` z;-J9b%*S)X!B1mC?-?~A#R;4&sSfGScQ%L#J!G5kW-hk^`)Y=LhH96BQXlFbbK_7t z!unWgN8ohFY2TavblNGlZ?gEPB&6acG*u>MhPFOR!RKv}#hQ=>#lgeHdRs8sw z)U`rx-ECL4JoS@Zd8Kx`T-BSE+2@&8n%()}>?TpTWMSG9>FoAv@u$oq79W<fE9%9g(JjZRap}`C5jXb*w zzaN|zJX@+-){p=B zPw5MP8n3;LDNXxcz~XQ-YiUwrtMGNdD@{Ff+nPjoEIi|XDE9k=@Y6jBe-E!HXxB^R zzIn}X?)M#M#T)7+s)cudKUkOeUee<8+7)dDNeaen?sk*aYb_NQ?+@Bz{VkpQ!1DvT zH#hz~e(Q1AsfgZ}S9RL=H&1?NJo{Eu_NC-i;+>P7e+hbgW_jB0FiTydo~0;Xxn4o} zthmSb^jTf~Co7HCMyOQHSy?%`x8_gu+f{FKX6_B(_kWaGckb8Yy3D%jdYf-&_;1#+ zEU$X&{zEFjR+hD$f2+2G-qq~aC#40>i7MVrd31iZuePs`oFQ+z>6;}R>werS?Uyy$ zz3WZOtCPFr?!H~|_k%%p@&4PrZ~9pM_U=-8e&={{`h@kn_OpJ_e-WG;R~qy6?#cOn ze5vdAI~ns!e_-EI=3DA?IQ;6i538T8HCt~Lcf~fJ-&QH%pI7mpexs@TGheypzWIOu z!hz+B9?AR`S1lJ=xLm8Ffq{X6Ey>&6h2cL4F4((#G6MqxXMsm#F$061G6*wPEVVCV zU|?V`@$_|Nf5O5p%E%gZ>ZAk%1A}CVYeb22er|4RUI~M9QEFmIYKlU6W=V#EyQgnJ zcq5-U0|TS2r;B5V#p$z?&U-NhiX5v?p8Tk%>aX!3(XCFgJk5Gd6HbY1a5R#(=+{e?}wwoXajt}!QaWLKQknd2(5&EV)owO+H@f}P*%p3i$Px3750tGro3fdw1l zRpxmpO_Ef)Xz;X%ZQ;w9n0t%zG?ygZK9ZCuH&0e<;g(sfLKjV}fAWW9I)oL~6uGaC zRG25e!bJL|!e1MkI|m$#(;*m+@7gXY@< zpU%$OerPR+&a}OpPW!9=KKkSP;BIBCzQK#EMkeECbEAuEDle<%YLzfNt4@$^UG`Y2 zH*>XG{E|SmoCekdmQI)Vi*HzEdB0_?^wa;%{`J$`Sj_WmK(WH$>FVdQ&MBb@0Le-{ A!2kdN literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_side.png b/src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_side.png new file mode 100644 index 0000000000000000000000000000000000000000..e71307dd8d011368868bc4c6881773e92eaaec1b GIT binary patch literal 1808 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-^{YZ6N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHU}kDT;Gi|5aCkzy8=jdJa#kM2H@t)FyM@A@Ut*t^NQc&2Ui zm@+9~^|$}^v40O9)mhW?^`Pr>6Fp(yCoPT_UVDA^yLIMw-G$>1{MMZl=b5-*`d5iN z)67puey%)y`r2fL?c%N*--`eI=X$Hl;tgw`=yxZlY{tE!tG9@9zMp!w>Rfj8t)mat zM{88q-G9Ga@Y1{VBWjdB?s9@iW3MFF4L! znQ&t6UvJBsvtwd4=kAFOD|;97(=YFn!HK6<5z_nbWxv?F_O9yV4XNABOLJ2D4D|W4 z&#sr5b^QD*gC5%`7jBMDCi_(dn|i`Dgqn}}wP~rox{?;Zf??6aV-F_IXJlgI2-?KV$YBgO@fY4gYdpK8rEx6!Y2gBAj9J z`e;TU78bDtj{^;5D$`^#&#$o&Y~y&cS!9ZYL_^KH(q%&F|0 z$l=%`y8KAfLj_gH=eY)b0gdMz@)x!26HQ2EfD&v_|Vo*b3V-~0IPe}G_4V{?OrMvEj zkeANNqN!aL>(>2}%38BBuvy#ma!&Et?H7`~W!GI>Iy-Oo>wWEVq5kUGv#%}x#+bb& z^6|+lGhX;#Q~3HXz)^Ybjf0XB$<7LQ_}M(JSWIU6p_E)& zEXX{v?efXHy_Tzw{FS~Z(($1F;2fzdwaq)z@5OBlyKM6!#mMAPfkY_xqV>n`IX#+i z%=722_xpMpAH_7KJel(Gh^CG2nG~IW3-0U9_1kenljoRctn%`j1(w@%k3YKR{&>&d zHQysmcVAJM&9*&U>)DKizbsPDmCON4de=5G>$>V^ZeM!xmMUM1f0f3ipyH^k6aQQhd)iloGj_FnZCUjEWP$l{?OI7|$-9AX&**DC+Fo=0*!R6v4?nNWyZrh>7t40B zLaE~ijD(U;vRjhx6^G*pUlZk{W~{q?@Wig%Vh0X_J7}X&Nb%Z zdBujVTu0^)uhr~6eR+A|Rjd1bj-9O6R#>o^O(?ms>0(#A)vJk;+1H<3yku1J`Im9a zt8=|>PV)1Uj6Q|N`WsG5`~6L0$%DQ>`?jcs1SC2K*r)vN%=ucoZKa;}twpw36I!{J z-(gx^U{(;>=E9oyD$YR7=}~;Q79`)qQpSWYzPuQts;_2(k{)B~Hl!5OGv*JYt1_sFz*N775 z{M_8syb=cIqSVBa)D(sC%#sWRcTeAd@J2py1_nl3PZ!4!i_=pl@Ao##uqr8Y`i?LD zlfGU17OnTa=6TJW_jihUjWVuiZ(P0SgttaB9}JoL}F zs>?Uf7S)#i@vm#yW+mJ7GeLXzTXs*DWhhx(u&Q=#YtYT*e?R)EbG&d<{G9NrqsDRL zyWcxbEe`oMUEG7OL~hOM&&;1#TUXoUXljV5`>&W?$x{#UPhVa=WqWwA?4Rt1Ddzo;cRw)`b-(&Nk=>g6C;Ns~<@!-Y3ngE4 zJo&uSFRzj@n(JO%^FrRgMoYssUtM!_@}e*9Qgd%kHJg0&ZP|=Z^JEw4vab7-aYg&1 c#y5816WKbSu893+U|?YIboFyt=akR{03x7d2><{9 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_top.png b/src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_top.png new file mode 100644 index 0000000000000000000000000000000000000000..41146dff53993405aec62497da0916f529e22549 GIT binary patch literal 1977 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-H&%s2lmsP~D-;yvr)B1( zDwI?fq$;FVWTr7NRNPt{9=Yj>2G5`8BE>8!8#^9va67~M?8juCvY)wsQUMuc1w{@f^9b3-% z^^AOa%}z#pV|hO%c6kje8`;|u&Q9?+cX90B%2^WP_t`qWZ&CUCXR}TnI1=ljGcQqT z%HPvJc7E+C=6kotZBxeuH?D0rZgkY``t$4c>+ln2S^oz7YrC}DJ^qBe&+MlcvK{Uj zioE+4Z25Eg9UINqJ+@(d595CJ+WdMs;dtprv-)Hc*~R&dUoX zS`YeuRIa{!^RwR$QO!U>cV5S^f^e@R3koMJcALre^=jCvh8H`GD^i}Fa=PlULP5EZ zbtTK9!pjddJZEWStm9yBoRY7zWtwmHPOq+3>6MOO1g2cWrUS8uf+ zJery5U^r9RXqo|U&jky=ucr@Q|NC#&9xjP5r_XXRZ`KWFexW}v?x1RS#zB|^o1RtCj>HN6tC zb=fRw^UM>E#iDX<1)ZL2<+O75yXqGkv;A%PTW(Ew*e0KNWTEoRiTT&~*2=%TpU}&f zy=TTO;m2pCmKerqWlR&cw`5L!!_jj5BtvK7yg8Ao&nt|~EjM>g6rIO-$-hX-_t=32 zuaDGSID5C-GGFD7{T@kyga6r!(}KP;KePMvO)^_|eXpOGvoY`T;I#_B+nUu)idU+* z7MIMMBYs6`?xUTxace8}f2@CN?Rxk5)z@9Ty151`qawB(X}(*qHFt{URk;{umrr|V zhnR}&zuyt{A-VqD!*}lsQlgeMrRSf$^l*dwDWPMNl!f=~4eh>mJZII{6VliE3+sg> zZugp3A7DPs{j7S*#7B`w6Q}N2dpvAwOWF2kul_9RU6^BQ{2<-O_wsC^r`w)#ZTWCf zykMeawbYxsx{0gy+bPscxLjvEOF*KX^&j7D_BCbe1y-5HKk`dHIXB39hUm;$=brFi zIMd5}LFsoguU5?+gRRr{+9}<8pTU)MeMZ!}*(Ig-YK-EKzg()nvF6L_$iC9{do6b( z^*=M&_O4Qwlk%(HC4PL_=dOUw&LznLUWxq=Ey@<^drx1z!a4f2%}QJ6!enD(k;O5I zvS|_f6E192$a=fyaY4nA1IivxKBQUgzqTjhZ^z%ZHQ`=&FSLrWvWbSBI$12D^lfL=Q-;V^m z)|{eocZE~5ozvTQPKgZ7PtQoTWZ3G6NPGyo;(u!UE4$8+-J9yG-ia$#FTa|2EmKzA zlKbG~qCc$`Qs=H}b8MV{ML*`+Wy6kFHlOtP(<2r?oYCg|q4DG8X{E=zZ?T(wX4<~t zCFkxH&UW8_Pp-DSBi?=Z#ZB|Jxf36s(|Nz^x9tKpkKctkVzTC^dD)LX-obZ#<9_o~ zZ7cQ`L_fHfarU^uPUcr#ugxC+dicu!^;4aHbb3hzukKsiHmh{HvzDLOyDOnj zXG~8s4sFxsDm=xV-*w>zv+LL0Udj7BfAhcmf9dlosk+}8b>C-wU-dJ+`>1{6H1lIt zkN>JP?&j*cyLP8l{jEp)Pb`*V-#y(e%hqhV{M+hh+s%&Ti`s5Wua@`MOaFVu?DO$+ zjB|u&A2hkQAm@+E`IrWdVE)8sk2qynZz$i7jOIFewXw>I;jG_@iW!G{ zvNb%f&A9VA?qj%jGyCi`_G5>+HgpIrIeGqY`L)%&tEQ#yy~UtX9eSE+!p$(lUb}uL zb!LZ~3j%Z=ww|_qkmsvD(Rrf&lT)80H};=D!I^S1Q?*?rg->(B)i<|-w(2zMUns8J zcc=1HYhqs-`;9}(AG_puYB4dzUw9p7xH)ge1&^Hp_bYENzq;?wRVUl&{wGXz=B+s9 zJZI+&w?A^H*0aYaY?-V6#8!OSyqRAt=bkvMt;!r?DjX(lx23EnazRf{d)VS*_4Zdz ylyTHF7V$_v`1437D>1?M!;wcT-pl-G%GdtCH9$Y+ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_underside.png b/src/main/resources/assets/ebwizardry/textures/blocks/birch_lectern_underside.png new file mode 100644 index 0000000000000000000000000000000000000000..e8ae2c11e877b80fee27ad24b6a7ebba6daca2c0 GIT binary patch literal 1593 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-mQ;mAlmsP~D-;yvr)B1( zDwI?fq$;FVWTr7NRNPuSEqm4)4W73D>7tuiggB-@RXFwe3}4#M&xIE^d9B{%KmS^g zL4wdHwV*A3zvnNlFW|6t_H+Jnvby=O>U5RG!e=dt_HQoyYbSr^Ugf0Ah6)0aTgq(Q z?4L?MY5HmOH1*ZV39mf^Z#_Hk$!5ZGrOwL-@(QIk+3?I*Ipb4EN@`Tu<4VcXMs`OF z;+$rFel7Dp@qp{wXE_4v=QSU`+sS!Q{mWtrae?hyEu*@Ze4D2ieDSpL+hA6^lW8}l z?$mz1(5L;l=&#%@nK^6E@359`U%o&!h3Qvj|Lfl+X+XcTH zY89XKe+xVJYr2&c)C@gr(xof zk`?Rb#;D#%372Nvtstwjr0BDrF0;ZutKBYetMNM;3qa5vI+Nv)CTQPmw>FYMs&D*;OWD&|a$L z(D9)0kmNRn1e@s8^J*Od32q9?45>TxuAOAkS)-*rMk%gJ`xjG$AJZpJ3*d*&J{K6e${y~9;}e~YSK=1JETHy(9~#XEJX zPTg}VEBwmGSJyWjGhJKxn0MutoxbM#TXpj?Pm8V$%PkFEy?)`bSu373-#Sud`M*KR zKkJg`*3O3u*fKeGbLGt!&OPJcBg0&g!E$4gM{=5ywR&$%a@wQ?S|JaXm|19>Cn>xz z6I)xep?X5_rhnx-1RYM~PgvacPWs8ZJMUJk-I-sqO*kv{MpKr~s(epB$=y{&9hs~C z9(27tYa8Rds4CGvTWW(?GVZF`F|>c3e8&6C56g+EZx1~>9&vkqX2}-+gZCtlr@Z;= zJo)L4=${MN*Iz#UTh*sGb^4miZawlPvlnX2idNP))7!UfbN`o5`}kuQFG?soxncdg z%fEy_)H_sNU)1*O^m*kL#k;#0`+rH<{#kw^`D9tu`+TK81^X<055LY%(VJ)+v+E>} zvB=!R`IQ!}wFTv;dN<}Q;fcK{>v!{E+Lw9H5-izD)f{w;rB=*L{>UGzE_*k9*YS^< zbMq!#UG^eY_Ko-L+x-{ytUjNx6)Uj_-L0})S@`-wWB1Rq7SHf5{?zVdV9si2DU-5h zvFyo>O|uK+vUg6Xe6aZWjr-59{LZR=A`)x#uf5an{Kop(!R!5Oi*)#wdsxgh{_Ea( z@6E;VGyAj3@02{6YxiNV)t9E3eyTri#VMBr*Y38fihDWzWbMiwdza?Fk$S)S?Ubx9 zy)nCQzFjAu^y|0nx3G&Rf2l3LZT2@L)QvUw*1mtnm)?b3|KiQ}_WqkqJk6^OfApLD zUh%|sRpQLnkbt|__x2wb|1`_dKBHafY3D@qSoajBe=8o&4_@@2k;DE>(fr@t77Ppw zY)RhkE)4%caKYZ?lNlHoI14-?iy0X7ltGxWVyS%@0|NtliKnkC`x6#+QAPuvN?vOQ z1_sFz*N775{M_8syb=cIqSVBa)D(sC%#sWRcTeAd@J2py1_p+Yo-U3d7N_S1pXO?E z5V1YAfa4$sbJJhP`B&4w@ap|)-lp4Br<@_X_7*?Kp@=Cjj)@tSJy%Rx_wH-Y_j$!O z!ZKaC@vFK1+t`REY+ZFAxY45Xg4tb;W1cJwkFC41*Gv)0;o4GdHA64ym%-l)6PDDa zPJ4b%puKV0liOilnF#?79+DYiyOy0>}yCJX<1jFE=h{S~?}~&6N+*D>w2n^QL&(WzI48D7*D6D3c{~(x$0S@#ljZ z6E?>^thw{`>+`)Y)b8aA9sMU_z_24mKB8)A{Q1Un;(SwA{NL^T#+ZSDfx*+&&t;uc GLK6T-vGEK5 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_lectern_base.png b/src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_lectern_base.png new file mode 100644 index 0000000000000000000000000000000000000000..f0e125ed0b174cd88d44ff575a8ff37feb310944 GIT binary patch literal 369 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^OD)tY%>NXqEAh zfq{Xuz$3Dlfk96hgc&QA+LtjfFtC?+`ns||VU`zX5Ut9eZ_L2JAX(xXQR1ARo12;HZf51i%l z^QHt}Z2j>Q2M!!Kz$9Gr_oI08A`||@)AfJwK75#%yyO2D?m6e1bbdZPt>4CBt=D#P zw!J|}>Iq{Gtq+HudTFl%bI!#5li4k9vb5q~_ovN<0-xA?&VEggmync@keG2nG$i%J z<12UWE^Jn8OJvxzxs%aniALT32ga#vHFduP%dHg51tK@FCf)97|G-fF|AF!0=gZ|K z)$;!xnDAQrz=lT`%2=k Qfq{X+)78&qol`;+07~|aQvd(} literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_lectern_side.png b/src/main/resources/assets/ebwizardry/textures/blocks/dark_oak_lectern_side.png new file mode 100644 index 0000000000000000000000000000000000000000..4c040558dbf0855015cd02969205e637fcc3c7a1 GIT binary patch literal 1605 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-PF00OlmsP~D-;yvr)B1( zDwI?fq$;FVWTr7NRNPt{9$ofEgXhn6tu)SQ4ouSyAD&}uJ>#iAelKdly~d@@9mfEceMDP$nKbS#w^C}r;6m<9c#7EPcyf_{rGrU*}E?#yc{8Xm)6d| z$oH3TZ^AyAUsY=+FT6gx^->+rl;4dtIw~Ix3ZiUyEaTX=B`2Ob?&QaJy6&%DcJ%C} z5_#JL`hWhFn4fpRdGSZRj+KQUGWO;uMmX7@ddB?c8ry|9)!y%0WVmhrEGu5cF!@d2 z-L`9NPTS}896ujs*gn@eKE~noorkp>RJX8xx%~3_`t|E4T;}|{;7{MB)yMU}nEPBV zd13iLPGXaH-KzAz7cT2AoxEN8blUW%*0zUK?!5h(l54Yf`;FiG%BJs4JYAOa>dvV* ziE)zmX5~x1YjEFgF~{~@i?x!f)}s}QC%E37>SQ%3`*z2o_<8NAQ=$TU-v}wVuCiFi zlFB0Kxk~!UufpQ#4<=>nHNEI6D7bK{wRQPTle3|H26rrk7PW7bcsw=!?w4~*?zuY| z=70PoT5$4&$pVQphL?}cU~}6c?|1d(gU@w)*w*v$yecU^!uC1r7}tXR8ucvGXS-PY zgq)aqK<3s)h8PwWE`v7*4y3)A(z{*3sZp}8M?r&mmxVs-mJO>+8bsdDX_QG}%y~Tb zK=G;-I;LET9RcYrU%C@CnswfbeiW{8;Ylrh6?)Yq8>>BuS+e6%x0v?ABa^(=Ib98n z-x|ehdM%`Q@-wMVJ0iJtw_W*k&e|#S`|fw!+HK!uo|j#CE%tlt?ynb;{jYpJdducn>GkdAOoE@xXDoKSD}BcH^Eb(C-Sf@oeB0cl_?GAK-Mk)r zKY7}W}2fTTFgmqbUe3ii{kgkQ#!TB3 zT~p(@V^Uhp@g6Pb8=Tgi({)){dGOknnd^3F|LCz~?vqpgz^|&@`)pCj#Dki8$=haU zMAlfX+@=;Tvvm5pbuTm-mv54pa`wf-`+;Sr_D(qaPEYTC)Nhp=r5Afny{b4GxLQN1 za&Plv55Km0b4a#w&LY#kf7aEb7j`@T;-s`Gr^Ct-0wG zk-c+~?90kmc4iLd|9(x~{lf2K+wOITz8p}pz4RkE;)~4(sj!HT`Qe$*6VlZ#ou2Xk zu)y-^hX0kG%;%Gl`Rshjp=$P)`BOW}RrUrerL}srZ+yYeQ-01UYTuFLu6MNGod?Q!xPmQ>LXI~!rS6TZ%=#uzX8PDx!H22L}-%)4PbDiVvyhHcr zosMUb+{-g>&GUsHe>p#R8B=}i%a+FV8^8MT-naW=|7Z8d%Y6Qyd>I%R*pj^6T^Rm@ z;DWu&Co?cGa29w(7BevDDT6R$#Zvn+1_lQ95>H=O_9rZ?JkoMEYgR=wFfd4#xJHyX z=jZ08=9Mrw7o{eaq^2m8XO?6rxO@5rgg5euGcYh*_jGX#u{gao_#j`C0Z;8xUKT~g zi4`9o+@1g7|Ng^^k4W@59C&CmW2U8qN6%iJ?D*T)xA#4`q1Rt3^zpZzPr%+bgN18E z`<}Pu)HDbRt8wfvP>cL3wK(GX#g*)*qI#RGze@bb;MsA1*{w7NhS1ymFRI;dq$qY} zw{HqtaZ4^(><80AmJ?x3DoHt;3Mv_+FP!`la$)(O>pahPofk8({t(V)pZ!2L!9IRg z+kuslVGc6_rpUH%PQQKk^n>IRhwIk*PWnCZ%*%Bp3F@=`(#pbJ5}kw$%PRkfJ?CT) Xjd=0$RQ(wS1_lOCS3j3^P6NXqEAh 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 AUH||9 delta 42 ycmbO&(8n@CS%85p$=lt9L6X6Ufn#s`cZbOiZ1S8;vfO5J&%We5+?>I-nGpc%&kTqF diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/jungle_lectern_base.png b/src/main/resources/assets/ebwizardry/textures/blocks/jungle_lectern_base.png new file mode 100644 index 0000000000000000000000000000000000000000..a4f7fde5fc9823c27abb720cb8b3a9e6d6be345d GIT binary patch literal 1763 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-K2?Q8lmsP~D-;yvr)B1( zDwI?fq$;FVWTr7NRNOin9yv`}ljqNKkzy8=jUA6S?7MD0;~jqDGed3nM-6BkVX zyXxTy>nA+5cAu)=q@4IWcj1-uE5vUy^!s+ z&p;&aum960_D`P8yL9yS+~)#$^XyM9KGSd~Ry_N&+~SuKwd?hEH>kF6UXiVt&9c{M z@zy%g3rczUrEJyPT9#>acG+ZkY-$SA6l^}`*e8`LbtOE0CDWqEeL|bh#6+%;;t)+y zS*I}7!905S0S{J3$qSJ(;uoCm6*GI3DKS1-!1X|Sf9IBvo1ev! zm01MinkI7OO-h;YbdLK!m*poGzBy|BzV^e#)&6$uoSP3hbtJx9Vd+(J@wh^o1RtCj>HN6tC zb=fRw^UM{G#iDX<1)ZL26|{2qyXqGkv;A%Pm)x50uuVSk$U^0r6Z6-|M(y8f9V}1NqaW3W%f+xoOQc%{+vd`GL}P+QyPvK_@7%dsn}-b>^V1&tl&D&@KW8fS1mc= zLTQryi(_}$i|={-u-+pnaIn7F(s*TcZqS!&AZ(udU1!Xxpq|L%5#f<7u!4Cj=FnYFv~Fa=J!r#t7%N_dlVbu?EBQM9o`rj z8Ey0Xo3ui*e(h|PmZcpBlq)iK%dyUWd!zA?;Kv>2{-R61HaETcAi8Jm_IVR-#)q8c zK3Dfn`TF7Ik7PFAo3mq8NNvw+m*vSGLATu9uIxKzaq5h6Sl!k6OT4=Hm%U^&eATsf z*EQLfb}8rfu)M7+ynabUvrFmTX`V^13e=ZoC*{<+N$to)iTBPOXqev8l0Ft z%kccW>Ur}Po9Q2@JN9d>`TGrjc;;7HuTf7}%J*hb=^P7|nqPb8G@Xr~xWz-EzBS0Y zW76SUDk&#!<}Wj@jrh7wevx6x-keJQ;`8^q8E&-kemnMj$!FdT&p2;wKQK!@<33Zi z+~Qh&&$Id0uEjC?JihQHDDlM-$6WpEwffRW5*!u_x2JvhR%C1IoON&I^7Orx%jeB| zX#D=lWbgT>pL9j({uVj;BlB)r(cBs5PCne~elop%oku|R8}W!gv(`U*`${T(>yK3v zg^U02|HzR3nOe9rSMNc?eQOiD><_Y^k{&)joTzW?)jeNq`-}f`55K6MG;254+L9o% zGw&Cf=gzce`)qG!@_~0|f!<&C1$&oIW?*38Ebxdd zW?;}$24TjErS@eE3=Hfgp1!W^PgvMRS;f!F*M=}KFi4iTMwB?`=jNv7l`uFLr6!i7 zrYMwWmSiZnd-?{1H}Z)yFfjUhx;TbdoIX3r*86aPNZb4`r*EmK@rro`Z`5l#(iFX5 z_JxIA3Ojak>?-*dZm~;LeXeY5*2Hs54=z=@%j$VjWYWS|ua(V9zBGq)ck9RO_-^~$ z=6TUO(Qn&LKNSfT*7o{THHT<97OFQd%XlOAuCT1mSx0Nav_~F6O}|zj-qI3f+P@@c z(kZPDOHo(fpj9?OtDj9QZ7p$p_GFRojFyDY1+_NYcA4#;nE4{+-Zj>Sh=i!v*^7e& zua{33%kDZ>{JOTN&xbwszN*yw#M0a+YwaS9?IIh^?<<(yUwBoL`9e-bgSl|FnT?P0 zoR3PM8;V?c_x^hsns_jpp~G@&s7R#h&ikMLu}N6`JKwPUcYlC`!Gopi1Jk4@oYaXYNa;Cr#a{-BAAjP?l;M#0Ms7e83Dzj1BId|_;_6Jxe)Ve$7* d$Npas*V`4-9JBl34F(1V22WQ%mvv4FO#so}K>q*$ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/jungle_lectern_side.png b/src/main/resources/assets/ebwizardry/textures/blocks/jungle_lectern_side.png new file mode 100644 index 0000000000000000000000000000000000000000..44fb95211d548aeffc867c4c098f66431971cef5 GIT binary patch literal 1864 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-gR4R!N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHU}kKFV`gXhn4kzy7V#c7WdrgiK*C{sV_=(?}De{Z=+Mov=< z^lCDCyT5+-Pj>Om+mB^-*j45i?tCJY&T(;__Ic|_^Y_0G&O6k6FGgQN%TfQe;jgv% z7mVLjzCJCd?!3O7dFi%znST9GavhK7J(yGIud+pLLv_OB?+%Bm)3(p+->UOEnCH8< z#_fIYzhCD4F|)8(=*H^h4+_e2oIh|DrMzbP6UKfa&Z77ImmAEswzDr!Y3!UmA>z(o z2aVU$KNf$MYva8&fwg?%Tdoa9kFlM2`Q`QV>**)Xa@Gd_b9kwGk^PtTva=;Gemc#Q z_!Ry>%)jdL>}oId{Bq;!`m=vl@E=wzzkc%8kFRmb?{nAcS@)~{-n*h$Gq~xl;U3fd zpE4Njw-#DdZ)p*mD8yPABACJ+wa|q}%Gz?(l$F7=YZo$g^hr0nr|Id0@Jta*QPESF z>R=*O$~S3=7t2l7hYA-ywYEOnlIa(&k@U#xqCyo%^TnPC@g|>T7vFPt61-XQlWRvs zic|p4vyL9s8I6LkZm+bGt=aZG`-4_O!1CG3Vl&T1C@jWRYI!%W(KnzSlrzNrZCPu`jIipW2&~pCgG0??2!#SKl(1nJax*u*NH%qq1Yc!FGWp55?IU7hQYp z+9Z;@EDMD~Vth{=7YX*VOup1J(RklEgA+d=IP;aic-UWXL|Jy{2F;+Di;uiDHZGZ@ z9lr9!<5$@$HZGebZQ2r4>^nPu;n8VP?>M&V-7S5)uU)R%$TogWZdCj&{)okArTmw* zCGLo5Hft&7kb3IEo}SPn+xWpiLc#D%v$6C^OSv<==W-qe{*zEBoe-R9z{VD#e9Y{Vznvqrp%kvhWiZ}~Tin9b+Af5+u7nwo6+qGM^}dRDb(@B0o{ zCYh{b@V{G;^Vt5d@W%DIjMc|Jxi@>5hx^prxSX$NS*c%|y)6BVer)mAWA_xcvaSq% z8MUh=d+)DTmW8J+3S&;3iC22dcaiN!hI>XRkkC z|CeYn<9Wr8%LNAEHZ7(}2Rh#Iu+Pc3HIsAuuc*}XH)^h&T{?UD#Oi7JmL=bxt~*wk zGruhS*~6K;jdJ&`JHA%`3ggnHrl+#!@=3?pP3y2-ApI`;!aBVrWx8J?ey;NkvcAOJ zW4=^&!rAFF_V1i3@qO;rwR?Crw%(EB*t>Mu&V#>2e>;4soLaF`%Kx1AZPuKEBNw;6 zXFDkWL^wWfLQBx+*(OVa?A+4p8lDB0uzk2y<>IwGySwggzm5LpK%09HjP@Tc{5)x; z1xMDc*5_Ih+e7wo>2Y2A!_1$&oIW?*38EbxddW?;}$ z24TjErS@eE3=Hfgp1!W^PgvMRnN?J#?moi6z#v)T8d2h$pPQSSSHj?2l$uzQnxasi zS(2gP?&%v4-pD7;z`z*j>Eak-aeC_Hy8;%n(PjR? z|9Vr0Nc#0hxfVODzH-SNIcqH-9(cu7L)OS+RqG-_Z6WdBlectMP3wRE*K*JEzvsjc zwntgMm)d2vr+bBkpScMy4IGjy1CUzW;Ffo?c<+e0~O-Lhp zsZ3`DkMnhbz{r!*!rZ*go5JV1Z}Jq{DWX4Dr}dBwzxZ{zzYAHskEwStE=jv_&z5n) z%@tKP0$v3r&lEnjtlL|(W4~XfCG+`sIsO)*H*3x+aeE4-b)Eh7^9aj+uNCt5S=9b- zXFSP^qC(%$He15>~0?A}$@S)b~b9DKRGq-4*}ZK>5;eoxm}l=MPs gYtcQ~Bl{SYrWO6@_s*Kfz`(%Z>FVdQ&MBb@01v=x5dZ)H literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/jungle_lectern_top.png b/src/main/resources/assets/ebwizardry/textures/blocks/jungle_lectern_top.png new file mode 100644 index 0000000000000000000000000000000000000000..0683e9c752ff407fd00dc727f29bd79ee24ebf2a GIT binary patch literal 1954 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-d#XYrN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHU}&z|;q1>c|ZT4|gf3J#|qR-U_iP^Z4D{jTTo%a`xZ-R953 z7AX`uNnzRlzxxya9Xi@~qoPwYgF|e3`1&~)JL$Y{7k2%f zbx88PMEZ%(lIz|5kL|G0xKyWh>a&YomxV0z^rOiqE}A9uCr$2GI(~f9{r9Q*r`dcz z7IJ<)(Qmvz;lTMb?d21jS-Rx>&zl`v@Irl?9n*F*S=p(jb?Q$wetpT)3vBbhBWu~6 zK0|KT-_t*Se(ecn`~HUA=IF<7A~_p33fAr|`1|kIx#HrTbsF`HzP!6Q`y>0ZvrJ!R z70j35D*ye|;^gril}k!KeoL@yx&7(#j%l1H4wr5;t54oy;&y-aX}g4z4|QHeoqFZC z&S%TDaK`c@{yDP}&PNxjFL7~|F=dH780*>T_*lWe*NFGk>a`&c7d=1sVB$>eqP0>y zY7-X5IBGR!+%d7RDm|+@Ay$x&Z*k3Ur_*cnil++kn0v8ZbPjOScKUZXw@%6An2(0i z?7TG(#LpMBh)=duO6E*TXgS6hDt6%d-=DMgSQ%(=KWkx4i(bZdA-=_~>9p=5Sr*o} zehFP~H5gnzI6UC7G)Q2!Tb#ylDs^F!@}CTw7Y7qELmlFIHT=Z&w68)BE6WHkFH)C8<#9n4L7?Q8o#tn zDtlE_@#AMwpLVp0MQ7bATHY7%YM(d1?YApipEm|b=lxJr?ymDo~KlvJzMpp>`jfPe8ugv_kSrL zo!#B^L8Y9*IVC0M`Hju}_kaB1m1R>awSL(@`CH!SuwGr^nDdHr-b~ZE7QD@>fAjfE zsVoOS3N4cl{AFgcjrR=871<@zcRq6b`}x+LYvNWh|5{!%uPMLMwe!Q?+JD7!uJ`<8 zox^

kH=h@(-(zemPiqFsF|3`Qcmqh5wlDt5=*A-d8SnPB-_}se-qfg8a5@=iAY7)~U%=d67%u*SmW zjYH1$&oI zW?*38EbxddW?;}$24TjErS@eE3=Hfgp1!W^PgvMR*-VXt@{1W57$i$vBTAg}b8}Pk zN*J7rQWHy3QxwWGOEMJPJ$(bh8~MZ;7#NE@T^vI!PS2gZ-;<){-l@)w)eu>@L zE|~DkR8?FA59J0~hII3m%Sb$*TcEzYnfLnKs;CL? zKkYkz`tx7A`_IEy)RykN;5#?A&n7^iBsg7vkndkh_XB%uW7n&eyz~_1kpbi>JM*A z??@84J&*B6eqayNf|JEuuAO1oP7(?er+FMnez9!dvln+)s~l8jv2U8#Z1%YHf58L= zjq1>oW~>i$xtg-}Y;iH{GA;E`4ZbmJr_aK>GC^z8FFvuHe`%#9w8S3S=y z@ZRUBH$&e$ZrVJSowk}?x2q=1mtV5f_@IKl6oOa3up&+@(JDz|2{Ox4%t=RYz^C!qD*D2diTsHB--^P6c zMaRD`o%7b&FMUb#?US2|+n=gi9{Z>HY;Kf&)ar#>bKR!*IG@O4-P&&CHe-7AwqwzJ zyN@S^zBu`=`iR^GA=c86z!b^qhbNeNdU?bB?o>!$Kg6mhb(2NLbxFrFCjqtt9YGC~ z8KtbvKU%$7#hf5MrMWGHFTP?%@8Ot-42P#OFRYR{RS|OH|BaX0lcQeq9$j;2qV^Bf zy_X-jsvr01GURpHv3AL=i4SV`=gnQsW@>#&Mj#<*XEwvXoS%U)RSnl>WE9TjJ+n`b z)kDx`LqVGeqGg79-y-hHW4Fo*Ra)@i7#0 zYS?gV;>fcv9I6mdx_^NuZLfGveo=v)$_glcE4O4 z*@bfo4z>#2+*?iirU8Y#e$yGc!r|{_X zfF#e*RqI|nezj`-!eg_{?geD%*tDK^VzKYg|1mI7i`;b?^}Mr zV^?1`P1{*T%h^_b-_7;zbm!YM4n920JKnQIO!i1$#w4vgHzs}Aq6OM(4tU;v81n9j z!-X83Z66Q5b37*`cGmt-FN4!R7Ry&J_Bp&e_f~Z4tYhtd{7!p$tAnPxeSg?2G>N6= zPf6~6Z$6qY!C2CO}^Q+Deu&we_FCun~sSDaIcEr z@Mx27uxK4ekLRmqIoI#son?fsd+Gx4t@`Yp@XWHY--<;LdP<@x3MFQ?94s^@v< zY?g6+kI=m(4ZE)O$Hd2H%c&Xt%I$L^rqH2L}V zFLy3iAE{m^aLfBi-;I+VO}^^KABjBdobB~!-LwmQmpfN2D!VN)?bC(X&sRsZKH5}y z>fl0OW7{n?OrJMyJEl{m)ph^NQMre!4=v7qI_FSzOX-snzH^^FjM#L!y6aiib3^O8 ztX_WIqC;N}6fb!><%aTlcf}pO>t^(&|KU3PTWv|S_&njdx6S-d{o_~t*KVtxY8cxo zml}SiDQhGILzSyzqO27Fpe1q?;jNcgd$whs6{FbM&_jgCU zTzOBw_lMS<{o8)LPj&b2{nvc-_m6ddzbMyklH0a3Eq!@%M&9hN;y%uX=cNCf|GBB| z*D~cFxyJSLT>da~ov!%)gd) zT=bBEfq^Z_+ueoXKL{?^yL>VO0|RG)M`SSrgPt-7Ggd6MFJoX}U@!6Xb!C6T!Y<0j z{O855R}2gck|nMYCC>S|xv6<249-QVi6yBi3gww484B*6z5(HleBulY44*t*978Nl z&kZ)@YjO~fInnwkNl)>il}Jzpgsz(^)SktJQI4%7ukZQy5pR%5&6YX2`f7cqhqomNQRdoWt%Wpv`>L!@SRCg4 z*f7iNSi`qNwg+SvNk_6>TeE7_Lg!YMv%fbQ-@otC`_^!SgU({d$2SyqW=eafs(Um~ znJZGXmrr!h)0(!wWtTw%+z*mjU+&Lv;9<$ZE82f+_A)ZaTl`z$vQxGxj)8%J N!PC{xWt~$(697D}`{n=u literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/oak_lectern_base.png b/src/main/resources/assets/ebwizardry/textures/blocks/oak_lectern_base.png new file mode 100644 index 0000000000000000000000000000000000000000..a7b2109ee45b256f82142dd40fcc0b229ddf794a GIT binary patch literal 1844 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s--K#<(N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsEj3kDR2e$@BNQNHL3wK*!?^ZeMtx{rDX5W>f6btlhin)r^f4 z9X*`9)JCCQewKa+sgT!MfMZK$f z?z!t^`{nT&ed7m)pP$A)QD|Ir{PyNvLzOR6*YhJpI z9JTi0y<2A_xBp(@r3NMILc#o)=->rMkGXxhe01IY`1_f8daLA5yjR%!?ZwF-yqD4> zzZgA~6X3f3?R4R!{=#DJmxtcE6>)^0EPp(4nPc8M?yS#}i(g9AzSsNRpxeHA<>|06 z{*u(F?6q7wl;t*B_1eB~i53(Rt-JCdg|&RC3y%bQW!99;fcV^{T%xw+Y<*{97*kCp zLK-ISU`k-Rup_geoYPMjeso`GFW$+zg?ZtE zyNo^?W!Ve_O$0QY`8aMk_-P!FoZ+j|q$tXey5iiqVn!X6L%d#_%JmpzTo_Z5eV4eN zI#eyuDd5n-&GvA)g9fwCd)9x#dk?j5viPVZq|)UzNhW57pqLYn@#1GM7WE4`_XH+) z-CPm8ev#wiRcdEDCp#aT+EU>&E9jJ@ld(nmnsW*#b`}&K71vyNWRdFBn42NtwNs_C z)=T^|^xmGbNf9u(Pxv_QmJocE}9S_@k3?5yWc;-a-6}~m|-BC}S|7T&&Rcfjv=?We=ta$r7QPvNBdt95tMXCZ(kE|T z-MAg{I_l=`12(_>KBRt#)DBy&o0xV=?vG%2>5k=5ceg*YJa}Tt_L%7Q?RVdI_C?hO zzFlV8&#b>yPyX{_pW73E-MrQDZMH0r<+XO%?zhR=Hw)J}+|glpo|t6#tI_Lt?H;o^ zHq#y!UeMCJy74J<$v?g?)lW~kZQ=g3dDdI|txD2Y6kji1ent7wqZ&?A`A;7=AE>te zoU#4(j8ktU0v4Iv*Z*wIut*_k^*c@jZo{A58mk`8Tf8%I?rE{x&RJ5a$2nG6uKLTn z-M!Z@`_1G|{(t8!?!8W*x2}Gjy!>vhPkeoi**3F#Qg%ux+y7s-bl#n~Wd#Qder)Y> z^0v-ceBe&)@ojXw-y;G*AZCiQQ;meLi%s-dSdzS5b|N7;cwJ&~G z&2oQc|JCQS+$Z&yiz9;hbH8`bjmz5i{g`%seZ%qnLEA2sze(#a&blSZSnB&)p51=O z%)`=m;!dBPfBWpkn8><xH6bWPtFfrygYc;A@?ey{mlP1PL1t&HgnA_ z;V{N}y=Pk!q%6Gi<(FRhy1FOx&2BB*lMB*z+r6n?#MJ)qM;6=8+_Ss6|H=1!SZ?Oy z{h{$c!@)kKr~Eu2YZ(|A*pj^6T^Rm@;DWu&Co?cGa29w(7BevDDT6R$#Zvn+1_lQ9 z5>H=O_9ratB9=-o=dRk%z`!6`;u=xnoS&PUnpeW$T$GwvlA5AWo>`Ki;O^-g5Z=fq z&cMLv>FMGaVsZNHr1M@(fgoM-qlj>dKnj3ZI*v%_pQ6UeQT`qXNki2!( z_{Ju24xuGxdBTSj3vRD-IDSB^%Su{&YvvLAm7cTf3Lf6OZ~5JNdU5}j-ID3q&h8x? z8&@<5B$jO#RtfF>eA;@7$Ao|YrDdm9WX)iIKDW^GsMD+$Q8F9XEndRI=XZ)hggbM~ z6hEECg(bg^*m(YX_UzS<&0*y~dvi`dd9z4|v885nSh?k$`wQ>B*WEKG#Ppqfyxa2w z6&vopSFGO?xO(@CB6j1$89(opG+roa&2na2@TlTJh499%$BEI$TEu6tJ=v7>?(^r$ z;$yQJH8h(;MIuxyFTDA~`(W#?(~L_m{fYb_z2}2?QKaM1k8ZruCe{^e^{%#^iK{wM zAgHi1Ys>G1XtsB&-%Cv}*dT21=iE=hgy?5ST6CqKzP_FQuYDhb!L)Fnp#SNg85kHC NJYD@<);T3K0RWwkZS4R6 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/oak_lectern_side.png b/src/main/resources/assets/ebwizardry/textures/blocks/oak_lectern_side.png new file mode 100644 index 0000000000000000000000000000000000000000..414690ab8a3f3543f17a0d907af2852cf6743d5c GIT binary patch literal 1743 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s-g{wj$N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHU}kDl~egXeF$=w=p`j{>I;+N?@r-~7kYW{$<&JMT)qZ`Ecy z%_Li*;^_1H{r$Xu2amp4Q)rjvp%@XPxATak?AF((K6_p}`@C*r`g6y;Jb4KvNA1_4 zho_mJko>jhY3ljOir=Sq2HlqLJI-I}e`FW$9I@?+3sVog>-4=R%$a{WZ?AlIq?tgd&{^%71d>c5IP^~UkN z#JyRIncj8G-)=R>_FYT#L?PC~5Wy7ocTSxWCS^q-n!(G>Zac9G$eOeDpNVluH5AY~ z;Su9t(3o-Oilx^iEv{YMha452N{h`(yKEBfk+@@$lnZ~N(V+?Pcb~~Fz31*E7`gW+ z*N%!5sQ{j59X+Zm8U4Yb2wvj-LA-u2XguinP1r3+->$&yU66)U#4ns zbeDQVcf$c@9!`l3jYonNq-Xl7G%1U+q&lP*)-;HHny2lsNtjcCJ(A(JgmC7nP0v-` z6j=mM*eVO&Q8}q_+Oq#gr=Otn&F=DhzaK0PpI^f#_ zH&fQ%ewTLqL*T9y+g+bo_*LCE8<(BB`pe{th4t*$B8sb>XEY1z=vj+KZ=13!ZrA3s zw=KDrexJQ|#wqUP>6eUzO7=~e@p`)& zm#_^}`?jxYbdHPnJc-Hoh8S-4kkW4 z!&>amc9Q$mM6cSAI(6x@-!E@|X%^wK;nQ0|lMntSwTg%qRfqIp^Gw8t~@(ta@XD!&j=JvCG550N2 zo*(i2{rTXL7(dSS{_Od3d}qX;R!Zk+eY&~xiRPyGIZNlAH{bm#W$(PTXO>EF@-?3P z!*86Y@cPXs&u?k)`fL2$v|<&zm07&r?&B8wRq^pruEv0|xx83O|Ydx@v7 zEBg}`b`c8`mLR?L3=9mCC9V-A&iT2ysd*&~&PAz-C8;S2<(VZJ3hti10pX2&;tULo z5}q!OAr_~TQyiED^Q8W>{@n4fMo-VIE&qO7xR1A`4eZfY166mB%;_*7^Na?XntoeOp}ED%5BQz5xS<)`G1rK}QnlFdMB4bJ3$ zRK3S)D6AJK!e+rNX!*;fvcl86p}V{H;g#0(fc!lNGa0-t&AI>RxBNm*Q{{shUsTSv xtoeCrt+_^>JHHFaTaI>lZ5-yk{|yWoc55Vjed%cs!oa}5;OXk;vd$@?2>=&DB|iWF literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/oak_lectern_top.png b/src/main/resources/assets/ebwizardry/textures/blocks/oak_lectern_top.png new file mode 100644 index 0000000000000000000000000000000000000000..ad128f79de0ad0f8efd0d4d565ad28eb4957dac6 GIT binary patch literal 2001 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/spruce_lectern_base.png b/src/main/resources/assets/ebwizardry/textures/blocks/spruce_lectern_base.png new file mode 100644 index 0000000000000000000000000000000000000000..2ab6af8e17e46ad825129c63e5495a4aae9b610d GIT binary patch literal 1721 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/spruce_lectern_top.png b/src/main/resources/assets/ebwizardry/textures/blocks/spruce_lectern_top.png new file mode 100644 index 0000000000000000000000000000000000000000..6e3c7f1c3f2dfee07f497d0862e5f603bf407a4b GIT binary patch literal 1947 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 V&o}p*{@t{qOlxyk3QUKlAxa zgzCywMGu=eSeRa3tN*w6?*1PWA2oN+@?NEB`{#&Mc1DfH`M*2g|Lc7I|7YiW&3*q% z?zHv`H##5MeP?~<-s|Rludnm|ls)-2q+E1Pb>`>n;z9iqUs=xYg?Hj+Ax|oUoe@N8X8D2A>%Zd%?|0-sh|NU8;Y-Se{?^ zyYybWypCa;C`UO*B8%m5u7~Rr1uFa>RQ{CR6f)C~tAZy$n6Lf()*r_Cwx4<9XH-_L zD6#aW!#{Ptye$GU&r(M1Jf>n^MBGM`&J?>$3H$IhuoD;OKqD+A~Mne2Sxp73!d zyWXknoeP^A!g(+6<~38|Eb!W}bEo?6;@k@b{K~ov3n!^C`%YzxFx2{Ua^r!8%NAW+ znRRK6>*{SPnrgn9PgA6>ZhA7qbord(lcw91RK;{xJ_+*DUE8MRG2>du=G9BD-IAJY z)|PkY?v6!U<#v6!v32>p>X%!$GuFDvSY0}?xX0=iPq5lc`^#tYrbVVl*Euc~oxX0% zC#}`%wmrNxO?LacEuZyvzx&pCtNYdY6+iP&{I$&b=XzG4`}wXvty1m9|0E90p0gs6 zElIIT;CvfH3t!vK;F_L;6O7Nylk(u=UGVJcftmLNm=Er#x$xG0<8P;T{)NR-|Aq1% z_D?8gyZfKnGX28d6MwsI=LgJw8zN%K5GfYrBY%@)cE)_pgP+gea{TtC`s|+I*~R^5 zb`*cxH2KQLYx5TN&%1tQuf_GnMNcPBXRZFSVYQL$r!B?OLf2lLjW$S{tNm_{b@Nj{ zj-ppq+qSJ+siinK+hy8mtBY?`=B)IdQIeYGyH=p`nt1xOyYd^KuH6)#mL0n-_PA8F z`?lL>bXNLJ_?)*zYN_7b+~xa@-zi=ylg@BivS`xs>ev>`b!NHGmaFC6eKT$CnOmG) z>8CG$)ywWkXJ3;u#cuoh$}-8jdCqsU#cx^{bbJ*$bSloFwO=*={x*TPLbFzHHTZn( z(kCh1-?1g{ZWZUP-f~em`+aE|zcj<+la(7E8MgA41;oadmdf4)n*57LsK5r=P zS+~F)RN3b)hUd3=w@mrXqVWEm(>zo4->-5L=a$S+?R+n`d6ubfUUZ7i*X+&32i^&~ z=_*!Pehho?{#;&YY=%*TW8BtK4#TK7n;Oe+o=-Y`)WN`JfrLqX=lYvFH1=}NyyRV$ zsLi49nb~%RnT_+A<-K=%FJ$V>u<}?eAT6b^b>(|Uu9cBdFP7%4GMFEe`Rv-87xDG% z>#mjyMqH5TXz9;4&3u_GbU2c2W~1V4>(&ExVr9CFpX+{EEu6POBX*r?Q1uSh!waPM zC>+p#UweI7S(RQ<0hj08jn&p=Z`}HLZ}G&Md9=!23^{E*$&|s!RIlvAgo_WXrP6BT zxMpsS7w!ttyT@$hU-C3?L8XJ=?J3V5Yk6(Z-MS^{&qrmBI6hfB?ul`CW=d@dOEvlL zkWz6;fx~HI=i!@4q3|G(eulmGE8FK_wT&{v|SDldk2qNKMn+ATYC%h}yx z;=j$C8|`jvE0cO9qL#BZ{pX7o``Z-;pG$3*?9z;ENUWYN@!jEB?%&#p<_0d4)^=W8 z(sb(%?=r(f`P*2kxh$$GOqsHM%9Ydwl%18O+YrB89&77*BqcL4AvS8g=w?tMM z;m(^smuAkZ*tGnrEYrV&tv(BVul#eUO3$xa9G&@9D(3KoExXMQ-A_}oUcPpc$!62U z2WQmQCcRAOy1y-2r8`7+YhHG0M7_GfvAC?)Cztw09`2kwF~Catb&tz`{u+&p?JulF zW?#9L@$CRd=RF^GKIKmLle-O9&OW^|GtPwL^qTo`0_t07x2^8Ptec(tm2)`Huen~WySe!A8D^(cZ6lLv9*wRYN9Gw?q}$%zKgX@- z?Nxh&PHvHZijFiC0NBj-|9*WEZF@yB>)cS!T3V;Nezei|mkn~O(Y?KAfm zG&X)?z!ZAmPBQZW=6GG7Oa43Uwm^w1?wV$1DZkAbH zVhg(+`Oq%@gX~G=2S*Ogu?cbvoPWJi;zwG|4eq6E?G?{^1`tOEj*YnGkc8uR|7oHSR=kzZQzF^F-cva#pmZBF6@1H-j+eEbGP|9wd*9Iq? z>l^0&nES5oX;z)m=ZX!7PbtUE_3PZS=B49b-@daSj3kVD%5FCKyq5VY?zm*nwApbc zSDt>AVR=?x{XKfK_tr;z7iyf3of6v0nHv)PyUL=6Z9`|}g|k0oq6&f*-e0FWtwy-@ zmi?MWPF=Z8uczwss^8Bj>~wTmF^Q)tL2Ukt+IvMp(`T%D*<&hTG*|Ob;!4SP%O{jJ zXlG9PEU>V@>Q`UlqqHD?pKFZ*fm`b?{hAaO!Tha)e{~UCrh@R=^ShoNd?7Ylp(Nqc zmz!;Z_x3G#oZ-J?Tl13S8<*zpbTgFZ3Ef}ui`lpT-oKB_f@j?9+H75wo)~QDJMG4z zkI%MclnADt;Cvry=pxFP**U584zIBIDtkxiDKkSl0+Zw~yA(X>&-j?hk-N>7H=&k$ z`~FPJ7fLI79*eB%l8eyG)n?&f){c4>pLuG{UhQXZ^f$Qj+j_fBmM*nR%#Y6R-1~>+ zy6fvQ_x(+-{xf^eUEK3v-!>lBWU=5bFYAn}4H`PxFZKF^?1D0U)8=MQ<}i)CJW;pw zqQSCQh26HHcT1Kmm~niC@vGM&=Nw$TCiXiXpMBP;cuQRR|6LurkIfGE+d3M&i;yrl zuJWr?&}+AKjQNc>nvSOD`!lr;m-EEW-g7xam`mt~QEucpg_1VQrqjpP&bPji+O94W zq#JqG@|n+c|I}^QY9r0(I21_e3q(l8FWqmVxX@{?oV9%E4%@Yl7H+D$cW-{iuPy8B zBH1P{$;zCUz|iOQ!u8>f_Lic{@w?X_{p))4hqG#7cAV{zvm51G@gj$&Hm3hk1l)6%lOc$Zr8qVP9ef)*V@|(MCP~NzUCChzo`AIx2ntk z07k>OlPN846*!T(F7~PrmFjah3bySu2w48|xUiu49h+U} z*ms`a!FKH6l;mR?G7DVe3~52`)58?r_~y+`t0{kqx>vD*V} zFGy`jzx>7ftWERThDND#lWttu6TS9OYSuo}=l-h^Ly`*7(jbG7YLoB~C;es;NgnVhmXSTk?p%i32D4GQajU%b)( z{@r}O5A~8KZh3x>KP2J0+Sehio`HdZEy>&6h2cL4F4((#G6MqxXMsm#F$061G6*wP zEVVCVU|?V`@$_|Nf5O5pETX+tC9aTxf!W5>#W5t~-rMQ^9wLPz$L)W!N0-NxnjQ@d z;}P2$9K!N&;pD~uSu2r>)xj)^5+$5V4m$}rIT2&ip)C?t z8#~|aI`>X(-R;EL@%wMa9=5#Ev-SSv_upr~pFQ(??fbLUkE`tcGC!#F`1L3xJDkHKV!wv z8>g@T-j{j8jMn4p*L}+U%)R~G5zjS;58nuwn$Wg)<2TXfEoR%8CY)Oysi|YB$Gqp$ zu>!dTo*#|nc1EQZJdS+JGeOoo!6JW6IG48t?>n#Osp{|iZ62L^>8{q?yiZPQ?>*VY z`|m&ad--UTR=+2UXimx>yvMneAhGa{(F7zZ?L8Q&QQ_2wIE0Tyr_}YY|-Dh9@N`&Fz)Gf z_1di{Z*QGAGycjluXk;6h1>S-^iYddxN%Sa8-v`}Cf!3jk|sZvu{4VaV%op>Z>pl^ zwf0}X87^$LWUxD<8WZ*-k8j5EgC%VVD|&0TWmvtptUD>jxQF4^^YzOPU)j~SY8&H+ zpLdsQDQo``KK$Hg;yD(ZHMOxvuis*F2;bf`M{yg6fSpxB*DQDMsj^qYjQc zC*8eUE)k*dvrsaA^}9Jj6JA@^lrWg|OiQ12=}JMzm6$_9_t$+(%M{quwaDzKUi<97 zf35cjpUTbMm$jqN$@jk~V;MA rj-TGK2X-Fzl`Xvz=q|hai2w57GpD0BJ(FQzU|{fc^>bP0l+XkKn9!iR literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/gui/arcane_workbench.png b/src/main/resources/assets/ebwizardry/textures/gui/arcane_workbench.png index 786a491b95d8b0de62fd33bb51933362fa83f6a3..252ba0a658ed994512ee5df55ad4829e34b26d8e 100644 GIT binary patch delta 11921 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%0D1PQ*ug?lM$EFJ7 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 b3s(N)zp~a$$ET@85+v^F>gTe~DWM4f1cy4l delta 11972 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%0Da< VYCXRA+MisIxTmY1%Q~loCID2ZU?Bhi diff --git a/src/main/resources/assets/ebwizardry/textures/gui/lectern.png b/src/main/resources/assets/ebwizardry/textures/gui/lectern.png new file mode 100644 index 0000000000000000000000000000000000000000..a6b253c2939165bdda399d39ce0448caef1a4e7e GIT binary patch literal 43369 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 Nr=G5UF6*2UngF2QIn4k7 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_book_advanced.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_book_advanced.png index 72b51efd3f6c6b001ba0a9ca5af6aaed55721f59..1bc9af056afa0cb9d56db1c4a6d09cb3b1285f6d 100644 GIT binary patch literal 11179 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 X2fyPtT_j~V1>{ptS3j3^P6n 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 N#M9N!Wt~$(69A-1`MCf9 diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_book_master.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_book_master.png index 6a6396e11c97dad57d0775af6c6847b39915fc01..7266a64eea4ba58e102bb008f2597ca7aed0bd97 100644 GIT binary patch literal 11748 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 L)z4*}Q$iB}3ZS@| diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_book_novice.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_book_novice.png index 19a78f8f0fe877aba80bd1dd0a8f8d8b3950de30..206999144918d643ca9f0001ec28d4cc40443401 100644 GIT binary patch literal 10720 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 diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_sort_buttons.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_sort_buttons.png new file mode 100644 index 0000000000000000000000000000000000000000..9ed4d33735dc0de0b2c8767280196e7d1d815d83 GIT binary patch literal 723 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}Y)RhkE)4%caKYZ?lNlHo zI14-?iy0WWg+Z8+Vb&Z81_lQ95>H=O_9rZ?0@{r8_)dE=Ffd4#xJHyX=jZ08=9Mrw z7o{eaq^2m8XO?6rxO@5rgg5euGcYh&d%8G=L>zuQ%|7F{gUB&^R{pK*)B9R^V!gJq zWI6jBbT&Mxl*`NF!{PDZs^3YG%K|;^Q}m8*IVY9X%G1avDOKYv^vJ1MT6)sKZm(%P zjeV+eKDIlXEf(6BFIRpQ^eW#+~#jmQXvhnpd(%G)Q zUf;09IPW^&&S!yld71M15SnMvsbRWcQk&uSvv+1r zyWHS)Yo>-x0^9VbPND|Sw$*kwc}@tDU*g{I?7~co-*5kPx;Kn000O z+zj7?8J*rb;koSxs!8&aSykd` zT~Fdi>mNLoaesDwN%`XKg?91YSbF2hQjdzrvhT$^x2HOH z{VL-wJ=t$s-^#ds=3L>Y513avzrV4njm2tHY1fXeFIT)s{V!2fXBIu>p>Vn4#utab z1iZ6;To%rMG)Y{2fq3WkcnhpX~Sdw0=4Cw)d$(-_!CxkNP{C=iL0zd-0LP-+QrTjM04A!PVO0tUHYsm#*c? z=(>H|qS-dusavCy%R+k6ngyX=M;ZzzEOwj8_BDG=$iqdC&vi}QxhcRj!L4E94yF*M z3mGO6s-BZOSXOwjGfwZ6C4djG3kXd4BVCHdHpsCb26}pGTe?>T++Ge`Rv|i0f!1s zH<24ECmBv#_WubKpXhwEyZqko2Up$a*YF9YwYDxuoVOyQi%*~LM>wZbCx)u?9ws*G4 zl|pXc=$w^L{bW~OiT!R{@^<6$dF(;CCkhVt82{Q^EwlUm1an9q;J4eo_xrJ7=O|EOYoT0?V6082LnXioh z$)1A?e?}J=Gc?t+7o|CU=X|p7=-cWQTWj{dc;b{X?IF)9(Gd9-BlkZaj{T`H`8K(! zUgWR7=*cxRyQkbSzgiouvfb_6<5c&+=N;!+a(!m^q~H27$ILt@@YkUXJ<+Y(*Y=)X zL{wasJ!;+ip{gRw%Ba4msn}5VVdr5!Db)#{bR#uxlu=$?egS+=jvyV!@K3zBSy4e@^i}jv@ z`w~8Uzxsz;K4eeK?#PdS7#JAXlDyqr82*Fcg1yTpGcYi47I;J!Gca%qgD@k*tT_@4 z3=Hfgp1!W^PgvN++0?dV{5s6Qz#v)T8d2h$pPQSSSHj?2l$uzQnxasiS(2gP?&%v4 z-pD7;z`&^F>Eak-aeC?Gja|(SBCh$@M8w#(mi#Vh`{PvS!10qoTRKsC@Q4>(z5y2+bD5jxw%%Tmq8w>{^Y)oN=6epO?#RFT{V)5!g zy}f)ppIhzU!XLGJ_T1f{63NC85GuNBopYeblvK{sn@i_xab$1}JYc|c?z!7%IWZxE>5)`xk zdMd-xFAvXe)Y;7=;3~LCqv`REmB&BZusAln|IOcQ^0LClFU;M%{V?O~vux{LA1a)4 zU)Ez1OXM`c91D8?^jm! a%qIjAQ&p3%r!z1xFnGH9xvX Date: Thu, 9 Apr 2020 20:08:28 +0100 Subject: [PATCH 019/415] Make spell books return to the slots they came from (provided the container isn't closed) --- .../inventory/ContainerArcaneWorkbench.java | 81 +++++++++++++++++-- .../wizardry/inventory/VirtualSlot.java | 20 ++++- .../wizardry/util/WizardryUtilities.java | 17 ++++ 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index 123fa1c0..4377e1eb 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -11,6 +11,7 @@ import electroblob.wizardry.spell.Spell; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import electroblob.wizardry.util.ISpellSortable; import electroblob.wizardry.util.WandHelper; +import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.inventory.ClickType; @@ -230,8 +231,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl // Workbench -> inventory/bookshelves if(clickedSlotId <= UPGRADE_SLOT){ // Try to move the stack into the bookshelves. If this fails... - if(getBookshelfSlots().isEmpty() || !this.mergeItemStack(stack, getBookshelfSlots().get(0).slotNumber, - getBookshelfSlots().get(getBookshelfSlots().size()-1).slotNumber + 1, false)){ + if(!mergeStackIntoBookshelves(stack)){ // ...try to move the stack into the player's inventory. If this fails... if(!this.mergeItemStack(stack, UPGRADE_SLOT + 1, UPGRADE_SLOT + 1 + PLAYER_INVENTORY_SIZE, true)){ return ItemStack.EMPTY; // ...nothing else happens. @@ -259,8 +259,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl // Try to move the stack into the workbench. If this fails... if(slotRange == null || !this.mergeItemStack(stack, slotRange[0], slotRange[1] + 1, false)){ // ...try to move the stack into the bookshelves. If this fails... - if(getBookshelfSlots().isEmpty() || !this.mergeItemStack(stack, getBookshelfSlots().get(0).slotNumber, - getBookshelfSlots().get(getBookshelfSlots().size()-1).slotNumber + 1, false)){ + if(!mergeStackIntoBookshelves(stack)){ return ItemStack.EMPTY; // ...nothing else happens. } } @@ -328,7 +327,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl ItemStack stack = player.inventory.getItemStack(); if(!stack.isEmpty() && !getBookshelfSlots().isEmpty()){ - mergeItemStack(stack, getBookshelfSlots().get(0).slotNumber, getBookshelfSlots().get(getBookshelfSlots().size() - 1).slotNumber + 1, false); + mergeStackIntoBookshelves(stack); return stack; } } @@ -336,6 +335,78 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl return super.slotClick(slotId, dragType, clickTypeIn, player); } + /** + * Tries to merge the given stack into the bookshelf slots, accounting for their previous contents. The stack will + * be merged into slots in the following order of priority: + *

+ * 1. Slots that currently contain a matching item
+ * 2. Empty slots that previously contained a matching item
+ * 3. Empty slots that did not previously contain any items
+ * 4. Empty slots that previously contained a non-matching item + * @param stack The {@link ItemStack} to be merged; will be reduced by the number of items that fitted into the + * bookshelf slots + * @return True if the entire stack was merged into the bookshelf slots, false if not (equivalent to calling + * {@link ItemStack#isEmpty()} on the input stack after this method returns) + */ + private boolean mergeStackIntoBookshelves(ItemStack stack){ + + // LinkedHashSet preserves iteration order whilst ignoring duplicates - neat! + Set slots = new LinkedHashSet<>(bookshelfSlots.size()); + + // Add all slots that currently contain a matching stack + slots.addAll(bookshelfSlots.stream().filter(s -> WizardryUtilities.canMerge(stack, s.getStack())).collect(Collectors.toSet())); + // Then add all empty slots that previously contained a matching stack, if they weren't already added + // No need to actually check if they're empty since inserting a new stack overwrites prevStack anyway + slots.addAll(bookshelfSlots.stream().filter(s -> WizardryUtilities.canMerge(stack, s.getPrevStack())).collect(Collectors.toSet())); + // Then add all slots that did not previously contain anything + slots.addAll(bookshelfSlots.stream().filter(s -> s.getPrevStack().isEmpty()).collect(Collectors.toSet())); + // Finally add all other empty slots (these will be the ones that used to contain something else) + slots.addAll(bookshelfSlots.stream().filter(s -> !s.getHasStack()).collect(Collectors.toSet())); + + slots.removeIf(s -> !s.isItemValid(stack)); // Should never be true, but just in case... + + // Now we have a set of slots in order of priority to try merging into + // We already know the stack will fit, so it's simply a question of distributing it + // This is waaay neater and more flexible than vanilla's (disgusting) mergeItemStack implementation - and that + // only has two priorities of slot to deal with! + + for(Slot slot : slots){ + + ItemStack contents = slot.getStack(); + + if(contents.isEmpty()){ + + // Not sure why mergeItemStack differentiates between full/partial merging, as far as I can tell the + // following line will work for both cases + slot.putStack(stack.splitStack(contents.getMaxStackSize())); + slot.onSlotChanged(); + + if(stack.isEmpty()) return true; // The whole stack has been merged, so we're done! + + }else{ + + int totalItemCount = contents.getCount() + stack.getCount(); + int maxSize = Math.min(slot.getSlotStackLimit(), stack.getMaxStackSize()); + + if(totalItemCount <= maxSize){ + + stack.setCount(0); + contents.setCount(totalItemCount); + slot.onSlotChanged(); + return true; // The whole stack has been merged, so we're done! + + }else if(contents.getCount() < maxSize){ + + stack.shrink(maxSize - contents.getCount()); + contents.setCount(maxSize); + slot.onSlotChanged(); + } + } + } + + return false; // If we get this far, it didn't all fit so return false + } + /** * Called (via {@link electroblob.wizardry.packet.PacketControlInput PacketControlInput}) when the apply button in * the arcane workbench GUI is pressed. diff --git a/src/main/java/electroblob/wizardry/inventory/VirtualSlot.java b/src/main/java/electroblob/wizardry/inventory/VirtualSlot.java index cf806246..619a77b4 100644 --- a/src/main/java/electroblob/wizardry/inventory/VirtualSlot.java +++ b/src/main/java/electroblob/wizardry/inventory/VirtualSlot.java @@ -20,11 +20,14 @@ import net.minecraft.tileentity.TileEntity; public class VirtualSlot extends Slot { private final TileEntity tileEntity; + /** Allows the virtual slot to remember what was last stored in it, so items can be put back in the same place. */ + private ItemStack prevStack; // For now this doesn't persist over GUI close public VirtualSlot(IInventory inventory, int index){ super(inventory, index, -999, -999); if(!(inventory instanceof TileEntity)) throw new IllegalArgumentException("Inventory must be a tile entity!"); this.tileEntity = (TileEntity)inventory; + this.prevStack = getStack().copy(); // We MUST copy the stack or it will get changed from elsewhere later! } @Override @@ -59,15 +62,28 @@ public class VirtualSlot extends Slot { return isValid() ? super.onTake(player, stack) : ItemStack.EMPTY; } + @Override + public void onSlotChanged(){ + super.onSlotChanged(); + if(this.getHasStack()) this.prevStack = this.getStack().copy(); // Ignore stack removal (insertion of empty stacks) + } + @Override public ItemStack getStack(){ return isValid() ? super.getStack() : ItemStack.EMPTY; } + /** Returns the stack that was last in this slot. */ + public ItemStack getPrevStack(){ + return prevStack; + } + @Override public void putStack(ItemStack stack){ - if(isValid() && inventory instanceof TileEntityBookshelf) ((TileEntityBookshelf)inventory).sync(); - if(isValid()) super.putStack(stack); + if(isValid()){ + if(inventory instanceof TileEntityBookshelf) ((TileEntityBookshelf)inventory).sync(); + super.putStack(stack); + } } @Override diff --git a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java index 6e019a5e..05224957 100644 --- a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java +++ b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java @@ -39,6 +39,7 @@ import javax.annotation.Nullable; import java.util.*; import java.util.function.BiPredicate; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * "Where do you put random but useful bits and pieces? {@code WizardryUtilities} of course - the 'stuff that doesn't @@ -810,6 +811,22 @@ public final class WizardryUtilities { return copy; } + /** + * Returns whether the two given item stacks can be merged, i.e. if they both contain the same (stackable) item, + * metadata and NBT. Importantly, the number of items in each stack need not be the same. No actual merging is + * performed by this method; the input stacks will not be modified. + * @param stack1 The first stack to be tested for mergeability + * @param stack2 The second stack to be tested for mergeability (order does not matter) + * @return True if the two stacks can be merged, false if not + */ + public static boolean canMerge(ItemStack stack1, ItemStack stack2){ + return !stack1.isEmpty() && !stack2.isEmpty() + && stack1.isStackable() && stack2.isStackable() + && stack1.getItem() == stack2.getItem() + && (!stack1.getHasSubtypes() || stack1.getMetadata() == stack2.getMetadata()) + && ItemStack.areItemStackTagsEqual(stack1, stack2); + } + /** * Checks if the given player is opped on the given server. If the server is a singleplayer or LAN server, this * means they have cheats enabled. From 1833ed9c56acb9a797f26e6bbdda79dd50d8c25e Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 20:09:13 +0100 Subject: [PATCH 020/415] Tweak block highlight scale to remove the tiny gap due to the anti-z-fighting offset --- .../wizardry/client/particle/ParticleBlockHighlight.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleBlockHighlight.java b/src/main/java/electroblob/wizardry/client/particle/ParticleBlockHighlight.java index 8188fd45..e1674398 100644 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleBlockHighlight.java +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleBlockHighlight.java @@ -1,6 +1,7 @@ package electroblob.wizardry.client.particle; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; @@ -22,7 +23,8 @@ public class ParticleBlockHighlight extends ParticleWizardry { this.particleGravity = 0; this.setMaxAge(160); - this.particleScale = 5; + // 5 makes it the exact size of a block face, plus a little bit to account for the anti-z-fighting offset + this.particleScale = 5 * (1 + 2 * (float)WizardryUtilities.ANTI_Z_FIGHTING_OFFSET); this.shaded = false; } From f973099fd39fbe525984cf8ce05493818c3d1b09 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 21:07:06 +0100 Subject: [PATCH 021/415] Add sound for locating spells using the lectern --- .../java/electroblob/wizardry/client/gui/GuiLectern.java | 6 ++++++ .../java/electroblob/wizardry/registry/WizardrySounds.java | 2 ++ src/main/resources/assets/ebwizardry/sounds.json | 1 + 3 files changed, 9 insertions(+) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java b/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java index 6c69b6f2..ee6ff187 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java @@ -28,6 +28,7 @@ import net.minecraft.network.play.client.CPacketCloseWindow; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundCategory; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -275,7 +276,9 @@ public class GuiLectern extends GuiSpellInfo implements ISpellSortable { Spell spell = Spell.byMetadata(stack.getMetadata()); if(spell == this.currentSpell){ + BlockPos pos = (((TileEntity)bookshelf).getPos()); + for(EnumFacing side : EnumFacing.VALUES){ ParticleBuilder.create(ParticleBuilder.Type.BLOCK_HIGHLIGHT).pos( WizardryUtilities.getFaceCentre(pos, side) @@ -283,6 +286,9 @@ public class GuiLectern extends GuiSpellInfo implements ISpellSortable { .scale(WizardryUtilities.ANTI_Z_FIGHTING_OFFSET))) .face(side).clr(0.9f, 0.5f, 0.8f).fade(0.7f, 0, 1).spawn(mc.world); } + + mc.world.playSound(pos, WizardrySounds.BLOCK_LECTERN_LOCATE_SPELL, SoundCategory.BLOCKS, 1, 0.7f, false); + break; // This bookshelf has the spell, skip to the next bookshelf } } diff --git a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java index f5a9b447..95c01466 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java +++ b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java @@ -28,6 +28,7 @@ public final class WizardrySounds { public static final SoundEvent BLOCK_ARCANE_WORKBENCH_SPELLBIND = createSound("block.arcane_workbench.bind_spell"); public static final SoundEvent BLOCK_PEDESTAL_ACTIVATE = createSound("block.pedestal.activate"); public static final SoundEvent BLOCK_PEDESTAL_CONQUER = createSound("block.pedestal.conquer"); + public static final SoundEvent BLOCK_LECTERN_LOCATE_SPELL = createSound("block.lectern.locate_spell"); public static final SoundEvent ITEM_WAND_SWITCH_SPELL = createSound("item.wand.switch_spell"); public static final SoundEvent ITEM_WAND_LEVELUP = createSound("item.wand.levelup"); @@ -154,6 +155,7 @@ public final class WizardrySounds { event.getRegistry().register(BLOCK_ARCANE_WORKBENCH_SPELLBIND); event.getRegistry().register(BLOCK_PEDESTAL_ACTIVATE); event.getRegistry().register(BLOCK_PEDESTAL_CONQUER); + event.getRegistry().register(BLOCK_LECTERN_LOCATE_SPELL); event.getRegistry().register(ITEM_WAND_SWITCH_SPELL); event.getRegistry().register(ITEM_WAND_LEVELUP); diff --git a/src/main/resources/assets/ebwizardry/sounds.json b/src/main/resources/assets/ebwizardry/sounds.json index 75efd3a7..975ef6c1 100644 --- a/src/main/resources/assets/ebwizardry/sounds.json +++ b/src/main/resources/assets/ebwizardry/sounds.json @@ -2,6 +2,7 @@ "block.arcane_workbench.bind_spell": {"category": "blocks", "sounds": ["ebwizardry:spellbind"]}, "block.pedestal.activate": {"category": "blocks", "sounds": ["mob/evocation_illager/prepare_summon"]}, "block.pedestal.conquer": {"category": "blocks", "sounds": ["ui/toast/challenge_complete"]}, + "block.lectern.locate_spell": {"category": "blocks", "sounds": ["ebwizardry:conjure"]}, "item.wand.switch_spell": {"category": "player", "sounds": ["ebwizardry:select"]}, "item.wand.levelup": {"category": "player", "sounds": ["random/levelup"]}, From 6cabe7ffd473957dc571316615189d16bca4c510 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 21:07:32 +0100 Subject: [PATCH 022/415] Make sure the slot order is retained! --- .../wizardry/inventory/ContainerArcaneWorkbench.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index 4377e1eb..e64a8ee1 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -353,15 +353,15 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl // LinkedHashSet preserves iteration order whilst ignoring duplicates - neat! Set slots = new LinkedHashSet<>(bookshelfSlots.size()); - // Add all slots that currently contain a matching stack - slots.addAll(bookshelfSlots.stream().filter(s -> WizardryUtilities.canMerge(stack, s.getStack())).collect(Collectors.toSet())); + // Add all slots that currently contain a matching stack - collect to list to retain order + slots.addAll(bookshelfSlots.stream().filter(s -> WizardryUtilities.canMerge(stack, s.getStack())).collect(Collectors.toList())); // Then add all empty slots that previously contained a matching stack, if they weren't already added // No need to actually check if they're empty since inserting a new stack overwrites prevStack anyway - slots.addAll(bookshelfSlots.stream().filter(s -> WizardryUtilities.canMerge(stack, s.getPrevStack())).collect(Collectors.toSet())); + slots.addAll(bookshelfSlots.stream().filter(s -> WizardryUtilities.canMerge(stack, s.getPrevStack())).collect(Collectors.toList())); // Then add all slots that did not previously contain anything - slots.addAll(bookshelfSlots.stream().filter(s -> s.getPrevStack().isEmpty()).collect(Collectors.toSet())); + slots.addAll(bookshelfSlots.stream().filter(s -> s.getPrevStack().isEmpty()).collect(Collectors.toList())); // Finally add all other empty slots (these will be the ones that used to contain something else) - slots.addAll(bookshelfSlots.stream().filter(s -> !s.getHasStack()).collect(Collectors.toSet())); + slots.addAll(bookshelfSlots.stream().filter(s -> !s.getHasStack()).collect(Collectors.toList())); slots.removeIf(s -> !s.isItemValid(stack)); // Should never be true, but just in case... From f7b678e2916afbfe9391f0f18a27a2fe34742e2f Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 21:18:56 +0100 Subject: [PATCH 023/415] Reminder to fix this --- src/main/java/electroblob/wizardry/data/WizardData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/data/WizardData.java b/src/main/java/electroblob/wizardry/data/WizardData.java index 7e70b21d..46e7a7be 100644 --- a/src/main/java/electroblob/wizardry/data/WizardData.java +++ b/src/main/java/electroblob/wizardry/data/WizardData.java @@ -340,7 +340,7 @@ public class WizardData implements INBTSerializable { activeImbuements.add((Imbuement)enchantment); }else{ // Otherwise, removes the enchantment from the item - iterator.remove(); + iterator.remove(); // FIXME: Apparently this can cause a CME } } } From bd11c9c759761026e0f869f16d05f171c40de1c0 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 21:22:43 +0100 Subject: [PATCH 024/415] Cleanup --- src/main/java/electroblob/wizardry/data/WizardData.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/electroblob/wizardry/data/WizardData.java b/src/main/java/electroblob/wizardry/data/WizardData.java index 46e7a7be..a6880a63 100644 --- a/src/main/java/electroblob/wizardry/data/WizardData.java +++ b/src/main/java/electroblob/wizardry/data/WizardData.java @@ -285,14 +285,13 @@ public class WizardData implements INBTSerializable { * @throws IllegalArgumentException if the given {@link Enchantment} is not an {@link Imbuement}. */ public void setImbuementDuration(Enchantment enchantment, int duration){ - // It is best to throw an exception here, because otherwise the error would either go unnoticed (if - // non-imbuements - // were ignored) or cause a ClassCastException later (if non-imbuements were allowed to be added). + // It is best to throw an exception here, because otherwise the error would either go unnoticed (if non- + // -imbuements were ignored) or cause a ClassCastException later (if non-imbuements were allowed to be added) if(enchantment instanceof Imbuement){ this.imbuementDurations.put((Imbuement)enchantment, duration); }else{ throw new IllegalArgumentException( - "Attempted to set an imbuement duration for something that isn't an Imbuement! (This exception has been thrown now to prevent a ClassCastException from occurring later.)"); + "Attempted to set an imbuement duration for something that isn't an Imbuement!"); } } From c50b3440419bf581f48bcad17658329b4dd7bd99 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 21:28:48 +0100 Subject: [PATCH 025/415] Remove usage of deprecated server-side I18n --- src/main/java/electroblob/wizardry/Settings.java | 4 +--- src/main/java/electroblob/wizardry/spell/Spell.java | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index c70459e2..4d2ebf30 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -11,7 +11,6 @@ import net.minecraft.entity.EntityList; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.translation.I18n; import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; @@ -53,7 +52,6 @@ import java.util.regex.Pattern; */ // For the time being, I'm sticking with the old config system because @Config doesn't support custom config entry classes // @Config(modid = Wizardry.MODID) -@SuppressWarnings("deprecation") // Used server I18n deliberately; we want to write the comments in the actual config file in english. public final class Settings { // Category names @@ -447,7 +445,7 @@ public final class Settings { for(Spell spell : Spell.getAllSpells()){ property = config.get(SPELLS_CATEGORY, spell.getRegistryName().toString(), true, - I18n.translateToLocal("spell." + spell.getUnlocalisedName() + ".desc")); + "Set to false to disable this spell"); // Uses the same config key as the spell name, because - well, that's what it's called! property.setLanguageKey("spell." + spell.getUnlocalisedName()); Wizardry.proxy.setToNamedBooleanEntry(property); diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java index b93b35ff..62d31b67 100644 --- a/src/main/java/electroblob/wizardry/spell/Spell.java +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -621,8 +621,8 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C } /** - * Returns the unlocalised name of the spell, without any prefixes or suffixes, e.g. "flame_ray". This should - * only be used for translation purposes. + * Returns the unlocalised name of the spell, which is now its registry name as a string, e.g. "ebwizardry:flame_ray". + * This should only be used for translation purposes. */ public final String getUnlocalisedName(){ return unlocalisedName; From e272d4f9990e9fc4988ff05e02c65b45016b1ccd Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 21:35:37 +0100 Subject: [PATCH 026/415] Add recipes for lecterns --- .../ebwizardry/recipes/acacia_lectern.json | 23 +++++++++++++++++++ .../ebwizardry/recipes/birch_lectern.json | 23 +++++++++++++++++++ .../ebwizardry/recipes/dark_oak_lectern.json | 23 +++++++++++++++++++ .../ebwizardry/recipes/jungle_lectern.json | 23 +++++++++++++++++++ .../ebwizardry/recipes/oak_lectern.json | 23 +++++++++++++++++++ .../ebwizardry/recipes/spruce_lectern.json | 23 +++++++++++++++++++ 6 files changed, 138 insertions(+) create mode 100644 src/main/resources/assets/ebwizardry/recipes/acacia_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/birch_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/dark_oak_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/jungle_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/oak_lectern.json create mode 100644 src/main/resources/assets/ebwizardry/recipes/spruce_lectern.json diff --git a/src/main/resources/assets/ebwizardry/recipes/acacia_lectern.json b/src/main/resources/assets/ebwizardry/recipes/acacia_lectern.json new file mode 100644 index 00000000..b785dbdf --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/acacia_lectern.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "wbw", + "cwc", + "www" + ], + "key": { + "b": { + "item": "minecraft:book" + }, + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 4 + } + }, + "result": { + "item": "ebwizardry:acacia_lectern" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/birch_lectern.json b/src/main/resources/assets/ebwizardry/recipes/birch_lectern.json new file mode 100644 index 00000000..cb4f6322 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/birch_lectern.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "wbw", + "cwc", + "www" + ], + "key": { + "b": { + "item": "minecraft:book" + }, + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 2 + } + }, + "result": { + "item": "ebwizardry:birch_lectern" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/dark_oak_lectern.json b/src/main/resources/assets/ebwizardry/recipes/dark_oak_lectern.json new file mode 100644 index 00000000..4f8354d8 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/dark_oak_lectern.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "wbw", + "cwc", + "www" + ], + "key": { + "b": { + "item": "minecraft:book" + }, + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 5 + } + }, + "result": { + "item": "ebwizardry:dark_oak_lectern" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/jungle_lectern.json b/src/main/resources/assets/ebwizardry/recipes/jungle_lectern.json new file mode 100644 index 00000000..f1e85736 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/jungle_lectern.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "wbw", + "cwc", + "www" + ], + "key": { + "b": { + "item": "minecraft:book" + }, + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 3 + } + }, + "result": { + "item": "ebwizardry:jungle_lectern" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/oak_lectern.json b/src/main/resources/assets/ebwizardry/recipes/oak_lectern.json new file mode 100644 index 00000000..e791bc85 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/oak_lectern.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "wbw", + "cwc", + "www" + ], + "key": { + "b": { + "item": "minecraft:book" + }, + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 0 + } + }, + "result": { + "item": "ebwizardry:oak_lectern" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/recipes/spruce_lectern.json b/src/main/resources/assets/ebwizardry/recipes/spruce_lectern.json new file mode 100644 index 00000000..459a8c38 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/spruce_lectern.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "wbw", + "cwc", + "www" + ], + "key": { + "b": { + "item": "minecraft:book" + }, + "c": { + "item": "ebwizardry:grand_crystal" + }, + "w": { + "item": "ebwizardry:gilded_wood", + "data": 1 + } + }, + "result": { + "item": "ebwizardry:spruce_lectern" + } +} \ No newline at end of file From c41875704266c4b8646ee2f7ce7403d02f95a6a9 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 23:13:05 +0100 Subject: [PATCH 027/415] Hide bookshelf interface when there are no nearby bookshelves --- .../client/gui/GuiArcaneWorkbench.java | 127 ++++++++++-------- .../inventory/ContainerArcaneWorkbench.java | 47 +++++-- .../wizardry/inventory/SlotBookList.java | 7 +- 3 files changed, 116 insertions(+), 65 deletions(-) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index 245390eb..a551cd73 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -51,18 +51,13 @@ import java.util.Locale; @Mod.EventBusSubscriber(Side.CLIENT) public class GuiArcaneWorkbench extends GuiContainer { - private GuiButton applyBtn; public static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/arcane_workbench.png"); - private InventoryPlayer playerInventory; - private IInventory arcaneWorkbenchInventory; - private ContainerArcaneWorkbench arcaneWorkbenchContainer; - private static final int TOOLTIP_WIDTH = 144; private static final int TOOLTIP_TEXT_INSET = 6; - private static final int BOOKSHELF_UI_WIDTH = 122; + private static final int BOOKSHELF_UI_WIDTH = ContainerArcaneWorkbench.BOOKSHELF_UI_WIDTH; // For conciseness /** We report the actual size of the GUI to Minecraft when a wand is in so JEI doesn't overdraw it. * For calculations, we use the size without the tooltip, which is stored in this constant. */ @@ -87,12 +82,20 @@ public class GuiArcaneWorkbench extends GuiContainer { private static final int TEXTURE_WIDTH = 512; private static final int TEXTURE_HEIGHT = 512; - private int animationTimer = 0; private static final int ANIMATION_DURATION = 20; + private InventoryPlayer playerInventory; + private IInventory arcaneWorkbenchInventory; + private ContainerArcaneWorkbench arcaneWorkbenchContainer; + + private GuiButton applyBtn; + private GuiButton[] sortButtons = new GuiButton[3]; + private GuiTextField searchField; private boolean searchNeedsClearing; + private int animationTimer = 0; + private float scroll = 0; private boolean scrolling = false; @@ -116,9 +119,9 @@ public class GuiArcaneWorkbench extends GuiContainer { this.buttonList.clear(); this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width / 2 + 64, this.height / 2 + 3)); - this.buttonList.add(new GuiButtonSpellSort(1, this.guiLeft - 44, this.guiTop + 8, ISpellSortable.SortType.TIER, arcaneWorkbenchContainer, this)); - this.buttonList.add(new GuiButtonSpellSort(2, this.guiLeft - 31, this.guiTop + 8, ISpellSortable.SortType.ELEMENT, arcaneWorkbenchContainer, this)); - this.buttonList.add(new GuiButtonSpellSort(3, this.guiLeft - 18, this.guiTop + 8, ISpellSortable.SortType.ALPHABETICAL, arcaneWorkbenchContainer, this)); + this.buttonList.add(sortButtons[0] = new GuiButtonSpellSort(1, this.guiLeft - 44, this.guiTop + 8, ISpellSortable.SortType.TIER, arcaneWorkbenchContainer, this)); + this.buttonList.add(sortButtons[1] = new GuiButtonSpellSort(2, this.guiLeft - 31, this.guiTop + 8, ISpellSortable.SortType.ELEMENT, arcaneWorkbenchContainer, this)); + this.buttonList.add(sortButtons[2] = new GuiButtonSpellSort(3, this.guiLeft - 18, this.guiTop + 8, ISpellSortable.SortType.ALPHABETICAL, arcaneWorkbenchContainer, this)); this.searchField = new GuiTextField(0, this.fontRenderer, this.guiLeft - 113, this.guiTop + 22, 104, this.fontRenderer.FONT_HEIGHT); this.searchField.setMaxStringLength(50); @@ -164,21 +167,27 @@ public class GuiArcaneWorkbench extends GuiContainer { arcaneWorkbenchContainer.scrollTo((int)(getMaxScrollRows() * scroll + 0.5f)); } - Slot slot = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT); + Slot centreSlot = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT); - // Tests if there is a wand in the workbench and edits the positioning accordingly - if(slot.getHasStack() && slot.getStack().getItem() instanceof IWorkbenchItem - && ((IWorkbenchItem)slot.getStack().getItem()).showTooltip(slot.getStack())){ - xSize = MAIN_GUI_WIDTH + TOOLTIP_WIDTH; -// guiLeft = (this.width - this.xSize) / 2; -// this.applyBtn.x = (this.width - TOOLTIP_WIDTH) / 2 + 64; - }else{ - xSize = MAIN_GUI_WIDTH; -// guiLeft = (this.width - this.xSize) / 2; -// this.applyBtn.x = this.width / 2 + 64; + // Update the GUI dimensions based on whether there is a wand present and whether there are bookshelves + + xSize = MAIN_GUI_WIDTH; + guiLeft = (this.width - MAIN_GUI_WIDTH) / 2; + + if(centreSlot.getHasStack() && centreSlot.getStack().getItem() instanceof IWorkbenchItem + && ((IWorkbenchItem)centreSlot.getStack().getItem()).showTooltip(centreSlot.getStack())){ + xSize += TOOLTIP_WIDTH; } - this.applyBtn.enabled = slot.getHasStack(); + if(arcaneWorkbenchContainer.hasBookshelves()){ + xSize += BOOKSHELF_UI_WIDTH; + guiLeft -= BOOKSHELF_UI_WIDTH; + } + + // Show/hide the relevant gui elements + this.applyBtn.enabled = centreSlot.getHasStack(); + for(GuiButton button : this.sortButtons) button.visible = arcaneWorkbenchContainer.hasBookshelves(); + this.searchField.setVisible(arcaneWorkbenchContainer.hasBookshelves()); super.drawScreen(mouseX, mouseY, partialTicks); @@ -192,10 +201,14 @@ public class GuiArcaneWorkbench extends GuiContainer { GlStateManager.color(1, 1, 1, 1); Minecraft.getMinecraft().renderEngine.bindTexture(texture); + // Coordinates of the top left corner of the main GUI + int left = arcaneWorkbenchContainer.hasBookshelves() ? guiLeft + BOOKSHELF_UI_WIDTH : guiLeft; + int top = guiTop; + // Animation // Grey background - DrawingUtils.drawTexturedRect(guiLeft + RUNE_LEFT, guiTop + RUNE_TOP, MAIN_GUI_WIDTH + TOOLTIP_WIDTH, 0, + DrawingUtils.drawTexturedRect(left + RUNE_LEFT, top + RUNE_TOP, MAIN_GUI_WIDTH + TOOLTIP_WIDTH, 0, RUNE_WIDTH, RUNE_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); // Yellow 'halo' @@ -205,8 +218,8 @@ public class GuiArcaneWorkbench extends GuiContainer { GlStateManager.enableBlend(); GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); - int x = guiLeft + RUNE_LEFT + RUNE_WIDTH/2; - int y = guiTop + RUNE_TOP + RUNE_HEIGHT/2; + int x = left + RUNE_LEFT + RUNE_WIDTH/2; + int y = top + RUNE_TOP + RUNE_HEIGHT/2; float scale = (animationTimer + partialTicks)/ANIMATION_DURATION; scale = (float)(1 - Math.pow(1-scale, 1.4f)); // Makes it slower at the start and speed up @@ -221,11 +234,11 @@ public class GuiArcaneWorkbench extends GuiContainer { } // Main inventory - DrawingUtils.drawTexturedRect(guiLeft, guiTop, 0, 0, MAIN_GUI_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT); + DrawingUtils.drawTexturedRect(left, top, 0, 0, MAIN_GUI_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT); float opacity = (animationTimer + partialTicks)/ANIMATION_DURATION; - // Changing slots + // Spell book slots (always use guiLeft and guiTop here regardless of bookshelf UI visibility for(int i = 0; i < ContainerArcaneWorkbench.CRYSTAL_SLOT; i++){ Slot slot = this.inventorySlots.getSlot(i); @@ -265,7 +278,7 @@ public class GuiArcaneWorkbench extends GuiContainer { GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); GlStateManager.color(1, 1, 1, opacity); - DrawingUtils.drawTexturedRect(guiLeft + crystals.xPos - 8, guiTop + crystals.yPos - 8, + DrawingUtils.drawTexturedRect(left + crystals.xPos - 8, top + crystals.yPos - 8, MAIN_GUI_WIDTH + TOOLTIP_WIDTH + RUNE_WIDTH, 0, 32, 32, TEXTURE_WIDTH, TEXTURE_HEIGHT); GlStateManager.color(1, 1, 1, 1); @@ -280,7 +293,7 @@ public class GuiArcaneWorkbench extends GuiContainer { GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); GlStateManager.color(1, 1, 1, opacity); - DrawingUtils.drawTexturedRect(guiLeft + upgrades.xPos - 8, guiTop + upgrades.yPos - 8, + DrawingUtils.drawTexturedRect(left + upgrades.xPos - 8, top + upgrades.yPos - 8, MAIN_GUI_WIDTH + TOOLTIP_WIDTH + RUNE_WIDTH, 0, 32, 32, TEXTURE_WIDTH, TEXTURE_HEIGHT); GlStateManager.color(1, 1, 1, 1); @@ -290,13 +303,16 @@ public class GuiArcaneWorkbench extends GuiContainer { } // Bookshelf interface - DrawingUtils.drawTexturedRect(guiLeft - BOOKSHELF_UI_WIDTH, guiTop, 0, 256, BOOKSHELF_UI_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT); + if(arcaneWorkbenchContainer.hasBookshelves()){ - // Scroll bar - DrawingUtils.drawTexturedRect(guiLeft - BOOKSHELF_UI_WIDTH + SCROLL_BAR_LEFT, - guiTop + SCROLL_BAR_TOP + (int)(scroll * (SCROLL_BAR_HEIGHT - SCROLL_HANDLE_HEIGHT) + 0.5f), - getMaxScrollRows() > 0 ? 0 : SCROLL_BAR_WIDTH, 476, - SCROLL_BAR_WIDTH, SCROLL_HANDLE_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); + DrawingUtils.drawTexturedRect(left - BOOKSHELF_UI_WIDTH, top, 0, 256, BOOKSHELF_UI_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT); + + // Scroll bar + DrawingUtils.drawTexturedRect(left - BOOKSHELF_UI_WIDTH + SCROLL_BAR_LEFT, + top + SCROLL_BAR_TOP + (int)(scroll * (SCROLL_BAR_HEIGHT - SCROLL_HANDLE_HEIGHT) + 0.5f), + getMaxScrollRows() > 0 ? 0 : SCROLL_BAR_WIDTH, 476, + SCROLL_BAR_WIDTH, SCROLL_HANDLE_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); + } // Tooltip only drawn if there is a wand if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack()){ @@ -311,9 +327,9 @@ public class GuiArcaneWorkbench extends GuiContainer { if(((IWorkbenchItem)stack.getItem()).showTooltip(stack)){ // Tooltip box - DrawingUtils.drawTexturedRect(guiLeft + MAIN_GUI_WIDTH, guiTop, MAIN_GUI_WIDTH, 0, TOOLTIP_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT); + DrawingUtils.drawTexturedRect(left + MAIN_GUI_WIDTH, top, MAIN_GUI_WIDTH, 0, TOOLTIP_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT); - int y = guiTop + 20; + int y = top + 20; if(stack.getItem() instanceof IManaStoringItem && ((IManaStoringItem)stack.getItem()).showManaInWorkbench(this.mc.player, stack)){ y += 14; @@ -332,9 +348,9 @@ public class GuiArcaneWorkbench extends GuiContainer { progressFraction = (float)WandHelper.getProgression(stack) / Tier.values()[tier.level + 1].progression; } - DrawingUtils.drawTexturedRect(guiLeft + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, MAIN_GUI_WIDTH, ySize + PROGRESSION_BAR_HEIGHT, PROGRESSION_BAR_WIDTH, PROGRESSION_BAR_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); + DrawingUtils.drawTexturedRect(left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, MAIN_GUI_WIDTH, ySize + PROGRESSION_BAR_HEIGHT, PROGRESSION_BAR_WIDTH, PROGRESSION_BAR_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); int width = (int)(PROGRESSION_BAR_WIDTH * progressFraction); - DrawingUtils.drawTexturedRect(guiLeft + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, MAIN_GUI_WIDTH, ySize, width, PROGRESSION_BAR_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); + DrawingUtils.drawTexturedRect(left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, MAIN_GUI_WIDTH, ySize, width, PROGRESSION_BAR_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); y += 8; } @@ -358,7 +374,7 @@ public class GuiArcaneWorkbench extends GuiContainer { .bindTexture(discovered ? spell.getElement().getIcon() : Element.MAGIC.getIcon()); // Renders the little element icon - DrawingUtils.drawTexturedRect(guiLeft + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET - 1, y, 8, 8); + DrawingUtils.drawTexturedRect(left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET - 1, y, 8, 8); y += 10; } @@ -377,8 +393,8 @@ public class GuiArcaneWorkbench extends GuiContainer { if(level > 0){ ItemStack stack1 = new ItemStack(item, level); GlStateManager.enableDepth(); - this.itemRender.renderItemAndEffectIntoGUI(stack1, guiLeft + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y); - this.itemRender.renderItemOverlayIntoGUI(this.fontRenderer, stack1, guiLeft + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y, + this.itemRender.renderItemAndEffectIntoGUI(stack1, left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y); + this.itemRender.renderItemOverlayIntoGUI(this.fontRenderer, stack1, left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y, null); x += 18; GlStateManager.disableDepth(); @@ -404,12 +420,17 @@ public class GuiArcaneWorkbench extends GuiContainer { GlStateManager.color(1, 1, 1, 1); // Just in case + int left = arcaneWorkbenchContainer.hasBookshelves() ? BOOKSHELF_UI_WIDTH : 0; + this.fontRenderer .drawString(this.arcaneWorkbenchInventory.hasCustomName() ? this.arcaneWorkbenchInventory.getName() - : I18n.format(this.arcaneWorkbenchInventory.getName()), 8, 6, 4210752); + : I18n.format(this.arcaneWorkbenchInventory.getName()), left + 8, 6, 4210752); this.fontRenderer.drawString(this.playerInventory.hasCustomName() ? this.playerInventory.getName() - : I18n.format(this.playerInventory.getName()), 8, this.ySize - 96 + 2, 4210752); - this.fontRenderer.drawString(I18n.format("container." + Wizardry.MODID + ":arcane_workbench.bookshelves"), 8 - BOOKSHELF_UI_WIDTH, 6, 4210752); + : I18n.format(this.playerInventory.getName()), left + 8, this.ySize - 96 + 2, 4210752); + + if(arcaneWorkbenchContainer.hasBookshelves()){ + this.fontRenderer.drawString(I18n.format("container." + Wizardry.MODID + ":arcane_workbench.bookshelves"), 8, 6, 4210752); + } if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack()){ @@ -424,7 +445,7 @@ public class GuiArcaneWorkbench extends GuiContainer { int y = 6; - this.fontRenderer.drawStringWithShadow("\u00A7f" + stack.getDisplayName(), MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); + this.fontRenderer.drawStringWithShadow("\u00A7f" + stack.getDisplayName(), left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); if(stack.getItem() instanceof IManaStoringItem && ((IManaStoringItem)stack.getItem()).showManaInWorkbench(this.mc.player, stack)){ y += 14; @@ -432,7 +453,7 @@ public class GuiArcaneWorkbench extends GuiContainer { "\u00A77" + I18n.format("container." + Wizardry.MODID + ":arcane_workbench.mana") + " " + ((IManaStoringItem)stack.getItem()).getMana(stack) + "/" + ((IManaStoringItem)stack.getItem()).getManaCapacity(stack), - MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); + left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); } // Progression tier text @@ -442,13 +463,13 @@ public class GuiArcaneWorkbench extends GuiContainer { Tier tier = ((ItemWand)stack.getItem()).tier; - this.fontRenderer.drawStringWithShadow(tier.getDisplayNameWithFormatting(), MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); + this.fontRenderer.drawStringWithShadow(tier.getDisplayNameWithFormatting(), left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); if(tier != Tier.MASTER){ Tier nextTier = Tier.values()[tier.level + 1]; String s = TextFormatting.DARK_GRAY.toString() + nextTier.getDisplayName(); if(WandHelper.getProgression(stack) >= nextTier.progression) s = nextTier.getDisplayNameWithFormatting(); - this.fontRenderer.drawStringWithShadow(s, MAIN_GUI_WIDTH + TOOLTIP_WIDTH + this.fontRenderer.drawStringWithShadow(s, left + MAIN_GUI_WIDTH + TOOLTIP_WIDTH - TOOLTIP_TEXT_INSET - fontRenderer.getStringWidth(s), y, 0); } @@ -470,10 +491,10 @@ public class GuiArcaneWorkbench extends GuiContainer { } if(discovered){ - this.fontRenderer.drawStringWithShadow(spell.getDisplayNameWithFormatting(), MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + 10, y, 0); + this.fontRenderer.drawStringWithShadow(spell.getDisplayNameWithFormatting(), left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + 10, y, 0); }else{ this.mc.standardGalacticFontRenderer.drawStringWithShadow( - "\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.world), MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + 10, y, 0); + "\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.world), left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + 10, y, 0); } y += 10; } @@ -484,7 +505,7 @@ public class GuiArcaneWorkbench extends GuiContainer { y += 6; this.fontRenderer.drawStringWithShadow("\u00A7f" + I18n.format("container." - + Wizardry.MODID + ":arcane_workbench.upgrades"), MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); + + Wizardry.MODID + ":arcane_workbench.upgrades"), left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); int x = 0; y += 10; @@ -497,7 +518,7 @@ public class GuiArcaneWorkbench extends GuiContainer { if(level > 0){ // The javadoc for isPointInRegion is ambiguous; what it means is that the REGION is // relative to the GUI but the POINT isn't. - if(isPointInRegion(MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y, 16, 16, mouseX, mouseY)){ + if(isPointInRegion(left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y, 16, 16, mouseX, mouseY)){ ItemStack stack1 = new ItemStack(item, level); this.renderToolTip(stack1, mouseX - guiLeft, mouseY - guiTop); } diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index e64a8ee1..884ed425 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -66,9 +66,13 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl public static final int PLAYER_INVENTORY_SIZE = 36; + public static final int BOOKSHELF_UI_WIDTH = 122; + private List bookshelfSlots = new ArrayList<>(); private List activeBookshelfSlots = new ArrayList<>(); + private boolean hasBookshelves; + private int scroll = 0; private ISpellSortable.SortType sortType = ISpellSortable.SortType.TIER; private boolean sortDescending = false; @@ -111,7 +115,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl for(int y = 0; y < BOOKSHELF_SLOTS_Y; y++){ for(int x = 0; x < BOOKSHELF_SLOTS_X; x++){ int index = x + y * BOOKSHELF_SLOTS_X; - this.addSlotToContainer(new SlotBookList(tileentity, UPGRADE_SLOT + 1 + index, -114 + x * 18, 34 + y * 18, this, index)); + this.addSlotToContainer(new SlotBookList(tileentity, UPGRADE_SLOT + 1 + index, 8 + x * 18, 34 + y * 18, this, index)); } } @@ -146,6 +150,8 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl * @param index The index of the slot to hide. * @param player The player that is using this container. */ + // We're not using slot.isEnabled() because it would mean making another slot class, and we'd still need to handle + // what happens to the contents when the slot is hidden private void hideSlot(int index, EntityPlayer player){ Slot slot = this.getSlot(index); @@ -476,7 +482,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl return validSlots; } - /** Updates the active bookshelf slots with the current search term and sorting. Client-side only! */ + /** Updates the active bookshelf slots with the current search term and sorting. Client-side only! */ public void updateActiveBookshelfSlots(){ activeBookshelfSlots = bookshelfSlots.stream().filter(s -> s.isValid() && !s.getStack().isEmpty() // Slot 0 is a convenient way of testing if the item is a valid spell book @@ -501,7 +507,12 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl return activeSlots.subList(BOOKSHELF_SLOTS_X * scroll, activeSlots.size()); } - // The following operations are expensive so should not be done every tick! + /** Returns true if there are any nearby bookshelves that are accessible from this workbench, false if not. */ + public boolean hasBookshelves(){ + return hasBookshelves; + } + + // This is relatively expensive so should not be done every tick! // N.B. If we drop the requirement of it working with any container it could potentially be a lot easier since // we then always have control over the bookshelf classes @@ -510,18 +521,34 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl private void refreshBookshelfSlots(){ this.inventorySlots.removeAll(bookshelfSlots); - // TESTME: May need to do this for inventoryItemStacks (probably not though, seems like MC handles it) bookshelfSlots.clear(); - for(IInventory bookshelf : BlockBookshelf.findNearbyBookshelves(tileentity.getWorld(), tileentity.getPos(), tileentity)){ - for(int i=0; i bookshelves = BlockBookshelf.findNearbyBookshelves(tileentity.getWorld(), tileentity.getPos(), tileentity); + + if(!bookshelves.isEmpty()){ + + for(IInventory bookshelf : bookshelves){ + for(int i = 0; i < bookshelf.getSizeInventory(); i++){ + VirtualSlot slot = new VirtualSlot(bookshelf, i); // This sets the slot INDEX (for the INVENTORY) + bookshelfSlots.add(slot); + this.addSlotToContainer(slot); // This sets the slot NUMBER (for the CONTAINER) + } } + + if(tileentity.getWorld().isRemote) updateActiveBookshelfSlots(); } - if(tileentity.getWorld().isRemote) updateActiveBookshelfSlots(); + if(bookshelves.isEmpty() == hasBookshelves){ // If the bookshelf status changed + + // Move all the slots appropriately + for(Slot slot : this.inventorySlots){ + if(!(slot instanceof SlotBookList || slot instanceof VirtualSlot)){ + slot.xPos += bookshelves.isEmpty() ? -BOOKSHELF_UI_WIDTH : BOOKSHELF_UI_WIDTH; + } + } + + hasBookshelves = !bookshelves.isEmpty(); + } } diff --git a/src/main/java/electroblob/wizardry/inventory/SlotBookList.java b/src/main/java/electroblob/wizardry/inventory/SlotBookList.java index 8d7696bd..b39e712a 100644 --- a/src/main/java/electroblob/wizardry/inventory/SlotBookList.java +++ b/src/main/java/electroblob/wizardry/inventory/SlotBookList.java @@ -5,8 +5,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; -import java.util.List; - public class SlotBookList extends SlotItemClassList { private final ContainerArcaneWorkbench container; @@ -42,6 +40,11 @@ public class SlotBookList extends SlotItemClassList { return container.getBookshelfSlots().stream().anyMatch(s -> s.isItemValid(stack)); } + @Override + public boolean isEnabled(){ + return container.hasBookshelves(); + } + // Actual item interaction is delegated client-side before it ever gets here (in the GUI class) // Therefore we only need to handle empty slot clicks From 3ff6be4cb0689ef31d014262012798ad59f3d22f Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 23:18:23 +0100 Subject: [PATCH 028/415] Ignore empty slot books in the lectern GUI --- src/main/java/electroblob/wizardry/client/gui/GuiLectern.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java b/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java index ee6ff187..188329ee 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java @@ -398,7 +398,7 @@ public class GuiLectern extends GuiSpellInfo implements ISpellSortable { if(stack.getItem() instanceof ItemSpellBook){ Spell spell = Spell.byMetadata(stack.getMetadata()); - if(!availableSpells.contains(spell)) availableSpells.add(spell); + if(spell != Spells.none && !availableSpells.contains(spell)) availableSpells.add(spell); } } } From 4875d5a9706ca3ce1288dbd853b65cde4a48a27a Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 23:33:40 +0100 Subject: [PATCH 029/415] Put excess upgrades onto a second row so they don't go off the edge --- .../client/gui/GuiArcaneWorkbench.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index a551cd73..5b5f2ff3 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -391,12 +391,20 @@ public class GuiArcaneWorkbench extends GuiContainer { int level = WandHelper.getUpgradeLevel(stack, item); if(level > 0){ + ItemStack stack1 = new ItemStack(item, level); GlStateManager.enableDepth(); + this.itemRender.renderItemAndEffectIntoGUI(stack1, left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y); - this.itemRender.renderItemOverlayIntoGUI(this.fontRenderer, stack1, left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y, - null); + this.itemRender.renderItemOverlayIntoGUI(this.fontRenderer, stack1, left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y, null); + x += 18; + + if(TOOLTIP_TEXT_INSET * 2 + x + 16 > TOOLTIP_WIDTH){ + x = 0; + y += 18; + } + GlStateManager.disableDepth(); GlStateManager.disableLighting(); // Whyyyyyy? } @@ -522,7 +530,14 @@ public class GuiArcaneWorkbench extends GuiContainer { ItemStack stack1 = new ItemStack(item, level); this.renderToolTip(stack1, mouseX - guiLeft, mouseY - guiTop); } + x += 18; + + if(TOOLTIP_TEXT_INSET * 2 + x + 16 > TOOLTIP_WIDTH){ + x = 0; + y += 18; + } + } } } From 531700378fdded144a8834b4a7649df9833477ee Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 23:44:06 +0100 Subject: [PATCH 030/415] Use the correct progression bar width --- .../electroblob/wizardry/client/gui/GuiArcaneWorkbench.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index 5b5f2ff3..f54524cd 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -74,7 +74,7 @@ public class GuiArcaneWorkbench extends GuiContainer { private static final int SCROLL_BAR_HEIGHT = 178; private static final int SCROLL_HANDLE_HEIGHT = 15; - private static final int PROGRESSION_BAR_WIDTH = 152; + private static final int PROGRESSION_BAR_WIDTH = 131; private static final int PROGRESSION_BAR_HEIGHT = 3; private static final int HALO_DIAMETER = 156; From 793ba0eb05f01bb6f8b612a5c0fee02b90c77092 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 9 Apr 2020 23:47:03 +0100 Subject: [PATCH 031/415] Fix the scroll bar --- .../electroblob/wizardry/client/gui/GuiArcaneWorkbench.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index f54524cd..29445ed8 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -154,7 +154,7 @@ public class GuiArcaneWorkbench extends GuiContainer { boolean mouseHeld = Mouse.isButtonDown(0); - if(!scrolling && mouseHeld && getMaxScrollRows() > 0 && isPointInRegion(SCROLL_BAR_LEFT - BOOKSHELF_UI_WIDTH, + if(!scrolling && mouseHeld && getMaxScrollRows() > 0 && isPointInRegion(SCROLL_BAR_LEFT, SCROLL_BAR_TOP, SCROLL_BAR_WIDTH, SCROLL_BAR_HEIGHT, mouseX, mouseY)){ scrolling = true; } From a4fd88f21da024621ae28ccfe189d7908f56882b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 10 Apr 2020 00:17:01 +0100 Subject: [PATCH 032/415] Organise texture folders and misc cleanup --- .../client/WizardryClientEventHandler.java | 24 +++++------ .../client/gui/GuiArcaneWorkbench.java | 2 +- .../wizardry/client/gui/GuiBookshelf.java | 2 +- .../client/gui/GuiButtonSpellSort.java | 2 +- .../wizardry/client/gui/GuiLectern.java | 2 +- .../gui/handbook/GuiWizardHandbook.java | 2 +- .../client/renderer/RenderArcaneLock.java | 8 ++-- .../renderer/RenderArcaneWorkbench.java | 4 +- .../client/renderer/RenderBlackHole.java | 14 +++---- .../client/renderer/RenderBubble.java | 38 +++++++++--------- .../renderer/RenderContainmentField.java | 8 ++-- .../wizardry/client/renderer/RenderDecay.java | 6 +-- .../client/renderer/RenderEvilWizard.java | 7 ++-- .../client/renderer/RenderMagicLight.java | 12 +++--- .../client/renderer/RenderWizard.java | 6 +-- .../wizardry/constants/Element.java | 2 +- .../inventory/ContainerArcaneWorkbench.java | 6 +-- .../wizardry/potion/CurseEnfeeblement.java | 2 +- .../wizardry/potion/CurseUndeath.java | 2 +- .../wizardry/potion/PotionContainment.java | 2 +- .../wizardry/potion/PotionDecay.java | 2 +- .../wizardry/potion/PotionFrost.java | 2 +- .../wizardry/potion/PotionFrostStep.java | 2 +- .../wizardry/potion/PotionSlowTime.java | 2 +- .../ebwizardry/texts/handbook_en_gb.json | 18 ++++----- .../ebwizardry/texts/handbook_en_us.json | 18 ++++----- .../ebwizardry/texts/handbook_es_es.json | 18 ++++----- .../ebwizardry/texts/handbook_zh_cn.json | 18 ++++----- .../ebwizardry/texts/handbook_zh_tw.json | 18 ++++----- .../{entity => blocks}/arcane_lock_0.png | Bin .../{entity => blocks}/arcane_lock_1.png | Bin .../{entity => blocks}/arcane_lock_2.png | Bin .../{entity => blocks}/arcane_lock_3.png | Bin .../{entity => blocks}/arcane_lock_4.png | Bin .../{entity => blocks}/arcane_lock_5.png | Bin .../{entity => blocks}/arcane_lock_6.png | Bin .../{entity => blocks}/arcane_lock_7.png | Bin .../{rune.png => arcane_workbench_rune.png} | Bin .../ebwizardry/textures/entity/aura.png | Bin 1165 -> 0 bytes .../{black_hole.png => black_hole/centre.png} | Bin .../{dark_ray.png => black_hole/ray.png} | Bin .../textures/entity/{ => decay}/decay_0.png | Bin .../textures/entity/{ => decay}/decay_1.png | Bin .../textures/entity/{ => decay}/decay_2.png | Bin .../textures/entity/{ => decay}/decay_3.png | Bin .../textures/entity/{ => decay}/decay_4.png | Bin .../textures/entity/{ => decay}/decay_5.png | Bin .../textures/entity/{ => decay}/decay_6.png | Bin .../textures/entity/{ => decay}/decay_7.png | Bin .../textures/entity/{ => decay}/decay_8.png | Bin .../textures/entity/{ => decay}/decay_9.png | Bin .../entity/{dark_orb.png => entrapment.png} | Bin .../{ => evil_wizard}/evil_wizard_0.png | Bin .../{ => evil_wizard}/evil_wizard_1.png | Bin .../{ => evil_wizard}/evil_wizard_2.png | Bin .../{ => evil_wizard}/evil_wizard_3.png | Bin .../{ => evil_wizard}/evil_wizard_4.png | Bin .../{ => evil_wizard}/evil_wizard_5.png | Bin .../{light_aura.png => light/flare.png} | Bin .../entity/{light_ray.png => light/ray.png} | Bin .../textures/entity/{ => wizard}/wizard_0.png | Bin .../textures/entity/{ => wizard}/wizard_1.png | Bin .../textures/entity/{ => wizard}/wizard_2.png | Bin .../textures/entity/{ => wizard}/wizard_3.png | Bin .../textures/entity/{ => wizard}/wizard_4.png | Bin .../textures/entity/{ => wizard}/wizard_5.png | Bin .../textures/entity/{ => wizard}/wizard_7.png | Bin .../textures/entity/{ => wizard}/wizard_8.png | Bin .../entity/{ => wizard}/wizard_female_0.png | Bin .../entity/{ => wizard}/wizard_female_1.png | Bin .../entity/{ => wizard}/wizard_female_2.png | Bin .../containment_field_0.png | Bin .../containment_field_1.png | Bin .../containment_field_2.png | Bin .../containment_field_3.png | Bin .../containment_field_4.png | Bin .../containment_field_5.png | Bin .../containment_field_6.png | Bin .../containment_field_7.png | Bin .../gui/{ => container}/arcane_workbench.png | Bin .../gui/{ => container}/bookshelf.png | Bin .../{ => container}/element_icon_earth.png | Bin .../gui/{ => container}/element_icon_fire.png | Bin .../{ => container}/element_icon_healing.png | Bin .../gui/{ => container}/element_icon_ice.png | Bin .../element_icon_lightning.png | Bin .../{ => container}/element_icon_magic.png | Bin .../element_icon_necromancy.png | Bin .../{ => container}/element_icon_sorcery.png | Bin .../{ => container}/empty_slot_crystal.png | Bin .../{ => container}/empty_slot_upgrade.png | Bin .../textures/gui/{ => container}/lectern.png | Bin .../{ => container}/spell_sort_buttons.png | Bin .../textures/gui/{ => handbook}/handbook.png | Bin .../pictures}/arcane_workbench_picture.png | Bin .../pictures}/armour_picture.png | Bin .../pictures}/elements_picture.png | Bin .../pictures}/flower_picture.png | Bin .../pictures}/obelisk_picture.png | Bin .../{ => handbook/pictures}/ore_picture.png | Bin .../pictures}/shrine_picture.png | Bin .../{ => handbook/pictures}/tiers_picture.png | Bin .../{ => handbook/pictures}/tower_picture.png | Bin .../textures/{entity => gui}/pointer.png | Bin .../arcane_jammer.png} | Bin .../containment.png} | Bin .../curse_of_enfeeblement.png} | Bin .../curse_of_soulbinding.png} | Bin .../curse_of_undeath.png} | Bin .../decay.png} | Bin .../empowerment.png} | Bin .../fear.png} | Bin .../fireskin.png} | Bin .../font_of_mana.png} | Bin .../frost.png} | Bin .../frost_step.png} | Bin .../ice_shroud.png} | Bin .../mind_control.png} | Bin .../mind_trick.png} | Bin .../muffle.png} | Bin .../paralysis.png} | Bin .../sixth_sense.png} | Bin .../slow_time.png} | Bin .../static_aura.png} | Bin .../transience.png} | Bin .../ward.png} | Bin .../sixth_sense_marker.png} | Bin .../{entity => gui}/target_pointer.png | Bin 128 files changed, 125 insertions(+), 124 deletions(-) rename src/main/resources/assets/ebwizardry/textures/{entity => blocks}/arcane_lock_0.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => blocks}/arcane_lock_1.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => blocks}/arcane_lock_2.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => blocks}/arcane_lock_3.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => blocks}/arcane_lock_4.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => blocks}/arcane_lock_5.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => blocks}/arcane_lock_6.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => blocks}/arcane_lock_7.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{rune.png => arcane_workbench_rune.png} (100%) delete mode 100644 src/main/resources/assets/ebwizardry/textures/entity/aura.png rename src/main/resources/assets/ebwizardry/textures/entity/{black_hole.png => black_hole/centre.png} (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{dark_ray.png => black_hole/ray.png} (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => decay}/decay_0.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => decay}/decay_1.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => decay}/decay_2.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => decay}/decay_3.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => decay}/decay_4.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => decay}/decay_5.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => decay}/decay_6.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => decay}/decay_7.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => decay}/decay_8.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => decay}/decay_9.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{dark_orb.png => entrapment.png} (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => evil_wizard}/evil_wizard_0.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => evil_wizard}/evil_wizard_1.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => evil_wizard}/evil_wizard_2.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => evil_wizard}/evil_wizard_3.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => evil_wizard}/evil_wizard_4.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => evil_wizard}/evil_wizard_5.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{light_aura.png => light/flare.png} (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{light_ray.png => light/ray.png} (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_0.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_1.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_2.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_3.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_4.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_5.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_7.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_8.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_female_0.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_female_1.png (100%) rename src/main/resources/assets/ebwizardry/textures/entity/{ => wizard}/wizard_female_2.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => environment}/containment_field_0.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => environment}/containment_field_1.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => environment}/containment_field_2.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => environment}/containment_field_3.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => environment}/containment_field_4.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => environment}/containment_field_5.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => environment}/containment_field_6.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => environment}/containment_field_7.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/arcane_workbench.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/bookshelf.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/element_icon_earth.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/element_icon_fire.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/element_icon_healing.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/element_icon_ice.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/element_icon_lightning.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/element_icon_magic.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/element_icon_necromancy.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/element_icon_sorcery.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/empty_slot_crystal.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/empty_slot_upgrade.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/lectern.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => container}/spell_sort_buttons.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => handbook}/handbook.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => handbook/pictures}/arcane_workbench_picture.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => handbook/pictures}/armour_picture.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => handbook/pictures}/elements_picture.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => handbook/pictures}/flower_picture.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => handbook/pictures}/obelisk_picture.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => handbook/pictures}/ore_picture.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => handbook/pictures}/shrine_picture.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => handbook/pictures}/tiers_picture.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{ => handbook/pictures}/tower_picture.png (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => gui}/pointer.png (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_arcane_jammer.png => potion_icons/arcane_jammer.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_containment.png => potion_icons/containment.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_curse_of_enfeeblement.png => potion_icons/curse_of_enfeeblement.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_curse_of_soulbinding.png => potion_icons/curse_of_soulbinding.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_curse_of_undeath.png => potion_icons/curse_of_undeath.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_decay.png => potion_icons/decay.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_empowerment.png => potion_icons/empowerment.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_fear.png => potion_icons/fear.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_fireskin.png => potion_icons/fireskin.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_font_of_mana.png => potion_icons/font_of_mana.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_frost.png => potion_icons/frost.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_frost_step.png => potion_icons/frost_step.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_ice_shroud.png => potion_icons/ice_shroud.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_mind_control.png => potion_icons/mind_control.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_mind_trick.png => potion_icons/mind_trick.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_muffle.png => potion_icons/muffle.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_paralysis.png => potion_icons/paralysis.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_sixth_sense.png => potion_icons/sixth_sense.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_slow_time.png => potion_icons/slow_time.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_static_aura.png => potion_icons/static_aura.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_transience.png => potion_icons/transience.png} (100%) rename src/main/resources/assets/ebwizardry/textures/gui/{potion_icon_ward.png => potion_icons/ward.png} (100%) rename src/main/resources/assets/ebwizardry/textures/{entity/sixth_sense.png => gui/sixth_sense_marker.png} (100%) rename src/main/resources/assets/ebwizardry/textures/{entity => gui}/target_pointer.png (100%) diff --git a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java index 4a5586da..aacade75 100644 --- a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java +++ b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java @@ -55,12 +55,12 @@ import java.util.List; @Mod.EventBusSubscriber(Side.CLIENT) public final class WizardryClientEventHandler { - private static final ResourceLocation sixthSenseTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/sixth_sense.png"); - private static final ResourceLocation sixthSenseOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/sixth_sense_overlay.png"); - private static final ResourceLocation frostOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_overlay.png"); - private static final ResourceLocation blinkOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/blink_overlay.png"); - private static final ResourceLocation pointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/pointer.png"); - private static final ResourceLocation targetPointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/target_pointer.png"); + private static final ResourceLocation SIXTH_SENSE_MARKER_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/sixth_sense_marker.png"); + private static final ResourceLocation SIXTH_SENSE_OVERLAY_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/sixth_sense_overlay.png"); + private static final ResourceLocation FROST_OVERLAY_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_overlay.png"); + private static final ResourceLocation BLINK_OVERLAY_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/blink_overlay.png"); + private static final ResourceLocation POINTER_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/pointer.png"); + private static final ResourceLocation TARGET_POINTER_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/target_pointer.png"); /** The remaining time for which the blink screen overlay effect will be displayed in first-person. Since this is * only for the first-person player (the instance of which is itself stored in a static variable), this can simply @@ -298,7 +298,7 @@ public final class WizardryClientEventHandler { buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - mc.renderEngine.bindTexture(targetPointerTexture); + mc.renderEngine.bindTexture(TARGET_POINTER_TEXTURE); buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex(); buffer.pos(0.2, 0.24, 0).tex(9f / 16f, 0).endVertex(); @@ -340,7 +340,7 @@ public final class WizardryClientEventHandler { buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - mc.renderEngine.bindTexture(pointerTexture); + mc.renderEngine.bindTexture(POINTER_TEXTURE); buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex(); buffer.pos(0.2, 0.24, 0).tex(9f / 16f, 0).endVertex(); @@ -385,7 +385,7 @@ public final class WizardryClientEventHandler { buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - mc.renderEngine.bindTexture(sixthSenseTexture); + mc.renderEngine.bindTexture(SIXTH_SENSE_MARKER_TEXTURE); buffer.pos(-0.6, 0.6, 0).tex(0, 0).endVertex(); buffer.pos(0.6, 0.6, 0).tex(1, 0).endVertex(); @@ -414,7 +414,7 @@ public final class WizardryClientEventHandler { GlStateManager.color(1, 1, 1, 1); GlStateManager.disableAlpha(); - renderScreenOverlay(event, sixthSenseOverlayTexture); + renderScreenOverlay(event, SIXTH_SENSE_OVERLAY_TEXTURE); GlStateManager.enableAlpha(); GlStateManager.color(1, 1, 1, 1); @@ -426,7 +426,7 @@ public final class WizardryClientEventHandler { GlStateManager.color(1, 1, 1, 1); GlStateManager.disableAlpha(); - renderScreenOverlay(event, frostOverlayTexture); + renderScreenOverlay(event, FROST_OVERLAY_TEXTURE); GlStateManager.enableAlpha(); GlStateManager.color(1, 1, 1, 1); @@ -440,7 +440,7 @@ public final class WizardryClientEventHandler { GlStateManager.color(1, 1, 1, alpha); GlStateManager.disableAlpha(); - renderScreenOverlay(event, blinkOverlayTexture); + renderScreenOverlay(event, BLINK_OVERLAY_TEXTURE); GlStateManager.enableAlpha(); GlStateManager.color(1, 1, 1, 1); diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index 29445ed8..07590df6 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -52,7 +52,7 @@ import java.util.Locale; public class GuiArcaneWorkbench extends GuiContainer { public static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, - "textures/gui/arcane_workbench.png"); + "textures/gui/container/arcane_workbench.png"); private static final int TOOLTIP_WIDTH = 144; private static final int TOOLTIP_TEXT_INSET = 6; diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiBookshelf.java b/src/main/java/electroblob/wizardry/client/gui/GuiBookshelf.java index 67091424..476674dd 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiBookshelf.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiBookshelf.java @@ -14,7 +14,7 @@ import net.minecraftforge.fml.relauncher.SideOnly; @SideOnly(Side.CLIENT) public class GuiBookshelf extends GuiContainer { - private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/bookshelf.png"); + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/bookshelf.png"); /** The player inventory bound to this GUI. */ private final InventoryPlayer playerInventory; /** The inventory contained within the corresponding bookshelf. */ diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiButtonSpellSort.java b/src/main/java/electroblob/wizardry/client/gui/GuiButtonSpellSort.java index 8e5c8439..cfde6624 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiButtonSpellSort.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiButtonSpellSort.java @@ -11,7 +11,7 @@ import net.minecraft.util.ResourceLocation; public class GuiButtonSpellSort extends GuiButton { - private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_sort_buttons.png"); + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/spell_sort_buttons.png"); private static final int TEXTURE_WIDTH = 32; private static final int TEXTURE_HEIGHT = 32; diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java b/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java index 188329ee..c795d842 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java @@ -42,7 +42,7 @@ import java.util.stream.Collectors; public class GuiLectern extends GuiSpellInfo implements ISpellSortable { - private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/lectern.png"); + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/lectern.png"); /** The distance of the page buttons from the bottom outside corners of the GUI. */ private static final int PAGE_BUTTON_INSET_X = 22, PAGE_BUTTON_INSET_Y = 13; /** The distance between adjacent page turn buttons. */ diff --git a/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java b/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java index bf947a03..ca2dc312 100644 --- a/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java +++ b/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java @@ -54,7 +54,7 @@ public class GuiWizardHandbook extends GuiScreen { private static final ResourceLocation DEFAULT = new ResourceLocation(Wizardry.MODID, "texts/handbook_en_us.json"); - static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png"); + static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook/handbook.png"); /** Global Gson instance for the handbook. */ private static final Gson gson = new Gson(); diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneLock.java b/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneLock.java index a9f36d1e..7722bc6d 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneLock.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneLock.java @@ -26,11 +26,11 @@ import java.util.List; @Mod.EventBusSubscriber(Side.CLIENT) public class RenderArcaneLock { - private static final ResourceLocation[] textures = new ResourceLocation[8]; + private static final ResourceLocation[] TEXTURES = new ResourceLocation[8]; static { - for(int i=0; i { - private static final ResourceLocation runeTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/rune.png"); + private static final ResourceLocation RUNE_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/arcane_workbench_rune.png"); public RenderArcaneWorkbench(){} @@ -56,7 +56,7 @@ public class RenderArcaneWorkbench extends TileEntitySpecialRenderer { - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, - "textures/entity/dark_ray.png"); - private static final ResourceLocation texture2 = new ResourceLocation(Wizardry.MODID, - "textures/entity/black_hole.png"); + private static final ResourceLocation RAY_TEXTURE = new ResourceLocation(Wizardry.MODID, + "textures/entity/black_hole/ray.png"); + private static final ResourceLocation CENTRE_TEXTURE = new ResourceLocation(Wizardry.MODID, + "textures/entity/black_hole/centre.png"); public RenderBlackHole(RenderManager renderManager){ super(renderManager); @@ -57,7 +57,7 @@ public class RenderBlackHole extends Render { GlStateManager.scale(scale, scale, scale); } - this.bindTexture(texture); + this.bindTexture(RAY_TEXTURE); // In theory this stuff should sort the rays into the correct render order based on the distance from // the 'camera' (i.e. the player's viewpoint) @@ -142,7 +142,7 @@ public class RenderBlackHole extends Render { buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - this.bindTexture(texture2); + this.bindTexture(CENTRE_TEXTURE); buffer.pos(-0.4, 0.4, 0).tex(0, 0).endVertex(); buffer.pos(0.4, 0.4, 0).tex(1, 0).endVertex(); @@ -164,7 +164,7 @@ public class RenderBlackHole extends Render { @Override protected ResourceLocation getEntityTexture(EntityBlackHole entity){ - return texture; + return RAY_TEXTURE; } } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java b/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java index 2694f1e8..dedc3bd8 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java @@ -11,14 +11,14 @@ import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.entity.Render; import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; import net.minecraft.util.ResourceLocation; import org.lwjgl.opengl.GL11; public class RenderBubble extends Render { - private static final ResourceLocation particleTextures = new ResourceLocation("textures/particle/particles.png"); - private static final ResourceLocation darkOrbTexture = new ResourceLocation(Wizardry.MODID, - "textures/entity/dark_orb.png"); + private static final ResourceLocation PARTICLE_TEXTURES = new ResourceLocation("textures/particle/particles.png"); + private static final ResourceLocation ENTRAPMENT_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/entrapment.png"); public RenderBubble(RenderManager renderManager){ super(renderManager); @@ -33,18 +33,20 @@ public class RenderBubble extends Render { float yOffset = 0; - if(WizardryUtilities.getRider(entity) != null){ - yOffset = WizardryUtilities.getRider(entity).height / 2; + Entity rider = WizardryUtilities.getRider(entity); + + if(rider != null){ + yOffset = rider.height / 2; } GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); - this.bindTexture(((EntityBubble)entity).isDarkOrb ? darkOrbTexture : particleTextures); + this.bindTexture(entity.isDarkOrb ? ENTRAPMENT_TEXTURE : PARTICLE_TEXTURES); float f6 = 1.0F; float f7 = 0.5F; float f8 = 0.5F; - if(((EntityBubble)entity).isDarkOrb){ + if(entity.isDarkOrb){ GlStateManager.disableLighting(); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); }else{ @@ -74,24 +76,22 @@ public class RenderBubble extends Render { // tessellator.setColorRGBA_I(k1, 128); // buffer.normal(0.0F, 1.0F, 0.0F); - if(((EntityBubble)entity).isDarkOrb){ - buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.0D).tex(0, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.0D).tex(1, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.0D).tex(1, 0).endVertex(); - buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.0D).tex(0, 0).endVertex(); + if(entity.isDarkOrb){ + buffer.pos(0.0F - f7, 0.0F - f8, 0.0D).tex(0, 1).endVertex(); + buffer.pos(f6 - f7, 0.0F - f8, 0.0D).tex(1, 1).endVertex(); + buffer.pos(f6 - f7, 1.0F - f8, 0.0D).tex(1, 0).endVertex(); + buffer.pos(0.0F - f7, 1.0F - f8, 0.0D).tex(0, 0).endVertex(); }else{ - buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.0D).tex(pixelwidth, pixelwidth * 24).endVertex(); - buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.0D).tex(pixelwidth * 8, pixelwidth * 24).endVertex(); - buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.0D).tex(pixelwidth * 8, pixelwidth * 17).endVertex(); - buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.0D).tex(pixelwidth, pixelwidth * 17).endVertex(); + buffer.pos(0.0F - f7, 0.0F - f8, 0.0D).tex(pixelwidth, pixelwidth * 24).endVertex(); + buffer.pos(f6 - f7, 0.0F - f8, 0.0D).tex(pixelwidth * 8, pixelwidth * 24).endVertex(); + buffer.pos(f6 - f7, 1.0F - f8, 0.0D).tex(pixelwidth * 8, pixelwidth * 17).endVertex(); + buffer.pos(0.0F - f7, 1.0F - f8, 0.0D).tex(pixelwidth, pixelwidth * 17).endVertex(); } tessellator.draw(); GlStateManager.disableBlend(); - if(((EntityBubble)entity).isDarkOrb){ - GlStateManager.enableLighting(); - } + if(entity.isDarkOrb) GlStateManager.enableLighting(); GlStateManager.disableRescaleNormal(); GlStateManager.popMatrix(); diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderContainmentField.java b/src/main/java/electroblob/wizardry/client/renderer/RenderContainmentField.java index b52b83b0..25660b04 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderContainmentField.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderContainmentField.java @@ -24,14 +24,14 @@ import org.lwjgl.opengl.GL11; @Mod.EventBusSubscriber(Side.CLIENT) public class RenderContainmentField { - private static final ResourceLocation[] textures = new ResourceLocation[8]; + private static final ResourceLocation[] TEXTURES = new ResourceLocation[8]; private static final float ANIMATION_SPEED = 0.004f; private static final float FADE_DISTANCE_SQUARED = 15; static { - for(int i=0; i { - private static final ResourceLocation[] textures = new ResourceLocation[10]; + private static final ResourceLocation[] TEXTURES = new ResourceLocation[10]; public RenderDecay(RenderManager renderManager){ super(renderManager); for(int i = 0; i < 10; i++){ - textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/decay_" + i + ".png"); + TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/decay/decay_" + i + ".png"); } } @@ -36,7 +36,7 @@ public class RenderDecay extends Render { GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); - this.bindTexture(textures[((EntityDecay)entity).textureIndex]); + this.bindTexture(TEXTURES[((EntityDecay)entity).textureIndex]); float f6 = 1.0F; float f7 = 0.5F; float f8 = 0.5F; diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderEvilWizard.java b/src/main/java/electroblob/wizardry/client/renderer/RenderEvilWizard.java index 34299e1c..e4470c75 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderEvilWizard.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderEvilWizard.java @@ -10,14 +10,15 @@ import net.minecraft.util.ResourceLocation; //@SideOnly(Side.CLIENT) public class RenderEvilWizard extends RenderBiped { - static final ResourceLocation[] textures = new ResourceLocation[6]; + + static final ResourceLocation[] TEXTURES = new ResourceLocation[6]; public RenderEvilWizard(RenderManager renderManager){ super(renderManager, new ModelWizard(), 0.5F); for(int i = 0; i < 6; i++){ - textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/evil_wizard_" + i + ".png"); + TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/evil_wizard/evil_wizard_" + i + ".png"); } // Just using the default without overriding models, since the armour sets its own model anyway. this.addLayer(new LayerBipedArmor(this)); @@ -25,7 +26,7 @@ public class RenderEvilWizard extends RenderBiped { @Override protected ResourceLocation getEntityTexture(EntityEvilWizard wizard){ - return textures[wizard.textureIndex]; + return TEXTURES[wizard.textureIndex]; } } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderMagicLight.java b/src/main/java/electroblob/wizardry/client/renderer/RenderMagicLight.java index a0e2a2ce..1d6d1f65 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderMagicLight.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderMagicLight.java @@ -12,10 +12,10 @@ import org.lwjgl.opengl.GL11; public class RenderMagicLight extends TileEntitySpecialRenderer { - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, - "textures/entity/light_ray.png"); - private static final ResourceLocation texture2 = new ResourceLocation(Wizardry.MODID, - "textures/entity/light_aura.png"); + private static final ResourceLocation RAY_TEXTURE = new ResourceLocation(Wizardry.MODID, + "textures/entity/light/ray.png"); + private static final ResourceLocation FLARE_TEXTURE = new ResourceLocation(Wizardry.MODID, + "textures/entity/light/flare.png"); @Override public void render(TileEntityMagicLight tileentity, double x, double y, double z, float f, @@ -60,7 +60,7 @@ public class RenderMagicLight extends TileEntitySpecialRenderer= 30){ for(int j = 0; j < 30; j++){ diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java b/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java index 566bc5a7..120e3447 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java @@ -11,14 +11,14 @@ import net.minecraft.util.ResourceLocation; //@SideOnly(Side.CLIENT) public class RenderWizard extends RenderBiped { - static final ResourceLocation[] textures = new ResourceLocation[6]; + static final ResourceLocation[] TEXTURES = new ResourceLocation[6]; public RenderWizard(RenderManager renderManager){ super(renderManager, new ModelWizard(), 0.5F); for(int i = 0; i < 6; i++){ - textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/wizard_" + i + ".png"); + TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/wizard/wizard_" + i + ".png"); } // Just using the default without overriding models, since the armour sets its own model anyway. this.addLayer(new LayerBipedArmor(this)); @@ -26,7 +26,7 @@ public class RenderWizard extends RenderBiped { @Override protected ResourceLocation getEntityTexture(EntityWizard wizard){ - return textures[wizard.textureIndex]; + return TEXTURES[wizard.textureIndex]; } } diff --git a/src/main/java/electroblob/wizardry/constants/Element.java b/src/main/java/electroblob/wizardry/constants/Element.java index 7b433693..49dc4be4 100644 --- a/src/main/java/electroblob/wizardry/constants/Element.java +++ b/src/main/java/electroblob/wizardry/constants/Element.java @@ -37,7 +37,7 @@ public enum Element implements IStringSerializable { Element(Style colour, String name, String modid){ this.colour = colour; this.unlocalisedName = name; - this.icon = new ResourceLocation(modid, "textures/gui/element_icon_" + unlocalisedName + ".png"); + this.icon = new ResourceLocation(modid, "textures/gui/container/element_icon_" + unlocalisedName + ".png"); } /** Returns the element with the given name, or throws an {@link java.lang.IllegalArgumentException} if no such diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index 884ed425..c3e8b16b 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -52,8 +52,8 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl /** The arcane workbench tile entity associated with this container. */ public TileEntityArcaneWorkbench tileentity; - public static final ResourceLocation EMPTY_SLOT_CRYSTAL = new ResourceLocation(Wizardry.MODID, "gui/empty_slot_crystal"); - public static final ResourceLocation EMPTY_SLOT_UPGRADE = new ResourceLocation(Wizardry.MODID, "gui/empty_slot_upgrade"); + public static final ResourceLocation EMPTY_SLOT_CRYSTAL = new ResourceLocation(Wizardry.MODID, "gui/container/empty_slot_crystal"); + public static final ResourceLocation EMPTY_SLOT_UPGRADE = new ResourceLocation(Wizardry.MODID, "gui/container/empty_slot_upgrade"); public static final int CRYSTAL_SLOT = 8; public static final int CENTRE_SLOT = 9; @@ -192,7 +192,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl int centreX = this.getSlot(CENTRE_SLOT).xPos; int centreY = this.getSlot(CENTRE_SLOT).yPos; - + // Show however many spell book slots are necessary for(int i = 0; i < spellSlots; i++){ diff --git a/src/main/java/electroblob/wizardry/potion/CurseEnfeeblement.java b/src/main/java/electroblob/wizardry/potion/CurseEnfeeblement.java index 69daa132..234bc8b9 100644 --- a/src/main/java/electroblob/wizardry/potion/CurseEnfeeblement.java +++ b/src/main/java/electroblob/wizardry/potion/CurseEnfeeblement.java @@ -24,7 +24,7 @@ public class CurseEnfeeblement extends Curse { } public CurseEnfeeblement(boolean isBadEffect, int liquiidColour){ - super(isBadEffect, liquiidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_curse_of_enfeeblement.png")); + super(isBadEffect, liquiidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/curse_of_enfeeblement.png")); // This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet. this.setPotionName("potion." + Wizardry.MODID + ":curse_of_enfeeblement"); this.registerPotionAttributeModifier(SharedMonsterAttributes.MAX_HEALTH, diff --git a/src/main/java/electroblob/wizardry/potion/CurseUndeath.java b/src/main/java/electroblob/wizardry/potion/CurseUndeath.java index 6f2d0a9f..4bea1099 100644 --- a/src/main/java/electroblob/wizardry/potion/CurseUndeath.java +++ b/src/main/java/electroblob/wizardry/potion/CurseUndeath.java @@ -10,7 +10,7 @@ import net.minecraft.util.math.BlockPos; public class CurseUndeath extends Curse { public CurseUndeath(boolean isBadEffect, int liquiidColour){ - super(isBadEffect, liquiidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_curse_of_undeath.png")); + super(isBadEffect, liquiidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/curse_of_undeath.png")); // This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet. this.setPotionName("potion." + Wizardry.MODID + ":curse_of_undeath"); } diff --git a/src/main/java/electroblob/wizardry/potion/PotionContainment.java b/src/main/java/electroblob/wizardry/potion/PotionContainment.java index ec8d269d..46188438 100644 --- a/src/main/java/electroblob/wizardry/potion/PotionContainment.java +++ b/src/main/java/electroblob/wizardry/potion/PotionContainment.java @@ -21,7 +21,7 @@ public class PotionContainment extends PotionMagicEffect { public static final String ENTITY_TAG = "containmentPos"; public PotionContainment(boolean isBadEffect, int liquidColour){ - super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_containment.png")); + super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/containment.png")); this.setPotionName("potion." + Wizardry.MODID + ":containment"); } diff --git a/src/main/java/electroblob/wizardry/potion/PotionDecay.java b/src/main/java/electroblob/wizardry/potion/PotionDecay.java index c4562d3c..7ce204ff 100644 --- a/src/main/java/electroblob/wizardry/potion/PotionDecay.java +++ b/src/main/java/electroblob/wizardry/potion/PotionDecay.java @@ -19,7 +19,7 @@ import java.util.List; public class PotionDecay extends PotionMagicEffect { public PotionDecay(boolean isBadEffect, int liquidColour){ - super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_decay.png")); + super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/decay.png")); // This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet. this.setPotionName("potion." + Wizardry.MODID + ":decay"); this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED, diff --git a/src/main/java/electroblob/wizardry/potion/PotionFrost.java b/src/main/java/electroblob/wizardry/potion/PotionFrost.java index 28b89553..dc4366dd 100644 --- a/src/main/java/electroblob/wizardry/potion/PotionFrost.java +++ b/src/main/java/electroblob/wizardry/potion/PotionFrost.java @@ -16,7 +16,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; public class PotionFrost extends PotionMagicEffect implements ICustomPotionParticles { public PotionFrost(boolean isBadEffect, int liquidColour){ - super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_frost.png")); + super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/frost.png")); // This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet. this.setPotionName("potion." + Wizardry.MODID + ":frost"); // With -0.5 as the 'amount', frost 1 slows the entity down by a half and frost 2 roots it to the spot diff --git a/src/main/java/electroblob/wizardry/potion/PotionFrostStep.java b/src/main/java/electroblob/wizardry/potion/PotionFrostStep.java index 4c369526..44377266 100644 --- a/src/main/java/electroblob/wizardry/potion/PotionFrostStep.java +++ b/src/main/java/electroblob/wizardry/potion/PotionFrostStep.java @@ -32,7 +32,7 @@ public class PotionFrostStep extends PotionMagicEffect implements ICustomPotionP private static final Field prevBlockPos = ObfuscationReflectionHelper.findField(EntityLivingBase.class, "field_184620_bC"); public PotionFrostStep(boolean isBadEffect, int liquidColour){ - super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_frost_step.png")); + super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/frost_step.png")); this.setPotionName("potion." + Wizardry.MODID + ":frost_step"); } diff --git a/src/main/java/electroblob/wizardry/potion/PotionSlowTime.java b/src/main/java/electroblob/wizardry/potion/PotionSlowTime.java index e849de0c..0a00a31d 100644 --- a/src/main/java/electroblob/wizardry/potion/PotionSlowTime.java +++ b/src/main/java/electroblob/wizardry/potion/PotionSlowTime.java @@ -34,7 +34,7 @@ public class PotionSlowTime extends PotionMagicEffect implements ISyncedPotion { public static final String NBT_KEY = "time_slowed"; public PotionSlowTime(boolean isBadEffect, int liquidColour){ - super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_slow_time.png")); + super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/slow_time.png")); this.setPotionName("potion." + Wizardry.MODID + ":slow_time"); } diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_en_gb.json b/src/main/resources/assets/ebwizardry/texts/handbook_en_gb.json index a040b1c2..3b821cb6 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_en_gb.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_en_gb.json @@ -11,7 +11,7 @@ "images": { "workbench": { - "location": "ebwizardry:textures/gui/arcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesarcane_workbench_picture.png", "caption": "The Arcane Workbench", "u": 0, "v": 0, @@ -19,7 +19,7 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/ore_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesore_picture.png", "caption": "Crystal Ore", "u": 0, "v": 0, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/flower_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesflower_picture.png", "caption": "A Crystal Flower", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/tower_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturestower_picture.png", "caption": "A Wizard's Tower", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/armour_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesarmour_picture.png", "caption": "A Set Of Wizard Armour", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/obelisk_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesobelisk_picture.png", "caption": "An Obelisk", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/shrine_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesshrine_picture.png", "caption": "An Earth Shrine", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/tiers_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturestiers_picture.png", "caption": "The 4 Tiers Of Wand", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/elements_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictureselements_picture.png", "caption": "The 7 Arcane Elements", "u": 0, "v": 0, diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_en_us.json b/src/main/resources/assets/ebwizardry/texts/handbook_en_us.json index 45d60db3..34703d44 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_en_us.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_en_us.json @@ -11,7 +11,7 @@ "images": { "workbench": { - "location": "ebwizardry:textures/gui/arcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesarcane_workbench_picture.png", "caption": "The Arcane Workbench", "u": 0, "v": 0, @@ -19,7 +19,7 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/ore_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesore_picture.png", "caption": "Crystal Ore", "u": 0, "v": 0, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/flower_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesflower_picture.png", "caption": "A Crystal Flower", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/tower_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturestower_picture.png", "caption": "A Wizard's Tower", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/armour_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesarmour_picture.png", "caption": "A Set Of Wizard Armour", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/obelisk_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesobelisk_picture.png", "caption": "An Obelisk", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/shrine_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesshrine_picture.png", "caption": "An Earth Shrine", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/tiers_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturestiers_picture.png", "caption": "The 4 Tiers Of Wand", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/elements_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictureselements_picture.png", "caption": "The 7 Arcane Elements", "u": 0, "v": 0, diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json b/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json index a8b933ab..1ab4d80d 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json @@ -11,7 +11,7 @@ "images": { "workbench": { - "location": "ebwizardry:textures/gui/arcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesarcane_workbench_picture.png", "caption": "Mesa Arcana", "u": 0, "v": 0, @@ -19,7 +19,7 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/ore_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesore_picture.png", "caption": "Mena de Cristal Mágico", "u": 0, "v": 0, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/flower_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesflower_picture.png", "caption": "Flor de Cristal", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/tower_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturestower_picture.png", "caption": "Torre de mago", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/armour_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesarmour_picture.png", "caption": "Conjunto de Armadura de mago", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/obelisk_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesobelisk_picture.png", "caption": "Obelisco", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/shrine_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesshrine_picture.png", "caption": "Santuario de la tierra", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/tiers_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturestiers_picture.png", "caption": "Los 4 niveles en las Varitas", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/elements_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictureselements_picture.png", "caption": "Los 7 elementos Arcanos", "u": 0, "v": 0, diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_zh_cn.json b/src/main/resources/assets/ebwizardry/texts/handbook_zh_cn.json index 1fa2da89..5d892c93 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_zh_cn.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_zh_cn.json @@ -11,7 +11,7 @@ "images": { "workbench": { - "location": "ebwizardry:textures/gui/arcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesarcane_workbench_picture.png", "caption": "The Arcane Workbench", "u": 0, "v": 0, @@ -19,7 +19,7 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/ore_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesore_picture.png", "caption": "Crystal Ore", "u": 0, "v": 0, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/flower_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesflower_picture.png", "caption": "A Crystal Flower", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/tower_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturestower_picture.png", "caption": "A Wizard's Tower", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/armour_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesarmour_picture.png", "caption": "A Set Of Wizard Armour", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/obelisk_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesobelisk_picture.png", "caption": "An Obelisk", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/shrine_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesshrine_picture.png", "caption": "An Earth Shrine", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/tiers_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturestiers_picture.png", "caption": "The 4 Tiers Of Wand", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/elements_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictureselements_picture.png", "caption": "The 7 Arcane Elements", "u": 0, "v": 0, diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_zh_tw.json b/src/main/resources/assets/ebwizardry/texts/handbook_zh_tw.json index 7158605b..9b6f7e35 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_zh_tw.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_zh_tw.json @@ -11,7 +11,7 @@ "images": { "workbench": { - "location": "ebwizardry:textures/gui/arcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesarcane_workbench_picture.png", "caption": "法術工作å°", "u": 0, "v": 0, @@ -19,7 +19,7 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/ore_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesore_picture.png", "caption": "水晶礦", "u": 0, "v": 0, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/flower_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesflower_picture.png", "caption": "水晶花", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/tower_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturestower_picture.png", "caption": "法師塔", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/armour_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesarmour_picture.png", "caption": "法師æœ", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/obelisk_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesobelisk_picture.png", "caption": "方尖碑", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/shrine_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturesshrine_picture.png", "caption": "神殿", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/tiers_picture.png", + "location": "ebwizardry:textures/gui/handbook/picturestiers_picture.png", "caption": "四種等級的法æ–", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/elements_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictureselements_picture.png", "caption": "七種法術元素", "u": 0, "v": 0, diff --git a/src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_0.png b/src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_0.png similarity index 100% rename from src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_0.png rename to src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_0.png diff --git a/src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_1.png b/src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_1.png similarity index 100% rename from src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_1.png rename to src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_1.png diff --git a/src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_2.png b/src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_2.png similarity index 100% rename from src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_2.png rename to src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_2.png diff --git a/src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_3.png b/src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_3.png similarity index 100% rename from src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_3.png rename to src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_3.png diff --git a/src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_4.png b/src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_4.png similarity index 100% rename from src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_4.png rename to src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_4.png diff --git a/src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_5.png b/src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_5.png similarity index 100% rename from src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_5.png rename to src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_5.png diff --git a/src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_6.png b/src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_6.png similarity index 100% rename from src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_6.png rename to src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_6.png diff --git a/src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_7.png b/src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_7.png similarity index 100% rename from src/main/resources/assets/ebwizardry/textures/entity/arcane_lock_7.png rename to src/main/resources/assets/ebwizardry/textures/blocks/arcane_lock_7.png diff --git a/src/main/resources/assets/ebwizardry/textures/entity/rune.png b/src/main/resources/assets/ebwizardry/textures/entity/arcane_workbench_rune.png similarity index 100% rename from src/main/resources/assets/ebwizardry/textures/entity/rune.png rename to src/main/resources/assets/ebwizardry/textures/entity/arcane_workbench_rune.png diff --git a/src/main/resources/assets/ebwizardry/textures/entity/aura.png b/src/main/resources/assets/ebwizardry/textures/entity/aura.png deleted file mode 100644 index 89e2234bca4424726056e7dab295eb5196610de8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1165 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}Y)RhkE)0_xCNpThPYOK6 zz`(#+;1OBOz`!jG!i)^F=14FwFtC?+`ns||;N<2NWZU%EQH6nlK{hiaqQp5rH#aq} zgaHJci&7IyQd1PlGfOfQ+&z5*QuI>U85o#9db&7yi_y*fzRUEo;#+Sp7%wQr0~ zoOUiUvyVJ2dU{7wCg-!qI{}tM;vCnK<6r%8Jbx)`3rBeB>SbX|=f3>+>-F;k!t-Zy zKmFC6ceng&_# z8eJ3&uL_rO>2rw_tkvI|TRKQNE+1~0=T=}SLD z87^pUHx-`9_25^CrNyLnhP;14-{S29_C0s#VeWX+bj0B;gHF!boLc5Q*ClK_EW^Lu zzw`2xaKpA3?k%k95JW9U|~x5=Bo&0KrJh09}p*V@SVndjRN+Qzo6$Wm2G zjlN%dCSUK$^9&|~GKaFc-g~r#j#@ByPggWFom}m;z@|pu=lm2K#f=BN8N)0LcK1zu z&UfU*(xl{5?gw>!PxLTJtb7&Vl9j7)Jye{3<*D8(2bbw0+v0sM9$FJFE}YDMX88w! z6}QETa;+J5>hh;Z9B?V;d7JapNGmdyS_nifW$CWwPVXl z-p$bH<&EF4X~Hzg1FP>Zb=FPfy>sl#Ihh5A9QGLOGCbtO8sox!_SdX! zBC2>gi{ww7yDR^SDMPeweN2e?x;x7@dxv)gzuM~OH>H~Y!KVm8!O*K$0;1H9XzR?a z5B{L*8<9FSKe75%!9LDS(GB(g0*ZF38_2DQE&Z%&R(kLhMY^Sf`5M8;<|WnqJibhh#Bms3+vX$1 zJfkLL)59uR1^LLW0xLsGuU$UBn%8(COWC747U)|(?8}Ia=TV+-B* Date: Fri, 10 Apr 2020 00:21:13 +0100 Subject: [PATCH 033/415] =?UTF-8?q?Was=20bound=20to=20mess=20up=20at=20lea?= =?UTF-8?q?st=20one=20of=20them=20=F0=9F=99=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ebwizardry/texts/handbook_en_gb.json | 18 +++++++++--------- .../ebwizardry/texts/handbook_en_us.json | 18 +++++++++--------- .../ebwizardry/texts/handbook_es_es.json | 18 +++++++++--------- .../ebwizardry/texts/handbook_zh_cn.json | 18 +++++++++--------- .../ebwizardry/texts/handbook_zh_tw.json | 18 +++++++++--------- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_en_gb.json b/src/main/resources/assets/ebwizardry/texts/handbook_en_gb.json index 3b821cb6..2d89c8d8 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_en_gb.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_en_gb.json @@ -11,7 +11,7 @@ "images": { "workbench": { - "location": "ebwizardry:textures/gui/handbook/picturesarcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/arcane_workbench_picture.png", "caption": "The Arcane Workbench", "u": 0, "v": 0, @@ -19,7 +19,7 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/handbook/picturesore_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/ore_picture.png", "caption": "Crystal Ore", "u": 0, "v": 0, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/handbook/picturesflower_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/flower_picture.png", "caption": "A Crystal Flower", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/handbook/picturestower_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/tower_picture.png", "caption": "A Wizard's Tower", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/handbook/picturesarmour_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/armour_picture.png", "caption": "A Set Of Wizard Armour", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/handbook/picturesobelisk_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/obelisk_picture.png", "caption": "An Obelisk", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/handbook/picturesshrine_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/shrine_picture.png", "caption": "An Earth Shrine", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/handbook/picturestiers_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/tiers_picture.png", "caption": "The 4 Tiers Of Wand", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/handbook/pictureselements_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/elements_picture.png", "caption": "The 7 Arcane Elements", "u": 0, "v": 0, diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_en_us.json b/src/main/resources/assets/ebwizardry/texts/handbook_en_us.json index 34703d44..1d4d65cd 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_en_us.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_en_us.json @@ -11,7 +11,7 @@ "images": { "workbench": { - "location": "ebwizardry:textures/gui/handbook/picturesarcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/arcane_workbench_picture.png", "caption": "The Arcane Workbench", "u": 0, "v": 0, @@ -19,7 +19,7 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/handbook/picturesore_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/ore_picture.png", "caption": "Crystal Ore", "u": 0, "v": 0, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/handbook/picturesflower_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/flower_picture.png", "caption": "A Crystal Flower", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/handbook/picturestower_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/tower_picture.png", "caption": "A Wizard's Tower", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/handbook/picturesarmour_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/armour_picture.png", "caption": "A Set Of Wizard Armour", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/handbook/picturesobelisk_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/obelisk_picture.png", "caption": "An Obelisk", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/handbook/picturesshrine_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/shrine_picture.png", "caption": "An Earth Shrine", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/handbook/picturestiers_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/tiers_picture.png", "caption": "The 4 Tiers Of Wand", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/handbook/pictureselements_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/elements_picture.png", "caption": "The 7 Arcane Elements", "u": 0, "v": 0, diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json b/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json index 1ab4d80d..d0386c39 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json @@ -11,7 +11,7 @@ "images": { "workbench": { - "location": "ebwizardry:textures/gui/handbook/picturesarcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/arcane_workbench_picture.png", "caption": "Mesa Arcana", "u": 0, "v": 0, @@ -19,7 +19,7 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/handbook/picturesore_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/ore_picture.png", "caption": "Mena de Cristal Mágico", "u": 0, "v": 0, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/handbook/picturesflower_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/flower_picture.png", "caption": "Flor de Cristal", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/handbook/picturestower_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/tower_picture.png", "caption": "Torre de mago", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/handbook/picturesarmour_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/armour_picture.png", "caption": "Conjunto de Armadura de mago", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/handbook/picturesobelisk_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/obelisk_picture.png", "caption": "Obelisco", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/handbook/picturesshrine_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/shrine_picture.png", "caption": "Santuario de la tierra", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/handbook/picturestiers_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/tiers_picture.png", "caption": "Los 4 niveles en las Varitas", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/handbook/pictureselements_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/elements_picture.png", "caption": "Los 7 elementos Arcanos", "u": 0, "v": 0, diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_zh_cn.json b/src/main/resources/assets/ebwizardry/texts/handbook_zh_cn.json index 5d892c93..009819c9 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_zh_cn.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_zh_cn.json @@ -11,7 +11,7 @@ "images": { "workbench": { - "location": "ebwizardry:textures/gui/handbook/picturesarcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/arcane_workbench_picture.png", "caption": "The Arcane Workbench", "u": 0, "v": 0, @@ -19,7 +19,7 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/handbook/picturesore_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/ore_picture.png", "caption": "Crystal Ore", "u": 0, "v": 0, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/handbook/picturesflower_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/flower_picture.png", "caption": "A Crystal Flower", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/handbook/picturestower_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/tower_picture.png", "caption": "A Wizard's Tower", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/handbook/picturesarmour_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/armour_picture.png", "caption": "A Set Of Wizard Armour", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/handbook/picturesobelisk_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/obelisk_picture.png", "caption": "An Obelisk", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/handbook/picturesshrine_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/shrine_picture.png", "caption": "An Earth Shrine", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/handbook/picturestiers_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/tiers_picture.png", "caption": "The 4 Tiers Of Wand", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/handbook/pictureselements_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/elements_picture.png", "caption": "The 7 Arcane Elements", "u": 0, "v": 0, diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_zh_tw.json b/src/main/resources/assets/ebwizardry/texts/handbook_zh_tw.json index 9b6f7e35..849c0a21 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_zh_tw.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_zh_tw.json @@ -11,7 +11,7 @@ "images": { "workbench": { - "location": "ebwizardry:textures/gui/handbook/picturesarcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/arcane_workbench_picture.png", "caption": "法術工作å°", "u": 0, "v": 0, @@ -19,7 +19,7 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/handbook/picturesore_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/ore_picture.png", "caption": "水晶礦", "u": 0, "v": 0, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/handbook/picturesflower_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/flower_picture.png", "caption": "水晶花", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/handbook/picturestower_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/tower_picture.png", "caption": "法師塔", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/handbook/picturesarmour_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/armour_picture.png", "caption": "法師æœ", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/handbook/picturesobelisk_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/obelisk_picture.png", "caption": "方尖碑", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/handbook/picturesshrine_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/shrine_picture.png", "caption": "神殿", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/handbook/picturestiers_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/tiers_picture.png", "caption": "四種等級的法æ–", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/handbook/pictureselements_picture.png", + "location": "ebwizardry:textures/gui/handbook/pictures/elements_picture.png", "caption": "七種法術元素", "u": 0, "v": 0, From 15d3d9e70b3141858b07e4ebb9d7d9d2c403e96a Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 10 Apr 2020 15:56:34 +0100 Subject: [PATCH 034/415] Arcane workbench tooltip refactoring - Reorganises the huge amount of code in the draw background/foreground methods into a hierarchical object system - Makes the tooltip box change height to fit the content - Adds text wrapping support for items/spells with super long names - Adds proper format arguments to the mana readout translation - The tooltip now shows the new spells that will be applied with a flashing animation --- .../wizardry/client/DrawingUtils.java | 2 +- .../client/gui/GuiArcaneWorkbench.java | 686 +++++++++++++----- .../assets/ebwizardry/lang/en_gb.lang | 5 +- .../assets/ebwizardry/lang/en_us.lang | 5 +- .../gui/container/arcane_workbench.png | Bin 40214 -> 40141 bytes 5 files changed, 508 insertions(+), 190 deletions(-) diff --git a/src/main/java/electroblob/wizardry/client/DrawingUtils.java b/src/main/java/electroblob/wizardry/client/DrawingUtils.java index 20b99a60..ff591a5c 100644 --- a/src/main/java/electroblob/wizardry/client/DrawingUtils.java +++ b/src/main/java/electroblob/wizardry/client/DrawingUtils.java @@ -156,7 +156,7 @@ public final class DrawingUtils { * @return The resulting integer colour code, which will be an 8-digit hexadecimal. */ public static int makeTranslucent(int colour, float opacity){ - return colour + ((int)(0xff * opacity * 0x01000000)); + return colour + ((int)(opacity * 0xff) << 24); } /** diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index 07590df6..63289ffd 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -5,13 +5,9 @@ import electroblob.wizardry.client.DrawingUtils; import electroblob.wizardry.constants.Element; import electroblob.wizardry.constants.Tier; import electroblob.wizardry.data.SpellGlyphData; -import electroblob.wizardry.data.WizardData; import electroblob.wizardry.inventory.ContainerArcaneWorkbench; import electroblob.wizardry.inventory.SlotBookList; -import electroblob.wizardry.item.IManaStoringItem; -import electroblob.wizardry.item.ISpellCastingItem; -import electroblob.wizardry.item.IWorkbenchItem; -import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.item.*; import electroblob.wizardry.packet.PacketControlInput; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.WizardrySounds; @@ -21,6 +17,7 @@ import electroblob.wizardry.util.ISpellSortable; import electroblob.wizardry.util.WandHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiTextField; import net.minecraft.client.gui.inventory.GuiContainer; @@ -36,6 +33,7 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.text.Style; import net.minecraft.util.text.TextFormatting; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.fml.common.Mod; @@ -46,6 +44,8 @@ import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; @Mod.EventBusSubscriber(Side.CLIENT) @@ -55,7 +55,9 @@ public class GuiArcaneWorkbench extends GuiContainer { "textures/gui/container/arcane_workbench.png"); private static final int TOOLTIP_WIDTH = 144; - private static final int TOOLTIP_TEXT_INSET = 6; + private static final int TOOLTIP_BORDER = 6; + private static final int LINE_SPACING_WIDE = 5; + private static final int LINE_SPACING_NARROW = 1; private static final int BOOKSHELF_UI_WIDTH = ContainerArcaneWorkbench.BOOKSHELF_UI_WIDTH; // For conciseness @@ -74,9 +76,6 @@ public class GuiArcaneWorkbench extends GuiContainer { private static final int SCROLL_BAR_HEIGHT = 178; private static final int SCROLL_HANDLE_HEIGHT = 15; - private static final int PROGRESSION_BAR_WIDTH = 131; - private static final int PROGRESSION_BAR_HEIGHT = 3; - private static final int HALO_DIAMETER = 156; private static final int TEXTURE_WIDTH = 512; @@ -94,6 +93,8 @@ public class GuiArcaneWorkbench extends GuiContainer { private GuiTextField searchField; private boolean searchNeedsClearing; + private final List tooltipElements = new ArrayList<>(); + private int animationTimer = 0; private float scroll = 0; @@ -131,6 +132,12 @@ public class GuiArcaneWorkbench extends GuiContainer { this.searchField.setCanLoseFocus(false); this.searchField.setFocused(true); + this.tooltipElements.add(new TooltipElementItemName(new Style().setColor(TextFormatting.WHITE), LINE_SPACING_WIDE)); + this.tooltipElements.add(new TooltipElementManaReadout(LINE_SPACING_WIDE)); + this.tooltipElements.add(new TooltipElementProgressionBar(LINE_SPACING_WIDE)); + this.tooltipElements.add(new TooltipElementSpellList(LINE_SPACING_WIDE)); + this.tooltipElements.add(new TooltipElementUpgradeList(LINE_SPACING_WIDE)); + } @Override @@ -326,88 +333,20 @@ public class GuiArcaneWorkbench extends GuiContainer { if(((IWorkbenchItem)stack.getItem()).showTooltip(stack)){ + int tooltipHeight = tooltipElements.stream().mapToInt(e -> e.getTotalHeight(stack)).sum() + - tooltipElements.get(tooltipElements.size() - 1).spaceAfter; // Remove space after last element + // Tooltip box - DrawingUtils.drawTexturedRect(left + MAIN_GUI_WIDTH, top, MAIN_GUI_WIDTH, 0, TOOLTIP_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT); + DrawingUtils.drawTexturedRect(left + MAIN_GUI_WIDTH, top, MAIN_GUI_WIDTH, 0, TOOLTIP_WIDTH, + TOOLTIP_BORDER + tooltipHeight, TEXTURE_WIDTH, TEXTURE_HEIGHT); + DrawingUtils.drawTexturedRect(left + MAIN_GUI_WIDTH, top + TOOLTIP_BORDER + tooltipHeight, + MAIN_GUI_WIDTH, ySize - TOOLTIP_BORDER, TOOLTIP_WIDTH, TOOLTIP_BORDER, TEXTURE_WIDTH, TEXTURE_HEIGHT); - int y = top + 20; + int x = left + MAIN_GUI_WIDTH + TOOLTIP_BORDER; + int y = top + TOOLTIP_BORDER; - if(stack.getItem() instanceof IManaStoringItem && ((IManaStoringItem)stack.getItem()).showManaInWorkbench(this.mc.player, stack)){ - y += 14; - } - - // Progression bar - if(stack.getItem() instanceof ItemWand && !Wizardry.settings.legacyWandLevelling){ - - y += 10; - - Tier tier = ((ItemWand)stack.getItem()).tier; - - float progressFraction = 1; - - if(tier != Tier.MASTER){ - progressFraction = (float)WandHelper.getProgression(stack) / Tier.values()[tier.level + 1].progression; - } - - DrawingUtils.drawTexturedRect(left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, MAIN_GUI_WIDTH, ySize + PROGRESSION_BAR_HEIGHT, PROGRESSION_BAR_WIDTH, PROGRESSION_BAR_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); - int width = (int)(PROGRESSION_BAR_WIDTH * progressFraction); - DrawingUtils.drawTexturedRect(left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, MAIN_GUI_WIDTH, ySize, width, PROGRESSION_BAR_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); - - y += 8; - } - - if(stack.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)stack.getItem()).showSpellsInWorkbench(this.mc.player, stack)){ - - Spell[] spells = ((ISpellCastingItem)stack.getItem()).getSpells(stack); - - GlStateManager.enableBlend(); - - for(Spell spell : spells){ - - boolean discovered = true; - - if(!this.mc.player.isCreative() && WizardData.get(this.mc.player) != null){ - discovered = WizardData.get(this.mc.player).hasSpellBeenDiscovered(spell); - } - // As of Wizardry 1.2, the icons have been split off into their own texture files to allow for add-on - // mods to add their own. - Minecraft.getMinecraft().renderEngine - .bindTexture(discovered ? spell.getElement().getIcon() : Element.MAGIC.getIcon()); - - // Renders the little element icon - DrawingUtils.drawTexturedRect(left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET - 1, y, 8, 8); - - y += 10; - } - } - - GlStateManager.disableBlend(); - - int x = 0; - y += 16; - - // Look how much shorter this is with the WandHelper class! - for(Item item : WandHelper.getSpecialUpgrades()){ - - int level = WandHelper.getUpgradeLevel(stack, item); - - if(level > 0){ - - ItemStack stack1 = new ItemStack(item, level); - GlStateManager.enableDepth(); - - this.itemRender.renderItemAndEffectIntoGUI(stack1, left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y); - this.itemRender.renderItemOverlayIntoGUI(this.fontRenderer, stack1, left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y, null); - - x += 18; - - if(TOOLTIP_TEXT_INSET * 2 + x + 16 > TOOLTIP_WIDTH){ - x = 0; - y += 18; - } - - GlStateManager.disableDepth(); - GlStateManager.disableLighting(); // Whyyyyyy? - } + for(TooltipElement element : this.tooltipElements){ + y = element.drawBackgroundLayer(x, y, stack, partialTicks, mouseX, mouseY); } } } @@ -451,95 +390,11 @@ public class GuiArcaneWorkbench extends GuiContainer { if(((IWorkbenchItem)stack.getItem()).showTooltip(stack)){ - int y = 6; + int x = left + MAIN_GUI_WIDTH + TOOLTIP_BORDER; + int y = TOOLTIP_BORDER; - this.fontRenderer.drawStringWithShadow("\u00A7f" + stack.getDisplayName(), left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); - - if(stack.getItem() instanceof IManaStoringItem && ((IManaStoringItem)stack.getItem()).showManaInWorkbench(this.mc.player, stack)){ - y += 14; - this.fontRenderer.drawStringWithShadow( - "\u00A77" + I18n.format("container." + Wizardry.MODID + ":arcane_workbench.mana") - + " " + ((IManaStoringItem)stack.getItem()).getMana(stack) + "/" - + ((IManaStoringItem)stack.getItem()).getManaCapacity(stack), - left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); - } - - // Progression tier text - if(stack.getItem() instanceof ItemWand && !Wizardry.settings.legacyWandLevelling){ - - y += 14; - - Tier tier = ((ItemWand)stack.getItem()).tier; - - this.fontRenderer.drawStringWithShadow(tier.getDisplayNameWithFormatting(), left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); - - if(tier != Tier.MASTER){ - Tier nextTier = Tier.values()[tier.level + 1]; - String s = TextFormatting.DARK_GRAY.toString() + nextTier.getDisplayName(); - if(WandHelper.getProgression(stack) >= nextTier.progression) s = nextTier.getDisplayNameWithFormatting(); - this.fontRenderer.drawStringWithShadow(s, left + MAIN_GUI_WIDTH + TOOLTIP_WIDTH - - TOOLTIP_TEXT_INSET - fontRenderer.getStringWidth(s), y, 0); - } - - y += 4; - } - - y += 14; - - if(stack.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)stack.getItem()).showSpellsInWorkbench(this.mc.player, stack)){ - - Spell[] spells = ((ISpellCastingItem)stack.getItem()).getSpells(stack); - - for(Spell spell : spells){ - - boolean discovered = true; - - if(!this.mc.player.isCreative() && WizardData.get(this.mc.player) != null){ - discovered = WizardData.get(this.mc.player).hasSpellBeenDiscovered(spell); - } - - if(discovered){ - this.fontRenderer.drawStringWithShadow(spell.getDisplayNameWithFormatting(), left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + 10, y, 0); - }else{ - this.mc.standardGalacticFontRenderer.drawStringWithShadow( - "\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.world), left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + 10, y, 0); - } - y += 10; - } - } - - if(WandHelper.getTotalUpgrades(stack) > 0){ - - y += 6; - - this.fontRenderer.drawStringWithShadow("\u00A7f" + I18n.format("container." - + Wizardry.MODID + ":arcane_workbench.upgrades"), left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET, y, 0); - - int x = 0; - y += 10; - - // Wand upgrade tooltips - for(Item item : WandHelper.getSpecialUpgrades()){ - - int level = WandHelper.getUpgradeLevel(stack, item); - - if(level > 0){ - // The javadoc for isPointInRegion is ambiguous; what it means is that the REGION is - // relative to the GUI but the POINT isn't. - if(isPointInRegion(left + MAIN_GUI_WIDTH + TOOLTIP_TEXT_INSET + x, y, 16, 16, mouseX, mouseY)){ - ItemStack stack1 = new ItemStack(item, level); - this.renderToolTip(stack1, mouseX - guiLeft, mouseY - guiTop); - } - - x += 18; - - if(TOOLTIP_TEXT_INSET * 2 + x + 16 > TOOLTIP_WIDTH){ - x = 0; - y += 18; - } - - } - } + for(TooltipElement element : this.tooltipElements){ + y = element.drawForegroundLayer(x, y, stack, mouseX, mouseY); } } } @@ -575,7 +430,7 @@ public class GuiArcaneWorkbench extends GuiContainer { } @Override - public void handleMouseInput() throws IOException{ + public void handleMouseInput() throws IOException { super.handleMouseInput(); int scrollDist = -Mouse.getEventDWheel(); @@ -624,6 +479,477 @@ public class GuiArcaneWorkbench extends GuiContainer { } } + @SubscribeEvent + public static void onTextureStitchEvent(TextureStitchEvent.Pre event){ + event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_CRYSTAL); + event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_UPGRADE); + } + + private abstract static class TooltipElement { + + private final TooltipElement[] children; + private final int spaceAfter; + + public TooltipElement(int spaceAfter, TooltipElement... children){ + this.children = children; + this.spaceAfter = spaceAfter; + } + + // Externally-called methods + + /** + * Returns the height of this tooltip element and its children, with spacing, or 0 if it is not visible. + * @param stack The item stack currently in the central slot of the workbench + * @return The total height of this tooltip element and its children, including spacing + */ + public int getTotalHeight(ItemStack stack){ + if(!this.isVisible(stack)) return 0; + int height = this.getHeight(stack); + for(TooltipElement child : children) height += child.getTotalHeight(stack); + return height + spaceAfter; + } + + /** + * Draws the background layer of this tooltip element and all of its children. + * @param x The x-coordinate of the top left corner of this element + * @param y The y-coordinate of the top left corner of this element + * @param stack The item stack currently in the central slot of the workbench + * @param partialTicks The current partial tick time + * @param mouseX The current x-coordinate of the cursor + * @param mouseY The current y-coordinate of the cursor + * @return The y-coordinate at which the next tooltip element should start + */ + public int drawBackgroundLayer(int x, int y, ItemStack stack, float partialTicks, int mouseX, int mouseY){ + if(!this.isVisible(stack)) return y; + this.drawBackground(x, y, stack, partialTicks, mouseX, mouseY); + y += this.getHeight(stack); + for(TooltipElement child : children) y = child.drawBackgroundLayer(x, y, stack, partialTicks, mouseX, mouseY); + return y + spaceAfter; + } + + /** + * Draws the foreground layer of this tooltip element and all of its children. + * @param x The x-coordinate of the top left corner of this element + * @param y The y-coordinate of the top left corner of this element + * @param stack The item stack currently in the central slot of the workbench + * @param mouseX The current x-coordinate of the cursor + * @param mouseY The current y-coordinate of the cursor + * @return The y-coordinate at which the next tooltip element should start + */ + public int drawForegroundLayer(int x, int y, ItemStack stack, int mouseX, int mouseY){ + if(!this.isVisible(stack)) return y; + this.drawForeground(x, y, stack, mouseX, mouseY); + y += this.getHeight(stack); + for(TooltipElement child : children) y = child.drawForegroundLayer(x, y, stack, mouseX, mouseY); + return y + spaceAfter; + } + + // Abstract internal methods + + /** + * Returns whether this tooltip element should be shown. + * @param stack The item stack currently in the central slot of the workbench + * @return True if this element should be shown, false if not. + */ + protected abstract boolean isVisible(ItemStack stack); + + /** + * Returns the height of this tooltip element. This is for internal implementation. + * @param stack The item stack currently in the central slot of the workbench + * @return The height of this tooltip element, excluding children and spacing. + */ + protected abstract int getHeight(ItemStack stack); + + /** + * Draws the background layer of this tooltip element (excluding children). This is for internal implementation. + * @param x The x-coordinate of the top left corner of this element + * @param y The y-coordinate of the top left corner of this element + * @param stack The item stack currently in the central slot of the workbench + * @param partialTicks The current partial tick time + * @param mouseX The current x-coordinate of the cursor + * @param mouseY The current y-coordinate of the cursor + */ + protected abstract void drawBackground(int x, int y, ItemStack stack, float partialTicks, int mouseX, int mouseY); + + /** + * Draws the foreground layer of this tooltip element (excluding children). This is for internal implementation. + * @param x The x-coordinate of the top left corner of this element + * @param y The y-coordinate of the top left corner of this element + * @param stack The item stack currently in the central slot of the workbench + * @param mouseX The current x-coordinate of the cursor + * @param mouseY The current y-coordinate of the cursor + */ + protected abstract void drawForeground(int x, int y, ItemStack stack, int mouseX, int mouseY); + + } + + private class TooltipElementText extends TooltipElement { + + private final String text; // Can't change the language whilst in a GUI so we can just store the translated text + private final Style style; + + public TooltipElementText(String text, Style style, int spaceAfter, TooltipElement... children){ + super(spaceAfter, children); + this.text = text; + this.style = style; + } + + /** Returns the text for this element. */ + protected String getText(ItemStack stack){ + return text; + } + + protected FontRenderer getFontRenderer(ItemStack stack){ + return fontRenderer; + } + + protected int getColour(ItemStack stack){ + return 0; + } + + @Override + protected boolean isVisible(ItemStack stack){ + return true; // Always visible by default + } + + @Override + protected int getHeight(ItemStack stack){ + return getFontRenderer(stack).listFormattedStringToWidth(getText(stack), TOOLTIP_WIDTH - 2 * TOOLTIP_BORDER) + .size() * getFontRenderer(stack).FONT_HEIGHT; + } + + @Override + protected void drawBackground(int x, int y, ItemStack stack, float partialTicks, int mouseX, int mouseY){ + // Nothing here because this element is only text! + } + + @Override + protected void drawForeground(int x, int y, ItemStack stack, int mouseX, int mouseY){ + for(String line : getFontRenderer(stack).listFormattedStringToWidth(getText(stack), TOOLTIP_WIDTH - 2 * TOOLTIP_BORDER)){ + getFontRenderer(stack).drawStringWithShadow(style.getFormattingCode() + line, x, y, getColour(stack)); + y += getFontRenderer(stack).FONT_HEIGHT; + } + } + } + + private class TooltipElementItemName extends TooltipElementText { + + public TooltipElementItemName(Style style, int spaceAfter){ + super(null, style, spaceAfter); + } + + @Override + protected String getText(ItemStack stack){ + return stack.getDisplayName(); + } + + } + + private class TooltipElementManaReadout extends TooltipElementText { + + public TooltipElementManaReadout(int spaceAfter){ + super(null, new Style().setColor(TextFormatting.BLUE), spaceAfter); + } + + @Override + protected String getText(ItemStack stack){ + return I18n.format("container." + Wizardry.MODID + ":arcane_workbench.mana", + ((IManaStoringItem)stack.getItem()).getMana(stack), + ((IManaStoringItem)stack.getItem()).getManaCapacity(stack)); + } + + @Override + protected boolean isVisible(ItemStack stack){ + return stack.getItem() instanceof IManaStoringItem && ((IManaStoringItem)stack.getItem()).showManaInWorkbench(mc.player, stack); + } + + } + + private class TooltipElementProgressionBar extends TooltipElement { + + private static final int PROGRESSION_BAR_WIDTH = 131; + private static final int PROGRESSION_BAR_HEIGHT = 3; + + public TooltipElementProgressionBar(int spaceAfter){ + super(spaceAfter); + } + + @Override + protected boolean isVisible(ItemStack stack){ + return stack.getItem() instanceof ItemWand && !Wizardry.settings.legacyWandLevelling; + } + + @Override + protected int getHeight(ItemStack stack){ + return fontRenderer.FONT_HEIGHT + LINE_SPACING_NARROW + PROGRESSION_BAR_HEIGHT; + } + + @Override + protected void drawBackground(int x, int y, ItemStack stack, float partialTicks, int mouseX, int mouseY){ + + y += fontRenderer.FONT_HEIGHT + LINE_SPACING_NARROW; + + Tier tier = ((ItemWand)stack.getItem()).tier; // Only called if isVisible() returns true so this is ok + + float progressFraction = 1; + + if(tier != Tier.MASTER){ + progressFraction = (float)WandHelper.getProgression(stack) / Tier.values()[tier.level + 1].progression; + } + + DrawingUtils.drawTexturedRect(x, y, MAIN_GUI_WIDTH, ySize + PROGRESSION_BAR_HEIGHT, PROGRESSION_BAR_WIDTH, PROGRESSION_BAR_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); + int width = (int)(PROGRESSION_BAR_WIDTH * progressFraction); + DrawingUtils.drawTexturedRect(x, y, MAIN_GUI_WIDTH, ySize, width, PROGRESSION_BAR_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT); + } + + @Override + protected void drawForeground(int x, int y, ItemStack stack, int mouseX, int mouseY){ + + Tier tier = ((ItemWand)stack.getItem()).tier; + + fontRenderer.drawStringWithShadow(tier.getDisplayNameWithFormatting(), x, y, 0); + + if(tier != Tier.MASTER){ + Tier nextTier = Tier.values()[tier.level + 1]; + String s = TextFormatting.DARK_GRAY.toString() + nextTier.getDisplayName(); + if(WandHelper.getProgression(stack) >= nextTier.progression) s = nextTier.getDisplayNameWithFormatting(); + fontRenderer.drawStringWithShadow(s, x + TOOLTIP_WIDTH - TOOLTIP_BORDER * 2 - fontRenderer.getStringWidth(s), y, 0); + } + } + + } + + private class TooltipElementSpellList extends TooltipElement { + + public TooltipElementSpellList(int spaceAfter){ + super(spaceAfter, generateSpellEntries(8)); + } + + @Override + protected boolean isVisible(ItemStack stack){ + return stack.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)stack.getItem()).showSpellsInWorkbench(mc.player, stack); + } + + @Override + protected int getHeight(ItemStack stack){ + return 0; // Doesn't have any height of its own + } + + @Override + public int drawBackgroundLayer(int x, int y, ItemStack stack, float partialTicks, int mouseX, int mouseY){ + // It's more efficient to do GL state changes once in here + GlStateManager.enableBlend(); + y = super.drawBackgroundLayer(x, y, stack, partialTicks, mouseX, mouseY); + GlStateManager.disableBlend(); + return y; + } + + @Override + protected void drawBackground(int x, int y, ItemStack stack, float partialTicks, int mouseX, int mouseY){ + // Has no background of its own + } + + @Override + protected void drawForeground(int x, int y, ItemStack stack, int mouseX, int mouseY){ + //Has no text of its own + } + } + + private TooltipElement[] generateSpellEntries(int count){ + TooltipElement[] entries = new TooltipElement[count]; + for(int i=0; i 0; + } + + } + + private class TooltipElementUpgrades extends TooltipElement { + + private static final int ITEM_SIZE = 16; + private static final int ITEM_SPACING = 2; + + public TooltipElementUpgrades(int spaceAfter){ + super(spaceAfter); + } + + @Override + protected boolean isVisible(ItemStack stack){ + return true; // Handled by parent + } + + @Override + protected int getHeight(ItemStack stack){ + int rows = 1 + (WandHelper.getTotalUpgrades(stack) * (ITEM_SIZE + ITEM_SPACING) - ITEM_SPACING) + / (TOOLTIP_WIDTH - TOOLTIP_BORDER * 2); + return rows * (ITEM_SIZE + ITEM_SPACING) - ITEM_SPACING; + } + + @Override + protected void drawBackground(int x, int y, ItemStack stack, float partialTicks, int mouseX, int mouseY){ + + GlStateManager.enableDepth(); + + int x1 = 0; + + // Upgrades + for(Item item : WandHelper.getSpecialUpgrades()){ + + int level = WandHelper.getUpgradeLevel(stack, item); + + if(level > 0){ + + ItemStack upgrade = new ItemStack(item, level); + + itemRender.renderItemAndEffectIntoGUI(upgrade, x + x1, y); + itemRender.renderItemOverlayIntoGUI(fontRenderer, upgrade, x + x1, y, null); + + x1 += ITEM_SIZE + ITEM_SPACING; + + if(x1 + ITEM_SIZE > TOOLTIP_WIDTH - TOOLTIP_BORDER * 2){ + x1 = 0; + y += ITEM_SIZE + ITEM_SPACING; + } + } + } + + GlStateManager.disableDepth(); + GlStateManager.disableLighting(); // Whyyyyyy? + } + + @Override + protected void drawForeground(int x, int y, ItemStack stack, int mouseX, int mouseY){ + + int x1 = 0; + + // Wand upgrade tooltips + for(Item item : WandHelper.getSpecialUpgrades()){ + + int level = WandHelper.getUpgradeLevel(stack, item); + + if(level > 0){ + // The javadoc for isPointInRegion is ambiguous; what it means is that the REGION is + // relative to the GUI but the POINT isn't. + if(isPointInRegion(x + x1, y, ITEM_SIZE, ITEM_SIZE, mouseX, mouseY)){ + ItemStack upgrade = new ItemStack(item, level); + renderToolTip(upgrade, mouseX - guiLeft, mouseY - guiTop); + } + + x1 += ITEM_SIZE + ITEM_SPACING; + + if(TOOLTIP_BORDER * 2 + x1 + ITEM_SIZE > TOOLTIP_WIDTH){ + x1 = 0; + y += ITEM_SIZE + ITEM_SPACING; + } + + } + } + + } + + } + private static class GuiButtonApply extends GuiButton { public GuiButtonApply(int id, int x, int y){ @@ -656,10 +982,4 @@ public class GuiArcaneWorkbench extends GuiContainer { } } - @SubscribeEvent - public static void onTextureStitchEvent(TextureStitchEvent.Pre event){ - event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_CRYSTAL); - event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_UPGRADE); - } - } \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index c2b51ac9..688732dd 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -529,9 +529,8 @@ gui.ebwizardry\:spell_book.mana_cost_undiscovered=Mana Cost: ? gui.ebwizardry\:lectern.title=Spell Lookup container.ebwizardry\:arcane_workbench=Arcane Workbench -container.ebwizardry\:arcane_workbench.apply=Apply -container.ebwizardry\:arcane_workbench.mana=Mana\: -container.ebwizardry\:arcane_workbench.upgrades=Applied Upgrades\: +container.ebwizardry\:arcane_workbench.mana=Mana\: %s/%s +container.ebwizardry\:arcane_workbench.upgrades=Applied Upgrades container.ebwizardry\:arcane_workbench.bookshelves=Bookshelves container.ebwizardry\:arcane_workbench.sort_tier=Sort by Tier diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index ab54b962..e628f760 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -529,9 +529,8 @@ gui.ebwizardry\:spell_book.mana_cost_undiscovered=Mana Cost: ? gui.ebwizardry\:lectern.title=Spell Lookup container.ebwizardry\:arcane_workbench=Arcane Workbench -container.ebwizardry\:arcane_workbench.apply=Apply -container.ebwizardry\:arcane_workbench.mana=Mana\: -container.ebwizardry\:arcane_workbench.upgrades=Applied Upgrades\: +container.ebwizardry\:arcane_workbench.mana=Mana\: %s/%s +container.ebwizardry\:arcane_workbench.upgrades=Applied Upgrades container.ebwizardry\:arcane_workbench.bookshelves=Bookshelves container.ebwizardry\:arcane_workbench.sort_tier=Sort by Tier diff --git a/src/main/resources/assets/ebwizardry/textures/gui/container/arcane_workbench.png b/src/main/resources/assets/ebwizardry/textures/gui/container/arcane_workbench.png index 252ba0a658ed994512ee5df55ad4829e34b26d8e..2261cfa34e1dfda337be3c81fd024f410e0a24a7 100644 GIT binary patch delta 37801 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 RR3Zry^mO%eS?83{1ORQ4j0XS! From 40ed431ee2786762688df073d8d3e82059dec40c Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 10 Apr 2020 16:01:18 +0100 Subject: [PATCH 035/415] Fix crash when throwing stuff with greater telekinesis via commands --- .../java/electroblob/wizardry/spell/GreaterTelekinesis.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/spell/GreaterTelekinesis.java b/src/main/java/electroblob/wizardry/spell/GreaterTelekinesis.java index b5f90f81..1bc1e45b 100644 --- a/src/main/java/electroblob/wizardry/spell/GreaterTelekinesis.java +++ b/src/main/java/electroblob/wizardry/spell/GreaterTelekinesis.java @@ -18,6 +18,7 @@ import net.minecraft.item.EnumAction; import net.minecraft.network.play.server.SPacketEntityVelocity; import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; @@ -86,7 +87,8 @@ public class GreaterTelekinesis extends SpellRay { Vec3d look = caster.getLookVec().scale(getProperty(THROW_VELOCITY).floatValue() * modifiers.get(WizardryItems.range_upgrade)); target.addVelocity(look.x, look.y, look.z); - if(caster instanceof EntityPlayer) caster.swingArm(caster.getActiveHand()); + // No IntelliJ, it's not always false, that's not how polymorphism works + if(caster instanceof EntityPlayer) caster.swingArm(caster.getActiveHand() == null ? EnumHand.MAIN_HAND : caster.getActiveHand()); }else{ From b3fd39c42cbd5909830e3e9ee2983cfdb489199e Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 10 Apr 2020 22:42:31 +0100 Subject: [PATCH 036/415] Add config options for bookshelf blocks and bookshelf search radius --- .../java/electroblob/wizardry/Settings.java | 54 ++++++++++++++++++- .../wizardry/block/BlockBookshelf.java | 7 +-- .../wizardry/packet/PacketSyncSettings.java | 23 ++++++++ .../assets/ebwizardry/lang/en_gb.lang | 4 ++ .../assets/ebwizardry/lang/en_us.lang | 4 ++ 5 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index 4d2ebf30..0d607205 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -278,6 +278,23 @@ public final class Settings { public boolean replaceVanillaFireballs = true; /** [Synchronised] Chance of 'misreading' an undiscovered spell and triggering a forfeit instead. */ public double forfeitChance = 0.2; + /** + * [Synchronised] The maximum number of blocks a bookshelf can be from an arcane workbench or lectern to be + * able to link to it. + */ + public int bookshelfSearchRadius = 4; + /** + * [Synchronised] List of registry names of blocks that count as bookshelves for the arcane workbench and + * lectern. + */ + public Pair[] bookshelfBlocks = parseItemMetaStrings( + Wizardry.MODID + ":oak_bookshelf", + Wizardry.MODID + ":spruce_bookshelf", + Wizardry.MODID + ":birch_bookshelf", + Wizardry.MODID + ":jungle_bookshelf", + Wizardry.MODID + ":acacia_bookshelf", + Wizardry.MODID + ":dark_oak_bookshelf" + ); // 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. @@ -708,6 +725,21 @@ public final class Settings { bowItemWhitelist = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); + property = config.get(TWEAKS_CATEGORY, "bookshelfBlocks", new String[0], "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. " + Wizardry.MODID + ":oak_bookshelf)."); + property.setLanguageKey("config." + Wizardry.MODID + ".bookshelf_blocks"); + property.setRequiresWorldRestart(true); + bookshelfBlocks = 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); + property.setLanguageKey("config." + Wizardry.MODID + ".bookshelf_search_radius"); + Wizardry.proxy.setToNumberSliderEntry(property); + property.setRequiresMcRestart(true); + bookshelfSearchRadius = property.getInt(); + propOrder.add(property.getName()); + property = config.get(TWEAKS_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade.", Pattern.compile("[A-Za-z0-9:_]+ [0-9]+")); property.setLanguageKey("config." + Wizardry.MODID + ".currency_items"); @@ -1119,7 +1151,7 @@ public final class Settings { return Arrays.stream(strings).map(s -> new ResourceLocation(s.toLowerCase(Locale.ROOT).trim())).toArray(ResourceLocation[]::new); } - /** Applies {@link Settings#parseItemMetaString(String)} to each input string and returns and array of the resulting + /** Applies {@link Settings#parseItemMetaString(String)} to each input string and returns an array of the resulting * {@link Pair}s. */ @SuppressWarnings("unchecked") // Shut up java public static Pair[] parseItemMetaStrings(String... strings){ @@ -1147,14 +1179,34 @@ public final class Settings { return Pair.of(new ResourceLocation(item), meta); } + /** + * Checks a metadata-sensitive list option (see {@link Settings#parseItemMetaStrings(String...)} for the given block. + * @param array The config option to check + * @param block The block state to search for + * @return True if the given array contains an entry that matches the given block, false if not. + */ public static boolean containsMetaBlock(Pair[] array, IBlockState block){ return containsMetaThing(array, block.getBlock().getRegistryName(), (short)block.getBlock().getMetaFromState(block)); } + /** + * Checks a metadata-sensitive list option (see {@link Settings#parseItemMetaStrings(String...)} for the given item. + * @param array The config option to check + * @param stack An item stack with the item and metadata to search for + * @return True if the given array contains an entry that matches the given stack, false if not. + */ public static boolean containsMetaItem(Pair[] array, ItemStack stack){ return containsMetaThing(array, stack.getItem().getRegistryName(), (short)stack.getMetadata()); } + /** + * Checks a metadata-sensitive list option (see {@link Settings#parseItemMetaStrings(String...)} for the given + * id/metadata pair. + * @param array The config option to check + * @param id The id to search for + * @param metadata The metadata value to search for + * @return True if the given array contains the given id/metadata pair, or the given id paired with the wildcard value. + */ public static boolean containsMetaThing(Pair[] array, ResourceLocation id, short metadata){ return Arrays.asList(array).contains(Pair.of(id, metadata)) || Arrays.asList(array).contains(Pair.of(id, OreDictionary.WILDCARD_VALUE)); } diff --git a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java index ee4de190..42d6317e 100644 --- a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java +++ b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java @@ -1,5 +1,6 @@ package electroblob.wizardry.block; +import electroblob.wizardry.Settings; import electroblob.wizardry.Wizardry; import electroblob.wizardry.WizardryGuiHandler; import electroblob.wizardry.registry.WizardryTabs; @@ -150,15 +151,15 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid List bookshelves = new ArrayList<>(); - int searchRadius = 4; // TODO: Config option for this + int searchRadius = Wizardry.settings.bookshelfSearchRadius; for(int x = -searchRadius; x <= searchRadius; x++){ for(int y = -searchRadius; y <= searchRadius; y++){ for(int z = -searchRadius; z <= searchRadius; z++){ BlockPos pos = centre.add(x, y, z); - // TODO: Config option for allowed containers - if(world.getBlockState(pos).getBlock() instanceof BlockBookshelf){ + + if(Settings.containsMetaBlock(Wizardry.settings.bookshelfBlocks, world.getBlockState(pos))){ TileEntity te = world.getTileEntity(pos); if(te instanceof IInventory && !ArrayUtils.contains(exclude, te)) bookshelves.add((IInventory)te); } diff --git a/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java b/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java index 6a408b81..55958461 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java +++ b/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java @@ -4,9 +4,15 @@ import electroblob.wizardry.Settings; import electroblob.wizardry.Wizardry; import electroblob.wizardry.packet.PacketSyncSettings.Message; import io.netty.buffer.ByteBuf; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.network.ByteBufUtils; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.ArrayList; +import java.util.List; /** * [Server -> Client] This packet is sent to synchronise the config settings with clients on player login. @@ -31,7 +37,10 @@ public class PacketSyncSettings implements IMessageHandler { Wizardry.settings.discoveryMode = message.settings.discoveryMode; Wizardry.settings.creativeBypassesArcaneLock = message.settings.creativeBypassesArcaneLock; Wizardry.settings.slowTimeAffectsPlayers = message.settings.slowTimeAffectsPlayers; + Wizardry.settings.replaceVanillaFireballs = message.settings.replaceVanillaFireballs; Wizardry.settings.forfeitChance = message.settings.forfeitChance; + Wizardry.settings.bookshelfSearchRadius = message.settings.bookshelfSearchRadius; + Wizardry.settings.bookshelfBlocks = message.settings.bookshelfBlocks; } public static class Message implements IMessage { @@ -48,6 +57,7 @@ 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. @@ -58,6 +68,13 @@ public class PacketSyncSettings implements IMessageHandler { settings.slowTimeAffectsPlayers = buf.readBoolean(); 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.slowTimeAffectsPlayers); 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){ + ByteBufUtils.writeUTF8String(buf, entry.getLeft().toString()); + buf.writeShort(entry.getRight()); + } } } } diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 688732dd..d23663ff 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -1200,6 +1200,10 @@ config.ebwizardry.sword_item_whitelist=Sword Item Whitelist config.ebwizardry.sword_item_whitelist.tooltip=List of registry names of items which should count as swords for imbuement spells. Most swords 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:broadsword). 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.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 config.ebwizardry.currency_items.tooltip=List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade. diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index e628f760..2e43452c 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -1200,6 +1200,10 @@ config.ebwizardry.sword_item_whitelist=Sword Item Whitelist config.ebwizardry.sword_item_whitelist.tooltip=List of registry names of items which should count as swords for imbuement spells. Most swords 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:broadsword). 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.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 config.ebwizardry.currency_items.tooltip=List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade. From 6f0636da9f2bc9b32ba1bacfd2d746f2bbe4016d Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 10 Apr 2020 22:49:40 +0100 Subject: [PATCH 037/415] These don't need world restarts! Also I forgot the default values for bookshelf blocks... --- src/main/java/electroblob/wizardry/Settings.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index 0d607205..c504adef 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -696,38 +696,32 @@ public final class Settings { property = config.get(TWEAKS_CATEGORY, "mindControlTargetsBlacklist", new String[]{}, "List of names of entities which cannot be mind controlled, in addition to the defaults. Add creatures to this list if allowing them to be mind-controlled causes problems or could be exploited. SoundLoopSpellEntity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. " + Wizardry.MODID + ":wizard)."); property.setLanguageKey("config." + Wizardry.MODID + ".mind_control_targets_blacklist"); - property.setRequiresWorldRestart(true); mindControlTargetsBlacklist = getResourceLocationList(property); propOrder.add(property.getName()); property = config.get(TWEAKS_CATEGORY, "pocketFurnaceItemBlacklist", new String[]{"cobblestone", "netherrack"}, "List of registry names of blocks or items which cannot be smelted by the pocket furnace spell, in addition to armour, tools and weapons. Block/item names are not case sensitive. For mod items, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore)."); property.setLanguageKey("config." + Wizardry.MODID + ".pocket_furnace_item_blacklist"); - property.setRequiresWorldRestart(true); pocketFurnaceItemBlacklist = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); property = config.get(TWEAKS_CATEGORY, "divinationOreWhitelist", new String[0], "List of registry names of ore blocks which can be detected by the divination spell. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore)."); property.setLanguageKey("config." + Wizardry.MODID + ".divination_ore_whitelist"); - property.setRequiresWorldRestart(true); divinationOreWhitelist = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); property = config.get(TWEAKS_CATEGORY, "swordItemWhitelist", new String[0], "List of registry names of items which should count as swords for imbuement spells. Most swords 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:broadsword)."); property.setLanguageKey("config." + Wizardry.MODID + ".sword_item_whitelist"); - property.setRequiresWorldRestart(true); swordItemWhitelist = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); property = config.get(TWEAKS_CATEGORY, "bowItemWhitelist", new String[0], "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)."); property.setLanguageKey("config." + Wizardry.MODID + ".bow_item_whitelist"); - property.setRequiresWorldRestart(true); bowItemWhitelist = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); - property = config.get(TWEAKS_CATEGORY, "bookshelfBlocks", new String[0], "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. " + Wizardry.MODID + ":oak_bookshelf)."); + property = config.get(TWEAKS_CATEGORY, "bookshelfBlocks", new String[]{Wizardry.MODID + ":oak_bookshelf", Wizardry.MODID + ":spruce_bookshelf", Wizardry.MODID + ":birch_bookshelf", Wizardry.MODID + ":jungle_bookshelf", Wizardry.MODID + ":acacia_bookshelf", Wizardry.MODID + ":dark_oak_bookshelf"}, "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. " + Wizardry.MODID + ":oak_bookshelf)."); property.setLanguageKey("config." + Wizardry.MODID + ".bookshelf_blocks"); - property.setRequiresWorldRestart(true); bookshelfBlocks = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); @@ -736,14 +730,12 @@ public final class Settings { 1, 10); property.setLanguageKey("config." + Wizardry.MODID + ".bookshelf_search_radius"); Wizardry.proxy.setToNumberSliderEntry(property); - property.setRequiresMcRestart(true); bookshelfSearchRadius = property.getInt(); propOrder.add(property.getName()); property = config.get(TWEAKS_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade.", Pattern.compile("[A-Za-z0-9:_]+ [0-9]+")); property.setLanguageKey("config." + Wizardry.MODID + ".currency_items"); - property.setRequiresWorldRestart(true); propOrder.add(property.getName()); currencyItems = new HashMap<>(); for(String string : property.getStringList()){ From b5f61205eb2f9ec4f1519b66b777f86732150714 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 10 Apr 2020 23:42:05 +0100 Subject: [PATCH 038/415] Implement custom search behaviour for the spells tab, so it works like the bookshelf interface --- .../client/WizardryClientEventHandler.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java index aacade75..e41802a0 100644 --- a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java +++ b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java @@ -13,17 +13,21 @@ import electroblob.wizardry.potion.PotionSlowTime; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardryTabs; import electroblob.wizardry.spell.*; import electroblob.wizardry.util.RayTracer; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiMerchant; +import net.minecraft.client.gui.GuiTextField; +import net.minecraft.client.gui.inventory.GuiContainerCreative; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityArmorStand; @@ -37,13 +41,16 @@ import net.minecraft.village.MerchantRecipe; import net.minecraft.world.World; import net.minecraftforge.client.event.*; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.ObfuscationReflectionHelper; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.relauncher.Side; import org.lwjgl.opengl.GL11; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; +import java.util.Locale; /** * Event handler responsible for client-side only events, mostly rendering. @@ -72,6 +79,15 @@ public final class WizardryClientEventHandler { /** The remaining time for which the screen shake effect will be active. */ private static int screenShakeCounter = 0; private static final float SHAKINESS = 0.5f; + + /** Reflected into {@code GuiContainerCreative#searchField} */ + private static final Field searchField; + + private static GuiTextField currentSearchField = null; + + static { + searchField = ObfuscationReflectionHelper.findField(GuiContainerCreative.class, "field_147062_A"); + } /** Starts the first-person blink overlay effect. */ public static void playBlinkEffect(){ @@ -254,6 +270,42 @@ public final class WizardryClientEventHandler { } } + @SubscribeEvent + public static void onInitGuiEvent(GuiScreenEvent.InitGuiEvent.Post event){ + // Reduces reflection as much as possible - searchField is created on GUI init and never reassigned so we need + // not (and should not!) use reflection to retrieve it every time a key is typed + if(event.getGui() instanceof GuiContainerCreative){ + try{ + currentSearchField = (GuiTextField)searchField.get(event.getGui()); + }catch(IllegalAccessException e){ + e.printStackTrace(); + } + } + } + + @SubscribeEvent + public static void onKeyboardInputEvent(GuiScreenEvent.KeyboardInputEvent.Post event){ + // Custom creative tab search behaviour + if(event.getGui() instanceof GuiContainerCreative){ + + GuiContainerCreative gui = (GuiContainerCreative)event.getGui(); + CreativeTabs tab = CreativeTabs.CREATIVE_TAB_ARRAY[gui.getSelectedTabIndex()]; + + if(tab == WizardryTabs.SPELLS){ + + GuiContainerCreative.ContainerCreative container = (GuiContainerCreative.ContainerCreative)gui.inventorySlots; + container.itemList.clear(); + String searchText = currentSearchField.getText().toLowerCase(Locale.ROOT); + tab.displayAllRelevantItems(container.itemList); + + if(!searchText.isEmpty()){ + container.itemList.removeIf(s -> !Spell.byMetadata(s.getMetadata()).matches(searchText)); + container.scrollTo(0); + } + } + } + } + @SubscribeEvent public static void onRenderLivingEvent(RenderLivingEvent.Post event){ From e8c1b4d5819a767f6a4e77fa4230a5c2b90f8cf5 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 10 Apr 2020 23:55:54 +0100 Subject: [PATCH 039/415] All the synchronised settings require world restart --- src/main/java/electroblob/wizardry/Settings.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index c504adef..3fb74e10 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -519,6 +519,7 @@ public final class Settings { "Whether players are slowed when another nearby player uses the slow time spell. If this is false, mobs and projectiles will still be affected but players will move at normal speed."); property.setLanguageKey("config." + Wizardry.MODID + ".slow_time_affects_players"); Wizardry.proxy.setToNamedBooleanEntry(property); + property.setRequiresWorldRestart(true); slowTimeAffectsPlayers = property.getBoolean(); propOrder.add(property.getName()); @@ -526,6 +527,7 @@ public final class Settings { "Whether any player in creative mode can bypass arcane-locked blocks. If this is false, players must also be op in order to do so."); property.setLanguageKey("config." + Wizardry.MODID + ".creative_bypasses_arcane_lock"); Wizardry.proxy.setToNamedBooleanEntry(property); + property.setRequiresWorldRestart(true); creativeBypassesArcaneLock = property.getBoolean(); propOrder.add(property.getName()); @@ -582,6 +584,7 @@ public final class Settings { 0, 1); property.setLanguageKey("config." + Wizardry.MODID + ".forfeit_chance"); Wizardry.proxy.setToNumberSliderEntry(property); + property.setRequiresWorldRestart(true); forfeitChance = property.getDouble(); propOrder.add(property.getName()); @@ -643,6 +646,7 @@ public final class Settings { "Whether to replace Minecraft's own fireballs with wizardry fireballs. If this is disabled, only wizardry spells will use the custom fireballs."); property.setLanguageKey("config." + Wizardry.MODID + ".replace_vanilla_fireballs"); Wizardry.proxy.setToNamedBooleanEntry(property); + property.setRequiresWorldRestart(true); replaceVanillaFireballs = property.getBoolean(); propOrder.add(property.getName()); @@ -722,6 +726,7 @@ public final class Settings { property = config.get(TWEAKS_CATEGORY, "bookshelfBlocks", new String[]{Wizardry.MODID + ":oak_bookshelf", Wizardry.MODID + ":spruce_bookshelf", Wizardry.MODID + ":birch_bookshelf", Wizardry.MODID + ":jungle_bookshelf", Wizardry.MODID + ":acacia_bookshelf", Wizardry.MODID + ":dark_oak_bookshelf"}, "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. " + Wizardry.MODID + ":oak_bookshelf)."); property.setLanguageKey("config." + Wizardry.MODID + ".bookshelf_blocks"); + property.setRequiresWorldRestart(true); bookshelfBlocks = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); @@ -730,6 +735,7 @@ public final class Settings { 1, 10); property.setLanguageKey("config." + Wizardry.MODID + ".bookshelf_search_radius"); Wizardry.proxy.setToNumberSliderEntry(property); + property.setRequiresWorldRestart(true); bookshelfSearchRadius = property.getInt(); propOrder.add(property.getName()); From 1d161e613c2863ce265ebd894cb0e1b6f869a56a Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 10 Apr 2020 23:56:00 +0100 Subject: [PATCH 040/415] Comments --- .../wizardry/client/WizardryClientEventHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java index e41802a0..b56b8cad 100644 --- a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java +++ b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java @@ -294,13 +294,13 @@ public final class WizardryClientEventHandler { if(tab == WizardryTabs.SPELLS){ GuiContainerCreative.ContainerCreative container = (GuiContainerCreative.ContainerCreative)gui.inventorySlots; - container.itemList.clear(); + container.itemList.clear(); // Required or duplicates will appear! String searchText = currentSearchField.getText().toLowerCase(Locale.ROOT); tab.displayAllRelevantItems(container.itemList); if(!searchText.isEmpty()){ container.itemList.removeIf(s -> !Spell.byMetadata(s.getMetadata()).matches(searchText)); - container.scrollTo(0); + container.scrollTo(0); // Seems to refresh the GUI somehow so it displays correctly } } } From 62540200247c9d917ee05f3ecc0141ab314cd92b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 11 Apr 2020 00:12:07 +0100 Subject: [PATCH 041/415] Ugh just why --- src/main/java/electroblob/wizardry/Settings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index 3fb74e10..ba446aca 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -1206,7 +1206,7 @@ public final class Settings { * @return True if the given array contains the given id/metadata pair, or the given id paired with the wildcard value. */ public static boolean containsMetaThing(Pair[] array, ResourceLocation id, short metadata){ - return Arrays.asList(array).contains(Pair.of(id, metadata)) || Arrays.asList(array).contains(Pair.of(id, OreDictionary.WILDCARD_VALUE)); + return Arrays.asList(array).contains(Pair.of(id, metadata)) || Arrays.asList(array).contains(Pair.of(id, (short)OreDictionary.WILDCARD_VALUE)); } } From c6221588182ae62957d1aeb78c04f97231fbf0b7 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 12 Apr 2020 18:35:30 +0100 Subject: [PATCH 042/415] Fix syncing of everything --- .../electroblob/wizardry/CommonProxy.java | 15 +++++ .../wizardry/block/BlockBookshelf.java | 62 +++++++++++++++++++ .../wizardry/client/ClientProxy.java | 16 +++++ .../wizardry/client/gui/GuiLectern.java | 12 ++-- .../client/model/BakedModelBookshelf.java | 4 +- .../inventory/ContainerArcaneWorkbench.java | 10 +-- .../tileentity/TileEntityArcaneWorkbench.java | 19 +++--- .../tileentity/TileEntityBookshelf.java | 20 +++--- .../tileentity/TileEntityLectern.java | 2 +- 9 files changed, 124 insertions(+), 36 deletions(-) diff --git a/src/main/java/electroblob/wizardry/CommonProxy.java b/src/main/java/electroblob/wizardry/CommonProxy.java index 947a6e93..5d79bd80 100644 --- a/src/main/java/electroblob/wizardry/CommonProxy.java +++ b/src/main/java/electroblob/wizardry/CommonProxy.java @@ -1,5 +1,7 @@ package electroblob.wizardry; +import electroblob.wizardry.block.BlockBookshelf; +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; import electroblob.wizardry.item.ItemSpectralBow; import electroblob.wizardry.packet.*; import electroblob.wizardry.registry.WizardryItems; @@ -261,4 +263,17 @@ public class CommonProxy { public Set getSpellHUDSkins(){ return null; } + + /** Notifies nearby players of a bookshelf change, causing any lectern or arcane workbench GUI (client-side) or + * container (both sides) to refresh its linked bookshelves (does not send packets). */ + public void notifyBookshelfChange(World world, BlockPos pos){ + for(EntityPlayer player : world.playerEntities){ + if(player.getDistanceSq(pos) < BlockBookshelf.PLAYER_NOTIFY_RANGE * BlockBookshelf.PLAYER_NOTIFY_RANGE){ + if(player.openContainer instanceof ContainerArcaneWorkbench){ + ((ContainerArcaneWorkbench)player.openContainer).refreshBookshelfSlots(); + } + } + } + } + } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java index 42d6317e..2c50e8b5 100644 --- a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java +++ b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java @@ -12,6 +12,7 @@ import net.minecraft.block.properties.PropertyBool; 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.inventory.IInventory; @@ -19,19 +20,30 @@ import net.minecraft.inventory.InventoryHelper; 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.math.BlockPos; import net.minecraft.world.IBlockAccess; +import net.minecraft.world.IWorldEventListener; import net.minecraft.world.World; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.Properties; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import org.apache.commons.lang3.ArrayUtils; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; +@Mod.EventBusSubscriber public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvider { + /** When a bookshelf block (of any kind specified in the config) is added or removed, players within this range will + * be notified of the change. */ + public static final double PLAYER_NOTIFY_RANGE = 32; public static final int SLOT_COUNT = 12; public static final UnlistedPropertyBool[] BOOKS = new UnlistedPropertyBool[SLOT_COUNT]; @@ -65,6 +77,11 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid return this.getDefaultState().withProperty(FACING, placer.getHorizontalFacing().getOpposite()); } + @Override + public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state){ + super.onBlockAdded(worldIn, pos, state); + } + @Override public void breakBlock(World world, BlockPos pos, IBlockState block){ @@ -172,4 +189,49 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid } + @SubscribeEvent + public static void onWorldLoadEvent(WorldEvent.Load event){ + event.getWorld().addEventListener(Listener.instance); + } + + @SubscribeEvent + public static void onWorldUnloadEvent(WorldEvent.Unload event){ + event.getWorld().removeEventListener(Listener.instance); + } + + public static class Listener implements IWorldEventListener { + + public static final Listener instance = new Listener(); + + private Listener(){} + + @Override + public void notifyBlockUpdate(World world, BlockPos pos, IBlockState oldState, IBlockState newState, int flags){ + + if(oldState == newState) return; // Probably won't happen but just in case + + if(Settings.containsMetaBlock(Wizardry.settings.bookshelfBlocks, oldState) // Bookshelf removed + || Settings.containsMetaBlock(Wizardry.settings.bookshelfBlocks, newState)){ // Bookshelf placed + // It is also possible (with commands) for a bookshelf to be replaced with another bookshelf, in which + // case this should still just be called once + Wizardry.proxy.notifyBookshelfChange(world, pos); + } + + } + + // Dummy implementations + @Override public void notifyLightSet(BlockPos pos){} + @Override public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2){} + @Override public void playSoundToAllNearExcept(@Nullable EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch){} + @Override public void playRecord(SoundEvent soundIn, BlockPos pos){} + @Override public void spawnParticle(int particleID, boolean ignoreRange, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int... parameters){} + @Override public void spawnParticle(int id, boolean ignoreRange, boolean minimiseParticleLevel, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, int... parameters){} + @Override public void onEntityAdded(Entity entityIn){} + @Override public void onEntityRemoved(Entity entityIn){} + @Override public void broadcastSound(int soundID, BlockPos pos, int data){} + @Override public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data){} + @Override public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress){} + + } + } diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 64e746d7..df4b4a80 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -2,9 +2,11 @@ package electroblob.wizardry.client; import electroblob.wizardry.CommonProxy; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.block.BlockBookshelf; import electroblob.wizardry.client.audio.MovingSoundEntity; import electroblob.wizardry.client.audio.SoundLoop; import electroblob.wizardry.client.audio.SoundLoopSpell; +import electroblob.wizardry.client.gui.GuiLectern; import electroblob.wizardry.client.gui.GuiSpellDisplay; import electroblob.wizardry.client.gui.config.NamedBooleanEntry; import electroblob.wizardry.client.gui.config.SpellHUDSkinChooserEntry; @@ -202,6 +204,20 @@ public class ClientProxy extends CommonProxy { return GuiSpellDisplay.getSkinKeys(); } + @Override + public void notifyBookshelfChange(World world, BlockPos pos){ + + super.notifyBookshelfChange(world, pos); + + EntityPlayer player = Minecraft.getMinecraft().player; + + if(player.getDistanceSq(pos) < BlockBookshelf.PLAYER_NOTIFY_RANGE * BlockBookshelf.PLAYER_NOTIFY_RANGE){ + if(Minecraft.getMinecraft().currentScreen instanceof GuiLectern){ + ((GuiLectern)Minecraft.getMinecraft().currentScreen).refreshAvailableSpells(); + } + } + } + // SECTION Items // =============================================================================================================== diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java b/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java index c795d842..4a6571f4 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiLectern.java @@ -127,8 +127,6 @@ public class GuiLectern extends GuiSpellInfo implements ISpellSortable { super.initGui(); - updateAvailableSpells(); - final int left = this.width / 2 - this.xSize / 2; final int top = this.height / 2 - this.ySize / 2; @@ -181,8 +179,7 @@ public class GuiLectern extends GuiSpellInfo implements ISpellSortable { this.searchField.setCanLoseFocus(false); this.searchField.setFocused(true); - updateMatchingSpells(); - updateButtonVisiblity(); + refreshAvailableSpells(); // Must be done last } @@ -383,9 +380,9 @@ public class GuiLectern extends GuiSpellInfo implements ISpellSortable { .collect(Collectors.toList()); } - // TODO: Call this when a bookshelf is added or removed // TODO: Config option to always display all spells when in creative - private void updateAvailableSpells(){ + /** Called on initialisation and whenever a bookshelf is added or removed, to update the list of spells. */ + public void refreshAvailableSpells(){ availableSpells.clear(); @@ -405,6 +402,9 @@ public class GuiLectern extends GuiSpellInfo implements ISpellSortable { if(!availableSpells.contains(currentSpell)) currentSpell = Spells.none; // TODO: Do we want this? + updateMatchingSpells(); + updateButtonVisiblity(); + } private class GuiButtonLocateBook extends GuiButton { diff --git a/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java b/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java index 4e682688..c100b369 100644 --- a/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java +++ b/src/main/java/electroblob/wizardry/client/model/BakedModelBookshelf.java @@ -36,7 +36,9 @@ public class BakedModelBookshelf implements IBakedModel { IExtendedBlockState extendedState = (IExtendedBlockState)state; for(int i = 0; i getInventoryStackLimit()){ stack.setCount(getInventoryStackLimit()); @@ -199,12 +202,10 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, NBTTagList itemList = new NBTTagList(); for(int i = 0; i < getSizeInventory(); i++){ ItemStack stack = getStackInSlot(i); - if(!stack.isEmpty()){ - NBTTagCompound tag = new NBTTagCompound(); - tag.setByte("Slot", (byte)i); - stack.writeToNBT(tag); - itemList.appendTag(tag); - } + NBTTagCompound tag = new NBTTagCompound(); + tag.setByte("Slot", (byte)i); + stack.writeToNBT(tag); + itemList.appendTag(tag); } NBTExtras.storeTagSafely(tagCompound, "Inventory", itemList); diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java index ce513635..c469e62d 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java @@ -30,7 +30,7 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick /** Called to manually sync the tile entity with clients. */ public void sync(){ - this.world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); + this.world.markAndNotifyBlock(pos, null, world.getBlockState(pos), world.getBlockState(pos), 3); } @Override @@ -82,16 +82,15 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick @Override public void setInventorySlotContents(int slot, ItemStack stack){ - - inventory.set(slot, stack); - //ItemStack previous = inventory.set(slot, stack); + ItemStack previous = inventory.set(slot, stack); - //if(previous.isEmpty() != stack.isEmpty()) this.sync(); + if(previous.isEmpty() != stack.isEmpty()) this.sync(); if(!stack.isEmpty() && stack.getCount() > getInventoryStackLimit()){ stack.setCount(getInventoryStackLimit()); } + } @Override @@ -157,12 +156,10 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick NBTTagList itemList = new NBTTagList(); for(int i = 0; i < getSizeInventory(); i++){ ItemStack stack = getStackInSlot(i); - if(!stack.isEmpty()){ - NBTTagCompound tag = new NBTTagCompound(); - tag.setByte("Slot", (byte)i); - stack.writeToNBT(tag); - itemList.appendTag(tag); - } + NBTTagCompound tag = new NBTTagCompound(); + tag.setByte("Slot", (byte)i); + stack.writeToNBT(tag); + itemList.appendTag(tag); } NBTExtras.storeTagSafely(tagCompound, "Inventory", itemList); @@ -182,6 +179,7 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick @Override public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt){ readFromNBT(pkt.getNbtCompound()); + Wizardry.proxy.notifyBookshelfChange(world, pos); } // What are all these for? diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityLectern.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityLectern.java index 2cbdd8e6..bcaded49 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityLectern.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityLectern.java @@ -64,7 +64,7 @@ public class TileEntityLectern extends TileEntity implements ITickable { /** Called to manually sync the tile entity with clients. */ public void sync(){ - this.world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); + this.world.markAndNotifyBlock(pos, null, world.getBlockState(pos), world.getBlockState(pos), 3); } @Override From e8ca7735573f597d33a929877ccfdbf69aafa571 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 12 Apr 2020 21:59:22 +0100 Subject: [PATCH 043/415] Don't sync when loading from NBT --- .../wizardry/inventory/ContainerBookshelf.java | 4 +--- .../tileentity/TileEntityArcaneWorkbench.java | 11 ++++++++++- .../wizardry/tileentity/TileEntityBookshelf.java | 11 ++++++++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java b/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java index abe711df..92520dea 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java @@ -35,13 +35,11 @@ public class ContainerBookshelf extends Container { } } - this.onSlotChanged(); - } /** Called from individual slots when their item is changed or removed. */ public void onSlotChanged(){ - this.tileentity.sync(); + //this.tileentity.sync(); } @Override diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java index 7f8125e6..ef18dcc9 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java @@ -37,6 +37,8 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, /** Controls the rotating rune and floating wand animations. */ public float timer = 0; + private boolean doNotSync; + public TileEntityArcaneWorkbench(){ inventory = NonNullList.withSize(ContainerArcaneWorkbench.UPGRADE_SLOT + 1, ItemStack.EMPTY); } @@ -48,7 +50,7 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, /** Called to manually sync the tile entity with clients. */ public void sync(){ - this.world.markAndNotifyBlock(pos, null, world.getBlockState(pos), world.getBlockState(pos), 3); + if(!doNotSync) this.world.markAndNotifyBlock(pos, null, world.getBlockState(pos), world.getBlockState(pos), 3); } @Override @@ -117,6 +119,7 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, ItemStack previous = inventory.set(slot, stack); // Only the central slot affects the in-world rendering, so only sync if that changes + // This must be done in the tile entity because containers only exist for player interaction, not hoppers etc. if(slot == ContainerArcaneWorkbench.CENTRE_SLOT && previous.isEmpty() != stack.isEmpty()) this.sync(); if(!stack.isEmpty() && stack.getCount() > getInventoryStackLimit()){ @@ -182,6 +185,10 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, @Override public void readFromNBT(NBTTagCompound tagCompound){ + // Prevent sync() happening when loading from NBT or weirdness ensues when loading a world + // Normally I'd pass this as a flag to setInventorySlotContents but we can't change the method signature + this.doNotSync = true; + super.readFromNBT(tagCompound); NBTTagList tagList = tagCompound.getTagList("Inventory", NBT.TAG_COMPOUND); @@ -192,6 +199,8 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, setInventorySlotContents(slot, new ItemStack(tag)); } } + + this.doNotSync = false; } @Override diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java index c469e62d..7b70b5f7 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java @@ -24,13 +24,15 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick /** The inventory of the bookshelf. */ private NonNullList inventory; + private boolean doNotSync; + public TileEntityBookshelf(){ inventory = NonNullList.withSize(BlockBookshelf.SLOT_COUNT, ItemStack.EMPTY); } /** Called to manually sync the tile entity with clients. */ public void sync(){ - this.world.markAndNotifyBlock(pos, null, world.getBlockState(pos), world.getBlockState(pos), 3); + if(!doNotSync) this.world.markAndNotifyBlock(pos, null, world.getBlockState(pos), world.getBlockState(pos), 3); } @Override @@ -85,6 +87,7 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick ItemStack previous = inventory.set(slot, stack); + // This must be done in the tile entity because containers only exist for player interaction, not hoppers etc. if(previous.isEmpty() != stack.isEmpty()) this.sync(); if(!stack.isEmpty() && stack.getCount() > getInventoryStackLimit()){ @@ -136,6 +139,10 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick @Override public void readFromNBT(NBTTagCompound tagCompound){ + // Prevent sync() happening when loading from NBT or weirdness ensues when loading a world + // Normally I'd pass this as a flag to setInventorySlotContents but we can't change the method signature + this.doNotSync = true; + super.readFromNBT(tagCompound); NBTTagList tagList = tagCompound.getTagList("Inventory", NBT.TAG_COMPOUND); @@ -146,6 +153,8 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick setInventorySlotContents(slot, new ItemStack(tag)); } } + + this.doNotSync = false; } @Override From 8682fcbe9162136e2c743e91ffa83a04f48194d8 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 12 Apr 2020 22:51:24 +0100 Subject: [PATCH 044/415] Only prevent syncing when first loading the world --- .../tileentity/TileEntityArcaneWorkbench.java | 11 +++++------ .../wizardry/tileentity/TileEntityBookshelf.java | 12 +++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java index ef18dcc9..a93acf55 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java @@ -41,6 +41,9 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, public TileEntityArcaneWorkbench(){ inventory = NonNullList.withSize(ContainerArcaneWorkbench.UPGRADE_SLOT + 1, ItemStack.EMPTY); + // Prevent sync() happening when loading from NBT the first time or weirdness ensues when loading a world + // Normally I'd pass this as a flag to setInventorySlotContents but we can't change the method signature + this.doNotSync = true; } @Override @@ -56,6 +59,8 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, @Override public void update(){ + this.doNotSync = false; + ItemStack stack = this.getStackInSlot(ContainerArcaneWorkbench.CENTRE_SLOT); // Decrements wand damage (increases mana) every 1.5 seconds if it has a condenser upgrade @@ -185,10 +190,6 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, @Override public void readFromNBT(NBTTagCompound tagCompound){ - // Prevent sync() happening when loading from NBT or weirdness ensues when loading a world - // Normally I'd pass this as a flag to setInventorySlotContents but we can't change the method signature - this.doNotSync = true; - super.readFromNBT(tagCompound); NBTTagList tagList = tagCompound.getTagList("Inventory", NBT.TAG_COMPOUND); @@ -199,8 +200,6 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, setInventorySlotContents(slot, new ItemStack(tag)); } } - - this.doNotSync = false; } @Override diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java index 7b70b5f7..6d3565a7 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java @@ -28,15 +28,19 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick public TileEntityBookshelf(){ inventory = NonNullList.withSize(BlockBookshelf.SLOT_COUNT, ItemStack.EMPTY); + // Prevent sync() happening when loading from NBT or weirdness ensues when loading a world + // Normally I'd pass this as a flag to setInventorySlotContents but we can't change the method signature + this.doNotSync = true; } /** Called to manually sync the tile entity with clients. */ public void sync(){ - if(!doNotSync) this.world.markAndNotifyBlock(pos, null, world.getBlockState(pos), world.getBlockState(pos), 3); + if(!this.doNotSync) this.world.markAndNotifyBlock(pos, null, world.getBlockState(pos), world.getBlockState(pos), 3); } @Override public void update(){ + this.doNotSync = false; // Nothing here for now } @@ -139,10 +143,6 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick @Override public void readFromNBT(NBTTagCompound tagCompound){ - // Prevent sync() happening when loading from NBT or weirdness ensues when loading a world - // Normally I'd pass this as a flag to setInventorySlotContents but we can't change the method signature - this.doNotSync = true; - super.readFromNBT(tagCompound); NBTTagList tagList = tagCompound.getTagList("Inventory", NBT.TAG_COMPOUND); @@ -153,8 +153,6 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick setInventorySlotContents(slot, new ItemStack(tag)); } } - - this.doNotSync = false; } @Override From e985bc6f69cd3021578e7155b723356284905936 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 12 Apr 2020 23:00:23 +0100 Subject: [PATCH 045/415] Since we're syncing anyway, make the client do a deferred refresh to allow syncing to occur, rather than trying to keep track of prev stacks itself --- .../electroblob/wizardry/client/gui/GuiArcaneWorkbench.java | 4 ++++ .../wizardry/inventory/ContainerArcaneWorkbench.java | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index 63289ffd..3fe7a09f 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -150,6 +150,10 @@ public class GuiArcaneWorkbench extends GuiContainer { @Override public void updateScreen(){ if(animationTimer > 0) animationTimer--; + if(arcaneWorkbenchContainer.needsRefresh){ + arcaneWorkbenchContainer.refreshBookshelfSlots(); + arcaneWorkbenchContainer.needsRefresh = false; + } } @Override diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index 5f96ce39..926ffe14 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -78,6 +78,8 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl private boolean sortDescending = false; private String searchText = ""; + public boolean needsRefresh; + public ContainerArcaneWorkbench(IInventory inventory, TileEntityArcaneWorkbench tileentity){ this.tileentity = tileentity; @@ -351,6 +353,10 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl */ private boolean mergeStackIntoBookshelves(ItemStack stack){ + // As far as the client is concerned, which stacks were *previously* in which slots never affects whether a + // given book can fit or not, so as long as it refreshes it doesn't matter where the book actually gets put + if(tileentity.getWorld().isRemote) this.needsRefresh = true; // It's a bit clunky but it works! + // LinkedHashSet preserves iteration order whilst ignoring duplicates - neat! Set slots = new LinkedHashSet<>(bookshelfSlots.size()); From 16ad8543d889a18a8b2a8244f0082a45064030ec Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Mon, 13 Apr 2020 15:32:07 +0100 Subject: [PATCH 046/415] Purple mana (durability) bar! --- src/main/java/electroblob/wizardry/item/ItemWand.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/electroblob/wizardry/item/ItemWand.java b/src/main/java/electroblob/wizardry/item/ItemWand.java index f87e9e10..c37878d4 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWand.java +++ b/src/main/java/electroblob/wizardry/item/ItemWand.java @@ -2,6 +2,7 @@ package electroblob.wizardry.item; import com.google.common.collect.Multimap; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.DrawingUtils; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.constants.Element; import electroblob.wizardry.constants.Tier; @@ -171,6 +172,11 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem, && WandHelper.getProgression(stack) >= Tier.values()[tier.ordinal() + 1].progression; } + @Override + public int getRGBDurabilityForDisplay(ItemStack stack){ + return DrawingUtils.mix(0xff8bfe, 0x8e2ee4, (float)getDurabilityForDisplay(stack)); + } + // Max damage is modifiable with upgrades. @Override public int getMaxDamage(ItemStack stack){ From b45c202ac502dc07883d77a862463575df6706c5 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Mon, 13 Apr 2020 15:41:59 +0100 Subject: [PATCH 047/415] Add tint to background for book-style GUIs --- src/main/java/electroblob/wizardry/client/gui/GuiSpellInfo.java | 1 + .../wizardry/client/gui/handbook/GuiWizardHandbook.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiSpellInfo.java b/src/main/java/electroblob/wizardry/client/gui/GuiSpellInfo.java index 0bb77c4f..4e49ed1e 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiSpellInfo.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiSpellInfo.java @@ -46,6 +46,7 @@ public abstract class GuiSpellInfo extends GuiScreen { public void drawScreen(int mouseX, int mouseY, float partialTicks){ int left = this.width/2 - xSize/2; int top = this.height/2 - this.ySize/2; + this.drawDefaultBackground(); this.drawBackgroundLayer(left, top, mouseX, mouseY); super.drawScreen(mouseX, mouseY, partialTicks); // Just draws the buttons this.drawForegroundLayer(left, top, mouseX, mouseY); diff --git a/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java b/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java index ca2dc312..f74a68b2 100644 --- a/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java +++ b/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java @@ -244,6 +244,8 @@ public class GuiWizardHandbook extends GuiScreen { @Override public void drawScreen(int mouseX, int mouseY, float partialTicks){ + this.drawDefaultBackground(); + int left = this.width / 2 - GUI_WIDTH / 2; int top = this.height / 2 - GUI_HEIGHT / 2; From aecc8b0889271321d9693c35c9e7bd2799a1a9b3 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Mon, 13 Apr 2020 17:20:43 +0100 Subject: [PATCH 048/415] Okay it really is fixed now I promise --- .../inventory/ContainerArcaneWorkbench.java | 40 +++++++++++-------- .../inventory/ContainerBookshelf.java | 2 +- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index 926ffe14..3ef290d7 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -386,7 +386,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl // Not sure why mergeItemStack differentiates between full/partial merging, as far as I can tell the // following line will work for both cases slot.putStack(stack.splitStack(contents.getMaxStackSize())); - slot.onSlotChanged(); + //slot.onSlotChanged(); if(stack.isEmpty()) return true; // The whole stack has been merged, so we're done! @@ -520,24 +520,16 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl /** Called on initialisation and whenever a bookshelf is added or removed, to update the virtual slot list. */ public void refreshBookshelfSlots(){ - this.inventorySlots.removeAll(bookshelfSlots); - bookshelfSlots.clear(); + // We can't simply re-initialise everything because this method gets called whenever a bookshelf is updated, + // and that also happens whenever a stack is added or removed to update the book state on the client - and + // unfortunately there is no way for a world event listener to detect whether the tile entity changed or not + + // Remove slots that are no longer valid + this.inventorySlots.removeIf(s -> s instanceof VirtualSlot && !((VirtualSlot)s).isValid()); + bookshelfSlots.removeIf(s -> !s.isValid()); List bookshelves = BlockBookshelf.findNearbyBookshelves(tileentity.getWorld(), tileentity.getPos(), tileentity); - if(!bookshelves.isEmpty()){ - - for(IInventory bookshelf : bookshelves){ - for(int i = 0; i < bookshelf.getSizeInventory(); i++){ - VirtualSlot slot = new VirtualSlot(bookshelf, i); // This sets the slot INDEX (for the INVENTORY) - bookshelfSlots.add(slot); - this.addSlotToContainer(slot); // This sets the slot NUMBER (for the CONTAINER) - } - } - - if(tileentity.getWorld().isRemote) updateActiveBookshelfSlots(); - } - if(bookshelves.isEmpty() == hasBookshelves){ // If the bookshelf status changed // Move all the slots appropriately @@ -550,6 +542,22 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl hasBookshelves = !bookshelves.isEmpty(); } + // Ignore bookshelves we already have slots for + bookshelves.removeIf(b -> bookshelfSlots.stream().anyMatch(s -> s.inventory == b)); + + if(!bookshelves.isEmpty()){ + + for(IInventory bookshelf : bookshelves){ + for(int i = 0; i < bookshelf.getSizeInventory(); i++){ + VirtualSlot slot = new VirtualSlot(bookshelf, i); // This sets the slot INDEX (for the INVENTORY) + bookshelfSlots.add(slot); + this.addSlotToContainer(slot); // This sets the slot NUMBER (for the CONTAINER) + } + } + } + + if(tileentity.getWorld().isRemote) updateActiveBookshelfSlots(); + } } diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java b/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java index 92520dea..5ece23a0 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java @@ -39,7 +39,7 @@ public class ContainerBookshelf extends Container { /** Called from individual slots when their item is changed or removed. */ public void onSlotChanged(){ - //this.tileentity.sync(); + this.tileentity.sync(); } @Override From 4980beea87ece92fd8a268b249bf54f5bd610e75 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 14 Apr 2020 18:42:50 +0100 Subject: [PATCH 049/415] Wooden things are now more wooden --- src/main/java/electroblob/wizardry/block/BlockBookshelf.java | 4 ++++ src/main/java/electroblob/wizardry/block/BlockGildedWood.java | 4 ++++ src/main/java/electroblob/wizardry/block/BlockLectern.java | 4 ++++ .../java/electroblob/wizardry/registry/WizardryBlocks.java | 3 ++- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java index 2c50e8b5..b84ed7d8 100644 --- a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java +++ b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java @@ -7,6 +7,7 @@ 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.state.BlockFaceShape; @@ -58,6 +59,9 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid super(Material.WOOD); this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH)); this.setCreativeTab(WizardryTabs.WIZARDRY); + this.setHardness(2.0F); + this.setResistance(5.0F); + this.setSoundType(SoundType.WOOD); } @Override diff --git a/src/main/java/electroblob/wizardry/block/BlockGildedWood.java b/src/main/java/electroblob/wizardry/block/BlockGildedWood.java index f70ba642..9e95cc6a 100644 --- a/src/main/java/electroblob/wizardry/block/BlockGildedWood.java +++ b/src/main/java/electroblob/wizardry/block/BlockGildedWood.java @@ -2,12 +2,16 @@ package electroblob.wizardry.block; import electroblob.wizardry.registry.WizardryTabs; import net.minecraft.block.BlockPlanks; +import net.minecraft.block.SoundType; public class BlockGildedWood extends BlockPlanks { public BlockGildedWood(){ super(); this.setCreativeTab(WizardryTabs.WIZARDRY); + this.setHardness(2.0F); + this.setResistance(5.0F); + this.setSoundType(SoundType.WOOD); // Why is this protected?! } } diff --git a/src/main/java/electroblob/wizardry/block/BlockLectern.java b/src/main/java/electroblob/wizardry/block/BlockLectern.java index 922c4118..4fed0090 100644 --- a/src/main/java/electroblob/wizardry/block/BlockLectern.java +++ b/src/main/java/electroblob/wizardry/block/BlockLectern.java @@ -8,6 +8,7 @@ import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; 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.state.BlockFaceShape; import net.minecraft.block.state.BlockStateContainer; @@ -32,6 +33,9 @@ public class BlockLectern extends BlockHorizontal implements ITileEntityProvider super(Material.WOOD); this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH)); this.setCreativeTab(WizardryTabs.WIZARDRY); + this.setHardness(2.0F); + this.setResistance(5.0F); + this.setSoundType(SoundType.WOOD); } @Override diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java index 244e5291..5c47d0a4 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java @@ -5,6 +5,7 @@ import electroblob.wizardry.block.*; import electroblob.wizardry.tileentity.*; import net.minecraft.block.Block; import net.minecraft.block.BlockPlanks; +import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; @@ -109,7 +110,7 @@ public final class WizardryBlocks { registerBlock(registry, "thorns", new BlockThorns()); registerBlock(registry, "obsidian_crust", new BlockObsidianCrust()); registerBlock(registry, "dry_frosted_ice", new BlockDryFrostedIce()); - registerBlock(registry, "gilded_wood", new BlockPlanks().setCreativeTab(WizardryTabs.WIZARDRY)); + registerBlock(registry, "gilded_wood", new BlockGildedWood()); registerBlock(registry, "oak_bookshelf", new BlockBookshelf()); registerBlock(registry, "spruce_bookshelf", new BlockBookshelf()); registerBlock(registry, "birch_bookshelf", new BlockBookshelf()); From 1e5add914ef17e11f9ff2d7ce8eebae37bae4369 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 14 Apr 2020 22:33:12 +0100 Subject: [PATCH 050/415] Fix incorrect rounding for whole numbers of crystals --- src/main/java/electroblob/wizardry/item/ItemBlankScroll.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/item/ItemBlankScroll.java b/src/main/java/electroblob/wizardry/item/ItemBlankScroll.java index 068e78c8..3c88cb62 100644 --- a/src/main/java/electroblob/wizardry/item/ItemBlankScroll.java +++ b/src/main/java/electroblob/wizardry/item/ItemBlankScroll.java @@ -13,6 +13,7 @@ import net.minecraft.inventory.Slot; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.MathHelper; public class ItemBlankScroll extends Item implements IWorkbenchItem { @@ -55,7 +56,7 @@ public class ItemBlankScroll extends Item implements IWorkbenchItem { if(crystals.getStack().getCount() * manaPerItem > cost){ // Rounds up to the nearest whole crystal - crystals.decrStackSize(cost / manaPerItem + 1); + crystals.decrStackSize(MathHelper.ceil((float)cost / manaPerItem)); centre.putStack(new ItemStack(WizardryItems.scroll, centre.getStack().getCount(), spell.metadata())); return true; } From 702398dd347a110282c3f61b9e39741fb5bace9b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 14 Apr 2020 22:33:48 +0100 Subject: [PATCH 051/415] Fix spell book tooltips not showing up in JEI --- src/main/java/electroblob/wizardry/item/ItemSpellBook.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/item/ItemSpellBook.java b/src/main/java/electroblob/wizardry/item/ItemSpellBook.java index 5db1eb9a..037dfeda 100644 --- a/src/main/java/electroblob/wizardry/item/ItemSpellBook.java +++ b/src/main/java/electroblob/wizardry/item/ItemSpellBook.java @@ -60,8 +60,11 @@ public class ItemSpellBook extends Item { @Override @SideOnly(Side.CLIENT) public void addInformation(ItemStack itemstack, World world, List tooltip, net.minecraft.client.util.ITooltipFlag advanced){ + + if(world == null) world = Wizardry.proxy.getTheWorld(); // But... I need the world! + // Tooltip is left blank for wizards buying generic spell books. - if(world != null && itemstack.getItemDamage() != OreDictionary.WILDCARD_VALUE){ + if(itemstack.getItemDamage() != OreDictionary.WILDCARD_VALUE){ Spell spell = Spell.byMetadata(itemstack.getItemDamage()); From df526dc76fabeae40fea6ac6f5ded4912b074783 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 14 Apr 2020 23:06:49 +0100 Subject: [PATCH 052/415] Search trees don't like this very much :( --- src/main/java/electroblob/wizardry/item/ItemSpellBook.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/item/ItemSpellBook.java b/src/main/java/electroblob/wizardry/item/ItemSpellBook.java index 037dfeda..7cb48615 100644 --- a/src/main/java/electroblob/wizardry/item/ItemSpellBook.java +++ b/src/main/java/electroblob/wizardry/item/ItemSpellBook.java @@ -57,6 +57,10 @@ public class ItemSpellBook extends Item { return ActionResult.newResult(EnumActionResult.SUCCESS, stack); } + // This is accessed during loading (before we even get to the main menu) for search tree population + // Obviously the world is always null at that point, because no world objects exist! However, outside of a world + // there are no guarantees as to spell metadata order so we just have to give up (and we can't account for discovery) + // TODO: Search trees seem to get reloaded when the mappings change so in theory this should work ok, why doesn't it? @Override @SideOnly(Side.CLIENT) public void addInformation(ItemStack itemstack, World world, List tooltip, net.minecraft.client.util.ITooltipFlag advanced){ @@ -64,7 +68,7 @@ public class ItemSpellBook extends Item { if(world == null) world = Wizardry.proxy.getTheWorld(); // But... I need the world! // Tooltip is left blank for wizards buying generic spell books. - if(itemstack.getItemDamage() != OreDictionary.WILDCARD_VALUE){ + if(world != null && itemstack.getItemDamage() != OreDictionary.WILDCARD_VALUE){ Spell spell = Spell.byMetadata(itemstack.getItemDamage()); From 9bc920073b9d9123f686d6d90236ac637b048ae5 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 14 Apr 2020 23:42:19 +0100 Subject: [PATCH 053/415] Fix animation positions --- .../electroblob/wizardry/client/gui/GuiArcaneWorkbench.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index 3fe7a09f..afc9ebe4 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -289,7 +289,7 @@ public class GuiArcaneWorkbench extends GuiContainer { GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); GlStateManager.color(1, 1, 1, opacity); - DrawingUtils.drawTexturedRect(left + crystals.xPos - 8, top + crystals.yPos - 8, + DrawingUtils.drawTexturedRect(guiLeft + crystals.xPos - 8, guiTop + crystals.yPos - 8, MAIN_GUI_WIDTH + TOOLTIP_WIDTH + RUNE_WIDTH, 0, 32, 32, TEXTURE_WIDTH, TEXTURE_HEIGHT); GlStateManager.color(1, 1, 1, 1); @@ -304,7 +304,7 @@ public class GuiArcaneWorkbench extends GuiContainer { GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); GlStateManager.color(1, 1, 1, opacity); - DrawingUtils.drawTexturedRect(left + upgrades.xPos - 8, top + upgrades.yPos - 8, + DrawingUtils.drawTexturedRect(guiLeft + upgrades.xPos - 8, guiTop + upgrades.yPos - 8, MAIN_GUI_WIDTH + TOOLTIP_WIDTH + RUNE_WIDTH, 0, 32, 32, TEXTURE_WIDTH, TEXTURE_HEIGHT); GlStateManager.color(1, 1, 1, 1); From 13ba19c1ff971e69b4456649215221c19634f33b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 15 Apr 2020 18:57:31 +0100 Subject: [PATCH 054/415] Purple mana bars for wizard robes too! --- .../java/electroblob/wizardry/item/ItemWizardArmour.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java index 71cbdc45..e70301c1 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java +++ b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java @@ -4,6 +4,7 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Streams; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.DrawingUtils; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.constants.Element; import electroblob.wizardry.event.SpellCastEvent; @@ -83,6 +84,11 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana return this.getMaxDamage(stack); } + @Override + public int getRGBDurabilityForDisplay(ItemStack stack){ + return DrawingUtils.mix(0xff8bfe, 0x8e2ee4, (float)getDurabilityForDisplay(stack)); + } + @Override @SideOnly(Side.CLIENT) public void addInformation(ItemStack stack, World world, List tooltip, net.minecraft.client.util.ITooltipFlag advanced){ From 945c789482628dfcf66a6685cd1e295b189d062b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 15 Apr 2020 20:13:36 +0100 Subject: [PATCH 055/415] Must have missed those --- .../wizardry/registry/WizardryPotions.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/electroblob/wizardry/registry/WizardryPotions.java b/src/main/java/electroblob/wizardry/registry/WizardryPotions.java index 3f799f6a..9b66caa5 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryPotions.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryPotions.java @@ -83,7 +83,7 @@ public final class WizardryPotions { registerPotion(registry, "frost", new PotionFrost(true, 0)); // Colour was 0x38ddec (was arbitrary anyway) registerPotion(registry, "transience", new PotionMagicEffectParticles(false, 0, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_transience.png")){ + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/transience.png")){ @Override public void spawnCustomParticle(World world, double x, double y, double z){ ParticleBuilder.create(Type.DUST).pos(x, y, z).clr(0.8f, 0.8f, 1.0f).shaded(true).spawn(world); @@ -91,7 +91,7 @@ public final class WizardryPotions { }.setBeneficial()); // 0xffe89b registerPotion(registry, "fireskin", new PotionMagicEffectParticles(false, 0, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_fireskin.png")){ + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/fireskin.png")){ @Override public void spawnCustomParticle(World world, double x, double y, double z){ world.spawnParticle(EnumParticleTypes.FLAME, x, y, z, 0, 0, 0); @@ -105,7 +105,7 @@ public final class WizardryPotions { }.setBeneficial()); // 0xff2f02 registerPotion(registry, "ice_shroud", new PotionMagicEffectParticles(false, 0, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_ice_shroud.png")){ + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/ice_shroud.png")){ @Override public void spawnCustomParticle(World world, double x, double y, double z){ float brightness = 0.5f + (world.rand.nextFloat() / 2); @@ -115,7 +115,7 @@ public final class WizardryPotions { }.setBeneficial()); // 0x52f1ff registerPotion(registry, "static_aura", new PotionMagicEffectParticles(false, 0, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_static_aura.png")){ + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/static_aura.png")){ @Override public void spawnCustomParticle(World world, double x, double y, double z){ ParticleBuilder.create(Type.SPARK).pos(x, y, z).spawn(world); @@ -125,7 +125,7 @@ public final class WizardryPotions { registerPotion(registry, "decay", new PotionDecay(true, 0x3c006c)); registerPotion(registry, "sixth_sense", new PotionMagicEffect(false, 0xc6ff01, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_sixth_sense.png")){ + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/sixth_sense.png")){ @Override public void performEffect(EntityLivingBase target, int strength){ // Reset the shader (a bit dirty but both the potion expiry hooks are only fired server-side, and @@ -138,22 +138,22 @@ public final class WizardryPotions { }.setBeneficial()); registerPotion(registry, "arcane_jammer", new PotionMagicEffect(true, 0xcf4aa2, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_arcane_jammer.png"))); + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/arcane_jammer.png"))); registerPotion(registry, "mind_trick", new PotionMagicEffect(true, 0x601683, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_mind_trick.png"))); + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/mind_trick.png"))); registerPotion(registry, "mind_control", new PotionMagicEffect(true, 0x320b44, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_mind_control.png"))); + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/mind_control.png"))); registerPotion(registry, "font_of_mana", new PotionMagicEffect(false, 0xffe5bb, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_font_of_mana.png")).setBeneficial()); + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/font_of_mana.png")).setBeneficial()); registerPotion(registry, "fear", new PotionMagicEffect(true, 0xbd0100, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_fear.png"))); + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/fear.png"))); registerPotion(registry, "curse_of_soulbinding", new Curse(true, 0x0f000f, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_curse_of_soulbinding.png")){ + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/curse_of_soulbinding.png")){ @Override // We're not removing any attributes, but it's called when we want it to be so... public void removeAttributesModifiersFromEntity(EntityLivingBase entity, net.minecraft.entity.ai.attributes.AbstractAttributeMap attributeMapIn, int amplifier){ // TODO: Hmmmm... @@ -161,7 +161,7 @@ public final class WizardryPotions { }); registerPotion(registry, "paralysis", new PotionMagicEffectParticles(true, 0, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_paralysis.png")){ + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/paralysis.png")){ @Override public void spawnCustomParticle(World world, double x, double y, double z){ ParticleBuilder.create(Type.SPARK).pos(x, y, z).spawn(world); @@ -169,15 +169,15 @@ public final class WizardryPotions { }); registerPotion(registry, "muffle", new PotionMagicEffect(false, 0x4464d9, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_muffle.png")).setBeneficial()); + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/muffle.png")).setBeneficial()); registerPotion(registry, "ward", new PotionMagicEffect(false, 0xc991d0, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_ward.png")).setBeneficial()); + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/ward.png")).setBeneficial()); registerPotion(registry, "slow_time", new PotionSlowTime(false, 0x5be3bb).setBeneficial()); registerPotion(registry, "empowerment", new PotionMagicEffect(false, 0x8367bd, - new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_empowerment.png")).setBeneficial()); + new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/empowerment.png")).setBeneficial()); registerPotion(registry, "curse_of_enfeeblement", new CurseEnfeeblement(true, 0x36000b)); From 8301a24afe42fc0e0984eaed07f150484b18aa70 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 15 Apr 2020 23:01:45 +0100 Subject: [PATCH 056/415] Just enough wizardry! - Adds JEI support for arcane workbench 'recipes' (charging, upgrading, and scroll enchantment) - Adds an item transfer handler for said recipes, including bookshelf virtual slot support - Prevents conjured items from showing up in JEI --- .../java/electroblob/wizardry/Settings.java | 19 +- .../client/gui/GuiArcaneWorkbench.java | 3 +- .../jei/ArcaneWorkbenchRecipe.java | 119 ++++++++ .../jei/ArcaneWorkbenchRecipeCategory.java | 269 ++++++++++++++++++ .../jei/ArcaneWorkbenchTransferHandler.java | 52 ++++ .../integration/jei/WizardryJEIPlugin.java | 56 ++++ .../inventory/ContainerArcaneWorkbench.java | 22 +- .../wizardry/item/IWorkbenchItem.java | 19 +- .../electroblob/wizardry/item/ItemWand.java | 88 +++--- .../wizardry/item/ItemWizardArmour.java | 41 ++- .../wizardry/registry/WizardryRecipes.java | 31 +- .../arcane_workbench_jei_background.png | Bin 0 -> 10281 bytes 12 files changed, 641 insertions(+), 78 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipe.java create mode 100644 src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java create mode 100644 src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchTransferHandler.java create mode 100644 src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java create mode 100644 src/main/resources/assets/ebwizardry/textures/gui/container/arcane_workbench_jei_background.png diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index d58ff358..952d9062 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -241,10 +241,11 @@ public final class Settings { public String[] damageSourceBlacklist = {}; /** [Server-only] Whether to print compatibility warnings to the console. */ public boolean compatibilityWarnings = true; + // TODO: Should these really be server-only? /** [Server-only] Whether Baubles integration features are enabled. */ public boolean baublesIntegration = true; -// /** [Server-only] Whether JEI integration features are enabled. */ -// public boolean jeiIntegration = true; + /** [Server-only] Whether JEI integration features are enabled. */ + public boolean jeiIntegration = true; /** [Server-only] Whether Antique Atlas integration features are enabled. */ public boolean antiqueAtlasIntegration = true; /** [Server-only] Whether global markers for wizard towers are added to antique atlases. */ @@ -1111,13 +1112,13 @@ public final class Settings { baublesIntegration = property.getBoolean(); propOrder.add(property.getName()); -// property = config.get(COMPATIBILITY_CATEGORY, "jeiIntegration", true, -// "If JEI (Just Enough Items) is installed, controls whether JEI integration features are enabled. If this is disabled, wizardry will always behave as if JEI is not installed."); -// property.setLanguageKey("config." + Wizardry.MODID + ".jei_integration"); -// property.setRequiresMcRestart(true); -// Wizardry.proxy.setToNamedBooleanEntry(property); -// jeiIntegration = property.getBoolean(); -// propOrder.add(property.getName()); + property = config.get(COMPATIBILITY_CATEGORY, "jeiIntegration", true, + "If JEI (Just Enough Items) is installed, controls whether JEI integration features are enabled. If this is disabled, wizardry will always behave as if JEI is not installed."); + property.setLanguageKey("config." + Wizardry.MODID + ".jei_integration"); + property.setRequiresMcRestart(true); + Wizardry.proxy.setToNamedBooleanEntry(property); + jeiIntegration = property.getBoolean(); + propOrder.add(property.getName()); property = config.get(COMPATIBILITY_CATEGORY, "antiqueAtlasIntegration", true, "If Antique Atlas is installed, controls whether Antique Atlas integration features are enabled. If this is disabled, wizardry will always behave as if Antique Atlas is not installed."); diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java index afc9ebe4..8b0ea1e8 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiArcaneWorkbench.java @@ -113,7 +113,7 @@ public class GuiArcaneWorkbench extends GuiContainer { public void initGui(){ this.mc.player.openContainer = this.inventorySlots; - this.guiLeft = (this.width - this.xSize) / 2; + this.guiLeft = (this.width - MAIN_GUI_WIDTH) / 2; // Use MAIN_GUI_WIDTH, not xSize, otherwise JEI messes with it this.guiTop = (this.height - this.ySize) / 2; Keyboard.enableRepeatEvents(true); @@ -132,6 +132,7 @@ public class GuiArcaneWorkbench extends GuiContainer { this.searchField.setCanLoseFocus(false); this.searchField.setFocused(true); + this.tooltipElements.clear(); this.tooltipElements.add(new TooltipElementItemName(new Style().setColor(TextFormatting.WHITE), LINE_SPACING_WIDE)); this.tooltipElements.add(new TooltipElementManaReadout(LINE_SPACING_WIDE)); this.tooltipElements.add(new TooltipElementProgressionBar(LINE_SPACING_WIDE)); diff --git a/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipe.java b/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipe.java new file mode 100644 index 00000000..9adea78d --- /dev/null +++ b/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipe.java @@ -0,0 +1,119 @@ +package electroblob.wizardry.integration.jei; + +import electroblob.wizardry.client.DrawingUtils; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; +import electroblob.wizardry.item.IWorkbenchItem; +import electroblob.wizardry.registry.WizardryItems; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.ingredients.VanillaTypes; +import mezz.jei.api.recipe.IRecipeWrapper; +import net.minecraft.client.Minecraft; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.MathHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Represents a 'recipe' for the arcane workbench. + * + * @author Electroblob + * @since Wizardry 4.3 + */ +public class ArcaneWorkbenchRecipe implements IRecipeWrapper { + + private final ItemStack centreStack; + private final List books; + private final List crystals; + private final List upgrades; + private final ItemStack result; + + private final int bookSlots; + + private final List> inputs; + + public ArcaneWorkbenchRecipe(ItemStack centreStack, List books, List crystals, List upgrades, ItemStack result){ + + this.centreStack = centreStack; + this.books = books; // CAUTION! This list is an OUTER LIST! + this.crystals = crystals; + this.upgrades = upgrades; + this.result = result; + + this.inputs = new ArrayList<>(); + for(ItemStack book : books) this.inputs.add(Collections.singletonList(book)); + this.inputs.add(crystals); + this.inputs.add(Collections.singletonList(centreStack)); + this.inputs.add(upgrades); + + if(centreStack.getItem() instanceof IWorkbenchItem){ + bookSlots = ((IWorkbenchItem)centreStack.getItem()).getSpellSlotCount(centreStack); + }else{ + bookSlots = 0; + } + + } + + public ArcaneWorkbenchRecipe(ItemStack centreStack, List books, int mana, List upgrades, ItemStack result){ + this(centreStack, books, generateCrystalStacks(mana), upgrades, result); + } + + @Override + public void getIngredients(IIngredients ingredients){ + ingredients.setInputLists(VanillaTypes.ITEM, inputs); + ingredients.setOutput(VanillaTypes.ITEM, result); + } + + @Override + public void drawInfo(Minecraft minecraft, int recipeWidth, int recipeHeight, int mouseX, int mouseY){ + // Can't do this in IRecipeCategory#drawExtras because we have no access to the recipe there! + // ArcaneWorkbenchRecipeCategory.TEXTURE is already bound at this point + for(int i = 0; i < bookSlots; i++){ + int x = ArcaneWorkbenchRecipeCategory.CENTRE_SLOT_X + ContainerArcaneWorkbench.getBookSlotXOffset(i, bookSlots) - 9; + int y = ArcaneWorkbenchRecipeCategory.CENTRE_SLOT_Y + ContainerArcaneWorkbench.getBookSlotYOffset(i, bookSlots) - 9; + DrawingUtils.drawTexturedRect(x, y, 0, ArcaneWorkbenchRecipeCategory.HEIGHT, 36, 36, 256, 256); + } + } + + /** + * Returns a list of item stacks, one for each type of crystal (regular, elemental, grand and shards), each with + * the minimum quantity needed to supply the given amount of mana. Types of crystal for which more than the max. + * stack size would be needed are ignored. + */ + public static List generateCrystalStacks(int mana){ + + if(mana < 0) throw new IllegalArgumentException("Cannot create an arcane workbench recipe with negative mana!"); + + if(mana == 0) return Collections.emptyList(); + + List crystalStacks = new ArrayList<>(); + + int count = MathHelper.ceil((float)mana / Constants.MANA_PER_CRYSTAL); + // A stack of crystals will almost certainly be enough mana, but you never know! + // Using ItemStack.EMPTY to avoid deprecated method; crystals' stack size is not stack-sensitive so it doesn't matter + if(count <= WizardryItems.magic_crystal.getItemStackLimit(ItemStack.EMPTY)){ + for(int meta = 0; meta < Element.values().length; meta++){ + crystalStacks.add(new ItemStack(WizardryItems.magic_crystal, count, meta)); + } + } + + count = MathHelper.ceil((float)mana / Constants.MANA_PER_SHARD); + + if(count <= WizardryItems.crystal_shard.getItemStackLimit(ItemStack.EMPTY)){ + crystalStacks.add(new ItemStack(WizardryItems.crystal_shard, count)); + } + + count = MathHelper.ceil((float)mana / Constants.GRAND_CRYSTAL_MANA); + + if(count <= WizardryItems.grand_crystal.getItemStackLimit(ItemStack.EMPTY)){ + crystalStacks.add(new ItemStack(WizardryItems.grand_crystal, count)); + } + + return crystalStacks; + + } + +} diff --git a/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java b/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java new file mode 100644 index 00000000..ad5896b8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java @@ -0,0 +1,269 @@ +package electroblob.wizardry.integration.jei; + +import com.google.common.collect.Streams; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; +import electroblob.wizardry.item.*; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryRecipes; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.WandHelper; +import mezz.jei.api.IGuiHelper; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.gui.IGuiItemStackGroup; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.ingredients.VanillaTypes; +import mezz.jei.api.recipe.IRecipeCategory; +import mezz.jei.api.recipe.IRecipeCategoryRegistration; +import net.minecraft.client.resources.I18n; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * JEI recipe category implementation for all 'recipes' in the arcane workbench (of course, the arcane workbench + * doesn't have 'recipes' in the normal sense, but that's how they're displayed in JEI). + * + * @author Electroblob + * @since Wizardry 4.3 + */ +public class ArcaneWorkbenchRecipeCategory implements IRecipeCategory { + + static final String UID = "ebwizardry:arcane_workbench"; + static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/arcane_workbench_jei_background.png"); + + static final int WIDTH = 166; + static final int HEIGHT = 126; + // Annoyingly, these seem to be for 18x18 slots rather than the actual highlighted area, unlike container slots + static final int CENTRE_SLOT_X = 74; + static final int CENTRE_SLOT_Y = 54; + static final int CRYSTAL_SLOT_X = 7; + static final int CRYSTAL_SLOT_Y = 91; + static final int UPGRADE_SLOT_X = 141; + static final int UPGRADE_SLOT_Y = 7; + static final int OUTPUT_SLOT_X = 141; + static final int OUTPUT_SLOT_Y = 101; + + private final IDrawable background; + + public ArcaneWorkbenchRecipeCategory(IRecipeCategoryRegistration registry){ + IGuiHelper helper = registry.getJeiHelpers().getGuiHelper(); + background = helper.createDrawable(TEXTURE, 0, 0, WIDTH, HEIGHT); + } + + @Override + public String getUid(){ + return UID; + } + + @Override + public String getTitle(){ + // JEI is client-side, so client classes can safely be used here + return I18n.format("integration.jei.category." + UID); + } + + @Override + public String getModName(){ + return Wizardry.NAME; + } + + @Override + public IDrawable getBackground(){ + return background; + } + + @Override + public void setRecipe(IRecipeLayout recipeLayout, ArcaneWorkbenchRecipe recipeWrapper, IIngredients ingredients){ + + // Okay, they're not technically *slots* but to all intents and purposes, that's how they behave + IGuiItemStackGroup slots = recipeLayout.getItemStacks(); + + List> inputs = ingredients.getInputs(VanillaTypes.ITEM); + List> outputs = ingredients.getOutputs(VanillaTypes.ITEM); + + ItemStack centreStack = inputs.get(inputs.size() - 2).get(0); + + int bookSlots = 0; + + if(centreStack.getItem() instanceof IWorkbenchItem){ + bookSlots = ((IWorkbenchItem)centreStack.getItem()).getSpellSlotCount(centreStack); + } + + // Slot initialisation + int i = 0; + + while(i < bookSlots){ + int x = CENTRE_SLOT_X + ContainerArcaneWorkbench.getBookSlotXOffset(i, bookSlots); + int y = CENTRE_SLOT_Y + ContainerArcaneWorkbench.getBookSlotYOffset(i, bookSlots); + slots.init(i++, true, x, y); + } + + // Add dummy slots for the hidden book slots so the transfer handler works correctly + // Sure, we COULD use an IRecipeTransferHandler, but this is far less effort! + while(i < ContainerArcaneWorkbench.CRYSTAL_SLOT){ + slots.init(i++, true, 0, 0); + } + + slots.init(i++, true, CRYSTAL_SLOT_X, CRYSTAL_SLOT_Y); + slots.init(i++, true, CENTRE_SLOT_X, CENTRE_SLOT_Y); + slots.init(i++, true, UPGRADE_SLOT_X, UPGRADE_SLOT_Y); + + slots.init(i++, false, OUTPUT_SLOT_X, OUTPUT_SLOT_Y); + + // Assign ingredients to slots + // The number of books we actually have is inputs.size() - 3, probably less than the number of book slots + for(int j = 0; j < Math.min(bookSlots, inputs.size() - 3); j++){ + slots.set(j, inputs.get(j)); + } + + slots.set(ContainerArcaneWorkbench.CRYSTAL_SLOT, inputs.get(inputs.size() - 3)); + slots.set(ContainerArcaneWorkbench.CENTRE_SLOT, inputs.get(inputs.size() - 2)); + slots.set(ContainerArcaneWorkbench.UPGRADE_SLOT, inputs.get(inputs.size() - 1)); + + for(int k = 0; k < outputs.size(); k++) slots.set(11 + k, outputs.get(k)); + + } + + /** Called to generate all of wizardry's arcane workbench 'recipes' for JEI. */ + public static Collection generateRecipes(){ + + List recipes = new ArrayList<>(); + + recipes.addAll(generateUpgradeRecipes()); // Probably nicest to have these first, they're the most useful + recipes.addAll(generateChargingRecipes()); + recipes.addAll(generateScrollRecipes()); + + return recipes; + + } + + private static Collection generateUpgradeRecipes(){ + + List recipes = new ArrayList<>(); + + List upgrades = new ArrayList<>(); + + for(Item item : Item.REGISTRY){ + if(item instanceof ItemArcaneTome || item instanceof ItemArmourUpgrade){ + NonNullList variants = NonNullList.create(); + item.getSubItems(item.getCreativeTab(), variants); + upgrades.addAll(variants); + } + } + + // Condense all special upgrades into one ingredient in an effort to reduce the number of separate recipes + List specialUpgrades = new ArrayList<>(); + + for(Item item : WandHelper.getSpecialUpgrades()){ + NonNullList variants = NonNullList.create(); + item.getSubItems(item.getCreativeTab(), variants); + specialUpgrades.addAll(variants); + } + + for(Item item : Item.REGISTRY){ + + if(item instanceof IWorkbenchItem){ + + ItemStack original = new ItemStack(item); + + for(ItemStack upgrade : upgrades){ + // Copy both input stacks to ignore any modifications to them during the upgrading process + ItemStack result = ((IWorkbenchItem)item).applyUpgrade(null, original.copy(), upgrade.copy()); + // It's only a valid 'recipe' if something actually changed + if(!ItemStack.areItemStacksEqual(original, result)){ + recipes.add(new ArcaneWorkbenchRecipe(original, Collections.emptyList(), Collections.emptyList(), + Collections.singletonList(upgrade), result)); + } + } + + List applicableSpecialUpgrades = new ArrayList<>(); + + for(ItemStack upgrade : specialUpgrades){ + // Copy both input stacks to ignore any modifications to them during the upgrading process + ItemStack result = ((IWorkbenchItem)item).applyUpgrade(null, original.copy(), upgrade.copy()); + // It's only a valid 'recipe' if something actually changed + if(!ItemStack.areItemStacksEqual(original, result)){ + applicableSpecialUpgrades.add(upgrade); + } + } + + recipes.add(new ArcaneWorkbenchRecipe(original, Collections.emptyList(), Collections.emptyList(), + applicableSpecialUpgrades, original)); // Wands with special upgrades look no different anyway + + } + } + + return recipes; + + } + + private static Collection generateChargingRecipes(){ + + List recipes = new ArrayList<>(); + + List crystals = new ArrayList<>(); + for(int meta = 0; meta < Element.values().length; meta++) crystals.add(new ItemStack(WizardryItems.magic_crystal, 1, meta)); + List shard = Collections.singletonList(new ItemStack(WizardryItems.crystal_shard)); + List grandCrystal = Collections.singletonList(new ItemStack(WizardryItems.grand_crystal)); + + for(Item chargeable : WizardryRecipes.getChargeableItems()){ + + if(!(chargeable instanceof IManaStoringItem)) throw new IllegalArgumentException("Item to be charged must be an instance of IManaStoringItem"); + + ItemStack input = new ItemStack(chargeable); + ((IManaStoringItem)chargeable).setMana(input, 0); + + ItemStack result = new ItemStack(chargeable); + ((IManaStoringItem)chargeable).setMana(result, Constants.MANA_PER_CRYSTAL); + recipes.add(new ArcaneWorkbenchRecipe(input, Collections.emptyList(), crystals, Collections.emptyList(), result)); + + result = new ItemStack(chargeable); + ((IManaStoringItem)chargeable).setMana(result, Constants.MANA_PER_SHARD); + recipes.add(new ArcaneWorkbenchRecipe(input, Collections.emptyList(), shard, Collections.emptyList(), result)); + + result = new ItemStack(chargeable); + ((IManaStoringItem)chargeable).setMana(result, Constants.GRAND_CRYSTAL_MANA); + recipes.add(new ArcaneWorkbenchRecipe(input, Collections.emptyList(), grandCrystal, Collections.emptyList(), result)); + } + + return recipes; + + } + + private static Collection generateScrollRecipes(){ + + List recipes = new ArrayList<>(); + + ItemStack blankScroll = new ItemStack(WizardryItems.blank_scroll); + + // We need not make people register these manually since spells already have control over what they can be put on + List spellBooks = Streams.stream(Item.REGISTRY).filter(i -> i instanceof ItemSpellBook).collect(Collectors.toList()); + List scrolls = Streams.stream(Item.REGISTRY).filter(i -> i instanceof ItemScroll).collect(Collectors.toList()); + + for(Spell spell : Spell.getAllSpells()){ + for(Item spellBook : spellBooks){ + for(Item scroll : scrolls){ + if(spell.applicableForItem(spellBook) && spell.applicableForItem(scroll)){ + List books = Collections.singletonList(new ItemStack(WizardryItems.spell_book, 1, spell.metadata())); + ItemStack result = new ItemStack(WizardryItems.scroll, 1, spell.metadata()); + recipes.add(new ArcaneWorkbenchRecipe(blankScroll, books, spell.getCost(), Collections.emptyList(), result)); + } + } + } + } + + return recipes; + + } + +} diff --git a/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchTransferHandler.java b/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchTransferHandler.java new file mode 100644 index 00000000..a911bd1a --- /dev/null +++ b/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchTransferHandler.java @@ -0,0 +1,52 @@ +package electroblob.wizardry.integration.jei; + +import electroblob.wizardry.inventory.ContainerArcaneWorkbench; +import mezz.jei.api.recipe.transfer.IRecipeTransferInfo; +import net.minecraft.inventory.Slot; + +import java.util.ArrayList; +import java.util.List; + +/** + * JEI recipe transfer handler for the arcane workbench. This differs from a standard recipe handler in that it returns + * the active bookshelf (virtual) slots from the workbench as part of the inventory slots, and does not require complete + * sets of ingredients. + * + * @author Electroblob + * @since Wizardry 4.3 + */ +public class ArcaneWorkbenchTransferHandler implements IRecipeTransferInfo { + + @Override + public Class getContainerClass(){ + return ContainerArcaneWorkbench.class; + } + + @Override + public String getRecipeCategoryUid(){ + return ArcaneWorkbenchRecipeCategory.UID; + } + + @Override + public boolean canHandle(ContainerArcaneWorkbench container){ + return true; + } + + @Override + public boolean requireCompleteSets(){ + return false; // Arcane workbench maths doesn't work like crafting maths! + } + + @Override + public List getRecipeSlots(ContainerArcaneWorkbench container){ + return container.inventorySlots.subList(0, ContainerArcaneWorkbench.UPGRADE_SLOT + 1); + } + + @Override + public List getInventorySlots(ContainerArcaneWorkbench container){ + List slots = new ArrayList<>(container.inventorySlots.subList(ContainerArcaneWorkbench.UPGRADE_SLOT + 1, ContainerArcaneWorkbench.UPGRADE_SLOT + 37)); + slots.addAll(container.getBookshelfSlots()); + return slots; + } + +} diff --git a/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java b/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java new file mode 100644 index 00000000..a5b050f2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java @@ -0,0 +1,56 @@ +package electroblob.wizardry.integration.jei; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.IConjuredItem; +import electroblob.wizardry.registry.WizardryBlocks; +import mezz.jei.api.IModPlugin; +import mezz.jei.api.IModRegistry; +import mezz.jei.api.ISubtypeRegistry; +import mezz.jei.api.JEIPlugin; +import mezz.jei.api.ingredients.IIngredientBlacklist; +import mezz.jei.api.recipe.IRecipeCategoryRegistration; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +@JEIPlugin +public class WizardryJEIPlugin implements IModPlugin { + + @Override + public void registerCategories(IRecipeCategoryRegistration registry){ + + if(!Wizardry.settings.jeiIntegration) return; + + registry.addRecipeCategories(new ArcaneWorkbenchRecipeCategory(registry)); + } + + @Override + public void register(IModRegistry registry){ + + if(!Wizardry.settings.jeiIntegration) return; + + // Add arcane workbench as the item required to use arcane workbench recipes + registry.addRecipeCatalyst(new ItemStack(WizardryBlocks.arcane_workbench), ArcaneWorkbenchRecipeCategory.UID); + + registry.addRecipes(ArcaneWorkbenchRecipeCategory.generateRecipes(), ArcaneWorkbenchRecipeCategory.UID); + + registry.getRecipeTransferRegistry().addRecipeTransferHandler(new ArcaneWorkbenchTransferHandler()); + + // Hide conjured items from JEI + IIngredientBlacklist blacklist = registry.getJeiHelpers().getIngredientBlacklist(); + for(Item item : Item.REGISTRY){ + if(item instanceof IConjuredItem) blacklist.addIngredientToBlacklist(new ItemStack(item)); + } + + } + + @Override + public void registerItemSubtypes(ISubtypeRegistry subtypeRegistry){ + + if(!Wizardry.settings.jeiIntegration) return; + + // TODO: Probably need to distinguish between normal/legendary armour, at the very least + // ... that being said, we may well be changing that mechanic anyway + + } + +} diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java index 3ef290d7..771d3146 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerArcaneWorkbench.java @@ -197,12 +197,8 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl // Show however many spell book slots are necessary for(int i = 0; i < spellSlots; i++){ - - float angle = i * (2 * (float)Math.PI)/spellSlots; - int x = centreX + Math.round(SLOT_RADIUS * MathHelper.sin(angle)); - // -cos because +y is downwards - int y = centreY + Math.round(SLOT_RADIUS * -MathHelper.cos(angle)); - + int x = centreX + getBookSlotXOffset(i, spellSlots); + int y = centreY + getBookSlotYOffset(i, spellSlots); showSlot(i, x, y); } @@ -216,6 +212,20 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl } } + /** Returns the x offset (relative to the central slot) of the ith book slot when the total number of book slots is + * equal to {@code bookSlotCount}. */ + public static int getBookSlotXOffset(int i, int bookSlotCount){ + float angle = i * (2 * (float)Math.PI) / bookSlotCount; + return Math.round(SLOT_RADIUS * MathHelper.sin(angle)); + } + + /** Returns the y offset (relative to the central slot) of the ith book slot when the total number of book slots is + * equal to {@code bookSlotCount}. */ + public static int getBookSlotYOffset(int i, int bookSlotCount){ + float angle = i * (2 * (float)Math.PI) / bookSlotCount; + return Math.round(SLOT_RADIUS * -MathHelper.cos(angle)); // -cos because +y is downwards + } + // FIXME: Shift-clicking a stack of special upgrades when in the arcane workbench causes the whole stack to be // transferred when it should be just one (this is a bug with vanilla as well - try putting a stack of // bottles into a brewing stand). I have at least made it so only one gets used now, so it has no impact on diff --git a/src/main/java/electroblob/wizardry/item/IWorkbenchItem.java b/src/main/java/electroblob/wizardry/item/IWorkbenchItem.java index e85fd314..146246e3 100644 --- a/src/main/java/electroblob/wizardry/item/IWorkbenchItem.java +++ b/src/main/java/electroblob/wizardry/item/IWorkbenchItem.java @@ -5,6 +5,8 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; +import javax.annotation.Nullable; + /** * Items that implement this interface may be placed in the central slot of the arcane workbench as long as * {@link IWorkbenchItem#canPlace(ItemStack)} returns true. The number of spell book slots displayed is also specified @@ -37,7 +39,7 @@ public interface IWorkbenchItem { * Can be 0, but must not be negative. */ int getSpellSlotCount(ItemStack stack); - + /** * Called when this item is in the central slot of an arcane workbench and the apply button is pressed. Items must * implement this method to define what happens when the apply button is pressed. Note that {@link SpellBindEvent} @@ -60,5 +62,20 @@ public interface IWorkbenchItem { * @return True if the workbench tooltip should be shown, false if not. */ boolean showTooltip(ItemStack stack); + + /** + * Applies the given upgrade to this wand. This method is responsible for all checks including tier, progression, + * upgrade stack limits, etc. Subclasses are responsible for calling this (usually from + * {@link IWorkbenchItem#onApplyButtonPressed(EntityPlayer, Slot, Slot, Slot, Slot[])}), but it has been extracted + * as an interface method here for use by JEI 'recipes'. + * @param player The player doing the upgrading, or null during JEI recipe lookup (mainly used for advancements) + * @param stack The stack being upgraded (it is guaranteed that {@code this == stack.getItem()}) + * @param upgrade The upgrade item stack being applied. This method is responsible for consuming it! + * @return The resulting upgraded wand stack. In many cases, this is simply the input {@code stack}, which has + * had its NBT modified. If the given upgrade cannot be applied, simply return the input {@code stack}. + */ + default ItemStack applyUpgrade(@Nullable EntityPlayer player, ItemStack stack, ItemStack upgrade){ + return stack; + } } diff --git a/src/main/java/electroblob/wizardry/item/ItemWand.java b/src/main/java/electroblob/wizardry/item/ItemWand.java index c37878d4..8272098a 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWand.java +++ b/src/main/java/electroblob/wizardry/item/ItemWand.java @@ -45,6 +45,7 @@ import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import javax.annotation.Nullable; import java.util.List; import java.util.Random; @@ -625,97 +626,110 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem, } @Override - public boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks){ - - boolean changed = false; - + public ItemStack applyUpgrade(@Nullable EntityPlayer player, ItemStack wand, ItemStack upgrade){ + // Upgrades wand if necessary. Damage is copied, preserving remaining durability, // and also the entire NBT tag compound. - if(upgrade.getStack().getItem() == WizardryItems.arcane_tome){ + if(upgrade.getItem() == WizardryItems.arcane_tome){ - Tier tier = Tier.values()[upgrade.getStack().getItemDamage()]; + Tier tier = Tier.values()[upgrade.getItemDamage()]; // Checks the wand upgrade is for the tier above the wand's tier, and that either the wand has enough // progression or the player is in creative mode. - // It is guaranteed that: this == centre.getStack().getItem() - if((player.isCreative() || Wizardry.settings.legacyWandLevelling - || WandHelper.getProgression(centre.getStack()) >= tier.progression) + if((player == null || player.isCreative() || Wizardry.settings.legacyWandLevelling + || WandHelper.getProgression(wand) >= tier.progression) && tier.ordinal() - 1 == this.tier.ordinal()){ // We're not carrying over excess progression for now, but if we do want to, this is how // if(!Wizardry.settings.legacyWandLevelling){ // // Easy way to carry excess progression over to the new stack -// WandHelper.setProgression(centre.getStack(), WandHelper.getProgression(centre.getStack()) - tier.progression); +// WandHelper.setProgression(wand, WandHelper.getProgression(wand) - tier.progression); // } - WandHelper.setProgression(centre.getStack(), 0); + WandHelper.setProgression(wand, 0); ItemStack newWand = new ItemStack(WizardryItems.getWand(tier, this.element)); - newWand.setTagCompound(centre.getStack().getTagCompound()); + newWand.setTagCompound(wand.getTagCompound()); // This needs to be done after copying the tag compound so the mana capacity for the new wand // takes storage upgrades into account // Note the usage of the new wand item and not 'this' to ensure the correct capacity is used - ((IManaStoringItem)newWand.getItem()).setMana(newWand, this.getMana(centre.getStack())); + ((IManaStoringItem)newWand.getItem()).setMana(newWand, this.getMana(wand)); - centre.putStack(newWand); - upgrade.decrStackSize(1); - - changed = true; + upgrade.shrink(1); + + return newWand; } - }else if(WandHelper.isWandUpgrade(upgrade.getStack().getItem())){ + }else if(WandHelper.isWandUpgrade(upgrade.getItem())){ // Special upgrades - Item specialUpgrade = upgrade.getStack().getItem(); + Item specialUpgrade = upgrade.getItem(); - if(WandHelper.getTotalUpgrades(centre.getStack()) < this.tier.upgradeLimit - && WandHelper.getUpgradeLevel(centre.getStack(), specialUpgrade) < Constants.UPGRADE_STACK_LIMIT){ + if(WandHelper.getTotalUpgrades(wand) < this.tier.upgradeLimit + && WandHelper.getUpgradeLevel(wand, specialUpgrade) < Constants.UPGRADE_STACK_LIMIT){ // Used to preserve existing mana when upgrading storage rather than creating free mana. - int prevMana = this.getMana(centre.getStack()); + int prevMana = this.getMana(wand); - WandHelper.applyUpgrade(centre.getStack(), specialUpgrade); + WandHelper.applyUpgrade(wand, specialUpgrade); // Special behaviours for specific upgrades if(specialUpgrade == WizardryItems.storage_upgrade){ - this.setMana(centre.getStack(), prevMana); - + this.setMana(wand, prevMana); + }else if(specialUpgrade == WizardryItems.attunement_upgrade){ - int newSlotCount = BASE_SPELL_SLOTS + WandHelper.getUpgradeLevel(centre.getStack(), + int newSlotCount = BASE_SPELL_SLOTS + WandHelper.getUpgradeLevel(wand, WizardryItems.attunement_upgrade); - - Spell[] spells = WandHelper.getSpells(centre.getStack()); + + Spell[] spells = WandHelper.getSpells(wand); Spell[] newSpells = new Spell[newSlotCount]; for(int i = 0; i < newSpells.length; i++){ newSpells[i] = i < spells.length && spells[i] != null ? spells[i] : Spells.none; } - WandHelper.setSpells(centre.getStack(), newSpells); + WandHelper.setSpells(wand, newSpells); - int[] cooldowns = WandHelper.getCooldowns(centre.getStack()); + int[] cooldowns = WandHelper.getCooldowns(wand); int[] newCooldowns = new int[newSlotCount]; if(cooldowns.length > 0){ System.arraycopy(cooldowns, 0, newCooldowns, 0, cooldowns.length); } - WandHelper.setCooldowns(centre.getStack(), newCooldowns); + WandHelper.setCooldowns(wand, newCooldowns); } - upgrade.decrStackSize(1); - WizardryAdvancementTriggers.special_upgrade.triggerFor(player); + upgrade.shrink(1); - if(WandHelper.getTotalUpgrades(centre.getStack()) == Tier.MASTER.upgradeLimit){ - WizardryAdvancementTriggers.max_out_wand.triggerFor(player); + if(player != null){ + + WizardryAdvancementTriggers.special_upgrade.triggerFor(player); + + if(WandHelper.getTotalUpgrades(wand) == Tier.MASTER.upgradeLimit){ + WizardryAdvancementTriggers.max_out_wand.triggerFor(player); + } } - - changed = true; + } } + return wand; + } + + @Override + public boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks){ + + boolean changed = false; // Used for advancements + + if(upgrade.getHasStack()){ + ItemStack original = centre.getStack().copy(); + centre.putStack(this.applyUpgrade(player, centre.getStack(), upgrade.getStack())); + changed = ItemStack.areItemStacksEqual(centre.getStack(), original); + } + // Reads NBT spell metadata array to variable, edits this, then writes it back to NBT. // Original spells are preserved; if a slot is left empty the existing spell binding will remain. // Accounts for spells which cannot be applied because they are above the wand's tier; these spells diff --git a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java index e70301c1..9b5ff051 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java +++ b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java @@ -38,6 +38,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import javax.annotation.Nullable; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -247,25 +248,35 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana return 0; // Doesn't have any spell slots! } + @Override + public ItemStack applyUpgrade(@Nullable EntityPlayer player, ItemStack stack, ItemStack upgrade){ + + // Applies legendary upgrade + if(upgrade.getItem() == WizardryItems.armour_upgrade){ + + if(!stack.hasTagCompound()){ + stack.setTagCompound(new NBTTagCompound()); + } + + if(!stack.getTagCompound().hasKey("legendary")){ + stack.getTagCompound().setBoolean("legendary", true); + upgrade.shrink(1); + if(player != null) WizardryAdvancementTriggers.legendary.triggerFor(player); + } + } + + return stack; + } + @Override public boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks){ boolean changed = false; - - // Applies legendary upgrade - if(upgrade.getStack().getItem() == WizardryItems.armour_upgrade){ - - if(!centre.getStack().hasTagCompound()){ - centre.getStack().setTagCompound(new NBTTagCompound()); - } - - if(!centre.getStack().getTagCompound().hasKey("legendary")){ - - centre.getStack().getTagCompound().setBoolean("legendary", true); - upgrade.decrStackSize(1); - WizardryAdvancementTriggers.legendary.triggerFor(player); - changed = true; - } + + if(upgrade.getHasStack()){ + ItemStack original = centre.getStack().copy(); + centre.putStack(this.applyUpgrade(player, centre.getStack(), upgrade.getStack())); + changed = ItemStack.areItemStacksEqual(centre.getStack(), original); } // Charges armour by appropriate amount diff --git a/src/main/java/electroblob/wizardry/registry/WizardryRecipes.java b/src/main/java/electroblob/wizardry/registry/WizardryRecipes.java index 869088ff..5d2b5e7b 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryRecipes.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryRecipes.java @@ -14,8 +14,9 @@ import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.registries.IForgeRegistry; -import java.util.LinkedList; -import java.util.Queue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * Class responsible for defining and registering wizardry's non-JSON recipes (i.e. smelting recipes and dynamic @@ -29,14 +30,27 @@ public final class WizardryRecipes { private WizardryRecipes(){} // No instances! - private static final Queue chargingRecipeQueue = new LinkedList<>(); + private static final List chargeableItems = new ArrayList<>(); + + private static boolean registered; /** Adds the given item to the list of items that can be charged using mana flasks. Dynamic charging recipes * will be added for these items during {@code RegistryEvent.Register}. The item must implement * {@link IManaStoringItem} for the recipes to work correctly. This method should be called from the item's * constructor. */ public static void addToManaFlaskCharging(Item item){ - chargingRecipeQueue.offer(item); + + if(registered){ + Wizardry.logger.warn("Tried to add an item to mana flask charging after it was registered, this will do nothing!"); + return; + } + + chargeableItems.add(item); + } + + /** Returns an unmodifiable view of all registered items that can be charged with mana flasks. */ + public static List getChargeableItems(){ + return Collections.unmodifiableList(chargeableItems); } /** Now only deals with the dynamic crafting recipes and the smelting recipes. */ @@ -49,11 +63,7 @@ public final class WizardryRecipes { // Mana flask recipes - Item chargeable; - - while(!chargingRecipeQueue.isEmpty()){ - // Use remove() and not poll() because the queue shouldn't be empty in here - chargeable = chargingRecipeQueue.remove(); + for(Item chargeable : chargeableItems){ registry.register(new RecipeRechargeWithFlask(chargeable, (ItemManaFlask)WizardryItems.small_mana_flask) .setRegistryName(new ResourceLocation(Wizardry.MODID, "recipes/small_flask_" + chargeable.getRegistryName().getPath()))); @@ -65,6 +75,9 @@ public final class WizardryRecipes { .setRegistryName(new ResourceLocation(Wizardry.MODID, "recipes/large_flask_" + chargeable.getRegistryName().getPath()))); } + + registered = true; + } } diff --git a/src/main/resources/assets/ebwizardry/textures/gui/container/arcane_workbench_jei_background.png b/src/main/resources/assets/ebwizardry/textures/gui/container/arcane_workbench_jei_background.png new file mode 100644 index 0000000000000000000000000000000000000000..9bd25c580f7bda50feef7b665b6c2b43d7cd8bf8 GIT binary patch literal 10281 zcmeAS@N?(olHy`uVBq!ia0y~yU}OMc4mJh`hM1xiX$%aKid7*IB|(Yh3I#>^X_+~x z3MG{VsS2qTnQ06R6}R5b3ZJH8KFRg}#;!=FEfIWE<{DeiczV9JBZYI5#;$a`-ILaw z>FFw9_pwPVDF6R_|JlFd^;x}FpI)kx-CFhU$wc=#e=huee*Ss&ho67{-k$&A>v#Y4 zyYubc_Nnh%e%}7d@2@LQzJB_fJ?dXo?cYDUvVVVeeiJYL>+QLCai3kqPukai|GJ)g z>5od=n!oRkU4NeTFVQ;w|NXC}H$VSee*gbG>4O%nua~}f+J7$h zh|GVKy=;nB{q!&D8;k43_SkpNogcq``F!VxHGiv||Hk(0i{pQKePgxYuR_;{zvtHf zYTxVh-TvQC#j>>Jv+tgNm$uGj)voovf45EjE}S3JVzjqEbf46J@w&YC*&nXne597# zo};&K8@IB{qFlvx;j|3}_rJ@Ud7t@s?AUjSa-a8G=S9gBPUaC>xISUy(usZEbLZr2 znpR<17wui~UG{z5Y+t#0ruRLK-L2D)->=;nzxT)cn%dnHW_p>k+~0q+tJL+X$?1S+ zU(DYtDIVOH6ctxm+Y3hf)+b*3hepGuj{ki>G!COCf zF(^K|*Lt+Yg{_8nWs6iz_r_KS8S9@j*VqJo>*i?*dc=9|1n*V94M^7cKwb~|tXW531MbRxGUU7b}bZMHx4t@djB zblpFC#r1Qyewb>Ss> zC7NC;*$Xu+)ish+4DJhbx90EsJ#Xj!GqbgS#-ED*v-ynW(s%!ri~UyqK6OX_^SACb zbAL*x?qylYEgC$ZCt-6;`kO!Oa{pg`_neZ)Q@df8?ejfH)Qb+En#-i^b#DIU?Lm9K zpH1GrBg|-*_3_x-JUTfk{$ju8)S3q5U)}ll?%MeIU&QQk&1aq7-sjD6XhEm@SrPZ# zjTLpi?XzxtUY7jwb+=aQI`3pF?Y}fwZg1-63yWg|?CxHTJeB+K zR%T!+i{AGcyYY_9uQ%EqDBwmXcStzST)w?6Xn5P~#${ z%SB&QqfMl?O!f7@YnbPMdB@XTYyB1{p0rMt%~{Tvwxv4!RPWm>$Ly~jnzq*2efEzp zMVDV4ykr}s)%yS6zujCvZ+pM{z5nB7&zMOPp$pf^U-4cvQ!49O&O3qc9!Hi`zxuzX zdinm(5z7wwmHd%5*gMTYn_v5Z)}!) zTX!v*xUB0^)Y@YzXQbQ$gLfScXD-TmGATWzd-?JU+wS>kB^7u63p(GE`+_e!{Yb>w z4#o#5)B3l$Ms4}@`tg}9f?uY6zuT1@eEQa>XHutci>Jtj+|vDfBhI0Kt5sBPn}ce8 z+-~k2Cy&`KJnMHMSniLnPMM6^w$x1Bxc`~e154bZ38gPF}y`Yt=VtsY~X}et_S*n>!H^^Sv$zrk8H|$1Y z(Wzq_4|wVdg)6MQHLLQs?SqL7≶rRB|g!#nVxJ-p{_O0OpNC=Ps@kySHz%^p;tn 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 Date: Wed, 15 Apr 2020 23:02:04 +0100 Subject: [PATCH 057/415] Update spanish handbook translations, courtesy of Alsentar --- .../ebwizardry/texts/handbook_es_es.json | 198 +++++++++--------- 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json b/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json index 1a1f40fd..736cbedb 100644 --- a/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json +++ b/src/main/resources/assets/ebwizardry/texts/handbook_es_es.json @@ -1,6 +1,6 @@ { "bookmark_start_section": "introduction", - + "colours": { "text": "#000000", "caption": "#666666", @@ -8,10 +8,10 @@ "highlight": "#dd4c1d", "new_section": "#ee88f7" }, - + "images": { "workbench": { - "location": "ebwizardry:textures/gui/handbook/pictures/arcane_workbench_picture.png", + "location": "ebwizardry:textures/gui/arcane_workbench_picture.png", "caption": "Mesa Arcana", "u": 0, "v": 0, @@ -19,8 +19,8 @@ "height": 110 }, "crystal_ore": { - "location": "ebwizardry:textures/gui/handbook/pictures/ore_picture.png", - "caption": "Mena de Cristal Mágico", + "location": "ebwizardry:textures/gui/ore_picture.png", + "caption": "Mena de Cristal Magico", "u": 0, "v": 0, "width": 64, @@ -28,7 +28,7 @@ }, "magic_crystal": { "location": "ebwizardry:textures/items/crystal_magic.png", - "caption": "Cristal Mágico", + "caption": "Cristal Magico", "u": 6, "v": 6, "width": 36, @@ -39,7 +39,7 @@ }, "magic_wand": { "location": "ebwizardry:textures/items/wand_novice.png", - "caption": "Varita Mágica", + "caption": "Varita Magica", "u": 0, "v": 0, "width": 64, @@ -47,7 +47,7 @@ "border": false }, "crystal_flower": { - "location": "ebwizardry:textures/gui/handbook/pictures/flower_picture.png", + "location": "ebwizardry:textures/gui/flower_picture.png", "caption": "Flor de Cristal", "u": 0, "v": 0, @@ -55,7 +55,7 @@ "height": 48 }, "wizard_tower": { - "location": "ebwizardry:textures/gui/handbook/pictures/tower_picture.png", + "location": "ebwizardry:textures/gui/tower_picture.png", "caption": "Torre de mago", "u": 0, "v": 0, @@ -63,7 +63,7 @@ "height": 110 }, "wizard_armour": { - "location": "ebwizardry:textures/gui/handbook/pictures/armour_picture.png", + "location": "ebwizardry:textures/gui/armour_picture.png", "caption": "Conjunto de Armadura de mago", "u": 0, "v": 0, @@ -71,7 +71,7 @@ "height": 90 }, "obelisk": { - "location": "ebwizardry:textures/gui/handbook/pictures/obelisk_picture.png", + "location": "ebwizardry:textures/gui/obelisk_picture.png", "caption": "Obelisco", "u": 0, "v": 0, @@ -79,7 +79,7 @@ "height": 67 }, "shrine": { - "location": "ebwizardry:textures/gui/handbook/pictures/shrine_picture.png", + "location": "ebwizardry:textures/gui/shrine_picture.png", "caption": "Santuario de la tierra", "u": 0, "v": 0, @@ -87,7 +87,7 @@ "height": 67 }, "tiers": { - "location": "ebwizardry:textures/gui/handbook/pictures/tiers_picture.png", + "location": "ebwizardry:textures/gui/tiers_picture.png", "caption": "Los 4 niveles en las Varitas", "u": 0, "v": 0, @@ -96,7 +96,7 @@ "border": false }, "elements": { - "location": "ebwizardry:textures/gui/handbook/pictures/elements_picture.png", + "location": "ebwizardry:textures/gui/elements_picture.png", "caption": "Los 7 elementos Arcanos", "u": 0, "v": 0, @@ -226,36 +226,36 @@ "locations": [ "ebwizardry:spark_bomb" ] } }, - + "sections": { - + "inside_cover": { "centre": { "x": true, "y": true }, "text": [ - + "El Manual del Mago", - + "Por Electroblob" ] }, - + "introduction": { "title": "Introduccion", "text": [ + + "Saludos Mago! Este gran libro te mostrara los secretos de las artes arcanas, y de como usarlas. Este no es un libro comun y corriente, ya que su conocimiento y sus paginas aumentaran a medida que descubres mas y mas sobre mundo magico.", - "¡Saludos Mago! Este libro te mostrara los secretos de las artes arcanas, y de cómo usarlas. Este no es un libro común y corriente, ya que su conocimiento y sus páginas aumentarán a medida que descubres más y más sobre mundo mágico.", + "Utiliza los botones en forma de flecha para pasar la pagina, y los botones de doble flecha para pasar de una seccion a otra. Usa el boton de menu central para regresar al indice.", - "Utiliza los botones en forma de flecha para pasar la página, y los botones de doble flecha para pasar de una sección a otra. Usa el botón de menú central para regresar al índice.", - - "Haz clic en cualquier palabra en color purpura para ir directamente al tema en cuestión. Haz clic derecho al marcador rojo para moverlo a la página actual, y clic izquierdo para saltar a la página marcada." + "Haz clic en cualquier palabra en color purpura para ir directamente al tema en cuestion. Haz clic derecho al marcador rojo para moverlo a la pagina actual, y clic izquierdo para saltar a la pagina marcada." ] }, "main_contents": { - "title": "Ãndice", + "title": "Indice", "contents": { "id": "main_contents", "hyperlinks": true, @@ -265,13 +265,13 @@ }, "setting_up": { - "title": "Iniciación", + "title": "Iniciacion", "include_in_contents": "main_contents", "text": [ - "Para comenzar como mago, necesitarás conseguir lo siguiente:", + "Para comenzar como mago, necesitaras conseguir lo siguiente:", - "- Una Varita mágica, hecha de la siguiente forma:", + "- Una Varita magica, hecha de la siguiente forma:", "#recipe magic_wand", @@ -279,29 +279,29 @@ "#recipe arcane_workbench", - "- Un @spells Libro de hechizos@. Estos se pueden encontrar en cofres, recompensas por matar monstruos, y comprado a otros magos. Puedes crear el hechizo Misil Mágico, un hechizo de bajo nivel, de la siguiente forma:", + "- Un @spells Libro de hechizo@. Estos se pueden encontrar en cofres, recompensas por matar monstruos, y comprado a otros magos. Puedes crear el hechizo Misil Magico, un hechizo de bajo nivel, de la siguiente forma:", "#recipe magic_missile_spell_book", - "También necesitaras bastantes Cristales Mágicos para suplir tu varita de @mana@." + "Tambien necesitaras bastantes Cristales Mágicos para suplir tu varita de @mana@." ] }, "mana": { - "title": "Maná", + "title": "Mana", "include_in_contents": "main_contents", "triggers": [ "ebwizardry:crystal" ], "text": [ - "El Maná es la energía mística que concede a los magos sus habilidades. Mucho más que una sustancia física, el Maná es un aura que impregna la realidad de forma omnipresente. En nuestro mundo, suele acumularse hasta formar cristales sólidos, los cuales pueden ser hallados incrustados en piedra en el subsuelo. Las @wands Varitas@ almacenan Maná dentro de ellas, y este Maná es canalizado hacia fuera al conjurar un @spells Hechizo@. Diferentes Hechizos requieren diferentes cantidades de Maná, dependiendo de que tan poderosos sean y de cuanto duren sus efectos.", + "El Mana es la energia mistica que concede a los magos sus habilidades. Mucho mas que una sustancia fisica, el Mana es un aura que impregna la realidad de forma omnipresente. En nuestro mundo, suele acumularse hasta formar cristales solidos, los cuales pueden ser hallados incrustados en piedra en el subsuelo. Las @wands Varitas@ almacenan Mana dentro de ellas, y este Mana es canalizado hacia fuera al conjurar un @spells Hechizo@. Diferentes Hechizos requieren diferentes cantidades de Mana, dependiendo de que tan poderosos sean y de cuanto duren sus efectos.", "#image crystal_ore", "#image magic_crystal", - "Al ser energía pura, el Maná no puede ser creado ni destruido. Cuando se conjura un hechizo, El Maná canalizado simplemente se disipa en el ambiente. Ciertamente, la mayoría de Maná se encuentra esparcido en pequeñas cantidades a través de todo el Cosmos. Sin embargo, para poder ser usado, el Maná debe de estar concentrado. Esto se puede lograr de forma natural a través de miles de años, como pasa con los cristales mágicos, o de forma artificial, un método avanzado no abordado en este libro. También existen diferentes formas de que el Maná sea mejor administrado al conjurar hechizos, y de recuperar una parte del Maná disipado en el ambiente." + "Al ser energia pura, el Mana no puede ser creado ni destruido. Cuando se conjura un hechizo, El Mana canalizado simplemente se disipa en el ambiente. Ciertamente, la mayoria de Mana se encuentra esparcido en peque\u00f1as cantidades a traves de todo el Cosmos. Sin embargo, para poder ser usado, el Mana debe de estar concentrado. Esto se puede lograr de forma natural a traves de miles de a\u00f1os, como pasa con los cristales magicos, o de forma artificial, un metodo avanzado no abordado en este libro. Tambien existen diferentes formas de que el Mana sea mejor administrado al conjurar hechizos, y de recuperar una parte del Mana disipado en el ambiente." ] }, @@ -313,17 +313,17 @@ ], "text": [ - "La varita es el arma y herramienta principal de un mago. Con ella, podrás conjurar cualquier @spells hechizo@ que la varita pueda canalizar (véase @tiers Niveles@). Las varitas vienen en diferentes variedades, pero es muy probable que empieces con una varita básica.", + "La varita es el arma y herramienta principal de un mago. Con ella, podras conjurar cualquier @spells hechizo@ que la varita pueda canalizar (vease @tiers Niveles@). Las varitas vienen en diferentes variedades, pero es muy probable que empieces con una varita basica.", "#image magic_wand", - "La mayoria de varitas comienzan como una estructura simple de un cristal mágico unido a un palo de madera, con una pepita de oro unido en el otro extremo. El cristal es, claro está, la fuente de poder de la varita, ya que provee el foco necesario para canalizar @spells hechizos@. El resto de la varita es importante también, ya que no solo provee un medio para sostener la varita, sino también un circuito interno por el cual el @mana maná@ puede ser canalizado y dirigido.", + "La mayoria de varitas comienzan como una estructura simple de un cristal magico unido a un palo de madera, con una pepita de oro unido en el otro extremo. El cristal es, claro esta, la fuente de poder de la varita, ya que provee el foco necesario para canalizar @spells hechizos@. El resto de la varita es importante tambien, ya que no solo provee un medio para sostener la varita, sino tambien un circuito interno por el cual el @mana mana@ puede ser canalizado y dirigido.", - "A medida que se conjuren hechizos con la varita, esta se volverá más efectiva en canalizar dichos @spells hechizos@, y eventualmente, en conjurar hechizos mas poderosos. Esto se puede notar debido a ciertos cambios en la varita misma: Mientras mas poderosa se vuelva una varita, su cristal mágico se volverá más brillante, su color puede llegar a cambiar dependiendo de su @elements elemento@, y después de un tiempo, la madera de la que esta hecha comenzara a crecer por si sola y a retorcerse en figuras mas complejas.", + "A medida que se conjuren hechizos con la varita, esta se volvera mas efectiva en canalizar dichos @spells hechizos@, y eventualmente, en conjurar hechizos mas poderosos. Esto se puede notar debido a ciertos cambios en la varita misma: Mientras mas poderosa se vuelva una varita, su cristal magico se volvera mas brillante, su color puede llegar a cambiar dependiendo de su @elements elemento@, y despues de un tiempo, la madera de la que esta hecha comenzara a crecer por si sola y a retorcerse en figuras mas complejas.", - "Mientras sostienes una varita, un pequeño recuadro aparecerá en la esquina de tu pantalla. Este recuadro muestra el hechizo seleccionado actualmente, además de una imagen para representar tal hechizo, si el hechizo ya fue conjurado, y una barra de cooldown indicando el tiempo restante para poder conjurar el hechizo otra vez. Tambien muestra los nombres del hechizo anterior y el hechizo siguiente vinculados a la varita. Para cambiar de hechizo, utiliza las teclas #next_spell_key and #previous_spell_key (Estas pueden ser modificadas en Opciones > Controles). Tambien puedes cambiar de hechizo utilizando la rueda del mouse mientras estas agachado.", + "Mientras sostienes una varita, un peque\u00f1o recuadro aparecera en la esquina de tu pantalla. Este recuadro muestra el hechizo seleccionado actualmente, ademas de una imagen para representar tal hechizo, si el hechizo ya fue conjurado, y una barra de cooldown indicando el tiempo restante para poder conjurar el hechizo otra vez. Tambien muestra los nombres del hechizo anterior y el hechizo siguiente vinculados a la varita. Para cambiar de hechizo, utiliza las teclas #next_spell_key y #previous_spell_key (Estas pueden ser modificadas en Opciones > Controles). Tambien puedes cambiar de hechizo utilizando la rueda del mouse mientras estas agachado.", - "Cuando veas tu inventario, si pones el mouse sobre la varita, veras cuanto @mana mana@ le queda, junto con el @spells hechizo@ seleccionado actualmente y otras habilidades que tenga. Puedes visualizar más información sobre la varita colocándola en una @arcane_workbench mesa arcana@." + "Cuando veas tu inventario, si pones el mouse sobre la varita, veras cuanto @mana mana@ le queda, junto con el @spells hechizo@ seleccionado actualmente y otras habilidades que tenga. Puedes visualizar mas informacion sobre la varita colocandola en una @arcane_workbench mesa arcana@." ] }, @@ -335,15 +335,15 @@ ], "text": [ - "Sin hechizos para conjurar y lanzar, una @wands varita@ es prácticamente inútil. Los hechizos son grabados en dos formas: en libros, los cual los hace permanentes, y en pergaminos, los cuales son temporales.", + "Sin hechizos para conjurar y lanzar, una @wands varita@ es practicamente inutil. Los hechizos son grabados en dos formas: en libros, los cual los hace permanentes, y en pergaminos, los cuales son temporales.", - "Los libros de hechizos son inútiles por si mismos, por lo que son usados para vincular el hechizo que contienen a las @wands varitas@. Los libros de hechizos se encuentran pueden ser hallados a través de todo el planeta, y no tomara mucho tiempo encontrarte con uno. Cuando lo encuentres, puedes colocar el mouse sobre el libro de hechizo para ver información básica sobre el hechizo que contiene. También puedes darle clic derecho mientras lo sostienes para leer mas acerca de él.", + "Los libros de hechizos son inutiles por si mismos, por lo que son usados para vincular el hechizo que contienen a las @wands varitas@. Los libros de hechizos se encuentran pueden ser hallados a traves de todo el planeta, y no tomara mucho tiempo encontrarte con uno. Cuando lo encuentres, puedes colocar el mouse sobre el libro de hechizo para ver informacion basica sobre el hechizo que contiene. Tambien puedes darle clic derecho mientras lo sostienes para leer mas acerca de el.", - "Por otro lado, los pergaminos sirven otro propósito: Haciendo click derecho mientras sostienes uno conjurará el hechizo escrito en el, consumiendo el pergamino. Al igual que los libros de hechizos, los pergaminos pueden ser hallados en muchas partes del mundo, pero también puedes crearlos tu mismo(a) (véase @enchanting_scrolls Encantando Pergaminos@).", + "Por otro lado, los pergaminos sirven otro proposito: Haciendo click derecho mientras sostienes uno conjurara el hechizo escrito en el, consumiendo el pergamino. Al igual que los libros de hechizos, los pergaminos pueden ser hallados en muchas partes del mundo, pero tambien puedes crearlos tu mismo(a) (vease @enchanting_scrolls Encantando Pergaminos@).", - "Los pergaminos son bastante utiles cuando necesitas utilizar un hechizo una o dos veces en una misión específica, y no quieres que tome espacio innecesario en tu @wands varita@. De todas formas, cabe destacar que no son tan efectivas para conjurar hechizos en comparación con las varitas.", + "Los pergaminos son bastante utiles cuando necesitas utilizar un hechizo una o dos veces en una mision especifica, y no quieres que tome espacio innecesario en tu @wands varita@. De todas formas, cabe destacar que no son tan efectivas para conjurar hechizos en comparacion con las varitas.", - "Desde la perspectiva de alguien común y corriente, los símbolos y runas en los cuales están escritos los hechizos son solo garabatos ilegibles. Pero tú no eres alguien común. ¡Eres un mago, joder! Y como tal, poco a poco iras aprendiendo a descifrar este lenguaje. Para identificar un hechizo podrías, claro está, simplemente conjurarlo y ver que pasa. Pero ten en cuenta que al hacerlo puede haber efectos no deseados, además de que muchos hechizos necesitan ciertas condiciones para funcionar. Además de eso, para novatos es probable leer el hechizo de forma errónea y pueden llegar a haber consecuencias peligrosas dependiendo de que tan poderoso sea el hechizo. Una opción más segura es utilizar un pergamino de identificación, los cuales pueden ser encontrados en cofres o comprados a otros magos. Al hacer click derecho mientras se sostiene uno, se identificará el primer hechizo desconocido en tu inventario, consumiendo el pergamino de identificación." + "Desde la perspectiva de alguien comun y corriente, los simbolos y runas en los cuales estan escritos los hechizos son solo garabatos ilegibles. Pero tu no eres alguien comun. Eres un mago, joder! Y como tal, poco a poco iras aprendiendo a descifrar este lenguaje. Para identificar un hechizo podrias, claro esta, simplemente conjurarlo y ver que pasa. Pero ten en cuenta que al hacerlo puede haber efectos no deseados, ademas de que muchos hechizos necesitan ciertas condiciones para funcionar. Ademas de eso, para novatos es probable leer el hechizo de forma erronea y pueden llegar a haber consecuencias peligrosas dependiendo de que tan poderoso sea el hechizo. Una opcion mas segura es utilizar un pergamino de identificacion, los cuales pueden ser encontrados en cofres o comprados a otros magos. Al hacer click derecho mientras se sostiene uno, se identificara el primer hechizo desconocido en tu inventario, consumiendo el pergamino de identificacion." ] }, @@ -361,7 +361,7 @@ ], "text": [ - "La mesa arcana es un bloque similar a la mesa de encantamientos, donde podrás recargar, mejorar, y vincular @spells hechizos@ a tu @wands varita@. Puedes crearla de la siguiente forma:", + "La mesa arcana es un bloque similar a la mesa de encantamientos, donde podras recargar, mejorar, y vincular @spells hechizos@ a tu @wands varita@. Puedes crearla de la siguiente forma:", "#recipe arcane_workbench", @@ -378,7 +378,7 @@ ], "text": [ - "Para vincular un @spells hechizo@ a tu @wands varita@, solo tienes que colocar la varita en la ranura central de la mesa arcana, luego colocar el libro de hechizo que desees en las ranuras alrededor y presionar “Aplicarâ€. Como habrás de notar, el acto de vincular un hechizo no afecta al libro, esto es porque al vincular un hechizo, la mesa arcana no “toma†el hechizo del libro, sino que de cierta forma “copia†de forma resumida el hechizo en la varita. De esta forma, puedes cambiar los hechizos vinculados a tu varita tantas veces como quieras con solo repetir el proceso. Puedes vincular hasta cinco hechizos diferentes a una sola varita cada vez, aunque este numero puede aumentar con mejoras de vinculación." + "Para vincular un @spells hechizo@ a tu @wands varita@, solo tienes que colocar la varita en la ranura central de la mesa arcana, luego colocar el libro de hechizo que desees en las ranuras alrededor y presionar \u0022Aplicar\u0022. Como habras de notar, el acto de vincular un hechizo no afecta al libro, esto es porque al vincular un hechizo, la mesa arcana no \u0022toma\u0022 el hechizo del libro, sino que de cierta forma \u0022copia\u0022 de forma resumida el hechizo en la varita. De esta forma, puedes cambiar los hechizos vinculados a tu varita tantas veces como quieras con solo repetir el proceso. Puedes vincular hasta cinco hechizos diferentes a una sola varita cada vez, aunque este numero puede aumentar con mejoras de vinculacion." ] }, "charging_wands": { @@ -389,7 +389,7 @@ ], "text": [ - "Para recargar tu @wands varita@, coloca la varita en la ranura central de la mesa arcana, coloca algunos cristales mágicos en la ranura inferior izquierda y luego presiona “Aplicarâ€. Cada cristal vale #mana_per_crystal @mana maná@, y la varita solo tomara lo que necesita, por lo que puedes dejar una provision de cristales en la mesa arcana para cuando la necesites. Sin embargo, ten en cuenta que la varita solo puede tomar números de cristales enteros, así que si por ejemplo, la varita necesita 30 mas de maná, #example_charging_loss de maná se perderán al cargarla." + "Para recargar tu @wands varita@, coloca la varita en la ranura central de la mesa arcana, coloca algunos cristales magicos en la ranura inferior izquierda y luego presiona \u0022Aplicar\u0022. Cada cristal vale #mana_per_crystal @mana mana@, y la varita solo tomara lo que necesita, por lo que puedes dejar una provision de cristales en la mesa arcana para cuando la necesites. Sin embargo, ten en cuenta que la varita solo puede tomar numeros de cristales enteros, asi que si por ejemplo, la varita necesita 30 mas de mana, #example_charging_loss de mana se perderan al cargarla." ] }, "upgrading_wands": { @@ -400,7 +400,7 @@ ], "text": [ - "Para mejorar tu @wands varita@, necesitaras un Tomo de Arcana del @tiers nivel@ adecuado, o también, una mejora especial para tu varita. Los Tomos de Arcana y las mejoras especiales pueden ser hallados en cofres o comprados a otros magos. Para mejorar tu varita, colocala en la ranura central, y coloca el objeto de mejora en la ranura superior derecha, luego presiona “Aplicarâ€.", + "Para mejorar tu @wands varita@, necesitaras un Tomo de Arcana del @tiers nivel@ adecuado, o tambien, una mejora especial para tu varita. Los Tomos de Arcana y las mejoras especiales pueden ser hallados en cofres o comprados a otros magos. Para mejorar tu varita, colocala en la ranura central, y coloca el objeto de mejora en la ranura superior derecha, luego presiona \u0022Aplicar\u0022.", "Mejoras especiales para la varita cambian el aspecto de la varita. Cada tipo de mejora se puede acumular tres veces, y el total de mejoras que una varita puede tomar depende de su nivel. Solo recuerda que las mejoras no pueden ser removidas." ] @@ -413,7 +413,7 @@ ], "text": [ - "La mesa Arcana tambien puede ser usada para encantar @spells hechizos@ en pergaminos. Para hacerlo, necesitaras un pergamino en blanco, creado con papel y un poco de cuerda, un libro de hechizo de tu preferencia, y suficientes cristales mágicos para proveer @mana maná@ para conjurar.", + "La mesa Arcana tambien puede ser usada para encantar @spells hechizos@ en pergaminos. Para hacerlo, necesitaras un pergamino en blanco, creado con papel y un poco de cuerda, un libro de hechizo de tu preferencia, y suficientes cristales magicos para proveer @mana mana@ para conjurar.", "#recipe blank_scroll", @@ -431,11 +431,11 @@ ], "text": [ - "Como el legendario y poderoso mago que eres, necesitaras algo para protegerte de las criaturas y monstruos que obviamente vencerás. Las armaduras normales son buenas, sí… pero checa eso: Sombrero, túnica, pantalones y botas. A diferencia de las armaduras normales, la armadura de mago no se rompe, ya que utiliza maná para endurecer su tela al punto de simular el metal, y proteger al usuario. Estas armaduras deben de ser recargadas con cristales mágicos, como las varitas. Afortunadamente, estas ropas no consumen mucho @mana maná@. Recárgalas como lo harías con una varita en la @arcane_workbench mesa arcana@ o con @mana_flasks frascos de mana@. Pero ten cuidado, si se le acaba el maná, lo único que te protegerá de aquellos monstruos será simple tela.", + "Como el legendario y poderoso mago que eres, necesitaras algo para protegerte de las criaturas y monstruos que obviamente venceras. Las armaduras normales son buenas, si... pero checa eso: Sombrero, tunica, pantalones y botas. A diferencia de las armaduras normales, la armadura de mago no se rompe, ya que utiliza mana para endurecer su tela al punto de simular el metal, y proteger al usuario. Estas armaduras deben de ser recargadas con cristales magicos, como las varitas. Afortunadamente, estas ropas no consumen mucho @mana mana@. Recargalas como lo harias con una varita en la @arcane_workbench mesa arcana@ o con @mana_flasks frascos de mana@. Pero ten cuidado, si se le acaba el mana, lo único que te protegera de aquellos monstruos sera simple tela.", "#image wizard_armour", - "Puedes crear una armadura de mago a partir de tela mágica:", + "Puedes crear una armadura de mago a partir de tela magica:", "#recipe magic_silk", "#recipe wizard_hat", @@ -443,11 +443,11 @@ "#recipe wizard_leggings", "#recipe wizard_boots", - "Puedes encantar una armadura de mago usando una mesa de encantamientos común, y debido a las propiedades de la tela mágica, es bastante efectiva en retener encantamientos. Los encantamientos de Protección contra Magia, Hielo y Electricidad son de particular utilidad para magos como tú, especialmente en defensa contra otros magos.", + "Puedes encantar una armadura de mago usando una mesa de encantamientos comun, y debido a las propiedades de la tela magica, es bastante efectiva en retener encantamientos. Los encantamientos de Proteccion contra Magia, Hielo y Electricidad son de particular utilidad para magos como tu, especialmente en defensa contra otros magos.", - "Aquellos magos que se dedican a la práctica de un elemento particular suelen llevar ropas especiales que les proporcionan efectos adicionales para dominar tal elemento. Conjuntos enteros de aquellas ropas son muy buscadas por los magos y no son fáciles de encontrar.", + "Aquellos magos que se dedican a la practica de un elemento particular suelen llevar ropas especiales que les proporcionan efectos adicionales para dominar tal elemento. Conjuntos enteros de aquellas ropas son muy buscadas por los magos y no son faciles de encontrar.", - "Leyendas hablan sobre un sello arcano usado por los magos y brujas mas poderosos. Dicen que aquel sello volverá la armadura de cualquier mago tan resistente como su contraparte mundana…" + "Leyendas hablan sobre un sello arcano usado por los magos y brujas mas poderosos. Dicen que aquel sello volvera la armadura de cualquier mago tan resistente como su contraparte mundana..." ] }, @@ -465,9 +465,9 @@ ], "text": [ - "No puedes dominar las artes arcanas quedándote encerrado en tu cuarto ¿o sí? ¡Hay un mundo entero de magia allá afuera para explorar! Ruinas Antiguas, reliquias y seres misteriosos benévolos y malévolos esperan ser descubiertos – si sabes donde buscar. Los más grandes magos también son los más curiosos, y mucho de su conocimiento y poder lo encontraron explorando el mundo que los rodea, y experimentando con lo que encuentran.", + "No puedes dominar las artes arcanas quedandote encerrado en tu cuarto, o si? Hay un mundo entero de magia alla afuera para explorar! Ruinas Antiguas, reliquias y seres misteriosos benevolos y malevolos esperan ser descubiertos, si sabes donde buscar. Los mas grandes magos tambien son los mas curiosos, y mucho de su conocimiento y poder lo encontraron explorando el mundo que los rodea, y experimentando con lo que encuentran.", - "Aunque un mago pasa la mayoría de su tiempo solo, compartir conocimiento es vital para aprender las artes arcanas. Busca a otros practicantes de la magia y aprende todo lo que puedas de ellos. La comunicación es uno de los poderes mas grandes que puedas tener." + "Aunque un mago pasa la mayoria de su tiempo solo, compartir conocimiento es vital para aprender las artes arcanas. Busca a otros practicantes de la magia y aprende todo lo que puedas de ellos. La comunicacion es uno de los poderes mas grandes que puedas tener." ], "sections": { "crystal_flowers": { @@ -478,7 +478,7 @@ ], "text": [ - "Durante tu travesía, es probable que te encuentre una curiosa y colorida flor que emite luz. Estas interesantes flores se les conoce como flores de cristal, y son altamente concentradas en @mana maná@. Nade sabe el porque de esto, lo que si sabemos es que sus pétalos están hechos de cristales de mana, haciendo de estas flores una fuente útil de mana en la superficie. La cantidad de maná obtenida de esta forma es limitada debido al pequeño tamaño de las flores, y que suelen crecer en grupos pequeños.", + "Durante tu travesia, es probable que te encuentre una curiosa y colorida flor que emite luz. Estas interesantes flores se les conoce como flores de cristal, y son altamente concentradas en @mana mana@. Nade sabe el porque de esto, lo que si sabemos es que sus petalos están hechos de cristales de mana, haciendo de estas flores una fuente util de mana en la superficie. La cantidad de mana obtenida de esta forma es limitada debido al peque\u00f1o tama\u00f1o de las flores, y que suelen crecer en grupos peque\u00f1os.", "#image crystal_flower", "#recipe crystal_flower_to_crystals" @@ -492,15 +492,15 @@ ], "text": [ - "No temas, no eres el único practicante de magia en este mundo. En tus viajes, puede que te encuentres con una o dos altas torres con techos puntiagudos. Esta es la residencia de un mago. A veces una vida de soledad puede volver a los magos un poco gruñones, pero usualmente son personas amigables que están dispuestos a compartir su conocimiento contigo, por un precio. No te sorprendas si debes pagar raros y preciosos metales y gemas, ya que a cambio recibirás grandes maravillas arcanas.", + "No temas, no eres el unico practicante de magia en este mundo. En tus viajes, puede que te encuentres con una o dos altas torres con techos puntiagudos. Esta es la residencia de un mago. A veces una vida de soledad puede volver a los magos un poco gru\u00f1ones, pero usualmente son personas amigables que estan dispuestos a compartir su conocimiento contigo, por un precio. No te sorprendas si debes pagar raros y preciosos metales y gemas, ya que a cambio recibiras grandes maravillas arcanas.", "En el caso de que fuese un hechizo de nivel @tiers maestro@ lo que buscas, necesitaras hablar con un especialista.", "#image wizard_tower", - "Desafortunadamente, existen algunos magos en cuyas puertas los visitantes no son bienvenidos. Estos magos suelen ser marginados, y son hostiles hacia todo aquel que se cruce en su camino. Ten cuidado con estos individuos. Si los encuentras, prepárate para defenderte contra magia poderosa. Véncelos, y su conocimiento será tuyo.", + "Desafortunadamente, existen algunos magos en cuyas puertas los visitantes no son bienvenidos. Estos magos suelen ser marginados, y son hostiles hacia todo aquel que se cruce en su camino. Ten cuidado con estos individuos. Si los encuentras, preparate para defenderte contra magia poderosa. Vencelos, y su conocimiento sera tuyo.", - "Cabe destacar que los magos no toman de buena forma el ser atacados o robados, y siendo ese el caso, no dudarán en tomar la justicia por sus propias manos." + "Cabe destacar que los magos no toman de buena forma el ser atacados o robados, y siendo ese el caso, no dudaran en tomar la justicia por sus propias manos." ] }, "obelisks": { @@ -511,13 +511,13 @@ ], "text": [ - "Vestigios de antiguas civilizaciones yacen esparcidas por todo el mundo. Los mas notables son estructuras de piedra tallada con símbolos y runas. Estas ruinas son todo lo que queda de lo que parece ser alguna clase de civilización antigua. Quien sea, o lo que sea que las haya construido, claramente tenían amplios conocimientos sobre magia, conocimientos que uso para conjurar encantamientos protectores que aun hoy día permanecen en aquellos lugares.", + "Vestigios de antiguas civilizaciones yacen esparcidas por todo el mundo. Los mas notables son estructuras de piedra tallada con simbolos y runas. Estas ruinas son todo lo que queda de lo que parece ser alguna clase de civilizacion antigua. Quien sea, o lo que sea que las haya construido, claramente tenian amplios conocimientos sobre magia, conocimientos que uso para conjurar encantamientos protectores que aun hoy dia permanecen en aquellos lugares.", - "Estas estructuras suelen ser de dos tipos. El primero, y el mas común, son los Obeliscos: altas construcciones puntiagudas hechas de piedras rúnicas, con una estructura inferior abierta que contiene reliquias arcanas menores, ya olvidadas en el pasado.", + "Estas estructuras suelen ser de dos tipos. El primero, y el mas comun, son los Obeliscos: altas construcciones puntiagudas hechas de piedras runicas, con una estructura inferior abierta que contiene reliquias arcanas menores, ya olvidadas en el pasado.", "#image obelisk", - "Estas estructuras suelen estar protegidas por un encantamiento que invoca @creatures criaturas@ del Caos Elemental cuando un humano se acerca demasiado. Vence a estas criaturas, destruye su invocador, y las reliquias serán tuyas." + "Estas estructuras suelen estar protegidas por un encantamiento que invoca @creatures criaturas@ del Caos Elemental cuando un humano se acerca demasiado. Vence a estas criaturas, destruye su invocador, y las reliquias seran tuyas." ] }, "shrines": { @@ -528,11 +528,11 @@ ], "text": [ - "El Segundo tipo es mucho mas raro, y son conocidos como santuarios. Estas estructuras consisten en un circulo de pilares de piedras rúnicas rodeando un pedestal central, en el cualse encuentra un cofre que contiene @artefacts artefactos@. Este cofre suele estar protegido por un encantamiento de Cerradura Arcana, impidiendo que cualquiera excepto el dueño lo abra. La estructura completa también esta protegida por un campo de contención, el cual impide el escape de cualquier cosa que se acerque demasiado.", + "El Segundo tipo es mucho mas raro, y son conocidos como santuarios. Estas estructuras consisten en un circulo de pilares de piedras runicas rodeando un pedestal central, en el cual se encuentra un cofre que contiene @artefacts artefactos@. Este cofre suele estar protegido por un encantamiento de Cerradura Arcana, impidiendo que cualquiera excepto el due\u00f1o lo abra. La estructura completa tambien esta protegida por un campo de contencion, el cual impide el escape de cualquier cosa que se acerque demasiado.", "#image shrine", - "Debido al gran poder e importancia arcana, sin mencionar las riquezas que contiene, estas estructuras son particularmente atractivas para cualquier mago aspirante. Pero ten cuidado, ha habido reportes de magos quedando atrapados en en el campo de contención y lentamente perdiendo la cordura a medida que pasa el tiempo. Algunos otros, creen que aquellas ocurrencias es un deliberado esfuerzo de la magia protectora del santuario, y que aquellos magos atrapados son controlados para proteger la estructura. Una perspectiva escalofriante sin duda para todo aquel que se atreva a aventurarse cerca…." + "Debido al gran poder e importancia arcana, sin mencionar las riquezas que contiene, estas estructuras son particularmente atractivas para cualquier mago aspirante. Pero ten cuidado, ha habido reportes de magos quedando atrapados en en el campo de contencion y lentamente perdiendo la cordura a medida que pasa el tiempo. Algunos otros, creen que aquellas ocurrencias es un deliberado esfuerzo de la magia protectora del santuario, y que aquellos magos atrapados son controlados para proteger la estructura. Una perspectiva escalofriante sin duda para todo aquel que se atreva a aventurarse cerca...." ] }, "creatures": { @@ -543,7 +543,7 @@ ], "text": [ - "Además de magos, una gran cantidad de criaturas utilizan magia. Muchas de estas criaturas son de origen arcano, y la mayoría pueden ser invocadas desde el Caos Elemental usando @spells hechizos@. Es posible encontrarse con estas criaturas protegiendo un obelisco o a un poderoso mago invocador. Seres arcanos menores pueden ser encontrados casualmente en la naturaleza." + "Ademas de magos, una gran cantidad de criaturas utilizan magia. Muchas de estas criaturas son de origen arcano, y la mayoria pueden ser invocadas desde el Caos Elemental usando @spells hechizos@. Es posible encontrarse con estas criaturas protegiendo un obelisco o a un poderoso mago invocador. Seres arcanos menores pueden ser encontrados casualmente en la naturaleza." ] }, "artefacts": { @@ -554,7 +554,7 @@ ], "text": [ - "Al explorar un @shrines santuario@, puedes ser lo bastante afortunado(a) como para encontrar alguna clase de artefacto mágico antiguo, un objeto capaz de concederle a su portador poderes y habilidades únicas y poderosas. Tres tipos de artefactos se han encontrado son: anillos, lo cuales conceden efectos especiales a hechizos, amuletos, los cuales mejoran habilidades defensivas y talismanes, que conceden efectos de utilidad. Estos artefactos parecen funcionar solo si se llevan de forma apropiada, el simple hecho de tenerla en su persona es insuficiente." + "Al explorar un @shrines santuario@, puedes ser lo bastante afortunado(a) como para encontrar alguna clase de artefacto magico antiguo, un objeto capaz de concederle a su portador poderes y habilidades unicas y poderosas. Tres tipos de artefactos se han encontrado son: anillos, lo cuales conceden efectos especiales a hechizos, amuletos, los cuales mejoran habilidades defensivas y talismanes, que conceden efectos de utilidad. Estos artefactos parecen funcionar solo si se llevan de forma apropiada, el simple hecho de tenerla en su persona es insuficiente." ] } } @@ -568,21 +568,21 @@ ], "text":[ - "Las @wands varitas@ y @spells hechizos@ vienen en cuatro niveles: #colour_novicenovice#colour_reset, #colour_apprenticeapprentice#colour_reset, #colour_advancedadvanced #colour_resetand #colour_mastermaster#colour_reset. Cada nivel es mas poderoso que el anterior.", + "Las @wands varitas@ y @spells hechizos@ vienen en cuatro niveles: #colour_novicenovato#colour_reset, #colour_apprenticeapprendiz#colour_reset, #colour_advancedavanzado #colour_reset y #colour_mastermaestro#colour_reset. Cada nivel es mas poderoso que el anterior.", "#image tiers", - "#colour_noviceNovice #colour_resetwands son las que pueden ser creadas. Pueden almacenar hasta #novice_max_charge maná, y solo pueden conjurar hechizos de nivel novato. Hechizos de nivel novato son lo bastante simples para ser conjurados por cualquiera y no suelen usar mucho maná. Esto no significa que son inútiles en niveles mas altos, ya que su bajo costo los hace bastante útiles incluso al tener acceso a hechizos de nivel maestro.", + "#colour_noviceNovato #colour_reset,es el nivel de varita en las que pueden ser creadas. Pueden almacenar hasta #novice_max_charge mana, y solo pueden conjurar hechizos de nivel novato. Hechizos de nivel novato son lo bastante simples para ser conjurados por cualquiera y no suelen usar mucho mana. Esto no significa que son inutiles en niveles mas altos, ya que su bajo costo los hace bastante utiles incluso al tener acceso a hechizos de nivel maestro.", - "#colour_apprenticeApprentice #colour_resetwands son el siguiente nivel de varitas después del nivel novato. Pueden almacenar hasta #apprentice_max_charge maná y puede conjurar hechizos de novato y hechizos de aprendiz. Los hechizos de aprendiz son mas impresionantes que los hechizos de novato, pero suelen costar más.", + "#colour_apprenticeApprendiz #colour_reset, es el siguiente nivel de varitas después del nivel novato. Pueden almacenar hasta #apprentice_max_charge mana y puede conjurar hechizos de novato y hechizos de aprendiz. Los hechizos de aprendiz son mas impresionantes que los hechizos de novato, pero suelen costar mas.", - "#colour_advancedAdvanced #colour_resetwands son mas raras y ponderosas. Pueden conjurar todos los hechizos excepto hechizos de nivel maestro, y almacenan hasta #advanced_max_charge maná. Hechizos de nivel avanzado pueden conceder poderes y habilidades super humanas como inivisibilidad y causar estragos en enemigos.", + "#colour_advancedAvanzado #colour_resetwands,las varitas de este nivel son mas raras y poderosas. Pueden conjurar todos los hechizos excepto hechizos de nivel maestro, y almacenan hasta #advanced_max_charge mana. Hechizos de nivel avanzado pueden conceder poderes y habilidades super humanas como invisibilidad y causar estragos en enemigos.", - "#colour_masterMaster #colour_resetwands son las varitas más poderosas que existen. Almacenan hasta #master_max_charge maná y pueden conjurar cualquier hechizo. Libros de hechizo de nivel maestro son muy raros, y ciertos hechizos pueden causar una terrible devastación no solo a enemigos, sino al mundo mismo. Usar con precaución.", + "#colour_masterMaestro #colour_reset,las varitas mas poderosas que existen. Almacenan hasta #master_max_charge mana y pueden conjurar cualquier hechizo. Libros de hechizo de nivel maestro son muy raros, y ciertos hechizos pueden causar una terrible devastacion no solo a enemigos, sino al mundo que los rodea. Usar con precaucion.", - "Para poder ser mejorada a un nivel mayor, la varita debe de volverse lo suficientemente poderosa como para canalizar hechizos de ese nivel. Las varitas ganan poder a medida de que hechizos son conjurados con ellas. Variedad, poder, efectos elementales y otros fatores externos pueden tener efecto en que tan rápido una varita gana este poder.", + "Para poder ser mejorada a un nivel mayor, la varita debe de volverse lo suficientemente poderosa como para canalizar hechizos de ese nivel. Las varitas ganan poder a medida de que hechizos son conjurados con ellas. Variedad, poder, efectos elementales y otros fatores externos pueden tener efecto en que tan rapido una varita gana este poder.", - "Una vez que una varita es lo suficientemente poderosa, un tomo de arcana proveerá el catalizador final para elevar a la varita al siguiente nivel (véase @upgrading_wands Mejorando varitas@), una vez que esto pasa, el proceso de maduración para la varita comienza de nuevo." + "Una vez que una varita es lo suficientemente poderosa, un tomo de arcana proveera el catalizador final para elevar a la varita al siguiente nivel (vease @upgrading_wands Mejorando varitas@), una vez que esto pasa, el proceso de maduracion para la varita comienza de nuevo." ] }, @@ -598,23 +598,23 @@ "#image elements", - "#colour_fireFire#colour_reset \n Talvez el elemento mas destructivo. El fuego se trata sobre quemar, consumir, lava y explosiones. Un Pirómano poderoso desatará el infierno ante sus enemigos y probablemente incendie el mundo en el proceso. La mayoría de hechizos de fuego envuelven a los enemigos en llamas, causando Daño adicional con el tiempo. Sin embargo, ten en cuenta que los hechizos de fuego no tienen efecto sobre las criaturas provenientes del Nether.", + "#colour_fireFuego#colour_reset \n Talvez el elemento mas destructivo. El fuego se trata sobre quemar, consumir, lava y explosiones. Un Piromano poderoso desatara el infierno ante sus enemigos y probablemente incendie el mundo en el proceso. La mayoria de hechizos de fuego envuelven a los enemigos en llamas, causando Da\u00f1o adicional con el tiempo. Sin embargo, ten en cuenta que los hechizos de fuego no tienen efecto sobre las criaturas provenientes del Nether.", - "#colour_iceIce#colour_reset \n El elemento de hielo contiene todo lo que es frio. Los hechizos gélidos suelen ralentizar a los enemigos o congelarlos completamente, y son especialmente efectivos en criaturas de fuego. Los hechizos de hielo suelen también ser útiles fuera de combate, como por ejemplo, congelar el agua para cruzar un rio.", + "#colour_iceHielo#colour_reset \n El elemento de hielo contiene todo lo que es frio. Los hechizos gelidos suelen ralentizar a los enemigos o congelarlos completamente, y son especialmente efectivos en criaturas de fuego. Los hechizos de hielo suelen tambien ser utiles fuera de combate, como por ejemplo, congelar el agua para cruzar un rio.", - "#colour_lightningLightning#colour_reset \n Este es el element que concierne los relámpagos, truenos y rayos, tormentas, lluvias y el clima. Un poderoso mago de la tormenta es una fuerza a temer, y algunos poseen la habilidad de comandar a los rayos a voluntad. Los hechizos de tormenta suelen dañar múltiples enemigos al mismo tiempo e incluso siguen a sus objetivos, haciendo de estos un ataque efectivo contra cualquier enemigo…..pero ten cuidado con los creepers.", + "#colour_lightningElectricidad#colour_reset \n Este es el elemento que concierne los relampagos, truenos y rayos, tormentas, lluvias y el clima. Un poderoso mago de la tormenta es una fuerza a temer, y algunos poseen la habilidad de comandar a los rayos a voluntad. Los hechizos de tormenta suelen da\u00f1ar multiples enemigos al mismo tiempo e incluso siguen a sus objetivos, haciendo de estos un ataque efectivo contra cualquier enemigo....pero ten cuidado con los creepers.", - "#colour_necromancyNecromancy#colour_reset \n La necromancia es el elemento de la oscuridad, caos y de la no-muerte. Los necromagos son misteriosos, y suelen considerarse malvados, aunque este no suele ser el caso. Los hechizos de necromancia suelen ser usados para invocar @creatures criaturas@ para pelear por ti o incluso doblegar la voluntad de tus enemigos.", + "#colour_necromancyNecromancia#colour_reset \n La necromancia es el elemento de la oscuridad, caos y de la no-muerte. Los necromagos son misteriosos, y suelen considerarse malvados, aunque este no suele ser el caso. Los hechizos de necromancia suelen ser usados para invocar @creatures criaturas@ para pelear por ti o incluso doblegar la voluntad de tus enemigos.", - "#colour_earthEarth#colour_reset \n El elemento de la tierra se enfoca en el mundo natural: animales, plantas, el viento, y cosas por el estilo. Los hechizos de tierra son diversos y toman una gran variedad de formas, desde envenenar enemigos hasta desencadenar la furia del clima terrenal. Los hechizos de tierra suelen ser una mezcla de ataque, defensa y utilidad.", + "#colour_earthTierra#colour_reset \n El elemento de la tierra se enfoca en el mundo natural: animales, plantas, el viento, y cosas por el estilo. Los hechizos de tierra son diversos y toman una gran variedad de formas, desde envenenar enemigos hasta desencadenar la furia del clima terrenal. Los hechizos de tierra suelen ser una mezcla de ataque, defensa y utilidad.", - "#colour_sorcerySorcery#colour_reset \n La hechicería es el elemento del cambio y la fuerza. Los hechiceros manipulan la luz, la gravedad e incluso la realidad misma para que se ajuste a sus necesidades. Los hechizos de hechicería pueden ser usados, entre muchas otras cosas, para darle al mago poderes especiales o mover objetos a voluntad.", + "#colour_sorceryHechiceria#colour_reset \n La hechiceria es el elemento del cambio y la fuerza. Los hechiceros manipulan la luz, la gravedad e incluso la realidad misma para que se ajuste a sus necesidades. Los hechizos de hechiceria pueden ser usados, entre muchas otras cosas, para darle al mago poderes especiales o mover objetos a voluntad.", - "#colour_healingHealing#colour_reset \n El elemento de curación es un elemento perfectamente válido, ¿verdad? ¡¿Verdad?! Este elemento se trata sobre defensa y regeneración. Los curanderos buscan protegerse a ellos mismos y a sus aliados en los mas posible y por eso suelen ser oponentes formidables. Aunque no suele ser usado de forma ofensiva, la luz purificadora de algunos hechizos de curación hará un Daño considerable a los no-muertos. Los hechizos de curación son indispensables en combate", + "#colour_healingCuracion#colour_reset \n El elemento de curacion es un elemento perfectamente valido, verdad? Verdad?! Este elemento se trata sobre defensa y regeneracion. Los curanderos buscan protegerse a ellos mismos y a sus aliados en los mas posible y por eso suelen ser oponentes formidables. Aunque no suele ser usado de forma ofensiva, la luz purificadora de algunos hechizos de curacion hara un Da\u00f1o considerable a los no-muertos. Los hechizos de curacion son indispensables en combate", - "Los límites entre diferentes elementos no son siempre diferentes, y algunos hechizos suelen tener semejanza a otros elementos diferentes al suyo.", + "Los limites entre diferentes elementos no son siempre diferentes, y algunos hechizos suelen tener semejanza a otros elementos diferentes al suyo.", - "Si tienes suerte, podrás encontrar un cristal elemental. Los cristales elementales son cristales mágicos que absorbieron propiedades elementales mientras se formaban. Estos cristales pueden ser usados para crear varitas elementales, las cuales potencian los hechizos de tal elemento." + "Si tienes suerte, podras encontrar un cristal elemental. Los cristales elementales son cristales magicos que absorbieron propiedades elementales mientras se formaban. Estos cristales pueden ser usados para crear varitas elementales, las cuales potencian los hechizos de tal elemento." ] }, @@ -639,18 +639,18 @@ ], "text": [ - "Provisiones Arcanas de Arkendur inc. – ¡Objetos mágicos para todas tus necesidades!", + "Provisiones Arcanas de Arkendur inc. Objetos magicos para todas tus necesidades!", - "¿Necesitas recargar tu @wands varita@ de forma rápida? ¡No hay problema! El @mana mana@ ya puede ser embotellado. Simplemente creas un frasco de mana con una botella y ocho cristales mágicos. Cuando quieras usarlo, solo colócalo en la mesa de crafteo con tu varita y cargara cierta parte. *", + "Necesitas recargar tu @wands varita@ de forma rapida? No hay problema! El @mana mana@ ya puede ser embotellado. Simplemente creas un frasco de mana con una botella y ocho cristales magicos. Cuando quieras usarlo, solo colocalo en la mesa de crafteo con tu varita y cargara cierta parte. *", "#recipe medium_mana_flask", - "¡NUEVO! Aquí llegaron los nuevos frascos de mana de tamaños pequeños y grandes – ¡ahora puedes seleccionar un frasco que satisface tus necesidades!", + "NUEVO! Aqui llegaron los nuevos frascos de mana de tama\u00f1os peque\u00f1os y grandes, ahora puedes seleccionar un frasco que satisface tus necesidades!", "#recipe small_mana_flask", "#recipe large_mana_flask", - "* Este proceso destruirá la botella. Cierto nivel de mana se pierde durante el embotellamiento." + "* Este proceso destruira la botella. Cierto nivel de mana se pierde durante el embotellamiento." ] }, "throwable_items": { @@ -661,7 +661,7 @@ ], "text": [ - "Algunos @spells hechizos@ conjuran objetos físicos, algunos de estos pueden ser creados directamente. Bombas de fuego, Bombas de veneno, Bombas de chispas y Bombas de humo pueden todas ser creadas:", + "Algunos @spells hechizos@ conjuran objetos fisicos, algunos de estos pueden ser creados directamente. Bombas de fuego, Bombas de veneno, Bombas de chispas y Bombas de humo pueden todas ser creadas:", "#recipe firebomb", "#recipe poison_bomb", @@ -676,13 +676,13 @@ "ebwizardry:enchant_scroll" ], "text": [ - - "Experimentación reciente ha revelado que es posible automatizar la conjuración de @spells hechizos@, hasta cierto punto, usando nada mas que un dispensador simple. Nadie sabe porque, pero parece que las extrañas propiedades de la piedra roja se extienden hasta la conjuración de un pergamino de hechizo. Poniendo un par de estos en un dispensador y dándole poder serviría como una buena trampa…." + + "Experimentacion reciente ha revelado que es posible automatizar la conjuracion de @spells hechizos@, hasta cierto punto, usando nada mas que un dispensador simple. Nadie sabe porque, pero parece que las extra\u00f1as propiedades de la piedra roja se extienden hasta la conjuracion de un pergamino de hechizo. Poniendo un par de estos en un dispensador y dandole poder serviria como una buena trampa." ] } } }, - + "crafting_recipes": { "title": "Recetas para crafteos", "include_in_contents": "main_contents", @@ -714,37 +714,37 @@ "#recipe spark_bomb" ] }, - + "credits": { "title": "Creditos", "include_in_contents": "main_contents", "text": [ - + "Electroblob's Wizardry \nVersion #version \nPara Minecraft #mcversion", - "Diseñado, programado y texturizado por Electroblob", + "Dise\u00f1ado, programado y texturizado por Electroblob", "Gracias a Minecraft Forge y MCP, cuya presencia hizo posible este mod.", - "¡Gracias también a la Comunidad de Mods de Minecraft, que siempre tiene una respuesta para mis preguntas!", + "Gracias tambien a la Comunidad de Mods de Minecraft, que siempre tiene una respuesta para mis preguntas!", - "Adicionalmente, me gustaría agradecer a los siguientes individuos por sus contribuciones al mod:", + "Adicionalmente, me gustaria agradecer a los siguientes individuos por sus contribuciones al mod:", "Codigo:", - "- Corail31 \n- 12foo \n- Shadows-of-Fire \n- HellFirePvP \n- Tora-B \n- Avatair \n- Aeronica \n- UltraHex \n- Azim-Palmer \n- raoulvdberge \n- rafasoares", + "- Corail31 \n- 12foo \n- Shadows-of-Fire \n- HellFirePvP \n- Tora-B \n- Avatair \n- Aeronica", "Traducciones:", - "- Español: MadWrist, Alsentar \n- Español de Mexico: MadWrist \n- Ruso: VilagVil, kellixon, bigenergy \n- Frances: Hahdrim \n- Portuges Brasileño: lorrampi \n- Chino: ZHENGLOC, dragon-evol, Hokorizero, TUsama, Determancer \n- Koreano: shejery, rewi_wire \n- Polaco: Trozuu, \nAlemán: BirdyDragon", + "- Espa\u00f1ol de Espa\u00f1a y Mexico: MadWrist, Alsentar \n- Ruso: VilagVil, kellixon \n- Frances: Hahdrim \n- Portuges Brasile\u00f1o: lorrampi \n- Chino: ZHENGLOC, dragon-evol \n- Koreano: shejery, rewi_wire \n- Polaco: Trozuu", - "Efecto de sonido para los relámpagos por OhhWowProductions", + "Efecto de sonido para los relampagos por OhhWowProductions", "Para mas informacion, revisa nuestra @https://github.com/Electroblob77/Wizardry/wiki wiki@.", - "¿Te encanta este mod? ¡unete al @https://discord.gg/MTmMzMv servidor de Discord@ para las ultimas noticias,discusiones y extras!" + "Te encanta este mod? unete al @https://discord.gg/MTmMzMv servidor de Discord@ para las ultimas noticias,discusiones y extras!" ] } - + } } \ No newline at end of file From 821361f9bfc9a4f04fef0cff21121ea29f2dcff0 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 15 Apr 2020 23:11:43 +0100 Subject: [PATCH 058/415] Update JEI --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a4e7eb14..c98a6360 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,6 +3,6 @@ org.gradle.jvmargs=-Xmx3G mc_version=1.12.2 mc_version_short=1.12 -jei_version=4.13.1.225 +jei_version=4.15.0.291 baubles_version=1.5.2 antique_atlas_version=4.6.3 \ No newline at end of file From 50ca70b0db978cd325f7553a9fd92ebe5178c9e9 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 15 Apr 2020 23:23:31 +0100 Subject: [PATCH 059/415] Specify JEI and Baubles as dependencies --- src/main/java/electroblob/wizardry/Wizardry.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/Wizardry.java b/src/main/java/electroblob/wizardry/Wizardry.java index 68e9b058..7fe6eae2 100644 --- a/src/main/java/electroblob/wizardry/Wizardry.java +++ b/src/main/java/electroblob/wizardry/Wizardry.java @@ -46,7 +46,8 @@ import java.util.Calendar; */ @Mod(modid = Wizardry.MODID, name = Wizardry.NAME, version = Wizardry.VERSION, acceptedMinecraftVersions = "[1.12.2]", - guiFactory = "electroblob.wizardry.WizardryGuiFactory", dependencies = "required-after:forge@[14.23.5.2814,);after:antiqueatlas@[4.6,)") + guiFactory = "electroblob.wizardry.WizardryGuiFactory", + dependencies = "required-after:forge@[14.23.5.2814,);after:jei@[4.15.0,);after:baubles@[1.5.2,);after:antiqueatlas@[4.6,)") public class Wizardry { From 57f4704e1807aaab620e85ab7169c067001c3e1f Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 15 Apr 2020 23:24:06 +0100 Subject: [PATCH 060/415] Update Forge --- build.gradle | 2 +- src/main/java/electroblob/wizardry/Wizardry.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 8623ebcb..cee2fefd 100644 --- a/build.gradle +++ b/build.gradle @@ -38,7 +38,7 @@ repositories { } minecraft { - version = "1.12.2-14.23.5.2814" + version = "1.12.2-14.23.5.2847" runDir = "run" // the mappings can be changed at any time, and must be in the following format. diff --git a/src/main/java/electroblob/wizardry/Wizardry.java b/src/main/java/electroblob/wizardry/Wizardry.java index 7fe6eae2..07ce43cf 100644 --- a/src/main/java/electroblob/wizardry/Wizardry.java +++ b/src/main/java/electroblob/wizardry/Wizardry.java @@ -47,7 +47,7 @@ import java.util.Calendar; @Mod(modid = Wizardry.MODID, name = Wizardry.NAME, version = Wizardry.VERSION, acceptedMinecraftVersions = "[1.12.2]", guiFactory = "electroblob.wizardry.WizardryGuiFactory", - dependencies = "required-after:forge@[14.23.5.2814,);after:jei@[4.15.0,);after:baubles@[1.5.2,);after:antiqueatlas@[4.6,)") + dependencies = "required-after:forge@[14.23.5.2847,);after:jei@[4.15.0,);after:baubles@[1.5.2,);after:antiqueatlas@[4.6,)") public class Wizardry { From 6882fb4519491c8e28ba9d154e1cc35237e88d13 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 15 Apr 2020 23:34:41 +0100 Subject: [PATCH 061/415] Prevent imbuements from showing up on enchanted books in JEI --- .../wizardry/integration/jei/WizardryJEIPlugin.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java b/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java index a5b050f2..ad70fb64 100644 --- a/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java +++ b/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java @@ -1,6 +1,7 @@ package electroblob.wizardry.integration.jei; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.enchantment.Imbuement; import electroblob.wizardry.item.IConjuredItem; import electroblob.wizardry.registry.WizardryBlocks; import mezz.jei.api.IModPlugin; @@ -9,6 +10,8 @@ import mezz.jei.api.ISubtypeRegistry; import mezz.jei.api.JEIPlugin; import mezz.jei.api.ingredients.IIngredientBlacklist; import mezz.jei.api.recipe.IRecipeCategoryRegistration; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentData; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -41,6 +44,15 @@ public class WizardryJEIPlugin implements IModPlugin { if(item instanceof IConjuredItem) blacklist.addIngredientToBlacklist(new ItemStack(item)); } + // Hide imbuements from JEI + for(Enchantment enchantment : Enchantment.REGISTRY){ + if(enchantment instanceof Imbuement){ + for(int level = enchantment.getMinLevel(); level <= enchantment.getMaxLevel(); level++){ + blacklist.addIngredientToBlacklist(new EnchantmentData(enchantment, level)); + } + } + } + } @Override From 7b1283c418671de469021f886ad7dab4f380257b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 16 Apr 2020 15:46:50 +0100 Subject: [PATCH 062/415] Yes it does, I fixed that! --- src/main/java/electroblob/wizardry/item/ItemArtefact.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/item/ItemArtefact.java b/src/main/java/electroblob/wizardry/item/ItemArtefact.java index 68b51e6e..21377347 100644 --- a/src/main/java/electroblob/wizardry/item/ItemArtefact.java +++ b/src/main/java/electroblob/wizardry/item/ItemArtefact.java @@ -436,7 +436,6 @@ public class ItemArtefact extends Item { }else if(artefact == WizardryItems.charm_flight){ if(event.getSpell() == Spells.flight || event.getSpell() == Spells.glide){ - // FIXME: Does not appear to be working, for some reason modifiers.set(SpellModifiers.POTENCY, 1.5f * potency, true); } From d93b9a6765af2d861c41d97e1f5007131f83a76a Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 16 Apr 2020 15:49:10 +0100 Subject: [PATCH 063/415] Make bottomless provisions work with satiety, and have them trigger as soon as the player has enough hunger to fill --- .../java/electroblob/wizardry/item/ItemArtefact.java | 11 ++++++++--- src/main/resources/assets/ebwizardry/lang/en_gb.lang | 2 +- src/main/resources/assets/ebwizardry/lang/en_us.lang | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/electroblob/wizardry/item/ItemArtefact.java b/src/main/java/electroblob/wizardry/item/ItemArtefact.java index 21377347..630a10c1 100644 --- a/src/main/java/electroblob/wizardry/item/ItemArtefact.java +++ b/src/main/java/electroblob/wizardry/item/ItemArtefact.java @@ -349,9 +349,14 @@ public class ItemArtefact extends Item { }); }else if(artefact == WizardryItems.charm_feeding){ - // Every 5 seconds, feed the player if they are near starving - if(player.ticksExisted % 100 == 0 && player.getFoodStats().getFoodLevel() < 2){ - findMatchingWandAndCast(player, Spells.replenish_hunger); + // Every 5 seconds, feed the player if they are hungry enough + if(player.ticksExisted % 100 == 0){ + if(player.getFoodStats().getFoodLevel() < 20 - Spells.satiety.getProperty(Satiety.HUNGER_POINTS).intValue()){ + if(findMatchingWandAndCast(player, Spells.satiety)) continue; + } + if(player.getFoodStats().getFoodLevel() < 20 - Spells.replenish_hunger.getProperty(ReplenishHunger.HUNGER_POINTS).intValue()){ + findMatchingWandAndCast(player, Spells.replenish_hunger); + } } } } diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 709769a5..c2c9a5ac 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -359,7 +359,7 @@ item.ebwizardry\:charm_light.desc=Conjured light sources last forever and may be item.ebwizardry\:charm_transportation.name=Ancient Compass item.ebwizardry\:charm_transportation.desc=Up to four stone circles may be remembered and selected from when using transportation item.ebwizardry\:charm_feeding.name=Bottomless Provisions -item.ebwizardry\:charm_feeding.desc=Replenish hunger triggers automatically when bound to a wand on your hotbar +item.ebwizardry\:charm_feeding.desc=Replenish hunger and satiety trigger automatically when bound to a wand on your hotbar item.charge_status.full=Full item.charge_status.almost_full=Almost full diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index a136fbb7..06a2ef56 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -359,7 +359,7 @@ item.ebwizardry\:charm_light.desc=Conjured light sources last forever and may be item.ebwizardry\:charm_transportation.name=Ancient Compass item.ebwizardry\:charm_transportation.desc=Up to four stone circles may be remembered and selected from when using transportation item.ebwizardry\:charm_feeding.name=Bottomless Provisions -item.ebwizardry\:charm_feeding.desc=Replenish hunger triggers automatically when bound to a wand on your hotbar +item.ebwizardry\:charm_feeding.desc=Replenish hunger and satiety trigger automatically when bound to a wand on your hotbar item.charge_status.full=Full item.charge_status.almost_full=Almost full From 4c024f9e467d471b4d2e6c691caec5c5dbed1ba5 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 17 Apr 2020 21:38:21 +0100 Subject: [PATCH 064/415] Hmmm I hope this incorrect description hasn't resulted in any bugs :/ --- src/main/java/electroblob/wizardry/spell/Spell.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java index 62d31b67..ffac262b 100644 --- a/src/main/java/electroblob/wizardry/spell/Spell.java +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -996,7 +996,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C return registry.getValuesCollection().stream().filter(filter.and(s -> s != Spells.none)).collect(Collectors.toList()); } - /** Returns all registered spells, except the {@link None} spell. */ + /** Returns all registered spells, including the {@link None} spell. */ public static List getAllSpells(){ return getSpells(s -> true); } From cdc326e36c6093db2000b9181741a8ba48949354 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 17 Apr 2020 21:42:39 +0100 Subject: [PATCH 065/415] Remove the none spell from scroll recipes in JEI --- .../integration/jei/ArcaneWorkbenchRecipeCategory.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java b/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java index ad5896b8..012c8188 100644 --- a/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java +++ b/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java @@ -6,6 +6,7 @@ import electroblob.wizardry.constants.Constants; import electroblob.wizardry.constants.Element; import electroblob.wizardry.inventory.ContainerArcaneWorkbench; import electroblob.wizardry.item.*; +import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryRecipes; import electroblob.wizardry.spell.Spell; @@ -251,6 +252,9 @@ public class ArcaneWorkbenchRecipeCategory implements IRecipeCategory scrolls = Streams.stream(Item.REGISTRY).filter(i -> i instanceof ItemScroll).collect(Collectors.toList()); for(Spell spell : Spell.getAllSpells()){ + + if(spell == Spells.none) continue; + for(Item spellBook : spellBooks){ for(Item scroll : scrolls){ if(spell.applicableForItem(spellBook) && spell.applicableForItem(scroll)){ From 618df1c668458b1cabc2f7ac8265db21deffafeb Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 17 Apr 2020 21:49:01 +0100 Subject: [PATCH 066/415] Add descriptions to some wizardry items in JEI Covers most of the items players might stumble across --- .../integration/jei/WizardryJEIPlugin.java | 77 +++++++++++++++++++ .../wizardry/item/ItemArmourUpgrade.java | 4 +- .../wizardry/item/ItemWizardHandbook.java | 3 +- .../assets/ebwizardry/lang/en_gb.lang | 36 +++++++-- .../assets/ebwizardry/lang/en_us.lang | 36 +++++++-- 5 files changed, 138 insertions(+), 18 deletions(-) diff --git a/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java b/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java index ad70fb64..3a80a71a 100644 --- a/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java +++ b/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java @@ -3,17 +3,27 @@ package electroblob.wizardry.integration.jei; import electroblob.wizardry.Wizardry; import electroblob.wizardry.enchantment.Imbuement; import electroblob.wizardry.item.IConjuredItem; +import electroblob.wizardry.item.ItemArcaneTome; +import electroblob.wizardry.item.ItemArtefact; +import electroblob.wizardry.item.ItemWandUpgrade; import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardryEnchantments; +import electroblob.wizardry.registry.WizardryItems; import mezz.jei.api.IModPlugin; import mezz.jei.api.IModRegistry; import mezz.jei.api.ISubtypeRegistry; import mezz.jei.api.JEIPlugin; import mezz.jei.api.ingredients.IIngredientBlacklist; +import mezz.jei.api.ingredients.VanillaTypes; import mezz.jei.api.recipe.IRecipeCategoryRegistration; import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentData; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.util.NonNullList; + +import java.util.Arrays; +import java.util.Locale; @JEIPlugin public class WizardryJEIPlugin implements IModPlugin { @@ -53,6 +63,41 @@ public class WizardryJEIPlugin implements IModPlugin { } } + addItemInfo(registry, WizardryItems.magic_crystal); + addItemInfo(registry, WizardryItems.wizard_handbook); + addItemInfo(registry, WizardryItems.crystal_shard); + addItemInfo(registry, WizardryItems.grand_crystal); + addItemInfo(registry, WizardryItems.identification_scroll); + addItemInfo(registry, WizardryItems.spell_book); + addItemInfo(registry, WizardryItems.scroll); + addItemInfo(registry, WizardryItems.armour_upgrade, ".desc_extended"); + addItemInfo(registry, WizardryItems.purifying_elixir, ".desc_extended"); + addItemInfo(registry, WizardryItems.astral_diamond); + + for(Item item : Item.REGISTRY){ + + if(item instanceof ItemArtefact){ + + ItemStack stack = new ItemStack(item); + registry.addIngredientInfo(stack, VanillaTypes.ITEM, "item." + Wizardry.MODID + ":" + + ((ItemArtefact)item).getType().toString().toLowerCase(Locale.ROOT) + ".generic.desc"); + + }else if(item instanceof ItemWandUpgrade || item instanceof ItemArcaneTome){ + + NonNullList subItems = NonNullList.create(); + item.getSubItems(item.getCreativeTab(), subItems); + + for(ItemStack stack : subItems){ + registry.addIngredientInfo(stack, VanillaTypes.ITEM, "item." + Wizardry.MODID + + ":wand_upgrade.generic.desc"); + } + } + } + + addEnchantmentInfo(registry, WizardryEnchantments.magic_protection); + addEnchantmentInfo(registry, WizardryEnchantments.frost_protection); + addEnchantmentInfo(registry, WizardryEnchantments.shock_protection); + } @Override @@ -65,4 +110,36 @@ public class WizardryJEIPlugin implements IModPlugin { } + private static void addItemInfo(IModRegistry registry, Item item){ + addItemInfo(registry, item, ".desc"); + } + + private static void addItemInfo(IModRegistry registry, Item item, String... suffixes){ + NonNullList subItems = NonNullList.create(); + item.getSubItems(item.getCreativeTab(), subItems); + for(ItemStack stack : subItems) addItemInfo(registry, stack, suffixes); + } + + private static void addItemInfo(IModRegistry registry, ItemStack stack, String... suffixes){ + String prefix = stack.getItem().getTranslationKey(stack); + String[] keys = Arrays.stream(suffixes).map(s -> prefix + s).toArray(String[]::new); + registry.addIngredientInfo(stack, VanillaTypes.ITEM, keys); + } + + private static void addEnchantmentInfo(IModRegistry registry, Enchantment enchantment){ + addEnchantmentInfo(registry, enchantment, ".desc"); + } + + private static void addEnchantmentInfo(IModRegistry registry, Enchantment enchantment, String... suffixes){ + for(int level = enchantment.getMinLevel(); level <= enchantment.getMaxLevel(); level++){ + addEnchantmentInfo(registry, new EnchantmentData(enchantment, level), suffixes); + } + } + + private static void addEnchantmentInfo(IModRegistry registry, EnchantmentData data, String... suffixes){ + String prefix = data.enchantment.getName().replace(":", "."); // Compat with WAWLA descriptions + String[] keys = Arrays.stream(suffixes).map(s -> prefix + s).toArray(String[]::new); + registry.addIngredientInfo(data, VanillaTypes.ENCHANT, keys); + } + } diff --git a/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java b/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java index b7307b15..921947bd 100644 --- a/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java +++ b/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java @@ -34,9 +34,7 @@ public class ItemArmourUpgrade extends Item { @Override @SideOnly(Side.CLIENT) public void addInformation(ItemStack stack, @Nullable World worldIn, List tooltip, net.minecraft.client.util.ITooltipFlag flagIn) { - tooltip.add(net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":armour_upgrade.desc1", "\u00A77")); - tooltip.add( - net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":armour_upgrade.desc2", "\u00A77", "\u00A7d")); + Wizardry.proxy.addMultiLineDescription(tooltip, "item." + this.getRegistryName() + ".desc", "\u00A7d"); } } diff --git a/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java b/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java index e2c6a39b..298b0f2d 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java +++ b/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java @@ -27,8 +27,7 @@ public class ItemWizardHandbook extends Item { @Override public void addInformation(ItemStack stack, @Nullable World world, List tooltip, net.minecraft.client.util.ITooltipFlag flag) { - tooltip.add( - "\u00A77" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wizard_handbook.desc", AUTHOR)); + tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wizard_handbook.author", AUTHOR)); } @Override diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index c2c9a5ac..e5199a7c 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -48,19 +48,24 @@ tile.ebwizardry\:acacia_lectern.name=Acacia Lectern tile.ebwizardry\:dark_oak_lectern.name=Dark Oak Lectern item.ebwizardry\:crystal_magic.name=Magic Crystal +item.ebwizardry\:crystal_magic.desc=The iridescent colours of this crystal seem to slowly shift and swirl, hinting at a magical energy within. Perhaps this magic could be harnessed in some way? item.ebwizardry\:crystal_fire.name=Fiery Crystal +item.ebwizardry\:crystal_fire.desc=A crystal that glows a fiery orange. Maybe it is the key to unlocking the element of fire... item.ebwizardry\:crystal_ice.name=Icy Crystal +item.ebwizardry\:crystal_ice.desc=A crystal with a cold, bluish tint. Perhaps it holds the secrets to the power of frost... item.ebwizardry\:crystal_lightning.name=Stormy Crystal +item.ebwizardry\:crystal_lightning.desc=A crystal that seems to swirl and flash with a chaotic energy. Maybe it hides a greater power within... item.ebwizardry\:crystal_necromancy.name=Dark Crystal +item.ebwizardry\:crystal_necromancy.desc=A crystal with a deep, purplish appearance that seems to draw you in. Perhaps it is a gateway to something darker... item.ebwizardry\:crystal_earth.name=Verdant Crystal +item.ebwizardry\:crystal_earth.desc=A crystal patterened with a great many greens and browns. Perhaps it holds a connection to the forces of nature... item.ebwizardry\:crystal_sorcery.name=Mystical Crystal +item.ebwizardry\:crystal_sorcery.desc=A crystal that glimmers with a bluish-green light. Maybe it conceals the secrets of an ancient force... item.ebwizardry\:crystal_healing.name=Radiant Crystal +item.ebwizardry\:crystal_healing.desc=A crystal that glints a golden yellow in the sunlight. Perhaps it possesses the link to a divine power... -item.ebwizardry\:magic_wand.name=Magic Wand -item.ebwizardry\:apprentice_wand.name=Apprentice Wand -item.ebwizardry\:advanced_wand.name=Advanced Wand -item.ebwizardry\:master_wand.name=Master Wand item.ebwizardry\:spell_book.name=Spell Book +item.ebwizardry\:spell_book.desc=A book filled with the runes and glyphs of an ancient script. Perhaps with dedication, one might be able to decipher its contents...\n\nRight-click while holding this book to read it. item.ebwizardry\:spell_book.apply_to_wizard=Replaced %1$s's spell %2$s with %3$s @@ -68,6 +73,7 @@ item.ebwizardry\:arcane_tome.name=Tome of Arcana item.ebwizardry\:arcane_tome.desc=Upgrades any %1$s wand with sufficient progression to %2$s tier item.ebwizardry\:wizard_handbook.name=The Wizard's Handbook +item.ebwizardry\:wizard_handbook.author=by %1$s item.ebwizardry\:wizard_handbook.desc=by %1$s item.ebwizardry\:wand.generic=wand @@ -82,6 +88,11 @@ item.ebwizardry\:wand.levelup=%1$s is ready to upgrade to %2$s tier item.ebwizardry\:wand.addally=%1$s has been added to your list of allies item.ebwizardry\:wand.removeally=%1$s has been removed from your list of allies +item.ebwizardry\:magic_wand.name=Magic Wand +item.ebwizardry\:apprentice_wand.name=Apprentice Wand +item.ebwizardry\:advanced_wand.name=Advanced Wand +item.ebwizardry\:master_wand.name=Master Wand + item.ebwizardry\:novice_fire_wand.name=Wand of Embers item.ebwizardry\:novice_ice_wand.name=Wand of Frost item.ebwizardry\:novice_lightning_wand.name=Wand of Sparks @@ -126,12 +137,16 @@ item.ebwizardry\:medium_mana_flask.name=Medium Mana Flask item.ebwizardry\:large_mana_flask.name=Large Mana Flask item.ebwizardry\:grand_crystal.name=Grand Magic Crystal +item.ebwizardry\:grand_crystal.desc=A huge magic crystal, several times the size of those found naturally. Someone must have grown it artificially - but how? item.ebwizardry\:crystal_shard.name=Magic Crystal Shard +item.ebwizardry\:crystal_shard.desc=A small piece of a shattered magic crystal. It contains little mana on its own, but it might have other uses... item.ebwizardry\:astral_diamond.name=Astral Diamond +item.ebwizardry\:astral_diamond.desc=A highly-prized gemstone that sparkles brightly under starlight. It is said that the most powerful wizards seek astral diamonds in return for the most elusive arcane knowledge. item.ebwizardry\:purifying_elixir.name=Purifying Elixir item.ebwizardry\:purifying_elixir.desc=Removes curses when consumed +item.ebwizardry\:purifying_elixir.desc_extended=A small vial of exotic-looking golden liquid, which clearly has powerful healing properties. It would probably be wise to save this for when it is needed. item.ebwizardry\:storage_upgrade.name=Wand Storage Upgrade item.ebwizardry\:siphon_upgrade.name=Wand Siphon Upgrade @@ -143,6 +158,8 @@ item.ebwizardry\:blast_upgrade.name=Wand Blast Upgrade item.ebwizardry\:attunement_upgrade.name=Wand Attunement Upgrade item.ebwizardry\:melee_upgrade.name=Wand Melee Upgrade +item.ebwizardry\:wand_upgrade.generic.desc=Apply this upgrade to a wand in the arcane workbench. + item.ebwizardry\:storage_upgrade.desc=Upgrades the mana capacity of a wand item.ebwizardry\:siphon_upgrade.desc=Upgrades a wand to extract mana from mobs when killed item.ebwizardry\:condenser_upgrade.desc=Upgrades a wand to slowly regenerate mana over time @@ -168,13 +185,14 @@ item.ebwizardry\:blank_scroll.name=Blank Scroll item.ebwizardry\:scroll.generic=scroll item.ebwizardry\:scroll.name=Scroll of %1$s item.ebwizardry\:scroll.undiscovered.name=Scroll "%1$s" +item.ebwizardry\:scroll.desc=An enchanted sheet of parchment bound loosely with string. Upon the parchment is written an incantation of some kind, presumably to be read aloud.\n\nRight-click while holding this scroll to use it. item.ebwizardry\:identification_scroll.name=Scroll of Identification item.ebwizardry\:identification_scroll.desc=Identifies an unknown spell book or scroll item.ebwizardry\:identification_scroll.nothing_to_identify=Nothing to identify! item.ebwizardry\:armour_upgrade.name=Arcane Seal of Protection -item.ebwizardry\:armour_upgrade.desc1=%1$sUpgrades any wizard armour -item.ebwizardry\:armour_upgrade.desc2=%1$sto make it %2$slegendary +item.ebwizardry\:armour_upgrade.desc=Upgrades any wizard armour to make it %1$slegendary +item.ebwizardry\:armour_upgrade.desc_extended=Apply this upgrade to a piece of wizard armour in the arcane workbench. item.ebwizardry\:magic_silk.name=Magical Silk @@ -232,6 +250,10 @@ item.ebwizardry\:spectral_boots.name=Spectral Boots item.ebwizardry\:lightning_hammer.name=Lightning Hammer +item.ebwizardry\:ring.generic.desc=Equip this ring in the Baubles inventory or by placing it on your hotbar. Up to 2 rings may be equipped at once. +item.ebwizardry\:amulet.generic.desc=Equip this amulet in the Baubles inventory or by placing it on your hotbar. Only one amulet may be equipped at a time. +item.ebwizardry\:charm.generic.desc=Equip this charm in the Baubles inventory or by placing it on your hotbar. Only one charm may be equipped at a time. + item.ebwizardry\:ring_condensing.name=Ring of Condensing item.ebwizardry\:ring_condensing.desc=Slowly regenerates mana for all wands on your hotbar item.ebwizardry\:ring_siphoning.name=Ring of Siphoning @@ -1350,3 +1372,5 @@ spell.ebwizardry\:invigorating_presence_festive=Invigorating Presents spell.ebwizardry\:empowering_presence_festive=Empowering Presents wizard.debug=%1$s, %2$s, %3$s +item.ebwizardry\:armour_upgrade.desc1=%1$sUpgrades any wizard armour +item.ebwizardry\:armour_upgrade.desc2=%1$sto make it %2$slegendary diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 06a2ef56..13fb7659 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -48,19 +48,24 @@ tile.ebwizardry\:acacia_lectern.name=Acacia Lectern tile.ebwizardry\:dark_oak_lectern.name=Dark Oak Lectern item.ebwizardry\:crystal_magic.name=Magic Crystal +item.ebwizardry\:crystal_magic.desc=The iridescent colors of this crystal seem to slowly shift and swirl, hinting at a magical energy within. Perhaps this magic could be harnessed, somehow... item.ebwizardry\:crystal_fire.name=Fiery Crystal +item.ebwizardry\:crystal_fire.desc=A crystal that glows a fiery orange. Maybe it is the key to unlocking the element of fire... item.ebwizardry\:crystal_ice.name=Icy Crystal +item.ebwizardry\:crystal_ice.desc=A crystal with a cold, bluish tint. Perhaps it holds the secrets to the power of frost... item.ebwizardry\:crystal_lightning.name=Stormy Crystal +item.ebwizardry\:crystal_lightning.desc=A crystal that seems to swirl and flash with a chaotic energy. Maybe it hides a greater power within... item.ebwizardry\:crystal_necromancy.name=Dark Crystal +item.ebwizardry\:crystal_necromancy.desc=A crystal with a deep, purplish appearance that seems to draw you in. Perhaps it is a gateway to something darker... item.ebwizardry\:crystal_earth.name=Verdant Crystal +item.ebwizardry\:crystal_earth.desc=A crystal patterened with a great many greens and browns. Perhaps it holds a connection to the forces of nature... item.ebwizardry\:crystal_sorcery.name=Mystical Crystal +item.ebwizardry\:crystal_sorcery.desc=A crystal that glimmers with a bluish-green light. Maybe it conceals the secrets of an ancient force... item.ebwizardry\:crystal_healing.name=Radiant Crystal +item.ebwizardry\:crystal_healing.desc=A crystal that glints a golden yellow in the sunlight. Perhaps it possesses the link to a divine power... -item.ebwizardry\:magic_wand.name=Magic Wand -item.ebwizardry\:apprentice_wand.name=Apprentice Wand -item.ebwizardry\:advanced_wand.name=Advanced Wand -item.ebwizardry\:master_wand.name=Master Wand item.ebwizardry\:spell_book.name=Spell Book +item.ebwizardry\:spell_book.desc=A book filled with the runes and glyphs of an ancient script. Perhaps with dedication, one might be able to decipher its contents...\n\nRight-click while holding this book to read it. item.ebwizardry\:spell_book.apply_to_wizard=Replaced %1$s's spell %2$s with %3$s @@ -68,7 +73,8 @@ item.ebwizardry\:arcane_tome.name=Tome of Arcana item.ebwizardry\:arcane_tome.desc=Upgrades any %1$s wand with sufficient progression to %2$s tier item.ebwizardry\:wizard_handbook.name=The Wizard's Handbook -item.ebwizardry\:wizard_handbook.desc=by %1$s +item.ebwizardry\:wizard_handbook.author=by %1$s +item.ebwizardry\:wizard_handbook.desc=Your not-so-pocket-sized guide to the arcane arts! A must-have for any aspiring wizard who seeks to learn the secrets of magic.\n\nRight-click while holding this book to read it. item.ebwizardry\:wand.generic=wand @@ -82,6 +88,11 @@ item.ebwizardry\:wand.levelup=%1$s is ready to upgrade to %2$s tier item.ebwizardry\:wand.addally=%1$s has been added to your list of allies item.ebwizardry\:wand.removeally=%1$s has been removed from your list of allies +item.ebwizardry\:magic_wand.name=Magic Wand +item.ebwizardry\:apprentice_wand.name=Apprentice Wand +item.ebwizardry\:advanced_wand.name=Advanced Wand +item.ebwizardry\:master_wand.name=Master Wand + item.ebwizardry\:novice_fire_wand.name=Wand of Embers item.ebwizardry\:novice_ice_wand.name=Wand of Frost item.ebwizardry\:novice_lightning_wand.name=Wand of Sparks @@ -126,12 +137,16 @@ item.ebwizardry\:medium_mana_flask.name=Medium Mana Flask item.ebwizardry\:large_mana_flask.name=Large Mana Flask item.ebwizardry\:grand_crystal.name=Grand Magic Crystal +item.ebwizardry\:grand_crystal.desc=A huge magic crystal, several times the size of those found naturally. Someone must have grown it artificially - but how? item.ebwizardry\:crystal_shard.name=Magic Crystal Shard +item.ebwizardry\:crystal_shard.desc=A small piece of a shattered magic crystal. It contains little mana on its own, but it might have other uses... item.ebwizardry\:astral_diamond.name=Astral Diamond +item.ebwizardry\:astral_diamond.desc=A highly-prized gemstone that sparkles brightly under starlight. It is said that the most powerful wizards seek astral diamonds in return for the most elusive arcane knowledge. item.ebwizardry\:purifying_elixir.name=Purifying Elixir item.ebwizardry\:purifying_elixir.desc=Removes curses when consumed +item.ebwizardry\:purifying_elixir.desc_extended=A small vial of exotic-looking golden liquid, which clearly has powerful healing properties. It would probably be wise to save this for when it is needed. item.ebwizardry\:storage_upgrade.name=Wand Storage Upgrade item.ebwizardry\:siphon_upgrade.name=Wand Siphon Upgrade @@ -143,6 +158,8 @@ item.ebwizardry\:blast_upgrade.name=Wand Blast Upgrade item.ebwizardry\:attunement_upgrade.name=Wand Attunement Upgrade item.ebwizardry\:melee_upgrade.name=Wand Melee Upgrade +item.ebwizardry\:wand_upgrade.generic.desc=Apply this upgrade to a wand in the arcane workbench. + item.ebwizardry\:storage_upgrade.desc=Upgrades the mana capacity of a wand item.ebwizardry\:siphon_upgrade.desc=Upgrades a wand to extract mana from mobs when killed item.ebwizardry\:condenser_upgrade.desc=Upgrades a wand to slowly regenerate mana over time @@ -168,13 +185,14 @@ item.ebwizardry\:blank_scroll.name=Blank Scroll item.ebwizardry\:scroll.generic=scroll item.ebwizardry\:scroll.name=Scroll of %1$s item.ebwizardry\:scroll.undiscovered.name=Scroll "%1$s" +item.ebwizardry\:scroll.desc=An enchanted sheet of parchment bound loosely with string. Upon the parchment is written an incantation of some kind, presumably to be read aloud.\n\nRight-click while holding this scroll to use it. item.ebwizardry\:identification_scroll.name=Scroll of Identification item.ebwizardry\:identification_scroll.desc=Identifies an unknown spell book or scroll item.ebwizardry\:identification_scroll.nothing_to_identify=Nothing to identify! item.ebwizardry\:armour_upgrade.name=Arcane Seal of Protection -item.ebwizardry\:armour_upgrade.desc1=%1$sUpgrades any wizard armour -item.ebwizardry\:armour_upgrade.desc2=%1$sto make it %2$slegendary +item.ebwizardry\:armour_upgrade.desc=Upgrades any wizard armour to make it %1$slegendary +item.ebwizardry\:armour_upgrade.desc_extended=Apply this upgrade to a piece of wizard armour in the arcane workbench. item.ebwizardry\:magic_silk.name=Magical Silk @@ -232,6 +250,10 @@ item.ebwizardry\:spectral_boots.name=Spectral Boots item.ebwizardry\:lightning_hammer.name=Lightning Hammer +item.ebwizardry\:ring.generic.desc=Equip this ring in the Baubles inventory or by placing it on your hotbar. Up to 2 rings may be equipped at once. +item.ebwizardry\:amulet.generic.desc=Equip this amulet in the Baubles inventory or by placing it on your hotbar. Only one amulet may be equipped at a time. +item.ebwizardry\:charm.generic.desc=Equip this charm in the Baubles inventory or by placing it on your hotbar. Only one charm may be equipped at a time. + item.ebwizardry\:ring_condensing.name=Ring of Condensing item.ebwizardry\:ring_condensing.desc=Slowly regenerates mana for all wands on your hotbar item.ebwizardry\:ring_siphoning.name=Ring of Siphoning From 8291373427954cd5d769a90883d6c80ef8cbef8c Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 17 Apr 2020 21:53:05 +0100 Subject: [PATCH 067/415] Say something a bit more useful about scrolls of identification --- .../electroblob/wizardry/integration/jei/WizardryJEIPlugin.java | 2 +- src/main/resources/assets/ebwizardry/lang/en_gb.lang | 1 + src/main/resources/assets/ebwizardry/lang/en_us.lang | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java b/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java index 3a80a71a..8df4b5a0 100644 --- a/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java +++ b/src/main/java/electroblob/wizardry/integration/jei/WizardryJEIPlugin.java @@ -67,7 +67,7 @@ public class WizardryJEIPlugin implements IModPlugin { addItemInfo(registry, WizardryItems.wizard_handbook); addItemInfo(registry, WizardryItems.crystal_shard); addItemInfo(registry, WizardryItems.grand_crystal); - addItemInfo(registry, WizardryItems.identification_scroll); + addItemInfo(registry, WizardryItems.identification_scroll, ".desc_extended"); addItemInfo(registry, WizardryItems.spell_book); addItemInfo(registry, WizardryItems.scroll); addItemInfo(registry, WizardryItems.armour_upgrade, ".desc_extended"); diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index e5199a7c..a9f7ec46 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -188,6 +188,7 @@ item.ebwizardry\:scroll.undiscovered.name=Scroll "%1$s" item.ebwizardry\:scroll.desc=An enchanted sheet of parchment bound loosely with string. Upon the parchment is written an incantation of some kind, presumably to be read aloud.\n\nRight-click while holding this scroll to use it. item.ebwizardry\:identification_scroll.name=Scroll of Identification item.ebwizardry\:identification_scroll.desc=Identifies an unknown spell book or scroll +item.ebwizardry\:identification_scroll.desc_extended=Right-click while holding this scroll to identify the first undiscovered spell book or scroll on your hotbar. item.ebwizardry\:identification_scroll.nothing_to_identify=Nothing to identify! item.ebwizardry\:armour_upgrade.name=Arcane Seal of Protection diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 13fb7659..d058ba19 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -188,6 +188,7 @@ item.ebwizardry\:scroll.undiscovered.name=Scroll "%1$s" item.ebwizardry\:scroll.desc=An enchanted sheet of parchment bound loosely with string. Upon the parchment is written an incantation of some kind, presumably to be read aloud.\n\nRight-click while holding this scroll to use it. item.ebwizardry\:identification_scroll.name=Scroll of Identification item.ebwizardry\:identification_scroll.desc=Identifies an unknown spell book or scroll +item.ebwizardry\:identification_scroll.desc_extended=Right-click while holding this scroll to identify the first undiscovered spell book or scroll on your hotbar. item.ebwizardry\:identification_scroll.nothing_to_identify=Nothing to identify! item.ebwizardry\:armour_upgrade.name=Arcane Seal of Protection From b23136d56851ac8b5f6e54e9dba7567fbe7072ca Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 17 Apr 2020 23:02:45 +0100 Subject: [PATCH 068/415] Add method for checking if a spell property exists --- .../java/electroblob/wizardry/spell/Spell.java | 15 +++++++++++++-- .../wizardry/util/SpellProperties.java | 14 +++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java index ffac262b..3547010b 100644 --- a/src/main/java/electroblob/wizardry/spell/Spell.java +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -606,16 +606,27 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C return properties.cooldown; } + /** + * Returns whether a property has been defined for the given identifier. + * @param identifier The JSON identifier to check for. + * @return True if a property has been defined for the given identifier, false if not. + */ + public final boolean hasProperty(String identifier){ + return properties.hasBaseValue(identifier); + } + /** * Returns the base value specified in JSON for the given identifier. This may be used from within the spell * class, or from elsewhere (entities, items, etc.) via the spell's instance. * * @param identifier The JSON identifier for the required property. This must have been defined using - * {@link Spell#addProperties(String...)} or an exception will be thrown. + * {@link Spell#addProperties(String...)} or an exception will be thrown. To check if an + * identifier exists, use {@link Spell#hasProperty(String)}. * @return The base value of the property, as a {@code Number} object. Internally this is handled as a float, but * it is passed through as a {@code Number} to avoid casting. Be careful with rounding when extracting integer * values! The JSON parser cannot guarantee that the property file has an integer value. - * @throws IllegalArgumentException if no property was defined with the given identifier. */ + * @throws IllegalArgumentException if no property was defined with the given identifier. + */ public final Number getProperty(String identifier){ return properties.getBaseValue(identifier); } diff --git a/src/main/java/electroblob/wizardry/util/SpellProperties.java b/src/main/java/electroblob/wizardry/util/SpellProperties.java index de12eb52..535c60a4 100644 --- a/src/main/java/electroblob/wizardry/util/SpellProperties.java +++ b/src/main/java/electroblob/wizardry/util/SpellProperties.java @@ -224,11 +224,23 @@ public final class SpellProperties { } /** - * Returns the base value for this spell that corresponds to the given identifier. + * Returns whether a base value was defined with the given identifier. + * @param identifier The string identifier to check for. + * @return True if a base value was defined with the given identifier, false otherwise. + */ + public boolean hasBaseValue(String identifier){ + return baseValues.containsKey(identifier); + } + + /** + * Returns the base value for this spell that corresponds to the given identifier. To check whether an identifier + * exists, use {@link SpellProperties#hasBaseValue(String)}. * @param identifier The string identifier to fetch the base value for. * @return The base value, as a {@code Number}. * @throws IllegalArgumentException if no base value was defined with the given identifier. */ + // Better to throw an exception than make this nullable because the vast majority of uses are for retrieving + // specific spells' properties that are known to exist, and IntelliJ would scream at us for not checking public Number getBaseValue(String identifier){ if(!baseValues.containsKey(identifier)){ throw new IllegalArgumentException("Base value with identifier '" + identifier + "' is not defined."); From abc598a7221e834efd686a84adca5637e7c6d7e1 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 17 Apr 2020 23:03:16 +0100 Subject: [PATCH 069/415] Remove deprecated fields --- .../java/electroblob/wizardry/spell/Spell.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java index 3547010b..da99082a 100644 --- a/src/main/java/electroblob/wizardry/spell/Spell.java +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -1012,21 +1012,6 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C return getSpells(s -> true); } - /** Predicate which allows all spells. - * @deprecated Use {@link Spell#getAllSpells()}. */ - @Deprecated - public static Predicate allSpells = s -> true; - - /** Predicate which allows all non-continuous spells, even those that have been disabled. - * @deprecated Nobody ever uses this now we have continuous scrolls, if you really need it just use a lambda. */ - @Deprecated - public static Predicate nonContinuousSpells = s -> !s.isContinuous; - - /** Predicate which allows all enabled spells for which {@link Spell#canBeCastBy(EntityLiving, boolean)} returns true. - * @deprecated in favour of entity-sensitive version, use a lambda expression directly. */ - @Deprecated - public static Predicate npcSpells = s -> s.isEnabled(SpellProperties.Context.NPCS) && s.canBeCastByNPCs(); - /** * Predicate which allows all enabled spells of the given tier and element (create an instance of this class each * time you want to use it). This is somewhat useless now that Wizardry uses Java 8, but it is more readable than a From 50ad3655c9d1284df2682206e18b3bb71b985229 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 17 Apr 2020 23:08:58 +0100 Subject: [PATCH 070/415] IntelliJ is too clever for its own good sometimes --- .../electroblob/wizardry/integration/DamageSafetyChecker.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/electroblob/wizardry/integration/DamageSafetyChecker.java b/src/main/java/electroblob/wizardry/integration/DamageSafetyChecker.java index 2bbfb421..4444507c 100644 --- a/src/main/java/electroblob/wizardry/integration/DamageSafetyChecker.java +++ b/src/main/java/electroblob/wizardry/integration/DamageSafetyChecker.java @@ -124,9 +124,9 @@ public final class DamageSafetyChecker { boolean vanillaName = VANILLA_DAMAGE_NAMES.contains(originalSourceName); if(aborted){ - Wizardry.logger.warn("SoundLoopSpellEntity attack excessive call limit reached, aborting entity damage entirely!"); + Wizardry.logger.warn("Entity attack excessive call limit reached, aborting entity damage entirely!"); }else{ - Wizardry.logger.warn("SoundLoopSpellEntity attack excessive call threshold reached, substituting for non-entity-based " + + Wizardry.logger.warn("Entity attack excessive call threshold reached, substituting for non-entity-based " + "damage to avert a crash."); } From a7b608a06ea43591009bb0b8a34fddaa7535a149 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 17 Apr 2020 23:16:48 +0100 Subject: [PATCH 071/415] This description was correct in the first place! Turns out it was the upgrading recipes' fault. --- .../integration/jei/ArcaneWorkbenchRecipeCategory.java | 8 ++++---- src/main/java/electroblob/wizardry/spell/Spell.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java b/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java index 012c8188..a23df377 100644 --- a/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java +++ b/src/main/java/electroblob/wizardry/integration/jei/ArcaneWorkbenchRecipeCategory.java @@ -198,8 +198,10 @@ public class ArcaneWorkbenchRecipeCategory implements IRecipeCategory implements C return registry.getValuesCollection().stream().filter(filter.and(s -> s != Spells.none)).collect(Collectors.toList()); } - /** Returns all registered spells, including the {@link None} spell. */ + /** Returns all registered spells, excluding the {@link None} spell. */ public static List getAllSpells(){ return getSpells(s -> true); } From d4c2397a68d5333c0402ac1f50f601f94c679952 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 17 Apr 2020 23:25:48 +0100 Subject: [PATCH 072/415] Fix lectern model rotation in first person --- .../resources/assets/ebwizardry/models/block/lectern.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/assets/ebwizardry/models/block/lectern.json b/src/main/resources/assets/ebwizardry/models/block/lectern.json index 03d694b3..a8084ad5 100644 --- a/src/main/resources/assets/ebwizardry/models/block/lectern.json +++ b/src/main/resources/assets/ebwizardry/models/block/lectern.json @@ -93,11 +93,11 @@ "scale": [0.375, 0.375, 0.375] }, "firstperson_righthand": { - "rotation": [0, 45, 0], + "rotation": [0, 135, 0], "scale": [0.4, 0.4, 0.4] }, "firstperson_lefthand": { - "rotation": [0, 225, 0], + "rotation": [0, 135, 0], "scale": [0.4, 0.4, 0.4] }, "ground": { From 5f8477a4ada386cffa4b9c3e11ff866d65986967 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 17 Apr 2020 23:56:38 +0100 Subject: [PATCH 073/415] Snowballs were a projectile last time I checked... --- src/main/resources/assets/ebwizardry/spells/snowball.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/ebwizardry/spells/snowball.json b/src/main/resources/assets/ebwizardry/spells/snowball.json index 2191aa3a..7b56a7fa 100644 --- a/src/main/resources/assets/ebwizardry/spells/snowball.json +++ b/src/main/resources/assets/ebwizardry/spells/snowball.json @@ -12,7 +12,7 @@ }, "tier": "novice", "element": "ice", - "type": "attack", + "type": "projectile", "cost": 1, "chargeup": 0, "cooldown": 1, From f1cb7dbbbe9174818c55104a3047691d6893489a Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 18 Apr 2020 16:12:05 +0100 Subject: [PATCH 074/415] Make arcane lock work on both halves of a double chest at once --- .../wizardry/spell/ArcaneLock.java | 41 ++++++++++++------- .../wizardry/util/WizardryUtilities.java | 25 ++++++++++- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/main/java/electroblob/wizardry/spell/ArcaneLock.java b/src/main/java/electroblob/wizardry/spell/ArcaneLock.java index b2c88f5a..a93264e2 100644 --- a/src/main/java/electroblob/wizardry/spell/ArcaneLock.java +++ b/src/main/java/electroblob/wizardry/spell/ArcaneLock.java @@ -47,28 +47,39 @@ public class ArcaneLock extends SpellRay { protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){ if(caster instanceof EntityPlayer){ - - TileEntity tileentity = world.getTileEntity(pos); - if(tileentity != null){ - - if(tileentity.getTileData().hasUniqueId(NBT_KEY)){ - // Unlocking - if(world.getPlayerEntityByUUID(tileentity.getTileData().getUniqueId(NBT_KEY)) == caster){ - NBTExtras.removeUniqueId(tileentity.getTileData(), NBT_KEY); - return true; - } - }else{ - // Locking - tileentity.getTileData().setUniqueId(NBT_KEY, caster.getUniqueID()); - return true; - } + if(toggleLock(world, pos, (EntityPlayer)caster)){ + BlockPos otherHalf = WizardryUtilities.getConnectedChest(world, pos); + if(otherHalf != null) toggleLock(world, otherHalf, (EntityPlayer)caster); + return true; } } return false; } + private boolean toggleLock(World world, BlockPos pos, EntityPlayer player){ + + TileEntity tileentity = world.getTileEntity(pos); + + if(tileentity != null){ + + if(tileentity.getTileData().hasUniqueId(NBT_KEY)){ + // Unlocking + if(world.getPlayerEntityByUUID(tileentity.getTileData().getUniqueId(NBT_KEY)) == player){ + NBTExtras.removeUniqueId(tileentity.getTileData(), NBT_KEY); + return true; + } + }else{ + // Locking + tileentity.getTileData().setUniqueId(NBT_KEY, player.getUniqueID()); + return true; + } + } + + return false; + } + @Override protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){ return false; diff --git a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java index b5b97d35..870803a9 100644 --- a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java +++ b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java @@ -9,6 +9,7 @@ import electroblob.wizardry.item.ISpellCastingItem; import electroblob.wizardry.spell.Spell; import net.minecraft.block.Block; import net.minecraft.block.BlockCactus; +import net.minecraft.block.BlockChest; import net.minecraft.block.BlockLog; import net.minecraft.block.properties.IProperty; import net.minecraft.block.state.IBlockState; @@ -39,7 +40,6 @@ import javax.annotation.Nullable; import java.util.*; import java.util.function.BiPredicate; import java.util.function.Predicate; -import java.util.stream.Collectors; /** * "Where do you put random but useful bits and pieces? {@code WizardryUtilities} of course - the 'stuff that doesn't @@ -307,6 +307,29 @@ public final class WizardryUtilities { || Settings.containsMetaBlock(Wizardry.settings.treeBlocks, world.getBlockState(pos)); } + /** + * Given the position of one half of a double chest, returns the position of the other half of that double chest. + * @param world The world in which the chest is located + * @param pos The position of one half of the double chest + * @return The position of the other half of the double chest, or null if the given position is not part of a + * double chest. + */ + public static BlockPos getConnectedChest(World world, BlockPos pos){ + + Block block = world.getBlockState(pos).getBlock(); + + if(block instanceof BlockChest){ + for(EnumFacing enumfacing : EnumFacing.Plane.HORIZONTAL){ + BlockPos pos1 = pos.offset(enumfacing); + if(world.getBlockState(pos1).getBlock() == block){ + return pos1; + } + } + } + + return null; + } + /** * Finds the nearest floor level in the given direction from the given position, * within the range specified. This is a shorthand for From 8cbe5dd602a716d0a8c9e6ca9bceab06aad3f8de Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 18 Apr 2020 17:12:29 +0100 Subject: [PATCH 075/415] Assign key conflict contexts to wizardry's keybinds, fixes them showing up as conflicting with GUI keys --- src/main/java/electroblob/wizardry/client/ClientProxy.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 2dd06e42..989589dc 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -71,6 +71,7 @@ import net.minecraft.util.text.Style; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.village.MerchantRecipeList; import net.minecraft.world.World; +import net.minecraftforge.client.settings.KeyConflictContext; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.config.Property; import net.minecraftforge.fml.client.config.GuiConfigEntries.NumberSliderEntry; @@ -100,8 +101,8 @@ public class ClientProxy extends CommonProxy { private static final Map factories = new HashMap<>(); // Key Bindings - public static final KeyBinding NEXT_SPELL = new KeyBinding("key." + Wizardry.MODID + ".next_spell", Keyboard.KEY_N, "key.categories." + Wizardry.MODID); - public static final KeyBinding PREVIOUS_SPELL = new KeyBinding("key." + Wizardry.MODID + ".previous_spell", Keyboard.KEY_B, "key.categories." + Wizardry.MODID); + public static final KeyBinding NEXT_SPELL = new KeyBinding("key." + Wizardry.MODID + ".next_spell", KeyConflictContext.IN_GAME, Keyboard.KEY_N, "key.categories." + Wizardry.MODID); + public static final KeyBinding PREVIOUS_SPELL = new KeyBinding("key." + Wizardry.MODID + ".previous_spell", KeyConflictContext.IN_GAME, Keyboard.KEY_B, "key.categories." + Wizardry.MODID); // Armour Model public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f); From b9958ce16fe0319ca4d0c349104f42bbabff42fd Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 18 Apr 2020 18:06:22 +0100 Subject: [PATCH 076/415] Add cloud particle from TF spell pack, and change smoke bomb to use it instead of vanilla's large_smoke particles --- .../wizardry/client/ClientProxy.java | 1 + .../client/particle/ParticleCloud.java | 52 ++++++++++++++++++ .../entity/projectile/EntitySmokeBomb.java | 11 ++-- .../wizardry/util/ParticleBuilder.java | 2 + .../ebwizardry/textures/particle/cloud_0.png | Bin 0 -> 2006 bytes .../ebwizardry/textures/particle/cloud_1.png | Bin 0 -> 2038 bytes .../ebwizardry/textures/particle/cloud_2.png | Bin 0 -> 1988 bytes .../ebwizardry/textures/particle/cloud_3.png | Bin 0 -> 2084 bytes 8 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleCloud.java create mode 100644 src/main/resources/assets/ebwizardry/textures/particle/cloud_0.png create mode 100644 src/main/resources/assets/ebwizardry/textures/particle/cloud_1.png create mode 100644 src/main/resources/assets/ebwizardry/textures/particle/cloud_2.png create mode 100644 src/main/resources/assets/ebwizardry/textures/particle/cloud_3.png diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 989589dc..da53f756 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -333,6 +333,7 @@ public class ClientProxy extends CommonProxy { ParticleWizardry.registerParticle(Type.BEAM, ParticleBeam::new); ParticleWizardry.registerParticle(Type.BLOCK_HIGHLIGHT, ParticleBlockHighlight::new); ParticleWizardry.registerParticle(Type.BUFF, ParticleBuff::new); + ParticleWizardry.registerParticle(Type.CLOUD, ParticleCloud::new); ParticleWizardry.registerParticle(Type.DARK_MAGIC, ParticleDarkMagic::new); ParticleWizardry.registerParticle(Type.DUST, ParticleDust::new); ParticleWizardry.registerParticle(Type.FLASH, ParticleFlash::new); diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleCloud.java b/src/main/java/electroblob/wizardry/client/particle/ParticleCloud.java new file mode 100644 index 00000000..f59ebbf7 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleCloud.java @@ -0,0 +1,52 @@ +package electroblob.wizardry.client.particle; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; +import net.minecraftforge.client.event.TextureStitchEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.Side; + +@Mod.EventBusSubscriber(Side.CLIENT) +public class ParticleCloud extends ParticleWizardry { + + private static final ResourceLocation[] TEXTURES = generateTextures("cloud", 4); + + public ParticleCloud(World world, double x, double y, double z){ + + super(world, x, y, z, TEXTURES[world.rand.nextInt(TEXTURES.length)]); + + this.setRBGColorF(1, 1, 1); + this.particleMaxAge = 48 + this.rand.nextInt(12); + this.particleScale *= 6; + this.setGravity(false); + this.setAlphaF(0); + this.canCollide = false; + this.shaded = true; + } + + @Override + public boolean shouldDisableDepth(){ + return true; + } + + @Override + public void onUpdate(){ + + super.onUpdate(); + + // Fading + float fadeTime = this.particleMaxAge * 0.3f; + this.setAlphaF(MathHelper.clamp(Math.min(this.particleAge / fadeTime, (this.particleMaxAge - this.particleAge) / fadeTime), 0, 1)); + + } + + @SubscribeEvent + public static void onTextureStitchEvent(TextureStitchEvent.Pre event){ + for(ResourceLocation texture : TEXTURES){ + event.getMap().registerSprite(texture); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java index 1339c929..ae0c078f 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java @@ -39,13 +39,12 @@ public class EntitySmokeBomb extends EntityBomb { this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); for(int i = 0; i < 60 * blastMultiplier; i++){ - - this.world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, - this.posX + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, - this.posY + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, - this.posZ + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, 0, 0, 0); - + float brightness = rand.nextFloat() * 0.3f; + ParticleBuilder.create(Type.CLOUD, rand, posX, posY, posZ, 2*blastMultiplier, false) + .clr(brightness, brightness, brightness).spawn(world); + + brightness = rand.nextFloat() * 0.3f; ParticleBuilder.create(Type.DARK_MAGIC, rand, posX, posY, posZ, 2*blastMultiplier, false) .clr(brightness, brightness, brightness).spawn(world); } diff --git a/src/main/java/electroblob/wizardry/util/ParticleBuilder.java b/src/main/java/electroblob/wizardry/util/ParticleBuilder.java index e0a1e8e2..fa6e6660 100644 --- a/src/main/java/electroblob/wizardry/util/ParticleBuilder.java +++ b/src/main/java/electroblob/wizardry/util/ParticleBuilder.java @@ -98,6 +98,8 @@ public final class ParticleBuilder { /** Helical animated 'buffing' particle.

Defaults:
Lifetime: 15 ticks *
Velocity: (0, 0.27, 0)
Colour: white */ public static final ResourceLocation BUFF = new ResourceLocation(Wizardry.MODID,"buff"); + /** Large, thick cloud.

Defaults:
Lifetime: 48-60 ticks
Colour: dark grey */ + public static final ResourceLocation CLOUD = new ResourceLocation(Wizardry.MODID,"cloud"); /** Spiral particle, like potions.

Defaults:
Lifetime: 8-40 ticks
Colour: white */ public static final ResourceLocation DARK_MAGIC = new ResourceLocation(Wizardry.MODID,"dark_magic"); /** Single pixel particle.

Defaults:
Lifetime: 16-80 ticks
Colour: white */ diff --git a/src/main/resources/assets/ebwizardry/textures/particle/cloud_0.png b/src/main/resources/assets/ebwizardry/textures/particle/cloud_0.png new file mode 100644 index 0000000000000000000000000000000000000000..a2f8dd5fe1a06cf5c14b4a71de2b52d094e6a26a GIT binary patch literal 2006 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 RF)%PNc)I$ztaD0e0swn0-L3!t literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/particle/cloud_2.png b/src/main/resources/assets/ebwizardry/textures/particle/cloud_2.png new file mode 100644 index 0000000000000000000000000000000000000000..7e49bfcc775dc803f8a9f928677013bfe7568c62 GIT binary patch literal 1988 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}Y)RhkE(~`;@T(}tTm}XP z&H|6fVg?3oVGw3ym^DX&fq{X&#M9T6{Rtzlkb;Gd{>y3x2KGQt7srr@!)vGecBHzC z9C5-c`-z*A@Kcov*y12xBzGu(C zo>#up4yYIDd-LvH*&>aodlr4kbBehh?EOCZWD55S`6v}9 znd4JZBbyoj@cr0g`0VXM>)>hReg8L7aHsSR>+_$59o$`{9GEjAvZWnD5?8-@^X5yB zVbuJ9(5*VBGpy$Bc8~`^xXt@sZO4Bd1+V+SuX55!EOk!hWOVT&?_r zHtD03%n7Z~52{QPI1&H4Oq_uS9ztg2@< zv1+QRc%OYer`yB6FWJn@%x?GAt*+Z|AFcM^U*93-qbB^xX8FyWNy{`HcD$0T6s$NT zu{l(Dv(FP9 zkw%i$+(^h-+FEcfu_v-= zT56{uTd1u`+S*hbxyxBwPkf%UNQ2>>_(XM=C#r%!r*GZHFm0Jy%YtjS!g%v~5)C8* zrm0?jIpt)^p$eP0-r`3Vb}Vu28#^v!ZS}Z3#iGx$`kRi@C*23liY<>7ThelCLqp@` z7ie^CE}hfOv8t=XVE%c}NiAi&Yc<68L}zb}`ck#m%6>v>o&)-_?gH3|J$8q?Ca*(oS*5Vw)M5$lfC~X zK2$%*Jdm~dX3v~r-sdsOzL$#+tX5>{G3?-2?l(6nH}|cr`X7NqllWZ&AIwp1`M_{N zeB$+n_Qrg-RkuFeb60F>D}HBwV6D3Uyd{@k#!P>7=6Ug&tsNX+Tdn5y`A>S{CwP;| ztby0`*bR%m;P-z8c7(c|*z{(X>iX-i4Q2SI$E*;#qAFM!7h2C%!}enJ_qf$8hRpNW zJtjRV@?G#cGf?EJ=gP8^s-d>}N@6#d>Q;z)?~fKantkA{Vhguo%dxBzfA{BZj7V4; z+dId z6^75&v$L~zU(7JEE`4=H_r3c|;p2wycbv#o`lK#cdBIXVw9=z?@kNcQz4y*H>@F)W zUoX9fjaTu)%aR>0m(314K7XaffuKHZ$vF09-A9`~R`|?&?z8-IRK&gDlq+fLoeT2w z`5$n7*we8mbFD1%eCC=hN$WD_JUgu;M4kNXD0BSysZCq1Pf6Ju_uem|JhbQAw{IPfXVmTA&+`4=hB{&Mi(!ME3L zJ!aUwdpA$J^R2gL;p<`?T|6eGXhxjd`LzCQ+UE%cp;HA9T&TXk^4IT%sc+xDb(A@N z+Ud)K2M?YfXm7OMb@$!BPGNPobC%n-ZPPeC>0pAxyystj*FO7fxi+l(V#by))$^Xe zdGkg=DI%LwQD>F6EtkxCoj+e+IhaM&vRAy{djI|X>8UqmuiYw}wM^P`(idkAwU!N4 zuMK9ORq0YCI@gY~^SH7xz|5v}qaDB+r zpF*Fc#6wQ4xOS_t_+8xdJN-R|_66tevetyS#06ZP-eY(_dux=U@8wI?@(n?u)yMl} zwU0mkcsl*uoS#3W^Y=X5`+e!EI<1z1#fHx!1wW=n>SZ5Vv8v;W#NunWG+Gv{%G!N5 z&uad8d7+8>x8Hu7-Z=fT$zER{F> zx!VRUt&+_)UGVZAN%D(>oeBkF)Q`Qd&qH*!e`*`l$&oKGr8hE3;Qt@}Wno{j? zmgrhm1Lk|Hei=XLcZfRso5^7PnYv`te=;BDa@CaT@4xiD;lKGFefj$nozLxLU|?YI MboFyt=akR{0CFJD7XSbN literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/particle/cloud_3.png b/src/main/resources/assets/ebwizardry/textures/particle/cloud_3.png new file mode 100644 index 0000000000000000000000000000000000000000..a53b59f4aab399a283183916afff8c80b9b94092 GIT binary patch literal 2084 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}Y)RhkE(~`;@T(}tTm}XP z&H|6fVg?3oVGw3ym^DX&fq{X&#M9T6{RtzlkfLmz0Z%Oh1N%l#7srr@!*i!suShKw zJ#L@hHt%GUs`iEp;vCoUL2C zbzgSv_43@v_;~$_vr{h5o?Fz<#P~z!gZ2Yf1$I5QYmJH*n3lba^ge#}i~V+)3!8MF z_euA^vUJAY#*|sR;8s`N$|WAnCRZf969ap;k)m<4By3a{K_l;A08Si zA9{6vw})(U$%B>-pN6uYk1~RiyMD7Ckk}#kquA-(r27mCuV24DyGdv3#SD!D1r~Q! zubM3JcD*6ETPtvA^Sgh#f|dQdpU?G6KmPIY@o<+X zg5~znF7G@W|A<*|?NPL}XL!PuF)h_HU`O@liy3=3d|aMb3f>f&_`K2VLraGZ-yH53 z`?-GEf-CRt6`IK1I6G?XwcmIC7Cqo9|IhK^o5-D=N=3fYQg1hM2|SQ(c-pXQcbh;! z=-Ev?3&bX_-L-B%uMFEi7M-UdLQqif<<}}h zO9{r~4XT2YJAPj|b8n83{8F{rwd{9n`wh)pk2EBtSu;+XKK;C0ubX^_p_0;HzvY`_ zLPg*8b$IN*fBw{_C3o|TC3p-r`?xUZo%;Oe&mxU?a=mUVUU8>wOKxyX>xi_g!zsd*42xO0m}S7h_0yzQALQl}%QEnKyE!OGXO1emn7wb!g!laQ8^muDow zqvo)A-E05ZH|s)ct){+bdXTt)`OTc~4zbr|1$Ww?-n6@6zo`^&_V(LXN_#sdJhxmM z*1bvRdC7~i-D}pao%r06J0~eMwN>Y|m*B#6*$v?htMB}kO3*#qEYGk^bMjS*WcApP zgi@=yuZ|sKJDWCn)v6gjYApgBif87oUAJ!11G|%-Y?iOmx_0fF#_3NJR{dk@P%7g1 z;X8{{`oP)B#TPH^TBk2KF*PB-=cDt1Io}UIEQo75>hphksg>**o>1H4ckk;e@iY4~ z{BC$^VyIhQe)jX9l@gEBlX}ectjvv_INsda+MTxfP(;EJ*GR8 zsuJW5M5fOzJ$5d+=kw#cyUUZ)OlJFvPt@P>J3M$&J)>E=Z^nUl!J+5x7JfHAS7rFX z=DcT!Sb4tMyLjF|f;Y^j$A{K3{80a(a)Eips;<24r~hYXYFx}Qlk9byaWh%-b?t$( z##>B}?GUa|JH6?5W4B96>i2hdmoL3OQ)%PQou2Ej&z|~au2PZo2h|5ZO(vae)D4~6 zB5;qtmm({vNsZu z)6SMA7)Th*^^1&+o!c#V;Mp^^(9l4Uqn@>wGfX6S*rufJJh9Ag(ZhoxMY(78|k9O)FkQtBHyt%F06#ci?Rd}XX zht+#Kw`}+(#I%#i(S>hGYoQ%;QqPK4H8TCjA5S)#(;3txZoK&RTO+C77iGJ@{Qm3u zwu77FkIW9UxtnD&R=sM@+r{X^`O_ydDdCA%P*%$D7yw;cv({Ly>i0jlPMd&&E~wB&!qa{ z@0Wjtd^2`Gubjt!_pkN4d9!bBH9Y3MS!cKYu^s*{_q3H}NV*34%sRWwfBEH0cdwpV z@%k3OjDU$2(_{I@%&)7ESF9@_XuNsBs&>~WQzafBJ zJbRsN@}JcmUtaIZ>+;yH!8KP=z)8S3xwQ1_jNSf0&HwB?^YmGZJ%tu)y*$7$V_x3T zi;Fb!%D=B)nKg0S!|Kq|>Uq!4u3GhtN&fxUs;#@tH|Z$1Y~ah8lKOK^#~+K955}Q? zm+t<5`))qNzKK`zmDm0(p8WOe*JqmYb3ex~nmFs`Ii1s+{FZOttkHFvU(UvY0_WWmH z`S!DC&ng=pd)|;L$hqp4k+I8?=kKb|ya_X|X8DnKtEQdX`q=TH=k~lcazA7Ro|w#k zzU#vUp1Chqrh0w9Gv%MWmsh#Fl)?4o7xOw9H~)6t_3z-9GqeA3*IE2}ZxNHy#K6G7 N;OXk;vd$@?2>`_z|4;w` literal 0 HcmV?d00001 From 0a2368650aecbb4776492074b5b313e55cfc2627 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 18 Apr 2020 18:11:43 +0100 Subject: [PATCH 077/415] Tweak particle brightness and lifetime --- .../wizardry/entity/projectile/EntitySmokeBomb.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java index ae0c078f..75b5a590 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java @@ -40,9 +40,9 @@ public class EntitySmokeBomb extends EntityBomb { for(int i = 0; i < 60 * blastMultiplier; i++){ - float brightness = rand.nextFloat() * 0.3f; + float brightness = rand.nextFloat() * 0.1f + 0.1f; ParticleBuilder.create(Type.CLOUD, rand, posX, posY, posZ, 2*blastMultiplier, false) - .clr(brightness, brightness, brightness).spawn(world); + .clr(brightness, brightness, brightness).time(80 + this.rand.nextInt(12)).spawn(world); brightness = rand.nextFloat() * 0.3f; ParticleBuilder.create(Type.DARK_MAGIC, rand, posX, posY, posZ, 2*blastMultiplier, false) From 3316c8e5cbe5c74fcd87245c59808444805a2171 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 18 Apr 2020 19:14:47 +0100 Subject: [PATCH 078/415] Note to self: syncing is important --- src/main/java/electroblob/wizardry/Settings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index 952d9062..cf3db7a4 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -174,7 +174,7 @@ public final class Settings { * [Server-only] Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, * velocity-based one. */ - public boolean replaceVanillaFallDamage = true; + public boolean replaceVanillaFallDamage = true; // TODO: This should be synced /** [Server-only] Whether using bonemeal on grass blocks has a chance to grow crystal flowers. */ public boolean bonemealGrowsCrystalFlowers = true; /** From ed63704184b1316063f779118fbfd40fc186c401 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 18 Apr 2020 21:10:02 +0100 Subject: [PATCH 079/415] Add descriptions to mana flasks --- .../electroblob/wizardry/item/ItemManaFlask.java | 15 +++++++++++++++ .../resources/assets/ebwizardry/lang/en_gb.lang | 1 + .../resources/assets/ebwizardry/lang/en_us.lang | 1 + 3 files changed, 17 insertions(+) diff --git a/src/main/java/electroblob/wizardry/item/ItemManaFlask.java b/src/main/java/electroblob/wizardry/item/ItemManaFlask.java index 8823b632..9b09c9e8 100644 --- a/src/main/java/electroblob/wizardry/item/ItemManaFlask.java +++ b/src/main/java/electroblob/wizardry/item/ItemManaFlask.java @@ -1,9 +1,17 @@ package electroblob.wizardry.item; +import electroblob.wizardry.Wizardry; import electroblob.wizardry.registry.WizardryTabs; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.EnumRarity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nullable; +import java.util.List; public class ItemManaFlask extends Item { @@ -34,4 +42,11 @@ public class ItemManaFlask extends Item { public EnumRarity getRarity(ItemStack stack){ return size.rarity; } + + @Override + @SideOnly(Side.CLIENT) + public void addInformation(ItemStack stack, @Nullable World worldIn, List tooltip, ITooltipFlag flagIn){ + Wizardry.proxy.addMultiLineDescription(tooltip, "item." + Wizardry.MODID + "mana_flask.desc", size.capacity); + } + } diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index a9f7ec46..70f25949 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -135,6 +135,7 @@ item.ebwizardry\:spectral_pickaxe_upgraded.name=Spectral Pickaxe item.ebwizardry\:small_mana_flask.name=Small Mana Flask item.ebwizardry\:medium_mana_flask.name=Medium Mana Flask item.ebwizardry\:large_mana_flask.name=Large Mana Flask +item.ebwizardry\:mana_flask.desc=Craft with a wand or piece of wizard armour to restore %s mana item.ebwizardry\:grand_crystal.name=Grand Magic Crystal item.ebwizardry\:grand_crystal.desc=A huge magic crystal, several times the size of those found naturally. Someone must have grown it artificially - but how? diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index d058ba19..d7b710a0 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -135,6 +135,7 @@ item.ebwizardry\:spectral_pickaxe_upgraded.name=Spectral Pickaxe item.ebwizardry\:small_mana_flask.name=Small Mana Flask item.ebwizardry\:medium_mana_flask.name=Medium Mana Flask item.ebwizardry\:large_mana_flask.name=Large Mana Flask +item.ebwizardry\:mana_flask.desc=Craft with a wand or piece of wizard armor to restore %s mana item.ebwizardry\:grand_crystal.name=Grand Magic Crystal item.ebwizardry\:grand_crystal.desc=A huge magic crystal, several times the size of those found naturally. Someone must have grown it artificially - but how? From f0752317e64b9bcd2485657897f426471c2d79d1 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 18 Apr 2020 21:45:01 +0100 Subject: [PATCH 080/415] Add spell quick access keys --- .../wizardry/client/ClientProxy.java | 11 ++++ .../client/WizardryControlHandler.java | 28 ++++++++ .../wizardry/item/ISpellCastingItem.java | 10 +++ .../electroblob/wizardry/item/ItemWand.java | 5 ++ .../packet/PacketSpellQuickAccess.java | 66 +++++++++++++++++++ .../packet/WizardryPacketHandler.java | 37 ++++++----- .../electroblob/wizardry/util/WandHelper.java | 20 ++++++ .../assets/ebwizardry/lang/en_gb.lang | 8 +++ .../assets/ebwizardry/lang/en_us.lang | 8 +++ 9 files changed, 175 insertions(+), 18 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/packet/PacketSpellQuickAccess.java diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index da53f756..d4d68860 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -16,6 +16,7 @@ import electroblob.wizardry.client.particle.*; import electroblob.wizardry.client.particle.ParticleWizardry.IWizardryParticleFactory; import electroblob.wizardry.client.renderer.*; import electroblob.wizardry.command.SpellEmitter; +import electroblob.wizardry.constants.Constants; import electroblob.wizardry.data.DispenserCastingData; import electroblob.wizardry.data.SpellEmitterData; import electroblob.wizardry.data.SpellGlyphData; @@ -72,6 +73,7 @@ import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.village.MerchantRecipeList; import net.minecraft.world.World; import net.minecraftforge.client.settings.KeyConflictContext; +import net.minecraftforge.client.settings.KeyModifier; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.config.Property; import net.minecraftforge.fml.client.config.GuiConfigEntries.NumberSliderEntry; @@ -103,6 +105,14 @@ public class ClientProxy extends CommonProxy { // Key Bindings public static final KeyBinding NEXT_SPELL = new KeyBinding("key." + Wizardry.MODID + ".next_spell", KeyConflictContext.IN_GAME, Keyboard.KEY_N, "key.categories." + Wizardry.MODID); public static final KeyBinding PREVIOUS_SPELL = new KeyBinding("key." + Wizardry.MODID + ".previous_spell", KeyConflictContext.IN_GAME, Keyboard.KEY_B, "key.categories." + Wizardry.MODID); + public static final KeyBinding[] SPELL_QUICK_ACCESS = new KeyBinding[ItemWand.BASE_SPELL_SLOTS + Constants.UPGRADE_STACK_LIMIT]; + + static { + for(int i = 0; i < SPELL_QUICK_ACCESS.length; i++){ + SPELL_QUICK_ACCESS[i] = new KeyBinding("key." + Wizardry.MODID + ".spell_" + (i+1), + KeyConflictContext.IN_GAME, KeyModifier.ALT, Keyboard.KEY_1 + i, "key.categories." + Wizardry.MODID); + } + } // Armour Model public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f); @@ -122,6 +132,7 @@ public class ClientProxy extends CommonProxy { public void registerKeyBindings(){ ClientRegistry.registerKeyBinding(NEXT_SPELL); ClientRegistry.registerKeyBinding(PREVIOUS_SPELL); + for(KeyBinding key : SPELL_QUICK_ACCESS) ClientRegistry.registerKeyBinding(key); } @Override diff --git a/src/main/java/electroblob/wizardry/client/WizardryControlHandler.java b/src/main/java/electroblob/wizardry/client/WizardryControlHandler.java index 7b98aca0..377880a1 100644 --- a/src/main/java/electroblob/wizardry/client/WizardryControlHandler.java +++ b/src/main/java/electroblob/wizardry/client/WizardryControlHandler.java @@ -4,6 +4,7 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.client.gui.GuiSpellDisplay; import electroblob.wizardry.item.ISpellCastingItem; import electroblob.wizardry.packet.PacketControlInput; +import electroblob.wizardry.packet.PacketSpellQuickAccess; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.WizardrySounds; import net.minecraft.client.Minecraft; @@ -24,6 +25,7 @@ public class WizardryControlHandler { static boolean NkeyPressed = false; static boolean BkeyPressed = false; + static boolean[] quickAccessKeyPressed = new boolean[ClientProxy.SPELL_QUICK_ACCESS.length]; // Changed to a tick event to allow mouse button keybinds // The 'lag' that happened previously was actually because the code only fired when a keyboard key was pressed! @@ -59,6 +61,19 @@ public class WizardryControlHandler { }else{ BkeyPressed = false; } + + for(int i = 0; i < ClientProxy.SPELL_QUICK_ACCESS.length; i++){ + if(ClientProxy.SPELL_QUICK_ACCESS[i].isKeyDown() && Minecraft.getMinecraft().inGameHasFocus){ + if(!quickAccessKeyPressed[i]){ + quickAccessKeyPressed[i] = true; + // Packet building + selectSpell(wand, i); + } + }else{ + quickAccessKeyPressed[i] = false; + } + } + } } } @@ -118,4 +133,17 @@ public class WizardryControlHandler { GuiSpellDisplay.playSpellSwitchAnimation(false); Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1)); } + + private static void selectSpell(ItemStack wand, int index){ + // GUI switch animation + if(((ISpellCastingItem)wand.getItem()).selectSpell(wand, index)){ // Makes sure the spell is set immediately for the client + // Packet building (no point sending it unless the client-side spell selection succeeded + IMessage msg = new PacketSpellQuickAccess.Message(index); + WizardryPacketHandler.net.sendToServer(msg); + + GuiSpellDisplay.playSpellSwitchAnimation(true); // This will do, it's only an animation + Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1)); + } + } + } diff --git a/src/main/java/electroblob/wizardry/item/ISpellCastingItem.java b/src/main/java/electroblob/wizardry/item/ISpellCastingItem.java index 0d532200..2666a5bb 100644 --- a/src/main/java/electroblob/wizardry/item/ISpellCastingItem.java +++ b/src/main/java/electroblob/wizardry/item/ISpellCastingItem.java @@ -65,6 +65,16 @@ public interface ISpellCastingItem { // Nothing here either } + /** + * Selects the spell at the given index bound to the given itemstack. The given itemstack will be of this item. + * @param stack The itemstack to query. + * @param index The index to set. + * @return True if the operation succeeded, false if not. + */ + default boolean selectSpell(ItemStack stack, int index){ + return false; + } + /** * Returns whether the spell HUD should be shown when a player is holding this item. Only called client-side. * @param player The player holding the item. diff --git a/src/main/java/electroblob/wizardry/item/ItemWand.java b/src/main/java/electroblob/wizardry/item/ItemWand.java index 8272098a..8c80cb75 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWand.java +++ b/src/main/java/electroblob/wizardry/item/ItemWand.java @@ -113,6 +113,11 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem, WandHelper.selectPreviousSpell(stack); } + @Override + public boolean selectSpell(ItemStack stack, int index){ + return WandHelper.selectSpell(stack, index); + } + @Override public boolean showSpellHUD(EntityPlayer player, ItemStack stack){ return true; diff --git a/src/main/java/electroblob/wizardry/packet/PacketSpellQuickAccess.java b/src/main/java/electroblob/wizardry/packet/PacketSpellQuickAccess.java new file mode 100644 index 00000000..4828216b --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/PacketSpellQuickAccess.java @@ -0,0 +1,66 @@ +package electroblob.wizardry.packet; + +import electroblob.wizardry.item.ISpellCastingItem; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** [Client -> Server] This packet is for the spell quick access keys. */ +public class PacketSpellQuickAccess implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx){ + + // Just to make sure that the side is correct + if(ctx.side.isServer()){ + + final EntityPlayerMP player = ctx.getServerHandler().player; + + player.getServerWorld().addScheduledTask(() -> { + + ItemStack wand = player.getHeldItemMainhand(); + + if(!(wand.getItem() instanceof ISpellCastingItem)){ + wand = player.getHeldItemOffhand(); + } + + if(wand.getItem() instanceof ISpellCastingItem){ + + ((ISpellCastingItem)wand.getItem()).selectSpell(wand, message.index); + // This line fixes the bug with continuous spells casting when they shouldn't be + player.stopActiveHand(); + } + + }); + } + + return null; + } + + public static class Message implements IMessage { + + private int index; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message(){ + } + + public Message(int index){ + this.index = index; + } + + @Override + public void fromBytes(ByteBuf buf){ + // The order is important + this.index = buf.readInt(); + } + + @Override + public void toBytes(ByteBuf buf){ + buf.writeInt(index); + } + } +} diff --git a/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java b/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java index 8feabd56..7d7133c2 100644 --- a/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java +++ b/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java @@ -13,25 +13,26 @@ public class WizardryPacketHandler { public static void initPackets(){ net = NetworkRegistry.INSTANCE.newSimpleChannel(Wizardry.MODID.toUpperCase()); - registerMessage(PacketControlInput.class, PacketControlInput.Message.class); - registerMessage(PacketCastSpell.class, PacketCastSpell.Message.class); - registerMessage(PacketTransportation.class, PacketTransportation.Message.class); - registerMessage(PacketPlayerSync.class, PacketPlayerSync.Message.class); - registerMessage(PacketGlyphData.class, PacketGlyphData.Message.class); - registerMessage(PacketCastContinuousSpell.class, PacketCastContinuousSpell.Message.class); - registerMessage(PacketClairvoyance.class, PacketClairvoyance.Message.class); - registerMessage(PacketSyncSettings.class, PacketSyncSettings.Message.class); - registerMessage(PacketNPCCastSpell.class, PacketNPCCastSpell.Message.class); - registerMessage(PacketDispenserCastSpell.class, PacketDispenserCastSpell.Message.class); - registerMessage(PacketSpellProperties.class, PacketSpellProperties.Message.class); - registerMessage(PacketSyncAdvancements.class, PacketSyncAdvancements.Message.class); + registerMessage(PacketControlInput.class, PacketControlInput.Message.class); + registerMessage(PacketCastSpell.class, PacketCastSpell.Message.class); + registerMessage(PacketTransportation.class, PacketTransportation.Message.class); + registerMessage(PacketPlayerSync.class, PacketPlayerSync.Message.class); + registerMessage(PacketGlyphData.class, PacketGlyphData.Message.class); + registerMessage(PacketCastContinuousSpell.class, PacketCastContinuousSpell.Message.class); + registerMessage(PacketClairvoyance.class, PacketClairvoyance.Message.class); + registerMessage(PacketSyncSettings.class, PacketSyncSettings.Message.class); + registerMessage(PacketNPCCastSpell.class, PacketNPCCastSpell.Message.class); + registerMessage(PacketDispenserCastSpell.class, PacketDispenserCastSpell.Message.class); + registerMessage(PacketSpellProperties.class, PacketSpellProperties.Message.class); + registerMessage(PacketSyncAdvancements.class, PacketSyncAdvancements.Message.class); registerMessage(PacketRequestAdvancementSync.class, PacketRequestAdvancementSync.Message.class); - registerMessage(PacketResurrection.class, PacketResurrection.Message.class); - registerMessage(PacketCastSpellAtPos.class, PacketCastSpellAtPos.Message.class); - registerMessage(PacketEmitterData.class, PacketEmitterData.Message.class); - registerMessage(PacketPossession.class, PacketPossession.Message.class); - registerMessage(PacketConquerShrine.class, PacketConquerShrine.Message.class); - registerMessage(PacketLectern.class, PacketLectern.Message.class); + registerMessage(PacketResurrection.class, PacketResurrection.Message.class); + registerMessage(PacketCastSpellAtPos.class, PacketCastSpellAtPos.Message.class); + registerMessage(PacketEmitterData.class, PacketEmitterData.Message.class); + registerMessage(PacketPossession.class, PacketPossession.Message.class); + registerMessage(PacketConquerShrine.class, PacketConquerShrine.Message.class); + registerMessage(PacketLectern.class, PacketLectern.Message.class); + registerMessage(PacketSpellQuickAccess.class, PacketSpellQuickAccess.Message.class); } private static int nextPacketId = 0; diff --git a/src/main/java/electroblob/wizardry/util/WandHelper.java b/src/main/java/electroblob/wizardry/util/WandHelper.java index 677761a3..ca8fde07 100644 --- a/src/main/java/electroblob/wizardry/util/WandHelper.java +++ b/src/main/java/electroblob/wizardry/util/WandHelper.java @@ -170,6 +170,26 @@ public final class WandHelper { wand.getTagCompound().setInteger(SELECTED_SPELL_KEY, getPreviousSpellIndex(wand)); } } + + /** + * Selects the spell at the given index in this wand's list of spells. + * @param wand The stack to set the spell of + * @param index The spell index to set the wand to + * @return False if the index was out-of-bounds, true otherwise + */ + public static boolean selectSpell(ItemStack wand, int index){ + + if(index < 0 || index > getSpells(wand).length) return false; // Out-of-bounds + + // 5 here because if the spell array doesn't exist, the wand can't possibly have attunement upgrades + if(getSpells(wand).length < 0) setSpells(wand, new Spell[ItemWand.BASE_SPELL_SLOTS]); + + if(wand.getTagCompound() != null){ + wand.getTagCompound().setInteger(SELECTED_SPELL_KEY, index); + } + + return true; + } private static int getNextSpellIndex(ItemStack wand){ diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 70f25949..ffc3116e 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -1076,6 +1076,14 @@ key.categories.ebwizardry=Wizardry key.ebwizardry.next_spell=Next Spell key.ebwizardry.previous_spell=Previous Spell +key.ebwizardry.spell_1=Spell Slot 1 +key.ebwizardry.spell_2=Spell Slot 2 +key.ebwizardry.spell_3=Spell Slot 3 +key.ebwizardry.spell_4=Spell Slot 4 +key.ebwizardry.spell_5=Spell Slot 5 +key.ebwizardry.spell_6=Spell Slot 6 +key.ebwizardry.spell_7=Spell Slot 7 +key.ebwizardry.spell_8=Spell Slot 8 death.attack.wizardry_magic=%1$s was killed by %2$s using magic death.attack.indirect_wizardry_magic=%1$s was killed by %2$s using magic diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index d7b710a0..7b5d46f9 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -1076,6 +1076,14 @@ key.categories.ebwizardry=Wizardry key.ebwizardry.next_spell=Next Spell key.ebwizardry.previous_spell=Previous Spell +key.ebwizardry.spell_1=Spell Slot 1 +key.ebwizardry.spell_2=Spell Slot 2 +key.ebwizardry.spell_3=Spell Slot 3 +key.ebwizardry.spell_4=Spell Slot 4 +key.ebwizardry.spell_5=Spell Slot 5 +key.ebwizardry.spell_6=Spell Slot 6 +key.ebwizardry.spell_7=Spell Slot 7 +key.ebwizardry.spell_8=Spell Slot 8 death.attack.wizardry_magic=%1$s was killed by %2$s using magic death.attack.indirect_wizardry_magic=%1$s was killed by %2$s using magic From 5ad4cc06101f47e2675a14b80ca00b8c335be490 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 19 Apr 2020 17:41:12 +0100 Subject: [PATCH 081/415] Fix hoppers putting spell books in hidden slots --- .../tileentity/TileEntityArcaneWorkbench.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java index a93acf55..72daa018 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java @@ -168,7 +168,17 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory, if(itemstack == ItemStack.EMPTY) return true; if(slotNumber >= 0 && slotNumber < ContainerArcaneWorkbench.CRYSTAL_SLOT){ - return itemstack.getItem() instanceof ItemSpellBook; + + if(!(itemstack.getItem() instanceof ItemSpellBook)) return false; + + ItemStack centreStack = getStackInSlot(ContainerArcaneWorkbench.CENTRE_SLOT); + + if(centreStack.getItem() instanceof IWorkbenchItem){ + int spellSlots = ((IWorkbenchItem)centreStack.getItem()).getSpellSlotCount(centreStack); + return slotNumber < spellSlots; + } + + return false; }else if(slotNumber == ContainerArcaneWorkbench.CRYSTAL_SLOT){ return itemstack.getItem() instanceof ItemCrystal; From f89c71f3145ea38e8b138acea7ac5b265a67ae39 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 21 Apr 2020 18:55:13 +0100 Subject: [PATCH 082/415] Make spell scrolls implement IWorkbenchItem and have 1 slot This means the book doesn't leave the workbench until the scrolls are taken out, which is good for automation - however, canPlace always returns false so they can't be put back in, kinda like the furnace output --- .../electroblob/wizardry/item/ItemScroll.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/item/ItemScroll.java b/src/main/java/electroblob/wizardry/item/ItemScroll.java index c1947d35..6c061f5d 100644 --- a/src/main/java/electroblob/wizardry/item/ItemScroll.java +++ b/src/main/java/electroblob/wizardry/item/ItemScroll.java @@ -11,6 +11,7 @@ import electroblob.wizardry.util.SpellModifiers; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Slot; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.*; @@ -22,7 +23,7 @@ import net.minecraftforge.fml.relauncher.SideOnly; import java.util.List; -public class ItemScroll extends Item implements ISpellCastingItem { +public class ItemScroll extends Item implements ISpellCastingItem, IWorkbenchItem { /** The maximum number of ticks a continuous spell scroll can be cast for (by holding the use item button). */ public static final int CASTING_TIME = 120; @@ -222,4 +223,24 @@ public class ItemScroll extends Item implements ISpellCastingItem { public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){ return Wizardry.proxy.getFontRenderer(stack); } + + @Override + public int getSpellSlotCount(ItemStack stack){ + return 1; // Stop spell books immediately leaving the workbench when a scroll is enchanted + } + + @Override + public boolean canPlace(ItemStack stack){ + return false; // Prevent players putting scrolls back in the workbench + } + + @Override + public boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks){ + return false; + } + + @Override + public boolean showTooltip(ItemStack stack){ + return false; + } } From 5d8a90167c9246d2c28f097c5b232025b8aca565 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 21 Apr 2020 18:55:58 +0100 Subject: [PATCH 083/415] Prevent ray spells being cast through walls (oops, my bad :P) --- .../electroblob/wizardry/spell/SpellRay.java | 58 +++++++------------ 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/src/main/java/electroblob/wizardry/spell/SpellRay.java b/src/main/java/electroblob/wizardry/spell/SpellRay.java index 25271427..df5d4ab4 100644 --- a/src/main/java/electroblob/wizardry/spell/SpellRay.java +++ b/src/main/java/electroblob/wizardry/spell/SpellRay.java @@ -173,44 +173,28 @@ public abstract class SpellRay extends Spell { public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ // IDEA: Add in an aiming error and trigger onMiss accordingly Vec3d origin = new Vec3d(caster.posX, caster.getEntityBoundingBox().minY + caster.getEyeHeight() - Y_OFFSET, caster.posZ); - Vec3d direction = null; - - boolean flag = false; - - if(target != null){ + Vec3d targetPos = null; - if((!ignoreLivingEntities || !WizardryUtilities.isLiving(target)) - && onEntityHit(world, target, null, caster, origin, ticksInUse, modifiers)){ - - direction = new Vec3d(target.posX, target.getEntityBoundingBox().minY + target.height/2, target.posZ) - .subtract(origin); - flag = true; - - }else{ // Will run if the spell does not do anything special on entity hit. - - int x = MathHelper.floor(target.posX); - int y = (int)target.getEntityBoundingBox().minY - 1; // -1 because we need the block under the target - int z = MathHelper.floor(target.posZ); - BlockPos pos = new BlockPos(x, y, z); - - // This works as if the NPC had actually aimed at the floor beneath the target, so it needs to check - // that the block is not air and (optionally) not a liquid. - if(!world.isAirBlock(pos) && (!world.getBlockState(pos).getMaterial().isLiquid() || hitLiquids) - && onBlockHit(world, pos, EnumFacing.UP, null, caster, origin, ticksInUse, modifiers)){ - - direction = new Vec3d(x + 0.5, y + 1, z + 0.5).subtract(origin); - flag = true; - } + if(!ignoreLivingEntities || !WizardryUtilities.isLiving(target)){ + targetPos = new Vec3d(target.posX, target.getEntityBoundingBox().minY + target.height / 2, target.posZ); + + }else{ + + int x = MathHelper.floor(target.posX); + int y = (int)target.getEntityBoundingBox().minY - 1; // -1 because we need the block under the target + int z = MathHelper.floor(target.posZ); + BlockPos pos = new BlockPos(x, y, z); + + // This works as if the NPC had actually aimed at the floor beneath the target, so it needs to check that + // the block is not air and (optionally) not a liquid. + if(!world.isAirBlock(pos) && (!world.getBlockState(pos).getMaterial().isLiquid() || hitLiquids)){ + targetPos = new Vec3d(x + 0.5, y + 1, z + 0.5); } } - - // Wizards don't miss... yet - if(!flag) return false; - - // Particle spawning - if(world.isRemote){ - spawnParticleRay(world, origin, direction.normalize(), caster, direction.length()); - } + + if(targetPos == null) return false; // If there was nothing to aim at (e.g. snare when the target is in the air) + + if(!shootSpell(world, origin, targetPos.subtract(origin).normalize(), caster, ticksInUse, modifiers)) return false; if(casterSwingsArm(world, caster, hand, ticksInUse, modifiers)) caster.swingArm(hand); this.playSound(world, caster, ticksInUse, -1, modifiers); @@ -266,8 +250,8 @@ public abstract class SpellRay extends Spell { return !this.isContinuous; } - /** Player and dispenser casting are almost identical so this takes care of the shared stuff. This is mainly for internal use. */ - protected boolean shootSpell(World world, Vec3d origin, Vec3d direction, @Nullable EntityPlayer caster, int ticksInUse, SpellModifiers modifiers){ + /** Takes care of the shared stuff for the three casting methods. This is mainly for internal use. */ + protected boolean shootSpell(World world, Vec3d origin, Vec3d direction, @Nullable EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){ double range = getRange(world, origin, direction, caster, ticksInUse, modifiers); Vec3d endpoint = origin.add(direction.scale(range)); From 755933486c93c6211618d28fdf12c67202285011 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 21 Apr 2020 22:10:55 +0100 Subject: [PATCH 084/415] Also prevent these two from being cast through walls --- src/main/java/electroblob/wizardry/spell/Grapple.java | 4 ++++ .../electroblob/wizardry/spell/SpellConstructRanged.java | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/spell/Grapple.java b/src/main/java/electroblob/wizardry/spell/Grapple.java index 22b0df43..437b27b7 100644 --- a/src/main/java/electroblob/wizardry/spell/Grapple.java +++ b/src/main/java/electroblob/wizardry/spell/Grapple.java @@ -218,6 +218,10 @@ public class Grapple extends Spell { // because the entity will have moved! Vec3d targetVec = new Vec3d(target.posX, target.getEntityBoundingBox().minY + target.height/2, target.posZ); + RayTraceResult hit = findTarget(world, caster, origin, targetVec.subtract(origin).normalize(), modifiers); + + if(hit.typeOfHit != RayTraceResult.Type.ENTITY || hit.entityHit != target) return false; // Something was in the way + double distance = origin.distanceTo(targetVec); // Can't cast the spell at all if the target is too far away diff --git a/src/main/java/electroblob/wizardry/spell/SpellConstructRanged.java b/src/main/java/electroblob/wizardry/spell/SpellConstructRanged.java index b48a28ac..04b5059c 100644 --- a/src/main/java/electroblob/wizardry/spell/SpellConstructRanged.java +++ b/src/main/java/electroblob/wizardry/spell/SpellConstructRanged.java @@ -129,7 +129,8 @@ public class SpellConstructRanged extends SpellC SpellModifiers modifiers){ double range = getProperty(RANGE).doubleValue() * modifiers.get(WizardryItems.range_upgrade); - + Vec3d origin = new Vec3d(caster.posX, caster.getEntityBoundingBox().minY + caster.getEyeHeight(), caster.posZ); + if(target != null && caster.getDistance(target) <= range){ if(!world.isRemote){ @@ -138,6 +139,12 @@ public class SpellConstructRanged extends SpellC double y = target.posY; double z = target.posZ; + RayTraceResult hit = world.rayTraceBlocks(origin, new Vec3d(x, y, z), hitLiquids, ignoreUncollidables, false); + + if(hit != null && hit.typeOfHit == RayTraceResult.Type.BLOCK && !hit.getBlockPos().equals(new BlockPos(x, y, z))){ + return false; // Something was in the way + } + EnumFacing side = null; // If the target is not on the ground but the construct must be placed on the floor, searches for the From 53f545caada0bd05107a134229de408f0dbccd78 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 21 Apr 2020 23:05:48 +0100 Subject: [PATCH 085/415] Replace use of deprecated ReflectionHelper with ObfuscationReflectionHelper --- .../electroblob/wizardry/client/renderer/RenderDecoy.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java b/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java index 10acfa56..c81a862b 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java @@ -8,7 +8,7 @@ import net.minecraft.client.renderer.entity.RenderBiped; import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.entity.Entity; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.ReflectionHelper; +import net.minecraftforge.fml.common.ObfuscationReflectionHelper; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -18,8 +18,8 @@ public class RenderDecoy extends RenderBiped { private static final ResourceLocation steveTextures = new ResourceLocation("textures/entity/steve.png"); - private static final Method getEntityTexture = ReflectionHelper.findMethod(Render.class, "getEntityTexture", - "func_110775_a", Entity.class); // Generic parameter T is erased to Entity at runtime. + private static final Method getEntityTexture = ObfuscationReflectionHelper.findMethod(Render.class, "func_110775_a", + ResourceLocation.class, Entity.class); // Generic parameter T is erased to Entity at runtime. public RenderDecoy(RenderManager manager){ super(manager, new ModelBiped(0.0f), 0.5f); From ee96ff3dddf50c6718d6e4b0f37f09a2c72bbd5b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 22 Apr 2020 17:13:55 +0100 Subject: [PATCH 086/415] Reflect into Block#getSilkTouchDrop instead of copying it, fixes #418 --- .../java/electroblob/wizardry/spell/Mine.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/electroblob/wizardry/spell/Mine.java b/src/main/java/electroblob/wizardry/spell/Mine.java index 30389fb9..d1f4a666 100644 --- a/src/main/java/electroblob/wizardry/spell/Mine.java +++ b/src/main/java/electroblob/wizardry/spell/Mine.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.item.ISpellCastingItem; import electroblob.wizardry.item.ItemArtefact; @@ -15,7 +16,6 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.EnumAction; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; @@ -24,11 +24,20 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.fml.common.ObfuscationReflectionHelper; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.List; public class Mine extends SpellRay { + private static final Method getSilkTouchDrop; + + static { + getSilkTouchDrop = ObfuscationReflectionHelper.findMethod(Block.class, "func_180643_i", ItemStack.class, IBlockState.class); + } + public Mine(){ super("mine", false, EnumAction.NONE); this.ignoreLivingEntities(true); @@ -108,7 +117,10 @@ public class Mine extends SpellRay { if(silkTouch){ flag = world.destroyBlock(pos1, false); - if(flag) Block.spawnAsEntity(world, pos1, getSilkTouchDrop(state1)); + if(flag){ + ItemStack stack = getSilkTouchDrop(state1); + if(stack != null) Block.spawnAsEntity(world, pos1, stack); + } }else{ flag = world.destroyBlock(pos1, true); if(flag) state1.getBlock().dropXpOnBlockBreak(world, pos1, xp); @@ -141,17 +153,15 @@ public class Mine extends SpellRay { .shaded(false).spawn(world); } - // Copied from Block, where (for some reason) it's protected private static ItemStack getSilkTouchDrop(IBlockState state){ - Item item = Item.getItemFromBlock(state.getBlock()); - int i = 0; - - if(item.getHasSubtypes()){ - i = state.getBlock().getMetaFromState(state); + try { + return (ItemStack)getSilkTouchDrop.invoke(state.getBlock(), state); + }catch(IllegalAccessException | InvocationTargetException e){ + Wizardry.logger.error("Error while reflectively retrieving silk touch drop", e); } - return new ItemStack(item, 1, i); + return null; } } From 301ec86c27d09dac946313b7bdffb7c10d3a9bcd Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 22 Apr 2020 17:14:21 +0100 Subject: [PATCH 087/415] Add comparator behaviour to bookshelves --- .../wizardry/block/BlockBookshelf.java | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java index b84ed7d8..267cc4b1 100644 --- a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java +++ b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java @@ -29,7 +29,6 @@ import net.minecraft.world.IWorldEventListener; import net.minecraft.world.World; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.Properties; -import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; @@ -145,6 +144,31 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid return true; } + @Override + public boolean hasComparatorInputOverride(IBlockState state){ + return true; + } + + @Override + public int getComparatorInputOverride(IBlockState state, World world, BlockPos pos){ + + TileEntity tileEntity = world.getTileEntity(pos); + + if(tileEntity instanceof TileEntityBookshelf){ + + int slotsOccupied = 0; + + for(int i = 0; i < ((TileEntityBookshelf)tileEntity).getSizeInventory(); i++){ + if(!((TileEntityBookshelf)tileEntity).getStackInSlot(i).isEmpty()) slotsOccupied++; + } + + return slotsOccupied; + + } + + return super.getComparatorInputOverride(state, world, pos); + } + @Override public boolean eventReceived(IBlockState state, World world, BlockPos pos, int id, int param){ super.eventReceived(state, world, pos, id, param); From 0354f99bd2fa4f10e1f889949ff77a23c5b13c10 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 25 Apr 2020 13:00:40 +0100 Subject: [PATCH 088/415] Make wizardry-specific overloads package-private where possible to avoid confusion --- .../electroblob/wizardry/client/particle/ParticleWizardry.java | 2 +- src/main/java/electroblob/wizardry/spell/Spell.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleWizardry.java b/src/main/java/electroblob/wizardry/client/particle/ParticleWizardry.java index 1b91c6aa..8c365b63 100644 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleWizardry.java +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleWizardry.java @@ -547,7 +547,7 @@ public abstract class ParticleWizardry extends Particle { /** Internal overload for {@link ParticleWizardry#generateTextures(String, String, int, int)} which uses wizardry's * mod ID automatically. */ - public static ResourceLocation[][] generateTextures(String stem, int m, int n){ + static ResourceLocation[][] generateTextures(String stem, int m, int n){ return generateTextures(Wizardry.MODID, stem, m, n); } diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java index 216bf78b..6845f152 100644 --- a/src/main/java/electroblob/wizardry/spell/Spell.java +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -188,7 +188,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C * @param action The vanilla usage action to be displayed when casting this spell. * @param isContinuous Whether this spell is continuous, meaning you cast it for a length of time by holding the */ - public Spell(String name, EnumAction action, boolean isContinuous){ + Spell(String name, EnumAction action, boolean isContinuous){ this(Wizardry.MODID, name, action, isContinuous); } From a31b1c79e339e08e84f8e2aaf9814a567b5bb6fc Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 6 May 2020 20:21:49 +0100 Subject: [PATCH 089/415] Make indirect magic damage count as magic damage, fixes #396 --- .../java/electroblob/wizardry/util/IndirectMagicDamage.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/electroblob/wizardry/util/IndirectMagicDamage.java b/src/main/java/electroblob/wizardry/util/IndirectMagicDamage.java index 826a23fa..957a6b46 100644 --- a/src/main/java/electroblob/wizardry/util/IndirectMagicDamage.java +++ b/src/main/java/electroblob/wizardry/util/IndirectMagicDamage.java @@ -13,6 +13,9 @@ public class IndirectMagicDamage extends EntityDamageSourceIndirect implements I super(name, magic, caster); this.type = type; this.isRetaliatory = isRetaliatory; + this.setMagicDamage(); + if(type == DamageType.FIRE) this.setFireDamage(); + if(type == DamageType.BLAST) this.setExplosion(); } @Override From cf38a16f4908471c3a1e8d829ad3d1cc8d3fdce2 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 7 May 2020 23:22:52 +0100 Subject: [PATCH 090/415] A slightly better way of loading the book model texture --- .../wizardry/client/model/ModelBookshelf.java | 40 ++++++++++++++++++- .../wizardry/client/model/WizardryModels.java | 5 --- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/main/java/electroblob/wizardry/client/model/ModelBookshelf.java b/src/main/java/electroblob/wizardry/client/model/ModelBookshelf.java index 38303c5c..107ea85d 100644 --- a/src/main/java/electroblob/wizardry/client/model/ModelBookshelf.java +++ b/src/main/java/electroblob/wizardry/client/model/ModelBookshelf.java @@ -1,5 +1,6 @@ package electroblob.wizardry.client.model; +import com.google.common.collect.ImmutableList; import electroblob.wizardry.Wizardry; import electroblob.wizardry.block.BlockBookshelf; import net.minecraft.client.renderer.block.model.IBakedModel; @@ -11,13 +12,26 @@ import net.minecraftforge.client.model.IModel; import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.common.model.IModelState; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.function.Function; public class ModelBookshelf implements IModel { + private static final ResourceLocation BOOK_TEXTURE = new ResourceLocation(Wizardry.MODID, "blocks/books"); + + private static final List bookModelLocations = new ArrayList<>(); + private final ModelResourceLocation bookshelfModelLocation; private final String variant; + static { + for(int i = 0; i < BlockBookshelf.SLOT_COUNT; i++){ + bookModelLocations.add(new ResourceLocation(Wizardry.MODID, "bookshelf_parts/books" + i)); + } + } + public ModelBookshelf(String bookshelfModelName){ String[] args = bookshelfModelName.split("#", 2); String model = args[0]; @@ -37,10 +51,12 @@ public class ModelBookshelf implements IModel { IBakedModel[] books = new IBakedModel[BlockBookshelf.SLOT_COUNT]; for(int i = 0; i < BlockBookshelf.SLOT_COUNT; i++){ - IModel bookModel = ModelLoaderRegistry.getModel(new ModelResourceLocation(new ResourceLocation(Wizardry.MODID, "bookshelf_parts/books" + i), variant)); + IModel bookModel = ModelLoaderRegistry.getModel(new ModelResourceLocation(bookModelLocations.get(i), variant)); books[i] = 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 + // Therefore we have to have a baked bookshelf model that's kind of dynamic return new BakedModelBookshelf(bookshelf, books); }catch(Exception exception){ @@ -50,4 +66,26 @@ public class ModelBookshelf implements IModel { } + @Override + public Collection getDependencies(){ + return bookModelLocations; + } + + @Override + public Collection getTextures(){ + + // I simply have no idea how I'm *supposed* to do it, so I'm using the best solution I can come up with + // This is at least better than manually registering it with TextureStitchEvent + // However I'm also pretty sure that if a texture pack decides to change the texture that the book models point + // to, it won't work... but since nobody ever will, I don't really care! + + // Why am I confused? + // 1. How come it magically finds the bookshelf texture, but not the book texture? + // 2. Why don't the textures get retrieved automatically based on getDependencies? Surely that's the point? + // 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); + } + } diff --git a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java index e03e22d5..eae2ecc9 100644 --- a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java +++ b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java @@ -378,9 +378,4 @@ public final class WizardryModels { new ModelResourceLocation(item.getRegistryName(), variant)); } - @SubscribeEvent - public static void onTextureStitchEvent(TextureStitchEvent.Pre event){ - event.getMap().registerSprite(new ResourceLocation(Wizardry.MODID, "blocks/books")); - } - } From be250e218ece62ee211e7cff8cc9eec55b63e49b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 9 May 2020 20:17:15 +0100 Subject: [PATCH 091/415] Add player animation system! - Hooks into ModelRenderer by wrapping it in an extended version that allows modification of angles after the model has set them - Is compatible with pretty much everything, yay (even modded armour!) - Adds property overrides to ItemWand so that gets animated too - Adds a system for generating override models from a template at runtime so I don't have to duplicate all the wand models - Tweaks particles and ray spells so they line up with the end of the newly-animated wand - Fixes a long-standing bug where wand usage actions would stop immediately for all non-continuous spells --- .../electroblob/wizardry/CommonProxy.java | 10 + .../java/electroblob/wizardry/Wizardry.java | 1 + .../wizardry/client/ClientProxy.java | 41 ++-- .../client/animation/ActionAnimation.java | 154 +++++++++++++ .../wizardry/client/animation/Animation.java | 77 +++++++ .../animation/ModelRendererExtended.java | 177 +++++++++++++++ .../client/animation/PlayerAnimator.java | 213 ++++++++++++++++++ .../model/BakedModelGenerateOverrides.java | 163 ++++++++++++++ .../client/model/ItemOverrideListMapped.java | 42 ++++ .../wizardry/client/model/WizardryModels.java | 4 +- .../client/particle/ParticleTargeted.java | 19 ++ .../client/particle/ParticleVine.java | 5 + .../electroblob/wizardry/item/ItemWand.java | 17 +- .../wizardry/item/SpellActions.java | 63 ++++++ .../electroblob/wizardry/registry/Spells.java | 5 +- .../java/electroblob/wizardry/spell/Arc.java | 4 +- .../wizardry/spell/ArcaneJammer.java | 4 +- .../wizardry/spell/ArcaneLock.java | 4 +- .../electroblob/wizardry/spell/Banish.java | 4 +- .../electroblob/wizardry/spell/Blink.java | 5 +- .../electroblob/wizardry/spell/Bubble.java | 4 +- .../wizardry/spell/ChainLightning.java | 4 +- .../electroblob/wizardry/spell/Charge.java | 4 +- .../wizardry/spell/Clairvoyance.java | 4 +- .../electroblob/wizardry/spell/Cobwebs.java | 4 +- .../wizardry/spell/ConjureBlock.java | 4 +- .../wizardry/spell/Containment.java | 4 +- .../wizardry/spell/CurseOfEnfeeblement.java | 4 +- .../wizardry/spell/CurseOfSoulbinding.java | 4 +- .../wizardry/spell/CurseOfUndeath.java | 4 +- .../electroblob/wizardry/spell/Decoy.java | 4 +- .../electroblob/wizardry/spell/Detonate.java | 4 +- .../wizardry/spell/Disintegration.java | 4 +- .../wizardry/spell/Divination.java | 5 +- .../wizardry/spell/Earthquake.java | 4 +- .../wizardry/spell/EmpoweringPresence.java | 4 +- .../wizardry/spell/Entrapment.java | 4 +- .../wizardry/spell/FireBreath.java | 4 +- .../electroblob/wizardry/spell/FlameRay.java | 4 +- .../wizardry/spell/FlamingWeapon.java | 4 +- .../electroblob/wizardry/spell/Flight.java | 4 +- .../wizardry/spell/FontOfMana.java | 4 +- .../wizardry/spell/Forcefield.java | 4 +- .../wizardry/spell/ForestOfThorns.java | 4 +- .../wizardry/spell/ForestsCurse.java | 4 +- .../electroblob/wizardry/spell/Freeze.java | 4 +- .../wizardry/spell/FreezingWeapon.java | 4 +- .../electroblob/wizardry/spell/FrostRay.java | 4 +- .../electroblob/wizardry/spell/Glide.java | 4 +- .../electroblob/wizardry/spell/Grapple.java | 4 +- .../wizardry/spell/GreaterTelekinesis.java | 4 +- .../electroblob/wizardry/spell/GroupHeal.java | 4 +- .../wizardry/spell/GrowthAura.java | 4 +- .../electroblob/wizardry/spell/HealAlly.java | 4 +- .../electroblob/wizardry/spell/IceAge.java | 4 +- .../electroblob/wizardry/spell/IceStatue.java | 4 +- .../electroblob/wizardry/spell/Ignite.java | 4 +- .../wizardry/spell/ImbueWeapon.java | 7 +- .../wizardry/spell/Intimidate.java | 4 +- .../wizardry/spell/InvigoratingPresence.java | 4 +- .../wizardry/spell/InvokeWeather.java | 4 +- .../wizardry/spell/Levitation.java | 4 +- .../electroblob/wizardry/spell/LifeDrain.java | 4 +- .../electroblob/wizardry/spell/Light.java | 7 +- .../wizardry/spell/LightningBolt.java | 4 +- .../wizardry/spell/LightningPulse.java | 7 +- .../wizardry/spell/LightningRay.java | 4 +- .../wizardry/spell/LightningWeb.java | 4 +- .../wizardry/spell/Metamorphosis.java | 4 +- .../electroblob/wizardry/spell/Meteor.java | 4 +- .../wizardry/spell/MindControl.java | 4 +- .../electroblob/wizardry/spell/MindTrick.java | 4 +- .../java/electroblob/wizardry/spell/Mine.java | 4 +- .../electroblob/wizardry/spell/Paralysis.java | 4 +- .../electroblob/wizardry/spell/Petrify.java | 4 +- .../electroblob/wizardry/spell/PhaseStep.java | 5 +- .../wizardry/spell/PlagueOfDarkness.java | 4 +- .../wizardry/spell/PocketFurnace.java | 8 +- .../wizardry/spell/PocketWorkbench.java | 4 +- .../electroblob/wizardry/spell/Poison.java | 4 +- .../wizardry/spell/Possession.java | 6 +- .../wizardry/spell/RayOfPurification.java | 4 +- .../wizardry/spell/Resurrection.java | 4 +- .../electroblob/wizardry/spell/Reversal.java | 4 +- .../electroblob/wizardry/spell/Shockwave.java | 4 +- .../wizardry/spell/ShulkerBullet.java | 4 +- .../wizardry/spell/SixthSense.java | 4 +- .../electroblob/wizardry/spell/Slime.java | 4 +- .../electroblob/wizardry/spell/Snare.java | 4 +- .../wizardry/spell/SpectralPathway.java | 4 +- .../electroblob/wizardry/spell/SpeedTime.java | 4 +- .../wizardry/spell/SpellArrow.java | 6 +- .../electroblob/wizardry/spell/SpellBuff.java | 4 +- .../wizardry/spell/SpellConjuration.java | 4 +- .../wizardry/spell/SpellConstructRanged.java | 7 +- .../wizardry/spell/SpellMinion.java | 4 +- .../electroblob/wizardry/spell/SpellRay.java | 10 +- .../wizardry/spell/SummonIronGolem.java | 4 +- .../wizardry/spell/SummonSnowGolem.java | 4 +- .../wizardry/spell/SummonSpiritHorse.java | 4 +- .../wizardry/spell/SummonSpiritWolf.java | 4 +- .../wizardry/spell/Telekinesis.java | 4 +- .../wizardry/spell/Thunderstorm.java | 4 +- .../electroblob/wizardry/spell/Tornado.java | 4 +- .../wizardry/spell/Transience.java | 4 +- .../wizardry/spell/Transportation.java | 4 +- .../wizardry/spell/VanishingBox.java | 4 +- .../wizardry/spell/WallOfFrost.java | 4 +- .../electroblob/wizardry/spell/Whirlwind.java | 4 +- .../electroblob/wizardry/spell/Wither.java | 4 +- .../wizardry/util/WizardryUtilities.java | 27 +++ .../models/item/advanced_earth_wand.json | 10 +- .../models/item/advanced_fire_wand.json | 10 +- .../models/item/advanced_healing_wand.json | 10 +- .../models/item/advanced_ice_wand.json | 10 +- .../models/item/advanced_lightning_wand.json | 10 +- .../models/item/advanced_necromancy_wand.json | 10 +- .../models/item/advanced_sorcery_wand.json | 10 +- .../ebwizardry/models/item/advanced_wand.json | 10 +- .../models/item/apprentice_earth_wand.json | 10 +- .../models/item/apprentice_fire_wand.json | 10 +- .../models/item/apprentice_healing_wand.json | 10 +- .../models/item/apprentice_ice_wand.json | 10 +- .../item/apprentice_lightning_wand.json | 10 +- .../item/apprentice_necromancy_wand.json | 10 +- .../models/item/apprentice_sorcery_wand.json | 10 +- .../models/item/apprentice_wand.json | 10 +- .../ebwizardry/models/item/festive_wand.json | 10 +- .../ebwizardry/models/item/magic_wand.json | 10 +- .../models/item/master_earth_wand.json | 10 +- .../models/item/master_fire_wand.json | 10 +- .../models/item/master_healing_wand.json | 10 +- .../models/item/master_ice_wand.json | 10 +- .../models/item/master_lightning_wand.json | 10 +- .../models/item/master_necromancy_wand.json | 10 +- .../models/item/master_sorcery_wand.json | 10 +- .../ebwizardry/models/item/master_wand.json | 10 +- .../models/item/novice_earth_wand.json | 10 +- .../models/item/novice_fire_wand.json | 10 +- .../models/item/novice_healing_wand.json | 10 +- .../models/item/novice_ice_wand.json | 10 +- .../models/item/novice_lightning_wand.json | 10 +- .../models/item/novice_necromancy_wand.json | 10 +- .../models/item/novice_sorcery_wand.json | 10 +- .../models/item/override_generators/wand.json | 11 + .../ebwizardry/models/item/wand_point.json | 25 ++ 146 files changed, 1531 insertions(+), 262 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/client/animation/ActionAnimation.java create mode 100644 src/main/java/electroblob/wizardry/client/animation/Animation.java create mode 100644 src/main/java/electroblob/wizardry/client/animation/ModelRendererExtended.java create mode 100644 src/main/java/electroblob/wizardry/client/animation/PlayerAnimator.java create mode 100644 src/main/java/electroblob/wizardry/client/model/BakedModelGenerateOverrides.java create mode 100644 src/main/java/electroblob/wizardry/client/model/ItemOverrideListMapped.java create mode 100644 src/main/java/electroblob/wizardry/item/SpellActions.java create mode 100644 src/main/resources/assets/ebwizardry/models/item/override_generators/wand.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/wand_point.json diff --git a/src/main/java/electroblob/wizardry/CommonProxy.java b/src/main/java/electroblob/wizardry/CommonProxy.java index ff568f0a..7a452957 100644 --- a/src/main/java/electroblob/wizardry/CommonProxy.java +++ b/src/main/java/electroblob/wizardry/CommonProxy.java @@ -44,6 +44,8 @@ public class CommonProxy { public void initialiseLayers(){} + public void initialiseAnimations(){} + public void registerKeyBindings(){} public net.minecraft.client.model.ModelBiped getWizardArmourModel(){ @@ -266,6 +268,14 @@ public class CommonProxy { return null; } + /** + * Returns true if the game is being viewed from the perspective of the given entity and is set to first-person + * view. Always returns false on the server side. + */ + public boolean isFirstPerson(Entity entity){ + return false; + } + /** Returns an unmodifiable set of the string keys for all of the loaded spell HUD skins. */ public Set getSpellHUDSkins(){ return null; diff --git a/src/main/java/electroblob/wizardry/Wizardry.java b/src/main/java/electroblob/wizardry/Wizardry.java index 07ce43cf..0c9d7b50 100644 --- a/src/main/java/electroblob/wizardry/Wizardry.java +++ b/src/main/java/electroblob/wizardry/Wizardry.java @@ -174,6 +174,7 @@ public class Wizardry { @EventHandler public void postInit(FMLPostInitializationEvent event){ proxy.initialiseLayers(); + proxy.initialiseAnimations(); } @EventHandler diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index d4d68860..c64b918d 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -3,6 +3,8 @@ package electroblob.wizardry.client; import electroblob.wizardry.CommonProxy; import electroblob.wizardry.Wizardry; import electroblob.wizardry.block.BlockBookshelf; +import electroblob.wizardry.client.animation.ActionAnimation; +import electroblob.wizardry.client.animation.PlayerAnimator; import electroblob.wizardry.client.audio.MovingSoundEntity; import electroblob.wizardry.client.audio.SoundLoop; import electroblob.wizardry.client.audio.SoundLoopSpell; @@ -140,7 +142,7 @@ public class ClientProxy extends CommonProxy { mixedFontRenderer = new MixedFontRenderer(Minecraft.getMinecraft().gameSettings, new ResourceLocation("textures/font/ascii.png"), Minecraft.getMinecraft().renderEngine, false); } - + @Override public void registerResourceReloadListeners(){ IResourceManager manager = Minecraft.getMinecraft().getResourceManager(); @@ -166,7 +168,7 @@ public class ClientProxy extends CommonProxy { public void setToNumberSliderEntry(Property property){ property.setConfigEntryClass(NumberSliderEntry.class); } - + @Override public void setToHUDChooserEntry(Property property){ property.setConfigEntryClass(SpellHUDSkinChooserEntry.class); @@ -182,11 +184,16 @@ public class ClientProxy extends CommonProxy { return Minecraft.getMinecraft().world; } + @Override + public boolean isFirstPerson(Entity entity){ + return entity == Minecraft.getMinecraft().getRenderViewEntity() && Minecraft.getMinecraft().gameSettings.thirdPersonView == 0; + } + @Override public void playMovingSound(Entity entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){ Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundEntity<>(entity, sound, category, volume, pitch, repeat)); } - + @Override public void playSpellSoundLoop(EntityLivingBase entity, Spell spell, SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, float pitch){ SoundLoop.addLoop(new SoundLoopSpell.SoundLoopSpellEntity(start, loop, end, spell, entity, volume, pitch)); @@ -363,7 +370,7 @@ public class ClientProxy extends CommonProxy { ParticleWizardry.registerParticle(Type.SUMMON, ParticleSummon::new); ParticleWizardry.registerParticle(Type.VINE, ParticleVine::new); } - + @Override public ParticleWizardry createParticle(ResourceLocation type, World world, double x, double y, double z){ IWizardryParticleFactory factory = factories.get(type); @@ -483,36 +490,36 @@ public class ClientProxy extends CommonProxy { Wizardry.logger.warn("Recieved a PacketNPCCastSpell, but the caster ID was not the ID of an EntityLiving"); } } - + @Override public void handleDispenserCastSpellPacket(PacketDispenserCastSpell.Message message){ - + World world = Minecraft.getMinecraft().world; - + if(world.getTileEntity(message.pos) instanceof TileEntityDispenser){ // Should always be true - + Spell spell = Spell.byNetworkID(message.spellID); - + spell.cast(world, message.x, message.y, message.z, message.direction, 0, -1, message.modifiers); // No need to check if the spell succeeded, because the packet is only ever sent when it succeeds. MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.DISPENSER, spell, world, message.x, message.y, message.z, message.direction, message.modifiers)); - + if(spell.isContinuous || spell instanceof None){ - + DispenserCastingData data = DispenserCastingData.get((TileEntityDispenser)world.getTileEntity(message.pos)); - + if(spell.isContinuous){ data.startCasting(spell, message.x, message.y, message.z, message.duration, message.modifiers); }else{ data.stopCasting(); } } - + }else{ Wizardry.logger.warn("Recieved a PacketDispenserCastSpell, but no tileEntity was found at the supplied location."); } - + } @Override @@ -682,6 +689,12 @@ public class ClientProxy extends CommonProxy { LayerFrost.initialiseLayers(); } + @Override + public void initialiseAnimations(){ + PlayerAnimator.init(); + ActionAnimation.register(); + } + @Override public void registerRenderers(){ diff --git a/src/main/java/electroblob/wizardry/client/animation/ActionAnimation.java b/src/main/java/electroblob/wizardry/client/animation/ActionAnimation.java new file mode 100644 index 00000000..c6eeeeb8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/animation/ActionAnimation.java @@ -0,0 +1,154 @@ +package electroblob.wizardry.client.animation; + +import electroblob.wizardry.data.WizardData; +import electroblob.wizardry.item.SpellActions; +import electroblob.wizardry.spell.Grapple; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.model.ModelBiped; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHandSide; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; + +/** + * An animation that is associated with a particular {@link net.minecraft.item.EnumAction}. This animation will display + * when a player is using an item with that action. + * @author Electroblob + * @since Wizardry 4.3 + * @see electroblob.wizardry.item.SpellActions + */ +public abstract class ActionAnimation extends Animation { + + private final EnumAction action; + + public ActionAnimation(EnumAction action){ + super(action.name()); + this.action = action; + } + + @Override + public boolean shouldDisplay(EntityPlayer player, boolean firstPerson){ + return !firstPerson && player.getItemInUseCount() > 0 && player.getActiveItemStack().getItemUseAction() == action; + } + + /** Called from the client proxy to register all of wizardry's action animations. */ + public static void register(){ + + PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT){ + @Override + public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){ + // Something (probably some sort of race condition) causes getActiveItemStack to sometimes be wrong + if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return; + + EnumHandSide side = WizardryUtilities.getSideForHand(player, player.getActiveHand()); + + float pitch = (float)Math.toRadians(player.prevRotationPitch + (player.rotationPitch - player.prevRotationPitch) * partialTicks); + float yaw = (float)Math.toRadians(player.prevRotationYawHead + (player.rotationYawHead - player.prevRotationYawHead) * partialTicks) + - (float)Math.toRadians(player.prevRenderYawOffset + (player.renderYawOffset - player.prevRenderYawOffset) * partialTicks); + + float x = -((float)Math.PI / 2) + pitch + 0.2f; + float y = (side == EnumHandSide.RIGHT ? -0.25f : 0.25f) + yaw; + + ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side); + arm.setRotation(x, y, 0); + } + }); + + PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT_UP){ + @Override + public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){ + if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return; + EnumHandSide side = WizardryUtilities.getSideForHand(player, player.getActiveHand()); + ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side); + arm.addRotation(-2.2f, side == EnumHandSide.RIGHT ? 0.2f : -0.2f, 0); + } + }); + + PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT_DOWN){ + @Override + public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){ + float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all! + float y = Math.min(0.4f + tick * 0.05f, 0.7f); + EnumHandSide side = WizardryUtilities.getSideForHand(player, player.getActiveHand()); + ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side); + arm.addRotation(-0.2f, side == EnumHandSide.RIGHT ? y : -y, 0); + } + }); + + PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.SUMMON){ + @Override + public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){ + float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all! + float x = -Math.min(0.4f + tick * 0.2f, 2f); + ((ModelRendererExtended)getArmForSide(model, EnumHandSide.RIGHT)).addRotation(x, 1.2f, 0); + ((ModelRendererExtended)getArmForSide(model, EnumHandSide.LEFT)).addRotation(x, -1.2f, 0); + } + }); + + PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.GRAPPLE){ + @Override + public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){ + + WizardData data = WizardData.get(player); + RayTraceResult hit = data.getVariable(Grapple.TARGET_KEY); + if(hit == null || hit.typeOfHit == RayTraceResult.Type.MISS) return; + Vec3d target = hit.hitVec; + + if(hit.entityHit instanceof EntityLivingBase){ + // If the target is an entity, we need to use the entity's centre rather than the original hit position + // because the entity will have moved! + target = new Vec3d(hit.entityHit.posX, hit.entityHit.getEntityBoundingBox().minY + hit.entityHit.height/2, hit.entityHit.posZ); + } + + EnumHandSide side = WizardryUtilities.getSideForHand(player, player.getActiveHand()); + + ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side); + + Vec3d direction = target.subtract(player.getPositionEyes(partialTicks)); + float yaw = player.prevRenderYawOffset + (player.renderYawOffset - player.prevRenderYawOffset) * partialTicks; + + float pitch = (float)MathHelper.atan2(MathHelper.sqrt(direction.x*direction.x + direction.z*direction.z), direction.y); + float x = pitch - (float)Math.PI * 0.9f; + float y = -(float)Math.toRadians(yaw) - (float)MathHelper.atan2(direction.x, direction.z); + y += (side == EnumHandSide.RIGHT ? -0.25f : 0.25f); + if(Math.abs(pitch) < 0.2f) y = arm.rotateAngleY; + + arm.setRotation(x, y, 0); + } + }); + + PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.IMBUE){ + @Override + public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){ + float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all! + float z = Math.max(1.5f - tick * 0.1f, 0.8f); + EnumHandSide side = WizardryUtilities.getSideForHand(player, player.getActiveHand()); + ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side); + arm.addRotation(-1.2f, side == EnumHandSide.RIGHT ? -0.2f : 0.2f, side == EnumHandSide.RIGHT ? z : -z); + if(!player.getHeldItem(WizardryUtilities.getHandForSide(player, side.opposite())).isEmpty()){ + ((ModelRendererExtended)getArmForSide(model, side.opposite())).addRotation(-0.8f, side == EnumHandSide.LEFT ? 0.3f : -0.3f, 0); + } + } + }); + + PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.THRUST){ + @Override + public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){ + if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return; + EnumHandSide side = WizardryUtilities.getSideForHand(player, player.getActiveHand()); + ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side); + float y = side == EnumHandSide.RIGHT ? -0.6f : 0.6f; + arm.addRotation(-1.2f, y, 0); + if(player.getHeldItem(WizardryUtilities.getHandForSide(player, side.opposite())).isEmpty()){ + ModelRendererExtended otherArm = (ModelRendererExtended)getArmForSide(model, side.opposite()); + otherArm.setRotation(arm.rotateAngleX - 1.2f, -arm.rotateAngleY - y, otherArm.rotateAngleZ); + } + } + }); + + } + +} diff --git a/src/main/java/electroblob/wizardry/client/animation/Animation.java b/src/main/java/electroblob/wizardry/client/animation/Animation.java new file mode 100644 index 00000000..71be8a42 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/animation/Animation.java @@ -0,0 +1,77 @@ +package electroblob.wizardry.client.animation; + +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumHandSide; + +/** + * Represents a player animation. Use one of the predefined subclasses below for common animation types, or extend this + * class directly to define a completely custom animation. Register instances of this class using + * {@link PlayerAnimator#registerAnimation(Animation)}. + * @author Electroblob + * @since Wizardry 4.3 + * @see ActionAnimation + */ +public abstract class Animation { + + private final String name; + + public Animation(String name){ + this.name = name; + } + + /** Returns the name of this animation, currently only used for warning messages / debugging. */ + public String getName(){ + return name; + } + + /** + * Returns whether this animation should be displayed for the given player. Note that if two registered + * animations have overlapping conditions, both will attempt to display simultaneously, so if they both animate the + * same model part, that part may not behave as expected. + * @param player The player being animated + * @param firstPerson True if the player being animated is the local client player, and they are in first-person + * view. In first-person, animations are only useful for animating the player's empty hand. + * @return True if the animation should be displayed, false if not. + */ + public abstract boolean shouldDisplay(EntityPlayer player, boolean firstPerson); + + /** + * Sets the rotation of the model parts for this animation. This method is called every time the player is rendered + * when {@link Animation#shouldDisplay(EntityPlayer, boolean)} returns true. + * @param player The player being animated + * @param model The model to animate. All of the standard {@link ModelBiped} parts will already be wrapped and may + * be safely cast to {@code ModelRendererExtended} in order to override the rotations set by the model + * itself - see {@link ModelRendererExtended ModelRendererExtended} for details. + * @param partialTicks The current partial tick time + * @param firstPerson True if the player being animated is the local client player, and they are in first-person + * view. In first-person, animations are only useful for animating the player's empty hand. + */ + public abstract void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson); + + /** + * Returns whether the boxes that form the second layer of the player's skin (for models that are instances of + * {@link net.minecraft.client.model.ModelPlayer ModelPlayer}) should automatically be set to the same angles as + * their corresponding first-layer parts. + * @param player The player being rendered, for reference + * @param firstPerson True if the player being animated is the local client player, and they are in first-person + * view. In first-person, animations are only useful for animating the player's empty hand. + * @return True to let {@link PlayerAnimator} auto-rotate the second skin layer, false to rotate them manually. + */ + public boolean autoRotateSecondLayer(EntityPlayer player, boolean firstPerson){ + return true; + } + + /** + * Returns the arm of the given model corresponding to the given side. Function is identical to the method of the + * same name in {@link ModelBiped}. + * @param model The model to get the arm of + * @param side The {@link EnumHandSide} to return the arm for + * @return The {@link ModelRenderer} corresponding to the given arm + */ + public static ModelRenderer getArmForSide(ModelBiped model, EnumHandSide side){ + return side == EnumHandSide.LEFT ? model.bipedLeftArm : model.bipedRightArm; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/animation/ModelRendererExtended.java b/src/main/java/electroblob/wizardry/client/animation/ModelRendererExtended.java new file mode 100644 index 00000000..ba6cc2cb --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/animation/ModelRendererExtended.java @@ -0,0 +1,177 @@ +package electroblob.wizardry.client.animation; + +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.model.ModelPlayer; +import net.minecraft.client.model.ModelRenderer; + +/** + * A wrapper around {@link ModelRenderer} which can be swapped into models to allow render pre/post events to + * circumvent {@code ModelBase.setRotationAngles(...)} and set model part rotations themselves. Instances of this class + * keep a reference to the original {@code ModelRenderer} and delegate all the actual rendering to it. + *

+ * {@link PlayerAnimator PlayerAnimator} does all the necessary setup with this + * class for player animations. It is also possible to set up custom animations for non-player entities with this class, + * see {@link ModelRendererExtended#wrap(ModelBiped)} and {@link ModelRendererExtended#wrap(ModelBase, ModelRenderer)}. + * @author Electroblob + * @since Wizardry 4.3 + */ +public class ModelRendererExtended extends ModelRenderer { + + // We probably could have copied all the fields over instead, but this way if anyone else modifies the boxes we + // won't overwrite their changes + // This way also avoids reflection because we don't need to copy private fields, which is better for performance + private ModelRenderer delegate; + + private float actualRotationX; + private float actualRotationY; + private float actualRotationZ; + + private float extraRotationX; + private float extraRotationY; + private float extraRotationZ; + + private ModelRendererExtended(ModelBase baseModel, ModelRenderer delegate){ + super(baseModel, delegate.boxName); + this.delegate = delegate; + // Copy over any public fields that might get changed + this.textureWidth = delegate.textureWidth; + this.textureHeight = delegate.textureHeight; + this.showModel = delegate.showModel; + this.isHidden = delegate.isHidden; + this.rotationPointX = delegate.rotationPointX; + this.rotationPointY = delegate.rotationPointY; + this.rotationPointZ = delegate.rotationPointZ; + this.resetRotation(); + } + + // Static initialisers + + /** + * Replaces the given part of the given {@link ModelBase} with a new {@code ModelRendererExtended} that wraps the + * original part. This method takes care of modifying the model's box list, but is unable to change any + * individual {@link ModelRenderer} fields - this method instead returns the resulting + * {@code ModelRendererExtended} to be assigned to the appropriate field. + * @param model The model whose part is to be wrapped + * @param box The part of the model to wrap, must belong to the above model + * @return The resulting wrapped model part, this should be assigned to the appropriate part field in the model. If + * the given model part was already wrapped, this method simply returns it unchanged. + * @throws IllegalArgumentException if the given box does not belong to the given model + */ + public static ModelRendererExtended wrap(ModelBase model, ModelRenderer box){ + if(box instanceof ModelRendererExtended) return (ModelRendererExtended)box; // Ignore already-wrapped parts + ModelRendererExtended wrapper = new ModelRendererExtended(model, box); + int index = model.boxList.indexOf(box); + if(index < 0) throw new IllegalArgumentException(String.format("The given ModelRenderer %s does not belong to the given model %s", model, box)); + model.boxList.set(index, wrapper); // I doubt the order matters but we may as well put it at the same index + return wrapper; + } + + /** + * Replaces the given {@link ModelBiped}'s parts with new {@code ModelRendererExtended} versions that wrap the + * original parts. If the given model is a {@link ModelPlayer}, the extra boxes for player skin overlays will also + * be wrapped. + */ + public static void wrap(ModelBiped model){ + // MMmmmmmm wraps + model.bipedHead = wrap(model, model.bipedHead); + model.bipedBody = wrap(model, model.bipedBody); + model.bipedRightArm = wrap(model, model.bipedRightArm); + model.bipedLeftArm = wrap(model, model.bipedLeftArm); + model.bipedRightLeg = wrap(model, model.bipedRightLeg); + model.bipedLeftLeg = wrap(model, model.bipedLeftLeg); + model.bipedHeadwear = wrap(model, model.bipedHeadwear); + + if(model instanceof ModelPlayer){ + ((ModelPlayer)model).bipedBodyWear = wrap(model, ((ModelPlayer)model).bipedBodyWear); + ((ModelPlayer)model).bipedRightArmwear = wrap(model, ((ModelPlayer)model).bipedRightArmwear); + ((ModelPlayer)model).bipedLeftArmwear = wrap(model, ((ModelPlayer)model).bipedLeftArmwear); + ((ModelPlayer)model).bipedRightLegwear = wrap(model, ((ModelPlayer)model).bipedRightLegwear); + ((ModelPlayer)model).bipedLeftLegwear = wrap(model, ((ModelPlayer)model).bipedLeftLegwear); + } + } + + /** Resets the rotation of this model part to the angle set by the parent model. */ + public void resetRotation(){ + this.actualRotationX = Float.NaN; + this.actualRotationY = Float.NaN; + this.actualRotationZ = Float.NaN; + this.extraRotationX = 0; + this.extraRotationY = 0; + this.extraRotationZ = 0; + } + + /** Sets the rotation of this model part, which will overwrite the angle set by the parent model. */ + public void setRotation(float x, float y, float z){ + this.actualRotationX = x; + this.actualRotationY = y; + this.actualRotationZ = z; + } + + /** Sets the extra rotation of this model part, which will be added onto the angle set by the parent model. */ + public void addRotation(float x, float y, float z){ + this.extraRotationX = x; + this.extraRotationY = y; + this.extraRotationZ = z; + } + + /** Sets the rotation of this model part to the same values as the given box. */ + public void setRotationTo(ModelRendererExtended box){ + this.actualRotationX = box.actualRotationX; + this.actualRotationY = box.actualRotationY; + this.actualRotationZ = box.actualRotationZ; + this.extraRotationX = box.extraRotationX; + this.extraRotationY = box.extraRotationY; + this.extraRotationZ = box.extraRotationZ; + } + + // Delegate rendering, but fiddle with the angles first + + @Override + public void render(float scale){ + + // Need to copy these over each time in case they were changed + delegate.showModel = this.showModel; + delegate.isHidden = this.isHidden; + delegate.rotationPointX = this.rotationPointX; + delegate.rotationPointY = this.rotationPointY; + delegate.rotationPointZ = this.rotationPointZ; + + if(!Float.isNaN(actualRotationX) && !Float.isNaN(actualRotationY) && !Float.isNaN(actualRotationZ)){ + delegate.rotateAngleX = actualRotationX; + delegate.rotateAngleY = actualRotationY; + delegate.rotateAngleZ = actualRotationZ; + }else{ + delegate.rotateAngleX = this.rotateAngleX + extraRotationX; + delegate.rotateAngleY = this.rotateAngleY + extraRotationY; + delegate.rotateAngleZ = this.rotateAngleZ + extraRotationZ; + } + + delegate.render(scale); + } + + // Delegate all other methods + + @Override + public void addChild(ModelRenderer renderer){ + delegate.addChild(renderer); + } + + @Override + public void renderWithRotation(float scale){ + delegate.renderWithRotation(scale); + } + + @Override + public void postRender(float scale){ + // TODO: It may just be easier to hardcode the item rotation part of the animation as well + // Exactly the same setup as above, just add item rotation/translation fields and setters +// float angle = 1; +// float radius = 10; +// delegate.rotationPointY -= radius * MathHelper.cos(angle); +// delegate.rotationPointZ -= radius * MathHelper.sin(angle); +// delegate.rotateAngleX += angle; + delegate.postRender(scale); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/animation/PlayerAnimator.java b/src/main/java/electroblob/wizardry/client/animation/PlayerAnimator.java new file mode 100644 index 00000000..71ea7e57 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/animation/PlayerAnimator.java @@ -0,0 +1,213 @@ +package electroblob.wizardry.client.animation; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.model.ModelPlayer; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderLivingBase; +import net.minecraft.client.renderer.entity.RenderPlayer; +import net.minecraft.client.renderer.entity.layers.LayerBipedArmor; +import net.minecraft.client.renderer.entity.layers.LayerRenderer; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumHandSide; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.event.RenderPlayerEvent; +import net.minecraftforge.client.event.RenderSpecificHandEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.ObfuscationReflectionHelper; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.Side; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Handles the setup and rendering events for custom player animations, as well as registering of animations. Addons + * can also register their own animations here, see {@link PlayerAnimator#registerAnimation(Animation)}. + * @author Electroblob + * @since Wizardry 4.3 + * @see electroblob.wizardry.item.SpellActions SpellActions + */ +@Mod.EventBusSubscriber(Side.CLIENT) +public class PlayerAnimator { + + // TODO: Attempt to extend this to all bipeds + + private static final List animations = new ArrayList<>(); + + /** Stores all registered layer renderers for lazy-loading of armour models later. */ + private static final Map>> playerLayers = new HashMap<>(); + /** Stores the main model for each renderer plus all models for registered layer renderers. */ + private static final Map> playerLayerModels = new HashMap<>(); + /** Reflected into {@link RenderLivingBase}{@code #layerRenderers}. */ + private static final Field layerRenderers; + + static { + layerRenderers = ObfuscationReflectionHelper.findField(RenderLivingBase.class, "field_177097_h"); + } + + /** + * Registers a new player animation. This method should be called from the {@code init()} phase via a client proxy. + * @param animation The animation to register + */ + // N.B. This can (and will) be called before PlayerAnimator#init() is called + public static void registerAnimation(Animation animation){ + if(animations.contains(animation)){ + Wizardry.logger.warn("Animation {} is already registered!", animation.getName()); + }else{ + animations.add(animation); + } + } + + // TODO: Figure out if calling this from postInit ensures we catch all the layers, and lazy-load it if not + @SuppressWarnings("unchecked") + public static void init(){ + + for(RenderPlayer renderer : Minecraft.getMinecraft().getRenderManager().getSkinMap().values()){ + + List models = new ArrayList<>(); + ModelRendererExtended.wrap(renderer.getMainModel()); + models.add(renderer.getMainModel()); + + try { + + List> layers = (List>)layerRenderers.get(renderer); + + playerLayers.put(renderer, layers); + + for(LayerRenderer layer : layers){ + + for(Field field : WizardryUtilities.getAllFields(layer.getClass())){ + + field.setAccessible(true); + + // If your layer model doesn't extend ModelBiped, you DESERVE to be incompatible! + // (Just kidding... there's nothing I can do about it anyway) + if(field.get(layer) instanceof ModelBiped){ + ModelBiped model = (ModelBiped)field.get(layer); + ModelRendererExtended.wrap(model); + models.add(model); + } + } + + } + + }catch(IllegalAccessException e){ + Wizardry.logger.error("Error during reflective access of render layers: ", e); + } + + playerLayerModels.put(renderer, models); + + } + } + + private static void updateModels(EntityPlayer player, Render renderer, float partialTicks, boolean firstPerson){ + + // Biped armour is special because of Forge's armour item render hook + // This needs to be lazy-loaded because we need access to an actual item + for(LayerRenderer layer : playerLayers.get(renderer)){ + if(layer instanceof LayerBipedArmor){ + for(EntityEquipmentSlot slot : WizardryUtilities.ARMOUR_SLOTS){ + + ItemStack armour = player.getItemStackFromSlot(slot); + ModelBiped model = ForgeHooksClient.getArmorModel(player, armour, slot, ((LayerBipedArmor)layer).getModelFromSlot(slot)); + + List models = playerLayerModels.get(renderer); + + if(!models.contains(model)){ + models.add(model); + ModelRendererExtended.wrap(model); + } + } + } + } + + boolean flag = false; + + for(Animation animation : animations){ + + boolean autoRotateSecondLayer = animation.autoRotateSecondLayer(player, firstPerson); + + if(animation.shouldDisplay(player, firstPerson)){ + + flag = true; + + for(ModelBiped model : playerLayerModels.get(renderer)){ + + animation.setRotationAngles(player, model, partialTicks, firstPerson); + + if(autoRotateSecondLayer && model instanceof ModelPlayer){ + alignSecondLayer((ModelPlayer)model); + } + } + } + } + + if(!flag){ + for(ModelBiped model : playerLayerModels.get(renderer)){ + for(ModelRenderer box : model.boxList){ // For ModelPlayer, this will include the second layer + // Some models have extra boxes, they (probably) don't need wrapping but we need this check! + if(box instanceof ModelRendererExtended){ + ((ModelRendererExtended)box).resetRotation(); + } + } + } + } + + } + + /** Rotates the second layer boxes of the given (wrapped) player model to match the first layer. */ + public static void alignSecondLayer(ModelPlayer model){ + ((ModelRendererExtended)model.bipedBodyWear) .setRotationTo((ModelRendererExtended)model.bipedBody); + ((ModelRendererExtended)model.bipedRightArmwear).setRotationTo((ModelRendererExtended)model.bipedRightArm); + ((ModelRendererExtended)model.bipedLeftArmwear) .setRotationTo((ModelRendererExtended)model.bipedLeftArm); + ((ModelRendererExtended)model.bipedRightLegwear).setRotationTo((ModelRendererExtended)model.bipedBody); + ((ModelRendererExtended)model.bipedLeftLegwear) .setRotationTo((ModelRendererExtended)model.bipedBody); + } + + @SubscribeEvent + public static void onRenderHandEvent(RenderSpecificHandEvent event){ + + AbstractClientPlayer player = Minecraft.getMinecraft().player; + + EnumAction action = event.getItemStack().getItemUseAction(); + + if(player.isHandActive() && player.getActiveHand() == event.getHand() && SpellActions.getSpellActions().contains(action)){ + // Minecraft's item renderer helpfully doesn't have a default case for the usage action so it doesn't do any + // transformations at all (which is VERY ANNOYING!) - the following lines are from ItemRenderer#transformSideFirstPerson + int i = (player.getPrimaryHand() == EnumHandSide.RIGHT) == (event.getHand() == EnumHand.MAIN_HAND) ? 1 : -1; + GlStateManager.translate((float)i * 0.56F, -0.52F + event.getEquipProgress() * -0.6F, -0.72F); + } + + updateModels(player, Minecraft.getMinecraft().getRenderManager().getEntityRenderObject(player), event.getPartialTicks(), true); + } + + @SubscribeEvent + public static void onRenderPlayerPreEvent(RenderPlayerEvent.Pre event){ + +// boolean firstPerson = event.getEntityPlayer() == Minecraft.getMinecraft().player +// && Minecraft.getMinecraft().gameSettings.thirdPersonView == 0; + + updateModels(event.getEntityPlayer(), event.getRenderer(), event.getPartialRenderTick(), false); + } + +// @SubscribeEvent +// public static void onRenderPlayerPostEvent(RenderPlayerEvent.Post event){ + // TODO: Would we ever need to unwrap the models? +// } + +} diff --git a/src/main/java/electroblob/wizardry/client/model/BakedModelGenerateOverrides.java b/src/main/java/electroblob/wizardry/client/model/BakedModelGenerateOverrides.java new file mode 100644 index 00000000..32d08e4a --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/BakedModelGenerateOverrides.java @@ -0,0 +1,163 @@ +package electroblob.wizardry.client.model; + +import com.google.common.collect.ImmutableMap; +import electroblob.wizardry.Wizardry; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.renderer.block.model.*; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.event.ModelBakeEvent; +import net.minecraftforge.client.model.IModel; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.client.model.ModelLoaderRegistry; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.Side; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nullable; +import javax.vecmath.Matrix4f; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A special type of model that generates override models dynamically. This allows the same overrides to be applied to + * multiple item models without needing an override version of each one just to point to a texture. + *

+ * To use this system in a model file:

+ * 1. Make a base model file and define the item overrides in it. This model must be placed in the folder + * {@code models/item/override_generators} (unfortunately the folder has to be predefined, see below).
+ * 2. Make the model files your overrides point to. These should define everything about the model except textures.
+ * 3. In each model that should generate overrides dynamically, define a single override that points to the location of + * the model from step 1 (the predicate doesn't matter).
+ *

+ * It is advisable to make the parent of the override model (from step 2) the same as the parent of the model to + * be overridden, so that they share any necessary properties and texture variables. For example, both + * {@code ebwizardry:item/magic_wand.json} and {@code ebwizardry:item/wand_point.json} have {@code item/handheld} as + * their parent. The override model will reference the textures in the original model, so if the override defines a + * face with the texture {@code "#face"}, the texture used will be the file associated with {@code "face"} in the + * original model. For simple models, this will just be the standard {@code "layer0"}. + * @since Wizardry 4.3 + * @author Electroblob + */ +// Okay, it's not *that* bad if you only have one override, but imagine having to duplicate every single wand model 5 +// times if we wanted 5 animations... +// The nice thing is resource packs don't lose any flexibility this way, they can still redefine the models themselves +// and specify custom models for the overrides if they want, or they can keep using this system - in fact, they GAIN +// flexibility because they can also overwrite the base wand.json model and specify global overrides too +@Mod.EventBusSubscriber(Side.CLIENT) +public class BakedModelGenerateOverrides implements IBakedModel { + + // I tried having this as a prefix that gets removed at runtime, which worked fine but it spammed the log with + // errors because the prefixed file doesn't actually exist, so even though it's never used the game still complains + private static final String OVERRIDE_GENERATORS = "override_generators"; + + private final IBakedModel delegate; + private final ItemOverrideList overrides; + + public BakedModelGenerateOverrides(IBakedModel delegate, ItemOverrideList overrides){ + this.delegate = delegate; + this.overrides = overrides; + } + + @Override + public ItemOverrideList getOverrides(){ + return overrides; // The only thing that's not delegated + } + + // Delegate everything else + + @Override + public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand){ + return delegate.getQuads(state, side, rand); + } + + @Override + public boolean isAmbientOcclusion(){ + return delegate.isAmbientOcclusion(); + } + + @Override + public boolean isGui3d(){ + return delegate.isGui3d(); + } + + @Override + public boolean isBuiltInRenderer(){ + return delegate.isBuiltInRenderer(); + } + + @Override + public TextureAtlasSprite getParticleTexture(){ + return delegate.getParticleTexture(); + } + + @Override + public ItemCameraTransforms getItemCameraTransforms(){ + return delegate.getItemCameraTransforms(); + } + + @Override + public boolean isAmbientOcclusion(IBlockState state){ + return delegate.isAmbientOcclusion(state); + } + + @Override + public Pair handlePerspective(ItemCameraTransforms.TransformType cameraTransformType){ + return delegate.handlePerspective(cameraTransformType); + } + + // Baking + + @SubscribeEvent + public static void bake(ModelBakeEvent event){ + + for(ModelResourceLocation location : event.getModelRegistry().getKeys()){ + + IBakedModel original = event.getModelRegistry().getObject(location); + + if(original != null){ + original.getOverrides().getOverrides().stream().map(ItemOverride::getLocation) + .filter(l -> l.getPath().contains(OVERRIDE_GENERATORS)).findFirst() + .ifPresent(l -> event.getModelRegistry().putObject(location, + substituteWandModel(event.getModelManager(), location, original, l))); + } + } + } + + // We have to use ModelBakeEvent for this because we need the original model to be baked with the vanilla system + private static IBakedModel substituteWandModel(ModelManager modelManager, ModelResourceLocation location, IBakedModel original, ResourceLocation overrideLocation){ + + try { + + IModel unbakedOriginal = ModelLoaderRegistry.getModel(location); + IModel referenceModel = ModelLoaderRegistry.getModel(overrideLocation); + ModelBlock vanillaOriginal = unbakedOriginal.asVanillaModel().orElse(null); + ModelBlock vanillaRefModel = referenceModel.asVanillaModel().orElse(null); + + if(vanillaOriginal != null && vanillaRefModel != null){ + + // So... many... data structures... + List overrides = vanillaRefModel.getOverrides(); + Map replacementMap = new HashMap<>(); + + for(ItemOverride override : overrides){ + IModel replacement = ModelLoaderRegistry.getModel(override.getLocation()).retexture(ImmutableMap.copyOf(vanillaOriginal.textures)); + replacementMap.put(override.getLocation(), replacement.bake(unbakedOriginal.getDefaultState(), DefaultVertexFormats.ITEM, ModelLoader.defaultTextureGetter())); + } + + ItemOverrideListMapped overrideList = new ItemOverrideListMapped(overrides, replacementMap); + return new BakedModelGenerateOverrides(original, overrideList); + } + + }catch(Exception exception){ + Wizardry.logger.error("Error baking item display override models: ", exception); + } + + return modelManager.getMissingModel(); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/model/ItemOverrideListMapped.java b/src/main/java/electroblob/wizardry/client/model/ItemOverrideListMapped.java new file mode 100644 index 00000000..2e8837bc --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ItemOverrideListMapped.java @@ -0,0 +1,42 @@ +package electroblob.wizardry.client.model; + +import com.google.common.collect.ImmutableMap; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.client.renderer.block.model.ItemOverride; +import net.minecraft.client.renderer.block.model.ItemOverrideList; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; + +/** A version of {@link ItemOverrideList} that uses a predefined map to substitute a specific baked model depending on + * the location the currently-applicable override points to. */ +public class ItemOverrideListMapped extends ItemOverrideList { + + private final Map replacementMap; + + public ItemOverrideListMapped(List overrides, Map replacementMap){ + super(overrides); + this.replacementMap = ImmutableMap.copyOf(replacementMap); + } + + @Override + public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity){ + + if(!stack.isEmpty() && stack.getItem().hasCustomProperties()){ + + // Get the location the original override points to... + ResourceLocation location = applyOverride(stack, world, entity); // I wonder why this is deprecated? + + if(location != null){ + return replacementMap.get(location); // ... then substitute in the corresponding generated model + } + } + + return originalModel; + } +} diff --git a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java index eae2ecc9..5dc88bc8 100644 --- a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java +++ b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java @@ -308,11 +308,11 @@ public final class WizardryModels { if(location.getNamespace().equals(Wizardry.MODID)){ + IBakedModel original = event.getModelRegistry().getObject(location); + if(location.getPath().contains("runestone") || location.getPath().contains("runestone_pedestal")){ - IBakedModel original = event.getModelRegistry().getObject(location); event.getModelRegistry().putObject(location, new BakedModelGlowingOverlay(original, "overlay")); }else if(location.getPath().contains("spectral_block")){ - IBakedModel original = event.getModelRegistry().getObject(location); event.getModelRegistry().putObject(location, new BakedModelGlowingOverlay(original, "spectral_block")); } } diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleTargeted.java b/src/main/java/electroblob/wizardry/client/particle/ParticleTargeted.java index cd89d0af..b7942a75 100644 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleTargeted.java +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleTargeted.java @@ -1,6 +1,7 @@ package electroblob.wizardry.client.particle; import electroblob.wizardry.Wizardry; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.Tessellator; @@ -15,6 +16,8 @@ import javax.annotation.Nullable; /** Superclass for particles with a second target entity or target position. */ public abstract class ParticleTargeted extends ParticleWizardry { + private static final double THIRD_PERSON_AXIAL_OFFSET = 1.2; + protected double targetX; protected double targetY; protected double targetZ; @@ -79,6 +82,16 @@ public abstract class ParticleTargeted extends ParticleWizardry { float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks); float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks); + // Translates the particle a short distance in front of the entity + if(this.entity != null && this.shouldApplyOriginOffset()){ // TODO: Replace with a protected boolean method + if(this.entity != viewer || Minecraft.getMinecraft().gameSettings.thirdPersonView != 0){ + Vec3d look = entity.getLook(partialTicks).scale(THIRD_PERSON_AXIAL_OFFSET); + x += look.x; + y += look.y; + z += look.z; + } + } + if(this.target != null){ this.targetX = this.target.prevPosX + (this.target.posX - this.target.prevPosX) * partialTicks; @@ -133,6 +146,12 @@ public abstract class ParticleTargeted extends ParticleWizardry { GlStateManager.popMatrix(); } + /** Returns whether the origin of this particle should be moved a short distance in front of the entity it is + * linked to, if any. */ + protected boolean shouldApplyOriginOffset(){ + return true; + } + /** Called from {@link ParticleTargeted#renderParticle(BufferBuilder, Entity, float, float, float, float, float, float)}, * once the appropriate calculations and transformations have been applied, to actually render the particle. Subclasses * override this instead of overriding {@code renderParticle} directly, and inside render the particle along diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleVine.java b/src/main/java/electroblob/wizardry/client/particle/ParticleVine.java index 79c9d872..21f336f2 100644 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleVine.java +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleVine.java @@ -46,6 +46,11 @@ public class ParticleVine extends ParticleTargeted { buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP); } + @Override + protected boolean shouldApplyOriginOffset(){ + return false; + } + @Override protected void draw(Tessellator tessellator, double length, float partialTicks){ diff --git a/src/main/java/electroblob/wizardry/item/ItemWand.java b/src/main/java/electroblob/wizardry/item/ItemWand.java index 8c80cb75..71c00ecb 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWand.java +++ b/src/main/java/electroblob/wizardry/item/ItemWand.java @@ -28,10 +28,7 @@ import net.minecraft.inventory.Slot; import net.minecraft.item.EnumAction; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.util.ActionResult; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumHand; -import net.minecraft.util.SoundCategory; +import net.minecraft.util.*; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; @@ -91,6 +88,14 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem, this.element = element; setMaxDamage(this.tier.maxCharge); WizardryRecipes.addToManaFlaskCharging(this); + // TODO: Hook to allow addon devs to have this override apply to their own animations + addPropertyOverride(new ResourceLocation("pointing"), + (s, w, e) -> e != null && e.getActiveItemStack() == s + && (s.getItemUseAction() == SpellActions.POINT + || s.getItemUseAction() == SpellActions.POINT_UP + || s.getItemUseAction() == SpellActions.POINT_DOWN + || s.getItemUseAction() == SpellActions.GRAPPLE + || s.getItemUseAction() == SpellActions.SUMMON) ? 1 : 0); } @Override @@ -381,6 +386,8 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem, Spell spell = WandHelper.getCurrentSpell(stack); + if(!spell.isContinuous) return; + SpellModifiers modifiers; if(WizardData.get(player) != null){ @@ -393,7 +400,7 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem, // Continuous spells (these must check if they can be cast each tick since the mana changes) // Don't call canCast when castingTick == 0 because we already did it in onItemRightClick - if(spell.isContinuous && (castingTick == 0 || canCast(stack, spell, player, player.getActiveHand(), castingTick, modifiers))){ + if(castingTick == 0 || canCast(stack, spell, player, player.getActiveHand(), castingTick, modifiers)){ cast(stack, spell, player, player.getActiveHand(), castingTick, modifiers); }else{ // Stops the casting if it was interrupted, either by events or because the wand ran out of mana diff --git a/src/main/java/electroblob/wizardry/item/SpellActions.java b/src/main/java/electroblob/wizardry/item/SpellActions.java new file mode 100644 index 00000000..fe8aa7c0 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/SpellActions.java @@ -0,0 +1,63 @@ +package electroblob.wizardry.item; + +import electroblob.wizardry.Wizardry; +import net.minecraft.item.EnumAction; +import net.minecraftforge.common.util.EnumHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Defines and stores wizardry's custom {@link EnumAction}s. + * @author Electroblob + * @since Wizardry 4.3 + */ +public final class SpellActions { + + private static final List spellActions = new ArrayList<>(); + + /** An {@link EnumAction} that causes the player to point in the direction they are looking. */ + public static final EnumAction POINT = createAction("point"); + /** An {@link EnumAction} that causes the player to point directly upwards. */ + public static final EnumAction POINT_UP = createAction("point_up"); + /** An {@link EnumAction} that causes the player to point down towards the ground. */ + public static final EnumAction POINT_DOWN = createAction("point_down"); + /** An {@link EnumAction} that causes the player to stretch both arms out and up slightly, as if summoning. */ + public static final EnumAction SUMMON = createAction("summon"); + /** An {@link EnumAction} that causes the player to hold their item vertically in front of them. */ + public static final EnumAction THRUST = createAction("thrust"); + /** An {@link EnumAction} that causes the player to point the item in use towards their other hand. */ + public static final EnumAction IMBUE = createAction("imbue"); + /** An {@link EnumAction} that causes the player to point towards their grappling target (see + * {@link electroblob.wizardry.spell.Grapple Grapple}). */ + public static final EnumAction GRAPPLE = createAction("grapple"); + + private SpellActions(){} // No instances! + + private static EnumAction createAction(String name){ + return createAction(Wizardry.MODID, name); + } + + /** + * Creates a new {@link EnumAction} with the given mod ID and name and adds it to the internal spell action list. + * Use this method to add extra spell actions for use with wizardry's player animator. + * + * @param modID The ID of the mod adding this action (avoids naming conflicts) + * @param name The name of the action (for Forge to use as the in-code name; should only contain characters that + * can be used in Java identifiers) + * @return The resulting {@code EnumAction} + */ + public static EnumAction createAction(String modID, String name){ + // Using $ because this name will be used as the in-code name of the enum constant, and : wouldn't compile + EnumAction action = EnumHelper.addAction(modID + "$" + name); + spellActions.add(action); + return action; + } + + /** Returns an unmodifiable list of all spell actions. */ + public static List getSpellActions(){ + return Collections.unmodifiableList(spellActions); + } + +} diff --git a/src/main/java/electroblob/wizardry/registry/Spells.java b/src/main/java/electroblob/wizardry/registry/Spells.java index 85ee05a1..d81fd47d 100644 --- a/src/main/java/electroblob/wizardry/registry/Spells.java +++ b/src/main/java/electroblob/wizardry/registry/Spells.java @@ -4,6 +4,7 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.construct.*; import electroblob.wizardry.entity.living.*; import electroblob.wizardry.entity.projectile.*; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.spell.*; import net.minecraft.entity.projectile.EntitySnowball; import net.minecraft.init.MobEffects; @@ -299,7 +300,7 @@ public final class Spells { registry.register(new HealAlly()); registry.register(new SpellMinion<>("summon_blaze", EntityBlazeMinion::new).soundValues(1, 1.1f, 0.2f)); - registry.register(new SpellConstruct<>("ring_of_fire", EnumAction.BOW, EntityFireRing::new, false).floor(true).addProperties(Spell.DAMAGE, Spell.BURN_DURATION)); + registry.register(new SpellConstruct<>("ring_of_fire", SpellActions.POINT_DOWN, EntityFireRing::new, false).floor(true).addProperties(Spell.DAMAGE, Spell.BURN_DURATION)); registry.register(new Detonate()); registry.register(new SpellBuff("fire_resistance", 1, 0.5f, 0, () -> MobEffects.FIRE_RESISTANCE).soundValues(0.7f, 1.2f, 0.4f)); registry.register(new SpellBuff("fireskin", 1, 0.5f, 0, () -> WizardryPotions.fireskin).addProperties(Spell.BURN_DURATION)); @@ -337,7 +338,7 @@ public final class Spells { registry.register(new PhaseStep()); registry.register(new VanishingBox()); registry.register(new GreaterHeal()); - registry.register(new SpellConstruct<>("healing_aura", EnumAction.BOW, EntityHealAura::new, false).addProperties(Spell.DAMAGE, Spell.HEALTH)); + registry.register(new SpellConstruct<>("healing_aura", SpellActions.POINT_DOWN, EntityHealAura::new, false).addProperties(Spell.DAMAGE, Spell.HEALTH)); registry.register(new Forcefield()); registry.register(new SpellBuff("ironflesh", 0.4f, 0.5f, 0.6f, () -> MobEffects.RESISTANCE).soundValues(0.7f, 1.2f, 0.4f)); registry.register(new Transience()); diff --git a/src/main/java/electroblob/wizardry/spell/Arc.java b/src/main/java/electroblob/wizardry/spell/Arc.java index adfe2670..ab961d1c 100644 --- a/src/main/java/electroblob/wizardry/spell/Arc.java +++ b/src/main/java/electroblob/wizardry/spell/Arc.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; import electroblob.wizardry.util.ParticleBuilder; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; @@ -19,7 +19,7 @@ import net.minecraft.world.World; public class Arc extends SpellRay { public Arc(){ - super("arc", false, EnumAction.NONE); + super("arc", false, SpellActions.POINT); this.aimAssist(0.6f); this.soundValues(1, 1.7f, 0.2f); this.addProperties(DAMAGE); diff --git a/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java b/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java index cbf02353..7f2121d1 100644 --- a/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java +++ b/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java @@ -2,6 +2,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.event.SpellCastEvent; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.ParticleBuilder; @@ -11,7 +12,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.EntitySpellcasterIllager; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -35,7 +35,7 @@ public class ArcaneJammer extends SpellRay { } public ArcaneJammer(){ - super("arcane_jammer", false, EnumAction.NONE); + super("arcane_jammer", false, SpellActions.POINT); this.soundValues(0.7f, 1, 0.4f); this.addProperties(EFFECT_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/ArcaneLock.java b/src/main/java/electroblob/wizardry/spell/ArcaneLock.java index a93264e2..6d22d37a 100644 --- a/src/main/java/electroblob/wizardry/spell/ArcaneLock.java +++ b/src/main/java/electroblob/wizardry/spell/ArcaneLock.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.AllyDesignationSystem; import electroblob.wizardry.util.NBTExtras; import electroblob.wizardry.util.SpellModifiers; @@ -9,7 +10,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityDispenser; @@ -29,7 +29,7 @@ public class ArcaneLock extends SpellRay { public static final String NBT_KEY = "arcaneLockOwner"; public ArcaneLock(){ - super("arcane_lock", false, EnumAction.NONE); + super("arcane_lock", false, SpellActions.POINT); } @Override public boolean requiresPacket(){ return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Banish.java b/src/main/java/electroblob/wizardry/spell/Banish.java index e12fe1ff..50cd6701 100644 --- a/src/main/java/electroblob/wizardry/spell/Banish.java +++ b/src/main/java/electroblob/wizardry/spell/Banish.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.math.BlockPos; @@ -23,7 +23,7 @@ public class Banish extends SpellRay { public static final String MAXIMUM_TELEPORT_DISTANCE = "maximum_teleport_distance"; public Banish(){ - super("banish", false, EnumAction.NONE); + super("banish", false, SpellActions.POINT); this.addProperties(MINIMUM_TELEPORT_DISTANCE, MAXIMUM_TELEPORT_DISTANCE); } diff --git a/src/main/java/electroblob/wizardry/spell/Blink.java b/src/main/java/electroblob/wizardry/spell/Blink.java index fcc4975e..841b025b 100644 --- a/src/main/java/electroblob/wizardry/spell/Blink.java +++ b/src/main/java/electroblob/wizardry/spell/Blink.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.RayTracer; import electroblob.wizardry.util.SpellModifiers; @@ -8,7 +9,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumParticleTypes; @@ -20,7 +20,7 @@ import net.minecraft.world.World; public class Blink extends Spell { public Blink(){ - super("blink", EnumAction.NONE, false); + super("blink", SpellActions.POINT, false); addProperties(RANGE); } @@ -73,7 +73,6 @@ public class Blink extends Spell { if(!world.isRemote) caster.setPositionAndUpdate(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); this.playSound(world, caster, ticksInUse, -1, modifiers); - caster.swingArm(hand); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Bubble.java b/src/main/java/electroblob/wizardry/spell/Bubble.java index 2ac47406..499b4ca0 100644 --- a/src/main/java/electroblob/wizardry/spell/Bubble.java +++ b/src/main/java/electroblob/wizardry/spell/Bubble.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.entity.construct.EntityBubble; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -10,7 +11,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.SoundEvent; @@ -21,7 +21,7 @@ import net.minecraft.world.World; public class Bubble extends SpellRay { public Bubble(){ - super("bubble", false, EnumAction.NONE); + super("bubble", false, SpellActions.POINT); this.soundValues(0.5f, 1.1f, 0.2f); addProperties(DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/ChainLightning.java b/src/main/java/electroblob/wizardry/spell/ChainLightning.java index 83b3df2f..1a31f86f 100644 --- a/src/main/java/electroblob/wizardry/spell/ChainLightning.java +++ b/src/main/java/electroblob/wizardry/spell/ChainLightning.java @@ -1,12 +1,12 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.*; import electroblob.wizardry.util.MagicDamage.DamageType; import electroblob.wizardry.util.ParticleBuilder.Type; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; @@ -28,7 +28,7 @@ public class ChainLightning extends SpellRay { public static final String TERTIARY_MAX_TARGETS = "tertiary_max_targets"; // This is per secondary target public ChainLightning(){ - super("chain_lightning", false, EnumAction.NONE); + super("chain_lightning", false, SpellActions.POINT); this.aimAssist(0.6f); this.soundValues(1, 1.7f, 0.2f); addProperties(PRIMARY_DAMAGE, SECONDARY_DAMAGE, TERTIARY_DAMAGE, SECONDARY_RANGE, TERTIARY_RANGE, diff --git a/src/main/java/electroblob/wizardry/spell/Charge.java b/src/main/java/electroblob/wizardry/spell/Charge.java index d6e9a4df..20725625 100644 --- a/src/main/java/electroblob/wizardry/spell/Charge.java +++ b/src/main/java/electroblob/wizardry/spell/Charge.java @@ -3,6 +3,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.data.IVariable; import electroblob.wizardry.data.Persistence; import electroblob.wizardry.data.WizardData; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; @@ -13,7 +14,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.math.Vec3d; @@ -37,7 +37,7 @@ public class Charge extends Spell { private static final double EXTRA_HIT_MARGIN = 1; public Charge(){ - super("charge", EnumAction.NONE, false); + super("charge", SpellActions.POINT, false); addProperties(CHARGE_SPEED, DURATION, DAMAGE, KNOCKBACK_STRENGTH); this.soundValues(0.6f, 1, 0); } diff --git a/src/main/java/electroblob/wizardry/spell/Clairvoyance.java b/src/main/java/electroblob/wizardry/spell/Clairvoyance.java index c5ffba8b..a2d7c868 100644 --- a/src/main/java/electroblob/wizardry/spell/Clairvoyance.java +++ b/src/main/java/electroblob/wizardry/spell/Clairvoyance.java @@ -4,6 +4,7 @@ import electroblob.wizardry.data.IStoredVariable; import electroblob.wizardry.data.Persistence; import electroblob.wizardry.data.WizardData; import electroblob.wizardry.item.ISpellCastingItem; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.misc.WizardryPathFinder; import electroblob.wizardry.packet.PacketClairvoyance; import electroblob.wizardry.packet.WizardryPacketHandler; @@ -17,7 +18,6 @@ import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.monster.EntityZombie; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; import net.minecraft.pathfinding.Path; import net.minecraft.pathfinding.PathNodeType; @@ -41,7 +41,7 @@ public class Clairvoyance extends Spell { public static final IStoredVariable DIMENSION_KEY = IStoredVariable.StoredVariable.ofInt("clairvoyanceDimension", Persistence.ALWAYS); public Clairvoyance(){ - super("clairvoyance", EnumAction.BOW, false); + super("clairvoyance", SpellActions.POINT_UP, false); addProperties(RANGE, DURATION); WizardData.registerStoredVariables(LOCATION_KEY, DIMENSION_KEY); } diff --git a/src/main/java/electroblob/wizardry/spell/Cobwebs.java b/src/main/java/electroblob/wizardry/spell/Cobwebs.java index 9d2367a1..857f7317 100644 --- a/src/main/java/electroblob/wizardry/spell/Cobwebs.java +++ b/src/main/java/electroblob/wizardry/spell/Cobwebs.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.tileentity.TileEntityTimer; @@ -8,7 +9,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; @@ -19,7 +19,7 @@ import java.util.List; public class Cobwebs extends SpellRay { public Cobwebs(){ - super("cobwebs", false, EnumAction.NONE); + super("cobwebs", false, SpellActions.POINT); this.ignoreLivingEntities(true); addProperties(EFFECT_RADIUS, DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/ConjureBlock.java b/src/main/java/electroblob/wizardry/spell/ConjureBlock.java index 50ce61d1..c4a3bc56 100644 --- a/src/main/java/electroblob/wizardry/spell/ConjureBlock.java +++ b/src/main/java/electroblob/wizardry/spell/ConjureBlock.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.tileentity.TileEntityTimer; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; @@ -20,7 +20,7 @@ public class ConjureBlock extends SpellRay { private static final String BLOCK_LIFETIME = "block_lifetime"; public ConjureBlock(){ - super("conjure_block", false, EnumAction.NONE); + super("conjure_block", false, SpellActions.POINT); this.ignoreLivingEntities(true); addProperties(BLOCK_LIFETIME); } diff --git a/src/main/java/electroblob/wizardry/spell/Containment.java b/src/main/java/electroblob/wizardry/spell/Containment.java index cc9c0b71..ddedbe53 100644 --- a/src/main/java/electroblob/wizardry/spell/Containment.java +++ b/src/main/java/electroblob/wizardry/spell/Containment.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.ParticleBuilder; @@ -8,7 +9,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -18,7 +18,7 @@ import net.minecraft.world.World; public class Containment extends SpellRay { public Containment(){ - super("containment", false, EnumAction.NONE); + super("containment", false, SpellActions.POINT); this.soundValues(1, 1, 0.2f); addProperties(EFFECT_DURATION, EFFECT_STRENGTH); } diff --git a/src/main/java/electroblob/wizardry/spell/CurseOfEnfeeblement.java b/src/main/java/electroblob/wizardry/spell/CurseOfEnfeeblement.java index a6a5f2e1..29fcc938 100644 --- a/src/main/java/electroblob/wizardry/spell/CurseOfEnfeeblement.java +++ b/src/main/java/electroblob/wizardry/spell/CurseOfEnfeeblement.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.ParticleBuilder; @@ -8,7 +9,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -18,7 +18,7 @@ import net.minecraft.world.World; public class CurseOfEnfeeblement extends SpellRay { public CurseOfEnfeeblement(){ - super("curse_of_enfeeblement", false, EnumAction.NONE); + super("curse_of_enfeeblement", false, SpellActions.POINT); this.soundValues(1, 1.1f, 0.2f); addProperties(EFFECT_STRENGTH); } diff --git a/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java b/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java index e47b7865..e36bbc36 100644 --- a/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java +++ b/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java @@ -4,6 +4,7 @@ import electroblob.wizardry.data.IStoredVariable; import electroblob.wizardry.data.Persistence; import electroblob.wizardry.data.WizardData; import electroblob.wizardry.integration.DamageSafetyChecker; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.util.*; @@ -12,7 +13,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTUtil; import net.minecraft.potion.PotionEffect; @@ -42,7 +42,7 @@ public class CurseOfSoulbinding extends SpellRay { Persistence.DIMENSION_CHANGE); public CurseOfSoulbinding(){ - super("curse_of_soulbinding", false, EnumAction.NONE); + super("curse_of_soulbinding", false, SpellActions.POINT); this.soundValues(1, 1.1f, 0.2f); WizardData.registerStoredVariables(TARGETS_KEY); } diff --git a/src/main/java/electroblob/wizardry/spell/CurseOfUndeath.java b/src/main/java/electroblob/wizardry/spell/CurseOfUndeath.java index f97e1148..eec32c73 100644 --- a/src/main/java/electroblob/wizardry/spell/CurseOfUndeath.java +++ b/src/main/java/electroblob/wizardry/spell/CurseOfUndeath.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; @@ -7,7 +8,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -17,7 +17,7 @@ import net.minecraft.world.World; public class CurseOfUndeath extends SpellRay { public CurseOfUndeath(){ - super("curse_of_undeath", false, EnumAction.NONE); + super("curse_of_undeath", false, SpellActions.POINT); this.soundValues(1, 1.1f, 0.2f); addProperties(EFFECT_STRENGTH); } diff --git a/src/main/java/electroblob/wizardry/spell/Decoy.java b/src/main/java/electroblob/wizardry/spell/Decoy.java index 46f21c4e..a667e8a7 100644 --- a/src/main/java/electroblob/wizardry/spell/Decoy.java +++ b/src/main/java/electroblob/wizardry/spell/Decoy.java @@ -1,12 +1,12 @@ package electroblob.wizardry.spell; import electroblob.wizardry.entity.living.EntityDecoy; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.world.World; @@ -16,7 +16,7 @@ public class Decoy extends Spell { public static final String MOB_TRICK_CHANCE = "mob_trick_chance"; public Decoy(){ - super("decoy", EnumAction.BOW, false); + super("decoy", SpellActions.SUMMON, false); this.soundValues(1, 0.9f, 0.2f); addProperties(DECOY_LIFETIME, MOB_TRICK_CHANCE); } diff --git a/src/main/java/electroblob/wizardry/spell/Detonate.java b/src/main/java/electroblob/wizardry/spell/Detonate.java index 34a28d4f..08692d80 100644 --- a/src/main/java/electroblob/wizardry/spell/Detonate.java +++ b/src/main/java/electroblob/wizardry/spell/Detonate.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -7,7 +8,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.math.BlockPos; @@ -22,7 +22,7 @@ public class Detonate extends SpellRay { public static final String MAX_DAMAGE = "max_damage"; public Detonate(){ - super("detonate", false, EnumAction.NONE); + super("detonate", false, SpellActions.POINT); this.soundValues(4, 0.7f, 0.14f); this.ignoreLivingEntities(true); addProperties(MAX_DAMAGE, BLAST_RADIUS); diff --git a/src/main/java/electroblob/wizardry/spell/Disintegration.java b/src/main/java/electroblob/wizardry/spell/Disintegration.java index 45d89781..36afaa0c 100644 --- a/src/main/java/electroblob/wizardry/spell/Disintegration.java +++ b/src/main/java/electroblob/wizardry/spell/Disintegration.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.entity.projectile.EntityEmber; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -11,7 +12,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; @@ -26,7 +26,7 @@ public class Disintegration extends SpellRay { public static final String EMBER_LIFETIME = "ember_lifetime"; public Disintegration(){ - super("disintegration", false, EnumAction.NONE); + super("disintegration", false, SpellActions.POINT); addProperties(DAMAGE, BURN_DURATION, EMBER_LIFETIME, EMBER_COUNT); } diff --git a/src/main/java/electroblob/wizardry/spell/Divination.java b/src/main/java/electroblob/wizardry/spell/Divination.java index 79c69440..be21e09f 100644 --- a/src/main/java/electroblob/wizardry/spell/Divination.java +++ b/src/main/java/electroblob/wizardry/spell/Divination.java @@ -3,6 +3,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Settings; import electroblob.wizardry.Wizardry; import electroblob.wizardry.block.BlockCrystalOre; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.RelativeFacing; @@ -13,7 +14,6 @@ import net.minecraft.block.BlockOre; import net.minecraft.block.BlockRedstoneOre; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.FurnaceRecipes; import net.minecraft.util.EnumFacing; @@ -22,7 +22,6 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; -import org.apache.commons.lang3.tuple.Pair; import java.util.Arrays; import java.util.Comparator; @@ -33,7 +32,7 @@ public class Divination extends Spell { private static final float NUDGE_SPEED = 0.2f; public Divination(){ - super("divination", EnumAction.NONE, false); + super("divination", SpellActions.THRUST, false); addProperties(RANGE); } diff --git a/src/main/java/electroblob/wizardry/spell/Earthquake.java b/src/main/java/electroblob/wizardry/spell/Earthquake.java index 2c9ae2b8..50c7f901 100644 --- a/src/main/java/electroblob/wizardry/spell/Earthquake.java +++ b/src/main/java/electroblob/wizardry/spell/Earthquake.java @@ -2,6 +2,7 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.construct.EntityEarthquake; + import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; @@ -9,7 +10,6 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; - import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.world.World; @@ -19,7 +19,7 @@ public static final String SPREAD_SPEED = "spread_speed"; public Earthquake(){ - super("earthquake", EnumAction.NONE, EntityEarthquake::new, true); + super("earthquake", SpellActions.POINT_DOWN, EntityEarthquake::new, true); this.soundValues(2, 1, 0); this.overlap(true); this.floor(true); diff --git a/src/main/java/electroblob/wizardry/spell/EmpoweringPresence.java b/src/main/java/electroblob/wizardry/spell/EmpoweringPresence.java index 7fcbaf18..093124e5 100644 --- a/src/main/java/electroblob/wizardry/spell/EmpoweringPresence.java +++ b/src/main/java/electroblob/wizardry/spell/EmpoweringPresence.java @@ -3,6 +3,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.event.SpellCastEvent; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.AllyDesignationSystem; @@ -11,7 +12,6 @@ import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumHand; import net.minecraft.util.math.MathHelper; @@ -26,7 +26,7 @@ import java.util.List; public class EmpoweringPresence extends Spell { public EmpoweringPresence(){ - super("empowering_presence", EnumAction.BOW, false); + super("empowering_presence", SpellActions.POINT_UP, false); addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH); } diff --git a/src/main/java/electroblob/wizardry/spell/Entrapment.java b/src/main/java/electroblob/wizardry/spell/Entrapment.java index 7f20fbd8..107eba36 100644 --- a/src/main/java/electroblob/wizardry/spell/Entrapment.java +++ b/src/main/java/electroblob/wizardry/spell/Entrapment.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.entity.construct.EntityBubble; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -10,7 +11,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.math.BlockPos; @@ -22,7 +22,7 @@ public class Entrapment extends SpellRay { public static final String DAMAGE_INTERVAL = "damage_interval"; public Entrapment(){ - super("entrapment", false, EnumAction.NONE); + super("entrapment", false, SpellActions.POINT); this.soundValues(1, 0.85f, 0.3f); addProperties(EFFECT_DURATION, DAMAGE_INTERVAL); } diff --git a/src/main/java/electroblob/wizardry/spell/FireBreath.java b/src/main/java/electroblob/wizardry/spell/FireBreath.java index 338f375d..f4242e29 100644 --- a/src/main/java/electroblob/wizardry/spell/FireBreath.java +++ b/src/main/java/electroblob/wizardry/spell/FireBreath.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -11,7 +12,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; @@ -22,7 +22,7 @@ import net.minecraft.world.World; public class FireBreath extends SpellRay { public FireBreath(){ - super("fire_breath", true, EnumAction.NONE); + super("fire_breath", true, SpellActions.POINT); this.particleVelocity(1); this.particleJitter(0.3); this.particleSpacing(0.25); diff --git a/src/main/java/electroblob/wizardry/spell/FlameRay.java b/src/main/java/electroblob/wizardry/spell/FlameRay.java index b13e2e45..35cf6ca6 100644 --- a/src/main/java/electroblob/wizardry/spell/FlameRay.java +++ b/src/main/java/electroblob/wizardry/spell/FlameRay.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -10,7 +11,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; @@ -21,7 +21,7 @@ import net.minecraft.world.World; public class FlameRay extends SpellRay { public FlameRay(){ - super("flame_ray", true, EnumAction.NONE); + super("flame_ray", true, SpellActions.POINT); this.particleVelocity(1); this.particleSpacing(0.5); addProperties(DAMAGE, BURN_DURATION); diff --git a/src/main/java/electroblob/wizardry/spell/FlamingWeapon.java b/src/main/java/electroblob/wizardry/spell/FlamingWeapon.java index b77f4e9a..6fbfcd61 100644 --- a/src/main/java/electroblob/wizardry/spell/FlamingWeapon.java +++ b/src/main/java/electroblob/wizardry/spell/FlamingWeapon.java @@ -2,6 +2,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.data.WizardData; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryEnchantments; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; @@ -10,7 +11,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumHand; import net.minecraft.world.World; @@ -18,7 +18,7 @@ import net.minecraft.world.World; public class FlamingWeapon extends Spell { public FlamingWeapon(){ - super("flaming_weapon", EnumAction.BOW, false); + super("flaming_weapon", SpellActions.IMBUE, false); addProperties(EFFECT_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/Flight.java b/src/main/java/electroblob/wizardry/spell/Flight.java index 9e2d0d8a..175070e7 100644 --- a/src/main/java/electroblob/wizardry/spell/Flight.java +++ b/src/main/java/electroblob/wizardry/spell/Flight.java @@ -1,11 +1,11 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.world.World; @@ -17,7 +17,7 @@ public class Flight extends Spell { private static final double Y_NUDGE_ACCELERATION = 0.075; public Flight(){ - super("flight", EnumAction.NONE, true); + super("flight", SpellActions.POINT, true); addProperties(SPEED, ACCELERATION); } diff --git a/src/main/java/electroblob/wizardry/spell/FontOfMana.java b/src/main/java/electroblob/wizardry/spell/FontOfMana.java index 4dc20f74..abf10b97 100644 --- a/src/main/java/electroblob/wizardry/spell/FontOfMana.java +++ b/src/main/java/electroblob/wizardry/spell/FontOfMana.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.event.SpellCastEvent; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.AllyDesignationSystem; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumHand; import net.minecraft.util.math.MathHelper; @@ -22,7 +22,7 @@ import java.util.List; public class FontOfMana extends Spell { public FontOfMana(){ - super("font_of_mana", EnumAction.BOW, false); + super("font_of_mana", SpellActions.POINT_UP, false); this.soundValues(0.7f, 1.2f, 0.4f); addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH); } diff --git a/src/main/java/electroblob/wizardry/spell/Forcefield.java b/src/main/java/electroblob/wizardry/spell/Forcefield.java index bfc298b2..f3413c30 100644 --- a/src/main/java/electroblob/wizardry/spell/Forcefield.java +++ b/src/main/java/electroblob/wizardry/spell/Forcefield.java @@ -1,16 +1,16 @@ package electroblob.wizardry.spell; import electroblob.wizardry.entity.construct.EntityForcefield; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; public class Forcefield extends SpellConstruct { public Forcefield(){ - super("forcefield", EnumAction.BOW, EntityForcefield::new, false); + super("forcefield", SpellActions.THRUST, EntityForcefield::new, false); addProperties(Spell.EFFECT_RADIUS); } diff --git a/src/main/java/electroblob/wizardry/spell/ForestOfThorns.java b/src/main/java/electroblob/wizardry/spell/ForestOfThorns.java index 9a8a6e49..8368924c 100644 --- a/src/main/java/electroblob/wizardry/spell/ForestOfThorns.java +++ b/src/main/java/electroblob/wizardry/spell/ForestOfThorns.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.block.BlockThorns; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.tileentity.TileEntityPlayerSaveTimed; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.util.EnumFacing; @@ -25,7 +25,7 @@ import java.util.List; public class ForestOfThorns extends Spell { public ForestOfThorns(){ - super("forest_of_thorns", EnumAction.BOW, false); + super("forest_of_thorns", SpellActions.SUMMON, false); addProperties(EFFECT_RADIUS, DURATION, DAMAGE); } diff --git a/src/main/java/electroblob/wizardry/spell/ForestsCurse.java b/src/main/java/electroblob/wizardry/spell/ForestsCurse.java index edb3f8d4..a618609b 100644 --- a/src/main/java/electroblob/wizardry/spell/ForestsCurse.java +++ b/src/main/java/electroblob/wizardry/spell/ForestsCurse.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -9,14 +10,13 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLivingBase; import net.minecraft.init.MobEffects; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.world.World; public class ForestsCurse extends SpellAreaEffect { public ForestsCurse(){ - super("forests_curse", EnumAction.BOW); + super("forests_curse", SpellActions.POINT_UP); this.soundValues(1, 1.1f, 0.2f); addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); } diff --git a/src/main/java/electroblob/wizardry/spell/Freeze.java b/src/main/java/electroblob/wizardry/spell/Freeze.java index d0d59939..3d0b1cf2 100644 --- a/src/main/java/electroblob/wizardry/spell/Freeze.java +++ b/src/main/java/electroblob/wizardry/spell/Freeze.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.MagicDamage; @@ -14,7 +15,6 @@ import net.minecraft.entity.monster.EntityBlaze; import net.minecraft.entity.monster.EntityMagmaCube; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -25,7 +25,7 @@ import net.minecraft.world.World; public class Freeze extends SpellRay { public Freeze(){ - super("freeze", false, EnumAction.NONE); + super("freeze", false, SpellActions.POINT); this.soundValues(1, 1.4f, 0.4f); addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); this.hitLiquids(true); diff --git a/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java b/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java index 46cc6111..a4862ffe 100644 --- a/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java +++ b/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java @@ -2,6 +2,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.data.WizardData; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryEnchantments; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; @@ -10,7 +11,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumHand; import net.minecraft.world.World; @@ -24,7 +24,7 @@ public class FreezingWeapon extends Spell { public static final String FREEZING_ARROW_NBT_KEY = "frostLevel"; public FreezingWeapon(){ - super("freezing_weapon", EnumAction.BOW, false); + super("freezing_weapon", SpellActions.IMBUE, false); addProperties(EFFECT_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/FrostRay.java b/src/main/java/electroblob/wizardry/spell/FrostRay.java index 7f4dbcaa..556d2b8d 100644 --- a/src/main/java/electroblob/wizardry/spell/FrostRay.java +++ b/src/main/java/electroblob/wizardry/spell/FrostRay.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.MagicDamage; @@ -13,7 +14,6 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.EntityBlaze; import net.minecraft.entity.monster.EntityMagmaCube; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundEvent; @@ -25,7 +25,7 @@ import net.minecraft.world.World; public class FrostRay extends SpellRay { public FrostRay(){ - super("frost_ray", true, EnumAction.NONE); + super("frost_ray", true, SpellActions.POINT); this.particleVelocity(1); this.particleSpacing(0.5); addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); diff --git a/src/main/java/electroblob/wizardry/spell/Glide.java b/src/main/java/electroblob/wizardry/spell/Glide.java index 8239f676..4551c6d7 100644 --- a/src/main/java/electroblob/wizardry/spell/Glide.java +++ b/src/main/java/electroblob/wizardry/spell/Glide.java @@ -1,13 +1,13 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.world.World; @@ -18,7 +18,7 @@ public class Glide extends Spell { public static final String ACCELERATION = "acceleration"; public Glide(){ - super("glide", EnumAction.NONE, true); + super("glide", SpellActions.POINT_DOWN, true); addProperties(SPEED, FALL_SPEED, ACCELERATION); } diff --git a/src/main/java/electroblob/wizardry/spell/Grapple.java b/src/main/java/electroblob/wizardry/spell/Grapple.java index 437b27b7..a61e46e0 100644 --- a/src/main/java/electroblob/wizardry/spell/Grapple.java +++ b/src/main/java/electroblob/wizardry/spell/Grapple.java @@ -5,6 +5,7 @@ import electroblob.wizardry.data.IVariable; import electroblob.wizardry.data.Persistence; import electroblob.wizardry.data.WizardData; import electroblob.wizardry.item.ItemArtefact; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; @@ -17,7 +18,6 @@ import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.EnumAction; import net.minecraft.network.play.server.SPacketEntityVelocity; import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.util.EnumFacing; @@ -54,7 +54,7 @@ public class Grapple extends Spell { protected static final double PARTICLE_JITTER = 0.04; public Grapple(){ - super("grapple", EnumAction.NONE, true); + super("grapple", SpellActions.GRAPPLE, true); addProperties(RANGE, EXTENSION_SPEED, REEL_SPEED); } diff --git a/src/main/java/electroblob/wizardry/spell/GreaterTelekinesis.java b/src/main/java/electroblob/wizardry/spell/GreaterTelekinesis.java index 1bc1e45b..b7a7c17c 100644 --- a/src/main/java/electroblob/wizardry/spell/GreaterTelekinesis.java +++ b/src/main/java/electroblob/wizardry/spell/GreaterTelekinesis.java @@ -3,6 +3,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.EntityLevitatingBlock; import electroblob.wizardry.item.ItemArtefact; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; @@ -14,7 +15,6 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityTNTPrimed; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.EnumAction; import net.minecraft.network.play.server.SPacketEntityVelocity; import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.util.EnumFacing; @@ -35,7 +35,7 @@ public class GreaterTelekinesis extends SpellRay { private static final float UNDERSHOOT = 0.2f; public GreaterTelekinesis(){ - super("greater_telekinesis", true, EnumAction.NONE); + super("greater_telekinesis", true, SpellActions.POINT); this.aimAssist(0.4f); this.particleSpacing(1); this.particleJitter(0.05); diff --git a/src/main/java/electroblob/wizardry/spell/GroupHeal.java b/src/main/java/electroblob/wizardry/spell/GroupHeal.java index cc2677e0..75ec5df3 100644 --- a/src/main/java/electroblob/wizardry/spell/GroupHeal.java +++ b/src/main/java/electroblob/wizardry/spell/GroupHeal.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.AllyDesignationSystem; import electroblob.wizardry.util.ParticleBuilder; @@ -7,7 +8,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.world.World; @@ -16,7 +16,7 @@ import java.util.List; public class GroupHeal extends Spell { public GroupHeal(){ - super("group_heal", EnumAction.BOW, false); + super("group_heal", SpellActions.POINT_UP, false); this.soundValues(0.7f, 1.2f, 0.4f); addProperties(EFFECT_RADIUS, HEALTH); } diff --git a/src/main/java/electroblob/wizardry/spell/GrowthAura.java b/src/main/java/electroblob/wizardry/spell/GrowthAura.java index 929c803f..09dfa81c 100644 --- a/src/main/java/electroblob/wizardry/spell/GrowthAura.java +++ b/src/main/java/electroblob/wizardry/spell/GrowthAura.java @@ -1,13 +1,13 @@ package electroblob.wizardry.spell; import electroblob.wizardry.item.ItemArtefact; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.block.IGrowable; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemDye; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; @@ -18,7 +18,7 @@ import java.util.List; public class GrowthAura extends Spell { public GrowthAura(){ - super("growth_aura", EnumAction.NONE, false); + super("growth_aura", SpellActions.POINT_DOWN, false); addProperties(EFFECT_RADIUS); soundValues(0.7f, 1.2f, 0.2f); } diff --git a/src/main/java/electroblob/wizardry/spell/HealAlly.java b/src/main/java/electroblob/wizardry/spell/HealAlly.java index 2a31ad2e..0b375381 100644 --- a/src/main/java/electroblob/wizardry/spell/HealAlly.java +++ b/src/main/java/electroblob/wizardry/spell/HealAlly.java @@ -1,12 +1,12 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; @@ -15,7 +15,7 @@ import net.minecraft.world.World; public class HealAlly extends SpellRay { public HealAlly(){ - super("heal_ally", false, EnumAction.NONE); + super("heal_ally", false, SpellActions.POINT); this.soundValues(0.7f, 1.2f, 0.4f); addProperties(HEALTH); } diff --git a/src/main/java/electroblob/wizardry/spell/IceAge.java b/src/main/java/electroblob/wizardry/spell/IceAge.java index 8c94d203..5086207f 100644 --- a/src/main/java/electroblob/wizardry/spell/IceAge.java +++ b/src/main/java/electroblob/wizardry/spell/IceAge.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.block.BlockStatue; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardrySounds; @@ -11,7 +12,6 @@ import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; @@ -22,7 +22,7 @@ import java.util.List; public class IceAge extends Spell { public IceAge(){ - super("ice_age", EnumAction.BOW, false); + super("ice_age", SpellActions.POINT_DOWN, false); this.soundValues(0.7f, 1.0f, 0); addProperties(EFFECT_RADIUS, EFFECT_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/IceStatue.java b/src/main/java/electroblob/wizardry/spell/IceStatue.java index 35665491..6bdebce9 100644 --- a/src/main/java/electroblob/wizardry/spell/IceStatue.java +++ b/src/main/java/electroblob/wizardry/spell/IceStatue.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.block.BlockStatue; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; @@ -19,7 +19,7 @@ import net.minecraft.world.World; public class IceStatue extends SpellRay { public IceStatue(){ - super("ice_statue", false, EnumAction.NONE); + super("ice_statue", false, SpellActions.POINT); this.soundValues(1, 1.4f, 0.4f); addProperties(EFFECT_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/Ignite.java b/src/main/java/electroblob/wizardry/spell/Ignite.java index b686d89d..63561d53 100644 --- a/src/main/java/electroblob/wizardry/spell/Ignite.java +++ b/src/main/java/electroblob/wizardry/spell/Ignite.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -9,7 +10,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.math.BlockPos; @@ -20,7 +20,7 @@ import net.minecraft.world.World; public class Ignite extends SpellRay { public Ignite(){ - super("ignite", false, EnumAction.NONE); + super("ignite", false, SpellActions.POINT); this.soundValues(1, 1, 0.4f); addProperties(BURN_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java b/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java index 631b9222..b3c881b4 100644 --- a/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java +++ b/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java @@ -4,6 +4,7 @@ import electroblob.wizardry.Settings; import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.data.WizardData; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryEnchantments; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; @@ -12,20 +13,16 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemBow; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemSword; import net.minecraft.util.EnumHand; import net.minecraft.world.World; -import org.apache.commons.lang3.tuple.Pair; - -import java.util.Arrays; public class ImbueWeapon extends Spell { public ImbueWeapon(){ - super("imbue_weapon", EnumAction.BOW, false); + super("imbue_weapon", SpellActions.IMBUE, false); addProperties(EFFECT_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/Intimidate.java b/src/main/java/electroblob/wizardry/spell/Intimidate.java index 6c9889ea..408d0304 100644 --- a/src/main/java/electroblob/wizardry/spell/Intimidate.java +++ b/src/main/java/electroblob/wizardry/spell/Intimidate.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.ParticleBuilder; @@ -11,7 +12,6 @@ import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.ai.RandomPositionGenerator; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.pathfinding.PathPoint; import net.minecraft.potion.PotionEffect; @@ -36,7 +36,7 @@ public class Intimidate extends Spell { private static final double AVOID_DISTANCE_PER_LEVEL = 4; public Intimidate(){ - super("intimidate", EnumAction.BOW, false); + super("intimidate", SpellActions.SUMMON, false); addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH); } diff --git a/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java b/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java index a90b7a44..503d80f9 100644 --- a/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java +++ b/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.AllyDesignationSystem; import electroblob.wizardry.util.ParticleBuilder; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumHand; import net.minecraft.util.math.MathHelper; @@ -20,7 +20,7 @@ import java.util.List; public class InvigoratingPresence extends Spell { public InvigoratingPresence(){ - super("invigorating_presence", EnumAction.BOW, false); + super("invigorating_presence", SpellActions.POINT_UP, false); this.soundValues(0.7f, 1.2f, 0.4f); addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH); } diff --git a/src/main/java/electroblob/wizardry/spell/InvokeWeather.java b/src/main/java/electroblob/wizardry/spell/InvokeWeather.java index e62718ac..45b711dc 100644 --- a/src/main/java/electroblob/wizardry/spell/InvokeWeather.java +++ b/src/main/java/electroblob/wizardry/spell/InvokeWeather.java @@ -1,12 +1,12 @@ package electroblob.wizardry.spell; import electroblob.wizardry.item.ItemArtefact; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; @@ -18,7 +18,7 @@ public class InvokeWeather extends Spell { public static final String THUNDERSTORM_CHANCE = "thunderstorm_chance"; public InvokeWeather(){ - super("invoke_weather", EnumAction.BOW, false); + super("invoke_weather", SpellActions.POINT_UP, false); addProperties(THUNDERSTORM_CHANCE); soundValues(0.5f, 1, 0); } diff --git a/src/main/java/electroblob/wizardry/spell/Levitation.java b/src/main/java/electroblob/wizardry/spell/Levitation.java index f70e8f29..2d08335d 100644 --- a/src/main/java/electroblob/wizardry/spell/Levitation.java +++ b/src/main/java/electroblob/wizardry/spell/Levitation.java @@ -1,12 +1,12 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.util.SoundEvent; import net.minecraft.world.World; @@ -17,7 +17,7 @@ public class Levitation extends Spell { public static final String ACCELERATION = "acceleration"; public Levitation(){ - super("levitation", EnumAction.BOW, true); + super("levitation", SpellActions.POINT_DOWN, true); addProperties(SPEED, ACCELERATION); soundValues(0.5f, 1, 0); } diff --git a/src/main/java/electroblob/wizardry/spell/LifeDrain.java b/src/main/java/electroblob/wizardry/spell/LifeDrain.java index a4cf6add..3d5d9751 100644 --- a/src/main/java/electroblob/wizardry/spell/LifeDrain.java +++ b/src/main/java/electroblob/wizardry/spell/LifeDrain.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; import electroblob.wizardry.util.ParticleBuilder; @@ -8,7 +9,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; @@ -20,7 +20,7 @@ public class LifeDrain extends SpellRay { public static final String HEAL_FACTOR = "heal_factor"; public LifeDrain(){ - super("life_drain", true, EnumAction.NONE); + super("life_drain", true, SpellActions.POINT); this.particleVelocity(-0.5); this.particleSpacing(0.4); addProperties(DAMAGE, HEAL_FACTOR); diff --git a/src/main/java/electroblob/wizardry/spell/Light.java b/src/main/java/electroblob/wizardry/spell/Light.java index 95571fb5..216da264 100644 --- a/src/main/java/electroblob/wizardry/spell/Light.java +++ b/src/main/java/electroblob/wizardry/spell/Light.java @@ -1,13 +1,13 @@ package electroblob.wizardry.spell; import electroblob.wizardry.item.ItemArtefact; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.tileentity.TileEntityTimer; import electroblob.wizardry.util.RayTracer; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; @@ -16,7 +16,7 @@ import net.minecraft.world.World; public class Light extends Spell { public Light(){ - super("light", EnumAction.NONE, false); + super("light", SpellActions.POINT, false); addProperties(RANGE, DURATION); } @@ -47,7 +47,6 @@ public class Light extends Spell { } } - caster.swingArm(hand); this.playSound(world, caster, ticksInUse, -1, modifiers); return true; } @@ -68,7 +67,7 @@ public class Light extends Spell { ((TileEntityTimer)world.getTileEntity(pos)).setLifetime(lifetime); } } - caster.swingArm(hand); + this.playSound(world, caster, ticksInUse, -1, modifiers); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/LightningBolt.java b/src/main/java/electroblob/wizardry/spell/LightningBolt.java index 461cb126..7cb37cf9 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningBolt.java +++ b/src/main/java/electroblob/wizardry/spell/LightningBolt.java @@ -1,12 +1,12 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.effect.EntityLightningBolt; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -20,7 +20,7 @@ public class LightningBolt extends SpellRay { public static final String NBT_KEY = "summoningPlayer"; public LightningBolt(){ - super("lightning_bolt", false, EnumAction.NONE); + super("lightning_bolt", false, SpellActions.POINT); this.ignoreLivingEntities(true); } diff --git a/src/main/java/electroblob/wizardry/spell/LightningPulse.java b/src/main/java/electroblob/wizardry/spell/LightningPulse.java index 42b8037b..f916d0b6 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningPulse.java +++ b/src/main/java/electroblob/wizardry/spell/LightningPulse.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.*; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -7,7 +8,6 @@ import electroblob.wizardry.util.ParticleBuilder.Type; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.EnumAction; import net.minecraft.network.play.server.SPacketEntityVelocity; import net.minecraft.util.EnumHand; import net.minecraft.util.SoundEvent; @@ -21,7 +21,7 @@ public class LightningPulse extends Spell { public static final String REPULSION_VELOCITY = "repulsion_velocity"; public LightningPulse(){ - super("lightning_pulse", EnumAction.NONE, false); + super("lightning_pulse", SpellActions.POINT_DOWN, false); addProperties(EFFECT_RADIUS, DAMAGE, REPULSION_VELOCITY); this.soundValues(2, 1, 0); } @@ -74,8 +74,7 @@ public class LightningPulse extends Spell { + WizardryUtilities.ANTI_Z_FIGHTING_OFFSET, caster.posZ) .scale(modifiers.get(WizardryItems.blast_upgrade)).spawn(world); } - - caster.swingArm(hand); + this.playSound(world, caster, ticksInUse, -1, modifiers); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/LightningRay.java b/src/main/java/electroblob/wizardry/spell/LightningRay.java index 21bb5c0b..273b4e04 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningRay.java +++ b/src/main/java/electroblob/wizardry/spell/LightningRay.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; import electroblob.wizardry.util.ParticleBuilder; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; @@ -20,7 +20,7 @@ import net.minecraft.world.World; public class LightningRay extends SpellRay { public LightningRay(){ - super("lightning_ray", true, EnumAction.NONE); + super("lightning_ray", true, SpellActions.POINT); this.aimAssist(0.6f); addProperties(DAMAGE); } diff --git a/src/main/java/electroblob/wizardry/spell/LightningWeb.java b/src/main/java/electroblob/wizardry/spell/LightningWeb.java index 759ea581..5587cba8 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningWeb.java +++ b/src/main/java/electroblob/wizardry/spell/LightningWeb.java @@ -1,12 +1,12 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.*; import electroblob.wizardry.util.MagicDamage.DamageType; import electroblob.wizardry.util.ParticleBuilder.Type; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; @@ -29,7 +29,7 @@ public class LightningWeb extends SpellRay { public static final String TERTIARY_MAX_TARGETS = "tertiary_max_targets"; // This is per secondary target public LightningWeb(){ - super("lightning_web", true, EnumAction.NONE); + super("lightning_web", true, SpellActions.POINT); this.aimAssist(0.6f); addProperties(PRIMARY_DAMAGE, SECONDARY_DAMAGE, TERTIARY_DAMAGE, SECONDARY_RANGE, TERTIARY_RANGE, SECONDARY_MAX_TARGETS, TERTIARY_MAX_TARGETS); diff --git a/src/main/java/electroblob/wizardry/spell/Metamorphosis.java b/src/main/java/electroblob/wizardry/spell/Metamorphosis.java index 2270da29..a0689642 100644 --- a/src/main/java/electroblob/wizardry/spell/Metamorphosis.java +++ b/src/main/java/electroblob/wizardry/spell/Metamorphosis.java @@ -4,6 +4,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.living.*; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.NBTExtras; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; @@ -14,7 +15,6 @@ import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.*; import net.minecraft.entity.passive.*; -import net.minecraft.item.EnumAction; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -49,7 +49,7 @@ public class Metamorphosis extends SpellRay { } public Metamorphosis(){ - super("metamorphosis", false, EnumAction.NONE); + super("metamorphosis", false, SpellActions.POINT); this.soundValues(0.5f, 1f, 0); } diff --git a/src/main/java/electroblob/wizardry/spell/Meteor.java b/src/main/java/electroblob/wizardry/spell/Meteor.java index a89f1173..e26ec67b 100644 --- a/src/main/java/electroblob/wizardry/spell/Meteor.java +++ b/src/main/java/electroblob/wizardry/spell/Meteor.java @@ -1,12 +1,12 @@ package electroblob.wizardry.spell; import electroblob.wizardry.entity.EntityMeteor; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; @@ -18,7 +18,7 @@ public class Meteor extends SpellRay { public static final String BLAST_STRENGTH = "blast_strength"; public Meteor(){ - super("meteor", false, EnumAction.NONE); + super("meteor", false, SpellActions.POINT); this.soundValues(3, 1, 0); this.ignoreLivingEntities(true); addProperties(BLAST_STRENGTH); diff --git a/src/main/java/electroblob/wizardry/spell/MindControl.java b/src/main/java/electroblob/wizardry/spell/MindControl.java index 039b642c..7f9d27d2 100644 --- a/src/main/java/electroblob/wizardry/spell/MindControl.java +++ b/src/main/java/electroblob/wizardry/spell/MindControl.java @@ -2,6 +2,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.living.EntityEvilWizard; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.registry.WizardrySounds; @@ -15,7 +16,6 @@ import net.minecraft.entity.item.EntityArmorStand; import net.minecraft.entity.passive.EntitySheep; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; -import net.minecraft.item.EnumAction; import net.minecraft.item.EnumDyeColor; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.PotionEffect; @@ -40,7 +40,7 @@ public class MindControl extends SpellRay { public static final String NBT_KEY = "controllingEntity"; public MindControl(){ - super("mind_control", false, EnumAction.NONE); + super("mind_control", false, SpellActions.POINT); addProperties(EFFECT_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/MindTrick.java b/src/main/java/electroblob/wizardry/spell/MindTrick.java index 7aeb5cde..45abead1 100644 --- a/src/main/java/electroblob/wizardry/spell/MindTrick.java +++ b/src/main/java/electroblob/wizardry/spell/MindTrick.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.ParticleBuilder; @@ -11,7 +12,6 @@ import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -26,7 +26,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; public class MindTrick extends SpellRay { public MindTrick(){ - super("mind_trick", false, EnumAction.NONE); + super("mind_trick", false, SpellActions.POINT); this.soundValues(0.7f, 1, 0.4f); addProperties(EFFECT_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/Mine.java b/src/main/java/electroblob/wizardry/spell/Mine.java index d1f4a666..93e445de 100644 --- a/src/main/java/electroblob/wizardry/spell/Mine.java +++ b/src/main/java/electroblob/wizardry/spell/Mine.java @@ -4,6 +4,7 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.item.ISpellCastingItem; import electroblob.wizardry.item.ItemArtefact; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; @@ -15,7 +16,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; @@ -39,7 +39,7 @@ public class Mine extends SpellRay { } public Mine(){ - super("mine", false, EnumAction.NONE); + super("mine", false, SpellActions.POINT); this.ignoreLivingEntities(true); this.particleSpacing(0.5); } diff --git a/src/main/java/electroblob/wizardry/spell/Paralysis.java b/src/main/java/electroblob/wizardry/spell/Paralysis.java index 795a884a..490cac00 100644 --- a/src/main/java/electroblob/wizardry/spell/Paralysis.java +++ b/src/main/java/electroblob/wizardry/spell/Paralysis.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; @@ -13,7 +14,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -34,7 +34,7 @@ public class Paralysis extends SpellRay { private static final String CRITICAL_HEALTH = "critical_health"; public Paralysis(){ - super("paralysis", false, EnumAction.NONE); + super("paralysis", false, SpellActions.POINT); addProperties(DAMAGE, EFFECT_DURATION, CRITICAL_HEALTH); } diff --git a/src/main/java/electroblob/wizardry/spell/Petrify.java b/src/main/java/electroblob/wizardry/spell/Petrify.java index de5e6d4b..a6f05d4b 100644 --- a/src/main/java/electroblob/wizardry/spell/Petrify.java +++ b/src/main/java/electroblob/wizardry/spell/Petrify.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.block.BlockStatue; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; @@ -21,7 +21,7 @@ public class Petrify extends SpellRay { public static final String MINIMUM_EFFECT_DURATION = "minimum_effect_duration"; public Petrify(){ - super("petrify", false, EnumAction.NONE); + super("petrify", false, SpellActions.POINT); this.soundValues(1, 1.1f, 0.2f); addProperties(MINIMUM_EFFECT_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/PhaseStep.java b/src/main/java/electroblob/wizardry/spell/PhaseStep.java index 9f21eac8..8dbe25df 100644 --- a/src/main/java/electroblob/wizardry/spell/PhaseStep.java +++ b/src/main/java/electroblob/wizardry/spell/PhaseStep.java @@ -2,12 +2,12 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.RayTracer; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumParticleTypes; @@ -21,7 +21,7 @@ public class PhaseStep extends Spell { public static final String WALL_THICKNESS = "wall_thickness"; public PhaseStep(){ - super("phase_step", EnumAction.NONE, false); + super("phase_step", SpellActions.POINT, false); addProperties(RANGE, WALL_THICKNESS); } @@ -78,7 +78,6 @@ public class PhaseStep extends Spell { caster.setPositionAndUpdate(pos1.getX() + 0.5, pos1.getY() + 0.5, pos1.getZ() + 0.5); } - caster.swingArm(hand); this.playSound(world, caster, ticksInUse, -1, modifiers); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java b/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java index bf1c9d32..fc1e4501 100644 --- a/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java +++ b/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.*; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -9,7 +10,6 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumParticleTypes; @@ -20,7 +20,7 @@ import java.util.List; public class PlagueOfDarkness extends Spell { public PlagueOfDarkness(){ - super("plague_of_darkness", EnumAction.BOW, false); + super("plague_of_darkness", SpellActions.POINT_DOWN, false); addProperties(EFFECT_RADIUS, DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); soundValues(1, 1.1f, 0.2f); } diff --git a/src/main/java/electroblob/wizardry/spell/PocketFurnace.java b/src/main/java/electroblob/wizardry/spell/PocketFurnace.java index 29a2bdb8..8e24e4d6 100644 --- a/src/main/java/electroblob/wizardry/spell/PocketFurnace.java +++ b/src/main/java/electroblob/wizardry/spell/PocketFurnace.java @@ -2,10 +2,14 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Settings; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.*; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemSword; +import net.minecraft.item.ItemTool; import net.minecraft.item.crafting.FurnaceRecipes; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumParticleTypes; @@ -16,7 +20,7 @@ public class PocketFurnace extends Spell { public static final String ITEMS_SMELTED = "items_smelted"; public PocketFurnace(){ - super("pocket_furnace", EnumAction.BOW, false); + super("pocket_furnace", SpellActions.IMBUE, false); addProperties(ITEMS_SMELTED); soundValues(1, 0.75f, 0); } diff --git a/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java b/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java index fb4ea0d1..7341e885 100644 --- a/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java +++ b/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java @@ -2,16 +2,16 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.WizardryGuiHandler; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class PocketWorkbench extends Spell { public PocketWorkbench(){ - super("pocket_workbench", EnumAction.BOW, false); + super("pocket_workbench", SpellActions.IMBUE, false); } @Override diff --git a/src/main/java/electroblob/wizardry/spell/Poison.java b/src/main/java/electroblob/wizardry/spell/Poison.java index 0e747f0f..48a28e1d 100644 --- a/src/main/java/electroblob/wizardry/spell/Poison.java +++ b/src/main/java/electroblob/wizardry/spell/Poison.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -12,7 +13,6 @@ import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -23,7 +23,7 @@ import net.minecraft.world.World; public class Poison extends SpellRay { public Poison(){ - super("poison", false, EnumAction.NONE); + super("poison", false, SpellActions.POINT); this.soundValues(1, 1.1f, 0.2f); addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); } diff --git a/src/main/java/electroblob/wizardry/spell/Possession.java b/src/main/java/electroblob/wizardry/spell/Possession.java index c94d26dc..79d2c6f6 100644 --- a/src/main/java/electroblob/wizardry/spell/Possession.java +++ b/src/main/java/electroblob/wizardry/spell/Possession.java @@ -11,6 +11,7 @@ import electroblob.wizardry.data.WizardData; import electroblob.wizardry.entity.living.*; import electroblob.wizardry.entity.projectile.*; import electroblob.wizardry.integration.DamageSafetyChecker; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.packet.PacketControlInput; import electroblob.wizardry.packet.PacketPossession; import electroblob.wizardry.packet.WizardryPacketHandler; @@ -38,7 +39,6 @@ import net.minecraft.init.Enchantments; import net.minecraft.init.Items; import net.minecraft.init.PotionTypes; import net.minecraft.inventory.EntityEquipmentSlot; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemBow; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagList; @@ -121,7 +121,7 @@ public class Possession extends SpellRay { } public Possession(){ - super("possession", false, EnumAction.NONE); + super("possession", false, SpellActions.POINT); addProperties(EFFECT_DURATION, CRITICAL_HEALTH); } @@ -146,7 +146,7 @@ public class Possession extends SpellRay { if(!shootSpell(world, origin, look, caster, ticksInUse, modifiers)) return false; - if(casterSwingsArm(world, caster, hand, ticksInUse, modifiers)) caster.swingArm(hand); +// if(casterSwingsArm(world, caster, hand, ticksInUse, modifiers)) caster.swingArm(hand); this.playSound(world, caster, ticksInUse, -1, modifiers, "possess"); // TODO: There must be a better way... return true; } diff --git a/src/main/java/electroblob/wizardry/spell/RayOfPurification.java b/src/main/java/electroblob/wizardry/spell/RayOfPurification.java index 4367fd39..d1262e96 100644 --- a/src/main/java/electroblob/wizardry/spell/RayOfPurification.java +++ b/src/main/java/electroblob/wizardry/spell/RayOfPurification.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -11,7 +12,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundEvent; @@ -27,7 +27,7 @@ public class RayOfPurification extends SpellRay { public static final String UNDEAD_DAMAGE_MULTIPLIER = "undead_damage_multiplier"; public RayOfPurification(){ - super("ray_of_purification", true, EnumAction.NONE); + super("ray_of_purification", true, SpellActions.POINT); addProperties(DAMAGE, EFFECT_DURATION, BURN_DURATION, UNDEAD_DAMAGE_MULTIPLIER); } diff --git a/src/main/java/electroblob/wizardry/spell/Resurrection.java b/src/main/java/electroblob/wizardry/spell/Resurrection.java index a28d0fe9..f5511d56 100644 --- a/src/main/java/electroblob/wizardry/spell/Resurrection.java +++ b/src/main/java/electroblob/wizardry/spell/Resurrection.java @@ -2,6 +2,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.data.WizardData; import electroblob.wizardry.item.ISpellCastingItem; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.packet.PacketResurrection; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.Spells; @@ -10,7 +11,6 @@ import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumHand; import net.minecraft.util.math.MathHelper; @@ -28,7 +28,7 @@ public class Resurrection extends Spell { private static final String POTION_CORE_FIX_NBT_KEY = "Potion Core - Health Fix"; public Resurrection(){ - super("resurrection", EnumAction.NONE, false); + super("resurrection", SpellActions.SUMMON, false); addProperties(EFFECT_RADIUS, WAIT_TIME); } diff --git a/src/main/java/electroblob/wizardry/spell/Reversal.java b/src/main/java/electroblob/wizardry/spell/Reversal.java index c763fe91..3af6848a 100644 --- a/src/main/java/electroblob/wizardry/spell/Reversal.java +++ b/src/main/java/electroblob/wizardry/spell/Reversal.java @@ -1,12 +1,12 @@ package electroblob.wizardry.spell; import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.util.EnumFacing; @@ -24,7 +24,7 @@ public class Reversal extends SpellRay { public static final String REVERSED_EFFECTS = "reversed_effects"; public Reversal(){ - super("reversal", false, EnumAction.NONE); + super("reversal", false, SpellActions.POINT); addProperties(REVERSED_EFFECTS); } diff --git a/src/main/java/electroblob/wizardry/spell/Shockwave.java b/src/main/java/electroblob/wizardry/spell/Shockwave.java index 2d3d89c7..a7a0f6e0 100644 --- a/src/main/java/electroblob/wizardry/spell/Shockwave.java +++ b/src/main/java/electroblob/wizardry/spell/Shockwave.java @@ -2,6 +2,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.item.ItemArtefact; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.*; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -11,7 +12,6 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.EnumAction; import net.minecraft.network.play.server.SPacketEntityVelocity; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumParticleTypes; @@ -27,7 +27,7 @@ public class Shockwave extends Spell { private static final double EPICENTRE_RADIUS = 1; public Shockwave(){ - super("shockwave", EnumAction.BOW, false); + super("shockwave", SpellActions.POINT_DOWN, false); this.soundValues(2, 0.5f, 0); addProperties(BLAST_RADIUS, DAMAGE, MAX_REPULSION_VELOCITY); } diff --git a/src/main/java/electroblob/wizardry/spell/ShulkerBullet.java b/src/main/java/electroblob/wizardry/spell/ShulkerBullet.java index 7998a426..69599b6e 100644 --- a/src/main/java/electroblob/wizardry/spell/ShulkerBullet.java +++ b/src/main/java/electroblob/wizardry/spell/ShulkerBullet.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.NBTExtras; import electroblob.wizardry.util.SpellModifiers; @@ -10,7 +11,6 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityArmorStand; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.projectile.EntityShulkerBullet; -import net.minecraft.item.EnumAction; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTUtil; import net.minecraft.tileentity.TileEntityDispenser; @@ -26,7 +26,7 @@ import java.util.List; public class ShulkerBullet extends Spell { public ShulkerBullet(){ - super("shulker_bullet", EnumAction.NONE, false); + super("shulker_bullet", SpellActions.POINT_DOWN, false); this.soundValues(2, 1, 0.3f); addProperties(RANGE); } diff --git a/src/main/java/electroblob/wizardry/spell/SixthSense.java b/src/main/java/electroblob/wizardry/spell/SixthSense.java index e8f5f926..46eae1ac 100644 --- a/src/main/java/electroblob/wizardry/spell/SixthSense.java +++ b/src/main/java/electroblob/wizardry/spell/SixthSense.java @@ -2,11 +2,11 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; @@ -22,7 +22,7 @@ public class SixthSense extends Spell { public static final ResourceLocation SHADER = new ResourceLocation(Wizardry.MODID, "shaders/post/sixth_sense.json"); public SixthSense(){ - super("sixth_sense", EnumAction.BOW, false); + super("sixth_sense", SpellActions.POINT_UP, false); addProperties(EFFECT_DURATION, EFFECT_RADIUS); soundValues(1, 1.1f, 0.2f); } diff --git a/src/main/java/electroblob/wizardry/spell/Slime.java b/src/main/java/electroblob/wizardry/spell/Slime.java index 14bbfaf0..9497f838 100644 --- a/src/main/java/electroblob/wizardry/spell/Slime.java +++ b/src/main/java/electroblob/wizardry/spell/Slime.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.entity.living.EntityMagicSlime; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; @@ -10,7 +11,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.EntitySlime; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.math.BlockPos; @@ -21,7 +21,7 @@ import net.minecraft.world.World; public class Slime extends SpellRay { public Slime(){ - super("slime", false, EnumAction.NONE); + super("slime", false, SpellActions.POINT); addProperties(DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/Snare.java b/src/main/java/electroblob/wizardry/spell/Snare.java index 4b70e60d..9bf5a698 100644 --- a/src/main/java/electroblob/wizardry/spell/Snare.java +++ b/src/main/java/electroblob/wizardry/spell/Snare.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.tileentity.TileEntityPlayerSave; import electroblob.wizardry.util.ParticleBuilder; @@ -8,7 +9,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; @@ -17,7 +17,7 @@ import net.minecraft.world.World; public class Snare extends SpellRay { public Snare(){ - super("snare", false, EnumAction.NONE); + super("snare", false, SpellActions.POINT); this.soundValues(1, 1.4f, 0.4f); this.ignoreLivingEntities(true); addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); diff --git a/src/main/java/electroblob/wizardry/spell/SpectralPathway.java b/src/main/java/electroblob/wizardry/spell/SpectralPathway.java index adc6c73d..9c667462 100644 --- a/src/main/java/electroblob/wizardry/spell/SpectralPathway.java +++ b/src/main/java/electroblob/wizardry/spell/SpectralPathway.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.tileentity.TileEntityTimer; @@ -7,7 +8,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing.AxisDirection; import net.minecraft.util.EnumHand; @@ -20,7 +20,7 @@ public class SpectralPathway extends Spell { public static final String LENGTH = "length"; public SpectralPathway(){ - super("spectral_pathway", EnumAction.BOW, false); + super("spectral_pathway", SpellActions.POINT, false); addProperties(LENGTH, DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/SpeedTime.java b/src/main/java/electroblob/wizardry/spell/SpeedTime.java index 4692b176..8caaf5eb 100644 --- a/src/main/java/electroblob/wizardry/spell/SpeedTime.java +++ b/src/main/java/electroblob/wizardry/spell/SpeedTime.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.SpellModifiers; @@ -8,7 +9,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumHand; import net.minecraft.util.ITickable; @@ -32,7 +32,7 @@ public class SpeedTime extends Spell { public static final String EXTRA_TICKS = "extra_ticks"; public SpeedTime(){ - super("speed_time", EnumAction.BOW, true); + super("speed_time", SpellActions.POINT_UP, true); addProperties(EFFECT_RADIUS, TIME_INCREMENT, EXTRA_TICKS); } diff --git a/src/main/java/electroblob/wizardry/spell/SpellArrow.java b/src/main/java/electroblob/wizardry/spell/SpellArrow.java index ca26c045..4d967d65 100644 --- a/src/main/java/electroblob/wizardry/spell/SpellArrow.java +++ b/src/main/java/electroblob/wizardry/spell/SpellArrow.java @@ -4,13 +4,13 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.living.ISpellCaster; import electroblob.wizardry.entity.projectile.EntityMagicArrow; import electroblob.wizardry.entity.projectile.EntityMagicProjectile; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; @@ -56,7 +56,7 @@ public class SpellArrow extends Spell { } public SpellArrow(String modID, String name, Function arrowFactory){ - super(modID, name, EnumAction.NONE, false); + super(modID, name, SpellActions.POINT, false); this.arrowFactory = arrowFactory; this.addProperties(RANGE); this.npcSelector((e, o) -> true); @@ -101,8 +101,6 @@ public class SpellArrow extends Spell { // Spawns the projectile in the world world.spawnEntity(projectile); } - - caster.swingArm(hand); this.playSound(world, caster, ticksInUse, -1, modifiers); diff --git a/src/main/java/electroblob/wizardry/spell/SpellBuff.java b/src/main/java/electroblob/wizardry/spell/SpellBuff.java index 2e3faec2..b39e1549 100644 --- a/src/main/java/electroblob/wizardry/spell/SpellBuff.java +++ b/src/main/java/electroblob/wizardry/spell/SpellBuff.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; @@ -8,7 +9,6 @@ import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; import net.minecraft.tileentity.TileEntityDispenser; @@ -63,7 +63,7 @@ public class SpellBuff extends Spell { @SafeVarargs public SpellBuff(String modID, String name, float r, float g, float b, Supplier... effects){ - super(modID, name, EnumAction.BOW, false); + super(modID, name, SpellActions.POINT_UP, false); this.effects = effects; this.r = r; this.g = g; diff --git a/src/main/java/electroblob/wizardry/spell/SpellConjuration.java b/src/main/java/electroblob/wizardry/spell/SpellConjuration.java index 1d86c527..88034e57 100644 --- a/src/main/java/electroblob/wizardry/spell/SpellConjuration.java +++ b/src/main/java/electroblob/wizardry/spell/SpellConjuration.java @@ -2,6 +2,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.item.IConjuredItem; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumHand; @@ -46,7 +46,7 @@ public class SpellConjuration extends Spell { } public SpellConjuration(String modID, String name, Item item){ - super(modID, name, EnumAction.BOW, false); + super(modID, name, SpellActions.IMBUE, false); this.item = item; addProperties(ITEM_LIFETIME); } diff --git a/src/main/java/electroblob/wizardry/spell/SpellConstructRanged.java b/src/main/java/electroblob/wizardry/spell/SpellConstructRanged.java index 04b5059c..2ef4670a 100644 --- a/src/main/java/electroblob/wizardry/spell/SpellConstructRanged.java +++ b/src/main/java/electroblob/wizardry/spell/SpellConstructRanged.java @@ -2,6 +2,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.construct.EntityMagicConstruct; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.RayTracer; import electroblob.wizardry.util.SpellModifiers; @@ -9,7 +10,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; @@ -53,7 +53,7 @@ public class SpellConstructRanged extends SpellC } public SpellConstructRanged(String modID, String name, Function constructFactory, boolean permanent){ - super(modID, name, EnumAction.NONE, constructFactory, permanent); + super(modID, name, SpellActions.POINT, constructFactory, permanent); this.addProperties(RANGE); this.npcSelector((e, o) -> true); } @@ -118,8 +118,7 @@ public class SpellConstructRanged extends SpellC }else{ return false; } - - caster.swingArm(hand); + this.playSound(world, caster, ticksInUse, -1, modifiers); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SpellMinion.java b/src/main/java/electroblob/wizardry/spell/SpellMinion.java index 21dcf505..c9e8b1ed 100644 --- a/src/main/java/electroblob/wizardry/spell/SpellMinion.java +++ b/src/main/java/electroblob/wizardry/spell/SpellMinion.java @@ -2,6 +2,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.living.ISummonedCreature; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; @@ -13,7 +14,6 @@ import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.ai.attributes.IAttributeInstance; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; @@ -64,7 +64,7 @@ public class SpellMinion extends Spe } public SpellMinion(String modID, String name, Function minionFactory){ - super(modID, name, EnumAction.BOW, false); + super(modID, name, SpellActions.SUMMON, false); this.minionFactory = minionFactory; addProperties(MINION_LIFETIME, MINION_COUNT, SUMMON_RADIUS); this.npcSelector((e, o) -> true); diff --git a/src/main/java/electroblob/wizardry/spell/SpellRay.java b/src/main/java/electroblob/wizardry/spell/SpellRay.java index df5d4ab4..123b655c 100644 --- a/src/main/java/electroblob/wizardry/spell/SpellRay.java +++ b/src/main/java/electroblob/wizardry/spell/SpellRay.java @@ -161,10 +161,13 @@ public abstract class SpellRay extends Spell { Vec3d look = caster.getLookVec(); Vec3d origin = new Vec3d(caster.posX, caster.getEntityBoundingBox().minY + caster.getEyeHeight() - Y_OFFSET, caster.posZ); - + if(!this.isContinuous && world.isRemote && !Wizardry.proxy.isFirstPerson(caster)){ + origin = origin.add(look.scale(1.2)); + } + if(!shootSpell(world, origin, look, caster, ticksInUse, modifiers)) return false; - if(casterSwingsArm(world, caster, hand, ticksInUse, modifiers)) caster.swingArm(hand); + //if(casterSwingsArm(world, caster, hand, ticksInUse, modifiers)) caster.swingArm(hand); this.playSound(world, caster, ticksInUse, -1, modifiers); return true; } @@ -249,7 +252,7 @@ public abstract class SpellRay extends Spell { protected boolean casterSwingsArm(World world, EntityLivingBase caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ return !this.isContinuous; } - + /** Takes care of the shared stuff for the three casting methods. This is mainly for internal use. */ protected boolean shootSpell(World world, Vec3d origin, Vec3d direction, @Nullable EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){ @@ -267,6 +270,7 @@ public abstract class SpellRay extends Spell { // Doesn't matter which way round these are, they're mutually exclusive if(rayTrace.typeOfHit == RayTraceResult.Type.ENTITY){ // Do whatever the spell does when it hits an entity + // FIXME: Some spells (e.g. lightning web) seem to not render when aimed at item frames flag = onEntityHit(world, rayTrace.entityHit, rayTrace.hitVec, caster, origin, ticksInUse, modifiers); // If the spell succeeded, clip the particles to the correct distance so they don't go through the entity if(flag) range = origin.distanceTo(rayTrace.hitVec); diff --git a/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java b/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java index 9feaca7d..fc7329f4 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java +++ b/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java @@ -1,12 +1,12 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.monster.EntityIronGolem; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -14,7 +14,7 @@ import net.minecraft.world.World; public class SummonIronGolem extends Spell { public SummonIronGolem(){ - super("summon_iron_golem", EnumAction.BOW, false); + super("summon_iron_golem", SpellActions.SUMMON, false); addProperties(SpellMinion.SUMMON_RADIUS); soundValues(1, 1.1f, 0.2f); } diff --git a/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java b/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java index 8e7c9476..67b5abe5 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java +++ b/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java @@ -1,13 +1,13 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.monster.EntitySnowman; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -15,7 +15,7 @@ import net.minecraft.world.World; public class SummonSnowGolem extends Spell { public SummonSnowGolem(){ - super("summon_snow_golem", EnumAction.BOW, false); + super("summon_snow_golem", SpellActions.SUMMON, false); this.soundValues(1, 1, 0.4f); addProperties(SpellMinion.SUMMON_RADIUS); } diff --git a/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java b/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java index a9f02984..8e775f51 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java +++ b/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java @@ -4,6 +4,7 @@ import electroblob.wizardry.data.IStoredVariable; import electroblob.wizardry.data.Persistence; import electroblob.wizardry.data.WizardData; import electroblob.wizardry.entity.living.EntitySpiritHorse; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities.Operations; @@ -13,7 +14,6 @@ import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.ai.attributes.IAttribute; import net.minecraft.entity.passive.AbstractHorse; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -36,7 +36,7 @@ public class SummonSpiritHorse extends Spell { public static final IStoredVariable UUID_KEY = IStoredVariable.StoredVariable.ofUUID("spiritHorseUUID", Persistence.ALWAYS); public SummonSpiritHorse(){ - super("summon_spirit_horse", EnumAction.BOW, false); + super("summon_spirit_horse", SpellActions.SUMMON, false); addProperties(SpellMinion.SUMMON_RADIUS); soundValues(0.7f, 1.2f, 0.4f); WizardData.registerStoredVariables(UUID_KEY); diff --git a/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java b/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java index f4a8d6eb..59c7286c 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java +++ b/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java @@ -4,6 +4,7 @@ import electroblob.wizardry.data.IStoredVariable; import electroblob.wizardry.data.Persistence; import electroblob.wizardry.data.WizardData; import electroblob.wizardry.entity.living.EntitySpiritWolf; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities.Operations; @@ -11,7 +12,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -26,7 +26,7 @@ public class SummonSpiritWolf extends Spell { public static final IStoredVariable UUID_KEY = IStoredVariable.StoredVariable.ofUUID("spiritWolfUUID", Persistence.ALWAYS); public SummonSpiritWolf(){ - super("summon_spirit_wolf", EnumAction.BOW, false); + super("summon_spirit_wolf", SpellActions.SUMMON, false); addProperties(SpellMinion.SUMMON_RADIUS); soundValues(0.7f, 1.2f, 0.4f); WizardData.registerStoredVariables(UUID_KEY); diff --git a/src/main/java/electroblob/wizardry/spell/Telekinesis.java b/src/main/java/electroblob/wizardry/spell/Telekinesis.java index 9d97aaa6..507a2f2f 100644 --- a/src/main/java/electroblob/wizardry/spell/Telekinesis.java +++ b/src/main/java/electroblob/wizardry/spell/Telekinesis.java @@ -1,13 +1,13 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; @@ -18,7 +18,7 @@ import net.minecraft.world.World; public class Telekinesis extends SpellRay { public Telekinesis(){ - super("telekinesis", false, EnumAction.NONE); + super("telekinesis", false, SpellActions.POINT); } @Override public boolean requiresPacket(){ return false; } diff --git a/src/main/java/electroblob/wizardry/spell/Thunderstorm.java b/src/main/java/electroblob/wizardry/spell/Thunderstorm.java index 05e7884f..4b5bd2a6 100644 --- a/src/main/java/electroblob/wizardry/spell/Thunderstorm.java +++ b/src/main/java/electroblob/wizardry/spell/Thunderstorm.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.*; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -8,7 +9,6 @@ import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.effect.EntityLightningBolt; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; @@ -32,7 +32,7 @@ public class Thunderstorm extends Spell { private static final float CENTRE_RADIUS_FRACTION = 0.5f; public Thunderstorm(){ - super("thunderstorm", EnumAction.BOW, false); + super("thunderstorm", SpellActions.POINT_UP, false); this.soundValues(1, 1.7f, 0.2f); addProperties(EFFECT_RADIUS, LIGHTNING_BOLTS, SECONDARY_DAMAGE, TERTIARY_DAMAGE, SECONDARY_RANGE, TERTIARY_RANGE, SECONDARY_MAX_TARGETS, TERTIARY_MAX_TARGETS); diff --git a/src/main/java/electroblob/wizardry/spell/Tornado.java b/src/main/java/electroblob/wizardry/spell/Tornado.java index b79d3f0b..b9378c6f 100644 --- a/src/main/java/electroblob/wizardry/spell/Tornado.java +++ b/src/main/java/electroblob/wizardry/spell/Tornado.java @@ -1,9 +1,9 @@ package electroblob.wizardry.spell; import electroblob.wizardry.entity.construct.EntityTornado; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.Vec3d; @@ -13,7 +13,7 @@ public class Tornado extends SpellConstruct { public static final String UPWARD_ACCELERATION = "upward_acceleration"; public Tornado(){ - super("tornado", EnumAction.NONE, EntityTornado::new, false); + super("tornado", SpellActions.POINT, EntityTornado::new, false); addProperties(EFFECT_RADIUS, SPEED, DAMAGE, UPWARD_ACCELERATION); } diff --git a/src/main/java/electroblob/wizardry/spell/Transience.java b/src/main/java/electroblob/wizardry/spell/Transience.java index e20f1ac5..e6f17353 100644 --- a/src/main/java/electroblob/wizardry/spell/Transience.java +++ b/src/main/java/electroblob/wizardry/spell/Transience.java @@ -1,13 +1,13 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumHand; @@ -26,7 +26,7 @@ public class Transience extends Spell { public static final ResourceLocation SHADER = new ResourceLocation(Wizardry.MODID, "shaders/post/transience.json"); public Transience(){ - super("transience", EnumAction.BOW, false); + super("transience", SpellActions.POINT_UP, false); addProperties(EFFECT_DURATION); } diff --git a/src/main/java/electroblob/wizardry/spell/Transportation.java b/src/main/java/electroblob/wizardry/spell/Transportation.java index 07e7d9a6..2992b06c 100644 --- a/src/main/java/electroblob/wizardry/spell/Transportation.java +++ b/src/main/java/electroblob/wizardry/spell/Transportation.java @@ -5,6 +5,7 @@ import electroblob.wizardry.data.IStoredVariable; import electroblob.wizardry.data.Persistence; import electroblob.wizardry.data.WizardData; import electroblob.wizardry.item.ItemArtefact; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.packet.PacketTransportation; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.WizardryItems; @@ -14,7 +15,6 @@ import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; -import net.minecraft.item.EnumAction; import net.minecraft.nbt.NBTTagList; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumHand; @@ -41,7 +41,7 @@ public class Transportation extends Spell { public static final IStoredVariable COUNTDOWN_KEY = IStoredVariable.StoredVariable.ofInt("tpCountdown", Persistence.NEVER).withTicker(Transportation::update); public Transportation(){ - super("transportation", EnumAction.BOW, false); + super("transportation", SpellActions.POINT_UP, false); addProperties(TELEPORT_COUNTDOWN); WizardData.registerStoredVariables(LOCATIONS_KEY, COUNTDOWN_KEY); } diff --git a/src/main/java/electroblob/wizardry/spell/VanishingBox.java b/src/main/java/electroblob/wizardry/spell/VanishingBox.java index 471450de..06c57933 100644 --- a/src/main/java/electroblob/wizardry/spell/VanishingBox.java +++ b/src/main/java/electroblob/wizardry/spell/VanishingBox.java @@ -1,16 +1,16 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.InventoryEnderChest; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class VanishingBox extends Spell { public VanishingBox(){ - super("vanishing_box", EnumAction.BOW, false); + super("vanishing_box", SpellActions.POINT_UP, false); } @Override public boolean requiresPacket(){ return false; } diff --git a/src/main/java/electroblob/wizardry/spell/WallOfFrost.java b/src/main/java/electroblob/wizardry/spell/WallOfFrost.java index 2c059bef..8bdc50a7 100644 --- a/src/main/java/electroblob/wizardry/spell/WallOfFrost.java +++ b/src/main/java/electroblob/wizardry/spell/WallOfFrost.java @@ -1,6 +1,7 @@ package electroblob.wizardry.spell; import electroblob.wizardry.block.BlockStatue; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardrySounds; @@ -11,7 +12,6 @@ import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.item.EnumAction; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; @@ -23,7 +23,7 @@ public class WallOfFrost extends SpellRay { private static final int MINIMUM_PLACEMENT_RANGE = 2; public WallOfFrost(){ - super("wall_of_frost", true, EnumAction.NONE); + super("wall_of_frost", true, SpellActions.POINT); this.particleVelocity(1); this.particleSpacing(0.5); addProperties(DURATION); diff --git a/src/main/java/electroblob/wizardry/spell/Whirlwind.java b/src/main/java/electroblob/wizardry/spell/Whirlwind.java index 733c891a..772d9a97 100644 --- a/src/main/java/electroblob/wizardry/spell/Whirlwind.java +++ b/src/main/java/electroblob/wizardry/spell/Whirlwind.java @@ -2,13 +2,13 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.item.ItemArtefact; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.EnumAction; import net.minecraft.network.play.server.SPacketEntityVelocity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; @@ -22,7 +22,7 @@ public class Whirlwind extends SpellRay { public static final String REPULSION_VELOCITY = "repulsion_velocity"; public Whirlwind(){ - super("whirlwind", false, EnumAction.NONE); + super("whirlwind", false, SpellActions.POINT); this.soundValues(0.8f, 0.7f, 0.2f); addProperties(REPULSION_VELOCITY); } diff --git a/src/main/java/electroblob/wizardry/spell/Wither.java b/src/main/java/electroblob/wizardry/spell/Wither.java index f649d9b8..0b1667b0 100644 --- a/src/main/java/electroblob/wizardry/spell/Wither.java +++ b/src/main/java/electroblob/wizardry/spell/Wither.java @@ -1,5 +1,6 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; @@ -11,7 +12,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; -import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -22,7 +22,7 @@ import net.minecraft.world.World; public class Wither extends SpellRay { public Wither(){ - super("wither", false, EnumAction.NONE); + super("wither", false, SpellActions.POINT); this.soundValues(1, 1.1f, 0.2f); addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); } diff --git a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java index 870803a9..4d899a5f 100644 --- a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java +++ b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java @@ -37,6 +37,7 @@ import net.minecraftforge.event.ForgeEventFactory; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; import javax.annotation.Nullable; +import java.lang.reflect.Field; import java.util.*; import java.util.function.BiPredicate; import java.util.function.Predicate; @@ -793,6 +794,21 @@ public final class WizardryUtilities { return hotbar; } + /** Returns which {@link EnumHandSide} the given {@link EnumHand} is on for the given entity. */ + public static EnumHandSide getSideForHand(EntityLivingBase entity, EnumHand hand){ + return hand == EnumHand.MAIN_HAND ? entity.getPrimaryHand() : entity.getPrimaryHand().opposite(); + } + + /** Returns which {@link EnumHand} is on the given {@link EnumHandSide} for the given entity. */ + public static EnumHand getHandForSide(EntityLivingBase entity, EnumHandSide side){ + return side == entity.getPrimaryHand() ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND; + } + + /** Returns the opposite {@link EnumHand} to the one given. */ + public static EnumHand getOpposite(EnumHand hand){ + return hand == EnumHand.OFF_HAND ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND; + } + /** * Tests whether the specified player has any of the specified item in their entire inventory, including armour * slots and offhand. @@ -1008,4 +1024,15 @@ public final class WizardryUtilities { // return iterator.next(); // } + /** + * Returns a list of all fields belonging to the given class and all those belonging to all of its superclasses. + * @param c The class to query + * @return The resulting {@link List} of fields + */ + public static List getAllFields(Class c){ + List fields = new ArrayList<>(Arrays.asList(c.getDeclaredFields())); + if(c.getSuperclass() != null) fields.addAll(getAllFields(c.getSuperclass())); + return fields; + } + } diff --git a/src/main/resources/assets/ebwizardry/models/item/advanced_earth_wand.json b/src/main/resources/assets/ebwizardry/models/item/advanced_earth_wand.json index cf039f84..c096a218 100644 --- a/src/main/resources/assets/ebwizardry/models/item/advanced_earth_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/advanced_earth_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_advanced_earth" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/advanced_fire_wand.json b/src/main/resources/assets/ebwizardry/models/item/advanced_fire_wand.json index a71872f0..e3ee972b 100644 --- a/src/main/resources/assets/ebwizardry/models/item/advanced_fire_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/advanced_fire_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_advanced_fire" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/advanced_healing_wand.json b/src/main/resources/assets/ebwizardry/models/item/advanced_healing_wand.json index bf0096f6..0854d2a9 100644 --- a/src/main/resources/assets/ebwizardry/models/item/advanced_healing_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/advanced_healing_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_advanced_healing" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/advanced_ice_wand.json b/src/main/resources/assets/ebwizardry/models/item/advanced_ice_wand.json index 0c4eccd5..a3c64272 100644 --- a/src/main/resources/assets/ebwizardry/models/item/advanced_ice_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/advanced_ice_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_advanced_ice" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/advanced_lightning_wand.json b/src/main/resources/assets/ebwizardry/models/item/advanced_lightning_wand.json index 7be6ebe9..bfd46bfd 100644 --- a/src/main/resources/assets/ebwizardry/models/item/advanced_lightning_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/advanced_lightning_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_advanced_lightning" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/advanced_necromancy_wand.json b/src/main/resources/assets/ebwizardry/models/item/advanced_necromancy_wand.json index 6f0856fe..8604ea87 100644 --- a/src/main/resources/assets/ebwizardry/models/item/advanced_necromancy_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/advanced_necromancy_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_advanced_necromancy" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/advanced_sorcery_wand.json b/src/main/resources/assets/ebwizardry/models/item/advanced_sorcery_wand.json index 73de0258..32b308f6 100644 --- a/src/main/resources/assets/ebwizardry/models/item/advanced_sorcery_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/advanced_sorcery_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_advanced_sorcery" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/advanced_wand.json b/src/main/resources/assets/ebwizardry/models/item/advanced_wand.json index ceb0b2e0..2852b87f 100644 --- a/src/main/resources/assets/ebwizardry/models/item/advanced_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/advanced_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_advanced" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/apprentice_earth_wand.json b/src/main/resources/assets/ebwizardry/models/item/apprentice_earth_wand.json index dc0c9748..448931cc 100644 --- a/src/main/resources/assets/ebwizardry/models/item/apprentice_earth_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/apprentice_earth_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_apprentice_earth" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/apprentice_fire_wand.json b/src/main/resources/assets/ebwizardry/models/item/apprentice_fire_wand.json index 49568c5b..12f60681 100644 --- a/src/main/resources/assets/ebwizardry/models/item/apprentice_fire_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/apprentice_fire_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_apprentice_fire" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/apprentice_healing_wand.json b/src/main/resources/assets/ebwizardry/models/item/apprentice_healing_wand.json index bdfd6ceb..3478e831 100644 --- a/src/main/resources/assets/ebwizardry/models/item/apprentice_healing_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/apprentice_healing_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_apprentice_healing" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/apprentice_ice_wand.json b/src/main/resources/assets/ebwizardry/models/item/apprentice_ice_wand.json index 9f83e3e1..a3fb8819 100644 --- a/src/main/resources/assets/ebwizardry/models/item/apprentice_ice_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/apprentice_ice_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_apprentice_ice" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/apprentice_lightning_wand.json b/src/main/resources/assets/ebwizardry/models/item/apprentice_lightning_wand.json index ad2565e1..8c28070c 100644 --- a/src/main/resources/assets/ebwizardry/models/item/apprentice_lightning_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/apprentice_lightning_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_apprentice_lightning" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/apprentice_necromancy_wand.json b/src/main/resources/assets/ebwizardry/models/item/apprentice_necromancy_wand.json index 3750104a..d5f28f6f 100644 --- a/src/main/resources/assets/ebwizardry/models/item/apprentice_necromancy_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/apprentice_necromancy_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_apprentice_necromancy" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/apprentice_sorcery_wand.json b/src/main/resources/assets/ebwizardry/models/item/apprentice_sorcery_wand.json index debce2e4..dd4169be 100644 --- a/src/main/resources/assets/ebwizardry/models/item/apprentice_sorcery_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/apprentice_sorcery_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_apprentice_sorcery" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/apprentice_wand.json b/src/main/resources/assets/ebwizardry/models/item/apprentice_wand.json index d3baa0fe..c35d3d43 100644 --- a/src/main/resources/assets/ebwizardry/models/item/apprentice_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/apprentice_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_apprentice" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/festive_wand.json b/src/main/resources/assets/ebwizardry/models/item/festive_wand.json index b31af7dd..5ce21ed9 100644 --- a/src/main/resources/assets/ebwizardry/models/item/festive_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/festive_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_festive" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/magic_wand.json b/src/main/resources/assets/ebwizardry/models/item/magic_wand.json index 59387cf7..04e194f2 100644 --- a/src/main/resources/assets/ebwizardry/models/item/magic_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/magic_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_novice" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/master_earth_wand.json b/src/main/resources/assets/ebwizardry/models/item/master_earth_wand.json index 65b2b9a5..d8c92322 100644 --- a/src/main/resources/assets/ebwizardry/models/item/master_earth_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/master_earth_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_master_earth" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/master_fire_wand.json b/src/main/resources/assets/ebwizardry/models/item/master_fire_wand.json index a66856d3..0055168f 100644 --- a/src/main/resources/assets/ebwizardry/models/item/master_fire_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/master_fire_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_master_fire" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/master_healing_wand.json b/src/main/resources/assets/ebwizardry/models/item/master_healing_wand.json index 3f4c37cb..e7882b9a 100644 --- a/src/main/resources/assets/ebwizardry/models/item/master_healing_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/master_healing_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_master_healing" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/master_ice_wand.json b/src/main/resources/assets/ebwizardry/models/item/master_ice_wand.json index 5f98c5b5..803b8bb0 100644 --- a/src/main/resources/assets/ebwizardry/models/item/master_ice_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/master_ice_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_master_ice" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/master_lightning_wand.json b/src/main/resources/assets/ebwizardry/models/item/master_lightning_wand.json index 14659d01..5c480232 100644 --- a/src/main/resources/assets/ebwizardry/models/item/master_lightning_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/master_lightning_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_master_lightning" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/master_necromancy_wand.json b/src/main/resources/assets/ebwizardry/models/item/master_necromancy_wand.json index ebf1ae67..57165e31 100644 --- a/src/main/resources/assets/ebwizardry/models/item/master_necromancy_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/master_necromancy_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_master_necromancy" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/master_sorcery_wand.json b/src/main/resources/assets/ebwizardry/models/item/master_sorcery_wand.json index eabb2af3..47d27356 100644 --- a/src/main/resources/assets/ebwizardry/models/item/master_sorcery_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/master_sorcery_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_master_sorcery" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/master_wand.json b/src/main/resources/assets/ebwizardry/models/item/master_wand.json index c0c29b76..b298e38b 100644 --- a/src/main/resources/assets/ebwizardry/models/item/master_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/master_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_master" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/novice_earth_wand.json b/src/main/resources/assets/ebwizardry/models/item/novice_earth_wand.json index 7431443b..f893f16a 100644 --- a/src/main/resources/assets/ebwizardry/models/item/novice_earth_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/novice_earth_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_novice_earth" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/novice_fire_wand.json b/src/main/resources/assets/ebwizardry/models/item/novice_fire_wand.json index 27055741..15bab228 100644 --- a/src/main/resources/assets/ebwizardry/models/item/novice_fire_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/novice_fire_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_novice_fire" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/novice_healing_wand.json b/src/main/resources/assets/ebwizardry/models/item/novice_healing_wand.json index e9095bf3..4f9d16c5 100644 --- a/src/main/resources/assets/ebwizardry/models/item/novice_healing_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/novice_healing_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_novice_healing" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/novice_ice_wand.json b/src/main/resources/assets/ebwizardry/models/item/novice_ice_wand.json index feb24c4c..a60b71b2 100644 --- a/src/main/resources/assets/ebwizardry/models/item/novice_ice_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/novice_ice_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_novice_ice" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/novice_lightning_wand.json b/src/main/resources/assets/ebwizardry/models/item/novice_lightning_wand.json index ce044666..925a23a4 100644 --- a/src/main/resources/assets/ebwizardry/models/item/novice_lightning_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/novice_lightning_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_novice_lightning" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/novice_necromancy_wand.json b/src/main/resources/assets/ebwizardry/models/item/novice_necromancy_wand.json index ab64fb68..5f1a07d0 100644 --- a/src/main/resources/assets/ebwizardry/models/item/novice_necromancy_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/novice_necromancy_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_novice_necromancy" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/novice_sorcery_wand.json b/src/main/resources/assets/ebwizardry/models/item/novice_sorcery_wand.json index 968ff38e..7db5be49 100644 --- a/src/main/resources/assets/ebwizardry/models/item/novice_sorcery_wand.json +++ b/src/main/resources/assets/ebwizardry/models/item/novice_sorcery_wand.json @@ -2,5 +2,13 @@ "parent": "item/handheld", "textures": { "layer0": "ebwizardry:items/wand_novice_sorcery" - } + }, + "overrides": [ + { + "predicate": { + "this_gets_ignored": 1 + }, + "model": "ebwizardry:item/override_generators/wand" + } + ] } diff --git a/src/main/resources/assets/ebwizardry/models/item/override_generators/wand.json b/src/main/resources/assets/ebwizardry/models/item/override_generators/wand.json new file mode 100644 index 00000000..bcdf7d94 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/override_generators/wand.json @@ -0,0 +1,11 @@ +{ + "parent": "item/handheld", + "overrides": [ + { + "predicate": { + "pointing": 1 + }, + "model": "ebwizardry:item/wand_point" + } + ] +} diff --git a/src/main/resources/assets/ebwizardry/models/item/wand_point.json b/src/main/resources/assets/ebwizardry/models/item/wand_point.json new file mode 100644 index 00000000..67d781b2 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/wand_point.json @@ -0,0 +1,25 @@ +{ + "parent": "item/handheld", + "display": { + "thirdperson_righthand": { + "rotation": [0, -90, 105], + "translation": [0, 0, -3.25], + "scale": [0.85, 0.85, 0.85] + }, + "thirdperson_lefthand": { + "rotation": [0, 90, -105], + "translation": [0, 0, -3.25], + "scale": [0.85, 0.85, 0.85] + }, + "firstperson_righthand": { + "rotation": [0, -90, 105], + "translation": [1.13, 3.2, 0.5], + "scale": [0.68, 0.68, 0.68] + }, + "firstperson_lefthand": { + "rotation": [0, 90, -105], + "translation": [1.13, 3.2, 0.5], + "scale": [0.68, 0.68, 0.68] + } + } +} From 1f9f5a1cc5d6e2a565fd595db4d1754898d9f9ac Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 10 May 2020 11:10:52 +0100 Subject: [PATCH 092/415] Add spectral dust --- .../wizardry/item/ItemSpectralDust.java | 39 ++++++++++++++++++ .../wizardry/registry/WizardryItems.java | 8 +++- .../assets/ebwizardry/lang/en_gb.lang | 2 + .../assets/ebwizardry/lang/en_us.lang | 2 + .../models/item/spectral_dust_earth.json | 6 +++ .../models/item/spectral_dust_fire.json | 6 +++ .../models/item/spectral_dust_healing.json | 6 +++ .../models/item/spectral_dust_ice.json | 6 +++ .../models/item/spectral_dust_lightning.json | 6 +++ .../models/item/spectral_dust_necromancy.json | 6 +++ .../models/item/spectral_dust_sorcery.json | 6 +++ .../textures/items/spectral_dust_earth.png | Bin 0 -> 2214 bytes .../textures/items/spectral_dust_fire.png | Bin 0 -> 2127 bytes .../textures/items/spectral_dust_healing.png | Bin 0 -> 2164 bytes .../textures/items/spectral_dust_ice.png | Bin 0 -> 2238 bytes .../items/spectral_dust_lightning.png | Bin 0 -> 2197 bytes .../items/spectral_dust_necromancy.png | Bin 0 -> 2265 bytes .../textures/items/spectral_dust_sorcery.png | Bin 0 -> 2213 bytes 18 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/item/ItemSpectralDust.java create mode 100644 src/main/resources/assets/ebwizardry/models/item/spectral_dust_earth.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/spectral_dust_fire.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/spectral_dust_healing.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/spectral_dust_ice.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/spectral_dust_lightning.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/spectral_dust_necromancy.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/spectral_dust_sorcery.json create mode 100644 src/main/resources/assets/ebwizardry/textures/items/spectral_dust_earth.png create mode 100644 src/main/resources/assets/ebwizardry/textures/items/spectral_dust_fire.png create mode 100644 src/main/resources/assets/ebwizardry/textures/items/spectral_dust_healing.png create mode 100644 src/main/resources/assets/ebwizardry/textures/items/spectral_dust_ice.png create mode 100644 src/main/resources/assets/ebwizardry/textures/items/spectral_dust_lightning.png create mode 100644 src/main/resources/assets/ebwizardry/textures/items/spectral_dust_necromancy.png create mode 100644 src/main/resources/assets/ebwizardry/textures/items/spectral_dust_sorcery.png diff --git a/src/main/java/electroblob/wizardry/item/ItemSpectralDust.java b/src/main/java/electroblob/wizardry/item/ItemSpectralDust.java new file mode 100644 index 00000000..30b6504e --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemSpectralDust.java @@ -0,0 +1,39 @@ +package electroblob.wizardry.item; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.registry.WizardryTabs; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; + +import java.util.Arrays; + +public class ItemSpectralDust extends Item implements IMultiTexturedItem { + + public ItemSpectralDust(){ + super(); + this.setHasSubtypes(true); + this.setMaxDamage(0); + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + + @Override + public ResourceLocation getModelName(ItemStack stack){ + int metadata = stack.getMetadata(); + if(metadata >= Element.values().length) metadata = 0; + return new ResourceLocation(Wizardry.MODID, "spectral_dust_" + Element.values()[metadata].getName()); + } + + @Override + public void getSubItems(CreativeTabs tab, NonNullList items){ + if(tab == WizardryTabs.WIZARDRY){ + for(Element element : Arrays.copyOfRange(Element.values(), 1, Element.values().length)){ + items.add(new ItemStack(this, 1, element.ordinal())); + } + } + } + +} diff --git a/src/main/java/electroblob/wizardry/registry/WizardryItems.java b/src/main/java/electroblob/wizardry/registry/WizardryItems.java index e717d7f8..bc998db7 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryItems.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryItems.java @@ -154,6 +154,8 @@ public final class WizardryItems { public static final Item smoke_bomb = placeholder(); public static final Item spark_bomb = placeholder(); + public static final Item spectral_dust = placeholder(); + public static final Item wizard_hat = placeholder(); public static final Item wizard_robe = placeholder(); public static final Item wizard_leggings = placeholder(); @@ -500,6 +502,8 @@ public final class WizardryItems { registerItem(registry, "smoke_bomb", new ItemSmokeBomb()); registerItem(registry, "spark_bomb", new ItemSparkBomb()); + registerItem(registry, "spectral_dust", new ItemSpectralDust()); + registerItem(registry, "wizard_hat", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.HEAD, null), true); registerItem(registry, "wizard_robe", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.CHEST, null)); registerItem(registry, "wizard_leggings", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, null)); @@ -540,8 +544,8 @@ public final class WizardryItems { registerItem(registry, "wizard_leggings_healing", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.LEGS, Element.HEALING)); registerItem(registry, "wizard_boots_healing", new ItemWizardArmour(Materials.SILK, 1, EntityEquipmentSlot.FEET, Element.HEALING)); - registerItem(registry, "spectral_helmet", new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.HEAD)); - registerItem(registry, "spectral_chestplate", new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.CHEST)); + registerItem(registry, "spectral_helmet", new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.HEAD)); + registerItem(registry, "spectral_chestplate", new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.CHEST)); registerItem(registry, "spectral_leggings", new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.LEGS)); registerItem(registry, "spectral_boots", new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.FEET)); diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index ffc3116e..1cca607e 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -198,6 +198,8 @@ item.ebwizardry\:armour_upgrade.desc_extended=Apply this upgrade to a piece of w item.ebwizardry\:magic_silk.name=Magical Silk +item.ebwizardry\:spectral_dust.name=Spectral Dust + item.ebwizardry\:wizard_armour.legendary=Legendary item.ebwizardry\:wizard_armour.buff=-%1$s %2$s cost item.ebwizardry\:wizard_armour.mana=Mana\: %1$s/%2$s diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 7b5d46f9..66fafa64 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -198,6 +198,8 @@ item.ebwizardry\:armour_upgrade.desc_extended=Apply this upgrade to a piece of w item.ebwizardry\:magic_silk.name=Magical Silk +item.ebwizardry\:spectral_dust.name=Spectral Dust + item.ebwizardry\:wizard_armour.legendary=Legendary item.ebwizardry\:wizard_armour.buff=-%1$s %2$s cost item.ebwizardry\:wizard_armour.mana=Mana\: %1$s/%2$s diff --git a/src/main/resources/assets/ebwizardry/models/item/spectral_dust_earth.json b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_earth.json new file mode 100644 index 00000000..d22acbb9 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_earth.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ebwizardry:items/spectral_dust_earth" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/item/spectral_dust_fire.json b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_fire.json new file mode 100644 index 00000000..7dbf7125 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_fire.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ebwizardry:items/spectral_dust_fire" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/item/spectral_dust_healing.json b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_healing.json new file mode 100644 index 00000000..31fb5b3a --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_healing.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ebwizardry:items/spectral_dust_healing" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/item/spectral_dust_ice.json b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_ice.json new file mode 100644 index 00000000..4a1bdfd8 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_ice.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ebwizardry:items/spectral_dust_ice" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/item/spectral_dust_lightning.json b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_lightning.json new file mode 100644 index 00000000..26b8b830 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_lightning.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ebwizardry:items/spectral_dust_lightning" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/item/spectral_dust_necromancy.json b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_necromancy.json new file mode 100644 index 00000000..d79c130e --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_necromancy.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ebwizardry:items/spectral_dust_necromancy" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/item/spectral_dust_sorcery.json b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_sorcery.json new file mode 100644 index 00000000..bf7f068b --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/spectral_dust_sorcery.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ebwizardry:items/spectral_dust_sorcery" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_earth.png b/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_earth.png new file mode 100644 index 0000000000000000000000000000000000000000..7b8fa46ed069236d5835fe721b6e2c63716b157e GIT binary patch literal 2214 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!uT9I8ShN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHU}&%X3ngXhnA&2JnW9|ev-jNEE|<8gj+ihALs{IqRm>yDW5 zbFPf$HGDjO|MH*Q^3R)%?NmA~-MahF+wE>*-}rm;`;@;QSL=VPsmL@BZ+8}ceD1d? zpKSaEIXU4;*T4O`l@}Cjy{OIabk>CF>Fw;Y>RBCk^VDn2BcF@@7wN0BlvP`I@O%0E z2|aQbPMq6u@QB#{!pet#b$_t^StqxK^Lp0FAEj^XpP!5A-~J@lfBT)Ai{Ad3#g)96 zuVcGRrgiK2oz_Mr&Tqf`xis;YYt!~SXK$vS+qhWUPAT|eMnJm8abdQwC6oFy1;Y=S zKd_VyKc>pDQSqg0Mp(?*0JZixexI(ays)J3jm_4 zC7cE&hnZBQ0+LO3XX}>yINQG8jp_VcgG+)vf@ynVp9Mdz->K(ZtoKSNa%WL%=}uP% zon*!Bb%tCG7p}}{Q{I0(bzVjC{DxEoi9QoFUUqpd!*{a= zk%Q$2k`83}n+dUZrA#jFRsvuhN*4xiAN^3_1K(Zxvu7Kf4IlkcV^`@Zd2E`yYq@#BZ~F8@rIX^^|I-PdI8_9Qr6j-B{`DqZJ!x zeZFxhz<8DZjTYM!g-Kd0Y<*u-!b}3S>l|jj;rh|_-PEsIeBV=nqN`Q6uD+l6^N-Vs z^_xC>3I{!1a8j*t(uuvlc4xfPFrWRQ-OD+9^RBrt^G;uGZCN}=J!sMDaFoaQeH%ZR%AY@= zF4I$*+@Q5$BsdVlUNrZ2Z}K_-goDJN{VS+s&0e$7W7@5_5F@yUkZ@^DQdo z>uYOv-#?qb_IGph`?h^Pe;$k0+IswZR(So-cjaHXt~MY4D?L8& zSATZ$D$%0{HfS&~Ft8 z;_2(k{)Cl_i&H`~)#@b!1LJc~7sn8b(^DrKW(X&W9Iv;O@v>?;z-|@sGR>gz%Y(Ke zXJHOSv6cpz7XrIPZ)rA9E)m_%!^*xeT6T>}hu%)Pg*SAPTt6@g8hLR^DY~}E#Cpwl z-m|`(v+3XT>ids>|KI=p-+S3le>p!D^4?_pZ)Oto$NtIPs5ft>uY2Sa))_zR*`l8Y zelDG=+0AZQ^WFEG?Xbw_ZphWTS1-4@I(bfnkr`nuLGSu!DXQ?1cKv#Da2n7+Te!*bxa*`yDl4fn1Y9ep)n!>ldYFS?SL z7`{#vx*cLuk(lYU(dx;Mx4I1xGEUqv-}t{Sb-a-DJH=_E)sdo@uB(dMR&J097ryPL zXV>Wz)_L8hOW)LT^13;m;e2Y`k4z_T%vZGjsx+rbQ1)c$bGzmRuIeiaPRTuW?Vta8 zIzvRCcYb!{_AvRgGdMOJQOmrpu=&M?Bg@XEi^*C!vNqiPceheFyzkLlo+h#7Z!U6b zExEheyz8ps*E`M|6ectLTUVKXYbNql^@$2XlT9z|( zI`mC*mtFTc^*G9Hwz_qFWa@sFpP?H2X3jG1emwoW(4H!f_lLj8Y@0A+tV!Z literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_fire.png b/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_fire.png new file mode 100644 index 0000000000000000000000000000000000000000..9e64160c8ed34235a007f6c795e35e96681f11f5 GIT binary patch literal 2127 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 B{LcUY literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_healing.png b/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_healing.png new file mode 100644 index 0000000000000000000000000000000000000000..218e92df3d61a226938c23cfca43091592f93e52 GIT binary patch literal 2164 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!uTysAPXN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHU}kDR2f&G$E5bT5ko&!r*>(Io#JYrp29+20I! zm+H^xkC!gG@bi~Veo?U9njXINR1fb>$J;M$G*a6Cd~RRwi+w_JeT&VXb|3ZqW5p+V z?1kXuC%y7dPCS}9P2^VjLD}`ob(CUQ%lFOuaIon_bwUgoo1t0mRX9lmv$^?GdK<=)qKW171T^3VVHZC~ZCXEQd5-)CrEyg|>r zR&8NkN23dKR!e66>qmM!i%w?uDeklmsA5lhcj)6&qxWk6=f6%fd0rIzT<89_rnovw zg)0#!)~c^y=$M^#x#H&cU1yr_-(XMRye$62-;UXx57!rbom#!Pxd|j#>;2hc5Q)&;`cC%!8imrE={OasZ>$%h2w(?#RoN1Q* zXW4Ogz4=RLx*y8t$-e)*=_<46Ppc;`1s#iw4o^y3mVdGJ#e*|@_7v5!zpi?F{mqAU zx2ImX=CqDeH2G>{w&v-U(}la+*!(6LO*nJ({Z~&Ob2}dQzS-{Om)<-}-@mtbW!h@v zMLY=$ngTg@i_ctj?1X?;ZNFH4<>K|0T>dS0w(dBTYH{So;M|2XBIIL{K(#2&wez<#I-~sW~!&_*}js` zUH9F+HhbKERQce~*8rnyCl*YMGCp7QI^=#=-Q1m>vrc=tp9(gaSzNIAiLH}y*B52a z+_={Hj!iYGmnMCY*EoCP#QxiNZiKS@?QLRppH#OdE1y62uR+=7it8bj#u8^kD@0|k z@YwF0n5UMz>Z=Wt;?ouBGDp+R6szx=B%R)Bqir8Ii^fy0YtHU2b2HLC zo9D#8Rry`^RmZQ>x_RNf48{%Lw5``Y$@pg>y~t*+in-1COW(H{a_ntn**wwzCEx3a zQs$~?`7gICtBZL4>}#K0vFcG$fMBNO1v%>}&T{`kG%QrEt|{d^dCg9L>q0MarNiH6 z3Pvvvh_c{Wvn=JHpB_tjz}E@Rhb^AvPkku0ct+u>D_Q@J&cAi?c-XwF6FKg-czekO zZawW;w}D&6$Tn@8eDl}m0Y>|G#w@cgj?Q$qJ(5nLJ!NBu9`Ys!Co%)9nY^R zB2G4nlPW~6WVBxXoUuJ4ZL6oMirJsjyWIkq{yloSE&TVL*L%9|IA*sU+}}si|xJjYcC!T zdlP!Ozo$O&%kuX&y#Hc5e#-A>TG;g4@W(3#1_rhyZ+91l{|uKHR-~$LD`#L};4JWn zEM{Qf76xHPhFNnY7#JAXOFVsD*`Kg-ib_j_#4*lfU|?MB>Eak-aeC@x`)p}Pfur^1 zD<(>dzVq=Yx8z{re0WHqK$&%^hwhyW?t0eU#+gaNq91En8?X0H5_#9dY`Bs~k)@EG z>Hq61OZ*;Z_Z&Z(5?(IE_WWSs=Kfma&H3pQ?b%PP_f6ryBr!?qKmX5-kx|R%R@%H! z%yi$W^#0KTjW=tgC(TNIzCJPcDwo4{-uwCq&*v?uT*t}Sw|}Ks$)o=KS1P8bGb7S- zuX=4?yXU6t#a!ngM>g9d^IqIi{ls~1Zyn=-R148?(F1aM$-e~GNvApA57cDLn3^Yj z=JyqDANxafJFn((ZFD}$%758-#S67hoZnJdHY*;HTy~!!Pw`xXD1+Z&D+Ar#y5SQ$ zmM9f1zkkM2GGJg}VDNPHb6Mw<&;$TUQ3`ed literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_ice.png b/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_ice.png new file mode 100644 index 0000000000000000000000000000000000000000..73122cf126a4cbc9fde013505242c584ac01dd75 GIT binary patch literal 2238 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 Xx4$y(i!NheU|{fc^>bP0l+XkKLF^64 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_necromancy.png b/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_necromancy.png new file mode 100644 index 0000000000000000000000000000000000000000..afcbaf87bbbfbc48c0b8253aeedc4c67f529a69f GIT binary patch literal 2265 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!uT@~T22N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHU}&z$r{jpy%q%_5eTh$Dp&tBaat?D>`NoX)wwi^Aqre8CsJgLS*sJKT=etvBkEAz(Ro8PDW{kod}8&BM`vwsgIb`?JVE75*R z{7n1trYEKQ_MMx3C8U4FqQ#M-pKjeuTu`*A>&Dy4OX-tKV?X*Izo@_5`>5`pe{Y26 zTx3?eDQQ0A#v}7}u@&{)`yT!}>&+3~7_zJG@bi7f-#?tK{Qlx>`L-7}`*$6>v4ZL2 zS$8Y{hsF0}<*J{#sYM^Ysrb+3@|C*2H?X<$9`E#m@^zN`p0!Lqy^fJBX}{hq zy~1aC5e=TqSv{HMx1Fx<`*~~q#DwR56G~W8OCJ6xn|WUC|LIcgS;c|VPn>$IedDPZ zW8yKVyuVId4HrV@x1HX9J#}71^8AKW1qnTKZoY_|yINTb*th>UG*eN0jd|JSxk2Hq z1%luB4B8|(Zg3a{tIVF`?ebwU!+fUGven^@46P5WW^(4tP(C2I{oAowF4Na==lLsd zW#~8{=&0ndf#Zg`YH+%m|t^C0I~Q z-*J(O;K`s(r+Y-iiq4!&IcYh)$(K7se2v}&jitLzUA-QV>>HZ3ZfT?4+3gpSynUnc zmhL_$`|y6U|1$6MdwHZ5T>7BgV{vK1ME98wZasOKKBI7%rt$fr#miRgTjZ91qPAa3 zGmG1>{^U~jtQmeUS1g$7cjypLho!%RjV_Br#&(0vPWN`V-Z^w8t$hIp--^J92PIJ* zjE?pvT0PCn_@C@MdY0wC_MJQ7CuXwV{ognz@`7fOeBp1Ft#RwR4v9Nxb4~Zx^;g*@ zxzj>(9bevM@9y6#6;DP?+cRH=e`4bGh*LT%_>BtwygvE6)5|&{-fgDP+pRn|zg#c> zyqBSW*)zX9n@z_#OOsV@&OE*Mz#LKA>ADKMbsz66l8T$Dem%PQPSvp`HbvhXFWB^i zOp+9OSrx-ta)0ucea8xlS}y#x3+1bx$5L>jdrqa$p9Kb2_a<*#;QL_n-%CwjO3qB& zdfR|=XNs@F%DlyE-*$Rps?|B|KoLimp{LL$$Ncy^6AuF+f|D5+Ygmp*?&LX z!e-?ib?NmA4|tW>a(2$UUx5q=Z;R>J@JqD_L_3k_#fKaX2ry;?qDlC1wCqwO1IPpVJw>h#a(xx&n*w_sKN zuJel{S($r%wD&7LY47+iuwQpx!*ou!ug%pTjc+NQTKq13*UMc+kEKj!o)CIju)WYf zW09Xy_T`&9{7pLC7HphRE30*Zr{h_3g{N-%#T83>K3pwyR&@<7XWEg_8+6vjLi~2v z<;Ld=D;@WVzT5J4OL*Vh?K6M!9eLSP#Qv!9vkm7i!|YG18>@dm>&;*@WnLLA$GKs% zl~n12Q_GfE_g+hreru+9EZFdkOW|DYYi7&teLQtK_E_yILx*L1xBM>Cc3c|dBJeu& zD6g|vOQ6G|xBrq4d9MCw+ry<%_g2&LL)v6bgYT(vveseiR~|IF#i%PZ$MwdqC6V<@ z@4fTun6hp0%L7uv2KqIv992bI^b+r7Hp^Sf$c1DFy!boK@i)Ul-?oSViFYTQrwA8? z2Jk%+T%FQltyJfZwa2zKrln6--y|t|vG_!b!<6t@Q%X2m?r1${-7l0a zc8syLmW=oPxW@LWpV#9tFWzZUE>$*Ob8 zEG<5tFr%#c
B&reQKZS4OMUX`tF@-F00nR&g#_U*#98=sol_e!%|UAzDMQ{1%w1cUy}12(!6p%a&;Ew`N;rJ=3;=f>}u{I>VB zru|5^3|lBtdagw1!D5d;A+B|6v{L7MNs^bIG2i;ydGUYj-T%%6eJ*AGz5BPh`{T}f ziA_JnCqLN98gnOd2?GNITavfC3&VegOAISg)wh*1FfecyctjR6FmMZlFeAgPIT8#E z4D2PIzOL*~SUE)n1r}r_Dl#xIzVUQ%46!&pb+TcGaG=ETdhsI%d7kpI_%n%fm@MGj z+a$9+hTVUn)9w@@7WIij0&5;5UB9$;%Yy`tmy5df^unWB#aP8TGyKm9=Lc}GNL!f% zvm5`AIrqINJ>4{Qv*^-v3(u!W~^N&MAf|tU2^c0go?VKY!>_F2g%H z{lu4MpV>7FowsZ#k=9(qBBnQmq2kB8o((0l?-{TpT+RrTtYC=PHKD5}~HRGml<&XT@Hd#&Bcq+pKT@^cyeu-@ZTlo|@M$hAXR8 zPsL4KFqvUP#ppz^e;wIZ^bwDIK>w#TW0e%1)hoeGATjia-LboB)i#e-g_HN3X*Dk4-5K# z{jOBj@A5{jbMkNXM{XZGXSrQ2Xo)!4ziFE2jKYV17Jrzopr0Gv5M8UO$Q literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_sorcery.png b/src/main/resources/assets/ebwizardry/textures/items/spectral_dust_sorcery.png new file mode 100644 index 0000000000000000000000000000000000000000..666caa7cf1f4586fb3181136d6b8990217fec379 GIT binary patch literal 2213 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!uT9IHYiN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHU}&)xK974M((TANrL8jchh$cNr^oHm~=<@}BEXLrhjB(L9S zXq|9Hx6$|i`Tgxb#p~=`@|{vVEmgbYPbmIwYP<3GwtxEJUD?Oy_1@mGGp>S1aNd^P zD_7SXtf=vQu;B6W+B*fIwu`#_&aRs9?35huRN+vUx_;}ov)6p~swj5Yq34 zZU#|D6e=HETW;F1d?(l2`h)iCs^>ZF;94T_-Qi*Jn=@BySl(yZCZ%`xUA7fDrY7pq zFY$6m_x$ATN?#n_#{B%&blbJbKFQo|vf;09>sneoR2TcrUAIPzg}-IewJj~(ANw;t z?=sSB>=t#*cldlDwD`u4YyMEra83MoGXeSgHe-OFz z!Dr?yMMgEz6@qPlQ@f*IKie8t+B9``^OEv_XJUQ*r)#AC#IN(dC^320xyYSKth@iH zD4dyb;;i)&h7M_|%R3(J*86-+`Zt>n>SI^jOLBA-|-dX zvvLSt>oYheD6rvVV9CkIZ}a9l%dq|siFl{^gPCK8L~U^zgY+Vnf+Od*8n)k^^!4KV z#3^D7f|`n+EF7W@qE0OJ7918KN=FltdNhls%t-c};?$+7>bKcVREZRxv9#rn)K(k6#GlzHWhK52+&b-j``{qFEC{mPt0 zW`f+_eeo+2XZ#F#m}c22Y^?CCnfZ~8Aj87HN6vI{AM}5smS$G1!rEoOrbl?rGqofK zgOcZu3&hLxpVS>Wd*p-moj>MJ&a}My-#BOH1cS&EdH;dZ#G=w7d_R!uura@xfLfD$RG0Q^vT92C%R5?6q_3FVPl+7HcgfxyZXNF!D9}Gyq8=# z`0(uPmuFI^1ipU1FaFh?vZef4Z&%;db;?>~W;1P-)()N13;9a30{nyizH@!GtCY>ne* z$D`}*uCC&iUXr)ATt)Anlh}vUz`G1D zKi=KbWqQ_LuA%)^rC0XHF^jd@Z_gas{gNg*E5B z<$muLId^o8X5ob6CmeTQ-^i!o9kzAHodPw>Z%$jE7T)ObZ@rs8;f!z3y4sr;Ud@Zu z^?q>7YA?&20E_&q1?&6RL(f?Ibv|scIRyW&x#Y}W_#K1 zY4`rEE$Gg-#pVqjokOY(MiVffE*iD5;m`nGZg1_sUokH}&M25w;xW@MN( zM}mQYfxX1j*OmPVE2pTSPCu*cG6n|5r=Bj3Ar_~nPPWew4wN}=FCKGhvRJ0zlah|^ zg)OUPxTZP@He0ytjJBDh((T>Oic=lv?x72AwI`>b)~zfeWXS#k3;H zbskOb(;l3ExaV}NQq%pXf9ve;egA#${qH*dkMou1Uz_OrBChck(|`V-JLa9-z0`A! zOzf_uE2gbm{+MNHHvh|Mi$X1TX}C}SeEYkKSSRb$#olL59zA&awrdvm`x~c4Gvmu# zZ>lkv=kIm9sg~U>xG^Swan9rj6~P_5YXlp_pVxJ4@;!7{nSt+{c|e=!+`QQ(O=1t< z3AQg+k8)$^@Z0D+Z|Z$@meh!r_*sGTy3(V5T>U*Er^wy;)K{}p8$NJ}PDxytBfLJq zv!i-v(lv*tFDAV=P02Qx@UKGW@5hKa|296U?mZ(lTVnPsZ~K#zy}uW_JFjZ=H|ANX zAN%0fC0T|&POZ&Pi`CS`ITcFI02 Date: Sun, 10 May 2020 16:21:24 +0100 Subject: [PATCH 093/415] Reorganise block registry --- .../wizardry/registry/WizardryBlocks.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java index 5c47d0a4..6bbccd46 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java @@ -43,28 +43,33 @@ public final class WizardryBlocks { public static final Block arcane_workbench = placeholder(); public static final Block crystal_ore = placeholder(); + public static final Block crystal_flower = placeholder(); + public static final Block transportation_stone = placeholder(); + public static final Block crystal_block = placeholder(); + public static final Block petrified_stone = placeholder(); public static final Block ice_statue = placeholder(); public static final Block magic_light = placeholder(); - public static final Block crystal_flower = placeholder(); public static final Block snare = placeholder(); - public static final Block transportation_stone = placeholder(); public static final Block spectral_block = placeholder(); - public static final Block crystal_block = placeholder(); public static final Block meteor = placeholder(); public static final Block vanishing_cobweb = placeholder(); - public static final Block runestone = placeholder(); - public static final Block runestone_pedestal = placeholder(); public static final Block thorns = placeholder(); public static final Block obsidian_crust = placeholder(); public static final Block dry_frosted_ice = placeholder(); + + public static final Block runestone = placeholder(); + public static final Block runestone_pedestal = placeholder(); + public static final Block gilded_wood = placeholder(); + public static final Block oak_bookshelf = placeholder(); public static final Block spruce_bookshelf = placeholder(); public static final Block birch_bookshelf = placeholder(); public static final Block jungle_bookshelf = placeholder(); public static final Block acacia_bookshelf = placeholder(); public static final Block dark_oak_bookshelf = placeholder(); + public static final Block oak_lectern = placeholder(); public static final Block spruce_lectern = placeholder(); public static final Block birch_lectern = placeholder(); @@ -95,28 +100,33 @@ public final class WizardryBlocks { registerBlock(registry, "arcane_workbench", new BlockArcaneWorkbench().setHardness(1.0F).setCreativeTab(WizardryTabs.WIZARDRY)); registerBlock(registry, "crystal_ore", new BlockCrystalOre(Material.ROCK).setHardness(3.0F).setCreativeTab(WizardryTabs.WIZARDRY)); + registerBlock(registry, "crystal_flower", new BlockCrystalFlower(Material.PLANTS).setHardness(0.0F).setCreativeTab(WizardryTabs.WIZARDRY)); + registerBlock(registry, "transportation_stone", new BlockTransportationStone(Material.ROCK).setHardness(0.3F).setLightLevel(0.5f).setLightOpacity(0).setCreativeTab(WizardryTabs.WIZARDRY)); + registerBlock(registry, "crystal_block", new BlockCrystal(Material.IRON).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY)); + registerBlock(registry, "petrified_stone", new BlockStatue(Material.ROCK).setHardness(1.5F).setResistance(10.0F)); registerBlock(registry, "ice_statue", new BlockStatue(Material.ICE).setHardness(0.5F).setLightOpacity(3)); registerBlock(registry, "magic_light", new BlockMagicLight(Material.CIRCUITS)); - registerBlock(registry, "crystal_flower", new BlockCrystalFlower(Material.PLANTS).setHardness(0.0F).setCreativeTab(WizardryTabs.WIZARDRY)); registerBlock(registry, "snare", new BlockSnare(Material.PLANTS).setHardness(0.0F)); - registerBlock(registry, "transportation_stone", new BlockTransportationStone(Material.ROCK).setHardness(0.3F).setLightLevel(0.5f).setLightOpacity(0).setCreativeTab(WizardryTabs.WIZARDRY)); registerBlock(registry, "spectral_block", new BlockSpectral(Material.GLASS).setLightOpacity(1).setBlockUnbreakable().setResistance(6000000.0F)); - registerBlock(registry, "crystal_block", new BlockCrystal(Material.IRON).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY)); registerBlock(registry, "meteor", new Block(Material.ROCK).setLightLevel(1)); registerBlock(registry, "vanishing_cobweb", new BlockVanishingCobweb(Material.WEB).setLightOpacity(1).setHardness(4.0F)); - registerBlock(registry, "runestone", new BlockRunestone(Material.ROCK)); - registerBlock(registry, "runestone_pedestal", new BlockPedestal(Material.ROCK)); registerBlock(registry, "thorns", new BlockThorns()); registerBlock(registry, "obsidian_crust", new BlockObsidianCrust()); registerBlock(registry, "dry_frosted_ice", new BlockDryFrostedIce()); + + registerBlock(registry, "runestone", new BlockRunestone(Material.ROCK)); + registerBlock(registry, "runestone_pedestal", new BlockPedestal(Material.ROCK)); + registerBlock(registry, "gilded_wood", new BlockGildedWood()); + registerBlock(registry, "oak_bookshelf", new BlockBookshelf()); registerBlock(registry, "spruce_bookshelf", new BlockBookshelf()); registerBlock(registry, "birch_bookshelf", new BlockBookshelf()); registerBlock(registry, "jungle_bookshelf", new BlockBookshelf()); registerBlock(registry, "acacia_bookshelf", new BlockBookshelf()); registerBlock(registry, "dark_oak_bookshelf", new BlockBookshelf()); + registerBlock(registry, "oak_lectern", new BlockLectern()); registerBlock(registry, "birch_lectern", new BlockLectern()); registerBlock(registry, "spruce_lectern", new BlockLectern()); From c04ee35fc3a8e1280816470d0e85505ca7dd6216 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Mon, 11 May 2020 18:29:32 +0100 Subject: [PATCH 094/415] Helps if you actually define the curative items... fixes #435 --- src/main/java/electroblob/wizardry/potion/Curse.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/potion/Curse.java b/src/main/java/electroblob/wizardry/potion/Curse.java index 09b9e8ab..1a6e6d06 100644 --- a/src/main/java/electroblob/wizardry/potion/Curse.java +++ b/src/main/java/electroblob/wizardry/potion/Curse.java @@ -1,6 +1,7 @@ package electroblob.wizardry.potion; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryItems; import net.minecraft.item.ItemStack; import net.minecraft.potion.PotionEffect; import net.minecraft.util.ResourceLocation; @@ -27,7 +28,9 @@ public class Curse extends PotionMagicEffect { @Override public List getCurativeItems(){ - return new ArrayList<>(); // Cannot be cured! + List items = new ArrayList<>(); + items.add(new ItemStack(WizardryItems.purifying_elixir)); + return items; } @Override From c4d4c67302157e03cea32d135c39ee69dc87de70 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Mon, 11 May 2020 18:35:37 +0100 Subject: [PATCH 095/415] Well this is awkward :/ --- src/main/java/electroblob/wizardry/util/ParticleBuilder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/electroblob/wizardry/util/ParticleBuilder.java b/src/main/java/electroblob/wizardry/util/ParticleBuilder.java index fa6e6660..a9af72cf 100644 --- a/src/main/java/electroblob/wizardry/util/ParticleBuilder.java +++ b/src/main/java/electroblob/wizardry/util/ParticleBuilder.java @@ -328,7 +328,7 @@ public final class ParticleBuilder { * @throws IllegalStateException if the particle builder is not yet building. */ public ParticleBuilder fade(int r, int g, int b){ - return this.clr(r/255f, g/255f, b/255f); // Yes, 255 is correct and not 256, or else we can't have pure white + return this.fade(r/255f, g/255f, b/255f); // Yes, 255 is correct and not 256, or else we can't have pure white } /** @@ -345,7 +345,7 @@ public final class ParticleBuilder { int r = (hex & 0xFF0000) >> 16; int g = (hex & 0xFF00) >> 8; int b = (hex & 0xFF); - return this.clr(r, g, b); + return this.fade(r, g, b); } /** From 5c34b379d1469f35c98cc48b05797cb9bce143d2 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Mon, 11 May 2020 18:36:58 +0100 Subject: [PATCH 096/415] Update French translations, courtesy of Crowller --- .../assets/ebwizardry/lang/fr_fr.lang | 652 ++++++++++++++---- 1 file changed, 520 insertions(+), 132 deletions(-) diff --git a/src/main/resources/assets/ebwizardry/lang/fr_fr.lang b/src/main/resources/assets/ebwizardry/lang/fr_fr.lang index b51fa23d..2993099b 100644 --- a/src/main/resources/assets/ebwizardry/lang/fr_fr.lang +++ b/src/main/resources/assets/ebwizardry/lang/fr_fr.lang @@ -1,30 +1,61 @@ tile.ebwizardry:arcane_workbench.name=Table des arcanes -tile.ebwizardry:crystal_ore.name=Minerais de cristal -tile.ebwizardry:petrified_stone.name=Roche pétrifiée +tile.ebwizardry:crystal_ore.name=Minerai de cristal +tile.ebwizardry:petrified_stone.name=Pierre pétrifiée tile.ebwizardry:ice_statue.name=Statue de glace tile.ebwizardry:crystal_flower.name=Fleur de cristal tile.ebwizardry:snare.name=Ronces -tile.ebwizardry:transportation_stone.name=Pierre de Transportation -tile.ebwizardry:spectral_block.name=Bloc spectrale -tile.ebwizardry:crystal_block.name=Bloc de cristal +tile.ebwizardry:transportation_stone.name=Pierre de téléportation +tile.ebwizardry:transportation_stone.confirm=Vous serez transporté ici en lançant le sort %1$s +tile.ebwizardry:transportation_stone.remember=La position %s, %s, %s a été marquée dans la dimension %s +tile.ebwizardry:transportation_stone.forget=La marque %s, %s, %s a été effacée dans la dimension %s +tile.ebwizardry:transportation_stone.invalid=Vous devez d'abord créer un cercle avec 8 pierres de téléportation! +tile.ebwizardry:spectral_block.name=Bloc spectral +tile.ebwizardry:runestone.name=Pierre runique +tile.ebwizardry:runestone_pedestal.name=Piédestal runique +tile.ebwizardry:thorns.name=Épines +tile.ebwizardry:obsidian_crust.name=Croûte d'obsidienne +tile.ebwizardry:dry_frosted_ice.name=Glace dépolie + +tile.ebwizardry:magic_crystal_block.name=Bloc de cristal +tile.ebwizardry:fire_crystal_block.name=Bloc de cristal embrasé +tile.ebwizardry:ice_crystal_block.name=Bloc de cristal gelé +tile.ebwizardry:lightning_crystal_block.name=Bloc de cristal électrique +tile.ebwizardry:necromancy_crystal_block.name=Bloc de cristal ténébreux +tile.ebwizardry:earth_crystal_block.name=Bloc de cristal verdoyant +tile.ebwizardry:sorcery_crystal_block.name=Bloc de cristal mystique +tile.ebwizardry:healing_crystal_block.name=Bloc de cristal éclatant + +item.ebwizardry:crystal_magic.name=Cristal magique +item.ebwizardry:crystal_fire.name=Cristal embrasé +item.ebwizardry:crystal_ice.name=Cristal gelé +item.ebwizardry:crystal_lightning.name=Cristal électrique +item.ebwizardry:crystal_necromancy.name=Cristal ténébreux +item.ebwizardry:crystal_earth.name=Cristal verdoyant +item.ebwizardry:crystal_sorcery.name=Cristal mystique +item.ebwizardry:crystal_healing.name=Cristal éclatant -item.ebwizardry:magic_crystal.name=Cristal magique item.ebwizardry:magic_wand.name=Baguette magique item.ebwizardry:apprentice_wand.name=Baguette d'apprenti item.ebwizardry:advanced_wand.name=Baguette d'expert item.ebwizardry:master_wand.name=Baguette de maître -item.ebwizardry:spell_book.name=Livre de sort +item.ebwizardry:spell_book.name=Livre de sorts + +item.ebwizardry:spell_book.apply_to_wizard=Le sort %2$s a été remplacé par %3$s sur %1$s item.ebwizardry:arcane_tome.name=Tome des arcanes -item.ebwizardry:arcane_tome.desc1=Améliore n'importe quelle -item.ebwizardry:arcane_tome.desc2=baguette du tier %1$s au tier %1$s +item.ebwizardry:arcane_tome.desc=Améliore n'importe quelle baguette du tier %1$s avec assez de progression au tier %2$s -item.ebwizardry:wizard_handbook.name=Le manuel du sorcier -item.ebwizardry:wizard_handbook.desc=par %1$s +item.ebwizardry:wizard_handbook.name=The Wizard's Handbook +item.ebwizardry:wizard_handbook.desc=by %1$s + +item.ebwizardry:wand.generic=baguette item.ebwizardry:wand.buff=+%1$s de puissance de %2$s item.ebwizardry:wand.spell=Sort actuel: %1$s item.ebwizardry:wand.mana=Mana: %1$s/%2$s +item.ebwizardry:wand.progression=Progression\: %1$s/%2$s + +item.ebwizardry:wand.levelup=%1$s est prête à être améliorée au tier %2$s item.ebwizardry:wand.addally=%1$s a été ajouté a vote liste d'alliés item.ebwizardry:wand.removeally=%1$s a été retiré de votre liste d'alliés @@ -39,136 +70,329 @@ item.ebwizardry:basic_healing_wand.name=Baguette de soin item.ebwizardry:apprentice_fire_wand.name=Baguette d'apprenti pyromancien item.ebwizardry:apprentice_ice_wand.name=Baguette d'apprenti cryomancien -item.ebwizardry:apprentice_lightning_wand.name=Baguette d'apprenti electromancien -item.ebwizardry:apprentice_necromancy_wand.name=Baguette d'apprenti necromancien -item.ebwizardry:apprentice_earth_wand.name=Baguette d'apprenti Geomancien +item.ebwizardry:apprentice_lightning_wand.name=Baguette d'apprenti électromancien +item.ebwizardry:apprentice_necromancy_wand.name=Baguette d'apprenti nécromancien +item.ebwizardry:apprentice_earth_wand.name=Baguette d'apprenti géomancien item.ebwizardry:apprentice_sorcery_wand.name=Baguette d'apprenti sorcier item.ebwizardry:apprentice_healing_wand.name=Baguette d'apprenti soigneur -item.ebwizardry:advanced_fire_wand.name=Baguette de brasier -item.ebwizardry:advanced_ice_wand.name=Baguette de blizzard -item.ebwizardry:advanced_lightning_wand.name=Baguette de foudre -item.ebwizardry:advanced_necromancy_wand.name=Baguette de necromancie -item.ebwizardry:advanced_earth_wand.name=Baguette de forêt +item.ebwizardry:advanced_fire_wand.name=Baguette de pyromancien +item.ebwizardry:advanced_ice_wand.name=Baguette de cryomancien +item.ebwizardry:advanced_lightning_wand.name=Baguette d'électromancien +item.ebwizardry:advanced_necromancy_wand.name=Baguette de nécromancien +item.ebwizardry:advanced_earth_wand.name=Baguette de géomancien item.ebwizardry:advanced_sorcery_wand.name=Baguette de sorcier item.ebwizardry:advanced_healing_wand.name=Baguette de soigneur item.ebwizardry:master_fire_wand.name=Baguette de maître pyromancien item.ebwizardry:master_ice_wand.name=Baguette de maître cryomancien -item.ebwizardry:master_lightning_wand.name=Baguette de maître electromancien -item.ebwizardry:master_necromancy_wand.name=Baguette de maître necromancien -item.ebwizardry:master_earth_wand.name=Baguette de maître Geomancien +item.ebwizardry:master_lightning_wand.name=Baguette de maître électromancien +item.ebwizardry:master_necromancy_wand.name=Baguette de maître nécromancien +item.ebwizardry:master_earth_wand.name=Baguette de maître géomancien item.ebwizardry:master_sorcery_wand.name=Baguette de maître sorcier item.ebwizardry:master_healing_wand.name=Baguette de maître soigneur -item.ebwizardry:spectral_sword.name=Epée spectrale +item.ebwizardry:spectral_sword.name=Épée spectrale item.ebwizardry:spectral_pickaxe.name=Pioche spectrale item.ebwizardry:spectral_bow.name=Arc spectral -item.ebwizardry:mana_flask.name=Fiole de mana +item.ebwizardry:spectral_sword_upgraded.name=Épée spectrale +item.ebwizardry:spectral_pickaxe_upgraded.name=Pioche spectrale + +item.ebwizardry:small_mana_flask.name=Petite fiole de mana +item.ebwizardry:medium_mana_flask.name=Fiole de mana +item.ebwizardry:large_mana_flask.name=Grande fiole de mana + +item.ebwizardry:grand_crystal.name=Cristal magique majeur +item.ebwizardry:crystal_shard.name=Éclat de cristal magique + +item.ebwizardry:astral_diamond.name=Diamant astral + +item.ebwizardry:purifying_elixir.name=Élixir purifiant +item.ebwizardry:purifying_elixir.desc=Élimine toutes les malédictions + item.ebwizardry:storage_upgrade.name=Amélioration de baguette de capacité de mana item.ebwizardry:siphon_upgrade.name=Amélioration de baguette de siphon de mana item.ebwizardry:condenser_upgrade.name=Amélioration de puissance de baguette item.ebwizardry:range_upgrade.name=Amélioration de portée de baguette item.ebwizardry:duration_upgrade.name=Amélioration de durée de sort -item.ebwizardry:cooldown_upgrade.name=Amélioration de temps de rechargement de baguette +item.ebwizardry:cooldown_upgrade.name=Amélioration de temps de récupération de baguette item.ebwizardry:blast_upgrade.name=Amélioration d'explosion de sort item.ebwizardry:attunement_upgrade.name=Amélioration d'harmonisation de baguette +item.ebwizardry:melee_upgrade.name=Amélioration de mêlée de baguette + +item.ebwizardry:storage_upgrade.desc=Améliore la capacité en mana de la baguette +item.ebwizardry:siphon_upgrade.desc=Améliore la baguette pour extraire du mana des monstres tués +item.ebwizardry:condenser_upgrade.desc=Améliore la baguette pour qu'elle régénère lentement du mana sur la durée +item.ebwizardry:range_upgrade.desc=Améliore la baguette pour augmenter la portée effective des sorts lancés +item.ebwizardry:duration_upgrade.desc=Améliore la baguette pour faire durer plus longtemps ses sorts +item.ebwizardry:cooldown_upgrade.desc=Améliore les délais de récupération de la baguette +item.ebwizardry:blast_upgrade.desc=Améliore les zones d'impact de la baguette +item.ebwizardry:attunement_upgrade.desc=Augmente le nombre de sorts pouvant être attachés à la baguette +item.ebwizardry:melee_upgrade.desc=Améliore la baguette pour utiliser du mana pour faire plus de dégâts en frappant avec item.ebwizardry:flaming_axe.name=Hache enflammée item.ebwizardry:frost_axe.name=Hache gelée +item.ebwizardry:flaming_axe_upgraded.name=Hache enflamée +item.ebwizardry:frost_axe_upgraded.name=Hache gelée + item.ebwizardry:firebomb.name=Bombe de feu item.ebwizardry:poison_bomb.name=Bombe de poison item.ebwizardry:smoke_bomb.name=Bombe de fumée +item.ebwizardry:spark_bomb.name=Bombe d'étincelles item.ebwizardry:blank_scroll.name=Parchemin vierge +item.ebwizardry:scroll.generic=parchemin item.ebwizardry:scroll.name=Parchemin de %1$s -item.ebwizardry:scroll.undiscovered.name=Parchemin de "%1$s" -item.ebwizardry:identification_scroll.name=Parchemin d'identification -item.ebwizardry:identification_scroll.desc1=%1$sIdentifie un livre ou un -item.ebwizardry:identification_scroll.desc2=%1$sparchemin de sort inconnu +item.ebwizardry:scroll.undiscovered.name=Parchemin "%1$s" +item.ebwizardry:identification_scroll.name=Parchemin d'dentification +item.ebwizardry:identification_scroll.desc=Identifie un livre ou un parchemin inconnu item.ebwizardry:identification_scroll.nothing_to_identify=Il n'y a rien à identifier! item.ebwizardry:armour_upgrade.name=Sceau arcanique de protection item.ebwizardry:armour_upgrade.desc1=%1$sAméliore n'importe quelle -item.ebwizardry:armour_upgrade.desc2=%1$srobe au tier %2$slegendaire +item.ebwizardry:armour_upgrade.desc2=%1$srobe au tier %2$slégendaire item.ebwizardry:magic_silk.name=Soie magique -item.ebwizardry:wizard_armour.legendary=Legendaire +item.ebwizardry:wizard_armour.legendary=Légendaire item.ebwizardry:wizard_armour.buff=-%1$s en coût de %2$s item.ebwizardry:wizard_armour.mana=Mana: %1$s/%2$s item.ebwizardry:wizard_hat.name=Chapeau de sorcier item.ebwizardry:wizard_robe.name=Robe de sorcier -item.ebwizardry:wizard_leggings.name=Pantalon de sorcier +item.ebwizardry:wizard_leggings.name=Guêtres de sorcier item.ebwizardry:wizard_boots.name=Chaussures de sorcier item.ebwizardry:wizard_hat_fire.name=Chapeau de pyromancien item.ebwizardry:wizard_robe_fire.name=Robe de pyromancien -item.ebwizardry:wizard_leggings_fire.name=Pantalon de pyromancien +item.ebwizardry:wizard_leggings_fire.name=Guêtres de pyromancien item.ebwizardry:wizard_boots_fire.name=Bottes de pyromancien item.ebwizardry:wizard_hat_ice.name=Chapeau de cryomancien item.ebwizardry:wizard_robe_ice.name=Robe de cryomancien -item.ebwizardry:wizard_leggings_ice.name=Pentalon de cryomancien +item.ebwizardry:wizard_leggings_ice.name=Guêtres de cryomancien item.ebwizardry:wizard_boots_ice.name=Bottes de cryomancien -item.ebwizardry:wizard_hat_lightning.name=Chapeau d'electomancien -item.ebwizardry:wizard_robe_lightning.name=Robe d'electromancien -item.ebwizardry:wizard_leggings_lightning.name=Pentalon d'electromancien -item.ebwizardry:wizard_boots_lightning.name=Bottes d'electromancien +item.ebwizardry:wizard_hat_lightning.name=Chapeau d'éectomancien +item.ebwizardry:wizard_robe_lightning.name=Robe d'électromancien +item.ebwizardry:wizard_leggings_lightning.name=Guêtres d'électromancien +item.ebwizardry:wizard_boots_lightning.name=Bottes d'électromancien -item.ebwizardry:wizard_hat_necromancy.name=Chapeau de necromancie -item.ebwizardry:wizard_robe_necromancy.name=Robe de necromancien -item.ebwizardry:wizard_leggings_necromancy.name=Pentalon de necromacien +item.ebwizardry:wizard_hat_necromancy.name=Chapeau de nécromancien +item.ebwizardry:wizard_robe_necromancy.name=Robe de nécromancien +item.ebwizardry:wizard_leggings_necromancy.name=Guêtres de necromacien item.ebwizardry:wizard_boots_necromancy.name=Bottes de necromancie -item.ebwizardry:wizard_hat_earth.name=Chapeau de Geomancien -item.ebwizardry:wizard_robe_earth.name=Robe de Geomancien -item.ebwizardry:wizard_leggings_earth.name=Pentalon de Geomancien -item.ebwizardry:wizard_boots_earth.name=Bottes de Geomancien +item.ebwizardry:wizard_hat_earth.name=Chapeau de géomancien +item.ebwizardry:wizard_robe_earth.name=Robe de géomancien +item.ebwizardry:wizard_leggings_earth.name=Guêtres de géomancien +item.ebwizardry:wizard_boots_earth.name=Bottes de géomancien item.ebwizardry:wizard_hat_sorcery.name=Chapeau de sorcier item.ebwizardry:wizard_robe_sorcery.name=Robe de sorcier -item.ebwizardry:wizard_leggings_sorcery.name=Pentalon de sorcier +item.ebwizardry:wizard_leggings_sorcery.name=Guêtres de sorcier item.ebwizardry:wizard_boots_sorcery.name=Bottes de sorcier item.ebwizardry:wizard_hat_healing.name=Chapeau de soigneur item.ebwizardry:wizard_robe_healing.name=Robe de soigneur -item.ebwizardry:wizard_leggings_healing.name=Pentalon de soigneur +item.ebwizardry:wizard_leggings_healing.name=Guêtres de soigneur item.ebwizardry:wizard_boots_healing.name=Bottes de soigneur -item.ebwizardry:spawn_wizard.name=Oeuf de sorcier -item.ebwizardry:spawn_evil_wizard.name=Oeuf de sorcier maléfique +item.ebwizardry:spawn_wizard.name=Oeuf de mage +item.ebwizardry:spawn_evil_wizard.name=Oeuf de mage noir item.ebwizardry:spectral_helmet.name=Casque spectral item.ebwizardry:spectral_chestplate.name=Plastron spectral item.ebwizardry:spectral_leggings.name=Jambières spectrales item.ebwizardry:spectral_boots.name=Bottes spectrales +item.ebwizardry:ring_condensing.name=Anneau de condensation +item.ebwizardry:ring_condensing.desc=Régénère lentement le mana de toutes les baguettes de la barre d'inventaire +item.ebwizardry:ring_siphoning.name=Anneau de siphon +item.ebwizardry:ring_siphoning.desc=Augmente de 30%% le mana siphoné +item.ebwizardry:ring_battlemage.name=Anneau du mage de guerre +item.ebwizardry:ring_battlemage.desc=Tenir une épée dans votre main principale et une baguette dans l'autre octroie 10%% de dégâts magiques supplémentaires +item.ebwizardry:ring_combustion.name=Anneau de combustion +item.ebwizardry:ring_combustion.desc=Les créatures tuées par des sorts de feu explosent +item.ebwizardry:ring_fire_melee.name=Anneau de touché embrasé +item.ebwizardry:ring_fire_melee.desc=Frapper une créature avec un baton de feu l'embrase +item.ebwizardry:ring_fire_biome.name=Anneau du désert +item.ebwizardry:ring_fire_biome.desc=Les sorts de feu sont 30%% plus puissants dans les biomes chauds +item.ebwizardry:ring_disintegration.name=Anneau des braises ardentes +item.ebwizardry:ring_disintegration.desc=Tous les sorts de feu d'attaque désintègrent leur victime +item.ebwizardry:ring_ice_melee.name=Anneau de touché glacial +item.ebwizardry:ring_ice_melee.desc=Frapper une entité avec une baguette de glace lui donne l'effet Morsure du froid +item.ebwizardry:ring_ice_biome.name=Anneau de glaciation +item.ebwizardry:ring_ice_biome.desc=Les sorts de glace sont 30%% plus puissants dans les biomes enneigés +item.ebwizardry:ring_arcane_frost.name=Anneau des arcanes gelées +item.ebwizardry:ring_arcane_frost.desc=Les créatures tuées par des sorts de glace projettent des éclats de glace +item.ebwizardry:ring_shattering.name=Anneau écrasant +item.ebwizardry:ring_shattering.desc=Les attaques de mêlée sur des entités avec Morsure du froid ont une chance de les faires éclater +item.ebwizardry:ring_lightning_melee.name=Anneau de conduction +item.ebwizardry:ring_lightning_melee.desc=Frapper une entité avec une baguette de foudre lance des éclairs sur les créatures à proximité +item.ebwizardry:ring_storm.name=Anneau des tempêtes +item.ebwizardry:ring_storm.desc=Pendant les orages, les sorts de foudre ont un délai de récupération drastiquement réduit +item.ebwizardry:ring_seeking.name=Anneau d'attraction +item.ebwizardry:ring_seeking.desc=Tous les sorts de projectile poursuivent leur cible +item.ebwizardry:ring_hammer.name=Anneau de Thalek le Tout-puissant +item.ebwizardry:ring_hammer.desc=Les marteaux de foudre peuvent être ramassés et lancés +item.ebwizardry:ring_soulbinding.name=L'anneau du Marcheur d'âme +item.ebwizardry:ring_soulbinding.desc=L'âme des créatures touchées par un sort de nécromancie est liée à la vôtre +item.ebwizardry:ring_leeching.name=Anneau de sangsue +item.ebwizardry:ring_leeching.desc=Tous les sorts de nécromancie ont 30%% de chance de déclencher un drain de vie +item.ebwizardry:ring_necromancy_melee.name=Anneau de touché nécrotique +item.ebwizardry:ring_necromancy_melee.desc=Frapper une entité avec une baguette de nécromancie lui inflige l'effet Wither +item.ebwizardry:ring_mind_control.name=Anneau du Psychomancien +item.ebwizardry:ring_mind_control.desc=Les créatures hypnotisées ont une chance d'affecter les entités aux alentours +item.ebwizardry:ring_poison.name=Anneau du serpent +item.ebwizardry:ring_poison.desc=Tous les sorts de géomancie empoisonnent leur cible +item.ebwizardry:ring_earth_melee.name=Anneau de touché venimeux +item.ebwizardry:ring_earth_melee.desc=Frapper une entité avec une baguette de géomancie l'empoisonne +item.ebwizardry:ring_earth_biome.name=Anneau de la dryade +item.ebwizardry:ring_earth_biome.desc=Les sorts de géomancie sont 30%% plus forts dans les forêts et canopées +item.ebwizardry:ring_full_moon.name=Anneau du loup +item.ebwizardry:ring_full_moon.desc=Les sorts de géomancie ont un délai de récupération drastiquement réduit à la pleine lune +item.ebwizardry:ring_extraction.name=Anneau d'extraction +item.ebwizardry:ring_extraction.desc=Tuer avec un sort de Sorcellerie offre un bonus de mana +item.ebwizardry:ring_mana_return.name=Anneau du perfectionniste +item.ebwizardry:ring_mana_return.desc=Les flèches de force qui ne touchent pas leur cible rendent leur mana +item.ebwizardry:ring_blockwrangler.name=Anneau du jongleur +item.ebwizardry:ring_blockwrangler.desc=Les blocs jetés infligent deux fois plus de dégâts +item.ebwizardry:ring_conjurer.name=Anneau du conjurateur +item.ebwizardry:ring_conjurer.desc=Les objets conjurés durent deux fois plus longtemps +item.ebwizardry:ring_defender.name=Anneau du défenseur +item.ebwizardry:ring_defender.desc=Vos projectiles traversent les champs de force alliés +item.ebwizardry:ring_paladin.name=Anneau du paladin +item.ebwizardry:ring_paladin.desc=Quand vous vous soignez vous ou un allié, tous les alliés autour en profitent +item.ebwizardry:ring_interdiction.name=Anneau d'interdiction +item.ebwizardry:ring_interdiction.desc=Vos champs de force infligent des dégâts à ceux qui les touchent + +item.ebwizardry:amulet_arcane_defence.name=Amulette de défense arcanique +item.ebwizardry:amulet_arcane_defence.desc=Régénère lentement le mana de tous les vêtements magiques +item.ebwizardry:amulet_warding.name=Amulette de barrière +item.ebwizardry:amulet_warding.desc=Réduits les dégâts magiques reçus 10%% +item.ebwizardry:amulet_wisdom.name=Amulette du sage +item.ebwizardry:amulet_wisdom.desc=Réduit grandement les risques d'effet négatifs en utilisant un sort inconnu +item.ebwizardry:amulet_fire_protection.name=Amulette d'ignifugation +item.ebwizardry:amulet_fire_protection.desc=Réduit les dégâts de feu 30%% +item.ebwizardry:amulet_fire_cloaking.name=Amulette de manteau de feu +item.ebwizardry:amulet_fire_cloaking.desc=Réduits les dégâts reçus de 75%% si vous êtes dans un cercle de feu personnel ou allié +item.ebwizardry:amulet_ice_immunity.name=Amulette de permafrost +item.ebwizardry:amulet_ice_immunity.desc=Octroie une immunité aux morsures du froid +item.ebwizardry:amulet_ice_protection.name=Amulette gelée +item.ebwizardry:amulet_ice_protection.desc=Réduit les dégâts de glace reçus de 30%% +item.ebwizardry:amulet_potential.name=Amulette chargée +item.ebwizardry:amulet_potential.desc=Offre 15%% de chance t'électrocuter les entités qui vous attaquent en mêlée +item.ebwizardry:amulet_channeling.name=Amulette paratonnerre +item.ebwizardry:amulet_channeling.desc=Offre 30%% de chance d'annuler les dégâts de foudre reçus +item.ebwizardry:amulet_lich.name=Amulette de la liche +item.ebwizardry:amulet_lich.desc=Offre 15%% de chance d'infliger à un sbire à proximité, les dégâts qui vous étaient destinés +item.ebwizardry:amulet_wither_immunity.name=Amulette de perles nécrosées +item.ebwizardry:amulet_wither_immunity.desc=Octroie une immunité totale à l'effet Wither +item.ebwizardry:amulet_glide.name=Amulette de plume +item.ebwizardry:amulet_glide.desc=Tomber de plus de 3 blocs a 50%% de chance d'activer un vol plané +item.ebwizardry:amulet_banishing.name=Amulette du néant +item.ebwizardry:amulet_banishing.desc=Les créatures qui vous attaquent en mêlée ont 15%% de chance d'être téléportés aléatoirement à proximité +item.ebwizardry:amulet_anchoring.name=Amulette d'ancrage +item.ebwizardry:amulet_anchoring.desc=Octroie une immunité aux déplacements magiques +item.ebwizardry:amulet_recovery.name=Amulette de guérison +item.ebwizardry:amulet_recovery.desc=Quand vous tombez en dessous de 50%% de vie, le mana de votre armure sera utilisé pour vous soigner +item.ebwizardry:amulet_transience.name=Amulet éthérée +item.ebwizardry:amulet_transience.desc=Octroie 25%% de chance de vous rendre éthéré si vous êtes mortellement blessé +item.ebwizardry:amulet_resurrection.name=Amulette de l'immortel +item.ebwizardry:amulet_resurrection.desc=Vous pouvez lancer le sort de résurrection sur vous-même depuis votre tombe +item.ebwizardry:amulet_auto_shield.name=Amulette de Hyeleth +item.ebwizardry:amulet_auto_shield.desc=Octroie 25%% de chance de lancer un sort de bouclier s'il est attaché à une baguette de votre barre d'inventaire + +item.ebwizardry:charm_haggler.name=Charme du marchand +item.ebwizardry:charm_haggler.desc=Les sorciers proposeront de nouveaux échanges à chaque interaction +item.ebwizardry:charm_experience_tome.name=Tome de l'apprenti +item.ebwizardry:charm_experience_tome.desc=Augmente la vitesse de progression des baguettes 50%% +item.ebwizardry:charm_auto_smelt.name=Marque du métallurgiste +item.ebwizardry:charm_auto_smelt.desc=Le Fourneau de poche agit automatiquement s'il est attaché à une baguette de votre barre d'inventaire +item.ebwizardry:charm_lava_walking.name=Cœur de glace infernal +item.ebwizardry:charm_lava_walking.desc=Le sort Pied gelé transforme la lave en croûte d'obsidienne +item.ebwizardry:charm_storm.name=Cumulonimbus en bouteille +item.ebwizardry:charm_storm.desc=Invoquer une tempête provoquera toujours un orage +item.ebwizardry:charm_minion_health.name=Tête de zombie en obsidienne +item.ebwizardry:charm_minion_health.desc=Les créatures invoquées sont 25%% plus fortes +item.ebwizardry:charm_minion_variants.name=Talisman de transformation +item.ebwizardry:charm_minion_variants.desc=Les zombies invoqués sont momifiés; les squelettes invoqués sont des vagabonds +item.ebwizardry:charm_flight.name=Aile de scarabée d'émeraude +item.ebwizardry:charm_flight.desc=Le vol et le vol plané sont 50%% plus rapides +item.ebwizardry:charm_growth.name=Charme de la fleur de cristal +item.ebwizardry:charm_growth.desc=L'aura de croissance a 35%% de chance de faire grandir complètement les cultures +item.ebwizardry:charm_abseiling.name=Vigne enchantée +item.ebwizardry:charm_abseiling.desc=Maintenir sneak tout en tenant une liane permet de l'étendre +item.ebwizardry:charm_silk_touch.name=Orbre de pierre-de-lune +item.ebwizardry:charm_silk_touch.desc=Les blocs brisés avec le sort Minage sont conservés indemnes +item.ebwizardry:charm_stop_time.name=Montre à gousset péliculaire +item.ebwizardry:charm_stop_time.desc=Le ralentissement temporel arrête complètement le temps +item.ebwizardry:charm_light.name=La lanterne d'Elevinia +item.ebwizardry:charm_light.desc=Les sources de lumières invoquées sont éternelles et peuvent être éteintes en réactivant le sort dessus +item.ebwizardry:charm_transportation.name=Bousolle antique +item.ebwizardry:charm_transportation.desc=Permet de marquer jusqu'à 4 cercles de téléportation +item.ebwizardry:charm_feeding.name=Provisions sans fond +item.ebwizardry:charm_feeding.desc=Le sort Restauration est lancé automatiquement s'il est attaché à une baguette dans la barre d'inventaire + +item.charge_status.full=Plein +item.charge_status.almost_full=Quasiment plein +item.charge_status.mostly_full=Assez plein +item.charge_status.half_full=À moitié plein +item.charge_status.mostly_empty=Assez vide +item.charge_status.almost_empty=Quasiment vide +item.charge_status.empty=Vide + entity.ebwizardry:summonedcreature.nameplate=%1$s's %2$s entity.ebwizardry:summonedcreature.nameplate_fallback=Someone's %1$s +entity.ebwizardry:wizard.greeting_0=Bonjour, voyageur. +entity.ebwizardry:wizard.greeting_1=Salutations, voyageur. Qu'est-ce qui vous amène? +entity.ebwizardry:wizard.greeting_2=Ah, il est bon de voir quelqu'un d'autre par ici. +entity.ebwizardry:wizard.speech_0=Pourrais-je vous proposer quelques sorts? +entity.ebwizardry:wizard.speech_1=La magie est partout, si vous savez où regarder... +entity.ebwizardry:wizard.speech_2=Il reste beaucoup à découvrir sur les arcanes, voyageur. +entity.ebwizardry:wizard.speech_3=Peut-être voulez-vous partager votre savoir? +entity.ebwizardry:wizard.speech_4=Étudier la magie est la chose la plus intéressante au monde, vous ne trouvez pas? +entity.ebwizardry:wizard.farewell_0=Bonne chance dans votre quête, aventurier. +entity.ebwizardry:wizard.farewell_1=Nous nous reverrons très bientôt, mon ami. +entity.ebwizardry:wizard.farewell_2=Alors adieu, voyageur. +entity.ebwizardry:wizard.combat_0=Arrière, viles créatures! +entity.ebwizardry:wizard.combat_1=Disparaissez, peste! +entity.ebwizardry:wizard.combat_2=Les morts-vivants ne sont pas les bienvenus, zou! +entity.ebwizardry:wizard.combat_3=Recule, créature des ténèbres! +entity.ebwizardry:wizard.combat_4=C'est bien ma veine! Hors d'ici, monstres! +entity.ebwizardry:wizard.combat_5=Retournez dans vos limbes sombres, monstres! +entity.ebwizardry:wizard.player_combat_0=Tu vas le regretter, voyageur! +entity.ebwizardry:wizard.player_combat_1=Que crois-tu faire?! +entity.ebwizardry:wizard.player_combat_2=Seul un idiot mettrait un magicien en colère! +entity.ebwizardry:wizard.player_combat_3=Tu va payer pour ton arrogance, aventurier! +entity.ebwizardry:wizard.player_combat_4=Prépare toi à combattre, mécréant! +entity.ebwizardry:wizard.player_combat_5=Affrontez ma colère! + entity.ebwizardry:zombie_minion.name=Zombie +entity.ebwizardry:husk_minion.name=Momie entity.ebwizardry:skeleton_minion.name=Squelette +entity.ebwizardry:stray_minion.name=Vagabond entity.ebwizardry:spider_minion.name=Araignée entity.ebwizardry:blaze_minion.name=Blaze entity.ebwizardry:wither_skeleton_minion.name=Wither squelette entity.ebwizardry:ice_wraith.name=Spectre de glace entity.ebwizardry:lightning_wraith.name=Spectre de foudre entity.ebwizardry:shadow_wraith.name=Spectre d'ombre -entity.ebwizardry:spirit_wolf.name=Esprit de loup -entity.ebwizardry:spirit_horse.name=Esprit de cheval -entity.ebwizardry:ice_giant.name=Geant de glace +entity.ebwizardry:spirit_wolf.name=Loup spirituel +entity.ebwizardry:spirit_horse.name=Cheval spirituel +entity.ebwizardry:ice_giant.name=Géant de glace entity.ebwizardry:phoenix.name=Phoenix -entity.ebwizardry:wizard.name=Sorcier -entity.ebwizardry:magic_slime.name=Slime magique +entity.ebwizardry:wizard.name=Mage +entity.ebwizardry:magic_slime.name=Limon magique entity.ebwizardry:silverfish_minion.name=Poisson d'argent -entity.ebwizardry:storm_elemental.name=Tempête elementaire -entity.ebwizardry:evil_wizard.name=Sorcier +entity.ebwizardry:storm_elemental.name=Tempête élémentaire +entity.ebwizardry:evil_wizard.name=Mage noir entity.ebwizardry:decoy.name=Leurre +entity.ebwizardry:vex_minion.name=Vex entity.ebwizardry:magic_missile.name=Magique entity.ebwizardry:arc.name=Magique @@ -196,7 +420,7 @@ entity.ebwizardry:ice_spike.name=Magique entity.ebwizardry:black_hole.name=Trou noir entity.ebwizardry:shield.name=Bouclier -entity.ebwizardry:meteor.name=Meteor +entity.ebwizardry:meteor.name=Météor entity.ebwizardry:blizzard.name=Blizzard entity.ebwizardry:bubble.name=Bulle entity.ebwizardry:tornado.name=Tornade @@ -205,22 +429,22 @@ entity.ebwizardry:arrow_rain.name=Pluie de flèches entity.ebwizardry:healing_aura.name=Aura de soin entity.ebwizardry:forcefield.name=Champ de force entity.ebwizardry:ring_of_fire.name=Anneau de feu -entity.ebwizardry:earthquake.name=Seisme +entity.ebwizardry:earthquake.name=Séisme entity.ebwizardry:falling_grass.name=Chute d'herbe entity.ebwizardry:hailstorm.name=Grêle -entity.ebwizardry:lightning_pulse.name=Pulsation electrique +entity.ebwizardry:lightning_pulse.name=Impulsion electrique itemGroup.ebwizardry=Wizardry itemGroup.ebwizardryspells=Spells -advancement.ebwizardry:root=Sorcier +advancement.ebwizardry:root=Sorcellerie advancement.ebwizardry:root.desc=Le voyage d'un sorcier pour maîtriser les arcanes advancement.ebwizardry:crystal=Un mystérieux cristal... advancement.ebwizardry:crystal.desc=Miner un cristal magique advancement.ebwizardry:arcane_initiate=Initiation arcanique advancement.ebwizardry:arcane_initiate.desc=Fabriquer une baguette magique avec une pépite d'or, un bâton et un cristal magique -advancement.ebwizardry:apprentice=Apprenti Magicien -advancement.ebwizardry:apprentice.desc=Utilisez un tome d'arcane pour améliorer votre baguette +advancement.ebwizardry:apprentice=Apprenti sorcier +advancement.ebwizardry:apprentice.desc=Utilisez un tome des arcanes pour améliorer votre baguette advancement.ebwizardry:master=Maître des Arcanes advancement.ebwizardry:master.desc=Obtenir une baguette de maître advancement.ebwizardry:all_spells=Mage de tous les métiers @@ -265,12 +489,12 @@ advancement.ebwizardry:identify_spell=Examin arcanique advancement.ebwizardry:identify_spell.desc=Utiliser un parchemin pour identifier un livre ou un autre parchemin tile.ebwizardry:transportation_stone.confirm=Vous pouvez désormais revenir ici plus tard en lançant le sort %1$s -tile.ebwizardry:transportation_stone.invalid=Vous devez dabord faire un carré avec 8 pierres de transportation et vous y lier avec une baguette! +tile.ebwizardry:transportation_stone.invalid=Vous devez d'abord faire un carré avec 8 pierres de transportation et vous y lier avec une baguette! container.ebwizardry:arcane_workbench=Table des arcanes container.ebwizardry:arcane_workbench.apply=Appliquer container.ebwizardry:arcane_workbench.mana=Mana: -container.ebwizardry:arcane_workbench.upgrades=Amélioration appliquée: +container.ebwizardry:arcane_workbench.upgrades=Améliorations appliquées: tier.basic=Novice tier.apprentice=Apprenti @@ -281,23 +505,23 @@ element.simple=Aucun element.fire=Feu element.ice=Glace element.lightning=Foudre -element.necromancy=Necromancie -element.earth=Terra +element.necromancy=Nécromancie +element.earth=Terre element.sorcery=Sorcellerie element.healing=Soin element.simple.wizard=Sorcier element.fire.wizard=Pyromancien element.ice.wizard=Cryomancien -element.lightning.wizard=Electromancien -element.necromancy.wizard=Necromancien -element.earth.wizard=Geomancien +element.lightning.wizard=Électromancien +element.necromancy.wizard=Nécromancien +element.earth.wizard=Géomancien element.sorcery.wizard=Sorcier element.healing.wizard=Soigneur spelltype.attack=Attaque spelltype.defence=Defense -spelltype.utility=Utilité +spelltype.utility=Utilitaire spelltype.minion=Sbire spell.disabled=%1$s a été désactivé dans la config! @@ -307,40 +531,52 @@ spell.discover=Le sort %1$s a été découvert! spell.ebwizardry:agility=Agilité spell.ebwizardry:arc=Arc spell.ebwizardry:arcane_jammer=Perturbation arcanique +spell.ebwizardry:arcane_lock=Verrou arcanique spell.ebwizardry:arrow_rain=Pluie de flèche spell.ebwizardry:banish=Banissement spell.ebwizardry:black_hole=Trou noir -spell.ebwizardry:blink=Teleportation +spell.ebwizardry:blink=Transfert spell.ebwizardry:blizzard=Blizzard spell.ebwizardry:bubble=Bulle spell.ebwizardry:chain_lightning=Chaîne d'éclair +spell.ebwizardry:charge=Ruée spell.ebwizardry:clairvoyance=Clairvoyance -spell.ebwizardry:cobwebs=Toile +spell.ebwizardry:cobwebs=Toiles +spell.ebwizardry:combustion_rune=Rune de combustion spell.ebwizardry:conjure_armour=Conjuration d'armure spell.ebwizardry:conjure_bow=Conjuration d'arc +spell.ebwizardry:conjure_block=Conjuration de bloc spell.ebwizardry:conjure_pickaxe=Conjuration de pioche spell.ebwizardry:conjure_sword=Conjuration d'épée -spell.ebwizardry:cure_effects=Curation +spell.ebwizardry:containment=Restriction +spell.ebwizardry:cure_effects=Purification +spell.ebwizardry:curse_of_enfeeblement=Malédiction affaiblissante spell.ebwizardry:curse_of_soulbinding=Malédiction de liaison d'âme -spell.ebwizardry:darkness_orb=Orbe de ténèbre +spell.ebwizardry\:curse_of_undeath=Malédiction de non-vie +spell.ebwizardry:darkness_orb=Orbe des ténèbres spell.ebwizardry:darkvision=Nyctalopie spell.ebwizardry:dart=Dard -spell.ebwizardry:decay=Decomposition +spell.ebwizardry:decay=Décomposition spell.ebwizardry:decoy=Leurre -spell.ebwizardry:detonate=Detonation +spell.ebwizardry:detonate=Détonation spell.ebwizardry:diamondflesh=Peau de diamant -spell.ebwizardry:earthquake=Seisme -spell.ebwizardry:entrapment=Piège de ténèbre +spell.ebwizardry:disintegration=Désintégration +spell.ebwizardry:divination=Divination +spell.ebwizardry:dragon_fireball=Flammes de dragon +spell.ebwizardry:earthquake=Séisme +spell.ebwizardry:empowering_presence=Présence fortifiante +spell.ebwizardry:entrapment=Piège de ténèbres +spell.ebwizardry:evade=Évasion spell.ebwizardry:fireball=Boule de feu -spell.ebwizardry:firebolt=Eclair de feu +spell.ebwizardry:firebolt=Éclair de feu spell.ebwizardry:firebomb=Bombe de feu -spell.ebwizardry:fire_resistance=Resistance aux flammes +spell.ebwizardry:fire_resistance=Résistance aux flammes spell.ebwizardry:fire_sigil=Rune de feu spell.ebwizardry:fireskin=Aura de feu -spell.ebwizardry:firestorm=Tempête de feu +spell.ebwizardry:fire_breath=Souffle de feu spell.ebwizardry:flame_ray=Trait de feu -spell.ebwizardry:flaming_axe=Hache enflammé -spell.ebwizardry:flaming_weapon=Enflamment d'arme +spell.ebwizardry:flaming_axe=Hache enflammée +spell.ebwizardry:flaming_weapon=Embrasement d'arme spell.ebwizardry:flight=Vol spell.ebwizardry:font_of_mana=Fontaine de mana spell.ebwizardry:font_of_vitality=Fontaine de vitalité @@ -348,99 +584,119 @@ spell.ebwizardry:force_arrow=Flèche de force spell.ebwizardry:forcefield=Champ de force spell.ebwizardry:force_orb=Orbe de force spell.ebwizardry:forests_curse=Malédiction de la forêt +spell.ebwizardry:forest_of_thorns=Forêt épineuse spell.ebwizardry:freeze=Gel spell.ebwizardry:freezing_weapon=Gel d'arme spell.ebwizardry:frost_axe=Hache de glace spell.ebwizardry:frost_ray=Trait de glace spell.ebwizardry:frost_sigil=Rune de glace +spell.ebwizardry:frost_step=Pied de glace spell.ebwizardry:glide=Vol plané -spell.ebwizardry:greater_fireball=Grosse boule de feu +spell.ebwizardry:grapple=Liane +spell.ebwizardry:greater_fireball=Boule de feu majeure spell.ebwizardry:greater_heal=Soin magistral +spell.ebwizardry:greater_telekinesis=Télékinésie majeure +spell.ebwizardry:greater_ward=Protection majeure spell.ebwizardry:group_heal=Soin de groupe spell.ebwizardry:growth_aura=Aura de croissance spell.ebwizardry:hailstorm=Grêle spell.ebwizardry:heal=Soin spell.ebwizardry:heal_ally=Touché soignant spell.ebwizardry:healing_aura=Aura de soin -spell.ebwizardry:homing_spark=Etincelle auto-guidé +spell.ebwizardry:homing_spark=Étincelle chasseresse +spell.ebwizardry:iceball=Boule de glace spell.ebwizardry:ice_age=Âge de glace spell.ebwizardry:ice_charge=Charge de glace spell.ebwizardry:ice_lance=Lance de glace -spell.ebwizardry:ice_shard=Eclat de glace +spell.ebwizardry:ice_shard=Éclat de glace spell.ebwizardry:ice_shroud=Aura de glace spell.ebwizardry:ice_spikes=Pic de glace spell.ebwizardry:ice_statue=Statue de glace -spell.ebwizardry:ignite=Enflammer -spell.ebwizardry:imbue_weapon=imprégnation d'arme +spell.ebwizardry:ignite=Embraser +spell.ebwizardry:imbue_weapon=Imprégnation d'arme spell.ebwizardry:intimidate=Intimidation -spell.ebwizardry:invigorating_presence=Présance tonifiante +spell.ebwizardry:invigorating_presence=Présence tonifiante spell.ebwizardry:invisibility=Invisibilité spell.ebwizardry:invoke_weather=Tempête spell.ebwizardry:ironflesh=Peau de fer -spell.ebwizardry:leap=Saut -spell.ebwizardry:levitation=Levitation +spell.ebwizardry:leap=Bond +spell.ebwizardry:levitation=Lévitation spell.ebwizardry:life_drain=Drain de vie spell.ebwizardry:light=Lumière spell.ebwizardry:lightning_arrow=Flèche de foudre -spell.ebwizardry:lightning_bolt=Eclair +spell.ebwizardry:lightning_bolt=Éclair spell.ebwizardry:lightning_disc=Disque de foudre spell.ebwizardry:lightning_hammer=Marteau de foudre -spell.ebwizardry:lightning_pulse=Pulsation electrique +spell.ebwizardry:lightning_pulse=Impulsion électrique spell.ebwizardry:lightning_ray=Trait de foudre spell.ebwizardry:lightning_sigil=Rune de foudre -spell.ebwizardry:lightning_web=Eclair en chaîne -spell.ebwizardry:magic_missile=Missile magique -spell.ebwizardry:metamorphosis=Metamorphose -spell.ebwizardry:meteor=Meteor +spell.ebwizardry:lightning_web=Réseau de foudre +spell.ebwizardry:magic_missile=Projectile magique +spell.ebwizardry:metamorphosis=Métamorphose +spell.ebwizardry:meteor=Météor spell.ebwizardry:mind_control=Hypnose spell.ebwizardry:mind_trick=Confusion +spell.ebwizardry:mine=Minage +spell.ebwizardry:muffle=Amortissement spell.ebwizardry:none=[Emplacement vide] spell.ebwizardry:oakflesh=Peau de chêne -spell.ebwizardry:petrify=Petrification +spell.ebwizardry:paralysis=Paralysie +spell.ebwizardry:petrify=Pétrification spell.ebwizardry:phase_step=Passe muraille spell.ebwizardry:plague_of_darkness=Peste ténébreuse -spell.ebwizardry:pocket_furnace=Cuisson -spell.ebwizardry:pocket_workbench=Conjuration d'etabli +spell.ebwizardry:pocket_furnace=Conjuration de fourneau +spell.ebwizardry:pocket_workbench=Conjuration d'établi spell.ebwizardry:poison=Poison spell.ebwizardry:poison_bomb=Bombe de poison -spell.ebwizardry:replenish_hunger=Restoration +spell.ebwizardry:possession=Possession +spell.ebwizardry:ray_of_purification=Rayon purificateur +spell.ebwizardry:remove_curse=Purge arcanique +spell.ebwizardry:replenish_hunger=Restauration +spell.ebwizardry:resurrection=Résurrection +spell.ebwizardry:reversal=Échange spell.ebwizardry:ring_of_fire=Anneau de feu +spell.ebwizardry:satiety=Satiété spell.ebwizardry:shadow_ward=Miroir d'ombre spell.ebwizardry:shield=Barrière spell.ebwizardry:shockwave=Onde de choc -spell.ebwizardry:silverfish_swarm=Essaim de poisson d'argent +spell.ebwizardry:shulker_bullet=Projectile de shulker +spell.ebwizardry:silverfish_swarm=Essaim de poissons d'argent spell.ebwizardry:sixth_sense=Sixième sense -spell.ebwizardry:slime=Slime +spell.ebwizardry:slime=Limon +spell.ebwizardry:slow_time=Ralentissement temporel spell.ebwizardry:smoke_bomb=Bombe de fumée spell.ebwizardry:snare=Ronce spell.ebwizardry:snowball=Boule de neige -spell.ebwizardry:spark_bomb=Bombe electrique +spell.ebwizardry:spark_bomb=Bombe électrique spell.ebwizardry:spectral_pathway=Chemin spectral +spell.ebwizardry:speed_time=Accélération temporelle spell.ebwizardry:spider_swarm=Essaim d'araignées -spell.ebwizardry:static_aura=Aura electrique -spell.ebwizardry:summon_blaze=Invocation de blaize -spell.ebwizardry:summon_ice_giant=Invocation de geant de glace +spell.ebwizardry:static_aura=Aura électrique +spell.ebwizardry:summon_blaze=Invocation de blaze +spell.ebwizardry:summon_ice_giant=Invocation de géant de glace spell.ebwizardry:summon_ice_wraith=Invocation de spectre de glace spell.ebwizardry:summon_iron_golem=Invocation de golem de fer spell.ebwizardry:summon_lightning_wraith=Invocation de spectre de foudre -spell.ebwizardry:summon_phoenix=Invocation de Phoenix +spell.ebwizardry:summon_phoenix=Invocation de phoenix spell.ebwizardry:summon_shadow_wraith=Invocation de spectre d'ombre spell.ebwizardry:summon_skeleton=Invocation de squelette spell.ebwizardry:summon_skeleton_legion=Invocation de legion de squelette -spell.ebwizardry:summon_snow_golem=Invocation de golem de neige +spell.ebwizardry:summon_snow_golem=Invocation de bonhomme de neige spell.ebwizardry:summon_spirit_horse=Invocation de cheval spectral spell.ebwizardry:summon_spirit_wolf=Invocation de chien spectral spell.ebwizardry:summon_storm_elemental=Invocation d'élémentaire de foudre spell.ebwizardry:summon_wither_skeleton=Invocation de squelette wither spell.ebwizardry:summon_zombie=Invocation de zombie -spell.ebwizardry:telekinesis=Telekinesie +spell.ebwizardry:telekinesis=Télékinésie spell.ebwizardry:thunderbolt=Coup de tonnerre spell.ebwizardry:thunderstorm=Orage spell.ebwizardry:tornado=Tornade spell.ebwizardry:transience=Forme éthérée -spell.ebwizardry:transportation=Transportation -spell.ebwizardry:vanishing_box=Conjuration de coffre de l'ender +spell.ebwizardry:transportation=Téléportation +spell.ebwizardry:vanishing_box=Conjuration de coffre du néant +spell.ebwizardry:vex_swarm=Essaim de vex spell.ebwizardry:wall_of_frost=Mur de glace +spell.ebwizardry:ward=Protection spell.ebwizardry:water_breathing=Respiration aquatique spell.ebwizardry:whirlwind=Bourrasque spell.ebwizardry:wither=Wither @@ -449,6 +705,7 @@ spell.ebwizardry:wither_skull=Crâne de wither spell.ebwizardry:agility.desc=Augmente la vitesse de déplacement et la hauteur de saut du lanceur pendant 30 secondes. spell.ebwizardry:arc.desc=Tire une étincelle de foudre sur la cible. spell.ebwizardry:arcane_jammer.desc=Empêche la cible d'utiliser la magie pendant 15 secondes. +spell.ebwizardry:arcane_lock.desc=Crée une barrière impénétrable autour du conteneur, prévenant son ouverture ou sa destruction. Le lanceur et ses alliés peuvent toujours l'ouvrir, ceci dit. spell.ebwizardry:arrow_rain.desc="Archers, tirez!" spell.ebwizardry:banish.desc=Téléporte la cible contre sa volonté vers un emplacement aléatoire dans une certaine distance. spell.ebwizardry:black_hole.desc=Déchirez la réalité. @@ -456,14 +713,20 @@ spell.ebwizardry:blink.desc=Téléporte le lanceur sur une courte distance à l' spell.ebwizardry:blizzard.desc=Crée une zone de vent glacé tourbillonnant qui ralentit et endommage continuellement tout ce qui est piégé à l'intérieur. Le lanceur est immunisé contre les dégâts, mais il est quand même ralenti. spell.ebwizardry:bubble.desc=Lance un jet de bulles qui capture tout ce qu’il frappe dans une bulle qui flottera vers le haut. La cible tombera après un certain temps ou si la bulle est endommagée. spell.ebwizardry:chain_lightning.desc=Lance une étincelle de foudre sur la cible, qui frappe ensuite deux cibles supplémentaires. +spell.ebwizardry:charge.desc=Permet au lanceur de charger dans la direction où il regarde, repoussant et infligeant des dommages à ce qui se trouve sur son chemin. spell.ebwizardry:clairvoyance.desc=Révèle le chemin vers un lieu mémorisé. Avec ce sort sélectionné, sneak-cliquez sur un bloc pour définir l'emplacement. Jetez ce sort normalement pour révéler le chemin. Le chemin disparaîtra au bout de 90 secondes. spell.ebwizardry:cobwebs.desc=Crée des toiles d'araignées à l'endroit que vous visez, ce qui gêne grandement le mouvement des créatures prises dedans. Les toiles d'araignées disparaîtront après 20 secondes ou si elles sont cassées. +spell.ebwizardry:combustion_rune.desc=Place une rune magique, à l'endroit visé, qui explose au contact d'un ennemi. spell.ebwizardry:conjure_armour.desc=Crée une armure spectrale autour du lanceur qui offre une protection égale à celle d’une armure de fer. L'armure dure 60 secondes. Le lanceur doit avoir un emplacement d'armure vide. +spell.ebwizardry:conjure_block.desc=Crée un bloc spectral à l'endroit visé. Il disparaît au bout de 45 secondes, ou en lançant à nouveau le sort dessus. spell.ebwizardry:conjure_bow.desc=Crée un arc spectral avec des flèches illimitées qui dure 30 secondes. spell.ebwizardry:conjure_pickaxe.desc=Crée une pioche spectrale de force égale à une pioche de fer qui dure 30 secondes. spell.ebwizardry:conjure_sword.desc=Crée une épée spectrale de force égale à une épée de fer qui dure 30 secondes. +spell.ebwizardry:containment.desc=Restreint la cible dans un périmètre étroit autour de sa position initiale. spell.ebwizardry:cure_effects.desc=Supprime tous les effets de potion affectant actuellement le lanceur, bons ou mauvais. +spell.ebwizardry:curse_of_enfeeblement.desc="Il fut soudainement affaiblit, comme si on arrachait une partie de son énergie vitale" - Témoignage de la seule personne connue à avoir rencontré un membre du culte des Marcheurs d'âme... et à avoir survécu. spell.ebwizardry:curse_of_soulbinding.desc=Fait en sorte que l'âme de la cible soit inextricablement liée à celle du lanceur, ce qui signifie que tous les dommages qui lui sont infligés sont également infligés à la victime. Dure jusqu'à ce que la victime ou le lanceur meurt. +spell.ebwizardry:curse_of_undeath.desc=Inflige la non-vie à la cible, la faisant brûler au Soleil comme un zombie. Dure jusqu'à ce que la victime ou le lanceur meure. N'a aucun effet sur les créatures mort-vivantes. spell.ebwizardry:darkness_orb.desc=Tire un orbe d'énergie sombre se déplaçant lentement dans la direction que vous visez, causant l'effet wither à ce qu'il frappera. spell.ebwizardry:darkvision.desc=Donne la vision nocturne au lanceur pendant 45 secondes. spell.ebwizardry:dart.desc=Tire une flèche dans la direction que vous visez qui endommage et affaiblit sa cible. @@ -471,15 +734,20 @@ spell.ebwizardry:decay.desc=Crée une flaque de décomposition sur le sol qui in spell.ebwizardry:decoy.desc=Crée un clone illusoire du lanceur qui incite les monstres à l'attaquer à la place. Le leurre disparaîtra au bout de 30 secondes. spell.ebwizardry:detonate.desc=Provoque une explosion à l'endroit où vous vous visez, causant des dommages à toutes les créatures proches, y compris le lanceur, si il est trop proche. spell.ebwizardry:diamondflesh.desc="Vos flèches ne sont pas de taille contre moi!" +spell.ebwizardry:disintegration.desc=Crée un puissant rayon embrasé sur une courte distance, faisant exploser la cible en une multitude de braises. Les créatures qui marchent sur les braises s'enflamment. +spell.ebwizardry:divination.desc=Guide le lanceur vers les ressources et minerais à proximité. La puissance augmentera les chances de trouver des ressources plus rares. +spell.ebwizardry:dragon_fireball.desc=Lance une boule de feu du néant qui propage des nuées de brume toxique. spell.ebwizardry:earthquake.desc=Un vrai maître de la magie de la terre peut déplacer des montagnes. +spell.ebwizardry:empowering_presence.desc=Améliore les sorts du lanceur et de ses alliés à proximité pendant 30 secondes. spell.ebwizardry:entrapment.desc=Enferme la cible dans une sphère de ténèbres qui la tire de façon impuissante vers le haut et l’endommage continuellement. +spell.ebwizardry:evade.desc=Permet au lanceur de se déplacer rapidement de côté pour éviter une attaque. spell.ebwizardry:fireball.desc=Lance une boule de feu dans la direction que vous pointez. spell.ebwizardry:firebolt.desc=Lance un jet de feu à une courte distance de vous. spell.ebwizardry:firebomb.desc=Déclenche une bombe incendiaire dans la direction que vous pointez qui explose sous l’impact, ce qui met le feu à vos cibles. spell.ebwizardry:fire_resistance.desc=Confère au lanceur une résistance au feu pendant 30 secondes. spell.ebwizardry:fire_sigil.desc=Place un piège magique sur le sol qui endommage et met le feu à la créature qui le déclenche. spell.ebwizardry:fireskin.desc=Le lanceur s'enveloppe de feu pendant 30 secondes, ce qui fait s'enflammer tout ce qui l'attaque. -spell.ebwizardry:firestorm.desc="Je suis le Dragon" +spell.ebwizardry:fire_breath.desc="Je suis le Dragon" spell.ebwizardry:flame_ray.desc=Crée un souffle de flammes dans la direction que vous pointez qui enflamme et endommage continuellement les cibles. spell.ebwizardry:flaming_axe.desc=Crée une hache enflammée qui met le feu à l'ennemi lorsqu'il est touché. Dure 30 secondes. spell.ebwizardry:flaming_weapon.desc=La première arme de la barre de raccourci du lanceur est temporairement imprégnée du pouvoir de la flamme, ce qui incendie ses victimes. L'effet disparaît après 45 secondes. @@ -490,14 +758,19 @@ spell.ebwizardry:force_arrow.desc=Tire une flèche de force dans la direction qu spell.ebwizardry:forcefield.desc=Crée un champ de force autour du lanceur qui repousse les créatures et dévie les projectiles. spell.ebwizardry:force_orb.desc=Lance une sphère de force qui endommage et repousse les créatures proches lors de l'impact. spell.ebwizardry:forests_curse.desc="Comment osez-vous entrer dans ma forêt!" +spell.ebwizardry:forest_of_thorns.desc=Au milieu des bois se trouve une clairière,\nDans la clairière, un mage pratique,\nLes arts magique d'une antique ère,\nAu plus profond de la forêt d'épines. spell.ebwizardry:freeze.desc=Gèle la cible pendant 10 secondes. Peut également geler l'eau et créer de la neige sur le sol. spell.ebwizardry:freezing_weapon.desc=La première arme située sur la barre de raccourci du lanceur est temporairement imprégnée de gel, ce qui gèle ses victimes. L'effet disparaît après 45 secondes. spell.ebwizardry:frost_axe.desc=Crée une hache gelée qui gèle les ennemis quand ils sont touchés. Dure 30 secondes. spell.ebwizardry:frost_ray.desc=Crée un jet de givre dans la direction que vous visez, ce qui ralentit et endommage continuellement les cibles. spell.ebwizardry:frost_sigil.desc=Place un piège de glace magique sur le sol qui endommage et gèle la créature qui le déclenche. +spell.ebwizardry:frost_step.desc=Permet au lanceur, pendant 30 secondes, de geler l'eau lorsqu'il marche à proximité. +spell.ebwizardry:grapple.desc=Lance une liane magique pour se rapprocher d'un bloc ou attirer une entité. Relâchez le bouton pour arrêter le sort. spell.ebwizardry:glide.desc=Permet au lanceur de planer en l'air tout en maintenant enfoncé le bouton d'utilisation. spell.ebwizardry:greater_fireball.desc=Lance une grosse boule de feu dans la direction que vous pointez qui explose à l’impact. spell.ebwizardry:greater_heal.desc=Soigne le lanceur de 4 coeurs. +spell.ebwizardry:greater_telekinesis.desc=Permet au lanceur de saisir un bloc ou une entité pendant aussi longtemps que le bouton reste enfoncé. S'accroupir permet de lancer la cible sur une courte distance. +spell.ebwizardry:greater_ward.desc=Offre au lanceur un puissant effet de protection qui réduit grandement les dégâts reçus pendant 30 secondes. spell.ebwizardry:group_heal.desc=Soigne le lanceur et tous les alliés proches et les créatures invoquées de 3 coeurs. spell.ebwizardry:growth_aura.desc=Fais pousser toutes les cultures près du lanceur. Fais pousser également des herbes hautes et des fleurs sur l’herbe. spell.ebwizardry:hailstorm.desc=C'est au cours du grand hiver du troisième âge que les mages de glace ont découvert leur véritable pouvoir. @@ -505,6 +778,7 @@ spell.ebwizardry:heal.desc=Soigne le lanceur de sorts de 2 coeurs. spell.ebwizardry:heal_ally.desc=Soigne la cible de 2 cœurs et demi. spell.ebwizardry:healing_aura.desc=Crée une zone d'énergie de guérison qui régénère la santé de tous les alliés qui s'y trouvent. Tout mort-vivant à l'intérieur de l'aura de guérison subira lentement des dégâts. spell.ebwizardry:homing_spark.desc=Crée une étincelle flottante qui se déplace vers les ennemis. +spell.ebwizardry:iceball.desc=Lance une sphère gelée dans la direction ciblée. spell.ebwizardry:ice_age.desc="Vous serez gelés pour une éternité!" spell.ebwizardry:ice_charge.desc=Lance une charge de glace qui explose à l’impact, gèle les créatures proches et libère des éclats dans toutes les directions. spell.ebwizardry:ice_lance.desc=Tire une grande lance de glace dans la direction que vous pointez, qui transperce les cibles, les endommageant et les gelant. @@ -521,7 +795,7 @@ spell.ebwizardry:invoke_weather.desc=Change la météo dans le monde. spell.ebwizardry:ironflesh.desc=Améliore considérablement la résistance aux dégâts du lanceur pendant 30 secondes. spell.ebwizardry:leap.desc=Le lanceur effectue un super saut. spell.ebwizardry:levitation.desc=Fait léviter le lanceur vers le haut pendant que le bouton d'utilisation est maintenu enfoncé. Annulera également les dégâts de chute si utilisé avant de heurter le sol. -spell.ebwizardry:life_drain.desc=Crée un flux d’énergie de wither dans la direction que vous visez qui draine la vie de la cible et l’utilise pour régénérer progressivement votre santé. +spell.ebwizardry:life_drain.desc=Crée un flux d’énergie malsaine dans la direction que vous visez qui draine la vie de la cible et l’utilise pour régénérer progressivement votre santé. spell.ebwizardry:light.desc=Crée un point de lumière magique qui illumine les environs. Dure 30 secondes. spell.ebwizardry:lightning_arrow.desc=Tire une flèche de foudre dans la direction que vous visez. spell.ebwizardry:lightning_bolt.desc=Fait frapper la foudre là où vous visez. @@ -536,8 +810,11 @@ spell.ebwizardry:metamorphosis.desc=Change la cible en une autre variante. Ne fo spell.ebwizardry:meteor.desc=Certains sorciers veulent juste voir le monde brûler ... spell.ebwizardry:mind_control.desc=Prend le contrôle de l'esprit de la cible pendant 30 secondes, la faisant changer de côté et se battre pour le lanceur à sa place. Ne fonctionnera pas sur des créatures à la volonté trop forte. spell.ebwizardry:mind_trick.desc=Confond et désoriente la cible pendant 15 secondes, la rendant incapable d’attaquer efficacement. L'effet sera dissipé si la cible subit des dégâts. +spell.ebwizardry:mine.desc=Détruit le bloc visé. La puissance permet de détruire des blocs plus résistants. +spell.ebwizardry:muffle.desc=Atténue les bruits émis par le lanceur pendant 30 secondes. Tant que le sort est actif, les monstres ne peuvent vous détecter qu'en vous regardant directement. spell.ebwizardry:none.desc=Pour obtenir un livre de sorts avec la commande / give, utilisez les métadonnées: / give [joueur] ebwizardry: spell_book 1 [nom de sort] (si vous avez trouvé ce livre dans un coffre, un autre mod a tout gâché). spell.ebwizardry:oakflesh.desc=Améliore la résistance aux dégâts du lanceur pendant 30 secondes. +spell.ebwizardry:paralysis.desc=Délivre un puissant choc électrique qui paralyse la cible pendant 5 secondes. Les entités paralysées ne peuvent plus bouger, mais accusent toujours les dégâts. Attention cependant, trop de dégâts fait sortir la cible de la paralysie. spell.ebwizardry:petrify.desc=Transforme la cible en pierre jusqu'à ce qu'elle soit éclatée, avec une chance pour qu'elle éclate quand il fait noir. La cible ne peut ni bouger ni faire quoi que ce soit tant qu'elle est pétrifiée, mais est également insensible à tous les dégâts. spell.ebwizardry:phase_step.desc=Téléporte le lanceur à travers un mur épais d'un bloc devant eux. Les améliorations de portée augmenteront l'épaisseur à travers laquelle vous pourrez vous téléporter. spell.ebwizardry:plague_of_darkness.desc=Les ténèbres les dévoreront tous ... @@ -545,19 +822,28 @@ spell.ebwizardry:pocket_furnace.desc=Fait cuire jusqu'à 5 objets dans l'inventa spell.ebwizardry:pocket_workbench.desc=Permet au lanceur de fabriquer des objets comme s'il était à une table d'artisanat. spell.ebwizardry:poison.desc=Tire du poison dans la direction que vous pointez. spell.ebwizardry:poison_bomb.desc=Lance une bombe empoisonnée dans la direction que vous visez, qui explose à l’impact et empoisonne les créatures proches. +spell.ebwizardry:possession.desc="Glisse toi dans la peau de ton ennemi." +spell.ebwizardry:ray_of_purification.desc=Émet un rayon de lumière qui aveugle et inflige des dommages à ceux qu'il touche. Les morts-vivants subissent deux fois plus de dégâts et prennent feu. +spell.ebwizardry:remove_curse.desc=Supprime toutes les malédictions infligées au lanceur. spell.ebwizardry:replenish_hunger.desc=Remonte le niveau de nourriture du lanceur de 6 points de stamina. +spell.ebwizardry:resurrection.desc=Avec un maître soigneur à vos côtés, la mort devient... Optionnelle. +spell.ebwizardry:reversal.desc=Retire un effet négatif aléatoire du lanceur pour le placer sur la cible qui en souffrira jusqu'à la fin de l'effet. La puissance augment le nombre d'effets transférés. spell.ebwizardry:ring_of_fire.desc=Crée un anneau de feu autour du lanceur, infligeant des dégâts à tous les ennemis proches et les incendiant. +spell.ebwizardry:satiety.desc=Remplit 8 points de la barre de faim du lanceur. spell.ebwizardry:shadow_ward.desc=Crée un mur de ténèbres devant le lanceur de sorts, qui inflige la moitié des dégâts à l'attaquant. spell.ebwizardry:shield.desc=Crée une barrière protectrice de force qui bloque les projectiles et la magie. Confère également au lanceur un faible effet de résistance. spell.ebwizardry:shockwave.desc=Boom. +spell.ebwizardry:shulker_bullet.desc=Lance un projectile de shulker qui poursuit une cible et les fait léviter à l'impact. spell.ebwizardry:silverfish_swarm.desc="Ahhhh! Ils se MULTIPLIENT!" spell.ebwizardry:sixth_sense.desc=Permet au lanceur de détecter les emplacements des créatures proches, même à travers les murs, pendant 20 secondes. spell.ebwizardry:slime.desc=Embourbe la cible dans la boue qui le ralentit et l’endommage continuellement. La boue éclate après 10 secondes. +spell.ebwizardry:slow_time.desc=La chronomancie est l'art de manipuler l'écoulement du temps à sa guise. Des siècles durant, on a cru cet art perdu... Jusqu'à maintenant. spell.ebwizardry:smoke_bomb.desc=Lance une bombe de fumée dans la direction que vous pointez qui explose sous l’impact, libérant de la fumée et aveuglant les créatures à proximité pendant un court instant. spell.ebwizardry:snare.desc=Met au sol un piège qui endommage et ralentit brièvement la créature qui le déclenche. spell.ebwizardry:snowball.desc=Lance une boule de neige dans la direction que vous visez. spell.ebwizardry:spark_bomb.desc=Lance une charge de choc dans la direction que vous pointez, ce qui libère des étincelles chez les ennemis proches lors de l’impact. spell.ebwizardry:spectral_pathway.desc=Crée devant vous un pont magique indestructible qui s'étend sur 15 blocs. Le pont disparaît après 60 secondes. +spell.ebwizardry:speed_time.desc=...jour, nuit, aube, crépuscule...\: ainsi passe le temps, nous entravant dans un cycle éternel de... spell.ebwizardry:spider_swarm.desc=Invoque un essaim d'araignées venimeuses qui se battent pour vous. Les araignées disparaîtront au bout de 30 secondes ou si elles sont tuées. spell.ebwizardry:static_aura.desc=Entoure le lanceur avec un éclair pendant 30 secondes, projetant une étincelle sur tout ce qui le frappe. spell.ebwizardry:summon_blaze.desc=Invoque un blaze pour se battre à vos côtés. Le blaze disparaîtra après 30 secondes ou s'il est tué. @@ -568,11 +854,11 @@ spell.ebwizardry:summon_lightning_wraith.desc=Invoque un spectre de foudre pour spell.ebwizardry:summon_phoenix.desc=De la cendre ... spell.ebwizardry:summon_shadow_wraith.desc=Invoque un spectre de l'ombre qui se bat pour vous. spell.ebwizardry:summon_skeleton.desc=Invoque un squelette pour qu'il se batte pour vous. Le squelette disparaîtra après 30 secondes ou s'il est tué. -spell.ebwizardry:summon_skeleton_legion.desc="Lèvez-vous, armée de morts-vivants!" -spell.ebwizardry:summon_snow_golem.desc=Crée un golem de neige qui se bat pour vous. Dure jusqu'à la mort du golem des neiges. +spell.ebwizardry:summon_skeleton_legion.desc="Levez-vous, armée de morts-vivants!" +spell.ebwizardry:summon_snow_golem.desc=Crée un golem de neige qui se bat pour vous. Dure jusqu'à la mort du golem de neige. spell.ebwizardry:summon_spirit_horse.desc=Invoque un cheval spirituel à monter. Le cheval spirituel disparaîtra peu de temps après en être descendu, ou vous pouvez le renvoyer en faisant un shift-clic droit dessus avec n'importe quelle baguette. spell.ebwizardry:summon_spirit_wolf.desc=Invoque un loup spirituel qui se bat pour vous. L'esprit loup ne disparaîtra que s'il est tué, ou vous pouvez le renvoyer en faisant un shift-clic droit dessus avec n'importe quelle baguette. -spell.ebwizardry:summon_storm_elemental.desc="Elementaire de foudre: Une ancienne manifestation des éléments, il peut difficilement contenir la puissance brute qu'il contient." - Le guide du sorcier sur les êtres arcaniques, volume I_i +spell.ebwizardry:summon_storm_elemental.desc="Élementaire de foudre: Une ancienne manifestation des éléments, il peut difficilement contenir la puissance brute qu'il contient." - Le guide du sorcier sur les êtres arcaniques, volume II spell.ebwizardry:summon_wither_skeleton.desc=Invoque un squelette wither qui se bat pour vous. Le squelette flétri disparaîtra après 30 secondes ou s'il est tué. spell.ebwizardry:summon_zombie.desc=Invoque un zombie pour qu'il se batte pour vous. Le zombie disparaîtra au bout de 30 secondes ou s'il est tué. spell.ebwizardry:telekinesis.desc=Déplace un objet ou un autre petit objet vers vous, ou cliquez avec le bouton droit sur le bloc que vous regardez. Peut également être utilisé pour désarmer les joueurs. @@ -581,41 +867,130 @@ spell.ebwizardry:thunderstorm.desc="Mwahahahahahaha!" spell.ebwizardry:tornado.desc=Libère une tornade dans la direction que vous pointez qui projette tout ce qui se trouve sur son chemin vers le ciel. spell.ebwizardry:transience.desc=Rend la lanceur éthéré pendant 20 secondes. Le lanceur est à l'abri de tous les dégâts en forme éthérée mais ne peut pas casser, placer de blocs ou causer des dégâts. spell.ebwizardry:transportation.desc=Transporte le lanceur vers son cercle de pierres mémorisé. Pour utiliser ce sort, faites un cercle de pierres de transport, puis faites un clic droit dessus avec une baguette. -spell.ebwizardry:vanishing_box.desc=Donne au lanceur accès à son coffre de l'end. +spell.ebwizardry:vanishing_box.desc=Donne au lanceur accès à son coffre du néant. +spell.ebwizardry:vex_swarm.desc=Invoque un essaim de vex pour qu'il se batte pour vous. Les vex disparaissent au bout de 30 secondes ou s'ils sont tués. spell.ebwizardry:wall_of_frost.desc=L'hiver au bout des doigts. +spell.ebwizardry:ward.desc=Donne au lanceur un effet protecteur qui réduit les dégâts magiques reçus. spell.ebwizardry:water_breathing.desc=Permet au lanceur de respirer sous l'eau pendant 60 secondes. spell.ebwizardry:whirlwind.desc=Propulse la cible. -spell.ebwizardry:wither.desc=Tire un rayon d'obscurité qui inflige l'effet wither à tout ce qu'il touche. +spell.ebwizardry:wither.desc=Tire un rayon ténébreux qui inflige l'effet wither à tout ce qu'il touche. spell.ebwizardry:wither_skull.desc=Lance un crâne de wither dans la direction que vous pointez. -spell.ebwizardry:invoke_weather.sun=La pluie a cessé... +spell.ebwizardry:invoke_weather.sun=La pluie s'estompe... spell.ebwizardry:invoke_weather.rain=Le ciel se couvre... -spell.ebwizardry:transportation.missing=Vos pierres de téléportation sont obstruées ou inaccessible +spell.ebwizardry:transportation.missing=Vos pierres de téléportation sont obstruées ou inexistantes! spell.ebwizardry:transportation.undefined=Vous devez vous lier à un cercle de pierre de téléportation avant! -spell.ebwizardry:transportation.wrongdimension=Votre cercle de téléportation est dans une autre dimension... +spell.ebwizardry:transportation.wrongdimension=Vous n'avez aucun cercle de téléportation dans cette dimension... spell.ebwizardry:clairvoyance.searching=Recherche... spell.ebwizardry:clairvoyance.confirm=Une marque a été laissé. Utilisez le sort %1$s pour vous montrer le chemin pour y revenir plus tard. spell.ebwizardry:clairvoyance.outofrange=La marque est trop loin ou est inaccessible... spell.ebwizardry:clairvoyance.undefined=Vous devez dabord deposer une marque avec sneak-clic droit! spell.ebwizardry:clairvoyance.wrongdimension=Votre marque est dans une dimension... +spell.ebwizardry:possession.insufficienthealth=Vous n'avez pas assez de vie pour posséder %1$s! +spell.ebwizardry:possession.success=Appuyez sur %s pour arrêter la possession. +spell.ebwizardry:divination.nothing=Rien ne se passe. +spell.ebwizardry:divination.weak=Vous sentez votre baguette vibrer légèrement vers %s. +spell.ebwizardry:divination.moderate=Vous sentez votre baguette vibrer sérieusement vers %s. +spell.ebwizardry:divination.strong=Votre baguette vibre énergiquement vers %s. +spell.ebwizardry:divination.very_strong=Votre baguette tend très fortement vers %s. +spell.ebwizardry:divination.down=le bas +spell.ebwizardry:divination.up=le haut +spell.ebwizardry:divination.front=l'avant +spell.ebwizardry:divination.back=l'arrière +spell.ebwizardry:divination.left=la gauche +spell.ebwizardry:divination.right=la droite +spell.ebwizardry:resurrection.resurrect_ally=%s a été ressuscité par %s +spell.ebwizardry:resurrection.resurrect_self=%s est revenu à la vie +spell.ebwizardry:resurrection.button_wait=Ressusciter (%ss) +spell.ebwizardry:resurrection.button_ready=Ressusciter + +forfeit.ebwizardry:do_nothing=Rien ne se passe. + +forfeit.ebwizardry:burn_self=Le %s s'embrase entre vos mains! +forfeit.ebwizardry:fireball=Une boule de feu apparaît devant vous! +forfeit.ebwizardry:firebomb=Une bombe de feu apparaît au dessus de vous! +forfeit.ebwizardry:explode=Une soudaine explosion vous projette du sol! +forfeit.ebwizardry:blazes=Soudainement, un blaze hostile apparaît devant vous! +forfeit.ebwizardry:burn_surroundings=La terre brûle autour de vous! +forfeit.ebwizardry:meteors=L'Armageddon est sur vous! + +forfeit.ebwizardry:freeze_self=Vous sentez un courant glacial à travers vous! +forfeit.ebwizardry:freeze_self_2=Un froid si intense émane de %s qu'il vous gèle sur place! +forfeit.ebwizardry:ice_spikes=Des pieux de glace apparaissent sous vos pieds! +forfeit.ebwizardry:blizzard=Vous êtes soudainement entouré d'un blizzard mordant! +forfeit.ebwizardry:ice_wraiths=Soudainement, des spectres gelés hostiles apparaissent devant vous! +forfeit.ebwizardry:hailstorm=Des pointes de glace acérées se matérialisent au-dessus de vous! +forfeit.ebwizardry:ice_giant=Un géant de glace hostile apparaît devant vous! + +forfeit.ebwizardry:thunder=Le parchemin émet un bruit assourdissant et vous projette au sol! +forfeit.ebwizardry:storm=Il semble qu'une tempête se prépare! +forfeit.ebwizardry:lightning_sigils=Regardez où vous marchez! +forfeit.ebwizardry:lightning=Vous êtes frappés par la foudre! +forfeit.ebwizardry:paralyse_self=Vous êtes paralysé! +forfeit.ebwizardry:lightning_wraiths=Soudainement, des spectres de foudre apparaissent devant vous! +forfeit.ebwizardry:storm_elementals=Des élémentaires de tempête apparaissent autour de vous! + +forfeit.ebwizardry:nausea=...hein? Que s'est-il passé? Où suis-je? +forfeit.ebwizardry:zombie_horde=Une horde de zombies se lèvent contre vous! +forfeit.ebwizardry:wither_self=Les ténèbres emplissent votre âme! +forfeit.ebwizardry:cripple_self=Votre %s pousse un hurlement et vous laisse de profondes marques! +forfeit.ebwizardry:shadow_wraiths=Des spectres ténébreux apparaissent autour de vous! + +forfeit.ebwizardry:snares=C'est un piège! +forfeit.ebwizardry:squid=POULPE! +forfeit.ebwizardry:uproot_plants=Les plantes aux alentours sont déracinées! +forfeit.ebwizardry:poison_self=Vous êtes empoisonné! +forfeit.ebwizardry:flood=De l'eau jaillit autour de vous! +forfeit.ebwizardry:bury_self=Le sol s'effondre sous vos pieds! + +forfeit.ebwizardry:spill_inventory=Vos objets s'éparpillent d'eux-même autour de vous! +forfeit.ebwizardry:teleport_self=Vous êtes instantanément téléporté ailleurs! +forfeit.ebwizardry:levitate_self=Vous commencez à flotter désespérément dans les airs! +forfeit.ebwizardry:vex_horde=Une horde de vex apparaît autour de vous! +forfeit.ebwizardry:black_hole=Un vortex tourbillonant apparaît devant vous! +forfeit.ebwizardry:arrow_rain=Un barrage de flèches apparaît devant vous! + +forfeit.ebwizardry:damage_self=Aïe! +forfeit.ebwizardry:spill_armour=Votre armure tombe sans explication! +forfeit.ebwizardry:hunger=Vous vous sentez soudainement affamé! +forfeit.ebwizardry:blind_self=Vous êtes aveugle! +forfeit.ebwizardry:weaken_self=Vous êtes passablement affaibli! +forfeit.ebwizardry:jam_self=Votre magie est réduite à néant! +forfeit.ebwizardry:curse_self=Vous êtes devenu un mort-vivant! + potion.ebwizardry:frost=Morsure du froid potion.ebwizardry:fireskin=Aura de feu potion.ebwizardry:ice_shroud=Aura de glace -potion.ebwizardry:static_aura=Aura electrique -potion.ebwizardry:transience=Forme etheree -potion.ebwizardry:decay=Decomposition +potion.ebwizardry:static_aura=Aura électrique +potion.ebwizardry:transience=Forme éthérée +potion.ebwizardry:decay=Décomposition potion.ebwizardry:sixth_sense=Sixième sense potion.ebwizardry:arcane_jammer=Pertubation arcanique potion.ebwizardry:mind_trick=Confusion potion.ebwizardry:mind_control=Hypnose potion.ebwizardry:font_of_mana=Fontaine de mana potion.ebwizardry:fear=Peur +potion.ebwizardry:curse_of_soulbinding=Malédiction de liaison d'âme +potion.ebwizardry:paralysis=Paralysie +potion.ebwizardry:muffle=Pied léger +potion.ebwizardry:ward=Barrière +potion.ebwizardry:slow_time=Ralentissement temporel +potion.ebwizardry:empowerment=Fortification +potion.ebwizardry:curse_of_enfeeblement=Malédiction affaiblissante +potion.ebwizardry:curse_of_undeath=Mort-vie +potion.ebwizardry:containment=Restriction +potion.ebwizardry:frost_step=Pied de glace enchantment.ebwizardry:magic_sword=Imprégné enchantment.ebwizardry:magic_bow=Imprégné enchantment.ebwizardry:flaming_weapon=Imprégné de feu enchantment.ebwizardry:freezing_weapon=Imprégné de gel +enchantment.ebwizardry:shocking_weapon=Imprégné de foudre + +enchantment.ebwizardry:magic_protection=Protection magique +enchantment.ebwizardry:frost_protection=Protection au froid +enchantment.ebwizardry:shock_protection=Protection à la foudre key.categories.ebwizardry=Wizardry @@ -625,14 +1000,19 @@ key.ebwizardry.previous_spell=Sort précédent death.attack.wizardry_magic=%1$s a été tué par %2$s avec de la magie death.attack.indirect_wizardry_magic=%1$s a été tué par %2$s avec de la magie -commands.ebwizardry:cast.usage=/%1$s [joueur] [multiplicateur dommage] [multiplicateur distance] [multiplicateur durée] [multiplicateur explosion] -commands.ebwizardry:cast.success=Sort %1$s lancé avec succès -commands.ebwizardry:cast.success_continuous=Sort %1$s lancé; recommencer la commande pour arreter -commands.ebwizardry:cast.success_remote=Sort %1$s lancé en tant que %2$s -commands.ebwizardry:cast.success_remote_continuous=Sort %1$s lancé en tant que %2$s; recommencer la commande pour arreter +commands.ebwizardry:cast.usage=/%1$s [joueur | direction x y z] [durée] [modificateurs] +commands.ebwizardry:cast.success=Sort %s lancé avec succès +commands.ebwizardry:cast.success_continuous=Sort %s lancé avec succès pour %s secondes +commands.ebwizardry:cast.success_remote=Sort %s lancé avec succès en tant que %s +commands.ebwizardry:cast.success_remote_continuous=Sort %s lancé avec succès en tant que %s pour %s secondes +commands.ebwizardry:cast.success_position=Sort %s lancé avec succès à la position %s, %s, %s +commands.ebwizardry:cast.success_position_continuous=Sort %s lancé avec succès à la position %s, %s, %s pour %s secondes commands.ebwizardry:cast.fail=Impossible de lancer %1$s -commands.ebwizardry:cast.not_found=Il n'y a pas de sort avec le nom %1$s -commands.ebwizardry:cast.tag_error=L'analyse des balises de données a échoué: %s +commands.ebwizardry:cast.not_found=Il n'y a pas de sort avec l'ID %1$s +commands.ebwizardry:cast.invalid_direction=Direction invalide: %1$s (Les directions valides sont: up, down, north, south, east, west) +commands.ebwizardry:cast.tag_error=La lecture des données a échoué\: %s +commands.ebwizardry:cast.origin_not_specified=Vous devez spécifier un joueur ou une position pour lancer ce sort +commands.ebwizardry:cast.duration_not_specified=Vous devez spécifier une durée pour ce sort commands.ebwizardry:ally.usage=/%1$s [joueur] commands.ebwizardry:ally.addally=%1$s a été ajouté à la liste d'alliés de %2$s @@ -655,6 +1035,14 @@ commands.ebwizardry:discoverspell.removespell=Retrait de %1$s à la mémoire %2$ config.ebwizardry.title.general=Mod Options +config.ebwizardry.generic.true=Activé +config.ebwizardry.generic.false=Désactivé + +config.ebwizardry.category.spells=Configuration des sorts +config.ebwizardry.category.spells.tooltip=Sélectionner les sorts activés +config.ebwizardry.title.spells=Configuration des sorts +config.ebwizardry.subtitle.spells=Désactiver un sort de manière générale, ici, va écraser les spécification dans les fichiers JSON. + config.ebwizardry.category.gameplay=Paramètre de gameplay config.ebwizardry.category.gameplay.tooltip=Configuration général de wizardry config.ebwizardry.title.gameplay=Paramètre de gameplay From 11821ff14dd94a3de91de779ac541abca555b1a2 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Mon, 11 May 2020 18:38:18 +0100 Subject: [PATCH 097/415] Apparently we don't need this any more --- .../wizardry/item/ItemWizardArmour.java | 56 +------------------ 1 file changed, 2 insertions(+), 54 deletions(-) diff --git a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java index 9b5ff051..643bccb8 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java +++ b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java @@ -25,11 +25,9 @@ import net.minecraft.entity.ai.attributes.IAttributeInstance; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.inventory.Slot; -import net.minecraft.item.EnumAction; import net.minecraft.item.ItemArmor; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumHandSide; import net.minecraft.world.World; import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; import net.minecraftforge.fml.common.Mod; @@ -118,62 +116,12 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana @Override @SideOnly(Side.CLIENT) public net.minecraft.client.model.ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack, - EntityEquipmentSlot armourSlot, net.minecraft.client.model.ModelBiped _default){ + EntityEquipmentSlot armourSlot, net.minecraft.client.model.ModelBiped original){ // Legs use modelBiped if(armourSlot == EntityEquipmentSlot.LEGS && !entityLiving.isInvisible()) return null; - net.minecraft.client.model.ModelBiped model = Wizardry.proxy.getWizardArmourModel(); - - if(model != null){ - - model.bipedHead.showModel = armourSlot == EntityEquipmentSlot.HEAD; - model.bipedHeadwear.showModel = false; - model.bipedBody.showModel = armourSlot == EntityEquipmentSlot.CHEST; - model.bipedRightArm.showModel = armourSlot == EntityEquipmentSlot.CHEST; - model.bipedLeftArm.showModel = armourSlot == EntityEquipmentSlot.CHEST; - model.bipedRightLeg.showModel = armourSlot == EntityEquipmentSlot.FEET; - model.bipedLeftLeg.showModel = armourSlot == EntityEquipmentSlot.FEET; - - model.isSneak = entityLiving.isSneaking(); - model.isRiding = entityLiving.isRiding(); - model.isChild = entityLiving.isChild(); - - boolean leftHanded = entityLiving.getPrimaryHand() == EnumHandSide.LEFT; - - ItemStack itemstackR = leftHanded ? entityLiving.getHeldItemOffhand() : entityLiving.getHeldItemMainhand(); - ItemStack itemstackL = leftHanded ? entityLiving.getHeldItemMainhand() : entityLiving.getHeldItemOffhand(); - - if(!itemstackR.isEmpty()){ - model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.ITEM; - - if(entityLiving.getItemInUseCount() > 0){ - EnumAction enumaction = itemstackR.getItemUseAction(); - - if(enumaction == EnumAction.BLOCK){ - model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BLOCK; - }else if(enumaction == EnumAction.BOW){ - model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BOW_AND_ARROW; - } - } - } - - if(!itemstackL.isEmpty()){ - model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.ITEM; - - if(entityLiving.getItemInUseCount() > 0){ - EnumAction enumaction1 = itemstackL.getItemUseAction(); - - if(enumaction1 == EnumAction.BLOCK){ - model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BLOCK; - }else if(enumaction1 == EnumAction.BOW){ - model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BOW_AND_ARROW; - } - } - } - } - - return model; + return Wizardry.proxy.getWizardArmourModel(); } @Override From a97abd26d0f9945392537f5c56b350bde13bef8b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Mon, 11 May 2020 18:42:00 +0100 Subject: [PATCH 098/415] Add receptacles --- .../wizardry/block/BlockReceptacle.java | 211 ++++++++++++++ .../wizardry/client/model/WizardryModels.java | 9 +- .../wizardry/registry/WizardryBlocks.java | 7 +- .../wizardry/registry/WizardryItems.java | 2 + .../wizardry/registry/WizardrySounds.java | 2 + .../tileentity/TileEntityReceptacle.java | 61 ++++ .../ebwizardry/blockstates/receptacle.json | 9 + .../assets/ebwizardry/lang/en_gb.lang | 2 + .../assets/ebwizardry/lang/en_us.lang | 2 + .../ebwizardry/models/block/receptacle.json | 262 ++++++++++++++++++ .../models/block/wall_receptacle.json | 238 ++++++++++++++++ .../ebwizardry/models/item/receptacle.json | 3 + .../resources/assets/ebwizardry/sounds.json | 1 + .../ebwizardry/textures/blocks/receptacle.png | Bin 0 -> 6729 bytes .../textures/blocks/wall_receptacle.png | Bin 0 -> 6387 bytes 15 files changed, 803 insertions(+), 6 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/block/BlockReceptacle.java create mode 100644 src/main/java/electroblob/wizardry/tileentity/TileEntityReceptacle.java create mode 100644 src/main/resources/assets/ebwizardry/blockstates/receptacle.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/receptacle.json create mode 100644 src/main/resources/assets/ebwizardry/models/block/wall_receptacle.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/receptacle.json create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/receptacle.png create mode 100644 src/main/resources/assets/ebwizardry/textures/blocks/wall_receptacle.png diff --git a/src/main/java/electroblob/wizardry/block/BlockReceptacle.java b/src/main/java/electroblob/wizardry/block/BlockReceptacle.java new file mode 100644 index 00000000..72983785 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockReceptacle.java @@ -0,0 +1,211 @@ +package electroblob.wizardry.block; + +import com.google.common.collect.Maps; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.item.ItemSpectralDust; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.tileentity.TileEntityReceptacle; +import electroblob.wizardry.util.ParticleBuilder; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.block.BlockTorch; +import net.minecraft.block.ITileEntityProvider; +import net.minecraft.block.SoundType; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.NonNullList; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import java.util.Map; +import java.util.Random; + +public class BlockReceptacle extends BlockTorch implements ITileEntityProvider { + + protected static final AxisAlignedBB STANDING_AABB = new AxisAlignedBB(4/16d, 0/16d, 4/16d, 12/16d, 8/16d, 12/16d); + protected static final AxisAlignedBB NORTH_WALL_AABB = new AxisAlignedBB(4/16d, 2/16d, 7/16d, 12/16d, 10/16d, 16/16d); + protected static final AxisAlignedBB SOUTH_WALL_AABB = new AxisAlignedBB(4/16d, 2/16d, 0/16d, 12/16d, 10/16d, 9/16d); + protected static final AxisAlignedBB WEST_WALL_AABB = new AxisAlignedBB(7/16d, 2/16d, 4/16d, 16/16d, 10/16d, 12/16d); + protected static final AxisAlignedBB EAST_WALL_AABB = new AxisAlignedBB(0/16d, 2/16d, 4/16d, 9/16d, 10/16d, 12/16d); + + private static final double WALL_PARTICLE_OFFSET = 3/16d; + + private static final Map PARTICLE_COLOURS; + + static { + + Map map = Maps.newEnumMap(Element.class); + + map.put(Element.MAGIC, new int[]{0xe4c7cd, 0xfeffbe, 0x9d2cf3}); + map.put(Element.FIRE, new int[]{0xff9600, 0xfffe67, 0xd02700}); + map.put(Element.ICE, new int[]{0xa3e8f4, 0xe9f9fc, 0x138397}); + map.put(Element.LIGHTNING, new int[]{0x409ee1, 0xf5f0ff, 0x225474}); + map.put(Element.NECROMANCY, new int[]{0xa811ce, 0xf575f5, 0x382366}); + map.put(Element.EARTH, new int[]{0xa8f408, 0xc8ffb2, 0x795c28}); + map.put(Element.SORCERY, new int[]{0x56e8e3, 0xe8fcfc, 0x16a64d}); + map.put(Element.HEALING, new int[]{0xfff69e, 0xfffff6, 0xa18200}); + + PARTICLE_COLOURS = Maps.immutableEnumMap(map); + } + + public BlockReceptacle(){ + super(); + this.setCreativeTab(WizardryTabs.WIZARDRY); + this.setHardness(0); + this.setLightLevel(0.5f); + this.setSoundType(SoundType.STONE); + } + + @Override + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){ + switch(state.getValue(FACING)){ + case EAST: return EAST_WALL_AABB; + case WEST: return WEST_WALL_AABB; + case SOUTH: return SOUTH_WALL_AABB; + case NORTH: return NORTH_WALL_AABB; + default: return STANDING_AABB; + } + } + + @Override + public AxisAlignedBB getCollisionBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){ + return state.getBoundingBox(world, pos); + } + + @Override + public int getLightValue(IBlockState state, IBlockAccess world, BlockPos pos){ + + TileEntity tileEntity = world.getTileEntity(pos); + + if(tileEntity instanceof TileEntityReceptacle && ((TileEntityReceptacle)tileEntity).getElement() != null){ + return super.getLightValue(state, world, pos); // Return super to use float value from constructor + } + + return 0; + } + + @Override + public void getDrops(NonNullList drops, IBlockAccess world, BlockPos pos, IBlockState state, int fortune){ + + super.getDrops(drops, world, pos, state, fortune); + + TileEntity tileEntity = world.getTileEntity(pos); + + if(tileEntity instanceof TileEntityReceptacle){ + Element element = ((TileEntityReceptacle)tileEntity).getElement(); + if(element != null) drops.add(new ItemStack(WizardryItems.spectral_dust, 1, element.ordinal())); + } + } + + // See BlockFlowerPot for these two (this class is essentially based on a flower pot) + + @Override + public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest){ + if(willHarvest) return true; // If it will harvest, delay deletion of the block until after getDrops + return super.removedByPlayer(state, world, pos, player, willHarvest); + } + + @Override + public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, @Nullable TileEntity te, ItemStack tool){ + super.harvestBlock(world, player, pos, state, te, tool); + world.setBlockToAir(pos); + } + + @Override + public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ){ + + TileEntity tileEntity = world.getTileEntity(pos); + + ItemStack stack = player.getHeldItem(hand); + + if(tileEntity instanceof TileEntityReceptacle){ + + Element currentElement = ((TileEntityReceptacle)tileEntity).getElement(); + + if(currentElement == null){ + + if(stack.getItem() instanceof ItemSpectralDust && stack.getMetadata() >= 0 + && stack.getMetadata() < Element.values().length){ + + ((TileEntityReceptacle)tileEntity).setElement(Element.values()[stack.getMetadata()]); + if(!player.capabilities.isCreativeMode) stack.shrink(1); + world.playSound(pos.getX(), pos.getY(), pos.getZ(), WizardrySounds.BLOCK_RECEPTACLE_IGNITE, + SoundCategory.BLOCKS, 0.7f, 0.7f, false); + world.checkLight(pos); + return true; + } + + }else{ + + ((TileEntityReceptacle)tileEntity).setElement(null); + + ItemStack dust = new ItemStack(WizardryItems.spectral_dust, 1, currentElement.ordinal()); + + if(stack.isEmpty()){ + player.setHeldItem(hand, dust); + }else if(!player.addItemStackToInventory(dust)){ + player.dropItem(dust, false); + } + + world.checkLight(pos); + return true; + } + } + + return super.onBlockActivated(world, pos, state, player, hand, facing, hitX, hitY, hitZ); + } + + @Override + public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random rand){ + + TileEntity tileEntity = world.getTileEntity(pos); + + if(tileEntity instanceof TileEntityReceptacle){ + + Element element = ((TileEntityReceptacle)tileEntity).getElement(); + + if(element != null){ + + EnumFacing facing = state.getValue(FACING).getOpposite(); + + Vec3d centre = WizardryUtilities.getCentre(pos); + if(facing.getAxis().isHorizontal()){ + centre = centre.add(new Vec3d(facing.getDirectionVec()).scale(WALL_PARTICLE_OFFSET)).add(0, 0.125, 0); + } + + int[] colours = PARTICLE_COLOURS.get(element); + + ParticleBuilder.create(ParticleBuilder.Type.FLASH).pos(centre).scale(0.35f).time(48).clr(colours[0]).spawn(world); + + double r = 0.12; + + for(int i = 0; i < 3; i++){ + + double x = r * (rand.nextDouble() * 2 - 1); + double y = r * (rand.nextDouble() * 2 - 1); + double z = r * (rand.nextDouble() * 2 - 1); + + ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(centre.x + x, centre.y + y, centre.z + z) + .vel(x * -0.03, 0.02, z * -0.03).time(24 + rand.nextInt(8)).clr(colours[1]).fade(colours[2]).spawn(world); + } + } + } + } + + @Nullable + @Override + public TileEntity createNewTileEntity(World world, int meta){ + return new TileEntityReceptacle(); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java index 5dc88bc8..84a3aa9f 100644 --- a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java +++ b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java @@ -7,23 +7,20 @@ import electroblob.wizardry.block.BlockRunestone; import electroblob.wizardry.item.IMultiTexturedItem; import electroblob.wizardry.item.ItemBlockMultiTextured; import electroblob.wizardry.item.ItemCrystal; +import electroblob.wizardry.item.ItemSpectralDust; import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import net.minecraft.block.BlockPlanks; -import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.block.model.IBakedModel; import net.minecraft.client.renderer.block.model.ModelBakery; import net.minecraft.client.renderer.block.model.ModelResourceLocation; import net.minecraft.client.renderer.block.statemap.StateMap; -import net.minecraft.client.renderer.block.statemap.StateMapperBase; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.NonNullList; -import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.client.event.ModelRegistryEvent; -import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.fml.common.Mod; @@ -91,6 +88,8 @@ public final class WizardryModels { registerItemModel(Item.getItemFromBlock(WizardryBlocks.acacia_lectern)); registerItemModel(Item.getItemFromBlock(WizardryBlocks.dark_oak_lectern)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.receptacle)); + // Items registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal); @@ -179,6 +178,8 @@ public final class WizardryModels { registerItemModel(WizardryItems.magic_silk); + registerMultiTexturedModel((ItemSpectralDust)WizardryItems.spectral_dust); + registerItemModel(WizardryItems.wizard_hat); registerItemModel(WizardryItems.wizard_robe); registerItemModel(WizardryItems.wizard_leggings); diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java index 6bbccd46..b126b19c 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java @@ -4,8 +4,6 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.block.*; import electroblob.wizardry.tileentity.*; import net.minecraft.block.Block; -import net.minecraft.block.BlockPlanks; -import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; @@ -77,6 +75,8 @@ public final class WizardryBlocks { public static final Block acacia_lectern = placeholder(); public static final Block dark_oak_lectern = placeholder(); + public static final Block receptacle = placeholder(); + /** * Sets both the registry and unlocalised names of the given block, then registers it with the given registry. Use * this instead of {@link Block#setRegistryName(String)} and {@link Block#setTranslationKey(String)} during @@ -134,6 +134,8 @@ public final class WizardryBlocks { registerBlock(registry, "acacia_lectern", new BlockLectern()); registerBlock(registry, "dark_oak_lectern", new BlockLectern()); + registerBlock(registry, "receptacle", new BlockReceptacle()); + } /** Called from the preInit method in the main mod class to register all the tile entities. */ @@ -148,5 +150,6 @@ public final class WizardryBlocks { GameRegistry.registerTileEntity(TileEntityShrineCore.class, new ResourceLocation(Wizardry.MODID, "shrine_core")); GameRegistry.registerTileEntity(TileEntityBookshelf.class, new ResourceLocation(Wizardry.MODID, "bookshelf")); GameRegistry.registerTileEntity(TileEntityLectern.class, new ResourceLocation(Wizardry.MODID, "lectern")); + GameRegistry.registerTileEntity(TileEntityReceptacle.class, new ResourceLocation(Wizardry.MODID, "receptacle")); } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryItems.java b/src/main/java/electroblob/wizardry/registry/WizardryItems.java index bc998db7..a8e47661 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryItems.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryItems.java @@ -416,6 +416,8 @@ public final class WizardryItems { registerItemBlock(registry, WizardryBlocks.acacia_lectern); registerItemBlock(registry, WizardryBlocks.dark_oak_lectern); + registerItemBlock(registry, WizardryBlocks.receptacle); + // Items registerItem(registry, "magic_crystal", new ItemCrystal()); diff --git a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java index 95c01466..6b732c7b 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java +++ b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java @@ -29,6 +29,7 @@ public final class WizardrySounds { public static final SoundEvent BLOCK_PEDESTAL_ACTIVATE = createSound("block.pedestal.activate"); public static final SoundEvent BLOCK_PEDESTAL_CONQUER = createSound("block.pedestal.conquer"); public static final SoundEvent BLOCK_LECTERN_LOCATE_SPELL = createSound("block.lectern.locate_spell"); + public static final SoundEvent BLOCK_RECEPTACLE_IGNITE = createSound("block.receptacle.ignite"); public static final SoundEvent ITEM_WAND_SWITCH_SPELL = createSound("item.wand.switch_spell"); public static final SoundEvent ITEM_WAND_LEVELUP = createSound("item.wand.levelup"); @@ -156,6 +157,7 @@ public final class WizardrySounds { event.getRegistry().register(BLOCK_PEDESTAL_ACTIVATE); event.getRegistry().register(BLOCK_PEDESTAL_CONQUER); event.getRegistry().register(BLOCK_LECTERN_LOCATE_SPELL); + event.getRegistry().register(BLOCK_RECEPTACLE_IGNITE); event.getRegistry().register(ITEM_WAND_SWITCH_SPELL); event.getRegistry().register(ITEM_WAND_LEVELUP); diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityReceptacle.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityReceptacle.java new file mode 100644 index 00000000..064bfc9b --- /dev/null +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityReceptacle.java @@ -0,0 +1,61 @@ +package electroblob.wizardry.tileentity; + +import electroblob.wizardry.constants.Element; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.play.server.SPacketUpdateTileEntity; +import net.minecraft.tileentity.TileEntity; + +import javax.annotation.Nullable; + +public class TileEntityReceptacle extends TileEntity { + + private Element element; + + public TileEntityReceptacle(){ + this(null); + } + + public TileEntityReceptacle(Element element){ + this.element = element; + } + + public Element getElement(){ + return element; + } + + public void setElement(Element element){ + this.element = element; + } + + @Override + public NBTTagCompound writeToNBT(NBTTagCompound compound){ + super.writeToNBT(compound); + compound.setInteger("Element", element == null ? -1 : element.ordinal()); + return compound; + } + + @Override + public void readFromNBT(NBTTagCompound compound){ + super.readFromNBT(compound); + int i = compound.getInteger("Element"); + element = i == -1 ? null : Element.values()[i]; + } + + @Override + public NBTTagCompound getUpdateTag(){ + return this.writeToNBT(new NBTTagCompound()); + } + + @Nullable + @Override + public SPacketUpdateTileEntity getUpdatePacket(){ + return new SPacketUpdateTileEntity(pos, getBlockMetadata(), this.getUpdateTag()); + } + + @Override + public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt){ + readFromNBT(pkt.getNbtCompound()); + } + +} diff --git a/src/main/resources/assets/ebwizardry/blockstates/receptacle.json b/src/main/resources/assets/ebwizardry/blockstates/receptacle.json new file mode 100644 index 00000000..d272bf0a --- /dev/null +++ b/src/main/resources/assets/ebwizardry/blockstates/receptacle.json @@ -0,0 +1,9 @@ +{ + "variants": { + "facing=up": { "model": "ebwizardry:receptacle" }, + "facing=east": { "model": "ebwizardry:wall_receptacle", "y": 270 }, + "facing=south": { "model": "ebwizardry:wall_receptacle" }, + "facing=west": { "model": "ebwizardry:wall_receptacle", "y": 90 }, + "facing=north": { "model": "ebwizardry:wall_receptacle", "y": 180 } + } +} diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 1cca607e..cee7452f 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -47,6 +47,8 @@ tile.ebwizardry\:jungle_lectern.name=Jungle Lectern tile.ebwizardry\:acacia_lectern.name=Acacia Lectern tile.ebwizardry\:dark_oak_lectern.name=Dark Oak Lectern +tile.ebwizardry\:receptacle.name=Receptacle + item.ebwizardry\:crystal_magic.name=Magic Crystal item.ebwizardry\:crystal_magic.desc=The iridescent colours of this crystal seem to slowly shift and swirl, hinting at a magical energy within. Perhaps this magic could be harnessed in some way? item.ebwizardry\:crystal_fire.name=Fiery Crystal diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 66fafa64..f3e9bde0 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -47,6 +47,8 @@ tile.ebwizardry\:jungle_lectern.name=Jungle Lectern tile.ebwizardry\:acacia_lectern.name=Acacia Lectern tile.ebwizardry\:dark_oak_lectern.name=Dark Oak Lectern +tile.ebwizardry\:receptacle.name=Receptacle + item.ebwizardry\:crystal_magic.name=Magic Crystal item.ebwizardry\:crystal_magic.desc=The iridescent colors of this crystal seem to slowly shift and swirl, hinting at a magical energy within. Perhaps this magic could be harnessed, somehow... item.ebwizardry\:crystal_fire.name=Fiery Crystal diff --git a/src/main/resources/assets/ebwizardry/models/block/receptacle.json b/src/main/resources/assets/ebwizardry/models/block/receptacle.json new file mode 100644 index 00000000..b64a8d89 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/receptacle.json @@ -0,0 +1,262 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "0": "ebwizardry:blocks/receptacle", + "particle": "ebwizardry:blocks/receptacle" + }, + "elements": [ + { + "from": [6, 6.5, 10], + "to": [10, 7.5, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [10, 6.5, 12]}, + "faces": { + "north": {"uv": [1, 5, 5, 6], "texture": "#0"}, + "east": {"uv": [4, 5, 5, 6], "texture": "#0"}, + "south": {"uv": [1, 5, 5, 6], "texture": "#0"}, + "west": {"uv": [1, 5, 2, 6], "texture": "#0"}, + "up": {"uv": [1, 5, 5, 6], "texture": "#0"}, + "down": {"uv": [1, 5, 5, 6], "texture": "#0"} + } + }, + { + "from": [6, 5.5, 6], + "to": [10, 6.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 8]}, + "faces": { + "north": {"uv": [1, 7, 5, 8], "texture": "#0"}, + "east": {"uv": [1, 7, 5, 8], "texture": "#0"}, + "south": {"uv": [1, 7, 5, 8], "texture": "#0"}, + "west": {"uv": [1, 7, 5, 8], "texture": "#0"}, + "up": {"uv": [1, 1, 5, 5], "texture": "#0"}, + "down": {"uv": [1, 1, 5, 5], "texture": "#0"} + } + }, + { + "from": [5, 6.5, 6], + "to": [6, 7.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 8]}, + "faces": { + "north": {"uv": [0, 1, 1, 2], "texture": "#0"}, + "east": {"uv": [0, 1, 1, 5], "rotation": 90, "texture": "#0"}, + "south": {"uv": [0, 4, 1, 5], "texture": "#0"}, + "west": {"uv": [0, 1, 1, 5], "rotation": 90, "texture": "#0"}, + "up": {"uv": [0, 1, 1, 5], "texture": "#0"}, + "down": {"uv": [0, 1, 1, 5], "texture": "#0"} + } + }, + { + "from": [6, 6.5, 5], + "to": [10, 7.5, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [10, 6.5, 7]}, + "faces": { + "north": {"uv": [1, 0, 5, 1], "texture": "#0"}, + "east": {"uv": [4, 0, 5, 1], "texture": "#0"}, + "south": {"uv": [1, 0, 5, 1], "texture": "#0"}, + "west": {"uv": [1, 0, 2, 1], "texture": "#0"}, + "up": {"uv": [1, 0, 5, 1], "texture": "#0"}, + "down": {"uv": [1, 0, 5, 1], "texture": "#0"} + } + }, + { + "from": [10, 6.5, 6], + "to": [11, 7.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [13, 6.5, 8]}, + "faces": { + "north": {"uv": [5, 1, 6, 2], "texture": "#0"}, + "east": {"uv": [5, 1, 6, 5], "rotation": 90, "texture": "#0"}, + "south": {"uv": [5, 4, 6, 5], "texture": "#0"}, + "west": {"uv": [5, 1, 6, 5], "rotation": 90, "texture": "#0"}, + "up": {"uv": [5, 1, 6, 5], "texture": "#0"}, + "down": {"uv": [5, 1, 6, 5], "texture": "#0"} + } + }, + { + "from": [10, 2.5, 7.5], + "to": [11, 6.5, 8.5], + "rotation": {"angle": -45, "axis": "z", "origin": [11, 5, 8]}, + "faces": { + "north": {"uv": [15, 1, 16, 5], "texture": "#0"}, + "east": {"uv": [14, 1, 15, 5], "texture": "#0"}, + "south": {"uv": [13, 1, 14, 5], "texture": "#0"}, + "west": {"uv": [12, 1, 13, 5], "texture": "#0"}, + "down": {"uv": [13, 4, 14, 5], "texture": "#0"} + } + }, + { + "from": [11.06066, 6.06066, 7.5], + "to": [12.06066, 8.06066, 8.5], + "rotation": {"angle": 0, "axis": "y", "origin": [19.06066, 15.06066, 16]}, + "faces": { + "north": {"uv": [15, 0, 16, 2], "texture": "#0"}, + "east": {"uv": [14, 0, 15, 2], "texture": "#0"}, + "south": {"uv": [13, 0, 14, 2], "texture": "#0"}, + "west": {"uv": [12, 0, 13, 2], "texture": "#0"}, + "up": {"uv": [13, 0, 14, 1], "texture": "#0"} + } + }, + { + "from": [7.5, 2.5, 5], + "to": [8.5, 6.5, 6], + "rotation": {"angle": -45, "axis": "x", "origin": [8, 5, 5]}, + "faces": { + "north": {"uv": [14, 1, 15, 5], "texture": "#0"}, + "east": {"uv": [13, 1, 14, 5], "texture": "#0"}, + "south": {"uv": [12, 1, 13, 5], "texture": "#0"}, + "west": {"uv": [15, 1, 16, 5], "texture": "#0"}, + "down": {"uv": [13, 4, 14, 5], "rotation": 90, "texture": "#0"} + } + }, + { + "from": [7.5, 6.06066, 3.93934], + "to": [8.5, 8.06066, 4.93934], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 15.06066, -3.06066]}, + "faces": { + "north": {"uv": [14, 0, 15, 2], "texture": "#0"}, + "east": {"uv": [13, 0, 14, 2], "texture": "#0"}, + "south": {"uv": [12, 0, 13, 2], "texture": "#0"}, + "west": {"uv": [15, 0, 16, 2], "texture": "#0"}, + "up": {"uv": [13, 0, 14, 1], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [5, 2.5, 7.5], + "to": [6, 6.5, 8.5], + "rotation": {"angle": 45, "axis": "z", "origin": [5, 5, 8]}, + "faces": { + "north": {"uv": [13, 1, 14, 5], "texture": "#0"}, + "east": {"uv": [12, 1, 13, 5], "texture": "#0"}, + "south": {"uv": [15, 1, 16, 5], "texture": "#0"}, + "west": {"uv": [14, 1, 15, 5], "texture": "#0"}, + "down": {"uv": [13, 4, 14, 5], "rotation": 180, "texture": "#0"} + } + }, + { + "from": [3.93934, 6.06066, 7.5], + "to": [4.93934, 8.06066, 8.5], + "rotation": {"angle": 0, "axis": "y", "origin": [-3.06066, 15.06066, 0]}, + "faces": { + "north": {"uv": [13, 0, 14, 2], "texture": "#0"}, + "east": {"uv": [12, 0, 13, 2], "texture": "#0"}, + "south": {"uv": [15, 0, 16, 2], "texture": "#0"}, + "west": {"uv": [14, 0, 15, 2], "texture": "#0"}, + "up": {"uv": [13, 0, 14, 1], "rotation": 180, "texture": "#0"} + } + }, + { + "from": [7.5, 2.5, 10], + "to": [8.5, 6.5, 11], + "rotation": {"angle": 45, "axis": "x", "origin": [8, 5, 11]}, + "faces": { + "north": {"uv": [12, 1, 13, 5], "texture": "#0"}, + "east": {"uv": [15, 1, 16, 5], "texture": "#0"}, + "south": {"uv": [14, 1, 15, 5], "texture": "#0"}, + "west": {"uv": [13, 1, 14, 5], "texture": "#0"}, + "down": {"uv": [13, 4, 14, 5], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [7.5, 6.06066, 11.06066], + "to": [8.5, 8.06066, 12.06066], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 15.06066, 19.06066]}, + "faces": { + "north": {"uv": [12, 0, 13, 2], "texture": "#0"}, + "east": {"uv": [15, 0, 16, 2], "texture": "#0"}, + "south": {"uv": [14, 0, 15, 2], "texture": "#0"}, + "west": {"uv": [13, 0, 14, 2], "texture": "#0"}, + "up": {"uv": [13, 0, 14, 1], "rotation": 90, "texture": "#0"} + } + }, + { + "name": "connector", + "from": [6.5, 3, 6.5], + "to": [9.5, 4, 9.5], + "rotation": {"angle": 0, "axis": "y", "origin": [14, 11, 17]}, + "faces": { + "north": {"uv": [7, 3, 10, 4], "texture": "#0"}, + "east": {"uv": [7, 3, 10, 4], "texture": "#0"}, + "south": {"uv": [7, 3, 10, 4], "texture": "#0"}, + "west": {"uv": [7, 3, 10, 4], "texture": "#0"}, + "up": {"uv": [7, 0, 10, 3], "texture": "#0"}, + "down": {"uv": [7, 0, 10, 3], "texture": "#0"} + } + }, + { + "name": "pillar", + "from": [7, 1, 7], + "to": [9, 3, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [15, 9.5, 15]}, + "faces": { + "north": {"uv": [5, 9, 7, 11], "texture": "#0"}, + "east": {"uv": [5, 9, 7, 11], "texture": "#0"}, + "south": {"uv": [5, 9, 7, 11], "texture": "#0"}, + "west": {"uv": [5, 9, 7, 11], "texture": "#0"} + } + }, + { + "name": "base", + "from": [6, 0, 6], + "to": [10, 1, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [14, 8, 17]}, + "faces": { + "north": {"uv": [4, 11, 8, 12], "texture": "#0"}, + "east": {"uv": [4, 11, 8, 12], "texture": "#0"}, + "south": {"uv": [4, 11, 8, 12], "texture": "#0"}, + "west": {"uv": [4, 11, 8, 12], "texture": "#0"}, + "up": {"uv": [0, 8, 4, 12], "texture": "#0"}, + "down": {"uv": [0, 12, 4, 16], "texture": "#0", "cullface": "down"} + } + } + ], + "display": { + "thirdperson_righthand": { + "translation": [0.25, 3.5, 0] + }, + "thirdperson_lefthand": { + "translation": [0.25, 3.5, 0] + }, + "firstperson_righthand": { + "translation": [0.25, 3.5, 0] + }, + "firstperson_lefthand": { + "translation": [0.25, 3.5, 0] + }, + "ground": { + "translation": [0, 3, 0], + "scale": [0.75, 0.75, 0.75] + }, + "gui": { + "rotation": [30, 225, 0], + "translation": [0, 3, 0] + }, + "fixed": { + "translation": [0.25, 3.5, 0] + } + }, + "groups": [ + { + "name": "dish", + "origin": [13, 8, 8], + "children": [0, 1, 2, 3, 4] + }, + { + "name": "arm", + "origin": [19.06066, 16.56066, 16], + "children": [5, 6] + }, + { + "name": "arm", + "origin": [19.06066, 16.56066, 16], + "children": [7, 8] + }, + { + "name": "arm", + "origin": [19.06066, 16.56066, 16], + "children": [9, 10] + }, + { + "name": "arm", + "origin": [19.06066, 16.56066, 16], + "children": [11, 12] + }, 13, 14, 15] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/block/wall_receptacle.json b/src/main/resources/assets/ebwizardry/models/block/wall_receptacle.json new file mode 100644 index 00000000..d378d412 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/block/wall_receptacle.json @@ -0,0 +1,238 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "ebwizardry:blocks/wall_receptacle", + "main": "ebwizardry:blocks/wall_receptacle" + }, + "elements": [ + { + "from": [6, 8.5, 7], + "to": [10, 9.5, 8], + "rotation": {"angle": 0, "axis": "y", "origin": [10, 8.5, 9]}, + "faces": { + "north": {"uv": [1, 5, 5, 6], "texture": "#main"}, + "east": {"uv": [4, 5, 5, 6], "texture": "#main"}, + "south": {"uv": [1, 5, 5, 6], "texture": "#main"}, + "west": {"uv": [1, 5, 2, 6], "texture": "#main"}, + "up": {"uv": [1, 5, 5, 6], "texture": "#main"}, + "down": {"uv": [1, 5, 5, 6], "texture": "#main"} + } + }, + { + "from": [6, 7.5, 3], + "to": [10, 8.5, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.5, 5]}, + "faces": { + "north": {"uv": [1, 7, 5, 8], "texture": "#main"}, + "east": {"uv": [1, 7, 5, 8], "texture": "#main"}, + "south": {"uv": [1, 7, 5, 8], "texture": "#main"}, + "west": {"uv": [1, 7, 5, 8], "texture": "#main"}, + "up": {"uv": [1, 1, 5, 5], "texture": "#main"}, + "down": {"uv": [1, 1, 5, 5], "texture": "#main"} + } + }, + { + "from": [5, 8.5, 3], + "to": [6, 9.5, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.5, 5]}, + "faces": { + "north": {"uv": [0, 1, 1, 2], "texture": "#main"}, + "east": {"uv": [0, 1, 1, 5], "rotation": 90, "texture": "#main"}, + "south": {"uv": [0, 4, 1, 5], "texture": "#main"}, + "west": {"uv": [0, 1, 1, 5], "rotation": 90, "texture": "#main"}, + "up": {"uv": [0, 1, 1, 5], "texture": "#main"}, + "down": {"uv": [0, 1, 1, 5], "texture": "#main"} + } + }, + { + "from": [6, 8.5, 2], + "to": [10, 9.5, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [10, 8.5, 4]}, + "faces": { + "north": {"uv": [1, 0, 5, 1], "texture": "#main"}, + "east": {"uv": [4, 0, 5, 1], "texture": "#main"}, + "south": {"uv": [1, 0, 5, 1], "texture": "#main"}, + "west": {"uv": [1, 0, 2, 1], "texture": "#main"}, + "up": {"uv": [1, 0, 5, 1], "texture": "#main"}, + "down": {"uv": [1, 0, 5, 1], "texture": "#main"} + } + }, + { + "from": [10, 8.5, 3], + "to": [11, 9.5, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [13, 8.5, 5]}, + "faces": { + "north": {"uv": [5, 1, 6, 2], "texture": "#main"}, + "east": {"uv": [5, 1, 6, 5], "rotation": 90, "texture": "#main"}, + "south": {"uv": [5, 4, 6, 5], "texture": "#main"}, + "west": {"uv": [5, 1, 6, 5], "rotation": 90, "texture": "#main"}, + "up": {"uv": [5, 1, 6, 5], "texture": "#main"}, + "down": {"uv": [5, 1, 6, 5], "texture": "#main"} + } + }, + { + "from": [10, 4.5, 4.5], + "to": [11, 8.5, 5.5], + "rotation": {"angle": -45, "axis": "z", "origin": [11, 7, 5]}, + "faces": { + "north": {"uv": [15, 1, 16, 5], "texture": "#main"}, + "east": {"uv": [14, 1, 15, 5], "texture": "#main"}, + "south": {"uv": [13, 1, 14, 5], "texture": "#main"}, + "west": {"uv": [12, 1, 13, 5], "texture": "#main"}, + "down": {"uv": [13, 4, 14, 5], "texture": "#main"} + } + }, + { + "from": [11.06066, 8.06066, 4.5], + "to": [12.06066, 10.06066, 5.5], + "rotation": {"angle": 0, "axis": "y", "origin": [19.06066, 17.06066, 13]}, + "faces": { + "north": {"uv": [15, 0, 16, 2], "texture": "#main"}, + "east": {"uv": [14, 0, 15, 2], "texture": "#main"}, + "south": {"uv": [13, 0, 14, 2], "texture": "#main"}, + "west": {"uv": [12, 0, 13, 2], "texture": "#main"}, + "up": {"uv": [13, 0, 14, 1], "texture": "#main"} + } + }, + { + "from": [7.5, 4.5, 2], + "to": [8.5, 8.5, 3], + "rotation": {"angle": -45, "axis": "x", "origin": [8, 7, 2]}, + "faces": { + "north": {"uv": [14, 1, 15, 5], "texture": "#main"}, + "east": {"uv": [13, 1, 14, 5], "texture": "#main"}, + "south": {"uv": [12, 1, 13, 5], "texture": "#main"}, + "west": {"uv": [15, 1, 16, 5], "texture": "#main"}, + "down": {"uv": [13, 4, 14, 5], "rotation": 90, "texture": "#main"} + } + }, + { + "from": [7.5, 8.06066, 0.93934], + "to": [8.5, 10.06066, 1.93934], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 17.06066, -6.06066]}, + "faces": { + "north": {"uv": [14, 0, 15, 2], "texture": "#main"}, + "east": {"uv": [13, 0, 14, 2], "texture": "#main"}, + "south": {"uv": [12, 0, 13, 2], "texture": "#main"}, + "west": {"uv": [15, 0, 16, 2], "texture": "#main"}, + "up": {"uv": [13, 0, 14, 1], "rotation": 270, "texture": "#main"} + } + }, + { + "from": [5, 4.5, 4.5], + "to": [6, 8.5, 5.5], + "rotation": {"angle": 45, "axis": "z", "origin": [5, 7, 5]}, + "faces": { + "north": {"uv": [13, 1, 14, 5], "texture": "#main"}, + "east": {"uv": [12, 1, 13, 5], "texture": "#main"}, + "south": {"uv": [15, 1, 16, 5], "texture": "#main"}, + "west": {"uv": [14, 1, 15, 5], "texture": "#main"}, + "down": {"uv": [13, 4, 14, 5], "rotation": 180, "texture": "#main"} + } + }, + { + "from": [3.93934, 8.06066, 4.5], + "to": [4.93934, 10.06066, 5.5], + "rotation": {"angle": 0, "axis": "y", "origin": [-3.06066, 17.06066, -3]}, + "faces": { + "north": {"uv": [13, 0, 14, 2], "texture": "#main"}, + "east": {"uv": [12, 0, 13, 2], "texture": "#main"}, + "south": {"uv": [15, 0, 16, 2], "texture": "#main"}, + "west": {"uv": [14, 0, 15, 2], "texture": "#main"}, + "up": {"uv": [13, 0, 14, 1], "rotation": 180, "texture": "#main"} + } + }, + { + "from": [7.5, 4.5, 7], + "to": [8.5, 8.5, 8], + "rotation": {"angle": 45, "axis": "x", "origin": [8, 7, 8]}, + "faces": { + "north": {"uv": [12, 1, 13, 5], "texture": "#main"}, + "east": {"uv": [15, 1, 16, 5], "texture": "#main"}, + "south": {"uv": [14, 1, 15, 5], "texture": "#main"}, + "west": {"uv": [13, 1, 14, 5], "texture": "#main"}, + "down": {"uv": [13, 4, 14, 5], "rotation": 270, "texture": "#main"} + } + }, + { + "from": [7.5, 8.06066, 8.06066], + "to": [8.5, 10.06066, 9.06066], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 17.06066, 16.06066]}, + "faces": { + "north": {"uv": [12, 0, 13, 2], "texture": "#main"}, + "east": {"uv": [15, 0, 16, 2], "texture": "#main"}, + "south": {"uv": [14, 0, 15, 2], "texture": "#main"}, + "west": {"uv": [13, 0, 14, 2], "texture": "#main"}, + "up": {"uv": [13, 0, 14, 1], "rotation": 90, "texture": "#main"} + } + }, + { + "name": "connector", + "from": [6.5, 5, 3.5], + "to": [9.5, 6, 6.5], + "rotation": {"angle": 0, "axis": "y", "origin": [14, 13, 14]}, + "faces": { + "north": {"uv": [7, 3, 10, 4], "texture": "#main"}, + "east": {"uv": [7, 3, 10, 4], "texture": "#main"}, + "south": {"uv": [7, 3, 10, 4], "texture": "#main"}, + "west": {"uv": [7, 3, 10, 4], "texture": "#main"}, + "up": {"uv": [7, 0, 10, 3], "texture": "#main"}, + "down": {"uv": [7, 0, 10, 3], "texture": "#main"} + } + }, + { + "name": "pillar", + "from": [7, 3, 1], + "to": [9, 5, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [15, 11.5, 15]}, + "faces": { + "east": {"uv": [10, 10, 5, 12], "texture": "#main"}, + "south": {"uv": [10, 10, 12, 12], "texture": "#main"}, + "west": {"uv": [5, 10, 10, 12], "texture": "#main"}, + "up": {"uv": [10, 10, 5, 12], "rotation": 270, "texture": "#main"}, + "down": {"uv": [5, 10, 10, 12], "rotation": 270, "texture": "#main"} + } + }, + { + "name": "base", + "from": [6, 2, 0], + "to": [10, 6, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 4.5, 1]}, + "faces": { + "north": {"uv": [0, 9, 4, 13], "texture": "#main", "cullface": "north"}, + "east": {"uv": [4, 9, 5, 13], "texture": "#main"}, + "south": {"uv": [0, 9, 4, 13], "texture": "#main"}, + "west": {"uv": [4, 9, 5, 13], "texture": "#main"}, + "up": {"uv": [0, 8, 4, 9], "texture": "#main"}, + "down": {"uv": [0, 13, 4, 14], "texture": "#main"} + } + } + ], + "groups": [ + { + "name": "dish", + "origin": [13, 8, 8], + "children": [0, 1, 2, 3, 4] + }, + { + "name": "arm", + "origin": [19.06066, 16.56066, 16], + "children": [5, 6] + }, + { + "name": "arm", + "origin": [19.06066, 16.56066, 16], + "children": [7, 8] + }, + { + "name": "arm", + "origin": [19.06066, 16.56066, 16], + "children": [9, 10] + }, + { + "name": "arm", + "origin": [19.06066, 16.56066, 16], + "children": [11, 12] + }, 13, 14, 15] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/item/receptacle.json b/src/main/resources/assets/ebwizardry/models/item/receptacle.json new file mode 100644 index 00000000..6d9b51f0 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/receptacle.json @@ -0,0 +1,3 @@ +{ + "parent": "ebwizardry:block/receptacle" +} diff --git a/src/main/resources/assets/ebwizardry/sounds.json b/src/main/resources/assets/ebwizardry/sounds.json index 975ef6c1..4ff79637 100644 --- a/src/main/resources/assets/ebwizardry/sounds.json +++ b/src/main/resources/assets/ebwizardry/sounds.json @@ -3,6 +3,7 @@ "block.pedestal.activate": {"category": "blocks", "sounds": ["mob/evocation_illager/prepare_summon"]}, "block.pedestal.conquer": {"category": "blocks", "sounds": ["ui/toast/challenge_complete"]}, "block.lectern.locate_spell": {"category": "blocks", "sounds": ["ebwizardry:conjure"]}, + "block.receptacle.ignite": {"category": "blocks", "sounds": ["mob/evocation_illager/cast1", "mob/evocation_illager/cast2"]}, "item.wand.switch_spell": {"category": "player", "sounds": ["ebwizardry:select"]}, "item.wand.levelup": {"category": "player", "sounds": ["random/levelup"]}, diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/receptacle.png b/src/main/resources/assets/ebwizardry/textures/blocks/receptacle.png new file mode 100644 index 0000000000000000000000000000000000000000..300eadb5108039a4daf9818494393ec5cc928a34 GIT binary patch literal 6729 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 a^$ef&Sv!=q1NMSO);wMPT-G@yGywpc?D^dQ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ebwizardry/textures/blocks/wall_receptacle.png b/src/main/resources/assets/ebwizardry/textures/blocks/wall_receptacle.png new file mode 100644 index 0000000000000000000000000000000000000000..bbae2f2d9a9947bf09550612a7e20e8d50eeb8bf GIT binary patch literal 6387 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�ZlE!HA3|+ zeW`Dwe-=H`NH8x?ubg#P@@&oLvo*>Le*OHbQ|;Ilddw?7{8(D?_l=zWrA@y~cQ3G) z5{ijpTu?T-YgXsw$A_~%JrrIg&bUB)+TXpgW#8<&`}6mQOB*dRn6^QO#ld9XMTQqT zpDkC38%{7}mRwvV*ymesvczE8zl;~$MY+=X2c%9fW7{PtZIpF{J6Sqk;MnXJN5khY z5T9n?XRy*ujbmbF&a#P}ObI{FnmHWVrOISeX%Ut9tl^(&K*_7445w?(oxJF{HP2*n zmd~oc@eFfhWJ6}2{LA5ge~&Q3dHK5^Pp&JL^-$yb`0D82SI=*ntC=YS z&#_Ng3;+N9e3ECT2LFadhvp}&W>S9Dy3S&@RLUZWo~7TJugf0zzdP{iwOH2ZDR(D5 zxYU)CbD?aN#D?dsLi{X6Zmj{IlRxbSO|&1JETpwT2x LS3j3^P6 Date: Tue, 12 May 2020 23:09:48 +0100 Subject: [PATCH 099/415] Remove a bunch of deprecated methods --- .../wizardry/registry/WizardryLoot.java | 33 -------- .../electroblob/wizardry/spell/Spell.java | 24 +----- .../electroblob/wizardry/util/NBTExtras.java | 23 ------ .../wizardry/util/WizardryUtilities.java | 75 ------------------- 4 files changed, 1 insertion(+), 154 deletions(-) diff --git a/src/main/java/electroblob/wizardry/registry/WizardryLoot.java b/src/main/java/electroblob/wizardry/registry/WizardryLoot.java index bf2584c7..3239ec2c 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryLoot.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryLoot.java @@ -75,39 +75,6 @@ public final class WizardryLoot { } - /** - * Helper method which gets a spell id according to the standard weighting. The tier is a weighted random value; the - * actual spell within that tier is completely random. Will not return the id of a spell which has been disabled in - * the config. This is for simple stuff like chests and drops; more complex generators like wizard trades don't use - * this method. - *

- * For reference, the standard weighting is as follows: Novice: 60%, Apprentice: 25%, Advanced: 10%, Master: 5% - * - * @param random An instance of {@link Random} to use for RNG - * @param filter A {@link Predicate} specifying any requirements the chosen spell must fulfil - * @return A random spell id number, or -1 if no spell exists that satisfies the given filter - * @deprecated Everything uses loot tables now, I may remove this as some point - */ - @Deprecated - public static int getStandardWeightedRandomSpellId(Random random, Predicate filter){ - - Tier tier = Tier.getWeightedRandomTier(random); - - List spells = Spell.getSpells(new Spell.TierElementFilter(tier, null)); - spells.removeIf(filter.negate()); - - // Ensures the tier chosen actually has spells in it, and if not uses NOVICE instead. - if(spells.isEmpty()){ - spells = Spell.getSpells(new Spell.TierElementFilter(Tier.NOVICE, null)); - spells.removeIf(filter.negate()); - } - - if(spells.isEmpty()) return -1; - - // Finds a random spell in the list and returns its id. - return spells.get(random.nextInt(spells.size())).metadata(); - } - @SubscribeEvent public static void onLootTableLoadEvent(LootTableLoadEvent event){ // General dungeon loot diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java index 6845f152..d706c7da 100644 --- a/src/main/java/electroblob/wizardry/spell/Spell.java +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -211,7 +211,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C this.sounds = createSounds(); this.id = nextSpellId++; this.items(WizardryItems.spell_book, WizardryItems.scroll); - this.npcSelector((e, o) -> canBeCastByNPCs()); // Fallback to old behaviour until we remove it entirely + this.npcSelector((e, o) -> false); } // ========================================= Initialisation methods =========================================== @@ -488,17 +488,6 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C return npcSelector.test(npc, override); } - /** - * Whether NPCs such as wizards can cast this spell. If you have overridden - * {@link Spell#cast(World, EntityLiving, EnumHand, int, EntityLivingBase, SpellModifiers)}, you should override - * this to return true. - * @deprecated Use the entity-sensitive version {@link Spell#canBeCastBy(EntityLiving, boolean)}. - */ - @Deprecated - public boolean canBeCastByNPCs(){ - return false; - } - /** * Whether the given dispenser can cast this spell. If you have overridden * {@link Spell#cast(World, double, double, double, EnumFacing, int, int, SpellModifiers)}, you should override this @@ -506,17 +495,6 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C * @param dispenser The dispenser to query. */ public boolean canBeCastBy(TileEntityDispenser dispenser){ - return canBeCastByDispensers(); - } - - /** - * Whether dispensers can cast this spell. If you have overridden - * {@link Spell#cast(World, double, double, double, EnumFacing, int, int, SpellModifiers)}, you should override this - * to return true. - * @deprecated Use the tileentity-sensitive version {@link Spell#canBeCastBy(TileEntityDispenser)}. - */ - @Deprecated - public boolean canBeCastByDispensers(){ return false; } diff --git a/src/main/java/electroblob/wizardry/util/NBTExtras.java b/src/main/java/electroblob/wizardry/util/NBTExtras.java index eac5464d..8baeb305 100644 --- a/src/main/java/electroblob/wizardry/util/NBTExtras.java +++ b/src/main/java/electroblob/wizardry/util/NBTExtras.java @@ -250,27 +250,4 @@ public final class NBTExtras { return false; } - /** - * Returns an NBTTagCompound which contains only the given UUID, stored using - * {@link NBTTagCompound#setUniqueId(String, UUID)}. Allows for neater storage to NBTTagLists. - * @deprecated Use {@link net.minecraft.nbt.NBTUtil#createUUIDTag(UUID)}. Note that this will break backwards - * compatibility because it uses "M" and "L" instead of "uuidMost" and "uuidLeast". - */ - @Deprecated - public static NBTTagCompound UUIDtoTagCompound(UUID id){ - NBTTagCompound tag = new NBTTagCompound(); - tag.setUniqueId("uuid", id); - return tag; - } - - /** - * Wrapper for {@link NBTTagCompound#getUniqueId(String)} which converts an NBTTagCompound directly to a UUID. - * Intended to be used as the inverse of {@link NBTExtras#UUIDtoTagCompound(UUID)}. - * @deprecated Use {@link net.minecraft.nbt.NBTUtil#getUUIDFromTag(NBTTagCompound)}. Note that this will break - * backwards compatibility because it uses "M" and "L" instead of "uuidMost" and "uuidLeast". - */ - @Deprecated - public static UUID tagCompoundToUUID(NBTTagCompound tag){ - return tag.getUniqueId("uuid"); - } } diff --git a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java index 4d899a5f..61f56148 100644 --- a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java +++ b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java @@ -343,62 +343,6 @@ public final class WizardryUtilities { return getNearestSurface(world, pos, EnumFacing.UP, range, true, SurfaceCriteria.COLLIDABLE); } - /** - * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. - * As of Wizardry 4.2, this is now a wrapper for {@link WizardryUtilities#getNearestFloor(World, BlockPos, int)} - * which retains the old functionality (i.e. returning an {@code int}, with -1 as 'not found') for compatibility. - * - * @param world The world to search in - * @param pos The coordinates to search from - * @param range The maximum distance from the given y coordinate to search. - * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the - * floor as would be seen in the debug screen when the player is standing on it. - * @deprecated Use {@link WizardryUtilities#getNearestFloor(World, BlockPos, int)}; this method may be removed in - * future. - */ - // Since this is always a y-coordinate, the 'not found' value can just be any negative number. - @Deprecated - public static int getNearestFloorLevel(World world, BlockPos pos, int range){ - Integer floor = getNearestFloor(world, pos, range); - return floor == null ? -1 : floor; - } - - /** - * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. Only - * works if the block above the floor is actually air and the floor is solid or a liquid. - * - * @param world The world to search in - * @param pos The coordinates to search from - * @param range The maximum distance from the given y coordinate to search. - * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the - * floor as would be seen in the debug screen when the player is standing on it. - * @deprecated Use {@link WizardryUtilities#getNearestSurface(World, BlockPos, EnumFacing, int, boolean, SurfaceCriteria)}; - * this method may be removed in future. - */ - @Deprecated - public static int getNearestFloorLevelB(World world, BlockPos pos, int range){ - Integer floor = getNearestSurface(world, pos, EnumFacing.UP, range, true, SurfaceCriteria.SOLID_LIQUID_TO_AIR); - return floor == null ? -1 : floor; - } - - /** - * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. - * Everything that is not air is treated as floor, even stuff that can't be walked on. - * - * @param world The world to search in - * @param pos The coordinates to search from - * @param range The maximum distance from the given y coordinate to search. - * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the - * floor as would be seen in the debug screen when the player is standing on it. - * @deprecated Use {@link WizardryUtilities#getNearestSurface(World, BlockPos, EnumFacing, int, boolean, SurfaceCriteria)}; - * this method may be removed in future. - */ - @Deprecated - public static int getNearestFloorLevelC(World world, BlockPos pos, int range){ - Integer floor = getNearestSurface(world, pos, EnumFacing.UP, range, true, SurfaceCriteria.NOT_AIR_TO_AIR); - return floor == null ? -1 : floor; - } - /** * Gets a random position on the ground near the given entity within the specified horizontal and vertical ranges. * Used to find a position to spawn entities in summoning spells. @@ -984,25 +928,6 @@ public final class WizardryUtilities { // Miscellaneous // =============================================================================================================== - /** - * Verifies that the given string is a valid string representation of a UUID. More specifically, returns true if and - * only if the given string is not null and matches the regular expression: - *

- *
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/

- * which is the regex equivalent of the standard string representation of a UUID as described in - * {@link UUID#toString()}. This method is intended to be used as a check to prevent an - * {@link IllegalArgumentException} from occurring when calling {@link UUID#fromString(String)}. - * - * @param string The string to be checked - * @return Whether the given string is a valid string representation of a UUID - * @deprecated UUIDs can now be stored in NBT directly; use that in preference to storing them as strings. - */ - @Deprecated - public static boolean verifyUUIDString(String string){ - return string != null - && string.matches("/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/"); - } - /** * Flattens the given nested collection. The returned collection is an unmodifiable collection of all the elements * contained within all of the sub-collections of the given nested collection. From 25475632d9a410dfb6f8cb318f5aa244b1e3aef3 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 12 May 2020 23:28:05 +0100 Subject: [PATCH 100/415] Require line-of-sight for summoning spells --- .../electroblob/wizardry/spell/Decay.java | 2 +- .../wizardry/util/WizardryUtilities.java | 46 +++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/main/java/electroblob/wizardry/spell/Decay.java b/src/main/java/electroblob/wizardry/spell/Decay.java index afca662c..119b7b79 100644 --- a/src/main/java/electroblob/wizardry/spell/Decay.java +++ b/src/main/java/electroblob/wizardry/spell/Decay.java @@ -37,7 +37,7 @@ public class Decay extends SpellConstructRanged { int verticalRange = (int)(6 * modifiers.get(WizardryItems.blast_upgrade)); for(int i=0; iThe number of operations performed by this method is proportional to the square of this + * parameter, so for performance reasons it is recommended that it does not exceed around 10.
+ * @param verticalRange The maximum number of blocks on the y axis the returned position can be from the given + * position. + * @param lineOfSight Whether to require line-of-sight from the origin to the returned position. + * @return A BlockPos with the coordinates of the block directly above the ground at the position found, or null if + * none were found within range. Importantly, since this method checks all possible positions within + * range (i.e. randomness only occurs when deciding between the possible positions), if it returns null once + * then it will always return null given the same circumstances and parameters. What this means is that you + * can (and should) immediately stop trying to cast a summoning spell if this returns null. + */ + @Nullable + public static BlockPos findNearbyFloorSpace(World world, BlockPos origin, int horizontalRange, int verticalRange, boolean lineOfSight){ List possibleLocations = new ArrayList(); + final Vec3d centre = WizardryUtilities.getCentre(origin); + for(int x = -horizontalRange; x <= horizontalRange; x++){ for(int z = -horizontalRange; z <= horizontalRange; z++){ + Integer y = WizardryUtilities.getNearestFloor(world, origin.add(x, 0, z), verticalRange); - if(y != null) possibleLocations.add(new BlockPos(origin.getX() + x, y, origin.getZ() + z)); + + if(y != null){ + + BlockPos location = new BlockPos(origin.getX() + x, y, origin.getZ() + z); + + if(lineOfSight){ + // Since we're only using finding collidable surfaces, it doesn't make much sense to include + // non-collidable blocks here! + RayTraceResult rayTrace = world.rayTraceBlocks(centre, WizardryUtilities.getCentre(location), + false, true, false); + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK) continue; + } + + possibleLocations.add(location); + } } } From 34a4609af2032a9a6982fdbac982bf1575b756b2 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 12 May 2020 23:46:05 +0100 Subject: [PATCH 101/415] Tweak receptacle model --- .../assets/ebwizardry/models/block/receptacle.json | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/resources/assets/ebwizardry/models/block/receptacle.json b/src/main/resources/assets/ebwizardry/models/block/receptacle.json index b64a8d89..5c3cd492 100644 --- a/src/main/resources/assets/ebwizardry/models/block/receptacle.json +++ b/src/main/resources/assets/ebwizardry/models/block/receptacle.json @@ -210,16 +210,22 @@ ], "display": { "thirdperson_righthand": { - "translation": [0.25, 3.5, 0] + "translation": [-0.25, 3.5, 0], + "scale": [0.75, 0.75, 0.75] }, "thirdperson_lefthand": { - "translation": [0.25, 3.5, 0] + "translation": [-0.25, 3.5, 0], + "scale": [0.75, 0.75, 0.75] }, "firstperson_righthand": { - "translation": [0.25, 3.5, 0] + "rotation": [0, 45, 0], + "translation": [1, 3, 0], + "scale": [0.5, 0.5, 0.5] }, "firstperson_lefthand": { - "translation": [0.25, 3.5, 0] + "rotation": [0, 45, 0], + "translation": [1, 3, 0], + "scale": [0.5, 0.5, 0.5] }, "ground": { "translation": [0, 3, 0], From ff4c17d1c1f66579ae12830d7eddf2a0cefb18d5 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 12 May 2020 23:49:18 +0100 Subject: [PATCH 102/415] Add crafting recipe for receptacle --- .../assets/ebwizardry/recipes/receptacle.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/resources/assets/ebwizardry/recipes/receptacle.json diff --git a/src/main/resources/assets/ebwizardry/recipes/receptacle.json b/src/main/resources/assets/ebwizardry/recipes/receptacle.json new file mode 100644 index 00000000..21743a82 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/recipes/receptacle.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "gpg", + " g " + ], + "key": { + "g": { + "type": "forge:ore_dict", + "ore": "ingotGold" + }, + "p": { + "item": "minecraft:flower_pot" + } + }, + "result": { + "item": "ebwizardry:receptacle" + } +} \ No newline at end of file From 9205f082f10e787ddf4b669d5cf6827f9f4f07d9 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 12 May 2020 23:56:36 +0100 Subject: [PATCH 103/415] Rename texture variable to something more sensible --- .../ebwizardry/models/block/receptacle.json | 174 +++++++++--------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/main/resources/assets/ebwizardry/models/block/receptacle.json b/src/main/resources/assets/ebwizardry/models/block/receptacle.json index 5c3cd492..407c7cc1 100644 --- a/src/main/resources/assets/ebwizardry/models/block/receptacle.json +++ b/src/main/resources/assets/ebwizardry/models/block/receptacle.json @@ -2,7 +2,7 @@ "credit": "Made with Blockbench", "parent": "block/block", "textures": { - "0": "ebwizardry:blocks/receptacle", + "main": "ebwizardry:blocks/receptacle", "particle": "ebwizardry:blocks/receptacle" }, "elements": [ @@ -11,12 +11,12 @@ "to": [10, 7.5, 11], "rotation": {"angle": 0, "axis": "y", "origin": [10, 6.5, 12]}, "faces": { - "north": {"uv": [1, 5, 5, 6], "texture": "#0"}, - "east": {"uv": [4, 5, 5, 6], "texture": "#0"}, - "south": {"uv": [1, 5, 5, 6], "texture": "#0"}, - "west": {"uv": [1, 5, 2, 6], "texture": "#0"}, - "up": {"uv": [1, 5, 5, 6], "texture": "#0"}, - "down": {"uv": [1, 5, 5, 6], "texture": "#0"} + "north": {"uv": [1, 5, 5, 6], "texture": "#main"}, + "east": {"uv": [4, 5, 5, 6], "texture": "#main"}, + "south": {"uv": [1, 5, 5, 6], "texture": "#main"}, + "west": {"uv": [1, 5, 2, 6], "texture": "#main"}, + "up": {"uv": [1, 5, 5, 6], "texture": "#main"}, + "down": {"uv": [1, 5, 5, 6], "texture": "#main"} } }, { @@ -24,12 +24,12 @@ "to": [10, 6.5, 10], "rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 8]}, "faces": { - "north": {"uv": [1, 7, 5, 8], "texture": "#0"}, - "east": {"uv": [1, 7, 5, 8], "texture": "#0"}, - "south": {"uv": [1, 7, 5, 8], "texture": "#0"}, - "west": {"uv": [1, 7, 5, 8], "texture": "#0"}, - "up": {"uv": [1, 1, 5, 5], "texture": "#0"}, - "down": {"uv": [1, 1, 5, 5], "texture": "#0"} + "north": {"uv": [1, 7, 5, 8], "texture": "#main"}, + "east": {"uv": [1, 7, 5, 8], "texture": "#main"}, + "south": {"uv": [1, 7, 5, 8], "texture": "#main"}, + "west": {"uv": [1, 7, 5, 8], "texture": "#main"}, + "up": {"uv": [1, 1, 5, 5], "texture": "#main"}, + "down": {"uv": [1, 1, 5, 5], "texture": "#main"} } }, { @@ -37,12 +37,12 @@ "to": [6, 7.5, 10], "rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 8]}, "faces": { - "north": {"uv": [0, 1, 1, 2], "texture": "#0"}, - "east": {"uv": [0, 1, 1, 5], "rotation": 90, "texture": "#0"}, - "south": {"uv": [0, 4, 1, 5], "texture": "#0"}, - "west": {"uv": [0, 1, 1, 5], "rotation": 90, "texture": "#0"}, - "up": {"uv": [0, 1, 1, 5], "texture": "#0"}, - "down": {"uv": [0, 1, 1, 5], "texture": "#0"} + "north": {"uv": [0, 1, 1, 2], "texture": "#main"}, + "east": {"uv": [0, 1, 1, 5], "rotation": 90, "texture": "#main"}, + "south": {"uv": [0, 4, 1, 5], "texture": "#main"}, + "west": {"uv": [0, 1, 1, 5], "rotation": 90, "texture": "#main"}, + "up": {"uv": [0, 1, 1, 5], "texture": "#main"}, + "down": {"uv": [0, 1, 1, 5], "texture": "#main"} } }, { @@ -50,12 +50,12 @@ "to": [10, 7.5, 6], "rotation": {"angle": 0, "axis": "y", "origin": [10, 6.5, 7]}, "faces": { - "north": {"uv": [1, 0, 5, 1], "texture": "#0"}, - "east": {"uv": [4, 0, 5, 1], "texture": "#0"}, - "south": {"uv": [1, 0, 5, 1], "texture": "#0"}, - "west": {"uv": [1, 0, 2, 1], "texture": "#0"}, - "up": {"uv": [1, 0, 5, 1], "texture": "#0"}, - "down": {"uv": [1, 0, 5, 1], "texture": "#0"} + "north": {"uv": [1, 0, 5, 1], "texture": "#main"}, + "east": {"uv": [4, 0, 5, 1], "texture": "#main"}, + "south": {"uv": [1, 0, 5, 1], "texture": "#main"}, + "west": {"uv": [1, 0, 2, 1], "texture": "#main"}, + "up": {"uv": [1, 0, 5, 1], "texture": "#main"}, + "down": {"uv": [1, 0, 5, 1], "texture": "#main"} } }, { @@ -63,12 +63,12 @@ "to": [11, 7.5, 10], "rotation": {"angle": 0, "axis": "y", "origin": [13, 6.5, 8]}, "faces": { - "north": {"uv": [5, 1, 6, 2], "texture": "#0"}, - "east": {"uv": [5, 1, 6, 5], "rotation": 90, "texture": "#0"}, - "south": {"uv": [5, 4, 6, 5], "texture": "#0"}, - "west": {"uv": [5, 1, 6, 5], "rotation": 90, "texture": "#0"}, - "up": {"uv": [5, 1, 6, 5], "texture": "#0"}, - "down": {"uv": [5, 1, 6, 5], "texture": "#0"} + "north": {"uv": [5, 1, 6, 2], "texture": "#main"}, + "east": {"uv": [5, 1, 6, 5], "rotation": 90, "texture": "#main"}, + "south": {"uv": [5, 4, 6, 5], "texture": "#main"}, + "west": {"uv": [5, 1, 6, 5], "rotation": 90, "texture": "#main"}, + "up": {"uv": [5, 1, 6, 5], "texture": "#main"}, + "down": {"uv": [5, 1, 6, 5], "texture": "#main"} } }, { @@ -76,11 +76,11 @@ "to": [11, 6.5, 8.5], "rotation": {"angle": -45, "axis": "z", "origin": [11, 5, 8]}, "faces": { - "north": {"uv": [15, 1, 16, 5], "texture": "#0"}, - "east": {"uv": [14, 1, 15, 5], "texture": "#0"}, - "south": {"uv": [13, 1, 14, 5], "texture": "#0"}, - "west": {"uv": [12, 1, 13, 5], "texture": "#0"}, - "down": {"uv": [13, 4, 14, 5], "texture": "#0"} + "north": {"uv": [15, 1, 16, 5], "texture": "#main"}, + "east": {"uv": [14, 1, 15, 5], "texture": "#main"}, + "south": {"uv": [13, 1, 14, 5], "texture": "#main"}, + "west": {"uv": [12, 1, 13, 5], "texture": "#main"}, + "down": {"uv": [13, 4, 14, 5], "texture": "#main"} } }, { @@ -88,11 +88,11 @@ "to": [12.06066, 8.06066, 8.5], "rotation": {"angle": 0, "axis": "y", "origin": [19.06066, 15.06066, 16]}, "faces": { - "north": {"uv": [15, 0, 16, 2], "texture": "#0"}, - "east": {"uv": [14, 0, 15, 2], "texture": "#0"}, - "south": {"uv": [13, 0, 14, 2], "texture": "#0"}, - "west": {"uv": [12, 0, 13, 2], "texture": "#0"}, - "up": {"uv": [13, 0, 14, 1], "texture": "#0"} + "north": {"uv": [15, 0, 16, 2], "texture": "#main"}, + "east": {"uv": [14, 0, 15, 2], "texture": "#main"}, + "south": {"uv": [13, 0, 14, 2], "texture": "#main"}, + "west": {"uv": [12, 0, 13, 2], "texture": "#main"}, + "up": {"uv": [13, 0, 14, 1], "texture": "#main"} } }, { @@ -100,11 +100,11 @@ "to": [8.5, 6.5, 6], "rotation": {"angle": -45, "axis": "x", "origin": [8, 5, 5]}, "faces": { - "north": {"uv": [14, 1, 15, 5], "texture": "#0"}, - "east": {"uv": [13, 1, 14, 5], "texture": "#0"}, - "south": {"uv": [12, 1, 13, 5], "texture": "#0"}, - "west": {"uv": [15, 1, 16, 5], "texture": "#0"}, - "down": {"uv": [13, 4, 14, 5], "rotation": 90, "texture": "#0"} + "north": {"uv": [14, 1, 15, 5], "texture": "#main"}, + "east": {"uv": [13, 1, 14, 5], "texture": "#main"}, + "south": {"uv": [12, 1, 13, 5], "texture": "#main"}, + "west": {"uv": [15, 1, 16, 5], "texture": "#main"}, + "down": {"uv": [13, 4, 14, 5], "rotation": 90, "texture": "#main"} } }, { @@ -112,11 +112,11 @@ "to": [8.5, 8.06066, 4.93934], "rotation": {"angle": 0, "axis": "y", "origin": [16, 15.06066, -3.06066]}, "faces": { - "north": {"uv": [14, 0, 15, 2], "texture": "#0"}, - "east": {"uv": [13, 0, 14, 2], "texture": "#0"}, - "south": {"uv": [12, 0, 13, 2], "texture": "#0"}, - "west": {"uv": [15, 0, 16, 2], "texture": "#0"}, - "up": {"uv": [13, 0, 14, 1], "rotation": 270, "texture": "#0"} + "north": {"uv": [14, 0, 15, 2], "texture": "#main"}, + "east": {"uv": [13, 0, 14, 2], "texture": "#main"}, + "south": {"uv": [12, 0, 13, 2], "texture": "#main"}, + "west": {"uv": [15, 0, 16, 2], "texture": "#main"}, + "up": {"uv": [13, 0, 14, 1], "rotation": 270, "texture": "#main"} } }, { @@ -124,11 +124,11 @@ "to": [6, 6.5, 8.5], "rotation": {"angle": 45, "axis": "z", "origin": [5, 5, 8]}, "faces": { - "north": {"uv": [13, 1, 14, 5], "texture": "#0"}, - "east": {"uv": [12, 1, 13, 5], "texture": "#0"}, - "south": {"uv": [15, 1, 16, 5], "texture": "#0"}, - "west": {"uv": [14, 1, 15, 5], "texture": "#0"}, - "down": {"uv": [13, 4, 14, 5], "rotation": 180, "texture": "#0"} + "north": {"uv": [13, 1, 14, 5], "texture": "#main"}, + "east": {"uv": [12, 1, 13, 5], "texture": "#main"}, + "south": {"uv": [15, 1, 16, 5], "texture": "#main"}, + "west": {"uv": [14, 1, 15, 5], "texture": "#main"}, + "down": {"uv": [13, 4, 14, 5], "rotation": 180, "texture": "#main"} } }, { @@ -136,11 +136,11 @@ "to": [4.93934, 8.06066, 8.5], "rotation": {"angle": 0, "axis": "y", "origin": [-3.06066, 15.06066, 0]}, "faces": { - "north": {"uv": [13, 0, 14, 2], "texture": "#0"}, - "east": {"uv": [12, 0, 13, 2], "texture": "#0"}, - "south": {"uv": [15, 0, 16, 2], "texture": "#0"}, - "west": {"uv": [14, 0, 15, 2], "texture": "#0"}, - "up": {"uv": [13, 0, 14, 1], "rotation": 180, "texture": "#0"} + "north": {"uv": [13, 0, 14, 2], "texture": "#main"}, + "east": {"uv": [12, 0, 13, 2], "texture": "#main"}, + "south": {"uv": [15, 0, 16, 2], "texture": "#main"}, + "west": {"uv": [14, 0, 15, 2], "texture": "#main"}, + "up": {"uv": [13, 0, 14, 1], "rotation": 180, "texture": "#main"} } }, { @@ -148,11 +148,11 @@ "to": [8.5, 6.5, 11], "rotation": {"angle": 45, "axis": "x", "origin": [8, 5, 11]}, "faces": { - "north": {"uv": [12, 1, 13, 5], "texture": "#0"}, - "east": {"uv": [15, 1, 16, 5], "texture": "#0"}, - "south": {"uv": [14, 1, 15, 5], "texture": "#0"}, - "west": {"uv": [13, 1, 14, 5], "texture": "#0"}, - "down": {"uv": [13, 4, 14, 5], "rotation": 270, "texture": "#0"} + "north": {"uv": [12, 1, 13, 5], "texture": "#main"}, + "east": {"uv": [15, 1, 16, 5], "texture": "#main"}, + "south": {"uv": [14, 1, 15, 5], "texture": "#main"}, + "west": {"uv": [13, 1, 14, 5], "texture": "#main"}, + "down": {"uv": [13, 4, 14, 5], "rotation": 270, "texture": "#main"} } }, { @@ -160,11 +160,11 @@ "to": [8.5, 8.06066, 12.06066], "rotation": {"angle": 0, "axis": "y", "origin": [0, 15.06066, 19.06066]}, "faces": { - "north": {"uv": [12, 0, 13, 2], "texture": "#0"}, - "east": {"uv": [15, 0, 16, 2], "texture": "#0"}, - "south": {"uv": [14, 0, 15, 2], "texture": "#0"}, - "west": {"uv": [13, 0, 14, 2], "texture": "#0"}, - "up": {"uv": [13, 0, 14, 1], "rotation": 90, "texture": "#0"} + "north": {"uv": [12, 0, 13, 2], "texture": "#main"}, + "east": {"uv": [15, 0, 16, 2], "texture": "#main"}, + "south": {"uv": [14, 0, 15, 2], "texture": "#main"}, + "west": {"uv": [13, 0, 14, 2], "texture": "#main"}, + "up": {"uv": [13, 0, 14, 1], "rotation": 90, "texture": "#main"} } }, { @@ -173,12 +173,12 @@ "to": [9.5, 4, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [14, 11, 17]}, "faces": { - "north": {"uv": [7, 3, 10, 4], "texture": "#0"}, - "east": {"uv": [7, 3, 10, 4], "texture": "#0"}, - "south": {"uv": [7, 3, 10, 4], "texture": "#0"}, - "west": {"uv": [7, 3, 10, 4], "texture": "#0"}, - "up": {"uv": [7, 0, 10, 3], "texture": "#0"}, - "down": {"uv": [7, 0, 10, 3], "texture": "#0"} + "north": {"uv": [7, 3, 10, 4], "texture": "#main"}, + "east": {"uv": [7, 3, 10, 4], "texture": "#main"}, + "south": {"uv": [7, 3, 10, 4], "texture": "#main"}, + "west": {"uv": [7, 3, 10, 4], "texture": "#main"}, + "up": {"uv": [7, 0, 10, 3], "texture": "#main"}, + "down": {"uv": [7, 0, 10, 3], "texture": "#main"} } }, { @@ -187,10 +187,10 @@ "to": [9, 3, 9], "rotation": {"angle": 0, "axis": "y", "origin": [15, 9.5, 15]}, "faces": { - "north": {"uv": [5, 9, 7, 11], "texture": "#0"}, - "east": {"uv": [5, 9, 7, 11], "texture": "#0"}, - "south": {"uv": [5, 9, 7, 11], "texture": "#0"}, - "west": {"uv": [5, 9, 7, 11], "texture": "#0"} + "north": {"uv": [5, 9, 7, 11], "texture": "#main"}, + "east": {"uv": [5, 9, 7, 11], "texture": "#main"}, + "south": {"uv": [5, 9, 7, 11], "texture": "#main"}, + "west": {"uv": [5, 9, 7, 11], "texture": "#main"} } }, { @@ -199,12 +199,12 @@ "to": [10, 1, 10], "rotation": {"angle": 0, "axis": "y", "origin": [14, 8, 17]}, "faces": { - "north": {"uv": [4, 11, 8, 12], "texture": "#0"}, - "east": {"uv": [4, 11, 8, 12], "texture": "#0"}, - "south": {"uv": [4, 11, 8, 12], "texture": "#0"}, - "west": {"uv": [4, 11, 8, 12], "texture": "#0"}, - "up": {"uv": [0, 8, 4, 12], "texture": "#0"}, - "down": {"uv": [0, 12, 4, 16], "texture": "#0", "cullface": "down"} + "north": {"uv": [4, 11, 8, 12], "texture": "#main"}, + "east": {"uv": [4, 11, 8, 12], "texture": "#main"}, + "south": {"uv": [4, 11, 8, 12], "texture": "#main"}, + "west": {"uv": [4, 11, 8, 12], "texture": "#main"}, + "up": {"uv": [0, 8, 4, 12], "texture": "#main"}, + "down": {"uv": [0, 12, 4, 16], "texture": "#main", "cullface": "down"} } } ], From fe5060a2c0b25159bea930fc350f59c654805db8 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Wed, 13 May 2020 12:06:41 +0100 Subject: [PATCH 104/415] Don't decrement spell index if spells haven't been initialised, and add the missing safety check to getCurrentMaxCooldown - fixes #428 --- src/main/java/electroblob/wizardry/util/WandHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/electroblob/wizardry/util/WandHelper.java b/src/main/java/electroblob/wizardry/util/WandHelper.java index ca8fde07..9dda1184 100644 --- a/src/main/java/electroblob/wizardry/util/WandHelper.java +++ b/src/main/java/electroblob/wizardry/util/WandHelper.java @@ -217,7 +217,7 @@ public final class WandHelper { int spellIndex = wand.getTagCompound().getInteger(SELECTED_SPELL_KEY); if(spellIndex <= 0){ - spellIndex = numberOfSpells - 1; + spellIndex = Math.max(0, numberOfSpells - 1); }else{ spellIndex--; } @@ -370,7 +370,7 @@ public final class WandHelper { int selectedSpell = wand.getTagCompound().getInteger(SELECTED_SPELL_KEY); - if(cooldowns.length <= selectedSpell) return 0; + if(selectedSpell < 0 || cooldowns.length <= selectedSpell) return 0; return cooldowns[selectedSpell]; } From 04bebe59108cfe3ec3444d309db0da859dd5d951 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 14 May 2020 17:20:19 +0100 Subject: [PATCH 105/415] Allow receptacles to be ignited using dispensers --- .../wizardry/registry/WizardryItems.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/main/java/electroblob/wizardry/registry/WizardryItems.java b/src/main/java/electroblob/wizardry/registry/WizardryItems.java index a8e47661..b33d4577 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryItems.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryItems.java @@ -11,12 +11,15 @@ import electroblob.wizardry.item.*; import electroblob.wizardry.misc.BehaviourSpellDispense; import electroblob.wizardry.registry.WizardryTabs.CreativeTabListed; import electroblob.wizardry.registry.WizardryTabs.CreativeTabSorted; +import electroblob.wizardry.tileentity.TileEntityReceptacle; import net.minecraft.block.Block; import net.minecraft.block.BlockDispenser; import net.minecraft.block.BlockPlanks; import net.minecraft.dispenser.BehaviorProjectileDispense; +import net.minecraft.dispenser.IBlockSource; import net.minecraft.dispenser.IPosition; import net.minecraft.entity.IProjectile; +import net.minecraft.init.Bootstrap; import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.inventory.EntityEquipmentSlot.Type; import net.minecraft.item.EnumRarity; @@ -25,6 +28,10 @@ import net.minecraft.item.Item.ToolMaterial; import net.minecraft.item.ItemArmor.ArmorMaterial; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.common.util.EnumHelper; import net.minecraftforge.event.RegistryEvent; @@ -670,6 +677,38 @@ public final class WizardryItems { BlockDispenser.DISPENSE_BEHAVIOR_REGISTRY.putObject(scroll, new BehaviourSpellDispense()); + BlockDispenser.DISPENSE_BEHAVIOR_REGISTRY.putObject(spectral_dust, new Bootstrap.BehaviorDispenseOptional(){ + + // TODO: Needs packets of some kind, may be able to get away with using the tile entity sync packet + + @Override + protected ItemStack dispenseStack(IBlockSource source, ItemStack stack){ + + World world = source.getWorld(); + EnumFacing direction = source.getBlockState().getValue(BlockDispenser.FACING); + BlockPos pos = source.getBlockPos().offset(direction); + TileEntity tileEntity = world.getTileEntity(pos); + + if(tileEntity instanceof TileEntityReceptacle && ((TileEntityReceptacle)tileEntity).getElement() == null){ + ((TileEntityReceptacle)tileEntity).setElement(Element.values()[stack.getMetadata()]); + stack.shrink(1); + world.checkLight(pos); + world.notifyBlockUpdate(pos, source.getBlockState(), source.getBlockState(), 3); + return stack; + } + + return super.dispenseStack(source, stack); + } + + @Override + protected void playDispenseSound(IBlockSource source){ + EnumFacing direction = source.getBlockState().getValue(BlockDispenser.FACING); + BlockPos pos = source.getBlockPos().offset(direction); + source.getWorld().playSound(pos.getX(), pos.getY(), pos.getZ(), WizardrySounds.BLOCK_RECEPTACLE_IGNITE, + SoundCategory.BLOCKS, 0.7f, 0.7f, false); + } + }); + } public static void populateWandMap(){ From f5ce186a7daed79445540b22cbf366736127f67b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 15 May 2020 20:30:56 +0100 Subject: [PATCH 106/415] Well this hasn't been looked at in a while --- .../java/electroblob/wizardry/inventory/SlotItemClassList.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/electroblob/wizardry/inventory/SlotItemClassList.java b/src/main/java/electroblob/wizardry/inventory/SlotItemClassList.java index bbdbd1b7..d29c000e 100644 --- a/src/main/java/electroblob/wizardry/inventory/SlotItemClassList.java +++ b/src/main/java/electroblob/wizardry/inventory/SlotItemClassList.java @@ -23,10 +23,12 @@ public class SlotItemClassList extends Slot { this.stackLimit = stackLimit; } + @Override public int getSlotStackLimit(){ return stackLimit; } + @Override public boolean isItemValid(ItemStack stack){ for(Class itemClass : itemClasses){ From 28da6810d772918d14dcefc227c0ed673dc43e83 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 15 May 2020 21:21:21 +0100 Subject: [PATCH 107/415] Oops --- src/main/java/electroblob/wizardry/item/ItemManaFlask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/item/ItemManaFlask.java b/src/main/java/electroblob/wizardry/item/ItemManaFlask.java index 9b09c9e8..79f2105d 100644 --- a/src/main/java/electroblob/wizardry/item/ItemManaFlask.java +++ b/src/main/java/electroblob/wizardry/item/ItemManaFlask.java @@ -46,7 +46,7 @@ public class ItemManaFlask extends Item { @Override @SideOnly(Side.CLIENT) public void addInformation(ItemStack stack, @Nullable World worldIn, List tooltip, ITooltipFlag flagIn){ - Wizardry.proxy.addMultiLineDescription(tooltip, "item." + Wizardry.MODID + "mana_flask.desc", size.capacity); + Wizardry.proxy.addMultiLineDescription(tooltip, "item." + Wizardry.MODID + ":mana_flask.desc", size.capacity); } } 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 108/415] 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 From cda9947e17c51d489dff9e5786766d775dc202db Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 16 May 2020 18:26:59 +0100 Subject: [PATCH 109/415] All synchronised settings require world restart! --- src/main/java/electroblob/wizardry/Settings.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index 89b1cf32..d70ade04 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -739,6 +739,7 @@ public final class Settings { 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"); + property.setRequiresWorldRestart(true); bookItems = parseItemMetaStrings(property.getStringList()); propOrder.add(property.getName()); From 594c546f118ee1cf253e613d56b4239f2d90ff26 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 16 May 2020 18:33:53 +0100 Subject: [PATCH 110/415] Make all valid books work with shift-clicking and hopper insertion --- .../wizardry/inventory/ContainerBookshelf.java | 10 +++++++--- .../wizardry/tileentity/TileEntityBookshelf.java | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java b/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java index a32b7d2b..8905a7de 100644 --- a/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java +++ b/src/main/java/electroblob/wizardry/inventory/ContainerBookshelf.java @@ -3,7 +3,6 @@ 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; @@ -83,7 +82,7 @@ public class ContainerBookshelf extends Container { int minSlotId = 0; int maxSlotId = BlockBookshelf.SLOT_COUNT - 1; - if(!(stack.getItem() instanceof ItemSpellBook)) return ItemStack.EMPTY; + if(!isBook(stack)) return ItemStack.EMPTY; if(!this.mergeItemStack(stack, minSlotId, maxSlotId + 1, false)){ return ItemStack.EMPTY; @@ -106,6 +105,11 @@ public class ContainerBookshelf extends Container { return remainder; } + /** Returns true if the given stack counts as a book and can be placed in a bookshelf, false if not. */ + public static boolean isBook(ItemStack stack){ + return validItems.contains(stack.getItem()) || Settings.containsMetaItem(Wizardry.settings.bookItems, stack); + } + /** * 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. @@ -149,7 +153,7 @@ public class ContainerBookshelf extends Container { @Override public boolean isItemValid(ItemStack stack){ - return validItems.contains(stack.getItem()) || Settings.containsMetaItem(Wizardry.settings.bookItems, stack); + return isBook(stack); } } diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java index 6d3565a7..55eb9cf2 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityBookshelf.java @@ -2,6 +2,7 @@ package electroblob.wizardry.tileentity; import electroblob.wizardry.Wizardry; import electroblob.wizardry.block.BlockBookshelf; +import electroblob.wizardry.inventory.ContainerBookshelf; import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.util.NBTExtras; import net.minecraft.entity.player.EntityPlayer; @@ -136,8 +137,8 @@ public class TileEntityBookshelf extends TileEntity implements IInventory, ITick } @Override - public boolean isItemValidForSlot(int slotNumber, ItemStack itemstack){ - return itemstack.isEmpty() || itemstack.getItem() instanceof ItemSpellBook; // TODO: Add a whitelist + public boolean isItemValidForSlot(int slotNumber, ItemStack stack){ + return stack.isEmpty() || ContainerBookshelf.isBook(stack); } @Override From 94a17a63c1e616968e8c08f993443d8c5b90403d Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sat, 16 May 2020 22:24:14 +0100 Subject: [PATCH 111/415] Sync tile entity when book texture changes --- src/main/java/electroblob/wizardry/block/BlockBookshelf.java | 5 +++++ .../electroblob/wizardry/inventory/ContainerBookshelf.java | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java index 53935b17..041e2273 100644 --- a/src/main/java/electroblob/wizardry/block/BlockBookshelf.java +++ b/src/main/java/electroblob/wizardry/block/BlockBookshelf.java @@ -196,6 +196,11 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid return bookTextures == null ? ImmutableList.copyOf(BOOK_TEXTURE_MAP.values()) : bookTextures; } + /** Returns the list of registered book items, in ID order. */ + public static ImmutableList getBookItems(){ + return bookItems; + } + /** Called during block registration to initialise the block properties for each book. */ public static void initBookProperties(){ for(int i=0; i Date: Sat, 16 May 2020 23:35:23 +0100 Subject: [PATCH 112/415] This is nullable --- src/main/java/electroblob/wizardry/item/IManaStoringItem.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/item/IManaStoringItem.java b/src/main/java/electroblob/wizardry/item/IManaStoringItem.java index de93846d..585f0752 100644 --- a/src/main/java/electroblob/wizardry/item/IManaStoringItem.java +++ b/src/main/java/electroblob/wizardry/item/IManaStoringItem.java @@ -4,6 +4,8 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; +import javax.annotation.Nullable; + /** * Interface for any items that store mana. This interface simply specifies methods for setting and getting the amount * of mana held in the item (plus a few convenience methods); implementations may differ between items. @@ -48,7 +50,7 @@ public interface IManaStoringItem { /** Convenience method that decreases the amount of mana contained in the given item stack by the given value. This * method automatically limits the mana to a minimum of 0 and performs the relevant checks for creative mode, etc. */ - default void consumeMana(ItemStack stack, int mana, EntityLivingBase wielder){ + default void consumeMana(ItemStack stack, int mana, @Nullable EntityLivingBase wielder){ if(wielder instanceof EntityPlayer && ((EntityPlayer)wielder).isCreative()) return; // Mana isn't consumed in creative setMana(stack, Math.max(getMana(stack) - mana, 0)); } From ef39757229966d25eba7cd6188282da668f863f5 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 17 May 2020 00:04:38 +0100 Subject: [PATCH 113/415] Add right-click use for mana flasks - right-click while holding a mana flask to charge the emptiest item from the hotbar, offhand or armour slots --- .../wizardry/item/IManaStoringItem.java | 6 ++++ .../wizardry/item/ItemManaFlask.java | 35 +++++++++++++++++++ .../wizardry/registry/WizardrySounds.java | 4 +++ .../resources/assets/ebwizardry/sounds.json | 4 ++- 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/electroblob/wizardry/item/IManaStoringItem.java b/src/main/java/electroblob/wizardry/item/IManaStoringItem.java index 585f0752..26317e9e 100644 --- a/src/main/java/electroblob/wizardry/item/IManaStoringItem.java +++ b/src/main/java/electroblob/wizardry/item/IManaStoringItem.java @@ -71,4 +71,10 @@ public interface IManaStoringItem { default boolean isManaEmpty(ItemStack stack){ return getMana(stack) == 0; } + + /** Returns how full the given stack's mana is, as a fraction between 0 (empty) and 1 (full) */ + default float getFullness(ItemStack stack){ + return (float)getMana(stack) / getManaCapacity(stack); + } + } diff --git a/src/main/java/electroblob/wizardry/item/ItemManaFlask.java b/src/main/java/electroblob/wizardry/item/ItemManaFlask.java index 79f2105d..4b8ee67a 100644 --- a/src/main/java/electroblob/wizardry/item/ItemManaFlask.java +++ b/src/main/java/electroblob/wizardry/item/ItemManaFlask.java @@ -1,16 +1,23 @@ package electroblob.wizardry.item; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumRarity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import javax.annotation.Nullable; +import java.util.Comparator; import java.util.List; public class ItemManaFlask extends Item { @@ -49,4 +56,32 @@ public class ItemManaFlask extends Item { Wizardry.proxy.addMultiLineDescription(tooltip, "item." + Wizardry.MODID + ":mana_flask.desc", size.capacity); } + @Override + public ActionResult onItemRightClick(World world, EntityPlayer player, EnumHand hand){ + + ItemStack flask = player.getHeldItem(hand); + + List stacks = WizardryUtilities.getPrioritisedHotbarAndOffhand(player); + stacks.addAll(player.inventory.armorInventory); // player#getArmorInventoryList() only returns an Iterable + + // Find the chargeable item with the least mana + ItemStack toCharge = stacks.stream() + .filter(s -> s.getItem() instanceof IManaStoringItem && !((IManaStoringItem)s.getItem()).isManaFull(s)) + .min(Comparator.comparingDouble(s -> ((IManaStoringItem)s.getItem()).getFullness(s))).orElse(null); + + if(toCharge != null){ + + ((IManaStoringItem)toCharge.getItem()).rechargeMana(toCharge, size.capacity); + + WizardryUtilities.playSoundAtPlayer(player, WizardrySounds.ITEM_MANA_FLASK_USE, 1, 1); + WizardryUtilities.playSoundAtPlayer(player, WizardrySounds.ITEM_MANA_FLASK_RECHARGE, 0.7f, 1.1f); + + if(!player.isCreative()) flask.shrink(1); + + return new ActionResult<>(EnumActionResult.SUCCESS, flask); + + }else{ + return new ActionResult<>(EnumActionResult.FAIL, flask); + } + } } diff --git a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java index 6b732c7b..2e203b04 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java +++ b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java @@ -36,6 +36,8 @@ public final class WizardrySounds { public static final SoundEvent ITEM_WAND_MELEE = createSound("item.wand.melee"); public static final SoundEvent ITEM_ARMOUR_EQUIP_SILK = createSound("item.armour.equip_silk"); public static final SoundEvent ITEM_PURIFYING_ELIXIR_DRINK = createSound("item.purifying_elixir.drink"); + public static final SoundEvent ITEM_MANA_FLASK_USE = createSound("item.mana_flask.use"); + public static final SoundEvent ITEM_MANA_FLASK_RECHARGE = createSound("item.mana_flask.recharge"); public static final SoundEvent ENTITY_BLACK_HOLE_AMBIENT = createSound("entity.black_hole.ambient"); public static final SoundEvent ENTITY_BLACK_HOLE_VANISH = createSound("entity.black_hole.vanish"); @@ -164,6 +166,8 @@ public final class WizardrySounds { event.getRegistry().register(ITEM_WAND_MELEE); event.getRegistry().register(ITEM_ARMOUR_EQUIP_SILK); event.getRegistry().register(ITEM_PURIFYING_ELIXIR_DRINK); + event.getRegistry().register(ITEM_MANA_FLASK_USE); + event.getRegistry().register(ITEM_MANA_FLASK_RECHARGE); event.getRegistry().register(ENTITY_BLACK_HOLE_AMBIENT); event.getRegistry().register(ENTITY_BLACK_HOLE_VANISH); diff --git a/src/main/resources/assets/ebwizardry/sounds.json b/src/main/resources/assets/ebwizardry/sounds.json index 4ff79637..7686d7ba 100644 --- a/src/main/resources/assets/ebwizardry/sounds.json +++ b/src/main/resources/assets/ebwizardry/sounds.json @@ -10,7 +10,9 @@ "item.wand.melee": {"category": "player", "sounds": ["ebwizardry:effect1", "ebwizardry:effect2"]}, "item.armour.equip_silk": {"category": "player", "sounds": ["item/armor/equip_leather1", "item/armor/equip_leather2", "item/armor/equip_leather3", "item/armor/equip_leather4", "item/armor/equip_leather5", "item/armor/equip_leather6"]}, "item.purifying_elixir.drink": {"category": "player", "sounds": ["ebwizardry:buff_large"]}, - + "item.mana_flask.use": {"category": "player", "sounds": ["item/bottle/fill1", "item/bottle/fill2", "item/bottle/fill3", "item/bottle/fill4"]}, + "item.mana_flask.recharge": {"category": "player", "sounds": ["ebwizardry:conjure"]}, + "entity.black_hole.ambient": {"category": "spells", "sounds": ["portal/portal"]}, "entity.black_hole.vanish": {"category": "spells", "sounds": ["portal/trigger"]}, "entity.bubble.pop": {"category": "spells", "sounds": ["random/pop"]}, From 4739d9f9fca181726587ef96cc607a811c10a1b4 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 17 May 2020 00:32:02 +0100 Subject: [PATCH 114/415] Add cooldowns (using the vanilla mechanic) to various consumable items --- .../wizardry/item/ItemFirebomb.java | 2 ++ .../wizardry/item/ItemManaFlask.java | 1 + .../wizardry/item/ItemPoisonBomb.java | 2 ++ .../electroblob/wizardry/item/ItemScroll.java | 25 +++++++++++++------ .../wizardry/item/ItemSmokeBomb.java | 2 ++ .../wizardry/item/ItemSparkBomb.java | 2 ++ 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/main/java/electroblob/wizardry/item/ItemFirebomb.java b/src/main/java/electroblob/wizardry/item/ItemFirebomb.java index c3404136..d08ac5dc 100644 --- a/src/main/java/electroblob/wizardry/item/ItemFirebomb.java +++ b/src/main/java/electroblob/wizardry/item/ItemFirebomb.java @@ -29,6 +29,8 @@ public class ItemFirebomb extends Item { player.playSound(WizardrySounds.ENTITY_FIREBOMB_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + player.getCooldownTracker().setCooldown(this, 20); + if(!world.isRemote){ EntityFirebomb firebomb = new EntityFirebomb(world); firebomb.aim(player, 1); diff --git a/src/main/java/electroblob/wizardry/item/ItemManaFlask.java b/src/main/java/electroblob/wizardry/item/ItemManaFlask.java index 4b8ee67a..8ecad4a1 100644 --- a/src/main/java/electroblob/wizardry/item/ItemManaFlask.java +++ b/src/main/java/electroblob/wizardry/item/ItemManaFlask.java @@ -77,6 +77,7 @@ public class ItemManaFlask extends Item { WizardryUtilities.playSoundAtPlayer(player, WizardrySounds.ITEM_MANA_FLASK_RECHARGE, 0.7f, 1.1f); if(!player.isCreative()) flask.shrink(1); + player.getCooldownTracker().setCooldown(this, 20); return new ActionResult<>(EnumActionResult.SUCCESS, flask); diff --git a/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java b/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java index a68c8aa4..742cdfb1 100644 --- a/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java +++ b/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java @@ -29,6 +29,8 @@ public class ItemPoisonBomb extends Item { player.playSound(WizardrySounds.ENTITY_POISON_BOMB_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + player.getCooldownTracker().setCooldown(this, 20); + if(!world.isRemote){ EntityPoisonBomb poisonbomb = new EntityPoisonBomb(world); poisonbomb.aim(player, 1); diff --git a/src/main/java/electroblob/wizardry/item/ItemScroll.java b/src/main/java/electroblob/wizardry/item/ItemScroll.java index 6c061f5d..89d24c4d 100644 --- a/src/main/java/electroblob/wizardry/item/ItemScroll.java +++ b/src/main/java/electroblob/wizardry/item/ItemScroll.java @@ -190,6 +190,9 @@ public class ItemScroll extends Item implements ISpellCastingItem, IWorkbenchIte // Scrolls are consumed upon successful use in survival mode if(!spell.isContinuous && !caster.isCreative()) stack.shrink(1); + + // Now uses the vanilla cooldown mechanic to prevent spamming of spells + if(!spell.isContinuous) caster.getCooldownTracker().setCooldown(this, spell.getCooldown()); } return true; @@ -201,21 +204,29 @@ public class ItemScroll extends Item implements ISpellCastingItem, IWorkbenchIte @Override public void onPlayerStoppedUsing(ItemStack stack, World world, EntityLivingBase user, int timeLeft){ // Consumes a continuous spell scroll when a player in survival mode stops using it. - if(Spell.byMetadata(stack.getItemDamage()).isContinuous - && (!(user instanceof EntityPlayer) || !((EntityPlayer)user).isCreative())){ - stack.shrink(1); - } + finishCasting(stack, user); } - + @Override public ItemStack onItemUseFinish(ItemStack stack, World world, EntityLivingBase user){ // Consumes a continuous spell scroll when the casting elapses whilst in use by a player in survival mode. + finishCasting(stack, user); + return stack; + } + + private void finishCasting(ItemStack stack, EntityLivingBase user){ + if(Spell.byMetadata(stack.getItemDamage()).isContinuous && (!(user instanceof EntityPlayer) || !((EntityPlayer)user).isCreative())){ + stack.shrink(1); + + Spell spell = Spell.byMetadata(stack.getItemDamage()); + + if(user instanceof EntityPlayer){ + ((EntityPlayer)user).getCooldownTracker().setCooldown(this, spell.getCooldown()); + } } - - return stack; } @Override diff --git a/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java b/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java index a62a1179..22a47cf2 100644 --- a/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java +++ b/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java @@ -29,6 +29,8 @@ public class ItemSmokeBomb extends Item { player.playSound(WizardrySounds.ENTITY_SMOKE_BOMB_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + player.getCooldownTracker().setCooldown(this, 20); + if(!world.isRemote){ EntitySmokeBomb smokebomb = new EntitySmokeBomb(world); smokebomb.aim(player, 1); diff --git a/src/main/java/electroblob/wizardry/item/ItemSparkBomb.java b/src/main/java/electroblob/wizardry/item/ItemSparkBomb.java index 319b3dae..4378f417 100644 --- a/src/main/java/electroblob/wizardry/item/ItemSparkBomb.java +++ b/src/main/java/electroblob/wizardry/item/ItemSparkBomb.java @@ -29,6 +29,8 @@ public class ItemSparkBomb extends Item { player.playSound(WizardrySounds.ENTITY_SPARK_BOMB_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + player.getCooldownTracker().setCooldown(this, 20); + if(!world.isRemote){ EntitySparkBomb sparkBomb = new EntitySparkBomb(world); sparkBomb.aim(player, 1); From ebde514c9861d5d86b4570e9f9df6ac2b8b85fe5 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 17 May 2020 00:35:56 +0100 Subject: [PATCH 115/415] Fix scrolls not triggering spell casting finish method and event --- .../electroblob/wizardry/item/ItemScroll.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/electroblob/wizardry/item/ItemScroll.java b/src/main/java/electroblob/wizardry/item/ItemScroll.java index 89d24c4d..f1426bd2 100644 --- a/src/main/java/electroblob/wizardry/item/ItemScroll.java +++ b/src/main/java/electroblob/wizardry/item/ItemScroll.java @@ -203,25 +203,29 @@ public class ItemScroll extends Item implements ISpellCastingItem, IWorkbenchIte @Override public void onPlayerStoppedUsing(ItemStack stack, World world, EntityLivingBase user, int timeLeft){ - // Consumes a continuous spell scroll when a player in survival mode stops using it. - finishCasting(stack, user); + // Casting has stopped before the full time has elapsed + finishCasting(stack, user, timeLeft); } @Override public ItemStack onItemUseFinish(ItemStack stack, World world, EntityLivingBase user){ - // Consumes a continuous spell scroll when the casting elapses whilst in use by a player in survival mode. - finishCasting(stack, user); + // Full casting time has elapsed + finishCasting(stack, user, 0); return stack; } - private void finishCasting(ItemStack stack, EntityLivingBase user){ + private void finishCasting(ItemStack stack, EntityLivingBase user, int timeLeft){ - if(Spell.byMetadata(stack.getItemDamage()).isContinuous - && (!(user instanceof EntityPlayer) || !((EntityPlayer)user).isCreative())){ - - stack.shrink(1); + if(Spell.byMetadata(stack.getItemDamage()).isContinuous){ + // Consume scrolls in survival mode + if(!(user instanceof EntityPlayer) || !((EntityPlayer)user).isCreative()) stack.shrink(1); Spell spell = Spell.byMetadata(stack.getItemDamage()); + SpellModifiers modifiers = new SpellModifiers(); + int castingTick = stack.getMaxItemUseDuration() - timeLeft; + + MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Finish(Source.SCROLL, spell, user, modifiers, castingTick)); + spell.finishCasting(user.world, user, Double.NaN, Double.NaN, Double.NaN, null, castingTick, modifiers); if(user instanceof EntityPlayer){ ((EntityPlayer)user).getCooldownTracker().setCooldown(this, spell.getCooldown()); From 650344a0c8c13c6f315f0497cf663fe1fbd2ee5d Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Mon, 18 May 2020 20:41:41 +0100 Subject: [PATCH 116/415] Add stormcloud spell --- .../wizardry/client/ClientProxy.java | 1 + .../entity/construct/EntityStormcloud.java | 122 ++++++++++++++++++ .../electroblob/wizardry/registry/Spells.java | 10 +- .../wizardry/registry/WizardryEntities.java | 1 + .../wizardry/registry/WizardrySounds.java | 2 + .../wizardry/spell/Stormcloud.java | 22 ++++ .../assets/ebwizardry/lang/en_gb.lang | 3 + .../assets/ebwizardry/lang/en_us.lang | 3 + .../resources/assets/ebwizardry/sounds.json | 3 + .../assets/ebwizardry/spells/stormcloud.json | 25 ++++ .../ebwizardry/textures/spells/stormcloud.png | Bin 0 -> 5816 bytes 11 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 src/main/java/electroblob/wizardry/entity/construct/EntityStormcloud.java create mode 100644 src/main/java/electroblob/wizardry/spell/Stormcloud.java create mode 100644 src/main/resources/assets/ebwizardry/spells/stormcloud.json create mode 100644 src/main/resources/assets/ebwizardry/textures/spells/stormcloud.png diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index c64b918d..ddb07c7b 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -795,6 +795,7 @@ public class ClientProxy extends CommonProxy { RenderingRegistry.registerEntityRenderingHandler(EntityStormElemental.class, RenderBlank::new); RenderingRegistry.registerEntityRenderingHandler(EntityEarthquake.class, RenderBlank::new); RenderingRegistry.registerEntityRenderingHandler(EntityHailstorm.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityStormcloud.class, RenderBlank::new); // Runes on ground RenderingRegistry.registerEntityRenderingHandler(EntityHealAura.class, diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityStormcloud.java b/src/main/java/electroblob/wizardry/entity/construct/EntityStormcloud.java new file mode 100644 index 00000000..895b5511 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityStormcloud.java @@ -0,0 +1,122 @@ +package electroblob.wizardry.entity.construct; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.ParticleBuilder; +import electroblob.wizardry.util.ParticleBuilder.Type; +import electroblob.wizardry.util.WizardryUtilities; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.world.World; + +import java.util.List; + +public class EntityStormcloud extends EntityMagicConstruct { + +// private static final DataParameter RADIUS = new DataParameter<>(21, DataSerializers.FLOAT); + + public EntityStormcloud(World world){ + super(world); + this.height = 2.0f; + this.width = Spells.stormcloud.getProperty(Spell.EFFECT_RADIUS).floatValue() * 2; + } + +// @Override +// protected void entityInit(){ +// super.entityInit(); +// this.getDataManager().register(RADIUS, width); // TODO: This doesn't work, implement blast modifiers properly into SpellConstruct +// } + +// public void multiplyWidth(float multiplier){ +// this.setSize(width * multiplier, height); +// this.getDataManager().set(RADIUS, width); +// } + + public void onUpdate(){ + + super.onUpdate(); + +// if(world.isRemote){ +// float radius = this.getDataManager().get(RADIUS); +// if(radius != width) this.setSize(radius, height); +// }else{ +// if(this.getDataManager().get(RADIUS) != width) this.getDataManager().set(RADIUS, width); +// } + +// if(this.ticksExisted % 35 == 0) this.playSound(WizardrySounds.ENTITY_STORMCLOUD_AMBIENT, 1, 1); + + if(this.world.isRemote){ + + float areaFactor = (width * width) / 36; // Ensures cloud/raindrop density stays the same for different sizes + + for(int i = 0; i < 2 * areaFactor; i++) ParticleBuilder.create(Type.CLOUD, this) + .clr(0.3f, 0.3f, 0.3f).shaded(true).spawn(world); + } + + List targets = world.getEntitiesWithinAABB(EntityLivingBase.class, + this.getEntityBoundingBox().expand(0, -10, 0)); + + float damage = Spells.stormcloud.getProperty(Spell.DAMAGE).floatValue() * this.damageMultiplier; + + for(EntityLivingBase target : targets){ + + if(this.isValidTarget(target)){ + + if(target.ticksExisted % 150 == 0){ // Use target's lifetime so they don't all get hit at once, looks better + + if(!this.world.isRemote){ + WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage( + this, this.getCaster(), MagicDamage.DamageType.SHOCK), damage); + }else{ + ParticleBuilder.create(Type.LIGHTNING).pos(target.posX, posY + height/2, target.posZ) + .target(target).scale(2).spawn(world); + ParticleBuilder.spawnShockParticles(world, target.posX, target.getEntityBoundingBox().minY + target.height, target.posZ); + } + + target.playSound(WizardrySounds.ENTITY_STORMCLOUD_THUNDER, 1, 1.6f); + target.playSound(WizardrySounds.ENTITY_STORMCLOUD_ATTACK, 1, 1); + } + } + } + +// BlockPos pos = new BlockPos(this); +// +// for(int x = -(int)(this.width/2); x <= this.width/2 + 0.5; x++){ +// for(int z = -(int)(this.width/2); z <= this.width/2 + 0.5; z++){ +// +// int y = WizardryUtilities.getNearestFloor() +// +// } +// } + + } + + // Need to sync the caster so they don't have particles spawned at them + + @Override + public void writeSpawnData(ByteBuf data){ + super.writeSpawnData(data); + if(getCaster() != null) data.writeInt(getCaster().getEntityId()); + } + + @Override + public void readSpawnData(ByteBuf data){ + + super.readSpawnData(data); + + if(!data.isReadable()) return; + + Entity entity = world.getEntityByID(data.readInt()); + + if(entity instanceof EntityLivingBase){ + setCaster((EntityLivingBase)entity); + }else{ + Wizardry.logger.warn("Stormcloud caster with ID in spawn data not found"); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/registry/Spells.java b/src/main/java/electroblob/wizardry/registry/Spells.java index d81fd47d..704a956f 100644 --- a/src/main/java/electroblob/wizardry/registry/Spells.java +++ b/src/main/java/electroblob/wizardry/registry/Spells.java @@ -8,7 +8,6 @@ import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.spell.*; import net.minecraft.entity.projectile.EntitySnowball; import net.minecraft.init.MobEffects; -import net.minecraft.item.EnumAction; import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.fml.common.Mod; @@ -239,6 +238,10 @@ public final class Spells { public static final Spell slow_time = placeholder(); public static final Spell resurrection = placeholder(); + // Wizardry 4.3 spells + + public static final Spell stormcloud = placeholder(); + @SubscribeEvent public static void register(RegistryEvent.Register event){ @@ -437,6 +440,11 @@ public final class Spells { registry.register(new SpeedTime()); registry.register(new SlowTime()); registry.register(new Resurrection()); + + // Wizardry 4.3 spells + + registry.register(new Stormcloud()); + } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java index ddb374bb..8324e692 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java @@ -150,6 +150,7 @@ public class WizardryEntities { registry.register(createEntry(EntityArrowRain.class, "arrow_rain", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityEarthquake.class, "earthquake", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityHailstorm.class, "hailstorm", TrackingType.CONSTRUCT).build()); + registry.register(createEntry(EntityStormcloud.class, "stormcloud", TrackingType.CONSTRUCT).build()); // These ones move, velocity updates are sent if that's not at constant velocity registry.register(createEntry(EntityShield.class, "shield") .tracker(160, 10, true).build()); diff --git a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java index 2e203b04..9f2538c4 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java +++ b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java @@ -86,6 +86,8 @@ public final class WizardrySounds { public static final SoundEvent ENTITY_STORM_ELEMENTAL_WIND = createSound("entity.storm_elemental.wind"); public static final SoundEvent ENTITY_STORM_ELEMENTAL_HURT = createSound("entity.storm_elemental.hurt"); public static final SoundEvent ENTITY_STORM_ELEMENTAL_DEATH = createSound("entity.storm_elemental.death"); + public static final SoundEvent ENTITY_STORMCLOUD_THUNDER = createSound("entity.stormcloud.thunder"); + public static final SoundEvent ENTITY_STORMCLOUD_ATTACK = createSound("entity.stormcloud.attack"); public static final SoundEvent ENTITY_WIZARD_YES = createSound("entity.wizard.yes"); public static final SoundEvent ENTITY_WIZARD_NO = createSound("entity.wizard.no"); public static final SoundEvent ENTITY_WIZARD_AMBIENT = createSound("entity.wizard.ambient"); diff --git a/src/main/java/electroblob/wizardry/spell/Stormcloud.java b/src/main/java/electroblob/wizardry/spell/Stormcloud.java new file mode 100644 index 00000000..5be5ec34 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Stormcloud.java @@ -0,0 +1,22 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.entity.construct.EntityStormcloud; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.EnumFacing; + +import javax.annotation.Nullable; + +public class Stormcloud extends SpellConstructRanged { + + public Stormcloud(){ + super("stormcloud", EntityStormcloud::new, false); + this.addProperties(DAMAGE, EFFECT_RADIUS); + this.floor(true); + } + + @Override + protected void addConstructExtras(EntityStormcloud construct, EnumFacing side, @Nullable EntityLivingBase caster, SpellModifiers modifiers){ + construct.posY += 5; + } +} diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index adcf6990..086a6d18 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -485,6 +485,7 @@ entity.ebwizardry\:ring_of_fire.name=Ring of Fire entity.ebwizardry\:earthquake.name=Earthquake entity.ebwizardry\:hailstorm.name=Hailstorm entity.ebwizardry\:lightning_pulse.name=Lightning Pulse +entity.ebwizardry\:stormcloud.name=Stormcloud itemGroup.ebwizardry=Wizardry itemGroup.ebwizardryspells=Spells @@ -747,6 +748,7 @@ spell.ebwizardry\:spectral_pathway=Spectral Pathway spell.ebwizardry\:speed_time=Speed Time spell.ebwizardry\:spider_swarm=Spider Swarm spell.ebwizardry\:static_aura=Static Aura +spell.ebwizardry\:stormcloud=Stormcloud spell.ebwizardry\:summon_blaze=Summon Blaze spell.ebwizardry\:summon_ice_giant=Summon Ice Giant spell.ebwizardry\:summon_ice_wraith=Summon Ice Wraith @@ -921,6 +923,7 @@ spell.ebwizardry\:spectral_pathway.desc=Creates an indestructible magical bridge spell.ebwizardry\:speed_time.desc=...day, night, dawn, dusk, sunrise and sunset\: thus is the passage of time, which traps us in an endless cycle of... spell.ebwizardry\:spider_swarm.desc=Summons a swarm of venomous spiders to fight for you. The spiders will disappear after 30 seconds or if they are killed. spell.ebwizardry\:static_aura.desc=Surrounds the caster with lightning for 30 seconds, firing a spark of lightning at anything that hits them. +spell.ebwizardry\:stormcloud.desc=Conjures a floating stormcloud where you are pointing that strikes lightning beneath it. spell.ebwizardry\:summon_blaze.desc=Summons a blaze to fight for you. The blaze will disappear after 30 seconds or if it is killed. spell.ebwizardry\:summon_ice_giant.desc="Smash them!" spell.ebwizardry\:summon_ice_wraith.desc=Summons an ice wraith to fight for you. The ice wraith will disappear after 30 seconds or if it is killed. diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index fc4a1010..5fa24eb1 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -485,6 +485,7 @@ entity.ebwizardry\:ring_of_fire.name=Ring of Fire entity.ebwizardry\:earthquake.name=Earthquake entity.ebwizardry\:hailstorm.name=Hailstorm entity.ebwizardry\:lightning_pulse.name=Lightning Pulse +entity.ebwizardry\:stormcloud.name=Stormcloud itemGroup.ebwizardry=Wizardry itemGroup.ebwizardryspells=Spells @@ -747,6 +748,7 @@ spell.ebwizardry\:spectral_pathway=Spectral Pathway spell.ebwizardry\:speed_time=Speed Time spell.ebwizardry\:spider_swarm=Spider Swarm spell.ebwizardry\:static_aura=Static Aura +spell.ebwizardry\:stormcloud=Stormcloud spell.ebwizardry\:summon_blaze=Summon Blaze spell.ebwizardry\:summon_ice_giant=Summon Ice Giant spell.ebwizardry\:summon_ice_wraith=Summon Ice Wraith @@ -921,6 +923,7 @@ spell.ebwizardry\:spectral_pathway.desc=Creates an indestructible magical bridge spell.ebwizardry\:speed_time.desc=...day, night, dawn, dusk, sunrise and sunset\: thus is the passage of time, which traps us in an endless cycle of... spell.ebwizardry\:spider_swarm.desc=Summons a swarm of venomous spiders to fight for you. The spiders will disappear after 30 seconds or if they are killed. spell.ebwizardry\:static_aura.desc=Surrounds the caster with lightning for 30 seconds, firing a spark of lightning at anything that hits them. +spell.ebwizardry\:stormcloud.desc=Conjures a floating stormcloud where you are pointing that strikes lightning beneath it. spell.ebwizardry\:summon_blaze.desc=Summons a blaze to fight for you. The blaze will disappear after 30 seconds or if it is killed. spell.ebwizardry\:summon_ice_giant.desc="Smash them!" spell.ebwizardry\:summon_ice_wraith.desc=Summons an ice wraith to fight for you. The ice wraith will disappear after 30 seconds or if it is killed. diff --git a/src/main/resources/assets/ebwizardry/sounds.json b/src/main/resources/assets/ebwizardry/sounds.json index 7686d7ba..1078a058 100644 --- a/src/main/resources/assets/ebwizardry/sounds.json +++ b/src/main/resources/assets/ebwizardry/sounds.json @@ -33,6 +33,8 @@ "entity.lightning_sigil.trigger": {"category": "spells", "sounds": ["ebwizardry:arc1", "ebwizardry:arc2", "ebwizardry:arc3"]}, "entity.meteor.falling": {"category": "spells", "sounds": ["ebwizardry:flames_loop"]}, "entity.shield.deflect": {"category": "spells", "sounds": ["ebwizardry:effect1", "ebwizardry:effect2"]}, + "entity.stormcloud.thunder": {"category": "spells", "sounds": ["ambient/weather/thunder1", "ambient/weather/thunder2", "ambient/weather/thunder3"]}, + "entity.stormcloud.attack": {"category": "spells", "sounds": ["ebwizardry:arc1", "ebwizardry:arc2", "ebwizardry:arc3"]}, "entity.tornado.ambient": {"category": "spells", "sounds": ["ebwizardry:wind"]}, "entity.evil_wizard.ambient": {"category": "hostile", "sounds": ["mob/evocation_illager/idle1", "mob/evocation_illager/idle2", "mob/evocation_illager/idle3", "mob/evocation_illager/idle4"]}, @@ -285,6 +287,7 @@ "spell.spider_swarm": {"category": "spells", "sounds": ["random/fizz"]}, "spell.static_aura": {"category": "spells", "sounds": ["ebwizardry:buff"]}, "spell.static_aura.retaliate": {"category": "spells", "sounds": ["ebwizardry:arc1", "ebwizardry:arc2", "ebwizardry:arc3"]}, + "spell.stormcloud": {"category": "spells", "sounds": ["ambient/weather/thunder1", "ambient/weather/thunder2", "ambient/weather/thunder3"]}, "spell.summon_blaze": {"category": "spells", "sounds": ["mob/wither/idle1", "mob/wither/idle2", "mob/wither/idle3", "mob/wither/idle4"]}, "spell.summon_ice_giant": {"category": "spells", "sounds": ["mob/evocation_illager/prepare_summon"]}, "spell.summon_ice_wraith": {"category": "spells", "sounds": ["mob/wither/idle1", "mob/wither/idle2", "mob/wither/idle3", "mob/wither/idle4"]}, diff --git a/src/main/resources/assets/ebwizardry/spells/stormcloud.json b/src/main/resources/assets/ebwizardry/spells/stormcloud.json new file mode 100644 index 00000000..20a33a0a --- /dev/null +++ b/src/main/resources/assets/ebwizardry/spells/stormcloud.json @@ -0,0 +1,25 @@ +{ + "enabled": { + "book": true, + "scroll": true, + "wands": true, + "npcs": true, + "dispensers": true, + "commands": true, + "treasure": true, + "trades": true, + "looting": true + }, + "tier": "advanced", + "element": "lightning", + "type": "construct", + "cost": 60, + "chargeup": 0, + "cooldown": 150, + "base_properties": { + "duration": 600, + "range": 10, + "damage": 8, + "effect_radius": 3 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/spells/stormcloud.png b/src/main/resources/assets/ebwizardry/textures/spells/stormcloud.png new file mode 100644 index 0000000000000000000000000000000000000000..27340d0b80a40fb3fc113c2904b2f8eaf449c30e GIT binary patch literal 5816 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 literal 0 HcmV?d00001 From 30f2a99a2c07d1f5d49e32e2b75c07221682479b Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 19 May 2020 22:08:36 +0100 Subject: [PATCH 117/415] Not sure what was going on here --- .../loot_tables/entities/evil_wizard.json | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/resources/assets/ebwizardry/loot_tables/entities/evil_wizard.json b/src/main/resources/assets/ebwizardry/loot_tables/entities/evil_wizard.json index e769c78f..70d02ff4 100644 --- a/src/main/resources/assets/ebwizardry/loot_tables/entities/evil_wizard.json +++ b/src/main/resources/assets/ebwizardry/loot_tables/entities/evil_wizard.json @@ -9,12 +9,14 @@ "name": "ebwizardry:magic_crystal", "weight": 1, "functions": [ - { - "function": "set_count", - "count": { - "min": 3, - "max": 5 - }, + { + "function": "set_count", + "count": { + "min": 3, + "max": 5 + } + }, + { "function": "looting_enchant", "count": { "min": 0, From 1275f1d31faf0e951efe1527ca8674471aa76a7d Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 21 May 2020 18:40:01 +0100 Subject: [PATCH 118/415] Add remnants --- .../wizardry/block/BlockReceptacle.java | 2 +- .../wizardry/client/ClientProxy.java | 1 + .../wizardry/client/model/ModelRemnant.java | 48 +++ .../client/renderer/RenderRemnant.java | 75 ++++ .../wizardry/entity/living/EntityRemnant.java | 332 ++++++++++++++++++ .../wizardry/registry/WizardryEntities.java | 1 + .../wizardry/registry/WizardrySounds.java | 6 + .../assets/ebwizardry/lang/en_gb.lang | 1 + .../assets/ebwizardry/lang/en_us.lang | 1 + .../loot_tables/entities/remnant/earth.json | 28 ++ .../loot_tables/entities/remnant/fire.json | 28 ++ .../loot_tables/entities/remnant/healing.json | 28 ++ .../loot_tables/entities/remnant/ice.json | 28 ++ .../entities/remnant/lightning.json | 28 ++ .../entities/remnant/necromancy.json | 28 ++ .../loot_tables/entities/remnant/sorcery.json | 28 ++ .../resources/assets/ebwizardry/sounds.json | 3 + .../ebwizardry/sounds/remnant_death.ogg | Bin 0 -> 73046 bytes .../assets/ebwizardry/sounds/remnant_hiss.ogg | Bin 0 -> 48900 bytes .../assets/ebwizardry/sounds/remnant_hurt.ogg | Bin 0 -> 50831 bytes .../ebwizardry/textures/entity/remnant.png | Bin 0 -> 8496 bytes 21 files changed, 665 insertions(+), 1 deletion(-) create mode 100644 src/main/java/electroblob/wizardry/client/model/ModelRemnant.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderRemnant.java create mode 100644 src/main/java/electroblob/wizardry/entity/living/EntityRemnant.java create mode 100644 src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/earth.json create mode 100644 src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/fire.json create mode 100644 src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/healing.json create mode 100644 src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/ice.json create mode 100644 src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/lightning.json create mode 100644 src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/necromancy.json create mode 100644 src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/sorcery.json create mode 100644 src/main/resources/assets/ebwizardry/sounds/remnant_death.ogg create mode 100644 src/main/resources/assets/ebwizardry/sounds/remnant_hiss.ogg create mode 100644 src/main/resources/assets/ebwizardry/sounds/remnant_hurt.ogg create mode 100644 src/main/resources/assets/ebwizardry/textures/entity/remnant.png diff --git a/src/main/java/electroblob/wizardry/block/BlockReceptacle.java b/src/main/java/electroblob/wizardry/block/BlockReceptacle.java index 72983785..7f4dc2f0 100644 --- a/src/main/java/electroblob/wizardry/block/BlockReceptacle.java +++ b/src/main/java/electroblob/wizardry/block/BlockReceptacle.java @@ -40,7 +40,7 @@ public class BlockReceptacle extends BlockTorch implements ITileEntityProvider { private static final double WALL_PARTICLE_OFFSET = 3/16d; - private static final Map PARTICLE_COLOURS; + public static final Map PARTICLE_COLOURS; static { diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index ddb07c7b..7ab6fc2a 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -746,6 +746,7 @@ public class ClientProxy extends CommonProxy { RenderingRegistry.registerEntityRenderingHandler(EntityWizard.class, RenderWizard::new); RenderingRegistry.registerEntityRenderingHandler(EntityEvilWizard.class, RenderEvilWizard::new); RenderingRegistry.registerEntityRenderingHandler(EntityDecoy.class, RenderDecoy::new); + RenderingRegistry.registerEntityRenderingHandler(EntityRemnant.class, RenderRemnant::new); // Throwables RenderingRegistry.registerEntityRenderingHandler(EntitySparkBomb.class, diff --git a/src/main/java/electroblob/wizardry/client/model/ModelRemnant.java b/src/main/java/electroblob/wizardry/client/model/ModelRemnant.java new file mode 100644 index 00000000..ec9d74d2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelRemnant.java @@ -0,0 +1,48 @@ +package electroblob.wizardry.client.model; + +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.MathHelper; + +/** Based on ModelEnderCrystal */ +public class ModelRemnant extends ModelBase { + + private final ModelRenderer cube = new ModelRenderer(this, "cube"); + + public ModelRemnant(){ + this.cube.setTextureOffset(0, 0).addBox(-4.0F, -4.0F, -4.0F, 8, 8, 8); + } + + @Override + public void render(Entity entity, float rotationSpeed, float expandFraction, float age, float netHeadYaw, float headPitch, float scale){ + + GlStateManager.pushMatrix(); + +// GlStateManager.scale(2.0F, 2.0F, 2.0F); +// GlStateManager.translate(0.0F, -0.5F, 0.0F); + + GlStateManager.disableCull(); + + GlStateManager.translate(0.0F, entity.height/2, 0.0F); + + float s = expandFraction + MathHelper.sin(age * 0.1f) * 0.06f; + + GlStateManager.scale(s, s, s); + + GlStateManager.rotate(age * rotationSpeed/2, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F); + GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F); + this.cube.render(scale); + + GlStateManager.scale(0.875F, 0.875F, 0.875F); + GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F); + GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F); + this.cube.render(scale); + + GlStateManager.enableCull(); + + GlStateManager.popMatrix(); + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderRemnant.java b/src/main/java/electroblob/wizardry/client/renderer/RenderRemnant.java new file mode 100644 index 00000000..4275c41a --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderRemnant.java @@ -0,0 +1,75 @@ +package electroblob.wizardry.client.renderer; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.model.ModelRemnant; +import electroblob.wizardry.entity.living.EntityRemnant; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.MathHelper; + +import javax.annotation.Nullable; + +public class RenderRemnant extends Render { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/remnant.png"); + + private final ModelRemnant model; + + public RenderRemnant(RenderManager manager){ + super(manager); + model = new ModelRemnant(); + } + + @Override + public void doRender(EntityRemnant entity, double x, double y, double z, float entityYaw, float partialTicks){ + + float age = (float)entity.ticksExisted + partialTicks; + GlStateManager.pushMatrix(); + GlStateManager.translate((float)x, (float)y, (float)z); + this.bindTexture(TEXTURE); +// float whatIsThis = MathHelper.sin(age * 0.2F) / 2.0F + 0.5F; +// whatIsThis = whatIsThis * whatIsThis + whatIsThis; + + if(this.renderOutlines){ + GlStateManager.enableColorMaterial(); + GlStateManager.enableOutlineMode(this.getTeamColor(entity)); + } + + float expansion = 0.9f; + + if(entity.deathTime > 0){ + float f = (entity.deathTime + partialTicks) * 0.25f; + f = 1 - (1 / (f + 1)); + expansion -= f * 0.75f; + + }else if(entity.hurtTime > 0){ + float f = (entity.hurtTime - partialTicks) / entity.maxHurtTime; + // Neat bit of maths borrowed from the camera tilt effect when hurt + f = MathHelper.sin(f * f * f * f * (float)Math.PI); + expansion += f * 0.2f; + } + +// if(entity.isAttacking()) s -= 0.4f; + float rotationSpeed = entity.isAttacking() ? 10 : 3; + + this.model.render(entity, rotationSpeed, expansion, age, 0, 0, 0.0625f); + + if(this.renderOutlines){ + GlStateManager.disableOutlineMode(); + GlStateManager.disableColorMaterial(); + } + + GlStateManager.popMatrix(); + + super.doRender(entity, x, y, z, entityYaw, partialTicks); + } + + @Nullable + @Override + protected ResourceLocation getEntityTexture(EntityRemnant entity){ + return TEXTURE; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityRemnant.java b/src/main/java/electroblob/wizardry/entity/living/EntityRemnant.java new file mode 100644 index 00000000..35efc0ee --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityRemnant.java @@ -0,0 +1,332 @@ +package electroblob.wizardry.entity.living; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.block.BlockReceptacle; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.ParticleBuilder; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.*; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTUtil; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.util.DamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class EntityRemnant extends EntityMob { + + /** Data parameter for the remnant's element. */ + private static final DataParameter ELEMENT = EntityDataManager.createKey(EntityRemnant.class, DataSerializers.VARINT); + /** Data parameter that tracks whether the remnant is currently attacking (charging). */ + private static final DataParameter ATTACKING = EntityDataManager.createKey(EntityRemnant.class, DataSerializers.BOOLEAN); + + private ResourceLocation lootTable; + + @Nullable + private BlockPos boundOrigin; + + public EntityRemnant(World world){ + super(world); + this.setSize(0.8f, 0.8f); + this.isImmuneToFire = true; + this.moveHelper = new EntityRemnant.AIMoveControl(this); + this.experienceValue = 8; + } + + @Override + protected void entityInit(){ + super.entityInit(); + this.dataManager.register(ELEMENT, 1); // Default to fire + this.dataManager.register(ATTACKING, false); // Default to fire + } + + @Override + protected void initEntityAI(){ + super.initEntityAI(); + this.tasks.addTask(0, new EntityAISwimming(this)); + this.tasks.addTask(4, new EntityRemnant.AIChargeAttack()); + this.tasks.addTask(8, new EntityRemnant.AIMoveRandom()); + this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 3.0F, 1.0F)); + this.tasks.addTask(10, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, EntityRemnant.class)); + this.targetTasks.addTask(3, new EntityAINearestAttackableTarget<>(this, EntityPlayer.class, true)); + } + + @Override + protected void applyEntityAttributes(){ + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(16); + this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(4); + } + + @Nullable + @Override + public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata){ + this.setElement(Element.values()[1 + rand.nextInt(Element.values().length - 1)]); // Exclude MAGIC + this.setBoundOrigin(new BlockPos(this)); + return super.onInitialSpawn(difficulty, livingdata); + } + + public Element getElement(){ + return Element.values()[this.dataManager.get(ELEMENT)]; + } + + public void setElement(Element element){ + this.dataManager.set(ELEMENT, element.ordinal()); + this.lootTable = new ResourceLocation(Wizardry.MODID, "entities/remnant/" + element.getName()); + } + + public boolean isAttacking(){ + return this.dataManager.get(ATTACKING); + } + + public void setAttacking(boolean attacking){ + this.dataManager.set(ATTACKING, attacking); + } + + @Nullable + public BlockPos getBoundOrigin(){ + return this.boundOrigin; + } + + public void setBoundOrigin(@Nullable BlockPos boundOriginIn){ + this.boundOrigin = boundOriginIn; + } + + @Override + protected boolean isValidLightLevel(){ + return true; + } + + @Override + public void onUpdate(){ + + // Use the same trick as EntityVex to fly through stuff + this.noClip = true; + super.onUpdate(); + this.noClip = false; + + this.setNoGravity(true); + + if(world.isRemote){ + + Vec3d centre = this.getPositionVector().add(0, height/2, 0); + + int[] colours = BlockReceptacle.PARTICLE_COLOURS.get(this.getElement()); + + if(rand.nextInt(10) == 0){ + ParticleBuilder.create(ParticleBuilder.Type.FLASH).entity(this).pos(0, height/2, 0).scale(width).time(48).clr(colours[0]).spawn(world); + } + + double r = width/3; + + double x = r * (rand.nextDouble() * 2 - 1); + double y = r * (rand.nextDouble() * 2 - 1); + double z = r * (rand.nextDouble() * 2 - 1); + + if(this.deathTime > 0){ + // Spew out particles on death + for(int i = 0; i < 8; i++){ + ParticleBuilder.create(ParticleBuilder.Type.DUST, rand, centre.x + x, centre.y + y, centre.z + z, 0.1, true) + .time(12).clr(colours[1]).fade(colours[2]).spawn(world); + } + }else{ + ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(centre.x + x, centre.y + y, centre.z + z) + .vel(x * -0.03, 0.02, z * -0.03).time(24 + rand.nextInt(8)).clr(colours[1]).fade(colours[2]).spawn(world); + } + } + + } + + @Override + protected SoundEvent getAmbientSound(){ + return WizardrySounds.ENTITY_REMNANT_AMBIENT; + } + + @Override + protected SoundEvent getDeathSound(){ + return WizardrySounds.ENTITY_REMNANT_DEATH; + } + + @Override + protected SoundEvent getHurtSound(DamageSource source){ + return WizardrySounds.ENTITY_REMNANT_HURT; + } + + @Nullable + @Override + protected ResourceLocation getLootTable(){ + return lootTable; + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbt){ + super.readEntityFromNBT(nbt); + this.setElement(Element.values()[nbt.getInteger("Element")]); + if(nbt.hasKey("BoundOrigin")) boundOrigin = NBTUtil.getPosFromTag(nbt.getCompoundTag("BoundOrigin")); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbt){ + super.writeEntityToNBT(nbt); + nbt.setInteger("Element", this.getElement().ordinal()); + if(boundOrigin != null) nbt.setTag("BoundOrigin", NBTUtil.createPosTag(boundOrigin)); + } + + // AI classes (copied from EntityVex) + + class AIChargeAttack extends EntityAIBase { + + public AIChargeAttack(){ + this.setMutexBits(1); + } + + @Override + public boolean shouldExecute(){ + if(EntityRemnant.this.getAttackTarget() != null && !EntityRemnant.this.getMoveHelper().isUpdating() && EntityRemnant.this.rand.nextInt(7) == 0){ + return EntityRemnant.this.getDistanceSq(EntityRemnant.this.getAttackTarget()) > 4.0D; + }else{ + return false; + } + } + + @Override + public boolean shouldContinueExecuting(){ + return EntityRemnant.this.getMoveHelper().isUpdating() && EntityRemnant.this.isAttacking() && EntityRemnant.this.getAttackTarget() != null && EntityRemnant.this.getAttackTarget().isEntityAlive(); + } + + @Override + public void startExecuting(){ + EntityLivingBase entitylivingbase = EntityRemnant.this.getAttackTarget(); + Vec3d vec3d = entitylivingbase.getPositionEyes(1.0F); + EntityRemnant.this.moveHelper.setMoveTo(vec3d.x, vec3d.y, vec3d.z, 1.0D); + EntityRemnant.this.setAttacking(true); +// EntityRemnant.this.playSound(SoundEvents.ENTITY_VEX_CHARGE, 1.0F, 1.0F); + } + + @Override + public void resetTask(){ + EntityRemnant.this.setAttacking(false); + } + + @Override + public void updateTask(){ + + EntityLivingBase entitylivingbase = EntityRemnant.this.getAttackTarget(); + + if(EntityRemnant.this.getEntityBoundingBox().intersects(entitylivingbase.getEntityBoundingBox())){ + EntityRemnant.this.attackEntityAsMob(entitylivingbase); + EntityRemnant.this.setAttacking(false); + }else{ + double d0 = EntityRemnant.this.getDistanceSq(entitylivingbase); + + if(d0 < 9.0D){ + Vec3d vec3d = entitylivingbase.getPositionEyes(1.0F); + EntityRemnant.this.moveHelper.setMoveTo(vec3d.x, vec3d.y, vec3d.z, 1.0D); + } + } + } + } + + class AIMoveControl extends EntityMoveHelper { + + public AIMoveControl(EntityRemnant host){ + super(host); + } + + @Override + public void onUpdateMoveHelper(){ + + if(this.action == EntityMoveHelper.Action.MOVE_TO){ + + double d0 = this.posX - EntityRemnant.this.posX; + double d1 = this.posY - EntityRemnant.this.posY; + double d2 = this.posZ - EntityRemnant.this.posZ; + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + + d3 = MathHelper.sqrt(d3); + + if(d3 < EntityRemnant.this.getEntityBoundingBox().getAverageEdgeLength()){ + + this.action = EntityMoveHelper.Action.WAIT; + EntityRemnant.this.motionX *= 0.5D; + EntityRemnant.this.motionY *= 0.5D; + EntityRemnant.this.motionZ *= 0.5D; + + }else{ + + EntityRemnant.this.motionX += d0 / d3 * 0.05D * this.speed; + EntityRemnant.this.motionY += d1 / d3 * 0.05D * this.speed; + EntityRemnant.this.motionZ += d2 / d3 * 0.05D * this.speed; + + if(EntityRemnant.this.getAttackTarget() == null){ + EntityRemnant.this.rotationYaw = -((float)MathHelper.atan2(EntityRemnant.this.motionX, EntityRemnant.this.motionZ)) * (180F / (float)Math.PI); + }else{ + double d4 = EntityRemnant.this.getAttackTarget().posX - EntityRemnant.this.posX; + double d5 = EntityRemnant.this.getAttackTarget().posZ - EntityRemnant.this.posZ; + EntityRemnant.this.rotationYaw = -((float)MathHelper.atan2(d4, d5)) * (180F / (float)Math.PI); + } + + EntityRemnant.this.renderYawOffset = EntityRemnant.this.rotationYaw; + } + } + } + } + + class AIMoveRandom extends EntityAIBase { + + public AIMoveRandom(){ + this.setMutexBits(1); + } + + @Override + public boolean shouldExecute(){ + return !EntityRemnant.this.getMoveHelper().isUpdating() && EntityRemnant.this.rand.nextInt(7) == 0; + } + + @Override + public boolean shouldContinueExecuting(){ + return false; + } + + @Override + public void updateTask(){ + + BlockPos blockpos = EntityRemnant.this.getBoundOrigin(); + + if(blockpos == null){ + blockpos = new BlockPos(EntityRemnant.this); + } + + for(int i = 0; i < 3; ++i){ + BlockPos blockpos1 = blockpos.add(EntityRemnant.this.rand.nextInt(15) - 7, EntityRemnant.this.rand.nextInt(11) - 5, EntityRemnant.this.rand.nextInt(15) - 7); + + if(EntityRemnant.this.world.isAirBlock(blockpos1)){ + EntityRemnant.this.moveHelper.setMoveTo((double)blockpos1.getX() + 0.5D, (double)blockpos1.getY() + 0.5D, (double)blockpos1.getZ() + 0.5D, 0.25D); + + if(EntityRemnant.this.getAttackTarget() == null){ + EntityRemnant.this.getLookHelper().setLookPosition((double)blockpos1.getX() + 0.5D, (double)blockpos1.getY() + 0.5D, (double)blockpos1.getZ() + 0.5D, 180.0F, 20.0F); + } + + break; + } + } + } + } + +} diff --git a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java index 8324e692..b2c70894 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java @@ -103,6 +103,7 @@ public class WizardryEntities { .spawn(EnumCreatureType.MONSTER, Wizardry.settings.evilWizardSpawnRate, 1, 1, ForgeRegistries.BIOMES.getValuesCollection().stream() .filter(b -> !Arrays.asList(Wizardry.settings.mobSpawnBiomeBlacklist).contains(b.getRegistryName())) .collect(Collectors.toSet())).build()); + registry.register(createEntry(EntityRemnant.class, "remnant", TrackingType.LIVING).egg(0xe4c7cd, 0x9d2cf3).build()); // Directed projectiles registry.register(createEntry(EntityMagicMissile.class, "magic_missile", TrackingType.PROJECTILE).build()); diff --git a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java index 9f2538c4..adf6eba4 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java +++ b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java @@ -75,6 +75,9 @@ public final class WizardrySounds { public static final SoundEvent ENTITY_PHOENIX_FLAP = createSound("entity.phoenix.flap"); public static final SoundEvent ENTITY_PHOENIX_HURT = createSound("entity.phoenix.hurt"); public static final SoundEvent ENTITY_PHOENIX_DEATH = createSound("entity.phoenix.death"); + public static final SoundEvent ENTITY_REMNANT_AMBIENT = createSound("entity.remnant.ambient"); + public static final SoundEvent ENTITY_REMNANT_HURT = createSound("entity.remnant.hurt"); + public static final SoundEvent ENTITY_REMNANT_DEATH = createSound("entity.remnant.death"); public static final SoundEvent ENTITY_SHADOW_WRAITH_AMBIENT = createSound("entity.shadow_wraith.ambient"); public static final SoundEvent ENTITY_SHADOW_WRAITH_NOISE = createSound("entity.shadow_wraith.noise"); public static final SoundEvent ENTITY_SHADOW_WRAITH_HURT = createSound("entity.shadow_wraith.hurt"); @@ -207,6 +210,9 @@ public final class WizardrySounds { event.getRegistry().register(ENTITY_PHOENIX_FLAP); event.getRegistry().register(ENTITY_PHOENIX_HURT); event.getRegistry().register(ENTITY_PHOENIX_DEATH); + event.getRegistry().register(ENTITY_REMNANT_AMBIENT); + event.getRegistry().register(ENTITY_REMNANT_HURT); + event.getRegistry().register(ENTITY_REMNANT_DEATH); event.getRegistry().register(ENTITY_SHADOW_WRAITH_AMBIENT); event.getRegistry().register(ENTITY_SHADOW_WRAITH_NOISE); event.getRegistry().register(ENTITY_SHADOW_WRAITH_HURT); diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 086a6d18..d8982789 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -445,6 +445,7 @@ entity.ebwizardry\:storm_elemental.name=Storm Elemental entity.ebwizardry\:evil_wizard.name=Wizard entity.ebwizardry\:decoy.name=Decoy entity.ebwizardry\:vex_minion.name=Vex +entity.ebwizardry\:remnant.name=Remnant entity.ebwizardry\:magic_missile.name=Magic entity.ebwizardry\:arc.name=Magic diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 5fa24eb1..5c21e185 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -445,6 +445,7 @@ entity.ebwizardry\:storm_elemental.name=Storm Elemental entity.ebwizardry\:evil_wizard.name=Wizard entity.ebwizardry\:decoy.name=Decoy entity.ebwizardry\:vex_minion.name=Vex +entity.ebwizardry\:remnant.name=Remnant entity.ebwizardry\:magic_missile.name=Magic entity.ebwizardry\:arc.name=Magic diff --git a/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/earth.json b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/earth.json new file mode 100644 index 00000000..ceee3c72 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/earth.json @@ -0,0 +1,28 @@ +{ + "pools": [ + { + "name": "spectral_dust", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "ebwizardry:spectral_dust", + "weight": 1, + "functions": [ + { + "function": "set_count", + "count": { + "min": 0, + "max": 1 + } + }, + { + "function": "minecraft:set_data", + "data": 5 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/fire.json b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/fire.json new file mode 100644 index 00000000..30f1446d --- /dev/null +++ b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/fire.json @@ -0,0 +1,28 @@ +{ + "pools": [ + { + "name": "spectral_dust", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "ebwizardry:spectral_dust", + "weight": 1, + "functions": [ + { + "function": "set_count", + "count": { + "min": 0, + "max": 1 + } + }, + { + "function": "minecraft:set_data", + "data": 1 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/healing.json b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/healing.json new file mode 100644 index 00000000..876cb55b --- /dev/null +++ b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/healing.json @@ -0,0 +1,28 @@ +{ + "pools": [ + { + "name": "spectral_dust", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "ebwizardry:spectral_dust", + "weight": 1, + "functions": [ + { + "function": "set_count", + "count": { + "min": 0, + "max": 1 + } + }, + { + "function": "minecraft:set_data", + "data": 7 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/ice.json b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/ice.json new file mode 100644 index 00000000..61a1d59f --- /dev/null +++ b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/ice.json @@ -0,0 +1,28 @@ +{ + "pools": [ + { + "name": "spectral_dust", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "ebwizardry:spectral_dust", + "weight": 1, + "functions": [ + { + "function": "set_count", + "count": { + "min": 0, + "max": 1 + } + }, + { + "function": "minecraft:set_data", + "data": 2 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/lightning.json b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/lightning.json new file mode 100644 index 00000000..6a4d5dec --- /dev/null +++ b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/lightning.json @@ -0,0 +1,28 @@ +{ + "pools": [ + { + "name": "spectral_dust", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "ebwizardry:spectral_dust", + "weight": 1, + "functions": [ + { + "function": "set_count", + "count": { + "min": 0, + "max": 1 + } + }, + { + "function": "minecraft:set_data", + "data": 3 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/necromancy.json b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/necromancy.json new file mode 100644 index 00000000..0064048f --- /dev/null +++ b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/necromancy.json @@ -0,0 +1,28 @@ +{ + "pools": [ + { + "name": "spectral_dust", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "ebwizardry:spectral_dust", + "weight": 1, + "functions": [ + { + "function": "set_count", + "count": { + "min": 0, + "max": 1 + } + }, + { + "function": "minecraft:set_data", + "data": 4 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/sorcery.json b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/sorcery.json new file mode 100644 index 00000000..8c9f9e8d --- /dev/null +++ b/src/main/resources/assets/ebwizardry/loot_tables/entities/remnant/sorcery.json @@ -0,0 +1,28 @@ +{ + "pools": [ + { + "name": "spectral_dust", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "ebwizardry:spectral_dust", + "weight": 1, + "functions": [ + { + "function": "set_count", + "count": { + "min": 0, + "max": 1 + } + }, + { + "function": "minecraft:set_data", + "data": 6 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/sounds.json b/src/main/resources/assets/ebwizardry/sounds.json index 1078a058..16307596 100644 --- a/src/main/resources/assets/ebwizardry/sounds.json +++ b/src/main/resources/assets/ebwizardry/sounds.json @@ -51,6 +51,9 @@ "entity.phoenix.flap": {"category": "hostile", "sounds": ["mob/enderdragon/wings1", "mob/enderdragon/wings2", "mob/enderdragon/wings3", "mob/enderdragon/wings4", "mob/enderdragon/wings5", "mob/enderdragon/wings6"]}, "entity.phoenix.hurt": {"category": "hostile", "sounds": ["mob/blaze/hit1", "mob/blaze/hit2", "mob/blaze/hit3", "mob/blaze/hit4"]}, "entity.phoenix.death": {"category": "hostile", "sounds": ["mob/blaze/death"]}, + "entity.remnant.ambient": {"category": "hostile", "sounds": ["ebwizardry:remnant_hiss"]}, + "entity.remnant.hurt": {"category": "hostile", "sounds": ["ebwizardry:remnant_hurt"]}, + "entity.remnant.death": {"category": "hostile", "sounds": ["ebwizardry:remnant_death"]}, "entity.shadow_wraith.ambient": {"category": "hostile", "sounds": ["mob/blaze/breathe1", "mob/blaze/breathe2", "mob/blaze/breathe3", "mob/blaze/breathe4"]}, "entity.shadow_wraith.noise": {"category": "hostile", "sounds": ["portal/portal"]}, "entity.shadow_wraith.hurt": {"category": "hostile", "sounds": ["mob/blaze/hit1", "mob/blaze/hit2", "mob/blaze/hit3", "mob/blaze/hit4"]}, diff --git a/src/main/resources/assets/ebwizardry/sounds/remnant_death.ogg b/src/main/resources/assets/ebwizardry/sounds/remnant_death.ogg new file mode 100644 index 0000000000000000000000000000000000000000..4607b82b2bc1e2b0f3b787229b621d286afd4f06 GIT binary patch literal 73046 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