diff --git a/.gitignore b/.gitignore index 201bcefa6..42316d04f 100644 --- a/.gitignore +++ b/.gitignore @@ -67,7 +67,7 @@ nbproject/ .idea cmake-build-debug/* cmake-build-debug-coverage/* -cmake-build-debug-event-trace +cmake-build-debug-event-trace/* coverage-report/ # diff --git a/data/sql/updates/db_world/2022_06_26_00.sql b/data/sql/updates/db_world/2022_06_26_00.sql new file mode 100644 index 000000000..5763d5df0 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_26_00.sql @@ -0,0 +1,5 @@ +-- DB update 2022_06_22_03 -> 2022_06_26_00 +-- +DELETE FROM `spell_script_names` WHERE `spell_id`=24315; +INSERT INTO `spell_script_names` VALUES +(24315,'spell_threatening_gaze_charge'); diff --git a/data/sql/updates/db_world/2022_06_26_01.sql b/data/sql/updates/db_world/2022_06_26_01.sql new file mode 100644 index 000000000..5504d37bb --- /dev/null +++ b/data/sql/updates/db_world/2022_06_26_01.sql @@ -0,0 +1,10 @@ +-- DB update 2022_06_26_00 -> 2022_06_26_01 +-- +SET @ENTRY := 14517; +SET @PATH := @ENTRY*10; +UPDATE `creature_template_movement` SET `Flight` = 2 WHERE `CreatureId` = @ENTRY; + +DELETE FROM `waypoint_data` WHERE `id` = @PATH; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(@PATH, 1, -12291.88, -1380.0778, 144.90173, 2.286381244659423828, 0, 2, 0, 100, 0), +(@PATH, 2, -12273.857, -1407.774, 132.22281, 0, 0, 2, 0, 100, 0); diff --git a/data/sql/updates/db_world/2022_06_26_02.sql b/data/sql/updates/db_world/2022_06_26_02.sql new file mode 100644 index 000000000..2b2bf7d43 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_26_02.sql @@ -0,0 +1,5 @@ +-- DB update 2022_06_26_01 -> 2022_06_26_02 +-- +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ('spell_hakkar_blood_siphon'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(24324, 'spell_hakkar_blood_siphon'); diff --git a/data/sql/updates/db_world/2022_06_26_03.sql b/data/sql/updates/db_world/2022_06_26_03.sql new file mode 100644 index 000000000..48b6a32e4 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_26_03.sql @@ -0,0 +1,6 @@ +-- DB update 2022_06_26_02 -> 2022_06_26_03 + +DELETE FROM `creature_template_movement` WHERE `CreatureId`=11502; +INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`, `Chase`, `Random`, `InteractionPauseTimer`) VALUES +(11502, 0, 0, 0, 1, 0, 0, NULL); + diff --git a/data/sql/updates/db_world/2022_06_26_04.sql b/data/sql/updates/db_world/2022_06_26_04.sql new file mode 100644 index 000000000..d55bb1a61 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_26_04.sql @@ -0,0 +1,3 @@ +-- DB update 2022_06_26_03 -> 2022_06_26_04 +-- +UPDATE `quest_offer_reward` SET `RewardText` = 'Your tenacity and courage are astounding, $G priest:priestess;. You have earned the right to hold the Splinter of Nordrassil. Only one task remains: The Eye of Shadow must be recovered. Scour the world.' WHERE `ID` = 7622; diff --git a/data/sql/updates/db_world/2022_06_26_05.sql b/data/sql/updates/db_world/2022_06_26_05.sql new file mode 100644 index 000000000..4bb6b9e00 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_26_05.sql @@ -0,0 +1,7 @@ +-- DB update 2022_06_26_04 -> 2022_06_26_05 +-- +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 14605; + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 14605) AND (`source_type` = 0) AND (`id` IN (0)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(14605, 0, 0, 0, 67, 0, 100, 0, 3000, 10000, 0, 0, 0, 11, 8355, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Bone Construct - On Behind Target - Cast \'Exploit Weakness\''); diff --git a/data/sql/updates/db_world/2022_06_27_00.sql b/data/sql/updates/db_world/2022_06_27_00.sql new file mode 100644 index 000000000..ccafe61d4 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_27_00.sql @@ -0,0 +1,3 @@ +-- DB update 2022_06_26_05 -> 2022_06_27_00 +-- fix Woodlands Walker gossip +UPDATE `creature_template` SET `npcflag`=`npcflag` |1 WHERE `entry` = 26421; diff --git a/data/sql/updates/db_world/2022_06_27_01.sql b/data/sql/updates/db_world/2022_06_27_01.sql new file mode 100644 index 000000000..60cf47c26 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_27_01.sql @@ -0,0 +1,9 @@ +-- DB update 2022_06_27_00 -> 2022_06_27_01 +-- +DELETE FROM `areatrigger_scripts` WHERE `entry`=3956; +INSERT INTO `areatrigger_scripts` VALUES +(3956,'at_zulgurub_bridge_speech'); + +DELETE FROM `creature_text` WHERE `CreatureID`=14834 AND `GroupId`=4; +INSERT INTO `creature_text` VALUES +(14834,4,0,'Your callous disregard for the sovereign might of the Gurubashi Empire has been noted. The inhabitants of Zul\'Gurub have been alerted to your presence.',16,0,100,0,0,0,10550,3,'Hakkar SAY_PROTECT_GURUBASHI_EMPIRE'); diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index f08e5eaf8..b03e25578 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -207,6 +207,9 @@ public: static bool IsInBounds(CreatureBoundary const& boundary, Position const* who); bool IsInBoundary(Position const* who = nullptr) const; + + virtual void CalculateThreat(Unit* /*hatedUnit*/, float& /*threat*/, SpellInfo const* /*threatSpell*/) { } + protected: virtual void MoveInLineOfSight(Unit* /*who*/); diff --git a/src/server/game/Combat/HostileRefMgr.cpp b/src/server/game/Combat/HostileRefMgr.cpp index 20b30a27a..f44638080 100644 --- a/src/server/game/Combat/HostileRefMgr.cpp +++ b/src/server/game/Combat/HostileRefMgr.cpp @@ -38,12 +38,23 @@ void HostileRefMgr::threatAssist(Unit* victim, float baseThreat, SpellInfo const return; HostileReference* ref = getFirst(); - float threat = ThreatCalcHelper::calcThreat(victim, iOwner, baseThreat, (threatSpell ? threatSpell->GetSchoolMask() : SPELL_SCHOOL_MASK_NORMAL), threatSpell); + float threat = ThreatCalcHelper::calcThreat(victim, baseThreat, (threatSpell ? threatSpell->GetSchoolMask() : SPELL_SCHOOL_MASK_NORMAL), threatSpell); threat /= getSize(); while (ref) { - if (ThreatCalcHelper::isValidProcess(victim, ref->GetSource()->GetOwner(), threatSpell)) + Unit* refOwner = ref->GetSource()->GetOwner(); + if (ThreatCalcHelper::isValidProcess(victim, refOwner, threatSpell)) + { + if (Creature* hatingCreature = refOwner->ToCreature()) + { + if (hatingCreature->IsAIEnabled) + { + hatingCreature->AI()->CalculateThreat(victim, threat, threatSpell); + } + } + ref->GetSource()->doAddThreat(victim, threat); + } ref = ref->next(); } diff --git a/src/server/game/Combat/ThreatMgr.cpp b/src/server/game/Combat/ThreatMgr.cpp index 5a9bfcde5..87af1c4d4 100644 --- a/src/server/game/Combat/ThreatMgr.cpp +++ b/src/server/game/Combat/ThreatMgr.cpp @@ -32,11 +32,11 @@ //============================================================== // The hatingUnit is not used yet -float ThreatCalcHelper::calcThreat(Unit* hatedUnit, Unit* /*hatingUnit*/, float threat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell) +float ThreatCalcHelper::calcThreat(Unit* hatedUnit, float threat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell) { if (threatSpell) { - if (SpellThreatEntry const* threatEntry = sSpellMgr->GetSpellThreatEntry(threatSpell->Id)) + if (SpellThreatEntry const* threatEntry = sSpellMgr->GetSpellThreatEntry(threatSpell->Id)) if (threatEntry->pctMod != 1.0f) threat *= threatEntry->pctMod; @@ -427,10 +427,19 @@ void ThreatMgr::clearReferences() void ThreatMgr::addThreat(Unit* victim, float threat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell) { - if (!ThreatCalcHelper::isValidProcess(victim, GetOwner(), threatSpell)) + if (!ThreatCalcHelper::isValidProcess(victim, iOwner, threatSpell)) return; - doAddThreat(victim, ThreatCalcHelper::calcThreat(victim, iOwner, threat, schoolMask, threatSpell)); + threat = ThreatCalcHelper::calcThreat(victim, threat, schoolMask, threatSpell); + if (Creature* hatingCreature = iOwner->ToCreature()) + { + if (hatingCreature->IsAIEnabled) + { + hatingCreature->AI()->CalculateThreat(victim, threat, threatSpell); + } + } + + doAddThreat(victim, threat); } void ThreatMgr::doAddThreat(Unit* victim, float threat) diff --git a/src/server/game/Combat/ThreatMgr.h b/src/server/game/Combat/ThreatMgr.h index b6622d8e1..5f87b094c 100644 --- a/src/server/game/Combat/ThreatMgr.h +++ b/src/server/game/Combat/ThreatMgr.h @@ -39,7 +39,7 @@ class SpellInfo; struct ThreatCalcHelper { - static float calcThreat(Unit* hatedUnit, Unit* hatingUnit, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr); + static float calcThreat(Unit* hatedUnit, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr); static bool isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellInfo const* threatSpell = nullptr); }; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 47d29b0e9..2584cbdae 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -487,7 +487,7 @@ bool Creature::InitEntry(uint32 Entry, const CreatureData* data) return true; } -bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changelevel) +bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changelevel, bool updateAI) { if (!InitEntry(Entry, data)) return false; @@ -596,6 +596,12 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele SetDetectionDistance(cInfo->detection_range); LoadSpellTemplateImmunity(); + + if (updateAI) + { + AIM_Initialize(); + } + return true; } diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index f3ee608a5..636fdace6 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -169,7 +169,8 @@ public: void UpdateMovementFlags(); uint32 GetRandomId(uint32 id1, uint32 id2, uint32 id3); - bool UpdateEntry(uint32 entry, const CreatureData* data = nullptr, bool changelevel = true ); + bool UpdateEntry(uint32 entry, const CreatureData* data = nullptr, bool changelevel = true, bool updateAI = false); + bool UpdateEntry(uint32 entry, bool updateAI) { return UpdateEntry(entry, nullptr, true, updateAI); } bool UpdateStats(Stats stat) override; bool UpdateAllStats() override; void UpdateResistances(uint32 school) override; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index c1be0701d..dccb099bd 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -553,8 +553,10 @@ void Pet::SavePetToDB(PetSaveMode mode) // save pet std::string actionBar = GenerateActionBarData(); - ASSERT(owner->GetPetStable()->CurrentPet && owner->GetPetStable()->CurrentPet->PetNumber == m_charmInfo->GetPetNumber()); - FillPetInfo(&owner->GetPetStable()->CurrentPet.value()); + if (owner->GetPetStable()->CurrentPet && owner->GetPetStable()->CurrentPet->PetNumber == m_charmInfo->GetPetNumber()) + { + FillPetInfo(&owner->GetPetStable()->CurrentPet.value()); + } stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_PET); stmt->SetData(0, m_charmInfo->GetPetNumber()); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 77f285220..706cc7840 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8975,16 +8975,18 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) // only if current pet in slot pet->SavePetToDB(mode); - ASSERT(m_petStable->CurrentPet && m_petStable->CurrentPet->PetNumber == pet->GetCharmInfo()->GetPetNumber()); - if (mode == PET_SAVE_NOT_IN_SLOT) + if (m_petStable->CurrentPet && m_petStable->CurrentPet->PetNumber == pet->GetCharmInfo()->GetPetNumber()) { - m_petStable->UnslottedPets.push_back(std::move(*m_petStable->CurrentPet)); - m_petStable->CurrentPet.reset(); + if (mode == PET_SAVE_NOT_IN_SLOT) + { + m_petStable->UnslottedPets.push_back(std::move(*m_petStable->CurrentPet)); + m_petStable->CurrentPet.reset(); + } + else if (mode == PET_SAVE_AS_DELETED) + m_petStable->CurrentPet.reset(); + // else if (stable slots) handled in opcode handlers due to required swaps + // else (current pet) doesnt need to do anything } - else if (mode == PET_SAVE_AS_DELETED) - m_petStable->CurrentPet.reset(); - // else if (stable slots) handled in opcode handlers due to required swaps - // else (current pet) doesnt need to do anything SetMinion(pet, false); @@ -10594,7 +10596,7 @@ void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 ite // cooldown information stored in item prototype // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client. - bool useSpellCooldown = false; + bool useSpellCooldown = spellInfo->HasAttribute(SPELL_ATTR7_CAN_BE_MULTI_CAST); if (itemId) { if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId)) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 250118625..debaaa62f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -2330,14 +2330,14 @@ void Unit::CalcHealAbsorb(HealInfo& healInfo) healInfo.AbsorbHeal(absorbAmount); } -void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extra) +void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType /*= BASE_ATTACK*/, bool extra /*= false*/, bool ignoreCasting /*= false*/) { if (HasUnitFlag(UNIT_FLAG_PACIFIED)) { return; } - if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) && !extra) + if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) && !extra && !ignoreCasting) { return; } @@ -7845,13 +7845,9 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere if (AuraEffect const* aurEff = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_SHAMAN, 0x10000000, 0x0, 0x0, GetGUID())) { Aura* flameShock = aurEff->GetBase(); - int32 maxDuration = flameShock->GetMaxDuration(); - int32 newDuration = flameShock->GetDuration() + 2 * aurEff->GetAmplitude(); - - flameShock->SetDuration(newDuration); - // is it blizzlike to change max duration for FS? - if (newDuration > maxDuration) - flameShock->SetMaxDuration(newDuration); + int32 extraTime = 2 * aurEff->GetAmplitude(); + flameShock->SetMaxDuration(flameShock->GetMaxDuration() + extraTime); + flameShock->SetDuration(flameShock->GetDuration() + extraTime); return true; } @@ -17994,8 +17990,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au GetMotionMaster()->MoveIdle(); StopMoving(); - if (charmer->GetTypeId() == TYPEID_PLAYER && - charmer->getClass() == CLASS_WARLOCK) + if (charmer->GetTypeId() == TYPEID_PLAYER && charmer->getClass() == CLASS_WARLOCK && ToCreature()->GetCreatureTemplate()->type == CREATURE_TYPE_DEMON) { // Disable CreatureAI/SmartAI and switch to CharmAI when charmed by warlock Creature* charmed = ToCreature(); @@ -18649,23 +18644,38 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) } } - WorldObject::SetPhaseMask(newPhaseMask, update); + WorldObject::SetPhaseMask(newPhaseMask, false); if (!IsInWorld()) + { return; + } for (ControlSet::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ) { Unit* controlled = *itr; ++itr; if (controlled->GetTypeId() == TYPEID_UNIT) + { controlled->SetPhaseMask(newPhaseMask, true); + } } for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i) + { if (m_SummonSlot[i]) + { if (Creature* summon = GetMap()->GetCreature(m_SummonSlot[i])) + { summon->SetPhaseMask(newPhaseMask, true); + } + } + } + + if (update) + { + UpdateObjectVisibility(); + } } void Unit::UpdateObjectVisibility(bool forced, bool /*fromUpdate*/) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 8bdc2d8e1..a7e774788 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1534,7 +1534,7 @@ public: void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, std::list& procAuras); void HandleEmoteCommand(uint32 emoteId); - void AttackerStateUpdate (Unit* victim, WeaponAttackType attType = BASE_ATTACK, bool extra = false); + void AttackerStateUpdate (Unit* victim, WeaponAttackType attType = BASE_ATTACK, bool extra = false, bool ignoreCasting = false); void CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* damageInfo, WeaponAttackType attackType = BASE_ATTACK, const bool sittingVictim = false); void DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss); diff --git a/src/server/game/Handlers/LFGHandler.cpp b/src/server/game/Handlers/LFGHandler.cpp index 9c494d627..e7cf852a6 100644 --- a/src/server/game/Handlers/LFGHandler.cpp +++ b/src/server/game/Handlers/LFGHandler.cpp @@ -48,7 +48,7 @@ void BuildPartyLockDungeonBlock(WorldPacket& data, const lfg::LfgLockPartyMap& l void WorldSession::HandleLfgJoinOpcode(WorldPackets::LFG::LFGJoin& packet) { - if (!sLFGMgr->isOptionEnabled(lfg::LFG_OPTION_ENABLE_DUNGEON_FINDER | lfg::LFG_OPTION_ENABLE_RAID_BROWSER) || + if (!sLFGMgr->isOptionEnabled(lfg::LFG_OPTION_ENABLE_DUNGEON_FINDER | lfg::LFG_OPTION_ENABLE_RAID_BROWSER | lfg::LFG_OPTION_ENABLE_SEASONAL_BOSSES) || (GetPlayer()->GetGroup() && GetPlayer()->GetGroup()->GetLeaderGUID() != GetPlayer()->GetGUID() && (GetPlayer()->GetGroup()->GetMembersCount() == MAXGROUPSIZE || !GetPlayer()->GetGroup()->isLFGGroup()))) return; diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 9039961d5..985ead07c 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -1363,12 +1363,6 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->Effects[EFFECT_0].Amplitude = 15000; }); - // Threatening Gaze - ApplySpellFix({ 24314 }, [](SpellInfo* spellInfo) - { - spellInfo->AuraInterruptFlags |= AURA_INTERRUPT_FLAG_CAST; - }); - // Frightening Shout ApplySpellFix({ 19134 }, [](SpellInfo* spellInfo) { @@ -4295,6 +4289,12 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->Effects[EFFECT_0].MiscValueB = 844; }); + // Hakkar Cause Insanity + ApplySpellFix({ 24327 }, [](SpellInfo* spellInfo) + { + spellInfo->Dispel = DISPEL_NONE; + }); + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { SpellInfo* spellInfo = mSpellInfoMap[i]; diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp index ec924ec79..4a8fe95d7 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp @@ -982,7 +982,7 @@ struct npc_drakonid_spawner : public ScriptedAI { if (Creature* construct = ObjectAccessor::GetCreature(*me, summonGuid)) { - construct->UpdateEntry(NPC_BONE_CONSTRUCT); + construct->UpdateEntry(NPC_BONE_CONSTRUCT, true); construct->SetReactState(REACT_PASSIVE); construct->SetStandState(UNIT_STAND_STATE_DEAD); construct->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp index deb48fa13..77877043f 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp @@ -105,7 +105,8 @@ public: me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_DAGGER)); me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(WEAPON_DAGGER)); me->SetWalk(false); - me->GetMotionMaster()->MovePoint(0, PosMoveOnSpawn[0]); + me->SetHomePosition(PosMoveOnSpawn[0]); + me->GetMotionMaster()->MoveTargetedHome(); } void JustDied(Unit* /*killer*/) override @@ -131,31 +132,37 @@ public: { uint8 sideA = 0; uint8 sideB = 0; - for (std::list::const_iterator itr = triggerList.begin(); itr != triggerList.end(); ++itr) + for (auto const& trigger : triggerList) { - if (Creature* trigger = *itr) + if (trigger->GetPositionY() < -1625.0f) { - if (trigger->GetPositionY() < -1625.0f) - { - _triggersSideAGUID[sideA] = trigger->GetGUID(); - ++sideA; - } - else - { - _triggersSideBGUID[sideB] = trigger->GetGUID(); - ++sideB; - } + _triggersSideAGUID[sideA] = trigger->GetGUID(); + ++sideA; + } + else + { + _triggersSideBGUID[sideB] = trigger->GetGUID(); + ++sideB; } } } } + void JustReachedHome() override + { + if (GameObject* object = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_GONG_OF_BETHEKK))) + object->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + me->DespawnOrUnsummon(); + } + void EnterEvadeMode(EvadeReason why) override { BossAI::EnterEvadeMode(why); - if (GameObject* object = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_GONG_OF_BETHEKK))) - object->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - me->DespawnOrUnsummon(4000); + + std::list panthers; + GetCreatureListWithEntryInGrid(panthers, me, NPC_ZULIAN_PROWLER, 200.f); + for (auto const& panther : panthers) + panther->DespawnOrUnsummon(); } void SetData(uint32 id, uint32 /*value*/) override @@ -221,33 +228,27 @@ public: } case EVENT_TRANSFORM: { - DoCast(me, SPELL_PANTHER_TRANSFORM); // SPELL_AURA_TRANSFORM + DoCastSelf(SPELL_PANTHER_TRANSFORM); // SPELL_AURA_TRANSFORM me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(EQUIP_UNEQUIP)); me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(EQUIP_UNEQUIP)); - /* - const CreatureTemplate* cinfo = me->GetCreatureTemplate(); - me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35))); - me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35))); - me->UpdateDamagePhysical(BASE_ATTACK); - */ me->AttackStop(); DoResetThreat(); me->SetReactState(REACT_PASSIVE); me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - DoCast(me, SPELL_VANISH_VISUAL); - DoCast(me, SPELL_VANISH); + DoCastSelf(SPELL_VANISH_VISUAL); + DoCastSelf(SPELL_VANISH); events.ScheduleEvent(EVENT_VANISH, 1000, 0, PHASE_ONE); break; } case EVENT_VANISH: - DoCast(me, SPELL_SUPER_INVIS); + DoCastSelf(SPELL_SUPER_INVIS); me->SetWalk(false); me->GetMotionMaster()->MovePoint(0, frand(-11551.0f, -11508.0f), frand(-1638.0f, -1617.0f), me->GetPositionZ()); events.ScheduleEvent(EVENT_VANISH_2, 9000, 0, PHASE_ONE); break; case EVENT_VANISH_2: - DoCast(me, SPELL_VANISH); - DoCast(me, SPELL_SUPER_INVIS); + DoCastSelf(SPELL_VANISH); + DoCastSelf(SPELL_SUPER_INVIS); events.ScheduleEvent(EVENT_VISIBLE, urand(7000, 10000), 0, PHASE_ONE); break; case EVENT_VISIBLE: @@ -272,12 +273,6 @@ public: DoCast(me, SPELL_VANISH_VISUAL); me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_DAGGER)); me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(WEAPON_DAGGER)); - /* - const CreatureTemplate* cinfo = me->GetCreatureTemplate(); - me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg)); - me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg)); - me->UpdateDamagePhysical(BASE_ATTACK); - */ me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(4000, 7000), 0, PHASE_ONE); events.ScheduleEvent(EVENT_GOUGE, urand(12000, 15000), 0, PHASE_ONE); @@ -322,11 +317,6 @@ enum ZulianProwlerEvents EVENT_ATTACK = 1 }; -/*Position const PosProwlerCenter[1] = -{ - { -11556.7f, -1631.344f, 41.2994f, 0.0f } -};*/ - class npc_zulian_prowler : public CreatureScript { public: @@ -361,7 +351,9 @@ public: void SpellHit(Unit* caster, SpellInfo const* spell) override { if (spell->Id == SPELL_MARK_OF_ARLOKK_TRIGGER) // Should only hit if line of sight - me->Attack(caster, true); + { + AttackStart(caster); + } } void JustDied(Unit* /*killer*/) override @@ -390,7 +382,9 @@ public: { case EVENT_ATTACK: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0.0f, 100, false)) - me->Attack(target, true); + { + AttackStart(target); + } break; default: break; diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp index 0c7fef454..6fd79576d 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp @@ -38,7 +38,8 @@ enum Events EVENT_AVATAR = 1, EVENT_GROUND_TREMOR = 2, EVENT_START_PURSUIT = 3, - EVENT_ENTANGLING_ROOTS = 4 + EVENT_STOP_PURSUIT = 4, + EVENT_ENTANGLING_ROOTS = 5 }; class boss_grilek : public CreatureScript // grilek @@ -52,6 +53,12 @@ public: { } + void Reset() override + { + _pursuitTargetGUID.Clear(); + BossAI::Reset(); + } + void EnterCombat(Unit* /*who*/) override { _EnterCombat(); @@ -75,10 +82,15 @@ public: switch (eventId) { case EVENT_AVATAR: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + _pursuitTargetGUID = target->GetGUID(); + } DoCast(me, SPELL_AVATAR); - DoResetThreat(); me->SetReactState(REACT_PASSIVE); + DoResetThreat(); events.ScheduleEvent(EVENT_START_PURSUIT, 2s); + events.ScheduleEvent(EVENT_STOP_PURSUIT, 15s); events.ScheduleEvent(EVENT_AVATAR, 45s, 50s); break; case EVENT_GROUND_TREMOR: @@ -87,6 +99,17 @@ public: break; case EVENT_START_PURSUIT: me->SetReactState(REACT_AGGRESSIVE); + if (Unit* pursuitTarget = ObjectAccessor::GetUnit(*me, _pursuitTargetGUID)) + { + me->GetThreatMgr().addThreat(pursuitTarget, 1000000.f); + } + break; + case EVENT_STOP_PURSUIT: + if (Unit* pursuitTarget = ObjectAccessor::GetUnit(*me, _pursuitTargetGUID)) + { + _pursuitTargetGUID.Clear(); + me->GetThreatMgr().addThreat(pursuitTarget, -1000000.f); + } break; case EVENT_ENTANGLING_ROOTS: DoCastVictim(SPELL_ENTANGLING_ROOTS); @@ -99,6 +122,9 @@ public: DoMeleeAttackIfReady(); } + + private: + ObjectGuid _pursuitTargetGUID; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp index a4c181b99..780669e1d 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp @@ -25,22 +25,26 @@ Category: Zul'Gurub #include "Player.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" #include "zulgurub.h" enum Says { - SAY_AGGRO = 0, - SAY_FLEEING = 1, - SAY_MINION_DESTROY = 2, - SAY_PROTECT_ALTAR = 3 + SAY_AGGRO = 0, + SAY_FLEEING = 1, + SAY_MINION_DESTROY = 2, + SAY_PROTECT_ALTAR = 3, + SAY_PROTECT_GURUBASHI_EMPIRE = 4 }; enum Spells { - SPELL_BLOOD_SIPHON = 24322, // Buggy ? + SPELL_POISONOUS_BLOOD = 24321, + SPELL_BLOOD_SIPHON_HEAL = 24322, + SPELL_BLOOD_SIPHON_DMG = 24323, + SPELL_BLOOD_SIPHON = 24324, SPELL_CORRUPTED_BLOOD = 24328, SPELL_CAUSE_INSANITY = 24327, - SPELL_WILL_OF_HAKKAR = 24178, SPELL_ENRAGE = 24318, // The Aspects of all High Priests spells SPELL_ASPECT_OF_JEKLIK = 24687, @@ -55,14 +59,13 @@ enum Events EVENT_BLOOD_SIPHON = 1, EVENT_CORRUPTED_BLOOD = 2, EVENT_CAUSE_INSANITY = 3, - EVENT_WILL_OF_HAKKAR = 4, - EVENT_ENRAGE = 5, + EVENT_ENRAGE = 4, // The Aspects of all High Priests events - EVENT_ASPECT_OF_JEKLIK = 6, - EVENT_ASPECT_OF_VENOXIS = 7, - EVENT_ASPECT_OF_MARLI = 8, - EVENT_ASPECT_OF_THEKAL = 9, - EVENT_ASPECT_OF_ARLOKK = 10 + EVENT_ASPECT_OF_JEKLIK = 5, + EVENT_ASPECT_OF_VENOXIS = 6, + EVENT_ASPECT_OF_MARLI = 7, + EVENT_ASPECT_OF_THEKAL = 8, + EVENT_ASPECT_OF_ARLOKK = 9 }; class boss_hakkar : public CreatureScript @@ -74,6 +77,16 @@ public: { boss_hakkarAI(Creature* creature) : BossAI(creature, DATA_HAKKAR) { } + bool CheckInRoom() override + { + if (me->GetPositionZ() < 52.f || me->GetPositionZ() > 57.28f) + { + BossAI::EnterEvadeMode(EVADE_REASON_BOUNDARY); + return false; + } + return true; + } + void Reset() override { _Reset(); @@ -90,7 +103,6 @@ public: events.ScheduleEvent(EVENT_BLOOD_SIPHON, 90000); events.ScheduleEvent(EVENT_CORRUPTED_BLOOD, 25000); events.ScheduleEvent(EVENT_CAUSE_INSANITY, 17000); - events.ScheduleEvent(EVENT_WILL_OF_HAKKAR, 17000); events.ScheduleEvent(EVENT_ENRAGE, 600000); if (instance->GetBossState(DATA_JEKLIK) != DONE) events.ScheduleEvent(EVENT_ASPECT_OF_JEKLIK, 4000); @@ -107,7 +119,7 @@ public: void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) + if (!UpdateVictim() || !CheckInRoom()) return; events.Update(diff); @@ -120,7 +132,7 @@ public: switch (eventId) { case EVENT_BLOOD_SIPHON: - DoCastVictim(SPELL_BLOOD_SIPHON, true); + DoCastAOE(SPELL_BLOOD_SIPHON, true); events.ScheduleEvent(EVENT_BLOOD_SIPHON, 90000); break; case EVENT_CORRUPTED_BLOOD: @@ -128,20 +140,15 @@ public: events.ScheduleEvent(EVENT_CORRUPTED_BLOOD, urand(30000, 45000)); break; case EVENT_CAUSE_INSANITY: - if (Unit* victim = SelectTarget(SelectTargetMethod::MaxThreat, 0)) + if (Unit* victim = SelectTarget(SelectTargetMethod::MaxThreat, 0, 30.f, true)) { - DoCast(victim, SPELL_CAUSE_INSANITY, true); + DoCast(victim, SPELL_CAUSE_INSANITY); } events.ScheduleEvent(EVENT_CAUSE_INSANITY, urand(35000, 45000)); break; - case EVENT_WILL_OF_HAKKAR: - // Xinef: Skip Tank - DoCast(SelectTarget(SelectTargetMethod::Random, 1, 100, true), SPELL_WILL_OF_HAKKAR); - events.ScheduleEvent(EVENT_WILL_OF_HAKKAR, urand(25000, 35000)); - break; case EVENT_ENRAGE: if (!me->HasAura(SPELL_ENRAGE)) - DoCast(me, SPELL_ENRAGE); + DoCastSelf(SPELL_ENRAGE); events.ScheduleEvent(EVENT_ENRAGE, 90000); break; case EVENT_ASPECT_OF_JEKLIK: @@ -153,7 +160,11 @@ public: events.ScheduleEvent(EVENT_ASPECT_OF_VENOXIS, 8000); break; case EVENT_ASPECT_OF_MARLI: - DoCastVictim(SPELL_ASPECT_OF_MARLI, true); + if (Unit* victim = SelectTarget(SelectTargetMethod::MaxThreat, 0, 5.f, true)) + { + DoCast(victim, SPELL_ASPECT_OF_MARLI, true); + me->GetThreatMgr().modifyThreatPercent(victim, -100.f); + } events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 10000); break; case EVENT_ASPECT_OF_THEKAL: @@ -161,7 +172,11 @@ public: events.ScheduleEvent(EVENT_ASPECT_OF_THEKAL, 15000); break; case EVENT_ASPECT_OF_ARLOKK: - DoCastVictim(SPELL_ASPECT_OF_ARLOKK, true); + if (Unit* victim = SelectTarget(SelectTargetMethod::MaxThreat, 0, 5.f, true)) + { + DoCast(victim, SPELL_ASPECT_OF_ARLOKK, true); + me->GetThreatMgr().modifyThreatPercent(victim, -100.f); + } events.ScheduleEvent(EVENT_ASPECT_OF_ARLOKK, urand(10000, 15000)); break; default: @@ -195,6 +210,28 @@ public: if (Creature* hakkar = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_HAKKAR))) { hakkar->setActive(true); + if (hakkar->GetAI()) + { + hakkar->AI()->Talk(SAY_PROTECT_GURUBASHI_EMPIRE); + } + } + return false; + } + return false; + } +}; + +class at_zulgurub_bridge_speech : public OnlyOnceAreaTriggerScript +{ +public: + at_zulgurub_bridge_speech() : OnlyOnceAreaTriggerScript("at_zulgurub_bridge_speech") {} + + bool _OnTrigger(Player* player, const AreaTrigger* /*at*/) override + { + if (InstanceScript* instance = player->GetInstanceScript()) + { + if (Creature* hakkar = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_HAKKAR))) + { if (hakkar->GetAI()) { hakkar->AI()->Talk(SAY_PROTECT_ALTAR); @@ -228,9 +265,39 @@ public: } }; +class spell_hakkar_blood_siphon : public SpellScript +{ + PrepareSpellScript(spell_hakkar_blood_siphon); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_BLOOD_SIPHON_HEAL, SPELL_BLOOD_SIPHON_DMG }); + } + + void OnSpellHit() + { + Unit* caster = GetCaster(); + Unit* target = GetHitUnit(); + if (!caster || !target) + return; + + if (target->HasAura(SPELL_POISONOUS_BLOOD)) + target->CastSpell(caster, SPELL_BLOOD_SIPHON_DMG, true); + else + target->CastSpell(caster, SPELL_BLOOD_SIPHON_HEAL, true); + } + + void Register() override + { + OnHit += SpellHitFn(spell_hakkar_blood_siphon::OnSpellHit); + } +}; + void AddSC_boss_hakkar() { new boss_hakkar(); new at_zulgurub_entrance_speech(); + new at_zulgurub_bridge_speech(); new at_zulgurub_temple_speech(); + RegisterSpellScript(spell_hakkar_blood_siphon); } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp index ebb69ebee..5b35f0ba4 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp @@ -97,6 +97,13 @@ Position const SpawnBat[6] = { -12293.6220f, -1380.2640f, 144.8304f, 5.483f } }; +enum Misc +{ + PATH_JEKLIK_INTRO = 145170 +}; + +Position const homePosition = { -12291.9f, -1380.08f, 144.902f, 2.28638f }; + struct boss_jeklik : public BossAI { boss_jeklik(Creature* creature) : BossAI(creature, DATA_JEKLIK) { } @@ -106,7 +113,9 @@ struct boss_jeklik : public BossAI DoCastSelf(SPELL_GREEN_CHANNELING); me->SetHover(false); me->SetDisableGravity(false); + me->SetReactState(REACT_PASSIVE); _Reset(); + SetCombatMovement(false); } void JustDied(Unit* /*killer*/) override @@ -117,22 +126,31 @@ struct boss_jeklik : public BossAI void EnterEvadeMode(EvadeReason why) override { - const Position homePos = me->GetHomePosition(); - me->NearTeleportTo(homePos.GetPositionX(), homePos.GetPositionY(), homePos.GetPositionZ(), homePos.GetOrientation()); + me->GetMotionMaster()->Clear(); + me->SetHomePosition(homePosition); + me->NearTeleportTo(homePosition.GetPositionX(), homePosition.GetPositionY(), homePosition.GetPositionZ(), homePosition.GetOrientation()); BossAI::EnterEvadeMode(why); } void EnterCombat(Unit* /*who*/) override { - _EnterCombat(); Talk(SAY_AGGRO); me->RemoveAurasDueToSpell(SPELL_GREEN_CHANNELING); me->SetHover(true); me->SetDisableGravity(true); - me->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); - DoCastSelf(SPELL_BAT_FORM); - events.SetPhase(PHASE_ONE); + DoCastSelf(SPELL_BAT_FORM, true); + me->GetMotionMaster()->MovePath(PATH_JEKLIK_INTRO, false); + } + + void PathEndReached(uint32 /*pathId*/) override + { + me->SetHover(false); + me->SetDisableGravity(false); + _EnterCombat(); + SetCombatMovement(true); + me->SetReactState(REACT_AGGRESSIVE); + events.SetPhase(PHASE_ONE); events.ScheduleEvent(EVENT_CHARGE_JEKLIK, urand(10000, 20000), PHASE_ONE); events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(5000, 15000), PHASE_ONE); events.ScheduleEvent(EVENT_BLOOD_LEECH, urand(5000, 15000), PHASE_ONE); @@ -146,8 +164,6 @@ struct boss_jeklik : public BossAI if (events.IsInPhase(PHASE_ONE) && !HealthAbovePct(50)) { me->RemoveAurasDueToSpell(SPELL_BAT_FORM); - me->SetHover(false); - me->SetDisableGravity(false); DoResetThreat(); events.SetPhase(PHASE_TWO); events.CancelEventGroup(PHASE_ONE); diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp index 6959632e2..018b64b64 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp @@ -80,7 +80,8 @@ enum Misc MODEL_OHGAN_MOUNT = 15271, PATH_MANDOKIR = 492861, POINT_MANDOKIR_END = 24, - CHAINED_SPIRIT_COUNT = 20 + CHAINED_SPIRIT_COUNT = 20, + ACTION_CHARGE = 1 }; Position const PosSummonChainedSpirits[CHAINED_SPIRIT_COUNT] = @@ -169,6 +170,7 @@ public: me->Mount(MODEL_OHGAN_MOUNT); reviveGUID.Clear(); _useExecute = false; + _chargeTarget.first.Clear(); } void JustDied(Unit* /*killer*/) override @@ -226,9 +228,44 @@ public: } } - void SetGUID(ObjectGuid const guid, int32 /*type = 0 */) override + void DoAction(int32 action) override { - reviveGUID = guid; + if (action == ACTION_START_REVIVE) + { + std::list creatures; + GetCreatureListWithEntryInGrid(creatures, me, NPC_CHAINED_SPIRIT, 200.0f); + if (creatures.empty()) + return; + + for (std::list::iterator itr = creatures.begin(); itr != creatures.end(); ++itr) + { + if (Creature* chainedSpirit = ObjectAccessor::GetCreature(*me, (*itr)->GetGUID())) + { + chainedSpirit->AI()->SetGUID(reviveGUID); + chainedSpirit->AI()->DoAction(ACTION_REVIVE); + reviveGUID.Clear(); + } + } + } + } + + void SetGUID(ObjectGuid const guid, int32 type) override + { + if (type == ACTION_CHARGE) + { + if (_chargeTarget.first == guid && _chargeTarget.second > 0.f) + { + if (Unit* target = ObjectAccessor::GetUnit(*me, _chargeTarget.first)) + { + me->RemoveAurasDueToSpell(SPELL_WHIRLWIND); + DoCast(target, SPELL_WATCH_CHARGE, true); + } + } + } + else + { + reviveGUID = guid; + } } void MovementInform(uint32 type, uint32 id) override @@ -244,6 +281,18 @@ public: } } + void CalculateThreat(Unit* hatedUnit, float& threat, SpellInfo const* threatSpell) override + { + if (_chargeTarget.first == hatedUnit->GetGUID()) + { + // Do not count DOTs/HOTs + if (!threatSpell || !threatSpell->HasAttribute(SPELL_ATTR0_CU_NO_INITIAL_THREAT)) + { + _chargeTarget.second += threat; + } + } + } + void DamageDealt(Unit* doneTo, uint32& damage, DamageEffectType /*damagetype*/) override { if (doneTo && doneTo == me->GetVictim()) @@ -264,6 +313,43 @@ public: } } + void DoMeleeAttackIfReady(bool ignoreCasting) + { + if (!ignoreCasting && me->HasUnitState(UNIT_STATE_CASTING)) + { + return; + } + + Unit* victim = me->GetVictim(); + if (!victim || !victim->IsInWorld()) + return; + + if (!me->IsWithinMeleeRange(victim)) + return; + + //Make sure our attack is ready and we aren't currently casting before checking distance + if (me->isAttackReady()) + { + // xinef: prevent base and off attack in same time, delay attack at 0.2 sec + if (me->haveOffhandWeapon()) + if (me->getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY) + me->setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY); + + me->AttackerStateUpdate(victim, BASE_ATTACK, false, ignoreCasting); + me->resetAttackTimer(); + } + + if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK)) + { + // xinef: delay main hand attack if both will hit at the same time (players code) + if (me->getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY) + me->setAttackTimer(BASE_ATTACK, ATTACK_DISPLAY_DELAY); + + me->AttackerStateUpdate(victim, OFF_ATTACK, false, ignoreCasting); + me->resetAttackTimer(OFF_ATTACK); + } + } + void UpdateAI(uint32 diff) override { events.Update(diff); @@ -299,7 +385,14 @@ public: } if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasUnitState(UNIT_STATE_CHARGING)) + { + if (me->GetCurrentSpellCastTime(SPELL_WATCH) >= 0) + { + DoMeleeAttackIfReady(true); + } + return; + } while (uint32 eventId = events.ExecuteEvent()) { @@ -339,6 +432,7 @@ public: { DoCast(player, SPELL_WATCH); Talk(SAY_WATCH, player); + _chargeTarget = std::make_pair(player->GetGUID(), 0.f); } events.ScheduleEvent(EVENT_WATCH_PLAYER, urand(12000, 24000)); break; @@ -393,13 +487,14 @@ public: } } - DoMeleeAttackIfReady(); + DoMeleeAttackIfReady(false); } private: uint8 killCount; ObjectGuid reviveGUID; bool _useExecute; + std::pair _chargeTarget; }; CreatureAI* GetAI(Creature* creature) const override @@ -614,13 +709,16 @@ public: void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (Unit* caster = GetCaster()) + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) { if (Unit* target = GetTarget()) { - if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE && GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEATH) + if (Creature* caster = GetCaster()->ToCreature()) { - caster->CastSpell(target, SPELL_WATCH_CHARGE, true); + if (caster->IsAIEnabled) + { + caster->AI()->SetGUID(target->GetGUID(), ACTION_CHARGE); + } } } } @@ -656,6 +754,29 @@ class spell_mandokir_charge : public SpellScript } }; +class spell_threatening_gaze_charge : public SpellScript +{ + PrepareSpellScript(spell_threatening_gaze_charge) + + void PreventLaunchHit(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + } + + void LaunchHit(SpellEffIndex effIndex) + { + if (Unit* caster = GetCaster()) + if (Unit* target = GetHitUnit()) + caster->CastSpell(target, GetSpellInfo()->Effects[effIndex].TriggerSpell, true); + } + + void Register() override + { + OnEffectLaunchTarget += SpellEffectFn(spell_threatening_gaze_charge::PreventLaunchHit, EFFECT_1, SPELL_EFFECT_TRIGGER_SPELL); + OnEffectHitTarget += SpellEffectFn(spell_threatening_gaze_charge::LaunchHit, EFFECT_1, SPELL_EFFECT_TRIGGER_SPELL); + } +}; + void AddSC_boss_mandokir() { new boss_mandokir(); @@ -664,4 +785,5 @@ void AddSC_boss_mandokir() RegisterZulGurubCreatureAI(npc_vilebranch_speaker); new spell_threatening_gaze(); RegisterSpellScript(spell_mandokir_charge); + RegisterSpellScript(spell_threatening_gaze_charge); } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp index 51c05e37c..f2afa88ca 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp @@ -212,9 +212,15 @@ public: DoCast(target, SPELL_THOUSAND_BLADES, false); } - _thousandBladesTargets.erase(itr); - - events.ScheduleEvent(EVENT_THOUSAND_BLADES, 500ms); + if (_thousandBladesTargets.erase(itr) != _thousandBladesTargets.end()) + { + events.ScheduleEvent(EVENT_THOUSAND_BLADES, 500ms); + } + else + { + _thousandBladesCount = urand(2, 5); + events.ScheduleEvent(EVENT_THOUSAND_BLADES, 15s, 22s); + } } else { diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp index 5edc7b053..dcd0ee6a3 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp @@ -15,12 +15,18 @@ * with this program. If not, see . */ +#include "AreaBoundary.h" #include "CreatureAIImpl.h" #include "Player.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "obsidian_sanctum.h" +BossBoundaryData const boundaries = +{ + { DATA_SARTHARION, new RectangleBoundary(3218.86f, 3275.69f, 484.68f, 572.4f) } +}; + class instance_obsidian_sanctum : public InstanceMapScript { public: @@ -36,6 +42,7 @@ public: instance_obsidian_sanctum_InstanceMapScript(Map* pMap) : InstanceScript(pMap), portalCount(0) { SetBossNumber(MAX_ENCOUNTERS); + LoadBossBoundaries(boundaries); } bool IsEncounterInProgress() const override