package net.minecraft.world.entity.vehicle;

import net.minecraft.Util;
import com.google.common.collect.Maps;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.network.syncher.EntityDataSerializers;
import java.util.EnumMap;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.Packet;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.entity.MoverType;
import java.util.Iterator;
import java.util.List;
import net.minecraft.world.entity.animal.IronGolem;
import java.util.function.Predicate;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.level.block.PoweredRailBlock;
import net.minecraft.world.level.block.BaseRailBlock;
import net.minecraft.util.Mth;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntityDimensions;
import com.google.common.collect.UnmodifiableIterator;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.block.TrapDoorBlock;
import net.minecraft.tags.Tag;
import net.minecraft.tags.BlockTags;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.LivingEntity;
import javax.annotation.Nullable;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import net.minecraft.core.Vec3i;
import com.mojang.datafixers.util.Pair;
import net.minecraft.world.level.block.state.properties.RailShape;
import java.util.Map;
import com.google.common.collect.ImmutableList;
import net.minecraft.world.entity.Pose;
import com.google.common.collect.ImmutableMap;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.world.entity.Entity;

public abstract class AbstractMinecart extends Entity {
    private static final EntityDataAccessor<Integer> DATA_ID_HURT;
    private static final EntityDataAccessor<Integer> DATA_ID_HURTDIR;
    private static final EntityDataAccessor<Float> DATA_ID_DAMAGE;
    private static final EntityDataAccessor<Integer> DATA_ID_DISPLAY_BLOCK;
    private static final EntityDataAccessor<Integer> DATA_ID_DISPLAY_OFFSET;
    private static final EntityDataAccessor<Boolean> DATA_ID_CUSTOM_DISPLAY;
    private static final ImmutableMap<Pose, ImmutableList<Integer>> POSE_DISMOUNT_HEIGHTS;
    private boolean flipped;
    private static final Map<RailShape, Pair<Vec3i, Vec3i>> EXITS;
    private int lSteps;
    private double lx;
    private double ly;
    private double lz;
    private double lyr;
    private double lxr;
    private double lxd;
    private double lyd;
    private double lzd;
    
    protected AbstractMinecart(final EntityType<?> aoq, final Level bqb) {
        super(aoq, bqb);
        this.blocksBuilding = true;
    }
    
    protected AbstractMinecart(final EntityType<?> aoq, final Level bqb, final double double3, final double double4, final double double5) {
        this(aoq, bqb);
        this.setPos(double3, double4, double5);
        this.setDeltaMovement(Vec3.ZERO);
        this.xo = double3;
        this.yo = double4;
        this.zo = double5;
    }
    
    public static AbstractMinecart createMinecart(final Level bqb, final double double2, final double double3, final double double4, final Type a) {
        if (a == Type.CHEST) {
            return new MinecartChest(bqb, double2, double3, double4);
        }
        if (a == Type.FURNACE) {
            return new MinecartFurnace(bqb, double2, double3, double4);
        }
        if (a == Type.TNT) {
            return new MinecartTNT(bqb, double2, double3, double4);
        }
        if (a == Type.SPAWNER) {
            return new MinecartSpawner(bqb, double2, double3, double4);
        }
        if (a == Type.HOPPER) {
            return new MinecartHopper(bqb, double2, double3, double4);
        }
        if (a == Type.COMMAND_BLOCK) {
            return new MinecartCommandBlock(bqb, double2, double3, double4);
        }
        return new Minecart(bqb, double2, double3, double4);
    }
    
    @Override
    protected boolean isMovementNoisy() {
        return false;
    }
    
    @Override
    protected void defineSynchedData() {
        this.entityData.<Integer>define(AbstractMinecart.DATA_ID_HURT, 0);
        this.entityData.<Integer>define(AbstractMinecart.DATA_ID_HURTDIR, 1);
        this.entityData.<Float>define(AbstractMinecart.DATA_ID_DAMAGE, 0.0f);
        this.entityData.<Integer>define(AbstractMinecart.DATA_ID_DISPLAY_BLOCK, Block.getId(Blocks.AIR.defaultBlockState()));
        this.entityData.<Integer>define(AbstractMinecart.DATA_ID_DISPLAY_OFFSET, 6);
        this.entityData.<Boolean>define(AbstractMinecart.DATA_ID_CUSTOM_DISPLAY, false);
    }
    
    @Nullable
    @Override
    public AABB getCollideAgainstBox(final Entity aom) {
        if (aom.isPushable()) {
            return aom.getBoundingBox();
        }
        return null;
    }
    
    @Override
    public boolean isPushable() {
        return true;
    }
    
    @Override
    public double getPassengersRidingOffset() {
        return 0.0;
    }
    
    @Override
    public Vec3 getDismountLocationForPassenger(final LivingEntity aoy) {
        final Direction fz3 = this.getMotionDirection();
        if (fz3.getAxis() == Direction.Axis.Y) {
            return super.getDismountLocationForPassenger(aoy);
        }
        final int[][] arr4 = DismountHelper.offsetsForDirection(fz3);
        final BlockPos fu5 = this.blockPosition();
        final BlockPos.MutableBlockPos a6 = new BlockPos.MutableBlockPos();
        final ImmutableList<Pose> immutableList7 = aoy.getDismountPoses();
        for (final Pose apj9 : immutableList7) {
            final EntityDimensions aon10 = aoy.getDimensions(apj9);
            final float float11 = Math.min(aon10.width, 1.0f) / 2.0f;
            for (final int integer13 : AbstractMinecart.POSE_DISMOUNT_HEIGHTS.get(apj9)) {
                for (final int[] arr5 : arr4) {
                    a6.set(fu5.getX() + arr5[0], fu5.getY() + integer13, fu5.getZ() + arr5[1]);
                    final double double18 = this.level.getRelativeFloorHeight(a6, cfj -> {
                        if (cfj.is(BlockTags.CLIMBABLE)) {
                            return true;
                        }
                        else {
                            return cfj.getBlock() instanceof TrapDoorBlock && cfj.<Boolean>getValue((Property<Boolean>)TrapDoorBlock.OPEN);
                        }
                    });
                    if (DismountHelper.isFloorValid(double18)) {
                        final AABB deg20 = new AABB(-float11, double18, -float11, float11, double18 + aon10.height, float11);
                        final Vec3 dem21 = Vec3.upFromBottomCenterOf(a6, double18);
                        if (DismountHelper.canDismountTo(this.level, aoy, deg20.move(dem21))) {
                            aoy.setPose(apj9);
                            return dem21;
                        }
                    }
                }
            }
        }
        final double double19 = this.getBoundingBox().maxY;
        a6.set(fu5.getX(), double19, fu5.getZ());
        for (final Pose apj10 : immutableList7) {
            final double double20 = aoy.getDimensions(apj10).height;
            final double double21 = a6.getY() + this.level.getRelativeCeilingHeight(a6, double19 - a6.getY() + double20);
            if (double19 + double20 <= double21) {
                aoy.setPose(apj10);
                break;
            }
        }
        return super.getDismountLocationForPassenger(aoy);
    }
    
    @Override
    public boolean hurt(final DamageSource anw, final float float2) {
        if (this.level.isClientSide || this.removed) {
            return true;
        }
        if (this.isInvulnerableTo(anw)) {
            return false;
        }
        this.setHurtDir(-this.getHurtDir());
        this.setHurtTime(10);
        this.markHurt();
        this.setDamage(this.getDamage() + float2 * 10.0f);
        final boolean boolean4 = anw.getEntity() instanceof Player && ((Player)anw.getEntity()).abilities.instabuild;
        if (boolean4 || this.getDamage() > 40.0f) {
            this.ejectPassengers();
            if (!boolean4 || this.hasCustomName()) {
                this.destroy(anw);
            }
            else {
                this.remove();
            }
        }
        return true;
    }
    
    @Override
    protected float getBlockSpeedFactor() {
        final BlockState cfj2 = this.level.getBlockState(this.blockPosition());
        if (cfj2.is(BlockTags.RAILS)) {
            return 1.0f;
        }
        return super.getBlockSpeedFactor();
    }
    
    public void destroy(final DamageSource anw) {
        this.remove();
        if (this.level.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
            final ItemStack bki3 = new ItemStack(Items.MINECART);
            if (this.hasCustomName()) {
                bki3.setHoverName(this.getCustomName());
            }
            this.spawnAtLocation(bki3);
        }
    }
    
    @Override
    public void animateHurt() {
        this.setHurtDir(-this.getHurtDir());
        this.setHurtTime(10);
        this.setDamage(this.getDamage() + this.getDamage() * 10.0f);
    }
    
    @Override
    public boolean isPickable() {
        return !this.removed;
    }
    
    private static Pair<Vec3i, Vec3i> exits(final RailShape cgm) {
        return AbstractMinecart.EXITS.get(cgm);
    }
    
    @Override
    public Direction getMotionDirection() {
        return this.flipped ? this.getDirection().getOpposite().getClockWise() : this.getDirection().getClockWise();
    }
    
    @Override
    public void tick() {
        if (this.getHurtTime() > 0) {
            this.setHurtTime(this.getHurtTime() - 1);
        }
        if (this.getDamage() > 0.0f) {
            this.setDamage(this.getDamage() - 1.0f);
        }
        if (this.getY() < -64.0) {
            this.outOfWorld();
        }
        this.handleNetherPortal();
        if (this.level.isClientSide) {
            if (this.lSteps > 0) {
                final double double2 = this.getX() + (this.lx - this.getX()) / this.lSteps;
                final double double3 = this.getY() + (this.ly - this.getY()) / this.lSteps;
                final double double4 = this.getZ() + (this.lz - this.getZ()) / this.lSteps;
                final double double5 = Mth.wrapDegrees(this.lyr - this.yRot);
                this.yRot += (float)(double5 / this.lSteps);
                this.xRot += (float)((this.lxr - this.xRot) / this.lSteps);
                --this.lSteps;
                this.setPos(double2, double3, double4);
                this.setRot(this.yRot, this.xRot);
            }
            else {
                this.reapplyPosition();
                this.setRot(this.yRot, this.xRot);
            }
            return;
        }
        if (!this.isNoGravity()) {
            this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.04, 0.0));
        }
        final int integer2 = Mth.floor(this.getX());
        int integer3 = Mth.floor(this.getY());
        final int integer4 = Mth.floor(this.getZ());
        if (this.level.getBlockState(new BlockPos(integer2, integer3 - 1, integer4)).is(BlockTags.RAILS)) {
            --integer3;
        }
        final BlockPos fu5 = new BlockPos(integer2, integer3, integer4);
        final BlockState cfj6 = this.level.getBlockState(fu5);
        if (BaseRailBlock.isRail(cfj6)) {
            this.moveAlongTrack(fu5, cfj6);
            if (cfj6.is(Blocks.ACTIVATOR_RAIL)) {
                this.activateMinecart(integer2, integer3, integer4, cfj6.<Boolean>getValue((Property<Boolean>)PoweredRailBlock.POWERED));
            }
        }
        else {
            this.comeOffTrack();
        }
        this.checkInsideBlocks();
        this.xRot = 0.0f;
        final double double6 = this.xo - this.getX();
        final double double7 = this.zo - this.getZ();
        if (double6 * double6 + double7 * double7 > 0.001) {
            this.yRot = (float)(Mth.atan2(double7, double6) * 180.0 / 3.141592653589793);
            if (this.flipped) {
                this.yRot += 180.0f;
            }
        }
        final double double8 = Mth.wrapDegrees(this.yRot - this.yRotO);
        if (double8 < -170.0 || double8 >= 170.0) {
            this.yRot += 180.0f;
            this.flipped = !this.flipped;
        }
        this.setRot(this.yRot, this.xRot);
        if (this.getMinecartType() == Type.RIDEABLE && Entity.getHorizontalDistanceSqr(this.getDeltaMovement()) > 0.01) {
            final List<Entity> list13 = this.level.getEntities(this, this.getBoundingBox().inflate(0.20000000298023224, 0.0, 0.20000000298023224), EntitySelector.pushableBy(this));
            if (!list13.isEmpty()) {
                for (int integer5 = 0; integer5 < list13.size(); ++integer5) {
                    final Entity aom15 = list13.get(integer5);
                    if (aom15 instanceof Player || aom15 instanceof IronGolem || aom15 instanceof AbstractMinecart || this.isVehicle() || aom15.isPassenger()) {
                        aom15.push(this);
                    }
                    else {
                        aom15.startRiding(this);
                    }
                }
            }
        }
        else {
            for (final Entity aom16 : this.level.getEntities(this, this.getBoundingBox().inflate(0.20000000298023224, 0.0, 0.20000000298023224))) {
                if (!this.hasPassenger(aom16) && aom16.isPushable() && aom16 instanceof AbstractMinecart) {
                    aom16.push(this);
                }
            }
        }
        this.updateInWaterStateAndDoFluidPushing();
    }
    
    protected double getMaxSpeed() {
        return 0.4;
    }
    
    public void activateMinecart(final int integer1, final int integer2, final int integer3, final boolean boolean4) {
    }
    
    protected void comeOffTrack() {
        final double double2 = this.getMaxSpeed();
        final Vec3 dem4 = this.getDeltaMovement();
        this.setDeltaMovement(Mth.clamp(dem4.x, -double2, double2), dem4.y, Mth.clamp(dem4.z, -double2, double2));
        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 void moveAlongTrack(final BlockPos fu, final BlockState cfj) {
        this.fallDistance = 0.0f;
        double double4 = this.getX();
        double double5 = this.getY();
        double double6 = this.getZ();
        final Vec3 dem10 = this.getPos(double4, double5, double6);
        double5 = fu.getY();
        boolean boolean11 = false;
        boolean boolean12 = false;
        final BaseRailBlock bvj13 = (BaseRailBlock)cfj.getBlock();
        if (bvj13 == Blocks.POWERED_RAIL) {
            boolean11 = cfj.<Boolean>getValue((Property<Boolean>)PoweredRailBlock.POWERED);
            boolean12 = !boolean11;
        }
        final double double7 = 0.0078125;
        Vec3 dem11 = this.getDeltaMovement();
        final RailShape cgm17 = cfj.<RailShape>getValue(bvj13.getShapeProperty());
        switch (cgm17) {
            case ASCENDING_EAST: {
                this.setDeltaMovement(dem11.add(-0.0078125, 0.0, 0.0));
                ++double5;
                break;
            }
            case ASCENDING_WEST: {
                this.setDeltaMovement(dem11.add(0.0078125, 0.0, 0.0));
                ++double5;
                break;
            }
            case ASCENDING_NORTH: {
                this.setDeltaMovement(dem11.add(0.0, 0.0, 0.0078125));
                ++double5;
                break;
            }
            case ASCENDING_SOUTH: {
                this.setDeltaMovement(dem11.add(0.0, 0.0, -0.0078125));
                ++double5;
                break;
            }
        }
        dem11 = this.getDeltaMovement();
        final Pair<Vec3i, Vec3i> pair18 = exits(cgm17);
        final Vec3i gr19 = (Vec3i)pair18.getFirst();
        final Vec3i gr20 = (Vec3i)pair18.getSecond();
        double double8 = gr20.getX() - gr19.getX();
        double double9 = gr20.getZ() - gr19.getZ();
        final double double10 = Math.sqrt(double8 * double8 + double9 * double9);
        final double double11 = dem11.x * double8 + dem11.z * double9;
        if (double11 < 0.0) {
            double8 = -double8;
            double9 = -double9;
        }
        final double double12 = Math.min(2.0, Math.sqrt(Entity.getHorizontalDistanceSqr(dem11)));
        dem11 = new Vec3(double12 * double8 / double10, dem11.y, double12 * double9 / double10);
        this.setDeltaMovement(dem11);
        final Entity aom31 = this.getPassengers().isEmpty() ? null : this.getPassengers().get(0);
        if (aom31 instanceof Player) {
            final Vec3 dem12 = aom31.getDeltaMovement();
            final double double13 = Entity.getHorizontalDistanceSqr(dem12);
            final double double14 = Entity.getHorizontalDistanceSqr(this.getDeltaMovement());
            if (double13 > 1.0E-4 && double14 < 0.01) {
                this.setDeltaMovement(this.getDeltaMovement().add(dem12.x * 0.1, 0.0, dem12.z * 0.1));
                boolean12 = false;
            }
        }
        if (boolean12) {
            final double double15 = Math.sqrt(Entity.getHorizontalDistanceSqr(this.getDeltaMovement()));
            if (double15 < 0.03) {
                this.setDeltaMovement(Vec3.ZERO);
            }
            else {
                this.setDeltaMovement(this.getDeltaMovement().multiply(0.5, 0.0, 0.5));
            }
        }
        final double double15 = fu.getX() + 0.5 + gr19.getX() * 0.5;
        final double double16 = fu.getZ() + 0.5 + gr19.getZ() * 0.5;
        final double double17 = fu.getX() + 0.5 + gr20.getX() * 0.5;
        final double double18 = fu.getZ() + 0.5 + gr20.getZ() * 0.5;
        double8 = double17 - double15;
        double9 = double18 - double16;
        double double19;
        if (double8 == 0.0) {
            double19 = double6 - fu.getZ();
        }
        else if (double9 == 0.0) {
            double19 = double4 - fu.getX();
        }
        else {
            final double double20 = double4 - double15;
            final double double21 = double6 - double16;
            double19 = (double20 * double8 + double21 * double9) * 2.0;
        }
        double4 = double15 + double8 * double19;
        double6 = double16 + double9 * double19;
        this.setPos(double4, double5, double6);
        final double double20 = this.isVehicle() ? 0.75 : 1.0;
        final double double21 = this.getMaxSpeed();
        dem11 = this.getDeltaMovement();
        this.move(MoverType.SELF, new Vec3(Mth.clamp(double20 * dem11.x, -double21, double21), 0.0, Mth.clamp(double20 * dem11.z, -double21, double21)));
        if (gr19.getY() != 0 && Mth.floor(this.getX()) - fu.getX() == gr19.getX() && Mth.floor(this.getZ()) - fu.getZ() == gr19.getZ()) {
            this.setPos(this.getX(), this.getY() + gr19.getY(), this.getZ());
        }
        else if (gr20.getY() != 0 && Mth.floor(this.getX()) - fu.getX() == gr20.getX() && Mth.floor(this.getZ()) - fu.getZ() == gr20.getZ()) {
            this.setPos(this.getX(), this.getY() + gr20.getY(), this.getZ());
        }
        this.applyNaturalSlowdown();
        final Vec3 dem13 = this.getPos(this.getX(), this.getY(), this.getZ());
        if (dem13 != null && dem10 != null) {
            final double double22 = (dem10.y - dem13.y) * 0.05;
            final Vec3 dem14 = this.getDeltaMovement();
            final double double23 = Math.sqrt(Entity.getHorizontalDistanceSqr(dem14));
            if (double23 > 0.0) {
                this.setDeltaMovement(dem14.multiply((double23 + double22) / double23, 1.0, (double23 + double22) / double23));
            }
            this.setPos(this.getX(), dem13.y, this.getZ());
        }
        final int integer47 = Mth.floor(this.getX());
        final int integer48 = Mth.floor(this.getZ());
        if (integer47 != fu.getX() || integer48 != fu.getZ()) {
            final Vec3 dem14 = this.getDeltaMovement();
            final double double23 = Math.sqrt(Entity.getHorizontalDistanceSqr(dem14));
            this.setDeltaMovement(double23 * (integer47 - fu.getX()), dem14.y, double23 * (integer48 - fu.getZ()));
        }
        if (boolean11) {
            final Vec3 dem14 = this.getDeltaMovement();
            final double double23 = Math.sqrt(Entity.getHorizontalDistanceSqr(dem14));
            if (double23 > 0.01) {
                final double double24 = 0.06;
                this.setDeltaMovement(dem14.add(dem14.x / double23 * 0.06, 0.0, dem14.z / double23 * 0.06));
            }
            else {
                final Vec3 dem15 = this.getDeltaMovement();
                double double25 = dem15.x;
                double double26 = dem15.z;
                if (cgm17 == RailShape.EAST_WEST) {
                    if (this.isRedstoneConductor(fu.west())) {
                        double25 = 0.02;
                    }
                    else if (this.isRedstoneConductor(fu.east())) {
                        double25 = -0.02;
                    }
                }
                else {
                    if (cgm17 != RailShape.NORTH_SOUTH) {
                        return;
                    }
                    if (this.isRedstoneConductor(fu.north())) {
                        double26 = 0.02;
                    }
                    else if (this.isRedstoneConductor(fu.south())) {
                        double26 = -0.02;
                    }
                }
                this.setDeltaMovement(double25, dem15.y, double26);
            }
        }
    }
    
    private boolean isRedstoneConductor(final BlockPos fu) {
        return this.level.getBlockState(fu).isRedstoneConductor(this.level, fu);
    }
    
    protected void applyNaturalSlowdown() {
        final double double2 = this.isVehicle() ? 0.997 : 0.96;
        this.setDeltaMovement(this.getDeltaMovement().multiply(double2, 0.0, double2));
    }
    
    @Nullable
    public Vec3 getPosOffs(double double1, double double2, double double3, final double double4) {
        final int integer10 = Mth.floor(double1);
        int integer11 = Mth.floor(double2);
        final int integer12 = Mth.floor(double3);
        if (this.level.getBlockState(new BlockPos(integer10, integer11 - 1, integer12)).is(BlockTags.RAILS)) {
            --integer11;
        }
        final BlockState cfj13 = this.level.getBlockState(new BlockPos(integer10, integer11, integer12));
        if (BaseRailBlock.isRail(cfj13)) {
            final RailShape cgm14 = cfj13.<RailShape>getValue(((BaseRailBlock)cfj13.getBlock()).getShapeProperty());
            double2 = integer11;
            if (cgm14.isAscending()) {
                double2 = integer11 + 1;
            }
            final Pair<Vec3i, Vec3i> pair15 = exits(cgm14);
            final Vec3i gr16 = (Vec3i)pair15.getFirst();
            final Vec3i gr17 = (Vec3i)pair15.getSecond();
            double double5 = gr17.getX() - gr16.getX();
            double double6 = gr17.getZ() - gr16.getZ();
            final double double7 = Math.sqrt(double5 * double5 + double6 * double6);
            double5 /= double7;
            double6 /= double7;
            double1 += double5 * double4;
            double3 += double6 * double4;
            if (gr16.getY() != 0 && Mth.floor(double1) - integer10 == gr16.getX() && Mth.floor(double3) - integer12 == gr16.getZ()) {
                double2 += gr16.getY();
            }
            else if (gr17.getY() != 0 && Mth.floor(double1) - integer10 == gr17.getX() && Mth.floor(double3) - integer12 == gr17.getZ()) {
                double2 += gr17.getY();
            }
            return this.getPos(double1, double2, double3);
        }
        return null;
    }
    
    @Nullable
    public Vec3 getPos(double double1, double double2, double double3) {
        final int integer8 = Mth.floor(double1);
        int integer9 = Mth.floor(double2);
        final int integer10 = Mth.floor(double3);
        if (this.level.getBlockState(new BlockPos(integer8, integer9 - 1, integer10)).is(BlockTags.RAILS)) {
            --integer9;
        }
        final BlockState cfj11 = this.level.getBlockState(new BlockPos(integer8, integer9, integer10));
        if (BaseRailBlock.isRail(cfj11)) {
            final RailShape cgm12 = cfj11.<RailShape>getValue(((BaseRailBlock)cfj11.getBlock()).getShapeProperty());
            final Pair<Vec3i, Vec3i> pair13 = exits(cgm12);
            final Vec3i gr14 = (Vec3i)pair13.getFirst();
            final Vec3i gr15 = (Vec3i)pair13.getSecond();
            final double double4 = integer8 + 0.5 + gr14.getX() * 0.5;
            final double double5 = integer9 + 0.0625 + gr14.getY() * 0.5;
            final double double6 = integer10 + 0.5 + gr14.getZ() * 0.5;
            final double double7 = integer8 + 0.5 + gr15.getX() * 0.5;
            final double double8 = integer9 + 0.0625 + gr15.getY() * 0.5;
            final double double9 = integer10 + 0.5 + gr15.getZ() * 0.5;
            final double double10 = double7 - double4;
            final double double11 = (double8 - double5) * 2.0;
            final double double12 = double9 - double6;
            double double13;
            if (double10 == 0.0) {
                double13 = double3 - integer10;
            }
            else if (double12 == 0.0) {
                double13 = double1 - integer8;
            }
            else {
                final double double14 = double1 - double4;
                final double double15 = double3 - double6;
                double13 = (double14 * double10 + double15 * double12) * 2.0;
            }
            double1 = double4 + double10 * double13;
            double2 = double5 + double11 * double13;
            double3 = double6 + double12 * double13;
            if (double11 < 0.0) {
                ++double2;
            }
            else if (double11 > 0.0) {
                double2 += 0.5;
            }
            return new Vec3(double1, double2, double3);
        }
        return null;
    }
    
    @Override
    public AABB getBoundingBoxForCulling() {
        final AABB deg2 = this.getBoundingBox();
        if (this.hasCustomDisplay()) {
            return deg2.inflate(Math.abs(this.getDisplayOffset()) / 16.0);
        }
        return deg2;
    }
    
    @Override
    protected void readAdditionalSaveData(final CompoundTag le) {
        if (le.getBoolean("CustomDisplayTile")) {
            this.setDisplayBlockState(NbtUtils.readBlockState(le.getCompound("DisplayState")));
            this.setDisplayOffset(le.getInt("DisplayOffset"));
        }
    }
    
    @Override
    protected void addAdditionalSaveData(final CompoundTag le) {
        if (this.hasCustomDisplay()) {
            le.putBoolean("CustomDisplayTile", true);
            le.put("DisplayState", NbtUtils.writeBlockState(this.getDisplayBlockState()));
            le.putInt("DisplayOffset", this.getDisplayOffset());
        }
    }
    
    @Override
    public void push(final Entity aom) {
        if (this.level.isClientSide) {
            return;
        }
        if (aom.noPhysics || this.noPhysics) {
            return;
        }
        if (this.hasPassenger(aom)) {
            return;
        }
        double double3 = aom.getX() - this.getX();
        double double4 = aom.getZ() - this.getZ();
        double double5 = double3 * double3 + double4 * double4;
        if (double5 >= 9.999999747378752E-5) {
            double5 = Mth.sqrt(double5);
            double3 /= double5;
            double4 /= double5;
            double double6 = 1.0 / double5;
            if (double6 > 1.0) {
                double6 = 1.0;
            }
            double3 *= double6;
            double4 *= double6;
            double3 *= 0.10000000149011612;
            double4 *= 0.10000000149011612;
            double3 *= 1.0f - this.pushthrough;
            double4 *= 1.0f - this.pushthrough;
            double3 *= 0.5;
            double4 *= 0.5;
            if (aom instanceof AbstractMinecart) {
                final double double7 = aom.getX() - this.getX();
                final double double8 = aom.getZ() - this.getZ();
                final Vec3 dem15 = new Vec3(double7, 0.0, double8).normalize();
                final Vec3 dem16 = new Vec3(Mth.cos(this.yRot * 0.017453292f), 0.0, Mth.sin(this.yRot * 0.017453292f)).normalize();
                final double double9 = Math.abs(dem15.dot(dem16));
                if (double9 < 0.800000011920929) {
                    return;
                }
                final Vec3 dem17 = this.getDeltaMovement();
                final Vec3 dem18 = aom.getDeltaMovement();
                if (((AbstractMinecart)aom).getMinecartType() == Type.FURNACE && this.getMinecartType() != Type.FURNACE) {
                    this.setDeltaMovement(dem17.multiply(0.2, 1.0, 0.2));
                    this.push(dem18.x - double3, 0.0, dem18.z - double4);
                    aom.setDeltaMovement(dem18.multiply(0.95, 1.0, 0.95));
                }
                else if (((AbstractMinecart)aom).getMinecartType() != Type.FURNACE && this.getMinecartType() == Type.FURNACE) {
                    aom.setDeltaMovement(dem18.multiply(0.2, 1.0, 0.2));
                    aom.push(dem17.x + double3, 0.0, dem17.z + double4);
                    this.setDeltaMovement(dem17.multiply(0.95, 1.0, 0.95));
                }
                else {
                    final double double10 = (dem18.x + dem17.x) / 2.0;
                    final double double11 = (dem18.z + dem17.z) / 2.0;
                    this.setDeltaMovement(dem17.multiply(0.2, 1.0, 0.2));
                    this.push(double10 - double3, 0.0, double11 - double4);
                    aom.setDeltaMovement(dem18.multiply(0.2, 1.0, 0.2));
                    aom.push(double10 + double3, 0.0, double11 + double4);
                }
            }
            else {
                this.push(-double3, 0.0, -double4);
                aom.push(double3 / 4.0, 0.0, double4 / 4.0);
            }
        }
    }
    
    @Override
    public void lerpTo(final double double1, final double double2, final double double3, final float float4, final float float5, final int integer, final boolean boolean7) {
        this.lx = double1;
        this.ly = double2;
        this.lz = double3;
        this.lyr = float4;
        this.lxr = float5;
        this.lSteps = integer + 2;
        this.setDeltaMovement(this.lxd, this.lyd, this.lzd);
    }
    
    @Override
    public void lerpMotion(final double double1, final double double2, final double double3) {
        this.lxd = double1;
        this.lyd = double2;
        this.lzd = double3;
        this.setDeltaMovement(this.lxd, this.lyd, this.lzd);
    }
    
    public void setDamage(final float float1) {
        this.entityData.<Float>set(AbstractMinecart.DATA_ID_DAMAGE, float1);
    }
    
    public float getDamage() {
        return this.entityData.<Float>get(AbstractMinecart.DATA_ID_DAMAGE);
    }
    
    public void setHurtTime(final int integer) {
        this.entityData.<Integer>set(AbstractMinecart.DATA_ID_HURT, integer);
    }
    
    public int getHurtTime() {
        return this.entityData.<Integer>get(AbstractMinecart.DATA_ID_HURT);
    }
    
    public void setHurtDir(final int integer) {
        this.entityData.<Integer>set(AbstractMinecart.DATA_ID_HURTDIR, integer);
    }
    
    public int getHurtDir() {
        return this.entityData.<Integer>get(AbstractMinecart.DATA_ID_HURTDIR);
    }
    
    public abstract Type getMinecartType();
    
    public BlockState getDisplayBlockState() {
        if (!this.hasCustomDisplay()) {
            return this.getDefaultDisplayBlockState();
        }
        return Block.stateById(this.getEntityData().<Integer>get(AbstractMinecart.DATA_ID_DISPLAY_BLOCK));
    }
    
    public BlockState getDefaultDisplayBlockState() {
        return Blocks.AIR.defaultBlockState();
    }
    
    public int getDisplayOffset() {
        if (!this.hasCustomDisplay()) {
            return this.getDefaultDisplayOffset();
        }
        return this.getEntityData().<Integer>get(AbstractMinecart.DATA_ID_DISPLAY_OFFSET);
    }
    
    public int getDefaultDisplayOffset() {
        return 6;
    }
    
    public void setDisplayBlockState(final BlockState cfj) {
        this.getEntityData().<Integer>set(AbstractMinecart.DATA_ID_DISPLAY_BLOCK, Block.getId(cfj));
        this.setCustomDisplay(true);
    }
    
    public void setDisplayOffset(final int integer) {
        this.getEntityData().<Integer>set(AbstractMinecart.DATA_ID_DISPLAY_OFFSET, integer);
        this.setCustomDisplay(true);
    }
    
    public boolean hasCustomDisplay() {
        return this.getEntityData().<Boolean>get(AbstractMinecart.DATA_ID_CUSTOM_DISPLAY);
    }
    
    public void setCustomDisplay(final boolean boolean1) {
        this.getEntityData().<Boolean>set(AbstractMinecart.DATA_ID_CUSTOM_DISPLAY, boolean1);
    }
    
    @Override
    public Packet<?> getAddEntityPacket() {
        return new ClientboundAddEntityPacket(this);
    }
    
    static {
        DATA_ID_HURT = SynchedEntityData.<Integer>defineId(AbstractMinecart.class, EntityDataSerializers.INT);
        DATA_ID_HURTDIR = SynchedEntityData.<Integer>defineId(AbstractMinecart.class, EntityDataSerializers.INT);
        DATA_ID_DAMAGE = SynchedEntityData.<Float>defineId(AbstractMinecart.class, EntityDataSerializers.FLOAT);
        DATA_ID_DISPLAY_BLOCK = SynchedEntityData.<Integer>defineId(AbstractMinecart.class, EntityDataSerializers.INT);
        DATA_ID_DISPLAY_OFFSET = SynchedEntityData.<Integer>defineId(AbstractMinecart.class, EntityDataSerializers.INT);
        DATA_ID_CUSTOM_DISPLAY = SynchedEntityData.<Boolean>defineId(AbstractMinecart.class, EntityDataSerializers.BOOLEAN);
        POSE_DISMOUNT_HEIGHTS = ImmutableMap.<Pose, ImmutableList<Integer>>of(Pose.STANDING, ImmutableList.<Integer>of(0, 1, -1), Pose.CROUCHING, ImmutableList.<Integer>of(0, 1, -1), Pose.SWIMMING, ImmutableList.<Integer>of(0, 1));
        final Vec3i gr2;
        final Vec3i gr3;
        final Vec3i gr4;
        final Vec3i gr5;
        final Vec3i gr6;
        final Vec3i gr7;
        final Vec3i gr8;
        final Vec3i gr9;
        EXITS = Util.<Map<RailShape, Pair<Vec3i, Vec3i>>>make(Maps.newEnumMap(RailShape.class), enumMap -> {
            gr2 = Direction.WEST.getNormal();
            gr3 = Direction.EAST.getNormal();
            gr4 = Direction.NORTH.getNormal();
            gr5 = Direction.SOUTH.getNormal();
            gr6 = gr2.below();
            gr7 = gr3.below();
            gr8 = gr4.below();
            gr9 = gr5.below();
            enumMap.put(RailShape.NORTH_SOUTH, Pair.of(gr4, gr5));
            enumMap.put(RailShape.EAST_WEST, Pair.of(gr2, gr3));
            enumMap.put(RailShape.ASCENDING_EAST, Pair.of(gr6, gr3));
            enumMap.put(RailShape.ASCENDING_WEST, Pair.of(gr2, gr7));
            enumMap.put(RailShape.ASCENDING_NORTH, Pair.of(gr4, gr9));
            enumMap.put(RailShape.ASCENDING_SOUTH, Pair.of(gr8, gr5));
            enumMap.put(RailShape.SOUTH_EAST, Pair.of(gr5, gr3));
            enumMap.put(RailShape.SOUTH_WEST, Pair.of(gr5, gr2));
            enumMap.put(RailShape.NORTH_WEST, Pair.of(gr4, gr2));
            enumMap.put(RailShape.NORTH_EAST, Pair.of(gr4, gr3));
        });
    }
    
    public enum Type {
        RIDEABLE, 
        CHEST, 
        FURNACE, 
        TNT, 
        SPAWNER, 
        HOPPER, 
        COMMAND_BLOCK;
    }
}
