Zul Aman : les quatre vagues de trolls de Nalorakk

Ecrit d apres AzerothCore, qui tient la meilleure version de ce contenu BC :
le boss attend au pied de la rampe, jette sur les joueurs le groupe de trolls
pose autour de lui, ATTEND QUE LA VAGUE SOIT MORTE, puis recule d un palier en
lancant Make way for da Nalorakk!. Il n est attaquable qu apres la quatrieme.

Composition des vagues reprise telle quelle (lanceurs de hache et hommes de
tribu, puis avec le medecin, puis les porte-guerre, enfin porte-guerre et
medecin a 25 m) : nos spawns correspondent deja exactement a ces groupes le
long de la rampe. Les deplacements passent par des positions en dur, ce core
n ayant ni spawn groups ni chemins de waypoints.

Deux pieges evites : notre spawn est pose a la position FINALE du boss, il est
donc ramene au pied de la rampe au depart ; et l evenement ne se rejoue jamais
apres coup, avec une sortie de secours si un joueur arrive au contact -- une
immunite d introduction qui ne se leve pas rend un boss definitivement intuable.

La numerotation des textes est celle de NOTRE creature_text, verifiee ligne a
ligne : le groupe 10 est le cri de repli, pas le retour en forme de troll.
This commit is contained in:
BlaMacfly
2026-09-12 12:32:57 +02:00
parent da7d96f124
commit 4f21e52986
@@ -15,14 +15,23 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// Nalorakk n'avait qu'une coquille de script : il frappait en melee et rien d'autre, // Nalorakk n'avait qu'une coquille de script : ni sorts, ni formes, ni les quatre
// ni sorts ni changement de forme. Le combat est ici ecrit a partir du script amont // vagues de trolls de la rampe.
// TrinityCore, en gardant NOTRE numerotation de creature_text (0-11). //
// Ce qui n'est pas porte : les quatre vagues de trolls qui l'annoncent dans la rampe. // L'evenement suit AzerothCore, qui tient la meilleure version de ce contenu BC :
// L'amont les pilote par des spawn groups et des chemins de waypoints que ce core // le boss attend au pied de la rampe, lance sur les joueurs le groupe de trolls
// n'a pas (pas de table spawn_group) -- cela demanderait de recreer les donnees. // pose autour de lui, ATTEND QUE LA VAGUE SOIT MORTE, puis recule d'un palier en
// lancant « Make way for da Nalorakk! ». Il n'est attaquable qu'apres la quatrieme.
// Les deplacements passent par des positions en dur (reprises de l'ancien script
// TrinityCore) : ni spawn groups ni chemins de waypoints n'existent sur ce core.
//
// Numerotation des textes : celle de NOTRE creature_text (verifiee ligne a ligne),
// qui est l'ancienne numerotation BC et non celle des cores modernes.
#include "ScriptMgr.h" #include "ScriptMgr.h"
#include "Creature.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "ScriptedCreature.h" #include "ScriptedCreature.h"
#include "zulaman.h" #include "zulaman.h"
@@ -37,8 +46,8 @@ enum Says
SAY_SURGE = 6, SAY_SURGE = 6,
EMOTE_SURGE = 7, EMOTE_SURGE = 7,
EMOTE_BEAR = 8, EMOTE_BEAR = 8,
SAY_BEAR = 9, SAY_SHIFT_TO_BEAR = 9,
SAY_TROLL = 10, SAY_MAKE_WAY = 10, // « Make way for da Nalorakk! » : il recule d'un palier
SAY_DEATH = 11 SAY_DEATH = 11
}; };
@@ -47,7 +56,7 @@ enum Spells
// Forme troll // Forme troll
SPELL_BRUTAL_SWIPE = 42384, SPELL_BRUTAL_SWIPE = 42384,
SPELL_MANGLE = 42389, SPELL_MANGLE = 42389,
SPELL_SURGE = 44019, SPELL_SURGE = 42402,
// Forme d'ours // Forme d'ours
SPELL_LACERATING_SLASH = 42395, SPELL_LACERATING_SLASH = 42395,
@@ -73,6 +82,29 @@ enum Events
EVENT_BERSERK EVENT_BERSERK
}; };
enum WaveCreatures
{
NPC_AMANISHI_AXE_THROWER = 23542,
NPC_AMANISHI_WARBRINGER = 23580,
NPC_AMANISHI_MEDICINE_MAN = 23581,
NPC_AMANISHI_TRIBESMAN = 23582
};
// Le chemin de la rampe. Les paliers ou il s'arrete pour lancer une vague sont
// 0, 1, 5 et 8 ; les autres points ne sont que des etapes de trajet.
static float const NalorakkWay[9][3] =
{
{ 18.569f, 1414.512f, 11.42f }, // palier 1
{ -17.264f, 1419.551f, 12.62f }, // palier 2
{ -52.642f, 1419.357f, 27.31f },
{ -69.908f, 1419.721f, 27.31f },
{ -79.929f, 1395.958f, 27.31f },
{ -80.072f, 1374.555f, 40.87f }, // palier 3
{ -80.072f, 1314.398f, 40.87f },
{ -80.072f, 1295.775f, 48.60f },
{ -80.072f, 1362.000f, 40.87f } // palier 4
};
class boss_nalorakk : public CreatureScript class boss_nalorakk : public CreatureScript
{ {
public: public:
@@ -81,13 +113,193 @@ class boss_nalorakk : public CreatureScript
struct boss_nalorakkAI : public BossAI struct boss_nalorakkAI : public BossAI
{ {
boss_nalorakkAI(Creature* creature) : BossAI(creature, DATA_NALORAKK), _bearForm(false) { } boss_nalorakkAI(Creature* creature) : BossAI(creature, DATA_NALORAKK),
_bearForm(false), _waveEvent(true), _waveInProgress(false), _inMove(false),
_wave(0), _currentPoint(0), _targetPoint(0), _checkTimer(0) { }
void Reset() override void Reset() override
{ {
_Reset(); _Reset();
_bearForm = false; _bearForm = false;
me->RemoveAurasDueToSpell(SPELL_SHAPE_OF_THE_BEAR); me->RemoveAurasDueToSpell(SPELL_SHAPE_OF_THE_BEAR);
if (!_waveEvent)
return;
// Une fois l'evenement joue, il ne recommence pas : sans cela un simple
// repli rendrait le boss intouchable pour de bon.
_waveInProgress = false;
_inMove = false;
_wave = 0;
_currentPoint = 0;
_targetPoint = 0;
_checkTimer = 0;
_waveGuids.clear();
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
me->SetSpeedRate(MOVE_RUN, 2.0f);
me->SetWalk(false);
// Notre spawn est pose a la position finale du boss, en haut de la rampe :
// on le remet au pied, sinon l'evenement se deroulerait a l'envers.
me->NearTeleportTo(NalorakkWay[0][0], NalorakkWay[0][1], NalorakkWay[0][2], 3.14f);
me->SetHomePosition(NalorakkWay[0][0], NalorakkWay[0][1], NalorakkWay[0][2], 3.14f);
}
// Tant que les vagues durent, il ne repond pas aux provocations.
void AttackStart(Unit* who) override
{
if (!_waveEvent)
BossAI::AttackStart(who);
}
void GatherWave(uint32 entry, float radius)
{
std::list<Creature*> found;
me->GetCreatureListWithEntryInGrid(found, entry, radius);
for (Creature* creature : found)
if (creature->IsAlive())
_waveGuids.push_back(creature->GetGUID());
}
void LaunchWave(Unit* target)
{
_waveGuids.clear();
switch (_wave)
{
case 0:
GatherWave(NPC_AMANISHI_AXE_THROWER, 10.0f);
GatherWave(NPC_AMANISHI_TRIBESMAN, 10.0f);
break;
case 1:
GatherWave(NPC_AMANISHI_AXE_THROWER, 10.0f);
GatherWave(NPC_AMANISHI_TRIBESMAN, 10.0f);
GatherWave(NPC_AMANISHI_MEDICINE_MAN, 10.0f);
break;
case 2:
GatherWave(NPC_AMANISHI_WARBRINGER, 10.0f);
break;
case 3:
GatherWave(NPC_AMANISHI_WARBRINGER, 25.0f);
GatherWave(NPC_AMANISHI_MEDICINE_MAN, 25.0f);
break;
default:
break;
}
Talk(uint8(SAY_WAVE_1 + _wave));
for (ObjectGuid const& guid : _waveGuids)
if (Creature* member = ObjectAccessor::GetCreature(*me, guid))
{
member->SetNoCallAssistance(true);
member->AI()->AttackStart(target);
}
// La quatrieme vague est la derniere : il descend dans l'arene.
if (_wave == 3)
{
EndWaveEvent();
return;
}
_waveInProgress = true;
_checkTimer = 2000;
}
bool WaveIsDead() const
{
for (ObjectGuid const& guid : _waveGuids)
if (Creature* member = ObjectAccessor::GetCreature(*me, guid))
if (member->IsAlive())
return false;
return true;
}
// Fin de l'evenement : il redevient un boss ordinaire. Appele aussi en filet
// de securite si un joueur lui arrive au contact, parce qu'une immunite
// d'introduction qui ne se leve jamais rend un boss definitivement intuable.
void EndWaveEvent()
{
if (!_waveEvent)
return;
_waveEvent = false;
_waveInProgress = false;
_inMove = false;
_waveGuids.clear();
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
}
void MoveInLineOfSight(Unit* who) override
{
if (!_waveEvent)
{
BossAI::MoveInLineOfSight(who);
return;
}
if (who->GetTypeId() != TYPEID_PLAYER || !me->IsHostileTo(who) || !who->IsAlive())
return;
if (me->IsWithinDistInMap(who, 8.0f))
{
EndWaveEvent();
return;
}
if (_waveInProgress || _inMove)
return;
float const range = (_wave == 0 || _wave == 3) ? 50.0f : 40.0f;
if (me->IsWithinDistInMap(who, range))
LaunchWave(who);
}
void MoveToNextStop()
{
Talk(SAY_MAKE_WAY);
_waveInProgress = false;
_inMove = true;
++_wave;
switch (_wave)
{
case 1: _targetPoint = 1; break;
case 2: _targetPoint = 5; break;
case 3: _targetPoint = 8; break;
default: _targetPoint = _currentPoint; break;
}
AdvanceOnePoint();
}
void AdvanceOnePoint()
{
uint32 next = (_currentPoint < 7 && _targetPoint != 8) ? _currentPoint + 1
: (_targetPoint == 8 ? 8 : _targetPoint);
_currentPoint = next;
me->GetMotionMaster()->MovePoint(_currentPoint, NalorakkWay[_currentPoint][0], NalorakkWay[_currentPoint][1], NalorakkWay[_currentPoint][2]);
}
void MovementInform(uint32 type, uint32 id) override
{
if (!_waveEvent || type != POINT_MOTION_TYPE || !_inMove || _currentPoint != id)
return;
if (_currentPoint == _targetPoint)
{
_inMove = false;
me->SetFacingTo(float(M_PI) * 0.5f);
return;
}
AdvanceOnePoint();
} }
void EnterCombat(Unit* /*who*/) override void EnterCombat(Unit* /*who*/) override
@@ -114,16 +326,16 @@ class boss_nalorakk : public CreatureScript
void ScheduleTrollEvents() void ScheduleTrollEvents()
{ {
events.ScheduleEvent(EVENT_BRUTAL_SWIPE, urand(10000, 20000)); events.ScheduleEvent(EVENT_BRUTAL_SWIPE, urand(7000, 12000));
events.ScheduleEvent(EVENT_MANGLE, urand(15000, 20000)); events.ScheduleEvent(EVENT_MANGLE, urand(10000, 15000));
events.ScheduleEvent(EVENT_SURGE, urand(20000, 25000)); events.ScheduleEvent(EVENT_SURGE, urand(15000, 20000));
} }
void ScheduleBearEvents() void ScheduleBearEvents()
{ {
events.ScheduleEvent(EVENT_LACERATING_SLASH, urand(10000, 15000)); events.ScheduleEvent(EVENT_LACERATING_SLASH, 2000);
events.ScheduleEvent(EVENT_REND_FLESH, urand(12000, 18000)); events.ScheduleEvent(EVENT_REND_FLESH, 3000);
events.ScheduleEvent(EVENT_DEAFENING_ROAR, urand(20000, 25000)); events.ScheduleEvent(EVENT_DEAFENING_ROAR, urand(5000, 10000));
} }
void CancelFormEvents() void CancelFormEvents()
@@ -138,6 +350,20 @@ class boss_nalorakk : public CreatureScript
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
// Deroulement des vagues : il ne recule qu'une fois la vague abattue.
if (_waveInProgress)
{
if (_checkTimer <= diff)
{
if (WaveIsDead())
MoveToNextStop();
else
_checkTimer = 2000;
}
else
_checkTimer -= diff;
}
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
@@ -152,11 +378,11 @@ class boss_nalorakk : public CreatureScript
{ {
case EVENT_BRUTAL_SWIPE: case EVENT_BRUTAL_SWIPE:
DoCastVictim(SPELL_BRUTAL_SWIPE); DoCastVictim(SPELL_BRUTAL_SWIPE);
events.ScheduleEvent(EVENT_BRUTAL_SWIPE, urand(10000, 20000)); events.ScheduleEvent(EVENT_BRUTAL_SWIPE, urand(7000, 12000));
break; break;
case EVENT_MANGLE: case EVENT_MANGLE:
DoCastVictim(SPELL_MANGLE); DoCastVictim(SPELL_MANGLE);
events.ScheduleEvent(EVENT_MANGLE, urand(15000, 20000)); events.ScheduleEvent(EVENT_MANGLE, urand(10000, 15000));
break; break;
case EVENT_SURGE: case EVENT_SURGE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, NonTankTargetSelector(me))) if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, NonTankTargetSelector(me)))
@@ -165,19 +391,19 @@ class boss_nalorakk : public CreatureScript
Talk(EMOTE_SURGE, target); Talk(EMOTE_SURGE, target);
DoCast(target, SPELL_SURGE); DoCast(target, SPELL_SURGE);
} }
events.ScheduleEvent(EVENT_SURGE, urand(20000, 25000)); events.ScheduleEvent(EVENT_SURGE, urand(15000, 20000));
break; break;
case EVENT_LACERATING_SLASH: case EVENT_LACERATING_SLASH:
DoCastVictim(SPELL_LACERATING_SLASH); DoCastVictim(SPELL_LACERATING_SLASH);
events.ScheduleEvent(EVENT_LACERATING_SLASH, urand(10000, 15000)); events.ScheduleEvent(EVENT_LACERATING_SLASH, urand(18000, 22000));
break; break;
case EVENT_REND_FLESH: case EVENT_REND_FLESH:
DoCastVictim(SPELL_REND_FLESH); DoCastVictim(SPELL_REND_FLESH);
events.ScheduleEvent(EVENT_REND_FLESH, urand(12000, 18000)); events.ScheduleEvent(EVENT_REND_FLESH, urand(10000, 15000));
break; break;
case EVENT_DEAFENING_ROAR: case EVENT_DEAFENING_ROAR:
DoCast(me, SPELL_DEAFENING_ROAR); DoCast(me, SPELL_DEAFENING_ROAR);
events.ScheduleEvent(EVENT_DEAFENING_ROAR, urand(20000, 25000)); events.ScheduleEvent(EVENT_DEAFENING_ROAR, urand(10000, 15000));
break; break;
case EVENT_SHAPESHIFT: case EVENT_SHAPESHIFT:
CancelFormEvents(); CancelFormEvents();
@@ -185,18 +411,17 @@ class boss_nalorakk : public CreatureScript
if (_bearForm) if (_bearForm)
{ {
Talk(SAY_BEAR); Talk(SAY_SHIFT_TO_BEAR);
Talk(EMOTE_BEAR); Talk(EMOTE_BEAR);
DoCast(me, SPELL_SHAPE_OF_THE_BEAR); DoCast(me, SPELL_SHAPE_OF_THE_BEAR);
ScheduleBearEvents(); ScheduleBearEvents();
events.ScheduleEvent(EVENT_SHAPESHIFT, 45000); events.ScheduleEvent(EVENT_SHAPESHIFT, urand(20000, 25000));
} }
else else
{ {
Talk(SAY_TROLL);
me->RemoveAurasDueToSpell(SPELL_SHAPE_OF_THE_BEAR); me->RemoveAurasDueToSpell(SPELL_SHAPE_OF_THE_BEAR);
ScheduleTrollEvents(); ScheduleTrollEvents();
events.ScheduleEvent(EVENT_SHAPESHIFT, 45000); events.ScheduleEvent(EVENT_SHAPESHIFT, urand(45000, 50000));
} }
break; break;
case EVENT_BERSERK: case EVENT_BERSERK:
@@ -215,6 +440,14 @@ class boss_nalorakk : public CreatureScript
private: private:
bool _bearForm; bool _bearForm;
bool _waveEvent;
bool _waveInProgress;
bool _inMove;
uint32 _wave;
uint32 _currentPoint;
uint32 _targetPoint;
uint32 _checkTimer;
std::vector<ObjectGuid> _waveGuids;
}; };
CreatureAI* GetAI(Creature* creature) const override CreatureAI* GetAI(Creature* creature) const override