/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
C++ | 407 lines | 307 code | 63 blank | 37 comment | 88 complexity | 94938161342391cf6d8f4fef4c94c7e9 MD5 | raw file
- /*
- * Copyright (C) 2005-2011 MaNGOS <http://www.getmangos.com/>
- *
- * Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/>
- *
- * Copyright (C) 2010-2011 VoragineCore <http://www.projectvoragine.com/>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include "gamePCH.h"
- /* ScriptData
- SDName: FollowerAI
- SD%Complete: 50
- SDComment: This AI is under development
- SDCategory: Npc
- EndScriptData */
- #include "ScriptPCH.h"
- #include "ScriptedFollowerAI.h"
- #include "Group.h"
- const float MAX_PLAYER_DISTANCE = 100.0f;
- enum ePoints
- {
- POINT_COMBAT_START = 0xFFFFFF
- };
- FollowerAI::FollowerAI(Creature* pCreature) : ScriptedAI(pCreature),
- m_uiLeaderGUID(0),
- m_uiUpdateFollowTimer(2500),
- m_uiFollowState(STATE_FOLLOW_NONE),
- m_pQuestForFollow(NULL)
- {}
- void FollowerAI::AttackStart(Unit* pWho)
- {
- if (!pWho)
- return;
- if (me->Attack(pWho, true))
- {
- me->AddThreat(pWho, 0.0f);
- me->SetInCombatWith(pWho);
- pWho->SetInCombatWith(me);
- if (me->HasUnitState(UNIT_STAT_FOLLOW))
- me->ClearUnitState(UNIT_STAT_FOLLOW);
- if (IsCombatMovement())
- me->GetMotionMaster()->MoveChase(pWho);
- }
- }
- //This part provides assistance to a player that are attacked by pWho, even if out of normal aggro range
- //It will cause me to attack pWho that are attacking _any_ player (which has been confirmed may happen also on offi)
- //The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
- bool FollowerAI::AssistPlayerInCombat(Unit* pWho)
- {
- if (!pWho || !pWho->getVictim())
- return false;
- //experimental (unknown) flag not present
- if (!(me->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
- return false;
- //not a player
- if (!pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
- return false;
- //never attack friendly
- if (me->IsFriendlyTo(pWho))
- return false;
- //too far away and no free sight?
- if (me->IsWithinDistInMap(pWho, MAX_PLAYER_DISTANCE) && me->IsWithinLOSInMap(pWho))
- {
- //already fighting someone?
- if (!me->getVictim())
- {
- AttackStart(pWho);
- return true;
- }
- else
- {
- pWho->SetInCombatWith(me);
- me->AddThreat(pWho, 0.0f);
- return true;
- }
- }
- return false;
- }
- void FollowerAI::MoveInLineOfSight(Unit* pWho)
- {
- if (!me->HasUnitState(UNIT_STAT_STUNNED) && pWho->isTargetableForAttack() && pWho->isInAccessiblePlaceFor(me))
- {
- if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(pWho))
- return;
- if (!me->canFly() && me->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE)
- return;
- if (me->IsHostileTo(pWho))
- {
- float fAttackRadius = me->GetAttackDistance(pWho);
- if (me->IsWithinDistInMap(pWho, fAttackRadius) && me->IsWithinLOSInMap(pWho))
- {
- if (!me->getVictim())
- {
- pWho->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
- AttackStart(pWho);
- }
- else if (me->GetMap()->IsDungeon())
- {
- pWho->SetInCombatWith(me);
- me->AddThreat(pWho, 0.0f);
- }
- }
- }
- }
- }
- void FollowerAI::JustDied(Unit* /*pKiller*/)
- {
- if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || !m_uiLeaderGUID || !m_pQuestForFollow)
- return;
- //TODO: need a better check for quests with time limit.
- if (Player* pPlayer = GetLeaderForFollower())
- {
- if (Group* pGroup = pPlayer->GetGroup())
- {
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
- {
- if (Player* pMember = pRef->getSource())
- {
- if (pMember->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
- pMember->FailQuest(m_pQuestForFollow->GetQuestId());
- }
- }
- }
- else
- {
- if (pPlayer->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
- pPlayer->FailQuest(m_pQuestForFollow->GetQuestId());
- }
- }
- }
- void FollowerAI::JustRespawned()
- {
- m_uiFollowState = STATE_FOLLOW_NONE;
- if (!IsCombatMovement())
- SetCombatMovement(true);
- if (me->getFaction() != me->GetCreatureInfo()->faction_A)
- me->setFaction(me->GetCreatureInfo()->faction_A);
- Reset();
- }
- void FollowerAI::EnterEvadeMode()
- {
- me->RemoveAllAuras();
- me->DeleteThreatList();
- me->CombatStop(true);
- me->SetLootRecipient(NULL);
- if (HasFollowState(STATE_FOLLOW_INPROGRESS))
- {
- sLog->outDebug("TSCR: FollowerAI left combat, returning to CombatStartPosition.");
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
- {
- float fPosX, fPosY, fPosZ;
- me->GetPosition(fPosX, fPosY, fPosZ);
- me->GetMotionMaster()->MovePoint(POINT_COMBAT_START, fPosX, fPosY, fPosZ);
- }
- }
- else
- {
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
- me->GetMotionMaster()->MoveTargetedHome();
- }
- Reset();
- }
- void FollowerAI::UpdateAI(const uint32 uiDiff)
- {
- if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !me->getVictim())
- {
- if (m_uiUpdateFollowTimer <= uiDiff)
- {
- if (HasFollowState(STATE_FOLLOW_COMPLETE) && !HasFollowState(STATE_FOLLOW_POSTEVENT))
- {
- sLog->outDebug("TSCR: FollowerAI is set completed, despawns.");
- me->ForcedDespawn();
- return;
- }
- bool bIsMaxRangeExceeded = true;
- if (Player* pPlayer = GetLeaderForFollower())
- {
- if (HasFollowState(STATE_FOLLOW_RETURNING))
- {
- sLog->outDebug("TSCR: FollowerAI is returning to leader.");
- RemoveFollowState(STATE_FOLLOW_RETURNING);
- me->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
- return;
- }
- if (Group* pGroup = pPlayer->GetGroup())
- {
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
- {
- Player* pMember = pRef->getSource();
- if (pMember && me->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE))
- {
- bIsMaxRangeExceeded = false;
- break;
- }
- }
- }
- else
- {
- if (me->IsWithinDistInMap(pPlayer, MAX_PLAYER_DISTANCE))
- bIsMaxRangeExceeded = false;
- }
- }
- if (bIsMaxRangeExceeded)
- {
- sLog->outDebug("TSCR: FollowerAI failed because player/group was to far away or not found");
- me->ForcedDespawn();
- return;
- }
- m_uiUpdateFollowTimer = 1000;
- }
- else
- m_uiUpdateFollowTimer -= uiDiff;
- }
- UpdateFollowerAI(uiDiff);
- }
- void FollowerAI::UpdateFollowerAI(const uint32 /*uiDiff*/)
- {
- if (!UpdateVictim())
- return;
- DoMeleeAttackIfReady();
- }
- void FollowerAI::MovementInform(uint32 uiMotionType, uint32 uiPointId)
- {
- if (uiMotionType != POINT_MOTION_TYPE || !HasFollowState(STATE_FOLLOW_INPROGRESS))
- return;
- if (uiPointId == POINT_COMBAT_START)
- {
- if (GetLeaderForFollower())
- {
- if (!HasFollowState(STATE_FOLLOW_PAUSED))
- AddFollowState(STATE_FOLLOW_RETURNING);
- }
- else
- me->ForcedDespawn();
- }
- }
- void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const Quest* pQuest)
- {
- if (me->getVictim())
- {
- sLog->outDebug("TSCR: FollowerAI attempt to StartFollow while in combat.");
- return;
- }
- if (HasFollowState(STATE_FOLLOW_INPROGRESS))
- {
- sLog->outError("TSCR: FollowerAI attempt to StartFollow while already following.");
- return;
- }
- //set variables
- m_uiLeaderGUID = pLeader->GetGUID();
- if (uiFactionForFollower)
- me->setFaction(uiFactionForFollower);
- m_pQuestForFollow = pQuest;
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
- {
- me->GetMotionMaster()->Clear();
- me->GetMotionMaster()->MoveIdle();
- sLog->outDebug("TSCR: FollowerAI start with WAYPOINT_MOTION_TYPE, set to MoveIdle.");
- }
- me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
- AddFollowState(STATE_FOLLOW_INPROGRESS);
- me->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
- sLog->outDebug("TSCR: FollowerAI start follow %s (GUID " UI64FMTD ")", pLeader->GetName(), m_uiLeaderGUID);
- }
- Player* FollowerAI::GetLeaderForFollower()
- {
- if (Player* pLeader = Unit::GetPlayer(*me, m_uiLeaderGUID))
- {
- if (pLeader->isAlive())
- return pLeader;
- else
- {
- if (Group* pGroup = pLeader->GetGroup())
- {
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
- {
- Player* pMember = pRef->getSource();
- if (pMember && pMember->isAlive() && me->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE))
- {
- sLog->outDebug("TSCR: FollowerAI GetLeader changed and returned new leader.");
- m_uiLeaderGUID = pMember->GetGUID();
- return pMember;
- break;
- }
- }
- }
- }
- }
- sLog->outDebug("TSCR: FollowerAI GetLeader can not find suitable leader.");
- return NULL;
- }
- void FollowerAI::SetFollowComplete(bool bWithEndEvent)
- {
- if (me->HasUnitState(UNIT_STAT_FOLLOW))
- {
- me->ClearUnitState(UNIT_STAT_FOLLOW);
- me->StopMoving();
- me->GetMotionMaster()->Clear();
- me->GetMotionMaster()->MoveIdle();
- }
- if (bWithEndEvent)
- AddFollowState(STATE_FOLLOW_POSTEVENT);
- else
- {
- if (HasFollowState(STATE_FOLLOW_POSTEVENT))
- RemoveFollowState(STATE_FOLLOW_POSTEVENT);
- }
- AddFollowState(STATE_FOLLOW_COMPLETE);
- }
- void FollowerAI::SetFollowPaused(bool bPaused)
- {
- if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || HasFollowState(STATE_FOLLOW_COMPLETE))
- return;
- if (bPaused)
- {
- AddFollowState(STATE_FOLLOW_PAUSED);
- if (me->HasUnitState(UNIT_STAT_FOLLOW))
- {
- me->ClearUnitState(UNIT_STAT_FOLLOW);
- me->StopMoving();
- me->GetMotionMaster()->Clear();
- me->GetMotionMaster()->MoveIdle();
- }
- }
- else
- {
- RemoveFollowState(STATE_FOLLOW_PAUSED);
- if (Player* pLeader = GetLeaderForFollower())
- me->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
- }
- }