1
0
Fork 0
cuberite-2a/src/BlockEntities/MobSpawnerEntity.cpp

299 lines
5.9 KiB
C++
Raw Normal View History


2014-09-17 15:45:13 +00:00
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "MobSpawnerEntity.h"
2014-09-19 21:00:54 +00:00
#include "../World.h"
#include "../FastRandom.h"
#include "../MobSpawner.h"
#include "../ClientHandle.h"
2014-09-19 21:00:54 +00:00
#include "../Items/ItemSpawnEgg.h"
2014-09-17 15:45:13 +00:00
2017-06-15 13:32:33 +00:00
cMobSpawnerEntity::cMobSpawnerEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World):
Super(a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World),
m_Entity(mtPig),
m_SpawnDelay(100),
m_IsActive(false)
2014-09-17 15:45:13 +00:00
{
2017-06-15 13:32:33 +00:00
ASSERT(a_BlockType == E_BLOCK_MOB_SPAWNER);
}
void cMobSpawnerEntity::CopyFrom(const cBlockEntity & a_Src)
{
Super::CopyFrom(a_Src);
auto & src = reinterpret_cast<const cMobSpawnerEntity &>(a_Src);
m_Entity = src.m_Entity;
m_IsActive = src.m_IsActive;
m_SpawnDelay = src.m_SpawnDelay;
2014-09-17 15:45:13 +00:00
}
2014-09-19 21:00:54 +00:00
void cMobSpawnerEntity::SendTo(cClientHandle & a_Client)
2014-09-17 15:45:13 +00:00
{
2014-09-19 21:00:54 +00:00
a_Client.SendUpdateBlockEntity(*this);
2014-09-17 15:45:13 +00:00
}
bool cMobSpawnerEntity::UsedBy(cPlayer * a_Player)
2014-09-19 21:00:54 +00:00
{
if (a_Player->GetEquippedItem().m_ItemType == E_ITEM_SPAWN_EGG)
{
2014-11-18 14:33:41 +00:00
eMonsterType MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(a_Player->GetEquippedItem().m_ItemDamage);
if (MonsterType == eMonsterType::mtInvalidType)
2014-09-19 21:00:54 +00:00
{
return false;
2014-09-19 21:00:54 +00:00
}
m_Entity = MonsterType;
ResetTimer();
if (!a_Player->IsGameModeCreative())
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
LOGD("Changed monster spawner at {%d, %d, %d} to type %s.", GetPosX(), GetPosY(), GetPosZ(), cMonster::MobTypeToString(MonsterType).c_str());
return true;
2014-09-19 21:00:54 +00:00
}
return false;
2014-09-19 21:00:54 +00:00
}
void cMobSpawnerEntity::UpdateActiveState(void)
2014-09-17 15:45:13 +00:00
{
2014-09-19 21:00:54 +00:00
if (GetNearbyPlayersNum() > 0)
{
m_IsActive = true;
}
else
{
m_IsActive = false;
}
2014-09-17 15:45:13 +00:00
}
bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
2014-09-17 15:45:13 +00:00
{
2014-09-19 21:00:54 +00:00
// Update the active flag every 5 seconds
if ((m_World->GetWorldAge() % 100) == 0)
{
UpdateActiveState();
}
if (!m_IsActive)
{
return false;
}
if (m_SpawnDelay <= 0)
2014-09-17 15:45:13 +00:00
{
2014-09-19 21:00:54 +00:00
SpawnEntity();
return true;
2014-09-17 15:45:13 +00:00
}
else
{
2014-09-19 21:00:54 +00:00
m_SpawnDelay--;
}
return false;
}
void cMobSpawnerEntity::ResetTimer(void)
{
2017-06-13 19:35:30 +00:00
m_SpawnDelay = GetRandomProvider().RandInt<short>(200, 800);
2014-09-19 21:00:54 +00:00
m_World->BroadcastBlockEntity(m_PosX, m_PosY, m_PosZ);
}
void cMobSpawnerEntity::SpawnEntity(void)
{
2014-09-26 22:07:17 +00:00
int NearbyEntities = GetNearbyMonsterNum(m_Entity);
2014-09-19 21:00:54 +00:00
if (NearbyEntities >= 6)
{
ResetTimer();
return;
}
auto MobType = m_Entity;
bool EntitiesSpawned = m_World->DoWithChunk(GetChunkX(), GetChunkZ(), [&](cChunk & a_Chunk)
2014-09-19 21:00:54 +00:00
{
2017-06-13 19:35:30 +00:00
auto & Random = GetRandomProvider();
2014-09-19 21:00:54 +00:00
bool HaveSpawnedEntity = false;
2014-09-19 21:00:54 +00:00
for (size_t i = 0; i < 4; i++)
{
if (NearbyEntities >= 6)
2014-09-19 21:00:54 +00:00
{
break;
}
2017-06-13 19:35:30 +00:00
int RelX = m_RelX + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0);
int RelY = m_PosY + Random.RandInt(-1, 1);
2017-06-13 19:35:30 +00:00
int RelZ = m_RelZ + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0);
2014-09-19 21:00:54 +00:00
cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelX, RelZ);
2014-12-16 23:18:59 +00:00
if ((Chunk == nullptr) || !Chunk->IsValid())
2014-09-19 21:00:54 +00:00
{
continue;
}
EMCSBiome Biome = Chunk->GetBiomeAt(RelX, RelZ);
if (cMobSpawner::CanSpawnHere(Chunk, RelX, RelY, RelZ, MobType, Biome))
2014-09-19 21:00:54 +00:00
{
double PosX = Chunk->GetPosX() * cChunkDef::Width + RelX;
double PosZ = Chunk->GetPosZ() * cChunkDef::Width + RelZ;
auto Monster = cMonster::NewMonsterFromType(MobType);
2014-12-16 23:18:59 +00:00
if (Monster == nullptr)
2014-09-19 21:00:54 +00:00
{
continue;
}
Monster->SetPosition(PosX, RelY, PosZ);
2017-06-13 19:35:30 +00:00
Monster->SetYaw(Random.RandReal(360.0f));
if (Chunk->GetWorld()->SpawnMobFinalize(std::move(Monster)) != cEntity::INVALID_ID)
2014-09-19 21:00:54 +00:00
{
HaveSpawnedEntity = true;
2015-05-24 11:56:56 +00:00
Chunk->BroadcastSoundParticleEffect(
EffectID::PARTICLE_MOBSPAWN,
2015-05-24 11:56:56 +00:00
static_cast<int>(PosX * 8.0),
static_cast<int>(RelY * 8.0),
static_cast<int>(PosZ * 8.0),
2015-05-28 11:29:26 +00:00
0
);
NearbyEntities++;
2014-09-19 21:00:54 +00:00
}
}
}
return EntitiesSpawned;
}
);
2014-09-19 21:00:54 +00:00
if (EntitiesSpawned)
2014-09-19 21:00:54 +00:00
{
ResetTimer();
2014-09-17 15:45:13 +00:00
}
}
2014-09-19 21:00:54 +00:00
int cMobSpawnerEntity::GetNearbyPlayersNum(void)
{
Vector3d SpawnerPos(m_PosX + 0.5, m_PosY + 0.5, m_PosZ + 0.5);
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;
}
2014-09-26 22:07:17 +00:00
int cMobSpawnerEntity::GetNearbyMonsterNum(eMonsterType a_EntityType)
2014-09-19 21:00:54 +00:00
{
Vector3d SpawnerPos(m_PosX + 0.5, m_PosY + 0.5, m_PosZ + 0.5);
int NumEntities = 0;
class cCallback : public cChunkDataCallback
{
public:
cCallback(Vector3d a_SpawnerPos, eMonsterType a_CallbackEntityType, int & a_NumEntities) :
2014-09-19 21:00:54 +00:00
m_SpawnerPos(a_SpawnerPos),
m_EntityType(a_CallbackEntityType),
2014-09-19 21:00:54 +00:00
m_NumEntities(a_NumEntities)
{
}
virtual void Entity(cEntity * a_Entity) override
{
if (!a_Entity->IsMob())
{
return;
}
2015-05-24 11:56:56 +00:00
cMonster * Mob = static_cast<cMonster *>(a_Entity);
2014-09-19 21:00:54 +00:00
if (Mob->GetMobType() != m_EntityType)
{
return;
}
2014-12-01 13:58:13 +00:00
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))
2014-09-19 21:00:54 +00:00
{
m_NumEntities++;
}
}
protected:
Vector3d m_SpawnerPos;
2014-09-26 22:07:17 +00:00
eMonsterType m_EntityType;
2014-09-19 21:00:54 +00:00
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);
2014-09-17 15:45:13 +00:00
2014-09-19 21:00:54 +00:00
return NumEntities;
2014-09-17 15:45:13 +00:00
}