001 package net.minecraft.entity;
002
003 import net.minecraft.pathfinding.PathEntity;
004 import net.minecraft.util.MathHelper;
005 import net.minecraft.util.Vec3;
006 import net.minecraft.world.World;
007
008 public 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 }