1 //
2 // C++ Implementation: bot_util
3 //
4 // Description: Basic util functions, mostly bot related
5 //
6 //
7 // Author: <rickhelmus@gmail.com>
8 //
9 //
10 //
11 //
12
13 #include "cube.h"
14 #include "bot.h"
15
16 // Function code by PMB - Begin
17
18
19 // Random functions begin
20 // copied from
21 // http://forums.bots-united.com/showthread.php?t=244&page=1&pp=10&highlight=random
22 // So credits to PMB and the person(s) who actually made it :]
23
24 // maximum value returned by our number generator (2^31 - 1 = 0x7FFFFFFF)
25 #define LRAND_MAX 2147483647L
26 long lseed; // our random number generator's seed
27
lrand(void)28 long lrand (void)
29 {
30 // this function is the equivalent of the rand() standard C library function,
31 // except that whereas rand() works only with short integers
32 // (i.e. not above 32767), this function is able to generate 32-bit random
33 // numbers. Isn't that nice ?
34 // credits go to Ray Gardner for his fast implementation of minimal random
35 // number generators
36 // http://c.snippets.org/snip_lister.php?fname=rg_rand.c
37
38 // compose the two 16-bit parts of the long integer and assemble them
39 static unsigned long lrand_lo, lrand_hi;
40 lrand_lo = 16807 * (long) (lseed & 0xFFFF); // low part
41 lrand_hi = 16807 * (long) ((unsigned long) lseed >> 16);
42 // high part
43 lrand_lo += (lrand_hi & 0x7FFF) << 16;
44 // assemble both in lrand_lo
45 // is the resulting number greater than LRAND_MAX (half the capacity) ?
46 if (lrand_lo > LRAND_MAX)
47 {
48 lrand_lo &= LRAND_MAX; // then get rid of the disturbing bit
49 lrand_lo++; // and increase it a bit (to avoid overflow problems, I suppose)
50 }
51
52 lrand_lo += lrand_hi >> 15;
53 // now do twisted maths to generate the next seed
54 // is the resulting number greater than LRAND_MAX (half the capacity) ?
55 if (lrand_lo > LRAND_MAX)
56 {
57 lrand_lo &= LRAND_MAX; // then get rid of the disturbing bit
58 lrand_lo++; // and increase it a bit (to avoid overflow problems, I suppose)
59 }
60 // now we've got our (pseudo-)random number.
61 lseed = (long) lrand_lo; // put it in the seed for next time
62 return (lseed); // and return it. Yeah, simple as that.
63 }
64
lsrand(unsigned long initial_seed)65 void lsrand (unsigned long initial_seed)
66 {
67 // this function initializes the random seed based on the initial seed value
68 // passed in the initial_seed parameter. Since random seeds are
69 // usually initialized with the time of day, and time is a value that
70 // changes slowly, we bump the seed twice to have it in a more
71 // random state for future calls of the random number generator.
72
73 // fill in the initial seed of the random number generator
74 lseed = (long) initial_seed;
75 lseed = lrand (); // bump it once
76 lseed = lrand (); // bump it twice
77 return; // that's all folks
78 }
79
RandomLong(long from,long to)80 long RandomLong (long from, long to)
81 {
82 // this function returns a random integer number between (and including)
83 // the starting and ending values passed by parameters from and to.
84 if (to <= from)
85 return (from);
86
87 return (from + lrand () / (LRAND_MAX / (to - from + 1)));
88 }
89
RandomFloat(float from,float to)90 float RandomFloat (float from, float to)
91 {
92 // this function returns a random floating-point number between (and including)
93 // the starting and ending values passed by parameters from and to.
94
95 if (to <= from)
96 return (from);
97
98 return (from + (float) lrand () / (LRAND_MAX / (to - from)));
99 }
100
101 // End random functions
102
AnglesToVectors(vec angles,vec & forward,vec & right,vec & up)103 void AnglesToVectors(vec angles, vec &forward, vec &right, vec &up)
104 {
105 static float degrees_to_radians = 2 * PI / 360;
106 float angle;
107 float sin_pitch;
108 float sin_yaw;
109 float sin_roll;
110 float cos_pitch;
111 float cos_yaw;
112 float cos_roll;
113
114 // For some reason this has to be done :)
115 // Me = math n00b
116 angles.x = -angles.x;
117 angles.y -= 90;
118
119 // compute the sin and cosine of the pitch component
120 angle = angles.x * degrees_to_radians;
121 sin_pitch = sinf (angle);
122 cos_pitch = cosf (angle);
123
124 // compute the sin and cosine of the yaw component
125 angle = angles.y * degrees_to_radians;
126 sin_yaw = sinf (angle);
127 cos_yaw = cosf (angle);
128
129 // compute the sin and cosine of the roll component
130 angle = angles.z * degrees_to_radians;
131 sin_roll = sinf (angle);
132 cos_roll = cosf (angle);
133
134 // build the FORWARD vector
135 forward.x = cos_pitch * cos_yaw;
136 forward.y = cos_pitch * sin_yaw;
137 forward.z = -sin_pitch;
138
139 // build the RIGHT vector
140 right.x = -(-(sin_roll * sin_pitch * cos_yaw) - (cos_roll * -sin_yaw));
141 right.y = -(-(sin_roll * sin_pitch * sin_yaw) - (cos_roll * cos_yaw));
142 right.z = -(sin_roll * cos_pitch);
143
144 // build the UPWARDS vector
145 up.x = ((cos_roll * sin_pitch * cos_yaw) - (sin_roll * -sin_yaw));
146 up.y = ((cos_roll * sin_pitch * sin_yaw) - (sin_roll * cos_yaw));
147 up.z = cos_roll * cos_pitch;
148
149 return;
150 }
151
152 // Function code by PMB - End
153
WrapXAngle(float angle)154 float WrapXAngle(float angle)
155 {
156 if (angle > 90) angle = 90;
157 else if (angle < -90) angle = -90;
158
159 return angle;
160 }
161
WrapYZAngle(float angle)162 float WrapYZAngle(float angle)
163 {
164 while (angle >= 360.0f) angle -= 360.0f;
165 while (angle < 0.0f) angle += 360.0f;
166
167 return angle;
168 }
169
170 // Sees how far it can come from 'from' to 'to' and puts the end vector in 'end'
171 // (1337 description :-)
172 // UNDONE: Optimize this as much as possible
TraceLine(vec from,vec to,dynent * pTracer,bool CheckPlayers,traceresult_s * tr,bool SkipTags)173 void TraceLine(vec from, vec to, dynent *pTracer, bool CheckPlayers, traceresult_s *tr,
174 bool SkipTags)
175 {
176 tr->end = from;
177 tr->collided = false;
178
179 static float flNearestDist, flDist;
180 static vec v;
181 static bool solid;
182
183 flNearestDist = 9999.0f;
184
185 if(OUTBORD((int)from.x, (int)from.y)) return;
186
187 // Check if the 'line' collides with entities like mapmodels
188 loopv(ents)
189 {
190 entity &e = ents[i];
191 if(e.type!=MAPMODEL) continue; // Only check map models for now
192
193 mapmodelinfo &mmi = getmminfo(e.attr2);
194 if(!&mmi || !mmi.h) continue;
195
196 float lo = (float)(S(e.x, e.y)->floor+mmi.zoff+e.attr3);
197 float hi = lo+mmi.h;
198 float z = (fabs(from.z-lo) < fabs(from.z-hi)) ? lo : hi;
199 makevec(&v, e.x, e.y, z);
200 flDist = GetDistance(from, v);
201
202 if ((flDist < flNearestDist) && (intersect(&e, from, to, &tr->end)))
203 {
204 flNearestDist = GetDistance(from, tr->end);
205 tr->collided = true;
206 }
207 }
208
209 if (CheckPlayers)
210 {
211 // Check if the 'line' collides with players
212 loopv(players)
213 {
214 playerent *d = players[i];
215 if(!d || (d==pTracer) || (d->state != CS_ALIVE)) continue; // Only check valid players
216
217 flDist = GetDistance(from, d->o);
218
219 if ((flDist < flNearestDist) && (intersect(d, from, to, &tr->end)))
220 {
221 flNearestDist = flDist;
222 tr->collided = true;
223 }
224 }
225
226 // Check if the 'line' collides with the local player(player1)
227 playerent *d = player1; // Shortcut
228 if (d && (d!=pTracer) && !BotManager.m_pBotToView && (d->state == CS_ALIVE))
229 {
230 flDist = GetDistance(from, d->o);
231
232 if ((flDist < flNearestDist) && (intersect(d, from, to, &tr->end)))
233 {
234 flNearestDist = flDist;
235 tr->collided = true;
236 }
237 }
238 }
239
240
241 float dx = to.x-from.x;
242 float dy = to.y-from.y;
243 int steps = (int)(sqrt(dx*dx+dy*dy)/0.9);
244 if(!steps) // If from and to are on the same cube...
245 {
246 if (GetDistance(from, to) < flNearestDist)
247 {
248 tr->end = to;
249 sqr *s = S(int(from.x), int(from.y));
250 float flr = GetCubeFloor(int(from.x), int(from.y));
251 solid = ((!SkipTags || !s->tag) && SOLID(s));
252 if (solid || (to.z < flr) || (to.z > s->ceil))
253 {
254 tr->collided = true;
255 tr->end.z = (fabs(to.z-s->ceil) < fabs(to.z-flr)) ? s->ceil : flr;
256 }
257 }
258 return;
259 }
260
261 float x = from.x;
262 float y = from.y;
263 int i = 0;
264 vec endcube = from;
265
266 // Now check if the 'line' is hit by a cube
267 while(i<steps)
268 {
269 if(OUTBORD((int)x, (int)y)) break;
270 if (GetDistance(from, endcube) >= flNearestDist) break;
271 sqr *s = S(int(x), int(y));
272 solid = ((!SkipTags || !s->tag) && SOLID(s));
273 if(solid) break;
274 float floor = s->floor;
275 if(s->type==FHF) floor -= s->vdelta/4.0f;
276 float ceil = s->ceil;
277 if(s->type==CHF) ceil += s->vdelta/4.0f;
278 float rz = from.z-((from.z-to.z)*(i/(float)steps));
279 if(rz<floor || rz>ceil) break;
280
281 endcube.x = x;
282 endcube.y = y;
283 endcube.z = rz;
284 x += dx/(float)steps;
285 y += dy/(float)steps;
286 i++;
287 }
288
289 if ((i>=steps) && !tr->collided)
290 {
291 tr->end = to;
292 }
293 else
294 {
295 tr->collided = true;
296 if (GetDistance(from, endcube) < flNearestDist)
297 tr->end = endcube;
298 }
299
300 return;
301 }
302
GetDistance(vec v1,vec v2)303 float GetDistance(vec v1, vec v2)
304 {
305 if ((v1.x == 0) && (v2.x == 0) && (v1.y == 0) && (v2.y == 0) && (v1.z == 0) &&
306 (v2.z == 0))
307 return 0.0f;
308
309 return v1.dist(v2);
310 }
311
312 // As GetDistance(), but doesn take height in account.
Get2DDistance(vec v1,vec v2)313 float Get2DDistance(vec v1, vec v2)
314 {
315 if ((v1.x == 0) && (v2.x == 0) && (v1.y == 0) && (v2.y == 0))
316 return 0.0f;
317
318 v1.z = v2.z = 0.0f;
319 return v1.dist(v2);
320 }
321
IsVisible(vec v1,vec v2,dynent * tracer,bool SkipTags)322 bool IsVisible(vec v1, vec v2, dynent *tracer, bool SkipTags)
323 {
324 traceresult_s tr;
325 TraceLine(v1, v2, tracer, (tracer!=NULL), &tr, SkipTags);
326 return !tr.collided;
327 }
328
329 // Prediction:
330 // - pos: Current position
331 // - vel: Current velocity
332 // - Time: In seconds, predict how far it is in Time seconds
PredictPos(vec pos,vec vel,float Time)333 vec PredictPos(vec pos, vec vel, float Time)
334 {
335 float flVelLength = vel.magnitude();
336
337 if (flVelLength <= 1.0)
338 return pos; // don't bother with low velocities...
339
340 vec v_end = vel;
341 v_end.mul(Time);
342 v_end.add(pos);
343 return v_end;
344 }
345
346 // returns true if the given file is valid
IsValidFile(const char * szFileName)347 bool IsValidFile(const char *szFileName)
348 {
349 FILE *fp = fopen(szFileName, "r");
350
351 if (fp)
352 {
353 fclose(fp);
354 return true;
355 }
356
357 return false;
358 }
359
FileIsOlder(const char * szFileName1,const char * szFileName2)360 bool FileIsOlder(const char *szFileName1, const char *szFileName2)
361 {
362 /* int file1, file2;
363 struct stat stat1, stat2;
364
365 file1 = open(szFileName1, O_RDONLY);
366 file2 = open(szFileName2, O_RDONLY);
367
368 fstat(file1, &stat1);
369 fstat(file2, &stat2);
370
371 close(file1);
372 close(file2);
373
374 return(stat1.st_mtime < stat2.st_mtime);*/ return false; // FIXME
375 }
376
Normalize(vec v)377 vec Normalize(vec v)
378 {
379 float flLen = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
380 static vec v_norm = g_vecZero;
381 if (flLen == 0)
382 {
383 v_norm.x=0;v_norm.y=0;v_norm.z=1;
384 }
385 else
386 {
387 flLen = 1 / flLen;
388 v_norm.x = v.x * flLen; v_norm.y = v.y * flLen; v_norm.z = v.z * flLen;
389 }
390 return v_norm;
391 }
392
GetYawDiff(float curyaw,vec v1,vec v2)393 float GetYawDiff(float curyaw, vec v1, vec v2)
394 {
395 float yaw = -(float)atan2(v2.x - v1.x, v2.y - v1.y)/PI*180+180;
396 return yaw-curyaw;
397 }
398
CrossProduct(const vec & a,const vec & b)399 vec CrossProduct(const vec &a, const vec &b)
400 {
401 vec cross(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x);
402 return cross;
403 }
404
GetDirection(const vec & angles,const vec & v1,const vec & v2)405 int GetDirection(const vec &angles, const vec &v1, const vec &v2)
406 {
407 vec tmp, forward, right, up, cross;
408 float flDot;
409
410 AnglesToVectors(angles, forward, right, up);
411
412 tmp = v2;
413 tmp.sub(v1);
414 tmp.z = 0.0f; // Make 2D
415 tmp = Normalize(tmp);
416 forward.z = 0; // Make 2D
417
418 flDot = tmp.dot(forward);
419 cross = CrossProduct(tmp, forward);
420
421 int Dir = DIR_NONE;
422
423 if (cross.z > 0.1f) Dir |= LEFT;
424 else if (cross.z < -0.1f) Dir |= RIGHT;
425 if (flDot < -0.1f) Dir |= BACKWARD;
426 else if (flDot > 0.1f) Dir |= FORWARD;
427
428 if (v1.z > v2.z) Dir |= DOWN;
429 if (v1.z < v2.z) Dir |= UP;
430
431 return Dir;
432 }
433
GetCubeFloor(int x,int y)434 float GetCubeFloor(int x, int y)
435 {
436 sqr *s = S(x, y);
437 float floor = s->floor;
438 if (s->type == FHF)
439 floor -= (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f;
440 return floor;
441 }
442
GetCubeHeight(int x,int y)443 float GetCubeHeight(int x, int y)
444 {
445 sqr *s = S(x, y);
446 float ceil = s->ceil;
447 if(s->type == CHF)
448 ceil += (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f;
449 return ceil;
450 }
451
SkillNrToSkillName(short skillnr)452 const char *SkillNrToSkillName(short skillnr)
453 {
454 if (skillnr == 0) return "best";
455 else if (skillnr == 1) return "good";
456 else if (skillnr == 2) return "medium";
457 else if (skillnr == 3) return "worse";
458 else if (skillnr == 4) return "bad";
459
460 return NULL;
461 }
462
IsInGame(dynent * d)463 bool IsInGame(dynent *d)
464 {
465 if (!d) return false;
466
467 if (d->type==ENT_BOT)
468 {
469 loopv(bots)
470 {
471 if (bots[i] == d)
472 return true;
473 }
474 }
475 else
476 {
477 if (d == player1)
478 return true;
479 else
480 {
481 loopv(players)
482 {
483 #ifdef VANILLA_CUBE
484 if (!players[i] || (players[i]->state == CS_DEDHOST)) continue;
485 #elif defined(AC_CUBE)
486 if (!players[i]) continue;
487 #endif
488 if (players[i] == d)
489 return true;
490 }
491 }
492 }
493 return false;
494 }
495