4jcraft/Minecraft.World/Level/BaseMobSpawner.cpp
2026-03-21 17:07:51 -05:00

362 lines
11 KiB
C++

#include "../Platform/stdafx.h"
#include "../Headers/net.minecraft.world.entity.h"
#include "../Headers/net.minecraft.world.entity.item.h"
#include "../Headers/net.minecraft.world.level.h"
#include "../Headers/net.minecraft.world.level.tile.h"
#include "../Headers/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_VAR(it, spawnPotentials->begin());
it != spawnPotentials->end(); ++it) {
delete *it;
}
delete spawnPotentials;
}
}
std::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 std::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++) {
std::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;
std::shared_ptr<Mob> mob =
entity->instanceof(eTYPE_MOB)
? std::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();
}
}
std::shared_ptr<Entity> BaseMobSpawner::loadDataAndAddEntity(
std::shared_ptr<Entity> entity) {
if (getNextSpawnData() != NULL) {
CompoundTag* data = new CompoundTag();
entity->save(data);
std::vector<Tag*>* tags = getNextSpawnData()->tag->getAllTags();
for (AUTO_VAR(it, tags->begin()); it != tags->end(); ++it) {
Tag* tag = *it;
data->put(tag->getName(), tag->copy());
}
delete tags;
entity->load(data);
if (entity->level != NULL) entity->level->addEntity(entity);
// add mounts
std::shared_ptr<Entity> rider = entity;
while (data->contains(Entity::RIDING_TAG)) {
CompoundTag* ridingTag = data->getCompound(Entity::RIDING_TAG);
std::shared_ptr<Entity> mount =
EntityIO::newEntity(ridingTag->getString(L"id"), entity->level);
if (mount != NULL) {
CompoundTag* mountData = new CompoundTag();
mount->save(mountData);
std::vector<Tag*>* ridingTags = ridingTag->getAllTags();
for (AUTO_VAR(it, ridingTags->begin()); it != ridingTags->end();
++it) {
Tag* tag = *it;
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) {
std::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,
(std::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 std::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 != NULL && spawnPotentials->size() > 0) {
for (AUTO_VAR(it, spawnPotentials->begin());
it != spawnPotentials->end(); ++it) {
SpawnData* data = *it;
list->add(data->save());
}
} else {
list->add(getNextSpawnData()->save());
}
tag->put(L"SpawnPotentials", list);
}
}
std::shared_ptr<Entity> BaseMobSpawner::getDisplayEntity() {
if (displayEntity == NULL) {
std::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");
std::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, std::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;
}