001 package net.minecraft.world;
002
003 import java.util.ArrayList;
004 import java.util.Collections;
005 import java.util.HashMap;
006 import java.util.Iterator;
007 import java.util.List;
008 import java.util.Random;
009 import net.minecraft.block.Block;
010 import net.minecraft.block.material.Material;
011 import net.minecraft.entity.EntityLiving;
012 import net.minecraft.entity.EnumCreatureType;
013 import net.minecraft.entity.monster.EntitySkeleton;
014 import net.minecraft.entity.monster.EntitySpider;
015 import net.minecraft.entity.monster.EntityZombie;
016 import net.minecraft.entity.player.EntityPlayer;
017 import net.minecraft.util.ChunkCoordinates;
018 import net.minecraft.util.MathHelper;
019 import net.minecraft.util.WeightedRandom;
020 import net.minecraft.world.biome.BiomeGenBase;
021 import net.minecraft.world.biome.SpawnListEntry;
022 import net.minecraft.world.chunk.Chunk;
023
024 import net.minecraftforge.common.MinecraftForge;
025 import net.minecraftforge.event.entity.living.LivingSpecialSpawnEvent;
026
027 public final class SpawnerAnimals
028 {
029 /** The 17x17 area around the player where mobs can spawn */
030 private static HashMap eligibleChunksForSpawning = new HashMap();
031
032 /** An array of entity classes that spawn at night. */
033 protected static final Class[] nightSpawnEntities = new Class[] {EntitySpider.class, EntityZombie.class, EntitySkeleton.class};
034
035 /**
036 * Given a chunk, find a random position in it.
037 */
038 protected static ChunkPosition getRandomSpawningPointInChunk(World par0World, int par1, int par2)
039 {
040 Chunk var3 = par0World.getChunkFromChunkCoords(par1, par2);
041 int var4 = par1 * 16 + par0World.rand.nextInt(16);
042 int var5 = par2 * 16 + par0World.rand.nextInt(16);
043 int var6 = par0World.rand.nextInt(var3 == null ? par0World.getActualHeight() : var3.getTopFilledSegment() + 16 - 1);
044 return new ChunkPosition(var4, var6, var5);
045 }
046
047 /**
048 * adds all chunks within the spawn radius of the players to eligibleChunksForSpawning. pars: the world,
049 * hostileCreatures, passiveCreatures. returns number of eligible chunks.
050 */
051 public static final int findChunksForSpawning(WorldServer par0WorldServer, boolean par1, boolean par2, boolean par3)
052 {
053 if (!par1 && !par2)
054 {
055 return 0;
056 }
057 else
058 {
059 eligibleChunksForSpawning.clear();
060 int var4;
061 int var7;
062
063 for (var4 = 0; var4 < par0WorldServer.playerEntities.size(); ++var4)
064 {
065 EntityPlayer var5 = (EntityPlayer)par0WorldServer.playerEntities.get(var4);
066 int var6 = MathHelper.floor_double(var5.posX / 16.0D);
067 var7 = MathHelper.floor_double(var5.posZ / 16.0D);
068 byte var8 = 8;
069
070 for (int var9 = -var8; var9 <= var8; ++var9)
071 {
072 for (int var10 = -var8; var10 <= var8; ++var10)
073 {
074 boolean var11 = var9 == -var8 || var9 == var8 || var10 == -var8 || var10 == var8;
075 ChunkCoordIntPair var12 = new ChunkCoordIntPair(var9 + var6, var10 + var7);
076
077 if (!var11)
078 {
079 eligibleChunksForSpawning.put(var12, Boolean.valueOf(false));
080 }
081 else if (!eligibleChunksForSpawning.containsKey(var12))
082 {
083 eligibleChunksForSpawning.put(var12, Boolean.valueOf(true));
084 }
085 }
086 }
087 }
088
089 var4 = 0;
090 ChunkCoordinates var32 = par0WorldServer.getSpawnPoint();
091 EnumCreatureType[] var33 = EnumCreatureType.values();
092 var7 = var33.length;
093
094 for (int var34 = 0; var34 < var7; ++var34)
095 {
096 EnumCreatureType var35 = var33[var34];
097
098 if ((!var35.getPeacefulCreature() || par2) && (var35.getPeacefulCreature() || par1) && (!var35.getAnimal() || par3) && par0WorldServer.countEntities(var35.getCreatureClass()) <= var35.getMaxNumberOfCreature() * eligibleChunksForSpawning.size() / 256)
099 {
100 Iterator var37 = eligibleChunksForSpawning.keySet().iterator();
101 ArrayList<ChunkCoordIntPair> tmp = new ArrayList(eligibleChunksForSpawning.keySet());
102 Collections.shuffle(tmp);
103 var37 = tmp.iterator();
104 label110:
105
106 while (var37.hasNext())
107 {
108 ChunkCoordIntPair var36 = (ChunkCoordIntPair)var37.next();
109
110 if (!((Boolean)eligibleChunksForSpawning.get(var36)).booleanValue())
111 {
112 ChunkPosition var38 = getRandomSpawningPointInChunk(par0WorldServer, var36.chunkXPos, var36.chunkZPos);
113 int var13 = var38.x;
114 int var14 = var38.y;
115 int var15 = var38.z;
116
117 if (!par0WorldServer.isBlockNormalCube(var13, var14, var15) && par0WorldServer.getBlockMaterial(var13, var14, var15) == var35.getCreatureMaterial())
118 {
119 int var16 = 0;
120 int var17 = 0;
121
122 while (var17 < 3)
123 {
124 int var18 = var13;
125 int var19 = var14;
126 int var20 = var15;
127 byte var21 = 6;
128 SpawnListEntry var22 = null;
129 int var23 = 0;
130
131 while (true)
132 {
133 if (var23 < 4)
134 {
135 label103:
136 {
137 var18 += par0WorldServer.rand.nextInt(var21) - par0WorldServer.rand.nextInt(var21);
138 var19 += par0WorldServer.rand.nextInt(1) - par0WorldServer.rand.nextInt(1);
139 var20 += par0WorldServer.rand.nextInt(var21) - par0WorldServer.rand.nextInt(var21);
140
141 if (canCreatureTypeSpawnAtLocation(var35, par0WorldServer, var18, var19, var20))
142 {
143 float var24 = (float)var18 + 0.5F;
144 float var25 = (float)var19;
145 float var26 = (float)var20 + 0.5F;
146
147 if (par0WorldServer.getClosestPlayer((double)var24, (double)var25, (double)var26, 24.0D) == null)
148 {
149 float var27 = var24 - (float)var32.posX;
150 float var28 = var25 - (float)var32.posY;
151 float var29 = var26 - (float)var32.posZ;
152 float var30 = var27 * var27 + var28 * var28 + var29 * var29;
153
154 if (var30 >= 576.0F)
155 {
156 if (var22 == null)
157 {
158 var22 = par0WorldServer.spawnRandomCreature(var35, var18, var19, var20);
159
160 if (var22 == null)
161 {
162 break label103;
163 }
164 }
165
166 EntityLiving var39;
167
168 try
169 {
170 var39 = (EntityLiving)var22.entityClass.getConstructor(new Class[] {World.class}).newInstance(new Object[] {par0WorldServer});
171 }
172 catch (Exception var31)
173 {
174 var31.printStackTrace();
175 return var4;
176 }
177
178 var39.setLocationAndAngles((double)var24, (double)var25, (double)var26, par0WorldServer.rand.nextFloat() * 360.0F, 0.0F);
179
180 if (var39.getCanSpawnHere())
181 {
182 ++var16;
183 par0WorldServer.spawnEntityInWorld(var39);
184 creatureSpecificInit(var39, par0WorldServer, var24, var25, var26);
185
186 if (var16 >= var39.getMaxSpawnedInChunk())
187 {
188 continue label110;
189 }
190 }
191
192 var4 += var16;
193 }
194 }
195 }
196
197 ++var23;
198 continue;
199 }
200 }
201
202 ++var17;
203 break;
204 }
205 }
206 }
207 }
208 }
209 }
210 }
211
212 return var4;
213 }
214 }
215
216 /**
217 * Returns whether or not the specified creature type can spawn at the specified location.
218 */
219 public static boolean canCreatureTypeSpawnAtLocation(EnumCreatureType par0EnumCreatureType, World par1World, int par2, int par3, int par4)
220 {
221 if (par0EnumCreatureType.getCreatureMaterial() == Material.water)
222 {
223 return par1World.getBlockMaterial(par2, par3, par4).isLiquid() && par1World.getBlockMaterial(par2, par3 - 1, par4).isLiquid() && !par1World.isBlockNormalCube(par2, par3 + 1, par4);
224 }
225 else if (!par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4))
226 {
227 return false;
228 }
229 else
230 {
231 int var5 = par1World.getBlockId(par2, par3 - 1, par4);
232 boolean spawnBlock = (Block.blocksList[var5] != null && Block.blocksList[var5].canCreatureSpawn(par0EnumCreatureType, par1World, par2, par3 - 1, par4));
233 return spawnBlock && var5 != Block.bedrock.blockID && !par1World.isBlockNormalCube(par2, par3, par4) && !par1World.getBlockMaterial(par2, par3, par4).isLiquid() && !par1World.isBlockNormalCube(par2, par3 + 1, par4);
234 }
235 }
236
237 /**
238 * determines if a skeleton spawns on a spider, and if a sheep is a different color
239 */
240 private static void creatureSpecificInit(EntityLiving par0EntityLiving, World par1World, float par2, float par3, float par4)
241 {
242 if (MinecraftForge.EVENT_BUS.post(new LivingSpecialSpawnEvent(par0EntityLiving, par1World, par2, par3, par4)))
243 {
244 return;
245 }
246
247 par0EntityLiving.initCreature();
248 }
249
250 /**
251 * Called during chunk generation to spawn initial creatures.
252 */
253 public static void performWorldGenSpawning(World par0World, BiomeGenBase par1BiomeGenBase, int par2, int par3, int par4, int par5, Random par6Random)
254 {
255 List var7 = par1BiomeGenBase.getSpawnableList(EnumCreatureType.creature);
256
257 if (!var7.isEmpty())
258 {
259 while (par6Random.nextFloat() < par1BiomeGenBase.getSpawningChance())
260 {
261 SpawnListEntry var8 = (SpawnListEntry)WeightedRandom.getRandomItem(par0World.rand, var7);
262 int var9 = var8.minGroupCount + par6Random.nextInt(1 + var8.maxGroupCount - var8.minGroupCount);
263 int var10 = par2 + par6Random.nextInt(par4);
264 int var11 = par3 + par6Random.nextInt(par5);
265 int var12 = var10;
266 int var13 = var11;
267
268 for (int var14 = 0; var14 < var9; ++var14)
269 {
270 boolean var15 = false;
271
272 for (int var16 = 0; !var15 && var16 < 4; ++var16)
273 {
274 int var17 = par0World.getTopSolidOrLiquidBlock(var10, var11);
275
276 if (canCreatureTypeSpawnAtLocation(EnumCreatureType.creature, par0World, var10, var17, var11))
277 {
278 float var18 = (float)var10 + 0.5F;
279 float var19 = (float)var17;
280 float var20 = (float)var11 + 0.5F;
281 EntityLiving var21;
282
283 try
284 {
285 var21 = (EntityLiving)var8.entityClass.getConstructor(new Class[] {World.class}).newInstance(new Object[] {par0World});
286 }
287 catch (Exception var23)
288 {
289 var23.printStackTrace();
290 continue;
291 }
292
293 var21.setLocationAndAngles((double)var18, (double)var19, (double)var20, par6Random.nextFloat() * 360.0F, 0.0F);
294 par0World.spawnEntityInWorld(var21);
295 creatureSpecificInit(var21, par0World, var18, var19, var20);
296 var15 = true;
297 }
298
299 var10 += par6Random.nextInt(5) - par6Random.nextInt(5);
300
301 for (var11 += par6Random.nextInt(5) - par6Random.nextInt(5); var10 < par2 || var10 >= par2 + par4 || var11 < par3 || var11 >= par3 + par4; var11 = var13 + par6Random.nextInt(5) - par6Random.nextInt(5))
302 {
303 var10 = var12 + par6Random.nextInt(5) - par6Random.nextInt(5);
304 }
305 }
306 }
307 }
308 }
309 }
310 }