#include "stdafx.h"
#include "net.minecraft.h"
#include "net.minecraft.world.entity.h"
#include "net.minecraft.world.entity.animal.h"
#include "net.minecraft.world.entity.monster.h"
#include "net.minecraft.world.entity.player.h"
#include "net.minecraft.world.level.h"
#include "net.minecraft.world.level.biome.h"
#include "net.minecraft.world.level.material.h"
#include "net.minecraft.world.level.pathfinder.h"
#include "net.minecraft.world.level.tile.h"
#include "Difficulty.h"
#include "WeighedRandom.h"
#include "Level.h"
#include "ChunkPos.h"
#include "TilePos.h"
#include "..\Minecraft.Client\ServerLevel.h"
#include "MobSpawner.h"
#include "Dimension.h"

const int MobSpawner::MIN_SPAWN_DISTANCE = 24;

TilePos MobSpawner::getRandomPosWithin(Level *level, int cx, int cz)
{
	// 4J Stu - Added 1.2.3 but we don't need it as it was only used to access sections
	// Leaving here though to help explain why chunk coords are not passed in rather than full coords
	//LevelChunk *chunk = level->getChunk(cx, cz);
	int x = cx * 16 + level->random->nextInt(16);
	int y = level->random->nextInt(level->getHeight());
	int z = cz * 16 + level->random->nextInt(16);

	return TilePos(x, y, z);
}

#ifdef __PSVITA__
	// AP - See CustomMap.h for an explanation of this
	CustomMap MobSpawner::chunksToPoll;
#else
	unordered_map<ChunkPos,bool,ChunkPosKeyHash,ChunkPosKeyEq> MobSpawner::chunksToPoll;
#endif

const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFriendlies)
{
#ifndef _CONTENT_PACKAGE

#if 0
	// PIX output for mob counters - generally disabling as Entity::countFlagsForPIX is reasonably expensive
	if( level->dimension->id == 0 )
	{
		Entity::countFlagsForPIX();
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_WATERANIMAL    ,false), "eTYPE_WATERANIMAL");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_ANIMALS_SPAWN_LIMIT_CHECK			,false), "eTYPE_ANIMAL");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_MONSTER	    ,false), "eTYPE_MONSTER");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_SQUID		  	,true ), "eTYPE_SQUID");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_VILLAGER		  	,true ), "eTYPE_VILLAGER");

		unsigned int totalCount[4];
		unsigned int protectedCount[4];
		unsigned int unprotectedCount[4];
		unsigned int couldWanderCount[4];

		totalCount[0] = level->countInstanceOf(eTYPE_COW         	,true, &protectedCount[0], &couldWanderCount[0] );
		totalCount[1] = level->countInstanceOf(eTYPE_SHEEP       	,true, &protectedCount[1], &couldWanderCount[1] );
		totalCount[2] = level->countInstanceOf(eTYPE_CHICKEN     	,true, &protectedCount[2], &couldWanderCount[2] );
		totalCount[3] = level->countInstanceOf(eTYPE_PIG         	,true, &protectedCount[3], &couldWanderCount[3] );

		for( int i = 0; i < 4; i++ ) unprotectedCount[i] = totalCount[i] - protectedCount[i];

		PIXAddNamedCounter( unprotectedCount[0], "eTYPE_COW (unprotected)");
		PIXAddNamedCounter( unprotectedCount[1], "eTYPE_SHEEP (unprotected)");
		PIXAddNamedCounter( unprotectedCount[2], "eTYPE_CHICKEN (unprotected)");
		PIXAddNamedCounter( unprotectedCount[3], "eTYPE_PIG (unprotected)");

		PIXAddNamedCounter( protectedCount[0], "eTYPE_COW (protected)");
		PIXAddNamedCounter( protectedCount[1], "eTYPE_SHEEP (protected)");
		PIXAddNamedCounter( protectedCount[2], "eTYPE_CHICKEN (protected)");
		PIXAddNamedCounter( protectedCount[3], "eTYPE_PIG (protected)");

		PIXAddNamedCounter( couldWanderCount[0], "eTYPE_COW (could wander)");
		PIXAddNamedCounter( couldWanderCount[1], "eTYPE_SHEEP (could wander)");
		PIXAddNamedCounter( couldWanderCount[2], "eTYPE_CHICKEN (could wander)");
		PIXAddNamedCounter( couldWanderCount[3], "eTYPE_PIG (could wander)");

		PIXAddNamedCounter( level->countInstanceOf(eTYPE_WOLF        	,true ), "eTYPE_WOLF");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_CREEPER     	,true ), "eTYPE_CREEPER");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_GIANT       	,true ), "eTYPE_GIANT");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_SKELETON    	,true ), "eTYPE_SKELETON");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_SPIDER      	,true ), "eTYPE_SPIDER");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_ZOMBIE      	,true ), "eTYPE_ZOMBIE");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_PIGZOMBIE   	,true ), "eTYPE_PIGZOMBIE");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_SLIME   		,true ), "eTYPE_SLIME");
		PIXAddNamedCounter( level->countInstanceOf(eTYPE_GHAST   		,true ), "eTYPE_GHAST");
	}
#endif
#endif

	if (!spawnEnemies && !spawnFriendlies)
	{
		return 0;
	}
	MemSect(20);
	chunksToPoll.clear();
	
#if 0
	AUTO_VAR(itEnd, level->players.end());
	for (AUTO_VAR(it, level->players.begin()); it != itEnd; it++)
	{
		shared_ptr<Player> player = *it; //level->players.at(i);
		int xx = Mth::floor(player->x / 16);
		int zz = Mth::floor(player->z / 16);

		int r = 128 / 16;
		for (int x = -r; x <= r; x++)
			for (int z = -r; z <= r; z++)
			{
				chunksToPoll.insert(ChunkPos(x + xx, z + zz));
			}
	}
#else
	// 4J - rewritten to add chunks interleaved by player, and to add them from the centre outwards. We're going to be
	// potentially adding less creatures than the original so that our count stays consistent with number of players added, so
	// we want to make sure as best we can that the ones we do add are near the active players
	int playerCount = (int)level->players.size();
	int *xx = new int[playerCount];
	int *zz = new int[playerCount];
	for (int i = 0; i < playerCount; i++)
	{
		shared_ptr<Player> player = level->players[i];
		xx[i] = Mth::floor(player->x / 16);
		zz[i] = Mth::floor(player->z / 16);
#ifdef __PSVITA__
		chunksToPoll.insert(ChunkPos(xx[i], zz[i] ),false);
#else
		chunksToPoll.insert(std::pair<ChunkPos,bool>(ChunkPos(xx[i], zz[i] ),false));
#endif
	}

	for( int r = 1; r <= 8; r++ )
	{
		for( int l = 0; l < ( r * 2 ) ; l++ )
		{
			for( int i = 0; i < playerCount; i++ )
			{
				bool edgeChunk = ( r == 8 );

				// If this chunk isn't at the edge of the region for this player, then always store with a flag of false
				// so that if it was at the edge of another player, then this will remove that
				if( !edgeChunk )
				{
#ifdef __PSVITA__
					chunksToPoll.insert(ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r )		), false);
					chunksToPoll.insert(ChunkPos( ( xx[i] + r )	 , ( zz[i] - r ) + l    ), false);
					chunksToPoll.insert(ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r )		), false);
					chunksToPoll.insert(ChunkPos( ( xx[i] - r )	 , ( zz[i] + r ) - l    ), false);
#else
					chunksToPoll.insert(std::pair<ChunkPos,bool>(ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r )		), false));
					chunksToPoll.insert(std::pair<ChunkPos,bool>(ChunkPos( ( xx[i] + r )	 , ( zz[i] - r ) + l    ), false));
					chunksToPoll.insert(std::pair<ChunkPos,bool>(ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r )		), false));
					chunksToPoll.insert(std::pair<ChunkPos,bool>(ChunkPos( ( xx[i] - r )	 , ( zz[i] + r ) - l    ), false));
#endif
				}
				else
				{
#ifdef __PSVITA__
					ChunkPos cp = ChunkPos( ( xx[i] - r  ) + l , ( zz[i] - r ));
					if( chunksToPoll.find( cp ) )	chunksToPoll.insert(cp, true);
					cp = ChunkPos( ( xx[i] + r ), ( zz[i] - r ) + l    );												 
					if( chunksToPoll.find( cp ) )	chunksToPoll.insert(cp, true);
					cp = ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r ));													 
					if( chunksToPoll.find( cp ) )	chunksToPoll.insert(cp, true);
					cp = ChunkPos( ( xx[i] - r ), ( zz[i] + r ) - l);													 
					if( chunksToPoll.find( cp ) )	chunksToPoll.insert(cp, true);
#else
					ChunkPos cp = ChunkPos( ( xx[i] - r  ) + l , ( zz[i] - r ));
					if( chunksToPoll.find( cp ) == chunksToPoll.end() )	chunksToPoll.insert(std::pair<ChunkPos,bool>(cp, true));
					cp = ChunkPos( ( xx[i] + r ), ( zz[i] - r ) + l    );												 
					if( chunksToPoll.find( cp ) == chunksToPoll.end() )	chunksToPoll.insert(std::pair<ChunkPos,bool>(cp, true));
					cp = ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r ));													 
					if( chunksToPoll.find( cp ) == chunksToPoll.end() )	chunksToPoll.insert(std::pair<ChunkPos,bool>(cp, true));
					cp = ChunkPos( ( xx[i] - r ), ( zz[i] + r ) - l);													 
					if( chunksToPoll.find( cp ) == chunksToPoll.end() )	chunksToPoll.insert(std::pair<ChunkPos,bool>(cp, true));
#endif

				}
			}
		}
	}
	delete [] xx;
	delete [] zz;
#endif
	MemSect(0);
	int count = 0;
	MemSect(31);
	Pos *spawnPos = level->getSharedSpawnPos();
	MemSect(0);
	
	for (unsigned int i = 0; i < MobCategory::values.length; i++)
	{
		MobCategory *mobCategory = MobCategory::values[i];
		if ((mobCategory->isFriendly() && !spawnFriendlies) || (!mobCategory->isFriendly() && !spawnEnemies))
		{
			continue;
		}

		// 4J - this is now quite different to the java version. We just have global max counts for the level whereas the original has a max per chunk that
		// scales with the number of chunks to be polled.
		int categoryCount = level->countInstanceOf( mobCategory->getEnumBaseClass(), mobCategory->isSingleType());
		if( categoryCount >= mobCategory->getMaxInstancesPerLevel())
		{
			continue;
		}

#ifdef __PSVITA__
		for( int i = 0;i < chunksToPoll.end();i += 1 )
		{
			SCustomMapNode *it = chunksToPoll.get(i);
#else
		AUTO_VAR(itEndCTP, chunksToPoll.end());
		for (AUTO_VAR(it, chunksToPoll.begin()); it != itEndCTP; it++)
		   {
#endif
			   if( it->second )
			   {
                    // don't add mobs to edge chunks, to prevent adding mobs
                    // "outside" of the active playground
				   continue;
			   }
			   ChunkPos *cp = (ChunkPos *) (&it->first);

			   // 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread
			   if( !level->hasChunk(cp->x,cp->z) ) continue;

			   TilePos start = getRandomPosWithin(level, cp->x, cp->z);
			   int xStart = start.x;
			   int yStart = start.y;
			   int zStart = start.z;

			   if (level->isSolidBlockingTile(xStart, yStart, zStart)) continue;
			   if (level->getMaterial(xStart, yStart, zStart) != mobCategory->getSpawnPositionMaterial()) continue;
			   int clusterSize = 0;

			   for (int dd = 0; dd < 3; dd++)
			   {
				   int x = xStart;
				   int y = yStart;
				   int z = zStart;
				   int ss = 6;

				   Biome::MobSpawnerData *currentMobType = NULL;

				   for (int ll = 0; ll < 4; ll++)
				   {
					   x += level->random->nextInt(ss) - level->random->nextInt(ss);
					   y += level->random->nextInt(1) - level->random->nextInt(1);
					   z += level->random->nextInt(ss) - level->random->nextInt(ss);
					   // int y = heightMap[x + z * w] + 1;

					   // 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread
					   if( !level->hasChunkAt( x, y, z ) ) continue;

					   if (isSpawnPositionOk(mobCategory, level, x, y, z))
					   {
						   float xx = x + 0.5f;
						   float yy = (float) y;
						   float zz = z + 0.5f;
						   if (level->getNearestPlayer(xx, yy, zz, MIN_SPAWN_DISTANCE) != NULL)
						   {
							   continue;
						   }
						   else
						   {
							   float xd = xx - spawnPos->x;
							   float yd = yy - spawnPos->y;
							   float zd = zz - spawnPos->z;
							   float sd = xd * xd + yd * yd + zd * zd;
							   if (sd < MIN_SPAWN_DISTANCE * MIN_SPAWN_DISTANCE)
							   {
								   continue;
							   }
						   }

                            if (currentMobType == NULL)
							{
                                currentMobType = level->getRandomMobSpawnAt(mobCategory, x, y, z);
                                if (currentMobType == NULL)
								{
                                    break;
                                }
                            }

						   shared_ptr<Mob> mob;
						   // 4J - removed try/catch
//						   try
//						   {
	MemSect(29);
							   //mob = type.mobClass.getConstructor(Level.class).newInstance(level);
							   mob = dynamic_pointer_cast<Mob>(EntityIO::newByEnumType(currentMobType->mobClass, level));
	MemSect(0);
//						   }
//						   catch (exception e)
//						   {
//							   // TODO 4J We can't print a stack trace, and the newInstance function doesn't throw an exception just now anyway
//							   //e.printStackTrace();
//							   return count;
//						   }

							// 4J - If it is an animal or a monster, don't let any one type of mob represent more than 50% of the total amount of these things. This
							// was added initially to stop flat lands being totally populated with slimes but seems like a generally good rule.
						   eINSTANCEOF mobType = mob->GetType();

						   if( ( mobType & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK ) || ( mobType & eTYPE_MONSTER ) )
						   {
							   // even more special rule for ghasts, because filling up the nether with 25 of them is a bit unpleasant. In the java version they are
							   // only limited by the fact that the world fills up with pig zombies (the only other type of enemy mob in the nether) before them - they
							   // aren't actually even counted properly themselves
							   if( mobType == eTYPE_GHAST )
							   {
								   if( level->countInstanceOf(mobType, true) >= 4 ) continue;
							   }
							   else if( mobType == eTYPE_ENDERMAN && level->dimension->id == 1 )
							   {
								   // Special rule for the end, as we only have Endermen (plus the dragon). Increase the spawnable counts based on level difficulty
								   int maxEndermen = mobCategory->getMaxInstancesPerLevel();

								   if( level->difficulty == Difficulty::NORMAL )
								   {
									   maxEndermen -= mobCategory->getMaxInstancesPerLevel()/4;
								   }
								   else if( level->difficulty <= Difficulty::EASY)
								   {
									   maxEndermen -= mobCategory->getMaxInstancesPerLevel()/2;
								   }

								   if( level->countInstanceOf(mobType, true) >= maxEndermen ) continue;
							   }
							   else if( level->countInstanceOf(mobType, true) >= ( mobCategory->getMaxInstancesPerLevel() / 2 ) ) continue;
						   }

						   mob->moveTo(xx, yy, zz, level->random->nextFloat() * 360, 0);

						   if (mob->canSpawn())
						   {
							   // 4J - check if we are going to despawn straight away too, and don't add if we will - otherwise we'll be sending
							   // network packets for adding & removal that we don't need
							   mob->checkDespawn();
							   if( !mob->removed )
							   {
								   clusterSize++;
								   categoryCount++;
								   mob->setDespawnProtected();	// 4J added - default to protected against despawning
								   level->addEntity(mob);
								   finalizeMobSettings(mob, level, xx, yy, zz);
								   mob->finalizeMobSpawn();
								   // 4J - change here so that we can't ever make more than the desired amount of entities in each priority. In the original java version
								   // depending on the random spawn positions being considered the only limit as to the number of entities created per category is the number
								   // of chunks to poll.
								   if (categoryCount >= mobCategory->getMaxInstancesPerLevel() ) goto categoryLoop;
								   if (clusterSize >= mob->getMaxSpawnClusterSize()) goto chunkLoop;
							   }
						   }
						   count += clusterSize;
					   }
				   }
			   }
			   chunkLoop: continue;
		   }
		categoryLoop: continue;
	}
	delete spawnPos;

	return count;
}

bool MobSpawner::isSpawnPositionOk(MobCategory *category, Level *level, int x, int y, int z)
{
	// 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread
	if( !level->hasChunkAt(x, y, z ) ) return false;

#ifdef __PSVITA__
	// AP - added this for Vita. Make sure a new spawn point has 2 chunks around it. This will make sure monsters don't keep getting spawned on the edge preventing other new monsters
	// from being spawned
	int r = 32;
	if( !level->hasChunksAt(x - r, 0, z - r, x + r, 0, z + r))
	{
		return false;
	}
#endif

	if (category->getSpawnPositionMaterial() == Material::water)
	{
		// 4J - changed to spawn water things only in deep water
		int yo = 0;
		int liquidCount = 0;
		
		while( ( y - yo ) >= 0 && ( yo < 5 ) )
		{
			if( level->getMaterial(x, y - yo, z)->isLiquid() ) liquidCount++;
			yo++;
		}

		// 4J - Sometimes deep water could be just a waterfall, so check that it's wide as well
		bool inEnoughWater = false;
		if( liquidCount == 5 )
		{
			if( level->getMaterial(x+5, y, z)->isLiquid() &&
				level->getMaterial(x-5, y, z)->isLiquid() &&
				level->getMaterial(x, y, z+5)->isLiquid() &&
				level->getMaterial(x, y, z-5)->isLiquid()
				)
			{
				inEnoughWater = true;
			}
		}

		return inEnoughWater && !level->isSolidBlockingTile(x, y + 1, z);
	}
	else
	{
		if (!level->isTopSolidBlocking(x, y - 1, z)) return false;
		int tt = level->getTile(x, y - 1, z);
		return tt != Tile::unbreakable_Id && !level->isSolidBlockingTile(x, y, z) && !level->getMaterial(x, y, z)->isLiquid() && !level->isSolidBlockingTile(x, y + 1, z);
	}
}


void MobSpawner::finalizeMobSettings(shared_ptr<Mob> mob, Level *level, float xx, float yy, float zz)
{
	if (dynamic_pointer_cast<Spider>( mob ) != NULL && level->random->nextInt(100) == 0)
	{
		shared_ptr<Skeleton> skeleton = shared_ptr<Skeleton>( new Skeleton(level) );
		skeleton->moveTo(xx, yy, zz, mob->yRot, 0);
		level->addEntity(skeleton);
		skeleton->ride(mob);
	}
	else if (dynamic_pointer_cast<Sheep >( mob ) != NULL)
	{
		(dynamic_pointer_cast<Sheep>( mob ))->setColor(Sheep::getSheepColor(level->random));
	}
	else if (dynamic_pointer_cast<Ozelot >( mob ) != NULL)
	{
		if (level->random->nextInt(7) == 0)
		{
			for (int kitten = 0; kitten < 2; kitten++)
			{
				shared_ptr<Ozelot> ozelot = shared_ptr<Ozelot>(new Ozelot(level));
				ozelot->moveTo(xx, yy, zz, mob->yRot, 0);
				ozelot->setAge(-20 * 60 * 20);
				level->addEntity(ozelot);
			}
		}
	}
}


// 4J Stu TODO This was an array of Class type. I haven't made a base Class type yet, but don't need to
// as this can be an array of Mob type?
eINSTANCEOF MobSpawner::bedEnemies[bedEnemyCount] = {
	eTYPE_SPIDER, eTYPE_ZOMBIE, eTYPE_SKELETON
};


bool MobSpawner::attackSleepingPlayers(Level *level, vector<shared_ptr<Player> > *players)
{

	bool somebodyWokeUp = false;

	PathFinder finder = PathFinder(level, true, false, false, true);

	AUTO_VAR(itEnd, players->end());
	for (AUTO_VAR(it, players->begin()); it != itEnd; it++)
	{
		shared_ptr<Player> player = (*it);

		bool nextPlayer = false;

		for (int attemptCount = 0; attemptCount < 20 && !nextPlayer; attemptCount++)\
		{


			// limit position within the range of the player
			int x = Mth::floor(player->x) + level->random->nextInt(32) - level->random->nextInt(32);
			int z = Mth::floor(player->z) + level->random->nextInt(32) - level->random->nextInt(32);
			int yStart = Mth::floor(player->y) + level->random->nextInt(16) - level->random->nextInt(16);
			if (yStart < 1)
			{
				yStart = 1;
			}
			else if (yStart > Level::maxBuildHeight)
			{
				yStart = Level::maxBuildHeight;
			}

			{
				int type = level->random->nextInt(bedEnemyCount);
				int y = yStart;

				while (y > 2 && !level->isTopSolidBlocking(x, y - 1, z))
				{
					y--;
				}

				while (!isSpawnPositionOk( (MobCategory *) MobCategory::monster, level, x, y, z) && y < (yStart + 16) && y < Level::maxBuildHeight)
				{
					y++;
				}
				if (y >= (yStart + 16) || y >= Level::maxBuildHeight)
				{
					y = yStart;
					continue;
				}
				else
				{
					float xx = x + 0.5f;
					float yy = (float) y;
					float zz = z + 0.5f;

					shared_ptr<Mob> mob;
// 4J - removed try/catch
//					try
//					{
						//mob = classes[type].getConstructor(Level.class).newInstance(level);
						// 4J - there was a classes array here which duplicated the bedEnemies array but have removed it
						mob = dynamic_pointer_cast<Mob>(EntityIO::newByEnumType(bedEnemies[type], level ));
//					}
//					catch (exception e)
//					{
//						// TODO 4J Stu - We can't print a stack trace, and newInstance doesn't currently throw an exception anyway
//						//e.printStackTrace();
//						return somebodyWokeUp;
//					}

					// System.out.println("Placing night mob");
					mob->moveTo(xx, yy, zz, level->random->nextFloat() * 360, 0);
					// check if the mob can spawn at this location
					if (!mob->canSpawn())
					{
						continue;
					}
					Pos *bedPos = BedTile::findStandUpPosition(level, Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z), 1);
					if (bedPos == NULL)
					{
						// an unlikely case where the bed is
						// completely blocked
						bedPos = new Pos(x, y + 1, z);
					}

					// 4J Stu - TU-1 hotfix
					// Fix for #13152 - If the player sleeps in a bed next to a wall in an enclosed, well lit area they will be awoken by a monster
					// The pathfinder should attempt to get close to the position that we will move the mob to,
					// instead of the player who could be next to a wall. Otherwise the paths gets to the other
					// side of the the wall, then moves the mob inside the building
					//Path *findPath = finder.findPath(mob.get(), player.get(), 32.0f);
					Path *findPath = finder.findPath(mob.get(), bedPos->x, bedPos->y, bedPos->z, 32.0f);
					if (findPath != NULL && findPath->getSize() > 1)
					{
						Node *last = findPath->last();

						if (abs(last->x - bedPos->x) < 1.5 && abs(last->z - bedPos->z) < 1.5 && abs(last->y - bedPos->y) < 1.5)
						{
							// System.out.println("Found path!");

							mob->moveTo(bedPos->x + 0.5f, bedPos->y, bedPos->z + 0.5f, 0, 0);
							// the mob would maybe not be able to
							// spawn here, but we ignore that now (we assume
							// it walked here)
							{
								level->addEntity(mob);
								finalizeMobSettings(mob, level, bedPos->x + 0.5f, (float) bedPos->y, bedPos->z + 0.5f);
								mob->finalizeMobSpawn();
								player->stopSleepInBed(true, false, false);
								// play a sound effect to scare the player
								mob->playAmbientSound();
								somebodyWokeUp = true;
								nextPlayer = true;
							}
						}
						delete findPath;
					}
					delete bedPos;
				}
			}
		}
	}


	return somebodyWokeUp;
}

void MobSpawner::postProcessSpawnMobs(Level *level, Biome *biome, int xo, int zo, int cellWidth, int cellHeight, Random *random)
{
	// 4J - not for our version. Creates a few too many mobs.
#if 0
	vector<Biome::MobSpawnerData *> *mobs = biome->getMobs(MobCategory::creature);
	if (mobs->empty())
	{
		return;
	}

	while (random->nextFloat() < biome->getCreatureProbability())
	{
		Biome::MobSpawnerData *type = (Biome::MobSpawnerData *) WeighedRandom::getRandomItem(level->random, ((vector<WeighedRandomItem *> *)mobs));
		int count = type->minCount + random->nextInt(1 + type->maxCount - type->minCount);

		int x = xo + random->nextInt(cellWidth);
		int z = zo + random->nextInt(cellHeight);
		int startX = x, startZ = z;

		for (int c = 0; c < count; c++)
		{
			bool success = false;
			for (int attempts = 0; !success && attempts < 4; attempts++)
			{
				// these mobs always spawn at the topmost position
				int y = level->getTopSolidBlock(x, z);
				if (isSpawnPositionOk(MobCategory::creature, level, x, y, z))
				{

					float xx = x + 0.5f;
					float yy = (float)y;
					float zz = z + 0.5f;

					shared_ptr<Mob> mob;
					//try {
					mob = dynamic_pointer_cast<Mob>( EntityIO::newByEnumType(type->mobClass, level ) );
					//} catch (Exception e) {
					//	e.printStackTrace();
					//	continue;
					//}

					// System.out.println("Placing night mob");
					mob->moveTo(xx, yy, zz, random->nextFloat() * 360, 0);

					mob->setDespawnProtected();

					level->addEntity(mob);
					finalizeMobSettings(mob, level, xx, yy, zz);
					success = true;
				}

				x += random->nextInt(5) - random->nextInt(5);
				z += random->nextInt(5) - random->nextInt(5);
				while (x < xo || x >= (xo + cellWidth) || z < zo || z >= (zo + cellWidth))
				{
					x = startX + random->nextInt(5) - random->nextInt(5);
					z = startZ + random->nextInt(5) - random->nextInt(5);
				}
			}
		}
	}
#endif
}
