aboutsummaryrefslogtreecommitdiff
path: root/src/bindings/scripts/base
diff options
context:
space:
mode:
authormaximius <none@none>2009-10-17 15:51:44 -0700
committermaximius <none@none>2009-10-17 15:51:44 -0700
commite585187b248f48b3c6e9247b49fa07c6565d65e5 (patch)
tree637c5b7ddacf41040bef4ea4f75a97da64c6a9bc /src/bindings/scripts/base
parent26b5e033ffde3d161382fc9addbfa99738379641 (diff)
*Backed out changeset 3be01fb200a5
--HG-- branch : trunk
Diffstat (limited to 'src/bindings/scripts/base')
-rw-r--r--src/bindings/scripts/base/escort_ai.cpp88
-rw-r--r--src/bindings/scripts/base/escort_ai.h29
-rw-r--r--src/bindings/scripts/base/follower_ai.cpp69
-rw-r--r--src/bindings/scripts/base/follower_ai.h21
-rw-r--r--src/bindings/scripts/base/guard_ai.cpp31
-rw-r--r--src/bindings/scripts/base/guard_ai.h13
-rw-r--r--src/bindings/scripts/base/simple_ai.cpp42
-rw-r--r--src/bindings/scripts/base/simple_ai.h17
8 files changed, 305 insertions, 5 deletions
diff --git a/src/bindings/scripts/base/escort_ai.cpp b/src/bindings/scripts/base/escort_ai.cpp
index 5c256aceb65..4d6083ab470 100644
--- a/src/bindings/scripts/base/escort_ai.cpp
+++ b/src/bindings/scripts/base/escort_ai.cpp
@@ -1,19 +1,23 @@
/* 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: Npc_EscortAI
SD%Complete: 100
SDComment:
SDCategory: Npc
EndScriptData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
enum ePoints
{
POINT_LAST_POINT = 0xFFFFFF,
POINT_HOME = 0xFFFFFE
};
+
npc_escortAI::npc_escortAI(Creature* pCreature) : ScriptedAI(pCreature),
m_uiPlayerGUID(0),
MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE),
@@ -30,32 +34,40 @@ npc_escortAI::npc_escortAI(Creature* pCreature) : ScriptedAI(pCreature),
m_bCanReturnToStart(false),
ScriptWP(false)
{}
+
void npc_escortAI::AttackStart(Unit* pWho)
{
if (!pWho)
return;
+
if (m_creature->Attack(pWho, true))
{
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
m_creature->GetMotionMaster()->MovementExpired();
+
if (IsCombatMovement())
m_creature->GetMotionMaster()->MoveChase(pWho);
}
}
+
//see followerAI
bool npc_escortAI::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, GetMaxPlayerDistance()) && m_creature->IsWithinLOSInMap(pWho))
{
@@ -72,16 +84,20 @@ bool npc_escortAI::AssistPlayerInCombat(Unit* pWho)
return true;
}
}
+
return false;
}
+
void npc_escortAI::MoveInLineOfSight(Unit* pWho)
{
if (!m_creature->hasUnitState(UNIT_STAT_STUNNED) && pWho->isTargetableForAttack() && pWho->isInAccessiblePlaceFor(m_creature))
{
if (HasEscortState(STATE_ESCORT_ESCORTING) && 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);
@@ -101,15 +117,17 @@ void npc_escortAI::MoveInLineOfSight(Unit* pWho)
}
}
}
+
void npc_escortAI::JustDied(Unit* pKiller)
{
if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_uiPlayerGUID || !m_pQuestForEscort)
return;
+
if (Player* pPlayer = GetPlayerForEscort())
{
if (Group* pGroup = pPlayer->GetGroup())
{
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
+ for(GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
{
if (Player* pMember = pRef->getSource())
{
@@ -125,29 +143,37 @@ void npc_escortAI::JustDied(Unit* pKiller)
}
}
}
+
void npc_escortAI::JustRespawned()
{
m_uiEscortState = STATE_ESCORT_NONE;
+
if (!IsCombatMovement())
SetCombatMovement(true);
+
//add a small delay before going to first waypoint, normal in near all cases
m_uiWPWaitTimer = 2500;
+
if (m_creature->getFaction() != m_creature->GetCreatureInfo()->faction_A)
me->RestoreFaction();
+
Reset();
}
+
void npc_escortAI::ReturnToLastPoint()
{
float x, y, z, o;
m_creature->GetHomePosition(x, y, z, o);
m_creature->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z);
}
+
void npc_escortAI::EnterEvadeMode()
{
m_creature->RemoveAllAuras();
m_creature->DeleteThreatList();
m_creature->CombatStop(true);
m_creature->SetLootRecipient(NULL);
+
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
AddEscortState(STATE_ESCORT_RETURNING);
@@ -156,17 +182,20 @@ void npc_escortAI::EnterEvadeMode()
}
else
m_creature->GetMotionMaster()->MoveTargetedHome();
+
Reset();
}
+
bool npc_escortAI::IsPlayerOrGroupInRange()
{
if (Player* pPlayer = GetPlayerForEscort())
{
if (Group* pGroup = pPlayer->GetGroup())
{
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
+ for(GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
{
Player* pMember = pRef->getSource();
+
if (pMember && m_creature->IsWithinDistInMap(pMember, GetMaxPlayerDistance()))
{
return true;
@@ -182,6 +211,7 @@ bool npc_escortAI::IsPlayerOrGroupInRange()
}
return false;
}
+
void npc_escortAI::UpdateAI(const uint32 uiDiff)
{
//Waypoint Updating
@@ -195,15 +225,20 @@ void npc_escortAI::UpdateAI(const uint32 uiDiff)
if (DespawnAtEnd)
{
debug_log("TSCR: EscortAI reached end of waypoints");
+
if (m_bCanReturnToStart)
{
float fRetX, fRetY, fRetZ;
m_creature->GetRespawnCoord(fRetX, fRetY, fRetZ);
+
m_creature->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ);
+
m_uiWPWaitTimer = 0;
+
debug_log("TSCR: EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ);
return;
}
+
if (m_bCanInstantRespawn)
{
m_creature->setDeathState(JUST_DIED);
@@ -211,25 +246,31 @@ void npc_escortAI::UpdateAI(const uint32 uiDiff)
}
else
m_creature->ForcedDespawn();
+
return;
}
else
{
debug_log("TSCR: EscortAI reached end of waypoints with Despawn off");
+
return;
}
}
+
if (!HasEscortState(STATE_ESCORT_PAUSED))
{
m_creature->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z);
debug_log("TSCR: EscortAI start waypoint %u (%f, %f, %f).", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z);
+
WaypointStart(CurrentWP->id);
+
m_uiWPWaitTimer = 0;
}
}
else
m_uiWPWaitTimer -= uiDiff;
}
+
//Check if player or any member of his group is within range
if (HasEscortState(STATE_ESCORT_ESCORTING) && m_uiPlayerGUID && !m_creature->getVictim() && !HasEscortState(STATE_ESCORT_RETURNING))
{
@@ -238,6 +279,7 @@ void npc_escortAI::UpdateAI(const uint32 uiDiff)
if (DespawnAtFar && !IsPlayerOrGroupInRange())
{
debug_log("TSCR: EscortAI failed because player/group was to far away or not found");
+
if (m_bCanInstantRespawn)
{
m_creature->setDeathState(JUST_DIED);
@@ -245,39 +287,49 @@ void npc_escortAI::UpdateAI(const uint32 uiDiff)
}
else
m_creature->ForcedDespawn();
+
return;
}
+
m_uiPlayerCheckTimer = 1000;
}
else
m_uiPlayerCheckTimer -= uiDiff;
}
+
UpdateEscortAI(uiDiff);
}
+
void npc_escortAI::UpdateEscortAI(const uint32 uiDiff)
{
if (CanMelee && UpdateVictim())
DoMeleeAttackIfReady();
}
+
void npc_escortAI::MovementInform(uint32 uiMoveType, uint32 uiPointId)
{
if (uiMoveType != POINT_MOTION_TYPE || !HasEscortState(STATE_ESCORT_ESCORTING))
return;
+
//Combat start position reached, continue waypoint movement
if (uiPointId == POINT_LAST_POINT)
{
debug_log("TSCR: EscortAI has returned to original position before combat");
+
if (m_bIsRunning && m_creature->HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE))
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
else if (!m_bIsRunning && !m_creature->HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE))
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
RemoveEscortState(STATE_ESCORT_RETURNING);
+
if (!m_uiWPWaitTimer)
m_uiWPWaitTimer = 1;
}
else if (uiPointId == POINT_HOME)
{
debug_log("TSCR: EscortAI has returned to original home location and will continue from beginning of waypoint list.");
+
CurrentWP = WaypointList.begin();
m_uiWPWaitTimer = 1;
}
@@ -289,13 +341,18 @@ void npc_escortAI::MovementInform(uint32 uiMoveType, uint32 uiPointId)
error_log("TSCR ERROR: EscortAI reached waypoint out of order %u, expected %u", uiPointId, CurrentWP->id);
return;
}
+
debug_log("TSCR: EscortAI Waypoint %u reached", CurrentWP->id);
+
//Call WP function
WaypointReached(CurrentWP->id);
+
m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1;
+
++CurrentWP;
}
}
+
/*
void npc_escortAI::OnPossess(bool apply)
{
@@ -314,10 +371,13 @@ void npc_escortAI::OnPossess(bool apply)
}
}
*/
+
void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 WaitTimeMs)
{
Escort_Waypoint t(id, x, y, z, WaitTimeMs);
+
WaypointList.push_back(t);
+
// i think SD2 no longer uses this function
ScriptWP = true;
/*PointMovement wp;
@@ -329,18 +389,23 @@ void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 Wait
wp.m_uiWaitTime = WaitTimeMs;
PointMovementMap[wp.m_uiCreatureEntry].push_back(wp);*/
}
+
void npc_escortAI::FillPointMovementListForCreature()
{
std::vector<ScriptPointMove> const &pPointsEntries = pSystemMgr.GetPointMoveList(m_creature->GetEntry());
+
if (pPointsEntries.empty())
return;
+
std::vector<ScriptPointMove>::const_iterator itr;
+
for (itr = pPointsEntries.begin(); itr != pPointsEntries.end(); ++itr)
{
Escort_Waypoint pPoint(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime);
WaypointList.push_back(pPoint);
}
}
+
void npc_escortAI::SetRun(bool bRun)
{
if (bRun)
@@ -359,6 +424,7 @@ void npc_escortAI::SetRun(bool bRun)
}
m_bIsRunning = bRun;
}
+
//TODO: get rid of this many variables passed in function.
void npc_escortAI::Start(bool bIsActiveAttacker, bool bRun, uint64 uiPlayerGUID, const Quest* pQuest, bool bInstantRespawn, bool bCanLoopPath)
{
@@ -367,52 +433,70 @@ void npc_escortAI::Start(bool bIsActiveAttacker, bool bRun, uint64 uiPlayerGUID,
error_log("TSCR ERROR: EscortAI attempt to Start while in combat.");
return;
}
+
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
error_log("TSCR: EscortAI attempt to Start while already escorting.");
return;
}
+
if(!ScriptWP) // sd2 never adds wp in script, but tc does
{
+
if (!WaypointList.empty())
WaypointList.clear();
+
FillPointMovementListForCreature();
+
}
+
if (WaypointList.empty())
{
error_db_log("TSCR: EscortAI Start with 0 waypoints (possible missing entry in script_waypoint).");
return;
}
+
//set variables
m_bIsActiveAttacker = bIsActiveAttacker;
m_bIsRunning = bRun;
+
m_uiPlayerGUID = uiPlayerGUID;
m_pQuestForEscort = pQuest;
+
m_bCanInstantRespawn = bInstantRespawn;
m_bCanReturnToStart = bCanLoopPath;
+
if (m_bCanReturnToStart && m_bCanInstantRespawn)
debug_log("TSCR: EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
+
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
{
m_creature->GetMotionMaster()->MovementExpired();
m_creature->GetMotionMaster()->MoveIdle();
debug_log("TSCR: EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle.");
}
+
//disable npcflags
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
+
debug_log("TSCR: EscortAI started with %u waypoints. ActiveAttacker = %d, Run = %d, PlayerGUID = %u", WaypointList.size(), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID);
+
CurrentWP = WaypointList.begin();
+
//Set initial speed
if (m_bIsRunning)
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
else
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
AddEscortState(STATE_ESCORT_ESCORTING);
}
+
void npc_escortAI::SetEscortPaused(bool bPaused)
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
return;
+
if (bPaused)
AddEscortState(STATE_ESCORT_PAUSED);
else
diff --git a/src/bindings/scripts/base/escort_ai.h b/src/bindings/scripts/base/escort_ai.h
index 89a0fc596e8..d0cb55b100a 100644
--- a/src/bindings/scripts/base/escort_ai.h
+++ b/src/bindings/scripts/base/escort_ai.h
@@ -1,10 +1,14 @@
/* 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 */
+
#ifndef SC_ESCORTAI_H
#define SC_ESCORTAI_H
+
#include "../system/system.h"
+
#define DEFAULT_MAX_PLAYER_DISTANCE 50
+
struct Escort_Waypoint
{
Escort_Waypoint(uint32 _id, float _x, float _y, float _z, uint32 _w)
@@ -15,12 +19,14 @@ struct Escort_Waypoint
z = _z;
WaitTimeMs = _w;
}
+
uint32 id;
float x;
float y;
float z;
uint32 WaitTimeMs;
};
+
enum eEscortState
{
STATE_ESCORT_NONE = 0x000, //nothing in progress
@@ -28,53 +34,76 @@ enum eEscortState
STATE_ESCORT_RETURNING = 0x002, //escort is returning after being in combat
STATE_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed
};
+
struct TRINITY_DLL_DECL npc_escortAI : public ScriptedAI
{
public:
explicit npc_escortAI(Creature* pCreature);
~npc_escortAI() {}
+
// CreatureAI functions
void AttackStart(Unit* who);
+
void MoveInLineOfSight(Unit* who);
+
void JustDied(Unit*);
+
void JustRespawned();
+
void ReturnToLastPoint();
+
void EnterEvadeMode();
+
void UpdateAI(const uint32); //the "internal" update, calls UpdateEscortAI()
virtual void UpdateEscortAI(const uint32); //used when it's needed to add code in update (abilities, scripted events, etc)
+
void MovementInform(uint32, uint32);
+
// EscortAI functions
void AddWaypoint(uint32 id, float x, float y, float z, uint32 WaitTimeMs = 0);
+
virtual void WaypointReached(uint32 uiPointId) = 0;
virtual void WaypointStart(uint32 uiPointId) {}
+
void Start(bool bIsActiveAttacker = true, bool bRun = false, uint64 uiPlayerGUID = 0, const Quest* pQuest = NULL, bool bInstantRespawn = false, bool bCanLoopPath = false);
+
void SetRun(bool bRun = true);
void SetEscortPaused(bool uPaused);
+
bool HasEscortState(uint32 uiEscortState) { return (m_uiEscortState & uiEscortState); }
+
void SetMaxPlayerDistance(float newMax) { MaxPlayerDistance = newMax; }
float GetMaxPlayerDistance() { return MaxPlayerDistance; }
+
void SetCanMelee(bool usemelee) { CanMelee = usemelee; }
void SetDespawnAtEnd(bool despawn) { DespawnAtEnd = despawn; }
void SetDespawnAtFar(bool despawn) { DespawnAtFar = despawn; }
bool GetAttack() { return m_bIsActiveAttacker; }//used in EnterEvadeMode override
void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; }
uint64 GetEventStarterGUID() { return m_uiPlayerGUID; }
+
protected:
Player* GetPlayerForEscort() { return (Player*)Unit::GetUnit(*m_creature, m_uiPlayerGUID); }
+
private:
bool AssistPlayerInCombat(Unit* pWho);
bool IsPlayerOrGroupInRange();
void FillPointMovementListForCreature();
+
void AddEscortState(uint32 uiEscortState) { m_uiEscortState |= uiEscortState; }
void RemoveEscortState(uint32 uiEscortState) { m_uiEscortState &= ~uiEscortState; }
+
uint64 m_uiPlayerGUID;
uint32 m_uiWPWaitTimer;
uint32 m_uiPlayerCheckTimer;
uint32 m_uiEscortState;
float MaxPlayerDistance;
+
const Quest* m_pQuestForEscort; //generally passed in Start() when regular escort script.
+
std::list<Escort_Waypoint> WaypointList;
std::list<Escort_Waypoint>::iterator CurrentWP;
+
bool m_bIsActiveAttacker; //obsolete, determined by faction.
bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK)
bool m_bCanInstantRespawn; //if creature should respawn instantly after escort over (if not, database respawntime are used)
diff --git a/src/bindings/scripts/base/follower_ai.cpp b/src/bindings/scripts/base/follower_ai.cpp
index 67c03c396ce..8284af7aec0 100644
--- a/src/bindings/scripts/base/follower_ai.cpp
+++ b/src/bindings/scripts/base/follower_ai.cpp
@@ -1,40 +1,50 @@
/* 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 "precompiled.h"
#include "follower_ai.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.
@@ -42,15 +52,19 @@ 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))
{
@@ -67,16 +81,20 @@ bool FollowerAI::AssistPlayerInCombat(Unit* pWho)
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);
@@ -96,16 +114,18 @@ void FollowerAI::MoveInLineOfSight(Unit* pWho)
}
}
}
+
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())
+ for(GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
{
if (Player* pMember = pRef->getSource())
{
@@ -121,24 +141,31 @@ void FollowerAI::JustDied(Unit* pKiller)
}
}
}
+
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;
@@ -151,8 +178,10 @@ void FollowerAI::EnterEvadeMode()
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())
@@ -165,21 +194,26 @@ void FollowerAI::UpdateAI(const uint32 uiDiff)
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())
+ 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;
@@ -193,29 +227,36 @@ void FollowerAI::UpdateAI(const uint32 uiDiff)
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())
@@ -227,6 +268,7 @@ void FollowerAI::MovementInform(uint32 uiMotionType, uint32 uiPointId)
m_creature->ForcedDespawn();
}
}
+
void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const Quest* pQuest)
{
if (m_creature->getVictim())
@@ -234,27 +276,37 @@ void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const
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))
@@ -265,9 +317,10 @@ Player* FollowerAI::GetLeaderForFollower()
{
if (Group* pGroup = pLeader->GetGroup())
{
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
+ 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.");
@@ -279,18 +332,22 @@ Player* FollowerAI::GetLeaderForFollower()
}
}
}
+
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
@@ -298,18 +355,23 @@ void FollowerAI::SetFollowComplete(bool bWithEndEvent)
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();
@@ -318,6 +380,7 @@ void FollowerAI::SetFollowPaused(bool bPaused)
else
{
RemoveFollowState(STATE_FOLLOW_PAUSED);
+
if (Player* pLeader = GetLeaderForFollower())
m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
}
diff --git a/src/bindings/scripts/base/follower_ai.h b/src/bindings/scripts/base/follower_ai.h
index 2a7ab778629..289efd4a6b0 100644
--- a/src/bindings/scripts/base/follower_ai.h
+++ b/src/bindings/scripts/base/follower_ai.h
@@ -1,9 +1,12 @@
/* 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 */
+
#ifndef SC_FOLLOWERAI_H
#define SC_FOLLOWERAI_H
+
#include "../system/system.h"
+
enum eFollowState
{
STATE_FOLLOW_NONE = 0x000,
@@ -14,33 +17,51 @@ enum eFollowState
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
{
public:
explicit FollowerAI(Creature* pCreature);
~FollowerAI() {}
+
//virtual void WaypointReached(uint32 uiPointId) = 0;
+
void MovementInform(uint32 uiMotionType, uint32 uiPointId);
+
void AttackStart(Unit*);
+
void MoveInLineOfSight(Unit*);
+
void EnterEvadeMode();
+
void JustDied(Unit*);
+
void JustRespawned();
+
void UpdateAI(const uint32); //the "internal" update, calls UpdateFollowerAI()
virtual void UpdateFollowerAI(const uint32); //used when it's needed to add code in update (abilities, scripted events, etc)
+
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
void SetFollowComplete(bool bWithEndEvent = false);
+
bool HasFollowState(uint32 uiFollowState) { return (m_uiFollowState & uiFollowState); }
+
protected:
Player* GetLeaderForFollower();
+
private:
void AddFollowState(uint32 uiFollowState) { m_uiFollowState |= uiFollowState; }
void RemoveFollowState(uint32 uiFollowState) { m_uiFollowState &= ~uiFollowState; }
+
bool AssistPlayerInCombat(Unit* pWho);
+
uint64 m_uiLeaderGUID;
uint32 m_uiUpdateFollowTimer;
uint32 m_uiFollowState;
+
const Quest* m_pQuestForFollow; //normally we have a quest
};
+
#endif
diff --git a/src/bindings/scripts/base/guard_ai.cpp b/src/bindings/scripts/base/guard_ai.cpp
index badd763a78b..1001d3a1170 100644
--- a/src/bindings/scripts/base/guard_ai.cpp
+++ b/src/bindings/scripts/base/guard_ai.cpp
@@ -13,67 +13,84 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Guard_AI
SD%Complete: 90
SDComment:
SDCategory: Guards
EndScriptData */
+
#include "precompiled.h"
#include "guard_ai.h"
+
// **** This script is for use within every single guard to save coding time ****
+
#define GENERIC_CREATURE_COOLDOWN 5000
+
#define SAY_GUARD_SIL_AGGRO1 -1070001
#define SAY_GUARD_SIL_AGGRO2 -1070002
#define SAY_GUARD_SIL_AGGRO3 -1070003
+
guardAI::guardAI(Creature* pCreature) : ScriptedAI(pCreature),
GlobalCooldown(0),
BuffTimer(0)
{}
+
void guardAI::Reset()
{
GlobalCooldown = 0;
BuffTimer = 0; //Rebuff as soon as we can
}
+
void guardAI::EnterCombat(Unit *who)
{
if (m_creature->GetEntry() == 15184)
DoScriptText(RAND(SAY_GUARD_SIL_AGGRO1,SAY_GUARD_SIL_AGGRO2,SAY_GUARD_SIL_AGGRO3), m_creature, who);
+
if (SpellEntry const *spell = m_creature->reachWithSpellAttack(who))
DoCastSpell(who, spell);
}
+
void guardAI::JustDied(Unit *Killer)
{
//Send Zone Under Attack message to the LocalDefense and WorldDefense Channels
if (Player* pKiller = Killer->GetCharmerOrOwnerPlayerOrPlayerItself())
m_creature->SendZoneUnderAttackMessage(pKiller);
}
+
void guardAI::UpdateAI(const uint32 diff)
{
//Always decrease our global cooldown first
if (GlobalCooldown > diff)
GlobalCooldown -= diff;
else GlobalCooldown = 0;
+
//Buff timer (only buff when we are alive and not in combat
if (m_creature->isAlive() && !m_creature->isInCombat())
if (BuffTimer < diff)
{
//Find a spell that targets friendly and applies an aura (these are generally buffs)
SpellEntry const *info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_AURA);
+
if (info && !GlobalCooldown)
{
//Cast the buff spell
DoCastSpell(m_creature, info);
+
//Set our global cooldown
GlobalCooldown = GENERIC_CREATURE_COOLDOWN;
+
//Set our timer to 10 minutes before rebuff
BuffTimer = 600000;
} //Try agian in 30 seconds
else BuffTimer = 30000;
}else BuffTimer -= diff;
+
//Return since we have no target
if (!UpdateVictim())
return;
+
// Make sure our attack is ready and we arn't currently casting
if (m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false))
{
@@ -82,22 +99,27 @@ void guardAI::UpdateAI(const uint32 diff)
{
bool Healing = false;
SpellEntry const *info = NULL;
+
//Select a healing spell if less than 30% hp
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 30)
info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING);
+
//No healing spell available, select a hostile spell
if (info) Healing = true;
else info = SelectSpell(m_creature->getVictim(), -1, -1, SELECT_TARGET_ANY_ENEMY, 0, 0, 0, 0, SELECT_EFFECT_DONTCARE);
+
//20% chance to replace our white hit with a spell
if (info && rand() % 5 == 0 && !GlobalCooldown)
{
//Cast the spell
if (Healing)DoCastSpell(m_creature, info);
else DoCastSpell(m_creature->getVictim(), info);
+
//Set our global cooldown
GlobalCooldown = GENERIC_CREATURE_COOLDOWN;
}
else m_creature->AttackerStateUpdate(m_creature->getVictim());
+
m_creature->resetAttackTimer();
}
}
@@ -108,12 +130,15 @@ void guardAI::UpdateAI(const uint32 diff)
{
bool Healing = false;
SpellEntry const *info = NULL;
+
//Select a healing spell if less than 30% hp ONLY 33% of the time
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 30 && rand() % 3 == 0)
info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING);
+
//No healing spell available, See if we can cast a ranged spell (Range must be greater than ATTACK_DISTANCE)
if (info) Healing = true;
else info = SelectSpell(m_creature->getVictim(), -1, -1, SELECT_TARGET_ANY_ENEMY, 0, 0, NOMINAL_MELEE_RANGE, 0, SELECT_EFFECT_DONTCARE);
+
//Found a spell, check if we arn't on cooldown
if (info && !GlobalCooldown)
{
@@ -123,11 +148,14 @@ void guardAI::UpdateAI(const uint32 diff)
(*m_creature).GetMotionMaster()->Clear(false);
(*m_creature).GetMotionMaster()->MoveIdle();
}
+
//Cast spell
if (Healing) DoCastSpell(m_creature,info);
else DoCastSpell(m_creature->getVictim(),info);
+
//Set our global cooldown
GlobalCooldown = GENERIC_CREATURE_COOLDOWN;
+
} //If no spells available and we arn't moving run to target
else if ((*m_creature).GetMotionMaster()->GetCurrentMovementGeneratorType()!=TARGETED_MOTION_TYPE)
{
@@ -139,6 +167,7 @@ void guardAI::UpdateAI(const uint32 diff)
}
}
}
+
void guardAI::DoReplyToTextEmote(uint32 em)
{
switch(em)
@@ -151,11 +180,13 @@ void guardAI::DoReplyToTextEmote(uint32 em)
case TEXTEMOTE_CHICKEN: m_creature->HandleEmoteCommand(EMOTE_ONESHOT_POINT); break;
}
}
+
void guardAI_orgrimmar::ReceiveEmote(Player* pPlayer, uint32 text_emote)
{
if (pPlayer->GetTeam()==HORDE)
DoReplyToTextEmote(text_emote);
}
+
void guardAI_stormwind::ReceiveEmote(Player* pPlayer, uint32 text_emote)
{
if (pPlayer->GetTeam() == ALLIANCE)
diff --git a/src/bindings/scripts/base/guard_ai.h b/src/bindings/scripts/base/guard_ai.h
index dd4e414d1ae..a7fff32e3ab 100644
--- a/src/bindings/scripts/base/guard_ai.h
+++ b/src/bindings/scripts/base/guard_ai.h
@@ -1,31 +1,44 @@
/* 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 */
+
#ifndef SC_GUARDAI_H
#define SC_GUARDAI_H
+
#define GENERIC_CREATURE_COOLDOWN 5000
+
struct TRINITY_DLL_DECL guardAI : public ScriptedAI
{
public:
explicit guardAI(Creature* pCreature);
~guardAI() {}
+
uint32 GlobalCooldown; //This variable acts like the global cooldown that players have (1.5 seconds)
uint32 BuffTimer; //This variable keeps track of buffs
+
void Reset();
+
void EnterCombat(Unit *who);
+
void JustDied(Unit *Killer);
+
void UpdateAI(const uint32 diff);
+
//common used for guards in main cities
void DoReplyToTextEmote(uint32 em);
};
+
struct TRINITY_DLL_DECL guardAI_orgrimmar : public guardAI
{
guardAI_orgrimmar(Creature *c) : guardAI(c) {}
+
void ReceiveEmote(Player *player, uint32 text_emote);
};
+
struct TRINITY_DLL_DECL guardAI_stormwind : public guardAI
{
guardAI_stormwind(Creature *c) : guardAI(c) {}
+
void ReceiveEmote(Player *player, uint32 text_emote);
};
#endif
diff --git a/src/bindings/scripts/base/simple_ai.cpp b/src/bindings/scripts/base/simple_ai.cpp
index c607885f4d3..60511d163b7 100644
--- a/src/bindings/scripts/base/simple_ai.cpp
+++ b/src/bindings/scripts/base/simple_ai.cpp
@@ -13,14 +13,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: SimpleAI
SD%Complete: 100
SDComment: Base Class for SimpleAI creatures
SDCategory: Creatures
EndScriptData */
+
#include "precompiled.h"
#include "simple_ai.h"
+
SimpleAI::SimpleAI(Creature *c) : ScriptedAI(c)
{
//Clear all data
@@ -30,6 +33,7 @@ SimpleAI::SimpleAI(Creature *c) : ScriptedAI(c)
Aggro_Sound[0] = 0;
Aggro_Sound[1] = 0;
Aggro_Sound[2] = 0;
+
Death_TextId[0] = 0;
Death_TextId[1] = 0;
Death_TextId[2] = 0;
@@ -38,6 +42,7 @@ SimpleAI::SimpleAI(Creature *c) : ScriptedAI(c)
Death_Sound[2] = 0;
Death_Spell = 0;
Death_Target_Type = 0;
+
Kill_TextId[0] = 0;
Kill_TextId[1] = 0;
Kill_TextId[2] = 0;
@@ -46,12 +51,16 @@ SimpleAI::SimpleAI(Creature *c) : ScriptedAI(c)
Kill_Sound[2] = 0;
Kill_Spell = 0;
Kill_Target_Type = 0;
+
memset(Spell,0,sizeof(Spell));
+
EnterEvadeMode();
}
+
void SimpleAI::Reset()
{
}
+
void SimpleAI::EnterCombat(Unit *who)
{
//Reset cast timers
@@ -85,26 +94,35 @@ void SimpleAI::EnterCombat(Unit *who)
if (Spell[9].First_Cast >= 0)
Spell_Timer[9] = Spell[9].First_Cast;
else Spell_Timer[9] = 1000;
+
uint8 random_text = urand(0,2);
+
//Random text
if (Aggro_TextId[random_text])
DoScriptText(Aggro_TextId[random_text], m_creature, who);
+
//Random sound
if (Aggro_Sound[random_text])
DoPlaySoundToSet(m_creature, Aggro_Sound[random_text]);
}
+
void SimpleAI::KilledUnit(Unit *victim)
{
uint8 random_text = urand(0,2);
+
//Random yell
if (Kill_TextId[random_text])
DoScriptText(Kill_TextId[random_text], m_creature, victim);
+
//Random sound
if (Kill_Sound[random_text])
DoPlaySoundToSet(m_creature, Kill_Sound[random_text]);
+
if (!Kill_Spell)
return;
+
Unit* target = NULL;
+
switch (Kill_Target_Type)
{
case CAST_SELF:
@@ -126,25 +144,33 @@ void SimpleAI::KilledUnit(Unit *victim)
target = victim;
break;
}
+
//Target is ok, cast a spell on it
if (target)
DoCast(target, Kill_Spell);
}
+
void SimpleAI::DamageTaken(Unit *killer, uint32 &damage)
{
//Return if damage taken won't kill us
if (m_creature->GetHealth() > damage)
return;
+
uint8 random_text = urand(0,2);
+
//Random yell
if (Death_TextId[random_text])
DoScriptText(Death_TextId[random_text], m_creature, killer);
+
//Random sound
if (Death_Sound[random_text])
DoPlaySoundToSet(m_creature, Death_Sound[random_text]);
+
if (!Death_Spell)
return;
+
Unit* target = NULL;
+
switch (Death_Target_Type)
{
case CAST_SELF:
@@ -166,30 +192,36 @@ void SimpleAI::DamageTaken(Unit *killer, uint32 &damage)
target = killer;
break;
}
+
//Target is ok, cast a spell on it
if (target)
DoCast(target, Death_Spell);
}
+
void SimpleAI::UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Spells
for (uint32 i = 0; i < 10; ++i)
{
//Spell not valid
if (!Spell[i].Enabled || !Spell[i].Spell_Id)
continue;
+
if (Spell_Timer[i] < diff)
{
//Check if this is a percentage based
if (Spell[i].First_Cast < 0 && Spell[i].First_Cast > -100 && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() > -Spell[i].First_Cast)
continue;
+
//Check Current spell
if (!(Spell[i].InterruptPreviousCast && m_creature->IsNonMeleeSpellCasted(false)))
{
Unit* target = NULL;
+
switch (Spell[i].Cast_Target_Type)
{
case CAST_SELF:
@@ -208,29 +240,39 @@ void SimpleAI::UpdateAI(const uint32 diff)
target = SelectUnit(SELECT_TARGET_RANDOM,0);
break;
}
+
//Target is ok, cast a spell on it and then do our random yell
if (target)
{
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(false);
+
DoCast(target, Spell[i].Spell_Id);
+
//Yell and sound use the same number so that you can make
//the Creature yell with the correct sound effect attached
uint8 random_text = urand(0,2);
+
//Random yell
if (Spell[i].TextId[random_text])
DoScriptText(Spell[i].TextId[random_text], m_creature, target);
+
//Random sound
if (Spell[i].Text_Sound[random_text])
DoPlaySoundToSet(m_creature, Spell[i].Text_Sound[random_text]);
}
+
}
+
//Spell will cast agian when the cooldown is up
if (Spell[i].CooldownRandomAddition)
Spell_Timer[i] = Spell[i].Cooldown + (rand() % Spell[i].CooldownRandomAddition);
else Spell_Timer[i] = Spell[i].Cooldown;
+
}else Spell_Timer[i] -= diff;
+
}
+
DoMeleeAttackIfReady();
}
diff --git a/src/bindings/scripts/base/simple_ai.h b/src/bindings/scripts/base/simple_ai.h
index 308f5a475ec..3a2e8a9341a 100644
--- a/src/bindings/scripts/base/simple_ai.h
+++ b/src/bindings/scripts/base/simple_ai.h
@@ -1,8 +1,10 @@
/* 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 */
+
#ifndef SC_SIMPLEAI_H
#define SC_SIMPLEAI_H
+
enum CastTarget
{
CAST_SELF = 0, //Self cast
@@ -11,29 +13,41 @@ enum CastTarget
CAST_HOSTILE_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for)
CAST_HOSTILE_RANDOM, //Just any random target on our threat list
CAST_FRIENDLY_RANDOM, //NOT YET IMPLEMENTED
+
//Special cases
CAST_KILLEDUNIT_VICTIM, //Only works within KilledUnit function
CAST_JUSTDIED_KILLER, //Only works within JustDied function
};
+
struct TRINITY_DLL_DECL SimpleAI : public ScriptedAI
{
SimpleAI(Creature *c);// : ScriptedAI(c);
+
void Reset();
+
void EnterCombat(Unit *who);
+
void KilledUnit(Unit *victim);
+
void DamageTaken(Unit *killer, uint32 &damage);
+
void UpdateAI(const uint32 diff);
+
public:
+
int32 Aggro_TextId[3];
uint32 Aggro_Sound[3];
+
int32 Death_TextId[3];
uint32 Death_Sound[3];
uint32 Death_Spell;
uint32 Death_Target_Type;
+
int32 Kill_TextId[3];
uint32 Kill_Sound[3];
uint32 Kill_Spell;
uint32 Kill_Target_Type;
+
struct SimpleAI_Spell
{
uint32 Spell_Id; //Spell ID to cast
@@ -43,12 +57,15 @@ public:
uint32 Cast_Target_Type; //Target type (note that certain spells may ignore this)
bool InterruptPreviousCast; //Interrupt a previous cast if this spell needs to be cast
bool Enabled; //Spell enabled or disabled (default: false)
+
//3 texts to many?
int32 TextId[3];
uint32 Text_Sound[3];
}Spell[10];
+
protected:
uint32 Spell_Timer[10];
};
+
#endif