/* * This file is part of the AzerothCore 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 Affero General Public License as published by the * Free Software Foundation; either version 3 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 Affero 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 "CombatAI.h" #include "CreatureScript.h" #include "GameObjectScript.h" #include "PassiveAI.h" #include "Player.h" #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" #include "ScriptedGossip.h" #include "SpellAuras.h" #include "SpellScript.h" #include "SpellScriptLoader.h" #include "Vehicle.h" #include "WaypointMgr.h" enum songOfWindandWater { NPC_SOWAW_WATER_ELEMENTAL = 28999, NPC_SOWAW_WIND_ELEMENTAL = 28985, NPC_SOWAW_WIND_MODEL = 14516, NPC_SOWAW_WATER_MODEL = 20076, }; class spell_q12726_song_of_wind_and_water : public SpellScript { PrepareSpellScript(spell_q12726_song_of_wind_and_water); void HandleHealPct(SpellEffIndex /*effIndex*/) { if (Creature* cr = GetHitCreature()) { //cr->UpdateEntry((cr->GetEntry() == NPC_SOWAW_WATER_ELEMENTAL ? NPC_SOWAW_WIND_ELEMENTAL : NPC_SOWAW_WATER_ELEMENTAL)); cr->SetDisplayId(cr->GetDisplayId() == NPC_SOWAW_WATER_MODEL ? NPC_SOWAW_WIND_MODEL : NPC_SOWAW_WATER_MODEL); if (Player* player = cr->GetCharmerOrOwnerPlayerOrPlayerItself()) { player->KilledMonsterCredit(cr->GetDisplayId() == NPC_SOWAW_WATER_MODEL ? 29008 : 29009); CreatureTemplate const* ct = sObjectMgr->GetCreatureTemplate(cr->GetDisplayId() == NPC_SOWAW_WIND_MODEL ? NPC_SOWAW_WIND_ELEMENTAL : NPC_SOWAW_WATER_ELEMENTAL); for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) cr->m_spells[i] = ct->spells[i]; player->VehicleSpellInitialize(); } } } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_q12726_song_of_wind_and_water::HandleHealPct, EFFECT_2, SPELL_EFFECT_HEAL_PCT); } }; enum AHerosBurden { SPELL_TOMB_OF_THE_HEARTLESS = 52182, SPELL_ARTRUIS_FROST_NOVA = 11831, SPELL_ARTRUIS_FROSTBOLT = 15530, SPELL_ARTRUIS_ICE_LANCE = 54261, SPELL_ARTRUIS_ICY_VEINS = 54792, SPELL_ARTRUIS_BINDING = 52185, NPC_JALOOT = 28667, NPC_ZEPIK = 28668, NPC_ARTRUIS = 28659, EVENT_CAST_FROST_BOLT = 1, EVENT_CAST_FROST_NOVA = 2, EVENT_CAST_ICE_LANCE = 3, EVENT_CAST_ICY_VEINS = 4, EVENT_ARTRUIS_HP_CHECK = 5, EVENT_ARTRUIS_TALK1 = 6, EVENT_ARTRUIS_TALK2 = 7, EVENT_ARTRUIS_TALK3 = 8, ACTION_BIND_MINIONS = 1, ACTION_MAKE_FRIENDLY = 2, GO_ARTRUIS_PHYLACTERY = 190777, // Texts SAY_TURNED_FRIENDLY = 0, // Zepik and Jaloot SAY_ARTRUIS_AGGRO = 0, SAY_ARTRUIS_TALK_1 = 1, SAY_ARTRUIS_TALK_2 = 2, SAY_ARTRUIS_TALK_3 = 3, SAY_ARTRUIS_SHIELD = 4, // Boss emote SAY_ARTRUIS_TALK_4 = 5, }; class npc_artruis_the_hearthless : public CreatureScript { public: npc_artruis_the_hearthless() : CreatureScript("npc_artruis_the_hearthless") { } struct npc_artruis_the_hearthlessAI : public ScriptedAI { npc_artruis_the_hearthlessAI(Creature* creature) : ScriptedAI(creature), summons(me) { } EventMap events; SummonList summons; void Reset() override { events.Reset(); summons.DespawnAll(); me->SetControlled(false, UNIT_STATE_STUNNED); Creature* cr; if ((cr = me->SummonCreature(NPC_JALOOT, 5616.91f, 3772.67f, -94.26f, 1.78f))) { summons.Summon(cr); cr->CastSpell(cr, SPELL_TOMB_OF_THE_HEARTLESS, true); cr->SetFaction(me->GetFaction()); } if ((cr = me->SummonCreature(NPC_ZEPIK, 5631.63f, 3794.36f, -92.24f, 3.45f))) { summons.Summon(cr); cr->CastSpell(cr, SPELL_TOMB_OF_THE_HEARTLESS, true); cr->SetFaction(me->GetFaction()); } } void MoveInLineOfSight(Unit* who) override { if (who->GetEntry() == NPC_JALOOT || who->GetEntry() == NPC_ZEPIK) return; ScriptedAI::MoveInLineOfSight(who); } void JustEngagedWith(Unit* /*who*/) override { Talk(SAY_ARTRUIS_AGGRO); me->CastSpell(me, SPELL_ARTRUIS_ICY_VEINS, true); events.RescheduleEvent(EVENT_CAST_FROST_BOLT, 4s); events.RescheduleEvent(EVENT_CAST_FROST_NOVA, 15s); events.RescheduleEvent(EVENT_CAST_ICE_LANCE, 8500ms); events.RescheduleEvent(EVENT_CAST_ICY_VEINS, 30s); events.RescheduleEvent(EVENT_ARTRUIS_HP_CHECK, 1s); events.RescheduleEvent(EVENT_ARTRUIS_TALK1, 6s); } void JustDied(Unit* /*killer*/) override { if (GameObject* go = me->SummonGameObject(GO_ARTRUIS_PHYLACTERY, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 600000)) me->RemoveGameObject(go, false); } void SummonedCreatureDies(Creature* summon, Unit*) override { SummonsAction(ACTION_MAKE_FRIENDLY); me->RemoveAurasDueToSpell(SPELL_ARTRUIS_BINDING); summon->DespawnOrUnsummon(60000); me->SetControlled(false, UNIT_STATE_STUNNED); } void SummonsAction(uint8 action) { if (!summons.empty()) { if (action == ACTION_BIND_MINIONS) me->CastSpell(me, SPELL_ARTRUIS_BINDING, true); for (ObjectGuid const& guid : summons) { Creature* minion = ObjectAccessor::GetCreature(*me, guid); if (minion && minion->IsAlive()) { if (action == ACTION_BIND_MINIONS) { minion->RemoveAurasDueToSpell(SPELL_TOMB_OF_THE_HEARTLESS); if (me->GetVictim()) minion->AI()->AttackStart(me->GetVictim()); } else if (action == ACTION_MAKE_FRIENDLY && me->GetVictim()) { minion->AI()->Talk(SAY_TURNED_FRIENDLY); minion->RemoveAurasDueToSpell(SPELL_ARTRUIS_BINDING); minion->SetFaction(me->GetVictim()->GetFaction()); minion->AddThreat(me, 100000.0f); minion->AI()->AttackStart(me); minion->DespawnOrUnsummon(900000); events.RescheduleEvent(EVENT_ARTRUIS_TALK3, 5s); } } } } } void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; switch (events.ExecuteEvent()) { case EVENT_ARTRUIS_HP_CHECK: if (me->GetHealthPct() <= 30) { me->SetControlled(true, UNIT_STATE_STUNNED); Talk(SAY_ARTRUIS_SHIELD); Talk(SAY_ARTRUIS_TALK_3); SummonsAction(ACTION_BIND_MINIONS); break; } events.Repeat(1s); break; case EVENT_ARTRUIS_TALK1: Talk(SAY_ARTRUIS_TALK_1); events.RescheduleEvent(EVENT_ARTRUIS_TALK2, 10s); break; case EVENT_ARTRUIS_TALK2: Talk(SAY_ARTRUIS_TALK_2); break; case EVENT_ARTRUIS_TALK3: Talk(SAY_ARTRUIS_TALK_4); break; case EVENT_CAST_FROST_BOLT: me->CastSpell(me->GetVictim(), SPELL_ARTRUIS_FROSTBOLT, false); events.Repeat(4s); break; case EVENT_CAST_ICE_LANCE: me->CastSpell(me->GetVictim(), SPELL_ARTRUIS_ICE_LANCE, false); events.Repeat(8500ms); break; case EVENT_CAST_FROST_NOVA: me->CastSpell(me, SPELL_ARTRUIS_FROST_NOVA, false); events.Repeat(15s); break; case EVENT_CAST_ICY_VEINS: me->CastSpell(me, SPELL_ARTRUIS_ICY_VEINS, false); events.Repeat(30s); break; } } }; CreatureAI* GetAI(Creature* creature) const override { return new npc_artruis_the_hearthlessAI(creature); } }; /****** quest Still At It (12644) ******/ enum StillAtIt { NPC_MANUS = 28566, NPC_WANTS_BANANAS = 28537, QUEST_STILL_AT_IT = 12644, GOSSIP_MANUS_MENU = 9713, SAY_MANUS_START = 0, SAY_MANUS_ORANGE = 1, SAY_MANUS_PAPAYA = 2, SAY_MANUS_BANANA = 3, SAY_MANUS_PRESSUE = 4, SAY_MANUS_HEAT = 5, SAY_MANUS_WELL_DONE = 6, SAY_MANUS_FAILED = 7, SAY_MANUS_END = 8, }; class npc_still_at_it_trigger : public CreatureScript { public: npc_still_at_it_trigger() : CreatureScript("npc_still_at_it_trigger") { } CreatureAI* GetAI(Creature* pCreature) const override { return new npc_still_at_it_triggerAI(pCreature); } struct npc_still_at_it_triggerAI : public NullCreatureAI { bool running; bool success; ObjectGuid playerGUID; ObjectGuid thunderbrewGUID; int32 tensecstimer; int32 timer; uint8 stepcount; uint8 currentstep; uint8 expectedaction; uint8 playeraction; npc_still_at_it_triggerAI(Creature* pCreature) : NullCreatureAI(pCreature) {} Creature* GetManus() {return ObjectAccessor::GetCreature(*me, thunderbrewGUID);} void Reset() override { running = false; success = false; playerGUID.Clear(); thunderbrewGUID.Clear(); tensecstimer = 0; timer = 0; stepcount = 0; currentstep = 0; expectedaction = 0; playeraction = 0; } void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override { damage = 0; } void Start() { timer = 5000; running = true; stepcount = urand(5, 10); GetManus()->AI()->Talk(SAY_MANUS_START); } void CheckAction(uint8 a, ObjectGuid guid) { if (guid != playerGUID) return; if (a == expectedaction) { currentstep++; if (Creature* th = ObjectAccessor::GetCreature(*me, thunderbrewGUID)) th->HandleEmoteCommand(EMOTE_ONESHOT_CHEER_NO_SHEATHE); GetManus()->AI()->Talk(SAY_MANUS_WELL_DONE); if (currentstep >= stepcount) { GetManus()->AI()->Talk(SAY_MANUS_WELL_DONE); success = true; timer = 3000; } else { expectedaction = 0; timer = 3000; } } else { GetManus()->AI()->Talk(SAY_MANUS_FAILED); Reset(); } } void SpellHit(Unit* caster, SpellInfo const* spellInfo) override // for banana(51932), orange(51931), papaya(51933) { if (running) { uint8 a = 0; switch (spellInfo->Id) { case 51931: a = 4; break; case 51932: a = 3; break; case 51933: a = 5; break; } CheckAction(a, caster->GetGUID()); } } void UpdateAI(uint32 diff) override { if (running) { if (timer) { timer -= diff; if (timer < 0) timer = 0; } else if (success) { GetManus()->AI()->Talk(SAY_MANUS_END); me->SummonGameObject(190643, 5546.55f, 5768.0f, -78.03f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0); Reset(); } else if (expectedaction != 0) // didn't make it in 10 seconds { GetManus()->AI()->Talk(SAY_MANUS_FAILED); Reset(); } else // it's time to rand next move { expectedaction = urand(1, 5); switch (expectedaction) { case 1: GetManus()->AI()->Talk(SAY_MANUS_PRESSUE); break; case 2: GetManus()->AI()->Talk(SAY_MANUS_HEAT); break; case 3: GetManus()->AI()->Talk(SAY_MANUS_BANANA); break; case 4: GetManus()->AI()->Talk(SAY_MANUS_ORANGE); break; case 5: GetManus()->AI()->Talk(SAY_MANUS_PAPAYA); break; } timer = 10000; } } } }; }; class npc_mcmanus : public CreatureScript { public: npc_mcmanus() : CreatureScript("npc_mcmanus") {} bool OnGossipHello(Player* player, Creature* creature) override { if (!player) return true; if (creature->IsQuestGiver()) player->PrepareQuestMenu(creature->GetGUID()); if (player->GetQuestStatus(QUEST_STILL_AT_IT) == QUEST_STATUS_INCOMPLETE) AddGossipItemFor(player, GOSSIP_MANUS_MENU, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); return true; } bool OnGossipSelect(Player* player, Creature* creature, uint32 /*uiSender*/, uint32 uiAction) override { if (!player) return true; if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) { Creature* trigger = creature->FindNearestCreature(NPC_WANTS_BANANAS, 20.0f, true); if (trigger && trigger->AI()) if (!CAST_AI(npc_still_at_it_trigger::npc_still_at_it_triggerAI, trigger->AI())->running) { CAST_AI(npc_still_at_it_trigger::npc_still_at_it_triggerAI, trigger->AI())->playerGUID = player->GetGUID(); CAST_AI(npc_still_at_it_trigger::npc_still_at_it_triggerAI, trigger->AI())->thunderbrewGUID = creature->GetGUID(); CAST_AI(npc_still_at_it_trigger::npc_still_at_it_triggerAI, trigger->AI())->Start(); } } CloseGossipMenuFor(player); return true; } }; class go_pressure_valve : public GameObjectScript { public: go_pressure_valve() : GameObjectScript("go_pressure_valve") { } bool OnGossipHello(Player* player, GameObject* go) override { if (!player) return true; Creature* trigger = go->FindNearestCreature(NPC_WANTS_BANANAS, 20.0f, true); if (trigger && trigger->AI()) if (CAST_AI(npc_still_at_it_trigger::npc_still_at_it_triggerAI, trigger->AI())->running) CAST_AI(npc_still_at_it_trigger::npc_still_at_it_triggerAI, trigger->AI())->CheckAction(1, player->GetGUID()); return false; } }; class go_brazier : public GameObjectScript { public: go_brazier() : GameObjectScript("go_brazier") { } bool OnGossipHello(Player* player, GameObject* go) override { if (!player) return true; Creature* trigger = go->FindNearestCreature(NPC_WANTS_BANANAS, 20.0f, true); if (trigger && trigger->AI()) if (CAST_AI(npc_still_at_it_trigger::npc_still_at_it_triggerAI, trigger->AI())->running) CAST_AI(npc_still_at_it_trigger::npc_still_at_it_triggerAI, trigger->AI())->CheckAction(2, player->GetGUID()); return false; } }; /*###### ## npc_vekjik ######*/ enum Vekjik { GOSSIP_VEKJIK_MENU_1 = 9678, GOSSIP_VEKJIK_MENU_2 = 9686, GOSSIP_TEXTID_VEKJIK_1 = 13137, GOSSIP_TEXTID_VEKJIK_2 = 13138, SAY_TEXTID_VEKJIK1 = 0, SPELL_FREANZYHEARTS_FURY = 51469, QUEST_MAKING_PEACE = 12573 }; class npc_vekjik : public CreatureScript { public: npc_vekjik() : CreatureScript("npc_vekjik") { } bool OnGossipHello(Player* player, Creature* creature) override { if (creature->IsQuestGiver()) player->PrepareQuestMenu(creature->GetGUID()); if (player->GetQuestStatus(QUEST_MAKING_PEACE) == QUEST_STATUS_INCOMPLETE) { AddGossipItemFor(player, GOSSIP_VEKJIK_MENU_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); SendGossipMenuFor(player, GOSSIP_TEXTID_VEKJIK_1, creature->GetGUID()); return true; } SendGossipMenuFor(player, player->GetGossipTextId(creature), 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_VEKJIK_MENU_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); SendGossipMenuFor(player, GOSSIP_TEXTID_VEKJIK_2, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF+2: CloseGossipMenuFor(player); creature->AI()->Talk(SAY_TEXTID_VEKJIK1, player); player->AreaExploredOrEventHappens(QUEST_MAKING_PEACE); //creature->CastSpell(player, SPELL_FREANZYHEARTS_FURY, true); player->KnockbackFrom(creature->GetPositionX(), creature->GetPositionY(), 30.0f, 18.0f); break; } return true; } }; /*###### ## avatar_of_freya ######*/ enum Freya { QUEST_FREYA_PACT = 12621, SPELL_FREYA_CONVERSATION = 52045, GOSSIP_AVATAR_MENU_1 = 9720, GOSSIP_AVATAR_MENU_2 = 9721, GOSSIP_AVATAR_MENU_3 = 9722, GOSSIP_TEXTID_AVATAR_1 = 13303, GOSSIP_TEXTID_AVATAR_2 = 13304, GOSSIP_TEXTID_AVATAR_3 = 13305, }; class npc_avatar_of_freya : public CreatureScript { public: npc_avatar_of_freya() : CreatureScript("npc_avatar_of_freya") { } bool OnGossipHello(Player* player, Creature* creature) override { if (creature->IsQuestGiver()) player->PrepareQuestMenu(creature->GetGUID()); if (player->GetQuestStatus(QUEST_FREYA_PACT) == QUEST_STATUS_INCOMPLETE) AddGossipItemFor(player, GOSSIP_AVATAR_MENU_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); SendGossipMenuFor(player, GOSSIP_TEXTID_AVATAR_1, creature); 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_AVATAR_MENU_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); SendGossipMenuFor(player, GOSSIP_TEXTID_AVATAR_2, creature); break; case GOSSIP_ACTION_INFO_DEF+2: AddGossipItemFor(player, GOSSIP_AVATAR_MENU_3, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); SendGossipMenuFor(player, GOSSIP_TEXTID_AVATAR_3, creature); break; case GOSSIP_ACTION_INFO_DEF+3: player->CastSpell(player, SPELL_FREYA_CONVERSATION, true); CloseGossipMenuFor(player); break; } return true; } }; /*###### ## npc_bushwhacker ######*/ class npc_bushwhacker : public CreatureScript { public: npc_bushwhacker() : CreatureScript("npc_bushwhacker") { } struct npc_bushwhackerAI : public ScriptedAI { npc_bushwhackerAI(Creature* creature) : ScriptedAI(creature) { } void InitializeAI() override { if (me->isDead()) return; if (TempSummon* summ = me->ToTempSummon()) if (Unit* summoner = summ->GetSummonerUnit()) me->GetMotionMaster()->MovePoint(0, summoner->GetPositionX(), summoner->GetPositionY(), summoner->GetPositionZ()); Reset(); } void UpdateAI(uint32 /*uiDiff*/) override { if (!UpdateVictim()) return; DoMeleeAttackIfReady(); } }; CreatureAI* GetAI(Creature* creature) const override { return new npc_bushwhackerAI(creature); } }; /*###### ## npc_engineer_helice ######*/ enum EngineerHelice { // Spells SPELL_EXPLODE_CRYSTAL = 62487, SPELL_FLAMES = 64561, // Yells SAY_WP_1 = 0, SAY_WP_2 = 1, SAY_WP_3 = 2, SAY_WP_4 = 3, SAY_WP_5 = 4, SAY_WP_6 = 5, SAY_WP_7 = 6, // Quests QUEST_DISASTER = 12688 }; class npc_engineer_helice : public CreatureScript { public: npc_engineer_helice() : CreatureScript("npc_engineer_helice") { } struct npc_engineer_heliceAI : public npc_escortAI { npc_engineer_heliceAI(Creature* creature) : npc_escortAI(creature) { } uint32 m_uiChatTimer; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); switch (waypointId) { case 0: Talk(SAY_WP_2); break; case 1: Talk(SAY_WP_3); me->CastSpell(5918.33f, 5372.91f, -98.770f, SPELL_EXPLODE_CRYSTAL, true); me->SummonGameObject(184743, 5918.33f, 5372.91f, -98.770f, 0, 0, 0, 0, 0, TEMPSUMMON_MANUAL_DESPAWN); //approx 3 to 4 seconds me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH); break; case 2: Talk(SAY_WP_4); break; case 7: Talk(SAY_WP_5); break; case 8: me->CastSpell(5887.37f, 5379.39f, -91.289f, SPELL_EXPLODE_CRYSTAL, true); me->SummonGameObject(184743, 5887.37f, 5379.39f, -91.289f, 0, 0, 0, 0, 0, TEMPSUMMON_MANUAL_DESPAWN); //approx 3 to 4 seconds me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH); break; case 9: Talk(SAY_WP_6); break; case 13: if (player) { player->GroupEventHappens(QUEST_DISASTER, me); Talk(SAY_WP_7); } break; } } void Reset() override { m_uiChatTimer = 4000; } void JustDied(Unit* /*killer*/) override { if (HasEscortState(STATE_ESCORT_ESCORTING)) { if (Player* player = GetPlayerForEscort()) player->FailQuest(QUEST_DISASTER); } } void UpdateAI(uint32 uiDiff) override { npc_escortAI::UpdateAI(uiDiff); if (HasEscortState(STATE_ESCORT_ESCORTING)) { if (m_uiChatTimer <= uiDiff) { m_uiChatTimer = 12000; } else m_uiChatTimer -= uiDiff; } } }; CreatureAI* GetAI(Creature* creature) const override { return new npc_engineer_heliceAI(creature); } bool OnQuestAccept(Player* player, Creature* creature, const Quest* quest) override { if (quest->GetQuestId() == QUEST_DISASTER) { if (npc_engineer_heliceAI* pEscortAI = CAST_AI(npc_engineer_helice::npc_engineer_heliceAI, creature->AI())) { creature->GetMotionMaster()->MoveJumpTo(0, 0.4f, 0.4f); creature->SetFaction(FACTION_ESCORTEE_N_NEUTRAL_PASSIVE); pEscortAI->Start(false, false, player->GetGUID()); creature->AI()->Talk(SAY_WP_1); } } return true; } }; /*##### ## npc_jungle_punch_target #####*/ enum JunglePunch { ITEM_TANKARD = 2705, NPC_HEMET = 27986, NPC_HADRIUS = 28047, SPELL_KNOCKDOWN = 42963, SPELL_OFFER = 51962, QUEST_TASTE_TEST = 12645, SAY_HEMET_HADRIUS_TAMARA_1 = 0, SAY_HEMET_HADRIUS_TAMARA_2 = 1, SAY_HEMET_HADRIUS_TAMARA_3 = 2, SAY_HEMET_4 = 3, // unused SAY_HEMET_5 = 4, // unused // Player Say SAY_OFFER = 28558, }; enum NesingwaryChildrensWeek { SPELL_ORPHAN_OUT = 58818, QUEST_THE_MIGHTY_HEMET_NESINGWARY = 13957, ORPHAN_WOLVAR = 33532, TEXT_NESINGWARY_1 = 5, TEXT_WOLVAR_ORPHAN_6 = 6, TEXT_WOLVAR_ORPHAN_7 = 7, TEXT_WOLVAR_ORPHAN_8 = 8, TEXT_WOLVAR_ORPHAN_9 = 9 }; class npc_jungle_punch_target : public CreatureScript { public: npc_jungle_punch_target() : CreatureScript("npc_jungle_punch_target") { } struct npc_jungle_punch_targetAI : public ScriptedAI { npc_jungle_punch_targetAI(Creature* creature) : ScriptedAI(creature) { } void Reset() override { sayTimer = 3500; sayStep = 0; timer = 0; phase = 0; playerGUID.Clear(); orphanGUID.Clear(); } void MoveInLineOfSight(Unit* who) override { if (!phase && who && who->GetDistance2d(me) < 10.0f) if (Player* player = who->ToPlayer()) if (player->GetQuestStatus(QUEST_THE_MIGHTY_HEMET_NESINGWARY) == QUEST_STATUS_INCOMPLETE) { playerGUID = player->GetGUID(); if (Aura* orphanOut = player->GetAura(SPELL_ORPHAN_OUT)) if (orphanOut->GetCaster() && orphanOut->GetCaster()->GetEntry() == ORPHAN_WOLVAR) { orphanGUID = orphanOut->GetCaster()->GetGUID(); phase = 1; } } } void proceedCwEvent(const uint32 diff) { if (timer <= diff) { Player* player = ObjectAccessor::GetPlayer(*me, playerGUID); Creature* orphan = ObjectAccessor::GetCreature(*me, orphanGUID); if (!orphan || !player) { Reset(); return; } switch (phase) { case 1: orphan->GetMotionMaster()->MovePoint(0, me->GetPositionX() + cos(me->GetOrientation()) * 5, me->GetPositionY() + std::sin(me->GetOrientation()) * 5, me->GetPositionZ()); orphan->AI()->Talk(TEXT_WOLVAR_ORPHAN_6); timer = 5000; break; case 2: orphan->SetFacingToObject(me); orphan->AI()->Talk(TEXT_WOLVAR_ORPHAN_7); timer = 5000; break; case 3: Talk(TEXT_NESINGWARY_1); timer = 5000; break; case 4: orphan->AI()->Talk(TEXT_WOLVAR_ORPHAN_8); timer = 5000; break; case 5: orphan->AI()->Talk(TEXT_WOLVAR_ORPHAN_9); timer = 5000; break; case 6: orphan->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); player->GroupEventHappens(QUEST_THE_MIGHTY_HEMET_NESINGWARY, me); Reset(); return; } ++phase; } else timer -= diff; } void UpdateAI(uint32 diff) override { if (phase) proceedCwEvent(diff); if (!sayStep) return; if (sayTimer < diff) { if (sayStep == 2) { me->SetSheath(SHEATH_STATE_MELEE); SetEquipmentSlots(false, ITEM_TANKARD, EQUIP_UNEQUIP, EQUIP_UNEQUIP); } else if (sayStep == 3) { if (me->GetEntry() == NPC_HEMET) me->SetSheath(SHEATH_STATE_RANGED); else if (me->GetEntry() == NPC_HADRIUS) { me->SetSheath(SHEATH_STATE_UNARMED); me->CastSpell(me, SPELL_KNOCKDOWN, false); } SetEquipmentSlots(true); } Talk(SAY_HEMET_HADRIUS_TAMARA_1 + sayStep - 1); sayTimer = 6000; sayStep++; if (sayStep > 3) // end sayStep = 0; } else sayTimer -= diff; } void SpellHit(Unit* caster, SpellInfo const* spellInfo) override { if (spellInfo->Id != SPELL_OFFER) return; Player* player = caster->ToPlayer(); if (!player) return; Quest const* quest = sObjectMgr->GetQuestTemplate(QUEST_TASTE_TEST); if (!quest) return; QuestStatusMap::const_iterator itr = player->getQuestStatusMap().find(QUEST_TASTE_TEST); if (itr->second.Status != QUEST_STATUS_INCOMPLETE) return; for (uint8 i = 0; i < 3; ++i) { if (uint32(quest->RequiredNpcOrGo[i]) != me->GetEntry()) continue; if (itr->second.CreatureOrGOCount[i] != 0) continue; player->KilledMonsterCredit(me->GetEntry()); player->Say(SAY_OFFER); sayStep = 1; break; } } private: uint16 sayTimer; uint8 sayStep; uint32 timer; int8 phase; ObjectGuid playerGUID; ObjectGuid orphanGUID; }; CreatureAI* GetAI(Creature* creature) const override { return new npc_jungle_punch_targetAI(creature); } }; /*###### ## npc_adventurous_dwarf ######*/ enum AdventurousDwarf { QUEST_12634 = 12634, ITEM_BANANAS = 38653, ITEM_PAPAYA = 38655, ITEM_ORANGE = 38656, SPELL_ADD_ORANGE = 52073, SPELL_ADD_BANANAS = 52074, SPELL_ADD_PAPAYA = 52076, SAY_DWARF_OUCH = 0, SAY_DWARF_HELP = 1, // Gossips GOSSIP_DWARF_MENU = 9724, GOSSIP_DWARF_ORANGE = 0, GOSSIP_DWARF_BANANA = 1, GOSSIP_DWARF_PAPAYA = 2, }; class npc_adventurous_dwarf : public CreatureScript { public: npc_adventurous_dwarf() : CreatureScript("npc_adventurous_dwarf") { } struct npc_adventurous_dwarfAI : public ScriptedAI { npc_adventurous_dwarfAI(Creature* creature) : ScriptedAI(creature) { Talk(SAY_DWARF_OUCH); } }; CreatureAI* GetAI(Creature* creature) const override { return new npc_adventurous_dwarfAI(creature); } bool OnGossipHello(Player* player, Creature* creature) override { if (player->GetQuestStatus(QUEST_12634) != QUEST_STATUS_INCOMPLETE) return false; if (player->GetItemCount(ITEM_ORANGE) < 1) AddGossipItemFor(player, GOSSIP_DWARF_MENU, GOSSIP_DWARF_ORANGE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); if (player->GetItemCount(ITEM_BANANAS) < 2) AddGossipItemFor(player, GOSSIP_DWARF_MENU, GOSSIP_DWARF_BANANA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); if (player->GetItemCount(ITEM_PAPAYA) < 1) AddGossipItemFor(player, GOSSIP_DWARF_MENU, GOSSIP_DWARF_PAPAYA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); SendGossipMenuFor(player, player->GetGossipTextId(creature), creature); return true; } bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override { ClearGossipMenuFor(player); uint32 spellId = 0; switch (action) { case GOSSIP_ACTION_INFO_DEF + 1: spellId = SPELL_ADD_ORANGE; break; case GOSSIP_ACTION_INFO_DEF + 2: spellId = SPELL_ADD_BANANAS; break; case GOSSIP_ACTION_INFO_DEF + 3: spellId = SPELL_ADD_PAPAYA; break; } if (spellId) player->CastSpell(player, spellId, true); creature->AI()->Talk(SAY_DWARF_HELP); creature->DespawnOrUnsummon(); return true; } }; /*###### ## Quest The Lifewarden's Wrath ######*/ enum MiscLifewarden { NPC_PRESENCE = 28563, // Freya's Presence NPC_SABOTEUR = 28538, // Cultist Saboteur NPC_SERVANT = 28320, // Servant of Freya WHISPER_ACTIVATE = 0, SPELL_FREYA_DUMMY = 51318, SPELL_LIFEFORCE = 51395, SPELL_FREYA_DUMMY_TRIGGER = 51335, SPELL_LASHER_EMERGE = 48195, SPELL_WILD_GROWTH = 52948, }; class spell_q12620_the_lifewarden_wrath : public SpellScript { PrepareSpellScript(spell_q12620_the_lifewarden_wrath); bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_FREYA_DUMMY, SPELL_FREYA_DUMMY_TRIGGER, SPELL_LASHER_EMERGE, SPELL_WILD_GROWTH, SPELL_LIFEFORCE }); } void HandleSendEvent(SpellEffIndex effIndex) { PreventHitDefaultEffect(effIndex); if (Unit* caster = GetCaster()) { if (Creature* presence = caster->FindNearestCreature(NPC_PRESENCE, 50.0f)) { presence->AI()->Talk(WHISPER_ACTIVATE, caster); presence->CastSpell(presence, SPELL_FREYA_DUMMY, true); // will target plants // Freya Dummy could be scripted with the following code // Revive plants std::list servants; GetCaster()->GetCreatureListWithEntryInGrid(servants, NPC_SERVANT, 200.0f); for (std::list::iterator itr = servants.begin(); itr != servants.end(); ++itr) { // Couldn't find a spell that does this if ((*itr)->isDead()) (*itr)->Respawn(true); (*itr)->CastSpell(*itr, SPELL_FREYA_DUMMY_TRIGGER, true); (*itr)->CastSpell(*itr, SPELL_LASHER_EMERGE, false); (*itr)->CastSpell(*itr, SPELL_WILD_GROWTH, false); if (Unit* target = (*itr)->SelectNearestTarget(150.0f)) (*itr)->AI()->AttackStart(target); } // Kill nearby enemies std::list saboteurs; caster->GetCreatureListWithEntryInGrid(saboteurs, NPC_SABOTEUR, 200.0f); for (std::list::iterator itr = saboteurs.begin(); itr != saboteurs.end(); ++itr) if ((*itr)->IsAlive()) // Lifeforce has a cast duration, it should be cast at all saboteurs one by one presence->CastSpell((*itr), SPELL_LIFEFORCE, false); } } } void Register() override { OnEffectHit += SpellEffectFn(spell_q12620_the_lifewarden_wrath::HandleSendEvent, EFFECT_0, SPELL_EFFECT_SEND_EVENT); } }; /*###### ## Quest Kick, What Kick? (12589) ######*/ enum KickWhatKick { NPC_LUCKY_WILHELM = 28054, NPC_APPLE = 28053, NPC_DROSTAN = 28328, NPC_CRUNCHY = 28346, NPC_THICKBIRD = 28093, SPELL_HIT_APPLE = 51331, SPELL_MISS_APPLE = 51332, SPELL_MISS_BIRD_APPLE = 51366, SPELL_APPLE_FALL = 51371, SPELL_BIRD_FALL = 51369, EVENT_MISS = 0, EVENT_HIT = 1, EVENT_MISS_BIRD = 2, SAY_WILHELM_MISS = 0, SAY_WILHELM_HIT = 1, SAY_DROSTAN_REPLY_MISS = 0, }; class spell_q12589_shoot_rjr : public SpellScript { PrepareSpellScript(spell_q12589_shoot_rjr); bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_MISS_BIRD_APPLE, SPELL_BIRD_FALL, SPELL_MISS_APPLE, SPELL_HIT_APPLE, SPELL_APPLE_FALL }); } SpellCastResult CheckCast() { if (Unit* target = GetExplTargetUnit()) if (target->GetEntry() == NPC_LUCKY_WILHELM) return SPELL_CAST_OK; SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_MUST_TARGET_WILHELM); return SPELL_FAILED_CUSTOM_ERROR; } void HandleDummy(SpellEffIndex /*effIndex*/) { uint32 roll = urand(1, 100); uint8 ev; if (roll <= 50) ev = EVENT_MISS; else if (roll <= 83) ev = EVENT_HIT; else ev = EVENT_MISS_BIRD; Unit* shooter = GetCaster(); Creature* wilhelm = GetHitUnit()->ToCreature(); Creature* apple = shooter->FindNearestCreature(NPC_APPLE, 30); Creature* drostan = shooter->FindNearestCreature(NPC_DROSTAN, 30); if (!wilhelm || !apple || !drostan) return; switch (ev) { case EVENT_MISS_BIRD: { Creature* crunchy = shooter->FindNearestCreature(NPC_CRUNCHY, 30); Creature* bird = shooter->FindNearestCreature(NPC_THICKBIRD, 30); if (!bird || !crunchy) ; // fall to EVENT_MISS else { shooter->CastSpell(bird, SPELL_MISS_BIRD_APPLE); bird->CastSpell(bird, SPELL_BIRD_FALL); wilhelm->AI()->Talk(SAY_WILHELM_MISS); drostan->AI()->Talk(SAY_DROSTAN_REPLY_MISS); Unit::Kill(bird, bird); crunchy->GetMotionMaster()->MovePoint(0, bird->GetPositionX(), bird->GetPositionY(), bird->GetMapWaterOrGroundLevel(bird->GetPositionX(), bird->GetPositionY(), bird->GetPositionZ())); /// @todo Make crunchy perform emote eat when he reaches the bird break; } [[fallthrough]]; } case EVENT_MISS: { shooter->CastSpell(wilhelm, SPELL_MISS_APPLE); wilhelm->AI()->Talk(SAY_WILHELM_MISS); drostan->AI()->Talk(SAY_DROSTAN_REPLY_MISS); break; } case EVENT_HIT: { shooter->CastSpell(apple, SPELL_HIT_APPLE); apple->CastSpell(apple, SPELL_APPLE_FALL); wilhelm->AI()->Talk(SAY_WILHELM_HIT); if (Player* player = shooter->ToPlayer()) player->KilledMonsterCredit(NPC_APPLE); //apple->DespawnOrUnsummon(); zomg! break; } } } void Register() override { OnCheckCast += SpellCheckCastFn(spell_q12589_shoot_rjr::CheckCast); OnEffectHitTarget += SpellEffectFn(spell_q12589_shoot_rjr::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } }; /*###### ## Quest: Reconnaissance Flight (12671) ######*/ enum ReconnaissanceFlight { NPC_PLANE = 28710, // Vic's Flying Machine NPC_PILOT = 28646, VIC_SAY_0 = 0, VIC_SAY_1 = 1, VIC_SAY_2 = 2, VIC_SAY_3 = 3, VIC_SAY_4 = 4, VIC_SAY_5 = 5, VIC_SAY_6 = 6, PLANE_EMOTE = 0, AURA_ENGINE = 52255, // Engine on Fire SPELL_LAND = 52226, // Land Flying Machine SPELL_CREDIT = 53328 // Land Flying Machine Credit }; class npc_vics_flying_machine : public CreatureScript { public: npc_vics_flying_machine() : CreatureScript("npc_vics_flying_machine") { } struct npc_vics_flying_machineAI : public VehicleAI { npc_vics_flying_machineAI(Creature* creature) : VehicleAI(creature) { pointId = 0; } uint8 pointId; void PassengerBoarded(Unit* passenger, int8 /*seatId*/, bool apply) override { if (apply && passenger->IsPlayer()) { Movement::PointsArray pathPoints; pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); WaypointPath const* i_path = sWaypointMgr->GetPath(NPC_PLANE); for (uint8 i = 0; i < i_path->size(); ++i) { WaypointData const* node = i_path->at(i); pathPoints.push_back(G3D::Vector3(node->x, node->y, node->z)); } me->GetMotionMaster()->MoveSplinePath(&pathPoints); } } void MovementInform(uint32 type, uint32 /*id*/) override { if (type != ESCORT_MOTION_TYPE) return; if (Vehicle* veh = me->GetVehicleKit()) if (Unit* pilot = veh->GetPassenger(0)) switch (pointId) { case 5: pilot->ToCreature()->AI()->Talk(VIC_SAY_0); break; case 11: pilot->ToCreature()->AI()->Talk(VIC_SAY_1); break; case 12: pilot->ToCreature()->AI()->Talk(VIC_SAY_2); break; case 14: pilot->ToCreature()->AI()->Talk(VIC_SAY_3); break; case 15: pilot->ToCreature()->ToCreature()->AI()->Talk(VIC_SAY_4); break; case 17: pilot->ToCreature()->AI()->Talk(VIC_SAY_5); break; case 21: pilot->ToCreature()->AI()->Talk(VIC_SAY_6); break; case 25: Talk(PLANE_EMOTE); DoCastSelf(AURA_ENGINE); break; } pointId++; } void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { if (spell->Id == SPELL_LAND) { Unit* passenger = me->GetVehicleKit()->GetPassenger(1); // player should be on seat 1 if (passenger && passenger->IsPlayer()) passenger->CastSpell(passenger, SPELL_CREDIT, true); me->DespawnOrUnsummon(); } } }; CreatureAI* GetAI(Creature* creature) const override { return new npc_vics_flying_machineAI(creature); } }; /*###### ## Quest Dreadsaber Mastery: Stalking the Prey (12550) ######*/ enum ShangoTracks { SPELL_CORRECT_TRACKS = 52160, SPELL_INCORRECT_TRACKS = 52163, SAY_CORRECT_TRACKS = 28634, SAY_INCORRECT_TRACKS = 28635 }; class spell_shango_tracks : public SpellScript { PrepareSpellScript(spell_shango_tracks); void HandleScript(SpellEffIndex /*effIndex*/) { if (Unit* target = GetHitUnit()) { switch (GetSpellInfo()->Id) { case SPELL_CORRECT_TRACKS: target->Say(SAY_CORRECT_TRACKS, target); break; case SPELL_INCORRECT_TRACKS: target->Say(SAY_INCORRECT_TRACKS, target); break; default: break; } } } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_shango_tracks::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; enum ReturnedSevenfold { SPELL_FREYAS_WARD = 51845, SPELL_SEVENFOLD_RETRIBUTION = 51856, SPELL_DEATHBOLT = 51855 }; class spell_q12611_deathbolt : public SpellScript { PrepareSpellScript(spell_q12611_deathbolt); void HandleScriptEffect(SpellEffIndex /* effIndex */) { Unit* caster = GetCaster(); Unit* target = GetHitUnit(); if (target->HasAura(SPELL_FREYAS_WARD)) { target->CastSpell(caster, SPELL_SEVENFOLD_RETRIBUTION, true); } else { caster->CastSpell(target, SPELL_DEATHBOLT, true); } } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_q12611_deathbolt::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; void AddSC_sholazar_basin() { RegisterSpellScript(spell_q12726_song_of_wind_and_water); new npc_artruis_the_hearthless(); new npc_still_at_it_trigger(); new npc_mcmanus(); new go_pressure_valve(); new go_brazier(); new npc_vekjik(); new npc_avatar_of_freya(); new npc_bushwhacker(); new npc_engineer_helice(); new npc_adventurous_dwarf(); new npc_jungle_punch_target(); RegisterSpellScript(spell_q12620_the_lifewarden_wrath); RegisterSpellScript(spell_q12589_shoot_rjr); new npc_vics_flying_machine(); RegisterSpellScript(spell_shango_tracks); RegisterSpellScript(spell_q12611_deathbolt); }