package cn.liutils.util.raytrace;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import cn.liutils.util.generic.VecUtils;
import cn.liutils.util.helper.Motion3D;
import cn.liutils.util.mc.BlockFilters;
import cn.liutils.util.mc.EntitySelectors;
import cn.liutils.util.mc.IBlockFilter;
import cn.liutils.util.mc.WorldUtils;
import net.minecraft.block.Block;
import net.minecraft.command.IEntitySelector;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
/**
* A better wrap up for ray trace routines, supporting entity filtering, block filtering, and combined RayTrace of
* blocks and entities. Also provided functions for fast implementation on entity looking traces.
* @author WeAthFolD
*/
public class Raytrace {
/**
* Perform a ray trace.
* @param world
* @param vec1 Start point
* @param vec2 End point
* @param entitySel The entity filter
* @param blockSel The block filter
* @return The trace result, might be null
*/
public static MovingObjectPosition perform(World world, Vec3 vec1, Vec3 vec2, IEntitySelector entitySel, IBlockFilter blockSel) {
MovingObjectPosition
mop1 = rayTraceEntities(world, vec1, vec2, entitySel),
mop2 = rayTraceBlocks(world, vec1, vec2, blockSel);
if(mop1 != null && mop2 != null) {
double d1 = mop1.hitVec.distanceTo(vec1);
double d2 = mop2.hitVec.distanceTo(vec1);
return d1 <= d2 ? mop1 : mop2;
}
if(mop1 != null)
return mop1;
return mop2;
}
public static MovingObjectPosition perform(World world, Vec3 vec1, Vec3 vec2, IEntitySelector entitySel) {
return perform(world, vec1, vec2, entitySel, null);
}
public static MovingObjectPosition perform(World world, Vec3 vec1, Vec3 vec2) {
return perform(world, vec1, vec2, null, null);
}
public static Pair<Vec3, MovingObjectPosition> getLookingPos(EntityLivingBase living, double dist) {
return getLookingPos(living, dist, null, null);
}
public static Pair<Vec3, MovingObjectPosition> getLookingPos(EntityLivingBase living, double dist, IEntitySelector esel) {
return getLookingPos(living, dist, esel, null);
}
public static Pair<Vec3, MovingObjectPosition> getLookingPos(EntityLivingBase living, double dist, IEntitySelector esel, IBlockFilter bsel) {
MovingObjectPosition pos = traceLiving(living, dist, esel, bsel);
Vec3 end = null;
if(pos != null) {
end = pos.hitVec;
if(pos.entityHit != null)
end.yCoord += pos.entityHit.getEyeHeight() * 0.6;
}
if(end == null)
end = new Motion3D(living, true).move(dist).getPosVec();
return Pair.of(end, pos);
}
public static MovingObjectPosition rayTraceEntities(World world, Vec3 vec1, Vec3 vec2, IEntitySelector selector) {
Entity entity = null;
AxisAlignedBB boundingBox = WorldUtils.getBoundingBox(vec1, vec2);
List list = world.getEntitiesWithinAABBExcludingEntity(null, boundingBox.expand(1.0D, 1.0D, 1.0D), selector);
double d0 = 0.0D;
for (int j = 0; j < list.size(); ++j) {
Entity entity1 = (Entity)list.get(j);
if(!entity1.canBeCollidedWith() || (selector != null && !selector.isEntityApplicable(entity1)))
continue;
float f = 0.3F;
AxisAlignedBB axisalignedbb = entity1.boundingBox.expand(f, f, f);
MovingObjectPosition movingobjectposition1 = axisalignedbb.calculateIntercept(vec1, vec2);
if (movingobjectposition1 != null) {
double d1 = vec1.distanceTo(movingobjectposition1.hitVec);
if (d1 < d0 || d0 == 0.0D)
{
entity = entity1;
d0 = d1;
}
}
}
if (entity != null) {
return new MovingObjectPosition(entity);
}
return null;
}
/**
* Mojang code with minor changes to support block filtering.
* @param world world
* @param vec1 startPoint
* @param vec2 endPoint
* @param filter BlockFilter
* @return MovingObjectPosition
*/
@SuppressWarnings("unused")
public static MovingObjectPosition rayTraceBlocks(World world, Vec3 vec1, Vec3 vec2, IBlockFilter filter) {
if(Double.isNaN(vec1.xCoord) || Double.isNaN(vec1.yCoord) || Double.isNaN(vec1.zCoord) ||
Double.isNaN(vec2.xCoord) || Double.isNaN(vec2.yCoord) || Double.isNaN(vec2.zCoord)) {
return null;
}
//HACKHACK: copy the vec to prevent modifying the parameter
vec1 = VecUtils.copy(vec1);
if(filter == null)
filter = BlockFilters.filNormal;
int x2 = MathHelper.floor_double(vec2.xCoord);
int y2 = MathHelper.floor_double(vec2.yCoord);
int z2 = MathHelper.floor_double(vec2.zCoord);
int x1 = MathHelper.floor_double(vec1.xCoord);
int y1 = MathHelper.floor_double(vec1.yCoord);
int z1 = MathHelper.floor_double(vec1.zCoord);
Block block = world.getBlock(x1, y1, z1);
int k1 = world.getBlockMetadata(x1, y1, z1);
if (filter.accepts(world, x1, y1, z1, block))
{
MovingObjectPosition movingobjectposition = block.collisionRayTrace(world, x1, y1, z1, vec1, vec2);
if (movingobjectposition != null)
{
return movingobjectposition;
}
}
MovingObjectPosition movingobjectposition2 = null;
k1 = 200;
while (k1-- >= 0)
{
if (Double.isNaN(vec1.xCoord) || Double.isNaN(vec1.yCoord) || Double.isNaN(vec1.zCoord))
{
return null;
}
if (x1 == x2 && y1 == y2 && z1 == z2)
{
return null;
}
boolean flag6 = true;
boolean flag3 = true;
boolean flag4 = true;
double d0 = 999.0D;
double d1 = 999.0D;
double d2 = 999.0D;
if (x2 > x1)
{
d0 = (double)x1 + 1.0D;
}
else if (x2 < x1)
{
d0 = (double)x1 + 0.0D;
}
else
{
flag6 = false;
}
if (y2 > y1)
{
d1 = (double)y1 + 1.0D;
}
else if (y2 < y1)
{
d1 = (double)y1 + 0.0D;
}
else
{
flag3 = false;
}
if (z2 > z1)
{
d2 = (double)z1 + 1.0D;
}
else if (z2 < z1)
{
d2 = (double)z1 + 0.0D;
}
else
{
flag4 = false;
}
double d3 = 999.0D;
double d4 = 999.0D;
double d5 = 999.0D;
double d6 = vec2.xCoord - vec1.xCoord;
double d7 = vec2.yCoord - vec1.yCoord;
double d8 = vec2.zCoord - vec1.zCoord;
if (flag6)
{
d3 = (d0 - vec1.xCoord) / d6;
}
if (flag3)
{
d4 = (d1 - vec1.yCoord) / d7;
}
if (flag4)
{
d5 = (d2 - vec1.zCoord) / d8;
}
boolean flag5 = false;
byte b0;
if (d3 < d4 && d3 < d5)
{
if (x2 > x1)
{
b0 = 4;
}
else
{
b0 = 5;
}
vec1.xCoord = d0;
vec1.yCoord += d7 * d3;
vec1.zCoord += d8 * d3;
}
else if (d4 < d5)
{
if (y2 > y1)
{
b0 = 0;
}
else
{
b0 = 1;
}
vec1.xCoord += d6 * d4;
vec1.yCoord = d1;
vec1.zCoord += d8 * d4;
}
else
{
if (z2 > z1)
{
b0 = 2;
}
else
{
b0 = 3;
}
vec1.xCoord += d6 * d5;
vec1.yCoord += d7 * d5;
vec1.zCoord = d2;
}
Vec3 vec32 = Vec3.createVectorHelper(vec1.xCoord, vec1.yCoord, vec1.zCoord);
x1 = (int)(vec32.xCoord = (double)MathHelper.floor_double(vec1.xCoord));
if (b0 == 5)
{
--x1;
++vec32.xCoord;
}
y1 = (int)(vec32.yCoord = (double)MathHelper.floor_double(vec1.yCoord));
if (b0 == 1)
{
--y1;
++vec32.yCoord;
}
z1 = (int)(vec32.zCoord = (double)MathHelper.floor_double(vec1.zCoord));
if (b0 == 3)
{
--z1;
++vec32.zCoord;
}
Block block1 = world.getBlock(x1, y1, z1);
int l1 = world.getBlockMetadata(x1, y1, z1);
if (filter.accepts(world, x1, y1, z1, block1))
{
if (true)
{
MovingObjectPosition movingobjectposition1 = block1.collisionRayTrace(world, x1, y1, z1, vec1, vec2);
if (movingobjectposition1 != null)
{
return movingobjectposition1;
}
}
else
{
movingobjectposition2 = new MovingObjectPosition(x1, y1, z1, b0, vec1, false);
}
}
}
return null;
}
public static MovingObjectPosition traceLiving(EntityLivingBase entity, double dist) {
return traceLiving(entity, dist, null, null);
}
public static MovingObjectPosition traceLiving(EntityLivingBase entity, double dist, IEntitySelector entitySel) {
return traceLiving(entity, dist, entitySel, null);
}
/**
* Performs a RayTrace starting from the target entity's eye towards its looking direction.
* The trace will automatically ignore the target entity.
*/
public static MovingObjectPosition traceLiving(EntityLivingBase entity, double dist, IEntitySelector entitySel, IBlockFilter blockSel) {
Motion3D mo = new Motion3D(entity, true);
Vec3 v1 = mo.getPosVec(), v2 = mo.move(dist).getPosVec();
IEntitySelector exclude = EntitySelectors.excludeOf(entity);
return perform(entity.worldObj, v1, v2, entitySel == null ? exclude : EntitySelectors.combine(exclude, entitySel), blockSel);
}
}