001 package net.minecraft.src;
002
003 import java.util.ArrayList;
004 import java.util.Iterator;
005 import java.util.List;
006
007 public class Village
008 {
009 private final World worldObj;
010
011 /** list of VillageDoorInfo objects */
012 private final List villageDoorInfoList = new ArrayList();
013
014 /**
015 * This is the sum of all door coordinates and used to calculate the actual village center by dividing by the number
016 * of doors.
017 */
018 private final ChunkCoordinates centerHelper = new ChunkCoordinates(0, 0, 0);
019
020 /** This is the actual village center. */
021 private final ChunkCoordinates center = new ChunkCoordinates(0, 0, 0);
022 private int villageRadius = 0;
023 private int lastAddDoorTimestamp = 0;
024 private int tickCounter = 0;
025 private int numVillagers = 0;
026 private List villageAgressors = new ArrayList();
027 private int numIronGolems = 0;
028
029 public Village(World par1World)
030 {
031 this.worldObj = par1World;
032 }
033
034 /**
035 * Called periodically by VillageCollection
036 */
037 public void tick(int par1)
038 {
039 this.tickCounter = par1;
040 this.removeDeadAndOutOfRangeDoors();
041 this.removeDeadAndOldAgressors();
042
043 if (par1 % 20 == 0)
044 {
045 this.updateNumVillagers();
046 }
047
048 if (par1 % 30 == 0)
049 {
050 this.updateNumIronGolems();
051 }
052
053 int var2 = this.numVillagers / 16;
054
055 if (this.numIronGolems < var2 && this.villageDoorInfoList.size() > 20 && this.worldObj.rand.nextInt(7000) == 0)
056 {
057 Vec3 var3 = this.tryGetIronGolemSpawningLocation(MathHelper.floor_float((float)this.center.posX), MathHelper.floor_float((float)this.center.posY), MathHelper.floor_float((float)this.center.posZ), 2, 4, 2);
058
059 if (var3 != null)
060 {
061 EntityIronGolem var4 = new EntityIronGolem(this.worldObj);
062 var4.setPosition(var3.xCoord, var3.yCoord, var3.zCoord);
063 this.worldObj.spawnEntityInWorld(var4);
064 ++this.numIronGolems;
065 }
066 }
067 }
068
069 /**
070 * Tries up to 10 times to get a valid spawning location before eventually failing and returning null.
071 */
072 private Vec3 tryGetIronGolemSpawningLocation(int par1, int par2, int par3, int par4, int par5, int par6)
073 {
074 for (int var7 = 0; var7 < 10; ++var7)
075 {
076 int var8 = par1 + this.worldObj.rand.nextInt(16) - 8;
077 int var9 = par2 + this.worldObj.rand.nextInt(6) - 3;
078 int var10 = par3 + this.worldObj.rand.nextInt(16) - 8;
079
080 if (this.isInRange(var8, var9, var10) && this.isValidIronGolemSpawningLocation(var8, var9, var10, par4, par5, par6))
081 {
082 return Vec3.getVec3Pool().getVecFromPool((double)var8, (double)var9, (double)var10);
083 }
084 }
085
086 return null;
087 }
088
089 private boolean isValidIronGolemSpawningLocation(int par1, int par2, int par3, int par4, int par5, int par6)
090 {
091 if (!this.worldObj.doesBlockHaveSolidTopSurface(par1, par2 - 1, par3))
092 {
093 return false;
094 }
095 else
096 {
097 int var7 = par1 - par4 / 2;
098 int var8 = par3 - par6 / 2;
099
100 for (int var9 = var7; var9 < var7 + par4; ++var9)
101 {
102 for (int var10 = par2; var10 < par2 + par5; ++var10)
103 {
104 for (int var11 = var8; var11 < var8 + par6; ++var11)
105 {
106 if (this.worldObj.isBlockNormalCube(var9, var10, var11))
107 {
108 return false;
109 }
110 }
111 }
112 }
113
114 return true;
115 }
116 }
117
118 private void updateNumIronGolems()
119 {
120 List var1 = this.worldObj.getEntitiesWithinAABB(EntityIronGolem.class, AxisAlignedBB.getAABBPool().addOrModifyAABBInPool((double)(this.center.posX - this.villageRadius), (double)(this.center.posY - 4), (double)(this.center.posZ - this.villageRadius), (double)(this.center.posX + this.villageRadius), (double)(this.center.posY + 4), (double)(this.center.posZ + this.villageRadius)));
121 this.numIronGolems = var1.size();
122 }
123
124 private void updateNumVillagers()
125 {
126 List var1 = this.worldObj.getEntitiesWithinAABB(EntityVillager.class, AxisAlignedBB.getAABBPool().addOrModifyAABBInPool((double)(this.center.posX - this.villageRadius), (double)(this.center.posY - 4), (double)(this.center.posZ - this.villageRadius), (double)(this.center.posX + this.villageRadius), (double)(this.center.posY + 4), (double)(this.center.posZ + this.villageRadius)));
127 this.numVillagers = var1.size();
128 }
129
130 public ChunkCoordinates getCenter()
131 {
132 return this.center;
133 }
134
135 public int getVillageRadius()
136 {
137 return this.villageRadius;
138 }
139
140 /**
141 * Actually get num village door info entries, but that boils down to number of doors. Called by
142 * EntityAIVillagerMate and VillageSiege
143 */
144 public int getNumVillageDoors()
145 {
146 return this.villageDoorInfoList.size();
147 }
148
149 public int getTicksSinceLastDoorAdding()
150 {
151 return this.tickCounter - this.lastAddDoorTimestamp;
152 }
153
154 public int getNumVillagers()
155 {
156 return this.numVillagers;
157 }
158
159 /**
160 * Returns true, if the given coordinates are within the bounding box of the village.
161 */
162 public boolean isInRange(int par1, int par2, int par3)
163 {
164 return this.center.getDistanceSquared(par1, par2, par3) < (float)(this.villageRadius * this.villageRadius);
165 }
166
167 /**
168 * called only by class EntityAIMoveThroughVillage
169 */
170 public List getVillageDoorInfoList()
171 {
172 return this.villageDoorInfoList;
173 }
174
175 public VillageDoorInfo findNearestDoor(int par1, int par2, int par3)
176 {
177 VillageDoorInfo var4 = null;
178 int var5 = Integer.MAX_VALUE;
179 Iterator var6 = this.villageDoorInfoList.iterator();
180
181 while (var6.hasNext())
182 {
183 VillageDoorInfo var7 = (VillageDoorInfo)var6.next();
184 int var8 = var7.getDistanceSquared(par1, par2, par3);
185
186 if (var8 < var5)
187 {
188 var4 = var7;
189 var5 = var8;
190 }
191 }
192
193 return var4;
194 }
195
196 /**
197 * Find a door suitable for shelter. If there are more doors in a distance of 16 blocks, then the least restricted
198 * one (i.e. the one protecting the lowest number of villagers) of them is chosen, else the nearest one regardless
199 * of restriction.
200 */
201 public VillageDoorInfo findNearestDoorUnrestricted(int par1, int par2, int par3)
202 {
203 VillageDoorInfo var4 = null;
204 int var5 = Integer.MAX_VALUE;
205 Iterator var6 = this.villageDoorInfoList.iterator();
206
207 while (var6.hasNext())
208 {
209 VillageDoorInfo var7 = (VillageDoorInfo)var6.next();
210 int var8 = var7.getDistanceSquared(par1, par2, par3);
211
212 if (var8 > 256)
213 {
214 var8 *= 1000;
215 }
216 else
217 {
218 var8 = var7.getDoorOpeningRestrictionCounter();
219 }
220
221 if (var8 < var5)
222 {
223 var4 = var7;
224 var5 = var8;
225 }
226 }
227
228 return var4;
229 }
230
231 public VillageDoorInfo getVillageDoorAt(int par1, int par2, int par3)
232 {
233 if (this.center.getDistanceSquared(par1, par2, par3) > (float)(this.villageRadius * this.villageRadius))
234 {
235 return null;
236 }
237 else
238 {
239 Iterator var4 = this.villageDoorInfoList.iterator();
240 VillageDoorInfo var5;
241
242 do
243 {
244 if (!var4.hasNext())
245 {
246 return null;
247 }
248
249 var5 = (VillageDoorInfo)var4.next();
250 }
251 while (var5.posX != par1 || var5.posZ != par3 || Math.abs(var5.posY - par2) > 1);
252
253 return var5;
254 }
255 }
256
257 public void addVillageDoorInfo(VillageDoorInfo par1VillageDoorInfo)
258 {
259 this.villageDoorInfoList.add(par1VillageDoorInfo);
260 this.centerHelper.posX += par1VillageDoorInfo.posX;
261 this.centerHelper.posY += par1VillageDoorInfo.posY;
262 this.centerHelper.posZ += par1VillageDoorInfo.posZ;
263 this.updateVillageRadiusAndCenter();
264 this.lastAddDoorTimestamp = par1VillageDoorInfo.lastActivityTimestamp;
265 }
266
267 /**
268 * Returns true, if there is not a single village door left. Called by VillageCollection
269 */
270 public boolean isAnnihilated()
271 {
272 return this.villageDoorInfoList.isEmpty();
273 }
274
275 public void addOrRenewAgressor(EntityLiving par1EntityLiving)
276 {
277 Iterator var2 = this.villageAgressors.iterator();
278 VillageAgressor var3;
279
280 do
281 {
282 if (!var2.hasNext())
283 {
284 this.villageAgressors.add(new VillageAgressor(this, par1EntityLiving, this.tickCounter));
285 return;
286 }
287
288 var3 = (VillageAgressor)var2.next();
289 }
290 while (var3.agressor != par1EntityLiving);
291
292 var3.agressionTime = this.tickCounter;
293 }
294
295 public EntityLiving findNearestVillageAggressor(EntityLiving par1EntityLiving)
296 {
297 double var2 = Double.MAX_VALUE;
298 VillageAgressor var4 = null;
299 Iterator var5 = this.villageAgressors.iterator();
300
301 while (var5.hasNext())
302 {
303 VillageAgressor var6 = (VillageAgressor)var5.next();
304 double var7 = var6.agressor.getDistanceSqToEntity(par1EntityLiving);
305
306 if (var7 <= var2)
307 {
308 var4 = var6;
309 var2 = var7;
310 }
311 }
312
313 return var4 != null ? var4.agressor : null;
314 }
315
316 private void removeDeadAndOldAgressors()
317 {
318 Iterator var1 = this.villageAgressors.iterator();
319
320 while (var1.hasNext())
321 {
322 VillageAgressor var2 = (VillageAgressor)var1.next();
323
324 if (!var2.agressor.isEntityAlive() || Math.abs(this.tickCounter - var2.agressionTime) > 300)
325 {
326 var1.remove();
327 }
328 }
329 }
330
331 private void removeDeadAndOutOfRangeDoors()
332 {
333 boolean var1 = false;
334 boolean var2 = this.worldObj.rand.nextInt(50) == 0;
335 Iterator var3 = this.villageDoorInfoList.iterator();
336
337 while (var3.hasNext())
338 {
339 VillageDoorInfo var4 = (VillageDoorInfo)var3.next();
340
341 if (var2)
342 {
343 var4.resetDoorOpeningRestrictionCounter();
344 }
345
346 if (!this.isBlockDoor(var4.posX, var4.posY, var4.posZ) || Math.abs(this.tickCounter - var4.lastActivityTimestamp) > 1200)
347 {
348 this.centerHelper.posX -= var4.posX;
349 this.centerHelper.posY -= var4.posY;
350 this.centerHelper.posZ -= var4.posZ;
351 var1 = true;
352 var4.isDetachedFromVillageFlag = true;
353 var3.remove();
354 }
355 }
356
357 if (var1)
358 {
359 this.updateVillageRadiusAndCenter();
360 }
361 }
362
363 private boolean isBlockDoor(int par1, int par2, int par3)
364 {
365 int var4 = this.worldObj.getBlockId(par1, par2, par3);
366 return var4 <= 0 ? false : var4 == Block.doorWood.blockID;
367 }
368
369 private void updateVillageRadiusAndCenter()
370 {
371 int var1 = this.villageDoorInfoList.size();
372
373 if (var1 == 0)
374 {
375 this.center.set(0, 0, 0);
376 this.villageRadius = 0;
377 }
378 else
379 {
380 this.center.set(this.centerHelper.posX / var1, this.centerHelper.posY / var1, this.centerHelper.posZ / var1);
381 int var2 = 0;
382 VillageDoorInfo var4;
383
384 for (Iterator var3 = this.villageDoorInfoList.iterator(); var3.hasNext(); var2 = Math.max(var4.getDistanceSquared(this.center.posX, this.center.posY, this.center.posZ), var2))
385 {
386 var4 = (VillageDoorInfo)var3.next();
387 }
388
389 this.villageRadius = Math.max(32, (int)Math.sqrt((double)var2) + 1);
390 }
391 }
392 }