2017-09-19 04:34:08 -04:00
|
|
|
|
2014-09-17 11:45:13 -04:00
|
|
|
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
|
|
|
|
|
|
|
#include "MobSpawnerEntity.h"
|
|
|
|
|
2014-09-19 17:00:54 -04:00
|
|
|
#include "../World.h"
|
|
|
|
#include "../FastRandom.h"
|
|
|
|
#include "../MobSpawner.h"
|
2016-11-18 14:00:04 -05:00
|
|
|
#include "../ClientHandle.h"
|
2014-09-19 17:00:54 -04:00
|
|
|
#include "../Items/ItemSpawnEgg.h"
|
|
|
|
|
2014-09-17 11:45:13 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-09-29 08:59:24 -04:00
|
|
|
cMobSpawnerEntity::cMobSpawnerEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld * a_World):
|
|
|
|
super(a_BlockType, a_BlockMeta, a_Pos, a_World),
|
2017-06-15 09:32:33 -04:00
|
|
|
m_Entity(mtPig),
|
|
|
|
m_SpawnDelay(100),
|
|
|
|
m_IsActive(false)
|
2014-09-17 11:45:13 -04:00
|
|
|
{
|
2017-06-15 09:32:33 -04:00
|
|
|
ASSERT(a_BlockType == E_BLOCK_MOB_SPAWNER);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cMobSpawnerEntity::CopyFrom(const cBlockEntity & a_Src)
|
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
super::CopyFrom(a_Src);
|
2018-05-02 03:50:36 -04:00
|
|
|
auto & src = static_cast<const cMobSpawnerEntity &>(a_Src);
|
2017-06-15 09:32:33 -04:00
|
|
|
m_Entity = src.m_Entity;
|
|
|
|
m_IsActive = src.m_IsActive;
|
|
|
|
m_SpawnDelay = src.m_SpawnDelay;
|
2014-09-17 11:45:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-09-19 17:00:54 -04:00
|
|
|
void cMobSpawnerEntity::SendTo(cClientHandle & a_Client)
|
2014-09-17 11:45:13 -04:00
|
|
|
{
|
2014-09-19 17:00:54 -04:00
|
|
|
a_Client.SendUpdateBlockEntity(*this);
|
2014-09-17 11:45:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-01 17:12:44 -05:00
|
|
|
bool cMobSpawnerEntity::UsedBy(cPlayer * a_Player)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
if (a_Player->GetEquippedItem().m_ItemType == E_ITEM_SPAWN_EGG)
|
|
|
|
{
|
2014-11-18 09:33:41 -05:00
|
|
|
eMonsterType MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(a_Player->GetEquippedItem().m_ItemDamage);
|
|
|
|
if (MonsterType == eMonsterType::mtInvalidType)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2015-12-01 17:12:44 -05:00
|
|
|
return false;
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
m_Entity = MonsterType;
|
|
|
|
ResetTimer();
|
|
|
|
if (!a_Player->IsGameModeCreative())
|
|
|
|
{
|
|
|
|
a_Player->GetInventory().RemoveOneEquippedItem();
|
|
|
|
}
|
2018-09-24 16:33:39 -04:00
|
|
|
FLOGD("Changed monster spawner at {0} to type {1}.", GetPos(), cMonster::MobTypeToString(MonsterType));
|
2015-12-01 17:12:44 -05:00
|
|
|
return true;
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
2015-12-01 17:12:44 -05:00
|
|
|
return false;
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cMobSpawnerEntity::UpdateActiveState(void)
|
2014-09-17 11:45:13 -04:00
|
|
|
{
|
2014-09-19 17:00:54 -04:00
|
|
|
if (GetNearbyPlayersNum() > 0)
|
|
|
|
{
|
|
|
|
m_IsActive = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_IsActive = false;
|
|
|
|
}
|
2014-09-17 11:45:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-01-11 16:12:26 -05:00
|
|
|
bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
2014-09-17 11:45:13 -04:00
|
|
|
{
|
2014-09-19 17:00:54 -04: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 11:45:13 -04:00
|
|
|
{
|
2014-09-19 17:00:54 -04:00
|
|
|
SpawnEntity();
|
|
|
|
return true;
|
2014-09-17 11:45:13 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-09-19 17:00:54 -04:00
|
|
|
m_SpawnDelay--;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cMobSpawnerEntity::ResetTimer(void)
|
|
|
|
{
|
2017-06-13 15:35:30 -04:00
|
|
|
m_SpawnDelay = GetRandomProvider().RandInt<short>(200, 800);
|
2017-09-25 12:17:45 -04:00
|
|
|
m_World->BroadcastBlockEntity(GetPos());
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cMobSpawnerEntity::SpawnEntity(void)
|
|
|
|
{
|
2014-09-26 18:07:17 -04:00
|
|
|
int NearbyEntities = GetNearbyMonsterNum(m_Entity);
|
2014-09-19 17:00:54 -04:00
|
|
|
if (NearbyEntities >= 6)
|
|
|
|
{
|
|
|
|
ResetTimer();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-11 17:20:49 -04:00
|
|
|
auto MobType = m_Entity;
|
|
|
|
bool EntitiesSpawned = m_World->DoWithChunk(GetChunkX(), GetChunkZ(), [&](cChunk & a_Chunk)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2017-06-13 15:35:30 -04:00
|
|
|
auto & Random = GetRandomProvider();
|
2014-09-19 17:00:54 -04:00
|
|
|
|
2017-09-11 17:20:49 -04:00
|
|
|
bool HaveSpawnedEntity = false;
|
2014-09-19 17:00:54 -04:00
|
|
|
for (size_t i = 0; i < 4; i++)
|
|
|
|
{
|
2017-09-11 17:20:49 -04:00
|
|
|
if (NearbyEntities >= 6)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-10-11 05:02:53 -04:00
|
|
|
Vector3i 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())
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2019-10-11 05:02:53 -04:00
|
|
|
EMCSBiome Biome = chunk->GetBiomeAt(spawnRelPos.x, spawnRelPos.z);
|
2014-09-19 17:00:54 -04:00
|
|
|
|
2019-10-11 05:02:53 -04:00
|
|
|
if (cMobSpawner::CanSpawnHere(chunk, spawnRelPos, MobType, Biome))
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2019-10-11 05:02:53 -04:00
|
|
|
auto absPos = chunk->RelativeToAbsolute(spawnRelPos);
|
|
|
|
auto monster = cMonster::NewMonsterFromType(MobType);
|
|
|
|
if (monster == nullptr)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2019-10-11 05:02:53 -04:00
|
|
|
monster->SetPosition(absPos);
|
|
|
|
monster->SetYaw(Random.RandReal(360.0f));
|
|
|
|
if (chunk->GetWorld()->SpawnMobFinalize(std::move(monster)) != cEntity::INVALID_ID)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2017-09-11 17:20:49 -04:00
|
|
|
HaveSpawnedEntity = true;
|
2018-07-24 17:30:49 -04:00
|
|
|
m_World->BroadcastSoundParticleEffect(
|
2015-11-23 18:39:19 -05:00
|
|
|
EffectID::PARTICLE_MOBSPAWN,
|
2019-10-11 05:02:53 -04:00
|
|
|
absPos,
|
2015-05-28 07:29:26 -04:00
|
|
|
0
|
|
|
|
);
|
2017-09-11 17:20:49 -04:00
|
|
|
NearbyEntities++;
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-10-02 15:59:25 -04:00
|
|
|
return HaveSpawnedEntity;
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
2017-09-11 17:20:49 -04:00
|
|
|
);
|
2014-09-19 17:00:54 -04:00
|
|
|
|
2017-09-11 17:20:49 -04:00
|
|
|
if (EntitiesSpawned)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
ResetTimer();
|
2014-09-17 11:45:13 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-09-19 17:00:54 -04:00
|
|
|
int cMobSpawnerEntity::GetNearbyPlayersNum(void)
|
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
auto SpawnerPos = Vector3d(0.5, 0.5, 0.5) + m_Pos;
|
2014-09-19 17:00:54 -04:00
|
|
|
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 18:07:17 -04:00
|
|
|
int cMobSpawnerEntity::GetNearbyMonsterNum(eMonsterType a_EntityType)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
auto SpawnerPos = Vector3d(0.5, 0.5, 0.5) + m_Pos;
|
2014-09-19 17:00:54 -04:00
|
|
|
int NumEntities = 0;
|
|
|
|
|
|
|
|
class cCallback : public cChunkDataCallback
|
|
|
|
{
|
|
|
|
public:
|
2015-05-19 14:32:10 -04:00
|
|
|
cCallback(Vector3d a_SpawnerPos, eMonsterType a_CallbackEntityType, int & a_NumEntities) :
|
2014-09-19 17:00:54 -04:00
|
|
|
m_SpawnerPos(a_SpawnerPos),
|
2015-05-19 14:32:10 -04:00
|
|
|
m_EntityType(a_CallbackEntityType),
|
2014-09-19 17:00:54 -04:00
|
|
|
m_NumEntities(a_NumEntities)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Entity(cEntity * a_Entity) override
|
|
|
|
{
|
|
|
|
if (!a_Entity->IsMob())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-24 07:56:56 -04:00
|
|
|
cMonster * Mob = static_cast<cMonster *>(a_Entity);
|
2014-09-19 17:00:54 -04:00
|
|
|
if (Mob->GetMobType() != m_EntityType)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-01 08:58:13 -05: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 17:00:54 -04:00
|
|
|
{
|
|
|
|
m_NumEntities++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
Vector3d m_SpawnerPos;
|
2014-09-26 18:07:17 -04:00
|
|
|
eMonsterType m_EntityType;
|
2014-09-19 17:00:54 -04: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 11:45:13 -04:00
|
|
|
|
2014-09-19 17:00:54 -04:00
|
|
|
return NumEntities;
|
2014-09-17 11:45:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|