package com.hypixel.hytale.server.npc.movement.controllers;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.npc.util.NPCPhysicsMath;
import java.util.Arrays;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ProbeMoveData {
@Nonnull
public final Vector3d probePosition = new Vector3d();
@Nonnull
public final Vector3d probeDirection = new Vector3d();
@Nonnull
public final Vector3d initialPosition = new Vector3d();
@Nonnull
public final Vector3d targetPosition = new Vector3d();
@Nonnull
public final Vector3d directionComponentSelector = new Vector3d();
public boolean isAvoidingBlockDamage = true;
public boolean isRelaxedMoveConstraints = false;
public boolean onGround;
public boolean isSavingSegments = false;
public int segmentCount = 0;
@Nullable
public Segment[] segments = null;
public ProbeMoveData() {
}
public void setSaveSegments(boolean saveSegments) {
this.isSavingSegments = saveSegments;
if (this.isSavingSegments && this.segments == null) {
this.segments = new Segment[6];
for(int i = 0; i < this.segments.length; ++i) {
this.segments[i] = new Segment();
}
}
}
public boolean isAvoidingBlockDamage() {
return this.isAvoidingBlockDamage;
}
public void setAvoidingBlockDamage(boolean avoid) {
this.isAvoidingBlockDamage = avoid;
}
public boolean isRelaxedMoveConstraints() {
return this.isRelaxedMoveConstraints;
}
public void setRelaxedMoveConstraints(boolean relaxedMoveConstraints) {
this.isRelaxedMoveConstraints = relaxedMoveConstraints;
}
@Nonnull
public ProbeMoveData setPosition(@Nonnull Vector3d position) {
this.probePosition.assign(position);
this.initialPosition.assign(position);
return this;
}
@Nonnull
public ProbeMoveData setDirection(@Nonnull Vector3d direction) {
this.probeDirection.assign(direction);
this.targetPosition.assign(this.probePosition).add(this.probeDirection);
return this;
}
@Nonnull
public ProbeMoveData setTargetPosition(@Nonnull Vector3d targetPosition) {
this.targetPosition.assign(targetPosition);
this.probeDirection.assign(targetPosition).subtract(this.probePosition);
return this;
}
public boolean canAdvance(@Nonnull Ref<EntityStore> ref, @Nonnull MotionController motionController, double threshold, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
double requiredDistance = threshold * this.probeDirection.length();
return this.canAdvanceAbs(ref, motionController, requiredDistance, componentAccessor);
}
public boolean canAdvanceAbs(@Nonnull Ref<EntityStore> ref, @Nonnull MotionController motionController, double requiredDistance, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
double distance = motionController.probeMove(ref, this, componentAccessor);
return distance >= requiredDistance;
}
public boolean canMoveTo(@Nonnull Ref<EntityStore> ref, @Nonnull MotionController motionController, double maxDistance, double maxDistanceY, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
if (!this.canMoveTo(ref, motionController, maxDistance, componentAccessor)) {
return false;
} else if (!motionController.is2D()) {
return true;
} else {
double dy = NPCPhysicsMath.getProjectedDifference(this.targetPosition, this.probePosition, motionController.getComponentSelector());
return Math.abs(dy) <= maxDistanceY;
}
}
public boolean canMoveTo(@Nonnull Ref<EntityStore> ref, @Nonnull MotionController motionController, double maxDistance, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
motionController.probeMove(ref, this, componentAccessor);
return motionController.waypointDistanceSquared(this.targetPosition, this.probePosition) <= maxDistance * maxDistance;
}
public boolean computePosition(double distance, @Nonnull Vector3d result) {
if (this.segmentCount < 2) {
return false;
} else if (distance <= 0.0) {
result.assign(this.segments[0].position);
return true;
} else {
int index = 1;
Segment segment = this.segments[0];
Segment prevSegment;
for(prevSegment = null; index < this.segmentCount; ++index) {
prevSegment = segment;
segment = this.segments[index];
if (segment.distance >= distance) {
break;
}
}
if (segment.distance <= distance) {
result.assign(segment.position);
return true;
} else if (segment.type.canInterpolate()) {
double lambda = (distance - prevSegment.distance) / (segment.distance - prevSegment.distance);
NPCPhysicsMath.lerp(prevSegment.position, segment.position, lambda, result);
return true;
} else {
result.assign(prevSegment.position);
return true;
}
}
}
public boolean startProbing() {
if (this.isSavingSegments) {
this.segmentCount = 0;
}
return this.isSavingSegments;
}
public void addStartSegment(@Nonnull Vector3d position, boolean onGround) {
this.newSegment().initAsStartSegment(position, onGround);
}
public void addEndSegment(@Nonnull Vector3d position, boolean onGround, double distance) {
this.newSegment().initAsEndSegment(position, onGround, distance);
}
public void addBlockedGroundSegment(@Nonnull Vector3d position, double distance, @Nonnull Vector3d normal, int blockId) {
this.newSegment().initAsBlockedGroundSegment(position, distance, normal, blockId);
}
public void addHitGroundSegment(@Nonnull Vector3d position, double distance, @Nonnull Vector3d normal, int blockId) {
this.newSegment().initAsHitGroundSegment(position, distance, normal, blockId);
}
public void addHitWallSegment(@Nonnull Vector3d position, boolean onGround, double distance, @Nonnull Vector3d normal, int blockId) {
this.newSegment().initAsHitWallSegment(position, onGround, distance, normal, blockId);
}
public void addMoveSegment(@Nonnull Vector3d position, boolean onGround, double distance) {
this.newSegment().initAsMoveSegment(position, onGround, distance);
}
public void addClimbSegment(@Nonnull Vector3d position, double distance, int blockId) {
this.newSegment().initAsClimbSegment(position, distance, blockId);
}
public void addHitEdgeSegment(@Nonnull Vector3d position, double distance) {
this.newSegment().initAsHitEdgeSegment(position, distance);
}
public void addDropSegment(@Nonnull Vector3d position, double distance) {
this.newSegment().initAsDropSegment(position, distance);
}
public void addBlockedDropSegment(@Nonnull Vector3d position, double distance) {
this.newSegment().initAsBlockedDropSegment(position, distance);
}
public void changeSegmentToBlockedWall() {
this.segments[this.segmentCount - 1].type = ProbeMoveData.Segment.Type.BLOCKED_WALL;
}
public void changeSegmentToBlockedEdge() {
this.segments[this.segmentCount - 1].type = ProbeMoveData.Segment.Type.BLOCKED_EDGE;
}
public double getLastDistance() {
return this.segments[this.segmentCount - 1].distance;
}
protected Segment newSegment() {
if (this.segmentCount == this.segments.length) {
this.segments = (Segment[])Arrays.copyOf(this.segments, this.segmentCount + 4);
for(int i = this.segmentCount; i < this.segments.length; ++i) {
this.segments[i] = new Segment();
}
}
return this.segments[this.segmentCount++];
}
public static class Segment {
public Type type;
public final Vector3d position = new Vector3d();
public final Vector3d normal = new Vector3d();
public double distance;
public boolean onGround;
public int blockId;
public Segment() {
}
public void initAsStartSegment(@Nonnull Vector3d position, boolean onGround) {
this.type = ProbeMoveData.Segment.Type.START;
this.position.assign(position);
this.normal.assign(Vector3d.ZERO);
this.distance = 0.0;
this.onGround = onGround;
this.blockId = -2147483648;
}
public void initAsEndSegment(@Nonnull Vector3d position, boolean onGround, double distance) {
this.type = ProbeMoveData.Segment.Type.END;
this.position.assign(position);
this.normal.assign(Vector3d.ZERO);
this.distance = distance;
this.onGround = onGround;
this.blockId = -2147483648;
}
public void initAsBlockedGroundSegment(@Nonnull Vector3d position, double distance, @Nonnull Vector3d normal, int blockId) {
this.type = ProbeMoveData.Segment.Type.BLOCKED_GROUND;
this.position.assign(position);
this.normal.assign(normal);
this.distance = distance;
this.onGround = true;
this.blockId = blockId;
}
public void initAsHitGroundSegment(@Nonnull Vector3d position, double distance, @Nonnull Vector3d normal, int blockId) {
this.type = ProbeMoveData.Segment.Type.HIT_GROUND;
this.position.assign(position);
this.normal.assign(normal);
this.distance = distance;
this.onGround = true;
this.blockId = blockId;
}
public void initAsHitWallSegment(@Nonnull Vector3d position, boolean onGround, double distance, @Nonnull Vector3d normal, int blockId) {
this.type = ProbeMoveData.Segment.Type.HIT_WALL;
this.position.assign(position);
this.normal.assign(normal);
this.distance = distance;
this.onGround = onGround;
this.blockId = blockId;
}
public void initAsClimbSegment(@Nonnull Vector3d position, double distance, int blockId) {
this.type = ProbeMoveData.Segment.Type.CLIMB;
this.position.assign(position);
this.normal.assign(Vector3d.ZERO);
this.distance = distance;
this.onGround = true;
this.blockId = blockId;
}
public void initAsMoveSegment(@Nonnull Vector3d position, boolean onGround, double distance) {
this.type = ProbeMoveData.Segment.Type.MOVE;
this.position.assign(position);
this.normal.assign(Vector3d.ZERO);
this.distance = distance;
this.onGround = onGround;
this.blockId = -2147483648;
}
public void initAsDropSegment(@Nonnull Vector3d position, double distance) {
this.type = ProbeMoveData.Segment.Type.DROP;
this.position.assign(position);
this.normal.assign(Vector3d.ZERO);
this.distance = distance;
this.onGround = true;
this.blockId = -2147483648;
}
public void initAsBlockedDropSegment(@Nonnull Vector3d position, double distance) {
this.type = ProbeMoveData.Segment.Type.BLOCKED_DROP;
this.position.assign(position);
this.normal.assign(Vector3d.ZERO);
this.distance = distance;
this.onGround = false;
this.blockId = -2147483648;
}
public void initAsHitEdgeSegment(@Nonnull Vector3d position, double distance) {
this.type = ProbeMoveData.Segment.Type.HIT_EDGE;
this.position.assign(position);
this.normal.assign(Vector3d.ZERO);
this.distance = distance;
this.onGround = true;
this.blockId = -2147483648;
}
public static enum Type {
START(false, false),
HIT_GROUND(false, true),
MOVE(false, true),
BLOCKED_GROUND(true, true),
HIT_WALL(false, true),
BLOCKED_WALL(true, true),
CLIMB(false, false),
HIT_EDGE(false, true),
BLOCKED_EDGE(true, true),
DROP(false, false),
BLOCKED_DROP(true, false),
END(false, true);
protected final boolean isBlocked;
protected final boolean canInterpolate;
public boolean isBlocked() {
return this.isBlocked;
}
public boolean canInterpolate() {
return this.canInterpolate;
}
private Type(boolean isBlocked, boolean canInterpolate) {
this.isBlocked = isBlocked;
this.canInterpolate = canInterpolate;
}
}
}
}