TrinityCore
Loading...
Searching...
No Matches
CombatAI.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 "CombatAI.h"
19#include "ConditionMgr.h"
20#include "Creature.h"
21#include "CreatureAIImpl.h"
22#include "Log.h"
23#include "Map.h"
24#include "MotionMaster.h"
25#include "ObjectAccessor.h"
26#include "Player.h"
27#include "SpellInfo.h"
28#include "SpellMgr.h"
29#include "Vehicle.h"
30
32// AggressorAI
34
36{
37 // have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight
38 if (!creature->IsCivilian() && !creature->IsNeutralToAll())
40
41 return PERMIT_BASE_NO;
42}
43
45{
47}
48
50// CombatAI
52
54{
55 for (uint32 spell : me->m_spells)
56 if (spell && sSpellMgr->GetSpellInfo(spell, me->GetMap()->GetDifficultyID()))
57 _spells.push_back(spell);
58
60}
61
63{
64 _events.Reset();
65}
66
68{
69 for (uint32 spell : _spells)
70 {
71 if (AISpellInfoType const* info = GetAISpellInfo(spell, me->GetMap()->GetDifficultyID()))
72 if (info->condition == AICOND_DIE)
73 me->CastSpell(killer, spell, true);
74 }
75}
76
78{
79 for (std::size_t i = 0; i < _spells.size(); ++i)
80 {
81 uint32 spell = _spells[i];
82 if (AISpellInfoType const* info = GetAISpellInfo(spell, me->GetMap()->GetDifficultyID()))
83 {
84 if (info->condition == AICOND_AGGRO)
85 me->CastSpell(who, spell, false);
86 else if (info->condition == AICOND_COMBAT)
87 _events.ScheduleEvent(i, info->cooldown, info->cooldown * 2);
88 }
89 }
90}
91
93{
94 if (!UpdateVictim())
95 return;
96
97 _events.Update(diff);
98
100 return;
101
102 if (uint32 i = _events.ExecuteEvent(); i && i < _spells.size())
103 {
104 uint32 spellId = _spells[i];
105 DoCast(spellId);
106 if (AISpellInfoType const* info = GetAISpellInfo(spellId, me->GetMap()->GetDifficultyID()))
107 _events.ScheduleEvent(i, info->cooldown, info->cooldown * 2);
108 }
109}
110
112{
113 auto spell = std::ranges::find(_spells, spellId);
114 if (spell != _spells.end())
115 _events.RescheduleEvent(std::ranges::distance(_spells.begin(), spell), Milliseconds(unTimeMs));
116}
117
119// CasterAI
121
123{
125
126 _attackDistance = 30.0f;
127
128 for (uint32 spell : _spells)
129 {
130 if (AISpellInfoType const* info = GetAISpellInfo(spell, me->GetMap()->GetDifficultyID()))
131 if (info->condition == AICOND_COMBAT && _attackDistance > info->maxRange)
132 _attackDistance = info->maxRange;
133 }
134
135 if (_attackDistance == 30.0f)
137}
138
140{
141 if (_spells.empty())
142 return;
143
144 uint32 spell = rand32() % _spells.size();
145 for (std::size_t i = 0; i < _spells.size(); ++i)
146 {
147 uint32 itr = _spells[i];
148 if (AISpellInfoType const* info = GetAISpellInfo(itr, me->GetMap()->GetDifficultyID()))
149 {
150 if (info->condition == AICOND_AGGRO)
151 me->CastSpell(who, itr, false);
152 else if (info->condition == AICOND_COMBAT)
153 {
154 Milliseconds cooldown = info->realCooldown;
155 if (i == spell)
156 {
157 DoCast(_spells[spell]);
158 cooldown += Milliseconds(me->GetCurrentSpellCastTime(itr));
159 }
160 _events.ScheduleEvent(i, cooldown);
161 }
162 }
163 }
164}
165
167{
168 if (!UpdateVictim())
169 return;
170
171 _events.Update(diff);
172
174 {
176 return;
177 }
178
180 return;
181
182 if (uint32 i = _events.ExecuteEvent(); i && i < _spells.size())
183 {
184 uint32 spellId = _spells[i];
185 DoCast(spellId);
186 uint32 casttime = me->GetCurrentSpellCastTime(spellId);
187 if (AISpellInfoType const* info = GetAISpellInfo(spellId, me->GetMap()->GetDifficultyID()))
188 _events.ScheduleEvent(i, Milliseconds(casttime ? casttime : 500) + info->realCooldown);
189 }
190}
191
193// TurretAI
195
196TurretAI::TurretAI(Creature* creature, uint32 scriptId) noexcept : CreatureAI(creature, scriptId), _minimumRange(0.0f)
197{
198 if (!creature->m_spells[0])
199 TC_LOG_ERROR("scripts.ai", "TurretAI set for creature with spell1 = 0. AI will do nothing ({})", creature->GetGUID().ToString());
200
201 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(creature->m_spells[0], creature->GetMap()->GetDifficultyID()))
202 {
203 auto [minRange, maxRange] = spellInfo->GetMinMaxRange(false);
204 _minimumRange = minRange;
205 creature->m_CombatDistance = maxRange;
206 }
207 creature->m_SightDistance = creature->m_CombatDistance;
208 creature->SetCanMelee(false);
209}
210
211bool TurretAI::CanAIAttack(Unit const* who) const
212{
215 return false;
216 return true;
217}
218
220{
221 if (who)
222 me->Attack(who, false);
223}
224
226{
227 if (!UpdateVictim())
228 return;
229
231}
232
234// VehicleAI
236
237VehicleAI::VehicleAI(Creature* creature, uint32 scriptId) noexcept : CreatureAI(creature, scriptId), _hasConditions(false), _conditionsTimer(VEHICLE_CONDITION_CHECK_TIME)
238{
239 LoadConditions();
240 _dismiss = false;
241 _dismissTimer = VEHICLE_DISMISS_TIME;
242 me->SetCanMelee(false);
243}
244
245// NOTE: VehicleAI::UpdateAI runs even while the vehicle is mounted
247{
248 CheckConditions(diff);
249
250 if (_dismiss)
251 {
252 if (_dismissTimer < diff)
253 {
254 _dismiss = false;
256 }
257 else
258 _dismissTimer -= diff;
259 }
260}
261
262void VehicleAI::OnCharmed(bool /*isNew*/)
263{
264 bool const charmed = me->IsCharmed();
265 if (!me->GetVehicleKit()->IsVehicleInUse() && !charmed && _hasConditions) // was used and has conditions
266 {
267 _dismiss = true; // needs reset
268 }
269 else if (charmed)
270 _dismiss = false; // in use again
271
272 _dismissTimer = VEHICLE_DISMISS_TIME; // reset timer
273}
274
279
281{
282 if (!_hasConditions)
283 return;
284
285 if (_conditionsTimer <= diff)
286 {
287 if (Vehicle * vehicleKit = me->GetVehicleKit())
288 {
289 for (auto const& [i, vehicleSeat] : vehicleKit->Seats)
290 {
291 if (Unit* passenger = ObjectAccessor::GetUnit(*me, vehicleSeat.Passenger.Guid))
292 {
293 if (Player * player = passenger->ToPlayer())
294 {
295 if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry(), player, me))
296 {
297 player->ExitVehicle();
298 return; // check other pessanger in next tick
299 }
300 }
301 }
302 }
303 }
304
306 }
307 else
308 _conditionsTimer -= diff;
309}
310
312{
313 if (creature->IsVehicle())
314 return PERMIT_BASE_SPECIAL;
315
316 return PERMIT_BASE_NO;
317}
#define VEHICLE_CONDITION_CHECK_TIME
Definition CombatAI.h:82
#define VEHICLE_DISMISS_TIME
Definition CombatAI.h:83
#define sConditionMgr
@ CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE
@ AICOND_COMBAT
@ AICOND_AGGRO
@ AICOND_DIE
AISpellInfoType * GetAISpellInfo(uint32 spellId, Difficulty difficulty)
@ PERMIT_BASE_SPECIAL
Definition CreatureAI.h:51
@ PERMIT_BASE_NO
Definition CreatureAI.h:46
@ PERMIT_BASE_REACTIVE
Definition CreatureAI.h:48
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
#define MELEE_RANGE
uint32 rand32()
Definition Random.cpp:71
#define sSpellMgr
Definition SpellMgr.h:812
@ UNIT_STATE_CASTING
Definition Unit.h:290
static int32 Permissible(Creature const *creature)
Definition CombatAI.cpp:35
void UpdateAI(uint32) override
Definition CombatAI.cpp:44
void UpdateAI(uint32 diff) override
Definition CombatAI.cpp:166
void InitializeAI() override
Definition CombatAI.cpp:122
void JustEngagedWith(Unit *) override
Definition CombatAI.cpp:139
float _attackDistance
Definition CombatAI.h:65
SpellVector _spells
Definition CombatAI.h:53
void SpellInterrupted(uint32 spellId, uint32 unTimeMs) override
Definition CombatAI.cpp:111
void InitializeAI() override
Definition CombatAI.cpp:53
void Reset() override
Definition CombatAI.cpp:62
void JustDied(Unit *killer) override
Definition CombatAI.cpp:67
void JustEngagedWith(Unit *who) override
Definition CombatAI.cpp:77
EventMap _events
Definition CombatAI.h:52
void UpdateAI(uint32 diff) override
Definition CombatAI.cpp:92
bool UpdateVictim()
Creature *const me
Definition CreatureAI.h:63
bool IsCivilian() const
Definition Creature.h:126
uint32 m_spells[MAX_CREATURE_SPELLS]
Definition Creature.h:332
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
float m_CombatDistance
Definition Creature.h:435
void Update(uint32 time)
Definition EventMap.h:77
EventId ExecuteEvent()
Definition EventMap.cpp:77
void RescheduleEvent(EventId eventId, Milliseconds time, GroupIndex group=0u, PhaseIndex phase=0u)
Definition EventMap.cpp:56
void ScheduleEvent(EventId eventId, Milliseconds time, GroupIndex group=0u, PhaseIndex phase=0u)
Definition EventMap.cpp:40
void Reset()
Definition EventMap.cpp:25
Difficulty GetDifficultyID() const
Definition Map.h:363
Player * ToPlayer()
Definition Object.h:126
uint32 GetEntry() const
Definition Object.h:89
bool DoSpellAttackIfReady(uint32 spellId)
Definition UnitAI.cpp:53
virtual void InitializeAI()
Definition UnitAI.cpp:35
SpellCastResult DoCast(uint32 spellId)
Definition UnitAI.cpp:81
Definition Unit.h:647
bool IsVehicle() const
Definition Unit.h:766
bool IsCharmed() const
Definition Unit.h:1256
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
Unit * EnsureVictim() const
Definition Unit.h:740
bool Attack(Unit *victim, bool meleeAttack)
Definition Unit.cpp:5872
Unit * GetVictim() const
Definition Unit.h:738
bool HasUnitState(const uint32 f) const
Definition Unit.h:755
bool HasBreakableByDamageCrowdControlAura(Unit const *excludeCasterChannel=nullptr) const
Definition Unit.cpp:783
Vehicle * GetVehicleKit() const
Definition Unit.h:1798
int32 GetCurrentSpellCastTime(uint32 spell_id) const
Definition Unit.cpp:3276
bool IsVehicleInUse() const
Returns information whether the vehicle is currently used by any unit.
Definition Vehicle.cpp:612
Map * GetMap() const
Definition Object.h:411
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition Object.cpp:2222
bool IsNeutralToAll() const
Definition Object.cpp:2209
TC_GAME_API Unit * GetUnit(WorldObject const &, ObjectGuid const &guid)
float _minimumRange
Definition CombatAI.h:79
bool CanAIAttack(Unit const *who) const override
Definition CombatAI.cpp:211
TurretAI(Creature *creature, uint32 scriptId={}) noexcept
Definition CombatAI.cpp:196
void UpdateAI(uint32 diff) override
Definition CombatAI.cpp:225
void AttackStart(Unit *who) override
== Triggered Actions Requested ==================
Definition CombatAI.cpp:219
VehicleAI(Creature *creature, uint32 scriptId={}) noexcept
Definition CombatAI.cpp:237
void LoadConditions()
Definition CombatAI.cpp:275
bool _hasConditions
Definition CombatAI.h:101
static int32 Permissible(Creature const *creature)
Definition CombatAI.cpp:311
void UpdateAI(uint32 diff) override
Definition CombatAI.cpp:246
bool _dismiss
Definition CombatAI.h:103
uint32 _dismissTimer
Definition CombatAI.h:104
void OnCharmed(bool isNew) override
Definition CombatAI.cpp:262
uint32 _conditionsTimer
Definition CombatAI.h:102
void CheckConditions(uint32 diff)
Definition CombatAI.cpp:280