#include "stdafx.h" #include "NPCStateManager.h" #include #include "GameServerEx.h" #include "MovingPlanner.h" #include "MoveStateControl.h" #include "NPCStateFactory.h" #include "NPC.h" #include "State.h" //================================================================================================== // @history // ~09/12/22 // 09/12/22, waverix, code-arrangement static NPCStateFactory s_NPCStateFactory; //================================================================================================== // (WAVERIX)(081005)(NOTE) // - ´ÙÀ½ '///' »çÀÌÀÇ ·ÎÁ÷Àº Àӽà ·ÎÁ÷À̸ç, // NPCState °ü·Ã ±¸Á¶ º¯°æÀÌ ¿Ï·áµÇ´Â ½ÃÁ¡¿¡ Á¦°ÅµÉ °ÍÀÌ´Ù. namespace nsAI { static const WzVector NullVector = { .0f, .0f, .0f }; // // °¢°¢ÀÇ State¿¡ º°µµÀÇ ÀڷᱸÁ¶¸¦ µÎÁö ¾Êµµ·Ï ÇÏÀÚ. ÇöÀç´Â ±âÁ¸ ¹æ½Ä¿¡ ¹¯¾î°¡´Â ÇüÅ·Î... class EscapeState { enum eTracingMethod { eTM_None, eTM_TileTracing, eTM_DestPosTracing, }; public: EscapeState() : m_pOwner(0) , m_pGameFieldSync(0) , m_TimeoutTick(0) , m_TracingMethod(eTM_None) , m_DestTileNo(-1) , m_DestPos(NullVector) { m_UpdateTimer.Disabled(); } ~EscapeState() { } void Init(NPC* pNPC) { m_pOwner = pNPC; m_pGameFieldSync = pNPC->GetField(); } BOOLEAN OnEnter(const CmdAI::Escape& rCmd); BOOLEAN OnUpdate(const DWORD delta_tick); BOOLEAN OnExit(); private: NPC* m_pOwner; GameField* m_pGameFieldSync; ITimeout m_UpdateTimer; DWORD m_TimeoutTick; eTracingMethod m_TracingMethod; // ¹¹ ¶§¹®¿¡ ÇÇÇÏ´ÂÁö¸¦ ¸í½ÃÇÒ ÇÊ¿ä ÀÖ´Ù. WORD m_DestTileNo; WzVector m_DestPos; }; }; // end of namespace 'nsAI' // //================================================================================================== // (CHANGES) (091222) (WAVERIX) code arrangement NPCStateManager::NPCStateManager(NPC* pOwner) : m_pNPC(pOwner) , m_pEscapeState(0) { ClearFields(); } NPCStateManager::~NPCStateManager() { Release(); } //void //NPCStateManager::_ReleaseNode(STATE_MAP_PAIR rPair) //{ // State* pState = rPair.second; // s_NPCStateFactory.Free(pState); //} void NPCStateManager::ClearFields() { m_pCurState = 0; //STATE_MAP m_mapStates m_eMoveType = eMOVE_ATTITUDE_WANDER; m_dwMoveAreaID = 0; pos_index_ = 0; //ITimerBase m_SpecialTimer //ITimeout m_StateChangeCheckTimer; m_StateChangeCount = 0; m_bStateTransaction = 0; m_bRequestHelp = 0; if (m_pEscapeState) { TAllocDelete(nsAI::EscapeState, m_pEscapeState); m_pEscapeState = 0; // (NOTE) (WAVERIX) >-- ÀÏ´Ü ´ÙÀ½ÀÌ ¹®Á¦À̱ä Çѵ¥... Àӽà ÄÚµåÀÓ. } FOREACH_CONTAINER(const STATE_MAP::value_type& node, m_mapStates, STATE_MAP) { State* state = node.second; s_NPCStateFactory.Free(state); } m_mapStates.clear(); } // _NA_0_20100514_NPC_DEAD_STATE_TIME void NPCStateManager::ResetStateObject() { STATE_MAP::iterator it = m_mapStates.begin(); while(it != m_mapStates.end()) { State* state = it->second; if (m_pCurState != state) { m_mapStates.erase(it++); s_NPCStateFactory.Free(state); } else { ++it; } } } void NPCStateManager::Init(DWORD stateID, BYTE byMoveType, DWORD moveAreaID, LPARAM param1) { m_StateChangeCount = 0; m_StateChangeCheckTimer.SetNextTimeoutByCurTickBased(1000); m_bRequestHelp = false; m_eMoveType = (eNPC_MOVE_ATTITUDE)byMoveType; m_dwMoveAreaID = moveAreaID; const BASE_NPCINFO* pNpcInfo = m_pNPC->GetBaseInfo(); // óÀ½ State STATE_MAP::const_iterator it = m_mapStates.find(stateID); if (it == m_mapStates.end()) { #ifdef _DEBUG __debugbreak(); #endif SUNLOG(eCRITICAL_LOG, "Can't find NPC state [NPC:%u][ID:%u]", pNpcInfo->m_MonsterCode, stateID); } m_pCurState = it->second; m_pCurState->OnEnter(param1); m_SpecialTimer.SetTimer(pNpcInfo->m_wSkillUpdateTime); } BOOL NPCStateManager::Update(DWORD delta_tick) { if (m_SpecialTimer.IsExpired() && m_pNPC->IsAlive()) { SPECIAL_CONDITION* special_condition = m_pNPC->GetDrawLotsSpecialAction(); if (special_condition) m_pNPC->SpecialAction(*special_condition); m_pNPC->StatusResist(); // »óÅÂÀúÇ× Ã¼Å© } if (m_pEscapeState) { BOOLEAN bProgress = m_pEscapeState->OnUpdate(delta_tick); if (bProgress != false) return true; m_pEscapeState->OnExit(); TAllocDelete(nsAI::EscapeState, m_pEscapeState); m_pEscapeState = 0; // (NOTE) (WAVERIX) >-- ÀÏ´Ü ´ÙÀ½ÀÌ ¹®Á¦À̱ä Çѵ¥... Àӽà ÄÚµåÀÓ. } return m_pCurState->OnUpdate(delta_tick); } void NPCStateManager::AddStateObject(DWORD stateID, DWORD linkStateID) { STATE_MAP::const_iterator it = m_mapStates.find(stateID); if (it != m_mapStates.end()) { State* pAddState = it->second; const NPC* find_state_owner_npc = pAddState->GetOwnerNpc(); const DWORD find_state_link_stateID = pAddState->GetLinkStateID(); if (find_state_owner_npc != m_pNPC || find_state_link_stateID != linkStateID) { const BASE_NPCINFO* manager_npc_info = m_pNPC->GetBaseInfo(); const BASE_NPCINFO* find_state_owner_npc_info = find_state_owner_npc->GetBaseInfo(); SUNLOG(eCRITICAL_LOG, "[NPCStateManager::AddStateObject] ÀÌ¹Ì Á¸ÀçÇÏ´Â »óÅ¿¡·¯" "manager_npc[%d] find_state_owner_npc[%d]" "new_link_stateID[%d] cur_link_stateID[%d]", manager_npc_info->m_MonsterCode, find_state_owner_npc_info->m_MonsterCode, linkStateID, find_state_link_stateID ); } } else { State* pAddState = s_NPCStateFactory.Alloc(linkStateID); pAddState->SetNPC(m_pNPC); pAddState->SetStateID((ENUM_STATD_ID)stateID); pAddState->SetLinkStateID((ENUM_STATD_ID)linkStateID); pAddState->Init(); m_mapStates.insert(std::make_pair(stateID, pAddState)); } } void NPCStateManager::ChangeState(DWORD stateID, LPARAM param1, LPARAM param2, LPARAM param3) { if (++m_StateChangeCount > 10) { const BASE_NPCINFO* pNpcInfo = m_pNPC->GetBaseInfo(); Player* npc_owner = m_pNPC->GetPlayerOwner(); if (npc_owner == NULL) { SUNLOG(eCRITICAL_LOG, "too many state change try! NPCCode(%d) MoveType[%d] " "CurState[%d] CurLinkState[%d] NextState[%d] AIMsgID[%d]", pNpcInfo->m_MonsterCode, pNpcInfo->m_byMoveAttitude, m_pCurState->GetStateID(), m_pCurState->GetLinkStateID(), stateID, param3); } else //(npc_owner != NULL) { SUNLOG(eCRITICAL_LOG, "too many state change try! NPC_Owner_Userkey(%d) NPCCode(%d) MoveType[%d] " "CurState[%d] CurLinkState[%d] NextState[%d] AIMsgID[%d]", npc_owner->GetCharGuid(), pNpcInfo->m_MonsterCode, pNpcInfo->m_byMoveAttitude, m_pCurState->GetStateID(), m_pCurState->GetLinkStateID(), stateID, param3); } } if (m_StateChangeCheckTimer.IsExpired()) { m_StateChangeCheckTimer.SetNextTimeoutByCurTickBased(1000); m_StateChangeCount = 0; } // WAVERIX_WRAPPER((WAVERIX_LC_CALL3("waverix_log_ai_changes", m_pNPC, stateID, m_pCurState))); // ¹Ù²ð »óÅÂÀÇ Æ÷ÀÎÅ͸¦ ã´Â´Ù. STATE_MAP::const_iterator it = m_mapStates.find(stateID); // ¹Ù²ð »óŰ¡ Á¤ÀǵǾî ÀÖÁö ¾ÊÀº °æ¿ì ¸®ÅÏÇÑ´Ù. // -or- ¹Ù²ð »óŰ¡ ÇöÀç »óÅÂ¿Í °°À¸¸é ±×³É ¸®ÅÏÇÑ´Ù. if (it == m_mapStates.end() || it->second == m_pCurState) { return; } else { StateTransaction trans(m_bStateTransaction); // ÇöÀç »óÅÂÀÇ ¸¶¹«¸® ÄÚµå m_pCurState->OnExit(); // »óÅ Æ÷ÀÎÅÍ ±³Ã¼ m_pCurState = it->second; // ¹Ù²ï »óÅÂÀÇ ÃʱâÈ­ ÄÚµå m_pCurState->OnEnter(param1, param2, param3); } } const WzVector& NPCStateManager::GetStartPos() const { UnitRegenInfo* const regeninfo = m_pNPC->GetUnitRegenInfo(); ASSERT(regeninfo); return regeninfo->GetStartPos(pos_index_); } const WzVector& NPCStateManager::GetDestPos() const { UnitRegenInfo* const regeninfo = m_pNPC->GetUnitRegenInfo(); ASSERT(regeninfo); return regeninfo->GetDestPos(pos_index_); } const eCHAR_MOVE_STATE NPCStateManager::GetWalkState() const { UnitRegenInfo* const regeninfo = m_pNPC->GetUnitRegenInfo(); ASSERT(regeninfo); return regeninfo->GetWalkState(pos_index_); } // num_of_pos ±îÁö ¼ø ¹æÇâÀ¸·Î Áõ°¡ BOOL NPCStateManager::IncreaseMovePosIndex() { UnitRegenInfo* const regeninfo = m_pNPC->GetUnitRegenInfo(); ASSERT(regeninfo); const WORD num_of_pos = regeninfo->GetNumOfPos(); if (pos_index_ < num_of_pos-1) { ++pos_index_; return TRUE; } return FALSE; } // num_of_pos ±îÁö ¼ø ¹æÇâÀ¸·Î Áõ°¡ BOOL NPCStateManager::DecreaseMovePosIndex() { UnitRegenInfo* const regeninfo = m_pNPC->GetUnitRegenInfo(); ASSERT(regeninfo); const WORD num_of_pos = regeninfo->GetNumOfPos(); if (pos_index_ > 0) { --pos_index_; return TRUE; } return FALSE; } // À§Ä¡ ¸ñ·Ï Áß ¸¶Áö¸· À§Ä¡¿¡ µµ´Þ Çß´ÂÁö °Ë»çÇÑ´Ù. bool NPCStateManager::IsArrivedLastPosition() const { const UnitRegenInfo* const unit_regen = m_pNPC->GetUnitRegenInfo(); ASSERT(unit_regen != NULL); const WORD num_of_pos = unit_regen->GetNumOfPos(); return (pos_index_ == num_of_pos-1); } //================================================================================================== void NPCStateManager::RequestCommand_(const nsAI::CmdAI& rCmd) { using namespace nsAI; switch (rCmd.Cmd) { case CmdAI::eCmd_Escape: { if (m_pEscapeState) { TAllocDelete(nsAI::EscapeState, m_pEscapeState); } m_pEscapeState = TAllocNew(nsAI::EscapeState); m_pEscapeState->Init(m_pNPC); m_pEscapeState->OnEnter(static_cast(rCmd)); } break; default: ASSERT(!"["__FUNCTION__"] Unexpected Cmd"); break; } } //================================================================================================== // namespace namespace nsAI { const DWORD escape_check_timer = 50; // ´ëÃæ ¸¸µé°í ÀÖ´Ù. ¹Ýµå½Ã ´Ù¸¥ À§Ä¡¿¡ ´Ù¸¥ ·ÎÁ÷À¸·Î À籸ÃàÇ϶ó!!! BOOLEAN EscapeState::OnEnter(const CmdAI::Escape& rCmd) { if ((m_pOwner && m_pGameFieldSync) == false) return false; const DWORD curTick = GetTickCount(); m_UpdateTimer.SetNextTimeoutByCurTickBased(escape_check_timer); m_TimeoutTick = curTick + rCmd.TimeoutInterval; BOOLEAN valid_pos = (rCmd.pDestPos != 0); BOOLEAN valid_tile = (rCmd.DestTileNo != -1); if ((valid_tile & valid_pos) == 0) { // ÇöÀç·Î¼± Áö¿øÇÒ ½Ã°£Àû ¿©À¯ ¾øÀ½. ¾îÂ÷ÇÇ ÀçÁ¤¸®ÇÒ ¿¹Á¤À̱ä ÇÏÁö¸¸... ASSERT(!"Current Version not implementation"); return false; } m_DestTileNo = rCmd.DestTileNo; m_DestPos = *rCmd.pDestPos; /* CPathExplorer* const pPathExplorer = m_pOwner->GetPathExplorer(); const BOOLEAN isMoving = pPathExplorer->IsMoving(); if (isMoving) pPathExplorer->Stop(); WzVector& diff = (m_DestPos - (*pCurPos)); const float lenQ2 = 5.f*5.f; const float calc_lenQ2 = VectorLength2(&diff); BOOLEAN bJump = false; if (calc_lenQ2 < lenQ2) { const DWORD bJumpRaio = !!RandomNumberGenerator::GetRandomNumberRange(0, 1000); if (bJumpRaio < 900) bJump = true; } WzVector normal_diff; VectorNormalize(&normal_diff, &diff); if (!bJump) { BOOL bTouchedSomething; WzVector wvRotatedVector; if (m_pOwner->ThrustMoveAndBroadcast( &m_DestPos , CMS_RUN , bTouchedSomething , wvRotatedVector )) { return true; } } */ const WzVector* pCurPos = m_pOwner->GetPosPtr(); if (m_pGameFieldSync->FindPath(m_pOwner, &m_DestPos)) { MoveStateControl* const pMoveStateControl = m_pOwner->GetMoveStateControl(); eCHAR_MOVE_STATE move_state = pMoveStateControl->GetMoveState(); if (move_state == CMS_WALK) { move_state = CMS_RUN; pMoveStateControl->SetMoveState(move_state); } MSGSUB_SYNC_MOVE_BRD msg; msg.m_dwObjectKey = m_pOwner->GetObjectKey(); msg.m_byState = move_state; msg.m_ForcedMove = true; msg.m_wvCurPos = *pCurPos; msg.m_wvDestPos = m_DestPos; m_pOwner->SendPacketAroundForSync(&msg); } return true; } BOOLEAN EscapeState::OnUpdate(const DWORD delta_tick) { const BOOLEAN update_continue = true; const BOOLEAN update_stop = false; if (!m_UpdateTimer.IsExpired()) return update_continue; GameField* pGameField = m_pOwner->GetField(); if ((pGameField && (pGameField == m_pGameFieldSync)) == false) return update_stop; if (m_UpdateTimer.GetTimeoutTicks() > m_TimeoutTick) return update_stop; m_UpdateTimer.SetNextTimeoutByCurTickBased(escape_check_timer); CPathExplorer* const pPathExplorer = m_pOwner->GetPathExplorer(); BOOLEAN isMoving = pPathExplorer->IsMoving(); //const int iTile = pPathExplorer->GetTile(); //if ((WORD)iTile == m_DestTileNo) // return update_stop; if (isMoving == false) return update_stop; return update_continue; } BOOLEAN EscapeState::OnExit() { IPlanEventObject& rPlanEvent = m_pOwner->PlanEvent; rPlanEvent.RemoveField(IPlanEventObject::AIACT_ESCAPE); return false; } }; //end of namespace 'nsAI' //==================================================================================================