934 lines
24 KiB
Java
934 lines
24 KiB
Java
/**
|
|
* Copyright (c) 2011-2017, 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.builders;
|
|
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Sets;
|
|
import io.netty.buffer.ByteBuf;
|
|
|
|
import net.minecraft.block.Block;
|
|
import net.minecraft.block.BlockLiquid;
|
|
import net.minecraft.entity.item.EntityItem;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.inventory.ISidedInventory;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.util.AxisAlignedBB;
|
|
import net.minecraft.util.ChatComponentTranslation;
|
|
import net.minecraft.util.MathHelper;
|
|
import net.minecraft.world.ChunkCoordIntPair;
|
|
|
|
import net.minecraftforge.common.ForgeChunkManager;
|
|
import net.minecraftforge.common.ForgeChunkManager.Ticket;
|
|
import net.minecraftforge.common.ForgeChunkManager.Type;
|
|
import net.minecraftforge.common.util.FakePlayer;
|
|
import net.minecraftforge.common.util.ForgeDirection;
|
|
import net.minecraftforge.fluids.IFluidBlock;
|
|
|
|
import buildcraft.BuildCraftBuilders;
|
|
import buildcraft.BuildCraftCore;
|
|
import buildcraft.api.blueprints.BuilderAPI;
|
|
import buildcraft.api.core.BuildCraftAPI;
|
|
import buildcraft.api.core.IAreaProvider;
|
|
import buildcraft.api.core.SafeTimeTracker;
|
|
import buildcraft.api.filler.FillerManager;
|
|
import buildcraft.api.statements.IStatementParameter;
|
|
import buildcraft.api.tiles.IControllable;
|
|
import buildcraft.api.tiles.IHasWork;
|
|
import buildcraft.api.transport.IPipeConnection;
|
|
import buildcraft.api.transport.IPipeTile;
|
|
import buildcraft.core.Box;
|
|
import buildcraft.core.Box.Kind;
|
|
import buildcraft.core.CoreConstants;
|
|
import buildcraft.core.DefaultAreaProvider;
|
|
import buildcraft.core.DefaultProps;
|
|
import buildcraft.core.blueprints.Blueprint;
|
|
import buildcraft.core.blueprints.BptBuilderBase;
|
|
import buildcraft.core.blueprints.BptBuilderBlueprint;
|
|
import buildcraft.core.builders.TileAbstractBuilder;
|
|
import buildcraft.core.builders.patterns.FillerPattern;
|
|
import buildcraft.core.internal.IDropControlInventory;
|
|
import buildcraft.core.internal.ILEDProvider;
|
|
import buildcraft.core.lib.RFBattery;
|
|
import buildcraft.core.lib.utils.BlockMiner;
|
|
import buildcraft.core.lib.utils.BlockUtils;
|
|
import buildcraft.core.lib.utils.Utils;
|
|
import buildcraft.core.proxy.CoreProxy;
|
|
|
|
public class TileQuarry extends TileAbstractBuilder implements IHasWork, ISidedInventory, IDropControlInventory, IPipeConnection, IControllable, ILEDProvider {
|
|
private enum Stage {
|
|
BUILDING,
|
|
DIGGING,
|
|
MOVING,
|
|
IDLE,
|
|
DONE
|
|
}
|
|
|
|
public EntityMechanicalArm arm;
|
|
public EntityPlayer placedBy;
|
|
|
|
protected Box box = new Box();
|
|
private int targetX, targetY, targetZ;
|
|
private double headPosX, headPosY, headPosZ;
|
|
private double speed = 0.03;
|
|
private Stage stage = Stage.BUILDING;
|
|
private boolean movingHorizontally;
|
|
private boolean movingVertically;
|
|
private float headTrajectory;
|
|
|
|
private SafeTimeTracker updateTracker = new SafeTimeTracker(BuildCraftCore.updateFactor);
|
|
|
|
private BptBuilderBase builder;
|
|
|
|
private final LinkedList<int[]> visitList = Lists.newLinkedList();
|
|
|
|
private boolean loadDefaultBoundaries = false;
|
|
private Ticket chunkTicket;
|
|
|
|
private boolean frameProducer = true;
|
|
|
|
private NBTTagCompound initNBT = null;
|
|
|
|
private BlockMiner miner;
|
|
private int ledState;
|
|
|
|
public TileQuarry() {
|
|
box.kind = Kind.STRIPES;
|
|
this.setBattery(new RFBattery((int) (2 * 64 * BuilderAPI.BREAK_ENERGY * BuildCraftCore.miningMultiplier), (int) (1000 * BuildCraftCore.miningMultiplier), 0));
|
|
}
|
|
|
|
public void createUtilsIfNeeded() {
|
|
if (!worldObj.isRemote) {
|
|
if (builder == null) {
|
|
if (!box.isInitialized()) {
|
|
setBoundaries(loadDefaultBoundaries);
|
|
}
|
|
|
|
initializeBlueprintBuilder();
|
|
}
|
|
}
|
|
|
|
if (stage != Stage.BUILDING) {
|
|
box.isVisible = false;
|
|
|
|
if (arm == null) {
|
|
createArm();
|
|
}
|
|
|
|
if (findTarget(false)) {
|
|
if ((headPosX < box.xMin || headPosX > box.xMax) || (headPosZ < box.zMin || headPosZ > box.zMax)) {
|
|
setHead(box.xMin + 1, yCoord + 2, box.zMin + 1);
|
|
}
|
|
}
|
|
} else {
|
|
box.isVisible = true;
|
|
}
|
|
}
|
|
|
|
private void createArm() {
|
|
worldObj.spawnEntityInWorld
|
|
(new EntityMechanicalArm(worldObj,
|
|
box.xMin + CoreConstants.PIPE_MAX_POS,
|
|
yCoord + box.sizeY() - 1 + CoreConstants.PIPE_MIN_POS,
|
|
box.zMin + CoreConstants.PIPE_MAX_POS,
|
|
box.sizeX() - 2 + CoreConstants.PIPE_MIN_POS * 2,
|
|
box.sizeZ() - 2 + CoreConstants.PIPE_MIN_POS * 2,
|
|
this));
|
|
}
|
|
|
|
// Callback from the arm once it's created
|
|
public void setArm(EntityMechanicalArm arm) {
|
|
this.arm = arm;
|
|
}
|
|
|
|
public boolean areChunksLoaded() {
|
|
if (!BuildCraftBuilders.quarryLoadsChunks) {
|
|
// Each chunk covers the full height, so we only check one of them per height.
|
|
for (int chunkX = box.xMin >> 4; chunkX <= box.xMax >> 4; chunkX++) {
|
|
for (int chunkZ = box.zMin >> 4; chunkZ <= box.zMax >> 4; chunkZ++) {
|
|
if (!worldObj.blockExists(chunkX << 4, box.yMax, chunkZ << 4)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void updateEntity() {
|
|
super.updateEntity();
|
|
|
|
if (worldObj.isRemote) {
|
|
if (stage != Stage.DONE) {
|
|
moveHead(speed);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (stage == Stage.DONE) {
|
|
if (mode == Mode.Loop) {
|
|
stage = Stage.IDLE;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!areChunksLoaded()) {
|
|
return;
|
|
}
|
|
|
|
if (mode == Mode.Off && stage != Stage.MOVING) {
|
|
return;
|
|
}
|
|
|
|
createUtilsIfNeeded();
|
|
|
|
if (stage == Stage.BUILDING) {
|
|
if (builder != null && !builder.isDone(this)) {
|
|
builder.buildNextSlot(worldObj, this, xCoord, yCoord, zCoord);
|
|
} else {
|
|
stage = Stage.IDLE;
|
|
}
|
|
} else if (stage == Stage.DIGGING) {
|
|
dig();
|
|
} else if (stage == Stage.IDLE) {
|
|
idling();
|
|
|
|
// We are sending a network packet update ONLY below.
|
|
// In this case, since idling() does it anyway, we should return.
|
|
return;
|
|
} else if (stage == Stage.MOVING) {
|
|
int energyUsed = this.getBattery().useEnergy(20, (int) Math.ceil(20D + (double) getBattery().getEnergyStored() / 10), false);
|
|
|
|
if (energyUsed >= 20) {
|
|
|
|
speed = 0.1 + energyUsed / 2000F;
|
|
|
|
// If it's raining or snowing above the head, slow down.
|
|
if (worldObj.isRaining()) {
|
|
int headBPX = (int) headPosX;
|
|
int headBPY = (int) headPosY;
|
|
int headBPZ = (int) headPosZ;
|
|
if (worldObj.getHeightValue(headBPX, headBPZ) < headBPY) {
|
|
speed *= 0.7;
|
|
}
|
|
}
|
|
|
|
moveHead(speed);
|
|
} else {
|
|
speed = 0;
|
|
}
|
|
}
|
|
|
|
if (updateTracker.markTimeIfDelay(worldObj)) {
|
|
sendNetworkUpdate();
|
|
}
|
|
}
|
|
|
|
protected void dig() {
|
|
if (worldObj.isRemote) {
|
|
return;
|
|
}
|
|
|
|
if (miner == null) {
|
|
// Hmm. Probably shouldn't be mining if there's no miner.
|
|
stage = Stage.IDLE;
|
|
return;
|
|
}
|
|
|
|
int rfTaken = miner.acceptEnergy(getBattery().getEnergyStored());
|
|
getBattery().useEnergy(rfTaken, rfTaken, false);
|
|
|
|
if (miner.hasMined()) {
|
|
// Collect any lost items laying around.
|
|
double[] head = getHead();
|
|
AxisAlignedBB axis = AxisAlignedBB.getBoundingBox(head[0] - 2, head[1] - 2, head[2] - 2, head[0] + 3, head[1] + 3, head[2] + 3);
|
|
List<EntityItem> result = worldObj.getEntitiesWithinAABB(EntityItem.class, axis);
|
|
for (EntityItem entity : result) {
|
|
if (entity.isDead) {
|
|
continue;
|
|
}
|
|
|
|
ItemStack mineable = entity.getEntityItem();
|
|
if (mineable.stackSize <= 0) {
|
|
continue;
|
|
}
|
|
CoreProxy.proxy.removeEntity(entity);
|
|
miner.mineStack(mineable);
|
|
}
|
|
}
|
|
|
|
if (miner.hasMined() || miner.hasFailed()) {
|
|
miner = null;
|
|
|
|
if (!findFrame()) {
|
|
initializeBlueprintBuilder();
|
|
stage = Stage.BUILDING;
|
|
} else {
|
|
stage = Stage.IDLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected boolean findFrame() {
|
|
for (int i = 2; i < 6; i++) {
|
|
ForgeDirection o = ForgeDirection.getOrientation(i);
|
|
if (box.contains(xCoord + o.offsetX, yCoord + o.offsetY, zCoord + o.offsetZ)) {
|
|
return worldObj.getBlock(xCoord + o.offsetX, yCoord + o.offsetY, zCoord + o.offsetZ) == BuildCraftBuilders.frameBlock;
|
|
}
|
|
}
|
|
|
|
// Could not find any location in box - this is strange, so obviously
|
|
// we're going to ignore it!
|
|
return true;
|
|
}
|
|
|
|
protected void idling() {
|
|
if (!findTarget(true)) {
|
|
// I believe the issue is box going null becuase of bad chunkloader positioning
|
|
if (arm != null && box != null) {
|
|
setTarget(box.xMin + 1, yCoord + 2, box.zMin + 1);
|
|
}
|
|
|
|
stage = Stage.DONE;
|
|
} else {
|
|
stage = Stage.MOVING;
|
|
}
|
|
|
|
movingHorizontally = true;
|
|
movingVertically = true;
|
|
double[] head = getHead();
|
|
int[] target = getTarget();
|
|
headTrajectory = (float) Math.atan2(target[2] - head[2], target[0] - head[0]);
|
|
sendNetworkUpdate();
|
|
}
|
|
|
|
public boolean findTarget(boolean doSet) {
|
|
if (worldObj.isRemote) {
|
|
return false;
|
|
}
|
|
|
|
boolean columnVisitListIsUpdated = false;
|
|
|
|
if (visitList.isEmpty()) {
|
|
createColumnVisitList();
|
|
columnVisitListIsUpdated = true;
|
|
}
|
|
|
|
if (!doSet) {
|
|
return !visitList.isEmpty();
|
|
}
|
|
|
|
if (visitList.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
int[] nextTarget = visitList.removeFirst();
|
|
|
|
if (!columnVisitListIsUpdated) { // nextTarget may not be accurate, at least search the target column for changes
|
|
for (int y = nextTarget[1] + 1; y < yCoord + 3; y++) {
|
|
if (isQuarriableBlock(nextTarget[0], y, nextTarget[2])) {
|
|
createColumnVisitList();
|
|
columnVisitListIsUpdated = true;
|
|
nextTarget = null;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (columnVisitListIsUpdated && nextTarget == null && !visitList.isEmpty()) {
|
|
nextTarget = visitList.removeFirst();
|
|
} else if (columnVisitListIsUpdated && nextTarget == null) {
|
|
return false;
|
|
}
|
|
|
|
setTarget(nextTarget[0], nextTarget[1] + 1, nextTarget[2]);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Make the column visit list: called once per layer
|
|
*/
|
|
private void createColumnVisitList() {
|
|
visitList.clear();
|
|
boolean[][] blockedColumns = new boolean[builder.blueprint.sizeX - 2][builder.blueprint.sizeZ - 2];
|
|
|
|
for (int searchY = yCoord + 3; searchY >= 1; --searchY) {
|
|
int startX, endX, incX;
|
|
|
|
if (searchY % 2 == 0) {
|
|
startX = 0;
|
|
endX = builder.blueprint.sizeX - 2;
|
|
incX = 1;
|
|
} else {
|
|
startX = builder.blueprint.sizeX - 3;
|
|
endX = -1;
|
|
incX = -1;
|
|
}
|
|
|
|
for (int searchX = startX; searchX != endX; searchX += incX) {
|
|
int startZ, endZ, incZ;
|
|
|
|
if (searchX % 2 == searchY % 2) {
|
|
startZ = 0;
|
|
endZ = builder.blueprint.sizeZ - 2;
|
|
incZ = 1;
|
|
} else {
|
|
startZ = builder.blueprint.sizeZ - 3;
|
|
endZ = -1;
|
|
incZ = -1;
|
|
}
|
|
|
|
for (int searchZ = startZ; searchZ != endZ; searchZ += incZ) {
|
|
if (!blockedColumns[searchX][searchZ]) {
|
|
int bx = box.xMin + searchX + 1, by = searchY, bz = box.zMin + searchZ + 1;
|
|
|
|
Block block = worldObj.getBlock(bx, by, bz);
|
|
|
|
if (!BlockUtils.canChangeBlock(block, worldObj, bx, by, bz)) {
|
|
blockedColumns[searchX][searchZ] = true;
|
|
} else if (!BuildCraftAPI.isSoftBlock(worldObj, bx, by, bz) && !(block instanceof BlockLiquid) && !(block instanceof IFluidBlock)) {
|
|
visitList.add(new int[]{bx, by, bz});
|
|
}
|
|
|
|
// Stop at two planes - generally any obstructions will have been found and will force a recompute prior to this
|
|
|
|
if (visitList.size() > builder.blueprint.sizeZ * builder.blueprint.sizeX * 2) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void readFromNBT(NBTTagCompound nbttagcompound) {
|
|
super.readFromNBT(nbttagcompound);
|
|
|
|
if (nbttagcompound.hasKey("box")) {
|
|
box.initialize(nbttagcompound.getCompoundTag("box"));
|
|
|
|
loadDefaultBoundaries = false;
|
|
} else if (nbttagcompound.hasKey("xSize")) {
|
|
// This is a legacy save, get old data
|
|
|
|
int xMin = nbttagcompound.getInteger("xMin");
|
|
int zMin = nbttagcompound.getInteger("zMin");
|
|
|
|
int xSize = nbttagcompound.getInteger("xSize");
|
|
int ySize = nbttagcompound.getInteger("ySize");
|
|
int zSize = nbttagcompound.getInteger("zSize");
|
|
|
|
box.initialize(xMin, yCoord, zMin, xMin + xSize - 1, yCoord + ySize - 1, zMin + zSize - 1);
|
|
|
|
loadDefaultBoundaries = false;
|
|
} else {
|
|
// This is a legacy save, compute boundaries
|
|
|
|
loadDefaultBoundaries = true;
|
|
}
|
|
|
|
targetX = nbttagcompound.getInteger("targetX");
|
|
targetY = nbttagcompound.getInteger("targetY");
|
|
targetZ = nbttagcompound.getInteger("targetZ");
|
|
headPosX = nbttagcompound.getDouble("headPosX");
|
|
headPosY = nbttagcompound.getDouble("headPosY");
|
|
headPosZ = nbttagcompound.getDouble("headPosZ");
|
|
|
|
// The rest of load has to be done upon initialize.
|
|
initNBT = (NBTTagCompound) nbttagcompound.getCompoundTag("bpt").copy();
|
|
}
|
|
|
|
@Override
|
|
public void writeToNBT(NBTTagCompound nbttagcompound) {
|
|
super.writeToNBT(nbttagcompound);
|
|
|
|
nbttagcompound.setInteger("targetX", targetX);
|
|
nbttagcompound.setInteger("targetY", targetY);
|
|
nbttagcompound.setInteger("targetZ", targetZ);
|
|
nbttagcompound.setDouble("headPosX", headPosX);
|
|
nbttagcompound.setDouble("headPosY", headPosY);
|
|
nbttagcompound.setDouble("headPosZ", headPosZ);
|
|
|
|
NBTTagCompound boxTag = new NBTTagCompound();
|
|
box.writeToNBT(boxTag);
|
|
nbttagcompound.setTag("box", boxTag);
|
|
|
|
NBTTagCompound bptNBT = new NBTTagCompound();
|
|
|
|
if (builder != null) {
|
|
NBTTagCompound builderCpt = new NBTTagCompound();
|
|
builder.saveBuildStateToNBT(builderCpt, this);
|
|
bptNBT.setTag("builderState", builderCpt);
|
|
}
|
|
|
|
nbttagcompound.setTag("bpt", bptNBT);
|
|
}
|
|
|
|
@SuppressWarnings("rawtypes")
|
|
public void positionReached() {
|
|
if (worldObj.isRemote) {
|
|
return;
|
|
}
|
|
|
|
if (isQuarriableBlock(targetX, targetY - 1, targetZ)) {
|
|
miner = new BlockMiner(worldObj, this, targetX, targetY - 1, targetZ);
|
|
stage = Stage.DIGGING;
|
|
} else {
|
|
stage = Stage.IDLE;
|
|
}
|
|
}
|
|
|
|
private boolean isQuarriableBlock(int bx, int by, int bz) {
|
|
Block block = worldObj.getBlock(bx, by, bz);
|
|
return BlockUtils.canChangeBlock(block, worldObj, bx, by, bz)
|
|
&& !BuildCraftAPI.isSoftBlock(worldObj, bx, by, bz)
|
|
&& !(block instanceof BlockLiquid) && !(block instanceof IFluidBlock);
|
|
}
|
|
|
|
@Override
|
|
protected int getNetworkUpdateRange() {
|
|
return DefaultProps.NETWORK_UPDATE_RANGE + (int) Math.ceil(Math.sqrt(yCoord * yCoord + box.sizeX() * box.sizeX() + box.sizeZ() * box.sizeZ()));
|
|
}
|
|
|
|
@Override
|
|
public void invalidate() {
|
|
if (chunkTicket != null) {
|
|
ForgeChunkManager.releaseTicket(chunkTicket);
|
|
}
|
|
|
|
super.invalidate();
|
|
destroy();
|
|
}
|
|
|
|
@Override
|
|
public void onChunkUnload() {
|
|
destroy();
|
|
}
|
|
|
|
@Override
|
|
public void destroy() {
|
|
if (arm != null) {
|
|
arm.setDead();
|
|
}
|
|
|
|
arm = null;
|
|
|
|
frameProducer = false;
|
|
|
|
if (miner != null) {
|
|
miner.invalidate();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean hasWork() {
|
|
return stage != Stage.DONE;
|
|
}
|
|
|
|
private void setBoundaries(boolean useDefaultI) {
|
|
boolean useDefault = useDefaultI;
|
|
|
|
if (BuildCraftBuilders.quarryLoadsChunks && chunkTicket == null) {
|
|
chunkTicket = ForgeChunkManager.requestTicket(BuildCraftBuilders.instance, worldObj, Type.NORMAL);
|
|
}
|
|
|
|
if (chunkTicket != null) {
|
|
chunkTicket.getModData().setInteger("quarryX", xCoord);
|
|
chunkTicket.getModData().setInteger("quarryY", yCoord);
|
|
chunkTicket.getModData().setInteger("quarryZ", zCoord);
|
|
}
|
|
|
|
IAreaProvider a = null;
|
|
|
|
if (!useDefault) {
|
|
a = Utils.getNearbyAreaProvider(worldObj, xCoord, yCoord, zCoord);
|
|
}
|
|
|
|
if (a == null) {
|
|
a = new DefaultAreaProvider(xCoord, yCoord, zCoord, xCoord + 10, yCoord + 4, zCoord + 10);
|
|
|
|
useDefault = true;
|
|
}
|
|
|
|
int xSize = a.xMax() - a.xMin() + 1;
|
|
int zSize = a.zMax() - a.zMin() + 1;
|
|
|
|
if (xSize < 3 || zSize < 3 || (chunkTicket != null && ((xSize * zSize) >> 8) >= chunkTicket.getMaxChunkListDepth())) {
|
|
if (placedBy != null) {
|
|
placedBy.addChatMessage(new ChatComponentTranslation("chat.buildcraft.quarry.tooSmall",
|
|
xSize, zSize,
|
|
chunkTicket != null ? chunkTicket.getMaxChunkListDepth() : 0));
|
|
}
|
|
|
|
a = new DefaultAreaProvider(xCoord, yCoord, zCoord, xCoord + 10, yCoord + 4, zCoord + 10);
|
|
useDefault = true;
|
|
}
|
|
|
|
xSize = a.xMax() - a.xMin() + 1;
|
|
int ySize = a.yMax() - a.yMin() + 1;
|
|
zSize = a.zMax() - a.zMin() + 1;
|
|
|
|
box.initialize(a);
|
|
|
|
if (ySize < 5) {
|
|
ySize = 5;
|
|
box.yMax = box.yMin + ySize - 1;
|
|
}
|
|
|
|
if (useDefault) {
|
|
int xMin, zMin;
|
|
|
|
int dir = worldObj.getBlockMetadata(xCoord, yCoord, zCoord);
|
|
ForgeDirection o = ForgeDirection.getOrientation(dir > 6 ? 6 : dir).getOpposite();
|
|
|
|
switch (o) {
|
|
case EAST:
|
|
xMin = xCoord + 1;
|
|
zMin = zCoord - 4 - 1;
|
|
break;
|
|
case WEST:
|
|
xMin = xCoord - 9 - 2;
|
|
zMin = zCoord - 4 - 1;
|
|
break;
|
|
case SOUTH:
|
|
xMin = xCoord - 4 - 1;
|
|
zMin = zCoord + 1;
|
|
break;
|
|
case NORTH:
|
|
default:
|
|
xMin = xCoord - 4 - 1;
|
|
zMin = zCoord - 9 - 2;
|
|
break;
|
|
}
|
|
|
|
box.initialize(xMin, yCoord, zMin, xMin + xSize - 1, yCoord + ySize - 1, zMin + zSize - 1);
|
|
}
|
|
|
|
a.removeFromWorld();
|
|
if (chunkTicket != null) {
|
|
forceChunkLoading(chunkTicket);
|
|
}
|
|
|
|
sendNetworkUpdate();
|
|
}
|
|
|
|
private void initializeBlueprintBuilder() {
|
|
Blueprint bpt = ((FillerPattern) FillerManager.registry.getPattern("buildcraft:frame"))
|
|
.getBlueprint(box, worldObj, new IStatementParameter[0], BuildCraftBuilders.frameBlock, 0);
|
|
|
|
if (bpt != null) {
|
|
builder = new BptBuilderBlueprint(bpt, worldObj, box.xMin, yCoord, box.zMin);
|
|
speed = 0;
|
|
stage = Stage.BUILDING;
|
|
sendNetworkUpdate();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void writeData(ByteBuf stream) {
|
|
super.writeData(stream);
|
|
box.writeData(stream);
|
|
stream.writeInt(targetX);
|
|
stream.writeShort(targetY);
|
|
stream.writeInt(targetZ);
|
|
stream.writeDouble(headPosX);
|
|
stream.writeDouble(headPosY);
|
|
stream.writeDouble(headPosZ);
|
|
stream.writeFloat((float) speed);
|
|
stream.writeFloat(headTrajectory);
|
|
int flags = stage.ordinal();
|
|
flags |= movingHorizontally ? 0x10 : 0;
|
|
flags |= movingVertically ? 0x20 : 0;
|
|
stream.writeByte(flags);
|
|
|
|
ledState = (hasWork() && mode != Mode.Off && getTicksSinceEnergyReceived() < 12 ? 16 : 0) | (getBattery().getEnergyStored() * 15 / getBattery().getMaxEnergyStored());
|
|
stream.writeByte(ledState);
|
|
}
|
|
|
|
@Override
|
|
public void readData(ByteBuf stream) {
|
|
super.readData(stream);
|
|
box.readData(stream);
|
|
targetX = stream.readInt();
|
|
targetY = stream.readUnsignedShort();
|
|
targetZ = stream.readInt();
|
|
headPosX = stream.readDouble();
|
|
headPosY = stream.readDouble();
|
|
headPosZ = stream.readDouble();
|
|
speed = stream.readFloat();
|
|
headTrajectory = stream.readFloat();
|
|
int flags = stream.readUnsignedByte();
|
|
stage = Stage.values()[flags & 0x07];
|
|
movingHorizontally = (flags & 0x10) != 0;
|
|
movingVertically = (flags & 0x20) != 0;
|
|
int newLedState = stream.readUnsignedByte();
|
|
if (newLedState != ledState) {
|
|
ledState = newLedState;
|
|
worldObj.markBlockRangeForRenderUpdate(xCoord, yCoord, zCoord, xCoord, yCoord, zCoord);
|
|
}
|
|
|
|
createUtilsIfNeeded();
|
|
|
|
if (arm != null) {
|
|
arm.setHead(headPosX, headPosY, headPosZ);
|
|
arm.updatePosition();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void initialize() {
|
|
super.initialize();
|
|
|
|
if (!this.getWorldObj().isRemote && !box.initialized) {
|
|
setBoundaries(false);
|
|
}
|
|
|
|
createUtilsIfNeeded();
|
|
|
|
if (initNBT != null && builder != null) {
|
|
builder.loadBuildStateToNBT(
|
|
initNBT.getCompoundTag("builderState"), this);
|
|
}
|
|
|
|
initNBT = null;
|
|
|
|
sendNetworkUpdate();
|
|
}
|
|
|
|
public void reinitalize() {
|
|
initializeBlueprintBuilder();
|
|
}
|
|
|
|
@Override
|
|
public int getSizeInventory() {
|
|
return 1;
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getStackInSlot(int i) {
|
|
if (frameProducer) {
|
|
return new ItemStack(BuildCraftBuilders.frameBlock);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public ItemStack decrStackSize(int i, int j) {
|
|
if (frameProducer) {
|
|
return new ItemStack(BuildCraftBuilders.frameBlock, j);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setInventorySlotContents(int i, ItemStack itemstack) {
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getStackInSlotOnClosing(int slot) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public String getInventoryName() {
|
|
return "";
|
|
}
|
|
|
|
@Override
|
|
public int getInventoryStackLimit() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean isItemValidForSlot(int i, ItemStack itemstack) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isUseableByPlayer(EntityPlayer entityplayer) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void openInventory() {
|
|
}
|
|
|
|
@Override
|
|
public void closeInventory() {
|
|
}
|
|
|
|
@Override
|
|
public boolean isBuildingMaterialSlot(int i) {
|
|
return true;
|
|
}
|
|
|
|
public void moveHead(double instantSpeed) {
|
|
int[] target = getTarget();
|
|
double[] head = getHead();
|
|
|
|
if (movingHorizontally) {
|
|
if (Math.abs(target[0] - head[0]) < instantSpeed * 2 && Math.abs(target[2] - head[2]) < instantSpeed * 2) {
|
|
head[0] = target[0];
|
|
head[2] = target[2];
|
|
|
|
movingHorizontally = false;
|
|
|
|
if (!movingVertically) {
|
|
positionReached();
|
|
head[1] = target[1];
|
|
}
|
|
} else {
|
|
head[0] += MathHelper.cos(headTrajectory) * instantSpeed;
|
|
head[2] += MathHelper.sin(headTrajectory) * instantSpeed;
|
|
}
|
|
setHead(head[0], head[1], head[2]);
|
|
}
|
|
|
|
if (movingVertically) {
|
|
if (Math.abs(target[1] - head[1]) < instantSpeed * 2) {
|
|
head[1] = target[1];
|
|
|
|
movingVertically = false;
|
|
if (!movingHorizontally) {
|
|
positionReached();
|
|
head[0] = target[0];
|
|
head[2] = target[2];
|
|
}
|
|
} else {
|
|
if (target[1] > head[1]) {
|
|
head[1] += instantSpeed;
|
|
} else {
|
|
head[1] -= instantSpeed;
|
|
}
|
|
}
|
|
setHead(head[0], head[1], head[2]);
|
|
}
|
|
|
|
updatePosition();
|
|
}
|
|
|
|
private void updatePosition() {
|
|
if (arm != null && worldObj.isRemote) {
|
|
arm.setHead(headPosX, headPosY, headPosZ);
|
|
arm.updatePosition();
|
|
}
|
|
}
|
|
|
|
private void setHead(double x, double y, double z) {
|
|
this.headPosX = x;
|
|
this.headPosY = y;
|
|
this.headPosZ = z;
|
|
}
|
|
|
|
private double[] getHead() {
|
|
return new double[]{headPosX, headPosY, headPosZ};
|
|
}
|
|
|
|
private int[] getTarget() {
|
|
return new int[]{targetX, targetY, targetZ};
|
|
}
|
|
|
|
private void setTarget(int x, int y, int z) {
|
|
this.targetX = x;
|
|
this.targetY = y;
|
|
this.targetZ = z;
|
|
}
|
|
|
|
public void forceChunkLoading(Ticket ticket) {
|
|
if (chunkTicket == null) {
|
|
chunkTicket = ticket;
|
|
}
|
|
|
|
Set<ChunkCoordIntPair> chunks = Sets.newHashSet();
|
|
ChunkCoordIntPair quarryChunk = new ChunkCoordIntPair(xCoord >> 4, zCoord >> 4);
|
|
chunks.add(quarryChunk);
|
|
ForgeChunkManager.forceChunk(ticket, quarryChunk);
|
|
|
|
for (int chunkX = box.xMin >> 4; chunkX <= box.xMax >> 4; chunkX++) {
|
|
for (int chunkZ = box.zMin >> 4; chunkZ <= box.zMax >> 4; chunkZ++) {
|
|
ChunkCoordIntPair chunk = new ChunkCoordIntPair(chunkX, chunkZ);
|
|
ForgeChunkManager.forceChunk(ticket, chunk);
|
|
chunks.add(chunk);
|
|
}
|
|
}
|
|
|
|
if (placedBy != null && !(placedBy instanceof FakePlayer)) {
|
|
placedBy.addChatMessage(new ChatComponentTranslation(
|
|
"chat.buildcraft.quarry.chunkloadInfo",
|
|
xCoord, yCoord, zCoord, chunks.size()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean hasCustomInventoryName() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public AxisAlignedBB getRenderBoundingBox() {
|
|
return new Box(this).extendToEncompass(box).expand(50).getBoundingBox();
|
|
}
|
|
|
|
@Override
|
|
public Box getBox() {
|
|
return box;
|
|
}
|
|
|
|
@Override
|
|
public int[] getAccessibleSlotsFromSide(int side) {
|
|
return new int[]{};
|
|
}
|
|
|
|
@Override
|
|
public boolean canInsertItem(int p1, ItemStack p2, int p3) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean canExtractItem(int p1, ItemStack p2, int p3) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean acceptsControlMode(Mode mode) {
|
|
return mode == Mode.Off || mode == Mode.On || mode == Mode.Loop;
|
|
}
|
|
|
|
@Override
|
|
public boolean doDrop() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public ConnectOverride overridePipeConnection(IPipeTile.PipeType type, ForgeDirection with) {
|
|
return type == IPipeTile.PipeType.ITEM ? ConnectOverride.CONNECT : ConnectOverride.DEFAULT;
|
|
}
|
|
|
|
@Override
|
|
public int getLEDLevel(int led) {
|
|
if (led == 0) { // Red LED
|
|
return ledState & 15;
|
|
} else { // Green LED
|
|
return (ledState >> 4) > 0 ? 15 : 0;
|
|
}
|
|
}
|
|
}
|