fix(Core/Scripts): refactor Violet Hold to use DoAction and move gossip to DB (#25456)

Co-authored-by: Tartalo <none@none>
Co-authored-by: profPlum <dwyerfire@gmail.com>
Co-authored-by: Nay <dnpd.dd@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Andrew 2026-04-14 04:40:26 -03:00 committed by GitHub
parent db2e1325d2
commit 51208baf4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 103 additions and 129 deletions

View file

@ -0,0 +1,41 @@
-- Move npc_vh_sinclari (30658) and go_vh_activation_crystal (193611) from C++ to DB
UPDATE `creature_template` SET `gossip_menu_id` = 9997, `AIName` = 'SmartAI', `ScriptName` = '' WHERE `entry` = 30658;
-- (9997, 13853) already exists for NOT_STARTED
DELETE FROM `gossip_menu` WHERE `MenuID` = 9997 AND `TextID` IN (14271, 13910);
INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES
(9997, 14271),
(9997, 13910);
DELETE FROM `gossip_menu_option` WHERE `MenuID` = 9997 AND `OptionID` = 1;
INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`)
VALUES (9997, 1, 0, 'I''m not fighting, so send me in now!', 33204, 1, 1, 0, 0, 0, 0, '', 0, 0);
-- Gossip menu text conditions (CONDITION_INSTANCE_INFO on DATA_ENCOUNTER_STATUS)
DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 14 AND `SourceGroup` = 9997;
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(14, 9997, 13853, 0, 0, 13, 0, 30, 0, 0, 0, 0, 0, '', 'Sinclari - Show text 13853 when encounter NOT_STARTED'),
(14, 9997, 14271, 0, 0, 13, 0, 30, 1, 0, 0, 0, 0, '', 'Sinclari - Show text 14271 when encounter IN_PROGRESS'),
(14, 9997, 13910, 0, 0, 13, 0, 30, 2, 0, 0, 0, 0, '', 'Sinclari - Show text 13910 when encounter DONE');
-- Gossip option conditions
DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 15 AND `SourceGroup` = 9997;
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 9997, 0, 0, 0, 13, 0, 30, 0, 0, 0, 0, 0, '', 'Sinclari - Show option 0 (start) only when NOT_STARTED'),
(15, 9997, 1, 0, 0, 13, 0, 30, 1, 0, 0, 0, 0, '', 'Sinclari - Show option 1 (late join) only when IN_PROGRESS');
DELETE FROM `smart_scripts` WHERE `entryorguid` = 30658 AND `source_type` = 0;
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(30658, 0, 0, 0, 62, 0, 100, 0, 9998, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sinclari - On Gossip Select (Start) - Close Gossip'),
(30658, 0, 1, 0, 62, 0, 100, 0, 9998, 0, 0, 0, 0, 0, 223, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sinclari - On Gossip Select (Start) - DoAction ACTION_START_INSTANCE on Instance'),
(30658, 0, 2, 0, 62, 0, 100, 0, 9997, 1, 0, 0, 0, 0, 62, 608, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 1830.531006, 803.939758, 44.340508, 6.281611, 'Sinclari - On Gossip Select (Late Join) - Teleport Player'),
(30658, 0, 3, 0, 62, 0, 100, 0, 9997, 1, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sinclari - On Gossip Select (Late Join) - Close Gossip');
-- Activation Crystal: invoker casts spell 57804 → SPELL_EFFECT_SEND_EVENT(20001) → ProcessEvent
UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI', `ScriptName` = '' WHERE `entry` = 193611;
DELETE FROM `smart_scripts` WHERE `entryorguid` = 193611 AND `source_type` = 1;
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(193611, 1, 0, 0, 64, 0, 100, 0, 0, 0, 0, 0, 0, 0, 134, 57804, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Activation Crystal - On Gossip Hello - Invoker Cast Crystal Activation'),
(193611, 1, 1, 0, 64, 0, 100, 0, 0, 0, 0, 0, 0, 0, 105, 16, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Activation Crystal - On Gossip Hello - Add GO_FLAG_NOT_SELECTABLE');

View file

@ -189,19 +189,20 @@ public:
return true;
}
void SetData(uint32 type, uint32 data) override
void ProcessEvent(WorldObject* /*obj*/, uint32 eventId) override
{
switch (type)
if (eventId == EVENT_ACTIVATE_CRYSTAL)
{
case DATA_ACTIVATE_DEFENSE_SYSTEM:
{
if (data)
_defensesUsed = true;
Position const pos = {1919.09546f, 812.29724f, 86.2905f, M_PI};
instance->SummonCreature(NPC_DEFENSE_SYSTEM, pos, 0, 6499);
}
break;
case DATA_START_INSTANCE:
_defensesUsed = true;
SummonDefenseSystem();
}
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_START_INSTANCE:
if (_encounterStatus == NOT_STARTED)
{
_encounterStatus = IN_PROGRESS;
@ -214,13 +215,10 @@ public:
_events.RescheduleEvent(EVENT_CHECK_PLAYERS, 5s);
}
break;
case DATA_PORTAL_DEFEATED:
case ACTION_PORTAL_DEFEATED:
_events.RescheduleEvent(EVENT_SUMMON_PORTAL, 3s);
break;
case DATA_PORTAL_LOCATION:
_portalLocation = data;
break;
case DATA_DECREASE_DOOR_HEALTH:
case ACTION_DECREASE_DOOR_HEALTH:
if (_gateHealth > 0)
--_gateHealth;
if (_gateHealth == 0)
@ -230,12 +228,22 @@ public:
}
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)_gateHealth);
break;
case DATA_RELEASE_BOSS:
case ACTION_RELEASE_BOSS:
if (_waveCount == 6)
StartBossEncounter(GetPersistentData(PERSISTENT_DATA_FIRST_BOSS));
else
StartBossEncounter(GetPersistentData(PERSISTENT_DATA_SECOND_BOSS));
break;
}
}
void SetData(uint32 type, uint32 data) override
{
switch (type)
{
case DATA_PORTAL_LOCATION:
_portalLocation = data;
break;
case DATA_ACHIEV:
_achievementCompleted = !!data;
break;
@ -370,14 +378,14 @@ public:
sinclari->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
sinclari->GetMotionMaster()->MovePoint(0, sinclariOutsidePosition);
}
SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 0);
SummonDefenseSystem();
_events.RescheduleEvent(EVENT_START_ENCOUNTER, 4s);
break;
case EVENT_START_ENCOUNTER:
if (Creature* sinclari = GetCreature(DATA_SINCLARI))
{
sinclari->AI()->Talk(SAY_SINCLARI_DOOR_LOCK);
sinclari->SetVisible(false);
sinclari->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
}
if (Creature* doorSeal = GetCreature(DATA_DOOR_SEAL))
doorSeal->RemoveAllAuras();
@ -583,6 +591,12 @@ public:
return false;
}
void SummonDefenseSystem()
{
Position const pos = {1919.09546f, 812.29724f, 86.2905f, M_PI};
instance->SummonCreature(NPC_DEFENSE_SYSTEM, pos, 0, 6499);
}
private:
bool _cleaned{ false };
uint8 _encounterStatus{ NOT_STARTED };

View file

@ -17,102 +17,14 @@
#include "violet_hold.h"
#include "CreatureScript.h"
#include "GameObjectScript.h"
#include "PassiveAI.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
/// @todo: Missing Sinclari Trigger announcements (32204) Look at its creature_text for more info.
enum Texts
{
GOSSIP_MENU_START_1 = 9997,
GOSSIP_MENU_START_2 = 9998,
GOSSIP_MENU_LATE_JOIN = 10275,
NPC_TEXT_SINCLARI_IN = 13853,
NPC_TEXT_SINCLARI_START = 13854,
NPC_TEXT_SINCLARI_DONE = 13910,
NPC_TEXT_SINCLARI_LATE_JOIN = 14271,
};
/***********
** DEFENSE SYSTEM CRYSTAL
***********/
class go_vh_activation_crystal : public GameObjectScript
{
public:
go_vh_activation_crystal() : GameObjectScript("go_vh_activation_crystal") { }
bool OnGossipHello(Player* /*player*/, GameObject* go) override
{
if (InstanceScript* Instance = go->GetInstanceScript())
{
Instance->SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 1);
go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
}
return true;
}
};
/***********
** SINCLARI
***********/
class npc_vh_sinclari : public CreatureScript
{
public:
npc_vh_sinclari() : CreatureScript("npc_vh_sinclari") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
if (InstanceScript* Instance = creature->GetInstanceScript())
switch (Instance->GetData(DATA_ENCOUNTER_STATUS))
{
case NOT_STARTED:
AddGossipItemFor(player, GOSSIP_MENU_START_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_IN, creature->GetGUID());
break;
case IN_PROGRESS:
AddGossipItemFor(player, GOSSIP_MENU_LATE_JOIN, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_LATE_JOIN, creature->GetGUID());
break;
default:
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_DONE, creature->GetGUID());
}
return true;
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
switch (action)
{
case GOSSIP_ACTION_INFO_DEF+1:
AddGossipItemFor(player, GOSSIP_MENU_START_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_START, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF+2:
CloseGossipMenuFor(player);
if (InstanceScript* Instance = creature->GetInstanceScript())
Instance->SetData(DATA_START_INSTANCE, 1);
break;
case GOSSIP_ACTION_INFO_DEF+3:
player->NearTeleportTo(playerTeleportPosition.GetPositionX(), playerTeleportPosition.GetPositionY(), playerTeleportPosition.GetPositionZ(), playerTeleportPosition.GetOrientation(), true);
CloseGossipMenuFor(player);
break;
}
return true;
}
};
/***********
** TELEPORTATION PORTAL
***********/
@ -213,7 +125,7 @@ struct npc_vh_teleportation_portal : public NullCreatureAI
_events.Reset();
if (_wave % 6 == 0)
return;
_instance->SetData(DATA_PORTAL_DEFEATED, 0);
_instance->DoAction(ACTION_PORTAL_DEFEATED);
}
void JustSummoned(Creature* summoned) override
@ -1012,7 +924,7 @@ struct npc_azure_saboteur : public npc_escortAI
_events.RescheduleEvent(EVENT_SABOTEUR_SHIELD_DISRUPTION, 1s);
else
{
_instance->SetData(DATA_RELEASE_BOSS, 0);
_instance->DoAction(ACTION_RELEASE_BOSS);
_events.RescheduleEvent(EVENT_SABOTEUR_DISAPPEAR, 500ms);
}
break;
@ -1059,7 +971,7 @@ class spell_destroy_door_seal_aura : public AuraScript
PreventDefaultAction();
if (Unit* target = GetTarget())
if (InstanceScript* Instance = target->GetInstanceScript())
Instance->SetData(DATA_DECREASE_DOOR_HEALTH, 0);
Instance->DoAction(ACTION_DECREASE_DOOR_HEALTH);
}
void Register() override
@ -1070,13 +982,16 @@ class spell_destroy_door_seal_aura : public AuraScript
struct npc_violet_hold_defense_system : public ScriptedAI
{
npc_violet_hold_defense_system(Creature* creature) : ScriptedAI(creature) { }
npc_violet_hold_defense_system(Creature* creature) : ScriptedAI(creature)
{
_tickCount = 0;
}
void Reset() override
{
_tickCount = 0;
DoCast(RAND(SPELL_DEFENSE_SYSTEM_SPAWN_EFFECT, SPELL_DEFENSE_SYSTEM_VISUAL));
events.ScheduleEvent(EVENT_ARCANE_LIGHTNING, 4s);
events.ScheduleEvent(EVENT_ARCANE_LIGHTNING_INSTAKILL, 4s);
me->DespawnOrUnsummon(7s, 0s);
}
@ -1084,25 +999,24 @@ struct npc_violet_hold_defense_system : public ScriptedAI
{
events.Update(diff);
switch (events.ExecuteEvent())
if (events.ExecuteEvent() == EVENT_ARCANE_LIGHTNING)
{
case EVENT_ARCANE_LIGHTNING:
DoCastAOE(RAND(SPELL_ARCANE_LIGHTNING, SPELL_ARCANE_LIGHTNING_VISUAL));
events.Repeat(2s);
break;
case EVENT_ARCANE_LIGHTNING_INSTAKILL:
DoCastAOE(SPELL_ARCANE_LIGHTNING);
DoCastAOE(SPELL_ARCANE_LIGHTNING_VISUAL);
if (++_tickCount >= 3)
DoCastAOE(SPELL_ARCANE_LIGHTNING_INSTAKILL);
else
events.Repeat(1s);
break;
}
}
private:
uint8 _tickCount;
};
void AddSC_violet_hold()
{
new go_vh_activation_crystal();
new npc_vh_sinclari();
RegisterVioletHoldCreatureAI(npc_vh_teleportation_portal);
RegisterVioletHoldCreatureAI(npc_azure_saboteur);

View file

@ -43,17 +43,12 @@ enum VHData
DATA_XEVOZZ_CELL,
DATA_ZURAMAT_CELL,
// Instance action/state IDs (used by SetData/GetData)
// Instance state IDs (used by SetData/GetData)
DATA_ENCOUNTER_STATUS = 30,
DATA_ACTIVATE_DEFENSE_SYSTEM,
DATA_START_INSTANCE,
DATA_ADD_TRASH_MOB,
DATA_DELETE_TRASH_MOB,
DATA_PORTAL_DEFEATED,
DATA_WAVE_COUNT,
DATA_PORTAL_LOCATION,
DATA_RELEASE_BOSS,
DATA_DECREASE_DOOR_HEALTH,
DATA_ACHIEV,
// Manual GUID tracking (multi-instance entries)
@ -61,6 +56,14 @@ enum VHData
DATA_EREKEM_GUARD_2_GUID,
};
enum VHActions
{
ACTION_START_INSTANCE = 1,
ACTION_PORTAL_DEFEATED,
ACTION_RELEASE_BOSS,
ACTION_DECREASE_DOOR_HEALTH,
};
enum VHPersistentData
{
PERSISTENT_DATA_FIRST_BOSS,
@ -165,7 +168,9 @@ enum VHInstanceEvents
// Event defense system
EVENT_ARCANE_LIGHTNING,
EVENT_ARCANE_LIGHTNING_INSTAKILL
// Spell event (SPELL_EFFECT_SEND_EVENT from spell 57804)
EVENT_ACTIVATE_CRYSTAL = 20001,
};
enum VHAchievCriteria