Scripts/Naxxramas: Maexxna cleanup:

- Improved Web Wrap target selection
- Added missing text
- Cleanup of web wrap positioning - two players will never be flung to the same position (25-man)
- Further improvement to web wrap spawn behavior - targetable web wrap now only spawns after the player has finished being flung to the respective position (blizzlike)
- Add additional web wrap positions (sniff)

(cherry picked from commit 5dd6163387)

# Conflicts:
#	src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp
This commit is contained in:
treeston
2015-10-09 18:38:54 +02:00
committed by MitchesD
parent f48469496e
commit d3ea8c5750
2 changed files with 116 additions and 50 deletions

View File

@@ -0,0 +1,6 @@
-- maexxna cleanup
DELETE FROM `creature_text` WHERE `entry`=15952;
INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`probability`,`BroadcastTextId`,`TextRange`,`comment`) VALUES
(15952,0,0,"Spiderlings appear on the web!",41,100,32305,3,"Maexxna EMOTE_SPIDERS"),
(15952,1,0,"%s spins her web into a cocoon!",41,100,32303,3,"Maexxna EMOTE_WEB_WRAP"),
(15952,2,0,"%s sprays strands of web everywhere!",41,100,32304,3,"Maexxna EMOTE_WEB_SPRAY");

View File

@@ -18,16 +18,26 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "PassiveAI.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum Spells
{
SPELL_WEB_WRAP = 28622,
SPELL_WEB_SPRAY = 29484,
SPELL_WEB_SPRAY_10 = 29484,
SPELL_WEB_SPRAY_25 = 54125,
SPELL_POISON_SHOCK = 28741,
SPELL_NECROTIC_POISON = 28776,
SPELL_FRENZY = 54123
};
#define SPELL_FRENZY_HELPER RAID_MODE(54123,54124)
enum Emotes
{
EMOTE_SPIDERS = 0,
EMOTE_WEB_WRAP = 1,
EMOTE_WEB_SPRAY = 2
};
enum Creatures
{
@@ -35,12 +45,16 @@ enum Creatures
NPC_SPIDERLING = 17055,
};
#define MAX_POS_WRAP 3
const Position PosWrap[MAX_POS_WRAP] =
#define MAX_WRAP_POSITION 7
const Position WrapPositions[MAX_WRAP_POSITION] =
{
{3546.796f, -3869.082f, 296.450f, 0.0f},
{3531.271f, -3847.424f, 299.450f, 0.0f},
{3497.067f, -3843.384f, 302.384f, 0.0f},
{3453.818f, -3854.651f, 308.7581f, 4.362833f},
{3535.042f, -3842.383f, 300.795f, 3.179324f},
{3538.399f, -3846.088f, 299.964f, 4.310297f},
{3548.464f, -3854.676f, 298.6075f, 4.546609f},
{3557.663f, -3870.123f, 297.5027f, 3.756433f},
{3560.546f, -3879.353f, 297.4843f, 2.508937f},
{3562.535f, -3892.507f, 298.532f, 6.022466f},
};
enum Events
@@ -51,7 +65,24 @@ enum Events
EVENT_POISON,
EVENT_WRAP,
EVENT_SUMMON,
EVENT_FRENZY,
};
const float WEB_WRAP_MOVE_SPEED = 20.0f;
struct WebTargetSelector : public std::unary_function<Unit*, bool>
{
WebTargetSelector(Unit* maexxna) : _maexxna(maexxna) {}
bool operator()(Unit const* target) const
{
if (_maexxna->GetVictim() == target) // never target tank
return false;
if (target->HasAura(SPELL_WEB_WRAP)) // never target targets that are already webbed
return false;
return true;
}
private:
const Unit* _maexxna;
};
class boss_maexxna : public CreatureScript
@@ -66,27 +97,22 @@ public:
struct boss_maexxnaAI : public BossAI
{
boss_maexxnaAI(Creature* creature) : BossAI(creature, BOSS_MAEXXNA)
{
Initialize();
}
void Initialize()
{
enraged = false;
}
bool enraged;
boss_maexxnaAI(Creature* creature) : BossAI(creature, BOSS_MAEXXNA) { }
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
Initialize();
events.ScheduleEvent(EVENT_WRAP, 20000);
events.ScheduleEvent(EVENT_SPRAY, 40000);
events.ScheduleEvent(EVENT_SHOCK, urand(5000, 10000));
events.ScheduleEvent(EVENT_POISON, urand(10000, 15000));
events.ScheduleEvent(EVENT_SUMMON, 30000);
events.ScheduleEvent(EVENT_WRAP, 20 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SPRAY, 40 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SHOCK, urandms(5, 10));
events.ScheduleEvent(EVENT_POISON, urandms(10, 15));
events.ScheduleEvent(EVENT_SUMMON, 30 * IN_MILLISECONDS);
}
void Reset() override
{
_Reset();
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_WEB_WRAP);
}
void UpdateAI(uint32 diff) override
@@ -94,10 +120,9 @@ public:
if (!UpdateVictim() || !CheckInRoom())
return;
if (!enraged && HealthBelowPct(30))
if (HealthBelowPct(30) && !me->HasAura(SPELL_FRENZY_HELPER))
{
enraged = true;
events.ScheduleEvent(EVENT_FRENZY, 0); // will be cast immediately
DoCast(SPELL_FRENZY);
}
events.Update(diff);
@@ -107,41 +132,49 @@ public:
switch (eventId)
{
case EVENT_WRAP:
/// @todo Add missing text
for (uint8 i = 0; i < RAID_MODE(1, 2); ++i)
{
std::list<Unit*> targets;
SelectTargetList(targets, WebTargetSelector(me), RAID_MODE(1, 2), SELECT_TARGET_RANDOM);
if (!targets.empty())
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0, true, -SPELL_WEB_WRAP))
Talk(EMOTE_WEB_WRAP);
int8 wrapPos = -1;
for (Unit* target : targets)
{
target->RemoveAura(SPELL_WEB_SPRAY);
uint8 pos = rand32() % MAX_POS_WRAP;
target->GetMotionMaster()->MoveJump(PosWrap[pos].GetPositionX(), PosWrap[pos].GetPositionY(), PosWrap[pos].GetPositionZ(), 20, 20);
if (Creature* wrap = DoSummon(NPC_WEB_WRAP, PosWrap[pos], 0, TEMPSUMMON_CORPSE_DESPAWN))
wrap->AI()->SetGUID(target->GetGUID());
if (wrapPos == -1) // allow all positions on the first target
wrapPos = urand(0, MAX_WRAP_POSITION - 1);
else // on subsequent iterations, only allow positions that are not equal to the previous one (this is sufficient since we should only have two targets at most, ever)
wrapPos = (wrapPos + urand(1, MAX_WRAP_POSITION - 1)) % MAX_WRAP_POSITION;
target->RemoveAura(RAID_MODE(SPELL_WEB_SPRAY_10, SPELL_WEB_SPRAY_25));
if (Creature* wrap = DoSummon(NPC_WEB_WRAP, WrapPositions[wrapPos], 70 * IN_MILLISECONDS, TEMPSUMMON_TIMED_DESPAWN))
{
wrap->AI()->SetGUID(target->GetGUID()); // handles application of debuff
target->GetMotionMaster()->MoveJump(WrapPositions[wrapPos], WEB_WRAP_MOVE_SPEED, WEB_WRAP_MOVE_SPEED); // move after stun to avoid stun cancelling move
}
}
}
events.ScheduleEvent(EVENT_WRAP, 40000);
break;
}
case EVENT_SPRAY:
DoCastAOE(SPELL_WEB_SPRAY);
Talk(EMOTE_WEB_SPRAY);
DoCastAOE(RAID_MODE(SPELL_WEB_SPRAY_10, SPELL_WEB_SPRAY_25));
events.ScheduleEvent(EVENT_SPRAY, 40000);
break;
case EVENT_SHOCK:
DoCastAOE(SPELL_POISON_SHOCK);
events.ScheduleEvent(EVENT_SHOCK, urand(10000, 20000));
events.ScheduleEvent(EVENT_SHOCK, urandms(10, 20));
break;
case EVENT_POISON:
DoCastVictim(SPELL_NECROTIC_POISON);
events.ScheduleEvent(EVENT_POISON, urand(10000, 20000));
break;
case EVENT_FRENZY:
DoCast(me, SPELL_FRENZY, true);
events.ScheduleEvent(EVENT_FRENZY, 600000);
events.ScheduleEvent(EVENT_POISON, urandms(10, 20));
break;
case EVENT_SUMMON:
/// @todo Add missing text
Talk(EMOTE_SPIDERS);
uint8 amount = urand(8, 10);
for (uint8 i = 0; i < amount; ++i)
DoSummon(NPC_SPIDERLING, me, 0, TEMPSUMMON_CORPSE_DESPAWN);
DoSummon(NPC_SPIDERLING, me, 4.0f, 5 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
events.ScheduleEvent(EVENT_SUMMON, 40000);
break;
}
@@ -165,23 +198,49 @@ public:
struct npc_webwrapAI : public NullCreatureAI
{
npc_webwrapAI(Creature* creature) : NullCreatureAI(creature) { }
npc_webwrapAI(Creature* creature) : NullCreatureAI(creature), visibleTimer(0) { }
ObjectGuid victimGUID;
uint32 visibleTimer;
void InitializeAI() override
{
me->SetVisible(false);
}
void SetGUID(ObjectGuid guid, int32 /*param*/) override
{
if (!guid)
return;
victimGUID = guid;
if (me->m_spells[0] && !victimGUID.IsEmpty())
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
victim->CastSpell(victim, me->m_spells[0], true, NULL, NULL, me->GetGUID());
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
{
visibleTimer = (me->GetDistance2d(victim)/WEB_WRAP_MOVE_SPEED + 0.5f) * IN_MILLISECONDS;
victim->CastSpell(victim, SPELL_WEB_WRAP, true, NULL, NULL, me->GetGUID());
}
}
void UpdateAI(uint32 diff) override
{
if (!visibleTimer)
return;
if (diff >= visibleTimer)
{
visibleTimer = 0;
me->SetVisible(true);
}
else
visibleTimer -= diff;
}
void JustDied(Unit* /*killer*/) override
{
if (me->m_spells[0] && !victimGUID.IsEmpty())
if (!victimGUID.IsEmpty())
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
victim->RemoveAurasDueToSpell(me->m_spells[0], me->GetGUID());
victim->RemoveAurasDueToSpell(SPELL_WEB_WRAP, me->GetGUID());
me->DespawnOrUnsummon(5 * IN_MILLISECONDS);
}
};
@@ -190,5 +249,6 @@ public:
void AddSC_boss_maexxna()
{
new boss_maexxna();
new npc_webwrap();
}