aboutsummaryrefslogtreecommitdiff
path: root/src/game/ScriptedFollowerAI.cpp
diff options
context:
space:
mode:
authorRat <none@none>2010-01-19 11:36:05 +0100
committerRat <none@none>2010-01-19 11:36:05 +0100
commit0cc053ea4d42ce405a915857f75ee00f0f65666b (patch)
tree7c25955ee5db618deee963f515ba061fbb1e1e8c /src/game/ScriptedFollowerAI.cpp
parentf5dea61b66a616110cfc82ff640ec448b1efa702 (diff)
*Integrate Script system to Core
-added ScriptMgr for loading scripts -removed bindings -moved script system to src/game -moved scripts to src/scripts -VC project files updated -cmakes updated (not 100% done yet) NOTE to Devs: -file locations changed -precompiled renamed to ScriptedPch -ecsort_ai renamed to ScriptedEscortAI -follower_ai renamed to ScriptedFollowerAI -guard_ai renamed to ScriptedGuardAI -simple_ai renamed to ScriptedSimpleAI -sc_creature renamed to ScriptedCreature -sc_gossip renamed to ScriptedGossip -sc_instance renamed to ScriptedInstance *use the new headers in scripts, thank you NOTE to ALL: cmake not fully tested, please report any errors with it could make creashes, incompability USE AT YOUR OWN RISK before further tests!! --HG-- branch : trunk
Diffstat (limited to 'src/game/ScriptedFollowerAI.cpp')
-rw-r--r--src/game/ScriptedFollowerAI.cpp387
1 files changed, 387 insertions, 0 deletions
diff --git a/src/game/ScriptedFollowerAI.cpp b/src/game/ScriptedFollowerAI.cpp
new file mode 100644
index 00000000000..b47f534556f
--- /dev/null
+++ b/src/game/ScriptedFollowerAI.cpp
@@ -0,0 +1,387 @@
+/* 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 "ScriptedPch.h"
+#include "ScriptedFollowerAI.h"
+
+const float MAX_PLAYER_DISTANCE = 100.0f;
+
+enum ePoints
+{
+ POINT_COMBAT_START = 0xFFFFFF
+};
+
+FollowerAI::FollowerAI(Creature* pCreature) : ScriptedAI(pCreature),
+ m_uiLeaderGUID(0),
+ m_pQuestForFollow(NULL),
+ m_uiUpdateFollowTimer(2500),
+ m_uiFollowState(STATE_FOLLOW_NONE)
+{}
+
+void FollowerAI::AttackStart(Unit* pWho)
+{
+ if (!pWho)
+ return;
+
+ if (m_creature->Attack(pWho, true))
+ {
+ m_creature->AddThreat(pWho, 0.0f);
+ m_creature->SetInCombatWith(pWho);
+ pWho->SetInCombatWith(m_creature);
+
+ if (m_creature->hasUnitState(UNIT_STAT_FOLLOW))
+ m_creature->clearUnitState(UNIT_STAT_FOLLOW);
+
+ if (IsCombatMovement())
+ m_creature->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 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;
+
+ //experimental (unknown) flag not present
+ if (!(m_creature->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_UNK13))
+ return false;
+
+ //not a player
+ if (!pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
+ return false;
+
+ //never attack friendly
+ if (m_creature->IsFriendlyTo(pWho))
+ return false;
+
+ //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;
+ }
+ }
+
+ return false;
+}
+
+void FollowerAI::MoveInLineOfSight(Unit* pWho)
+{
+ if (!m_creature->hasUnitState(UNIT_STAT_STUNNED) && pWho->isTargetableForAttack() && pWho->isInAccessiblePlaceFor(m_creature))
+ {
+ if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(pWho))
+ return;
+
+ if (!m_creature->canFly() && m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE)
+ return;
+
+ if (m_creature->IsHostileTo(pWho))
+ {
+ float fAttackRadius = m_creature->GetAttackDistance(pWho);
+ if (m_creature->IsWithinDistInMap(pWho, fAttackRadius) && m_creature->IsWithinLOSInMap(pWho))
+ {
+ 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);
+ }
+ }
+ }
+ }
+}
+
+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 (m_creature->getFaction() != m_creature->GetCreatureInfo()->faction_A)
+ m_creature->setFaction(m_creature->GetCreatureInfo()->faction_A);
+
+ Reset();
+}
+
+void FollowerAI::EnterEvadeMode()
+{
+ m_creature->RemoveAllAuras();
+ m_creature->DeleteThreatList();
+ m_creature->CombatStop(true);
+ m_creature->SetLootRecipient(NULL);
+
+ if (HasFollowState(STATE_FOLLOW_INPROGRESS))
+ {
+ debug_log("TSCR: FollowerAI left combat, returning to CombatStartPosition.");
+
+ 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();
+ }
+
+ Reset();
+}
+
+void FollowerAI::UpdateAI(const uint32 uiDiff)
+{
+ if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !m_creature->getVictim())
+ {
+ if (m_uiUpdateFollowTimer <= uiDiff)
+ {
+ if (HasFollowState(STATE_FOLLOW_COMPLETE) && !HasFollowState(STATE_FOLLOW_POSTEVENT))
+ {
+ debug_log("TSCR: FollowerAI is set completed, despawns.");
+ m_creature->ForcedDespawn();
+ return;
+ }
+
+ bool bIsMaxRangeExceeded = true;
+
+ if (Player* pPlayer = GetLeaderForFollower())
+ {
+ if (HasFollowState(STATE_FOLLOW_RETURNING))
+ {
+ debug_log("TSCR: FollowerAI is returning to leader.");
+
+ RemoveFollowState(STATE_FOLLOW_RETURNING);
+ m_creature->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 && m_creature->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE))
+ {
+ bIsMaxRangeExceeded = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (m_creature->IsWithinDistInMap(pPlayer, MAX_PLAYER_DISTANCE))
+ bIsMaxRangeExceeded = false;
+ }
+ }
+
+ if (bIsMaxRangeExceeded)
+ {
+ debug_log("TSCR: FollowerAI failed because player/group was to far away or not found");
+ m_creature->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
+ m_creature->ForcedDespawn();
+ }
+}
+
+void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const Quest* pQuest)
+{
+ if (m_creature->getVictim())
+ {
+ debug_log("TSCR: FollowerAI attempt to StartFollow while in combat.");
+ return;
+ }
+
+ if (HasFollowState(STATE_FOLLOW_INPROGRESS))
+ {
+ error_log("TSCR: FollowerAI attempt to StartFollow while already following.");
+ return;
+ }
+
+ //set variables
+ m_uiLeaderGUID = pLeader->GetGUID();
+
+ if (uiFactionForFollower)
+ m_creature->setFaction(uiFactionForFollower);
+
+ m_pQuestForFollow = pQuest;
+
+ if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
+ {
+ m_creature->GetMotionMaster()->Clear();
+ m_creature->GetMotionMaster()->MoveIdle();
+ debug_log("TSCR: FollowerAI start with WAYPOINT_MOTION_TYPE, set to MoveIdle.");
+ }
+
+ m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
+
+ AddFollowState(STATE_FOLLOW_INPROGRESS);
+
+ m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
+
+ debug_log("TSCR: FollowerAI start follow %s (GUID %u)", pLeader->GetName(), m_uiLeaderGUID);
+}
+
+Player* FollowerAI::GetLeaderForFollower()
+{
+ if (Player* pLeader = Unit::GetPlayer(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() && m_creature->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE))
+ {
+ debug_log("TSCR: FollowerAI GetLeader changed and returned new leader.");
+ m_uiLeaderGUID = pMember->GetGUID();
+ return pMember;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ debug_log("TSCR: FollowerAI GetLeader can not find suitable leader.");
+ return NULL;
+}
+
+void FollowerAI::SetFollowComplete(bool bWithEndEvent)
+{
+ if (m_creature->hasUnitState(UNIT_STAT_FOLLOW))
+ {
+ m_creature->clearUnitState(UNIT_STAT_FOLLOW);
+
+ m_creature->StopMoving();
+ m_creature->GetMotionMaster()->Clear();
+ m_creature->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 (m_creature->hasUnitState(UNIT_STAT_FOLLOW))
+ {
+ m_creature->clearUnitState(UNIT_STAT_FOLLOW);
+
+ m_creature->StopMoving();
+ m_creature->GetMotionMaster()->Clear();
+ m_creature->GetMotionMaster()->MoveIdle();
+ }
+ }
+ else
+ {
+ RemoveFollowState(STATE_FOLLOW_PAUSED);
+
+ if (Player* pLeader = GetLeaderForFollower())
+ m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
+ }
+}