Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2024-08-30 22:38:08 +08:00
49 changed files with 1958 additions and 1121 deletions
@@ -1289,20 +1289,6 @@ Visibility.Distance.Continents = 90
Visibility.Distance.Instances = 120
Visibility.Distance.BGArenas = 180
#
# Visibility.Notify.Period.OnContinents
# Visibility.Notify.Period.InInstances
# Visibility.Notify.Period.InBGArenas
# Description: Time (in milliseconds) for visibility update period. Lower values may have
# performance impact.
# Default: 1000 - (Visibility.Notify.Period.OnContinents)
# 1000 - (Visibility.Notify.Period.InInstances)
# 1000 - (Visibility.Notify.Period.InBGArenas)
Visibility.Notify.Period.OnContinents = 1000
Visibility.Notify.Period.InInstances = 1000
Visibility.Notify.Period.InBGArenas = 1000
#
# Visibility.ObjectSparkles
# Description: Whether or not to display sparkles on gameobjects related to active quests.
@@ -1721,15 +1707,6 @@ PlayerStart.MapsExplored = 0
InstantLogout = 1
#
# CastUnstuck
# Description: Allow casting the Unstuck spell using .start or unstuck button in client
# help options.
# Default: 1 - (Enabled)
# 0 - (Disabled)
CastUnstuck = 1
#
###################################################################################################
@@ -2103,7 +2080,6 @@ Rate.Talent.Pet = 1
# Rate.RunicPower.Loss
# Rate.Focus
# Rate.Energy
# Rate.Loyalty
# Description: Multiplier to configure health, mana, incoming rage, loss of rage, focus
# energy and loyalty increase or decrease.
# Default: 1 - (Rate.Health)
@@ -2114,7 +2090,6 @@ Rate.Talent.Pet = 1
# 1 - (Rate.RunicPower.Loss)
# 1 - (Rate.Focus)
# 1 - (Rate.Energy)
# 1 - (Rate.Loyalty)
Rate.Health = 1
Rate.Mana = 1
+3
View File
@@ -253,6 +253,9 @@ SpellCastResult UnitAI::DoCastAOE(uint32 spellId, bool triggered)
return me->CastSpell((Unit*)nullptr, spellId, triggered);
}
/**
* @brief Cast the spell on a random unit from the threat list
*/
SpellCastResult UnitAI::DoCastRandomTarget(uint32 spellId, uint32 threatTablePosition, float dist, bool playerOnly, bool triggered, bool withTank)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, threatTablePosition, dist, playerOnly, withTank))
+27 -25
View File
@@ -198,14 +198,14 @@ public:
virtual ~UnitAI() {}
virtual bool CanAIAttack(Unit const* /*target*/) const { return true; }
virtual void AttackStart(Unit* /*target*/);
virtual void AttackStart(Unit* /*target*/); /// @brief Use to start attacking a target. Called just before JustEngagedWith()
virtual void UpdateAI(uint32 /*diff*/) = 0;
virtual void InitializeAI() { if (!me->isDead()) Reset(); }
virtual void Reset() {};
// Called when unit is charmed
/// @brief Called when unit is charmed
virtual void OnCharmed(bool apply) = 0;
// Pass parameters between AI
@@ -258,16 +258,16 @@ public:
}
}
// Select the best (up to) <num> targets (in <targetType> order) from the threat list that fulfill the following:
// - Not among the first <offset> entries in <targetType> order (or SelectTargetMethod::MaxThreat order,
// if <targetType> is SelectTargetMethod::Random).
// - Within at most <dist> yards (if dist > 0.0f)
// - At least -<dist> yards away (if dist < 0.0f)
// - Is a player (if playerOnly = true)
// - Not the current tank (if withTank = false)
// - Has aura with ID <aura> (if aura > 0)
// - Does not have aura with ID -<aura> (if aura < 0)
// The resulting targets are stored in <targetList> (which is cleared first).
/** @brief Select the best (up to) <num> targets (in <targetType> order) from the threat list that fulfill the following:
* - Not among the first <offset> entries in <targetType> order (or SelectTargetMethod::MaxThreat order, if <targetType> is SelectTargetMethod::Random).
* - Within at most <dist> yards (if dist > 0.0f)
* - At least -<dist> yards away (if dist < 0.0f)
* - Is a player (if playerOnly = true)
* - Not the current tank (if withTank = false)
* - Has aura with ID <aura> (if aura > 0)
* - Does not have aura with ID -<aura> (if aura < 0)
* The resulting targets are stored in <targetList> (which is cleared first).
*/
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, uint32 position = 0, float dist = 0.0f, bool playerOnly = false, bool withTank = true, int32 aura = 0);
// Select the best (up to) <num> targets (in <targetType> order) satisfying <predicate> from the threat list and stores them in <targetList> (which is cleared first).
@@ -345,7 +345,7 @@ public:
/**
* @brief Called when the unit enters combat
* (NOTE: Creature engage logic should NOT be here, but in JustEngagedWith, which happens once threat is established!)
* @note NOTE: Creature engage logic should NOT be here, but in JustEngagedWith, which happens once threat is established!)
*
* @todo Never invoked right now. Preparation for Combat Threat refactor
*/
@@ -358,28 +358,30 @@ public:
*/
virtual void JustExitedCombat() { }
// Called at any Damage to any victim (before damage apply)
/// @brief Called at any Damage to any victim (before damage apply)
virtual void DamageDealt(Unit* /*victim*/, uint32& /*damage*/, DamageEffectType /*damageType*/) { }
// Called at any Damage from any attacker (before damage apply)
// Note: it for recalculation damage or special reaction at damage
// for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also
/** @brief Called at any Damage from any attacker (before damage apply)
*
* @note It use for recalculation damage or special reaction at damage
* for attack reaction use AttackedBy called for non DOT damage in Unit::DealDamage also
*/
virtual void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) {}
// Called when the creature receives heal
/// @brief Called when the creature receives heal
virtual void HealReceived(Unit* /*done_by*/, uint32& /*addhealth*/) {}
// Called when the creature power updates
/// @brief Called when the creature power updates
virtual void OnPowerUpdate(Powers /*power*/, int32 /*updateVal*/, int32 /*gain*/, uint32 /*currPower*/) {}
// Called when the unit heals
/// @brief Called when the unit heals
virtual void HealDone(Unit* /*done_to*/, uint32& /*addhealth*/) {}
// Called during damage calculations
/// @brief Called during damage calculations
virtual void OnCalculateMeleeDamageReceived(uint32& /*damage*/, Unit* /*attacker*/) {}
virtual void OnCalculateSpellDamageReceived(int32& /*damage*/, Unit* /*attacker*/) {}
// Called during calculation when receiving periodic healing or damage (DoT or HoT)
/// @brief Called during calculation when receiving periodic healing or damage (DoT or HoT)
virtual void OnCalculatePeriodicTickReceived(uint32& /*damage*/, Unit* /*attacker*/) {}
void AttackStartCaster(Unit* victim, float dist);
@@ -387,13 +389,13 @@ public:
SpellCastResult DoAddAuraToAllHostilePlayers(uint32 spellid);
SpellCastResult DoCast(uint32 spellId);
SpellCastResult DoCast(Unit* victim, uint32 spellId, bool triggered = false);
SpellCastResult DoCastSelf(uint32 spellId, bool triggered = false) { return DoCast(me, spellId, triggered); }
SpellCastResult DoCastSelf(uint32 spellId, bool triggered = false) { return DoCast(me, spellId, triggered); } /// @brief To specify the caster as target if the spell is self-cast
SpellCastResult DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false);
SpellCastResult DoCastVictim(uint32 spellId, bool triggered = false);
SpellCastResult DoCastAOE(uint32 spellId, bool triggered = false);
SpellCastResult DoCastRandomTarget(uint32 spellId, uint32 threatTablePosition = 0, float dist = 0.0f, bool playerOnly = true, bool triggered = false, bool withTank = true);
// Cast spell on the top threat target, which may not be the current victim.
/// @brief Cast spell on the top threat target, which may not be the current victim.
SpellCastResult DoCastMaxThreat(uint32 spellId, uint32 threatTablePosition = 0, float dist = 0.0f, bool playerOnly = true, bool triggered = false);
float DoGetSpellMaxRange(uint32 spellId, bool positive = false);
@@ -405,7 +407,7 @@ public:
static AISpellInfoType* AISpellInfo;
static void FillAISpellInfo();
// Called when a summon reaches a waypoint or point movement finished.
/// @brief Called when a summon reaches a waypoint or point movement finished.
virtual void SummonMovementInform(Creature* /*creature*/, uint32 /*motionType*/, uint32 /*point*/) { }
virtual void sGossipHello(Player* /*player*/) {}
@@ -720,9 +720,18 @@ void BossAI::DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*
{
if (!_healthCheckEvents.empty())
{
for (auto& check : _healthCheckEvents)
{
if (check._valid && me->HealthBelowPctDamaged(check._healthPct, damage))
{
check._exec();
check._valid = false;
}
}
_healthCheckEvents.remove_if([&](HealthCheckEventData data) -> bool
{
return _ProccessHealthCheckEvent(data._healthPct, damage, data._exec);
return !data._valid;
});
}
}
@@ -746,17 +755,6 @@ void BossAI::ScheduleHealthCheckEvent(std::initializer_list<uint8> healthPct, st
}
}
bool BossAI::_ProccessHealthCheckEvent(uint8 healthPct, uint32 damage, std::function<void()> exec) const
{
if (me->HealthBelowPctDamaged(healthPct, damage))
{
exec();
return true;
}
return false;
}
// WorldBossAI - for non-instanced bosses
WorldBossAI::WorldBossAI(Creature* creature) :
@@ -449,10 +449,11 @@ private:
struct HealthCheckEventData
{
HealthCheckEventData(uint8 healthPct, std::function<void()> exec) : _healthPct(healthPct), _exec(exec) { };
HealthCheckEventData(uint8 healthPct, std::function<void()> exec, bool valid = true) : _healthPct(healthPct), _exec(exec), _valid(valid) { };
uint8 _healthPct;
std::function<void()> _exec;
bool _valid;
};
class BossAI : public ScriptedAI
@@ -497,7 +498,6 @@ protected:
void _JustDied();
void _JustReachedHome() { me->setActive(false); }
void _EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
[[nodiscard]] bool _ProccessHealthCheckEvent(uint8 healthPct, uint32 damage, std::function<void()> exec) const;
void TeleportCheaters();
@@ -70,6 +70,8 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
mJustReset = false;
mcanSpawn = true;
// Xinef: Vehicle conditions
m_ConditionsTimer = 0;
if (me->GetVehicleKit())
@@ -1166,6 +1168,7 @@ void SmartAI::OnSpellClick(Unit* clicker, bool& /*result*/)
void SmartAI::PathEndReached(uint32 /*pathId*/)
{
GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_DATA_ENDED, nullptr, 0, me->GetWaypointPath());
me->LoadPath(0);
}
void SmartGameObjectAI::SummonedCreatureDies(Creature* summon, Unit* /*killer*/)
@@ -207,6 +207,9 @@ public:
void PathEndReached(uint32 pathId) override;
bool CanRespawn() override { return mcanSpawn; };
void SetCanRespawn(bool canSpawn) { mcanSpawn = canSpawn; }
// Xinef
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
@@ -249,6 +252,8 @@ private:
uint32 mEscortInvokerCheckTimer;
bool mJustReset;
bool mcanSpawn;
// Xinef: Vehicle conditions
void CheckConditions(const uint32 diff);
ConditionList conditions;
@@ -2940,15 +2940,64 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
break;
}
case SMART_ACTION_DISABLE:
case SMART_ACTION_SCRIPTED_SPAWN:
{
for (WorldObject* target : targets)
// Enable Scripted Spawns
switch (e.action.scriptSpawn.state)
{
if (IsUnit(target))
case 0: // Disable Respawn
{
for (WorldObject* target : targets)
{
target->ToUnit()->SetImmuneToAll(!e.action.disable.state);
target->ToUnit()->SetVisible(e.action.disable.state);
if (Creature* c = target->ToCreature())
{
CAST_AI(SmartAI, c->AI())->SetCanRespawn(false);
if (!e.action.scriptSpawn.dontDespawn)
c->DespawnOrUnsummon();
}
}
break;
}
case 1: // Respawn Once
{
for (WorldObject* target : targets)
{
if (Creature* c = target->ToCreature())
{
CAST_AI(SmartAI, c->AI())->SetCanRespawn(true);
c->Respawn(true);
CAST_AI(SmartAI, c->AI())->SetCanRespawn(false);
}
}
break;
}
case 2: // Enable Respawning
{
for (WorldObject* target : targets)
{
if (Creature* c = target->ToCreature())
{
CAST_AI(SmartAI, c->AI())->SetCanRespawn(true);
// If 0, respawn immediately
if (e.action.scriptSpawn.spawnTimerMax)
c->SetRespawnTime(urand(e.action.scriptSpawn.spawnTimerMin, e.action.scriptSpawn.spawnTimerMax));
else
c->Respawn(true);
// If 0, use DB values
if (e.action.scriptSpawn.respawnDelay)
c->SetRespawnDelay(e.action.scriptSpawn.respawnDelay);
// If 0, use default
if (e.action.scriptSpawn.corpseDelay)
c->SetCorpseDelay(e.action.scriptSpawn.corpseDelay);
}
}
break;
}
default:
break;
}
break;
}
@@ -766,7 +766,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_LOAD_GRID: return NO_PARAMS;
case SMART_ACTION_MUSIC: return sizeof(SmartAction::music);
case SMART_ACTION_SET_GUID: return sizeof(SmartAction::setGuid);
case SMART_ACTION_DISABLE: return sizeof(SmartAction::disable);
case SMART_ACTION_SCRIPTED_SPAWN: return sizeof(SmartAction::scriptSpawn);
case SMART_ACTION_SET_SCALE: return sizeof(SmartAction::setScale);
case SMART_ACTION_SUMMON_RADIAL: return sizeof(SmartAction::radialSummon);
case SMART_ACTION_PLAY_SPELL_VISUAL: return sizeof(SmartAction::spellVisual);
@@ -1949,7 +1949,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_ATTACK_STOP:
case SMART_ACTION_PLAY_CINEMATIC:
case SMART_ACTION_SET_GUID:
case SMART_ACTION_DISABLE:
case SMART_ACTION_SCRIPTED_SPAWN:
case SMART_ACTION_SET_SCALE:
case SMART_ACTION_SUMMON_RADIAL:
case SMART_ACTION_PLAY_SPELL_VISUAL:
@@ -716,7 +716,7 @@ enum SMART_ACTION
SMART_ACTION_DO_ACTION = 223, // ActionId
SMART_ACTION_ATTACK_STOP = 224, //
SMART_ACTION_SET_GUID = 225, // Sends the invoker's or the base object's own ObjectGuid to target
SMART_ACTION_DISABLE = 226, // state
SMART_ACTION_SCRIPTED_SPAWN = 226, // state, spawnTimerMin, spawnTimerMax, respawnDelay, corpseDelay, dontDespawn
SMART_ACTION_SET_SCALE = 227, // scale
SMART_ACTION_SUMMON_RADIAL = 228, // summonEntry, summonDuration, repetitions, startAngle, stepAngle, dist
SMART_ACTION_PLAY_SPELL_VISUAL = 229, // visualId, visualIdImpact
@@ -1411,7 +1411,12 @@ struct SmartAction
struct
{
SAIBool state;
} disable;
uint32 spawnTimerMin;
uint32 spawnTimerMax;
uint32 respawnDelay;
uint32 corpseDelay;
SAIBool dontDespawn;
} scriptSpawn;
struct
{
+41 -21
View File
@@ -1946,11 +1946,21 @@ bool Creature::CanStartAttack(Unit const* who) const
return IsWithinLOSInMap(who);
}
void Creature::setDeathState(DeathState s, bool despawn)
/**
* @brief A creature can be in 4 different states: Alive, JustDied, Corpse, and JustRespawned. The cycle follows the next order:
* - Alive: The creature is alive and has spawned in the world
* - JustDied: The creature has just died. This is the state just before his body appeared
* - Corpse: The creature has been removed from the world, and this corpse has been spawned
* - JustRespawned: The creature has just respawned. Follow when the corpse disappears and the respawn timer is finished
*
* @param state Specify one of 4 states above
* @param despawn Despawn the creature immediately, without waiting for any movement to finish
*/
void Creature::setDeathState(DeathState state, bool despawn)
{
Unit::setDeathState(s, despawn);
Unit::setDeathState(state, despawn);
if (s == DeathState::JustDied)
if (state == DeathState::JustDied)
{
_lastDamagedTime.reset();
@@ -1961,10 +1971,10 @@ void Creature::setDeathState(DeathState s, bool despawn)
if (GetMap()->IsDungeon() || isWorldBoss() || GetCreatureTemplate()->rank >= CREATURE_ELITE_ELITE)
SaveRespawnTime();
SetTarget(); // remove target selection in any cases (can be set at aura remove in Unit::setDeathState)
SetTarget(); // remove target selection in any cases (can be set at aura remove in Unit::setDeathState)
ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE);
Dismount(); // if creature is mounted on a virtual mount, remove it at death
Dismount(); // if creature is mounted on a virtual mount, remove it at death
setActive(false);
@@ -1986,10 +1996,8 @@ void Creature::setDeathState(DeathState s, bool despawn)
Unit::setDeathState(DeathState::Corpse, despawn);
}
else if (s == DeathState::JustRespawned)
else if (state == DeathState::JustRespawned)
{
//if (IsPet())
// setActive(true);
SetFullHealth();
SetLootRecipient(nullptr);
ResetPlayerDamageReq();
@@ -2017,6 +2025,9 @@ void Creature::setDeathState(DeathState s, bool despawn)
}
}
/**
* @param force Force the respawn by killing the creature.
*/
void Creature::Respawn(bool force)
{
if (force)
@@ -2041,7 +2052,7 @@ void Creature::Respawn(bool force)
}
bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning
if (!allowed && !force) // Will be rechecked on next Update call
if (!allowed && !force) // Will be rechecked on next Update call
return;
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(m_creatureData ? m_creatureData->id1 : GetEntry(), m_spawnId);
@@ -2083,8 +2094,7 @@ void Creature::Respawn(bool force)
setDeathState(DeathState::JustRespawned);
// MDic - Acidmanifesto
// Do not override transform auras
// MDic - Acidmanifesto: Do not override transform auras
if (GetAuraEffectsByType(SPELL_AURA_TRANSFORM).empty())
{
CreatureModel display(GetNativeDisplayId(), GetNativeObjectScale(), 1.0f);
@@ -2103,7 +2113,7 @@ void Creature::Respawn(bool force)
{
//reset the AI to be sure no dirty or uninitialized values will be used till next tick
AI()->Reset();
TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
TriggerJustRespawned = true; //delay event to next tick so all creatures are created on the map before processing
}
uint32 poolid = m_spawnId ? sPoolMgr->IsPartOfAPool<Creature>(m_spawnId) : 0;
@@ -2121,7 +2131,7 @@ void Creature::Respawn(bool force)
UpdateObjectVisibility(false);
}
else // the master is dead
else // the master is dead
{
ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
@@ -2149,7 +2159,7 @@ void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds forceRespawnTimer)
if (IsAlive())
setDeathState(DeathState::JustDied, true);
// Xinef: set new respawn time, ignore corpse decay time...
// Xinef: Set new respawn time, ignore corpse decay time...
RemoveCorpse(true);
if (forceRespawnTimer > Seconds::zero())
@@ -2195,8 +2205,6 @@ void Creature::InitializeReactState()
SetReactState(REACT_PASSIVE);
else
SetReactState(REACT_AGGRESSIVE);
/*else if (IsCivilian())
SetReactState(REACT_DEFENSIVE);*/
}
bool Creature::HasMechanicTemplateImmunity(uint32 mask) const
@@ -2358,8 +2366,7 @@ SpellInfo const* Creature::reachWithSpellCure(Unit* victim)
float range = spellInfo->GetMaxRange(true);
float minrange = spellInfo->GetMinRange(true);
float dist = GetDistance(victim);
//if (!isInFront(victim, range) && spellInfo->AttributesEx)
// continue;
if (dist > range || dist < minrange)
continue;
if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && HasUnitFlag(UNIT_FLAG_SILENCED))
@@ -2371,7 +2378,9 @@ SpellInfo const* Creature::reachWithSpellCure(Unit* victim)
return nullptr;
}
// select nearest hostile unit within the given distance (regardless of threat list).
/**
* @brief Select nearest hostile unit within the given distance (regardless of threat list).
*/
Unit* Creature::SelectNearestTarget(float dist, bool playerOnly /* = false */) const
{
if (dist == 0.0f)
@@ -2387,7 +2396,9 @@ Unit* Creature::SelectNearestTarget(float dist, bool playerOnly /* = false */) c
return target;
}
// select nearest hostile unit within the given attack distance (i.e. distance is ignored if > than ATTACK_DISTANCE), regardless of threat list.
/**
* @brief Select nearest hostile unit within the given attack distance (i.e. distance is ignored if > than ATTACK_DISTANCE), regardless of threat list.
*/
Unit* Creature::SelectNearestTargetInAttackDistance(float dist) const
{
if (dist < ATTACK_DISTANCE)
@@ -2630,7 +2641,7 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const
return false;
// cannot attack if is during 5 second grace period, unless being attacked
if (m_respawnedTime && (GameTime::GetGameTime().count() - m_respawnedTime) < 5 && victim->getAttackers().empty())
if (m_respawnedTime && (GameTime::GetGameTime().count() - m_respawnedTime) < 5 && !GetLastDamagedTime())
{
return false;
}
@@ -2781,6 +2792,9 @@ void Creature::SendZoneUnderAttackMessage(Player* attacker)
sWorld->SendGlobalMessage(&data, nullptr, (attacker->GetTeamId() == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE));
}
/**
* @brief Set in combat all units in the dungeon/raid. Affect only units with IsAIEnabled.
*/
void Creature::SetInCombatWithZone()
{
if (IsAIEnabled)
@@ -3147,6 +3161,9 @@ bool Creature::IsImmuneToKnockback() const
return cinfo && (cinfo->flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK);
}
/**
* @brief Enable or disable the creature's walk mode by removing: MOVEMENTFLAG_WALKING. Infom also the client
*/
bool Creature::SetWalk(bool enable)
{
if (!Unit::SetWalk(enable))
@@ -3158,6 +3175,9 @@ bool Creature::SetWalk(bool enable)
return true;
}
/**
* @brief Enable or disable the creature's fly mode by adding or removing: MOVEMENTFLAG_FLYING. Infom also the client
*/
bool Creature::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool updateAnimationTier /*= true*/)
{
//! It's possible only a packet is sent but moveflags are not updated
+28 -25
View File
@@ -40,7 +40,7 @@ class CreatureGroup;
// max different by z coordinate for creature aggro reaction
#define CREATURE_Z_ATTACK_RANGE 3
#define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY
#define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY
class Creature : public Unit, public GridObject<Creature>, public MovableMapObject
{
@@ -67,7 +67,7 @@ public:
[[nodiscard]] ObjectGuid::LowType GetSpawnId() const { return m_spawnId; }
void Update(uint32 time) override; // overwrited Unit::Update
void Update(uint32 time) override; // overwrited Unit::Update
void GetRespawnPosition(float& x, float& y, float& z, float* ori = nullptr, float* dist = nullptr) const;
void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; }
@@ -88,9 +88,15 @@ public:
MovementGeneratorType GetDefaultMovementType() const override { return m_defaultMovementType; }
void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; }
void SetReactState(ReactStates st) { m_reactState = st; }
/**
* @brief A creature can have 3 ReactStates : Agressive, Passive, Neutral
* - Agressive : The creature will attack any non friend units in sight
* - Passive : The creature will not attack anyone
* - Neutral : The creature will attack only if attacked
*/
void SetReactState(ReactStates state) { m_reactState = state; }
[[nodiscard]] ReactStates GetReactState() const { return m_reactState; }
[[nodiscard]] bool HasReactState(ReactStates state) const { return (m_reactState == state); }
[[nodiscard]] bool HasReactState(ReactStates state) const { return (m_reactState == state); } /// @brief Check if the creature has the specified ReactState
void InitializeReactState();
///// @todo RENAME THIS!!!!!
@@ -209,14 +215,14 @@ public:
// override WorldObject function for proper name localization
[[nodiscard]] std::string const& GetNameForLocaleIdx(LocaleConstant locale_idx) const override;
void setDeathState(DeathState s, bool despawn = false) override; // override virtual Unit::setDeathState
void setDeathState(DeathState s, bool despawn = false) override; // override virtual Unit::setDeathState
bool LoadFromDB(ObjectGuid::LowType guid, Map* map, bool allowDuplicate = false) { return LoadCreatureFromDB(guid, map, false, allowDuplicate); }
bool LoadCreatureFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true, bool allowDuplicate = false);
void SaveToDB();
// overriden in Pet
virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
virtual void DeleteFromDB(); // overriden in Pet
virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); // overriden in Pet
virtual void DeleteFromDB(); // overriden in Pet
Loot loot;
[[nodiscard]] ObjectGuid GetLootRecipientGUID() const { return m_lootRecipient; }
@@ -224,7 +230,7 @@ public:
[[nodiscard]] ObjectGuid::LowType GetLootRecipientGroupGUID() const { return m_lootRecipientGroup; }
[[nodiscard]] Group* GetLootRecipientGroup() const;
[[nodiscard]] bool hasLootRecipient() const { return m_lootRecipient || m_lootRecipientGroup; }
bool isTappedBy(Player const* player) const; // return true if the creature is tapped by the player or a member of his party.
bool isTappedBy(Player const* player) const; // return true if the creature is tapped by the player or a member of his party.
[[nodiscard]] bool CanGeneratePickPocketLoot() const;
void SetPickPocketLootTime();
void ResetPickPocketLootTime() { lootPickPocketRestoreTime = 0; }
@@ -266,7 +272,7 @@ public:
bool _IsTargetAcceptable(Unit const* target) const;
[[nodiscard]] bool CanIgnoreFeignDeath() const { return (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH) != 0; }
// pussywizard: updated at faction change, disable move in line of sight if actual faction is not hostile to anyone
// pussywizard: Updated at faction change, disable move in line of sight if actual faction is not hostile to anyone
void UpdateMoveInLineOfSightState();
bool IsMoveInLineOfSightDisabled() { return m_moveInLineOfSightDisabled; }
bool IsMoveInLineOfSightStrictlyDisabled() { return m_moveInLineOfSightStrictlyDisabled; }
@@ -299,8 +305,8 @@ public:
void DoImmediateBoundaryCheck() { m_boundaryCheckTime = 0; }
uint32 m_groupLootTimer; // (msecs)timer used for group loot
uint32 lootingGroupLowGUID; // used to find group which is looting corpse
uint32 m_groupLootTimer; // (msecs)timer used for group loot
uint32 lootingGroupLowGUID; // used to find group which is looting corpse
void SendZoneUnderAttackMessage(Player* attacker);
@@ -310,8 +316,8 @@ public:
[[nodiscard]] bool hasInvolvedQuest(uint32 quest_id) const override;
bool isRegeneratingHealth() { return m_regenHealth; }
void SetRegeneratingHealth(bool c) { m_regenHealth = c; }
void SetRegeneratingPower(bool c) { m_regenPower = c; }
void SetRegeneratingHealth(bool enable) { m_regenHealth = enable; }
void SetRegeneratingPower(bool enable) { m_regenPower = enable; }
[[nodiscard]] virtual uint8 GetPetAutoSpellSize() const { return MAX_SPELL_CHARM; }
[[nodiscard]] virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const
{
@@ -398,20 +404,17 @@ public:
/**
* @brief Helper to resume chasing current victim.
*
* */
*/
void ResumeChasingVictim() { GetMotionMaster()->MoveChase(GetVictim()); };
/**
* @brief Returns true if the creature is able to cast the spell.
*
* */
*/
bool CanCastSpell(uint32 spellID) const;
/**
* @brief Helper to get the creature's summoner GUID, if it is a summon
*
* */
* @brief Helper to get the creature's summoner GUID, if it is a summon
*/
[[nodiscard]] ObjectGuid GetSummonerGUID() const;
// Used to control if MoveChase() is to be used or not in AttackStart(). Some creatures does not chase victims
@@ -474,11 +477,11 @@ protected:
bool DisableReputationGain;
CreatureTemplate const* m_creatureInfo; // in difficulty mode > 0 can different from sObjectMgr->GetCreatureTemplate(GetEntry())
CreatureTemplate const* m_creatureInfo; // in difficulty mode > 0 can different from sObjectMgr->GetCreatureTemplate(GetEntry())
CreatureData const* m_creatureData;
float m_detectionDistance;
uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
[[nodiscard]] bool IsInvisibleDueToDespawn() const override;
bool CanAlwaysSee(WorldObject const* obj) const override;
@@ -489,11 +492,11 @@ private:
[[nodiscard]] bool CanPeriodicallyCallForAssistance() const;
//WaypointMovementGenerator vars
// WaypointMovementGenerator variable
uint32 m_waypointID;
uint32 m_path_id;
//Formation var
// Formation variable
CreatureGroup* m_formation;
bool TriggerJustRespawned;
@@ -13595,6 +13595,7 @@ uint32 Player::CalculateTalentsPoints() const
}
talentPointsForLevel += m_extraBonusTalentCount;
sScriptMgr->OnCalculateTalentsPoints(this, talentPointsForLevel);
return uint32(talentPointsForLevel * sWorld->getRate(RATE_TALENT));
}
+25 -1
View File
@@ -1141,6 +1141,9 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
return damage;
}
/**
* @brief Interrupt the unit cast for all the current spells
*/
void Unit::CastStop(uint32 except_spellid, bool withInstant)
{
for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
@@ -3034,6 +3037,10 @@ void Unit::SendMeleeAttackStart(Unit* victim, Player* sendTo)
LOG_DEBUG("entities.unit", "WORLD: Sent SMSG_ATTACKSTART");
}
/**
* @brief Send to the client SMSG_ATTACKSTOP but doesn't clear UNIT_STATE_MELEE_ATTACKING on server side
* or interrupt spells. Unless you know exactly what you're doing, use AttackStop() or RemoveAllAttackers() instead
*/
void Unit::SendMeleeAttackStop(Unit* victim)
{
// pussywizard: calling SendMeleeAttackStop without clearing UNIT_STATE_MELEE_ATTACKING and then AttackStart the same player may spoil npc rotating!
@@ -10348,6 +10355,10 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
return true;
}
/**
* @brief Force the unit to stop attacking. This will clear UNIT_STATE_MELEE_ATTACKING,
* Interrupt current spell, AI assistance, and call SendMeleeAttackStop() to the client
*/
bool Unit::AttackStop()
{
if (!m_attacking)
@@ -10424,6 +10435,9 @@ bool Unit::isAttackingPlayer() const
return false;
}
/**
* @brief Remove all units in m_attackers list and send them AttackStop()
*/
void Unit::RemoveAllAttackers()
{
while (!m_attackers.empty())
@@ -16617,7 +16631,7 @@ void Unit::ResumeMovement(uint32 timer /* = 0*/, uint8 slot /* = 0*/)
movementGenerator->Resume(timer);
}
void Unit::StopMovingOnCurrentPos() // pussywizard
void Unit::StopMovingOnCurrentPos()
{
ClearUnitState(UNIT_STATE_MOVING);
@@ -20518,6 +20532,12 @@ bool Unit::SetSwim(bool enable)
return true;
}
/**
* @brief Add the movement flag: MOVEMENTFLAGCAN_FLY. Generaly only use by players, allowing
* them to fly by pressing space for example. For creatures, please look for DisableGravity().
*
* Doesn't inform the client.
*/
bool Unit::SetCanFly(bool enable, bool /*packetOnly = false */)
{
if (enable == HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY))
@@ -20536,6 +20556,10 @@ bool Unit::SetCanFly(bool enable, bool /*packetOnly = false */)
return true;
}
/**
* @brief Allow to walk on water. Doesn't inform the client.
* Need to use SendMovementWaterWalking() if it's for players.
*/
bool Unit::SetWaterWalking(bool enable, bool /*packetOnly = false*/)
{
if (enable == HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING))
+6 -5
View File
@@ -717,6 +717,7 @@ public:
return nullptr;
}
bool Attack(Unit* victim, bool meleeAttack);
void CastStop(uint32 except_spellid = 0, bool withInstant = true);
bool AttackStop();
void RemoveAllAttackers();
@@ -821,10 +822,10 @@ public:
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); }
void SetUnitFlag(UnitFlags flags) { SetFlag(UNIT_FIELD_FLAGS, flags); }
void RemoveUnitFlag(UnitFlags flags) { RemoveFlag(UNIT_FIELD_FLAGS, flags); }
void ReplaceAllUnitFlags(UnitFlags flags) { SetUInt32Value(UNIT_FIELD_FLAGS, 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); }
@@ -1609,7 +1610,7 @@ public:
[[nodiscard]] bool IsStopped() const { return !(HasUnitState(UNIT_STATE_MOVING)); }
void StopMoving();
void StopMovingOnCurrentPos();
void StopMovingOnCurrentPos(); /// @brief Disable the unit movement by clearing UNIT_STATE_MOVING and stopping the spline.
virtual void PauseMovement(uint32 timer = 0, uint8 slot = 0); // timer in ms
void ResumeMovement(uint32 timer = 0, uint8 slot = 0);
+64 -13
View File
@@ -236,9 +236,11 @@ void MotionMaster::MoveIdle()
Mutate(GetIdleMovementGenerator(), MOTION_SLOT_IDLE);
}
/**
* @brief Enable a random movement in desired range around the unit. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveRandom(float wanderDistance)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -249,6 +251,11 @@ void MotionMaster::MoveRandom(float wanderDistance)
}
}
/**
* @brief The unit will return this initial position (owner for pets and summoned creatures). Doesn't work with UNIT_FLAG_DISABLE_MOVE
*
* @param walk The unit will run by default, but you can set it to walk
*/
void MotionMaster::MoveTargetedHome(bool walk /*= false*/)
{
Clear(false);
@@ -261,7 +268,7 @@ void MotionMaster::MoveTargetedHome(bool walk /*= false*/)
else if (_owner->GetTypeId() == TYPEID_UNIT && _owner->ToCreature()->GetCharmerOrOwnerGUID())
{
_owner->ClearUnitState(UNIT_STATE_EVADE);
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -279,6 +286,9 @@ void MotionMaster::MoveTargetedHome(bool walk /*= false*/)
}
}
/**
* @brief Enable the confusion movement. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveConfused()
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
@@ -297,9 +307,11 @@ void MotionMaster::MoveConfused()
}
}
/**
* @brief Force the unit to chase this target. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveChase(Unit* target, std::optional<ChaseRange> dist, std::optional<ChaseAngle> angle)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
// ignore movement request if target not exist
if (!target || target == _owner || _owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -391,9 +403,11 @@ void MotionMaster::MoveCircleTarget(Unit* target)
init.Launch();
}
/**
* @brief The unit will follow this target. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot, bool inheritWalkState)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
// ignore movement request if target not exist
if (!target || target == _owner || _owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
{
@@ -415,9 +429,13 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo
}
}
/**
* @brief The unit will move to a specific point. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*
* For transition movement between the ground and the air, use MoveLand or MoveTakeoff instead.
*/
void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generatePath, bool forceDestination, MovementSlot slot, float orientation /* = 0.0f*/)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -464,9 +482,11 @@ void MotionMaster::MoveSplinePath(uint32 path_id)
MoveSplinePath(points);
}
/**
* @brief Use to move the unit from the air to the ground. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveLand(uint32 id, Position const& pos, float speed /* = 0.0f*/)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -488,15 +508,20 @@ void MotionMaster::MoveLand(uint32 id, Position const& pos, float speed /* = 0.0
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_ACTIVE);
}
/**
* @brief Use to move the unit from the air to the ground. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveLand(uint32 id, float x, float y, float z, float speed /* = 0.0f*/)
{
Position pos = {x, y, z, 0.0f};
MoveLand(id, pos, speed);
}
/**
* @brief Use to move the unit from the ground to the air. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveTakeoff(uint32 id, Position const& pos, float speed /* = 0.0f*/)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -518,6 +543,9 @@ void MotionMaster::MoveTakeoff(uint32 id, Position const& pos, float speed /* =
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_ACTIVE);
}
/**
* @brief Use to move the unit from the air to the ground. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveTakeoff(uint32 id, float x, float y, float z, float speed /* = 0.0f*/)
{
Position pos = {x, y, z, 0.0f};
@@ -550,6 +578,9 @@ void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, floa
Mutate(new EffectMovementGenerator(0), MOTION_SLOT_CONTROLLED);
}
/**
* @brief The unit will jump in a specific direction
*/
void MotionMaster::MoveJumpTo(float angle, float speedXY, float speedZ)
{
//this function may make players fall below map
@@ -564,6 +595,9 @@ void MotionMaster::MoveJumpTo(float angle, float speedXY, float speedZ)
MoveJump(x, y, z, speedXY, speedZ);
}
/**
* @brief The unit will jump to a specific point
*/
void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id, Unit const* target)
{
LOG_DEBUG("movement.motionmaster", "Unit ({}) jump to point (X: {} Y: {} Z: {})", _owner->GetGUID().ToString(), x, y, z);
@@ -584,9 +618,11 @@ void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float spee
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED);
}
/**
* @brief The unit will fall. Used when in the air. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveFall(uint32 id /*=0*/, bool addFlagForNPC)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -625,9 +661,11 @@ void MotionMaster::MoveFall(uint32 id /*=0*/, bool addFlagForNPC)
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED);
}
/**
* @brief The unit will charge the target. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, const Movement::PointsArray* path, bool generatePath, float orientation /* = 0.0f*/, ObjectGuid targetGUID /*= ObjectGuid::Empty*/)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -646,6 +684,9 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id,
}
}
/**
* @brief The unit will charge the target. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_CHARGE*/, ObjectGuid targetGUID /*= ObjectGuid::Empty*/)
{
G3D::Vector3 dest = path.GetActualEndPosition();
@@ -696,12 +737,14 @@ void MotionMaster::MoveSeekAssistanceDistract(uint32 time)
}
}
/**
* @brief Enable the target's fleeing movement. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveFleeing(Unit* enemy, uint32 time)
{
if (!enemy)
return;
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -745,12 +788,15 @@ void MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode)
}
}
/**
* @brief Enable the target's distract movement. Doesn't work with UNIT_FLAG_DISABLE_MOVE and
* if the unit has MOTION_SLOT_CONTROLLED (generaly apply when the unit is controlled).
*/
void MotionMaster::MoveDistract(uint32 timer)
{
if (Impl[MOTION_SLOT_CONTROLLED])
return;
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -773,7 +819,7 @@ void MotionMaster::Mutate(MovementGenerator* m, MovementSlot slot)
{
bool delayed = (_top == slot && (_cleanFlag & MMCF_UPDATE));
// pussywizard: clear slot AND decrease top immediately to avoid crashes when referencing null top in DirectDelete
// clear slot AND decrease top immediately to avoid crashes when referencing null top in DirectDelete
Impl[slot] = nullptr;
while (!empty() && !top())
--_top;
@@ -797,12 +843,14 @@ void MotionMaster::Mutate(MovementGenerator* m, MovementSlot slot)
}
}
/**
* @brief Move the unit following a specific path. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MovePath(uint32 path_id, bool repeatable)
{
if (!path_id)
return;
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
@@ -825,6 +873,9 @@ void MotionMaster::MovePath(uint32 path_id, bool repeatable)
_owner->IsPlayer() ? "Player" : "Creature", _owner->GetGUID().ToString(), path_id, repeatable ? "YES" : "NO");
}
/**
* @brief Rotate the unit. You can specify the time of the rotation.
*/
void MotionMaster::MoveRotate(uint32 time, RotateDirection direction)
{
if (!time)
+9 -7
View File
@@ -330,7 +330,7 @@ void PoolGroup<T>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 trig
// Triggering object is marked as spawned at this time and can be also rolled (respawn case)
// so this need explicit check for this case
if (roll < 0 && (/*obj.guid == triggerFrom ||*/ !spawns.IsActiveObject<T>(obj.guid)))
if (roll < 0 && (obj.guid == triggerFrom || !spawns.IsActiveObject<T>(obj.guid)))
{
rolledObjects.push_back(obj);
break;
@@ -340,9 +340,9 @@ void PoolGroup<T>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 trig
if (!EqualChanced.empty() && rolledObjects.empty())
{
std::copy_if(EqualChanced.begin(), EqualChanced.end(), std::back_inserter(rolledObjects), [/*triggerFrom, */&spawns](PoolObject const& object)
std::copy_if(EqualChanced.begin(), EqualChanced.end(), std::back_inserter(rolledObjects), [triggerFrom, &spawns](PoolObject const& object)
{
return /*object.guid == triggerFrom ||*/ !spawns.IsActiveObject<T>(object.guid);
return object.guid == triggerFrom || !spawns.IsActiveObject<T>(object.guid);
});
Acore::Containers::RandomResize(rolledObjects, count);
@@ -527,16 +527,18 @@ void PoolGroup<Quest>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32
// Method that does the respawn job on the specified creature
template <>
void PoolGroup<Creature>::ReSpawn1Object(PoolObject* /*obj*/)
void PoolGroup<Creature>::ReSpawn1Object(PoolObject* obj)
{
// Creature is still on map, nothing to do
Despawn1Object(obj->guid);
Spawn1Object(obj);
}
// Method that does the respawn job on the specified gameobject
template <>
void PoolGroup<GameObject>::ReSpawn1Object(PoolObject* /*obj*/)
void PoolGroup<GameObject>::ReSpawn1Object(PoolObject* obj)
{
// Gameobject is still on map, nothing to do
Despawn1Object(obj->guid);
Spawn1Object(obj);
}
// Nothing to do for a child Pool
@@ -54,6 +54,11 @@ void ScriptMgr::OnPlayerJustDied(Player* player)
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_PLAYER_JUST_DIED, script->OnPlayerJustDied(player));
}
void ScriptMgr::OnCalculateTalentsPoints(Player const* player, uint32& talentPointsForLevel)
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_CALCULATE_TALENTS_POINTS, script->OnCalculateTalentsPoints(player, talentPointsForLevel));
}
void ScriptMgr::OnPlayerReleasedGhost(Player* player)
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_PLAYER_RELEASED_GHOST, script->OnPlayerReleasedGhost(player));
@@ -28,6 +28,7 @@
enum PlayerHook
{
PLAYERHOOK_ON_PLAYER_JUST_DIED,
PLAYERHOOK_ON_CALCULATE_TALENTS_POINTS,
PLAYERHOOK_ON_PLAYER_RELEASED_GHOST,
PLAYERHOOK_ON_SEND_INITIAL_PACKETS_BEFORE_ADD_TO_MAP,
PLAYERHOOK_ON_BATTLEGROUND_DESERTION,
@@ -213,6 +214,9 @@ public:
// Called when a player dies
virtual void OnPlayerJustDied(Player* /*player*/) { }
// Called player talent points are calculated
virtual void OnCalculateTalentsPoints(Player const* /*player*/, uint32& /*talentPointsForLevel*/) { }
// Called when clicking the release button
virtual void OnPlayerReleasedGhost(Player* /*player*/) { }
+1
View File
@@ -331,6 +331,7 @@ public: /* PlayerScript */
void OnAfterPlayerUpdate(Player* player, uint32 diff);
void OnSendInitialPacketsBeforeAddToMap(Player* player, WorldPacket& data);
void OnPlayerJustDied(Player* player);
void OnCalculateTalentsPoints(Player const* player, uint32& talentPointsForLevel);
void OnPlayerReleasedGhost(Player* player);
void OnPVPKill(Player* killer, Player* killed);
void OnPlayerPVPFlagChange(Player* player, bool state);
+21 -10
View File
@@ -3065,16 +3065,27 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
continue;
[[fallthrough]]; /// @todo: Not sure whether the fallthrough was a mistake (forgetting a break) or intended. This should be double-checked.
default:
if (spellInfo->Effects[j].CalcValue() || ((spellInfo->Effects[j].Effect == SPELL_EFFECT_INTERRUPT_CAST || spellInfo->HasAttribute(SPELL_ATTR0_CU_DONT_BREAK_STEALTH)) && !spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)))
if (spellInfo->Id != 69649 && spellInfo->Id != 71056 && spellInfo->Id != 71057 && spellInfo->Id != 71058 && spellInfo->Id != 73061 && spellInfo->Id != 73062 && spellInfo->Id != 73063 && spellInfo->Id != 73064) // Sindragosa Frost Breath
if (spellInfo->SpellFamilyName != SPELLFAMILY_MAGE || !(spellInfo->SpellFamilyFlags[0] & 0x20)) // frostbolt
if (spellInfo->Id != 55095) // frost fever
if (spellInfo->SpellFamilyName != SPELLFAMILY_WARLOCK || !(spellInfo->SpellFamilyFlags[1] & 0x40000)) // Haunt
{
spellInfo->AttributesCu |= SPELL_ATTR0_CU_BINARY_SPELL;
break;
}
continue;
if (!(spellInfo->Effects[j].CalcValue() &&
((spellInfo->Effects[j].Effect == SPELL_EFFECT_INTERRUPT_CAST || spellInfo->HasAttribute(SPELL_ATTR0_CU_DONT_BREAK_STEALTH)) &&
!spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))))
continue;
if (spellInfo->Id == 69649 || spellInfo->Id == 71056 || spellInfo->Id == 71057 || spellInfo->Id == 71058 ||
spellInfo->Id == 73061 || spellInfo->Id == 73062 || spellInfo->Id == 73063 || spellInfo->Id == 73064)
continue;
if (spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (spellInfo->SpellFamilyFlags[0] & 0x20)) // Frostbolt
continue;
if (spellInfo->Id == 55095) // Frost Fever
continue;
if (spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK &&
((spellInfo->SpellFamilyFlags[1] & 0x40000) || (spellInfo->SpellFamilyFlags[0] & 0x4000))) // Haunt/Drain Soul
continue;
spellInfo->AttributesCu |= SPELL_ATTR0_CU_BINARY_SPELL;
break;
}
}
}
+7 -17
View File
@@ -2800,7 +2800,7 @@ public:
return true;
}
static bool HandleDamageCommand(ChatHandler* handler, uint32 damage)
static bool HandleDamageCommand(ChatHandler* handler, uint32 damage, Optional<std::string> percent)
{
Unit* target = handler->getSelectedUnit();
if (!target || !handler->GetSession()->GetPlayer()->GetTarget())
@@ -2810,34 +2810,24 @@ public:
}
if (target->IsPlayer())
{
if (handler->HasLowerSecurity(target->ToPlayer()))
{
return false;
}
}
if (!target->IsAlive())
{
if (!target->IsAlive() || !damage)
return true;
}
if (!damage)
{
return true;
}
if (target->GetTypeId() == TYPEID_UNIT && handler->GetSession()->GetSecurity() == SEC_CONSOLE) // pussywizard
{
target->ToCreature()->LowerPlayerDamageReq(target->GetMaxHealth());
}
if (percent)
if (StringStartsWith("pct", *percent))
if (damage <= 100)
damage = target->CountPctFromMaxHealth(damage);
Unit::DealDamage(handler->GetSession()->GetPlayer(), target, damage, nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false, true);
if (target != handler->GetSession()->GetPlayer())
{
handler->GetSession()->GetPlayer()->SendAttackStateUpdate(HITINFO_AFFECTS_VICTIM, target, 1, SPELL_SCHOOL_MASK_NORMAL, damage, 0, 0, VICTIMSTATE_HIT, 0);
}
return true;
}
@@ -157,7 +157,6 @@ void AddSC_isle_of_queldanas();
void AddSC_redridge_mountains();
void AddSC_silverpine_forest();
void AddSC_stormwind_city();
void AddSC_tirisfal_glades();
void AddSC_undercity();
void AddSC_western_plaguelands();
void AddSC_westfall();
@@ -309,7 +308,6 @@ void AddEasternKingdomsScripts()
AddSC_redridge_mountains();
AddSC_silverpine_forest();
AddSC_stormwind_city();
AddSC_tirisfal_glades();
AddSC_undercity();
AddSC_western_plaguelands();
AddSC_westfall();
@@ -1,174 +0,0 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Tirisfal_Glades
SD%Complete: 100
SDComment: Quest support: 590, 1819
SDCategory: Tirisfal Glades
EndScriptData */
/* ContentData
npc_calvin_montague
go_mausoleum_door
go_mausoleum_trigger
EndContentData */
#include "CreatureScript.h"
#include "Player.h"
#include "ScriptedCreature.h"
/*######
## npc_calvin_montague
######*/
enum Calvin
{
SAY_COMPLETE = 0,
SPELL_DRINK = 2639, // possibly not correct spell (but iconId is correct)
QUEST_590 = 590
};
class npc_calvin_montague : public CreatureScript
{
public:
npc_calvin_montague() : CreatureScript("npc_calvin_montague") { }
bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest) override
{
if (quest->GetQuestId() == QUEST_590)
{
creature->SetFaction(FACTION_ENEMY);
creature->SetImmuneToPC(false);
CAST_AI(npc_calvin_montague::npc_calvin_montagueAI, creature->AI())->AttackStart(player);
}
return true;
}
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_calvin_montagueAI(creature);
}
struct npc_calvin_montagueAI : public ScriptedAI
{
npc_calvin_montagueAI(Creature* creature) : ScriptedAI(creature) { }
uint32 m_uiPhase;
uint32 m_uiPhaseTimer;
ObjectGuid m_uiPlayerGUID;
void Reset() override
{
m_uiPhase = 0;
m_uiPhaseTimer = 5000;
m_uiPlayerGUID.Clear();
me->RestoreFaction();
if (!me->IsImmuneToPC())
me->SetImmuneToPC(true);
}
void JustEngagedWith(Unit* /*who*/) override { }
void AttackedBy(Unit* pAttacker) override
{
if (me->GetVictim() || me->IsFriendlyTo(pAttacker))
return;
AttackStart(pAttacker);
}
void DamageTaken(Unit* pDoneBy, uint32& uiDamage, DamageEffectType, SpellSchoolMask) override
{
if (!pDoneBy)
return;
if (uiDamage >= me->GetHealth() || me->HealthBelowPctDamaged(15, uiDamage))
{
uiDamage = 0;
me->RestoreFaction();
me->SetImmuneToPC(true);
me->CombatStop(true);
m_uiPhase = 1;
if (pDoneBy->IsPlayer())
{
m_uiPlayerGUID = pDoneBy->GetGUID();
}
else if (pDoneBy->IsPet())
{
if (Unit* owner = pDoneBy->GetOwner())
{
// not sure if this is needed.
if (owner->IsPlayer())
{
m_uiPlayerGUID = owner->GetGUID();
}
}
}
}
}
void UpdateAI(uint32 diff) override
{
if (m_uiPhase)
{
if (m_uiPhaseTimer <= diff)
m_uiPhaseTimer = 7500;
else
{
m_uiPhaseTimer -= diff;
return;
}
switch (m_uiPhase)
{
case 1:
Talk(SAY_COMPLETE);
++m_uiPhase;
break;
case 2:
if (Player* player = ObjectAccessor::GetPlayer(*me, m_uiPlayerGUID))
player->AreaExploredOrEventHappens(QUEST_590);
DoCast(me, SPELL_DRINK, true);
++m_uiPhase;
break;
case 3:
EnterEvadeMode();
break;
}
return;
}
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
};
};
void AddSC_tirisfal_glades()
{
new npc_calvin_montague();
}
@@ -0,0 +1,753 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CreatureScript.h"
#include "ScriptedCreature.h"
#include "SpellScriptLoader.h"
#include "black_temple.h"
#include "Player.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
enum Says
{
SAY_COUNCIL_AGGRO = 0,
SAY_COUNCIL_ENRAGE = 1,
SAY_COUNCIL_SPECIAL = 2,
SAY_COUNCIL_SLAY = 3,
SAY_COUNCIL_DEATH = 4
};
enum Spells
{
SPELL_EMPYREAL_EQUIVALENCY = 41333,
SPELL_SHARED_RULE_DMG = 41342,
SPELL_SHARED_RULE_HEAL = 41343,
SPELL_EMPYREAL_BALANCE = 41499,
SPELL_BERSERK = 41924,
// Gathios the Shatterer
SPELL_BLESSING_OF_PROTECTION = 41450,
SPELL_BLESSING_OF_SPELL_WARDING = 41451,
SPELL_CONSECRATION = 41541,
SPELL_HAMMER_OF_JUSTICE = 41468,
SPELL_SEAL_OF_COMMAND = 41469,
SPELL_SEAL_OF_BLOOD = 41459,
SPELL_CHROMATIC_RESISTANCE_AURA = 41453,
SPELL_DEVOTION_AURA = 41452,
SPELL_JUDGEMENT = 41467,
// High Nethermancer Zerevor
SPELL_FLAMESTRIKE = 41481,
SPELL_BLIZZARD = 41482,
SPELL_ARCANE_BOLT = 41483,
SPELL_ARCANE_EXPLOSION = 41524,
SPELL_DAMPEN_MAGIC = 41478,
// Lady Malande
SPELL_EMPOWERED_SMITE = 41471,
SPELL_CIRCLE_OF_HEALING = 41455,
SPELL_REFLECTIVE_SHIELD = 41475,
SPELL_REFLECTIVE_SHIELD_T = 33619,
SPELL_DIVINE_WRATH = 41472,
SPELL_HEAL_VISUAL = 24171,
// Veras Darkshadow
SPELL_DEADLY_STRIKE = 41480,
SPELL_DEADLY_POISON = 41485,
SPELL_ENVENOM = 41487,
SPELL_VANISH = 41476,
SPELL_VANISH_OUT = 41479,
SPELL_VANISH_VISUAL = 24222
};
enum Misc
{
ACTION_START_ENCOUNTER = 1,
ACTION_END_ENCOUNTER = 2,
ACTION_ENRAGE = 3,
EVENT_SPELL_BLESSING = 1,
EVENT_SPELL_AURA = 2,
EVENT_SPELL_SEAL = 3,
EVENT_SPELL_HAMMER_OF_JUSTICE = 4,
EVENT_SPELL_JUDGEMENT = 5,
EVENT_SPELL_CONSECRATION = 6,
EVENT_SPELL_FLAMESTRIKE = 10,
EVENT_SPELL_BLIZZARD = 11,
EVENT_SPELL_ARCANE_BOLT = 12,
EVENT_SPELL_DAMPEN_MAGIC = 13,
EVENT_SPELL_ARCANE_EXPLOSION = 14,
EVENT_SPELL_REFLECTIVE_SHIELD = 20,
EVENT_SPELL_CIRCLE_OF_HEALING = 21,
EVENT_SPELL_DIVINE_WRATH = 22,
EVENT_SPELL_EMPOWERED_SMITE = 23,
EVENT_SPELL_VANISH = 30,
EVENT_SPELL_VANISH_OUT = 31,
EVENT_SPELL_ENRAGE = 32,
EVENT_KILL_TALK = 100
};
class VerasEnvenom : public BasicEvent
{
public:
VerasEnvenom(Unit& owner, ObjectGuid targetGUID) : _owner(owner), _targetGUID(targetGUID) { }
bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override
{
if (Player* target = ObjectAccessor::GetPlayer(_owner, _targetGUID))
{
target->m_clientGUIDs.insert(_owner.GetGUID());
_owner.CastSpell(target, SPELL_ENVENOM, true);
target->RemoveAurasDueToSpell(SPELL_DEADLY_POISON);
target->m_clientGUIDs.erase(_owner.GetGUID());
}
return true;
}
private:
Unit& _owner;
ObjectGuid _targetGUID;
};
struct boss_illidari_council : public BossAI
{
boss_illidari_council(Creature* creature) : BossAI(creature, DATA_ILLIDARI_COUNCIL) { }
void EnterEvadeMode(EvadeReason why) override
{
for (uint8 i = DATA_GATHIOS_THE_SHATTERER; i <= DATA_VERAS_DARKSHADOW; ++i)
if (Creature* member = instance->GetCreature(i))
member->AI()->EnterEvadeMode();
BossAI::EnterEvadeMode(why);
}
void AttackStart(Unit*) override { }
void MoveInLineOfSight(Unit*) override { }
void DoAction(int32 param) override
{
if (!me->isActiveObject() && param == ACTION_START_ENCOUNTER)
{
me->setActive(true);
bool spoken = false;
me->CastSpell(me, SPELL_EMPYREAL_BALANCE, true);
ScheduleTimedEvent(3200ms, [&]
{
me->CastSpell(me, SPELL_EMPYREAL_EQUIVALENCY, true);
}, 3200ms);
for (uint8 i = DATA_GATHIOS_THE_SHATTERER; i <= DATA_VERAS_DARKSHADOW; ++i)
{
if (Creature* member = instance->GetCreature(i))
{
if (!spoken && (roll_chance_i(33) || i == 3))
{
spoken = true;
member->AI()->Talk(SAY_COUNCIL_AGGRO);
}
member->SetOwnerGUID(me->GetGUID());
member->SetInCombatWithZone();
}
}
}
else if (param == ACTION_ENRAGE)
{
for (uint8 i = DATA_GATHIOS_THE_SHATTERER; i <= DATA_VERAS_DARKSHADOW; ++i)
if (Creature* member = instance->GetCreature(i))
member->AI()->DoAction(ACTION_ENRAGE);
}
else if (param == ACTION_END_ENCOUNTER)
{
me->setActive(false);
for (uint8 i = DATA_GATHIOS_THE_SHATTERER; i <= DATA_VERAS_DARKSHADOW; ++i)
if (Creature* member = instance->GetCreature(i))
if (member->IsAlive())
Unit::Kill(me, member);
me->KillSelf();
}
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override
{
if (me->GetHealth() <= damage)
{
damage = me->GetHealth() - 1;
DoAction(ACTION_END_ENCOUNTER);
}
}
void UpdateAI(uint32 diff) override
{
if (!me->isActiveObject())
return;
if (!SelectTargetFromPlayerList(115.0f))
{
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
return;
}
scheduler.Update(diff);
}
};
struct boss_illidari_council_memberAI : public ScriptedAI
{
boss_illidari_council_memberAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
SetBoundary(instance->GetBossBoundary(DATA_ILLIDARI_COUNCIL));
}
InstanceScript* instance;
EventMap events;
void Reset() override
{
events.Reset();
}
void EnterEvadeMode(EvadeReason why) override
{
me->SetOwnerGUID(ObjectGuid::Empty);
ScriptedAI::EnterEvadeMode(why);
}
void DoAction(int32 param) override
{
if (param == ACTION_ENRAGE)
{
me->CastSpell(me, SPELL_BERSERK, true);
Talk(SAY_COUNCIL_ENRAGE);
}
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override
{
InstanceScript* instance = me->GetInstanceScript();
if (me->GetHealth() <= damage)
damage = me->GetHealth() - 1;
int32 damageTaken = damage;
Creature* target = instance->GetCreature(DATA_ILLIDARI_COUNCIL);
me->CastCustomSpell(target->ToUnit(), SPELL_SHARED_RULE_DMG, &damageTaken, &damageTaken, &damageTaken, true, nullptr, nullptr, me->GetGUID());
}
void KilledUnit(Unit*) override
{
if (events.GetNextEventTime(EVENT_KILL_TALK) == 0)
{
Talk(SAY_COUNCIL_SLAY);
events.ScheduleEvent(EVENT_KILL_TALK, 6000);
}
}
void JustDied(Unit*) override
{
Talk(SAY_COUNCIL_DEATH);
}
void JustEngagedWith(Unit* /*who*/) override
{
if (Creature* council = instance->GetCreature(DATA_ILLIDARI_COUNCIL))
council->GetAI()->DoAction(ACTION_START_ENCOUNTER);
}
};
struct boss_gathios_the_shatterer : public boss_illidari_council_memberAI
{
boss_gathios_the_shatterer(Creature* creature) : boss_illidari_council_memberAI(creature)
{
_toggleBlessing = RAND(true, false);
_toggleAura = RAND(true, false);
_toggleSeal = RAND(true, false);
}
Creature* SelectCouncilMember()
{
if (roll_chance_i(50))
return instance->GetCreature(DATA_LADY_MALANDE);
if (roll_chance_i(20))
if (Creature* veras = instance->GetCreature(DATA_VERAS_DARKSHADOW))
if (!veras->HasAura(SPELL_VANISH))
return veras;
return instance->GetCreature(RAND(DATA_GATHIOS_THE_SHATTERER, DATA_HIGH_NETHERMANCER_ZEREVOR));
}
void JustEngagedWith(Unit* who) override
{
boss_illidari_council_memberAI::JustEngagedWith(who);
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
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_BLESSING:
if (Unit* member = SelectCouncilMember())
{
me->CastSpell(member, _toggleBlessing ? SPELL_BLESSING_OF_PROTECTION : SPELL_BLESSING_OF_SPELL_WARDING);
_toggleBlessing = !_toggleBlessing;
}
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, 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, 30000);
break;
case EVENT_SPELL_HAMMER_OF_JUSTICE:
if (Unit* target = me->GetVictim())
if (target->IsPlayer() && me->IsInRange(target, 10.0f, 40.0f, true))
{
me->CastSpell(target, SPELL_HAMMER_OF_JUSTICE);
events.ScheduleEvent(EVENT_SPELL_HAMMER_OF_JUSTICE, 20s);
break;
}
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, 20000);
break;
case EVENT_SPELL_JUDGEMENT:
me->CastSpell(me->GetVictim(), SPELL_JUDGEMENT, false);
events.ScheduleEvent(EVENT_SPELL_JUDGEMENT, 20000);
break;
}
DoMeleeAttackIfReady();
}
private:
bool _toggleBlessing;
bool _toggleAura;
bool _toggleSeal;
};
struct boss_high_nethermancer_zerevor : public boss_illidari_council_memberAI
{
boss_high_nethermancer_zerevor(Creature* creature) : boss_illidari_council_memberAI(creature) { }
void AttackStart(Unit* who) override
{
if (who && me->Attack(who, true))
me->GetMotionMaster()->MoveChase(who, 20.0f);
}
void JustEngagedWith(Unit* who) override
{
boss_illidari_council_memberAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 25000);
events.ScheduleEvent(EVENT_SPELL_BLIZZARD, 5000);
events.ScheduleEvent(EVENT_SPELL_ARCANE_BOLT, 15000);
events.ScheduleEvent(EVENT_SPELL_DAMPEN_MAGIC, 0);
events.ScheduleEvent(EVENT_SPELL_ARCANE_EXPLOSION, 10000);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_DAMPEN_MAGIC:
me->CastSpell(me, SPELL_DAMPEN_MAGIC, false);
events.ScheduleEvent(EVENT_SPELL_DAMPEN_MAGIC, 120000);
break;
case EVENT_SPELL_ARCANE_BOLT:
me->CastSpell(me->GetVictim(), SPELL_ARCANE_BOLT, false);
events.ScheduleEvent(EVENT_SPELL_ARCANE_BOLT, 3000);
break;
case EVENT_SPELL_FLAMESTRIKE:
if (roll_chance_i(50))
Talk(SAY_COUNCIL_SPECIAL);
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f))
me->CastSpell(target, SPELL_FLAMESTRIKE, false);
events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 40000);
break;
case EVENT_SPELL_BLIZZARD:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f))
me->CastSpell(target, SPELL_BLIZZARD, false);
events.ScheduleEvent(EVENT_SPELL_BLIZZARD, 40000);
break;
case EVENT_SPELL_ARCANE_EXPLOSION:
if (SelectTarget(SelectTargetMethod::Random, 0, 10.0f))
me->CastSpell(me, SPELL_ARCANE_EXPLOSION, false);
events.ScheduleEvent(EVENT_SPELL_ARCANE_EXPLOSION, 10000);
break;
}
DoMeleeAttackIfReady();
}
};
struct boss_lady_malande : public boss_illidari_council_memberAI
{
boss_lady_malande(Creature* creature) : boss_illidari_council_memberAI(creature) { }
void AttackStart(Unit* who) override
{
if (who && me->Attack(who, true))
me->GetMotionMaster()->MoveChase(who, 20.0f);
}
void JustEngagedWith(Unit* who) override
{
boss_illidari_council_memberAI::JustEngagedWith(who);
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
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_CIRCLE_OF_HEALING:
me->CastSpell(me, SPELL_CIRCLE_OF_HEALING, false);
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, 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, 20000);
break;
case EVENT_SPELL_EMPOWERED_SMITE:
me->CastSpell(me->GetVictim(), SPELL_EMPOWERED_SMITE, false);
events.ScheduleEvent(EVENT_SPELL_EMPOWERED_SMITE, 3000);
break;
}
}
};
struct boss_veras_darkshadow : public boss_illidari_council_memberAI
{
boss_veras_darkshadow(Creature* creature) : boss_illidari_council_memberAI(creature) { }
void JustEngagedWith(Unit* who) override
{
me->SetCanDualWield(true);
boss_illidari_council_memberAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_SPELL_VANISH, 10000);
events.ScheduleEvent(EVENT_SPELL_ENRAGE, 900000);
}
void JustSummoned(Creature* summon) override
{
summon->CastSpell(summon, SPELL_VANISH_VISUAL, true);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_VANISH:
if (roll_chance_i(50))
Talk(SAY_COUNCIL_SPECIAL);
me->CastSpell(me, SPELL_DEADLY_STRIKE, false);
me->CastSpell(me, SPELL_VANISH, false);
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);
break;
case EVENT_SPELL_ENRAGE:
DoResetThreatList();
if (Creature* council = instance->GetCreature(DATA_ILLIDARI_COUNCIL))
council->GetAI()->DoAction(ACTION_ENRAGE);
break;
}
if (events.GetNextEventTime(EVENT_SPELL_VANISH_OUT) == 0)
DoMeleeAttackIfReady();
}
};
class spell_illidari_council_balance_of_power_aura : public AuraScript
{
PrepareAuraScript(spell_illidari_council_balance_of_power_aura);
void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
{
// Set absorption amount to unlimited (no absorb)
amount = -1;
}
void Register() override
{
DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_illidari_council_balance_of_power_aura::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
}
};
class spell_illidari_council_empyreal_balance : public SpellScript
{
PrepareSpellScript(spell_illidari_council_empyreal_balance);
bool Load() override
{
_targetCount = 0;
return GetCaster()->GetTypeId() == TYPEID_UNIT;
}
void HandleDummy(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
if (GetHitUnit())
{
_targetCount++;
}
}
void HandleAfterCast()
{
if (_targetCount != 4)
{
GetCaster()->ToCreature()->AI()->EnterEvadeMode();
return;
}
std::list<TargetInfo> const* targetsInfo = GetSpell()->GetUniqueTargetInfo();
for (std::list<TargetInfo>::const_iterator ihit = targetsInfo->begin(); ihit != targetsInfo->end(); ++ihit)
if (Creature* target = ObjectAccessor::GetCreature(*GetCaster(), ihit->targetGUID))
{
target->SetMaxHealth(GetCaster()->GetMaxHealth() / _targetCount);
target->SetHealth(GetCaster()->GetHealth() / _targetCount);
target->LowerPlayerDamageReq(target->GetMaxHealth());
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidari_council_empyreal_balance::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
AfterCast += SpellCastFn(spell_illidari_council_empyreal_balance::HandleAfterCast);
}
private:
uint8 _targetCount;
};
class spell_illidari_council_empyreal_equivalency : public SpellScript
{
PrepareSpellScript(spell_illidari_council_empyreal_equivalency);
bool Load() override
{
_targetCount = 0;
return GetCaster()->GetTypeId() == TYPEID_UNIT;
}
void HandleDummy(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
if (GetHitUnit())
{
_targetCount++;
}
}
void HandleAfterCast()
{
if (_targetCount != 4)
{
return;
}
std::list<TargetInfo> const* targetsInfo = GetSpell()->GetUniqueTargetInfo();
for (std::list<TargetInfo>::const_iterator ihit = targetsInfo->begin(); ihit != targetsInfo->end(); ++ihit)
if (Creature* target = ObjectAccessor::GetCreature(*GetCaster(), ihit->targetGUID))
target->SetHealth(GetCaster()->GetHealth() / _targetCount);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidari_council_empyreal_equivalency::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
AfterCast += SpellCastFn(spell_illidari_council_empyreal_equivalency::HandleAfterCast);
}
private:
uint8 _targetCount;
};
class spell_illidari_council_reflective_shield_aura : public AuraScript
{
PrepareAuraScript(spell_illidari_council_reflective_shield_aura);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_REFLECTIVE_SHIELD_T });
}
void ReflectDamage(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)
{
Unit* target = GetTarget();
if (dmgInfo.GetAttacker() == target)
return;
int32 bp = absorbAmount / 2;
target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_REFLECTIVE_SHIELD_T, &bp, nullptr, nullptr, true, nullptr, aurEff);
}
void Register() override
{
AfterEffectAbsorb += AuraEffectAbsorbFn(spell_illidari_council_reflective_shield_aura::ReflectDamage, EFFECT_0);
}
};
class spell_illidari_council_circle_of_healing : public SpellScript
{
PrepareSpellScript(spell_illidari_council_circle_of_healing);
void HandleSharedRule(SpellEffIndex /*effIndex*/)
{
Unit* councilMember = GetHitUnit();
if (!councilMember)
return;
InstanceScript* instance = councilMember->GetInstanceScript();
if (!instance)
return;
Creature* target = instance->GetCreature(DATA_ILLIDARI_COUNCIL);
int32 heal = GetHitHeal();
target->CastCustomSpell(target, SPELL_SHARED_RULE_HEAL, &heal, &heal, &heal, true, nullptr, nullptr, target->GetGUID());
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidari_council_circle_of_healing::HandleSharedRule, EFFECT_0, SPELL_EFFECT_HEAL);
}
};
class spell_illidari_council_judgement : public SpellScript
{
PrepareSpellScript(spell_illidari_council_judgement);
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
auto const& auras = GetCaster()->GetAuraEffectsByType(SPELL_AURA_DUMMY);
for (auto i = auras.begin(); i != auras.end(); ++i)
{
if ((*i)->GetSpellInfo()->GetSpellSpecific() == SPELL_SPECIFIC_SEAL && (*i)->GetEffIndex() == EFFECT_2)
if (sSpellMgr->GetSpellInfo((*i)->GetAmount()))
{
GetCaster()->CastSpell(GetHitUnit(), (*i)->GetAmount(), true);
GetCaster()->RemoveAurasDueToSpell((*i)->GetSpellInfo()->Id);
break;
}
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidari_council_judgement::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_illidari_council_deadly_strike_aura : public AuraScript
{
PrepareAuraScript(spell_illidari_council_deadly_strike_aura);
void Update(AuraEffect const* effect)
{
PreventDefaultAction();
if (Unit* target = GetUnitOwner()->GetAI()->SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
{
GetUnitOwner()->CastSpell(target, GetSpellInfo()->Effects[effect->GetEffIndex()].TriggerSpell, true);
GetUnitOwner()->m_Events.AddEvent(new VerasEnvenom(*GetUnitOwner(), target->GetGUID()), GetUnitOwner()->m_Events.CalculateTime(urand(1500, 3500)));
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidari_council_deadly_strike_aura::Update, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
void AddSC_boss_illidari_council()
{
RegisterBlackTempleCreatureAI(boss_illidari_council);
RegisterBlackTempleCreatureAI(boss_gathios_the_shatterer);
RegisterBlackTempleCreatureAI(boss_lady_malande);
RegisterBlackTempleCreatureAI(boss_veras_darkshadow);
RegisterBlackTempleCreatureAI(boss_high_nethermancer_zerevor);
RegisterSpellScript(spell_illidari_council_balance_of_power_aura);
RegisterSpellScript(spell_illidari_council_empyreal_balance);
RegisterSpellScript(spell_illidari_council_empyreal_equivalency);
RegisterSpellScript(spell_illidari_council_reflective_shield_aura);
RegisterSpellScript(spell_illidari_council_circle_of_healing);
RegisterSpellScript(spell_illidari_council_judgement);
RegisterSpellScript(spell_illidari_council_deadly_strike_aura);
}
@@ -1,714 +0,0 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CreatureScript.h"
#include "ScriptedCreature.h"
#include "SpellScriptLoader.h"
#include "black_temple.h"
#include "Player.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
enum Says
{
SAY_COUNCIL_AGGRO = 0,
SAY_COUNCIL_ENRAGE = 1,
SAY_COUNCIL_SPECIAL = 2,
SAY_COUNCIL_SLAY = 3,
SAY_COUNCIL_DEATH = 4
};
enum Spells
{
SPELL_EMPYREAL_BALANCE = 41499,
SPELL_BERSERK = 41924,
// Gathios the Shatterer
SPELL_BLESSING_OF_PROTECTION = 41450,
SPELL_BLESSING_OF_SPELL_WARDING = 41451,
SPELL_CONSECRATION = 41541,
SPELL_HAMMER_OF_JUSTICE = 41468,
SPELL_SEAL_OF_COMMAND = 41469,
SPELL_SEAL_OF_BLOOD = 41459,
SPELL_CHROMATIC_RESISTANCE_AURA = 41453,
SPELL_DEVOTION_AURA = 41452,
SPELL_JUDGEMENT = 41467,
// High Nethermancer Zerevor
SPELL_FLAMESTRIKE = 41481,
SPELL_BLIZZARD = 41482,
SPELL_ARCANE_BOLT = 41483,
SPELL_ARCANE_EXPLOSION = 41524,
SPELL_DAMPEN_MAGIC = 41478,
// Lady Malande
SPELL_EMPOWERED_SMITE = 41471,
SPELL_CIRCLE_OF_HEALING = 41455,
SPELL_REFLECTIVE_SHIELD = 41475,
SPELL_REFLECTIVE_SHIELD_T = 33619,
SPELL_DIVINE_WRATH = 41472,
SPELL_HEAL_VISUAL = 24171,
// Veras Darkshadow
SPELL_DEADLY_STRIKE = 41480,
SPELL_DEADLY_POISON = 41485,
SPELL_ENVENOM = 41487,
SPELL_VANISH = 41476,
SPELL_VANISH_OUT = 41479,
SPELL_VANISH_VISUAL = 24222
};
enum Misc
{
ACTION_START_ENCOUNTER = 1,
ACTION_END_ENCOUNTER = 2,
ACTION_ENRAGE = 3,
EVENT_SPELL_BLESSING = 1,
EVENT_SPELL_AURA = 2,
EVENT_SPELL_SEAL = 3,
EVENT_SPELL_HAMMER_OF_JUSTICE = 4,
EVENT_SPELL_JUDGEMENT = 5,
EVENT_SPELL_CONSECRATION = 6,
EVENT_SPELL_FLAMESTRIKE = 10,
EVENT_SPELL_BLIZZARD = 11,
EVENT_SPELL_ARCANE_BOLT = 12,
EVENT_SPELL_DAMPEN_MAGIC = 13,
EVENT_SPELL_ARCANE_EXPLOSION = 14,
EVENT_SPELL_REFLECTIVE_SHIELD = 20,
EVENT_SPELL_CIRCLE_OF_HEALING = 21,
EVENT_SPELL_DIVINE_WRATH = 22,
EVENT_SPELL_EMPOWERED_SMITE = 23,
EVENT_SPELL_VANISH = 30,
EVENT_SPELL_VANISH_OUT = 31,
EVENT_SPELL_ENRAGE = 32,
EVENT_KILL_TALK = 100
};
class VerasEnvenom : public BasicEvent
{
public:
VerasEnvenom(Unit& owner, ObjectGuid targetGUID) : _owner(owner), _targetGUID(targetGUID) { }
bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override
{
if (Player* target = ObjectAccessor::GetPlayer(_owner, _targetGUID))
{
target->m_clientGUIDs.insert(_owner.GetGUID());
_owner.CastSpell(target, SPELL_ENVENOM, true);
target->RemoveAurasDueToSpell(SPELL_DEADLY_POISON);
target->m_clientGUIDs.erase(_owner.GetGUID());
}
return true;
}
private:
Unit& _owner;
ObjectGuid _targetGUID;
};
class boss_illidari_council : public CreatureScript
{
public:
boss_illidari_council() : CreatureScript("boss_illidari_council") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetBlackTempleAI<boss_illidari_councilAI>(creature);
}
struct boss_illidari_councilAI : public BossAI
{
boss_illidari_councilAI(Creature* creature) : BossAI(creature, DATA_ILLIDARI_COUNCIL) { }
void EnterEvadeMode(EvadeReason why) override
{
for (uint8 i = DATA_GATHIOS_THE_SHATTERER; i <= DATA_VERAS_DARKSHADOW; ++i)
if (Creature* member = instance->GetCreature(i))
member->AI()->EnterEvadeMode();
BossAI::EnterEvadeMode(why);
}
void AttackStart(Unit*) override { }
void MoveInLineOfSight(Unit*) override { }
void DoAction(int32 param) override
{
if (!me->isActiveObject() && param == ACTION_START_ENCOUNTER)
{
me->setActive(true);
bool spoken = false;
for (uint8 i = DATA_GATHIOS_THE_SHATTERER; i <= DATA_VERAS_DARKSHADOW; ++i)
{
if (Creature* member = instance->GetCreature(i))
{
if (!spoken && (roll_chance_i(33) || i == 3))
{
spoken = true;
member->AI()->Talk(SAY_COUNCIL_AGGRO);
}
member->SetOwnerGUID(me->GetGUID());
member->SetInCombatWithZone();
}
}
}
else if (param == ACTION_ENRAGE)
{
for (uint8 i = DATA_GATHIOS_THE_SHATTERER; i <= DATA_VERAS_DARKSHADOW; ++i)
if (Creature* member = instance->GetCreature(i))
member->AI()->DoAction(ACTION_ENRAGE);
}
else if (param == ACTION_END_ENCOUNTER)
{
me->setActive(false);
for (uint8 i = DATA_GATHIOS_THE_SHATTERER; i <= DATA_VERAS_DARKSHADOW; ++i)
if (Creature* member = instance->GetCreature(i))
if (member->IsAlive())
Unit::Kill(me, member);
me->KillSelf();
}
}
void UpdateAI(uint32 /*diff*/) override
{
if (!me->isActiveObject())
return;
if (!SelectTargetFromPlayerList(115.0f))
{
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
return;
}
me->CastSpell(me, SPELL_EMPYREAL_BALANCE, true);
}
};
};
struct boss_illidari_council_memberAI : public ScriptedAI
{
boss_illidari_council_memberAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
SetBoundary(instance->GetBossBoundary(DATA_ILLIDARI_COUNCIL));
}
InstanceScript* instance;
EventMap events;
void Reset() override
{
events.Reset();
}
void EnterEvadeMode(EvadeReason why) override
{
me->SetOwnerGUID(ObjectGuid::Empty);
ScriptedAI::EnterEvadeMode(why);
}
void DoAction(int32 param) override
{
if (param == ACTION_ENRAGE)
{
me->CastSpell(me, SPELL_BERSERK, true);
Talk(SAY_COUNCIL_ENRAGE);
}
}
void KilledUnit(Unit*) override
{
if (events.GetNextEventTime(EVENT_KILL_TALK) == 0)
{
Talk(SAY_COUNCIL_SLAY);
events.ScheduleEvent(EVENT_KILL_TALK, 6000);
}
}
void JustDied(Unit*) override
{
Talk(SAY_COUNCIL_DEATH);
if (Creature* council = instance->GetCreature(DATA_ILLIDARI_COUNCIL))
council->GetAI()->DoAction(ACTION_END_ENCOUNTER);
}
void JustEngagedWith(Unit* /*who*/) override
{
if (Creature* council = instance->GetCreature(DATA_ILLIDARI_COUNCIL))
council->GetAI()->DoAction(ACTION_START_ENCOUNTER);
}
};
class boss_gathios_the_shatterer : public CreatureScript
{
public:
boss_gathios_the_shatterer() : CreatureScript("boss_gathios_the_shatterer") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetBlackTempleAI<boss_gathios_the_shattererAI>(creature);
}
struct boss_gathios_the_shattererAI : public boss_illidari_council_memberAI
{
boss_gathios_the_shattererAI(Creature* creature) : boss_illidari_council_memberAI(creature)
{
_toggleBlessing = RAND(true, false);
_toggleAura = RAND(true, false);
_toggleSeal = RAND(true, false);
}
Creature* SelectCouncilMember()
{
if (roll_chance_i(50))
return instance->GetCreature(DATA_LADY_MALANDE);
if (roll_chance_i(20))
if (Creature* veras = instance->GetCreature(DATA_VERAS_DARKSHADOW))
if (!veras->HasAura(SPELL_VANISH))
return veras;
return instance->GetCreature(RAND(DATA_GATHIOS_THE_SHATTERER, DATA_HIGH_NETHERMANCER_ZEREVOR));
}
void JustEngagedWith(Unit* who) override
{
boss_illidari_council_memberAI::JustEngagedWith(who);
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
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_BLESSING:
if (Unit* member = SelectCouncilMember())
{
me->CastSpell(member, _toggleBlessing ? SPELL_BLESSING_OF_PROTECTION : SPELL_BLESSING_OF_SPELL_WARDING);
_toggleBlessing = !_toggleBlessing;
}
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, 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, 30000);
break;
case EVENT_SPELL_HAMMER_OF_JUSTICE:
if (Unit* target = me->GetVictim())
if (target->IsPlayer() && me->IsInRange(target, 10.0f, 40.0f, true))
{
me->CastSpell(target, SPELL_HAMMER_OF_JUSTICE);
events.ScheduleEvent(EVENT_SPELL_HAMMER_OF_JUSTICE, 20s);
break;
}
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, 20000);
break;
case EVENT_SPELL_JUDGEMENT:
me->CastSpell(me->GetVictim(), SPELL_JUDGEMENT, false);
events.ScheduleEvent(EVENT_SPELL_JUDGEMENT, 20000);
break;
}
DoMeleeAttackIfReady();
}
private:
bool _toggleBlessing;
bool _toggleAura;
bool _toggleSeal;
};
};
class boss_high_nethermancer_zerevor : public CreatureScript
{
public:
boss_high_nethermancer_zerevor() : CreatureScript("boss_high_nethermancer_zerevor") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetBlackTempleAI<boss_high_nethermancer_zerevorAI>(creature);
}
struct boss_high_nethermancer_zerevorAI : public boss_illidari_council_memberAI
{
boss_high_nethermancer_zerevorAI(Creature* creature) : boss_illidari_council_memberAI(creature) { }
void AttackStart(Unit* who) override
{
if (who && me->Attack(who, true))
me->GetMotionMaster()->MoveChase(who, 20.0f);
}
void JustEngagedWith(Unit* who) override
{
boss_illidari_council_memberAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 25000);
events.ScheduleEvent(EVENT_SPELL_BLIZZARD, 5000);
events.ScheduleEvent(EVENT_SPELL_ARCANE_BOLT, 15000);
events.ScheduleEvent(EVENT_SPELL_DAMPEN_MAGIC, 0);
events.ScheduleEvent(EVENT_SPELL_ARCANE_EXPLOSION, 10000);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_DAMPEN_MAGIC:
me->CastSpell(me, SPELL_DAMPEN_MAGIC, false);
events.ScheduleEvent(EVENT_SPELL_DAMPEN_MAGIC, 120000);
break;
case EVENT_SPELL_ARCANE_BOLT:
me->CastSpell(me->GetVictim(), SPELL_ARCANE_BOLT, false);
events.ScheduleEvent(EVENT_SPELL_ARCANE_BOLT, 3000);
break;
case EVENT_SPELL_FLAMESTRIKE:
if (roll_chance_i(50))
Talk(SAY_COUNCIL_SPECIAL);
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f))
me->CastSpell(target, SPELL_FLAMESTRIKE, false);
events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 40000);
break;
case EVENT_SPELL_BLIZZARD:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f))
me->CastSpell(target, SPELL_BLIZZARD, false);
events.ScheduleEvent(EVENT_SPELL_BLIZZARD, 40000);
break;
case EVENT_SPELL_ARCANE_EXPLOSION:
if (SelectTarget(SelectTargetMethod::Random, 0, 10.0f))
me->CastSpell(me, SPELL_ARCANE_EXPLOSION, false);
events.ScheduleEvent(EVENT_SPELL_ARCANE_EXPLOSION, 10000);
break;
}
DoMeleeAttackIfReady();
}
};
};
class boss_lady_malande : public CreatureScript
{
public:
boss_lady_malande() : CreatureScript("boss_lady_malande") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetBlackTempleAI<boss_lady_malandeAI>(creature);
}
struct boss_lady_malandeAI : public boss_illidari_council_memberAI
{
boss_lady_malandeAI(Creature* creature) : boss_illidari_council_memberAI(creature) { }
void AttackStart(Unit* who) override
{
if (who && me->Attack(who, true))
me->GetMotionMaster()->MoveChase(who, 20.0f);
}
void JustEngagedWith(Unit* who) override
{
boss_illidari_council_memberAI::JustEngagedWith(who);
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
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_CIRCLE_OF_HEALING:
me->CastSpell(me, SPELL_CIRCLE_OF_HEALING, false);
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, 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, 20000);
break;
case EVENT_SPELL_EMPOWERED_SMITE:
me->CastSpell(me->GetVictim(), SPELL_EMPOWERED_SMITE, false);
events.ScheduleEvent(EVENT_SPELL_EMPOWERED_SMITE, 3000);
break;
}
}
};
};
class boss_veras_darkshadow : public CreatureScript
{
public:
boss_veras_darkshadow() : CreatureScript("boss_veras_darkshadow") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetBlackTempleAI<boss_veras_darkshadowAI>(creature);
}
struct boss_veras_darkshadowAI : public boss_illidari_council_memberAI
{
boss_veras_darkshadowAI(Creature* creature) : boss_illidari_council_memberAI(creature) { }
void JustEngagedWith(Unit* who) override
{
me->SetCanDualWield(true);
boss_illidari_council_memberAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_SPELL_VANISH, 10000);
events.ScheduleEvent(EVENT_SPELL_ENRAGE, 900000);
}
void JustSummoned(Creature* summon) override
{
summon->CastSpell(summon, SPELL_VANISH_VISUAL, true);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_VANISH:
if (roll_chance_i(50))
Talk(SAY_COUNCIL_SPECIAL);
me->CastSpell(me, SPELL_DEADLY_STRIKE, false);
me->CastSpell(me, SPELL_VANISH, false);
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);
break;
case EVENT_SPELL_ENRAGE:
DoResetThreatList();
if (Creature* council = instance->GetCreature(DATA_ILLIDARI_COUNCIL))
council->GetAI()->DoAction(ACTION_ENRAGE);
break;
}
if (events.GetNextEventTime(EVENT_SPELL_VANISH_OUT) == 0)
DoMeleeAttackIfReady();
}
};
};
class spell_illidari_council_balance_of_power_aura : public AuraScript
{
PrepareAuraScript(spell_illidari_council_balance_of_power_aura);
void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
{
// Set absorbtion amount to unlimited (no absorb)
amount = -1;
}
void Register() override
{
DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_illidari_council_balance_of_power_aura::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
}
};
class spell_illidari_council_empyreal_balance : public SpellScript
{
PrepareSpellScript(spell_illidari_council_empyreal_balance);
bool Load() override
{
_sharedHealth = 0;
_sharedHealthMax = 0;
_targetCount = 0;
return GetCaster()->GetTypeId() == TYPEID_UNIT;
}
void HandleDummy(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
if (Unit* target = GetHitUnit())
{
_targetCount++;
_sharedHealth += target->GetHealth();
_sharedHealthMax += target->GetMaxHealth();
}
}
void HandleAfterCast()
{
if (_targetCount != 4)
{
GetCaster()->ToCreature()->AI()->EnterEvadeMode();
return;
}
float pct = (_sharedHealth / _sharedHealthMax) * 100.0f;
std::list<TargetInfo> const* targetsInfo = GetSpell()->GetUniqueTargetInfo();
for (std::list<TargetInfo>::const_iterator ihit = targetsInfo->begin(); ihit != targetsInfo->end(); ++ihit)
if (Creature* target = ObjectAccessor::GetCreature(*GetCaster(), ihit->targetGUID))
{
target->LowerPlayerDamageReq(target->GetMaxHealth());
target->SetHealth(CalculatePct(target->GetMaxHealth(), pct));
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidari_council_empyreal_balance::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
AfterCast += SpellCastFn(spell_illidari_council_empyreal_balance::HandleAfterCast);
}
private:
float _sharedHealth;
float _sharedHealthMax;
uint8 _targetCount;
};
class spell_illidari_council_reflective_shield_aura : public AuraScript
{
PrepareAuraScript(spell_illidari_council_reflective_shield_aura);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_REFLECTIVE_SHIELD_T });
}
void ReflectDamage(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)
{
Unit* target = GetTarget();
if (dmgInfo.GetAttacker() == target)
return;
int32 bp = absorbAmount / 2;
target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_REFLECTIVE_SHIELD_T, &bp, nullptr, nullptr, true, nullptr, aurEff);
}
void Register() override
{
AfterEffectAbsorb += AuraEffectAbsorbFn(spell_illidari_council_reflective_shield_aura::ReflectDamage, EFFECT_0);
}
};
class spell_illidari_council_judgement : public SpellScript
{
PrepareSpellScript(spell_illidari_council_judgement);
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
auto const& auras = GetCaster()->GetAuraEffectsByType(SPELL_AURA_DUMMY);
for (auto i = auras.begin(); i != auras.end(); ++i)
{
if ((*i)->GetSpellInfo()->GetSpellSpecific() == SPELL_SPECIFIC_SEAL && (*i)->GetEffIndex() == EFFECT_2)
if (sSpellMgr->GetSpellInfo((*i)->GetAmount()))
{
GetCaster()->CastSpell(GetHitUnit(), (*i)->GetAmount(), true);
GetCaster()->RemoveAurasDueToSpell((*i)->GetSpellInfo()->Id);
break;
}
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidari_council_judgement::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_illidari_council_deadly_strike_aura : public AuraScript
{
PrepareAuraScript(spell_illidari_council_deadly_strike_aura);
void Update(AuraEffect const* effect)
{
PreventDefaultAction();
if (Unit* target = GetUnitOwner()->GetAI()->SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
{
GetUnitOwner()->CastSpell(target, GetSpellInfo()->Effects[effect->GetEffIndex()].TriggerSpell, true);
GetUnitOwner()->m_Events.AddEvent(new VerasEnvenom(*GetUnitOwner(), target->GetGUID()), GetUnitOwner()->m_Events.CalculateTime(urand(1500, 3500)));
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidari_council_deadly_strike_aura::Update, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
void AddSC_boss_illidari_council()
{
new boss_illidari_council();
new boss_gathios_the_shatterer();
new boss_lady_malande();
new boss_veras_darkshadow();
new boss_high_nethermancer_zerevor();
RegisterSpellScript(spell_illidari_council_balance_of_power_aura);
RegisterSpellScript(spell_illidari_council_empyreal_balance);
RegisterSpellScript(spell_illidari_council_reflective_shield_aura);
RegisterSpellScript(spell_illidari_council_judgement);
RegisterSpellScript(spell_illidari_council_deadly_strike_aura);
}
+8 -5
View File
@@ -1195,29 +1195,31 @@ class spell_gen_haunted_aura : public AuraScript
};
/* 39228 - Argussian Compass
60218 - Essence of Gossamer */
60218 - Essence of Gossamer
64765 - The General's Heart */
class spell_gen_absorb0_hitlimit1 : public AuraScript
{
PrepareAuraScript(spell_gen_absorb0_hitlimit1);
uint32 limit;
bool Load() override
{
// Max absorb stored in 1 dummy effect
limit = GetSpellInfo()->Effects[EFFECT_1].CalcValue();
_limit = GetSpellInfo()->Effects[EFFECT_1].CalcValue();
return true;
}
void Absorb(AuraEffect* /*aurEff*/, DamageInfo& /*dmgInfo*/, uint32& absorbAmount)
{
absorbAmount = std::min(limit, absorbAmount);
absorbAmount = std::min(_limit, absorbAmount);
}
void Register() override
{
OnEffectAbsorb += AuraEffectAbsorbFn(spell_gen_absorb0_hitlimit1::Absorb, EFFECT_0);
}
private:
uint32 _limit;
};
enum AdaptiveWarding
@@ -5345,6 +5347,7 @@ void AddSC_generic_spell_scripts()
RegisterSpellScript(spell_gen_area_aura_select_players_and_caster);
RegisterSpellScriptWithArgs(spell_gen_select_target_count, "spell_gen_select_target_count_15_1", TARGET_UNIT_SRC_AREA_ENEMY, 1);
RegisterSpellScriptWithArgs(spell_gen_select_target_count, "spell_gen_select_target_count_15_2", TARGET_UNIT_SRC_AREA_ENEMY, 2);
RegisterSpellScriptWithArgs(spell_gen_select_target_count, "spell_gen_select_target_count_15_3", TARGET_UNIT_SRC_AREA_ENEMY, 3);
RegisterSpellScriptWithArgs(spell_gen_select_target_count, "spell_gen_select_target_count_15_5", TARGET_UNIT_SRC_AREA_ENEMY, 5);
RegisterSpellScriptWithArgs(spell_gen_select_target_count, "spell_gen_select_target_count_7_1", TARGET_UNIT_SRC_AREA_ENTRY, 1);
RegisterSpellScriptWithArgs(spell_gen_select_target_count, "spell_gen_select_target_count_24_1", TARGET_UNIT_CONE_ENEMY_24, 1);