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):
|
2020-04-13 12:38:06 -04:00
|
|
|
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)
|
|
|
|
{
|
2020-04-13 12:38:06 -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;
|
2020-10-10 15:31:44 -04:00
|
|
|
m_SpawnCount = src.m_SpawnCount;
|
|
|
|
m_SpawnRange = src.m_SpawnRange;
|
|
|
|
m_MinSpawnDelay = src.m_MinSpawnDelay;
|
|
|
|
m_MaxSpawnDelay = src.m_MaxSpawnDelay;
|
|
|
|
m_MaxNearbyEntities = src.m_MaxNearbyEntities;
|
|
|
|
m_RequiredPlayerRange = src.m_RequiredPlayerRange;
|
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();
|
|
|
|
}
|
2021-03-28 09:40:57 -04:00
|
|
|
m_World->BroadcastBlockEntity(GetPos());
|
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
|
|
|
{
|
2020-05-14 18:15:35 -04:00
|
|
|
m_IsActive = (GetNearbyPlayersNum() > 0);
|
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
|
|
|
{
|
2021-04-04 20:38:43 -04:00
|
|
|
using namespace std::chrono_literals;
|
|
|
|
|
|
|
|
// Update the active flag every 5 seconds:
|
|
|
|
if ((m_World->GetWorldTickAge() % 5s) == 0s)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
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();
|
2021-03-28 09:40:57 -04:00
|
|
|
m_World->BroadcastBlockEntity(GetPos());
|
2014-09-19 17:00:54 -04:00
|
|
|
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)
|
|
|
|
{
|
2020-10-10 15:31:44 -04:00
|
|
|
m_SpawnDelay = GetRandomProvider().RandInt<short>(m_MinSpawnDelay, m_MaxSpawnDelay);
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cMobSpawnerEntity::SpawnEntity(void)
|
|
|
|
{
|
2020-09-27 13:02:16 -04:00
|
|
|
auto NearbyEntities = GetNearbyMonsterNum(m_Entity);
|
2020-10-10 15:31:44 -04:00
|
|
|
if (NearbyEntities >= m_MaxNearbyEntities)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
ResetTimer();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-19 21:13:34 -05:00
|
|
|
bool EntitiesSpawned = m_World->DoWithChunk(GetChunkX(), GetChunkZ(), [this, NearbyEntities](cChunk & a_Chunk)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2017-06-13 15:35:30 -04:00
|
|
|
auto & Random = GetRandomProvider();
|
2020-12-19 21:13:34 -05:00
|
|
|
auto EntitySpawnTally = NearbyEntities;
|
2014-09-19 17:00:54 -04:00
|
|
|
|
2017-09-11 17:20:49 -04:00
|
|
|
bool HaveSpawnedEntity = false;
|
2020-10-10 15:31:44 -04:00
|
|
|
for (short I = 0; I < m_SpawnCount; I++)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2020-12-19 21:13:34 -05:00
|
|
|
if (EntitySpawnTally >= m_MaxNearbyEntities)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-09-27 13:02:16 -04:00
|
|
|
auto SpawnRelPos(GetRelPos());
|
|
|
|
SpawnRelPos += Vector3i(
|
2020-10-10 15:31:44 -04:00
|
|
|
static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * static_cast<double>(m_SpawnRange)),
|
2019-10-11 05:02:53 -04:00
|
|
|
Random.RandInt(-1, 1),
|
2020-10-10 15:31:44 -04:00
|
|
|
static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * static_cast<double>(m_SpawnRange))
|
2019-10-11 05:02:53 -04:00
|
|
|
);
|
|
|
|
|
2020-09-27 13:02:16 -04:00
|
|
|
auto Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(SpawnRelPos);
|
|
|
|
if ((Chunk == nullptr) || !Chunk->IsValid())
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2020-09-27 13:02:16 -04:00
|
|
|
EMCSBiome Biome = Chunk->GetBiomeAt(SpawnRelPos.x, SpawnRelPos.z);
|
2014-09-19 17:00:54 -04:00
|
|
|
|
2020-09-27 13:02:16 -04:00
|
|
|
if (cMobSpawner::CanSpawnHere(Chunk, SpawnRelPos, m_Entity, Biome, true))
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2020-09-27 13:02:16 -04:00
|
|
|
auto AbsPos = Chunk->RelativeToAbsolute(SpawnRelPos);
|
|
|
|
auto Monster = cMonster::NewMonsterFromType(m_Entity);
|
|
|
|
if (Monster == nullptr)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2020-09-27 13:02:16 -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;
|
2020-09-27 13:02:16 -04:00
|
|
|
m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_MOBSPAWN, AbsPos, 0);
|
2020-12-19 21:13:34 -05:00
|
|
|
EntitySpawnTally++;
|
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)
|
|
|
|
{
|
|
|
|
int NumPlayers = 0;
|
|
|
|
|
2020-12-19 21:13:34 -05:00
|
|
|
auto Callback = [this, &NumPlayers](cEntity & a_Entity)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2020-10-10 15:31:44 -04:00
|
|
|
if (!a_Entity.IsPlayer())
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2020-10-10 15:31:44 -04:00
|
|
|
return false;
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
2020-10-10 15:31:44 -04:00
|
|
|
if ((m_Pos - a_Entity.GetPosition()).Length() <= m_RequiredPlayerRange)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2020-10-10 15:31:44 -04:00
|
|
|
NumPlayers++;
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
2020-10-10 15:31:44 -04:00
|
|
|
return false;
|
|
|
|
};
|
2014-09-19 17:00:54 -04:00
|
|
|
|
2020-12-19 21:13:34 -05:00
|
|
|
const cBoundingBox PlayerBoundingBox(Vector3d(m_Pos.x, m_Pos.y - m_RequiredPlayerRange, m_Pos.z), m_RequiredPlayerRange, m_RequiredPlayerRange * 2);
|
2020-10-10 15:31:44 -04:00
|
|
|
m_World->ForEachEntityInBox(PlayerBoundingBox, Callback);
|
2014-09-19 17:00:54 -04:00
|
|
|
|
|
|
|
return NumPlayers;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-09-26 18:07:17 -04:00
|
|
|
int cMobSpawnerEntity::GetNearbyMonsterNum(eMonsterType a_EntityType)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
|
|
|
int NumEntities = 0;
|
|
|
|
|
2020-12-19 21:13:34 -05:00
|
|
|
auto Callback = [this, &NumEntities](cEntity & a_Entity)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2020-10-10 15:31:44 -04:00
|
|
|
if (!a_Entity.IsMob())
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2020-10-10 15:31:44 -04:00
|
|
|
return false;
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
|
|
|
|
2020-10-10 15:31:44 -04:00
|
|
|
auto & Mob = static_cast<cMonster &>(a_Entity);
|
|
|
|
if (Mob.GetMobType() == m_Entity)
|
2014-09-19 17:00:54 -04:00
|
|
|
{
|
2020-10-10 15:31:44 -04:00
|
|
|
NumEntities++;
|
2014-09-19 17:00:54 -04:00
|
|
|
}
|
2020-10-10 15:31:44 -04:00
|
|
|
return false;
|
|
|
|
};
|
2014-09-19 17:00:54 -04:00
|
|
|
|
2020-12-19 21:13:34 -05:00
|
|
|
const cBoundingBox EntityBoundingBox(Vector3d(m_Pos.x, m_Pos.y - 4, m_Pos.z), m_SpawnRange, 8);
|
2020-10-10 15:31:44 -04:00
|
|
|
m_World->ForEachEntityInBox(EntityBoundingBox, 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
|
|
|
}
|