Merge branch 'azerothcore:master' into Playerbot

This commit is contained in:
ZhengPeiRu21
2022-06-27 09:06:46 -06:00
committed by GitHub
29 changed files with 459 additions and 129 deletions

2
.gitignore vendored
View File

@@ -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/
#

View File

@@ -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');

View File

@@ -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);

View File

@@ -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');

View File

@@ -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);

View File

@@ -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;

View File

@@ -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\'');

View File

@@ -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;

View File

@@ -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');

View File

@@ -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*/);

View File

@@ -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();
}

View File

@@ -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)

View File

@@ -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);
};

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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());

View File

@@ -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))

View File

@@ -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*/)

View File

@@ -1534,7 +1534,7 @@ public:
void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, std::list<AuraApplication*>& 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);

View File

@@ -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;

View File

@@ -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];

View File

@@ -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);

View File

@@ -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<Creature*>::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<Creature*> 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;

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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<Creature*> creatures;
GetCreatureListWithEntryInGrid(creatures, me, NPC_CHAINED_SPIRIT, 200.0f);
if (creatures.empty())
return;
for (std::list<Creature*>::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<ObjectGuid, float> _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);
}

View File

@@ -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
{

View File

@@ -15,12 +15,18 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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