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