package micdoodle8.mods.galacticraft.core.tile;
import com.mojang.authlib.GameProfile;
import micdoodle8.mods.galacticraft.api.entity.ITelemetry;
import micdoodle8.mods.galacticraft.api.prefab.entity.EntitySpaceshipBase;
import micdoodle8.mods.galacticraft.api.vector.BlockVec3;
import micdoodle8.mods.galacticraft.api.vector.BlockVec3Dim;
import micdoodle8.mods.galacticraft.core.GalacticraftCore;
import micdoodle8.mods.galacticraft.core.entities.player.GCPlayerStats;
import micdoodle8.mods.galacticraft.core.network.PacketSimple;
import micdoodle8.mods.galacticraft.core.network.PacketSimple.EnumSimplePacket;
import micdoodle8.mods.galacticraft.core.util.GCCoreUtil;
import micdoodle8.mods.galacticraft.core.util.GCLog;
import micdoodle8.mods.galacticraft.core.util.PlayerUtil;
import micdoodle8.mods.galacticraft.core.util.WorldUtil;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.monster.EntitySkeleton;
import net.minecraft.entity.monster.EntityZombie;
import net.minecraft.entity.passive.*;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockPos;
import net.minecraft.util.ITickable;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.WorldProvider;
import net.minecraftforge.fml.common.network.NetworkRegistry.TargetPoint;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
public class TileEntityTelemetry extends TileEntity implements ITickable
{
public Class clientClass;
public int[] clientData = { -1 };
public String clientName;
public GameProfile clientGameProfile = null;
public static HashSet<BlockVec3Dim> loadedList = new HashSet<BlockVec3Dim>();
public Entity linkedEntity;
private UUID toUpdate = null;
private int pulseRate = 400;
private int lastHurttime = 0;
private int ticks = 0;
@Override
public void onLoad()
{
if (this.worldObj.isRemote)
{
loadedList.add(new BlockVec3Dim(this));
}
}
@Override
public void invalidate()
{
super.invalidate();
if (this.worldObj.isRemote)
{
loadedList.remove(new BlockVec3Dim(this));
}
}
@Override
public void update()
{
if (!this.worldObj.isRemote && ++this.ticks % 2 == 0)
{
if (this.toUpdate != null)
{
this.addTrackedEntity(this.toUpdate);
this.toUpdate = null;
}
String name;
int[] data = { -1, -1, -1, -1, -1 };
String strUUID = "";
if (linkedEntity != null)
{
//Help the Garbage Collector
if (linkedEntity.isDead)
{
linkedEntity = null;
name = "";
//TODO: track players after death and respawn? or not?
}
else
{
if (linkedEntity instanceof EntityPlayerMP)
{
name = "$" + ((EntityPlayerMP) linkedEntity).getName();
}
else
{
name = (String) EntityList.classToStringMapping.get(linkedEntity.getClass());
}
if (name == null)
{
GCLog.info("Telemetry Unit: Error finding name for " + linkedEntity.getClass().getSimpleName());
name = "";
}
double xmotion = linkedEntity.motionX;
double ymotion = linkedEntity instanceof EntityLivingBase ? linkedEntity.motionY + 0.078D : linkedEntity.motionY;
double zmotion = linkedEntity.motionZ;
data[2] = (int) (MathHelper.sqrt_double(xmotion * xmotion + ymotion * ymotion + zmotion * zmotion) * 2000D);
if (linkedEntity instanceof ITelemetry)
{
((ITelemetry) linkedEntity).transmitData(data);
}
else if (linkedEntity instanceof EntityLivingBase)
{
EntityLivingBase eLiving = (EntityLivingBase) linkedEntity;
data[0] = eLiving.hurtTime;
//Calculate a "pulse rate" based on motion and taking damage
this.pulseRate--;
if (eLiving.hurtTime > this.lastHurttime)
{
this.pulseRate += 100;
}
this.lastHurttime = eLiving.hurtTime;
if (eLiving.ridingEntity != null)
{
data[2] /= 4; //reduced pulse effect if riding a vehicle
}
else if (data[2] > 1)
{
this.pulseRate += 2;
}
this.pulseRate += Math.max(data[2] - pulseRate, 0) / 4;
if (this.pulseRate > 2000)
{
this.pulseRate = 2000;
}
if (this.pulseRate < 400)
{
this.pulseRate = 400;
}
data[2] = this.pulseRate / 10;
data[1] = (int) (eLiving.getHealth() * 100 / eLiving.getMaxHealth());
if (eLiving instanceof EntityPlayerMP)
{
data[3] = ((EntityPlayerMP) eLiving).getFoodStats().getFoodLevel() * 5;
GCPlayerStats stats = GCPlayerStats.get(eLiving);
data[4] = stats.getAirRemaining() * 4096 + stats.getAirRemaining2();
UUID uuid = ((EntityPlayerMP) eLiving).getUniqueID();
if (uuid != null)
{
strUUID = uuid.toString();
}
}
else if (eLiving instanceof EntityHorse)
{
data[3] = ((EntityHorse) eLiving).getHorseType();
data[4] = ((EntityHorse) eLiving).getHorseVariant();
}
else if (eLiving instanceof EntityVillager)
{
data[3] = ((EntityVillager) eLiving).getProfession();
data[4] = ((EntityVillager) eLiving).getGrowingAge();
}
else if (eLiving instanceof EntityWolf)
{
data[3] = ((EntityWolf) eLiving).getCollarColor().getDyeDamage();
data[4] = ((EntityWolf) eLiving).isBegging() ? 1 : 0;
}
else if (eLiving instanceof EntitySheep)
{
data[3] = ((EntitySheep) eLiving).getFleeceColor().getDyeDamage();
data[4] = ((EntitySheep) eLiving).getSheared() ? 1 : 0;
}
else if (eLiving instanceof EntityOcelot)
{
data[3] = ((EntityOcelot) eLiving).getTameSkin();
}
else if (eLiving instanceof EntitySkeleton)
{
data[3] = ((EntitySkeleton) eLiving).getSkeletonType();
}
else if (eLiving instanceof EntityZombie)
{
data[3] = ((EntityZombie) eLiving).isVillager() ? 1 : 0;
data[4] = ((EntityZombie) eLiving).isChild() ? 1 : 0;
}
}
}
}
else
{
name = "";
}
GalacticraftCore.packetPipeline.sendToAllAround(new PacketSimple(EnumSimplePacket.C_UPDATE_TELEMETRY, GCCoreUtil.getDimensionID(this.worldObj), new Object[] { this.getPos(), name, data[0], data[1], data[2], data[3], data[4], strUUID }), new TargetPoint(GCCoreUtil.getDimensionID(this.worldObj), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ(), 320D));
}
}
@SideOnly(Side.CLIENT)
public void receiveUpdate(List <Object> data, int dimID)
{
String name = (String) data.get(1);
if (name.startsWith("$"))
{
//It's a player name
this.clientClass = EntityPlayerMP.class;
String strName = name.substring(1);
this.clientName = strName;
this.clientGameProfile = PlayerUtil.getSkinForName(strName, (String) data.get(7), dimID);
}
else
{
this.clientClass = EntityList.stringToClassMapping.get(name);
}
this.clientData = new int[5];
for (int i = 2; i < 7; i++)
{
this.clientData[i - 2] = (Integer) data.get(i);
}
}
@Override
public void readFromNBT(NBTTagCompound nbt)
{
super.readFromNBT(nbt);
Long msb = nbt.getLong("entityUUIDMost");
Long lsb = nbt.getLong("entityUUIDLeast");
this.toUpdate = new UUID(msb, lsb);
}
@Override
public void writeToNBT(NBTTagCompound nbt)
{
super.writeToNBT(nbt);
if (this.linkedEntity != null && !this.linkedEntity.isDead)
{
nbt.setLong("entityUUIDMost", this.linkedEntity.getUniqueID().getMostSignificantBits());
nbt.setLong("entityUUIDLeast", this.linkedEntity.getUniqueID().getLeastSignificantBits());
}
}
public void addTrackedEntity(UUID uuid)
{
this.pulseRate = 400;
this.lastHurttime = 0;
List<Entity> eList = this.worldObj.loadedEntityList;
for (Entity e : eList)
{
if (e.getUniqueID().equals(uuid))
{
this.linkedEntity = e;
if (e instanceof EntitySpaceshipBase)
{
((EntitySpaceshipBase) e).addTelemetry(this);
}
return;
}
}
//TODO Add some kind of watcher to add the entity when next loaded
this.linkedEntity = null;
}
public void addTrackedEntity(Entity e)
{
this.pulseRate = 400;
this.lastHurttime = 0;
this.linkedEntity = e;
if (e instanceof EntitySpaceshipBase)
{
((EntitySpaceshipBase) e).addTelemetry(this);
}
}
public void removeTrackedEntity()
{
this.pulseRate = 400;
this.linkedEntity = null;
}
public static TileEntityTelemetry getNearest(TileEntity te)
{
if (te == null)
{
return null;
}
BlockVec3 target = new BlockVec3(te);
int distSq = 1025;
BlockVec3Dim nearest = null;
int dim = GCCoreUtil.getDimensionID(te.getWorld());
for (BlockVec3Dim telemeter : loadedList)
{
if (telemeter.dim != dim)
{
continue;
}
int dist = telemeter.distanceSquared(target);
if (dist < distSq)
{
distSq = dist;
nearest = telemeter;
}
}
if (nearest == null)
{
return null;
}
TileEntity result = te.getWorld().getTileEntity(new BlockPos(nearest.x, nearest.y, nearest.z));
if (result instanceof TileEntityTelemetry)
{
return (TileEntityTelemetry) result;
}
return null;
}
/**
* Call this when a player wears a frequency module to check
* whether it has been linked with a Telemetry Unit.
*
* @param held The frequency module
* @param player
*/
public static void frequencyModulePlayer(ItemStack held, EntityPlayerMP player)
{
if (held == null)
{
return;
}
NBTTagCompound fmData = held.getTagCompound();
if (fmData != null && fmData.hasKey("teDim"))
{
int dim = fmData.getInteger("teDim");
int x = fmData.getInteger("teCoordX");
int y = fmData.getInteger("teCoordY");
int z = fmData.getInteger("teCoordZ");
WorldProvider wp = WorldUtil.getProviderForDimensionServer(dim);
//TODO
if (wp == null || wp.worldObj == null)
{
GCLog.debug("Frequency module worn: world provider is null. This is a bug. " + dim);
}
else
{
TileEntity te = wp.worldObj.getTileEntity(new BlockPos(x, y, z));
if (te instanceof TileEntityTelemetry)
{
if (player == null)
{
((TileEntityTelemetry) te).removeTrackedEntity();
}
else
{
((TileEntityTelemetry) te).addTrackedEntity(player.getUniqueID());
}
}
}
}
}
public static void updateLinkedPlayer(EntityPlayerMP playerOld, EntityPlayerMP playerNew)
{
for (BlockVec3Dim telemeter : loadedList)
{
TileEntity te = telemeter.getTileEntityNoLoad();
if (te instanceof TileEntityTelemetry)
{
if (((TileEntityTelemetry) te).linkedEntity == playerOld)
{
((TileEntityTelemetry) te).linkedEntity = playerNew;
}
}
}
}
@Override
public boolean shouldRefresh(World world, BlockPos pos, IBlockState oldState, IBlockState newSate)
{
return oldState.getBlock() != newSate.getBlock();
}
}