Merge branch 'azerothcore:master' into Playerbot

This commit is contained in:
bash
2025-09-23 14:43:56 +02:00
committed by GitHub
11 changed files with 270 additions and 141 deletions

View File

@@ -0,0 +1,16 @@
-- DB update 2025_09_22_03 -> 2025_09_23_00
-- Update SmartAIs
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE (`entry` IN (18855, 19643, 21660));
DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` IN (18855, 19643, 21660));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(18855, 0, 0, 0, 0, 0, 100, 0, 3000, 6000, 3000, 4000, 0, 0, 11, 9053, 64, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Sunfury Magister - In Combat - Cast \'Fireball\''),
(18855, 0, 1, 0, 0, 0, 100, 0, 8000, 12000, 26000, 31000, 0, 0, 11, 35778, 33, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sunfury Magister - In Combat - Cast \'Bloodcrystal Surge\''),
(18855, 0, 2, 0, 2, 0, 100, 512, 0, 15, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sunfury Magister - Between 0-15% Health - Flee For Assist'),
(18855, 0, 3, 0, 1, 0, 50, 0, 10000, 20000, 15000, 30000, 0, 0, 11, 34397, 0, 0, 0, 0, 0, 19, 19421, 30, 0, 0, 0, 0, 0, 0, 'Sunfury Magister - Out of Combat - Cast \'Red Beam\''),
(19643, 0, 0, 0, 1, 0, 100, 0, 1000, 1000, 600000, 600000, 0, 0, 11, 35917, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sunfury Astromancer - Out of Combat - Cast \'Fiery Intellect\''),
(19643, 0, 1, 0, 0, 0, 100, 0, 2000, 3000, 1500, 2000, 0, 30, 11, 38391, 64, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Sunfury Astromancer - In Combat - Cast \'Scorch\''),
(19643, 0, 2, 0, 0, 0, 100, 0, 12000, 16000, 12000, 16000, 0, 30, 11, 35914, 64, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 'Sunfury Astromancer - In Combat - Cast \'Astral Focus\''),
(21660, 0, 0, 0, 0, 0, 100, 0, 0, 0, 2400, 3800, 0, 0, 11, 34447, 64, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Cabal Abjurist - In Combat - Cast \'Arcane Missiles\''),
(21660, 0, 1, 0, 106, 0, 100, 0, 5000, 9000, 13000, 18000, 0, 10, 11, 11831, 64, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cabal Abjurist - On Hostile in Range - Cast \'Frost Nova\'');

View File

@@ -0,0 +1,19 @@
-- DB update 2025_09_23_00 -> 2025_09_23_01
--
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 27996);
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(27996, 0, 0, 0, 1, 0, 100, 512, 9000, 9000, 30000, 30000, 0, 0, 1, 0, 0, 0, 0, 0, 0, 21, 10, 0, 0, 0, 0, 0, 0, 0, 'Wyrmrest Vanquisher - Out of Combat - Say Line 0'),
(27996, 0, 1, 0, 2, 0, 100, 513, 0, 33, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 21, 10, 0, 0, 0, 0, 0, 0, 0, 'Wyrmrest Vanquisher - Between 0-33% Health - Say Line 1 (No Repeat)'),
(27996, 0, 3, 0, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wyrmrest Vanquisher - On Just Summoned - Set Reactstate Passive'),
(27996, 0, 4, 5, 28, 0, 100, 512, 0, 0, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 0, 202, 15, 0, 0, 0, 0, 0, 0, 0, 'Wyrmrest Vanquisher - On Passenger Removed - Move To Random Point'),
(27996, 0, 5, 6, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wyrmrest Vanquisher - On Passenger Removed - Say Line 2'),
(27996, 0, 6, 7, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 3000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wyrmrest Vanquisher - On Passenger Removed - Despawn In 3000 ms'),
(27996, 0, 7, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 134, 44795, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Wyrmrest Vanquisher - On Passenger Removed - Invoker Cast \'Parachute\'');
DELETE FROM `smart_scripts` WHERE `entryorguid` = 2800500 AND `source_type` = 9;
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28005) AND (`source_type` = 0) AND (`id` IN (1));
DELETE FROM `spell_script_names` WHERE `ScriptName` IN ('spell_dragonblight_devour_ghoul', 'spell_dragonblight_devour_ghoul_periodic');
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(50430, 'spell_dragonblight_devour_ghoul'),
(50432, 'spell_dragonblight_devour_ghoul_periodic');

View File

@@ -746,27 +746,39 @@ void BossAI::UpdateAI(uint32 diff)
DoMeleeAttackIfReady();
}
void BossAI::OnSpellCastFinished(SpellInfo const* spellInfo, SpellFinishReason reason)
{
ScriptedAI::OnSpellCastFinished(spellInfo, reason);
// Check if any health check events are pending (i.e. waiting for the boss to stop casting.
if (_nextHealthCheck.IsPending() && me->IsInCombat())
{
_nextHealthCheck.UpdateStatus(HEALTH_CHECK_PROCESSED);
// This must be delayed because creature might still have unit state casting at this point, which might break scripts.
scheduler.Schedule(1s, [this](TaskContext context)
{
if (me->HasUnitState(UNIT_STATE_CASTING))
context.Repeat();
else
ProcessHealthCheck();
});
}
}
void BossAI::DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask)
{
ScriptedAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask);
if (_nextHealthCheck._valid)
if (!_nextHealthCheck.HasBeenProcessed())
{
if (!_nextHealthCheck._allowedWhileCasting && me->HasUnitState(UNIT_STATE_CASTING))
return;
if (me->HealthBelowPctDamaged(_nextHealthCheck._healthPct, damage))
{
_nextHealthCheck._exec();
_nextHealthCheck._valid = false;
_healthCheckEvents.remove_if([&](HealthCheckEventData data) -> bool
if (!_nextHealthCheck._allowedWhileCasting && me->HasUnitState(UNIT_STATE_CASTING))
{
return data._healthPct == _nextHealthCheck._healthPct;
});
_nextHealthCheck.UpdateStatus(HEALTH_CHECK_PENDING);
return;
}
if (!_healthCheckEvents.empty())
_nextHealthCheck = _healthCheckEvents.front();
ProcessHealthCheck();
}
}
}
@@ -780,18 +792,32 @@ void BossAI::DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damage
*/
void BossAI::ScheduleHealthCheckEvent(uint32 healthPct, std::function<void()> exec, bool allowedWhileCasting /*=true*/)
{
_healthCheckEvents.push_back(HealthCheckEventData(healthPct, exec, true, allowedWhileCasting));
_healthCheckEvents.push_back(HealthCheckEventData(healthPct, exec, HEALTH_CHECK_SCHEDULED, allowedWhileCasting));
_nextHealthCheck = _healthCheckEvents.front();
};
void BossAI::ScheduleHealthCheckEvent(std::initializer_list<uint8> healthPct, std::function<void()> exec, bool allowedWhileCasting /*=true*/)
{
for (auto const& checks : healthPct)
_healthCheckEvents.push_back(HealthCheckEventData(checks, exec, true, allowedWhileCasting));
_healthCheckEvents.push_back(HealthCheckEventData(checks, exec, HEALTH_CHECK_SCHEDULED, allowedWhileCasting));
_nextHealthCheck = _healthCheckEvents.front();
}
void BossAI::ProcessHealthCheck()
{
_nextHealthCheck.UpdateStatus(HEALTH_CHECK_PROCESSED);
_nextHealthCheck._exec();
_healthCheckEvents.remove_if([&](HealthCheckEventData data) -> bool
{
return data._healthPct == _nextHealthCheck._healthPct;
});
if (!_healthCheckEvents.empty())
_nextHealthCheck = _healthCheckEvents.front();
}
void BossAI::ScheduleEnrageTimer(uint32 spellId, Milliseconds timer, uint8 textId /*= 0*/)
{
me->m_Events.AddEventAtOffset([this, spellId, textId]

View File

@@ -454,14 +454,27 @@ private:
std::unordered_set<uint32> _uniqueTimedEvents;
};
enum HealthCheckStatus
{
HEALTH_CHECK_PROCESSED,
HEALTH_CHECK_SCHEDULED,
HEALTH_CHECK_PENDING
};
struct HealthCheckEventData
{
HealthCheckEventData(uint8 healthPct, std::function<void()> exec, bool valid = true, bool allowedWhileCasting = true) : _healthPct(healthPct), _exec(exec), _valid(valid), _allowedWhileCasting(allowedWhileCasting) { };
HealthCheckEventData(uint8 healthPct, std::function<void()> exec, uint8 status = HEALTH_CHECK_SCHEDULED, bool allowedWhileCasting = true, Milliseconds Delay = 0s) : _healthPct(healthPct), _exec(exec), _status(status), _allowedWhileCasting(allowedWhileCasting), _delay(Delay) { };
uint8 _healthPct;
std::function<void()> _exec;
bool _valid;
uint8 _status;
bool _allowedWhileCasting;
Milliseconds _delay;
[[nodiscard]] bool HasBeenProcessed() const { return _status == HEALTH_CHECK_PROCESSED; };
[[nodiscard]] bool IsPending() const { return _status == HEALTH_CHECK_PENDING; };
[[nodiscard]] Milliseconds GetDelay() const { return _delay; };
void UpdateStatus(uint8 status) { _status = status; };
};
class BossAI : public ScriptedAI
@@ -476,6 +489,7 @@ public:
bool CanRespawn() override;
void OnSpellCastFinished(SpellInfo const* spell, SpellFinishReason reason) override;
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override;
void JustSummoned(Creature* summon) override;
void SummonedCreatureDespawn(Creature* summon) override;
@@ -485,6 +499,7 @@ public:
void ScheduleHealthCheckEvent(uint32 healthPct, std::function<void()> exec, bool allowedWhileCasting = true);
void ScheduleHealthCheckEvent(std::initializer_list<uint8> healthPct, std::function<void()> exec, bool allowedWhileCasting = true);
void ProcessHealthCheck();
// @brief Casts the spell after the fixed time and says the text id if provided. Timer will run even if the creature is casting or out of combat.
// @param spellId The spell to cast.

View File

@@ -272,7 +272,7 @@ bool TemporaryThreatModifierEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
Creature::Creature(): Unit(), MovableMapObject(), m_groupLootTimer(0), lootingGroupLowGUID(0), m_lootRecipientGroup(0),
m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_wanderDistance(0.0f), m_boundaryCheckTime(2500),
m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE),
m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_alreadyCallForHelp(false), m_AlreadyCallAssistance(false),
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_regenPower(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f),_sparringPct(0.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_lastLeashExtensionTime(nullptr), m_cannotReachTimer(0),
_isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false), _isCombatMovementAllowed(true)
@@ -2486,6 +2486,9 @@ void Creature::CallForHelp(float radius, Unit* target /*= nullptr*/)
return;
}
if (m_alreadyCallForHelp) // avoid recursive call for help for any reason
return;
Acore::CallOfHelpCreatureInRangeDo u_do(this, target, radius);
Acore::CreatureWorker<Acore::CallOfHelpCreatureInRangeDo> worker(this, u_do);
Cell::VisitObjects(this, worker, radius);

View File

@@ -268,6 +268,7 @@ public:
void DoFleeToGetAssistance();
void CallForHelp(float fRadius, Unit* target = nullptr);
void CallAssistance(Unit* target = nullptr);
void SetNoCallForHelp(bool val) { m_alreadyCallForHelp = val; }
void SetNoCallAssistance(bool val) { m_AlreadyCallAssistance = val; }
void SetNoSearchAssistance(bool val) { m_AlreadySearchedAssistance = val; }
bool HasSearchedAssistance() { return m_AlreadySearchedAssistance; }
@@ -469,6 +470,7 @@ protected:
uint8 m_equipmentId;
int8 m_originalEquipmentId; // can be -1
bool m_alreadyCallForHelp;
bool m_AlreadyCallAssistance;
bool m_AlreadySearchedAssistance;
bool m_regenHealth;

View File

@@ -1157,7 +1157,11 @@ namespace Acore
return;
if (u->AI())
{
u->SetNoCallForHelp(true); // avoid recursive call for help causing stack overflow
u->AI()->AttackStart(i_enemy);
u->SetNoCallForHelp(false);
}
}
private:
Unit* const i_funit;

View File

@@ -5287,6 +5287,8 @@ void SpellMgr::LoadSpellInfoCorrections()
vse->m_flags &= ~VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE;
vse = const_cast<VehicleSeatEntry*>(sVehicleSeatStore.LookupEntry(4693)); // Siege Engine, Accessory
vse->m_flags &= ~VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE;
vse = const_cast<VehicleSeatEntry*>(sVehicleSeatStore.LookupEntry(1520)); // Wyrmrest Vanquisher
vse->m_flags |= VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE;
// pussywizard: fix z offset for some vehicles:
vse = const_cast<VehicleSeatEntry*>(sVehicleSeatStore.LookupEntry(6206)); // Marrowgar - Bone Spike

View File

@@ -56,21 +56,24 @@ enum Misc
{
ACHIEV_TIMED_START_EVENT = 20381,
EVENT_CHECK_HEALTH_25 = 1,
EVENT_CHECK_HEALTH_50 = 2,
EVENT_CHECK_HEALTH_75 = 3,
EVENT_CARRION_BEETELS = 4,
EVENT_LEECHING_SWARM = 5,
EVENT_IMPALE = 6,
EVENT_POUND = 7,
EVENT_CLOSE_DOORS = 8,
EVENT_EMERGE = 9,
EVENT_SUMMON_VENOMANCER = 10,
EVENT_SUMMON_DARTER = 11,
EVENT_SUMMON_GUARDIAN = 12,
EVENT_SUMMON_ASSASSINS = 13,
EVENT_ENABLE_ROTATE = 14,
EVENT_KILL_TALK = 15
EVENT_CARRION_BEETELS = 1,
EVENT_LEECHING_SWARM = 2,
EVENT_IMPALE = 3,
EVENT_POUND = 4,
EVENT_CLOSE_DOORS = 5,
EVENT_EMERGE = 6,
EVENT_SUMMON_VENOMANCER = 7,
EVENT_SUMMON_DARTER = 8,
EVENT_SUMMON_GUARDIAN = 9,
EVENT_SUMMON_ASSASSINS = 10,
EVENT_ENABLE_ROTATE = 11,
EVENT_KILL_TALK = 12
};
enum ANAnubarakNpcs
{
NPC_ANUBAR_GUARDIAN = 29216,
NPC_ANUBAR_VENOMANCER = 29217
};
class boss_anub_arak : public CreatureScript
@@ -83,11 +86,10 @@ class boss_anub_arak : public CreatureScript
boss_anub_arakAI(Creature* creature) : BossAI(creature, DATA_ANUBARAK_EVENT)
{
me->m_SightDistance = 120.0f;
intro = false;
_intro = false;
_summonedMinions = false;
}
bool intro;
void EnterEvadeMode(EvadeReason why) override
{
me->DisableRotate(false);
@@ -96,9 +98,9 @@ class boss_anub_arak : public CreatureScript
void MoveInLineOfSight(Unit* who) override
{
if (!intro && who->IsPlayer())
if (!_intro && who->IsPlayer())
{
intro = true;
_intro = true;
Talk(SAY_INTRO);
}
BossAI::MoveInLineOfSight(who);
@@ -129,8 +131,42 @@ class boss_anub_arak : public CreatureScript
void Reset() override
{
BossAI::Reset();
_summonedMinions = false;
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
ScheduleHealthCheckEvent({ 75, 50, 25 }, [&]{
Talk(SAY_SUBMERGE);
_summonedMinions = false;
DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS, true);
DoCastSelf(SPELL_SUBMERGE, false);
me->m_Events.AddEventAtOffset([this] {
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
DoCastSelf(SPELL_IMPALE_PERIODIC, true);
}, 2s);
events.Reset();
events.ScheduleEvent(EVENT_EMERGE, 60s);
events.ScheduleEvent(EVENT_SUMMON_ASSASSINS, 2s);
events.ScheduleEvent(EVENT_SUMMON_GUARDIAN, 4s);
events.ScheduleEvent(EVENT_SUMMON_ASSASSINS, 15s);
events.ScheduleEvent(EVENT_SUMMON_VENOMANCER, 20s);
events.ScheduleEvent(EVENT_SUMMON_DARTER, 30s);
events.ScheduleEvent(EVENT_SUMMON_ASSASSINS, 35s);
}, false);
}
void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) override
{
if (!me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
return;
if (_summonedMinions && !summons.IsAnyCreatureWithEntryAlive(NPC_ANUBAR_GUARDIAN) && !summons.IsAnyCreatureWithEntryAlive(NPC_ANUBAR_VENOMANCER))
{
events.Reset();
events.ScheduleEvent(EVENT_EMERGE, 5s);
}
}
void JustEngagedWith(Unit* ) override
@@ -141,16 +177,13 @@ class boss_anub_arak : public CreatureScript
events.ScheduleEvent(EVENT_CARRION_BEETELS, 6500ms);
events.ScheduleEvent(EVENT_LEECHING_SWARM, 20s);
events.ScheduleEvent(EVENT_POUND, 15s);
events.ScheduleEvent(EVENT_CHECK_HEALTH_75, 1s);
events.ScheduleEvent(EVENT_CHECK_HEALTH_50, 1s);
events.ScheduleEvent(EVENT_CHECK_HEALTH_25, 1s);
events.ScheduleEvent(EVENT_CLOSE_DOORS, 5s);
}
void SummonHelpers(float x, float y, float z, uint32 spellId)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
me->SummonCreature(spellInfo->Effects[EFFECT_0].MiscValue, x, y, z);
me->SummonCreature(spellInfo->Effects[EFFECT_0].MiscValue, x, y, z, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);
}
void UpdateAI(uint32 diff) override
@@ -159,10 +192,12 @@ class boss_anub_arak : public CreatureScript
return;
events.Update(diff);
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (uint32 eventId = events.ExecuteEvent())
switch (events.ExecuteEvent())
{
case EVENT_CLOSE_DOORS:
_JustEngagedWith();
@@ -191,34 +226,14 @@ class boss_anub_arak : public CreatureScript
me->RemoveAurasDueToSpell(SPELL_SELF_ROOT);
me->DisableRotate(false);
break;
case EVENT_CHECK_HEALTH_25:
case EVENT_CHECK_HEALTH_50:
case EVENT_CHECK_HEALTH_75:
if (me->HealthBelowPct(eventId*25))
{
Talk(SAY_SUBMERGE);
DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS, true);
me->CastSpell(me, SPELL_IMPALE_PERIODIC, true);
me->CastSpell(me, SPELL_SUBMERGE, false);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
events.DelayEvents(46000, 0);
events.ScheduleEvent(EVENT_EMERGE, 45s);
events.ScheduleEvent(EVENT_SUMMON_ASSASSINS, 2s);
events.ScheduleEvent(EVENT_SUMMON_GUARDIAN, 4s);
events.ScheduleEvent(EVENT_SUMMON_ASSASSINS, 15s);
events.ScheduleEvent(EVENT_SUMMON_VENOMANCER, 20s);
events.ScheduleEvent(EVENT_SUMMON_DARTER, 30s);
events.ScheduleEvent(EVENT_SUMMON_ASSASSINS, 35s);
break;
}
events.ScheduleEvent(eventId, 500ms);
break;
case EVENT_EMERGE:
me->CastSpell(me, SPELL_EMERGE, true);
me->RemoveAura(SPELL_SUBMERGE);
me->RemoveAura(SPELL_IMPALE_PERIODIC);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
events.ScheduleEvent(EVENT_CARRION_BEETELS, 6500ms);
events.ScheduleEvent(EVENT_LEECHING_SWARM, 20s);
events.ScheduleEvent(EVENT_POUND, 15s);
break;
case EVENT_SUMMON_ASSASSINS:
SummonHelpers(509.32f, 247.42f, 239.48f, SPELL_SUMMON_ASSASSIN);
@@ -232,6 +247,7 @@ class boss_anub_arak : public CreatureScript
SummonHelpers(550.34f, 316.00f, 234.30f, SPELL_SUMMON_GUARDIAN);
break;
case EVENT_SUMMON_VENOMANCER:
_summonedMinions = true;
SummonHelpers(550.34f, 316.00f, 234.30f, SPELL_SUMMON_VENOMANCER);
break;
}
@@ -239,6 +255,10 @@ class boss_anub_arak : public CreatureScript
if (!me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
DoMeleeAttackIfReady();
}
private:
bool _intro;
bool _summonedMinions;
};
CreatureAI* GetAI(Creature* creature) const override

View File

@@ -75,21 +75,32 @@ class boss_slad_ran : public CreatureScript
public:
boss_slad_ran() : CreatureScript("boss_slad_ran") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetGundrakAI<boss_slad_ranAI>(creature);
}
struct boss_slad_ranAI : public BossAI
{
boss_slad_ranAI(Creature* creature) : BossAI(creature, DATA_SLAD_RAN)
{
}
boss_slad_ranAI(Creature* creature) : BossAI(creature, DATA_SLAD_RAN) { }
void Reset() override
{
BossAI::Reset();
_achievement = true;
ScheduleHealthCheckEvent(90, [&] {
Talk(SAY_SUMMON_SNAKES);
ScheduleTimedEvent(1s, [&] {
for (uint8 i = MAX_CONSTRICTOR; i < MAX_SUMMONS; ++i)
me->SummonCreature(NPC_SLADRAN_VIPER, SpawnLoc[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20 * IN_MILLISECONDS);
}, 8s);
});
ScheduleHealthCheckEvent(DUNGEON_MODE(50, 75), [&] {
Talk(SAY_SUMMON_CONSTRICTORS);
ScheduleTimedEvent(1s, [&] {
for (uint8 i = 0; i < MAX_CONSTRICTOR; ++i)
me->SummonCreature(NPC_SLADRAN_CONSTRICTORS, SpawnLoc[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20 * IN_MILLISECONDS);
}, 3s, 5s);
});
}
uint32 GetData(uint32 data) const override
@@ -110,11 +121,18 @@ public:
Talk(SAY_AGGRO);
BossAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_POISON_NOVA, 10s);
events.ScheduleEvent(EVENT_POWERFULL_BITE, 3s);
events.ScheduleEvent(EVENT_VENOM_BOLT, 15s);
events.ScheduleEvent(EVENT_CHECK_HEALTH1, 1s);
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1s);
ScheduleTimedEvent(10s, [&]{
Talk(EMOTE_NOVA);
DoCastAOE(SPELL_POISON_NOVA);
}, 15s);
ScheduleTimedEvent(3s, [&] {
DoCastVictim(SPELL_POWERFULL_BITE);
}, 10s);
ScheduleTimedEvent(15s, [&] {
DoCastVictim(SPELL_VENOM_BOLT);
}, 10s);
}
void JustDied(Unit* killer) override
@@ -133,72 +151,14 @@ public:
}
}
void JustSummoned(Creature* summon) override
{
summon->SetInCombatWithZone();
summons.Summon(summon);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_CHECK_HEALTH1:
if (me->HealthBelowPct(70))
{
Talk(SAY_SUMMON_SNAKES);
events.ScheduleEvent(EVENT_SUMMON1, 1s);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH1, 1s);
break;
case EVENT_CHECK_HEALTH2:
if (me->HealthBelowPct(50))
{
Talk(SAY_SUMMON_CONSTRICTORS);
events.ScheduleEvent(EVENT_SUMMON2, 1s);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1s);
break;
case EVENT_POISON_NOVA:
Talk(EMOTE_NOVA);
me->CastSpell(me, SPELL_POISON_NOVA, false);
events.ScheduleEvent(EVENT_POISON_NOVA, 15s);
break;
case EVENT_POWERFULL_BITE:
me->CastSpell(me->GetVictim(), SPELL_POWERFULL_BITE, false);
events.ScheduleEvent(EVENT_POWERFULL_BITE, 10s);
break;
case EVENT_VENOM_BOLT:
me->CastSpell(me->GetVictim(), SPELL_VENOM_BOLT, false);
events.ScheduleEvent(EVENT_VENOM_BOLT, 10s);
break;
case EVENT_SUMMON1:
for (uint8 i = MAX_CONSTRICTOR; i < MAX_SUMMONS; ++i)
me->SummonCreature(NPC_SLADRAN_VIPER, SpawnLoc[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SUMMON1, 8s);
break;
case EVENT_SUMMON2:
for (uint8 i = 0; i < MAX_CONSTRICTOR; ++i)
me->SummonCreature(NPC_SLADRAN_CONSTRICTORS, SpawnLoc[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SUMMON2, 3s, 5s);
break;
}
DoMeleeAttackIfReady();
}
private:
bool _achievement;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetGundrakAI<boss_slad_ranAI>(creature);
}
};
class spell_sladran_grip_of_sladran_aura : public AuraScript

View File

@@ -2271,6 +2271,66 @@ private:
}
};
enum DevourGhoulSpells
{
SPELL_DEVOUR_GHOUL_RIDE_VEHICLE = 50437,
SPELL_DEVOUR_PERIODIC = 50432,
SPELL_NOURISHMENT = 50443
};
// 50430 - Devour Ghoul
class spell_dragonblight_devour_ghoul: public SpellScript
{
PrepareSpellScript(spell_dragonblight_devour_ghoul);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_DEVOUR_GHOUL_RIDE_VEHICLE });
}
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (GetCaster())
{
GetHitUnit()->CastSpell(GetCaster(), SPELL_DEVOUR_GHOUL_RIDE_VEHICLE, true);
GetCaster()->CastSpell(GetHitUnit(), SPELL_DEVOUR_PERIODIC, true);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_dragonblight_devour_ghoul::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 50432 - Devour Ghoul
class spell_dragonblight_devour_ghoul_periodic : public AuraScript
{
PrepareAuraScript(spell_dragonblight_devour_ghoul_periodic);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_NOURISHMENT });
}
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE && GetCaster())
GetCaster()->CastSpell(GetCaster(), SPELL_NOURISHMENT, true);
if (GetUnitOwner() && GetUnitOwner()->ToCreature())
{
GetUnitOwner()->ExitVehicle();
GetUnitOwner()->ToCreature()->DespawnOrUnsummon(2000);
}
}
void Register() override
{
OnEffectRemove += AuraEffectRemoveFn(spell_dragonblight_devour_ghoul_periodic::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
}
};
void AddSC_dragonblight()
{
new npc_conversing_with_the_depths_trigger();
@@ -2299,4 +2359,6 @@ void AddSC_dragonblight()
RegisterSpellScript(spell_dragonblight_corrosive_spit);
RegisterSpellScript(spell_handover_reins);
RegisterSpellScript(spell_dragonblight_flame_fury);
RegisterSpellScript(spell_dragonblight_devour_ghoul);
RegisterSpellScript(spell_dragonblight_devour_ghoul_periodic);
}