TrinityCore
Loading...
Searching...
No Matches
SmartScript.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 "SmartScript.h"
19#include "CellImpl.h"
20#include "ChatTextBuilder.h"
21#include "Containers.h"
22#include "Creature.h"
23#include "CreatureTextMgr.h"
24#include "CreatureTextMgrImpl.h"
25#include "DB2Stores.h"
26#include "GameEventMgr.h"
27#include "GameEventSender.h"
28#include "GameObject.h"
29#include "GameTime.h"
30#include "GossipDef.h"
31#include "GridNotifiersImpl.h"
32#include "Group.h"
33#include "InstanceScript.h"
34#include "Language.h"
35#include "Log.h"
36#include "Map.h"
37#include "MotionMaster.h"
38#include "MovementTypedefs.h"
39#include "ObjectAccessor.h"
40#include "ObjectMgr.h"
41#include "PhasingHandler.h"
42#include "Random.h"
43#include "ScriptActions.h"
44#include "SmartAI.h"
45#include "SpellMgr.h"
46#include "SpellAuras.h"
47#include "TemporarySummon.h"
48#include "Vehicle.h"
49#include "WaypointDefines.h"
50#include "WaypointManager.h"
51
53{
54 go = nullptr;
55 me = nullptr;
56 player = nullptr;
57 trigger = nullptr;
58 areaTrigger = nullptr;
59 sceneTemplate = nullptr;
60 quest = nullptr;
61 event = 0;
62 mEventPhase = 0;
63 mPathId = 0;
64 mTextTimer = 0;
65 mLastTextID = 0;
66 mUseTextTimer = false;
67 mTalkerEntry = 0;
74}
75
76SmartScript::SmartScript(SmartScript const& other) = default;
77SmartScript::SmartScript(SmartScript&& other) noexcept = default;
78SmartScript& SmartScript::operator=(SmartScript const& other) = default;
79SmartScript& SmartScript::operator=(SmartScript&& other) noexcept = default;
81
82bool SmartScript::IsSmart(Creature* c, bool silent) const
83{
84 if (!c)
85 return false;
86
87 bool smart = true;
88 if (!dynamic_cast<SmartAI*>(c->AI()))
89 smart = false;
90
91 if (!smart && !silent)
92 TC_LOG_ERROR("sql.sql", "SmartScript: Action target Creature (GUID: {} Entry: {}) is not using SmartAI, action called by Creature (GUID: {} Entry: {}) skipped to prevent crash.", c->GetSpawnId(), c->GetEntry(), uint64(me ? me->GetSpawnId() : UI64LIT(0)), me ? me->GetEntry() : 0);
93
94 return smart;
95}
96
97bool SmartScript::IsSmart(GameObject* g, bool silent) const
98{
99 if (!g)
100 return false;
101
102 bool smart = true;
103 if (!dynamic_cast<SmartGameObjectAI*>(g->AI()))
104 smart = false;
105
106 if (!smart && !silent)
107 TC_LOG_ERROR("sql.sql", "SmartScript: Action target GameObject (GUID: {} Entry: {}) is not using SmartGameObjectAI, action called by GameObject (GUID: {} Entry: {}) skipped to prevent crash.", g->GetSpawnId(), g->GetEntry(), uint64(go ? go->GetSpawnId() : UI64LIT(0)), go ? go->GetEntry() : 0);
108
109 return smart;
110}
111
112bool SmartScript::IsSmart(bool silent) const
113{
114 if (me)
115 return IsSmart(me, silent);
116 if (go)
117 return IsSmart(go, silent);
118 return false;
119}
120
122{
123 _storedTargets.erase(id);
124}
125
127{
128 // insert or replace
129 _storedTargets.erase(id);
130 _storedTargets.emplace(id, ObjectGuidVector(targets));
131}
132
134{
135 auto [itr, inserted] = _storedTargets.try_emplace(id, targets);
136 if (!inserted)
137 for (WorldObject* obj : targets)
138 itr->second.AddGuid(obj->GetGUID());
139}
140
142{
143 auto itr = _storedTargets.find(id);
144 if (itr != _storedTargets.end())
145 return itr->second.GetObjectVector(ref);
146 return nullptr;
147}
148
150{
151 CounterMap::iterator itr = mCounterList.find(id);
152 if (itr != mCounterList.end())
153 {
154 if (reset == 0)
155 itr->second += value;
156 else
157 itr->second = value;
158 }
159 else
160 mCounterList.insert(std::make_pair(id, value));
161
163}
164
166{
167 CounterMap::const_iterator itr = mCounterList.find(id);
168 if (itr != mCounterList.end())
169 return itr->second;
170 return 0;
171}
172
174{
175 auto bounds = searchObject->GetMap()->GetGameObjectBySpawnIdStore().equal_range(guid);
176 if (bounds.first == bounds.second)
177 return nullptr;
178
179 return bounds.first->second;
180}
181
183{
184 auto bounds = searchObject->GetMap()->GetCreatureBySpawnIdStore().equal_range(guid);
185 if (bounds.first == bounds.second)
186 return nullptr;
187
188 auto creatureItr = std::find_if(bounds.first, bounds.second, [](Map::CreatureBySpawnIdContainer::value_type const& pair)
189 {
190 return pair.second->IsAlive();
191 });
192
193 return creatureItr != bounds.second ? creatureItr->second : bounds.first->second;
194}
195
197{
200 {
201 if (!(event.event.event_flags & SMART_EVENT_FLAG_DONT_RESET))
202 {
204 event.runOnce = false;
205 }
206
208 {
211 }
212 }
215}
216
218{
219 WorldObject* lookupRoot = me;
220 if (!lookupRoot)
221 lookupRoot = go;
222
223 if (lookupRoot)
224 {
225 if (!meOrigGUID.IsEmpty())
226 {
227 if (Creature* m = ObjectAccessor::GetCreature(*lookupRoot, meOrigGUID))
228 {
229 me = m;
230 go = nullptr;
231 areaTrigger = nullptr;
232 player = nullptr;
233 }
234 }
235
236 if (!goOrigGUID.IsEmpty())
237 {
239 {
240 me = nullptr;
241 go = o;
242 areaTrigger = nullptr;
243 player = nullptr;
244 }
245 }
246 }
249}
250
251void SmartScript::ProcessEventsFor(SMART_EVENT e, Unit* unit, uint32 var0, uint32 var1, bool bvar, SpellInfo const* spell, GameObject* gob, std::string_view varString)
252{
254
255 // Allow only a fixed number of nested ProcessEventsFor calls
257 {
258 TC_LOG_WARN("scripts.ai", "SmartScript::ProcessEventsFor: reached the limit of max allowed nested ProcessEventsFor() calls with event {}, skipping!\n{}", e, GetBaseObject()->GetDebugInfo());
259 }
260 else
261 {
263 {
264 SMART_EVENT eventType = SMART_EVENT(event.GetEventType());
265 if (eventType == SMART_EVENT_LINK)//special handling
266 continue;
267
268 if (eventType == e)
269 if (sConditionMgr->IsObjectMeetingSmartEventConditions(event.entryOrGuid, event.event_id, event.source_type, unit, GetBaseObject()))
270 ProcessEvent(event, unit, var0, var1, bvar, spell, gob, varString);
271 }
272 }
273
275}
276
277namespace
278{
279template <typename Result, typename ConcreteActionImpl = Scripting::v2::ActionResult<Result>, typename... Args>
280static std::shared_ptr<ConcreteActionImpl> CreateTimedActionListWaitEventFor(SmartScriptHolder const& e, Args&&... args)
281{
283 return nullptr;
284
286 return nullptr;
287
288 return std::make_shared<ConcreteActionImpl>(std::forward<Args>(args)...);
289}
290
291template <typename InnerResult>
292struct MultiActionResult : Scripting::v2::ActionResult<void>
293{
294 std::vector<Scripting::v2::ActionResult<InnerResult>> Results;
295
296 explicit MultiActionResult(std::size_t estimatedSize) { Results.reserve(estimatedSize); }
297
298 bool IsReady() const noexcept override
299 {
300 return std::ranges::all_of(Results, [](Scripting::v2::ActionResult<InnerResult> const& result) { return result.IsReady(); });
301 }
302};
303}
304
305void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, uint32 var1, bool bvar, SpellInfo const* spell, GameObject* gob, std::string_view varString)
306{
307 e.runOnce = true; //used for repeat check
308
309 // calc random
311 {
313 return;
314 }
315
316 // Remove SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL flag after processing roll chances as it's not needed anymore
317 e.event.event_flags &= ~SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL;
318
319 if (unit)
320 mLastInvoker = unit->GetGUID();
321
322 if (Unit* tempInvoker = GetLastInvoker())
323 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: Invoker: {} {}", tempInvoker->GetName(), tempInvoker->GetGUID());
324
325 ObjectVector targets;
326 GetTargets(targets, e, Coalesce<WorldObject>(unit, gob));
327
328 switch (e.GetActionType())
329 {
331 {
332 Creature* talker = e.target.type == 0 ? me : nullptr;
333 Unit* talkTarget = nullptr;
334
335 for (WorldObject* target : targets)
336 {
337 if (Unit* targetUnit = target->ToUnit())
338 {
339 if (targetUnit->IsPet()) // Prevented sending text to pets.
340 continue;
341
342 if (targetUnit->IsPlayer() || e.action.talk.useTalkTarget)
343 {
344 talker = me;
345 talkTarget = targetUnit;
346 }
347 else
348 talker = targetUnit->ToCreature();
349
350 break;
351 }
352 }
353
354 if (!talkTarget)
355 talkTarget = GetLastInvoker();
356
357 if (!talker)
358 break;
359
360 mTalkerEntry = talker->GetEntry();
363 mUseTextTimer = true;
364 uint32 duration = sCreatureTextMgr->SendChat(talker, uint8(e.action.talk.textGroupID), talkTarget);
365 mTimedActionWaitEvent = CreateTimedActionListWaitEventFor<void, Scripting::v2::WaitAction>(e, GameTime::Now() + Milliseconds(duration));
366 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_TALK: talker: {} {}, textGuid: {}",
367 talker->GetName(), talker->GetGUID(), Object::GetGUID(talkTarget));
368 break;
369 }
371 {
372 uint32 duration = 0;
373 for (WorldObject* target : targets)
374 {
375 if (Creature* creatureTarget = target->ToCreature())
376 duration = std::max(sCreatureTextMgr->SendChat(creatureTarget, uint8(e.action.simpleTalk.textGroupID), Object::ToPlayer(GetLastInvoker())), duration);
377 else if (Player* playerTarget = target->ToPlayer(); playerTarget && me)
379 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SIMPLE_TALK: talker: {} {}, textGroupId: {}",
380 target->GetName(), target->GetGUID(), uint8(e.action.simpleTalk.textGroupID));
381 }
382 mTimedActionWaitEvent = CreateTimedActionListWaitEventFor<void, Scripting::v2::WaitAction>(e, GameTime::Now() + Milliseconds(duration));
383 break;
384 }
386 {
387 for (WorldObject* target : targets)
388 {
389 if (Unit* unitTarget = target->ToUnit())
390 {
391 unitTarget->HandleEmoteCommand(static_cast<Emote>(e.action.emote.emote));
392 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_PLAY_EMOTE: target: {} {}, emote: {}",
393 target->GetName(), target->GetGUID(), e.action.emote.emote);
394 }
395 }
396 break;
397 }
399 {
400 for (WorldObject* target : targets)
401 {
402 if (e.action.sound.distance == 1)
403 target->PlayDistanceSound(e.action.sound.sound, e.action.sound.onlySelf ? target->ToPlayer() : nullptr);
404 else
405 target->PlayDirectSound(e.action.sound.sound, e.action.sound.onlySelf ? target->ToPlayer() : nullptr, e.action.sound.keyBroadcastTextId);
406
407 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SOUND: target: {} {}, sound: {}, onlyself: {}",
408 target->GetName(), target->GetGUID(), e.action.sound.sound, e.action.sound.onlySelf);
409 }
410 break;
411 }
413 {
414 for (WorldObject* target : targets)
415 {
416 if (Creature* creatureTarget = target->ToCreature())
417 {
419 {
420 creatureTarget->SetFaction(e.action.faction.factionID);
421 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SET_FACTION: Creature {} set faction to {}",
422 target->GetGUID(), e.action.faction.factionID);
423 }
424 else
425 {
426 creatureTarget->RestoreFaction();
427 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SET_FACTION: Creature {} set faction to {}",
428 target->GetGUID(), creatureTarget->GetFaction());
429 }
430 }
431 }
432 break;
433 }
435 {
436 for (WorldObject* target : targets)
437 {
438 Creature* creatureTarget = target->ToCreature();
439 if (!creatureTarget)
440 continue;
441
443 {
444 //set model based on entry from creature_template
446 {
447 if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature))
448 {
449 CreatureModel const* model = ObjectMgr::ChooseDisplayId(ci);
450 creatureTarget->SetDisplayId(model->CreatureDisplayID);
451 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: Creature {} set displayid to {}",
452 target->GetGUID(), model->CreatureDisplayID);
453 }
454 }
455 //if no param1, then use value from param2 (modelId)
456 else
457 {
458 creatureTarget->SetDisplayId(e.action.morphOrMount.model);
459 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: Creature {} set displayid to {}",
460 target->GetGUID(), e.action.morphOrMount.model);
461 }
462 }
463 else
464 {
465 creatureTarget->DeMorph();
466 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: Creature {} demorphs.",
467 target->GetGUID());
468 }
469 }
470 break;
471 }
473 {
474 for (WorldObject* target : targets)
475 {
476 if (Player* playerTarget = target->ToPlayer())
477 {
478 playerTarget->FailQuest(e.action.quest.quest);
479 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_FAIL_QUEST: Player {} fails quest {}",
480 target->GetGUID(), e.action.quest.quest);
481 }
482 }
483 break;
484 }
486 {
487 for (WorldObject* target : targets)
488 {
489 if (Player* player = target->ToPlayer())
490 {
491 if (Quest const* q = sObjectMgr->GetQuestTemplate(e.action.questOffer.questID))
492 {
493 if (me && e.action.questOffer.directAdd == 0)
494 {
495 if (player->CanTakeQuest(q, true))
496 {
497 player->PlayerTalkClass->SendQuestGiverQuestDetails(q, me->GetGUID(), true, false);
498 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_OFFER_QUEST: Player {} - offering quest {}", player->GetGUID(), e.action.questOffer.questID);
499 }
500 }
501 else
502 {
504 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_OFFER_QUEST: Player {} - quest {} added",
506 }
507 }
508 }
509 }
510 break;
511 }
513 {
514 for (WorldObject* target : targets)
515 if (Creature* creatureTarget = target->ToCreature())
516 creatureTarget->SetReactState(ReactStates(e.action.react.state));
517 break;
518 }
520 {
521 std::array<uint32, std::extent_v<decltype(e.action.randomEmote.emotes)>> emotes;
522 auto emotesEnd = std::ranges::remove_copy(e.action.randomEmote.emotes, emotes.begin(), 0u).out;
523
524 for (WorldObject* target : targets)
525 {
526 if (Unit* unitTarget = target->ToUnit())
527 {
528 Emote emote = static_cast<Emote>(Trinity::Containers::SelectRandomContainerElement(Trinity::IteratorPair(emotes.begin(), emotesEnd)));
529 unitTarget->HandleEmoteCommand(emote);
530 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_RANDOM_EMOTE: Creature {} handle random emote {}",
531 target->GetGUID(), emote);
532 }
533 }
534 break;
535 }
537 {
538 if (!me)
539 break;
540
541 for (auto* ref : me->GetThreatManager().GetModifiableThreatList())
542 {
543 ref->ModifyThreatByPercent(std::max<int32>(-100,int32(e.action.threatPCT.threatINC) - int32(e.action.threatPCT.threatDEC)));
544 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_THREAT_ALL_PCT: Creature {} modify threat for unit {}, value {}",
545 me->GetGUID(), ref->GetVictim()->GetGUID(), int32(e.action.threatPCT.threatINC)-int32(e.action.threatPCT.threatDEC));
546 }
547 break;
548 }
550 {
551 if (!me)
552 break;
553
554 for (WorldObject* target : targets)
555 {
556 if (Unit* unitTarget = target->ToUnit())
557 {
559 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_THREAT_SINGLE_PCT: Creature {} modify threat for unit {}, value {}",
560 me->GetGUID(), target->GetGUID(), int32(e.action.threatPCT.threatINC) - int32(e.action.threatPCT.threatDEC));
561 }
562 }
563 break;
564 }
566 {
567 if (targets.empty())
568 break;
569
570 if (e.action.cast.targetsLimit > 0 && targets.size() > e.action.cast.targetsLimit)
572
573 bool failedSpellCast = false, successfulSpellCast = false;
574
577 {
580 else
582 }
583
584 std::shared_ptr<MultiActionResult<SpellCastResult>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<SpellCastResult>>(e, targets.size());
585
586 for (WorldObject* target : targets)
587 {
588 if (e.action.cast.castFlags & SMARTCAST_AURA_NOT_PRESENT && (!target->IsUnit() || target->ToUnit()->HasAura(e.action.cast.spell)))
589 {
590 TC_LOG_DEBUG("scripts.ai", "Spell {} not cast because it has flag SMARTCAST_AURA_NOT_PRESENT and the target ({}) already has the aura", e.action.cast.spell, target->GetGUID());
591 continue;
592 }
593
594 if (waitEvent)
595 {
596 args.SetScriptResult(Scripting::v2::ActionResult<SpellCastResult>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() }));
597 args.SetScriptWaitsForSpellHit((e.action.cast.castFlags & SMARTCAST_WAIT_FOR_HIT) != 0);
598 }
599
601 if (me)
602 {
603 if (e.action.cast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS)
605
606 result = me->CastSpell(target, e.action.cast.spell, args);
607 }
608 else if (go)
609 result = go->CastSpell(target, e.action.cast.spell, args);
610
611 bool spellCastFailed = result != SPELL_CAST_OK && result != SPELL_FAILED_SPELL_IN_PROGRESS;
612 if (me && e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE)
613 {
614 // If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS.
615 ENSURE_AI(SmartAI, me->AI())->SetCombatMove(spellCastFailed, true);
616 }
617
618 if (spellCastFailed)
619 failedSpellCast = true;
620 else
621 successfulSpellCast = true;
622
623 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_CAST:: {} casts spell {} on target {} with castflags {}",
624 me ? me->GetGUID() : go->GetGUID(), e.action.cast.spell, target->GetGUID(), e.action.cast.castFlags);
625 }
626
627 if (waitEvent && !waitEvent->Results.empty())
628 mTimedActionWaitEvent = std::move(waitEvent);
629
630 // If there is at least 1 failed cast and no successful casts at all, retry again on next loop
631 if (failedSpellCast && !successfulSpellCast)
632 {
633 RetryLater(e, true);
634 // Don't execute linked events
635 return;
636 }
637 break;
638 }
640 {
641 if (targets.empty())
642 break;
643
646
647 std::shared_ptr<MultiActionResult<SpellCastResult>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<SpellCastResult>>(e, targets.size());
648
650 if (e.action.cast.castFlags & SMARTCAST_TRIGGERED)
651 {
652 if (e.action.cast.triggerFlags)
653 args.TriggerFlags = TriggerCastFlags(e.action.cast.triggerFlags);
654 else
655 args.TriggerFlags = TRIGGERED_FULL_MASK;
656 }
657
658 for (WorldObject* target : targets)
659 {
660 if (e.action.cast.castFlags & SMARTCAST_AURA_NOT_PRESENT && (!target->IsUnit() || target->ToUnit()->HasAura(e.action.cast.spell)))
661 continue;
662
663 if (waitEvent)
664 {
665 args.SetScriptResult(Scripting::v2::ActionResult<SpellCastResult>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() }));
666 args.SetScriptWaitsForSpellHit((e.action.cast.castFlags & SMARTCAST_WAIT_FOR_HIT) != 0);
667 }
668
669 if (e.action.cast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS && target->IsUnit())
670 target->ToUnit()->InterruptNonMeleeSpells(false);
671
672 target->CastSpell(target, e.action.cast.spell, args);
673 }
674 if (waitEvent && !waitEvent->Results.empty())
675 mTimedActionWaitEvent = std::move(waitEvent);
676 break;
677 }
679 {
680 Unit* tempLastInvoker = GetLastInvoker(unit);
681 if (!tempLastInvoker)
682 break;
683
684 if (targets.empty())
685 break;
686
689
692 {
695 else
697 }
698
699 std::shared_ptr<MultiActionResult<SpellCastResult>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<SpellCastResult>>(e, targets.size());
700
701 for (WorldObject* target : targets)
702 {
703 if (e.action.cast.castFlags & SMARTCAST_AURA_NOT_PRESENT && (!target->IsUnit() || target->ToUnit()->HasAura(e.action.cast.spell)))
704 {
705 TC_LOG_DEBUG("scripts.ai", "Spell {} not cast because it has flag SMARTCAST_AURA_NOT_PRESENT and the target ({}) already has the aura", e.action.cast.spell, target->GetGUID());
706 continue;
707 }
708
709 if (e.action.cast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS)
710 tempLastInvoker->InterruptNonMeleeSpells(false);
711
712 if (waitEvent)
713 {
714 args.SetScriptResult(Scripting::v2::ActionResult<SpellCastResult>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() }));
715 args.SetScriptWaitsForSpellHit((e.action.cast.castFlags & SMARTCAST_WAIT_FOR_HIT) != 0);
716 }
717
718 tempLastInvoker->CastSpell(target, e.action.cast.spell, args);
719 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_INVOKER_CAST: Invoker {} casts spell {} on target {} with castflags {}",
720 tempLastInvoker->GetGUID(), e.action.cast.spell, target->GetGUID(), e.action.cast.castFlags);
721 }
722 if (waitEvent && !waitEvent->Results.empty())
723 mTimedActionWaitEvent = std::move(waitEvent);
724 break;
725 }
727 {
728 for (WorldObject* target : targets)
729 {
730 if (GameObject* gameObject = target->ToGameObject())
731 {
732 // Activate
733 gameObject->SetLootState(GO_READY);
734 gameObject->UseDoorOrButton(0, false, unit);
735 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_ACTIVATE_GOBJECT. Gameobject {} activated",
736 target->GetGUID());
737 }
738 }
739 break;
740 }
742 {
743 for (WorldObject* target : targets)
744 {
745 if (GameObject* gameObject = target->ToGameObject())
746 {
747 gameObject->ResetDoorOrButton();
748 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_RESET_GOBJECT. Gameobject {} reset",
749 target->GetGUID());
750 }
751 }
752 break;
753 }
755 {
756 for (WorldObject* target : targets)
757 {
758 if (Unit* unitTarget = target->ToUnit())
759 {
760 unitTarget->SetEmoteState(Emote(e.action.emote.emote));
761 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SET_EMOTE_STATE. Unit {} set emotestate to {}",
762 target->GetGUID(), e.action.emote.emote);
763 }
764 }
765 break;
766 }
768 {
770 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_AUTO_ATTACK: Creature: {} bool on = {}",
772 break;
773 }
775 {
776 if (!IsSmart())
777 break;
778
779 bool move = e.action.combatMove.move != 0;
780 ENSURE_AI(SmartAI, me->AI())->SetCombatMove(move);
781 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_ALLOW_COMBAT_MOVEMENT: Creature {} bool on = {}",
783 break;
784 }
786 {
787 if (!GetBaseObject())
788 break;
789
791 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SET_EVENT_PHASE: Creature {} set event phase {}",
792 GetBaseObject()->GetGUID(), e.action.setEventPhase.phase);
793 break;
794 }
796 {
797 if (!GetBaseObject())
798 break;
799
802 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_INC_EVENT_PHASE: Creature {} inc event phase by {}, "
803 "decrease by {}", GetBaseObject()->GetGUID(), e.action.incEventPhase.inc, e.action.incEventPhase.dec);
804 break;
805 }
807 {
808 if (!me)
809 break;
810
811 // Reset home position to respawn position if specified in the parameters
812 if (e.action.evade.toRespawnPosition == 0)
814
815 me->AI()->EnterEvadeMode();
816 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_EVADE: Creature {} EnterEvadeMode", me->GetGUID());
817 break;
818 }
820 {
821 if (!me)
822 break;
823
825
827 {
830 }
831 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_FLEE_FOR_ASSIST: Creature {} DoFleeToGetAssistance", me->GetGUID());
832 break;
833 }
835 {
836 if (!me)
837 break;
838
839 me->CombatStop(true);
840 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_COMBAT_STOP: {} CombatStop", me->GetGUID());
841 break;
842 }
844 {
845 for (WorldObject* target : targets)
846 {
847 Unit* unitTarget = target->ToUnit();
848 if (!unitTarget)
849 continue;
850
851 if (e.action.removeAura.spell)
852 {
853 ObjectGuid casterGUID;
855 {
856 if (!me)
857 break;
858 casterGUID = me->GetGUID();
859 }
860
862 {
863 if (Aura* aur = unitTarget->GetAura(e.action.removeAura.spell, casterGUID))
864 aur->ModCharges(-static_cast<int32>(e.action.removeAura.charges), AURA_REMOVE_BY_EXPIRE);
865 }
866 else
867 unitTarget->RemoveAurasDueToSpell(e.action.removeAura.spell, casterGUID);
868 }
869 else
870 unitTarget->RemoveAllAuras();
871
872 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_REMOVEAURASFROMSPELL: Unit {}, spell {}",
873 target->GetGUID(), e.action.removeAura.spell);
874 }
875 break;
876 }
878 {
879 if (!IsSmart())
880 break;
881
882 if (targets.empty())
883 {
884 ENSURE_AI(SmartAI, me->AI())->StopFollow(false);
885 break;
886 }
887
888 for (WorldObject* target : targets)
889 {
890 if (Unit* unitTarget = target->ToUnit())
891 {
892 float angle = e.action.follow.angle > 6 ? (e.action.follow.angle * M_PI / 180.0f) : e.action.follow.angle;
893 ENSURE_AI(SmartAI, me->AI())->SetFollow(unitTarget, float(e.action.follow.dist) + 0.1f, angle, e.action.follow.credit, e.action.follow.entry, e.action.follow.creditType);
894 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_FOLLOW: Creature {} following target {}",
895 me->GetGUID(), target->GetGUID());
896 break;
897 }
898 }
899 break;
900 }
902 {
903 if (!GetBaseObject())
904 break;
905
906 std::array<uint32, std::extent_v<decltype(e.action.randomPhase.phases)>> phases;
907 auto phasesEnd = std::ranges::remove_copy(e.action.randomPhase.phases, phases.begin(), 0u).out;
908
910 SetPhase(phase);
911 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_RANDOM_PHASE: Creature {} sets event phase to {}",
912 GetBaseObject()->GetGUID(), phase);
913 break;
914 }
916 {
917 if (!GetBaseObject())
918 break;
919
921 SetPhase(phase);
922 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_RANDOM_PHASE_RANGE: Creature {} sets event phase to {}",
923 GetBaseObject()->GetGUID(), phase);
924 break;
925 }
927 {
928 if (e.target.type == SMART_TARGET_NONE || e.target.type == SMART_TARGET_SELF) // Loot recipient and his group members
929 {
930 if (!me)
931 break;
932
933 for (ObjectGuid tapperGuid : me->GetTapList())
934 {
935 if (Player* tapper = ObjectAccessor::GetPlayer(*me, tapperGuid))
936 {
937 tapper->KilledMonsterCredit(e.action.killedMonster.creature, me->GetGUID());
938 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_KILLEDMONSTER: Player {}, Killcredit: {}",
939 tapper->GetGUID(), e.action.killedMonster.creature);
940 }
941 }
942 }
943 else // Specific target type
944 {
945 for (WorldObject* target : targets)
946 {
947 if (Player* playerTarget = target->ToPlayer())
948 {
949 playerTarget->KilledMonsterCredit(e.action.killedMonster.creature);
950 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_KILLEDMONSTER: Player {}, Killcredit: {}",
951 target->GetGUID(), e.action.killedMonster.creature);
952 }
953 else if (Unit* unitTarget = target->ToUnit()) // Special handling for vehicles
954 if (Vehicle* vehicle = unitTarget->GetVehicleKit())
955 for (std::pair<int8 const, VehicleSeat>& seat : vehicle->Seats)
956 if (Player* player = ObjectAccessor::GetPlayer(*target, seat.second.Passenger.Guid))
958 }
959 }
960 break;
961 }
963 {
964 WorldObject* obj = GetBaseObject();
965 if (!obj)
966 obj = unit;
967
968 if (!obj)
969 break;
970
971 InstanceScript* instance = obj->GetInstanceScript();
972 if (!instance)
973 {
974 TC_LOG_ERROR("sql.sql", "SmartScript: Event {} attempt to set instance data without instance script. EntryOrGuid {}", e.GetEventType(), e.entryOrGuid);
975 break;
976 }
977
978 switch (e.action.setInstanceData.type)
979 {
980 case 0:
982 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA: SetData Field: {}, data: {}",
984 break;
985 case 1:
987 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA: SetBossState BossId: {}, State: {} ({})",
989 break;
990 default: // Static analysis
991 break;
992 }
993 break;
994 }
996 {
997 WorldObject* obj = GetBaseObject();
998 if (!obj)
999 obj = unit;
1000
1001 if (!obj)
1002 break;
1003
1004 InstanceScript* instance = obj->GetInstanceScript();
1005 if (!instance)
1006 {
1007 TC_LOG_ERROR("sql.sql", "SmartScript: Event {} attempt to set instance data without instance script. EntryOrGuid {}", e.GetEventType(), e.entryOrGuid);
1008 break;
1009 }
1010
1011 if (targets.empty())
1012 break;
1013
1014 instance->SetGuidData(e.action.setInstanceData64.field, targets.front()->GetGUID());
1015 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA64: Field: {}, data: {}",
1016 e.action.setInstanceData64.field, targets.front()->GetGUID());
1017 break;
1018 }
1020 {
1021 for (WorldObject* target : targets)
1022 if (Creature* creatureTarget = target->ToCreature())
1023 creatureTarget->UpdateEntry(e.action.updateTemplate.creature, nullptr, e.action.updateTemplate.updateLevel != 0);
1024 break;
1025 }
1026 case SMART_ACTION_DIE:
1027 {
1028 if (me && !me->isDead())
1029 {
1030 me->KillSelf();
1031 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_DIE: Creature {}", me->GetGUID());
1032 }
1033 break;
1034 }
1036 {
1037 if (me && me->IsAIEnabled())
1038 {
1039 me->AI()->DoZoneInCombat();
1040 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_IN_COMBAT_WITH_ZONE: Creature {}", me->GetGUID());
1041 }
1042 break;
1043 }
1045 {
1046 if (me)
1047 {
1048 me->CallForHelp(float(e.action.callHelp.range));
1050 {
1052 sCreatureTextMgr->SendChatPacket(me, builder, CHAT_MSG_MONSTER_EMOTE);
1053 }
1054 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_FOR_HELP: Creature {}", me->GetGUID());
1055 }
1056 break;
1057 }
1059 {
1060 if (me)
1061 {
1063 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_SHEATH: Creature {}, State: {}",
1065 }
1066 break;
1067 }
1069 {
1070 // there should be at least a world update tick before despawn, to avoid breaking linked actions
1071 Milliseconds despawnDelay(e.action.forceDespawn.delay);
1072 if (despawnDelay <= 0ms)
1073 despawnDelay = 1ms;
1074
1075 Seconds forceRespawnTimer(e.action.forceDespawn.forceRespawnTimer);
1076
1077 for (WorldObject* target : targets)
1078 {
1079 if (Creature* creature = target->ToCreature())
1080 creature->DespawnOrUnsummon(despawnDelay, forceRespawnTimer);
1081 else if (GameObject* goTarget = target->ToGameObject())
1082 goTarget->DespawnOrUnsummon(despawnDelay, forceRespawnTimer);
1083 }
1084 break;
1085 }
1087 {
1088 for (WorldObject* target : targets)
1089 {
1090 if (e.action.ingamePhaseId.apply == 1)
1092 else
1094 }
1095 break;
1096 }
1098 {
1099 for (WorldObject* target : targets)
1100 {
1101 if (e.action.ingamePhaseGroup.apply == 1)
1103 else
1105 }
1106 break;
1107 }
1109 {
1110 for (WorldObject* target : targets)
1111 {
1112 Unit* unitTarget = target->ToUnit();
1113 if (!unitTarget)
1114 continue;
1115
1117 {
1118 if (e.action.morphOrMount.creature > 0)
1119 {
1120 if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature))
1122 }
1123 else
1124 unitTarget->Mount(e.action.morphOrMount.model);
1125 }
1126 else
1127 unitTarget->Dismount();
1128 }
1129 break;
1130 }
1132 {
1133 for (WorldObject* target : targets)
1134 {
1135 if (Creature* creatureTarget = target->ToCreature())
1136 {
1137 SmartAI* ai = CAST_AI(SmartAI, creatureTarget->AI());
1138 if (!ai)
1139 continue;
1140
1141 if (e.action.invincHP.percent)
1142 ai->SetInvincibilityHpLevel(creatureTarget->CountPctFromMaxHealth(e.action.invincHP.percent));
1143 else
1145 }
1146 }
1147 break;
1148 }
1150 {
1151 for (WorldObject* target : targets)
1152 {
1153 if (Creature* cTarget = target->ToCreature())
1154 {
1155 CreatureAI* ai = cTarget->AI();
1156 if (IsSmart(cTarget, true))
1158 else
1160 }
1161 else if (GameObject* oTarget = target->ToGameObject())
1162 {
1163 GameObjectAI* ai = oTarget->AI();
1164 if (IsSmart(oTarget, true))
1166 else
1168 }
1169 }
1170 break;
1171 }
1173 {
1174 for (WorldObject* target : targets)
1175 if (Unit* unitTarget = target->ToUnit())
1176 unitTarget->AttackStop();
1177 break;
1178 }
1180 {
1183 fadeObject.emplace(Milliseconds(e.action.moveOffset.FadeObjectDuration));
1184
1185 std::shared_ptr<MultiActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<MovementStopReason>>(e, targets.size());
1186
1187 for (WorldObject* target : targets)
1188 {
1189 Creature* creatureTarget = target->ToCreature();
1190 if (!creatureTarget)
1191 continue;
1192
1193 if (!(e.event.event_flags & SMART_EVENT_FLAG_WHILE_CHARMED) && creatureTarget->IsCharmed())
1194 continue;
1195
1196 Position pos = target->GetPosition();
1197
1198 // Use forward/backward/left/right cartesian plane movement
1199 float x, y, z, o;
1200 o = pos.GetOrientation();
1201 x = pos.GetPositionX() + (std::cos(o - (M_PI / 2))*e.target.x) + (std::cos(o)*e.target.y);
1202 y = pos.GetPositionY() + (std::sin(o - (M_PI / 2))*e.target.x) + (std::sin(o)*e.target.y);
1203 z = pos.GetPositionZ() + e.target.z;
1204
1206 if (waitEvent)
1207 scriptResult = Scripting::v2::ActionResult<MovementStopReason>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() });
1208
1209 creatureTarget->GetMotionMaster()->MovePoint(e.action.moveOffset.PointId, x, y, z,
1210 true, {}, {}, MovementWalkRunSpeedSelectionMode::Default, {}, {}, std::move(scriptResult));
1211 }
1212 if (waitEvent && !waitEvent->Results.empty())
1213 mTimedActionWaitEvent = std::move(waitEvent);
1214 break;
1215 }
1217 {
1218 for (WorldObject* target : targets)
1219 if (Unit* unitTarget = target->ToUnit())
1220 unitTarget->SetVisible(e.action.visibility.state ? true : false);
1221 break;
1222 }
1224 {
1225 for (WorldObject* target : targets)
1226 target->setActive(e.action.active.state ? true : false);
1227 break;
1228 }
1230 {
1231 if (!me)
1232 break;
1233
1234 std::erase_if(targets, [](WorldObject const* target)
1235 {
1236 return !target->IsUnit();
1237 });
1238
1239 if (targets.empty())
1240 break;
1241
1242 // attack random target
1243 if (Unit * target = Trinity::Containers::SelectRandomContainerElement(targets)->ToUnit())
1244 me->AI()->AttackStart(target);
1245 break;
1246 }
1248 {
1250 bool preferUnit = flags.HasFlag(SmartActionSummonCreatureFlags::PreferUnit);
1251 bool attackInvoker = flags.HasFlag(SmartActionSummonCreatureFlags::AttackInvoker);
1252
1253 WorldObject* summoner = preferUnit ? unit : GetBaseObjectOrUnitInvoker(unit);
1254 if (!summoner)
1255 break;
1256
1257 ObjectGuid privateObjectOwner;
1259 privateObjectOwner = summoner->IsPrivateObject() ? summoner->GetPrivateObjectOwner() : summoner->GetGUID();
1260 uint32 spawnsCount = std::max(e.action.summonCreature.count, 1u);
1261
1264
1265 SummonPropertiesEntry const* summonProperties = [&]() -> SummonPropertiesEntry const*
1266 {
1267 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.summonCreature.createdBySpell, DIFFICULTY_NONE))
1268 for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
1269 if (spellEffectInfo.IsEffect(SPELL_EFFECT_SUMMON))
1270 if (SummonPropertiesEntry const* summonProps = sSummonPropertiesStore.LookupEntry(spellEffectInfo.MiscValueB))
1271 return summonProps;
1272
1273 return nullptr;
1274 }();
1275
1276 auto DoSummon = [&](float x, float y, float z, float o, Unit* attackTarget)
1277 {
1278 for (uint32 counter = 0; counter < spawnsCount; counter++)
1279 {
1280 if (TempSummon* summon = summoner->GetMap()->SummonCreature(e.action.summonCreature.creature, { x, y, z, o }, summonProperties, Milliseconds(e.action.summonCreature.duration), summoner, e.action.summonCreature.createdBySpell, 0, privateObjectOwner))
1281 {
1282 summon->SetTempSummonType((TempSummonType)e.action.summonCreature.type);
1285 if (attackInvoker && attackTarget)
1286 summon->AI()->AttackStart(attackTarget);
1287 }
1288 }
1289 };
1290
1291 float x, y, z, o;
1292 for (WorldObject* target : targets)
1293 {
1294 target->GetPosition(x, y, z, o);
1295 x += e.target.x;
1296 y += e.target.y;
1297 z += e.target.z;
1298 o += e.target.o;
1299 DoSummon(x, y, z, o, target->ToUnit());
1300 }
1301
1303 break;
1304
1305 DoSummon(e.target.x, e.target.y, e.target.z, e.target.o, unit);
1306 break;
1307 }
1309 {
1310 WorldObject* summoner = GetBaseObjectOrUnitInvoker(unit);
1311 if (!summoner)
1312 break;
1313
1316
1317 for (WorldObject* target : targets)
1318 {
1319 Position pos = target->GetPositionWithOffset(Position(e.target.x, e.target.y, e.target.z, e.target.o));
1322 if (e.action.summonGO.storedTargetId && summon)
1324 }
1325
1327 break;
1328
1331 if (e.action.summonGO.storedTargetId && summon)
1333 break;
1334 }
1336 {
1337 for (WorldObject* target : targets)
1338 if (Unit* unitTarget = target->ToUnit())
1339 unitTarget->KillSelf();
1340 break;
1341 }
1343 {
1344 for (WorldObject* target : targets)
1345 if (Player* playerTarget = target->ToPlayer())
1346 playerTarget->AddItem(e.action.item.entry, e.action.item.count);
1347 break;
1348 }
1350 {
1351 for (WorldObject* target : targets)
1352 if (Player* playerTarget = target->ToPlayer())
1353 playerTarget->DestroyItemCount(e.action.item.entry, e.action.item.count, true);
1354 break;
1355 }
1357 {
1359 break;
1360 }
1362 {
1363 for (WorldObject* target : targets)
1364 {
1365 if (Player* playerTarget = target->ToPlayer())
1366 playerTarget->TeleportTo(e.action.teleport.mapID, e.target.x, e.target.y, e.target.z, e.target.o);
1367 else if (Creature* creatureTarget = target->ToCreature())
1368 creatureTarget->NearTeleportTo(e.target.x, e.target.y, e.target.z, e.target.o);
1369 }
1370 break;
1371 }
1373 {
1374 for (WorldObject* target : targets)
1375 if (Creature* creatureTarget = target->ToCreature())
1376 creatureTarget->SetFloating(e.action.setDisableGravity.disable != 0);
1377 break;
1378 }
1380 {
1381 if (!IsSmart())
1382 break;
1383
1384 ENSURE_AI(SmartAI, me->AI())->SetRun(e.action.setRun.run != 0);
1385 break;
1386 }
1388 {
1389 if (!targets.empty())
1390 {
1391 for (WorldObject* target : targets)
1392 {
1393 if (Creature* creatureTarget = target->ToCreature())
1394 {
1395 if (SmartAI* ai = CAST_AI(SmartAI, creatureTarget->AI()))
1396 ai->GetScript()->StoreCounter(e.action.setCounter.counterId, e.action.setCounter.value, e.action.setCounter.reset);
1397 else
1398 TC_LOG_ERROR("sql.sql", "SmartScript: Action target for SMART_ACTION_SET_COUNTER is not using SmartAI, skipping");
1399 }
1400 else if (GameObject* gameObject = target->ToGameObject())
1401 {
1402 if (SmartGameObjectAI* ai = CAST_AI(SmartGameObjectAI, gameObject->AI()))
1403 ai->GetScript()->StoreCounter(e.action.setCounter.counterId, e.action.setCounter.value, e.action.setCounter.reset);
1404 else
1405 TC_LOG_ERROR("sql.sql", "SmartScript: Action target for SMART_ACTION_SET_COUNTER is not using SmartGameObjectAI, skipping");
1406 }
1407 }
1408 }
1409 else
1411 break;
1412 }
1414 {
1415 if (!IsSmart())
1416 break;
1417
1418 uint32 entry = e.action.wpStart.pathID;
1419 bool repeat = e.action.wpStart.repeat != 0;
1420
1421 for (WorldObject* target : targets)
1422 {
1423 if (target->IsPlayer())
1424 {
1426 break;
1427 }
1428 }
1429
1430 std::shared_ptr<Scripting::v2::ActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<MovementStopReason>(e);
1432 if (waitEvent)
1434
1435 ENSURE_AI(SmartAI, me->AI())->StartPath(entry, repeat, unit, 0, e.action.wpStart.FadeObjectDuration, std::move(scriptResult));
1436 mTimedActionWaitEvent = std::move(waitEvent);
1437
1439 uint32 DespawnTime = e.action.wpStart.despawnTime;
1440 ENSURE_AI(SmartAI, me->AI())->SetEscortQuest(quest);
1441 ENSURE_AI(SmartAI, me->AI())->SetDespawnTime(DespawnTime);
1442 break;
1443 }
1445 {
1446 if (!IsSmart())
1447 break;
1448
1449 uint32 delay = e.action.wpPause.delay;
1450 ENSURE_AI(SmartAI, me->AI())->PausePath(delay, true);
1451 break;
1452 }
1454 {
1455 if (!IsSmart())
1456 break;
1457
1458 uint32 DespawnTime = e.action.wpStop.despawnTime;
1460 bool fail = e.action.wpStop.fail != 0;
1461 ENSURE_AI(SmartAI, me->AI())->StopPath(DespawnTime, quest, fail);
1462 break;
1463 }
1465 {
1466 if (!IsSmart())
1467 break;
1468
1469 // Set the timer to 1 ms so the path will be resumed on next update loop
1470 if (ENSURE_AI(SmartAI, me->AI())->CanResumePath())
1471 ENSURE_AI(SmartAI, me->AI())->SetWPPauseTimer(1);
1472 break;
1473 }
1475 {
1476 if (!me)
1477 break;
1478
1480 me->SetFacingTo((me->GetTransport() ? me->GetTransportHomePosition() : me->GetHomePosition()).GetOrientation());
1481 else if (e.GetTargetType() == SMART_TARGET_POSITION)
1482 me->SetFacingTo(e.target.o);
1483 else if (!targets.empty())
1484 me->SetFacingToObject(targets.front());
1485 break;
1486 }
1488 {
1489 for (WorldObject* target : targets)
1490 if (Player* playerTarget = target->ToPlayer())
1491 playerTarget->SendMovieStart(e.action.movie.entry);
1492 break;
1493 }
1495 {
1496 if (!IsSmart())
1497 break;
1498
1499 WorldObject* target = nullptr;
1500
1501 // we want to move to random element
1502 if (!targets.empty())
1504
1507 fadeObject.emplace(Milliseconds(e.action.moveToPos.FadeObjectDuration));
1508
1509 std::shared_ptr<Scripting::v2::ActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<MovementStopReason>(e);
1511 if (waitEvent)
1513
1514 if (target)
1515 {
1516 float x, y, z;
1517 target->GetPosition(x, y, z);
1519 target->GetContactPoint(me, x, y, z, e.action.moveToPos.ContactDistance);
1521 {}, {}, MovementWalkRunSpeedSelectionMode::Default, {}, fadeObject, std::move(scriptResult));
1522 mTimedActionWaitEvent = std::move(waitEvent);
1523 }
1524
1526 break;
1527
1528 Position dest(e.target.x, e.target.y, e.target.z);
1530 if (TransportBase* trans = me->GetDirectTransport())
1531 dest = trans->GetPositionWithOffset(dest);
1532
1534 MovementWalkRunSpeedSelectionMode::Default, {}, fadeObject, std::move(scriptResult));
1535 mTimedActionWaitEvent = std::move(waitEvent);
1536 break;
1537 }
1539 {
1540 for (WorldObject* target : targets)
1541 {
1542 if (target->IsCreature())
1543 TC_LOG_WARN("sql.sql", "Invalid creature target '{}' (entry {}, spawnId {}) specified for SMART_ACTION_ENABLE_TEMP_GOBJ", target->GetName(), target->GetEntry(), target->ToCreature()->GetSpawnId());
1544 else if (GameObject* gameObject = target->ToGameObject())
1545 {
1546 if (gameObject->isSpawnedByDefault())
1547 TC_LOG_WARN("sql.sql", "Invalid gameobject target '{}' (entry {}, spawnId {}) for SMART_ACTION_ENABLE_TEMP_GOBJ - the object is spawned by default", target->GetName(), target->GetEntry(), gameObject->GetSpawnId());
1548 else
1549 gameObject->SetRespawnTime(e.action.enableTempGO.duration);
1550 }
1551 }
1552 break;
1553 }
1555 {
1556 for (WorldObject* target : targets)
1557 if (Player* playerTarget = target->ToPlayer())
1558 playerTarget->PlayerTalkClass->SendCloseGossip();
1559 break;
1560 }
1561 case SMART_ACTION_EQUIP:
1562 {
1563 for (WorldObject* target : targets)
1564 {
1565 if (Creature* npc = target->ToCreature())
1566 {
1567 std::array<EquipmentItem, MAX_EQUIPMENT_ITEMS> slot;
1568 if (int8 equipId = static_cast<int8>(e.action.equip.entry))
1569 {
1570 EquipmentInfo const* eInfo = sObjectMgr->GetEquipmentInfo(npc->GetEntry(), equipId);
1571 if (!eInfo)
1572 {
1573 TC_LOG_ERROR("sql.sql", "SmartScript: SMART_ACTION_EQUIP uses non-existent equipment info id {} for creature {}", equipId, npc->GetEntry());
1574 break;
1575 }
1576
1577 npc->SetCurrentEquipmentId(equipId);
1578
1579 std::copy(std::begin(eInfo->Items), std::end(eInfo->Items), std::begin(slot));
1580 }
1581 else
1582 {
1583 slot[0].ItemId = e.action.equip.slot1;
1584 slot[1].ItemId = e.action.equip.slot2;
1585 slot[2].ItemId = e.action.equip.slot3;
1586 }
1587
1588 for (uint32 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
1589 if (!e.action.equip.mask || (e.action.equip.mask & (1 << i)))
1590 npc->SetVirtualItem(i, slot[i].ItemId, slot[i].AppearanceModId, slot[i].ItemVisual);
1591 }
1592 }
1593 break;
1594 }
1596 {
1597 SmartScriptHolder& ev = mStoredEvents.emplace_back();
1598 ev.event_id = e.action.timeEvent.id;
1601 if (!ev.event.event_chance) ev.event.event_chance = 100;
1602
1607
1608 ev.event.event_flags = 0;
1611
1614
1615 ev.target = e.target;
1616
1617 InitTimer(ev);
1618 break;
1619 }
1621 {
1623
1624 // remove this event if not repeatable
1626 mRemIDs.push_back(e.action.timeEvent.id);
1627 break;
1628 }
1630 {
1631 mRemIDs.push_back(e.action.timeEvent.id);
1632 break;
1633 }
1635 {
1636 SetPhase(0);
1637 OnReset();
1638 break;
1639 }
1641 {
1642 if (!IsSmart())
1643 break;
1644
1645 float attackDistance = float(e.action.setRangedMovement.distance);
1646 float attackAngle = float(e.action.setRangedMovement.angle) / 180.0f * float(M_PI);
1647
1648 for (WorldObject* target : targets)
1649 {
1650 if (Creature* creature = target->ToCreature())
1651 if (IsSmart(creature) && creature->GetVictim())
1652 if (ENSURE_AI(SmartAI, creature->AI())->CanCombatMove())
1653 creature->StartDefaultCombatMovement(creature->GetVictim(), attackDistance, attackAngle);
1654 }
1655
1656 break;
1657 }
1659 {
1661 {
1662 TC_LOG_ERROR("sql.sql", "SmartScript: Entry {} SourceType {} Event {} Action {} is using TARGET_NONE(0) for Script9 target. Please correct target_type in database.", e.entryOrGuid, e.GetScriptType(), e.GetEventType(), e.GetActionType());
1663 break;
1664 }
1665
1666 for (WorldObject* target : targets)
1667 {
1668 if (Creature* creature = target->ToCreature())
1669 {
1670 if (IsSmart(creature))
1672 }
1673 else if (GameObject* goTarget = target->ToGameObject())
1674 {
1675 if (IsSmart(goTarget))
1677 }
1678 else if (AreaTrigger* areaTriggerTarget = target->ToAreaTrigger())
1679 if (SmartAreaTriggerAI* atSAI = CAST_AI(SmartAreaTriggerAI, areaTriggerTarget->AI()))
1680 atSAI->SetTimedActionList(e, e.action.timedActionList.id, GetLastInvoker());
1681 }
1682 break;
1683 }
1685 {
1686 for (WorldObject* target : targets)
1687 if (Creature* creatureTarget = target->ToCreature())
1688 creatureTarget->ReplaceAllNpcFlags(NPCFlags(e.action.flag.flag));
1689 break;
1690 }
1692 {
1693 for (WorldObject* target : targets)
1694 if (Creature* creatureTarget = target->ToCreature())
1695 creatureTarget->SetNpcFlag(NPCFlags(e.action.flag.flag));
1696 break;
1697 }
1699 {
1700 for (WorldObject* target : targets)
1701 if (Creature* creatureTarget = target->ToCreature())
1702 creatureTarget->RemoveNpcFlag(NPCFlags(e.action.flag.flag));
1703 break;
1704 }
1706 {
1707 if (targets.empty())
1708 break;
1709
1710 ObjectVector casters;
1712
1713 std::shared_ptr<MultiActionResult<SpellCastResult>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<SpellCastResult>>(e, casters.size()* targets.size());
1714
1715 CastSpellExtraArgs args;
1716 if (e.action.crossCast.castFlags & SMARTCAST_TRIGGERED)
1718
1719 for (WorldObject* caster : casters)
1720 {
1721 Unit* casterUnit = caster->ToUnit();
1722 if (!casterUnit)
1723 continue;
1724
1725 bool interruptedSpell = false;
1726
1727 for (WorldObject* target : targets)
1728 {
1729 if (e.action.crossCast.castFlags & SMARTCAST_AURA_NOT_PRESENT && (!target->IsUnit() || target->ToUnit()->HasAura(e.action.crossCast.spell)))
1730 {
1731 TC_LOG_DEBUG("scripts.ai", "Spell {} not cast because it has flag SMARTCAST_AURA_NOT_PRESENT and the target ({}) already has the aura", e.action.crossCast.spell, target->GetGUID());
1732 continue;
1733 }
1734
1735 if (!interruptedSpell && e.action.crossCast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS)
1736 {
1737 casterUnit->InterruptNonMeleeSpells(false);
1738 interruptedSpell = true;
1739 }
1740
1741 if (waitEvent)
1742 {
1743 args.SetScriptResult(Scripting::v2::ActionResult<SpellCastResult>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() }));
1744 args.SetScriptWaitsForSpellHit((e.action.crossCast.castFlags & SMARTCAST_WAIT_FOR_HIT) != 0);
1745 }
1746
1747 casterUnit->CastSpell(target, e.action.crossCast.spell, args);
1748 }
1749 }
1750 if (waitEvent && !waitEvent->Results.empty())
1751 mTimedActionWaitEvent = std::move(waitEvent);
1752 break;
1753 }
1755 {
1756 std::array<uint32, std::extent_v<decltype(e.action.randTimedActionList.actionLists)>> actionLists;
1757 auto actionListsEnd = std::ranges::remove_copy(e.action.randTimedActionList.actionLists, actionLists.begin(), 0u).out;
1758
1759 uint32 id = Trinity::Containers::SelectRandomContainerElement(Trinity::IteratorPair(actionLists.begin(), actionListsEnd));
1761 {
1762 TC_LOG_ERROR("sql.sql", "SmartScript: Entry {} SourceType {} Event {} Action {} is using TARGET_NONE(0) for Script9 target. Please correct target_type in database.", e.entryOrGuid, e.GetScriptType(), e.GetEventType(), e.GetActionType());
1763 break;
1764 }
1765
1766 for (WorldObject* target : targets)
1767 {
1768 if (Creature* creature = target->ToCreature())
1769 {
1770 if (IsSmart(creature))
1771 ENSURE_AI(SmartAI, creature->AI())->SetTimedActionList(e, id, GetLastInvoker());
1772 }
1773 else if (GameObject* goTarget = target->ToGameObject())
1774 {
1775 if (IsSmart(goTarget))
1777 }
1778 else if (AreaTrigger* areaTriggerTarget = target->ToAreaTrigger())
1779 if (SmartAreaTriggerAI* atSAI = CAST_AI(SmartAreaTriggerAI, areaTriggerTarget->AI()))
1780 atSAI->SetTimedActionList(e, id, GetLastInvoker());
1781 }
1782 break;
1783 }
1785 {
1788 {
1789 TC_LOG_ERROR("sql.sql", "SmartScript: Entry {} SourceType {} Event {} Action {} is using TARGET_NONE(0) for Script9 target. Please correct target_type in database.", e.entryOrGuid, e.GetScriptType(), e.GetEventType(), e.GetActionType());
1790 break;
1791 }
1792
1793 for (WorldObject* target : targets)
1794 {
1795 if (Creature* creature = target->ToCreature())
1796 {
1797 if (IsSmart(creature))
1798 ENSURE_AI(SmartAI, creature->AI())->SetTimedActionList(e, id, GetLastInvoker());
1799 }
1800 else if (GameObject* goTarget = target->ToGameObject())
1801 {
1802 if (IsSmart(goTarget))
1804 }
1805 else if (AreaTrigger* areaTriggerTarget = target->ToAreaTrigger())
1806 if (SmartAreaTriggerAI* atSAI = CAST_AI(SmartAreaTriggerAI, areaTriggerTarget->AI()))
1807 atSAI->SetTimedActionList(e, id, GetLastInvoker());
1808 }
1809 break;
1810 }
1812 {
1813 std::shared_ptr<MultiActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<MovementStopReason>>(e, targets.size());
1814
1815 for (WorldObject* target : targets)
1816 {
1817 if (Player* playerTarget = target->ToPlayer())
1818 {
1820 if (waitEvent)
1821 scriptResult = Scripting::v2::ActionResult<MovementStopReason>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() });
1822
1823 if (!playerTarget->ActivateTaxiPathTo(e.action.taxi.id, 0, {}, scriptResult))
1824 if (scriptResult)
1826 }
1827 }
1828 if (waitEvent && !waitEvent->Results.empty())
1829 mTimedActionWaitEvent = std::move(waitEvent);
1830 break;
1831 }
1833 {
1834 bool foundTarget = false;
1835
1836 for (WorldObject* target : targets)
1837 {
1838 if (Creature* creatureTarget = target->ToCreature())
1839 {
1840 foundTarget = true;
1841
1843 creatureTarget->GetMotionMaster()->MoveRandom(float(e.action.moveRandom.distance));
1844 else
1845 creatureTarget->GetMotionMaster()->MoveIdle();
1846 }
1847 }
1848
1849 if (!foundTarget && me && me->IsCreature())
1850 {
1853 else
1855 }
1856 break;
1857 }
1859 {
1860 for (WorldObject* target : targets)
1861 if (Unit* unitTarget = target->ToUnit())
1862 {
1863 switch (e.action.setunitByte.type)
1864 {
1865 case 0:
1866 unitTarget->SetStandState(UnitStandStateType(e.action.setunitByte.byte1));
1867 break;
1868 case 1:
1869 // pet talent points
1870 break;
1871 case 2:
1872 unitTarget->SetVisFlag(UnitVisFlags(e.action.setunitByte.byte1));
1873 break;
1874 case 3:
1875 unitTarget->SetAnimTier(AnimTier(e.action.setunitByte.byte1));
1876 break;
1877 }
1878 }
1879 break;
1880 }
1882 {
1883 for (WorldObject* target : targets)
1884 if (Unit* unitTarget = target->ToUnit())
1885 {
1886 switch (e.action.setunitByte.type)
1887 {
1888 case 0:
1889 unitTarget->SetStandState(UNIT_STAND_STATE_STAND);
1890 break;
1891 case 1:
1892 // pet talent points
1893 break;
1894 case 2:
1895 unitTarget->RemoveVisFlag(UnitVisFlags(e.action.setunitByte.byte1));
1896 break;
1897 case 3:
1898 unitTarget->SetAnimTier(AnimTier::Ground);
1899 break;
1900 }
1901 }
1902 break;
1903 }
1905 {
1906 for (WorldObject* target : targets)
1907 if (Unit* unitTarget = target->ToUnit())
1909 break;
1910 }
1912 {
1913 WorldObject* target = nullptr;
1914
1915 if (!targets.empty())
1917
1918 Position pos(e.target.x, e.target.y, e.target.z);
1919 if (target)
1920 {
1921 float x, y, z;
1922 target->GetPosition(x, y, z);
1923 if (e.action.jump.ContactDistance > 0)
1924 target->GetContactPoint(me, x, y, z, e.action.jump.ContactDistance);
1925 pos = Position(x + e.target.x, y + e.target.y, z + e.target.z);
1926 }
1927 else if (e.GetTargetType() != SMART_TARGET_POSITION)
1928 break;
1929
1930 std::shared_ptr<Scripting::v2::ActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<MovementStopReason>(e);
1932 if (waitEvent)
1934
1938 {}, false, false, {}, nullptr, nullptr, std::move(actionResultSetter));
1939
1940 mTimedActionWaitEvent = std::move(waitEvent);
1941 break;
1942 }
1944 {
1945 for (WorldObject* target : targets)
1946 if (GameObject* gameObject = target->ToGameObject())
1947 gameObject->SetLootState((LootState)e.action.setGoLootState.state);
1948 break;
1949 }
1951 {
1952 for (WorldObject* target : targets)
1953 if (GameObject* gameObject = target->ToGameObject())
1954 gameObject->SetGoState((GOState)e.action.goState.state);
1955 break;
1956 }
1958 {
1959 WorldObject* ref = GetBaseObject();
1960 if (!ref)
1961 ref = unit;
1962
1963 if (!ref)
1964 break;
1965
1966 ObjectVector const* storedTargets = GetStoredTargetVector(e.action.sendTargetToTarget.id, *ref);
1967 if (!storedTargets)
1968 break;
1969
1970 for (WorldObject* target : targets)
1971 {
1972 if (Creature* creatureTarget = target->ToCreature())
1973 {
1974 if (SmartAI* ai = CAST_AI(SmartAI, creatureTarget->AI()))
1975 ai->GetScript()->StoreTargetList(ObjectVector(*storedTargets), e.action.sendTargetToTarget.id); // store a copy of target list
1976 else
1977 TC_LOG_ERROR("sql.sql", "SmartScript: Action target for SMART_ACTION_SEND_TARGET_TO_TARGET is not using SmartAI, skipping");
1978 }
1979 else if (GameObject* gameObject = target->ToGameObject())
1980 {
1981 if (SmartGameObjectAI* ai = CAST_AI(SmartGameObjectAI, gameObject->AI()))
1982 ai->GetScript()->StoreTargetList(ObjectVector(*storedTargets), e.action.sendTargetToTarget.id); // store a copy of target list
1983 else
1984 TC_LOG_ERROR("sql.sql", "SmartScript: Action target for SMART_ACTION_SEND_TARGET_TO_TARGET is not using SmartGameObjectAI, skipping");
1985 }
1986 }
1987 break;
1988 }
1990 {
1991 if (!GetBaseObject() || !IsSmart())
1992 break;
1993
1994 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SEND_GOSSIP_MENU: gossipMenuId {}, gossipNpcTextId {}",
1996
1997 // override default gossip
1998 if (me)
1999 ENSURE_AI(SmartAI, me->AI())->SetGossipReturn(true);
2000 else if (go)
2001 ENSURE_AI(SmartGameObjectAI, go->AI())->SetGossipReturn(true);
2002
2003 for (WorldObject* target : targets)
2004 {
2005 if (Player* player = target->ToPlayer())
2006 {
2009 else
2010 player->PlayerTalkClass->ClearMenus();
2011
2012 uint32 gossipNpcTextId = e.action.sendGossipMenu.gossipNpcTextId;
2013 if (!gossipNpcTextId)
2015
2016 player->PlayerTalkClass->SendGossipMenu(gossipNpcTextId, GetBaseObject()->GetGUID());
2017 }
2018 }
2019 break;
2020 }
2022 {
2023 for (WorldObject* target : targets)
2024 {
2025 if (Creature* creatureTarget = target->ToCreature())
2026 {
2028 creatureTarget->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
2029 else if (e.GetTargetType() == SMART_TARGET_POSITION)
2030 creatureTarget->SetHomePosition(e.target.x, e.target.y, e.target.z, e.target.o);
2038 {
2039 creatureTarget->SetHomePosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation());
2040 }
2041 else
2042 TC_LOG_ERROR("sql.sql", "SmartScript: Action target for SMART_ACTION_SET_HOME_POS is invalid, skipping");
2043 }
2044 }
2045 break;
2046 }
2048 {
2049 for (WorldObject* target : targets)
2050 if (Creature* creatureTarget = target->ToCreature())
2051 creatureTarget->SetRegenerateHealth(e.action.setHealthRegen.regenHealth != 0);
2052 break;
2053 }
2055 {
2056 for (WorldObject* target : targets)
2057 if (Creature* creatureTarget = target->ToCreature())
2058 creatureTarget->SetSessile(e.action.setRoot.root != 0);
2059 break;
2060 }
2062 {
2063 std::list<TempSummon*> summonList;
2065
2066 for (TempSummon* summon : summonList)
2067 if (unit && e.action.creatureGroup.attackInvoker)
2068 summon->AI()->AttackStart(unit);
2069
2071 StoreTargetList({ summonList.begin(), summonList.end() }, e.action.creatureGroup.storedTargetId);
2072 break;
2073 }
2075 {
2076 for (WorldObject* target : targets)
2077 if (Unit* unitTarget = target->ToUnit())
2078 unitTarget->SetPower(Powers(e.action.power.powerType), e.action.power.newPower);
2079 break;
2080 }
2082 {
2083 for (WorldObject* target : targets)
2084 if (Unit* unitTarget = target->ToUnit())
2085 unitTarget->ModifyPower(Powers(e.action.power.powerType), e.action.power.newPower);
2086 break;
2087 }
2089 {
2090 for (WorldObject* target : targets)
2091 if (Unit* unitTarget = target->ToUnit())
2092 unitTarget->ModifyPower(Powers(e.action.power.powerType), -int32(e.action.power.newPower));
2093 break;
2094 }
2096 {
2097 uint32 eventId = e.action.gameEventStop.id;
2098 if (!sGameEventMgr->IsActiveEvent(eventId))
2099 {
2100 TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: At case SMART_ACTION_GAME_EVENT_STOP, inactive event (id: {})", eventId);
2101 break;
2102 }
2103 sGameEventMgr->StopEvent(eventId, true);
2104 break;
2105 }
2107 {
2108 uint32 eventId = e.action.gameEventStart.id;
2109 if (sGameEventMgr->IsActiveEvent(eventId))
2110 {
2111 TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: At case SMART_ACTION_GAME_EVENT_START, already activated event (id: {})", eventId);
2112 break;
2113 }
2114 sGameEventMgr->StartEvent(eventId, true);
2115 break;
2116 }
2118 {
2119 float distanceToClosest = std::numeric_limits<float>::max();
2120 std::pair<uint32, uint32> closest = { 0, 0 };
2121
2122 std::shared_ptr<MultiActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<MovementStopReason>>(e, targets.size());
2123
2124 for (WorldObject* target : targets)
2125 {
2126 if (Creature* creature = target->ToCreature())
2127 {
2128 if (IsSmart(creature))
2129 {
2130 for (uint32 pathId : e.action.closestWaypointFromList.wps)
2131 {
2132 WaypointPath const* path = sWaypointMgr->GetPath(pathId);
2133 if (!path || path->Nodes.empty())
2134 continue;
2135
2136 for (WaypointNode const& waypoint : path->Nodes)
2137 {
2138 float distanceToThisNode = creature->GetDistance(waypoint.X, waypoint.Y, waypoint.Z);
2139 if (distanceToThisNode < distanceToClosest)
2140 {
2141 distanceToClosest = distanceToThisNode;
2142 closest.first = pathId;
2143 closest.second = waypoint.Id;
2144 }
2145 }
2146 }
2147
2148 if (closest.first != 0)
2149 {
2151 if (waitEvent)
2152 actionResultSetter = Scripting::v2::ActionResult<MovementStopReason>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() });
2153
2154 ENSURE_AI(SmartAI, creature->AI())->StartPath(closest.first, true, nullptr, closest.second, 0, std::move(actionResultSetter));
2155 }
2156 }
2157 }
2158 }
2159
2160 if (waitEvent && !waitEvent->Results.empty())
2161 mTimedActionWaitEvent = std::move(waitEvent);
2162 break;
2163 }
2165 {
2166 std::array<uint32, std::extent_v<decltype(e.action.randomSound.sounds)>> sounds;
2167 auto soundsEnd = std::ranges::remove_copy(e.action.randomSound.sounds, sounds.begin(), 0u).out;
2168
2169 bool onlySelf = e.action.randomSound.onlySelf != 0;
2170 for (WorldObject* const target : targets)
2171 {
2173
2174 if (e.action.randomSound.distance == 1)
2175 target->PlayDistanceSound(sound, onlySelf ? target->ToPlayer() : nullptr);
2176 else
2177 target->PlayDirectSound(sound, onlySelf ? target->ToPlayer() : nullptr);
2178
2179 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_RANDOM_SOUND: target: {} ({}), sound: {}, onlyself: {}",
2180 target->GetName(), target->GetGUID(), sound, onlySelf ? "true" : "false");
2181 }
2182 break;
2183 }
2185 {
2186 for (WorldObject* const target : targets)
2187 if (Creature* creatureTarget = target->ToCreature())
2188 creatureTarget->SetCorpseDelay(e.action.corpseDelay.timer, !e.action.corpseDelay.includeDecayRatio);
2189 break;
2190 }
2192 {
2193 if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0)
2194 {
2197
2198 // Instant spawn
2199 GetBaseObject()->GetMap()->SpawnGroupSpawn(e.action.groupSpawn.groupId, ignoreRespawn, force);
2200 }
2201 else
2202 {
2203 // Delayed spawn (use values from parameter to schedule event to call us back
2204 SmartScriptHolder& ev = mStoredEvents.emplace_back();
2205 ev.event_id = e.event_id;
2207 ev.event.event_chance = 100;
2208
2213
2214 ev.event.event_flags = 0;
2216
2219 ev.action.groupSpawn.minDelay = 0;
2220 ev.action.groupSpawn.maxDelay = 0;
2223
2224 ev.target = e.target;
2225
2226 InitTimer(ev);
2227 }
2228 break;
2229 }
2231 {
2232 if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0)
2233 {
2234 bool const deleteRespawnTimes = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN) != 0);
2235
2236 // Instant spawn
2237 GetBaseObject()->GetMap()->SpawnGroupDespawn(e.action.groupSpawn.groupId, deleteRespawnTimes);
2238 }
2239 else
2240 {
2241 // Delayed spawn (use values from parameter to schedule event to call us back
2242 SmartScriptHolder& ev = mStoredEvents.emplace_back();
2243 ev.event_id = e.event_id;
2245 ev.event.event_chance = 100;
2246
2251
2252 ev.event.event_flags = 0;
2254
2257 ev.action.groupSpawn.minDelay = 0;
2258 ev.action.groupSpawn.maxDelay = 0;
2261
2262 ev.target = e.target;
2263
2264 InitTimer(ev);
2265 }
2266 break;
2267 }
2269 {
2270 if (!IsSmart())
2271 break;
2272
2273 ENSURE_AI(SmartAI, me->AI())->SetEvadeDisabled(e.action.disableEvade.disable != 0);
2274 break;
2275 }
2277 {
2278 if (!me->CanHaveThreatList())
2279 break;
2280 for (WorldObject* const target : targets)
2281 if (Unit* unitTarget = target->ToUnit())
2282 me->GetThreatManager().AddThreat(unitTarget, float(e.action.threat.threatINC) - float(e.action.threat.threatDEC), nullptr, true, true);
2283 break;
2284 }
2286 {
2287 for (WorldObject* const target : targets)
2288 if (Creature* creatureTarget = target->ToCreature())
2289 creatureTarget->LoadEquipment(e.action.loadEquipment.id, e.action.loadEquipment.force != 0);
2290 break;
2291 }
2293 {
2296 break;
2297 }
2299 {
2300 for (WorldObject* const target : targets)
2301 if (Unit* unitTarget = target->ToUnit())
2303 break;
2304 }
2306 {
2307 Map* map = nullptr;
2308 if (WorldObject* obj = GetBaseObject())
2309 map = obj->GetMap();
2310 else if (!targets.empty())
2311 map = targets.front()->GetMap();
2312
2313 if (map)
2315 else
2316 TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {} - tries to respawn by spawnId but does not provide a map", e.entryOrGuid, e.GetScriptType(), e.event_id);
2317 break;
2318 }
2320 {
2321 for (WorldObject* const target : targets)
2322 {
2323 if (Creature* creatureTarget = target->ToCreature())
2324 {
2325 if (e.action.animKit.type == 0)
2326 creatureTarget->PlayOneShotAnimKitId(e.action.animKit.animKit);
2327 else if (e.action.animKit.type == 1)
2328 creatureTarget->SetAIAnimKitId(e.action.animKit.animKit);
2329 else if (e.action.animKit.type == 2)
2330 creatureTarget->SetMeleeAnimKitId(e.action.animKit.animKit);
2331 else if (e.action.animKit.type == 3)
2332 creatureTarget->SetMovementAnimKitId(e.action.animKit.animKit);
2333
2334 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_PLAY_ANIMKIT: target: {} ({}), AnimKit: {}, Type: {}",
2335 target->GetName(), target->GetGUID(), e.action.animKit.animKit, e.action.animKit.type);
2336 }
2337 else if (GameObject* gameObject = target->ToGameObject())
2338 {
2339 switch (e.action.animKit.type)
2340 {
2341 case 0:
2342 gameObject->SetAnimKitId(e.action.animKit.animKit, true);
2343 break;
2344 case 1:
2345 gameObject->SetAnimKitId(e.action.animKit.animKit, false);
2346 break;
2347 default:
2348 break;
2349 }
2350
2351 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_PLAY_ANIMKIT: target: {} ({}), AnimKit: {}, Type: {}",
2352 target->GetName(), target->GetGUID(), e.action.animKit.animKit, e.action.animKit.type);
2353 }
2354 }
2355
2356 break;
2357 }
2359 {
2360 for (WorldObject* const target : targets)
2361 if (Player* playerTarget = target->ToPlayer())
2362 playerTarget->GetSceneMgr().PlayScene(e.action.scene.sceneId);
2363
2364 break;
2365 }
2367 {
2368 for (WorldObject* const target : targets)
2369 if (Player* playerTarget = target->ToPlayer())
2370 playerTarget->GetSceneMgr().CancelSceneBySceneId(e.action.scene.sceneId);
2371
2372 break;
2373 }
2375 {
2376 for (WorldObject* target : targets)
2377 if (Player* playerTarget = target->ToPlayer())
2378 playerTarget->SendCinematicStart(e.action.cinematic.entry);
2379 break;
2380 }
2382 {
2383 uint32 speedInteger = e.action.movementSpeed.speedInteger;
2384 uint32 speedFraction = e.action.movementSpeed.speedFraction;
2385 float speed = float(speedInteger) + float(speedFraction) / std::pow(10, std::floor(std::log10(float(speedFraction ? speedFraction : 1)) + 1));
2386
2387 for (WorldObject* const target : targets)
2388 if (Creature* creatureTarget = target->ToCreature())
2389 creatureTarget->SetSpeed(UnitMoveType(e.action.movementSpeed.movementType), speed);
2390
2391 break;
2392 }
2394 {
2395 for (WorldObject* const target : targets)
2396 {
2397 if (Unit* unitTarget = target->ToUnit())
2398 {
2399 unitTarget->SendPlaySpellVisualKit(e.action.spellVisualKit.spellVisualKitId, e.action.spellVisualKit.kitType,
2401
2402 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_PLAY_SPELL_VISUAL_KIT: target: {} ({}), SpellVisualKit: {}",
2403 target->GetName(), target->GetGUID(), e.action.spellVisualKit.spellVisualKitId);
2404 }
2405 }
2406
2407 break;
2408 }
2410 {
2411 if (WorldObject* obj = GetBaseObject())
2412 {
2414 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_OVERRIDE_LIGHT: {} sets zone override light (zoneId: {}, areaLightId: {}, overrideLightId: {}, transitionMilliseconds: {})",
2416 }
2417 break;
2418 }
2420 {
2421 if (WorldObject* obj = GetBaseObject())
2422 {
2424 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_OVERRIDE_WEATHER: {} sets zone weather (zoneId: {}, weatherId: {}, intensity: {})",
2426 }
2427 break;
2428 }
2430 {
2431 for (WorldObject* target : targets)
2432 if (Unit* unitTarget = target->ToUnit())
2433 unitTarget->SetHover(e.action.setHover.enable != 0);
2434 break;
2435 }
2437 {
2438 for (WorldObject* target : targets)
2439 if (Unit* targetUnit = target->ToUnit())
2440 targetUnit->SetHealth(targetUnit->CountPctFromMaxHealth(e.action.setHealthPct.percent));
2441 break;
2442 }
2444 {
2445 WorldObject* baseObject = GetBaseObject();
2446
2447 for (WorldObject* const target : targets)
2448 {
2449 if (Player* playerTarget = target->ToPlayer())
2450 {
2451 Conversation* conversation = Conversation::CreateConversation(e.action.conversation.id, playerTarget,
2452 *playerTarget, playerTarget->GetGUID(), nullptr);
2453 if (!conversation)
2454 TC_LOG_WARN("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_CREATE_CONVERSATION: id {}, baseObject {}, target {} - failed to create conversation",
2455 e.action.conversation.id, !baseObject ? "" : baseObject->GetName().c_str(), playerTarget->GetName());
2456 }
2457 }
2458
2459 break;
2460 }
2462 {
2463 for (WorldObject* target : targets)
2464 {
2465 if (Unit* unitTarget = target->ToUnit())
2466 {
2468 unitTarget->SetUnitFlag(UNIT_FLAG_IMMUNE_TO_PC);
2469 else
2470 unitTarget->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC);
2471 }
2472 }
2473 break;
2474 }
2476 {
2477 for (WorldObject* target : targets)
2478 {
2479 if (Unit* unitTarget = target->ToUnit())
2480 {
2482 unitTarget->SetUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC);
2483 else
2484 unitTarget->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC);
2485 }
2486 }
2487 break;
2488 }
2490 {
2491 for (WorldObject* target : targets)
2492 if (Unit* unitTarget = target->ToUnit())
2493 unitTarget->SetUninteractible(e.action.setUninteractible.uninteractible != 0);
2494 break;
2495 }
2497 {
2498 for (WorldObject* target : targets)
2499 {
2500 if (GameObject* targetGo = target->ToGameObject())
2501 {
2503 }
2504 }
2505 break;
2506 }
2508 {
2509 if (!targets.empty())
2511 else
2512 {
2513 WorldObject* baseObject = GetBaseObject();
2514 TC_LOG_WARN("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_ADD_TO_STORED_TARGET_LIST: var {}, baseObject {}, event {} - tried to add no targets to stored target list",
2515 e.action.addToStoredTargets.id, !baseObject ? "" : baseObject->GetName().c_str(), e.event_id);
2516 }
2517 break;
2518 }
2520 {
2521 WorldObject* baseObject = GetBaseObject();
2522
2523 auto doCreatePersonalClone = [&](Position const& position, Player* privateObjectOwner)
2524 {
2526 if (IsSmart(summon))
2527 ENSURE_AI(SmartAI, summon->AI())->SetTimedActionList(e, e.entryOrGuid, privateObjectOwner, e.event_id + 1);
2528 };
2529
2530 // if target is position then targets container was empty
2532 {
2533 for (WorldObject* target : targets)
2534 if (Player* playerTarget = Object::ToPlayer(target))
2535 doCreatePersonalClone(baseObject->GetPosition(), playerTarget);
2536 }
2537 else
2538 {
2539 if (Player* invoker = Object::ToPlayer(GetLastInvoker()))
2540 doCreatePersonalClone({ e.target.x, e.target.y, e.target.z, e.target.o }, invoker);
2541 }
2542
2543 // action list will continue on personal clones
2544 std::erase_if(mTimedActionList, [e](SmartScriptHolder const& script) { return script.event_id > e.event_id; });
2545 break;
2546 }
2548 {
2549 WorldObject* sourceObject = GetBaseObjectOrUnitInvoker(unit);
2550 for (WorldObject* target : targets)
2551 {
2553 GameEvents::Trigger(e.action.triggerGameEvent.eventId, target, sourceObject);
2554 else
2555 GameEvents::Trigger(e.action.triggerGameEvent.eventId, sourceObject, target);
2556 }
2557
2558 break;
2559 }
2561 {
2562 for (WorldObject* target : targets)
2563 {
2564 if (Unit* unitTarget = Object::ToUnit(target))
2565 {
2566 if (unitTarget->GetAI())
2567 unitTarget->GetAI()->DoAction(e.action.doAction.actionId);
2568 }
2569 else if (GameObject* goTarget = Object::ToGameObject(target))
2570 {
2571 if (goTarget->AI())
2572 goTarget->AI()->DoAction(e.action.doAction.actionId);
2573 }
2574 }
2575
2576 break;
2577 }
2579 {
2580 uint32 questId = e.action.quest.quest;
2581 Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
2582 if (!quest)
2583 break;
2584
2585 for (WorldObject* target : targets)
2586 {
2587 Player* player = Object::ToPlayer(target);
2588 if (!player)
2589 continue;
2590
2591 QuestStatus questStatus = player->GetQuestStatus(questId);
2592 if (questStatus == QUEST_STATUS_REWARDED)
2593 continue;
2594
2596 {
2597 if (questStatus == QUEST_STATUS_INCOMPLETE)
2599 }
2600 else if (quest->HasFlag(QUEST_FLAGS_TRACKING_EVENT)) // Check if the quest is used as a serverside flag
2601 player->CompleteQuest(questId);
2602 }
2603
2604 break;
2605 }
2607 {
2608 if (!me)
2609 break;
2610
2611 for (WorldObject* target : targets)
2612 {
2613 Player* player = Object::ToPlayer(target);
2614 if (!player)
2615 continue;
2616
2618 }
2619 break;
2620 }
2622 {
2623 auto work = [&](Conversation* conversation)
2624 {
2625 if (conversation->GetEntry() != e.action.destroyConversation.id)
2626 return;
2627
2628 if (conversation->IsPrivateObject())
2629 {
2631 || !advstd::ranges::contains(targets, conversation->GetPrivateObjectOwner(), [](WorldObject const* target) { return target->GetGUID(); }))
2632 return;
2633 }
2635 return;
2636
2637 conversation->Remove();
2638 };
2639
2642 break;
2643 }
2645 {
2646 if (!me)
2647 break;
2648
2649 for (WorldObject* target : targets)
2650 {
2651 if (Unit* unitTarget = target->ToUnit())
2652 {
2653 me->EnterVehicle(unitTarget, (uint8)e.action.enterVehicle.seatId);
2654 break;
2655 }
2656 }
2657 break;
2658 }
2660 {
2661 if (!me)
2662 break;
2663
2664 for (WorldObject* target : targets)
2665 {
2666 if (Unit* unitTarget = target->ToUnit())
2667 {
2668 unitTarget->EnterVehicle(me, (uint8)e.action.enterVehicle.seatId);
2669 break;
2670 }
2671 }
2672 break;
2673 }
2675 {
2676 for (WorldObject* target : targets)
2677 {
2678 if (Unit* unitTarget = target->ToUnit())
2679 {
2680 unitTarget->ExitVehicle();
2681 }
2682 }
2683 break;
2684 }
2685 case SMART_ACTION_FALL:
2686 {
2687 std::shared_ptr<MultiActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<MovementStopReason>>(e, targets.size());
2688
2689 for (WorldObject* target : targets)
2690 {
2691 if (Unit* unitTarget = target->ToUnit())
2692 {
2694 if (waitEvent)
2695 actionResultSetter = Scripting::v2::ActionResult<MovementStopReason>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() });
2696
2697 unitTarget->GetMotionMaster()->MoveFall(e.action.fall.pointId, std::move(actionResultSetter));
2698 }
2699 }
2700
2701 if (waitEvent && !waitEvent->Results.empty())
2702 mTimedActionWaitEvent = std::move(waitEvent);
2703 break;
2704 }
2705 default:
2706 TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Unhandled Action type {}", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
2707 break;
2708 }
2709
2710 if (e.link && e.link != e.event_id)
2711 {
2713 if (linked)
2714 ProcessEvent(linked, unit, var0, var1, bvar, spell, gob, varString);
2715 else
2716 TC_LOG_DEBUG("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Link Event {} not found or invalid, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.link);
2717 }
2718}
2719
2720void SmartScript::ProcessTimedAction(SmartScriptHolder& e, uint32 const& min, uint32 const& max, Unit* unit, uint32 var0, uint32 var1, bool bvar, SpellInfo const* spell, GameObject* gob, std::string_view varString)
2721{
2722 // We may want to execute action rarely and because of this if condition is not fulfilled the action will be rechecked in a long time
2723 if (sConditionMgr->IsObjectMeetingSmartEventConditions(e.entryOrGuid, e.event_id, e.source_type, unit, GetBaseObject()))
2724 {
2725 RecalcTimer(e, min, max);
2726 ProcessAction(e, unit, var0, var1, bvar, spell, gob, varString);
2727 }
2728 else
2729 RecalcTimer(e, std::min<uint32>(min, 5000), std::min<uint32>(min, 5000));
2730}
2731
2732SmartScriptHolder SmartScript::CreateSmartEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, uint32 event_param5, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, uint32 action_param7, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 target_param4, std::string_view targetParamString, uint32 phaseMask)
2733{
2734 SmartScriptHolder script;
2735 script.event.type = e;
2736 script.event.raw.param1 = event_param1;
2737 script.event.raw.param2 = event_param2;
2738 script.event.raw.param3 = event_param3;
2739 script.event.raw.param4 = event_param4;
2740 script.event.raw.param5 = event_param5;
2741 script.event.event_phase_mask = phaseMask;
2742 script.event.event_flags = event_flags;
2743 script.event.event_chance = 100;
2744
2745 script.action.type = action;
2746 script.action.raw.param1 = action_param1;
2747 script.action.raw.param2 = action_param2;
2748 script.action.raw.param3 = action_param3;
2749 script.action.raw.param4 = action_param4;
2750 script.action.raw.param5 = action_param5;
2751 script.action.raw.param6 = action_param6;
2752 script.action.raw.param7 = action_param7;
2753
2754 script.target.type = t;
2755 script.target.raw.param1 = target_param1;
2756 script.target.raw.param2 = target_param2;
2757 script.target.raw.param3 = target_param3;
2758 script.target.raw.param4 = target_param4;
2759 script.target.param_string = targetParamString;
2760
2762 InitTimer(script);
2763 return script;
2764}
2765
2766void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e, WorldObject* invoker /*= nullptr*/) const
2767{
2768 WorldObject* scriptTrigger = nullptr;
2769 if (invoker)
2770 scriptTrigger = invoker;
2771 else if (Unit* tempLastInvoker = GetLastInvoker())
2772 scriptTrigger = tempLastInvoker;
2773
2774 WorldObject* baseObject = GetBaseObject();
2775 switch (e.GetTargetType())
2776 {
2777 case SMART_TARGET_SELF:
2778 if (baseObject)
2779 targets.push_back(baseObject);
2780 break;
2782 if (me)
2783 if (Unit* victim = me->GetVictim())
2784 targets.push_back(victim);
2785 break;
2787 if (me)
2788 {
2790 {
2792 targets.push_back(u);
2793 }
2795 targets.push_back(u);
2796 }
2797 break;
2799 if (me)
2800 {
2802 {
2804 targets.push_back(u);
2805 }
2807 targets.push_back(u);
2808 }
2809 break;
2811 if (me)
2812 {
2814 {
2816 targets.push_back(u);
2817 }
2819 targets.push_back(u);
2820 }
2821 break;
2823 if (me)
2824 {
2826 {
2828 targets.push_back(u);
2829 }
2831 targets.push_back(u);
2832 }
2833 break;
2835 if (me)
2836 {
2838 targets.push_back(u);
2839 }
2840 break;
2842 if (scriptTrigger)
2843 targets.push_back(scriptTrigger);
2844 break;
2846 if (scriptTrigger && scriptTrigger->ToUnit() && scriptTrigger->ToUnit()->GetVehicle() && scriptTrigger->ToUnit()->GetVehicle()->GetBase())
2847 targets.push_back(scriptTrigger->ToUnit()->GetVehicle()->GetBase());
2848 break;
2850 if (scriptTrigger)
2851 {
2852 if (Player* player = scriptTrigger->ToPlayer())
2853 {
2854 if (Group* group = player->GetGroup())
2855 {
2856 for (GroupReference const& groupRef : group->GetMembers())
2857 if (groupRef.GetSource()->IsInMap(player))
2858 targets.push_back(groupRef.GetSource());
2859 }
2860 // We still add the player to the list if there is no group. If we do
2861 // this even if there is a group (thus the else-check), it will add the
2862 // same player to the list twice. We don't want that to happen.
2863 else
2864 targets.push_back(scriptTrigger);
2865 }
2866 }
2867 break;
2869 {
2870 WorldObject* ref = baseObject;
2871 if (!ref)
2872 ref = scriptTrigger;
2873
2874 if (!ref)
2875 {
2876 TC_LOG_ERROR("sql.sql", "SMART_TARGET_CREATURE_RANGE: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
2878 break;
2879 }
2880
2881 std::vector<Creature*> creatures;
2882 ref->GetCreatureListWithOptionsInGrid(creatures, static_cast<float>(e.target.unitRange.maxDist), {
2883 .CreatureId = e.target.unitRange.creature ? Optional<uint32>(e.target.unitRange.creature) : Optional<uint32>(),
2884 .StringId = !e.target.param_string.empty() ? Optional<std::string_view>(e.target.param_string) : Optional<std::string_view>(),
2885 });
2886
2887 std::ranges::copy_if(creatures, std::back_inserter(targets), [&](Creature const* target) { return !ref->IsWithinDist(target, static_cast<float>(e.target.unitRange.minDist)); });
2888
2889 if (e.target.unitRange.maxSize)
2891 break;
2892 }
2894 {
2895 if (!baseObject)
2896 break;
2897
2898 std::vector<Creature*> creatures;
2899 baseObject->GetCreatureListWithOptionsInGrid(creatures, static_cast<float>(e.target.unitDistance.dist), {
2900 .CreatureId = e.target.unitDistance.creature ? Optional<uint32>(e.target.unitDistance.creature) : Optional<uint32>(),
2901 .StringId = !e.target.param_string.empty() ? Optional<std::string_view>(e.target.param_string) : Optional<std::string_view>(),
2902 });
2903
2904 targets = { creatures.begin(), creatures.end() };
2905
2908 break;
2909 }
2911 {
2912 WorldObject* ref = baseObject;
2913 if (!ref)
2914 ref = scriptTrigger;
2915
2916 if (!ref)
2917 {
2918 TC_LOG_ERROR("sql.sql", "SMART_TARGET_GAMEOBJECT_RANGE: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
2920 break;
2921 }
2922
2923 std::vector<GameObject*> gameObjects;
2924 ref->GetGameObjectListWithOptionsInGrid(gameObjects, static_cast<float>(e.target.goRange.maxDist), {
2925 .GameObjectId = e.target.goRange.entry ? Optional<uint32>(e.target.goRange.entry) : Optional<uint32>(),
2926 .StringId = !e.target.param_string.empty() ? Optional<std::string_view>(e.target.param_string) : Optional<std::string_view>(),
2927 });
2928
2929 std::ranges::copy_if(gameObjects, std::back_inserter(targets), [&](GameObject const* target) { return !ref->IsWithinDist(target, static_cast<float>(e.target.goRange.minDist)); });
2930
2931 if (e.target.goRange.maxSize)
2933 break;
2934 }
2936 {
2937 if (!baseObject)
2938 break;
2939
2940 std::vector<GameObject*> gameObjects;
2941 baseObject->GetGameObjectListWithOptionsInGrid(gameObjects, static_cast<float>(e.target.goDistance.dist), {
2942 .GameObjectId = e.target.goDistance.entry ? Optional<uint32>(e.target.goDistance.entry) : Optional<uint32>(),
2943 .StringId = !e.target.param_string.empty() ? Optional<std::string_view>(e.target.param_string) : Optional<std::string_view>(),
2944 });
2945
2946 targets = { gameObjects.begin(), gameObjects.end() };
2947
2950 break;
2951 }
2953 {
2954 if (!scriptTrigger && !baseObject)
2955 {
2956 TC_LOG_ERROR("sql.sql", "SMART_TARGET_CREATURE_GUID: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
2958 break;
2959 }
2960
2961 if (Creature* target = FindCreatureNear(scriptTrigger ? scriptTrigger : baseObject, e.target.unitGUID.dbGuid))
2962 if (!e.target.unitGUID.entry || target->GetEntry() == e.target.unitGUID.entry)
2963 targets.push_back(target);
2964 break;
2965 }
2967 {
2968 if (!scriptTrigger && !baseObject)
2969 {
2970 TC_LOG_ERROR("sql.sql", "SMART_TARGET_GAMEOBJECT_GUID: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
2972 break;
2973 }
2974
2975 if (GameObject* target = FindGameObjectNear(scriptTrigger ? scriptTrigger : baseObject, e.target.goGUID.dbGuid))
2976 if (!e.target.goGUID.entry || target->GetEntry() == e.target.goGUID.entry)
2977 targets.push_back(target);
2978 break;
2979 }
2981 {
2982 if (!baseObject)
2983 break;
2984
2985 std::vector<Player*> players;
2986 baseObject->GetPlayerListInGrid(players, static_cast<float>(e.target.playerRange.maxDist));
2987 std::ranges::copy_if(players, std::back_inserter(targets), [&](Player const* target) { return !baseObject->IsWithinDist(target, static_cast<float>(e.target.playerRange.minDist)); });
2988 break;
2989 }
2991 {
2992 if (!baseObject)
2993 break;
2994
2995 std::vector<Player*> players;
2996 baseObject->GetPlayerListInGrid(players, static_cast<float>(e.target.playerDistance.dist));
2997 targets = { players.begin(), players.end() };
2998 break;
2999 }
3001 {
3002 WorldObject* ref = baseObject;
3003 if (!ref)
3004 ref = scriptTrigger;
3005
3006 if (!ref)
3007 {
3008 TC_LOG_ERROR("sql.sql", "SMART_TARGET_STORED: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
3010 break;
3011 }
3012
3013 if (ObjectVector const* stored = GetStoredTargetVector(e.target.stored.id, *ref))
3014 targets.assign(stored->begin(), stored->end());
3015 break;
3016 }
3018 {
3019 WorldObject* ref = baseObject;
3020 if (!ref)
3021 ref = scriptTrigger;
3022
3023 if (!ref)
3024 {
3025 TC_LOG_ERROR("sql.sql", "SMART_TARGET_CLOSEST_CREATURE: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
3027 break;
3028 }
3029
3031 .CreatureId = e.target.unitClosest.entry,
3034 });
3035
3036 if (target)
3037 targets.push_back(target);
3038 break;
3039 }
3041 {
3042 WorldObject* ref = baseObject;
3043 if (!ref)
3044 ref = scriptTrigger;
3045
3046 if (!ref)
3047 {
3048 TC_LOG_ERROR("sql.sql", "SMART_TARGET_CLOSEST_GAMEOBJECT: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
3050 break;
3051 }
3052
3054 .GameObjectId = e.target.goClosest.entry,
3056 });
3057
3058 if (target)
3059 targets.push_back(target);
3060 break;
3061 }
3063 {
3064 WorldObject* ref = baseObject;
3065 if (!ref)
3066 ref = scriptTrigger;
3067
3068 if (!ref)
3069 {
3070 TC_LOG_ERROR("sql.sql", "SMART_TARGET_CLOSEST_PLAYER: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
3072 break;
3073 }
3074
3075 if (Player* target = ref->SelectNearestPlayer(float(e.target.playerDistance.dist)))
3076 targets.push_back(target);
3077 break;
3078 }
3080 {
3081 if (me)
3082 {
3083 ObjectGuid charmerOrOwnerGuid = me->GetCharmerOrOwnerGUID();
3084
3085 if (!charmerOrOwnerGuid)
3086 if (TempSummon* tempSummon = me->ToTempSummon())
3087 if (WorldObject* summoner = tempSummon->GetSummoner())
3088 charmerOrOwnerGuid = summoner->GetGUID();
3089
3090 if (!charmerOrOwnerGuid)
3091 charmerOrOwnerGuid = me->GetCreatorGUID();
3092
3093 if (WorldObject* owner = ObjectAccessor::GetWorldObject(*me, charmerOrOwnerGuid))
3094 targets.push_back(owner);
3095 }
3096 else if (go)
3097 {
3098 if (Unit* owner = ObjectAccessor::GetUnit(*go, go->GetOwnerGUID()))
3099 targets.push_back(owner);
3100 }
3101
3102 // Get owner of owner
3103 if (e.target.owner.useCharmerOrOwner && !targets.empty())
3104 {
3105 WorldObject* owner = targets.front();
3106 targets.clear();
3107
3108 if (Unit* base = ObjectAccessor::GetUnit(*owner, owner->GetCharmerOrOwnerGUID()))
3109 targets.push_back(base);
3110 }
3111 break;
3112 }
3114 {
3115 if (me && me->CanHaveThreatList())
3116 for (auto* ref : me->GetThreatManager().GetUnsortedThreatList())
3117 if (!e.target.threatList.maxDist || me->IsWithinCombatRange(ref->GetVictim(), float(e.target.threatList.maxDist)))
3118 targets.push_back(ref->GetVictim());
3119 break;
3120 }
3122 {
3123 if (me)
3125 targets.push_back(target);
3126 break;
3127 }
3129 {
3130 if (me)
3132 targets.push_back(target);
3133 break;
3134 }
3136 {
3137 if (me)
3138 for (ObjectGuid tapperGuid : me->GetTapList())
3139 if (Player* tapper = ObjectAccessor::GetPlayer(*me, tapperGuid))
3140 targets.push_back(tapper);
3141
3142 break;
3143 }
3145 {
3146 if (me && me->IsVehicle())
3147 for (std::pair<int8 const, VehicleSeat>& seat : me->GetVehicleKit()->Seats)
3148 if (!e.target.vehicle.seatMask || (e.target.vehicle.seatMask & (1 << seat.first)))
3149 if (Unit* u = ObjectAccessor::GetUnit(*me, seat.second.Passenger.Guid))
3150 targets.push_back(u);
3151 break;
3152 }
3154 {
3155 if (GameObject* target = baseObject->FindNearestUnspawnedGameObject(e.target.goClosest.entry, float(e.target.goClosest.dist ? e.target.goClosest.dist : 100)))
3156 targets.push_back(target);
3157 break;
3158 }
3160 case SMART_TARGET_NONE:
3161 default:
3162 break;
3163 }
3164}
3165
3166void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, uint32 var1, bool bvar, SpellInfo const* spell, GameObject* gob, std::string_view varString)
3167{
3168 if (!e.active && e.GetEventType() != SMART_EVENT_LINK)
3169 return;
3170
3172 return;
3173
3175 return;
3176
3177 switch (e.GetEventType())
3178 {
3179 case SMART_EVENT_LINK://special handling
3180 ProcessAction(e, unit, var0, var1, bvar, spell, gob);
3181 break;
3182 //called from Update tick
3183 case SMART_EVENT_UPDATE:
3185 break;
3187 if (me && me->IsEngaged())
3188 return;
3190 break;
3192 if (!me || !me->IsEngaged())
3193 return;
3195 break;
3197 {
3198 if (!me || me->IsInEvadeMode() || !me->GetMaxHealth())
3199 return;
3200 uint32 perc = (uint32)me->GetHealthPct();
3201 if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
3202 return;
3204 break;
3205 }
3207 {
3208 if (!me || !me->IsEngaged() || !me->GetMaxPower(POWER_MANA))
3209 return;
3211 if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
3212 return;
3214 break;
3215 }
3216 case SMART_EVENT_RANGE:
3217 {
3218 if (!me || !me->IsEngaged() || !me->GetVictim())
3219 return;
3220
3221 if (me->IsInRange(me->GetVictim(), (float)e.event.minMaxRepeat.min, (float)e.event.minMaxRepeat.max))
3223 else // make it predictable
3224 RecalcTimer(e, 500, 500);
3225 break;
3226 }
3228 {
3229 if (!me || !me->IsEngaged())
3230 return;
3231
3232 Unit* victim = me->GetVictim();
3233
3234 if (!victim || !victim->IsNonMeleeSpellCast(false, false, true))
3235 return;
3236
3237 if (e.event.targetCasting.spellId > 0)
3238 if (Spell* currSpell = victim->GetCurrentSpell(CURRENT_GENERIC_SPELL))
3239 if (currSpell->m_spellInfo->Id != e.event.targetCasting.spellId)
3240 return;
3241
3243 break;
3244 }
3246 {
3247 if (!me || !me->IsEngaged())
3248 return;
3249
3250 std::vector<Creature*> creatures;
3251 DoFindFriendlyCC(creatures, float(e.event.friendlyCC.radius));
3252 if (creatures.empty())
3253 {
3254 // if there are at least two same npcs, they will perform the same action immediately even if this is useless...
3255 RecalcTimer(e, 1000, 3000);
3256 return;
3257 }
3259 break;
3260 }
3262 {
3263 std::vector<Creature*> creatures;
3265
3266 if (creatures.empty())
3267 return;
3268
3270 break;
3271 }
3273 {
3274 if (!me)
3275 return;
3276 uint32 count = me->GetAuraCount(e.event.aura.spell);
3277 if ((!e.event.aura.count && !count) || (e.event.aura.count && count >= e.event.aura.count))
3279 break;
3280 }
3282 {
3283 if (!me || !me->GetVictim())
3284 return;
3286 if (count < e.event.aura.count)
3287 return;
3289 break;
3290 }
3292 {
3293 if (bvar == (e.event.charm.onRemove != 1))
3294 ProcessAction(e, unit, var0, var1, bvar, spell, gob);
3295 break;
3296 }
3301 {
3302 ProcessAction(e, unit);
3303 break;
3304 }
3306 {
3307 if (var0 == (e.event.questObjective.id))
3308 ProcessAction(e, unit);
3309 break;
3310 }
3311 //no params
3312 case SMART_EVENT_AGGRO:
3313 case SMART_EVENT_DEATH:
3314 case SMART_EVENT_EVADE:
3316 case SMART_EVENT_RESET:
3329 ProcessAction(e, unit, var0, var1, bvar, spell, gob);
3330 break;
3332 switch (e.event.gossipHello.filter)
3333 {
3334 case 0:
3335 // no filter set, always execute action
3336 break;
3337 case 1:
3338 // OnGossipHello only filter set, skip action if OnReportUse
3339 if (var0)
3340 return;
3341 break;
3342 case 2:
3343 // OnReportUse only filter set, skip action if OnGossipHello
3344 if (!var0)
3345 return;
3346 break;
3347 default:
3348 // Ignore any other value
3349 break;
3350 }
3351
3352 ProcessAction(e, unit, var0, var1, bvar, spell, gob);
3353 break;
3355 if (e.event.emote.emote == var0)
3356 {
3357 RecalcTimer(e, e.event.emote.cooldownMin, e.event.emote.cooldownMax);
3358 ProcessAction(e, unit);
3359 }
3360 break;
3361 case SMART_EVENT_KILL:
3362 {
3363 if (!me || !unit)
3364 return;
3365 if (e.event.kill.playerOnly && unit->GetTypeId() != TYPEID_PLAYER)
3366 return;
3367 if (e.event.kill.creature && unit->GetEntry() != e.event.kill.creature)
3368 return;
3370 ProcessAction(e, unit);
3371 break;
3372 }
3375 {
3376 if (!spell)
3377 return;
3378 if ((!e.event.spellHit.spell || spell->Id == e.event.spellHit.spell) &&
3379 (!e.event.spellHit.school || (spell->SchoolMask & e.event.spellHit.school)))
3380 {
3382 ProcessAction(e, unit, 0, 0, bvar, spell, gob);
3383 }
3384 break;
3385 }
3391 {
3392 if (!spell)
3393 return;
3394
3395 if (spell->Id != e.event.spellCast.spell)
3396 return;
3397
3399 ProcessAction(e, nullptr, 0, 0, bvar, spell);
3400 break;
3401 }
3403 {
3404 if (!me || me->IsEngaged())
3405 return;
3406 //can trigger if closer than fMaxAllowedRange
3407 float range = (float)e.event.los.maxDist;
3408
3409 //if range is ok and we are actually in LOS
3410 if (me->IsWithinDistInMap(unit, range) && me->IsWithinLOSInMap(unit))
3411 {
3413 //if friendly event&&who is not hostile OR hostile event&&who is hostile
3414 if ((hostilityMode == SmartEvent::LOSHostilityMode::Any) ||
3415 (hostilityMode == SmartEvent::LOSHostilityMode::NotHostile && !me->IsHostileTo(unit)) ||
3416 (hostilityMode == SmartEvent::LOSHostilityMode::Hostile && me->IsHostileTo(unit)))
3417 {
3418 if (e.event.los.playerOnly && unit->GetTypeId() != TYPEID_PLAYER)
3419 return;
3421 ProcessAction(e, unit);
3422 }
3423 }
3424 break;
3425 }
3426 case SMART_EVENT_IC_LOS:
3427 {
3428 if (!me || !me->IsEngaged())
3429 return;
3430 //can trigger if closer than fMaxAllowedRange
3431 float range = (float)e.event.los.maxDist;
3432
3433 //if range is ok and we are actually in LOS
3434 if (me->IsWithinDistInMap(unit, range) && me->IsWithinLOSInMap(unit))
3435 {
3437 //if friendly event&&who is not hostile OR hostile event&&who is hostile
3438 if ((hostilityMode == SmartEvent::LOSHostilityMode::Any) ||
3439 (hostilityMode == SmartEvent::LOSHostilityMode::NotHostile && !me->IsHostileTo(unit)) ||
3440 (hostilityMode == SmartEvent::LOSHostilityMode::Hostile && me->IsHostileTo(unit)))
3441 {
3442 if (e.event.los.playerOnly && unit->GetTypeId() != TYPEID_PLAYER)
3443 return;
3445 ProcessAction(e, unit);
3446 }
3447 }
3448 break;
3449 }
3451 {
3452 if (!GetBaseObject())
3453 return;
3455 return;
3457 return;
3458 ProcessAction(e);
3459 break;
3460 }
3463 {
3464 if (!unit->IsCreature())
3465 return;
3466 if (e.event.summoned.creature && unit->GetEntry() != e.event.summoned.creature)
3467 return;
3469 ProcessAction(e, unit);
3470 break;
3471 }
3475 {
3476 if (var0 > e.event.minMaxRepeat.max || var0 < e.event.minMaxRepeat.min)
3477 return;
3479 ProcessAction(e, unit);
3480 break;
3481 }
3483 {
3484 if ((e.event.movementInform.type && var0 != e.event.movementInform.type) || (e.event.movementInform.id != 0xFFFFFFFF && var1 != e.event.movementInform.id))
3485 return;
3486 ProcessAction(e, unit, var0, var1);
3487 break;
3488 }
3490 {
3492 return;
3493 ProcessAction(e, unit, var0);
3494 break;
3495 }
3501 {
3502 if (!me || (e.event.waypoint.pointID != 0xFFFFFFFF && var0 != e.event.waypoint.pointID) || (e.event.waypoint.pathID && var1 != e.event.waypoint.pathID))
3503 return;
3504 ProcessAction(e, unit);
3505 break;
3506 }
3508 {
3509 if (e.event.summoned.creature && e.event.summoned.creature != var0)
3510 return;
3512 ProcessAction(e, unit, var0);
3513 break;
3514 }
3516 {
3518 return;
3520 ProcessAction(e, unit, var0);
3521 break;
3522 }
3525 {
3526 if (e.event.quest.quest && var0 != e.event.quest.quest)
3527 return;
3528 RecalcTimer(e, e.event.quest.cooldownMin, e.event.quest.cooldownMax);
3529 ProcessAction(e, unit, var0);
3530 break;
3531 }
3533 {
3535 return;
3536 ProcessAction(e, unit, var0);
3537 break;
3538 }
3540 {
3542 return;
3543 ProcessAction(e, unit, var0);
3544 break;
3545 }
3547 {
3548 if (e.event.dataSet.id != var0 || e.event.dataSet.value != var1)
3549 return;
3551 ProcessAction(e, unit, var0, var1);
3552 break;
3553 }
3556 {
3557 if (!unit)
3558 return;
3560 ProcessAction(e, unit);
3561 break;
3562 }
3564 {
3565 if (e.event.timedEvent.id == var0)
3566 ProcessAction(e, unit);
3567 break;
3568 }
3570 {
3571 TC_LOG_DEBUG("scripts.ai", "SmartScript: Gossip Select: menu {} action {}", var0, var1);//little help for scripters
3572 if (e.event.gossip.sender != var0 || e.event.gossip.action != var1)
3573 return;
3574 ProcessAction(e, unit, var0, var1);
3575 break;
3576 }
3579 {
3580 if (e.event.gameEvent.gameEventId != var0)
3581 return;
3582 ProcessAction(e, nullptr, var0);
3583 break;
3584 }
3586 {
3587 if (e.event.goLootStateChanged.lootState != var0)
3588 return;
3589 ProcessAction(e, unit, var0, var1);
3590 break;
3591 }
3593 {
3594 if (e.event.eventInform.eventId != var0)
3595 return;
3596 ProcessAction(e, nullptr, var0);
3597 break;
3598 }
3600 {
3601 if (e.event.doAction.eventId != var0)
3602 return;
3603 ProcessAction(e, unit, var0);
3604 break;
3605 }
3607 {
3608 if (!me || me->IsInEvadeMode())
3609 return;
3610
3611 Unit* unitTarget = nullptr;
3612 switch (e.GetTargetType())
3613 {
3621 {
3622 ObjectVector targets;
3623 GetTargets(targets, e);
3624
3625 auto unitTargetItr = std::ranges::find_if(targets, [this, &e](WorldObject* target)
3626 {
3627 Unit* unit = target->ToUnit();
3628 if (unit && me->IsFriendlyTo(unit) && unit->IsAlive() && unit->IsInCombat())
3629 {
3630 uint32 healthPct = uint32(unit->GetHealthPct());
3631 if (e.event.friendlyHealthPct.minHpPct <= healthPct && healthPct <= e.event.friendlyHealthPct.maxHpPct)
3632 return true;
3633 }
3634 return false;
3635 });
3636
3637 if (unitTargetItr != targets.end())
3638 unitTarget = (*unitTargetItr)->ToUnit();
3639 break;
3640 }
3643 break;
3644 default:
3645 return;
3646 }
3647
3648 if (!unitTarget)
3649 return;
3650
3652 break;
3653 }
3655 {
3656 if (!me)
3657 return;
3658
3659 Creature* creature = nullptr;
3660
3661 if (e.event.distance.guid != 0)
3662 {
3663 creature = FindCreatureNear(me, e.event.distance.guid);
3664 if (!creature)
3665 return;
3666
3667 if (!me->IsInRange(creature, 0, static_cast<float>(e.event.distance.dist)))
3668 return;
3669 }
3670 else if (e.event.distance.entry != 0)
3671 {
3672 std::list<Creature*> list;
3673 me->GetCreatureListWithEntryInGrid(list, e.event.distance.entry, static_cast<float>(e.event.distance.dist));
3674
3675 if (!list.empty())
3676 creature = list.front();
3677 }
3678
3679 if (creature)
3681
3682 break;
3683 }
3685 {
3686 if (!me)
3687 return;
3688
3689 GameObject* gameobject = nullptr;
3690
3691 if (e.event.distance.guid != 0)
3692 {
3693 gameobject = FindGameObjectNear(me, e.event.distance.guid);
3694 if (!gameobject)
3695 return;
3696
3697 if (!me->IsInRange(gameobject, 0, static_cast<float>(e.event.distance.dist)))
3698 return;
3699 }
3700 else if (e.event.distance.entry != 0)
3701 {
3702 std::list<GameObject*> list;
3703 me->GetGameObjectListWithEntryInGrid(list, e.event.distance.entry, static_cast<float>(e.event.distance.dist));
3704
3705 if (!list.empty())
3706 gameobject = list.front();
3707 }
3708
3709 if (gameobject)
3710 ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat, nullptr, 0, 0, false, nullptr, gameobject);
3711
3712 break;
3713 }
3715 if (e.event.counter.id != var0 || GetCounterValue(e.event.counter.id) != e.event.counter.value)
3716 return;
3717
3719 break;
3723 {
3724 ProcessAction(e, unit);
3725 break;
3726 }
3728 {
3729 if (e.event.param_string != varString)
3730 return;
3731
3732 ProcessAction(e, unit, var0, 0, false, nullptr, nullptr, varString);
3733 break;
3734 }
3735 default:
3736 TC_LOG_ERROR("sql.sql", "SmartScript::ProcessEvent: Unhandled Event type {}", e.GetEventType());
3737 break;
3738 }
3739}
3740
3742{
3743 switch (e.GetEventType())
3744 {
3745 //set only events which have initial timers
3746 case SMART_EVENT_UPDATE:
3750 break;
3754 break;
3755 default:
3756 e.active = true;
3757 break;
3758 }
3759}
3761{
3762 // min/max was checked at loading!
3763 e.timer = urand(min, max);
3764 e.active = e.timer == 0;
3765}
3766
3768{
3769 if (e.GetEventType() == SMART_EVENT_LINK)
3770 return;
3771
3773 return;
3774
3775 if (e.GetEventType() == SMART_EVENT_UPDATE_IC && (!me || !me->IsEngaged()))
3776 return;
3777
3778 if (e.GetEventType() == SMART_EVENT_UPDATE_OOC && (me && me->IsEngaged())) //can be used with me=nullptr (go script)
3779 return;
3780
3782 return;
3783
3784 if (e.timer < diff)
3785 {
3786 // delay spell cast event if another spell is being cast
3788 {
3790 {
3792 {
3793 RaisePriority(e);
3794 return;
3795 }
3796 }
3797 }
3798
3799 // Delay flee for assist event if stunned or rooted
3801 {
3803 {
3804 e.timer = 1;
3805 return;
3806 }
3807 }
3808
3809 e.active = true;//activate events with cooldown
3810
3811 switch (e.GetEventType())//process ONLY timed events
3812 {
3813 case SMART_EVENT_UPDATE:
3818 case SMART_EVENT_RANGE:
3827 {
3829 {
3830 Unit* invoker = nullptr;
3833 ProcessEvent(e, invoker);
3834 e.enableTimed = false;//disable event if it is in an ActionList and was processed once
3835 for (SmartScriptHolder& scriptholder : mTimedActionList)
3836 {
3837 //find the first event which is not the current one and enable it
3838 if (scriptholder.event_id > e.event_id)
3839 {
3840 scriptholder.enableTimed = true;
3841 break;
3842 }
3843 }
3844 }
3845 else
3846 ProcessEvent(e);
3847 break;
3848 }
3849 }
3850
3852 {
3853 // Reset priority to default one only if the event hasn't been rescheduled again to next loop
3854 if (e.timer > 1)
3855 {
3856 // Re-sort events if this was moved to the top of the queue
3857 mEventSortingRequired = true;
3858 // Reset priority to default one
3860 }
3861 }
3862 }
3863 else
3864 e.timer -= diff;
3865}
3866
3868{
3869 if (!mStoredEvents.empty())
3870 {
3871 for (auto i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
3872 {
3873 if (i->event_id == id)
3874 {
3875 mStoredEvents.erase(i);
3876 return;
3877 }
3878 }
3879 }
3880}
3881
3883{
3884 WorldObject* obj = nullptr;
3885 if (me)
3886 obj = me;
3887 else if (go)
3888 obj = go;
3889 else if (areaTrigger)
3890 obj = areaTrigger;
3891 else if (player)
3892 obj = player;
3893 return obj;
3894}
3895
3897{
3898 return Coalesce<WorldObject>(GetBaseObject(), invoker);
3899}
3900
3902{
3907 && !GetBaseObject())
3908 return;
3909
3910 // Don't run any action while evading
3911 if (me && me->IsInEvadeMode())
3912 {
3913 // Check if the timed action list finished and clear it if so.
3914 // This is required by SMART_ACTION_CALL_TIMED_ACTIONLIST failing if mTimedActionList is not empty.
3915 if (!mTimedActionList.empty())
3916 {
3917 bool needCleanup = true;
3918 for (SmartScriptHolder& scriptholder : mTimedActionList)
3919 {
3920 if (scriptholder.enableTimed)
3921 needCleanup = false;
3922 }
3923
3924 if (needCleanup)
3925 mTimedActionList.clear();
3926 }
3927
3928 return;
3929 }
3930
3932 {
3934 mEventSortingRequired = false;
3935 }
3936
3937 for (SmartScriptHolder& mEvent : mEvents)
3938 UpdateTimer(mEvent, diff);
3939
3940 if (!mStoredEvents.empty())
3941 {
3942 SmartAIEventStoredList::iterator i, icurr;
3943 for (i = mStoredEvents.begin(); i != mStoredEvents.end();)
3944 {
3945 icurr = i++;
3946 UpdateTimer(*icurr, diff);
3947 }
3948 }
3949
3950 bool needCleanup = true;
3951 if (!mTimedActionList.empty())
3952 {
3954
3955 for (size_t i = 0; i < mTimedActionList.size(); ++i)
3956 {
3957 SmartScriptHolder& scriptHolder = mTimedActionList[i];
3958 if (scriptHolder.enableTimed)
3959 {
3960 UpdateTimer(scriptHolder, diff);
3961 needCleanup = false;
3962 }
3963 }
3964
3966 }
3967
3968 if (needCleanup)
3969 mTimedActionList.clear();
3970
3971 if (!mRemIDs.empty())
3972 {
3973 for (auto i : mRemIDs)
3975
3976 mRemIDs.clear();
3977 }
3978
3979 if (mUseTextTimer && me)
3980 {
3981 if (mTextTimer < diff)
3982 {
3983 uint32 textID = mLastTextID;
3984 mLastTextID = 0;
3985 uint32 entry = mTalkerEntry;
3986 mTalkerEntry = 0;
3987 mTextTimer = 0;
3988 mUseTextTimer = false;
3989 ProcessEventsFor(SMART_EVENT_TEXT_OVER, nullptr, textID, entry);
3990 } else mTextTimer -= diff;
3991 }
3992}
3993
3995{
3996 std::sort(events.begin(), events.end());
3997}
3998
4000{
4001 e.timer = 1;
4002 // Change priority only if it's set to default, otherwise keep the current order of events
4004 {
4006 mEventSortingRequired = true;
4007 }
4008}
4009
4010void SmartScript::RetryLater(SmartScriptHolder& e, bool ignoreChanceRoll)
4011{
4012 RaisePriority(e);
4013
4014 // This allows to retry the action later without rolling again the chance roll (which might fail and end up not executing the action)
4015 if (ignoreChanceRoll)
4017
4018 e.runOnce = false;
4019}
4020
4021void SmartScript::FillScript(SmartAIEventList&& e, WorldObject* obj, AreaTriggerEntry const* at, SceneTemplate const* scene, Quest const* quest, uint32 event)
4022{
4023 if (e.empty())
4024 {
4025 if (obj)
4026 TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for Entry {} is empty but is using SmartScript.", obj->GetEntry());
4027 if (at)
4028 TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for AreaTrigger {} is empty but is using SmartScript.", at->ID);
4029 if (scene)
4030 TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for SceneId {} is empty but is using SmartScript.", scene->SceneId);
4031 if (quest)
4032 TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for Quest {} is empty but is using SmartScript.", quest->GetQuestId());
4033 if (event)
4034 TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for Event {} is empty but is using SmartScript.", event);
4035 return;
4036 }
4037 for (SmartScriptHolder& scriptholder : std::move(e))
4038 {
4039 #ifndef TRINITY_DEBUG
4040 if (scriptholder.event.event_flags & SMART_EVENT_FLAG_DEBUG_ONLY)
4041 continue;
4042 #endif
4043
4044 if (obj && !scriptholder.Difficulties.empty())
4045 {
4046 bool foundValidDifficulty = false;
4047 for (Difficulty difficulty : scriptholder.Difficulties)
4048 {
4049 if (difficulty == obj->GetMap()->GetDifficultyID())
4050 {
4051 foundValidDifficulty = true;
4052 break;
4053 }
4054 }
4055
4056 if (!foundValidDifficulty)
4057 continue;
4058 }
4059
4060 mAllEventFlags |= scriptholder.event.event_flags;
4061 mEvents.push_back(std::move(scriptholder));
4062 }
4063}
4064
4066{
4068
4069 // We must use script type to avoid ambiguities
4070 switch (mScriptType)
4071 {
4073 e = sSmartScriptMgr->GetScript(-((int32)me->GetSpawnId()), mScriptType);
4074 if (e.empty())
4075 e = sSmartScriptMgr->GetScript((int32)me->GetEntry(), mScriptType);
4076 FillScript(std::move(e), me, nullptr, nullptr, nullptr, 0);
4077 break;
4079 e = sSmartScriptMgr->GetScript(-((int32)go->GetSpawnId()), mScriptType);
4080 if (e.empty())
4081 e = sSmartScriptMgr->GetScript((int32)go->GetEntry(), mScriptType);
4082 FillScript(std::move(e), go, nullptr, nullptr, nullptr, 0);
4083 break;
4087 FillScript(std::move(e), areaTrigger, nullptr, nullptr, nullptr, 0);
4088 break;
4090 e = sSmartScriptMgr->GetScript((int32)trigger->ID, mScriptType);
4091 FillScript(std::move(e), nullptr, trigger, nullptr, nullptr, 0);
4092 break;
4095 FillScript(std::move(e), nullptr, nullptr, sceneTemplate, nullptr, 0);
4096 break;
4098 e = sSmartScriptMgr->GetScript(quest->GetQuestId(), mScriptType);
4099 FillScript(std::move(e), nullptr, nullptr, nullptr, quest, 0);
4100 break;
4102 e = sSmartScriptMgr->GetScript((int32)event, mScriptType);
4103 FillScript(std::move(e), nullptr, nullptr, nullptr, nullptr, event);
4104 break;
4105 default:
4106 break;
4107 }
4108}
4109
4110void SmartScript::OnInitialize(WorldObject* obj, AreaTriggerEntry const* at, SceneTemplate const* scene, Quest const* qst, uint32 evnt)
4111{
4112 if (at)
4113 {
4115 trigger = at;
4116 player = obj->ToPlayer();
4117
4118 if (!player)
4119 {
4120 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: source is AreaTrigger with id {}, missing trigger player", trigger->ID);
4121 return;
4122 }
4123
4124 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is AreaTrigger with id {}, triggered by player {}", trigger->ID, player->GetGUID());
4125 }
4126 else if (scene)
4127 {
4129 sceneTemplate = scene;
4130 player = obj->ToPlayer();
4131
4132 if (!player)
4133 {
4134 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: source is Scene with id {}, missing trigger player", scene->SceneId);
4135 return;
4136 }
4137
4138 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Scene with id {}, triggered by player {}", scene->SceneId, player->GetGUID());
4139 }
4140 else if (qst)
4141 {
4143 quest = qst;
4144 player = obj->ToPlayer();
4145
4146 if (!player)
4147 {
4148 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: source is Quest with id {}, missing trigger player", qst->GetQuestId());
4149 return;
4150 }
4151
4152 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Quest with id {}, triggered by player {}", qst->GetQuestId(), player->GetGUID());
4153 }
4154 else if (evnt)
4155 {
4157 event = evnt;
4158
4159 if (obj->IsPlayer())
4160 {
4161 player = obj->ToPlayer();
4162 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Event {}, triggered by player {}", event, player->GetGUID());
4163 }
4164 else if (obj->IsCreature())
4165 {
4166 me = obj->ToCreature();
4167 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Event {}, triggered by creature {}", event, me->GetEntry());
4168 }
4169 else if (obj->IsGameObject())
4170 {
4171 go = obj->ToGameObject();
4172 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Event {}, triggered by gameobject {}", event, go->GetEntry());
4173 }
4174 else
4175 {
4176 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: source is Event {}, missing trigger WorldObject", event);
4177 return;
4178 }
4179 }
4180 else if (obj) // Handle object based scripts
4181 {
4182 switch (obj->GetTypeId())
4183 {
4184 case TYPEID_UNIT:
4186 me = obj->ToCreature();
4187 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Creature {}", me->GetEntry());
4188 break;
4189 case TYPEID_GAMEOBJECT:
4191 go = obj->ToGameObject();
4192 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is GameObject {}", go->GetEntry());
4193 break;
4194 case TYPEID_AREATRIGGER:
4195 areaTrigger = obj->ToAreaTrigger();
4197 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is AreaTrigger {}, IsCustom {}", areaTrigger->GetEntry(), uint32(areaTrigger->IsCustom()));
4198 break;
4199 default:
4200 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: Unhandled TypeID !WARNING!");
4201 return;
4202 }
4203 }
4204 else
4205 {
4206 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: !WARNING! Initialized objects are NULL.");
4207 return;
4208 }
4209
4210 GetScript();//load copy of script
4211
4213 InitTimer(event);//calculate timers for first time use
4214
4217 mCounterList.clear();
4218}
4219
4221{
4222 if (!me)
4223 return;
4224
4226}
4227// SmartScript end
4228
4230{
4231 if (!me)
4232 return nullptr;
4233
4234 Unit* unit = nullptr;
4235
4236 Trinity::MostHPMissingInRange u_check(me, range, MinHPDiff);
4238 Cell::VisitGridObjects(me, searcher, range);
4239 return unit;
4240}
4241
4243{
4244 if (!me)
4245 return nullptr;
4246
4247 Unit* unit = nullptr;
4248 Trinity::MostHPPercentMissingInRange u_check(me, range, minHpPct, maxHpPct);
4250 Cell::VisitGridObjects(me, searcher, range);
4251 return unit;
4252}
4253
4254void SmartScript::DoFindFriendlyCC(std::vector<Creature*>& creatures, float range) const
4255{
4256 if (!me)
4257 return;
4258
4259 Trinity::FriendlyCCedInRange u_check(me, range);
4261 Cell::VisitGridObjects(me, searcher, range);
4262}
4263
4264void SmartScript::DoFindFriendlyMissingBuff(std::vector<Creature*>& creatures, float range, uint32 spellid) const
4265{
4266 if (!me)
4267 return;
4268
4269 Trinity::FriendlyMissingBuffInRange u_check(me, range, spellid);
4271 Cell::VisitGridObjects(me, searcher, range);
4272}
4273
4274Unit* SmartScript::DoFindClosestFriendlyInRange(float range, bool playerOnly) const
4275{
4276 if (!me)
4277 return nullptr;
4278
4279 Unit* unit = nullptr;
4280 Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(me, me, range, playerOnly);
4282 Cell::VisitAllObjects(me, searcher, range);
4283 return unit;
4284}
4285
4286void SmartScript::SetTimedActionList(SmartScriptHolder& e, uint32 entry, Unit* invoker, uint32 startFromEventId)
4287{
4288 //do NOT clear mTimedActionList if it's being iterated because it will invalidate the iterator and delete
4289 // any SmartScriptHolder contained like the "e" parameter passed to this function
4291 {
4292 TC_LOG_ERROR("scripts.ai", "Entry {} SourceType {} Event {} Action {} is trying to overwrite timed action list from a timed action, this is not allowed!.", e.entryOrGuid, e.GetScriptType(), e.GetEventType(), e.GetActionType());
4293 return;
4294 }
4295
4296 // Do NOT allow to start a new actionlist if a previous one is already running, unless explicitly allowed. We need to always finish the current actionlist
4298 return;
4299
4300 mTimedActionList.clear();
4302 if (mTimedActionList.empty())
4303 return;
4304
4305 Trinity::Containers::EraseIf(mTimedActionList, [startFromEventId](SmartScriptHolder const& script) { return script.event_id < startFromEventId; });
4306
4307 mTimedActionListInvoker = invoker ? invoker->GetGUID() : ObjectGuid::Empty;
4308 for (SmartAIEventList::iterator i = mTimedActionList.begin(); i != mTimedActionList.end(); ++i)
4309 {
4310 i->enableTimed = i == mTimedActionList.begin();//enable processing only for the first action
4311
4312 if (e.action.timedActionList.timerType == 0)
4313 i->event.type = SMART_EVENT_UPDATE_OOC;
4314 else if (e.action.timedActionList.timerType == 1)
4315 i->event.type = SMART_EVENT_UPDATE_IC;
4316 else if (e.action.timedActionList.timerType > 1)
4317 i->event.type = SMART_EVENT_UPDATE;
4318
4319 InitTimer((*i));
4320 }
4321}
4322
4324{
4325 // Look for invoker only on map of base object... Prevents multithreaded crashes
4326 if (WorldObject* baseObject = GetBaseObject())
4327 return ObjectAccessor::GetUnit(*baseObject, mLastInvoker);
4328 // used for area triggers invoker cast
4329 else if (invoker)
4330 return ObjectAccessor::GetUnit(*invoker, mLastInvoker);
4331
4332 return nullptr;
4333}
4334
4336{
4337 // protect phase from overflowing
4338 SetPhase(std::min<uint32>(SMART_EVENT_PHASE_12, mEventPhase + p));
4339}
4340
4342{
4343 if (p >= mEventPhase)
4344 SetPhase(0);
4345 else
4346 SetPhase(mEventPhase - p);
4347}
4348
4350{
4351 mEventPhase = p;
4352}
4353
4355{
4356 if (mEventPhase == 0)
4357 return false;
4358 return ((1 << (mEventPhase - 1)) & p) != 0;
4359}
#define M_PI
Definition Common.h:118
#define sConditionMgr
#define sCreatureTextMgr
@ TEXT_RANGE_NORMAL
DB2Storage< SummonPropertiesEntry > sSummonPropertiesStore("SummonProperties.db2", &SummonPropertiesLoadInfo::Instance)
Difficulty
Definition DBCEnums.h:932
@ DIFFICULTY_NONE
Definition DBCEnums.h:933
uint8_t uint8
Definition Define.h:156
int8_t int8
Definition Define.h:152
int32_t int32
Definition Define.h:150
uint64_t uint64
Definition Define.h:153
#define UI64LIT(N)
Definition Define.h:139
uint32_t uint32
Definition Define.h:154
uint16 flags
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
std::chrono::seconds Seconds
Seconds shorthand typedef.
Definition Duration.h:28
std::string GetDebugInfo()
Definition Errors.cpp:170
#define sGameEventMgr
GameObjectActions
LootState
Definition GameObject.h:155
@ GO_READY
Definition GameObject.h:157
EncounterState
@ BROADCAST_TEXT_CALL_FOR_HELP
Definition Language.h:24
@ BROADCAST_TEXT_FLEE_FOR_ASSIST
Definition Language.h:25
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_WARN(filterType__, message__,...)
Definition Log.h:187
TempSummonType
GOSummonType
@ TYPEID_AREATRIGGER
Definition ObjectGuid.h:49
@ TYPEID_GAMEOBJECT
Definition ObjectGuid.h:46
@ TYPEID_UNIT
Definition ObjectGuid.h:43
@ TYPEID_PLAYER
Definition ObjectGuid.h:44
#define sObjectMgr
Definition ObjectMgr.h:1885
FindCreatureAliveState
Definition Object.h:222
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
QuestStatus
Definition QuestDef.h:146
@ QUEST_STATUS_REWARDED
Definition QuestDef.h:153
@ QUEST_STATUS_INCOMPLETE
Definition QuestDef.h:150
@ QUEST_FLAGS_COMPLETION_AREA_TRIGGER
Definition QuestDef.h:220
@ QUEST_FLAGS_COMPLETION_EVENT
Definition QuestDef.h:219
@ QUEST_FLAGS_TRACKING_EVENT
Definition QuestDef.h:228
uint32 urand(uint32 min, uint32 max)
Definition Random.cpp:42
bool roll_chance(T chance)
Definition Random.h:55
@ LANG_ADDON
@ SPELL_EFFECT_SUMMON
@ TEAM_OTHER
Powers
@ POWER_MANA
SpellCastResult
@ SPELL_FAILED_BAD_TARGETS
@ SPELL_CAST_OK
@ SPELL_FAILED_SPELL_IN_PROGRESS
@ CHAT_MSG_MONSTER_EMOTE
@ CHAT_MSG_ADDON
GOState
@ SMART_SCRIPT_TYPE_TIMED_ACTIONLIST
@ SMART_SCRIPT_TYPE_CREATURE
@ SMART_SCRIPT_TYPE_AREATRIGGER_ENTITY_CUSTOM
@ SMART_SCRIPT_TYPE_GAMEOBJECT
@ SMART_SCRIPT_TYPE_AREATRIGGER
@ SMART_SCRIPT_TYPE_AREATRIGGER_ENTITY
@ SMART_SCRIPT_TYPE_EVENT
@ SMART_SCRIPT_TYPE_SCENE
@ SMART_SCRIPT_TYPE_QUEST
std::vector< SmartScriptHolder > SmartAIEventList
@ SMART_EVENT_FLAG_WHILE_CHARMED
@ SMART_EVENT_FLAG_DONT_RESET
@ SMART_EVENT_FLAG_ACTIONLIST_WAITS
@ SMART_EVENT_FLAG_DEBUG_ONLY
@ SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL
@ SMART_EVENT_FLAG_NOT_REPEATABLE
SMARTAI_TARGETS
@ SMART_TARGET_LOOT_RECIPIENTS
@ SMART_TARGET_CLOSEST_CREATURE
@ SMART_TARGET_CREATURE_DISTANCE
@ SMART_TARGET_HOSTILE_RANDOM_NOT_TOP
@ SMART_TARGET_INVOKER_PARTY
@ SMART_TARGET_CLOSEST_FRIENDLY
@ SMART_TARGET_CLOSEST_GAMEOBJECT
@ SMART_TARGET_VEHICLE_PASSENGER
@ SMART_TARGET_GAMEOBJECT_RANGE
@ SMART_TARGET_CREATURE_GUID
@ SMART_TARGET_PLAYER_RANGE
@ SMART_TARGET_CLOSEST_UNSPAWNED_GAMEOBJECT
@ SMART_TARGET_VICTIM
@ SMART_TARGET_GAMEOBJECT_DISTANCE
@ SMART_TARGET_CREATURE_RANGE
@ SMART_TARGET_CLOSEST_PLAYER
@ SMART_TARGET_HOSTILE_RANDOM
@ SMART_TARGET_GAMEOBJECT_GUID
@ SMART_TARGET_HOSTILE_SECOND_AGGRO
@ SMART_TARGET_OWNER_OR_SUMMONER
@ SMART_TARGET_SELF
@ SMART_TARGET_ACTION_INVOKER
@ SMART_TARGET_POSITION
@ SMART_TARGET_HOSTILE_LAST_AGGRO
@ SMART_TARGET_ACTION_INVOKER_VEHICLE
@ SMART_TARGET_FARTHEST
@ SMART_TARGET_THREAT_LIST
@ SMART_TARGET_CLOSEST_ENEMY
@ SMART_TARGET_NONE
@ SMART_TARGET_PLAYER_DISTANCE
@ SMART_TARGET_STORED
std::vector< WorldObject * > ObjectVector
@ SMART_SCRIPT_RESPAWN_CONDITION_AREA
@ SMART_SCRIPT_RESPAWN_CONDITION_MAP
#define sSmartScriptMgr
SMART_ACTION
@ SMART_ACTION_REMOVE_TIMED_EVENT
@ SMART_ACTION_NONE
@ SMART_ACTION_WP_RESUME
@ SMART_ACTION_UPDATE_TEMPLATE
@ SMART_ACTION_STORE_TARGET_LIST
@ SMART_ACTION_SET_HEALTH_REGEN
@ SMART_ACTION_ACTIVATE_GOBJECT
@ SMART_ACTION_FORCE_DESPAWN
@ SMART_ACTION_GAME_EVENT_START
@ SMART_ACTION_CREATE_CONVERSATION
@ SMART_ACTION_DESTROY_CONVERSATION
@ SMART_ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST
@ SMART_ACTION_REMOVE_UNIT_FIELD_BYTES_1
@ SMART_ACTION_SET_DISABLE_GRAVITY
@ SMART_ACTION_SET_INST_DATA64
@ SMART_ACTION_SET_FACTION
@ SMART_ACTION_THREAT_SINGLE_PCT
@ SMART_ACTION_OFFER_QUEST
@ SMART_ACTION_OVERRIDE_LIGHT
@ SMART_ACTION_SET_UNIT_FIELD_BYTES_1
@ SMART_ACTION_CLOSE_GOSSIP
@ SMART_ACTION_DISABLE_EVADE
@ SMART_ACTION_KILL_UNIT
@ SMART_ACTION_LOAD_EQUIPMENT
@ SMART_ACTION_ATTACK_START
@ SMART_ACTION_BECOME_PERSONAL_CLONE_FOR_PLAYER
@ SMART_ACTION_MOUNT_TO_ENTRY_OR_MODEL
@ SMART_ACTION_INVOKER_CAST
@ SMART_ACTION_SCENE_PLAY
@ SMART_ACTION_JUMP_TO_POS
@ SMART_ACTION_CALL_RANDOM_TIMED_ACTIONLIST
@ SMART_ACTION_SEND_GOSSIP_MENU
@ SMART_ACTION_SET_COUNTER
@ SMART_ACTION_FLEE_FOR_ASSIST
@ SMART_ACTION_EQUIP
@ SMART_ACTION_SET_ROOT
@ SMART_ACTION_ATTACK_STOP
@ SMART_ACTION_SUMMON_GO
@ SMART_ACTION_SET_HOVER
@ SMART_ACTION_WP_PAUSE
@ SMART_ACTION_SIMPLE_TALK
@ SMART_ACTION_CAST
@ SMART_ACTION_SPAWN_SPAWNGROUP
@ SMART_ACTION_ALLOW_COMBAT_MOVEMENT
@ SMART_ACTION_THREAT_ALL_PCT
@ SMART_ACTION_SOUND
@ SMART_ACTION_SET_MOVEMENT_SPEED
@ SMART_ACTION_PLAY_CINEMATIC
@ SMART_ACTION_ADD_NPC_FLAG
@ SMART_ACTION_EVADE
@ SMART_ACTION_FAIL_QUEST
@ SMART_ACTION_INTERRUPT_SPELL
@ SMART_ACTION_ENTER_VEHICLE
@ SMART_ACTION_ADD_POWER
@ SMART_ACTION_RANDOM_SOUND
@ SMART_ACTION_SET_IMMUNE_PC
@ SMART_ACTION_SCENE_CANCEL
@ SMART_ACTION_SET_POWER
@ SMART_ACTION_SEND_TARGET_TO_TARGET
@ SMART_ACTION_SET_INGAME_PHASE_ID
@ SMART_ACTION_REMOVE_POWER
@ SMART_ACTION_FALL
@ SMART_ACTION_GO_SET_GO_STATE
@ SMART_ACTION_MOVE_OFFSET
@ SMART_ACTION_REMOVE_ITEM
@ SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL
@ SMART_ACTION_RANDOM_PHASE
@ SMART_ACTION_SET_EMOTE_STATE
@ SMART_ACTION_CROSS_CAST
@ SMART_ACTION_WP_STOP
@ SMART_ACTION_GAME_EVENT_STOP
@ SMART_ACTION_CALL_KILLEDMONSTER
@ SMART_ACTION_TRIGGER_GAME_EVENT
@ SMART_ACTION_TALK
@ SMART_ACTION_CALL_SCRIPT_RESET
@ SMART_ACTION_SET_DATA
@ SMART_ACTION_PLAY_SPELL_VISUAL_KIT
@ SMART_ACTION_WP_START
@ SMART_ACTION_BOARD_PASSENGER
@ SMART_ACTION_COMBAT_STOP
@ SMART_ACTION_EXIT_VEHICLE
@ SMART_ACTION_SET_INGAME_PHASE_GROUP
@ SMART_ACTION_SET_RUN
@ SMART_ACTION_ACTIVATE_GAMEOBJECT
@ SMART_ACTION_ADD_TO_STORED_TARGET_LIST
@ SMART_ACTION_SET_HEALTH_PCT
@ SMART_ACTION_AUTO_ATTACK
@ SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL
@ SMART_ACTION_SET_VISIBILITY
@ SMART_ACTION_RANDOM_PHASE_RANGE
@ SMART_ACTION_GO_SET_LOOT_STATE
@ SMART_ACTION_CREDIT_QUEST_OBJECTIVE_TALK_TO
@ SMART_ACTION_SELF_CAST
@ SMART_ACTION_SET_INST_DATA
@ SMART_ACTION_PLAY_ANIMKIT
@ SMART_ACTION_CALL_FOR_HELP
@ SMART_ACTION_SET_UNINTERACTIBLE
@ SMART_ACTION_OVERRIDE_WEATHER
@ SMART_ACTION_ADD_THREAT
@ SMART_ACTION_TELEPORT
@ SMART_ACTION_PLAYMOVIE
@ SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT
@ SMART_ACTION_FOLLOW
@ SMART_ACTION_PLAY_EMOTE
@ SMART_ACTION_SET_EVENT_PHASE
@ SMART_ACTION_DESPAWN_SPAWNGROUP
@ SMART_ACTION_SET_CORPSE_DELAY
@ SMART_ACTION_SET_SHEATH
@ SMART_ACTION_SET_ORIENTATION
@ SMART_ACTION_RESPAWN_BY_SPAWNID
@ SMART_ACTION_SET_NPC_FLAG
@ SMART_ACTION_MOVE_TO_POS
@ SMART_ACTION_RANDOM_EMOTE
@ SMART_ACTION_INC_EVENT_PHASE
@ SMART_ACTION_ENABLE_TEMP_GOBJ
@ SMART_ACTION_CREATE_TIMED_EVENT
@ SMART_ACTION_DIE
@ SMART_ACTION_REMOVE_NPC_FLAG
@ SMART_ACTION_COMPLETE_QUEST
@ SMART_ACTION_SUMMON_CREATURE
@ SMART_ACTION_RESET_GOBJECT
@ SMART_ACTION_CALL_TIMED_ACTIONLIST
@ SMART_ACTION_SET_IN_COMBAT_WITH_ZONE
@ SMART_ACTION_DO_ACTION
@ SMART_ACTION_SET_HOME_POS
@ SMART_ACTION_ADD_ITEM
@ SMART_ACTION_SET_ACTIVE
@ SMART_ACTION_SET_RANGED_MOVEMENT
@ SMART_ACTION_ACTIVATE_TAXI
@ SMART_ACTION_START_CLOSEST_WAYPOINT
@ SMART_ACTION_SUMMON_CREATURE_GROUP
@ SMART_ACTION_SET_REACT_STATE
@ SMART_ACTION_RANDOM_MOVE
@ SMART_ACTION_SET_IMMUNE_NPC
@ SMART_ACTION_TRIGGER_TIMED_EVENT
@ SMART_ACTION_REMOVEAURASFROMSPELL
@ SMART_ACTION_PAUSE_MOVEMENT
SMART_EVENT
@ SMART_EVENT_IC_LOS
@ SMART_EVENT_EVADE
@ SMART_EVENT_ACTION_DONE
@ SMART_EVENT_SUMMON_DESPAWNED
@ SMART_EVENT_SPELLHIT
@ SMART_EVENT_SCENE_START
@ SMART_EVENT_RECEIVE_EMOTE
@ SMART_EVENT_FRIENDLY_HEALTH_PCT
@ SMART_EVENT_QUEST_FAIL
@ SMART_EVENT_ON_AURA_APPLIED
@ SMART_EVENT_DATA_SET
@ SMART_EVENT_RECEIVE_HEAL
@ SMART_EVENT_TIMED_EVENT_TRIGGERED
@ SMART_EVENT_SCENE_COMPLETE
@ SMART_EVENT_QUEST_COMPLETION
@ SMART_EVENT_JUST_CREATED
@ SMART_EVENT_HEALTH_PCT
@ SMART_EVENT_DISTANCE_GAMEOBJECT
@ SMART_EVENT_ON_SPELLCLICK
@ SMART_EVENT_MOVEMENTINFORM
@ SMART_EVENT_RANGE
@ SMART_EVENT_MANA_PCT
@ SMART_EVENT_PASSENGER_REMOVED
@ SMART_EVENT_ON_AURA_REMOVED
@ SMART_EVENT_INSTANCE_PLAYER_ENTER
@ SMART_EVENT_LINK
@ SMART_EVENT_WAYPOINT_PAUSED
@ SMART_EVENT_REACHED_HOME
@ SMART_EVENT_TRANSPORT_ADDCREATURE
@ SMART_EVENT_REWARD_QUEST
@ SMART_EVENT_GO_EVENT_INFORM
@ SMART_EVENT_AREATRIGGER_EXIT
@ SMART_EVENT_GO_LOOT_STATE_CHANGED
@ SMART_EVENT_UPDATE_IC
@ SMART_EVENT_RESET
@ SMART_EVENT_SCENE_CANCEL
@ SMART_EVENT_JUST_SUMMONED
@ SMART_EVENT_CHARMED
@ SMART_EVENT_AI_INIT
@ SMART_EVENT_ON_SPELL_CAST
@ SMART_EVENT_SPELLHIT_TARGET
@ SMART_EVENT_GAME_EVENT_START
@ SMART_EVENT_KILL
@ SMART_EVENT_TRANSPORT_REMOVE_PLAYER
@ SMART_EVENT_GOSSIP_HELLO
@ SMART_EVENT_GOSSIP_SELECT
@ SMART_EVENT_CORPSE_REMOVED
@ SMART_EVENT_PASSENGER_BOARDED
@ SMART_EVENT_SCENE_TRIGGER
@ SMART_EVENT_UPDATE
@ SMART_EVENT_AREATRIGGER_ENTER
@ SMART_EVENT_TRANSPORT_ADDPLAYER
@ SMART_EVENT_WAYPOINT_ENDED
@ SMART_EVENT_UPDATE_OOC
@ SMART_EVENT_ACCEPTED_QUEST
@ SMART_EVENT_COUNTER_SET
@ SMART_EVENT_FRIENDLY_MISSING_BUFF
@ SMART_EVENT_WAYPOINT_RESUMED
@ SMART_EVENT_ON_SPELL_FAILED
@ SMART_EVENT_WAYPOINT_REACHED
@ SMART_EVENT_TARGET_BUFFED
@ SMART_EVENT_RESPAWN
@ SMART_EVENT_QUEST_ACCEPTED
@ SMART_EVENT_QUEST_REWARDED
@ SMART_EVENT_TEXT_OVER
@ SMART_EVENT_DEATH
@ SMART_EVENT_TRANSPORT_RELOCATE
@ SMART_EVENT_GAME_EVENT_END
@ SMART_EVENT_DAMAGED
@ SMART_EVENT_FOLLOW_COMPLETED
@ SMART_EVENT_QUEST_OBJ_COMPLETION
@ SMART_EVENT_DISTANCE_CREATURE
@ SMART_EVENT_WAYPOINT_STOPPED
@ SMART_EVENT_SUMMONED_UNIT_DIES
@ SMART_EVENT_FRIENDLY_IS_CC
@ SMART_EVENT_SEND_EVENT_TRIGGER
@ SMART_EVENT_OOC_LOS
@ SMART_EVENT_ON_SPELL_START
@ SMART_EVENT_ON_DESPAWN
@ SMART_EVENT_AGGRO
@ SMART_EVENT_VICTIM_CASTING
@ SMART_EVENT_DAMAGED_TARGET
@ SMART_EVENT_HAS_AURA
@ SMART_EVENT_SUMMONED_UNIT
SmartActionSummonCreatureFlags
@ SMART_ESCORT_TARGETS
@ SMARTAI_SPAWN_FLAG_FORCE_SPAWN
@ SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN
@ SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN
@ SMARTCAST_TRIGGERED
@ SMARTCAST_COMBAT_MOVE
@ SMARTCAST_INTERRUPT_PREVIOUS
@ SMARTCAST_AURA_NOT_PRESENT
@ SMARTCAST_WAIT_FOR_HIT
@ SMART_EVENT_PHASE_12
SpawnObjectType
Definition SpawnData.h:35
@ AURA_REMOVE_BY_EXPIRE
TriggerCastFlags
@ TRIGGERED_FULL_MASK
Used when doing CastSpell with triggered == true.
#define sSpellMgr
Definition SpellMgr.h:812
#define CAST_AI(a, b)
Definition UnitAI.h:29
#define ENSURE_AI(a, b)
Definition UnitAI.h:30
UnitMoveType
ReactStates
UnitStandStateType
Definition UnitDefines.h:41
@ UNIT_STAND_STATE_STAND
Definition UnitDefines.h:42
NPCFlags
Non Player Character flags.
SheathState
Definition UnitDefines.h:81
#define MAX_EQUIPMENT_ITEMS
Definition UnitDefines.h:37
AnimTier
Definition UnitDefines.h:69
UnitVisFlags
Definition UnitDefines.h:58
@ UNIT_FLAG_IMMUNE_TO_NPC
@ UNIT_FLAG_IMMUNE_TO_PC
@ CURRENT_GENERIC_SPELL
Definition Unit.h:598
@ UNIT_STATE_LOST_CONTROL
Definition Unit.h:302
@ UNIT_STATE_ROOT
Definition Unit.h:271
@ UNIT_STATE_CASTING
Definition Unit.h:276
#define sWaypointMgr
static float waypoint[6][3]
Definition boss_alar.cpp:54
bool IsCustom() const
bool IsUnit() const
Definition BaseEntity.h:171
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
bool IsCreature() const
Definition BaseEntity.h:172
bool IsGameObject() const
Definition BaseEntity.h:174
bool IsPlayer() const
Definition BaseEntity.h:173
TypeID GetTypeId() const
Definition BaseEntity.h:166
static Conversation * CreateConversation(uint32 conversationEntry, Unit *creator, Position const &pos, ObjectGuid privateObjectOwner, SpellInfo const *spellInfo=nullptr, bool autoStart=true)
void DoZoneInCombat()
Definition CreatureAI.h:169
virtual void EnterEvadeMode(EvadeReason why=EvadeReason::Other)
void AttackStart(Unit *victim) override
== Triggered Actions Requested ==================
static void SendChatPacket(WorldObject *source, Builder const &builder, ChatMsg msgType, WorldObject const *whisperTarget=nullptr, CreatureTextRange range=TEXT_RANGE_NORMAL, Team team=TEAM_OTHER, bool gmOnly=false)
void SetHomePosition(float x, float y, float z, float o)
Definition Creature.h:386
void SetCanMelee(bool canMelee, bool fleeFromMelee=false)
void CallForHelp(float fRadius)
void GetHomePosition(float &x, float &y, float &z, float &ori) const
Definition Creature.h:388
void GetTransportHomePosition(float &x, float &y, float &z, float &ori) const
Definition Creature.h:393
bool IsEngaged() const override
GuidUnorderedSet const & GetTapList() const
Definition Creature.h:304
void DoFleeToGetAssistance()
ObjectGuid::LowType GetSpawnId() const
Definition Creature.h:110
Position GetRespawnPosition(float *dist=nullptr) const
void SetDisplayId(uint32 displayId, bool setNative=false) override
Unit * SelectNearestTarget(float dist=0, bool playerOnly=false) const
bool IsInEvadeMode() const
Definition Creature.h:217
CreatureAI * AI() const
Definition Creature.h:228
virtual void SetData(uint32, uint32)
ObjectGuid GetOwnerGUID() const override
Definition GameObject.h:245
GameObjectAI * AI() const
Definition GameObject.h:384
ObjectGuid::LowType GetSpawnId() const
Definition GameObject.h:212
Definition Group.h:205
virtual bool SetBossState(uint32 id, EncounterState state)
static char const * GetBossStateName(uint8 state)
Definition Map.h:225
bool SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn=false, bool force=false, std::vector< WorldObject * > *spawnedObjects=nullptr)
Definition Map.cpp:2350
TempSummon * SummonCreature(uint32 entry, Position const &pos, SummonPropertiesEntry const *properties=nullptr, Milliseconds duration=0ms, WorldObject *summoner=nullptr, uint32 spellId=0, uint32 vehId=0, ObjectGuid privateObjectOwner=ObjectGuid::Empty, SmoothPhasingInfo const *smoothPhasingInfo=nullptr)
Definition Object.cpp:1186
Difficulty GetDifficultyID() const
Definition Map.h:360
GameObjectBySpawnIdContainer & GetGameObjectBySpawnIdStore()
Definition Map.h:465
bool SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes=false, size_t *count=nullptr)
Definition Map.cpp:2439
CreatureBySpawnIdContainer & GetCreatureBySpawnIdStore()
Definition Map.h:461
void Respawn(RespawnInfo *info, CharacterDatabaseTransaction dbTrans=nullptr)
Definition Map.cpp:2060
void MoveJump(uint32 id, Position const &pos, std::variant< std::monostate, float, Milliseconds > speedOrTime={}, Optional< float > minHeight={}, Optional< float > maxHeight={}, MovementFacingTarget const &facing={}, bool orientationFixed=false, bool unlimitedSpeed=false, Optional< float > speedMultiplier={}, JumpArrivalCastArgs const *arrivalCast=nullptr, Movement::SpellEffectExtraData const *spellEffectExtraData=nullptr, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
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={})
static ObjectGuid const Empty
Definition ObjectGuid.h:314
bool IsEmpty() const
Definition ObjectGuid.h:362
uint64 LowType
Definition ObjectGuid.h:321
void Clear()
Definition ObjectGuid.h:329
static CreatureModel const * ChooseDisplayId(CreatureTemplate const *cinfo, CreatureData const *data=nullptr)
Player * ToPlayer()
Definition Object.h:126
AreaTrigger * ToAreaTrigger()
Definition Object.h:146
GameObject * ToGameObject()
Definition Object.h:131
uint32 GetEntry() const
Definition Object.h:89
Creature * ToCreature()
Definition Object.h:121
Unit * ToUnit()
Definition Object.h:116
static void AddPhase(WorldObject *object, uint32 phaseId, bool updateVisibility)
static void AddPhaseGroup(WorldObject *object, uint32 phaseGroupId, bool updateVisibility)
static void RemovePhaseGroup(WorldObject *object, uint32 phaseGroupId, bool updateVisibility)
static PhaseShift const & GetAlwaysVisiblePhaseShift()
static void RemovePhase(WorldObject *object, uint32 phaseId, bool updateVisibility)
void KilledMonsterCredit(uint32 entry, ObjectGuid guid=ObjectGuid::Empty)
Definition Player.cpp:16679
void TalkedToCreature(uint32 entry, ObjectGuid guid)
Definition Player.cpp:16708
void CompleteQuest(uint32 quest_id)
Definition Player.cpp:14936
void AddQuestAndCheckCompletion(Quest const *quest, Object *questGiver)
Definition Player.cpp:14676
bool CanTakeQuest(Quest const *quest, bool msg) const
Definition Player.cpp:14522
void AreaExploredOrEventHappens(uint32 questId)
Definition Player.cpp:16552
QuestStatus GetQuestStatus(uint32 quest_id) const
Definition Player.cpp:15962
Group * GetGroup(Optional< uint8 > partyIndex)
Definition Player.h:2796
void PrepareGossipMenu(WorldObject *source, uint32 menuId, bool showQuests=false)
Definition Player.cpp:13927
uint32 GetGossipTextId(uint32 menuId, WorldObject *source)
Definition Player.cpp:14269
std::unique_ptr< PlayerMenu > PlayerTalkClass
Definition Player.h:2570
uint32 GetQuestId() const
Definition QuestDef.h:637
bool HasFlag(QuestFlags flag) const
Definition QuestDef.h:619
ReferenceType * front()
Definition RefManager.h:32
virtual bool IsReady() const noexcept
static ActionResultSetter< T > GetResultSetter(std::shared_ptr< ActionResult > action)
static SmartScriptHolder & FindLinkedEvent(SmartAIEventList &list, uint32 link)
void SetInvincibilityHpLevel(uint32 level)
Definition SmartAI.h:204
uint32 mEventPhase
static constexpr uint32 MAX_NESTED_EVENTS
void DoFindFriendlyMissingBuff(std::vector< Creature * > &creatures, float range, uint32 spellid) const
Creature * me
uint32 mPathId
ObjectGuid mLastInvoker
Definition SmartScript.h:99
void ProcessTimedAction(SmartScriptHolder &e, uint32 const &min, uint32 const &max, Unit *unit=nullptr, uint32 var0=0, uint32 var1=0, bool bvar=false, SpellInfo const *spell=nullptr, GameObject *gob=nullptr, std::string_view varString={ })
void OnUpdate(const uint32 diff)
void IncPhase(uint32 p)
void DoFindFriendlyCC(std::vector< Creature * > &creatures, float range) const
SmartScriptType mScriptType
void UpdateTimer(SmartScriptHolder &e, uint32 const diff)
CounterMap mCounterList
bool mUseTextTimer
static SmartScriptHolder CreateSmartEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, uint32 event_param5, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, uint32 action_param7, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 target_param4, std::string_view targetParamString, uint32 phaseMask)
void SortEvents(SmartAIEventList &events)
uint32 mLastTextID
SceneTemplate const * sceneTemplate
static void RecalcTimer(SmartScriptHolder &e, uint32 min, uint32 max)
void OnInitialize(WorldObject *obj, AreaTriggerEntry const *at=nullptr, SceneTemplate const *scene=nullptr, Quest const *qst=nullptr, uint32 evnt=0)
void FillScript(SmartAIEventList &&e, WorldObject *obj, AreaTriggerEntry const *at, SceneTemplate const *scene, Quest const *quest, uint32 event=0)
uint32 GetCounterValue(uint32 id) const
void StoreTargetList(ObjectVector const &targets, uint32 id)
void AddToStoredTargetList(ObjectVector const &targets, uint32 id)
void ProcessAction(SmartScriptHolder &e, Unit *unit=nullptr, uint32 var0=0, uint32 var1=0, bool bvar=false, SpellInfo const *spell=nullptr, GameObject *gob=nullptr, std::string_view varString={ })
uint32 mTextTimer
void RetryLater(SmartScriptHolder &e, bool ignoreChanceRoll=false)
SmartAIEventStoredList mStoredEvents
void SetPhase(uint32 p)
void RaisePriority(SmartScriptHolder &e)
Unit * GetLastInvoker(Unit *invoker=nullptr) const
void OnMoveInLineOfSight(Unit *who)
bool IsSmart(Creature *c, bool silent=false) const
WorldObject * GetBaseObjectOrUnitInvoker(Unit *invoker)
Unit * DoSelectLowestHpFriendly(float range, uint32 MinHPDiff) const
GameObject * go
bool IsInPhase(uint32 p) const
static void InitTimer(SmartScriptHolder &e)
AreaTriggerEntry const * trigger
void ProcessEvent(SmartScriptHolder &e, Unit *unit=nullptr, uint32 var0=0, uint32 var1=0, bool bvar=false, SpellInfo const *spell=nullptr, GameObject *gob=nullptr, std::string_view varString={ })
Unit * DoSelectLowestHpPercentFriendly(float range, uint32 minHpPct, uint32 maxHpPct) const
uint32 mAllEventFlags
SmartScript & operator=(SmartScript const &other)
Player * player
AreaTrigger * areaTrigger
void StoreCounter(uint32 id, uint32 value, uint32 reset)
uint32 mCurrentPriority
WorldObject * GetBaseObject() const
uint32 mNestedEventsCounter
Creature * FindCreatureNear(WorldObject *searchObject, ObjectGuid::LowType guid) const
uint32 mTalkerEntry
void GetTargets(ObjectVector &targets, SmartScriptHolder const &e, WorldObject *invoker=nullptr) const
SmartAIEventList mEvents
Quest const * quest
std::shared_ptr< Scripting::v2::ActionBase > mTimedActionWaitEvent
bool isProcessingTimedActionList
ObjectVector const * GetStoredTargetVector(uint32 id, WorldObject const &ref) const
Unit * DoFindClosestFriendlyInRange(float range, bool playerOnly) const
ObjectGuid goOrigGUID
SmartAIEventList mTimedActionList
void DecPhase(uint32 p)
ObjectGuid meOrigGUID
ObjectVectorMap _storedTargets
void ClearTargetList(uint32 id)
bool mEventSortingRequired
ObjectGuid mTimedActionListInvoker
void RemoveStoredEvent(uint32 id)
void SetTimedActionList(SmartScriptHolder &e, uint32 entry, Unit *invoker, uint32 startFromEventId=0)
std::vector< uint32 > mRemIDs
GameObject * FindGameObjectNear(WorldObject *searchObject, ObjectGuid::LowType guid) const
void ResetBaseObject()
void ProcessEventsFor(SMART_EVENT e, Unit *unit=nullptr, uint32 var0=0, uint32 var1=0, bool bvar=false, SpellInfo const *spell=nullptr, GameObject *gob=nullptr, std::string_view varString={ })
uint32 const Id
Definition SpellInfo.h:328
uint32 SchoolMask
Definition SpellInfo.h:419
Definition Spell.h:277
Trinity::IteratorPair< ThreatListIterator, std::nullptr_t > GetUnsortedThreatList() const
void ModifyThreatByPercent(Unit *target, float percent)
void AddThreat(Unit *target, float amount, SpellInfo const *spell=nullptr, bool ignoreModifiers=false, bool ignoreRedirects=false)
== AFFECT MY THREAT LIST ==
std::vector< ThreatReference * > GetModifiableThreatList()
Utility class to enable range for loop syntax for multimap.equal_range uses.
virtual void SetData(uint32 id, uint32 value)
Definition UnitAI.h:75
Unit * SelectTarget(SelectTargetMethod targetType, uint32 offset=0, float dist=0.0f, bool playerOnly=false, bool withTank=true, int32 aura=0)
Definition UnitAI.cpp:79
Definition Unit.h:635
void EnterVehicle(Unit *base, int8 seatId=-1)
Definition Unit.cpp:12749
bool IsVehicle() const
Definition Unit.h:754
bool IsCharmed() const
Definition Unit.h:1236
Vehicle * GetVehicle() const
Definition Unit.h:1784
float GetHealthPct() const
Definition Unit.h:796
void CombatStop(bool includingCast=false, bool mutualPvP=true, bool(*unitFilter)(Unit const *otherUnit)=nullptr)
Definition Unit.cpp:6012
bool CanHaveThreatList() const
====================== THREAT & COMBAT ====================
Definition Unit.h:1030
ThreatManager & GetThreatManager()
Definition Unit.h:1078
bool IsWithinCombatRange(Unit const *obj, float dist2compare) const
Definition Unit.cpp:670
void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid=0, bool withInstant=true)
Definition Unit.cpp:3231
MotionMaster * GetMotionMaster()
Definition Unit.h:1723
void Dismount()
Definition Unit.cpp:8317
bool IsNonMeleeSpellCast(bool withDelayed, bool skipChanneled=false, bool skipAutorepeat=false, bool isAutoshoot=false, bool skipInstant=true) const
Definition Unit.cpp:3201
void SetFacingToObject(WorldObject const *object, bool force=true)
Definition Unit.cpp:13307
bool IsAlive() const
Definition Unit.h:1185
int32 GetMaxPower(Powers power) const
Definition Unit.cpp:10037
TempSummon * ToTempSummon()
Definition Unit.h:1828
ObjectGuid GetCharmerOrOwnerGUID() const override
Definition Unit.h:1216
ObjectGuid GetCreatorGUID() const override
Definition Unit.h:1193
void Mount(uint32 mount, uint32 vehicleId=0, uint32 creatureEntry=0)
Definition Unit.cpp:8284
void SetSheath(SheathState sheathed)
Definition Unit.cpp:5813
Gender GetGender() const
Definition Unit.h:767
Unit * EnsureVictim() const
Definition Unit.h:728
bool IsAIEnabled() const
Definition Unit.h:666
uint32 GetAuraCount(uint32 spellId) const
Definition Unit.cpp:4788
uint64 GetMaxHealth() const
Definition Unit.h:789
Aura * GetAura(uint32 spellId, ObjectGuid casterGUID=ObjectGuid::Empty, ObjectGuid itemCasterGUID=ObjectGuid::Empty, uint32 reqEffMask=0) const
Definition Unit.cpp:4700
TransportBase * GetDirectTransport() const
Returns the transport this unit is on directly (if on vehicle and transport, return vehicle)
Definition Unit.cpp:12151
Unit * GetVictim() const
Definition Unit.h:726
void SetFacingTo(float const ori, bool force=true)
Definition Unit.cpp:13289
bool HasUnitState(const uint32 f) const
Definition Unit.h:743
float GetPowerPct(Powers power) const
Definition Unit.h:820
void RemoveAllAuras()
Definition Unit.cpp:4382
Vehicle * GetVehicleKit() const
Definition Unit.h:1782
void KillSelf(bool durabilityLoss=true, bool skipSettingDeathState=false)
Definition Unit.h:936
void DeMorph()
Definition Unit.cpp:3382
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
bool isDead() const
Definition Unit.h:1187
Spell * GetCurrentSpell(CurrentSpellTypes spellType) const
Definition Unit.h:1466
Unit * GetBase() const
Definition Vehicle.h:49
SeatMap Seats
The collection of all seats on the vehicle. Including vacant ones.
Definition Vehicle.h:67
TempSummon * SummonPersonalClone(Position const &pos, TempSummonType despawnType=TEMPSUMMON_MANUAL_DESPAWN, Milliseconds despawnTime=0s, uint32 vehId=0, uint32 spellId=0, Player *privateObjectOwner=nullptr)
Definition Object.cpp:1421
void GetPlayerListInGrid(Container &playerContainer, float maxSearchRange, bool alive=true) const
Definition Object.cpp:2678
Player * SelectNearestPlayer(float range) const
Definition Object.cpp:1579
Map * GetMap() const
Definition Object.h:411
void GetGameObjectListWithOptionsInGrid(Container &gameObjectContainer, float maxSearchRange, FindGameObjectOptions const &options) const
Definition Object.cpp:2646
Creature * FindNearestCreatureWithOptions(float range, FindCreatureOptions const &options) const
Definition Object.cpp:1526
InstanceScript * GetInstanceScript() const
Definition Object.cpp:396
void GetCreatureListWithEntryInGrid(Container &creatureContainer, uint32 entry, float maxSearchRange=250.0f) const
Definition Object.cpp:2658
ObjectGuid GetPrivateObjectOwner() const
Definition Object.h:569
void GetGameObjectListWithEntryInGrid(Container &gameObjectContainer, uint32 entry, float maxSearchRange=250.0f) const
Definition Object.cpp:2638
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition Object.cpp:2217
bool IsHostileTo(WorldObject const *target) const
Definition Object.cpp:2181
virtual ObjectGuid GetCharmerOrOwnerGUID() const
Definition Object.h:439
TransportBase * GetTransport() const
Definition Object.h:537
bool IsPrivateObject() const
Definition Object.h:568
GameObject * SummonGameObject(uint32 entry, Position const &pos, QuaternionData const &rot, Seconds respawnTime, GOSummonType summonType=GO_SUMMON_TIMED_OR_CORPSE_DESPAWN)
Definition Object.cpp:1441
std::string const & GetName() const
Definition Object.h:342
bool IsWithinLOSInMap(WorldObject const *obj, LineOfSightChecks checks=LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags ignoreFlags=VMAP::ModelIgnoreFlags::Nothing) const
Definition Object.cpp:535
void GetContactPoint(WorldObject const *obj, float &x, float &y, float &z, float distance2d=CONTACT_DISTANCE) const
Definition Object.cpp:2776
GameObject * FindNearestGameObjectWithOptions(float range, FindGameObjectOptions const &options) const
Definition Object.cpp:1548
GameObject * FindNearestUnspawnedGameObject(uint32 entry, float range) const
Definition Object.cpp:1561
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition Object.cpp:501
bool IsInRange(WorldObject const *obj, float minRange, float maxRange, bool is3D=true) const
Definition Object.cpp:592
void GetCreatureListWithOptionsInGrid(Container &creatureContainer, float maxSearchRange, FindCreatureOptions const &options) const
Definition Object.cpp:2666
bool IsWithinDist(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition Object.cpp:496
void SummonCreatureGroup(uint8 group, std::list< TempSummon * > *list=nullptr)
Definition Object.cpp:1507
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2186
virtual void SetData(uint32, uint32)
Definition ZoneScript.h:100
virtual void SetGuidData(uint32, ObjectGuid)
Definition ZoneScript.h:92
WeatherState
Definition Weather.h:46
TC_GAME_API void Trigger(uint32 gameEventId, WorldObject *source, WorldObject *target)
TimePoint Now()
Current chrono steady_clock time point.
Definition GameTime.cpp:67
TC_GAME_API WorldObject * GetWorldObject(WorldObject const &, ObjectGuid const &)
TC_GAME_API Unit * GetUnit(WorldObject const &, ObjectGuid const &guid)
TC_GAME_API GameObject * GetGameObject(WorldObject const &u, ObjectGuid const &guid)
TC_GAME_API Player * GetPlayer(Map const *, ObjectGuid const &guid)
TC_GAME_API Creature * GetCreature(WorldObject const &u, ObjectGuid const &guid)
constexpr void EraseIf(Container &c, Predicate p)
Definition Containers.h:283
auto SelectRandomContainerElement(C const &container) -> std::add_const_t< decltype(*std::ranges::begin(container))> &
Definition Containers.h:110
void RandomResize(C &container, std::size_t requestedSize)
Definition Containers.h:67
struct advstd::ranges::Contains contains
TriggerCastFlags TriggerFlags
CastSpellExtraArgs & SetScriptWaitsForSpellHit(bool scriptWaitsForSpellHit)
CastSpellExtraArgs & SetScriptResult(Scripting::v2::ActionResultSetter< SpellCastResult > &&scriptResult)
static void VisitAllObjects(WorldObject const *obj, T &visitor, float radius, bool dont_load=true)
Definition CellImpl.h:203
static void VisitGridObjects(WorldObject const *obj, T &visitor, float radius, bool dont_load=true)
Definition CellImpl.h:179
uint32 CreatureDisplayID
EquipmentItem Items[MAX_EQUIPMENT_ITEMS]
constexpr float GetPositionX() const
Definition Position.h:87
constexpr float GetPositionY() const
Definition Position.h:88
Position GetPositionWithOffset(Position const &offset) const
Definition Position.cpp:61
constexpr void GetPosition(float &x, float &y) const
Definition Position.h:92
constexpr float GetOrientation() const
Definition Position.h:90
constexpr float GetPositionZ() const
Definition Position.h:89
static QuaternionData fromEulerAnglesZYX(float Z, float Y, float X)
uint32 SceneId
Definition ObjectMgr.h:856
std::string param_string
uint32 sounds[4]
uint32 targetParam2
struct SmartAction::@61::@148 scene
struct SmartAction::@61::@142 groupSpawn
struct SmartAction::@61::@70 questOffer
uint32 targetsLimit
struct SmartAction::@61::@112 timeEvent
struct SmartAction::@61::@129 setRangedMovement
struct SmartAction::@61::@116 setunitByte
struct SmartAction::@61::@168 raw
struct SmartAction::@61::@66 morphOrMount
struct SmartAction::@61::@165 doAction
struct SmartAction::@61::@125 moveToPos
struct SmartAction::@61::@164 triggerGameEvent
uint32 gameObjectAction
uint32 targetParam4
struct SmartAction::@61::@63 talk
struct SmartAction::@61::@105 wpStop
struct SmartAction::@61::@97 moveRandom
struct SmartAction::@61::@132 goState
struct SmartAction::@61::@161 activateGameObject
struct SmartAction::@61::@92 forceDespawn
struct SmartAction::@61::@160 setUninteractible
SAIBool regenHealth
SAIBool allowOverride
struct SmartAction::@61::@121 interruptSpellCasting
struct SmartAction::@61::@98 visibility
struct SmartAction::@61::@77 threat
struct SmartAction::@61::@158 setImmunePC
struct SmartAction::@61::@64 simpleTalk
struct SmartAction::@61::@71 react
struct SmartAction::@61::@76 threatPCT
uint32 triggerFlags
uint32 emotes[SMART_ACTION_PARAM_COUNT]
SAIBool withDelayed
struct SmartAction::@61::@79 combatMove
struct SmartAction::@61::@103 wpStart
struct SmartAction::@61::@84 randomPhase
SAIBool withInstant
uint32 transitionMilliseconds
struct SmartAction::@61::@140 corpseDelay
struct SmartAction::@61::@81 incEventPhase
struct SmartAction::@61::@118 timedActionList
struct SmartAction::@61::@72 randomEmote
struct SmartAction::@61::@82 removeAura
struct SmartAction::@61::@95 ingamePhaseGroup
struct SmartAction::@61::@141 disableEvade
uint32 targetParam1
struct SmartAction::@61::@99 summonGO
uint32 includeDecayRatio
struct SmartAction::@61::@128 sendTargetToTarget
struct SmartAction::@61::@138 moveOffset
SAIBool toRespawnPosition
struct SmartAction::@61::@75 summonCreature
struct SmartAction::@61::@150 movementSpeed
SAIBool useTalkTarget
struct SmartAction::@61::@93 invincHP
SAIBool uninteractible
uint32 targetParam3
struct SmartAction::@61::@123 fleeAssist
struct SmartAction::@61::@65 faction
struct SmartAction::@61::@120 randRangeTimedActionList
struct SmartAction::@61::@149 cinematic
uint32 spellVisualKitId
struct SmartAction::@61::@122 jump
struct SmartAction::@61::@156 setHealthPct
struct SmartAction::@61::@154 setHover
struct SmartAction::@61::@94 ingamePhaseId
struct SmartAction::@61::@155 evade
struct SmartAction::@61::@96 setData
struct SmartAction::@61::@133 creatureGroup
struct SmartAction::@61::@113 movie
struct SmartAction::@61::@85 randomPhaseRange
struct SmartAction::@61::@111 storeTargets
struct SmartAction::@61::@153 overrideWeather
uint32 FadeObjectDuration
struct SmartAction::@61::@124 enableTempGO
struct SmartAction::@61::@74 crossCast
SAIBool updateLevel
uint32 actionLists[SMART_ACTION_PARAM_COUNT]
SMART_ACTION type
uint32 forceRespawnTimer
struct SmartAction::@61::@100 active
struct SmartAction::@61::@157 conversation
uint32 phases[SMART_ACTION_PARAM_COUNT]
struct SmartAction::@61::@162 addToStoredTargets
struct SmartAction::@61::@130 setHealthRegen
struct SmartAction::@61::@163 becomePersonalClone
struct SmartAction::@61::@126 sendGossipMenu
struct SmartAction::@61::@131 setRoot
SAIBool onlyOwnedAuras
struct SmartAction::@61::@90 callHelp
uint32 overrideLightId
struct SmartAction::@61::@139 randomSound
struct SmartAction::@61::@108 setDisableGravity
struct SmartAction::@61::@104 wpPause
struct SmartAction::@61::@106 item
struct SmartAction::@61::@146 respawnData
uint32 gossipNpcTextId
struct SmartAction::@61::@143 loadEquipment
SAIBool disablePathfinding
struct SmartAction::@61::@166 enterVehicle
uint32 ContactDistance
struct SmartAction::@61::@134 power
struct SmartAction::@61::@110 setCounter
struct SmartAction::@61::@78 autoAttack
struct SmartAction::@61::@159 setImmuneNPC
struct SmartAction::@61::@144 randomTimedEvent
struct SmartAction::@61::@152 overrideLight
SAIBool withEmote
SAIBool useSaiTargetAsGameEventSource
struct SmartAction::@61::@89 updateTemplate
struct SmartAction::@61::@87 setInstanceData
struct SmartAction::@61::@119 randTimedActionList
SAIBool directAdd
uint32 storedTargetId
struct SmartAction::@61::@114 equip
struct SmartAction::@61::@86 killedMonster
uint32 createdBySpell
struct SmartAction::@61::@136 gameEventStart
struct SmartAction::@61::@107 setRun
struct SmartAction::@61::@145 pauseMovement
struct SmartAction::@61::@167 destroyConversation
struct SmartAction::@61::@109 teleport
struct SmartAction::@61::@88 setInstanceData64
struct SmartAction::@61::@91 setSheath
struct SmartAction::@61::@73 cast
struct SmartAction::@61::@135 gameEventStop
struct SmartAction::@61::@127 setGoLootState
struct SmartAction::@61::@83 follow
struct SmartAction::@61::@80 setEventPhase
struct SmartAction::@61::@151 spellVisualKit
struct SmartEvent::@25::@48 textOver
uint32 event_flags
struct SmartEvent::@25::@34 friendlyCC
struct SmartEvent::@25::@52 gameEvent
struct SmartEvent::@25::@50 gossipHello
uint32 event_phase_mask
struct SmartEvent::@25::@56 friendlyHealthPct
struct SmartEvent::@25::@40 aura
struct SmartEvent::@25::@33 targetCasting
struct SmartEvent::@25::@49 timedEvent
struct SmartEvent::@25::@44 waypoint
std::string param_string
uint32 cooldownMax
struct SmartEvent::@25::@47 instancePlayerEnter
struct SmartEvent::@25::@54 eventInform
struct SmartEvent::@25::@42 movementInform
struct SmartEvent::@25::@60 raw
struct SmartEvent::@25::@31 respawn
struct SmartEvent::@25::@53 goLootStateChanged
struct SmartEvent::@25::@58 counter
struct SmartEvent::@25::@59 spellCast
struct SmartEvent::@25::@51 gossip
struct SmartEvent::@25::@28 kill
uint32 hostilityMode
Hostility mode of the event. 0: hostile, 1: not hostile, 2: any.
struct SmartEvent::@25::@46 transportRelocate
uint32 event_chance
SAIBool playerOnly
struct SmartEvent::@25::@45 transportAddCreature
struct SmartEvent::@25::@32 minMax
struct SmartEvent::@25::@30 los
uint32 creatureEntry
struct SmartEvent::@25::@36 summoned
struct SmartEvent::@25::@35 missingBuff
struct SmartEvent::@25::@38 questObjective
uint32 gameEventId
struct SmartEvent::@25::@55 doAction
uint32 textGroupID
uint32 cooldownMin
struct SmartEvent::@25::@29 spellHit
struct SmartEvent::@25::@41 charm
struct SmartEvent::@25::@57 distance
struct SmartEvent::@25::@27 minMaxRepeat
SAIBool onRemove
struct SmartEvent::@25::@43 dataSet
SMART_EVENT type
static constexpr uint32 DEFAULT_PRIORITY
uint32 GetScriptType() const
uint32 GetEventType() const
uint32 GetTargetType() const
SmartScriptType source_type
uint32 GetActionType() const
struct SmartTarget::@169::@186 owner
struct SmartTarget::@169::@175 unitDistance
std::string param_string
struct SmartTarget::@169::@176 playerDistance
SAIBool useCharmerOrOwner
struct SmartTarget::@169::@178 stored
struct SmartTarget::@169::@185 closestFriendly
struct SmartTarget::@169::@187 vehicle
struct SmartTarget::@169::@184 closestAttackable
struct SmartTarget::@169::@180 goGUID
struct SmartTarget::@169::@172 farthest
struct SmartTarget::@169::@177 playerRange
struct SmartTarget::@169::@183 goClosest
struct SmartTarget::@169::@182 unitClosest
struct SmartTarget::@169::@189 raw
struct SmartTarget::@169::@179 goRange
SMARTAI_TARGETS type
struct SmartTarget::@169::@171 hostilRandom
struct SmartTarget::@169::@188 threatList
struct SmartTarget::@169::@173 unitRange
uint32 findCreatureAliveState
struct SmartTarget::@169::@174 unitGUID
struct SmartTarget::@169::@181 goDistance
std::vector< WaypointNode > Nodes