TrinityCore
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 (uint32 spell : _spells)
80 {
81 if (AISpellInfoType const* info = GetAISpellInfo(spell, me->GetMap()->GetDifficultyID()))
82 {
83 if (info->condition == AICOND_AGGRO)
84 me->CastSpell(who, spell, false);
85 else if (info->condition == AICOND_COMBAT)
86 _events.ScheduleEvent(spell, info->cooldown, info->cooldown * 2);
87 }
88 }
89}
90
92{
93 if (!UpdateVictim())
94 return;
95
96 _events.Update(diff);
97
99 return;
100
101 if (uint32 spellId = _events.ExecuteEvent())
102 {
103 DoCast(spellId);
104 if (AISpellInfoType const* info = GetAISpellInfo(spellId, me->GetMap()->GetDifficultyID()))
105 _events.ScheduleEvent(spellId, info->cooldown, info->cooldown * 2);
106 }
107}
108
110{
111 _events.RescheduleEvent(spellId, Milliseconds(unTimeMs));
112}
113
115// CasterAI
117
119{
121
122 _attackDistance = 30.0f;
123
124 for (uint32 spell : _spells)
125 {
126 if (AISpellInfoType const* info = GetAISpellInfo(spell, me->GetMap()->GetDifficultyID()))
127 if (info->condition == AICOND_COMBAT && _attackDistance > info->maxRange)
128 _attackDistance = info->maxRange;
129 }
130
131 if (_attackDistance == 30.0f)
133}
134
136{
137 if (_spells.empty())
138 return;
139
140 uint32 spell = rand32() % _spells.size();
141 uint32 count = 0;
142 for (auto itr = _spells.begin(); itr != _spells.end(); ++itr, ++count)
143 {
144 if (AISpellInfoType const* info = GetAISpellInfo(*itr, me->GetMap()->GetDifficultyID()))
145 {
146 if (info->condition == AICOND_AGGRO)
147 me->CastSpell(who, *itr, false);
148 else if (info->condition == AICOND_COMBAT)
149 {
150 Milliseconds cooldown = info->realCooldown;
151 if (count == spell)
152 {
153 DoCast(_spells[spell]);
154 cooldown += Milliseconds(me->GetCurrentSpellCastTime(*itr));
155 }
156 _events.ScheduleEvent(*itr, cooldown);
157 }
158 }
159 }
160}
161
163{
164 if (!UpdateVictim())
165 return;
166
167 _events.Update(diff);
168
170 {
172 return;
173 }
174
176 return;
177
178 if (uint32 spellId = _events.ExecuteEvent())
179 {
180 DoCast(spellId);
181 uint32 casttime = me->GetCurrentSpellCastTime(spellId);
182 if (AISpellInfoType const* info = GetAISpellInfo(spellId, me->GetMap()->GetDifficultyID()))
183 _events.ScheduleEvent(spellId, Milliseconds(casttime ? casttime : 500) + info->realCooldown);
184 }
185}
186
188// TurretAI
190
191TurretAI::TurretAI(Creature* creature, uint32 scriptId) : CreatureAI(creature, scriptId)
192{
193 if (!creature->m_spells[0])
194 TC_LOG_ERROR("scripts.ai", "TurretAI set for creature with spell1 = 0. AI will do nothing ({})", creature->GetGUID().ToString());
195
196 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(creature->m_spells[0], creature->GetMap()->GetDifficultyID());
197 _minimumRange = spellInfo ? spellInfo->GetMinRange(false) : 0;
198 creature->m_CombatDistance = spellInfo ? spellInfo->GetMaxRange(false) : 0;
199 creature->m_SightDistance = creature->m_CombatDistance;
200 creature->SetCanMelee(false);
201}
202
203bool TurretAI::CanAIAttack(Unit const* who) const
204{
207 return false;
208 return true;
209}
210
212{
213 if (who)
214 me->Attack(who, false);
215}
216
218{
219 if (!UpdateVictim())
220 return;
221
223}
224
226// VehicleAI
228
229VehicleAI::VehicleAI(Creature* creature, uint32 scriptId) : CreatureAI(creature, scriptId), _hasConditions(false), _conditionsTimer(VEHICLE_CONDITION_CHECK_TIME)
230{
232 _dismiss = false;
234 me->SetCanMelee(false);
235}
236
237// NOTE: VehicleAI::UpdateAI runs even while the vehicle is mounted
239{
240 CheckConditions(diff);
241
242 if (_dismiss)
243 {
244 if (_dismissTimer < diff)
245 {
246 _dismiss = false;
248 }
249 else
250 _dismissTimer -= diff;
251 }
252}
253
254void VehicleAI::OnCharmed(bool /*isNew*/)
255{
256 bool const charmed = me->IsCharmed();
257 if (!me->GetVehicleKit()->IsVehicleInUse() && !charmed && _hasConditions) // was used and has conditions
258 {
259 _dismiss = true; // needs reset
260 }
261 else if (charmed)
262 _dismiss = false; // in use again
263
264 _dismissTimer = VEHICLE_DISMISS_TIME; // reset timer
265}
266
268{
270}
271
273{
274 if (!_hasConditions)
275 return;
276
277 if (_conditionsTimer <= diff)
278 {
279 if (Vehicle * vehicleKit = me->GetVehicleKit())
280 {
281 for (auto const& [i, vehicleSeat] : vehicleKit->Seats)
282 {
283 if (Unit* passenger = ObjectAccessor::GetUnit(*me, vehicleSeat.Passenger.Guid))
284 {
285 if (Player * player = passenger->ToPlayer())
286 {
287 if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry(), player, me))
288 {
289 player->ExitVehicle();
290 return; // check other pessanger in next tick
291 }
292 }
293 }
294 }
295 }
296
298 }
299 else
300 _conditionsTimer -= diff;
301}
302
304{
305 if (creature->IsVehicle())
306 return PERMIT_BASE_SPECIAL;
307
308 return PERMIT_BASE_NO;
309}
#define VEHICLE_CONDITION_CHECK_TIME
Definition: CombatAI.h:82
#define VEHICLE_DISMISS_TIME
Definition: CombatAI.h:83
#define sConditionMgr
Definition: ConditionMgr.h:365
@ CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE
Definition: ConditionMgr.h:170
@ AICOND_COMBAT
@ AICOND_AGGRO
@ AICOND_DIE
AISpellInfoType * GetAISpellInfo(uint32 spellId, Difficulty difficulty)
Definition: CreatureAI.cpp:40
@ PERMIT_BASE_SPECIAL
Definition: CreatureAI.h:49
@ PERMIT_BASE_NO
Definition: CreatureAI.h:44
@ PERMIT_BASE_REACTIVE
Definition: CreatureAI.h:46
int32_t int32
Definition: Define.h:138
uint32_t uint32
Definition: Define.h:142
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition: Duration.h:29
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define MELEE_RANGE
Definition: ObjectDefines.h:45
uint32 rand32()
Definition: Random.cpp:70
#define sSpellMgr
Definition: SpellMgr.h:849
@ UNIT_STATE_CASTING
Definition: Unit.h:270
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:162
void InitializeAI() override
Definition: CombatAI.cpp:118
void JustEngagedWith(Unit *) override
Definition: CombatAI.cpp:135
float _attackDistance
Definition: CombatAI.h:65
SpellVector _spells
Definition: CombatAI.h:53
void SpellInterrupted(uint32 spellId, uint32 unTimeMs) override
Definition: CombatAI.cpp:109
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:91
bool UpdateVictim()
Definition: CreatureAI.cpp:245
Creature *const me
Definition: CreatureAI.h:61
void SetCanMelee(bool canMelee, bool fleeFromMelee=false)
Definition: Creature.cpp:2822
bool IsCivilian() const
Definition: Creature.h:112
uint32 m_spells[MAX_CREATURE_SPELLS]
Definition: Creature.h:302
float m_SightDistance
Definition: Creature.h:412
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
Definition: Creature.cpp:2415
float m_CombatDistance
Definition: Creature.h:412
uint32 ExecuteEvent()
Definition: EventMap.cpp:73
void Update(uint32 time)
Definition: EventMap.h:56
void ScheduleEvent(uint32 eventId, Milliseconds time, uint32 group=0, uint8 phase=0)
Definition: EventMap.cpp:36
void RescheduleEvent(uint32 eventId, Milliseconds time, uint32 group=0, uint8 phase=0)
Definition: EventMap.cpp:52
void Reset()
Definition: EventMap.cpp:21
Difficulty GetDifficultyID() const
Definition: Map.h:324
std::string ToString() const
Definition: ObjectGuid.cpp:554
uint32 GetEntry() const
Definition: Object.h:161
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
static Player * ToPlayer(Object *o)
Definition: Object.h:213
float GetMaxRange(bool positive=false, WorldObject *caster=nullptr, Spell *spell=nullptr) const
Definition: SpellInfo.cpp:3768
float GetMinRange(bool positive=false) const
Definition: SpellInfo.cpp:3761
bool DoSpellAttackIfReady(uint32 spellId)
Definition: UnitAI.cpp:61
virtual void InitializeAI()
Definition: UnitAI.cpp:43
SpellCastResult DoCast(uint32 spellId)
Definition: UnitAI.cpp:89
Definition: Unit.h:627
bool IsVehicle() const
Definition: Unit.h:743
bool IsCharmed() const
Definition: Unit.h:1215
bool HasBreakableByDamageCrowdControlAura(Unit *excludeCasterChannel=nullptr) const
Definition: Unit.cpp:734
bool IsWithinCombatRange(Unit const *obj, float dist2compare) const
Definition: Unit.cpp:635
void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid=0, bool withInstant=true)
Definition: Unit.cpp:3089
Unit * EnsureVictim() const
Definition: Unit.h:717
bool Attack(Unit *victim, bool meleeAttack)
Definition: Unit.cpp:5670
Unit * GetVictim() const
Definition: Unit.h:715
bool HasUnitState(const uint32 f) const
Definition: Unit.h:732
Vehicle * GetVehicleKit() const
Definition: Unit.h:1711
int32 GetCurrentSpellCastTime(uint32 spell_id) const
Definition: Unit.cpp:3112
bool IsVehicleInUse() const
Returns information whether the vehicle is currently used by any unit.
Definition: Vehicle.cpp:591
Map * GetMap() const
Definition: Object.h:624
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition: Object.cpp:2896
bool IsNeutralToAll() const
Definition: Object.cpp:2883
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:203
TurretAI(Creature *creature, uint32 scriptId={})
Definition: CombatAI.cpp:191
void UpdateAI(uint32 diff) override
Definition: CombatAI.cpp:217
void AttackStart(Unit *who) override
Definition: CombatAI.cpp:211
void LoadConditions()
Definition: CombatAI.cpp:267
bool _hasConditions
Definition: CombatAI.h:101
static int32 Permissible(Creature const *creature)
Definition: CombatAI.cpp:303
VehicleAI(Creature *creature, uint32 scriptId={})
Definition: CombatAI.cpp:229
void UpdateAI(uint32 diff) override
Definition: CombatAI.cpp:238
bool _dismiss
Definition: CombatAI.h:103
uint32 _dismissTimer
Definition: CombatAI.h:104
void OnCharmed(bool isNew) override
Definition: CombatAI.cpp:254
uint32 _conditionsTimer
Definition: CombatAI.h:102
void CheckConditions(uint32 diff)
Definition: CombatAI.cpp:272