package pneumaticCraft.common.tileentity;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.ChunkPosition;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidContainerRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;
import net.minecraftforge.fluids.FluidTankInfo;
import net.minecraftforge.fluids.IFluidContainerItem;
import net.minecraftforge.fluids.IFluidHandler;
import net.minecraftforge.fluids.IFluidTank;
import pneumaticCraft.common.block.Blockss;
import pneumaticCraft.common.fluid.Fluids;
import pneumaticCraft.common.network.DescSynced;
import pneumaticCraft.common.network.GuiSynced;
import pneumaticCraft.common.util.PneumaticCraftUtils;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class TileEntityKeroseneLamp extends TileEntityBase implements IFluidHandler, IRedstoneControlled,
ISidedInventory{
private final Set<ChunkPosition> managingLights = new HashSet<ChunkPosition>();
@DescSynced
private boolean isOn;
@GuiSynced
private int range;
@GuiSynced
private int targetRange = 10;
@GuiSynced
private int redstoneMode;
@GuiSynced
private int fuel;
private static final int LIGHT_SPACING = 3;
public static final int FUEL_PER_MB = 10000;
private int checkingX, checkingY, checkingZ;
@DescSynced
private ForgeDirection sideConnected = ForgeDirection.DOWN;
@DescSynced
private final FluidTank tank = new FluidTank(1000);
private final ItemStack[] inventory = new ItemStack[2];
@Override
public void updateEntity(){
super.updateEntity();
if(!worldObj.isRemote) {
processFluidItem(0, 1);
if(worldObj.getTotalWorldTime() % 5 == 0) {
int realTargetRange = redstoneAllows() ? targetRange : 0;
if(redstoneMode == 3) realTargetRange = (int)(poweredRedstone / 15D * targetRange);
updateRange(Math.min(realTargetRange, tank.getFluidAmount())); //Fade out the lamp when almost empty.
updateLights();
useFuel();
}
} else {
if(isOn && worldObj.getTotalWorldTime() % 5 == 0) {
worldObj.spawnParticle("flame", xCoord + 0.4 + 0.2 * worldObj.rand.nextDouble(), yCoord + 0.2 + tank.getFluidAmount() / 1000D * 3 / 16D, zCoord + 0.4 + 0.2 * worldObj.rand.nextDouble(), 0, 0, 0);
}
}
}
private void useFuel(){
fuel -= Math.pow(range, 3);
if(fuel < 0 && tank.drain(1, true) != null) {
fuel += FUEL_PER_MB;
}
if(fuel < 0) fuel = 0;
}
@Override
public void validate(){
super.validate();
checkingX = xCoord;
checkingY = yCoord;
checkingZ = zCoord;
}
@Override
public void invalidate(){
super.invalidate();
for(ChunkPosition pos : managingLights) {
if(isLampLight(pos)) {
worldObj.setBlockToAir(pos.chunkPosX, pos.chunkPosY, pos.chunkPosZ);
}
}
}
private boolean isLampLight(ChunkPosition pos){
return worldObj.getBlock(pos.chunkPosX, pos.chunkPosY, pos.chunkPosZ) == Blockss.keroseneLampLight;
}
private void updateLights(){
int roundedRange = range / LIGHT_SPACING * LIGHT_SPACING;
checkingX += LIGHT_SPACING;
if(checkingX > xCoord + roundedRange) {
checkingX = xCoord - roundedRange;
checkingY += LIGHT_SPACING;
if(checkingY > yCoord + roundedRange) {
checkingY = yCoord - roundedRange;
checkingZ += LIGHT_SPACING;
if(checkingZ > zCoord + roundedRange) checkingZ = zCoord - roundedRange;
}
}
ChunkPosition pos = new ChunkPosition(checkingX, checkingY, checkingZ);
ChunkPosition lampPos = new ChunkPosition(xCoord, yCoord, zCoord);
if(managingLights.contains(pos)) {
if(isLampLight(pos)) {
if(!passesRaytraceTest(pos, lampPos)) {
worldObj.setBlockToAir(pos.chunkPosX, pos.chunkPosY, pos.chunkPosZ);
managingLights.remove(pos);
}
} else {
managingLights.remove(pos);
}
} else {
tryAddLight(pos, lampPos);
}
}
private void updateRange(int targetRange){
if(targetRange > range) {
range++;
ChunkPosition lampPos = new ChunkPosition(xCoord, yCoord, zCoord);
int roundedRange = range / LIGHT_SPACING * LIGHT_SPACING;
for(int x = -roundedRange; x <= roundedRange; x += LIGHT_SPACING) {
for(int y = -roundedRange; y <= roundedRange; y += LIGHT_SPACING) {
for(int z = -roundedRange; z <= roundedRange; z += LIGHT_SPACING) {
ChunkPosition pos = new ChunkPosition(x + xCoord, y + yCoord, z + zCoord);
if(!managingLights.contains(pos)) {
tryAddLight(pos, lampPos);
}
}
}
}
} else if(targetRange < range) {
range--;
Iterator<ChunkPosition> iterator = managingLights.iterator();
ChunkPosition lampPos = new ChunkPosition(xCoord, yCoord, zCoord);
while(iterator.hasNext()) {
ChunkPosition pos = iterator.next();
if(!isLampLight(pos)) {
iterator.remove();
} else if(PneumaticCraftUtils.distBetween(pos, lampPos) > range) {
worldObj.setBlockToAir(pos.chunkPosX, pos.chunkPosY, pos.chunkPosZ);
iterator.remove();
}
}
}
isOn = range > 0;
}
private boolean passesRaytraceTest(ChunkPosition pos, ChunkPosition lampPos){
MovingObjectPosition mop = worldObj.rayTraceBlocks(Vec3.createVectorHelper(pos.chunkPosX + 0.5, pos.chunkPosY + 0.5, pos.chunkPosZ + 0.5), Vec3.createVectorHelper(lampPos.chunkPosX + 0.5, lampPos.chunkPosY + 0.5, lampPos.chunkPosZ + 0.5));
return mop != null && lampPos.equals(new ChunkPosition(mop.blockX, mop.blockY, mop.blockZ));
}
private boolean tryAddLight(ChunkPosition pos, ChunkPosition lampPos){
if(PneumaticCraftUtils.distBetween(pos, lampPos) <= range) {
if(worldObj.isAirBlock(pos.chunkPosX, pos.chunkPosY, pos.chunkPosZ) && !isLampLight(pos)) {
if(passesRaytraceTest(pos, lampPos)) {
worldObj.setBlock(pos.chunkPosX, pos.chunkPosY, pos.chunkPosZ, Blockss.keroseneLampLight);
managingLights.add(pos);
return true;
}
}
}
return false;
}
@Override
public void onNeighborBlockUpdate(){
super.onNeighborBlockUpdate();
sideConnected = ForgeDirection.DOWN;
for(ForgeDirection d : ForgeDirection.VALID_DIRECTIONS) {
int x = xCoord + d.offsetX;
int y = yCoord + d.offsetY;
int z = zCoord + d.offsetZ;
Block block = worldObj.getBlock(x, y, z);
if(block.isSideSolid(worldObj, x, y, z, d.getOpposite())) {
sideConnected = d;
break;
}
}
}
@Override
public int fill(ForgeDirection from, FluidStack resource, boolean doFill){
return canFill(from, resource.getFluid()) ? tank.fill(resource, doFill) : 0;
}
@Override
public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain){
return tank.getFluid() != null && tank.getFluid().isFluidEqual(resource) ? drain(ForgeDirection.UNKNOWN, resource.amount, doDrain) : null;
}
@Override
public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain){
return tank.drain(maxDrain, doDrain);
}
@Override
public boolean canFill(ForgeDirection from, Fluid fluid){
return Fluids.areFluidsEqual(fluid, Fluids.kerosene);
}
@Override
public boolean canDrain(ForgeDirection from, Fluid fluid){
return true;
}
@Override
public FluidTankInfo[] getTankInfo(ForgeDirection from){
return new FluidTankInfo[]{new FluidTankInfo(tank)};
}
@Override
public void writeToNBT(NBTTagCompound tag){
super.writeToNBT(tag);
NBTTagList lights = new NBTTagList();
for(ChunkPosition pos : managingLights) {
NBTTagCompound t = new NBTTagCompound();
t.setInteger("x", pos.chunkPosX);
t.setInteger("y", pos.chunkPosY);
t.setInteger("z", pos.chunkPosZ);
lights.appendTag(t);
}
tag.setTag("lights", lights);
NBTTagCompound tankTag = new NBTTagCompound();
tank.writeToNBT(tankTag);
tag.setTag("tank", tankTag);
tag.setByte("redstoneMode", (byte)redstoneMode);
tag.setByte("targetRange", (byte)targetRange);
tag.setByte("range", (byte)range);
tag.setByte("sideConnected", (byte)sideConnected.ordinal());
writeInventoryToNBT(tag, inventory);
}
@Override
public void readFromNBT(NBTTagCompound tag){
super.readFromNBT(tag);
managingLights.clear();
NBTTagList lights = tag.getTagList("lights", 10);
for(int i = 0; i < lights.tagCount(); i++) {
NBTTagCompound t = lights.getCompoundTagAt(i);
managingLights.add(new ChunkPosition(t.getInteger("x"), t.getInteger("y"), t.getInteger("z")));
}
tank.readFromNBT(tag.getCompoundTag("tank"));
redstoneMode = tag.getByte("redstoneMode");
targetRange = tag.getByte("targetRange");
range = tag.getByte("range");
sideConnected = ForgeDirection.getOrientation(tag.getByte("sideConnected"));
readInventoryFromNBT(tag, inventory);
}
@Override
public boolean redstoneAllows(){
if(redstoneMode == 3) return true;
return super.redstoneAllows();
}
@Override
public int getRedstoneMode(){
return redstoneMode;
}
@Override
public void handleGUIButtonPress(int buttonID, EntityPlayer player){
if(buttonID == 0) {
redstoneMode++;
if(redstoneMode > 3) redstoneMode = 0;
} else if(buttonID > 0 && buttonID <= 30) {
targetRange = buttonID;
}
}
@SideOnly(Side.CLIENT)
public IFluidTank getTank(){
return tank;
}
public int getRange(){
return range;
}
public int getTargetRange(){
return targetRange;
}
public int getFuel(){
return fuel;
}
public ForgeDirection getSideConnected(){
return sideConnected;
}
/*
* ---------------IInventory---------------------
*/
/**
* Returns the name of the inventory.
*/
@Override
public String getInventoryName(){
return Blockss.keroseneLamp.getUnlocalizedName();
}
/**
* Returns the number of slots in the inventory.
*/
@Override
public int getSizeInventory(){
return inventory.length;
}
/**
* Returns the stack in slot i
*/
@Override
public ItemStack getStackInSlot(int par1){
return inventory[par1];
}
@Override
public ItemStack decrStackSize(int slot, int amount){
ItemStack itemStack = getStackInSlot(slot);
if(itemStack != null) {
if(itemStack.stackSize <= amount) {
setInventorySlotContents(slot, null);
} else {
itemStack = itemStack.splitStack(amount);
if(itemStack.stackSize == 0) {
setInventorySlotContents(slot, null);
}
}
}
return itemStack;
}
@Override
public ItemStack getStackInSlotOnClosing(int slot){
ItemStack itemStack = getStackInSlot(slot);
if(itemStack != null) {
setInventorySlotContents(slot, null);
}
return itemStack;
}
@Override
public void setInventorySlotContents(int slot, ItemStack itemStack){
inventory[slot] = itemStack;
if(itemStack != null && itemStack.stackSize > getInventoryStackLimit()) {
itemStack.stackSize = getInventoryStackLimit();
}
}
@Override
public boolean isItemValidForSlot(int slot, ItemStack stack){
return slot == 1 ? false : stack != null && (FluidContainerRegistry.getFluidForFilledItem(stack) != null || stack.getItem() instanceof IFluidContainerItem && ((IFluidContainerItem)stack.getItem()).getFluid(stack) != null);
}
@Override
public boolean hasCustomInventoryName(){
return false;
}
@Override
public int getInventoryStackLimit(){
return 64;
}
@Override
public boolean isUseableByPlayer(EntityPlayer p_70300_1_){
return isGuiUseableByPlayer(p_70300_1_);
}
@Override
public void openInventory(){}
@Override
public void closeInventory(){}
@Override
public int[] getAccessibleSlotsFromSide(int p_94128_1_){
return new int[]{0, 1};
}
@Override
public boolean canInsertItem(int slot, ItemStack stack, int side){
return isItemValidForSlot(slot, stack);
}
@Override
public boolean canExtractItem(int slot, ItemStack stack, int side){
return slot == 1;
}
}