Code/net/minecraft/world/entity/vehicle/AbstractMinecart.java

600 lines
22 KiB
Java
Raw Normal View History

2025-07-01 06:20:03 +00:00
package net.minecraft.world.entity.vehicle;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.mojang.datafixers.util.Pair;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nullable;
import net.minecraft.BlockUtil;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.RegistryOps;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.InterpolationHandler;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.npc.WanderingTrader;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
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;
public abstract class AbstractMinecart extends VehicleEntity {
private static final Vec3 LOWERED_PASSENGER_ATTACHMENT = new Vec3(0.0, 0.0, 0.0);
private static final EntityDataAccessor<Optional<BlockState>> DATA_ID_CUSTOM_DISPLAY_BLOCK = SynchedEntityData.defineId(
AbstractMinecart.class, EntityDataSerializers.OPTIONAL_BLOCK_STATE
);
private static final EntityDataAccessor<Integer> DATA_ID_DISPLAY_OFFSET = SynchedEntityData.defineId(AbstractMinecart.class, EntityDataSerializers.INT);
private static final ImmutableMap<Pose, ImmutableList<Integer>> POSE_DISMOUNT_HEIGHTS = ImmutableMap.of(
Pose.STANDING, ImmutableList.of(0, 1, -1), Pose.CROUCHING, ImmutableList.of(0, 1, -1), Pose.SWIMMING, ImmutableList.of(0, 1)
);
protected static final float WATER_SLOWDOWN_FACTOR = 0.95F;
private static final boolean DEFAULT_FLIPPED_ROTATION = false;
private boolean onRails;
private boolean flipped = false;
private final MinecartBehavior behavior;
private static final Map<RailShape, Pair<Vec3i, Vec3i>> EXITS = Maps.newEnumMap(
Util.make(
() -> {
Vec3i vec3i = Direction.WEST.getUnitVec3i();
Vec3i vec3i1 = Direction.EAST.getUnitVec3i();
Vec3i vec3i2 = Direction.NORTH.getUnitVec3i();
Vec3i vec3i3 = Direction.SOUTH.getUnitVec3i();
Vec3i vec3i4 = vec3i.below();
Vec3i vec3i5 = vec3i1.below();
Vec3i vec3i6 = vec3i2.below();
Vec3i vec3i7 = vec3i3.below();
return ImmutableMap.of(
RailShape.NORTH_SOUTH,
Pair.of(vec3i2, vec3i3),
RailShape.EAST_WEST,
Pair.of(vec3i, vec3i1),
RailShape.ASCENDING_EAST,
Pair.of(vec3i4, vec3i1),
RailShape.ASCENDING_WEST,
Pair.of(vec3i, vec3i5),
RailShape.ASCENDING_NORTH,
Pair.of(vec3i2, vec3i7),
RailShape.ASCENDING_SOUTH,
Pair.of(vec3i6, vec3i3),
RailShape.SOUTH_EAST,
Pair.of(vec3i3, vec3i1),
RailShape.SOUTH_WEST,
Pair.of(vec3i3, vec3i),
RailShape.NORTH_WEST,
Pair.of(vec3i2, vec3i),
RailShape.NORTH_EAST,
Pair.of(vec3i2, vec3i1)
);
}
)
);
protected AbstractMinecart(EntityType<?> p_38087_, Level p_38088_) {
super(p_38087_, p_38088_);
this.blocksBuilding = true;
if (useExperimentalMovement(p_38088_)) {
this.behavior = new NewMinecartBehavior(this);
} else {
this.behavior = new OldMinecartBehavior(this);
}
}
protected AbstractMinecart(EntityType<?> p_38090_, Level p_38091_, double p_38092_, double p_38093_, double p_38094_) {
this(p_38090_, p_38091_);
this.setInitialPos(p_38092_, p_38093_, p_38094_);
}
public void setInitialPos(double p_364838_, double p_369805_, double p_367256_) {
this.setPos(p_364838_, p_369805_, p_367256_);
this.xo = p_364838_;
this.yo = p_369805_;
this.zo = p_367256_;
}
@Nullable
public static <T extends AbstractMinecart> T createMinecart(
Level p_368792_,
double p_38121_,
double p_38122_,
double p_38123_,
EntityType<T> p_363374_,
EntitySpawnReason p_365925_,
ItemStack p_311363_,
@Nullable Player p_310754_
) {
T t = (T)p_363374_.create(p_368792_, p_365925_);
if (t != null) {
t.setInitialPos(p_38121_, p_38122_, p_38123_);
EntityType.createDefaultStackConfig(p_368792_, p_311363_, p_310754_).accept(t);
if (t.getBehavior() instanceof NewMinecartBehavior newminecartbehavior) {
BlockPos blockpos = t.getCurrentBlockPosOrRailBelow();
BlockState blockstate = p_368792_.getBlockState(blockpos);
newminecartbehavior.adjustToRails(blockpos, blockstate, true);
}
}
return t;
}
public MinecartBehavior getBehavior() {
return this.behavior;
}
@Override
protected Entity.MovementEmission getMovementEmission() {
return Entity.MovementEmission.EVENTS;
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder p_333316_) {
super.defineSynchedData(p_333316_);
p_333316_.define(DATA_ID_CUSTOM_DISPLAY_BLOCK, Optional.empty());
p_333316_.define(DATA_ID_DISPLAY_OFFSET, this.getDefaultDisplayOffset());
}
@Override
public boolean canCollideWith(Entity p_38168_) {
return AbstractBoat.canVehicleCollide(this, p_38168_);
}
@Override
public boolean isPushable() {
return true;
}
@Override
public Vec3 getRelativePortalPosition(Direction.Axis p_38132_, BlockUtil.FoundRectangle p_38133_) {
return LivingEntity.resetForwardDirectionOfRelativePortalPosition(super.getRelativePortalPosition(p_38132_, p_38133_));
}
@Override
protected Vec3 getPassengerAttachmentPoint(Entity p_300806_, EntityDimensions p_300201_, float p_299127_) {
boolean flag = p_300806_ instanceof Villager || p_300806_ instanceof WanderingTrader;
return flag ? LOWERED_PASSENGER_ATTACHMENT : super.getPassengerAttachmentPoint(p_300806_, p_300201_, p_299127_);
}
@Override
public Vec3 getDismountLocationForPassenger(LivingEntity p_38145_) {
Direction direction = this.getMotionDirection();
if (direction.getAxis() == Direction.Axis.Y) {
return super.getDismountLocationForPassenger(p_38145_);
} else {
int[][] aint = DismountHelper.offsetsForDirection(direction);
BlockPos blockpos = this.blockPosition();
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
ImmutableList<Pose> immutablelist = p_38145_.getDismountPoses();
for (Pose pose : immutablelist) {
EntityDimensions entitydimensions = p_38145_.getDimensions(pose);
float f = Math.min(entitydimensions.width(), 1.0F) / 2.0F;
for (int i : POSE_DISMOUNT_HEIGHTS.get(pose)) {
for (int[] aint1 : aint) {
blockpos$mutableblockpos.set(blockpos.getX() + aint1[0], blockpos.getY() + i, blockpos.getZ() + aint1[1]);
double d0 = this.level()
.getBlockFloorHeight(
DismountHelper.nonClimbableShape(this.level(), blockpos$mutableblockpos),
() -> DismountHelper.nonClimbableShape(this.level(), blockpos$mutableblockpos.below())
);
if (DismountHelper.isBlockFloorValid(d0)) {
AABB aabb = new AABB(-f, 0.0, -f, f, entitydimensions.height(), f);
Vec3 vec3 = Vec3.upFromBottomCenterOf(blockpos$mutableblockpos, d0);
if (DismountHelper.canDismountTo(this.level(), p_38145_, aabb.move(vec3))) {
p_38145_.setPose(pose);
return vec3;
}
}
}
}
}
double d1 = this.getBoundingBox().maxY;
blockpos$mutableblockpos.set(blockpos.getX(), d1, blockpos.getZ());
for (Pose pose1 : immutablelist) {
double d2 = p_38145_.getDimensions(pose1).height();
int j = Mth.ceil(d1 - blockpos$mutableblockpos.getY() + d2);
double d3 = DismountHelper.findCeilingFrom(
blockpos$mutableblockpos, j, p_375185_ -> this.level().getBlockState(p_375185_).getCollisionShape(this.level(), p_375185_)
);
if (d1 + d2 <= d3) {
p_38145_.setPose(pose1);
break;
}
}
return super.getDismountLocationForPassenger(p_38145_);
}
}
@Override
protected float getBlockSpeedFactor() {
BlockState blockstate = this.level().getBlockState(this.blockPosition());
return blockstate.is(BlockTags.RAILS) ? 1.0F : super.getBlockSpeedFactor();
}
@Override
public void animateHurt(float p_265349_) {
this.setHurtDir(-this.getHurtDir());
this.setHurtTime(10);
this.setDamage(this.getDamage() + this.getDamage() * 10.0F);
}
@Override
public boolean isPickable() {
return !this.isRemoved();
}
public static Pair<Vec3i, Vec3i> exits(RailShape p_38126_) {
return EXITS.get(p_38126_);
}
@Override
public Direction getMotionDirection() {
return this.behavior.getMotionDirection();
}
@Override
protected double getDefaultGravity() {
return this.isInWater() ? 0.005 : 0.04;
}
@Override
public void tick() {
if (this.getHurtTime() > 0) {
this.setHurtTime(this.getHurtTime() - 1);
}
if (this.getDamage() > 0.0F) {
this.setDamage(this.getDamage() - 1.0F);
}
this.checkBelowWorld();
this.handlePortal();
this.behavior.tick();
this.updateInWaterStateAndDoFluidPushing();
if (this.isInLava()) {
this.lavaIgnite();
this.lavaHurt();
this.fallDistance *= 0.5;
}
this.firstTick = false;
}
public boolean isFirstTick() {
return this.firstTick;
}
public BlockPos getCurrentBlockPosOrRailBelow() {
int i = Mth.floor(this.getX());
int j = Mth.floor(this.getY());
int k = Mth.floor(this.getZ());
if (useExperimentalMovement(this.level())) {
double d0 = this.getY() - 0.1 - 1.0E-5F;
if (this.level().getBlockState(BlockPos.containing(i, d0, k)).is(BlockTags.RAILS)) {
j = Mth.floor(d0);
}
} else if (this.level().getBlockState(new BlockPos(i, j - 1, k)).is(BlockTags.RAILS)) {
j--;
}
return new BlockPos(i, j, k);
}
protected double getMaxSpeed(ServerLevel p_368180_) {
return this.behavior.getMaxSpeed(p_368180_);
}
public void activateMinecart(int p_38111_, int p_38112_, int p_38113_, boolean p_38114_) {
}
@Override
public void lerpPositionAndRotationStep(int p_363253_, double p_361925_, double p_362778_, double p_361683_, double p_360914_, double p_361120_) {
super.lerpPositionAndRotationStep(p_363253_, p_361925_, p_362778_, p_361683_, p_360914_, p_361120_);
}
@Override
public void applyGravity() {
super.applyGravity();
}
@Override
public void reapplyPosition() {
super.reapplyPosition();
}
@Override
public boolean updateInWaterStateAndDoFluidPushing() {
return super.updateInWaterStateAndDoFluidPushing();
}
@Override
public Vec3 getKnownMovement() {
return this.behavior.getKnownMovement(super.getKnownMovement());
}
@Override
public InterpolationHandler getInterpolation() {
return this.behavior.getInterpolation();
}
@Override
public void lerpMotion(double p_38171_, double p_38172_, double p_38173_) {
this.behavior.lerpMotion(p_38171_, p_38172_, p_38173_);
}
protected void moveAlongTrack(ServerLevel p_367889_) {
this.behavior.moveAlongTrack(p_367889_);
}
protected void comeOffTrack(ServerLevel p_365684_) {
double d0 = this.getMaxSpeed(p_365684_);
Vec3 vec3 = this.getDeltaMovement();
this.setDeltaMovement(Mth.clamp(vec3.x, -d0, d0), vec3.y, Mth.clamp(vec3.z, -d0, d0));
if (this.onGround()) {
this.setDeltaMovement(this.getDeltaMovement().scale(0.5));
}
this.move(MoverType.SELF, this.getDeltaMovement());
if (!this.onGround()) {
this.setDeltaMovement(this.getDeltaMovement().scale(0.95));
}
}
protected double makeStepAlongTrack(BlockPos p_368364_, RailShape p_364631_, double p_369237_) {
return this.behavior.stepAlongTrack(p_368364_, p_364631_, p_369237_);
}
@Override
public void move(MoverType p_361237_, Vec3 p_364999_) {
if (useExperimentalMovement(this.level())) {
Vec3 vec3 = this.position().add(p_364999_);
super.move(p_361237_, p_364999_);
boolean flag = this.behavior.pushAndPickupEntities();
if (flag) {
super.move(p_361237_, vec3.subtract(this.position()));
}
if (p_361237_.equals(MoverType.PISTON)) {
this.onRails = false;
}
} else {
super.move(p_361237_, p_364999_);
this.applyEffectsFromBlocks();
}
}
@Override
public void applyEffectsFromBlocks() {
if (!useExperimentalMovement(this.level())) {
this.applyEffectsFromBlocks(this.position(), this.position());
} else {
super.applyEffectsFromBlocks();
}
}
@Override
public boolean isOnRails() {
return this.onRails;
}
public void setOnRails(boolean p_361351_) {
this.onRails = p_361351_;
}
public boolean isFlipped() {
return this.flipped;
}
public void setFlipped(boolean p_361801_) {
this.flipped = p_361801_;
}
public Vec3 getRedstoneDirection(BlockPos p_369874_) {
BlockState blockstate = this.level().getBlockState(p_369874_);
if (blockstate.is(Blocks.POWERED_RAIL) && blockstate.getValue(PoweredRailBlock.POWERED)) {
RailShape railshape = blockstate.getValue(((BaseRailBlock)blockstate.getBlock()).getShapeProperty());
if (railshape == RailShape.EAST_WEST) {
if (this.isRedstoneConductor(p_369874_.west())) {
return new Vec3(1.0, 0.0, 0.0);
}
if (this.isRedstoneConductor(p_369874_.east())) {
return new Vec3(-1.0, 0.0, 0.0);
}
} else if (railshape == RailShape.NORTH_SOUTH) {
if (this.isRedstoneConductor(p_369874_.north())) {
return new Vec3(0.0, 0.0, 1.0);
}
if (this.isRedstoneConductor(p_369874_.south())) {
return new Vec3(0.0, 0.0, -1.0);
}
}
return Vec3.ZERO;
} else {
return Vec3.ZERO;
}
}
public boolean isRedstoneConductor(BlockPos p_38130_) {
return this.level().getBlockState(p_38130_).isRedstoneConductor(this.level(), p_38130_);
}
protected Vec3 applyNaturalSlowdown(Vec3 p_368399_) {
double d0 = this.behavior.getSlowdownFactor();
Vec3 vec3 = p_368399_.multiply(d0, 0.0, d0);
if (this.isInWater()) {
vec3 = vec3.scale(0.95F);
}
return vec3;
}
@Override
protected void readAdditionalSaveData(CompoundTag p_38137_) {
RegistryOps<Tag> registryops = this.registryAccess().createSerializationContext(NbtOps.INSTANCE);
this.setCustomDisplayBlockState(p_38137_.read("DisplayState", BlockState.CODEC, registryops));
this.setDisplayOffset(p_38137_.getIntOr("DisplayOffset", this.getDefaultDisplayOffset()));
this.flipped = p_38137_.getBooleanOr("FlippedRotation", false);
this.firstTick = p_38137_.getBooleanOr("HasTicked", false);
}
@Override
protected void addAdditionalSaveData(CompoundTag p_38151_) {
this.getCustomDisplayBlockState().ifPresent(p_390770_ -> {
RegistryOps<Tag> registryops = this.registryAccess().createSerializationContext(NbtOps.INSTANCE);
p_38151_.store("DisplayState", BlockState.CODEC, registryops, p_390770_);
});
int i = this.getDisplayOffset();
if (i != this.getDefaultDisplayOffset()) {
p_38151_.putInt("DisplayOffset", i);
}
p_38151_.putBoolean("FlippedRotation", this.flipped);
p_38151_.putBoolean("HasTicked", this.firstTick);
}
@Override
public void push(Entity p_38165_) {
if (!this.level().isClientSide) {
if (!p_38165_.noPhysics && !this.noPhysics) {
if (!this.hasPassenger(p_38165_)) {
double d0 = p_38165_.getX() - this.getX();
double d1 = p_38165_.getZ() - this.getZ();
double d2 = d0 * d0 + d1 * d1;
if (d2 >= 1.0E-4F) {
d2 = Math.sqrt(d2);
d0 /= d2;
d1 /= d2;
double d3 = 1.0 / d2;
if (d3 > 1.0) {
d3 = 1.0;
}
d0 *= d3;
d1 *= d3;
d0 *= 0.1F;
d1 *= 0.1F;
d0 *= 0.5;
d1 *= 0.5;
if (p_38165_ instanceof AbstractMinecart abstractminecart) {
this.pushOtherMinecart(abstractminecart, d0, d1);
} else {
this.push(-d0, 0.0, -d1);
p_38165_.push(d0 / 4.0, 0.0, d1 / 4.0);
}
}
}
}
}
}
private void pushOtherMinecart(AbstractMinecart p_363124_, double p_365746_, double p_363827_) {
double d0;
double d1;
if (useExperimentalMovement(this.level())) {
d0 = this.getDeltaMovement().x;
d1 = this.getDeltaMovement().z;
} else {
d0 = p_363124_.getX() - this.getX();
d1 = p_363124_.getZ() - this.getZ();
}
Vec3 vec3 = new Vec3(d0, 0.0, d1).normalize();
Vec3 vec31 = new Vec3(Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)), 0.0, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)))
.normalize();
double d2 = Math.abs(vec3.dot(vec31));
if (!(d2 < 0.8F) || useExperimentalMovement(this.level())) {
Vec3 vec32 = this.getDeltaMovement();
Vec3 vec33 = p_363124_.getDeltaMovement();
if (p_363124_.isFurnace() && !this.isFurnace()) {
this.setDeltaMovement(vec32.multiply(0.2, 1.0, 0.2));
this.push(vec33.x - p_365746_, 0.0, vec33.z - p_363827_);
p_363124_.setDeltaMovement(vec33.multiply(0.95, 1.0, 0.95));
} else if (!p_363124_.isFurnace() && this.isFurnace()) {
p_363124_.setDeltaMovement(vec33.multiply(0.2, 1.0, 0.2));
p_363124_.push(vec32.x + p_365746_, 0.0, vec32.z + p_363827_);
this.setDeltaMovement(vec32.multiply(0.95, 1.0, 0.95));
} else {
double d3 = (vec33.x + vec32.x) / 2.0;
double d4 = (vec33.z + vec32.z) / 2.0;
this.setDeltaMovement(vec32.multiply(0.2, 1.0, 0.2));
this.push(d3 - p_365746_, 0.0, d4 - p_363827_);
p_363124_.setDeltaMovement(vec33.multiply(0.2, 1.0, 0.2));
p_363124_.push(d3 + p_365746_, 0.0, d4 + p_363827_);
}
}
}
public BlockState getDisplayBlockState() {
return this.getCustomDisplayBlockState().orElseGet(this::getDefaultDisplayBlockState);
}
private Optional<BlockState> getCustomDisplayBlockState() {
return this.getEntityData().get(DATA_ID_CUSTOM_DISPLAY_BLOCK);
}
public BlockState getDefaultDisplayBlockState() {
return Blocks.AIR.defaultBlockState();
}
public int getDisplayOffset() {
return this.getEntityData().get(DATA_ID_DISPLAY_OFFSET);
}
public int getDefaultDisplayOffset() {
return 6;
}
public void setCustomDisplayBlockState(Optional<BlockState> p_393882_) {
this.getEntityData().set(DATA_ID_CUSTOM_DISPLAY_BLOCK, p_393882_);
}
public void setDisplayOffset(int p_38175_) {
this.getEntityData().set(DATA_ID_DISPLAY_OFFSET, p_38175_);
}
public static boolean useExperimentalMovement(Level p_368699_) {
return p_368699_.enabledFeatures().contains(FeatureFlags.MINECART_IMPROVEMENTS);
}
@Override
public abstract ItemStack getPickResult();
public boolean isRideable() {
return false;
}
public boolean isFurnace() {
return false;
}
}