buildcraft/common/buildcraft/robotics/EntityRobot.java

1557 lines
41 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.robotics;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.item.EntityFallingBlock;
import net.minecraft.entity.monster.IMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemSkull;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EntityDamageSource;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
import net.minecraft.util.StringUtils;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidContainerRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTankInfo;
import net.minecraftforge.fluids.IFluidHandler;
import buildcraft.BuildCraftCore;
import buildcraft.api.boards.RedstoneBoardNBT;
import buildcraft.api.boards.RedstoneBoardRegistry;
import buildcraft.api.boards.RedstoneBoardRobot;
import buildcraft.api.boards.RedstoneBoardRobotNBT;
import buildcraft.api.core.BCLog;
import buildcraft.api.core.BlockIndex;
import buildcraft.api.core.IZone;
import buildcraft.api.events.RobotEvent;
import buildcraft.api.robots.AIRobot;
import buildcraft.api.robots.DockingStation;
import buildcraft.api.robots.EntityRobotBase;
import buildcraft.api.robots.IRobotOverlayItem;
import buildcraft.api.robots.RobotManager;
import buildcraft.api.statements.StatementSlot;
import buildcraft.api.tiles.IDebuggable;
import buildcraft.core.DefaultProps;
import buildcraft.core.ItemWrench;
import buildcraft.core.LaserData;
import buildcraft.core.lib.RFBattery;
import buildcraft.core.lib.network.command.CommandWriter;
import buildcraft.core.lib.network.command.ICommandReceiver;
import buildcraft.core.lib.network.command.PacketCommand;
import buildcraft.core.lib.utils.NetworkUtils;
import buildcraft.core.proxy.CoreProxy;
import buildcraft.robotics.ai.AIRobotMain;
import buildcraft.robotics.ai.AIRobotShutdown;
import buildcraft.robotics.ai.AIRobotSleep;
import buildcraft.robotics.statements.ActionRobotWorkInArea;
import buildcraft.robotics.statements.ActionRobotWorkInArea.AreaType;
public class EntityRobot extends EntityRobotBase implements
IEntityAdditionalSpawnData, IInventory, IFluidHandler, ICommandReceiver, IDebuggable {
public static final ResourceLocation ROBOT_BASE = new ResourceLocation(
DefaultProps.TEXTURE_PATH_ROBOTS + "/robot_base.png");
public static final int MAX_WEARABLES = 8;
public static final int TRANSFER_INV_SLOTS = 4;
private static Set<Integer> blacklistedItemsForUpdate = Sets.newHashSet();
public LaserData laser = new LaserData();
public DockingStation linkedDockingStation;
public BlockIndex linkedDockingStationIndex;
public ForgeDirection linkedDockingStationSide;
public BlockIndex currentDockingStationIndex;
public ForgeDirection currentDockingStationSide;
public boolean isDocked = false;
public RedstoneBoardRobot board;
public AIRobotMain mainAI;
public ItemStack itemInUse;
public float itemAngle1 = 0;
public float itemAngle2 = 0;
public boolean itemActive = false;
public float itemActiveStage = 0;
public long lastUpdateTime = 0;
private DockingStation currentDockingStation;
private List<ItemStack> wearables = new ArrayList<ItemStack>();
private boolean needsUpdate = false;
private ItemStack[] inv = new ItemStack[TRANSFER_INV_SLOTS];
private FluidStack tank;
private int maxFluid = FluidContainerRegistry.BUCKET_VOLUME * 4;
private ResourceLocation texture;
private WeakHashMap<Entity, Long> unreachableEntities = new WeakHashMap<Entity, Long>();
private NBTTagList stackRequestNBT;
private RFBattery battery = new RFBattery(MAX_ENERGY, MAX_ENERGY, 100);
private boolean firstUpdateDone = false;
private boolean isActiveClient = false;
private long robotId = EntityRobotBase.NULL_ROBOT_ID;
private int energySpendPerCycle = 0;
private int ticksCharging = 0;
private float energyFX = 0;
private int steamDx = 0;
private int steamDy = -1;
private int steamDz = 0;
public EntityRobot(World world, RedstoneBoardRobotNBT boardNBT) {
this(world);
board = boardNBT.create(this);
dataWatcher.updateObject(16, board.getNBTHandler().getID());
if (!world.isRemote) {
mainAI = new AIRobotMain(this);
mainAI.start();
}
}
public EntityRobot(World world) {
super(world);
motionX = 0;
motionY = 0;
motionZ = 0;
ignoreFrustumCheck = true;
laser.isVisible = false;
entityCollisionReduction = 1F;
width = 0.25F;
height = 0.25F;
}
@Override
protected void entityInit() {
super.entityInit();
setNullBoundingBox();
preventEntitySpawning = false;
noClip = true;
isImmuneToFire = true;
this.func_110163_bv(); // persistenceRequired = true
dataWatcher.addObject(12, (float) 0);
dataWatcher.addObject(13, (float) 0);
dataWatcher.addObject(14, (float) 0);
dataWatcher.addObject(15, (byte) 0);
dataWatcher.addObject(16, "");
dataWatcher.addObject(17, (float) 0);
dataWatcher.addObject(18, (float) 0);
dataWatcher.addObject(19, 0);
dataWatcher.addObject(20, (byte) 0);
dataWatcher.addObject(21, 0);
}
protected void updateDataClient() {
laser.tail.x = dataWatcher.getWatchableObjectFloat(12);
laser.tail.y = dataWatcher.getWatchableObjectFloat(13);
laser.tail.z = dataWatcher.getWatchableObjectFloat(14);
laser.isVisible = dataWatcher.getWatchableObjectByte(15) == 1;
RedstoneBoardNBT<?> boardNBT = RedstoneBoardRegistry.instance.getRedstoneBoard(dataWatcher
.getWatchableObjectString(16));
if (boardNBT != null) {
texture = ((RedstoneBoardRobotNBT) boardNBT).getRobotTexture();
}
itemAngle1 = dataWatcher.getWatchableObjectFloat(17);
itemAngle2 = dataWatcher.getWatchableObjectFloat(18);
energySpendPerCycle = dataWatcher.getWatchableObjectInt(19);
isActiveClient = dataWatcher.getWatchableObjectByte(20) == 1;
battery.setEnergy(dataWatcher.getWatchableObjectInt(21));
}
protected void updateDataServer() {
dataWatcher.updateObject(12, (float) laser.tail.x);
dataWatcher.updateObject(13, (float) laser.tail.y);
dataWatcher.updateObject(14, (float) laser.tail.z);
dataWatcher.updateObject(15, (byte) (laser.isVisible ? 1 : 0));
dataWatcher.updateObject(17, itemAngle1);
dataWatcher.updateObject(18, itemAngle2);
}
public boolean isActive() {
if (worldObj.isRemote) {
return isActiveClient;
} else {
return mainAI.getActiveAI() instanceof AIRobotSleep || mainAI.getActiveAI() instanceof AIRobotShutdown;
}
}
protected void init() {
if (worldObj.isRemote) {
BuildCraftCore.instance.sendToServer(new PacketCommand(this, "requestInitialization", null));
}
}
public void setLaserDestination(float x, float y, float z) {
if (x != laser.tail.x || y != laser.tail.y || z != laser.tail.z) {
laser.tail.x = x;
laser.tail.y = y;
laser.tail.z = z;
needsUpdate = true;
}
}
public void showLaser() {
if (!laser.isVisible) {
laser.isVisible = true;
needsUpdate = true;
}
}
public void hideLaser() {
if (laser.isVisible) {
laser.isVisible = false;
needsUpdate = true;
}
}
protected void firstUpdate() {
if (stackRequestNBT != null) {
}
if (!worldObj.isRemote) {
getRegistry().registerRobot(this);
}
}
@Override
public String getCommandSenderName() {
return StatCollector.translateToLocal("item.robot.name");
}
@Override
public void onEntityUpdate() {
this.worldObj.theProfiler.startSection("bcEntityRobot");
if (!firstUpdateDone) {
firstUpdate();
firstUpdateDone = true;
}
if (ticksCharging > 0) {
ticksCharging--;
}
if (!worldObj.isRemote) {
// The client-side sleep indicator should also display if the robot is charging.
// To not break gates and other things checking for sleep, this is done here.
dataWatcher.updateObject(20, (byte) ((isActive() && ticksCharging == 0) ? 1 : 0));
dataWatcher.updateObject(21, getEnergy());
if (needsUpdate) {
updateDataServer();
needsUpdate = false;
}
}
if (worldObj.isRemote) {
updateDataClient();
updateRotationYaw(60.0f);
updateEnergyFX();
}
if (currentDockingStation != null) {
motionX = 0;
motionY = 0;
motionZ = 0;
posX = currentDockingStation.x() + 0.5F + currentDockingStation.side().offsetX * 0.5F;
posY = currentDockingStation.y() + 0.5F + currentDockingStation.side().offsetY * 0.5F;
posZ = currentDockingStation.z() + 0.5F + currentDockingStation.side().offsetZ * 0.5F;
}
if (!worldObj.isRemote) {
if (linkedDockingStation == null) {
if (linkedDockingStationIndex != null) {
linkedDockingStation = getRegistry().getStation(linkedDockingStationIndex.x,
linkedDockingStationIndex.y, linkedDockingStationIndex.z,
linkedDockingStationSide);
}
if (linkedDockingStation == null) {
shutdown("no docking station");
} else {
if (linkedDockingStation.robotTaking() != this) {
if (linkedDockingStation.robotIdTaking() == robotId) {
BCLog.logger.warn("A robot entity was not properly unloaded");
linkedDockingStation.invalidateRobotTakingEntity();
}
if (linkedDockingStation.robotTaking() != this) {
shutdown("wrong docking station");
}
}
}
}
if (currentDockingStationIndex != null && currentDockingStation == null) {
currentDockingStation = getRegistry().getStation(
currentDockingStationIndex.x,
currentDockingStationIndex.y,
currentDockingStationIndex.z,
currentDockingStationSide);
}
if (posY < -128) {
isDead = true;
BCLog.logger.info("Destroying robot " + this.toString() + " - Fallen into Void");
getRegistry().killRobot(this);
}
if (linkedDockingStation == null || linkedDockingStation.isInitialized()) {
this.worldObj.theProfiler.startSection("bcRobotAI");
mainAI.cycle();
this.worldObj.theProfiler.endSection();
if (energySpendPerCycle != mainAI.getActiveAI().getEnergyCost()) {
energySpendPerCycle = mainAI.getActiveAI().getEnergyCost();
dataWatcher.updateObject(19, energySpendPerCycle);
}
}
}
// tick all carried itemstacks
for (int i = 0; i < inv.length; i++) {
updateItem(inv[i], i, false);
}
// tick the item the robot is currently holding
updateItem(itemInUse, 0, true);
// do not tick wearables or equipment from EntityLiving
super.onEntityUpdate();
this.worldObj.theProfiler.endSection();
}
@Override
protected void updateEntityActionState() {
}
@Override
public boolean handleWaterMovement() {
return false;
}
@SideOnly(Side.CLIENT)
private void updateEnergyFX() {
energyFX += energySpendPerCycle;
if (energyFX >= (100 << (2 * Minecraft.getMinecraft().gameSettings.particleSetting))) {
energyFX = 0;
spawnEnergyFX();
}
}
@SideOnly(Side.CLIENT)
private void spawnEnergyFX() {
Minecraft.getMinecraft().effectRenderer.addEffect(new EntityRobotEnergyParticle(
worldObj,
posX + steamDx * 0.25, posY + steamDy * 0.25, posZ + steamDz * 0.25,
steamDx * 0.05, steamDy * 0.05, steamDz * 0.05,
energySpendPerCycle * 0.075F < 1 ? 1 : energySpendPerCycle * 0.075F));
}
@Override
public AxisAlignedBB getBoundingBox() {
return AxisAlignedBB.getBoundingBox(posX - 0.25F, posY - 0.25F, posZ - 0.25F, posX + 0.25F, posY + 0.25F, posZ + 0.25F);
}
public void setNullBoundingBox() {
width = 0F;
height = 0F;
boundingBox.minX = posX;
boundingBox.minY = posY;
boundingBox.minZ = posZ;
boundingBox.maxX = posX;
boundingBox.maxY = posY;
boundingBox.maxZ = posZ;
}
private void shutdown(String reason) {
if (!(mainAI.getDelegateAI() instanceof AIRobotShutdown)) {
BCLog.logger.info("Shutting down robot " + this.toString() + " - " + reason);
mainAI.startDelegateAI(new AIRobotShutdown(this));
}
}
@Override
public void writeSpawnData(ByteBuf data) {
data.writeByte(wearables.size());
for (ItemStack s : wearables) {
NetworkUtils.writeStack(data, s);
}
}
@Override
public void readSpawnData(ByteBuf data) {
int amount = data.readUnsignedByte();
while (amount > 0) {
wearables.add(NetworkUtils.readStack(data));
amount--;
}
init();
}
@Override
public ItemStack getHeldItem() {
return itemInUse;
}
@Override
public void setCurrentItemOrArmor(int i, ItemStack itemstack) {
}
@Override
public ItemStack[] getLastActiveItems() {
return new ItemStack[0];
}
@Override
protected void fall(float par1) {
}
@Override
protected void updateFallState(double par1, boolean par3) {
}
@Override
public void moveEntityWithHeading(float par1, float par2) {
this.setPosition(posX + motionX, posY + motionY, posZ + motionZ);
}
@Override
public boolean isOnLadder() {
return false;
}
public ResourceLocation getTexture() {
return texture;
}
@Override
public void writeEntityToNBT(NBTTagCompound nbt) {
super.writeEntityToNBT(nbt);
if (linkedDockingStationIndex != null) {
NBTTagCompound linkedStationNBT = new NBTTagCompound();
NBTTagCompound linkedStationIndexNBT = new NBTTagCompound();
linkedDockingStationIndex.writeTo(linkedStationIndexNBT);
linkedStationNBT.setTag("index", linkedStationIndexNBT);
linkedStationNBT.setByte("side", (byte) linkedDockingStationSide.ordinal());
nbt.setTag("linkedStation", linkedStationNBT);
}
if (currentDockingStationIndex != null) {
NBTTagCompound currentStationNBT = new NBTTagCompound();
NBTTagCompound currentStationIndexNBT = new NBTTagCompound();
currentDockingStationIndex.writeTo(currentStationIndexNBT);
currentStationNBT.setTag("index", currentStationIndexNBT);
currentStationNBT.setByte("side", (byte) currentDockingStationSide.ordinal());
nbt.setTag("currentStation", currentStationNBT);
}
NBTTagCompound nbtLaser = new NBTTagCompound();
laser.writeToNBT(nbtLaser);
nbt.setTag("laser", nbtLaser);
NBTTagCompound batteryNBT = new NBTTagCompound();
battery.writeToNBT(batteryNBT);
nbt.setTag("battery", batteryNBT);
if (itemInUse != null) {
NBTTagCompound itemNBT = new NBTTagCompound();
itemInUse.writeToNBT(itemNBT);
nbt.setTag("itemInUse", itemNBT);
nbt.setBoolean("itemActive", itemActive);
}
for (int i = 0; i < inv.length; ++i) {
NBTTagCompound stackNbt = new NBTTagCompound();
if (inv[i] != null) {
nbt.setTag("inv[" + i + "]", inv[i].writeToNBT(stackNbt));
}
}
if (wearables.size() > 0) {
NBTTagList wearableList = new NBTTagList();
for (ItemStack wearable : wearables) {
NBTTagCompound item = new NBTTagCompound();
wearable.writeToNBT(item);
wearableList.appendTag(item);
}
nbt.setTag("wearables", wearableList);
}
NBTTagCompound ai = new NBTTagCompound();
mainAI.writeToNBT(ai);
nbt.setTag("mainAI", ai);
if (mainAI.getDelegateAI() != board) {
NBTTagCompound boardNBT = new NBTTagCompound();
board.writeToNBT(boardNBT);
nbt.setTag("board", boardNBT);
}
nbt.setLong("robotId", robotId);
if (tank != null) {
NBTTagCompound tankNBT = new NBTTagCompound();
tank.writeToNBT(tankNBT);
nbt.setTag("tank", tankNBT);
}
}
@Override
public void readEntityFromNBT(NBTTagCompound nbt) {
super.readEntityFromNBT(nbt);
if (nbt.hasKey("linkedStation")) {
NBTTagCompound linkedStationNBT = nbt.getCompoundTag("linkedStation");
linkedDockingStationIndex = new BlockIndex(linkedStationNBT.getCompoundTag("index"));
linkedDockingStationSide = ForgeDirection.values()[linkedStationNBT.getByte("side")];
}
if (nbt.hasKey("currentStation")) {
NBTTagCompound currentStationNBT = nbt.getCompoundTag("currentStation");
currentDockingStationIndex = new BlockIndex(currentStationNBT.getCompoundTag("index"));
currentDockingStationSide = ForgeDirection.values()[currentStationNBT.getByte("side")];
}
laser.readFromNBT(nbt.getCompoundTag("laser"));
battery.readFromNBT(nbt.getCompoundTag("battery"));
wearables.clear();
if (nbt.hasKey("wearables")) {
NBTTagList list = nbt.getTagList("wearables", 10);
for (int i = 0; i < list.tagCount(); i++) {
ItemStack stack = ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(i));
if (stack != null) {
wearables.add(stack);
}
}
}
if (nbt.hasKey("itemInUse")) {
itemInUse = ItemStack.loadItemStackFromNBT(nbt.getCompoundTag("itemInUse"));
itemActive = nbt.getBoolean("itemActive");
}
for (int i = 0; i < inv.length; ++i) {
inv[i] = ItemStack.loadItemStackFromNBT(nbt.getCompoundTag("inv[" + i + "]"));
}
NBTTagCompound ai = nbt.getCompoundTag("mainAI");
mainAI = (AIRobotMain) AIRobot.loadAI(ai, this);
if (nbt.hasKey("board")) {
board = (RedstoneBoardRobot) AIRobot.loadAI(nbt.getCompoundTag("board"), this);
} else {
board = (RedstoneBoardRobot) mainAI.getDelegateAI();
}
if (board == null) {
board = RedstoneBoardRegistry.instance.getEmptyRobotBoard().create(this);
}
dataWatcher.updateObject(16, board.getNBTHandler().getID());
stackRequestNBT = nbt.getTagList("stackRequests", Constants.NBT.TAG_COMPOUND);
if (nbt.hasKey("robotId")) {
robotId = nbt.getLong("robotId");
}
if (nbt.hasKey("tank")) {
tank = FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag("tank"));
} else {
tank = null;
}
// Restore robot persistence on pre-6.1.9 robotics
this.func_110163_bv();
}
@Override
public void dock(DockingStation station) {
currentDockingStation = station;
setSteamDirection(
currentDockingStation.side.offsetX,
currentDockingStation.side.offsetY,
currentDockingStation.side.offsetZ);
currentDockingStationIndex = currentDockingStation.index();
currentDockingStationSide = currentDockingStation.side();
}
@Override
public void undock() {
if (currentDockingStation != null) {
currentDockingStation.release(this);
currentDockingStation = null;
setSteamDirection(0, -1, 0);
currentDockingStationIndex = null;
currentDockingStationSide = null;
}
}
@Override
public DockingStation getDockingStation() {
return currentDockingStation;
}
@Override
public void setMainStation(DockingStation station) {
if (linkedDockingStation != null && linkedDockingStation != station) {
linkedDockingStation.unsafeRelease(this);
}
linkedDockingStation = station;
if (station != null) {
linkedDockingStationIndex = linkedDockingStation.index();
linkedDockingStationSide = linkedDockingStation.side();
} else {
linkedDockingStationIndex = null;
linkedDockingStationSide = ForgeDirection.UNKNOWN;
}
}
@Override
public ItemStack getEquipmentInSlot(int var1) {
return null;
}
@Override
public int getSizeInventory() {
return inv.length;
}
@Override
public ItemStack getStackInSlot(int var1) {
return inv[var1];
}
@Override
public ItemStack decrStackSize(int var1, int var2) {
ItemStack result = inv[var1].splitStack(var2);
if (inv[var1].stackSize == 0) {
inv[var1] = null;
}
updateClientSlot(var1);
return result;
}
@Override
public ItemStack getStackInSlotOnClosing(int var1) {
ItemStack stack = inv[var1];
inv[var1] = null;
return stack;
}
@Override
public void setInventorySlotContents(int var1, ItemStack var2) {
inv[var1] = var2;
updateClientSlot(var1);
}
@Override
public String getInventoryName() {
return null;
}
@Override
public boolean hasCustomInventoryName() {
return false;
}
@Override
public int getInventoryStackLimit() {
return 64;
}
@Override
public void markDirty() {
}
public void updateClientSlot(final int slot) {
BuildCraftCore.instance.sendToEntity(new PacketCommand(this, "clientSetInventory", new CommandWriter() {
public void write(ByteBuf data) {
data.writeShort(slot);
NetworkUtils.writeStack(data, inv[slot]);
}
}), this);
}
@Override
public boolean isUseableByPlayer(EntityPlayer var1) {
return false;
}
@Override
public void openInventory() {
}
@Override
public void closeInventory() {
}
@Override
public boolean isItemValidForSlot(int var1, ItemStack var2) {
return inv[var1] == null
|| (inv[var1].isItemEqual(var2) && inv[var1].isStackable() && inv[var1].stackSize
+ var2.stackSize <= inv[var1].getItem().getItemStackLimit(inv[var1]));
}
@Override
public boolean isMoving() {
return motionX != 0 || motionY != 0 || motionZ != 0;
}
@Override
public void setItemInUse(ItemStack stack) {
itemInUse = stack;
BuildCraftCore.instance.sendToEntity(new PacketCommand(this, "clientSetItemInUse", new CommandWriter() {
public void write(ByteBuf data) {
NetworkUtils.writeStack(data, itemInUse);
}
}), this);
}
private void setSteamDirection(final int x, final int y, final int z) {
if (!worldObj.isRemote) {
BuildCraftCore.instance.sendToEntity(new PacketCommand(this, "setSteamDirection", new CommandWriter() {
public void write(ByteBuf data) {
data.writeInt(x);
data.writeShort(y);
data.writeInt(z);
}
}), this);
} else {
Vec3 v = Vec3.createVectorHelper(x, y, z);
v = v.normalize();
steamDx = (int) v.xCoord;
steamDy = (int) v.yCoord;
steamDz = (int) v.zCoord;
}
}
@Override
public void receiveCommand(String command, Side side, Object sender, ByteBuf stream) {
if (side.isClient()) {
if ("clientSetItemInUse".equals(command)) {
itemInUse = NetworkUtils.readStack(stream);
} else if ("clientSetInventory".equals(command)) {
int slot = stream.readUnsignedShort();
inv[slot] = NetworkUtils.readStack(stream);
} else if ("initialize".equals(command)) {
itemInUse = NetworkUtils.readStack(stream);
itemActive = stream.readBoolean();
} else if ("setItemActive".equals(command)) {
itemActive = stream.readBoolean();
itemActiveStage = 0;
lastUpdateTime = new Date().getTime();
if (!itemActive) {
setSteamDirection(0, -1, 0);
}
} else if ("setSteamDirection".equals(command)) {
setSteamDirection(stream.readInt(), stream.readShort(), stream.readInt());
} else if ("syncWearables".equals(command)) {
wearables.clear();
int amount = stream.readUnsignedByte();
while (amount > 0) {
wearables.add(NetworkUtils.readStack(stream));
amount--;
}
}
} else if (side.isServer()) {
EntityPlayer p = (EntityPlayer) sender;
if ("requestInitialization".equals(command)) {
BuildCraftCore.instance.sendToPlayer(p, new PacketCommand(this, "initialize", new CommandWriter() {
public void write(ByteBuf data) {
NetworkUtils.writeStack(data, itemInUse);
data.writeBoolean(itemActive);
}
}));
for (int i = 0; i < inv.length; ++i) {
final int j = i;
BuildCraftCore.instance.sendToPlayer(p, new PacketCommand(this, "clientSetInventory", new CommandWriter() {
public void write(ByteBuf data) {
data.writeShort(j);
NetworkUtils.writeStack(data, inv[j]);
}
}));
}
if (currentDockingStation != null) {
setSteamDirection(
currentDockingStation.side.offsetX,
currentDockingStation.side.offsetY,
currentDockingStation.side.offsetZ);
} else {
setSteamDirection(0, -1, 0);
}
}
}
}
@Override
public void setHealth(float par1) {
// deactivate health management
}
@Override
public boolean attackEntityFrom(DamageSource source, float f) {
// Ignore hits from mobs or when docked.
Entity src = source.getSourceOfDamage();
if (src != null && !(src instanceof EntityFallingBlock) && !(src instanceof IMob) && currentDockingStation == null) {
if (ForgeHooks.onLivingAttack(this, source, f)) {
return false;
}
if (!worldObj.isRemote) {
hurtTime = maxHurtTime = 10;
int mul = 2600;
for (ItemStack s : wearables) {
if (s.getItem() instanceof ItemArmor) {
mul = mul * 2 / (2 + ((ItemArmor) s.getItem()).damageReduceAmount);
} else {
mul *= 0.7;
}
}
int energy = Math.round(f * mul);
if (battery.getEnergyStored() - energy > 0) {
battery.setEnergy(battery.getEnergyStored() - energy);
return true;
} else {
onRobotHit(true);
}
}
return true;
}
return false;
}
@Override
public float getAimYaw() {
return itemAngle1;
}
@Override
public float getAimPitch() {
return itemAngle2;
}
@Override
public void aimItemAt(float yaw, float pitch) {
itemAngle1 = yaw;
itemAngle2 = pitch;
updateDataServer();
}
@Override
public void aimItemAt(int x, int y, int z) {
int deltaX = x - (int) Math.floor(posX);
int deltaY = y - (int) Math.floor(posY);
int deltaZ = z - (int) Math.floor(posZ);
if (deltaX != 0 || deltaZ != 0) {
itemAngle1 = (float) (Math.atan2(deltaZ, deltaX) * 180f / Math.PI) + 180f;
}
double d3 = MathHelper.sqrt_double(deltaX * deltaX + deltaZ * deltaZ);
itemAngle2 = (float) (-(Math.atan2(deltaY, d3) * 180.0D / Math.PI));
setSteamDirection(deltaX, deltaY, deltaZ);
updateDataServer();
}
private void updateRotationYaw(float maxStep) {
float step = MathHelper.wrapAngleTo180_float(itemAngle1 - rotationYaw);
if (step > maxStep) {
step = maxStep;
}
if (step < -maxStep) {
step = -maxStep;
}
rotationYaw = rotationYaw + step;
}
@Override
protected float func_110146_f(float targetYaw, float dist) {
if (worldObj.isRemote) {
float f2 = MathHelper.wrapAngleTo180_float(this.rotationYaw - this.renderYawOffset);
this.renderYawOffset += f2 * 0.5F;
float f3 = MathHelper.wrapAngleTo180_float(this.rotationYaw - this.renderYawOffset);
boolean flag = f3 < -90.0F || f3 >= 90.0F;
this.renderYawOffset = this.rotationYaw - f3;
if (f3 * f3 > 2500.0F) {
this.renderYawOffset += f3 * 0.2F;
}
float newDist = dist;
if (flag) {
newDist *= -1.0F;
}
return newDist;
}
return 0;
}
@Override
public void setItemActive(final boolean isActive) {
if (isActive != itemActive) {
itemActive = isActive;
BuildCraftCore.instance.sendToEntity(new PacketCommand(this, "setItemActive", new CommandWriter() {
public void write(ByteBuf data) {
data.writeBoolean(isActive);
}
}), this);
}
}
@Override
public RedstoneBoardRobot getBoard() {
return board;
}
@Override
public DockingStation getLinkedStation() {
return linkedDockingStation;
}
@SideOnly(Side.CLIENT)
@Override
public boolean isInRangeToRenderDist(double par1) {
return true;
}
@Override
public int getEnergy() {
return battery.getEnergyStored();
}
@Override
public RFBattery getBattery() {
return battery;
}
@Override
protected boolean canDespawn() {
return false;
}
public AIRobot getOverridingAI() {
return mainAI.getOverridingAI();
}
public void overrideAI(AIRobot ai) {
mainAI.setOverridingAI(ai);
}
public void attackTargetEntityWithCurrentItem(Entity par1Entity) {
if (MinecraftForge.EVENT_BUS.post(new AttackEntityEvent(
CoreProxy.proxy.getBuildCraftPlayer((WorldServer) worldObj, (int) posX, (int) posY, (int) posZ).get(),
par1Entity))) {
return;
}
if (par1Entity.canAttackWithItem()) {
if (!par1Entity.hitByEntity(this)) {
Multimap<String, AttributeModifier> attributes = itemInUse != null ? (Multimap<String, AttributeModifier>) itemInUse.getAttributeModifiers() : null;
float attackDamage = 2.0F;
int knockback = 0;
if (attributes != null) {
for (AttributeModifier modifier : attributes.get(SharedMonsterAttributes.attackDamage.getAttributeUnlocalizedName())) {
switch (modifier.getOperation()) {
case 0:
attackDamage += modifier.getAmount();
break;
case 1:
attackDamage *= modifier.getAmount();
break;
case 2:
attackDamage *= 1.0F + modifier.getAmount();
break;
}
}
}
if (par1Entity instanceof EntityLivingBase) {
attackDamage += EnchantmentHelper.getEnchantmentModifierLiving(this, (EntityLivingBase) par1Entity);
knockback += EnchantmentHelper.getKnockbackModifier(this, (EntityLivingBase) par1Entity);
}
if (attackDamage > 0.0F) {
int fireAspect = EnchantmentHelper.getFireAspectModifier(this);
if (par1Entity instanceof EntityLivingBase && fireAspect > 0 && !par1Entity.isBurning()) {
par1Entity.setFire(fireAspect * 4);
}
if (par1Entity.attackEntityFrom(new EntityDamageSource("robot", this), attackDamage)) {
this.setLastAttacker(par1Entity);
if (knockback > 0) {
par1Entity.addVelocity((double) (-MathHelper.sin(this.rotationYaw * (float) Math.PI / 180.0F) * (float) knockback * 0.5F), 0.1D, (double) (MathHelper.cos(this.rotationYaw * (float) Math.PI / 180.0F) * (float) knockback * 0.5F));
this.motionX *= 0.6D;
this.motionZ *= 0.6D;
this.setSprinting(false);
}
if (par1Entity instanceof EntityLivingBase) {
EnchantmentHelper.func_151384_a((EntityLivingBase) par1Entity, this);
}
EnchantmentHelper.func_151385_b(this, par1Entity);
ItemStack itemstack = itemInUse;
if (itemstack != null && par1Entity instanceof EntityLivingBase) {
itemstack.getItem().hitEntity(itemstack, (EntityLivingBase) par1Entity, this);
}
if (itemInUse.stackSize == 0) {
setItemInUse(null);
}
}
}
}
}
}
@Override
public IZone getZoneToWork() {
return getZone(ActionRobotWorkInArea.AreaType.WORK);
}
@Override
public IZone getZoneToLoadUnload() {
IZone zone = getZone(ActionRobotWorkInArea.AreaType.LOAD_UNLOAD);
if (zone == null) {
zone = getZoneToWork();
}
return zone;
}
private IZone getZone(AreaType areaType) {
if (linkedDockingStation != null) {
for (StatementSlot s : linkedDockingStation.getActiveActions()) {
if (s.statement instanceof ActionRobotWorkInArea
&& ((ActionRobotWorkInArea) s.statement).getAreaType() == areaType) {
IZone zone = ActionRobotWorkInArea.getArea(s);
if (zone != null) {
return zone;
}
}
}
}
return null;
}
@Override
public boolean containsItems() {
for (ItemStack element : inv) {
if (element != null) {
return true;
}
}
return false;
}
@Override
public boolean hasFreeSlot() {
for (ItemStack element : inv) {
if (element == null) {
return true;
}
}
return false;
}
@Override
public void unreachableEntityDetected(Entity entity) {
unreachableEntities.put(entity, worldObj.getTotalWorldTime() + 1200);
}
@Override
public boolean isKnownUnreachable(Entity entity) {
if (unreachableEntities.containsKey(entity)) {
if (unreachableEntities.get(entity) >= worldObj.getTotalWorldTime()) {
return true;
} else {
unreachableEntities.remove(entity);
return false;
}
} else {
return false;
}
}
protected void onRobotHit(boolean attacked) {
if (!worldObj.isRemote) {
if (attacked) {
convertToItems();
} else {
if (wearables.size() > 0) {
entityDropItem(wearables.remove(wearables.size() - 1), 0);
syncWearablesToClient();
} else if (itemInUse != null) {
entityDropItem(itemInUse, 0);
itemInUse = null;
} else {
convertToItems();
}
}
}
}
@Override
protected boolean interact(EntityPlayer player) {
ItemStack stack = player.getCurrentEquippedItem();
if (stack == null || stack.getItem() == null) {
return false;
}
RobotEvent.Interact robotInteractEvent = new RobotEvent.Interact(this, player, stack);
MinecraftForge.EVENT_BUS.post(robotInteractEvent);
if (robotInteractEvent.isCanceled()) {
return false;
}
if (player.isSneaking() && stack.getItem() == BuildCraftCore.wrenchItem) {
RobotEvent.Dismantle robotDismantleEvent = new RobotEvent.Dismantle(this, player);
MinecraftForge.EVENT_BUS.post(robotDismantleEvent);
if (robotDismantleEvent.isCanceled()) {
return false;
}
onRobotHit(false);
if (worldObj.isRemote) {
((ItemWrench) stack.getItem()).wrenchUsed(player, 0, 0, 0);
}
return true;
} else if (wearables.size() < MAX_WEARABLES && stack.getItem().isValidArmor(stack, 0, this)) {
if (!worldObj.isRemote) {
wearables.add(stack.splitStack(1));
syncWearablesToClient();
} else {
player.swingItem();
}
return true;
} else if (wearables.size() < MAX_WEARABLES && stack.getItem() instanceof IRobotOverlayItem && ((IRobotOverlayItem) stack.getItem()).isValidRobotOverlay(stack)) {
if (!worldObj.isRemote) {
wearables.add(stack.splitStack(1));
syncWearablesToClient();
} else {
player.swingItem();
}
return true;
} else if (wearables.size() < MAX_WEARABLES && stack.getItem() instanceof ItemSkull) {
if (!worldObj.isRemote) {
ItemStack skullStack = stack.splitStack(1);
initSkullItem(skullStack);
wearables.add(skullStack);
syncWearablesToClient();
} else {
player.swingItem();
}
return true;
} else {
return super.interact(player);
}
}
private void initSkullItem(ItemStack skullStack) {
if (skullStack.hasTagCompound()) {
NBTTagCompound nbttagcompound = skullStack.getTagCompound();
GameProfile gameProfile = null;
if (nbttagcompound.hasKey("SkullOwner", NBT.TAG_COMPOUND)) {
gameProfile = NBTUtil.func_152459_a(nbttagcompound.getCompoundTag("SkullOwner"));
} else if (nbttagcompound.hasKey("SkullOwner", NBT.TAG_STRING)
&& !StringUtils.isNullOrEmpty(nbttagcompound.getString("SkullOwner"))) {
gameProfile = new GameProfile(null, nbttagcompound.getString("SkullOwner"));
}
if (gameProfile != null && !StringUtils.isNullOrEmpty(gameProfile.getName())) {
if (!gameProfile.isComplete()
|| !gameProfile.getProperties().containsKey("textures")) {
gameProfile = MinecraftServer.getServer().func_152358_ax()
.func_152655_a(gameProfile.getName());
if (gameProfile != null) {
Property property = (Property) Iterables.getFirst(gameProfile
.getProperties().get("textures"), (Object) null);
if (property == null) {
gameProfile = MinecraftServer.getServer().func_147130_as()
.fillProfileProperties(gameProfile, true);
}
}
}
}
if (gameProfile != null && gameProfile.isComplete()
&& gameProfile.getProperties().containsKey("textures")) {
NBTTagCompound profileNBT = new NBTTagCompound();
NBTUtil.func_152460_a(profileNBT, gameProfile);
nbttagcompound.setTag("SkullOwner", profileNBT);
} else {
nbttagcompound.removeTag("SkullOwner");
}
}
}
private void syncWearablesToClient() {
BuildCraftCore.instance.sendToEntity(new PacketCommand(this, "syncWearables", new CommandWriter() {
public void write(ByteBuf data) {
data.writeByte(wearables.size());
for (ItemStack s : wearables) {
NetworkUtils.writeStack(data, s);
}
}
}), this);
}
private List<ItemStack> getDrops() {
List<ItemStack> drops = new ArrayList<ItemStack>();
drops.add(ItemRobot.createRobotStack(board.getNBTHandler(), battery.getEnergyStored()));
if (itemInUse != null) {
drops.add(itemInUse);
}
for (ItemStack element : inv) {
if (element != null) {
drops.add(element);
}
}
drops.addAll(wearables);
return drops;
}
private void convertToItems() {
if (!worldObj.isRemote && !isDead) {
if (mainAI != null) {
mainAI.abort();
}
List<ItemStack> drops = getDrops();
for (ItemStack stack : drops) {
entityDropItem(stack, 0);
}
isDead = true;
}
getRegistry().killRobot(this);
}
@Override
public void setDead() {
if (worldObj.isRemote) {
super.setDead();
}
}
@Override
public void onChunkUnload() {
getRegistry().unloadRobot(this);
}
@Override
public boolean canBePushed() {
return false;
}
@Override
protected void collideWithEntity(Entity par1Entity) {
}
@Override
public void applyEntityCollision(Entity par1Entity) {
}
public void setUniqueRobotId(long iRobotId) {
robotId = iRobotId;
}
@Override
public long getRobotId() {
return robotId;
}
@Override
public RobotRegistry getRegistry() {
return (RobotRegistry) RobotManager.registryProvider.getRegistry(worldObj);
}
@Override
public void releaseResources() {
getRegistry().releaseResources(this);
}
/**
* Tries to receive items in parameters, return items that are left after
* the operation.
*/
@Override
public ItemStack receiveItem(TileEntity tile, ItemStack stack) {
if (currentDockingStation != null
&& currentDockingStation.index().nextTo(new BlockIndex(tile))
&& mainAI != null) {
return mainAI.getActiveAI().receiveItem(stack);
} else {
return stack;
}
}
@Override
public int fill(ForgeDirection from, FluidStack resource, boolean doFill) {
int result;
if (tank != null && !tank.isFluidEqual(resource)) {
return 0;
}
if (tank == null) {
tank = new FluidStack(resource.getFluid(), 0);
}
if (tank.amount + resource.amount <= maxFluid) {
result = resource.amount;
if (doFill) {
tank.amount += resource.amount;
}
} else {
result = maxFluid - tank.amount;
if (doFill) {
tank.amount = maxFluid;
}
}
if (tank != null && tank.amount == 0) {
tank = null;
}
return result;
}
@Override
public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) {
if (tank != null && tank.isFluidEqual(resource)) {
return drain(from, resource.amount, doDrain);
} else {
return null;
}
}
@Override
public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) {
FluidStack result;
if (tank == null) {
result = null;
} else if (tank.amount <= maxDrain) {
result = tank.copy();
if (doDrain) {
tank = null;
}
} else {
result = tank.copy();
result.amount = maxDrain;
if (doDrain) {
tank.amount -= maxDrain;
}
}
if (tank != null && tank.amount == 0) {
tank = null;
}
return result;
}
@Override
public boolean canFill(ForgeDirection from, Fluid fluid) {
return tank == null
|| tank.amount == 0
|| (tank.amount < maxFluid
&& tank.getFluid().getID() == fluid.getID());
}
@Override
public boolean canDrain(ForgeDirection from, Fluid fluid) {
return tank != null
&& tank.amount != 0
&& tank.getFluid().getID() == fluid.getID();
}
@Override
public FluidTankInfo[] getTankInfo(ForgeDirection from) {
return new FluidTankInfo[]{new FluidTankInfo(tank, maxFluid)};
}
@SideOnly(Side.CLIENT)
public IIcon getItemIcon(ItemStack stack, int renderPass) {
IIcon iicon = super.getItemIcon(stack, renderPass);
if (iicon == null) {
iicon = stack.getItem().getIcon(stack, renderPass, null, itemInUse, 0);
}
return iicon;
}
@Override
public void getDebugInfo(List<String> info, ForgeDirection side, ItemStack debugger, EntityPlayer player) {
info.add("Robot " + board.getNBTHandler().getID() + " (" + getBattery().getEnergyStored() + "/" + getBattery().getMaxEnergyStored() + " RF)");
info.add(String.format("Position: %.2f, %.2f, %.2f", posX, posY, posZ));
info.add("AI tree:");
AIRobot aiRobot = mainAI;
while (aiRobot != null) {
info.add("- " + RobotManager.getAIRobotName(aiRobot.getClass()) + " (" + aiRobot.getEnergyCost() + " RF/t)");
if (aiRobot instanceof IDebuggable) {
((IDebuggable) aiRobot).getDebugInfo(info, side, debugger, player);
}
aiRobot = aiRobot.getDelegateAI();
}
}
public int receiveEnergy(int maxReceive, boolean simulate) {
int energyReceived = getBattery().receiveEnergy(maxReceive, simulate);
// 5 RF/t is set as the "sleep threshold" for detecting charging.
if (!simulate && energyReceived > 5 && ticksCharging <= 25) {
ticksCharging += 5;
}
return energyReceived;
}
public List<ItemStack> getWearables() {
return wearables;
}
private void updateItem(ItemStack stack, int i, boolean held) {
if (stack != null && stack.getItem() != null) {
int id = Item.getIdFromItem(stack.getItem());
// did this item not throw an exception before?
if (!blacklistedItemsForUpdate.contains(id)) {
try {
stack.getItem().onUpdate(stack, worldObj, this, i, held);
} catch (Exception e) {
// the item threw an exception, print it and do not let it update once more
e.printStackTrace();
blacklistedItemsForUpdate.add(id);
}
}
}
}
}