/**
* 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 @ [Jan 26, 2014, 12:23:55 AM (GMT)]
*/
package vazkii.botania.common.block.tile.mana;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import org.lwjgl.opengl.GL11;
import com.google.common.base.Predicates;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.EnumDyeColor;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.SPacketUpdateTileEntity;
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 net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import vazkii.botania.api.BotaniaAPI;
import vazkii.botania.api.internal.VanillaPacketDispatcher;
import vazkii.botania.api.item.IManaDissolvable;
import vazkii.botania.api.mana.IKeyLocked;
import vazkii.botania.api.mana.IManaItem;
import vazkii.botania.api.mana.IManaPool;
import vazkii.botania.api.mana.IThrottledPacket;
import vazkii.botania.api.mana.ManaNetworkEvent;
import vazkii.botania.api.mana.spark.ISparkAttachable;
import vazkii.botania.api.mana.spark.ISparkEntity;
import vazkii.botania.api.recipe.RecipeManaInfusion;
import vazkii.botania.api.sound.BotaniaSoundEvents;
import vazkii.botania.api.state.BotaniaStateProps;
import vazkii.botania.api.state.enums.PoolVariant;
import vazkii.botania.client.core.handler.HUDHandler;
import vazkii.botania.client.core.helper.RenderHelper;
import vazkii.botania.client.lib.LibResources;
import vazkii.botania.common.Botania;
import vazkii.botania.common.block.ModBlocks;
import vazkii.botania.common.block.tile.TileMod;
import vazkii.botania.common.core.handler.ConfigHandler;
import vazkii.botania.common.core.handler.ManaNetworkHandler;
import vazkii.botania.common.core.handler.MethodHandles;
import vazkii.botania.common.item.ItemManaTablet;
import vazkii.botania.common.item.ModItems;
import vazkii.botania.common.network.PacketBotaniaEffect;
import vazkii.botania.common.network.PacketHandler;
public class TilePool extends TileMod implements IManaPool, IKeyLocked, ISparkAttachable, IThrottledPacket {
public static final Color PARTICLE_COLOR = new Color(0x00C6FF);
public static final int MAX_MANA = 1000000;
public static final int MAX_MANA_DILLUTED = 10000;
private static final String TAG_MANA = "mana";
private static final String TAG_KNOWN_MANA = "knownMana";
private static final String TAG_OUTPUTTING = "outputting";
private static final String TAG_COLOR = "color";
private static final String TAG_MANA_CAP = "manaCap";
private static final String TAG_CAN_ACCEPT = "canAccept";
private static final String TAG_CAN_SPARE = "canSpare";
private static final String TAG_FRAGILE = "fragile";
private static final String TAG_INPUT_KEY = "inputKey";
private static final String TAG_OUTPUT_KEY = "outputKey";
private boolean outputting = false;
public EnumDyeColor color = EnumDyeColor.WHITE;
int mana;
private int knownMana = -1;
public int manaCap = -1;
private int soundTicks = 0;
private boolean canAccept = true;
private boolean canSpare = true;
public boolean fragile = false;
boolean isDoingTransfer = false;
int ticksDoingTransfer = 0;
private String inputKey = "";
private final String outputKey = "";
private int ticks = 0;
private boolean sendPacket = false;
@Override
public boolean shouldRefresh(World world, BlockPos pos, @Nonnull IBlockState oldState, @Nonnull IBlockState newState) {
if(oldState.getBlock() != newState.getBlock())
return true;
if(oldState.getBlock() != ModBlocks.pool || newState.getBlock() != ModBlocks.pool)
return true;
return oldState.getValue(BotaniaStateProps.POOL_VARIANT) != newState.getValue(BotaniaStateProps.POOL_VARIANT);
}
@Override
public boolean isFull() {
Block blockBelow = world.getBlockState(pos.down()).getBlock();
return blockBelow != ModBlocks.manaVoid && getCurrentMana() >= manaCap;
}
@Override
public void recieveMana(int mana) {
this.mana = Math.max(0, Math.min(getCurrentMana() + mana, manaCap));
world.updateComparatorOutputLevel(pos, world.getBlockState(pos).getBlock());
markDispatchable();
}
@Override
public void invalidate() {
super.invalidate();
ManaNetworkEvent.removePool(this);
}
@Override
public void onChunkUnload() {
super.onChunkUnload();
invalidate();
}
public static RecipeManaInfusion getMatchingRecipe(@Nonnull ItemStack stack, @Nonnull IBlockState state) {
List<RecipeManaInfusion> matchingNonCatRecipes = new ArrayList<>();
List<RecipeManaInfusion> matchingCatRecipes = new ArrayList<>();
for (RecipeManaInfusion recipe : BotaniaAPI.manaInfusionRecipes) {
if (recipe.matches(stack)) {
if(recipe.getCatalyst() == null)
matchingNonCatRecipes.add(recipe);
else if (recipe.getCatalyst() == state)
matchingCatRecipes.add(recipe);
}
}
// Recipes with matching catalyst take priority above recipes with no catalyst specified
return !matchingCatRecipes.isEmpty() ? matchingCatRecipes.get(0) :
!matchingNonCatRecipes.isEmpty() ? matchingNonCatRecipes.get(0) :
null;
}
public boolean collideEntityItem(EntityItem item) {
if(world.isRemote || item.isDead || item.getEntityItem().isEmpty())
return false;
ItemStack stack = item.getEntityItem();
if(stack.getItem() instanceof IManaDissolvable) {
((IManaDissolvable) stack.getItem()).onDissolveTick(this, stack, item);
}
int age;
try {
age = (int) MethodHandles.itemAge_getter.invokeExact(item);
} catch (Throwable throwable) { return false; }
if(age > 100 && age < 130)
return false;
RecipeManaInfusion recipe = getMatchingRecipe(stack, world.getBlockState(pos.down()));
if(recipe != null) {
int mana = recipe.getManaToConsume();
if(getCurrentMana() >= mana) {
recieveMana(-mana);
stack.shrink(1);
ItemStack output = recipe.getOutput().copy();
EntityItem outputItem = new EntityItem(world, pos.getX() + 0.5, pos.getY() + 1.5, pos.getZ() + 0.5, output);
try {
MethodHandles.itemAge_setter.invokeExact(outputItem, 105);
} catch (Throwable ignored) {}
world.spawnEntity(outputItem);
craftingFanciness();
return true;
}
}
return false;
}
private void craftingFanciness() {
if(soundTicks == 0) {
world.playSound(null, pos, BotaniaSoundEvents.manaPoolCraft, SoundCategory.BLOCKS, 0.4F, 4F);
soundTicks = 6;
}
PacketHandler.sendToNearby(world, getPos(),
new PacketBotaniaEffect(PacketBotaniaEffect.EffectType.POOL_CRAFT, pos.getX(), pos.getY(), pos.getZ()));
}
@Override
public void update() {
if(manaCap == -1)
manaCap = world.getBlockState(getPos()).getValue(BotaniaStateProps.POOL_VARIANT) == PoolVariant.DILUTED ? MAX_MANA_DILLUTED : MAX_MANA;
if(!ManaNetworkHandler.instance.isPoolIn(this) && !isInvalid())
ManaNetworkEvent.addPool(this);
if(world.isRemote) {
double particleChance = 1F - (double) getCurrentMana() / (double) manaCap * 0.1;
if(Math.random() > particleChance)
Botania.proxy.wispFX(pos.getX() + 0.3 + Math.random() * 0.5, pos.getY() + 0.6 + Math.random() * 0.25, pos.getZ() + Math.random(), PARTICLE_COLOR.getRed() / 255F, PARTICLE_COLOR.getGreen() / 255F, PARTICLE_COLOR.getBlue() / 255F, (float) Math.random() / 3F, (float) -Math.random() / 25F, 2F);
return;
}
boolean wasDoingTransfer = isDoingTransfer;
isDoingTransfer = false;
if(soundTicks > 0) {
soundTicks--;
}
if(sendPacket && ticks % 10 == 0) {
VanillaPacketDispatcher.dispatchTEToNearbyPlayers(this);
sendPacket = false;
}
List<EntityItem> items = world.getEntitiesWithinAABB(EntityItem.class, new AxisAlignedBB(pos, pos.add(1, 1, 1)));
for(EntityItem item : items) {
if(item.isDead)
continue;
ItemStack stack = item.getEntityItem();
if(!stack.isEmpty() && stack.getItem() instanceof IManaItem) {
IManaItem mana = (IManaItem) stack.getItem();
if(outputting && mana.canReceiveManaFromPool(stack, this) || !outputting && mana.canExportManaToPool(stack, this)) {
boolean didSomething = false;
int bellowCount = 0;
if(outputting)
for(EnumFacing dir : EnumFacing.HORIZONTALS) {
TileEntity tile = world.getTileEntity(pos.offset(dir));
if(tile != null && tile instanceof TileBellows && ((TileBellows) tile).getLinkedTile() == this)
bellowCount++;
}
int transfRate = 1000 * (bellowCount + 1);
if(outputting) {
if(canSpare) {
if(getCurrentMana() > 0 && mana.getMana(stack) < mana.getMaxMana(stack))
didSomething = true;
int manaVal = Math.min(transfRate, Math.min(getCurrentMana(), mana.getMaxMana(stack) - mana.getMana(stack)));
mana.addMana(stack, manaVal);
recieveMana(-manaVal);
}
} else {
if(canAccept) {
if(mana.getMana(stack) > 0 && !isFull())
didSomething = true;
int manaVal = Math.min(transfRate, Math.min(manaCap - getCurrentMana(), mana.getMana(stack)));
mana.addMana(stack, -manaVal);
recieveMana(manaVal);
}
}
if(didSomething) {
if(ConfigHandler.chargingAnimationEnabled && world.rand.nextInt(20) == 0) {
PacketHandler.sendToNearby(world, getPos(),
new PacketBotaniaEffect(PacketBotaniaEffect.EffectType.POOL_CHARGE, getPos().getX(), getPos().getY(), getPos().getZ(), outputting ? 1 : 0));
}
isDoingTransfer = outputting;
}
}
}
}
if(isDoingTransfer)
ticksDoingTransfer++;
else {
ticksDoingTransfer = 0;
if(wasDoingTransfer)
VanillaPacketDispatcher.dispatchTEToNearbyPlayers(this);
}
ticks++;
}
@Override
public void writePacketNBT(NBTTagCompound cmp) {
cmp.setInteger(TAG_MANA, mana);
cmp.setBoolean(TAG_OUTPUTTING, outputting);
cmp.setInteger(TAG_COLOR, color.getMetadata());
cmp.setInteger(TAG_MANA_CAP, manaCap);
cmp.setBoolean(TAG_CAN_ACCEPT, canAccept);
cmp.setBoolean(TAG_CAN_SPARE, canSpare);
cmp.setBoolean(TAG_FRAGILE, fragile);
cmp.setString(TAG_INPUT_KEY, inputKey);
cmp.setString(TAG_OUTPUT_KEY, outputKey);
}
@Override
public void readPacketNBT(NBTTagCompound cmp) {
mana = cmp.getInteger(TAG_MANA);
outputting = cmp.getBoolean(TAG_OUTPUTTING);
color = EnumDyeColor.byMetadata(cmp.getInteger(TAG_COLOR));
if(cmp.hasKey(TAG_MANA_CAP))
manaCap = cmp.getInteger(TAG_MANA_CAP);
if(cmp.hasKey(TAG_CAN_ACCEPT))
canAccept = cmp.getBoolean(TAG_CAN_ACCEPT);
if(cmp.hasKey(TAG_CAN_SPARE))
canSpare = cmp.getBoolean(TAG_CAN_SPARE);
fragile = cmp.getBoolean(TAG_FRAGILE);
if(cmp.hasKey(TAG_INPUT_KEY))
inputKey = cmp.getString(TAG_INPUT_KEY);
if(cmp.hasKey(TAG_OUTPUT_KEY))
inputKey = cmp.getString(TAG_OUTPUT_KEY);
if(cmp.hasKey(TAG_KNOWN_MANA))
knownMana = cmp.getInteger(TAG_KNOWN_MANA);
}
public void onWanded(EntityPlayer player, ItemStack wand) {
if(player == null)
return;
if(player.isSneaking()) {
outputting = !outputting;
VanillaPacketDispatcher.dispatchTEToNearbyPlayers(world, pos);
}
if(!world.isRemote) {
NBTTagCompound nbttagcompound = new NBTTagCompound();
writePacketNBT(nbttagcompound);
nbttagcompound.setInteger(TAG_KNOWN_MANA, getCurrentMana());
if(player instanceof EntityPlayerMP)
((EntityPlayerMP) player).connection.sendPacket(new SPacketUpdateTileEntity(pos, -999, nbttagcompound));
}
world.playSound(null, player.posX, player.posY, player.posZ, BotaniaSoundEvents.ding, SoundCategory.PLAYERS, 0.11F, 1F);
}
@SideOnly(Side.CLIENT)
public void renderHUD(Minecraft mc, ScaledResolution res) {
ItemStack pool = new ItemStack(ModBlocks.pool, 1, world.getBlockState(getPos()).getValue(BotaniaStateProps.POOL_VARIANT).ordinal());
String name = I18n.format(pool.getUnlocalizedName().replaceAll("tile.", "tile." + LibResources.PREFIX_MOD) + ".name");
int color = 0x4444FF;
HUDHandler.drawSimpleManaHUD(color, knownMana, manaCap, name, res);
int x = res.getScaledWidth() / 2 - 11;
int y = res.getScaledHeight() / 2 + 30;
int u = outputting ? 22 : 0;
int v = 38;
GlStateManager.enableBlend();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
mc.renderEngine.bindTexture(HUDHandler.manaBar);
RenderHelper.drawTexturedModalRect(x, y, 0, u, v, 22, 15);
GlStateManager.color(1F, 1F, 1F, 1F);
ItemStack tablet = new ItemStack(ModItems.manaTablet);
ItemManaTablet.setStackCreative(tablet);
net.minecraft.client.renderer.RenderHelper.enableGUIStandardItemLighting();
mc.getRenderItem().renderItemAndEffectIntoGUI(tablet, x - 20, y);
mc.getRenderItem().renderItemAndEffectIntoGUI(pool, x + 26, y);
net.minecraft.client.renderer.RenderHelper.disableStandardItemLighting();
GlStateManager.disableLighting();
GlStateManager.disableBlend();
}
@Override
public boolean canRecieveManaFromBursts() {
return true;
}
@Override
public boolean isOutputtingPower() {
return outputting;
}
@Override
public int getCurrentMana() {
if(world != null) {
IBlockState state = world.getBlockState(getPos());
if(state.getProperties().containsKey(BotaniaStateProps.POOL_VARIANT))
return state.getValue(BotaniaStateProps.POOL_VARIANT) == PoolVariant.CREATIVE ? MAX_MANA : mana;
}
return 0;
}
@Override
public String getInputKey() {
return inputKey;
}
@Override
public String getOutputKey() {
return outputKey;
}
@Override
public boolean canAttachSpark(ItemStack stack) {
return true;
}
@Override
public void attachSpark(ISparkEntity entity) {}
@Override
public ISparkEntity getAttachedSpark() {
List sparks = world.getEntitiesWithinAABB(Entity.class, new AxisAlignedBB(pos.up(), pos.up().add(1, 1, 1)), Predicates.instanceOf(ISparkEntity.class));
if(sparks.size() == 1) {
Entity e = (Entity) sparks.get(0);
return (ISparkEntity) e;
}
return null;
}
@Override
public boolean areIncomingTranfersDone() {
return false;
}
@Override
public int getAvailableSpaceForMana() {
return Math.max(0, manaCap - getCurrentMana());
}
@Override
public EnumDyeColor getColor() {
return color;
}
@Override
public void setColor(EnumDyeColor color) {
this.color = color;
world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 0b1011);
}
@Override
public void markDispatchable() {
sendPacket = true;
}
}