/** * 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.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; 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[4]; 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); } } } } }