summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattes D <github@xoft.cz>2013-10-18 12:41:43 +0200
committerMattes D <github@xoft.cz>2013-10-18 12:41:43 +0200
commit8bba2b02a132d236703835d93ab18e09e0ea7230 (patch)
treee637945c6eb845e82c4f2790c07c6fd82a1f79ef
parentAPIDump: Renamed output for helper stuff. (diff)
parentRemoved reading default values from settings.ini for worlds. (diff)
downloadcuberite-8bba2b02a132d236703835d93ab18e09e0ea7230.tar
cuberite-8bba2b02a132d236703835d93ab18e09e0ea7230.tar.gz
cuberite-8bba2b02a132d236703835d93ab18e09e0ea7230.tar.bz2
cuberite-8bba2b02a132d236703835d93ab18e09e0ea7230.tar.lz
cuberite-8bba2b02a132d236703835d93ab18e09e0ea7230.tar.xz
cuberite-8bba2b02a132d236703835d93ab18e09e0ea7230.tar.zst
cuberite-8bba2b02a132d236703835d93ab18e09e0ea7230.zip
-rw-r--r--.gitignore20
-rw-r--r--source/BlockID.cpp4
-rw-r--r--source/Chunk.cpp138
-rw-r--r--source/Chunk.h12
-rw-r--r--source/ChunkMap.cpp60
-rw-r--r--source/ChunkMap.h15
-rw-r--r--source/MobCensus.cpp89
-rw-r--r--source/MobCensus.h58
-rw-r--r--source/MobFamilyCollecter.cpp33
-rw-r--r--source/MobFamilyCollecter.h40
-rw-r--r--source/MobProximityCounter.cpp83
-rw-r--r--source/MobProximityCounter.h65
-rw-r--r--source/MobSpawner.cpp275
-rw-r--r--source/MobSpawner.h72
-rw-r--r--source/MobTypesManager.cpp184
-rw-r--r--source/MobTypesManager.h44
-rw-r--r--source/Mobs/AggressiveMonster.cpp2
-rw-r--r--source/Mobs/AggressiveMonster.h1
-rw-r--r--source/Mobs/Bat.cpp15
-rw-r--r--source/Mobs/Bat.h7
-rw-r--r--source/Mobs/IncludeAllMonsters.h29
-rw-r--r--source/Mobs/Monster.cpp6
-rw-r--r--source/Mobs/Monster.h15
-rw-r--r--source/Mobs/PassiveMonster.cpp1
-rw-r--r--source/Mobs/PassiveMonster.h1
-rw-r--r--source/Mobs/Squid.cpp1
-rw-r--r--source/Mobs/Squid.h1
-rw-r--r--source/World.cpp295
-rw-r--r--source/World.h10
29 files changed, 1349 insertions, 227 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 177652a46..2fe495c6f 100644
--- a/source/BlockID.cpp
+++ b/source/BlockID.cpp
@@ -662,6 +662,7 @@ public:
g_BlockTransparent[E_BLOCK_GLASS_PANE] = 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;
@@ -674,11 +675,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..600d7992d
--- /dev/null
+++ b/source/MobTypesManager.cpp
@@ -0,0 +1,184 @@
+
+#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;
+ // TODO: Implement sheep color
+ case cMonster::mtSheep: toReturn = new cSheep(0); break;
+ case cMonster::mtSilverfish: toReturn = new cSilverfish(); break;
+ // TODO: Implement wither geration
+ case cMonster::mtSkeleton: toReturn = new cSkeleton(false); break;
+ case cMonster::mtSpider: toReturn = new cSpider(); break;
+ case cMonster::mtSquid: toReturn = new cSquid(); break;
+ case cMonster::mtVillager: toReturn = new cVillager(cVillager::vtFarmer); break;
+ case cMonster::mtWitch: toReturn = new cWitch(); break;
+ case cMonster::mtWolf: toReturn = new cWolf(); break;
+ case cMonster::mtZombie: toReturn = new cZombie(false); 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 0b50e06cd..e878d0ee8 100644
--- a/source/Mobs/Bat.h
+++ b/source/Mobs/Bat.h
@@ -13,13 +13,10 @@ class cBat :
typedef cPassiveMonster super;
public:
- cBat(void) :
- super("Bat", 65, "mob.bat.hurt", "mob.bat.death", 0.5, 0.9)
- {
- }
+ cBat(void);
CLASS_PROTODEF(cBat);
-
+
bool IsHanging(void) const {return false; }
} ;
diff --git a/source/Mobs/IncludeAllMonsters.h b/source/Mobs/IncludeAllMonsters.h
new file mode 100644
index 000000000..1b436a11f
--- /dev/null
+++ b/source/Mobs/IncludeAllMonsters.h
@@ -0,0 +1,29 @@
+#include "Bat.h"
+#include "Blaze.h"
+#include "Cavespider.h"
+#include "Chicken.h"
+#include "Cow.h"
+#include "Creeper.h"
+#include "Enderman.h"
+#include "EnderDragon.h"
+#include "Ghast.h"
+#include "Giant.h"
+#include "Horse.h"
+#include "IronGolem.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 "SnowGolem.h"
+#include "Spider.h"
+#include "Squid.h"
+#include "Villager.h"
+#include "Witch.h"
+#include "Wither.h"
+#include "Wolf.h"
+#include "Zombie.h"
+#include "Zombiepigman.h"
diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp
index 334229a42..51ea644d3 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 d784f2eec..be60d9e00 100644
--- a/source/Mobs/Monster.h
+++ b/source/Mobs/Monster.h
@@ -55,7 +55,17 @@ public:
mtWolf = E_META_SPAWN_EGG_WOLF,
mtZombie = E_META_SPAWN_EGG_ZOMBIE,
mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN,
+ mtInvalidType
+ } ;
+
+ 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
@@ -82,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 ef56e7fe9..967cd8f59 100644
--- a/source/World.cpp
+++ b/source/World.cpp
@@ -28,35 +28,10 @@
#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/EnderDragon.h"
-#include "Mobs/Ghast.h"
-#include "Mobs/Giant.h"
-#include "Mobs/Horse.h"
-#include "Mobs/IronGolem.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/SnowGolem.h"
-#include "Mobs/Spider.h"
-#include "Mobs/Squid.h"
-#include "Mobs/Villager.h"
-#include "Mobs/Witch.h"
-#include "Mobs/Wither.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 "MersenneTwister.h"
#include "Generating/Trees.h"
@@ -252,7 +227,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 :)
@@ -515,15 +489,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);
@@ -553,6 +531,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())
{
@@ -648,7 +633,7 @@ void cWorld::Tick(float a_Dt)
UnloadUnusedChunks();
}
- TickSpawnMobs(a_Dt);
+ TickMobs(a_Dt);
std::vector<int> m_RSList_copy(m_RSList);
@@ -733,125 +718,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;
- 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)
+ // before every Mob action, we have to "counts" them depending on the distance to players, on their megatype ...
+ cMobCensus MobCensus;
+ m_ChunkMap->CollectMobCensus(MobCensus);
+ for(cMobFamilyCollecter::tMobFamilyList::const_iterator itr = cMobFamilyCollecter::m_AllFamilies().begin(); itr != cMobFamilyCollecter::m_AllFamilies().end(); itr++)
{
- case biNether:
- {
- // Spawn nether mobs
- switch (nightRand)
- {
- case 0: MobType = cMonster::mtBlaze; break;
- case 1: MobType = cMonster::mtGhast; break;
- case 2: MobType = cMonster::mtGhast; break;
- case 3: MobType = cMonster::mtGhast; break;
- case 4: MobType = cMonster::mtZombiePigman; break;
- case 5: MobType = cMonster::mtZombiePigman; break;
- case 6: MobType = cMonster::mtZombiePigman; break;
- case 7: MobType = cMonster::mtZombiePigman; break;
- case 8: MobType = cMonster::mtZombiePigman; break;
- case 9: MobType = cMonster::mtZombiePigman; break;
- }
- break;
- }
-
- case biEnd:
- {
- // Spawn only The End mobs
- switch (nightRand)
- {
- case 0: MobType = cMonster::mtEnderDragon; break;
- case 1: MobType = cMonster::mtEnderman; break;
- case 2: MobType = cMonster::mtEnderman; break;
- case 3: MobType = cMonster::mtEnderman; break;
- case 4: MobType = cMonster::mtEnderman; break;
- case 5: MobType = cMonster::mtEnderman; break;
- case 6: MobType = cMonster::mtEnderman; break;
- case 7: MobType = cMonster::mtEnderman; break;
- case 8: MobType = cMonster::mtEnderman; break;
- case 9: MobType = cMonster::mtEnderman; break;
- }
- 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;
- case 6: MobType = cMonster::mtHorse; 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,80 +2511,36 @@ int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eTyp
int SlSize = GetTickRandomNumber(2) + 1; // 1 .. 3 - Slime
int ShColor = GetTickRandomNumber(15); // 0 .. 15 - Sheep
- bool SkType = GetDimension() == biNether; // Skeleton
+ bool SkType = GetDimension() == dimNether ; // Skeleton
- int VilType = GetTickRandomNumber(cVillager::vtMax); // 0 .. 6 - Villager
- if (VilType == 6) { VilType = 0; } // Give farmers a better chance of spawning
+ Monster = cMobTypesManager::NewMonsterFromType(a_MonsterType);
+ if (Monster)
+ Monster->SetPosition(a_PosX, a_PosY, a_PosZ);
+ return SpawnMobFinalize(Monster);
+}
- int HseType = GetTickRandomNumber(7); // 0 .. 7 - Horse Type (donkey, zombie, etc.)
- int HseColor = GetTickRandomNumber(6); // 0 .. 6 - Horse
- int HseStyle = GetTickRandomNumber(4); // 0 .. 4 - Horse
- int HseTameTimes = GetTickRandomNumber(6) + 1; // 1 .. 7 - Horse tame amount
- if ((HseType == 5) || (HseType == 6) || (HseType == 7)) { HseType = 0; } // 5,6,7 = 0 because little chance of getting 0 with TickRand
-
- 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::mtEnderDragon: Monster = new cEnderDragon(); break;
- case cMonster::mtGhast: Monster = new cGhast(); break;
- case cMonster::mtGiant: Monster = new cGiant(); break;
- case cMonster::mtHorse:
- {
- Monster = new cHorse(HseType, HseColor, HseStyle, HseTameTimes); break;
- }
- case cMonster::mtIronGolem: Monster = new cIronGolem(); break;
- case cMonster::mtMagmaCube: Monster = new cMagmaCube(SlSize); 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(ShColor); break;
- case cMonster::mtSilverfish: Monster = new cSilverfish(); break;
- case cMonster::mtSkeleton: Monster = new cSkeleton(SkType); break;
- case cMonster::mtSlime: Monster = new cSlime(SlSize); break;
- case cMonster::mtSnowGolem: Monster = new cSnowGolem(); break;
- case cMonster::mtSpider: Monster = new cSpider(); break;
- case cMonster::mtSquid: Monster = new cSquid(); break;
- case cMonster::mtVillager:
- {
- Monster = new cVillager((cVillager::eVillagerType)VilType); break;
- }
- case cMonster::mtWitch: Monster = new cWitch(); break;
- case cMonster::mtWither: Monster = new cWither(); break;
- case cMonster::mtWolf: Monster = new cWolf(); break;
- case cMonster::mtZombie: Monster = new cZombie(false); break; // TODO: Villager infection
- case cMonster::mtZombiePigman: Monster = new cZombiePigman(); break;
-
- default:
- {
- LOGWARNING("%s: Unhandled monster type: %d. Not spawning.", __FUNCTION__, a_MonsterType);
- return -1;
- }
- }
- Monster->SetPosition(a_PosX, a_PosY, a_PosZ);
- Monster->SetHealth(Monster->GetMaxHealth());
- if (cPluginManager::Get()->CallHookSpawningMonster(*this, *Monster))
+
+
+
+int cWorld::SpawnMobFinalize(cMonster* a_Monster)
+{
+ if (!a_Monster)
+ return -1;
+ 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(*a_Monster);
+ cPluginManager::Get()->CallHookSpawnedMonster(*this, *a_Monster);
- BroadcastSpawnEntity(*Monster);
- // Because it's logical that ALL mob spawns need spawn effects, not just spawners
- BroadcastSoundParticleEffect(2004, (int)(floor(a_PosX) * 8), (int)(floor(a_PosY) * 8), (int)(floor(a_PosZ) * 8), 0);
-
- cPluginManager::Get()->CallHookSpawnedMonster(*this, *Monster);
- return Monster->GetUniqueID();
+ return a_Monster->GetUniqueID();
}
diff --git a/source/World.h b/source/World.h
index 25bc0b338..a91007b17 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;
@@ -580,6 +581,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
@@ -632,7 +634,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;
@@ -662,7 +664,7 @@ private:
cChunkMap * m_ChunkMap;
bool m_bAnimals;
- Int64 m_SpawnMonsterRate;
+ std::set<cMonster::eType> m_AllowedMobs;
eWeather m_Weather;
int m_WeatherInterval;
@@ -717,8 +719,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);