summaryrefslogtreecommitdiff
path: root/src/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
diff options
context:
space:
mode:
authorYehonal <yehonal.azeroth@gmail.com>2016-08-12 00:46:43 +0200
committerYehonal <yehonal.azeroth@gmail.com>2016-08-12 02:38:26 +0200
commitb0c8eceb08b9a7688893991e5ba4a3350617e6ed (patch)
tree39a52f0bea8e1ed803dc4298fdeb8b6bd808b67d /src/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
parenta73ad5cd6eefd619e9371d9b26c7e6317cacd7f7 (diff)
Refactoring part 2 [W.I.P]
Diffstat (limited to 'src/game/AI/ScriptedAI/ScriptedFollowerAI.cpp')
-rw-r--r--src/game/AI/ScriptedAI/ScriptedFollowerAI.cpp362
1 files changed, 362 insertions, 0 deletions
diff --git a/src/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
new file mode 100644
index 0000000000..4490e3dc0a
--- /dev/null
+++ b/src/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
@@ -0,0 +1,362 @@
+/* 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 */
+
+/* ScriptData
+SDName: FollowerAI
+SD%Complete: 50
+SDComment: This AI is under development
+SDCategory: Npc
+EndScriptData */
+
+#include "ScriptedCreature.h"
+#include "ScriptedFollowerAI.h"
+#include "Group.h"
+#include "Player.h"
+
+const float MAX_PLAYER_DISTANCE = 100.0f;
+
+enum ePoints
+{
+ POINT_COMBAT_START = 0xFFFFFF
+};
+
+FollowerAI::FollowerAI(Creature* creature) : ScriptedAI(creature),
+ m_uiLeaderGUID(0),
+ m_uiUpdateFollowTimer(2500),
+ m_uiFollowState(STATE_FOLLOW_NONE),
+ m_pQuestForFollow(NULL)
+{}
+
+void FollowerAI::AttackStart(Unit* who)
+{
+ if (!who)
+ return;
+
+ if (me->Attack(who, true))
+ {
+ // This is done in Unit::Attack function which wont bug npcs by not adding threat upon combat start...
+ //me->AddThreat(who, 0.0f);
+ //me->SetInCombatWith(who);
+ //who->SetInCombatWith(me);
+
+ if (me->HasUnitState(UNIT_STATE_FOLLOW))
+ me->ClearUnitState(UNIT_STATE_FOLLOW);
+
+ if (IsCombatMovementAllowed())
+ me->GetMotionMaster()->MoveChase(who);
+ }
+}
+
+//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)
+//The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
+bool FollowerAI::AssistPlayerInCombat(Unit* who)
+{
+ if (!who || !who->GetVictim())
+ return false;
+
+ //experimental (unknown) flag not present
+ if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
+ return false;
+
+ //not a player
+ if (!who->GetVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
+ return false;
+
+ //never attack friendly
+ if (me->IsFriendlyTo(who))
+ return false;
+
+ //too far away and no free sight?
+ if (me->IsWithinDistInMap(who, MAX_PLAYER_DISTANCE) && me->IsWithinLOSInMap(who))
+ {
+ AttackStart(who);
+ return true;
+ }
+
+ return false;
+}
+
+void FollowerAI::MoveInLineOfSight(Unit* who)
+{
+ if (me->GetVictim())
+ return;
+
+ if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack(true, me) && who->isInAccessiblePlaceFor(me))
+ if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(who))
+ return;
+
+ if (me->CanStartAttack(who))
+ AttackStart(who);
+}
+
+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* player = GetLeaderForFollower())
+ {
+ if (Group* group = player->GetGroup())
+ {
+ for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
+ {
+ if (Player* member = groupRef->GetSource())
+ {
+ if (member->IsInMap(player) && member->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
+ member->FailQuest(m_pQuestForFollow->GetQuestId());
+ }
+ }
+ }
+ else
+ {
+ if (player->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
+ player->FailQuest(m_pQuestForFollow->GetQuestId());
+ }
+ }
+}
+
+void FollowerAI::JustRespawned()
+{
+ m_uiFollowState = STATE_FOLLOW_NONE;
+
+ if (!IsCombatMovementAllowed())
+ SetCombatMovement(true);
+
+ if (me->getFaction() != me->GetCreatureTemplate()->faction)
+ me->setFaction(me->GetCreatureTemplate()->faction);
+
+ Reset();
+}
+
+void FollowerAI::EnterEvadeMode()
+{
+ me->RemoveAllAuras();
+ me->DeleteThreatList();
+ me->CombatStop(true);
+ me->SetLootRecipient(NULL);
+
+ if (HasFollowState(STATE_FOLLOW_INPROGRESS))
+ {
+ ;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI left combat, returning to CombatStartPosition.");
+
+ if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_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() == CHASE_MOTION_TYPE)
+ me->GetMotionMaster()->MoveTargetedHome();
+ }
+
+ Reset();
+}
+
+void FollowerAI::UpdateAI(uint32 uiDiff)
+{
+ if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !me->GetVictim())
+ {
+ if (m_uiUpdateFollowTimer <= uiDiff)
+ {
+ if (HasFollowState(STATE_FOLLOW_COMPLETE) && !HasFollowState(STATE_FOLLOW_POSTEVENT))
+ {
+ ;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI is set completed, despawns.");
+ me->DespawnOrUnsummon();
+ return;
+ }
+
+ bool bIsMaxRangeExceeded = true;
+
+ if (Player* player = GetLeaderForFollower())
+ {
+ if (HasFollowState(STATE_FOLLOW_RETURNING))
+ {
+ ;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI is returning to leader.");
+
+ RemoveFollowState(STATE_FOLLOW_RETURNING);
+ me->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
+ return;
+ }
+
+ if (Group* group = player->GetGroup())
+ {
+ for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
+ {
+ Player* member = groupRef->GetSource();
+
+ if (member && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE))
+ {
+ bIsMaxRangeExceeded = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (me->IsWithinDistInMap(player, MAX_PLAYER_DISTANCE))
+ bIsMaxRangeExceeded = false;
+ }
+ }
+
+ if (bIsMaxRangeExceeded)
+ {
+ ;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI failed because player/group was to far away or not found");
+ me->DespawnOrUnsummon();
+ return;
+ }
+
+ m_uiUpdateFollowTimer = 1000;
+ }
+ else
+ m_uiUpdateFollowTimer -= uiDiff;
+ }
+
+ UpdateFollowerAI(uiDiff);
+}
+
+void FollowerAI::UpdateFollowerAI(uint32 /*uiDiff*/)
+{
+ if (!UpdateVictim())
+ return;
+
+ DoMeleeAttackIfReady();
+}
+
+void FollowerAI::MovementInform(uint32 motionType, uint32 pointId)
+{
+ if (motionType != POINT_MOTION_TYPE || !HasFollowState(STATE_FOLLOW_INPROGRESS))
+ return;
+
+ if (pointId == POINT_COMBAT_START)
+ {
+ if (GetLeaderForFollower())
+ {
+ if (!HasFollowState(STATE_FOLLOW_PAUSED))
+ AddFollowState(STATE_FOLLOW_RETURNING);
+ }
+ else
+ me->DespawnOrUnsummon();
+ }
+}
+
+void FollowerAI::StartFollow(Player* player, uint32 factionForFollower, const Quest* quest)
+{
+ if (me->GetVictim())
+ {
+ ;//sLog->outDebug(LOG_FILTER_TSCR, "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 = player->GetGUID();
+
+ if (factionForFollower)
+ me->setFaction(factionForFollower);
+
+ m_pQuestForFollow = quest;
+
+ if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
+ {
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MoveIdle();
+ ;//sLog->outDebug(LOG_FILTER_TSCR, "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(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
+
+ ;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI start follow %s (GUID " UI64FMTD ")", player->GetName().c_str(), m_uiLeaderGUID);
+}
+
+Player* FollowerAI::GetLeaderForFollower()
+{
+ if (Player* player = ObjectAccessor::GetPlayer(*me, m_uiLeaderGUID))
+ {
+ if (player->IsAlive())
+ return player;
+ else
+ {
+ if (Group* group = player->GetGroup())
+ {
+ for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
+ {
+ Player* member = groupRef->GetSource();
+
+ if (member && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE) && member->IsAlive())
+ {
+ ;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI GetLeader changed and returned new leader.");
+ m_uiLeaderGUID = member->GetGUID();
+ return member;
+ }
+ }
+ }
+ }
+ }
+
+ ;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI GetLeader can not find suitable leader.");
+ return NULL;
+}
+
+void FollowerAI::SetFollowComplete(bool bWithEndEvent)
+{
+ if (me->HasUnitState(UNIT_STATE_FOLLOW))
+ {
+ me->ClearUnitState(UNIT_STATE_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 paused)
+{
+ if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || HasFollowState(STATE_FOLLOW_COMPLETE))
+ return;
+
+ if (paused)
+ {
+ AddFollowState(STATE_FOLLOW_PAUSED);
+
+ if (me->HasUnitState(UNIT_STATE_FOLLOW))
+ {
+ me->ClearUnitState(UNIT_STATE_FOLLOW);
+
+ me->StopMoving();
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MoveIdle();
+ }
+ }
+ else
+ {
+ RemoveFollowState(STATE_FOLLOW_PAUSED);
+
+ if (Player* leader = GetLeaderForFollower())
+ me->GetMotionMaster()->MoveFollow(leader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
+ }
+}