Merge [SD2]

r1355 Implement use of bitmask states instead of boolean variables in followerAI.
      Add function SetFollowPaused to temporary disable follow, with true/false argument to toggle the state on/off.
      Scripts affected are updated accordingly with needed changes.

--HG--
branch : trunk
This commit is contained in:
Kudlaty
2009-08-20 18:50:41 +02:00
parent c674463df4
commit f27fc3e939
7 changed files with 163 additions and 42 deletions
+66 -24
View File
@@ -23,10 +23,7 @@ FollowerAI::FollowerAI(Creature* pCreature) : ScriptedAI(pCreature),
m_uiLeaderGUID(0), m_uiLeaderGUID(0),
m_pQuestForFollow(NULL), m_pQuestForFollow(NULL),
m_uiUpdateFollowTimer(2500), m_uiUpdateFollowTimer(2500),
m_bIsFollowing(false), m_uiFollowState(STATE_FOLLOW_NONE)
m_bIsReturnToLeader(false),
m_bIsFollowComplete(false),
m_bIsEndEvent(false)
{} {}
void FollowerAI::AttackStart(Unit* pWho) void FollowerAI::AttackStart(Unit* pWho)
@@ -60,16 +57,23 @@ void FollowerAI::MoveInLineOfSight(Unit* pWho)
//It will cause m_creature to attack pWho that are attacking _any_ player (which has been confirmed may happen also on offi) //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. //The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
if (m_creature->hasUnitState(UNIT_STAT_FOLLOW) && if (m_creature->hasUnitState(UNIT_STAT_FOLLOW) &&
m_creature->GetCreatureInfo()->type_flags & 0x01000 && m_creature->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_UNK13 &&
pWho->getVictim() && pWho->getVictim() &&
pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() && pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() &&
m_creature->IsWithinDistInMap(pWho, MAX_PLAYER_DISTANCE) && m_creature->IsWithinDistInMap(pWho, MAX_PLAYER_DISTANCE) &&
m_creature->IsWithinLOSInMap(pWho)) m_creature->IsWithinLOSInMap(pWho))
{ {
pWho->RemoveAurasDueToSpell(SPELL_AURA_MOD_STEALTH); if (!m_creature->getVictim())
{
AttackStart(pWho); AttackStart(pWho);
} }
else else
{
pWho->SetInCombatWith(m_creature);
m_creature->AddThreat(pWho, 0.0f);
}
}
else
{ {
float attackRadius = m_creature->GetAttackDistance(pWho); float attackRadius = m_creature->GetAttackDistance(pWho);
if (m_creature->IsWithinDistInMap(pWho, attackRadius) && m_creature->IsWithinLOSInMap(pWho)) if (m_creature->IsWithinDistInMap(pWho, attackRadius) && m_creature->IsWithinLOSInMap(pWho))
@@ -91,7 +95,7 @@ void FollowerAI::MoveInLineOfSight(Unit* pWho)
void FollowerAI::JustDied(Unit* pKiller) void FollowerAI::JustDied(Unit* pKiller)
{ {
if (!m_bIsFollowing || !m_uiLeaderGUID || !m_pQuestForFollow) if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || !m_uiLeaderGUID || !m_pQuestForFollow)
return; return;
//TODO: need a better check for quests with time limit. //TODO: need a better check for quests with time limit.
@@ -118,10 +122,7 @@ void FollowerAI::JustDied(Unit* pKiller)
void FollowerAI::JustRespawned() void FollowerAI::JustRespawned()
{ {
m_bIsFollowing = false; m_uiFollowState = STATE_FOLLOW_NONE;
m_bIsReturnToLeader = false;
m_bIsFollowComplete = false;
m_bIsEndEvent = false;
if (!IsCombatMovement()) if (!IsCombatMovement())
SetCombatMovement(true); SetCombatMovement(true);
@@ -139,7 +140,7 @@ void FollowerAI::EnterEvadeMode()
m_creature->CombatStop(true); m_creature->CombatStop(true);
m_creature->SetLootRecipient(NULL); m_creature->SetLootRecipient(NULL);
if (m_bIsFollowing) if (HasFollowState(STATE_FOLLOW_INPROGRESS))
{ {
debug_log("TSCR: FollowerAI left combat, returning to CombatStartPosition."); debug_log("TSCR: FollowerAI left combat, returning to CombatStartPosition.");
@@ -161,11 +162,13 @@ void FollowerAI::EnterEvadeMode()
void FollowerAI::UpdateAI(const uint32 uiDiff) void FollowerAI::UpdateAI(const uint32 uiDiff)
{ {
if (m_bIsFollowing && !m_creature->getVictim()) Unit* pUnit = m_creature->getVictim();
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !m_creature->getVictim())
{ {
if (m_uiUpdateFollowTimer < uiDiff) if (m_uiUpdateFollowTimer < uiDiff)
{ {
if (m_bIsFollowComplete && !m_bIsEndEvent) if (HasFollowState(STATE_FOLLOW_COMPLETE) && !HasFollowState(STATE_FOLLOW_POSTEVENT))
{ {
debug_log("TSCR: FollowerAI is set completed, despawns."); debug_log("TSCR: FollowerAI is set completed, despawns.");
m_creature->ForcedDespawn(); m_creature->ForcedDespawn();
@@ -176,11 +179,12 @@ void FollowerAI::UpdateAI(const uint32 uiDiff)
if (Player* pPlayer = GetLeaderForFollower()) if (Player* pPlayer = GetLeaderForFollower())
{ {
if (m_bIsReturnToLeader) if (HasFollowState(STATE_FOLLOW_RETURNING))
{ {
debug_log("TSCR: FollowerAI is returning to leader."); debug_log("TSCR: FollowerAI is returning to leader.");
RemoveFollowState(STATE_FOLLOW_RETURNING);
m_creature->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); m_creature->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
m_bIsReturnToLeader = false;
return; return;
} }
@@ -230,13 +234,16 @@ void FollowerAI::UpdateFollowerAI(const uint32 uiDiff)
void FollowerAI::MovementInform(uint32 uiMotionType, uint32 uiPointId) void FollowerAI::MovementInform(uint32 uiMotionType, uint32 uiPointId)
{ {
if (uiMotionType != POINT_MOTION_TYPE || !m_bIsFollowing) if (uiMotionType != POINT_MOTION_TYPE || !HasFollowState(STATE_FOLLOW_INPROGRESS))
return; return;
if (uiPointId == POINT_COMBAT_START) if (uiPointId == POINT_COMBAT_START)
{ {
if (GetLeaderForFollower()) if (GetLeaderForFollower())
m_bIsReturnToLeader = true; {
if (!HasFollowState(STATE_FOLLOW_PAUSED))
AddFollowState(STATE_FOLLOW_RETURNING);
}
else else
m_creature->ForcedDespawn(); m_creature->ForcedDespawn();
} }
@@ -250,7 +257,7 @@ void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const
return; return;
} }
if (m_bIsFollowing) if (HasFollowState(STATE_FOLLOW_INPROGRESS))
{ {
error_log("TSCR: FollowerAI attempt to StartFollow while already following."); error_log("TSCR: FollowerAI attempt to StartFollow while already following.");
return; return;
@@ -273,9 +280,9 @@ void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); AddFollowState(STATE_FOLLOW_INPROGRESS);
m_bIsFollowing = true; m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
debug_log("TSCR: FollowerAI start follow %s (GUID %u)", pLeader->GetName(), m_uiLeaderGUID); debug_log("TSCR: FollowerAI start follow %s (GUID %u)", pLeader->GetName(), m_uiLeaderGUID);
} }
@@ -316,10 +323,45 @@ void FollowerAI::SetFollowComplete(bool bWithEndEvent)
{ {
m_creature->clearUnitState(UNIT_STAT_FOLLOW); m_creature->clearUnitState(UNIT_STAT_FOLLOW);
m_creature->GetMotionMaster()->MovementExpired(); m_creature->StopMoving();
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveIdle(); m_creature->GetMotionMaster()->MoveIdle();
} }
m_bIsEndEvent = bWithEndEvent; if (bWithEndEvent)
m_bIsFollowComplete = true; 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);
}
} }
+18 -7
View File
@@ -5,6 +5,17 @@
#ifndef SC_FOLLOWERAI_H #ifndef SC_FOLLOWERAI_H
#define SC_FOLLOWERAI_H #define SC_FOLLOWERAI_H
enum eFollowState
{
STATE_FOLLOW_NONE = 0x000,
STATE_FOLLOW_INPROGRESS = 0x001, //must always have this state for any follow
STATE_FOLLOW_RETURNING = 0x002, //when returning to combat start after being in combat
STATE_FOLLOW_PAUSED = 0x004, //disables following
STATE_FOLLOW_COMPLETE = 0x008, //follow is completed and may end
STATE_FOLLOW_PREEVENT = 0x010, //not implemented (allow pre event to run, before follow is initiated)
STATE_FOLLOW_POSTEVENT = 0x020 //can be set at complete and allow post event to run
};
class TRINITY_DLL_DECL FollowerAI : public ScriptedAI class TRINITY_DLL_DECL FollowerAI : public ScriptedAI
{ {
public: public:
@@ -30,21 +41,21 @@ class TRINITY_DLL_DECL FollowerAI : public ScriptedAI
void StartFollow(Player* pPlayer, uint32 uiFactionForFollower = 0, const Quest* pQuest = NULL); void StartFollow(Player* pPlayer, uint32 uiFactionForFollower = 0, const Quest* pQuest = NULL);
void SetFollowPaused(bool bPaused); //if special event require follow mode to hold/resume during the follow
protected: protected:
bool HasFollowState(uint32 uiFollowState) { return (m_uiFollowState & uiFollowState); }
void AddFollowState(uint32 uiFollowState) { m_uiFollowState |= uiFollowState; }
void RemoveFollowState(uint32 uiFollowState) { m_uiFollowState &= ~uiFollowState; }
void SetFollowComplete(bool bWithEndEvent = false); void SetFollowComplete(bool bWithEndEvent = false);
bool IsFollowComplete() { return m_bIsFollowComplete; }
bool IsEndEventInProgress() { return m_bIsEndEvent; }
Player* GetLeaderForFollower(); Player* GetLeaderForFollower();
private: private:
uint64 m_uiLeaderGUID; uint64 m_uiLeaderGUID;
uint32 m_uiUpdateFollowTimer; uint32 m_uiUpdateFollowTimer;
uint32 m_uiFollowState;
bool m_bIsFollowing;
bool m_bIsReturnToLeader;
bool m_bIsFollowComplete;
bool m_bIsEndEvent;
const Quest* m_pQuestForFollow; //normally we have a quest const Quest* m_pQuestForFollow; //normally we have a quest
}; };
@@ -55,6 +55,7 @@ enum
SAY_KER_END = -1000444, SAY_KER_END = -1000444,
SPELL_SLEEP_VISUAL = 25148,
SPELL_AWAKEN = 17536, SPELL_AWAKEN = 17536,
QUEST_SLEEPER_AWAKENED = 5321, QUEST_SLEEPER_AWAKENED = 5321,
NPC_LILADRIS = 11219, //attackers entries unknown NPC_LILADRIS = 11219, //attackers entries unknown
@@ -66,17 +67,20 @@ struct TRINITY_DLL_DECL npc_kerlonianAI : public FollowerAI
{ {
npc_kerlonianAI(Creature* pCreature) : FollowerAI(pCreature) { } npc_kerlonianAI(Creature* pCreature) : FollowerAI(pCreature) { }
uint32 m_uiFallAsleepTimer;
void Reset() void Reset()
{ {
m_uiFallAsleepTimer = urand(10000, 45000);
} }
void MoveInLineOfSight(Unit *pWho) void MoveInLineOfSight(Unit *pWho)
{ {
FollowerAI::MoveInLineOfSight(pWho); FollowerAI::MoveInLineOfSight(pWho);
if (!m_creature->getVictim() && !IsFollowComplete() && pWho->GetEntry() == NPC_LILADRIS) if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_LILADRIS)
{ {
if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE*2)) if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE*5))
{ {
if (Player* pPlayer = GetLeaderForFollower()) if (Player* pPlayer = GetLeaderForFollower())
{ {
@@ -90,6 +94,69 @@ struct TRINITY_DLL_DECL npc_kerlonianAI : public FollowerAI
} }
} }
} }
void SpellHit(Unit* pCaster, const SpellEntry* pSpell)
{
if (HasFollowState(STATE_FOLLOW_INPROGRESS | STATE_FOLLOW_PAUSED) && pSpell->Id == SPELL_AWAKEN)
ClearSleeping();
}
void SetSleeping()
{
SetFollowPaused(true);
switch(rand()%3)
{
case 0: DoScriptText(EMOTE_KER_SLEEP_1, m_creature); break;
case 1: DoScriptText(EMOTE_KER_SLEEP_2, m_creature); break;
case 2: DoScriptText(EMOTE_KER_SLEEP_3, m_creature); break;
}
switch(rand()%4)
{
case 0: DoScriptText(SAY_KER_SLEEP_1, m_creature); break;
case 1: DoScriptText(SAY_KER_SLEEP_2, m_creature); break;
case 2: DoScriptText(SAY_KER_SLEEP_3, m_creature); break;
case 3: DoScriptText(SAY_KER_SLEEP_4, m_creature); break;
}
m_creature->SetStandState(UNIT_STAND_STATE_SLEEP);
m_creature->CastSpell(m_creature, SPELL_SLEEP_VISUAL, false);
}
void ClearSleeping()
{
m_creature->RemoveAurasDueToSpell(SPELL_SLEEP_VISUAL);
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
DoScriptText(EMOTE_KER_AWAKEN, m_creature);
SetFollowPaused(false);
}
void UpdateFollowerAI(const uint32 uiDiff)
{
if (!UpdateVictim())
{
if (!HasFollowState(STATE_FOLLOW_INPROGRESS))
return;
if (!HasFollowState(STATE_FOLLOW_PAUSED))
{
if (m_uiFallAsleepTimer < uiDiff)
{
SetSleeping();
m_uiFallAsleepTimer = urand(25000, 90000);
}
else
m_uiFallAsleepTimer -= uiDiff;
}
return;
}
DoMeleeAttackIfReady();
}
}; };
CreatureAI* GetAI_npc_kerlonian(Creature* pCreature) CreatureAI* GetAI_npc_kerlonian(Creature* pCreature)
@@ -104,7 +171,7 @@ bool QuestAccept_npc_kerlonian(Player* pPlayer, Creature* pCreature, const Quest
if (npc_kerlonianAI* pKerlonianAI = CAST_AI(npc_kerlonianAI, pCreature->AI())) if (npc_kerlonianAI* pKerlonianAI = CAST_AI(npc_kerlonianAI, pCreature->AI()))
{ {
pCreature->SetStandState(UNIT_STAND_STATE_STAND); pCreature->SetStandState(UNIT_STAND_STATE_STAND);
DoScriptText(SAY_KER_START, pCreature); DoScriptText(SAY_KER_START, pCreature, pPlayer);
pKerlonianAI->StartFollow(pPlayer, FACTION_KER_ESCORTEE, pQuest); pKerlonianAI->StartFollow(pPlayer, FACTION_KER_ESCORTEE, pQuest);
} }
} }
@@ -264,7 +331,7 @@ struct TRINITY_DLL_DECL npc_threshwackonatorAI : public FollowerAI
{ {
FollowerAI::MoveInLineOfSight(pWho); FollowerAI::MoveInLineOfSight(pWho);
if (!m_creature->getVictim() && !IsFollowComplete() && pWho->GetEntry() == NPC_GELKAK) if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_GELKAK)
{ {
if (m_creature->IsWithinDistInMap(pWho, 10.0f)) if (m_creature->IsWithinDistInMap(pWho, 10.0f))
{ {
@@ -479,7 +479,7 @@ struct TRINITY_DLL_DECL npc_toogaAI : public FollowerAI
{ {
FollowerAI::MoveInLineOfSight(pWho); FollowerAI::MoveInLineOfSight(pWho);
if (!m_creature->getVictim() && !IsFollowComplete() && !IsEndEventInProgress() && pWho->GetEntry() == NPC_TORTA) if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE | STATE_FOLLOW_POSTEVENT) && pWho->GetEntry() == NPC_TORTA)
{ {
if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE)) if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE))
{ {
@@ -511,7 +511,7 @@ struct TRINITY_DLL_DECL npc_toogaAI : public FollowerAI
if (!UpdateVictim()) if (!UpdateVictim())
{ {
//we are doing the post-event, or... //we are doing the post-event, or...
if (IsEndEventInProgress()) if (HasFollowState(STATE_FOLLOW_POSTEVENT))
{ {
if (m_uiPostEventTimer < uiDiff) if (m_uiPostEventTimer < uiDiff)
{ {
@@ -553,7 +553,7 @@ struct TRINITY_DLL_DECL npc_toogaAI : public FollowerAI
m_uiPostEventTimer -= uiDiff; m_uiPostEventTimer -= uiDiff;
} }
//...we are doing regular speech check //...we are doing regular speech check
else if (!IsFollowComplete()) else if (HasFollowState(STATE_FOLLOW_INPROGRESS))
{ {
if (m_uiCheckSpeechTimer < uiDiff) if (m_uiCheckSpeechTimer < uiDiff)
{ {
@@ -51,7 +51,7 @@ struct TRINITY_DLL_DECL npc_mistAI : public FollowerAI
{ {
FollowerAI::MoveInLineOfSight(pWho); FollowerAI::MoveInLineOfSight(pWho);
if (!m_creature->getVictim() && !IsFollowComplete() && pWho->GetEntry() == NPC_ARYNIA) if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_ARYNIA)
{ {
if (m_creature->IsWithinDistInMap(pWho, 10.0f)) if (m_creature->IsWithinDistInMap(pWho, 10.0f))
{ {
@@ -189,7 +189,7 @@ struct TRINITY_DLL_DECL npc_ringoAI : public FollowerAI
{ {
FollowerAI::MoveInLineOfSight(pWho); FollowerAI::MoveInLineOfSight(pWho);
if (!m_creature->getVictim() && !IsFollowComplete() && pWho->GetEntry() == NPC_SPRAGGLE) if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_SPRAGGLE)
{ {
if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE)) if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE))
{ {
@@ -209,7 +209,7 @@ struct TRINITY_DLL_DECL npc_ringoAI : public FollowerAI
{ {
if (!UpdateVictim()) if (!UpdateVictim())
{ {
if (IsEndEventInProgress()) if (HasFollowState(STATE_FOLLOW_POSTEVENT))
{ {
if (m_uiEndEventTimer < uiDiff) if (m_uiEndEventTimer < uiDiff)
{ {
@@ -25,6 +25,7 @@ EndScriptData */
guard_azuremyst guard_azuremyst
guard_bluffwatcher guard_bluffwatcher
guard_contested guard_contested
guard_dalaran
guard_darnassus guard_darnassus
guard_dunmorogh guard_dunmorogh
guard_durotar guard_durotar