2012-06-14 09:06:06 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
# include "BlockID.h"
2012-09-23 18:09:57 -04:00
# include "World.h"
2012-06-14 09:06:06 -04:00
# include "ChunkDef.h"
2012-09-23 18:09:57 -04:00
# include "ClientHandle.h"
# include "Pickup.h"
# include "Player.h"
# include "Server.h"
# include "Item.h"
# include "Root.h"
2012-06-14 09:06:06 -04:00
# include "../iniFile/iniFile.h"
2012-09-23 18:09:57 -04:00
# include "ChunkMap.h"
2012-10-13 05:53:28 -04:00
// Simulators:
# include "Simulator/SimulatorManager.h"
2012-10-14 13:06:21 -04:00
# include "Simulator/FloodyFluidSimulator.h"
2012-10-13 05:53:28 -04:00
# include "Simulator/FluidSimulator.h"
# include "Simulator/FireSimulator.h"
2013-03-14 16:03:42 -04:00
# include "Simulator/NoopFluidSimulator.h"
2012-10-13 05:53:28 -04:00
# include "Simulator/SandSimulator.h"
# include "Simulator/RedstoneSimulator.h"
2013-03-14 15:44:27 -04:00
# include "Simulator/VaporizeFluidSimulator.h"
2012-09-23 16:53:08 -04:00
// Mobs:
2012-11-01 06:56:47 -04:00
# include "Mobs/Bat.h"
2012-10-28 10:57:35 -04:00
# include "Mobs/Blaze.h"
# include "Mobs/Cavespider.h"
2012-09-23 16:53:08 -04:00
# include "Mobs/Chicken.h"
# include "Mobs/Cow.h"
2012-10-28 10:57:35 -04:00
# include "Mobs/Creeper.h"
# include "Mobs/Enderman.h"
# include "Mobs/Ghast.h"
# include "Mobs/Magmacube.h"
# include "Mobs/Mooshroom.h"
# include "Mobs/Ocelot.h"
2012-09-23 16:53:08 -04:00
# include "Mobs/Pig.h"
# include "Mobs/Sheep.h"
2012-10-28 10:57:35 -04:00
# include "Mobs/Silverfish.h"
# include "Mobs/Skeleton.h"
# include "Mobs/Slime.h"
# include "Mobs/Spider.h"
# include "Mobs/Squid.h"
# include "Mobs/Villager.h"
2012-11-01 06:56:47 -04:00
# include "Mobs/Witch.h"
2012-10-28 10:57:35 -04:00
# include "Mobs/Wolf.h"
2012-09-23 16:53:08 -04:00
# include "Mobs/Zombie.h"
# include "Mobs/Zombiepigman.h"
2012-09-23 17:23:33 -04:00
# include "OSSupport/MakeDir.h"
2012-06-14 09:06:06 -04:00
# include "MersenneTwister.h"
2012-09-23 18:09:57 -04:00
# include "Tracer.h"
2012-09-23 16:14:04 -04:00
# include "Generating/Trees.h"
2012-09-23 18:09:57 -04:00
# include "PluginManager.h"
2012-09-29 09:59:32 -04:00
# include "Blocks/BlockHandler.h"
2012-06-14 09:06:06 -04:00
# include "Vector3d.h"
2013-06-18 15:09:51 -04:00
# include "TNTEntity.h"
2012-06-14 09:06:06 -04:00
# include "tolua++.h"
2013-08-04 10:08:09 -04:00
// DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
# include "LineBlockTracer.h"
2012-06-14 09:06:06 -04:00
# ifndef _WIN32
# include <stdlib.h>
# endif
/// Up to this many m_SpreadQueue elements are handled each world tick
const int MAX_LIGHTING_SPREAD_PER_TICK = 10 ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorldLoadProgress:
/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn()
class cWorldLoadProgress :
public cIsThread
{
public :
cWorldLoadProgress ( cWorld * a_World ) :
cIsThread ( " cWorldLoadProgress " ) ,
m_World ( a_World )
{
Start ( ) ;
}
void Stop ( void )
{
m_ShouldTerminate = true ;
Wait ( ) ;
}
protected :
cWorld * m_World ;
virtual void Execute ( void ) override
{
for ( ; ; )
{
LOG ( " %d chunks to load, %d chunks to generate " ,
m_World - > GetStorage ( ) . GetLoadQueueLength ( ) ,
m_World - > GetGenerator ( ) . GetQueueLength ( )
) ;
// Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
for ( int i = 0 ; i < 20 ; i + + )
{
cSleep : : MilliSleep ( 100 ) ;
if ( m_ShouldTerminate )
{
return ;
}
}
} // for (-ever)
}
} ;
/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
class cWorldLightingProgress :
public cIsThread
{
public :
cWorldLightingProgress ( cLightingThread * a_Lighting ) :
cIsThread ( " cWorldLightingProgress " ) ,
m_Lighting ( a_Lighting )
{
Start ( ) ;
}
void Stop ( void )
{
m_ShouldTerminate = true ;
Wait ( ) ;
}
protected :
cLightingThread * m_Lighting ;
virtual void Execute ( void ) override
{
for ( ; ; )
{
LOG ( " %d chunks remaining to light " , m_Lighting - > GetQueueLength ( )
) ;
// Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
for ( int i = 0 ; i < 20 ; i + + )
{
cSleep : : MilliSleep ( 100 ) ;
if ( m_ShouldTerminate )
{
return ;
}
}
} // for (-ever)
}
} ;
2013-06-15 11:29:20 -04:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld::cLock:
cWorld : : cLock : : cLock ( cWorld & a_World ) :
super ( & ( a_World . m_ChunkMap - > GetCS ( ) ) )
{
}
2012-06-14 09:06:06 -04:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld:
2012-10-25 15:20:29 -04:00
cWorld : : cWorld ( const AString & a_WorldName ) :
2012-11-01 17:38:20 -04:00
m_WorldAgeSecs ( 0 ) ,
m_TimeOfDaySecs ( 0 ) ,
m_WorldAge ( 0 ) ,
m_TimeOfDay ( 0 ) ,
m_LastTimeUpdate ( 0 ) ,
m_LastSpawnMonster ( 0 ) ,
2012-10-25 15:20:29 -04:00
m_RSList ( 0 ) ,
m_Weather ( eWeather_Sunny ) ,
m_WeatherInterval ( 24000 ) // Guaranteed 1 day of sunshine at server start :)
2012-06-14 09:06:06 -04:00
{
2012-10-25 15:20:29 -04:00
LOGD ( " cWorld::cWorld(%s) " , a_WorldName . c_str ( ) ) ;
2012-06-14 09:06:06 -04:00
m_WorldName = a_WorldName ;
m_IniFileName = m_WorldName + " /world.ini " ;
cMakeDir : : MakeDir ( m_WorldName . c_str ( ) ) ;
MTRand r1 ;
m_SpawnX = ( double ) ( ( r1 . randInt ( ) % 1000 ) - 500 ) ;
m_SpawnY = cChunkDef : : Height ;
m_SpawnZ = ( double ) ( ( r1 . randInt ( ) % 1000 ) - 500 ) ;
m_GameMode = eGameMode_Creative ;
AString StorageSchema ( " Default " ) ;
cIniFile IniFile ( m_IniFileName ) ;
IniFile . ReadFile ( ) ;
2013-06-22 14:41:08 -04:00
AString Dimension = IniFile . GetValueSet ( " General " , " Dimension " , " Overworld " ) ;
m_Dimension = StringToDimension ( Dimension ) ;
2013-03-11 13:15:34 -04:00
switch ( m_Dimension )
{
case dimNether :
case dimOverworld :
case dimEnd :
{
break ;
}
default :
{
2013-06-22 14:41:08 -04:00
LOGWARNING ( " Unknown dimension: \" %s \" . Setting to Overworld " , Dimension . c_str ( ) ) ;
2013-03-11 13:15:34 -04:00
m_Dimension = dimOverworld ;
break ;
}
} // switch (m_Dimension)
2012-06-14 09:06:06 -04:00
m_SpawnX = IniFile . GetValueSetF ( " SpawnPosition " , " X " , m_SpawnX ) ;
m_SpawnY = IniFile . GetValueSetF ( " SpawnPosition " , " Y " , m_SpawnY ) ;
m_SpawnZ = IniFile . GetValueSetF ( " SpawnPosition " , " Z " , m_SpawnZ ) ;
StorageSchema = IniFile . GetValueSet ( " Storage " , " Schema " , StorageSchema ) ;
m_MaxCactusHeight = IniFile . GetValueSetI ( " Plants " , " MaxCactusHeight " , 3 ) ;
m_MaxSugarcaneHeight = IniFile . GetValueSetI ( " Plants " , " MaxSugarcaneHeight " , 3 ) ;
2013-04-05 09:45:00 -04:00
m_IsCactusBonemealable = IniFile . GetValueSetB ( " Plants " , " IsCactusBonemealable " , false ) ;
m_IsCarrotsBonemealable = IniFile . GetValueSetB ( " Plants " , " IsCarrotsBonemealable " , true ) ;
2012-06-14 09:06:06 -04:00
m_IsCropsBonemealable = IniFile . GetValueSetB ( " Plants " , " IsCropsBonemealable " , true ) ;
m_IsGrassBonemealable = IniFile . GetValueSetB ( " Plants " , " IsGrassBonemealable " , true ) ;
m_IsMelonStemBonemealable = IniFile . GetValueSetB ( " Plants " , " IsMelonStemBonemealable " , true ) ;
m_IsMelonBonemealable = IniFile . GetValueSetB ( " Plants " , " IsMelonBonemealable " , false ) ;
2013-04-05 09:45:00 -04:00
m_IsPotatoesBonemealable = IniFile . GetValueSetB ( " Plants " , " IsPotatoesBonemealable " , true ) ;
2012-06-14 09:06:06 -04:00
m_IsPumpkinStemBonemealable = IniFile . GetValueSetB ( " Plants " , " IsPumpkinStemBonemealable " , true ) ;
m_IsPumpkinBonemealable = IniFile . GetValueSetB ( " Plants " , " IsPumpkinBonemealable " , false ) ;
2013-04-05 09:45:00 -04:00
m_IsSaplingBonemealable = IniFile . GetValueSetB ( " Plants " , " IsSaplingBonemealable " , true ) ;
2012-06-14 09:06:06 -04:00
m_IsSugarcaneBonemealable = IniFile . GetValueSetB ( " Plants " , " IsSugarcaneBonemealable " , false ) ;
2012-10-14 13:06:21 -04:00
m_bEnabledPVP = IniFile . GetValueSetB ( " PVP " , " Enabled " , true ) ;
2012-10-21 17:15:57 -04:00
m_IsDeepSnowEnabled = IniFile . GetValueSetB ( " Physics " , " DeepSnow " , false ) ;
2012-10-10 15:46:12 -04:00
2013-03-03 14:05:11 -05:00
m_GameMode = ( eGameMode ) IniFile . GetValueSetI ( " GameMode " , " GameMode " , m_GameMode ) ;
2012-06-14 09:06:06 -04:00
m_Lighting . Start ( this ) ;
m_Storage . Start ( this , StorageSchema ) ;
m_Generator . Start ( this , IniFile ) ;
m_bAnimals = true ;
2012-11-01 17:38:20 -04:00
m_SpawnMonsterRate = 200 ; // 1 mob each 10 seconds
2012-06-14 09:06:06 -04:00
cIniFile IniFile2 ( " settings.ini " ) ;
2012-10-25 15:20:29 -04:00
if ( IniFile2 . ReadFile ( ) )
2012-06-14 09:06:06 -04:00
{
2013-03-03 14:05:11 -05:00
m_bAnimals = IniFile2 . GetValueB ( " Monsters " , " AnimalsOn " , true ) ;
2012-11-01 17:38:20 -04:00
m_SpawnMonsterRate = ( Int64 ) ( IniFile2 . GetValueF ( " Monsters " , " AnimalSpawnInterval " , 10 ) * 20 ) ; // Convert from secs to ticks
// TODO: Move this into cServer instead:
SetMaxPlayers ( IniFile2 . GetValueI ( " Server " , " MaxPlayers " , 100 ) ) ;
m_Description = IniFile2 . GetValue ( " Server " , " Description " , " MCServer! - In C++! " ) . c_str ( ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
m_ChunkMap = new cChunkMap ( this ) ;
2012-06-14 09:06:06 -04:00
m_ChunkSender . Start ( this ) ;
m_LastSave = 0 ;
m_LastUnload = 0 ;
2012-10-14 13:06:21 -04:00
// preallocate some memory for ticking blocks so we don<6F> t need to allocate that often
2012-07-15 16:36:34 -04:00
m_BlockTickQueue . reserve ( 1000 ) ;
m_BlockTickQueueCopy . reserve ( 1000 ) ;
2012-10-13 05:53:28 -04:00
// Simulators:
2013-03-02 14:57:09 -05:00
m_SimulatorManager = new cSimulatorManager ( * this ) ;
2012-10-13 05:53:28 -04:00
m_WaterSimulator = InitializeFluidSimulator ( IniFile , " Water " , E_BLOCK_WATER , E_BLOCK_STATIONARY_WATER ) ;
m_LavaSimulator = InitializeFluidSimulator ( IniFile , " Lava " , E_BLOCK_LAVA , E_BLOCK_STATIONARY_LAVA ) ;
2013-03-02 14:57:09 -05:00
m_SandSimulator = new cSandSimulator ( * this , IniFile ) ;
2013-03-01 14:35:29 -05:00
m_FireSimulator = new cFireSimulator ( * this , IniFile ) ;
2013-02-28 08:39:20 -05:00
m_RedstoneSimulator = new cRedstoneSimulator ( * this ) ;
2012-06-14 09:06:06 -04:00
2012-10-15 16:16:43 -04:00
// Water and Lava simulators get registered in InitializeFluidSimulator()
2012-06-14 09:06:06 -04:00
m_SimulatorManager - > RegisterSimulator ( m_SandSimulator , 1 ) ;
2013-03-01 14:35:29 -05:00
m_SimulatorManager - > RegisterSimulator ( m_FireSimulator , 1 ) ;
2012-06-14 09:06:06 -04:00
m_SimulatorManager - > RegisterSimulator ( m_RedstoneSimulator , 1 ) ;
2012-10-14 13:06:21 -04:00
// Save any changes that the defaults may have done to the ini file:
if ( ! IniFile . WriteFile ( ) )
{
2012-10-25 15:20:29 -04:00
LOGWARNING ( " Could not write world config to %s " , m_IniFileName . c_str ( ) ) ;
2012-10-14 13:06:21 -04:00
}
2012-06-14 09:06:06 -04:00
}
2013-01-11 23:46:01 -05:00
cWorld : : ~ cWorld ( )
{
delete m_SimulatorManager ;
delete m_SandSimulator ;
delete m_WaterSimulator ;
delete m_LavaSimulator ;
delete m_FireSimulator ;
delete m_RedstoneSimulator ;
UnloadUnusedChunks ( ) ;
m_Storage . WaitForFinish ( ) ;
delete m_ChunkMap ;
}
2013-02-13 14:22:08 -05:00
void cWorld : : CastThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
BroadcastThunderbolt ( a_BlockX , a_BlockY , a_BlockZ ) ;
}
void cWorld : : SetWeather ( eWeather a_NewWeather )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// Do the plugins agree? Do they want a different weather?
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookWeatherChanging ( * this , a_NewWeather ) ;
// Set new period for the selected weather:
switch ( a_NewWeather )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
case eWeather_Sunny : m_WeatherInterval = 14400 + ( m_TickRand . randInt ( ) % 4800 ) ; break ; // 12 - 16 minutes
case eWeather_Rain : m_WeatherInterval = 9600 + ( m_TickRand . randInt ( ) % 7200 ) ; break ; // 8 - 14 minutes
case eWeather_ThunderStorm : m_WeatherInterval = 2400 + ( m_TickRand . randInt ( ) % 4800 ) ; break ; // 2 - 6 minutes
2012-08-25 17:46:18 -04:00
default :
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
LOGWARNING ( " Requested unknown weather %d, setting sunny for a minute instead. " , a_NewWeather ) ;
a_NewWeather = eWeather_Sunny ;
m_WeatherInterval = 1200 ;
2012-08-25 17:46:18 -04:00
break ;
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
} // switch (NewWeather)
m_Weather = a_NewWeather ;
BroadcastWeather ( m_Weather ) ;
// Let the plugins know about the change:
cPluginManager : : Get ( ) - > CallHookWeatherChanged ( * this ) ;
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
void cWorld : : ChangeWeather ( void )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// In the next tick the weather will be changed
m_WeatherInterval = 0 ;
2012-06-14 09:06:06 -04:00
}
2012-08-25 17:46:18 -04:00
2012-06-14 09:06:06 -04:00
void cWorld : : SetNextBlockTick ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
return m_ChunkMap - > SetNextBlockTick ( a_BlockX , a_BlockY , a_BlockZ ) ;
}
2012-08-25 17:46:18 -04:00
2012-06-14 09:06:06 -04:00
void cWorld : : InitializeSpawn ( void )
{
int ChunkX = 0 , ChunkY = 0 , ChunkZ = 0 ;
2013-03-03 14:05:11 -05:00
BlockToChunk ( ( int ) m_SpawnX , ( int ) m_SpawnY , ( int ) m_SpawnZ , ChunkX , ChunkY , ChunkZ ) ;
2012-06-14 09:06:06 -04:00
// For the debugging builds, don't make the server build too much world upon start:
2012-11-16 17:06:12 -05:00
# if defined(_DEBUG) || defined(ANDROID_NDK)
2012-06-14 09:06:06 -04:00
int ViewDist = 9 ;
# else
int ViewDist = 20 ; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
# endif // _DEBUG
LOG ( " Preparing spawn area in world \" %s \" ... " , m_WorldName . c_str ( ) ) ;
for ( int x = 0 ; x < ViewDist ; x + + )
{
for ( int z = 0 ; z < ViewDist ; z + + )
{
2013-03-03 14:05:11 -05:00
m_ChunkMap - > TouchChunk ( x + ChunkX - ( ViewDist - 1 ) / 2 , ZERO_CHUNK_Y , z + ChunkZ - ( ViewDist - 1 ) / 2 ) ; // Queue the chunk in the generator / loader
2012-06-14 09:06:06 -04:00
}
}
{
// Display progress during this process:
cWorldLoadProgress Progress ( this ) ;
// Wait for the loader to finish loading
m_Storage . WaitForQueuesEmpty ( ) ;
// Wait for the generator to finish generating
m_Generator . WaitForQueueEmpty ( ) ;
Progress . Stop ( ) ;
}
// Light all chunks that have been newly generated:
LOG ( " Lighting spawn area in world \" %s \" ... " , m_WorldName . c_str ( ) ) ;
for ( int x = 0 ; x < ViewDist ; x + + )
{
int ChX = x + ChunkX - ( ViewDist - 1 ) / 2 ;
for ( int z = 0 ; z < ViewDist ; z + + )
{
int ChZ = z + ChunkZ - ( ViewDist - 1 ) / 2 ;
if ( ! m_ChunkMap - > IsChunkLighted ( ChX , ChZ ) )
{
m_Lighting . QueueChunk ( ChX , ChZ ) ; // Queue the chunk in the lighting thread
}
} // for z
} // for x
{
cWorldLightingProgress Progress ( & m_Lighting ) ;
m_Lighting . WaitForQueueEmpty ( ) ;
Progress . Stop ( ) ;
}
// TODO: Better spawn detection - move spawn out of the water if it isn't set in the INI already
2013-03-03 14:05:11 -05:00
m_SpawnY = ( double ) GetHeight ( ( int ) m_SpawnX , ( int ) m_SpawnZ ) + 1.6f ; // +1.6f eye height
2013-08-04 10:08:09 -04:00
# ifdef TEST_LINEBLOCKTRACER
// DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
class cTracerCallbacks :
public cBlockTracer : : cCallbacks
{
virtual bool OnNextBlock ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta ) override
{
LOGD ( " Block {%d, %d, %d}: %d:%d (%s) " ,
a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta ,
ItemToString ( cItem ( a_BlockType , 1 , a_BlockMeta ) ) . c_str ( )
) ;
return false ;
}
virtual bool OnNextBlockNoData ( int a_BlockX , int a_BlockY , int a_BlockZ ) override
{
LOGD ( " Block {%d, %d, %d}: no data available " ,
a_BlockX , a_BlockY , a_BlockZ
) ;
return false ;
}
virtual bool OnOutOfWorld ( double a_BlockX , double a_BlockY , double a_BlockZ ) override
{
LOGD ( " Out of world at {%f, %f, %f} " , a_BlockX , a_BlockY , a_BlockZ ) ;
return false ;
}
virtual bool OnIntoWorld ( double a_BlockX , double a_BlockY , double a_BlockZ ) override
{
LOGD ( " Into world at {%f, %f, %f} " , a_BlockX , a_BlockY , a_BlockZ ) ;
return false ;
}
virtual void OnNoMoreHits ( void ) override
{
LOGD ( " No more hits " ) ;
}
} Callbacks ;
LOGD ( " Spawn is at {%f, %f, %f} " , m_SpawnX , m_SpawnY , m_SpawnZ ) ;
LOGD ( " Tracing a line along +X: " ) ;
cLineBlockTracer : : Trace ( * this , Callbacks , m_SpawnX - 10 , m_SpawnY , m_SpawnZ , m_SpawnX + 10 , m_SpawnY , m_SpawnZ ) ;
LOGD ( " Tracing a line along -Z: " ) ;
cLineBlockTracer : : Trace ( * this , Callbacks , m_SpawnX , m_SpawnY , m_SpawnZ + 10 , m_SpawnX , m_SpawnY , m_SpawnZ - 10 ) ;
LOGD ( " Tracing a line along -Y, out of world: " ) ;
cLineBlockTracer : : Trace ( * this , Callbacks , m_SpawnX , 260 , m_SpawnZ , m_SpawnX , - 5 , m_SpawnZ ) ;
LOGD ( " Tracing a line along XY: " ) ;
cLineBlockTracer : : Trace ( * this , Callbacks , m_SpawnX - 10 , m_SpawnY - 10 , m_SpawnZ , m_SpawnX + 10 , m_SpawnY + 10 , m_SpawnZ ) ;
LOGD ( " Tracing a line in generic direction: " ) ;
cLineBlockTracer : : Trace ( * this , Callbacks , m_SpawnX - 15 , m_SpawnY - 5 , m_SpawnZ + 7.5 , m_SpawnX + 13 , m_SpawnY - 10 , m_SpawnZ + 8.5 ) ;
LOGD ( " Tracing tests done " ) ;
# endif // TEST_LINEBLOCKTRACER
2012-06-14 09:06:06 -04:00
}
2012-07-15 16:07:38 -04:00
void cWorld : : StopThreads ( void )
{
m_Generator . Stop ( ) ;
m_ChunkSender . Stop ( ) ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : Tick ( float a_Dt )
{
2012-11-01 17:38:20 -04:00
// We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it
m_WorldAgeSecs + = ( double ) a_Dt / 1000.0 ;
m_TimeOfDaySecs + = ( double ) a_Dt / 1000.0 ;
2012-06-14 09:06:06 -04:00
2012-11-01 17:38:20 -04:00
// Wrap time of day each 20 minutes (1200 seconds)
if ( m_TimeOfDaySecs > 1200.0 )
2012-06-14 09:06:06 -04:00
{
2012-11-01 17:38:20 -04:00
m_TimeOfDaySecs - = 1200.0 ;
2012-06-14 09:06:06 -04:00
}
2012-11-01 17:38:20 -04:00
2012-12-17 09:23:17 -05:00
m_WorldAge = ( Int64 ) ( m_WorldAgeSecs * 20.0 ) ;
m_TimeOfDay = ( Int64 ) ( m_TimeOfDaySecs * 20.0 ) ;
2012-11-01 17:38:20 -04:00
// Broadcase time update every 40 ticks (2 seconds)
if ( m_LastTimeUpdate < m_WorldAge - 40 )
2012-06-14 09:06:06 -04:00
{
2012-08-25 17:46:18 -04:00
BroadcastTimeUpdate ( ) ;
2012-11-01 17:38:20 -04:00
m_LastTimeUpdate = m_WorldAge ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
m_ChunkMap - > Tick ( a_Dt ) ;
2012-07-15 16:36:34 -04:00
TickQueuedBlocks ( a_Dt ) ;
2012-06-14 09:06:06 -04:00
GetSimulatorManager ( ) - > Simulate ( a_Dt ) ;
TickWeather ( a_Dt ) ;
// Asynchronously set blocks:
sSetBlockList FastSetBlockQueueCopy ;
{
cCSLock Lock ( m_CSFastSetBlock ) ;
std : : swap ( FastSetBlockQueueCopy , m_FastSetBlockQueue ) ;
}
m_ChunkMap - > FastSetBlocks ( FastSetBlockQueueCopy ) ;
if ( ! FastSetBlockQueueCopy . empty ( ) )
{
// Some blocks failed, store them for next tick:
cCSLock Lock ( m_CSFastSetBlock ) ;
m_FastSetBlockQueue . splice ( m_FastSetBlockQueue . end ( ) , FastSetBlockQueueCopy ) ;
}
2012-11-01 17:38:20 -04:00
if ( m_WorldAge - m_LastSave > 60 * 5 * 20 ) // Save each 5 minutes
2012-06-14 09:06:06 -04:00
{
SaveAllChunks ( ) ;
}
2013-03-03 14:05:11 -05:00
if ( m_WorldAge - m_LastUnload > 10 * 20 ) // Unload every 10 seconds
2012-06-14 09:06:06 -04:00
{
UnloadUnusedChunks ( ) ;
}
TickSpawnMobs ( a_Dt ) ;
std : : vector < int > m_RSList_copy ( m_RSList ) ;
m_RSList . clear ( ) ;
std : : vector < int > : : const_iterator cii ; // FIXME - Please rename this variable, WTF is cii??? Use human readable variable names or common abbreviations (i, idx, itr, iter)
2012-10-06 16:04:58 -04:00
for ( cii = m_RSList_copy . begin ( ) ; cii ! = m_RSList_copy . end ( ) ; )
2012-06-14 09:06:06 -04:00
{
2012-10-06 16:04:58 -04:00
int tempX = * cii ; cii + + ;
int tempY = * cii ; cii + + ;
int tempZ = * cii ; cii + + ;
int state = * cii ; cii + + ;
2012-06-14 09:06:06 -04:00
2013-03-03 14:05:11 -05:00
if ( ( state = = 11111 ) & & ( ( int ) GetBlock ( tempX , tempY , tempZ ) = = E_BLOCK_REDSTONE_TORCH_OFF ) )
2012-06-14 09:06:06 -04:00
{
2013-03-03 14:05:11 -05:00
FastSetBlock ( tempX , tempY , tempZ , E_BLOCK_REDSTONE_TORCH_ON , ( int ) GetBlockMeta ( tempX , tempY , tempZ ) ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
else if ( ( state = = 00000 ) & & ( ( int ) GetBlock ( tempX , tempY , tempZ ) = = E_BLOCK_REDSTONE_TORCH_ON ) )
2012-06-14 09:06:06 -04:00
{
2013-03-03 14:05:11 -05:00
FastSetBlock ( tempX , tempY , tempZ , E_BLOCK_REDSTONE_TORCH_OFF , ( int ) GetBlockMeta ( tempX , tempY , tempZ ) ) ;
2012-06-14 09:06:06 -04:00
}
}
m_RSList_copy . erase ( m_RSList_copy . begin ( ) , m_RSList_copy . end ( ) ) ;
}
2013-02-13 14:22:08 -05:00
void cWorld : : TickWeather ( float a_Dt )
2012-06-14 09:06:06 -04:00
{
2013-06-24 12:50:32 -04:00
// There are no weather changes anywhere but in the Overworld:
if ( GetDimension ( ) ! = dimOverworld )
{
return ;
}
2013-02-13 14:22:08 -05:00
if ( m_WeatherInterval > 0 )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// Not yet, wait for the weather period to end
m_WeatherInterval - - ;
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
else
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// Change weather:
2012-06-14 09:06:06 -04:00
2013-02-13 14:22:08 -05:00
// Pick a new weather. Only reasonable transitions allowed:
eWeather NewWeather = m_Weather ;
switch ( m_Weather )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
case eWeather_Sunny : NewWeather = eWeather_Rain ; break ;
case eWeather_ThunderStorm : NewWeather = eWeather_Rain ; break ;
2012-06-14 09:06:06 -04:00
case eWeather_Rain :
2013-02-13 14:22:08 -05:00
{
// 1/8 chance of turning into a thunderstorm
NewWeather = ( ( m_TickRand . randInt ( ) % 256 ) < 32 ) ? eWeather_ThunderStorm : eWeather_Sunny ;
2012-06-14 09:06:06 -04:00
break ;
2013-02-13 14:22:08 -05:00
}
2012-06-14 09:06:06 -04:00
default :
2013-02-13 14:22:08 -05:00
{
LOGWARNING ( " Unknown current weather: %d. Setting sunny. " , m_Weather ) ;
ASSERT ( ! " Unknown weather " ) ;
NewWeather = eWeather_Sunny ;
}
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
SetWeather ( NewWeather ) ;
} // else (m_WeatherInterval > 0)
2012-06-14 09:06:06 -04:00
2013-02-13 14:22:08 -05:00
if ( m_Weather = = eWeather_ThunderStorm )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// 0.5% chance per tick of thunderbolt
if ( m_TickRand . randInt ( ) % 199 = = 0 )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
CastThunderbolt ( 0 , 0 , 0 ) ; // TODO: find random possitions near players to cast thunderbolts.
2012-06-14 09:06:06 -04:00
}
}
}
void cWorld : : TickSpawnMobs ( float a_Dt )
{
2012-11-01 17:38:20 -04:00
if ( ! m_bAnimals | | ( m_WorldAge - m_LastSpawnMonster < = m_SpawnMonsterRate ) )
2012-06-14 09:06:06 -04:00
{
return ;
}
2012-11-01 17:38:20 -04:00
m_LastSpawnMonster = m_WorldAge ;
2012-06-14 09:06:06 -04:00
Vector3d SpawnPos ;
{
cCSLock Lock ( m_CSPlayers ) ;
2013-03-03 14:05:11 -05:00
if ( m_Players . size ( ) < = 0 )
2012-06-14 09:06:06 -04:00
{
return ;
}
int RandomPlayerIdx = m_TickRand . randInt ( ) & m_Players . size ( ) ;
cPlayerList : : iterator itr = m_Players . begin ( ) ;
2013-03-03 14:05:11 -05:00
for ( int i = 1 ; i < RandomPlayerIdx ; i + + )
2012-06-14 09:06:06 -04:00
{
itr + + ;
}
SpawnPos = ( * itr ) - > GetPosition ( ) ;
}
2013-06-25 02:36:59 -04:00
int dayRand = ( m_TickRand . randInt ( ) / 7 ) % 6 ;
int nightRand = ( m_TickRand . randInt ( ) / 11 ) % 10 ;
2012-06-14 09:06:06 -04:00
2013-03-03 14:05:11 -05:00
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 ) ;
2012-06-14 09:06:06 -04:00
2013-06-25 02:36:59 -04:00
int MobType = - 1 ;
2012-11-01 17:38:20 -04:00
if ( m_TimeOfDay > = 12000 + 1000 )
2012-06-14 09:06:06 -04:00
{
2012-06-16 11:06:14 -04:00
if ( GetBiomeAt ( ( int ) SpawnPos . x , ( int ) SpawnPos . z ) = = biHell ) // Spawn nether mobs
2012-06-14 19:25:26 -04:00
{
2013-06-25 02:36:59 -04:00
switch ( nightRand )
{
case 1 : MobType = E_ENTITY_TYPE_ZOMBIE ; break ; // _X 2013_06_25: Really? Zombies in the Nether?
case 5 : MobType = E_ENTITY_TYPE_GHAST ; break ;
case 6 : MobType = E_ENTITY_TYPE_ZOMBIE_PIGMAN ; break ;
}
2012-06-14 19:25:26 -04:00
}
else
{
2013-06-25 02:36:59 -04:00
switch ( nightRand )
{
case 0 : MobType = E_ENTITY_TYPE_SPIDER ; break ;
case 2 : MobType = E_ENTITY_TYPE_ENDERMAN ; break ;
case 3 : MobType = E_ENTITY_TYPE_CREEPER ; break ;
case 4 : MobType = E_ENTITY_TYPE_CAVE_SPIDER ; break ;
case 7 : MobType = E_ENTITY_TYPE_SLIME ; break ;
case 8 : MobType = E_ENTITY_TYPE_SILVERFISH ; break ;
case 9 : MobType = E_ENTITY_TYPE_SKELETON ; break ;
}
2012-06-14 19:25:26 -04:00
}
2012-06-14 09:06:06 -04:00
}
else
{
2013-06-25 02:36:59 -04:00
switch ( dayRand )
{
case 0 : MobType = E_ENTITY_TYPE_CHICKEN ; break ;
case 1 : MobType = E_ENTITY_TYPE_COW ; break ;
case 2 : MobType = E_ENTITY_TYPE_PIG ; break ;
case 3 : MobType = E_ENTITY_TYPE_SHEEP ; break ;
case 4 : MobType = E_ENTITY_TYPE_SQUID ; break ;
case 5 : MobType = E_ENTITY_TYPE_WOLF ; break ;
}
2012-06-14 09:06:06 -04:00
}
2013-06-25 02:36:59 -04:00
if ( MobType > = 0 )
2012-06-14 09:06:06 -04:00
{
2013-06-25 02:36:59 -04:00
// A proper mob type was selected, now spawn the mob:
SpawnMob ( SpawnPos . x , SpawnPos . y , SpawnPos . z , MobType ) ;
2012-06-14 09:06:06 -04:00
}
}
2013-02-28 02:42:45 -05:00
void cWorld : : WakeUpSimulators ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
return m_ChunkMap - > WakeUpSimulators ( a_BlockX , a_BlockY , a_BlockZ ) ;
}
2013-06-21 16:47:58 -04:00
/// Wakes up the simulators for the specified area of blocks
void cWorld : : WakeUpSimulatorsInArea ( int a_MinBlockX , int a_MaxBlockX , int a_MinBlockY , int a_MaxBlockY , int a_MinBlockZ , int a_MaxBlockZ )
{
return m_ChunkMap - > WakeUpSimulatorsInArea ( a_MinBlockX , a_MaxBlockX , a_MinBlockY , a_MaxBlockY , a_MinBlockZ , a_MaxBlockZ ) ;
}
2012-06-17 15:58:39 -04:00
bool cWorld : : ForEachChestInChunk ( int a_ChunkX , int a_ChunkZ , cChestCallback & a_Callback )
{
return m_ChunkMap - > ForEachChestInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2012-12-26 12:16:33 -05:00
bool cWorld : : ForEachDispenserInChunk ( int a_ChunkX , int a_ChunkZ , cDispenserCallback & a_Callback )
{
return m_ChunkMap - > ForEachDispenserInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2013-05-26 10:39:04 -04:00
bool cWorld : : ForEachDropperInChunk ( int a_ChunkX , int a_ChunkZ , cDropperCallback & a_Callback )
{
return m_ChunkMap - > ForEachDropperInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
bool cWorld : : ForEachDropSpenserInChunk ( int a_ChunkX , int a_ChunkZ , cDropSpenserCallback & a_Callback )
{
return m_ChunkMap - > ForEachDropSpenserInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2012-06-17 15:58:39 -04:00
bool cWorld : : ForEachFurnaceInChunk ( int a_ChunkX , int a_ChunkZ , cFurnaceCallback & a_Callback )
{
return m_ChunkMap - > ForEachFurnaceInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2013-04-17 22:42:45 -04:00
void cWorld : : DoExplosiontAt ( float a_ExplosionSize , int a_BlockX , int a_BlockY , int a_BlockZ )
{
2013-05-04 02:25:58 -04:00
// TODO: Add damage to entities, add support for pickups, and implement block hardiness
2013-06-18 04:24:34 -04:00
Vector3d explosion_pos = Vector3d ( a_BlockX , a_BlockY , a_BlockZ ) ;
2013-06-18 15:09:51 -04:00
cVector3iArray BlocksAffected ;
m_ChunkMap - > DoExplosiontAt ( a_ExplosionSize , a_BlockX , a_BlockY , a_BlockZ , BlocksAffected ) ;
2013-04-17 22:42:45 -04:00
BroadcastSoundEffect ( " random.explode " , a_BlockX * 8 , a_BlockY * 8 , a_BlockZ * 8 , 1.0f , 0.6f ) ;
{
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
Vector3d distance_explosion = ( * itr ) - > GetPosition ( ) - explosion_pos ;
if ( distance_explosion . SqrLength ( ) < 4096.0 )
{
2013-04-19 04:21:39 -04:00
double real_distance = std : : max ( 0.004 , sqrt ( distance_explosion . SqrLength ( ) ) ) ;
2013-04-17 22:42:45 -04:00
double power = a_ExplosionSize / real_distance ;
if ( power < = 1 )
2013-04-19 04:21:39 -04:00
{
2013-04-17 22:42:45 -04:00
power = 0 ;
2013-04-19 04:21:39 -04:00
}
2013-04-17 22:42:45 -04:00
distance_explosion . Normalize ( ) ;
distance_explosion * = power ;
2013-06-18 15:09:51 -04:00
ch - > SendExplosion ( a_BlockX , a_BlockY , a_BlockZ , a_ExplosionSize , BlocksAffected , distance_explosion ) ;
2013-04-17 22:42:45 -04:00
}
}
}
}
2012-06-17 15:58:39 -04:00
bool cWorld : : DoWithChestAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cChestCallback & a_Callback )
{
return m_ChunkMap - > DoWithChestAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2012-12-26 12:16:33 -05:00
bool cWorld : : DoWithDispenserAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDispenserCallback & a_Callback )
{
return m_ChunkMap - > DoWithDispenserAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2013-05-26 10:39:04 -04:00
bool cWorld : : DoWithDropperAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDropperCallback & a_Callback )
{
return m_ChunkMap - > DoWithDropperAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
bool cWorld : : DoWithDropSpenserAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDropSpenserCallback & a_Callback )
{
return m_ChunkMap - > DoWithDropSpenserAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2012-12-26 12:16:33 -05:00
2012-06-17 15:58:39 -04:00
bool cWorld : : DoWithFurnaceAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cFurnaceCallback & a_Callback )
{
return m_ChunkMap - > DoWithFurnaceAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
bool cWorld : : GetSignLines ( int a_BlockX , int a_BlockY , int a_BlockZ , AString & a_Line1 , AString & a_Line2 , AString & a_Line3 , AString & a_Line4 )
{
return m_ChunkMap - > GetSignLines ( a_BlockX , a_BlockY , a_BlockZ , a_Line1 , a_Line2 , a_Line3 , a_Line4 ) ;
}
2013-08-03 14:26:50 -04:00
bool cWorld : : DoWithChunk ( int a_ChunkX , int a_ChunkZ , cChunkCallback & a_Callback )
{
return m_ChunkMap - > DoWithChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2013-03-03 14:05:11 -05:00
void cWorld : : GrowTree ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
if ( GetBlock ( a_X , a_Y , a_Z ) = = E_BLOCK_SAPLING )
{
// There is a sapling here, grow a tree according to its type:
GrowTreeFromSapling ( a_X , a_Y , a_Z , GetBlockMeta ( a_X , a_Y , a_Z ) ) ;
}
else
{
// There is nothing here, grow a tree based on the current biome here:
GrowTreeByBiome ( a_X , a_Y , a_Z ) ;
}
}
void cWorld : : GrowTreeFromSapling ( int a_X , int a_Y , int a_Z , char a_SaplingMeta )
{
cNoise Noise ( m_Generator . GetSeed ( ) ) ;
2012-07-15 09:33:43 -04:00
sSetBlockVector Logs , Other ;
2012-06-14 09:06:06 -04:00
switch ( a_SaplingMeta & 0x07 )
{
2012-11-01 17:38:20 -04:00
case E_META_SAPLING_APPLE : GetAppleTreeImage ( a_X , a_Y , a_Z , Noise , ( int ) ( m_WorldAge & 0xffffffff ) , Logs , Other ) ; break ;
case E_META_SAPLING_BIRCH : GetBirchTreeImage ( a_X , a_Y , a_Z , Noise , ( int ) ( m_WorldAge & 0xffffffff ) , Logs , Other ) ; break ;
case E_META_SAPLING_CONIFER : GetConiferTreeImage ( a_X , a_Y , a_Z , Noise , ( int ) ( m_WorldAge & 0xffffffff ) , Logs , Other ) ; break ;
case E_META_SAPLING_JUNGLE : GetJungleTreeImage ( a_X , a_Y , a_Z , Noise , ( int ) ( m_WorldAge & 0xffffffff ) , Logs , Other ) ; break ;
2012-06-14 09:06:06 -04:00
}
2012-07-15 09:33:43 -04:00
Other . insert ( Other . begin ( ) , Logs . begin ( ) , Logs . end ( ) ) ;
Logs . clear ( ) ;
2012-10-03 15:01:34 -04:00
GrowTreeImage ( Other ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : GrowTreeByBiome ( int a_X , int a_Y , int a_Z )
{
cNoise Noise ( m_Generator . GetSeed ( ) ) ;
2012-07-15 09:33:43 -04:00
sSetBlockVector Logs , Other ;
2012-11-01 17:38:20 -04:00
GetTreeImageByBiome ( a_X , a_Y , a_Z , Noise , ( int ) ( m_WorldAge & 0xffffffff ) , ( EMCSBiome ) GetBiomeAt ( a_X , a_Z ) , Logs , Other ) ;
2012-07-15 09:33:43 -04:00
Other . insert ( Other . begin ( ) , Logs . begin ( ) , Logs . end ( ) ) ;
Logs . clear ( ) ;
GrowTreeImage ( Other ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : GrowTreeImage ( const sSetBlockVector & a_Blocks )
{
// Check that the tree has place to grow
// Make a copy of the log blocks:
sSetBlockVector b2 ;
for ( sSetBlockVector : : const_iterator itr = a_Blocks . begin ( ) ; itr ! = a_Blocks . end ( ) ; + + itr )
{
if ( itr - > BlockType = = E_BLOCK_LOG )
{
b2 . push_back ( * itr ) ;
}
} // for itr - a_Blocks[]
// Query blocktypes and metas at those log blocks:
if ( ! GetBlocks ( b2 , false ) )
{
return ;
}
// Check that at each log's coord there's an block allowed to be overwritten:
for ( sSetBlockVector : : const_iterator itr = b2 . begin ( ) ; itr ! = b2 . end ( ) ; + + itr )
{
switch ( itr - > BlockType )
{
CASE_TREE_ALLOWED_BLOCKS :
{
break ;
}
default :
{
return ;
}
}
} // for itr - b2[]
// All ok, replace blocks with the tree image:
m_ChunkMap - > ReplaceTreeBlocks ( a_Blocks ) ;
}
2012-10-03 04:52:11 -04:00
bool cWorld : : GrowRipePlant ( int a_BlockX , int a_BlockY , int a_BlockZ , bool a_IsByBonemeal )
2012-06-14 09:06:06 -04:00
{
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta ) ;
switch ( BlockType )
{
2013-04-05 09:45:00 -04:00
case E_BLOCK_CARROTS :
{
if ( a_IsByBonemeal & & ! m_IsCarrotsBonemealable )
{
return false ;
}
if ( BlockMeta < 7 )
{
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 ) ;
}
return true ;
}
2012-06-14 09:06:06 -04:00
case E_BLOCK_CROPS :
{
2013-04-05 09:45:00 -04:00
if ( a_IsByBonemeal & & ! m_IsCropsBonemealable )
2012-06-14 09:06:06 -04:00
{
return false ;
}
if ( BlockMeta < 7 )
{
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 ) ;
}
return true ;
}
case E_BLOCK_MELON_STEM :
{
if ( BlockMeta < 7 )
{
if ( a_IsByBonemeal & & ! m_IsMelonStemBonemealable )
{
return false ;
}
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 ) ;
}
else
{
if ( a_IsByBonemeal & & ! m_IsMelonBonemealable )
{
return false ;
}
GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , BlockType ) ;
}
return true ;
}
2013-04-05 09:45:00 -04:00
case E_BLOCK_POTATOES :
{
if ( a_IsByBonemeal & & ! m_IsPotatoesBonemealable )
{
return false ;
}
if ( BlockMeta < 7 )
{
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 ) ;
}
return true ;
}
2012-06-14 09:06:06 -04:00
case E_BLOCK_PUMPKIN_STEM :
{
if ( BlockMeta < 7 )
{
if ( a_IsByBonemeal & & ! m_IsPumpkinStemBonemealable )
{
return false ;
}
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 ) ;
}
else
{
if ( a_IsByBonemeal & & ! m_IsPumpkinBonemealable )
{
return false ;
}
GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , BlockType ) ;
}
return true ;
}
case E_BLOCK_SAPLING :
{
if ( a_IsByBonemeal & & ! m_IsSaplingBonemealable )
{
return false ;
}
GrowTreeFromSapling ( a_BlockX , a_BlockY , a_BlockZ , BlockMeta ) ;
return true ;
}
case E_BLOCK_GRASS :
{
if ( a_IsByBonemeal & & ! m_IsGrassBonemealable )
{
return false ;
}
MTRand r1 ;
for ( int i = 0 ; i < 60 ; i + + )
{
int OfsX = ( r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 ) ) / 2 - 3 ;
int OfsY = r1 . randInt ( 3 ) + r1 . randInt ( 3 ) - 3 ;
int OfsZ = ( r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 ) ) / 2 - 3 ;
BLOCKTYPE Ground = GetBlock ( a_BlockX + OfsX , a_BlockY + OfsY , a_BlockZ + OfsZ ) ;
if ( Ground ! = E_BLOCK_GRASS )
{
continue ;
}
BLOCKTYPE Above = GetBlock ( a_BlockX + OfsX , a_BlockY + OfsY + 1 , a_BlockZ + OfsZ ) ;
if ( Above ! = E_BLOCK_AIR )
{
continue ;
}
BLOCKTYPE SpawnType ;
NIBBLETYPE SpawnMeta = 0 ;
switch ( r1 . randInt ( 10 ) )
{
case 0 : SpawnType = E_BLOCK_YELLOW_FLOWER ; break ;
case 1 : SpawnType = E_BLOCK_RED_ROSE ; break ;
default :
{
SpawnType = E_BLOCK_TALL_GRASS ;
SpawnMeta = E_META_TALL_GRASS_GRASS ;
break ;
}
2012-10-03 04:52:11 -04:00
} // switch (random spawn block type)
2012-06-14 09:06:06 -04:00
FastSetBlock ( a_BlockX + OfsX , a_BlockY + OfsY + 1 , a_BlockZ + OfsZ , SpawnType , SpawnMeta ) ;
} // for i - 50 times
return true ;
}
case E_BLOCK_SUGARCANE :
{
if ( a_IsByBonemeal & & ! m_IsSugarcaneBonemealable )
{
return false ;
}
m_ChunkMap - > GrowSugarcane ( a_BlockX , a_BlockY , a_BlockZ , m_MaxSugarcaneHeight ) ;
return true ;
}
case E_BLOCK_CACTUS :
{
if ( a_IsByBonemeal & & ! m_IsCactusBonemealable )
{
return false ;
}
m_ChunkMap - > GrowCactus ( a_BlockX , a_BlockY , a_BlockZ , m_MaxCactusHeight ) ;
return true ;
}
} // switch (BlockType)
return false ;
}
2012-10-03 04:52:11 -04:00
void cWorld : : GrowCactus ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_NumBlocksToGrow )
{
m_ChunkMap - > GrowCactus ( a_BlockX , a_BlockY , a_BlockZ , a_NumBlocksToGrow ) ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : GrowMelonPumpkin ( int a_BlockX , int a_BlockY , int a_BlockZ , char a_BlockType )
{
MTRand Rand ;
m_ChunkMap - > GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , Rand ) ;
}
2012-10-03 04:52:11 -04:00
void cWorld : : GrowSugarcane ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_NumBlocksToGrow )
{
m_ChunkMap - > GrowSugarcane ( a_BlockX , a_BlockY , a_BlockZ , a_NumBlocksToGrow ) ;
}
2012-06-14 09:06:06 -04:00
int cWorld : : GetBiomeAt ( int a_BlockX , int a_BlockZ )
{
return m_ChunkMap - > GetBiomeAt ( a_BlockX , a_BlockZ ) ;
}
2012-10-13 04:56:12 -04:00
void cWorld : : SetBlock ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2012-06-14 09:06:06 -04:00
{
2013-01-11 23:46:01 -05:00
if ( a_BlockType = = E_BLOCK_AIR )
2012-07-15 16:36:34 -04:00
{
2012-10-13 04:56:12 -04:00
BlockHandler ( GetBlock ( a_BlockX , a_BlockY , a_BlockZ ) ) - > OnDestroyed ( this , a_BlockX , a_BlockY , a_BlockZ ) ;
2012-07-15 16:36:34 -04:00
}
2012-10-13 04:56:12 -04:00
m_ChunkMap - > SetBlock ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta ) ;
2012-06-14 09:06:06 -04:00
2013-01-11 23:46:01 -05:00
BlockHandler ( a_BlockType ) - > OnPlaced ( this , a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
void cWorld : : FastSetBlock ( int a_X , int a_Y , int a_Z , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2012-06-14 09:06:06 -04:00
{
cCSLock Lock ( m_CSFastSetBlock ) ;
m_FastSetBlockQueue . push_back ( sSetBlock ( a_X , a_Y , a_Z , a_BlockType , a_BlockMeta ) ) ;
}
2012-08-03 07:53:11 -04:00
BLOCKTYPE cWorld : : GetBlock ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
// First check if it isn't queued in the m_FastSetBlockQueue:
{
int X = a_X , Y = a_Y , Z = a_Z ;
int ChunkX , ChunkY , ChunkZ ;
AbsoluteToRelative ( X , Y , Z , ChunkX , ChunkY , ChunkZ ) ;
cCSLock Lock ( m_CSFastSetBlock ) ;
for ( sSetBlockList : : iterator itr = m_FastSetBlockQueue . begin ( ) ; itr ! = m_FastSetBlockQueue . end ( ) ; + + itr )
{
if ( ( itr - > x = = X ) & & ( itr - > y = = Y ) & & ( itr - > z = = Z ) & & ( itr - > ChunkX = = ChunkX ) & & ( itr - > ChunkZ = = ChunkZ ) )
{
return itr - > BlockType ;
}
} // for itr - m_FastSetBlockQueue[]
}
return m_ChunkMap - > GetBlock ( a_X , a_Y , a_Z ) ;
}
2013-03-03 14:05:11 -05:00
NIBBLETYPE cWorld : : GetBlockMeta ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
// First check if it isn't queued in the m_FastSetBlockQueue:
{
cCSLock Lock ( m_CSFastSetBlock ) ;
for ( sSetBlockList : : iterator itr = m_FastSetBlockQueue . begin ( ) ; itr ! = m_FastSetBlockQueue . end ( ) ; + + itr )
{
if ( ( itr - > x = = a_X ) & & ( itr - > y = = a_Y ) & & ( itr - > y = = a_Y ) )
{
return itr - > BlockMeta ;
}
} // for itr - m_FastSetBlockQueue[]
}
return m_ChunkMap - > GetBlockMeta ( a_X , a_Y , a_Z ) ;
}
2013-03-03 14:05:11 -05:00
void cWorld : : SetBlockMeta ( int a_X , int a_Y , int a_Z , NIBBLETYPE a_MetaData )
2012-06-14 09:06:06 -04:00
{
m_ChunkMap - > SetBlockMeta ( a_X , a_Y , a_Z , a_MetaData ) ;
}
2013-03-03 14:05:11 -05:00
NIBBLETYPE cWorld : : GetBlockSkyLight ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
return m_ChunkMap - > GetBlockSkyLight ( a_X , a_Y , a_Z ) ;
}
2012-10-20 07:40:34 -04:00
NIBBLETYPE cWorld : : GetBlockBlockLight ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
return m_ChunkMap - > GetBlockBlockLight ( a_BlockX , a_BlockY , a_BlockZ ) ;
}
2012-10-26 04:47:30 -04:00
bool cWorld : : GetBlockTypeMeta ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_BlockMeta )
2012-06-14 09:06:06 -04:00
{
2012-10-26 04:47:30 -04:00
return m_ChunkMap - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , ( BLOCKTYPE & ) a_BlockType , ( NIBBLETYPE & ) a_BlockMeta ) ;
2012-06-14 09:06:06 -04:00
}
2012-10-26 04:47:30 -04:00
bool cWorld : : GetBlockInfo ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_Meta , NIBBLETYPE & a_SkyLight , NIBBLETYPE & a_BlockLight )
2012-10-20 07:40:34 -04:00
{
2012-10-26 04:47:30 -04:00
return m_ChunkMap - > GetBlockInfo ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_Meta , a_SkyLight , a_BlockLight ) ;
2012-10-20 07:40:34 -04:00
}
2012-10-06 12:58:31 -04:00
bool cWorld : : WriteBlockArea ( cBlockArea & a_Area , int a_MinBlockX , int a_MinBlockY , int a_MinBlockZ , int a_DataTypes )
{
return m_ChunkMap - > WriteBlockArea ( a_Area , a_MinBlockX , a_MinBlockY , a_MinBlockZ , a_DataTypes ) ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : SpawnItemPickups ( const cItems & a_Pickups , double a_BlockX , double a_BlockY , double a_BlockZ , double a_FlyAwaySpeed )
{
MTRand r1 ;
2012-08-24 03:58:26 -04:00
a_FlyAwaySpeed / = 1000 ; // Pre-divide, so that we don't have to divide each time inside the loop
2012-06-14 09:06:06 -04:00
for ( cItems : : const_iterator itr = a_Pickups . begin ( ) ; itr ! = a_Pickups . end ( ) ; + + itr )
{
float SpeedX = ( float ) ( a_FlyAwaySpeed * ( r1 . randInt ( 1000 ) - 500 ) ) ;
float SpeedY = ( float ) ( a_FlyAwaySpeed * r1 . randInt ( 1000 ) ) ;
float SpeedZ = ( float ) ( a_FlyAwaySpeed * ( r1 . randInt ( 1000 ) - 500 ) ) ;
2013-05-12 12:23:49 -04:00
2013-05-12 17:00:21 -04:00
// Add random offset to the spawn position:
int MicroX = ( int ) ( a_BlockX * 32 ) + ( r1 . randInt ( 16 ) + r1 . randInt ( 16 ) - 16 ) ;
int MicroY = ( int ) ( a_BlockY * 32 ) + ( r1 . randInt ( 16 ) + r1 . randInt ( 16 ) - 16 ) ;
int MicroZ = ( int ) ( a_BlockZ * 32 ) + ( r1 . randInt ( 16 ) + r1 . randInt ( 16 ) - 16 ) ;
2013-05-12 12:23:49 -04:00
// TODO 2013_05_12 _X: Because spawning pickups with nonzero speed causes them to bug (FS #338),
// I decided to temporarily reset the speed to zero to fix it, until we have proper pickup physics
SpeedX = SpeedY = SpeedZ = 0 ;
2013-05-12 17:00:21 -04:00
// TODO 2013_05_12 _X: It seems that pickups bug out even with zero speed, trying mid-block position:
MicroX = ( int ) ( floor ( a_BlockX ) * 32 ) + 16 ;
MicroY = ( int ) ( floor ( a_BlockY ) * 32 ) + 16 ;
MicroZ = ( int ) ( floor ( a_BlockZ ) * 32 ) + 16 ;
2012-06-14 09:06:06 -04:00
cPickup * Pickup = new cPickup (
2013-05-12 17:00:21 -04:00
MicroX , MicroY , MicroZ ,
2012-06-14 09:06:06 -04:00
* itr , SpeedX , SpeedY , SpeedZ
) ;
Pickup - > Initialize ( this ) ;
}
}
void cWorld : : SpawnItemPickups ( const cItems & a_Pickups , double a_BlockX , double a_BlockY , double a_BlockZ , double a_SpeedX , double a_SpeedY , double a_SpeedZ )
{
2013-05-12 17:00:21 -04:00
// TODO 2013_05_12 _X: Because spawning pickups with nonzero speed causes them to bug (FS #338),
// I decided to temporarily reset the speed to zero to fix it, until we have proper pickup physics
a_SpeedX = a_SpeedY = a_SpeedZ = 0 ;
2012-06-14 09:06:06 -04:00
MTRand r1 ;
for ( cItems : : const_iterator itr = a_Pickups . begin ( ) ; itr ! = a_Pickups . end ( ) ; + + itr )
{
2013-05-12 17:00:21 -04:00
// Add random offset to the spawn position:
int MicroX = ( int ) ( a_BlockX * 32 ) + ( r1 . randInt ( 16 ) + r1 . randInt ( 16 ) - 16 ) ;
int MicroY = ( int ) ( a_BlockY * 32 ) + ( r1 . randInt ( 16 ) + r1 . randInt ( 16 ) - 16 ) ;
int MicroZ = ( int ) ( a_BlockZ * 32 ) + ( r1 . randInt ( 16 ) + r1 . randInt ( 16 ) - 16 ) ;
// TODO 2013_05_12 _X: It seems that pickups bug out even with zero speed, trying mid-block position:
MicroX = ( int ) ( floor ( a_BlockX ) * 32 ) + 16 ;
MicroY = ( int ) ( floor ( a_BlockY ) * 32 ) + 16 ;
MicroZ = ( int ) ( floor ( a_BlockZ ) * 32 ) + 16 ;
2013-05-12 12:23:49 -04:00
2012-06-14 09:06:06 -04:00
cPickup * Pickup = new cPickup (
2013-05-12 17:00:21 -04:00
MicroX , MicroY , MicroZ ,
2012-06-14 09:06:06 -04:00
* itr , ( float ) a_SpeedX , ( float ) a_SpeedY , ( float ) a_SpeedZ
) ;
Pickup - > Initialize ( this ) ;
}
}
2013-06-21 17:19:47 -04:00
void cWorld : : SpawnPrimedTNT ( double a_X , double a_Y , double a_Z , float a_FuseTimeInSec , double a_InitialVelocityCoeff )
2013-06-18 15:09:51 -04:00
{
cTNTEntity * TNT = new cTNTEntity ( a_X , a_Y , a_Z , a_FuseTimeInSec ) ;
TNT - > Initialize ( this ) ;
2013-06-21 17:19:47 -04:00
// TODO: Add a bit of speed in horiz and vert axes, based on the a_InitialVelocityCoeff
2013-06-18 15:09:51 -04:00
}
2012-06-14 09:06:06 -04:00
void cWorld : : ReplaceBlocks ( const sSetBlockVector & a_Blocks , BLOCKTYPE a_FilterBlockType )
{
m_ChunkMap - > ReplaceBlocks ( a_Blocks , a_FilterBlockType ) ;
}
bool cWorld : : GetBlocks ( sSetBlockVector & a_Blocks , bool a_ContinueOnFailure )
{
return m_ChunkMap - > GetBlocks ( a_Blocks , a_ContinueOnFailure ) ;
}
2013-03-03 14:05:11 -05:00
bool cWorld : : DigBlock ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
2012-07-15 16:36:34 -04:00
cBlockHandler * Handler = cBlockHandler : : GetBlockHandler ( GetBlock ( a_X , a_Y , a_Z ) ) ;
Handler - > OnDestroyed ( this , a_X , a_Y , a_Z ) ;
2012-06-14 09:06:06 -04:00
return m_ChunkMap - > DigBlock ( a_X , a_Y , a_Z ) ;
}
2013-03-03 14:05:11 -05:00
void cWorld : : SendBlockTo ( int a_X , int a_Y , int a_Z , cPlayer * a_Player )
2012-06-14 09:06:06 -04:00
{
m_ChunkMap - > SendBlockTo ( a_X , a_Y , a_Z , a_Player ) ;
}
2013-03-03 14:05:11 -05:00
int cWorld : : GetHeight ( int a_X , int a_Z )
2012-06-14 09:06:06 -04:00
{
return m_ChunkMap - > GetHeight ( a_X , a_Z ) ;
}
2013-04-13 17:02:10 -04:00
bool cWorld : : TryGetHeight ( int a_BlockX , int a_BlockZ , int & a_Height )
{
return m_ChunkMap - > TryGetHeight ( a_BlockX , a_BlockZ , a_Height ) ;
}
2013-03-03 14:05:11 -05:00
void cWorld : : BroadcastAttachEntity ( const cEntity & a_Entity , const cEntity * a_Vehicle )
{
return m_ChunkMap - > BroadcastAttachEntity ( a_Entity , a_Vehicle ) ;
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastBlockAction ( int a_BlockX , int a_BlockY , int a_BlockZ , char a_Byte1 , char a_Byte2 , BLOCKTYPE a_BlockType , const cClientHandle * a_Exclude )
2012-08-18 06:38:15 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastBlockAction ( a_BlockX , a_BlockY , a_BlockZ , a_Byte1 , a_Byte2 , a_BlockType , a_Exclude ) ;
2012-08-18 06:38:15 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastBlockBreakAnimation ( int a_EntityID , int a_BlockX , int a_BlockY , int a_BlockZ , char a_Stage , const cClientHandle * a_Exclude )
2012-08-19 07:51:17 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastBlockBreakAnimation ( a_EntityID , a_BlockX , a_BlockY , a_BlockZ , a_Stage , a_Exclude ) ;
2012-08-19 07:51:17 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastBlockEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , const cClientHandle * a_Exclude )
2012-08-19 07:51:17 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastBlockEntity ( a_BlockX , a_BlockY , a_BlockZ , a_Exclude ) ;
2012-08-19 07:51:17 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastChat ( const AString & a_Message , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = a_Exclude ) | | ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
2013-07-07 09:06:06 -04:00
ch - > SendChat ( a_Message ) ;
2012-08-19 15:42:32 -04:00
}
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataSerializer & a_Serializer , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastChunkData ( a_ChunkX , a_ChunkZ , a_Serializer , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastCollectPickup ( const cPickup & a_Pickup , const cPlayer & a_Player , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastCollectPickup ( a_Pickup , a_Player , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastDestroyEntity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastDestroyEntity ( a_Entity , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityEquipment ( const cEntity & a_Entity , short a_SlotNum , const cItem & a_Item , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityEquipment ( a_Entity , a_SlotNum , a_Item , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityHeadLook ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityHeadLook ( a_Entity , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityLook ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityLook ( a_Entity , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityMetadata ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 17:14:45 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityMetadata ( a_Entity , a_Exclude ) ;
2012-08-19 17:14:45 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityRelMove ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ , const cClientHandle * a_Exclude )
2012-08-19 17:14:45 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityRelMove ( a_Entity , a_RelX , a_RelY , a_RelZ , a_Exclude ) ;
2012-08-19 17:14:45 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityRelMoveLook ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ , const cClientHandle * a_Exclude )
2012-08-24 03:58:26 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityRelMoveLook ( a_Entity , a_RelX , a_RelY , a_RelZ , a_Exclude ) ;
2012-08-24 03:58:26 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityStatus ( const cEntity & a_Entity , char a_Status , const cClientHandle * a_Exclude )
2012-08-24 05:49:00 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityStatus ( a_Entity , a_Status , a_Exclude ) ;
2012-08-24 05:49:00 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityVelocity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
{
m_ChunkMap - > BroadcastEntityVelocity ( a_Entity , a_Exclude ) ;
}
void cWorld : : BroadcastPlayerAnimation ( const cPlayer & a_Player , char a_Animation , const cClientHandle * a_Exclude )
{
m_ChunkMap - > BroadcastPlayerAnimation ( a_Player , a_Animation , a_Exclude ) ;
}
void cWorld : : BroadcastPlayerListItem ( const cPlayer & a_Player , bool a_IsOnline , const cClientHandle * a_Exclude )
2012-08-25 17:46:18 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = a_Exclude ) | | ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
2013-07-07 09:06:06 -04:00
ch - > SendPlayerListItem ( a_Player , a_IsOnline ) ;
2012-08-25 17:46:18 -04:00
}
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastSoundEffect ( const AString & a_SoundName , int a_SrcX , int a_SrcY , int a_SrcZ , float a_Volume , float a_Pitch , const cClientHandle * a_Exclude )
2012-08-25 17:46:18 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastSoundEffect ( a_SoundName , a_SrcX , a_SrcY , a_SrcZ , a_Volume , a_Pitch , a_Exclude ) ;
2012-08-25 17:46:18 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastSoundParticleEffect ( int a_EffectID , int a_SrcX , int a_SrcY , int a_SrcZ , int a_Data , const cClientHandle * a_Exclude )
2012-08-25 17:46:18 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastSoundParticleEffect ( a_EffectID , a_SrcX , a_SrcY , a_SrcZ , a_Data , a_Exclude ) ;
2012-08-25 17:46:18 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastSpawnEntity ( cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-26 17:01:07 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastSpawnEntity ( a_Entity , a_Exclude ) ;
2012-08-26 17:01:07 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastTeleportEntity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-27 13:31:16 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = a_Exclude ) | | ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
2013-07-07 09:06:06 -04:00
ch - > SendTeleportEntity ( a_Entity ) ;
2012-08-27 13:31:16 -04:00
}
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ , const cClientHandle * a_Exclude )
2012-09-25 05:54:36 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastThunderbolt ( a_BlockX , a_BlockY , a_BlockZ , a_Exclude ) ;
2012-10-21 03:46:28 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastTimeUpdate ( const cClientHandle * a_Exclude )
2012-10-21 03:46:28 -04:00
{
2013-07-07 09:06:06 -04:00
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = a_Exclude ) | | ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
ch - > SendTimeUpdate ( m_WorldAge , m_TimeOfDay ) ;
}
2012-09-25 05:54:36 -04:00
}
2013-03-03 14:05:11 -05:00
void cWorld : : BroadcastUseBed ( const cEntity & a_Entity , int a_BlockX , int a_BlockY , int a_BlockZ )
2012-09-29 16:43:42 -04:00
{
m_ChunkMap - > BroadcastUseBed ( a_Entity , a_BlockX , a_BlockY , a_BlockZ ) ;
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastWeather ( eWeather a_Weather , const cClientHandle * a_Exclude )
2012-08-24 03:58:26 -04:00
{
2013-07-07 09:06:06 -04:00
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = a_Exclude ) | | ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
ch - > SendWeather ( a_Weather ) ;
}
2012-08-24 03:58:26 -04:00
}
void cWorld : : SendBlockEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , cClientHandle & a_Client )
{
m_ChunkMap - > SendBlockEntity ( a_BlockX , a_BlockY , a_BlockZ , a_Client ) ;
}
2013-04-13 17:02:10 -04:00
void cWorld : : MarkChunkDirty ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > MarkChunkDirty ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : MarkChunkSaving ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > MarkChunkSaving ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : MarkChunkSaved ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > MarkChunkSaved ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : SetChunkData (
2013-04-13 17:02:10 -04:00
int a_ChunkX , int a_ChunkZ ,
2012-06-14 09:06:06 -04:00
const BLOCKTYPE * a_BlockTypes ,
const NIBBLETYPE * a_BlockMeta ,
const NIBBLETYPE * a_BlockLight ,
const NIBBLETYPE * a_BlockSkyLight ,
const cChunkDef : : HeightMap * a_HeightMap ,
const cChunkDef : : BiomeMap * a_BiomeMap ,
cEntityList & a_Entities ,
cBlockEntityList & a_BlockEntities ,
bool a_MarkDirty
)
{
// Validate biomes, if needed:
cChunkDef : : BiomeMap BiomeMap ;
const cChunkDef : : BiomeMap * Biomes = a_BiomeMap ;
if ( a_BiomeMap = = NULL )
{
// The biomes are not assigned, get them from the generator:
Biomes = & BiomeMap ;
m_Generator . GenerateBiomes ( a_ChunkX , a_ChunkZ , BiomeMap ) ;
}
2013-04-01 16:56:25 -04:00
m_ChunkMap - > SetChunkData (
2013-04-13 17:02:10 -04:00
a_ChunkX , a_ChunkZ ,
2013-04-01 16:56:25 -04:00
a_BlockTypes , a_BlockMeta , a_BlockLight , a_BlockSkyLight ,
a_HeightMap , * Biomes ,
2013-04-13 17:02:10 -04:00
a_BlockEntities ,
2013-04-01 16:56:25 -04:00
a_MarkDirty
) ;
// Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347):
for ( cEntityList : : iterator itr = a_Entities . begin ( ) , end = a_Entities . end ( ) ; itr ! = end ; + + itr )
2013-04-01 14:24:05 -04:00
{
2013-04-01 16:56:25 -04:00
( * itr ) - > Initialize ( this ) ;
2013-04-01 14:24:05 -04:00
}
2012-06-14 09:06:06 -04:00
// If a client is requesting this chunk, send it to them:
2013-04-13 17:02:10 -04:00
if ( m_ChunkMap - > HasChunkAnyClients ( a_ChunkX , a_ChunkZ ) )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkSender . ChunkReady ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
// Notify the lighting thread that the chunk has become valid (in case it is a neighbor of a postponed chunk):
m_Lighting . ChunkReady ( a_ChunkX , a_ChunkZ ) ;
}
void cWorld : : ChunkLighted (
int a_ChunkX , int a_ChunkZ ,
const cChunkDef : : BlockNibbles & a_BlockLight ,
const cChunkDef : : BlockNibbles & a_SkyLight
)
{
m_ChunkMap - > ChunkLighted ( a_ChunkX , a_ChunkZ , a_BlockLight , a_SkyLight ) ;
}
2013-04-13 17:02:10 -04:00
bool cWorld : : GetChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > GetChunkData ( a_ChunkX , a_ChunkZ , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : GetChunkBlockTypes ( int a_ChunkX , int a_ChunkZ , BLOCKTYPE * a_BlockTypes )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > GetChunkBlockTypes ( a_ChunkX , a_ChunkZ , a_BlockTypes ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : IsChunkValid ( int a_ChunkX , int a_ChunkZ ) const
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > IsChunkValid ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : HasChunkAnyClients ( int a_ChunkX , int a_ChunkZ ) const
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > HasChunkAnyClients ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
void cWorld : : UnloadUnusedChunks ( void )
2012-06-14 09:06:06 -04:00
{
2012-11-01 17:38:20 -04:00
m_LastUnload = m_WorldAge ;
2012-06-14 09:06:06 -04:00
m_ChunkMap - > UnloadUnusedChunks ( ) ;
}
void cWorld : : CollectPickupsByPlayer ( cPlayer * a_Player )
{
m_ChunkMap - > CollectPickupsByPlayer ( a_Player ) ;
}
void cWorld : : SetMaxPlayers ( int iMax )
{
m_MaxPlayers = MAX_PLAYERS ;
if ( iMax > 0 & & iMax < MAX_PLAYERS )
{
m_MaxPlayers = iMax ;
}
}
2013-04-13 17:02:10 -04:00
void cWorld : : AddPlayer ( cPlayer * a_Player )
2012-06-14 09:06:06 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
ASSERT ( std : : find ( m_Players . begin ( ) , m_Players . end ( ) , a_Player ) = = m_Players . end ( ) ) ; // Is it already in the list? HOW?
2013-03-03 14:05:11 -05:00
m_Players . remove ( a_Player ) ; // Make sure the player is registered only once
m_Players . push_back ( a_Player ) ;
2013-07-03 03:47:35 -04:00
// The player has already been added to the chunkmap as the entity, do NOT add again!
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : RemovePlayer ( cPlayer * a_Player )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:28:55 -04:00
m_ChunkMap - > RemoveEntity ( a_Player ) ;
2012-06-14 09:06:06 -04:00
cCSLock Lock ( m_CSPlayers ) ;
2013-03-03 14:05:11 -05:00
m_Players . remove ( a_Player ) ;
2012-06-14 09:06:06 -04:00
}
bool cWorld : : ForEachPlayer ( cPlayerListCallback & a_Callback )
{
// Calls the callback for each player in the list
cCSLock Lock ( m_CSPlayers ) ;
2012-06-19 16:31:21 -04:00
for ( cPlayerList : : iterator itr = m_Players . begin ( ) , itr2 = itr ; itr ! = m_Players . end ( ) ; itr = itr2 )
2012-06-14 09:06:06 -04:00
{
2012-06-19 16:31:21 -04:00
+ + itr2 ;
2012-06-14 09:06:06 -04:00
if ( a_Callback . Item ( * itr ) )
{
return false ;
}
} // for itr - m_Players[]
return true ;
}
2012-07-02 07:21:21 -04:00
bool cWorld : : DoWithPlayer ( const AString & a_PlayerName , cPlayerListCallback & a_Callback )
{
// Calls the callback for each player in the list
2012-06-14 09:06:06 -04:00
cCSLock Lock ( m_CSPlayers ) ;
2012-07-02 07:21:21 -04:00
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
2012-07-13 15:25:53 -04:00
if ( NoCaseCompare ( ( * itr ) - > GetName ( ) , a_PlayerName ) = = 0 )
2012-06-14 09:06:06 -04:00
{
2012-07-02 07:21:21 -04:00
a_Callback . Item ( * itr ) ;
return true ;
2012-06-14 09:06:06 -04:00
}
2012-07-02 07:21:21 -04:00
} // for itr - m_Players[]
return false ;
2012-06-14 09:06:06 -04:00
}
2013-02-01 14:59:58 -05:00
bool cWorld : : FindAndDoWithPlayer ( const AString & a_PlayerNameHint , cPlayerListCallback & a_Callback )
2012-08-22 19:05:12 -04:00
{
2013-02-01 14:59:58 -05:00
cPlayer * BestMatch = NULL ;
2012-08-22 19:05:12 -04:00
unsigned int BestRating = 0 ;
2013-02-01 14:59:58 -05:00
unsigned int NameLength = a_PlayerNameHint . length ( ) ;
2012-08-22 19:05:12 -04:00
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
2013-02-01 14:59:58 -05:00
unsigned int Rating = RateCompareString ( a_PlayerNameHint , ( * itr ) - > GetName ( ) ) ;
if ( Rating > = BestRating )
2012-08-22 19:05:12 -04:00
{
BestMatch = * itr ;
BestRating = Rating ;
}
2013-02-01 14:59:58 -05:00
if ( Rating = = NameLength ) // Perfect match
2012-08-22 19:05:12 -04:00
{
break ;
}
} // for itr - m_Players[]
2013-02-01 14:59:58 -05:00
if ( BestMatch ! = NULL )
2012-08-22 19:05:12 -04:00
{
2013-02-01 14:59:58 -05:00
LOG ( " Compared %s and %s with rating %i " , a_PlayerNameHint . c_str ( ) , BestMatch - > GetName ( ) . c_str ( ) , BestRating ) ;
2012-08-22 19:05:12 -04:00
return a_Callback . Item ( BestMatch ) ;
}
return false ;
}
2012-07-02 07:21:21 -04:00
// TODO: This interface is dangerous!
2012-06-14 09:06:06 -04:00
cPlayer * cWorld : : FindClosestPlayer ( const Vector3f & a_Pos , float a_SightLimit )
{
cTracer LineOfSight ( this ) ;
float ClosestDistance = a_SightLimit ;
cPlayer * ClosestPlayer = NULL ;
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : const_iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
Vector3f Pos = ( * itr ) - > GetPosition ( ) ;
float Distance = ( Pos - a_Pos ) . Length ( ) ;
if ( Distance < = a_SightLimit )
{
if ( ! LineOfSight . Trace ( a_Pos , ( Pos - a_Pos ) , ( int ) ( Pos - a_Pos ) . Length ( ) ) )
{
if ( Distance < ClosestDistance )
{
ClosestDistance = Distance ;
ClosestPlayer = * itr ;
}
}
}
}
return ClosestPlayer ;
}
void cWorld : : SendPlayerList ( cPlayer * a_DestPlayer )
{
// Sends the playerlist to a_DestPlayer
cCSLock Lock ( m_CSPlayers ) ;
2012-08-19 15:42:32 -04:00
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch ! = NULL ) & & ! ch - > IsDestroyed ( ) )
{
2012-08-27 13:31:16 -04:00
a_DestPlayer - > GetClientHandle ( ) - > SendPlayerListItem ( * ( * itr ) , true ) ;
2012-06-14 09:06:06 -04:00
}
}
}
2012-06-16 04:35:07 -04:00
bool cWorld : : ForEachEntity ( cEntityCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > ForEachEntity ( a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2012-06-16 04:35:07 -04:00
bool cWorld : : ForEachEntityInChunk ( int a_ChunkX , int a_ChunkZ , cEntityCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2012-06-16 04:35:07 -04:00
return m_ChunkMap - > ForEachEntityInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
bool cWorld : : DoWithEntityByID ( int a_UniqueID , cEntityCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > DoWithEntityByID ( a_UniqueID , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : CompareChunkClients ( int a_ChunkX1 , int a_ChunkZ1 , int a_ChunkX2 , int a_ChunkZ2 , cClientDiffCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > CompareChunkClients ( a_ChunkX1 , a_ChunkZ1 , a_ChunkX2 , a_ChunkZ2 , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : AddChunkClient ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > AddChunkClient ( a_ChunkX , a_ChunkZ , a_Client ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : RemoveChunkClient ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > RemoveChunkClient ( a_ChunkX , a_ChunkZ , a_Client ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : RemoveClientFromChunks ( cClientHandle * a_Client )
{
m_ChunkMap - > RemoveClientFromChunks ( a_Client ) ;
}
2013-04-13 17:02:10 -04:00
void cWorld : : SendChunkTo ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkSender . QueueSendChunkTo ( a_ChunkX , a_ChunkZ , a_Client ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : RemoveClientFromChunkSender ( cClientHandle * a_Client )
{
m_ChunkSender . RemoveClient ( a_Client ) ;
}
void cWorld : : TouchChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
m_ChunkMap - > TouchChunk ( a_ChunkX , a_ChunkY , a_ChunkZ ) ;
}
bool cWorld : : LoadChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
return m_ChunkMap - > LoadChunk ( a_ChunkX , a_ChunkY , a_ChunkZ ) ;
}
void cWorld : : LoadChunks ( const cChunkCoordsList & a_Chunks )
{
m_ChunkMap - > LoadChunks ( a_Chunks ) ;
}
void cWorld : : ChunkLoadFailed ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
m_ChunkMap - > ChunkLoadFailed ( a_ChunkX , a_ChunkY , a_ChunkZ ) ;
}
2013-06-12 03:14:06 -04:00
bool cWorld : : SetSignLines ( int a_BlockX , int a_BlockY , int a_BlockZ , const AString & a_Line1 , const AString & a_Line2 , const AString & a_Line3 , const AString & a_Line4 , cPlayer * a_Player )
2012-06-14 09:06:06 -04:00
{
2012-06-16 11:06:14 -04:00
AString Line1 ( a_Line1 ) ;
AString Line2 ( a_Line2 ) ;
AString Line3 ( a_Line3 ) ;
AString Line4 ( a_Line4 ) ;
2012-09-01 17:31:20 -04:00
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookUpdatingSign ( this , a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 , a_Player ) )
2012-06-16 11:06:14 -04:00
{
2013-06-12 03:14:06 -04:00
return false ;
}
if ( m_ChunkMap - > SetSignLines ( a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 ) )
{
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookUpdatedSign ( this , a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 , a_Player ) ;
return true ;
2012-06-16 11:06:14 -04:00
}
2013-06-12 03:14:06 -04:00
return false ;
}
bool cWorld : : UpdateSign ( int a_BlockX , int a_BlockY , int a_BlockZ , const AString & a_Line1 , const AString & a_Line2 , const AString & a_Line3 , const AString & a_Line4 , cPlayer * a_Player )
{
return SetSignLines ( a_BlockX , a_BlockY , a_BlockZ , a_Line1 , a_Line2 , a_Line3 , a_Line4 , a_Player ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : ChunksStay ( const cChunkCoordsList & a_Chunks , bool a_Stay )
{
m_ChunkMap - > ChunksStay ( a_Chunks , a_Stay ) ;
}
void cWorld : : RegenerateChunk ( int a_ChunkX , int a_ChunkZ )
{
m_ChunkMap - > MarkChunkRegenerating ( a_ChunkX , a_ChunkZ ) ;
// Trick: use Y=1 to force the chunk generation even though the chunk data is already present
m_Generator . QueueGenerateChunk ( a_ChunkX , 1 , a_ChunkZ ) ;
}
void cWorld : : GenerateChunk ( int a_ChunkX , int a_ChunkZ )
{
m_Generator . QueueGenerateChunk ( a_ChunkX , ZERO_CHUNK_Y , a_ChunkZ ) ;
}
void cWorld : : QueueLightChunk ( int a_ChunkX , int a_ChunkZ , cChunkCoordCallback * a_Callback )
{
m_Lighting . QueueChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
bool cWorld : : IsChunkLighted ( int a_ChunkX , int a_ChunkZ )
{
return m_ChunkMap - > IsChunkLighted ( a_ChunkX , a_ChunkZ ) ;
}
2012-07-02 12:30:17 -04:00
bool cWorld : : ForEachChunkInRect ( int a_MinChunkX , int a_MaxChunkX , int a_MinChunkZ , int a_MaxChunkZ , cChunkDataCallback & a_Callback )
{
return m_ChunkMap - > ForEachChunkInRect ( a_MinChunkX , a_MaxChunkX , a_MinChunkZ , a_MaxChunkZ , a_Callback ) ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : SaveAllChunks ( void )
{
2012-08-15 07:54:13 -04:00
LOGINFO ( " Saving all chunks... " ) ;
2012-11-01 17:38:20 -04:00
m_LastSave = m_WorldAge ;
2012-06-14 09:06:06 -04:00
m_ChunkMap - > SaveAllChunks ( ) ;
2012-08-15 07:54:13 -04:00
m_Storage . QueueSavedMessage ( ) ;
2012-06-14 09:06:06 -04:00
}
2012-11-01 17:38:20 -04:00
void cWorld : : AddEntity ( cEntity * a_Entity )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > AddEntity ( a_Entity ) ;
}
bool cWorld : : HasEntity ( int a_UniqueID )
{
return m_ChunkMap - > HasEntity ( a_UniqueID ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:28:55 -04:00
void cWorld : : RemoveEntity ( cEntity * a_Entity )
{
m_ChunkMap - > RemoveEntity ( a_Entity ) ;
}
2012-11-01 17:38:20 -04:00
unsigned int cWorld : : GetNumPlayers ( void )
2012-06-14 09:06:06 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
return m_Players . size ( ) ;
}
int cWorld : : GetNumChunks ( void ) const
{
return m_ChunkMap - > GetNumChunks ( ) ;
}
void cWorld : : GetChunkStats ( int & a_NumValid , int & a_NumDirty , int & a_NumInLightingQueue )
{
m_ChunkMap - > GetChunkStats ( a_NumValid , a_NumDirty ) ;
a_NumInLightingQueue = ( int ) m_Lighting . GetQueueLength ( ) ;
}
2012-10-13 04:56:12 -04:00
2012-07-15 16:36:34 -04:00
void cWorld : : TickQueuedBlocks ( float a_Dt )
{
2012-10-13 04:56:12 -04:00
if ( m_BlockTickQueue . empty ( ) )
{
2012-07-15 16:36:34 -04:00
return ;
2012-10-13 04:56:12 -04:00
}
2012-07-15 16:36:34 -04:00
m_BlockTickQueueCopy . clear ( ) ;
m_BlockTickQueue . swap ( m_BlockTickQueueCopy ) ;
2012-10-13 04:56:12 -04:00
for ( std : : vector < BlockTickQueueItem * > : : iterator itr = m_BlockTickQueueCopy . begin ( ) ; itr ! = m_BlockTickQueueCopy . end ( ) ; itr + + )
2012-07-15 16:36:34 -04:00
{
BlockTickQueueItem * Block = ( * itr ) ;
Block - > ToWait - = a_Dt ;
2012-10-13 04:56:12 -04:00
if ( Block - > ToWait < = 0 )
2012-07-15 16:36:34 -04:00
{
BlockHandler ( GetBlock ( Block - > X , Block - > Y , Block - > Z ) ) - > OnUpdate ( this , Block - > X , Block - > Y , Block - > Z ) ;
2012-10-13 04:56:12 -04:00
delete Block ; //We don't have to remove it from the vector, this will happen automatically on the next tick
}
else
{
2012-07-15 16:36:34 -04:00
m_BlockTickQueue . push_back ( Block ) ; //Keep the block in the queue
}
2012-10-13 04:56:12 -04:00
} // for itr - m_BlockTickQueueCopy[]
2012-07-15 16:36:34 -04:00
}
2012-10-13 04:56:12 -04:00
void cWorld : : QueueBlockForTick ( int a_BlockX , int a_BlockY , int a_BlockZ , float a_TimeToWait )
2012-07-15 16:36:34 -04:00
{
2012-10-13 04:56:12 -04:00
BlockTickQueueItem * Block = new BlockTickQueueItem ;
Block - > X = a_BlockX ;
Block - > Y = a_BlockY ;
Block - > Z = a_BlockZ ;
Block - > ToWait = a_TimeToWait ;
2012-07-15 16:36:34 -04:00
m_BlockTickQueue . push_back ( Block ) ;
}
2012-10-13 04:56:12 -04:00
bool cWorld : : IsBlockDirectlyWatered ( int a_BlockX , int a_BlockY , int a_BlockZ )
2012-07-15 16:36:34 -04:00
{
2012-10-13 04:56:12 -04:00
return (
IsBlockWater ( GetBlock ( a_BlockX - 1 , a_BlockY , a_BlockZ ) ) | |
IsBlockWater ( GetBlock ( a_BlockX + 1 , a_BlockY , a_BlockZ ) ) | |
IsBlockWater ( GetBlock ( a_BlockX , a_BlockY , a_BlockZ - 1 ) ) | |
IsBlockWater ( GetBlock ( a_BlockX , a_BlockY , a_BlockZ + 1 ) )
) ;
}
2012-10-28 10:57:35 -04:00
int cWorld : : SpawnMob ( double a_PosX , double a_PosY , double a_PosZ , int a_EntityType )
{
cMonster * Monster = NULL ;
2013-07-01 06:39:56 -04:00
int Size = GetTickRandomNumber ( 2 ) + 1 ; // 1 .. 3
2012-10-28 10:57:35 -04:00
switch ( a_EntityType )
{
2013-07-01 06:39:56 -04:00
case E_ENTITY_TYPE_BAT : Monster = new cBat ( ) ; break ;
case E_ENTITY_TYPE_BLAZE : Monster = new cBlaze ( ) ; break ;
case E_ENTITY_TYPE_CAVE_SPIDER : Monster = new cCavespider ( ) ; break ;
case E_ENTITY_TYPE_CHICKEN : Monster = new cChicken ( ) ; break ;
case E_ENTITY_TYPE_COW : Monster = new cCow ( ) ; break ;
case E_ENTITY_TYPE_CREEPER : Monster = new cCreeper ( ) ; break ;
case E_ENTITY_TYPE_ENDERMAN : Monster = new cEnderman ( ) ; break ;
case E_ENTITY_TYPE_GHAST : Monster = new cGhast ( ) ; break ;
case E_ENTITY_TYPE_MAGMA_CUBE : Monster = new cMagmacube ( Size ) ; break ;
case E_ENTITY_TYPE_MOOSHROOM : Monster = new cMooshroom ( ) ; break ;
case E_ENTITY_TYPE_OCELOT : Monster = new cOcelot ( ) ; break ;
case E_ENTITY_TYPE_PIG : Monster = new cPig ( ) ; break ;
case E_ENTITY_TYPE_SHEEP : Monster = new cSheep ( ) ; break ;
case E_ENTITY_TYPE_SILVERFISH : Monster = new cSilverfish ( ) ; break ;
case E_ENTITY_TYPE_SKELETON : Monster = new cSkeleton ( ) ; break ;
case E_ENTITY_TYPE_SLIME : Monster = new cSlime ( Size ) ; break ;
case E_ENTITY_TYPE_SPIDER : Monster = new cSpider ( ) ; break ;
case E_ENTITY_TYPE_SQUID : Monster = new cSquid ( ) ; break ;
case E_ENTITY_TYPE_VILLAGER : Monster = new cVillager ( ) ; break ;
case E_ENTITY_TYPE_WITCH : Monster = new cWitch ( ) ; break ;
case E_ENTITY_TYPE_WOLF : Monster = new cWolf ( ) ; break ;
case E_ENTITY_TYPE_ZOMBIE : Monster = new cZombie ( ) ; break ;
case E_ENTITY_TYPE_ZOMBIE_PIGMAN : Monster = new cZombiepigman ( ) ; break ;
2012-10-28 10:57:35 -04:00
default :
{
2012-10-28 17:01:54 -04:00
LOGWARNING ( " cWorld::SpawnMob(): Unhandled entity type: %d. Not spawning. " , a_EntityType ) ;
2012-10-28 10:57:35 -04:00
return - 1 ;
}
}
2013-04-13 17:02:10 -04:00
Monster - > SetPosition ( a_PosX , a_PosY , a_PosZ ) ;
2013-07-07 11:09:05 -04:00
Monster - > SetHealth ( Monster - > GetMaxHealth ( ) ) ;
2012-10-28 10:57:35 -04:00
Monster - > Initialize ( this ) ;
2013-07-07 09:06:06 -04:00
BroadcastSpawnEntity ( * Monster ) ;
2012-10-28 10:57:35 -04:00
return Monster - > GetUniqueID ( ) ;
}
2013-07-30 16:48:59 -04:00
void cWorld : : TabCompleteUserName ( const AString & a_Text , AStringVector & a_Results )
{
2013-07-31 05:16:11 -04:00
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) , end = m_Players . end ( ) ; itr ! = end ; + + itr )
{
if ( NoCaseCompare ( ( * itr ) - > GetName ( ) . substr ( 0 , a_Text . length ( ) ) , a_Text ) ! = 0 )
{
// Player name doesn't match
continue ;
}
a_Results . push_back ( ( * itr ) - > GetName ( ) ) ;
}
2013-07-30 16:48:59 -04:00
}
2012-10-13 05:53:28 -04:00
cFluidSimulator * cWorld : : InitializeFluidSimulator ( cIniFile & a_IniFile , const char * a_FluidName , BLOCKTYPE a_SimulateBlock , BLOCKTYPE a_StationaryBlock )
{
AString SimulatorNameKey ;
Printf ( SimulatorNameKey , " %sSimulator " , a_FluidName ) ;
AString SimulatorSectionName ;
Printf ( SimulatorSectionName , " %sSimulator " , a_FluidName ) ;
2012-10-14 13:06:21 -04:00
AString SimulatorName = a_IniFile . GetValueSet ( " Physics " , SimulatorNameKey , " " ) ;
2012-10-13 05:53:28 -04:00
if ( SimulatorName . empty ( ) )
{
2013-03-14 16:50:01 -04:00
LOGWARNING ( " %s [Physics]:%s not present or empty, using the default of \" Floody \" . " , GetIniFileName ( ) . c_str ( ) , SimulatorNameKey . c_str ( ) ) ;
SimulatorName = " Floody " ;
2012-10-13 05:53:28 -04:00
}
cFluidSimulator * res = NULL ;
2012-10-14 16:10:03 -04:00
bool IsWater = ( strcmp ( a_FluidName , " Water " ) = = 0 ) ; // Used for defaults
2012-10-15 16:16:43 -04:00
int Rate = 1 ;
2013-03-14 16:46:40 -04:00
if (
2013-03-14 15:44:27 -04:00
( NoCaseCompare ( SimulatorName , " vaporize " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " vaporise " ) = = 0 )
)
{
res = new cVaporizeFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock ) ;
}
2013-03-14 16:03:42 -04:00
else if (
( NoCaseCompare ( SimulatorName , " noop " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " nop " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " null " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " nil " ) = = 0 )
)
{
res = new cNoopFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock ) ;
}
2012-10-13 05:53:28 -04:00
else
{
2013-03-14 16:46:40 -04:00
if ( NoCaseCompare ( SimulatorName , " floody " ) ! = 0 )
2012-10-13 05:53:28 -04:00
{
// The simulator name doesn't match anything we have, issue a warning:
2013-03-14 16:46:40 -04:00
LOGWARNING ( " %s [Physics]:%s specifies an unknown simulator, using the default \" Floody \" . " , GetIniFileName ( ) . c_str ( ) , SimulatorNameKey . c_str ( ) ) ;
2012-10-13 05:53:28 -04:00
}
2013-03-14 16:46:40 -04:00
int Falloff = a_IniFile . GetValueSetI ( SimulatorSectionName , " Falloff " , IsWater ? 1 : 2 ) ;
int TickDelay = a_IniFile . GetValueSetI ( SimulatorSectionName , " TickDelay " , IsWater ? 5 : 30 ) ;
int NumNeighborsForSource = a_IniFile . GetValueSetI ( SimulatorSectionName , " NumNeighborsForSource " , IsWater ? 2 : - 1 ) ;
res = new cFloodyFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock , Falloff , TickDelay , NumNeighborsForSource ) ;
2012-10-13 05:53:28 -04:00
}
2012-10-15 16:16:43 -04:00
m_SimulatorManager - > RegisterSimulator ( res , Rate ) ;
2012-10-13 05:53:28 -04:00
return res ;
}