/** * 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 = ItemStack.EMPTY; 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.isEmpty() || !itemToEnchant.isItemEnchantable()) return; List<EntityItem> items = world.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 && !world.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 = world.getBlockState(getPos()); EnumFacing.Axis axis = state.getValue(BotaniaStateProps.ENCHANTER_DIRECTION); for(BlockPos pylon : PYLON_LOCATIONS.get(axis)) { TileEntity tile = world.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(world.isRemote) return; if(!canEnchanterExist(world, pos, axis)) { world.setBlockState(pos, Blocks.LAPIS_BLOCK.getDefaultState(), 1 | 2); PacketHandler.sendToNearby(world, pos, new PacketBotaniaEffect(PacketBotaniaEffect.EffectType.ENCHANTER_DESTROY, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5)); world.playSound(null, pos, BotaniaSoundEvents.enchanterFade, SoundCategory.BLOCKS, 0.5F, 10F); } switch(stage) { case GATHER_ENCHANTS : { // Get books if(stageTicks % 20 == 0) { List<EntityItem> items = world.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 && !world.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)); world.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) world.getTileEntity(pos.add(pylon))).activated = false; advanceStage(); } else { ISparkEntity spark = getAttachedSpark(); if(spark != null) { List<ISparkEntity> sparkEntities = SparkHelper.getSparksAround(world, 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() { world.playSound(null, pos, BotaniaSoundEvents.enchanterEnchant, SoundCategory.BLOCKS, 1F, 1F); PacketHandler.sendToNearby(world, 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(world, 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.isEmpty()) itemCmp = 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 = new ItemStack(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 (!ench.func_191560_c(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 = world.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.isEmpty()) { 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 } }