Edit Wither's Base AI

Discussion in 'Spigot Plugin Development' started by Necrone, May 31, 2017.

  1. This is also a general question that extends to similar mobs (code wise) such as creepers.

    Is there any way to change the AI called in onLivingUpdate() (currently labelled A_() in NMS).

    For example, this is the withers update method:

    Code (Text):

    /**
    * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
    * use this to react to sunlight and start to burn.
    */
    public void onLivingUpdate()
    {
        this.motionY *= 0.6000000238418579D;

        if (!this.world.isRemote && this.getWatchedTargetId(0) > 0)
        {
            Entity entity = this.world.getEntityByID(this.getWatchedTargetId(0));

            if (entity != null)
            {
                if (this.posY < entity.posY || !this.isArmored() && this.posY < entity.posY + 5.0D)
                {
                    if (this.motionY < 0.0D)
                    {
                        this.motionY = 0.0D;
                    }

                    this.motionY += (0.5D - this.motionY) * 0.6000000238418579D;
                }

                double d0 = entity.posX - this.posX;
                double d1 = entity.posZ - this.posZ;
                double d3 = d0 * d0 + d1 * d1;

                if (d3 > 9.0D)
                {
                    double d5 = (double)MathHelper.sqrt(d3);
                    this.motionX += (d0 / d5 * 0.5D - this.motionX) * 0.6000000238418579D;
                    this.motionZ += (d1 / d5 * 0.5D - this.motionZ) * 0.6000000238418579D;
                }
            }
        }

        if (this.motionX * this.motionX + this.motionZ * this.motionZ > 0.05000000074505806D)
        {
            this.rotationYaw = (float)MathHelper.atan2(this.motionZ, this.motionX) * (180F / (float)Math.PI) - 90.0F;
        }

        super.onLivingUpdate();

        for (int i = 0; i < 2; ++i)
        {
            this.yRotOHeads[i] = this.yRotationHeads[i];
            this.xRotOHeads[i] = this.xRotationHeads[i];
        }

        for (int j = 0; j < 2; ++j)
        {
            int k = this.getWatchedTargetId(j + 1);
            Entity entity1 = null;

            if (k > 0)
            {
                entity1 = this.world.getEntityByID(k);
            }

            if (entity1 != null)
            {
                double d11 = this.getHeadX(j + 1);
                double d12 = this.getHeadY(j + 1);
                double d13 = this.getHeadZ(j + 1);
                double d6 = entity1.posX - d11;
                double d7 = entity1.posY + (double)entity1.getEyeHeight() - d12;
                double d8 = entity1.posZ - d13;
                double d9 = (double)MathHelper.sqrt(d6 * d6 + d8 * d8);
                float f = (float)(MathHelper.atan2(d8, d6) * (180D / Math.PI)) - 90.0F;
                float f1 = (float)(-(MathHelper.atan2(d7, d9) * (180D / Math.PI)));
                this.xRotationHeads[j] = this.rotlerp(this.xRotationHeads[j], f1, 40.0F);
                this.yRotationHeads[j] = this.rotlerp(this.yRotationHeads[j], f, 10.0F);
            }
            else
            {
                this.yRotationHeads[j] = this.rotlerp(this.yRotationHeads[j], this.renderYawOffset, 10.0F);
            }
        }

        boolean flag = this.isArmored();

        for (int l = 0; l < 3; ++l)
        {
            double d10 = this.getHeadX(l);
            double d2 = this.getHeadY(l);
            double d4 = this.getHeadZ(l);
            this.world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, d10 + this.rand.nextGaussian() * 0.30000001192092896D, d2 + this.rand.nextGaussian() * 0.30000001192092896D, d4 + this.rand.nextGaussian() * 0.30000001192092896D, 0.0D, 0.0D, 0.0D, new int[0]);

            if (flag && this.world.rand.nextInt(4) == 0)
            {
                this.world.spawnParticle(EnumParticleTypes.SPELL_MOB, d10 + this.rand.nextGaussian() * 0.30000001192092896D, d2 + this.rand.nextGaussian() * 0.30000001192092896D, d4 + this.rand.nextGaussian() * 0.30000001192092896D, 0.699999988079071D, 0.699999988079071D, 0.5D, new int[0]);
            }
        }

        if (this.getInvulTime() > 0)
        {
            for (int i1 = 0; i1 < 3; ++i1)
            {
                this.world.spawnParticle(EnumParticleTypes.SPELL_MOB, this.posX + this.rand.nextGaussian(), this.posY + (double)(this.rand.nextFloat() * 3.3F), this.posZ + this.rand.nextGaussian(), 0.699999988079071D, 0.699999988079071D, 0.8999999761581421D, new int[0]);
            }
        }
    }
     

    I'd like to be able to edit the AI above in my derivative wither class, but there is no way to override that method properly, and I need to call the super of onLivingUpdate() in order for the entity to function on a basic level, since mojang has made the terrific design choice of completely ignoring PathFinderGoals for Wither head attacks and instead hard coding it into the mobs update method.

    Is there anyway to work around this problem without editing the spigot source directly? (Something I'm having a lot of trouble setting up properly, so if anyone could point me to a guide or walk me through this I'd be very grateful also).

    Thanks
     
  2. Another example from creepers: Handling explosions:
    Code (Text):

    if (this.isEntityAlive())
    {
        this.lastActiveTime = this.timeSinceIgnited;

        if (this.hasIgnited())
        {
            this.setCreeperState(1);
        }

        int i = this.getCreeperState();

        if (i > 0 && this.timeSinceIgnited == 0)
        {
            this.playSound(SoundEvents.ENTITY_CREEPER_PRIMED, 1.0F, 0.5F);
        }

        this.timeSinceIgnited += i;

        if (this.timeSinceIgnited < 0)
        {
            this.timeSinceIgnited = 0;
        }

        if (this.timeSinceIgnited >= this.fuseTime)
        {
            this.timeSinceIgnited = this.fuseTime;
            this.explode();
        }
    }

    super.onUpdate();
     
     
  3. Code (Text):
        @Override
        public void A_() {
            // ?
        }
    In your wither class?
     
  4. You need to call the super A_() methods in EntityLiving etc in order for the entity to function at all. Given java's basic inheritance principles, you cannot skip a level of inheritance when calling super methods, this means that it is impossible to get rid of the AI via this method. Unless anyone has another solution, I'll be using a custom spigot build for this.
     
  5. TomTheDeveloper

    Supporter

    Then why don't u adjust the extended class too? This way you also gain control over the super A_() method
     
    • Agree Agree x 1
  6. It think PathfinderGoals do control only the Wither Skulls from the center head, but not the ones for the other 2 Heads
     
  7. Although it might not seem nice, is there any issue with simply copying the Wither's update code and modifying it for your custom Wither? Apologies if I'm not seeing some obvious issue.