    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}