Merge branch 'master' into Playerbot

# Conflicts:
#	src/server/apps/worldserver/worldserver.conf.dist
#	src/server/game/Battlegrounds/Battleground.h
#	src/server/game/Entities/Player/Player.cpp
#	src/server/game/World/World.cpp
This commit is contained in:
郑佩茹
2022-07-13 10:31:30 -06:00
123 changed files with 6889 additions and 1214 deletions

View File

@@ -58,7 +58,8 @@ public:
{ "cinematic", HandleDebugPlayCinematicCommand, SEC_ADMINISTRATOR, Console::No },
{ "movie", HandleDebugPlayMovieCommand, SEC_ADMINISTRATOR, Console::No },
{ "sound", HandleDebugPlaySoundCommand, SEC_ADMINISTRATOR, Console::No },
{ "music", HandleDebugPlayMusicCommand, SEC_ADMINISTRATOR, Console::No }
{ "music", HandleDebugPlayMusicCommand, SEC_ADMINISTRATOR, Console::No },
{ "visual", HandleDebugVisualCommand, SEC_ADMINISTRATOR, Console::No }
};
static ChatCommandTable debugSendCommandTable =
{
@@ -203,6 +204,28 @@ public:
return true;
}
static bool HandleDebugVisualCommand(ChatHandler* handler, uint32 visualId)
{
if (!visualId)
{
handler->SendSysMessage(LANG_BAD_VALUE);
handler->SetSentErrorMessage(true);
return false;
}
Player* player = handler->GetPlayer();
Unit* target = handler->getSelectedUnit();
if (!target)
{
player->SendPlaySpellVisual(visualId);
return true;
}
player->SendPlaySpellImpact(target->GetGUID(), visualId);
return true;
}
static bool HandleDebugSendSpellFailCommand(ChatHandler* handler, SpellCastResult result, Optional<uint32> failArg1, Optional<uint32> failArg2)
{
WorldPacket data(SMSG_CAST_FAILED, 5);

View File

@@ -88,9 +88,11 @@ public:
*
* Example Usage:
* @code
* .deserter instance add 1h30m
* .deserter instance add 1h30m (using player target or self)
* -or-
* .deserter bg add 1h30m
* .deserter bg add 1h30m (using player target or self)
* -or-
* .deserter bg add Tester 1h30m (using player of name 'Tester')
* @endcode
*/
static bool HandleDeserterAdd(ChatHandler* handler, Optional<std::string> playerName, Optional<std::string> time, bool isInstance)
@@ -156,16 +158,18 @@ public:
return false;
}
uint32 deserterSpell = isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER;
if (target)
{
Aura* aura = target->GetAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER);
Aura* aura = target->GetAura(deserterSpell);
if (aura && aura->GetDuration() >= duration * IN_MILLISECONDS)
{
handler->PSendSysMessage("Player %s already has a longer %s Deserter active.", handler->playerLink(*playerName), isInstance ? "Instance" : "Battleground");
return true;
}
aura = target->AddAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER, target);
aura = target->AddAura(deserterSpell, target);
if (!aura)
{
handler->SendSysMessage(LANG_BAD_VALUE);
@@ -173,44 +177,45 @@ public:
return false;
}
aura->SetDuration(duration * IN_MILLISECONDS);
return true;
}
int32 remainTime = 0;
if (QueryResult result = CharacterDatabase.Query("SELECT remainTime FROM character_aura WHERE guid = {} AND spell = {}", guid.GetCounter(), isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER))
else
{
Field* fields = result->Fetch();
remainTime = fields[0].Get<int32>();
if (remainTime < 0 || remainTime >= duration * IN_MILLISECONDS)
int32 remainTime = 0;
if (QueryResult result = CharacterDatabase.Query("SELECT remainTime FROM character_aura WHERE guid = {} AND spell = {}", guid.GetCounter(), deserterSpell))
{
handler->PSendSysMessage("Player %s already has a longer %s Deserter active.", handler->playerLink(*playerName), isInstance ? "Instance" : "Battleground");
return true;
Field* fields = result->Fetch();
remainTime = fields[0].Get<int32>();
if (remainTime < 0 || remainTime >= duration * IN_MILLISECONDS)
{
handler->PSendSysMessage("Player %s already has a longer %s Deserter active.", handler->playerLink(*playerName), isInstance ? "Instance" : "Battleground");
return true;
}
CharacterDatabase.Query("DELETE FROM character_aura WHERE guid = {} AND spell = {}", guid.GetCounter(), deserterSpell);
}
CharacterDatabase.Query("DELETE FROM character_aura WHERE guid = {} AND spell = {}", guid.GetCounter(), isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER);
uint8 index = 0;
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AURA);
stmt->SetData(index++, guid.GetCounter());
stmt->SetData(index++, guid.GetCounter());
stmt->SetData(index++, 0);
stmt->SetData(index++, deserterSpell);
stmt->SetData(index++, 1);
stmt->SetData(index++, 1);
stmt->SetData(index++, 1);
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, isInstance ? 1800000 : 900000);
stmt->SetData(index++, duration * IN_MILLISECONDS);
stmt->SetData(index, 0);
CharacterDatabase.Execute(stmt);
}
uint8 index = 0;
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AURA);
stmt->SetData(index++, guid.GetCounter());
stmt->SetData(index++, guid.GetCounter());
stmt->SetData(index++, 0);
stmt->SetData(index++, isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER);
stmt->SetData(index++, 1);
stmt->SetData(index++, 1);
stmt->SetData(index++, 1);
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, isInstance ? 1800000 : 900000);
stmt->SetData(index++, duration * 1000);
stmt->SetData(index, 0);
CharacterDatabase.Execute(stmt);
handler->PSendSysMessage("%s of %s Deserter has been added to player %s.", secsToTimeString(duration), isInstance ? "Instance" : "Battleground", handler->playerLink(*playerName));
return true;
}
@@ -221,6 +226,7 @@ public:
* selected player.
*
* @param handler The ChatHandler, passed by the system.
* @param player The target player, either by name, the target or self
* @param isInstance provided by the relaying functions, so we don't have
* to write that much code :)
*
@@ -228,9 +234,11 @@ public:
*
* Example Usage:
* @code
* .deserter instance remove
* .deserter instance remove (using player target or self)
* -or-
* .deserter bg remove
* .deserter bg remove (using player target or self)
* -or-
* .deserter bg remove Tester (using player of name 'Tester')
* @endcode
*/
static bool HandleDeserterRemove(ChatHandler* handler, Optional<PlayerIdentifier> player, bool isInstance)
@@ -248,21 +256,75 @@ public:
}
Player* target = player->GetConnectedPlayer();
uint32 deserterSpell = isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER;
int32 duration = 0;
if (target)
{
target->RemoveAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER);
if (Aura* aura = target->GetAura(deserterSpell))
{
duration = aura->GetDuration();
target->RemoveAura(deserterSpell);
}
}
else
{
if (QueryResult result = CharacterDatabase.Query("SELECT remainTime FROM character_aura WHERE guid = {} AND spell = {}", player->GetGUID().GetCounter(), deserterSpell))
{
Field* fields = result->Fetch();
duration = fields[0].Get<int32>();
CharacterDatabase.Execute("DELETE FROM character_aura WHERE guid = {} AND spell = {}", player->GetGUID().GetCounter(), deserterSpell);
}
}
if (duration == 0)
{
handler->PSendSysMessage("Player %s does not have %s Deserter.", handler->playerLink(player->GetName()), isInstance ? "Instance" : "Battleground");
handler->SetSentErrorMessage(true);
return true;
}
CharacterDatabase.Query("DELETE FROM character_aura WHERE guid = {} AND spell = {}", player->GetGUID().GetCounter(), isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER);
if (duration < 0)
{
handler->PSendSysMessage("Permanent %s Deserter has been removed from player %s (GUID %u).", isInstance ? "Instance" : "Battleground", handler->playerLink(player->GetName()), player->GetGUID().GetCounter());
handler->SetSentErrorMessage(true);
return true;
}
handler->PSendSysMessage("%s of %s Deserter has been removed from player %s (GUID %u).", secsToTimeString(duration / IN_MILLISECONDS), isInstance ? "Instance" : "Battleground", handler->playerLink(player->GetName()), player->GetGUID().GetCounter());
return true;
}
/**
* @brief Removes the Deserter Debuff from all players
*
* This function removes a Deserter Debuff of the given type (Instance or BG) from
* all players, online or offline.
*
* @param handler The ChatHandler, passed by the system.
* @param isInstance provided by the relaying functions, so we don't have
* to write that much code :)
* @param maxTime Optional: The maximum remaining time of the Debuff on players to be removed.
* Any Player with a Deserter Debuff of this time or less will get their Debuff removed. Use -1 for any.
* Default: 15m for BG, 30m for Instance.
*
* @return true if everything was correct, false if an error occured.
*
* Example Usage:
* @code
* .deserter bg remove all
* -or-
* .deserter bg remove all 30m
* -or-
* .deserter bg remove all -1
* @endcode
*/
static bool HandleDeserterRemoveAll(ChatHandler* handler, bool isInstance, Optional<std::string> maxTime)
{
uint32 deserterSpell = isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER;
int32 remainTime = isInstance ? 1800 : 900;
uint64 deserterCount = 0;
bool countOnline = true;
if (maxTime)
{
@@ -273,6 +335,7 @@ public:
}
}
// Optimization. Do not execute any further functions or Queries if remainTime is 0.
if (remainTime == 0)
{
handler->SendSysMessage(LANG_BAD_VALUE);
@@ -280,13 +343,33 @@ public:
return false;
}
if (remainTime < 0)
QueryResult result;
if (remainTime > 0)
{
CharacterDatabase.Execute("DELETE FROM character_aura WHERE spell = {}", isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER);
result = CharacterDatabase.Query("SELECT COUNT(guid) FROM character_aura WHERE spell = {} AND remainTime <= {}", deserterSpell, remainTime * IN_MILLISECONDS);
}
else
{
CharacterDatabase.Execute("DELETE FROM character_aura WHERE spell = {} AND remainTime <= {}", isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER, remainTime * IN_MILLISECONDS);
result = CharacterDatabase.Query("SELECT COUNT(guid) FROM character_aura WHERE spell = {}", deserterSpell);
}
if (result)
{
deserterCount = (*result)[0].Get<uint64>();
}
// Optimization. Only execute these if there even is a result.
if (deserterCount > 0)
{
countOnline = false;
if (remainTime > 0)
{
CharacterDatabase.Execute("DELETE FROM character_aura WHERE spell = {} AND remainTime <= {}", deserterSpell, remainTime * IN_MILLISECONDS);
}
else
{
CharacterDatabase.Execute("DELETE FROM character_aura WHERE spell = {}", deserterSpell);
}
}
std::shared_lock<std::shared_mutex> lock(*HashMapHolder<Player>::GetLock());
@@ -294,14 +377,28 @@ public:
for (HashMapHolder<Player>::MapType::const_iterator itr = onlinePlayerList.begin(); itr != onlinePlayerList.end(); ++itr)
{
Player* player = itr->second;
Aura* aura = player->GetAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER);
Aura* aura = player->GetAura(deserterSpell);
if (aura && (remainTime < 0 || aura->GetDuration() <= remainTime * IN_MILLISECONDS))
{
player->RemoveAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER);
if (countOnline)
deserterCount++;
player->RemoveAura(deserterSpell);
}
}
handler->PSendSysMessage("%s Deserter has been removed from all players", isInstance ? "Instance" : "Battleground");
std::string remainTimeStr = secsToTimeString(remainTime);
if (remainTime < 0)
{
remainTimeStr = "infinity";
}
if (deserterCount == 0)
{
handler->PSendSysMessage("No player on this realm has %s Deserter with a duration of %s or less.", isInstance ? "Instance" : "Battleground", remainTimeStr);
return true;
}
handler->PSendSysMessage("%s Deserter has been removed from %u player(s) with a duration of %s or less.", isInstance ? "Instance" : "Battleground", deserterCount, remainTimeStr);
return true;
}

View File

@@ -143,9 +143,9 @@ public:
return commandTable;
}
static bool HandleSkirmishCommand(ChatHandler* handler, std::string_view args)
static bool HandleSkirmishCommand(ChatHandler* handler, std::vector<std::string_view> args)
{
auto tokens = Acore::Tokenize(args, ' ', true);
auto tokens = args;
if (args.empty() || !tokens.size())
{

View File

@@ -148,6 +148,7 @@ public:
_hasSubmergedOnce = false;
_isKnockbackEmoteAllowed = true;
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
me->SetControlled(true, UNIT_STATE_ROOT);
_lavaBurstGUIDS.clear();
}

View File

@@ -30,7 +30,8 @@ enum Spells
{
SPELL_AVATAR = 24646, // Enrage Spell
SPELL_GROUND_TREMOR = 6524,
SPELL_ENTANGLING_ROOTS = 24648
SPELL_ENTANGLING_ROOTS = 24648,
SPELL_SWEEPING_STRIKES = 18765
};
enum Events
@@ -39,7 +40,8 @@ enum Events
EVENT_GROUND_TREMOR = 2,
EVENT_START_PURSUIT = 3,
EVENT_STOP_PURSUIT = 4,
EVENT_ENTANGLING_ROOTS = 5
EVENT_ENTANGLING_ROOTS = 5,
EVENT_SWEEPING_STRIKES = 6
};
class boss_grilek : public CreatureScript // grilek
@@ -65,6 +67,7 @@ public:
events.ScheduleEvent(EVENT_AVATAR, 20s, 30s);
events.ScheduleEvent(EVENT_GROUND_TREMOR, 15s, 25s);
events.ScheduleEvent(EVENT_ENTANGLING_ROOTS, 5s, 15s);
events.ScheduleEvent(EVENT_SWEEPING_STRIKES, 30s);
}
void UpdateAI(uint32 diff) override
@@ -115,6 +118,10 @@ public:
DoCastVictim(SPELL_ENTANGLING_ROOTS);
events.ScheduleEvent(EVENT_ENTANGLING_ROOTS, 10s, 20s);
break;
case EVENT_SWEEPING_STRIKES:
DoCastSelf(SPELL_SWEEPING_STRIKES, true);
events.ScheduleEvent(EVENT_SWEEPING_STRIKES, 30s);
break;
default:
break;
}

View File

@@ -34,15 +34,17 @@ enum Says
SAY_FLEEING = 1,
SAY_MINION_DESTROY = 2,
SAY_PROTECT_ALTAR = 3,
SAY_PROTECT_GURUBASHI_EMPIRE = 4
SAY_PROTECT_GURUBASHI_EMPIRE = 4,
SAY_PLEDGE_ALLEGIANCE = 5,
SAY_WORLD_WILL_SUFFER = 6,
SAY_EVADE = 7
};
enum Spells
{
SPELL_POISONOUS_BLOOD = 24321,
SPELL_BLOOD_SIPHON_HEAL = 24322,
SPELL_BLOOD_SIPHON_DMG = 24323,
SPELL_BLOOD_SIPHON = 24324,
SPELL_BLOOD_SIPHON_HEAL = 24322,
SPELL_BLOOD_SIPHON_DAMAGE = 24323,
SPELL_CORRUPTED_BLOOD = 24328,
SPELL_CAUSE_INSANITY = 24327,
SPELL_ENRAGE = 24318,
@@ -51,7 +53,8 @@ enum Spells
SPELL_ASPECT_OF_VENOXIS = 24688,
SPELL_ASPECT_OF_MARLI = 24686,
SPELL_ASPECT_OF_THEKAL = 24689,
SPELL_ASPECT_OF_ARLOKK = 24690
SPELL_ASPECT_OF_ARLOKK = 24690,
SPELL_POISONOUS_BLOOD = 24321
};
enum Events
@@ -87,9 +90,19 @@ public:
return true;
}
void ApplyHakkarPowerStacks()
{
me->RemoveAurasDueToSpell(SPELL_HAKKAR_POWER);
for (int i = DATA_JEKLIK; i < DATA_HAKKAR; i++)
if (instance->GetBossState(i) != DONE)
DoCastSelf(SPELL_HAKKAR_POWER, true);
}
void Reset() override
{
_Reset();
ApplyHakkarPowerStacks();
}
void JustDied(Unit* /*killer*/) override
@@ -105,18 +118,25 @@ public:
events.ScheduleEvent(EVENT_CAUSE_INSANITY, 17000);
events.ScheduleEvent(EVENT_ENRAGE, 600000);
if (instance->GetBossState(DATA_JEKLIK) != DONE)
events.ScheduleEvent(EVENT_ASPECT_OF_JEKLIK, 4000);
events.ScheduleEvent(EVENT_ASPECT_OF_JEKLIK, 21000);
if (instance->GetBossState(DATA_VENOXIS) != DONE)
events.ScheduleEvent(EVENT_ASPECT_OF_VENOXIS, 7000);
events.ScheduleEvent(EVENT_ASPECT_OF_VENOXIS, 14000);
if (instance->GetBossState(DATA_MARLI) != DONE)
events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 12000);
events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 15000);
if (instance->GetBossState(DATA_THEKAL) != DONE)
events.ScheduleEvent(EVENT_ASPECT_OF_THEKAL, 8000);
events.ScheduleEvent(EVENT_ASPECT_OF_THEKAL, 10000);
if (instance->GetBossState(DATA_ARLOKK) != DONE)
events.ScheduleEvent(EVENT_ASPECT_OF_ARLOKK, 18000);
Talk(SAY_AGGRO);
}
void EnterEvadeMode(EvadeReason evadeReason) override
{
BossAI::EnterEvadeMode(evadeReason);
Talk(SAY_EVADE);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || !CheckInRoom())
@@ -140,9 +160,12 @@ public:
events.ScheduleEvent(EVENT_CORRUPTED_BLOOD, urand(30000, 45000));
break;
case EVENT_CAUSE_INSANITY:
if (Unit* victim = SelectTarget(SelectTargetMethod::MaxThreat, 0, 30.f, true))
if (me->GetThreatMgr().getThreatList().size() > 1)
{
DoCast(victim, SPELL_CAUSE_INSANITY);
if (Unit* victim = SelectTarget(SelectTargetMethod::MaxThreat, 0, 30.f, true))
{
DoCast(victim, SPELL_CAUSE_INSANITY);
}
}
events.ScheduleEvent(EVENT_CAUSE_INSANITY, urand(35000, 45000));
break;
@@ -153,11 +176,11 @@ public:
break;
case EVENT_ASPECT_OF_JEKLIK:
DoCastVictim(SPELL_ASPECT_OF_JEKLIK, true);
events.ScheduleEvent(EVENT_ASPECT_OF_JEKLIK, urand(10000, 14000));
events.ScheduleEvent(EVENT_ASPECT_OF_JEKLIK, 24000);
break;
case EVENT_ASPECT_OF_VENOXIS:
DoCastVictim(SPELL_ASPECT_OF_VENOXIS, true);
events.ScheduleEvent(EVENT_ASPECT_OF_VENOXIS, 8000);
events.ScheduleEvent(EVENT_ASPECT_OF_VENOXIS, urand(16000, 18000));
break;
case EVENT_ASPECT_OF_MARLI:
if (Unit* victim = SelectTarget(SelectTargetMethod::MaxThreat, 0, 5.f, true))
@@ -165,7 +188,7 @@ public:
DoCast(victim, SPELL_ASPECT_OF_MARLI, true);
me->GetThreatMgr().modifyThreatPercent(victim, -100.f);
}
events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 10000);
events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 45000);
break;
case EVENT_ASPECT_OF_THEKAL:
DoCastVictim(SPELL_ASPECT_OF_THEKAL, true);
@@ -215,8 +238,8 @@ public:
hakkar->AI()->Talk(SAY_PROTECT_GURUBASHI_EMPIRE);
}
}
return false;
}
return false;
}
};
@@ -230,6 +253,10 @@ public:
{
if (InstanceScript* instance = player->GetInstanceScript())
{
// Instance map's enormous, Hakkar's GRID is not loaded by the time players enter.
// Without this, the creature never says anything, because it doesn't load in time.
player->GetMap()->LoadGrid(-11783.99f, -1655.27f);
if (Creature* hakkar = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_HAKKAR)))
{
if (hakkar->GetAI())
@@ -237,8 +264,8 @@ public:
hakkar->AI()->Talk(SAY_PROTECT_ALTAR);
}
}
return false;
}
return false;
}
};
@@ -252,6 +279,10 @@ public:
{
if (InstanceScript* instance = player->GetInstanceScript())
{
// Instance map's enormous, Hakkar's GRID is not loaded by the time players enter.
// Without this, the creature never says anything, because it doesn't load in time.
player->GetMap()->LoadGrid(-11783.99f, -1655.27f);
if (Creature* hakkar = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_HAKKAR)))
{
if (hakkar->GetAI())
@@ -259,37 +290,114 @@ public:
hakkar->AI()->Talk(SAY_MINION_DESTROY);
}
}
return false;
}
return false;
}
};
class spell_hakkar_blood_siphon : public SpellScript
class at_zulgurub_bloodfire_pit_speech : public OnlyOnceAreaTriggerScript
{
PrepareSpellScript(spell_hakkar_blood_siphon);
public:
at_zulgurub_bloodfire_pit_speech() : OnlyOnceAreaTriggerScript("at_zulgurub_bloodfire_pit_speech") {}
bool _OnTrigger(Player* player, const AreaTrigger* /*at*/) override
{
if (InstanceScript* instance = player->GetInstanceScript())
{
// Instance map's enormous, Hakkar's GRID is not loaded by the time players enter.
// Without this, the creature never says anything, because it doesn't load in time.
player->GetMap()->LoadGrid(-11783.99f, -1655.27f);
if (Creature* hakkar = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_HAKKAR)))
{
if (hakkar->GetAI())
{
hakkar->AI()->Talk(SAY_PLEDGE_ALLEGIANCE, player);
}
}
}
return false;
}
};
class at_zulgurub_edge_of_madness_speech : public OnlyOnceAreaTriggerScript
{
public:
at_zulgurub_edge_of_madness_speech() : OnlyOnceAreaTriggerScript("at_zulgurub_edge_of_madness_speech") {}
bool _OnTrigger(Player* player, const AreaTrigger* /*at*/) override
{
if (InstanceScript* instance = player->GetInstanceScript())
{
// Instance map's enormous, Hakkar's GRID is not loaded by the time players enter.
// Without this, the creature never says anything, because it doesn't load in time.
player->GetMap()->LoadGrid(-11783.99f, -1655.27f);
if (Creature* hakkar = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_HAKKAR)))
{
if (hakkar->GetAI())
{
hakkar->AI()->Talk(SAY_WORLD_WILL_SUFFER, player);
}
}
}
return false;
}
};
class spell_blood_siphon : public SpellScript
{
PrepareSpellScript(spell_blood_siphon);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_BLOOD_SIPHON_HEAL, SPELL_BLOOD_SIPHON_DMG });
return ValidateSpellInfo({ SPELL_BLOOD_SIPHON_DAMAGE, SPELL_BLOOD_SIPHON_HEAL });
}
void OnSpellHit()
void FilterTargets(std::list<WorldObject*>& targets)
{
Unit* caster = GetCaster();
Unit* target = GetHitUnit();
if (!caster || !target)
return;
// Max. 20 targets
if (!targets.empty())
{
Acore::Containers::RandomResize(targets, 20);
}
}
if (target->HasAura(SPELL_POISONOUS_BLOOD))
target->CastSpell(caster, SPELL_BLOOD_SIPHON_DMG, true);
else
target->CastSpell(caster, SPELL_BLOOD_SIPHON_HEAL, true);
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Unit* caster = GetCaster())
{
if (Player* player = GetHitPlayer())
{
player->CastSpell(caster, player->HasAura(SPELL_POISONOUS_BLOOD) ? SPELL_BLOOD_SIPHON_DAMAGE : SPELL_BLOOD_SIPHON_HEAL, true);
}
}
}
void Register() override
{
OnHit += SpellHitFn(spell_hakkar_blood_siphon::OnSpellHit);
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_blood_siphon::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
OnEffectHitTarget += SpellEffectFn(spell_blood_siphon::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_hakkar_power_down : public SpellScript
{
PrepareSpellScript(spell_hakkar_power_down);
void HandleOnHit()
{
if (Unit* caster = GetCaster())
if (caster->HasAura(SPELL_HAKKAR_POWER))
caster->RemoveAuraFromStack(SPELL_HAKKAR_POWER);
}
void Register() override
{
OnHit += SpellHitFn(spell_hakkar_power_down::HandleOnHit);
}
};
@@ -299,5 +407,8 @@ void AddSC_boss_hakkar()
new at_zulgurub_entrance_speech();
new at_zulgurub_bridge_speech();
new at_zulgurub_temple_speech();
RegisterSpellScript(spell_hakkar_blood_siphon);
new at_zulgurub_bloodfire_pit_speech();
new at_zulgurub_edge_of_madness_speech();
RegisterSpellScript(spell_blood_siphon);
RegisterSpellScript(spell_hakkar_power_down);
}

View File

@@ -15,13 +15,6 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Boss_Hazzarah
SD%Complete: 100
SDComment:
SDCategory: Zul'Gurub
EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
@@ -45,100 +38,98 @@ enum Events
EVENT_ILLUSIONS = 4
};
class boss_hazzarah : public CreatureScript
struct boss_hazzarah : public BossAI
{
public:
boss_hazzarah() : CreatureScript("boss_hazzarah") { }
boss_hazzarah(Creature* creature) : BossAI(creature, DATA_EDGE_OF_MADNESS) { }
struct boss_hazzarahAI : public BossAI
void JustSummoned(Creature* summon) override
{
boss_hazzarahAI(Creature* creature) : BossAI(creature, DATA_EDGE_OF_MADNESS) { }
summons.Summon(summon);
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->SetCorpseDelay(10);
summon->SetReactState(REACT_PASSIVE);
summon->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE);
summon->SetVisible(false);
summon->m_Events.AddEventAtOffset([summon]()
summon->SetCorpseDelay(10);
summon->SetReactState(REACT_PASSIVE);
summon->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE);
summon->SetVisible(false);
summon->m_Events.AddEventAtOffset([summon]()
{
summon->SetVisible(true);
}, 2s);
summon->m_Events.AddEventAtOffset([summon]()
summon->m_Events.AddEventAtOffset([summon]()
{
summon->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE);
summon->SetReactState(REACT_AGGRESSIVE);
summon->SetInCombatWithZone();
}, 3500ms);
}
}, 5s);
}
void EnterCombat(Unit* /*who*/) override
void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
{
summons.Despawn(summon);
summon->DespawnOrUnsummon();
}
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
events.ScheduleEvent(EVENT_SLEEP, 12s, 15s);
events.ScheduleEvent(EVENT_EARTH_SHOCK, 8s, 18s);
events.ScheduleEvent(EVENT_CHAIN_BURN, 12s, 28s);
events.ScheduleEvent(EVENT_ILLUSIONS, 16s, 24s);
}
bool CanAIAttack(Unit const* target) const override
{
if (me->GetThreatMgr().getThreatList().size() > 1 && me->GetThreatMgr().getOnlineContainer().getMostHated()->getTarget() == target)
return !target->HasAura(SPELL_SLEEP);
return true;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
_EnterCombat();
events.ScheduleEvent(EVENT_SLEEP, 12s, 15s);
events.ScheduleEvent(EVENT_EARTH_SHOCK, 8s, 18s);
events.ScheduleEvent(EVENT_CHAIN_BURN, 12s, 28s);
events.ScheduleEvent(EVENT_ILLUSIONS, 16s, 24s);
}
bool CanAIAttack(Unit const* target) const override
{
if (me->GetThreatMgr().getThreatList().size() > 1 && me->GetThreatMgr().getOnlineContainer().getMostHated()->getTarget() == target)
return !target->HasAura(SPELL_SLEEP);
return true;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
switch (eventId)
{
switch (eventId)
{
case EVENT_SLEEP:
DoCastVictim(SPELL_SLEEP, true);
events.ScheduleEvent(EVENT_SLEEP, 24s, 32s);
return;
case EVENT_EARTH_SHOCK:
DoCastVictim(SPELL_EARTH_SHOCK);
events.ScheduleEvent(EVENT_EARTH_SHOCK, 8s, 18s);
break;
case EVENT_CHAIN_BURN:
case EVENT_SLEEP:
DoCastVictim(SPELL_SLEEP, true);
events.ScheduleEvent(EVENT_SLEEP, 24s, 32s);
return;
case EVENT_EARTH_SHOCK:
DoCastVictim(SPELL_EARTH_SHOCK);
events.ScheduleEvent(EVENT_EARTH_SHOCK, 8s, 18s);
break;
case EVENT_CHAIN_BURN:
if (me->GetPowerPct(POWER_MANA) > 5.f) // totally guessed
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, [&](Unit* u) { return u && !u->IsPet() && u->getPowerType() == POWER_MANA; }))
{
DoCast(target, SPELL_CHAIN_BURN, false);
DoCast(target, SPELL_CHAIN_BURN);
}
events.ScheduleEvent(EVENT_CHAIN_BURN, 12s, 28s);
break;
case EVENT_ILLUSIONS:
DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_LEFT, true);
DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_BACK, true);
DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_RIGHT, true);
events.ScheduleEvent(EVENT_ILLUSIONS, 16s, 24s);
break;
default:
break;
}
}
events.ScheduleEvent(EVENT_CHAIN_BURN, 12s, 28s);
break;
case EVENT_ILLUSIONS:
DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_LEFT, true);
DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_BACK, true);
DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_RIGHT, true);
events.ScheduleEvent(EVENT_ILLUSIONS, 15s, 25s);
break;
default:
break;
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulGurubAI<boss_hazzarahAI>(creature);
DoMeleeAttackIfReady();
}
};
@@ -164,6 +155,6 @@ class spell_chain_burn : public SpellScript
void AddSC_boss_hazzarah()
{
new boss_hazzarah();
RegisterZulGurubCreatureAI(boss_hazzarah);
RegisterSpellScript(spell_chain_burn);
}

View File

@@ -15,15 +15,10 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Boss_Jin'do the Hexxer
SD%Complete: 85
SDComment: Mind Control not working because of core bug. Shades visible for all.
SDCategory: Zul'Gurub
EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "TaskScheduler.h"
#include "zulgurub.h"
enum Say
@@ -33,269 +28,265 @@ enum Say
enum Spells
{
SPELL_BRAINWASHTOTEM = 24262,
SPELL_POWERFULLHEALINGWARD = 24309,
SPELL_BRAIN_WASH_TOTEM = 24262,
SPELL_POWERFULL_HEALING_WARD = 24309,
SPELL_HEX = 24053,
SPELL_DELUSIONSOFJINDO = 24306,
SPELL_DELUSIONS_OF_JINDO = 24306,
SPELL_SUMMON_SHADE_OF_JINDO = 24308,
SPELL_BANISH = 24466,
//Healing Ward Spell
SPELL_HEAL = 24311,
//Shade of Jindo Spell
SPELL_SHADEOFJINDO_PASSIVE = 24307,
SPELL_SHADEOFJINDO_VISUAL = 24313,
SPELL_SHADOWSHOCK = 19460
SPELL_SHADE_OF_JINDO_PASSIVE = 24307,
SPELL_SHADE_OF_JINDO_VISUAL = 24313,
SPELL_SHADOW_SHOCK = 19460,
SPELL_RANDOM_AGGRO = 23878
};
enum Events
{
EVENT_BRAINWASHTOTEM = 1,
EVENT_POWERFULLHEALINGWARD = 2,
EVENT_BRAIN_WASH_TOTEM = 1,
EVENT_POWERFULL_HEALING_WARD = 2,
EVENT_HEX = 3,
EVENT_DELUSIONSOFJINDO = 4,
EVENT_DELUSIONS_OF_JINDO = 4,
EVENT_TELEPORT = 5
};
Position const TeleportLoc = {-11583.7783f, -1249.4278f, 77.5471f, 4.745f};
class boss_jindo : public CreatureScript
struct boss_jindo : public BossAI
{
public:
boss_jindo() : CreatureScript("boss_jindo") { }
boss_jindo(Creature* creature) : BossAI(creature, DATA_JINDO) { }
struct boss_jindoAI : public BossAI
void EnterCombat(Unit* who) override
{
boss_jindoAI(Creature* creature) : BossAI(creature, DATA_JINDO) { }
BossAI::EnterCombat(who);
events.ScheduleEvent(EVENT_BRAIN_WASH_TOTEM, 20000);
events.ScheduleEvent(EVENT_POWERFULL_HEALING_WARD, 16000);
events.ScheduleEvent(EVENT_HEX, 8000);
events.ScheduleEvent(EVENT_DELUSIONS_OF_JINDO, 10000);
events.ScheduleEvent(EVENT_TELEPORT, 5000);
void Reset() override
Talk(SAY_AGGRO);
}
void JustSummoned(Creature* summon) override
{
BossAI::JustSummoned(summon);
switch (summon->GetEntry())
{
_Reset();
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
}
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
events.ScheduleEvent(EVENT_BRAINWASHTOTEM, 20000);
events.ScheduleEvent(EVENT_POWERFULLHEALINGWARD, 16000);
events.ScheduleEvent(EVENT_HEX, 8000);
events.ScheduleEvent(EVENT_DELUSIONSOFJINDO, 10000);
events.ScheduleEvent(EVENT_TELEPORT, 5000);
Talk(SAY_AGGRO);
}
void JustSummoned(Creature* summon) override
{
BossAI::JustSummoned(summon);
switch (summon->GetEntry())
case NPC_BRAIN_WASH_TOTEM:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1))
{
case NPC_BRAIN_WASH_TOTEM:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1))
{
summon->CastSpell(target, summon->m_spells[0], true);
}
break;
default:
break;
summon->CastSpell(target, summon->m_spells[0], true);
}
break;
default:
break;
}
}
void EnterEvadeMode(EvadeReason evadeReason) override
{
if (_EnterEvadeMode(evadeReason))
{
me->AddUnitState(UNIT_STATE_EVADE);
Reset();
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_DANCE);
me->m_Events.AddEventAtOffset([&]()
{
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
me->GetMotionMaster()->MoveTargetedHome();
}, 4s);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_BRAIN_WASH_TOTEM:
DoCastSelf(SPELL_BRAIN_WASH_TOTEM);
events.ScheduleEvent(EVENT_BRAIN_WASH_TOTEM, urand(18000, 26000));
break;
case EVENT_POWERFULL_HEALING_WARD:
DoCastSelf(SPELL_POWERFULL_HEALING_WARD, true);
events.ScheduleEvent(EVENT_POWERFULL_HEALING_WARD, urand(14000, 20000));
break;
case EVENT_HEX:
if (me->GetThreatMgr().getThreatList().size() > 1)
DoCastVictim(SPELL_HEX, true);
events.ScheduleEvent(EVENT_HEX, urand(12000, 20000));
break;
case EVENT_DELUSIONS_OF_JINDO:
DoCastRandomTarget(SPELL_DELUSIONS_OF_JINDO);
events.ScheduleEvent(EVENT_DELUSIONS_OF_JINDO, urand(4000, 12000));
break;
case EVENT_TELEPORT:
DoCastRandomTarget(SPELL_BANISH);
events.ScheduleEvent(EVENT_TELEPORT, urand(15000, 23000));
break;
default:
break;
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_BRAINWASHTOTEM:
DoCast(me, SPELL_BRAINWASHTOTEM);
events.ScheduleEvent(EVENT_BRAINWASHTOTEM, urand(18000, 26000));
break;
case EVENT_POWERFULLHEALINGWARD:
DoCastSelf(SPELL_POWERFULLHEALINGWARD, true);
events.ScheduleEvent(EVENT_POWERFULLHEALINGWARD, urand(14000, 20000));
break;
case EVENT_HEX:
DoCastVictim(SPELL_HEX, true);
events.ScheduleEvent(EVENT_HEX, urand(12000, 20000));
break;
case EVENT_DELUSIONSOFJINDO: // HACK
// Casting the delusion curse with a shade so shade will attack the same target with the curse.
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
DoCast(target, SPELL_DELUSIONSOFJINDO);
Creature* Shade = me->SummonCreature(NPC_SHADE_OF_JINDO, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (Shade)
Shade->AI()->AttackStart(target);
}
events.ScheduleEvent(EVENT_DELUSIONSOFJINDO, urand(4000, 12000));
break;
case EVENT_TELEPORT: // Possible HACK
// Teleports a random player and spawns 9 Sacrificed Trolls to attack player
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
DoTeleportPlayer(target, TeleportLoc.m_positionX, TeleportLoc.m_positionY, TeleportLoc.m_positionZ, TeleportLoc.GetOrientation());
if (DoGetThreat(me->GetVictim()))
DoModifyThreatPercent(target, -100);
Creature* SacrificedTroll;
SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, TeleportLoc.m_positionX + 2, TeleportLoc.m_positionY, TeleportLoc.m_positionZ, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (SacrificedTroll)
SacrificedTroll->AI()->AttackStart(target);
SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, TeleportLoc.m_positionX - 2, TeleportLoc.m_positionY, TeleportLoc.m_positionZ, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (SacrificedTroll)
SacrificedTroll->AI()->AttackStart(target);
SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, TeleportLoc.m_positionX + 4, TeleportLoc.m_positionY, TeleportLoc.m_positionZ, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (SacrificedTroll)
SacrificedTroll->AI()->AttackStart(target);
SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, TeleportLoc.m_positionX - 4, TeleportLoc.m_positionY, TeleportLoc.m_positionZ, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (SacrificedTroll)
SacrificedTroll->AI()->AttackStart(target);
SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, TeleportLoc.m_positionX, TeleportLoc.m_positionY + 2, TeleportLoc.m_positionZ, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (SacrificedTroll)
SacrificedTroll->AI()->AttackStart(target);
SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, TeleportLoc.m_positionX, TeleportLoc.m_positionY - 2, TeleportLoc.m_positionZ, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (SacrificedTroll)
SacrificedTroll->AI()->AttackStart(target);
SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, TeleportLoc.m_positionX, TeleportLoc.m_positionY + 4, TeleportLoc.m_positionZ, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (SacrificedTroll)
SacrificedTroll->AI()->AttackStart(target);
SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, TeleportLoc.m_positionX, TeleportLoc.m_positionY - 4, TeleportLoc.m_positionZ, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (SacrificedTroll)
SacrificedTroll->AI()->AttackStart(target);
SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, TeleportLoc.m_positionX + 3, TeleportLoc.m_positionY, TeleportLoc.m_positionZ, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (SacrificedTroll)
SacrificedTroll->AI()->AttackStart(target);
}
events.ScheduleEvent(EVENT_TELEPORT, urand(15000, 23000));
break;
default:
break;
}
}
DoMeleeAttackIfReady();
}
bool CanAIAttack(Unit const* target) const override
{
bool CanAIAttack(Unit const* target) const override
{
if (me->GetThreatMgr().getThreatList().size() > 1 && me->GetThreatMgr().getOnlineContainer().getMostHated()->getTarget() == target)
return !target->HasAura(SPELL_HEX);
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulGurubAI<boss_jindoAI>(creature);
return true;
}
};
//Healing Ward
class npc_healing_ward : public CreatureScript
struct npc_healing_ward : public ScriptedAI
{
public:
npc_healing_ward()
: CreatureScript("npc_healing_ward")
npc_healing_ward(Creature* creature) : ScriptedAI(creature)
{
_instance = creature->GetInstanceScript();
}
struct npc_healing_wardAI : public ScriptedAI
void Reset() override
{
npc_healing_wardAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
}
_scheduler.CancelAll();
}
uint32 Heal_Timer;
InstanceScript* instance;
void Reset() override
{
Heal_Timer = 2000;
}
void EnterCombat(Unit* /*who*/) override
{
}
void UpdateAI(uint32 diff) override
{
//Heal_Timer
if (Heal_Timer <= diff)
void EnterCombat(Unit* /*who*/) override
{
_scheduler.
Schedule(2s, [this](TaskContext context)
{
Unit* pJindo = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JINDO));
Unit* pJindo = ObjectAccessor::GetUnit(*me, _instance->GetGuidData(DATA_JINDO));
if (pJindo)
DoCast(pJindo, SPELL_HEAL);
Heal_Timer = 3000;
}
else Heal_Timer -= diff;
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulGurubAI<npc_healing_wardAI>(creature);
context.Repeat(3s);
});
}
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff, [this]
{
DoMeleeAttackIfReady();
});
}
private:
InstanceScript* _instance;
TaskScheduler _scheduler;
};
//Shade of Jindo
class npc_shade_of_jindo : public CreatureScript
struct npc_shade_of_jindo : public ScriptedAI
{
public:
npc_shade_of_jindo()
: CreatureScript("npc_shade_of_jindo")
npc_shade_of_jindo(Creature* creature) : ScriptedAI(creature) { }
void IsSummonedBy(Unit* /*summoner*/) override
{
DoZoneInCombat();
DoCastSelf(SPELL_SHADE_OF_JINDO_PASSIVE, true);
DoCastSelf(SPELL_SHADE_OF_JINDO_VISUAL, true);
DoCastAOE(SPELL_RANDOM_AGGRO, true);
}
struct npc_shade_of_jindoAI : public ScriptedAI
void Reset() override
{
npc_shade_of_jindoAI(Creature* creature) : ScriptedAI(creature) { }
_scheduler.CancelAll();
uint32 ShadowShock_Timer;
void Reset() override
{
ShadowShock_Timer = 1000;
DoCastSelf(SPELL_SHADEOFJINDO_PASSIVE, true);
DoCastSelf(SPELL_SHADEOFJINDO_VISUAL, true);
}
void EnterCombat(Unit* /*who*/) override { }
void UpdateAI(uint32 diff) override
{
//ShadowShock_Timer
if (ShadowShock_Timer <= diff)
_scheduler.
Schedule(1s, [this](TaskContext context)
{
DoCastVictim(SPELL_SHADOWSHOCK);
ShadowShock_Timer = 2000;
}
else ShadowShock_Timer -= diff;
DoCastAOE(SPELL_RANDOM_AGGRO, true);
context.Repeat();
});
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
void EnterCombat(Unit* /*who*/) override
{
return GetZulGurubAI<npc_shade_of_jindoAI>(creature);
_scheduler.
Schedule(1s, [this](TaskContext context)
{
DoCastVictim(SPELL_SHADOW_SHOCK);
context.Repeat(2s);
});
}
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff, [this]
{
DoMeleeAttackIfReady();
});
}
private:
TaskScheduler _scheduler;
};
class spell_random_aggro : public SpellScript
{
PrepareSpellScript(spell_random_aggro);
void HandleOnHit()
{
Unit* caster = GetCaster();
Unit* target = GetHitUnit();
if (!caster || !target || !caster->GetAI())
return;
caster->GetAI()->AttackStart(target);
}
void Register() override
{
OnHit += SpellHitFn(spell_random_aggro::HandleOnHit);
}
};
class spell_delusions_of_jindo : public SpellScript
{
PrepareSpellScript(spell_delusions_of_jindo);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_SUMMON_SHADE_OF_JINDO });
}
void HandleOnHit()
{
Unit* caster = GetCaster();
if (caster)
caster->CastSpell(caster, SPELL_SUMMON_SHADE_OF_JINDO, true);
}
void Register() override
{
OnHit += SpellHitFn(spell_delusions_of_jindo::HandleOnHit);
}
};
void AddSC_boss_jindo()
{
new boss_jindo();
new npc_healing_ward();
new npc_shade_of_jindo();
RegisterZulGurubCreatureAI(boss_jindo);
RegisterZulGurubCreatureAI(npc_healing_ward);
RegisterZulGurubCreatureAI(npc_shade_of_jindo);
RegisterSpellScript(spell_random_aggro);
RegisterSpellScript(spell_delusions_of_jindo);
}

View File

@@ -71,9 +71,6 @@ public:
Initialize();
}
bool Enraged;
bool WasDead;
void Initialize()
{
Enraged = false;
@@ -90,17 +87,16 @@ public:
me->SetStandState(UNIT_STAND_STATE_STAND);
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->LoadEquipment(1, true);
if (Creature* zealot = instance->GetCreature(DATA_LORKHAN))
{
zealot->AI()->Reset();
zealot->ResetFaction();
}
if (Creature* zealot = instance->GetCreature(DATA_ZATH))
{
zealot->AI()->Reset();
zealot->ResetFaction();
}
_scheduler.SetValidator([this]
@@ -148,7 +144,7 @@ public:
CheckPhaseTransition();
_scheduler.Schedule(10s, [this, data](TaskContext /*context*/) {
if ((!_lorkhanDied || !_zathDied) && !WasDead)
if (!_lorkhanDied || !_zathDied || !WasDead)
{
ReviveZealot(data);
}
@@ -157,19 +153,24 @@ public:
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (!WasDead && damage >= me->GetHealth())
if (!me->HasAura(SPELL_TIGER_FORM) && damage >= me->GetHealth())
{
damage = me->GetHealth() - 1;
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_PASSIVE);
me->SetStandState(UNIT_STAND_STATE_SLEEP);
me->AttackStop();
WasDead = true;
CheckPhaseTransition();
Talk(EMOTE_THEKAL_DIES);
if (!WasDead)
{
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_PASSIVE);
me->SetStandState(UNIT_STAND_STATE_SLEEP);
me->AttackStop();
DoResetThreat();
WasDead = true;
CheckPhaseTransition();
Talk(EMOTE_THEKAL_DIES);
}
}
if (!Enraged && me->HealthBelowPctDamaged(20, damage) && me->GetEntry() != NPC_HIGH_PRIEST_THEKAL)
if (!Enraged && me->HealthBelowPctDamaged(20, damage) && me->HasAura(SPELL_TIGER_FORM))
{
DoCastSelf(SPELL_ENRAGE);
Enraged = true;
@@ -182,7 +183,7 @@ public:
{
me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->ResetFaction();
me->RestoreFaction();
me->SetReactState(REACT_AGGRESSIVE);
me->SetFullHealth();
WasDead = false;
@@ -202,11 +203,8 @@ public:
{
if (Creature* zealot = instance->GetCreature(zealotData))
{
zealot->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
zealot->ResetFaction();
zealot->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
zealot->SetReactState(REACT_AGGRESSIVE);
zealot->SetFullHealth();
zealot->Respawn(true);
zealot->SetInCombatWithZone();
UpdateZealotStatus(zealotData, false);
}
}
@@ -231,10 +229,10 @@ public:
Talk(SAY_AGGRO);
me->SetStandState(UNIT_STAND_STATE_STAND);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
DoResetThreat();
_scheduler.Schedule(6s, [this](TaskContext /*context*/) {
DoCastSelf(SPELL_TIGER_FORM);
me->LoadEquipment(0, true);
me->SetReactState(REACT_AGGRESSIVE);
_scheduler.Schedule(30s, [this](TaskContext context) {
@@ -261,7 +259,10 @@ public:
else
{
_scheduler.Schedule(10s, [this](TaskContext /*context*/) {
DoAction(ACTION_RESSURRECT);
if (!(WasDead && _lorkhanDied && _zathDied))
{
DoAction(ACTION_RESSURRECT);
}
});
}
}
@@ -271,6 +272,8 @@ public:
GuidVector _catGuids;
bool _lorkhanDied;
bool _zathDied;
bool Enraged;
bool WasDead;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -295,10 +298,6 @@ public:
void Reset() override
{
me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_AGGRESSIVE);
_scheduler.CancelAll();
_scheduler.SetValidator([this]
@@ -339,24 +338,13 @@ public:
});
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
void JustDied(Unit* /*killer*/) override
{
if (damage >= me->GetHealth() && me->HasReactState(REACT_AGGRESSIVE))
Talk(EMOTE_ZEALOT_DIES);
if (Creature* thekal = instance->GetCreature(DATA_THEKAL))
{
Talk(EMOTE_ZEALOT_DIES);
me->RemoveAllAuras();
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetStandState(UNIT_STAND_STATE_SLEEP);
me->SetReactState(REACT_PASSIVE);
me->InterruptNonMeleeSpells(false);
me->AttackStop();
damage = 0;
if (Creature* thekal = instance->GetCreature(DATA_THEKAL))
{
thekal->AI()->SetData(ACTION_RESSURRECT, DATA_LORKHAN);
}
thekal->AI()->SetData(ACTION_RESSURRECT, DATA_LORKHAN);
}
}
@@ -395,10 +383,6 @@ public:
void Reset() override
{
me->SetStandState(UNIT_STAND_STATE_STAND);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_AGGRESSIVE);
_scheduler.CancelAll();
_scheduler.SetValidator([this]
@@ -436,23 +420,13 @@ public:
});
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
void JustDied(Unit* /*killer*/) override
{
if (damage >= me->GetHealth() && me->HasReactState(REACT_AGGRESSIVE))
Talk(EMOTE_ZEALOT_DIES);
if (Creature* thekal = instance->GetCreature(DATA_THEKAL))
{
Talk(EMOTE_ZEALOT_DIES);
me->RemoveAllAuras();
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetStandState(UNIT_STAND_STATE_SLEEP);
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
damage = 0;
if (Creature* thekal = instance->GetCreature(DATA_THEKAL))
{
thekal->AI()->SetData(ACTION_RESSURRECT, DATA_ZATH);
}
thekal->AI()->SetData(ACTION_RESSURRECT, DATA_ZATH);
}
}

View File

@@ -51,7 +51,6 @@ public:
instance_zulgurub_InstanceMapScript(Map* map) : InstanceScript(map)
{
SetBossNumber(EncounterCount);
LoadObjectData(creatureData, nullptr);
LoadDoorData(doorData);
LoadObjectData(creatureData, nullptr);
}
@@ -83,6 +82,12 @@ public:
case NPC_GAHZRANKA:
_gahzrankaGUID = creature->GetGUID();
break;
case NPC_GRILEK:
case NPC_HAZZARAH:
case NPC_RENATAKI:
case NPC_WUSHOOLAY:
_edgeOfMadnessGUID = creature->GetGUID();
break;
default:
break;
}
@@ -120,6 +125,8 @@ public:
return _goGongOfBethekkGUID;
case DATA_HAKKAR:
return _hakkarGUID;
case DATA_EDGE_OF_MADNESS:
return _edgeOfMadnessGUID;
}
return ObjectGuid::Empty;
@@ -135,6 +142,36 @@ public:
return 0;
}
void RemoveHakkarPowerStack()
{
if (Creature* hakkar = instance->GetCreature(_hakkarGUID))
{
hakkar->CastSpell(hakkar, SPELL_HAKKAR_POWER_DOWN, true);
}
}
bool SetBossState(uint32 type, EncounterState state) override
{
if (!InstanceScript::SetBossState(type, state))
return false;
switch (type)
{
case DATA_JEKLIK:
case DATA_VENOXIS:
case DATA_MARLI:
case DATA_ARLOKK:
case DATA_THEKAL:
if (state == DONE)
RemoveHakkarPowerStack();
break;
default:
break;
}
return true;
}
std::string GetSaveData() override
{
OUT_SAVE_INST_DATA;
@@ -187,6 +224,7 @@ public:
ObjectGuid _goGongOfBethekkGUID;
ObjectGuid _hakkarGUID;
ObjectGuid _gahzrankaGUID;
ObjectGuid _edgeOfMadnessGUID;
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override
@@ -224,6 +262,14 @@ struct go_brazier_of_madness : public GameObjectAI
return true;
}
if (InstanceScript* instanceScript = me->GetInstanceScript())
{
if (instanceScript->GetGuidData(DATA_EDGE_OF_MADNESS))
{
return false;
}
}
uint32 bossEntry = 0;
for (uint8 i = 0; i < 4; ++i)
{

View File

@@ -76,6 +76,12 @@ enum GameobjectIds
GO_GONG_OF_BETHEKK = 180526 // Arlokk Event
};
enum SpellIds
{
SPELL_HAKKAR_POWER = 24692,
SPELL_HAKKAR_POWER_DOWN = 24693
};
template <class AI, class T>
inline AI* GetZulGurubAI(T* obj)
{

View File

@@ -453,7 +453,8 @@ enum eJuggle
SPELL_TORCH_CHECK = 45644,
SPELL_GIVE_TORCH = 45280,
QUEST_CHECK = 11937,
QUEST_TORCH_CATCHING_A = 11657,
QUEST_TORCH_CATCHING_H = 11923
};
class spell_midsummer_juggling_torch : public SpellScript
@@ -507,6 +508,36 @@ class spell_midsummer_juggling_torch : public SpellScript
}
};
// 45644 - Juggle Torch (Catch)
class spell_midsummer_torch_catch : public SpellScript
{
PrepareSpellScript(spell_midsummer_torch_catch);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_GIVE_TORCH });
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
Player* player = GetHitPlayer();
if (!player)
{
return;
}
if (player->GetQuestStatus(QUEST_TORCH_CATCHING_A) == QUEST_STATUS_REWARDED || player->GetQuestStatus(QUEST_TORCH_CATCHING_H) == QUEST_STATUS_REWARDED)
{
player->CastSpell(player, SPELL_GIVE_TORCH);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_midsummer_torch_catch::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
void AddSC_event_midsummer_scripts()
{
// NPCs
@@ -520,4 +551,5 @@ void AddSC_event_midsummer_scripts()
RegisterSpellScript(spell_midsummer_torch_quest);
RegisterSpellScript(spell_midsummer_fling_torch);
RegisterSpellScript(spell_midsummer_juggling_torch);
RegisterSpellScript(spell_midsummer_torch_catch);
}

View File

@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AreaBoundary.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "azjol_nerub.h"
@@ -34,6 +35,13 @@ ObjectData const creatureData[] =
{ NPC_HADRONOX, DATA_HADRONOX_EVENT }
};
BossBoundaryData const boundaries =
{
{ DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, new RectangleBoundary(400.0f, 580.0f, 623.5f, 810.0f) },
{ DATA_HADRONOX_EVENT, new ZRangeBoundary(666.0f, 776.0f) },
{ DATA_ANUBARAK_EVENT, new CircleBoundary(Position(550.6178f, 253.5917f), 26.0f) }
};
class instance_azjol_nerub : public InstanceMapScript
{
public:
@@ -44,6 +52,7 @@ public:
instance_azjol_nerub_InstanceScript(Map* map) : InstanceScript(map)
{
SetBossNumber(MAX_ENCOUNTERS);
LoadBossBoundaries(boundaries);
LoadDoorData(doorData);
LoadObjectData(creatureData, nullptr);
};

View File

@@ -125,20 +125,27 @@ public:
{
if (HealthBelowPct(50) && !health50)
{
WorldObject* summoner = nullptr;
if (TempSummon const* tempSummon = me->ToTempSummon())
{
summoner = tempSummon->GetSummonerUnit();
if (WorldObject* summoner = tempSummon->GetSummonerUnit())
{
Talk(SAY_TURMOIL_HALF_HP, summoner);
}
}
Talk(SAY_TURMOIL_HALF_HP, summoner);
health50 = true;
}
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_TURMOIL_DEATH, me->ToTempSummon()->GetSummonerUnit()->ToPlayer());
if (TempSummon const* tempSummon = me->ToTempSummon())
{
if (WorldObject* summoner = tempSummon->GetSummonerUnit())
{
Talk(SAY_TURMOIL_DEATH, summoner);
}
}
}
void setphase(short newPhase)

View File

@@ -268,7 +268,19 @@ public:
{
Talk(SAY_YSONDRE_SUMMON_DRUIDS);
for (uint8 i = 0; i < 10; ++i)
auto const& attackers = me->GetThreatMgr().getThreatList();
uint8 attackersCount = 0;
for (const auto attacker : attackers)
{
if ((*attacker)->ToPlayer() && (*attacker)->IsAlive())
++attackersCount;
}
uint8 amount = attackersCount < 30 ? attackersCount * 0.5f : 15;
amount = amount < 1 ? 1 : amount;
for (uint8 i = 0; i < amount; ++i)
DoCast(me, SPELL_SUMMON_DRUID_SPIRITS, true);
++_stage;
}
@@ -643,6 +655,12 @@ public:
emerald_dragonAI::UpdateAI(diff);
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
}
private:
bool _banished; // used for shades activation testing
uint32 _banishedTimer; // counter for banishment timeout

View File

@@ -821,19 +821,19 @@ public:
if (!IsHolidayActive(HOLIDAY_FIRE_FESTIVAL))
break;
Map::PlayerList const& players = me->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
std::list<Player*> targets;
Acore::AnyPlayerInObjectRangeCheck check(me, me->GetVisibilityRange(), false);
Acore::PlayerListSearcherWithSharedVision<Acore::AnyPlayerInObjectRangeCheck> searcher(me, targets, check);
Cell::VisitWorldObjects(me, searcher, me->GetVisibilityRange());
for (Player* player : targets)
{
if (Player* player = itr->GetSource())
if (player->GetTeamId() == TEAM_HORDE)
{
if (player->GetTeamId() == TEAM_HORDE)
{
me->PlayDirectMusic(EVENTMIDSUMMERFIREFESTIVAL_H, player);
}
else
{
me->PlayDirectMusic(EVENTMIDSUMMERFIREFESTIVAL_A, player);
}
me->PlayDirectMusic(EVENTMIDSUMMERFIREFESTIVAL_H, player);
}
else
{
me->PlayDirectMusic(EVENTMIDSUMMERFIREFESTIVAL_A, player);
}
}

View File

@@ -769,13 +769,12 @@ public:
void Reset() override
{
Active = true;
CanIteract = 3500;
Active = false;
CanIteract = 0;
DoCast(me, SPELL_BRAZIER, true);
DoCast(me, SPELL_FIERY_AURA, false);
me->UpdateHeight(me->GetPositionZ() + 0.94f);
me->SetDisableGravity(true);
me->HandleEmoteCommand(EMOTE_ONESHOT_DANCE);
me->SendMovementFlagUpdate();
}