Scripts/Gundrak: Drakkari Colossus cleanup (#31275)

Drakkari Colossus implementation cleanup
This commit is contained in:
ccrs
2025-10-10 14:21:36 +02:00
committed by GitHub
parent 676c1398d6
commit bc68d013da
7 changed files with 227 additions and 155 deletions

View File

@@ -0,0 +1,26 @@
--
DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_drakkari_colossus_surge';
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(54801, 'spell_drakkari_colossus_surge');
DELETE FROM `spawn_group` WHERE (`groupId` = '33') and (`spawnType` = '0') and (`spawnId` = '127071');
DELETE FROM `spawn_group` WHERE (`groupId` = '33') and (`spawnType` = '0') and (`spawnId` = '127072');
DELETE FROM `spawn_group` WHERE (`groupId` = '33') and (`spawnType` = '0') and (`spawnId` = '127073');
DELETE FROM `spawn_group` WHERE (`groupId` = '33') and (`spawnType` = '0') and (`spawnId` = '127074');
DELETE FROM `spawn_group` WHERE (`groupId` = '33') and (`spawnType` = '0') and (`spawnId` = '127075');
DELETE FROM `spawn_group_template` WHERE `groupId`=328;
INSERT INTO `spawn_group_template` (`groupId`,`groupName`,`groupFlags`) VALUES
(328,'Gundrak - Drakkari Colossus',4);
DELETE FROM `spawn_group` WHERE `groupId`=328;
INSERT INTO `spawn_group` (`groupId`,`spawnType`,`spawnId`) VALUES
(328,0,127071),
(328,0,127072),
(328,0,127073),
(328,0,127074),
(328,0,127075);
DELETE FROM `instance_spawn_groups` WHERE `spawnGroupId` =328;
INSERT INTO `instance_spawn_groups` (`instanceMapId`,`bossStateId`,`bossStates`,`spawnGroupId`,`flags`) VALUES
(604,1,23,328,2);

View File

@@ -20,6 +20,7 @@
#include "Cell.h"
#include "CellImpl.h"
#include "Containers.h"
#include "CommonHelpers.h"
#include "DBCStores.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
@@ -489,6 +490,13 @@ void ScriptedAI::SetCombatMovement(bool allowMovement)
_isCombatMovementAllowed = allowMovement;
}
void ScriptedAI::SetAggressiveStateAfter(Milliseconds timer, Creature* who/* = nullptr*/, bool startCombat/* = true*/, Creature* summoner/* = nullptr*/)
{
if (!who)
who = me;
who->m_Events.AddEvent(new Trinity::Helpers::Events::SetAggresiveStateEvent(who, startCombat, summoner->GetGUID()), who->m_Events.CalculateTime(timer));
}
// BossAI - for instanced bosses
BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature), instance(creature->GetInstanceScript()), summons(creature), _bossId(bossId)
{

View File

@@ -246,6 +246,8 @@ struct TC_GAME_API ScriptedAI : public CreatureAI
// return true for 25 man or 25 man heroic mode
bool Is25ManRaid() const { return _difficulty & RAID_DIFFICULTY_MASK_25MAN; }
void SetAggressiveStateAfter(Milliseconds timer, Creature* who = nullptr, bool startCombat = true, Creature* summoner = nullptr);
template <class T>
inline T const& DUNGEON_MODE(T const& normal5, T const& heroic10) const
{

View File

@@ -17,10 +17,14 @@
#include "CommonHelpers.h"
#include "Common.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "Item.h"
#include "ItemTemplate.h"
#include "ObjectAccessor.h"
#include "Player.h"
#include "SharedDefines.h"
#include "UnitAI.h"
enum PlayerSpecializationIconicSpellIds
{
@@ -322,3 +326,26 @@ bool Trinity::Helpers::Entity::IsPlayerRangedAttacker(Player const* who)
return (Trinity::Helpers::Entity::GetPlayerSpecialization(who) == SPEC_DRUID_BALANCE);
}
}
Trinity::Helpers::Events::SetAggresiveStateEvent::SetAggresiveStateEvent(Creature* owner, bool startCombat/* = true*/, ObjectGuid summonerGUID/* = ObjectGuid::Empty*/) : _owner(owner), _startCombat(startCombat), _summonerGUID(summonerGUID)
{
}
bool Trinity::Helpers::Events::SetAggresiveStateEvent::Execute(uint64 /*time*/, uint32 /*diff*/)
{
_owner->SetReactState(REACT_AGGRESSIVE);
if (_startCombat)
{
if (Unit* currentVictim = _owner->SelectVictim())
{
if (_owner->IsAIEnabled())
_owner->AI()->AttackStart(currentVictim);
}
else if (!_summonerGUID.IsEmpty())
if (Creature* summoner = ObjectAccessor::GetCreature(*_owner, _summonerGUID))
if (summoner->IsEngaged() && summoner->IsAIEnabled() && _owner->IsAIEnabled())
if (Unit* target = summoner->AI()->SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true))
_owner->AI()->AttackStart(target);
}
return true;
}

View File

@@ -18,8 +18,11 @@
#ifndef TRINITY_COMMONHELPERS_H
#define TRINITY_COMMONHELPERS_H
#include "EventProcessor.h"
#include "Define.h"
#include "ObjectGuid.h"
class Creature;
class Player;
namespace Trinity
@@ -33,6 +36,21 @@ namespace Trinity
TC_GAME_API bool IsPlayerHealer(Player const* who);
bool IsPlayerRangedAttacker(Player const* who);
}
namespace Events
{
class SetAggresiveStateEvent : public BasicEvent
{
public:
SetAggresiveStateEvent(Creature* owner, bool startCombat = true, ObjectGuid summonerGUID = ObjectGuid::Empty);
bool Execute(uint64 /*time*/, uint32 /*diff*/) override;
private:
Creature* _owner;
bool const _startCombat;
ObjectGuid const _summonerGUID;
};
}
}
}

View File

@@ -19,21 +19,24 @@
* Comment: The event with the Living Mojos is not implemented, just is done that when one of the mojos around the boss take damage will make the boss enter in combat!
*/
#include "ScriptMgr.h"
#include "gundrak.h"
#include "InstanceScript.h"
#include "Map.h"
#include "MotionMaster.h"
#include "ObjectMgr.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "SpellInfo.h"
#include "SpellScript.h"
enum Texts
enum DrakkariColossusTexts
{
// Drakkari Elemental
EMOTE_MOJO = 0,
EMOTE_ACTIVATE_ALTAR = 1
};
enum Spells
enum DrakkariColossusSpells
{
SPELL_EMERGE = 54850,
SPELL_ELEMENTAL_SPAWN_EFFECT = 54888,
@@ -47,108 +50,85 @@ enum Spells
SPELL_MOJO_WAVE = 55626,
};
enum ColossusEvents
enum DrakkariColossusEvents
{
EVENT_MIGHTY_BLOW = 1,
EVENT_SURGE,
EVENT_SURGE_FINISH
};
enum ColossusActions
enum DrakkariColossusActions
{
ACTION_SUMMON_ELEMENTAL = 1,
ACTION_FREEZE_COLOSSUS = 2,
ACTION_UNFREEZE_COLOSSUS = 3,
ACTION_UNFREEZE_COLOSSUS = 1,
};
enum ColossusPhases
enum DrakkariColossusPhases
{
COLOSSUS_PHASE_NORMAL = 1,
COLOSSUS_PHASE_FIRST_ELEMENTAL_SUMMON = 2,
COLOSSUS_PHASE_SECOND_ELEMENTAL_SUMMON = 3
COLOSSUS_PHASE_NORMAL,
COLOSSUS_PHASE_FIRST_ELEMENTAL_SUMMON,
COLOSSUS_PHASE_SECOND_ELEMENTAL_SUMMON
};
enum ColossusData
enum DrakkariColossusData
{
DATA_COLOSSUS_PHASE = 1,
DATA_INTRO_DONE = 2
DATA_COLOSSUS_PHASE = 1
};
enum ElementalActions
enum DrakkariColossusPoints
{
ACTION_RETURN_TO_COLOSSUS = 1
POINT_COLOSSUS = 1
};
enum ElementalEvents
enum DrakkariColossusSpawnGroups
{
EVENT_SURGE = 1
SPAWN_GROUP_MOJO = 328
};
struct boss_drakkari_colossus : public BossAI
{
boss_drakkari_colossus(Creature* creature) : BossAI(creature, DATA_DRAKKARI_COLOSSUS)
{
Initialize();
me->SetReactState(REACT_PASSIVE);
introDone = false;
}
void Initialize()
{
phase = COLOSSUS_PHASE_NORMAL;
}
void Reset() override
{
_Reset();
if (GetData(DATA_INTRO_DONE))
{
me->SetReactState(REACT_AGGRESSIVE);
me->SetImmuneToPC(false);
me->RemoveAura(SPELL_FREEZE_ANIM);
}
instance->instance->SpawnGroupSpawn(SPAWN_GROUP_MOJO, true);
me->SetReactState(REACT_PASSIVE);
}
events.ScheduleEvent(EVENT_MIGHTY_BLOW, 10s, 30s);
Initialize();
void EnterEvadeMode(EvadeReason /*why*/) override
{
summons.DespawnAll();
_DespawnAtEvade();
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->RemoveAura(SPELL_FREEZE_ANIM);
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
_JustEngagedWith(who);
events.ScheduleEvent(EVENT_MIGHTY_BLOW, 10s, 30s);
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_SUMMON_ELEMENTAL:
DoCast(SPELL_EMERGE);
break;
case ACTION_FREEZE_COLOSSUS:
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
me->SetReactState(REACT_PASSIVE);
me->SetImmuneToPC(true);
DoCast(me, SPELL_FREEZE_ANIM);
break;
case ACTION_UNFREEZE_COLOSSUS:
if (me->GetReactState() == REACT_AGGRESSIVE)
return;
me->SetImmuneToPC(false);
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveAura(SPELL_FREEZE_ANIM);
DoZoneInCombat();
events.RescheduleEvent(EVENT_MIGHTY_BLOW, 10s, 30s);
if (me->IsEngaged())
{
if (Unit* victim = me->SelectVictim())
AttackStart(victim);
}
else
DoZoneInCombat();
break;
}
}
@@ -156,17 +136,26 @@ struct boss_drakkari_colossus : public BossAI
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
if (me->IsImmuneToPC())
damage = 0;
if (phase == COLOSSUS_PHASE_NORMAL ||
phase == COLOSSUS_PHASE_FIRST_ELEMENTAL_SUMMON)
{
if (HealthBelowPct(phase == COLOSSUS_PHASE_NORMAL ? 50 : 5))
damage = 0;
return;
}
if (!events.IsInPhase(COLOSSUS_PHASE_SECOND_ELEMENTAL_SUMMON))
{
if (me->HealthBelowPctDamaged(events.IsInPhase(COLOSSUS_PHASE_FIRST_ELEMENTAL_SUMMON) ? 5 : 50, damage))
{
damage = 0;
phase = (phase == COLOSSUS_PHASE_NORMAL ? COLOSSUS_PHASE_FIRST_ELEMENTAL_SUMMON : COLOSSUS_PHASE_SECOND_ELEMENTAL_SUMMON);
DoAction(ACTION_FREEZE_COLOSSUS);
DoAction(ACTION_SUMMON_ELEMENTAL);
events.SetPhase((events.IsInPhase(COLOSSUS_PHASE_FIRST_ELEMENTAL_SUMMON) ? COLOSSUS_PHASE_SECOND_ELEMENTAL_SUMMON : COLOSSUS_PHASE_FIRST_ELEMENTAL_SUMMON));
// Freeze Colossus
me->SetReactState(REACT_PASSIVE);
DoStopAttack();
me->DoNotReacquireSpellFocusTarget();
me->SetImmuneToPC(true);
me->GetMotionMaster()->Clear(MOTION_PRIORITY_NORMAL);
DoCastSelf(SPELL_FREEZE_ANIM);
DoCast(SPELL_EMERGE);
}
}
}
@@ -174,19 +163,11 @@ struct boss_drakkari_colossus : public BossAI
uint32 GetData(uint32 data) const override
{
if (data == DATA_COLOSSUS_PHASE)
return phase;
else if (data == DATA_INTRO_DONE)
return introDone;
return events.GetPhaseMask();
return 0;
}
void SetData(uint32 type, uint32 data) override
{
if (type == DATA_INTRO_DONE)
introDone = data != 0;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
@@ -197,7 +178,7 @@ struct boss_drakkari_colossus : public BossAI
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
if (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
@@ -211,37 +192,42 @@ struct boss_drakkari_colossus : public BossAI
return;
}
if (me->GetReactState() == REACT_AGGRESSIVE)
DoMeleeAttackIfReady();
DoMeleeAttackIfReady();
}
void JustSummoned(Creature* summon) override
{
DoZoneInCombat(summon);
BossAI::JustSummoned(summon);
if (phase == COLOSSUS_PHASE_SECOND_ELEMENTAL_SUMMON)
if (events.IsInPhase(COLOSSUS_PHASE_SECOND_ELEMENTAL_SUMMON))
summon->SetHealth(summon->GetMaxHealth() / 2);
}
private:
uint8 phase;
bool introDone;
};
struct boss_drakkari_elemental : public ScriptedAI
{
boss_drakkari_elemental(Creature* creature) : ScriptedAI(creature)
{
DoCast(me, SPELL_ELEMENTAL_SPAWN_EFFECT);
instance = creature->GetInstanceScript();
}
void InitializeAI() override
{
me->SetReactState(REACT_PASSIVE);
ScriptedAI::InitializeAI();
DoCastSelf(SPELL_ELEMENTAL_SPAWN_EFFECT);
SetAggressiveStateAfter(2s);
}
void Reset() override
{
events.Reset();
events.ScheduleEvent(EVENT_SURGE, 5s, 15s);
}
void JustEngagedWith(Unit* /*who*/) override
{
me->AddAura(SPELL_MOJO_VOLLEY, me);
events.ScheduleEvent(EVENT_SURGE, 5s, 15s);
}
void JustDied(Unit* killer) override
@@ -262,16 +248,24 @@ struct boss_drakkari_elemental : public ScriptedAI
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
if (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_SURGE:
DoCast(SPELL_SURGE_VISUAL);
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 0.0f, true))
DoCast(target, SPELL_SURGE);
{
DoStopAttack();
if (DoCast(target, SPELL_SURGE) == SpellCastResult::SPELL_CAST_OK)
events.ScheduleEvent(EVENT_SURGE_FINISH, 5s);
else
events.ScheduleEvent(EVENT_SURGE_FINISH, 0s);
}
events.ScheduleEvent(EVENT_SURGE, 5s, 15s);
break;
case EVENT_SURGE_FINISH:
if (Unit* victim = me->SelectVictim())
AttackStart(victim);
}
if (me->HasUnitState(UNIT_STATE_CASTING))
@@ -281,23 +275,9 @@ struct boss_drakkari_elemental : public ScriptedAI
DoMeleeAttackIfReady();
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_RETURN_TO_COLOSSUS:
Talk(EMOTE_MOJO);
DoCast(SPELL_SURGE_VISUAL);
if (Creature* colossus = instance->GetCreature(DATA_DRAKKARI_COLOSSUS))
// what if the elemental is more than 80 yards from drakkari colossus ?
DoCast(colossus, SPELL_MERGE, true);
break;
}
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
if (HealthBelowPct(50))
if (me->HealthBelowPctDamaged(50, damage))
{
if (Creature* colossus = instance->GetCreature(DATA_DRAKKARI_COLOSSUS))
{
@@ -319,7 +299,15 @@ struct boss_drakkari_elemental : public ScriptedAI
me->GetMotionMaster()->MovePoint(0, colossus->GetPositionX(), colossus->GetPositionY(), colossus->GetPositionZ());
return;
}*/
DoAction(ACTION_RETURN_TO_COLOSSUS);
Talk(EMOTE_MOJO);
me->SetReactState(REACT_PASSIVE);
me->GetMotionMaster()->Clear(MOTION_PRIORITY_NORMAL);
DoStopAttack();
me->DoNotReacquireSpellFocusTarget();
DoCast(SPELL_SURGE_VISUAL);
if (Creature* colossus = instance->GetCreature(DATA_DRAKKARI_COLOSSUS))
// what if the elemental is more than 80 yards from drakkari colossus ?
DoCast(colossus, SPELL_MERGE, true);
}
}
}
@@ -342,6 +330,12 @@ struct boss_drakkari_elemental : public ScriptedAI
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type == POINT_MOTION_TYPE && id == EVENT_CHARGE && events.HasEventScheduled(EVENT_SURGE_FINISH))
events.RescheduleEvent(EVENT_SURGE_FINISH, 0s);
}
private:
EventMap events;
InstanceScript* instance;
@@ -351,7 +345,10 @@ struct npc_living_mojo : public ScriptedAI
{
npc_living_mojo(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
_instance = creature->GetInstanceScript();
if (SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(SpawnObjectType::SPAWN_TYPE_CREATURE, me->GetSpawnId()))
if (data->spawnGroupData && data->spawnGroupData->groupId == SPAWN_GROUP_MOJO)
me->SetReactState(REACT_PASSIVE);
}
void Reset() override
@@ -361,6 +358,20 @@ struct npc_living_mojo : public ScriptedAI
void JustEngagedWith(Unit* /*who*/) override
{
if (me->HasReactState(REACT_PASSIVE))
{
if (Creature* colossus = _instance->GetCreature(DATA_DRAKKARI_COLOSSUS))
{
for (auto& pair : sObjectMgr->GetSpawnMetadataForGroup(SPAWN_GROUP_MOJO))
if (Creature* mojo = _instance->instance->GetCreatureBySpawnId(pair.second->spawnId))
if (mojo->GetMotionMaster()->GetCurrentMovementGeneratorType() == MovementGeneratorType::IDLE_MOTION_TYPE)
mojo->GetMotionMaster()->MovePoint(POINT_COLOSSUS, colossus->GetHomePosition());
return;
}
else
me->SetReactState(REACT_AGGRESSIVE);
}
_scheduler.Schedule(2s, [this](TaskContext task)
{
DoCastVictim(SPELL_MOJO_WAVE);
@@ -373,57 +384,14 @@ struct npc_living_mojo : public ScriptedAI
});
}
void MoveMojos(Creature* boss)
{
std::list<Creature*> mojosList;
boss->GetCreatureListWithEntryInGrid(mojosList, me->GetEntry(), 12.0f);
if (!mojosList.empty())
{
for (std::list<Creature*>::const_iterator itr = mojosList.begin(); itr != mojosList.end(); ++itr)
{
if (Creature* mojo = *itr)
mojo->GetMotionMaster()->MovePoint(1, boss->GetHomePosition().GetPositionX(), boss->GetHomePosition().GetPositionY(), boss->GetHomePosition().GetPositionZ());
}
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
if (id == 1)
if (type == POINT_MOTION_TYPE && id == POINT_COLOSSUS)
{
if (Creature* colossus = instance->GetCreature(DATA_DRAKKARI_COLOSSUS))
{
if (Creature* colossus = _instance->GetCreature(DATA_DRAKKARI_COLOSSUS))
colossus->AI()->DoAction(ACTION_UNFREEZE_COLOSSUS);
if (!colossus->AI()->GetData(DATA_INTRO_DONE))
colossus->AI()->SetData(DATA_INTRO_DONE, true);
DoZoneInCombat(colossus);
me->DespawnOrUnsummon();
}
}
}
void AttackStart(Unit* attacker) override
{
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
return;
// we do this checks to see if the creature is one of the creatures that sorround the boss
if (Creature* colossus = instance->GetCreature(DATA_DRAKKARI_COLOSSUS))
{
Position homePosition = me->GetHomePosition();
float distance = homePosition.GetExactDist(&colossus->GetHomePosition());
if (distance < 12.0f)
{
MoveMojos(colossus);
me->SetReactState(REACT_PASSIVE);
}
else
ScriptedAI::AttackStart(attacker);
me->DespawnOrUnsummon();
}
}
@@ -440,13 +408,35 @@ struct npc_living_mojo : public ScriptedAI
}
private:
InstanceScript* instance;
InstanceScript* _instance;
TaskScheduler _scheduler;
};
// 54801 - Surge
class spell_drakkari_colossus_surge : public SpellScript
{
PrepareSpellScript(spell_drakkari_colossus_surge);
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_SURGE_VISUAL });
}
void HandleVisual(SpellEffIndex /*effIndex*/)
{
GetCaster()->CastSpell(GetCaster(), SPELL_SURGE_VISUAL, true);
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_drakkari_colossus_surge::HandleVisual, EFFECT_1, SPELL_EFFECT_CHARGE_DEST);
}
};
void AddSC_boss_drakkari_colossus()
{
RegisterGundrakCreatureAI(boss_drakkari_colossus);
RegisterGundrakCreatureAI(boss_drakkari_elemental);
RegisterGundrakCreatureAI(npc_living_mojo);
RegisterSpellScript(spell_drakkari_colossus_surge);
}

View File

@@ -60,7 +60,8 @@ enum GDCreatureIds
NPC_RUIN_DWELLER = 29920,
NPC_ECK_THE_FEROCIOUS = 29932,
NPC_ALTAR_TRIGGER = 30298,
NPC_RHINO_SPIRIT = 29791
NPC_RHINO_SPIRIT = 29791,
NPC_LIVING_MOJO = 29830
};
enum GDGameObjectIds