package org.mctourney.autoreferee.util;
import java.util.Set;
import java.lang.Math;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.Material;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import com.google.common.collect.Sets;
import org.mctourney.autoreferee.AutoRefPlayer;
public class TeleportationUtil
{
private TeleportationUtil()
{ }
public static Location locationTeleport(Location loc)
{
if (loc == null) return null;
Location x, c = loc.clone(), best = c;
double sqd, bsqd = -1.0;
for (Vector vec : directions)
{
x = TeleportationUtil.checkDirection(c, vec);
sqd = x.distanceSquared(best);
if (sqd > bsqd) { bsqd = sqd; best = x; }
}
// return a location that is looking at the target
Location dest = locationLookingAt(best, loc);
return dest.distance(loc) < 1.0 ? null : dest;
}
public static Location blockTeleport(Block b)
{ return blockTeleport(b.getLocation()); }
public static Location blockTeleport(Location loc)
{ return locationTeleport(loc.clone().add(0.5, 0.5, 0.5)); }
public static Location entityTeleport(Entity e)
{
Location loc = e.getLocation().clone();
if (e instanceof LivingEntity)
{
LivingEntity live = (LivingEntity) e;
loc.add(0, live.getEyeHeight(), 0);
}
return locationTeleport(loc);
}
public static Location playerTeleport(AutoRefPlayer apl)
{
if (apl == null) return null;
if (apl.isOnline()) return entityTeleport(apl.getPlayer());
Location loc = apl.getLocation().clone();
return locationTeleport(loc);
}
private static Location locationLookingAt(Location base, Location target)
{
if (base == null || target == null) return base;
double dx = base.getX() - target.getX(),
dy = base.getY() - target.getY(),
dz = base.getZ() - target.getZ();
double dist = Math.sqrt(dx*dx + dz*dz);
Location res = base.clone();
res.setPitch((float)(Math.atan2(dy, dist)*180/Math.PI));
res.setYaw((float)(Math.atan2(dz, dx)*180/Math.PI) + 90.0f);
return res;
}
private static Set<Material> passableBlocks = Sets.newHashSet
( Material.AIR
, Material.DEAD_BUSH
, Material.DIODE_BLOCK_ON
, Material.DIODE_BLOCK_OFF
, Material.LADDER
, Material.LEVER
, Material.LONG_GRASS
, Material.MELON_STEM
, Material.NETHER_STALK
, Material.NETHER_WARTS
, Material.POWERED_RAIL
, Material.PUMPKIN_STEM
, Material.RAILS
, Material.RED_ROSE
, Material.REDSTONE_TORCH_ON
, Material.REDSTONE_TORCH_OFF
, Material.REDSTONE_WIRE
, Material.SAPLING
, Material.SIGN
, Material.SIGN_POST
, Material.SNOW
, Material.STATIONARY_WATER
, Material.STONE_BUTTON
, Material.STONE_PLATE
, Material.TORCH
, Material.TRIPWIRE
, Material.TRIPWIRE_HOOK
, Material.VINE
, Material.WALL_SIGN
, Material.WATER
, Material.WATER_LILY
, Material.WOOD_PLATE
, Material.YELLOW_FLOWER
);
public static boolean isBlockPassable(Block b)
{ return passableBlocks.contains(b.getType()); }
public static boolean safeLocation(Location loc)
{ return isBlockPassable(loc.getBlock()) &&
isBlockPassable(loc.getBlock().getRelative(0, 1, 0)); }
private static final int MAX_TELEPORT_DISTANCE = 4;
private static Location checkDirection(Location loc, Vector v)
{
v = v.normalize();
int d = -1;
// furthest we can stray in one direction
// based on the rows preceeding
int m = MAX_TELEPORT_DISTANCE;
Location best = loc.clone();
for (int h = 0; h <= MAX_TELEPORT_DISTANCE && m > h; ++h)
{
// attempt up to M blocks away from the center
Location c = loc.clone().add(0, h, 0);
int k; for (k = 1; k <= m; ++k)
{
// the next location we are checking
Location nc = c.add(v);
// if this block is impassable, dec k before quitting
if (!isBlockPassable(nc.getBlock())) { --k; break; }
// update c if the block is passable
else c = nc;
}
// we only got to row k, don't exceed this in future passes
if (k < m) m = k;
// if this is farther away than any previously, save it
// only allow locations below a grade of 45deg
if (h > 0 && k >= h && k + h > d) { d = k + h; best = c; }
}
return best;
}
private static Set<Vector> directions = Sets.newHashSet
( new Vector( 0, 0, 1)
, new Vector( 0, 0, -1)
, new Vector( 1, 0, 0)
, new Vector(-1, 0, 0)
);
}