aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTeleqraph <nyrdeveloper@gmail.com>2022-10-11 10:20:04 +0200
committerGitHub <noreply@github.com>2022-10-11 10:20:04 +0200
commitc7109629dcd26c106333a768523ad08552aac48f (patch)
tree74b8ff80ae972b64040092c2c5c270580abb473a /src
parentc67cb094d50058252f13d725c019baea102a28c9 (diff)
Scripts/Silverpine: quest fixes and spawns for Forsaken Rear Guard (#28303)
Diffstat (limited to 'src')
-rw-r--r--src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp1530
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp25
2 files changed, 1543 insertions, 12 deletions
diff --git a/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp b/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp
index 2b381c85c73..7f7a2fcc343 100644
--- a/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp
+++ b/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp
@@ -1645,6 +1645,7 @@ enum DeathstalkerRaneYorick
EVENT_SET_GUID_FOR_ARMOIRE = 5,
EVENT_RANE_TALK_TO_PLAYER = 6,
EVENT_RANE_LAST_MOVE = 7,
+ EVENT_RANE_SKIPS_PATH = 9,
ACTION_RANE_JUMP_DEATH = 1,
ACTION_RANE_SKIP_PATH = 2,
@@ -1705,22 +1706,11 @@ struct npc_silverpine_deathstalker_rane_yorick : public ScriptedAI
break;
case ACTION_RANE_SKIP_PATH:
- {
me->PauseMovement();
-
me->GetMotionMaster()->Clear();
-
- me->NearTeleportTo(YorickReadyPosition, false);
-
_events.Reset();
-
- _events.ScheduleEvent(EVENT_SET_GUID_FOR_ARMOIRE, 1s);
-
- DoCastSelf(SPELL_STEALTH);
-
- me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE);
+ _events.ScheduleEvent(EVENT_RANE_SKIPS_PATH, 250ms);
break;
- }
default:
break;
@@ -1737,6 +1727,7 @@ struct npc_silverpine_deathstalker_rane_yorick : public ScriptedAI
if (Unit* summoner = tempSummon->GetSummonerUnit())
{
+ // Note: SummonPropertiesFlags::DespawnWhenExpired is NYI.
if (!summoner->HasAura(SPELL_SUMMON_YORICK))
me->DespawnOrUnsummon();
}
@@ -1847,6 +1838,17 @@ struct npc_silverpine_deathstalker_rane_yorick : public ScriptedAI
me->SetDisableGravity(false);
break;
+ case EVENT_RANE_SKIPS_PATH:
+ me->NearTeleportTo(YorickReadyPosition, false);
+ _events.ScheduleEvent(EVENT_RANE_SKIPS_PATH + 1, 250ms);
+ break;
+
+ case EVENT_RANE_SKIPS_PATH + 1:
+ DoCastSelf(SPELL_STEALTH);
+ me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE);
+ _events.ScheduleEvent(EVENT_SET_GUID_FOR_ARMOIRE, 500ms);
+ break;
+
default:
break;
}
@@ -2580,6 +2582,1484 @@ struct npc_silverpine_packleader_ivar_bloodfang_exsanguinate : public ScriptedAI
}
};
+enum SaltyRocka
+{
+ NPC_SALTY_GORGAR = 45497,
+
+ EVENT_ROCKA_CHECK_CONVERSATION = 1,
+ EVENT_ROCKA_CHOOSE_CONVERSATION = 2,
+ EVENT_ROCKA_CONVERSATION_COOLDOWN = 3,
+ EVENT_ROCKA_TALK = 10,
+
+ TALK_ROCKA_0 = 0,
+ TALK_ROCKA_1 = 1,
+ TALK_ROCKA_2 = 2,
+ TALK_ROCKA_3 = 3,
+ TALK_ROCKA_4 = 4,
+ TALK_ROCKA_5 = 5,
+ TALK_ROCKA_6 = 6,
+ TALK_ROCKA_7 = 7,
+ TALK_ROCKA_8 = 8,
+ TALK_ROCKA_9 = 9,
+ TALK_GORGAR_0 = 0,
+ TALK_GORGAR_1 = 1,
+ TALK_GORGAR_2 = 2,
+ TALK_GORGAR_3 = 3,
+ TALK_GORGAR_4 = 4,
+ TALK_GORGAR_5 = 5
+};
+
+// 45498 - "Salty" Rocka
+struct npc_silverpine_salty_rocka : public ScriptedAI
+{
+ npc_silverpine_salty_rocka(Creature* creature) : ScriptedAI(creature), _isConversationOnCooldown(false) { }
+
+ void JustAppeared() override
+ {
+ CheckForGorgor();
+ }
+
+ void Reset() override
+ {
+ _isConversationOnCooldown = false;
+ _events.Reset();
+ }
+
+ void MoveInLineOfSight(Unit* who) override
+ {
+ if (who->GetTypeId() == TYPEID_PLAYER && who->GetDistance2d(me) < 20.0f)
+ _events.ScheduleEvent(EVENT_ROCKA_CHECK_CONVERSATION, 250ms);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_ROCKA_CHECK_CONVERSATION:
+ if (_isConversationOnCooldown)
+ return;
+ _isConversationOnCooldown = true;
+ _events.ScheduleEvent(EVENT_ROCKA_CHOOSE_CONVERSATION, 250ms);
+ _events.ScheduleEvent(EVENT_ROCKA_CONVERSATION_COOLDOWN, 180s);
+ break;
+
+ case EVENT_ROCKA_CHOOSE_CONVERSATION:
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + (urand(0, 4) * 10), 750ms);
+ break;
+
+ case EVENT_ROCKA_TALK:
+ Talk(TALK_ROCKA_0);
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + 1, 20s);
+ break;
+
+ case EVENT_ROCKA_TALK + 1:
+ {
+ if (Creature* gorgar = ObjectAccessor::GetCreature(*me, _gorgarGUID))
+ {
+ if (gorgar->IsAIEnabled())
+ gorgar->AI()->Talk(TALK_GORGAR_0);
+ }
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + 2, 20s);
+ break;
+ }
+
+ case EVENT_ROCKA_TALK + 2:
+ Talk(TALK_ROCKA_1);
+ break;
+
+ case EVENT_ROCKA_TALK + 10:
+ Talk(TALK_ROCKA_2);
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + 11, 20s);
+ break;
+
+ case EVENT_ROCKA_TALK + 11:
+ {
+ if (Creature* gorgar = ObjectAccessor::GetCreature(*me, _gorgarGUID))
+ {
+ if (gorgar->IsAIEnabled())
+ gorgar->AI()->Talk(TALK_GORGAR_1);
+ }
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + 12, 20s);
+ break;
+ }
+
+ case EVENT_ROCKA_TALK + 12:
+ Talk(TALK_ROCKA_3);
+ break;
+
+ case EVENT_ROCKA_TALK + 20:
+ Talk(TALK_ROCKA_4);
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + 21, 20s);
+ break;
+
+ case EVENT_ROCKA_TALK + 21:
+ {
+ if (Creature* gorgar = ObjectAccessor::GetCreature(*me, _gorgarGUID))
+ {
+ if (gorgar->IsAIEnabled())
+ gorgar->AI()->Talk(TALK_GORGAR_2);
+ }
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + 22, 20s);
+ break;
+ }
+
+ case EVENT_ROCKA_TALK + 22:
+ Talk(TALK_ROCKA_5);
+ break;
+
+ case EVENT_ROCKA_TALK + 30:
+ Talk(TALK_ROCKA_6);
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + 31, 20s);
+ break;
+
+ case EVENT_ROCKA_TALK + 31:
+ {
+ if (Creature* gorgar = ObjectAccessor::GetCreature(*me, _gorgarGUID))
+ {
+ if (gorgar->IsAIEnabled())
+ gorgar->AI()->Talk(TALK_GORGAR_3);
+ }
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + 32, 20s);
+ break;
+ }
+
+ case EVENT_ROCKA_TALK + 32:
+ Talk(TALK_ROCKA_7);
+ break;
+
+ case EVENT_ROCKA_TALK + 40:
+ Talk(TALK_ROCKA_8);
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + 1, 20s);
+ break;
+
+ case EVENT_ROCKA_TALK + 41:
+ {
+ if (Creature* gorgar = ObjectAccessor::GetCreature(*me, _gorgarGUID))
+ {
+ if (gorgar->IsAIEnabled())
+ gorgar->AI()->Talk(TALK_GORGAR_4);
+ }
+ _events.ScheduleEvent(EVENT_ROCKA_TALK + 2, 20s);
+ break;
+ }
+
+ case EVENT_ROCKA_TALK + 42:
+ Talk(TALK_ROCKA_9);
+ break;
+
+ case EVENT_ROCKA_CONVERSATION_COOLDOWN:
+ Reset();
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ void CheckForGorgor()
+ {
+ if (!_gorgarGUID)
+ {
+ if (Creature* gorgar = me->FindNearestCreature(NPC_SALTY_GORGAR, 50.0f))
+ _gorgarGUID = gorgar->GetGUID();
+ }
+ }
+
+private:
+ EventMap _events;
+ ObjectGuid _gorgarGUID;
+ bool _isConversationOnCooldown;
+};
+
+enum AtForsakenRearGuard
+{
+ QUEST_LOST_IN_THE_DARKNESS = 27093,
+ QUEST_DEEPER_INTO_DARKNESS = 27094,
+ QUEST_ITS_ONLY_POISONOUS_IF_YOU_INGEST_IT = 27088,
+
+ NPC_ADMIRAL_HATCHET = 44916,
+ NPC_APOTHECARY_WORMCRUD = 44912,
+
+ ACTION_HATCHET_START_CONVERSATION = 1,
+ ACTION_WORMCRUD_START_CONVERSATION = 2
+};
+
+// 6222 - Forsaken Rear Guard
+class at_silverpine_forsaken_rear_guard : public AreaTriggerScript
+{
+public:
+ at_silverpine_forsaken_rear_guard() : AreaTriggerScript("at_silverpine_forsaken_rear_guard") { }
+
+ bool OnTrigger(Player* player, AreaTriggerEntry const* /*at*/) override
+ {
+ if (player->IsAlive())
+ {
+ if ((player->GetQuestStatus(QUEST_LOST_IN_THE_DARKNESS) != QUEST_STATUS_COMPLETE && player->GetQuestStatus(QUEST_LOST_IN_THE_DARKNESS) != QUEST_STATUS_REWARDED) &&
+ (player->GetQuestStatus(QUEST_DEEPER_INTO_DARKNESS) != QUEST_STATUS_COMPLETE && player->GetQuestStatus(QUEST_DEEPER_INTO_DARKNESS) != QUEST_STATUS_REWARDED))
+ {
+ if (Creature* hatchet = player->FindNearestCreature(NPC_ADMIRAL_HATCHET, 50.0f))
+ {
+ if (hatchet->IsAIEnabled())
+ hatchet->AI()->DoAction(ACTION_HATCHET_START_CONVERSATION);
+ }
+ }
+ else if (player->GetQuestStatus(QUEST_ITS_ONLY_POISONOUS_IF_YOU_INGEST_IT) != QUEST_STATUS_COMPLETE && player->GetQuestStatus(QUEST_ITS_ONLY_POISONOUS_IF_YOU_INGEST_IT) != QUEST_STATUS_REWARDED)
+ {
+ if (Creature* wormcrud = player->FindNearestCreature(NPC_APOTHECARY_WORMCRUD, 50.0f))
+ {
+ if (wormcrud->IsAIEnabled())
+ wormcrud->AI()->DoAction(ACTION_WORMCRUD_START_CONVERSATION);
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+enum ApothecaryWormcrud
+{
+ NPC_DRUNKEN_ORC_SEA_DOG = 44913,
+
+ EVENT_WORMCRUD_CHECK_CONVERSATION = 1,
+ EVENT_WORMCRUD_CHOOSE_CONVERSATION = 2,
+ EVENT_WORMCRUD_CONVERSATION_COOLDOWN = 3,
+ EVENT_WORMCRUD_TALK = 4,
+
+ TALK_ORCSEA_0 = 0,
+ TALK_ORCSEA_1 = 1,
+ TALK_ORCSEA_2 = 2,
+ TALK_WORMCRUD_0 = 0
+};
+
+// 44912 - Apothecary Wormcrud
+struct npc_silverpine_apothecary_wormcrud : public ScriptedAI
+{
+ npc_silverpine_apothecary_wormcrud(Creature* creature) : ScriptedAI(creature), _isConversationOnCooldown(false) { }
+
+ void JustAppeared() override
+ {
+ CheckForSeaOrcs();
+ }
+
+ void Reset() override
+ {
+ _events.Reset();
+
+ _isConversationOnCooldown = false;
+ }
+
+ void DoAction(int32 param) override
+ {
+ if (param == ACTION_WORMCRUD_START_CONVERSATION)
+ {
+ if (_isConversationOnCooldown)
+ return;
+
+ _isConversationOnCooldown = true;
+
+ _events.ScheduleEvent(EVENT_WORMCRUD_TALK, 15s);
+ _events.ScheduleEvent(EVENT_WORMCRUD_CONVERSATION_COOLDOWN, 215s);
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_WORMCRUD_TALK:
+ {
+ if (Creature* orcsea1 = ObjectAccessor::GetCreature(*me, _drunkenOrcSeaDog[0]))
+ {
+ if (orcsea1->IsAIEnabled())
+ orcsea1->AI()->Talk(TALK_ORCSEA_0);
+ }
+ _events.ScheduleEvent(EVENT_WORMCRUD_TALK + 1, 12s);
+ break;
+ }
+
+ case EVENT_WORMCRUD_TALK + 1:
+ {
+ if (Creature* orcsea2 = ObjectAccessor::GetCreature(*me, _drunkenOrcSeaDog[1]))
+ {
+ if (orcsea2->IsAIEnabled())
+ orcsea2->AI()->Talk(TALK_ORCSEA_1);
+ }
+ _events.ScheduleEvent(EVENT_WORMCRUD_TALK + 2, 12s);
+ break;
+ }
+
+ case EVENT_WORMCRUD_TALK + 2:
+ {
+ if (Creature* orcsea3 = ObjectAccessor::GetCreature(*me, _drunkenOrcSeaDog[2]))
+ {
+ if (orcsea3->IsAIEnabled())
+ orcsea3->AI()->Talk(TALK_ORCSEA_2);
+ }
+ _events.ScheduleEvent(EVENT_WORMCRUD_TALK + 3, 12s);
+ break;
+ }
+
+ case EVENT_WORMCRUD_TALK + 3:
+ Talk(TALK_WORMCRUD_0);
+ break;
+
+ case EVENT_WORMCRUD_CONVERSATION_COOLDOWN:
+ Reset();
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ void CheckForSeaOrcs()
+ {
+ std::vector<Creature*> orcSeaDogList;
+ me->GetCreatureListWithEntryInGrid(orcSeaDogList, NPC_DRUNKEN_ORC_SEA_DOG, 5.0f);
+
+ if (orcSeaDogList.size() < 3)
+ return;
+
+ for (uint32 i = 0; i < orcSeaDogList.size(); i++)
+ _drunkenOrcSeaDog[i] = orcSeaDogList[i]->GetGUID();
+ }
+
+private:
+ EventMap _events;
+ std::array<ObjectGuid, 3> _drunkenOrcSeaDog;
+ bool _isConversationOnCooldown;
+};
+
+enum HatchetRearGuard
+{
+ QUEST_STEEL_THUNDER = 27069,
+
+ NPC_WARLORD_TOROK = 44917,
+
+ SPELL_SUMMON_ORC_SEA_PUP = 83839,
+ SPELL_SEA_PUP_TRIGGER = 83865,
+ SPELL_DESPAWN_ALL_SUMMONS_LOST_IN_DARKNESS = 83935,
+
+ EVENT_HATCHET_CHECK_CONVERSATION = 1,
+ EVENT_HATCHET_CONVERSATION_COOLDOWN = 2,
+ EVENT_HATCHET_TALK = 3,
+
+ TALK_HATCHET_0 = 0,
+ TALK_HATCHET_1 = 1,
+ TALK_TOROK_0 = 0,
+ TALK_TOROK_1 = 1,
+ TALK_TOROK_2 = 2,
+
+ ANIMKIT_TOROK = 594
+};
+
+// 44916 - Admiral Hatchet
+struct npc_silverpine_admiral_hatchet : public ScriptedAI
+{
+ npc_silverpine_admiral_hatchet(Creature* creature) : ScriptedAI(creature), _isConversationOnCooldown(false) { }
+
+ void JustAppeared() override
+ {
+ CheckForTorok();
+ }
+
+ void OnQuestAccept(Player* player, Quest const* quest) override
+ {
+ if (quest->GetQuestId() == QUEST_STEEL_THUNDER)
+ player->CastSpell(player, SPELL_SUMMON_ORC_SEA_PUP);
+ }
+
+ void OnQuestReward(Player* player, Quest const* quest, LootItemType /*type*/, uint32 /*opt*/) override
+ {
+ if (quest->GetQuestId() == QUEST_STEEL_THUNDER)
+ player->CastSpell(player, SPELL_SEA_PUP_TRIGGER, true);
+ else if (quest->GetQuestId() == QUEST_LOST_IN_THE_DARKNESS)
+ player->CastSpell(nullptr, SPELL_DESPAWN_ALL_SUMMONS_LOST_IN_DARKNESS, true);
+ }
+
+ bool OnGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
+ {
+ if (player->GetQuestStatus(QUEST_STEEL_THUNDER) == QUEST_STATUS_INCOMPLETE)
+ player->CastSpell(player, SPELL_SUMMON_ORC_SEA_PUP);
+
+ CloseGossipMenuFor(player);
+ return false;
+ }
+
+ void Reset() override
+ {
+ _events.Reset();
+ _isConversationOnCooldown = false;
+ }
+
+ void DoAction(int32 param) override
+ {
+ if (param == ACTION_HATCHET_START_CONVERSATION)
+ {
+ if (_isConversationOnCooldown)
+ return;
+
+ _isConversationOnCooldown = true;
+
+ _events.ScheduleEvent(EVENT_HATCHET_TALK, 1s);
+ _events.ScheduleEvent(EVENT_HATCHET_CONVERSATION_COOLDOWN, 230s);
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_HATCHET_TALK:
+ Talk(TALK_HATCHET_0);
+ _events.ScheduleEvent(EVENT_HATCHET_TALK + 1, 8s);
+ break;
+
+ case EVENT_HATCHET_TALK + 1:
+ {
+ if (Creature* torok = ObjectAccessor::GetCreature(*me, _torokGUID))
+ {
+ if (torok->IsAIEnabled())
+ torok->AI()->Talk(TALK_TOROK_0);
+ }
+ _events.ScheduleEvent(EVENT_HATCHET_TALK + 2, 6s);
+ break;
+ }
+
+ case EVENT_HATCHET_TALK + 2:
+ Talk(TALK_HATCHET_1);
+ _events.ScheduleEvent(EVENT_HATCHET_TALK + 3, 6s);
+ break;
+
+ case EVENT_HATCHET_TALK + 3:
+ {
+ if (Creature* torok = ObjectAccessor::GetCreature(*me, _torokGUID))
+ {
+ torok->PlayOneShotAnimKitId(ANIMKIT_TOROK);
+
+ if (torok->IsAIEnabled())
+ torok->AI()->Talk(TALK_TOROK_1);
+ }
+ break;
+ }
+
+ case EVENT_HATCHET_CONVERSATION_COOLDOWN:
+ Reset();
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ void CheckForTorok()
+ {
+ if (!_torokGUID)
+ {
+ if (Creature* torok = me->FindNearestCreature(NPC_WARLORD_TOROK, 30.0f))
+ _torokGUID = torok->GetGUID();
+ }
+ }
+
+private:
+ EventMap _events;
+ ObjectGuid _torokGUID;
+ bool _isConversationOnCooldown;
+};
+
+enum OrcSeaDog
+{
+ SPELL_DRINK_TANKARD = 42871,
+ SPELL_COSMETIC_SLEEP = 84186,
+ SPELL_COSMETIC_FEELING_SICK = 83832,
+ SPELL_COSMETIC_NOT_FEELING_SICK = 83829,
+ SPELL_SICK = 83885,
+
+ EVENT_SEA_DOG_DRINK = 1,
+ EVENT_SEA_DOG_ROAM = 2,
+ EVENT_SEA_DOG_VOMIT = 3,
+
+ COSMETIC_SIT_ON_THE_FLOOR = 1,
+ COSMETIC_SIT_ON_THE_FLOOR_TWICE = 2,
+ COSMETIC_GO_TO_SLEEP = 3,
+ COSMETIC_START_TO_FEEL_UNEASY = 4
+};
+
+// 44913 - Orc Sea Dog
+struct npc_silverpine_orc_sea_dog_not_sick : public ScriptedAI
+{
+ npc_silverpine_orc_sea_dog_not_sick(Creature* creature) : ScriptedAI(creature) {}
+
+ void JustAppeared() override
+ {
+ switch (urand(COSMETIC_SIT_ON_THE_FLOOR, COSMETIC_START_TO_FEEL_UNEASY))
+ {
+ case COSMETIC_SIT_ON_THE_FLOOR:
+ case COSMETIC_SIT_ON_THE_FLOOR_TWICE:
+ DoCastSelf(SPELL_COSMETIC_NOT_FEELING_SICK, false);
+ _events.ScheduleEvent(EVENT_SEA_DOG_DRINK, 1s, 2s);
+ break;
+ case COSMETIC_GO_TO_SLEEP:
+ DoCastSelf(SPELL_COSMETIC_SLEEP, false);
+ break;
+ case COSMETIC_START_TO_FEEL_UNEASY:
+ DoCastSelf(SPELL_COSMETIC_FEELING_SICK, false);
+ _events.ScheduleEvent(EVENT_SEA_DOG_ROAM, 1s, 5s);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_SEA_DOG_DRINK:
+ DoCastSelf(SPELL_DRINK_TANKARD, false);
+ _events.Repeat(6s, 20s);
+ break;
+ case EVENT_SEA_DOG_ROAM:
+ me->GetMotionMaster()->MoveCirclePath(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 1.5f, true, 4);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+private:
+ EventMap _events;
+};
+
+struct npc_silverpine_orc_sea_dog_sick : public ScriptedAI
+{
+ npc_silverpine_orc_sea_dog_sick(Creature* creature) : ScriptedAI(creature) {}
+
+ void JustAppeared() override
+ {
+ DoCastSelf(SPELL_COSMETIC_FEELING_SICK, false);
+
+ _events.ScheduleEvent(EVENT_SEA_DOG_VOMIT, 5s, 20s);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_SEA_DOG_VOMIT:
+ DoCastSelf(SPELL_SICK, false);
+ _events.Repeat(25s, 50s);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+private:
+ EventMap _events;
+};
+
+// Note: we're not using the generic spell for this one because Orc Sea Pup is unsummoned 3s later.
+// 83840 - Despawn All Summons
+class spell_silverpine_despawn_all_summons_steel_thunder : public SpellScript
+{
+ PrepareSpellScript(spell_silverpine_despawn_all_summons_steel_thunder);
+
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ Creature* target = GetHitCreature();
+
+ if (target->GetOwner() == caster)
+ target->DespawnOrUnsummon(3s);
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_silverpine_despawn_all_summons_steel_thunder::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
+
+enum OrcSeaPup
+{
+ NPC_ORC_CRATE = 44915,
+
+ SPELL_SUMMON_ORC_CRATE = 83835,
+ SPELL_EJECT_ALL_PASSENGERS = 68576,
+ SPELL_ANIM_DEAD = 98190,
+ SPELL_DESPAWN_ALL_SUMMONS_STEEL_THUNDER = 83840,
+
+ EVENT_ORC_PUP_JUST_SUMMONED = 1,
+ EVENT_ORC_PUP_REMOVE_PROTECTION = 2,
+ EVENT_ORC_PUP_TALK = 3,
+ EVENT_ORC_PUP_DELIVER_CRATES = 4,
+
+ TALK_ORC_PUP_SUMMONED = 0,
+ TALK_ORC_PUP_DELIVER_CRATES = 6,
+ TALK_ORC_PUP_WORN_OFF = 7
+};
+
+// 44914 - Orc Sea Pup
+struct npc_silverpine_orc_sea_pup : public VehicleAI
+{
+ npc_silverpine_orc_sea_pup(Creature* creature) : VehicleAI(creature), _isJustSummoned(true) { }
+
+ void JustAppeared() override
+ {
+ me->SetReactState(REACT_PASSIVE);
+ }
+
+ void IsSummonedBy(WorldObject* summoner) override
+ {
+ if (Player* player = summoner->ToPlayer())
+ {
+ me->RemoveNpcFlag(UNIT_NPC_FLAG_SPELLCLICK);
+
+ // Note: SummonPropertiesFlags::JoinSummonerSpawnGroup is NYI.
+ me->GetMotionMaster()->MoveFollow(player, 2.0f, float(M_PI / 2.0f));
+
+ if (player->GetQuestStatus(QUEST_STEEL_THUNDER) == QUEST_STATUS_INCOMPLETE)
+ {
+ _events.ScheduleEvent(EVENT_ORC_PUP_JUST_SUMMONED, 1s);
+ _events.ScheduleEvent(EVENT_ORC_PUP_REMOVE_PROTECTION, 1s + 500ms);
+ _events.ScheduleEvent(EVENT_ORC_PUP_TALK, 5s);
+
+ int c = player->GetReqKillOrCastCurrentCount(QUEST_STEEL_THUNDER, NPC_ORC_CRATE);
+
+ for (int i = 0; i < c; i++)
+ DoCastSelf(SPELL_SUMMON_ORC_CRATE);
+ }
+ }
+ }
+
+ void PassengerBoarded(Unit* passenger, int8 seatId, bool apply) override
+ {
+ TempSummon* tempSummon = me->ToTempSummon();
+ if (!tempSummon)
+ return;
+
+ Creature* passengerCreature = passenger->ToCreature();
+ if (!passengerCreature)
+ return;
+
+ if (passenger->GetEntry() != NPC_ORC_CRATE)
+ return;
+
+ if (apply)
+ {
+ if (_isJustSummoned)
+ return;
+
+ if (Unit* summoner = tempSummon->GetSummonerUnit())
+ Talk(seatId + 1, summoner);
+ }
+ else
+ passengerCreature->DespawnOrUnsummon(3s);
+ }
+
+ void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
+ {
+ TempSummon* tempSummon = me->ToTempSummon();
+ if (!tempSummon)
+ return;
+
+ switch (spellInfo->Id)
+ {
+ case SPELL_SEA_PUP_TRIGGER:
+ {
+ if (Unit* summoner = tempSummon->GetSummonerUnit())
+ {
+ Talk(TALK_ORC_PUP_DELIVER_CRATES, summoner);
+
+ summoner->CastSpell(nullptr, SPELL_DESPAWN_ALL_SUMMONS_STEEL_THUNDER, true);
+
+ _events.CancelEvent(EVENT_ORC_PUP_TALK);
+ }
+ break;
+ }
+
+ case SPELL_DESPAWN_ALL_SUMMONS_STEEL_THUNDER:
+ _events.ScheduleEvent(EVENT_ORC_PUP_DELIVER_CRATES, 1s + 250ms);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ TempSummon* tempSummon = me->ToTempSummon();
+ if (!tempSummon)
+ return;
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_ORC_PUP_JUST_SUMMONED:
+ if (Unit* summoner = tempSummon->GetSummonerUnit())
+ Talk(TALK_ORC_PUP_SUMMONED, summoner);
+ break;
+
+ case EVENT_ORC_PUP_TALK:
+ {
+ if (Vehicle* vehicle = me->GetVehicleKit())
+ {
+ if (vehicle->IsVehicleInUse())
+ {
+ if (Unit* summoner = tempSummon->GetSummonerUnit())
+ Talk(TALK_ORC_PUP_WORN_OFF, summoner);
+ }
+ }
+ _events.ScheduleEvent(EVENT_ORC_PUP_TALK, 30s, 75s);
+ break;
+ }
+
+ case EVENT_ORC_PUP_REMOVE_PROTECTION:
+ _isJustSummoned = false;
+ break;
+
+ case EVENT_ORC_PUP_DELIVER_CRATES:
+ DoCastSelf(SPELL_EJECT_ALL_PASSENGERS);
+ me->RemoveNpcFlag(UNIT_NPC_FLAG_SPELLCLICK);
+ me->SetStandState(UNIT_STAND_STATE_DEAD);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+private:
+ EventMap _events;
+ bool _isJustSummoned;
+};
+
+enum OrcCrate
+{
+ NPC_ORC_SEA_PUP = 44914
+};
+
+// 44915 - Orc Crate
+struct npc_silverpine_orc_crate : public ScriptedAI
+{
+ npc_silverpine_orc_crate(Creature* creature) : ScriptedAI(creature) { }
+
+ void IsSummonedBy(WorldObject* summoner) override
+ {
+ if (summoner->GetEntry() == NPC_ORC_SEA_PUP)
+ {
+ me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE);
+
+ me->EnterVehicle(summoner->ToUnit());
+ }
+ }
+};
+
+enum PickUpOrcCrate
+{
+ SPELL_KILL_CREDIT_SEA_DOG_CRATE = 83843
+};
+
+// 83838 - Pick Up Orc Crate
+class spell_silverpine_pick_up_orc_crate : public SpellScript
+{
+ PrepareSpellScript(spell_silverpine_pick_up_orc_crate);
+
+ bool Validate(SpellInfo const* /*spellInfi*/) override
+ {
+ return ValidateSpellInfo
+ ({
+ SPELL_SUMMON_ORC_CRATE,
+ SPELL_KILL_CREDIT_SEA_DOG_CRATE
+ });
+ }
+
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ GetHitUnit()->CastSpell(nullptr, SPELL_SUMMON_ORC_CRATE);
+ GetHitUnit()->CastSpell(nullptr, SPELL_KILL_CREDIT_SEA_DOG_CRATE);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_silverpine_pick_up_orc_crate::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
+
+enum ForestEttin
+{
+ NPC_MUTANT_BUSH_CHICKEN = 44935,
+
+ SPELL_HAULING_TIMBER = 88361,
+ SPELL_BONK = 80146,
+ SPELL_LOG_SMASH = 88421,
+ SPELL_RIDE_REVERSE_CAST_ITS_POISONOUS = 83904,
+ SPELL_ETTIN_MOUTH = 83907,
+ SPELL_BUSH_EXPLOSION = 83903,
+ SPELL_RELEASE_BUSH_CHICKEN = 83902,
+
+ EVENT_LOG_SMASH = 1,
+ EVENT_BONK = 2,
+ EVENT_GRAB_BUSH_CHICKEN = 3,
+ EVENT_AFTER_GRABBING_BUSH_CHICKEN = 4,
+ EVENT_BUSH_CHICKEN_CHANGE_SITS = 5,
+ EVENT_BUSH_CHICKEN_BLASTS = 6,
+ EVENT_RESET = 7,
+
+ ACTION_NOTICE_CHICKEN = 1,
+
+ SEAT_ETTIN_ARM = 0,
+ SEAT_ETTIN_MOUTH = 1,
+
+ SOUND_CHICKEN_MOUNT_WOUND = 15936
+};
+
+// 44367 - Forest Ettin
+struct npc_silverpine_forest_ettin : public ScriptedAI
+{
+ npc_silverpine_forest_ettin(Creature* creature) : ScriptedAI(creature), _isGrabbingBushChicken(false) { }
+
+ void JustAppeared() override
+ {
+ DoCastSelf(SPELL_HAULING_TIMBER, false);
+ }
+
+ void Reset() override
+ {
+ _events.Reset();
+
+ _isGrabbingBushChicken = false;
+
+ me->ResumeMovement();
+
+ _chickenGUID.Clear();
+ _playerGUID.Clear();
+ }
+
+ void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override
+ {
+ if (spellInfo->Id == SPELL_RELEASE_BUSH_CHICKEN)
+ _playerGUID = caster->GetGUID();
+ }
+
+ void SetGUID(ObjectGuid const& guid, int32 id) override
+ {
+ switch (id)
+ {
+ case NPC_MUTANT_BUSH_CHICKEN:
+ _chickenGUID = guid;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void PassengerBoarded(Unit* /*passenger*/, int8 seatId, bool apply) override
+ {
+ if (!_isGrabbingBushChicken)
+ return;
+
+ if (apply)
+ {
+ if (seatId == SEAT_ETTIN_ARM)
+ _events.ScheduleEvent(EVENT_AFTER_GRABBING_BUSH_CHICKEN, 2s);
+ else if (seatId == SEAT_ETTIN_MOUTH)
+ _events.ScheduleEvent(EVENT_BUSH_CHICKEN_BLASTS, 2s);
+ }
+ }
+
+ void DoAction(int32 param) override
+ {
+ switch (param)
+ {
+ case ACTION_NOTICE_CHICKEN:
+ {
+ if (Creature* mutantBushChicken = ObjectAccessor::GetCreature(*me, _chickenGUID))
+ {
+ me->PauseMovement();
+
+ me->SetFacingToObject(mutantBushChicken);
+
+ _events.ScheduleEvent(EVENT_GRAB_BUSH_CHICKEN, 1s + 250ms);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* spellInfo /*= nullptr*/) override
+ {
+ if (!spellInfo)
+ return;
+
+ // Hackfix: according to BasePoints damage is around 90k, which is wrong. On retail, it deals 80% of its health points only.
+ // Also, Mutant Bush Chicken is summoned as not player-controlled (not blue-taped), though it uses the same summonProperties's
+ // Control and Slot as some other summoning spells from this zone.
+ if (spellInfo->Id == SPELL_BUSH_EXPLOSION)
+ damage = 0;
+ }
+
+ void JustEngagedWith(Unit* /*who*/) override
+ {
+ _events.ScheduleEvent(EVENT_BONK, 2s);
+ _events.ScheduleEvent(EVENT_LOG_SMASH, 8s);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_BONK:
+ DoCastVictim(SPELL_BONK);
+ _events.Repeat(9s, 11s);
+ break;
+
+ case EVENT_LOG_SMASH:
+ DoCastVictim(SPELL_LOG_SMASH);
+ _events.Repeat(15s, 16s);
+ break;
+
+ case EVENT_GRAB_BUSH_CHICKEN:
+ {
+ if (Creature* mutantBushChicken = ObjectAccessor::GetCreature(*me, _chickenGUID))
+ {
+ _isGrabbingBushChicken = true;
+
+ me->CastSpell(mutantBushChicken, SPELL_RIDE_REVERSE_CAST_ITS_POISONOUS, true);
+
+ _events.ScheduleEvent(EVENT_RESET, 25s);
+ }
+ break;
+ }
+
+ case EVENT_AFTER_GRABBING_BUSH_CHICKEN:
+ me->ResumeMovement();
+ _events.ScheduleEvent(EVENT_BUSH_CHICKEN_CHANGE_SITS, 2s);
+ break;
+
+ case EVENT_BUSH_CHICKEN_CHANGE_SITS:
+ {
+ if (Creature* mutantBushChicken = ObjectAccessor::GetCreature(*me, _chickenGUID))
+ {
+ mutantBushChicken->CastSpell(me, SPELL_ETTIN_MOUTH, true);
+
+ if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID))
+ {
+ if (player->GetQuestStatus(QUEST_ITS_ONLY_POISONOUS_IF_YOU_INGEST_IT) == QUEST_STATUS_INCOMPLETE)
+ mutantBushChicken->PlayDirectSound(SOUND_CHICKEN_MOUNT_WOUND, player);
+ }
+ }
+ break;
+ }
+
+ case EVENT_BUSH_CHICKEN_BLASTS:
+ {
+ if (Creature* mutantBushChicken = ObjectAccessor::GetCreature(*me, _chickenGUID))
+ {
+ mutantBushChicken->CastSpell(me, SPELL_BUSH_EXPLOSION, true);
+
+ if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID))
+ AttackStart(player);
+ }
+ break;
+ }
+
+ case EVENT_RESET:
+ Reset();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!UpdateVictim())
+ return;
+
+ DoMeleeAttackIfReady();
+ }
+
+private:
+ EventMap _events;
+ ObjectGuid _chickenGUID;
+ ObjectGuid _playerGUID;
+ bool _isGrabbingBushChicken;
+};
+
+enum MutantBushChicken
+{
+ NPC_FOREST_ETTIN = 44367,
+
+ EVENT_MOVE_TO_FOREST_ETTIN = 1,
+
+ POINT_NEAR_FOREST_ETTIN = 1
+};
+
+// 44935 - Mutant Bush Chicken
+struct npc_silverpine_mutant_bush_chicken : public ScriptedAI
+{
+ npc_silverpine_mutant_bush_chicken(Creature* creature) : ScriptedAI(creature) { }
+
+ void JustAppeared() override
+ {
+ me->SetUnitFlag(UNIT_FLAG_IMMUNE_TO_PC);
+ me->SetUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC);
+ }
+
+ void IsSummonedBy(WorldObject* summoner) override
+ {
+ if (Player* player = summoner->ToPlayer())
+ {
+ if (player->GetQuestStatus(QUEST_ITS_ONLY_POISONOUS_IF_YOU_INGEST_IT) == QUEST_STATUS_INCOMPLETE)
+ me->PlayDirectSound(SOUND_CHICKEN_MOUNT_WOUND, player);
+ }
+ }
+
+ void MovementInform(uint32 type, uint32 id) override
+ {
+ if (type != POINT_MOTION_TYPE)
+ return;
+
+ if (id == POINT_NEAR_FOREST_ETTIN)
+ {
+ if (Creature* forestEttin = ObjectAccessor::GetCreature(*me, _forestEttinGUID))
+ {
+ if (forestEttin->IsAIEnabled())
+ forestEttin->AI()->DoAction(ACTION_NOTICE_CHICKEN);
+ }
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ TempSummon* tempSummon = me->ToTempSummon();
+ if (!tempSummon)
+ return;
+
+ if (Unit* summoner = tempSummon->GetSummonerUnit())
+ {
+ // Note: SummonPropertiesFlags::DespawnOnSummonerLogout is NYI.
+ if (!summoner->IsInWorld())
+ me->DespawnOrUnsummon();
+ }
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_MOVE_TO_FOREST_ETTIN:
+ if (Creature* forestEttin = ObjectAccessor::GetCreature(*me, _forestEttinGUID))
+ me->GetMotionMaster()->MovePoint(POINT_NEAR_FOREST_ETTIN, forestEttin->GetPosition(), false);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ void CheckForForestEttin(Unit* forestEttin)
+ {
+ _forestEttinGUID = forestEttin->GetGUID();
+
+ me->SetFacingToObject(forestEttin);
+
+ _events.ScheduleEvent(EVENT_MOVE_TO_FOREST_ETTIN, 1s);
+ }
+
+private:
+ EventMap _events;
+ ObjectGuid _forestEttinGUID;
+};
+
+// 83902 - Release Diseased Mutant Bush Chicken
+class spell_silverpine_release_diseased_mutant_bush_chicken : public SpellScript
+{
+ PrepareSpellScript(spell_silverpine_release_diseased_mutant_bush_chicken);
+
+ void HandleHit(SpellEffIndex /*effIndex*/)
+ {
+ Unit* target = GetHitUnit();
+
+ if (Unit* caster = GetCaster())
+ {
+ std::vector<Creature*> chickens;
+ caster->GetCreatureListWithEntryInGrid(chickens, NPC_MUTANT_BUSH_CHICKEN, 5.0f);
+ for (Creature* mutantBushChicken : chickens)
+ {
+ if (mutantBushChicken->GetOwner() != caster)
+ continue;
+
+ if (target->IsAIEnabled())
+ target->GetAI()->SetGUID(mutantBushChicken->GetGUID(), NPC_MUTANT_BUSH_CHICKEN);
+
+ if (npc_silverpine_mutant_bush_chicken* mutantBushChickenAI = CAST_AI(npc_silverpine_mutant_bush_chicken, mutantBushChicken->AI()))
+ mutantBushChickenAI->CheckForForestEttin(target);
+ }
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_silverpine_release_diseased_mutant_bush_chicken::HandleHit, EFFECT_1, SPELL_EFFECT_DUMMY);
+ }
+};
+
+enum WebbedVictim
+{
+ SPELL_FREE_WEBBED_VICTIM = 83927,
+ SPELL_FREE_WEBBED_VICTIM_RANDOM = 83919
+};
+
+// 44941 - Webbed Victim
+struct npc_silverpine_webbed_victim : public ScriptedAI
+{
+ npc_silverpine_webbed_victim(Creature* creature) : ScriptedAI(creature) { }
+
+ void JustAppeared() override
+ {
+ me->SetReactState(REACT_PASSIVE);
+ }
+
+ void JustDied(Unit* killer) override
+ {
+ if (Player* player = killer->ToPlayer())
+ {
+ if (player->GetQuestStatus(QUEST_LOST_IN_THE_DARKNESS) == QUEST_STATUS_INCOMPLETE)
+ {
+ if (roll_chance_i(50))
+ player->CastSpell(me, SPELL_FREE_WEBBED_VICTIM, true);
+ else
+ player->CastSpell(me, SPELL_FREE_WEBBED_VICTIM_RANDOM, true);
+ }
+ }
+ }
+};
+
+enum FreeWebbedVictim
+{
+ NPC_BLOODFANG_SCAVENGER = 44547,
+ NPC_RABID_DOG = 1766,
+ NPC_GIANT_RABBID_BEAR = 1797,
+
+ DATA_RANDOM_0 = 0,
+ DATA_RANDOM_1 = 1,
+ DATA_RANDOM_2 = 2,
+ DATA_RANDOM_3 = 3
+};
+
+// 83919 - Free Webbed Victim
+class spell_silverpine_free_webbed_victim_random : public SpellScript
+{
+ PrepareSpellScript(spell_silverpine_free_webbed_victim_random);
+
+ void HandleHit(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ uint32 randomCreature = 0;
+
+ /*
+ * Note: this spell is always cast when there's nothing in the web or there's a foe. Also,
+ * there are 4 serverside spells for this, but we're unable to know which one does what exactly:
+ * 83922 - (Serverside/Non-DB2) Free Webbed Victim, 83923 - (Serverside/Non-DB2) Free Webbed Victim,
+ * 83924 - (Serverside/Non-DB2) Free Webbed Victim, 83925 - (Serverside/Non-DB2) Free Webbed Victim.
+ * It is safe to assume that each summons one of the three foes, and the other summons nothing.
+ * 83927 - Free Webbed Victim summons orcs. We could try finding summonProperties, but it is hard.
+ */
+
+ switch (urand(DATA_RANDOM_0, DATA_RANDOM_3))
+ {
+ case DATA_RANDOM_0:
+ randomCreature = NPC_BLOODFANG_SCAVENGER;
+ break;
+ case DATA_RANDOM_1:
+ randomCreature = NPC_RABID_DOG;
+ break;
+ case DATA_RANDOM_2:
+ randomCreature = NPC_GIANT_RABBID_BEAR;
+ break;
+ case DATA_RANDOM_3:
+ randomCreature = 0;
+ break;
+ default:
+ break;
+ }
+
+ if (randomCreature != 0)
+ {
+ if (Creature* randomAggresiveCreature = caster->SummonCreature(randomCreature, GetHitUnit()->GetPosition()))
+ randomAggresiveCreature->Attack(caster, true);
+ }
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_silverpine_free_webbed_victim_random::HandleHit, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+};
+
+enum WebbedOrcSeaDog
+{
+ NPC_ORC_SEA_DOG = 44942,
+
+ SPELL_SINISTER_STRIKE = 60195,
+
+ EVENT_WEBBEB_ORC_CHECK_PLAYER = 1,
+ EVENT_WEBBEB_ORC_TALK = 2,
+ EVENT_SINISTER_STRIKE = 3,
+
+ TALK_WEBBEB_ORC_FREED = 0
+};
+
+// 44942 - Orc Sea Dog
+struct npc_silverpine_orc_sea_dog : public ScriptedAI
+{
+ npc_silverpine_orc_sea_dog(Creature* creature) : ScriptedAI(creature) {}
+
+ void JustAppeared() override
+ {
+ me->SetPowerType(POWER_ENERGY);
+ me->SetMaxPower(POWER_ENERGY, 100);
+ me->SetPower(POWER_ENERGY, 100, true);
+
+ // Note: SummonPropertiesFlags::HelpWhenSummonedInCombat is NYI.
+ me->SetReactState(REACT_ASSIST);
+ }
+
+ void IsSummonedBy(WorldObject* summoner) override
+ {
+ if (Player* player = summoner->ToPlayer())
+ {
+ player->KilledMonsterCredit(NPC_ORC_SEA_DOG);
+
+ // Note: SummonPropertiesFlags::JoinSummonerSpawnGroup is NYI. They should be faned around the back of the player depending on how many summons they have (Pets and Guardians included).
+ me->GetMotionMaster()->MoveFollow(player, 5.0f, frand(1.57f, 4.71f));
+
+ _events.ScheduleEvent(EVENT_WEBBEB_ORC_CHECK_PLAYER, 1s);
+ _events.ScheduleEvent(EVENT_WEBBEB_ORC_TALK, 1s + 500ms);
+ }
+ }
+
+ void Reset() override
+ {
+ _events.CancelEvent(EVENT_SINISTER_STRIKE);
+ }
+
+ void JustEngagedWith(Unit* /*who*/) override
+ {
+ _events.ScheduleEvent(EVENT_SINISTER_STRIKE, 2s, 4s);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ TempSummon* tempSummon = me->ToTempSummon();
+ if (!tempSummon)
+ return;
+
+ if (Unit* summoner = tempSummon->GetSummonerUnit())
+ {
+ // Note: SummonPropertiesFlags::DespawnOnSummonerDeath and SummonPropertiesFlags::DespawnOnSummonerLogout are NYI.
+ if (!summoner->IsAlive() || !summoner->IsInWorld())
+ me->DespawnOrUnsummon();
+ }
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_WEBBEB_ORC_TALK:
+ if (Unit* summoner = tempSummon->GetSummonerUnit())
+ Talk(TALK_WEBBEB_ORC_FREED, summoner);
+ break;
+
+ case EVENT_SINISTER_STRIKE:
+ DoCastVictim(SPELL_SINISTER_STRIKE);
+ _events.Repeat(6s, 8s);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!UpdateVictim())
+ return;
+
+ DoMeleeAttackIfReady();
+ }
+
+private:
+ EventMap _events;
+};
+
+enum SkitterwebMatriarch
+{
+ NPC_SKITTERWEB_STALKER = 44908,
+
+ SPELL_SKITTERWEB = 83827,
+ SPELL_SUMMNON_SPIDERLINGS = 87084,
+ SPELL_VENOM_SPLASH = 79607,
+
+ EVENT_MATRIARCH_AGGRO = 1,
+ EVENT_SUMMON_SPIDERLINGS = 4,
+ EVENT_VENOM_SPLASH = 5,
+
+ ANIMKIT_MATRIARCH_INTERACT = 1,
+ ANIMKIT_MATRIARCH_POSITION1 = 865,
+ ANIMKIT_MATRIARCH_POSITION2 = 866
+};
+
+// 44906 - Skitterweb Matriarch
+struct npc_silverpine_skitterweb_matriarch : public ScriptedAI
+{
+ npc_silverpine_skitterweb_matriarch(Creature* creature) : ScriptedAI(creature), _alreadyPulled(false) { }
+
+ void JustAppeared() override
+ {
+ _alreadyPulled = false;
+
+ me->SetDisableGravity(true);
+ me->SetHover(true);
+
+ me->SetAIAnimKitId(ANIMKIT_MATRIARCH_POSITION1);
+
+ me->SetReactState(REACT_PASSIVE);
+
+ me->CastSpell(nullptr, SPELL_SKITTERWEB, true);
+
+ std::vector<Creature*> stalkers;
+ me->GetCreatureListWithEntryInGrid(stalkers, NPC_MUTANT_BUSH_CHICKEN, 5.0f);
+ for (Creature* stalker : stalkers)
+ _stalkerGUIDs.push_back(stalker->GetGUID());
+ }
+
+ void Reset() override
+ {
+ _events.Reset();
+ }
+
+ void JustEngagedWith(Unit* /*who*/) override
+ {
+ if (!_alreadyPulled)
+ {
+ _alreadyPulled = true;
+
+ me->SetAIAnimKitId(ANIMKIT_RESET);
+ me->PlayOneShotAnimKitId(ANIMKIT_MATRIARCH_POSITION2);
+
+ _events.ScheduleEvent(EVENT_MATRIARCH_AGGRO, 2s + 500ms);
+ }
+ else
+ ScheduleEvents();
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_MATRIARCH_AGGRO:
+ me->SetDisableGravity(false);
+ me->SetHover(false);
+ me->GetMotionMaster()->MoveFall();
+ _events.ScheduleEvent(EVENT_MATRIARCH_AGGRO + 1, 1s);
+ break;
+
+ case EVENT_MATRIARCH_AGGRO + 1:
+ for (ObjectGuid const& stalkerGUID : _stalkerGUIDs)
+ {
+ if (Creature* stalker = ObjectAccessor::GetCreature(*me, stalkerGUID))
+ stalker->RemoveAura(SPELL_SKITTERWEB);
+ }
+ me->SetAIAnimKitId(ANIMKIT_MATRIARCH_INTERACT);
+ me->SetHomePosition(me->GetPosition());
+ _events.ScheduleEvent(EVENT_MATRIARCH_AGGRO + 2, 1s);
+ break;
+
+ case EVENT_MATRIARCH_AGGRO + 2:
+ me->SetReactState(REACT_AGGRESSIVE);
+ ScheduleEvents();
+ break;
+
+ case EVENT_SUMMON_SPIDERLINGS:
+ DoCastSelf(SPELL_SUMMNON_SPIDERLINGS);
+ break;
+
+ case EVENT_VENOM_SPLASH:
+ DoCastVictim(SPELL_VENOM_SPLASH);
+ _events.Repeat(15s, 18s);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!UpdateVictim())
+ return;
+
+ DoMeleeAttackIfReady();
+ }
+
+ void ScheduleEvents()
+ {
+ _events.ScheduleEvent(EVENT_SUMMON_SPIDERLINGS, 2s);
+ _events.ScheduleEvent(EVENT_VENOM_SPLASH, 4s, 7s);
+ }
+
+private:
+ EventMap _events;
+ bool _alreadyPulled;
+ std::vector<ObjectGuid> _stalkerGUIDs;
+};
+
void AddSC_silverpine_forest()
{
/* Vehicles */
@@ -2608,4 +4088,30 @@ void AddSC_silverpine_forest()
RegisterCreatureAI(npc_silverpine_armoire);
RegisterCreatureAI(npc_silverpine_lord_darius_crowley_exsanguinate);
RegisterCreatureAI(npc_silverpine_packleader_ivar_bloodfang_exsanguinate);
+
+ /* Forsaken Rear Guard */
+
+ new at_silverpine_forsaken_rear_guard();
+ RegisterCreatureAI(npc_silverpine_salty_rocka);
+ RegisterCreatureAI(npc_silverpine_apothecary_wormcrud);
+ RegisterCreatureAI(npc_silverpine_admiral_hatchet);
+ RegisterCreatureAI(npc_silverpine_orc_sea_dog_sick);
+ RegisterCreatureAI(npc_silverpine_orc_sea_dog_not_sick);
+
+ /* North Tide's Beachhead */
+
+ RegisterSpellScript(spell_silverpine_despawn_all_summons_steel_thunder);
+ RegisterCreatureAI(npc_silverpine_orc_sea_pup);
+ RegisterCreatureAI(npc_silverpine_orc_crate);
+ RegisterSpellScript(spell_silverpine_pick_up_orc_crate);
+ RegisterCreatureAI(npc_silverpine_forest_ettin);
+ RegisterCreatureAI(npc_silverpine_mutant_bush_chicken);
+ RegisterSpellScript(spell_silverpine_release_diseased_mutant_bush_chicken);
+
+ /* The Skittering Dark */
+
+ RegisterCreatureAI(npc_silverpine_webbed_victim);
+ RegisterSpellScript(spell_silverpine_free_webbed_victim_random);
+ RegisterCreatureAI(npc_silverpine_orc_sea_dog);
+ RegisterCreatureAI(npc_silverpine_skitterweb_matriarch);
}
diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
index 8cb2b7d547c..1acc02ae655 100644
--- a/src/server/scripts/Spells/spell_generic.cpp
+++ b/src/server/scripts/Spells/spell_generic.cpp
@@ -5157,6 +5157,30 @@ class spell_gen_reverse_cast_target_to_caster_triggered: public SpellScript
}
};
+// Note: this spell unsummons any creature owned by the caster. Set appropriate target conditions on the DB.
+// 84065 - Despawn All Summons
+// 83935 - Despawn All Summons
+class spell_gen_despawn_all_summons_owned_by_caster : public SpellScript
+{
+ PrepareSpellScript(spell_gen_despawn_all_summons_owned_by_caster);
+
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ Creature* target = GetHitCreature();
+
+ if (target->GetOwner() == caster)
+ target->DespawnOrUnsummon();
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_gen_despawn_all_summons_owned_by_caster::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
+
void AddSC_generic_spell_scripts()
{
RegisterSpellScript(spell_gen_absorb0_hitlimit1);
@@ -5316,4 +5340,5 @@ void AddSC_generic_spell_scripts()
RegisterSpellScript(spell_gen_ancestral_call);
RegisterSpellScript(spell_gen_eject_passengers_3_8);
RegisterSpellScript(spell_gen_reverse_cast_target_to_caster_triggered);
+ RegisterSpellScript(spell_gen_despawn_all_summons_owned_by_caster);
}