Jump to content

Projectile entity floats in the air when it hits a block


Jay Avery

Recommended Posts

I'm making a throwing spear entity, and having a weird bug that I can't figure out. When the spear lands in a block, it renders as if it's floating in the air about a block away from the place it lands. I'm pretty sure the entity's actual location is still in the block it's supposed to be in, based on when/where it's able to be picked up by the player. Sometimes the entity *does* appear to stick in the block as it's supposed to, but then after a second or two it pops back to a mid-air floating position. Here's a screenshot of what the entity looks like once it's 'landed' on the wall to the right (note that I'm using the arrow texture as a temporary stand-in to render it):

 

aAtwdOM.png

 

I'm mostly using code from the Arrow and Throwable classes with only some minor tweaks. Here's my abstract EntityThrowable class:

 

package com.jj.jjmod.entities.abstracts;

import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material ;
import net.minecraft.block.state.IBlockState ;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IProjectile ;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.SoundEvents ;
import net.minecraft.item.ItemStack ;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.datafix.DataFixer;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

public abstract class EntityThrowableVisible extends Entity implements IProjectile
{
    
    // TODO full copy of entitythrowable
    
    protected int xTile;
    protected int yTile;
    protected int zTile;
    protected Block inTile;
    public boolean inGround;
    public int throwableShake;
    /** The entity that threw this throwable item. */
    private EntityLivingBase thrower;
    private String throwerName;
    private int ticksInGround;
    private int ticksInAir;
    public Entity ignoreEntity;
    private int ignoreTime;

    public EntityThrowableVisible(World worldIn)
    {
        super(worldIn);
        this.xTile = -1;
        this.yTile = -1;
        this.zTile = -1;
        this.setSize(0.5F, 0.5F);
    }

    public EntityThrowableVisible(World worldIn, double x, double y, double z)
    {
        this(worldIn);
        this.setPosition(x, y, z);
    }

    public EntityThrowableVisible(World worldIn, EntityLivingBase throwerIn)
    {
        this(worldIn, throwerIn.posX, throwerIn.posY + (double)throwerIn.getEyeHeight() - 0.10000000149011612D, throwerIn.posZ);
        this.thrower = throwerIn;
    }

    protected void entityInit()
    {
    }

    /**
     * Checks if the entity is in range to render.
     */
    @SideOnly(Side.CLIENT)
    public boolean isInRangeToRenderDist(double distance)
    {
        double d0 = this.getEntityBoundingBox().getAverageEdgeLength() * 4.0D;

        if (Double.isNaN(d0))
        {
            d0 = 4.0D;
        }

        d0 = d0 * 64.0D;
        return distance < d0 * d0;
    }

    /**
     * Sets throwable heading based on an entity that's throwing it
     */
    public void setHeadingFromThrower(Entity entityThrower, float rotationPitchIn, float rotationYawIn, float pitchOffset, float velocity, float inaccuracy)
    {
        float f = -MathHelper.sin(rotationYawIn * 0.017453292F) * MathHelper.cos(rotationPitchIn * 0.017453292F);
        float f1 = -MathHelper.sin((rotationPitchIn + pitchOffset) * 0.017453292F);
        float f2 = MathHelper.cos(rotationYawIn * 0.017453292F) * MathHelper.cos(rotationPitchIn * 0.017453292F);
        this.setThrowableHeading((double)f, (double)f1, (double)f2, velocity, inaccuracy);
        this.motionX += entityThrower.motionX;
        this.motionZ += entityThrower.motionZ;

        if (!entityThrower.onGround)
        {
            this.motionY += entityThrower.motionY;
        }
    }

    /**
     * Similar to setArrowHeading, it's point the throwable entity to a x, y, z direction.
     */
    public void setThrowableHeading(double x, double y, double z, float velocity, float inaccuracy)
    {
        float f = MathHelper.sqrt_double(x * x + y * y + z * z);
        x = x / (double)f;
        y = y / (double)f;
        z = z / (double)f;
        x = x + this.rand.nextGaussian() * 0.007499999832361937D * (double)inaccuracy;
        y = y + this.rand.nextGaussian() * 0.007499999832361937D * (double)inaccuracy;
        z = z + this.rand.nextGaussian() * 0.007499999832361937D * (double)inaccuracy;
        x = x * (double)velocity;
        y = y * (double)velocity;
        z = z * (double)velocity;
        this.motionX = x;
        this.motionY = y;
        this.motionZ = z;
        float f1 = MathHelper.sqrt_double(x * x + z * z);
        this.rotationYaw = (float)(MathHelper.atan2(x, z) * (180D / Math.PI));
        this.rotationPitch = (float)(MathHelper.atan2(y, (double)f1) * (180D / Math.PI));
        this.prevRotationYaw = this.rotationYaw;
        this.prevRotationPitch = this.rotationPitch;
        this.ticksInGround = 0;
    }

    /**
     * Updates the velocity of the entity to a new value.
     */
    @SideOnly(Side.CLIENT)
    public void setVelocity(double x, double y, double z)
    {
        this.motionX = x;
        this.motionY = y;
        this.motionZ = z;

        if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F)
        {
            float f = MathHelper.sqrt_double(x * x + z * z);
            this.rotationYaw = (float)(MathHelper.atan2(x, z) * (180D / Math.PI));
            this.rotationPitch = (float)(MathHelper.atan2(y, (double)f) * (180D / Math.PI));
            this.prevRotationYaw = this.rotationYaw;
            this.prevRotationPitch = this.rotationPitch;
            this.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch);
            this.ticksInGround = 0;
        }
    }

    /**
     * Called to update the entity's position/logic.
     */
    public void onUpdate()
    {
        
        if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F)
        {
            float f = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
            this.rotationYaw = (float)(MathHelper.atan2(this.motionX, this.motionZ) * (180D / Math.PI));
            this.rotationPitch = (float)(MathHelper.atan2(this.motionY, (double)f) * (180D / Math.PI));
            this.prevRotationYaw = this.rotationYaw;
            this.prevRotationPitch = this.rotationPitch;
        }

        BlockPos blockpos = new BlockPos(this.xTile, this.yTile, this.zTile);
        IBlockState iblockstate = this.worldObj.getBlockState(blockpos);
        Block block = iblockstate.getBlock();

        if (iblockstate.getMaterial() != Material.AIR)
        {
            AxisAlignedBB axisalignedbb = iblockstate.getCollisionBoundingBox(this.worldObj, blockpos);

            if (axisalignedbb != Block.NULL_AABB && axisalignedbb.offset(blockpos).isVecInside(new Vec3d(this.posX, this.posY, this.posZ)))
            {
                this.inGround = true;
            }
        }


        if (this.inGround)
        {

            if (block == this.inTile )
            {
                ++this.ticksInGround;

                if (this.ticksInGround >= 1200)
                {
                    //System.out.println( "time up" ) ;
                    //this.setDead();
                }
            }
            else
            {
                this.inGround = false;
                this.motionX *= (double)(this.rand.nextFloat() * 0.2F);
                this.motionY *= (double)(this.rand.nextFloat() * 0.2F);
                this.motionZ *= (double)(this.rand.nextFloat() * 0.2F);
                this.ticksInGround = 0;
                this.ticksInAir = 0;
            }

        }
        else
        {
            ++this.ticksInAir;
            Vec3d vec3d1 = new Vec3d(this.posX, this.posY, this.posZ);
            Vec3d vec3d = new Vec3d(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
            RayTraceResult raytraceresult = this.worldObj.rayTraceBlocks(vec3d1, vec3d, false, true, false);
            vec3d1 = new Vec3d(this.posX, this.posY, this.posZ);
            vec3d = new Vec3d(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);

            if (raytraceresult != null)
            {
                vec3d = new Vec3d(raytraceresult.hitVec.xCoord, raytraceresult.hitVec.yCoord, raytraceresult.hitVec.zCoord);
            }

            Entity entity = null;
            List<Entity> list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox().addCoord(this.motionX, this.motionY, this.motionZ).expandXyz(1.0D));
            double d0 = 0.0D;
            boolean flag = false;

            for (int i = 0; i < list.size(); ++i)
            {
                Entity entity1 = (Entity)list.get(i);

                if (entity1.canBeCollidedWith())
                {
                    if (entity1 == this.ignoreEntity)
                    {
                        flag = true;
                    }
                    else if (this.ticksExisted < 2 && this.ignoreEntity == null)
                    {
                        this.ignoreEntity = entity1;
                        flag = true;
                    }
                    else
                    {
                        flag = false;
                        AxisAlignedBB axisalignedbb = entity1.getEntityBoundingBox().expandXyz(0.30000001192092896D);
                        RayTraceResult raytraceresult1 = axisalignedbb.calculateIntercept(vec3d, vec3d1);

                        if (raytraceresult1 != null)
                        {
                            double d1 = vec3d.squareDistanceTo(raytraceresult1.hitVec);

                            if (d1 < d0 || d0 == 0.0D)
                            {
                                entity = entity1;
                                d0 = d1;
                            }
                        }
                    }
                }
            }

            if (this.ignoreEntity != null)
            {
                if (flag)
                {
                    this.ignoreTime = 2;
                }
                else if (this.ignoreTime-- <= 0)
                {
                    this.ignoreEntity = null;
                }
            }
            if (entity != null)
            {
                raytraceresult = new RayTraceResult(entity);
            }

            if (raytraceresult != null && raytraceresult.entityHit != null && raytraceresult.entityHit instanceof EntityPlayer)
            {
                EntityPlayer entityplayer = (EntityPlayer)raytraceresult.entityHit;

                if (this.thrower instanceof EntityPlayer && !((EntityPlayer)this.thrower).canAttackPlayer(entityplayer))
                {
                    raytraceresult = null;
                }
            }

            if (raytraceresult != null)
            {
                this.onImpact(raytraceresult);
            }

            this.posX += this.motionX;
            this.posY += this.motionY;
            this.posZ += this.motionZ;
            float f4 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
            this.rotationYaw = (float)(MathHelper.atan2(this.motionX, this.motionZ) * (180D / Math.PI));

            for (this.rotationPitch = (float)(MathHelper.atan2(this.motionY, (double)f4) * (180D / Math.PI)); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F)
            {
                ;
            }

            while (this.rotationPitch - this.prevRotationPitch >= 180.0F)
            {
                this.prevRotationPitch += 360.0F;
            }

            while (this.rotationYaw - this.prevRotationYaw < -180.0F)
            {
                this.prevRotationYaw -= 360.0F;
            }

            while (this.rotationYaw - this.prevRotationYaw >= 180.0F)
            {
                this.prevRotationYaw += 360.0F;
            }

            this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F;
            this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F;
            float f1 = 0.99F;
            float f2 = 0.05F;

            if (this.isInWater())
            {
                for (int i = 0; i < 4; ++i)
                {
                    float f3 = 0.25F;
                    this.worldObj.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX - this.motionX * 0.25D, this.posY - this.motionY * 0.25D, this.posZ - this.motionZ * 0.25D, this.motionX, this.motionY, this.motionZ, new int[0]);
                }

                f1 = 0.6F;
            }

            if (this.isWet())
            {
                this.extinguish();
            }

            this.motionX *= (double)f1;
            this.motionY *= (double)f1;
            this.motionZ *= (double)f1;

            if (!this.func_189652_ae())
            {
                this.motionY -= 0.05000000074505806D;
            }

            System.out.println( "setting position to " + this.posX + " , " + this.posY + " , " + this.posZ ) ;
            this.setPosition(this.posX, this.posY, this.posZ);
            this.doBlockCollisions();
        }
        
        
        
        
        
        // mostly working
        /*
        this.lastTickPosX = this.posX;
        this.lastTickPosY = this.posY;
        this.lastTickPosZ = this.posZ;
        //super.onUpdate();
        
        
        
        if ( !this.inGround ) {
        
        

        if (this.throwableShake > 0)
        {
            --this.throwableShake;
        }


        ++this.ticksInAir;
        

        Vec3d vec3d = new Vec3d(this.posX, this.posY, this.posZ);
        Vec3d vec3d1 = new Vec3d(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
        RayTraceResult raytraceresult = this.worldObj.rayTraceBlocks(vec3d, vec3d1);
        vec3d = new Vec3d(this.posX, this.posY, this.posZ);
        vec3d1 = new Vec3d(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);

        if (raytraceresult != null)
        {
            vec3d1 = new Vec3d(raytraceresult.hitVec.xCoord, raytraceresult.hitVec.yCoord, raytraceresult.hitVec.zCoord);
        }

        Entity entity = null;
        List<Entity> list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox().addCoord(this.motionX, this.motionY, this.motionZ).expandXyz(1.0D));
        double d0 = 0.0D;
        boolean flag = false;

        for (int i = 0; i < list.size(); ++i)
        {
            Entity entity1 = (Entity)list.get(i);

            if (entity1.canBeCollidedWith())
            {
                if (entity1 == this.ignoreEntity)
                {
                    flag = true;
                }
                else if (this.ticksExisted < 2 && this.ignoreEntity == null)
                {
                    this.ignoreEntity = entity1;
                    flag = true;
                }
                else
                {
                    flag = false;
                    AxisAlignedBB axisalignedbb = entity1.getEntityBoundingBox().expandXyz(0.30000001192092896D);
                    RayTraceResult raytraceresult1 = axisalignedbb.calculateIntercept(vec3d, vec3d1);

                    if (raytraceresult1 != null)
                    {
                        double d1 = vec3d.squareDistanceTo(raytraceresult1.hitVec);

                        if (d1 < d0 || d0 == 0.0D)
                        {
                            entity = entity1;
                            d0 = d1;
                        }
                    }
                }
            }
        }

        if (this.ignoreEntity != null)
        {
            if (flag)
            {
                this.ignoreTime = 2;
            }
            else if (this.ignoreTime-- <= 0)
            {
                this.ignoreEntity = null;
            }
        }

        if (entity != null)
        {
            raytraceresult = new RayTraceResult(entity);
        }

        if (raytraceresult != null)
        {
            if (raytraceresult.typeOfHit == RayTraceResult.Type.BLOCK && this.worldObj.getBlockState(raytraceresult.getBlockPos()).getBlock() == Blocks.PORTAL)
            {
                this.setPortal(raytraceresult.getBlockPos());
            }
            else
            {
                if(!net.minecraftforge.common.ForgeHooks.onThrowableImpact(this, raytraceresult))
                this.onImpact(raytraceresult);
            }
        }

        this.posX += this.motionX;
        this.posY += this.motionY;
        this.posZ += this.motionZ;
        float f = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
        this.rotationYaw = (float)(MathHelper.atan2(this.motionX, this.motionZ) * (180D / Math.PI));

        for (this.rotationPitch = (float)(MathHelper.atan2(this.motionY, (double)f) * (180D / Math.PI)); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F)
        {
            ;
        }

        while (this.rotationPitch - this.prevRotationPitch >= 180.0F)
        {
            this.prevRotationPitch += 360.0F;
        }

        while (this.rotationYaw - this.prevRotationYaw < -180.0F)
        {
            this.prevRotationYaw -= 360.0F;
        }

        while (this.rotationYaw - this.prevRotationYaw >= 180.0F)
        {
            this.prevRotationYaw += 360.0F;
        }

        this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F;
        this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F;
        float f1 = 0.99F;
        float f2 = this.getGravityVelocity();

        if (this.isInWater())
        {
            for (int j = 0; j < 4; ++j)
            {
                float f3 = 0.25F;
                this.worldObj.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX - this.motionX * 0.25D, this.posY - this.motionY * 0.25D, this.posZ - this.motionZ * 0.25D, this.motionX, this.motionY, this.motionZ, new int[0]);
            }

            f1 = 0.8F;
        }

        this.motionX *= (double)f1;
        this.motionY *= (double)f1;
        this.motionZ *= (double)f1;

        if (!this.func_189652_ae())
        {
            this.motionY -= (double)f2;
        }
        }

        this.setPosition(this.posX, this.posY, this.posZ); */
    }

    /**
     * Gets the amount of gravity to apply to the thrown entity with each tick.
     */
    protected float getGravityVelocity()
    {
        return 0.03F;
    }

    /**
     * Called when this EntityThrowable hits a block or entity.
     */
    protected abstract void onImpact(RayTraceResult result);

    public static void func_189661_a(DataFixer p_189661_0_, String p_189661_1_)
    {
    }

    /**
     * (abstract) Protected helper method to write subclass entity data to NBT.
     */
    public void writeEntityToNBT(NBTTagCompound compound)
    {
        compound.setInteger("xTile", this.xTile);
        compound.setInteger("yTile", this.yTile);
        compound.setInteger("zTile", this.zTile);
        ResourceLocation resourcelocation = (ResourceLocation)Block.REGISTRY.getNameForObject(this.inTile);
        compound.setString("inTile", resourcelocation == null ? "" : resourcelocation.toString());
        compound.setByte("shake", (byte)this.throwableShake);
        compound.setByte("inGround", (byte)(this.inGround ? 1 : 0));

        if ((this.throwerName == null || this.throwerName.isEmpty()) && this.thrower instanceof EntityPlayer)
        {
            this.throwerName = this.thrower.getName();
        }

        compound.setString("ownerName", this.throwerName == null ? "" : this.throwerName);
    }

    /**
     * (abstract) Protected helper method to read subclass entity data from NBT.
     */
    public void readEntityFromNBT(NBTTagCompound compound)
    {
        this.xTile = compound.getInteger("xTile");
        this.yTile = compound.getInteger("yTile");
        this.zTile = compound.getInteger("zTile");

        if (compound.hasKey("inTile", )
        {
            this.inTile = Block.getBlockFromName(compound.getString("inTile"));
        }
        else
        {
            this.inTile = Block.getBlockById(compound.getByte("inTile") & 255);
        }

        this.throwableShake = compound.getByte("shake") & 255;
        this.inGround = compound.getByte("inGround") == 1;
        this.thrower = null;
        this.throwerName = compound.getString("ownerName");

        if (this.throwerName != null && this.throwerName.isEmpty())
        {
            this.throwerName = null;
        }

        this.thrower = this.getThrower();
    }

    @Nullable
    public EntityLivingBase getThrower()
    {
        if (this.thrower == null && this.throwerName != null && !this.throwerName.isEmpty())
        {
            this.thrower = this.worldObj.getPlayerEntityByName(this.throwerName);

            if (this.thrower == null && this.worldObj instanceof WorldServer)
            {
                try
                {
                    Entity entity = ((WorldServer)this.worldObj).getEntityFromUuid(UUID.fromString(this.throwerName));

                    if (entity instanceof EntityLivingBase)
                    {
                        this.thrower = (EntityLivingBase)entity;
                    }
                }
                catch (Throwable var2)
                {
                    this.thrower = null;
                }
            }
        }

        return this.thrower;
    }
    
    public void onCollideWithPlayer(EntityPlayer entityIn)
    {
        if (!this.worldObj.isRemote && this.inGround )
        {

            entityIn.inventory.addItemStackToInventory(this.getItem());
            this.playSound(SoundEvents.ENTITY_ITEM_PICKUP, 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F);
            entityIn.onItemPickup(this, 1);
            this.setDead();
            
        }
    }

    public abstract ItemStack getItem();
}

 

 

My EntitySpear class which extends the above:

 

package com.jj.jjmod.entities.abstracts ;

import com.jj.jjmod.entities.EntitySpearBronze ;
import com.jj.jjmod.entities.EntitySpearCopper ;
import com.jj.jjmod.entities.EntitySpearFlint ;
import com.jj.jjmod.entities.EntitySpearSteel ;
import com.jj.jjmod.entities.EntitySpearWood ;
import net.minecraft.block.material.Material ;
import net.minecraft.block.state.IBlockState ;
import net.minecraft.entity.EntityLivingBase ;
import net.minecraft.init.SoundEvents ;
import net.minecraft.util.DamageSource ;
import net.minecraft.util.math.BlockPos ;
import net.minecraft.util.math.MathHelper ;
import net.minecraft.util.math.RayTraceResult ;
import net.minecraft.world.World ;

public abstract class EntitySpearAbstract extends EntityThrowableVisible {
    
    // TODO make able to pick up
        
    public EntitySpearAbstract( World world ) {
        
        super( world ) ;
        
    }

    public EntitySpearAbstract( World world , EntityLivingBase thrower ) {

        super( world , thrower ) ;

    }
    
    public EntitySpearAbstract( World world , double x , double y , double z ) {
        
        super( world , x , y , z ) ;
        
    }

    @Override
    protected void onImpact( RayTraceResult result ) {
        
        float dam = 1.5F ;
        
        // CONFIG thrown spear damage
        
        if ( this instanceof EntitySpearWood ) {
            
            dam = 1.5F ;
            
        } else if ( this instanceof EntitySpearFlint ) {
            
            dam = 2.5F ;
            
        } else if ( this instanceof EntitySpearCopper ) {
            
            dam = 3.5F ;
            
        } else if ( this instanceof EntitySpearBronze ) {
            
            dam = 4.5F ;
            
        } else if ( this instanceof EntitySpearSteel ) {
            
            dam = 5.5F ;
            
        }

        if ( result.entityHit != null ) {

            result.entityHit.attackEntityFrom( DamageSource.causeThrownDamage(
                    this , this.getThrower() ) , dam ) ;

        } else {
            
            // TEST direct copy from arrow
            System.out.println( "hit block" ) ;
            BlockPos blockpos = result.getBlockPos();
            this.xTile = blockpos.getX();
            this.yTile = blockpos.getY();
            this.zTile = blockpos.getZ();
            IBlockState iblockstate = this.worldObj.getBlockState(blockpos);
            this.inTile = iblockstate.getBlock();
            this.motionX = (double)((float)(result.hitVec.xCoord - this.posX));
            this.motionY = (double)((float)(result.hitVec.yCoord - this.posY));
            this.motionZ = (double)((float)(result.hitVec.zCoord - this.posZ));
            float f2 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ);
            this.posX -= this.motionX / (double)f2 * 0.05000000074505806D;
            this.posY -= this.motionY / (double)f2 * 0.05000000074505806D;
            this.posZ -= this.motionZ / (double)f2 * 0.05000000074505806D;
            this.playSound(SoundEvents.ENTITY_ARROW_HIT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
            this.inGround = true;

            if (iblockstate.getMaterial() != Material.AIR)
            {
                this.inTile.onEntityCollidedWithBlock(this.worldObj, blockpos, iblockstate, this);
            }
        }
    }
}

 

 

The EntitySpear class is abstract, the subclasses only change the item dropped and the onImpact method, like so:

 

package com.jj.jjmod.entities.abstracts ;

import com.jj.jjmod.entities.EntitySpearBronze ;
import com.jj.jjmod.entities.EntitySpearCopper ;
import com.jj.jjmod.entities.EntitySpearFlint ;
import com.jj.jjmod.entities.EntitySpearSteel ;
import com.jj.jjmod.entities.EntitySpearWood ;
import net.minecraft.block.material.Material ;
import net.minecraft.block.state.IBlockState ;
import net.minecraft.entity.EntityLivingBase ;
import net.minecraft.init.SoundEvents ;
import net.minecraft.util.DamageSource ;
import net.minecraft.util.math.BlockPos ;
import net.minecraft.util.math.MathHelper ;
import net.minecraft.util.math.RayTraceResult ;
import net.minecraft.world.World ;

public abstract class EntitySpearAbstract extends EntityThrowableVisible {
    
    // TODO make able to pick up
        
    public EntitySpearAbstract( World world ) {
        
        super( world ) ;
        
    }

    public EntitySpearAbstract( World world , EntityLivingBase thrower ) {

        super( world , thrower ) ;

    }
    
    public EntitySpearAbstract( World world , double x , double y , double z ) {
        
        super( world , x , y , z ) ;
        
    }

    @Override
    protected void onImpact( RayTraceResult result ) {
        
        float dam = 1.5F ;
        
        // CONFIG thrown spear damage
        
        if ( this instanceof EntitySpearWood ) {
            
            dam = 1.5F ;
            
        } else if ( this instanceof EntitySpearFlint ) {
            
            dam = 2.5F ;
            
        } else if ( this instanceof EntitySpearCopper ) {
            
            dam = 3.5F ;
            
        } else if ( this instanceof EntitySpearBronze ) {
            
            dam = 4.5F ;
            
        } else if ( this instanceof EntitySpearSteel ) {
            
            dam = 5.5F ;
            
        }

        if ( result.entityHit != null ) {

            result.entityHit.attackEntityFrom( DamageSource.causeThrownDamage(
                    this , this.getThrower() ) , dam ) ;

        } else {
            
            // TEST direct copy from arrow
            System.out.println( "hit block" ) ;
            BlockPos blockpos = result.getBlockPos();
            this.xTile = blockpos.getX();
            this.yTile = blockpos.getY();
            this.zTile = blockpos.getZ();
            IBlockState iblockstate = this.worldObj.getBlockState(blockpos);
            this.inTile = iblockstate.getBlock();
            this.motionX = (double)((float)(result.hitVec.xCoord - this.posX));
            this.motionY = (double)((float)(result.hitVec.yCoord - this.posY));
            this.motionZ = (double)((float)(result.hitVec.zCoord - this.posZ));
            float f2 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ);
            this.posX -= this.motionX / (double)f2 * 0.05000000074505806D;
            this.posY -= this.motionY / (double)f2 * 0.05000000074505806D;
            this.posZ -= this.motionZ / (double)f2 * 0.05000000074505806D;
            this.playSound(SoundEvents.ENTITY_ARROW_HIT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
            this.inGround = true;

            if (iblockstate.getMaterial() != Material.AIR)
            {
                this.inTile.onEntityCollidedWithBlock(this.worldObj, blockpos, iblockstate, this);
            }
        }
    }
}

 

 

Here is my abstract RenderSpear class:

 

package com.jj.jjmod.render.abstracts;

import com.jj.jjmod.entities.abstracts.EntitySpearAbstract ;
import net.minecraft.client.renderer.GlStateManager ;
import net.minecraft.client.renderer.Tessellator ;
import net.minecraft.client.renderer.VertexBuffer ;
import net.minecraft.client.renderer.entity.Render ;
import net.minecraft.client.renderer.entity.RenderManager ;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats ;
import net.minecraft.entity.Entity ;
import net.minecraft.util.ResourceLocation ;
import net.minecraftforge.fml.relauncher.Side ;
import net.minecraftforge.fml.relauncher.SideOnly ;

@SideOnly(Side.CLIENT)
public abstract class RenderSpearAbstract<T extends Entity> extends Render< EntitySpearAbstract > {

    protected ResourceLocation texture ;
    
    public RenderSpearAbstract( RenderManager renderManager ) {
        
        super( renderManager ) ;

    }

    @Override
    protected ResourceLocation getEntityTexture( EntitySpearAbstract entity ) {

        return this.texture ;
        
    }
    
    @Override
    public void doRender(EntitySpearAbstract entity, double x, double y, double z, float entityYaw, float partialTicks)
    {
        
        System.out.println( "rendering at pos " + x + " , " + y + " , " + z ) ;
        System.out.println( "entity pos is " + entity.posX + " , " + entity.posY + " , " + entity.posZ ) ;
      
        this.bindEntityTexture(entity);
        GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
        GlStateManager.pushMatrix();
        GlStateManager.disableLighting();
        GlStateManager.translate((float)x, (float)y, (float)z);
        GlStateManager.rotate(entity.prevRotationYaw + (entity.rotationYaw - entity.prevRotationYaw) * partialTicks - 90.0F, 0.0F, 1.0F, 0.0F);
        GlStateManager.rotate(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * partialTicks, 0.0F, 0.0F, 1.0F);
        Tessellator tessellator = Tessellator.getInstance();
        VertexBuffer vertexbuffer = tessellator.getBuffer();
        int i = 0;
        float f = 0.0F;
        float f1 = 0.5F;
        float f2 = 0.0F;
        float f3 = 0.15625F;
        float f4 = 0.0F;
        float f5 = 0.15625F;
        float f6 = 0.15625F;
        float f7 = 0.3125F;
        float f8 = 0.05625F;
        GlStateManager.enableRescaleNormal();

        GlStateManager.rotate(45.0F, 1.0F, 0.0F, 0.0F);
        GlStateManager.scale(0.05625F, 0.05625F, 0.05625F);
        GlStateManager.translate(-4.0F, 0.0F, 0.0F);
        

        if (this.renderOutlines)
        {
            GlStateManager.enableColorMaterial();
            GlStateManager.enableOutlineMode(this.getTeamColor(entity));
        }

        GlStateManager.glNormal3f(0.05625F, 0.0F, 0.0F);
        vertexbuffer.begin(7, DefaultVertexFormats.POSITION_TEX);
        vertexbuffer.pos(-7.0D, -2.0D, -2.0D).tex(0.0D, 0.15625D).endVertex();
        vertexbuffer.pos(-7.0D, -2.0D, 2.0D).tex(0.15625D, 0.15625D).endVertex();
        vertexbuffer.pos(-7.0D, 2.0D, 2.0D).tex(0.15625D, 0.3125D).endVertex();
        vertexbuffer.pos(-7.0D, 2.0D, -2.0D).tex(0.0D, 0.3125D).endVertex();
        tessellator.draw();
        GlStateManager.glNormal3f(-0.05625F, 0.0F, 0.0F);
        vertexbuffer.begin(7, DefaultVertexFormats.POSITION_TEX);
        vertexbuffer.pos(-7.0D, 2.0D, -2.0D).tex(0.0D, 0.15625D).endVertex();
        vertexbuffer.pos(-7.0D, 2.0D, 2.0D).tex(0.15625D, 0.15625D).endVertex();
        vertexbuffer.pos(-7.0D, -2.0D, 2.0D).tex(0.15625D, 0.3125D).endVertex();
        vertexbuffer.pos(-7.0D, -2.0D, -2.0D).tex(0.0D, 0.3125D).endVertex();
        tessellator.draw();

        for (int j = 0; j < 4; ++j)
        {
            GlStateManager.rotate(90.0F, 1.0F, 0.0F, 0.0F);
            GlStateManager.glNormal3f(0.0F, 0.0F, 0.05625F);
            vertexbuffer.begin(7, DefaultVertexFormats.POSITION_TEX);
            vertexbuffer.pos(-8.0D, -2.0D, 0.0D).tex(0.0D, 0.0D).endVertex();
            vertexbuffer.pos(8.0D, -2.0D, 0.0D).tex(0.5D, 0.0D).endVertex();
            vertexbuffer.pos(8.0D, 2.0D, 0.0D).tex(0.5D, 0.15625D).endVertex();
            vertexbuffer.pos(-8.0D, 2.0D, 0.0D).tex(0.0D, 0.15625D).endVertex();
            tessellator.draw();
        }

        if (this.renderOutlines)
        {
            GlStateManager.disableOutlineMode();
            GlStateManager.disableColorMaterial();
        }

        GlStateManager.disableRescaleNormal();
        GlStateManager.enableLighting();
        GlStateManager.popMatrix();
        super.doRender(entity, x, y, z, entityYaw, partialTicks);
    }


    
}

 

 

The subclasses just define the texture that will be used for the render, at the moment I'm just using the vanilla arrow texture:

 

package com.jj.jjmod.render;

import com.jj.jjmod.render.abstracts.RenderSpearAbstract ;
import net.minecraft.client.renderer.entity.RenderManager ;
import net.minecraft.util.ResourceLocation ;

public class RenderSpearBronze extends RenderSpearAbstract {

    public RenderSpearBronze( RenderManager renderManager ) {
        
        super( renderManager ) ;
        this.texture = new ResourceLocation("textures/entity/projectiles/arrow.png") ;
        
    }

}

 

 

The entities and renders are registered in these methods:

 

package com.jj.jjmod.entities ;

import com.jj.jjmod.main.Main ;
import com.jj.jjmod.render.RenderSpearBronze ;
import com.jj.jjmod.render.RenderSpearCopper ;
import com.jj.jjmod.render.RenderSpearFlint ;
import com.jj.jjmod.render.RenderSpearSteel ;
import com.jj.jjmod.render.RenderSpearWood ;
import net.minecraftforge.fml.client.registry.RenderingRegistry ;
import net.minecraftforge.fml.common.registry.EntityRegistry ;
import net.minecraftforge.fml.relauncher.Side ;
import net.minecraftforge.fml.relauncher.SideOnly ;

public class ModEntities {

    public static int entityID = 0 ;

    public static void preInit() {

        EntityRegistry.registerModEntity( EntitySpearWood.class , "spearWood" ,
                entityID++ , Main.instance , 80 , 3 , true ) ;
        EntityRegistry.registerModEntity( EntitySpearFlint.class ,
                "spearFlint" ,
                entityID++ , Main.instance , 80 , 3 , true ) ;
        EntityRegistry.registerModEntity( EntitySpearCopper.class ,
                "spearCopper" ,
                entityID++ , Main.instance , 80 , 3 , true ) ;
        EntityRegistry.registerModEntity( EntitySpearBronze.class ,
                "spearBronze" ,
                entityID++ , Main.instance , 80 , 3 , true ) ;
        EntityRegistry.registerModEntity( EntitySpearSteel.class ,
                "spearSteel" ,
                entityID++ , Main.instance , 80 , 3 , true ) ;

    }

    @SideOnly( Side.CLIENT )
    public static void registerRenderers() {

        RenderingRegistry.registerEntityRenderingHandler(
                EntitySpearWood.class , RenderSpearWood::new ) ;
        RenderingRegistry.registerEntityRenderingHandler(
                EntitySpearFlint.class , RenderSpearFlint::new ) ;
        RenderingRegistry.registerEntityRenderingHandler(
                EntitySpearCopper.class , RenderSpearCopper::new ) ;
        RenderingRegistry.registerEntityRenderingHandler(
                EntitySpearBronze.class , RenderSpearBronze::new ) ;
        RenderingRegistry.registerEntityRenderingHandler(
                EntitySpearSteel.class , RenderSpearSteel::new ) ;

    }

}

 

 

Which are called in the ClientProxy in preInit().

 

I cannot figure out why it's rendering in the wrong place , or how the render position abruptly moves a few moments after landing. Can anyone advise?

Link to comment
Share on other sites

Try rendering your spear instead of the arrow because I am assuming that your spear is bigger than the arrow.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

I gave that a try, but it didn't make a difference. I don't think it's an inherent problem with the render, but something that's going wrong after it lands for some reason - because it sometimes *does* render correctly at first. I've also noticed that if I save and reload the world, then the entity renders correctly even if it was doing the floating thing before I exited the world. Something to do with NBT maybe? :S

Link to comment
Share on other sites

  • 2 years later...

Sorry for the necro, but this problem is due data not being synced instantly to the client. Arrows don't have this problem because they have their data specially synced for them (EntityTrackerEntry#updatePlayerList on lines 229-255). The simple solution to this (and the one that Tinkers Construct uses) is to just extend EntityArrow and build everything on top of it. However this isn't a very elegant solution & results in side effects like arrow-related achievements being called on your entities.

Heres my thread with the same problem

and a good thread about the entity data manager where jabelar shows that arrows are specially handled

 

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.