All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.entity.item.EntityMinecartAbstract Maven / Gradle / Ivy

There is a newer version: 1.20.40-r1
Show newest version
package cn.nukkit.entity.item;

import cn.nukkit.Player;
import cn.nukkit.api.API;
import cn.nukkit.api.API.Definition;
import cn.nukkit.api.API.Usage;
import cn.nukkit.api.PowerNukkitDifference;
import cn.nukkit.block.*;
import cn.nukkit.entity.Entity;
import cn.nukkit.entity.EntityHuman;
import cn.nukkit.entity.EntityLiving;
import cn.nukkit.entity.data.ByteEntityData;
import cn.nukkit.entity.data.IntEntityData;
import cn.nukkit.event.entity.EntityDamageByEntityEvent;
import cn.nukkit.event.entity.EntityDamageEvent;
import cn.nukkit.event.vehicle.VehicleMoveEvent;
import cn.nukkit.event.vehicle.VehicleUpdateEvent;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemMinecart;
import cn.nukkit.level.GameRule;
import cn.nukkit.level.Location;
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.math.MathHelper;
import cn.nukkit.math.NukkitMath;
import cn.nukkit.math.Vector3;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.utils.MinecartType;
import cn.nukkit.utils.Rail;
import cn.nukkit.utils.Rail.Orientation;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Objects;

/**
 * @author larryTheCoder (Nukkit Project, Minecart and Riding Project)
 * @since 2017/6/26
 */
public abstract class EntityMinecartAbstract extends EntityVehicle {

    private static final int[][][] matrix = new int[][][]{
            {{0, 0, -1}, {0, 0, 1}},
            {{-1, 0, 0}, {1, 0, 0}},
            {{-1, -1, 0}, {1, 0, 0}},
            {{-1, 0, 0}, {1, -1, 0}},
            {{0, 0, -1}, {0, -1, 1}},
            {{0, -1, -1}, {0, 0, 1}},
            {{0, 0, 1}, {1, 0, 0}},
            {{0, 0, 1}, {-1, 0, 0}},
            {{0, 0, -1}, {-1, 0, 0}},
            {{0, 0, -1}, {1, 0, 0}}
    };
    private final boolean devs = false; // Avoid maintained features into production
    private double currentSpeed = 0;
    private Block blockInside;
    // Plugins modifiers
    private boolean slowWhenEmpty = true;
    private double derailedX = 0.5;
    private double derailedY = 0.5;
    private double derailedZ = 0.5;
    private double flyingX = 0.95;
    private double flyingY = 0.95;
    private double flyingZ = 0.95;
    private double maxSpeed = 0.4D;
    private boolean hasUpdated = false;

    public EntityMinecartAbstract(FullChunk chunk, CompoundTag nbt) {
        super(chunk, nbt);

        setMaxHealth(40);
        setHealth(40);
    }

    public abstract MinecartType getType();

    public abstract boolean isRideable();

    @Override
    public float getHeight() {
        return 0.7F;
    }

    @Override
    public float getWidth() {
        return 0.98F;
    }

    @Override
    protected float getDrag() {
        return 0.1F;
    }

    @Override
    public float getBaseOffset() {
        return 0.35F;
    }

    @Override
    public boolean canDoInteraction() {
        return passengers.isEmpty() && this.getDisplayBlock() == null;
    }

    @Override
    public void initEntity() {
        super.initEntity();

        prepareDataProperty();
    }

    @PowerNukkitDifference(since = "1.3.1.2-PN", info = "Will despawn instantly after being 'killed'")
    @Override
    public boolean onUpdate(int currentTick) {
        if (this.closed) {
            return false;
        }

        if (!this.isAlive()) {
            this.despawnFromAll();
            this.close();
            return false;
        }

        int tickDiff = currentTick - this.lastUpdate;

        if (tickDiff <= 0) {
            return false;
        }

        this.lastUpdate = currentTick;

        if (isAlive()) {
            super.onUpdate(currentTick);

            // The damage token
            if (getHealth() < 20) {
                setHealth(getHealth() + 1);
            }

            // Entity variables
            lastX = x;
            lastY = y;
            lastZ = z;
            motionY -= 0.03999999910593033D;
            int dx = MathHelper.floor(x);
            int dy = MathHelper.floor(y);
            int dz = MathHelper.floor(z);

            // Some hack to check rails
            if (Rail.isRailBlock(level.getBlockIdAt(dx, dy - 1, dz))) {
                --dy;
            }

            Block block = level.getBlock(new Vector3(dx, dy, dz));

            // Ensure that the block is a rail
            if (Rail.isRailBlock(block)) {
                processMovement(dx, dy, dz, (BlockRail) block);
                // Activate the minecart/TNT
                if (block instanceof BlockRailActivator && ((BlockRailActivator) block).isActive()) {
                    activate(dx, dy, dz, (block.getDamage() & 0x8) != 0);
                    if (this.isRideable() && this.getRiding() != null) {
                        this.dismountEntity(this.getRiding());
                    }
                }
                if (block instanceof BlockRailDetector detector) {
                    detector.setActive();
                }
            } else {
                setFalling();
            }
            checkBlockCollision();

            // Minecart head
            pitch = 0;
            double diffX = this.lastX - this.x;
            double diffZ = this.lastZ - this.z;
            double yawToChange = yaw;
            if (diffX * diffX + diffZ * diffZ > 0.001D) {
                yawToChange = (Math.atan2(diffZ, diffX) * 180 / Math.PI);
            }

            // Reverse yaw if yaw is below 0
            if (yawToChange < 0) {
                // -90-(-90)-(-90) = 90
                yawToChange -= yawToChange - yawToChange;
            }

            setRotation(yawToChange, pitch);

            Location from = new Location(lastX, lastY, lastZ, lastYaw, lastPitch, level);
            Location to = new Location(this.x, this.y, this.z, this.yaw, this.pitch, level);

            this.getServer().getPluginManager().callEvent(new VehicleUpdateEvent(this));

            if (!from.equals(to)) {
                this.getServer().getPluginManager().callEvent(new VehicleMoveEvent(this, from, to));
            }

            // Collisions
            for (cn.nukkit.entity.Entity entity : level.getNearbyEntities(boundingBox.grow(0.2D, 0, 0.2D), this)) {
                if (!passengers.contains(entity) && entity instanceof EntityMinecartAbstract) {
                    entity.applyEntityCollision(this);
                }
            }

            Iterator linkedIterator = this.passengers.iterator();

            while (linkedIterator.hasNext()) {
                cn.nukkit.entity.Entity linked = linkedIterator.next();

                if (!linked.isAlive()) {
                    if (linked.riding == this) {
                        linked.riding = null;
                    }

                    linkedIterator.remove();
                }
            }

            // No need to onGround or Motion diff! This always have an update
            return true;
        }

        return false;
    }

    @Override
    public boolean attack(EntityDamageEvent source) {
        if (invulnerable) {
            return false;
        } else {
            source.setDamage(source.getDamage() * 15);

            boolean attack = super.attack(source);

            if (isAlive()) {
                performHurtAnimation();
            }

            return attack;
        }
    }

    public void dropItem() {
        if (this.lastDamageCause instanceof EntityDamageByEntityEvent entityDamageByEntityEvent) {
            Entity damager = entityDamageByEntityEvent.getDamager();
            if (damager instanceof Player player && player.isCreative()) {
                return;
            }
        }
        level.dropItem(this, new ItemMinecart());
    }

    @PowerNukkitDifference(info = "Fixes a dupe issue when attacking too quickly", since = "1.3.1.2-PN")
    @Override
    public void kill() {
        if (!isAlive()) {
            return;
        }
        super.kill();

        if (level.getGameRules().getBoolean(GameRule.DO_ENTITY_DROPS)) {
            dropItem();
        }
    }

    @PowerNukkitDifference(info = "Will not make a smoke particle and will do a proper dismount on the entities", since = "1.3.1.2-PN")
    @Override
    public void close() {
        super.close();

        for (Entity passenger : new ArrayList<>(this.passengers)) {
            dismountEntity(passenger);
        }
    }

    @Override
    public boolean onInteract(Player p, Item item, Vector3 clickedPos) {
        if (!passengers.isEmpty() && isRideable()) {
            return false;
        }

        if (blockInside == null) {
            mountEntity(p);
        }

        return super.onInteract(p, item, clickedPos);
    }

    @Override
    public void applyEntityCollision(cn.nukkit.entity.Entity entity) {
        if (entity != riding && !(entity instanceof Player && ((Player) entity).isSpectator())) {
            if (entity instanceof EntityLiving
                    && !(entity instanceof EntityHuman)
                    && motionX * motionX + motionZ * motionZ > 0.01D
                    && passengers.isEmpty()
                    && entity.riding == null
                    && blockInside == null) {
                if (riding == null && devs) {
                    mountEntity(entity);// TODO: rewrite (weird riding)
                }
            }

            double motiveX = entity.x - x;
            double motiveZ = entity.z - z;
            double square = motiveX * motiveX + motiveZ * motiveZ;

            if (square >= 9.999999747378752E-5D) {
                square = Math.sqrt(square);
                motiveX /= square;
                motiveZ /= square;
                double next = 1 / square;

                if (next > 1) {
                    next = 1;
                }

                motiveX *= next;
                motiveZ *= next;
                motiveX *= 0.10000000149011612D;
                motiveZ *= 0.10000000149011612D;
                motiveX *= 1 + entityCollisionReduction;
                motiveZ *= 1 + entityCollisionReduction;
                motiveX *= 0.5D;
                motiveZ *= 0.5D;
                if (entity instanceof EntityMinecartAbstract) {
                    EntityMinecartAbstract mine = (EntityMinecartAbstract) entity;
                    double desinityX = mine.x - x;
                    double desinityZ = mine.z - z;
                    Vector3 vector = new Vector3(desinityX, 0, desinityZ).normalize();
                    Vector3 vec = new Vector3(MathHelper.cos((float) yaw * 0.017453292F), 0, MathHelper.sin((float) yaw * 0.017453292F)).normalize();
                    double desinityXZ = Math.abs(vector.dot(vec));

                    if (desinityXZ < 0.800000011920929D) {
                        return;
                    }

                    double motX = mine.motionX + motionX;
                    double motZ = mine.motionZ + motionZ;

                    if (mine.getType().getId() == 2 && getType().getId() != 2) {
                        motionX *= 0.20000000298023224D;
                        motionZ *= 0.20000000298023224D;
                        motionX += mine.motionX - motiveX;
                        motionZ += mine.motionZ - motiveZ;
                        mine.motionX *= 0.949999988079071D;
                        mine.motionZ *= 0.949999988079071D;
                    } else if (mine.getType().getId() != 2 && getType().getId() == 2) {
                        mine.motionX *= 0.20000000298023224D;
                        mine.motionZ *= 0.20000000298023224D;
                        motionX += mine.motionX + motiveX;
                        motionZ += mine.motionZ + motiveZ;
                        motionX *= 0.949999988079071D;
                        motionZ *= 0.949999988079071D;
                    } else {
                        motX /= 2;
                        motZ /= 2;
                        motionX *= 0.20000000298023224D;
                        motionZ *= 0.20000000298023224D;
                        motionX += motX - motiveX;
                        motionZ += motZ - motiveZ;
                        mine.motionX *= 0.20000000298023224D;
                        mine.motionZ *= 0.20000000298023224D;
                        mine.motionX += motX + motiveX;
                        mine.motionZ += motZ + motiveZ;
                    }
                } else {
                    motionX -= motiveX;
                    motionZ -= motiveZ;
                }
            }
        }
    }

    @Override
    public void saveNBT() {
        super.saveNBT();

        saveEntityData();
    }

    public double getMaxSpeed() {
        return maxSpeed;
    }

    protected void activate(int x, int y, int z, boolean flag) {
    }

    private void setFalling() {
        motionX = NukkitMath.clamp(motionX, -getMaxSpeed(), getMaxSpeed());
        motionZ = NukkitMath.clamp(motionZ, -getMaxSpeed(), getMaxSpeed());

        if (!hasUpdated) {
            for (cn.nukkit.entity.Entity linked : passengers) {
                linked.setSeatPosition(getMountedOffset(linked).add(0, 0.35f));
                updatePassengerPosition(linked);
            }

            hasUpdated = true;
        }

        if (onGround) {
            motionX *= derailedX;
            motionY *= derailedY;
            motionZ *= derailedZ;
        }

        move(motionX, motionY, motionZ);
        if (!onGround) {
            motionX *= flyingX;
            motionY *= flyingY;
            motionZ *= flyingZ;
        }
    }

    private void processMovement(int dx, int dy, int dz, BlockRail block) {
        fallDistance = 0.0F;
        Vector3 vector = getNextRail(x, y, z);

        y = dy;
        boolean isPowered = false;
        boolean isSlowed = false;

        if (block instanceof BlockRailPowered) {
            isPowered = block.isActive();
            isSlowed = !block.isActive();
        }

        switch (Orientation.byMetadata(block.getRealMeta())) {
            case ASCENDING_NORTH:
                motionX -= 0.0078125D;
                y += 1;
                break;
            case ASCENDING_SOUTH:
                motionX += 0.0078125D;
                y += 1;
                break;
            case ASCENDING_EAST:
                motionZ += 0.0078125D;
                y += 1;
                break;
            case ASCENDING_WEST:
                motionZ -= 0.0078125D;
                y += 1;
                break;
        }

        int[][] facing = matrix[block.getRealMeta()];
        double facing1 = facing[1][0] - facing[0][0];
        double facing2 = facing[1][2] - facing[0][2];
        double speedOnTurns = Math.sqrt(facing1 * facing1 + facing2 * facing2);
        double realFacing = motionX * facing1 + motionZ * facing2;

        if (realFacing < 0) {
            facing1 = -facing1;
            facing2 = -facing2;
        }

        double squareOfFame = Math.sqrt(motionX * motionX + motionZ * motionZ);

        if (squareOfFame > 2) {
            squareOfFame = 2;
        }

        motionX = squareOfFame * facing1 / speedOnTurns;
        motionZ = squareOfFame * facing2 / speedOnTurns;
        double expectedSpeed;
        double playerYawNeg; // PlayerYawNegative
        double playerYawPos; // PlayerYawPositive
        double motion;

        cn.nukkit.entity.Entity linked = getPassenger();

        if (linked instanceof EntityLiving) {
            expectedSpeed = currentSpeed;
            if (expectedSpeed > 0) {
                // This is a trajectory (Angle of elevation)
                playerYawNeg = -Math.sin(linked.yaw * Math.PI / 180.0F);
                playerYawPos = Math.cos(linked.yaw * Math.PI / 180.0F);
                motion = motionX * motionX + motionZ * motionZ;
                if (motion < 0.01D) {
                    motionX += playerYawNeg * 0.1D;
                    motionZ += playerYawPos * 0.1D;

                    isSlowed = false;
                }
            }
        }

        //http://minecraft.gamepedia.com/Powered_Rail#Rail
        if (isSlowed) {
            expectedSpeed = Math.sqrt(motionX * motionX + motionZ * motionZ);
            if (expectedSpeed < 0.03D) {
                motionX *= 0;
                motionY *= 0;
                motionZ *= 0;
            } else {
                motionX *= 0.5D;
                motionY *= 0;
                motionZ *= 0.5D;
            }
        }

        playerYawNeg = (double) dx + 0.5D + (double) facing[0][0] * 0.5D;
        playerYawPos = (double) dz + 0.5D + (double) facing[0][2] * 0.5D;
        motion = (double) dx + 0.5D + (double) facing[1][0] * 0.5D;
        double wallOfFame = (double) dz + 0.5D + (double) facing[1][2] * 0.5D;

        facing1 = motion - playerYawNeg;
        facing2 = wallOfFame - playerYawPos;
        double motX;
        double motZ;

        if (facing1 == 0) {
            x = (double) dx + 0.5D;
            expectedSpeed = z - (double) dz;
        } else if (facing2 == 0) {
            z = (double) dz + 0.5D;
            expectedSpeed = x - (double) dx;
        } else {
            motX = x - playerYawNeg;
            motZ = z - playerYawPos;
            expectedSpeed = (motX * facing1 + motZ * facing2) * 2;
        }

        x = playerYawNeg + facing1 * expectedSpeed;
        z = playerYawPos + facing2 * expectedSpeed;
        setPosition(new Vector3(x, y, z)); // Hehe, my minstake :3

        motX = motionX;
        motZ = motionZ;
        if (!passengers.isEmpty()) {
            motX *= 0.75D;
            motZ *= 0.75D;
        }
        motX = NukkitMath.clamp(motX, -getMaxSpeed(), getMaxSpeed());
        motZ = NukkitMath.clamp(motZ, -getMaxSpeed(), getMaxSpeed());

        move(motX, 0, motZ);
        if (facing[0][1] != 0 && MathHelper.floor(x) - dx == facing[0][0] && MathHelper.floor(z) - dz == facing[0][2]) {
            setPosition(new Vector3(x, y + (double) facing[0][1], z));
        } else if (facing[1][1] != 0 && MathHelper.floor(x) - dx == facing[1][0] && MathHelper.floor(z) - dz == facing[1][2]) {
            setPosition(new Vector3(x, y + (double) facing[1][1], z));
        }

        applyDrag();
        Vector3 vector1 = getNextRail(x, y, z);

        if (vector1 != null && vector != null) {
            double d14 = (vector.y - vector1.y) * 0.05D;

            squareOfFame = Math.sqrt(motionX * motionX + motionZ * motionZ);
            if (squareOfFame > 0) {
                motionX = motionX / squareOfFame * (squareOfFame + d14);
                motionZ = motionZ / squareOfFame * (squareOfFame + d14);
            }

            setPosition(new Vector3(x, vector1.y, z));
        }

        int floorX = MathHelper.floor(x);
        int floorZ = MathHelper.floor(z);

        if (floorX != dx || floorZ != dz) {
            squareOfFame = Math.sqrt(motionX * motionX + motionZ * motionZ);
            motionX = squareOfFame * (double) (floorX - dx);
            motionZ = squareOfFame * (double) (floorZ - dz);
        }

        if (isPowered) {
            double newMovie = Math.sqrt(motionX * motionX + motionZ * motionZ);

            if (newMovie > 0.01D) {
                double nextMovie = 0.06D;

                motionX += motionX / newMovie * nextMovie;
                motionZ += motionZ / newMovie * nextMovie;
            } else if (block.getOrientation() == Orientation.STRAIGHT_NORTH_SOUTH) {
                if (level.getBlock(new Vector3(dx - 1, dy, dz)).isNormalBlock()) {
                    motionX = 0.02D;
                } else if (level.getBlock(new Vector3(dx + 1, dy, dz)).isNormalBlock()) {
                    motionX = -0.02D;
                }
            } else if (block.getOrientation() == Orientation.STRAIGHT_EAST_WEST) {
                if (level.getBlock(new Vector3(dx, dy, dz - 1)).isNormalBlock()) {
                    motionZ = 0.02D;
                } else if (level.getBlock(new Vector3(dx, dy, dz + 1)).isNormalBlock()) {
                    motionZ = -0.02D;
                }
            }
        }

    }

    private void applyDrag() {
        if (!passengers.isEmpty() || !slowWhenEmpty) {
            motionX *= 0.996999979019165D;
            motionY *= 0.0D;
            motionZ *= 0.996999979019165D;
        } else {
            motionX *= 0.9599999785423279D;
            motionY *= 0.0D;
            motionZ *= 0.9599999785423279D;
        }
    }

    private Vector3 getNextRail(double dx, double dy, double dz) {
        int checkX = MathHelper.floor(dx);
        int checkY = MathHelper.floor(dy);
        int checkZ = MathHelper.floor(dz);

        if (Rail.isRailBlock(level.getBlockIdAt(checkX, checkY - 1, checkZ))) {
            --checkY;
        }

        Block block = level.getBlock(new Vector3(checkX, checkY, checkZ));

        if (Rail.isRailBlock(block)) {
            int[][] facing = matrix[((BlockRail) block).getRealMeta()];
            double rail;
            // Genisys mistake (Doesn't check surrounding more exactly)
            double nextOne = (double) checkX + 0.5D + (double) facing[0][0] * 0.5D;
            double nextTwo = (double) checkY + 0.5D + (double) facing[0][1] * 0.5D;
            double nextThree = (double) checkZ + 0.5D + (double) facing[0][2] * 0.5D;
            double nextFour = (double) checkX + 0.5D + (double) facing[1][0] * 0.5D;
            double nextFive = (double) checkY + 0.5D + (double) facing[1][1] * 0.5D;
            double nextSix = (double) checkZ + 0.5D + (double) facing[1][2] * 0.5D;
            double nextSeven = nextFour - nextOne;
            double nextEight = (nextFive - nextTwo) * 2;
            double nextMax = nextSix - nextThree;

            if (nextSeven == 0) {
                rail = dz - (double) checkZ;
            } else if (nextMax == 0) {
                rail = dx - (double) checkX;
            } else {
                double whatOne = dx - nextOne;
                double whatTwo = dz - nextThree;

                rail = (whatOne * nextSeven + whatTwo * nextMax) * 2;
            }

            dx = nextOne + nextSeven * rail;
            dy = nextTwo + nextEight * rail;
            dz = nextThree + nextMax * rail;
            if (nextEight < 0) {
                ++dy;
            }

            if (nextEight > 0) {
                dy += 0.5D;
            }

            return new Vector3(dx, dy, dz);
        } else {
            return null;
        }
    }

    /**
     * Used to multiply the minecart current speed
     *
     * @param speed The speed of the minecart that will be calculated
     */
    public void setCurrentSpeed(double speed) {
        currentSpeed = speed;
    }

    private void prepareDataProperty() {
        setRollingAmplitude(0);
        setRollingDirection(1);
        if (namedTag.contains("CustomDisplayTile")) {
            if (namedTag.getBoolean("CustomDisplayTile")) {
                int display = namedTag.getInt("DisplayTile");
                int offSet = namedTag.getInt("DisplayOffset");
                setDataProperty(new ByteEntityData(DATA_HAS_DISPLAY, 1));
                setDataProperty(new IntEntityData(DATA_DISPLAY_ITEM, display));
                setDataProperty(new IntEntityData(DATA_DISPLAY_OFFSET, offSet));
            }
        } else {
            int display = blockInside == null ? 0
                    : blockInside.getId()
                    | blockInside.getDamage() << 16;
            if (display == 0) {
                setDataProperty(new ByteEntityData(DATA_HAS_DISPLAY, 0));
                return;
            }
            setDataProperty(new ByteEntityData(DATA_HAS_DISPLAY, 1));
            setDataProperty(new IntEntityData(DATA_DISPLAY_ITEM, display));
            setDataProperty(new IntEntityData(DATA_DISPLAY_OFFSET, 6));
        }
    }

    private void saveEntityData() {
        boolean hasDisplay = super.getDataPropertyByte(DATA_HAS_DISPLAY) == 1
                || blockInside != null;
        int display;
        int offSet;
        namedTag.putBoolean("CustomDisplayTile", hasDisplay);
        if (hasDisplay) {
            display = blockInside.getId()
                    | blockInside.getDamage() << 16;
            offSet = getDataPropertyInt(DATA_DISPLAY_OFFSET);
            namedTag.putInt("DisplayTile", display);
            namedTag.putInt("DisplayOffset", offSet);
        }
    }

    /**
     * Set the minecart display block
     *
     * @param block The block that will changed. Set {@code null} for BlockAir
     * @return {@code true} if the block is normal block
     */
    public boolean setDisplayBlock(Block block) {
        return setDisplayBlock(block, true);
    }

    /**
     * Set the minecart display block
     *
     * @param block  The block that will changed. Set {@code null} for BlockAir
     * @param update Do update for the block. (This state changes if you want to show the block)
     * @return {@code true} if the block is normal block
     */
    @API(usage = Usage.MAINTAINED, definition = Definition.UNIVERSAL)
    public boolean setDisplayBlock(Block block, boolean update) {
        if (!update) {
            if (block.isNormalBlock()) {
                blockInside = block;
            } else {
                blockInside = null;
            }
            return true;
        }
        if (block != null) {
            if (block.isNormalBlock()) {
                blockInside = block;
                int display = blockInside.getId()
                        | blockInside.getDamage() << 16;
                setDataProperty(new ByteEntityData(DATA_HAS_DISPLAY, 1));
                setDataProperty(new IntEntityData(DATA_DISPLAY_ITEM, display));
                setDisplayBlockOffset(6);
            }
        } else {
            // Set block to air (default).
            blockInside = null;
            setDataProperty(new ByteEntityData(DATA_HAS_DISPLAY, 0));
            setDataProperty(new IntEntityData(DATA_DISPLAY_ITEM, 0));
            setDisplayBlockOffset(0);
        }
        return true;
    }

    /**
     * Get the minecart display block
     *
     * @return Block of minecart display block
     */
    @API(usage = Usage.STABLE, definition = Definition.UNIVERSAL)
    public Block getDisplayBlock() {
        return blockInside;
    }

    /**
     * Get the block display offset
     *
     * @return integer
     */
    @API(usage = Usage.EXPERIMENTAL, definition = Definition.UNIVERSAL)
    public int getDisplayBlockOffset() {
        return super.getDataPropertyInt(DATA_DISPLAY_OFFSET);
    }

    /**
     * Set the block offset.
     *
     * @param offset The offset
     */
    @API(usage = Usage.EXPERIMENTAL, definition = Definition.PLATFORM_NATIVE)
    public void setDisplayBlockOffset(int offset) {
        setDataProperty(new IntEntityData(DATA_DISPLAY_OFFSET, offset));
    }

    /**
     * Is the minecart can be slowed when empty?
     *
     * @return boolean
     */
    @API(usage = Usage.EXPERIMENTAL, definition = Definition.UNIVERSAL)
    public boolean isSlowWhenEmpty() {
        return slowWhenEmpty;
    }

    /**
     * Set the minecart slowdown flag
     *
     * @param slow The slowdown flag
     */
    @API(usage = Usage.EXPERIMENTAL, definition = Definition.UNIVERSAL)
    public void setSlowWhenEmpty(boolean slow) {
        slowWhenEmpty = slow;
    }

    public Vector3 getFlyingVelocityMod() {
        return new Vector3(flyingX, flyingY, flyingZ);
    }

    public void setFlyingVelocityMod(Vector3 flying) {
        Objects.requireNonNull(flying, "Flying velocity modifiers cannot be null");
        flyingX = flying.getX();
        flyingY = flying.getY();
        flyingZ = flying.getZ();
    }

    public Vector3 getDerailedVelocityMod() {
        return new Vector3(derailedX, derailedY, derailedZ);
    }

    public void setDerailedVelocityMod(Vector3 derailed) {
        Objects.requireNonNull(derailed, "Derailed velocity modifiers cannot be null");
        derailedX = derailed.getX();
        derailedY = derailed.getY();
        derailedZ = derailed.getZ();
    }

    public void setMaximumSpeed(double speed) {
        maxSpeed = speed;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy