1 //
2 // C++ Implementation: bot
3 //
4 // Description:
5 //
6 // Contains code of the base bot class
7 //
8 // Author:  Rick <rickhelmus@gmail.com>
9 //
10 //
11 //
12 
13 #include "cube.h"
14 #include "bot.h"
15 
16 vector<botent *> bots;
17 
18 extern int triggertime;
19 extern itemstat itemstats[];
20 extern ENetHost *clienthost;
21 
22 const vec g_vecZero(0, 0, 0);
23 
24 //Bot class begin
25 
~CBot()26 CBot::~CBot()
27 {
28      // Delete all waypoints
29      loopi(MAX_MAP_GRIDS)
30      {
31           loopj(MAX_MAP_GRIDS)
32           {
33                while (m_WaypointList[i][j].Empty() == false)
34                     delete m_WaypointList[i][j].Pop();
35           }
36      }
37 }
38 
Spawn()39 void CBot::Spawn()
40 {
41     // Init all bot variabeles
42     spawnplayer(m_pMyEnt);
43 
44      m_pMyEnt->targetyaw = m_pMyEnt->targetpitch = 0.0f;
45      m_pMyEnt->enemy = NULL;
46      m_pMyEnt->maxspeed = 22.0f;
47      m_pMyEnt->pBot = this;
48 
49      m_eCurrentBotState = STATE_NORMAL;
50      m_iShootDelay = m_iChangeWeaponDelay = 0;
51      m_iCheckEnvDelay = 0;
52      m_vPrevOrigin = g_vecZero;
53      m_iStuckCheckDelay = lastmillis + 250;
54      m_bStuck = false;
55      m_iStuckTime = 0;
56      m_iStrafeTime = m_iStrafeCheckDelay = 0;
57      m_iMoveDir = DIR_NONE;
58 
59      m_pPrevEnemy = NULL;
60      m_iCombatNavTime = 0;
61      m_iSPMoveTime = 0;
62      m_iEnemySearchDelay = 0;
63      m_iSawEnemyTime = 0;
64      m_bCombatJump = false;
65      m_iCombatJumpDelay = 0;
66      m_bShootAtFeet = (RandomLong(1, 100) <= m_pBotSkill->sShootAtFeetWithRLPercent);
67      m_iHuntDelay = 0;
68      m_vHuntLocation = m_vPrevHuntLocation = g_vecZero;
69      m_pHuntTarget = NULL;
70      m_fPrevHuntDist = 0.0f;
71      m_iHuntLastTurnLessTime = m_iHuntPlayerUpdateTime = m_iHuntPauseTime = 0;
72      m_iLookAroundDelay = m_iLookAroundTime = m_iLookAroundUpdateTime = 0;
73      m_fLookAroundYaw = 0.0f;
74      m_bLookLeft = false;
75 
76      m_iLastJumpPad = 0;
77      m_pTargetEnt = NULL;
78      while(!m_UnreachableEnts.Empty()) delete m_UnreachableEnts.Pop();
79      m_iCheckTeleporterDelay = m_iCheckJumppadsDelay = 0;
80      m_iCheckEntsDelay = 0;
81      m_iCheckTriggersDelay = 0;
82      m_iLookForWaypointTime = 0;
83 
84      m_iAimDelay = 0;
85      m_fYawToTurn = m_fPitchToTurn = 0.0f;
86 
87      m_vGoal = m_vWaterGoal = g_vecZero;
88 
89      ResetWaypointVars();
90 }
91 
Think()92 void CBot::Think()
93 {
94     if (intermission) return;
95     // Bot is dead?
96     if (m_pMyEnt->state == CS_DEAD)
97     {
98         if(lastmillis-m_pMyEnt->respawnoffset<1200)
99         {
100             m_pMyEnt->move = 0;
101             moveplayer(m_pMyEnt, 1, true);
102         }
103         else if (!m_arena && lastmillis-m_pMyEnt->respawnoffset>5000) Spawn();
104         SendBotInfo();
105         return;
106     }
107     CheckItemPickup();
108     TLinkedList<unreachable_ent_s*>::node_s *p = m_UnreachableEnts.GetFirst(), *tmp;
109     while(p)
110     {
111         if ((lastmillis - p->Entry->time) > 3500)
112         {
113             tmp = p;
114             p = p->next;
115             delete tmp->Entry;
116             m_UnreachableEnts.DeleteNode(tmp);
117             continue;
118         }
119         p = p->next;
120     }
121     if (!BotManager.IdleBots()) { MainAI(); }
122     else { ResetMoveSpeed(); }
123     // Aim to ideal yaw and pitch
124     AimToIdeal();
125     // Store current location, to see if the bot is stuck
126     m_vPrevOrigin = m_pMyEnt->o;
127     // Don't check for stuck if the bot doesn't want to move
128     if (!m_pMyEnt->move && !m_pMyEnt->strafe) m_iStuckCheckDelay = max(m_iStuckCheckDelay, lastmillis+100.0f);
129     // Move the bot
130     moveplayer(m_pMyEnt, 1, true);
131     // Update bot info on all clients
132     SendBotInfo();
133 }
134 
AimToVec(const vec & o)135 void CBot::AimToVec(const vec &o)
136 {
137      m_pMyEnt->targetpitch = atan2(o.z-m_pMyEnt->o.z, GetDistance(o))*180/PI;
138      m_pMyEnt->targetyaw = -(float)atan2(o.x - m_pMyEnt->o.x, o.y -
139                                          m_pMyEnt->o.y)/PI*180+180;
140 }
141 
AimToIdeal()142 void CBot::AimToIdeal()
143 {
144      if (m_iAimDelay > lastmillis)
145           return;
146 
147      float MaxXSpeed = RandomFloat(m_pBotSkill->flMinAimXSpeed,
148                                    m_pBotSkill->flMaxAimXSpeed);
149      float MaxYSpeed = RandomFloat(m_pBotSkill->flMinAimYSpeed,
150                                    m_pBotSkill->flMaxAimYSpeed);
151      float XOffset = RandomFloat(m_pBotSkill->flMinAimXOffset,
152                                  m_pBotSkill->flMaxAimXOffset);
153      float YOffset = RandomFloat(m_pBotSkill->flMinAimYOffset,
154                                  m_pBotSkill->flMaxAimYOffset);
155      float RealXOffset, RealYOffset;
156      float AimXSpeed = MaxXSpeed, AimYSpeed = MaxYSpeed;
157      float XDiff = fabs(m_pMyEnt->targetpitch - m_pMyEnt->pitch);
158      float YDiff = fabs(m_pMyEnt->targetyaw - m_pMyEnt->yaw);
159 
160      // How higher the diff, how higher the offsets and aim speed
161 
162      if (XOffset)
163      {
164           if (RandomLong(0, 1))
165                RealXOffset = XDiff * (XOffset / 100.0f);
166           else
167                RealXOffset = -(XDiff * (XOffset / 100.0f));
168      }
169      else
170           RealXOffset = 0.0f;
171 
172      if (YOffset)
173      {
174           if (RandomLong(0, 1))
175                RealYOffset = YDiff * (YOffset / 100.0f);
176           else
177                RealYOffset = -(YDiff * (YOffset / 100.0f));
178      }
179      else
180           RealYOffset = 0.0f;
181 
182 
183      if (XDiff >= 1.0f)
184           AimXSpeed = (AimXSpeed * (XDiff / 80.0f)) + (AimXSpeed * 0.25f);
185      else
186           AimXSpeed *= 0.01f;
187 
188      if (YDiff >= 1.0f)
189           AimYSpeed = (AimYSpeed * (YDiff / 70.0f)) + (AimYSpeed * 0.25f);
190      else
191           AimYSpeed *= 0.015f;
192 
193      m_fPitchToTurn = fabs((m_pMyEnt->targetpitch + RealXOffset) - m_pMyEnt->pitch);
194      m_fYawToTurn = fabs((m_pMyEnt->targetyaw + RealYOffset) - m_pMyEnt->yaw);
195 
196      float flIdealPitch = ChangeAngle(AimXSpeed, m_pMyEnt->targetpitch + RealXOffset, m_pMyEnt->pitch);
197      float flIdealYaw = ChangeAngle(AimYSpeed, m_pMyEnt->targetyaw + RealYOffset, m_pMyEnt->yaw);
198 
199 //     m_pMyEnt->pitch = WrapXAngle(m_pMyEnt->targetpitch); // Uncomment for instant aiming
200 //     m_pMyEnt->yaw = WrapYZAngle(m_pMyEnt->targetyaw);
201 
202      m_pMyEnt->pitch = WrapXAngle(flIdealPitch);
203      m_pMyEnt->yaw = WrapYZAngle(flIdealYaw);
204 }
205 
206 // Function code by Botman
ChangeAngle(float speed,float ideal,float current)207 float CBot::ChangeAngle(float speed, float ideal, float current)
208 {
209      float current_180;  // current +/- 180 degrees
210      float diff;
211 
212      // find the difference in the current and ideal angle
213      diff = fabs(current - ideal);
214 
215      // speed that we can turn during this frame...
216      speed = speed * (float(BotManager.m_iFrameTime)/1000.0f);
217 
218      // check if difference is less than the max degrees per turn
219      if (diff < speed)
220           speed = diff;  // just need to turn a little bit (less than max)
221 
222      // check if the bot is already facing the idealpitch direction...
223      if (diff <= 0.5f)
224           return ideal;
225 
226      if ((current >= 180) && (ideal >= 180))
227      {
228           if (current > ideal)
229                current -= speed;
230           else
231                current += speed;
232      }
233      else if ((current >= 180) && (ideal < 180))
234      {
235           current_180 = current - 180;
236 
237           if (current_180 > ideal)
238                current += speed;
239           else
240                current -= speed;
241      }
242      else if ((current < 180) && (ideal >= 180))
243      {
244           current_180 = current + 180;
245 
246           if (current_180 > ideal)
247                current += speed;
248           else
249                current -= speed;
250      }
251      else  // (current < 180) && (ideal < 180)
252      {
253           if (current > ideal)
254                current -= speed;
255           else
256                current += speed;
257      }
258 
259 
260      return current;
261 }
262 
SendBotInfo()263 void CBot::SendBotInfo()
264 {
265      if(lastmillis-m_iLastBotUpdate<40) return;    // don't update faster than 25fps
266      m_iLastBotUpdate = lastmillis;
267 }
268 
GetDistance(const vec & o)269 float CBot::GetDistance(const vec &o)
270 {
271      return o.dist(m_pMyEnt->o);
272 }
273 
GetDistance(const vec & v1,const vec & v2)274 float CBot::GetDistance(const vec &v1, const vec &v2)
275 {
276      return v2.dist(v1);
277 }
278 
GetDistance(entity * e)279 float CBot::GetDistance(entity *e)
280 {
281      vec v(e->x, e->y, e->z);
282      return v.dist(m_pMyEnt->o);
283 }
284 
SelectGun(int Gun)285 bool CBot::SelectGun(int Gun)
286 {
287     if(!m_pMyEnt->weapons[Gun]->selectable()) return false;
288     if(m_pMyEnt->weaponsel->reloading || m_pMyEnt->weaponchanging) return false;
289     m_pMyEnt->gunselect = Gun;
290     if(m_pMyEnt->weaponsel->type != Gun)
291     {
292         audiomgr.playsound(S_GUNCHANGE, m_pMyEnt);
293         m_pMyEnt->weaponswitch(m_pMyEnt->weapons[Gun]);
294         m_iChangeWeaponDelay = lastmillis + 1000;
295     }
296     return true;
297 }
298 
IsVisible(entity * e,bool CheckPlayers)299 bool CBot::IsVisible(entity *e, bool CheckPlayers)
300 {
301      vec v(e->x, e->y, e->z);
302      return ::IsVisible(m_pMyEnt->o, v, (CheckPlayers) ? m_pMyEnt : NULL);
303 }
304 
IsVisible(vec o,int Dir,float flDist,bool CheckPlayers,float * pEndDist)305 bool CBot::IsVisible(vec o, int Dir, float flDist, bool CheckPlayers, float *pEndDist)
306 {
307      static vec angles, end, forward, right, up;
308      static traceresult_s tr;
309 
310      end = o;
311      angles = GetViewAngles();
312      angles.x = 0;
313 
314      if (Dir & UP)
315           angles.x = WrapXAngle(angles.x + 45.0f);
316      else if (Dir & DOWN)
317           angles.x = WrapXAngle(angles.x - 45.0f);
318 
319      if ((Dir & FORWARD) || (Dir & BACKWARD))
320      {
321           if (Dir & BACKWARD)
322                angles.y = WrapYZAngle(angles.y + 180.0f);
323 
324           if (Dir & LEFT)
325           {
326                if (Dir & FORWARD)
327                     angles.y = WrapYZAngle(angles.y - 45.0f);
328                else
329                     angles.y = WrapYZAngle(angles.y + 45.0f);
330           }
331           else if (Dir & RIGHT)
332           {
333                if (Dir & FORWARD)
334                     angles.y = WrapYZAngle(angles.y + 45.0f);
335                else
336                     angles.y = WrapYZAngle(angles.y - 45.0f);
337           }
338      }
339      else if (Dir & LEFT)
340           angles.y = WrapYZAngle(angles.y - 90.0f);
341      else if (Dir & RIGHT)
342           angles.y = WrapYZAngle(angles.y + 90.0f);
343      else if (Dir & UP)
344           angles.x = WrapXAngle(angles.x + 90.0f);
345      else if (Dir & DOWN)
346           angles.x = WrapXAngle(angles.x - 90.0f);
347 
348      AnglesToVectors(angles, forward, right, up);
349 
350      forward.mul(flDist);
351      end.add(forward);
352 
353      TraceLine(o, end, m_pMyEnt, CheckPlayers, &tr);
354 
355      if (pEndDist)
356           *pEndDist = GetDistance(o, tr.end);
357 
358      return !tr.collided;
359 }
360 
SetMoveDir(int iMoveDir,bool add)361 void CBot::SetMoveDir(int iMoveDir, bool add)
362 {
363      if (iMoveDir & FORWARD)
364           m_pMyEnt->move = 1;
365      else if (m_iMoveDir & BACKWARD)
366           m_pMyEnt->move = -1;
367      else if (!add)
368           m_pMyEnt->move = 0;
369 
370      if (iMoveDir & LEFT)
371           m_pMyEnt->strafe = 1;
372      else if (m_iMoveDir & RIGHT)
373           m_pMyEnt->strafe = -1;
374      else if (!add)
375           m_pMyEnt->strafe = 0;
376 
377      if (iMoveDir & UP)
378           m_pMyEnt->jumpnext = true;
379 }
380 
381 // Used when switching to another task/state
ResetCurrentTask()382 void CBot::ResetCurrentTask()
383 {
384      switch (m_eCurrentBotState)
385      {
386      case STATE_ENEMY:
387           m_pMyEnt->enemy = NULL;
388           m_pTargetEnt = NULL;
389           m_iCombatNavTime = m_iMoveDir = 0;
390           m_bCombatJump = false;
391           m_vGoal = g_vecZero;
392           break;
393      case STATE_ENT:
394           m_pTargetEnt = NULL;
395           m_vGoal = g_vecZero;
396           break;
397      case STATE_SP:
398           m_iSPMoveTime = m_iMoveDir = 0;
399           m_pTargetEnt = NULL;
400           m_vGoal = g_vecZero;
401           break;
402      case STATE_NORMAL:
403           m_iStrafeTime = m_iMoveDir = 0;
404           break;
405      default:
406           break;
407      }
408 }
409 
410 // Bot class end
411