Add imbuement altar

- Adds imbuement altar block and associated tile entity
- Adds basic functionality for imbuing wizard armour (other stuff to be decided)
- Incorporates imbuement altar into glowing overlay model event (this could do with being tidied up and generalised)
- Adds a TESR for the ray effect (based on some of the oldest code in the entire mod, which definitely needs improving!)
- Adds a simple sound effect for the imbuement process, needs upgrading to a ticked sound later
This commit is contained in:
Electroblob77
2020-06-17 23:44:02 +01:00
parent 99d0d115c7
commit 8e8e249426
25 changed files with 619 additions and 7 deletions
@@ -0,0 +1,168 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
import electroblob.wizardry.tileentity.TileEntityImbuementAltar;
import net.minecraft.block.Block;
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.player.EntityPlayer;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumBlockRenderType;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import java.util.Arrays;
public class BlockImbuementAltar extends Block implements ITileEntityProvider {
public static final PropertyBool ACTIVE = PropertyBool.create("active");
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0, 0.0, 0.0, 1.0, 0.75, 1.0);
public BlockImbuementAltar(){
super(Material.ROCK);
this.setBlockUnbreakable();
this.setResistance(6000000);
this.setLightLevel(0.4f);
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setDefaultState(this.blockState.getBaseState().withProperty(ACTIVE, false));
}
@Override
protected BlockStateContainer createBlockState(){
return new BlockStateContainer(this, ACTIVE);
}
@Override
public IBlockState getStateFromMeta(int meta){
return this.getDefaultState().withProperty(ACTIVE, meta == 1);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(ACTIVE) ? 1 : 0;
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return AABB;
}
@Override
public TileEntity createNewTileEntity(World world, int metadata){
return new TileEntityImbuementAltar();
}
@Override
public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos){
return false;
}
@Override
public EnumBlockRenderType getRenderType(IBlockState state){
return EnumBlockRenderType.MODEL;
}
@Override
public BlockRenderLayer getRenderLayer(){
return BlockRenderLayer.CUTOUT; // Required to shade parts of the block faces differently to others
}
@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){
// TODO: Change this back to how it should be and give receptacles a special case for attaching to the altar
return face == EnumFacing.UP ? BlockFaceShape.UNDEFINED : BlockFaceShape.SOLID;
}
@Override
public int getLightValue(IBlockState state, IBlockAccess world, BlockPos pos){
return state.getValue(ACTIVE) ? super.getLightValue(state, world, pos) : 0;
}
@Override
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos neighbour){
boolean shouldBeActive = Arrays.stream(EnumFacing.HORIZONTALS)
.allMatch(s -> world.getBlockState(pos.offset(s)).getBlock() == WizardryBlocks.receptacle
&& world.getBlockState(pos.offset(s)).getValue(BlockReceptacle.FACING) == s);
if(world.getBlockState(pos).getValue(ACTIVE) != shouldBeActive){ // Only set when it actually needs changing
world.setBlockState(pos, world.getBlockState(pos).withProperty(ACTIVE, shouldBeActive));
world.checkLight(pos);
}
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityImbuementAltar){
((TileEntityImbuementAltar)tileEntity).checkRecipe();
}
}
@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 instanceof TileEntityImbuementAltar) || player.isSneaking()){
return false;
}
ItemStack currentStack = ((TileEntityImbuementAltar)tileEntity).getStack();
ItemStack toInsert = player.getHeldItem(hand);
if(currentStack.isEmpty()){
ItemStack stack = toInsert.copy();
stack.setCount(1);
((TileEntityImbuementAltar)tileEntity).setStack(stack);
if(!player.isCreative()) toInsert.shrink(1);
}else{
if(toInsert.isEmpty()){
player.setHeldItem(hand, currentStack);
}else if(!player.addItemStackToInventory(currentStack)){
player.dropItem(currentStack, false);
}
((TileEntityImbuementAltar)tileEntity).setStack(ItemStack.EMPTY);
}
return true;
}
@Override
public void breakBlock(World world, BlockPos pos, IBlockState block){
TileEntity tileentity = world.getTileEntity(pos);
if(tileentity instanceof TileEntityArcaneWorkbench){
InventoryHelper.dropInventoryItems(world, pos, (TileEntityArcaneWorkbench)tileentity);
}
super.breakBlock(world, pos, block);
}
}
@@ -141,7 +141,6 @@ public class BlockReceptacle extends BlockTorch implements ITileEntityProvider {
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;
}
@@ -157,7 +156,6 @@ public class BlockReceptacle extends BlockTorch implements ITileEntityProvider {
player.dropItem(dust, false);
}
world.checkLight(pos);
return true;
}
}
@@ -20,10 +20,7 @@ import electroblob.wizardry.client.particle.ParticleWizardry.IWizardryParticleFa
import electroblob.wizardry.client.renderer.entity.*;
import electroblob.wizardry.client.renderer.entity.layers.*;
import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect;
import electroblob.wizardry.client.renderer.tileentity.RenderArcaneWorkbench;
import electroblob.wizardry.client.renderer.tileentity.RenderLectern;
import electroblob.wizardry.client.renderer.tileentity.RenderMagicLight;
import electroblob.wizardry.client.renderer.tileentity.RenderStatue;
import electroblob.wizardry.client.renderer.tileentity.*;
import electroblob.wizardry.command.SpellEmitter;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.data.DispenserCastingData;
@@ -848,6 +845,7 @@ public class ClientProxy extends CommonProxy {
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityStatue.class, renderStatue = new RenderStatue());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityMagicLight.class, new RenderMagicLight());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityLectern.class, new RenderLectern());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityImbuementAltar.class, new RenderImbuementAltar());
}
}
@@ -89,6 +89,7 @@ public final class WizardryModels {
registerItemModel(Item.getItemFromBlock(WizardryBlocks.dark_oak_lectern));
registerItemModel(Item.getItemFromBlock(WizardryBlocks.receptacle));
registerItemModel(Item.getItemFromBlock(WizardryBlocks.imbuement_altar));
// Items
@@ -314,7 +315,8 @@ public final class WizardryModels {
IBakedModel original = event.getModelRegistry().getObject(location);
if(location.getPath().contains("runestone") || location.getPath().contains("runestone_pedestal")){
if(location.getPath().contains("runestone") || location.getPath().contains("runestone_pedestal")
|| location.getPath().endsWith("imbuement_altar")){ // Ends with to exclude inactive version
event.getModelRegistry().putObject(location, new BakedModelGlowingOverlay(original, "overlay"));
}else if(location.getPath().contains("spectral_block")){
event.getModelRegistry().putObject(location, new BakedModelGlowingOverlay(original, "spectral_block"));
@@ -0,0 +1,138 @@
package electroblob.wizardry.client.renderer.tileentity;
import electroblob.wizardry.block.BlockReceptacle;
import electroblob.wizardry.tileentity.TileEntityImbuementAltar;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.MathHelper;
import org.lwjgl.opengl.GL11;
import java.util.Random;
public class RenderImbuementAltar extends TileEntitySpecialRenderer<TileEntityImbuementAltar> {
public RenderImbuementAltar(){}
@Override
public void render(TileEntityImbuementAltar tileentity, double x, double y, double z, float partialTicks, int destroyStage, float alpha){
GlStateManager.pushMatrix();
GlStateManager.translate((float)x + 0.5F, (float)y + 1.4F, (float)z + 0.5F);
GlStateManager.rotate(180, 0F, 0F, 1F);
float t = (getWorld().getTotalWorldTime() + partialTicks);
GlStateManager.translate(0, 0.05f * MathHelper.sin(t/15), 0);
this.renderItem(tileentity, t);
this.renderRays(tileentity, partialTicks);
GlStateManager.popMatrix();
}
private void renderItem(TileEntityImbuementAltar tileentity, float t){
ItemStack stack = tileentity.getStack();
if(!stack.isEmpty()){
GlStateManager.pushMatrix();
GlStateManager.rotate(180, 1, 0, 0);
GlStateManager.rotate(t, 0, 1, 0);
GlStateManager.scale(0.85F, 0.85F, 0.85F);
Minecraft.getMinecraft().getRenderItem().renderItem(stack, TransformType.FIXED);
GlStateManager.popMatrix();
}
}
private void renderRays(TileEntityImbuementAltar tileentity, float partialTicks){
float t = (getWorld().getTotalWorldTime() + partialTicks);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
Random random = new Random(tileentity.getPos().toLong()); // Use position to get a constant seed
int[] colours = BlockReceptacle.PARTICLE_COLOURS.get(tileentity.getDisplayElement());
if(colours == null) return; // Shouldn't happen
GlStateManager.disableCull();
GlStateManager.enableBlend();
GlStateManager.enableAlpha();
GlStateManager.disableTexture2D();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
GlStateManager.shadeModel(GL11.GL_SMOOTH);
GlStateManager.disableLighting();
GlStateManager.depthMask(false);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
RenderHelper.disableStandardItemLighting();
int r1 = colours[1] >> 16 & 255;
int g1 = colours[1] >> 8 & 255;
int b1 = colours[1] & 255;
int r2 = colours[2] >> 16 & 255;
int g2 = colours[2] >> 8 & 255;
int b2 = colours[2] & 255;
for(int j = 0; j < 30; j++){
int m = random.nextInt(10);
int n = random.nextInt(10);
int sliceAngle = 20 + m;
float scale = 0.5f;
GlStateManager.pushMatrix();
float progress = Math.min(tileentity.getImbuementProgress() + partialTicks/141, 1);
float s = 1 - progress;
s = 1 - s*s;
GlStateManager.scale(s, s, s);
// TODO: This needs optimising! We should easily be able to do this with a single draw() call
// Same for magic light and black hole, which don't need ray textures either!
GlStateManager.rotate(31 * m, 1, 0, 0);
GlStateManager.rotate(31 * n, 0, 0, 1);
buffer.begin(5, DefaultVertexFormats.POSITION_COLOR);
float fade = (Math.min(1, 1.9f - progress) - 0.9f) * 10;
buffer.pos(0, 0, 0).color(r1, g1, b1, (int)(255 * fade)).endVertex();
buffer.pos(0, 0, 0).color(r1, g1, b1, (int)(255 * fade)).endVertex();
double x1 = scale * MathHelper.sin((t + 40 * j) * ((float)Math.PI / 180));
double z1 = scale * MathHelper.cos((t + 40 * j) * ((float)Math.PI / 180));
double x2 = scale * MathHelper.sin((t + 40 * j - sliceAngle) * ((float)Math.PI / 180));
double z2 = scale * MathHelper.cos((t + 40 * j - sliceAngle) * ((float)Math.PI / 180));
buffer.pos(x1, 0, z1).color(r2, g2, b2, 0).endVertex();
buffer.pos(x2, 0, z2).color(r2, g2, b2, 0).endVertex();
tessellator.draw();
GlStateManager.popMatrix();
}
GlStateManager.shadeModel(GL11.GL_FLAT);
GlStateManager.enableCull();
GlStateManager.disableBlend();
GlStateManager.disableAlpha();
GlStateManager.depthMask(true);
GlStateManager.enableTexture2D();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GlStateManager.enableLighting();
RenderHelper.enableStandardItemLighting();
}
}
@@ -77,6 +77,7 @@ public final class WizardryBlocks {
public static final Block dark_oak_lectern = placeholder();
public static final Block receptacle = placeholder();
public static final Block imbuement_altar = placeholder();
/**
* Sets both the registry and unlocalised names of the given block, then registers it with the given registry. Use
@@ -101,6 +102,7 @@ public final class WizardryBlocks {
IForgeRegistry<Block> registry = event.getRegistry();
// TODO: Put everything in block classes wherever possible
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));
@@ -139,6 +141,7 @@ public final class WizardryBlocks {
registerBlock(registry, "dark_oak_lectern", new BlockLectern());
registerBlock(registry, "receptacle", new BlockReceptacle());
registerBlock(registry, "imbuement_altar", new BlockImbuementAltar());
}
@@ -155,5 +158,6 @@ public final class WizardryBlocks {
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"));
GameRegistry.registerTileEntity(TileEntityImbuementAltar.class, new ResourceLocation(Wizardry.MODID, "imbuement_altar"));
}
}
@@ -436,6 +436,7 @@ public final class WizardryItems {
registerItemBlock(registry, WizardryBlocks.dark_oak_lectern);
registerItemBlock(registry, WizardryBlocks.receptacle);
registerItemBlock(registry, WizardryBlocks.imbuement_altar);
// Items
@@ -708,6 +709,7 @@ public final class WizardryItems {
((TileEntityReceptacle)tileEntity).setElement(Element.values()[stack.getMetadata()]);
stack.shrink(1);
world.checkLight(pos);
// TESTME: Do we need this?
world.notifyBlockUpdate(pos, source.getBlockState(), source.getBlockState(), 3);
return stack;
}
@@ -33,6 +33,7 @@ public final class WizardrySounds {
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 BLOCK_IMBUEMENT_ALTAR_IMBUE = createSound("block.imbuement_altar.imbue");
public static final SoundEvent ITEM_WAND_SWITCH_SPELL = createSound("item.wand.switch_spell");
public static final SoundEvent ITEM_WAND_LEVELUP = createSound("item.wand.levelup");
@@ -0,0 +1,204 @@
package electroblob.wizardry.tileentity;
import electroblob.wizardry.block.BlockReceptacle;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.item.IManaStoringItem;
import electroblob.wizardry.item.ItemWizardArmour;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.item.ItemStack;
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.EnumFacing;
import net.minecraft.util.ITickable;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.Vec3d;
import javax.annotation.Nullable;
import java.util.Arrays;
public class TileEntityImbuementAltar extends TileEntity implements ITickable {
private static final int IMBUEMENT_DURATION = 140;
private ItemStack stack;
private int imbuementTimer;
private Element displayElement;
public TileEntityImbuementAltar(){
stack = ItemStack.EMPTY;
}
public void setStack(ItemStack stack){
this.stack = stack;
checkRecipe();
}
public void checkRecipe(){
if(getResult().isEmpty()){
imbuementTimer = 0;
}else if(imbuementTimer == 0){
imbuementTimer = 1;
}else{
return; // Don't sync if nothing changed
}
world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); // Sync
}
public ItemStack getStack(){
return stack;
}
@Override
public void update(){
if(imbuementTimer > 0){
if(imbuementTimer == 1){ // Has to be done here because of syncing
world.playSound(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5,
WizardrySounds.BLOCK_IMBUEMENT_ALTAR_IMBUE, SoundCategory.BLOCKS, 1, 1, false);
}
ItemStack result = getResult();
if(result.isEmpty()){
imbuementTimer = 0;
}else{
if(imbuementTimer++ >= IMBUEMENT_DURATION){
this.stack = result;
consumeReceptacleContents();
imbuementTimer = 0;
displayElement = null;
}
if(world.isRemote && world.rand.nextInt(2) == 0){
Element[] elements = getReceptacleElements();
Vec3d centre = GeometryUtils.getCentre(this.pos.up());
for(int i = 0; i < elements.length; i++){
if(elements[i] == null) continue;
Vec3d offset = new Vec3d(EnumFacing.byHorizontalIndex(i).getDirectionVec());
Vec3d vec = GeometryUtils.getCentre(this.pos).add(0, 0.3, 0).add(offset.scale(0.7));
int[] colours = BlockReceptacle.PARTICLE_COLOURS.get(elements[i]);
ParticleBuilder.create(Type.DUST, world.rand, vec.x, vec.y, vec.z, 0.1, false)
.vel(centre.subtract(vec).scale(0.02)).clr(colours[1]).fade(colours[2]).time(50).spawn(world);
}
}
}
}
}
/** Returns the element to use for the visual ray effect colours, or null if they should not be displayed. */
public Element getDisplayElement(){
return displayElement;
}
/** Returns how complete the current action is (from 0 to 1), or 0 if no action is being performed. */
public float getImbuementProgress(){
return (float)imbuementTimer / IMBUEMENT_DURATION;
}
private ItemStack getResult(){
if(stack.getItem() instanceof ItemWizardArmour && ((ItemWizardArmour)stack.getItem()).element == null){
Element[] elements = getReceptacleElements();
if(Arrays.stream(elements).distinct().count() == 1 && elements[0] != null){ // All the same element
ItemStack result = new ItemStack(WizardryItems.getArmour(elements[0], ((ItemWizardArmour)stack.getItem()).armorType));
displayElement = elements[0];
result.setTagCompound(stack.getTagCompound());
((IManaStoringItem)result.getItem()).setMana(result, ((ItemWizardArmour)stack.getItem()).getMana(stack));
return result;
}
}
displayElement = null;
return ItemStack.EMPTY;
}
/** Returns the elements of the 4 adjacent receptacles, in SWNE order. Null means an empty or missing receptacle. */
private Element[] getReceptacleElements(){
Element[] elements = new Element[4];
for(EnumFacing side : EnumFacing.HORIZONTALS){
TileEntity tileEntity = world.getTileEntity(pos.offset(side));
if(tileEntity instanceof TileEntityReceptacle){
elements[side.getHorizontalIndex()] = ((TileEntityReceptacle)tileEntity).getElement();
}else{
elements[side.getHorizontalIndex()] = null;
}
}
return elements;
}
/** Empties the 4 adjacent receptacles. */
private void consumeReceptacleContents(){
for(EnumFacing side : EnumFacing.HORIZONTALS){
TileEntity tileEntity = world.getTileEntity(pos.offset(side));
if(tileEntity instanceof TileEntityReceptacle){
((TileEntityReceptacle)tileEntity).setElement(null);
}
}
}
@Override
public NBTTagCompound writeToNBT(NBTTagCompound nbt){
super.writeToNBT(nbt);
NBTTagCompound itemTag = new NBTTagCompound();
stack.writeToNBT(itemTag);
nbt.setTag("item", itemTag);
nbt.setInteger("imbuementTimer", imbuementTimer);
return nbt;
}
@Override
public void readFromNBT(NBTTagCompound nbt){
super.readFromNBT(nbt);
NBTTagCompound itemTag = nbt.getCompoundTag("item");
this.stack = new ItemStack(itemTag);
this.imbuementTimer = nbt.getInteger("imbuementTimer");
}
@Override
public NBTTagCompound getUpdateTag(){
return this.writeToNBT(new NBTTagCompound());
}
@Nullable
@Override
public SPacketUpdateTileEntity getUpdatePacket(){
return new SPacketUpdateTileEntity(pos, 0, this.getUpdateTag());
}
@Override
public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt){
readFromNBT(pkt.getNbtCompound());
}
}
@@ -26,6 +26,8 @@ public class TileEntityReceptacle extends TileEntity {
public void setElement(Element element){
this.element = element;
world.notifyNeighborsRespectDebug(pos, blockType, true); // Update altar if connected
world.checkLight(pos);
}
@Override
@@ -0,0 +1,7 @@
{
"forge_marker": 1,
"variants": {
"active=false": { "model": "ebwizardry:imbuement_altar_inactive" },
"active=true": { "model": "ebwizardry:imbuement_altar" }
}
}
@@ -49,6 +49,7 @@ tile.ebwizardry\:acacia_lectern.name=Acacia Lectern
tile.ebwizardry\:dark_oak_lectern.name=Dark Oak Lectern
tile.ebwizardry\:receptacle.name=Receptacle
tile.ebwizardry\:imbuement_altar.name=Imbuement Altar
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?
@@ -49,6 +49,7 @@ tile.ebwizardry\:acacia_lectern.name=Acacia Lectern
tile.ebwizardry\:dark_oak_lectern.name=Dark Oak Lectern
tile.ebwizardry\:receptacle.name=Receptacle
tile.ebwizardry\:imbuement_altar.name=Imbuement Altar
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...
@@ -0,0 +1,36 @@
{
"parent": "block/block",
"textures": {
"particle": "ebwizardry:blocks/imbuement_altar_bottom",
"bottom": "ebwizardry:blocks/imbuement_altar_bottom",
"top": "ebwizardry:blocks/imbuement_altar_top",
"side": "ebwizardry:blocks/imbuement_altar_side",
"top_overlay": "ebwizardry:blocks/imbuement_altar_top_overlay",
"side_overlay": "ebwizardry:blocks/imbuement_altar_side_overlay"
},
"elements": [
{
"from": [ 0, 0, 0 ],
"to": [ 16, 12, 16 ],
"faces": {
"down": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "down" },
"up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" },
"north": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "north" },
"south": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "south" },
"west": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "west" },
"east": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "east" }
}
},
{
"from": [ 0, 0, 0 ],
"to": [ 16, 12, 16 ],
"faces": {
"up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top_overlay" },
"north": { "uv": [ 0, 4, 16, 16 ], "texture": "#side_overlay", "cullface": "north" },
"south": { "uv": [ 0, 4, 16, 16 ], "texture": "#side_overlay", "cullface": "south" },
"west": { "uv": [ 0, 4, 16, 16 ], "texture": "#side_overlay", "cullface": "west" },
"east": { "uv": [ 0, 4, 16, 16 ], "texture": "#side_overlay", "cullface": "east" }
}
}
]
}
@@ -0,0 +1,22 @@
{
"parent": "block/block",
"textures": {
"particle": "ebwizardry:blocks/imbuement_altar_bottom",
"bottom": "ebwizardry:blocks/imbuement_altar_bottom",
"top": "ebwizardry:blocks/imbuement_altar_top",
"side": "ebwizardry:blocks/imbuement_altar_side"
},
"elements": [
{ "from": [ 0, 0, 0 ],
"to": [ 16, 12, 16 ],
"faces": {
"down": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "down" },
"up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" },
"north": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "north" },
"south": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "south" },
"west": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "west" },
"east": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "east" }
}
}
]
}
@@ -0,0 +1,3 @@
{
"parent": "ebwizardry:block/imbuement_altar_inactive"
}
@@ -4,6 +4,7 @@
"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"]},
"block.imbuement_altar.imbue": {"category": "blocks", "sounds": ["ebwizardry:imbue"]},
"item.wand.switch_spell": {"category": "player", "sounds": ["ebwizardry:select"]},
"item.wand.levelup": {"category": "player", "sounds": ["random/levelup"]},
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

@@ -0,0 +1,12 @@
{
"animation": {
"frametime": 40,
"interpolate": true,
"frames": [0, 1, 2, 3]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

@@ -0,0 +1,12 @@
{
"animation": {
"frametime": 40,
"interpolate": true,
"frames": [0, 1, 2, 3]
}
}