Merge branch 'master' into Playerbot
# Conflicts: # src/server/game/Guilds/Guild.cpp
This commit is contained in:
@@ -72,13 +72,15 @@ char serviceDescription[] = "AzerothCore World of Warcraft emulator world servic
|
||||
* 2 - paused
|
||||
*/
|
||||
int m_ServiceStatus = -1;
|
||||
|
||||
#include <boost/dll/shared_library.hpp>
|
||||
#include <timeapi.h>
|
||||
#endif
|
||||
|
||||
#ifndef _ACORE_CORE_CONFIG
|
||||
#define _ACORE_CORE_CONFIG "worldserver.conf"
|
||||
#endif
|
||||
|
||||
#define WORLD_SLEEP_CONST 10
|
||||
using namespace boost::program_options;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -137,6 +139,44 @@ int main(int argc, char** argv)
|
||||
return WinServiceUninstall() == true ? 0 : 1;
|
||||
else if (configService.compare("run") == 0)
|
||||
WinServiceRun();
|
||||
|
||||
Optional<UINT> newTimerResolution;
|
||||
boost::system::error_code dllError;
|
||||
std::shared_ptr<boost::dll::shared_library> winmm(new boost::dll::shared_library("winmm.dll", dllError, boost::dll::load_mode::search_system_folders), [&](boost::dll::shared_library* lib)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (newTimerResolution)
|
||||
lib->get<decltype(timeEndPeriod)>("timeEndPeriod")(*newTimerResolution);
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
delete lib;
|
||||
});
|
||||
|
||||
if (winmm->is_loaded())
|
||||
{
|
||||
try
|
||||
{
|
||||
auto timeGetDevCapsPtr = winmm->get<decltype(timeGetDevCaps)>("timeGetDevCaps");
|
||||
// setup timer resolution
|
||||
TIMECAPS timeResolutionLimits;
|
||||
if (timeGetDevCapsPtr(&timeResolutionLimits, sizeof(TIMECAPS)) == TIMERR_NOERROR)
|
||||
{
|
||||
auto timeBeginPeriodPtr = winmm->get<decltype(timeBeginPeriod)>("timeBeginPeriod");
|
||||
newTimerResolution = std::min(std::max(timeResolutionLimits.wPeriodMin, 1u), timeResolutionLimits.wPeriodMax);
|
||||
timeBeginPeriodPtr(*newTimerResolution);
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
printf("Failed to initialize timer resolution: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Add file and args in config
|
||||
@@ -530,9 +570,15 @@ void ShutdownCLIThread(std::thread* cliThread)
|
||||
|
||||
void WorldUpdateLoop()
|
||||
{
|
||||
uint32 minUpdateDiff = uint32(sConfigMgr->GetOption<int32>("MinWorldUpdateTime", 1));
|
||||
uint32 realCurrTime = 0;
|
||||
uint32 realPrevTime = getMSTime();
|
||||
|
||||
uint32 maxCoreStuckTime = uint32(sConfigMgr->GetOption<int32>("MaxCoreStuckTime", 60)) * 1000;
|
||||
uint32 halfMaxCoreStuckTime = maxCoreStuckTime / 2;
|
||||
if (!halfMaxCoreStuckTime)
|
||||
halfMaxCoreStuckTime = std::numeric_limits<uint32>::max();
|
||||
|
||||
LoginDatabase.WarnAboutSyncQueries(true);
|
||||
CharacterDatabase.WarnAboutSyncQueries(true);
|
||||
WorldDatabase.WarnAboutSyncQueries(true);
|
||||
@@ -546,18 +592,19 @@ void WorldUpdateLoop()
|
||||
realCurrTime = getMSTime();
|
||||
|
||||
uint32 diff = getMSTimeDiff(realPrevTime, realCurrTime);
|
||||
if (diff < minUpdateDiff)
|
||||
{
|
||||
uint32 sleepTime = minUpdateDiff - diff;
|
||||
if (sleepTime >= halfMaxCoreStuckTime)
|
||||
LOG_ERROR("server.worldserver", "WorldUpdateLoop() waiting for {} ms with MaxCoreStuckTime set to {} ms", sleepTime, maxCoreStuckTime);
|
||||
// sleep until enough time passes that we can update all timers
|
||||
std::this_thread::sleep_for(Milliseconds(sleepTime));
|
||||
continue;
|
||||
}
|
||||
|
||||
sWorld->Update(diff);
|
||||
realPrevTime = realCurrTime;
|
||||
|
||||
uint32 executionTimeDiff = getMSTimeDiff(realCurrTime, getMSTime());
|
||||
|
||||
// we know exactly how long it took to update the world, if the update took less than WORLD_SLEEP_CONST, sleep for WORLD_SLEEP_CONST - world update time
|
||||
if (executionTimeDiff < WORLD_SLEEP_CONST)
|
||||
{
|
||||
std::this_thread::sleep_for(Milliseconds(WORLD_SLEEP_CONST - executionTimeDiff));
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (m_ServiceStatus == 0)
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
@@ -595,10 +642,14 @@ void FreezeDetector::Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, bo
|
||||
freezeDetector->_worldLoopCounter = worldLoopCounter;
|
||||
}
|
||||
// possible freeze
|
||||
else if (getMSTimeDiff(freezeDetector->_lastChangeMsTime, curtime) > freezeDetector->_maxCoreStuckTimeInMs)
|
||||
else
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!");
|
||||
ABORT();
|
||||
uint32 msTimeDiff = getMSTimeDiff(freezeDetector->_lastChangeMsTime, curtime);
|
||||
if (msTimeDiff > freezeDetector->_maxCoreStuckTimeInMs)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "World Thread hangs for {} ms, forcing a crash!", msTimeDiff);
|
||||
ABORT("World Thread hangs for {} ms, forcing a crash!", msTimeDiff);
|
||||
}
|
||||
}
|
||||
|
||||
freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(1));
|
||||
|
||||
@@ -339,12 +339,19 @@ SocketTimeOutTimeActive = 60000
|
||||
|
||||
SessionAddDelay = 10000
|
||||
|
||||
#
|
||||
# MinWorldUpdateTime
|
||||
# Description: Minimum time (milliseconds) between world update ticks (for mostly idle servers).
|
||||
# Default: 1 - (0.001 second)
|
||||
|
||||
MinWorldUpdateTime = 1
|
||||
|
||||
#
|
||||
# MapUpdateInterval
|
||||
# Description: Time (milliseconds) for map update interval.
|
||||
# Default: 100 - (0.1 second)
|
||||
# Default: 10 - (0.01 second)
|
||||
|
||||
MapUpdateInterval = 100
|
||||
MapUpdateInterval = 10
|
||||
|
||||
#
|
||||
# ChangeWeatherInterval
|
||||
@@ -467,6 +474,7 @@ LogDB.Opt.ClearTime = 1209600
|
||||
#
|
||||
# MaxCoreStuckTime
|
||||
# Description: Time (in seconds) before the server is forced to crash if it is frozen.
|
||||
# FreezeDetector
|
||||
# Default: 0 - (Disabled)
|
||||
# 10+ - (Enabled, Recommended 30+)
|
||||
# Note: If enabled and the setting is too low, it can cause unexpected crash.
|
||||
|
||||
@@ -3905,7 +3905,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
if (!me || !me->IsEngaged())
|
||||
return;
|
||||
|
||||
ObjectVector targets;
|
||||
Unit* unitTarget = nullptr;
|
||||
switch (e.GetTargetType())
|
||||
{
|
||||
case SMART_TARGET_CREATURE_RANGE:
|
||||
@@ -3916,8 +3916,8 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
case SMART_TARGET_PLAYER_RANGE:
|
||||
case SMART_TARGET_PLAYER_DISTANCE:
|
||||
{
|
||||
if (targets.empty())
|
||||
return;
|
||||
ObjectVector targets;
|
||||
GetTargets(targets, e);
|
||||
for (WorldObject* target : targets)
|
||||
{
|
||||
if (IsUnit(target) && me->IsFriendlyTo(target->ToUnit()) && target->ToUnit()->IsAlive() && target->ToUnit()->IsInCombat())
|
||||
@@ -3928,7 +3928,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
continue;
|
||||
}
|
||||
|
||||
target = target->ToUnit();
|
||||
unitTarget = target->ToUnit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3937,16 +3937,16 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_TARGET_SELF:
|
||||
case SMART_TARGET_ACTION_INVOKER:
|
||||
DoSelectLowestHpPercentFriendly((float)e.event.friendlyHealthPct.radius, e.event.friendlyHealthPct.minHpPct, e.event.friendlyHealthPct.maxHpPct);
|
||||
unitTarget = DoSelectLowestHpPercentFriendly((float)e.event.friendlyHealthPct.radius, e.event.friendlyHealthPct.minHpPct, e.event.friendlyHealthPct.maxHpPct);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (targets.empty())
|
||||
if (!unitTarget)
|
||||
return;
|
||||
|
||||
ProcessTimedAction(e, e.event.friendlyHealthPct.repeatMin, e.event.friendlyHealthPct.repeatMax);
|
||||
ProcessTimedAction(e, e.event.friendlyHealthPct.repeatMin, e.event.friendlyHealthPct.repeatMax, unitTarget);
|
||||
break;
|
||||
}
|
||||
case SMART_EVENT_DISTANCE_CREATURE:
|
||||
|
||||
@@ -2101,34 +2101,52 @@ void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* onlinePet /*= nullptr*/)
|
||||
{
|
||||
// not need after this call
|
||||
if (owner->ToPlayer()->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
|
||||
{
|
||||
owner->ToPlayer()->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS, true);
|
||||
}
|
||||
|
||||
// reset for online
|
||||
if (onlinePet)
|
||||
{
|
||||
onlinePet->resetTalents();
|
||||
}
|
||||
|
||||
PetStable* petStable = owner->GetPetStable();
|
||||
if (!petStable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_set<uint32> petIds;
|
||||
if (petStable->CurrentPet)
|
||||
{
|
||||
petIds.insert(petStable->CurrentPet->PetNumber);
|
||||
}
|
||||
|
||||
for (Optional<PetStable::PetInfo> const& stabledPet : petStable->StabledPets)
|
||||
{
|
||||
if (stabledPet)
|
||||
{
|
||||
petIds.insert(stabledPet->PetNumber);
|
||||
}
|
||||
}
|
||||
|
||||
for (PetStable::PetInfo const& unslottedPet : petStable->UnslottedPets)
|
||||
{
|
||||
petIds.insert(unslottedPet.PetNumber);
|
||||
}
|
||||
|
||||
// now need only reset for offline pets (all pets except online case)
|
||||
if (onlinePet)
|
||||
{
|
||||
petIds.erase(onlinePet->GetCharmInfo()->GetPetNumber());
|
||||
}
|
||||
|
||||
// no offline pets
|
||||
if (!petIds.empty())
|
||||
if (petIds.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool need_comma = false;
|
||||
std::ostringstream ss;
|
||||
@@ -2137,7 +2155,9 @@ void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* onlinePet /*= nullptr*/)
|
||||
for (uint32 id : petIds)
|
||||
{
|
||||
if (need_comma)
|
||||
{
|
||||
ss << ',';
|
||||
}
|
||||
|
||||
ss << id;
|
||||
|
||||
@@ -2150,7 +2170,9 @@ void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* onlinePet /*= nullptr*/)
|
||||
for (uint32 spell : sPetTalentSpells)
|
||||
{
|
||||
if (need_comma)
|
||||
{
|
||||
ss << ',';
|
||||
}
|
||||
|
||||
ss << spell;
|
||||
|
||||
|
||||
@@ -1462,7 +1462,10 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
|
||||
else
|
||||
{
|
||||
if (getClass() == CLASS_DEATH_KNIGHT && GetMapId() == 609 && !IsGameMaster() && !HasSpell(50977))
|
||||
{
|
||||
SendTransferAborted(mapid, TRANSFER_ABORT_UNIQUE_MESSAGE, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// far teleport to another map
|
||||
Map* oldmap = IsInWorld() ? GetMap() : nullptr;
|
||||
@@ -6824,8 +6827,10 @@ void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingSt
|
||||
if (extraDPS)
|
||||
{
|
||||
float average = extraDPS * proto->Delay / 1000.0f;
|
||||
minDamage = 0.7f * average;
|
||||
maxDamage = 1.3f * average;
|
||||
float mod = ssv->IsTwoHand(proto->ScalingStatValue) ? 0.2f : 0.3f;
|
||||
|
||||
minDamage = (1.0f - mod) * average;
|
||||
maxDamage = (1.0f + mod) * average;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7220,6 +7225,15 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry && (entry->attributeMask & ENCHANT_PROC_ATTR_EXCLUSIVE) != 0)
|
||||
{
|
||||
Unit* checkTarget = spellInfo->IsPositive() ? this : target;
|
||||
if (checkTarget->HasAura(spellInfo->Id, GetGUID()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
float chance = pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance();
|
||||
|
||||
if (entry)
|
||||
@@ -11579,7 +11593,6 @@ void Player::SendInitialPacketsAfterAddToMap()
|
||||
SendMessageToSet(&data2, true);
|
||||
}
|
||||
|
||||
GetAurasForTarget(this);
|
||||
SendEnchantmentDurations(); // must be after add to map
|
||||
SendItemDurations(); // must be after add to map
|
||||
SendQuestGiverStatusMultiple();
|
||||
@@ -11960,9 +11973,9 @@ void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value)
|
||||
}
|
||||
}
|
||||
|
||||
void Player::GetAurasForTarget(Unit* target) // pussywizard: contact before changing ANYTHING!
|
||||
void Player::GetAurasForTarget(Unit* target, bool force /*= false*/)
|
||||
{
|
||||
if (!target/* || target->GetVisibleAuras()->empty()*/) // speedup things
|
||||
if (!target || (!force && target->GetVisibleAuras()->empty())) // speedup things
|
||||
return;
|
||||
|
||||
/*! Blizz sends certain movement packets sometimes even before CreateObject
|
||||
|
||||
@@ -2183,7 +2183,7 @@ public:
|
||||
void SendBGWeekendWorldStates();
|
||||
void SendBattlefieldWorldStates();
|
||||
|
||||
void GetAurasForTarget(Unit* target);
|
||||
void GetAurasForTarget(Unit* target, bool force = false);
|
||||
|
||||
PlayerMenu* PlayerTalkClass;
|
||||
std::vector<ItemSetEffect*> ItemSetEff;
|
||||
|
||||
@@ -3822,6 +3822,8 @@ void Unit::_UpdateSpells(uint32 time)
|
||||
{
|
||||
if (i->second->IsExpired())
|
||||
RemoveOwnedAura(i, AURA_REMOVE_BY_EXPIRE);
|
||||
else if (i->second->GetSpellInfo()->IsChanneled() && i->second->GetCasterGUID() != GetGUID() && !ObjectAccessor::GetWorldObject(*this, i->second->GetCasterGUID()))
|
||||
RemoveOwnedAura(i, AURA_REMOVE_BY_CANCEL); // remove channeled auras when caster is not on the same map
|
||||
else
|
||||
++i;
|
||||
}
|
||||
@@ -6498,11 +6500,12 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 /*SwingType
|
||||
}
|
||||
|
||||
//victim may be nullptr
|
||||
bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, Spell const* spellProc /*= nullptr*/)
|
||||
bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, ProcEventInfo const& eventInfo)
|
||||
{
|
||||
SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo();
|
||||
uint32 effIndex = triggeredByAura->GetEffIndex();
|
||||
int32 triggerAmount = triggeredByAura->GetAmount();
|
||||
Spell const* spellProc = eventInfo.GetProcSpell();
|
||||
|
||||
Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER
|
||||
? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;
|
||||
@@ -7864,12 +7867,22 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
|
||||
if (!procSpell || !procSpell->IsPositive())
|
||||
return false;
|
||||
|
||||
// heal amount
|
||||
basepoints0 = int32(CalculatePct(std::min(damage, GetMaxHealth() - GetHealth()), triggerAmount));
|
||||
target = this;
|
||||
HealInfo const* healInfo = eventInfo.GetHealInfo();
|
||||
if (!healInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (basepoints0)
|
||||
triggered_spell_id = 31786;
|
||||
uint32 effectiveHeal = healInfo->GetEffectiveHeal();
|
||||
if (effectiveHeal)
|
||||
{
|
||||
// heal amount
|
||||
basepoints0 = int32(CalculatePct(effectiveHeal, triggerAmount));
|
||||
target = this;
|
||||
|
||||
if (basepoints0)
|
||||
triggered_spell_id = 31786;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Paladin Tier 6 Trinket (Ashtongue Talisman of Zeal)
|
||||
@@ -11202,16 +11215,18 @@ void Unit::UnsummonAllTotems(bool onDeath /*= false*/)
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::SendHealSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorb, bool critical)
|
||||
void Unit::SendHealSpellLog(HealInfo const& healInfo, bool critical)
|
||||
{
|
||||
uint32 overheal = healInfo.GetHeal() - healInfo.GetEffectiveHeal();
|
||||
|
||||
// we guess size
|
||||
WorldPacket data(SMSG_SPELLHEALLOG, (8 + 8 + 4 + 4 + 4 + 4 + 1 + 1));
|
||||
data << victim->GetPackGUID();
|
||||
data << healInfo.GetTarget()->GetPackGUID();
|
||||
data << GetPackGUID();
|
||||
data << uint32(SpellID);
|
||||
data << uint32(Damage);
|
||||
data << uint32(OverHeal);
|
||||
data << uint32(Absorb); // Absorb amount
|
||||
data << uint32(healInfo.GetSpellInfo()->Id);
|
||||
data << uint32(healInfo.GetHeal());
|
||||
data << uint32(overheal);
|
||||
data << uint32(healInfo.GetAbsorb()); // Absorb amount
|
||||
data << uint8(critical ? 1 : 0);
|
||||
data << uint8(0); // unused
|
||||
SendMessageToSet(&data, true);
|
||||
@@ -11227,7 +11242,9 @@ int32 Unit::HealBySpell(HealInfo& healInfo, bool critical)
|
||||
CalcHealAbsorb(healInfo);
|
||||
|
||||
int32 gain = Unit::DealHeal(healInfo.GetHealer(), healInfo.GetTarget(), healInfo.GetHeal());
|
||||
SendHealSpellLog(healInfo.GetTarget(), healInfo.GetSpellInfo()->Id, healInfo.GetHeal(), uint32(healInfo.GetHeal() - gain), healInfo.GetAbsorb(), critical);
|
||||
healInfo.SetEffectiveHeal(gain);
|
||||
|
||||
SendHealSpellLog(healInfo, critical);
|
||||
return gain;
|
||||
}
|
||||
|
||||
@@ -16555,7 +16572,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
|
||||
case SPELL_AURA_DUMMY:
|
||||
{
|
||||
LOG_DEBUG("spells.aura", "ProcDamageAndSpell: casting spell id {} (triggered by {} dummy aura of spell {})", spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId());
|
||||
if (HandleDummyAuraProc(target, damage, triggeredByAura, procSpellInfo, procFlag, procExtra, cooldown, procSpell))
|
||||
if (HandleDummyAuraProc(target, damage, triggeredByAura, procSpellInfo, procFlag, procExtra, cooldown, eventInfo))
|
||||
takeCharges = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -806,6 +806,7 @@ private:
|
||||
Unit* const m_healer;
|
||||
Unit* const m_target;
|
||||
uint32 m_heal;
|
||||
uint32 m_effectiveHeal;
|
||||
uint32 m_absorb;
|
||||
SpellInfo const* const m_spellInfo;
|
||||
SpellSchoolMask const m_schoolMask;
|
||||
@@ -814,12 +815,17 @@ public:
|
||||
: m_healer(_healer), m_target(_target), m_heal(_heal), m_spellInfo(_spellInfo), m_schoolMask(_schoolMask)
|
||||
{
|
||||
m_absorb = 0;
|
||||
m_effectiveHeal = 0;
|
||||
}
|
||||
|
||||
void AbsorbHeal(uint32 amount)
|
||||
{
|
||||
amount = std::min(amount, GetHeal());
|
||||
m_absorb += amount;
|
||||
m_heal -= amount;
|
||||
|
||||
amount = std::min(amount, GetEffectiveHeal());
|
||||
m_effectiveHeal -= amount;
|
||||
}
|
||||
|
||||
void SetHeal(uint32 amount)
|
||||
@@ -827,9 +833,15 @@ public:
|
||||
m_heal = amount;
|
||||
}
|
||||
|
||||
void SetEffectiveHeal(uint32 amount)
|
||||
{
|
||||
m_effectiveHeal = amount;
|
||||
}
|
||||
|
||||
[[nodiscard]] Unit* GetHealer() const { return m_healer; }
|
||||
[[nodiscard]] Unit* GetTarget() const { return m_target; }
|
||||
[[nodiscard]] uint32 GetHeal() const { return m_heal; }
|
||||
[[nodiscard]] uint32 GetEffectiveHeal() const { return m_effectiveHeal; }
|
||||
[[nodiscard]] uint32 GetAbsorb() const { return m_absorb; }
|
||||
[[nodiscard]] SpellInfo const* GetSpellInfo() const { return m_spellInfo; };
|
||||
[[nodiscard]] SpellSchoolMask GetSchoolMask() const { return m_schoolMask; };
|
||||
@@ -1724,7 +1736,7 @@ public:
|
||||
[[nodiscard]] virtual bool IsUnderWater() const;
|
||||
bool isInAccessiblePlaceFor(Creature const* c) const;
|
||||
|
||||
void SendHealSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorb, bool critical = false);
|
||||
void SendHealSpellLog(HealInfo const& healInfo, bool critical = false);
|
||||
int32 HealBySpell(HealInfo& healInfo, bool critical = false);
|
||||
void SendEnergizeSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, Powers powertype);
|
||||
void EnergizeBySpell(Unit* victim, uint32 SpellID, uint32 Damage, Powers powertype);
|
||||
@@ -2551,7 +2563,7 @@ protected:
|
||||
|
||||
private:
|
||||
bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent, ProcEventInfo const& eventInfo);
|
||||
bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, Spell const* spellProc = nullptr);
|
||||
bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, ProcEventInfo const& eventInfo);
|
||||
bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, bool* handled);
|
||||
bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, uint32 procPhase, ProcEventInfo& eventInfo);
|
||||
bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown);
|
||||
|
||||
@@ -39,7 +39,7 @@ class ObjectGuid;
|
||||
#define CENTER_GRID_OFFSET (SIZE_OF_GRIDS/2)
|
||||
|
||||
#define MIN_GRID_DELAY (MINUTE*IN_MILLISECONDS)
|
||||
#define MIN_MAP_UPDATE_DELAY 10
|
||||
#define MIN_MAP_UPDATE_DELAY 1
|
||||
|
||||
#define SIZE_OF_GRID_CELL (SIZE_OF_GRIDS/MAX_NUMBER_OF_CELLS)
|
||||
|
||||
|
||||
@@ -1201,6 +1201,7 @@ void Guild::HandleRoster(WorldSession* session)
|
||||
{
|
||||
WorldPackets::Guild::GuildRoster roster;
|
||||
|
||||
roster.RankData.reserve(m_ranks.size());
|
||||
for (RankInfo const& rank : m_ranks)
|
||||
{
|
||||
WorldPackets::Guild::GuildRankData& rankData = roster.RankData.emplace_back();
|
||||
@@ -1215,6 +1216,7 @@ void Guild::HandleRoster(WorldSession* session)
|
||||
}
|
||||
|
||||
bool sendOfficerNote = HasRankRight(session->GetPlayer(), GR_RIGHT_VIEWOFFNOTE);
|
||||
roster.MemberData.reserve(m_members.size());
|
||||
for (auto const& [guid, member] : m_members)
|
||||
{
|
||||
WorldPackets::Guild::GuildRosterMemberData& memberData = roster.MemberData.emplace_back();
|
||||
|
||||
@@ -784,14 +784,15 @@ void WorldSession::HandleRaidReadyCheckOpcode(WorldPacket& recvData)
|
||||
|
||||
void WorldSession::HandleRaidReadyCheckFinishedOpcode(WorldPacket& /*recvData*/)
|
||||
{
|
||||
//Group* group = GetPlayer()->GetGroup();
|
||||
//if (!group)
|
||||
// return;
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
//if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
|
||||
// return;
|
||||
if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
|
||||
return;
|
||||
|
||||
// Is any reaction need?
|
||||
WorldPacket data(MSG_RAID_READY_CHECK_FINISHED);
|
||||
group->BroadcastPacket(&data, true, -1);
|
||||
}
|
||||
|
||||
void WorldSession::BuildPartyMemberStatsChangedPacket(Player* player, WorldPacket* data)
|
||||
|
||||
@@ -2525,6 +2525,7 @@ void Map::SendInitSelf(Player* player)
|
||||
{
|
||||
LOG_DEBUG("maps", "Creating player data for himself {}", player->GetGUID().ToString());
|
||||
|
||||
WorldPacket packet;
|
||||
UpdateData data;
|
||||
|
||||
// attach to player data current transport data
|
||||
@@ -2534,15 +2535,25 @@ void Map::SendInitSelf(Player* player)
|
||||
// build data for self presence in world at own client (one time for map)
|
||||
player->BuildCreateUpdateBlockForPlayer(&data, player);
|
||||
|
||||
// build and send self update packet before sending to player his own auras
|
||||
data.BuildPacket(&packet);
|
||||
player->SendDirectMessage(&packet);
|
||||
|
||||
// send to player his own auras (this is needed here for timely initialization of some fields on client)
|
||||
player->GetAurasForTarget(player, true);
|
||||
|
||||
// clean buffers for further work
|
||||
packet.clear();
|
||||
data.Clear();
|
||||
|
||||
// build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map
|
||||
if (Transport* transport = player->GetTransport())
|
||||
for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr)
|
||||
if (player != (*itr) && player->HaveAtClient(*itr))
|
||||
(*itr)->BuildCreateUpdateBlockForPlayer(&data, player);
|
||||
|
||||
WorldPacket packet;
|
||||
data.BuildPacket(&packet);
|
||||
player->GetSession()->SendPacket(&packet);
|
||||
player->SendDirectMessage(&packet);
|
||||
}
|
||||
|
||||
void Map::SendInitTransports(Player* player)
|
||||
|
||||
@@ -6626,8 +6626,9 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
|
||||
HealInfo healInfo(caster, target, heal, GetSpellInfo(), GetSpellInfo()->GetSchoolMask());
|
||||
Unit::CalcHealAbsorb(healInfo);
|
||||
int32 gain = Unit::DealHeal(caster, target, healInfo.GetHeal());
|
||||
healInfo.SetEffectiveHeal(gain);
|
||||
|
||||
SpellPeriodicAuraLogInfo pInfo(this, healInfo.GetHeal(), healInfo.GetHeal() - gain, healInfo.GetAbsorb(), 0, 0.0f, crit);
|
||||
SpellPeriodicAuraLogInfo pInfo(this, healInfo.GetHeal(), healInfo.GetHeal() - healInfo.GetEffectiveHeal(), healInfo.GetAbsorb(), 0, 0.0f, crit);
|
||||
target->SendPeriodicAuraLog(&pInfo);
|
||||
|
||||
if (caster)
|
||||
|
||||
@@ -4374,7 +4374,8 @@ void SpellMgr::LoadSpellInfoCorrections()
|
||||
});
|
||||
|
||||
// Judgement (Paladin T2 8P Bonus)
|
||||
ApplySpellFix({ 23591 }, [](SpellInfo* spellInfo)
|
||||
// Battlegear of Eternal Justice
|
||||
ApplySpellFix({ 23591, 26135 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->ProcFlags = PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS;
|
||||
});
|
||||
|
||||
@@ -2211,8 +2211,8 @@ void SpellMgr::LoadSpellEnchantProcData()
|
||||
|
||||
mSpellEnchantProcEventMap.clear(); // need for reload case
|
||||
|
||||
// 0 1 2 3
|
||||
QueryResult result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data");
|
||||
// 0 1 2 3 4
|
||||
QueryResult result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx, attributeMask FROM spell_enchant_proc_data");
|
||||
if (!result)
|
||||
{
|
||||
LOG_WARN("server.loading", ">> Loaded 0 spell enchant proc event conditions. DB table `spell_enchant_proc_data` is empty.");
|
||||
@@ -2239,6 +2239,7 @@ void SpellMgr::LoadSpellEnchantProcData()
|
||||
spe.customChance = fields[1].Get<uint32>();
|
||||
spe.PPMChance = fields[2].Get<float>();
|
||||
spe.procEx = fields[3].Get<uint32>();
|
||||
spe.attributeMask = fields[4].Get<uint32>();
|
||||
|
||||
mSpellEnchantProcEventMap[enchantId] = spe;
|
||||
|
||||
|
||||
@@ -304,11 +304,17 @@ struct SpellProcEntry
|
||||
|
||||
typedef std::unordered_map<uint32, SpellProcEntry> SpellProcMap;
|
||||
|
||||
enum EnchantProcAttributes
|
||||
{
|
||||
ENCHANT_PROC_ATTR_EXCLUSIVE = 0x1 // Only one instance of that effect can be active
|
||||
};
|
||||
|
||||
struct SpellEnchantProcEntry
|
||||
{
|
||||
uint32 customChance;
|
||||
float PPMChance;
|
||||
uint32 procEx;
|
||||
uint32 attributeMask;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<uint32, SpellEnchantProcEntry> SpellEnchantProcEventMap;
|
||||
|
||||
@@ -667,7 +667,7 @@ void World::LoadConfigSettings(bool reload)
|
||||
_int_configs[CONFIG_MIN_LEVEL_STAT_SAVE] = 0;
|
||||
}
|
||||
|
||||
_int_configs[CONFIG_INTERVAL_MAPUPDATE] = sConfigMgr->GetOption<int32>("MapUpdateInterval", 100);
|
||||
_int_configs[CONFIG_INTERVAL_MAPUPDATE] = sConfigMgr->GetOption<int32>("MapUpdateInterval", 10);
|
||||
if (_int_configs[CONFIG_INTERVAL_MAPUPDATE] < MIN_MAP_UPDATE_DELAY)
|
||||
{
|
||||
LOG_ERROR("server.loading", "MapUpdateInterval ({}) must be greater {}. Use this minimal value.", _int_configs[CONFIG_INTERVAL_MAPUPDATE], MIN_MAP_UPDATE_DELAY);
|
||||
|
||||
@@ -141,8 +141,6 @@ enum WorldStates
|
||||
WS_DAILY_CALENDAR_DELETION_OLD_EVENTS_TIME = 20008 // Next daily calendar deletions of old events time
|
||||
};
|
||||
|
||||
#define WORLD_SLEEP_CONST 10
|
||||
|
||||
// xinef: petitions storage
|
||||
struct PetitionData
|
||||
{
|
||||
|
||||
+5
-6
@@ -168,20 +168,19 @@ public:
|
||||
int32 multiplier = 1;
|
||||
switch (aurEff->GetTickNumber())
|
||||
{
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
multiplier = 2;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
multiplier = 3;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
multiplier = 4;
|
||||
break;
|
||||
case 7:
|
||||
multiplier = 6;
|
||||
break;
|
||||
case 8:
|
||||
multiplier = 5;
|
||||
multiplier = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,12 @@ public:
|
||||
|
||||
// Not in progress
|
||||
instance->SetData(DATA_ARAN, NOT_STARTED);
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR), true);
|
||||
|
||||
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
||||
{
|
||||
libraryDoor->SetGoState(GO_STATE_ACTIVE);
|
||||
libraryDoor->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
@@ -147,7 +152,12 @@ public:
|
||||
Talk(SAY_DEATH);
|
||||
|
||||
instance->SetData(DATA_ARAN, DONE);
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR), true);
|
||||
|
||||
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
||||
{
|
||||
libraryDoor->SetGoState(GO_STATE_ACTIVE);
|
||||
libraryDoor->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
@@ -155,7 +165,13 @@ public:
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
instance->SetData(DATA_ARAN, IN_PROGRESS);
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR), false);
|
||||
|
||||
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
||||
{
|
||||
libraryDoor->SetGoState(GO_STATE_READY);
|
||||
libraryDoor->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
|
||||
DoZoneInCombat();
|
||||
}
|
||||
|
||||
@@ -203,7 +219,11 @@ public:
|
||||
{
|
||||
if (CloseDoorTimer <= diff)
|
||||
{
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR), false);
|
||||
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
||||
{
|
||||
libraryDoor->SetGoState(GO_STATE_READY);
|
||||
libraryDoor->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
CloseDoorTimer = 0;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -163,7 +163,6 @@ Position const RimefangFlyPos = {4413.309f, 2456.421f, 233.3795f, 2.890186f
|
||||
Position const RimefangLandPos = {4413.309f, 2456.421f, 203.3848f, 2.890186f};
|
||||
Position const SpinestalkerFlyPos = {4418.895f, 2514.233f, 230.4864f, 3.396045f};
|
||||
Position const SpinestalkerLandPos = {4418.895f, 2514.233f, 203.3848f, 3.396045f};
|
||||
Position const SindragosaSpawnPos = {4818.700f, 2483.710f, 287.0650f, 3.089233f};
|
||||
Position const SindragosaFlyInPos = {4420.190f, 2484.360f, 232.5150f, 3.141593f};
|
||||
Position const SindragosaLandPos = {4419.190f, 2484.570f, 203.3848f, 3.141593f};
|
||||
Position const SindragosaAirPos = {4475.990f, 2484.430f, 247.9340f, 3.141593f};
|
||||
@@ -273,7 +272,7 @@ public:
|
||||
|
||||
struct boss_sindragosaAI : public BossAI
|
||||
{
|
||||
boss_sindragosaAI(Creature* creature) : BossAI(creature, DATA_SINDRAGOSA), _summoned(false)
|
||||
boss_sindragosaAI(Creature* creature) : BossAI(creature, DATA_SINDRAGOSA)
|
||||
{
|
||||
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_HASTE_SPELLS, true);
|
||||
}
|
||||
@@ -286,16 +285,9 @@ public:
|
||||
_bombCount = 0;
|
||||
_mysticBuffetStack = 0;
|
||||
_Reset();
|
||||
me->DisableRotate(false);
|
||||
me->SetControlled(false, UNIT_STATE_ROOT);
|
||||
me->SetSpeed(MOVE_RUN, me->GetCreatureTemplate()->speed_run);
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
me->CastSpell(me, SPELL_TANK_MARKER, true);
|
||||
|
||||
if (!_summoned)
|
||||
{
|
||||
me->SetDisableGravity(true);
|
||||
}
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
@@ -359,20 +351,9 @@ public:
|
||||
return me->IsVisible() && target->GetEntry() != NPC_CROK_SCOURGEBANE;
|
||||
}
|
||||
|
||||
void JustReachedHome() override
|
||||
{
|
||||
_JustReachedHome();
|
||||
instance->SetBossState(DATA_SINDRAGOSA, FAIL);
|
||||
if (_summoned)
|
||||
{
|
||||
me->SetDisableGravity(false);
|
||||
}
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason why) override
|
||||
{
|
||||
me->DisableRotate(false);
|
||||
me->SetControlled(false, UNIT_STATE_ROOT);
|
||||
instance->SetBossState(DATA_SINDRAGOSA, FAIL);
|
||||
BossAI::EnterEvadeMode(why);
|
||||
}
|
||||
|
||||
@@ -386,10 +367,6 @@ public:
|
||||
{
|
||||
if (action == ACTION_START_FROSTWYRM)
|
||||
{
|
||||
if (_summoned)
|
||||
return;
|
||||
|
||||
_summoned = true;
|
||||
if (TempSummon* summon = me->ToTempSummon())
|
||||
summon->SetTempSummonType(TEMPSUMMON_DEAD_DESPAWN);
|
||||
|
||||
@@ -403,7 +380,11 @@ public:
|
||||
float moveTime = me->GetExactDist(&SindragosaFlyInPos) / (me->GetSpeed(MOVE_RUN) * 0.001f);
|
||||
me->m_Events.AddEvent(new FrostwyrmLandEvent(*me, SindragosaLandPos), me->m_Events.CalculateTime(uint64(moveTime) + 250));
|
||||
me->GetMotionMaster()->MovePoint(POINT_FROSTWYRM_FLY_IN, SindragosaFlyInPos);
|
||||
me->CastSpell(me, SPELL_SINDRAGOSA_S_FURY, true);
|
||||
|
||||
if (!instance->GetData(DATA_SINDRAGOSA_INTRO))
|
||||
{
|
||||
DoCastAOE(SPELL_SINDRAGOSA_S_FURY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,7 +666,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool _summoned;
|
||||
uint8 _bombCount;
|
||||
uint8 _mysticBuffetStack;
|
||||
bool _didFirstFlyPhase;
|
||||
@@ -1724,13 +1704,10 @@ public:
|
||||
if (Creature* rimefang = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_RIMEFANG)))
|
||||
rimefang->AI()->DoAction(ACTION_START_FROSTWYRM);
|
||||
|
||||
if (!instance->GetData(DATA_SINDRAGOSA_FROSTWYRMS) && !instance->GetGuidData(DATA_SINDRAGOSA) && instance->GetBossState(DATA_SINDRAGOSA) != DONE)
|
||||
if (!instance->GetData(DATA_SINDRAGOSA_FROSTWYRMS))
|
||||
{
|
||||
if (instance->GetData(DATA_HAS_LIMITED_ATTEMPTS) && !instance->GetData(DATA_HEROIC_ATTEMPTS))
|
||||
return true;
|
||||
|
||||
player->GetMap()->LoadGrid(SindragosaSpawnPos.GetPositionX(), SindragosaSpawnPos.GetPositionY());
|
||||
if (Creature* sindragosa = player->GetMap()->SummonCreature(NPC_SINDRAGOSA, SindragosaSpawnPos))
|
||||
if (Creature* sindragosa = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_SINDRAGOSA)))
|
||||
sindragosa->AI()->DoAction(ACTION_START_FROSTWYRM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ enum DataTypes
|
||||
DATA_PUTRICIDE_TRAP_STATE = 254,
|
||||
DATA_HAS_LIMITED_ATTEMPTS = 255,
|
||||
DATA_LK_HC_AVAILABLE = 256,
|
||||
DATA_SINDRAGOSA_INTRO = 257,
|
||||
|
||||
DATA_BPC_TRASH_DIED = 300,
|
||||
};
|
||||
|
||||
@@ -49,7 +49,8 @@ enum TimedEvents
|
||||
EVENT_UPDATE_EXECUTION_TIME = 1,
|
||||
EVENT_QUAKE_SHATTER = 2,
|
||||
EVENT_REBUILD_PLATFORM = 3,
|
||||
EVENT_RESPAWN_GUNSHIP = 4
|
||||
EVENT_RESPAWN_GUNSHIP = 4,
|
||||
EVENT_RESPAWN_SINDRAGOSA = 5
|
||||
};
|
||||
|
||||
enum Spells
|
||||
@@ -140,6 +141,7 @@ Position const JainaSpawnPos = { -48.65278f, 2211.026f, 27.98586f, 3.124139f
|
||||
Position const MuradinSpawnPos = { -47.34549f, 2208.087f, 27.98586f, 3.106686f };
|
||||
Position const UtherSpawnPos = { -26.58507f, 2211.524f, 30.19898f, 3.124139f };
|
||||
Position const SylvanasSpawnPos = { -41.45833f, 2222.891f, 27.98586f, 3.647738f };
|
||||
Position const SindragosaSpawnPos = { 4818.6997f, 2483.7102f, 287.06497f, 3.286661f };
|
||||
|
||||
// Set position traps Spirit Alarm
|
||||
std::vector<Position> GoSpiritAlarm_1 = { { -160.96f, 2210.46f, 35.24f, 0.0f }, { -176.27f, 2201.93f, 35.24f, 0.0f}, { -207.83f, 2207.38f, 35.24f, 0.0f } };
|
||||
@@ -212,6 +214,7 @@ public:
|
||||
BloodQuickeningState = NOT_STARTED;
|
||||
BloodQuickeningMinutes = 0;
|
||||
BloodPrinceTrashCount = 0;
|
||||
IsSindragosaIntroDone = false;
|
||||
}
|
||||
|
||||
void FillInitialWorldStates(WorldPacket& data) override
|
||||
@@ -254,6 +257,11 @@ public:
|
||||
if (GetBossState(DATA_LADY_DEATHWHISPER) == DONE && GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != DONE)
|
||||
SpawnGunship();
|
||||
|
||||
if (GetBossState(DATA_SINDRAGOSA) != DONE && IsSindragosaIntroDone && SindragosaGUID.IsEmpty() && Events.GetTimeUntilEvent(EVENT_RESPAWN_SINDRAGOSA) == Milliseconds::max())
|
||||
{
|
||||
Events.ScheduleEvent(EVENT_RESPAWN_SINDRAGOSA, 30s);
|
||||
}
|
||||
|
||||
if (IsBuffAvailable)
|
||||
{
|
||||
SpellAreaForAreaMapBounds saBounds = sSpellMgr->GetSpellAreaForAreaMapBounds(4812);
|
||||
@@ -937,6 +945,8 @@ public:
|
||||
return BloodQuickeningState;
|
||||
case DATA_HEROIC_ATTEMPTS:
|
||||
return HeroicAttempts;
|
||||
case DATA_SINDRAGOSA_INTRO:
|
||||
return (IsSindragosaIntroDone ? 1 : 0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1161,7 +1171,21 @@ public:
|
||||
if (state == DONE)
|
||||
CheckLichKingAvailability();
|
||||
else if (state == FAIL)
|
||||
{
|
||||
IsSindragosaIntroDone = true;
|
||||
HandleDropAttempt();
|
||||
if (instance->IsHeroic())
|
||||
{
|
||||
if (HeroicAttempts)
|
||||
{
|
||||
Events.ScheduleEvent(EVENT_RESPAWN_SINDRAGOSA, 30s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Events.ScheduleEvent(EVENT_RESPAWN_SINDRAGOSA, 30s);
|
||||
}
|
||||
}
|
||||
if (state == DONE && !instance->IsHeroic() && LichKingHeroicAvailable)
|
||||
{
|
||||
LichKingHeroicAvailable = false;
|
||||
@@ -1626,6 +1650,7 @@ public:
|
||||
data >> BloodPrinceTrashCount;
|
||||
data >> IsBuffAvailable;
|
||||
SetData(DATA_BUFF_AVAILABLE, IsBuffAvailable);
|
||||
data >> IsSindragosaIntroDone;
|
||||
}
|
||||
|
||||
void WriteSaveDataMore(std::ostringstream& data) override
|
||||
@@ -1638,7 +1663,8 @@ public:
|
||||
<< PutricideEventProgress << ' '
|
||||
<< uint32(LichKingHeroicAvailable ? 1 : 0) << ' '
|
||||
<< BloodPrinceTrashCount << ' '
|
||||
<< uint32(IsBuffAvailable ? 1 : 0);
|
||||
<< uint32(IsBuffAvailable ? 1 : 0) << ' '
|
||||
<< uint32(IsSindragosaIntroDone ? 1 : 0);
|
||||
}
|
||||
|
||||
void Update(uint32 diff) override
|
||||
@@ -1679,7 +1705,7 @@ public:
|
||||
else
|
||||
DarkwhisperElevatorTimer -= diff;
|
||||
|
||||
if (BloodQuickeningState != IN_PROGRESS && GetBossState(DATA_THE_LICH_KING) != IN_PROGRESS && GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != FAIL)
|
||||
if (Events.Empty())
|
||||
return;
|
||||
|
||||
Events.Update(diff);
|
||||
@@ -1732,6 +1758,21 @@ public:
|
||||
case EVENT_RESPAWN_GUNSHIP:
|
||||
SpawnGunship();
|
||||
break;
|
||||
case EVENT_RESPAWN_SINDRAGOSA:
|
||||
if (Creature* sindragosa = instance->SummonCreature(NPC_SINDRAGOSA, SindragosaSpawnPos))
|
||||
{
|
||||
sindragosa->setActive(true);
|
||||
sindragosa->SetDisableGravity(true);
|
||||
sindragosa->GetMotionMaster()->MovePath(NPC_SINDRAGOSA * 10, true);
|
||||
|
||||
if (TempSummon* summon = sindragosa->ToTempSummon())
|
||||
{
|
||||
summon->SetTempSummonType(TEMPSUMMON_DEAD_DESPAWN);
|
||||
}
|
||||
}
|
||||
// Could happen more than once if more than one player enters before she respawns.
|
||||
Events.CancelEvent(EVENT_RESPAWN_SINDRAGOSA);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1944,6 +1985,7 @@ public:
|
||||
bool IsOozeDanceEligible;
|
||||
bool IsNauseaEligible;
|
||||
bool IsOrbWhispererEligible;
|
||||
bool IsSindragosaIntroDone;
|
||||
};
|
||||
|
||||
InstanceScript* GetInstanceScript(InstanceMap* map) const override
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
enum vYells
|
||||
{
|
||||
CYANIGOSA_SAY_SPAWN = 3,
|
||||
SAY_SINCLARI_1 = 0
|
||||
SAY_SINCLARI_LEAVING = 0,
|
||||
SAY_SINCLARI_DOOR_LOCK = 1,
|
||||
SAY_SINCLARI_COMPLETE = 2,
|
||||
};
|
||||
|
||||
class instance_violet_hold : public InstanceMapScript
|
||||
@@ -214,7 +216,7 @@ public:
|
||||
{
|
||||
EncounterStatus = IN_PROGRESS;
|
||||
if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
|
||||
c->AI()->Talk(SAY_SINCLARI_1);
|
||||
c->AI()->Talk(SAY_SINCLARI_LEAVING);
|
||||
events.RescheduleEvent(EVENT_GUARDS_FALL_BACK, 4s);
|
||||
}
|
||||
break;
|
||||
@@ -251,7 +253,12 @@ public:
|
||||
EncounterStatus = DONE;
|
||||
HandleGameObject(GO_MainGateGUID, true);
|
||||
DoUpdateWorldState(WORLD_STATE_VH_SHOW, 0);
|
||||
if (Creature* c = instance->GetCreature(NPC_SinclariGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); }
|
||||
if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
|
||||
{
|
||||
c->AI()->Talk(SAY_SINCLARI_COMPLETE);
|
||||
c->DespawnOrUnsummon();
|
||||
c->SetRespawnTime(3);
|
||||
}
|
||||
}
|
||||
SaveToDB();
|
||||
if (WaveCount < 18)
|
||||
@@ -440,8 +447,14 @@ public:
|
||||
break;
|
||||
case EVENT_START_ENCOUNTER:
|
||||
{
|
||||
if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
|
||||
{
|
||||
c->AI()->Talk(SAY_SINCLARI_DOOR_LOCK);
|
||||
}
|
||||
if (Creature* c = instance->GetCreature(NPC_DoorSealGUID))
|
||||
{
|
||||
c->RemoveAllAuras(); // just to be sure...
|
||||
}
|
||||
GateHealth = 100;
|
||||
HandleGameObject(GO_MainGateGUID, false);
|
||||
DoUpdateWorldState(WORLD_STATE_VH_SHOW, 1);
|
||||
|
||||
@@ -24,6 +24,21 @@
|
||||
#include "ScriptedGossip.h"
|
||||
#include "SpellScript.h"
|
||||
|
||||
/// @todo: Missing Sinclari Trigger announcements (32204) Look at its creature_text for more info.
|
||||
/// @todo: Activation Crystals (go_vh_activation_crystal) (193611) are spammable, should be a 1 time use per crystal.
|
||||
|
||||
enum Texts
|
||||
{
|
||||
GOSSIP_MENU_START_EVENT = 9998,
|
||||
GOSSIP_MENU_ITEM = 9997,
|
||||
GOSSIP_MENU_LATE_JOIN = 10275,
|
||||
|
||||
NPC_TEXT_SINCLARI_IN = 13853,
|
||||
NPC_TEXT_SINCLARI_ITEM = 13854,
|
||||
NPC_TEXT_SINCLARI_DONE = 13910,
|
||||
NPC_TEXT_SINCLARI_LATE_JOIN = 14271,
|
||||
};
|
||||
|
||||
/***********
|
||||
** DEFENSE SYSTEM CRYSTAL
|
||||
***********/
|
||||
@@ -45,10 +60,6 @@ public:
|
||||
** SINCLARI
|
||||
***********/
|
||||
|
||||
#define GOSSIP_START_EVENT "Get your people to safety, we'll keep the Blue Dragonflight's forces at bay."
|
||||
#define GOSSIP_ITEM_1 "Activate the crystals when we get in trouble, right."
|
||||
#define GOSSIP_I_WANT_IN "Sorry, I'm late! Can I get in to help my friends?"
|
||||
|
||||
class npc_vh_sinclari : public CreatureScript
|
||||
{
|
||||
public:
|
||||
@@ -60,16 +71,16 @@ public:
|
||||
switch (pInstance->GetData(DATA_ENCOUNTER_STATUS))
|
||||
{
|
||||
case NOT_STARTED:
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_START_EVENT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
|
||||
SendGossipMenuFor(player, 13853, creature->GetGUID());
|
||||
AddGossipItemFor(player, GOSSIP_MENU_ITEM, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
|
||||
AddGossipItemFor(player, GOSSIP_MENU_START_EVENT, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
|
||||
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_IN, creature->GetGUID());
|
||||
break;
|
||||
case IN_PROGRESS:
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_I_WANT_IN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
|
||||
SendGossipMenuFor(player, 13853, creature->GetGUID());
|
||||
AddGossipItemFor(player, GOSSIP_MENU_LATE_JOIN, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
|
||||
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_LATE_JOIN, creature->GetGUID());
|
||||
break;
|
||||
default: // DONE or invalid
|
||||
SendGossipMenuFor(player, 13910, creature->GetGUID());
|
||||
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_DONE, creature->GetGUID());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -86,7 +97,7 @@ public:
|
||||
pInstance->SetData(DATA_START_INSTANCE, 1);
|
||||
break;
|
||||
case GOSSIP_ACTION_INFO_DEF+2:
|
||||
SendGossipMenuFor(player, 13854, creature->GetGUID());
|
||||
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_ITEM, creature->GetGUID());
|
||||
break;
|
||||
case GOSSIP_ACTION_INFO_DEF+3:
|
||||
player->NearTeleportTo(playerTeleportPosition.GetPositionX(), playerTeleportPosition.GetPositionY(), playerTeleportPosition.GetPositionZ(), playerTeleportPosition.GetOrientation(), true);
|
||||
|
||||
@@ -43,7 +43,7 @@ enum Spells
|
||||
|
||||
struct boss_talon_king_ikiss : public BossAI
|
||||
{
|
||||
boss_talon_king_ikiss(Creature* creature) : BossAI(creature, DATA_IKISS), _spoken(false), _manaShield(false)
|
||||
boss_talon_king_ikiss(Creature* creature) : BossAI(creature, DATA_IKISS), _spoken(false)
|
||||
{
|
||||
scheduler.SetValidator([this]
|
||||
{
|
||||
@@ -55,8 +55,44 @@ struct boss_talon_king_ikiss : public BossAI
|
||||
{
|
||||
_Reset();
|
||||
_spoken = false;
|
||||
_manaShield = false;
|
||||
_comboHealthStages.fill(false);
|
||||
|
||||
ScheduleHealthCheckEvent(80, [&] {
|
||||
TeleportAndCastExplosion();
|
||||
});
|
||||
|
||||
ScheduleHealthCheckEvent(50, [&] {
|
||||
TeleportAndCastExplosion();
|
||||
});
|
||||
|
||||
ScheduleHealthCheckEvent(25, [&] {
|
||||
TeleportAndCastExplosion();
|
||||
});
|
||||
|
||||
ScheduleHealthCheckEvent(20, [&] {
|
||||
DoCast(me, SPELL_MANA_SHIELD);
|
||||
});
|
||||
}
|
||||
|
||||
/// @todo: remove this once pets stop going through doors.
|
||||
bool CanAIAttack(Unit const* /*victim*/) const override
|
||||
{
|
||||
return _spoken;
|
||||
}
|
||||
|
||||
void TeleportAndCastExplosion()
|
||||
{
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
DoCastSelf(SPELL_ARCANE_BUBBLE, true);
|
||||
DoCastAOE(SPELL_BLINK);
|
||||
Talk(EMOTE_ARCANE_EXP);
|
||||
|
||||
scheduler.Schedule(1s, [this](TaskContext)
|
||||
{
|
||||
DoCastAOE(SPELL_ARCANE_EXPLOSION);
|
||||
}).Schedule(6500ms, [this](TaskContext /*context*/)
|
||||
{
|
||||
me->GetThreatMgr().ResetAllThreat();
|
||||
});
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
@@ -106,69 +142,6 @@ struct boss_talon_king_ikiss : public BossAI
|
||||
}
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override
|
||||
{
|
||||
if (!_comboHealthStages[0] && me->HealthBelowPctDamaged(80, damage))
|
||||
{
|
||||
_comboHealthStages[0] = true;
|
||||
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
DoCastSelf(SPELL_ARCANE_BUBBLE, true);
|
||||
DoCastAOE(SPELL_BLINK);
|
||||
Talk(EMOTE_ARCANE_EXP);
|
||||
|
||||
scheduler.Schedule(1s, [this](TaskContext)
|
||||
{
|
||||
DoCastAOE(SPELL_ARCANE_EXPLOSION);
|
||||
}).Schedule(6500ms, [this](TaskContext /*context*/)
|
||||
{
|
||||
me->GetThreatMgr().ResetAllThreat();
|
||||
});
|
||||
}
|
||||
|
||||
if (!_comboHealthStages[1] && me->HealthBelowPctDamaged(50, damage))
|
||||
{
|
||||
_comboHealthStages[1] = true;
|
||||
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
DoCastSelf(SPELL_ARCANE_BUBBLE, true);
|
||||
DoCastAOE(SPELL_BLINK);
|
||||
Talk(EMOTE_ARCANE_EXP);
|
||||
|
||||
scheduler.Schedule(1s, [this](TaskContext)
|
||||
{
|
||||
DoCastAOE(SPELL_ARCANE_EXPLOSION);
|
||||
}).Schedule(6500ms, [this](TaskContext /*context*/)
|
||||
{
|
||||
me->GetThreatMgr().ResetAllThreat();
|
||||
});
|
||||
}
|
||||
|
||||
if (!_comboHealthStages[2] && me->HealthBelowPctDamaged(25, damage))
|
||||
{
|
||||
_comboHealthStages[2] = true;
|
||||
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
DoCastSelf(SPELL_ARCANE_BUBBLE, true);
|
||||
DoCastAOE(SPELL_BLINK);
|
||||
Talk(EMOTE_ARCANE_EXP);
|
||||
|
||||
scheduler.Schedule(1s, [this](TaskContext)
|
||||
{
|
||||
DoCastAOE(SPELL_ARCANE_EXPLOSION);
|
||||
}).Schedule(6500ms, [this](TaskContext /*context*/)
|
||||
{
|
||||
me->GetThreatMgr().ResetAllThreat();
|
||||
});
|
||||
}
|
||||
|
||||
if (!_manaShield && me->HealthBelowPctDamaged(20, damage))
|
||||
{
|
||||
DoCast(me, SPELL_MANA_SHIELD);
|
||||
_manaShield = true;
|
||||
}
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
{
|
||||
if (urand(0, 1))
|
||||
@@ -177,8 +150,6 @@ struct boss_talon_king_ikiss : public BossAI
|
||||
|
||||
private:
|
||||
bool _spoken;
|
||||
bool _manaShield;
|
||||
std::array<bool, 3> _comboHealthStages;
|
||||
};
|
||||
|
||||
// 38194 - Blink
|
||||
|
||||
@@ -37,6 +37,8 @@ enum Spells
|
||||
SPELL_ARCANE_TORRENT = 36022,
|
||||
SPELL_MANA_TAP = 36021,
|
||||
SPELL_DOMINATION = 35280,
|
||||
SPELL_FRENZY = 36992,
|
||||
SPELL_SUICIDE = 35301,
|
||||
SPELL_ETHEREAL_TELEPORT = 34427,
|
||||
SPELL_GREATER_INVISIBILITY = 34426,
|
||||
SPELL_SUMMON_NETHER_WRAITH_1 = 35285,
|
||||
@@ -48,6 +50,8 @@ enum Spells
|
||||
enum Misc
|
||||
{
|
||||
ACTION_BRIDGE_MOB_DEATH = 1, // Used by SAI
|
||||
EQUIPMENT_NORMAL = 1,
|
||||
EQUIPMENT_FRENZY = 2,
|
||||
};
|
||||
|
||||
struct boss_pathaleon_the_calculator : public BossAI
|
||||
@@ -60,9 +64,13 @@ struct boss_pathaleon_the_calculator : public BossAI
|
||||
});
|
||||
}
|
||||
|
||||
bool _isEnraged;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
_isEnraged = false;
|
||||
me->LoadEquipment(EQUIPMENT_NORMAL);
|
||||
|
||||
if (instance->GetPersistentData(DATA_BRIDGE_MOB_DEATH_COUNT) < 4)
|
||||
{
|
||||
@@ -81,18 +89,23 @@ struct boss_pathaleon_the_calculator : public BossAI
|
||||
|
||||
ScheduleHealthCheckEvent(20, [&]()
|
||||
{
|
||||
summons.DespawnAll();
|
||||
DoCastSelf(SPELL_DISGRUNTLED_ANGER, true);
|
||||
DoCastSelf(SPELL_SUICIDE, true);
|
||||
DoCastSelf(SPELL_FRENZY, true);
|
||||
Talk(SAY_ENRAGE);
|
||||
_isEnraged = true;
|
||||
me->LoadEquipment(EQUIPMENT_FRENZY);
|
||||
});
|
||||
|
||||
scheduler.Schedule(30s, [this](TaskContext context)
|
||||
scheduler.Schedule(20s, 25s, [this](TaskContext context)
|
||||
{
|
||||
for (uint8 i = 0; i < DUNGEON_MODE(3, 4); ++i)
|
||||
me->CastSpell(me, SPELL_SUMMON_NETHER_WRAITH_1 + i, true);
|
||||
if (!_isEnraged)
|
||||
{
|
||||
for (uint8 i = 0; i < DUNGEON_MODE(3, 4); ++i)
|
||||
me->CastSpell(me, SPELL_SUMMON_NETHER_WRAITH_1 + i, true);
|
||||
|
||||
Talk(SAY_SUMMON);
|
||||
context.Repeat(30s, 40s);
|
||||
Talk(SAY_SUMMON);
|
||||
}
|
||||
context.Repeat(45s, 50s);
|
||||
}).Schedule(12s, [this](TaskContext context)
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, PowerUsersSelector(me, POWER_MANA, 40.0f, false)))
|
||||
@@ -106,13 +119,17 @@ struct boss_pathaleon_the_calculator : public BossAI
|
||||
me->ModifyPower(POWER_MANA, 5000);
|
||||
DoCastSelf(SPELL_ARCANE_TORRENT);
|
||||
context.Repeat(15s);
|
||||
}).Schedule(25s, [this](TaskContext context)
|
||||
}).Schedule(10s, 15s, [this](TaskContext context)
|
||||
{
|
||||
if (DoCastRandomTarget(SPELL_DOMINATION, 1, 50.0f) == SPELL_CAST_OK)
|
||||
{
|
||||
Talk(SAY_DOMINATION);
|
||||
}
|
||||
context.Repeat(30s);
|
||||
context.Repeat(27s, 40s);
|
||||
}).Schedule(25s, [this](TaskContext context)
|
||||
{
|
||||
DoCast(SPELL_DISGRUNTLED_ANGER);
|
||||
context.Repeat(40s, 90s);
|
||||
});
|
||||
|
||||
if (IsHeroic())
|
||||
|
||||
@@ -349,7 +349,10 @@ public:
|
||||
instance->SetBossState(DATA_WARDEN_MELLICHAR, DONE);
|
||||
if (Creature* creature = summons.GetCreatureWithEntry(NPC_MILLHOUSE))
|
||||
{
|
||||
instance->DoCastSpellOnPlayers(SPELL_QID10886);
|
||||
if (IsHeroic())
|
||||
{
|
||||
instance->DoCastSpellOnPlayers(SPELL_QID10886);
|
||||
}
|
||||
creature->AI()->Talk(SAY_COMPLETE);
|
||||
creature->ReplaceAllNpcFlags(UNIT_NPC_FLAG_GOSSIP);
|
||||
}
|
||||
|
||||
@@ -91,15 +91,15 @@ struct boss_dalliah_the_doomsayer : public BossAI
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.Schedule(1s, 4s, [this](TaskContext context)
|
||||
scheduler.Schedule(8s, 12s, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_GIFT_OF_THE_DOOMSAYER);
|
||||
context.Repeat(16s, 21s);
|
||||
}).Schedule(7s, 9s, [this](TaskContext context)
|
||||
context.Repeat(17s, 35s);
|
||||
}).Schedule(20s, 30s, [this](TaskContext context)
|
||||
{
|
||||
Talk(SAY_WHIRLWIND);
|
||||
DoCastAOE(SPELL_WHIRLWIND);
|
||||
context.Repeat(19s, 21s);
|
||||
context.Repeat();
|
||||
|
||||
scheduler.Schedule(7s, [this](TaskContext)
|
||||
{
|
||||
@@ -110,10 +110,10 @@ struct boss_dalliah_the_doomsayer : public BossAI
|
||||
|
||||
if (IsHeroic())
|
||||
{
|
||||
scheduler.Schedule(11s, 16s, [this](TaskContext context)
|
||||
scheduler.Schedule(11s, 30s, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_SHADOW_WAVE);
|
||||
context.Repeat(11s, 16s);
|
||||
context.Repeat();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,9 +128,10 @@ struct boss_wrath_scryer_soccothrates : public BossAI
|
||||
|
||||
scheduler.Schedule(30s, 35s, [this](TaskContext context)
|
||||
{
|
||||
scheduler.DelayAll(5s);
|
||||
me->CastSpell(me, SPELL_KNOCK_AWAY, false);
|
||||
me->HandleEmoteCommand(EMOTE_ONESHOT_POINT);
|
||||
Talk(SAY_KNOCK_AWAY);
|
||||
me->HandleEmoteCommand(EMOTE_ONESHOT_POINT);
|
||||
|
||||
scheduler.Schedule(4600ms, [this](TaskContext)
|
||||
{
|
||||
@@ -149,7 +150,7 @@ struct boss_wrath_scryer_soccothrates : public BossAI
|
||||
});
|
||||
|
||||
context.Repeat(20s, 35s);
|
||||
}).Schedule(12s, 14s, [this](TaskContext context)
|
||||
}).Schedule(8500ms, 22s, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_FELFIRE_SHOCK);
|
||||
context.Repeat();
|
||||
|
||||
@@ -56,22 +56,22 @@ struct boss_zereketh_the_unbound : public BossAI
|
||||
_JustEngagedWith();
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
scheduler.Schedule(6s, [this](TaskContext context)
|
||||
scheduler.Schedule(11s, 29s, [this](TaskContext context)
|
||||
{
|
||||
DoCastRandomTarget(SPELL_VOID_ZONE, 0, 60.0f);
|
||||
context.Repeat(15s);
|
||||
}).Schedule(10s, [this](TaskContext context)
|
||||
context.Repeat();
|
||||
}).Schedule(12s, 22s, [this](TaskContext context)
|
||||
{
|
||||
DoCastAOE(SPELL_SHADOW_NOVA);
|
||||
if (roll_chance_i(50))
|
||||
{
|
||||
Talk(SAY_SHADOW_NOVA);
|
||||
}
|
||||
context.Repeat(12s);
|
||||
}).Schedule(16s, [this](TaskContext context)
|
||||
context.Repeat();
|
||||
}).Schedule(6s, 12s, [this](TaskContext context)
|
||||
{
|
||||
DoCastRandomTarget(SPELL_SEED_OF_CORRUPTION, 0, 30.0f);
|
||||
context.Repeat(16s);
|
||||
context.Repeat(13s, 27s);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1554,6 +1554,16 @@ struct ScalingStatValuesEntry
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool IsTwoHand(uint32 mask) const
|
||||
{
|
||||
if (mask & 0x7E00)
|
||||
{
|
||||
if (mask & 0x00000400) return true;
|
||||
if (mask & 0x00001000) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32 getSpellBonus(uint32 mask) const
|
||||
{
|
||||
if (mask & 0x00008000) return spellPower;
|
||||
|
||||
@@ -121,8 +121,8 @@ protected:
|
||||
{
|
||||
LOG_DEBUG("misc", "Network Thread Starting");
|
||||
|
||||
_updateTimer.expires_from_now(boost::posix_time::milliseconds(10));
|
||||
_updateTimer.async_wait(std::bind(&NetworkThread<SocketType>::Update, this));
|
||||
_updateTimer.expires_from_now(boost::posix_time::milliseconds(1));
|
||||
_updateTimer.async_wait([this](boost::system::error_code const&) { Update(); });
|
||||
_ioContext.run();
|
||||
|
||||
LOG_DEBUG("misc", "Network Thread exits");
|
||||
@@ -135,8 +135,8 @@ protected:
|
||||
if (_stopped)
|
||||
return;
|
||||
|
||||
_updateTimer.expires_from_now(boost::posix_time::milliseconds(10));
|
||||
_updateTimer.async_wait(std::bind(&NetworkThread<SocketType>::Update, this));
|
||||
_updateTimer.expires_from_now(boost::posix_time::milliseconds(1));
|
||||
_updateTimer.async_wait([this](boost::system::error_code const&) { Update(); });
|
||||
|
||||
AddNewSockets();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user