package openblocks.common.entity;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import net.minecraft.command.IEntitySelector;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MathHelper;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import openblocks.Config;
import openblocks.api.IMagnetAware;
import openblocks.common.CraneRegistry;
import openblocks.common.MagnetWhitelists;
import openblocks.common.item.ItemCraneBackpack;
import openmods.entity.DelayedEntityLoadManager;
import openmods.entity.EntityBlock;
import openmods.entity.IEntityLoadListener;
public class EntityMagnet extends EntitySmoothMove implements IEntityAdditionalSpawnData, IEntitySelector {
private static final float MAGNET_HEIGHT = 0.5f;
private static final float MAGNET_WIDTH = 0.5f;
private static final Random RANDOM = new Random();
public interface IEntityBlockFactory {
public EntityBlock create(EntityPlayer player);
}
public interface IOwner {
public boolean isValid(EntityMagnet magnet);
public Vec3 getTarget();
public EntityBlock createByPlayer(IEntityBlockFactory factory);
}
private static class EntityPlayerTarget implements IOwner {
private WeakReference<EntityPlayer> owner;
public EntityPlayerTarget(EntityPlayer owner) {
this.owner = new WeakReference<EntityPlayer>(owner);
}
@Override
public boolean isValid(EntityMagnet magnet) {
EntityPlayer player = owner.get();
if (player == null || player.isDead) return false;
if (magnet.worldObj != player.worldObj) return false;
return ItemCraneBackpack.isWearingCrane(player);
}
@Override
public Vec3 getTarget() {
EntityPlayer player = owner.get();
if (player == null) return null;
double posX = player.posX + CraneRegistry.ARM_RADIUS * MathHelper.cos((player.rotationYaw + 90) * (float)Math.PI / 180);
double posZ = player.posZ + CraneRegistry.ARM_RADIUS * MathHelper.sin((player.rotationYaw + 90) * (float)Math.PI / 180);
double posY = player.posY + player.height
- CraneRegistry.instance.getCraneMagnetDistance(player);
return Vec3.createVectorHelper(posX, posY, posZ);
}
@Override
public EntityBlock createByPlayer(IEntityBlockFactory factory) {
EntityPlayer player = owner.get();
if (player == null) return null;
return factory.create(player);
}
}
public static class PlayerBound extends EntityMagnet implements IEntityLoadListener {
private WeakReference<Entity> owner;
public PlayerBound(World world) {
super(world);
owner = new WeakReference<Entity>(null);
}
public PlayerBound(World world, EntityPlayer owner) {
super(world, new EntityPlayerTarget(owner), false);
this.owner = new WeakReference<Entity>(owner);
CraneRegistry.instance.bindMagnetToPlayer(owner, this);
}
@Override
public void writeSpawnData(ByteBuf data) {
super.writeSpawnData(data);
Entity owner = this.owner.get();
data.writeInt(owner != null? owner.getEntityId() : -1);
}
@Override
public void readSpawnData(ByteBuf data) {
super.readSpawnData(data);
int entityId = data.readInt();
if (entityId >= 0) DelayedEntityLoadManager.instance.registerLoadListener(worldObj, this, entityId);
}
@Override
public boolean isEntityApplicable(Entity entity) {
return entity != owner.get() && super.isEntityApplicable(entity);
}
@Override
public void onEntityLoaded(Entity entity) {
if (entity instanceof EntityPlayer) {
owner = new WeakReference<Entity>(entity);
CraneRegistry.instance.bindMagnetToPlayer(entity, this);
}
}
}
private IOwner owner;
private boolean isAboveTarget;
private boolean isMagic;
public EntityMagnet(World world) {
super(world);
setSize(0.5f, 0.5f);
}
public EntityMagnet(World world, IOwner owner, boolean isMagic) {
this(world);
this.owner = owner;
this.isMagic = isMagic;
Vec3 initialTarget = owner.getTarget();
setPosition(initialTarget.xCoord, initialTarget.yCoord, initialTarget.zCoord);
}
@Override
public boolean isEntityInvulnerable() {
return true;
}
@Override
protected void dealFireDamage(int i) {}
@Override
public boolean canRenderOnFire() {
return false;
}
@Override
protected void entityInit() {}
@Override
protected void readEntityFromNBT(NBTTagCompound tag) {}
@Override
protected void writeEntityToNBT(NBTTagCompound tag) {}
@Override
public void writeSpawnData(ByteBuf data) {
data.writeBoolean(isMagic);
}
@Override
public void readSpawnData(ByteBuf data) {
isMagic = data.readBoolean();
}
@Override
public void onUpdate() {
fixSize();
if (!worldObj.isRemote) {
if (owner == null || !owner.isValid(this)) {
setDead();
return;
} else if (owner != null) {
final Vec3 target = owner.getTarget().addVector(0, -height, 0);
smoother.setTarget(target);
}
}
updatePrevPosition();
smoother.update();
isAboveTarget = !detectEntityTargets().isEmpty();
if (isMagic && worldObj.isRemote && RANDOM.nextDouble() < 0.2) worldObj.spawnParticle("portal",
posX + RANDOM.nextDouble() * 0.1,
posY - RANDOM.nextDouble() * 0.2, posZ + RANDOM.nextDouble() * 0.1,
RANDOM.nextGaussian(),
-Math.abs(RANDOM.nextGaussian()),
RANDOM.nextGaussian());
}
protected void fixSize() {
if (riddenByEntity != null) {
float width = Math.max(MAGNET_WIDTH, riddenByEntity.width);
float height = MAGNET_HEIGHT + riddenByEntity.height;
setSize(width, height);
} else {
setSize(MAGNET_WIDTH, MAGNET_HEIGHT);
}
}
@Override
public void setPosition(double x, double y, double z) {
if (smoother != null) smoother.setTarget(x, y, z);
super.setPosition(x, y, z);
}
@Override
public void setPositionAndRotation(double x, double y, double z, float yaw, float pitch) {
smoother.setTarget(x, y, z);
super.setPositionAndRotation(x, y, z, yaw, pitch);
}
@Override
@SideOnly(Side.CLIENT)
public void setPositionAndRotation2(double x, double y, double z, float yaw, float pitch, int something) {
smoother.setTarget(x, y, z);
super.setRotation(yaw, pitch);
}
@Override
public double getMountedYOffset() {
if (riddenByEntity == null) return 0;
return getMountedYOffset(riddenByEntity);
}
private static double getMountedYOffset(Entity rider) {
if (rider instanceof EntityPlayer) return 0.5f;
return 0;
}
public boolean toggleMagnet() {
if (riddenByEntity != null) {
final Entity tmp = riddenByEntity;
if (tmp instanceof IMagnetAware
&& !((IMagnetAware)tmp).canRelease())
return false;
// default unmount position is above entity and it
// looks strange, so we hack around that
double tmpPosY = tmp.posY;
tmp.mountEntity(null);
tmp.setPosition(tmp.posX, tmpPosY, tmp.posZ);
return true;
} else if (!worldObj.isRemote) {
Entity target = null;
if (Config.canMagnetPickEntities) target = findEntityToPick();
if (target == null && Config.canMagnetPickBlocks) target = createBlockEntity();
if (target != null) {
target.mountEntity(this);
return true;
}
}
return false;
}
private Entity findEntityToPick() {
List<Entity> result = detectEntityTargets();
Iterator<Entity> it = result.iterator();
return it.hasNext()? it.next() : null;
}
@Override
public boolean isEntityApplicable(Entity entity) {
return (entity instanceof EntityLivingBase) || MagnetWhitelists.instance.entityWhitelist.check(entity);
}
@SuppressWarnings("unchecked")
protected List<Entity> detectEntityTargets() {
AxisAlignedBB aabb = boundingBox.expand(0.25, 0, 0.25).copy();
aabb.minY -= 1;
return worldObj.getEntitiesWithinAABBExcludingEntity(this, aabb, this);
}
private Entity createBlockEntity() {
final int x = MathHelper.floor_double(posX);
final int y = MathHelper.floor_double(posY - 0.5);
final int z = MathHelper.floor_double(posZ);
if (!worldObj.blockExists(x, y, z) || worldObj.isAirBlock(x, y, z)) return null;
Entity result = null;
if (MagnetWhitelists.instance.testBlock(worldObj, x, y, z)) {
result = owner.createByPlayer(new IEntityBlockFactory() {
@Override
public EntityBlock create(EntityPlayer player) {
return EntityBlock.create(player, worldObj, x, y, z, EntityMountedBlock.class);
}
});
}
if (result != null) {
result.setPosition(posX, posY + getMountedYOffset(result), posZ);
worldObj.spawnEntityInWorld(result);
}
return result;
}
@Override
public boolean shouldRiderSit() {
return false;
}
@Override
public boolean shouldDismountInWater(Entity rider) {
return false;
}
@Override
public boolean canRiderInteract() {
return false;
}
public boolean isAboveTarget() {
return isAboveTarget && Config.canMagnetPickEntities;
}
public boolean isLocked() {
return riddenByEntity != null;
}
public boolean isValid() {
return owner != null? owner.isValid(this) : false;
}
}