vazkii.botania.common.block.tile.TileEnchanter.java Source code

Java tutorial

Introduction

Here is the source code for vazkii.botania.common.block.tile.TileEnchanter.java

Source

/**
 * This class was created by <Vazkii>. It's distributed as
 * part of the Botania Mod. Get the Source Code in github:
 * https://github.com/Vazkii/Botania
 *
 * Botania is Open Source and distributed under the
 * Botania License: http://botaniamod.net/license.php
 *
 * File Created @ [Mar 15, 2014, 4:57:52 PM (GMT)]
 */
package vazkii.botania.common.block.tile;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;

import com.google.common.base.Predicates;

import net.minecraft.block.state.IBlockState;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentData;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import vazkii.botania.api.BotaniaAPI;
import vazkii.botania.api.internal.VanillaPacketDispatcher;
import vazkii.botania.api.lexicon.multiblock.Multiblock;
import vazkii.botania.api.lexicon.multiblock.MultiblockSet;
import vazkii.botania.api.lexicon.multiblock.component.FlowerComponent;
import vazkii.botania.api.mana.IManaPool;
import vazkii.botania.api.mana.spark.ISparkAttachable;
import vazkii.botania.api.mana.spark.ISparkEntity;
import vazkii.botania.api.mana.spark.SparkHelper;
import vazkii.botania.api.sound.BotaniaSoundEvents;
import vazkii.botania.api.state.BotaniaStateProps;
import vazkii.botania.client.core.helper.RenderHelper;
import vazkii.botania.common.block.ModBlocks;
import vazkii.botania.common.network.PacketBotaniaEffect;
import vazkii.botania.common.network.PacketHandler;

public class TileEnchanter extends TileMod implements ISparkAttachable {

    private static final String TAG_STAGE = "stage";
    private static final String TAG_STAGE_TICKS = "stageTicks";
    private static final String TAG_STAGE_3_END_TICKS = "stage3EndTicks";
    private static final String TAG_MANA_REQUIRED = "manaRequired";
    private static final String TAG_MANA = "mana";
    private static final String TAG_ITEM = "item";
    private static final String TAG_ENCHANTS = "enchantsToApply";

    public State stage = State.IDLE;
    public int stageTicks = 0;

    public int stage3EndTicks = 0;

    private int manaRequired = -1;
    private int mana = 0;

    public ItemStack itemToEnchant = null;
    private final List<EnchantmentData> enchants = new ArrayList<>();

    private static final BlockPos[] OBSIDIAN_LOCATIONS = { new BlockPos(0, -1, 0), new BlockPos(0, -1, 1),
            new BlockPos(0, -1, -1), new BlockPos(1, -1, 0), new BlockPos(-1, -1, 0), new BlockPos(0, -1, 2),
            new BlockPos(-1, -1, 2), new BlockPos(1, -1, 2), new BlockPos(0, -1, -2), new BlockPos(-1, -1, -2),
            new BlockPos(1, -1, -2), new BlockPos(2, -1, 0), new BlockPos(2, -1, 1), new BlockPos(2, -1, -1),
            new BlockPos(-2, -1, 0), new BlockPos(-2, -1, 1), new BlockPos(-2, -1, -1) };

    private static final Map<EnumFacing.Axis, BlockPos[]> PYLON_LOCATIONS = new EnumMap<>(EnumFacing.Axis.class);

    static {
        PYLON_LOCATIONS.put(EnumFacing.Axis.X, new BlockPos[] { new BlockPos(-5, 1, 0), new BlockPos(5, 1, 0),
                new BlockPos(-4, 1, 3), new BlockPos(4, 1, 3), new BlockPos(-4, 1, -3), new BlockPos(4, 1, -3) });
        PYLON_LOCATIONS.put(EnumFacing.Axis.Z, new BlockPos[] { new BlockPos(0, 1, -5), new BlockPos(0, 1, 5),
                new BlockPos(3, 1, -4), new BlockPos(3, 1, 4), new BlockPos(-3, 1, -4), new BlockPos(-3, 1, 4) });
    }

    private static final BlockPos[] FLOWER_LOCATIONS = { new BlockPos(-1, 0, -1), new BlockPos(1, 0, -1),
            new BlockPos(-1, 0, 1), new BlockPos(1, 0, 1) };

    public static MultiblockSet makeMultiblockSet() {
        Multiblock mb = new Multiblock();

        for (BlockPos o : OBSIDIAN_LOCATIONS)
            mb.addComponent(o.up(), Blocks.OBSIDIAN.getDefaultState());
        for (BlockPos p : PYLON_LOCATIONS.get(EnumFacing.Axis.X)) {
            mb.addComponent(p.up(), ModBlocks.pylon.getDefaultState());
            mb.addComponent(new FlowerComponent(p, ModBlocks.flower));
        }
        for (BlockPos f : FLOWER_LOCATIONS)
            mb.addComponent(new FlowerComponent(f.up(), ModBlocks.flower));

        mb.addComponent(BlockPos.ORIGIN.up(), Blocks.LAPIS_BLOCK.getDefaultState());

        return mb.makeSet();
    }

    public void onWanded(EntityPlayer player, ItemStack wand) {
        if (stage != State.IDLE || itemToEnchant == null || !itemToEnchant.isItemEnchantable())
            return;

        List<EntityItem> items = worldObj.getEntitiesWithinAABB(EntityItem.class, new AxisAlignedBB(pos.getX() - 2,
                pos.getY(), pos.getZ() - 2, pos.getX() + 3, pos.getY() + 1, pos.getZ() + 3));
        int count = items.size();

        if (count > 0 && !worldObj.isRemote) {
            for (EntityItem entity : items) {
                ItemStack item = entity.getEntityItem();
                if (item.getItem() == Items.ENCHANTED_BOOK) {
                    NBTTagList enchants = Items.ENCHANTED_BOOK.getEnchantments(item);
                    if (enchants != null && enchants.tagCount() > 0) {
                        NBTTagCompound enchant = enchants.getCompoundTagAt(0);
                        short id = enchant.getShort("id");
                        if (isEnchantmentValid(Enchantment.getEnchantmentByID(id))) {
                            advanceStage();
                            return;
                        }
                    }
                }
            }
        }
    }

    @Override
    public void update() {
        IBlockState state = worldObj.getBlockState(getPos());
        EnumFacing.Axis axis = state.getValue(BotaniaStateProps.ENCHANTER_DIRECTION);

        for (BlockPos pylon : PYLON_LOCATIONS.get(axis)) {
            TileEntity tile = worldObj.getTileEntity(pos.add(pylon));
            if (tile != null && tile instanceof TilePylon) {
                ((TilePylon) tile).activated = stage == State.GATHER_MANA;
                if (stage == State.GATHER_MANA)
                    ((TilePylon) tile).centerPos = pos;
            }
        }

        if (stage != State.IDLE)
            stageTicks++;

        if (worldObj.isRemote)
            return;

        if (!canEnchanterExist(worldObj, pos, axis)) {
            worldObj.setBlockState(pos, Blocks.LAPIS_BLOCK.getDefaultState(), 1 | 2);
            PacketHandler.sendToNearby(worldObj, pos,
                    new PacketBotaniaEffect(PacketBotaniaEffect.EffectType.ENCHANTER_DESTROY, pos.getX() + 0.5,
                            pos.getY() + 0.5, pos.getZ() + 0.5));
            worldObj.playSound(null, pos, BotaniaSoundEvents.enchanterFade, SoundCategory.BLOCKS, 0.5F, 10F);
        }

        switch (stage) {
        case GATHER_ENCHANTS: { // Get books
            if (stageTicks % 20 == 0) {
                List<EntityItem> items = worldObj.getEntitiesWithinAABB(EntityItem.class,
                        new AxisAlignedBB(pos.getX() - 2, pos.getY(), pos.getZ() - 2, pos.getX() + 3,
                                pos.getY() + 1, pos.getZ() + 3));
                int count = items.size();
                boolean addedEnch = false;

                if (count > 0 && !worldObj.isRemote) {
                    for (EntityItem entity : items) {
                        ItemStack item = entity.getEntityItem();
                        if (item.getItem() == Items.ENCHANTED_BOOK) {
                            NBTTagList enchants = Items.ENCHANTED_BOOK.getEnchantments(item);
                            if (enchants != null && enchants.tagCount() > 0) {
                                NBTTagCompound enchant = enchants.getCompoundTagAt(0);
                                short enchantId = enchant.getShort("id");
                                short enchantLvl = enchant.getShort("lvl");
                                Enchantment ench = Enchantment.getEnchantmentByID(enchantId);
                                if (!hasEnchantAlready(ench) && isEnchantmentValid(ench)) {
                                    this.enchants.add(new EnchantmentData(ench, enchantLvl));
                                    worldObj.playSound(null, pos, BotaniaSoundEvents.ding, SoundCategory.BLOCKS, 1F,
                                            1F);
                                    addedEnch = true;
                                    break;
                                }
                            }
                        }
                    }
                }

                if (!addedEnch) {
                    if (enchants.isEmpty())
                        stage = State.IDLE;
                    else
                        advanceStage();
                }
            }
            break;
        }
        case GATHER_MANA: { // Get Mana
            if (manaRequired == -1) {
                manaRequired = 0;
                for (EnchantmentData data : enchants) {
                    manaRequired += (int) (5000F
                            * ((15 - Math.min(15, data.enchantmentobj.getRarity().getWeight())) * 1.05F)
                            * ((3F + data.enchantmentLevel * data.enchantmentLevel) * 0.25F)
                            * (0.9F + enchants.size() * 0.05F)
                            * (data.enchantmentobj.isTreasureEnchantment() ? 1.25F : 1F));
                }
            } else if (mana >= manaRequired) {
                manaRequired = 0;
                for (BlockPos pylon : PYLON_LOCATIONS.get(axis))
                    ((TilePylon) worldObj.getTileEntity(pos.add(pylon))).activated = false;

                advanceStage();
            } else {
                ISparkEntity spark = getAttachedSpark();
                if (spark != null) {
                    List<ISparkEntity> sparkEntities = SparkHelper.getSparksAround(worldObj, pos.getX() + 0.5,
                            pos.getY() + 0.5, pos.getZ() + 0.5);
                    for (ISparkEntity otherSpark : sparkEntities) {
                        if (spark == otherSpark)
                            continue;

                        if (otherSpark.getAttachedTile() != null
                                && otherSpark.getAttachedTile() instanceof IManaPool)
                            otherSpark.registerTransfer(spark);
                    }
                }
                if (stageTicks % 5 == 0)
                    sync();
            }

            break;
        }
        case DO_ENCHANT: { // Enchant
            if (stageTicks >= 100) {
                for (EnchantmentData data : enchants)
                    if (EnchantmentHelper.getEnchantmentLevel(data.enchantmentobj, itemToEnchant) == 0)
                        itemToEnchant.addEnchantment(data.enchantmentobj, data.enchantmentLevel);

                enchants.clear();
                manaRequired = -1;
                mana = 0;

                craftingFanciness();
                advanceStage();
            }
            break;
        }
        case RESET: { // Reset
            if (stageTicks >= 20)
                advanceStage();

            break;
        }
        default:
            break;
        }
    }

    private void advanceStage() {
        switch (stage) {
        case IDLE:
            stage = State.GATHER_ENCHANTS;
            break;
        case GATHER_ENCHANTS:
            stage = State.GATHER_MANA;
            break;
        case GATHER_MANA:
            stage = State.DO_ENCHANT;
            break;
        case DO_ENCHANT: {
            stage = State.RESET;
            stage3EndTicks = stageTicks;
            break;
        }
        case RESET: {
            stage = State.IDLE;
            stage3EndTicks = 0;
            break;
        }
        }

        stageTicks = 0;
        sync();
    }

    private void craftingFanciness() {
        worldObj.playSound(null, pos, BotaniaSoundEvents.enchanterEnchant, SoundCategory.BLOCKS, 1F, 1F);
        PacketHandler.sendToNearby(worldObj, pos,
                new PacketBotaniaEffect(PacketBotaniaEffect.EffectType.ENCHANTER_CRAFT, pos.getX() + 0.5,
                        pos.getY() + 1, pos.getZ() + 0.5));
    }

    @Nonnull
    @Override
    public AxisAlignedBB getRenderBoundingBox() {
        return INFINITE_EXTENT_AABB;
    }

    @Override
    public int getCurrentMana() {
        return mana;
    }

    @Override
    public boolean isFull() {
        return mana >= manaRequired;
    }

    @Override
    public void recieveMana(int mana) {
        this.mana = Math.min(manaRequired, this.mana + mana);
    }

    @Override
    public boolean canRecieveManaFromBursts() {
        return manaRequired > 0;
    }

    public void sync() {
        VanillaPacketDispatcher.dispatchTEToNearbyPlayers(worldObj, pos);
    }

    @Override
    public void writePacketNBT(NBTTagCompound cmp) {
        cmp.setInteger(TAG_MANA, mana);
        cmp.setInteger(TAG_MANA_REQUIRED, manaRequired);
        cmp.setInteger(TAG_STAGE, stage.ordinal());
        cmp.setInteger(TAG_STAGE_TICKS, stageTicks);
        cmp.setInteger(TAG_STAGE_3_END_TICKS, stage3EndTicks);

        NBTTagCompound itemCmp = new NBTTagCompound();
        if (itemToEnchant != null)
            itemToEnchant.writeToNBT(itemCmp);
        cmp.setTag(TAG_ITEM, itemCmp);

        String enchStr = enchants.stream()
                .map(e -> Enchantment.REGISTRY.getNameForObject(e.enchantmentobj) + "=" + e.enchantmentLevel)
                .collect(Collectors.joining(","));
        cmp.setString(TAG_ENCHANTS, enchStr);
    }

    @Override
    public void readPacketNBT(NBTTagCompound cmp) {
        mana = cmp.getInteger(TAG_MANA);
        manaRequired = cmp.getInteger(TAG_MANA_REQUIRED);
        stage = State.values()[cmp.getInteger(TAG_STAGE)];
        stageTicks = cmp.getInteger(TAG_STAGE_TICKS);
        stage3EndTicks = cmp.getInteger(TAG_STAGE_3_END_TICKS);

        NBTTagCompound itemCmp = cmp.getCompoundTag(TAG_ITEM);
        itemToEnchant = ItemStack.loadItemStackFromNBT(itemCmp);

        enchants.clear();
        String enchStr = cmp.getString(TAG_ENCHANTS);
        if (!enchStr.isEmpty()) {
            String[] enchTokens = enchStr.split(",");
            for (String token : enchTokens) {
                String[] entryTokens = token.split("=");
                Enchantment ench = Enchantment.getEnchantmentByLocation(entryTokens[0]);
                int lvl = Integer.parseInt(entryTokens[1]);
                enchants.add(new EnchantmentData(ench, lvl));
            }
        }
    }

    private boolean hasEnchantAlready(Enchantment enchant) {
        for (EnchantmentData data : enchants)
            if (data.enchantmentobj == enchant)
                return true;

        return false;
    }

    private boolean isEnchantmentValid(Enchantment ench) {
        if (!ench.canApply(itemToEnchant))
            return false;

        for (EnchantmentData data : enchants) {
            Enchantment otherEnch = data.enchantmentobj;
            if (!otherEnch.canApplyTogether(ench) || !ench.canApplyTogether(otherEnch))
                return false;
        }

        return true;
    }

    public static boolean canEnchanterExist(World world, BlockPos pos, EnumFacing.Axis axis) {
        for (BlockPos obsidian : OBSIDIAN_LOCATIONS)
            if (world.getBlockState(pos.add(obsidian)).getBlock() != Blocks.OBSIDIAN)
                return false;

        for (BlockPos pylon : PYLON_LOCATIONS.get(axis))
            if (world.getBlockState(pos.add(pylon)).getBlock() != ModBlocks.pylon
                    || !BotaniaAPI.internalHandler.isBotaniaFlower(world, pos.add(pylon).down()))
                return false;

        for (BlockPos flower : FLOWER_LOCATIONS)
            if (!BotaniaAPI.internalHandler.isBotaniaFlower(world, pos.add(flower)))
                return false;

        return true;
    }

    @Override
    public boolean canAttachSpark(ItemStack stack) {
        return true;
    }

    @Override
    public void attachSpark(ISparkEntity entity) {
    }

    @Override
    public ISparkEntity getAttachedSpark() {
        List<Entity> sparks = worldObj.getEntitiesWithinAABB(Entity.class, new AxisAlignedBB(pos.getX(),
                pos.getY() + 1, pos.getZ(), pos.getX() + 1, pos.getY() + 2, pos.getZ() + 1),
                Predicates.instanceOf(ISparkEntity.class));
        if (sparks.size() == 1) {
            Entity e = sparks.get(0);
            return (ISparkEntity) e;
        }

        return null;
    }

    @Override
    public boolean areIncomingTranfersDone() {
        return stage == State.DO_ENCHANT;
    }

    @Override
    public int getAvailableSpaceForMana() {
        return Math.max(0, manaRequired - getCurrentMana());
    }

    public void renderHUD(ScaledResolution res) {
        if (manaRequired > 0 && itemToEnchant != null) {
            int x = res.getScaledWidth() / 2 + 20;
            int y = res.getScaledHeight() / 2 - 8;

            RenderHelper.renderProgressPie(x, y, (float) mana / (float) manaRequired, itemToEnchant);
        }
    }

    public enum State {
        IDLE, GATHER_ENCHANTS, GATHER_MANA, DO_ENCHANT, RESET
    }

}