1 //
2 // C++ Implementation: bot_waypoint
3 //
4 // Description: Waypoint class for bots
5 //
6 //
7 // Author: Rick <rickhelmus@gmail.com>
8 //
9 
10 #include "cube.h"
11 #include "bot.h"
12 
13 vec v_debuggoal = g_vecZero;
14 
15 #if defined AC_CUBE
16 CACWaypointClass WaypointClass;
17 #elif defined VANILLA_CUBE
18 CCubeWaypointClass WaypointClass;
19 #endif
20 
21 VAR(xhairwpsel, 0, 1, 1);
22 // FIXME: multiple selections support ?
23 // well, TBH I haven't groked this myself yet, but the following curselection segfaults the client, so I'm just putting in a sane-variant until someone groks it ;-) - ft 2011sep28
24 extern vector<block> sels;
25 //#define curselection (xhairwpsel ? vec(sels.last().x, sels.last().y, S(sels.last().x, sels.last().y)->floor+2.0f) : vec(player1->o.x, player1->o.y, player1->o.z))
26 #define curselection (vec(player1->o.x, player1->o.y, player1->o.z))
27 
28 // Waypoint class begin
29 
CWaypointClass(void)30 CWaypointClass::CWaypointClass(void) : m_bDrawWaypoints(false), m_bDrawWPPaths(true),
31                                        m_bAutoWaypoint(false),  m_bAutoPlacePaths(true),
32                                        m_bDrawWPText(false), m_vLastCreatedWP(g_vecZero),
33                                        m_bFlooding(false), m_bFilteringNodes(false),
34                                        m_iFloodStartTime(0), m_iCurFloodX(0),
35                                        m_iCurFloodY(0), m_iFloodSize(0),
36                                        m_iFilteredNodes(0), m_iWaypointCount(0)
37 {
38      loopi(MAX_MAP_GRIDS)
39      {
40           loopj(MAX_MAP_GRIDS)
41           {
42                while(!m_Waypoints[i][j].Empty())
43                     delete m_Waypoints[i][j].Pop();
44           }
45      }
46 
47      m_szMapName[0] = 0;
48 }
49 
Init()50 void CWaypointClass::Init()
51 {
52      Clear();  // Free previously allocated path memory
53 
54      loopi(MAX_MAP_GRIDS)
55      {
56           loopj(MAX_MAP_GRIDS)
57           {
58                while(!m_Waypoints[i][j].Empty())
59                     delete m_Waypoints[i][j].Pop();
60           }
61      }
62 
63      m_fPathDrawTime = 0.0;
64      m_iWaypointCount = 0;
65      m_vLastCreatedWP = g_vecZero;
66 }
67 
Clear()68 void CWaypointClass::Clear()
69 {
70      for(int i=0;i<MAX_MAP_GRIDS;i++)
71      {
72           for(int j=0;j<MAX_MAP_GRIDS;j++)
73           {
74                while(!m_Waypoints[i][j].Empty())
75                {
76                     node_s *p = m_Waypoints[i][j].Pop();
77                     p->ConnectedWPs.DeleteAllNodes();
78                     p->ConnectedWPsWithMe.DeleteAllNodes();
79                     BotManager.DelWaypoint(p);
80                     delete p;
81                }
82           }
83      }
84 }
85 
86 //fixme
87 TLinkedList<node_s *>::node_s *testx;
88 
89 // returns true if waypoints succesfull loaded
LoadWaypoints()90 bool CWaypointClass::LoadWaypoints()
91 {
92      stream *bfp;
93      char szWPFileName[64];
94      char filename[256];
95      waypoint_header_s header;
96      short num, triggernr, yaw;
97      int i, j, flags;
98      vec from, to;
99 
100      strcpy(szWPFileName, m_szMapName);
101      strcat(szWPFileName, ".wpt");
102 
103      BotManager.MakeBotFileName(szWPFileName, "waypoints", NULL, filename);
104 
105      bfp = openfile(filename, "rb");
106 
107      BotManager.m_sCurrentTriggerNr = -1;
108 
109      // if file exists, read the waypoint structure from it
110      if (bfp != NULL)
111      {
112           bfp->read(&header, sizeof(header));
113 
114           header.szFileType[sizeof(header.szFileType)-1] = 0;
115           if (strcmp(header.szFileType, "cube_bot") == 0)
116           {
117                header.szMapName[sizeof(header.szMapName)-1] = 0;
118 
119                if (strcmp(header.szMapName, m_szMapName) == 0)
120                {
121                     Init();  // remove any existing waypoints
122 
123                     // Check which waypoint file version this is, handle each of them different
124                     if (header.iFileVersion == 1)
125                     {
126                          // First version, works with an big array and has a limit
127 
128                          waypoint_version_1_s WPs[1024];
129                          int path_index;
130 
131                          m_iWaypointCount = header.iWPCount;
132                          for (i=0; i < header.iWPCount; i++)
133                          {
134                               bfp->read(&WPs[i], sizeof(WPs[0]));
135                               WPs[i].ConnectedWPs.Reset(); // We get garbage when we read this from
136                                                            // a file, so just clear it
137 
138                               // Convert to new waypoint structure
139                               node_s *pNode = new node_s(WPs[i].v_origin, WPs[i].iFlags, 0);
140 
141                               short x, y;
142                               GetNodeIndexes(pNode->v_origin, &x, &y);
143 
144                               m_Waypoints[x][y].AddNode(pNode);
145                               BotManager.AddWaypoint(pNode);
146                          }
147 
148                          // read and add waypoint paths...
149                          for (i=0; i < m_iWaypointCount; i++)
150                          {
151                               // read the number of paths from this node...
152                               bfp->read(&num, sizeof(num));
153 
154                               // See which waypoint the current is
155                               node_s *pCurrent = GetWaypointFromVec(WPs[i].v_origin);
156 
157                               if (!pCurrent)
158                               {
159                                    conoutf("Error: NULL path in waypoint file");
160                                    continue;
161                               }
162 
163                               for (j=0; j < num; j++)
164                               {
165                                    bfp->read(&path_index, sizeof(path_index));
166 
167                                    // See which waypoint this is
168                                    node_s *pTo = GetWaypointFromVec(WPs[path_index].v_origin);
169 
170                                    if (!pTo)
171                                    {
172                                         conoutf("Error: NULL path in waypoint file");
173                                         continue;
174                                    }
175 
176                                    AddPath(pCurrent, pTo);
177                               }
178                          }
179                          conoutf("Old waypoint version(%d) converted to new version(%d)",
180                                   header.iFileVersion, WAYPOINT_VERSION);
181                          conoutf("Use the wpsave command to convert permently");
182 
183                     }
184                     else if (header.iFileVersion == 2)
185                     {
186                          m_iWaypointCount = header.iWPCount;
187 
188                          for (i=0; i < header.iWPCount; i++)
189                          {
190                               bfp->read(&from, sizeof(from)); // Read origin
191                               bfp->read(&flags, sizeof(flags)); // Read waypoint flags
192                               bfp->read(&triggernr, sizeof(triggernr)); // Read trigger nr
193                               bfp->read(&yaw, sizeof(yaw)); // Read target yaw
194 
195                               node_s *pNode = new node_s(from, flags, triggernr, yaw);
196 
197                               short x, y;
198                               GetNodeIndexes(pNode->v_origin, &x, &y);
199 
200                               m_Waypoints[x][y].AddNode(pNode);
201                               BotManager.AddWaypoint(pNode);
202 
203                               if ((BotManager.m_sCurrentTriggerNr == -1) ||
204                                   (triggernr < BotManager.m_sCurrentTriggerNr))
205                                   BotManager.m_sCurrentTriggerNr = triggernr;
206                          }
207 
208                          for (i=0; i < header.iWPCount; i++)
209                          {
210                               // read the number of paths from this node...
211                               bfp->read(&num, sizeof(num));
212                               bfp->read(&from, sizeof(from));
213 
214                               if (!num)
215                                    continue;
216 
217                               node_s *pCurrent = GetWaypointFromVec(from);
218 
219                               if (!pCurrent)
220                               {
221                                    conoutf("Error: NULL path in waypoint file");
222 
223                                    for(j=0;j<num;j++) bfp->read(&to, sizeof(to)); // Read rest of block
224                                    continue;
225                               }
226 
227                               for (j=0; j < num; j++)
228                               {
229                                    bfp->read(&to, sizeof(to));
230                                    node_s *p = GetWaypointFromVec(to);
231                                    if (p)
232                                         AddPath(pCurrent, p);
233                               }
234                          }
235                     }
236                     else if (header.iFileVersion > WAYPOINT_VERSION)
237                     {
238                          conoutf("Error: Waypoint file is newer then current, upgrade cube bot.");
239                          delete bfp;
240                          return false;
241                     }
242                     else
243                     {
244                          conoutf("Error: Unknown waypoint version for cube bot");
245                          delete bfp;
246                          return false;
247                     }
248                }
249                else
250                {
251                     conoutf("Waypoints aren't for map %s but for map %s", m_szMapName,
252                                   header.szMapName);
253                     delete bfp;
254                     return false;
255                }
256           }
257           else
258           {
259                conoutf("Waypoint file isn't for cube bot");
260                //conoutf("Header FileType: %s", int(header.szFileType));
261 
262                delete bfp;
263                return false;
264           }
265 
266           delete bfp;
267 
268           //RouteInit();
269      }
270      else
271      {
272           conoutf("Waypoint file %s does not exist", (filename));
273           return false;
274      }
275 
276      if (BotManager.m_sCurrentTriggerNr == -1)
277           BotManager.m_sCurrentTriggerNr = 0;
278 
279      ReCalcCosts();
280 
281      conoutf("Waypoints for map %s loaded", (m_szMapName));
282 
283      testx = m_Waypoints[1][1].pNodeList;
284 
285      return true;
286 }
287 
SaveWaypoints()288 void CWaypointClass::SaveWaypoints()
289 {
290      char filename[256];
291      char mapname[64];
292      waypoint_header_s header;
293      short int num;
294      TLinkedList<node_s *>::node_s *pPath;
295 
296      strcpy(header.szFileType, "cube_bot");
297 
298      header.iFileVersion = WAYPOINT_VERSION;
299 
300      header.iFileFlags = 0;  // not currently used
301 
302      header.iWPCount = m_iWaypointCount;
303 
304      memset(header.szMapName, 0, sizeof(header.szMapName));
305      strncpy(header.szMapName, m_szMapName, sizeof(header.szMapName)-1);
306      header.szMapName[sizeof(header.szMapName)-1] = 0;
307 
308      strcpy(mapname, m_szMapName);
309      strcat(mapname, ".wpt");
310 
311      BotManager.MakeBotFileName(mapname, "waypoints", NULL, filename);
312 
313      stream *bfp = openfile(filename, "wb");
314 
315      if (!bfp)
316      {
317           conoutf("Error writing waypoint file, check if the directory \"bot/waypoint\" exists and "
318                        "the right permissions are set");
319           return;
320      }
321 
322      // write the waypoint header to the file...
323      bfp->write(&header, sizeof(header));
324 
325      // write the waypoint data to the file...
326      loopi(MAX_MAP_GRIDS)
327      {
328           loopj(MAX_MAP_GRIDS)
329           {
330                TLinkedList<node_s *>::node_s *p = m_Waypoints[i][j].GetFirst();
331                while(p)
332                {
333                     bfp->write(&p->Entry->v_origin, sizeof(p->Entry->v_origin)); // Write origin
334                     bfp->write(&p->Entry->iFlags, sizeof(p->Entry->iFlags)); // Write waypoint flags
335                     bfp->write(&p->Entry->sTriggerNr, sizeof(p->Entry->sTriggerNr)); // Write trigger nr
336                     bfp->write(&p->Entry->sYaw, sizeof(p->Entry->sYaw)); // Write target yaw
337 
338                     p = p->next;
339                }
340           }
341      }
342 
343      loopi(MAX_MAP_GRIDS)
344      {
345           loopj(MAX_MAP_GRIDS)
346           {
347                TLinkedList<node_s *>::node_s *p = m_Waypoints[i][j].GetFirst();
348                while(p)
349                {
350                     // save the waypoint paths...
351 
352                     // count the number of paths from this node...
353                     pPath = p->Entry->ConnectedWPs.GetFirst();
354                     num = p->Entry->ConnectedWPs.NodeCount();
355 
356                     bfp->write(&num, sizeof(num));  // write the count
357                     bfp->write(&p->Entry->v_origin, sizeof(p->Entry->v_origin)); // write the origin of this path
358 
359                     // now write out each path...
360                     while (pPath != NULL)
361                     {
362                          bfp->write(&pPath->Entry->v_origin, sizeof(pPath->Entry->v_origin));
363                          pPath = pPath->next;  // go to next node in linked list
364                     }
365                     p = p->next;
366                }
367           }
368      }
369 
370      delete bfp;
371 }
372 
LoadWPExpFile()373 bool CWaypointClass::LoadWPExpFile()
374 {
375      FILE *bfp;
376      char szWPFileName[64];
377      char filename[256];
378      waypoint_header_s header;
379      short int num;
380      int i, j;
381      vec from, to;
382 
383      if (m_iWaypointCount == 0)
384           return false;
385 
386      strcpy(szWPFileName, m_szMapName);
387      strcat(szWPFileName, ".exp");
388 
389      BotManager.MakeBotFileName(szWPFileName, "waypoints", NULL, filename);
390 
391      bfp = fopen(filename, "rb");
392 
393      // if file exists, read the waypoint structure from it
394      if (bfp != NULL)
395      {
396           fread(&header, sizeof(header), 1, bfp);
397 
398           header.szFileType[sizeof(header.szFileType)-1] = 0;
399           if (strcmp(header.szFileType, "cube_bot") == 0)
400           {
401                header.szMapName[sizeof(header.szMapName)-1] = 0;
402 
403                if (strcmp(header.szMapName, m_szMapName) == 0)
404                {
405                     // Check which waypoint file version this is, handle each of them different
406                     if (header.iFileVersion == 1)
407                     {
408                          for (i=0; i < header.iWPCount; i++)
409                          {
410                               // read the number of paths from this node...
411                               fread(&num, sizeof(num), 1, bfp);
412                               fread(&from, sizeof(vec), 1, bfp);
413 
414                               if (!num) continue;
415 
416                               node_s *pCurrent = GetWaypointFromVec(from);
417 
418                               if (!pCurrent)
419                               {
420                                    conoutf("Error: NULL node in waypoint experience file");
421                                    continue;
422                               }
423 
424                               for (j=0; j < num; j++)
425                               {
426                                    fread(&to, sizeof(vec), 1, bfp);
427                                    node_s *p = GetWaypointFromVec(to);
428                                    if (p)
429                                    {
430                                         pCurrent->FailedGoalList.AddNode(p);
431                                    }
432                               }
433                          }
434                     }
435                     else if (header.iFileVersion > EXP_WP_VERSION)
436                     {
437                          conoutf("Error: Waypoint experience file is newer then current, upgrade cube bot.");
438                          fclose(bfp);
439                          return false;
440                     }
441                     else
442                     {
443                          conoutf("Error: Unknown waypoint experience file version for cube bot");
444                          fclose(bfp);
445                          return false;
446                     }
447                }
448                else
449                {
450                     conoutf("Waypoint experience file isn't for map %s but for map %s", (m_szMapName),
451                                   (header.szMapName));
452                     fclose(bfp);
453                     return false;
454                }
455           }
456           else
457           {
458                conoutf("Waypoint experience file isn't for cube bot");
459                //conoutf("Header FileType: %s", int(header.szFileType));
460 
461                fclose(bfp);
462                return false;
463           }
464 
465           fclose(bfp);
466 
467           //RouteInit();
468      }
469      else
470      {
471           conoutf("Waypoint experience file %s does not exist", (filename));
472           return false;
473      }
474 
475      conoutf("Waypoint experience file for map %s loaded", (m_szMapName));
476      return true;
477 }
478 
SaveWPExpFile()479 void CWaypointClass::SaveWPExpFile()
480 {
481      if (!m_iWaypointCount)
482           return;
483 
484      char filename[256];
485      char mapname[64];
486      waypoint_header_s header;
487      short int num;
488 
489      strcpy(header.szFileType, "cube_bot");
490 
491      header.iFileVersion = EXP_WP_VERSION;
492 
493      header.iFileFlags = 0;  // not currently used
494 
495      header.iWPCount = m_iWaypointCount;
496 
497      memset(header.szMapName, 0, sizeof(header.szMapName));
498      strncpy(header.szMapName, m_szMapName, 31);
499      header.szMapName[31] = 0;
500 
501      strcpy(mapname, m_szMapName);
502      strcat(mapname, ".exp");
503 
504      BotManager.MakeBotFileName(mapname, "waypoints", NULL, filename);
505 
506      FILE *bfp = fopen(filename, "wb");
507 
508      if (!bfp)
509      {
510           conoutf("Error writing waypoint experience file, check if the directory \"bot/waypoint\" exists and "
511                        "the right permissions are set");
512           return;
513      }
514 
515      // write the waypoint header to the file...
516      fwrite(&header, sizeof(header), 1, bfp);
517 
518      // save the waypoint experience data...
519      loopi(MAX_MAP_GRIDS)
520      {
521           loopj(MAX_MAP_GRIDS)
522           {
523                TLinkedList<node_s *>::node_s *p = m_Waypoints[i][j].GetFirst();
524                while(p)
525                {
526                     // count the number of paths from this node...
527                     TLinkedList<node_s *>::node_s *p2 = p->Entry->FailedGoalList.GetFirst();
528                     num = p->Entry->FailedGoalList.NodeCount();
529 
530                     if (!num || !p2) continue;
531 
532                     fwrite(&num, sizeof(num), 1, bfp);  // write the count
533                     fwrite(&p->Entry->v_origin, sizeof(vec), 1, bfp); // write the origin of this node
534 
535                     while (p2 != NULL)
536                     {
537                          // Write out the node which a bot can't reach with the current node
538                          fwrite(&p2->Entry->v_origin, sizeof(vec), 1, bfp);
539                          p2 = p2->next;  // go to next node in linked list
540                     }
541                     p = p->next;
542                }
543           }
544      }
545 
546      fclose(bfp);
547 }
548 
Think()549 void CWaypointClass::Think()
550 {
551      if (dedserv) return;
552 
553 #ifdef WP_FLOOD
554      FloodThink();
555 #endif
556 
557      if (m_bAutoWaypoint) // is auto waypoint on?
558      {
559           // find the distance from the last used waypoint
560           float flDistance = GetDistance(m_vLastCreatedWP, player1->o);
561 
562           bool NoLastCreatedWP = ((m_vLastCreatedWP.x == 0) && (m_vLastCreatedWP.y == 0) &&
563                                                     (m_vLastCreatedWP.z == 0));
564 
565           if ((flDistance > 3.0f) || NoLastCreatedWP)
566           {
567                // check that no other reachable waypoints are nearby...
568                if (!GetNearestWaypoint(player1, 10.0f))
569                {
570                     AddWaypoint(player1->o, true);  // place a waypoint here
571                }
572           }
573      }
574 
575      // Draw info for nearest waypoint?
576      if (m_bDrawWPText)
577      {
578           node_s *nearestwp = GetNearestWaypoint(player1, 20.0f);
579 
580 #ifdef WP_FLOOD
581           if (!nearestwp)
582                nearestwp = GetNearestFloodWP(player1, 10.0f);
583 #endif
584 
585           if (nearestwp)
586           {
587                char szWPInfo[256];
588                sprintf(szWPInfo, "Distance nearest waypoint: %f", GetDistance(player1->o, nearestwp->v_origin));
589                AddScreenText(szWPInfo);
590 
591                strcpy(szWPInfo, "Flags: ");
592                if (nearestwp->iFlags & W_FL_TELEPORT)
593                     strcat(szWPInfo, "Teleport ");
594                if (nearestwp->iFlags & W_FL_TELEPORTDEST)
595                     strcat(szWPInfo, "Teleport destination ");
596                if (nearestwp->iFlags & W_FL_JUMP)
597                     strcat(szWPInfo, "Jump ");
598                if (nearestwp->iFlags & W_FL_TRIGGER)
599                {
600                     char sz[32];
601                     sprintf(sz, "Trigger(nr %d) ", nearestwp->sTriggerNr);
602                     strcat(szWPInfo, sz);
603                }
604                if (nearestwp->iFlags & W_FL_INTAG)
605                     strcat(szWPInfo, "In tagged cube(s) ");
606                if (strlen(szWPInfo) == 7)
607                     strcat(szWPInfo, "None");
608 
609                AddScreenText(szWPInfo);
610 
611                if (nearestwp->sYaw != -1)
612                     AddScreenText("yaw %d", nearestwp->sYaw);
613 
614                sprintf(szWPInfo, "Waypoint has %d connections",
615                        nearestwp->ConnectedWPs.NodeCount());
616                AddScreenText(szWPInfo);
617                AddScreenText("Base Cost: %d", nearestwp->sCost);
618           }
619      }
620 
621      if (m_bDrawWaypoints)  // display the waypoints if turned on...
622      {
623           DrawNearWaypoints();
624      }
625 }
626 
DrawNearWaypoints()627 void CWaypointClass::DrawNearWaypoints()
628 {
629     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
630     glDisable(GL_CULL_FACE);
631 
632      TLinkedList<node_s *>::node_s *pNode;
633      node_s *pNearest = NULL;
634      short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceilf(15.0f / MAX_MAP_GRIDS);
635      float flNearestDist = 9999.99f, flDist;
636 
637      GetNodeIndexes(player1->o, &i, &j);
638      MinI = i - Offset;
639      MaxI = i + Offset;
640      MinJ = j - Offset;
641      MaxJ = j + Offset;
642 
643      if (MinI < 0)
644           MinI = 0;
645      if (MaxI > MAX_MAP_GRIDS - 1)
646           MaxI = MAX_MAP_GRIDS - 1;
647      if (MinJ < 0)
648           MinJ = 0;
649      if (MaxJ > MAX_MAP_GRIDS - 1)
650           MaxJ = MAX_MAP_GRIDS - 1;
651 
652      node_s *nearestwp = WaypointClass.GetNearestWaypoint(curselection, 20.0f);
653 
654 #ifdef WP_FLOOD
655      if (!nearestwp)
656           nearestwp = GetNearestFloodWP(player1, 20.0f);
657 #endif
658 
659      for (int x=MinI;x<=MaxI;x++)
660      {
661           for(int y=MinJ;y<=MaxJ;y++)
662           {
663                pNode = m_Waypoints[x][y].GetFirst();
664 
665                while(pNode)
666                {
667                     flDist = GetDistance(worldpos, pNode->Entry->v_origin);
668                     if (flDist <= 15)
669                     {
670                          vec o = pNode->Entry->v_origin;
671                          vec e = o;
672                          o.z -= 2;
673                          e.z += 2;
674 
675                          if (pNode->Entry->iFlags & W_FL_JUMP)
676                          {
677                               // draw a red waypoint
678                               linestyle(2.5f, 0xFF, 0x40, 0x40);
679                          }
680                          else if (nearestwp == pNode->Entry)
681                          {
682                               // draw a green waypoint
683                               linestyle(2.5f, 0x40, 0xFF, 0x40);
684                          }
685                          else
686                          {
687                               // draw a blue waypoint
688                               linestyle(2.5f, 0x40, 0x40, 0xFF);
689                          }
690 
691                          line(int(o.x), int(o.y), int(o.z), int(e.x), int(e.y), int(e.z));
692 
693                          if (flNearestDist > flDist)
694                          {
695                               flNearestDist = flDist;
696                               pNearest = pNode->Entry;
697                          }
698                     }
699 
700                     pNode = pNode->next;
701                }
702           }
703      }
704 
705      if (pNearest)
706      {
707           pNode = pNearest->ConnectedWPs.GetFirst();
708 
709           linestyle(2.0f, 0xFF, 0xFF, 0xFF);
710 
711           while(pNode)
712           {
713                vec o = pNode->Entry->v_origin;
714                vec e = pNearest->v_origin;
715 
716                line(int(o.x), int(o.y), int(o.z), int(e.x), int(e.y), int(e.z));
717 
718                pNode = pNode->next;
719           }
720 
721           // Has this waypoint an target yaw?
722           if (pNearest->sYaw != -1)
723           {
724                vec angles(0, pNearest->sYaw, 0);
725                vec from = pNearest->v_origin, end = pNearest->v_origin;
726                vec forward, right, up;
727 
728                from.z = end.z = (pNearest->v_origin.z-1.0f);
729 
730                AnglesToVectors(angles, forward, right, up);
731                forward.mul(5.0f);
732                end.add(forward);
733 
734                linestyle(2.0f, 0xFF, 0x40, 0x40);
735                line(int(from.x), int(from.y), int(from.z), int(end.x), int(end.y), int(end.z));
736           }
737      }
738 
739 #ifndef RELEASE_BUILD
740      // check if path waypointing is on...
741      if (m_bDrawWPPaths)
742      {
743           // Draw path from first bot
744           if (bots.length() && bots[0] && bots[0]->pBot && bots[0]->pBot->m_pCurrentWaypoint &&
745                bots[0]->pBot->m_pCurrentGoalWaypoint)
746           {
747                CBot *pB = bots[0]->pBot;
748                if (!pB->m_bCalculatingAStarPath && !pB->m_AStarNodeList.Empty())
749                {
750                     TLinkedList<waypoint_s *>::node_s *pNode = pB->m_AStarNodeList.GetFirst(), *pNext;
751 
752                     linestyle(2.5f, 0xFF, 0x40, 0x40);
753 
754                     line((int)pB->m_pCurrentWaypoint->pNode->v_origin.x,
755                            (int)pB->m_pCurrentWaypoint->pNode->v_origin.y,
756                            (int)pB->m_pCurrentWaypoint->pNode->v_origin.z,
757                            (int)pNode->Entry->pNode->v_origin.x,
758                            (int)pNode->Entry->pNode->v_origin.y,
759                            (int)pNode->Entry->pNode->v_origin.z);
760 
761                     while(pNode && pNode->next)
762                     {
763                          pNext = pNode->next;
764                          vec &v1 = pNode->Entry->pNode->v_origin;
765                          vec &v2 = pNext->Entry->pNode->v_origin;
766 
767                          line(int(v1.x), int(v1.y), int(v1.z), int(v2.x), int(v2.y), int(v2.z));
768 
769                          pNode = pNode->next;
770                     }
771                }
772           }
773      }
774 #endif
775 
776     glEnable(GL_CULL_FACE);
777     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
778 
779      if (intermission) return;
780 
781      /*for(int i=0;i<MAX_STORED_LOCATIONS;i++)
782      {
783           if (player1->PrevLocations.prevloc[i]==g_vecZero) continue;
784           vec v1 = player1->PrevLocations.prevloc[i];
785           v1.z -= 1.0f;
786           vec v2 = v1;
787           v2.z += 2.0f;
788           linestyle(2.5f, 0xFF, 0x40, 0x40);
789           line(int(v1.x), int(v1.y), int(v1.z), int(v2.x), int(v2.y), int(v2.z));
790      }*/
791 }
792 
793 // Add waypoint at location o, returns pointer of created wp
AddWaypoint(vec o,bool connectwp)794 node_s *CWaypointClass::AddWaypoint(vec o, bool connectwp)
795 {
796      short x, y;
797      int flags = 0;
798      if (S((int)o.x, (int)o.y)->tag) flags |= W_FL_INTAG;
799 
800      node_s *pNode = new node_s(o, flags, 0);
801      m_vLastCreatedWP = o;
802 
803      GetNodeIndexes(o, &x, &y);
804 
805      m_Waypoints[x][y].AddNode(pNode);
806      BotManager.AddWaypoint(pNode);
807 
808      m_iWaypointCount++;
809 
810      if (connectwp && m_bAutoPlacePaths)
811      {
812           // Connect new waypoint with other near waypoints.
813 
814           loopi(MAX_MAP_GRIDS)
815           {
816                loopj(MAX_MAP_GRIDS)
817                {
818                     TLinkedList<node_s *>::node_s *p = m_Waypoints[i][j].GetFirst();
819 
820                     while(p)
821                     {
822                          if (p->Entry == pNode)
823                          {
824                               p = p->next;
825                               continue;  // skip the waypoint that was just added
826                          }
827 
828                          // check if the waypoint is reachable from the new one (one-way)
829                          if (WPIsReachable(o, p->Entry->v_origin))
830                               AddPath(pNode, p->Entry); // Add a path from a to b
831                          if (WPIsReachable(p->Entry->v_origin, pNode->v_origin))
832                               AddPath(p->Entry, pNode); // Add a path from b to a
833 
834                          p = p->next;
835                     }
836                }
837           }
838      }
839 
840      return pNode;
841 }
842 
DeleteWaypoint(vec v_src)843 void CWaypointClass::DeleteWaypoint(vec v_src)
844 {
845      node_s *pWP;
846 
847      pWP = GetNearestWaypoint(v_src, 7.0f);
848 
849      if (!pWP)
850      {
851           conoutf("Error: Couldn't find near waypoint");
852           return;
853      }
854 
855      BotManager.DelWaypoint(pWP);
856 
857      // delete any paths that lead to this index...
858      DeletePath(pWP);
859 
860      short x, y;
861      GetNodeIndexes(pWP->v_origin, &x, &y);
862 
863      m_Waypoints[x][y].DeleteEntry(pWP);
864 
865      pWP->ConnectedWPs.DeleteAllNodes();
866      pWP->ConnectedWPsWithMe.DeleteAllNodes();
867 
868      m_iWaypointCount--;
869      delete pWP;
870 }
871 
AddPath(node_s * pWP1,node_s * pWP2)872 void CWaypointClass::AddPath(node_s *pWP1, node_s *pWP2)
873 {
874      pWP1->ConnectedWPs.AddNode(pWP2);
875      pWP2->ConnectedWPsWithMe.AddNode(pWP1);
876 }
877 
878 // Deletes all paths connected to the given waypoint
DeletePath(node_s * pWP)879 void CWaypointClass::DeletePath(node_s *pWP)
880 {
881      TLinkedList<node_s *>::node_s *p;
882 
883      loopi(MAX_MAP_GRIDS)
884      {
885           loopj(MAX_MAP_GRIDS)
886           {
887                p = m_Waypoints[i][j].GetFirst();
888                while (p)
889                {
890                     p->Entry->ConnectedWPs.DeleteEntry(pWP);
891                     pWP->ConnectedWPsWithMe.DeleteEntry(p->Entry);
892                     p = p->next;
893                }
894           }
895      }
896 }
897 
898 // Deletes path between 2 waypoints(1 way)
DeletePath(node_s * pWP1,node_s * pWP2)899 void CWaypointClass::DeletePath(node_s *pWP1, node_s *pWP2)
900 {
901      pWP1->ConnectedWPs.DeleteEntry(pWP2);
902      pWP2->ConnectedWPsWithMe.DeleteEntry(pWP1);
903 }
904 
ManuallyCreatePath(vec v_src,int iCmd,bool TwoWay)905 void CWaypointClass::ManuallyCreatePath(vec v_src, int iCmd, bool TwoWay)
906 {
907      static node_s *waypoint1 = NULL;  // initialized to unassigned
908      static node_s *waypoint2 = NULL;  // initialized to unassigned
909 
910      if (iCmd == 1)  // assign source of path
911      {
912           waypoint1 = GetNearestWaypoint(v_src, 7.0f);
913 
914           if (!waypoint1)
915           {
916                conoutf("Error: Couldn't find near waypoint");
917                return;
918           }
919 
920           return;
921      }
922 
923      if (iCmd == 2)  // assign dest of path and make path
924      {
925           if (!waypoint1)
926           {
927                conoutf("Error: First waypoint unset");
928                return;
929           }
930 
931           waypoint2 = GetNearestWaypoint(v_src, 7.0f);
932 
933           if (!waypoint2)
934           {
935                conoutf("Error: Couldn't find near waypoint");
936                return;
937           }
938 
939           AddPath(waypoint1, waypoint2);
940 
941           if (TwoWay)
942                AddPath(waypoint2, waypoint1);
943      }
944 }
945 
ManuallyDeletePath(vec v_src,int iCmd,bool TwoWay)946 void CWaypointClass::ManuallyDeletePath(vec v_src, int iCmd, bool TwoWay)
947 {
948      static node_s *waypoint1 = NULL;  // initialized to unassigned
949      static node_s *waypoint2 = NULL;  // initialized to unassigned
950 
951      if (iCmd == 1)  // assign source of path
952      {
953           waypoint1 = GetNearestWaypoint(v_src, 7.0f);
954 
955           if (!waypoint1)
956           {
957                conoutf("Error: Couldn't find near waypoint");
958                return;
959           }
960 
961           return;
962      }
963 
964      if (iCmd == 2)  // assign dest of path and delete path
965      {
966           if (!waypoint1)
967           {
968                conoutf("Error: First waypoint unset");
969                return;
970           }
971 
972           waypoint2 = GetNearestWaypoint(v_src, 7.0f);
973 
974           if (!waypoint2)
975           {
976                conoutf("Error: Couldn't find near waypoint");
977                return;
978           }
979 
980           DeletePath(waypoint1, waypoint2);
981 
982           if (TwoWay)
983                DeletePath(waypoint2, waypoint1);
984      }
985 }
986 
WPIsReachable(vec from,vec to)987 bool CWaypointClass::WPIsReachable(vec from, vec to)
988 {
989      traceresult_s tr;
990      float curr_height, last_height;
991 
992      float distance = GetDistance(from, to);
993 
994      // is the destination close enough?
995      if (distance < REACHABLE_RANGE)
996      {
997           if (IsVisible(from, to))
998           {
999                if (UnderWater(from) && UnderWater(to))
1000                {
1001                     // No need to worry about heights in water
1002                     return true;
1003                }
1004 /*
1005                if (to.z > (from.z + JUMP_HEIGHT))
1006                {
1007                     vec v_new_src = to;
1008                     vec v_new_dest = to;
1009 
1010                     v_new_dest.z = v_new_dest.z - (JUMP_HEIGHT + 1.0f);
1011 
1012                     // check if we didn't hit anything, if so then it's in mid-air
1013                     if (::IsVisible(v_new_src, v_new_dest, NULL))
1014                     {
1015                          conoutf("to is in midair");
1016                          debugbeam(from, to);
1017                          return false;  // can't reach this one
1018                     }
1019                }
1020 */
1021 
1022                // check if distance to ground increases more than jump height
1023                // at points between from and to...
1024 
1025                vec v_temp = to;
1026                v_temp.sub(from);
1027                vec v_direction = Normalize(v_temp);  // 1 unit long
1028                vec v_check = from;
1029                vec v_down = from;
1030 
1031                v_down.z = v_down.z - 100.0f;  // straight down
1032 
1033                TraceLine(v_check, v_down, NULL, false, &tr);
1034 
1035                  // height from ground
1036                last_height = GetDistance(v_check, tr.end);
1037 
1038                distance = GetDistance(to, v_check);  // distance from goal
1039 
1040                while (distance > 2.0f)
1041                {
1042                     // move 2 units closer to the goal
1043                     v_temp = v_direction;
1044                     v_temp.mul(2.0f);
1045                     v_check.add(v_temp);
1046 
1047                     v_down = v_check;
1048                     v_down.z = v_down.z - 100.0f;
1049 
1050                     TraceLine(v_check, v_down, NULL, false, &tr);
1051 
1052                     curr_height = GetDistance(v_check, tr.end);
1053 
1054                     // is the difference in the last height and the current height
1055                     // higher that the jump height?
1056                     if ((last_height - curr_height) >= JUMP_HEIGHT)
1057                     {
1058                          // can't get there from here...
1059                          //conoutf("traces failed to to");
1060                          return false;
1061                     }
1062 
1063                     last_height = curr_height;
1064 
1065                     distance = GetDistance(to, v_check);  // distance from goal
1066                }
1067 
1068                return true;
1069           }
1070      }
1071 
1072      return false;
1073 }
1074 
GetNearestWaypoint(vec v_src,float flRange)1075 node_s *CWaypointClass::GetNearestWaypoint(vec v_src, float flRange)
1076 {
1077      TLinkedList<node_s *>::node_s *pNode;
1078      node_s *pNearest = NULL;
1079      short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS);
1080      float flNearestDist = 9999.99f, flDist;
1081 
1082      GetNodeIndexes(v_src, &i, &j);
1083      MinI = i - Offset;
1084      MaxI = i + Offset;
1085      MinJ = j - Offset;
1086      MaxJ = j + Offset;
1087 
1088      if (MinI < 0)
1089           MinI = 0;
1090      if (MaxI > MAX_MAP_GRIDS - 1)
1091           MaxI = MAX_MAP_GRIDS - 1;
1092      if (MinJ < 0)
1093           MinJ = 0;
1094      if (MaxJ > MAX_MAP_GRIDS - 1)
1095           MaxJ = MAX_MAP_GRIDS - 1;
1096 
1097      for (int x=MinI;x<=MaxI;x++)
1098      {
1099           for(int y=MinJ;y<=MaxJ;y++)
1100           {
1101                pNode = m_Waypoints[x][y].GetFirst();
1102 
1103                while(pNode)
1104                {
1105                     flDist = GetDistance(v_src, pNode->Entry->v_origin);
1106                     if ((flDist < flNearestDist) && (flDist <= flRange))
1107                     {
1108                          if (IsVisible(v_src, pNode->Entry->v_origin, NULL))
1109                          {
1110                               pNearest = pNode->Entry;
1111                               flNearestDist = flDist;
1112                          }
1113                     }
1114 
1115                     pNode = pNode->next;
1116                }
1117           }
1118      }
1119      return pNearest;
1120 }
1121 
GetNearestTriggerWaypoint(vec v_src,float flRange)1122 node_s *CWaypointClass::GetNearestTriggerWaypoint(vec v_src, float flRange)
1123 {
1124      TLinkedList<node_s *>::node_s *pNode;
1125      node_s *pNearest = NULL;
1126      short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS);
1127      float flNearestDist = 9999.99f, flDist;
1128 
1129      GetNodeIndexes(v_src, &i, &j);
1130      MinI = i - Offset;
1131      MaxI = i + Offset;
1132      MinJ = j - Offset;
1133      MaxJ = j + Offset;
1134 
1135      if (MinI < 0)
1136           MinI = 0;
1137      if (MaxI > MAX_MAP_GRIDS - 1)
1138           MaxI = MAX_MAP_GRIDS - 1;
1139      if (MinJ < 0)
1140           MinJ = 0;
1141      if (MaxJ > MAX_MAP_GRIDS - 1)
1142           MaxJ = MAX_MAP_GRIDS - 1;
1143 
1144      for (int x=MinI;x<=MaxI;x++)
1145      {
1146           for(int y=MinJ;y<=MaxJ;y++)
1147           {
1148                pNode = m_Waypoints[x][y].GetFirst();
1149 
1150                while(pNode)
1151                {
1152                     if ((pNode->Entry->iFlags & W_FL_FLOOD) || !(pNode->Entry->iFlags & W_FL_TRIGGER))
1153                     {
1154                          pNode = pNode->next;
1155                          continue;
1156                     }
1157 
1158                     flDist = GetDistance(v_src, pNode->Entry->v_origin);
1159                     if ((flDist < flNearestDist) && (flDist <= flRange))
1160                     {
1161                          if (IsVisible(v_src, pNode->Entry->v_origin, NULL))
1162                          {
1163                               pNearest = pNode->Entry;
1164                               flNearestDist = flDist;
1165                          }
1166                     }
1167 
1168                     pNode = pNode->next;
1169                }
1170           }
1171      }
1172      return pNearest;
1173 }
1174 
GetWaypointFromVec(const vec & v_src)1175 node_s *CWaypointClass::GetWaypointFromVec(const vec &v_src)
1176 {
1177      static TLinkedList<node_s *>::node_s *pNode;
1178      static short x, y;
1179 
1180      GetNodeIndexes(v_src, &x, &y);
1181 
1182      pNode = m_Waypoints[x][y].GetFirst();
1183 
1184      while(pNode)
1185      {
1186           if (pNode->Entry->v_origin==v_src)
1187                return pNode->Entry;
1188 
1189           pNode = pNode->next;
1190      }
1191      return NULL;
1192 }
1193 
CalcCost(node_s * pNode)1194 void CWaypointClass::CalcCost(node_s *pNode)
1195 {
1196      float flCost = 10.0f;
1197 
1198      // Check nearby cubes...
1199      int x = int(pNode->v_origin.x);
1200      int y = int(pNode->v_origin.y);
1201      int a, b, row;
1202 
1203      for (row=0;row<=1;row++)
1204      {
1205           if (row==0) b = y - 6;
1206           else b = y + 6;
1207 
1208           for (a=(x-6);a<=(x+6);a++)
1209           {
1210                if (OUTBORD(a, b)) continue;
1211 
1212                vec to(a, b, GetCubeFloor(a, b) + 1.0f);
1213 
1214                // See if there is a obstacle(cube or mapmodel) nearby
1215                traceresult_s tr;
1216                TraceLine(pNode->v_origin, to, NULL, false, &tr);
1217                if (tr.collided)
1218                {
1219                     float flFraction = (GetDistance(pNode->v_origin, tr.end) /
1220                                         GetDistance(pNode->v_origin, to));
1221                     flCost += (1.0f-flFraction)*0.5f;
1222                }
1223 
1224                if ((iabs(a) > 4) || (iabs(b) > 4)) continue;
1225 
1226                vec from = to;
1227                to.z -= (JUMP_HEIGHT - 1.0f);
1228                TraceLine(from, to, NULL, false, &tr);
1229                if (!tr.collided)
1230                     flCost += 0.5f;
1231           }
1232 
1233           if (row==0) a = x - 6;
1234           else a = x + 6;
1235 
1236           for (b=(y-6);b<=(y+6);b++)
1237           {
1238                if (OUTBORD(a, b)) continue;
1239 
1240                vec to(a, b, GetCubeFloor(a, b) + 1.0f);
1241 
1242                // See if there is a obstacle(cube or mapmodel) nearby
1243                traceresult_s tr;
1244                TraceLine(pNode->v_origin, to, NULL, false, &tr);
1245                if (tr.collided)
1246                {
1247                     float flFraction = (GetDistance(pNode->v_origin, tr.end) /
1248                                         GetDistance(pNode->v_origin, to));
1249                     flCost += (1.0f-flFraction)*0.5f;
1250                }
1251 
1252                if ((iabs(a) > 4) || (iabs(b) > 4)) continue;
1253 
1254                vec from = to;
1255                to.z -= (JUMP_HEIGHT - 1.0f);
1256                TraceLine(from, to, NULL, false, &tr);
1257                if (!tr.collided)
1258                     flCost += 0.5f;
1259           }
1260      }
1261 
1262      if (UnderWater(pNode->v_origin)) // Water is annoying
1263           flCost += 5.0f;
1264 
1265      pNode->sCost = (short)flCost;
1266 }
1267 
ReCalcCosts(void)1268 void CWaypointClass::ReCalcCosts(void)
1269 {
1270      loopi(MAX_MAP_GRIDS)
1271      {
1272           loopj(MAX_MAP_GRIDS)
1273           {
1274                TLinkedList<node_s *>::node_s *p = m_Waypoints[i][j].GetFirst();
1275                while(p)
1276                {
1277                     CalcCost(p->Entry);
1278                     p = p->next;
1279                }
1280           }
1281      }
1282 }
1283 
1284 #ifdef WP_FLOOD
1285 
StartFlood()1286 void CWaypointClass::StartFlood()
1287 {
1288      if (m_bFlooding) return;
1289 
1290      conoutf("Starting flood, this may take a while on large maps....");
1291      m_bFlooding = true;
1292      m_iFloodStartTime = SDL_GetTicks();
1293      m_iCurFloodX = m_iCurFloodY = MINBORD;
1294      m_iFloodSize = 0;
1295 }
1296 
FloodThink()1297 void CWaypointClass::FloodThink()
1298 {
1299      if (!m_bFlooding) return;
1300 
1301      static int x, y, count;
1302      count = 0;
1303 
1304      if (!m_bFilteringNodes)
1305      {
1306           // loop through ALL cubes and check if we should add a waypoint here
1307           for (x=m_iCurFloodX; x<(ssize-MINBORD); x+=4)
1308           {
1309                if (count >= 3)
1310                {
1311                     AddScreenText("Flooding map with waypoints... %.2f %%",
1312                                   ((float)x / float(ssize-MINBORD)) * 100.0f);
1313                     m_iCurFloodX = x;
1314                     return;
1315                }
1316 
1317                count++;
1318 
1319                for (y=MINBORD; y<(ssize-MINBORD); y+=4)
1320                {
1321                     vec from(x, y, GetCubeFloor(x, y)+2.0f);
1322                     bool bFound = CanPlaceNodeHere(from);
1323 
1324                     if (!bFound)
1325                     {
1326                          for (int a=x-2;a<=(x+2);a++)
1327                          {
1328                               for (int b=y-2;b<=(y+2);b++)
1329                               {
1330                                    if (OUTBORD(a, b)) continue;
1331                                    if ((a==x) && (b==y)) continue;
1332                                    makevec(&from, a, b, GetCubeFloor(a, b) + 2.0f);
1333                                    if (CanPlaceNodeHere(from))
1334                                    {
1335                                         bFound = true;
1336                                         break;
1337                                    }
1338                               }
1339 
1340                               if (bFound) break;
1341                          }
1342                     }
1343 
1344                     if (!bFound) continue;
1345 
1346                     // Add WP
1347                     int flags = W_FL_FLOOD;
1348                     if (S((int)from.x, (int)from.y)->tag) flags |= W_FL_INTAG;
1349 
1350                     node_s *pWP = new node_s(from, flags, 0);
1351 
1352                     short i, j;
1353                     GetNodeIndexes(from, &i, &j);
1354                     m_Waypoints[i][j].PushNode(pWP);
1355                     BotManager.AddWaypoint(pWP);
1356                     m_iFloodSize += sizeof(node_s);
1357                     m_iWaypointCount++;
1358 
1359                     // Connect with other nearby nodes
1360                     ConnectFloodWP(pWP);
1361                }
1362           }
1363      }
1364 
1365      if (!m_bFilteringNodes)
1366      {
1367           m_bFilteringNodes = true;
1368           m_iCurFloodX = m_iCurFloodY = 0;
1369           m_iFilteredNodes = 0;
1370      }
1371 
1372      count = 0;
1373 
1374      // Filter all nodes which aren't connected to any other nodes
1375      for (x=m_iCurFloodX;x<MAX_MAP_GRIDS;x++)
1376      {
1377           if (count > 3)
1378           {
1379                AddScreenText("Filtering useless waypoints and");
1380                AddScreenText("adding costs... %.2f %%", ((float)x / float(MAX_MAP_GRIDS)) * 100.0f);
1381                m_iCurFloodX = x;
1382                return;
1383           }
1384 
1385           count++;
1386 
1387           for (y=0;y<MAX_MAP_GRIDS;y++)
1388           {
1389                TLinkedList<node_s *>::node_s *pNode = m_Waypoints[x][y].GetFirst(), *pTemp;
1390                while(pNode)
1391                {
1392                     if (pNode->Entry->ConnectedWPs.Empty() ||
1393                          pNode->Entry->ConnectedWPsWithMe.Empty())
1394                     {
1395                          BotManager.DelWaypoint(pNode->Entry);
1396                          pTemp = pNode;
1397                          pNode = pNode->next;
1398                          delete pTemp->Entry;
1399                          m_Waypoints[x][y].DeleteNode(pTemp);
1400                          m_iFilteredNodes++;
1401                          m_iFloodSize -= sizeof(node_s);
1402                          m_iWaypointCount--;
1403                          continue;
1404                     }
1405                     else
1406                          CalcCost(pNode->Entry);
1407                     pNode = pNode->next;
1408                }
1409           }
1410      }
1411 
1412      // Done with flooding
1413      m_bFlooding = false;
1414      m_bFilteringNodes = false;
1415 
1416      //ReCalcCosts();
1417      BotManager.PickNextTrigger();
1418 
1419      m_iFloodSize += sizeof(m_Waypoints);
1420      conoutf("Added %d wps in %d milliseconds", m_iWaypointCount, SDL_GetTicks()-m_iFloodStartTime);
1421      conoutf("Filtered %d wps", m_iFilteredNodes);
1422 
1423      BotManager.CalculateMaxAStarCount();
1424 
1425      char szSize[64];
1426      sprintf(szSize, "Total size: %.2f Kb", float(m_iFloodSize)/1024.0f);
1427      conoutf(szSize);
1428 }
1429 
CanPlaceNodeHere(const vec & from)1430 bool CWaypointClass::CanPlaceNodeHere(const vec &from)
1431 {
1432      static short x, y, a, b;
1433      static traceresult_s tr;
1434      static vec to, v1, v2;
1435 
1436      x = short(from.x);
1437      y = short(from.y);
1438 
1439      sqr *s = S(x, y);
1440      if (SOLID(s))
1441      {
1442           return false;
1443      }
1444 
1445      if (fabs((float)(s->ceil - s->floor)) < player1->radius)
1446      {
1447           return false;
1448      }
1449 
1450      if (GetNearestFloodWP(from, 2.0f, NULL)) return false;
1451 
1452      for (a=(x-1);a<=(x+1);a++)
1453      {
1454           for (b=(y-1);b<=(y+1);b++)
1455           {
1456                if ((x==a) && (y==b)) continue;
1457                if (OUTBORD(a, b)) return false;
1458                makevec(&v1, a, b, from.z);
1459                v2 = v1;
1460                v2.z -= 1000.0f;
1461 
1462                TraceLine(v1, v2, NULL, false, &tr, true);
1463                to = tr.end;
1464 
1465                if ((a >= (x-1)) && (a <= (x+1)) && (b >= (y-1)) && (b <= (y+1)))
1466                {
1467                     if (!tr.collided || (to.z < (from.z-4.0f)))
1468                     {
1469                          return false;
1470                     }
1471                }
1472 
1473                to.z += 2.0f;
1474 
1475                if (from.z < (to.z-JUMP_HEIGHT))
1476                {
1477                     return false;
1478                }
1479 
1480                TraceLine(from, to, NULL, false, &tr, true);
1481                if (tr.collided)
1482                     return false;
1483           }
1484      }
1485 
1486      return true;
1487 }
1488 
ConnectFloodWP(node_s * pWP)1489 void CWaypointClass::ConnectFloodWP(node_s *pWP)
1490 {
1491      if (!pWP) return;
1492 
1493      static float flRange;
1494      static TLinkedList<node_s *>::node_s *pNode;
1495      static short i, j, MinI, MaxI, MinJ, MaxJ, x, y, Offset;
1496      static float flDist;
1497      static node_s *p;
1498 
1499      // Calculate range, based on distance to nearest node
1500      p = GetNearestFloodWP(pWP->v_origin, 15.0f, pWP, true);
1501      if (p)
1502      {
1503           flDist = GetDistance(pWP->v_origin, p->v_origin);
1504           flRange = min(flDist+2.0f, 15.0f);
1505           if (flRange < 5.0f) flRange = 5.0f;
1506      }
1507      else
1508           return;
1509 
1510      Offset = (short)ceil(flRange / MAX_MAP_GRIDS);
1511 
1512      GetNodeIndexes(pWP->v_origin, &i, &j);
1513      MinI = i - Offset;
1514      MaxI = i + Offset;
1515      MinJ = j - Offset;
1516      MaxJ = j + Offset;
1517 
1518      if (MinI < 0)
1519           MinI = 0;
1520      if (MaxI > MAX_MAP_GRIDS - 1)
1521           MaxI = MAX_MAP_GRIDS - 1;
1522      if (MinJ < 0)
1523           MinJ = 0;
1524      if (MaxJ > MAX_MAP_GRIDS - 1)
1525           MaxJ = MAX_MAP_GRIDS - 1;
1526 
1527      for (x=MinI;x<=MaxI;x++)
1528      {
1529           for(y=MinJ;y<=MaxJ;y++)
1530           {
1531                pNode = m_Waypoints[x][y].GetFirst();
1532 
1533                while(pNode)
1534                {
1535                     if (pNode->Entry == pWP)
1536                     {
1537                          pNode = pNode->next;
1538                          continue;
1539                     }
1540 
1541                     flDist = GetDistance(pWP->v_origin, pNode->Entry->v_origin);
1542                     if (flDist <= flRange)
1543                     {
1544                          if (IsVisible(pWP->v_origin, pNode->Entry->v_origin, NULL, true))
1545                          {
1546                               // Connect a with b
1547                               pWP->ConnectedWPs.AddNode(pNode->Entry);
1548                               pNode->Entry->ConnectedWPsWithMe.AddNode(pWP);
1549 
1550                               // Connect b with a
1551                               pNode->Entry->ConnectedWPs.AddNode(pWP);
1552                               pWP->ConnectedWPsWithMe.AddNode(pNode->Entry);
1553 
1554                               m_iFloodSize += (2 * sizeof(node_s *));
1555                          }
1556                     }
1557 
1558                     pNode = pNode->next;
1559                }
1560           }
1561      }
1562 }
1563 
GetNearestFloodWP(vec v_origin,float flRange,node_s * pIgnore,bool SkipTags)1564 node_s *CWaypointClass::GetNearestFloodWP(vec v_origin, float flRange, node_s *pIgnore,
1565                                           bool SkipTags)
1566 {
1567      TLinkedList<node_s *>::node_s *p;
1568      node_s *pNearest = NULL;
1569      short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS);
1570      float flNearestDist = 9999.99f, flDist;
1571 
1572      GetNodeIndexes(v_origin, &i, &j);
1573      MinI = i - Offset;
1574      MaxI = i + Offset;
1575      MinJ = j - Offset;
1576      MaxJ = j + Offset;
1577 
1578      if (MinI < 0)
1579           MinI = 0;
1580      if (MaxI > MAX_MAP_GRIDS - 1)
1581           MaxI = MAX_MAP_GRIDS - 1;
1582      if (MinJ < 0)
1583           MinJ = 0;
1584      if (MaxJ > MAX_MAP_GRIDS - 1)
1585           MaxJ = MAX_MAP_GRIDS - 1;
1586 
1587      for (int x=MinI;x<=MaxI;x++)
1588      {
1589           for(int y=MinJ;y<=MaxJ;y++)
1590           {
1591                p = m_Waypoints[x][y].GetFirst();
1592 
1593                while(p)
1594                {
1595                     if ((p->Entry == pIgnore) || (!(p->Entry->iFlags & W_FL_FLOOD)))
1596                     {
1597                          p = p->next;
1598                          continue;
1599                     }
1600 
1601                     flDist = GetDistance(v_origin, p->Entry->v_origin);
1602                     if ((flDist < flNearestDist) && (flDist <= flRange))
1603                     {
1604                          if (IsVisible(v_origin, p->Entry->v_origin, NULL, SkipTags))
1605                          {
1606                               pNearest = p->Entry;
1607                               flNearestDist = flDist;
1608                          }
1609                     }
1610 
1611                     p = p->next;
1612                }
1613           }
1614      }
1615      return pNearest;
1616 }
1617 
GetNearestTriggerFloodWP(vec v_origin,float flRange)1618 node_s *CWaypointClass::GetNearestTriggerFloodWP(vec v_origin, float flRange)
1619 {
1620      TLinkedList<node_s *>::node_s *p;
1621      node_s *pNearest = NULL;
1622      short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS);
1623      float flNearestDist = 9999.99f, flDist;
1624 
1625      GetNodeIndexes(v_origin, &i, &j);
1626      MinI = i - Offset;
1627      MaxI = i + Offset;
1628      MinJ = j - Offset;
1629      MaxJ = j + Offset;
1630 
1631      if (MinI < 0)
1632           MinI = 0;
1633      if (MaxI > MAX_MAP_GRIDS - 1)
1634           MaxI = MAX_MAP_GRIDS - 1;
1635      if (MinJ < 0)
1636           MinJ = 0;
1637      if (MaxJ > MAX_MAP_GRIDS - 1)
1638           MaxJ = MAX_MAP_GRIDS - 1;
1639 
1640      for (int x=MinI;x<=MaxI;x++)
1641      {
1642           for(int y=MinJ;y<=MaxJ;y++)
1643           {
1644                p = m_Waypoints[x][y].GetFirst();
1645 
1646                while(p)
1647                {
1648                     if (!(p->Entry->iFlags & (W_FL_FLOOD | W_FL_TRIGGER)))
1649                     {
1650                          p = p->next;
1651                          continue;
1652                     }
1653 
1654                     flDist = GetDistance(v_origin, p->Entry->v_origin);
1655                     if ((flDist < flNearestDist) && (flDist <= flRange))
1656                     {
1657                          if (IsVisible(v_origin, p->Entry->v_origin, NULL))
1658                          {
1659                               pNearest = p->Entry;
1660                               flNearestDist = flDist;
1661                          }
1662                     }
1663 
1664                     p = p->next;
1665                }
1666           }
1667      }
1668      return pNearest;
1669 }
1670 
GetNodeIndexes(const vec & v_origin,short * i,short * j)1671 void CWaypointClass::GetNodeIndexes(const vec &v_origin, short *i, short *j)
1672 {
1673      // Function code by cheesy and PMB
1674      //*i = iabs((int)((int)(v_origin.x + (2*ssize)) / SECTOR_SIZE));
1675      //*j = iabs((int)((int)(v_origin.y + (2*ssize)) / SECTOR_SIZE));
1676      //*i = (int)((v_origin.x) / ssize * MAX_MAP_GRIDS);
1677      //*j = (int)((v_origin.y) / ssize * MAX_MAP_GRIDS);
1678      *i = iabs((int)((v_origin.x) / MAX_MAP_GRIDS));
1679      *j = iabs((int)((v_origin.y) / MAX_MAP_GRIDS));
1680 
1681      if (*i > MAX_MAP_GRIDS - 1)
1682           *i = MAX_MAP_GRIDS - 1;
1683      if (*j > MAX_MAP_GRIDS - 1)
1684           *j = MAX_MAP_GRIDS - 1;
1685 }
1686 
1687 #endif // WP_FLOOD
1688 // Waypoint class end
1689 
1690 #if defined AC_CUBE
1691 
1692 // AC waypoint class begin
StartFlood()1693 void CACWaypointClass::StartFlood()
1694 {
1695      // UNDONE?
1696      CWaypointClass::StartFlood();
1697 }
1698 
1699 // AC waypoint class end
1700 
1701 #elif defined VANILLA_CUBE
1702 
1703 // Cube waypoint class begin
1704 
StartFlood()1705 void CCubeWaypointClass::StartFlood()
1706 {
1707      CWaypointClass::StartFlood();
1708 
1709      // Add wps at triggers and teleporters and their destination
1710      loopv(ents)
1711      {
1712           entity &e = ents[i];
1713 
1714           if (OUTBORD(e.x, e.y)) continue;
1715 
1716           if (e.type == TELEPORT)
1717           {
1718                vec telepos = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }, teledestpos = g_vecZero;
1719 
1720                // Find the teleport destination
1721                int n = -1, tag = e.attr1, beenhere = -1;
1722                for(;;)
1723                {
1724                     n = findentity(TELEDEST, n+1);
1725                     if(n==beenhere || n<0) { conoutf("no teleport destination for tag %d", tag); break; };
1726                     if(beenhere<0) beenhere = n;
1727                     if(ents[n].attr2==tag)
1728                     {
1729                          teledestpos.x = ents[n].x;
1730                          teledestpos.y = ents[n].y;
1731                          teledestpos.z = S(ents[n].x, ents[n].y)->floor+player1->eyeheight;
1732                          break;
1733                     }
1734                }
1735 
1736                if (vis(teledestpos, g_vecZero)) continue;
1737 
1738                int flags = (W_FL_FLOOD | W_FL_TELEPORT);
1739                if (S((int)telepos.x, (int)telepos.y)->tag) flags |= W_FL_INTAG;
1740 
1741                // Add waypoint at teleporter and teleport destination
1742                node_s *pWP = new node_s(telepos, flags, 0);
1743 
1744                short i, j;
1745                GetNodeIndexes(telepos, &i, &j);
1746                m_Waypoints[i][j].PushNode(pWP);
1747                BotManager.AddWaypoint(pWP);
1748                m_iFloodSize += sizeof(node_s);
1749                m_iWaypointCount++;
1750 
1751                flags = (W_FL_FLOOD | W_FL_TELEPORTDEST);
1752                if (S((int)teledestpos.x, (int)teledestpos.y)->tag) flags |= W_FL_INTAG;
1753 
1754                node_s *pWP2 = new node_s(teledestpos, flags, 0);
1755 
1756                GetNodeIndexes(teledestpos, &i, &j);
1757                m_Waypoints[i][j].PushNode(pWP2);
1758                BotManager.AddWaypoint(pWP2);
1759                m_iFloodSize += sizeof(node_s);
1760                m_iWaypointCount++;
1761 
1762                // Connect the teleporter waypoint with the teleport-destination waypoint(1 way)
1763                AddPath(pWP, pWP2);
1764 
1765                // Connect with other nearby nodes
1766                ConnectFloodWP(pWP);
1767           }
1768           else if (e.type == CARROT)
1769           {
1770                vec pos = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight };
1771 
1772                int flags = (W_FL_FLOOD | W_FL_TRIGGER);
1773                if (S(e.x, e.y)->tag) flags |= W_FL_INTAG;
1774 
1775                node_s *pWP = new node_s(pos, flags, 0);
1776 
1777                short i, j;
1778                GetNodeIndexes(pos, &i, &j);
1779                m_Waypoints[i][j].PushNode(pWP);
1780                BotManager.AddWaypoint(pWP);
1781                m_iFloodSize += sizeof(node_s);
1782                m_iWaypointCount++;
1783 
1784                // Connect with other nearby nodes
1785                ConnectFloodWP(pWP);
1786           }
1787           else if (e.type == MAPMODEL)
1788           {
1789                mapmodelinfo &mmi = getmminfo(e.attr2);
1790                if(!&mmi || !mmi.h || !mmi.rad) continue;
1791 
1792                float floor = (float)(S(e.x, e.y)->floor+mmi.zoff+e.attr3)+mmi.h;
1793 
1794                float x1 = e.x - mmi.rad;
1795                float x2 = e.x + mmi.rad;
1796                float y1 = e.y - mmi.rad;
1797                float y2 = e.y + mmi.rad;
1798 
1799                // UNDONE?
1800                for (float x=(x1+1.0f);x<=(x2-1.0f);x++)
1801                {
1802                     for (float y=(y1+1.0f);y<=(y2-1.0f);y++)
1803                     {
1804                          vec from = { x, y, floor+2.0f };
1805                          if (GetNearestFloodWP(from, 2.0f, NULL)) continue;
1806 
1807                          // Add WP
1808                          int flags = W_FL_FLOOD;
1809                          if (S((int)x, (int)y)->tag) flags |= W_FL_INTAG;
1810 
1811                          node_s *pWP = new node_s(from, flags, 0);
1812 
1813                          short i, j;
1814                          GetNodeIndexes(from, &i, &j);
1815                          m_Waypoints[i][j].PushNode(pWP);
1816                          BotManager.AddWaypoint(pWP);
1817                          m_iFloodSize += sizeof(node_s);
1818                          m_iWaypointCount++;
1819 
1820                          // Connect with other nearby nodes
1821                          ConnectFloodWP(pWP);
1822                     }
1823                }
1824           }
1825      }
1826      CWaypointClass::StartFlood();
1827 }
1828 
CreateWPsAtTeleporters()1829 void CCubeWaypointClass::CreateWPsAtTeleporters()
1830 {
1831      loopv(ents)
1832      {
1833           entity &e = ents[i];
1834 
1835           if (e.type != TELEPORT) continue;
1836           if (OUTBORD(e.x, e.y)) continue;
1837 
1838           vec telepos = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }, teledestpos = g_vecZero;
1839 
1840           // Find the teleport destination
1841           int n = -1, tag = e.attr1, beenhere = -1;
1842           for(;;)
1843           {
1844                n = findentity(TELEDEST, n+1);
1845                if(n==beenhere || n<0) { conoutf("no teleport destination for tag %d", tag); continue; };
1846                if(beenhere<0) beenhere = n;
1847                if(ents[n].attr2==tag)
1848                {
1849                     teledestpos.x = ents[n].x;
1850                     teledestpos.y = ents[n].y;
1851                     teledestpos.z = S(ents[n].x, ents[n].y)->floor+player1->eyeheight;
1852                     break;
1853                }
1854           }
1855 
1856           if (vis(teledestpos, g_vecZero)) continue;
1857 
1858           // Add waypoint at teleporter and teleport destination
1859           node_s *telewp = AddWaypoint(telepos, false);
1860           node_s *teledestwp = AddWaypoint(teledestpos, false);
1861 
1862           if (telewp && teledestwp)
1863           {
1864                // Connect the teleporter waypoint with the teleport-destination waypoint(1 way)
1865                AddPath(telewp, teledestwp);
1866 
1867                // Flag waypoints
1868                telewp->iFlags = W_FL_TELEPORT;
1869                teledestwp->iFlags = W_FL_TELEPORTDEST;
1870           }
1871      }
1872 }
1873 
CreateWPsAtTriggers()1874 void CCubeWaypointClass::CreateWPsAtTriggers()
1875 {
1876      loopv(ents)
1877      {
1878           entity &e = ents[i];
1879 
1880           if (e.type != CARROT) continue;
1881           if (OUTBORD(e.x, e.y)) continue;
1882 
1883           vec pos = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight };
1884 
1885           node_s *wp = AddWaypoint(pos, false);
1886 
1887           if (wp)
1888           {
1889                // Flag waypoints
1890                wp->iFlags = W_FL_TRIGGER;
1891           }
1892      }
1893 }
1894 
1895 #endif
1896 
1897 // Cube waypoint class end
1898 
1899 // Waypoint commands begin
1900 
addwp(int * autoconnect)1901 void addwp(int *autoconnect)
1902 {
1903      WaypointClass.SetWaypointsVisible(true);
1904      WaypointClass.AddWaypoint(curselection, *autoconnect!=0);
1905 }
1906 
1907 COMMAND(addwp, "i");
1908 
delwp(void)1909 void delwp(void)
1910 {
1911      WaypointClass.SetWaypointsVisible(true);
1912      WaypointClass.DeleteWaypoint(curselection);
1913 }
1914 
1915 COMMAND(delwp, "");
1916 
wpvisible(int * on)1917 void wpvisible(int *on)
1918 {
1919      WaypointClass.SetWaypointsVisible(*on!=0);
1920 }
1921 
1922 COMMAND(wpvisible, "i");
1923 
wpsave(void)1924 void wpsave(void)
1925 {
1926      WaypointClass.SaveWaypoints();
1927 }
1928 
1929 COMMAND(wpsave, "");
1930 
wpload(void)1931 void wpload(void)
1932 {
1933      WaypointClass.LoadWaypoints();
1934 }
1935 
1936 COMMAND(wpload, "");
1937 
wpclear(void)1938 void wpclear(void)
1939 {
1940     WaypointClass.Clear();
1941 }
1942 
1943 COMMAND(wpclear, "");
1944 
autowp(int * on)1945 void autowp(int *on)
1946 {
1947      WaypointClass.SetWaypointsVisible(true);
1948      WaypointClass.SetAutoWaypoint(*on!=0);
1949 }
1950 
1951 COMMAND(autowp, "i");
1952 
wpinfo(int * on)1953 void wpinfo(int *on)
1954 {
1955      WaypointClass.SetWaypointsVisible(true);
1956      WaypointClass.SetWPInfo(*on!=0);
1957 }
1958 
1959 COMMAND(wpinfo, "i");
1960 
addpath1way1(void)1961 void addpath1way1(void)
1962 {
1963      WaypointClass.SetWaypointsVisible(true);
1964      WaypointClass.ManuallyCreatePath(curselection, 1, false);
1965 }
1966 
1967 COMMAND(addpath1way1, "");
1968 
addpath1way2(void)1969 void addpath1way2(void)
1970 {
1971      WaypointClass.SetWaypointsVisible(true);
1972      WaypointClass.ManuallyCreatePath(curselection, 2, false);
1973 }
1974 
1975 COMMAND(addpath1way2, "");
1976 
addpath2way1(void)1977 void addpath2way1(void)
1978 {
1979      WaypointClass.SetWaypointsVisible(true);
1980      WaypointClass.ManuallyCreatePath(curselection, 1, true);
1981 }
1982 
1983 COMMAND(addpath2way1, "");
1984 
addpath2way2(void)1985 void addpath2way2(void)
1986 {
1987      WaypointClass.SetWaypointsVisible(true);
1988      WaypointClass.ManuallyCreatePath(curselection, 2, true);
1989 }
1990 
1991 COMMAND(addpath2way2, "");
1992 
delpath1way1(void)1993 void delpath1way1(void)
1994 {
1995      WaypointClass.SetWaypointsVisible(true);
1996      WaypointClass.ManuallyDeletePath(curselection, 1, false);
1997 }
1998 
1999 COMMAND(delpath1way1, "");
2000 
delpath1way2(void)2001 void delpath1way2(void)
2002 {
2003      WaypointClass.SetWaypointsVisible(true);
2004      WaypointClass.ManuallyDeletePath(curselection, 2, false);
2005 }
2006 
2007 COMMAND(delpath1way2, "");
2008 
delpath2way1(void)2009 void delpath2way1(void)
2010 {
2011      WaypointClass.SetWaypointsVisible(true);
2012      WaypointClass.ManuallyDeletePath(curselection, 1, true);
2013 }
2014 
2015 COMMAND(delpath2way1, "");
2016 
delpath2way2(void)2017 void delpath2way2(void)
2018 {
2019      WaypointClass.SetWaypointsVisible(true);
2020      WaypointClass.ManuallyDeletePath(curselection, 2, true);
2021 }
2022 
2023 COMMAND(delpath2way2, "");
2024 
setjumpwp(void)2025 void setjumpwp(void)
2026 {
2027      node_s *wp = WaypointClass.GetNearestWaypoint(curselection, 20.0f);
2028      if (wp)
2029      {
2030           WaypointClass.SetWPFlags(wp, W_FL_JUMP);
2031      }
2032 }
2033 
2034 COMMAND(setjumpwp, "");
2035 
unsetjumpwp(void)2036 void unsetjumpwp(void)
2037 {
2038      node_s *wp = WaypointClass.GetNearestWaypoint(curselection, 20.0f);
2039      if (wp)
2040      {
2041           WaypointClass.UnsetWPFlags(wp, W_FL_JUMP);
2042      }
2043 }
2044 
2045 COMMAND(unsetjumpwp, "");
2046 
setwptriggernr(int * nr)2047 void setwptriggernr(int *nr)
2048 {
2049      node_s *wp = WaypointClass.GetNearestWaypoint(curselection, 20.0f);
2050      if (wp)
2051      {
2052           WaypointClass.SetWPTriggerNr(wp, *nr);
2053      }
2054 }
2055 
2056 COMMAND(setwptriggernr, "i");
2057 
setwpyaw(void)2058 void setwpyaw(void)
2059 {
2060      node_s *wp = WaypointClass.GetNearestWaypoint(curselection, 20.0f);
2061      if (wp)
2062      {
2063           WaypointClass.SetWPYaw(wp, short(player1->yaw));
2064      }
2065 }
2066 
2067 COMMAND(setwpyaw, "");
2068 
2069 #ifdef WP_FLOOD
wpflood(void)2070 void wpflood(void)
2071 {
2072      WaypointClass.StartFlood();
2073 }
2074 
2075 COMMAND(wpflood, "");
2076 #endif
2077 
2078 #ifdef VANILLA_CUBE
2079 // Commands specific for cube
addtelewps(void)2080 void addtelewps(void)
2081 {
2082      WaypointClass.SetWaypointsVisible(true);
2083      WaypointClass.CreateWPsAtTeleporters();
2084 }
2085 
2086 COMMAND(addtelewps, "");
2087 
addtriggerwps(void)2088 void addtriggerwps(void)
2089 {
2090      WaypointClass.SetWaypointsVisible(true);
2091      WaypointClass.CreateWPsAtTriggers();
2092 }
2093 
2094 COMMAND(addtriggerwps, "");
2095 #endif
2096 
2097 // Debug functions
2098 #ifdef WP_FLOOD
2099 
2100 #ifndef RELEASE_BUILD
botsheadtome(void)2101 void botsheadtome(void)
2102 {
2103      loopv(bots)
2104      {
2105           if (!bots[i] || !bots[i]->pBot) continue;
2106           bots[i]->pBot->HeadToGoal();
2107           bots[i]->pBot->GoToDebugGoal(player1->o);
2108      }
2109 }
2110 
2111 COMMAND(botsheadtome, "");
2112 
setdebuggoal(void)2113 void setdebuggoal(void) { v_debuggoal = player1->o; };
2114 COMMAND(setdebuggoal, "");
2115 
botsheadtodebuggoal(void)2116 void botsheadtodebuggoal(void)
2117 {
2118      loopv(bots)
2119      {
2120           if (!bots[i] || !bots[i]->pBot) continue;
2121 
2122           bots[i]->pBot->GoToDebugGoal(v_debuggoal);
2123      }
2124 }
2125 
2126 COMMAND(botsheadtodebuggoal, "");
2127 
2128 #endif // RELEASE_BUILD
2129 
2130 #endif // WP_FLOOD
2131 
2132 // End debug functions
2133 // Waypoint commands end
2134 
2135 
2136 // Bot class begin
2137 
FindWaypoint()2138 bool CBot::FindWaypoint()
2139 {
2140      waypoint_s *wp, *wpselect;
2141      int index;
2142      float distance, min_distance[3];
2143      waypoint_s *min_wp[3];
2144 
2145      for (index=0; index < 3; index++)
2146      {
2147           min_distance[index] = 9999.0;
2148           min_wp[index] = NULL;
2149      }
2150 
2151      TLinkedList<node_s *>::node_s *pNode = m_pCurrentWaypoint->pNode->ConnectedWPs.GetFirst();
2152 
2153      while (pNode)
2154      {
2155           if ((pNode->Entry->iFlags & W_FL_INTAG) &&
2156               SOLID(S((int)pNode->Entry->v_origin.x, (int)pNode->Entry->v_origin.y)))
2157           {
2158                pNode = pNode->next;
2159                continue;
2160           }
2161 
2162           wp = GetWPFromNode(pNode->Entry);
2163           if (!wp)
2164           {
2165                pNode = pNode->next;
2166                continue;
2167           }
2168 
2169           // if index is not a current or recent previous waypoint...
2170           if ((wp != m_pCurrentWaypoint) &&
2171               (wp != m_pPrevWaypoints[0]) &&
2172               (wp != m_pPrevWaypoints[1]) &&
2173               (wp != m_pPrevWaypoints[2]) &&
2174               (wp != m_pPrevWaypoints[3]) &&
2175               (wp != m_pPrevWaypoints[4]))
2176           {
2177                // find the distance from the bot to this waypoint
2178                distance = GetDistance(wp->pNode->v_origin);
2179 
2180                if (distance < min_distance[0])
2181                {
2182                     min_distance[2] = min_distance[1];
2183                     min_wp[2] = min_wp[1];
2184 
2185                     min_distance[1] = min_distance[0];
2186                     min_wp[1] = min_wp[0];
2187 
2188                     min_distance[0] = distance;
2189                     min_wp[0] = wp;
2190                }
2191                else if (distance < min_distance [1])
2192                {
2193                     min_distance[2] = min_distance[1];
2194                     min_wp[2] = min_wp[1];
2195 
2196                     min_distance[1] = distance;
2197                     min_wp[1] = wp;
2198                }
2199                else if (distance < min_distance[2])
2200                {
2201                     min_distance[2] = distance;
2202                     min_wp[2] = wp;
2203                }
2204           }
2205           pNode = pNode->next;
2206      }
2207 
2208      wpselect = NULL;
2209 
2210      // about 20% of the time choose a waypoint at random
2211      // (don't do this any more often than every 10 seconds)
2212 
2213      if ((RandomLong(1, 100) <= 20) && (m_iRandomWaypointTime <= lastmillis))
2214      {
2215           m_iRandomWaypointTime = lastmillis + 10000;
2216 
2217           if (min_wp[2])
2218                index = RandomLong(0, 2);
2219           else if (min_wp[1])
2220                index = RandomLong(0, 1);
2221           else if (min_wp[0])
2222                index = 0;
2223           else
2224                return false;  // no waypoints found!
2225 
2226           wpselect = min_wp[index];
2227      }
2228      else
2229      {
2230           // use the closest waypoint that has been recently used
2231           wpselect = min_wp[0];
2232      }
2233 
2234      if (wpselect)  // was a waypoint found?
2235      {
2236           m_pPrevWaypoints[4] = m_pPrevWaypoints[3];
2237           m_pPrevWaypoints[3] = m_pPrevWaypoints[2];
2238           m_pPrevWaypoints[2] = m_pPrevWaypoints[1];
2239           m_pPrevWaypoints[1] = m_pPrevWaypoints[0];
2240           m_pPrevWaypoints[0] = m_pCurrentWaypoint;
2241 
2242           SetCurrentWaypoint(wpselect);
2243           return true;
2244      }
2245 
2246      return false;  // couldn't find a waypoint
2247 }
2248 
HeadToWaypoint()2249 bool CBot::HeadToWaypoint()
2250 {
2251      if (!m_pCurrentWaypoint)
2252           return false; // Can't head to waypoint
2253 
2254      bool Touching = false;
2255      float WPDist = GetDistance(m_pCurrentWaypoint->pNode->v_origin);
2256 
2257 #ifndef RELEASE_BUILD
2258      if (m_pCurrentGoalWaypoint && m_vGoal==g_vecZero)
2259           condebug("Warning: m_vGoal unset");
2260 #endif
2261 
2262      // did the bot run past the waypoint? (prevent the loop-the-loop problem)
2263      if ((m_fPrevWaypointDistance > 1.0) && (WPDist > m_fPrevWaypointDistance) &&
2264          (WPDist <= 5.0f))
2265           Touching = true;
2266      // bot needs to be close for jump and trigger waypoints
2267      else if ((m_pCurrentWaypoint->pNode->iFlags & W_FL_JUMP) ||
2268                 (m_pCurrentWaypoint->pNode->iFlags & W_FL_TRIGGER))
2269           Touching = (WPDist <= 1.5f);
2270      else if (m_pCurrentWaypoint->pNode->iFlags & W_FL_TELEPORT)
2271           Touching = (WPDist <= 4.0f);
2272      // are we close enough to a target waypoint...
2273      else if (WPDist <= 3.0f)
2274      {
2275           if (!m_pCurrentGoalWaypoint || (m_pCurrentWaypoint != m_pCurrentGoalWaypoint) ||
2276                IsVisible(m_vGoal) || (WPDist <= 1.5f))
2277                Touching = true;
2278           // If the bot has a goal check if he can see his next wp
2279           if (m_pCurrentGoalWaypoint && (m_pCurrentGoalWaypoint != m_pCurrentWaypoint) &&
2280              !m_AStarNodeList.Empty() && (WPDist >= 1.0f) &&
2281              !IsVisible(m_AStarNodeList.GetFirst()->Entry->pNode->v_origin))
2282                Touching = false;
2283      }
2284 
2285      // save current distance as previous
2286      m_fPrevWaypointDistance = WPDist;
2287 
2288      // Reached the waypoint?
2289      if (Touching)
2290      {
2291           // Does this waypoint has a targetyaw?
2292           if (m_pCurrentWaypoint->pNode->sYaw != -1)
2293           {
2294                // UNDONE: Inhuman
2295                m_pMyEnt->yaw = m_pMyEnt->targetyaw = m_pCurrentWaypoint->pNode->sYaw;
2296           }
2297 
2298           // Reached a jump waypoint?
2299           if (m_pCurrentWaypoint->pNode->iFlags & W_FL_JUMP)
2300                m_pMyEnt->jumpnext = true;
2301 
2302           m_fPrevWaypointDistance = 0.0f;
2303 
2304           // Does the bot has a goal?
2305           if (m_pCurrentGoalWaypoint)
2306           {
2307                if (m_pCurrentWaypoint != m_pCurrentGoalWaypoint)
2308                {
2309                     if (m_AStarNodeList.Empty())
2310                     {
2311                          if (!AStar())
2312                          {
2313                               // Bot is calculating a new path, just stand still for now
2314                               ResetMoveSpeed();
2315                               m_iWaypointTime += 200;
2316                               return true;
2317                          }
2318                          else
2319                          {
2320                               if (m_AStarNodeList.Empty())
2321                               {
2322                                    //m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalWaypoint, gpGlobals->time));
2323                                    return false; // Couldn't get a new wp to go to
2324                               }
2325                          }
2326                     }
2327 
2328                     m_pCurrentWaypoint = m_AStarNodeList.Pop();
2329 
2330                     if (!IsVisible(m_pCurrentWaypoint->pNode->v_origin))
2331                         //(!(m_pCurrentWaypoint->iFlags & W_FL_TELEPORT)))
2332                     {
2333                          //m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalWaypoint, gpGlobals->time));
2334                          condebug("Next WP not visible");
2335                          return false;
2336                     }
2337 
2338                     SetCurrentWaypoint(m_pCurrentWaypoint);
2339                }
2340                else
2341                {
2342                     // Bot reached the goal waypoint but couldn't reach the goal itself
2343                     // (otherwise we wouldn't be in this function)
2344                     //m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalWaypoint, gpGlobals->time));
2345                     return false;
2346                }
2347           }
2348           else
2349           {
2350                short index = 4;
2351                bool status;
2352 
2353                // try to find the next waypoint
2354                while (((status = FindWaypoint()) == false) && (index > 0))
2355                {
2356                     // if waypoint not found, clear oldest previous index and try again
2357 
2358                     m_pPrevWaypoints[index] = NULL;
2359                     index--;
2360                }
2361 
2362                if (status == false)
2363                {
2364                     ResetWaypointVars();
2365                     condebug("Couldn't find new random waypoint");
2366                     return false;
2367                }
2368           }
2369           m_iWaypointHeadPauseTime = lastmillis + 75;
2370           m_iWaypointTime += 75;
2371      }
2372 
2373      // keep turning towards the waypoint...
2374      /*if (m_pCurrentWaypoint->pNode->iFlags & W_FL_FLOOD)
2375      {UNDONE?
2376           vec aim = m_pCurrentWaypoint->pNode->v_origin;
2377           aim.x+=0.5f;
2378           aim.y+=0.5f;
2379           AimToVec(aim);
2380      }
2381      else*/
2382           AimToVec(m_pCurrentWaypoint->pNode->v_origin);
2383 
2384      if (m_fYawToTurn <= 25.0f)
2385           m_iWaypointHeadLastTurnLessTime = lastmillis;
2386 
2387      // Bot had to turn much for a while?
2388      if ((m_iWaypointHeadLastTurnLessTime > 0) &&
2389          (m_iWaypointHeadLastTurnLessTime < (lastmillis - 1000)))
2390      {
2391           m_iWaypointHeadPauseTime = lastmillis + 200;
2392           m_iWaypointTime += 200;
2393      }
2394 
2395      if (m_iWaypointHeadPauseTime >= lastmillis)
2396      {
2397           m_pMyEnt->move = 0;
2398           //conoutf("Pause in HeadToWaypoint()");
2399      }
2400      else
2401      {
2402           // Check if bot has to jump
2403           vec from = m_pMyEnt->o;
2404           from.z -= (m_pMyEnt->eyeheight - 1.25f);
2405           float flEndDist;
2406           if (!IsVisible(from, FORWARD, 3.0f, false, &flEndDist) &&
2407               (GetDistance(from, m_pCurrentWaypoint->pNode->v_origin) > flEndDist))
2408           {
2409                m_pMyEnt->jumpnext = true;
2410                condebug("Low wall in HeadToWaypoint()");
2411           }
2412 
2413           // Check if bot has to strafe
2414           if (m_iStrafeTime > lastmillis)
2415                SetMoveDir(m_iMoveDir, true);
2416           else
2417           {
2418                m_iStrafeTime = 0;
2419                m_iMoveDir = DIR_NONE;
2420 
2421                vec forward, up, right;
2422                AnglesToVectors(GetViewAngles(), forward, right, up);
2423 
2424                float flLeftDist = -1.0f, flRightDist = -1.0f;
2425                vec dir = right;
2426                bool bStrafe = false;
2427                int iStrafeDir = 0;
2428 
2429                dir.mul(m_pMyEnt->radius);
2430 
2431                // Check left
2432                from = m_pMyEnt->o;
2433                from.sub(dir);
2434                if (IsVisible(from, FORWARD, 3.0f, false, &flLeftDist))
2435                     flLeftDist = -1.0f;
2436 
2437                // Check right
2438                from = m_pMyEnt->o;
2439                from.add(dir);
2440                if (IsVisible(from, FORWARD, 3.0f, false, &flRightDist))
2441                     flRightDist = -1.0f;
2442 
2443                if ((flLeftDist != -1.0f) && (flRightDist != -1.0f))
2444                {
2445                     if (flLeftDist < flRightDist)
2446                     {
2447                          // Strafe right
2448                          bStrafe = true;
2449                          iStrafeDir = RIGHT;
2450                     }
2451                     else if (flRightDist < flLeftDist)
2452                     {
2453                          // Strafe left
2454                          bStrafe = true;
2455                          iStrafeDir = LEFT;
2456                     }
2457                     else
2458                     {
2459                          // Randomly choose a strafe direction
2460                          bStrafe = true;
2461                          if (RandomLong(0, 1))
2462                               iStrafeDir = LEFT;
2463                          else
2464                               iStrafeDir = RIGHT;
2465                     }
2466                }
2467                else if (flLeftDist != -1.0f)
2468                {
2469                     // Strafe right
2470                     bStrafe = true;
2471                     iStrafeDir = RIGHT;
2472                }
2473                else if (flRightDist != -1.0f)
2474                {
2475                     // Strafe left
2476                     bStrafe = true;
2477                     iStrafeDir = LEFT;
2478                }
2479 
2480                if (bStrafe)
2481                {
2482                     SetMoveDir(iStrafeDir, true);
2483                     m_iMoveDir = iStrafeDir;
2484                     m_iStrafeTime = lastmillis + RandomLong(30, 50);
2485                }
2486           }
2487      }
2488 
2489      return true;
2490 }
2491 
2492 // returns true when done or failure
HeadToGoal()2493 bool CBot::HeadToGoal()
2494 {
2495      // Does the bot has a goal(waypoint)?
2496      if (m_pCurrentGoalWaypoint)
2497      {
2498           if (ReachedGoalWP())
2499           {
2500                return false;
2501           }
2502           else
2503           {
2504                if (CurrentWPIsValid())
2505                {
2506                     if (m_bCalculatingAStarPath)
2507                     {
2508                          // Bot is calculating a new path, just stand still for now
2509                          ResetMoveSpeed();
2510                          m_iWaypointTime += 200;
2511 
2512                          // Done calculating the path?
2513                          if (AStar())
2514                          {
2515                               if (m_AStarNodeList.Empty())
2516                               {
2517                                    return false; // Couldn't find a path
2518                               }
2519                               //else
2520                                //    SetCurrentWaypoint(m_AStarNodeList.Pop());
2521                          }
2522                          else
2523                               return true; // else just wait a little bit longer
2524                     }
2525                     //ResetMoveSpeed();
2526                     //return true;
2527                }
2528                else
2529                {
2530                     // Current waypoint isn't reachable, search new one
2531                     waypoint_s *pWP = NULL;
2532 
2533 #ifdef WP_FLOOD
2534                     if (m_pCurrentGoalWaypoint->pNode->iFlags & W_FL_FLOOD)
2535                          pWP = GetNearestFloodWP(8.0f);
2536                     else
2537 #endif
2538                          pWP = GetNearestWaypoint(15.0f);
2539 
2540                     if (!pWP || (pWP == m_pCurrentWaypoint))
2541                          return false;
2542 
2543                     SetCurrentWaypoint(pWP);
2544                     if (AStar())
2545                     {
2546                          if (m_AStarNodeList.Empty()) return false;
2547                     }
2548                     else
2549                     {
2550                          m_iWaypointTime += 200;
2551                          ResetMoveSpeed();
2552                          return true;
2553                     }
2554                }
2555 
2556                //debugbeam(m_pMyEnt->o, m_pCurrentWaypoint->pNode->v_origin);
2557           }
2558      }
2559      else
2560           return false;
2561 
2562      return HeadToWaypoint();
2563 }
2564 
2565 // return true when done calculating
AStar()2566 bool CBot::AStar()
2567 {
2568      if (!m_pCurrentGoalWaypoint || !m_pCurrentWaypoint)
2569      {
2570           if (m_bCalculatingAStarPath)
2571           {
2572                m_bCalculatingAStarPath = false;
2573                BotManager.m_sUsingAStarBotsCount--;
2574           }
2575           return true;
2576      }
2577 
2578      // Ideas by PMB :
2579      // * Make locals static to speed up a bit
2580      // * MaxCycles per frame and make it fps dependent
2581 
2582      static int iMaxCycles;
2583      static int iCurrentCycles;
2584      static short newg;
2585      static waypoint_s *n, *n2;
2586      static TLinkedList<node_s *>::node_s *pPath = NULL;
2587      static bool bPathFailed;
2588 
2589      iMaxCycles = BotManager.m_iFrameTime / 10;
2590      if (iMaxCycles < 10) iMaxCycles = 10;
2591      //condebug("MaxCycles: %d", iMaxCycles);
2592      iCurrentCycles = 0;
2593      bPathFailed = false;
2594 
2595      if (!m_bCalculatingAStarPath)
2596      {
2597           if ((BotManager.m_sUsingAStarBotsCount+1) > BotManager.m_sMaxAStarBots)
2598           {
2599                return true;
2600           }
2601 
2602           BotManager.m_sUsingAStarBotsCount++;
2603 
2604           CleanAStarLists(false);
2605 
2606           m_pCurrentWaypoint->g[0] = m_pCurrentWaypoint->g[1] = 0;
2607           m_pCurrentWaypoint->pParent[0] = m_pCurrentWaypoint->pParent[1] = NULL;
2608           m_pCurrentGoalWaypoint->g[0] = m_pCurrentGoalWaypoint->g[1] = 0;
2609           m_pCurrentGoalWaypoint->pParent[0] = m_pCurrentGoalWaypoint->pParent[1] = NULL;
2610 
2611           m_AStarNodeList.DeleteAllNodes();
2612 
2613           m_AStarOpenList[0].Clear();
2614           m_AStarOpenList[1].Clear();
2615           m_AStarClosedList[0].DeleteAllNodes();
2616           m_AStarClosedList[1].DeleteAllNodes();
2617 
2618           m_AStarOpenList[0].AddEntry(m_pCurrentWaypoint,
2619                                       GetDistance(m_pCurrentGoalWaypoint->pNode->v_origin));
2620           m_AStarOpenList[1].AddEntry(m_pCurrentGoalWaypoint,
2621                                       GetDistance(m_pCurrentGoalWaypoint->pNode->v_origin));
2622 
2623           m_pCurrentWaypoint->bIsOpen[0] = m_pCurrentGoalWaypoint->bIsOpen[1] = true;
2624           m_pCurrentWaypoint->bIsOpen[1] = m_pCurrentGoalWaypoint->bIsOpen[0] = false;
2625           m_pCurrentWaypoint->bIsClosed[0] = m_pCurrentGoalWaypoint->bIsClosed[1] = false;
2626           m_pCurrentWaypoint->bIsClosed[1] = m_pCurrentGoalWaypoint->bIsClosed[0] = false;
2627      }
2628 
2629      while(!m_AStarOpenList[0].Empty())
2630      {
2631           if (iCurrentCycles >= (iMaxCycles/2))
2632           {
2633                m_bCalculatingAStarPath = true;
2634                break;
2635           }
2636 
2637           n = m_AStarOpenList[0].Pop();
2638           n->bIsOpen[0] = false;
2639 
2640           if (n->pNode->FailedGoalList.IsInList(m_pCurrentGoalWaypoint->pNode))
2641           {
2642                bPathFailed = true;
2643                break; // Can't make path to goal
2644           }
2645 
2646           // Done with calculating
2647           if (n == m_pCurrentGoalWaypoint)
2648           {
2649                m_bCalculatingAStarPath = false;
2650                BotManager.m_sUsingAStarBotsCount--;
2651 
2652                while(n)
2653                {
2654                     m_AStarNodeList.PushNode(n);
2655                     n = n->pParent[0];
2656                }
2657 
2658                CleanAStarLists(false);
2659 
2660                return true;
2661           }
2662 
2663           pPath = n->pNode->ConnectedWPs.GetFirst();
2664           while(pPath)
2665           {
2666                if ((pPath->Entry->iFlags & W_FL_INTAG) &&
2667                     SOLID(S((int)pPath->Entry->v_origin.x, (int)pPath->Entry->v_origin.y)))
2668                {
2669                     pPath = pPath->next;
2670                     continue;
2671                }
2672 
2673                n2 = GetWPFromNode(pPath->Entry);
2674 
2675                if (!n2)
2676                {
2677                     pPath = pPath->next;
2678                     continue;
2679                }
2680 
2681                if (n2->bIsClosed[1])
2682                {
2683                     m_bCalculatingAStarPath = false;
2684                     BotManager.m_sUsingAStarBotsCount--;
2685 
2686                     while(n2)
2687                     {
2688                          m_AStarNodeList.AddNode(n2);
2689                          n2 = n2->pParent[1];
2690                     }
2691 
2692                     while(n)
2693                     {
2694                          m_AStarNodeList.PushNode(n);
2695                          n = n->pParent[0];
2696                     }
2697 
2698                     CleanAStarLists(false);
2699 
2700                     return true;
2701                }
2702 
2703                newg = n->g[0] + (short)AStarCost(n, n2);
2704 
2705                if ((n2->g[0] <= newg) && (n2->bIsOpen[0] || n2->bIsClosed[0]))
2706                {
2707                     pPath = pPath->next;
2708                     continue;
2709                }
2710 
2711                n2->pParent[0] = n;
2712                n2->g[0] = newg;
2713                n2->bIsClosed[0] = false;
2714 
2715                if (!n2->bIsOpen[0])
2716                {
2717                     m_AStarOpenList[0].AddEntry(n2, n2->g[1] + GetDistance(n2->pNode->v_origin,
2718                                                  m_pCurrentGoalWaypoint->pNode->v_origin));
2719                     n2->bIsOpen[0] = true;
2720                }
2721                pPath = pPath->next;
2722           }
2723 
2724           m_AStarClosedList[0].PushNode(n);
2725           n->bIsClosed[0] = true;
2726           iCurrentCycles++;
2727      }
2728 
2729      if (!bPathFailed)
2730      {
2731           while(!m_AStarOpenList[1].Empty())
2732           {
2733                if (iCurrentCycles >= iMaxCycles)
2734                {
2735                     m_bCalculatingAStarPath = true;
2736                     return false;
2737                }
2738 
2739                n = m_AStarOpenList[1].Pop();
2740                n->bIsOpen[1] = false;
2741 
2742                if (n->pNode->FailedGoalList.IsInList(m_pCurrentWaypoint->pNode))
2743                {
2744                     bPathFailed = true;
2745                     break; // Can't make path to goal
2746                }
2747 
2748                // Done with calculating
2749                if (n == m_pCurrentWaypoint)
2750                {
2751                     m_bCalculatingAStarPath = false;
2752                     BotManager.m_sUsingAStarBotsCount--;
2753 
2754                     while(n)
2755                     {
2756                          m_AStarNodeList.AddNode(n);
2757                          n = n->pParent[1];
2758                     }
2759 
2760                     CleanAStarLists(false);
2761 
2762                     return true;
2763                }
2764 
2765                pPath = n->pNode->ConnectedWPsWithMe.GetFirst();
2766                while(pPath)
2767                {
2768                     if ((pPath->Entry->iFlags & W_FL_INTAG) &&
2769                         SOLID(S((int)pPath->Entry->v_origin.x, (int)pPath->Entry->v_origin.y)))
2770                     {
2771                          pPath = pPath->next;
2772                          continue;
2773                     }
2774 
2775                     n2 = GetWPFromNode(pPath->Entry);
2776 
2777                     if (!n2)
2778                     {
2779                          pPath = pPath->next;
2780                          continue;
2781                     }
2782 
2783                     if (n2->bIsClosed[0])
2784                     {
2785                          m_bCalculatingAStarPath = false;
2786                          BotManager.m_sUsingAStarBotsCount--;
2787 
2788                          while(n2)
2789                          {
2790                               m_AStarNodeList.PushNode(n2);
2791                               n2 = n2->pParent[0];
2792                          }
2793 
2794                          while(n)
2795                          {
2796                               m_AStarNodeList.AddNode(n);
2797                               n = n->pParent[1];
2798                          }
2799 
2800                          CleanAStarLists(false);
2801 
2802                          return true;
2803                     }
2804 
2805                     newg = n->g[1] + (short)AStarCost(n, n2);
2806 
2807                     if ((n2->g[1] <= newg) && (n2->bIsOpen[1] || n2->bIsClosed[1]))
2808                     {
2809                          pPath = pPath->next;
2810                          continue;
2811                     }
2812 
2813                     n2->pParent[1] = n;
2814                     n2->g[1] = newg;
2815                     n2->bIsClosed[1] = false;
2816 
2817                     if (!n2->bIsOpen[1])
2818                     {
2819                          m_AStarOpenList[1].AddEntry(n2, n2->g[1] + GetDistance(n2->pNode->v_origin,
2820                                                       m_pCurrentWaypoint->pNode->v_origin));
2821                          n2->bIsOpen[1] = true;
2822                     }
2823                     pPath = pPath->next;
2824                }
2825 
2826                m_AStarClosedList[1].PushNode(n);
2827                n->bIsClosed[1] = true;
2828                iCurrentCycles++;
2829           }
2830      }
2831 
2832      // Failed making path
2833      condebug("Path failed");
2834 
2835      CleanAStarLists(true);
2836      m_bCalculatingAStarPath = false;
2837      BotManager.m_sUsingAStarBotsCount--;
2838      return true;
2839 }
2840 
AStarCost(waypoint_s * pWP1,waypoint_s * pWP2)2841 float CBot::AStarCost(waypoint_s *pWP1, waypoint_s *pWP2)
2842 {
2843      // UNDONE?
2844      return (GetDistance(pWP1->pNode->v_origin, pWP2->pNode->v_origin) * pWP2->pNode->sCost);
2845 }
2846 
CleanAStarLists(bool bPathFailed)2847 void CBot::CleanAStarLists(bool bPathFailed)
2848 {
2849      while(m_AStarOpenList[0].Empty() == false)
2850      {
2851           waypoint_s *p = m_AStarOpenList[0].Pop();
2852           p->bIsOpen[0] = p->bIsOpen[1] = false;
2853           p->bIsClosed[0] = p->bIsClosed[1] = false;
2854           p->pParent[0] = p->pParent[1] = NULL;
2855           p->g[0] = p->g[1] = 0;
2856      }
2857 
2858      while(m_AStarOpenList[1].Empty() == false)
2859      {
2860           waypoint_s *p = m_AStarOpenList[1].Pop();
2861           p->bIsOpen[0] = p->bIsOpen[1] = false;
2862           p->bIsClosed[0] = p->bIsClosed[1] = false;
2863           p->pParent[0] = p->pParent[1] = NULL;
2864           p->g[0] = p->g[1] = 0;
2865      }
2866 
2867      while(m_AStarClosedList[0].Empty() == false)
2868      {
2869           waypoint_s *p = m_AStarClosedList[0].Pop();
2870           p->bIsOpen[0] = p->bIsOpen[1] = false;
2871           p->bIsClosed[0] = p->bIsClosed[1] = false;
2872           p->pParent[0] = p->pParent[1] = NULL;
2873           p->g[0] = p->g[1] = 0;
2874           if (bPathFailed)
2875                p->pNode->FailedGoalList.AddNode(m_pCurrentGoalWaypoint->pNode);
2876      }
2877 
2878      while(m_AStarClosedList[1].Empty() == false)
2879      {
2880           waypoint_s *p = m_AStarClosedList[1].Pop();
2881           p->bIsOpen[0] = p->bIsOpen[1] = false;
2882           p->bIsClosed[0] = p->bIsClosed[1] = false;
2883           p->pParent[0] = p->pParent[1] = NULL;
2884           p->g[0] = p->g[1] = 0;
2885           if (bPathFailed)
2886                p->pNode->FailedGoalList.AddNode(m_pCurrentWaypoint->pNode);
2887      }
2888 }
2889 
ResetWaypointVars()2890 void CBot::ResetWaypointVars()
2891 {
2892      m_iWaypointTime = 0;
2893      m_pCurrentWaypoint = NULL;
2894      m_pCurrentGoalWaypoint = NULL;
2895      m_pPrevWaypoints[0] = NULL;
2896      m_pPrevWaypoints[1] = NULL;
2897      m_pPrevWaypoints[2] = NULL;
2898      m_pPrevWaypoints[3] = NULL;
2899      m_pPrevWaypoints[4] = NULL;
2900      m_fPrevWaypointDistance = 0;
2901      m_iWaypointHeadLastTurnLessTime = 0;
2902      m_iWaypointHeadPauseTime = 0;
2903      if (m_bCalculatingAStarPath)
2904      {
2905           m_bCalculatingAStarPath = false;
2906           BotManager.m_sUsingAStarBotsCount--;
2907      }
2908      m_AStarNodeList.DeleteAllNodes();
2909      CleanAStarLists(false);
2910      m_bGoToDebugGoal = false;
2911 }
2912 
SetCurrentWaypoint(node_s * pNode)2913 void CBot::SetCurrentWaypoint(node_s *pNode)
2914 {
2915      waypoint_s *pWP = GetWPFromNode(pNode);
2916 #ifndef RELEASE_BUILD
2917      if (!pWP || !pNode) condebug("NULL WP In SetCurrentWP");
2918 #endif
2919 
2920      m_pCurrentWaypoint = pWP;
2921      m_iWaypointTime = lastmillis;
2922 }
2923 
SetCurrentWaypoint(waypoint_s * pWP)2924 void CBot::SetCurrentWaypoint(waypoint_s *pWP)
2925 {
2926 #ifndef RELEASE_BUILD
2927      if (!pWP) condebug("NULL WP In SetCurrentWP(2)");
2928 #endif
2929 
2930      m_pCurrentWaypoint = pWP;
2931      m_iWaypointTime = lastmillis;
2932 }
2933 
SetCurrentGoalWaypoint(node_s * pNode)2934 void CBot::SetCurrentGoalWaypoint(node_s *pNode)
2935 {
2936      waypoint_s *pWP = GetWPFromNode(pNode);
2937 #ifndef RELEASE_BUILD
2938      if (!pWP || !pNode) condebug("NULL WP In SetCurrentGoalWP");
2939 #endif
2940 
2941      m_pCurrentGoalWaypoint = pWP;
2942 }
2943 
SetCurrentGoalWaypoint(waypoint_s * pWP)2944 void CBot::SetCurrentGoalWaypoint(waypoint_s *pWP)
2945 {
2946 #ifndef RELEASE_BUILD
2947      if (!pWP) condebug("NULL WP In SetCurrentGoalWP(2)");
2948 #endif
2949 
2950      m_pCurrentGoalWaypoint = pWP;
2951 }
2952 
CurrentWPIsValid()2953 bool CBot::CurrentWPIsValid()
2954 {
2955      if (!m_pCurrentWaypoint)
2956      {
2957           //condebug("Invalid WP: Is NULL");
2958           return false;
2959      }
2960 
2961      // check if the bot has been trying to get to this waypoint for a while...
2962      if ((m_iWaypointTime + 5000) < lastmillis)
2963      {
2964           condebug("Invalid WP: time over");
2965           return false;
2966      }
2967 
2968 #ifndef RELEASE_BUILD
2969      if (!IsVisible(m_pCurrentWaypoint->pNode->v_origin))
2970           condebug("Invalid WP: Not visible");
2971 #endif
2972 
2973      return (IsVisible(m_pCurrentWaypoint->pNode->v_origin));
2974 }
2975 
ReachedGoalWP()2976 bool CBot::ReachedGoalWP()
2977 {
2978      if ((!m_pCurrentWaypoint) || (!m_pCurrentGoalWaypoint))
2979           return false;
2980 
2981      if (m_pCurrentWaypoint != m_pCurrentGoalWaypoint)
2982           return false;
2983 
2984      return ((GetDistance(m_pCurrentGoalWaypoint->pNode->v_origin) <= 3.0f) &&
2985              (IsVisible(m_pCurrentGoalWaypoint->pNode->v_origin)));
2986 }
2987 
GetWPFromNode(node_s * pNode)2988 waypoint_s *CBot::GetWPFromNode(node_s *pNode)
2989 {
2990      if (!pNode) return NULL;
2991 
2992      short x, y;
2993      WaypointClass.GetNodeIndexes(pNode->v_origin, &x, &y);
2994 
2995      TLinkedList<waypoint_s *>::node_s *p = m_WaypointList[x][y].GetFirst();
2996      while(p)
2997      {
2998           if (p->Entry->pNode == pNode)
2999                return p->Entry;
3000 
3001           p = p->next;
3002      }
3003 
3004      return NULL;
3005 }
3006 
GetNearestWaypoint(vec v_src,float flRange)3007 waypoint_s *CBot::GetNearestWaypoint(vec v_src, float flRange)
3008 {
3009      TLinkedList<waypoint_s *>::node_s *p;
3010      waypoint_s *pNearest = NULL;
3011      short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS);
3012      float flNearestDist = 9999.99f, flDist;
3013 
3014      WaypointClass.GetNodeIndexes(v_src, &i, &j);
3015      MinI = i - Offset;
3016      MaxI = i + Offset;
3017      MinJ = j - Offset;
3018      MaxJ = j + Offset;
3019 
3020      if (MinI < 0)
3021           MinI = 0;
3022      if (MaxI > MAX_MAP_GRIDS - 1)
3023           MaxI = MAX_MAP_GRIDS - 1;
3024      if (MinJ < 0)
3025           MinJ = 0;
3026      if (MaxJ > MAX_MAP_GRIDS - 1)
3027           MaxJ = MAX_MAP_GRIDS - 1;
3028 
3029      for (int x=MinI;x<=MaxI;x++)
3030      {
3031           for(int y=MinJ;y<=MaxJ;y++)
3032           {
3033                p = m_WaypointList[x][y].GetFirst();
3034 
3035                while(p)
3036                {
3037                     if (p->Entry->pNode->iFlags & W_FL_FLOOD)
3038                     {
3039                          p = p->next;
3040                          continue;
3041                     }
3042 
3043                     flDist = GetDistance(v_src, p->Entry->pNode->v_origin);
3044                     if ((flDist < flNearestDist) && (flDist <= flRange))
3045                     {
3046                          if (::IsVisible(v_src, p->Entry->pNode->v_origin, NULL))
3047                          {
3048                               pNearest = p->Entry;
3049                               flNearestDist = flDist;
3050                          }
3051                     }
3052 
3053                     p = p->next;
3054                }
3055           }
3056      }
3057      return pNearest;
3058 }
3059 
GetNearestTriggerWaypoint(vec v_src,float flRange)3060 waypoint_s *CBot::GetNearestTriggerWaypoint(vec v_src, float flRange)
3061 {
3062      TLinkedList<waypoint_s *>::node_s *p;
3063      waypoint_s *pNearest = NULL;
3064      short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS);
3065      float flNearestDist = 9999.99f, flDist;
3066 
3067      WaypointClass.GetNodeIndexes(v_src, &i, &j);
3068      MinI = i - Offset;
3069      MaxI = i + Offset;
3070      MinJ = j - Offset;
3071      MaxJ = j + Offset;
3072 
3073      if (MinI < 0)
3074           MinI = 0;
3075      if (MaxI > MAX_MAP_GRIDS - 1)
3076           MaxI = MAX_MAP_GRIDS - 1;
3077      if (MinJ < 0)
3078           MinJ = 0;
3079      if (MaxJ > MAX_MAP_GRIDS - 1)
3080           MaxJ = MAX_MAP_GRIDS - 1;
3081 
3082      for (int x=MinI;x<=MaxI;x++)
3083      {
3084           for(int y=MinJ;y<=MaxJ;y++)
3085           {
3086                p = m_WaypointList[x][y].GetFirst();
3087 
3088                while(p)
3089                {
3090                     if (!(p->Entry->pNode->iFlags & W_FL_TRIGGER))
3091                     {
3092                          p = p->next;
3093                          continue;
3094                     }
3095 
3096                     flDist = GetDistance(v_src, p->Entry->pNode->v_origin);
3097                     if ((flDist < flNearestDist) && (flDist <= flRange))
3098                     {
3099                          if (::IsVisible(v_src, p->Entry->pNode->v_origin, NULL))
3100                          {
3101                               pNearest = p->Entry;
3102                               flNearestDist = flDist;
3103                          }
3104                     }
3105 
3106                     p = p->next;
3107                }
3108           }
3109      }
3110      return pNearest;
3111 }
3112 
3113 // Makes a waypoint list for this bot based on the list from WaypointClass
SyncWaypoints()3114 void CBot::SyncWaypoints()
3115 {
3116      short x, y;
3117      TLinkedList<node_s *>::node_s *p;
3118 
3119      // Clean everything first
3120      for (x=0;x<MAX_MAP_GRIDS;x++)
3121      {
3122           for (y=0;y<MAX_MAP_GRIDS;y++)
3123           {
3124                while(!m_WaypointList[x][y].Empty())
3125                     delete m_WaypointList[x][y].Pop();
3126           }
3127      }
3128 
3129      // Sync
3130      for (x=0;x<MAX_MAP_GRIDS;x++)
3131      {
3132           for (y=0;y<MAX_MAP_GRIDS;y++)
3133           {
3134                p = WaypointClass.m_Waypoints[x][y].GetFirst();
3135                while(p)
3136                {
3137                     waypoint_s *pWP = new waypoint_s;
3138                     pWP->pNode = p->Entry;
3139                     m_WaypointList[x][y].AddNode(pWP);
3140 
3141 #ifndef RELEASE_BUILD
3142                     if (!GetWPFromNode(p->Entry)) condebug("Error adding bot wp!");
3143 #endif
3144 
3145                     p = p->next;
3146                }
3147           }
3148      }
3149 }
3150 
3151 #ifdef WP_FLOOD
GetNearestFloodWP(vec v_origin,float flRange)3152 waypoint_s *CBot::GetNearestFloodWP(vec v_origin, float flRange)
3153 {
3154      TLinkedList<waypoint_s *>::node_s *p;
3155      waypoint_s *pNearest = NULL;
3156      short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS);
3157      float flNearestDist = 9999.99f, flDist;
3158 
3159      WaypointClass.GetNodeIndexes(v_origin, &i, &j);
3160      MinI = i - Offset;
3161      MaxI = i + Offset;
3162      MinJ = j - Offset;
3163      MaxJ = j + Offset;
3164 
3165      if (MinI < 0)
3166           MinI = 0;
3167      if (MaxI > MAX_MAP_GRIDS - 1)
3168           MaxI = MAX_MAP_GRIDS - 1;
3169      if (MinJ < 0)
3170           MinJ = 0;
3171      if (MaxJ > MAX_MAP_GRIDS - 1)
3172           MaxJ = MAX_MAP_GRIDS - 1;
3173 
3174      for (int x=MinI;x<=MaxI;x++)
3175      {
3176           for(int y=MinJ;y<=MaxJ;y++)
3177           {
3178                p = m_WaypointList[x][y].GetFirst();
3179 
3180                while(p)
3181                {
3182                     if (!(p->Entry->pNode->iFlags & W_FL_FLOOD))
3183                     {
3184                          p = p->next;
3185                          continue;
3186                     }
3187 
3188                     flDist = GetDistance(v_origin, p->Entry->pNode->v_origin);
3189                     if ((flDist < flNearestDist) && (flDist <= flRange))
3190                     {
3191                          if (::IsVisible(v_origin, p->Entry->pNode->v_origin, NULL))
3192                          {
3193                               pNearest = p->Entry;
3194                               flNearestDist = flDist;
3195                          }
3196                     }
3197 
3198                     p = p->next;
3199                }
3200           }
3201      }
3202      return pNearest;
3203 }
3204 
GetNearestTriggerFloodWP(vec v_origin,float flRange)3205 waypoint_s *CBot::GetNearestTriggerFloodWP(vec v_origin, float flRange)
3206 {
3207      TLinkedList<waypoint_s *>::node_s *p;
3208      waypoint_s *pNearest = NULL;
3209      short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS);
3210      float flNearestDist = 9999.99f, flDist;
3211 
3212      WaypointClass.GetNodeIndexes(v_origin, &i, &j);
3213      MinI = i - Offset;
3214      MaxI = i + Offset;
3215      MinJ = j - Offset;
3216      MaxJ = j + Offset;
3217 
3218      if (MinI < 0)
3219           MinI = 0;
3220      if (MaxI > MAX_MAP_GRIDS - 1)
3221           MaxI = MAX_MAP_GRIDS - 1;
3222      if (MinJ < 0)
3223           MinJ = 0;
3224      if (MaxJ > MAX_MAP_GRIDS - 1)
3225           MaxJ = MAX_MAP_GRIDS - 1;
3226 
3227      for (int x=MinI;x<=MaxI;x++)
3228      {
3229           for(int y=MinJ;y<=MaxJ;y++)
3230           {
3231                p = m_WaypointList[x][y].GetFirst();
3232 
3233                while(p)
3234                {
3235                     if (!(p->Entry->pNode->iFlags & (W_FL_FLOOD | W_FL_TRIGGER)))
3236                     {
3237                          p = p->next;
3238                          continue;
3239                     }
3240 
3241                     flDist = GetDistance(v_origin, p->Entry->pNode->v_origin);
3242                     if ((flDist < flNearestDist) && (flDist <= flRange))
3243                     {
3244                          if (::IsVisible(v_origin, p->Entry->pNode->v_origin, NULL))
3245                          {
3246                               pNearest = p->Entry;
3247                               flNearestDist = flDist;
3248                          }
3249                     }
3250 
3251                     p = p->next;
3252                }
3253           }
3254      }
3255      return pNearest;
3256 }
3257 
GoToDebugGoal(vec o)3258 void CBot::GoToDebugGoal(vec o)
3259 {
3260      ResetWaypointVars();
3261 
3262      node_s *wp = WaypointClass.GetNearestWaypoint(m_pMyEnt, 20.0f);
3263      node_s *goalwp = WaypointClass.GetNearestWaypoint(player1, 20.0f);
3264 
3265      if (!wp || !goalwp)
3266      {
3267           wp = WaypointClass.GetNearestFloodWP(m_pMyEnt, 8.0f);
3268           goalwp = WaypointClass.GetNearestFloodWP(player1, 5.0f);
3269      }
3270      if (!wp || !goalwp) { condebug("No near WP"); return; }
3271 
3272      SetCurrentWaypoint(wp);
3273      SetCurrentGoalWaypoint(goalwp);
3274      m_vGoal = o;
3275      m_bGoToDebugGoal = true;
3276 }
3277 #endif
3278 
3279 // Bot class end
3280