// NPC.cpp: implementation of the NPC class. // @history // ~09/12/29 // 09/12/29|waverix|change NPC moving controllers by '_NA001415_091123_CHANGE_MOB_AI_IN_SAFETY_AREA_' // |TARGETS={ ExecuteThrust, (Thrust)MoveAndBroadcast }| // //================================================================================================== #include "stdafx.h" #include "./NPC.h" // Utility #include //================================================================================================== // ProgramCommon #include #include #include #include #include #include //================================================================================================== #include "GameServerEx.h" #include "GameZone.h" #include "GameField.h" #include "GameRoom.h" #include "MovingPlanner.h" #include "CharacterFormula.h" #include "MoveStateControl.h" #include "NPCStateManager.h" #include "AIMessageQueue.h" #include "TargetManager.h" #include "StatusManager.h" #include "State.h" #include "EtcStatus.h" #include "RecoverStatus.h" #include "ChaoState.h" #include "Skill.h" #include "UnitListOfGroup.h" #include "UnitRegenInfo.h" #include "SummonManager.h" #include "SummonedNPCs.h" #include "Summoned.h" //================================================================================================== #include "GameParty.h" #include "GamePartyManager.h" #include "Player.h" #include "TargetFinder.h" //================================================================================================== // Construction/Destruction NPC::NPC() : m_pUnitRegenInfo(NULL) , m_pUnitListOfGroup(NULL) , m_pUnitGroupInfo( NULL ) { SetObjectType(NPC_OBJECT); // CHANGES: f110307.2L ;{ ns_object::SearchReferences ref_option = { 0, }; ref_option.hp = reinterpret_cast(&m_dwHP); _RegisterSearchReference(ref_option); }; m_pAIMessageQueue = new AIMessageQueue(eAIMESSAGE_DEFAULT_SIZE); m_pTempMessageQueue = new AIMessageQueue(eAIMESSAGE_TEMP_SIZE); m_pNPCStatusManager = new NPCStateManager(this); m_Attr.SetOwner( this ); m_pTargetManager = new TargetManager(this); } NPC::~NPC() { SAFE_DELETE( m_pAIMessageQueue ); SAFE_DELETE( m_pTempMessageQueue ); SAFE_DELETE( m_pNPCStatusManager ); SAFE_DELETE( m_pTargetManager ); } VOID NPC::Release() { m_LinkedAction = NULL; // OnDead¿¡¼­ Çϸé Á×Àº ÈÄ¿¡ ºÎȰÀÌ ¾ÈµÈ´Ù. °°Àº ÀûÀ» °ø°ÝÇÏ´ø ¾Ö¸¦ ºÎȰ½Ã۱⠶§¹®¿¡.. SetTargetChar( NULL ); Character::Release(); m_Attr.Clear(); m_pNPCStatusManager->Release(); m_SkillSelector.Release(); // AIMessage ÃʱâÈ­ m_pAIMessageQueue->Clear(); m_pTempMessageQueue->Clear(); m_pSkillSelectTimer->Stop(); // CHANGES, f100827.4L FreeAllSkillCoolTime( TRUE ); // CHANGES: f110311.4L add a slot type for a trigger area linker to support a multi-area link trigger_links_.Disconnect(NULL); } BOOL NPC::Init() { m_SelectedSkillCode = SKILLCODE_INVALID; m_wSelectedSkillDelay = 0; m_KillerType = MAX_OBJECT; m_dwKillerObjectKey = 0; m_bDestroy = FALSE; m_iAngle = -1; m_bGMIdleOn = FALSE; m_pUnitRegenInfo = NULL; m_pUnitListOfGroup = NULL; m_pUnitGroupInfo = NULL; // CHANGES: f110311.4L add a slot type for a trigger area linker to support a multi-area link trigger_links_.ns_trigger::TriggerNpcLinkInGameSlots::~TriggerNpcLinkInGameSlots(); new (&trigger_links_) ns_trigger::TriggerNpcLinkInGameSlots(this); // NOTE: need replacement, _NA_0_20100222_UNIT_TRIGGERS_FRIEND_MONSTER = { search_type_ = eRARGET_NOT_SEARCH; search_taget_unitID_ = 0; search_taget_monstercode_ = 0; search_taget_mapobjectID_ = 0; //} trigger_animation_code_ = 0; if (!Character::Init()) { return FALSE; } return TRUE; } VOID NPC::SetBaseInfo( WORD NPCCode, BYTE byMoveType, DWORD dwMoveAreaID, DWORD dwStateID, LPARAM param1 ) { m_pBaseInfo = NPCInfoParser::Instance()->GetNPCInfo( NPCCode ); ASSERT( m_pBaseInfo ); // HP, MP ȸº¹·ü int iRecoverHP = 0; int iRecoverMP = 0; int iRecoverSD = 0; if ( IsHPMPRegenNPC() ) { iRecoverHP = CalcNPCHPRecover( m_pBaseInfo->m_dwMaxHP ); iRecoverMP = CalcNPCMPRecover( m_pBaseInfo->m_dwMaxMP ); } if ( IsSDRegenNPC() ) { iRecoverSD = CalcNPCSDRecover( m_pBaseInfo->m_dwMaxSD ); } #ifdef _NA_007667_20141001_WITCHBLADE_SKILL // ¿©±â¼­ ½Ã°£À» ¼¼ÆÃÇØÁÖ¸é ½ºÅ³»ç¿ë¿¡ ¼±µôÀÌ ¹ß»ýÇÑ´Ù. // ¾îÂ÷ÇÇ ½ºÅ³»ç¿ë Èĸ¶´Ù SetTimeÇØÁÖ°í, 'È®·ü'ÀÌ Á¸ÀçÇϹǷΠÃʱâÈ­¶© 0À¸·ÎÇØµµ ¹«¹æ m_pSkillSelectTimer->SetTimer( 0 ); #else m_pSkillSelectTimer->SetTimer( m_pBaseInfo->m_wSkillUpdateTime ); #endif //_NA_007667_20141001_WITCHBLADE_SKILL m_Attr.SetCharInfo( m_pBaseInfo, iRecoverHP, iRecoverMP ,iRecoverSD ); m_Attr.Update(); // FSM ÃʱâÈ­ SetInitialState( dwStateID, byMoveType, dwMoveAreaID, param1 ); // ½ºÅ©¸³Æ®¿¡¼­ ¾ò¾î¿Â ½ºÇǵå´Â 0.001À» °öÇØÁØ´Ù. m_pMoveStateControl->Init( this, CMS_WALK ); SetHP( m_pBaseInfo->m_dwMaxHP ); SetMP( m_pBaseInfo->m_dwMaxMP ); SetSD( m_pBaseInfo->m_dwMaxSD ); m_pTargetManager->Init(); //_NA_0_20100222_UNIT_TRIGGERS_FRIEND_MONSTER search_type_ = FindSearchTypeByAttitude(m_pBaseInfo->m_byAttitude); // »ç¿ë °¡´ÉÇÑ ½ºÅ³µéÀ» µî·ÏÇÑ´Ù. AddDefaultSkill(); } // implemented by _NA_0_20100222_UNIT_TRIGGERS_FRIEND_MONSTER // °ø°Ý ¼ºÇâ°ú °Ë»ö ŸÀÔÀÇ º¯È¯ eTARGET_SEARCH_TYPE NPC::FindSearchTypeByAttitude(const BYTE attitude) { switch (attitude) { case ATTACK_ATTITUDE_AFTER: return eRARGET_NOT_SEARCH; case ATTACK_ATTITUDE_NEARES_FIRST: return eRARGET_SEARCH_NEAREST; case ATTACK_ATTITUDE_ONE_TARGET: return eRARGET_SEARCH_NONTARGET_LOW_HP; case ATTACK_ATTITUDE_LOW_HP_FIRST: return eRARGET_SEARCH_LOW_HP; case ATTACK_ATTITUDE_LOW_LEVEL_FIRST: return eRARGET_SEARCH_LOW_LEVEL; case ATTACK_ATTITUDE_HIGH_MP_FIRST: return eRARGET_SEARCH_HIGH_MP; //_NA001385_20090924_DOMINATION_MAPNPC_AI_BUG case ATTACK_ATTITUDE_NO_ATTACK: return eRARGET_SEARCH_NOT_SEARCH_AND_TARGET_CHNAGE; default: SUNLOG(eCRITICAL_LOG, "[NPC::FindSearchTypeByAttitude]Á¸ÀçÇÏÁö ¾Ê´Â °ø°Ý ¼ºÇâ " "MonsterCode[%d] attitude[%d]", m_pBaseInfo->m_MonsterCode, attitude); return eRARGET_NOT_SEARCH; } } void NPC::SetSearchType(eTARGET_SEARCH_TYPE search_type, DWORD object_key) { search_type_ = search_type; search_taget_unitID_ = 0; search_taget_mapobjectID_ = 0; search_taget_monstercode_ = 0; switch (search_type) { case eRARGET_SEARCH_MAPOBJECTID: search_taget_mapobjectID_ = object_key; break; case eRARGET_SEARCH_UNITID: search_taget_unitID_ = static_cast(object_key); break; case eRARGET_SEARCH_MONSTERCODE: search_taget_monstercode_ = static_cast(object_key); break; default: break; } } //================================================================================================== BOOL NPC::IsHPMPRegenNPC() { if ( !m_pBaseInfo ) return FALSE; switch ( m_pBaseInfo->m_byGrade ) { case eNPC_ELITE: case eNPC_LEADER: case eNPC_MIDDLEBOSS: case eNPC_BOSS: case eNPC_SUMMON_NPC: return TRUE; default: return FALSE; } } BOOL NPC::IsSDRegenNPC() { if (!m_pBaseInfo) return FALSE; // ¼­¹öÂÊ¿¡¼­´Â NPC µî±Þ º¸´Ù´Â °´Ã¼ ŸÀÔÀ¸·Î Æò°¡ [2/23/2010 lst1024] if (IsEqualObjectKind(SSQMONSTER_OBJECT) == 0) return TRUE; return FALSE; } //================================================================================================== VOID NPC::GetNPCRenderInfo( MONSTER_RENDER_INFO * OUT pRenderInfo ) { pRenderInfo->m_dwObjectKey = GetObjectKey(); pRenderInfo->m_dwMonsterCode = GetBaseInfo()->m_MonsterCode; GetPos( &pRenderInfo->m_wvPos ); pRenderInfo->m_dwMaxHP = GetMaxHP(); pRenderInfo->m_dwHP = GetHP(); pRenderInfo->m_wMoveSpeedRatio = (WORD)m_Attr.GetMoveSpeedRatio(); pRenderInfo->m_wAttSpeedRatio = m_Attr.GetAttSpeedRatio(); pRenderInfo->m_wAngle = 0; if ( m_iAngle >= 0 ) pRenderInfo->m_wAngle = (WORD)m_iAngle; // STATE_INFO¸¦ ä¿î´Ù. GetStatusManager()->SerialzieStatusInfo( pRenderInfo->m_StateTotalInfo ); } VOID NPC::GetSummonedRenderInfo( SUMMONED_RENDER_INFO * OUT pRenderInfo ) { ASSERT( !"[NPC::GetSummonedRenderInfo] ÀÏ·ç È£ÃâµÇ¸é ¾ÈµÈ´Ù!" ); SUNLOG( eCRITICAL_LOG, "[NPC::GetSummonedRenderInfo] ObjectType[%d] ÀÏ·ç È£ÃâµÇ¸é ¾ÈµÈ´Ù!", GetObjectType() ); } VOID NPC::GetTotemNPCRenderInfo( TOTEMNPC_RENDER_INFO * OUT pRenderInfo ) { ASSERT( !"[NPC::GetTotemNPCRenderInfo] ÀÏ·ç È£ÃâµÇ¸é ¾ÈµÈ´Ù!" ); SUNLOG( eCRITICAL_LOG, "[NPC::GetTotemNPCRenderInfo] ObjectType[%d] ÀÏ·ç È£ÃâµÇ¸é ¾ÈµÈ´Ù!", GetObjectType() ); } //================================================================================================== // profiling struct sProfileBlock { LARGE_INTEGER start_counter, end_counter; INT64 elapsedClock; INT64 elapsedMilliSecond; }; #ifdef _SUSUNLOVE_TEST_CODE_ #define PROFILING_NPC_UPDATE_START() \ QueryPerformanceCounter(&profile_storage.start_counter) #define PROFILING_NPC_UPDATE_CHECK() \ QueryPerformanceCounter(&profile_storage.end_counter) #define IF_PROFILING_NPC_UPDATE_OVERTHAN( millisecond ) \ profile_storage.elapsedClock = end_counter.QuadPart - start_counter.QuadPart; \ profile_storage.elapsedMilliSecond = profile_storage.elapsedClock * 1000 / g_pGameServer->m_freq.QuadPart; \ if ( profile_storage.elapsedMilliSecond >= (millisecond) ) #else #define PROFILING_NPC_UPDATE_START() #define PROFILING_NPC_UPDATE_CHECK() #define IF_PROFILING_NPC_UPDATE_OVERTHAN( millisecond ) #endif //================================================================================================== BOOL NPC::Update( DWORD dwDeltaTick ) { // need Time Profiling static sProfileBlock profile_storage; PROFILING_NPC_UPDATE_START(); #if WAVERIX_CODE_DELETE_RESERVED static MONSTERCODE npcCode_s; // (WAVERIX) debug code npcCode_s = this->GetBaseInfo()->m_MonsterCode; // (WAVERIX) debug code #endif if (Character::Update(dwDeltaTick) == FALSE) return FALSE; GameField* const pField = GetField(); if ( !pField ) return TRUE; GameZone* const pZone = pField->GetGameZone(); const sPOLICY_ZONE& ZonePolicy = pZone->Policyment; const SECTOR_IDX sector_index = GetSectorIndex(); BOOLEAN need_proc_state = FALSE; ////////////////////////////////////////////////////////////////////////// // FLOWCONTROL_START() { AIMsgProgress(); // Å¥¿¡ ½×ÀÎ AI ¸Þ¼¼Áö ó¸® Sector* const pSector = pField->GetSector( sector_index ); if ( !pSector ) break; need_proc_state = (ZonePolicy.FIELD & ZonePolicy.ENABLE_NPC_UPDATE_REF_SKIP) || pSector->GetReferenceCount(); if (need_proc_state == false) { if (Policyment.FIELD & Policyment.PCC_CONTINUING_NPC_UPDATE) { need_proc_state = true; } } #ifndef _SUSUNLOVE_TEST_CODE_ if ( need_proc_state ) #endif { const DWORD start_tick = GetTickCount(); #ifdef _SUSUNLOVE_TEST_CODE_ PROFILING_NPC_UPDATE_START(); BOOLEAN status_result = FALSE; PROFILE_TIME_TEST( status_result = PlanEvent->Update( dwDeltaTick, need_proc_state ); ) PROFILING_NPC_UPDATE_CHECK(); IF_PROFILING_NPC_UPDATE_OVERTHAN( 30 ) { SUNLOG( eCRITICAL_LOG, "[PathExp_Progress] ObjKey[%d] Tick[%I64d]", GetObjectKey(), profile_storage.elapsedMilliSecond ); } #else const BOOLEAN status_result = PlanEvent->Update( dwDeltaTick, need_proc_state ); #endif if ( !status_result ) return FALSE; } } FLOWCONTROL_END; ////////////////////////////////////////////////////////////////////////// // if ( !need_proc_state ) PlanEvent->Update( dwDeltaTick, FALSE ); // À̵¿ Áß ¹ß»ýÇÑ Æ®¸®°Å ¾×¼Ç¿¡ ÀÇÇØ Çʵå ÅðÀåÀÌ ÀÏ¾î ³µ´ÂÁö °Ë»çÇÑ´Ù. if (GetField() == NULL) { return FALSE; } // sector_index <- À̵¿ Àü ¼½ÅÍ À妽º const WzVector* const pwvCurPos = GetPosPtr(); const SECTOR_IDX new_sector_index = pField->GetSectorIndex( pwvCurPos ); // <- À̵¿ ÈÄ ¼½ÅÍ À妽º pField->CheckAndApplyChangeSector( this, new_sector_index ); return TRUE; } //================================================================================================== Character* NPC::SelectSkillTarget( SLOTCODE SkillCode, BOOL bHeal ) { SkillScriptInfo *pSkillInfo = SkillInfoParser::Instance()->GetSkillInfo( SkillCode ); if ( !pSkillInfo ) return NULL; GameField *pField = GetField(); if ( !pField ) return NULL; // ³ªÁß¿¡ ¼öÁ¤Á» ºÁ¾ßÇÔ... switch ( pSkillInfo->m_byTarget ) { case SKILL_TARGET_ENEMY: case SKILL_TARGET_REACHABLE_ENEMY: // ´ë»óÀÌ ÀûÀÏ °æ¿ì ÇöÀç Ÿ°ÙÀ» ±×´ë·Î À¯ÁöÇÑ´Ù. return GetTargetChar(); case SKILL_TARGET_FRIEND: case SKILL_TARGET_REACHABLE_FRIEND: { TargetFinderArg find_arg; find_arg.owner_ = this; find_arg.search_type_ = eRARGET_SEARCH_LOW_HPRATIO; find_arg.target_type_ = SKILL_TARGET_FRIEND; find_arg.unitID_ = 0; find_arg.mapobjectID_ = 0; find_arg.npc_code_ = 0; return pField->SearchTarget(find_arg); } case SKILL_TARGET_ME: case SKILL_TARGET_REACHABLE_ME: return this; case SKILL_TARGET_FRIEND_CORPSE: { TargetFinderArg find_arg; find_arg.owner_ = this; find_arg.search_type_ = eRARGET_SEARCH_CORPSE; find_arg.target_type_ = SKILL_TARGET_FRIEND; find_arg.unitID_ = 0; find_arg.mapobjectID_ = 0; find_arg.npc_code_ = 0; return pField->SearchTarget(find_arg); } case SKILL_TARGET_SUMMON: // ¼ÒȯÀ» ÇÒ °æ¿ì µû·Î Ÿ°ÙÀÌ Çʿ䰡 ¾øÀ¸¸ç ÇöÀç Ÿ°ÙÀ» ±×´ë·Î À¯ÁöÇÏ¸é µÈ´Ù. return GetTargetChar(); case SKILL_TARGET_AREA: return GetTargetChar(); default : SUNLOG( eFULL_LOG, "[NPC::SelectSkillTarget] Invalid TargetType[%d] NPCCode[%d] ", pSkillInfo->m_byTarget, GetBaseInfo()->m_MonsterCode ); } return GetTargetChar(); } VOID NPC::ChangeState( DWORD dwStateID, LPARAM param1, LPARAM param2, LPARAM param3 ) { m_pNPCStatusManager->ChangeState( dwStateID, param1, param2, param3 ); } // note2 : ¸ðÈ£ÇÔÀ» ÇØ°áÇϱâ À§ÇØ ÇÔ¼ö¸¦ Á¦°ÅÇÔ - arycoat. 2013.09.13 //float NPC::GetDistToTarget() const //{ // // (NOTE) »ý°¢ÇÏ¸é »ý°¢ÇÒ ¼ö·Ï ¹«´ë»Ç 󸮸¦ ¼öÇàÇÏ´Â °É·Î ÆÇ´ÜµÇ´Â ÇÔ¼ö´Ù. // // µµ´ëü ¹«½¼ ±Ù°Å¸¦ µÎ°í 󸮸¦ Çϴ°¡? ¹°·Ð º»ÀÎÀÇ °æ¿ì, ÀÌ·¸°Ô ÇÑ °æ¿ì°¡ ¾øÁö ¾Ê¾Æ Àִµ¥, // // ±× »ç·Ê¿¡ µû¸£¸é, À̰ÍÀº ±×¶§ ±×¶§ ÁïÈïÀûÀ¸·Î ÇÒ °æ¿ì¿¡³ª ³ª¿Ã ¹ýÇÑ ¹æ½ÄÀÌ´Ù. // // ½Ã³ª¸®¿À: ÀÌ·¯ÇÑ °è»êÀÌ ÇÊ¿äÇÏ´Ù. ¾ø¾î? Ãß°¡... ¾ø¾î? Ãß°¡... // // // Á» ´õ µûÁ® ºÁ¾ß°ÚÁö¸¸, ÃÖ´ëÇÑ ÀÌ·± °ÍµéÀ» »ç¿ëÇÏ´Â »ç·Ê¸¦ º¯°æ½Ã۵µ·Ï ÇØº»´Ù. // // ÀÌ°Í ¿Ü¿¡ °Å¸® °è»ê °ü·Ã in Character, in NPC, etc... // Character *pTarget = m_pTargetManager->GetCurTarget(); // // return GetDist( pTarget ); //} float NPC::GetDistToNewTarget( Character* pNewTargetChar ) const { return GetDist( pNewTargetChar ); } //================================================================================================== // for AI VOID NPC::SetInitialState( DWORD dwStateID, BYTE byMoveType, DWORD dwMoveAreaID, LPARAM param1 ) { if ( byMoveType ) { if ( !dwMoveAreaID ) { dwMoveAreaID = m_pBaseInfo->m_dwMoveAreaID; } } else { byMoveType = m_pBaseInfo->m_byMoveAttitude; dwMoveAreaID = m_pBaseInfo->m_dwMoveAreaID; } #ifdef _SUSUNLOVE_TEST_CODE_ byMoveType = eMOVE_ATTITUDE_WANDER; #endif // º¯½Å½Ã ±âÁ¸ »óŸ¦ Á¦°ÅÇÑ´Ù. m_pNPCStatusManager->ResetStateObject(); // ¿ÏÀü Á¤ÁöÇü if ( byMoveType == eMOVE_ATTITUDE_ETERNAL_STOP ) { m_pNPCStatusManager->AddStateObject( STATE_ID_IDLE, STATE_ID_STOP_IDLE ); m_pNPCStatusManager->AddStateObject( STATE_ID_WANDER, STATE_ID_STOP_IDLE ); m_pNPCStatusManager->AddStateObject( STATE_ID_TRACK, STATE_ID_STOP_IDLE ); m_pNPCStatusManager->AddStateObject( STATE_ID_HELP, STATE_ID_STOP_IDLE ); m_pNPCStatusManager->AddStateObject( STATE_ID_ATTACK, STATE_ID_ATTACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_DEAD, STATE_ID_DEAD ); m_pNPCStatusManager->AddStateObject( STATE_ID_KNOCKDOWN, STATE_ID_KNOCKDOWN ); m_pNPCStatusManager->AddStateObject( STATE_ID_CHAOS, STATE_ID_CHAOS ); m_pNPCStatusManager->AddStateObject( STATE_ID_BLIND, STATE_ID_BLIND ); m_pNPCStatusManager->AddStateObject( STATE_ID_DESTROY_OBJECT, STATE_ID_DESTROY_OBJECT ); m_pNPCStatusManager->AddStateObject( STATE_ID_SPECIAL_DEAD_ACTION, STATE_ID_SPECIAL_DEAD_ACTION ); } // ¿ÏÀü Á¤ÁöÇü(°ø°Ý¾ÈÇÔ, dead½Ã »ç¶óÁöÁö ¾ÊÀ½.) else if ( byMoveType == eMOVE_ATTITUDE_ETERNAL_STOP_NO_ATTACK ) { //»óÅ ÀüÀ̰¡ ¾ø´Â »óÅ m_pNPCStatusManager->AddStateObject( STATE_ID_IDLE, STATE_ID_NO_TRANS ); } // ½ºÆù Á¤ÁöÇü else if ( byMoveType == eMOVE_ATTITUDE_SPAWN_STOP ) { m_pNPCStatusManager->AddStateObject( STATE_ID_IDLE, STATE_ID_SPAWN_IDLE ); m_pNPCStatusManager->AddStateObject( STATE_ID_WANDER, STATE_ID_RETURN ); m_pNPCStatusManager->AddStateObject( STATE_ID_TRACK, STATE_ID_TRACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_ATTACK, STATE_ID_ATTACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_HELP, STATE_ID_HELP ); m_pNPCStatusManager->AddStateObject( STATE_ID_DEAD, STATE_ID_DEAD ); m_pNPCStatusManager->AddStateObject( STATE_ID_THRUST, STATE_ID_THRUST ); m_pNPCStatusManager->AddStateObject( STATE_ID_FLYING, STATE_ID_FLYING ); m_pNPCStatusManager->AddStateObject( STATE_ID_KNOCKDOWN, STATE_ID_KNOCKDOWN ); m_pNPCStatusManager->AddStateObject( STATE_ID_JUMP, STATE_ID_JUMP ); m_pNPCStatusManager->AddStateObject( STATE_ID_FALL_APART, STATE_ID_FALL_APART ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETURN, STATE_ID_RETURN ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETREAT, STATE_ID_RETREAT ); m_pNPCStatusManager->AddStateObject( STATE_ID_RUNAWAY, STATE_ID_RUNAWAY ); m_pNPCStatusManager->AddStateObject( STATE_ID_CHAOS, STATE_ID_CHAOS ); m_pNPCStatusManager->AddStateObject( STATE_ID_BLIND, STATE_ID_BLIND ); m_pNPCStatusManager->AddStateObject( STATE_ID_DESTROY_OBJECT, STATE_ID_DESTROY_OBJECT ); m_pNPCStatusManager->AddStateObject( STATE_ID_SPECIAL_DEAD_ACTION, STATE_ID_SPECIAL_DEAD_ACTION ); #ifdef _NA_007907_20141222_MODIFY_MONSTER_MOVE_TRIGGER m_pNPCStatusManager->AddStateObject( STATE_ID_TRACK_AREA, STATE_ID_TRACK_AREA ); #endif // _NA_007907_20141222_MODIFY_MONSTER_MOVE_TRIGGER } // ÆÐÆ®·Ñ else if ( byMoveType == eMOVE_ATTITUDE_PATROL ) { m_pNPCStatusManager->AddStateObject( STATE_ID_IDLE, STATE_ID_PATROL ); m_pNPCStatusManager->AddStateObject( STATE_ID_WANDER, STATE_ID_PATROL ); m_pNPCStatusManager->AddStateObject( STATE_ID_TRACK, STATE_ID_TRACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_ATTACK, STATE_ID_ATTACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_HELP, STATE_ID_HELP ); m_pNPCStatusManager->AddStateObject( STATE_ID_DEAD, STATE_ID_DEAD ); m_pNPCStatusManager->AddStateObject( STATE_ID_THRUST, STATE_ID_THRUST ); m_pNPCStatusManager->AddStateObject( STATE_ID_FLYING, STATE_ID_FLYING ); m_pNPCStatusManager->AddStateObject( STATE_ID_KNOCKDOWN, STATE_ID_KNOCKDOWN ); m_pNPCStatusManager->AddStateObject( STATE_ID_JUMP, STATE_ID_JUMP ); m_pNPCStatusManager->AddStateObject( STATE_ID_FALL_APART, STATE_ID_FALL_APART ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETURN, STATE_ID_RETURN ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETREAT, STATE_ID_RETREAT ); m_pNPCStatusManager->AddStateObject( STATE_ID_RUNAWAY, STATE_ID_RUNAWAY ); m_pNPCStatusManager->AddStateObject( STATE_ID_CHAOS, STATE_ID_CHAOS ); m_pNPCStatusManager->AddStateObject( STATE_ID_BLIND, STATE_ID_BLIND ); m_pNPCStatusManager->AddStateObject( STATE_ID_DESTROY_OBJECT, STATE_ID_DESTROY_OBJECT ); m_pNPCStatusManager->AddStateObject( STATE_ID_SPECIAL_DEAD_ACTION, STATE_ID_SPECIAL_DEAD_ACTION ); //_NA_0_20100817_HELLON_LAST_TRIGGER m_pNPCStatusManager->AddStateObject( STATE_ID_STOP_IDLE, STATE_ID_STOP_IDLE ); } // added by _NA_0_20100222_UNIT_TRIGGERS_PATHLIST = { else if (byMoveType == eMOVE_ATTITUDE_PATHLIST) { m_pNPCStatusManager->AddStateObject( STATE_ID_IDLE, STATE_ID_PATHLIST ); m_pNPCStatusManager->AddStateObject( STATE_ID_WANDER, STATE_ID_PATHLIST ); m_pNPCStatusManager->AddStateObject( STATE_ID_TRACK, STATE_ID_TRACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_ATTACK, STATE_ID_ATTACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_HELP, STATE_ID_HELP ); m_pNPCStatusManager->AddStateObject( STATE_ID_DEAD, STATE_ID_DEAD ); m_pNPCStatusManager->AddStateObject( STATE_ID_THRUST, STATE_ID_THRUST ); m_pNPCStatusManager->AddStateObject( STATE_ID_FLYING, STATE_ID_FLYING ); m_pNPCStatusManager->AddStateObject( STATE_ID_KNOCKDOWN, STATE_ID_KNOCKDOWN ); m_pNPCStatusManager->AddStateObject( STATE_ID_JUMP, STATE_ID_JUMP ); m_pNPCStatusManager->AddStateObject( STATE_ID_FALL_APART, STATE_ID_FALL_APART ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETURN, STATE_ID_RETURN ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETREAT, STATE_ID_RETREAT ); m_pNPCStatusManager->AddStateObject( STATE_ID_RUNAWAY, STATE_ID_RUNAWAY ); m_pNPCStatusManager->AddStateObject( STATE_ID_CHAOS, STATE_ID_CHAOS ); m_pNPCStatusManager->AddStateObject( STATE_ID_BLIND, STATE_ID_BLIND ); m_pNPCStatusManager->AddStateObject( STATE_ID_DESTROY_OBJECT, STATE_ID_DESTROY_OBJECT ); m_pNPCStatusManager->AddStateObject( STATE_ID_SPECIAL_DEAD_ACTION, STATE_ID_SPECIAL_DEAD_ACTION ); //_NA_0_20100817_HELLON_LAST_TRIGGER m_pNPCStatusManager->AddStateObject( STATE_ID_STOP_IDLE, STATE_ID_STOP_IDLE ); } else if (byMoveType == eMOVE_ATTITUDE_FOLLOW) { m_pNPCStatusManager->AddStateObject( STATE_ID_IDLE, STATE_ID_FOLLOW ); m_pNPCStatusManager->AddStateObject( STATE_ID_WANDER, STATE_ID_FOLLOW ); m_pNPCStatusManager->AddStateObject( STATE_ID_TRACK, STATE_ID_TRACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_ATTACK, STATE_ID_ATTACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_HELP, STATE_ID_HELP ); m_pNPCStatusManager->AddStateObject( STATE_ID_DEAD, STATE_ID_DEAD ); m_pNPCStatusManager->AddStateObject( STATE_ID_THRUST, STATE_ID_THRUST ); m_pNPCStatusManager->AddStateObject( STATE_ID_FLYING, STATE_ID_FLYING ); m_pNPCStatusManager->AddStateObject( STATE_ID_KNOCKDOWN, STATE_ID_KNOCKDOWN ); m_pNPCStatusManager->AddStateObject( STATE_ID_JUMP, STATE_ID_JUMP ); m_pNPCStatusManager->AddStateObject( STATE_ID_FALL_APART, STATE_ID_FALL_APART ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETURN, STATE_ID_FOLLOW ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETREAT, STATE_ID_RETREAT ); m_pNPCStatusManager->AddStateObject( STATE_ID_RUNAWAY, STATE_ID_RUNAWAY ); m_pNPCStatusManager->AddStateObject( STATE_ID_CHAOS, STATE_ID_CHAOS ); m_pNPCStatusManager->AddStateObject( STATE_ID_BLIND, STATE_ID_BLIND ); m_pNPCStatusManager->AddStateObject( STATE_ID_DESTROY_OBJECT, STATE_ID_DESTROY_OBJECT ); m_pNPCStatusManager->AddStateObject( STATE_ID_SPECIAL_DEAD_ACTION, STATE_ID_SPECIAL_DEAD_ACTION ); //_NA_0_20100817_HELLON_LAST_TRIGGER m_pNPCStatusManager->AddStateObject( STATE_ID_STOP_IDLE, STATE_ID_STOP_IDLE ); } //} end added ai list '_NA_0_20100222_UNIT_TRIGGERS_PATHLIST' #ifdef _NA_007907_20141222_MODIFY_MONSTER_MOVE_TRIGGER else if (byMoveType == eMOVE_ATTITUDE_ATTACK_GROUND) { // ¾îÅà ±×¶ó¿îµå Çü m_pNPCStatusManager->AddStateObject( STATE_ID_IDLE, STATE_ID_SPAWN_IDLE ); m_pNPCStatusManager->AddStateObject( STATE_ID_WANDER, STATE_ID_TRACK_AREA ); m_pNPCStatusManager->AddStateObject( STATE_ID_TRACK, STATE_ID_TRACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_ATTACK, STATE_ID_ATTACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_HELP, STATE_ID_HELP ); m_pNPCStatusManager->AddStateObject( STATE_ID_DEAD, STATE_ID_DEAD ); m_pNPCStatusManager->AddStateObject( STATE_ID_THRUST, STATE_ID_THRUST ); m_pNPCStatusManager->AddStateObject( STATE_ID_FLYING, STATE_ID_FLYING ); m_pNPCStatusManager->AddStateObject( STATE_ID_KNOCKDOWN, STATE_ID_KNOCKDOWN ); m_pNPCStatusManager->AddStateObject( STATE_ID_JUMP, STATE_ID_JUMP ); m_pNPCStatusManager->AddStateObject( STATE_ID_FALL_APART, STATE_ID_FALL_APART ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETURN, STATE_ID_RETURN ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETREAT, STATE_ID_RETREAT ); m_pNPCStatusManager->AddStateObject( STATE_ID_RUNAWAY, STATE_ID_TRACK_AREA ); m_pNPCStatusManager->AddStateObject( STATE_ID_CHAOS, STATE_ID_CHAOS ); m_pNPCStatusManager->AddStateObject( STATE_ID_BLIND, STATE_ID_BLIND ); m_pNPCStatusManager->AddStateObject( STATE_ID_DESTROY_OBJECT, STATE_ID_DESTROY_OBJECT ); m_pNPCStatusManager->AddStateObject( STATE_ID_SPECIAL_DEAD_ACTION, STATE_ID_SPECIAL_DEAD_ACTION ); m_pNPCStatusManager->AddStateObject( STATE_ID_TRACK_AREA, STATE_ID_TRACK_AREA ); } #endif // _NA_007907_20141222_MODIFY_MONSTER_MOVE_TRIGGER else { // ÀÚÀ¯ À̵¿Çü ¿Ü ±âŸ m_pNPCStatusManager->AddStateObject( STATE_ID_IDLE, STATE_ID_IDLE ); m_pNPCStatusManager->AddStateObject( STATE_ID_WANDER, STATE_ID_WANDER ); m_pNPCStatusManager->AddStateObject( STATE_ID_TRACK, STATE_ID_TRACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_ATTACK, STATE_ID_ATTACK ); m_pNPCStatusManager->AddStateObject( STATE_ID_HELP, STATE_ID_HELP ); m_pNPCStatusManager->AddStateObject( STATE_ID_DEAD, STATE_ID_DEAD ); m_pNPCStatusManager->AddStateObject( STATE_ID_THRUST, STATE_ID_THRUST ); m_pNPCStatusManager->AddStateObject( STATE_ID_FLYING, STATE_ID_FLYING ); m_pNPCStatusManager->AddStateObject( STATE_ID_KNOCKDOWN, STATE_ID_KNOCKDOWN ); m_pNPCStatusManager->AddStateObject( STATE_ID_JUMP, STATE_ID_JUMP ); m_pNPCStatusManager->AddStateObject( STATE_ID_FALL_APART, STATE_ID_FALL_APART ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETURN, STATE_ID_RETURN ); m_pNPCStatusManager->AddStateObject( STATE_ID_RETREAT, STATE_ID_RETREAT ); m_pNPCStatusManager->AddStateObject( STATE_ID_RUNAWAY, STATE_ID_RUNAWAY ); m_pNPCStatusManager->AddStateObject( STATE_ID_CHAOS, STATE_ID_CHAOS ); m_pNPCStatusManager->AddStateObject( STATE_ID_BLIND, STATE_ID_BLIND ); m_pNPCStatusManager->AddStateObject( STATE_ID_DESTROY_OBJECT, STATE_ID_DESTROY_OBJECT ); m_pNPCStatusManager->AddStateObject( STATE_ID_SPECIAL_DEAD_ACTION, STATE_ID_SPECIAL_DEAD_ACTION ); //_NA_0_20100817_HELLON_LAST_TRIGGER m_pNPCStatusManager->AddStateObject( STATE_ID_STOP_IDLE, STATE_ID_STOP_IDLE ); // ƯÁ¤¿µ¿ª ¿ì¼±°Ë»ö if ( byMoveType == eMOVE_ATTITUDE_SEARCH_AREA ) { m_pNPCStatusManager->AddStateObject( STATE_ID_TRACK_AREA, STATE_ID_TRACK_AREA ); // ƯÁ¤¿µ¿ªÀ¸·Î ´Þ·Á°¡¾ß ÇϹǷΠÃʱâ»óŸ¦ ´Þ·Á°¡´Â »óÅ·Π¼³Á¤ÇÑ´Ù. dwStateID = STATE_ID_TRACK_AREA; } } // Ãʱâ»óÅ ¼³Á¤ m_pNPCStatusManager->Init( dwStateID, byMoveType, dwMoveAreaID, param1 ); } VOID NPC::SendAIMessage( AI_MSG *pMsg, WORD wSize, DWORD dwDelay ) { m_pAIMessageQueue->SendAIMessage( pMsg, wSize, dwDelay ); } VOID NPC::SendAIMessageToGroupExceptMe( AI_MSG *pMsg, WORD wSize, DWORD dwDelay ) { if ( m_pUnitListOfGroup ) m_pUnitListOfGroup->SendAIMessage( pMsg, wSize, dwDelay, GetObjectKey() ); } VOID NPC::SendAIMessageAroundExceptMe( AI_MSG *pMsg, WORD wSize, DWORD dwDelay ) { GameField *pField = GetField(); if ( !pField ) return; pField->SendAIMessageAroundExceptMe( this, pMsg, wSize, dwDelay ); } // ÁÖº¯¿¡¼­ ÀûÀ» ã¾Æ¼­ ±× Æ÷ÀÎÅ͸¦ ¸®ÅÏÇÑ´Ù. Character* NPC::SearchTarget() { GameField *pField = GetField(); if ( !pField ) return NULL; TargetFinderArg find_arg; find_arg.owner_ = this; find_arg.search_type_ = search_type_; find_arg.target_type_ = SKILL_TARGET_ENEMY; find_arg.unitID_ = search_taget_unitID_; find_arg.mapobjectID_ = search_taget_mapobjectID_; find_arg.npc_code_ = search_taget_monstercode_; return pField->SearchTarget(find_arg); } Character* NPC::SearchTargetFriend() { GameField* field = GetField(); if (field == NULL) return NULL; TargetFinderArg find_arg; find_arg.owner_ = this; find_arg.search_type_ = search_type_; find_arg.target_type_ = SKILL_TARGET_FRIEND; find_arg.unitID_ = search_taget_unitID_; find_arg.mapobjectID_ = search_taget_mapobjectID_; find_arg.npc_code_ = search_taget_monstercode_; return field->SearchTarget(find_arg); } VOID NPC::AIMsgProgress() { // LARGE_INTEGER firstcounter,nextcounter; // __int64 elapsedclock, elapsedmillsecond; AI_MSG *pRecvMsg; WORD wSize; DWORD dwCurTick = GetTickCount(); pRecvMsg = (AI_MSG*)m_pAIMessageQueue->RecvAIMessage( wSize ); while ( pRecvMsg ) { //QueryPerformanceCounter(&firstcounter); if ( dwCurTick > pRecvMsg->dwDeliveryTick ) { m_pNPCStatusManager->GetCurState()->OnAIMsg( pRecvMsg ); } else { // ¸Þ¼¼Áö µô·¹À̸¸Å­ Áö³ªÁö ¾Ê¾ÒÀ¸¸é Àӽà ť¿¡ ³Ö°í ó¸®ÇÏÁö ¾Ê´Â´Ù. // µô·¹À̰ª ´Ù½Ã ³Ö¾îÁà¾ßÇÔ...Á½Ä¡¾ÈŸ -_-; m_pTempMessageQueue->SendAIMessage( pRecvMsg, wSize, pRecvMsg->dwDeliveryTick - dwCurTick ); } m_pAIMessageQueue->Completion( wSize ); //QueryPerformanceCounter(&nextcounter); //elapsedclock = nextcounter.QuadPart - firstcounter.QuadPart; //elapsedmillsecond = elapsedclock * 1000 / g_pGameServer->m_freq.QuadPart; //if ( elapsedmillsecond >= 10 ) //{ // SUNLOG( eCRITICAL_LOG, "[NPC::AIMsgProgress] ¾÷µ¥ÀÌÆ®°¡ ³Ê¹« ¿À·¡°É¸²! ObjKey[%d] CurState[%d] MsgID[%d] Tick[%d]", // GetObjectKey(), m_pNPCStatusManager->GetCurState()->GetStateID(), pRecvMsg->dwMsgID, elapsedmillsecond ); //} pRecvMsg = (AI_MSG*)m_pAIMessageQueue->RecvAIMessage( wSize ); } // Àӽà ť¿¡ ÀÖ´ø ¸Þ¼¼Áö ´Ù½Ã Áý¾î ³Ö±â pRecvMsg = (AI_MSG*)m_pTempMessageQueue->RecvAIMessage( wSize ); while ( pRecvMsg ) { // µô·¹À̰ª ´Ù½Ã ³Ö¾îÁà¾ßÇÔ...Á½Ä¡¾ÈŸ -_-; m_pAIMessageQueue->SendAIMessage( pRecvMsg, wSize, pRecvMsg->dwDeliveryTick - dwCurTick ); m_pTempMessageQueue->Completion( wSize ); pRecvMsg = (AI_MSG*)m_pTempMessageQueue->RecvAIMessage( wSize ); } } VOID NPC::SetTargetChar( Character *pTarget ) { m_pTargetManager->SetCurTarget( pTarget ); } Character* NPC::GetTargetChar() { Character* target = m_pTargetManager->GetCurTarget(); if (obj_util::IsValid(target, __FUNCTION__, __LINE__) == false) { SetTargetChar(NULL); return NULL; } return target; } VOID NPC::GroupCmdAttackToGroup( Character* pTargetChar ) { // ±×·ìÀÇ ¸®´õ¶ó¸é if ( IsLeader() ) //if ( GetGroupState().IsLeader() ) { AI_MSG_GROUP_C0MMAND AIGroupCmdMsg; AIGroupCmdMsg.byType = GROUP_CMD_TYPE_ATTACK; AIGroupCmdMsg.dwTargetKey = pTargetChar->GetObjectKey(); SendAIMessageToGroupExceptMe( &AIGroupCmdMsg, sizeof(AI_MSG_GROUP_C0MMAND) ); } } VOID NPC::GroupCmdStopAttackToGroup() { // ±×·ìÀÇ ¸®´õ¶ó¸é if ( IsLeader() ) //if ( GetGroupState().IsLeader() ) { AI_MSG_GROUP_C0MMAND AIGroupCmdMsg; AIGroupCmdMsg.byType = GROUP_CMD_TYPE_STOP_ATTACK; SendAIMessageToGroupExceptMe( &AIGroupCmdMsg, sizeof(AI_MSG_GROUP_C0MMAND) ); } } //================================================================================================== // moving supporter BOOL NPC::IsSamePositionWithTarget() { WzVector vTargetPos, vMyPos; if (GetTargetChar() == NULL) return FALSE; GetTargetChar()->GetPos( &vTargetPos ); GetPos( &vMyPos ); if ( vMyPos == vTargetPos ) { return TRUE; } else { return FALSE; } } VOID NPC::GetTrackPos( WzVector &wvDestPos, float fAttackRangeRatio ) { // ÀÌÀü¿¡ ÃßÀûÇÏ´ø Ÿ°Ù½½·ÔÀ» ReleaseÇÑ´Ù. GetTargetChar()->FreeEnemySlot( m_pTargetManager->GetTrackSlot() ); // »õ·Î¿î Ÿ°Ù½½·ÔÀ» ã¾Æ¼­ Á¤º¸¸¦ °»½ÅÇÑ´Ù. WzVector vMyPos; GetPos( &vMyPos ); BYTE byTrackSlot = GetTargetChar()->AllocEnemySlot( &vMyPos, wvDestPos, fAttackRangeRatio ); m_pTargetManager->SetTrackSlot( byTrackSlot ); } BOOL NPC::OnResurrection( float fRecoverExp, float fRecoverHP, float fRecoverMP, Player *pHealer ) { if ( Character::OnResurrection( fRecoverExp, fRecoverHP, fRecoverMP, pHealer ) ) { ChangeState( STATE_ID_IDLE ); return TRUE; } return FALSE; } VOID NPC::OnDead() { SASSERT( GetHP() == 0, "GetHP() != 0" ); // (WAVERIX)(080923)(NOTE) ó¸® º¯°æÇÒ ½Ã°£ÀÌ ¾ø´Ù. ±âÁ¸ ·ÎÁ÷ ÇüÅ·Π¹¯¾î°£´Ù. //!~ too many state change try! - arycoat 2014.08.14 State* pState = GetStateManager()->GetCurState(); if (!pState || pState->GetStateID() == STATE_ID_DEAD) return; //~! ChangeState(STATE_ID_DEAD, m_pBaseInfo->m_dwDeadWaitingTime); SetTargetChar( NULL ); m_pAIMessageQueue->Clear(); Character::OnDead(); } BOOL NPC::ExecuteThrust(BOOL bForcedMove, WzVector* pwvMoveDistance, WzVector& wvPosAfterThrust, float fMoveDistance, BOOL is_down_after_thrust) // ¹Ð¸®±â ÈÄ¿¡ ´Ù¿î »óÅ·Π³Ñ¾î°¥ °ÍÀΰ¡? { // (CHANGES) (091229) (WAVERIX) code-arrangement for NPC moving control GameField* pField = GetField(); if (pField == NULL) return false; // ¿ÏÀü Á¤ÁöÇüÀÎ °æ¿ì ¹Ð¸®±â°¡ ¾ÈµÈ´Ù. if (m_pBaseInfo->m_byMoveAttitude == eMOVE_ATTITUDE_ETERNAL_STOP || m_pBaseInfo->m_byMoveAttitude == eMOVE_ATTITUDE_ETERNAL_STOP_NO_ATTACK) { return false; } // TODO: ¹Ð¸®´Â ¹æÇâÀÌ ¾ø´Â °æ¿ì ¹®Á¦°¡ »ý±ä´Ù. ·£´ýÀ¸·Î?? if (pwvMoveDistance->x == 0.0f && pwvMoveDistance->y == 0.0f) { pwvMoveDistance->x = 1.0f; WzVector wvTemp = *pwvMoveDistance; int angle = State::GetStateRandomNo() % 360; Math_ZRotateVector(&wvTemp, *pwvMoveDistance, angle); *pwvMoveDistance = *pwvMoveDistance * fMoveDistance; } // NPC ¹Ð¸®±â ó¸® if (pField->FindPathThrust(this, pwvMoveDistance, bForcedMove) == false) return false; // (NOTE) FindPathThrust¿¡ µû¸¥ ¹«ÀǹÌÇÑ °ËÁõÀÌ µÇ¾î¾ß ÇÒ °ÍÀÌ´Ù. if (IsMoving() == false) return false; // ¹Ð¸®±â ÈÄÀÇ À̵¿ ÁÂÇ¥ ¾ò±â wvPosAfterThrust = GetPathExplorer()->GetTargetPos(); // NPC¿¡°Ô ¹Ð¸®±â ¸Þ¼¼Áö¸¦ º¸³¿ AI_MSG_THRUST thrust_msg; thrust_msg.bDownAfterThrust = !!is_down_after_thrust; SendAIMessage(&thrust_msg, sizeof(thrust_msg)); return true; } BOOL NPC::IsTooCloseToTarget() { if (GetTargetChar() == NULL) return FALSE; // Ÿ°ÙÀÌ º»ÀÎÀÏ °æ¿ì(¼¿ÇÁÈú) if ( GetTargetChar() == this ) return FALSE; // ³Ê¹« °¡±î¿î °æ¿ì.. ¿ÀÈ÷·Á ¸Ö¾îÁöÀÚ // (!) PlayerÀÇ °ø°Ý°Å¸®¶û Â÷À̰¡ ¸¹ÀÌ ³ª´Â °æ¿ì´Â Ç÷¹À̾ ´Ù°¡°¡¸é ¹«Á¶°Ç ¸Ö¾îÁö´Â °æÇâÀ» º¸ÀÏ ¼ö ÀÖ´Ù. // --> Àû´çÈ÷ ±âº» °ø°Ý°Å¸®¸¦ ÅëÀÏÇϵçÁö Çã¿ë ¹üÀ§¸¦ Á¶ÀýÇϵçÁö ÇØ¾ßÇÒµí AI_COMMON_INFO& stAICommonInfo = AIParamParser::Instance()->GetCommonInfo(); WzVector vCurPos, vTargetPos; GetPos( &vCurPos ); GetTargetChar()->GetPos( &vTargetPos ); float fDistToTarget2 = VectorLength2( &(vCurPos-vTargetPos) ); // Ÿ°Ù°úÀÇ °Å¸®°¡ MAX_ATTACK_RANGE_LIMITÀÌ»óÀÌ¸é ±×³É °ø°Ý->Àå°Å¸®ÀÎ ¾ÖµéÀÌ ³Ê¹« ¸Ö¾îÁö´Â°É ¹æÁö if ( fDistToTarget2 < stAICommonInfo.m_fFALLAPART_MIN_ATTACK_RANGE_LIMIT*stAICommonInfo.m_fFALLAPART_MIN_ATTACK_RANGE_LIMIT ) { return TRUE; } return FALSE; } BOOL NPC::NeedToRetreatFromTarget() { Character* target = GetTargetChar(); if (target == NULL) return FALSE; if ( GetSelectedSkill() != SKILLCODE_NORMAL_ATTACK ) return FALSE; // °ø°Ý°Å¸®°¡ 5m´Â ³Ñ¾î¾ß ÈÄÅð °ø°ÝÀ» ÇÑ´Ù. AI_COMMON_INFO& stAICommonInfo = AIParamParser::Instance()->GetCommonInfo(); if ( GetAttackRange()>=stAICommonInfo.m_fRETREAT_MIN_ATTACKRANGE_LIMIT ) { float fDistToTarget = GetDist(target); float fMinAttackDist = GetAttackRange() * stAICommonInfo.m_fRETREAT_MIN_DISTANCE_LIMIT_RATIO; if (fDistToTarget < fMinAttackDist) { return TRUE; } } return FALSE; } // Ÿ°Ùº¸´Ù Àû´çÈ÷ ³ôÀº À§Ä¡¿¡ ÀÖ°í ¼öÆò °Å¸®µµ ÃæºÐÈ÷ °¡±î¿ö¾ß Á¡ÇÁÇÒ ¼ö ÀÖÀ¸¹Ç·Î üũÇÑ´Ù. BOOL NPC::IsOnGoodPositionToJump() { if ( !GetTargetChar() ) return FALSE; WzVector wvCurPos, wvTargetPos; GetPos( &wvCurPos ); GetTargetChar()->GetPos( &wvTargetPos ); // ³ôÀ̰¡ ³Ê¹« ³ô°Å³ª ³Ê¹« ³·À¸¸é ½ÇÆÐ float fHeight = wvCurPos.z - wvTargetPos.z; AI_COMMON_INFO& stAICommonInfo = AIParamParser::Instance()->GetCommonInfo(); if ( fHeight < stAICommonInfo.m_fJUMP_MIN_HEIGHT || fHeight > stAICommonInfo.m_fJUMP_MAX_HEIGHT ) { return FALSE; } // ¼öÆò °Å¸®°¡ ³Ê¹« ¸Ö°Å³ª ³Ê¹« °¡±î¿ì¸é ½ÇÆÐ float fDistance = sqrt( ( wvTargetPos.x - wvCurPos.x ) * ( wvTargetPos.x - wvCurPos.x ) + ( wvTargetPos.y - wvCurPos.y ) * ( wvTargetPos.y - wvCurPos.y ) ); if ( fDistance < stAICommonInfo.m_fJUMP_MIN_DISTANCE || fDistance > stAICommonInfo.m_fJUMP_MAX_DISTANCE ) { return FALSE; } m_vJumpPos = wvTargetPos; return TRUE; } // ±æÃ£±â¸¦ ÇÏ°í ÆÐŶÀ» ³¯¸² BOOL NPC::MoveAndBroadcast(WzVector* pwvDestPos, eCHAR_MOVE_STATE moveState) { // (CHANGES) (091229) (WAVERIX) code-arrangement for NPC moving control GameField* pField = GetField(); if (pField == NULL) return false; #ifdef _SUSUNLOVE_TEST_CODE_ LARGE_INTEGER firstcounter,nextcounter; QueryPerformanceCounter(&firstcounter); #endif // ±æÃ£±â... ½ÇÆÐ½Ã ¸®ÅÏ if (pField->FindPath(this, pwvDestPos) == false) return false; #ifdef _SUSUNLOVE_TEST_CODE_ QueryPerformanceCounter(&nextcounter); __int64 elapsedclock = nextcounter.QuadPart - firstcounter.QuadPart; __int64 elapsedmillsecond = elapsedclock * 1000000 / g_pGameServer->m_freq.QuadPart; if (elapsedmillsecond >= 20) { //SUNLOG(eCRITICAL_LOG, "[PathExp_FindPath] ObjKey[%d] Tick[%I64d]", GetObjectKey(), elapsedmillsecond); } #endif SetMoveState(moveState); // changes NPC move state = { RUN/WALK/etc... } { //broadcast const WzVector* const pMobPos = GetPosPtr(); MSGSUB_SYNC_MOVE_BRD msg; msg.m_dwObjectKey = GetObjectKey(); msg.m_byState = (BYTE)moveState; // RUN or WALK msg.m_ForcedMove = false; msg.m_wvCurPos = *pMobPos; msg.m_wvDestPos = *pwvDestPos; SendPacketAroundForSync(&msg); }; return true; } BOOL NPC::CanJumpDist( Character *pTarget ) { if ( !pTarget ) return FALSE; AI_COMMON_INFO& stAICommonInfo = AIParamParser::Instance()->GetCommonInfo(); WzVector vCurPos, vTargetPos; GetPos( &vCurPos ); pTarget->GetPos( &vTargetPos ); // Ÿ°Ù°úÀÇ °Å¸®°¡ ÀÏÁ¤ ¹üÀ§¿¡ µé¾î¾ß Á¡ÇÁ¸¦ ÇÑ´Ù. float fDistToTarget = VectorLength( &(vCurPos-vTargetPos) ); if ( fDistToTarget < stAICommonInfo.m_fJUMP_ATTACK_MIN_DISTANCE || fDistToTarget > stAICommonInfo.m_fJUMP_ATTACK_MAX_DISTANCE ) return FALSE; return TRUE; } BOOL NPC::SetJumpPos( Character *pTarget ) { if ( !pTarget ) return FALSE; GameField *pField = GetField(); if ( !pField ) return FALSE; AI_COMMON_INFO& stAICommonInfo = AIParamParser::Instance()->GetCommonInfo(); WzVector vCurPos, vTargetPos, vNormal, vDistanceWantToMove, vDestPosAfterThrust; // ÇöÀç À§Ä¡ GetPos( &vCurPos ); pTarget->GetPos( &vTargetPos ); // Á¡ÇÁ ¸ñÀûÁö °è»ê VectorNormalize( &vNormal, &( vCurPos-vTargetPos ) ); vNormal.z = 0; float fAttackRange = max( stAICommonInfo.m_fFALLAPART_MIN_ATTACK_RANGE_LIMIT, GetAttackRange()*stAICommonInfo.m_fJUMP_ATTACK_RANGE_RATIO ); m_vJumpPos = vTargetPos + vNormal * fAttackRange; vDistanceWantToMove = m_vJumpPos - vCurPos; float fWantToMoveLength = VectorLength2( &vDistanceWantToMove ); // ±æÃ£±â °¡´ÉÇÑÁö üũ if ( !pField->FindPathThrust( this, &vDistanceWantToMove ) ) { return FALSE; } // FindPathÈÄ ¼³Á¤µÈ °Å¸®°¡ ÀǵµÇÑ °Å¸®¿¡ ¸ø¹ÌÄ¡¸é Àå¾Ö¹°¿¡ ´êÀº °ÍÀ¸·Î °£ÁÖÇÑ´Ù. vDestPosAfterThrust = GetPathExplorer()->GetTargetPos(); float fMoveLength = VectorLength2( &(vDestPosAfterThrust - vCurPos) ); // Thrust °Å¸®°¡ ÀǵµÇÑ °Å¸®¸¸Å­ ¾ÈµÇ´Â °æ¿ì if ( fMoveLength < fWantToMoveLength * 0.95f ) { StopMoving(); return FALSE; } SetMoveState( CMS_MONSTER_ATTACKJUMP ); return TRUE; } BOOL NPC::ThrustMoveAndBroadcast(WzVector* pwvDestPos, eCHAR_MOVE_STATE moveState, BOOL& bTouchedSomething, WzVector& wvRotatedVector) { GameField* pField = GetField(); if (pField == NULL) return false; const WzVector& wvCurPos = *GetPosPtr(); WzVector& wvDistanceWantToMove = *pwvDestPos - wvCurPos; float fWantToMoveLength = VectorLength2(&wvDistanceWantToMove); // ¸ñÀûÁö±îÁö ±æÃ£±â °æ·Î¸¦ ¼ÂÆÃÇÑ´Ù. #ifdef _SUSUNLOVE_TEST_CODE_ LARGE_INTEGER firstcounter,nextcounter; QueryPerformanceCounter(&firstcounter); BOOL bResult = false; // PROFILE_TIME_TEST(bResult = pField->FindPathThrust(this, &wvDistanceWantToMove);) PROFILE_TIME_TEST(bResult = pField->FindPath(this, pwvDestPos);) if (bResult == false) return false; QueryPerformanceCounter(&nextcounter); __int64 elapsedclock = nextcounter.QuadPart - firstcounter.QuadPart; __int64 elapsedmillsecond = elapsedclock * 1000000 / g_pGameServer->m_freq.QuadPart; if (elapsedmillsecond >= 20) { SUNLOG(eCRITICAL_LOG, "[PathExp_Thrust] ObjKey[%d] Tick[%I64d]", GetObjectKey(), elapsedmillsecond); } else { SUNLOG(eCRITICAL_LOG, "[PathExp_Thrust] No Delay!! ObjKey[%d] Tick[%I64d]", GetObjectKey(), elapsedmillsecond); } #else if (pField->FindPathThrust(this, &wvDistanceWantToMove) == false) { return false; } #endif // FindPathÈÄ ¼³Á¤µÈ °Å¸®°¡ ÀǵµÇÑ °Å¸®¿¡ ¸ø¹ÌÄ¡¸é Àå¾Ö¹°¿¡ ´êÀº °ÍÀ¸·Î °£ÁÖÇÑ´Ù. const WzVector& wvDestPosAfterThrust = GetPathExplorer()->GetTargetPos(); float fMoveLength = VectorLength2(&(wvDestPosAfterThrust - wvCurPos)); // Thrust °Å¸®°¡ ÀǵµÇÑ °Å¸®¸¸Å­ ¾ÈµÇ´Â °æ¿ì if (fMoveLength < fWantToMoveLength * 0.95f) { // Àå¾Ö¹°¿¡ ´ê¾Ò´Ù´Â Ç¥½Ã¸¦ Çϰí Áö±Ý °¡·Á´Â ¹æÇâ¿¡¼­ 49µµ ȸÀü½ÃŲ º¤Å͸¦ ±¸ÇصдÙ. bTouchedSomething = true; const float calc_cos = g_SinTable.FCOS(AIParamParser::AICommonInfo.m_wSEARCH_ROTATE_ANGLE); const float calc_sin = g_SinTable.FSIN(AIParamParser::AICommonInfo.m_wSEARCH_ROTATE_ANGLE); wvRotatedVector.x = wvDistanceWantToMove.x * calc_cos + wvDistanceWantToMove.y * calc_sin; wvRotatedVector.y = -wvDistanceWantToMove.x * calc_sin + wvDistanceWantToMove.y * calc_cos; wvRotatedVector.z = 0.0f; // (NOTE) (WAVERIX) Á» ´õ µûÁ®º¼ ÇÊ¿ä ÀÖ´Ù. À̵¿ÇÏÁö ¾ÊÀ» °æ¿ì »ý±â´Â ¹®Á¦´Â? // ¸¸¾à À̵¿Àº ÇÏ°í ¼º°øÃ³¸®½Ã¿¡´Â? // À̵¿ °Å¸®°¡ ³Ê¹« ªÀ¸¸é À̵¿ÇÏÁö ¸»°í(ÆÐŶ ¼Û½Å ¾øÀÌ) ±×³É ¸®ÅÏ if (fMoveLength < AIParamParser::AICommonInfo.m_fMIN_MOVABLE_DISTANCE * AIParamParser::AICommonInfo.m_fMIN_MOVABLE_DISTANCE) { StopMoving(); return false; } } SetMoveState(moveState); { MSGSUB_SYNC_MOVE_THRUST_BRD msg; msg.m_dwObjectKey = GetObjectKey(); msg.m_byState = moveState; msg.m_wTileIndex = GetPathExplorer()->GetTile(); // (WAVERIX) (081209) (BUG-FIX) ¹«Á¶°Ç ´Þ¸®±â ¼öÁ¤ //msg.m_byState = CMS_RUN; //ij¸¯ÅÍÀÇ °æ¿ì´Â ¹«Á¶°Ç ¶Ù±â¸¸ Á¸Àç!!(¸ó½ºÅÍ¿Í °°ÀÌ ¾²´Â ÆÐŶÀ̹ǷΠ°ªÀº ³Ö¾îÁÜ) msg.m_wvCurPos = wvCurPos; msg.m_wvDestPos = wvDestPosAfterThrust; SendPacketAroundForSync(&msg); WAVERIX_WRAPPER((WAVERIX_LC_CALL2("waverix_log_npcmove", this, &msg))); }; return true; } float NPC::GetAttackRange() const { float fAttackRange = GetNormalRangeBonus(); if ( GetSelectedSkill() == SKILLCODE_INVALID ) { // ÀÌ ÇÔ¼ö´Â 'ÀûÀÌ °ø°Ý»ç°Å¸®³»¿¡ µé¾î¿Ô´Â°¡?'¸¦ üũÇÒ ¶§¿¡µµ »ç¿ëÇϰí ÀÌ ¶§¿£, ´ë°³ ÇØ´ç ºÐ±â¸¦ Ÿ°ÔµÈ´Ù. // ÃßÀû»óÅ¿¡¼­ ºÒ·ÈÀ» ½Ã¿¡ À̺κÐÀ¸·Î // ÀÌ ºÐ±â¹®¿¡ µé¾î¿À´Â °ÍÀº ÀϾ ¼ö ÀÖ´Â ÀÏÀ̹ǷΠASSERT Á¦°Å //ASSERT( GetSelectedSkill() != SKILLCODE_INVALID ); fAttackRange += 2.5f; } else if ( GetSelectedSkill() == SKILLCODE_NORMAL_ATTACK ) { // ÀÏ¹Ý °ø°Ý½Ã NPCÀÇ ±âº» °ø°Ý »ç°Å¸®¸¦ ¸®ÅÏ fAttackRange += m_pBaseInfo->m_fAttRange; } else { // ½ºÅ³ °ø°Ý½Ã ÇØ´ç ½ºÅ³ÀÇ °ø°Ý »ç°Å¸®¸¦ ¸®ÅÏ SkillScriptInfo *pBaseSkillInfo = SkillInfoParser::Instance()->GetSkillInfo( GetSelectedSkill() ); if ( NULL == pBaseSkillInfo ) { SUNLOG( eCRITICAL_LOG,"[NPC::GetAttackRange] SkillInfo is NULL [%d]\n", GetSelectedSkill() ); return 1.0f; } fAttackRange = pBaseSkillInfo->m_wSkillRange/10.f + GetSkillRangeBonus(); } return fAttackRange + GetBodyRadius(); } BOOL NPC::CheckSkillRange( Character *pTarget, const WzVector &vTargetPos, float fSkillRange ) { fSkillRange += GetBodyRadius(); return Character::CheckSkillRange( pTarget, vTargetPos, fSkillRange ); } //================================================================================================== eUSER_RELATION_TYPE NPC::IsFriend( Character *pTarget ) { if (obj_util::IsValid(pTarget, __FUNCTION__, __LINE__) == false) { return USER_RELATION_NEUTRALIST; } // º»ÀÎÀÎÁö üũÇÑ´Ù. if ( this == pTarget ) return USER_RELATION_FRIEND; // º»Àΰú Ÿ°ÙÀÇ ¼ÒȯµÈ ¿©ºÎ¸¦ È®ÀÎÇÑ´Ù. Character* pMySummoner = NULL; Character* pTargetSummoner = NULL; #ifdef _NA_000000_20140224_SUMMONER_REFACTORING if (this->IsEqualObjectKind(SUMMON_OBJECT)) { pMySummoner = GetField()->FindCharacter(this->GetSummonerKey()); } if (pTarget->IsEqualObjectKind(SUMMON_OBJECT)) { pTargetSummoner = pTarget->GetField()->FindCharacter(pTarget->GetSummonerKey()); } #else SummonedNPCs* pMySummon = SummonManager::Instance()->FindSummonNPCs( m_dwSummonerKey ); SummonedNPCs* pTargetSummon = SummonManager::Instance()->FindSummonNPCs( pTarget->GetSummonerKey() ); if ( pMySummon ) { pMySummoner = pMySummon->GetSummoner(); if ( pMySummoner == this ) pMySummoner = NULL; } if ( pTargetSummon ) { pTargetSummoner = pTargetSummon->GetSummoner(); if ( pTargetSummoner == pTarget ) pTargetSummoner = NULL; } #endif //_NA_000000_20140224_SUMMONER_REFACTORING if ( pMySummoner && pTargetSummoner ) { // º»ÀμÒȯÀÚÀÇ Å¸ÀÔ°ú Ÿ°Ù¼ÒȯÀÚÀÇ Å¸ÀÔÀÌ °°À¸¸é ¾Æ±ºÀÌ µÈ´Ù. return pMySummoner->IsFriend( pTargetSummoner ); } else if ( pMySummoner ) { // º»ÀμÒȯÀÚÀÇ Å¸ÀÔ°ú Ÿ°ÙÀÇ Å¸ÀÔÀÌ °°À¸¸é ¾Æ±ºÀÌ µÈ´Ù. return pMySummoner->IsFriend( pTarget ); } else if ( pTargetSummoner ) { // º»ÀΠŸÀÔ°ú Ÿ°Ù¼ÒȯÀÚÀÇ Å¸ÀÔÀÌ °°À¸¸é ¾Æ±ºÀÌ µÈ´Ù. return IsFriend( pTargetSummoner ); } if ( Policyment.FIELD & Policyment.PCC_IN_SSQ_FIELD ) { // SSQMonster´Â ¸ó½ºÅÍµé ³¢¸®µµ ÀûÀ¸·Î ÀνÄÇÑ´Ù. // Æ®·¦ °è¿­ NPCµéÀ» °ø°ÝÇÏ´Â °ÍÀ» ¹æÁöÇϱâ À§Çؼ­ À̵鸸 Ä£±¸·Î ÀνÄÇϵµ·Ï ÇÑ´Ù. if (pTarget->IsEqualObjectKind(MAPNPC_OBJECT)) return USER_RELATION_FRIEND; return USER_RELATION_ENEMY; } else { // °°Àº ¸ó½ºÅͳ¢¸®´Â ¾Æ±ºÀ¸·Î ÆÇÁ¤ÇÑ´Ù. if (pTarget->IsEqualObjectKind(NPC_OBJECT)) { NPC* const npc = static_cast(pTarget); const BYTE grade = npc->GetGrade(); // CHANGES: f110525.9L, add iff relations about eNPC_MOVE_SYNC_MERCHANT if (grade != eNPC_FRIEND_MAPOBJECT_NPC && grade != eNPC_FRIEND_MONSTER && grade != eNPC_MOVE_SYNC_MERCHANT) { return USER_RELATION_FRIEND; } else if ((grade == eNPC_MOVE_SYNC_MERCHANT) && (npc->Policyment.FIELD & npc->Policyment.PCC_PROTECTED_NPC) == 0) { // CHANGES: f110704.5L, add iff relations to support the custom map unit. // see the NpcExtraInfo.NpcType and ExtraNPCManager::CheckProtectedNpc // the concept of 'protected' is based on player view. return USER_RELATION_FRIEND; } else { return USER_RELATION_ENEMY; } } else { return USER_RELATION_ENEMY; } } return USER_RELATION_NEUTRALIST; } VOID NPC::OnAttack( Character *pTarget, SLOTCODE skillcode, DAMAGETYPE damage ) { Character::OnAttack( pTarget, skillcode, damage ); // ÃÖÃÊ·Î °ø°ÝÀ» °¡ÇÑ ³Ñ¿¡°Ô ÃÊ±â Æ÷ÀÎÆ®¸¦ ÁÖ±âÀ§ÇØ AddAttackPoint¸¦ È£ÃâÇÑ´Ù. m_pTargetManager->AddBattlePoint( pTarget, BATTLE_POINT_DAMAGE, 0 ); } void NPC::_Damaged(DamageArgs* damage_arg) { Character* pAttacker = damage_arg->actor_; // (WAVERIX)(081013)(NOTE) ³Ê¹« ±æ°í Áߺ¹ÀÌ ¸¹´Ù. µðÆÄÀÎ Á¦°Å ÀÛ¾÷ÀÌ ³¡³ª¸é Á¤¸®Çϱâ·Î ÇÑ´Ù. if ( Policyment.FIELD & Policyment.PCC_IN_SSQ_FIELD ) { int damage = damage_arg->damage_; if (damage) { damage = (damage >> 1); if (damage == 0) { damage = 1; } damage_arg->damage_ = damage; } } if (damage_arg->damage_ == 0) { return; } #ifndef _NA_0_20100819_AGGRO_RENEWAL if ( pAttacker ) { DAMAGETYPE wResultDamage = damage_arg->damage_; DWORD hp = GetHP(); if ( hp <= damage_arg->damage_ ) wResultDamage = (DAMAGETYPE)hp; // °è»êµÈ µ¥¹ÌÁö Ãß°¡? ½ÇÁ¦ Àû¿ë µ¥¹ÌÁö¸¦ Ãß°¡ÇØ¾ß Çϴ°ÍÀÌ ¾Æ´Ñ°¡? [2/17/2010 lst1024] m_pTargetManager->AddBattlePoint( pAttacker, BATTLE_POINT_DAMAGE, wResultDamage ); } #endif Character::_Damaged(damage_arg); if ( pAttacker && IsDead() ) { Player* pAttackPlayer = pAttacker->GetPlayerOwner(); if ( pAttackPlayer ) { ChaoState& rChaoState = pAttackPlayer->GetChaoState(); rChaoState.KillMonster( this ); // ¸ó½ºÅͰ¡ Ä«¿À Ç÷¹À̾î·ÎºÎÅÍ °ø°ÝÀ» ¹Þ¾Æ¼­ Á×¾úÀ» °æ¿ì if (damage_arg->option_.is_mirror_ == false) { Attributes& rAtt = pAttackPlayer->GetAttr(); const int AbsorbHP = rAtt.GetAbsorbHP(); const int AbsorbMP = rAtt.GetAbsorbMP(); if ( AbsorbHP ) pAttackPlayer->OnRecover( AbsorbHP, 0 ); // HP Èí¼ö if ( AbsorbMP ) pAttackPlayer->OnRecover( 0, AbsorbMP ); // MP Èí¼ö } } } } VOID NPC::AddBattlePoint( Character* pAttacker, eBATTLE_POINT_TYPE ePointType, int iBattlePoint ) { m_pTargetManager->AddBattlePoint( pAttacker, ePointType, iBattlePoint ); } BOOL NPC::IsSpecialAction( eNPC_SPECIAL_ACTION_TYPE eType ) { // Á¶°Ç °Ë»ç for ( int i = 0; i < 2; ++i ) { SPECIAL_CONDITION *pSpecialCondition = &GetBaseInfo()->m_SpecialCondition[i]; if ( pSpecialCondition->wActionType == eType ) return TRUE; } return FALSE; } SPECIAL_CONDITION* NPC::GetDrawLotsSpecialAction() { // TODO: need compare with the custom logic 'SpecialAction()' for ( int i = 0; i < BASE_NPCINFO::MAX_SPECIAL_NUM; ++i ) { SPECIAL_CONDITION& special_condition = GetBaseInfo()->m_SpecialCondition[i]; if (CheckCondition(special_condition.byCondition, special_condition.wConditionParam)) { if (DrawLots(special_condition.byActionRate)) { return &special_condition; } } } return NULL; } VOID NPC::SpecialAction(const SPECIAL_CONDITION& special_condition) { switch (special_condition.wActionType) { case eNPC_SPECIAL_ACTION_HELPREQUEST: { // °ø°Ý´ë»óÀÌ ¾øÀ¸¸é Áö¿ø¿äû ¹«È¿ if ( !GetTargetChar() ) return; if ( !GetStateManager()->IsRequestHelp() ) { AI_MSG_HELP_REQUEST AIHelpMsg; AIHelpMsg.dwAttackerKey = GetTargetChar()->GetObjectKey(); AIHelpMsg.dwTargetKey = GetObjectKey(); SendAIMessageAroundExceptMe( &AIHelpMsg, sizeof(AIHelpMsg) ); GetStateManager()->SetRequestHelp( TRUE ); } } break; case eNPC_SPECIAL_ACTION_TRANSFORMATION: case eNPC_SPECIAL_ACTION_SELP_DESTRUCTION: { UseSkill( special_condition.wActionParam ); } break; case eNPC_SPECIAL_ACTION_SKILL: { // °ø°Ý´ë»óÀÌ ¾øÀ¸¸é ½ºÅ³±¸»ç ¹«È¿ if ( !GetTargetChar() ) return; SelectSkill( GetTargetChar(), special_condition.wActionParam ); ChangeState( STATE_ID_TRACK ); } break; } } //_NA_0_20100514_NPC_DEAD_STATE_TIME(»èÁ¦) VOID NPC::SpecialAction() { SPECIAL_CONDITION *pSpecialCondition = NULL; // Á¶°Ç °Ë»ç for ( int i = 0; i < 2; ++i ) { pSpecialCondition = &GetBaseInfo()->m_SpecialCondition[i]; if ( CheckCondition( pSpecialCondition->byCondition, pSpecialCondition->wConditionParam ) ) { // È®·ü üũ if ( DrawLots( pSpecialCondition->byActionRate ) ) break; } pSpecialCondition = NULL; } if ( !pSpecialCondition ) return; switch ( pSpecialCondition->wActionType ) { case eNPC_SPECIAL_ACTION_HELPREQUEST: { // °ø°Ý´ë»óÀÌ ¾øÀ¸¸é Áö¿ø¿äû ¹«È¿ if ( !GetTargetChar() ) return; if ( !GetStateManager()->IsRequestHelp() ) { AI_MSG_HELP_REQUEST AIHelpMsg; AIHelpMsg.dwAttackerKey = GetTargetChar()->GetObjectKey(); AIHelpMsg.dwTargetKey = GetObjectKey(); SendAIMessageAroundExceptMe( &AIHelpMsg, sizeof(AIHelpMsg) ); GetStateManager()->SetRequestHelp( TRUE ); } } break; case eNPC_SPECIAL_ACTION_TRANSFORMATION: case eNPC_SPECIAL_ACTION_SELP_DESTRUCTION: { UseSkill( pSpecialCondition->wActionParam ); } break; case eNPC_SPECIAL_ACTION_SKILL: { // °ø°Ý´ë»óÀÌ ¾øÀ¸¸é ½ºÅ³±¸»ç ¹«È¿ if ( !GetTargetChar() ) return; SelectSkill( GetTargetChar(), pSpecialCondition->wActionParam ); ChangeState( STATE_ID_TRACK ); } break; } } VOID NPC::StatusResist() { // (NOTE) if a monster has a resistance about specific states, // the monster enable released status by a release ratio of a probability. const BASE_NPCINFO* const npc_info = GetBaseInfo(); // (CHANGES) (f100628.3L) (WAVERIX) change to a more cheap routine for (int i = 0; i < _countof(npc_info->m_ResistanceCondition); ++i) { const RESISTANCE_CONDITION& resist_condition = npc_info->m_ResistanceCondition[i]; const eCHAR_STATE_TYPE state_id = \ static_cast(resist_condition.m_wStateCode); if (state_id == eCHAR_STATE_INVALID) { continue; }; if (m_pStatusManager->FindStatus(state_id) == false) { continue; }; if (DrawLots(resist_condition.m_byRatio) == false) { continue; }; if (m_pStatusManager->Remove(state_id)) { MSG_CG_BATTLE_MONSTER_STATUS_RESIST_BRD msg_brd; msg_brd.m_dwMonsterObjKey = GetObjectKey(); BOOST_STATIC_ASSERT(sizeof(msg_brd.m_ScriptIndex) == sizeof(BYTE)); msg_brd.m_ScriptIndex = static_cast(i); SendPacketAround(&msg_brd, sizeof(msg_brd)); }; } } //_NA_0_20091118_GAMEROOM_BATTLE_RESULT VOID NPC::OnRecover( INT recoverHP, INT recoverMP, INT recoverSD, eRECOVER_TYPE Type, Character* pAttacker ) { DWORD oldHP = GetHP(); // ½ÇÁ¦ ȸº¹À» ½ÃŲ´Ù. //_NA_0_20091118_GAMEROOM_BATTLE_RESULT Character::OnRecover( recoverHP, recoverMP, recoverSD, Type, pAttacker ); // HP º¯È­ if ( oldHP != GetHP() ) SendHPChange(); } void NPC::SendHPChange() //_NA001385_20090924_DOMINATION ÇÔ¼ö·Î º¯°æ { MSG_CG_STATUS_CHANGE_HP_BRD HPMsg; HPMsg.m_dwObjectKey = GetObjectKey(); HPMsg.m_dwTargetHP = GetHP(); //¿©±â´Â ¸ó½ºÅͳª npc µéÀÇ Á¤º¸´Ù. ¸ó½ºÅ͵éÀÇ ReserveHP´Â Ç×»ó ²Ë Â÷ÀÖ´Ù. HPMsg.m_dwReserveHP = GetMaxHP() - GetHP(); SendPacketAround(&HPMsg, sizeof(HPMsg), FALSE); } void NPC::SendAreaDamage(const DAMAGETYPE damage) { MSG_CG_TRIGGER_AREADAMAGE_BRD msg; msg.m_dwObjectKey = GetObjectKey(); msg.m_wDamage = damage; msg.m_dwHP = GetHP(); #ifdef _NA_008804_20160502_COMBAT_LOG time_t t; util::TimeSync::time(&t); msg.m_SvrTime = t; #endif //(_NA_008804_20160502_COMBAT_LOG) SendPacketAround(&msg, sizeof(msg), FALSE); } float NPC::GetHPRatio() const { const INT maxHP = m_Attr.GetMaxHP(); if ( maxHP <= 0 ) return 0; return (float)m_dwHP / maxHP; } float NPC::GetMPRatio() const { const INT maxMP = m_Attr.GetMaxMP(); if ( maxMP <= 0 ) return 0; return (float)m_dwMP / maxMP; } float NPC::GetSDRatio() const { const INT maxSD = m_Attr.GetMaxSD(); if ( maxSD <= 0 ) return 0; return (float)m_dwSD / maxSD; } VOID NPC::SetHP( DWORD dwHP ) { const DWORD maxHP = (DWORD)m_Attr.GetMaxHP(); m_dwHP = BOUND_CHECK( 0, dwHP, maxHP ); } VOID NPC::SetAttr( eATTR_TYPE eAttrType, eATTR_KIND eAttrKind, int iValue ) { if ( eAttrType <= eATTR_TYPE_INVALID || eAttrType >= eATTR_MAX ) { SUNLOG( eCRITICAL_LOG, "[NPC::SetAttr] eAttrType[%d] is Invalid", eAttrType ); return; } m_Attr[eAttrType][eAttrKind] = iValue; m_Attr.UpdateEx(); } bool NPC::IsLeaderAlive() { if ( !m_pUnitListOfGroup || !m_pUnitGroupInfo ) return false; return m_pUnitListOfGroup->IsAlive(m_pUnitGroupInfo->GetLeaderRegenID()); } bool NPC::IsLeader() { if ( !m_pUnitRegenInfo || !m_pUnitGroupInfo ) return false; return m_pUnitRegenInfo->GetID() == m_pUnitGroupInfo->GetLeaderRegenID(); } // (WAVERIX) (090617) (BUG-FIX) ±×·ì ¸â¹öµéÀº ÀÚ¹ßÀûÀ¸·Î ÈÄÅð ¸øÇÏ´Â »óȲ °¨¼Ò½Ã۱â ÀÛ¾÷ NPC* NPC::GetLeaderInField() { if (m_pUnitRegenInfo == 0 || m_pUnitGroupInfo == 0) return 0; REGENID leaderID = m_pUnitGroupInfo->GetLeaderRegenID(); BOOLEAN isLeader = m_pUnitRegenInfo->GetID() == leaderID; if (isLeader) return this; const UNIT_MEMBER_HASH& rList = m_pUnitListOfGroup->GetUnitMemberHash(); UNIT_MEMBER_HASH::const_iterator it = rList.find(leaderID); if (it != rList.end()) return it->second; return 0; } // NOTE: f110503.3L, logic improvements void NPC::SetHPAttackPowerAsParty(DWORD number_of_party_members) { if (number_of_party_members == 0) { return; }; switch (eOBJECT_TYPE npc_object_type = GetObjectType()) { case MONSTER_OBJECT: case MAPNPC_OBJECT: break; default: return; }; // const Attributes& attributes = m_Attr; //---------------------------------------------------------------------------------------------- // NOTE: increase minimum and maximum attack power ;{ bool is_melee_type = (this->GetWeaponBaseAttType() != eATTACK_TYPE_RANGE); int min_attack_power = is_melee_type ? attributes.GetBaseMeleeMinAttPower() : attributes.GetBaseRangeMinAttPower(); int max_attack_power = is_melee_type ? attributes.GetBaseMeleeMaxAttPower() : attributes.GetBaseRangeMaxAttPower(); ; const int additional_option_attack_power = ns_formula::CalcAttackPowerIncreaseAsParty( number_of_party_members, ((min_attack_power + max_attack_power) >> 1)); if (AttrValue* attr_value = &m_Attr[eATTR_OPTION_PHYSICAL_ATTACK_POWER]) { (*attr_value)[eATTR_KIND_BASE] = additional_option_attack_power; }; }; //---------------------------------------------------------------------------------------------- // NOTE: increase skill attack power, in cases of EP1, increase magical option attack power #ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_ if (const BASE_NPCINFO* npc_info = m_pBaseInfo) { int skill_attack_power = npc_info->skill_power;// GetAttr().GetBaseSkillAttackPower(); ; skill_attack_power = ns_formula::CalcAttackPowerIncreaseAsParty( number_of_party_members, skill_attack_power); if (AttrValue* attr_value = &m_Attr[eATTR_ADD_SKILL_ATTACK_POWER]) { (*attr_value)[eATTR_KIND_BASE] = skill_attack_power; }; }; #else ;{ // ¸¶¹ý ¿É¼Ç °ø°Ý·Â »ó½Â const int min_attack_power = attributes.GetBaseMagicMinAttPower(); const int max_attack_power = attributes.GetBaseMagicMaxAttPower(); const int additional_magic_option_attack_power = ns_formula::CalcAttackPowerIncreaseAsParty( number_of_party_members, ((min_attack_power + max_attack_power) >> 1)); if (AttrValue* attr_value = &m_Attr[eATTR_OPTION_MAGICAL_ATTACK_POWER]) { (*attr_value)[eATTR_KIND_BASE] = additional_magic_option_attack_power; }; }; #endif //---------------------------------------------------------------------------------------------- // NOTE: increase maximum HP quantity DWORD last_update_cur_hp = GetHP(); if (const BASE_NPCINFO* npc_info = m_pBaseInfo) { float increasing_ratio = \ ns_formula::CalcHPIncreaseRatioAsParty(number_of_party_members); DWORD npc_info_max_hp = npc_info->m_dwMaxHP; const DWORD max_hp = util::LL_Round(npc_info_max_hp * increasing_ratio); ; DWORD cur_hp = last_update_cur_hp; if (cur_hp) { DWORD cur_max_hp = GetMaxHP(); if (cur_max_hp) { float hp_relative_ratio = cur_hp / static_cast(cur_max_hp); DWORD calculated_hp = util::LL_Round(hp_relative_ratio * max_hp); cur_hp = max(calculated_hp, 1); }; }; // if (AttrValue* attr_value = &m_Attr[eATTR_MAX_HP]) { (*attr_value)[eATTR_KIND_BASE] = max_hp; }; // last_update_cur_hp = cur_hp; }; //---------------------------------------------------------------------------------------------- // NOTE: the latest changes apply to npc attributes = { last_update_cur_hp, ... } m_Attr.Update(); this->SetHP(last_update_cur_hp); //---------------------------------------------------------------------------------------------- }; ENUM_STATD_ID NPC::GetCurStateID() { // 2014.02.04 / ¹Î°æ½Â / Á¤ÀǵǾîÀÖÁö ¾Ê´ø NPC::GetCurStateID()¸¦ Á¤ÀÇ. // returnÇüÀ» DWORDÇü¿¡¼­ ENUM_STATD_ID·Î ¼öÁ¤. ÇØ´ç ÇÔ¼ö´Â ENUMÀ¸·Î Á¤ÀÇµÈ stateID°ª°úÀÇ ºñ±³¿¡ // ÁÖ·Î »ç¿ëµÇ±â ¶§¹®. return m_pNPCStatusManager->GetCurState()->GetStateID(); }