1
0
Fork 0

Merge branch 'MobSpawning' of https://github.com/mgueydan/MCServer into MobSpawning

Conflicts:
	source/Mobs/Bat.h
	source/Mobs/Monster.h
	source/World.cpp
This commit is contained in:
Samuel Barney 2013-10-14 10:03:47 -06:00
commit 318d552248
29 changed files with 1346 additions and 228 deletions

20
.gitignore vendored
View File

@ -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

View File

@ -663,6 +663,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;
@ -675,11 +676,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;

View File

@ -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:

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

89
source/MobCensus.cpp Normal file
View File

@ -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));
}

58
source/MobCensus.h Normal file
View File

@ -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();
};

View File

@ -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();
}

View File

@ -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();
};

View File

@ -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;
}

View File

@ -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);
};

275
source/MobSpawner.cpp Normal file
View File

@ -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;
}

72
source/MobSpawner.h Normal file
View File

@ -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();
};

184
source/MobTypesManager.cpp Normal file
View File

@ -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;
}

44
source/MobTypesManager.h Normal file
View File

@ -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

View File

@ -95,5 +95,3 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
}

View File

@ -19,6 +19,7 @@ public:
virtual void InStateChasing(float a_Dt) override;
virtual void EventSeePlayer(cEntity *) override;
protected:
float m_ChaseTime;

15
source/Mobs/Bat.cpp Normal file
View File

@ -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)
{
}

View File

@ -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; }
} ;

View File

@ -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"

View File

@ -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());
}

View File

@ -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);

View File

@ -56,3 +56,4 @@ void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk)

View File

@ -19,6 +19,7 @@ public:
/// When hit by someone, run away
virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
} ;

View File

@ -54,4 +54,3 @@ void cSquid::Tick(float a_Dt, cChunk & a_Chunk)

View File

@ -20,6 +20,7 @@ public:
CLASS_PROTODEF(cSquid);
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
} ;

View File

@ -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;
// 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++)
{
cCSLock Lock(m_CSPlayers);
if (m_Players.size() <= 0)
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)
{
return;
m_LastSpawnMonster[*itr] = m_WorldAge;
// each megatype of mob has it's own cap
if (!(MobCensus.isCaped(*itr)))
{
if (m_bAnimals)
{
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);
}
}
}
}
}
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)
// 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++)
{
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:
{
// 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
{
// During the day:
switch (dayRand)
{
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;
}
} // else (night)
} // case overworld biomes
} // switch (biome)
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);
}
}
@ -2596,80 +2513,32 @@ int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eTyp
int ShColor = GetTickRandomNumber(15); // 0 .. 15 - Sheep
bool SkType = GetDimension() == biNether; // Skeleton
int VilType = GetTickRandomNumber(cVillager::vtMax); // 0 .. 6 - Villager
if (VilType == 6) { VilType = 0; } // Give farmers a better chance of spawning
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;
}
}
while(this->GetBlock(a_PosX, a_PosY - 1, a_PosZ) == E_BLOCK_AIR)
--a_PosY;
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(*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();
}

View File

@ -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);