Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions source/client/app/NinecraftApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -232,6 +233,7 @@ void NinecraftApp::init()
Material::initMaterials();
EntityTypeDescriptor::initDescriptors(); // custom
MobCategory::initMobCategories();
MobFactory::initMobLists();
Tile::initTiles();
Item::initItems();
Biome::initBiomes();
Expand Down
14 changes: 13 additions & 1 deletion source/world/entity/MobCategory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ MobCategory MobCategory::waterCreature = MobCategory(EntityCategories(EntityCate
const MobCategory MobCategory::values[] = {
MobCategory::monster,
MobCategory::creature,
MobCategory::waterCreature
//MobCategory::waterCreature
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add ampersand within comment.

};
const int MobCategory::numValues = sizeof(MobCategory::values) / sizeof(MobCategory);

Expand All @@ -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;
}
}
5 changes: 4 additions & 1 deletion source/world/entity/MobCategory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
28 changes: 28 additions & 0 deletions source/world/entity/MobFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@

#define ENT(enumType, classType) case EntityType::enumType: return new classType(level);



std::map<EntityType::ID, int> monsterList;
std::map<EntityType::ID, int> creatureList;
std::map<EntityType::ID, int> waterCreatureList;
std::map<EntityType::ID, int> nullCreatureList;

Mob* MobFactory::CreateMob(EntityType::ID entityType, Level *level)
{
switch (entityType)
Expand All @@ -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<EntityType::ID, int> MobFactory::GetMobListOfCategory(EntityCategories::CategoriesMask category) {
return category == EntityCategories::MONSTER ? monsterList :
category == EntityCategories::ANIMAL ? creatureList :
nullCreatureList;
}

#undef ENT
6 changes: 5 additions & 1 deletion source/world/entity/MobFactory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<EntityType::ID, int> GetMobListOfCategory(EntityCategories::CategoriesMask category);
};
211 changes: 211 additions & 0 deletions source/world/entity/MobSpawner.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@

#include "world/entity/MobSpawner.hpp"
#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)
{
if (!allowHostile && !allowFriendly)
return;

chunksToPoll.clear();

for (std::vector<Player*>::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& 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 ((isFriendly && !allowFriendly) || (!isFriendly && !allowHostile))
continue;

if (level->getEntityCountOfCategory(mask) <= (category.getMaxInstancesPerChunk() * (int)chunksToPoll.size() / 256))
{
for (std::set<ChunkPos>::iterator it = chunksToPoll.begin(); it != chunksToPoll.end(); ++it)
{
ChunkPos pos = *it;

std::map<EntityType::ID, int> 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<EntityType::ID, int>::iterator it = spawnList.begin(); it != spawnList.end(); ++it)
{
spawnWeight += it->second;
}

int randomRate = level->m_random.nextInt(spawnWeight);

for (std::map<EntityType::ID, int>::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) != category.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(&category, 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;
}
}
}

}

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;
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)
{
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());

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)
{
// empty (0.7.1)
}
29 changes: 29 additions & 0 deletions source/world/entity/MobSpawner.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <set>

#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<ChunkPos> chunksToPoll;
};
Loading
Loading