001package net.minecraft.entity;
002
003import net.minecraft.pathfinding.PathEntity;
004import net.minecraft.util.MathHelper;
005import net.minecraft.util.Vec3;
006import net.minecraft.world.World;
007
008public abstract class EntityCreature extends EntityLiving
009{
010    private PathEntity pathToEntity;
011
012    /** The Entity this EntityCreature is set to attack. */
013    protected Entity entityToAttack;
014
015    /**
016     * returns true if a creature has attacked recently only used for creepers and skeletons
017     */
018    protected boolean hasAttacked = false;
019
020    /** Used to make a creature speed up and wander away when hit. */
021    protected int fleeingTick = 0;
022
023    public EntityCreature(World par1World)
024    {
025        super(par1World);
026    }
027
028    /**
029     * Disables a mob's ability to move on its own while true.
030     */
031    protected boolean isMovementCeased()
032    {
033        return false;
034    }
035
036    protected void updateEntityActionState()
037    {
038        this.worldObj.theProfiler.startSection("ai");
039
040        if (this.fleeingTick > 0)
041        {
042            --this.fleeingTick;
043        }
044
045        this.hasAttacked = this.isMovementCeased();
046        float var1 = 16.0F;
047
048        if (this.entityToAttack == null)
049        {
050            this.entityToAttack = this.findPlayerToAttack();
051
052            if (this.entityToAttack != null)
053            {
054                this.pathToEntity = this.worldObj.getPathEntityToEntity(this, this.entityToAttack, var1, true, false, false, true);
055            }
056        }
057        else if (this.entityToAttack.isEntityAlive())
058        {
059            float var2 = this.entityToAttack.getDistanceToEntity(this);
060
061            if (this.canEntityBeSeen(this.entityToAttack))
062            {
063                this.attackEntity(this.entityToAttack, var2);
064            }
065        }
066        else
067        {
068            this.entityToAttack = null;
069        }
070
071        this.worldObj.theProfiler.endSection();
072
073        if (!this.hasAttacked && this.entityToAttack != null && (this.pathToEntity == null || this.rand.nextInt(20) == 0))
074        {
075            this.pathToEntity = this.worldObj.getPathEntityToEntity(this, this.entityToAttack, var1, true, false, false, true);
076        }
077        else if (!this.hasAttacked && (this.pathToEntity == null && this.rand.nextInt(180) == 0 || this.rand.nextInt(120) == 0 || this.fleeingTick > 0) && this.entityAge < 100)
078        {
079            this.updateWanderPath();
080        }
081
082        int var21 = MathHelper.floor_double(this.boundingBox.minY + 0.5D);
083        boolean var3 = this.isInWater();
084        boolean var4 = this.handleLavaMovement();
085        this.rotationPitch = 0.0F;
086
087        if (this.pathToEntity != null && this.rand.nextInt(100) != 0)
088        {
089            this.worldObj.theProfiler.startSection("followpath");
090            Vec3 var5 = this.pathToEntity.getPosition(this);
091            double var6 = (double)(this.width * 2.0F);
092
093            while (var5 != null && var5.squareDistanceTo(this.posX, var5.yCoord, this.posZ) < var6 * var6)
094            {
095                this.pathToEntity.incrementPathIndex();
096
097                if (this.pathToEntity.isFinished())
098                {
099                    var5 = null;
100                    this.pathToEntity = null;
101                }
102                else
103                {
104                    var5 = this.pathToEntity.getPosition(this);
105                }
106            }
107
108            this.isJumping = false;
109
110            if (var5 != null)
111            {
112                double var8 = var5.xCoord - this.posX;
113                double var10 = var5.zCoord - this.posZ;
114                double var12 = var5.yCoord - (double)var21;
115                float var14 = (float)(Math.atan2(var10, var8) * 180.0D / Math.PI) - 90.0F;
116                float var15 = MathHelper.wrapAngleTo180_float(var14 - this.rotationYaw);
117                this.moveForward = this.moveSpeed;
118
119                if (var15 > 30.0F)
120                {
121                    var15 = 30.0F;
122                }
123
124                if (var15 < -30.0F)
125                {
126                    var15 = -30.0F;
127                }
128
129                this.rotationYaw += var15;
130
131                if (this.hasAttacked && this.entityToAttack != null)
132                {
133                    double var16 = this.entityToAttack.posX - this.posX;
134                    double var18 = this.entityToAttack.posZ - this.posZ;
135                    float var20 = this.rotationYaw;
136                    this.rotationYaw = (float)(Math.atan2(var18, var16) * 180.0D / Math.PI) - 90.0F;
137                    var15 = (var20 - this.rotationYaw + 90.0F) * (float)Math.PI / 180.0F;
138                    this.moveStrafing = -MathHelper.sin(var15) * this.moveForward * 1.0F;
139                    this.moveForward = MathHelper.cos(var15) * this.moveForward * 1.0F;
140                }
141
142                if (var12 > 0.0D)
143                {
144                    this.isJumping = true;
145                }
146            }
147
148            if (this.entityToAttack != null)
149            {
150                this.faceEntity(this.entityToAttack, 30.0F, 30.0F);
151            }
152
153            if (this.isCollidedHorizontally && !this.hasPath())
154            {
155                this.isJumping = true;
156            }
157
158            if (this.rand.nextFloat() < 0.8F && (var3 || var4))
159            {
160                this.isJumping = true;
161            }
162
163            this.worldObj.theProfiler.endSection();
164        }
165        else
166        {
167            super.updateEntityActionState();
168            this.pathToEntity = null;
169        }
170    }
171
172    /**
173     * Time remaining during which the Animal is sped up and flees.
174     */
175    protected void updateWanderPath()
176    {
177        this.worldObj.theProfiler.startSection("stroll");
178        boolean var1 = false;
179        int var2 = -1;
180        int var3 = -1;
181        int var4 = -1;
182        float var5 = -99999.0F;
183
184        for (int var6 = 0; var6 < 10; ++var6)
185        {
186            int var7 = MathHelper.floor_double(this.posX + (double)this.rand.nextInt(13) - 6.0D);
187            int var8 = MathHelper.floor_double(this.posY + (double)this.rand.nextInt(7) - 3.0D);
188            int var9 = MathHelper.floor_double(this.posZ + (double)this.rand.nextInt(13) - 6.0D);
189            float var10 = this.getBlockPathWeight(var7, var8, var9);
190
191            if (var10 > var5)
192            {
193                var5 = var10;
194                var2 = var7;
195                var3 = var8;
196                var4 = var9;
197                var1 = true;
198            }
199        }
200
201        if (var1)
202        {
203            this.pathToEntity = this.worldObj.getEntityPathToXYZ(this, var2, var3, var4, 10.0F, true, false, false, true);
204        }
205
206        this.worldObj.theProfiler.endSection();
207    }
208
209    /**
210     * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack.
211     */
212    protected void attackEntity(Entity par1Entity, float par2) {}
213
214    /**
215     * Takes a coordinate in and returns a weight to determine how likely this creature will try to path to the block.
216     * Args: x, y, z
217     */
218    public float getBlockPathWeight(int par1, int par2, int par3)
219    {
220        return 0.0F;
221    }
222
223    /**
224     * Finds the closest player within 16 blocks to attack, or null if this Entity isn't interested in attacking
225     * (Animals, Spiders at day, peaceful PigZombies).
226     */
227    protected Entity findPlayerToAttack()
228    {
229        return null;
230    }
231
232    /**
233     * Checks if the entity's current position is a valid location to spawn this entity.
234     */
235    public boolean getCanSpawnHere()
236    {
237        int var1 = MathHelper.floor_double(this.posX);
238        int var2 = MathHelper.floor_double(this.boundingBox.minY);
239        int var3 = MathHelper.floor_double(this.posZ);
240        return super.getCanSpawnHere() && this.getBlockPathWeight(var1, var2, var3) >= 0.0F;
241    }
242
243    /**
244     * Returns true if entity has a path to follow
245     */
246    public boolean hasPath()
247    {
248        return this.pathToEntity != null;
249    }
250
251    /**
252     * sets the Entities walk path in EntityCreature
253     */
254    public void setPathToEntity(PathEntity par1PathEntity)
255    {
256        this.pathToEntity = par1PathEntity;
257    }
258
259    /**
260     * Returns current entities target
261     */
262    public Entity getEntityToAttack()
263    {
264        return this.entityToAttack;
265    }
266
267    /**
268     * Sets the entity which is to be attacked.
269     */
270    public void setTarget(Entity par1Entity)
271    {
272        this.entityToAttack = par1Entity;
273    }
274
275    /**
276     * This method returns a value to be applied directly to entity speed, this factor is less than 1 when a slowdown
277     * potion effect is applied, more than 1 when a haste potion effect is applied and 2 for fleeing entities.
278     */
279    public float getSpeedModifier()
280    {
281        float var1 = super.getSpeedModifier();
282
283        if (this.fleeingTick > 0 && !this.isAIEnabled())
284        {
285            var1 *= 2.0F;
286        }
287
288        return var1;
289    }
290}