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

2057 lines
120 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 "MAWalkMovement.h"
#include "EtMatrixEx.h"
#include "DnWorld.h"
#include "DnActionBase.h"
#include "DnActor.h"
#include "VelocityFunc.h"
#include "MAActorRenderBase.h"
#include "PerfCheck.h"
#include "EtTestCollision.h"
#include "navigationcell.h"
#include "navigationmesh.h"
#include "navigationpath.h"
#ifndef _GAMESERVER
#include "EtDecal.h"
#endif
#if defined(_DEBUG) || defined(_RDEBUG)
#define _USE_COLLISION_DEBUG 0
#else
#define _USE_COLLISION_DEBUG 0
#endif
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#endif
MAWalkMovement::MAWalkMovement()
: m_vMovement( 0.f, 0.f )
, m_vJumpMovement( 0.f, 0.f )
, m_vJumpXVector( 0.f, 0.f, 0.f )
, m_vJumpZVector( 0.f, 0.f, 0.f )
, m_vVelocity( 0.f, 0.f, 0.f )
, m_vVelocityResist( -30.f, -30.f, -30.f )
, m_vLastVelocity( 0.f, 0.f, 0.f )
, m_vMoveVectorX( 0.f, 0.f, 0.f )
, m_vMoveVectorZ( 0.f, 0.f, 0.f )
, m_vMovePos( 0.f, 0.f, 0.f )
, m_vMagnetDir( 0.f, 0.f )
, m_vTargetLookVec( 0.f, 0.f )
, m_vLastVelocityValue( 0.f, 0.f, 0.f )
, m_fMoveYDistancePerSec( 0.0f )
, m_fLeftMoveYDistance( 0.0f )
, m_bAppliedYDistance( false )
, m_bMaintainYDistanceOnArriveDestPosition( false )
, m_bOnDrop(false)
, m_bOnFall(false)
, m_bOnStop(false)
, m_fDropVelocity(0.0f)
, m_fNaviTargetMinDistance(0.0f)
, m_nNaviType(0)
{
m_fLimitDgrees = 90.f;
m_PrevLocalTime = 0;
m_fJumpMoveSpeed = 0.f;
m_fTargetMinDistance = 0.f;
m_bRefreshZVector = false;
m_fMagnetLength = 0.f;
m_fAngleAssist = 0.f;
m_bEnableNaviMode = false;
m_bFloorForceVelocity = false;
m_bLastFloorForceVelocity = false;
m_bFloorCollision = false;
m_LastLookTargetTime = 0;
m_bDebugRenderAttr = false;
// m_fLastHeight = 0.f;
#ifndef _GAMESERVER
m_fLastMoveDelta = 0.f;
memset( m_fLastMoveLength, 0, sizeof(m_fLastMoveLength) );
m_nLastMoveCount = 0;
#endif
#ifdef PRE_MOD_NAVIGATION_PATH
m_bAutoMoving = false;
#endif // PRE_MOD_NAVIGATION_PATH
}
MAWalkMovement::~MAWalkMovement()
{
}
bool Compare_CollisionOrder( SCollisionInfo &a, SCollisionInfo &b )
{
return a.CollisionNormal.y < b.CollisionNormal.y;
}
EtVector2 g_DirVector[4] =
{
EtVector2( -0.707107f, 0.707107f ),
EtVector2( 0.707107f, 0.707107f ),
EtVector2( 0.707107f, -0.707107f ),
EtVector2( -0.707107f, -0.707107f )
};
void GetCheckDirctionVector( const int nIndex, EtVector2 & etVector )
{
if( 0 == nIndex )
{
etVector = EtVector2( -0.707107f, 0.707107f );
}
else if( 1 == nIndex )
{
etVector = EtVector2( 0.707107f, 0.707107f );
}
else if( 2 == nIndex )
{
etVector = EtVector2( 0.707107f, -0.707107f );
}
else if( 3 == nIndex )
{
etVector = EtVector2( -0.707107f, -0.707107f );
}
}
bool MAWalkMovement::CheckDiagonalBlock( float fX, float fY )
{
char cAttr = INSTANCE(CDnWorld).GetAttribute( fX, fY );
int nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( fX, fY );
if( nBlockSize == 0 ) return false;
float fWorldX, fWorldY;
INSTANCE(CDnWorld).CalcWorldBasePos( fX, fY, fWorldX, fWorldY );
int nX = int(fWorldX) / nBlockSize;
int nY = int(fWorldY) / nBlockSize;
float fTempX = fWorldX - ( nX * nBlockSize );
float fTempY = fWorldY - ( nY * nBlockSize );
if( cAttr & 0x10 )
{
if( fTempX + fTempY > nBlockSize ) return false;
}
if( cAttr & 0x20 )
{
if( fTempX > fTempY ) return false;
}
if( cAttr & 0x40 )
{
if( fTempX + fTempY < nBlockSize ) return false;
}
if( cAttr & 0x80 )
{
if( fTempX < fTempY ) return false;
}
return true;
}
bool MAWalkMovement::CheckMovableBlock( char cAttr )
{
if( cAttr == 0 ) return true;
if( ( cAttr & 0xf0 ) != 0 ) return true;
return false;
}
void MAWalkMovement::ProcessCommon( LOCAL_TIME LocalTime, float fDelta )
{
m_bOnDrop = m_bOnFall = m_bOnStop = false;
m_fDropVelocity = 0.f;
m_pActor->SetPrevPosition( m_pMatExWorld->m_vPosition );
EtVector3 vPrevPos = m_pMatExWorld->m_vPosition;
EtVector3 vPrevVel = m_vVelocity;
EtVector2 vAniDistance( 0.f, 0.f );
EtVector3 *vDist = m_pActor->GetAniDistance();
if( m_pActor->GetState() & CDnActorState::Move &&
false == (m_pActor->GetState() & CDnActorState::IgnoreBackMoveSpeed) ) {
EtVector2 vApplyMoveSpeed( 0.f, 0.f );
if( m_pActor->GetMoveSpeed() == 0 || fDelta == 0.f ) {
vApplyMoveSpeed.x = vDist->x;
vApplyMoveSpeed.y = vDist->z;
}
else {
float fMoveSpeed = m_pActor->GetMoveSpeed() * fDelta;
vApplyMoveSpeed.x = ( ( 1.f / fDelta ) * vDist->x ) / 300.f;
vApplyMoveSpeed.y = ( ( 1.f / fDelta ) * vDist->z ) / 300.f;
vApplyMoveSpeed *= fMoveSpeed;
}
vAniDistance += EtVector2( m_vMoveVectorX.x, m_vMoveVectorX.z ) * vApplyMoveSpeed.x;
vAniDistance += EtVector2( m_vMoveVectorZ.x, m_vMoveVectorZ.z ) * vApplyMoveSpeed.y;
}
else {
vAniDistance += EtVector2( m_pMatExWorld->m_vXAxis.x, m_pMatExWorld->m_vXAxis.z ) * vDist->x;
vAniDistance += EtVector2( m_pMatExWorld->m_vZAxis.x, m_pMatExWorld->m_vZAxis.z ) * vDist->z;
}
m_vMovement += vAniDistance;
// Look 처리
if( m_hLookTarget )
{
EtVector2 vLook;
#ifdef PRE_FIX_PARTSMONSTER_AI_TARGETTING
EtVector3 vTargetPosition = m_hLookTarget->FindAutoTargetPos();
vLook.x = vTargetPosition.x - m_pMatExWorld->m_vPosition.x;
vLook.y = vTargetPosition.z - m_pMatExWorld->m_vPosition.z;
#else
vLook.x = m_hLookTarget->GetPosition()->x - m_pMatExWorld->m_vPosition.x;
vLook.y = m_hLookTarget->GetPosition()->z - m_pMatExWorld->m_vPosition.z;
#endif
if( EtVec2LengthSq( &vLook ) > 0.f )
{
EtVec2Normalize( &vLook, &vLook );
float fDot = EtVec2Dot( &vLook, &EtVec3toVec2( m_pMatExWorld->m_vZAxis ) );
if( fDot >= 1.f ) fDot = 1.f;
float fAngle = EtToDegree( EtAcos( fDot ) );
bool bForceLook = true;
if( fAngle > m_pActor->GetRotateAngleSpeed() * fDelta ) bForceLook = false;
m_pActor->Look( vLook, bForceLook );
if( bForceLook )
OnEndLook();
m_LastLookTargetTime = LocalTime;
}
}
if ( m_bEnableNaviMode )
{
if( !m_WayPointList.empty() && m_WayPointId != m_WayPointList.end() )
{
m_vMovePos = (*m_WayPointId).Position;
}
else
{
m_bEnableNaviMode = false;
}
}
// 타겟 이동 처리
else if( m_hMoveTarget )
{
#ifdef PRE_FIX_PARTSMONSTER_AI_TARGETTING
m_vMovePos = m_hMoveTarget->FindAutoTargetPos();
#else
m_vMovePos = *m_hMoveTarget->GetPosition();
#endif
}
// Look 처리
if( EtVec2LengthSq( &m_vTargetLookVec ) > 0.f ) {
float fDot = D3DXVec2Dot( &m_vTargetLookVec, &EtVec3toVec2( m_pMatExWorld->m_vZAxis ) );
if( fDot >= 1.f ) fDot = 1.f;
float fAngle = EtToDegree( EtAcos(fDot) );
float fAngleSpeed = m_pActor->GetRotateAngleSpeed() * fDelta;
if( fAngle > fAngleSpeed ) {
EtVector3 vCrossVec;
D3DXVec3Cross( &vCrossVec, &EtVec2toVec3( m_vTargetLookVec ), &m_pMatExWorld->m_vZAxis );
if( vCrossVec.y > 0.f )
m_pMatExWorld->RotateYaw( fAngleSpeed );
else m_pMatExWorld->RotateYaw( -fAngleSpeed );
fDot = D3DXVec2Dot( &m_vTargetLookVec, &EtVec3toVec2( m_pMatExWorld->m_vZAxis ) );
if( fDot >= 1.f ) fDot = 1.f;
fAngle = EtToDegree( EtAcos(fDot) );
if( fAngle <= fAngleSpeed ) {
m_pMatExWorld->m_vZAxis = EtVec2toVec3( m_vTargetLookVec );
m_pMatExWorld->MakeUpCartesianByZAxis();
m_vTargetLookVec = EtVector2( 0.f, 0.f );
}
}
else {
m_pMatExWorld->m_vZAxis = EtVec2toVec3( m_vTargetLookVec );
m_pMatExWorld->MakeUpCartesianByZAxis();
m_vTargetLookVec = EtVector2( 0.f, 0.f );
}
}
// 포지션 이동 처리
if( EtVec3LengthSq( &m_vMovePos ) > 0.f ) {
EtVector3 vTemp = m_vMovePos - m_pMatExWorld->m_vPosition;
vTemp.y = 0.f;
float fLength = EtVec3Length( &vTemp );
if( !m_hMoveTarget ) {
if( m_pActor->GetMoveSpeed() > 0.f )
m_fTargetMinDistance = m_pActor->GetMoveSpeed() * fDelta;
else m_fTargetMinDistance = EtVec2Length( &m_vMovement );
if( m_fTargetMinDistance == 0.f ) {
m_fTargetMinDistance = EtVec3Length( m_pActor->GetAniDistance() );
}
m_fTargetMinDistance += 1.f;
}
if( fLength <= m_fTargetMinDistance || !m_pActor->IsMove() )
{
if( !m_hMoveTarget && fLength <= m_fTargetMinDistance ) {
EtVec3Normalize( &vTemp, &vTemp );
m_vMovement += EtVector2( vTemp.x, vTemp.z ) * fLength;
}
m_vMovePos = EtVector3( 0.f, 0.f, 0.f );
#ifdef PRE_MOD_NAVIGATION_PATH
// 자동이동 모드 네비 모드에서는 다음 웨이포인트로 넘긴다.
if( ( m_bAutoMoving ||m_bEnableNaviMode ) && !m_WayPointList.empty() )
{
WayPointID CurrentWayPointID = m_WayPointId;
m_WayPointId++;
if ( m_WayPointId == m_WayPointList.end() )
{
m_bOnStop = true;
m_bEnableNaviMode = false;
m_bAutoMoving = false;
}
else
{
if( m_bAutoMoving )
OnAutoMoving( (EtVector3)(*m_WayPointId).Position, (EtVector3)(*CurrentWayPointID).Position );
else if( m_bEnableNaviMode )
OnMoveNavi( (EtVector3)(*m_WayPointId).Position );
}
}
else
{
m_bOnStop = true;
m_bEnableNaviMode = false;
m_bAutoMoving = false;
}
#else // PRE_MOD_NAVIGATION_PATH
// 네비 모드에서는 다음 웨이포인트로 넘긴다.
if( m_bEnableNaviMode && !m_WayPointList.empty() )
{
m_WayPointId++;
if ( m_WayPointId == m_WayPointList.end() )
{
m_bOnStop = true;
m_bEnableNaviMode = false;
}
else OnMoveNavi( (EtVector3)(*m_WayPointId).Position );
}
else
{
m_bOnStop = true;
m_bEnableNaviMode = false;
}
#endif // PRE_MOD_NAVIGATION_PATH
}
else {
EtVec3Normalize( &vTemp, &vTemp );
if( m_bRefreshZVector ) {
m_vMoveVectorZ = vTemp;
if( EtVec2LengthSq( &m_vTargetLookVec ) == 0.f ) {
m_pMatExWorld->m_vZAxis = vTemp;
}
else m_vTargetLookVec = EtVec3toVec2( vTemp );
m_pMatExWorld->MakeUpCartesianByZAxis();
}
m_vMovement += EtVector2( vTemp.x, vTemp.z ) * ( m_pActor->GetMoveSpeed() * fDelta );
#ifndef _GAMESERVER
if( m_pActor->GetMoveSpeed() > 0 ) {
m_fLastMoveDelta += fDelta;
if( m_fLastMoveDelta >= FORCE_REVISION_DELTA ) {
m_fLastMoveDelta -= FORCE_REVISION_DELTA;
m_nLastMoveCount++;
if( m_nLastMoveCount == FORCE_REVISION_HISTORY_COUNT ) {
float fTemp = fLength;
for( int n=0; n<FORCE_REVISION_HISTORY_COUNT; n++ ) fTemp += m_fLastMoveLength[n];
fTemp /= ( FORCE_REVISION_HISTORY_COUNT + 1 );
bool bValidDiff = false;
float fOffsetSpeed = m_pActor->GetMoveSpeed() / 15.f;
if( abs( fTemp - fLength ) < fOffsetSpeed ) {
bValidDiff = true;
for( int n=0; n<FORCE_REVISION_HISTORY_COUNT; n++ )
if( abs( m_fLastMoveLength[n] - fTemp ) > fOffsetSpeed ) bValidDiff = false;
}
if( bValidDiff && !( m_pActor->IsPlayerActor() && (m_pActor->GetMySmartPtr() == CDnActor::s_hLocalActor ) ) ) {
m_fLastMoveDelta = 0.f;
m_nLastMoveCount = 0;
m_vMovement += EtVector2( vTemp.x, vTemp.z ) * fLength;
m_pMatExWorld->m_vPosition.y = vPrevPos.y = m_vMovePos.y;
m_pActor->SetPrevPosition( vPrevPos );
m_vMovePos = EtVector3( 0.f, 0.f, 0.f );
// OnStop( m_pMatEx->m_vPosition );
m_bOnStop = true;
m_bEnableNaviMode = false;
}
else {
m_fLastMoveDelta = 0.f;
m_nLastMoveCount = 0;
}
}
m_fLastMoveLength[m_nLastMoveCount] = fLength;
}
}
#endif
}
}
// Magnet 처리
if( m_fMagnetLength > 0.f ) {
float fSpeed = (float)m_pActor->GetMoveSpeed();
if( fSpeed == 0.f ) fSpeed = 300.f;
float fMagnetMove = fSpeed * fDelta;
if( m_fMagnetLength - fMagnetMove <= 0.f ) fMagnetMove = m_fMagnetLength;
m_vMovement += m_vMagnetDir * fMagnetMove;
m_fMagnetLength -= fMagnetMove;
if( m_fMagnetLength <= 0.f ) {
m_vMagnetDir = EtVector2( 0.f, 0.f );
m_fMagnetLength = 0.f;
}
}
// Velocity 처리
EtVector3 vVelocity = EtVector3( 0.f, 0.f, 0.f );
m_vLastVelocityValue = EtVector3( 0.f, 0.f, 0.f );
if( m_vVelocity.z ) {
float fTemp = m_vVelocity.z;
float fMin = ( fTemp > 0.f ) ? 0.f : -FLT_MAX;
float fMax = ( fTemp > 0.f ) ? FLT_MAX : 0.f;
float fTemp2 = m_vVelocity.z;
m_vLastVelocityValue.z = CalcMovement( fTemp2, 1.f, fMax, fMin, m_vVelocityResist.z );
vVelocity += m_vVelocityZVector * CalcMovement( m_vVelocity.z, fDelta, fMax, fMin, m_vVelocityResist.z );
if( m_vVelocity.z * fTemp <= 0.f ) m_vVelocity.z = 0.f;
}
if( m_vVelocity.x ) {
float fTemp = m_vVelocity.x;
float fMin = ( fTemp > 0.f ) ? 0.f : -FLT_MAX;
float fMax = ( fTemp > 0.f ) ? FLT_MAX : 0.f;
float fTemp2 = m_vVelocity.x;
m_vLastVelocityValue.x = CalcMovement( fTemp2, 1.f, fMax, fMin, m_vVelocityResist.x );
vVelocity += m_vVelocityXVector * CalcMovement( m_vVelocity.x, fDelta, fMax, fMin, m_vVelocityResist.x );
if( m_vVelocity.x * fTemp <= 0.f ) m_vVelocity.x = 0.f;
}
m_vMovement += EtVector2( vVelocity.x, vVelocity.z );
m_fAngleAssist = 0.f;
if( EtVec2Length( &m_vMovement ) > 0.f )
{
// 각도에 따른 속도 보정
EtVector3 vTempPos = m_pMatExWorld->m_vPosition;
vTempPos.x += m_vMovement.x;
vTempPos.z += m_vMovement.y;
vTempPos.y = INSTANCE(CDnWorld).GetHeight( vTempPos );
if( ( m_pActor->GetAddHeight() == 0.f && m_pMatExWorld->m_vPosition.y == INSTANCE(CDnWorld).GetHeight( m_pMatExWorld->m_vPosition ) ) || m_pMatExWorld->m_vPosition.y <= vTempPos.y )
{
EtVector3 vMoveNor, vDirNor;
EtVec3Normalize( &vMoveNor, &EtVector3( m_vMovement.x, vTempPos.y - m_pMatExWorld->m_vPosition.y, m_vMovement.y ) );
EtVec3Normalize( &vDirNor, &EtVector3( m_vMovement.x, 0.f, m_vMovement.y ) );
m_fAngleAssist = EtVec3Dot( &vMoveNor, &vDirNor );
m_vMovement *= m_fAngleAssist;
}
}
// 이동 처리
EtVector3 vTemp = m_pMatExWorld->m_vPosition;
vTemp.x += m_vMovement.x - ( vAniDistance.x * m_fAngleAssist );
vTemp.z += m_vMovement.y - ( vAniDistance.y * m_fAngleAssist );
vTemp.y += vVelocity.y;
m_pActor->SetStaticPosition( vTemp );
m_pMatExWorld->m_vPosition.x += m_vMovement.x;
m_pMatExWorld->m_vPosition.z += m_vMovement.y;
m_pMatExWorld->m_vPosition.y += vVelocity.y;
// 점프 처리
float fCurVelocity = 0.f;
float fHeight = INSTANCE(CDnWorld).GetHeight( m_pMatExWorld->m_vPosition ) + m_pActor->GetAddHeight();
if( m_vVelocity.y )
{
float fTemp2 = m_vVelocity.y;
m_vLastVelocityValue.y = CalcMovement( fTemp2, 1.f, FLT_MAX, -FLT_MAX, m_vVelocityResist.y );
m_pMatExWorld->m_vPosition.y += CalcMovement( m_vVelocity.y, fDelta, FLT_MAX, -FLT_MAX, m_vVelocityResist.y );
if( m_vVelocity.y == 0.f )
m_vVelocity.y += 0.0000001f;
if( EtVec2LengthSq( &m_vJumpMovement ) > 0.f )
{
EtVector2 vVec = m_vJumpMovement * ( m_fJumpMoveSpeed * fDelta );
m_pMatExWorld->m_vPosition += m_vJumpXVector * vVec.x;
m_pMatExWorld->m_vPosition += m_vJumpZVector * vVec.y;
}
if( m_pMatExWorld->m_vPosition.y <= fHeight )
{
fCurVelocity = m_vVelocity.y - ( ( m_pMatExWorld->m_vPosition.y - fHeight ) * fDelta );
m_vVelocity.y = 0.f;
if( m_pActor->GetAddHeight() == 0.f )
m_bFloorForceVelocity = false;
m_bOnDrop = true;
m_bLastFloorForceVelocity = m_bFloorForceVelocity;
if( m_pActor->GetAddHeight() == 0.f )
m_pMatExWorld->m_vPosition.y = fHeight;
m_bFloorForceVelocity = false;
}
else
{
m_bOnFall = true;
}
}
m_fDropVelocity = fCurVelocity;
// #48950 MoveY 시그널을 사용한 경우 Y 축 이동 처리
if( m_fMoveYDistancePerSec != 0.0f )
{
float fMoveDistance = m_fMoveYDistancePerSec * fDelta;
m_pMatExWorld->m_vPosition.y += fMoveDistance;
if( 0.0f < fMoveDistance )
m_fLeftMoveYDistance -= fMoveDistance;
else
m_fLeftMoveYDistance += fMoveDistance;
// 남은 거리를 넘어설 경우 도착한 거임.
// 위치를 셋팅해주고 끝냄.
if( m_fLeftMoveYDistance < 0.0f )
{
if( 0.0f < fMoveDistance )
m_pMatExWorld->m_vPosition.y -= (fMoveDistance + m_fLeftMoveYDistance);
else
m_pMatExWorld->m_vPosition.y += (fMoveDistance - m_fLeftMoveYDistance);
ResetMoveYDistance();
if( true == m_bMaintainYDistanceOnArriveDestPosition )
{
m_bAppliedYDistance = true;
m_bMaintainYDistanceOnArriveDestPosition = false;
}
}
}
#ifdef _WORK
if( !( GetAsyncKeyState( VK_CONTROL ) < 0 ) )
#endif
ProcessBlock( *m_pActor->GetPrevPosition() );
#ifdef _GAMESERVER
if( m_pActor->IsPlayerActor() ) {
EtVector2 vTemp2 = EtVector2( vPrevPos.x, vPrevPos.z ) - EtVector2( m_pMatExWorld->m_vPosition.x, m_pMatExWorld->m_vPosition.z );
AniDistStruct Struct;
Struct.Time = LocalTime;
Struct.fDist = EtVec2Length( &vTemp2 ) * ( 1.f / fDelta );
m_VecAniDistList.push_back( Struct );
m_fAverageAniDist = FLT_MIN;
for( DWORD i=0; i<m_VecAniDistList.size(); i++ ) {
if( LocalTime - m_VecAniDistList[i].Time > 1000 ) {
m_VecAniDistList.erase( m_VecAniDistList.begin() + i );
i--;
continue;
}
if( m_VecAniDistList[i].fDist > m_fAverageAniDist )
m_fAverageAniDist = m_VecAniDistList[i].fDist;
}
}
#endif
}
void MAWalkMovement::PostProcess( LOCAL_TIME LocalTime )
{
if( m_bOnStop ) {
#ifdef _SHADOW_TEST
if( !m_pActor->IsShadowActor() ) OnStop( m_pMatExWorld->m_vPosition );
#else
OnStop( m_pMatExWorld->m_vPosition );
#endif
}
if( m_bOnDrop ) {
#ifdef _SHADOW_TEST
if( !m_pActor->IsShadowActor() ) OnDrop( m_fDropVelocity );
#else
OnDrop( m_fDropVelocity );
#endif
}
if( m_bOnFall ) {
#ifdef _SHADOW_TEST
if( !m_pActor->IsShadowActor() ) OnFall( m_vVelocity.y );
#else
OnFall( m_vVelocity.y );
#endif
}
m_PrevLocalTime = LocalTime;
m_vMovement = EtVector2( 0.f, 0.f );
}
bool MAWalkMovement::IsMovableBlock( CDnWorld* pWorld, EtVector3 &vPos )
{
char cAttr = pWorld->GetAttribute( vPos.x, vPos.z );
int nBlockSize = pWorld->GetAttributeBlockSize( vPos.x, vPos.z );
float fWorldX, fWorldY;
pWorld->CalcWorldBasePos( vPos.x, vPos.z, fWorldX, fWorldY );
float fBlockX = fmodf( fWorldX, (float)nBlockSize );
float fBlockY = fmodf( fWorldY, (float)nBlockSize );
if( (( cAttr & 0x0f ) != 1 && ( cAttr & 0x0f ) != 2) ) {
return true;
}
bool bMovable = false;
char cHiAttr = ( cAttr & 0xf0 ) >> 4;
switch( cHiAttr ) {
case 1:
bMovable = fBlockX + fBlockY < nBlockSize;
break;
case 2:
bMovable = fBlockX < fBlockY;
break;
case 4:
bMovable = fBlockX + fBlockY > nBlockSize;
break;
case 8:
bMovable = fBlockX > fBlockY;
break;
}
return bMovable;
}
void MAWalkMovement::ProcessBlock( EtVector3 vPrevPos )
{
EtVector2 vMove;
vMove.x = m_pMatExWorld->m_vPosition.x - vPrevPos.x;
vMove.y = m_pMatExWorld->m_vPosition.z - vPrevPos.z;
EtVector2 vPos(vPrevPos.x, vPrevPos.z);
EtVector2 vAttrPos = vPos;
char cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
int nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
float fWorldX, fWorldY;
INSTANCE(CDnWorld).CalcWorldBasePos( vPos.x, vPos.y, fWorldX, fWorldY );
float fBlockX = fmodf( fWorldX, (float)nBlockSize );
float fBlockY = fmodf( fWorldY, (float)nBlockSize );
#if (_USE_COLLISION_DEBUG)
std::vector< int > debugAttr;
std::vector< EtVector2 > debugPos;
std::vector< EtVector2 > debugBlock;
std::vector< EtVector2 > debugBlockNext;
std::vector< EtVector2 > debugMove;
std::vector< EtVector2 > debugAttrPos;
std::vector< int > debugCode;
#endif
int nDebugCode = -1;
int nStep = 0;
int nSlideType = 0;
if( EtVec2LengthSq(&vMove) > 2000.0f * 2000.0f ) { // 한프레임에 20 미터 이상은 이동 못하도록.
nStep = 101;
}
bool bBreakLoop = false;
do
{
nStep++;
if( nStep > 100 ) { // 이런 경우가 생기면 안된다.
OutputDebug("Unknown Collision Error. Loop count exceed.\n");
vPos = EtVector2(vPrevPos.x, vPrevPos.z);
break;
}
if( EtVec2LengthSq(&vMove) <= 0.001f ) {
break;
}
float fBlockNextX = fBlockX + vMove.x;
float fBlockNextY = fBlockY + vMove.y;
#if (_USE_COLLISION_DEBUG)
debugAttr.push_back( cAttr );
debugPos.push_back( vPos);
debugBlock.push_back( EtVector2(fBlockX, fBlockY));
debugBlockNext.push_back( EtVector2(fBlockNextX, fBlockNextY));
debugMove.push_back( vMove );
debugAttrPos.push_back( EtVector2(vAttrPos.x/50.f, vAttrPos.y/50.f));
#endif
// 대각선 방향 벡터와의 외적을 통해 다음에 충돌할 블럭의 방향을 미리 알아낸다.
int DirIndices[2][2] = {{7,5},{1,3}};
int BitX = (vMove.x >= 0 ? 1 : 0);
int BitY = (vMove.y >= 0 ? 1 : 0);
float fDirX = BitX * nBlockSize - fBlockX;
float fDirY = BitY * nBlockSize - fBlockY;
int nCollideDir = DirIndices[BitX][BitY] + ((fDirX * vMove.y - fDirY * vMove.x > 0 ) ? 1 : -1);
nCollideDir = (nCollideDir>>1)&3; // 0 2 4 6 8 -> 0 1 2 3
float fDa = 0.f;
float fDb = 0.f;
int nIndex = -1;
bool bCollideBlock = (( cAttr & 0x0f ) == 1 || ( cAttr & 0x0f ) == 2); // 0x01, 0x02 는 충돌체크
char cHiAttr = ( cAttr & 0xf0 ) >> 4;
if( bCollideBlock ) {
switch( cHiAttr ) {
case 1:
if( nCollideDir == 1 || nCollideDir == 2 ) {
fDa = fBlockX + fBlockY - nBlockSize;
fDb = fBlockNextX + fBlockNextY - nBlockSize;
nIndex = 0;
}
break;
case 2:
if( nCollideDir == 0 || nCollideDir == 1) {
fDa = fBlockX - fBlockY;
fDb = fBlockNextX - fBlockNextY;
nIndex = 1;
}
break;
case 4:
if( nCollideDir == 0 || nCollideDir == 3 ) {
fDa = nBlockSize - fBlockX - fBlockY;
fDb = nBlockSize - fBlockNextX - fBlockNextY;
nIndex = 2;
}
break;
case 8:
if( nCollideDir == 2 || nCollideDir == 3 ) {
fDa = fBlockY - fBlockX;
fDb = fBlockNextY - fBlockNextX;
nIndex = 3;
}
break;
}
}
float fDiagEpsilon = 0.01f;
float fEpsilon = fDiagEpsilon * 1.4142f;
if( fDa <= 0.f && fDb > 0) { // 대각선 충돌해서 방향 바뀐 경우
nDebugCode = 1;
float fRatio = (-fDa-fDiagEpsilon) / (fDb-fDa);
vPos.x += vMove.x * fRatio;
vPos.y += vMove.y * fRatio;
fBlockX += vMove.x * fRatio;
fBlockY += vMove.y * fRatio;
ASSERT( (vPos.x==vPos.x&&vPos.y==vPos.y) );
vMove *= min(1,(1 - fRatio));
// g_DirVector라는 전역 변수에 핵킹 툴이 접근하므로 이를 전역변수를 사용 하지 않고 상수값을 받아서 처리하도록 수정합니다.
//vMove = g_DirVector[ nIndex ] * D3DXVec2Dot( &g_DirVector[ nIndex ], &vMove );
EtVector2 vCheckDirVector;
GetCheckDirctionVector( nIndex, vCheckDirVector );
vMove = vCheckDirVector * D3DXVec2Dot( &vCheckDirVector, &vMove );
if( nSlideType == 2 ) { // 슬라이딩 처리 한번이상 하지 않는다. 모서리에 걸리는 경우 떨림 방지.
vMove = EtVector2(0,0);
}
nSlideType = 1;
// 블럭 넘어가는지 체크
if( fBlockX+vMove.x < 0.f || fBlockX+vMove.x > nBlockSize ||
fBlockY+vMove.y < 0.f || fBlockY+vMove.y > nBlockSize )
{
bool bDiagDirA = vMove.x+vMove.y > 0.f;
bool bDiagDirB = vMove.x-vMove.y > 0.f;
if( (cHiAttr == 2 && !bDiagDirB ) || (cHiAttr==1 && !bDiagDirA) ) {
vAttrPos.x -= nBlockSize;
fBlockX += nBlockSize;
}
else if( (cHiAttr ==4 && bDiagDirB ) || (cHiAttr==8 && bDiagDirA ) ) {
vAttrPos.x += nBlockSize;
fBlockX -= nBlockSize;
}
else if( (cHiAttr==1 && bDiagDirB ) || (cHiAttr==8 && !bDiagDirA) ) {
vAttrPos.y -= nBlockSize;
fBlockY += nBlockSize;
}
else if( (cHiAttr==2 && bDiagDirA ) || (cHiAttr==4 && !bDiagDirB ) ) {
vAttrPos.y += nBlockSize;
fBlockY -= nBlockSize;
}
else {
ASSERT( false );
}
cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
}
else
{
bBreakLoop = true;
}
}
// 네모 블럭 충돌 한 경우 (밖에서 온 경우만) 미끄러져서 다음 블럭 까지 이동
else if( bCollideBlock && cHiAttr == 0 && (fBlockX < 0 || fBlockX >= nBlockSize || fBlockY < 0 || fBlockY >= nBlockSize) ) {
int OutDirIndices[2][2] = {{7,5},{1,3}};
int OutBitX = (vMove.x > 0.f) ? 0 : 1;
int OutBitY = (vMove.y > 0.f) ? 0 : 1;
float fOutDirX = OutBitX * nBlockSize - fBlockX;
float fOutDirY = OutBitY * nBlockSize - fBlockY;
int nOutCollideDir = OutDirIndices[OutBitX][OutBitY] + ((fOutDirX * vMove.y - fOutDirY * vMove.x > 0 ) ? -1 : 1);
nOutCollideDir = (nOutCollideDir>>1)&3; // 0 2 4 6 8 -> 0 1 2 3
nDebugCode = nOutCollideDir;
if( nOutCollideDir == 2 ) {
float fLocalDa = fBlockY - nBlockSize;
float fLocalDb = fBlockNextY - nBlockSize;
if( fLocalDa >= 0 && fLocalDb <= fEpsilon )
{
ASSERT( fLocalDa >= fLocalDb );
nDebugCode |= 0x10;
float fRatio = (fLocalDa-fEpsilon) / (fLocalDa - fLocalDb);
if(fLocalDa>fEpsilon) vPos.x += vMove.x * fRatio;
vPos.y += vMove.y * fRatio;
if(fLocalDa>fEpsilon) fBlockX += vMove.x * fRatio;
fBlockY += vMove.y * fRatio;
ASSERT( (vPos.x==vPos.x&&vPos.y==vPos.y) );
vMove *= min(1, (1.f - fRatio ));
vMove.y = 0.f;
if( nSlideType == 1 ) {
vMove = EtVector2(0,0);
bBreakLoop = true;
}
nSlideType = 2;
if( fBlockNextX >= nBlockSize ) {
nDebugCode |= 0x20;
vAttrPos.x += nBlockSize;
vAttrPos.y += nBlockSize;
fBlockX -= nBlockSize;
fBlockY -= nBlockSize;
cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
// continue to next step
}
else if( fBlockNextX < 0 ) {
nDebugCode |= 0x40;
vAttrPos.x -= nBlockSize;
vAttrPos.y += nBlockSize;
fBlockX += nBlockSize;
fBlockY -= nBlockSize;
cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
// continue to next step
}
else { // end of loop
bBreakLoop = true;
}
}
else {
vPos = EtVector2(vPrevPos.x, vPrevPos.z); // Unknown Collision Error
break;
}
}
else if( nOutCollideDir == 0 ) {
float fLocalDa = fBlockY;
float fLocalDb = fBlockNextY;
if( fLocalDa <= 0 && fLocalDb >= -fEpsilon)
{
ASSERT( fLocalDa <= fLocalDb );
nDebugCode |= 0x10;
float fRatio = (-fLocalDa-fEpsilon) / (fLocalDb - fLocalDa);
if(-fLocalDa>fEpsilon) vPos.x += vMove.x * fRatio;
vPos.y += vMove.y * fRatio;
if(-fLocalDa>fEpsilon) fBlockX += vMove.x * fRatio;
fBlockY += vMove.y * fRatio;
ASSERT( (vPos.x==vPos.x&&vPos.y==vPos.y) );
vMove *= min(1, (1.f - fRatio ));
vMove.y = 0.f;
if( nSlideType == 1 ) {
vMove = EtVector2(0,0);
bBreakLoop = true;
}
nSlideType = 2;
if( fBlockNextX >= nBlockSize ) {
nDebugCode |= 0x20;
vAttrPos.x += nBlockSize;
vAttrPos.y -= nBlockSize;
fBlockX -= nBlockSize;
fBlockY += nBlockSize;
cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
// continue to next step
}
else if( fBlockNextX < 0 ) {
nDebugCode |= 0x40;
vAttrPos.x -= nBlockSize;
vAttrPos.y -= nBlockSize;
fBlockX += nBlockSize;
fBlockY += nBlockSize;
cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
// continue to next step
}
else { // end of loop
bBreakLoop = true;
}
}
else {
vPos = EtVector2(vPrevPos.x, vPrevPos.z); // Unknown Collision Error
break;
}
}
else if( nOutCollideDir == 3 ) {
float fLocalDa = fBlockX;
float fLocalDb = fBlockNextX;
if( (fLocalDa <= 0 && fLocalDb >= -fEpsilon) )
{
ASSERT( fLocalDa <= fLocalDb );
nDebugCode |= 0x10;
float fRatio = (-fLocalDa-fEpsilon) / (fLocalDb - fLocalDa);
vPos.x += vMove.x * fRatio;
if(-fLocalDa>fEpsilon) vPos.y += vMove.y * fRatio;
fBlockX += vMove.x * fRatio;
if(-fLocalDa>fEpsilon) fBlockY += vMove.y * fRatio;
ASSERT( (vPos.x==vPos.x&&vPos.y==vPos.y) );
vMove *= min( 1, (1.f - fRatio ));
vMove.x = 0.f;
if( nSlideType == 1 ) {
vMove = EtVector2(0,0);
bBreakLoop = true;
}
nSlideType = 2;
if( fBlockNextY >= nBlockSize ) {
nDebugCode |= 0x20;
vAttrPos.x -= nBlockSize;
vAttrPos.y += nBlockSize;
fBlockX += nBlockSize;
fBlockY -= nBlockSize;
cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
// continue to next step
}
else if( fBlockNextY < 0 ) {
nDebugCode |= 0x40;
vAttrPos.x -= nBlockSize;
vAttrPos.y -= nBlockSize;
fBlockX += nBlockSize;
fBlockY += nBlockSize;
cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
// continue to next step
}
else { // end of loop
bBreakLoop = true;
}
}
else {
vPos = EtVector2(vPrevPos.x, vPrevPos.z); // Unknown Collision Error
break;
}
}
else if( nOutCollideDir == 1 ) {
float fLocalDa = fBlockX - nBlockSize;
float fLocalDb = fBlockNextX - nBlockSize ;
if( fLocalDa >= 0 && fLocalDb <= fEpsilon )
{
ASSERT( fLocalDa >= fLocalDb );
nDebugCode |= 0x10;
float fRatio = (fLocalDa-fEpsilon) / (fLocalDa - fLocalDb);
vPos.x += vMove.x * fRatio;
if(fLocalDa>fEpsilon) vPos.y += vMove.y * fRatio;
fBlockX += vMove.x * fRatio;
if(fLocalDa>fEpsilon) fBlockY += vMove.y * fRatio;
ASSERT((vPos.x==vPos.x&&vPos.y==vPos.y));
vMove *= min(1, (1.f - fRatio ));
vMove.x = 0.f;
if( nSlideType == 1 ) {
vMove = EtVector2(0,0);
bBreakLoop = true;
}
nSlideType = 2;
if( fBlockNextY >= nBlockSize ) {
nDebugCode |= 0x20;
vAttrPos.x += nBlockSize;
vAttrPos.y += nBlockSize;
fBlockX -= nBlockSize;
fBlockY -= nBlockSize;
cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
// continue to next step
}
else if( fBlockNextY < 0 ) {
nDebugCode |= 0x40;
vAttrPos.x += nBlockSize;
vAttrPos.y -= nBlockSize;
fBlockX -= nBlockSize;
fBlockY += nBlockSize;
cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
// continue to next step
}
else { // end of loop
bBreakLoop = true;
}
}
else {
vPos = EtVector2(vPrevPos.x, vPrevPos.z); // Unknown Collision Error
break;
}
}
}
else { // 그외의 경우 다음 블럭까지 가는지 체크
if( fBlockNextX < 0 || fBlockNextX >= nBlockSize || fBlockNextY < 0 || fBlockNextY >= nBlockSize ) {
switch( nCollideDir )
{
case 0:
vAttrPos.y -= nBlockSize;
fBlockY += nBlockSize;
break;
case 1:
vAttrPos.x += nBlockSize;
fBlockX -= nBlockSize;
break;
case 2:
vAttrPos.y += nBlockSize;
fBlockY -= nBlockSize;
break;
case 3:
vAttrPos.x -= nBlockSize;
fBlockX += nBlockSize;
break;
}
cAttr = INSTANCE(CDnWorld).GetAttribute( vAttrPos.x, vAttrPos.y );
nBlockSize = INSTANCE(CDnWorld).GetAttributeBlockSize( vAttrPos.x, vAttrPos.y );
nDebugCode = 255;
// continue to next step
}
else {
bBreakLoop = true;
}
}
#if( _USE_COLLISION_DEBUG )
debugCode.push_back( nDebugCode );
#endif
if( bBreakLoop ) {
vPos.x += vMove.x;
vPos.y += vMove.y;
ASSERT((vPos.x==vPos.x&&vPos.y==vPos.y));
}
}while( !bBreakLoop );
m_pMatExWorld->m_vPosition.x = vPos.x;
m_pMatExWorld->m_vPosition.z = vPos.y;
bool bClash = true;
char cAttr2 = INSTANCE(CDnWorld).GetAttribute( m_pMatExWorld->m_vPosition );
if( ( cAttr2 & 0x0f ) == 1 || ( cAttr2 & 0x0f ) == 2 ) {
char cHiAttr = ( cAttr2 & 0xf0 ) >> 4;
if( cHiAttr != 0 )
{
if( !CheckDiagonalBlock( m_pMatExWorld->m_vPosition.x, m_pMatExWorld->m_vPosition.z ) ) bClash = false;
}
else bClash = false;
}
if( bClash == false )
{
m_pMatExWorld->m_vPosition.x = vPrevPos.x;
m_pMatExWorld->m_vPosition.z = vPrevPos.z;
}
else {
if( ( cAttr & 0x0f ) == 2 && EtVec3LengthSq( &( m_pMatExWorld->m_vPosition - vPrevPos ) ) > 0.f ) {
#ifdef _SHADOW_TEST
if( !m_pActor->IsShadowActor() ) OnClash( m_pMatExWorld->m_vPosition, eCFCT_None ); // note by kalliste : 이 지점에서 바닥충돌 관련 검출이 필요 없어 None으로 셋팅. 이후 필요할 경우 NoFloor 셋팅할 것.
#else
OnClash( m_pMatExWorld->m_vPosition, eCFCT_None );
#endif
}
}
}
void MAWalkMovement::ProcessProp( EtVector3 vPrevPos )
{
m_bFloorCollision = false;
if( m_pActor->GetObjectHandle() )
{
#ifdef _GAMESERVER // 서버에서는 정밀하게 할필요 없어서 크게 한다.
const float fMinMove = 20.0f;
#else
const float fMinMove = 5.0f;
#endif
EtVector3 vDiffNormal;
float fLength;
vDiffNormal = m_pMatExWorld->m_vPosition - vPrevPos;
fLength = EtVec3Length( &vDiffNormal );
if( fLength <= fMinMove )
{
ProcessCollision( m_pActor, m_pActor->GetObjectHandle(), vPrevPos, m_bFloorCollision );
}
else
{
EtVector3 vTemp = m_pMatExWorld->m_vPosition;
vDiffNormal /= fLength;
int nCount = 0;
EtVector3 vCurPrevPos = vPrevPos;
while( fLength > 0.0f )
{
if( fLength > fMinMove )
{
m_pMatExWorld->m_vPosition = vCurPrevPos + fMinMove * vDiffNormal;
fLength -= fMinMove;
}
else
{
m_pMatExWorld->m_vPosition = vCurPrevPos + fLength * vDiffNormal;
fLength = 0.0f;
}
ProcessCollision( m_pActor, m_pActor->GetObjectHandle(), vCurPrevPos, m_bFloorCollision );
m_pActor->GetObjectHandle()->Update( *m_pMatExWorld );
vCurPrevPos = m_pMatExWorld->m_vPosition;
nCount++;
#ifdef _GAMESERVER
if( nCount == 50 )
#else
if( nCount == 200 )
#endif
{
m_pMatExWorld->m_vPosition = vCurPrevPos;
break;
}
}
}
}
if( m_bFloorCollision ) {
if( m_pActor->GetAddHeight() < 0.f ) m_bFloorCollision = false;
}
if( m_bFloorCollision == false )
{
if( m_pActor->GetAddHeight() != 0.f )
{
if( m_vVelocity.y == 0.f )
{
m_vVelocity.y = -1.f;
m_vVelocityResist.y = -20.f;
m_vJumpMovement = EtVector2( 0.f, 0.f );
m_bFloorForceVelocity = true;
}
}
m_pActor->SetAddHeight( 0.f );
}
if( m_bFloorCollision == false && m_vVelocity.y == 0.f )
{
#ifdef PRE_FIX_FALL_PROP_COLLISION
m_vVelocity.y = -1.f;
m_vVelocityResist.y = -20.f;
m_vJumpMovement = EtVector2( 0.f, 0.f );
m_bFloorForceVelocity = true;
#else
float fHeight = INSTANCE(CDnWorld).GetHeightWithProp( EtVector3( m_pMatExWorld->m_vPosition.x, m_pMatExWorld->m_vPosition.y + 100.f, m_pMatExWorld->m_vPosition.z ) );
float fCurHeight = fHeight + m_pActor->GetAddHeight();
m_pMatExWorld->m_vPosition.y = fCurHeight;
#endif
}
}
void MAWalkMovement::Process( LOCAL_TIME LocalTime, float fDelta )
{
// m_fLastHeight = m_pMatEx->m_vPosition.y;
ProcessCommon( LocalTime, fDelta );
#ifndef _GAMESERVER
DebugRenderAttr();
#endif
EtVector3 vPrevPos = *m_pActor->GetPrevPosition();
ProcessProp( vPrevPos );
PostProcess( LocalTime );
}
void MAWalkMovement::ProcessCollision( CDnActor *pActor, EtObjectHandle hObject, const EtVector3 &vPrevPos, bool &bFloorCollision )
{
#ifdef _WORK
if( GetAsyncKeyState( VK_CONTROL ) < 0 ) return;
#endif
int nLoop, i;
float fMoveDist;
EtVector3 vMoveDir;
DNVector(SCollisionResponse) &vecResult = m_vecResult;
vecResult.clear();
vMoveDir = m_pMatExWorld->m_vPosition - vPrevPos;
fMoveDist = EtVec3Length( &vMoveDir );
if( fMoveDist == 0.0f ) {
return;
}
vMoveDir /= fMoveDist;
if( hObject->FindCollision( *m_pMatExWorld, vecResult ) )
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 올라갈 수 있나 체크.
bool bIsClimb = CheckClimb( hObject, vPrevPos, vecResult, bFloorCollision );
if( bIsClimb )
{
float fLandHeight = INSTANCE(CDnWorld).GetHeight( m_pMatExWorld->m_vPosition );
m_pActor->SetAddHeight( m_pMatExWorld->m_vPosition.y - fLandHeight );
return;
}
if( fMoveDist <= 0.0f )
{
if( m_pActor->GetAddHeight() > 0.f )
bFloorCollision = true;
return;
}
for( nLoop = 0; nLoop < 5; nLoop++ )
{
SCollisionResponse CurResponse;
CurResponse.fContactTime = FLT_MAX;
for( i = 0; i < ( int )vecResult.size(); i++ )
{
EtVector3 vCurNormal = vecResult[ i ].vNormal;
// 충돌은 났지만 노말의 크기가 1.0이 안되면 슬라이딩 후보에 들어가지 않음
/* if( EtVec3LengthSq( &vCurNormal ) <= 0.5f )
{
continue;
}*/
if( EtVec3Dot( &vCurNormal, &vMoveDir ) > 0.0f ) // 벽의노말과 이동방향이 같으면 무시
{
continue;
}
if( vecResult[ i ].fContactTime < CurResponse.fContactTime )
{
CurResponse = vecResult[ i ];
}
}
// 슬라이딩할만한 충돌면을 못찾았으면 예전 포지션으로 돌린다.
if( CurResponse.fContactTime == FLT_MAX )
{
// m_pMatEx->m_vPosition = vPrevPos;
break;
}
bool bStopSlide = false;
bool bIsWall = true;
if( EtVec3Dot( &CurResponse.vNormal, &EtVector3( 0.0f, 1.0f, 0.0f ) ) > 0.707106f ) // 45도 체크
{
if( vMoveDir.y < -0.9999f ) // 경사면에 가만히 서 있을때는 안미끄러져야 한다.
{
m_pMatExWorld->m_vPosition.y = vPrevPos.y;
bStopSlide = true;
}
bIsWall = false;
bFloorCollision = true;
}
else if( EtVec3Dot( &CurResponse.vNormal, &EtVector3( 0.0f, -1.0f, 0.0f ) ) > 0.707106f ) // 45도 천장체크
{
bIsWall = false;
}
if( !bStopSlide )
{
EtVector3 vSlideDir;
float fWeight;
CalcSlide( vSlideDir, fWeight, CurResponse.vNormal, vMoveDir );
if( ( bIsWall ) && ( vSlideDir.y > 0.0f ) && ( vMoveDir.y <= 0.0f ) ) // 벽면은 기어올라 갈수 없지만.. vMoveDir.y > 0.0f 커서 점프 일때는 슬라이딩 일어나야 한다.
{
EtVector3 vWallNormal;
vWallNormal = CurResponse.vNormal;
vWallNormal.y = 0.0f;
EtVec3Normalize( &vWallNormal, &vWallNormal );
CalcSlide( vSlideDir, fWeight, vWallNormal, vMoveDir );
}
else if( ( bIsWall ) && ( vSlideDir.y < 0.0f ) ) // 땅 위에 서있는채로 더 큰 물체의 아래에 파고드려하는 경우인데, 가게하면 안된다. 앞으로 가게 하면 컬리전을 파고들어버리게 된다.
{
if( m_pActor->GetState() & CDnActorState::Move && (m_pActor->GetState() & CDnActorState::Air) == false )
{
EtVector3 vWallNormal;
vWallNormal = CurResponse.vNormal;
vWallNormal.y = 0.0f;
EtVec3Normalize( &vWallNormal, &vWallNormal );
CalcSlide( vSlideDir, fWeight, vWallNormal, vMoveDir );
}
}
vMoveDir = vSlideDir;
fMoveDist *= fWeight;
m_pMatExWorld->m_vPosition = vPrevPos + vMoveDir * fMoveDist;
}
vecResult.clear();
if( hObject->FindCollision( *m_pMatExWorld, vecResult ) )
{
for( i = ( int )vecResult.size() - 1; i >= 0; i-- )
{
if( EtVec3Dot( &vecResult[ i ].vNormal, &CurResponse.vNormal ) > 0.996194f ) // 충돌 노멀이 5도 정도 밖에 차이 안나면 같은 면으로 생각해서 스킵
{
vecResult.erase( vecResult.begin() + i );
}
}
}
if( vecResult.empty() )
{
break;
}
}
if( nLoop == 5 )
{
if( bFloorCollision )
{
float fLandHeight;
fLandHeight = INSTANCE(CDnWorld).GetHeight( m_pMatExWorld->m_vPosition );
if( m_pMatExWorld->m_vPosition.y > fLandHeight )
{
m_pActor->SetAddHeight( m_pMatExWorld->m_vPosition.y - fLandHeight );
}
m_pMatExWorld->m_vPosition = vPrevPos;
}
else // 바닥과 충돌이 안났을때는 x,z만 Previous로 바꿔준다. 두벽사이의 모서리에 끼어서 점프 했을때 계속 slide 하면서 이동 못하고 여기 들어오는 경우 있다.
{
m_pMatExWorld->m_vPosition.x = vPrevPos.x;
m_pMatExWorld->m_vPosition.z = vPrevPos.z;
// 바닥과 충돌이 안나더라도 충돌 체크해보고 y도 못움직이면 y도 옛날 위치로 바꿔줌
vecResult.clear();
if( hObject->FindCollision( *m_pMatExWorld, vecResult ) )
{
for( i = 0; i < ( int )vecResult.size(); i++ )
{
// 벽사이에 비비다 보면 벽과 충돌이 날수도 있어서 벽은 제외한다.
if( EtVec3Dot( &vecResult[ i ].vNormal, &EtVector3( 0.0f, 1.0f, 0.0f ) ) > 0.087155f )
{
m_pMatExWorld->m_vPosition.y = vPrevPos.y;
bFloorCollision = true;
break;
}
}
}
}
}
else
{
float fLandHeight;
fLandHeight = INSTANCE(CDnWorld).GetHeight( m_pMatExWorld->m_vPosition );
if( fLandHeight > m_pMatExWorld->m_vPosition.y )
{
m_pMatExWorld->m_vPosition.y = fLandHeight;
}
else if( bFloorCollision )
{
// 바닥 충돌보다 기울어진 벽충돌이 먼저 일어 나게 되면 밑으로 슬라이딩 처리가 되서.. y값이 이미 낮아져 있다.
// 그런 상태에서 바닥 충돌이 나게 되면 계속 아래쪽으로 꺼지는 현상이 있어서 이전 y값으로 돌려준다.
if( m_pMatExWorld->m_vPosition.y < vPrevPos.y )
{
m_pMatExWorld->m_vPosition.y = vPrevPos.y;
}
m_pActor->SetAddHeight( m_pMatExWorld->m_vPosition.y - fLandHeight );
}
}
#ifdef _SHADOW_TEST
if( !m_pActor->IsShadowActor() ) OnClash( m_pMatExWorld->m_vPosition, bFloorCollision ? eCFCT_Floor : eCFCT_NoFloor );
#else
OnClash( m_pMatExWorld->m_vPosition, bFloorCollision ? eCFCT_Floor : eCFCT_NoFloor );
#endif
char cAttr = INSTANCE(CDnWorld).GetAttribute( m_pMatExWorld->m_vPosition );
if( ( cAttr & 0x0f ) == 1 || ( cAttr & 0x0f ) == 2 ) // 0x01, 0x02 는 충돌체크
{
m_pMatExWorld->m_vPosition.x = vPrevPos.x;
m_pMatExWorld->m_vPosition.z = vPrevPos.z;
}
}
}
bool MAWalkMovement::CheckClimb( EtObjectHandle hObject, const EtVector3 &vPrevPos, DNVector(SCollisionResponse) &vecResult, bool &bFloorCollision )
{
int i;
float fMinContactTime;;
SCollisionResponse Response;
const float fClimbBasis = MAX_CLIMB_HEIGHT;
SCollisionPrimitive *pFloor;
pFloor = NULL;
fMinContactTime = 1.0f;
for( i = 0; i < ( int )vecResult.size(); i++ )
{
if( EtVec3Dot( &vecResult[ i ].vNormal, &EtVector3( 0.0f, 1.0f, 0.0f ) ) > 0.707106f )
{
hObject->CEtCollisionEntity::FindCollision( *vecResult[ i ].pCollisionPrimitive, EtVector3( 0.0f, -fClimbBasis, 0.0f ), Response, true );
if( ( Response.fContactTime >= 0.0f ) && ( fMinContactTime > Response.fContactTime ) )
{
fMinContactTime = Response.fContactTime;
pFloor = vecResult[ i ].pCollisionPrimitive;
}
}
}
if( fMinContactTime != 1.0f )
{
float fMoveHeight;
fMoveHeight = ( 1.0f - fMinContactTime ) * fClimbBasis;
if( fMoveHeight - ( vPrevPos.y - m_pMatExWorld->m_vPosition.y ) <= MAX_CLIMB_HEIGHT )
{
DNVector(SCollisionResponse) &vecClimbResult = m_vecResult;
vecClimbResult.clear();
bFloorCollision = true;
m_pMatExWorld->m_vPosition.y += fMoveHeight;
hObject->FindCollision( *m_pMatExWorld, vecClimbResult );
for( i = 0; i < ( int )vecClimbResult.size(); i++ )
{
if( vecClimbResult[ i ].pCollisionPrimitive != pFloor )
{
break;
}
}
if( i < ( int )vecClimbResult.size() )
{
m_pMatExWorld->m_vPosition.y -= fMoveHeight;
return false;
}
bFloorCollision = true;
return true;
}
}
return false;
}
void MAWalkMovement::CalcSlide( EtVector3 &SlideDir, float &fWeight, EtVector3 &CollisionNormal, EtVector3 &MoveDir )
{
EtVector3 WallCross;
EtVec3Cross( &WallCross, &CollisionNormal, &MoveDir );
EtVec3Normalize( &WallCross, &WallCross );
EtVec3Cross( &SlideDir, &WallCross, &CollisionNormal );
EtVec3Normalize( &SlideDir, &SlideDir );
fWeight = fabs( EtVec3Dot( &SlideDir, &MoveDir ) );
}
void MAWalkMovement::PushAndCollisionCheck( EtVector3 &vPush )
{
EtVector3 vPrevPos;
bool bFloorCollision;
vPrevPos = m_pMatExWorld->m_vPosition;
m_pMatExWorld->m_vPosition += vPush;
ProcessCollision( m_pActor, m_pActor->GetObjectHandle(), vPrevPos, bFloorCollision );
}
void MAWalkMovement::SetLimitDgrees( float fDgrees )
{
m_fLimitDgrees = fDgrees;
}
void MAWalkMovement::MoveZ( float fSpeed )
{
m_vMovement += EtVector2( m_vMoveVectorZ.x, m_vMoveVectorZ.z ) * fSpeed;
}
void MAWalkMovement::MoveX( float fSpeed )
{
m_vMovement += EtVector2( m_vMoveVectorX.x, m_vMoveVectorX.z ) * fSpeed;
}
void MAWalkMovement::SetMoveVectorX( EtVector3 &vVec )
{
m_vMoveVectorX = vVec;
}
void MAWalkMovement::SetMoveVectorZ( EtVector3 &vVec )
{
m_vMoveVectorZ = vVec;
}
void MAWalkMovement::Jump( float fPower, EtVector2 &vVec )
{
m_vLastVelocity.y = fPower;
m_vVelocity.y += fPower;
m_vJumpMovement = vVec;
m_vJumpXVector = m_vMoveVectorX;
m_vJumpZVector = m_vMoveVectorZ;
m_fJumpMoveSpeed = (float)m_pActor->GetMoveSpeed();
m_bFloorForceVelocity = false;
}
void MAWalkMovement::MoveJumpX( float fSpeed )
{
if( m_vJumpMovement.x > 0.f && fSpeed > 0.f ) return;
if( m_vJumpMovement.x < 0.f && fSpeed < 0.f ) return;
if( fSpeed > 0.f ) fSpeed = -fSpeed;
m_fJumpMoveSpeed += fSpeed;
if( m_fJumpMoveSpeed < 0.f ) m_fJumpMoveSpeed = 0.f;
}
void MAWalkMovement::MoveJumpZ( float fSpeed )
{
if( m_vJumpMovement.y > 0.f && fSpeed > 0.f ) return;
if( m_vJumpMovement.y < 0.f && fSpeed < 0.f ) return;
if( fSpeed > 0.f ) fSpeed = -fSpeed;
m_fJumpMoveSpeed += fSpeed;
if( m_fJumpMoveSpeed < 0.f ) m_fJumpMoveSpeed = 0.f;
}
void MAWalkMovement::SetVelocityByMoveVector( EtVector3 &vVec )
{
#ifdef PRE_MOD_VELOCITYACCEL_SIGNAL
// 컬리전 위에 올라가있는 상태에서 STE_VelocityAccel 시그널이 올때 문제가 발생할 가능성이 있다.
// 현재 컬리전 위에 올라가있을땐 가만히 세워둔 상태임에도 불구하고,
// 계속해서 OnDrop과 OnFall, m_bFloorCollision값 등이 수시로 바뀌면서 내부 코드가 돌게 되어있다.
// (물론 보이기엔 컬리전 위에 가만 있는 상태다.)
// 이 상태에서 STE_VelocityAccel 시그널이 와서 m_vVelocity의 y값을 0으로 초기화시켜버리면,
// 바닥에 있던 컬리전을 뚫고 아래로 빠져버린다.
// 그래서 컬리전 위에 올라가있는 상태에서 STE_VelocityAccel 시그널의 y가 0이라서 높이에 영향을 주지 않는거라면,
// 기존의 m_vVelocity.y 값을 유지하도록 하겠다.
// 이것과 더불이 항상 같이 쓰이는게 VelocityResist라서 이 값도 같은 처리를 해준다.
// 아래 m_vLastVelocity는 쓰이는데가 없는거 같아서 제외시킴.
bool bKeepVelocityY = false;
float fKeepVelocityY = 0.0f;
if( m_bLastFloorForceVelocity && vVec.y == 0.0f )
{
bKeepVelocityY = true;
fKeepVelocityY = m_vVelocity.y;
}
#endif
m_vVelocity = vVec;
#ifdef PRE_MOD_VELOCITYACCEL_SIGNAL
if( bKeepVelocityY ) m_vVelocity.y = fKeepVelocityY;
#endif
m_vVelocityXVector = m_vMoveVectorX;
m_vVelocityYVector = m_pMatExWorld->m_vYAxis;
m_vVelocityZVector = m_vMoveVectorZ;
m_vJumpMovement = EtVector2( 0.f, 0.f );
m_vLastVelocity = vVec;
m_bFloorForceVelocity = false;
}
void MAWalkMovement::SetVelocity( EtVector3 &vVec )
{
m_vVelocity = vVec;
m_vVelocityXVector = m_pMatExWorld->m_vXAxis;
m_vVelocityYVector = m_pMatExWorld->m_vYAxis;
m_vVelocityZVector = m_pMatExWorld->m_vZAxis;
m_vJumpMovement = EtVector2( 0.f, 0.f );
m_vLastVelocity = vVec;
m_bFloorForceVelocity = false;
}
void MAWalkMovement::SetVelocityX( float fPower )
{
m_vVelocity.x = fPower;
m_vVelocityXVector = m_pMatExWorld->m_vXAxis;
m_vLastVelocity.x = fPower;
}
void MAWalkMovement::SetVelocityY( float fPower )
{
m_vVelocity.y = fPower;
m_vVelocityYVector = m_pMatExWorld->m_vYAxis;
m_vJumpMovement = EtVector2( 0.f, 0.f );
m_vLastVelocity.y = fPower;
m_bFloorForceVelocity = false;
}
void MAWalkMovement::SetVelocityZ( float fPower )
{
m_vVelocity.z = fPower;
m_vVelocityZVector = m_pMatExWorld->m_vZAxis;
m_vLastVelocity.z = fPower;
}
void MAWalkMovement::SetResistance( EtVector3 &vVec )
{
#ifdef PRE_MOD_VELOCITYACCEL_SIGNAL
bool bKeepVelocityResistY = false;
float fKeepVelocityResistY = 0.0f;
if( m_bLastFloorForceVelocity && vVec.y == 0.0f )
{
bKeepVelocityResistY = true;
fKeepVelocityResistY = m_vVelocityResist.y;
}
#endif
m_vVelocityResist = vVec;
#ifdef PRE_MOD_VELOCITYACCEL_SIGNAL
if( bKeepVelocityResistY ) m_vVelocityResist.y = fKeepVelocityResistY;
#endif
}
void MAWalkMovement::SetResistanceX( float fPower )
{
m_vVelocityResist.x = fPower;
}
void MAWalkMovement::SetResistanceY( float fPower )
{
m_vVelocityResist.y = fPower;
}
void MAWalkMovement::SetResistanceZ( float fPower )
{
m_vVelocityResist.z = fPower;
}
void MAWalkMovement::MovePos( EtVector3 &vPos, bool bRefreshZVec )
{
m_bEnableNaviMode = false;
m_vMovePos = vPos;
m_bRefreshZVector = bRefreshZVec;
if( bRefreshZVec ) {
m_vTargetLookVec.x = vPos.x - m_pMatExWorld->m_vPosition.x;
m_vTargetLookVec.y = vPos.z - m_pMatExWorld->m_vPosition.z;
EtVec2Normalize( &m_vTargetLookVec, &m_vTargetLookVec );
}
#ifndef _GAMESERVER
m_fLastMoveDelta = 0.f;
m_nLastMoveCount = 0;
#endif
}
void MAWalkMovement::MoveTarget( DnActorHandle &hActor, float fMinDistance )
{
m_hMoveTarget = hActor;
m_fTargetMinDistance = fMinDistance;
m_bRefreshZVector = true;
m_bEnableNaviMode = false;
}
DnActorHandle MAWalkMovement::GetMoveTarget()
{
return m_hMoveTarget;
}
void MAWalkMovement::MoveTargetNavi( DnActorHandle &hActor, float fMinDistance, const char* szActionName )
{
MoveTargetNavi( *hActor->GetPosition(), fMinDistance, szActionName );
if( m_bEnableNaviMode )
{
m_hNaviTarget = hActor;
m_nNaviType = NaviType::eTarget;
}
}
void MAWalkMovement::MoveTargetNavi( EtVector3& vTargetPos, float fMinDistance, const char* szActionName )
{
m_fNaviTargetMinDistance = fMinDistance;
m_szNaviTargetActionName = szActionName;
m_hLookTarget.Identity();
m_WayPointList.clear();
m_fTargetMinDistance = fMinDistance;
m_bEnableNaviMode = false;
m_nNaviType = NaviType::eMax;
m_bRefreshZVector = true;
EtVector3 vStartPos = this->m_pMatExWorld->m_vPosition;
EtVector3 vEndPos = vTargetPos;
if( m_bRefreshZVector )
{
m_vTargetLookVec.x = vTargetPos.x - m_pMatExWorld->m_vPosition.x;
m_vTargetLookVec.y = vTargetPos.z - m_pMatExWorld->m_vPosition.z;
EtVec2Normalize( &m_vTargetLookVec, &m_vTargetLookVec );
}
CEtWorldGrid* pGrid = INSTANCE(CDnWorld).GetGrid();
if( pGrid == NULL )
return;
NavigationMesh* pNaviMesh = pGrid->GetNavMesh(vStartPos);
if( pNaviMesh == NULL )
return;
NavigationCell* pStartCell = pNaviMesh->FindClosestCell( vStartPos );
NavigationCell* pEndCell = pNaviMesh->FindClosestCell( vEndPos );
if ( !pStartCell || !pEndCell )
return;
NavigationPath naviPath;
pNaviMesh->BuildNavigationPath( naviPath, pStartCell, vStartPos, pEndCell, vEndPos );
WAYPOINT_LIST& fur_waypoint_list = naviPath.LineOfSightWayPointList();
if( fur_waypoint_list.empty() )
return;
m_WayPointList = fur_waypoint_list;
m_WayPointId = m_WayPointList.begin();
m_WayPointId++; // 맨첨위치는 현재 위치기 때문에 다음것으로 세팅
OnMoveNavi( (EtVector3)(*m_WayPointId).Position );
m_bEnableNaviMode = true;
m_nNaviType = NaviType::ePosition;
}
#ifdef PRE_MOD_NAVIGATION_PATH
void MAWalkMovement::AutoMoving( EtVector3& vTargetPos, float fMinDistance, const char* szActionName, bool bGeneratePath )
{
m_fNaviTargetMinDistance = fMinDistance;
m_szNaviTargetActionName = szActionName;
m_hLookTarget.Identity();
m_WayPointList.clear();
m_fTargetMinDistance = fMinDistance;
m_bEnableNaviMode = false;
m_nNaviType = NaviType::eMax;
m_bRefreshZVector = true;
EtVector3 vStartPos = this->m_pMatExWorld->m_vPosition;
EtVector3 vEndPos = vTargetPos;
if( m_bRefreshZVector )
{
m_vTargetLookVec.x = vTargetPos.x - m_pMatExWorld->m_vPosition.x;
m_vTargetLookVec.y = vTargetPos.z - m_pMatExWorld->m_vPosition.z;
EtVec2Normalize( &m_vTargetLookVec, &m_vTargetLookVec );
}
CEtWorldGrid* pGrid = INSTANCE(CDnWorld).GetGrid();
if( pGrid == NULL )
return;
NavigationMesh* pNaviMesh = pGrid->GetNavMesh(vStartPos);
if( pNaviMesh == NULL )
return;
NavigationCell* pStartCell = pNaviMesh->FindClosestCell( vStartPos );
NavigationCell* pEndCell = pNaviMesh->FindClosestCell( vEndPos );
// 네비게이션 메쉬를 벗어나 생기는 정점에 대해 고려가 필요함
if ( !pStartCell || !pEndCell )
return;
if( bGeneratePath )
GenerateAutoMovingPath( pNaviMesh, pStartCell, pEndCell, vStartPos, vEndPos );
m_bEnableNaviMode = true;
m_nNaviType = NaviType::ePosition;
}
// Solve the Catmull-Rom parametric equation for a given time(t) and vector quadruple (p1,p2,p3,p4)
EtVector3 Eq(float t, const EtVector3& p1, const EtVector3& p2, const EtVector3& p3, const EtVector3& p4)
{
float t2 = t * t;
float t3 = t2 * t;
float b1 = static_cast<float>( .5 * ( -t3 + 2*t2 - t) );
float b2 = static_cast<float>( .5 * ( 3*t3 - 5*t2 + 2) );
float b3 = static_cast<float>( .5 * (-3*t3 + 4*t2 + t) );
float b4 = static_cast<float>( .5 * ( t3 - t2 ) );
return (p1*b1 + p2*b2 + p3*b3 + p4*b4);
}
EtVector3 GetInterpolatedSplinePoint(std::vector<EtVector3>& vecList, float t)
{
float delta_t = 1.0f / static_cast<float>(vecList.size() - 1);
// Find out in which interval we are on the spline
int p = (int)(t / delta_t);
// Compute local control point indices
#define BOUNDS(pp) { if (pp < 0) pp = 0; else if (pp >= static_cast<int>( vecList.size() )) pp = static_cast<int>( vecList.size() ) - 1; }
int p0 = p - 1; BOUNDS(p0);
int p1 = p; BOUNDS(p1);
int p2 = p + 1; BOUNDS(p2);
int p3 = p + 2; BOUNDS(p3);
// Relative (local) time
float lt = (t - delta_t*(float)p) / delta_t;
// Interpolate
return Eq(lt, vecList[p0], vecList[p1], vecList[p2], vecList[p3]);
}
void MAWalkMovement::GenerateAutoMovingPath( NavigationMesh* pNaviMesh, NavigationCell* pStartCell, NavigationCell* pEndCell, EtVector3& vStartPos, EtVector3& vEndPos )
{
if( pNaviMesh == NULL || pStartCell == NULL || pEndCell == NULL )
return;
NavigationPath naviPath;
pNaviMesh->BuildNavigationPath( naviPath, pStartCell, vStartPos, pEndCell, vEndPos );
WAYPOINT_LIST& fur_waypoint_list = naviPath.LineOfSightWayPointList();
m_LOSWayPointList.clear();
std::list<WAYPOINT>::iterator iter1 = fur_waypoint_list.begin();
for( ; iter1 != fur_waypoint_list.end(); iter1++ )
m_LOSWayPointList.push_back( (*iter1) );
if( fur_waypoint_list.size() < 2 ) // 직선으로 경로가 나올 경우 Pass
return;
// 꺽이는 부분만 쪼개서 곡선화
WAYPOINT_LIST waypointlist;
int nInnerNodeCount = static_cast<int>( fur_waypoint_list.size() ) - 2; // 시작과 끝점을 제외한 중간 노드 갯 수
std::list<WAYPOINT>::iterator iterCurrent = fur_waypoint_list.begin();
// 갱신되는 노드 셋팅
waypointlist.push_back( *iterCurrent ); // 시작 노드
iterCurrent++; // 시작 노드의 바로 다음 노드 부터 시작
for( int i=0; i<nInnerNodeCount; i++, iterCurrent++ )
{
std::list<WAYPOINT>::iterator iterPrev = iterCurrent;
iterPrev--;
std::list<WAYPOINT>::iterator iterNext = iterCurrent;
iterNext++;
std::vector<EtVector3> tempvectorlist;
EtVector3 vecTarget = (*iterPrev).Position - (*iterCurrent).Position; // 현재 노드에서 이전노드 방향벡터
float fLength = EtVec3Length( &vecTarget );
float fPointLength = 0.0f;
if( fLength > 800.0f ) // 8m가 넘으면 앞뒤 4m를 5개 포인트로 쪼갬
fPointLength = 400.0f;
else // 8m가 넘지 않는다면 중간지점 까지만 쪼갬
fPointLength = fLength / 2.0f;
EtVector3 vecNormal;
EtVec3Normalize( &vecNormal, &vecTarget );
tempvectorlist.push_back( (*iterCurrent).Position + ( vecNormal * fPointLength ) );
tempvectorlist.push_back( (*iterCurrent).Position + ( vecNormal * ( fPointLength / 2.0f ) ) );
tempvectorlist.push_back( (*iterCurrent).Position );
vecTarget = (*iterNext).Position - (*iterCurrent).Position; // 현재 노드에서 다음노드 방향벡터
fLength = EtVec3Length( &vecTarget );
if( fLength > 800.0f ) // 8m가 넘으면 앞뒤 4m를 5개 포인트로 쪼갬
fPointLength = 400.0f;
else // 8m가 넘지 않는다면 중간지점 까지만 쪼갬
fPointLength = fLength / 2.0f;
EtVec3Normalize( &vecNormal, &vecTarget );
tempvectorlist.push_back( (*iterCurrent).Position + ( vecNormal * ( fPointLength / 2.0f ) ) );
tempvectorlist.push_back( (*iterCurrent).Position + ( vecNormal * fPointLength ) );
if( tempvectorlist.size() == 0 )
continue;
// 곡선 보간
WAYPOINT_LIST interpolationlist;
int nDivide = 40;
for( int j=0; j<nDivide; j++ )
{
WAYPOINT waypoint;
float fTime = static_cast<float>(j) / static_cast<float>(nDivide);
waypoint.Position = GetInterpolatedSplinePoint( tempvectorlist, fTime );
waypointlist.push_back( waypoint );
}
}
std::list<WAYPOINT>::iterator iterEnd = fur_waypoint_list.end();
iterEnd--;
waypointlist.push_back( *iterEnd ); // 마지막 노드
// 곡선 생성했으면 곡선의 정점이 네비게이션 메쉬안에 존재하는지 검사하여
// 존재하지 않는 정점은 어떻게 해야할까??????
m_WayPointList = waypointlist;
m_WayPointId = m_WayPointList.begin();
}
#endif // PRE_MOD_NAVIGATION_PATH
void MAWalkMovement::MoveToWorld( EtVector2 &vVec )
{
m_vMovement += vVec;
}
void MAWalkMovement::ResetMove()
{
m_hMoveTarget.Identity();
m_vMovePos = EtVector3( 0.f, 0.f, 0.f );
m_vMagnetDir = EtVector2( 0.f, 0.f );
m_fMagnetLength = 0.f;
m_bEnableNaviMode = false;
}
void MAWalkMovement::Look( EtVector2 &vVec, bool bForceRotate )
{
if( bForceRotate ) {
if( EtVec2LengthSq( &vVec ) > 0.f ) {
m_pMatExWorld->m_vZAxis = EtVector3( vVec.x, 0.f, vVec.y );
EtVec3Normalize( &m_pMatExWorld->m_vZAxis, &m_pMatExWorld->m_vZAxis );
m_pMatExWorld->MakeUpCartesianByZAxis();
}
m_vTargetLookVec = EtVector2( 0.f, 0.f );
}
else {
m_vTargetLookVec = vVec;
}
}
EtVector3 *MAWalkMovement::GetLookDir()
{
return &m_pMatExWorld->m_vZAxis;
}
void MAWalkMovement::LookTarget( DnActorHandle &hActor )
{
m_hLookTarget = hActor;
if ( m_hLookTarget )
{
OnBeginLook();
}
else
{
OnEndLook();
}
}
void MAWalkMovement::ResetLook()
{
m_hLookTarget.Identity();
m_vTargetLookVec = EtVector2( 0.f, 0.f );
}
DnActorHandle MAWalkMovement::GetLookTarget()
{
return m_hLookTarget;
}
void MAWalkMovement::SetMagnetDir( EtVector2 &vPos )
{
m_vMagnetDir = vPos;
}
void MAWalkMovement::SetMagnetLength( float fLength )
{
m_fMagnetLength = fLength;
}
EtVector3 *MAWalkMovement::GetVelocity()
{
return &m_vVelocity;
}
EtVector3 *MAWalkMovement::GetResistance()
{
return &m_vVelocityResist;
}
EtVector3 *MAWalkMovement::GetLastVelocity()
{
return &m_vLastVelocity;
}
EtVector3 *MAWalkMovement::GetVelocityValue()
{
return &m_vLastVelocityValue;
}
void MAWalkMovement::SetMagnetPosition( EtVector3 &vPos )
{
EtVector2 vMagnet;
float fHeightDiff = vPos.y - m_pMatExWorld->m_vPosition.y;
vMagnet.x = vPos.x - m_pMatExWorld->m_vPosition.x;
vMagnet.y = vPos.z - m_pMatExWorld->m_vPosition.z;
float fAberraionLength = EtVec2Length( &vMagnet );
if( ( fAberraionLength > 1.f && fAberraionLength < 200.f ) && fHeightDiff < 200.f ) {
SetMagnetLength( fAberraionLength );
EtVec2Normalize( &vMagnet, &vMagnet );
SetMagnetDir( vMagnet );
}
else {
m_pActor->SetPosition( vPos );
}
}
void MAWalkMovement::SetMoveYDistancePerSec( float fMoveYDistancePerSec, float fWholeMoveYDistance, bool bMaintainYDistanceOnArriveDestPosition )
{
if( (0.0f < m_fMoveYDistancePerSec && 0.0f < fMoveYDistancePerSec) ||
(0.0f > m_fMoveYDistancePerSec && 0.0f > fMoveYDistancePerSec) )
return;
// 이 값은 음수가 될 수도 있지만,
m_fMoveYDistancePerSec = fMoveYDistancePerSec;
// 이 값은 거리를 의미하기 때문에 무조건 양수이다.
_ASSERT( 0.0f < fWholeMoveYDistance );
m_fLeftMoveYDistance = fWholeMoveYDistance;
m_bAppliedYDistance = true;
// 목표로 되어있는 거리만큼 이동했을 때 Y 값을 더했다는 플래그 m_bAppliedYDistance 를
// 유지할 것인가.
// 이 플래그로 나중에 WalkMovementNavi 에서 바닥과의 높이를 맞추는 루틴을 할지 말지
// 결정하기 때문에 시그널이 끝나고 유지하지 않으면 MoveY 시그널이 끝나고 곧바로 다시 바닥에 붙게 된다.
m_bMaintainYDistanceOnArriveDestPosition = bMaintainYDistanceOnArriveDestPosition;
}
void MAWalkMovement::ResetMoveYDistance( void )
{
m_fMoveYDistancePerSec = 0.0f;
m_fLeftMoveYDistance = 0.0f;
m_bAppliedYDistance = false;
}
#ifndef _GAMESERVER
void MAWalkMovement::DebugRenderAttr()
{
#if defined(_DEBUG) || defined(_RDEBUG)
if( !m_bDebugRenderAttr ) {
return;
}
static EtTextureHandle hTexture, hTextureDiagonal;
if(!hTexture) {
hTexture = EternityEngine::LoadTexture(CEtResourceMng::GetInstance().GetFullName("Attr.dds").c_str() );
hTextureDiagonal = EternityEngine::LoadTexture(CEtResourceMng::GetInstance().GetFullName("AttrDiagonal.dds").c_str() );
}
for( int y = -5; y <= 5; y++)
for( int x = -5; x <= 5; x++)
{
float fX = ((int)(m_pMatExWorld->m_vPosition.x / 50.f)+x)*50.f+25.f;
float fZ = ((int)(m_pMatExWorld->m_vPosition.z / 50.f)+y)*50.f+25.f;
char cAttr = INSTANCE(CDnWorld).GetAttribute( EtVector3(fX,0,fZ) );
if( cAttr & 0x3 ) {
char cHiAttr = ( cAttr & 0xf0 ) >> 4;
EtDecalHandle hDecal = (new CEtDecal)->GetMySmartPtr();
float fAngle = 0.f;
if( cHiAttr == 0 ) {
hDecal->Initialize(hTexture, fX, fZ, 25.f, 0.f, 0.f, 0.f, EtColor(1,1,1,1) );
}
else {
if( cHiAttr == 1 ) fAngle = 0.f;
if( cHiAttr == 2 ) fAngle = 90.f;
if( cHiAttr == 4 ) fAngle = 180.f;
if( cHiAttr == 8 ) fAngle = 270.f;
hDecal->Initialize( hTextureDiagonal, fX, fZ, 25.f, 0.f, 0.f, fAngle, EtColor(1,1,1,1) );
}
}
}
#endif
}
#endif