From d6953a7ea2b20574ade4e0590ce030966c163394 Mon Sep 17 00:00:00 2001 From: "lapayo94@gmail.com" Date: Sun, 25 Dec 2011 16:20:06 +0000 Subject: [PATCH] Refactored both simulators (water and lava) to make them more flexible and easier to modify git-svn-id: http://mc-server.googlecode.com/svn/trunk@108 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- VC2010/MCServer.vcxproj | 2 + VC2010/MCServer.vcxproj.filters | 29 +-- source/cFluidSimulator.cpp | 304 +++++++++++++++++++++++++++++++ source/cFluidSimulator.h | 49 +++++ source/cLavaSimulator.cpp | 221 ++--------------------- source/cLavaSimulator.h | 23 +-- source/cWaterSimulator.cpp | 307 ++------------------------------ source/cWaterSimulator.h | 39 +--- 8 files changed, 409 insertions(+), 565 deletions(-) create mode 100644 source/cFluidSimulator.cpp create mode 100644 source/cFluidSimulator.h diff --git a/VC2010/MCServer.vcxproj b/VC2010/MCServer.vcxproj index a7728ea4f..2494a81f7 100644 --- a/VC2010/MCServer.vcxproj +++ b/VC2010/MCServer.vcxproj @@ -229,6 +229,7 @@ + @@ -366,6 +367,7 @@ + diff --git a/VC2010/MCServer.vcxproj.filters b/VC2010/MCServer.vcxproj.filters index a6e3af736..f3b970fc6 100644 --- a/VC2010/MCServer.vcxproj.filters +++ b/VC2010/MCServer.vcxproj.filters @@ -322,9 +322,6 @@ {dee92edf-fe18-479a-9108-b11353e14913} - - {02fd457d-3735-4f4b-aaf2-f7701a7ee7bc} - {82fda897-4c61-4ada-9160-8ae3982a72f8} @@ -382,9 +379,6 @@ {4f551776-009b-4f05-8ab8-748b82279a67} - - {b0401fd9-4021-4ab7-bf61-c8de112b4196} - {a71ee043-df55-4d42-a9aa-1643b40e1c22} @@ -403,6 +397,15 @@ {0d6f822b-71eb-406f-b17a-d188c4924283} + + {6f92474b-e2a5-4685-abf1-053b09dbc4f1} + + + {02fd457d-3735-4f4b-aaf2-f7701a7ee7bc} + + + {b0401fd9-4021-4ab7-bf61-c8de112b4196} + @@ -725,7 +728,7 @@ Packets\cPacket_EntityStatus - cWaterSimulator + cFluidSimulator\cWaterSimulator Threading\cSocket @@ -788,7 +791,7 @@ cPiston - cLavaSimulator + cFluidSimulator\cLavaSimulator LuaBindings\SquirrelBindings @@ -808,6 +811,9 @@ cChunkGenerator + + cFluidSimulator + @@ -1162,7 +1168,7 @@ Packets\cPacket_EntityStatus - cWaterSimulator + cFluidSimulator\cWaterSimulator Threading\cSocket @@ -1228,7 +1234,7 @@ cPiston - cLavaSimulator + cFluidSimulator\cLavaSimulator LuaBindings\SquirrelBindings @@ -1248,6 +1254,9 @@ cChunkGenerator + + cFluidSimulator + diff --git a/source/cFluidSimulator.cpp b/source/cFluidSimulator.cpp new file mode 100644 index 000000000..acbb19f56 --- /dev/null +++ b/source/cFluidSimulator.cpp @@ -0,0 +1,304 @@ +#include "cFluidSimulator.h" +#include "cWorld.h" +#include "Vector3i.h" +#include "BlockID.h" +#include "Defines.h" +#include + +class cFluidSimulator::FluidData +{ +public: + FluidData( cWorld* a_World, cFluidSimulator *a_Simulator ) + : m_ActiveFluid( new std::vector< Vector3i >() ) + , m_Simulator (a_Simulator) + , m_Buffer( new std::vector< Vector3i >() ) + , m_World( a_World ) + {} + + 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 ) + { + Points.push_back( Vector3i( a_X, a_Y-1, a_Z ) ); + return Points; + } + + 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 ) + { + 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 ) + { + 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; + } + } + + if( Points.size() == 0 && !bFluidFound ) + { + Vector3i LevelPoints [] = { + Vector3i( a_X-1, a_Y, a_Z ), + Vector3i( a_X+1, a_Y, a_Z ), + Vector3i( a_X, a_Y, a_Z-1 ), + Vector3i( a_X, a_Y, a_Z+1 ), + }; + for( int i = 0; i < 4; ++i ) + { + char Block = m_World->GetBlock( LevelPoints[i].x, a_Y, LevelPoints[i].z ); + if( m_Simulator->IsPassableForFluid(Block) ) + Points.push_back( LevelPoints[i] ); + } + } + return Points; + } + + std::vector< Vector3i >* m_ActiveFluid; + std::vector< Vector3i >* m_Buffer; + cWorld* m_World; + cFluidSimulator *m_Simulator; +}; + +cFluidSimulator::cFluidSimulator( cWorld* a_World ) + : m_World(a_World) + , m_Data(new FluidData(a_World, this)) +{ +} + +cFluidSimulator::~cFluidSimulator() +{ +} + +void cFluidSimulator::WakeUp( int a_X, int a_Y, int a_Z ) +{ + AddBlock( a_X, a_Y, a_Z ); + AddBlock( a_X-1, a_Y, a_Z ); + AddBlock( a_X+1, a_Y, a_Z ); + AddBlock( a_X, a_Y-1, a_Z ); + AddBlock( a_X, a_Y+1, a_Z ); + AddBlock( a_X, a_Y, a_Z-1 ); + AddBlock( a_X, a_Y, a_Z+1 ); +} + +void cFluidSimulator::AddBlock( int a_X, int a_Y, int a_Z ) +{ + // Check for duplicates + std::vector< Vector3i > & ActiveFluid = *m_Data->m_ActiveFluid; + for( std::vector< 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 ) ); +} + +char cFluidSimulator::GetHighestLevelAround( int a_X, int a_Y, int a_Z ) +{ + char Max = m_MaxHeight + 1; + //TODO Remove define function +#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; \ + if( Max == 0 ) return 0; \ + } + + __HIGHLEVEL_CHECK__( a_X-1, a_Y, a_Z ); + __HIGHLEVEL_CHECK__( a_X+1, a_Y, a_Z ); + __HIGHLEVEL_CHECK__( a_X, a_Y, a_Z-1 ); + __HIGHLEVEL_CHECK__( a_X, a_Y, a_Z+1 ); + + return Max; +} + +void cFluidSimulator::Simulate( float a_Dt ) +{ + m_Timer += a_Dt; + + if(m_Data->m_ActiveFluid->empty()) //Nothing to do if there is no active fluid ;) saves very little time ;D + return; + + 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::vector< Vector3i > & FluidBlocks = *m_Data->m_Buffer; + for( std::vector< Vector3i >::iterator itr = FluidBlocks.begin(); itr != FluidBlocks.end(); ++itr ) + { + 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( IsAllowedBlock( m_World->GetBlock(pos.x, pos.y+1, pos.z) ) ) // Block above is fluid + { + bIsFed = true; + Meta = 0; // Make it a full block + } + } + else if( Meta < m_FlowReduction ) // It's a full block, so it's always fed + { + bIsFed = true; + } + else + { + if( (Feed = GetHighestLevelAround( pos.x, pos.y, pos.z )) < Meta ) + bIsFed = true; + } + + + if( bIsFed ) + { + char DownID = m_World->GetBlock( pos.x, pos.y-1, pos.z ); + if( IsPassableForFluid(DownID) ) // free for fluid + { + m_World->FastSetBlock( pos.x, pos.y-1, pos.z, m_FluidBlock, 8 ); // falling + AddBlock( pos.x, pos.y-1, pos.z ); + } + else // 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 ); + } + else if( Meta < m_MaxHeight ) // 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 ) + { + Vector3i & p = *itr; + char BlockID = m_World->GetBlock( p.x, p.y, p.z ); + if( !IsAllowedBlock( BlockID ) ) + { + 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 ); + } + else // it's fluid + { + char PointMeta = m_World->GetBlockMeta( p.x, p.y, p.z ); + if( PointMeta > Meta + m_FlowReduction ) + { + AddBlock( p.x, p.y, p.z ); + } + } + } + } + } + } + else // not fed + { + m_World->FastSetBlock( pos.x, pos.y, pos.z, E_BLOCK_AIR, 0 ); + WakeUp( pos.x, pos.y, pos.z ); + } + } + } +} + +//TODO Not working very well yet :s +Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over) +{ + char BlockID = m_World->GetBlock(a_X, a_Y, a_Z); + if(!IsAllowedBlock(BlockID)) //No Fluid -> No Flowing direction :D + return NONE; + + + /* + Disabled because of causing problems and beeing useless atm + char BlockBelow = m_World->GetBlock(a_X, a_Y - 1, a_Z); //If there is nothing or fluid below it -> dominating flow is down :D + if(BlockBelow == E_BLOCK_AIR || IsAllowedBlock(BlockBelow)) + return Y_MINUS; + */ + + char LowestPoint = m_World->GetBlockMeta(a_X, a_Y, a_Z); //Current Block Meta so only lower points will be counted + int X, Y, Z; //Lowest Pos will be stored here + + if(IsAllowedBlock(m_World->GetBlock(a_X, a_Y + 1, a_Z)) && a_Over) //check for upper block to flow because this also affects the flowing direction + { + return GetFlowingDirection(a_X, a_Y + 1, a_Z, false); + } + + std::vector< Vector3i * > Points; + + //add blocks around the checking pos + Points.push_back(new Vector3i(a_X - 1, a_Y, a_Z)); + Points.push_back(new Vector3i(a_X + 1, a_Y, a_Z)); + Points.push_back(new Vector3i(a_X, a_Y, a_Z + 1)); + Points.push_back(new Vector3i(a_X, a_Y, a_Z - 1)); + + for(std::vector::iterator it = Points.begin(); it < Points.end(); it++) + { + Vector3i *Pos = (*it); + char BlockID = m_World->GetBlock(Pos->x, Pos->y, Pos->z); + if(IsAllowedBlock(BlockID)) + { + char Meta = m_World->GetBlockMeta(Pos->x, Pos->y, Pos->z); + + if(Meta > LowestPoint) + { + LowestPoint = Meta; + X = Pos->x; + Y = Pos->y; + Z = Pos->z; + } + }else if(BlockID == E_BLOCK_AIR) + { + LowestPoint = 9; //This always dominates + X = Pos->x; + Y = Pos->y; + Z = Pos->z; + + } + delete Pos; + } + + + if(LowestPoint == m_World->GetBlockMeta(a_X, a_Y, a_Z)) + return NONE; + + if(a_X - X > 0) + { + return X_MINUS; + } + + if(a_X - X < 0) + { + return X_PLUS; + } + + if(a_Z - Z > 0) + { + return Z_MINUS; + } + + if(a_Z - Z < 0) + { + return Z_PLUS; + } + + return NONE; + + +} \ No newline at end of file diff --git a/source/cFluidSimulator.h b/source/cFluidSimulator.h new file mode 100644 index 000000000..758bfc022 --- /dev/null +++ b/source/cFluidSimulator.h @@ -0,0 +1,49 @@ +#pragma once + + +//TODO This definitly needs a better naming :D but how? +enum Direction +{ + X_PLUS, + X_MINUS, + Y_PLUS, + Y_MINUS, + Z_PLUS, + Z_MINUS, + NONE +}; + +class Vector3i; +class cWorld; +class cFluidSimulator +{ +public: + cFluidSimulator( cWorld* a_World ); + ~cFluidSimulator(); + + virtual void Simulate( float a_Dt ); + void WakeUp( int a_X, int a_Y, int a_Z ); + + //Gets the flowing direction. if a_Over is true also the block over the current block affects the direction (standard) + Direction GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over = true); + + virtual inline bool IsAllowedBlock( char a_BlockID ) = 0; + virtual inline bool IsPassableForFluid( char a_BlockID ) = 0; + +protected: + void AddBlock( int a_X, int a_Y, int a_Z); + char GetHighestLevelAround( int a_X, int a_Y, int a_Z ); + + + float m_Timer; + cWorld* m_World; + + class FluidData; + FluidData* m_Data; + + //Customize + char m_FluidBlock; + char m_MaxHeight; + char m_FlowReduction; + +}; \ No newline at end of file diff --git a/source/cLavaSimulator.cpp b/source/cLavaSimulator.cpp index e8dbb4126..35a498ee7 100644 --- a/source/cLavaSimulator.cpp +++ b/source/cLavaSimulator.cpp @@ -1,216 +1,23 @@ #include "cLavaSimulator.h" -#include "cWorld.h" -#include "Vector3i.h" -#include "BlockID.h" -#include +#include "Defines.h" -class cLavaSimulator::LavaData -{ -public: - LavaData( cWorld* a_World ) - : m_ActiveLava( new std::vector< Vector3i >() ) - , m_Buffer( new std::vector< Vector3i >() ) - , m_World( a_World ) - {} - - 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 ) - { - Points.push_back( Vector3i( a_X, a_Y-1, a_Z ) ); - return Points; - } - - 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 bLavaFound = false; - 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 ) - { - Points.push_back( LowerPoints[i] ); - LowerPoints[i].y = a_Y; - Points.push_back( LowerPoints[i] ); - } - else if( (Block2 == E_BLOCK_LAVA || Block2 == E_BLOCK_STATIONARY_LAVA ) && ( Block1 == E_BLOCK_AIR || Block1 == E_BLOCK_LAVA || Block1 == E_BLOCK_STATIONARY_LAVA ) ) - { - bLavaFound = true; - } - } - - if( Points.size() == 0 && !bLavaFound ) - { - Vector3i LevelPoints [] = { - Vector3i( a_X-1, a_Y, a_Z ), - Vector3i( a_X+1, a_Y, a_Z ), - Vector3i( a_X, a_Y, a_Z-1 ), - Vector3i( a_X, a_Y, a_Z+1 ), - }; - for( int i = 0; i < 4; ++i ) - { - char Block = m_World->GetBlock( LevelPoints[i].x, a_Y, LevelPoints[i].z ); - if( Block == E_BLOCK_AIR || Block == E_BLOCK_LAVA || Block == E_BLOCK_STATIONARY_LAVA ) - Points.push_back( LevelPoints[i] ); - } - } - return Points; - } - - std::vector< Vector3i >* m_ActiveLava; - std::vector< Vector3i >* m_Buffer; - cWorld* m_World; -}; - -cLavaSimulator::cLavaSimulator( cWorld* a_World ) - : m_World( a_World ) - , m_Data( new LavaData( a_World ) ) + +cLavaSimulator::cLavaSimulator(cWorld *a_World) + : cFluidSimulator(a_World) { + m_FluidBlock = E_BLOCK_LAVA; + m_MaxHeight = 6; + m_FlowReduction = 2; } -cLavaSimulator::~cLavaSimulator() + +bool cLavaSimulator::IsAllowedBlock(char a_BlockID) { + return IsBlockLava(a_BlockID); } -void cLavaSimulator::WakeUp( int a_X, int a_Y, int a_Z ) + +bool cLavaSimulator::IsPassableForFluid(char a_BlockID) { - AddBlock( a_X, a_Y, a_Z ); - AddBlock( a_X-1, a_Y, a_Z ); - AddBlock( a_X+1, a_Y, a_Z ); - AddBlock( a_X, a_Y-1, a_Z ); - AddBlock( a_X, a_Y+1, a_Z ); - AddBlock( a_X, a_Y, a_Z-1 ); - AddBlock( a_X, a_Y, a_Z+1 ); -} - -void cLavaSimulator::AddBlock( int a_X, int a_Y, int a_Z ) -{ - // Check for duplicates - std::vector< Vector3i > & ActiveLava = *m_Data->m_ActiveLava; - for( std::vector< Vector3i >::iterator itr = ActiveLava.begin(); itr != ActiveLava.end(); ++itr ) - { - Vector3i & pos = *itr; - if( pos.x == a_X && pos.y == a_Y && pos.z == a_Z ) - return; - } - - ActiveLava.push_back( Vector3i( a_X, a_Y, a_Z ) ); -} - -char cLavaSimulator::GetHighestLevelAround( int a_X, int a_Y, int a_Z ) -{ - char Max = 8; -#define __HIGHLEVEL_CHECK__( x, y, z ) \ - if( IsLavaBlock( m_World->GetBlock( x, y, z ) ) ) \ - { \ - char Meta; \ - if( (Meta = m_World->GetBlockMeta( x, y, z ) ) < Max ) Max = Meta; \ - else if( Meta == 8 ) Max = 0; \ - if( Max == 0 ) return 0; \ - } - - __HIGHLEVEL_CHECK__( a_X-1, a_Y, a_Z ); - __HIGHLEVEL_CHECK__( a_X+1, a_Y, a_Z ); - __HIGHLEVEL_CHECK__( a_X, a_Y, a_Z-1 ); - __HIGHLEVEL_CHECK__( a_X, a_Y, a_Z+1 ); - - return Max; -} - -void cLavaSimulator::Simulate( float a_Dt ) -{ - m_Timer += a_Dt; - - std::swap( m_Data->m_ActiveLava, m_Data->m_Buffer ); // Swap so blocks can be added to empty ActiveLava array - m_Data->m_ActiveLava->clear(); - - std::vector< Vector3i > & LavaBlocks = *m_Data->m_Buffer; - for( std::vector< Vector3i >::iterator itr = LavaBlocks.begin(); itr != LavaBlocks.end(); ++itr ) - { - Vector3i & pos = *itr; - char BlockID = m_World->GetBlock( pos.x, pos.y, pos.z ); - if( IsLavaBlock( BlockID ) ) // only care about lava - { - bool bIsFed = false; - char Meta = m_World->GetBlockMeta( pos.x, pos.y, pos.z ); - char Feed = Meta; - if( Meta == 8 ) // Falling lava - { - if( IsLavaBlock( m_World->GetBlock(pos.x, pos.y+1, pos.z) ) ) // Block above is lava - { - bIsFed = true; - Meta = 0; // Make it a full block - } - } - else if( Meta <= 2 ) - { - bIsFed = true; - } - else - { - if( (Feed = GetHighestLevelAround( pos.x, pos.y, pos.z )) < Meta ) - bIsFed = true; - } - - - if( bIsFed ) - { - char DownID = m_World->GetBlock( pos.x, pos.y-1, pos.z ); - if( DownID == E_BLOCK_AIR || IsLavaBlock( DownID ) ) // free for Lava - { - m_World->FastSetBlock( pos.x, pos.y-1, pos.z, E_BLOCK_LAVA, 8 ); // falling - AddBlock( pos.x, pos.y-1, pos.z ); - } - else // Not falling Lava - { - if( Feed+2 < Meta ) - { - m_World->FastSetBlock( pos.x, pos.y, pos.z, E_BLOCK_LAVA, Feed+2 ); - AddBlock( pos.x, pos.y, pos.z ); - } - else if( Meta < 6 ) // 7 is only 1 unit high, so it cannot spread, lower than 7 can though. - { - 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 ) - { - Vector3i & p = *itr; - char BlockID = m_World->GetBlock( p.x, p.y, p.z ); - if( !IsLavaBlock( BlockID ) ) - { - if( p.y == pos.y ) - m_World->FastSetBlock(p.x, p.y, p.z, E_BLOCK_LAVA, Meta+2); - else - m_World->FastSetBlock(p.x, p.y, p.z, E_BLOCK_LAVA, 8); - AddBlock( p.x, p.y, p.z ); - } - else // it's Lava - { - char PointMeta = m_World->GetBlockMeta( p.x, p.y, p.z ); - if( PointMeta > Meta+2 ) - { - AddBlock( p.x, p.y, p.z ); - } - } - } - } - } - } - else // not fed - { - m_World->FastSetBlock( pos.x, pos.y, pos.z, E_BLOCK_AIR, 0 ); - WakeUp( pos.x, pos.y, pos.z ); - } - } - } -} - -bool cLavaSimulator::IsLavaBlock( char a_BlockID ) -{ - return a_BlockID == E_BLOCK_LAVA || a_BlockID == E_BLOCK_STATIONARY_LAVA; -} + return ( a_BlockID == E_BLOCK_AIR || IsAllowedBlock(a_BlockID) ); +} \ No newline at end of file diff --git a/source/cLavaSimulator.h b/source/cLavaSimulator.h index f1c59b939..cb3c743be 100644 --- a/source/cLavaSimulator.h +++ b/source/cLavaSimulator.h @@ -1,25 +1,12 @@ #pragma once +#include "cFluidSimulator.h" -class Vector3i; -class cWorld; -class cLavaSimulator +class cLavaSimulator : public cFluidSimulator { public: cLavaSimulator( cWorld* a_World ); - ~cLavaSimulator(); - void Simulate( float a_Dt ); - void WakeUp( int a_X, int a_Y, int a_Z ); + virtual inline bool IsAllowedBlock( char a_BlockID ); + virtual inline bool IsPassableForFluid( char a_BlockID ); -private: - void AddBlock( int a_X, int a_Y, int a_Z); - char GetHighestLevelAround( int a_X, int a_Y, int a_Z ); - - bool IsLavaBlock( char a_BlockID ); - - float m_Timer; - cWorld* m_World; - - class LavaData; - LavaData* m_Data; -}; +}; \ No newline at end of file diff --git a/source/cWaterSimulator.cpp b/source/cWaterSimulator.cpp index 9b41e42b9..372328914 100644 --- a/source/cWaterSimulator.cpp +++ b/source/cWaterSimulator.cpp @@ -1,306 +1,23 @@ #include "cWaterSimulator.h" -#include "cWorld.h" -#include "Vector3i.h" -#include "BlockID.h" #include "Defines.h" -#include -class cWaterSimulator::WaterData -{ -public: - WaterData( cWorld* a_World ) - : m_ActiveWater( new std::vector< Vector3i >() ) - , m_Buffer( new std::vector< Vector3i >() ) - , m_World( a_World ) - {} - - 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 ) - { - Points.push_back( Vector3i( a_X, a_Y-1, a_Z ) ); - return Points; - } - - 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 bWaterFound = false; - 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 ) - { - Points.push_back( LowerPoints[i] ); - LowerPoints[i].y = a_Y; - Points.push_back( LowerPoints[i] ); - } - else if( IsBlockWater(Block2) && ( Block1 == E_BLOCK_AIR || IsBlockWater(Block1) ) ) - { - bWaterFound = true; - } - } - - if( Points.size() == 0 && !bWaterFound ) - { - Vector3i LevelPoints [] = { - Vector3i( a_X-1, a_Y, a_Z ), - Vector3i( a_X+1, a_Y, a_Z ), - Vector3i( a_X, a_Y, a_Z-1 ), - Vector3i( a_X, a_Y, a_Z+1 ), - }; - for( int i = 0; i < 4; ++i ) - { - char Block = m_World->GetBlock( LevelPoints[i].x, a_Y, LevelPoints[i].z ); - if( Block == E_BLOCK_AIR || Block == E_BLOCK_WATER || Block == E_BLOCK_STATIONARY_WATER ) - Points.push_back( LevelPoints[i] ); - } - } - return Points; - } - - std::vector< Vector3i >* m_ActiveWater; - std::vector< Vector3i >* m_Buffer; - cWorld* m_World; -}; - -cWaterSimulator::cWaterSimulator( cWorld* a_World ) - : m_World( a_World ) - , m_Data( new WaterData( a_World ) ) + +cWaterSimulator::cWaterSimulator(cWorld *a_World) + : cFluidSimulator(a_World) { + m_FluidBlock = E_BLOCK_WATER; + m_MaxHeight = 7; + m_FlowReduction = 1; } -cWaterSimulator::~cWaterSimulator() + +bool cWaterSimulator::IsAllowedBlock(char a_BlockID) { + return IsBlockWater(a_BlockID); } -void cWaterSimulator::WakeUp( int a_X, int a_Y, int a_Z ) + +bool cWaterSimulator::IsPassableForFluid(char a_BlockID) { - AddBlock( a_X, a_Y, a_Z ); - AddBlock( a_X-1, a_Y, a_Z ); - AddBlock( a_X+1, a_Y, a_Z ); - AddBlock( a_X, a_Y-1, a_Z ); - AddBlock( a_X, a_Y+1, a_Z ); - AddBlock( a_X, a_Y, a_Z-1 ); - AddBlock( a_X, a_Y, a_Z+1 ); -} - -void cWaterSimulator::AddBlock( int a_X, int a_Y, int a_Z ) -{ - // Check for duplicates - std::vector< Vector3i > & ActiveWater = *m_Data->m_ActiveWater; - for( std::vector< Vector3i >::iterator itr = ActiveWater.begin(); itr != ActiveWater.end(); ++itr ) - { - Vector3i & pos = *itr; - if( pos.x == a_X && pos.y == a_Y && pos.z == a_Z ) - return; - } - - ActiveWater.push_back( Vector3i( a_X, a_Y, a_Z ) ); -} - -char cWaterSimulator::GetHighestLevelAround( int a_X, int a_Y, int a_Z ) -{ - char Max = 8; -#define __HIGHLEVEL_CHECK__( x, y, z ) \ - if( IsWaterBlock( m_World->GetBlock( x, y, z ) ) ) \ - { \ - char Meta; \ - if( (Meta = m_World->GetBlockMeta( x, y, z ) ) < Max ) Max = Meta; \ - else if( Meta == 8 ) Max = 0; \ - if( Max == 0 ) return 0; \ - } - - __HIGHLEVEL_CHECK__( a_X-1, a_Y, a_Z ); - __HIGHLEVEL_CHECK__( a_X+1, a_Y, a_Z ); - __HIGHLEVEL_CHECK__( a_X, a_Y, a_Z-1 ); - __HIGHLEVEL_CHECK__( a_X, a_Y, a_Z+1 ); - - return Max; -} - -void cWaterSimulator::Simulate( float a_Dt ) -{ - m_Timer += a_Dt; - - if(m_Data->m_ActiveWater->empty()) //Nothing to do if there is no ActiveWater ;) saves very little time ;D - return; - - std::swap( m_Data->m_ActiveWater, m_Data->m_Buffer ); // Swap so blocks can be added to empty ActiveWater array - m_Data->m_ActiveWater->clear(); - - std::vector< Vector3i > & WaterBlocks = *m_Data->m_Buffer; - for( std::vector< Vector3i >::iterator itr = WaterBlocks.begin(); itr != WaterBlocks.end(); ++itr ) - { - Vector3i & pos = *itr; - char BlockID = m_World->GetBlock( pos.x, pos.y, pos.z ); - if( IsWaterBlock( BlockID ) ) // only care about water - { - bool bIsFed = false; - char Meta = m_World->GetBlockMeta( pos.x, pos.y, pos.z ); - char Feed = Meta; - if( Meta == 8 ) // Falling water - { - if( IsWaterBlock( m_World->GetBlock(pos.x, pos.y+1, pos.z) ) ) // Block above is water - { - bIsFed = true; - Meta = 0; // Make it a full block - } - } - else if( Meta == 0 ) // It's a full block, so it's always fed - { - bIsFed = true; - } - else - { - if( (Feed = GetHighestLevelAround( pos.x, pos.y, pos.z )) < Meta ) - bIsFed = true; - } - - - if( bIsFed ) - { - char DownID = m_World->GetBlock( pos.x, pos.y-1, pos.z ); - if( DownID == E_BLOCK_AIR || IsWaterBlock( DownID ) ) // free for water - { - m_World->FastSetBlock( pos.x, pos.y-1, pos.z, E_BLOCK_WATER, 8 ); // falling - AddBlock( pos.x, pos.y-1, pos.z ); - } - else // Not falling water - { - if( Feed+1 < Meta ) - { - m_World->FastSetBlock( pos.x, pos.y, pos.z, E_BLOCK_WATER, Feed+1 ); - AddBlock( pos.x, pos.y, pos.z ); - } - else if( Meta < 7 ) // 7 is only 1 unit high, so it cannot spread, lower than 7 can though. - { - 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 ) - { - Vector3i & p = *itr; - char BlockID = m_World->GetBlock( p.x, p.y, p.z ); - if( !IsWaterBlock( BlockID ) ) - { - if( p.y == pos.y ) - m_World->FastSetBlock(p.x, p.y, p.z, E_BLOCK_WATER, Meta+1); - else - m_World->FastSetBlock(p.x, p.y, p.z, E_BLOCK_WATER, 8); - AddBlock( p.x, p.y, p.z ); - } - else // it's water - { - char PointMeta = m_World->GetBlockMeta( p.x, p.y, p.z ); - if( PointMeta > Meta+1 ) - { - AddBlock( p.x, p.y, p.z ); - } - } - } - } - } - } - else // not fed - { - m_World->FastSetBlock( pos.x, pos.y, pos.z, E_BLOCK_AIR, 0 ); - WakeUp( pos.x, pos.y, pos.z ); - } - } - } -} - -bool cWaterSimulator::IsWaterBlock( char a_BlockID ) -{ - return a_BlockID == E_BLOCK_WATER || a_BlockID == E_BLOCK_STATIONARY_WATER; -} - -//TODO Not working very well yet :s -Direction cWaterSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over) -{ - char BlockID = m_World->GetBlock(a_X, a_Y, a_Z); - if(!IsBlockWater(BlockID)) //No Water -> No Flowing direction :D - return NONE; - - - /* - Disabled because of causing problems and beeing useless atm - char BlockBelow = m_World->GetBlock(a_X, a_Y - 1, a_Z); //If there is nothing or water below it -> dominating flow is down :D - if(BlockBelow == E_BLOCK_AIR || IsBlockWater(BlockBelow)) - return Y_MINUS; - */ - - char LowestPoint = m_World->GetBlockMeta(a_X, a_Y, a_Z); //Current Block Meta so only lower points will be counted - int X, Y, Z; //Lowest Pos will be stored here - - if(IsBlockWater(m_World->GetBlock(a_X, a_Y + 1, a_Z)) && a_Over) //check for upper block to flow because this also affects the flowing direction - { - return GetFlowingDirection(a_X, a_Y + 1, a_Z, false); - } - - std::vector< Vector3i * > Points; - - //add blocks around the checking pos - Points.push_back(new Vector3i(a_X - 1, a_Y, a_Z)); - Points.push_back(new Vector3i(a_X + 1, a_Y, a_Z)); - Points.push_back(new Vector3i(a_X, a_Y, a_Z + 1)); - Points.push_back(new Vector3i(a_X, a_Y, a_Z - 1)); - - for(std::vector::iterator it = Points.begin(); it < Points.end(); it++) - { - Vector3i *Pos = (*it); - char BlockID = m_World->GetBlock(Pos->x, Pos->y, Pos->z); - if(IsBlockWater(BlockID)) - { - char Meta = m_World->GetBlockMeta(Pos->x, Pos->y, Pos->z); - - if(Meta > LowestPoint) - { - LowestPoint = Meta; - X = Pos->x; - Y = Pos->y; - Z = Pos->z; - } - }else if(BlockID == E_BLOCK_AIR) - { - LowestPoint = 9; //This always dominates - X = Pos->x; - Y = Pos->y; - Z = Pos->z; - - } - delete Pos; - } - - - if(LowestPoint == m_World->GetBlockMeta(a_X, a_Y, a_Z)) - return NONE; - - if(a_X - X > 0) - { - return X_MINUS; - } - - if(a_X - X < 0) - { - return X_PLUS; - } - - if(a_Z - Z > 0) - { - return Z_MINUS; - } - - if(a_Z - Z < 0) - { - return Z_PLUS; - } - - return NONE; - - + return ( a_BlockID == E_BLOCK_AIR || IsAllowedBlock(a_BlockID) ); } \ No newline at end of file diff --git a/source/cWaterSimulator.h b/source/cWaterSimulator.h index 5962cd83e..c4eab8d80 100644 --- a/source/cWaterSimulator.h +++ b/source/cWaterSimulator.h @@ -1,43 +1,12 @@ #pragma once +#include "cFluidSimulator.h" - -//TODO This definitly needs a better naming :D but how? -enum Direction -{ - X_PLUS, - X_MINUS, - Y_PLUS, - Y_MINUS, - Z_PLUS, - Z_MINUS, - NONE -}; - -class Vector3i; -class cWorld; -class cWaterSimulator +class cWaterSimulator : public cFluidSimulator { public: cWaterSimulator( cWorld* a_World ); - ~cWaterSimulator(); - void Simulate( float a_Dt ); - void WakeUp( int a_X, int a_Y, int a_Z ); + virtual inline bool IsAllowedBlock( char a_BlockID ); + virtual inline bool IsPassableForFluid( char a_BlockID ); - //Gets the flowing direction of a Waterblock if a_Over is true also the block over the current block affects the direction (standard) - Direction GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over = true); - -private: - void AddBlock( int a_X, int a_Y, int a_Z); - char GetHighestLevelAround( int a_X, int a_Y, int a_Z ); - - bool IsWaterBlock( char a_BlockID ); - - float m_Timer; - cWorld* m_World; - - class WaterData; - WaterData* m_Data; - - }; \ No newline at end of file