summaryrefslogblamecommitdiffstats
path: root/src/BlockEntities/MobSpawnerEntity.cpp
blob: f5fb5c3ff49be10c555ba71e995f3a3e0e25c164 (plain) (tree)
1
2
3
4
5
6
7
8
9
 


                                                                                              
 


                          
                            

                                  



 
                                                                                                                      
                                                        


                          
 








                                                            
                               
                                                                   


                                        





 
                                                        
 
                                              





 
                                                  


                                                                       

                                                                                                                                   
                 
                                     







                                                                         
                                                                                                                       
                            
         
                     






                                               
 
                                                 





 
                                                                              
 











                                                 
         

                              


            










                                        
                                                                    
                                                







                                         
                                                            





                                
                                                                                                   
                 
                                                            
 
                                                       

                                                      
                                                        



                                              

                                                              




                                                                                                                        

                                                                                                  


                                                 
                                                                                                  
 
                                                                                                         
                                 


                                                                                              


                                                         


                                                                                                                           
                                         
                                                                         
                                                                                                                              
                                                                 


                                         
                                                 
                 
          
 
                            

                             






         

                                                
                                                          







































                                                                                              
                                                                     
 
                                                          




                                                   
                                                                                                          
                                                   
                                                           










                                                                
                                                                           




                                                              
                                                                                                                                                                                           






                                                
                                          





                                                                                              
 
                           




 

#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "MobSpawnerEntity.h"

#include "../World.h"
#include "../FastRandom.h"
#include "../MobSpawner.h"
#include "../ClientHandle.h"
#include "../Items/ItemSpawnEgg.h"





cMobSpawnerEntity::cMobSpawnerEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld * a_World):
	Super(a_BlockType, a_BlockMeta, a_Pos, a_World),
	m_Entity(mtPig),
	m_SpawnDelay(100),
	m_IsActive(false)
{
	ASSERT(a_BlockType == E_BLOCK_MOB_SPAWNER);
}





void cMobSpawnerEntity::CopyFrom(const cBlockEntity & a_Src)
{
	Super::CopyFrom(a_Src);
	auto & src = static_cast<const cMobSpawnerEntity &>(a_Src);
	m_Entity = src.m_Entity;
	m_IsActive = src.m_IsActive;
	m_SpawnDelay = src.m_SpawnDelay;
}





void cMobSpawnerEntity::SendTo(cClientHandle & a_Client)
{
	a_Client.SendUpdateBlockEntity(*this);
}





bool cMobSpawnerEntity::UsedBy(cPlayer * a_Player)
{
	if (a_Player->GetEquippedItem().m_ItemType == E_ITEM_SPAWN_EGG)
	{
		eMonsterType MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(a_Player->GetEquippedItem().m_ItemDamage);
		if (MonsterType == eMonsterType::mtInvalidType)
		{
			return false;
		}

		m_Entity = MonsterType;
		ResetTimer();
		if (!a_Player->IsGameModeCreative())
		{
			a_Player->GetInventory().RemoveOneEquippedItem();
		}
		FLOGD("Changed monster spawner at {0} to type {1}.", GetPos(), cMonster::MobTypeToString(MonsterType));
		return true;
	}
	return false;
}





void cMobSpawnerEntity::UpdateActiveState(void)
{
	m_IsActive = (GetNearbyPlayersNum() > 0);
}





bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	// Update the active flag every 5 seconds
	if ((m_World->GetWorldAge() % 100) == 0)
	{
		UpdateActiveState();
	}

	if (!m_IsActive)
	{
		return false;
	}

	if (m_SpawnDelay <= 0)
	{
		SpawnEntity();
		return true;
	}
	else
	{
		m_SpawnDelay--;
	}
	return false;
}





void cMobSpawnerEntity::ResetTimer(void)
{
	m_SpawnDelay = GetRandomProvider().RandInt<short>(200, 800);
	m_World->BroadcastBlockEntity(GetPos());
}





void cMobSpawnerEntity::SpawnEntity(void)
{
	auto NearbyEntities = GetNearbyMonsterNum(m_Entity);
	if (NearbyEntities >= 6)
	{
		ResetTimer();
		return;
	}

	bool EntitiesSpawned = m_World->DoWithChunk(GetChunkX(), GetChunkZ(), [&](cChunk & a_Chunk)
		{
			auto & Random = GetRandomProvider();

			bool HaveSpawnedEntity = false;
			for (size_t i = 0; i < 4; i++)
			{
				if (NearbyEntities >= 6)
				{
					break;
				}

				auto SpawnRelPos(GetRelPos());
				SpawnRelPos += Vector3i(
					static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0),
					Random.RandInt(-1, 1),
					static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0)
				);

				auto Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(SpawnRelPos);
				if ((Chunk == nullptr) || !Chunk->IsValid())
				{
					continue;
				}
				EMCSBiome Biome = Chunk->GetBiomeAt(SpawnRelPos.x, SpawnRelPos.z);

				if (cMobSpawner::CanSpawnHere(Chunk, SpawnRelPos, m_Entity, Biome, true))
				{
					auto AbsPos = Chunk->RelativeToAbsolute(SpawnRelPos);
					auto Monster = cMonster::NewMonsterFromType(m_Entity);
					if (Monster == nullptr)
					{
						continue;
					}
					Monster->SetPosition(AbsPos);
					Monster->SetYaw(Random.RandReal(360.0f));
					if (Chunk->GetWorld()->SpawnMobFinalize(std::move(Monster)) != cEntity::INVALID_ID)
					{
						HaveSpawnedEntity = true;
						m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_MOBSPAWN, AbsPos, 0);
						NearbyEntities++;
					}
				}
			}
			return HaveSpawnedEntity;
		}
	);

	if (EntitiesSpawned)
	{
		ResetTimer();
	}
}





int cMobSpawnerEntity::GetNearbyPlayersNum(void)
{
	auto SpawnerPos = Vector3d(0.5, 0.5, 0.5) + m_Pos;
	int NumPlayers = 0;

	class cCallback : public cChunkDataCallback
	{
	public:
		cCallback(Vector3d a_SpawnerPos, int & a_NumPlayers) :
			m_SpawnerPos(a_SpawnerPos),
			m_NumPlayers(a_NumPlayers)
		{
		}

		virtual void Entity(cEntity * a_Entity) override
		{
			if (!a_Entity->IsPlayer())
			{
				return;
			}

			if ((m_SpawnerPos - a_Entity->GetPosition()).Length() <= 16)
			{
				m_NumPlayers++;
			}
		}

	protected:
		Vector3d m_SpawnerPos;
		int & m_NumPlayers;
	} Callback(SpawnerPos, NumPlayers);

	int ChunkX = GetChunkX();
	int ChunkZ = GetChunkZ();
	m_World->ForEachChunkInRect(ChunkX - 1, ChunkX + 1, ChunkZ - 1, ChunkZ + 1, Callback);

	return NumPlayers;
}





int cMobSpawnerEntity::GetNearbyMonsterNum(eMonsterType a_EntityType)
{
	auto SpawnerPos = Vector3d(0.5, 0.5, 0.5) + m_Pos;
	int NumEntities = 0;

	class cCallback : public cChunkDataCallback
	{
	public:
		cCallback(Vector3d a_SpawnerPos, eMonsterType a_CallbackEntityType, int & a_NumEntities) :
			m_SpawnerPos(a_SpawnerPos),
			m_EntityType(a_CallbackEntityType),
			m_NumEntities(a_NumEntities)
		{
		}

		virtual void Entity(cEntity * a_Entity) override
		{
			if (!a_Entity->IsMob())
			{
				return;
			}

			cMonster * Mob = static_cast<cMonster *>(a_Entity);
			if (Mob->GetMobType() != m_EntityType)
			{
				return;
			}

			if ((Diff(m_SpawnerPos.x, a_Entity->GetPosX()) <= 8.0) && (Diff(m_SpawnerPos.y, a_Entity->GetPosY()) <= 4.0) && (Diff(m_SpawnerPos.z, a_Entity->GetPosZ()) <= 8.0))
			{
				m_NumEntities++;
			}
		}

	protected:
		Vector3d m_SpawnerPos;
		eMonsterType m_EntityType;
		int & m_NumEntities;
	} Callback(SpawnerPos, a_EntityType, NumEntities);

	int ChunkX = GetChunkX();
	int ChunkZ = GetChunkZ();
	m_World->ForEachChunkInRect(ChunkX - 1, ChunkX + 1, ChunkZ - 1, ChunkZ + 1, Callback);

	return NumEntities;
}