Merge pull request #21 from liyunfan1223/revert_spell_crash

Revert "Merge branch 'master' into Playerbot"
This commit is contained in:
Yunfan Li
2024-09-13 17:45:21 +08:00
committed by GitHub
32 changed files with 499 additions and 539 deletions
@@ -1,3 +0,0 @@
-- DB update 2024_09_05_00 -> 2024_09_09_00
--
UPDATE `creature_template` SET `flags_extra` = `flags_extra` |2147483648 WHERE `entry` = 22841;
@@ -1,6 +0,0 @@
-- DB update 2024_09_09_00 -> 2024_09_11_00
UPDATE `smart_scripts`
SET `event_param4` = 6000
WHERE `entryorguid` = 22945
AND `source_type` = 0
AND `id` = 0;
@@ -1,15 +0,0 @@
-- DB update 2024_09_11_00 -> 2024_09_11_01
-- Missing spell (Cleave) for Bladespire Brute
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 19995) AND (`source_type` = 0) AND (`id` IN (0));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(19995, 0, 0, 0, 0, 0, 100, 0, 7000, 12000, 8000, 30000, 0, 0, 11, 15496, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Bladespire Brute - In Combat - Cast Cleave');
-- Missing spell (Knockdown) for Bloodmaul Mauler
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 19993) AND (`source_type` = 0) AND (`id` IN (4));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(19993, 0, 4, 0, 0, 0, 100, 0, 15000, 30000, 30000, 60000, 0, 0, 11, 37592, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Bloodmaul Mauler - In Combat - Cast Knockdown');
-- Missing spell (Knockdown) for Bloodmaul Taskmaster
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 22160) AND (`source_type` = 0) AND (`id` IN (5));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(22160, 0, 5, 0, 0, 0, 100, 0, 20000, 50000, 60000, 90000, 0, 0, 11, 37592, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Bloodmaul Taskmaster - In Combat - Cast Knockdown');
@@ -1277,17 +1277,17 @@ Visibility.GroupMode = 1
# Visibility.Distance.Instances
# Visibility.Distance.BGArenas
# Description: Visibility distance to see other players or gameobjects.
# Visibility on continents on retail ~100 yards. In BG/Arenas ~250.
# Visibility on continents on retail ~100 yards. In BG/Arenas ~533.
# For instances default ~170.
# Max limited by active player zone: ~ 333
# Min limit is max aggro radius (45) * Rate.Creature.Aggro
# Default: 100 - (Visibility.Distance.Continents)
# 170 - (Visibility.Distance.Instances)
# 250 - (Visibility.Distance.BGArenas)
# 533 - (Visibility.Distance.BGArenas)
Visibility.Distance.Continents = 100
Visibility.Distance.Instances = 170
Visibility.Distance.BGArenas = 250
Visibility.Distance.BGArenas = 533
#
# Visibility.ObjectSparkles
+1 -4
View File
@@ -1034,10 +1034,7 @@ void ArenaTeam::CreateTempArenaTeam(std::vector<Player*> playerList, uint8 type,
{
auto playerCountInTeam = static_cast<uint32>(playerList.size());
const auto standardArenaType = { ARENA_TYPE_2v2, ARENA_TYPE_3v3, ARENA_TYPE_5v5 };
bool isStandardArenaType = std::find(std::begin(standardArenaType), std::end(standardArenaType), type) != std::end(standardArenaType);
if (isStandardArenaType)
ASSERT(playerCountInTeam == GetReqPlayersForType(type));
ASSERT(playerCountInTeam == GetReqPlayersForType(type));
// Generate new arena team id
TeamId = sArenaTeamMgr->GenerateTempArenaTeamId();
@@ -611,8 +611,6 @@ inline void Battleground::_ProcessJoin(uint32 diff)
}
m_ToBeTeleported.clear();
}
sScriptMgr->OnArenaStart(this);
}
else
{
@@ -773,10 +773,6 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 diff, BattlegroundTypeId
sScriptMgr->OnQueueUpdate(this, diff, bgTypeId, bracket_id, arenaType, isRated, arenaRating);
if (!sScriptMgr->OnQueueUpdateValidity(this, diff, bgTypeId, bracket_id, arenaType, isRated, arenaRating)) {
return;
}
m_SelectionPools[TEAM_ALLIANCE].Init();
m_SelectionPools[TEAM_HORDE].Init();
@@ -3679,7 +3679,7 @@ bool Creature::IsNotReachableAndNeedRegen() const
std::shared_ptr<time_t> const& Creature::GetLastLeashExtensionTimePtr() const
{
if (m_lastLeashExtensionTime == nullptr)
m_lastLeashExtensionTime = std::make_shared<time_t>(GameTime::GetGameTime().count());
m_lastLeashExtensionTime = std::make_shared<time_t>(time(nullptr));
return m_lastLeashExtensionTime;
}
@@ -3700,7 +3700,7 @@ time_t Creature::GetLastLeashExtensionTime() const
void Creature::UpdateLeashExtensionTime()
{
(*GetLastLeashExtensionTimePtr()) = GameTime::GetGameTime().count();
(*GetLastLeashExtensionTimePtr()) = time(nullptr);
}
bool Creature::CanPeriodicallyCallForAssistance() const
@@ -419,7 +419,7 @@ std::string Minion::GetDebugInfo() const
Guardian::Guardian(SummonPropertiesEntry const* properties, ObjectGuid owner, bool isWorldObject) : Minion(properties, owner, isWorldObject)
{
m_unitTypeMask |= UNIT_MASK_GUARDIAN;
if (properties && (properties->Type == SUMMON_TYPE_PET || properties->Category == SUMMON_CATEGORY_PET))
if (properties && properties->Type == SUMMON_TYPE_PET)
{
m_unitTypeMask |= UNIT_MASK_CONTROLABLE_GUARDIAN;
InitCharmInfo();
@@ -40,7 +40,7 @@
#define DEFAULT_VISIBILITY_DISTANCE 100.0f // default visible distance, 100 yards on continents
#define DEFAULT_VISIBILITY_INSTANCE 170.0f // default visible distance in instances, 170 yards
#define VISIBILITY_DIST_WINTERGRASP 175.0f
#define DEFAULT_VISIBILITY_BGARENAS 250.0f // default visible distance in BG/Arenas, roughly 250 yards
#define DEFAULT_VISIBILITY_BGARENAS 533.0f // default visible distance in BG/Arenas, roughly 533 yards
#define DEFAULT_WORLD_OBJECT_SIZE 0.388999998569489f // player size, also currently used (correctly?) for any non Unit world objects
#define DEFAULT_COMBAT_REACH 1.5f
+5 -35
View File
@@ -738,20 +738,6 @@ bool Unit::GetRandomContactPoint(Unit const* obj, float& x, float& y, float& z,
return true;
}
Unit* Unit::getAttackerForHelper() const
{
if (GetVictim() != nullptr)
return GetVictim();
if (!IsEngaged())
return nullptr;
if (!m_attackers.empty())
return *(m_attackers.begin());
return nullptr;
}
void Unit::UpdateInterruptMask()
{
m_interruptMask = 0;
@@ -13573,9 +13559,6 @@ void Unit::SetInCombatWith(Unit* enemy, uint32 duration)
}
}
if (Creature* pCreature = ToCreature())
pCreature->UpdateLeashExtensionTime();
SetInCombatState(false, enemy, duration);
}
@@ -17253,17 +17236,6 @@ void Unit::SetContestedPvP(Player* attackedPlayer, bool lookForNearContestedGuar
}
}
void Unit::SetCantProc(bool apply)
{
if (apply)
++m_procDeep;
else
{
ASSERT(m_procDeep);
--m_procDeep;
}
}
void Unit::AddPetAura(PetAura const* petSpell)
{
if (!IsPlayer())
@@ -20256,13 +20228,13 @@ void Unit::OutDebugInfo() const
class AuraMunchingQueue : public BasicEvent
{
public:
AuraMunchingQueue(Unit& owner, ObjectGuid targetGUID, int32 basePoints, uint32 spellId, AuraEffect* aurEff) : _owner(owner), _targetGUID(targetGUID), _basePoints(basePoints), _spellId(spellId), _aurEff(aurEff) { }
AuraMunchingQueue(Unit& owner, ObjectGuid targetGUID, int32 basePoints, uint32 spellId) : _owner(owner), _targetGUID(targetGUID), _basePoints(basePoints), _spellId(spellId) { }
bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override
{
if (_owner.IsInWorld() && _owner.FindMap())
if (Unit* target = ObjectAccessor::GetUnit(_owner, _targetGUID))
_owner.CastCustomSpell(_spellId, SPELLVALUE_BASE_POINT0, _basePoints, target, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, _aurEff, _owner.GetGUID());
_owner.CastCustomSpell(_spellId, SPELLVALUE_BASE_POINT0, _basePoints, target, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, nullptr, _owner.GetGUID());
return true;
}
@@ -20272,15 +20244,13 @@ private:
ObjectGuid _targetGUID;
int32 _basePoints;
uint32 _spellId;
AuraEffect* _aurEff;
};
void Unit::CastDelayedSpellWithPeriodicAmount(Unit* caster, uint32 spellId, AuraType auraType, int32 addAmount, uint8 effectIndex)
{
AuraEffect* aurEff = nullptr;
for (AuraEffectList::iterator i = m_modAuras[auraType].begin(); i != m_modAuras[auraType].end(); ++i)
{
aurEff = *i;
AuraEffect* aurEff = *i;
if (aurEff->GetCasterGUID() != caster->GetGUID() || aurEff->GetId() != spellId || aurEff->GetEffIndex() != effectIndex || !aurEff->GetTotalTicks())
continue;
@@ -20290,9 +20260,9 @@ void Unit::CastDelayedSpellWithPeriodicAmount(Unit* caster, uint32 spellId, Aura
// xinef: delay only for casting on different unit
if (this == caster || !sWorld->getBoolConfig(CONFIG_MUNCHING_BLIZZLIKE))
caster->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, addAmount, this, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, aurEff, caster->GetGUID());
caster->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, addAmount, this, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, nullptr, caster->GetGUID());
else
caster->m_Events.AddEvent(new AuraMunchingQueue(*caster, GetGUID(), addAmount, spellId, aurEff), caster->m_Events.CalculateQueueTime(400));
caster->m_Events.AddEvent(new AuraMunchingQueue(*caster, GetGUID(), addAmount, spellId), caster->m_Events.CalculateQueueTime(400));
}
void Unit::SendClearTarget()
+243 -259
View File
@@ -653,8 +653,6 @@ public:
~Unit() override;
void Update(uint32 time) override;
UnitAI* GetAI() { return i_AI; }
void SetAI(UnitAI* newAI) { i_AI = newAI; }
@@ -664,101 +662,20 @@ public:
void CleanupBeforeRemoveFromMap(bool finalCleanup);
void CleanupsBeforeDelete(bool finalCleanup = true) override; // used in ~Creature/~Player (or before mass creature delete to remove cross-references to already deleted units)
/*********************************************************/
/*** UNIT HELPERS ***/
/*********************************************************/
void SetUInt32Value(uint16 index, uint32 value); /// @todo: move this in Object class or move GetUInt32value here but keep consistency
void AddUnitState(uint32 f) { m_state |= f; }
[[nodiscard]] bool HasUnitState(const uint32 f) const { return (m_state & f); }
void ClearUnitState(uint32 f) { m_state &= ~f; }
[[nodiscard]] uint32 GetUnitState() const { return m_state; }
[[nodiscard]] uint32 HasUnitTypeMask(uint32 mask) const { return mask & m_unitTypeMask; }
void AddUnitTypeMask(uint32 mask) { m_unitTypeMask |= mask; }
[[nodiscard]] uint32 GetUnitTypeMask() const { return m_unitTypeMask; }
UnitFlags GetUnitFlags() const { return UnitFlags(GetUInt32Value(UNIT_FIELD_FLAGS)); }
bool HasUnitFlag(UnitFlags flags) const { return HasFlag(UNIT_FIELD_FLAGS, flags); } /// @brief UnitFlags available in UnitDefines.h
void SetUnitFlag(UnitFlags flags) { SetFlag(UNIT_FIELD_FLAGS, flags); } /// @brief UnitFlags available in UnitDefines.h
void RemoveUnitFlag(UnitFlags flags) { RemoveFlag(UNIT_FIELD_FLAGS, flags); } /// @brief Remove the Unit flag specify only
void ReplaceAllUnitFlags(UnitFlags flags) { SetUInt32Value(UNIT_FIELD_FLAGS, flags); } /// @brief Remove all UnitFlags and set new ones. UnitFlags available in UnitDefines.h
UnitFlags2 GetUnitFlags2() const { return UnitFlags2(GetUInt32Value(UNIT_FIELD_FLAGS_2)); }
bool HasUnitFlag2(UnitFlags2 flags) const { return HasFlag(UNIT_FIELD_FLAGS_2, flags); }
void SetUnitFlag2(UnitFlags2 flags) { SetFlag(UNIT_FIELD_FLAGS_2, flags); }
void RemoveUnitFlag2(UnitFlags2 flags) { RemoveFlag(UNIT_FIELD_FLAGS_2, flags); }
void ReplaceAllUnitFlags2(UnitFlags2 flags) { SetUInt32Value(UNIT_FIELD_FLAGS_2, flags); }
NPCFlags GetNpcFlags() const { return NPCFlags(GetUInt32Value(UNIT_NPC_FLAGS)); }
bool HasNpcFlag(NPCFlags flags) const { return HasFlag(UNIT_NPC_FLAGS, flags) != 0; }
void SetNpcFlag(NPCFlags flags) { SetFlag(UNIT_NPC_FLAGS, flags); }
void RemoveNpcFlag(NPCFlags flags) { RemoveFlag(UNIT_NPC_FLAGS, flags); }
void ReplaceAllNpcFlags(NPCFlags flags) { SetUInt32Value(UNIT_NPC_FLAGS, flags); }
uint32 GetDynamicFlags() const override { return GetUInt32Value(UNIT_DYNAMIC_FLAGS); }
void ReplaceAllDynamicFlags(uint32 flag) override { SetUInt32Value(UNIT_DYNAMIC_FLAGS, flag); }
/*********************************************************/
/*** UNIT TYPES, CLASSES, RACES... ***/
/*********************************************************/
DiminishingLevels GetDiminishing(DiminishingGroup group);
void IncrDiminishing(DiminishingGroup group);
float ApplyDiminishingToDuration(DiminishingGroup group, int32& duration, Unit* caster, DiminishingLevels Level, int32 limitduration);
void ApplyDiminishingAura(DiminishingGroup group, bool apply);
void ClearDiminishings() { m_Diminishing.clear(); }
// Unit type methods
[[nodiscard]] bool IsSummon() const { return m_unitTypeMask & UNIT_MASK_SUMMON; }
[[nodiscard]] bool IsGuardian() const { return m_unitTypeMask & UNIT_MASK_GUARDIAN; }
[[nodiscard]] bool IsControllableGuardian() const { return m_unitTypeMask & UNIT_MASK_CONTROLABLE_GUARDIAN; }
[[nodiscard]] bool IsPet() const { return m_unitTypeMask & UNIT_MASK_PET; }
[[nodiscard]] bool IsHunterPet() const { return m_unitTypeMask & UNIT_MASK_HUNTER_PET; }
[[nodiscard]] bool IsTotem() const { return m_unitTypeMask & UNIT_MASK_TOTEM; }
[[nodiscard]] bool IsVehicle() const { return m_unitTypeMask & UNIT_MASK_VEHICLE; }
// target dependent range checks
float GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const;
float GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const;
// NPC type methods
[[nodiscard]] bool IsVendor() const { return HasNpcFlag(UNIT_NPC_FLAG_VENDOR); }
[[nodiscard]] bool IsTrainer() const { return HasNpcFlag(UNIT_NPC_FLAG_TRAINER); }
[[nodiscard]] bool IsQuestGiver() const { return HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); }
[[nodiscard]] bool IsGossip() const { return HasNpcFlag(UNIT_NPC_FLAG_GOSSIP); }
[[nodiscard]] bool IsTaxi() const { return HasNpcFlag(UNIT_NPC_FLAG_FLIGHTMASTER); }
[[nodiscard]] bool IsGuildMaster() const { return HasNpcFlag(UNIT_NPC_FLAG_PETITIONER); }
[[nodiscard]] bool IsBattleMaster() const { return HasNpcFlag(UNIT_NPC_FLAG_BATTLEMASTER); }
[[nodiscard]] bool IsBanker() const { return HasNpcFlag(UNIT_NPC_FLAG_BANKER); }
[[nodiscard]] bool IsInnkeeper() const { return HasNpcFlag(UNIT_NPC_FLAG_INNKEEPER); }
[[nodiscard]] bool IsSpiritHealer() const { return HasNpcFlag(UNIT_NPC_FLAG_SPIRITHEALER); }
[[nodiscard]] bool IsSpiritGuide() const { return HasNpcFlag(UNIT_NPC_FLAG_SPIRITGUIDE); }
[[nodiscard]] bool IsTabardDesigner() const { return HasNpcFlag(UNIT_NPC_FLAG_TABARDDESIGNER); }
[[nodiscard]] bool IsAuctioner() const { return HasNpcFlag(UNIT_NPC_FLAG_AUCTIONEER); }
[[nodiscard]] bool IsArmorer() const { return HasNpcFlag(UNIT_NPC_FLAG_REPAIR); }
[[nodiscard]] bool IsServiceProvider() const
{
return HasNpcFlag(UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_FLIGHTMASTER |
UNIT_NPC_FLAG_PETITIONER | UNIT_NPC_FLAG_BATTLEMASTER | UNIT_NPC_FLAG_BANKER |
UNIT_NPC_FLAG_INNKEEPER | UNIT_NPC_FLAG_SPIRITHEALER |
UNIT_NPC_FLAG_SPIRITGUIDE | UNIT_NPC_FLAG_TABARDDESIGNER | UNIT_NPC_FLAG_AUCTIONEER);
}
[[nodiscard]] bool IsSpiritService() const { return HasNpcFlag(UNIT_NPC_FLAG_SPIRITHEALER | UNIT_NPC_FLAG_SPIRITGUIDE); }
// Race methods
[[nodiscard]] uint8 getRace(bool original = false) const;
void setRace(uint8 race);
[[nodiscard]] uint32 getRaceMask() const { return 1 << (getRace(true) - 1); }
[[nodiscard]] DisplayRace GetDisplayRaceFromModelId(uint32 modelId) const;
[[nodiscard]] DisplayRace GetDisplayRace() const { return GetDisplayRaceFromModelId(GetDisplayId()); };
// Class methods
[[nodiscard]] uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, 1); }
[[nodiscard]] virtual bool IsClass(Classes unitClass, [[maybe_unused]] ClassContext context = CLASS_CONTEXT_NONE) const { return (getClass() == unitClass); }
[[nodiscard]] uint32 getClassMask() const { return 1 << (getClass() - 1); }
// Gender methods
[[nodiscard]] uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, 2); }
// Factions methods
[[nodiscard]] uint32 GetFaction() const { return GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); }
void SetFaction(uint32 faction);
[[nodiscard]] FactionTemplateEntry const* GetFactionTemplateEntry() const;
/*********************************************************/
/*** METHODS RELATED TO COMBATS ***/
/*********************************************************/
void Update(uint32 time) override;
void setAttackTimer(WeaponAttackType type, int32 time) { m_attackTimer[type] = time; } /// @todo - Look to convert to std::chrono
void resetAttackTimer(WeaponAttackType type = BASE_ATTACK);
@@ -775,9 +692,30 @@ public:
float GetMeleeRange(Unit const* target) const;
virtual SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = BASE_ATTACK, uint8 damageIndex = 0) const = 0;
bool GetRandomContactPoint(Unit const* target, float& x, float& y, float& z, bool force = false) const;
uint32 m_extraAttacks;
bool m_canDualWield;
[[nodiscard]] Unit* getAttackerForHelper() const; // If someone wants to help, who to give them
void _addAttacker(Unit* pAttacker) // must be called only from Unit::Attack(Unit*)
{
m_attackers.insert(pAttacker);
}
void _removeAttacker(Unit* pAttacker) // must be called only from Unit::AttackStop()
{
m_attackers.erase(pAttacker);
}
[[nodiscard]] Unit* getAttackerForHelper() const // If someone wants to help, who to give them
{
if (GetVictim() != nullptr)
return GetVictim();
if (!IsEngaged())
return nullptr;
if (!m_attackers.empty())
return *(m_attackers.begin());
return nullptr;
}
bool Attack(Unit* victim, bool meleeAttack);
void CastStop(uint32 except_spellid = 0, bool withInstant = true);
@@ -797,64 +735,39 @@ public:
void SendMeleeAttackStop(Unit* victim = nullptr);
void SendMeleeAttackStart(Unit* victim, Player* sendTo = nullptr);
[[nodiscard]] uint32 GetAttackTime(WeaponAttackType att) const
void AddUnitState(uint32 f) { m_state |= f; }
[[nodiscard]] bool HasUnitState(const uint32 f) const { return (m_state & f); }
void ClearUnitState(uint32 f) { m_state &= ~f; }
[[nodiscard]] uint32 GetUnitState() const { return m_state; }
[[nodiscard]] bool CanFreeMove() const
{
float f_BaseAttackTime = GetFloatValue(static_cast<uint16>(UNIT_FIELD_BASEATTACKTIME) + att) / m_modAttackSpeedPct[att];
return (uint32)f_BaseAttackTime;
return !HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT |
UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED) && !GetOwnerGUID();
}
void SetAttackTime(WeaponAttackType att, uint32 val) { SetFloatValue(static_cast<uint16>(UNIT_FIELD_BASEATTACKTIME) + att, val * m_modAttackSpeedPct[att]); }
void ApplyAttackTimePercentMod(WeaponAttackType att, float val, bool apply);
void ApplyCastTimePercentMod(float val, bool apply);
[[nodiscard]] uint32 HasUnitTypeMask(uint32 mask) const { return mask & m_unitTypeMask; }
void AddUnitTypeMask(uint32 mask) { m_unitTypeMask |= mask; }
[[nodiscard]] uint32 GetUnitTypeMask() const { return m_unitTypeMask; }
[[nodiscard]] bool IsSummon() const { return m_unitTypeMask & UNIT_MASK_SUMMON; }
[[nodiscard]] bool IsGuardian() const { return m_unitTypeMask & UNIT_MASK_GUARDIAN; }
[[nodiscard]] bool IsControllableGuardian() const { return m_unitTypeMask & UNIT_MASK_CONTROLABLE_GUARDIAN; }
[[nodiscard]] bool IsPet() const { return m_unitTypeMask & UNIT_MASK_PET; }
[[nodiscard]] bool IsHunterPet() const { return m_unitTypeMask & UNIT_MASK_HUNTER_PET; }
[[nodiscard]] bool IsTotem() const { return m_unitTypeMask & UNIT_MASK_TOTEM; }
[[nodiscard]] bool IsVehicle() const { return m_unitTypeMask & UNIT_MASK_VEHICLE; }
void SetImmuneToAll(bool apply, bool keepCombat = false) { SetImmuneToPC(apply, keepCombat); SetImmuneToNPC(apply, keepCombat); }
bool IsImmuneToAll() const { return IsImmuneToPC() && IsImmuneToNPC(); }
void SetImmuneToPC(bool apply, bool keepCombat = false);
bool IsImmuneToPC() const { return HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); }
void SetImmuneToNPC(bool apply, bool keepCombat = false);
bool IsImmuneToNPC() const { return HasUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC); }
bool IsEngaged() const { return IsInCombat(); }
bool IsEngagedBy(Unit const* who) const { return IsInCombatWith(who); }
[[nodiscard]] bool IsInCombat() const { return HasUnitFlag(UNIT_FLAG_IN_COMBAT); }
bool IsInCombatWith(Unit const* who) const;
[[nodiscard]] bool IsPetInCombat() const { return HasUnitFlag(UNIT_FLAG_PET_IN_COMBAT); }
void CombatStart(Unit* target, bool initialAggro = true);
void CombatStartOnCast(Unit* target, bool initialAggro = true, uint32 duration = 0);
void SetInCombatState(bool PvP, Unit* enemy = nullptr, uint32 duration = 0);
void SetInCombatWith(Unit* enemy, uint32 duration = 0);
void ClearInCombat();
void ClearInPetCombat();
[[nodiscard]] uint32 GetCombatTimer() const { return m_CombatTimer; }
void SetCombatTimer(uint32 timer) { m_CombatTimer = timer; }
// Threat related methods
[[nodiscard]] bool CanHaveThreatList() const;
void AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr);
float ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL);
void TauntApply(Unit* victim);
void TauntFadeOut(Unit* taunter);
ThreatMgr& GetThreatMgr() { return m_ThreatMgr; }
ThreatMgr const& GetThreatMgr() const { return m_ThreatMgr; }
void addHatedBy(HostileReference* pHostileReference) { m_HostileRefMgr.insertFirst(pHostileReference); };
void removeHatedBy(HostileReference* /*pHostileReference*/) { /* nothing to do yet */ }
HostileRefMgr& getHostileRefMgr() { return m_HostileRefMgr; }
// Redirect Threat
void SetRedirectThreat(ObjectGuid guid, uint32 pct) { _redirectThreatInfo.Set(guid, pct); }
void ResetRedirectThreat() { SetRedirectThreat(ObjectGuid::Empty, 0); }
void ModifyRedirectThreat(int32 amount) { _redirectThreatInfo.ModifyThreatPct(amount); }
uint32 GetRedirectThreatPercent() { return _redirectThreatInfo.GetThreatPct(); }
[[nodiscard]] Unit* GetRedirectThreatTarget() const;
/*********************************************************/
/*** METHODS RELATED TO STATS ***/
/*********************************************************/
[[nodiscard]] uint8 GetLevel() const { return uint8(GetUInt32Value(UNIT_FIELD_LEVEL)); }
uint8 getLevelForTarget(WorldObject const* /*target*/) const override { return GetLevel(); }
void SetLevel(uint8 lvl, bool showLevelChange = true);
[[nodiscard]] uint8 getRace(bool original = false) const;
void setRace(uint8 race);
[[nodiscard]] uint32 getRaceMask() const { return 1 << (getRace(true) - 1); }
[[nodiscard]] uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, 1); }
[[nodiscard]] virtual bool IsClass(Classes unitClass, [[maybe_unused]] ClassContext context = CLASS_CONTEXT_NONE) const { return (getClass() == unitClass); }
[[nodiscard]] uint32 getClassMask() const { return 1 << (getClass() - 1); }
[[nodiscard]] uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, 2); }
[[nodiscard]] DisplayRace GetDisplayRaceFromModelId(uint32 modelId) const;
[[nodiscard]] DisplayRace GetDisplayRace() const { return GetDisplayRaceFromModelId(GetDisplayId()); };
[[nodiscard]] float GetStat(Stats stat) const { return float(GetUInt32Value(static_cast<uint16>(UNIT_FIELD_STAT0) + stat)); }
void SetStat(Stats stat, int32 val) { SetStatInt32Value(static_cast<uint16>(UNIT_FIELD_STAT0) + stat, val); }
@@ -892,58 +805,42 @@ public:
[[nodiscard]] uint32 GetMaxPower(Powers power) const { return GetUInt32Value(static_cast<uint16>(UNIT_FIELD_MAXPOWER1) + power); }
void SetPower(Powers power, uint32 val, bool withPowerUpdate = true, bool fromRegenerate = false);
void SetMaxPower(Powers power, uint32 val);
// returns the change in power
int32 ModifyPower(Powers power, int32 val, bool withPowerUpdate = true);
int32 ModifyPowerPct(Powers power, float pct, bool apply = true);
// stat system
bool HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply);
void SetModifierValue(UnitMods unitMod, UnitModifierType modifierType, float value) { m_auraModifiersGroup[unitMod][modifierType] = value; }
[[nodiscard]] float GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const;
[[nodiscard]] float GetTotalStatValue(Stats stat, float additionalValue = 0.0f) const;
[[nodiscard]] float GetTotalAuraModValue(UnitMods unitMod) const;
[[nodiscard]] SpellSchools GetSpellSchoolByAuraGroup(UnitMods unitMod) const;
[[nodiscard]] Stats GetStatByAuraGroup(UnitMods unitMod) const;
[[nodiscard]] Powers GetPowerTypeByAuraGroup(UnitMods unitMod) const;
[[nodiscard]] bool CanModifyStats() const { return m_canModifyStats; }
void SetCanModifyStats(bool modifyStats) { m_canModifyStats = modifyStats; }
virtual bool UpdateStats(Stats stat) = 0;
virtual bool UpdateAllStats() = 0;
virtual void UpdateResistances(uint32 school) = 0;
virtual void UpdateAllResistances();
virtual void UpdateArmor() = 0;
virtual void UpdateMaxHealth() = 0;
virtual void UpdateMaxPower(Powers power) = 0;
virtual void UpdateAttackPowerAndDamage(bool ranged = false) = 0;
virtual void UpdateDamagePhysical(WeaponAttackType attType);
float GetTotalAttackPowerValue(WeaponAttackType attType, Unit* pVictim = nullptr) const;
[[nodiscard]] float GetWeaponDamageRange(WeaponAttackType attType, WeaponDamageRange type, uint8 damageIndex = 0) const;
void SetBaseWeaponDamage(WeaponAttackType attType, WeaponDamageRange damageRange, float value, uint8 damageIndex = 0) { m_weaponDamage[attType][damageRange][damageIndex] = value; }
virtual void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex = 0) = 0;
uint32 CalculateDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, uint8 itemDamagesMask = 0);
float GetAPMultiplier(WeaponAttackType attType, bool normalized);
//------------------------------------------------------//
DiminishingLevels GetDiminishing(DiminishingGroup group);
void IncrDiminishing(DiminishingGroup group);
float ApplyDiminishingToDuration(DiminishingGroup group, int32& duration, Unit* caster, DiminishingLevels Level, int32 limitduration);
void ApplyDiminishingAura(DiminishingGroup group, bool apply);
void ClearDiminishings() { m_Diminishing.clear(); }
// target dependent range checks
float GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const;
float GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const;
[[nodiscard]] bool CanFreeMove() const
[[nodiscard]] uint32 GetAttackTime(WeaponAttackType att) const
{
return !HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT |
UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED) && !GetOwnerGUID();
float f_BaseAttackTime = GetFloatValue(static_cast<uint16>(UNIT_FIELD_BASEATTACKTIME) + att) / m_modAttackSpeedPct[att];
return (uint32)f_BaseAttackTime;
}
void SetAttackTime(WeaponAttackType att, uint32 val) { SetFloatValue(static_cast<uint16>(UNIT_FIELD_BASEATTACKTIME) + att, val * m_modAttackSpeedPct[att]); }
void ApplyAttackTimePercentMod(WeaponAttackType att, float val, bool apply);
void ApplyCastTimePercentMod(float val, bool apply);
void SetUInt32Value(uint16 index, uint32 value);
UnitFlags GetUnitFlags() const { return UnitFlags(GetUInt32Value(UNIT_FIELD_FLAGS)); }
bool HasUnitFlag(UnitFlags flags) const { return HasFlag(UNIT_FIELD_FLAGS, flags); } /// @brief UnitFlags available in UnitDefines.h
void SetUnitFlag(UnitFlags flags) { SetFlag(UNIT_FIELD_FLAGS, flags); } /// @brief UnitFlags available in UnitDefines.h
void RemoveUnitFlag(UnitFlags flags) { RemoveFlag(UNIT_FIELD_FLAGS, flags); } /// @brief Remove the Unit flag specify only
void ReplaceAllUnitFlags(UnitFlags flags) { SetUInt32Value(UNIT_FIELD_FLAGS, flags); } /// @brief Remove all UnitFlags and set new ones. UnitFlags available in UnitDefines.h
UnitFlags2 GetUnitFlags2() const { return UnitFlags2(GetUInt32Value(UNIT_FIELD_FLAGS_2)); }
bool HasUnitFlag2(UnitFlags2 flags) const { return HasFlag(UNIT_FIELD_FLAGS_2, flags); }
void SetUnitFlag2(UnitFlags2 flags) { SetFlag(UNIT_FIELD_FLAGS_2, flags); }
void RemoveUnitFlag2(UnitFlags2 flags) { RemoveFlag(UNIT_FIELD_FLAGS_2, flags); }
void ReplaceAllUnitFlags2(UnitFlags2 flags) { SetUInt32Value(UNIT_FIELD_FLAGS_2, flags); }
[[nodiscard]] SheathState GetSheath() const { return SheathState(GetByteValue(UNIT_FIELD_BYTES_2, 0)); }
virtual void SetSheath(SheathState sheathed) { SetByteValue(UNIT_FIELD_BYTES_2, 0, sheathed); }
// faction template id
[[nodiscard]] uint32 GetFaction() const { return GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); }
void SetFaction(uint32 faction);
[[nodiscard]] FactionTemplateEntry const* GetFactionTemplateEntry() const;
ReputationRank GetReactionTo(Unit const* target, bool checkOriginalFaction = false) const;
ReputationRank GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, Unit const* target) const;
static ReputationRank GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, FactionTemplateEntry const* targetFactionTemplateEntry);
@@ -1082,7 +979,6 @@ public:
return value;
}
uint32 GetUnitMeleeSkill(Unit const* target = nullptr) const { return (target ? getLevelForTarget(target) : GetLevel()) * 5; }
uint32 GetDefenseSkillValue(Unit const* target = nullptr) const;
uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = nullptr) const;
@@ -1092,10 +988,61 @@ public:
MeleeHitOutcome RollMeleeOutcomeAgainst (Unit const* victim, WeaponAttackType attType) const;
MeleeHitOutcome RollMeleeOutcomeAgainst (Unit const* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const;
NPCFlags GetNpcFlags() const { return NPCFlags(GetUInt32Value(UNIT_NPC_FLAGS)); }
bool HasNpcFlag(NPCFlags flags) const { return HasFlag(UNIT_NPC_FLAGS, flags) != 0; }
void SetNpcFlag(NPCFlags flags) { SetFlag(UNIT_NPC_FLAGS, flags); }
void RemoveNpcFlag(NPCFlags flags) { RemoveFlag(UNIT_NPC_FLAGS, flags); }
void ReplaceAllNpcFlags(NPCFlags flags) { SetUInt32Value(UNIT_NPC_FLAGS, flags); }
[[nodiscard]] bool IsVendor() const { return HasNpcFlag(UNIT_NPC_FLAG_VENDOR); }
[[nodiscard]] bool IsTrainer() const { return HasNpcFlag(UNIT_NPC_FLAG_TRAINER); }
[[nodiscard]] bool IsQuestGiver() const { return HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); }
[[nodiscard]] bool IsGossip() const { return HasNpcFlag(UNIT_NPC_FLAG_GOSSIP); }
[[nodiscard]] bool IsTaxi() const { return HasNpcFlag(UNIT_NPC_FLAG_FLIGHTMASTER); }
[[nodiscard]] bool IsGuildMaster() const { return HasNpcFlag(UNIT_NPC_FLAG_PETITIONER); }
[[nodiscard]] bool IsBattleMaster() const { return HasNpcFlag(UNIT_NPC_FLAG_BATTLEMASTER); }
[[nodiscard]] bool IsBanker() const { return HasNpcFlag(UNIT_NPC_FLAG_BANKER); }
[[nodiscard]] bool IsInnkeeper() const { return HasNpcFlag(UNIT_NPC_FLAG_INNKEEPER); }
[[nodiscard]] bool IsSpiritHealer() const { return HasNpcFlag(UNIT_NPC_FLAG_SPIRITHEALER); }
[[nodiscard]] bool IsSpiritGuide() const { return HasNpcFlag(UNIT_NPC_FLAG_SPIRITGUIDE); }
[[nodiscard]] bool IsTabardDesigner() const { return HasNpcFlag(UNIT_NPC_FLAG_TABARDDESIGNER); }
[[nodiscard]] bool IsAuctioner() const { return HasNpcFlag(UNIT_NPC_FLAG_AUCTIONEER); }
[[nodiscard]] bool IsArmorer() const { return HasNpcFlag(UNIT_NPC_FLAG_REPAIR); }
[[nodiscard]] bool IsServiceProvider() const
{
return HasNpcFlag(UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_FLIGHTMASTER |
UNIT_NPC_FLAG_PETITIONER | UNIT_NPC_FLAG_BATTLEMASTER | UNIT_NPC_FLAG_BANKER |
UNIT_NPC_FLAG_INNKEEPER | UNIT_NPC_FLAG_SPIRITHEALER |
UNIT_NPC_FLAG_SPIRITGUIDE | UNIT_NPC_FLAG_TABARDDESIGNER | UNIT_NPC_FLAG_AUCTIONEER);
}
[[nodiscard]] bool IsSpiritService() const { return HasNpcFlag(UNIT_NPC_FLAG_SPIRITHEALER | UNIT_NPC_FLAG_SPIRITGUIDE); }
[[nodiscard]] bool IsCritter() const { return GetCreatureType() == CREATURE_TYPE_CRITTER; }
[[nodiscard]] bool IsInFlight() const { return HasUnitState(UNIT_STATE_IN_FLIGHT); }
void SetImmuneToAll(bool apply, bool keepCombat = false) { SetImmuneToPC(apply, keepCombat); SetImmuneToNPC(apply, keepCombat); }
bool IsImmuneToAll() const { return IsImmuneToPC() && IsImmuneToNPC(); }
void SetImmuneToPC(bool apply, bool keepCombat = false);
bool IsImmuneToPC() const { return HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); }
void SetImmuneToNPC(bool apply, bool keepCombat = false);
bool IsImmuneToNPC() const { return HasUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC); }
bool IsEngaged() const { return IsInCombat(); }
bool IsEngagedBy(Unit const* who) const { return IsInCombatWith(who); }
[[nodiscard]] bool IsInCombat() const { return HasUnitFlag(UNIT_FLAG_IN_COMBAT); }
bool IsInCombatWith(Unit const* who) const;
[[nodiscard]] bool IsPetInCombat() const { return HasUnitFlag(UNIT_FLAG_PET_IN_COMBAT); }
void CombatStart(Unit* target, bool initialAggro = true);
void CombatStartOnCast(Unit* target, bool initialAggro = true, uint32 duration = 0);
void SetInCombatState(bool PvP, Unit* enemy = nullptr, uint32 duration = 0);
void SetInCombatWith(Unit* enemy, uint32 duration = 0);
void ClearInCombat();
void ClearInPetCombat();
[[nodiscard]] uint32 GetCombatTimer() const { return m_CombatTimer; }
void SetCombatTimer(uint32 timer) { m_CombatTimer = timer; }
[[nodiscard]] bool HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint32 familyFlags) const;
[[nodiscard]] bool virtual HasSpell(uint32 /*spellID*/) const { return false; }
[[nodiscard]] bool HasBreakableByDamageAuraType(AuraType type, uint32 excludeAura = 0) const;
@@ -1262,6 +1209,7 @@ public:
void RemoveCharmedBy(Unit* charmer);
void RestoreFaction();
ControlSet m_Controlled;
[[nodiscard]] Unit* GetFirstControlled() const;
void RemoveAllControlled(bool onDeath = false);
@@ -1282,7 +1230,7 @@ public:
void DeleteCharmInfo();
void UpdateCharmAI();
//Player* GetMoverSource() const;
SafeUnitPointer m_movedByPlayer;
SharedVisionList const& GetSharedVisionList() { return m_sharedVision; }
void AddPlayerToVision(Player* player);
void RemovePlayerFromVision(Player* player);
@@ -1462,6 +1410,9 @@ public:
[[nodiscard]] virtual bool IsMovementPreventedByCasting() const;
ObjectGuid m_SummonSlot[MAX_SUMMON_SLOT];
ObjectGuid m_ObjectSlot[MAX_GAMEOBJECT_SLOT];
[[nodiscard]] ShapeshiftForm GetShapeshiftForm() const { return ShapeshiftForm(GetByteValue(UNIT_FIELD_BYTES_2, 3)); }
void SetShapeshiftForm(ShapeshiftForm form)
{
@@ -1476,6 +1427,44 @@ public:
[[nodiscard]] bool IsInDisallowedMountForm() const;
float m_modMeleeHitChance;
float m_modRangedHitChance;
float m_modSpellHitChance;
int32 m_baseSpellCritChance;
float m_threatModifier[MAX_SPELL_SCHOOL];
float m_modAttackSpeedPct[3];
// Event handler
EventProcessor m_Events;
// stat system
bool HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply);
void SetModifierValue(UnitMods unitMod, UnitModifierType modifierType, float value) { m_auraModifiersGroup[unitMod][modifierType] = value; }
[[nodiscard]] float GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const;
[[nodiscard]] float GetTotalStatValue(Stats stat, float additionalValue = 0.0f) const;
[[nodiscard]] float GetTotalAuraModValue(UnitMods unitMod) const;
[[nodiscard]] SpellSchools GetSpellSchoolByAuraGroup(UnitMods unitMod) const;
[[nodiscard]] Stats GetStatByAuraGroup(UnitMods unitMod) const;
[[nodiscard]] Powers GetPowerTypeByAuraGroup(UnitMods unitMod) const;
[[nodiscard]] bool CanModifyStats() const { return m_canModifyStats; }
void SetCanModifyStats(bool modifyStats) { m_canModifyStats = modifyStats; }
virtual bool UpdateStats(Stats stat) = 0;
virtual bool UpdateAllStats() = 0;
virtual void UpdateResistances(uint32 school) = 0;
virtual void UpdateAllResistances();
virtual void UpdateArmor() = 0;
virtual void UpdateMaxHealth() = 0;
virtual void UpdateMaxPower(Powers power) = 0;
virtual void UpdateAttackPowerAndDamage(bool ranged = false) = 0;
virtual void UpdateDamagePhysical(WeaponAttackType attType);
float GetTotalAttackPowerValue(WeaponAttackType attType, Unit* pVictim = nullptr) const;
[[nodiscard]] float GetWeaponDamageRange(WeaponAttackType attType, WeaponDamageRange type, uint8 damageIndex = 0) const;
void SetBaseWeaponDamage(WeaponAttackType attType, WeaponDamageRange damageRange, float value, uint8 damageIndex = 0) { m_weaponDamage[attType][damageRange][damageIndex] = value; }
virtual void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex = 0) = 0;
uint32 CalculateDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, uint8 itemDamagesMask = 0);
float GetAPMultiplier(WeaponAttackType attType, bool normalized);
bool isInFrontInMap(Unit const* target, float distance, float arc = M_PI) const;
bool isInBackInMap(Unit const* target, float distance, float arc = M_PI) const;
@@ -1489,6 +1478,21 @@ public:
void SetPhaseMask(uint32 newPhaseMask, bool update) override;// overwrite WorldObject::SetPhaseMask
void UpdateObjectVisibility(bool forced = true, bool fromUpdate = false) override;
SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY];
uint32 m_lastSanctuaryTime;
// Threat related methods
[[nodiscard]] bool CanHaveThreatList() const;
void AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr);
float ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL);
void TauntApply(Unit* victim);
void TauntFadeOut(Unit* taunter);
ThreatMgr& GetThreatMgr() { return m_ThreatMgr; }
ThreatMgr const& GetThreatMgr() const { return m_ThreatMgr; }
void addHatedBy(HostileReference* pHostileReference) { m_HostileRefMgr.insertFirst(pHostileReference); };
void removeHatedBy(HostileReference* /*pHostileReference*/) { /* nothing to do yet */ }
HostileRefMgr& getHostileRefMgr() { return m_HostileRefMgr; }
VisibleAuraMap const* GetVisibleAuras() { return &m_visibleAuras; }
AuraApplication* GetVisibleAura(uint8 slot)
{
@@ -1627,7 +1631,7 @@ public:
void DisableSpline();
///-----------Combo point system-------------------
// This unit having CP on other units
// This unit having CP on other units
[[nodiscard]] uint8 GetComboPoints(Unit const* who = nullptr) const { return (who && m_comboTarget != who) ? 0 : m_comboPoints; }
[[nodiscard]] uint8 GetComboPoints(ObjectGuid const& guid) const { return (m_comboTarget && m_comboTarget->GetGUID() == guid) ? m_comboPoints : 0; }
[[nodiscard]] Unit* GetComboTarget() const { return m_comboTarget; }
@@ -1658,9 +1662,21 @@ public:
void UpdateAuraForGroup(uint8 slot);
// proc trigger system
bool CanProc() { return !m_procDeep; }
void SetCantProc(bool apply);
bool CanProc() {return !m_procDeep;}
void SetCantProc(bool apply)
{
if (apply)
++m_procDeep;
else
{
ASSERT(m_procDeep);
--m_procDeep;
}
}
// pet auras
typedef std::set<PetAura const*> PetAuraSet;
PetAuraSet m_petAuras;
void AddPetAura(PetAura const* petSpell);
void RemovePetAura(PetAura const* petSpell);
void CastPetAura(PetAura const* aura);
@@ -1669,6 +1685,14 @@ public:
[[nodiscard]] uint32 GetModelForForm(ShapeshiftForm form, uint32 spellId) const;
uint32 GetModelForTotem(PlayerTotemType totemType);
// Redirect Threat
void SetRedirectThreat(ObjectGuid guid, uint32 pct) { _redirectThreatInfo.Set(guid, pct); }
void ResetRedirectThreat() { SetRedirectThreat(ObjectGuid::Empty, 0); }
void ModifyRedirectThreat(int32 amount) { _redirectThreatInfo.ModifyThreatPct(amount); }
uint32 GetRedirectThreatPercent() { return _redirectThreatInfo.GetThreatPct(); }
[[nodiscard]] Unit* GetRedirectThreatTarget() const;
bool IsAIEnabled, NeedChangeAI;
bool CreateVehicleKit(uint32 id, uint32 creatureEntry);
void RemoveVehicleKit();
[[nodiscard]] Vehicle* GetVehicleKit()const { return m_vehicleKit; }
@@ -1680,6 +1704,9 @@ public:
/// Returns the transport this unit is on directly (if on vehicle and transport, return vehicle)
[[nodiscard]] TransportBase* GetDirectTransport() const;
bool m_ControlledByPlayer;
bool m_CreatedByPlayer;
bool HandleSpellClick(Unit* clicker, int8 seatId = -1);
void EnterVehicle(Unit* base, int8 seatId = -1);
void EnterVehicleUnattackable(Unit* base, int8 seatId = -1);
@@ -1718,10 +1745,17 @@ public:
TempSummon* ToTempSummon() { if (IsSummon()) return reinterpret_cast<TempSummon*>(this); else return nullptr; }
[[nodiscard]] const TempSummon* ToTempSummon() const { if (IsSummon()) return reinterpret_cast<const TempSummon*>(this); else return nullptr; }
// Safe mover
std::set<SafeUnitPointer*> SafeUnitPointerSet;
void AddPointedBy(SafeUnitPointer* sup) { SafeUnitPointerSet.insert(sup); }
void RemovePointedBy(SafeUnitPointer* sup) { SafeUnitPointerSet.erase(sup); }
static void HandleSafeUnitPointersOnDelete(Unit* thisUnit);
// Relocation Nofier optimization
Position m_last_notify_position;
uint32 m_last_notify_mstime;
uint16 m_delayed_unit_relocation_timer;
uint16 m_delayed_unit_ai_notify_timer;
bool bRequestForcedVisibilityUpdate;
void ExecuteDelayedUnitRelocationEvent();
void ExecuteDelayedUnitAINotifyEvent();
@@ -1747,6 +1781,9 @@ public:
void SetInstantCast(bool set) { _instantCast = set; }
[[nodiscard]] bool CanInstantCast() const { return _instantCast; }
// Movement info
Movement::MoveSpline* movespline;
virtual void Talk(std::string_view text, ChatMsg msgType, Language language, float textRange, WorldObject const* target);
virtual void Say(std::string_view text, Language language, WorldObject const* target = nullptr);
virtual void Yell(std::string_view text, Language language, WorldObject const* target = nullptr);
@@ -1775,76 +1812,18 @@ public:
[[nodiscard]] uint32 GetOldFactionId() const { return _oldFactionId; }
//----------- Public variables ----------//
uint32 m_extraAttacks;
bool m_canDualWield;
ControlSet m_Controlled;
SafeUnitPointer m_movedByPlayer;
ObjectGuid m_SummonSlot[MAX_SUMMON_SLOT];
ObjectGuid m_ObjectSlot[MAX_GAMEOBJECT_SLOT];
float m_modMeleeHitChance;
float m_modRangedHitChance;
float m_modSpellHitChance;
int32 m_baseSpellCritChance;
float m_threatModifier[MAX_SPELL_SCHOOL];
float m_modAttackSpeedPct[3];
// Event handler
EventProcessor m_Events;
SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY];
uint32 m_lastSanctuaryTime;
// pet auras
typedef std::set<PetAura const*> PetAuraSet;
PetAuraSet m_petAuras;
bool IsAIEnabled;
bool NeedChangeAI;
bool m_ControlledByPlayer;
bool m_CreatedByPlayer;
// Safe mover
std::set<SafeUnitPointer*> SafeUnitPointerSet;
// Relocation Nofier optimization
Position m_last_notify_position;
uint32 m_last_notify_mstime;
uint16 m_delayed_unit_relocation_timer;
uint16 m_delayed_unit_ai_notify_timer;
bool bRequestForcedVisibilityUpdate;
// Movement info
Movement::MoveSpline* movespline;
protected:
explicit Unit (bool isWorldObject);
void BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) override;
UnitAI* i_AI, *i_disabledAI;
void _UpdateSpells(uint32 time);
void _DeleteRemovedAuras();
void _UpdateAutoRepeatSpell();
bool IsAlwaysVisibleFor(WorldObject const* seer) const override;
bool IsAlwaysDetectableFor(WorldObject const* seer) const override;
void SetFeared(bool apply, Unit* fearedBy = nullptr, bool isFear = false);
void SetConfused(bool apply);
void SetStunned(bool apply);
void SetRooted(bool apply, bool isStun = false);
//----------- Protected variables ----------//
UnitAI* i_AI;
UnitAI* i_disabledAI;
uint8 m_realRace;
uint8 m_race;
@@ -1878,7 +1857,7 @@ protected:
AuraEffectList m_modAuras[TOTAL_AURAS];
AuraList m_scAuras; // casted singlecast auras
AuraApplicationList m_interruptableAuras; // auras which have interrupt mask applied on unit
AuraApplicationList m_interruptableAuras; // auras which have interrupt mask applied on unit
AuraStateAurasMap m_auraStateAuras; // Used for improve performance of aura state checks on aura apply/remove
uint32 m_interruptMask;
@@ -1909,10 +1888,10 @@ protected:
// xinef: apply resilience
bool m_applyResilience;
bool IsAlwaysVisibleFor(WorldObject const* seer) const override;
bool IsAlwaysDetectableFor(WorldObject const* seer) const override;
bool _instantCast;
uint32 m_rootTimes;
private:
bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent, ProcEventInfo const& eventInfo);
bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, ProcEventInfo const& eventInfo);
@@ -1932,12 +1911,15 @@ private:
void PatchValuesUpdate(ByteBuffer& valuesUpdateBuf, BuildValuesCachePosPointers& posPointers, Player* target);
void InvalidateValuesUpdateCache() { _valuesUpdateCache.clear(); }
[[nodiscard]] float processDummyAuras(float TakenTotalMod) const;
protected:
void SetFeared(bool apply, Unit* fearedBy = nullptr, bool isFear = false);
void SetConfused(bool apply);
void SetStunned(bool apply);
void SetRooted(bool apply, bool isStun = false);
void _addAttacker(Unit* pAttacker) { m_attackers.insert(pAttacker); } ///@note: Call only in Unit::Attack()
void _removeAttacker(Unit* pAttacker) { m_attackers.erase(pAttacker); } ///@note: Call only in Unit::AttackStop()
uint32 m_rootTimes;
//----------- Private variables ----------//
private:
uint32 m_state; // Even derived shouldn't modify
uint32 m_CombatTimer;
uint32 m_lastManaUse; // msecs
@@ -1961,6 +1943,8 @@ private:
uint32 _oldFactionId; ///< faction before charm
bool _isWalkingBeforeCharm; ///< Are we walking before we were charmed?
[[nodiscard]] float processDummyAuras(float TakenTotalMod) const;
uint32 _lastExtraAttackSpell;
std::unordered_map<ObjectGuid /*guid*/, uint32 /*count*/> extraAttacksTargets;
ObjectGuid _lastDamagedTargetGuid;
@@ -78,11 +78,8 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
{
owner->StopMoving();
_lastTargetPosition.reset();
if (cOwner)
{
cOwner->UpdateLeashExtensionTime();
cOwner->SetCannotReachTarget();
}
if (Creature* cOwner2 = owner->ToCreature())
cOwner2->SetCannotReachTarget();
return true;
}
@@ -131,8 +128,8 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
{
i_recalculateTravel = false;
i_path = nullptr;
if (cOwner)
cOwner->SetCannotReachTarget();
if (Creature* cOwner2 = owner->ToCreature())
cOwner2->SetCannotReachTarget();
owner->StopMoving();
owner->SetInFront(target);
MovementInform(owner);
@@ -146,25 +143,21 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
{
i_recalculateTravel = false;
i_path = nullptr;
if (cOwner)
cOwner->SetCannotReachTarget();
if (Creature* cOwner2 = owner->ToCreature())
cOwner2->SetCannotReachTarget();
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
owner->SetInFront(target);
MovementInform(owner);
}
if (owner->movespline->Finalized())
{ // Mobs should chase you infinitely if you stop and wait every few seconds.
// Mobs should chase you infinitely if you stop and wait every few seconds.
i_leashExtensionTimer.Update(time_diff);
if (i_leashExtensionTimer.Passed())
{
i_leashExtensionTimer.Reset(1500);
if (cOwner)
cOwner->UpdateLeashExtensionTime();
i_leashExtensionTimer.Reset(5000);
if (Creature* creature = owner->ToCreature())
creature->UpdateLeashExtensionTime();
}
}
else if (i_recalculateTravel)
i_leashExtensionTimer.Reset(1500);
// if the target moved, we have to consider whether to adjust
if (!_lastTargetPosition || target->GetPosition() != _lastTargetPosition.value() || mutualChase != _mutualChase || !owner->IsWithinLOSInMap(target))
@@ -39,7 +39,7 @@ class ChaseMovementGenerator : public MovementGeneratorMedium<T, ChaseMovementGe
{
public:
ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range = {}, Optional<ChaseAngle> angle = {})
: TargetedMovementGeneratorBase(target), i_leashExtensionTimer(1500), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
: TargetedMovementGeneratorBase(target), i_leashExtensionTimer(0), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
~ChaseMovementGenerator() { }
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
@@ -54,11 +54,6 @@ void ScriptMgr::OnQueueUpdate(BattlegroundQueue* queue, uint32 diff, Battlegroun
CALL_ENABLED_HOOKS(AllBattlegroundScript, ALLBATTLEGROUNDHOOK_ON_QUEUE_UPDATE, script->OnQueueUpdate(queue, diff, bgTypeId, bracket_id, arenaType, isRated, arenaRating));
}
bool ScriptMgr::OnQueueUpdateValidity(BattlegroundQueue* queue, uint32 diff, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating)
{
CALL_ENABLED_BOOLEAN_HOOKS(AllBattlegroundScript, ALLBATTLEGROUNDHOOK_ON_QUEUE_UPDATE_VALIDITY, !script->OnQueueUpdateValidity(queue, diff, bgTypeId, bracket_id, arenaType, isRated, arenaRating));
}
void ScriptMgr::OnAddGroup(BattlegroundQueue* queue, GroupQueueInfo* ginfo, uint32& index, Player* leader, Group* group, BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 matchmakerRating, uint32 arenaTeamId, uint32 opponentsArenaTeamId)
{
CALL_ENABLED_HOOKS(AllBattlegroundScript, ALLBATTLEGROUNDHOOK_ON_ADD_GROUP, script->OnAddGroup(queue, ginfo, index, leader, group, bgTypeId, bracketEntry, arenaType, isRated, isPremade, arenaRating, matchmakerRating, arenaTeamId, opponentsArenaTeamId));
@@ -30,7 +30,6 @@ enum AllBattlegroundHook
ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_BEFORE_ADD_PLAYER,
ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_REMOVE_PLAYER_AT_LEAVE,
ALLBATTLEGROUNDHOOK_ON_QUEUE_UPDATE,
ALLBATTLEGROUNDHOOK_ON_QUEUE_UPDATE_VALIDITY,
ALLBATTLEGROUNDHOOK_ON_ADD_GROUP,
ALLBATTLEGROUNDHOOK_CAN_FILL_PLAYERS_TO_BG,
ALLBATTLEGROUNDHOOK_IS_CHECK_NORMAL_MATCH,
@@ -79,8 +78,6 @@ public:
virtual void OnQueueUpdate(BattlegroundQueue* /*queue*/, uint32 /* diff */, BattlegroundTypeId /* bgTypeId */, BattlegroundBracketId /* bracket_id */, uint8 /* arenaType */, bool /* isRated */, uint32 /* arenaRating */) { }
[[nodiscard]] virtual bool OnQueueUpdateValidity(BattlegroundQueue* /*queue*/, uint32 /* diff */, BattlegroundTypeId /* bgTypeId */, BattlegroundBracketId /* bracket_id */, uint8 /* arenaType */, bool /* isRated */, uint32 /* arenaRating */) { return true; }
virtual void OnAddGroup(BattlegroundQueue* /*queue*/, GroupQueueInfo* /*ginfo*/, uint32& /*index*/, Player* /*leader*/, Group* /*group*/, BattlegroundTypeId /* bgTypeId */, PvPDifficultyEntry const* /* bracketEntry */,
uint8 /* arenaType */, bool /* isRated */, bool /* isPremade */, uint32 /* arenaRating */, uint32 /* matchmakerRating */, uint32 /* arenaTeamId */, uint32 /* opponentsArenaTeamId */) { }
@@ -39,11 +39,6 @@ bool ScriptMgr::OnBeforeArenaCheckWinConditions(Battleground* const bg)
CALL_ENABLED_BOOLEAN_HOOKS(ArenaScript, ARENAHOOK_ON_BEFORE_CHECK_WIN_CONDITION, !script->OnBeforeArenaCheckWinConditions(bg));
}
void ScriptMgr::OnArenaStart(Battleground* bg)
{
CALL_ENABLED_HOOKS(ArenaScript, ARENAHOOK_ON_ARENA_START, script->OnArenaStart(bg));
}
ArenaScript::ArenaScript(const char* name, std::vector<uint16> enabledHooks)
: ScriptObject(name, ARENAHOOK_END)
{
@@ -28,7 +28,6 @@ enum ArenaHook
ARENAHOOK_ON_GET_POINTS,
ARENAHOOK_CAN_SAVE_TO_DB,
ARENAHOOK_ON_BEFORE_CHECK_WIN_CONDITION,
ARENAHOOK_ON_ARENA_START,
ARENAHOOK_END
};
@@ -49,8 +48,6 @@ public:
[[nodiscard]] virtual bool OnBeforeArenaCheckWinConditions(Battleground* const /* bg */) { return true; }
[[nodiscard]] virtual bool CanSaveToDB(ArenaTeam* /*team*/) { return true; }
virtual void OnArenaStart(Battleground* /* bg */) { };
};
#endif
@@ -367,6 +367,16 @@ void ScriptMgr::OnPlayerJoinArena(Player* player)
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_PLAYER_JOIN_ARENA, script->OnPlayerJoinArena(player));
}
void ScriptMgr::GetCustomGetArenaTeamId(Player const* player, uint8 slot, uint32& teamID) const
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_GET_CUSTOM_GET_ARENA_TEAM_ID, script->GetCustomGetArenaTeamId(player, slot, teamID));
}
void ScriptMgr::GetCustomArenaPersonalRating(Player const* player, uint8 slot, uint32& rating) const
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_GET_CUSTOM_ARENA_PERSONAL_RATING, script->GetCustomArenaPersonalRating(player, slot, rating));
}
void ScriptMgr::OnGetMaxPersonalArenaRatingRequirement(Player const* player, uint32 minSlot, uint32& maxArenaRating) const
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_GET_MAX_PERSONAL_ARENA_RATING_REQUIREMENT, script->OnGetMaxPersonalArenaRatingRequirement(player, minSlot, maxArenaRating));
@@ -405,6 +405,12 @@ public:
// After player enters queue for Arena
virtual void OnPlayerJoinArena(Player* /*player*/) { }
//Called when trying to get a team ID of a slot > 2 (This is for custom teams created by modules)
virtual void GetCustomGetArenaTeamId(Player const* /*player*/, uint8 /*slot*/, uint32& /*teamID*/) const { }
//Called when trying to get players personal rating of an arena slot > 2 (This is for custom teams created by modules)
virtual void GetCustomArenaPersonalRating(Player const* /*player*/, uint8 /*slot*/, uint32& /*rating*/) const { }
//Called after the normal slots (0..2) for arena have been evaluated so that custom arena teams could modify it if nececasry
virtual void OnGetMaxPersonalArenaRatingRequirement(Player const* /*player*/, uint32 /*minSlot*/, uint32& /*maxArenaRating*/) const {}
+17 -2
View File
@@ -30,9 +30,24 @@ namespace
template<typename T>
inline void SCR_CLEAR()
{
for (auto const& [scriptID, script] : ScriptRegistry<T>::ScriptPointerList)
for (auto& [scriptID, script] : ScriptRegistry<T>::ScriptPointerList)
{
delete script;
try
{
if(script)
{
delete script;
script = nullptr;
}
}
catch (const std::exception& e)
{
LOG_ERROR("scripts.unloading", "Failed to unload script {} with ID: {}. Error: {}", script->GetName(), scriptID, e.what());
}
catch (...)
{
LOG_ERROR("scripts.unloading", "Failed to unload script {} with ID: {}. Unknown error occurred.", script->GetName(), scriptID);
}
}
ScriptRegistry<T>::ScriptPointerList.clear();
+2 -2
View File
@@ -391,6 +391,8 @@ public: /* PlayerScript */
void OnEquip(Player* player, Item* it, uint8 bag, uint8 slot, bool update);
void OnPlayerJoinBG(Player* player);
void OnPlayerJoinArena(Player* player);
void GetCustomGetArenaTeamId(Player const* player, uint8 slot, uint32& teamID) const;
void GetCustomArenaPersonalRating(Player const* player, uint8 slot, uint32& rating) const;
void OnGetMaxPersonalArenaRatingRequirement(Player const* player, uint32 minSlot, uint32& maxArenaRating) const;
void OnLootItem(Player* player, Item* item, uint32 count, ObjectGuid lootguid);
void OnBeforeFillQuestLootItem(Player* player, LootItem& item);
@@ -615,7 +617,6 @@ public: /* BGScript */
void OnBattlegroundBeforeAddPlayer(Battleground* bg, Player* player);
void OnBattlegroundRemovePlayerAtLeave(Battleground* bg, Player* player);
void OnQueueUpdate(BattlegroundQueue* queue, uint32 diff, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating);
bool OnQueueUpdateValidity(BattlegroundQueue* queue, uint32 diff, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating);
void OnAddGroup(BattlegroundQueue* queue, GroupQueueInfo* ginfo, uint32& index, Player* leader, Group* group, BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry,
uint8 arenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 matchmakerRating, uint32 arenaTeamId, uint32 opponentsArenaTeamId);
bool CanFillPlayersToBG(BattlegroundQueue* queue, Battleground* bg, BattlegroundBracketId bracket_id);
@@ -679,7 +680,6 @@ public: /* ArenaScript */
void OnGetPoints(ArenaTeam* team, uint32 memberRating, float& points);
bool CanSaveToDB(ArenaTeam* team);
bool OnBeforeArenaCheckWinConditions(Battleground* const bg);
void OnArenaStart(Battleground* const bg);
public: /* MiscScript */
+1 -2
View File
@@ -2931,8 +2931,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
{
if (missInfo != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(effectUnit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)))
{
if (!m_triggeredByAuraSpell.spellInfo || (!(m_triggeredByAuraSpell.spellInfo->Effects[m_triggeredByAuraSpell.effectIndex].TriggerSpell == m_spellInfo->Id) && !(m_triggeredByAuraSpell.spellInfo->IsAuraEffectEqual(m_spellInfo))))
m_caster->CombatStart(effectUnit, !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_SUPRESS_TARGET_PROCS));
m_caster->CombatStart(effectUnit, !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_SUPRESS_TARGET_PROCS));
// Patch 3.0.8: All player spells which cause a creature to become aggressive to you will now also immediately cause the creature to be tapped.
if (effectUnit->IsInCombatWith(m_caster))
+17
View File
@@ -6064,6 +6064,23 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const*
ExecuteLogEffectSummonObject(i, summon);
}
// Summon infernal, cast enslave demon
// xinef: have to do it here because in Pet init stats infernal is not in world, imo this should be changed...
if (summon)
{
switch (m_spellInfo->Id)
{
// Inferno, Warlock summon spell
case 1122:
caster->AddAura(61191, summon);
break;
// Ritual of Doom, Warlock summon spell
case 60478:
caster->AddAura(SPELL_RITUAL_ENSLAVEMENT, summon);
break;
}
}
}
void Spell::EffectRenamePet(SpellEffIndex /*effIndex*/)
@@ -3954,6 +3954,12 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->AttributesEx7 |= SPELL_ATTR7_CAN_CAUSE_INTERRUPT;
});
// Ritual of Summoning
ApplySpellFix({ 61994 }, [](SpellInfo* spellInfo)
{
spellInfo->ManaCostPercentage = 0; // Clicking on Warlock Summoning portal should not require mana
});
// Shadowmeld
ApplySpellFix({ 58984 }, [](SpellInfo* spellInfo)
{
@@ -4816,21 +4822,6 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->AttributesEx3 |= SPELL_ATTR3_SUPRESS_CASTER_PROCS;
});
// Conjure Refreshment Table (Rank 1, Rank 2)
ApplySpellFix({ 43985, 58661 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_DEST_CASTER_FRONT);
spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_5_YARDS);
});
ApplySpellFix({
698, // Ritual of Summoning (portal for clicking)
61993 // Ritual of Summoning (summons the closet)
}, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_3_YARDS);
});
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
SpellInfo* spellInfo = mSpellInfoMap[i];
@@ -94,7 +94,7 @@ public:
_conflagrateThreat = me->GetThreatMgr().GetThreat(me->GetVictim());
me->GetThreatMgr().ModifyThreatByPercent(target, -100);
}
events.ScheduleEvent(EVENT_CONFLAGRATION, 18s, 25s);
events.ScheduleEvent(EVENT_CONFLAGRATION, 10s, 13s);
events.ScheduleEvent(EVENT_CHECK_CONFLAGRATION_TARGET, 10s);
break;
case EVENT_THUNDERCLAP:
@@ -89,7 +89,6 @@ struct boss_nalorakk : public BossAI
{
boss_nalorakk(Creature* creature) : BossAI(creature, DATA_NALORAKKEVENT)
{
_phase = PHASE_SEND_GUARDS_1;
_ranIntro = false;
_active = true;
creature->SetReactState(REACT_PASSIVE);
@@ -65,110 +65,127 @@ enum Misc
GROUP_DELAY = 1
};
struct boss_gurtogg_bloodboil : public BossAI
class boss_gurtogg_bloodboil : public CreatureScript
{
boss_gurtogg_bloodboil(Creature* creature) : BossAI(creature, DATA_GURTOGG_BLOODBOIL), _recentlySpoken(false) { }
public:
boss_gurtogg_bloodboil() : CreatureScript("boss_gurtogg_bloodboil") { }
void Reset() override
CreatureAI* GetAI(Creature* creature) const override
{
BossAI::Reset();
_recentlySpoken = false;
return GetBlackTempleAI<boss_gurtogg_bloodboilAI>(creature);
}
void JustEngagedWith(Unit* who) override
struct boss_gurtogg_bloodboilAI : public BossAI
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
boss_gurtogg_bloodboilAI(Creature* creature) : BossAI(creature, DATA_GURTOGG_BLOODBOIL), _recentlySpoken(false) { }
DoCastSelf(SPELL_ACIDIC_WOUND, true);
ScheduleTimedEvent(10s, [&] {
if (!me->HasAura(SPELL_FEL_RAGE_SELF))
me->CastCustomSpell(SPELL_BLOODBOIL, SPELLVALUE_MAX_TARGETS, 5, me, false);
}, 10s);
ScheduleTimedEvent(38s, [&] {
DoCastVictim(me->HasAura(SPELL_FEL_RAGE_SELF) ? SPELL_FEL_ACID_BREATH2 : SPELL_FEL_ACID_BREATH1);
}, 30s);
ScheduleTimedEvent(14s, [&] {
DoCastVictim(me->HasAura(SPELL_FEL_RAGE_SELF) ? SPELL_EJECT2 : SPELL_EJECT1);
}, 20s);
ScheduleTimedEvent(5s, [&] {
DoCastVictim(me->HasAura(SPELL_FEL_RAGE_SELF) ? SPELL_ARCING_SMASH2 : SPELL_ARCING_SMASH1);
}, 15s);
ScheduleTimedEvent(1min, [&] {
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 40.0f, true))
{
me->RemoveAurasByType(SPELL_AURA_MOD_TAUNT);
DoCastSelf(SPELL_FEL_RAGE_SELF, true);
DoCast(target, SPELL_FEL_RAGE_TARGET, true);
DoCast(target, SPELL_FEL_RAGE_2, true);
DoCast(target, SPELL_FEL_RAGE_3, true);
DoCast(target, SPELL_FEL_RAGE_SIZE, true);
target->CastSpell(me, SPELL_TAUNT_GURTOGG, true);
DoCast(target, SPELL_FEL_GEYSER_SUMMON, true);
DoCastSelf(SPELL_FEL_GEYSER_STUN, true);
DoCastSelf(SPELL_INSIGNIFICANCE, true);
me->m_Events.AddEventAtOffset([&] {
DoCastVictim(SPELL_CHARGE);
}, 2s);
scheduler.DelayGroup(GROUP_DELAY, 30s);
}
}, 90s);
ScheduleUniqueTimedEvent(10min, [&] {
Talk(SAY_ENRAGE);
DoCastSelf(SPELL_BERSERK, true);
}, EVENT_SPELL_BERSERK);
scheduler.Schedule(28s, [this](TaskContext context) {
context.SetGroup(GROUP_DELAY);
DoCastVictim(SPELL_BEWILDERING_STRIKE);
context.Repeat(30s);
});
}
bool CanAIAttack(Unit const* who) const override
{
return !who->IsImmunedToDamage(SPELL_SCHOOL_MASK_ALL);
}
void KilledUnit(Unit* /*victim*/) override
{
if (!_recentlySpoken)
void Reset() override
{
Talk(SAY_SLAY);
me->m_Events.AddEventAtOffset([&] {
_recentlySpoken = false;
}, 6s);
BossAI::Reset();
_recentlySpoken = false;
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->CastSpell(summon, SPELL_FEL_GEYSER_DAMAGE, false);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
}
DoCastSelf(SPELL_ACIDIC_WOUND, true);
bool CheckEvadeIfOutOfCombatArea() const override
{
return me->GetHomePosition().GetExactDist2d(me) > 105.0f;
}
ScheduleTimedEvent(10s, [&] {
me->CastCustomSpell(SPELL_BLOODBOIL, SPELLVALUE_MAX_TARGETS, 5, me, false);
}, 10s);
private:
bool _recentlySpoken;
ScheduleTimedEvent(38s, [&] {
DoCastVictim(me->HasAura(SPELL_FEL_RAGE_SELF) ? SPELL_FEL_ACID_BREATH2 : SPELL_FEL_ACID_BREATH1);
}, 30s);
ScheduleTimedEvent(14s, [&] {
DoCastVictim(me->HasAura(SPELL_FEL_RAGE_SELF) ? SPELL_EJECT2 : SPELL_EJECT1);
}, 20s);
ScheduleTimedEvent(5s, [&] {
DoCastVictim(me->HasAura(SPELL_FEL_RAGE_SELF) ? SPELL_ARCING_SMASH2 : SPELL_ARCING_SMASH1);
}, 15s);
ScheduleTimedEvent(1min, [&] {
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 40.0f, true))
{
me->RemoveAurasByType(SPELL_AURA_MOD_TAUNT);
DoCastSelf(SPELL_FEL_RAGE_SELF, true);
DoCast(target, SPELL_FEL_RAGE_TARGET, true);
DoCast(target, SPELL_FEL_RAGE_2, true);
DoCast(target, SPELL_FEL_RAGE_3, true);
DoCast(target, SPELL_FEL_RAGE_SIZE, true);
target->CastSpell(me, SPELL_TAUNT_GURTOGG, true);
DoCast(target, SPELL_FEL_GEYSER_SUMMON, true);
DoCastSelf(SPELL_FEL_GEYSER_STUN, true);
DoCastSelf(SPELL_INSIGNIFICANCE, true);
me->m_Events.AddEventAtOffset([&] {
DoCastVictim(SPELL_CHARGE);
}, 2s);
scheduler.DelayGroup(GROUP_DELAY, 30s);
}
}, 90s);
ScheduleUniqueTimedEvent(10min, [&] {
Talk(SAY_ENRAGE);
DoCastSelf(SPELL_BERSERK, true);
}, EVENT_SPELL_BERSERK);
scheduler.Schedule(28s, [this](TaskContext context) {
context.SetGroup(GROUP_DELAY);
DoCastVictim(SPELL_BEWILDERING_STRIKE);
context.Repeat(30s);
});
}
void KilledUnit(Unit* /*victim*/) override
{
if (!_recentlySpoken)
{
Talk(SAY_SLAY);
me->m_Events.AddEventAtOffset([&] {
_recentlySpoken = false;
}, 6s);
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->CastSpell(summon, SPELL_FEL_GEYSER_DAMAGE, false);
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
}
bool CheckEvadeIfOutOfCombatArea() const override
{
return me->GetHomePosition().GetExactDist2d(me) > 105.0f;
}
private:
bool _recentlySpoken;
};
};
class spell_gurtogg_bloodboil : public SpellScript
@@ -214,7 +231,7 @@ class spell_gurtogg_eject : public SpellScript
void AddSC_boss_gurtogg_bloodboil()
{
RegisterBlackTempleCreatureAI(boss_gurtogg_bloodboil);
new boss_gurtogg_bloodboil();
RegisterSpellScript(spell_gurtogg_bloodboil);
RegisterSpellScript(spell_gurtogg_eject);
}
@@ -1209,10 +1209,7 @@ enum WarbladeTear
struct npc_blade_of_azzinoth : public ScriptedAI
{
npc_blade_of_azzinoth(Creature* creature) : ScriptedAI(creature)
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
}
npc_blade_of_azzinoth(Creature* creature) : ScriptedAI(creature) { }
void IsSummonedBy(WorldObject* /*summoner*/) override
{
@@ -1260,13 +1257,12 @@ enum FlameAzzinoth
struct npc_flame_of_azzinoth : public ScriptedAI
{
npc_flame_of_azzinoth(Creature* creature) : ScriptedAI(creature) { }
npc_flame_of_azzinoth(Creature* creature) : ScriptedAI(creature), _bladeSummoner(nullptr) { }
void IsSummonedBy(WorldObject* /*summoner*/) override
{
// Flame is set to be Illidan's summon, so we check for nearest blade
if (Creature* _blade = me->FindNearestCreature(NPC_BLADE_OF_AZZINOTH, 15.0f))
_bladeGUID = _blade->GetGUID();
_bladeSummoner = me->FindNearestCreature(NPC_BLADE_OF_AZZINOTH, 15.0f);
me->SetCorpseDelay(2);
me->SetReactState(REACT_DEFENSIVE);
@@ -1289,8 +1285,8 @@ struct npc_flame_of_azzinoth : public ScriptedAI
void JustEngagedWith(Unit* /*who*/) override
{
ScheduleTimedEvent(10s, [&] {
if (Creature* _blade = ObjectAccessor::GetCreature(*me, _bladeGUID))
if (Unit* target = _blade->AI()->SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true))
if (_bladeSummoner)
if (Unit* target = _bladeSummoner->AI()->SelectTarget(SelectTargetMethod::Random, 0, 30.f, true))
DoCast(target, SPELL_CHARGE);
}, 5s, 20s);
@@ -1305,7 +1301,7 @@ struct npc_flame_of_azzinoth : public ScriptedAI
}
private:
ObjectGuid _bladeGUID;
Creature* _bladeSummoner;
};
class spell_illidan_draw_soul : public SpellScript
@@ -262,7 +262,7 @@ struct boss_illidari_council_memberAI : public ScriptedAI
if (events.GetNextEventTime(EVENT_KILL_TALK) == 0)
{
Talk(SAY_COUNCIL_SLAY);
events.ScheduleEvent(EVENT_KILL_TALK, 6s);
events.ScheduleEvent(EVENT_KILL_TALK, 6000);
}
}
@@ -303,12 +303,12 @@ struct boss_gathios_the_shatterer : public boss_illidari_council_memberAI
void JustEngagedWith(Unit* who) override
{
boss_illidari_council_memberAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_SPELL_BLESSING, 10s);
events.ScheduleEvent(EVENT_SPELL_AURA, 0s);
events.ScheduleEvent(EVENT_SPELL_SEAL, 2s);
events.ScheduleEvent(EVENT_SPELL_HAMMER_OF_JUSTICE, 6s);
events.ScheduleEvent(EVENT_SPELL_JUDGEMENT, 15s);
events.ScheduleEvent(EVENT_SPELL_CONSECRATION, 4s);
events.ScheduleEvent(EVENT_SPELL_BLESSING, 10000);
events.ScheduleEvent(EVENT_SPELL_AURA, 0);
events.ScheduleEvent(EVENT_SPELL_SEAL, 2000);
events.ScheduleEvent(EVENT_SPELL_HAMMER_OF_JUSTICE, 6000);
events.ScheduleEvent(EVENT_SPELL_JUDGEMENT, 8000);
events.ScheduleEvent(EVENT_SPELL_CONSECRATION, 4000);
}
void UpdateAI(uint32 diff) override
@@ -328,18 +328,18 @@ struct boss_gathios_the_shatterer : public boss_illidari_council_memberAI
me->CastSpell(member, _toggleBlessing ? SPELL_BLESSING_OF_PROTECTION : SPELL_BLESSING_OF_SPELL_WARDING);
_toggleBlessing = !_toggleBlessing;
}
events.ScheduleEvent(EVENT_SPELL_BLESSING, 15s);
events.ScheduleEvent(EVENT_SPELL_BLESSING, 15000);
break;
case EVENT_SPELL_AURA:
me->CastSpell(me, _toggleAura ? SPELL_DEVOTION_AURA : SPELL_CHROMATIC_RESISTANCE_AURA);
_toggleAura = !_toggleAura;
events.ScheduleEvent(EVENT_SPELL_AURA, 60s);
events.ScheduleEvent(EVENT_SPELL_AURA, 60000);
break;
case EVENT_SPELL_CONSECRATION:
if (roll_chance_i(50))
Talk(SAY_COUNCIL_SPECIAL);
me->CastSpell(me, SPELL_CONSECRATION, false);
events.ScheduleEvent(EVENT_SPELL_AURA, 30s);
events.ScheduleEvent(EVENT_SPELL_AURA, 30000);
break;
case EVENT_SPELL_HAMMER_OF_JUSTICE:
if (Unit* target = me->GetVictim())
@@ -349,16 +349,16 @@ struct boss_gathios_the_shatterer : public boss_illidari_council_memberAI
events.ScheduleEvent(EVENT_SPELL_HAMMER_OF_JUSTICE, 20s);
break;
}
events.ScheduleEvent(EVENT_SPELL_HAMMER_OF_JUSTICE, 0s);
events.ScheduleEvent(EVENT_SPELL_HAMMER_OF_JUSTICE, 0);
break;
case EVENT_SPELL_SEAL:
me->CastSpell(me, _toggleSeal ? SPELL_SEAL_OF_COMMAND : SPELL_SEAL_OF_BLOOD);
_toggleSeal = !_toggleSeal;
events.ScheduleEvent(EVENT_SPELL_SEAL, 20s);
events.ScheduleEvent(EVENT_SPELL_SEAL, 20000);
break;
case EVENT_SPELL_JUDGEMENT:
me->CastSpell(me->GetVictim(), SPELL_JUDGEMENT, false);
events.ScheduleEvent(EVENT_SPELL_JUDGEMENT, 16s, 20s);
events.ScheduleEvent(EVENT_SPELL_JUDGEMENT, 20000);
break;
}
@@ -493,10 +493,10 @@ struct boss_lady_malande : public boss_illidari_council_memberAI
void JustEngagedWith(Unit* who) override
{
boss_illidari_council_memberAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_SPELL_REFLECTIVE_SHIELD, 10s);
events.ScheduleEvent(EVENT_SPELL_CIRCLE_OF_HEALING, 20s);
events.ScheduleEvent(EVENT_SPELL_DIVINE_WRATH, 5s);
events.ScheduleEvent(EVENT_SPELL_EMPOWERED_SMITE, 15s);
events.ScheduleEvent(EVENT_SPELL_REFLECTIVE_SHIELD, 10000);
events.ScheduleEvent(EVENT_SPELL_CIRCLE_OF_HEALING, 20000);
events.ScheduleEvent(EVENT_SPELL_DIVINE_WRATH, 5000);
events.ScheduleEvent(EVENT_SPELL_EMPOWERED_SMITE, 15000);
}
void UpdateAI(uint32 diff) override
@@ -512,22 +512,22 @@ struct boss_lady_malande : public boss_illidari_council_memberAI
{
case EVENT_SPELL_CIRCLE_OF_HEALING:
me->CastSpell(me, SPELL_CIRCLE_OF_HEALING, false);
events.ScheduleEvent(EVENT_SPELL_CIRCLE_OF_HEALING, 20s);
events.ScheduleEvent(EVENT_SPELL_CIRCLE_OF_HEALING, 20000);
break;
case EVENT_SPELL_REFLECTIVE_SHIELD:
if (roll_chance_i(50))
Talk(SAY_COUNCIL_SPECIAL);
me->CastSpell(me, SPELL_REFLECTIVE_SHIELD, false);
events.ScheduleEvent(EVENT_SPELL_REFLECTIVE_SHIELD, 40s);
events.ScheduleEvent(EVENT_SPELL_REFLECTIVE_SHIELD, 40000);
break;
case EVENT_SPELL_DIVINE_WRATH:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f))
me->CastSpell(target, SPELL_DIVINE_WRATH, false);
events.ScheduleEvent(EVENT_SPELL_DIVINE_WRATH, 20s);
events.ScheduleEvent(EVENT_SPELL_DIVINE_WRATH, 20000);
break;
case EVENT_SPELL_EMPOWERED_SMITE:
me->CastSpell(me->GetVictim(), SPELL_EMPOWERED_SMITE, false);
events.ScheduleEvent(EVENT_SPELL_EMPOWERED_SMITE, 3s);
events.ScheduleEvent(EVENT_SPELL_EMPOWERED_SMITE, 3000);
break;
}
}
@@ -541,8 +541,8 @@ struct boss_veras_darkshadow : public boss_illidari_council_memberAI
{
me->SetCanDualWield(true);
boss_illidari_council_memberAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_SPELL_VANISH, 10s);
events.ScheduleEvent(EVENT_SPELL_ENRAGE, 90s);
events.ScheduleEvent(EVENT_SPELL_VANISH, 10000);
events.ScheduleEvent(EVENT_SPELL_ENRAGE, 900000);
}
void JustSummoned(Creature* summon) override
@@ -566,8 +566,8 @@ struct boss_veras_darkshadow : public boss_illidari_council_memberAI
Talk(SAY_COUNCIL_SPECIAL);
me->CastSpell(me, SPELL_DEADLY_STRIKE, false);
me->CastSpell(me, SPELL_VANISH, false);
events.ScheduleEvent(EVENT_SPELL_VANISH, 60s);
events.ScheduleEvent(EVENT_SPELL_VANISH_OUT, 29s);
events.ScheduleEvent(EVENT_SPELL_VANISH, 60000);
events.ScheduleEvent(EVENT_SPELL_VANISH_OUT, 29000);
break;
case EVENT_SPELL_VANISH_OUT:
me->CastSpell(me, SPELL_VANISH_OUT, false);
@@ -220,7 +220,6 @@ struct npc_akama_shade : public ScriptedAI
DoCastSelf(SPELL_STEALTH, true);
me->SetWalk(true);
_sayLowHealth = false;
_died = false;
scheduler.CancelAll();
}
@@ -233,14 +232,14 @@ struct npc_akama_shade : public ScriptedAI
case POINT_ENGAGE:
me->SetHomePosition(me->GetPosition());
me->SetFaction(FACTION_ENGAGE);
DoCastSelf(SPELL_AKAMA_SOUL_CHANNEL, true);
DoCast(me, SPELL_AKAMA_SOUL_CHANNEL, true);
break;
case POINT_OUTRO:
DoCastSelf(SPELL_AKAMA_SOUL_RETRIEVE, true);
ScheduleUniqueTimedEvent(15600ms, [&]
{
Talk(SAY_BROKEN_FREE_0);
me->SummonCreatureGroup(SUMMON_GROUP_BROKENS);
Talk(SAY_BROKEN_FREE_0);
me->SummonCreatureGroup(SUMMON_GROUP_BROKENS);
}, 1);
ScheduleUniqueTimedEvent(26550ms, [&]
{
@@ -276,19 +275,6 @@ struct npc_akama_shade : public ScriptedAI
_sayLowHealth = true;
Talk(SAY_LOW_HEALTH);
}
else if (damage >= me->GetHealth() && !_died)
{
damage = me->GetHealth() - 1;
Talk(SAY_DEATH);
if (Creature* shade = instance->GetCreature(DATA_SHADE_OF_AKAMA))
{
shade->SetHomePosition(shade->GetHomePosition());
shade->AI()->EnterEvadeMode();
}
me->DespawnOrUnsummon();
ScriptedAI::EnterEvadeMode(EvadeReason::EVADE_REASON_OTHER);
}
}
void DoAction(int32 param) override
@@ -312,6 +298,18 @@ struct npc_akama_shade : public ScriptedAI
void EnterEvadeMode(EvadeReason /*why*/) override { }
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
if (Creature* shade = instance->GetCreature(DATA_SHADE_OF_AKAMA))
{
shade->SetHomePosition(shade->GetHomePosition());
shade->AI()->EnterEvadeMode();
}
me->DespawnOrUnsummon();
}
void JustEngagedWith(Unit* /*who*/) override
{
ScheduleTimedEvent(2s, [&]
@@ -340,7 +338,6 @@ struct npc_akama_shade : public ScriptedAI
private:
bool _sayLowHealth;
bool _died;
};
struct npc_creature_generator_akama : public ScriptedAI
@@ -448,18 +445,27 @@ private:
struct npc_ashtongue_sorcerer : public NullCreatureAI
{
npc_ashtongue_sorcerer(Creature* creature) : NullCreatureAI(creature) { }
npc_ashtongue_sorcerer(Creature* creature) : NullCreatureAI(creature)
{
instance = creature->GetInstanceScript();
}
void MovementInform(uint32 type, uint32 point) override
{
if (type == POINT_MOTION_TYPE && point == POINT_ENGAGE)
me->CastSpell(me, SPELL_SHADE_SOUL_CHANNEL, true);
}
private:
InstanceScript* instance;
};
struct npc_ashtongue_channeler : public NullCreatureAI
{
npc_ashtongue_channeler(Creature* creature) : NullCreatureAI(creature) { }
npc_ashtongue_channeler(Creature* creature) : NullCreatureAI(creature)
{
instance = creature->GetInstanceScript();
}
void Reset() override
{
@@ -478,6 +484,7 @@ struct npc_ashtongue_channeler : public NullCreatureAI
}
private:
InstanceScript* instance;
TaskScheduler scheduler;
};
@@ -294,6 +294,11 @@ struct boss_kaelthas : public BossAI
BossAI::AttackStart(who);
}
void JustReachedHome() override
{
Reset();
}
void MoveInLineOfSight(Unit* who) override
{
if (_phase == PHASE_NONE && who->IsPlayer() && me->IsValidAttackTarget(who))