fix(Scripts/BRD): Restore Nagmara love event (#27287)

Co-authored-by: Peter <[email protected]>
Co-authored-by: Zoidwaffle <[email protected]>
Co-authored-by: Cala <[email protected]>
Co-authored-by: sudlud <[email protected]>
This commit is contained in:
moostigre
2026-10-06 22:20:10 -03:00
committed by GitHub
co-authored by Peter Zoidwaffle Cala sudlud
parent 0e8f90fb3f
commit abc45e7ab4
5 changed files with 700 additions and 8 deletions
@@ -0,0 +1,4 @@
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 9500) OR (`source_type` = 9 AND `entryorguid` = 950000);
UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_mistress_nagmara' WHERE `entry` = 9500;
UPDATE `creature_text` SET `Type` = 16 WHERE `CreatureID` = 9500 AND `GroupID` = 3 AND `ID` = 0;
UPDATE `creature_text` SET `Type` = 16 WHERE `CreatureID` = 9503 AND `GroupID` = 5 AND `ID` = 0;
@@ -0,0 +1,12 @@
-- Mistress Nagmara's route to the lovers' stop. The last point turns her after arrival.
DELETE FROM `waypoint_data` WHERE `id` = 95001;
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
(95001, 1, 869.12384, -202.85149, -43.708836, NULL, 0, 0, 0, 1, 0, 100, 0),
(95001, 2, 863.9559, -210.76521, -43.707447, NULL, 0, 0, 0, 1, 0, 100, 0),
(95001, 3, 866.69403, -221.29358, -43.709167, NULL, 0, 0, 0, 1, 0, 100, 0),
(95001, 4, 868.26624, -224.17285, -43.728756, NULL, 0, 0, 0, 1, 0, 100, 0),
(95001, 5, 882.0711, -226.17651, -46.92732, NULL, 0, 0, 0, 1, 0, 100, 0),
(95001, 6, 888.93, -221.56207, -49.944458, NULL, 0, 0, 0, 1, 0, 100, 0),
(95001, 7, 886.03735, -218.21387, -49.942142, NULL, 0, 0, 0, 1, 0, 100, 0),
(95001, 8, 878.1779, -222.06618, -49.967144, NULL, 0, 0, 0, 1, 0, 100, 0),
(95001, 9, 878.1779, -222.06618, -49.967144, 0.25003412, 0, 1, 0, 1, 0, 100, 0);
@@ -27,6 +27,9 @@
#include "ScriptedGossip.h" #include "ScriptedGossip.h"
#include "WaypointMgr.h" #include "WaypointMgr.h"
#include <iterator>
#include <limits>
enum IronhandData enum IronhandData
{ {
IRONHAND_N_GROUPS = 3, IRONHAND_N_GROUPS = 3,
@@ -596,14 +599,498 @@ private:
bool _volleyStarted; bool _volleyStarted;
}; };
// npc_rocknot // npc_mistress_nagmara
enum GrimGuzzlerNPCs
{
NPC_PRIVATE_ROCKNOT = 9503,
NPC_MISTRESS_NAGMARA = 9500
};
enum NagmaraSpells
{
SPELL_POTION_LOVE = 14928,
SPELL_NAGMARA_ROCKNOT = 15064
};
enum NagmaraTexts
{
SAY_NAGMARA_AMBIENT = 0,
SAY_NAGMARA_1 = 1,
EMOTE_NAGMARA = 3
};
enum RocknotSays enum RocknotSays
{ {
SAY_GOT_BEER = 0, SAY_GOT_BEER = 0,
SAY_MORE_ALE = 1, SAY_MORE_ALE = 1,
SAY_FIRST_EMPTY = 2, SAY_FIRST_EMPTY = 2,
SAY_SECOND_EMPTY = 3, SAY_SECOND_EMPTY = 3,
SAY_ALE = 4 SAY_ALE = 4,
EMOTE_ROCKNOT = 5
};
enum NagmaraQuests
{
QUEST_POTION_LOVE = 4201
};
enum NagmaraData
{
DATA_AMBIENT_ORDER = 1
};
enum NagmaraCreatures
{
NPC_GRIM_PATRON = 9545,
NPC_GUZZLING_PATRON = 9547
};
enum RocknotActions
{
ACTION_START_LOVE_POTION = 1,
ACTION_CANCEL_LOVE_POTION = 2,
ACTION_BEGIN_LOVERS_FOLLOW = 3,
ACTION_MOVE_TO_LOVERS_STOP = 4,
ACTION_ROCKNOT_AT_LOVERS_STOP = 5,
ACTION_COMPLETE_LOVE_POTION = 6,
ACTION_PAUSE_AT_BAR_DOOR = 7,
ACTION_RESUME_AFTER_BAR_DOOR = 8
};
enum RocknotData
{
DATA_CAN_START_LOVE_POTION = 1,
DATA_LOVE_POTION_ACTIVE = 2
};
enum NagmaraTaskGroups
{
GROUP_NAGMARA_LOVE_SEQUENCE = 1,
GROUP_NAGMARA_MOVEMENT_TIMEOUT,
GROUP_NAGMARA_AMBIENT_REPLY,
GROUP_NAGMARA_KISS
};
enum NagmaraGossip
{
GOSSIP_MENU_NAGMARA = 2076
};
enum NagmaraPoints
{
POINT_APPROACH = 100,
POINT_APPROACH_GREETING = 108,
POINT_ROCKNOT_FINAL = 300
};
enum NagmaraWaypoints
{
PATH_NAGMARA_LOVERS = 95001,
POINT_NAGMARA_DOOR_WAIT = 4
};
// WotLK Classic 3.4.1.49345 sniff, identical waypoints in two runs:
// https://github.com/azerothcore/azerothcore-wotlk/pull/27287#issuecomment-5786575146
Position const NagmaraApproachPosition = { 874.3762f, -187.63274f, -43.70371f };
Position const NagmaraGreetingPosition = { 889.49426f, -196.88141f, -43.713f };
Position const NagmaraFinalPosition = { 878.1779f, -222.06618f, -49.967144f, 0.25003412f };
Position const RocknotFinalPosition = { 880.1218f, -221.5959f, -49.95902f, 3.3916268f };
struct npc_mistress_nagmara : public CreatureAI
{
npc_mistress_nagmara(Creature* creature) : CreatureAI(creature), _instance(creature->GetInstanceScript()),
_doorOpenAttempts(0), _lovePotionEvent(false),
_lovePotionComplete(false), _doorOpenedByEvent(false) { }
void Reset() override
{
if (_lovePotionComplete)
{
me->setActive(false);
me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->GetMotionMaster()->MoveIdle();
return;
}
if (_instance && _instance->GetData(DATA_LOVE_POTION_EVENT) == DONE)
{
scheduler.CancelAll();
_ambientScheduler.CancelAll();
_rocknotGuid.Clear();
_doorOpenAttempts = 0;
_lovePotionEvent = false;
_lovePotionComplete = true;
_doorOpenedByEvent = false;
me->GetMotionMaster()->Clear();
Position const& finalPosition = NagmaraFinalPosition;
me->NearTeleportTo(finalPosition.GetPositionX(), finalPosition.GetPositionY(),
finalPosition.GetPositionZ(), finalPosition.GetOrientation());
me->SetHomePosition(finalPosition);
me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
ScheduleKiss();
me->GetMotionMaster()->MoveIdle();
me->setActive(false);
return;
}
if (_lovePotionEvent)
{
AbortLovePotionEvent(true);
return;
}
CancelLovePotionTasks();
_rocknotGuid.Clear();
_doorOpenAttempts = 0;
_doorOpenedByEvent = false;
}
void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
{
if (menuId != GOSSIP_MENU_NAGMARA || gossipListId != 0 || !player->GetQuestRewardStatus(QUEST_POTION_LOVE))
return;
CloseGossipMenuFor(player);
if (!_instance)
return;
uint32 const barState = _instance->GetData(TYPE_BAR);
if (barState != NOT_STARTED && barState != IN_PROGRESS)
return;
Creature* rocknot = me->FindNearestCreature(NPC_PRIVATE_ROCKNOT, 100.0f);
if (!rocknot || !rocknot->AI() || !rocknot->AI()->GetData(DATA_CAN_START_LOVE_POTION))
return;
rocknot->AI()->DoAction(ACTION_START_LOVE_POTION);
if (!rocknot->AI()->GetData(DATA_LOVE_POTION_ACTIVE))
return;
me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
_lovePotionEvent = true;
_rocknotGuid = rocknot->GetGUID();
me->setActive(true);
me->SetWalk(false);
me->GetMotionMaster()->Clear();
Talk(SAY_NAGMARA_1);
MoveToApproachPoint();
scheduler.Schedule(45s, GROUP_NAGMARA_MOVEMENT_TIMEOUT, [this](TaskContext context)
{
AbortLovePotionEvent(true, context);
});
}
void SetData(uint32 id, uint32 value) override
{
if (!_lovePotionComplete && id == DATA_AMBIENT_ORDER && value == 1 &&
!_ambientScheduler.IsGroupScheduled(GROUP_NAGMARA_AMBIENT_REPLY))
{
_ambientScheduler.Schedule(4s, GROUP_NAGMARA_AMBIENT_REPLY, [this](TaskContext /*context*/)
{
if (Creature* patron = FindNearestPatron())
Talk(SAY_NAGMARA_AMBIENT, patron);
});
}
}
void DoAction(int32 action) override
{
if (action == ACTION_CANCEL_LOVE_POTION)
AbortLovePotionEvent(false);
else if (action == ACTION_ROCKNOT_AT_LOVERS_STOP && _lovePotionEvent)
{
Creature* rocknot = ObjectAccessor::GetCreature(*me, _rocknotGuid);
if (!rocknot || !rocknot->AI())
{
AbortLovePotionEvent(false);
return;
}
me->SetFacingToObject(rocknot);
rocknot->SetFacingToObject(me);
scheduler.CancelGroupsOf({ GROUP_NAGMARA_LOVE_SEQUENCE, GROUP_NAGMARA_MOVEMENT_TIMEOUT });
_lovePotionEvent = false;
_lovePotionComplete = true;
_doorOpenedByEvent = false;
_ambientScheduler.CancelAll();
me->SetHomePosition(me->GetPosition());
rocknot->AI()->DoAction(ACTION_COMPLETE_LOVE_POTION);
if (_instance)
_instance->SetData(DATA_LOVE_POTION_EVENT, DONE);
me->setActive(false);
}
}
void MovementInform(uint32 type, uint32 pointId) override
{
if (type != POINT_MOTION_TYPE || !_lovePotionEvent)
return;
scheduler.RescheduleGroup(GROUP_NAGMARA_MOVEMENT_TIMEOUT, 45s);
if (pointId == POINT_APPROACH_GREETING)
{
scheduler.Schedule(1ms, GROUP_NAGMARA_LOVE_SEQUENCE, [this](TaskContext context)
{
GreetRocknot(context);
});
}
else if (pointId == POINT_APPROACH)
{
scheduler.Schedule(1ms, GROUP_NAGMARA_LOVE_SEQUENCE, [this](TaskContext /*context*/)
{
MoveToGreetingPosition();
});
}
}
void WaypointReached(uint32 pointId, uint32 pathId) override
{
if (!_lovePotionEvent || pathId != PATH_NAGMARA_LOVERS)
return;
scheduler.RescheduleGroup(GROUP_NAGMARA_MOVEMENT_TIMEOUT, 45s);
if (pointId != POINT_NAGMARA_DOOR_WAIT)
return;
me->PauseMovement();
_doorOpenAttempts = 0;
if (Creature* rocknot = ObjectAccessor::GetCreature(*me, _rocknotGuid))
if (rocknot->AI())
rocknot->AI()->DoAction(ACTION_PAUSE_AT_BAR_DOOR);
scheduler.Schedule(500ms, GROUP_NAGMARA_LOVE_SEQUENCE, [this](TaskContext context)
{
TryOpenBarDoor(context);
});
}
void PathEndReached(uint32 pathId) override
{
if (_lovePotionEvent && pathId == PATH_NAGMARA_LOVERS)
scheduler.Schedule(1ms, GROUP_NAGMARA_LOVE_SEQUENCE, [this](TaskContext context)
{
ReachLoversStop(context);
});
}
void UpdateAI(uint32 diff) override
{
if (!_lovePotionEvent && !_lovePotionComplete)
_ambientScheduler.Update(diff);
scheduler.Update(diff);
}
private:
Creature* FindNearestPatron() const
{
Creature* patron = me->FindNearestCreature(NPC_GRIM_PATRON, 50.0f);
if (Creature* guzzlingPatron = me->FindNearestCreature(NPC_GUZZLING_PATRON, 50.0f);
guzzlingPatron && (!patron || me->GetExactDist2d(guzzlingPatron) < me->GetExactDist2d(patron)))
patron = guzzlingPatron;
return patron;
}
void MoveToApproachPoint()
{
me->GetMotionMaster()->MovePoint(POINT_APPROACH, NagmaraApproachPosition,
FORCED_MOVEMENT_NONE, 0.0f, false);
}
void MoveToGreetingPosition()
{
me->GetMotionMaster()->MovePoint(POINT_APPROACH_GREETING, NagmaraGreetingPosition, FORCED_MOVEMENT_NONE, 0.0f, false);
}
void GreetRocknot(TaskContext& context)
{
Creature* rocknot = ObjectAccessor::GetCreature(*me, _rocknotGuid);
if (!rocknot || !me->IsWithinDistInMap(rocknot, 6.0f))
{
AbortLovePotionEvent(true, context);
return;
}
me->GetMotionMaster()->MoveIdle();
me->SetFacingToObject(rocknot);
rocknot->SetFacingToObject(me);
context.Schedule(300ms, GROUP_NAGMARA_LOVE_SEQUENCE, [this](TaskContext context)
{
CastLovePotion(context);
});
}
void CastLovePotion(TaskContext& context)
{
DoCast(me, SPELL_POTION_LOVE);
// The potion casts for 1 second, then she waits 2.2 seconds before leaving.
if (ObjectAccessor::GetCreature(*me, _rocknotGuid))
{
context.Schedule(3200ms, GROUP_NAGMARA_LOVE_SEQUENCE, [this](TaskContext context)
{
StartLoversRoute(context);
});
}
else
AbortLovePotionEvent(false, context);
}
void StartLoversRoute(TaskContext& context)
{
Creature* rocknot = ObjectAccessor::GetCreature(*me, _rocknotGuid);
if (!rocknot || !rocknot->AI())
{
AbortLovePotionEvent(false, context);
return;
}
rocknot->AI()->DoAction(ACTION_BEGIN_LOVERS_FOLLOW);
if (!rocknot->AI()->GetData(DATA_LOVE_POTION_ACTIVE))
{
AbortLovePotionEvent(false, context);
return;
}
me->GetMotionMaster()->MoveWaypoint(PATH_NAGMARA_LOVERS, false);
}
bool OpenBarDoor()
{
if (!_instance)
return false;
GameObject* door = _instance->instance->GetGameObject(_instance->GetGuidData(DATA_GO_BAR_DOOR));
if (!door)
return false;
// players can open this door with the Grim Guzzler Key, so only close it again if we opened it
_doorOpenedByEvent = door->GetGoState() == GO_STATE_READY;
door->SetGoState(GO_STATE_ACTIVE);
return true;
}
void TryOpenBarDoor(TaskContext& context)
{
if (OpenBarDoor())
{
context.Schedule(3200ms, GROUP_NAGMARA_LOVE_SEQUENCE, [this](TaskContext /*context*/)
{
ContinueAfterDoor();
});
}
else if (++_doorOpenAttempts < 30)
context.Repeat(1s);
else
AbortLovePotionEvent(true, context);
}
void ContinueAfterDoor()
{
if (Creature* rocknot = ObjectAccessor::GetCreature(*me, _rocknotGuid))
if (rocknot->AI())
rocknot->AI()->DoAction(ACTION_RESUME_AFTER_BAR_DOOR);
me->ResumeMovement();
}
void ReachLoversStop(TaskContext& context)
{
me->GetMotionMaster()->MoveIdle();
if (Creature* rocknot = ObjectAccessor::GetCreature(*me, _rocknotGuid))
if (rocknot->AI())
{
me->SetFacingTo(me->GetAngle(&RocknotFinalPosition));
context.Schedule(1ms, GROUP_NAGMARA_KISS, [this](TaskContext context)
{
KissRocknot(context);
});
rocknot->AI()->DoAction(ACTION_MOVE_TO_LOVERS_STOP);
return;
}
AbortLovePotionEvent(false, context);
}
void KissRocknot(TaskContext& context)
{
Talk(EMOTE_NAGMARA);
DoCastSelf(SPELL_NAGMARA_ROCKNOT, true);
context.Repeat(11s, 21s);
}
void ScheduleKiss()
{
scheduler.Schedule(1ms, GROUP_NAGMARA_KISS, [this](TaskContext context)
{
KissRocknot(context);
});
}
void CancelLovePotionTasks()
{
scheduler.CancelGroupsOf({ GROUP_NAGMARA_LOVE_SEQUENCE, GROUP_NAGMARA_MOVEMENT_TIMEOUT,
GROUP_NAGMARA_KISS });
}
void AbortLovePotionEvent(bool notifyRocknot)
{
if (_lovePotionComplete || !_lovePotionEvent)
return;
CancelLovePotionTasks();
ResetLovePotionEvent(notifyRocknot);
}
void AbortLovePotionEvent(bool notifyRocknot, TaskContext& context)
{
if (_lovePotionComplete || !_lovePotionEvent)
return;
context.CancelGroupsOf({ GROUP_NAGMARA_LOVE_SEQUENCE, GROUP_NAGMARA_MOVEMENT_TIMEOUT,
GROUP_NAGMARA_KISS });
ResetLovePotionEvent(notifyRocknot);
}
void ResetLovePotionEvent(bool notifyRocknot)
{
Creature* rocknot = ObjectAccessor::GetCreature(*me, _rocknotGuid);
if (_doorOpenedByEvent && _instance)
if (GameObject* door = _instance->instance->GetGameObject(_instance->GetGuidData(DATA_GO_BAR_DOOR)))
door->SetGoState(GO_STATE_READY);
_rocknotGuid.Clear();
_lovePotionEvent = false;
_doorOpenAttempts = 0;
_doorOpenedByEvent = false;
me->setActive(false);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAurasDueToSpell(SPELL_NAGMARA_ROCKNOT);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->InitDefault();
me->GetMotionMaster()->MoveTargetedHome();
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER);
if (notifyRocknot && rocknot && rocknot->AI())
rocknot->AI()->DoAction(ACTION_CANCEL_LOVE_POTION);
}
InstanceScript* _instance;
TaskScheduler _ambientScheduler;
ObjectGuid _rocknotGuid;
uint8 _doorOpenAttempts;
bool _lovePotionEvent;
bool _lovePotionComplete;
bool _doorOpenedByEvent;
}; };
enum RocknotQuests enum RocknotQuests
@@ -637,10 +1124,15 @@ enum RocknotPoints
POINT_ROCKNOT_FINAL_KEG = 8 POINT_ROCKNOT_FINAL_KEG = 8
}; };
enum RocknotTaskGroups
{
GROUP_ROCKNOT_KISS = 1
};
struct npc_rocknot : public npc_escortAI struct npc_rocknot : public npc_escortAI
{ {
npc_rocknot(Creature* creature) : npc_escortAI(creature), npc_rocknot(Creature* creature) : npc_escortAI(creature), _instance(creature->GetInstanceScript()),
_instance(creature->GetInstanceScript()), _aleComplete(false) _aleComplete(false), _lovePotionEvent(false), _lovePotionComplete(false)
{ {
if (WaypointPath const* path = sWaypointMgr->GetPath(PATH_ROCKNOT_ALE)) if (WaypointPath const* path = sWaypointMgr->GetPath(PATH_ROCKNOT_ALE))
for (WaypointNode const& node : path->Nodes) for (WaypointNode const& node : path->Nodes)
@@ -649,6 +1141,30 @@ struct npc_rocknot : public npc_escortAI
void Reset() override void Reset() override
{ {
if (_instance && _instance->GetData(DATA_LOVE_POTION_EVENT) == DONE)
{
_nagmaraGuid.Clear();
_lovePotionEvent = false;
_lovePotionComplete = true;
_events.Reset();
me->GetMotionMaster()->Clear();
me->NearTeleportTo(RocknotFinalPosition.GetPositionX(), RocknotFinalPosition.GetPositionY(),
RocknotFinalPosition.GetPositionZ(), RocknotFinalPosition.GetOrientation());
me->SetHomePosition(RocknotFinalPosition);
me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_QUESTGIVER);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
ScheduleKiss();
me->GetMotionMaster()->MoveIdle();
me->setActive(false);
return;
}
if (_lovePotionEvent && !_lovePotionComplete)
{
AbortLovePotionEvent();
return;
}
if (HasEscortState(STATE_ESCORT_ESCORTING) || _aleEventActive) if (HasEscortState(STATE_ESCORT_ESCORTING) || _aleEventActive)
return; return;
@@ -657,6 +1173,98 @@ struct npc_rocknot : public npc_escortAI
me->SetEmoteState(EMOTE_STATE_NONE); me->SetEmoteState(EMOTE_STATE_NONE);
} }
void DoAction(int32 action) override
{
if (action == ACTION_START_LOVE_POTION)
{
if (!CanStartLovePotionEvent())
return;
_lovePotionEvent = true;
_lovePotionComplete = false;
me->setActive(true);
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER);
}
else if (action == ACTION_CANCEL_LOVE_POTION)
AbortLovePotionEvent();
else if (action == ACTION_BEGIN_LOVERS_FOLLOW && _lovePotionEvent)
{
if (me->IsInCombat())
{
AbortLovePotionEvent();
return;
}
Creature* nagmara = me->FindNearestCreature(NPC_MISTRESS_NAGMARA, 100.0f);
if (!nagmara)
{
AbortLovePotionEvent();
return;
}
_nagmaraGuid = nagmara->GetGUID();
me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_QUESTGIVER);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetWalk(false);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveFollow(nagmara, 3.0f, M_PI);
}
else if (action == ACTION_MOVE_TO_LOVERS_STOP && _lovePotionEvent)
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(POINT_ROCKNOT_FINAL, RocknotFinalPosition);
}
else if (action == ACTION_PAUSE_AT_BAR_DOOR && _lovePotionEvent)
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
}
else if (action == ACTION_RESUME_AFTER_BAR_DOOR && _lovePotionEvent)
{
if (Creature* nagmara = ObjectAccessor::GetCreature(*me, _nagmaraGuid))
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveFollow(nagmara, 3.0f, M_PI);
}
}
else if (action == ACTION_COMPLETE_LOVE_POTION && _lovePotionEvent)
{
_lovePotionEvent = false;
_lovePotionComplete = true;
me->SetHomePosition(me->GetPosition());
me->GetMotionMaster()->MoveIdle();
me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_QUESTGIVER);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
ScheduleKiss();
me->setActive(false);
}
}
uint32 GetData(uint32 type) const override
{
if (type == DATA_CAN_START_LOVE_POTION)
return CanStartLovePotionEvent();
if (type == DATA_LOVE_POTION_ACTIVE)
return _lovePotionEvent;
return 0;
}
void MovementInform(uint32 type, uint32 pointId) override
{
npc_escortAI::MovementInform(type, pointId);
if (type != POINT_MOTION_TYPE || pointId != POINT_ROCKNOT_FINAL || !_lovePotionEvent)
return;
me->GetMotionMaster()->MoveIdle();
if (Creature* nagmara = ObjectAccessor::GetCreature(*me, _nagmaraGuid))
if (nagmara->AI())
nagmara->AI()->DoAction(ACTION_ROCKNOT_AT_LOVERS_STOP);
}
void JustRespawned() override void JustRespawned() override
{ {
bool const restartAleEvent = _aleEventActive && !_aleComplete; bool const restartAleEvent = _aleEventActive && !_aleComplete;
@@ -691,7 +1299,7 @@ struct npc_rocknot : public npc_escortAI
void sQuestReward(Player* player, Quest const* quest, uint32 /*opt*/) override void sQuestReward(Player* player, Quest const* quest, uint32 /*opt*/) override
{ {
if (!_instance || quest->GetQuestId() != QUEST_ALE) if (!_instance || _lovePotionEvent || _lovePotionComplete || quest->GetQuestId() != QUEST_ALE)
return; return;
if (HasEscortState(STATE_ESCORT_ESCORTING) || _aleEventActive) if (HasEscortState(STATE_ESCORT_ESCORTING) || _aleEventActive)
@@ -757,6 +1365,10 @@ struct npc_rocknot : public npc_escortAI
void UpdateEscortAI(uint32 diff) override void UpdateEscortAI(uint32 diff) override
{ {
scheduler.Update(diff);
if (_lovePotionEvent || _lovePotionComplete)
return;
_events.Update(diff); _events.Update(diff);
while (uint32 eventId = _events.ExecuteEvent()) while (uint32 eventId = _events.ExecuteEvent())
{ {
@@ -821,6 +1433,51 @@ struct npc_rocknot : public npc_escortAI
} }
private: private:
void ScheduleKiss()
{
scheduler.CancelGroup(GROUP_ROCKNOT_KISS);
scheduler.Schedule(1ms, GROUP_ROCKNOT_KISS, [this](TaskContext context)
{
me->HandleEmoteCommand(EMOTE_ONESHOT_KISS);
Talk(EMOTE_ROCKNOT);
DoCastSelf(SPELL_NAGMARA_ROCKNOT, true);
context.Repeat(11s, 21s);
});
}
bool CanStartLovePotionEvent() const
{
if (_lovePotionEvent || _lovePotionComplete || _aleEventActive || _aleComplete ||
!_instance || me->IsInCombat())
return false;
// Partial ale hand-ins do not commit to the escort route; SPECIAL means it has started.
uint32 const barState = _instance->GetData(TYPE_BAR);
if (barState != NOT_STARTED && barState != IN_PROGRESS)
return false;
return _instance->instance->GetGameObject(_instance->GetGuidData(DATA_GO_BAR_DOOR));
}
void AbortLovePotionEvent()
{
if (_lovePotionComplete || !_lovePotionEvent)
return;
_lovePotionEvent = false;
scheduler.CancelGroup(GROUP_ROCKNOT_KISS);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
_nagmaraGuid.Clear();
me->setActive(false);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveTargetedHome();
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER);
if (Creature* nagmara = me->FindNearestCreature(NPC_MISTRESS_NAGMARA, 100.0f))
if (nagmara->AI())
nagmara->AI()->DoAction(ACTION_CANCEL_LOVE_POTION);
}
void StartAleEvent() void StartAleEvent()
{ {
_events.Reset(); _events.Reset();
@@ -844,10 +1501,13 @@ private:
} }
InstanceScript* _instance; InstanceScript* _instance;
ObjectGuid _nagmaraGuid;
EventMap _events; EventMap _events;
bool _aleComplete; bool _aleComplete;
bool _aleEventActive = false; bool _aleEventActive = false;
bool _recovering = false; bool _recovering = false;
bool _lovePotionEvent;
bool _lovePotionComplete;
Position _originalPosition; Position _originalPosition;
NPCFlags _originalNpcFlags = UNIT_NPC_FLAG_NONE; NPCFlags _originalNpcFlags = UNIT_NPC_FLAG_NONE;
bool _originalImmuneToNPC = false; bool _originalImmuneToNPC = false;
@@ -859,6 +1519,7 @@ void AddSC_blackrock_depths()
new at_ring_of_law(); new at_ring_of_law();
RegisterBlackrockDepthsCreatureAI(npc_grimstone); RegisterBlackrockDepthsCreatureAI(npc_grimstone);
RegisterBlackrockDepthsCreatureAI(npc_phalanx); RegisterBlackrockDepthsCreatureAI(npc_phalanx);
RegisterBlackrockDepthsCreatureAI(npc_mistress_nagmara);
RegisterBlackrockDepthsCreatureAI(npc_rocknot); RegisterBlackrockDepthsCreatureAI(npc_rocknot);
RegisterBlackrockDepthsCreatureAI(brd_ironhand_guardian); RegisterBlackrockDepthsCreatureAI(brd_ironhand_guardian);
} }
@@ -83,7 +83,8 @@ enum DataTypes
DATA_TIME_RING_FAIL, DATA_TIME_RING_FAIL,
DATA_ARENA_MOBS, DATA_ARENA_MOBS,
DATA_ARENA_BOSS, DATA_ARENA_BOSS,
DATA_PHALANX_ACTIVATED DATA_PHALANX_ACTIVATED,
DATA_LOVE_POTION_EVENT
}; };
enum Creatures enum Creatures
@@ -140,6 +140,7 @@ struct instance_blackrock_depths : public InstanceScript
uint32 BarAleCount; uint32 BarAleCount;
uint32 GhostKillCount; uint32 GhostKillCount;
uint32 LovePotionEventState;
ObjectGuid TombBossGUIDs[7]; ObjectGuid TombBossGUIDs[7];
uint32 tombResetTimer; uint32 tombResetTimer;
uint32 TombTimer; uint32 TombTimer;
@@ -220,6 +221,7 @@ struct instance_blackrock_depths : public InstanceScript
BarAleCount = 0; BarAleCount = 0;
GhostKillCount = 0; GhostKillCount = 0;
LovePotionEventState = NOT_STARTED;
TombTimer = TIMER_TOMB_START; TombTimer = TIMER_TOMB_START;
TombEventCounter = 0; TombEventCounter = 0;
tombResetTimer = 0; tombResetTimer = 0;
@@ -384,7 +386,10 @@ struct instance_blackrock_depths : public InstanceScript
break; break;
case GO_BAR_DOOR: case GO_BAR_DOOR:
GoBarDoorGUID = go->GetGUID(); GoBarDoorGUID = go->GetGUID();
if (GetData(TYPE_BAR) == DONE) go->AllowSaveToDB(true);
if (LovePotionEventState == DONE)
go->SetGoState(GO_STATE_ACTIVE);
else if (GetData(TYPE_BAR) == DONE)
go->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); go->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
break; break;
case GO_TOMB_ENTER: case GO_TOMB_ENTER:
@@ -537,6 +542,11 @@ struct instance_blackrock_depths : public InstanceScript
else else
encounter[2] = data; encounter[2] = data;
break; break;
case DATA_LOVE_POTION_EVENT:
LovePotionEventState = data;
if (data == DONE)
encounter[2] = DONE;
break;
case TYPE_TOMB_OF_SEVEN: case TYPE_TOMB_OF_SEVEN:
encounter[3] = data; encounter[3] = data;
switch (data) switch (data)
@@ -687,7 +697,7 @@ struct instance_blackrock_depths : public InstanceScript
std::ostringstream saveStream; std::ostringstream saveStream;
saveStream << encounter[0] << ' ' << encounter[1] << ' ' << encounter[2] << ' ' saveStream << encounter[0] << ' ' << encounter[1] << ' ' << encounter[2] << ' '
<< encounter[3] << ' ' << encounter[4] << ' ' << encounter[5] << ' ' << GhostKillCount << ' ' << encounter[3] << ' ' << encounter[4] << ' ' << encounter[5] << ' ' << GhostKillCount << ' '
<< PhalanxActivationState; << PhalanxActivationState << ' ' << LovePotionEventState;
str_data = saveStream.str(); str_data = saveStream.str();
@@ -723,6 +733,8 @@ struct instance_blackrock_depths : public InstanceScript
return arenaBossToSpawn; return arenaBossToSpawn;
case DATA_PHALANX_ACTIVATED: case DATA_PHALANX_ACTIVATED:
return PhalanxActivationState; return PhalanxActivationState;
case DATA_LOVE_POTION_EVENT:
return LovePotionEventState;
} }
return 0; return 0;
} }
@@ -794,6 +806,8 @@ struct instance_blackrock_depths : public InstanceScript
>> encounter[4] >> encounter[5] >> GhostKillCount; >> encounter[4] >> encounter[5] >> GhostKillCount;
if (!(loadStream >> PhalanxActivationState) || PhalanxActivationState != DONE) if (!(loadStream >> PhalanxActivationState) || PhalanxActivationState != DONE)
PhalanxActivationState = NOT_STARTED; PhalanxActivationState = NOT_STARTED;
if (!(loadStream >> LovePotionEventState) || LovePotionEventState != DONE)
LovePotionEventState = NOT_STARTED;
for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (encounter[i] == IN_PROGRESS) if (encounter[i] == IN_PROGRESS)