minecraft-src/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
2025-07-04 03:15:13 +03:00

574 lines
20 KiB
Java

package net.minecraft.world.entity.vehicle;
import com.mojang.datafixers.util.Pair;
import io.netty.buffer.ByteBuf;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.animal.IronGolem;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.block.BaseRailBlock;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.PoweredRailBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.RailShape;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
public class NewMinecartBehavior extends MinecartBehavior {
public static final int POS_ROT_LERP_TICKS = 3;
public static final double ON_RAIL_Y_OFFSET = 0.1;
public static final double OPPOSING_SLOPES_REST_AT_SPEED_THRESHOLD = 0.005;
@Nullable
private NewMinecartBehavior.StepPartialTicks cacheIndexAlpha;
private int cachedLerpDelay;
private float cachedPartialTick;
private int lerpDelay = 0;
public final List<NewMinecartBehavior.MinecartStep> lerpSteps = new LinkedList();
public final List<NewMinecartBehavior.MinecartStep> currentLerpSteps = new LinkedList();
public double currentLerpStepsTotalWeight = 0.0;
public NewMinecartBehavior.MinecartStep oldLerp = NewMinecartBehavior.MinecartStep.ZERO;
public NewMinecartBehavior(AbstractMinecart abstractMinecart) {
super(abstractMinecart);
}
@Override
public void tick() {
if (this.level() instanceof ServerLevel serverLevel) {
BlockPos var5 = this.minecart.getCurrentBlockPosOrRailBelow();
BlockState blockState = this.level().getBlockState(var5);
if (this.minecart.isFirstTick()) {
this.minecart.setOnRails(BaseRailBlock.isRail(blockState));
this.adjustToRails(var5, blockState, true);
}
this.minecart.applyGravity();
this.minecart.moveAlongTrack(serverLevel);
} else {
this.lerpClientPositionAndRotation();
boolean bl = BaseRailBlock.isRail(this.level().getBlockState(this.minecart.getCurrentBlockPosOrRailBelow()));
this.minecart.setOnRails(bl);
}
}
private void lerpClientPositionAndRotation() {
if (--this.lerpDelay <= 0) {
this.setOldLerpValues();
this.currentLerpSteps.clear();
if (!this.lerpSteps.isEmpty()) {
this.currentLerpSteps.addAll(this.lerpSteps);
this.lerpSteps.clear();
this.currentLerpStepsTotalWeight = 0.0;
for (NewMinecartBehavior.MinecartStep minecartStep : this.currentLerpSteps) {
this.currentLerpStepsTotalWeight = this.currentLerpStepsTotalWeight + minecartStep.weight;
}
this.lerpDelay = this.currentLerpStepsTotalWeight == 0.0 ? 0 : 3;
}
}
if (this.cartHasPosRotLerp()) {
this.setPos(this.getCartLerpPosition(1.0F));
this.setDeltaMovement(this.getCartLerpMovements(1.0F));
this.setXRot(this.getCartLerpXRot(1.0F));
this.setYRot(this.getCartLerpYRot(1.0F));
}
}
public void setOldLerpValues() {
this.oldLerp = new NewMinecartBehavior.MinecartStep(this.position(), this.getDeltaMovement(), this.getYRot(), this.getXRot(), 0.0F);
}
public boolean cartHasPosRotLerp() {
return !this.currentLerpSteps.isEmpty();
}
public float getCartLerpXRot(float partialTick) {
NewMinecartBehavior.StepPartialTicks stepPartialTicks = this.getCurrentLerpStep(partialTick);
return Mth.rotLerp(stepPartialTicks.partialTicksInStep, stepPartialTicks.previousStep.xRot, stepPartialTicks.currentStep.xRot);
}
public float getCartLerpYRot(float partialTick) {
NewMinecartBehavior.StepPartialTicks stepPartialTicks = this.getCurrentLerpStep(partialTick);
return Mth.rotLerp(stepPartialTicks.partialTicksInStep, stepPartialTicks.previousStep.yRot, stepPartialTicks.currentStep.yRot);
}
public Vec3 getCartLerpPosition(float partialTick) {
NewMinecartBehavior.StepPartialTicks stepPartialTicks = this.getCurrentLerpStep(partialTick);
return Mth.lerp(stepPartialTicks.partialTicksInStep, stepPartialTicks.previousStep.position, stepPartialTicks.currentStep.position);
}
public Vec3 getCartLerpMovements(float partialTick) {
NewMinecartBehavior.StepPartialTicks stepPartialTicks = this.getCurrentLerpStep(partialTick);
return Mth.lerp(stepPartialTicks.partialTicksInStep, stepPartialTicks.previousStep.movement, stepPartialTicks.currentStep.movement);
}
private NewMinecartBehavior.StepPartialTicks getCurrentLerpStep(float partialTick) {
if (partialTick == this.cachedPartialTick && this.lerpDelay == this.cachedLerpDelay && this.cacheIndexAlpha != null) {
return this.cacheIndexAlpha;
} else {
float f = (3 - this.lerpDelay + partialTick) / 3.0F;
float g = 0.0F;
float h = 1.0F;
boolean bl = false;
int i;
for (i = 0; i < this.currentLerpSteps.size(); i++) {
float j = ((NewMinecartBehavior.MinecartStep)this.currentLerpSteps.get(i)).weight;
if (!(j <= 0.0F)) {
g += j;
if (g >= this.currentLerpStepsTotalWeight * f) {
float k = g - j;
h = (float)((f * this.currentLerpStepsTotalWeight - k) / j);
bl = true;
break;
}
}
}
if (!bl) {
i = this.currentLerpSteps.size() - 1;
}
NewMinecartBehavior.MinecartStep minecartStep = (NewMinecartBehavior.MinecartStep)this.currentLerpSteps.get(i);
NewMinecartBehavior.MinecartStep minecartStep2 = i > 0 ? (NewMinecartBehavior.MinecartStep)this.currentLerpSteps.get(i - 1) : this.oldLerp;
this.cacheIndexAlpha = new NewMinecartBehavior.StepPartialTicks(h, minecartStep, minecartStep2);
this.cachedLerpDelay = this.lerpDelay;
this.cachedPartialTick = partialTick;
return this.cacheIndexAlpha;
}
}
public void adjustToRails(BlockPos pos, BlockState state, boolean snapToStart) {
if (BaseRailBlock.isRail(state)) {
RailShape railShape = state.getValue(((BaseRailBlock)state.getBlock()).getShapeProperty());
Pair<Vec3i, Vec3i> pair = AbstractMinecart.exits(railShape);
Vec3 vec3 = new Vec3(pair.getFirst()).scale(0.5);
Vec3 vec32 = new Vec3(pair.getSecond()).scale(0.5);
Vec3 vec33 = vec3.horizontal();
Vec3 vec34 = vec32.horizontal();
if (this.getDeltaMovement().length() > 1.0E-5F && this.getDeltaMovement().dot(vec33) < this.getDeltaMovement().dot(vec34)
|| this.isDecending(vec34, railShape)) {
Vec3 vec35 = vec33;
vec33 = vec34;
vec34 = vec35;
}
float f = 180.0F - (float)(Math.atan2(vec33.z, vec33.x) * 180.0 / Math.PI);
f += this.minecart.isFlipped() ? 180.0F : 0.0F;
Vec3 vec36 = this.position();
boolean bl = vec3.x() != vec32.x() && vec3.z() != vec32.z();
Vec3 vec310;
if (bl) {
Vec3 vec37 = vec32.subtract(vec3);
Vec3 vec38 = vec36.subtract(pos.getBottomCenter()).subtract(vec3);
Vec3 vec39 = vec37.scale(vec37.dot(vec38) / vec37.dot(vec37));
vec310 = pos.getBottomCenter().add(vec3).add(vec39);
f = 180.0F - (float)(Math.atan2(vec39.z, vec39.x) * 180.0 / Math.PI);
f += this.minecart.isFlipped() ? 180.0F : 0.0F;
} else {
boolean bl2 = vec3.subtract(vec32).x != 0.0;
boolean bl3 = vec3.subtract(vec32).z != 0.0;
vec310 = new Vec3(bl3 ? pos.getCenter().x : vec36.x, pos.getY(), bl2 ? pos.getCenter().z : vec36.z);
}
Vec3 vec37 = vec310.subtract(vec36);
this.setPos(vec36.add(vec37));
float g = 0.0F;
boolean bl4 = vec3.y() != vec32.y();
if (bl4) {
Vec3 vec311 = pos.getBottomCenter().add(vec34);
double d = vec311.distanceTo(this.position());
this.setPos(this.position().add(0.0, d + 0.1, 0.0));
g = this.minecart.isFlipped() ? 45.0F : -45.0F;
} else {
this.setPos(this.position().add(0.0, 0.1, 0.0));
}
this.setRotation(f, g);
double e = vec36.distanceTo(this.position());
if (e > 0.0) {
this.lerpSteps
.add(new NewMinecartBehavior.MinecartStep(this.position(), this.getDeltaMovement(), this.getYRot(), this.getXRot(), snapToStart ? 0.0F : (float)e));
}
}
}
private void setRotation(float yRot, float xRot) {
double d = Math.abs(yRot - this.getYRot());
if (d >= 175.0 && d <= 185.0) {
this.minecart.setFlipped(!this.minecart.isFlipped());
yRot -= 180.0F;
xRot *= -1.0F;
}
xRot = Math.clamp(xRot, -45.0F, 45.0F);
this.setXRot(xRot % 360.0F);
this.setYRot(yRot % 360.0F);
}
@Override
public void moveAlongTrack(ServerLevel level) {
for (NewMinecartBehavior.TrackIteration trackIteration = new NewMinecartBehavior.TrackIteration();
trackIteration.shouldIterate() && this.minecart.isAlive();
trackIteration.firstIteration = false
) {
Vec3 vec3 = this.getDeltaMovement();
BlockPos blockPos = this.minecart.getCurrentBlockPosOrRailBelow();
BlockState blockState = this.level().getBlockState(blockPos);
boolean bl = BaseRailBlock.isRail(blockState);
if (this.minecart.isOnRails() != bl) {
this.minecart.setOnRails(bl);
this.adjustToRails(blockPos, blockState, false);
}
if (bl) {
this.minecart.resetFallDistance();
this.minecart.setOldPosAndRot();
if (blockState.is(Blocks.ACTIVATOR_RAIL)) {
this.minecart.activateMinecart(blockPos.getX(), blockPos.getY(), blockPos.getZ(), (Boolean)blockState.getValue(PoweredRailBlock.POWERED));
}
RailShape railShape = blockState.getValue(((BaseRailBlock)blockState.getBlock()).getShapeProperty());
Vec3 vec32 = this.calculateTrackSpeed(level, vec3.horizontal(), trackIteration, blockPos, blockState, railShape);
if (trackIteration.firstIteration) {
trackIteration.movementLeft = vec32.horizontalDistance();
} else {
trackIteration.movementLeft = trackIteration.movementLeft + (vec32.horizontalDistance() - vec3.horizontalDistance());
}
this.setDeltaMovement(vec32);
trackIteration.movementLeft = this.minecart.makeStepAlongTrack(blockPos, railShape, trackIteration.movementLeft);
} else {
this.minecart.comeOffTrack(level);
trackIteration.movementLeft = 0.0;
}
Vec3 vec33 = this.position();
Vec3 vec32 = vec33.subtract(this.minecart.oldPosition());
double d = vec32.length();
if (d > 1.0E-5F) {
if (!(vec32.horizontalDistanceSqr() > 1.0E-5F)) {
if (!this.minecart.isOnRails()) {
this.setXRot(this.minecart.onGround() ? 0.0F : Mth.rotLerp(0.2F, this.getXRot(), 0.0F));
}
} else {
float f = 180.0F - (float)(Math.atan2(vec32.z, vec32.x) * 180.0 / Math.PI);
float g = this.minecart.onGround() && !this.minecart.isOnRails()
? 0.0F
: 90.0F - (float)(Math.atan2(vec32.horizontalDistance(), vec32.y) * 180.0 / Math.PI);
f += this.minecart.isFlipped() ? 180.0F : 0.0F;
g *= this.minecart.isFlipped() ? -1.0F : 1.0F;
this.setRotation(f, g);
}
this.lerpSteps
.add(new NewMinecartBehavior.MinecartStep(vec33, this.getDeltaMovement(), this.getYRot(), this.getXRot(), (float)Math.min(d, this.getMaxSpeed(level))));
} else if (vec3.horizontalDistanceSqr() > 0.0) {
this.lerpSteps.add(new NewMinecartBehavior.MinecartStep(vec33, this.getDeltaMovement(), this.getYRot(), this.getXRot(), 1.0F));
}
if (d > 1.0E-5F || trackIteration.firstIteration) {
this.minecart.applyEffectsFromBlocks();
this.minecart.applyEffectsFromBlocks();
}
}
}
private Vec3 calculateTrackSpeed(
ServerLevel level, Vec3 speed, NewMinecartBehavior.TrackIteration trackIteration, BlockPos pos, BlockState state, RailShape railShape
) {
Vec3 vec3 = speed;
if (!trackIteration.hasGainedSlopeSpeed) {
Vec3 vec32 = this.calculateSlopeSpeed(speed, railShape);
if (vec32.horizontalDistanceSqr() != speed.horizontalDistanceSqr()) {
trackIteration.hasGainedSlopeSpeed = true;
vec3 = vec32;
}
}
if (trackIteration.firstIteration) {
Vec3 vec32 = this.calculatePlayerInputSpeed(vec3);
if (vec32.horizontalDistanceSqr() != vec3.horizontalDistanceSqr()) {
trackIteration.hasHalted = true;
vec3 = vec32;
}
}
if (!trackIteration.hasHalted) {
Vec3 vec32 = this.calculateHaltTrackSpeed(vec3, state);
if (vec32.horizontalDistanceSqr() != vec3.horizontalDistanceSqr()) {
trackIteration.hasHalted = true;
vec3 = vec32;
}
}
if (trackIteration.firstIteration) {
vec3 = this.minecart.applyNaturalSlowdown(vec3);
if (vec3.lengthSqr() > 0.0) {
double d = Math.min(vec3.length(), this.minecart.getMaxSpeed(level));
vec3 = vec3.normalize().scale(d);
}
}
if (!trackIteration.hasBoosted) {
Vec3 vec32 = this.calculateBoostTrackSpeed(vec3, pos, state);
if (vec32.horizontalDistanceSqr() != vec3.horizontalDistanceSqr()) {
trackIteration.hasBoosted = true;
vec3 = vec32;
}
}
return vec3;
}
private Vec3 calculateSlopeSpeed(Vec3 speed, RailShape railShape) {
double d = Math.max(0.0078125, speed.horizontalDistance() * 0.02);
if (this.minecart.isInWater()) {
d *= 0.2;
}
return switch (railShape) {
case ASCENDING_EAST -> speed.add(-d, 0.0, 0.0);
case ASCENDING_WEST -> speed.add(d, 0.0, 0.0);
case ASCENDING_NORTH -> speed.add(0.0, 0.0, d);
case ASCENDING_SOUTH -> speed.add(0.0, 0.0, -d);
default -> speed;
};
}
private Vec3 calculatePlayerInputSpeed(Vec3 speed) {
if (this.minecart.getFirstPassenger() instanceof ServerPlayer serverPlayer) {
Vec3 vec3 = serverPlayer.getLastClientMoveIntent();
if (vec3.lengthSqr() > 0.0) {
Vec3 vec32 = vec3.normalize();
double d = speed.horizontalDistanceSqr();
if (vec32.lengthSqr() > 0.0 && d < 0.01) {
return speed.add(new Vec3(vec32.x, 0.0, vec32.z).normalize().scale(0.001));
}
}
return speed;
} else {
return speed;
}
}
private Vec3 calculateHaltTrackSpeed(Vec3 speed, BlockState state) {
if (state.is(Blocks.POWERED_RAIL) && !(Boolean)state.getValue(PoweredRailBlock.POWERED)) {
return speed.length() < 0.03 ? Vec3.ZERO : speed.scale(0.5);
} else {
return speed;
}
}
private Vec3 calculateBoostTrackSpeed(Vec3 speed, BlockPos pos, BlockState state) {
if (state.is(Blocks.POWERED_RAIL) && (Boolean)state.getValue(PoweredRailBlock.POWERED)) {
if (speed.length() > 0.01) {
return speed.normalize().scale(speed.length() + 0.06);
} else {
Vec3 vec3 = this.minecart.getRedstoneDirection(pos);
return vec3.lengthSqr() <= 0.0 ? speed : vec3.scale(speed.length() + 0.2);
}
} else {
return speed;
}
}
@Override
public double stepAlongTrack(BlockPos pos, RailShape railShape, double speed) {
if (speed < 1.0E-5F) {
return 0.0;
} else {
Vec3 vec3 = this.position();
Pair<Vec3i, Vec3i> pair = AbstractMinecart.exits(railShape);
Vec3i vec3i = pair.getFirst();
Vec3i vec3i2 = pair.getSecond();
Vec3 vec32 = this.getDeltaMovement().horizontal();
if (vec32.length() < 1.0E-5F) {
this.setDeltaMovement(Vec3.ZERO);
return 0.0;
} else {
boolean bl = vec3i.getY() != vec3i2.getY();
Vec3 vec33 = new Vec3(vec3i2).scale(0.5).horizontal();
Vec3 vec34 = new Vec3(vec3i).scale(0.5).horizontal();
if (vec32.dot(vec34) < vec32.dot(vec33)) {
vec34 = vec33;
}
Vec3 vec35 = pos.getBottomCenter().add(vec34).add(0.0, 0.1, 0.0).add(vec34.normalize().scale(1.0E-5F));
if (bl && !this.isDecending(vec32, railShape)) {
vec35 = vec35.add(0.0, 1.0, 0.0);
}
Vec3 vec36 = vec35.subtract(this.position()).normalize();
vec32 = vec36.scale(vec32.length() / vec36.horizontalDistance());
Vec3 vec37 = vec3.add(vec32.normalize().scale(speed * (bl ? Mth.SQRT_OF_TWO : 1.0F)));
if (vec3.distanceToSqr(vec35) <= vec3.distanceToSqr(vec37)) {
speed = vec35.subtract(vec37).horizontalDistance();
vec37 = vec35;
} else {
speed = 0.0;
}
this.minecart.move(MoverType.SELF, vec37.subtract(vec3));
BlockState blockState = this.level().getBlockState(BlockPos.containing(vec37));
if (bl) {
if (BaseRailBlock.isRail(blockState)) {
RailShape railShape2 = blockState.getValue(((BaseRailBlock)blockState.getBlock()).getShapeProperty());
if (this.restAtVShape(railShape, railShape2)) {
return 0.0;
}
}
double d = vec35.horizontal().distanceTo(this.position().horizontal());
double e = vec35.y + (this.isDecending(vec32, railShape) ? d : -d);
if (this.position().y < e) {
this.setPos(this.position().x, e, this.position().z);
}
}
if (this.position().distanceTo(vec3) < 1.0E-5F && vec37.distanceTo(vec3) > 1.0E-5F) {
this.setDeltaMovement(Vec3.ZERO);
return 0.0;
} else {
this.setDeltaMovement(vec32);
return speed;
}
}
}
}
private boolean restAtVShape(RailShape shape1, RailShape shape2) {
if (this.getDeltaMovement().lengthSqr() < 0.005
&& shape2.isSlope()
&& this.isDecending(this.getDeltaMovement(), shape1)
&& !this.isDecending(this.getDeltaMovement(), shape2)) {
this.setDeltaMovement(Vec3.ZERO);
return true;
} else {
return false;
}
}
@Override
public double getMaxSpeed(ServerLevel level) {
return level.getGameRules().getInt(GameRules.RULE_MINECART_MAX_SPEED) * (this.minecart.isInWater() ? 0.5 : 1.0) / 20.0;
}
private boolean isDecending(Vec3 speed, RailShape railShape) {
return switch (railShape) {
case ASCENDING_EAST -> speed.x < 0.0;
case ASCENDING_WEST -> speed.x > 0.0;
case ASCENDING_NORTH -> speed.z > 0.0;
case ASCENDING_SOUTH -> speed.z < 0.0;
default -> false;
};
}
@Override
public double getSlowdownFactor() {
return this.minecart.isVehicle() ? 0.997 : 0.975;
}
@Override
public boolean pushAndPickupEntities() {
boolean bl = this.pickupEntities(this.minecart.getBoundingBox().inflate(0.2, 0.0, 0.2));
if (!this.minecart.horizontalCollision && !this.minecart.verticalCollision) {
return false;
} else {
boolean bl2 = this.pushEntities(this.minecart.getBoundingBox().inflate(1.0E-7));
return bl && !bl2;
}
}
public boolean pickupEntities(AABB box) {
if (this.minecart.isRideable() && !this.minecart.isVehicle()) {
List<Entity> list = this.level().getEntities(this.minecart, box, EntitySelector.pushableBy(this.minecart));
if (!list.isEmpty()) {
for (Entity entity : list) {
if (!(entity instanceof Player)
&& !(entity instanceof IronGolem)
&& !(entity instanceof AbstractMinecart)
&& !this.minecart.isVehicle()
&& !entity.isPassenger()) {
boolean bl = entity.startRiding(this.minecart);
if (bl) {
return true;
}
}
}
}
}
return false;
}
public boolean pushEntities(AABB box) {
boolean bl = false;
if (this.minecart.isRideable()) {
List<Entity> list = this.level().getEntities(this.minecart, box, EntitySelector.pushableBy(this.minecart));
if (!list.isEmpty()) {
for (Entity entity : list) {
if (entity instanceof Player || entity instanceof IronGolem || entity instanceof AbstractMinecart || this.minecart.isVehicle() || entity.isPassenger()) {
entity.push(this.minecart);
bl = true;
}
}
}
} else {
for (Entity entity2 : this.level().getEntities(this.minecart, box)) {
if (!this.minecart.hasPassenger(entity2) && entity2.isPushable() && entity2 instanceof AbstractMinecart) {
entity2.push(this.minecart);
bl = true;
}
}
}
return bl;
}
public record MinecartStep(Vec3 position, Vec3 movement, float yRot, float xRot, float weight) {
public static final StreamCodec<ByteBuf, NewMinecartBehavior.MinecartStep> STREAM_CODEC = StreamCodec.composite(
Vec3.STREAM_CODEC,
NewMinecartBehavior.MinecartStep::position,
Vec3.STREAM_CODEC,
NewMinecartBehavior.MinecartStep::movement,
ByteBufCodecs.ROTATION_BYTE,
NewMinecartBehavior.MinecartStep::yRot,
ByteBufCodecs.ROTATION_BYTE,
NewMinecartBehavior.MinecartStep::xRot,
ByteBufCodecs.FLOAT,
NewMinecartBehavior.MinecartStep::weight,
NewMinecartBehavior.MinecartStep::new
);
public static NewMinecartBehavior.MinecartStep ZERO = new NewMinecartBehavior.MinecartStep(Vec3.ZERO, Vec3.ZERO, 0.0F, 0.0F, 0.0F);
}
record StepPartialTicks(float partialTicksInStep, NewMinecartBehavior.MinecartStep currentStep, NewMinecartBehavior.MinecartStep previousStep) {
}
static class TrackIteration {
double movementLeft = 0.0;
boolean firstIteration = true;
boolean hasGainedSlopeSpeed = false;
boolean hasHalted = false;
boolean hasBoosted = false;
public boolean shouldIterate() {
return this.firstIteration || this.movementLeft > 1.0E-5F;
}
}
}