DragonNest/GameCommon/MAWalkMovementNav.cpp
2024-12-20 16:56:44 +08:00

233 lines
14 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "StdAfx.h"
#include "DnWorld.h"
#include "MAWalkMovementNav.h"
#include "DnActor.h"
#include "navigationmesh.h"
#include "navigationcell.h"
#include "PerfCheck.h"
#if defined( _GAMESERVER )
#include "DNGameRoom.h"
#endif // #if defined( _GAMESERVER )
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#endif
MAWalkMovementNav::MAWalkMovementNav(void)
{
m_pCurCell = NULL;
}
MAWalkMovementNav::~MAWalkMovementNav(void)
{
}
void MAWalkMovementNav::ValidateCurCell()
{
NavigationMesh *pNavMesh = INSTANCE(CDnWorld).GetNavMesh( m_pMatExWorld->m_vPosition );
if( !pNavMesh ) return;
m_pCurCell = pNavMesh->FindCell( m_pMatExWorld->m_vPosition );
}
void MAWalkMovementNav::ProcessNormal( LOCAL_TIME LocalTime, float fDelta )
{
ProcessCommon( LocalTime, fDelta );
EtVector3 vPrevPos = *m_pActor->GetPrevPosition();
ProcessWallCollision( m_pActor, m_pActor->GetObjectHandle(), vPrevPos );
PostProcess( LocalTime );
}
void MAWalkMovementNav::Process( LOCAL_TIME LocalTime, float fDelta )
{
NavigationMesh *pNavMesh = INSTANCE(CDnWorld).GetNavMesh( m_pMatExWorld->m_vPosition );
if( pNavMesh == NULL )
{
return ProcessNormal( LocalTime, fDelta );
}
if( m_pCurCell == NULL )
{
m_pCurCell = pNavMesh->FindCell( m_pMatExWorld->m_vPosition );
if( m_pCurCell == NULL )
{
return ProcessNormal( LocalTime, fDelta );
}
}
PROFILE_TIME_TEST( ProcessCommon( LocalTime, fDelta ) );
EtVector3 vPrevPos = *m_pActor->GetPrevPosition();
PROFILE_TIME_TEST( ProcessWallCollision( m_pActor, m_pActor->GetObjectHandle(), vPrevPos ) );
int nSide = -1;
NavigationCell *pLastCell = NULL;
bool bSuccess;
PROFILE_TIME_TEST_BLOCK_START( "m_pCurCell->FindLastCollision" );
bSuccess = m_pCurCell->FindLastCollision( vPrevPos, m_pMatExWorld->m_vPosition, &pLastCell, nSide );
PROFILE_TIME_TEST_BLOCK_END();
#if defined( _GAMESERVER )
if( bSuccess == false )
{
if( m_pActor && m_pActor->GetGameRoom() )
{
g_Log.Log( LogType::_NORMAL, L"[GameServer] FindLastCollision InfiniteLoop!!!!!! MapIndex=%d", m_pActor->GetGameRoom()->m_iMapIdx );
}
}
#endif // #if defined( _GAMESERVER )
PROFILE_TIME_TEST_BLOCK_START( "Movement1" );
if( nSide != -1 )
{
int nWallAttribute = pLastCell->GetWallAttribute( nSide ) & 0x0f;
if( ( nWallAttribute == NAV_WALLATTR_BLUE ) || ( nWallAttribute == NAV_WALLATTR_YELLOW ) )
{
OnClash( m_pMatExWorld->m_vPosition, eCFCT_None );
}
if( nWallAttribute == NAV_WALLATTR_GREEN )
{
OnBumpWall();
}
if( pLastCell->Link( nSide ) == NULL )
{
EtVector2 vWallDir2D;
EtVector2 vMoveDir2D( m_pMatExWorld->m_vPosition.x - vPrevPos.x, m_pMatExWorld->m_vPosition.z - vPrevPos.z );
float fMoveLength = EtVec2Length( &vMoveDir2D );
vMoveDir2D /= fMoveLength;
pLastCell->GetWallDir( nSide, vWallDir2D );
fMoveLength *= EtVec2Dot( &vWallDir2D, &vMoveDir2D );
m_pMatExWorld->m_vPosition.x = vPrevPos.x + fMoveLength * vWallDir2D.x;
m_pMatExWorld->m_vPosition.z = vPrevPos.z + fMoveLength * vWallDir2D.y;
int nNewSide = -1;
m_pCurCell->FindLastCollision( vPrevPos, m_pMatExWorld->m_vPosition, &pLastCell, nNewSide );
if( nNewSide != -1 )
{
if( pLastCell->Link( nNewSide ) )
{
NavigationCell *pNewCell;
pNewCell = pLastCell->Link( nNewSide );
if( pNewCell->IsPointInCellCollumn( m_pMatExWorld->m_vPosition ) )
{
m_pCurCell = pNewCell;
}
else
{
m_pMatExWorld->m_vPosition.x = vPrevPos.x;
m_pMatExWorld->m_vPosition.z = vPrevPos.z;
}
}
else if( !m_pCurCell->IsPointInCellCollumn( m_pMatExWorld->m_vPosition ) )
{
m_pMatExWorld->m_vPosition.x = vPrevPos.x;
m_pMatExWorld->m_vPosition.z = vPrevPos.z;
}
}
// 네비게이션 모드 작동
OnBeginNaviMode();
}
else
{
m_pCurCell = pLastCell->Link( nSide );
}
if( !m_pCurCell->IsPointInCellCollumn( m_pMatExWorld->m_vPosition ) )
{
m_pCurCell = pNavMesh->FindCell( m_pMatExWorld->m_vPosition );
}
}
PROFILE_TIME_TEST_BLOCK_END();
PROFILE_TIME_TEST_BLOCK_START( "Movement2" );
float fHeight = INSTANCE(CDnWorld).GetHeight( m_pMatExWorld->m_vPosition );
if( m_pCurCell && m_pCurCell->GetType() == NavigationCell::CT_PROP )
m_pActor->SetAddHeight( m_pCurCell->GetPlane()->SolveForY( m_pMatExWorld->m_vPosition.x, m_pMatExWorld->m_vPosition.z ) - fHeight );
else
m_pActor->SetAddHeight( 0.f );
if( !m_vVelocity.y && false == m_bAppliedYDistance )
m_pMatExWorld->m_vPosition.y = fHeight + m_pActor->GetAddHeight();
PROFILE_TIME_TEST_BLOCK_END();
PROFILE_TIME_TEST( PostProcess( LocalTime ) );
}
void MAWalkMovementNav::ProcessWallCollision( CDnActor *pActor, EtObjectHandle hObject, EtVector3 &vPrevPos )
{
int i;
float fMoveDist;
EtVector3 vMoveDir;
DNVector(SCollisionResponse) &vecResult = m_vecResult;
vecResult.clear();
vMoveDir = m_pMatExWorld->m_vPosition - vPrevPos;
fMoveDist = EtVec3Length( &vMoveDir );
vMoveDir /= fMoveDist;
if( fMoveDist <= 0.0f )
{
return;
}
int nLoopCount = 0;
float fTemoMoveDist;
EtVector3 vTempPrevPos = vPrevPos;
while( ( fMoveDist > 0.0f ) && ( nLoopCount < 20 ) )
{
if( fMoveDist > 20.0f )
{
fTemoMoveDist = 20.0f;
}
else
{
fTemoMoveDist = fMoveDist;
}
m_pMatExWorld->m_vPosition = vTempPrevPos + fTemoMoveDist * vMoveDir;
fMoveDist -= fTemoMoveDist;
nLoopCount++;
if( hObject && hObject->FindDynamicCollision( *m_pMatExWorld, vecResult, false ) )
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 충돌메쉬에서 천장과 바닥 추려내기
for( i = ( int )vecResult.size() - 1; i >= 0; i-- )
{
if( EtVec3Dot( &vecResult[ i ].vNormal, &EtVector3( 0.0f, -1.0f, 0.0f ) ) > 0.965925f ) // 천장은 15도 체크
{
vecResult.erase( vecResult.begin() + i );
continue;
}
if( EtVec3Dot( &vecResult[ i ].vNormal, &EtVector3( 0.0f, 1.0f, 0.0f ) ) > 0.342020f )
{
vecResult.erase( vecResult.begin() + i );
continue;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 벽충돌
if( !vecResult.empty() )
{
EtVector3 vSlideDir, vWallNormal, vTempMoveDir;
float fWeight;
vTempMoveDir = vMoveDir;
for( i = 0; i < ( int )vecResult.size(); i++ )
{
if( EtVec3Dot( &vecResult[ i ].vNormal, &vTempMoveDir ) < 0.0f ) // 벽의노말과 이동방향이 같으면 무시
{
vWallNormal = vecResult[ i ].vNormal;
vWallNormal.y = 0.0f;
EtVec3Normalize( &vWallNormal, &vWallNormal );
CalcSlide( vSlideDir, fWeight, vWallNormal, vTempMoveDir );
vTempMoveDir = vSlideDir;
fTemoMoveDist *= fWeight;
m_pMatExWorld->m_vPosition.x = vTempPrevPos.x + vTempMoveDir.x * fTemoMoveDist;
m_pMatExWorld->m_vPosition.z = vTempPrevPos.z + vTempMoveDir.z * fTemoMoveDist;
}
}
}
OnClash( m_pMatExWorld->m_vPosition, eCFCT_None ); // note by kalliste : 이 지점에서 바닥충돌 관련 검출이 필요 없어 None으로 셋팅. 이후 필요할 경우 NoFloor 셋팅할 것.
}
vTempPrevPos = m_pMatExWorld->m_vPosition;
}
}