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