@ -1,21 +1,29 @@
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
# include "Globals.h"
# include "cFluidSimulator.h"
# include "cWorld.h"
# include "Vector3i.h"
# include "BlockID.h"
# include "Defines.h"
# include <set>
# include "cPickup.h"
# include "cItem.h"
# include <queue>
//#define DEBUG_FLUID
# ifdef DEBUG_FLUID
# define LOG_FLUID(...) LOGWARN( __VA_ARGS__ )
# else
# define LOG_FLUID(...)
# endif
class cFluidSimulator : : FluidData
{
public :
FluidData ( cWorld * a_World , cFluidSimulator * a_Simulator )
: m_ActiveFluid ( new std : : list < Vector3i > ( ) )
: m_ActiveFluid ( new std : : se t < Vector3i > ( ) )
, m_Simulator ( a_Simulator )
, m_Buffer ( new std : : list < Vector3i > ( ) )
, m_Buffer ( new std : : se t< Vector3i > ( ) )
, m_World ( a_World )
{ }
@ -25,39 +33,156 @@ public:
delete m_ActiveFluid ;
}
void UpdateWave ( Vector3i a_LeftCorner , Vector3i a_CurBlock )
{
Vector3i LevelPoints [ ] = {
Vector3i ( a_CurBlock . x - 1 , a_CurBlock . y , a_CurBlock . z ) ,
Vector3i ( a_CurBlock . x + 1 , a_CurBlock . y , a_CurBlock . z ) ,
Vector3i ( a_CurBlock . x , a_CurBlock . y , a_CurBlock . z - 1 ) ,
Vector3i ( a_CurBlock . x , a_CurBlock . y , a_CurBlock . z + 1 ) ,
} ;
for ( int i = 0 ; i < 4 ; i + + )
{
Vector3i cur = LevelPoints [ i ] ;
switch ( m_Relief [ cur . x ] [ cur . z ] )
{
case E_HOLE :
{
m_StartSide [ cur . x ] [ cur . z ] = m_StartSide [ a_CurBlock . x ] [ a_CurBlock . z ] ;
m_CurResult | = m_StartSide [ cur . x ] [ cur . z ] ;
m_NearestHole = m_WayLength [ a_CurBlock . x ] [ a_CurBlock . z ] + 1 ;
LOG_FLUID ( " Hole found: %d \t curResult: %d " , int ( m_StartSide [ cur . x ] [ cur . z ] ) , int ( m_CurResult ) ) ;
LOG_FLUID ( " Coordinates: (%d, %d) " , cur . x , cur . z ) ;
} break ;
case E_BLOCK :
{ } break ;
case E_PLAIN :
{
if ( m_WayLength [ cur . x ] [ cur . z ] > m_WayLength [ a_CurBlock . x ] [ a_CurBlock . z ] + 1 )
{
m_WayLength [ cur . x ] [ cur . z ] = m_WayLength [ a_CurBlock . x ] [ a_CurBlock . z ] + 1 ;
m_StartSide [ cur . x ] [ cur . z ] = m_StartSide [ a_CurBlock . x ] [ a_CurBlock . z ] ;
m_WaveQueue . push ( cur ) ;
}
else if ( m_WayLength [ cur . x ] [ cur . z ] = = m_WayLength [ a_CurBlock . x ] [ a_CurBlock . z ] + 1 )
{
m_StartSide [ cur . x ] [ cur . z ] | = m_StartSide [ a_CurBlock . x ] [ a_CurBlock . z ] ;
}
LOG_FLUID ( " Plain step: (%d, %d) from %d " , cur . x , cur . z , m_StartSide [ cur . x ] [ cur . z ] ) ;
}
}
}
}
std : : vector < Vector3i > GetLowestPoints ( int a_X , int a_Y , int a_Z )
{
std : : vector < Vector3i > Points ;
if ( m_World - > GetBlock ( a_X , a_Y - 1 , a_Z ) = = E_BLOCK_AIR )
std : : vector < Vector3i > Points ; //result
Vector3i CornerGlobal ( a_X - AREA_WIDTH / 2 , a_Y , a_Z - AREA_WIDTH / 2 ) ;
//TODO: rewrite without relief, get blocks directly in algorithm
for ( int x = 0 ; x < AREA_WIDTH ; x + + )
{
Points . push_back ( Vector3i ( a_X , a_Y - 1 , a_Z ) ) ;
return Points ;
for ( int z = 0 ; z < AREA_WIDTH ; z + + )
{
char UpperBlock = m_World - > GetBlock ( CornerGlobal . x + x , CornerGlobal . y , CornerGlobal . z + z ) ;
char DownBlock = m_World - > GetBlock ( CornerGlobal . x + x , CornerGlobal . y - 1 , CornerGlobal . z + z ) ;
if ( m_Simulator - > IsSolidBlock ( UpperBlock ) | | ( m_Simulator - > IsStationaryBlock ( UpperBlock ) ) )
{
m_Relief [ x ] [ z ] = E_BLOCK ;
}
else if ( m_Simulator - > IsSolidBlock ( DownBlock ) )
{
m_Relief [ x ] [ z ] = E_PLAIN ;
}
else
{
m_Relief [ x ] [ z ] = E_HOLE ;
}
m_WayLength [ x ] [ z ] = 255 ;
m_StartSide [ x ] [ z ] = E_SIDE_NONE ;
}
LOG_FLUID ( " %d \t %d \t %d \t %d \t %d \t %d \t %d \t %d \t %d \t %d \t %d \t " , m_Relief [ x ] [ 0 ] , m_Relief [ x ] [ 1 ] , m_Relief [ x ] [ 2 ] , m_Relief [ x ] [ 3 ] , m_Relief [ x ] [ 4 ] , m_Relief [ x ] [ 5 ] , m_Relief [ x ] [ 6 ] , m_Relief [ x ] [ 7 ] , m_Relief [ x ] [ 8 ] , m_Relief [ x ] [ 9 ] , m_Relief [ x ] [ 10 ] ) ;
}
Vector3i LowerPoints [ ] = {
Vector3i ( a_X - 1 , a_Y - 1 , a_Z ) ,
Vector3i ( a_X + 1 , a_Y - 1 , a_Z ) ,
Vector3i ( a_X , a_Y - 1 , a_Z - 1 ) ,
Vector3i ( a_X , a_Y - 1 , a_Z + 1 ) ,
} ;
bool bFluidFound = false ;
for ( int i = 0 ; i < 4 ; + + i )
m_NearestHole = 5 ;
m_CurResult = 0 ;
while ( ! m_WaveQueue . empty ( ) ) m_WaveQueue . pop ( ) ;
int left = AREA_WIDTH / 2 - 1 ;
int right = AREA_WIDTH / 2 + 1 ;
int center = AREA_WIDTH / 2 ;
Vector3i r ( right , 0 , center ) ; //right block
Vector3i l ( left , 0 , center ) ; //left block
Vector3i f ( center , 0 , right ) ; //front block
Vector3i b ( center , 0 , left ) ; //back block
Vector3i c ( center , 0 , center ) ; //center block
m_WayLength [ c . x ] [ c . z ] = 0 ;
Vector3i Nearest [ ] = { r , l , f , b } ;
unsigned char Sides [ ] = { E_SIDE_RIGHT , E_SIDE_LEFT , E_SIDE_FRONT , E_SIDE_BACK } ;
for ( int i = 0 ; i < 4 ; i + + )
{
char Block1 = m_World - > GetBlock ( LowerPoints [ i ] . x , LowerPoints [ i ] . y , LowerPoints [ i ] . z ) ;
char Block2 = m_World - > GetBlock ( LowerPoints [ i ] . x , a_Y , LowerPoints [ i ] . z ) ;
if ( Block1 = = E_BLOCK_AIR & & Block2 = = E_BLOCK_AIR )
Vector3i cur = Nearest [ i ] ;
switch ( m_Relief [ cur . x ] [ cur . z ] )
{
Points . push_back ( LowerPoints [ i ] ) ;
LowerPoints [ i ] . y = a_Y ;
Points . push_back ( LowerPoints [ i ] ) ;
}
else if ( m_Simulator - > IsAllowedBlock ( Block2 ) & & m_Simulator - > IsPassableForFluid ( Block1 ) )
{
bFluidFound = true ;
case E_HOLE :
{
m_StartSide [ cur . x ] [ cur . z ] = Sides [ i ] ;
m_CurResult | = m_StartSide [ cur . x ] [ cur . z ] ;
m_NearestHole = 1 ;
LOG_FLUID ( " Hole found: %d \t curResult: %d " , int ( Sides [ i ] ) , int ( m_CurResult ) ) ;
} break ;
case E_BLOCK :
{ } break ;
case E_PLAIN :
{
m_WaveQueue . push ( cur ) ;
m_StartSide [ cur . x ] [ cur . z ] = Sides [ i ] ;
m_WayLength [ cur . x ] [ cur . z ] = 1 ;
LOG_FLUID ( " Plain found: %d " , int ( Sides [ i ] ) ) ;
}
}
}
if ( Points . size ( ) = = 0 & & ! bFluidFound )
Vector3i curBlock ;
bool bContinue = ! m_WaveQueue . empty ( ) ;
if ( ! m_WaveQueue . empty ( ) )
{
curBlock = m_WaveQueue . front ( ) ;
bContinue = ( m_WayLength [ curBlock . x ] [ curBlock . z ] < m_NearestHole ) ;
}
while ( bContinue )
{
LOG_FLUID ( " while iteration " ) ;
curBlock = m_WaveQueue . front ( ) ;
UpdateWave ( CornerGlobal , curBlock ) ;
m_WaveQueue . pop ( ) ;
bContinue = ( ( ! m_WaveQueue . empty ( ) ) & & ( m_WayLength [ m_WaveQueue . front ( ) . x ] [ m_WaveQueue . front ( ) . z ] < m_NearestHole ) ) ;
}
if ( m_CurResult & E_SIDE_LEFT ) Points . push_back ( Vector3i ( a_X - 1 , a_Y , a_Z ) ) ;
if ( m_CurResult & E_SIDE_RIGHT ) Points . push_back ( Vector3i ( a_X + 1 , a_Y , a_Z ) ) ;
if ( m_CurResult & E_SIDE_FRONT ) Points . push_back ( Vector3i ( a_X , a_Y , a_Z + 1 ) ) ;
if ( m_CurResult & E_SIDE_BACK ) Points . push_back ( Vector3i ( a_X , a_Y , a_Z - 1 ) ) ;
if ( Points . empty ( ) )
{
Vector3i LevelPoints [ ] = {
Vector3i ( a_X - 1 , a_Y , a_Z ) ,
@ -72,13 +197,35 @@ public:
Points . push_back ( LevelPoints [ i ] ) ;
}
}
return Points ;
}
std : : li st< Vector3i > * m_ActiveFluid ;
std : : li st< Vector3i > * m_Buffer ;
std : : se t< Vector3i > * m_ActiveFluid ;
std : : se t< Vector3i > * m_Buffer ;
cWorld * m_World ;
cFluidSimulator * m_Simulator ;
const static int AREA_WIDTH = 11 ;
const static unsigned char E_SIDE_RIGHT = 0x10 ;
const static unsigned char E_SIDE_LEFT = 0x20 ;
const static unsigned char E_SIDE_FRONT = 0x40 ;
const static unsigned char E_SIDE_BACK = 0x80 ;
const static unsigned char E_SIDE_NONE = 0x00 ;
enum eRelief { E_HOLE = 0 , E_PLAIN = 1 , E_BLOCK = 2 } ;
eRelief m_Relief [ AREA_WIDTH ] [ AREA_WIDTH ] ;
unsigned char m_WayLength [ AREA_WIDTH ] [ AREA_WIDTH ] ;
unsigned char m_StartSide [ AREA_WIDTH ] [ AREA_WIDTH ] ;
std : : queue < Vector3i > m_WaveQueue ;
int m_NearestHole ;
unsigned char m_CurResult ;
} ;
cFluidSimulator : : cFluidSimulator ( cWorld * a_World )
@ -98,28 +245,20 @@ void cFluidSimulator::AddBlock( int a_X, int a_Y, int a_Z )
if ( ! IsAllowedBlock ( m_World - > GetBlock ( a_X , a_Y , a_Z ) ) ) //This should save very much time because it doesn´ t have to iterate through all blocks
return ;
// Check for duplicates
std : : list < Vector3i > & ActiveFluid = * m_Data - > m_ActiveFluid ;
for ( std : : list < Vector3i > : : iterator itr = ActiveFluid . begin ( ) ; itr ! = ActiveFluid . end ( ) ; + + itr )
{
Vector3i & Pos = * itr ;
if ( Pos . x = = a_X & & Pos . y = = a_Y & & Pos . z = = a_Z )
return ;
}
ActiveFluid . push_back ( Vector3i ( a_X , a_Y , a_Z ) ) ;
std : : set < Vector3i > & ActiveFluid = * m_Data - > m_ActiveFluid ;
ActiveFluid . insert ( Vector3i ( a_X , a_Y , a_Z ) ) ;
}
char cFluidSimulator : : GetHighestLevelAround ( int a_X , int a_Y , int a_Z )
{
char Max = m_MaxHeight + 1 ;
char Max = m_MaxHeight + m_FlowReduction ;
# define __HIGHLEVEL_CHECK__( x, y, z ) \
if ( IsAllowedBlock ( m_World - > GetBlock ( x , y , z ) ) ) \
{ \
char Meta ; \
if ( ( Meta = m_World - > GetBlockMeta ( x , y , z ) ) < Max ) Max = Meta ; \
else if ( Meta = = m_MaxHeight + 1 ) Max = 0 ; \
else if ( Meta = = m_MaxHeight + m_FlowReduction ) Max = 0 ; \
if ( Max = = 0 ) return 0 ; \
}
@ -141,18 +280,24 @@ void cFluidSimulator::Simulate( float a_Dt )
std : : swap ( m_Data - > m_ActiveFluid , m_Data - > m_Buffer ) ; // Swap so blocks can be added to empty ActiveFluid array
m_Data - > m_ActiveFluid - > clear ( ) ;
std : : li st< Vector3i > & FluidBlocks = * m_Data - > m_Buffer ;
for ( std : : li st< Vector3i > : : iterator itr = FluidBlocks . begin ( ) ; itr ! = FluidBlocks . end ( ) ; + + itr )
std : : se t< Vector3i > & FluidBlocks = * m_Data - > m_Buffer ;
for ( std : : se t< Vector3i > : : iterator itr = FluidBlocks . begin ( ) ; itr ! = FluidBlocks . end ( ) ; + + itr )
{
const Vector3i & pos = * itr ;
if ( UniqueSituation ( pos ) )
{
continue ;
}
Vector3i & pos = * itr ;
char BlockID = m_World - > GetBlock ( pos . x , pos . y , pos . z ) ;
if ( IsAllowedBlock ( BlockID ) ) // only care about own fluid
{
bool bIsFed = false ;
char Meta = m_World - > GetBlockMeta ( pos . x , pos . y , pos . z ) ;
char Feed = Meta ;
if ( Meta & 8 ) // Falling fluid
if ( BlockID = = m_StationaryFluidBlock ) Meta = 0 ;
if ( Meta = = 8 ) // Falling fluid
{
if ( IsAllowedBlock ( m_World - > GetBlock ( pos . x , pos . y + 1 , pos . z ) ) ) // Block above is fluid
{
@ -175,7 +320,7 @@ void cFluidSimulator::Simulate( float a_Dt )
{
char DownID = m_World - > GetBlock ( pos . x , pos . y - 1 , pos . z ) ;
bool bWashedAwayItem = CanWashAway ( DownID ) ;
if ( IsPassableForFluid ( DownID ) | | bWashedAwayItem ) // free for fluid
if ( ( IsPassableForFluid ( DownID ) | | bWashedAwayItem ) & & ! IsStationaryBlock ( DownID ) ) // free for fluid
{
if ( bWashedAwayItem )
{
@ -185,15 +330,17 @@ void cFluidSimulator::Simulate( float a_Dt )
m_World - > FastSetBlock ( pos . x , pos . y - 1 , pos . z , m_FluidBlock , 8 ) ; // falling
AddBlock ( pos . x , pos . y - 1 , pos . z ) ;
ApplyUniqueToNearest ( pos - Vector3i ( 0 , 1 , 0 ) ) ;
}
else // Not falling
if( IsSolidBlock ( DownID ) | | ( BlockID = = m_StationaryFluidBlock ) ) // Not falling
{
if ( Feed + m_FlowReduction < Meta )
{
m_World - > FastSetBlock ( pos . x , pos . y , pos . z , m_FluidBlock , Feed + m_FlowReduction ) ;
AddBlock ( pos . x , pos . y , pos . z ) ;
ApplyUniqueToNearest ( pos ) ;
}
else if ( Meta < m_MaxHeight ) // max is the lowest, so it cannot spread
else if ( ( Meta < m_MaxHeight ) | | ( BlockID = = m_StationaryFluidBlock ) ) // max is the lowest, so it cannot spread
{
std : : vector < Vector3i > Points = m_Data - > GetLowestPoints ( pos . x , pos . y , pos . z ) ;
for ( std : : vector < Vector3i > : : iterator itr = Points . begin ( ) ; itr ! = Points . end ( ) ; + + itr )
@ -201,6 +348,9 @@ void cFluidSimulator::Simulate( float a_Dt )
Vector3i & p = * itr ;
char BlockID = m_World - > GetBlock ( p . x , p . y , p . z ) ;
bool bWashedAwayItem = CanWashAway ( BlockID ) ;
if ( ! IsPassableForFluid ( BlockID ) ) continue ;
if ( ! IsAllowedBlock ( BlockID ) )
{
if ( bWashedAwayItem )
@ -210,10 +360,15 @@ void cFluidSimulator::Simulate( float a_Dt )
}
if ( p . y = = pos . y )
{
m_World - > FastSetBlock ( p . x , p . y , p . z , m_FluidBlock , Meta + m_FlowReduction ) ;
}
else
{
m_World - > FastSetBlock ( p . x , p . y , p . z , m_FluidBlock , 8 ) ;
}
AddBlock ( p . x , p . y , p . z ) ;
ApplyUniqueToNearest ( p ) ;
}
else // it's fluid
{
@ -221,13 +376,14 @@ void cFluidSimulator::Simulate( float a_Dt )
if ( PointMeta > Meta + m_FlowReduction )
{
AddBlock ( p . x , p . y , p . z ) ;
ApplyUniqueToNearest ( p ) ;
}
}
}
}
}
}
else // not fed
else // not fed
{
m_World - > FastSetBlock ( pos . x , pos . y , pos . z , E_BLOCK_AIR , 0 ) ;
WakeUp ( pos . x , pos . y , pos . z ) ;
@ -245,6 +401,11 @@ bool cFluidSimulator::IsPassableForFluid(char a_BlockID)
| | CanWashAway ( a_BlockID ) ;
}
bool cFluidSimulator : : IsStationaryBlock ( char a_BlockID )
{
return a_BlockID = = m_StationaryFluidBlock ;
}
bool cFluidSimulator : : CanWashAway ( char a_BlockID )
{
switch ( a_BlockID )
@ -260,6 +421,15 @@ bool cFluidSimulator::CanWashAway( char a_BlockID )
} ;
}
bool cFluidSimulator : : IsSolidBlock ( char a_BlockID )
{
return ! ( a_BlockID = = E_BLOCK_AIR
| | a_BlockID = = E_BLOCK_FIRE
| | IsBlockLava ( a_BlockID )
| | IsBlockWater ( a_BlockID )
| | CanWashAway ( a_BlockID ) ) ;
}
//TODO Not working very well yet :s
Direction cFluidSimulator : : GetFlowingDirection ( int a_X , int a_Y , int a_Z , bool a_Over )
{
@ -346,4 +516,109 @@ Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a
return NONE ;
}
bool cFluidSimulator : : UniqueSituation ( Vector3i a_Pos )
{
bool result = false ;
char BlockId = m_World - > GetBlock ( a_Pos . x , a_Pos . y , a_Pos . z ) ;
char Meta = m_World - > GetBlockMeta ( a_Pos . x , a_Pos . y , a_Pos . z ) ;
if ( IsBlockWater ( BlockId ) )
{
char UpperBlock = m_World - > GetBlock ( a_Pos . x , a_Pos . y + 1 , a_Pos . z ) ;
if ( IsBlockLava ( UpperBlock ) )
{
m_World - > SetBlock ( a_Pos . x , a_Pos . y , a_Pos . z , E_BLOCK_STONE , 0 ) ;
}
if ( BlockId ! = E_BLOCK_STATIONARY_WATER )
{
char DownBlockId = m_World - > GetBlock ( a_Pos . x , a_Pos . y - 1 , a_Pos . z ) ;
if ( IsSolidBlock ( DownBlockId ) )
{
Vector3i LevelPoints [ ] = {
Vector3i ( a_Pos . x - 1 , a_Pos . y , a_Pos . z ) ,
Vector3i ( a_Pos . x + 1 , a_Pos . y , a_Pos . z ) ,
Vector3i ( a_Pos . x , a_Pos . y , a_Pos . z - 1 ) ,
Vector3i ( a_Pos . x , a_Pos . y , a_Pos . z + 1 ) ,
} ;
int SourceBlocksCount = 0 ;
for ( int i = 0 ; i < 4 ; i + + )
{
if ( m_World - > GetBlock ( LevelPoints [ i ] . x , LevelPoints [ i ] . y , LevelPoints [ i ] . z ) = = E_BLOCK_STATIONARY_WATER )
{
SourceBlocksCount + + ;
}
}
if ( SourceBlocksCount > = 2 )
{
m_World - > SetBlock ( a_Pos . x , a_Pos . y , a_Pos . z , E_BLOCK_STATIONARY_WATER , 0 ) ;
}
}
}
}
if ( IsBlockLava ( BlockId ) )
{
bool bWater = false ;
char UpperBlock = m_World - > GetBlock ( a_Pos . x , a_Pos . y + 1 , a_Pos . z ) ;
if ( IsBlockWater ( UpperBlock ) )
{
bWater = true ;
}
else
{
Vector3i LevelPoints [ ] = {
Vector3i ( a_Pos . x - 1 , a_Pos . y , a_Pos . z ) ,
Vector3i ( a_Pos . x + 1 , a_Pos . y , a_Pos . z ) ,
Vector3i ( a_Pos . x , a_Pos . y , a_Pos . z - 1 ) ,
Vector3i ( a_Pos . x , a_Pos . y , a_Pos . z + 1 ) ,
} ;
for ( int i = 0 ; i < 4 ; i + + )
{
if ( IsBlockWater ( m_World - > GetBlock ( LevelPoints [ i ] . x , LevelPoints [ i ] . y , LevelPoints [ i ] . z ) ) )
{
bWater = true ;
}
}
}
if ( bWater )
{
if ( BlockId = = E_BLOCK_STATIONARY_LAVA )
{
m_World - > SetBlock ( a_Pos . x , a_Pos . y , a_Pos . z , E_BLOCK_OBSIDIAN , 0 ) ;
}
else if ( Meta < m_MaxHeight )
{
m_World - > SetBlock ( a_Pos . x , a_Pos . y , a_Pos . z , E_BLOCK_COBBLESTONE , 0 ) ;
}
}
}
return result ;
}
void cFluidSimulator : : ApplyUniqueToNearest ( Vector3i a_Pos )
{
Vector3i NearPoints [ ] = {
Vector3i ( a_Pos . x - 1 , a_Pos . y , a_Pos . z ) ,
Vector3i ( a_Pos . x + 1 , a_Pos . y , a_Pos . z ) ,
Vector3i ( a_Pos . x , a_Pos . y , a_Pos . z - 1 ) ,
Vector3i ( a_Pos . x , a_Pos . y , a_Pos . z + 1 ) ,
Vector3i ( a_Pos . x , a_Pos . y - 1 , a_Pos . z )
} ;
for ( int i = 0 ; i < 5 ; i + + )
{
UniqueSituation ( NearPoints [ i ] ) ;
}
}