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:
commit
318d552248
20
.gitignore
vendored
20
.gitignore
vendored
@ -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
|
||||
|
@ -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;
|
||||
|
138
source/Chunk.cpp
138
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:
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
89
source/MobCensus.cpp
Normal 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
58
source/MobCensus.h
Normal 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();
|
||||
};
|
||||
|
33
source/MobFamilyCollecter.cpp
Normal file
33
source/MobFamilyCollecter.cpp
Normal 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();
|
||||
}
|
40
source/MobFamilyCollecter.h
Normal file
40
source/MobFamilyCollecter.h
Normal 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();
|
||||
};
|
||||
|
83
source/MobProximityCounter.cpp
Normal file
83
source/MobProximityCounter.cpp
Normal 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;
|
||||
}
|
65
source/MobProximityCounter.h
Normal file
65
source/MobProximityCounter.h
Normal 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
275
source/MobSpawner.cpp
Normal 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
72
source/MobSpawner.h
Normal 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
184
source/MobTypesManager.cpp
Normal 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
44
source/MobTypesManager.h
Normal 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
|
||||
|
@ -95,5 +95,3 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
15
source/Mobs/Bat.cpp
Normal 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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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; }
|
||||
} ;
|
||||
|
||||
|
29
source/Mobs/IncludeAllMonsters.h
Normal file
29
source/Mobs/IncludeAllMonsters.h
Normal 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"
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -56,3 +56,4 @@ void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@ public:
|
||||
|
||||
/// When hit by someone, run away
|
||||
virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -54,4 +54,3 @@ void cSquid::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -20,6 +20,7 @@ public:
|
||||
CLASS_PROTODEF(cSquid);
|
||||
|
||||
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
293
source/World.cpp
293
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;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user