From 60fecbca993389bedc7617a2de35a26cfdf8ccc4 Mon Sep 17 00:00:00 2001 From: YOUR_NAME Date: Sat, 6 Dec 2025 02:24:05 -0500 Subject: [PATCH 1/7] c++03 compliant natural mob spawner --- source/CMakeLists.txt | 1 + source/client/app/NinecraftApp.cpp | 2 + source/world/entity/MobCategory.cpp | 2 +- source/world/entity/MobFactory.cpp | 28 ++++ source/world/entity/MobFactory.hpp | 6 +- source/world/entity/MobSpawner.cpp | 201 ++++++++++++++++++++++++++++ source/world/entity/MobSpawner.hpp | 29 ++++ source/world/level/Level.cpp | 16 +++ source/world/level/Level.hpp | 7 + 9 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 source/world/entity/MobSpawner.cpp create mode 100644 source/world/entity/MobSpawner.hpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 63c4dc5f9..a4476dc53 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -236,6 +236,7 @@ add_library(reminecraftpe-core STATIC world/entity/Rocket.cpp world/entity/EntityFactory.cpp world/entity/MobFactory.cpp + world/entity/MobSpawner.cpp world/entity/Chicken.cpp world/entity/Cow.cpp world/entity/Creeper.cpp diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp index a023def62..d2b226392 100644 --- a/source/client/app/NinecraftApp.cpp +++ b/source/client/app/NinecraftApp.cpp @@ -9,6 +9,7 @@ #include "NinecraftApp.hpp" #include "world/item/Item.hpp" #include "world/entity/MobCategory.hpp" +#include "world/entity/MobFactory.hpp" #include "client/player/input/Multitouch.hpp" #include "client/gui/screens/StartMenuScreen.hpp" #include "client/renderer/FoliageColor.hpp" @@ -232,6 +233,7 @@ void NinecraftApp::init() Material::initMaterials(); EntityTypeDescriptor::initDescriptors(); // custom MobCategory::initMobCategories(); + MobFactory::initMobLists(); Tile::initTiles(); Item::initItems(); Biome::initBiomes(); diff --git a/source/world/entity/MobCategory.cpp b/source/world/entity/MobCategory.cpp index 96f8d8779..065a08bb0 100644 --- a/source/world/entity/MobCategory.cpp +++ b/source/world/entity/MobCategory.cpp @@ -8,7 +8,7 @@ MobCategory MobCategory::waterCreature = MobCategory(EntityCategories(EntityCate const MobCategory MobCategory::values[] = { MobCategory::monster, MobCategory::creature, - MobCategory::waterCreature + //MobCategory::waterCreature }; const int MobCategory::numValues = sizeof(MobCategory::values) / sizeof(MobCategory); diff --git a/source/world/entity/MobFactory.cpp b/source/world/entity/MobFactory.cpp index afa536d7d..4bcd911e7 100644 --- a/source/world/entity/MobFactory.cpp +++ b/source/world/entity/MobFactory.cpp @@ -24,6 +24,13 @@ #define ENT(enumType, classType) case EntityType::enumType: return new classType(level); + + +std::map monsterList; +std::map creatureList; +std::map waterCreatureList; +std::map nullCreatureList; + Mob* MobFactory::CreateMob(EntityType::ID entityType, Level *level) { switch (entityType) @@ -35,4 +42,25 @@ Mob* MobFactory::CreateMob(EntityType::ID entityType, Level *level) } } +void MobFactory::initMobLists() { + monsterList.insert(std::make_pair(EntityType::SPIDER, 10)); + monsterList.insert(std::make_pair(EntityType::ZOMBIE, 10)); + monsterList.insert(std::make_pair(EntityType::SKELETON, 10)); + monsterList.insert(std::make_pair(EntityType::CREEPER, 10)); + //monsterList.insert(std::make_pair(EntityType::SLIME, 10)); + + creatureList.insert(std::make_pair(EntityType::SHEEP, 12)); + creatureList.insert(std::make_pair(EntityType::PIG, 10)); + creatureList.insert(std::make_pair(EntityType::CHICKEN, 10)); + creatureList.insert(std::make_pair(EntityType::COW, 8)); + + waterCreatureList.insert(std::make_pair(EntityType::SQUID, 10)); +} + +std::map MobFactory::GetMobListOfCategory(EntityCategories::CategoriesMask category) { + return category == EntityCategories::MONSTER ? monsterList : + category == EntityCategories::ANIMAL ? creatureList : + nullCreatureList; +} + #undef ENT \ No newline at end of file diff --git a/source/world/entity/MobFactory.hpp b/source/world/entity/MobFactory.hpp index 0a9a078aa..170428aae 100644 --- a/source/world/entity/MobFactory.hpp +++ b/source/world/entity/MobFactory.hpp @@ -3,8 +3,12 @@ #include "EntityType.hpp" #include "Mob.hpp" +class MobCategory; + class MobFactory { -public: +public: + static void initMobLists(); static Mob* CreateMob(EntityType::ID entityType, Level *level); + static std::map GetMobListOfCategory(EntityCategories::CategoriesMask category); }; \ No newline at end of file diff --git a/source/world/entity/MobSpawner.cpp b/source/world/entity/MobSpawner.cpp new file mode 100644 index 000000000..5c6e044f8 --- /dev/null +++ b/source/world/entity/MobSpawner.cpp @@ -0,0 +1,201 @@ + +#include "world/entity/MobSpawner.hpp" +#include "world/entity/MobFactory.hpp" + +#define MOB_SPAWNER_HOSTILE_BRIGHTNESS 7 + +void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) +{ + if (!allowHostile && !allowFriendly) + return; + + chunksToPoll.clear(); + + for (std::vector::const_iterator it = level->m_players.begin(); it != level->m_players.end(); ++it) + { + Player* player = *it; + int cx = Mth::floor(player->m_pos.x / 16.0f); + int cz = Mth::floor(player->m_pos.z / 16.0f); + + for (int dx = -8; dx <= 8; ++dx) { + for (int dz = -8; dz <= 8; ++dz) { + chunksToPoll.insert(ChunkPos(cx + dx, cz + dz)); + } + } + } + + int totalSpawned = 0; + + for (int i = 0; i < MobCategory::numValues; i++) + { + MobCategory* pCategory = nullptr; + EntityCategories::CategoriesMask mask; + switch (i) { + case 0: + pCategory = &MobCategory::monster; + mask = EntityCategories::MONSTER; + break; + case 1: + pCategory = &MobCategory::creature; + mask = EntityCategories::ANIMAL; + break; + default: + continue; + } + + if ((level->getTime() % 12000 - 12000) >= 0 && pCategory->isFriendly()) + continue; + + if ((!pCategory->isFriendly() || allowFriendly) && (pCategory->isFriendly() || allowHostile) + && level->getEntityCountOfCategory(mask) <= (pCategory->getMaxInstancesPerChunk() * (int)chunksToPoll.size() / 256)) + { + for (std::set::iterator it = chunksToPoll.begin(); it != chunksToPoll.end(); ++it) { + ChunkPos pos = *it; + + std::map spawnList = MobFactory::GetMobListOfCategory(mask); + + assert(!spawnList.empty()); + + if (spawnList.empty()) + continue; + + EntityType::ID type = spawnList.begin()->first; + + int spawnWeight = 1; // make sure it starts with 1 so arithmetic exception doesn't occur + + for (std::map::iterator it = spawnList.begin(); it != spawnList.end(); ++it) + { + spawnWeight += it->second; + } + + int randomRate = level->m_random.nextInt(spawnWeight); + + for (std::map::iterator it = spawnList.begin(); it != spawnList.end(); ++it) + { + randomRate -= it->second; + if (randomRate < 0) { + type = it->first; + break; + } + } + + int idx = level->m_random.nextInt((int)spawnList.size()); + TilePos tpos = getRandomPosWithin(level, pos.x * 16, pos.z * 16); + + if (level->isSolidTile(tpos) || level->getMaterial(tpos) != pCategory->getSpawnPositionMaterial()) + continue; + + int spawned = 0; + for (int i = 0; i < 3; ++i) + { + TilePos tp(tpos); + + if (spawned == -1) + break; + + for (int j = 0; j < 4; ++j) { + tp.x += level->m_random.nextInt(6) - level->m_random.nextInt(6); + tp.y += level->m_random.nextInt(1) - level->m_random.nextInt(1); + tp.z += level->m_random.nextInt(6) - level->m_random.nextInt(6); + + if (IsSpawnPositionOk(pCategory, level, tp)) { + Vec3 pPos(tp.x + 0.5, tp.y, tp.z + 0.5); + + if (!level->getNearestPlayer(pPos, 24.0f, false)) { + Vec3 dPos = pPos - level->getSharedSpawnPos(); + if (dPos.lengthSqr() >= 576.0f) { + + + Mob* entity = MobFactory::CreateMob(type, level); + if (!entity) break; + + entity->moveTo(pPos, Vec2(level->m_random.nextFloat() * 360.0f, 0.0f)); + if (entity->canSpawn()) { + ++spawned; + level->addEntity(entity); + FinalizeMobSettings(entity, level, pPos); + if (spawned >= entity->getMaxSpawnClusterSize()) + { + totalSpawned += spawned; + spawned = -1; + break; + } + } + } + } + } + } + } + + if (spawned != -1) + totalSpawned += spawned; + } + } + } + + return; // totalSpawned; +} + +TilePos MobSpawner::getRandomPosWithin(Level *level, int chunkX, int chunkZ) +{ + int px = chunkX + level->m_random.nextInt(16); + int py = level->m_random.nextInt(128); + int pz = chunkZ + level->m_random.nextInt(16); + return TilePos(px, py, pz); +} + +//todo: bool? +int MobSpawner::AddMob(Level *level, Mob *mob, const Vec3& pos, const Vec2& rot) +{ + if (!level || !mob) + return 0; + + if (!mob->canSpawn() || !mob->isAlive()) + return 0; + + mob->moveTo(pos, rot); + level->addEntity(mob); + FinalizeMobSettings(mob, level, pos); + + return 1; +} + +bool MobSpawner::IsSpawnPositionOk(MobCategory *category, Level *level, const TilePos& pos) +{ + if (!level->isEmptyTile(pos)) + return false; + + if (category->getSpawnPositionMaterial() == Material::water) + return level->getMaterial(pos)->isLiquid() && !level->isSolidTile(pos.above()); + + if (!category->isFriendly() && level->getRawBrightness(pos) >= MOB_SPAWNER_HOSTILE_BRIGHTNESS) + return false; + + return level->isSolidTile(pos.below()) && !level->isSolidTile(pos) && !level->getMaterial(pos)->isLiquid() && !level->isSolidTile(pos.above()); +} + +void MobSpawner::FinalizeMobSettings(Mob *mob, Level *level, const Vec3& pos) +{ + if (!level || !mob) + return; + + //mob->finalizeMobSpawn(); + MakeBabyMob(mob, level); +} + + +void MobSpawner::MakeBabyMob(Mob *mob, Level *level) +{ + level->m_random.setSeed(0x5deea8f); + + if (mob->isBaby()) + return; + + // todo +} + + +void MobSpawner::PostProcessSpawnMobs(Level *level, Biome *biome, const Vec3& pos) +{ + +} \ No newline at end of file diff --git a/source/world/entity/MobSpawner.hpp b/source/world/entity/MobSpawner.hpp new file mode 100644 index 000000000..50476b2d1 --- /dev/null +++ b/source/world/entity/MobSpawner.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "world/tile/Tile.hpp" +#include "world/entity/Entity.hpp" +#include "world/entity/MobCategory.hpp" +#include "world/level/Level.hpp" +#include "world/level/levelgen/chunk/LevelChunk.hpp" +#include "world/level/levelgen/chunk/ChunkSource.hpp" +#include "world/level/storage/LevelStorageSource.hpp" +#include "world/level/storage/LevelSource.hpp" +#include "world/level/storage/LevelData.hpp" +#include "world/level/path/PathFinder.hpp" + +class MobSpawner { +public: + static bool IsSpawnPositionOk(MobCategory *category, Level *level, const TilePos& pos); + static void FinalizeMobSettings(Mob *mob, Level *level, const Vec3& pos); + static void MakeBabyMob(Mob *mob, Level *level); + static void PostProcessSpawnMobs(Level *level, Biome *biome, const Vec3& pos); + static int AddMob(Level *level, Mob *mob, const Vec3& pos, const Vec2& rot = Vec2::ZERO); + + TilePos getRandomPosWithin(Level *level, int chunkX, int chunkZ); + void tick(Level *level, bool allowHostile, bool allowFriendly); +private: + + std::set chunksToPoll; +}; \ No newline at end of file diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index 36a1bc16f..55bfe7145 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -62,6 +62,7 @@ Level::Level(LevelStorage* pStor, const std::string& name, const LevelSettings& m_pDimension->init(this); m_pPathFinder = new PathFinder(); + m_pMobSpawner = new MobSpawner(); m_pChunkSource = createChunkSource(); updateSkyBrightness(); @@ -72,6 +73,7 @@ Level::~Level() SAFE_DELETE(m_pChunkSource); SAFE_DELETE(m_pDimension); SAFE_DELETE(m_pPathFinder); + SAFE_DELETE(m_pMobSpawner); const size_t size = m_entities.size(); for (int i = 0; i < size; i++) @@ -1590,6 +1592,7 @@ int LASTTICKED = 0; void Level::tick() { + m_pMobSpawner->tick(this, m_difficulty > 0, true); m_pChunkSource->tick(); #ifdef ENH_RUN_DAY_NIGHT_CYCLE @@ -1939,3 +1942,16 @@ float Level::getSunAngle(float f) const { return (float(M_PI) * getTimeOfDay(f)) * 2; } + +int Level::getEntityCountOfCategory(EntityCategories::CategoriesMask category) const +{ + int count = 0; + for (std::vector::const_iterator it = m_entities.begin(); it != m_entities.end(); it++) + { + Entity* pEnt = *it; + if (pEnt && pEnt->getDescriptor().getCategories().contains(category) ) + count++; + + } + return count; +} diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp index 62c5554d0..a66426b17 100644 --- a/source/world/level/Level.hpp +++ b/source/world/level/Level.hpp @@ -17,6 +17,7 @@ #include "client/renderer/LightUpdate.hpp" #include "world/tile/Tile.hpp" #include "world/entity/Entity.hpp" +#include "world/entity/MobSpawner.hpp" #include "world/level/TileChange.hpp" #include "world/level/levelgen/chunk/LevelChunk.hpp" #include "world/level/levelgen/chunk/ChunkSource.hpp" @@ -34,6 +35,8 @@ class Level; class LevelListener; class RakNetInstance; +class MobSpawner; + typedef std::vector EntityVector; typedef std::vector AABBVector; @@ -190,6 +193,9 @@ class Level : public LevelSource bool hasDirectSignal(const TilePos& pos) const; bool hasNeighborSignal(const TilePos& pos) const; + + int getEntityCountOfCategory(EntityCategories::CategoriesMask) const; + #ifdef ENH_IMPROVED_SAVING void saveUnsavedChunks(); #endif @@ -227,5 +233,6 @@ class Level : public LevelSource uint8_t field_B0C; int field_B10; PathFinder* m_pPathFinder; + MobSpawner* m_pMobSpawner; }; From 621dfcabbc09e6bc39a1208785c3e0b5cd9b0243 Mon Sep 17 00:00:00 2001 From: "rep.stosw" Date: Sat, 20 Dec 2025 10:38:40 -0500 Subject: [PATCH 2/7] mob spawner patch (without crafting) --- source/world/entity/MobSpawner.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/source/world/entity/MobSpawner.cpp b/source/world/entity/MobSpawner.cpp index 5c6e044f8..ed9f209a1 100644 --- a/source/world/entity/MobSpawner.cpp +++ b/source/world/entity/MobSpawner.cpp @@ -28,6 +28,13 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) for (int i = 0; i < MobCategory::numValues; i++) { + /* + Note: + MobCategory::values does not work, we may want to refactor it so it initializes during static initialization + pCategory->getBaseTyoe() returns bad data, hence a seeparate CategoriesMask is used + + i have no idea why above happens, blame c++! + */ MobCategory* pCategory = nullptr; EntityCategories::CategoriesMask mask; switch (i) { @@ -162,15 +169,21 @@ int MobSpawner::AddMob(Level *level, Mob *mob, const Vec3& pos, const Vec2& rot) bool MobSpawner::IsSpawnPositionOk(MobCategory *category, Level *level, const TilePos& pos) { + int brightness = level->getRawBrightness(pos); + if (!level->isEmptyTile(pos)) return false; - if (category->getSpawnPositionMaterial() == Material::water) - return level->getMaterial(pos)->isLiquid() && !level->isSolidTile(pos.above()); + if (!category->isFriendly() && brightness >= MOB_SPAWNER_HOSTILE_BRIGHTNESS) + return false; - if (!category->isFriendly() && level->getRawBrightness(pos) >= MOB_SPAWNER_HOSTILE_BRIGHTNESS) + if (category->isFriendly() && brightness < MOB_SPAWNER_HOSTILE_BRIGHTNESS) return false; + + if (category->getSpawnPositionMaterial() == Material::water) + return level->getMaterial(pos)->isLiquid() && !level->isSolidTile(pos.above()); + return level->isSolidTile(pos.below()) && !level->isSolidTile(pos) && !level->getMaterial(pos)->isLiquid() && !level->isSolidTile(pos.above()); } From 433f76d6efab4d8ec085392a022d7e0aee6346d9 Mon Sep 17 00:00:00 2001 From: "rep.stosw" Date: Sat, 20 Dec 2025 11:33:27 -0500 Subject: [PATCH 3/7] make brent happy --- source/world/entity/MobCategory.cpp | 12 +++++ source/world/entity/MobCategory.hpp | 5 +- source/world/entity/MobSpawner.cpp | 83 ++++++++++++++--------------- 3 files changed, 56 insertions(+), 44 deletions(-) diff --git a/source/world/entity/MobCategory.cpp b/source/world/entity/MobCategory.cpp index 065a08bb0..731567b61 100644 --- a/source/world/entity/MobCategory.cpp +++ b/source/world/entity/MobCategory.cpp @@ -26,4 +26,16 @@ void MobCategory::initMobCategories() MobCategory::monster.m_pSpawnPositionMaterial = Material::air; MobCategory::creature.m_pSpawnPositionMaterial = Material::air; MobCategory::waterCreature.m_pSpawnPositionMaterial = Material::water; +} + +MobCategory& MobCategory::GetCategoryByIndex(int i) +{ + switch (i) + { + case 1: + return MobCategory::creature; + case 0: + default: + return MobCategory::monster; + } } \ No newline at end of file diff --git a/source/world/entity/MobCategory.hpp b/source/world/entity/MobCategory.hpp index d90b6c4b3..73e3a23fb 100644 --- a/source/world/entity/MobCategory.hpp +++ b/source/world/entity/MobCategory.hpp @@ -24,8 +24,11 @@ class MobCategory const Material* getSpawnPositionMaterial() const { return m_pSpawnPositionMaterial; } bool isFriendly() const { return m_bIsFriendly; } + // custom addition + static MobCategory& GetCategoryByIndex(int i); + private: - const EntityCategories& m_baseType; + const EntityCategories m_baseType; int field_4; int m_maxInstancesPerChunk; const Material* m_pSpawnPositionMaterial; diff --git a/source/world/entity/MobSpawner.cpp b/source/world/entity/MobSpawner.cpp index ed9f209a1..6cd2d36a5 100644 --- a/source/world/entity/MobSpawner.cpp +++ b/source/world/entity/MobSpawner.cpp @@ -3,6 +3,7 @@ #include "world/entity/MobFactory.hpp" #define MOB_SPAWNER_HOSTILE_BRIGHTNESS 7 +#define MOB_SPAWNER_FRIENDLY_BRIGHTNESS 9 void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) { @@ -17,8 +18,10 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) int cx = Mth::floor(player->m_pos.x / 16.0f); int cz = Mth::floor(player->m_pos.z / 16.0f); - for (int dx = -8; dx <= 8; ++dx) { - for (int dz = -8; dz <= 8; ++dz) { + for (int dx = -8; dx <= 8; ++dx) + { + for (int dz = -8; dz <= 8; ++dz) + { chunksToPoll.insert(ChunkPos(cx + dx, cz + dz)); } } @@ -28,35 +31,21 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) for (int i = 0; i < MobCategory::numValues; i++) { - /* - Note: - MobCategory::values does not work, we may want to refactor it so it initializes during static initialization - pCategory->getBaseTyoe() returns bad data, hence a seeparate CategoriesMask is used - - i have no idea why above happens, blame c++! - */ - MobCategory* pCategory = nullptr; - EntityCategories::CategoriesMask mask; - switch (i) { - case 0: - pCategory = &MobCategory::monster; - mask = EntityCategories::MONSTER; - break; - case 1: - pCategory = &MobCategory::creature; - mask = EntityCategories::ANIMAL; - break; - default: - continue; - } + MobCategory& category = MobCategory::getCategoryByIndex(i); + const EntityCategories::CategoriesMask& mask = category.getBaseType().getCategoryMask(); + bool isFriendly = category.isFriendly(); + + // good mobs don't spawn after dark, otherwise they will crowd around torches like beta + if (!level->isDay() && isFriendly) + continue; - if ((level->getTime() % 12000 - 12000) >= 0 && pCategory->isFriendly()) + if ((isFriendly && !allowFriendly) || (!isFriendly && !allowHostile)) continue; - if ((!pCategory->isFriendly() || allowFriendly) && (pCategory->isFriendly() || allowHostile) - && level->getEntityCountOfCategory(mask) <= (pCategory->getMaxInstancesPerChunk() * (int)chunksToPoll.size() / 256)) + if (level->getEntityCountOfCategory(mask) <= (category.getMaxInstancesPerChunk() * (int)chunksToPoll.size() / 256)) { - for (std::set::iterator it = chunksToPoll.begin(); it != chunksToPoll.end(); ++it) { + for (std::set::iterator it = chunksToPoll.begin(); it != chunksToPoll.end(); ++it) + { ChunkPos pos = *it; std::map spawnList = MobFactory::GetMobListOfCategory(mask); @@ -80,7 +69,8 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) for (std::map::iterator it = spawnList.begin(); it != spawnList.end(); ++it) { randomRate -= it->second; - if (randomRate < 0) { + if (randomRate < 0) + { type = it->first; break; } @@ -89,7 +79,7 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) int idx = level->m_random.nextInt((int)spawnList.size()); TilePos tpos = getRandomPosWithin(level, pos.x * 16, pos.z * 16); - if (level->isSolidTile(tpos) || level->getMaterial(tpos) != pCategory->getSpawnPositionMaterial()) + if (level->isSolidTile(tpos) || level->getMaterial(tpos) != category.getSpawnPositionMaterial()) continue; int spawned = 0; @@ -100,24 +90,30 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) if (spawned == -1) break; - for (int j = 0; j < 4; ++j) { + for (int j = 0; j < 4; ++j) + { tp.x += level->m_random.nextInt(6) - level->m_random.nextInt(6); tp.y += level->m_random.nextInt(1) - level->m_random.nextInt(1); tp.z += level->m_random.nextInt(6) - level->m_random.nextInt(6); - if (IsSpawnPositionOk(pCategory, level, tp)) { + if (IsSpawnPositionOk(&category, level, tp)) + { Vec3 pPos(tp.x + 0.5, tp.y, tp.z + 0.5); - if (!level->getNearestPlayer(pPos, 24.0f, false)) { + if (!level->getNearestPlayer(pPos, 24.0f, false)) + { Vec3 dPos = pPos - level->getSharedSpawnPos(); - if (dPos.lengthSqr() >= 576.0f) { + if (dPos.lengthSqr() >= 576.0f) + { Mob* entity = MobFactory::CreateMob(type, level); - if (!entity) break; + if (!entity) + break; entity->moveTo(pPos, Vec2(level->m_random.nextFloat() * 360.0f, 0.0f)); - if (entity->canSpawn()) { + if (entity->canSpawn()) + { ++spawned; level->addEntity(entity); FinalizeMobSettings(entity, level, pPos); @@ -140,14 +136,13 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) } } - return; // totalSpawned; } TilePos MobSpawner::getRandomPosWithin(Level *level, int chunkX, int chunkZ) { - int px = chunkX + level->m_random.nextInt(16); + int px = level->m_random.nextInt(16) + chunkX; int py = level->m_random.nextInt(128); - int pz = chunkZ + level->m_random.nextInt(16); + int pz = level->m_random.nextInt(16) + chunkZ; return TilePos(px, py, pz); } @@ -174,12 +169,14 @@ bool MobSpawner::IsSpawnPositionOk(MobCategory *category, Level *level, const Ti if (!level->isEmptyTile(pos)) return false; - if (!category->isFriendly() && brightness >= MOB_SPAWNER_HOSTILE_BRIGHTNESS) - return false; - - if (category->isFriendly() && brightness < MOB_SPAWNER_HOSTILE_BRIGHTNESS) + if (!category->isFriendly() && brightness > MOB_SPAWNER_HOSTILE_BRIGHTNESS) return false; + if (category->isFriendly()) + { + if(brightness < MOB_SPAWNER_FRIENDLY_BRIGHTNESS || level->getTile(pos.below()) != TILE_GRASS) + return false; + } if (category->getSpawnPositionMaterial() == Material::water) return level->getMaterial(pos)->isLiquid() && !level->isSolidTile(pos.above()); @@ -210,5 +207,5 @@ void MobSpawner::MakeBabyMob(Mob *mob, Level *level) void MobSpawner::PostProcessSpawnMobs(Level *level, Biome *biome, const Vec3& pos) { - + // empty (0.7.1) } \ No newline at end of file From 427122e8f95f35e51ffec4e94a0732a7b761f271 Mon Sep 17 00:00:00 2001 From: "rep.stosw" Date: Sat, 20 Dec 2025 11:42:50 -0500 Subject: [PATCH 4/7] oops --- source/world/entity/MobSpawner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/world/entity/MobSpawner.cpp b/source/world/entity/MobSpawner.cpp index 6cd2d36a5..c6cdc62f1 100644 --- a/source/world/entity/MobSpawner.cpp +++ b/source/world/entity/MobSpawner.cpp @@ -31,7 +31,7 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) for (int i = 0; i < MobCategory::numValues; i++) { - MobCategory& category = MobCategory::getCategoryByIndex(i); + MobCategory& category = MobCategory::GetCategoryByIndex(i); const EntityCategories::CategoriesMask& mask = category.getBaseType().getCategoryMask(); bool isFriendly = category.isFriendly(); From abff8ea11ddc1f0260b6d3bdfc5c74f8d9aedc4f Mon Sep 17 00:00:00 2001 From: "rep.stosw" Date: Sat, 20 Dec 2025 22:12:19 -0500 Subject: [PATCH 5/7] make brent happy --- source/world/entity/MobCategory.cpp | 6 +- source/world/entity/MobCategory.hpp | 9 ++- source/world/entity/MobFactory.cpp | 6 +- source/world/entity/MobFactory.hpp | 2 +- source/world/entity/MobSpawner.cpp | 98 ++++++++++++----------------- source/world/entity/MobSpawner.hpp | 14 ++--- source/world/level/Level.cpp | 21 +++---- source/world/level/Level.hpp | 7 ++- 8 files changed, 79 insertions(+), 84 deletions(-) diff --git a/source/world/entity/MobCategory.cpp b/source/world/entity/MobCategory.cpp index 731567b61..0d0a08222 100644 --- a/source/world/entity/MobCategory.cpp +++ b/source/world/entity/MobCategory.cpp @@ -28,13 +28,13 @@ void MobCategory::initMobCategories() MobCategory::waterCreature.m_pSpawnPositionMaterial = Material::water; } -MobCategory& MobCategory::GetCategoryByIndex(int i) +MobCategory& MobCategory::GetCategoryByIndex(MobCategory::ID i) { switch (i) { - case 1: + case MobCategory::CREATURE: return MobCategory::creature; - case 0: + case MobCategory::MONSTER: default: return MobCategory::monster; } diff --git a/source/world/entity/MobCategory.hpp b/source/world/entity/MobCategory.hpp index 73e3a23fb..f7cfefc2b 100644 --- a/source/world/entity/MobCategory.hpp +++ b/source/world/entity/MobCategory.hpp @@ -6,6 +6,13 @@ class Material; class MobCategory { public: + enum ID { + MONSTER, + CREATURE, + // WATER_CREATURE + }; + + static MobCategory monster; static MobCategory creature; static MobCategory waterCreature; @@ -25,7 +32,7 @@ class MobCategory bool isFriendly() const { return m_bIsFriendly; } // custom addition - static MobCategory& GetCategoryByIndex(int i); + static MobCategory& GetCategoryByIndex(MobCategory::ID i); private: const EntityCategories m_baseType; diff --git a/source/world/entity/MobFactory.cpp b/source/world/entity/MobFactory.cpp index 4bcd911e7..2176d2508 100644 --- a/source/world/entity/MobFactory.cpp +++ b/source/world/entity/MobFactory.cpp @@ -25,7 +25,7 @@ #define ENT(enumType, classType) case EntityType::enumType: return new classType(level); - +// format: ID, spawnrate std::map monsterList; std::map creatureList; std::map waterCreatureList; @@ -43,6 +43,8 @@ Mob* MobFactory::CreateMob(EntityType::ID entityType, Level *level) } void MobFactory::initMobLists() { + // format: ID, spawnrate + monsterList.insert(std::make_pair(EntityType::SPIDER, 10)); monsterList.insert(std::make_pair(EntityType::ZOMBIE, 10)); monsterList.insert(std::make_pair(EntityType::SKELETON, 10)); @@ -57,7 +59,7 @@ void MobFactory::initMobLists() { waterCreatureList.insert(std::make_pair(EntityType::SQUID, 10)); } -std::map MobFactory::GetMobListOfCategory(EntityCategories::CategoriesMask category) { +const std::map& MobFactory::GetMobListOfCategory(EntityCategories::CategoriesMask category) { return category == EntityCategories::MONSTER ? monsterList : category == EntityCategories::ANIMAL ? creatureList : nullCreatureList; diff --git a/source/world/entity/MobFactory.hpp b/source/world/entity/MobFactory.hpp index 170428aae..336d8d8da 100644 --- a/source/world/entity/MobFactory.hpp +++ b/source/world/entity/MobFactory.hpp @@ -10,5 +10,5 @@ class MobFactory public: static void initMobLists(); static Mob* CreateMob(EntityType::ID entityType, Level *level); - static std::map GetMobListOfCategory(EntityCategories::CategoriesMask category); + static const std::map& GetMobListOfCategory(EntityCategories::CategoriesMask category); }; \ No newline at end of file diff --git a/source/world/entity/MobSpawner.cpp b/source/world/entity/MobSpawner.cpp index c6cdc62f1..d80f50859 100644 --- a/source/world/entity/MobSpawner.cpp +++ b/source/world/entity/MobSpawner.cpp @@ -5,14 +5,14 @@ #define MOB_SPAWNER_HOSTILE_BRIGHTNESS 7 #define MOB_SPAWNER_FRIENDLY_BRIGHTNESS 9 -void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) +void MobSpawner::tick(Level& level, bool allowHostile, bool allowFriendly) { if (!allowHostile && !allowFriendly) return; chunksToPoll.clear(); - for (std::vector::const_iterator it = level->m_players.begin(); it != level->m_players.end(); ++it) + for (std::vector::const_iterator it = level.m_players.begin(); it != level.m_players.end(); ++it) { Player* player = *it; int cx = Mth::floor(player->m_pos.x / 16.0f); @@ -31,27 +31,25 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) for (int i = 0; i < MobCategory::numValues; i++) { - MobCategory& category = MobCategory::GetCategoryByIndex(i); + MobCategory& category = MobCategory::GetCategoryByIndex((MobCategory::ID)i); const EntityCategories::CategoriesMask& mask = category.getBaseType().getCategoryMask(); bool isFriendly = category.isFriendly(); // good mobs don't spawn after dark, otherwise they will crowd around torches like beta - if (!level->isDay() && isFriendly) + if (!level.isDay() && isFriendly) continue; if ((isFriendly && !allowFriendly) || (!isFriendly && !allowHostile)) continue; - if (level->getEntityCountOfCategory(mask) <= (category.getMaxInstancesPerChunk() * (int)chunksToPoll.size() / 256)) + if (level.countInstanceOfType(mask) <= (category.getMaxInstancesPerChunk() * (int)chunksToPoll.size() / 256)) { for (std::set::iterator it = chunksToPoll.begin(); it != chunksToPoll.end(); ++it) { - ChunkPos pos = *it; + const ChunkPos& pos = *it; - std::map spawnList = MobFactory::GetMobListOfCategory(mask); - - assert(!spawnList.empty()); - + const std::map& spawnList = MobFactory::GetMobListOfCategory(mask); + if (spawnList.empty()) continue; @@ -59,14 +57,14 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) int spawnWeight = 1; // make sure it starts with 1 so arithmetic exception doesn't occur - for (std::map::iterator it = spawnList.begin(); it != spawnList.end(); ++it) + for (std::map::const_iterator it = spawnList.begin(); it != spawnList.end(); ++it) { spawnWeight += it->second; } - int randomRate = level->m_random.nextInt(spawnWeight); + int randomRate = level.m_random.nextInt(spawnWeight); - for (std::map::iterator it = spawnList.begin(); it != spawnList.end(); ++it) + for (std::map::const_iterator it = spawnList.begin(); it != spawnList.end(); ++it) { randomRate -= it->second; if (randomRate < 0) @@ -76,10 +74,10 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) } } - int idx = level->m_random.nextInt((int)spawnList.size()); + int idx = level.m_random.nextInt((int)spawnList.size()); TilePos tpos = getRandomPosWithin(level, pos.x * 16, pos.z * 16); - if (level->isSolidTile(tpos) || level->getMaterial(tpos) != category.getSpawnPositionMaterial()) + if (level.isSolidTile(tpos) || level.getMaterial(tpos) != category.getSpawnPositionMaterial()) continue; int spawned = 0; @@ -92,30 +90,30 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) for (int j = 0; j < 4; ++j) { - tp.x += level->m_random.nextInt(6) - level->m_random.nextInt(6); - tp.y += level->m_random.nextInt(1) - level->m_random.nextInt(1); - tp.z += level->m_random.nextInt(6) - level->m_random.nextInt(6); + tp.x += level.m_random.nextInt(6) - level.m_random.nextInt(6); + tp.y += level.m_random.nextInt(1) - level.m_random.nextInt(1); + tp.z += level.m_random.nextInt(6) - level.m_random.nextInt(6); - if (IsSpawnPositionOk(&category, level, tp)) + if (IsSpawnPositionOk(category, level, tp)) { Vec3 pPos(tp.x + 0.5, tp.y, tp.z + 0.5); - if (!level->getNearestPlayer(pPos, 24.0f, false)) + if (!level.getNearestPlayer(pPos, 24.0f, false)) { - Vec3 dPos = pPos - level->getSharedSpawnPos(); + Vec3 dPos = pPos - level.getSharedSpawnPos(); + if (dPos.lengthSqr() >= 576.0f) - { - - - Mob* entity = MobFactory::CreateMob(type, level); + { + Mob* entity = MobFactory::CreateMob(type, &level); if (!entity) break; - entity->moveTo(pPos, Vec2(level->m_random.nextFloat() * 360.0f, 0.0f)); + entity->moveTo(pPos, Vec2(level.m_random.nextFloat() * 360.0f, 0.0f)); + if (entity->canSpawn()) { ++spawned; - level->addEntity(entity); + level.addEntity(entity); FinalizeMobSettings(entity, level, pPos); if (spawned >= entity->getMaxSpawnClusterSize()) { @@ -138,55 +136,41 @@ void MobSpawner::tick(Level *level, bool allowHostile, bool allowFriendly) } -TilePos MobSpawner::getRandomPosWithin(Level *level, int chunkX, int chunkZ) +TilePos MobSpawner::getRandomPosWithin(Level& level, int chunkX, int chunkZ) { - int px = level->m_random.nextInt(16) + chunkX; - int py = level->m_random.nextInt(128); - int pz = level->m_random.nextInt(16) + chunkZ; + int px = level.m_random.nextInt(16) + chunkX; + int py = level.m_random.nextInt(128); + int pz = level.m_random.nextInt(16) + chunkZ; return TilePos(px, py, pz); } //todo: bool? -int MobSpawner::AddMob(Level *level, Mob *mob, const Vec3& pos, const Vec2& rot) +int MobSpawner::AddMob(Level& level, Mob *mob, const Vec3& pos, const Vec2& rot) { - if (!level || !mob) + if (!mob) return 0; if (!mob->canSpawn() || !mob->isAlive()) return 0; mob->moveTo(pos, rot); - level->addEntity(mob); + level.addEntity(mob); FinalizeMobSettings(mob, level, pos); return 1; } -bool MobSpawner::IsSpawnPositionOk(MobCategory *category, Level *level, const TilePos& pos) +bool MobSpawner::IsSpawnPositionOk(MobCategory& category, Level& level, const TilePos& pos) { - int brightness = level->getRawBrightness(pos); - - if (!level->isEmptyTile(pos)) - return false; - - if (!category->isFriendly() && brightness > MOB_SPAWNER_HOSTILE_BRIGHTNESS) - return false; - - if (category->isFriendly()) - { - if(brightness < MOB_SPAWNER_FRIENDLY_BRIGHTNESS || level->getTile(pos.below()) != TILE_GRASS) - return false; - } - - if (category->getSpawnPositionMaterial() == Material::water) - return level->getMaterial(pos)->isLiquid() && !level->isSolidTile(pos.above()); + if (category.getSpawnPositionMaterial() == Material::water) + return level.getMaterial(pos)->isLiquid() && !level.isSolidTile(pos.above()); - return level->isSolidTile(pos.below()) && !level->isSolidTile(pos) && !level->getMaterial(pos)->isLiquid() && !level->isSolidTile(pos.above()); + return level.isSolidTile(pos.below()) && !level.isSolidTile(pos) && !level.getMaterial(pos)->isLiquid() && !level.isSolidTile(pos.above()); } -void MobSpawner::FinalizeMobSettings(Mob *mob, Level *level, const Vec3& pos) +void MobSpawner::FinalizeMobSettings(Mob *mob, Level& level, const Vec3& pos) { - if (!level || !mob) + if (!mob) return; //mob->finalizeMobSpawn(); @@ -194,9 +178,9 @@ void MobSpawner::FinalizeMobSettings(Mob *mob, Level *level, const Vec3& pos) } -void MobSpawner::MakeBabyMob(Mob *mob, Level *level) +void MobSpawner::MakeBabyMob(Mob *mob, Level& level) { - level->m_random.setSeed(0x5deea8f); + level.m_random.setSeed(0x5deea8f); if (mob->isBaby()) return; @@ -205,7 +189,7 @@ void MobSpawner::MakeBabyMob(Mob *mob, Level *level) } -void MobSpawner::PostProcessSpawnMobs(Level *level, Biome *biome, const Vec3& pos) +void MobSpawner::PostProcessSpawnMobs(Level& level, Biome& biome, const Vec3& pos) { // empty (0.7.1) } \ No newline at end of file diff --git a/source/world/entity/MobSpawner.hpp b/source/world/entity/MobSpawner.hpp index 50476b2d1..e7d74d740 100644 --- a/source/world/entity/MobSpawner.hpp +++ b/source/world/entity/MobSpawner.hpp @@ -15,14 +15,14 @@ class MobSpawner { public: - static bool IsSpawnPositionOk(MobCategory *category, Level *level, const TilePos& pos); - static void FinalizeMobSettings(Mob *mob, Level *level, const Vec3& pos); - static void MakeBabyMob(Mob *mob, Level *level); - static void PostProcessSpawnMobs(Level *level, Biome *biome, const Vec3& pos); - static int AddMob(Level *level, Mob *mob, const Vec3& pos, const Vec2& rot = Vec2::ZERO); + static bool IsSpawnPositionOk(MobCategory& category, Level& level, const TilePos& pos); + static void FinalizeMobSettings(Mob* mob, Level& level, const Vec3& pos); + static void MakeBabyMob(Mob* mob, Level& level); + static void PostProcessSpawnMobs(Level& level, Biome& biome, const Vec3& pos); + static int AddMob(Level& level, Mob* mob, const Vec3& pos, const Vec2& rot = Vec2::ZERO); - TilePos getRandomPosWithin(Level *level, int chunkX, int chunkZ); - void tick(Level *level, bool allowHostile, bool allowFriendly); + TilePos getRandomPosWithin(Level& level, int chunkX, int chunkZ); + void tick(Level& level, bool allowHostile, bool allowFriendly); private: std::set chunksToPoll; diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index 55bfe7145..02e0251a5 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -772,15 +772,22 @@ void Level::setTilesDirty(const TilePos& min, const TilePos& max) void Level::entityAdded(Entity* pEnt) { + EntityCategories::CategoriesMask mask = pEnt->getDescriptor().getCategories().getCategoryMask(); + m_entityTypeCounts[mask]++; + for (std::vector::iterator it = m_levelListeners.begin(); it != m_levelListeners.end(); it++) { LevelListener* pListener = *it; pListener->entityAdded(pEnt); } + } void Level::entityRemoved(Entity* pEnt) { + EntityCategories::CategoriesMask mask = pEnt->getDescriptor().getCategories().getCategoryMask(); + m_entityTypeCounts[mask]++; + for (std::vector::iterator it = m_levelListeners.begin(); it != m_levelListeners.end(); it++) { LevelListener* pListener = *it; @@ -1592,7 +1599,7 @@ int LASTTICKED = 0; void Level::tick() { - m_pMobSpawner->tick(this, m_difficulty > 0, true); + m_pMobSpawner->tick(*this, m_difficulty > 0, true); m_pChunkSource->tick(); #ifdef ENH_RUN_DAY_NIGHT_CYCLE @@ -1943,15 +1950,7 @@ float Level::getSunAngle(float f) const return (float(M_PI) * getTimeOfDay(f)) * 2; } -int Level::getEntityCountOfCategory(EntityCategories::CategoriesMask category) const +int Level::countInstanceOfType(EntityCategories::CategoriesMask category) { - int count = 0; - for (std::vector::const_iterator it = m_entities.begin(); it != m_entities.end(); it++) - { - Entity* pEnt = *it; - if (pEnt && pEnt->getDescriptor().getCategories().contains(category) ) - count++; - - } - return count; + return m_entityTypeCounts[category]; } diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp index a66426b17..8d68d2d9c 100644 --- a/source/world/level/Level.hpp +++ b/source/world/level/Level.hpp @@ -8,7 +8,8 @@ #pragma once -#include +#include +#include #ifndef _USE_MATH_DEFINES #define _USE_MATH_DEFINES #endif @@ -194,7 +195,7 @@ class Level : public LevelSource bool hasNeighborSignal(const TilePos& pos) const; - int getEntityCountOfCategory(EntityCategories::CategoriesMask) const; + int countInstanceOfType(EntityCategories::CategoriesMask); #ifdef ENH_IMPROVED_SAVING void saveUnsavedChunks(); @@ -234,5 +235,7 @@ class Level : public LevelSource int field_B10; PathFinder* m_pPathFinder; MobSpawner* m_pMobSpawner; + + std::map m_entityTypeCounts; }; From 370740478e9ce14dca441687c594dc0308557c6f Mon Sep 17 00:00:00 2001 From: "rep.stosw" Date: Tue, 23 Dec 2025 23:18:50 -0500 Subject: [PATCH 6/7] seems ok --- source/world/entity/EntityCategories.cpp | 23 +++++++++++++++++++++++ source/world/entity/EntityCategories.hpp | 2 ++ source/world/entity/MobCategory.cpp | 22 ++++++---------------- source/world/entity/MobCategory.hpp | 13 ++----------- source/world/entity/MobFactory.cpp | 11 +++++++---- source/world/entity/MobFactory.hpp | 2 +- source/world/entity/MobSpawner.cpp | 20 ++++++++++---------- source/world/entity/MobSpawner.hpp | 2 +- source/world/level/Level.cpp | 19 +++++++++++++++---- source/world/level/Level.hpp | 2 +- 10 files changed, 68 insertions(+), 48 deletions(-) diff --git a/source/world/entity/EntityCategories.cpp b/source/world/entity/EntityCategories.cpp index e7239c8ee..cbaeda556 100644 --- a/source/world/entity/EntityCategories.cpp +++ b/source/world/entity/EntityCategories.cpp @@ -1,5 +1,28 @@ #include "EntityCategories.hpp" +const EntityCategories::CategoriesMask EntityCategories::maskEnums[] = +{ + ENTITY, + MOB, + PATHFINDER_MOB, + UNKNOWN, + MONSTER, + ANIMAL, + WATER_ANIMAL, + TAMABLE_ANIMAL, + AMBIENT, + UNDEAD_MOB, + ZOMBIE_MONSTER, + ARTHROPOD, + MINECART, + SKELETON_MONSTER, + EQUINE_ANIMAL, + PROJECTILE, + ABSTRACT_ARROW, + VILLAGER_BASE +}; +const int EntityCategories::maskEnumCount = sizeof(EntityCategories::maskEnums) / sizeof(const EntityCategories::CategoriesMask); + EntityCategories::EntityCategories(CategoriesMask categoriesMask) { m_categoriesMask = categoriesMask; diff --git a/source/world/entity/EntityCategories.hpp b/source/world/entity/EntityCategories.hpp index c95d73a09..16a701c00 100644 --- a/source/world/entity/EntityCategories.hpp +++ b/source/world/entity/EntityCategories.hpp @@ -24,6 +24,8 @@ class EntityCategories ABSTRACT_ARROW = 1<<15 | PROJECTILE, VILLAGER_BASE = 1<<16 | PATHFINDER_MOB }; + static const CategoriesMask maskEnums[]; + static const int maskEnumCount; public: EntityCategories(CategoriesMask = ENTITY); diff --git a/source/world/entity/MobCategory.cpp b/source/world/entity/MobCategory.cpp index 0d0a08222..acc7ea72c 100644 --- a/source/world/entity/MobCategory.cpp +++ b/source/world/entity/MobCategory.cpp @@ -5,12 +5,14 @@ MobCategory MobCategory::monster = MobCategory(EntityCategories(EntityCategories::MONSTER), 10, 20, nullptr, false); MobCategory MobCategory::creature = MobCategory(EntityCategories(EntityCategories::ANIMAL), 10, 15, nullptr, true); MobCategory MobCategory::waterCreature = MobCategory(EntityCategories(EntityCategories::WATER_ANIMAL), 5, 10, nullptr, true); -const MobCategory MobCategory::values[] = { - MobCategory::monster, - MobCategory::creature, + +const MobCategory* MobCategory::all[] = { + &MobCategory::monster, + &MobCategory::creature, //MobCategory::waterCreature }; -const int MobCategory::numValues = sizeof(MobCategory::values) / sizeof(MobCategory); + +const int MobCategory::allCount = sizeof(MobCategory::all) / sizeof(const MobCategory*); MobCategory::MobCategory(const EntityCategories& baseType, int unknown, int max, const Material* material, bool friendly) : m_baseType(baseType) @@ -27,15 +29,3 @@ void MobCategory::initMobCategories() MobCategory::creature.m_pSpawnPositionMaterial = Material::air; MobCategory::waterCreature.m_pSpawnPositionMaterial = Material::water; } - -MobCategory& MobCategory::GetCategoryByIndex(MobCategory::ID i) -{ - switch (i) - { - case MobCategory::CREATURE: - return MobCategory::creature; - case MobCategory::MONSTER: - default: - return MobCategory::monster; - } -} \ No newline at end of file diff --git a/source/world/entity/MobCategory.hpp b/source/world/entity/MobCategory.hpp index f7cfefc2b..f50a4e277 100644 --- a/source/world/entity/MobCategory.hpp +++ b/source/world/entity/MobCategory.hpp @@ -6,18 +6,11 @@ class Material; class MobCategory { public: - enum ID { - MONSTER, - CREATURE, - // WATER_CREATURE - }; - - static MobCategory monster; static MobCategory creature; static MobCategory waterCreature; - static const MobCategory values[]; - static const int numValues; + static const MobCategory* all[]; + static const int allCount; private: MobCategory(const EntityCategories&, int, int, const Material*, bool); @@ -31,8 +24,6 @@ class MobCategory const Material* getSpawnPositionMaterial() const { return m_pSpawnPositionMaterial; } bool isFriendly() const { return m_bIsFriendly; } - // custom addition - static MobCategory& GetCategoryByIndex(MobCategory::ID i); private: const EntityCategories m_baseType; diff --git a/source/world/entity/MobFactory.cpp b/source/world/entity/MobFactory.cpp index 2176d2508..60a84cced 100644 --- a/source/world/entity/MobFactory.cpp +++ b/source/world/entity/MobFactory.cpp @@ -42,7 +42,8 @@ Mob* MobFactory::CreateMob(EntityType::ID entityType, Level *level) } } -void MobFactory::initMobLists() { +void MobFactory::initMobLists() +{ // format: ID, spawnrate monsterList.insert(std::make_pair(EntityType::SPIDER, 10)); @@ -59,9 +60,11 @@ void MobFactory::initMobLists() { waterCreatureList.insert(std::make_pair(EntityType::SQUID, 10)); } -const std::map& MobFactory::GetMobListOfCategory(EntityCategories::CategoriesMask category) { - return category == EntityCategories::MONSTER ? monsterList : - category == EntityCategories::ANIMAL ? creatureList : +const std::map& MobFactory::GetMobListOfCategory(const EntityCategories& category) +{ + EntityCategories::CategoriesMask mask = category.getCategoryMask(); + return mask == EntityCategories::MONSTER ? monsterList : + mask == EntityCategories::ANIMAL ? creatureList : nullCreatureList; } diff --git a/source/world/entity/MobFactory.hpp b/source/world/entity/MobFactory.hpp index 336d8d8da..669ef1071 100644 --- a/source/world/entity/MobFactory.hpp +++ b/source/world/entity/MobFactory.hpp @@ -10,5 +10,5 @@ class MobFactory public: static void initMobLists(); static Mob* CreateMob(EntityType::ID entityType, Level *level); - static const std::map& GetMobListOfCategory(EntityCategories::CategoriesMask category); + static const std::map& GetMobListOfCategory(const EntityCategories& category); }; \ No newline at end of file diff --git a/source/world/entity/MobSpawner.cpp b/source/world/entity/MobSpawner.cpp index d80f50859..16aae39cc 100644 --- a/source/world/entity/MobSpawner.cpp +++ b/source/world/entity/MobSpawner.cpp @@ -14,7 +14,7 @@ void MobSpawner::tick(Level& level, bool allowHostile, bool allowFriendly) for (std::vector::const_iterator it = level.m_players.begin(); it != level.m_players.end(); ++it) { - Player* player = *it; + const Player* player = *it; int cx = Mth::floor(player->m_pos.x / 16.0f); int cz = Mth::floor(player->m_pos.z / 16.0f); @@ -29,10 +29,10 @@ void MobSpawner::tick(Level& level, bool allowHostile, bool allowFriendly) int totalSpawned = 0; - for (int i = 0; i < MobCategory::numValues; i++) + for (int i = 0; i < MobCategory::allCount; i++) { - MobCategory& category = MobCategory::GetCategoryByIndex((MobCategory::ID)i); - const EntityCategories::CategoriesMask& mask = category.getBaseType().getCategoryMask(); + const MobCategory& category = *MobCategory::all[i]; + const EntityCategories& baseType = category.getBaseType(); bool isFriendly = category.isFriendly(); // good mobs don't spawn after dark, otherwise they will crowd around torches like beta @@ -42,18 +42,18 @@ void MobSpawner::tick(Level& level, bool allowHostile, bool allowFriendly) if ((isFriendly && !allowFriendly) || (!isFriendly && !allowHostile)) continue; - if (level.countInstanceOfType(mask) <= (category.getMaxInstancesPerChunk() * (int)chunksToPoll.size() / 256)) + if (level.getEntityCount(baseType) <= (category.getMaxInstancesPerChunk() * (int)chunksToPoll.size() / 256)) { for (std::set::iterator it = chunksToPoll.begin(); it != chunksToPoll.end(); ++it) { const ChunkPos& pos = *it; - const std::map& spawnList = MobFactory::GetMobListOfCategory(mask); + const std::map& spawnList = MobFactory::GetMobListOfCategory(baseType); if (spawnList.empty()) continue; - EntityType::ID type = spawnList.begin()->first; + EntityType::ID entityID = spawnList.begin()->first; int spawnWeight = 1; // make sure it starts with 1 so arithmetic exception doesn't occur @@ -69,7 +69,7 @@ void MobSpawner::tick(Level& level, bool allowHostile, bool allowFriendly) randomRate -= it->second; if (randomRate < 0) { - type = it->first; + entityID = it->first; break; } } @@ -104,7 +104,7 @@ void MobSpawner::tick(Level& level, bool allowHostile, bool allowFriendly) if (dPos.lengthSqr() >= 576.0f) { - Mob* entity = MobFactory::CreateMob(type, &level); + Mob* entity = MobFactory::CreateMob(entityID, &level); if (!entity) break; @@ -160,7 +160,7 @@ int MobSpawner::AddMob(Level& level, Mob *mob, const Vec3& pos, const Vec2& rot) return 1; } -bool MobSpawner::IsSpawnPositionOk(MobCategory& category, Level& level, const TilePos& pos) +bool MobSpawner::IsSpawnPositionOk(const MobCategory& category, Level& level, const TilePos& pos) { if (category.getSpawnPositionMaterial() == Material::water) return level.getMaterial(pos)->isLiquid() && !level.isSolidTile(pos.above()); diff --git a/source/world/entity/MobSpawner.hpp b/source/world/entity/MobSpawner.hpp index e7d74d740..74fede30f 100644 --- a/source/world/entity/MobSpawner.hpp +++ b/source/world/entity/MobSpawner.hpp @@ -15,7 +15,7 @@ class MobSpawner { public: - static bool IsSpawnPositionOk(MobCategory& category, Level& level, const TilePos& pos); + static bool IsSpawnPositionOk(const MobCategory& category, Level& level, const TilePos& pos); static void FinalizeMobSettings(Mob* mob, Level& level, const Vec3& pos); static void MakeBabyMob(Mob* mob, Level& level); static void PostProcessSpawnMobs(Level& level, Biome& biome, const Vec3& pos); diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index 02e0251a5..1bfd7a183 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -772,8 +772,14 @@ void Level::setTilesDirty(const TilePos& min, const TilePos& max) void Level::entityAdded(Entity* pEnt) { + // save for a bit EntityCategories::CategoriesMask mask = pEnt->getDescriptor().getCategories().getCategoryMask(); - m_entityTypeCounts[mask]++; + for (int i = 0; i < EntityCategories::maskEnumCount; i++ ) + { + EntityCategories::CategoriesMask category = EntityCategories::maskEnums[i]; + if ((mask & category) == category) + m_entityTypeCounts[category]++; + } for (std::vector::iterator it = m_levelListeners.begin(); it != m_levelListeners.end(); it++) { @@ -786,7 +792,12 @@ void Level::entityAdded(Entity* pEnt) void Level::entityRemoved(Entity* pEnt) { EntityCategories::CategoriesMask mask = pEnt->getDescriptor().getCategories().getCategoryMask(); - m_entityTypeCounts[mask]++; + for (int i = 0; i < EntityCategories::maskEnumCount; i++ ) + { + EntityCategories::CategoriesMask category = EntityCategories::maskEnums[i]; + if ((mask & category) == category) + m_entityTypeCounts[category]--; + } for (std::vector::iterator it = m_levelListeners.begin(); it != m_levelListeners.end(); it++) { @@ -1950,7 +1961,7 @@ float Level::getSunAngle(float f) const return (float(M_PI) * getTimeOfDay(f)) * 2; } -int Level::countInstanceOfType(EntityCategories::CategoriesMask category) +int Level::getEntityCount(const EntityCategories& category) { - return m_entityTypeCounts[category]; + return m_entityTypeCounts[category.getCategoryMask()]; } diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp index 8d68d2d9c..710252077 100644 --- a/source/world/level/Level.hpp +++ b/source/world/level/Level.hpp @@ -195,7 +195,7 @@ class Level : public LevelSource bool hasNeighborSignal(const TilePos& pos) const; - int countInstanceOfType(EntityCategories::CategoriesMask); + int getEntityCount(const EntityCategories&); #ifdef ENH_IMPROVED_SAVING void saveUnsavedChunks(); From dd604853780a2a69f15d9c168ca8b1835ec0c3ed Mon Sep 17 00:00:00 2001 From: "rep.stosw" Date: Tue, 23 Dec 2025 23:21:38 -0500 Subject: [PATCH 7/7] rename --- source/world/level/Level.cpp | 6 +++--- source/world/level/Level.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index 1bfd7a183..6e920de68 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -778,7 +778,7 @@ void Level::entityAdded(Entity* pEnt) { EntityCategories::CategoriesMask category = EntityCategories::maskEnums[i]; if ((mask & category) == category) - m_entityTypeCounts[category]++; + m_entityCountsByCategory[category]++; } for (std::vector::iterator it = m_levelListeners.begin(); it != m_levelListeners.end(); it++) @@ -796,7 +796,7 @@ void Level::entityRemoved(Entity* pEnt) { EntityCategories::CategoriesMask category = EntityCategories::maskEnums[i]; if ((mask & category) == category) - m_entityTypeCounts[category]--; + m_entityCountsByCategory[category]--; } for (std::vector::iterator it = m_levelListeners.begin(); it != m_levelListeners.end(); it++) @@ -1963,5 +1963,5 @@ float Level::getSunAngle(float f) const int Level::getEntityCount(const EntityCategories& category) { - return m_entityTypeCounts[category.getCategoryMask()]; + return m_entityCountsByCategory[category.getCategoryMask()]; } diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp index 710252077..7356e49c6 100644 --- a/source/world/level/Level.hpp +++ b/source/world/level/Level.hpp @@ -236,6 +236,6 @@ class Level : public LevelSource PathFinder* m_pPathFinder; MobSpawner* m_pMobSpawner; - std::map m_entityTypeCounts; + std::map m_entityCountsByCategory; };