Files
MinecraftConsoles/Minecraft.World/BaseMobSpawner.cpp
Loki Rautio 087b7e7abf Revert "Project modernization (#630)"
This code was not tested and breaks in Release builds, reverting to restore
functionality of the nightly. All in-game menus do not work and generating
a world crashes.

This reverts commit a9be52c41a.
2026-03-07 21:12:22 -06:00

404 lines
9.0 KiB
C++

#include "stdafx.h"
#include "net.minecraft.world.entity.h"
#include "net.minecraft.world.entity.item.h"
#include "net.minecraft.world.level.h"
#include "net.minecraft.world.level.tile.h"
#include "net.minecraft.world.phys.h"
#include "BaseMobSpawner.h"
BaseMobSpawner::BaseMobSpawner()
{
spawnPotentials = NULL;
spawnDelay = 20;
entityId = L"Pig";
nextSpawnData = NULL;
spin = oSpin = 0.0;
minSpawnDelay = SharedConstants::TICKS_PER_SECOND * 10;
maxSpawnDelay = SharedConstants::TICKS_PER_SECOND * 40;
spawnCount = 4;
displayEntity = nullptr;
maxNearbyEntities = 6;
requiredPlayerRange = 16;
spawnRange = 4;
}
BaseMobSpawner::~BaseMobSpawner()
{
if(spawnPotentials)
{
for( auto& it : *spawnPotentials )
{
delete it;
}
delete spawnPotentials;
}
}
wstring BaseMobSpawner::getEntityId()
{
if (getNextSpawnData() == NULL)
{
if (entityId.compare(L"Minecart") == 0)
{
entityId = L"MinecartRideable";
}
return entityId;
}
else
{
return getNextSpawnData()->type;
}
}
void BaseMobSpawner::setEntityId(const wstring &entityId)
{
this->entityId = entityId;
}
bool BaseMobSpawner::isNearPlayer()
{
return getLevel()->getNearestPlayer(getX() + 0.5, getY() + 0.5, getZ() + 0.5, requiredPlayerRange) != NULL;
}
void BaseMobSpawner::tick()
{
if (!isNearPlayer())
{
return;
}
if (getLevel()->isClientSide)
{
double xP = getX() + getLevel()->random->nextFloat();
double yP = getY() + getLevel()->random->nextFloat();
double zP = getZ() + getLevel()->random->nextFloat();
getLevel()->addParticle(eParticleType_smoke, xP, yP, zP, 0, 0, 0);
getLevel()->addParticle(eParticleType_flame, xP, yP, zP, 0, 0, 0);
if (spawnDelay > 0) spawnDelay--;
oSpin = spin;
spin = (int)(spin + 1000 / (spawnDelay + 200.0f)) % 360;
}
else
{
if (spawnDelay == -1) delay();
if (spawnDelay > 0)
{
spawnDelay--;
return;
}
bool _delay = false;
for (int c = 0; c < spawnCount; c++)
{
shared_ptr<Entity> entity = EntityIO::newEntity(getEntityId(), getLevel());
if (entity == NULL) return;
int nearBy = getLevel()->getEntitiesOfClass( typeid(entity.get()), AABB::newTemp(getX(), getY(), getZ(), getX() + 1, getY() + 1, getZ() + 1)->grow(spawnRange * 2, 4, spawnRange * 2))->size();
if (nearBy >= maxNearbyEntities)
{
delay();
return;
}
double xp = getX() + (getLevel()->random->nextDouble() - getLevel()->random->nextDouble()) * spawnRange;
double yp = getY() + getLevel()->random->nextInt(3) - 1;
double zp = getZ() + (getLevel()->random->nextDouble() - getLevel()->random->nextDouble()) * spawnRange;
shared_ptr<Mob> mob = entity->instanceof(eTYPE_MOB) ? dynamic_pointer_cast<Mob>( entity ) : nullptr;
entity->moveTo(xp, yp, zp, getLevel()->random->nextFloat() * 360, 0);
if (mob == NULL || mob->canSpawn())
{
loadDataAndAddEntity(entity);
getLevel()->levelEvent(LevelEvent::PARTICLES_MOBTILE_SPAWN, getX(), getY(), getZ(), 0);
if (mob != NULL)
{
mob->spawnAnim();
}
_delay = true;
}
}
if (_delay) delay();
}
}
shared_ptr<Entity> BaseMobSpawner::loadDataAndAddEntity(shared_ptr<Entity> entity)
{
if (getNextSpawnData() != NULL)
{
CompoundTag *data = new CompoundTag();
entity->save(data);
vector<Tag *> *tags = getNextSpawnData()->tag->getAllTags();
if ( tags )
{
for (auto& tag : *tags)
{
data->put(tag->getName(), tag->copy());
}
delete tags;
}
entity->load(data);
if (entity->level != NULL) entity->level->addEntity(entity);
// add mounts
shared_ptr<Entity> rider = entity;
while (data->contains(Entity::RIDING_TAG))
{
CompoundTag *ridingTag = data->getCompound(Entity::RIDING_TAG);
shared_ptr<Entity> mount = EntityIO::newEntity(ridingTag->getString(L"id"), entity->level);
if (mount != NULL)
{
CompoundTag *mountData = new CompoundTag();
mount->save(mountData);
vector<Tag *> *ridingTags = ridingTag->getAllTags();
if ( ridingTags )
{
for (auto& tag : *ridingTags)
{
mountData->put(tag->getName(), tag->copy());
}
delete ridingTags;
}
mount->load(mountData);
mount->moveTo(rider->x, rider->y, rider->z, rider->yRot, rider->xRot);
if (entity->level != NULL) entity->level->addEntity(mount);
rider->ride(mount);
}
rider = mount;
data = ridingTag;
}
}
else if (entity->instanceof(eTYPE_LIVINGENTITY) && entity->level != NULL)
{
dynamic_pointer_cast<Mob>( entity )->finalizeMobSpawn(NULL);
getLevel()->addEntity(entity);
}
return entity;
}
void BaseMobSpawner::delay()
{
if (maxSpawnDelay <= minSpawnDelay)
{
spawnDelay = minSpawnDelay;
}
else
{
spawnDelay = minSpawnDelay + getLevel()->random->nextInt(maxSpawnDelay - minSpawnDelay);
}
if ( (spawnPotentials != NULL) && (spawnPotentials->size() > 0) )
{
setNextSpawnData( (SpawnData*) WeighedRandom::getRandomItem((Random*)getLevel()->random, (vector<WeighedRandomItem*>*)spawnPotentials) );
}
broadcastEvent(EVENT_SPAWN);
}
void BaseMobSpawner::load(CompoundTag *tag)
{
entityId = tag->getString(L"EntityId");
spawnDelay = tag->getShort(L"Delay");
if (tag->contains(L"SpawnPotentials"))
{
spawnPotentials = new vector<SpawnData *>();
ListTag<CompoundTag> *potentials = (ListTag<CompoundTag> *) tag->getList(L"SpawnPotentials");
for (int i = 0; i < potentials->size(); i++)
{
spawnPotentials->push_back(new SpawnData(potentials->get(i)));
}
}
else
{
spawnPotentials = NULL;
}
if (tag->contains(L"SpawnData"))
{
setNextSpawnData(new SpawnData(tag->getCompound(L"SpawnData"), entityId));
}
else
{
setNextSpawnData(NULL);
}
if (tag->contains(L"MinSpawnDelay"))
{
minSpawnDelay = tag->getShort(L"MinSpawnDelay");
maxSpawnDelay = tag->getShort(L"MaxSpawnDelay");
spawnCount = tag->getShort(L"SpawnCount");
}
if (tag->contains(L"MaxNearbyEntities"))
{
maxNearbyEntities = tag->getShort(L"MaxNearbyEntities");
requiredPlayerRange = tag->getShort(L"RequiredPlayerRange");
}
if (tag->contains(L"SpawnRange")) spawnRange = tag->getShort(L"SpawnRange");
if (getLevel() != NULL && getLevel()->isClientSide)
{
displayEntity = nullptr;
}
}
void BaseMobSpawner::save(CompoundTag *tag)
{
tag->putString(L"EntityId", getEntityId());
tag->putShort(L"Delay", (short) spawnDelay);
tag->putShort(L"MinSpawnDelay", (short) minSpawnDelay);
tag->putShort(L"MaxSpawnDelay", (short) maxSpawnDelay);
tag->putShort(L"SpawnCount", (short) spawnCount);
tag->putShort(L"MaxNearbyEntities", (short) maxNearbyEntities);
tag->putShort(L"RequiredPlayerRange", (short) requiredPlayerRange);
tag->putShort(L"SpawnRange", (short) spawnRange);
if (getNextSpawnData() != NULL)
{
tag->putCompound(L"SpawnData", (CompoundTag *) getNextSpawnData()->tag->copy());
}
if (getNextSpawnData() != NULL || (spawnPotentials != NULL && spawnPotentials->size() > 0))
{
ListTag<CompoundTag> *list = new ListTag<CompoundTag>();
if (spawnPotentials && spawnPotentials->size() > 0)
{
for ( auto& data : *spawnPotentials )
{
list->add(data->save());
}
}
else
{
list->add(getNextSpawnData()->save());
}
tag->put(L"SpawnPotentials", list);
}
}
shared_ptr<Entity> BaseMobSpawner::getDisplayEntity()
{
if (displayEntity == NULL)
{
shared_ptr<Entity> e = EntityIO::newEntity(getEntityId(), NULL);
e = loadDataAndAddEntity(e);
displayEntity = e;
}
return displayEntity;
}
bool BaseMobSpawner::onEventTriggered(int id)
{
if (id == EVENT_SPAWN && getLevel()->isClientSide)
{
spawnDelay = minSpawnDelay;
return true;
}
return false;
}
BaseMobSpawner::SpawnData *BaseMobSpawner::getNextSpawnData()
{
return nextSpawnData;
}
void BaseMobSpawner::setNextSpawnData(SpawnData *nextSpawnData)
{
this->nextSpawnData = nextSpawnData;
}
BaseMobSpawner::SpawnData::SpawnData(CompoundTag *base) : WeighedRandomItem(base->getInt(L"Weight"))
{
CompoundTag *tag = base->getCompound(L"Properties");
wstring _type = base->getString(L"Type");
if (_type.compare(L"Minecart") == 0)
{
if (tag != NULL)
{
switch (tag->getInt(L"Type"))
{
case Minecart::TYPE_CHEST:
type = L"MinecartChest";
break;
case Minecart::TYPE_FURNACE:
type = L"MinecartFurnace";
break;
case Minecart::TYPE_RIDEABLE:
type = L"MinecartRideable";
break;
}
}
else
{
type = L"MinecartRideable";
}
}
this->tag = tag;
this->type = _type;
}
BaseMobSpawner::SpawnData::SpawnData(CompoundTag *tag, wstring _type) : WeighedRandomItem(1)
{
if (_type.compare(L"Minecart") == 0)
{
if (tag != NULL)
{
switch (tag->getInt(L"Type"))
{
case Minecart::TYPE_CHEST:
_type = L"MinecartChest";
break;
case Minecart::TYPE_FURNACE:
_type = L"MinecartFurnace";
break;
case Minecart::TYPE_RIDEABLE:
_type = L"MinecartRideable";
break;
}
}
else
{
_type = L"MinecartRideable";
}
}
this->tag = tag;
this->type = _type;
}
BaseMobSpawner::SpawnData::~SpawnData()
{
delete tag;
}
CompoundTag *BaseMobSpawner::SpawnData::save()
{
CompoundTag *result = new CompoundTag();
result->putCompound(L"Properties", tag);
result->putString(L"Type", type);
result->putInt(L"Weight", randomWeight);
return result;
}