diff options
author | Nay <dnpd.dd@gmail.com> | 2012-09-22 10:56:41 +0100 |
---|---|---|
committer | Nay <dnpd.dd@gmail.com> | 2012-09-22 10:56:41 +0100 |
commit | 95d713ac6f53a270be879b6661c3d988607261c5 (patch) | |
tree | e6be56757cba94dcd25af666a77386f25d16e1f2 /src | |
parent | 7090197c6ade85e1b223d3cb46923b89133f89a5 (diff) | |
parent | 5faa9d37b4240223a86939b4d898ec666e71f0c0 (diff) |
Merge remote-tracking branch 'origin/master' into 4.3.4
Diffstat (limited to 'src')
10 files changed, 346 insertions, 73 deletions
diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index 17cef3ec1a1..e5868117da8 100755 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -153,13 +153,20 @@ void CreatureAI::EnterEvadeMode() me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE); } else + { + // Required to prevent attacking creatures that are evading and cause them to reenter combat + // Does not apply to MoveFollow + me->AddUnitState(UNIT_STATE_EVADE); me->GetMotionMaster()->MoveTargetedHome(); + } } Reset(); if (me->IsVehicle()) // use the same sequence of addtoworld, aireset may remove all summons! me->GetVehicleKit()->Reset(true); + + me->SetLastDamagedTime(0); } /*void CreatureAI::AttackedBy(Unit* attacker) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 3952f70af71..71976bb3bfd 100755 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -173,6 +173,7 @@ Unit::Unit(bool isWorldObject): WorldObject(isWorldObject) , m_vehicleKit(NULL) , m_unitTypeMask(UNIT_MASK_NONE) , m_HostileRefManager(this) + , _lastDamagedTime(0) { #ifdef _MSC_VER #pragma warning(default:4355) @@ -11190,6 +11191,10 @@ int32 Unit::ModifyHealth(int32 dVal) if (dVal == 0) return 0; + // Part of Evade mechanics. Only track health lost, not gained. + if (dVal < 0 && GetTypeId() != TYPEID_PLAYER && !isPet()) + SetLastDamagedTime(time(NULL)); + int32 curHealth = (int32)GetHealth(); int32 val = dVal + curHealth; @@ -12134,6 +12139,14 @@ Unit* Creature::SelectVictim() return target; } + // Case where mob is being kited. + // Mob may not be in range to attack or may have dropped target. In any case, + // don't evade if damage received within the last 10 seconds + // Does not apply to world bosses to prevent kiting to cities + if (!isWorldBoss() && !GetInstanceId()) + if (time(NULL) - GetLastDamagedTime() <= MAX_AGGRO_RESET_TIME) + return target; + // last case when creature must not go to evade mode: // it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list // for example at owner command to pet attack some far away creature diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 7d9088bfabb..73d4cea3ba4 100755 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -263,6 +263,8 @@ enum UnitRename #define MAX_SPELL_POSSESS 8 #define MAX_SPELL_CONTROL_BAR 10 +#define MAX_AGGRO_RESET_TIME 10 // in seconds + enum Swing { NOSWING = 0, @@ -2219,6 +2221,10 @@ class Unit : public WorldObject // Movement info Movement::MoveSpline * movespline; + // Part of Evade mechanics + time_t GetLastDamagedTime() const { return _lastDamagedTime; } + void SetLastDamagedTime(time_t val) { _lastDamagedTime = val; } + protected: explicit Unit (bool isWorldObject); @@ -2345,6 +2351,8 @@ class Unit : public WorldObject Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing bool _isWalkingBeforeCharm; // Are we walking before we were charmed? + + time_t _lastDamagedTime; // Part of Evade mechanics }; namespace Trinity diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 91041384200..57337d3c6a6 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -74,6 +74,9 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto, // Pet charge effects (Infernal Awakening, Demon Charge) if (spellproto->SpellVisual[0] == 2816 && spellproto->SpellIconID == 15) return DIMINISHING_CONTROLLED_STUN; + // Frost Tomb + else if (spellproto->Id == 48400) + return DIMINISHING_NONE; // Gnaw else if (spellproto->Id == 47481) return DIMINISHING_CONTROLLED_STUN; @@ -3004,6 +3007,16 @@ void SpellMgr::LoadDbcDataCorrections() switch (spellInfo->Id) { + case 42730: + spellInfo->EffectTriggerSpell[EFFECT_1] = 42739; + break; + case 59735: + spellInfo->EffectTriggerSpell[EFFECT_1] = 59736; + break; + case 52611: // Summon Skeletons + case 52612: // Summon Skeletons + spellInfo->EffectMiscValueB[0] = 64; + break; case 40244: // Simon Game Visual case 40245: // Simon Game Visual case 40246: // Simon Game Visual diff --git a/src/server/scripts/Northrend/Nexus/Oculus/boss_eregos.cpp b/src/server/scripts/Northrend/Nexus/Oculus/boss_eregos.cpp index e68e28be99b..76237fe1890 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/boss_eregos.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/boss_eregos.cpp @@ -160,7 +160,7 @@ public: _emeraldVoid = false; if (me->FindNearestCreature(NPC_AMBER_DRAKE_VEHICLE, 500.0f, true)) _amberVoid = false; - } + } uint32 GetData(uint32 type) { diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp index 48667053373..ef17d6c0721 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp @@ -56,13 +56,16 @@ enum Events EVENT_DARK_SMASH, EVENT_DREADFUL_ROAR, EVENT_WOE_STRIKE, - EVENT_SHADOW_AXE + EVENT_SHADOW_AXE, + EVENT_JUST_TRANSFORMED, + EVENT_SUMMON_BANSHEE }; enum Phases { PHASE_HUMAN = 1, PHASE_UNDEAD, + PHASE_EVENT }; enum Spells @@ -83,7 +86,7 @@ enum Spells SPELL_WOE_STRIKE = 42730, ENTRY_THROW_TARGET = 23996, - SPELL_SHADOW_AXE_SUMMON = 42749 + SPELL_SHADOW_AXE_SUMMON = 42748 }; class boss_ingvar_the_plunderer : public CreatureScript @@ -107,9 +110,6 @@ public: InstanceScript* instance; bool bIsUndead; - bool bEventInProgress; - - uint32 uiSpawnResTimer; void Reset() { @@ -117,7 +117,6 @@ public: me->UpdateEntry(MOB_INGVAR_HUMAN); bIsUndead = false; - bEventInProgress = false; me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); me->SetStandState(UNIT_STAND_STATE_STAND); @@ -130,13 +129,6 @@ public: events.ScheduleEvent(EVENT_ENRAGE, urand(7,14)*IN_MILLISECONDS, 0, PHASE_HUMAN); events.ScheduleEvent(EVENT_SMASH, urand(12,17)*IN_MILLISECONDS, 0, PHASE_HUMAN); - events.ScheduleEvent(EVENT_DARK_SMASH, urand(14,22)*IN_MILLISECONDS, 0, PHASE_UNDEAD); - events.ScheduleEvent(EVENT_DREADFUL_ROAR, urand(18,21)*IN_MILLISECONDS, 0, PHASE_UNDEAD); - events.ScheduleEvent(EVENT_WOE_STRIKE, urand(10,14)*IN_MILLISECONDS, 0, PHASE_UNDEAD); - events.ScheduleEvent(EVENT_SHADOW_AXE, 30*IN_MILLISECONDS, 0, PHASE_UNDEAD); - - uiSpawnResTimer = 3000; - if (instance) instance->SetData(DATA_INGVAR_EVENT, NOT_STARTED); } @@ -156,37 +148,34 @@ public: me->SetStandState(UNIT_STAND_STATE_DEAD); // visuel hack end - bEventInProgress = true; - bIsUndead = true; - events.SetPhase(PHASE_UNDEAD); + events.SetPhase(PHASE_EVENT); + events.ScheduleEvent(EVENT_SUMMON_BANSHEE, 3 * IN_MILLISECONDS, 0, PHASE_EVENT); DoScriptText(YELL_DEAD_1, me); } - if (bEventInProgress) - { + if (events.GetPhaseMask() & PHASE_EVENT) damage = 0; - } } void StartZombiePhase() { bIsUndead = true; - bEventInProgress = false; - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); me->UpdateEntry(MOB_INGVAR_UNDEAD); - me->SetInCombatWith(me->getVictim()); - me->GetMotionMaster()->MoveChase(me->getVictim()); + events.ScheduleEvent(EVENT_JUST_TRANSFORMED, 2 * IN_MILLISECONDS, 0, PHASE_EVENT); DoScriptText(YELL_AGGRO_2, me); } void EnterCombat(Unit* /*who*/) { - DoScriptText(YELL_AGGRO_1, me); + if (!bIsUndead) + DoScriptText(YELL_AGGRO_1, me); if (instance) instance->SetData(DATA_INGVAR_EVENT, IN_PROGRESS); + + me->SetInCombatWithZone(); } void JustDied(Unit* /*killer*/) @@ -201,6 +190,15 @@ public: } } + void ScheduleSecondPhase() + { + events.SetPhase(PHASE_UNDEAD); + events.ScheduleEvent(EVENT_DARK_SMASH, urand(14,18)*IN_MILLISECONDS, 0, PHASE_UNDEAD); + events.ScheduleEvent(EVENT_DREADFUL_ROAR, urand(18,22)*IN_MILLISECONDS, 0, PHASE_UNDEAD); + events.ScheduleEvent(EVENT_WOE_STRIKE, urand(10,14)*IN_MILLISECONDS, 0, PHASE_UNDEAD); + events.ScheduleEvent(EVENT_SHADOW_AXE, 30*IN_MILLISECONDS, 0, PHASE_UNDEAD); + } + void KilledUnit(Unit* /*victim*/) { if (bIsUndead) @@ -211,24 +209,9 @@ public: void UpdateAI(const uint32 diff) { - if (!UpdateVictim()) + if (!UpdateVictim() && !(events.GetPhaseMask() & PHASE_EVENT)) return; - if (bEventInProgress) - { - if (uiSpawnResTimer) - { - if (uiSpawnResTimer <= diff) - { - DoCast(me, SPELL_SUMMON_BANSHEE); // Summons directly on caster position - // DoCast(me, SPELL_SCOURG_RESURRECTION, true); // Not needed ? - uiSpawnResTimer = 0; - } else uiSpawnResTimer -= diff; - } - - return; - } - events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) @@ -245,7 +228,7 @@ public: break; case EVENT_STAGGERING_ROAR: DoCast(me, SPELL_STAGGERING_ROAR); - events.ScheduleEvent(EVENT_STAGGERING_ROAR, urand(18,21)*IN_MILLISECONDS, 0, PHASE_HUMAN); + events.ScheduleEvent(EVENT_STAGGERING_ROAR, urand(18,22)*IN_MILLISECONDS, 0, PHASE_HUMAN); break; case EVENT_ENRAGE: DoCast(me, SPELL_ENRAGE); @@ -253,16 +236,25 @@ public: break; case EVENT_SMASH: DoCastVictim(SPELL_SMASH); - events.ScheduleEvent(EVENT_SMASH, urand(12,17)*IN_MILLISECONDS, 0, PHASE_HUMAN); + events.ScheduleEvent(EVENT_SMASH, urand(12,16)*IN_MILLISECONDS, 0, PHASE_HUMAN); break; + case EVENT_JUST_TRANSFORMED: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->SetInCombatWithZone(); + me->GetMotionMaster()->MoveChase(me->getVictim()); + ScheduleSecondPhase(); + return; + case EVENT_SUMMON_BANSHEE: + DoCast(me, SPELL_SUMMON_BANSHEE); + return; // PHASE TWO case EVENT_DARK_SMASH: DoCastVictim(SPELL_DARK_SMASH); - events.ScheduleEvent(EVENT_DARK_SMASH, urand(14,22)*IN_MILLISECONDS, 0, PHASE_UNDEAD); + events.ScheduleEvent(EVENT_DARK_SMASH, urand(12,16)*IN_MILLISECONDS, 0, PHASE_UNDEAD); break; case EVENT_DREADFUL_ROAR: DoCast(me, SPELL_DREADFUL_ROAR); - events.ScheduleEvent(EVENT_DREADFUL_ROAR, urand(18,21)*IN_MILLISECONDS, 0, PHASE_UNDEAD); + events.ScheduleEvent(EVENT_DREADFUL_ROAR, urand(18,22)*IN_MILLISECONDS, 0, PHASE_UNDEAD); break; case EVENT_WOE_STRIKE: DoCastVictim(SPELL_WOE_STRIKE); @@ -271,8 +263,7 @@ public: case EVENT_SHADOW_AXE: if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 1)) { - me->SummonCreature(ENTRY_THROW_TARGET, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 2000); - DoCast(me, SPELL_SHADOW_AXE_SUMMON); + DoCast(target, SPELL_SHADOW_AXE_SUMMON); } events.ScheduleEvent(EVENT_SHADOW_AXE, 30*IN_MILLISECONDS, 0, PHASE_UNDEAD); break; @@ -430,32 +421,34 @@ public: { } - uint32 uiDespawnTimer; - void Reset() { - Unit* target = me->FindNearestCreature(ENTRY_THROW_TARGET, 50); - if (target) + if (Creature* target = me->FindNearestCreature(ENTRY_THROW_TARGET, 50.0f)) { - DoCast(me, SPELL_SHADOW_AXE_DAMAGE); float x, y, z; target->GetPosition(x, y, z); - me->GetMotionMaster()->MovePoint(0, x, y, z); + me->GetMotionMaster()->MoveCharge(x, y, z, 42.0f, 28); + target->DisappearAndDie(); + } + else + { + me->DisappearAndDie(); } - uiDespawnTimer = 7000; } - void AttackStart(Unit* /*who*/) {} - void MoveInLineOfSight(Unit* /*who*/) {} - void EnterCombat(Unit* /*who*/) {} - void UpdateAI(const uint32 diff) + void MovementInform(uint32 type, uint32 id) { - if (uiDespawnTimer <= diff) + if (type == POINT_MOTION_TYPE && id == 28) { - me->DealDamage(me, me->GetHealth()); - me->RemoveCorpse(); - uiDespawnTimer = 0; - } else uiDespawnTimer -= diff; + DoCast(me, SPELL_SHADOW_AXE_DAMAGE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + if (TempSummon* summon = me->ToTempSummon()) + { + summon->UnSummon(10000); + } + else + me->DisappearAndDie(); + } } }; }; diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp index b987b00fa5b..e718942d091 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp @@ -43,6 +43,9 @@ enum KelsethEncounter NPC_FROSTTOMB = 23965, NPC_SKELETON = 23970, + NPC_RUNEMAGE = 23960, + NPC_STRATEGIST = 23956, + SAY_START_COMBAT = 1, SAY_SUMMON_SKELETONS, SAY_FROST_TOMB, @@ -118,10 +121,7 @@ public: struct boss_kelesethAI : public BossAI { - boss_kelesethAI(Creature* creature) : BossAI(creature, DATA_PRINCEKELESETH_EVENT) - { - creature->SetReactState(REACT_DEFENSIVE); - } + boss_kelesethAI(Creature* creature) : BossAI(creature, DATA_PRINCEKELESETH_EVENT){} void Reset() { @@ -138,12 +138,37 @@ public: onTheRocks = true; } - void EnterCombat(Unit* /*who*/) + void EnterCombat(Unit* who) { me->SetInCombatWithZone(); if (instance) instance->SetData(DATA_PRINCEKELESETH_EVENT, IN_PROGRESS); Talk(SAY_START_COMBAT); + + if (!who) + return; + + std::list<Creature*> runemages; + me->GetCreatureListWithEntryInGrid(runemages, NPC_RUNEMAGE, 60.0f); + if (!runemages.empty()) + { + for (std::list<Creature*>::iterator itr = runemages.begin(); itr != runemages.end(); ++itr) + { + if ((*itr)->isAlive() && (*itr)->IsWithinLOSInMap(me)) + (*itr)->AI()->AttackStart(who); + } + } + + std::list<Creature*> strategists; + me->GetCreatureListWithEntryInGrid(strategists, NPC_STRATEGIST, 60.0f); + if (!strategists.empty()) + { + for (std::list<Creature*>::iterator itr = strategists.begin(); itr != strategists.end(); ++itr) + { + if ((*itr)->isAlive() && (*itr)->IsWithinLOSInMap(me)) + (*itr)->AI()->AttackStart(who); + } + } } void JustDied(Unit* /*killer*/) @@ -168,10 +193,20 @@ public: return 0; } - void ExecuteEvent(uint32 const eventId) + void UpdateAI(uint32 const diff) { - switch (eventId) + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { + switch (eventId) + { case EVENT_SUMMON_SKELETONS: Talk(SAY_SUMMON_SKELETONS); SummonSkeletons(); @@ -192,7 +227,10 @@ public: } events.ScheduleEvent(EVENT_FROST_TOMB, urand(14,19)*IN_MILLISECONDS); break; + } } + + DoMeleeAttackIfReady(); } void SummonSkeletons() @@ -225,7 +263,6 @@ public: events.Reset(); events.ScheduleEvent(EVENT_DECREPIFY, urand(4,6)*IN_MILLISECONDS); - DoCast(SPELL_BONE_ARMOR); } void DamageTaken(Unit* /*done_by*/, uint32 &damage) @@ -278,6 +315,13 @@ public: break; case EVENT_SHADOW_FISSURE: DoCast(me, SPELL_SHADOW_FISSURE, true); + if (TempSummon* temp = me->ToTempSummon()) + { + if (Unit* summoner = temp->GetSummoner()) + { + DoCast(summoner, SPELL_BONE_ARMOR); + } + } me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->RemoveFlag(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_DEAD); me->GetMotionMaster()->MoveChase(me->getVictim()); diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_skarvald_dalronn.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_skarvald_dalronn.cpp index be8d60fbeb9..7c977250e79 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_skarvald_dalronn.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_skarvald_dalronn.cpp @@ -47,6 +47,7 @@ enum eEnums SPELL_CHARGE = 43651, SPELL_STONE_STRIKE = 48583, SPELL_SUMMON_SKARVALD_GHOST = 48613, + SPELL_ENRAGE = 48193, MOB_SKARVALD_GHOST = 27390, //Spells of Dalronn and his Ghost MOB_DALRONN_THE_CONTROLLER = 24201, @@ -58,6 +59,20 @@ enum eEnums MOB_DALRONN_GHOST = 27389 }; +class SkarvaldChargePredicate +{ + public: + SkarvaldChargePredicate(Unit* unit) : me(unit) {} + + bool operator() (WorldObject* object) const + { + return object->GetDistance2d(me) >= 5.0f && object->GetDistance2d(me) <= 30.0f; + } + + private: + Unit* me; +}; + class boss_skarvald_the_constructor : public CreatureScript { public: @@ -83,6 +98,7 @@ public: uint32 Response_Timer; uint32 Check_Timer; bool Dalronn_isDead; + bool Enraged; void Reset() { @@ -90,6 +106,7 @@ public: StoneStrike_Timer = 10000; Dalronn_isDead = false; Check_Timer = 5000; + Enraged = false; ghost = (me->GetEntry() == MOB_SKARVALD_GHOST); if (!ghost && instance) @@ -116,6 +133,15 @@ public: } } + void DamageTaken(Unit* /*attacker*/, uint32& damage) + { + if (!Enraged && !ghost && me->HealthBelowPctDamaged(15, damage)) + { + Enraged = true; + DoCast(me, SPELL_ENRAGE); + } + } + void JustDied(Unit* killer) { if (!ghost && instance) @@ -194,7 +220,7 @@ public: if (Charge_Timer <= diff) { - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 1), SPELL_CHARGE); + DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, SkarvaldChargePredicate(me)), SPELL_CHARGE); Charge_Timer = 5000+rand()%5000; } else Charge_Timer -= diff; @@ -204,7 +230,8 @@ public: StoneStrike_Timer = 5000+rand()%5000; } else StoneStrike_Timer -= diff; - DoMeleeAttackIfReady(); + if (!me->HasUnitState(UNIT_STATE_CASTING)) + DoMeleeAttackIfReady(); } }; diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp index 2d53e7062eb..35bf7ee0b10 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp @@ -18,6 +18,8 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "utgarde_keep.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" uint32 entry_search[3] = { @@ -164,7 +166,82 @@ public: }; }; +enum TickingTimeBomb +{ + SPELL_TICKING_TIME_BOMB_EXPLODE = 59687 +}; +class spell_ticking_time_bomb : public SpellScriptLoader +{ + public: + spell_ticking_time_bomb() : SpellScriptLoader("spell_ticking_time_bomb") { } + + class spell_ticking_time_bomb_AuraScript : public AuraScript + { + PrepareAuraScript(spell_ticking_time_bomb_AuraScript); + + bool Validate(SpellInfo const* /*spellEntry*/) + { + return (bool) sSpellMgr->GetSpellInfo(SPELL_TICKING_TIME_BOMB_EXPLODE); + } + + void HandleOnEffectRemove(AuraEffect const* /* aurEff */, AuraEffectHandleModes /* mode */) + { + if (GetCaster() == GetTarget()) + { + GetTarget()->CastSpell(GetTarget(), SPELL_TICKING_TIME_BOMB_EXPLODE, true); + } + } + + void Register() + { + OnEffectRemove += AuraEffectRemoveFn(spell_ticking_time_bomb_AuraScript::HandleOnEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_ticking_time_bomb_AuraScript(); + } +}; + +enum Fixate +{ + SPELL_FIXATE_TRIGGER = 40415 +}; +class spell_fixate : public SpellScriptLoader +{ + public: + spell_fixate() : SpellScriptLoader("spell_fixate") { } + + class spell_fixate_SpellScript : public SpellScript + { + PrepareSpellScript(spell_fixate_SpellScript); + + bool Validate(SpellInfo const* /*spellEntry*/) + { + return (bool) sSpellMgr->GetSpellInfo(SPELL_FIXATE_TRIGGER); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + // The unit has to cast the taunt on hisself, but we need the original caster for SPELL_AURA_MOD_TAUNT + GetCaster()->CastSpell(GetCaster(), SPELL_FIXATE_TRIGGER, true, 0, 0, GetHitUnit()->GetGUID()); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_fixate_SpellScript::HandleScriptEffect, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_fixate_SpellScript(); + } +}; void AddSC_utgarde_keep() { new npc_dragonflayer_forge_master(); + new spell_ticking_time_bomb(); + new spell_fixate(); } diff --git a/src/server/scripts/Outland/shadowmoon_valley.cpp b/src/server/scripts/Outland/shadowmoon_valley.cpp index 078f8f5a4f0..c84df2ee3d4 100644 --- a/src/server/scripts/Outland/shadowmoon_valley.cpp +++ b/src/server/scripts/Outland/shadowmoon_valley.cpp @@ -1899,6 +1899,96 @@ class spell_unlocking_zuluheds_chains : public SpellScriptLoader } }; +enum ShadowMoonTuberEnum +{ + SPELL_WHISTLE = 36652, + SPELL_SHADOWMOON_TUBER = 36462, + + NPC_BOAR_ENTRY = 21195, + GO_SHADOWMOON_TUBER_MOUND = 184701, + + POINT_TUBER = 1, + TYPE_BOAR = 1, + DATA_BOAR = 1 +}; + +class npc_shadowmoon_tuber_node : public CreatureScript +{ +public: + npc_shadowmoon_tuber_node() : CreatureScript("npc_shadowmoon_tuber_node") {} + + struct npc_shadowmoon_tuber_nodeAI : public ScriptedAI + { + npc_shadowmoon_tuber_nodeAI(Creature* creature) : ScriptedAI(creature) {} + + void Reset() + { + tapped = false; + tuberGUID = 0; + resetTimer = 60000; + } + + void SetData(uint32 id, uint32 data) + { + if (id == TYPE_BOAR && data == DATA_BOAR) + { + // Spawn chest GO + DoCast(SPELL_SHADOWMOON_TUBER); + + // Despawn the tuber + if (GameObject* tuber = me->FindNearestGameObject(GO_SHADOWMOON_TUBER_MOUND, 5.0f)) + { + tuberGUID = tuber->GetGUID(); + // @Workaround: find how to properly despawn the GO + tuber->SetPhaseMask(2, true); + } + } + } + + void SpellHit(Unit* /*caster*/, const SpellInfo* spell) + { + if (!tapped && spell->Id == SPELL_WHISTLE) + { + if (Creature* boar = me->FindNearestCreature(NPC_BOAR_ENTRY, 30.0f)) + { + // Disable trigger and force nearest boar to walk to him + tapped = true; + boar->SetWalk(false); + boar->GetMotionMaster()->MovePoint(POINT_TUBER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); + } + } + } + + void UpdateAI(const uint32 diff) + { + if (tapped) + { + if (resetTimer <= diff) + { + // Respawn the tuber + if (tuberGUID) + if (GameObject* tuber = GameObject::GetGameObject(*me, tuberGUID)) + // @Workaround: find how to properly respawn the GO + tuber->SetPhaseMask(1, true); + + Reset(); + } + else + resetTimer -= diff; + } + } + private: + bool tapped; + uint64 tuberGUID; + uint32 resetTimer; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_shadowmoon_tuber_nodeAI(creature); + } +}; + void AddSC_shadowmoon_valley() { new mob_mature_netherwing_drake(); @@ -1917,4 +2007,5 @@ void AddSC_shadowmoon_valley() new mob_torloth_the_magnificent(); new npc_enraged_spirit(); new spell_unlocking_zuluheds_chains(); + new npc_shadowmoon_tuber_node(); } |