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
1617
1618 ev.target = e.target;
1619
1620 InitTimer(ev);
1621 break;
1622 }
1624 {
1626
1627 // remove this event if not repeatable
1629 mRemIDs.push_back(e.action.timeEvent.id);
1630 break;
1631 }
1633 {
1634 mRemIDs.push_back(e.action.timeEvent.id);
1635 break;
1636 }
1638 {
1639 SetPhase(0);
1640 OnReset();
1641 break;
1642 }
1644 {
1645 if (!IsSmart())
1646 break;
1647
1648 float attackDistance = float(e.action.setRangedMovement.distance);
1649 float attackAngle = float(e.action.setRangedMovement.angle) / 180.0f * float(M_PI);
1650
1651 for (WorldObject* target : targets)
1652 {
1653 if (Creature* creature = target->ToCreature())
1654 if (IsSmart(creature) && creature->GetVictim())
1655 if (ENSURE_AI(SmartAI, creature->AI())->CanCombatMove())
1656 creature->StartDefaultCombatMovement(creature->GetVictim(), attackDistance, attackAngle);
1657 }
1658
1659 break;
1660 }
1662 {
1664 {
1665 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());
1666 break;
1667 }
1668
1669 for (WorldObject* target : targets)
1670 {
1671 if (Creature* creature = target->ToCreature())
1672 {
1673 if (IsSmart(creature))
1675 }
1676 else if (GameObject* goTarget = target->ToGameObject())
1677 {
1678 if (IsSmart(goTarget))
1680 }
1681 else if (AreaTrigger* areaTriggerTarget = target->ToAreaTrigger())
1682 if (SmartAreaTriggerAI* atSAI = CAST_AI(SmartAreaTriggerAI, areaTriggerTarget->AI()))
1683 atSAI->SetTimedActionList(e, e.action.timedActionList.id, GetLastInvoker());
1684 }
1685 break;
1686 }
1688 {
1689 for (WorldObject* target : targets)
1690 if (Creature* creatureTarget = target->ToCreature())
1691 creatureTarget->ReplaceAllNpcFlags(NPCFlags(e.action.flag.flag));
1692 break;
1693 }
1695 {
1696 for (WorldObject* target : targets)
1697 if (Creature* creatureTarget = target->ToCreature())
1698 creatureTarget->SetNpcFlag(NPCFlags(e.action.flag.flag));
1699 break;
1700 }
1702 {
1703 for (WorldObject* target : targets)
1704 if (Creature* creatureTarget = target->ToCreature())
1705 creatureTarget->RemoveNpcFlag(NPCFlags(e.action.flag.flag));
1706 break;
1707 }
1709 {
1710 if (targets.empty())
1711 break;
1712
1713 ObjectVector casters;
1715
1716 std::shared_ptr<MultiActionResult<SpellCastResult>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<SpellCastResult>>(e, casters.size()* targets.size());
1717
1718 CastSpellExtraArgs args;
1719 if (e.action.crossCast.castFlags & SMARTCAST_TRIGGERED)
1721
1722 for (WorldObject* caster : casters)
1723 {
1724 Unit* casterUnit = caster->ToUnit();
1725 if (!casterUnit)
1726 continue;
1727
1728 bool interruptedSpell = false;
1729
1730 for (WorldObject* target : targets)
1731 {
1732 if (e.action.crossCast.castFlags & SMARTCAST_AURA_NOT_PRESENT && (!target->IsUnit() || target->ToUnit()->HasAura(e.action.crossCast.spell)))
1733 {
1734 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());
1735 continue;
1736 }
1737
1738 if (!interruptedSpell && e.action.crossCast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS)
1739 {
1740 casterUnit->InterruptNonMeleeSpells(false);
1741 interruptedSpell = true;
1742 }
1743
1744 if (waitEvent)
1745 {
1746 args.SetScriptResult(Scripting::v2::ActionResult<SpellCastResult>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() }));
1747 args.SetScriptWaitsForSpellHit((e.action.crossCast.castFlags & SMARTCAST_WAIT_FOR_HIT) != 0);
1748 }
1749
1750 casterUnit->CastSpell(target, e.action.crossCast.spell, args);
1751 }
1752 }
1753 if (waitEvent && !waitEvent->Results.empty())
1754 mTimedActionWaitEvent = std::move(waitEvent);
1755 break;
1756 }
1758 {
1759 std::array<uint32, std::extent_v<decltype(e.action.randTimedActionList.actionLists)>> actionLists;
1760 auto actionListsEnd = std::ranges::remove_copy(e.action.randTimedActionList.actionLists, actionLists.begin(), 0u).out;
1761
1762 uint32 id = Trinity::Containers::SelectRandomContainerElement(Trinity::IteratorPair(actionLists.begin(), actionListsEnd));
1764 {
1765 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());
1766 break;
1767 }
1768
1769 for (WorldObject* target : targets)
1770 {
1771 if (Creature* creature = target->ToCreature())
1772 {
1773 if (IsSmart(creature))
1774 ENSURE_AI(SmartAI, creature->AI())->SetTimedActionList(e, id, GetLastInvoker());
1775 }
1776 else if (GameObject* goTarget = target->ToGameObject())
1777 {
1778 if (IsSmart(goTarget))
1780 }
1781 else if (AreaTrigger* areaTriggerTarget = target->ToAreaTrigger())
1782 if (SmartAreaTriggerAI* atSAI = CAST_AI(SmartAreaTriggerAI, areaTriggerTarget->AI()))
1783 atSAI->SetTimedActionList(e, id, GetLastInvoker());
1784 }
1785 break;
1786 }
1788 {
1791 {
1792 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());
1793 break;
1794 }
1795
1796 for (WorldObject* target : targets)
1797 {
1798 if (Creature* creature = target->ToCreature())
1799 {
1800 if (IsSmart(creature))
1801 ENSURE_AI(SmartAI, creature->AI())->SetTimedActionList(e, id, GetLastInvoker());
1802 }
1803 else if (GameObject* goTarget = target->ToGameObject())
1804 {
1805 if (IsSmart(goTarget))
1807 }
1808 else if (AreaTrigger* areaTriggerTarget = target->ToAreaTrigger())
1809 if (SmartAreaTriggerAI* atSAI = CAST_AI(SmartAreaTriggerAI, areaTriggerTarget->AI()))
1810 atSAI->SetTimedActionList(e, id, GetLastInvoker());
1811 }
1812 break;
1813 }
1815 {
1816 std::shared_ptr<MultiActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<MovementStopReason>>(e, targets.size());
1817
1818 for (WorldObject* target : targets)
1819 {
1820 if (Player* playerTarget = target->ToPlayer())
1821 {
1823 if (waitEvent)
1824 scriptResult = Scripting::v2::ActionResult<MovementStopReason>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() });
1825
1826 if (!playerTarget->ActivateTaxiPathTo(e.action.taxi.id, 0, {}, scriptResult))
1827 if (scriptResult)
1829 }
1830 }
1831 if (waitEvent && !waitEvent->Results.empty())
1832 mTimedActionWaitEvent = std::move(waitEvent);
1833 break;
1834 }
1836 {
1837 bool foundTarget = false;
1838
1839 for (WorldObject* target : targets)
1840 {
1841 if (Creature* creatureTarget = target->ToCreature())
1842 {
1843 foundTarget = true;
1844
1846 creatureTarget->GetMotionMaster()->MoveRandom(float(e.action.moveRandom.distance));
1847 else
1848 creatureTarget->GetMotionMaster()->MoveIdle();
1849 }
1850 }
1851
1852 if (!foundTarget && me && me->IsCreature())
1853 {
1856 else
1858 }
1859 break;
1860 }
1862 {
1863 for (WorldObject* target : targets)
1864 if (Unit* unitTarget = target->ToUnit())
1865 {
1866 switch (e.action.setunitByte.type)
1867 {
1868 case 0:
1869 unitTarget->SetStandState(UnitStandStateType(e.action.setunitByte.byte1));
1870 break;
1871 case 1:
1872 // pet talent points
1873 break;
1874 case 2:
1875 unitTarget->SetVisFlag(UnitVisFlags(e.action.setunitByte.byte1));
1876 break;
1877 case 3:
1878 unitTarget->SetAnimTier(AnimTier(e.action.setunitByte.byte1));
1879 break;
1880 }
1881 }
1882 break;
1883 }
1885 {
1886 for (WorldObject* target : targets)
1887 if (Unit* unitTarget = target->ToUnit())
1888 {
1889 switch (e.action.setunitByte.type)
1890 {
1891 case 0:
1892 unitTarget->SetStandState(UNIT_STAND_STATE_STAND);
1893 break;
1894 case 1:
1895 // pet talent points
1896 break;
1897 case 2:
1898 unitTarget->RemoveVisFlag(UnitVisFlags(e.action.setunitByte.byte1));
1899 break;
1900 case 3:
1901 unitTarget->SetAnimTier(AnimTier::Ground);
1902 break;
1903 }
1904 }
1905 break;
1906 }
1908 {
1909 for (WorldObject* target : targets)
1910 if (Unit* unitTarget = target->ToUnit())
1912 break;
1913 }
1915 {
1916 WorldObject* target = nullptr;
1917
1918 if (!targets.empty())
1920
1921 Position pos(e.target.x, e.target.y, e.target.z);
1922 if (target)
1923 {
1924 float x, y, z;
1925 target->GetPosition(x, y, z);
1926 if (e.action.jump.ContactDistance > 0)
1927 target->GetContactPoint(me, x, y, z, e.action.jump.ContactDistance);
1928 pos = Position(x + e.target.x, y + e.target.y, z + e.target.z);
1929 }
1930 else if (e.GetTargetType() != SMART_TARGET_POSITION)
1931 break;
1932
1933 std::shared_ptr<Scripting::v2::ActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<MovementStopReason>(e);
1935 if (waitEvent)
1937
1941 {}, false, false, {}, nullptr, nullptr, std::move(actionResultSetter));
1942
1943 mTimedActionWaitEvent = std::move(waitEvent);
1944 break;
1945 }
1947 {
1948 for (WorldObject* target : targets)
1949 if (GameObject* gameObject = target->ToGameObject())
1950 gameObject->SetLootState((LootState)e.action.setGoLootState.state);
1951 break;
1952 }
1954 {
1955 for (WorldObject* target : targets)
1956 if (GameObject* gameObject = target->ToGameObject())
1957 gameObject->SetGoState((GOState)e.action.goState.state);
1958 break;
1959 }
1961 {
1962 WorldObject* ref = GetBaseObject();
1963 if (!ref)
1964 ref = unit;
1965
1966 if (!ref)
1967 break;
1968
1969 ObjectVector const* storedTargets = GetStoredTargetVector(e.action.sendTargetToTarget.id, *ref);
1970 if (!storedTargets)
1971 break;
1972
1973 for (WorldObject* target : targets)
1974 {
1975 if (Creature* creatureTarget = target->ToCreature())
1976 {
1977 if (SmartAI* ai = CAST_AI(SmartAI, creatureTarget->AI()))
1978 ai->GetScript()->StoreTargetList(ObjectVector(*storedTargets), e.action.sendTargetToTarget.id); // store a copy of target list
1979 else
1980 TC_LOG_ERROR("sql.sql", "SmartScript: Action target for SMART_ACTION_SEND_TARGET_TO_TARGET is not using SmartAI, skipping");
1981 }
1982 else if (GameObject* gameObject = target->ToGameObject())
1983 {
1984 if (SmartGameObjectAI* ai = CAST_AI(SmartGameObjectAI, gameObject->AI()))
1985 ai->GetScript()->StoreTargetList(ObjectVector(*storedTargets), e.action.sendTargetToTarget.id); // store a copy of target list
1986 else
1987 TC_LOG_ERROR("sql.sql", "SmartScript: Action target for SMART_ACTION_SEND_TARGET_TO_TARGET is not using SmartGameObjectAI, skipping");
1988 }
1989 }
1990 break;
1991 }
1993 {
1994 if (!GetBaseObject() || !IsSmart())
1995 break;
1996
1997 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SEND_GOSSIP_MENU: gossipMenuId {}, gossipNpcTextId {}",
1999
2000 // override default gossip
2001 if (me)
2002 ENSURE_AI(SmartAI, me->AI())->SetGossipReturn(true);
2003 else if (go)
2004 ENSURE_AI(SmartGameObjectAI, go->AI())->SetGossipReturn(true);
2005
2006 for (WorldObject* target : targets)
2007 {
2008 if (Player* player = target->ToPlayer())
2009 {
2012 else
2013 player->PlayerTalkClass->ClearMenus();
2014
2015 uint32 gossipNpcTextId = e.action.sendGossipMenu.gossipNpcTextId;
2016 if (!gossipNpcTextId)
2018
2019 player->PlayerTalkClass->SendGossipMenu(gossipNpcTextId, GetBaseObject()->GetGUID());
2020 }
2021 }
2022 break;
2023 }
2025 {
2026 for (WorldObject* target : targets)
2027 {
2028 if (Creature* creatureTarget = target->ToCreature())
2029 {
2031 creatureTarget->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
2032 else if (e.GetTargetType() == SMART_TARGET_POSITION)
2033 creatureTarget->SetHomePosition(e.target.x, e.target.y, e.target.z, e.target.o);
2041 {
2042 creatureTarget->SetHomePosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation());
2043 }
2044 else
2045 TC_LOG_ERROR("sql.sql", "SmartScript: Action target for SMART_ACTION_SET_HOME_POS is invalid, skipping");
2046 }
2047 }
2048 break;
2049 }
2051 {
2052 for (WorldObject* target : targets)
2053 if (Creature* creatureTarget = target->ToCreature())
2054 creatureTarget->SetRegenerateHealth(e.action.setHealthRegen.regenHealth != 0);
2055 break;
2056 }
2058 {
2059 for (WorldObject* target : targets)
2060 if (Creature* creatureTarget = target->ToCreature())
2061 creatureTarget->SetSessile(e.action.setRoot.root != 0);
2062 break;
2063 }
2065 {
2066 std::list<TempSummon*> summonList;
2068
2069 for (TempSummon* summon : summonList)
2070 if (unit && e.action.creatureGroup.attackInvoker)
2071 summon->AI()->AttackStart(unit);
2072
2074 StoreTargetList({ summonList.begin(), summonList.end() }, e.action.creatureGroup.storedTargetId);
2075 break;
2076 }
2078 {
2079 for (WorldObject* target : targets)
2080 if (Unit* unitTarget = target->ToUnit())
2081 unitTarget->SetPower(Powers(e.action.power.powerType), e.action.power.newPower);
2082 break;
2083 }
2085 {
2086 for (WorldObject* target : targets)
2087 if (Unit* unitTarget = target->ToUnit())
2088 unitTarget->ModifyPower(Powers(e.action.power.powerType), e.action.power.newPower);
2089 break;
2090 }
2092 {
2093 for (WorldObject* target : targets)
2094 if (Unit* unitTarget = target->ToUnit())
2095 unitTarget->ModifyPower(Powers(e.action.power.powerType), -int32(e.action.power.newPower));
2096 break;
2097 }
2099 {
2100 uint32 eventId = e.action.gameEventStop.id;
2101 if (!sGameEventMgr->IsActiveEvent(eventId))
2102 {
2103 TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: At case SMART_ACTION_GAME_EVENT_STOP, inactive event (id: {})", eventId);
2104 break;
2105 }
2106 sGameEventMgr->StopEvent(eventId, true);
2107 break;
2108 }
2110 {
2111 uint32 eventId = e.action.gameEventStart.id;
2112 if (sGameEventMgr->IsActiveEvent(eventId))
2113 {
2114 TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: At case SMART_ACTION_GAME_EVENT_START, already activated event (id: {})", eventId);
2115 break;
2116 }
2117 sGameEventMgr->StartEvent(eventId, true);
2118 break;
2119 }
2121 {
2122 float distanceToClosest = std::numeric_limits<float>::max();
2123 std::pair<uint32, uint32> closest = { 0, 0 };
2124
2125 std::shared_ptr<MultiActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<MovementStopReason>>(e, targets.size());
2126
2127 for (WorldObject* target : targets)
2128 {
2129 if (Creature* creature = target->ToCreature())
2130 {
2131 if (IsSmart(creature))
2132 {
2133 for (uint32 pathId : e.action.closestWaypointFromList.wps)
2134 {
2135 WaypointPath const* path = sWaypointMgr->GetPath(pathId);
2136 if (!path || path->Nodes.empty())
2137 continue;
2138
2139 for (WaypointNode const& waypoint : path->Nodes)
2140 {
2141 float distanceToThisNode = creature->GetDistance(waypoint.X, waypoint.Y, waypoint.Z);
2142 if (distanceToThisNode < distanceToClosest)
2143 {
2144 distanceToClosest = distanceToThisNode;
2145 closest.first = pathId;
2146 closest.second = waypoint.Id;
2147 }
2148 }
2149 }
2150
2151 if (closest.first != 0)
2152 {
2154 if (waitEvent)
2155 actionResultSetter = Scripting::v2::ActionResult<MovementStopReason>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() });
2156
2157 ENSURE_AI(SmartAI, creature->AI())->StartPath(closest.first, true, nullptr, closest.second, 0, std::move(actionResultSetter));
2158 }
2159 }
2160 }
2161 }
2162
2163 if (waitEvent && !waitEvent->Results.empty())
2164 mTimedActionWaitEvent = std::move(waitEvent);
2165 break;
2166 }
2168 {
2169 std::array<uint32, std::extent_v<decltype(e.action.randomSound.sounds)>> sounds;
2170 auto soundsEnd = std::ranges::remove_copy(e.action.randomSound.sounds, sounds.begin(), 0u).out;
2171
2172 bool onlySelf = e.action.randomSound.onlySelf != 0;
2173 for (WorldObject* const target : targets)
2174 {
2176
2177 if (e.action.randomSound.distance == 1)
2178 target->PlayDistanceSound(sound, onlySelf ? target->ToPlayer() : nullptr);
2179 else
2180 target->PlayDirectSound(sound, onlySelf ? target->ToPlayer() : nullptr);
2181
2182 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_RANDOM_SOUND: target: {} ({}), sound: {}, onlyself: {}",
2183 target->GetName(), target->GetGUID(), sound, onlySelf ? "true" : "false");
2184 }
2185 break;
2186 }
2188 {
2189 for (WorldObject* const target : targets)
2190 if (Creature* creatureTarget = target->ToCreature())
2191 creatureTarget->SetCorpseDelay(e.action.corpseDelay.timer, !e.action.corpseDelay.includeDecayRatio);
2192 break;
2193 }
2195 {
2196 if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0)
2197 {
2200
2201 // Instant spawn
2202 GetBaseObject()->GetMap()->SpawnGroupSpawn(e.action.groupSpawn.groupId, ignoreRespawn, force);
2203 }
2204 else
2205 {
2206 // Delayed spawn (use values from parameter to schedule event to call us back
2207 SmartScriptHolder& ev = mStoredEvents.emplace_back();
2208 ev.event_id = e.event_id;
2210 ev.event.event_chance = 100;
2211
2216
2217 ev.event.event_flags = 0;
2219
2222 ev.action.groupSpawn.minDelay = 0;
2223 ev.action.groupSpawn.maxDelay = 0;
2226
2227 ev.target = e.target;
2228
2229 InitTimer(ev);
2230 }
2231 break;
2232 }
2234 {
2235 if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0)
2236 {
2237 bool const deleteRespawnTimes = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN) != 0);
2238
2239 // Instant spawn
2240 GetBaseObject()->GetMap()->SpawnGroupDespawn(e.action.groupSpawn.groupId, deleteRespawnTimes);
2241 }
2242 else
2243 {
2244 // Delayed spawn (use values from parameter to schedule event to call us back
2245 SmartScriptHolder& ev = mStoredEvents.emplace_back();
2246 ev.event_id = e.event_id;
2248 ev.event.event_chance = 100;
2249
2254
2255 ev.event.event_flags = 0;
2257
2260 ev.action.groupSpawn.minDelay = 0;
2261 ev.action.groupSpawn.maxDelay = 0;
2264
2265 ev.target = e.target;
2266
2267 InitTimer(ev);
2268 }
2269 break;
2270 }
2272 {
2273 if (!IsSmart())
2274 break;
2275
2276 ENSURE_AI(SmartAI, me->AI())->SetEvadeDisabled(e.action.disableEvade.disable != 0);
2277 break;
2278 }
2280 {
2281 if (!me->CanHaveThreatList())
2282 break;
2283 for (WorldObject* const target : targets)
2284 if (Unit* unitTarget = target->ToUnit())
2285 me->GetThreatManager().AddThreat(unitTarget, float(e.action.threat.threatINC) - float(e.action.threat.threatDEC), nullptr, true, true);
2286 break;
2287 }
2289 {
2290 for (WorldObject* const target : targets)
2291 if (Creature* creatureTarget = target->ToCreature())
2292 creatureTarget->LoadEquipment(e.action.loadEquipment.id, e.action.loadEquipment.force != 0);
2293 break;
2294 }
2296 {
2299 break;
2300 }
2302 {
2303 for (WorldObject* const target : targets)
2304 if (Unit* unitTarget = target->ToUnit())
2306 break;
2307 }
2309 {
2310 Map* map = nullptr;
2311 if (WorldObject* obj = GetBaseObject())
2312 map = obj->GetMap();
2313 else if (!targets.empty())
2314 map = targets.front()->GetMap();
2315
2316 if (map)
2318 else
2319 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);
2320 break;
2321 }
2323 {
2324 for (WorldObject* const target : targets)
2325 {
2326 if (Creature* creatureTarget = target->ToCreature())
2327 {
2328 if (e.action.animKit.type == 0)
2329 creatureTarget->PlayOneShotAnimKitId(e.action.animKit.animKit);
2330 else if (e.action.animKit.type == 1)
2331 creatureTarget->SetAIAnimKitId(e.action.animKit.animKit);
2332 else if (e.action.animKit.type == 2)
2333 creatureTarget->SetMeleeAnimKitId(e.action.animKit.animKit);
2334 else if (e.action.animKit.type == 3)
2335 creatureTarget->SetMovementAnimKitId(e.action.animKit.animKit);
2336
2337 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_PLAY_ANIMKIT: target: {} ({}), AnimKit: {}, Type: {}",
2338 target->GetName(), target->GetGUID(), e.action.animKit.animKit, e.action.animKit.type);
2339 }
2340 else if (GameObject* gameObject = target->ToGameObject())
2341 {
2342 switch (e.action.animKit.type)
2343 {
2344 case 0:
2345 gameObject->SetAnimKitId(e.action.animKit.animKit, true);
2346 break;
2347 case 1:
2348 gameObject->SetAnimKitId(e.action.animKit.animKit, false);
2349 break;
2350 default:
2351 break;
2352 }
2353
2354 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_PLAY_ANIMKIT: target: {} ({}), AnimKit: {}, Type: {}",
2355 target->GetName(), target->GetGUID(), e.action.animKit.animKit, e.action.animKit.type);
2356 }
2357 }
2358
2359 break;
2360 }
2362 {
2363 for (WorldObject* const target : targets)
2364 if (Player* playerTarget = target->ToPlayer())
2365 playerTarget->GetSceneMgr().PlayScene(e.action.scene.sceneId);
2366
2367 break;
2368 }
2370 {
2371 for (WorldObject* const target : targets)
2372 if (Player* playerTarget = target->ToPlayer())
2373 playerTarget->GetSceneMgr().CancelSceneBySceneId(e.action.scene.sceneId);
2374
2375 break;
2376 }
2378 {
2379 for (WorldObject* target : targets)
2380 if (Player* playerTarget = target->ToPlayer())
2381 playerTarget->SendCinematicStart(e.action.cinematic.entry);
2382 break;
2383 }
2385 {
2386 uint32 speedInteger = e.action.movementSpeed.speedInteger;
2387 uint32 speedFraction = e.action.movementSpeed.speedFraction;
2388 float speed = float(speedInteger) + float(speedFraction) / std::pow(10, std::floor(std::log10(float(speedFraction ? speedFraction : 1)) + 1));
2389
2390 for (WorldObject* const target : targets)
2391 if (Creature* creatureTarget = target->ToCreature())
2392 creatureTarget->SetSpeed(UnitMoveType(e.action.movementSpeed.movementType), speed);
2393
2394 break;
2395 }
2397 {
2398 for (WorldObject* const target : targets)
2399 {
2400 if (Unit* unitTarget = target->ToUnit())
2401 {
2402 unitTarget->SendPlaySpellVisualKit(e.action.spellVisualKit.spellVisualKitId, e.action.spellVisualKit.kitType,
2404
2405 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_PLAY_SPELL_VISUAL_KIT: target: {} ({}), SpellVisualKit: {}",
2406 target->GetName(), target->GetGUID(), e.action.spellVisualKit.spellVisualKitId);
2407 }
2408 }
2409
2410 break;
2411 }
2413 {
2414 if (WorldObject* obj = GetBaseObject())
2415 {
2417 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_OVERRIDE_LIGHT: {} sets zone override light (zoneId: {}, areaLightId: {}, overrideLightId: {}, transitionMilliseconds: {})",
2419 }
2420 break;
2421 }
2423 {
2424 if (WorldObject* obj = GetBaseObject())
2425 {
2427 TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_OVERRIDE_WEATHER: {} sets zone weather (zoneId: {}, weatherId: {}, intensity: {})",
2429 }
2430 break;
2431 }
2433 {
2434 for (WorldObject* target : targets)
2435 if (Unit* unitTarget = target->ToUnit())
2436 unitTarget->SetHover(e.action.setHover.enable != 0);
2437 break;
2438 }
2440 {
2441 for (WorldObject* target : targets)
2442 if (Unit* targetUnit = target->ToUnit())
2443 targetUnit->SetHealth(targetUnit->CountPctFromMaxHealth(e.action.setHealthPct.percent));
2444 break;
2445 }
2447 {
2448 WorldObject* baseObject = GetBaseObject();
2449
2450 for (WorldObject* const target : targets)
2451 {
2452 if (Player* playerTarget = target->ToPlayer())
2453 {
2454 Conversation* conversation = Conversation::CreateConversation(e.action.conversation.id, playerTarget,
2455 *playerTarget, playerTarget->GetGUID(), nullptr);
2456 if (!conversation)
2457 TC_LOG_WARN("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_CREATE_CONVERSATION: id {}, baseObject {}, target {} - failed to create conversation",
2458 e.action.conversation.id, !baseObject ? "" : baseObject->GetName().c_str(), playerTarget->GetName());
2459 }
2460 }
2461
2462 break;
2463 }
2465 {
2466 for (WorldObject* target : targets)
2467 {
2468 if (Unit* unitTarget = target->ToUnit())
2469 {
2471 unitTarget->SetUnitFlag(UNIT_FLAG_IMMUNE_TO_PC);
2472 else
2473 unitTarget->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC);
2474 }
2475 }
2476 break;
2477 }
2479 {
2480 for (WorldObject* target : targets)
2481 {
2482 if (Unit* unitTarget = target->ToUnit())
2483 {
2485 unitTarget->SetUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC);
2486 else
2487 unitTarget->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC);
2488 }
2489 }
2490 break;
2491 }
2493 {
2494 for (WorldObject* target : targets)
2495 if (Unit* unitTarget = target->ToUnit())
2496 unitTarget->SetUninteractible(e.action.setUninteractible.uninteractible != 0);
2497 break;
2498 }
2500 {
2501 for (WorldObject* target : targets)
2502 {
2503 if (GameObject* targetGo = target->ToGameObject())
2504 {
2506 }
2507 }
2508 break;
2509 }
2511 {
2512 if (!targets.empty())
2514 else
2515 {
2516 WorldObject* baseObject = GetBaseObject();
2517 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",
2518 e.action.addToStoredTargets.id, !baseObject ? "" : baseObject->GetName().c_str(), e.event_id);
2519 }
2520 break;
2521 }
2523 {
2524 WorldObject* baseObject = GetBaseObject();
2525
2526 auto doCreatePersonalClone = [&](Position const& position, Player* privateObjectOwner)
2527 {
2529 if (IsSmart(summon))
2530 ENSURE_AI(SmartAI, summon->AI())->SetTimedActionList(e, e.entryOrGuid, privateObjectOwner, e.event_id + 1);
2531 };
2532
2533 // if target is position then targets container was empty
2535 {
2536 for (WorldObject* target : targets)
2537 if (Player* playerTarget = Object::ToPlayer(target))
2538 doCreatePersonalClone(baseObject->GetPosition(), playerTarget);
2539 }
2540 else
2541 {
2542 if (Player* invoker = Object::ToPlayer(GetLastInvoker()))
2543 doCreatePersonalClone({ e.target.x, e.target.y, e.target.z, e.target.o }, invoker);
2544 }
2545
2546 // action list will continue on personal clones
2547 std::erase_if(mTimedActionList, [e](SmartScriptHolder const& script) { return script.event_id > e.event_id; });
2548 break;
2549 }
2551 {
2552 WorldObject* sourceObject = GetBaseObjectOrUnitInvoker(unit);
2553 for (WorldObject* target : targets)
2554 {
2556 GameEvents::Trigger(e.action.triggerGameEvent.eventId, target, sourceObject);
2557 else
2558 GameEvents::Trigger(e.action.triggerGameEvent.eventId, sourceObject, target);
2559 }
2560
2561 break;
2562 }
2564 {
2565 for (WorldObject* target : targets)
2566 {
2567 if (Unit* unitTarget = Object::ToUnit(target))
2568 {
2569 if (unitTarget->GetAI())
2570 unitTarget->GetAI()->DoAction(e.action.doAction.actionId);
2571 }
2572 else if (GameObject* goTarget = Object::ToGameObject(target))
2573 {
2574 if (goTarget->AI())
2575 goTarget->AI()->DoAction(e.action.doAction.actionId);
2576 }
2577 }
2578
2579 break;
2580 }
2582 {
2583 uint32 questId = e.action.quest.quest;
2584 Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
2585 if (!quest)
2586 break;
2587
2588 for (WorldObject* target : targets)
2589 {
2590 Player* player = Object::ToPlayer(target);
2591 if (!player)
2592 continue;
2593
2594 QuestStatus questStatus = player->GetQuestStatus(questId);
2595 if (questStatus == QUEST_STATUS_REWARDED)
2596 continue;
2597
2599 {
2600 if (questStatus == QUEST_STATUS_INCOMPLETE)
2602 }
2603 else if (quest->HasFlag(QUEST_FLAGS_TRACKING_EVENT)) // Check if the quest is used as a serverside flag
2604 player->CompleteQuest(questId);
2605 }
2606
2607 break;
2608 }
2610 {
2611 if (!me)
2612 break;
2613
2614 for (WorldObject* target : targets)
2615 {
2616 Player* player = Object::ToPlayer(target);
2617 if (!player)
2618 continue;
2619
2621 }
2622 break;
2623 }
2625 {
2626 auto work = [&](Conversation* conversation)
2627 {
2628 if (conversation->GetEntry() != e.action.destroyConversation.id)
2629 return;
2630
2631 if (conversation->IsPrivateObject())
2632 {
2634 || !advstd::ranges::contains(targets, conversation->GetPrivateObjectOwner(), [](WorldObject const* target) { return target->GetGUID(); }))
2635 return;
2636 }
2638 return;
2639
2640 conversation->Remove();
2641 };
2642
2645 break;
2646 }
2648 {
2649 if (!me)
2650 break;
2651
2652 for (WorldObject* target : targets)
2653 {
2654 if (Unit* unitTarget = target->ToUnit())
2655 {
2656 me->EnterVehicle(unitTarget, (uint8)e.action.enterVehicle.seatId);
2657 break;
2658 }
2659 }
2660 break;
2661 }
2663 {
2664 if (!me)
2665 break;
2666
2667 for (WorldObject* target : targets)
2668 {
2669 if (Unit* unitTarget = target->ToUnit())
2670 {
2671 unitTarget->EnterVehicle(me, (uint8)e.action.enterVehicle.seatId);
2672 break;
2673 }
2674 }
2675 break;
2676 }
2678 {
2679 for (WorldObject* target : targets)
2680 {
2681 if (Unit* unitTarget = target->ToUnit())
2682 {
2683 unitTarget->ExitVehicle();
2684 }
2685 }
2686 break;
2687 }
2689 {
2690 for (WorldObject* target : targets)
2691 if (Unit* unitTarget = target->ToUnit())
2692 unitTarget->ResumeMovement(e.action.resumeMovement.resumeTimer, e.action.resumeMovement.movementSlot);
2693 break;
2694 }
2695 case SMART_ACTION_FALL:
2696 {
2697 std::shared_ptr<MultiActionResult<MovementStopReason>> waitEvent = CreateTimedActionListWaitEventFor<void, MultiActionResult<MovementStopReason>>(e, targets.size());
2698
2699 for (WorldObject* target : targets)
2700 {
2701 if (Unit* unitTarget = target->ToUnit())
2702 {
2704 if (waitEvent)
2705 actionResultSetter = Scripting::v2::ActionResult<MovementStopReason>::GetResultSetter({ waitEvent, &waitEvent->Results.emplace_back() });
2706
2707 unitTarget->GetMotionMaster()->MoveFall(e.action.fall.pointId, std::move(actionResultSetter));
2708 }
2709 }
2710
2711 if (waitEvent && !waitEvent->Results.empty())
2712 mTimedActionWaitEvent = std::move(waitEvent);
2713 break;
2714 }
2715 default:
2716 TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Unhandled Action type {}", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
2717 break;
2718 }
2719
2720 if (e.link && e.link != e.event_id)
2721 {
2723 if (linked)
2724 ProcessEvent(linked, unit, var0, var1, bvar, spell, gob, varString);
2725 else
2726 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);
2727 }
2728}
2729
2730void 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)
2731{
2732 // 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
2733 if (sConditionMgr->IsObjectMeetingSmartEventConditions(e.entryOrGuid, e.event_id, e.source_type, unit, GetBaseObject()))
2734 {
2735 RecalcTimer(e, min, max);
2736 ProcessAction(e, unit, var0, var1, bvar, spell, gob, varString);
2737 }
2738 else
2739 RecalcTimer(e, std::min<uint32>(min, 5000), std::min<uint32>(min, 5000));
2740}
2741
2742SmartScriptHolder 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)
2743{
2744 SmartScriptHolder script;
2745 script.event.type = e;
2746 script.event.raw.param1 = event_param1;
2747 script.event.raw.param2 = event_param2;
2748 script.event.raw.param3 = event_param3;
2749 script.event.raw.param4 = event_param4;
2750 script.event.raw.param5 = event_param5;
2751 script.event.event_phase_mask = phaseMask;
2752 script.event.event_flags = event_flags;
2753 script.event.event_chance = 100;
2754
2755 script.action.type = action;
2756 script.action.raw.param1 = action_param1;
2757 script.action.raw.param2 = action_param2;
2758 script.action.raw.param3 = action_param3;
2759 script.action.raw.param4 = action_param4;
2760 script.action.raw.param5 = action_param5;
2761 script.action.raw.param6 = action_param6;
2762 script.action.raw.param7 = action_param7;
2763
2764 script.target.type = t;
2765 script.target.raw.param1 = target_param1;
2766 script.target.raw.param2 = target_param2;
2767 script.target.raw.param3 = target_param3;
2768 script.target.raw.param4 = target_param4;
2769 script.target.param_string = targetParamString;
2770
2772 InitTimer(script);
2773 return script;
2774}
2775
2776void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e, WorldObject* invoker /*= nullptr*/) const
2777{
2778 WorldObject* scriptTrigger = nullptr;
2779 if (invoker)
2780 scriptTrigger = invoker;
2781 else if (Unit* tempLastInvoker = GetLastInvoker())
2782 scriptTrigger = tempLastInvoker;
2783
2784 WorldObject* baseObject = GetBaseObject();
2785 switch (e.GetTargetType())
2786 {
2787 case SMART_TARGET_SELF:
2788 if (baseObject)
2789 targets.push_back(baseObject);
2790 break;
2792 if (me)
2793 if (Unit* victim = me->GetVictim())
2794 targets.push_back(victim);
2795 break;
2797 if (me)
2798 {
2800 {
2802 targets.push_back(u);
2803 }
2805 targets.push_back(u);
2806 }
2807 break;
2809 if (me)
2810 {
2812 {
2814 targets.push_back(u);
2815 }
2817 targets.push_back(u);
2818 }
2819 break;
2821 if (me)
2822 {
2824 {
2826 targets.push_back(u);
2827 }
2829 targets.push_back(u);
2830 }
2831 break;
2833 if (me)
2834 {
2836 {
2838 targets.push_back(u);
2839 }
2841 targets.push_back(u);
2842 }
2843 break;
2845 if (me)
2846 {
2848 targets.push_back(u);
2849 }
2850 break;
2852 if (scriptTrigger)
2853 targets.push_back(scriptTrigger);
2854 break;
2856 if (scriptTrigger && scriptTrigger->ToUnit() && scriptTrigger->ToUnit()->GetVehicle() && scriptTrigger->ToUnit()->GetVehicle()->GetBase())
2857 targets.push_back(scriptTrigger->ToUnit()->GetVehicle()->GetBase());
2858 break;
2860 if (scriptTrigger)
2861 {
2862 if (Player* player = scriptTrigger->ToPlayer())
2863 {
2864 if (Group* group = player->GetGroup())
2865 {
2866 for (GroupReference const& groupRef : group->GetMembers())
2867 if (groupRef.GetSource()->IsInMap(player))
2868 targets.push_back(groupRef.GetSource());
2869 }
2870 // We still add the player to the list if there is no group. If we do
2871 // this even if there is a group (thus the else-check), it will add the
2872 // same player to the list twice. We don't want that to happen.
2873 else
2874 targets.push_back(scriptTrigger);
2875 }
2876 }
2877 break;
2879 {
2880 WorldObject* ref = baseObject;
2881 if (!ref)
2882 ref = scriptTrigger;
2883
2884 if (!ref)
2885 {
2886 TC_LOG_ERROR("sql.sql", "SMART_TARGET_CREATURE_RANGE: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
2888 break;
2889 }
2890
2891 std::vector<Creature*> creatures;
2892 ref->GetCreatureListWithOptionsInGrid(creatures, static_cast<float>(e.target.unitRange.maxDist), {
2893 .CreatureId = e.target.unitRange.creature ? Optional<uint32>(e.target.unitRange.creature) : Optional<uint32>(),
2894 .StringId = !e.target.param_string.empty() ? Optional<std::string_view>(e.target.param_string) : Optional<std::string_view>(),
2895 });
2896
2897 std::ranges::copy_if(creatures, std::back_inserter(targets), [&](Creature const* target) { return !ref->IsWithinDist(target, static_cast<float>(e.target.unitRange.minDist)); });
2898
2899 if (e.target.unitRange.maxSize)
2901 break;
2902 }
2904 {
2905 if (!baseObject)
2906 break;
2907
2908 std::vector<Creature*> creatures;
2909 baseObject->GetCreatureListWithOptionsInGrid(creatures, static_cast<float>(e.target.unitDistance.dist), {
2910 .CreatureId = e.target.unitDistance.creature ? Optional<uint32>(e.target.unitDistance.creature) : Optional<uint32>(),
2911 .StringId = !e.target.param_string.empty() ? Optional<std::string_view>(e.target.param_string) : Optional<std::string_view>(),
2912 });
2913
2914 targets = { creatures.begin(), creatures.end() };
2915
2918 break;
2919 }
2921 {
2922 WorldObject* ref = baseObject;
2923 if (!ref)
2924 ref = scriptTrigger;
2925
2926 if (!ref)
2927 {
2928 TC_LOG_ERROR("sql.sql", "SMART_TARGET_GAMEOBJECT_RANGE: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
2930 break;
2931 }
2932
2933 std::vector<GameObject*> gameObjects;
2935 .GameObjectId = e.target.goRange.entry ? Optional<uint32>(e.target.goRange.entry) : Optional<uint32>(),
2936 .StringId = !e.target.param_string.empty() ? Optional<std::string_view>(e.target.param_string) : Optional<std::string_view>(),
2937 });
2938
2939 std::ranges::copy_if(gameObjects, std::back_inserter(targets), [&](GameObject const* target) { return !ref->IsWithinDist(target, static_cast<float>(e.target.goRange.minDist)); });
2940
2941 if (e.target.goRange.maxSize)
2943 break;
2944 }
2946 {
2947 if (!baseObject)
2948 break;
2949
2950 std::vector<GameObject*> gameObjects;
2951 baseObject->GetGameObjectListWithOptionsInGrid(gameObjects, static_cast<float>(e.target.goDistance.dist), {
2952 .GameObjectId = e.target.goDistance.entry ? Optional<uint32>(e.target.goDistance.entry) : Optional<uint32>(),
2953 .StringId = !e.target.param_string.empty() ? Optional<std::string_view>(e.target.param_string) : Optional<std::string_view>(),
2954 });
2955
2956 targets = { gameObjects.begin(), gameObjects.end() };
2957
2960 break;
2961 }
2963 {
2964 if (!scriptTrigger && !baseObject)
2965 {
2966 TC_LOG_ERROR("sql.sql", "SMART_TARGET_CREATURE_GUID: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
2968 break;
2969 }
2970
2971 if (Creature* target = FindCreatureNear(scriptTrigger ? scriptTrigger : baseObject, e.target.unitGUID.dbGuid))
2972 if (!e.target.unitGUID.entry || target->GetEntry() == e.target.unitGUID.entry)
2973 targets.push_back(target);
2974 break;
2975 }
2977 {
2978 if (!scriptTrigger && !baseObject)
2979 {
2980 TC_LOG_ERROR("sql.sql", "SMART_TARGET_GAMEOBJECT_GUID: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
2982 break;
2983 }
2984
2985 if (GameObject* target = FindGameObjectNear(scriptTrigger ? scriptTrigger : baseObject, e.target.goGUID.dbGuid))
2986 if (!e.target.goGUID.entry || target->GetEntry() == e.target.goGUID.entry)
2987 targets.push_back(target);
2988 break;
2989 }
2991 {
2992 if (!baseObject)
2993 break;
2994
2995 std::vector<Player*> players;
2996 baseObject->GetPlayerListInGrid(players, static_cast<float>(e.target.playerRange.maxDist));
2997 std::ranges::copy_if(players, std::back_inserter(targets), [&](Player const* target) { return !baseObject->IsWithinDist(target, static_cast<float>(e.target.playerRange.minDist)); });
2998 break;
2999 }
3001 {
3002 if (!baseObject)
3003 break;
3004
3005 std::vector<Player*> players;
3006 baseObject->GetPlayerListInGrid(players, static_cast<float>(e.target.playerDistance.dist));
3007 targets = { players.begin(), players.end() };
3008 break;
3009 }
3011 {
3012 WorldObject* ref = baseObject;
3013 if (!ref)
3014 ref = scriptTrigger;
3015
3016 if (!ref)
3017 {
3018 TC_LOG_ERROR("sql.sql", "SMART_TARGET_STORED: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
3020 break;
3021 }
3022
3023 if (ObjectVector const* stored = GetStoredTargetVector(e.target.stored.id, *ref))
3024 targets.assign(stored->begin(), stored->end());
3025 break;
3026 }
3028 {
3029 WorldObject* ref = baseObject;
3030 if (!ref)
3031 ref = scriptTrigger;
3032
3033 if (!ref)
3034 {
3035 TC_LOG_ERROR("sql.sql", "SMART_TARGET_CLOSEST_CREATURE: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
3037 break;
3038 }
3039
3041 .CreatureId = e.target.unitClosest.entry,
3044 });
3045
3046 if (target)
3047 targets.push_back(target);
3048 break;
3049 }
3051 {
3052 WorldObject* ref = baseObject;
3053 if (!ref)
3054 ref = scriptTrigger;
3055
3056 if (!ref)
3057 {
3058 TC_LOG_ERROR("sql.sql", "SMART_TARGET_CLOSEST_GAMEOBJECT: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
3060 break;
3061 }
3062
3064 .GameObjectId = e.target.goClosest.entry,
3066 });
3067
3068 if (target)
3069 targets.push_back(target);
3070 break;
3071 }
3073 {
3074 WorldObject* ref = baseObject;
3075 if (!ref)
3076 ref = scriptTrigger;
3077
3078 if (!ref)
3079 {
3080 TC_LOG_ERROR("sql.sql", "SMART_TARGET_CLOSEST_PLAYER: Entry {} SourceType {} Event {} Action {} Target {} is missing base object or invoker.",
3082 break;
3083 }
3084
3085 if (Player* target = ref->SelectNearestPlayer(float(e.target.playerDistance.dist)))
3086 targets.push_back(target);
3087 break;
3088 }
3090 {
3091 if (me)
3092 {
3093 ObjectGuid charmerOrOwnerGuid = me->GetCharmerOrOwnerGUID();
3094
3095 if (!charmerOrOwnerGuid)
3096 if (TempSummon* tempSummon = me->ToTempSummon())
3097 if (WorldObject* summoner = tempSummon->GetSummoner())
3098 charmerOrOwnerGuid = summoner->GetGUID();
3099
3100 if (!charmerOrOwnerGuid)
3101 charmerOrOwnerGuid = me->GetCreatorGUID();
3102
3103 if (WorldObject* owner = ObjectAccessor::GetWorldObject(*me, charmerOrOwnerGuid))
3104 targets.push_back(owner);
3105 }
3106 else if (go)
3107 {
3108 if (Unit* owner = ObjectAccessor::GetUnit(*go, go->GetOwnerGUID()))
3109 targets.push_back(owner);
3110 }
3111
3112 // Get owner of owner
3113 if (e.target.owner.useCharmerOrOwner && !targets.empty())
3114 {
3115 WorldObject* owner = targets.front();
3116 targets.clear();
3117
3118 if (Unit* base = ObjectAccessor::GetUnit(*owner, owner->GetCharmerOrOwnerGUID()))
3119 targets.push_back(base);
3120 }
3121 break;
3122 }
3124 {
3125 if (me && me->CanHaveThreatList())
3126 for (auto* ref : me->GetThreatManager().GetUnsortedThreatList())
3127 if (!e.target.threatList.maxDist || me->IsWithinCombatRange(ref->GetVictim(), float(e.target.threatList.maxDist)))
3128 targets.push_back(ref->GetVictim());
3129 break;
3130 }
3132 {
3133 if (me)
3135 targets.push_back(target);
3136 break;
3137 }
3139 {
3140 if (me)
3142 targets.push_back(target);
3143 break;
3144 }
3146 {
3147 if (me)
3148 for (ObjectGuid tapperGuid : me->GetTapList())
3149 if (Player* tapper = ObjectAccessor::GetPlayer(*me, tapperGuid))
3150 targets.push_back(tapper);
3151
3152 break;
3153 }
3155 {
3156 if (me && me->IsVehicle())
3157 for (std::pair<int8 const, VehicleSeat>& seat : me->GetVehicleKit()->Seats)
3158 if (!e.target.vehicle.seatMask || (e.target.vehicle.seatMask & (1 << seat.first)))
3159 if (Unit* u = ObjectAccessor::GetUnit(*me, seat.second.Passenger.Guid))
3160 targets.push_back(u);
3161 break;
3162 }
3164 {
3165 if (GameObject* target = baseObject->FindNearestUnspawnedGameObject(e.target.goClosest.entry, float(e.target.goClosest.dist ? e.target.goClosest.dist : 100)))
3166 targets.push_back(target);
3167 break;
3168 }
3170 case SMART_TARGET_NONE:
3171 default:
3172 break;
3173 }
3174}
3175
3176void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, uint32 var1, bool bvar, SpellInfo const* spell, GameObject* gob, std::string_view varString)
3177{
3178 if (!e.active && e.GetEventType() != SMART_EVENT_LINK)
3179 return;
3180
3182 return;
3183
3185 return;
3186
3187 switch (e.GetEventType())
3188 {
3189 case SMART_EVENT_LINK://special handling
3190 ProcessAction(e, unit, var0, var1, bvar, spell, gob);
3191 break;
3192 //called from Update tick
3193 case SMART_EVENT_UPDATE:
3195 break;
3197 if (me && me->IsEngaged())
3198 return;
3200 break;
3202 if (!me || !me->IsEngaged())
3203 return;
3205 break;
3207 {
3208 if (!me || me->IsInEvadeMode() || !me->GetMaxHealth())
3209 return;
3210 uint32 perc = (uint32)me->GetHealthPct();
3211 if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
3212 return;
3214 break;
3215 }
3217 {
3218 if (!me || !me->IsEngaged() || !me->GetMaxPower(POWER_MANA))
3219 return;
3221 if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
3222 return;
3224 break;
3225 }
3226 case SMART_EVENT_RANGE:
3227 {
3228 if (!me || !me->IsEngaged() || !me->GetVictim())
3229 return;
3230
3231 if (me->IsInRange(me->GetVictim(), (float)e.event.minMaxRepeat.min, (float)e.event.minMaxRepeat.max))
3233 else // make it predictable
3234 RecalcTimer(e, 500, 500);
3235 break;
3236 }
3238 {
3239 if (!me || !me->IsEngaged())
3240 return;
3241
3242 Unit* victim = me->GetVictim();
3243
3244 if (!victim || !victim->IsNonMeleeSpellCast(false, false, true))
3245 return;
3246
3247 if (e.event.targetCasting.spellId > 0)
3248 if (Spell* currSpell = victim->GetCurrentSpell(CURRENT_GENERIC_SPELL))
3249 if (currSpell->m_spellInfo->Id != e.event.targetCasting.spellId)
3250 return;
3251
3253 break;
3254 }
3256 {
3257 if (!me || !me->IsEngaged())
3258 return;
3259
3260 std::vector<Creature*> creatures;
3261 DoFindFriendlyCC(creatures, float(e.event.friendlyCC.radius));
3262 if (creatures.empty())
3263 {
3264 // if there are at least two same npcs, they will perform the same action immediately even if this is useless...
3265 RecalcTimer(e, 1000, 3000);
3266 return;
3267 }
3269 break;
3270 }
3272 {
3273 std::vector<Creature*> creatures;
3275
3276 if (creatures.empty())
3277 return;
3278
3280 break;
3281 }
3283 {
3284 if (!me)
3285 return;
3286 uint32 count = me->GetAuraCount(e.event.aura.spell);
3287 if ((!e.event.aura.count && !count) || (e.event.aura.count && count >= e.event.aura.count))
3289 break;
3290 }
3292 {
3293 if (!me || !me->GetVictim())
3294 return;
3296 if (count < e.event.aura.count)
3297 return;
3299 break;
3300 }
3302 {
3303 if (bvar == (e.event.charm.onRemove != 1))
3304 ProcessAction(e, unit, var0, var1, bvar, spell, gob);
3305 break;
3306 }
3311 {
3312 ProcessAction(e, unit);
3313 break;
3314 }
3316 {
3317 if (var0 == (e.event.questObjective.id))
3318 ProcessAction(e, unit);
3319 break;
3320 }
3321 //no params
3322 case SMART_EVENT_AGGRO:
3323 case SMART_EVENT_DEATH:
3324 case SMART_EVENT_EVADE:
3326 case SMART_EVENT_RESET:
3339 ProcessAction(e, unit, var0, var1, bvar, spell, gob);
3340 break;
3342 switch (e.event.gossipHello.filter)
3343 {
3344 case 0:
3345 // no filter set, always execute action
3346 break;
3347 case 1:
3348 // OnGossipHello only filter set, skip action if OnReportUse
3349 if (var0)
3350 return;
3351 break;
3352 case 2:
3353 // OnReportUse only filter set, skip action if OnGossipHello
3354 if (!var0)
3355 return;
3356 break;
3357 default:
3358 // Ignore any other value
3359 break;
3360 }
3361
3362 ProcessAction(e, unit, var0, var1, bvar, spell, gob);
3363 break;
3365 if (e.event.emote.emote == var0)
3366 {
3367 RecalcTimer(e, e.event.emote.cooldownMin, e.event.emote.cooldownMax);
3368 ProcessAction(e, unit);
3369 }
3370 break;
3371 case SMART_EVENT_KILL:
3372 {
3373 if (!me || !unit)
3374 return;
3375 if (e.event.kill.playerOnly && unit->GetTypeId() != TYPEID_PLAYER)
3376 return;
3377 if (e.event.kill.creature && unit->GetEntry() != e.event.kill.creature)
3378 return;
3380 ProcessAction(e, unit);
3381 break;
3382 }
3385 {
3386 if (!spell)
3387 return;
3388 if ((!e.event.spellHit.spell || spell->Id == e.event.spellHit.spell) &&
3389 (!e.event.spellHit.school || (spell->SchoolMask & e.event.spellHit.school)))
3390 {
3392 ProcessAction(e, unit, 0, 0, bvar, spell, gob);
3393 }
3394 break;
3395 }
3401 {
3402 if (!spell)
3403 return;
3404
3405 if (spell->Id != e.event.spellCast.spell)
3406 return;
3407
3409 ProcessAction(e, nullptr, 0, 0, bvar, spell);
3410 break;
3411 }
3413 {
3414 if (!me || me->IsEngaged())
3415 return;
3416 //can trigger if closer than fMaxAllowedRange
3417 float range = (float)e.event.los.maxDist;
3418
3419 //if range is ok and we are actually in LOS
3420 if (me->IsWithinDistInMap(unit, range) && me->IsWithinLOSInMap(unit))
3421 {
3423 //if friendly event&&who is not hostile OR hostile event&&who is hostile
3424 if ((hostilityMode == SmartEvent::LOSHostilityMode::Any) ||
3425 (hostilityMode == SmartEvent::LOSHostilityMode::NotHostile && !me->IsHostileTo(unit)) ||
3426 (hostilityMode == SmartEvent::LOSHostilityMode::Hostile && me->IsHostileTo(unit)))
3427 {
3428 if (e.event.los.playerOnly && unit->GetTypeId() != TYPEID_PLAYER)
3429 return;
3431 ProcessAction(e, unit);
3432 }
3433 }
3434 break;
3435 }
3436 case SMART_EVENT_IC_LOS:
3437 {
3438 if (!me || !me->IsEngaged())
3439 return;
3440 //can trigger if closer than fMaxAllowedRange
3441 float range = (float)e.event.los.maxDist;
3442
3443 //if range is ok and we are actually in LOS
3444 if (me->IsWithinDistInMap(unit, range) && me->IsWithinLOSInMap(unit))
3445 {
3447 //if friendly event&&who is not hostile OR hostile event&&who is hostile
3448 if ((hostilityMode == SmartEvent::LOSHostilityMode::Any) ||
3449 (hostilityMode == SmartEvent::LOSHostilityMode::NotHostile && !me->IsHostileTo(unit)) ||
3450 (hostilityMode == SmartEvent::LOSHostilityMode::Hostile && me->IsHostileTo(unit)))
3451 {
3452 if (e.event.los.playerOnly && unit->GetTypeId() != TYPEID_PLAYER)
3453 return;
3455 ProcessAction(e, unit);
3456 }
3457 }
3458 break;
3459 }
3461 {
3462 if (!GetBaseObject())
3463 return;
3465 return;
3467 return;
3468 ProcessAction(e);
3469 break;
3470 }
3473 {
3474 if (!unit->IsCreature())
3475 return;
3476 if (e.event.summoned.creature && unit->GetEntry() != e.event.summoned.creature)
3477 return;
3479 ProcessAction(e, unit);
3480 break;
3481 }
3485 {
3486 if (var0 > e.event.minMaxRepeat.max || var0 < e.event.minMaxRepeat.min)
3487 return;
3489 ProcessAction(e, unit);
3490 break;
3491 }
3493 {
3494 if ((e.event.movementInform.type && var0 != e.event.movementInform.type) || (e.event.movementInform.id != 0xFFFFFFFF && var1 != e.event.movementInform.id))
3495 return;
3496 ProcessAction(e, unit, var0, var1);
3497 break;
3498 }
3500 {
3502 return;
3503 ProcessAction(e, unit, var0);
3504 break;
3505 }
3511 {
3512 if (!me || (e.event.waypoint.pointID != 0xFFFFFFFF && var0 != e.event.waypoint.pointID) || (e.event.waypoint.pathID && var1 != e.event.waypoint.pathID))
3513 return;
3514 ProcessAction(e, unit);
3515 break;
3516 }
3518 {
3519 if (e.event.summoned.creature && e.event.summoned.creature != var0)
3520 return;
3522 ProcessAction(e, unit, var0);
3523 break;
3524 }
3526 {
3528 return;
3530 ProcessAction(e, unit, var0);
3531 break;
3532 }
3535 {
3536 if (e.event.quest.quest && var0 != e.event.quest.quest)
3537 return;
3538 RecalcTimer(e, e.event.quest.cooldownMin, e.event.quest.cooldownMax);
3539 ProcessAction(e, unit, var0);
3540 break;
3541 }
3543 {
3545 return;
3546 ProcessAction(e, unit, var0);
3547 break;
3548 }
3550 {
3552 return;
3553 ProcessAction(e, unit, var0);
3554 break;
3555 }
3557 {
3558 if (e.event.dataSet.id != var0 || e.event.dataSet.value != var1)
3559 return;
3561 ProcessAction(e, unit, var0, var1);
3562 break;
3563 }
3566 {
3567 if (!unit)
3568 return;
3570 ProcessAction(e, unit);
3571 break;
3572 }
3574 {
3575 if (e.event.timedEvent.id == var0)
3576 ProcessAction(e, unit);
3577 break;
3578 }
3580 {
3581 TC_LOG_DEBUG("scripts.ai", "SmartScript: Gossip Select: menu {} action {}", var0, var1);//little help for scripters
3582 if (e.event.gossip.sender != var0 || e.event.gossip.action != var1)
3583 return;
3584 ProcessAction(e, unit, var0, var1);
3585 break;
3586 }
3589 {
3590 if (e.event.gameEvent.gameEventId != var0)
3591 return;
3592 ProcessAction(e, nullptr, var0);
3593 break;
3594 }
3596 {
3597 if (e.event.goLootStateChanged.lootState != var0)
3598 return;
3599 ProcessAction(e, unit, var0, var1);
3600 break;
3601 }
3603 {
3604 if (e.event.eventInform.eventId != var0)
3605 return;
3606 ProcessAction(e, nullptr, var0);
3607 break;
3608 }
3610 {
3611 if (e.event.doAction.eventId != var0)
3612 return;
3613 ProcessAction(e, unit, var0);
3614 break;
3615 }
3617 {
3618 if (!me || me->IsInEvadeMode())
3619 return;
3620
3621 Unit* unitTarget = nullptr;
3622 switch (e.GetTargetType())
3623 {
3631 {
3632 ObjectVector targets;
3633 GetTargets(targets, e);
3634
3635 auto unitTargetItr = std::ranges::find_if(targets, [this, &e](WorldObject* target)
3636 {
3637 Unit* unit = target->ToUnit();
3638 if (unit && me->IsFriendlyTo(unit) && unit->IsAlive() && unit->IsInCombat())
3639 {
3640 uint32 healthPct = uint32(unit->GetHealthPct());
3641 if (e.event.friendlyHealthPct.minHpPct <= healthPct && healthPct <= e.event.friendlyHealthPct.maxHpPct)
3642 return true;
3643 }
3644 return false;
3645 });
3646
3647 if (unitTargetItr != targets.end())
3648 unitTarget = (*unitTargetItr)->ToUnit();
3649 break;
3650 }
3653 break;
3654 default:
3655 return;
3656 }
3657
3658 if (!unitTarget)
3659 return;
3660
3662 break;
3663 }
3665 {
3666 if (!me)
3667 return;
3668
3669 Creature* creature = nullptr;
3670
3671 if (e.event.distance.guid != 0)
3672 {
3673 creature = FindCreatureNear(me, e.event.distance.guid);
3674 if (!creature)
3675 return;
3676
3677 if (!me->IsInRange(creature, 0, static_cast<float>(e.event.distance.dist)))
3678 return;
3679 }
3680 else if (e.event.distance.entry != 0)
3681 {
3682 std::list<Creature*> list;
3683 me->GetCreatureListWithEntryInGrid(list, e.event.distance.entry, static_cast<float>(e.event.distance.dist));
3684
3685 if (!list.empty())
3686 creature = list.front();
3687 }
3688
3689 if (creature)
3691
3692 break;
3693 }
3695 {
3696 if (!me)
3697 return;
3698
3699 GameObject* gameobject = nullptr;
3700
3701 if (e.event.distance.guid != 0)
3702 {
3703 gameobject = FindGameObjectNear(me, e.event.distance.guid);
3704 if (!gameobject)
3705 return;
3706
3707 if (!me->IsInRange(gameobject, 0, static_cast<float>(e.event.distance.dist)))
3708 return;
3709 }
3710 else if (e.event.distance.entry != 0)
3711 {
3712 std::list<GameObject*> list;
3713 me->GetGameObjectListWithEntryInGrid(list, e.event.distance.entry, static_cast<float>(e.event.distance.dist));
3714
3715 if (!list.empty())
3716 gameobject = list.front();
3717 }
3718
3719 if (gameobject)
3720 ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat, nullptr, 0, 0, false, nullptr, gameobject);
3721
3722 break;
3723 }
3725 if (e.event.counter.id != var0 || GetCounterValue(e.event.counter.id) != e.event.counter.value)
3726 return;
3727
3729 break;
3733 {
3734 ProcessAction(e, unit);
3735 break;
3736 }
3738 {
3739 if (e.event.param_string != varString)
3740 return;
3741
3742 ProcessAction(e, unit, var0, 0, false, nullptr, nullptr, varString);
3743 break;
3744 }
3745 default:
3746 TC_LOG_ERROR("sql.sql", "SmartScript::ProcessEvent: Unhandled Event type {}", e.GetEventType());
3747 break;
3748 }
3749}
3750
3752{
3753 switch (e.GetEventType())
3754 {
3755 //set only events which have initial timers
3756 case SMART_EVENT_UPDATE:
3760 break;
3764 break;
3765 default:
3766 e.active = true;
3767 break;
3768 }
3769}
3771{
3772 // min/max was checked at loading!
3773 e.timer = urand(min, max);
3774 e.active = e.timer == 0;
3775}
3776
3778{
3779 if (e.GetEventType() == SMART_EVENT_LINK)
3780 return;
3781
3783 return;
3784
3785 if (e.GetEventType() == SMART_EVENT_UPDATE_IC && (!me || !me->IsEngaged()))
3786 return;
3787
3788 if (e.GetEventType() == SMART_EVENT_UPDATE_OOC && (me && me->IsEngaged())) //can be used with me=nullptr (go script)
3789 return;
3790
3792 return;
3793
3794 if (e.timer < diff)
3795 {
3796 // delay spell cast event if another spell is being cast
3798 {
3800 {
3802 {
3803 RaisePriority(e);
3804 return;
3805 }
3806 }
3807 }
3808
3809 // Delay flee for assist event if stunned or rooted
3811 {
3813 {
3814 e.timer = 1;
3815 return;
3816 }
3817 }
3818
3819 e.active = true;//activate events with cooldown
3820
3821 switch (e.GetEventType())//process ONLY timed events
3822 {
3823 case SMART_EVENT_UPDATE:
3828 case SMART_EVENT_RANGE:
3837 {
3839 {
3840 Unit* invoker = nullptr;
3843 ProcessEvent(e, invoker);
3844 e.enableTimed = false;//disable event if it is in an ActionList and was processed once
3845 for (SmartScriptHolder& scriptholder : mTimedActionList)
3846 {
3847 //find the first event which is not the current one and enable it
3848 if (scriptholder.event_id > e.event_id)
3849 {
3850 scriptholder.enableTimed = true;
3851 break;
3852 }
3853 }
3854 }
3855 else
3856 ProcessEvent(e);
3857 break;
3858 }
3859 }
3860
3862 {
3863 // Reset priority to default one only if the event hasn't been rescheduled again to next loop
3864 if (e.timer > 1)
3865 {
3866 // Re-sort events if this was moved to the top of the queue
3867 mEventSortingRequired = true;
3868 // Reset priority to default one
3870 }
3871 }
3872 }
3873 else
3874 e.timer -= diff;
3875}
3876
3878{
3879 if (!mStoredEvents.empty())
3880 {
3881 for (auto i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
3882 {
3883 if (i->event_id == id)
3884 {
3885 mStoredEvents.erase(i);
3886 return;
3887 }
3888 }
3889 }
3890}
3891
3893{
3894 WorldObject* obj = nullptr;
3895 if (me)
3896 obj = me;
3897 else if (go)
3898 obj = go;
3899 else if (areaTrigger)
3900 obj = areaTrigger;
3901 else if (player)
3902 obj = player;
3903 return obj;
3904}
3905
3907{
3908 return Coalesce<WorldObject>(GetBaseObject(), invoker);
3909}
3910
3912{
3917 && !GetBaseObject())
3918 return;
3919
3920 // Don't run any action while evading
3921 if (me && me->IsInEvadeMode())
3922 {
3923 // Check if the timed action list finished and clear it if so.
3924 // This is required by SMART_ACTION_CALL_TIMED_ACTIONLIST failing if mTimedActionList is not empty.
3925 if (!mTimedActionList.empty())
3926 {
3927 bool needCleanup = true;
3928 for (SmartScriptHolder& scriptholder : mTimedActionList)
3929 {
3930 if (scriptholder.enableTimed)
3931 needCleanup = false;
3932 }
3933
3934 if (needCleanup)
3935 mTimedActionList.clear();
3936 }
3937
3938 return;
3939 }
3940
3942 {
3944 mEventSortingRequired = false;
3945 }
3946
3947 for (SmartScriptHolder& mEvent : mEvents)
3948 UpdateTimer(mEvent, diff);
3949
3950 if (!mStoredEvents.empty())
3951 {
3952 SmartAIEventStoredList::iterator i, icurr;
3953 for (i = mStoredEvents.begin(); i != mStoredEvents.end();)
3954 {
3955 icurr = i++;
3956 UpdateTimer(*icurr, diff);
3957 }
3958 }
3959
3960 bool needCleanup = true;
3961 if (!mTimedActionList.empty())
3962 {
3964
3965 for (size_t i = 0; i < mTimedActionList.size(); ++i)
3966 {
3967 SmartScriptHolder& scriptHolder = mTimedActionList[i];
3968 if (scriptHolder.enableTimed)
3969 {
3970 UpdateTimer(scriptHolder, diff);
3971 needCleanup = false;
3972 }
3973 }
3974
3976 }
3977
3978 if (needCleanup)
3979 mTimedActionList.clear();
3980
3981 if (!mRemIDs.empty())
3982 {
3983 for (auto i : mRemIDs)
3985
3986 mRemIDs.clear();
3987 }
3988
3989 if (mUseTextTimer && me)
3990 {
3991 if (mTextTimer < diff)
3992 {
3993 uint32 textID = mLastTextID;
3994 mLastTextID = 0;
3995 uint32 entry = mTalkerEntry;
3996 mTalkerEntry = 0;
3997 mTextTimer = 0;
3998 mUseTextTimer = false;
3999 ProcessEventsFor(SMART_EVENT_TEXT_OVER, nullptr, textID, entry);
4000 } else mTextTimer -= diff;
4001 }
4002}
4003
4005{
4006 std::sort(events.begin(), events.end());
4007}
4008
4010{
4011 e.timer = 1;
4012 // Change priority only if it's set to default, otherwise keep the current order of events
4014 {
4016 mEventSortingRequired = true;
4017 }
4018}
4019
4020void SmartScript::RetryLater(SmartScriptHolder& e, bool ignoreChanceRoll)
4021{
4022 RaisePriority(e);
4023
4024 // This allows to retry the action later without rolling again the chance roll (which might fail and end up not executing the action)
4025 if (ignoreChanceRoll)
4027
4028 e.runOnce = false;
4029}
4030
4031void SmartScript::FillScript(SmartAIEventList&& e, WorldObject* obj, AreaTriggerEntry const* at, SceneTemplate const* scene, Quest const* quest, uint32 event)
4032{
4033 if (e.empty())
4034 {
4035 if (obj)
4036 TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for Entry {} is empty but is using SmartScript.", obj->GetEntry());
4037 if (at)
4038 TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for AreaTrigger {} is empty but is using SmartScript.", at->ID);
4039 if (scene)
4040 TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for SceneId {} is empty but is using SmartScript.", scene->SceneId);
4041 if (quest)
4042 TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for Quest {} is empty but is using SmartScript.", quest->GetQuestId());
4043 if (event)
4044 TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for Event {} is empty but is using SmartScript.", event);
4045 return;
4046 }
4047 for (SmartScriptHolder& scriptholder : std::move(e))
4048 {
4049 #ifndef TRINITY_DEBUG
4050 if (scriptholder.event.event_flags & SMART_EVENT_FLAG_DEBUG_ONLY)
4051 continue;
4052 #endif
4053
4054 if (obj && !scriptholder.Difficulties.empty())
4055 {
4056 bool foundValidDifficulty = false;
4057 for (Difficulty difficulty : scriptholder.Difficulties)
4058 {
4059 if (difficulty == obj->GetMap()->GetDifficultyID())
4060 {
4061 foundValidDifficulty = true;
4062 break;
4063 }
4064 }
4065
4066 if (!foundValidDifficulty)
4067 continue;
4068 }
4069
4070 mAllEventFlags |= scriptholder.event.event_flags;
4071 mEvents.push_back(std::move(scriptholder));
4072 }
4073}
4074
4076{
4078
4079 // We must use script type to avoid ambiguities
4080 switch (mScriptType)
4081 {
4083 e = sSmartScriptMgr->GetScript(-((int32)me->GetSpawnId()), mScriptType);
4084 if (e.empty())
4085 e = sSmartScriptMgr->GetScript((int32)me->GetEntry(), mScriptType);
4086 FillScript(std::move(e), me, nullptr, nullptr, nullptr, 0);
4087 break;
4089 e = sSmartScriptMgr->GetScript(-((int32)go->GetSpawnId()), mScriptType);
4090 if (e.empty())
4091 e = sSmartScriptMgr->GetScript((int32)go->GetEntry(), mScriptType);
4092 FillScript(std::move(e), go, nullptr, nullptr, nullptr, 0);
4093 break;
4097 FillScript(std::move(e), areaTrigger, nullptr, nullptr, nullptr, 0);
4098 break;
4100 e = sSmartScriptMgr->GetScript((int32)trigger->ID, mScriptType);
4101 FillScript(std::move(e), nullptr, trigger, nullptr, nullptr, 0);
4102 break;
4105 FillScript(std::move(e), nullptr, nullptr, sceneTemplate, nullptr, 0);
4106 break;
4108 e = sSmartScriptMgr->GetScript(quest->GetQuestId(), mScriptType);
4109 FillScript(std::move(e), nullptr, nullptr, nullptr, quest, 0);
4110 break;
4112 e = sSmartScriptMgr->GetScript((int32)event, mScriptType);
4113 FillScript(std::move(e), nullptr, nullptr, nullptr, nullptr, event);
4114 break;
4115 default:
4116 break;
4117 }
4118}
4119
4120void SmartScript::OnInitialize(WorldObject* obj, AreaTriggerEntry const* at, SceneTemplate const* scene, Quest const* qst, uint32 evnt)
4121{
4122 if (at)
4123 {
4125 trigger = at;
4126 player = obj->ToPlayer();
4127
4128 if (!player)
4129 {
4130 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: source is AreaTrigger with id {}, missing trigger player", trigger->ID);
4131 return;
4132 }
4133
4134 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is AreaTrigger with id {}, triggered by player {}", trigger->ID, player->GetGUID());
4135 }
4136 else if (scene)
4137 {
4139 sceneTemplate = scene;
4140 player = obj->ToPlayer();
4141
4142 if (!player)
4143 {
4144 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: source is Scene with id {}, missing trigger player", scene->SceneId);
4145 return;
4146 }
4147
4148 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Scene with id {}, triggered by player {}", scene->SceneId, player->GetGUID());
4149 }
4150 else if (qst)
4151 {
4153 quest = qst;
4154 player = obj->ToPlayer();
4155
4156 if (!player)
4157 {
4158 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: source is Quest with id {}, missing trigger player", qst->GetQuestId());
4159 return;
4160 }
4161
4162 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Quest with id {}, triggered by player {}", qst->GetQuestId(), player->GetGUID());
4163 }
4164 else if (evnt)
4165 {
4167 event = evnt;
4168
4169 if (obj->IsPlayer())
4170 {
4171 player = obj->ToPlayer();
4172 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Event {}, triggered by player {}", event, player->GetGUID());
4173 }
4174 else if (obj->IsCreature())
4175 {
4176 me = obj->ToCreature();
4177 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Event {}, triggered by creature {}", event, me->GetEntry());
4178 }
4179 else if (obj->IsGameObject())
4180 {
4181 go = obj->ToGameObject();
4182 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Event {}, triggered by gameobject {}", event, go->GetEntry());
4183 }
4184 else
4185 {
4186 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: source is Event {}, missing trigger WorldObject", event);
4187 return;
4188 }
4189 }
4190 else if (obj) // Handle object based scripts
4191 {
4192 switch (obj->GetTypeId())
4193 {
4194 case TYPEID_UNIT:
4196 me = obj->ToCreature();
4197 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Creature {}", me->GetEntry());
4198 break;
4199 case TYPEID_GAMEOBJECT:
4201 go = obj->ToGameObject();
4202 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is GameObject {}", go->GetEntry());
4203 break;
4204 case TYPEID_AREATRIGGER:
4205 areaTrigger = obj->ToAreaTrigger();
4207 TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is AreaTrigger {}, IsCustom {}", areaTrigger->GetEntry(), uint32(areaTrigger->IsCustom()));
4208 break;
4209 default:
4210 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: Unhandled TypeID !WARNING!");
4211 return;
4212 }
4213 }
4214 else
4215 {
4216 TC_LOG_ERROR("misc", "SmartScript::OnInitialize: !WARNING! Initialized objects are NULL.");
4217 return;
4218 }
4219
4220 GetScript();//load copy of script
4221
4223 InitTimer(event);//calculate timers for first time use
4224
4227 mCounterList.clear();
4228}
4229
4231{
4232 if (!me)
4233 return;
4234
4236}
4237// SmartScript end
4238
4240{
4241 if (!me)
4242 return nullptr;
4243
4244 Unit* unit = nullptr;
4245
4246 Trinity::MostHPMissingInRange u_check(me, range, MinHPDiff);
4248 Cell::VisitGridObjects(me, searcher, range);
4249 return unit;
4250}
4251
4253{
4254 if (!me)
4255 return nullptr;
4256
4257 Unit* unit = nullptr;
4258 Trinity::MostHPPercentMissingInRange u_check(me, range, minHpPct, maxHpPct);
4260 Cell::VisitGridObjects(me, searcher, range);
4261 return unit;
4262}
4263
4264void SmartScript::DoFindFriendlyCC(std::vector<Creature*>& creatures, float range) const
4265{
4266 if (!me)
4267 return;
4268
4269 Trinity::FriendlyCCedInRange u_check(me, range);
4271 Cell::VisitGridObjects(me, searcher, range);
4272}
4273
4274void SmartScript::DoFindFriendlyMissingBuff(std::vector<Creature*>& creatures, float range, uint32 spellid) const
4275{
4276 if (!me)
4277 return;
4278
4279 Trinity::FriendlyMissingBuffInRange u_check(me, range, spellid);
4281 Cell::VisitGridObjects(me, searcher, range);
4282}
4283
4284Unit* SmartScript::DoFindClosestFriendlyInRange(float range, bool playerOnly) const
4285{
4286 if (!me)
4287 return nullptr;
4288
4289 Unit* unit = nullptr;
4290 Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(me, me, range, playerOnly);
4292 Cell::VisitAllObjects(me, searcher, range);
4293 return unit;
4294}
4295
4296void SmartScript::SetTimedActionList(SmartScriptHolder& e, uint32 entry, Unit* invoker, uint32 startFromEventId)
4297{
4298 //do NOT clear mTimedActionList if it's being iterated because it will invalidate the iterator and delete
4299 // any SmartScriptHolder contained like the "e" parameter passed to this function
4301 {
4302 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());
4303 return;
4304 }
4305
4306 // 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
4308 return;
4309
4310 mTimedActionList.clear();
4312 if (mTimedActionList.empty())
4313 return;
4314
4315 Trinity::Containers::EraseIf(mTimedActionList, [startFromEventId](SmartScriptHolder const& script) { return script.event_id < startFromEventId; });
4316
4317 mTimedActionListInvoker = invoker ? invoker->GetGUID() : ObjectGuid::Empty;
4318 for (SmartAIEventList::iterator i = mTimedActionList.begin(); i != mTimedActionList.end(); ++i)
4319 {
4320 i->enableTimed = i == mTimedActionList.begin();//enable processing only for the first action
4321
4322 if (e.action.timedActionList.timerType == 0)
4323 i->event.type = SMART_EVENT_UPDATE_OOC;
4324 else if (e.action.timedActionList.timerType == 1)
4325 i->event.type = SMART_EVENT_UPDATE_IC;
4326 else if (e.action.timedActionList.timerType > 1)
4327 i->event.type = SMART_EVENT_UPDATE;
4328
4329 InitTimer((*i));
4330 }
4331}
4332
4334{
4335 // Look for invoker only on map of base object... Prevents multithreaded crashes
4336 if (WorldObject* baseObject = GetBaseObject())
4337 return ObjectAccessor::GetUnit(*baseObject, mLastInvoker);
4338 // used for area triggers invoker cast
4339 else if (invoker)
4340 return ObjectAccessor::GetUnit(*invoker, mLastInvoker);
4341
4342 return nullptr;
4343}
4344
4346{
4347 // protect phase from overflowing
4348 SetPhase(std::min<uint32>(SMART_EVENT_PHASE_12, mEventPhase + p));
4349}
4350
4352{
4353 if (p >= mEventPhase)
4354 SetPhase(0);
4355 else
4356 SetPhase(mEventPhase - p);
4357}
4358
4360{
4361 mEventPhase = p;
4362}
4363
4365{
4366 if (mEventPhase == 0)
4367 return false;
4368 return ((1 << (mEventPhase - 1)) & p) != 0;
4369}
#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:935
@ DIFFICULTY_NONE
Definition DBCEnums.h:936
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:172
#define sGameEventMgr
GameObjectActions
LootState
Definition GameObject.h:161
@ GO_READY
Definition GameObject.h:163
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:173
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
#define TC_LOG_WARN(filterType__, message__,...)
Definition Log.h:179
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:1873
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:142
@ QUEST_STATUS_REWARDED
Definition QuestDef.h:149
@ QUEST_STATUS_INCOMPLETE
Definition QuestDef.h:146
@ QUEST_FLAGS_COMPLETION_AREA_TRIGGER
Definition QuestDef.h:216
@ QUEST_FLAGS_COMPLETION_EVENT
Definition QuestDef.h:215
@ QUEST_FLAGS_TRACKING_EVENT
Definition QuestDef.h:224
bool roll_chance(float chance, PseudoRandomDistributionState &state)
Definition Random.cpp:131
uint32 urand(uint32 min, uint32 max)
Definition Random.cpp:43
@ SPELL_EFFECT_SUMMON
@ LANG_ADDON
@ 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_RESUME_MOVEMENT
@ 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:610
@ UNIT_STATE_LOST_CONTROL
Definition Unit.h:316
@ UNIT_STATE_ROOT
Definition Unit.h:285
@ UNIT_STATE_CASTING
Definition Unit.h:290
#define sWaypointMgr
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:394
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:396
void GetTransportHomePosition(float &x, float &y, float &z, float &ori) const
Definition Creature.h:401
bool IsEngaged() const override
GuidUnorderedSet const & GetTapList() const
Definition Creature.h:312
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:224
CreatureAI * AI() const
Definition Creature.h:235
virtual void SetData(uint32, uint32)
ObjectGuid GetOwnerGUID() const override
Definition GameObject.h:251
GameObjectAI * AI() const
Definition GameObject.h:390
ObjectGuid::LowType GetSpawnId() const
Definition GameObject.h:218
Definition Group.h:209
virtual bool SetBossState(uint32 id, EncounterState state)
static char const * GetBossStateName(uint8 state)
Definition Map.h:227
bool SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn=false, bool force=false, std::vector< WorldObject * > *spawnedObjects=nullptr)
Definition Map.cpp:2360
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:1191
Difficulty GetDifficultyID() const
Definition Map.h:363
GameObjectBySpawnIdContainer & GetGameObjectBySpawnIdStore()
Definition Map.h:470
bool SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes=false, size_t *count=nullptr)
Definition Map.cpp:2449
CreatureBySpawnIdContainer & GetCreatureBySpawnIdStore()
Definition Map.h:466
void Respawn(RespawnInfo *info, CharacterDatabaseTransaction dbTrans=nullptr)
Definition Map.cpp:2070
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:16646
void TalkedToCreature(uint32 entry, ObjectGuid guid)
Definition Player.cpp:16675
void CompleteQuest(uint32 quest_id)
Definition Player.cpp:14893
void AddQuestAndCheckCompletion(Quest const *quest, Object *questGiver)
Definition Player.cpp:14633
bool CanTakeQuest(Quest const *quest, bool msg) const
Definition Player.cpp:14479
void AreaExploredOrEventHappens(uint32 questId)
Definition Player.cpp:16519
QuestStatus GetQuestStatus(uint32 quest_id) const
Definition Player.cpp:15929
Group * GetGroup(Optional< uint8 > partyIndex)
Definition Player.h:2781
void PrepareGossipMenu(WorldObject *source, uint32 menuId, bool showQuests=false)
Definition Player.cpp:13884
uint32 GetGossipTextId(uint32 menuId, WorldObject *source)
Definition Player.cpp:14226
std::unique_ptr< PlayerMenu > PlayerTalkClass
Definition Player.h:2556
uint32 GetQuestId() const
Definition QuestDef.h:633
bool HasFlag(QuestFlags flag) const
Definition QuestDef.h:615
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:71
Definition Unit.h:647
void EnterVehicle(Unit *base, int8 seatId=-1)
Definition Unit.cpp:12845
bool IsVehicle() const
Definition Unit.h:766
bool IsCharmed() const
Definition Unit.h:1256
Vehicle * GetVehicle() const
Definition Unit.h:1800
float GetHealthPct() const
Definition Unit.h:808
bool IsNonMeleeSpellCast(bool withDelayed, bool skipChanneled=false, bool skipAutorepeat=false, bool isAutoshoot=false, bool skipInstant=true, bool skipChanneledAllowingActions=false) const
Definition Unit.cpp:3219
void CombatStop(bool includingCast=false, bool mutualPvP=true, bool(*unitFilter)(Unit const *otherUnit)=nullptr)
Definition Unit.cpp:6031
bool CanHaveThreatList() const
====================== THREAT & COMBAT ====================
Definition Unit.h:1045
ThreatManager & GetThreatManager()
Definition Unit.h:1093
bool IsWithinCombatRange(Unit const *obj, float dist2compare) const
Definition Unit.cpp:675
void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid=0, bool withInstant=true)
Definition Unit.cpp:3253
MotionMaster * GetMotionMaster()
Definition Unit.h:1750
void Dismount()
Definition Unit.cpp:8336
bool IsAlive() const
Definition Unit.h:1205
int32 GetMaxPower(Powers power) const
Definition Unit.cpp:10105
TempSummon * ToTempSummon()
Definition Unit.h:1844
ObjectGuid GetCharmerOrOwnerGUID() const override
Definition Unit.h:1236
ObjectGuid GetCreatorGUID() const override
Definition Unit.h:1213
void Mount(uint32 mount, uint32 vehicleId=0, uint32 creatureEntry=0)
Definition Unit.cpp:8303
void SetSheath(SheathState sheathed)
Definition Unit.cpp:5832
Gender GetGender() const
Definition Unit.h:779
Unit * EnsureVictim() const
Definition Unit.h:740
void SetFacingToObject(WorldObject const *object, bool force=true, uint32 movementId=EVENT_FACE)
Definition Unit.cpp:13394
bool IsAIEnabled() const
Definition Unit.h:678
uint32 GetAuraCount(uint32 spellId) const
Definition Unit.cpp:4806
uint64 GetMaxHealth() const
Definition Unit.h:801
Aura * GetAura(uint32 spellId, ObjectGuid casterGUID=ObjectGuid::Empty, ObjectGuid itemCasterGUID=ObjectGuid::Empty, uint32 reqEffMask=0) const
Definition Unit.cpp:4718
TransportBase * GetDirectTransport() const
Returns the transport this unit is on directly (if on vehicle and transport, return vehicle)
Definition Unit.cpp:12246
Unit * GetVictim() const
Definition Unit.h:738
bool HasUnitState(const uint32 f) const
Definition Unit.h:755
float GetPowerPct(Powers power) const
Definition Unit.h:832
void SetFacingTo(float ori, bool force=true, uint32 movementId=EVENT_FACE)
Definition Unit.cpp:13385
void RemoveAllAuras()
Definition Unit.cpp:4400
Vehicle * GetVehicleKit() const
Definition Unit.h:1798
void KillSelf(bool durabilityLoss=true, bool skipSettingDeathState=false)
Definition Unit.h:951
void DeMorph()
Definition Unit.cpp:3400
void RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID=ObjectGuid::Empty, uint32 reqEffMask=0, AuraRemoveMode removeMode=AURA_REMOVE_BY_DEFAULT)
Definition Unit.cpp:3992
bool IsInCombat() const
Definition Unit.h:1073
bool isDead() const
Definition Unit.h:1207
Spell * GetCurrentSpell(CurrentSpellTypes spellType) const
Definition Unit.h:1490
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:1426
void GetPlayerListInGrid(Container &playerContainer, float maxSearchRange, bool alive=true) const
Definition Object.cpp:2683
Player * SelectNearestPlayer(float range) const
Definition Object.cpp:1584
Map * GetMap() const
Definition Object.h:411
void GetGameObjectListWithOptionsInGrid(Container &gameObjectContainer, float maxSearchRange, FindGameObjectOptions const &options) const
Definition Object.cpp:2651
Creature * FindNearestCreatureWithOptions(float range, FindCreatureOptions const &options) const
Definition Object.cpp:1531
InstanceScript * GetInstanceScript() const
Definition Object.cpp:394
void GetCreatureListWithEntryInGrid(Container &creatureContainer, uint32 entry, float maxSearchRange=250.0f) const
Definition Object.cpp:2663
ObjectGuid GetPrivateObjectOwner() const
Definition Object.h:569
void GetGameObjectListWithEntryInGrid(Container &gameObjectContainer, uint32 entry, float maxSearchRange=250.0f) const
Definition Object.cpp:2643
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition Object.cpp:2222
bool IsHostileTo(WorldObject const *target) const
Definition Object.cpp:2186
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:1446
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:533
void GetContactPoint(WorldObject const *obj, float &x, float &y, float &z, float distance2d=CONTACT_DISTANCE) const
Definition Object.cpp:2781
GameObject * FindNearestGameObjectWithOptions(float range, FindGameObjectOptions const &options) const
Definition Object.cpp:1553
GameObject * FindNearestUnspawnedGameObject(uint32 entry, float range) const
Definition Object.cpp:1566
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition Object.cpp:499
bool IsInRange(WorldObject const *obj, float minRange, float maxRange, bool is3D=true) const
Definition Object.cpp:590
void GetCreatureListWithOptionsInGrid(Container &creatureContainer, float maxSearchRange, FindCreatureOptions const &options) const
Definition Object.cpp:2671
bool IsWithinDist(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition Object.cpp:494
void SummonCreatureGroup(uint8 group, std::list< TempSummon * > *list=nullptr)
Definition Object.cpp:1512
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2191
virtual void SetData(uint32, uint32)
Definition ZoneScript.h:100
virtual void SetGuidData(uint32, ObjectGuid)
Definition ZoneScript.h:92
WeatherState
Definition Weather.h:47
static constexpr ObjectData gameObjects[]
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)
Definition CellImpl.h:160
static void VisitGridObjects(WorldObject const *obj, T &visitor, float radius)
Definition CellImpl.h:140
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 fromOrientation(float orientation)
uint32 SceneId
Definition ObjectMgr.h:855
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::@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::@169 raw
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::@168 resumeMovement
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::@170::@183 unitClosest
struct SmartTarget::@170::@182 goDistance
struct SmartTarget::@170::@184 goClosest
struct SmartTarget::@170::@176 unitDistance
std::string param_string
struct SmartTarget::@170::@188 vehicle
struct SmartTarget::@170::@174 unitRange
SAIBool useCharmerOrOwner
struct SmartTarget::@170::@189 threatList
struct SmartTarget::@170::@180 goRange
struct SmartTarget::@170::@173 farthest
struct SmartTarget::@170::@178 playerRange
struct SmartTarget::@170::@186 closestFriendly
struct SmartTarget::@170::@172 hostilRandom
struct SmartTarget::@170::@190 raw
struct SmartTarget::@170::@175 unitGUID
struct SmartTarget::@170::@177 playerDistance
struct SmartTarget::@170::@181 goGUID
SMARTAI_TARGETS type
struct SmartTarget::@170::@185 closestAttackable
uint32 findCreatureAliveState
struct SmartTarget::@170::@187 owner
struct SmartTarget::@170::@179 stored
std::vector< WaypointNode > Nodes