/**
* Copyright (c) 2011-2015, SpaceToad and the BuildCraft Team
* http://www.mod-buildcraft.com
* <p/>
* BuildCraft is distributed under the terms of the Minecraft Mod Public
* License 1.0, or MMPL. Please check the contents of the license located in
* http://www.mod-buildcraft.com/MMPL-1.0.txt
*/
package buildcraft.silicon;
import java.util.LinkedList;
import java.util.List;
import io.netty.buffer.ByteBuf;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.util.ForgeDirection;
import buildcraft.api.core.Position;
import buildcraft.api.core.SafeTimeTracker;
import buildcraft.api.power.ILaserTarget;
import buildcraft.api.power.ILaserTargetBlock;
import buildcraft.api.tiles.IControllable;
import buildcraft.api.tiles.IHasWork;
import buildcraft.core.Box;
import buildcraft.core.EntityLaser;
import buildcraft.core.LaserData;
import buildcraft.core.lib.RFBattery;
import buildcraft.core.lib.block.TileBuildCraft;
import buildcraft.core.lib.utils.BlockUtils;
public class TileLaser extends TileBuildCraft implements IHasWork, IControllable {
private static final float LASER_OFFSET = 2.0F / 16.0F;
private static final short POWER_AVERAGING = 100;
public LaserData laser = new LaserData();
private final SafeTimeTracker laserTickTracker = new SafeTimeTracker(10);
private final SafeTimeTracker searchTracker = new SafeTimeTracker(100, 100);
private final SafeTimeTracker networkTracker = new SafeTimeTracker(20, 3);
private ILaserTarget laserTarget;
private int powerIndex = 0;
private short powerAverage = 0;
private final short[] power = new short[POWER_AVERAGING];
public TileLaser() {
super();
this.setBattery(new RFBattery(10000, 250, 0));
}
@Override
public void initialize() {
super.initialize();
if (laser == null) {
laser = new LaserData();
}
laser.isVisible = false;
laser.head = new Position(xCoord, yCoord, zCoord);
laser.tail = new Position(xCoord, yCoord, zCoord);
laser.isGlowing = true;
}
@Override
public void updateEntity() {
super.updateEntity();
laser.iterateTexture();
if (worldObj.isRemote) {
return;
}
// If a gate disabled us, remove laser and do nothing.
if (mode == IControllable.Mode.Off) {
removeLaser();
return;
}
// Check for any available tables at a regular basis
if (canFindTable()) {
findTable();
}
// If we still don't have a valid table or the existing has
// become invalid, we disable the laser and do nothing.
if (!isValidTable()) {
removeLaser();
return;
}
// Disable the laser and do nothing if no energy is available.
if (getBattery().getEnergyStored() == 0) {
removeLaser();
return;
}
// We have a laser
if (laser != null) {
// We have a table and can work, so we create a laser if
// necessary.
laser.isVisible = true;
// We may update laser
if (canUpdateLaser()) {
updateLaser();
}
}
// Consume power and transfer it to the table.
int localPower = getBattery().useEnergy(0, getMaxPowerSent(), false);
laserTarget.receiveLaserEnergy(localPower);
if (laser != null) {
pushPower(localPower);
}
onPowerSent(localPower);
sendNetworkUpdate();
}
protected int getMaxPowerSent() {
return 40;
}
protected void onPowerSent(int power) {
}
protected boolean canFindTable() {
return searchTracker.markTimeIfDelay(worldObj);
}
protected boolean canUpdateLaser() {
return laserTickTracker.markTimeIfDelay(worldObj);
}
protected boolean isValidTable() {
if (laserTarget == null || laserTarget.isInvalidTarget() || !laserTarget.requiresLaserEnergy()) {
return false;
}
return true;
}
protected void findTable() {
int meta = getBlockMetadata();
int minX = xCoord - 5;
int minY = yCoord - 5;
int minZ = zCoord - 5;
int maxX = xCoord + 5;
int maxY = yCoord + 5;
int maxZ = zCoord + 5;
switch (ForgeDirection.getOrientation(meta)) {
case WEST:
maxX = xCoord;
break;
case EAST:
minX = xCoord;
break;
case DOWN:
maxY = yCoord;
break;
case UP:
minY = yCoord;
break;
case NORTH:
maxZ = zCoord;
break;
default:
case SOUTH:
minZ = zCoord;
break;
}
List<ILaserTarget> targets = new LinkedList<ILaserTarget>();
if (minY < 0) {
minY = 0;
}
if (maxY > 255) {
maxY = 255;
}
for (int y = minY; y <= maxY; ++y) {
for (int x = minX; x <= maxX; ++x) {
for (int z = minZ; z <= maxZ; ++z) {
if (BlockUtils.getBlock(worldObj, x, y, z) instanceof ILaserTargetBlock) {
TileEntity tile = BlockUtils.getTileEntity(worldObj, x, y, z);
if (tile instanceof ILaserTarget) {
ILaserTarget table = (ILaserTarget) tile;
if (table.requiresLaserEnergy()) {
targets.add(table);
}
}
}
}
}
}
if (targets.isEmpty()) {
return;
}
laserTarget = targets.get(worldObj.rand.nextInt(targets.size()));
}
protected void updateLaser() {
int meta = getBlockMetadata();
double px = 0, py = 0, pz = 0;
switch (ForgeDirection.getOrientation(meta)) {
case WEST:
px = -LASER_OFFSET;
break;
case EAST:
px = LASER_OFFSET;
break;
case DOWN:
py = -LASER_OFFSET;
break;
case UP:
py = LASER_OFFSET;
break;
case NORTH:
pz = -LASER_OFFSET;
break;
case SOUTH:
default:
pz = LASER_OFFSET;
break;
}
Position head = new Position(xCoord + 0.5 + px, yCoord + 0.5 + py, zCoord + 0.5 + pz);
Position tail = new Position(laserTarget.getXCoord() + 0.475 + (worldObj.rand.nextFloat() - 0.5) / 5F, laserTarget.getYCoord() + 9F / 16F,
laserTarget.getZCoord() + 0.475 + (worldObj.rand.nextFloat() - 0.5) / 5F);
laser.head = head;
laser.tail = tail;
if (!laser.isVisible) {
laser.isVisible = true;
}
}
protected void removeLaser() {
if (powerAverage > 0) {
pushPower(0);
}
if (laser.isVisible) {
laser.isVisible = false;
// force sending the network update even if the network tracker
// refuses.
super.sendNetworkUpdate();
}
}
@Override
public void sendNetworkUpdate() {
if (networkTracker.markTimeIfDelay(worldObj)) {
super.sendNetworkUpdate();
}
}
@Override
public void readFromNBT(NBTTagCompound nbttagcompound) {
super.readFromNBT(nbttagcompound);
}
@Override
public void writeToNBT(NBTTagCompound nbttagcompound) {
super.writeToNBT(nbttagcompound);
}
@Override
public void readData(ByteBuf stream) {
laser = new LaserData();
laser.readData(stream);
powerAverage = stream.readShort();
}
@Override
public void writeData(ByteBuf stream) {
laser.writeData(stream);
stream.writeShort(powerAverage);
}
@Override
public void invalidate() {
super.invalidate();
removeLaser();
}
@Override
public boolean hasWork() {
return isValidTable();
}
private void pushPower(int received) {
powerAverage -= power[powerIndex];
powerAverage += received;
power[powerIndex] = (short) received;
powerIndex++;
if (powerIndex == power.length) {
powerIndex = 0;
}
}
public ResourceLocation getTexture() {
double avg = powerAverage / POWER_AVERAGING;
if (avg <= 10.0) {
return EntityLaser.LASER_TEXTURES[0];
} else if (avg <= 20.0) {
return EntityLaser.LASER_TEXTURES[1];
} else if (avg <= 30.0) {
return EntityLaser.LASER_TEXTURES[2];
} else {
return EntityLaser.LASER_TEXTURES[3];
}
}
@Override
public AxisAlignedBB getRenderBoundingBox() {
return new Box(this).extendToEncompass(laser.tail).getBoundingBox();
}
@Override
public boolean acceptsControlMode(Mode mode) {
return mode == IControllable.Mode.On ||
mode == IControllable.Mode.Off;
}
}