Files
TCPlayerbotCore/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
T

388 lines
11 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
#include "ScriptedCreature.h"
2010-01-19 11:36:05 +01:00
#include "ScriptedFollowerAI.h"
#include "Group.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
2011-08-20 20:31:31 -03:00
FollowerAI::FollowerAI(Creature* creature) : ScriptedAI(creature),
2009-08-16 18:34:45 +02:00
m_uiLeaderGUID(0),
m_uiUpdateFollowTimer(2500),
2010-08-21 20:08:47 +02:00
m_uiFollowState(STATE_FOLLOW_NONE),
m_pQuestForFollow(NULL)
2009-08-16 18:34:45 +02:00
{}
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
void FollowerAI::AttackStart(Unit* who)
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
if (!who)
2009-08-16 18:34:45 +02:00
return;
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
if (me->Attack(who, true))
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
me->AddThreat(who, 0.0f);
me->SetInCombatWith(who);
who->SetInCombatWith(me);
2009-10-17 15:51:44 -07:00
if (me->HasUnitState(UNIT_STATE_FOLLOW))
me->ClearUnitState(UNIT_STATE_FOLLOW);
2009-10-17 15:51:44 -07:00
if (IsCombatMovementAllowed())
2011-08-20 20:31:31 -03:00
me->GetMotionMaster()->MoveChase(who);
2009-08-16 18:34:45 +02:00
}
}
2009-10-17 15:51:44 -07:00
2011-10-07 19:45:43 -05:00
//This part provides assistance to a player that are attacked by who, even if out of normal aggro range
//It will cause me to attack who that are attacking _any_ player (which has been confirmed may happen also on offi)
2009-10-11 18:03:59 +02:00
//The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
bool FollowerAI::AssistPlayerInCombat(Unit* who)
2009-10-11 18:03:59 +02:00
{
if (!who || !who->getVictim())
2009-10-11 18:03:59 +02:00
return false;
2009-10-17 15:51:44 -07:00
2009-10-11 18:03:59 +02:00
//experimental (unknown) flag not present
if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
2009-10-11 18:03:59 +02:00
return false;
2009-10-17 15:51:44 -07:00
2009-10-11 18:03:59 +02:00
//not a player
if (!who->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
2009-10-11 18:03:59 +02:00
return false;
2009-10-17 15:51:44 -07:00
2009-10-11 18:03:59 +02:00
//never attack friendly
if (me->IsFriendlyTo(who))
2009-10-11 18:03:59 +02:00
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 (me->IsWithinDistInMap(who, MAX_PLAYER_DISTANCE) && me->IsWithinLOSInMap(who))
2009-10-11 18:03:59 +02:00
{
//already fighting someone?
2010-04-14 23:07:41 +02:00
if (!me->getVictim())
2009-10-11 18:03:59 +02:00
{
AttackStart(who);
2009-10-11 18:03:59 +02:00
return true;
}
else
{
who->SetInCombatWith(me);
me->AddThreat(who, 0.0f);
2009-10-11 18:03:59 +02:00
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
2011-08-20 20:31:31 -03:00
void FollowerAI::MoveInLineOfSight(Unit* who)
2009-08-16 18:34:45 +02:00
{
if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me))
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(who))
2009-10-11 18:03:59 +02:00
return;
2009-10-17 15:51:44 -07:00
2012-03-12 00:45:58 +01:00
if (!me->CanFly() && me->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
2009-08-16 18:34:45 +02:00
return;
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
if (me->IsHostileTo(who))
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
float fAttackRadius = me->GetAttackDistance(who);
if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who))
2009-08-16 18:34:45 +02:00
{
2010-04-14 23:07:41 +02:00
if (!me->getVictim())
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
who->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
AttackStart(who);
2009-08-16 18:34:45 +02:00
}
2010-04-14 23:07:41 +02:00
else if (me->GetMap()->IsDungeon())
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
who->SetInCombatWith(me);
me->AddThreat(who, 0.0f);
2009-08-16 18:34:45 +02:00
}
}
}
}
}
2009-10-17 15:51:44 -07:00
2010-04-19 09:26:37 +02:00
void FollowerAI::JustDied(Unit* /*pKiller*/)
2009-08-16 18:34:45 +02:00
{
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.
2011-08-20 20:31:31 -03:00
if (Player* player = GetLeaderForFollower())
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
if (Group* group = player->GetGroup())
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
if (Player* member = groupRef->getSource())
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
if (member->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
member->FailQuest(m_pQuestForFollow->GetQuestId());
2009-08-16 18:34:45 +02:00
}
}
}
else
{
2011-08-20 20:31:31 -03:00
if (player->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
player->FailQuest(m_pQuestForFollow->GetQuestId());
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::JustRespawned()
{
2009-08-20 18:50:41 +02:00
m_uiFollowState = STATE_FOLLOW_NONE;
2009-10-17 15:51:44 -07:00
if (!IsCombatMovementAllowed())
2009-08-16 18:34:45 +02:00
SetCombatMovement(true);
2009-10-17 15:51:44 -07:00
if (me->getFaction() != me->GetCreatureTemplate()->faction_A)
me->setFaction(me->GetCreatureTemplate()->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()
{
2010-04-14 23:07:41 +02:00
me->RemoveAllAuras();
me->DeleteThreatList();
me->CombatStop(true);
me->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
{
sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI left combat, returning to CombatStartPosition.");
2009-10-17 15:51:44 -07:00
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
2009-08-16 18:34:45 +02:00
{
float fPosX, fPosY, fPosZ;
2010-04-14 23:07:41 +02:00
me->GetPosition(fPosX, fPosY, fPosZ);
me->GetMotionMaster()->MovePoint(POINT_COMBAT_START, fPosX, fPosY, fPosZ);
2009-08-16 18:34:45 +02:00
}
}
else
{
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
2010-04-14 23:07:41 +02:00
me->GetMotionMaster()->MoveTargetedHome();
2009-08-16 18:34:45 +02:00
}
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)
{
2010-04-14 23:07:41 +02:00
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !me->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
{
sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI is set completed, despawns.");
me->DespawnOrUnsummon();
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
bool bIsMaxRangeExceeded = true;
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
if (Player* player = GetLeaderForFollower())
2009-08-16 18:34:45 +02:00
{
2009-08-20 18:50:41 +02:00
if (HasFollowState(STATE_FOLLOW_RETURNING))
2009-08-16 18:34:45 +02:00
{
sLog->outDebug(LOG_FILTER_TSCR, "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);
2011-08-20 20:31:31 -03:00
me->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
2009-08-16 18:34:45 +02:00
return;
}
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
if (Group* group = player->GetGroup())
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
Player* member = groupRef->getSource();
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
if (member && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE))
2009-08-16 18:34:45 +02:00
{
bIsMaxRangeExceeded = false;
break;
}
}
}
else
{
2011-08-20 20:31:31 -03:00
if (me->IsWithinDistInMap(player, MAX_PLAYER_DISTANCE))
2009-08-16 18:34:45 +02:00
bIsMaxRangeExceeded = false;
}
}
2009-10-17 15:51:44 -07:00
2009-08-16 18:34:45 +02:00
if (bIsMaxRangeExceeded)
{
sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI failed because player/group was to far away or not found");
me->DespawnOrUnsummon();
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
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
2010-04-19 09:26:37 +02:00
void FollowerAI::UpdateFollowerAI(const uint32 /*uiDiff*/)
2009-08-16 18:34:45 +02:00
{
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
2011-08-20 20:31:31 -03:00
void FollowerAI::MovementInform(uint32 motionType, uint32 pointId)
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
if (motionType != POINT_MOTION_TYPE || !HasFollowState(STATE_FOLLOW_INPROGRESS))
2009-08-16 18:34:45 +02:00
return;
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
if (pointId == POINT_COMBAT_START)
2009-08-16 18:34:45 +02:00
{
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
me->DespawnOrUnsummon();
2009-08-16 18:34:45 +02:00
}
}
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
void FollowerAI::StartFollow(Player* player, uint32 factionForFollower, const Quest* quest)
2009-08-16 18:34:45 +02:00
{
2010-04-14 23:07:41 +02:00
if (me->getVictim())
2009-08-16 18:34:45 +02:00
{
sLog->outDebug(LOG_FILTER_TSCR, "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
{
sLog->outError("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
2011-08-20 20:31:31 -03:00
m_uiLeaderGUID = player->GetGUID();
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
if (factionForFollower)
me->setFaction(factionForFollower);
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
m_pQuestForFollow = quest;
2009-10-17 15:51:44 -07:00
2010-04-14 23:07:41 +02:00
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
2009-08-16 18:34:45 +02:00
{
2010-04-14 23:07:41 +02:00
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
sLog->outDebug(LOG_FILTER_TSCR, "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
2010-04-14 23:07:41 +02:00
me->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
2011-08-20 20:31:31 -03:00
me->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI start follow %s (GUID " UI64FMTD ")", player->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()
{
2011-08-20 20:31:31 -03:00
if (Player* player = Unit::GetPlayer(*me, m_uiLeaderGUID))
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
if (player->isAlive())
return player;
2009-08-16 18:34:45 +02:00
else
{
2011-08-20 20:31:31 -03:00
if (Group* group = player->GetGroup())
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
2009-08-16 18:34:45 +02:00
{
2011-08-20 20:31:31 -03:00
Player* member = groupRef->getSource();
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
if (member && member->isAlive() && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE))
2009-08-16 18:34:45 +02:00
{
sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI GetLeader changed and returned new leader.");
2011-08-20 20:31:31 -03:00
m_uiLeaderGUID = member->GetGUID();
return member;
2009-08-16 18:34:45 +02:00
}
}
}
}
}
2009-10-17 15:51:44 -07:00
sLog->outDebug(LOG_FILTER_TSCR, "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 (me->HasUnitState(UNIT_STATE_FOLLOW))
2009-08-20 02:24:53 +02:00
{
me->ClearUnitState(UNIT_STATE_FOLLOW);
2009-10-17 15:51:44 -07:00
2010-04-14 23:07:41 +02:00
me->StopMoving();
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
2009-08-20 02:24:53 +02:00
}
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
2011-08-20 20:31:31 -03:00
void FollowerAI::SetFollowPaused(bool paused)
2009-08-20 18:50:41 +02:00
{
if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || HasFollowState(STATE_FOLLOW_COMPLETE))
return;
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
if (paused)
2009-08-20 18:50:41 +02:00
{
AddFollowState(STATE_FOLLOW_PAUSED);
2009-10-17 15:51:44 -07:00
if (me->HasUnitState(UNIT_STATE_FOLLOW))
2009-08-20 18:50:41 +02:00
{
me->ClearUnitState(UNIT_STATE_FOLLOW);
2009-10-17 15:51:44 -07:00
2010-04-14 23:07:41 +02:00
me->StopMoving();
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
2009-08-20 18:50:41 +02:00
}
}
else
{
RemoveFollowState(STATE_FOLLOW_PAUSED);
2009-10-17 15:51:44 -07:00
2011-08-20 20:31:31 -03:00
if (Player* leader = GetLeaderForFollower())
me->GetMotionMaster()->MoveFollow(leader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
2009-08-20 18:50:41 +02:00
}
2009-08-20 02:24:53 +02:00
}