TrinityCore
Loading...
Searching...
No Matches
zone_the_wandering_isle.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "AreaTrigger.h"
20#include "CellImpl.h"
21#include "Containers.h"
22#include "CreatureAI.h"
23#include "CreatureAIImpl.h" // for RAND()
24#include "GameObject.h"
25#include "GameObjectAI.h"
26#include "GridNotifiersImpl.h"
27#include "MotionMaster.h"
28#include "ObjectAccessor.h"
29#include "PhasingHandler.h"
30#include "Player.h"
31#include "ScriptMgr.h"
32#include "ScriptedCreature.h"
33#include "SpellScript.h"
34#include "TaskScheduler.h"
35#include "TemporarySummon.h"
36
38{
39namespace Spells
40{
41 // Generic Vehicle Spells
42 static constexpr uint32 ForceVehicleRide = 46598;
43 static constexpr uint32 EjectPassengers = 50630;
44 static constexpr uint32 OxCartRopeLeft = 108627; // Cart on Yak
45
46 // Singing Pools
47 static constexpr uint32 CurseOfTheFrog = 102938;
48 static constexpr uint32 CurseOfTheSkunk = 102939;
49 static constexpr uint32 CurseOfTheTurtle = 102940;
50 static constexpr uint32 CurseOfTheCrane = 102941;
51 static constexpr uint32 CurseOfTheCrocodile = 102942;
52 static constexpr uint32 RideVehiclePole = 102717;
53 static constexpr uint32 TrainingBellPoleExitExclusion = 133381;
54
55 // Only the Worthy Shall Pass
56 static constexpr uint32 FireCrashCover = 108149;
57 static constexpr uint32 FireCrashInvis = 108150;
58 static constexpr uint32 FireCrashPhaseShift = 102515;
59 static constexpr uint32 FlyingShadowKick = 108936;
60 static constexpr uint32 FlyingShadowKickJump = 108943;
61 static constexpr uint32 FeetOfFury = 108958;
62 static constexpr uint32 FeetOfFuryDamage = 108957;
63}
64
65namespace Quests
66{
67 static constexpr uint32 OnlyTheWorthyShallPass = 29421;
68 static constexpr uint32 TheSourceOfLivelihood = 29680;
69 static constexpr uint32 TheSpiritAndBodyOfShenzinsu = 29775;
70 static constexpr uint32 NewAllies = 29800;
71}
72
73namespace Creatures
74{
75 static constexpr uint32 MasterLiFei = 54135;
76 static constexpr uint32 MasterLiFeiCombat = 54734;
77
78 // Yak and Cart
79 static constexpr uint32 CartSingingPools = 57710;
80 static constexpr uint32 CartFarmstead = 59497;
81 static constexpr uint32 CartForest = 57741;
82
83 static constexpr uint32 CartVehicleSingingPools = 57208;
84 static constexpr uint32 CartVehicleFarmstead = 59496;
85 static constexpr uint32 CartVehicleForest = 57740;
86}
87
88namespace Talks
89{
90 static constexpr uint32 LiFeiDefeat = 0;
91}
92
93namespace Paths
94{
95 // Yak and Cart
96 static constexpr uint32 CartSingingPools = 5720800;
97 static constexpr uint32 CartFarmstead = 5949600;
98 static constexpr uint32 CartForest = 5774000;
99
100 static constexpr int8 NodeCartRemovePassenger = 28;
101 static constexpr int8 NodeForestCartRemovePassenger = 34;
102}
103
104namespace Events
105{
106 // Yak and Cart
107 static constexpr int8 YakCartPathStart = 1;
108 static constexpr int8 YakCartRopes = 2;
109}
110
126
128 { 1465.3872f, 3283.8604f, 137.69096f },
129 { 1431.401f, 3264.001f, 136.02579f },
130 { 1397.2067f, 3276.5618f, 133.84508f },
131 { 1441.566f, 3232.8013f, 135.01802f },
132 { 1403.632f, 3229.1094f, 132.14877f },
133 { 1347.1927f, 3286.5842f, 131.94803f },
134 { 1365.1865f, 3338.9502f, 128.57233f },
135 { 1349.6024f, 3315.0574f, 130.97443f },
136 { 1335.4618f, 3344.019f, 130.42047f },
137 { 1360.1198f, 3378.02f, 127.34183f },
138 { 1435.8524f, 3355.6423f, 173.77744f },
139 { 1432.7031f, 3385.1572f, 184.4187f },
140 { 1452.6094f, 3373.3315f, 187.0402f },
141 { 1426.7778f, 3364.7517f, 184.39569f },
142 { 1450.3646f, 3361.264f, 184.42484f },
143};
144
153
154// 54586 - Huojin Trainee
155// 65470 - Huojin Trainee
156// 54587 - Tushui Trainee
157// 65471 - Tushui Trainee
159{
160 npc_tushui_huojin_trainee(Creature* creature) : ScriptedAI(creature), _defeated(false) { }
161
168
169 void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
170 {
171 if (me->HealthBelowPctDamaged(20, damage))
172 {
173 damage = 0;
174 if (_defeated)
175 return;
176
177 _defeated = true;
178 if (attacker)
179 {
180 if (Player* player = attacker->ToPlayer())
181 player->KilledMonsterCredit(QUEST_29524_KILLCREDIT);
182 }
183
185 me->SetImmuneToPC(true);
186 me->CombatStop();
187
188 _scheduler.Schedule(1s, [this](TaskContext const& /*task*/)
189 {
191 });
192
193 _scheduler.Schedule(3s, [this](TaskContext const& /*task*/)
194 {
195 Position currentPosition;
196 float currentDist = 1000.0f;
197 for (Position const& pos : TraineeEndpoints)
198 {
199 float dist = pos.GetExactDist(me);
200 if (dist >= currentDist)
201 continue;
202
203 currentPosition = pos;
204 currentDist = dist;
205 }
206 me->GetMotionMaster()->MovePoint(POINT_DESPAWN, currentPosition);
207 });
208 }
209 }
210
211 void MovementInform(uint32 type, uint32 id) override
212 {
213 if (type != POINT_MOTION_TYPE)
214 return;
215
216 if (id != POINT_DESPAWN)
217 return;
218
220 }
221
222 void JustEngagedWith(Unit* /*attacker*/) override
223 {
224 _scheduler.Schedule(4s, [this](TaskContext& task)
225 {
226 if (me->GetVictim())
228
229 task.Repeat(8s);
230 });
231 }
232
233 void UpdateAI(uint32 diff) override
234 {
235 _scheduler.Update(diff);
236
237 if (!UpdateVictim())
238 return;
239 }
240
241 void EnterEvadeMode(EvadeReason why) override
242 {
243 if (!_defeated)
245 }
246
247protected:
250};
251
256
258{
259public:
260 HuojinTraineePartnerSearch(Creature* partner) : _partner(partner), _minDist(10.0f) { }
261
262 bool operator()(Creature const* target)
263 {
265 return false;
266 if (target == _partner)
267 return false;
268 if (target->IsInCombat())
269 return false;
270 if (target->IsInEvadeMode())
271 return false;
272 if (target->isDead())
273 return false;
274
275 float dist = target->GetDistance(_partner);
276 if (dist >= _minDist)
277 return false;
278
279 _minDist = dist;
280 return true;
281 }
282
283private:
285 float _minDist;
286};
287
288// 54586 - Huojin Trainee
289// 65470 - Huojin Trainee
291{
293
294 void JustEngagedWith(Unit* attacker) override
295 {
298
300 if (!partner)
301 return;
302
303 if (partner->AI())
305 }
306
307 void DoAction(int32 action) override
308 {
309 if (action == ACTION_PARTNER_ENTERED_COMBAT)
310 {
312
314 _scheduler.Schedule(1s, [this](TaskContext const& /*task*/ )
315 {
317 });
318 }
319 }
320
322 {
323 _partnerGuid = guid;
326
327 _scheduler.Schedule(1s, [this](TaskContext const& /*task*/)
328 {
330 });
331
332 _scheduler.Schedule(4s, [this](TaskContext& task)
333 {
335 task.Repeat(4s);
336 });
337 }
338
340 {
341 Creature* partner = nullptr;
344 Cell::VisitGridObjects(me, searcher, 10.0f);
345 return partner;
346 }
347
349 {
350 _partnerGuid = partnerGuid;
351 _scheduler.Schedule(1s, [this, partnerGuid](TaskContext const& /*task*/)
352 {
353 BeginSparring(partnerGuid);
354 });
355 }
356
358 {
359 Creature* partner = GetNewPartner();
360
361 if (!partner)
362 return;
363
364 BeginSparring(partner->GetGUID());
366 {
367 if (npc_huojin_trainee* ai = CAST_AI(npc_huojin_trainee, partner->GetAI()))
368 ai->BeginSparringDelayed(me->GetGUID());
369 }
370 }
371
372 void JustReachedHome() override
373 {
375 }
376
377 void JustAppeared() override
378 {
379 // partner is already assigned, sparring start is delayed
382 }
383private:
385};
386
388{
389public:
390 TushuiTraineeSearch(Creature* leader, float maxDist) : _leader(leader), _maxDist(maxDist) { }
391
392 bool operator()(Creature const* target) const
393 {
395 return false;
396 if (target->IsInCombat())
397 return false;
398 if (target->IsInEvadeMode())
399 return false;
400 if (target->GetDistance(_leader) >= _maxDist)
401 return false;
402 if (target->isDead())
403 return false;
404
405 return true;
406 }
407
408private:
410 float _maxDist;
411};
412
414{
415 std::list<Creature*> traineeList;
416 TushuiTraineeSearch check(leader, 10.0f);
417 Trinity::CreatureListSearcher<TushuiTraineeSearch> searcher(leader, traineeList, check);
418 Cell::VisitGridObjects(leader, searcher, 10.0f);
419
420 for (Creature* trainee : traineeList)
421 trainee->HandleEmoteCommand(emote);
422}
423
424// 54587 - Tushui Trainee
425// 65471 - Tushui Trainee
427{
429
431 {
432 _scheduler.Schedule(1s, [this](TaskContext& task)
433 {
434 Emote emote = PlayRandomEmote();
436 task.Repeat(6s);
437 });
438 }
439
440 void JustReachedHome() override
441 {
443 }
444
445 void JustAppeared() override
446 {
448 }
449
450 void JustEngagedWith(Unit* attacker) override
451 {
454 }
455};
456
457// 61411 - Instructor Zhi
459{
460 npc_instructor_zhi(Creature* creature) : ScriptedAI(creature) { }
461
462 void JustAppeared() override
463 {
464 _scheduler.Schedule(6s, [this](TaskContext& task)
465 {
467 me->HandleEmoteCommand(emote);
468
469 task.Schedule(1s, [this, emote](TaskContext const& /*task*/)
470 {
472 });
473 task.Repeat(6s);
474 });
475 }
476
477 void UpdateAI(uint32 diff) override
478 {
479 _scheduler.Update(diff);
480 }
481
482private:
484};
485
486// 210986 - Edict of Temperance
488{
490
491 bool OnGossipHello(Player* /*player*/) override
492 {
493 me->DespawnOrUnsummon(1ms);
494 return false;
495 }
496};
497
525
526// 54611 - Jaomin Ro
528{
530
531 void JustEngagedWith(Unit* /*who*/) override
532 {
534 }
535
541
542 void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override
543 {
544 if (spellInfo->Id == SPELL_CSA_AREATRIGGER_DUMMY && target->GetTypeId() == TYPEID_PLAYER && !me->IsInCombat())
545 {
546 Talk(SAY_INTRO, target);
547 me->SetOrientation(1.67690026f);
550 }
551 }
552
553 void UpdateAI(uint32 diff) override
554 {
555 if (!UpdateVictim())
556 return;
557
558 _events.Update(diff);
559
561 return;
562
563 switch (_events.ExecuteEvent())
564 {
566 {
568 DoCast(spellId);
570 break;
571 }
572 case EVENT_HEAL:
573 {
575 me->SetUninteractible(false);
576 break;
577 }
578 case EVENT_MOVE_HOME:
579 {
581 break;
582 }
583 default:
584 break;
585 }
586 }
587
604
605private:
607};
608
609// 57750 - Jaomin Ro (Hawk)
611{
613
614 void JustAppeared() override
615 {
617 me->SetSpeedRate(MOVE_RUN, 2.5f);
618 }
619
620 void IsSummonedBy(WorldObject* summonerWO) override
621 {
622 Unit* summoner = summonerWO->ToUnit();
623 if (!summoner)
624 return;
625 Unit* victim = summoner->GetVictim();
626 if (!victim)
627 return;
628
630 _scheduler.Schedule(1s, [this, orientation = me->GetAbsoluteAngle(victim) - me->GetOrientation()](TaskContext const& /*context*/)
631 {
632 me->GetMotionMaster()->MovePoint(POINT_RANDOM_DEST, me->GetFirstCollisionPosition(40.0f, orientation));
633 });
634 }
635
636 void UpdateAI(uint32 diff) override
637 {
638 _scheduler.Update(diff);
639 }
640
641 void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override
642 {
643 if (spellInfo->Id == SPELL_HAWK_DIVING_TO_EARTH_DMG && target->GetTypeId() == TYPEID_PLAYER)
644 {
647 }
648 }
649
650 void MovementInform(uint32 type, uint32 pointId) override
651 {
652 if (type != POINT_MOTION_TYPE)
653 return;
654
655 switch (pointId)
656 {
658 {
661 break;
662 }
663 default:
664 break;
665 }
666 }
667
668private:
670};
671
672// 108583 - Force Summoner to Ride Vehicle
685
686// 108582 - Ride Drake
688{
689 void OnRemoveVehicle(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) const
690 {
691 if (Unit* caster = GetCaster())
692 caster->CastSpell(caster, SPELL_DIZZY, TRIGGERED_FULL_MASK);
693 }
694
699};
700
701// Quest 29419 - The Missing Driver
733
734constexpr Position amberleafPos[5] =
735{
736 { 1410.2014f, 3598.6494f, 89.59319f },
737 { 1456.201f, 3568.265f, 88.39075f },
738 { 1383.158f, 3595.447f, 90.3155f },
739 { 1367.333f, 3594.927f, 88.89806f },
740 { 1350.278f, 3588.938f, 89.17908f }
741};
742
743// 6958 - Areatrigger
745{
746 public:
747 at_min_dimwind_captured() : AreaTriggerScript("at_min_dimwind_captured") { }
748
749 bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override
750 {
752 {
753 Creature* minDimwind = player->FindNearestCreatureWithOptions(20.0f, { .StringId = "npc_min_dimwind" });
754
755 if (!minDimwind)
756 return false;
757
760 PhasingHandler::OnConditionChange(player); // phase 630 is added when kill credit but immediately is removed to be added again when Min Dimwind reaches final waypoint
761 }
762
763 return false;
764 }
765};
766
767// 56503 - Min Dimwind (Summon)
769{
771
772 void IsSummonedBy(WorldObject* summoner) override
773 {
774 if (!summoner->IsPlayer())
775 return;
776
777 Creature* amberleafScamp1 = me->FindNearestCreatureWithOptions(20.0f, { .StringId = "npc_amberleaf_scamp_1" });
778 Creature* amberleafScamp2 = me->FindNearestCreatureWithOptions(20.0f, { .StringId = "npc_amberleaf_scamp_2" });
779 Creature* amberleafScamp3 = me->FindNearestCreatureWithOptions(20.0f, { .StringId = "npc_amberleaf_scamp_3" });
780 Creature* amberleafScamp5 = me->FindNearestCreatureWithOptions(20.0f, { .StringId = "npc_amberleaf_scamp_5" });
781
782 if (!amberleafScamp1 || !amberleafScamp2 || !amberleafScamp3 || !amberleafScamp5)
783 return;
784
785 amberleafScamp1->AI()->Talk(SAY_AMBERLEAF_SCAMP_0);
786 amberleafScamp1->GetMotionMaster()->MovePoint(0, amberleafPos[0]);
787
788 amberleafScamp2->GetMotionMaster()->MovePoint(0, amberleafPos[1]);
789
790 amberleafScamp3->GetMotionMaster()->MovePoint(0, amberleafPos[2]);
791
792 amberleafScamp5->GetMotionMaster()->MovePoint(0, amberleafPos[4]);
793
794 _scheduler.Schedule(2s, [this](TaskContext const& /*task*/)
795 {
796 Creature* amberleafScamp4 = me->FindNearestCreatureWithOptions(20.0f, { .StringId = "npc_amberleaf_scamp_4" });
797
798 if (!amberleafScamp4)
799 return;
800
801 amberleafScamp4->AI()->Talk(SAY_AMBERLEAF_SCAMP_1);
802 amberleafScamp4->GetMotionMaster()->MovePoint(0, amberleafPos[3]);
803 });
804
805 _scheduler.Schedule(5s, [this](TaskContext& task)
806 {
807 Unit* summoner = me->ToTempSummon()->GetSummonerUnit();
808
809 if (!summoner)
810 return;
811
812 me->SetFacingToObject(summoner);
813 Talk(SAY_MIN_DIMWIND_TEXT_0, summoner);
814
815 task.Schedule(4s, [this](TaskContext& task)
816 {
818
819 task.Schedule(4s, [this](TaskContext const& /*task*/)
820 {
821 me->GetMotionMaster()->MovePath(PATH_MOVE_RUN, false);
822 });
823 });
824 });
825 }
826
827 void WaypointReached(uint32 waypointId, uint32 pathId) override
828 {
829 if (pathId == PATH_MOVE_RUN)
830 {
831 switch (waypointId)
832 {
833 case WAYPOINT_TALK_0:
834 case WAYPOINT_TALK_1:
835 {
837 break;
838 }
840 {
843 break;
844 }
845 }
846 }
847 else if (pathId == PATH_MOVE_WALK)
848 {
849 if (waypointId == WAYPOINT_DESPAWN)
850 {
851 me->SetFacingTo(0.575958f);
853
854 _scheduler.Schedule(1s, [this](TaskContext const& /*task*/)
855 {
856 if (me->IsSummon())
857 {
858 Unit* summoner = me->ToTempSummon()->GetSummonerUnit();
859
860 if (!summoner)
861 return;
862
863 summoner->RemoveAurasDueToSpell(SPELL_SUMMON_CART_DRIVER);
864 PhasingHandler::OnConditionChange(summoner);
865 }
866 });
867 }
868 }
869 }
870
871 void UpdateAI(uint32 diff) override
872 {
873 _scheduler.Update(diff);
874 }
875
876private:
878};
879
880// 54130 - Amberleaf Scamp
882{
884
885 void MovementInform(uint32 type, uint32 id) override
886 {
887 if (type == POINT_MOTION_TYPE && id == POINT_MOVE_RANDOM && !me->IsInCombat())
888 {
889 me->GetMotionMaster()->MoveRandom(10.0f);
890
891 _scheduler.Schedule(10s, [this](TaskContext const& /*task*/)
892 {
893 if (!me->IsInCombat())
895 });
896 }
897 }
898
899 void JustReachedHome() override
900 {
902 }
903
904 void UpdateAI(uint32 diff) override
905 {
906 _scheduler.Update(diff);
907 }
908
909private:
911};
912
913// Quest 29414 - The Way of the Tushui
915{
916 // Texts
918
919 // Waypoints
923
924constexpr Position aysaJumpPos[3] =
925{
926 { 1196.72f, 3492.85f, 90.9836f },
927 { 1192.29f, 3478.69f, 108.788f },
928 { 1197.99f, 3460.63f, 103.04f }
929};
930
931// 59652 - Aysa Cloudsinger (summon)
933{
935
936 void IsSummonedBy(WorldObject* summoner) override
937 {
938 if (!summoner->IsPlayer())
939 return;
940
941 Talk(SAY_GO_CAVE, summoner);
942
943 _scheduler.Schedule(3s, [this](TaskContext& task)
944 {
945 me->GetMotionMaster()->MoveJump(EVENT_JUMP, aysaJumpPos[0], 12.0f, {}, 5.0f);
946
947 task.Schedule(1700ms, [this](TaskContext& task)
948 {
949 me->GetMotionMaster()->MoveJump(EVENT_JUMP, aysaJumpPos[1], 12.0f, {}, 5.0f);
950
951 task.Schedule(2s, [this](TaskContext const& /*task*/)
952 {
953 me->GetMotionMaster()->MoveJump(POINT_JUMP, aysaJumpPos[2], 12.0f, {}, 5.0f);
954 });
955 });
956 });
957 }
958
959 void MovementInform(uint32 type, uint32 pointId) override
960 {
961 if (type != EFFECT_MOTION_TYPE)
962 return;
963
964 if (pointId == POINT_JUMP)
966 }
967
968 void WaypointPathEnded(uint32 /*nodeId*/, uint32 pathId) override
969 {
970 if (pathId == PATH_CAVE_OF_MEDITATION)
972 }
973
974 void UpdateAI(uint32 diff) override
975 {
976 _scheduler.Update(diff);
977 }
978
979private:
981};
982
1007
1008// 59642 - Aysa Cloudsinger (Cave of Meditation)
1010{
1012
1013 void DoAction(int32 action) override
1014 {
1015 switch (action)
1016 {
1018 {
1019 _finishEvent = false;
1021 break;
1022 }
1024 {
1026 break;
1027 }
1029 {
1030 if (_finishEvent)
1031 return;
1032
1033 Creature* aysa = me->FindNearestCreatureWithOptions(40.0f, { .StringId = "npc_aysa_after_quest_29414", .IgnorePhases = true });
1034
1035 if (!aysa)
1036 return;
1037
1039 _finishEvent = true;
1040 break;
1041 }
1042 default:
1043 break;
1044 }
1045 }
1046
1047 void UpdateAI(uint32 diff) override
1048 {
1049 events.Update(diff);
1050
1051 while (uint32 eventId = events.ExecuteEvent())
1052 {
1053 switch (eventId)
1054 {
1056 {
1058 events.Repeat(11s);
1059 break;
1060 }
1061 default:
1062 break;
1063 }
1064 }
1065 }
1066
1067private:
1070};
1071
1073{
1074 // Waypoint
1076
1077 // Texts
1086
1087 // Spells
1090
1091// 54856 - Master Li Fei (Summon)
1093{
1095
1097 {
1098 if (me->IsSummon())
1099 {
1100 Unit* summoner = me->ToTempSummon()->GetSummonerUnit();
1101
1102 if (!summoner)
1103 return;
1104
1105 me->SetFacingToObject(summoner);
1106 }
1107 }
1108
1109 void IsSummonedBy(WorldObject* summoner) override
1110 {
1111 if (!summoner->IsPlayer())
1112 return;
1113
1115
1116 Seconds delay = 23s;
1117
1118 _scheduler.Schedule(delay, [this](TaskContext const&)
1119 {
1120 FaceToPlayer();
1121 });
1122
1123 delay += 2s;
1124
1125 _scheduler.Schedule(delay, [this](TaskContext const&)
1126 {
1128 });
1129
1130 delay += 10s;
1131
1132 _scheduler.Schedule(delay, [this](TaskContext const&)
1133 {
1135 });
1136
1137 delay += 12s;
1138
1139 _scheduler.Schedule(delay, [this](TaskContext const&)
1140 {
1142 });
1143
1144 delay += 11s;
1145
1146 _scheduler.Schedule(delay, [this](TaskContext const&)
1147 {
1148 FaceToPlayer();
1150 });
1151
1152 delay += 11s;
1153
1154 _scheduler.Schedule(delay, [this](TaskContext const&)
1155 {
1157 });
1158
1159 delay += 9s;
1160
1161 _scheduler.Schedule(delay, [this](TaskContext const&)
1162 {
1163 FaceToPlayer();
1164 });
1165
1166 delay += 2s;
1167
1168 _scheduler.Schedule(delay, [this](TaskContext const&)
1169 {
1171 });
1172
1173 delay += 6s;
1174
1175 _scheduler.Schedule(delay, [this](TaskContext const&)
1176 {
1177 Creature* aysa = me->FindNearestCreatureWithOptions(40.0f, { .StringId = "npc_aysa_quest_29414" });
1178
1179 if (!aysa)
1180 return;
1181
1185 me->DespawnOrUnsummon(200ms);
1186 });
1187 }
1188
1189 void UpdateAI(uint32 diff) override
1190 {
1191 _scheduler.Update(diff);
1192 }
1193
1194private:
1196};
1197
1198// 7756 - Areatrigger
1200{
1201public:
1202 at_cave_of_meditation() : AreaTriggerScript("at_cave_of_meditation") { }
1203
1204 bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override
1205 {
1207 {
1208 if (!player->HasAura(SPELL_MEDITATION_TIMER_BAR))
1210
1211 return true;
1212 }
1213 return false;
1214 }
1215
1216 bool OnExit(Player* player, AreaTriggerEntry const* /*trigger*/) override
1217 {
1219 return true;
1220 }
1221};
1222
1223// 7645 - Areatrigger
1225{
1226public:
1227 at_inside_of_cave_of_meditation() : AreaTriggerScript("at_inside_of_cave_of_meditation") { }
1228
1229 bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override
1230 {
1232 {
1233 if (!player->HasAura(SPELL_SUMMON_MASTER_LI_FEI))
1234 {
1235 Creature* aysa = player->FindNearestCreatureWithOptions(40.0f, { .StringId = "npc_aysa_quest_29414" });
1236
1237 if (!aysa)
1238 return false;
1239
1240 aysa->AI()->Talk(SAY_AYSA_HELP, player);
1243 }
1244
1245 return true;
1246 }
1247 return false;
1248 }
1249
1250 bool OnExit(Player* player, AreaTriggerEntry const* /*trigger*/) override
1251 {
1253 {
1254 if (Creature* aysa = player->FindNearestCreatureWithOptions(40.0f, { .StringId = "npc_aysa_quest_29414", .IgnorePhases = true }))
1255 aysa->AI()->DoAction(ACTION_FINISH_EVENT);
1256 }
1257
1258 return true;
1259 }
1260};
1261
1262// 116421 - Meditation Timer Bar
1264{
1265 void HandleEffectPeriodic(AuraEffect const* /*aurEff*/)
1266 {
1267 if (Unit* target = GetTarget())
1268 {
1269 target->ModifyPower(POWER_ALTERNATE_POWER, 1);
1270
1271 if (target->GetPowerPct(POWER_ALTERNATE_POWER) == 100)
1272 {
1275 Remove();
1276 }
1277 }
1278 }
1279
1284};
1285
1290
1291// 114684 - Flame Spout
1293{
1294 bool Validate(SpellInfo const* /*spellInfo*/) override
1295 {
1297 }
1298
1303
1308};
1309
1310template<uint32 CurseSpellID>
1312{
1313public:
1314 at_singing_pools_transform_base(char const* scriptName) : AreaTriggerScript(scriptName) {}
1315
1316 bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override
1317 {
1318 if (!player->IsAlive() || player->HasAura(Spells::RideVehiclePole))
1319 return true;
1320
1321 if (!player->HasAura(CurseSpellID))
1322 player->CastSpell(player, CurseSpellID);
1323
1324 return true;
1325 }
1326
1327 bool OnExit(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override
1328 {
1329 player->RemoveAurasDueToSpell(CurseSpellID);
1330 return true;
1331 }
1332};
1333
1334// 6986
1335// 6987
1337{
1338public:
1339 at_singing_pools_transform_frog() : AreaTriggerScript("at_singing_pools_transform_frog") {}
1340
1341 bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override
1342 {
1343 if (!player->IsAlive() || player->HasAura(Spells::RideVehiclePole))
1344 return true;
1345
1346 if (!player->HasAura(Spells::CurseOfTheFrog))
1347 player->CastSpell(player, Spells::CurseOfTheFrog);
1348
1351
1352 return true;
1353 }
1354
1355 bool OnExit(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override
1356 {
1358 return true;
1359 }
1360};
1361
1362// 54135 - Master Li Fei
1363struct npc_li_fei : public ScriptedAI
1364{
1365 npc_li_fei(Creature* creature) : ScriptedAI(creature) {}
1366
1367 void OnQuestAccept(Player* player, Quest const* quest) override
1368 {
1370 player->CastSpell(player, Spells::FireCrashCover);
1371 }
1372};
1373
1374// 102499 - Fire Crash
1376{
1377 static constexpr Position PlayerJumpPos = { 1351.3334f, 3939.0347f, 109.32395f, 6.00115f };
1378
1379 void SetDest(SpellDestination& dest) const
1380 {
1381 dest.Relocate(PlayerJumpPos);
1382 }
1383
1388};
1389
1390// 54734 - Master Li Fei (combat)
1392{
1393 npc_li_fei_combat(Creature* creature) : ScriptedAI(creature), _defeatTriggered(false) {}
1394
1400
1401 void Reset() override
1402 {
1403 _defeatTriggered = false;
1404 }
1405
1406 void JustEngagedWith(Unit* /*attacker*/) override
1407 {
1410 }
1411
1412 void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo*/) override
1413 {
1414 if (!_defeatTriggered && me->HealthBelowPctDamaged(50, damage))
1415 {
1416 _defeatTriggered = true;
1418
1419 Creature* liFei = me->FindNearestCreatureWithOptions(15.0f, { .CreatureId = Creatures::MasterLiFei, .IgnorePhases = true });
1420 if (!liFei)
1421 return;
1422
1423 for (ObjectGuid const& guid : me->GetTapList())
1424 {
1425 Player* player = ObjectAccessor::GetPlayer(*me, guid);
1426 if (!player)
1427 continue;
1428
1433 }
1434
1435 liFei->AI()->Talk(Talks::LiFeiDefeat, attacker);
1436
1438 }
1439 }
1440
1441 void KilledUnit(Unit* victim) override
1442 {
1443 Player* player = victim->ToPlayer();
1444 if (!player)
1445 return;
1446
1448 }
1449
1450 void UpdateAI(uint32 diff) override
1451 {
1452 if (!UpdateVictim())
1453 return;
1454
1455 _events.Update(diff);
1456
1458 return;
1459
1460 if (uint32 eventId = _events.ExecuteEvent())
1461 {
1462 switch (eventId)
1463 {
1464 case EVENT_FEET_OF_FURY:
1466
1468 break;
1469 case EVENT_SHADOW_KICK:
1471
1473 break;
1474 default:
1475 break;
1476 }
1477 }
1478 }
1479
1480private:
1483};
1484
1485// 108958 - Feet of Fury
1487{
1488 bool Validate(SpellInfo const* /*spellInfo*/) override
1489 {
1491 }
1492
1493 void PeriodicTick(AuraEffect const* aurEff)
1494 {
1496
1497 Unit* target = GetTarget();
1499 .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR,
1500 .TriggeringAura = aurEff
1501 });
1502 }
1503
1508};
1509
1510// 108936 - Flying Shadow Kick
1530
1540
1541static constexpr CartData CartDataTable[] =
1542{
1543 // Carts
1544 {
1546 .PathId = Paths::CartSingingPools,
1547 .EjectNodeId = Paths::NodeCartRemovePassenger,
1548 .CreditNPC = Creatures::CartSingingPools,
1550 },
1551 {
1553 .PathId = Paths::CartFarmstead,
1554 .EjectNodeId = Paths::NodeCartRemovePassenger,
1555 .CreditNPC = Creatures::CartFarmstead,
1557 },
1558 {
1560 .PathId = Paths::CartForest,
1562 .CreditNPC = Creatures::CartForest,
1563 .QuestId = Quests::NewAllies,
1564 }
1565};
1566
1568{
1569 for (CartData const& data : CartDataTable)
1570 if (data.Entry == entry)
1571 return data;
1572
1573 return {};
1574}
1575
1576// 57208 - Delivery Cart (Singing Pools)
1577// 59496 - Delivery Cart (Farmstead)
1578// 57740 - Delivery Cart (Forbidden Forest)
1580{
1581 npc_delivery_cart(Creature* creature) : ScriptedAI(creature), _data(GetCartData(creature->GetEntry())) { }
1582
1583 void Reset() override
1584 {
1585 _events.Reset();
1586 }
1587
1588 void PassengerBoarded(Unit* passenger, int8 /*seat*/, bool apply) override
1589 {
1590 if (!apply)
1591 return;
1592
1593 Player* player = passenger->ToPlayer();
1594 if (!player)
1595 return;
1596
1598
1601
1602 if (_data.QuestId && player->hasQuest(*_data.QuestId) && _data.CreditNPC)
1603 player->KilledMonsterCredit(*_data.CreditNPC, player->GetGUID());
1604 }
1605
1606 void WaypointReached(uint32 nodeId, uint32 /*pathId*/) override
1607 {
1608 if (_data.EjectNodeId && nodeId == *_data.EjectNodeId)
1610 }
1611
1612 void WaypointPathEnded(uint32 /*nodeId*/, uint32 /*pathId*/) override
1613 {
1614 me->DespawnOrUnsummon(1s);
1615 }
1616
1617 void UpdateAI(uint32 diff) override
1618 {
1619 _events.Update(diff);
1620
1621 while (uint32 eventId = _events.ExecuteEvent())
1622 {
1623 switch (eventId)
1624 {
1627 break;
1630 break;
1631 default:
1632 break;
1633 }
1634 }
1635 }
1636
1637private:
1640};
1641}
1642
1644{
1646
1659
1664
1668
1670 new at_singing_pools_transform_base<Spells::CurseOfTheSkunk>("at_singing_pools_transform_skunk");
1671 new at_singing_pools_transform_base<Spells::CurseOfTheCrocodile>("at_singing_pools_transform_crocodile");
1672 new at_singing_pools_transform_base<Spells::CurseOfTheCrane>("at_singing_pools_transform_crane");
1673 new at_singing_pools_transform_base<Spells::CurseOfTheTurtle>("at_singing_pools_transform_turtle");
1674
1680
1682}
First const & RAND(First const &first, Second const &second, Rest const &... rest)
T GetEntry(std::unordered_map< uint32, T > const &map, CriteriaTreeEntry const *tree)
int8_t int8
Definition Define.h:152
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
std::chrono::seconds Seconds
Seconds shorthand typedef.
Definition Duration.h:28
@ POINT_MOTION_TYPE
@ EFFECT_MOTION_TYPE
@ TYPEID_PLAYER
Definition ObjectGuid.h:44
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
Spells
Definition PlayerAI.cpp:32
@ QUEST_STATUS_INCOMPLETE
Definition QuestDef.h:150
@ QUEST_STATUS_COMPLETE
Definition QuestDef.h:148
#define RegisterCreatureAI(ai_name)
Definition ScriptMgr.h:1392
#define RegisterGameObjectAI(ai_name)
Definition ScriptMgr.h:1410
#define RegisterSpellScript(spell_script)
Definition ScriptMgr.h:1383
SpellEffIndex
@ EFFECT_1
@ EFFECT_0
@ TARGET_DEST_NEARBY_ENTRY
@ EMOTE_ONESHOT_MONKOFFENSE_ATTACKUNARMEDOFF
@ EMOTE_ONESHOT_MONKOFFENSE_PARRYUNARMED
@ EMOTE_STATE_READY_UNARMED
@ EMOTE_ONESHOT_PALMSTRIKE
@ EMOTE_ONESHOT_BOW
@ EMOTE_STATE_MONKOFFENSE_READYUNARMED
@ EMOTE_ONESHOT_SALUTE
@ EMOTE_ONESHOT_MONKOFFENSE_SPECIALUNARMED
@ EMOTE_ONESHOT_NONE
@ EMOTE_ONESHOT_MONKOFFENSE_ATTACKUNARMED
@ EVENT_JUMP
@ SPELL_EFFECT_SCRIPT_EFFECT
@ POWER_ALTERNATE_POWER
AuraEffectHandleModes
@ AURA_EFFECT_HANDLE_REAL
@ SPELL_AURA_CONTROL_VEHICLE
@ SPELL_AURA_PERIODIC_DUMMY
@ SPELL_AURA_PERIODIC_TRIGGER_SPELL
@ TRIGGERED_FULL_MASK
Used when doing CastSpell with triggered == true.
@ TRIGGERED_IGNORE_CAST_IN_PROGRESS
Will not check if a current cast is in progress.
@ TRIGGERED_DONT_REPORT_CAST_ERROR
Will return SPELL_FAILED_DONT_REPORT in CheckCast functions.
#define SpellEffectFn(F, I, N)
#define AuraEffectPeriodicFn(F, I, N)
#define SpellDestinationTargetSelectFn(F, I, N)
#define AuraEffectRemoveFn(F, I, N, M)
EvadeReason
#define CAST_AI(a, b)
Definition UnitAI.h:29
@ MOVE_RUN
@ REACT_PASSIVE
@ UNIT_STAND_STATE_STAND
Definition UnitDefines.h:42
DamageEffectType
@ UNIT_FLAG_NON_ATTACKABLE_2
@ UNIT_STATE_CASTING
Definition Unit.h:276
@ UNIT_STATE_STUNNED
Definition Unit.h:264
Creatures
void PreventDefaultAction()
HookList< EffectApplyHandler > AfterEffectRemove
HookList< EffectPeriodicHandler > OnEffectPeriodic
Unit * GetCaster() const
Unit * GetTarget() const
HookList< EffectApplyHandler > OnEffectRemove
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
bool IsPlayer() const
Definition BaseEntity.h:173
TypeID GetTypeId() const
Definition BaseEntity.h:166
virtual void EnterEvadeMode(EvadeReason why=EvadeReason::Other)
void Talk(uint8 id, WorldObject const *whisperTarget=nullptr)
bool UpdateVictim()
Creature *const me
Definition CreatureAI.h:63
void SetImmuneToPC(bool apply) override
Definition Creature.h:184
void SetReactState(ReactStates st)
Definition Creature.h:174
GuidUnorderedSet const & GetTapList() const
Definition Creature.h:304
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
void InitializeReactState()
bool IsInEvadeMode() const
Definition Creature.h:217
CreatureAI * AI() const
Definition Creature.h:228
uint32 ExecuteEvent()
Definition EventMap.cpp:77
void Update(uint32 time)
Definition EventMap.h:61
void Repeat(Milliseconds time)
Definition EventMap.cpp:67
void ScheduleEvent(uint32 eventId, Milliseconds time, uint32 group=0, uint8 phase=0)
Definition EventMap.cpp:40
void CancelEvent(uint32 eventId)
Definition EventMap.cpp:135
void Reset()
Definition EventMap.cpp:25
GameObject *const me
GameObjectAI(GameObject *go, uint32 scriptId={}) noexcept
void DespawnOrUnsummon(Milliseconds delay=0ms, Seconds forceRespawnTime=0s)
void MoveRandom(float wanderDistance=0.0f, Optional< Milliseconds > duration={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::ForceWalk, MovementSlot slot=MOTION_SLOT_DEFAULT, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void MovePoint(uint32 id, Position const &pos, bool generatePath=true, Optional< float > finalOrient={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Optional< float > closeEnoughDistance={}, Optional< MovementFadeObject > fadeObject={}, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void MoveTargetedHome()
void MovePath(uint32 pathId, bool repeatable, Optional< Milliseconds > duration={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Optional< std::pair< Milliseconds, Milliseconds > > waitTimeRangeAtPathEnd={}, Optional< float > wanderDistanceAtPathEnds={}, Optional< bool > followPathBackwardsFromEndToStart={}, Optional< bool > exactSplinePath={}, bool generatePath=true, Optional< MovementFadeObject > fadeObject={}, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void InitializeDefault()
static ObjectGuid const Empty
Definition ObjectGuid.h:314
Player * ToPlayer()
Definition Object.h:126
virtual bool hasQuest(uint32) const
Definition Object.h:103
uint32 GetEntry() const
Definition Object.h:89
Unit * ToUnit()
Definition Object.h:116
static bool OnConditionChange(WorldObject *object, bool updateVisibility=true)
void KilledMonsterCredit(uint32 entry, ObjectGuid guid=ObjectGuid::Empty)
Definition Player.cpp:16679
QuestStatus GetQuestStatus(uint32 quest_id) const
Definition Player.cpp:15962
void FailQuest(uint32 quest_id)
Definition Player.cpp:15347
uint32 GetQuestId() const
Definition QuestDef.h:637
bool OnExit(Player *player, AreaTriggerEntry const *) override
bool OnTrigger(Player *player, AreaTriggerEntry const *) override
bool OnExit(Player *player, AreaTriggerEntry const *) override
bool OnTrigger(Player *player, AreaTriggerEntry const *) override
bool OnTrigger(Player *player, AreaTriggerEntry const *) override
bool OnExit(Player *player, AreaTriggerEntry const *) override
bool OnTrigger(Player *player, AreaTriggerEntry const *) override
bool OnTrigger(Player *player, AreaTriggerEntry const *) override
bool OnExit(Player *player, AreaTriggerEntry const *) override
void OnRemove(AuraEffect const *, AuraEffectHandleModes)
void OnRemoveVehicle(AuraEffect const *, AuraEffectHandleModes) const
uint32 const Id
Definition SpellInfo.h:328
static bool ValidateSpellInfo(std::initializer_list< uint32 > spellIds)
Unit * GetCaster() const
HookList< DestinationTargetSelectHandler > OnDestinationTargetSelect
int32 GetEffectValueAsInt() const
Unit * GetHitUnit() const
HookList< EffectHandler > OnEffectHitTarget
TaskContext & Schedule(TaskScheduler::duration_t time, TaskScheduler::task_handler_t task)
TaskContext & Repeat(TaskScheduler::duration_t duration)
TaskScheduler & CancelAll()
TaskScheduler & Schedule(duration_t time, task_handler_t task)
TaskScheduler & Update()
Update the scheduler to the current time.
Unit * GetSummonerUnit() const
SpellCastResult DoCastSelf(uint32 spellId, CastSpellExtraArgs const &args={})
Definition UnitAI.h:160
virtual void DoAction(int32 param)
Definition UnitAI.h:73
SpellCastResult DoCastVictim(uint32 spellId, CastSpellExtraArgs const &args={})
Definition UnitAI.cpp:180
SpellCastResult DoCast(uint32 spellId)
Definition UnitAI.cpp:89
Definition Unit.h:635
void CombatStop(bool includingCast=false, bool mutualPvP=true, bool(*unitFilter)(Unit const *otherUnit)=nullptr)
Definition Unit.cpp:6012
void RemoveAura(AuraApplicationMap::iterator &i, AuraRemoveMode mode=AURA_REMOVE_BY_DEFAULT)
Definition Unit.cpp:3828
void SetStandState(UnitStandStateType state, uint32 animKitID=0)
Definition Unit.cpp:10731
void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid=0, bool withInstant=true)
Definition Unit.cpp:3231
MotionMaster * GetMotionMaster()
Definition Unit.h:1723
void SetFacingToObject(WorldObject const *object, bool force=true)
Definition Unit.cpp:13307
bool IsAlive() const
Definition Unit.h:1185
TempSummon * ToTempSummon()
Definition Unit.h:1828
UnitAI * GetAI() const
Definition Unit.h:668
void SetUninteractible(bool apply)
Definition Unit.cpp:8564
void SetEmoteState(Emote emote)
Definition Unit.h:865
bool IsSummon() const
Definition Unit.h:749
bool HealthBelowPctDamaged(float pct, uint32 damage) const
Definition Unit.h:793
Unit * GetVictim() const
Definition Unit.h:726
void SetSpeedRate(UnitMoveType mtype, float rate)
Definition Unit.cpp:8942
void SetFacingTo(float const ori, bool force=true)
Definition Unit.cpp:13289
bool HasUnitState(const uint32 f) const
Definition Unit.h:743
bool HasAura(uint32 spellId, ObjectGuid casterGUID=ObjectGuid::Empty, ObjectGuid itemCasterGUID=ObjectGuid::Empty, uint32 reqEffMask=0) const
Definition Unit.cpp:4804
void RemoveAllAuras()
Definition Unit.cpp:4382
void HandleEmoteCommand(Emote emoteId, Player *target=nullptr, Trinity::IteratorPair< int32 const * > spellVisualKitIds={}, int32 sequenceVariation=0)
Definition Unit.cpp:1657
void SetUnitFlag(UnitFlags flags)
Definition Unit.h:846
bool AttackStop()
Definition Unit.cpp:5965
void RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID=ObjectGuid::Empty, uint32 reqEffMask=0, AuraRemoveMode removeMode=AURA_REMOVE_BY_DEFAULT)
Definition Unit.cpp:3974
bool IsInCombat() const
Definition Unit.h:1058
void RemoveUnitFlag(UnitFlags flags)
Definition Unit.h:847
bool isDead() const
Definition Unit.h:1187
Creature * FindNearestCreatureWithOptions(float range, FindCreatureOptions const &options) const
Definition Object.cpp:1526
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition Object.cpp:2217
float GetDistance(WorldObject const *obj) const
Definition Object.cpp:432
TC_GAME_API Player * GetPlayer(Map const *, ObjectGuid const &guid)
TC_GAME_API Creature * GetCreature(WorldObject const &u, ObjectGuid const &guid)
void HandleEmoteNearbyTushuiTrainees(Creature *leader, Emote emote)
auto SelectRandomContainerElement(C const &container) -> std::add_const_t< decltype(*std::ranges::begin(container))> &
Definition Containers.h:110
TriggerCastFlags TriggerFlags
static void VisitGridObjects(WorldObject const *obj, T &visitor, float radius, bool dont_load=true)
Definition CellImpl.h:179
constexpr void SetOrientation(float orientation)
Definition Position.h:82
float GetExactDist(float x, float y, float z) const
Definition Position.h:129
float GetAbsoluteAngle(float x, float y) const
Definition Position.h:136
constexpr float GetOrientation() const
Definition Position.h:90
ScriptedAI(Creature *creature, uint32 scriptId=0) noexcept
void PassengerBoarded(Unit *passenger, int8, bool apply) override
== Fields =======================================
void MovementInform(uint32 type, uint32 pointId) override
void SpellHitTarget(WorldObject *target, SpellInfo const *spellInfo) override
void SpellHitTarget(WorldObject *target, SpellInfo const *spellInfo) override
void DamageTaken(Unit *attacker, uint32 &damage, DamageEffectType, SpellInfo const *) override
void OnQuestAccept(Player *player, Quest const *quest) override
void WaypointReached(uint32 waypointId, uint32 pathId) override
void DamageTaken(Unit *attacker, uint32 &damage, DamageEffectType, SpellInfo const *) override
void Relocate(Position const &pos)
Definition Spell.cpp:82
void AddSC_zone_the_wandering_isle()