diff options
| author | maximius <none@none> | 2009-10-17 15:51:44 -0700 |
|---|---|---|
| committer | maximius <none@none> | 2009-10-17 15:51:44 -0700 |
| commit | e585187b248f48b3c6e9247b49fa07c6565d65e5 (patch) | |
| tree | 637c5b7ddacf41040bef4ea4f75a97da64c6a9bc /src/bindings/scripts/base | |
| parent | 26b5e033ffde3d161382fc9addbfa99738379641 (diff) | |
*Backed out changeset 3be01fb200a5
--HG--
branch : trunk
Diffstat (limited to 'src/bindings/scripts/base')
| -rw-r--r-- | src/bindings/scripts/base/escort_ai.cpp | 88 | ||||
| -rw-r--r-- | src/bindings/scripts/base/escort_ai.h | 29 | ||||
| -rw-r--r-- | src/bindings/scripts/base/follower_ai.cpp | 69 | ||||
| -rw-r--r-- | src/bindings/scripts/base/follower_ai.h | 21 | ||||
| -rw-r--r-- | src/bindings/scripts/base/guard_ai.cpp | 31 | ||||
| -rw-r--r-- | src/bindings/scripts/base/guard_ai.h | 13 | ||||
| -rw-r--r-- | src/bindings/scripts/base/simple_ai.cpp | 42 | ||||
| -rw-r--r-- | src/bindings/scripts/base/simple_ai.h | 17 |
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 |
