/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "ScriptMgr.h" #include "AreaTrigger.h" #include "AreaTriggerAI.h" #include "DB2Structure.h" #include "GameObject.h" #include "GameTime.h" #include "MotionMaster.h" #include "ObjectAccessor.h" #include "PathGenerator.h" #include "ScriptedCreature.h" #include "Spell.h" #include "SpellInfo.h" #include "Player.h" #include "TemporarySummon.h" #include "World.h" #include "ZoneScript.h" /*###### ## at_coilfang_waterfall ######*/ enum CoilfangGOs { GO_COILFANG_WATERFALL = 184212 }; class AreaTrigger_at_coilfang_waterfall : public AreaTriggerScript { public: AreaTrigger_at_coilfang_waterfall() : AreaTriggerScript("at_coilfang_waterfall") { } bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { if (GameObject* go = GetClosestGameObjectWithEntry(player, GO_COILFANG_WATERFALL, 35.0f)) if (go->getLootState() == GO_READY) go->UseDoorOrButton(); return false; } }; /*##### ## at_legion_teleporter #####*/ enum LegionTeleporter { SPELL_TELE_A_TO = 37387, QUEST_GAINING_ACCESS_A = 10589, SPELL_TELE_H_TO = 37389, QUEST_GAINING_ACCESS_H = 10604 }; class AreaTrigger_at_legion_teleporter : public AreaTriggerScript { public: AreaTrigger_at_legion_teleporter() : AreaTriggerScript("at_legion_teleporter") { } bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { if (player->IsAlive() && !player->IsInCombat()) { if (player->GetTeam() == ALLIANCE && player->GetQuestRewardStatus(QUEST_GAINING_ACCESS_A)) { player->CastSpell(player, SPELL_TELE_A_TO, false); return true; } if (player->GetTeam() == HORDE && player->GetQuestRewardStatus(QUEST_GAINING_ACCESS_H)) { player->CastSpell(player, SPELL_TELE_H_TO, false); return true; } return false; } return false; } }; /*###### ## at_scent_larkorwi ######*/ enum ScentLarkorwi { QUEST_SCENT_OF_LARKORWI = 4291, NPC_LARKORWI_MATE = 9683 }; class AreaTrigger_at_scent_larkorwi : public AreaTriggerScript { public: AreaTrigger_at_scent_larkorwi() : AreaTriggerScript("at_scent_larkorwi") { } bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { if (!player->isDead() && player->GetQuestStatus(QUEST_SCENT_OF_LARKORWI) == QUEST_STATUS_INCOMPLETE) { if (!player->FindNearestCreature(NPC_LARKORWI_MATE, 15)) player->SummonCreature(NPC_LARKORWI_MATE, player->GetPositionX()+5, player->GetPositionY(), player->GetPositionZ(), 3.3f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 100s); } return false; } }; /*###### ## at_sholazar_waygate ######*/ enum Waygate { SPELL_SHOLAZAR_TO_UNGORO_TELEPORT = 52056, SPELL_UNGORO_TO_SHOLAZAR_TELEPORT = 52057, AT_SHOLAZAR = 5046, AT_UNGORO = 5047, QUEST_THE_MAKERS_OVERLOOK = 12613, QUEST_THE_MAKERS_PERCH = 12559, QUEST_MEETING_A_GREAT_ONE = 13956, }; class AreaTrigger_at_sholazar_waygate : public AreaTriggerScript { public: AreaTrigger_at_sholazar_waygate() : AreaTriggerScript("at_sholazar_waygate") { } bool OnTrigger(Player* player, AreaTriggerEntry const* trigger) override { if (!player->isDead() && (player->GetQuestStatus(QUEST_MEETING_A_GREAT_ONE) != QUEST_STATUS_NONE || (player->GetQuestStatus(QUEST_THE_MAKERS_OVERLOOK) == QUEST_STATUS_REWARDED && player->GetQuestStatus(QUEST_THE_MAKERS_PERCH) == QUEST_STATUS_REWARDED))) { switch (trigger->ID) { case AT_SHOLAZAR: player->CastSpell(player, SPELL_SHOLAZAR_TO_UNGORO_TELEPORT, true); break; case AT_UNGORO: player->CastSpell(player, SPELL_UNGORO_TO_SHOLAZAR_TELEPORT, true); break; } } return false; } }; /*###### ## at_nats_landing ######*/ enum NatsLanding { QUEST_NATS_BARGAIN = 11209, SPELL_FISH_PASTE = 42644, NPC_LURKING_SHARK = 23928 }; class AreaTrigger_at_nats_landing : public AreaTriggerScript { public: AreaTrigger_at_nats_landing() : AreaTriggerScript("at_nats_landing") { } bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { if (!player->IsAlive() || !player->HasAura(SPELL_FISH_PASTE)) return false; if (player->GetQuestStatus(QUEST_NATS_BARGAIN) == QUEST_STATUS_INCOMPLETE) { if (!player->FindNearestCreature(NPC_LURKING_SHARK, 20.0f)) { if (Creature* shark = player->SummonCreature(NPC_LURKING_SHARK, -4246.243f, -3922.356f, -7.488f, 5.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 100s)) shark->AI()->AttackStart(player); return false; } } return true; } }; /*###### ## at_brewfest ######*/ enum Brewfest { NPC_TAPPER_SWINDLEKEG = 24711, NPC_IPFELKOFER_IRONKEG = 24710, AT_BREWFEST_DUROTAR = 4829, AT_BREWFEST_DUN_MOROGH = 4820, SAY_WELCOME = 4, AREATRIGGER_TALK_COOLDOWN = 5, // in seconds }; class AreaTrigger_at_brewfest : public AreaTriggerScript { public: AreaTrigger_at_brewfest() : AreaTriggerScript("at_brewfest") { // Initialize for cooldown _triggerTimes[AT_BREWFEST_DUROTAR] = _triggerTimes[AT_BREWFEST_DUN_MOROGH] = 0; } bool OnTrigger(Player* player, AreaTriggerEntry const* trigger) override { uint32 triggerId = trigger->ID; // Second trigger happened too early after first, skip for now if (GameTime::GetGameTime() - _triggerTimes[triggerId] < AREATRIGGER_TALK_COOLDOWN) return false; switch (triggerId) { case AT_BREWFEST_DUROTAR: if (Creature* tapper = player->FindNearestCreature(NPC_TAPPER_SWINDLEKEG, 20.0f)) tapper->AI()->Talk(SAY_WELCOME, player); break; case AT_BREWFEST_DUN_MOROGH: if (Creature* ipfelkofer = player->FindNearestCreature(NPC_IPFELKOFER_IRONKEG, 20.0f)) ipfelkofer->AI()->Talk(SAY_WELCOME, player); break; default: break; } _triggerTimes[triggerId] = GameTime::GetGameTime(); return false; } private: std::map _triggerTimes; }; /*###### ## at_area_52_entrance ######*/ enum Area52Entrance { SPELL_A52_NEURALYZER = 34400, NPC_SPOTLIGHT = 19913, SUMMON_COOLDOWN = 5, AT_AREA_52_SOUTH = 4472, AT_AREA_52_NORTH = 4466, AT_AREA_52_WEST = 4471, AT_AREA_52_EAST = 4422, }; class AreaTrigger_at_area_52_entrance : public AreaTriggerScript { public: AreaTrigger_at_area_52_entrance() : AreaTriggerScript("at_area_52_entrance") { _triggerTimes[AT_AREA_52_SOUTH] = _triggerTimes[AT_AREA_52_NORTH] = _triggerTimes[AT_AREA_52_WEST] = _triggerTimes[AT_AREA_52_EAST] = 0; } bool OnTrigger(Player* player, AreaTriggerEntry const* trigger) override { float x = 0.0f, y = 0.0f, z = 0.0f; if (!player->IsAlive()) return false; uint32 triggerId = trigger->ID; if (GameTime::GetGameTime() - _triggerTimes[triggerId] < SUMMON_COOLDOWN) return false; switch (triggerId) { case AT_AREA_52_EAST: x = 3044.176f; y = 3610.692f; z = 143.61f; break; case AT_AREA_52_NORTH: x = 3114.87f; y = 3687.619f; z = 143.62f; break; case AT_AREA_52_WEST: x = 3017.79f; y = 3746.806f; z = 144.27f; break; case AT_AREA_52_SOUTH: x = 2950.63f; y = 3719.905f; z = 143.33f; break; } player->SummonCreature(NPC_SPOTLIGHT, x, y, z, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 5s); player->AddAura(SPELL_A52_NEURALYZER, player); _triggerTimes[trigger->ID] = GameTime::GetGameTime(); return false; } private: std::map _triggerTimes; }; /*###### ## at_frostgrips_hollow ######*/ enum FrostgripsHollow { QUEST_THE_LONESOME_WATCHER = 12877, NPC_STORMFORGED_MONITOR = 29862, NPC_STORMFORGED_ERADICTOR = 29861, TYPE_WAYPOINT = 0, DATA_START = 0 }; Position const stormforgedMonitorPosition = {6963.95f, 45.65f, 818.71f, 4.948f}; Position const stormforgedEradictorPosition = {6983.18f, 7.15f, 806.33f, 2.228f}; class AreaTrigger_at_frostgrips_hollow : public AreaTriggerScript { public: AreaTrigger_at_frostgrips_hollow() : AreaTriggerScript("at_frostgrips_hollow") { stormforgedMonitorGUID.Clear(); stormforgedEradictorGUID.Clear(); } bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { if (player->GetQuestStatus(QUEST_THE_LONESOME_WATCHER) != QUEST_STATUS_INCOMPLETE) return false; Creature* stormforgedMonitor = ObjectAccessor::GetCreature(*player, stormforgedMonitorGUID); if (stormforgedMonitor) return false; Creature* stormforgedEradictor = ObjectAccessor::GetCreature(*player, stormforgedEradictorGUID); if (stormforgedEradictor) return false; stormforgedMonitor = player->SummonCreature(NPC_STORMFORGED_MONITOR, stormforgedMonitorPosition, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1min); if (stormforgedMonitor) { stormforgedMonitorGUID = stormforgedMonitor->GetGUID(); stormforgedMonitor->SetWalk(false); /// The npc would search an alternative way to get to the last waypoint without this unit state. stormforgedMonitor->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); stormforgedMonitor->GetMotionMaster()->MovePath((NPC_STORMFORGED_MONITOR * 100) << 3, false); } stormforgedEradictor = player->SummonCreature(NPC_STORMFORGED_ERADICTOR, stormforgedEradictorPosition, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1min); if (stormforgedEradictor) { stormforgedEradictorGUID = stormforgedEradictor->GetGUID(); stormforgedEradictor->GetMotionMaster()->MovePath((NPC_STORMFORGED_ERADICTOR * 100) << 3, false); } return true; } private: ObjectGuid stormforgedMonitorGUID; ObjectGuid stormforgedEradictorGUID; }; struct areatrigger_stormwind_teleport_unit : AreaTriggerAI { enum MiscIds { SPELL_DUST_IN_THE_STORMWIND = 312593, NPC_KILL_CREDIT_TELEPORT_STORMWIND = 160561 }; areatrigger_stormwind_teleport_unit(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } void OnUnitEnter(Unit* unit) override { Player* player = unit->ToPlayer(); if (!player) return; player->CastSpell(unit, SPELL_DUST_IN_THE_STORMWIND); player->KilledMonsterCredit(NPC_KILL_CREDIT_TELEPORT_STORMWIND); } }; void HandleBuffAreaTrigger(Player* player) { if (GameObject* buffObject = player->FindNearestGameObjectWithOptions(4.0f, { .StringId = "bg_buff_object" })) { buffObject->ActivateObject(GameObjectActions::Disturb, 0, player); buffObject->DespawnOrUnsummon(); } } struct areatrigger_battleground_buffs : AreaTriggerAI { areatrigger_battleground_buffs(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } void OnUnitEnter(Unit* unit) override { if (!unit->IsPlayer()) return; HandleBuffAreaTrigger(unit->ToPlayer()); } }; class AreaTrigger_at_battleground_buffs : public AreaTriggerScript { public: AreaTrigger_at_battleground_buffs() : AreaTriggerScript("at_battleground_buffs") { } bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { HandleBuffAreaTrigger(player); return true; } }; struct areatrigger_action_capture_flag : AreaTriggerAI { areatrigger_action_capture_flag(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } void OnUnitEnter(Unit* unit) override { if (!unit->IsPlayer()) return; Player* player = unit->ToPlayer(); if (ZoneScript* zoneScript = at->GetZoneScript()) if (zoneScript->CanCaptureFlag(at, player)) zoneScript->OnCaptureFlag(at, player); } }; // 18235 - Void Orb struct at_void_orb_harbinger : AreaTriggerAI { at_void_orb_harbinger(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } enum Spells { SPELL_VOID_ORB_DAMAGE = 273502, }; void OnInitialize() override { if (Unit* caster = at->GetCaster()) { at->SetOrientation(caster->GetOrientation()); Position destPos = caster->GetPosition(); at->MovePositionToFirstCollision(destPos, 35.0f, 0.0f); PathGenerator path(at); path.CalculatePath(destPos.GetPositionX(), destPos.GetPositionY(), destPos.GetPositionZ(), false); at->InitSplines(path.GetPath()); } } void OnDestinationReached() override { at->Remove(); } void OnUnitEnter(Unit* unit) override { Unit* caster = at->GetCaster(); if (!caster) return; if (caster->IsFriendlyTo(unit)) return; caster->CastSpell(unit, SPELL_VOID_ORB_DAMAGE); } }; // 18242 - Abyssal Portal struct at_abyssal_portal_harbinger : AreaTriggerAI { at_abyssal_portal_harbinger(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger), _remainingSummons(0) { } enum Spells { SPELL_ABYSSAL_PORTAL_SUMMON = 273587 }; void OnCreate(Spell const* creatingSpell) override { if (Unit* caster = at->GetCaster()) _remainingSummons = creatingSpell->GetSpellInfo()->GetEffect(EFFECT_0).CalcValue(caster); _scheduler.Schedule(500ms, [this](TaskContext task) { if (Unit* caster = at->GetCaster()) caster->CastSpell(at->GetRandomNearPosition(3.0f), SPELL_ABYSSAL_PORTAL_SUMMON, true); _remainingSummons--; if (_remainingSummons > 0) task.Repeat(1s); }); } void OnUpdate(uint32 diff) override { _scheduler.Update(diff); } private: TaskScheduler _scheduler; uint8 _remainingSummons; }; void AddSC_areatrigger_scripts() { new AreaTrigger_at_coilfang_waterfall(); new AreaTrigger_at_legion_teleporter(); new AreaTrigger_at_scent_larkorwi(); new AreaTrigger_at_sholazar_waygate(); new AreaTrigger_at_nats_landing(); new AreaTrigger_at_brewfest(); new AreaTrigger_at_area_52_entrance(); new AreaTrigger_at_frostgrips_hollow(); RegisterAreaTriggerAI(areatrigger_stormwind_teleport_unit); RegisterAreaTriggerAI(areatrigger_battleground_buffs); new AreaTrigger_at_battleground_buffs(); RegisterAreaTriggerAI(areatrigger_action_capture_flag); RegisterAreaTriggerAI(at_void_orb_harbinger); RegisterAreaTriggerAI(at_abyssal_portal_harbinger); }