package pneumaticCraft.common.tileentity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ai.EntityAITasks;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.NetHandlerPlayServer;
import net.minecraft.network.NetworkManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.Vec3;
import net.minecraft.world.ChunkPosition;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.IExtendedEntityProperties;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;
import net.minecraftforge.fluids.FluidTankInfo;
import net.minecraftforge.fluids.IFluidHandler;
import net.minecraftforge.fluids.IFluidTank;
import pneumaticCraft.api.drone.DroneConstructingEvent;
import pneumaticCraft.api.drone.IPathNavigator;
import pneumaticCraft.api.item.IProgrammable;
import pneumaticCraft.common.ai.DroneAIManager;
import pneumaticCraft.common.ai.FakePlayerItemInWorldManager;
import pneumaticCraft.common.ai.IDroneBase;
import pneumaticCraft.common.block.Blockss;
import pneumaticCraft.common.entity.EntityProgrammableController;
import pneumaticCraft.common.entity.living.EntityDrone.DroneFakePlayer;
import pneumaticCraft.common.item.ItemMachineUpgrade;
import pneumaticCraft.common.network.DescSynced;
import pneumaticCraft.common.network.LazySynced;
import pneumaticCraft.common.network.NetworkHandler;
import pneumaticCraft.common.network.PacketSpawnParticle;
import pneumaticCraft.common.progwidgets.IProgWidget;
import pneumaticCraft.common.progwidgets.ProgWidgetDroneConditionEntity;
import pneumaticCraft.common.progwidgets.ProgWidgetEntityAttack;
import pneumaticCraft.common.progwidgets.ProgWidgetEntityExport;
import pneumaticCraft.common.progwidgets.ProgWidgetEntityImport;
import pneumaticCraft.common.progwidgets.ProgWidgetStandby;
import pneumaticCraft.common.progwidgets.ProgWidgetSuicide;
import pneumaticCraft.common.progwidgets.ProgWidgetTeleport;
import pneumaticCraft.common.thirdparty.computercraft.ProgWidgetCC;
import pneumaticCraft.common.util.PneumaticCraftUtils;
import pneumaticCraft.lib.Log;
import com.mojang.authlib.GameProfile;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class TileEntityProgrammableController extends TileEntityPneumaticBase implements ISidedInventory,
IFluidHandler, IMinWorkingPressure, IDroneBase{
private ItemStack[] inventory;
private final int INVENTORY_SIZE = 5;
private final FluidTank tank = new FluidTank(16000);
private DroneAIManager aiManager;
private DroneFakePlayer fakePlayer;
private ItemStack[] droneItems;
private final Map<String, IExtendedEntityProperties> properties = new HashMap<String, IExtendedEntityProperties>();
private List<IProgWidget> progWidgets = new ArrayList<IProgWidget>();
private final int[] redstoneLevels = new int[6];
private String droneName = "";
@DescSynced
private double targetX, targetY, targetZ;
@DescSynced
@LazySynced
private double curX, curY, curZ;
public double oldCurX, oldCurY, oldCurZ;
private EntityProgrammableController drone;
@DescSynced
private int diggingX, diggingY, diggingZ;
private int dispenserUpgrades, speedUpgrades;
private static final Set<Class<? extends IProgWidget>> WIDGET_BLACKLIST = new HashSet<Class<? extends IProgWidget>>();
static {
WIDGET_BLACKLIST.add(ProgWidgetCC.class);
WIDGET_BLACKLIST.add(ProgWidgetEntityAttack.class);
WIDGET_BLACKLIST.add(ProgWidgetDroneConditionEntity.class);
WIDGET_BLACKLIST.add(ProgWidgetStandby.class);
WIDGET_BLACKLIST.add(ProgWidgetSuicide.class);
WIDGET_BLACKLIST.add(ProgWidgetTeleport.class);
WIDGET_BLACKLIST.add(ProgWidgetEntityExport.class);
WIDGET_BLACKLIST.add(ProgWidgetEntityImport.class);
}
public TileEntityProgrammableController(){
super(5, 7, 5000);
inventory = new ItemStack[INVENTORY_SIZE];
// setUpgradeSlots(new int[]{UPGRADE_SLOT_START, 1, 2, UPGRADE_SLOT_END});
setUpgradeSlots(new int[]{1, 2, 3, 4});
MinecraftForge.EVENT_BUS.post(new DroneConstructingEvent(this));
}
@Override
public void updateEntity(){
super.updateEntity();
oldCurX = curX;
oldCurY = curY;
oldCurZ = curZ;
if(PneumaticCraftUtils.distBetween(getPosition(), targetX, targetY, targetZ) <= getSpeed()) {
curX = targetX;
curY = targetY;
curZ = targetZ;
} else {
Vec3 vec = Vec3.createVectorHelper(targetX - curX, targetY - curY, targetZ - curZ).normalize();
curX += vec.xCoord * getSpeed();
curY += vec.yCoord * getSpeed();
curZ += vec.zCoord * getSpeed();
}
if(!worldObj.isRemote) {
getAIManager();
if(worldObj.getTotalWorldTime() % 40 == 0) {
dispenserUpgrades = getUpgrades(ItemMachineUpgrade.UPGRADE_DISPENSER_DAMAGE);
speedUpgrades = getUpgrades(ItemMachineUpgrade.UPGRADE_SPEED_DAMAGE);
for(int i = getDroneSlots(); i < 36; i++) {
ItemStack stack = getFakePlayer().inventory.getStackInSlot(i);
if(stack != null) {
worldObj.spawnEntityInWorld(new EntityItem(worldObj, xCoord + 0.5, yCoord + 1.5, zCoord + 0.5, stack));
getFakePlayer().inventory.setInventorySlotContents(i, null);
}
}
tank.setCapacity((dispenserUpgrades + 1) * 16000);
if(tank.getFluidAmount() > tank.getCapacity()) {
tank.getFluid().amount = tank.getCapacity();
}
}
for(int i = 0; i < 4; i++) {
getFakePlayer().theItemInWorldManager.updateBlockRemoving();
}
if(getPressure(ForgeDirection.UNKNOWN) >= getMinWorkingPressure()) {
if(!aiManager.isIdling()) addAir(-10, ForgeDirection.UNKNOWN);
aiManager.onUpdateTasks();
}
} else {
if(drone == null || drone.isDead) {
drone = new EntityProgrammableController(worldObj, this);
drone.posX = curX;
drone.posY = curY;
drone.posZ = curZ;
worldObj.spawnEntityInWorld(drone);
}
drone.setPosition(curX, curY, curZ);
// drone.getMoveHelper().setMoveTo(curX, curY, curZ, 0);
/* drone.prevPosX = oldCurX;
drone.prevPosY = oldCurY;
drone.prevPosZ = oldCurZ;*/
//drone.getMoveHelper().setMoveTo(curX, curY, curZ, getSpeed());
}
}
@Override
public void onDescUpdate(){
super.onDescUpdate();
if(drone != null) {
drone.setDead();
}
}
private double getSpeed(){
return Math.min(10, speedUpgrades) * 0.1 + 0.1;
}
private void initializeFakePlayer(){
String playerUUID = null;
String playerName = "Drone";
fakePlayer = new DroneFakePlayer((WorldServer)worldObj, new GameProfile(playerUUID != null ? UUID.fromString(playerUUID) : null, playerName), new FakePlayerItemInWorldManager(worldObj, fakePlayer, this), this);
fakePlayer.playerNetServerHandler = new NetHandlerPlayServer(MinecraftServer.getServer(), new NetworkManager(false), fakePlayer);
fakePlayer.inventory = new InventoryPlayer(fakePlayer){
private ItemStack oldStack;
@Override
public int getSizeInventory(){
return getDroneSlots();
}
@Override
public void setInventorySlotContents(int slot, ItemStack stack){
super.setInventorySlotContents(slot, stack);
if(slot == 0) {
if(oldStack != null) {
getFakePlayer().getAttributeMap().removeAttributeModifiers(oldStack.getAttributeModifiers());
}
if(stack != null) {
getFakePlayer().getAttributeMap().applyAttributeModifiers(stack.getAttributeModifiers());
}
oldStack = stack;
}
}
};
}
@Override
public void handleGUIButtonPress(int buttonID, EntityPlayer player){
}
@Override
public boolean hasCustomInventoryName(){
return false;
}
/**
* Returns the number of slots in the inventory.
*/
@Override
public int getSizeInventory(){
return inventory.length + getDroneSlots();
}
/**
* Returns the stack in slot i
*/
@Override
public ItemStack getStackInSlot(int slot){
return slot < 5 ? inventory[slot] : getFakePlayer().inventory.getStackInSlot(slot - 5);
}
@Override
public ItemStack decrStackSize(int slot, int amount){
ItemStack itemStack = getStackInSlot(slot);
if(itemStack != null) {
if(itemStack.stackSize <= amount) {
setInventorySlotContents(slot, null);
} else {
itemStack = itemStack.splitStack(amount);
if(itemStack.stackSize == 0) {
setInventorySlotContents(slot, null);
}
}
}
return itemStack;
}
@Override
public ItemStack getStackInSlotOnClosing(int slot){
ItemStack itemStack = getStackInSlot(slot);
if(itemStack != null) {
setInventorySlotContents(slot, null);
}
return itemStack;
}
@Override
public void setInventorySlotContents(int slot, ItemStack itemStack){
if(slot < 5) {
inventory[slot] = itemStack;
if(itemStack != null && itemStack.stackSize > getInventoryStackLimit()) {
itemStack.stackSize = getInventoryStackLimit();
}
if(slot == 0) {
if(itemStack != null && isProgrammableAndValidForDrone(this, itemStack)) {
progWidgets = TileEntityProgrammer.getProgWidgets(itemStack);
if(!worldObj.isRemote) getAIManager().setWidgets(progWidgets);
} else {
progWidgets.clear();
targetX = xCoord + 0.5;
targetY = yCoord + 0.6;
targetZ = zCoord + 0.5;
boolean updateNeighbours = false;
for(int i = 0; i < redstoneLevels.length; i++) {
if(redstoneLevels[i] > 0) {
redstoneLevels[i] = 0;
updateNeighbours = true;
}
}
if(updateNeighbours) updateNeighbours();
}
getAIManager();
}
} else {
getFakePlayer().inventory.setInventorySlotContents(slot - 5, itemStack);
}
}
@Override
public String getInventoryName(){
return Blockss.programmableController.getUnlocalizedName();
}
@Override
public int getInventoryStackLimit(){
return 64;
}
@Override
public void readFromNBT(NBTTagCompound tag){
super.readFromNBT(tag);
NBTTagList tagList = tag.getTagList("Items", 10);
inventory = new ItemStack[INVENTORY_SIZE];
for(int i = 0; i < tagList.tagCount(); ++i) {
NBTTagCompound tagCompound = tagList.getCompoundTagAt(i);
byte slot = tagCompound.getByte("Slot");
if(slot >= 0 && slot < inventory.length) {
inventory[slot] = ItemStack.loadItemStackFromNBT(tagCompound);
}
}
tank.readFromNBT(tag.getCompoundTag("tank"));
NBTTagList droneItemTag = tag.getTagList("droneItems", 10);
droneItems = new ItemStack[getDroneSlots()];
for(int i = 0; i < droneItemTag.tagCount(); ++i) {
NBTTagCompound tagCompound = droneItemTag.getCompoundTagAt(i);
byte slot = tagCompound.getByte("Slot");
if(slot >= 0 && slot < droneItems.length) {
droneItems[slot] = ItemStack.loadItemStackFromNBT(tagCompound);
}
}
NBTTagList extendedList = tag.getTagList("extendedProperties", 10);
for(int i = 0; i < extendedList.tagCount(); ++i) {
NBTTagCompound propertyTag = extendedList.getCompoundTagAt(i);
String key = propertyTag.getString("key");
IExtendedEntityProperties property = properties.get(key);
if(property != null) {
property.loadNBTData(propertyTag);
} else {
Log.warning("Extended entity property \"" + key + "\" doesn't exist in a Programmable Controller");
}
}
}
@Override
public void writeToNBT(NBTTagCompound tag){
super.writeToNBT(tag);
NBTTagList tagList = new NBTTagList();
for(int currentIndex = 0; currentIndex < inventory.length; ++currentIndex) {
if(inventory[currentIndex] != null) {
NBTTagCompound tagCompound = new NBTTagCompound();
tagCompound.setByte("Slot", (byte)currentIndex);
inventory[currentIndex].writeToNBT(tagCompound);
tagList.appendTag(tagCompound);
}
}
tag.setTag("Items", tagList);
NBTTagCompound tankTag = new NBTTagCompound();
tank.writeToNBT(tankTag);
tag.setTag("tank", tankTag);
NBTTagList droneItems = new NBTTagList();
for(int currentIndex = 0; currentIndex < getDroneSlots(); ++currentIndex) {
if(getFakePlayer().inventory.getStackInSlot(currentIndex) != null) {
NBTTagCompound tagCompound = new NBTTagCompound();
tagCompound.setByte("Slot", (byte)currentIndex);
getFakePlayer().inventory.getStackInSlot(currentIndex).writeToNBT(tagCompound);
droneItems.appendTag(tagCompound);
}
}
tag.setTag("droneItems", droneItems);
NBTTagList extendedList = new NBTTagList();
for(Map.Entry<String, IExtendedEntityProperties> entry : properties.entrySet()) {
NBTTagCompound propertyTag = new NBTTagCompound();
propertyTag.setString("key", entry.getKey());
entry.getValue().saveNBTData(propertyTag);
extendedList.appendTag(propertyTag);
}
tag.setTag("extendedProperties", extendedList);
}
@Override
protected void onFirstServerUpdate(){
super.onFirstServerUpdate();
setInventorySlotContents(0, getStackInSlot(0));
}
private int getDroneSlots(){
return worldObj != null && worldObj.isRemote ? 0 : Math.min(36, 1 + dispenserUpgrades);
}
@Override
public boolean isItemValidForSlot(int i, ItemStack itemstack){
return i != 0 || isProgrammableAndValidForDrone(this, itemstack);
}
public static boolean isProgrammableAndValidForDrone(IDroneBase drone, ItemStack programmable){
if(programmable != null && programmable.getItem() instanceof IProgrammable && ((IProgrammable)programmable.getItem()).canProgram(programmable) && ((IProgrammable)programmable.getItem()).usesPieces(programmable)) {
List<IProgWidget> widgets = TileEntityProgrammer.getProgWidgets(programmable);
for(IProgWidget widget : widgets) {
if(!drone.isProgramApplicable(widget)) return false;
}
return true;
}
return false;
}
@Override
public boolean isUseableByPlayer(EntityPlayer var1){
return isGuiUseableByPlayer(var1);
}
@Override
public void openInventory(){}
@Override
public void closeInventory(){}
@Override
public int[] getAccessibleSlotsFromSide(int var1){
if(ForgeDirection.getOrientation(var1) == ForgeDirection.UP) {
return new int[]{0};
} else {
if(worldObj.isRemote) return new int[0];
int[] mainInv = new int[getSizeInventory()];
for(int i = 0; i < getSizeInventory(); i++) {
mainInv[i] = i + 5;
}
return mainInv;
}
}
@Override
public boolean canInsertItem(int slot, ItemStack stack, int p_102007_3_){
return isItemValidForSlot(slot, stack);
}
@Override
public boolean canExtractItem(int p_102008_1_, ItemStack p_102008_2_, int p_102008_3_){
return true;
}
@Override
public float getMinWorkingPressure(){
return 3;
}
@Override
@SideOnly(Side.CLIENT)
public AxisAlignedBB getRenderBoundingBox(){
return INFINITE_EXTENT_AABB;
}
/*
* IDrone
*/
@Override
public float getPressure(ItemStack iStack){
return getPressure(ForgeDirection.UNKNOWN);
}
@Override
public void addAir(ItemStack iStack, int amount){
addAir(amount, ForgeDirection.UNKNOWN);
}
@Override
public float maxPressure(ItemStack iStack){
return 7;
}
@Override
public World getWorld(){
return worldObj;
}
@Override
public IFluidTank getTank(){
return tank;
}
@Override
public IInventory getInventory(){
return getFakePlayer().inventory;
}
@Override
public Vec3 getPosition(){
if(curX == 0 && curY == 0 && curZ == 0) {
curX = xCoord + 0.5;
curY = yCoord + 0.6;
curZ = zCoord + 0.5;
targetX = curX;
targetY = curY;
targetZ = curZ;
}
return Vec3.createVectorHelper(curX, curY, curZ);
}
@Override
public IPathNavigator getPathNavigator(){
return new IPathNavigator(){
@Override
public boolean moveToXYZ(double x, double y, double z){
int blockX = (int)Math.floor(x);
int blockY = (int)Math.floor(y);
int blockZ = (int)Math.floor(z);
if(isBlockValidPathfindBlock(blockX, blockY, blockZ)) {
targetX = x + 0.5;
targetY = y - 0.3;
targetZ = z + 0.5;
return true;
} else {
return false;
}
}
@Override
public boolean moveToEntity(Entity entity){
return moveToXYZ(entity.posX, entity.posY + 0.3, entity.posZ);
}
@Override
public boolean hasNoPath(){
return targetX == curX && targetY == curY && targetZ == curZ;
}
@Override
public boolean isGoingToTeleport(){
return false;
}
};
}
@Override
public void sendWireframeToClient(int x, int y, int z){}
@Override
public EntityPlayerMP getFakePlayer(){
if(fakePlayer == null) initializeFakePlayer();
if(droneItems != null) {
ItemStack[] copyDroneItems = droneItems;
droneItems = null;
for(int i = 0; i < copyDroneItems.length; i++) {
fakePlayer.inventory.setInventorySlotContents(i, copyDroneItems[i]);
}
}
return fakePlayer;
}
@Override
public boolean isBlockValidPathfindBlock(int x, int y, int z){
return worldObj.isAirBlock(x, y, z);
}
@Override
public void dropItem(ItemStack stack){
Vec3 pos = getPosition();
worldObj.spawnEntityInWorld(new EntityItem(worldObj, pos.xCoord, pos.yCoord, pos.zCoord, stack));
}
@Override
public void setDugBlock(int x, int y, int z){
diggingX = x;
diggingY = y;
diggingZ = z;
}
public ChunkPosition getDugPosition(){
return diggingX != 0 || diggingY != 0 || diggingZ != 0 ? new ChunkPosition(diggingX, diggingY, diggingZ) : null;
}
@Override
public List<IProgWidget> getProgWidgets(){
return progWidgets;
}
@Override
public void setActiveProgram(IProgWidget widget){}
@Override
public boolean isProgramApplicable(IProgWidget widget){
return !WIDGET_BLACKLIST.contains(widget.getClass());
}
@Override
public EntityAITasks getTargetAI(){
return null;
}
@Override
public IExtendedEntityProperties getProperty(String key){
return properties.get(key);
}
@Override
public void setProperty(String key, IExtendedEntityProperties property){
properties.put(key, property);
}
@Override
public void setEmittingRedstone(ForgeDirection orientation, int emittingRedstone){
redstoneLevels[orientation.ordinal()] = emittingRedstone;
updateNeighbours();
}
public int getEmittingRedstone(int direction){
return redstoneLevels[direction];
}
@Override
public void setName(String string){
droneName = string;
if(drone != null) drone.setCustomNameTag(droneName);
if(inventory[0] != null) inventory[0].setStackDisplayName(string);
}
@Override
public void setCarryingEntity(Entity entity){
Log.warning("Drone AI setting carrying entity. However a Programmable Controller can't carry entities!");
new Throwable().printStackTrace();
}
@Override
public Entity getCarryingEntity(){
return null;
}
@Override
public boolean isAIOverriden(){
return false;
}
@Override
public void onItemPickupEvent(EntityItem curPickingUpEntity, int stackSize){
}
/*
* Liquid handling
*/
@Override
public int fill(ForgeDirection from, FluidStack resource, boolean doFill){
return tank.fill(resource, doFill);
}
@Override
public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain){
return tank.getFluid() != null && tank.getFluid().isFluidEqual(resource) ? tank.drain(resource.amount, doDrain) : null;
}
@Override
public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain){
return tank.drain(maxDrain, doDrain);
}
@Override
public boolean canFill(ForgeDirection from, Fluid fluid){
return true;
}
@Override
public boolean canDrain(ForgeDirection from, Fluid fluid){
return true;
}
@Override
public FluidTankInfo[] getTankInfo(ForgeDirection from){
return new FluidTankInfo[]{new FluidTankInfo(tank)};
}
@Override
public void overload(){
for(int i = 0; i < 10; i++) {
NetworkHandler.sendToAllAround(new PacketSpawnParticle("largesmoke", xCoord + worldObj.rand.nextDouble(), yCoord + 1, zCoord + worldObj.rand.nextDouble(), 0, 0, 0), worldObj);
}
}
@Override
public DroneAIManager getAIManager(){
if(aiManager == null && worldObj != null && !worldObj.isRemote) {
aiManager = new DroneAIManager(this, new ArrayList<IProgWidget>());
aiManager.setWidgets(getProgWidgets());
aiManager.dontStopWhenEndReached();
}
return aiManager;
}
@Override
public void updateLabel(){}
@Override
public void addDebugEntry(String message){
}
@Override
public void addDebugEntry(String message, ChunkPosition pos){
}
}