EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
SentryAI.cpp
Go to the documentation of this file.
1 
10 #include "eve-server.h"
11 
12 #include "Client.h"
14 #include "npc/Sentry.h"
15 #include "npc/SentryAI.h"
16 #include "system/DestinyManager.h"
17 #include "system/Damage.h"
18 #include "system/SystemBubble.h"
19 
21 : m_state(State::Idle),
22 m_npc(who),
23 m_mainAttackTimer(1000),
24 m_processTimer(5000), //arbitrary.
25 m_beginFindTarget(5000), //arbitrary.
26 m_warpScramblerTimer(5000), //arbitrary.
27 m_webifierTimer(5000) //arbitrary.
28 {
29  m_webifierTimer.Disable(); //not implemented yet
30  m_beginFindTarget.Disable(); //arbitrary.
31  m_mainAttackTimer.Disable(); //waiting till engaged
32  m_warpScramblerTimer.Disable(); //not implemented yet
33 
34  m_webber = false;
35  m_warpScram = false;
36 
38 
39  /* set npc ship speeds and distances */
43 
44  // Optimal Range
46  // Accuracy falloff (distance past maximum range at which accuracy has fallen by half)
49  // max firing range default:10000
51  if (!m_maxAttackRange)
52  m_maxAttackRange = 10000;
53  // 'sight' range
54  m_sightRange = 20000;
57 
58  // advanced AI variables only used by sleepers for now. will update advanced npcs to use these also
60  m_useTargSwitching = true;
61  } else {
62  m_useTargSwitching = false;
63  }
65  m_useSecondTarget = true;
66  } else {
67  m_useSecondTarget = false;
68  }
70  m_useSigRadius = true;
72  } else {
73  m_useSigRadius = false;
75  }
78  } else {
79  m_switchTargChance = 0.0f;
80  }
81 }
82 
84  if ((!m_processTimer.Check()) or (!m_npc->SysBubble()->HasPlayers()))
85  return;
86 
87  /* NPC::State definitions -allan 25July15 (UD 1June16)
88  * Idle, // not doing anything, nothing in sight....idle.
89  * Engaged, // actively fighting.
90  * Signaling // calling for help
91  */
92  switch(m_state) {
93  case State::Idle: {
94  // The parameter proximityRange (154) tells us how far we "see" (npc's dont have this, but drones do)
95  if (m_beginFindTarget.Check()) {
96  std::vector<Client*> clientVec;
97  clientVec.clear();
98  DestinyManager* pDestiny(nullptr);
99  m_npc->SysBubble()->GetPlayers(clientVec); // what about player drones? yes...later
100  for (auto cur : clientVec) {
115  return;
116  }
117  } else {
118  if (!m_beginFindTarget.Enabled())
119  m_beginFindTarget.Start(m_attackSpeed); //find target is based on npc attack speed.
120  }
121  } break;
122  case State::Engaged: {
123  if (m_npc->TargetMgr()->HasNoTargets()) {
124  _log(NPC__AI_TRACE, "%s(%u): Stopped %s, HasNoTargets = true.", m_npc->GetName(), m_npc->GetID(), GetStateName(m_state).c_str());
125  SetIdle();
126  return;
127  }
128  SystemEntity* pTarget = m_npc->TargetMgr()->GetFirstTarget(false);
129  if (!pTarget) {
130  _log(NPC__AI_TRACE, "%s(%u): Stopped %s, GetFirstTarget() returned NULL.", m_npc->GetName(), m_npc->GetID(), GetStateName(m_state).c_str());
131  SetIdle();
132  return;
133  } else if (!pTarget->SysBubble()) {
134  ClearTarget(pTarget);
135  return;
136  }
137  CheckDistance(pTarget);
138  } break;
139  case State::Signaling: {
140  _log(NPC__AI_TRACE, "%s(%u): Called %s, needs to be completed.", m_npc->GetName(), m_npc->GetID(), GetStateName(m_state).c_str());
141  // not sure how im gonna do this
142  } break;
143  }
144 }
145 
147  if (m_state == State::Idle) return;
148  // not doing anything....idle.
149  _log(NPC__AI_TRACE, "%s(%u): Idle: returning to idle.", \
150  m_npc->GetName(), m_npc->GetID());
152 
157 }
158 
160  if (m_state == State::Engaged)
161  return;
162  _log(NPC__AI_TRACE, "%s(%u): Engaged: Begin engaging. Target is %s(%u).", \
163  m_npc->GetName(), m_npc->GetID(), pTarget->GetName(), pTarget->GetID());
164  // actively fighting
166 }
167 
169  if (m_state == State::Signaling)
170  return;
171  _log(NPC__AI_TRACE, "%s(%u): Signaling: Begin signaling. Target is %s(%u).", \
172  m_npc->GetName(), m_npc->GetID(), pTarget->GetName(), pTarget->GetID());
173  // actively signaling
175 }
176 
178 {
179  double dist = m_npc->GetPosition().distance(pTarget->GetPosition());
180  if ((dist > m_sightRange) and (!m_npc->TargetMgr()->IsTargetedBy(pTarget))) {
181  _log(NPC__AI_TRACE, "%s(%u): CheckDistance: %s(%u) is too far away (%u). Return to Idle.", \
182  m_npc->GetName(), m_npc->GetID(), pTarget->GetName(), pTarget->GetID(), dist);
183  if (m_state != State::Idle) {
184  // target is no longer in npc's "sight range" and is NOT targeting this npc. unlock target and return to idle.
185  // should we do anything else here? search for another target? wander around? yes..later
186  // if npc is targeted greater than this distance, it will chase
187  ClearTarget(pTarget);
188  }
189  return;
190  }
191 
192  SetEngaged(pTarget);
193 
194  if (!m_mainAttackTimer.Enabled())
196 
197  Attack(pTarget);
198 }
199 
201  m_npc->TargetMgr()->ClearTarget(pTarget);
202  //m_npc->TargetMgr()->OnTarget(pTarget, TargMgr::Mode::Lost);
203 
204  if (m_npc->TargetMgr()->HasNoTargets())
205  SetIdle();
206 }
207 
210  //m_npc->TargetMgr()->OnTarget(nullptr, TargMgr::Mode::Clear, TargMgr::Msg::ClientReq);
211 }
212 
214  double targetTime = GetTargetTime();
215  bool chase = false;
216 
217  if (!m_npc->TargetMgr()->StartTargeting(pTarget, targetTime, (uint8)m_npc->GetSelf()->GetAttribute(AttrMaxAttackTargets).get_int(), m_sightRange, chase)) {
218  if (!chase) {
219  _log(NPC__AI_TRACE, "%s(%u): Targeting of %s(%u) failed. Clear Target and Return to Idle.", \
220  m_npc->GetName(), m_npc->GetID(), pTarget->GetName(), pTarget->GetID());
221  SetIdle();
222  }
223  return;
224  }
226  CheckDistance(pTarget);
227 }
228 
230  double targetTime = GetTargetTime();
231  switch(m_state) {
232  case State::Idle: {
233  _log(NPC__AI_TRACE, "%s(%u): Targeted by %s(%u) in Idle. Begin Targeting sequence.", \
234  m_npc->GetName(), m_npc->GetID(), pAgressor->GetName(), pAgressor->GetID());
235 
236  bool chase = false;
237  if (!m_npc->TargetMgr()->StartTargeting( pAgressor, targetTime, (uint8)m_npc->GetSelf()->GetAttribute(AttrMaxAttackTargets).get_int(), m_sightRange, chase)) {
238  if (!chase) {
239  _log(NPC__AI_TRACE, "%s(%u): Targeting of %s(%u) failed. Clear Target and Return to Idle.", \
240  m_npc->GetName(), m_npc->GetID(), pAgressor->GetName(), pAgressor->GetID());
241  SetIdle();
242  }
243  }
245  } break;
247  case State::Engaged: {
248  _log(NPC__AI_TRACE, "%s(%u): Targeted by %s(%u) while engaged.", \
249  m_npc->GetName(), m_npc->GetID(), pAgressor->GetName(), pAgressor->GetID());
250  } break;
251  case State::Signaling: {
252  _log(NPC__AI_TRACE, "%s(%u): Targeted by %s(%u) while signaling.", \
253  m_npc->GetName(), m_npc->GetID(), pAgressor->GetName(), pAgressor->GetID());
254  } break;
255  }
256 }
257 
259  if (m_state == State::Engaged) {
260  if (m_npc->TargetMgr()->HasNoTargets()) {
261  _log(NPC__AI_TRACE, "%s(%u): Target %s(%u) lost. No targets remain. Return to Idle.", \
262  m_npc->GetName(), m_npc->GetID(), pTarget->GetName(), pTarget->GetID());
263  SetIdle();
264  } else {
265  _log(NPC__AI_TRACE, "%s(%u): Target %s(%u) lost, but more targets remain.", \
266  m_npc->GetName(), m_npc->GetID(), pTarget->GetName(), pTarget->GetID());
268  }
269  }
270 }
271 
273 {
274  if (m_mainAttackTimer.Check()) {
275  if (!pTarget) return;
276  // Check to see if the target still in the bubble (Client warped out)
277  if (!m_npc->SysBubble()->InBubble(pTarget->GetPosition())) {
278  _log(NPC__AI_TRACE, "%s(%u): Target %s(%u) no longer in bubble. Clear target and move on",
279  m_npc->GetName(), m_npc->GetID(), pTarget->GetName(), pTarget->GetID());
280  ClearTarget(pTarget);
281  return;
282  }
283  if (!pTarget->DestinyMgr()) {
284  _log(NPC__AI_TRACE, "%s(%u): Target %s(%u) has no destiny manager. Clear target and move on",
285  m_npc->GetName(), m_npc->GetID(), pTarget->GetName(), pTarget->GetID());
286  ClearTarget(pTarget);
287  return;
288  }
289  // Check to see if the target is not cloaked:
290  if (pTarget->DestinyMgr()->IsCloaked()) {
291  _log(NPC__AI_TRACE, "%s(%u): Target %s(%u) is cloaked. Clear target and move on",
292  m_npc->GetName(), m_npc->GetID(), pTarget->GetName(), pTarget->GetID());
293  ClearTarget(pTarget);
294  return;
295  }
296  if (m_npc->TargetMgr()->CanAttack())
297  AttackTarget(pTarget);
298  }
299 }
300 
301 //also check for special effects and write code to implement them
302 //modifyTargetSpeedRange, modifyTargetSpeedChance
303 //entityWarpScrambleChance
305  // some npcs use missiles.....write code for using missiles -- entityMissileTypeID
306  std::string guid = "effects.Laser";
307  // sentry does NOT have a destiny manager...use target's destiny manager for sending fx
308  pTarget->DestinyMgr()->SendSpecialEffect(m_npc->GetSelf()->itemID(),
309  m_npc->GetSelf()->itemID(),
310  m_npc->GetSelf()->typeID(), //m_npc->GetSelf()->GetAttribute(AttrGfxTurretID).get_int(),
311  pTarget->GetID(),
312  0,guid,1,1,1,m_attackSpeed,0);
313 
314  Damage d(m_npc,
315  m_npc->GetSelf(),
316  m_npc->GetKinetic(),
317  m_npc->GetThermal(),
318  m_npc->GetEM(),
319  m_npc->GetExplosive(),
320  m_formula.GetSentryToHit(m_npc, pTarget),
322  );
323 
324  d *= m_damageMultiplier;
325  pTarget->ApplyDamage(d);
326 }
327 
329 {
330  double targetTime = (m_npc->GetSelf()->GetAttribute(AttrScanSpeed).get_int());
331  float radius = m_npc->GetSelf()->GetAttribute(AttrRadius).get_float();
332  if (!targetTime) {
333  if (radius < 30) {
334  targetTime = 1500;
335  } else if (radius < 60) {
336  targetTime = 2500;
337  } else if (radius < 150) {
338  targetTime = 4000;
339  } else if (radius < 280) {
340  targetTime = 6000;
341  } else if (radius < 550) {
342  targetTime = 8000;
343  } else {
344  targetTime = 13000;
345  }
346  }
347  return targetTime;
348 }
349 
351 {
352  switch (name) {
353  case State::Idle: return "Idle";
354  case State::Engaged: return "Engaged";
355  case State::Signaling: return "Signaling";
356  }
357  return "Undefined";
358 }
bool m_webber
Definition: SentryAI.h:66
unsigned __int8 uint8
Definition: eve-compat.h:46
double m_trackingSpeed
Definition: SentryAI.h:84
bool m_useTargSwitching
Definition: SentryAI.h:69
#define _log(type, fmt,...)
Definition: logsys.h:124
void TargetLost(SystemEntity *by_who)
Definition: SentryAI.cpp:258
void Disable()
Definition: timer.h:39
uint32 m_maxAttackRange
Definition: SentryAI.h:82
void ClearAllTargets(bool notify=true)
void ClearTarget(SystemEntity *tSE)
bool HasAttribute(const uint16 attrID) const
State m_state
Definition: SentryAI.h:61
uint32 m_sigRadius
Definition: SentryAI.h:79
SystemBubble * SysBubble()
Definition: SystemEntity.h:195
float m_switchTargChance
Definition: SentryAI.h:71
bool InBubble(const GPoint &pt, bool inWarp=false) const
std::string GetStateName(State name)
Definition: SentryAI.cpp:350
void SetEngaged(SystemEntity *pTarget)
Definition: SentryAI.cpp:159
uint16 m_preferedSigRadius
Definition: SentryAI.h:72
void Target(SystemEntity *by_who)
Definition: SentryAI.cpp:213
uint16 m_optimalRange
Definition: SentryAI.h:76
Timer m_processTimer
Definition: SentryAI.h:90
void SetIdle()
Definition: SentryAI.cpp:146
TargetManager * TargetMgr()
Definition: SystemEntity.h:197
Definition: Sentry.h:20
const GPoint & GetPosition() const
Definition: SystemEntity.h:211
void SendSpecialEffect(uint32 entityID, uint32 moduleID, uint32 moduleTypeID, uint32 targetID, uint32 chargeTypeID, std::string guid, bool isOffensive, bool start, bool isActive, int32 duration, uint32 repeat, int32 graphicInfo=0) const
SystemEntity * GetFirstTarget(bool need_locked=false)
DestinyManager * DestinyMgr()
Definition: SystemEntity.h:198
TurretFormulas m_formula
Definition: SentryAI.h:88
InventoryItemRef GetSelf()
Definition: SystemEntity.h:202
double GetTargetTime()
Definition: SentryAI.cpp:328
bool Enabled() const
Definition: timer.h:41
void SetSignaling(SystemEntity *pTarget)
Definition: SentryAI.cpp:168
int64 get_int()
Definition: EvilNumber.cpp:166
void ClearAllTargets()
Definition: SentryAI.cpp:208
Definition: Damage.h:33
float GetThermal()
Definition: Sentry.h:46
uint16 m_attackSpeed
Definition: SentryAI.h:74
uint32 m_sightRange
Definition: SentryAI.h:81
uint32 GetID()
Definition: SystemEntity.h:207
bool HasPlayers() const
Definition: SystemBubble.h:85
bool Check(bool reset=true)
Definition: timer.cpp:62
uint32 m_falloff
Definition: SentryAI.h:80
float GetExplosive()
Definition: Sentry.h:49
bool m_useSigRadius
Definition: SentryAI.h:68
const char * GetName() const
Definition: SystemEntity.h:210
Timer m_warpScramblerTimer
Definition: SentryAI.h:93
bool m_warpScram
Definition: SentryAI.h:67
uint16 m_damageMultiplier
Definition: SentryAI.h:77
Timer m_beginFindTarget
Definition: SentryAI.h:92
Sentry * m_npc
Definition: SentryAI.h:86
void Attack(SystemEntity *pTarget)
Definition: SentryAI.cpp:272
void Process()
Definition: SentryAI.cpp:83
bool HasNoTargets() const
EvilNumber GetAttribute(const uint16 attrID) const
float GetEM()
Definition: Sentry.h:47
bool m_useSecondTarget
Definition: SentryAI.h:70
float GetKinetic()
Definition: Sentry.h:48
void CheckDistance(SystemEntity *pTarget)
Definition: SentryAI.cpp:177
float get_float()
Definition: EvilNumber.cpp:184
GaExpInl GaFloat distance(const GaVec3 &oth) const
Definition: GaTypes.h:158
void AttackTarget(SystemEntity *pTarget)
Definition: SentryAI.cpp:304
Timer m_mainAttackTimer
Definition: SentryAI.h:91
bool ApplyDamage(Damage &d)
Definition: Damage.cpp:108
uint16 typeID() const
bool IsTargetedBy(SystemEntity *pSE)
float GetSentryToHit(Sentry *pSentry, SystemEntity *pTarget)
void Targeted(SystemEntity *by_who)
Definition: SentryAI.cpp:229
Timer m_webifierTimer
Definition: SentryAI.h:94
SentryAI(Sentry *who)
Definition: SentryAI.cpp:20
bool StartTargeting(SystemEntity *tSE, ShipItemRef sRef)
uint32 itemID() const
Definition: InventoryItem.h:98
void ClearTarget(SystemEntity *pTarget)
Definition: SentryAI.cpp:200
void GetPlayers(std::vector< Client * > &into) const
void Start(uint32 setTimerTime=0, bool changeResetTimer=true)
Definition: timer.cpp:81