    001package net.minecraft.entity.monster;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import net.minecraft.block.Block;
    006import net.minecraft.entity.Entity;
    007import net.minecraft.entity.EntityLiving;
    008import net.minecraft.entity.ai.EntityAIAttackOnCollide;
    009import net.minecraft.entity.ai.EntityAIDefendVillage;
    010import net.minecraft.entity.ai.EntityAIHurtByTarget;
    011import net.minecraft.entity.ai.EntityAILookAtVillager;
    012import net.minecraft.entity.ai.EntityAILookIdle;
    013import net.minecraft.entity.ai.EntityAIMoveThroughVillage;
    014import net.minecraft.entity.ai.EntityAIMoveTowardsTarget;
    015import net.minecraft.entity.ai.EntityAIMoveTwardsRestriction;
    016import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
    017import net.minecraft.entity.ai.EntityAIWander;
    018import net.minecraft.entity.ai.EntityAIWatchClosest;
    019import net.minecraft.entity.player.EntityPlayer;
    020import net.minecraft.item.Item;
    021import net.minecraft.nbt.NBTTagCompound;
    022import net.minecraft.util.ChunkCoordinates;
    023import net.minecraft.util.DamageSource;
    024import net.minecraft.util.MathHelper;
    025import net.minecraft.village.Village;
    026import net.minecraft.world.World;
    027
    028public class EntityIronGolem extends EntityGolem
    029{
    030    /** deincrements, and a distance-to-home check is done at 0 */
    031    private int homeCheckTimer = 0;
    032    Village villageObj = null;
    033    private int attackTimer;
    034    private int holdRoseTick;
    035
    036    public EntityIronGolem(World par1World)
    037    {
    038        super(par1World);
    039        this.texture = "/mob/villager_golem.png";
    040        this.setSize(1.4F, 2.9F);
    041        this.getNavigator().setAvoidsWater(true);
    042        this.tasks.addTask(1, new EntityAIAttackOnCollide(this, 0.25F, true));
    043        this.tasks.addTask(2, new EntityAIMoveTowardsTarget(this, 0.22F, 32.0F));
    044        this.tasks.addTask(3, new EntityAIMoveThroughVillage(this, 0.16F, true));
    045        this.tasks.addTask(4, new EntityAIMoveTwardsRestriction(this, 0.16F));
    046        this.tasks.addTask(5, new EntityAILookAtVillager(this));
    047        this.tasks.addTask(6, new EntityAIWander(this, 0.16F));
    048        this.tasks.addTask(7, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
    049        this.tasks.addTask(8, new EntityAILookIdle(this));
    050        this.targetTasks.addTask(1, new EntityAIDefendVillage(this));
    051        this.targetTasks.addTask(2, new EntityAIHurtByTarget(this, false));
    052        this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityLiving.class, 16.0F, 0, false, true, IMob.mobSelector));
    053    }
    054
    055    protected void entityInit()
    056    {
    057        super.entityInit();
    058        this.dataWatcher.addObject(16, Byte.valueOf((byte)0));
    059    }
    060
    061    /**
    062     * Returns true if the newer Entity AI code should be run
    063     */
    064    public boolean isAIEnabled()
    065    {
    066        return true;
    067    }
    068
    069    /**
    070     * main AI tick function, replaces updateEntityActionState
    071     */
    072    protected void updateAITick()
    073    {
    074        if (--this.homeCheckTimer <= 0)
    075        {
    076            this.homeCheckTimer = 70 + this.rand.nextInt(50);
    077            this.villageObj = this.worldObj.villageCollectionObj.findNearestVillage(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ), 32);
    078
    079            if (this.villageObj == null)
    080            {
    081                this.detachHome();
    082            }
    083            else
    084            {
    085                ChunkCoordinates var1 = this.villageObj.getCenter();
    086                this.setHomeArea(var1.posX, var1.posY, var1.posZ, (int)((float)this.villageObj.getVillageRadius() * 0.6F));
    087            }
    088        }
    089
    090        super.updateAITick();
    091    }
    092
    093    public int getMaxHealth()
    094    {
    095        return 100;
    096    }
    097
    098    /**
    099     * Decrements the entity's air supply when underwater
    100     */
    101    protected int decreaseAirSupply(int par1)
    102    {
    103        return par1;
    104    }
    105
    106    protected void collideWithEntity(Entity par1Entity)
    107    {
    108        if (par1Entity instanceof IMob && this.getRNG().nextInt(20) == 0)
    109        {
    110            this.setAttackTarget((EntityLiving)par1Entity);
    111        }
    112
    113        super.collideWithEntity(par1Entity);
    114    }
    115
    116    /**
    117     * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
    118     * use this to react to sunlight and start to burn.
    119     */
    120    public void onLivingUpdate()
    121    {
    122        super.onLivingUpdate();
    123
    124        if (this.attackTimer > 0)
    125        {
    126            --this.attackTimer;
    127        }
    128
    129        if (this.holdRoseTick > 0)
    130        {
    131            --this.holdRoseTick;
    132        }
    133
    134        if (this.motionX * this.motionX + this.motionZ * this.motionZ > 2.500000277905201E-7D && this.rand.nextInt(5) == 0)
    135        {
    136            int var1 = MathHelper.floor_double(this.posX);
    137            int var2 = MathHelper.floor_double(this.posY - 0.20000000298023224D - (double)this.yOffset);
    138            int var3 = MathHelper.floor_double(this.posZ);
    139            int var4 = this.worldObj.getBlockId(var1, var2, var3);
    140
    141            if (var4 > 0)
    142            {
    143                this.worldObj.spawnParticle("tilecrack_" + var4 + "_" + this.worldObj.getBlockMetadata(var1, var2, var3), this.posX + ((double)this.rand.nextFloat() - 0.5D) * (double)this.width, this.boundingBox.minY + 0.1D, this.posZ + ((double)this.rand.nextFloat() - 0.5D) * (double)this.width, 4.0D * ((double)this.rand.nextFloat() - 0.5D), 0.5D, ((double)this.rand.nextFloat() - 0.5D) * 4.0D);
    144            }
    145        }
    146    }
    147
    148    /**
    149     * Returns true if this entity can attack entities of the specified class.
    150     */
    151    public boolean canAttackClass(Class par1Class)
    152    {
    153        return this.isPlayerCreated() && EntityPlayer.class.isAssignableFrom(par1Class) ? false : super.canAttackClass(par1Class);
    154    }
    155
    156    /**
    157     * (abstract) Protected helper method to write subclass entity data to NBT.
    158     */
    159    public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    160    {
    161        super.writeEntityToNBT(par1NBTTagCompound);
    162        par1NBTTagCompound.setBoolean("PlayerCreated", this.isPlayerCreated());
    163    }
    164
    165    /**
    166     * (abstract) Protected helper method to read subclass entity data from NBT.
    167     */
    168    public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    169    {
    170        super.readEntityFromNBT(par1NBTTagCompound);
    171        this.setPlayerCreated(par1NBTTagCompound.getBoolean("PlayerCreated"));
    172    }
    173
    174    public boolean attackEntityAsMob(Entity par1Entity)
    175    {
    176        this.attackTimer = 10;
    177        this.worldObj.setEntityState(this, (byte)4);
    178        boolean var2 = par1Entity.attackEntityFrom(DamageSource.causeMobDamage(this), 7 + this.rand.nextInt(15));
    179
    180        if (var2)
    181        {
    182            par1Entity.motionY += 0.4000000059604645D;
    183        }
    184
    185        this.playSound("mob.irongolem.throw", 1.0F, 1.0F);
    186        return var2;
    187    }
    188
    189    public Village getVillage()
    190    {
    191        return this.villageObj;
    192    }
    193
    194    @SideOnly(Side.CLIENT)
    195    public void handleHealthUpdate(byte par1)
    196    {
    197        if (par1 == 4)
    198        {
    199            this.attackTimer = 10;
    200            this.playSound("mob.irongolem.throw", 1.0F, 1.0F);
    201        }
    202        else if (par1 == 11)
    203        {
    204            this.holdRoseTick = 400;
    205        }
    206        else
    207        {
    208            super.handleHealthUpdate(par1);
    209        }
    210    }
    211
    212    @SideOnly(Side.CLIENT)
    213    public int getAttackTimer()
    214    {
    215        return this.attackTimer;
    216    }
    217
    218    public void setHoldingRose(boolean par1)
    219    {
    220        this.holdRoseTick = par1 ? 400 : 0;
    221        this.worldObj.setEntityState(this, (byte)11);
    222    }
    223
    224    /**
    225     * Returns the sound this mob makes while it's alive.
    226     */
    227    protected String getLivingSound()
    228    {
    229        return "none";
    230    }
    231
    232    /**
    233     * Returns the sound this mob makes when it is hurt.
    234     */
    235    protected String getHurtSound()
    236    {
    237        return "mob.irongolem.hit";
    238    }
    239
    240    /**
    241     * Returns the sound this mob makes on death.
    242     */
    243    protected String getDeathSound()
    244    {
    245        return "mob.irongolem.death";
    246    }
    247
    248    /**
    249     * Plays step sound at given x, y, z for the entity
    250     */
    251    protected void playStepSound(int par1, int par2, int par3, int par4)
    252    {
    253        this.playSound("mob.irongolem.walk", 1.0F, 1.0F);
    254    }
    255
    256    /**
    257     * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param
    258     * par2 - Level of Looting used to kill this mob.
    259     */
    260    protected void dropFewItems(boolean par1, int par2)
    261    {
    262        int var3 = this.rand.nextInt(3);
    263        int var4;
    264
    265        for (var4 = 0; var4 < var3; ++var4)
    266        {
    267            this.dropItem(Block.plantRed.blockID, 1);
    268        }
    269
    270        var4 = 3 + this.rand.nextInt(3);
    271
    272        for (int var5 = 0; var5 < var4; ++var5)
    273        {
    274            this.dropItem(Item.ingotIron.itemID, 1);
    275        }
    276    }
    277
    278    public int getHoldRoseTick()
    279    {
    280        return this.holdRoseTick;
    281    }
    282
    283    public boolean isPlayerCreated()
    284    {
    285        return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0;
    286    }
    287
    288    public void setPlayerCreated(boolean par1)
    289    {
    290        byte var2 = this.dataWatcher.getWatchableObjectByte(16);
    291
    292        if (par1)
    293        {
    294            this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 1)));
    295        }
    296        else
    297        {
    298            this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -2)));
    299        }
    300    }
    301
    302    /**
    303     * Called when the mob's health reaches 0.
    304     */
    305    public void onDeath(DamageSource par1DamageSource)
    306    {
    307        if (!this.isPlayerCreated() && this.attackingPlayer != null && this.villageObj != null)
    308        {
    309            this.villageObj.setReputationForPlayer(this.attackingPlayer.getCommandSenderName(), -5);
    310        }
    311
    312        super.onDeath(par1DamageSource);
    313    }
    314}