MotionControllerDive.java
package com.hypixel.hytale.server.npc.movement.controllers;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.protocol.BlockMaterial;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.server.core.modules.collision.BlockCollisionData;
import com.hypixel.hytale.server.core.modules.collision.CollisionModule;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.physics.component.Velocity;
import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import com.hypixel.hytale.server.npc.movement.MotionKind;
import com.hypixel.hytale.server.npc.movement.Steering;
import com.hypixel.hytale.server.npc.movement.controllers.builders.BuilderMotionControllerDive;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.util.NPCPhysicsMath;
import com.hypixel.hytale.server.npc.util.PositionProbeWater;
import java.util.EnumSet;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class MotionControllerDive extends MotionControllerBase {
public static final String TYPE = "Dive";
public static final int COLLISION_MATERIALS_ACTIVE = 5;
public static final int COLLISION_MATERIALS_PASSIVE = 4;
public static final double DEFAULT_SWIM_DEPTH = 0.5;
protected static double DAMPING_FACTOR = 20.0;
protected final double maxVerticalSpeed;
protected final double acceleration;
protected final double maxFallSpeed;
protected final double maxSinkSpeed;
protected final double maxRotationSpeed;
protected final float maxMoveTurnAngle;
protected final double minDiveDepth;
protected final double maxDiveDepth;
protected final double minWaterDepth;
protected final double maxWaterDepth;
protected final double minDepthAboveGround;
protected final double minDepthBelowSurface;
protected final double relativeSwimDepth;
protected final double sinkRatio;
protected final double fastDiveThreshold;
protected final double minSpeedAfterForceSquared;
protected final double desiredDepthWeight;
protected double swimDepth;
protected double climbSpeed;
protected double currentRelativeSpeed;
protected boolean collisionWithSolid;
protected final MotionController.VerticalRange verticalRange = new MotionController.VerticalRange();
protected final PositionProbeWater moveProbe = new PositionProbeWater();
protected final PositionProbeWater probeMoveProbe = new PositionProbeWater();
protected final Vector3d tempPosition = new Vector3d();
protected final Vector3d tempDirection = new Vector3d();
private static final EnumSet<MotionKind> VALID_MOTIONS;
public MotionControllerDive(@Nonnull BuilderSupport builderSupport, @Nonnull BuilderMotionControllerDive builder) {
super(builderSupport, builder);
this.setGravity(builder.getGravity());
this.componentSelector.assign(1.0, 1.0, 1.0);
this.maxVerticalSpeed = builder.getMaxVerticalSpeed();
this.acceleration = builder.getAcceleration();
this.maxSinkSpeed = builder.getMaxSinkSpeed();
this.maxFallSpeed = builder.getMaxFallSpeed();
this.maxRotationSpeed = builder.getMaxRotationSpeed();
this.maxMoveTurnAngle = builder.getMaxMoveTurnAngle();
this.minDiveDepth = builder.getMinDiveDepth();
this.maxDiveDepth = builder.getMaxDiveDepth();
this.minWaterDepth = builder.getMinWaterDepth();
this.maxWaterDepth = builder.getMaxWaterDepth();
this.minDepthAboveGround = builder.getMinDepthAboveGround();
this.minDepthBelowSurface = builder.getMinDepthBelowSurface();
this.relativeSwimDepth = builder.getSwimDepth();
this.sinkRatio = builder.getSinkRatio();
this.fastDiveThreshold = builder.getFastDiveThreshold();
this.desiredDepthWeight = builder.getDesiredDepthWeight();
double minSpeedAfterForceSquared = MathUtil.minValue(this.maxVerticalSpeed, this.maxSinkSpeed, this.maxFallSpeed);
this.minSpeedAfterForceSquared = minSpeedAfterForceSquared * minSpeedAfterForceSquared;
}
public void activate() {
super.activate();
this.collisionResult.disableSlides();
}
public double getWanderVerticalMovementRatio() {
return this.sinkRatio;
}
@Nonnull
public MotionController.VerticalRange getDesiredVerticalRange(@Nonnull Ref<EntityStore> ref, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
double waterLevel = (double)this.moveProbe.getWaterLevel() + 1.0;
double maxY = waterLevel - this.swimDepth - this.minDepthBelowSurface;
double groundY = (double)this.moveProbe.getGroundLevel() + this.minDepthAboveGround;
double lowY = waterLevel - this.maxDiveDepth;
double minY = Math.max(groundY, lowY);
TransformComponent transformComponent = (TransformComponent)componentAccessor.getComponent(ref, TransformComponent.getComponentType());
assert transformComponent != null;
double y = transformComponent.getPosition().y;
if (this.onGround()) {
minY = y;
}
if (this.moveProbe.isTouchCeil()) {
maxY = y;
}
if (minY > maxY) {
minY = y;
maxY = y;
}
this.verticalRange.assign(y, minY, maxY);
return this.verticalRange;
}
protected double computeMove(@Nonnull Ref<EntityStore> ref, @Nonnull Role role, @Nonnull Steering steering, double dt, @Nonnull Vector3d translation, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
this.saveMotionKind();
this.moveProbe.probePosition(ref, this.collisionBoundingBox, this.position, this.collisionResult, this.swimDepth, componentAccessor);
this.setMotionKind(!this.moveProbe.isInWater() && this.moveProbe.isOnGround() ? MotionKind.MOVING : MotionKind.SWIMMING);
this.currentRelativeSpeed = steering.getSpeed();
Vector3d dir = steering.getTranslation();
float heading = this.getYaw();
float pitch = this.getPitch();
if (this.collisionWithSolid) {
this.moveSpeed = 0.0;
this.climbSpeed = 0.0;
this.forceVelocity.assign(Vector3d.ZERO);
this.appliedVelocities.clear();
}
if (this.canAct(ref, componentAccessor)) {
this.tempDirection.assign(dir.x, 0.0, dir.z);
double maxVerticalSpeed = this.maxVerticalSpeed * this.effectHorizontalSpeedMultiplier;
double hSpeed = this.tempDirection.length() * this.getMaximumSpeed();
double vSpeed = dir.y * maxVerticalSpeed;
this.moveSpeed = NPCPhysicsMath.accelerateToTargetSpeed(this.moveSpeed, hSpeed, dt, this.acceleration, this.getMaximumSpeed());
this.climbSpeed = NPCPhysicsMath.accelerateToTargetSpeed(this.climbSpeed, vSpeed, dt, this.acceleration, this.acceleration, -maxVerticalSpeed, maxVerticalSpeed);
float maxRotation = (float)(dt * this.getCurrentMaxBodyRotationSpeed());
boolean isMoving = this.moveSpeed * this.moveSpeed + this.climbSpeed * this.climbSpeed > 1.0E-12 && steering.hasTranslation();
float newHeading;
float newPitch;
if (this.moveSpeed * this.moveSpeed > 1.0000000000000002E-10 && steering.hasTranslation()) {
newHeading = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(dir.x, dir.z));
newPitch = PhysicsMath.normalizeTurnAngle(PhysicsMath.pitchFromDirection(dir.x, dir.y, dir.z));
} else {
translation.assign(Vector3d.ZERO);
if (steering.hasYaw()) {
newHeading = steering.getYaw();
} else {
newHeading = heading;
}
steering.clearYaw();
if (steering.hasPitch()) {
newPitch = steering.getPitch();
} else {
newPitch = pitch;
}
steering.clearPitch();
}
float turnAngle = NPCPhysicsMath.turnAngle(heading, newHeading);
float inclinationAngle = NPCPhysicsMath.turnAngle(pitch, newPitch);
if (Math.abs(turnAngle) > this.maxMoveTurnAngle) {
this.moveSpeed = 0.0;
}
if (this.isNearZero(turnAngle)) {
heading = newHeading;
turnAngle = 0.0F;
} else {
turnAngle = MathUtil.clamp(turnAngle, -maxRotation, maxRotation);
heading += turnAngle;
if (!isMoving) {
this.setMotionKind(MotionKind.SWIMMING_TURNING);
}
}
heading = PhysicsMath.normalizeTurnAngle(heading);
if (this.isNearZero(inclinationAngle)) {
pitch = newPitch;
} else {
inclinationAngle = MathUtil.clamp(inclinationAngle, -maxRotation, maxRotation);
pitch += inclinationAngle;
}
pitch = PhysicsMath.normalizeTurnAngle(pitch);
if (!steering.hasYaw()) {
steering.setYaw(heading);
}
if (!steering.hasPitch()) {
steering.setPitch(pitch);
}
if (this.debugModeSteer) {
LOGGER.at(Level.INFO).log("=== Compute = t =%.4f v =%.4f h =%.4f nh=%.4f dh=%.4f", dt, this.moveSpeed, 57.295776F * heading, 57.295776F * newHeading, 57.295776F * turnAngle);
}
this.computeTranslation(translation, dt, heading, this.moveSpeed, this.climbSpeed);
return dt;
} else {
double maxSpeed = this.moveProbe.isInWater() ? this.maxSinkSpeed : this.maxFallSpeed;
boolean onGround = this.onGround();
if (!this.isAlive(ref, componentAccessor)) {
steering.setYaw(this.getYaw());
steering.setPitch(onGround ? 0.0F : this.getPitch());
this.forceVelocity.assign(Vector3d.ZERO);
this.appliedVelocities.clear();
this.moveSpeed = 0.0;
this.climbSpeed = 0.0;
if (onGround) {
translation.assign(Vector3d.ZERO);
} else {
Velocity velocityComponent = (Velocity)componentAccessor.getComponent(ref, Velocity.getComponentType());
double sinkSpeed = velocityComponent.getVelocity().y;
sinkSpeed = NPCPhysicsMath.gravityDrag(sinkSpeed, 5.0 * this.gravity, dt, maxSpeed);
translation.assign(0.0, sinkSpeed, 0.0).scale(dt);
}
return dt;
} else if (this.forceVelocity.equals(Vector3d.ZERO) && this.appliedVelocities.isEmpty()) {
if (!steering.hasYaw()) {
steering.setYaw(heading);
}
if (!steering.hasPitch()) {
steering.setPitch(this.onGround() ? 0.0F : pitch);
}
this.climbSpeed = NPCPhysicsMath.gravityDrag(this.climbSpeed, 5.0 * this.gravity, dt, maxSpeed);
this.computeTranslation(translation, dt, heading, this.moveSpeed, this.climbSpeed);
return dt;
} else {
this.moveSpeed = 0.0;
this.climbSpeed = 0.0;
if (!this.isObstructed()) {
translation.assign(this.forceVelocity);
for(int i = 0; i < this.appliedVelocities.size(); ++i) {
MotionControllerBase.AppliedVelocity entry = (MotionControllerBase.AppliedVelocity)this.appliedVelocities.get(i);
if (entry.velocity.y + this.forceVelocity.y <= 0.0 || entry.velocity.y < 0.0) {
entry.canClear = true;
}
if (onGround && entry.canClear) {
entry.velocity.y = 0.0;
}
translation.add(entry.velocity);
}
} else {
translation.assign(Vector3d.ZERO);
this.appliedVelocities.clear();
this.forceVelocity.assign(Vector3d.ZERO);
}
translation.y = NPCPhysicsMath.gravityDrag(this.forceVelocity.y, 5.0 * this.gravity, dt, maxSpeed);
translation.scale(dt);
steering.setYaw(this.getYaw());
steering.setPitch(this.getPitch());
return dt;
}
}
}
protected boolean shouldDampenAppliedVelocitiesY() {
return true;
}
protected boolean shouldAlwaysUseGroundResistance() {
return true;
}
private void computeTranslation(@Nonnull Vector3d translation, double dt, float heading, double moveSpeed, double climbSpeed) {
translation.x = moveSpeed * dt * (double)PhysicsMath.headingX(heading);
translation.z = moveSpeed * dt * (double)PhysicsMath.headingZ(heading);
translation.y = climbSpeed * dt;
translation.clipToZero(this.getEpsilonSpeed());
}
private boolean isNearZero(float angle) {
float epsilonAngle = this.getEpsilonAngle();
return angle >= -epsilonAngle && angle <= epsilonAngle;
}
public void setMotionKind(MotionKind motionKind) {
if (!VALID_MOTIONS.contains(motionKind)) {
motionKind = MotionKind.SWIMMING;
}
super.setMotionKind(motionKind);
}
protected double executeMove(@Nonnull Ref<EntityStore> ref, @Nonnull Role role, double dt, @Nonnull Vector3d translation, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) {
throw new IllegalStateException("Invalid position");
} else {
boolean canAct = this.canAct(ref, componentAccessor);
this.collisionResult.setCollisionByMaterial(canAct ? 5 : 4);
if (this.debugModeBlockCollisions) {
this.collisionResult.setLogger(LOGGER);
}
CollisionModule.get();
CollisionModule.findCollisions(this.collisionBoundingBox, this.position, translation, this.collisionResult, componentAccessor);
if (this.debugModeBlockCollisions) {
this.collisionResult.setLogger((HytaleLogger)null);
}
if (this.debugModeCollisions) {
this.dumpCollisionResults();
}
this.lastValidPosition.assign(this.position);
this.isObstructed = false;
this.collisionWithSolid = false;
BlockCollisionData collision = this.collisionResult.getFirstBlockCollision();
if (collision == null) {
double time = dt;
this.position.add(translation);
if (!this.moveProbe.probePosition(ref, this.collisionBoundingBox, this.position, this.collisionResult, this.swimDepth, componentAccessor) || canAct && !this.moveProbe.isInWater()) {
time = this.bisect(ref, this.lastValidPosition, 0.0, this.position, dt, this.position, componentAccessor);
this.isObstructed = true;
if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Move - Dive: No Collision, Bisect pos=%s, blocked=%s", Vector3d.formatShortString(this.position), this.isObstructed);
}
} else if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Move - Dive: No collision, pos=%s, blocked=%s", Vector3d.formatShortString(this.position), this.isObstructed);
}
if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) {
throw new IllegalStateException("Invalid position");
} else {
this.processTriggers(ref, this.collisionResult, time / dt, componentAccessor);
return time;
}
} else if (this.debugModeValidatePositions && !this.isValidPosition(collision.collisionPoint, this.collisionResult, componentAccessor)) {
throw new IllegalStateException("Invalid position");
} else {
double collisionStart = collision.collisionStart;
this.position.assign(collision.collisionPoint);
this.isObstructed = true;
this.collisionWithSolid = collision.blockMaterial == BlockMaterial.Solid;
if (!this.moveProbe.probePosition(ref, this.collisionBoundingBox, this.position, this.collisionResult, this.swimDepth, componentAccessor) || canAct && !this.moveProbe.isInWater()) {
collisionStart = this.bisect(ref, this.lastValidPosition, 0.0, this.position, collisionStart, this.position, componentAccessor);
if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Move - Dive: Collision with solid=%s, Bisect pos=%s, blocked=%s", this.collisionWithSolid, Vector3d.formatShortString(this.position), this.isObstructed);
}
} else if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Move - Dive: Collision with solid=%s, pos=%s, blocked=%s", this.collisionWithSolid, Vector3d.formatShortString(this.position), this.isObstructed);
}
if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) {
throw new IllegalStateException("Invalid position");
} else {
this.processTriggers(ref, this.collisionResult, collisionStart, componentAccessor);
return dt * collisionStart;
}
}
}
}
public void constrainRotations(Role role, @Nonnull TransformComponent transform) {
transform.getRotation().setRoll(0.0F);
}
public double getCurrentMaxBodyRotationSpeed() {
return this.maxRotationSpeed * this.effectHorizontalSpeedMultiplier;
}
public boolean canAct(@Nonnull Ref<EntityStore> ref, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
return super.canAct(ref, componentAccessor) && this.moveProbe.isInWater();
}
public boolean inAir() {
return !this.moveProbe.isOnGround();
}
public boolean inWater() {
return this.moveProbe.isInWater();
}
public boolean onGround() {
return this.moveProbe.isOnGround();
}
@Nonnull
public String getType() {
return "Dive";
}
public double bisect(@Nonnull Ref<EntityStore> ref, @Nonnull Vector3d validPosition, double validDistance, @Nonnull Vector3d invalidPosition, double invalidDistance, @Nonnull Vector3d result, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
return NPCPhysicsMath.lerp(validDistance, invalidDistance, this.bisect(ref, validPosition, invalidPosition, result, componentAccessor));
}
public double bisect(@Nonnull Ref<EntityStore> ref, @Nonnull Vector3d validPosition, @Nonnull Vector3d invalidPosition, @Nonnull Vector3d result, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
return this.bisect(validPosition, invalidPosition, this, (_this, position) -> _this.probeMoveProbe.probePosition(ref, _this.collisionBoundingBox, position, _this.collisionResult, _this.swimDepth, componentAccessor), result);
}
public double probeMove(@Nonnull Ref<EntityStore> ref, @Nonnull ProbeMoveData probeMoveData, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
boolean saveSegments = probeMoveData.startProbing();
this.collisionResult.setCollisionByMaterial(5);
Vector3d probePosition = probeMoveData.probePosition;
Vector3d probeMovement = probeMoveData.probeDirection;
CollisionModule collisionModule = CollisionModule.get();
if (saveSegments) {
probeMoveData.addStartSegment(probePosition, false);
}
if (!this.probeMoveProbe.probePosition(ref, this.collisionBoundingBox, probePosition, this.collisionResult, this.swimDepth, componentAccessor)) {
if (saveSegments) {
probeMoveData.addEndSegment(probePosition, false, 0.0);
}
return 0.0;
} else {
double maxDistance = probeMovement.length();
CollisionModule.findCollisions(this.collisionBoundingBox, probePosition, probeMovement, this.collisionResult, componentAccessor);
if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Probe Step: pos=%s mov=%s left=%s", Vector3d.formatShortString(probePosition), Vector3d.formatShortString(probeMovement), maxDistance);
}
if (this.debugModeCollisions) {
this.dumpCollisionResults();
}
BlockCollisionData collision = this.collisionResult.getFirstBlockCollision();
this.tempPosition.assign(probePosition);
if (collision == null) {
probePosition.add(probeMovement);
probeMovement.assign(Vector3d.ZERO);
double distanceTravelled;
if (this.probeMoveProbe.probePosition(ref, this.collisionBoundingBox, probePosition, this.collisionResult, this.swimDepth, componentAccessor) && this.probeMoveProbe.isInWater()) {
distanceTravelled = maxDistance;
if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Probe - Dive: No Collision, valid pos=%s, distanceLeft=%s", Vector3d.formatShortString(probePosition), maxDistance - maxDistance);
}
} else {
distanceTravelled = this.bisect(ref, this.tempPosition, 0.0, probePosition, maxDistance, probePosition, componentAccessor);
if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Probe - Dive: No Collision, Bisect pos=%s, distanceLeft=%s", Vector3d.formatShortString(probePosition), maxDistance - distanceTravelled);
}
}
if (this.debugModeValidatePositions && !this.isValidPosition(this.tempPosition, this.collisionResult, componentAccessor)) {
throw new IllegalStateException("Invalid position");
} else {
if (saveSegments) {
probeMoveData.addMoveSegment(probePosition, false, distanceTravelled);
probeMoveData.addEndSegment(probePosition, false, distanceTravelled);
}
if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Probe Move done: No collision - maxDistance=%s distanceLeft=%s", maxDistance, maxDistance - distanceTravelled);
}
return distanceTravelled;
}
} else {
double collisionStart = collision.collisionStart;
double distanceTravelled = maxDistance * collisionStart;
probePosition.assign(collision.collisionPoint);
if (this.probeMoveProbe.probePosition(ref, this.collisionBoundingBox, probePosition, this.collisionResult, this.swimDepth, componentAccessor) && this.probeMoveProbe.isInWater()) {
if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Probe - Dive: Collision, valid pos=%s, distanceLeft=%s, collision start=%s", Vector3d.formatShortString(probePosition), maxDistance - distanceTravelled, collisionStart);
}
} else {
distanceTravelled = this.bisect(ref, this.tempPosition, 0.0, probePosition, distanceTravelled, probePosition, componentAccessor);
if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Probe - Dive: Collision, Bisect pos=%s, distanceLeft=%s, collision start=%s", Vector3d.formatShortString(probePosition), maxDistance - distanceTravelled, collisionStart);
}
}
if (this.debugModeValidatePositions && !this.isValidPosition(probePosition, this.collisionResult, componentAccessor)) {
throw new IllegalStateException("Invalid position");
} else {
if (saveSegments) {
if (this.getWorldNormal().equals(collision.collisionNormal)) {
probeMoveData.addHitGroundSegment(probePosition, distanceTravelled, collision.collisionNormal, collision.blockId);
} else {
probeMoveData.addHitWallSegment(probePosition, false, distanceTravelled, collision.collisionNormal, collision.blockId);
}
}
if (saveSegments) {
probeMoveData.addEndSegment(probePosition, false, distanceTravelled);
}
if (this.debugModeMove) {
LOGGER.at(Level.INFO).log("Probe Move done: maxDistance=%s distanceLeft=%s", maxDistance, maxDistance - distanceTravelled);
}
return distanceTravelled;
}
}
}
}
public void spawned() {
}
public double getCurrentSpeed() {
return this.moveSpeed;
}
public double getCurrentTurnRadius() {
return 0.0;
}
public float getMaxClimbAngle() {
return 1.5707964F;
}
public float getMaxSinkAngle() {
return 1.5707964F;
}
public double getMaximumSpeed() {
return this.maxHorizontalSpeed * this.effectHorizontalSpeedMultiplier;
}
public boolean isFastMotionKind(double speed) {
return this.currentRelativeSpeed > this.fastDiveThreshold;
}
public boolean is2D() {
return false;
}
public boolean canRestAtPlace() {
return true;
}
public double getDesiredAltitudeWeight() {
return this.desiredDepthWeight;
}
public double getHeightOverGround() {
return (double)this.probeMoveProbe.getHeightOverGround();
}
public boolean estimateVelocity(Steering steering, @Nonnull Vector3d velocityOut) {
velocityOut.assign(Vector3d.ZERO);
return false;
}
public void updateModelParameters(Ref<EntityStore> ref, Model model, @Nonnull Box boundingBox, ComponentAccessor<EntityStore> componentAccessor) {
super.updateModelParameters(ref, model, boundingBox, componentAccessor);
this.swimDepth = relativeSwimDepthToHeight(ref, this.relativeSwimDepth, model, boundingBox, componentAccessor);
}
protected void dampForceVelocity(@Nonnull Vector3d forceVelocity, double forceVelocityDamping, double interval, ComponentAccessor<EntityStore> componentAccessor) {
if (forceVelocity.squaredLength() < this.minSpeedAfterForceSquared) {
forceVelocity.assign(Vector3d.ZERO);
} else {
NPCPhysicsMath.deccelerateToStop(forceVelocity, this.getDampingDeceleration(), interval);
}
}
public static double relativeSwimDepthToBoundingBox(double swimDepth, @Nullable Box boundingBox, float eyeHeight) {
if (boundingBox == null) {
return 0.5;
} else {
return swimDepth < 0.0 ? NPCPhysicsMath.lerp((double)eyeHeight, boundingBox.getMin().getY(), -swimDepth) : NPCPhysicsMath.lerp((double)eyeHeight, boundingBox.getMax().getY(), swimDepth);
}
}
public static double relativeSwimDepthToHeight(double swimDepth, @Nullable Box boundingBox, float eyeHeight) {
return boundingBox == null ? 0.5 : relativeSwimDepthToBoundingBox(swimDepth, boundingBox, eyeHeight) - boundingBox.getMin().getY();
}
public static double relativeSwimDepthToHeight(@Nullable Ref<EntityStore> ref, double swimDepth, Model model, Box boundingBox, @Nullable ComponentAccessor<EntityStore> componentAccessor) {
return relativeSwimDepthToHeight(swimDepth, boundingBox, model != null ? model.getEyeHeight(ref, componentAccessor) : 0.0F);
}
public double getDampingDeceleration() {
return this.forceVelocityDamping * DAMPING_FACTOR;
}
static {
VALID_MOTIONS = EnumSet.of(MotionKind.SWIMMING, MotionKind.SWIMMING_TURNING, MotionKind.MOVING);
}
}