DragonNest/Common/EtWorldBase/EtMaze.cpp
2024-12-19 09:48:26 +08:00

539 lines
No EOL
15 KiB
C++

#include "StdAfx.h"
#include "EtMaze.h"
#include "EtMazeMask.h"
#include "EtRandomDungeon.h"
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#endif
CEtMaze::CEtMaze( int nWidth, int nHeight, int nLevel, int nSeed, CEtMazeMask *pMask )
{
m_Size.nX = nWidth;
m_Size.nY = nHeight;
m_Size.nZ = nLevel;
m_pMask = pMask;
/*
if( nSeed > 0 ) m_nSeed = nSeed;
else m_nSeed = rand()%LONG_MAX;
srand( m_nSeed );
*/
m_pMaze = new int **[nWidth];
for( int i=0; i<nWidth; i++ ) {
m_pMaze[i] = new int *[nHeight];
for( int j=0; j<nHeight; j++ ) {
m_pMaze[i][j] = new int[nLevel];
memset( m_pMaze[i][j], 0, nLevel * sizeof(int) );
}
}
}
CEtMaze::~CEtMaze()
{
for( int i=0; i<m_Size.nX; i++ ) {
for( int j=0; j<m_Size.nY; j++ ) {
SAFE_DELETEA( m_pMaze[i][j] );
}
SAFE_DELETEA( m_pMaze[i] );
}
SAFE_DELETEA( m_pMaze );
SAFE_DELETE_VEC( m_VecSolveList );
}
void CEtMaze::Generation( int nRandomness )
{
unsigned long nRemainCount = 0;
int nAllDir = MD_WEST | MD_EAST | MD_NORTH | MD_SOUTH | MD_UP | MD_DOWN;
int nAccumDir = MD_NONE;
int nCurDir = MD_NONE;
int nLastDir = MD_NONE;
bool bRandomSelect;
int nStraightStretch = 0;
for( int i=0; i<m_Size.nX; i++ ) {
for( int j=0; j<m_Size.nY; j++ ) {
for( int k=0; k<m_Size.nZ; k++ ) {
if( !m_pMask->IsCheckMask( i, j, k ) ) nRemainCount++;
}
}
}
// nRemainCount *= m_Size.nZ;
nRemainCount--;
IntVec3 Pos;
do {
Pos.nX = rand() % m_Size.nX;
Pos.nY = rand() % m_Size.nY;
Pos.nZ = rand() % m_Size.nZ;
} while( m_pMask->IsCheckMask( Pos.nX, Pos.nY, Pos.nZ ) );
int nUpDownCount = 10;
while( nRemainCount > 0 ) {
if( nAccumDir == nAllDir ) {
do {
Pos.nX = rand() % m_Size.nX;
Pos.nY = rand() % m_Size.nY;
Pos.nZ = rand() % m_Size.nZ;
} while( m_pMaze[Pos.nX][Pos.nY][Pos.nZ] == 0 );
nAccumDir = m_pMaze[Pos.nX][Pos.nY][Pos.nZ];
}
if( Pos.nX < 1 ) nAccumDir |= MD_WEST;
if( Pos.nX + 1 >= m_Size.nX ) nAccumDir |= MD_EAST;
if( Pos.nY < 1 ) nAccumDir |= MD_NORTH;
if( Pos.nY + 1 >= m_Size.nY ) nAccumDir |= MD_SOUTH;
if( Pos.nZ < 1 ) nAccumDir |= MD_DOWN;
if( Pos.nZ + 1 >= m_Size.nZ ) nAccumDir |= MD_UP;
bRandomSelect = false;
if( rand() % 100 < nRandomness ) bRandomSelect = true;
else {
switch( nLastDir ) {
case MD_NORTH:
if( ( nStraightStretch < ( m_Size.nY >> 1 ) ) && ( Pos.nY > 0 ) && ( m_pMaze[Pos.nX][Pos.nY-1][Pos.nZ] == 0 ) && ( !m_pMask->IsCheckMask( Pos.nX, Pos.nY-1, Pos.nZ ) ) )
nCurDir = nLastDir;
else bRandomSelect = true;
break;
case MD_SOUTH:
if( ( nStraightStretch < ( m_Size.nY >> 1 ) ) && ( Pos.nY+1 < m_Size.nY ) && ( m_pMaze[Pos.nX][Pos.nY+1][Pos.nZ] == 0 ) && ( !m_pMask->IsCheckMask( Pos.nX, Pos.nY+1, Pos.nZ ) ) )
nCurDir = nLastDir;
else bRandomSelect = true;
break;
case MD_WEST:
if( ( nStraightStretch < ( m_Size.nX >> 1 ) ) && ( Pos.nX > 0 ) && ( m_pMaze[Pos.nX-1][Pos.nY][Pos.nZ] == 0 ) && ( !m_pMask->IsCheckMask( Pos.nX-1, Pos.nY, Pos.nZ ) ) )
nCurDir = nLastDir;
else bRandomSelect = true;
break;
case MD_EAST:
if( ( nStraightStretch < ( m_Size.nX >> 1 ) ) && ( Pos.nX+1 < m_Size.nX ) && ( m_pMaze[Pos.nX+1][Pos.nY][Pos.nZ] == 0 ) && ( !m_pMask->IsCheckMask( Pos.nX+1, Pos.nY, Pos.nZ ) ) )
nCurDir = nLastDir;
else bRandomSelect = true;
break;
case MD_UP:
/*
if( ( nStraightStretch < ( m_Size.nZ >> 1 ) ) && ( Pos.nZ > 0 ) && ( m_pMaze[Pos.nX][Pos.nY][Pos.nZ-1] == 0 ) )
nCurDir = nLastDir;
else bRandomSelect = true;
*/
bRandomSelect = true;
break;
case MD_DOWN:
/*
if( ( nStraightStretch < ( m_Size.nZ >> 1 ) ) && ( Pos.nZ+1 < m_Size.nZ ) && ( m_pMaze[Pos.nX][Pos.nY][Pos.nZ+1] == 0 ) )
nCurDir = nLastDir;
else bRandomSelect = true;
*/
bRandomSelect = true;
break;
default:
bRandomSelect = true;
}
}
if( bRandomSelect ) {
nStraightStretch = 0;
nCurDir = 0;
while( ( nCurDir == MD_NONE ) || ( nAccumDir & nCurDir ) ) {
IntVec3 Temp;
Temp = Pos;
int nRandomSeed;
if( nLastDir == MD_UP || nLastDir == MD_DOWN ||
m_pMaze[Pos.nX][Pos.nY][Pos.nZ] & MD_UP || m_pMaze[Pos.nX][Pos.nY][Pos.nZ] & MD_DOWN ) {
nRandomSeed = rand()%4;
nAccumDir |= ( MD_UP | MD_DOWN );
}
else {
nRandomSeed = rand()%6;
}
switch( nRandomSeed ) {
case 0: if( Pos.nY > 0 ) { nCurDir = MD_NORTH; Temp.nY--; } else { nAccumDir |= MD_NORTH; } break;
case 1: if( Pos.nY+1 < m_Size.nY ) { nCurDir = MD_SOUTH; Temp.nY++; } else { nAccumDir |= MD_SOUTH; } break;
case 2: if( Pos.nX > 0 ) { nCurDir = MD_WEST; Temp.nX--; } else { nAccumDir |= MD_WEST; } break;
case 3: if( Pos.nX+1 < m_Size.nX ) { nCurDir = MD_EAST; Temp.nX++; } else { nAccumDir |= MD_EAST; } break;
case 4: if( Pos.nZ > 0 ) { nCurDir = MD_DOWN; Temp.nZ--; } else { nAccumDir |= MD_DOWN; } break;
case 5: if( Pos.nZ+1 < m_Size.nZ ) { nCurDir = MD_UP; Temp.nZ++; } else { nAccumDir |= MD_UP; } break;
}
if( ( m_pMask->IsCheckMask( Temp.nX, Temp.nY, Temp.nZ ) ) || ( m_pMaze[Temp.nX][Temp.nY][Temp.nZ] != 0 ) ) {
nAccumDir |= nCurDir;
if( nAccumDir == nAllDir ) break;
nCurDir = MD_NONE;
}
}
}
else nStraightStretch++;
if( nAccumDir == nAllDir ) continue;
nLastDir = nCurDir;
m_pMaze[Pos.nX][Pos.nY][Pos.nZ] |= nCurDir;
switch( nCurDir ) {
case MD_NORTH: Pos.nY--; nCurDir = MD_SOUTH; break;
case MD_SOUTH: Pos.nY++; nCurDir = MD_NORTH; break;
case MD_WEST: Pos.nX--; nCurDir = MD_EAST; break;
case MD_EAST: Pos.nX++; nCurDir = MD_WEST; break;
case MD_DOWN: Pos.nZ--; nCurDir = MD_UP; break;
case MD_UP: Pos.nZ++; nCurDir = MD_DOWN; break;
}
m_pMaze[Pos.nX][Pos.nY][Pos.nZ] |= nCurDir;
nAccumDir = m_pMaze[Pos.nX][Pos.nY][Pos.nZ];
nRemainCount--;
}
}
void CEtMaze::GenerationBySingleLevel( int nRandomness, int nLevel )
{
unsigned long nRemainCount = 0;
int nAllDir = MD_WEST | MD_EAST | MD_NORTH | MD_SOUTH;
int nAccumDir = MD_NONE;
int nCurDir = MD_NONE;
int nLastDir = MD_NONE;
bool bRandomSelect;
int nStraightStretch = 0;
for( int i=0; i<m_Size.nX; i++ ) {
for( int j=0; j<m_Size.nY; j++ ) {
if( !m_pMask->IsCheckMask( i, j, nLevel ) ) nRemainCount++;
}
}
nRemainCount--;
IntVec3 Pos;
do {
Pos.nX = rand() % m_Size.nX;
Pos.nY = rand() % m_Size.nY;
Pos.nZ = nLevel;
} while( m_pMask->IsCheckMask( Pos.nX, Pos.nY, nLevel ) );
int nUpDownCount = 10;
while( nRemainCount > 0 ) {
if( nAccumDir == nAllDir ) {
do {
Pos.nX = rand() % m_Size.nX;
Pos.nY = rand() % m_Size.nY;
Pos.nZ = nLevel;
} while( m_pMaze[Pos.nX][Pos.nY][Pos.nZ] == 0 );
nAccumDir = m_pMaze[Pos.nX][Pos.nY][Pos.nZ];
}
if( Pos.nX < 1 ) nAccumDir |= MD_WEST;
if( Pos.nX + 1 >= m_Size.nX ) nAccumDir |= MD_EAST;
if( Pos.nY < 1 ) nAccumDir |= MD_NORTH;
if( Pos.nY + 1 >= m_Size.nY ) nAccumDir |= MD_SOUTH;
bRandomSelect = false;
if( rand() % 100 < nRandomness ) bRandomSelect = true;
else {
switch( nLastDir ) {
case MD_NORTH:
if( ( nStraightStretch < ( m_Size.nY >> 1 ) ) && ( Pos.nY > 0 ) && ( m_pMaze[Pos.nX][Pos.nY-1][Pos.nZ] == 0 ) && ( !m_pMask->IsCheckMask( Pos.nX, Pos.nY-1, Pos.nZ ) ) )
nCurDir = nLastDir;
else bRandomSelect = true;
break;
case MD_SOUTH:
if( ( nStraightStretch < ( m_Size.nY >> 1 ) ) && ( Pos.nY+1 < m_Size.nY ) && ( m_pMaze[Pos.nX][Pos.nY+1][Pos.nZ] == 0 ) && ( !m_pMask->IsCheckMask( Pos.nX, Pos.nY+1, Pos.nZ ) ) )
nCurDir = nLastDir;
else bRandomSelect = true;
break;
case MD_WEST:
if( ( nStraightStretch < ( m_Size.nX >> 1 ) ) && ( Pos.nX > 0 ) && ( m_pMaze[Pos.nX-1][Pos.nY][Pos.nZ] == 0 ) && ( !m_pMask->IsCheckMask( Pos.nX-1, Pos.nY, Pos.nZ ) ) )
nCurDir = nLastDir;
else bRandomSelect = true;
break;
case MD_EAST:
if( ( nStraightStretch < ( m_Size.nX >> 1 ) ) && ( Pos.nX+1 < m_Size.nX ) && ( m_pMaze[Pos.nX+1][Pos.nY][Pos.nZ] == 0 ) && ( !m_pMask->IsCheckMask( Pos.nX+1, Pos.nY, Pos.nZ ) ) )
nCurDir = nLastDir;
else bRandomSelect = true;
break;
default:
bRandomSelect = true;
}
}
if( bRandomSelect ) {
nStraightStretch = 0;
nCurDir = 0;
while( ( nCurDir == MD_NONE ) || ( nAccumDir & nCurDir ) ) {
IntVec3 Temp;
Temp = Pos;
switch( rand()%4 ) {
case 0: if( Pos.nY > 0 ) { nCurDir = MD_NORTH; Temp.nY--; } else { nAccumDir |= MD_NORTH; } break;
case 1: if( Pos.nY+1 < m_Size.nY ) { nCurDir = MD_SOUTH; Temp.nY++; } else { nAccumDir |= MD_SOUTH; } break;
case 2: if( Pos.nX > 0 ) { nCurDir = MD_WEST; Temp.nX--; } else { nAccumDir |= MD_WEST; } break;
case 3: if( Pos.nX+1 < m_Size.nX ) { nCurDir = MD_EAST; Temp.nX++; } else { nAccumDir |= MD_EAST; } break;
}
if( ( m_pMask->IsCheckMask( Temp.nX, Temp.nY, Temp.nZ ) ) || ( m_pMaze[Temp.nX][Temp.nY][Temp.nZ] != 0 ) ) {
nAccumDir |= nCurDir;
if( nAccumDir == nAllDir ) break;
nCurDir = MD_NONE;
}
}
}
else nStraightStretch++;
if( nAccumDir == nAllDir ) continue;
nLastDir = nCurDir;
m_pMaze[Pos.nX][Pos.nY][Pos.nZ] |= nCurDir;
switch( nCurDir ) {
case MD_NORTH: Pos.nY--; nCurDir = MD_SOUTH; break;
case MD_SOUTH: Pos.nY++; nCurDir = MD_NORTH; break;
case MD_WEST: Pos.nX--; nCurDir = MD_EAST; break;
case MD_EAST: Pos.nX++; nCurDir = MD_WEST; break;
}
m_pMaze[Pos.nX][Pos.nY][Pos.nZ] |= nCurDir;
nAccumDir = m_pMaze[Pos.nX][Pos.nY][Pos.nZ];
nRemainCount--;
}
}
void CEtMaze::Sparsify( int nAmount )
{
for( int i=0; i<nAmount; i++ ) {
for( int x=0; x<m_Size.nX; x++ ) {
for( int y=0; y<m_Size.nY; y++ ) {
for( int z=0; z<m_Size.nZ; z++ ) {
if( m_pMask->GetMaskAt( x, y, z ) != 0 ) continue;
int nDir = m_pMaze[x][y][z];
switch( nDir ) {
case MD_NORTH:
case MD_SOUTH:
case MD_WEST:
case MD_EAST:
case MD_UP:
case MD_DOWN:
break;
default:
continue;
}
m_pMaze[x][y][z] = 0;
if( ( nDir & MD_NORTH ) != 0 ) {
m_pMaze[ x ][ y - 1 ][ z ] &= ~MD_SOUTH;
m_pMaze[ x ][ y - 1 ][ z ] |= 0x8000;
}
else if( ( nDir & MD_SOUTH ) != 0 ) {
m_pMaze[ x ][ y + 1 ][ z ] &= ~MD_NORTH;
m_pMaze[ x ][ y + 1 ][ z ] |= 0x8000;
}
else if( ( nDir & MD_WEST ) != 0 ) {
m_pMaze[ x - 1 ][ y ][ z ] &= ~MD_EAST;
m_pMaze[ x - 1 ][ y ][ z ] |= 0x8000;
}
else if( ( nDir & MD_EAST ) != 0 ) {
m_pMaze[ x + 1 ][ y ][ z ] &= ~MD_WEST;
m_pMaze[ x + 1 ][ y ][ z ] |= 0x8000;
}
else if( ( nDir & MD_DOWN ) != 0 ) {
m_pMaze[ x ][ y ][ z - 1 ] &= ~MD_UP;
m_pMaze[ x ][ y ][ z - 1 ] |= 0x8000;
}
else if( ( nDir & MD_UP ) != 0 ) {
m_pMaze[ x ][ y ][ z + 1 ] &= ~MD_DOWN;
m_pMaze[ x ][ y ][ z + 1 ] |= 0x8000;
}
}
}
}
for( int l=0; l<m_Size.nX; l++ ) {
for( int m=0; m<m_Size.nY; m++ ) {
for( int n=0; n<m_Size.nZ; n++ ) {
m_pMaze[l][m][n] &= ~0x8000;
}
}
}
}
}
void CEtMaze::ClearDeadends( int nPer )
{
int nDir, nRDir, nTestDir;
IntVec3 Pos;
IntVec3 TempPos;
for( int x=0; x<m_Size.nX; x++ ) {
for( int y=0; y<m_Size.nY; y++ ) {
for( int z=0; z<m_Size.nZ; z++ ) {
nDir = m_pMaze[ x ][ y ][ z ];
switch( nDir ) {
case MD_NORTH:
case MD_SOUTH:
case MD_WEST:
case MD_EAST:
case MD_UP:
case MD_DOWN:
break;
default:
continue;
}
if( rand() % 100 + 1 > nPer ) continue;
Pos = IntVec3( x, y, z );
do {
nDir = 0;
nTestDir = 0;
do {
TempPos = Pos;
switch( rand() % 6 ) {
case 0:
if( Pos.nY > 0 ) { nDir = MD_NORTH; nRDir = MD_SOUTH; TempPos.nY--; }
else { nTestDir |= MD_NORTH; }
break;
case 1:
if( Pos.nY+1 < m_Size.nY ) { nDir = MD_SOUTH; nRDir = MD_NORTH; TempPos.nY++; }
else { nTestDir |= MD_SOUTH; }
break;
case 2:
if( Pos.nX > 0 ) { nDir = MD_WEST; nRDir = MD_EAST; TempPos.nX--; }
else { nTestDir |= MD_WEST; }
break;
case 3:
if( Pos.nX+1 < m_Size.nX ) { nDir = MD_EAST; nRDir = MD_WEST; TempPos.nX++; }
else { nTestDir |= MD_EAST; }
break;
case 4:
if( Pos.nZ > 0 ) { nDir = MD_DOWN; nRDir = MD_UP; TempPos.nZ--; }
else { nTestDir |= MD_DOWN; }
break;
case 5:
if( Pos.nZ+1 < m_Size.nZ ) { nDir = MD_UP; nRDir = MD_DOWN; TempPos.nZ++; }
else { nTestDir |= MD_UP; }
break;
}
if( m_pMaze[ Pos.nX ][ Pos.nY ][ Pos.nZ ] == nDir ) {
nTestDir |= nDir;
nDir = 0;
}
if( m_pMask->GetMaskAt( TempPos.nX, TempPos.nY, TempPos.nZ ) ) {
nTestDir |= nDir;
nDir = 0;
}
if( nTestDir == ( MD_WEST | MD_EAST | MD_NORTH | MD_SOUTH | MD_UP | MD_DOWN ) ) break;
} while( nDir == 0 );
if( nTestDir == ( MD_WEST | MD_EAST | MD_NORTH | MD_SOUTH | MD_UP | MD_DOWN ) ) break;
m_pMaze[ Pos.nX ][ Pos.nY ][ Pos.nZ ] |= nDir;
m_pMaze[ TempPos.nX ][ TempPos.nY ][ TempPos.nZ ] |= nRDir;
Pos = TempPos;
} while( m_pMaze[ TempPos.nX ][ TempPos.nY ][ TempPos.nZ ] == nRDir );
}
}
}
}
void CEtMaze::ClearDeadendsBySingleLevel( int nPer, int nLevel )
{
int nDir, nRDir, nTestDir;
IntVec3 Pos;
IntVec3 TempPos;
for( int x=0; x<m_Size.nX; x++ ) {
for( int y=0; y<m_Size.nY; y++ ) {
nDir = m_pMaze[ x ][ y ][ nLevel ];
switch( nDir ) {
case MD_NORTH:
case MD_SOUTH:
case MD_WEST:
case MD_EAST:
case MD_UP:
case MD_DOWN:
break;
default:
continue;
}
if( rand() % 100 + 1 > nPer ) continue;
Pos = IntVec3( x, y, nLevel );
do {
nDir = 0;
nTestDir = 0;
do {
TempPos = Pos;
switch( rand() % 6 ) {
case 0:
if( Pos.nY > 0 ) { nDir = MD_NORTH; nRDir = MD_SOUTH; TempPos.nY--; }
else { nTestDir |= MD_NORTH; }
break;
case 1:
if( Pos.nY+1 < m_Size.nY ) { nDir = MD_SOUTH; nRDir = MD_NORTH; TempPos.nY++; }
else { nTestDir |= MD_SOUTH; }
break;
case 2:
if( Pos.nX > 0 ) { nDir = MD_WEST; nRDir = MD_EAST; TempPos.nX--; }
else { nTestDir |= MD_WEST; }
break;
case 3:
if( Pos.nX+1 < m_Size.nX ) { nDir = MD_EAST; nRDir = MD_WEST; TempPos.nX++; }
else { nTestDir |= MD_EAST; }
break;
}
if( m_pMaze[ Pos.nX ][ Pos.nY ][ Pos.nZ ] == nDir ) {
nTestDir |= nDir;
nDir = 0;
}
if( m_pMask->GetMaskAt( TempPos.nX, TempPos.nY, TempPos.nZ ) ) {
nTestDir |= nDir;
nDir = 0;
}
if( nTestDir == ( MD_WEST | MD_EAST | MD_NORTH | MD_SOUTH ) ) break;
} while( nDir == 0 );
if( nTestDir == ( MD_WEST | MD_EAST | MD_NORTH | MD_SOUTH ) ) break;
m_pMaze[ Pos.nX ][ Pos.nY ][ Pos.nZ ] |= nDir;
m_pMaze[ TempPos.nX ][ TempPos.nY ][ TempPos.nZ ] |= nRDir;
Pos = TempPos;
} while( m_pMaze[ TempPos.nX ][ TempPos.nY ][ TempPos.nZ ] == nRDir );
}
}
}
bool CEtMaze::CalcSolve( IntVec3 &StartPos, IntVec3 &EndPos )
{
return true;
}
int CEtMaze::GetMazeAt( int nX, int nY, int nZ )
{
return m_pMaze[nX][nY][nZ];
}
int CEtMaze::GetWidth()
{
return m_Size.nX;
}
int CEtMaze::GetHeight()
{
return m_Size.nY;
}
int CEtMaze::GetLevel()
{
return m_Size.nZ;
}