package com.hypixel.hytale.server.npc.util;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.util.TrigMathUtil;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath;
import com.hypixel.hytale.server.core.modules.projectile.config.BallisticData;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.npc.sensorinfo.ExtraInfoProvider;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class AimingData implements ExtraInfoProvider {
public static final double MIN_MOVE_SPEED_STATIC = 0.01;
public static final double MIN_MOVE_SPEED_STATIC_2 = 1.0E-4;
public static final double MIN_AIMING_DISTANCE = 0.01;
public static final double MIN_AIMING_DISTANCE_2 = 1.0E-4;
public static final double MIN_AIR_TIME = 0.01;
public static final double ANGLE_EPSILON = 0.1;
@Nullable
private BallisticData ballisticData;
private boolean useFlatTrajectory = true;
private double depthOffset;
private boolean pitchAdjustOffset;
private boolean haveSolution;
private boolean haveOrientation;
private boolean haveAttacked;
private double chargeDistance;
private double desiredHitAngle;
private final float[] pitch = new float[2];
private final float[] yaw = new float[2];
@Nullable
private Ref<EntityStore> target;
private int owner = -2147483648;
public AimingData() {
}
public boolean isHaveAttacked() {
return this.haveAttacked;
}
public void setHaveAttacked(boolean haveAttacked) {
this.haveAttacked = haveAttacked;
}
public void requireBallistic(@Nonnull BallisticData ballisticData) {
this.ballisticData = ballisticData;
this.haveSolution = false;
this.haveOrientation = false;
}
public void requireCloseCombat() {
this.ballisticData = null;
this.haveSolution = false;
this.haveOrientation = false;
}
public float getPitch() {
return this.getPitch(this.useFlatTrajectory);
}
public float getPitch(boolean flatTrajectory) {
return this.pitch[flatTrajectory ? 0 : 1];
}
public float getYaw() {
return this.getYaw(this.useFlatTrajectory);
}
public float getYaw(boolean flatTrajectory) {
return this.yaw[flatTrajectory ? 0 : 1];
}
public boolean isBallistic() {
return this.ballisticData != null;
}
@Nullable
public BallisticData getBallisticData() {
return this.ballisticData;
}
public void setUseFlatTrajectory(boolean useFlatTrajectory) {
this.useFlatTrajectory = useFlatTrajectory;
}
public void setChargeDistance(double chargeDistance) {
this.chargeDistance = chargeDistance;
}
public double getChargeDistance() {
return this.chargeDistance;
}
public void setDesiredHitAngle(double desiredHitAngle) {
this.desiredHitAngle = desiredHitAngle;
}
public double getDesiredHitAngle() {
return this.desiredHitAngle;
}
@Nonnull
public Class<AimingData> getType() {
return AimingData.class;
}
public void setDepthOffset(double depthOffset, boolean pitchAdjustOffset) {
this.depthOffset = depthOffset;
this.pitchAdjustOffset = depthOffset != 0.0 && pitchAdjustOffset;
}
@Nullable
public Ref<EntityStore> getTarget() {
if (this.target == null) {
return null;
} else if (this.target.isValid() && !this.target.getStore().getArchetype(this.target).contains(DeathComponent.getComponentType())) {
return this.target;
} else {
this.target = null;
return null;
}
}
public void setTarget(Ref<EntityStore> ref) {
this.target = ref;
}
public boolean haveOrientation() {
return this.haveOrientation || this.haveSolution;
}
public void setOrientation(float yaw, float pitch) {
this.yaw[0] = this.yaw[1] = yaw;
this.pitch[0] = this.pitch[1] = pitch;
this.haveOrientation = true;
}
public void clearSolution() {
this.haveOrientation = false;
this.haveSolution = false;
this.target = null;
}
public boolean computeSolution(double x, double y, double z, double vx, double vy, double vz) {
double xxzz = x * x + z * z;
double d2 = xxzz + y * y;
if (d2 < 0.01) {
return this.haveSolution = false;
} else if (!this.isBallistic()) {
this.yaw[0] = this.yaw[1] = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z));
this.pitch[0] = this.pitch[1] = PhysicsMath.pitchFromDirection(x, y, z);
return this.haveSolution = true;
} else {
if (!this.pitchAdjustOffset && xxzz > this.depthOffset * this.depthOffset) {
double len = Math.sqrt(xxzz);
double newLen = len - this.depthOffset;
double scale = newLen / len;
x *= scale;
z *= scale;
xxzz = newLen * newLen;
d2 = xxzz + y * y;
}
double v2 = NPCPhysicsMath.dotProduct(vx, vy, vz);
if (v2 < 1.0E-4) {
if (this.haveSolution = this.computeStaticSolution(Math.sqrt(xxzz), y)) {
this.yaw[0] = this.yaw[1] = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z));
}
return this.haveSolution;
} else {
double gravity = this.ballisticData.getGravity();
double muzzleVelocity = this.ballisticData.getMuzzleVelocity();
double[] solutions = new double[4];
double c4 = gravity * gravity / 4.0;
double c3 = vy * gravity;
double c2 = v2 + y * gravity - muzzleVelocity * muzzleVelocity;
double c1 = 2.0 * (x * vx + y * vy + z * vz);
int numSolutions = RootSolver.solveQuartic(c4, c3, c2, c1, d2, solutions);
if (numSolutions == 0) {
return this.haveSolution = false;
} else {
int numResults = 0;
double lastT = 1.7976931348623157E308;
for(int i = 0; i < numSolutions; ++i) {
double t = solutions[i];
if (!(t <= 0.01)) {
double tx = x + t * vx;
double tz = z + t * vz;
xxzz = tx * tx + tz * tz;
if (!(xxzz < 0.01)) {
double sine = (y / t + 0.5 * gravity * t) / muzzleVelocity;
if (!(sine < -1.0) && !(sine > 1.0)) {
float p = TrigMathUtil.asin(sine);
float h = PhysicsMath.headingFromDirection(tx, tz);
if (numResults < 2) {
if (numResults != 0 && !(t > lastT)) {
this.pitch[numResults] = this.pitch[numResults - 1];
this.yaw[numResults] = this.yaw[numResults - 1];
this.pitch[numResults - 1] = p;
this.yaw[numResults - 1] = h;
} else {
lastT = t;
this.pitch[numResults] = p;
this.yaw[numResults] = h;
}
++numResults;
}
}
}
}
}
if (numResults == 0) {
return this.haveSolution = false;
} else {
if (numResults == 1) {
this.pitch[1] = this.pitch[0];
this.yaw[1] = this.yaw[0];
}
return this.haveSolution = true;
}
}
}
}
}
public boolean isOnTarget(float yaw, float pitch, double hitAngle) {
if (!this.haveOrientation()) {
return false;
} else {
double differenceYaw = (double)NPCPhysicsMath.turnAngle(yaw, this.getYaw());
if (!this.isBallistic()) {
return -hitAngle <= differenceYaw && differenceYaw <= hitAngle;
} else {
double differencePitch = (double)NPCPhysicsMath.turnAngle(pitch, this.getPitch());
return differencePitch >= -0.1 && differencePitch <= 0.1 && differenceYaw >= -0.1 && differenceYaw <= 0.1;
}
}
}
public void tryClaim(int id) {
if (this.owner == -2147483648) {
this.owner = id;
this.clear();
}
}
public boolean isClaimedBy(int id) {
return this.owner == id;
}
public void release() {
this.owner = -2147483648;
}
public void clear() {
this.clearSolution();
this.ballisticData = null;
this.useFlatTrajectory = true;
this.depthOffset = 0.0;
this.pitchAdjustOffset = false;
this.haveAttacked = false;
}
protected boolean computeStaticSolution(double dx, double dy) {
return this.haveSolution = AimingHelper.computePitch(dx, dy, this.ballisticData.getMuzzleVelocity(), this.ballisticData.getGravity(), this.pitch);
}
}