#include "stdafx.h" #include "TrackState.h" #include "PublicMath.h" #include "AIParamParser.h" #include "GameField.h" #include "FieldInfo.h" #include "TargetManager.h" #include "Skill.h" #include "../MovingPlanner.h" #ifdef _NA_007009_20131226_RETURN_TO_POSITION_IF_SAFETYZONE #include "StatusManager.h" #endif //_NA_007009_20131226_RETURN_TO_POSITION_IF_SAFETYZONE TrackState::TrackState() { } TrackState::~TrackState() { } void TrackState::Init() { } void TrackState::OnEnter(LPARAM param1, LPARAM param2, LPARAM param3) { State::OnEnter(param1, param2, param3); //m_TrackTimer.SetTimer(m_pAITypeInfo->m_wTRACK_PERIOD); m_TrackTimer.SetTimer(m_TrackTimer.eStaticTimer_Track, m_pAITypeInfo->m_wTRACK_PERIOD); m_ReturnCheckTimer.SetTimer(m_ReturnCheckTimer.eStaticTimer_ReturnCheck, 0); m_JumpRatio = m_pNPC->GetBaseInfo()->m_byJumpRatio; m_bJumpTry = false; m_TrackCount = 0; // °ø°Ý ¹æ¹ý ¼±ÅõǾî ÀÖÁö ¾ÊÀ¸¸é ¼±Åà if (m_pNPC->GetSelectedSkill() == SKILLCODE_INVALID) { m_pNPC->SelectSkill(); } } void TrackState::OnExit() { State::OnExit(); } BOOL TrackState::OnUpdate(DWORD dwDeltaTick) { __UNUSED(dwDeltaTick); Character* pTargetChar = m_pNPC->GetTargetChar(); if (pTargetChar == NULL) #ifdef _RU_006484_20130225_VALKYRIES_SUMMONS_BUG { m_pNPC->SetTargetChar(NULL); m_pNPC->ChangeState(STATE_ID_IDLE); return true; } #else return true; #endif // _RU_006484_20130225_VALKYRIES_SUMMONS_BUG #ifdef _NA_007009_20131226_RETURN_TO_POSITION_IF_SAFETYZONE //ÀÚ½ÅÀÇ À§Ä¡°¡ ¾ÈÀüÁö´ë ŸÀÏÀÌ¸é ¿øÀ§Ä¡·Î µ¹¾Æ°£´Ù. const nsAI::TileEvent* tile_event = 0; if (m_pNPC->GetStatusManager()->CanMove() && (m_pNPC->PlanEvent->GetTileEvent(&tile_event) == false || (tile_event->Attr & PTA_SAFETY_ZONE))) { m_pNPC->SetTargetChar(NULL); m_pNPC->ChangeState(STATE_ID_RETURN); return true; } #endif //_NA_007009_20131226_RETURN_TO_POSITION_IF_SAFETYZONE WzVector vTargetPos = *pTargetChar->GetPosPtr(); const float attack_range = m_pNPC->GetAttackRange(); BOOLEAN is_movable_cur_tile = true; // (CHANGES) (091229) (WAVERIX) ÀÚ±â ÀÚ½ÅÀÇ ÀûÀýÇÑ »óŸ¦ Æò°¡ÇÒ ÇÊ¿ä ÀÖ´Ù. // ¼±Çà Á¶°ÇÀ¸·Î ÃæºÐÇϹǷΠ¹Ì¸® °è»êÇØ µÐ´Ù. // track->return:change regeneration pos // track->attack is_movable_cur_tile = !!nsAI::PlanEventObject::AcceptableTarget(m_pNPC, 0, 0); // (BUGFIX) (090617) (WAVERIX) ±×·ì ¼³Á¤µÈ ¸ó½ºÅÍ Áß leader°¡ ¾Æ´Ñ ¸ó½ºÅÍÀÇ º¹±Í ó¸® //ÀÌ·ïÁöÁö ¾Ê´Â Çö»ó ¼öÁ¤ // Àڱ⠿µ¿ªÀ» ¹þ¾î³ª°Å³ª Ÿ°Ù°úÀÇ °Å¸®°¡ ¸Ö¾îÁö¸é ¸®ÅÏ »óÅ·ΠÀüÀÌ BOOLEAN isReturnCheckTimeout = !!m_ReturnCheckTimer.IsExpired(); if (isReturnCheckTimeout) { BOOL isOutOfRange = m_pNPC->IsOutOfRegenLocationLimit(vTargetPos); if (isOutOfRange) { // (WAVERIX) (090617) (NOTE) return AI policy state diagram // member_state | leader_state // 0 0 - return // 0 1 - x // 1 0 - follower : return by command // 1 1 - leader group commander : return BOOLEAN member_state = !!m_pNPC->IsGroupMember(); BOOLEAN leader_state = 0; // ¸¸¾à ±×·ìÀÇ ¸®´õÀ̸é Â̵û±¸µéÇÑÅ×µµ ¸Þ¼¼Áö º¸³¿ if (member_state) { if (leader_state = !!m_pNPC->IsLeader()) { // (CHANGES) (091231) (WAVERIX) return command by leader control // moved to return_state /* AI_MSG_CHANGETORETURNSTATE returnMsg; m_pNPC->SendAIMessageToGroupExceptMe(&returnMsg, sizeof(returnMsg), (WORD)dRandom(AIParamParser::AICommonInfo.m_wGROUP_AIMESSAGE_MIN_DELAY, AIParamParser::AICommonInfo.m_wGROUP_AIMESSAGE_MAX_DELAY)); */ } else { NPC* pLeader = m_pNPC->GetLeaderInField(); if (pLeader == 0) { // leader ÇöÀç Çʵå»ó¿¡ ¾ø´Ù. member_state = 0; } else { // target°úÀÇ °Å¸®¶ó... ÀÌ°Ô ³ªÀ» ¼öµµ ÀÖ°Ú³×... float length_sq = Math_VectorLength2(vTargetPos, *pLeader->GetPosPtr()); if (50.f * 50.f < length_sq) member_state = 0; } } } if ((member_state ^ leader_state) == 0) { m_pNPC->SetTargetChar(NULL); m_pNPC->ChangeState(STATE_ID_RETURN, CMS_RUN); } return true; } } // Ÿ°ÙÀ» °ø°ÝÇÒ ¼ö ¾ø´Â »óÅÂÀÌ¸é »óÅ ÀüÀÌ if ((pTargetChar->CanBeAttacked() || m_pNPC->UseResurrectionSkill()) == false) { m_pNPC->SetTargetChar(NULL); m_pNPC->ChangeState(STATE_ID_IDLE); return true; } // Ÿ°ÙÀÌ °ø°Ý »ç°Å¸® ³»¿¡ µé¾îÀÖÀ¸¸é Attack »óÅ·ΠÀüÀÌ // (CHANGES) (091229) (WAVERIX) const float attack_range_sq = attack_range * attack_range; float distance_sq = m_pNPC->GetDist2(vTargetPos); if(distance_sq < attack_range_sq) { // changes '<=' to '<' m_pNPC->ChangeState(STATE_ID_ATTACK); return true; } // Á¡ÇÁ¼Ó¼ºÀÌ ÀÖÀ» °æ¿ì ÀÏÁ¤ °Å¸®°¡ µÇ¸é Á¡ÇÁÇÑ´Ù. if (m_JumpRatio && m_bJumpTry == false && m_pNPC->CanJumpDist(pTargetChar)) { m_bJumpTry = true; // È®·ü üũ const int ratio = random(1, 100); if (ratio <= m_JumpRatio) { // ±æÃ£±â°¡ ¼º°øÇØ¾ß Á¡ÇÁ»óÅ·ΠÀüȯÇÑ´Ù. if (m_pNPC->SetJumpPos(pTargetChar)) { m_pNPC->ChangeState(STATE_ID_JUMP, true); return true; } } } if (is_movable_cur_tile == false) { // (NOTE) ´õ ÀÌ»ó À̵¿ ºÒ°¡ »óÅ·Π°è¼Ó À̵¿ »óÅ À¯ÁöµÇ´Â »óȲÀ» ¹æÁöÇÑ´Ù. // |Current-state=¼±Çà °Ë»ç¸¦ ÅëÇØ ´Ù¸¥ »óÅ ÀüÀ̸¦ À¯µµ ÀÌÈÄ Ãß°¡ ó¸® »óÅÂ| // |Action=changes to return-state|Side-effect=change regeneration position| // |Other=Group°ú º°°³ 󸮷Π¼öÇàÇÑ´Ù. º° ¼ö ¾ø´Ù.| m_pNPC->SetTargetChar(NULL); m_pNPC->ChangeState(STATE_ID_RETURN, CMS_RUN); return true; } if (m_pNPC->IsMoving()) { // NPC°¡ ÀÌ¹Ì ¶Ù°í ÀÖ´Â °æ¿ì //----------------------------------------------------------------------------------------- // n: ÇöÀç NPC ÁÂÇ¥ // p: ÇöÀç Ç÷¹À̾î ÁÂÇ¥ // d: NPCÀÇ À̵¿ ¸ñÀû ÁÂÇ¥ // a: º¤ÅÍ n->d // b: º¤ÅÍ n->p // ¶ó°í ÇÒ ¶§ »õ·Î ±æÃ£±â ÇÏ´Â Á¶°ÇÀº ´ÙÀ½°ú °°´Ù. // // a¿Í bÀÇ °¢ÀÌ ÀÏÁ¤ ¼öÄ¡ ÀÌ»ó ¹ú¾îÁö°í p¿Í d¿ÍÀÇ °Å¸®°¡ NPC °ø°Ý »ç°Å¸®¸¦ ¹þ¾î³ª´Â °æ¿ì //----------------------------------------------------------------------------------------- if (m_TrackTimer.IsExpired()) { WzVector wvNPCPos = *m_pNPC->GetPosPtr(); WzVector wvNPCDestPos = m_pNPC->GetPathExplorer()->GetTargetPos(); // Ç÷¹À̾ NPC°¡ ¶Ù·Á´Â ¹æÇâÀÇ ºÎä²ÃÀ» ¹þ¾î³­ °¢¿¡ À§Ä¡Çϴ°¡? BOOLEAN bOutOfAngle = !Math_IsPositionInFanshapedArea(&vTargetPos, &wvNPCPos, &(wvNPCDestPos - wvNPCPos), AIParamParser::AICommonInfo.m_wTRACK_INNER_ANGLE); // Ç÷¹À̾ NPC°¡ ¶Ù·Á´Â ¸ñÀûÁö¿¡¼­ ¶§¸± ¼ö ¾ø´Â °Å¸®¿¡ Àִ°¡? float attack_range = m_pNPC->GetAttackRange(); ; attack_range *= attack_range; BOOLEAN bOutOfRange = VectorLength2(&(wvNPCDestPos - vTargetPos)) > attack_range; if (bOutOfAngle && bOutOfRange) { // printf("À̵¿Áß Æ®·¢ "); DoTrack(); } } } else { // NPC°¡ ¶Ù°í ÀÖÁö ¾ÊÀº °æ¿ì // printf("¸ØÃç¼­ Æ®·¢ "); DoTrack(); } return true; } void TrackState::DoTrack() { Character* pTargetChar = m_pNPC->GetTargetChar(); ASSERT(NULL != pTargetChar); if (pTargetChar == NULL) { m_pNPC->SetTargetChar(NULL); m_pNPC->ChangeState(STATE_ID_RETURN, CMS_RUN); return; } ++m_TrackCount; WzVector vTargetPos = *pTargetChar->GetPosPtr(); WzVector wvCurPos = *m_pNPC->GetPosPtr(); // Á¡ÇÁ ŸÀÏ À§¿¡ ¼­ÀÖÀ¸¸é ³ôÀÌ ÆÇ´ÜÈÄ °­Á¦ Á¡ÇÁ! if (m_pNPC->IsStandingOnJumpTile() && m_pNPC->IsOnGoodPositionToJump()) { float fJumpWaitingTick = Math_VectorLength(vTargetPos, wvCurPos) * 60.0f; m_pNPC->ChangeState(STATE_ID_JUMP, false, (LPARAM)fJumpWaitingTick); return; } // »õ·Î¿î Track PositionÀ» ã´Â´Ù. // (NOTE) ¾Æ¹«·¡µµ °è»ê½ÄÀÌ À§ÇèÇØ º¸À̴µ¥... 0.1f * fAttackRangeRatioÀÇ °á°ú´Â? float fAttackRangeRatio = 1.0f - 0.1f * static_cast(m_TrackCount); ; fAttackRangeRatio = max(0, fAttackRangeRatio); WzVector vTrackPos; m_pNPC->GetTrackPos(vTrackPos, m_pNPC->GetAttackRange() * fAttackRangeRatio); // Ÿ°ÙÀÌ À̵¿ÁßÀ̶ó¸é À̵¿ ¹æÇâ °¨¾ÈÇØ¼­ ÃßÀûÁ¡ º¸Á¤ if (pTargetChar->IsMoving()) { const WzVector& wvPlayerDestPos = pTargetChar->GetPathExplorer()->GetTargetPos(); // Ÿ°Ù Ç÷¹À̾ NPCÀÇ ¹Ý´ëÂÊÀ¸·Î À̵¿ÇÏ·Á´Â °æ¿ì¿¡¸¸ º¸Á¤ if (!Math_IsPositionInFanshapedArea(&wvCurPos, &vTargetPos, &(wvPlayerDestPos - vTargetPos), 180)) { WzVector wvNormal; VectorNormalize(&wvNormal, &(wvPlayerDestPos - vTargetPos)); wvNormal = wvNormal * 2.0f; vTrackPos = vTrackPos + wvNormal; } } // ±æÃ£±â if (m_pNPC->CanMove() == false) { // arycoat, 2012.09.04 // THRUST, STUN µîÀÇ state·Î Àá½Ã À̵¿ÀÌ ºÒ°¡ ÇÒ¶§ÀÌ´Ù. ÇÑ ÅÏ ½¬°í ´Ù½Ã ÃßÀûÇÑ´Ù. } else if (m_pNPC->MoveAndBroadcast(&vTrackPos, CMS_RUN) == false) { // ½ÇÆÐ½Ã Target°ú µ¿ÀÏ ÁÂÇ¥·Î ´Ù½Ã ±æÃ£±â if (m_pNPC->MoveAndBroadcast(&vTargetPos, CMS_RUN) == false) { // ±×·¡µµ ½ÇÆÐÇÏ¸é »óÅ ÀüÀÌ m_pNPC->SetTargetChar(NULL); m_pNPC->ChangeState(STATE_ID_RETURN, CMS_RUN); } } m_TrackTimer.SetTimer(m_TrackTimer.eStaticTimer_Track, m_pAITypeInfo->m_wTRACK_PERIOD); } void TrackState::OnMsgForceAttack(const AI_MSG* pMsg) { const AI_MSG_FORCE_ATTACK* pAttackMsg = static_cast(pMsg); // ÇöÀç °ø°ÝÇϰí ÀÖ´Â ´ë»óÀ̶ó¸é ¿ø·¡»óŸ¦ À¯ÁöÇÑ´Ù. Character* pCurTarget = m_pNPC->GetTargetChar(); if (pCurTarget && pCurTarget->GetObjectKey() == pAttackMsg->dwTargetKey) { return; } State::OnMsgForceAttack(pMsg); }