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