Skip to content

Instantly share code, notes, and snippets.

@dumptruckman
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dumptruckman/9216392 to your computer and use it in GitHub Desktop.
Save dumptruckman/9216392 to your computer and use it in GitHub Desktop.
A utility for finding the target entity of an entity.
/* Copyright (C) dumptruckman 2014
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.util.Vector;
import java.util.HashSet;
import java.util.List;
/**
* A utility for finding the target entity of an entity.
* <p/>
* Original concept by CyberTiger.
*/
public class TargetFinder {
// Tolerance value of within this many blocks of the casted ray.
private static final int TARGET_TOLERANCE = 1;
private static final HashSet<Byte> TRANSPARENT_AND_PASSABLE_BLOCKS = new HashSet<Byte>();
static {
for (Material material : Material.values()) {
if (!material.isSolid() && material.isTransparent()) {
TRANSPARENT_AND_PASSABLE_BLOCKS.add((byte) material.getId());
}
}
TRANSPARENT_AND_PASSABLE_BLOCKS.add((byte) Material.AIR.getId());
TRANSPARENT_AND_PASSABLE_BLOCKS.add((byte) Material.LAVA.getId());
TRANSPARENT_AND_PASSABLE_BLOCKS.add((byte) Material.STATIONARY_LAVA.getId());
TRANSPARENT_AND_PASSABLE_BLOCKS.add((byte) Material.WATER.getId());
TRANSPARENT_AND_PASSABLE_BLOCKS.add((byte) Material.STATIONARY_WATER.getId());
}
/**
* Gets the entity that the given entity is targeting, if any, within the given maxDistance.
* <p/>
* This method is not perfect. It may only work if you are targeting the correct part of the entity (the upper
* portion of human sized entities.)
*
* @param entity the entity to find the target.
* @param maxDistance the maximum distance to search for, in blocks.
* @return the targeted entity or null if there is no target within the given maxDistance.
*/
public static LivingEntity getTargetedLivingEntity(LivingEntity entity, int maxDistance) {
return new TargetFinder(entity).getTargetedLivingEntity(maxDistance, TARGET_TOLERANCE);
}
private final LivingEntity targetingEntity;
private int maxTargetDistance;
private int targetTolerance;
private Vector targeterVector;
private Vector targeterDirection;
private double targetBlockDistance;
private TargetFinder(LivingEntity targetingEntity) {
this.targetingEntity = targetingEntity;
}
private LivingEntity getTargetedLivingEntity(int maxTargetDistance, int targetTolerance) {
this.maxTargetDistance = maxTargetDistance;
this.targetTolerance = targetTolerance;
Location loc = targetingEntity.getLocation();
this.targeterVector = loc.toVector();
this.targeterDirection = loc.getDirection();
this.targetBlockDistance = getTargetBlockDistanceOrMaxDistance();
LivingEntity targetEntity = null;
double nearestTargetDistance = Double.MAX_VALUE;
List<Entity> nearbyEntities = targetingEntity.getNearbyEntities(maxTargetDistance, maxTargetDistance, maxTargetDistance);
for (Entity e : nearbyEntities) {
if (e instanceof LivingEntity) {
LivingEntity potentialTarget = (LivingEntity) e;
Vector potentialTargetVector = potentialTarget.getLocation().toVector();
double potentialTargetDistance = getPotentialTargetDistance(potentialTargetVector);
if (isWithinToleranceRange(potentialTargetVector, potentialTargetDistance)
&& isCloserTarget(potentialTargetDistance, nearestTargetDistance)) {
nearestTargetDistance = potentialTargetDistance;
targetEntity = potentialTarget;
}
}
}
return targetEntity;
}
private double getTargetBlockDistanceOrMaxDistance() {
Vector targetBlockVector = getTargetBlockVector();
if (targetBlockVector != null) {
return getDistance(targeterDirection, targeterVector, targetBlockVector);
} else {
return Double.MAX_VALUE;
}
}
private double getDistance(final Vector dir, final Vector n1, final Vector n0) {
return -n1.clone().subtract(n0).dot(dir) / dir.clone().lengthSquared();
}
private Vector getTargetBlockVector() {
// TODO Ought to be getting the vector for the CENTER of the block.
Block block = targetingEntity.getTargetBlock(TRANSPARENT_AND_PASSABLE_BLOCKS, maxTargetDistance);
return block != null ? block.getLocation().toVector() : null;
}
private double getPotentialTargetDistance(Vector potentialTargetVector) {
//potentialTargetVector.setY(potentialTargetVector.getY() + potentialTarget.getEyeHeight());
return getDistance(targeterDirection, targeterVector, potentialTargetVector);
}
private boolean isWithinToleranceRange(final Vector potentialTargetVector, final double distanceToPotentialTarget) {
double linearDistance = getLinearDistance(potentialTargetVector, distanceToPotentialTarget);
return linearDistance < targetTolerance;
}
private double getLinearDistance(final Vector potentialTargetVector, final double distanceToPotentialTarget) {
double linearDistance = targeterVector.distanceSquared(potentialTargetVector) - distanceToPotentialTarget * distanceToPotentialTarget;
if (targetTolerance != 1) {
linearDistance = Math.sqrt(linearDistance);
}
return linearDistance;
}
private boolean isCloserTarget(double potentialTargetDistance, double nearestTargetDistance) {
return isDistanceValid(potentialTargetDistance)
&& isCloserThanPreviousTarget(potentialTargetDistance, nearestTargetDistance)
&& isCloserThanTargetedBlock(potentialTargetDistance);
}
private boolean isDistanceValid(double distanceToPotentialTarget) {
// In front of the player, not behind, and less than max distance.
return distanceToPotentialTarget > 0 && distanceToPotentialTarget <= maxTargetDistance;
}
private boolean isCloserThanPreviousTarget(final double distanceToPotentialTarget, double nearestTargetDistance) {
return distanceToPotentialTarget < nearestTargetDistance;
}
private boolean isCloserThanTargetedBlock(double distance) {
return distance < targetBlockDistance;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment