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