/* * Copyright (C) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ /* * Ordered alphabetically using scriptname. * Scriptnames of files in this file should be prefixed with "npc_pet_dk_". */ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "CombatAI.h" #include "Cell.h" #include "CellImpl.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" enum DeathKnightSpells { SPELL_DK_SUMMON_GARGOYLE_1 = 49206, SPELL_DK_SUMMON_GARGOYLE_2 = 50514, SPELL_DK_DISMISS_GARGOYLE = 50515, SPELL_DK_SANCTUARY = 54661, SPELL_DK_NIGHT_OF_THE_DEAD = 62137, SPELL_DK_PET_SCALING = 61017 }; class npc_pet_dk_ebon_gargoyle : public CreatureScript { public: npc_pet_dk_ebon_gargoyle() : CreatureScript("npc_pet_dk_ebon_gargoyle") { } struct npc_pet_dk_ebon_gargoyleAI : ScriptedAI { npc_pet_dk_ebon_gargoyleAI(Creature* creature) : ScriptedAI(creature) { _despawnTimer = 36000; // 30 secs + 4 fly out + 2 initial attack timer _despawning = false; _initialSelection = true; _targetGUID = 0; } void MovementInform(uint32 type, uint32 point) { if (type == POINT_MOTION_TYPE && point == 1) { me->SetCanFly(false); me->SetDisableGravity(false); } } void InitializeAI() { ScriptedAI::InitializeAI(); Unit* owner = me->GetOwner(); if (!owner) return; // Xinef: Night of the Dead avoidance if (Aura *aur = me->GetAura(SPELL_DK_NIGHT_OF_THE_DEAD)) if (Unit* owner = me->GetOwner()) if (AuraEffect *aurEff = owner->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_DEATHKNIGHT, 2718, 0)) if (aur->GetEffect(0)) aur->GetEffect(0)->SetAmount(-aurEff->GetSpellInfo()->Effects[EFFECT_2].CalcValue()); float tz = me->GetMap()->GetHeight(me->GetPhaseMask(), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), true, MAX_FALL_DISTANCE); me->GetMotionMaster()->MoveCharge(me->GetPositionX(), me->GetPositionY(), tz, 7.0f, 1); me->AddUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD); me->SetCanFly(true); me->SetDisableGravity(true); _selectionTimer = 2000; _initialCastTimer = 0; } void MySelectNextTarget() { Unit* owner = me->GetOwner(); if (owner && owner->GetTypeId() == TYPEID_PLAYER && (!me->GetVictim() || me->GetVictim()->IsImmunedToSpell(sSpellMgr->GetSpellInfo(51963)) || !me->IsValidAttackTarget(me->GetVictim()) || !owner->CanSeeOrDetect(me->GetVictim()))) { Unit* selection = owner->ToPlayer()->GetSelectedUnit(); if (selection && selection != me->GetVictim() && me->IsValidAttackTarget(selection)) { me->GetMotionMaster()->Clear(false); SetGazeOn(selection); } else if (!me->GetVictim() || !owner->CanSeeOrDetect(me->GetVictim())) { me->CombatStop(true); me->GetMotionMaster()->Clear(false); me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, 0.0f); RemoveTargetAura(); } } } void AttackStart(Unit* who) { RemoveTargetAura(); _targetGUID = who->GetGUID(); me->AddAura(SPELL_DK_SUMMON_GARGOYLE_1, who); ScriptedAI::AttackStart(who); } void RemoveTargetAura() { if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) target->RemoveAura(SPELL_DK_SUMMON_GARGOYLE_1, me->GetGUID()); } void Reset() { _selectionTimer = 0; me->SetReactState(REACT_PASSIVE); MySelectNextTarget(); } // Fly away when dismissed void FlyAway() { RemoveTargetAura(); // Stop Fighting me->CombatStop(true); me->ApplyModFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE, true); // Sanctuary me->CastSpell(me, SPELL_DK_SANCTUARY, true); me->SetReactState(REACT_PASSIVE); me->SetSpeed(MOVE_FLIGHT, 1.0f, true); me->SetSpeed(MOVE_RUN, 1.0f, true); float x = me->GetPositionX() + 20 * cos(me->GetOrientation()); float y = me->GetPositionY() + 20 * sin(me->GetOrientation()); float z = me->GetPositionZ() + 40; me->DisableSpline(); me->GetMotionMaster()->Clear(false); me->GetMotionMaster()->MoveCharge(x, y, z, 7.0f, 1); me->SetCanFly(true); me->SetDisableGravity(true); _despawning = true; } void UpdateAI(uint32 diff) { if (_initialSelection) { _initialSelection = false; // Find victim of Summon Gargoyle spell std::list targets; Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, 50); Trinity::UnitListSearcher searcher(me, targets, u_check); me->VisitNearbyObject(50, searcher); for (std::list::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) if ((*iter)->GetAura(SPELL_DK_SUMMON_GARGOYLE_1, me->GetOwnerGUID())) { (*iter)->RemoveAura(SPELL_DK_SUMMON_GARGOYLE_1, me->GetOwnerGUID()); SetGazeOn(*iter); _targetGUID = (*iter)->GetGUID(); break; } } if (_despawnTimer > 4000) { _despawnTimer -= diff; if (!UpdateVictimWithGaze()) { MySelectNextTarget(); return; } _initialCastTimer += diff; _selectionTimer += diff; if (_selectionTimer >= 1000) { MySelectNextTarget(); _selectionTimer = 0; } if (_initialCastTimer >= 2000 && !me->HasUnitState(UNIT_STATE_CASTING|UNIT_STATE_LOST_CONTROL) && me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE) me->CastSpell(me->GetVictim(), 51963, false); } else { if (!_despawning) FlyAway(); if (_despawnTimer > diff) _despawnTimer -= diff; else me->DespawnOrUnsummon(); } } private: uint64 _targetGUID; uint32 _despawnTimer; uint32 _selectionTimer; uint32 _initialCastTimer; bool _despawning; bool _initialSelection; }; CreatureAI* GetAI(Creature* creature) const { return new npc_pet_dk_ebon_gargoyleAI(creature); } }; class npc_pet_dk_ghoul : public CreatureScript { public: npc_pet_dk_ghoul() : CreatureScript("npc_pet_dk_ghoul") { } struct npc_pet_dk_ghoulAI : public CombatAI { npc_pet_dk_ghoulAI(Creature *c) : CombatAI(c) { } void JustDied(Unit *who) { if (me->IsGuardian() || me->IsSummon()) me->ToTempSummon()->UnSummon(); } }; CreatureAI* GetAI(Creature* pCreature) const { return new npc_pet_dk_ghoulAI (pCreature); } }; class npc_pet_dk_army_of_the_dead : public CreatureScript { public: npc_pet_dk_army_of_the_dead() : CreatureScript("npc_pet_dk_army_of_the_dead") { } struct npc_pet_dk_army_of_the_deadAI : public CombatAI { npc_pet_dk_army_of_the_deadAI(Creature* creature) : CombatAI(creature) { } void InitializeAI() { CombatAI::InitializeAI(); ((Minion*)me)->SetFollowAngle(rand_norm()*2*M_PI); // Heroism / Bloodlust immunity me->ApplySpellImmune(0, IMMUNITY_ID, 32182, true); me->ApplySpellImmune(0, IMMUNITY_ID, 2825, true); } }; CreatureAI* GetAI(Creature* creature) const { return new npc_pet_dk_army_of_the_deadAI (creature); } }; class npc_pet_dk_dancing_rune_weapon : public CreatureScript { public: npc_pet_dk_dancing_rune_weapon() : CreatureScript("npc_pet_dk_dancing_rune_weapon") { } struct npc_pet_dk_dancing_rune_weaponAI : public NullCreatureAI { npc_pet_dk_dancing_rune_weaponAI(Creature* creature) : NullCreatureAI(creature) { } void InitializeAI() { // Xinef: Hit / Expertise scaling me->AddAura(61017, me); if (Unit* owner = me->GetOwner()) me->GetMotionMaster()->MoveFollow(owner, 0.01f, me->GetFollowAngle(), MOTION_SLOT_CONTROLLED); NullCreatureAI::InitializeAI(); } }; CreatureAI* GetAI(Creature* creature) const { return new npc_pet_dk_dancing_rune_weaponAI (creature); } }; void AddSC_deathknight_pet_scripts() { new npc_pet_dk_ebon_gargoyle(); new npc_pet_dk_ghoul(); new npc_pet_dk_army_of_the_dead(); new npc_pet_dk_dancing_rune_weapon(); }