Files
Sun1602/Server/GameServer/Npcai/PatrolState.cpp
T
2022-10-26 12:25:11 +08:00

374 lines
11 KiB
C++

#include "stdafx.h"
#include <Timer.h>
#include "PatrolState.h"
#include "GameField.h"
#include <3DEngine/3DTerrain/PathExplorer.h>
#include "AIParamParser.h"
#include "Monster.h"
#include "PublicMath.h"
#include "NPCStateManager.h"
#include "MovingPlanner.h"
PatrolState::PatrolState()
{
}
PatrolState::~PatrolState()
{
}
void
PatrolState::Init()
{
}
void
PatrolState::SetNPC(NPC* pNpc)
{
m_pMonster = DOWNCASTER_SELECT<Monster*>(pNpc);
if (m_pMonster == NULL || pNpc->IsEqualObjectKind(MONSTER_OBJECT) == false) {
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]|Unexpected type %08X", pNpc->GetObjectType());
__debugbreak();
}
State::SetNPC(pNpc);
}
void
PatrolState::OnEnter(LPARAM param1, LPARAM param2, LPARAM param3)
{
State::OnEnter(param1, param2, param3);
m_SearchTimer.SetTimer(m_SearchTimer.eStaticTimer_Search, 0);
m_PatrolTimer.SetTimer(m_PatrolTimer.eStaticTimer_Track, m_pAITypeInfo->m_wTRACK_PERIOD);
// 현재 타일 위치랑 가장 가까운 패트롤 좌표를 찾아서 이동한다.
m_bRandomPatrol = false;
m_bIncreaseIndex = true;
m_bFirst = true;
}
void
PatrolState::OnExit()
{
State::OnExit();
}
BOOL
PatrolState::OnUpdate(DWORD dwDeltaTick)
{
__UNUSED(dwDeltaTick);
if (m_SearchTimer.IsExpired())
{
// 주변에 적이 있는지 탐색
Character* pTargetChar = m_pMonster->SearchTarget();
if (pTargetChar)
{
m_pMonster->SetTargetChar(pTargetChar);
m_pMonster->ChangeState(STATE_ID_TRACK);
}
}
// 목적지에 도달한 상태인가
if (m_PatrolTimer.IsExpired() && m_pMonster->IsMoving() == false)
{
DoPatrol();
}
return true;
}
// 목적지 선택에 성공하면 true를 리턴
BOOL
PatrolState::DoPatrol()
{
BOOL bFindPos = false;
// 이전 길찾기에서 조금씩 나눠서 가던 중인지
if (m_bRandomPatrol)
{
// 나눠서 가던 길을 한번에 갈 수 있는지 시도해 본다.
bFindPos = true;
FindNextRandomPos(true);
}
else
{
// 다음 패트롤 좌표를 찾는다.
bFindPos = FindNextPos();
}
if (bFindPos == false)
return false;
// 이동가능 여부를 체크후 패킷을 날린다.
if (m_pMonster->MoveAndBroadcast(&m_vDestPos, CMS_WALK))
{
m_bRandomPatrol = false;
}
else
{
// 패트롤 지점 방향의 랜덤 좌표로 이동을 시도해 본다.
FindNextRandomPos(false);
if (m_pMonster->MoveAndBroadcast(&m_vDestPos, CMS_WALK))
{
m_bRandomPatrol = true;
}
}
m_PatrolTimer.SetTimer(m_PatrolTimer.eStaticTimer_Track, m_pAITypeInfo->m_wTRACK_PERIOD);
return true;
}
void
PatrolState::FindNextRandomPos(BOOL direct)
{
GameField* pField = m_pMonster->GetField();
if (pField == NULL)
return;
NPCStateManager* state_manager = m_pMonster->GetStateManager();
if (m_pMonster->GetRegenID() != 0) //맵툴로 찍은 몬스터 라면.
{
if (m_bIncreaseIndex)
m_vDestPos = state_manager->GetDestPos();
else
m_vDestPos = state_manager->GetStartPos();
}
else
{
// 이동해야할 패트롤 위치
WzSpecialArea* pBaseAreaInfo = \
pField->GetFieldInfo()->GetAreaInfo(state_manager->GetMoveAreaID());
if (pBaseAreaInfo == NULL)
{
SUNLOG(eFULL_LOG,
"[PatrolState::FindNextRandomPos] MonsterCode[%d] MoveAreaInfo doesn't exist!",
m_pMonster->GetBaseInfo()->m_MonsterCode);
return;
}
if (pBaseAreaInfo ->m_wbvRegion.m_eBvType != BVT_PATH_TILE_INDEX)
{
SUNLOG(eCRITICAL_LOG,
"[PatrolState::FindNextRandomPos] MonsterCode[%d] AreaType is Invalid!!",
m_pMonster->GetBaseInfo()->m_MonsterCode);
return;
}
DWORD* pdwTileIndex = pBaseAreaInfo->m_wbvRegion.m_BvPathTileIndex.m_pdwData;
nsAI::MovingPlanner* const pMovingPlanner = pField->GetMovingPlanner();
m_vDestPos = pMovingPlanner->GetRandomPosInTile(pdwTileIndex[m_iIndexOfTileIndex]);
}
if (direct == false)
{
// 현재위치
const WzVector& wvCurPos = *m_pMonster->GetPosPtr();
// 이동할 거리
float MOVE_DISTANCE = (float)dRandom(AIParamParser::AICommonInfo.m_fMIN_MOVE_DISTANCE,
AIParamParser::AICommonInfo.m_fMAX_MOVE_DISTANCE);
WzVector wvNormal;
VectorNormalize(&wvNormal, &(m_vDestPos - wvCurPos));
wvNormal.z = 0;
// 이동해야할 방향의 45도 안팍으로 랜덤한 위치를 얻어온다.
int iRotateAngle = random(0, 90);
if (iRotateAngle > 45)
iRotateAngle = 405 - iRotateAngle;
Math_ZRotateVector(&wvNormal, m_vDestPos, iRotateAngle);
m_vDestPos = wvCurPos + m_vDestPos * MOVE_DISTANCE;
}
// 리턴상태를 방지하기 위해서 목적지 좌표를 리젠좌표로 셋팅한다.
m_pMonster->SetRegenPos(m_vDestPos);
}
BOOL
PatrolState::FindNextPos()
{
GameField* pField = m_pMonster->GetField();
if (pField == NULL)
return false;
//const WzVector& wvCurPos = *m_pMonster->GetPosPtr();
//WzVector wvTempPos;
NPCStateManager* state_manager = m_pMonster->GetStateManager();
if (m_pMonster->GetRegenID() != 0) //맵툴로 찍은 몬스터 라면.
{
if (m_bFirst)
{
// (CHANGES) (WAVERIX) fault! fix up invalid calculation which is dropped base position
const WzVector& cur_pos = *m_pMonster->GetPosPtr();
float dist_with_start = VectorLength2(&(cur_pos - state_manager->GetStartPos()));
float dist_with_dest = VectorLength2(&(cur_pos - state_manager->GetDestPos()));
if (dist_with_start > dist_with_dest) {
m_bIncreaseIndex = false;
}
else {
m_bIncreaseIndex = true;
}
}
if (m_bIncreaseIndex) {
m_vDestPos = state_manager->GetDestPos();
}
else {
m_vDestPos = state_manager->GetStartPos();
}
m_bIncreaseIndex = !m_bIncreaseIndex;
// 리턴상태를 방지하기 위해서 목적지 좌표를 리젠좌표로 셋팅한다.
m_pMonster->SetRegenPos(m_vDestPos);
return true;
}
// 몬스터가 패트롤해야할 영역 정보를 얻는다.
WzSpecialArea* pBaseAreaInfo = pField->GetFieldInfo()->GetAreaInfo(state_manager->GetMoveAreaID());
if (pBaseAreaInfo == NULL)
{
// SUNLOG(eFULL_LOG, "[PatrolState::FindNextPos] MonsterCode[%d] MoveAreaInfo doesn't exist!", m_pMonster->GetBaseInfo()->m_MonsterCode);
return false;
}
if (pBaseAreaInfo ->m_wbvRegion.m_eBvType != BVT_PATH_TILE_INDEX)
{
// SUNLOG(eCRITICAL_LOG, "[PatrolState::FindNextPos] MonsterCode[%d] AreaType is Invalid!!", m_pMonster->GetBaseInfo()->m_MonsterCode);
return false;
}
//영역 정보
DWORD dwAreaTileCnt = pBaseAreaInfo->m_wbvRegion.m_BvPathTileIndex.m_nCount;
if (dwAreaTileCnt == 0)
{
SUNLOG(eFULL_LOG, "[PatrolState::FindNextPos] MonsterCode[%d] dwAreaTileCnt is Zero!",
m_pMonster->GetBaseInfo()->m_MonsterCode);
return false;
}
DWORD* pdwTileIndex = pBaseAreaInfo->m_wbvRegion.m_BvPathTileIndex.m_pdwData;
if (pdwTileIndex == NULL)
{
SUNLOG(eCRITICAL_LOG, "[PatrolState::FindNextPos] MonsterCode[%d] pdwTileIndex is NULL!",
m_pMonster->GetBaseInfo()->m_MonsterCode);
return false;
}
nsAI::MovingPlanner* const pMovingPlanner = pField->GetMovingPlanner();
// 처음으로 목적지 좌표를 찾는거라면
if (m_bFirst)
{
// 현재 타일과 가장 가까운 타일인덱스를 구한다.
m_iIndexOfTileIndex = 0;
int iCurTileIndex = m_pMonster->GetPathExplorer()->GetTile();
float fMinDistance = 0.f, fCurDistance = 0.f;
m_bFirst = false;
const WzVector& wvCurPos = *m_pMonster->GetPosPtr();
WzVector wvTempPos;
// (CHANGES) (091228) (WAVERIX) changes tile selector
for (DWORD index = 0; index < dwAreaTileCnt; ++index)
{
#if WAVERIX_CODE_RENEWAL
wvTempPos = pMovingPlanner->GetRandomPosInTile(pdwTileIndex[index]);
#endif
// 현재 서 있는 타일과 같으면 거기서부터 시작한다.
if (iCurTileIndex == pdwTileIndex[index])
{
m_iIndexOfTileIndex = index;
m_vDestPos = wvTempPos;
break;
}
// (BUGFIX) (091223) (WAVERIX) remove invalid usage that is use uninitialized 'wvTempPos'
#if WAVERIX_CODE_RENEWAL
fCurDistance = VectorLength2(&(wvTempPos - wvCurPos));
#endif
#if WAVERIX_CODE_BACKUP
m_vDestPos = pMovingPlanner->GetRandomPosInTile(pdwTileIndex[index]);
fCurDistance = VectorLength(&(wvTempPos - wvCurPos));
#endif
if (index == 0 || fCurDistance < fMinDistance)
{
fMinDistance = fCurDistance;
m_iIndexOfTileIndex = index;
m_vDestPos = wvTempPos;
}
}
if (fMinDistance >= 30)
{
//SUNLOG(eCRITICAL_LOG, "[FindNextPos::First] MonsterCode[%d] fMinDistance[%f]",
// m_pMonster->GetBaseInfo()->m_MonsterCode, fMinDistance);
}
// current state : setup 'm_vDestPos'
}
else
{
// 끝까지 갔으면 다시 돌아온다.
if ((m_iIndexOfTileIndex + 1) == dwAreaTileCnt)
m_bIncreaseIndex = false;
else if (m_iIndexOfTileIndex == 0)
m_bIncreaseIndex = true;
if (m_bIncreaseIndex)
{
++m_iIndexOfTileIndex;
m_iIndexOfTileIndex = min((int)(dwAreaTileCnt - 1), m_iIndexOfTileIndex);
}
else
{
--m_iIndexOfTileIndex;
m_iIndexOfTileIndex = max(0, m_iIndexOfTileIndex);
}
// 선택된 타일 안에서 랜덤 좌표를 얻어온다.
m_vDestPos = pMovingPlanner->GetRandomPosInTile(pdwTileIndex[m_iIndexOfTileIndex]);
}
// 리턴상태를 방지하기 위해서 목적지 좌표를 리젠좌표로 셋팅한다.
m_pMonster->SetRegenPos(m_vDestPos);
return true;
}
void
PatrolState::OnMsgAttacked(const AI_MSG* pMsg)
{
State::OnMsgAttacked(pMsg);
const AI_MSG_ATTACKED* pAttackMsg = static_cast<const AI_MSG_ATTACKED*>(pMsg);
GameField* pField = m_pMonster->GetField();
if (pField == NULL)
return;
Character* pAttacker = pField->FindCharacter(pAttackMsg->dwAttackerKey);
if (pAttacker == NULL)
return;
// 현재 선택된 타겟이 없고 상대가 적군이면 공격 상태로 전이
#ifdef _NA_007667_20141001_WITCHBLADE_SKILL
State::SetTarget(pMsg, STATE_ID_TRACK);
#else
if (m_pMonster->GetTargetChar() == NULL &&
m_pMonster->IsFriend(pAttacker) == USER_RELATION_ENEMY)
{
m_pMonster->SetTargetChar(pAttacker);
m_pMonster->ChangeState(STATE_ID_TRACK, 0, 0, pAttackMsg->dwMsgID);
}
#endif //_NA_007667_20141001_WITCHBLADE_SKILL
}