diff options
29 files changed, 1341 insertions, 176 deletions
diff --git a/.gitignore b/.gitignore index 7706ecc0a..b0ad11d53 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,23 @@ cloc.xsl *.suo /EveryNight.cmd *.sublime-* + +# emacs stuff +*.*~ + +# world inside source +ChunkWorx.ini +groups.ini +items.ini +monsters.ini +settings.ini +terrain.ini +users.ini +webadmin.ini +world.ini +crafting.txt +motd.txt +logs +players +world +world_nether diff --git a/source/BlockID.cpp b/source/BlockID.cpp index 793e5e523..687f27aa8 100644 --- a/source/BlockID.cpp +++ b/source/BlockID.cpp @@ -603,6 +603,7 @@ public: g_BlockTransparent[E_BLOCK_GLASS] = true; g_BlockTransparent[E_BLOCK_ICE] = true; g_BlockTransparent[E_BLOCK_IRON_DOOR] = true; + g_BlockTransparent[E_BLOCK_LAVA] = true; g_BlockTransparent[E_BLOCK_LEAVES] = true; g_BlockTransparent[E_BLOCK_LEVER] = true; g_BlockTransparent[E_BLOCK_MELON_STEM] = true; @@ -615,11 +616,14 @@ public: g_BlockTransparent[E_BLOCK_RED_MUSHROOM] = true; g_BlockTransparent[E_BLOCK_RED_ROSE] = true; g_BlockTransparent[E_BLOCK_SIGN_POST] = true; + g_BlockTransparent[E_BLOCK_STATIONARY_LAVA] = true; + g_BlockTransparent[E_BLOCK_STATIONARY_WATER] = true; g_BlockTransparent[E_BLOCK_STONE_PRESSURE_PLATE] = true; g_BlockTransparent[E_BLOCK_SNOW] = true; g_BlockTransparent[E_BLOCK_TALL_GRASS] = true; g_BlockTransparent[E_BLOCK_TORCH] = true; g_BlockTransparent[E_BLOCK_VINES] = true; + g_BlockTransparent[E_BLOCK_WATER] = true; g_BlockTransparent[E_BLOCK_WALLSIGN] = true; g_BlockTransparent[E_BLOCK_WOODEN_DOOR] = true; g_BlockTransparent[E_BLOCK_WOODEN_PRESSURE_PLATE] = true; diff --git a/source/Chunk.cpp b/source/Chunk.cpp index db533f642..21401163b 100644 --- a/source/Chunk.cpp +++ b/source/Chunk.cpp @@ -30,6 +30,9 @@ #include "PluginManager.h" #include "Blocks/BlockHandler.h" #include "Simulator/FluidSimulator.h" +#include "MobCensus.h" +#include "MobSpawner.h" + #include <json/json.h> @@ -429,6 +432,133 @@ void cChunk::Stay(bool a_Stay) +void cChunk::CollectMobCensus(cMobCensus& toFill) +{ + toFill.CollectSpawnableChunk(*this); + std::list<const Vector3d*> playerPositions; + cPlayer* currentPlayer; + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + currentPlayer = (*itr)->GetPlayer(); + playerPositions.push_back(&(currentPlayer->GetPosition())); + } + + Vector3d currentPosition; + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + //LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass()); + if ((*itr)->IsMob()) + { + cMonster& Monster = (cMonster&)(**itr); + currentPosition = Monster.GetPosition(); + for (std::list<const Vector3d*>::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); itr2 ++) + { + toFill.CollectMob(Monster,*this,(currentPosition-**itr2).SqrLength()); + } + } + } // for itr - m_Entitites[] +} + + + + +void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ) +{ + ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff); + int Random = m_World->GetTickRandomNumber(0x00ffffff); + a_X = Random % (a_MaxX * 2); + a_Y = (Random / (a_MaxX * 2)) % (a_MaxY * 2); + a_Z = ((Random / (a_MaxX * 2)) / (a_MaxY * 2)) % (a_MaxZ * 2); + a_X /= 2; + a_Y /= 2; + a_Z /= 2; +} + + + + + +void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z) +{ + // MG TODO : check if this kind of optimization (only one random call) is still needed + // MG TODO : if so propagate it + + getThreeRandomNumber(a_X, a_Y, a_Z, Width, Height-2, Width); + a_Y++; +} + + + + + +void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + int Center_X,Center_Y,Center_Z; + getRandomBlockCoords(Center_X,Center_Y,Center_Z); + + BLOCKTYPE PackCenterBlock = GetBlock(Center_X, Center_Y, Center_Z); + if (a_MobSpawner.CheckPackCenter(PackCenterBlock)) + { + a_MobSpawner.NewPack(); + int NumberOfTries = 0; + int NumberOfSuccess = 0; + int MaxNbOfSuccess = 4; // this can be changed during the process for Wolves and Ghass + while (NumberOfTries < 12 && NumberOfSuccess < MaxNbOfSuccess) + { + const int HorizontalRange = 20; // MG TODO : relocate + const int VerticalRange = 0; // MG TODO : relocate + int Try_X, Try_Y, Try_Z; + getThreeRandomNumber(Try_X, Try_Y, Try_Z, 2*HorizontalRange+1 , 2*VerticalRange+1 , 2*HorizontalRange+1); + Try_X -= HorizontalRange; + Try_Y -= VerticalRange; + Try_Z -= HorizontalRange; + Try_X += Center_X; + Try_Y += Center_Y; + Try_Z += Center_Z; + + ASSERT(Try_Y > 0); + ASSERT(Try_Y < cChunkDef::Height-1); + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + BLOCKTYPE BlockType_below; + NIBBLETYPE BlockMeta_below; + BLOCKTYPE BlockType_above; + NIBBLETYPE BlockMeta_above; + if (UnboundedRelGetBlock(Try_X, Try_Y , Try_Z, BlockType, BlockMeta) && + UnboundedRelGetBlock(Try_X, Try_Y-1, Try_Z, BlockType_below, BlockMeta_below)&& + UnboundedRelGetBlock(Try_X, Try_Y+1, Try_Z, BlockType_above, BlockMeta_above) + ) + { + EMCSBiome Biome = m_ChunkMap->GetBiomeAt (Try_X, Try_Z); + // MG TODO : + // Moon cycle (for slime) + // check player and playerspawn presence < 24 blocks + // check mobs presence on the block + + // MG TODO: fix the "light" thing, I'm pretty sure that UnboundedRelGetBlock s not returning the right thing + + // MG TODO : check that "Level" really means Y + cEntity* newMob = a_MobSpawner.TryToSpawnHere(BlockType, BlockMeta, BlockType_below, BlockMeta_below, BlockType_above, BlockMeta_above, Biome, Try_Y, MaxNbOfSuccess); + if (newMob) + { + int WorldX, WorldY, WorldZ; + PositionToWorldPosition(Try_X, Try_Y, Try_Z, WorldX, WorldY, WorldZ); + newMob->SetPosition(WorldX, WorldY, WorldZ); + LOGD("Spawning %s #%i at %d,%d,%d",newMob->GetClass(),newMob->GetUniqueID(),WorldX, WorldY, WorldZ); + NumberOfSuccess++; + } + } + + NumberOfTries++; + } + } + +} + + + + void cChunk::Tick(float a_Dt) { @@ -457,10 +587,14 @@ void cChunk::Tick(float a_Dt) m_IsDirty = (*itr)->Tick(a_Dt, *this) | m_IsDirty; } - // Tick all entities in this chunk: + // Tick all entities in this chunk (except mobs): for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) { - (*itr)->Tick(a_Dt, *this); + // Mobs are tickes inside MobTick (as we don't have to tick them if they are far away from players) + if (!((*itr)->IsMob())) + { + (*itr)->Tick(a_Dt, *this); + } } // for itr - m_Entitites[] // Remove all entities that were scheduled for removal: diff --git a/source/Chunk.h b/source/Chunk.h index c979b7928..aca180d16 100644 --- a/source/Chunk.h +++ b/source/Chunk.h @@ -49,6 +49,8 @@ class cPickup; class cChunkDataSerializer; class cBlockArea; class cFluidSimulatorData; +class cMobCensus; +class cMobSpawner; typedef std::list<cClientHandle *> cClientHandleList; typedef cItemCallback<cEntity> cEntityCallback; @@ -124,6 +126,12 @@ public: /// Sets or resets the internal flag that prevents chunk from being unloaded void Stay(bool a_Stay = true); + /// Recence all mobs proximities to players in order to know what to do with them + void CollectMobCensus(cMobCensus& toFill); + + /// Try to Spawn Monsters inside chunk + void SpawnMobs(cMobSpawner& a_MobSpawner); + void Tick(float a_Dt); int GetPosX(void) const { return m_PosX; } @@ -385,6 +393,10 @@ private: cSandSimulatorChunkData m_SandSimulatorData; + // pick up a random block of this chunk + void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z); + void getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ); + void RemoveBlockEntity(cBlockEntity * a_BlockEntity); void AddBlockEntity (cBlockEntity * a_BlockEntity); diff --git a/source/ChunkMap.cpp b/source/ChunkMap.cpp index 3c098fdfe..c3bd5f33d 100644 --- a/source/ChunkMap.cpp +++ b/source/ChunkMap.cpp @@ -12,6 +12,8 @@ #include "BlockArea.h" #include "PluginManager.h" #include "Entities/TNTEntity.h" +#include "MobCensus.h" +#include "MobSpawner.h" #ifndef _WIN32 #include <cstdlib> // abs @@ -2152,6 +2154,32 @@ void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) +void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->CollectMobCensus(a_ToFill); + } // for itr - m_Layers +} + + + + + + +void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->SpawnMobs(a_MobSpawner); + } // for itr - m_Layers +} + + + + void cChunkMap::Tick(float a_Dt) { @@ -2310,6 +2338,38 @@ cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ) +void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // We do count every Mobs in the world. But we are assuming that every chunk not loaded by any client + // doesn't affect us. Normally they should not have mobs because every "too far" mobs despawn + // If they have (f.i. when player disconnect) we assume we don't have to make them live or despawn + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->CollectMobCensus(a_ToFill); + } + } // for i - m_Chunks[] +} + + + + + + +void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // We only spawn close to players + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->SpawnMobs(a_MobSpawner); + } + } // for i - m_Chunks[] +} + + void cChunkMap::cChunkLayer::Tick(float a_Dt) { diff --git a/source/ChunkMap.h b/source/ChunkMap.h index fcb164f7b..f68cb6472 100644 --- a/source/ChunkMap.h +++ b/source/ChunkMap.h @@ -26,6 +26,8 @@ class cPawn; class cPickup; class cChunkDataSerializer; class cBlockArea; +class cMobCensus; +class cMobSpawner; typedef std::list<cClientHandle *> cClientHandleList; typedef cChunk * cChunkPtr; @@ -263,9 +265,15 @@ public: /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); - /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call + /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); + /// Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player + void CollectMobCensus(cMobCensus& a_ToFill); + + /// Try to Spawn Monsters inside all Chunks + void SpawnMobs(cMobSpawner& a_MobSpawner); + void Tick(float a_Dt); void UnloadUnusedChunks(void); @@ -309,6 +317,11 @@ private: void Save(void); void UnloadUnusedChunks(void); + /// Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player + void CollectMobCensus(cMobCensus& a_ToFill); + /// Try to Spawn Monsters inside all Chunks + void SpawnMobs(cMobSpawner& a_MobSpawner); + void Tick(float a_Dt); void RemoveClient(cClientHandle * a_Client); diff --git a/source/MobCensus.cpp b/source/MobCensus.cpp new file mode 100644 index 000000000..612f25916 --- /dev/null +++ b/source/MobCensus.cpp @@ -0,0 +1,89 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobCensus.h" + + + +cMobCensus::tCapMultipliersMap cMobCensus::CapMultiplierInitializerBeforeCx11() +{ + std::map<cMonster::eFamily,int> toReturn; + toReturn[cMonster::mfHostile] = 79; + toReturn[cMonster::mfPassive] = 11; + toReturn[cMonster::mfAmbient] = 16; + toReturn[cMonster::mfWater] = 5; + return toReturn; +} + +cMobCensus::tMobSpawnRate cMobCensus::MobSpawnRateInitializerBeforeCx11() +{ + std::map<cMonster::eFamily,int> toReturn; + toReturn[cMonster::mfHostile] = 1; + toReturn[cMonster::mfPassive] = 400; + toReturn[cMonster::mfAmbient] = 400; + toReturn[cMonster::mfWater] = 400; + return toReturn; +} + +cMobCensus::tCapMultipliersMap& cMobCensus::m_CapMultipliers() +{ + static tCapMultipliersMap* value = new tCapMultipliersMap(CapMultiplierInitializerBeforeCx11()); + return *value; +} + +cMobCensus::tMobSpawnRate& cMobCensus::m_SpawnRate() +{ + static tMobSpawnRate* value = new tMobSpawnRate(MobSpawnRateInitializerBeforeCx11()); + return *value; +} + +cMobCensus::cMobCensus() +{ +} + +void cMobCensus::CollectMob(cMonster& a_Monster, cChunk& a_Chunk, double a_Distance) +{ + m_ProximityCounter.CollectMob(a_Monster,a_Chunk,a_Distance); + m_MobFamilyCollecter.CollectMob(a_Monster); +} + +bool cMobCensus::isCaped(cMonster::eFamily a_MobFamily) +{ + bool toReturn = true; + const int ratio = 319; // this should be 256 as we are only supposed to take account from chunks that are in 17x17 from a player + // but for now, we use all chunks loaded by players. that means 19 x 19 chucks. That's why we use 256 * (19*19) / (17*17) = 319 + // MG TODO : code the correct count + tCapMultipliersMap::const_iterator capMultiplier = m_CapMultipliers().find(a_MobFamily); + if ( + (capMultiplier != m_CapMultipliers().end()) && + (capMultiplier->second * getChunkNb()) / ratio >= m_MobFamilyCollecter.getNumberOfCollectedMobs(a_MobFamily) + ) + { + toReturn = false; + } + return toReturn; +} + +void cMobCensus::CollectSpawnableChunk(cChunk& a_Chunk) +{ + m_EligibleForSpawnChunks.insert(&a_Chunk); +} + +int cMobCensus::getChunkNb() +{ + return m_EligibleForSpawnChunks.size(); +} + +cMobProximityCounter& cMobCensus::getProximityCounter() +{ + return m_ProximityCounter; +} + + +void cMobCensus::logd() +{ + LOGD((std::string("Hostile mobs : %d") + (isCaped(cMonster::mfHostile)?"(capped)":"")).c_str(),m_MobFamilyCollecter.getNumberOfCollectedMobs(cMonster::mfHostile)); + LOGD((std::string("Ambiant mobs : %d") + (isCaped(cMonster::mfAmbient)?"(capped)":"")).c_str(),m_MobFamilyCollecter.getNumberOfCollectedMobs(cMonster::mfAmbient)); + LOGD((std::string("Water mobs : %d") + (isCaped(cMonster::mfWater)? "(capped)":"")).c_str(),m_MobFamilyCollecter.getNumberOfCollectedMobs(cMonster::mfWater)); + LOGD((std::string("Passive mobs : %d") + (isCaped(cMonster::mfPassive)?"(capped)":"")).c_str(),m_MobFamilyCollecter.getNumberOfCollectedMobs(cMonster::mfPassive)); +} diff --git a/source/MobCensus.h b/source/MobCensus.h new file mode 100644 index 000000000..8aa8f3a6c --- /dev/null +++ b/source/MobCensus.h @@ -0,0 +1,58 @@ + +#pragma once + +#include "MobProximityCounter.h" +#include "MobFamilyCollecter.h" + +class cChunk; +class cMonster; + +// This class is used to collect, for each Mob, what is the distance of the closest player +// it was first being designed in order to make mobs spawn / despawn / act +// as the behaviour and even life of mobs depends on the distance to closest player +// +// as side effect : it also collect the chunks that are elligible for spawning +// as side effect 2 : it also know the caps for mobs number and can compare census to this numbers +class cMobCensus +{ +public : + cMobCensus(); + +protected : + cMobProximityCounter m_ProximityCounter; + cMobFamilyCollecter m_MobFamilyCollecter; + + typedef const std::map<cMonster::eFamily,int> tCapMultipliersMap; + static tCapMultipliersMap& m_CapMultipliers(); + + std::set<cChunk*> m_EligibleForSpawnChunks; + + // count the chunks that are elligible to spawn (for now, the loaded valide not null chunks) + int getChunkNb(); + +public: + typedef const std::map<cMonster::eFamily,int> tMobSpawnRate; + static tMobSpawnRate& m_SpawnRate(); + + // return the nested proximity counter + cMobProximityCounter& getProximityCounter(); + +public : + // collect an elligible Chunk for Mob Spawning + // MG TODO : code the correct rule (not loaded chunk but short distant from players) + void CollectSpawnableChunk(cChunk& a_Chunk); + + // collect a mob - it's distance to player, it's family ... + void CollectMob(cMonster& a_Monster, cChunk& a_Chunk, double a_Distance); + + // return true if the family is caped (i.e. there is more mobs of this family than max) + bool isCaped(cMonster::eFamily a_MobFamily); + + // log the results of census + void logd(); + +protected : + static tCapMultipliersMap CapMultiplierInitializerBeforeCx11(); + static tCapMultipliersMap MobSpawnRateInitializerBeforeCx11(); +}; + diff --git a/source/MobFamilyCollecter.cpp b/source/MobFamilyCollecter.cpp new file mode 100644 index 000000000..2aa46599a --- /dev/null +++ b/source/MobFamilyCollecter.cpp @@ -0,0 +1,33 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobFamilyCollecter.h" +#include "Mobs/Monster.h" + + + +cMobFamilyCollecter::tMobFamilyList cMobFamilyCollecter::initMobFamilyBeforeCx11() +{ + std::set<cMonster::eFamily> toReturn; + toReturn.insert(cMonster::mfHostile); + toReturn.insert(cMonster::mfPassive); + toReturn.insert(cMonster::mfAmbient); + toReturn.insert(cMonster::mfWater); + return toReturn; +} +cMobFamilyCollecter::tMobFamilyList& cMobFamilyCollecter::m_AllFamilies() +{ + static tMobFamilyList* AllFamilies = new tMobFamilyList(initMobFamilyBeforeCx11()); + return *AllFamilies; +} + +void cMobFamilyCollecter::CollectMob(cMonster& a_Monster) +{ + cMonster::eFamily MobFamily = a_Monster.GetMobFamily(); + m_Mobs[MobFamily].insert(&a_Monster); +} + +int cMobFamilyCollecter::getNumberOfCollectedMobs(cMonster::eFamily a_Family) +{ + return m_Mobs[a_Family].size(); +} diff --git a/source/MobFamilyCollecter.h b/source/MobFamilyCollecter.h new file mode 100644 index 000000000..659d2b687 --- /dev/null +++ b/source/MobFamilyCollecter.h @@ -0,0 +1,40 @@ + +#pragma once + +#include <map> +#include <set> +#include "BlockID.h" +#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it + +class cChunk; + + +// This class is used to collect, for each Mob, what is it's family. It was first +// being designed to check the caps of the mobs (no more than ... hostile mob in the world) +// +// as side effects : it also know what is the spawnrate of each family : MG TODO relocate +class cMobFamilyCollecter +{ +protected : + std::map<cMonster::eFamily,std::set<cMonster*> > m_Mobs; + +public : + // collect a mob + void CollectMob(cMonster& a_Monster); + + // return the number of mobs for this family + int getNumberOfCollectedMobs(cMonster::eFamily a_Family); + +public : + typedef const std::set<cMonster::eFamily> tMobFamilyList; + static tMobFamilyList& m_AllFamilies(); + +public : + typedef const std::map<cMonster::eFamily,int> tMobSpawRate; + static tMobSpawRate& m_SpawnRate(); + +protected : + static tMobFamilyList initMobFamilyBeforeCx11(); + static tMobSpawRate initMobSpawnRateBeforeCx11(); +}; + diff --git a/source/MobProximityCounter.cpp b/source/MobProximityCounter.cpp new file mode 100644 index 000000000..583a71579 --- /dev/null +++ b/source/MobProximityCounter.cpp @@ -0,0 +1,83 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobProximityCounter.h" + +#include "Entities/Entity.h" +#include "Chunk.h" + +void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance) +{ +// LOGD("Collecting monster %s, with distance %f",a_Monster->GetClass(),a_Distance); + tMonsterToDistance::iterator it = m_MonsterToDistance.find(&a_Monster); + if (it == m_MonsterToDistance.end()) + { + sDistanceAndChunk newDistanceAndChunk(a_Distance,a_Chunk); + std::pair<tMonsterToDistance::iterator,bool> result = m_MonsterToDistance.insert(tMonsterToDistance::value_type(&a_Monster,newDistanceAndChunk)); + if (!result.second) + { + ASSERT(!"A collected Monster was not found inside distance map using find(), but insert() said there already is a key for it"); + } + } + else + { + if (a_Distance < it->second.m_Distance) + { + it->second.m_Distance = a_Distance; + it->second.m_Chunk = a_Chunk; + } + } + + m_EligibleForSpawnChunks.insert(&a_Chunk); + +} + +void cMobProximityCounter::convertMaps() +{ + for(tMonsterToDistance::const_iterator itr = m_MonsterToDistance.begin(); itr != m_MonsterToDistance.end(); itr++) + { + m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,itr->second.m_Chunk))); + } +} + +cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax) +{ + sIterablePair toReturn; + toReturn.m_Count = 0; + toReturn.m_Begin = m_DistanceToMonster.end(); + toReturn.m_End = m_DistanceToMonster.end(); + + a_DistanceMin *= a_DistanceMin;// this is because is use square distance + a_DistanceMax *= a_DistanceMax; + + if (m_DistanceToMonster.size() <= 0) + { + convertMaps(); + } + + for(tDistanceToMonster::const_iterator itr = m_DistanceToMonster.begin(); itr != m_DistanceToMonster.end(); itr++) + { + if (toReturn.m_Begin == m_DistanceToMonster.end()) + { + if (a_DistanceMin == -1 || itr->first > a_DistanceMin) + { + toReturn.m_Begin = itr; // this is the first one with distance > a_DistanceMin; + } + } + + if (toReturn.m_Begin != m_DistanceToMonster.end()) + { + if (a_DistanceMax != -1 && itr->first > a_DistanceMax) + { + toReturn.m_End = itr; // this is just after the last one with distance < a_DistanceMax + // Note : if we are not going through this, it's ok, toReturn.m_End will be end(); + break; + } + else + { + toReturn.m_Count ++; + } + } + } + return toReturn; +} diff --git a/source/MobProximityCounter.h b/source/MobProximityCounter.h new file mode 100644 index 000000000..8a67139aa --- /dev/null +++ b/source/MobProximityCounter.h @@ -0,0 +1,65 @@ + +#pragma once + +#include <set> + +class cChunk; +class cEntity; + + +// This class is used to collect, for each Mob, what is the distance of the closest player +// it was first being designed in order to make mobs spawn / despawn / act +// as the behaviour and even life of mobs depends on the distance to closest player +class cMobProximityCounter +{ +protected : + // structs used for later maps (see m_MonsterToDistance and m_DistanceToMonster) + struct sDistanceAndChunk + { + sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(a_Chunk) {} + double m_Distance; + cChunk& m_Chunk; + }; + struct sMonsterAndChunk + { + sMonsterAndChunk(cEntity& a_Monster, cChunk& a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {} + cEntity& m_Monster; + cChunk& m_Chunk; + }; + +public : + typedef std::map<cEntity*,sDistanceAndChunk> tMonsterToDistance; + typedef std::multimap<double,sMonsterAndChunk> tDistanceToMonster; + +protected : + // this map is filled during collection phase, it will be later transformed into DistanceToMonster + tMonsterToDistance m_MonsterToDistance; + + // this map is generated after collection phase, in order to access monster by distance to player + tDistanceToMonster m_DistanceToMonster; + + // this are the collected chunks. Used to determinate the number of elligible chunk for spawning. + std::set<cChunk*> m_EligibleForSpawnChunks; + +protected : + // transform monsterToDistance map (that was usefull for collecting) into distanceToMonster + // that will be usefull for picking up. + void convertMaps(); + +public : + // count a mob on a specified chunk with specified distance to an unkown player + // if the distance is shortest than the one collected, this become the new closest + // distance and the chunk become the "hosting" chunk (that is the one that will perform the action) + void CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance); + + // return the mobs that are within the range of distance of the closest player they are + // that means that if a mob is 30 m from a player and 150 m from another one. It will be + // in the range [0..50] but not in [100..200] + struct sIterablePair{ + tDistanceToMonster::const_iterator m_Begin; + tDistanceToMonster::const_iterator m_End; + int m_Count; + }; + sIterablePair getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax); + +}; diff --git a/source/MobSpawner.cpp b/source/MobSpawner.cpp new file mode 100644 index 000000000..9bff87533 --- /dev/null +++ b/source/MobSpawner.cpp @@ -0,0 +1,275 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobSpawner.h" +#include "MobTypesManager.h" +#include "Mobs/Monster.h" +#include "Mobs/IncludeAllMonsters.h" + +cMobSpawner::tMobTypes& cMobSpawner::m_MobTypes() +{ + static tMobTypes* value = new tMobTypes(initMobTypesBeforeCx11()); + return *value; +} + +cMobSpawner::tMobTypes cMobSpawner::initMobTypesBeforeCx11() +{ + std::set<cMonster::eType> toReturn; + toReturn.insert(cMonster::mtCreeper); + toReturn.insert(cMonster::mtSkeleton); + toReturn.insert(cMonster::mtSpider); + toReturn.insert(cMonster::mtGiant); + toReturn.insert(cMonster::mtZombie); + toReturn.insert(cMonster::mtSlime); + toReturn.insert(cMonster::mtGhast); + toReturn.insert(cMonster::mtZombiePigman); + toReturn.insert(cMonster::mtEnderman); + toReturn.insert(cMonster::mtCaveSpider); + toReturn.insert(cMonster::mtSilverfish); + toReturn.insert(cMonster::mtBlaze); + toReturn.insert(cMonster::mtMagmaCube); + toReturn.insert(cMonster::mtEnderDragon); + toReturn.insert(cMonster::mtWither); + toReturn.insert(cMonster::mtBat); + toReturn.insert(cMonster::mtWitch); + toReturn.insert(cMonster::mtPig); + toReturn.insert(cMonster::mtSheep); + toReturn.insert(cMonster::mtCow); + toReturn.insert(cMonster::mtChicken); + toReturn.insert(cMonster::mtSquid); + toReturn.insert(cMonster::mtWolf); + toReturn.insert(cMonster::mtMooshroom); + toReturn.insert(cMonster::mtSnowGolem); + toReturn.insert(cMonster::mtOcelot); + toReturn.insert(cMonster::mtIronGolem); + toReturn.insert(cMonster::mtVillager); + return toReturn; +} + + +cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily,const std::set<cMonster::eType>& a_AllowedTypes) : + m_MonsterFamily(a_MonsterFamily), + m_NewPack(true), + m_MobType(cMonster::mtInvalidType) +{ + for (std::set<cMonster::eType>::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); itr++) + { + if (cMobTypesManager::getFamilyFromType(*itr) == a_MonsterFamily) + { + m_AllowedTypes.insert(*itr); + } + } +} + +bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType) +{ + // Packs of non-water mobs can only be centered on an air block + // Packs of water mobs can only be centered on a water block + if (m_MonsterFamily == cMonster::mfWater) + { + return IsBlockWater(a_BlockType); + } + else + { + return a_BlockType == E_BLOCK_AIR; + } +} + +void cMobSpawner::addIfAllowed(cMonster::eType toAdd, std::set<cMonster::eType>& toAddIn) +{ + std::set<cMonster::eType>::iterator itr = m_AllowedTypes.find(toAdd); + if (itr != m_AllowedTypes.end()) + { + toAddIn.insert(toAdd); + } +} + +cMonster::eType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) +{ + std::set<cMonster::eType> allowedMobs; + + if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore) + { + addIfAllowed(cMonster::mtMooshroom, allowedMobs); + } + else if (a_Biome == biNether) + { + addIfAllowed(cMonster::mtGhast, allowedMobs); + addIfAllowed(cMonster::mtZombiePigman, allowedMobs); + addIfAllowed(cMonster::mtMagmaCube, allowedMobs); + } + /*else if (a_Biome == biEnder) MG TODO : figure out what are the biomes of the ender + { + addIfAllowed(cMonster::mtEnderman, allowedMobs); + }*/ + else + { + addIfAllowed(cMonster::mtBat, allowedMobs); + addIfAllowed(cMonster::mtSpider, allowedMobs); + addIfAllowed(cMonster::mtZombie, allowedMobs); + addIfAllowed(cMonster::mtSkeleton, allowedMobs); + addIfAllowed(cMonster::mtCreeper, allowedMobs); + addIfAllowed(cMonster::mtSquid, allowedMobs); + + if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean) + { + addIfAllowed(cMonster::mtSheep, allowedMobs); + addIfAllowed(cMonster::mtPig, allowedMobs); + addIfAllowed(cMonster::mtCow, allowedMobs); + addIfAllowed(cMonster::mtChicken, allowedMobs); + addIfAllowed(cMonster::mtEnderman, allowedMobs); + addIfAllowed(cMonster::mtSlime, allowedMobs); // MG TODO : much more complicated rule + + if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga || a_Biome == biTaigaHills) + { + addIfAllowed(cMonster::mtWolf, allowedMobs); + } + else if (a_Biome == biJungle || a_Biome == biJungleHills) + { + addIfAllowed(cMonster::mtOcelot, allowedMobs); + } + } + } + + int allowedMobsSize = allowedMobs.size(); + if (allowedMobsSize > 0) + { + std::set<cMonster::eType>::iterator itr = allowedMobs.begin(); + int iRandom = m_Random.NextInt(allowedMobsSize,a_Biome); + + for(int i = 0; i < iRandom; i++) + { + itr++; + } + + return *itr; + } + return cMonster::mtInvalidType; +} + + +bool cMobSpawner::CanSpawnHere(cMonster::eType a_MobType, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level) +{ + bool toReturn = false; + std::set<cMonster::eType>::iterator itr = m_AllowedTypes.find(a_MobType); + if (itr != m_AllowedTypes.end()) + { + // MG TODO : find a nicer paging + if (a_MobType == cMonster::mtSquid) + { + toReturn = ( + IsBlockLiquid(a_BlockType) && + a_Level >= 45 && + a_Level <= 62 + ); + } + else if (a_MobType == cMonster::mtBat) + { + toReturn = a_Level <= 60; // MG TODO : find a real rule + } + else + { + if ( + a_BlockType == E_BLOCK_AIR && + a_BlockType_above == E_BLOCK_AIR && + ! (g_BlockTransparent[a_BlockType_below]) + ) + { + if (a_MobType == cMonster::mtChicken || a_MobType == cMonster::mtPig || a_MobType == cMonster::mtCow || a_MobType == cMonster::mtSheep) + { + toReturn = ( + a_BlockType_below == E_BLOCK_GRASS /*&& // MG TODO + a_LightLevel >= 9 */ + ); + } + else if (a_MobType == cMonster::mtOcelot) + { + toReturn = ( + a_Level >= 62 && + ( + a_BlockType_below == E_BLOCK_GRASS || + a_BlockType_below == E_BLOCK_LEAVES + ) && + m_Random.NextInt(3,a_Biome) != 0 + ); + } + else if (a_MobType == cMonster::mtCreeper || a_MobType == cMonster::mtSkeleton || a_MobType == cMonster::mtZombie || a_MobType == cMonster::mtSpider || a_MobType == cMonster::mtEnderman || a_MobType == cMonster::mtZombiePigman) + { + toReturn = true /*a_LightLevel <= 7 MG TODO*/; + /*if (a_SunLight) MG TODO + { + if (m_Random.NextInt(2,a_Biome) != 0) + { + toReturn = false; + } + }*/ + } + else if (a_MobType == cMonster::mtSlime) + { + toReturn = a_Level <= 40; + // MG TODO : much more complicated rules + } + else if (a_MobType == cMonster::mtGhast) + { + toReturn = m_Random.NextInt(20,a_Biome) == 0; + } + else + { + LOGD("MG TODO : check I've got a Rule to write for type %d",a_MobType); + toReturn = true; + } + } + } + } + return toReturn; +} + + +cMonster* cMobSpawner::TryToSpawnHere(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level, int& a_MaxPackSize) +{ + cMonster* toReturn = NULL; + if (m_NewPack) + { + m_MobType = ChooseMobType(a_Biome); + if (m_MobType == cMonster::mtInvalidType) + { + return toReturn; + } + if (m_MobType == cMonster::mtWolf) + { + a_MaxPackSize = 8; + } + else if (m_MobType == cMonster::mtGhast) + { + a_MaxPackSize = 1; + } + m_NewPack = false; + } + + + if (CanSpawnHere(m_MobType, a_BlockType, a_BlockMeta, a_BlockType_below, a_BlockMeta_below, a_BlockType_above, a_BlockMeta_above, a_Biome, a_Level)) + { + cMonster* newMob = cMobTypesManager::NewMonsterFromType(m_MobType); + if (newMob) + { + m_Spawned.insert(newMob); + } + toReturn = newMob; + } + return toReturn; +} + +void cMobSpawner::NewPack() +{ + m_NewPack = true; +} + +cMobSpawner::tSpawnedContainer& cMobSpawner::getSpawned() +{ + return m_Spawned; +} + +bool cMobSpawner::CanSpawnSomething() +{ + return m_AllowedTypes.size() > 0; +} diff --git a/source/MobSpawner.h b/source/MobSpawner.h new file mode 100644 index 000000000..bb9e95172 --- /dev/null +++ b/source/MobSpawner.h @@ -0,0 +1,72 @@ + +#pragma once + +#include <set> +#include "BlockID.h" +#include "ChunkDef.h" +#include "FastRandom.h" +#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it + +class cChunk; + + +// This class is used to determine wich monster can be spawned on wich place +// it is essentially static (f.i. Squids spawn in water, Zombie spawn in dark places) +// but it also has dynamic part depending on the world.ini +class cMobSpawner +{ +public : + // constructor + // a_MobFamily is the Family of mobs that this spawner will spawn + // a_AllowedTypes is the set of types allowed for mobs it will spawn. Empty set + // would result in no spawn at all + // Allowed mobs thah are not of the right Family will not be include (no warning) + cMobSpawner(cMonster::eFamily MobFamily, const std::set<cMonster::eType>& a_AllowedTypes); + + // Check if specified block can be a Pack center for this spawner + bool CheckPackCenter(BLOCKTYPE a_BlockType); + + // Try to create a monster here + // if this is the first of a Pack : determine the type of monster + // BlockType & BlockMeta are use to know what kind of Mob can Spawn here + // MaxPackSize is set to the maximal size for a pack this type of mob + cMonster* TryToSpawnHere(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level, int& a_MaxPackSize); + + // mark the beginning of a new Pack + // all mobs of the same Pack are the same type + void NewPack(); + + // return true if there is at least one allowed type + bool CanSpawnSomething(); + + typedef const std::set<cMonster*> tSpawnedContainer; + tSpawnedContainer& getSpawned(); + +protected : + // return true if specified type of mob can spawn on specified block + bool CanSpawnHere(cMonster::eType a_MobType, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level); + + // return a random type that can spawn on specified biome. + // returns E_ENTITY_TYPE_DONOTUSE if none is possible + cMonster::eType ChooseMobType(EMCSBiome a_Biome); + + // add toAdd inside toAddIn, if toAdd is in m_AllowedTypes + void addIfAllowed(cMonster::eType toAdd, std::set<cMonster::eType>& toAddIn); + +protected : + cMonster::eFamily m_MonsterFamily; + std::set<cMonster::eType> m_AllowedTypes; + bool m_NewPack; + cMonster::eType m_MobType; + std::set<cMonster*> m_Spawned; + cFastRandom m_Random; + +public : + typedef const std::set<cMonster::eType> tMobTypes; // MG TODO : maybe relocate all those statics set/maps in the same place ? + static tMobTypes& m_MobTypes(); + +protected : + static tMobTypes initMobTypesBeforeCx11(); + + +}; diff --git a/source/MobTypesManager.cpp b/source/MobTypesManager.cpp new file mode 100644 index 000000000..2d24bd39b --- /dev/null +++ b/source/MobTypesManager.cpp @@ -0,0 +1,182 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobTypesManager.h" +#include "MersenneTwister.h" +#include "Mobs/Monster.h" +#include "Mobs/IncludeAllMonsters.h" +#include "FastRandom.h" + + +cMobTypesManager::tMobTypes2Names& cMobTypesManager::m_MobsTypes2Names() +{ + static std::map<cMonster::eType,std::string>* value = new std::map<cMonster::eType,std::string>(MobTypes2NamesInitializerBeforeCx11()); + return *value; +} + +cMobTypesManager::tMobTypes2Names cMobTypesManager::MobTypes2NamesInitializerBeforeCx11() +{ + std::map<cMonster::eType,std::string> toReturn; + typedef std::map<cMonster::eType,std::string>::value_type ValueType; + toReturn.insert(ValueType(cMonster::mtMagmaCube,"Magmacube")); + toReturn.insert(ValueType(cMonster::mtSlime,"Slime")); + toReturn.insert(ValueType(cMonster::mtBat,"Bat")); + toReturn.insert(ValueType(cMonster::mtBlaze,"Blaze")); + toReturn.insert(ValueType(cMonster::mtCaveSpider,"Cavespider")); + toReturn.insert(ValueType(cMonster::mtChicken,"Chicken")); + toReturn.insert(ValueType(cMonster::mtCow,"Cow")); + toReturn.insert(ValueType(cMonster::mtCreeper,"Creeper")); + toReturn.insert(ValueType(cMonster::mtEnderman,"Enderman")); + toReturn.insert(ValueType(cMonster::mtGhast,"Ghast")); + toReturn.insert(ValueType(cMonster::mtMooshroom,"Mooshroom")); + toReturn.insert(ValueType(cMonster::mtOcelot,"Ocelot")); + toReturn.insert(ValueType(cMonster::mtPig,"Pig")); + toReturn.insert(ValueType(cMonster::mtSheep,"Sheep")); + toReturn.insert(ValueType(cMonster::mtSilverfish,"Silverfish")); + toReturn.insert(ValueType(cMonster::mtSkeleton,"Skeleton")); + toReturn.insert(ValueType(cMonster::mtSpider,"Spider")); + toReturn.insert(ValueType(cMonster::mtSquid,"Squid")); + toReturn.insert(ValueType(cMonster::mtVillager,"Villager")); + toReturn.insert(ValueType(cMonster::mtWitch,"Witch")); + toReturn.insert(ValueType(cMonster::mtWolf,"Wolf")); + toReturn.insert(ValueType(cMonster::mtZombie,"Zombie")); + toReturn.insert(ValueType(cMonster::mtZombiePigman,"Zombiepigman")); + return toReturn; +} + +cMobTypesManager::tMobType2Family& cMobTypesManager::m_MobsType2Family() +{ + static std::map<cMonster::eType,cMonster::eFamily>* value = new std::map<cMonster::eType,cMonster::eFamily>(MobType2FamilyInitializerBeforeCx11()); + return *value; +} + +cMobTypesManager::tMobType2Family cMobTypesManager::MobType2FamilyInitializerBeforeCx11() +{ + std::map<cMonster::eType,cMonster::eFamily> toReturn; + typedef std::map<cMonster::eType,cMonster::eFamily>::value_type ValueType; + toReturn.insert(ValueType(cMonster::mtBat,cMonster::mfAmbient)); + toReturn.insert(ValueType(cMonster::mtSquid,cMonster::mfWater)); + toReturn.insert(ValueType(cMonster::mtCow,cMonster::mfPassive)); + toReturn.insert(ValueType(cMonster::mtPig,cMonster::mfPassive)); + toReturn.insert(ValueType(cMonster::mtSheep,cMonster::mfPassive)); + toReturn.insert(ValueType(cMonster::mtChicken,cMonster::mfPassive)); + toReturn.insert(ValueType(cMonster::mtVillager,cMonster::mfPassive)); + toReturn.insert(ValueType(cMonster::mtMagmaCube,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtSlime,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtBlaze,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtCaveSpider,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtCreeper,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtEnderman,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtGhast,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtMooshroom,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtOcelot,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtSilverfish,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtSkeleton,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtSpider,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtWitch,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtWolf,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtZombie,cMonster::mfHostile)); + toReturn.insert(ValueType(cMonster::mtZombiePigman,cMonster::mfHostile)); + + return toReturn; +} + + +cFastRandom& cMobTypesManager::m_Random() +{ + static cFastRandom* value = new cFastRandom(); + return *value; +} + + +cMonster* cMobTypesManager::NewMonsterFromType(cMonster::eType a_MobType, int a_Size) +{ + cMonster * toReturn = NULL; + + // unspecified size get rand[1,3] for Monsters that need size + switch (a_MobType) + { + case cMonster::mtMagmaCube: + case cMonster::mtSlime: + if (a_Size == -1) + { + a_Size = m_Random().NextInt(2,a_MobType)+1; + } + if (a_Size <= 0 || a_Size >= 4) + { + ASSERT(!"Random for size was supposed to pick in [1..3] and picked outside"); + a_Size = 1; + } + break; + default : break; + } + + // the big switch + switch (a_MobType) + { + case cMonster::mtMagmaCube: toReturn = new cMagmacube(a_Size); break; + case cMonster::mtSlime: toReturn = new cSlime(a_Size); break; + case cMonster::mtBat: toReturn = new cBat(); break; + case cMonster::mtBlaze: toReturn = new cBlaze(); break; + case cMonster::mtCaveSpider: toReturn = new cCavespider(); break; + case cMonster::mtChicken: toReturn = new cChicken(); break; + case cMonster::mtCow: toReturn = new cCow(); break; + case cMonster::mtCreeper: toReturn = new cCreeper(); break; + case cMonster::mtEnderman: toReturn = new cEnderman(); break; + case cMonster::mtGhast: toReturn = new cGhast(); break; + case cMonster::mtMooshroom: toReturn = new cMooshroom(); break; + case cMonster::mtOcelot: toReturn = new cOcelot(); break; + case cMonster::mtPig: toReturn = new cPig(); break; + case cMonster::mtSheep: toReturn = new cSheep(); break; + case cMonster::mtSilverfish: toReturn = new cSilverfish(); break; + case cMonster::mtSkeleton: toReturn = new cSkeleton(); break; + case cMonster::mtSpider: toReturn = new cSpider(); break; + case cMonster::mtSquid: toReturn = new cSquid(); break; + case cMonster::mtVillager: toReturn = new cVillager(); break; + case cMonster::mtWitch: toReturn = new cWitch(); break; + case cMonster::mtWolf: toReturn = new cWolf(); break; + case cMonster::mtZombie: toReturn = new cZombie(); break; + case cMonster::mtZombiePigman: toReturn = new cZombiepigman(); break; + default: + { + ASSERT(!"Unhandled Mob type"); + } + } + return toReturn; +} + + +const std::string& cMobTypesManager::fromMobTypeToString(cMonster::eType a_MobType) +{ + static std::string toReturnDefault = ""; + std::string& toReturn = toReturnDefault; + std::map<cMonster::eType,std::string>::const_iterator itr = m_MobsTypes2Names().find(a_MobType); + if (itr != m_MobsTypes2Names().end()) + { + toReturn = itr->second; + } + return toReturn; +} + +cMonster::eType cMobTypesManager::fromStringToMobType(const std::string& a_Name) +{ + for(std::map<cMonster::eType,std::string>::const_iterator itr = m_MobsTypes2Names().begin(); itr != m_MobsTypes2Names().end(); itr++) + { + if (itr->second == a_Name) + { + return itr->first; + } + } + return cMonster::mtInvalidType; +} + +cMonster::eFamily cMobTypesManager::getFamilyFromType(cMonster::eType a_Type) +{ + cMonster::eFamily toReturn = cMonster::mfMaxplusone; + std::map<cMonster::eType,cMonster::eFamily>::const_iterator itr = m_MobsType2Family().find(a_Type); + if (itr != m_MobsType2Family().end()) + { + toReturn = itr->second; + } + return toReturn; +} diff --git a/source/MobTypesManager.h b/source/MobTypesManager.h new file mode 100644 index 000000000..941dac729 --- /dev/null +++ b/source/MobTypesManager.h @@ -0,0 +1,44 @@ + +#pragma once + +#include <vector> +#include "Mobs/Monster.h" // this is a side effect of declaring cMonster::eType inside cMonster MG TODO : make a namespace + +class cFastRandom; + +// this aggregate static functionnalities about mob types (some could call it helper) +// functionnalities are (in the first version) : +// - create a mob from its type (as enum) (in that way it is a compiler-proxy for mobs) +// - can transform MobTypes from enums to string and reciprocal +// - return mob family from providen type +class cMobTypesManager +{ +public: + static const std::string& fromMobTypeToString(cMonster::eType a_MobType); + static cMonster::eType fromStringToMobType(const std::string& a_MobTypeName); + static cMonster::eFamily getFamilyFromType(cMonster::eType a_MobType); + +protected : + typedef const std::map<cMonster::eType,std::string> tMobTypes2Names; + static tMobTypes2Names& m_MobsTypes2Names(); + static tMobTypes2Names MobTypes2NamesInitializerBeforeCx11(); + + typedef const std::map<cMonster::eType,cMonster::eFamily> tMobType2Family; + static tMobType2Family& m_MobsType2Family(); + static tMobType2Family MobType2FamilyInitializerBeforeCx11(); + + static cFastRandom& m_Random(); + +public : + /** create a new object of the specified mob. + Warning, new without delete here; + a_MobType is the type of the mob to be created + a_Size is the size (for mobs with size) + if a_Size is let to -1 for entities that need size, size will be random + assert or return null if mob type is not specified + assert if size < 1 or > 3 for entities that need size + */ + static cMonster* NewMonsterFromType(cMonster::eType a_MobType, int a_Size=-1); + +}; // tolua_export + diff --git a/source/Mobs/AggressiveMonster.cpp b/source/Mobs/AggressiveMonster.cpp index 2eae772d7..93dba6d7b 100644 --- a/source/Mobs/AggressiveMonster.cpp +++ b/source/Mobs/AggressiveMonster.cpp @@ -95,5 +95,3 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) } - - diff --git a/source/Mobs/AggressiveMonster.h b/source/Mobs/AggressiveMonster.h index 1eff1831e..f22ed5b89 100644 --- a/source/Mobs/AggressiveMonster.h +++ b/source/Mobs/AggressiveMonster.h @@ -19,6 +19,7 @@ public: virtual void InStateChasing(float a_Dt) override; virtual void EventSeePlayer(cEntity *) override; + protected: float m_ChaseTime; diff --git a/source/Mobs/Bat.cpp b/source/Mobs/Bat.cpp new file mode 100644 index 000000000..715f25483 --- /dev/null +++ b/source/Mobs/Bat.cpp @@ -0,0 +1,15 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Bat.h" +#include "../Vector3d.h" +#include "../Chunk.h" + + +cBat::cBat(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Bat", 65, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7) +{ +} + + diff --git a/source/Mobs/Bat.h b/source/Mobs/Bat.h index 8e4cde29f..fd3e00a07 100644 --- a/source/Mobs/Bat.h +++ b/source/Mobs/Bat.h @@ -13,13 +13,10 @@ class cBat : typedef cPassiveMonster super; public: - cBat(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Bat", 65, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7) - { - } + cBat(void); CLASS_PROTODEF(cBat); + } ; diff --git a/source/Mobs/IncludeAllMonsters.h b/source/Mobs/IncludeAllMonsters.h new file mode 100644 index 000000000..d89a6c5b5 --- /dev/null +++ b/source/Mobs/IncludeAllMonsters.h @@ -0,0 +1,23 @@ +#include "Bat.h" +#include "Blaze.h" +#include "Cavespider.h" +#include "Chicken.h" +#include "Cow.h" +#include "Creeper.h" +#include "Enderman.h" +#include "Ghast.h" +#include "Magmacube.h" +#include "Mooshroom.h" +#include "Ocelot.h" +#include "Pig.h" +#include "Sheep.h" +#include "Silverfish.h" +#include "Skeleton.h" +#include "Slime.h" +#include "Spider.h" +#include "Squid.h" +#include "Villager.h" +#include "Witch.h" +#include "Wolf.h" +#include "Zombie.h" +#include "Zombiepigman.h" diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp index a42ae30ee..b378b2bc6 100644 --- a/source/Mobs/Monster.cpp +++ b/source/Mobs/Monster.cpp @@ -9,6 +9,7 @@ #include "../Entities/Player.h" #include "../Defines.h" #include "../MonsterConfig.h" +#include "../MobTypesManager.h" #include "../MersenneTwister.h" #include "../Vector3f.h" @@ -17,6 +18,7 @@ #include "../Tracer.h" #include "../Chunk.h" + // #include "../../iniFile/iniFile.h" @@ -510,3 +512,7 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk) +cMonster::eFamily cMonster::GetMobFamily(void) const +{ + return cMobTypesManager::getFamilyFromType(GetMobTypeAsEnum()); +} diff --git a/source/Mobs/Monster.h b/source/Mobs/Monster.h index 5f33d4450..e08fd518a 100644 --- a/source/Mobs/Monster.h +++ b/source/Mobs/Monster.h @@ -54,6 +54,18 @@ public: mtOcelot = E_META_SPAWN_EGG_OCELOT, mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM, mtVillager = E_META_SPAWN_EGG_VILLAGER, + + mtInvalidType, // MG TODO : be sure this is the way we do in this project. (needed inside cMobSpawner::ChooscMonster for instance if nothing can be spawned) + } ; + + enum eFamily + { + mfHostile = 0, // Spider, Zombies ... + mfPassive = 1, // Cows, Pigs + mfAmbient = 2, // Bats + mfWater = 3, // Squid + + mfMaxplusone, // Nothing. Be sure this is the last and the others are in order } ; // tolua_end @@ -80,7 +92,10 @@ public: virtual void MoveToPosition(const Vector3f & a_Position); virtual bool ReachedDestination(void); - char GetMobType(void) const {return m_MobType; } + char GetMobType(void) const {return m_MobType; } // MG TODO : see if we can delete this one. + eType GetMobTypeAsEnum(void) const {return (eType)m_MobType; } // MG TODO : see if we should store m_MobType as enum instead of char. + eFamily GetMobFamily(void) const; + const char * GetState(); void SetState(const AString & str); diff --git a/source/Mobs/PassiveMonster.cpp b/source/Mobs/PassiveMonster.cpp index 7a6140c04..8c69c8059 100644 --- a/source/Mobs/PassiveMonster.cpp +++ b/source/Mobs/PassiveMonster.cpp @@ -56,3 +56,4 @@ void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk) + diff --git a/source/Mobs/PassiveMonster.h b/source/Mobs/PassiveMonster.h index ae0bea3fb..908bb0ce6 100644 --- a/source/Mobs/PassiveMonster.h +++ b/source/Mobs/PassiveMonster.h @@ -19,6 +19,7 @@ public: /// When hit by someone, run away virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + } ; diff --git a/source/Mobs/Squid.cpp b/source/Mobs/Squid.cpp index cb796f5ec..e6a44079a 100644 --- a/source/Mobs/Squid.cpp +++ b/source/Mobs/Squid.cpp @@ -54,4 +54,3 @@ void cSquid::Tick(float a_Dt, cChunk & a_Chunk) - diff --git a/source/Mobs/Squid.h b/source/Mobs/Squid.h index 35d7295b3..ad299b95c 100644 --- a/source/Mobs/Squid.h +++ b/source/Mobs/Squid.h @@ -20,6 +20,7 @@ public: CLASS_PROTODEF(cSquid); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + } ; diff --git a/source/World.cpp b/source/World.cpp index a3572a48a..e5011e65d 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -28,29 +28,11 @@ #include "Simulator/VaporizeFluidSimulator.h" // Mobs: -#include "Mobs/Bat.h" -#include "Mobs/Blaze.h" -#include "Mobs/Cavespider.h" -#include "Mobs/Chicken.h" -#include "Mobs/Cow.h" -#include "Mobs/Creeper.h" -#include "Mobs/Enderman.h" -#include "Mobs/Ghast.h" -#include "Mobs/Magmacube.h" -#include "Mobs/Mooshroom.h" -#include "Mobs/Ocelot.h" -#include "Mobs/Pig.h" -#include "Mobs/Sheep.h" -#include "Mobs/Silverfish.h" -#include "Mobs/Skeleton.h" -#include "Mobs/Slime.h" -#include "Mobs/Spider.h" -#include "Mobs/Squid.h" -#include "Mobs/Villager.h" -#include "Mobs/Witch.h" -#include "Mobs/Wolf.h" -#include "Mobs/Zombie.h" -#include "Mobs/Zombiepigman.h" +#include "Mobs/IncludeAllMonsters.h" +#include "MobCensus.h" +#include "MobSpawner.h" +#include "MobTypesManager.h" + #include "OSSupport/MakeDir.h" #include "MersenneTwister.h" @@ -247,7 +229,6 @@ cWorld::cWorld(const AString & a_WorldName) : m_WorldAge(0), m_TimeOfDay(0), m_LastTimeUpdate(0), - m_LastSpawnMonster(0), m_RSList(0), m_Weather(eWeather_Sunny), m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :) @@ -510,15 +491,19 @@ void cWorld::Start(void) m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode); - m_bAnimals = true; - m_SpawnMonsterRate = 200; // 1 mob each 10 seconds - cIniFile IniFile2("settings.ini"); - if (IniFile2.ReadFile()) + m_bAnimals = IniFile.GetValueB("Monsters", "AnimalsOn", true); + AString sAllMonsters = IniFile.GetValue("Monsters", "Types"); + AStringVector SplitList = StringSplit(sAllMonsters, ","); + for (unsigned int i = 0; i < SplitList.size(); ++i) { - m_bAnimals = IniFile2.GetValueB("Monsters", "AnimalsOn", true); - m_SpawnMonsterRate = (Int64)(IniFile2.GetValueF("Monsters", "AnimalSpawnInterval", 10) * 20); // Convert from secs to ticks - - } + cMonster::eType ToAdd = cMobTypesManager::fromStringToMobType(SplitList[i]); + if (ToAdd != cMonster::mtInvalidType) + { + m_AllowedMobs.insert(ToAdd); + LOGD("Allowed mob: %s",cMobTypesManager::fromMobTypeToString(ToAdd).c_str()); // a bit reverse working, but very few ressources wasted + } + }; + m_ChunkMap = new cChunkMap(this); @@ -548,6 +533,13 @@ void cWorld::Start(void) m_ChunkSender.Start(this); m_TickThread.Start(); + // Init of the spawn monster time (as they are supposed to have different spawn rate) + m_LastSpawnMonster.insert(std::map<cMonster::eFamily,Int64>::value_type(cMonster::mfHostile,0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily,Int64>::value_type(cMonster::mfPassive,0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily,Int64>::value_type(cMonster::mfAmbient,0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily,Int64>::value_type(cMonster::mfWater,0)); + + // Save any changes that the defaults may have done to the ini file: if (!IniFile.WriteFile()) { @@ -643,7 +635,7 @@ void cWorld::Tick(float a_Dt) UnloadUnusedChunks(); } - TickSpawnMobs(a_Dt); + TickMobs(a_Dt); std::vector<int> m_RSList_copy(m_RSList); @@ -728,104 +720,57 @@ void cWorld::TickWeather(float a_Dt) -void cWorld::TickSpawnMobs(float a_Dt) +void cWorld::TickMobs(float a_Dt) { - if (!m_bAnimals || (m_WorldAge - m_LastSpawnMonster <= m_SpawnMonsterRate)) + if (!m_bAnimals) { return; } - - m_LastSpawnMonster = m_WorldAge; - Vector3d SpawnPos; - { - cCSLock Lock(m_CSPlayers); - if (m_Players.size() <= 0) - { - return; - } - int RandomPlayerIdx = m_TickRand.randInt() & m_Players.size(); - cPlayerList::iterator itr = m_Players.begin(); - for (int i = 1; i < RandomPlayerIdx; i++) - { - itr++; - } - SpawnPos = (*itr)->GetPosition(); - } - int dayRand = (m_TickRand.randInt() / 7) % 6; - int nightRand = (m_TickRand.randInt() / 11) % 10; + // before every Mob action, we have to "counts" them depending on the distance to players, on their megatype ... + cMobCensus MobCensus; + m_ChunkMap->CollectMobCensus(MobCensus); - SpawnPos += Vector3d((double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32); - int Height = GetHeight((int)SpawnPos.x, (int)SpawnPos.z); - - int MobType = -1; - int Biome = GetBiomeAt((int)SpawnPos.x, (int)SpawnPos.z); - switch (Biome) + for(cMobFamilyCollecter::tMobFamilyList::const_iterator itr = cMobFamilyCollecter::m_AllFamilies().begin(); itr != cMobFamilyCollecter::m_AllFamilies().end(); itr++) { - case biNether: - { - // Spawn nether mobs - switch (nightRand) - { - case 5: MobType = cMonster::mtGhast; break; - case 6: MobType = cMonster::mtZombiePigman; break; - } - break; - } - - case biEnd: - { - // Only endermen spawn in the End - MobType = cMonster::mtEnderman; - break; - } - - case biMushroomIsland: - case biMushroomShore: - { - // Mushroom land gets only mooshrooms - MobType = cMonster::mtMooshroom; - break; - } - - default: + cMobCensus::tMobSpawnRate::const_iterator spawnrate = cMobCensus::m_SpawnRate().find(*itr); + // hostile mobs are spawned more often + if (spawnrate != cMobCensus::m_SpawnRate().end() && m_LastSpawnMonster[*itr] < m_WorldAge - spawnrate->second) { - // Overworld biomes depend on whether it's night or day: - if (m_TimeOfDay >= 12000 + 1000) - { - // Night mobs: - switch (nightRand) - { - case 0: MobType = cMonster::mtSpider; break; - case 1: MobType = cMonster::mtZombie; break; - case 2: MobType = cMonster::mtEnderman; break; - case 3: MobType = cMonster::mtCreeper; break; - case 4: MobType = cMonster::mtCaveSpider; break; - case 7: MobType = cMonster::mtSlime; break; - case 8: MobType = cMonster::mtSilverfish; break; - case 9: MobType = cMonster::mtSkeleton; break; - } - } // if (night) - else + m_LastSpawnMonster[*itr] = m_WorldAge; + // each megatype of mob has it's own cap + if (!(MobCensus.isCaped(*itr))) { - // During the day: - switch (dayRand) + if (m_bAnimals) { - case 0: MobType = cMonster::mtChicken; break; - case 1: MobType = cMonster::mtCow; break; - case 2: MobType = cMonster::mtPig; break; - case 3: MobType = cMonster::mtSheep; break; - case 4: MobType = cMonster::mtSquid; break; - case 5: MobType = cMonster::mtWolf; break; + cMobSpawner Spawner(*itr,m_AllowedMobs); + if (Spawner.CanSpawnSomething()) + { + m_ChunkMap->SpawnMobs(Spawner); + // do the spawn + + for(cMobSpawner::tSpawnedContainer::const_iterator itr2 = Spawner.getSpawned().begin(); itr2 != Spawner.getSpawned().end(); itr2++) + { + SpawnMobFinalize(*itr2); + } + } } - } // else (night) - } // case overworld biomes - } // switch (biome) + } + } + } + + // move close mobs + cMobProximityCounter::sIterablePair allCloseEnoughToMoveMobs = MobCensus.getProximityCounter().getMobWithinThosesDistances(-1,64*16);// MG TODO : deal with this magic number (the 16 is the size of a block) + for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allCloseEnoughToMoveMobs.m_Begin; itr != allCloseEnoughToMoveMobs.m_End; itr++) + { + itr->second.m_Monster.Tick(a_Dt,itr->second.m_Chunk); + } - if (MobType >= 0) + // remove too far mobs + cMobProximityCounter::sIterablePair allTooFarMobs = MobCensus.getProximityCounter().getMobWithinThosesDistances(128*16,-1);// MG TODO : deal with this magic number (the 16 is the size of a block) + for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allTooFarMobs.m_Begin; itr != allTooFarMobs.m_End; itr++) { - // A proper mob type was selected, now spawn the mob: - SpawnMob(SpawnPos.x, SpawnPos.y, SpawnPos.z, (cMonster::eType)MobType); + itr->second.m_Monster.Destroy(true); } } @@ -2594,55 +2539,32 @@ int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eTyp { cMonster * Monster = NULL; - int Size = GetTickRandomNumber(2) + 1; // 1 .. 3 - - switch (a_MonsterType) - { - case cMonster::mtBat: Monster = new cBat(); break; - case cMonster::mtBlaze: Monster = new cBlaze(); break; - case cMonster::mtCaveSpider: Monster = new cCavespider(); break; - case cMonster::mtChicken: Monster = new cChicken(); break; - case cMonster::mtCow: Monster = new cCow(); break; - case cMonster::mtCreeper: Monster = new cCreeper(); break; - case cMonster::mtEnderman: Monster = new cEnderman(); break; - case cMonster::mtGhast: Monster = new cGhast(); break; - case cMonster::mtMagmaCube: Monster = new cMagmacube(Size); break; - case cMonster::mtMooshroom: Monster = new cMooshroom(); break; - case cMonster::mtOcelot: Monster = new cOcelot(); break; - case cMonster::mtPig: Monster = new cPig(); break; - case cMonster::mtSheep: Monster = new cSheep(); break; - case cMonster::mtSilverfish: Monster = new cSilverfish(); break; - case cMonster::mtSkeleton: Monster = new cSkeleton(); break; - case cMonster::mtSlime: Monster = new cSlime(Size); break; - case cMonster::mtSpider: Monster = new cSpider(); break; - case cMonster::mtSquid: Monster = new cSquid(); break; - case cMonster::mtVillager: Monster = new cVillager(); break; - case cMonster::mtWitch: Monster = new cWitch(); break; - case cMonster::mtWolf: Monster = new cWolf(); break; - case cMonster::mtZombie: Monster = new cZombie(); break; - case cMonster::mtZombiePigman: Monster = new cZombiepigman(); break; - - default: - { - LOGWARNING("%s: Unhandled monster type: %d. Not spawning.", __FUNCTION__, a_MonsterType); - return -1; - } - } + cMobTypesManager::NewMonsterFromType(a_MonsterType); Monster->SetPosition(a_PosX, a_PosY, a_PosZ); - Monster->SetHealth(Monster->GetMaxHealth()); - if (cPluginManager::Get()->CallHookSpawningMonster(*this, *Monster)) + + return SpawnMobFinalize(Monster); +} + + + + +int cWorld::SpawnMobFinalize(cMonster* a_Monster) +{ + a_Monster->SetHealth(a_Monster->GetMaxHealth()); + if (cPluginManager::Get()->CallHookSpawningMonster(*this, *a_Monster)) { - delete Monster; + delete a_Monster; return -1; } - if (!Monster->Initialize(this)) + if (!a_Monster->Initialize(this)) { - delete Monster; + delete a_Monster; return -1; } - BroadcastSpawnEntity(*Monster); - cPluginManager::Get()->CallHookSpawnedMonster(*this, *Monster); - return Monster->GetUniqueID(); + BroadcastSpawnEntity(*a_Monster); + cPluginManager::Get()->CallHookSpawnedMonster(*this, *a_Monster); + + return a_Monster->GetUniqueID(); } diff --git a/source/World.h b/source/World.h index 315904f79..359fbe68e 100644 --- a/source/World.h +++ b/source/World.h @@ -42,6 +42,7 @@ class cChunkGenerator; // The thread responsible for generating chunks class cChestEntity;
class cDispenserEntity;
class cFurnaceEntity;
+class cMobCensus;
typedef std::list< cPlayer * > cPlayerList;
@@ -573,6 +574,7 @@ public: /// Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise
int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType); // tolua_export
+ int SpawnMobFinalize(cMonster* a_Monster);
/// Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise
int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed = NULL); // tolua_export
@@ -625,7 +627,7 @@ private: Int64 m_LastTimeUpdate; // The tick in which the last time update has been sent.
Int64 m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred
Int64 m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred
- Int64 m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned
+ std::map<cMonster::eFamily,Int64> m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed)
eGameMode m_GameMode;
bool m_bEnabledPVP;
@@ -655,7 +657,7 @@ private: cChunkMap * m_ChunkMap;
bool m_bAnimals;
- Int64 m_SpawnMonsterRate;
+ std::set<cMonster::eType> m_AllowedMobs;
eWeather m_Weather;
int m_WeatherInterval;
@@ -710,8 +712,8 @@ private: /// Handles the weather in each tick
void TickWeather(float a_Dt);
- /// Handles the mob spawning each tick
- void TickSpawnMobs(float a_Dt);
+ /// Handles the mob spawning/moving/destroying each tick
+ void TickMobs(float a_Dt);
/// Executes all tasks queued onto the tick thread
void TickQueuedTasks(void);
|