Files
TCPlayerbotCore/src/bindings/scripts/base/follower_ai.cpp
T

388 lines
12 KiB
C++
Raw Normal View History

2009-08-16 18:34:45 +02:00
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
/* ScriptData
SDName: FollowerAI
SD%Complete: 50
SDComment: This AI is under development
SDCategory: Npc
EndScriptData */
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
#include "precompiled.h"
#include "follower_ai.h"
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
const float MAX_PLAYER_DISTANCE = 100.0f;
2009-10-17 15:51:44 -07:00
2009-10-17 07:42:48 -07:00
enum ePoints
2009-08-16 18:34:45 +02:00
{
POINT_COMBAT_START = 0xFFFFFF
};
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
FollowerAI::FollowerAI(Creature* pCreature) : ScriptedAI(pCreature),
m_uiLeaderGUID(0),
m_pQuestForFollow(NULL),
m_uiUpdateFollowTimer(2500),
2009-08-20 18:50:41 +02:00
m_uiFollowState(STATE_FOLLOW_NONE)
2009-08-16 18:34:45 +02:00
{}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
void FollowerAI::AttackStart(Unit* pWho)
{
if (!pWho)
return;
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (m_creature->Attack(pWho, true))
{
m_creature->AddThreat(pWho, 0.0f);
m_creature->SetInCombatWith(pWho);
pWho->SetInCombatWith(m_creature);
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (m_creature->hasUnitState(UNIT_STAT_FOLLOW))
m_creature->clearUnitState(UNIT_STAT_FOLLOW);
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (IsCombatMovement())
m_creature->GetMotionMaster()->MoveChase(pWho);
}
}
2009-10-17 15:51:44 -07:00
2009-10-11 18:03:59 +02:00
//This part provides assistance to a player that are attacked by pWho, even if out of normal aggro range
//It will cause m_creature 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;
2009-10-17 15:51:44 -07:00
2009-10-11 18:03:59 +02:00
//experimental (unknown) flag not present
if (!(m_creature->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_UNK13))
return false;
2009-10-17 15:51:44 -07:00
2009-10-11 18:03:59 +02:00
//not a player
if (!pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
return false;
2009-10-17 15:51:44 -07:00
2009-10-11 18:03:59 +02:00
//never attack friendly
if (m_creature->IsFriendlyTo(pWho))
return false;
2009-10-17 15:51:44 -07:00
2009-10-11 18:03:59 +02:00
//too far away and no free sight?
if (m_creature->IsWithinDistInMap(pWho, MAX_PLAYER_DISTANCE) && m_creature->IsWithinLOSInMap(pWho))
{
//already fighting someone?
if (!m_creature->getVictim())
{
AttackStart(pWho);
return true;
}
else
{
pWho->SetInCombatWith(m_creature);
m_creature->AddThreat(pWho, 0.0f);
return true;
}
}
2009-10-17 15:51:44 -07:00
2009-10-11 18:03:59 +02:00
return false;
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
void FollowerAI::MoveInLineOfSight(Unit* pWho)
{
2009-10-11 18:03:59 +02:00
if (!m_creature->hasUnitState(UNIT_STAT_STUNNED) && pWho->isTargetableForAttack() && pWho->isInAccessiblePlaceFor(m_creature))
2009-08-16 18:34:45 +02:00
{
2009-10-11 18:03:59 +02:00
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(pWho))
return;
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (!m_creature->canFly() && m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE)
return;
2009-10-17 15:51:44 -07:00
2009-10-11 18:03:59 +02:00
if (m_creature->IsHostileTo(pWho))
2009-08-16 18:34:45 +02:00
{
2009-10-11 18:03:59 +02:00
float fAttackRadius = m_creature->GetAttackDistance(pWho);
if (m_creature->IsWithinDistInMap(pWho, fAttackRadius) && m_creature->IsWithinLOSInMap(pWho))
2009-08-16 18:34:45 +02:00
{
if (!m_creature->getVictim())
{
pWho->RemoveAurasDueToSpell(SPELL_AURA_MOD_STEALTH);
AttackStart(pWho);
}
else if (m_creature->GetMap()->IsDungeon())
{
pWho->SetInCombatWith(m_creature);
m_creature->AddThreat(pWho, 0.0f);
}
}
}
}
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
void FollowerAI::JustDied(Unit* pKiller)
{
2009-08-20 18:50:41 +02:00
if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || !m_uiLeaderGUID || !m_pQuestForFollow)
2009-08-16 18:34:45 +02:00
return;
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
//TODO: need a better check for quests with time limit.
if (Player* pPlayer = GetLeaderForFollower())
{
if (Group* pGroup = pPlayer->GetGroup())
{
2009-10-17 16:20:24 -07:00
for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
2009-08-16 18:34:45 +02:00
{
if (Player* pMember = pRef->getSource())
{
2010-01-14 03:55:18 -03:00
if (pMember->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
pMember->FailQuest(m_pQuestForFollow->GetQuestId());
2009-08-16 18:34:45 +02:00
}
}
}
else
{
if (pPlayer->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
pPlayer->FailQuest(m_pQuestForFollow->GetQuestId());
}
}
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
void FollowerAI::JustRespawned()
{
2009-08-20 18:50:41 +02:00
m_uiFollowState = STATE_FOLLOW_NONE;
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (!IsCombatMovement())
SetCombatMovement(true);
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (m_creature->getFaction() != m_creature->GetCreatureInfo()->faction_A)
m_creature->setFaction(m_creature->GetCreatureInfo()->faction_A);
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
Reset();
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
void FollowerAI::EnterEvadeMode()
{
m_creature->RemoveAllAuras();
m_creature->DeleteThreatList();
m_creature->CombatStop(true);
m_creature->SetLootRecipient(NULL);
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
if (HasFollowState(STATE_FOLLOW_INPROGRESS))
2009-08-16 18:34:45 +02:00
{
2009-08-20 02:24:53 +02:00
debug_log("TSCR: FollowerAI left combat, returning to CombatStartPosition.");
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
{
float fPosX, fPosY, fPosZ;
m_creature->GetPosition(fPosX, fPosY, fPosZ);
m_creature->GetMotionMaster()->MovePoint(POINT_COMBAT_START, fPosX, fPosY, fPosZ);
}
}
else
{
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
m_creature->GetMotionMaster()->MoveTargetedHome();
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
Reset();
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
void FollowerAI::UpdateAI(const uint32 uiDiff)
{
2009-08-20 18:50:41 +02:00
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !m_creature->getVictim())
2009-08-16 18:34:45 +02:00
{
2009-10-27 17:07:40 -07:00
if (m_uiUpdateFollowTimer <= uiDiff)
2009-08-16 18:34:45 +02:00
{
2009-08-20 18:50:41 +02:00
if (HasFollowState(STATE_FOLLOW_COMPLETE) && !HasFollowState(STATE_FOLLOW_POSTEVENT))
2009-08-16 18:34:45 +02:00
{
2009-08-20 02:24:53 +02:00
debug_log("TSCR: FollowerAI is set completed, despawns.");
2009-08-16 18:34:45 +02:00
m_creature->ForcedDespawn();
return;
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
bool bIsMaxRangeExceeded = true;
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (Player* pPlayer = GetLeaderForFollower())
{
2009-08-20 18:50:41 +02:00
if (HasFollowState(STATE_FOLLOW_RETURNING))
2009-08-16 18:34:45 +02:00
{
2009-08-20 02:24:53 +02:00
debug_log("TSCR: FollowerAI is returning to leader.");
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
RemoveFollowState(STATE_FOLLOW_RETURNING);
2009-08-16 18:34:45 +02:00
m_creature->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
return;
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (Group* pGroup = pPlayer->GetGroup())
{
2009-10-17 16:20:24 -07:00
for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
2009-08-16 18:34:45 +02:00
{
Player* pMember = pRef->getSource();
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (pMember && m_creature->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE))
{
bIsMaxRangeExceeded = false;
break;
}
}
}
else
{
if (m_creature->IsWithinDistInMap(pPlayer, MAX_PLAYER_DISTANCE))
bIsMaxRangeExceeded = false;
}
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (bIsMaxRangeExceeded)
{
2009-08-20 02:24:53 +02:00
debug_log("TSCR: FollowerAI failed because player/group was to far away or not found");
2009-08-16 18:34:45 +02:00
m_creature->ForcedDespawn();
return;
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
m_uiUpdateFollowTimer = 1000;
}
else
m_uiUpdateFollowTimer -= uiDiff;
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
UpdateFollowerAI(uiDiff);
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
void FollowerAI::UpdateFollowerAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
DoMeleeAttackIfReady();
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
void FollowerAI::MovementInform(uint32 uiMotionType, uint32 uiPointId)
{
2009-08-20 18:50:41 +02:00
if (uiMotionType != POINT_MOTION_TYPE || !HasFollowState(STATE_FOLLOW_INPROGRESS))
2009-08-16 18:34:45 +02:00
return;
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (uiPointId == POINT_COMBAT_START)
{
if (GetLeaderForFollower())
2009-08-20 18:50:41 +02:00
{
if (!HasFollowState(STATE_FOLLOW_PAUSED))
AddFollowState(STATE_FOLLOW_RETURNING);
}
2009-08-16 18:34:45 +02:00
else
m_creature->ForcedDespawn();
}
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const Quest* pQuest)
{
if (m_creature->getVictim())
{
2009-08-20 02:24:53 +02:00
debug_log("TSCR: FollowerAI attempt to StartFollow while in combat.");
2009-08-16 18:34:45 +02:00
return;
}
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
if (HasFollowState(STATE_FOLLOW_INPROGRESS))
2009-08-16 18:34:45 +02:00
{
2009-08-20 02:24:53 +02:00
error_log("TSCR: FollowerAI attempt to StartFollow while already following.");
2009-08-16 18:34:45 +02:00
return;
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
//set variables
m_uiLeaderGUID = pLeader->GetGUID();
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (uiFactionForFollower)
m_creature->setFaction(uiFactionForFollower);
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
m_pQuestForFollow = pQuest;
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
{
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveIdle();
2009-08-20 02:24:53 +02:00
debug_log("TSCR: FollowerAI start with WAYPOINT_MOTION_TYPE, set to MoveIdle.");
2009-08-16 18:34:45 +02:00
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
AddFollowState(STATE_FOLLOW_INPROGRESS);
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
2009-10-17 15:51:44 -07:00
2009-08-20 02:24:53 +02:00
debug_log("TSCR: FollowerAI start follow %s (GUID %u)", pLeader->GetName(), m_uiLeaderGUID);
2009-08-16 18:34:45 +02:00
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
Player* FollowerAI::GetLeaderForFollower()
{
if (Player* pLeader = Unit::GetPlayer(m_uiLeaderGUID))
2009-08-16 18:34:45 +02:00
{
if (pLeader->isAlive())
return pLeader;
else
{
if (Group* pGroup = pLeader->GetGroup())
{
2009-10-17 16:20:24 -07:00
for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
2009-08-16 18:34:45 +02:00
{
Player* pMember = pRef->getSource();
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (pMember && pMember->isAlive() && m_creature->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE))
{
2009-08-20 02:24:53 +02:00
debug_log("TSCR: FollowerAI GetLeader changed and returned new leader.");
2009-08-16 18:34:45 +02:00
m_uiLeaderGUID = pMember->GetGUID();
return pMember;
break;
}
}
}
}
}
2009-10-17 15:51:44 -07:00
2009-08-20 02:24:53 +02:00
debug_log("TSCR: FollowerAI GetLeader can not find suitable leader.");
2009-08-16 18:34:45 +02:00
return NULL;
}
2009-10-17 15:51:44 -07:00
2009-08-20 02:24:53 +02:00
void FollowerAI::SetFollowComplete(bool bWithEndEvent)
{
if (m_creature->hasUnitState(UNIT_STAT_FOLLOW))
{
m_creature->clearUnitState(UNIT_STAT_FOLLOW);
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
m_creature->StopMoving();
m_creature->GetMotionMaster()->Clear();
2009-08-20 02:24:53 +02:00
m_creature->GetMotionMaster()->MoveIdle();
}
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
if (bWithEndEvent)
AddFollowState(STATE_FOLLOW_POSTEVENT);
else
{
if (HasFollowState(STATE_FOLLOW_POSTEVENT))
RemoveFollowState(STATE_FOLLOW_POSTEVENT);
}
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
AddFollowState(STATE_FOLLOW_COMPLETE);
}
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
void FollowerAI::SetFollowPaused(bool bPaused)
{
if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || HasFollowState(STATE_FOLLOW_COMPLETE))
return;
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
if (bPaused)
{
AddFollowState(STATE_FOLLOW_PAUSED);
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
if (m_creature->hasUnitState(UNIT_STAT_FOLLOW))
{
m_creature->clearUnitState(UNIT_STAT_FOLLOW);
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
m_creature->StopMoving();
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveIdle();
}
}
else
{
RemoveFollowState(STATE_FOLLOW_PAUSED);
2009-10-17 15:51:44 -07:00
2009-08-20 18:50:41 +02:00
if (Player* pLeader = GetLeaderForFollower())
m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
}
2009-08-20 02:24:53 +02:00
}