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