00001 #include "g_local.h"
00002 #include "q_shared.h"
00003 #include "botlib.h"
00004 #include "ai_main.h"
00005
00006 float gWPRenderTime = 0;
00007 float gDeactivated = 0;
00008 float gBotEdit = 0;
00009 int gWPRenderedFrame = 0;
00010
00011 #include "../namespace_begin.h"
00012 wpobject_t *gWPArray[MAX_WPARRAY_SIZE];
00013 int gWPNum = 0;
00014 #include "../namespace_end.h"
00015
00016 int gLastPrintedIndex = -1;
00017
00018 #ifndef _XBOX
00019 nodeobject_t nodetable[MAX_NODETABLE_SIZE];
00020 int nodenum;
00021 #endif
00022
00023 int gLevelFlags = 0;
00024
00025 char *GetFlagStr( int flags )
00026 {
00027 char *flagstr;
00028 int i;
00029
00030 flagstr = (char *)B_TempAlloc(128);
00031 i = 0;
00032
00033 if (!flags)
00034 {
00035 strcpy(flagstr, "none\0");
00036 goto fend;
00037 }
00038
00039 if (flags & WPFLAG_JUMP)
00040 {
00041 flagstr[i] = 'j';
00042 i++;
00043 }
00044
00045 if (flags & WPFLAG_DUCK)
00046 {
00047 flagstr[i] = 'd';
00048 i++;
00049 }
00050
00051 if (flags & WPFLAG_SNIPEORCAMPSTAND)
00052 {
00053 flagstr[i] = 'c';
00054 i++;
00055 }
00056
00057 if (flags & WPFLAG_WAITFORFUNC)
00058 {
00059 flagstr[i] = 'f';
00060 i++;
00061 }
00062
00063 if (flags & WPFLAG_SNIPEORCAMP)
00064 {
00065 flagstr[i] = 's';
00066 i++;
00067 }
00068
00069 if (flags & WPFLAG_ONEWAY_FWD)
00070 {
00071 flagstr[i] = 'x';
00072 i++;
00073 }
00074
00075 if (flags & WPFLAG_ONEWAY_BACK)
00076 {
00077 flagstr[i] = 'y';
00078 i++;
00079 }
00080
00081 if (flags & WPFLAG_GOALPOINT)
00082 {
00083 flagstr[i] = 'g';
00084 i++;
00085 }
00086
00087 if (flags & WPFLAG_NOVIS)
00088 {
00089 flagstr[i] = 'n';
00090 i++;
00091 }
00092
00093 if (flags & WPFLAG_NOMOVEFUNC)
00094 {
00095 flagstr[i] = 'm';
00096 i++;
00097 }
00098
00099 if (flags & WPFLAG_RED_FLAG)
00100 {
00101 if (i)
00102 {
00103 flagstr[i] = ' ';
00104 i++;
00105 }
00106 flagstr[i] = 'r';
00107 i++;
00108 flagstr[i] = 'e';
00109 i++;
00110 flagstr[i] = 'd';
00111 i++;
00112 flagstr[i] = ' ';
00113 i++;
00114 flagstr[i] = 'f';
00115 i++;
00116 flagstr[i] = 'l';
00117 i++;
00118 flagstr[i] = 'a';
00119 i++;
00120 flagstr[i] = 'g';
00121 i++;
00122 }
00123
00124 if (flags & WPFLAG_BLUE_FLAG)
00125 {
00126 if (i)
00127 {
00128 flagstr[i] = ' ';
00129 i++;
00130 }
00131 flagstr[i] = 'b';
00132 i++;
00133 flagstr[i] = 'l';
00134 i++;
00135 flagstr[i] = 'u';
00136 i++;
00137 flagstr[i] = 'e';
00138 i++;
00139 flagstr[i] = ' ';
00140 i++;
00141 flagstr[i] = 'f';
00142 i++;
00143 flagstr[i] = 'l';
00144 i++;
00145 flagstr[i] = 'a';
00146 i++;
00147 flagstr[i] = 'g';
00148 i++;
00149 }
00150
00151 if (flags & WPFLAG_SIEGE_IMPERIALOBJ)
00152 {
00153 if (i)
00154 {
00155 flagstr[i] = ' ';
00156 i++;
00157 }
00158 flagstr[i] = 's';
00159 i++;
00160 flagstr[i] = 'a';
00161 i++;
00162 flagstr[i] = 'g';
00163 i++;
00164 flagstr[i] = 'a';
00165 i++;
00166 flagstr[i] = '_';
00167 i++;
00168 flagstr[i] = 'i';
00169 i++;
00170 flagstr[i] = 'm';
00171 i++;
00172 flagstr[i] = 'p';
00173 i++;
00174 }
00175
00176 if (flags & WPFLAG_SIEGE_REBELOBJ)
00177 {
00178 if (i)
00179 {
00180 flagstr[i] = ' ';
00181 i++;
00182 }
00183 flagstr[i] = 's';
00184 i++;
00185 flagstr[i] = 'a';
00186 i++;
00187 flagstr[i] = 'g';
00188 i++;
00189 flagstr[i] = 'a';
00190 i++;
00191 flagstr[i] = '_';
00192 i++;
00193 flagstr[i] = 'r';
00194 i++;
00195 flagstr[i] = 'e';
00196 i++;
00197 flagstr[i] = 'b';
00198 i++;
00199 }
00200
00201 flagstr[i] = '\0';
00202
00203 if (i == 0)
00204 {
00205 strcpy(flagstr, "unknown\0");
00206 }
00207
00208 fend:
00209 return flagstr;
00210 }
00211
00212 void G_TestLine(vec3_t start, vec3_t end, int color, int time)
00213 {
00214 gentity_t *te;
00215
00216 te = G_TempEntity( start, EV_TESTLINE );
00217 VectorCopy(start, te->s.origin);
00218 VectorCopy(end, te->s.origin2);
00219 te->s.time2 = time;
00220 te->s.weapon = color;
00221 te->r.svFlags |= SVF_BROADCAST;
00222 }
00223
00224 void BotWaypointRender(void)
00225 {
00226 int i, n;
00227 int inc_checker;
00228 int bestindex;
00229 int gotbestindex;
00230 float bestdist;
00231 float checkdist;
00232 gentity_t *plum;
00233 gentity_t *viewent;
00234 char *flagstr;
00235 vec3_t a;
00236
00237 if (!gBotEdit)
00238 {
00239 return;
00240 }
00241
00242 bestindex = 0;
00243
00244 if (gWPRenderTime > level.time)
00245 {
00246 goto checkprint;
00247 }
00248
00249 gWPRenderTime = level.time + 100;
00250
00251 i = gWPRenderedFrame;
00252 inc_checker = gWPRenderedFrame;
00253
00254 while (i < gWPNum)
00255 {
00256 if (gWPArray[i] && gWPArray[i]->inuse)
00257 {
00258 plum = G_TempEntity( gWPArray[i]->origin, EV_SCOREPLUM );
00259 plum->r.svFlags |= SVF_BROADCAST;
00260 plum->s.time = i;
00261
00262 n = 0;
00263
00264 while (n < gWPArray[i]->neighbornum)
00265 {
00266 if (gWPArray[i]->neighbors[n].forceJumpTo && gWPArray[gWPArray[i]->neighbors[n].num])
00267 {
00268 G_TestLine(gWPArray[i]->origin, gWPArray[gWPArray[i]->neighbors[n].num]->origin, 0x0000ff, 5000);
00269 }
00270 n++;
00271 }
00272
00273 gWPRenderedFrame++;
00274 }
00275 else
00276 {
00277 gWPRenderedFrame = 0;
00278 break;
00279 }
00280
00281 if ((i - inc_checker) > 4)
00282 {
00283 break;
00284 }
00285 i++;
00286 }
00287
00288 if (i >= gWPNum)
00289 {
00290 gWPRenderTime = level.time + 1500;
00291 gWPRenderedFrame = 0;
00292 }
00293
00294 checkprint:
00295
00296 if (!bot_wp_info.value)
00297 {
00298 return;
00299 }
00300
00301 viewent = &g_entities[0];
00302
00303 if (!viewent || !viewent->client)
00304 {
00305 return;
00306 }
00307
00308 bestdist = 256;
00309 gotbestindex = 0;
00310
00311 i = 0;
00312
00313 while (i < gWPNum)
00314 {
00315 if (gWPArray[i] && gWPArray[i]->inuse)
00316 {
00317 VectorSubtract(viewent->client->ps.origin, gWPArray[i]->origin, a);
00318
00319 checkdist = VectorLength(a);
00320
00321 if (checkdist < bestdist)
00322 {
00323 bestdist = checkdist;
00324 bestindex = i;
00325 gotbestindex = 1;
00326 }
00327 }
00328 i++;
00329 }
00330
00331 if (gotbestindex && bestindex != gLastPrintedIndex)
00332 {
00333 flagstr = GetFlagStr(gWPArray[bestindex]->flags);
00334 gLastPrintedIndex = bestindex;
00335 G_Printf(S_COLOR_YELLOW "Waypoint %i\nFlags - %i (%s) (w%f)\nOrigin - (%i %i %i)\n", (int)(gWPArray[bestindex]->index), (int)(gWPArray[bestindex]->flags), flagstr, gWPArray[bestindex]->weight, (int)(gWPArray[bestindex]->origin[0]), (int)(gWPArray[bestindex]->origin[1]), (int)(gWPArray[bestindex]->origin[2]));
00336
00337 B_TempFree(128);
00338
00339 plum = G_TempEntity( gWPArray[bestindex]->origin, EV_SCOREPLUM );
00340 plum->r.svFlags |= SVF_BROADCAST;
00341 plum->s.time = bestindex;
00342 }
00343 else if (!gotbestindex)
00344 {
00345 gLastPrintedIndex = -1;
00346 }
00347 }
00348
00349 void TransferWPData(int from, int to)
00350 {
00351 if (!gWPArray[to])
00352 {
00353 gWPArray[to] = (wpobject_t *)B_Alloc(sizeof(wpobject_t));
00354 }
00355
00356 if (!gWPArray[to])
00357 {
00358 G_Printf(S_COLOR_RED "FATAL ERROR: Could not allocated memory for waypoint\n");
00359 }
00360
00361 gWPArray[to]->flags = gWPArray[from]->flags;
00362 gWPArray[to]->weight = gWPArray[from]->weight;
00363 gWPArray[to]->associated_entity = gWPArray[from]->associated_entity;
00364 gWPArray[to]->disttonext = gWPArray[from]->disttonext;
00365 gWPArray[to]->forceJumpTo = gWPArray[from]->forceJumpTo;
00366 gWPArray[to]->index = to;
00367 gWPArray[to]->inuse = gWPArray[from]->inuse;
00368 VectorCopy(gWPArray[from]->origin, gWPArray[to]->origin);
00369 }
00370
00371 void CreateNewWP(vec3_t origin, int flags)
00372 {
00373 if (gWPNum >= MAX_WPARRAY_SIZE)
00374 {
00375 if (!g_RMG.integer)
00376 {
00377 G_Printf(S_COLOR_YELLOW "Warning: Waypoint limit hit (%i)\n", MAX_WPARRAY_SIZE);
00378 }
00379 return;
00380 }
00381
00382 if (!gWPArray[gWPNum])
00383 {
00384 gWPArray[gWPNum] = (wpobject_t *)B_Alloc(sizeof(wpobject_t));
00385 }
00386
00387 if (!gWPArray[gWPNum])
00388 {
00389 G_Printf(S_COLOR_RED "ERROR: Could not allocated memory for waypoint\n");
00390 }
00391
00392 gWPArray[gWPNum]->flags = flags;
00393 gWPArray[gWPNum]->weight = 0;
00394 gWPArray[gWPNum]->associated_entity = ENTITYNUM_NONE;
00395 gWPArray[gWPNum]->forceJumpTo = 0;
00396 gWPArray[gWPNum]->disttonext = 0;
00397 gWPArray[gWPNum]->index = gWPNum;
00398 gWPArray[gWPNum]->inuse = 1;
00399 VectorCopy(origin, gWPArray[gWPNum]->origin);
00400 gWPNum++;
00401 }
00402
00403 void CreateNewWP_FromObject(wpobject_t *wp)
00404 {
00405 int i;
00406
00407 if (gWPNum >= MAX_WPARRAY_SIZE)
00408 {
00409 return;
00410 }
00411
00412 if (!gWPArray[gWPNum])
00413 {
00414 gWPArray[gWPNum] = (wpobject_t *)B_Alloc(sizeof(wpobject_t));
00415 }
00416
00417 if (!gWPArray[gWPNum])
00418 {
00419 G_Printf(S_COLOR_RED "ERROR: Could not allocated memory for waypoint\n");
00420 }
00421
00422 gWPArray[gWPNum]->flags = wp->flags;
00423 gWPArray[gWPNum]->weight = wp->weight;
00424 gWPArray[gWPNum]->associated_entity = wp->associated_entity;
00425 gWPArray[gWPNum]->disttonext = wp->disttonext;
00426 gWPArray[gWPNum]->forceJumpTo = wp->forceJumpTo;
00427 gWPArray[gWPNum]->index = gWPNum;
00428 gWPArray[gWPNum]->inuse = 1;
00429 VectorCopy(wp->origin, gWPArray[gWPNum]->origin);
00430 gWPArray[gWPNum]->neighbornum = wp->neighbornum;
00431
00432 i = wp->neighbornum;
00433
00434 while (i >= 0)
00435 {
00436 gWPArray[gWPNum]->neighbors[i].num = wp->neighbors[i].num;
00437 gWPArray[gWPNum]->neighbors[i].forceJumpTo = wp->neighbors[i].forceJumpTo;
00438
00439 i--;
00440 }
00441
00442 if (gWPArray[gWPNum]->flags & WPFLAG_RED_FLAG)
00443 {
00444 flagRed = gWPArray[gWPNum];
00445 oFlagRed = flagRed;
00446 }
00447 else if (gWPArray[gWPNum]->flags & WPFLAG_BLUE_FLAG)
00448 {
00449 flagBlue = gWPArray[gWPNum];
00450 oFlagBlue = flagBlue;
00451 }
00452
00453 gWPNum++;
00454 }
00455
00456 void RemoveWP(void)
00457 {
00458 if (gWPNum <= 0)
00459 {
00460 return;
00461 }
00462
00463 gWPNum--;
00464
00465 if (!gWPArray[gWPNum] || !gWPArray[gWPNum]->inuse)
00466 {
00467 return;
00468 }
00469
00470
00471 if (gWPArray[gWPNum])
00472 {
00473 memset( gWPArray[gWPNum], 0, sizeof(gWPArray[gWPNum]) );
00474 }
00475
00476
00477
00478 if (gWPArray[gWPNum])
00479 {
00480 gWPArray[gWPNum]->inuse = 0;
00481 }
00482 }
00483
00484 void RemoveAllWP(void)
00485 {
00486 while(gWPNum) {
00487 RemoveWP();
00488 }
00489 }
00490
00491 void RemoveWP_InTrail(int afterindex)
00492 {
00493 int foundindex;
00494 int foundanindex;
00495 int didchange;
00496 int i;
00497
00498 foundindex = 0;
00499 foundanindex = 0;
00500 didchange = 0;
00501 i = 0;
00502
00503 if (afterindex < 0 || afterindex >= gWPNum)
00504 {
00505 G_Printf(S_COLOR_YELLOW "Waypoint number %i does not exist\n", afterindex);
00506 return;
00507 }
00508
00509 while (i < gWPNum)
00510 {
00511 if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index == afterindex)
00512 {
00513 foundindex = i;
00514 foundanindex = 1;
00515 break;
00516 }
00517
00518 i++;
00519 }
00520
00521 if (!foundanindex)
00522 {
00523 G_Printf(S_COLOR_YELLOW "Waypoint index %i should exist, but does not (?)\n", afterindex);
00524 return;
00525 }
00526
00527 i = 0;
00528
00529 while (i <= gWPNum)
00530 {
00531 if (gWPArray[i] && gWPArray[i]->index == foundindex)
00532 {
00533
00534
00535
00536 memset( gWPArray[i], 0, sizeof(gWPArray[i]) );
00537
00538
00539 gWPArray[i]->inuse = 0;
00540 didchange = 1;
00541 }
00542 else if (gWPArray[i] && didchange)
00543 {
00544 TransferWPData(i, i-1);
00545
00546
00547
00548 memset( gWPArray[i], 0, sizeof(gWPArray[i]) );
00549
00550
00551 gWPArray[i]->inuse = 0;
00552 }
00553
00554 i++;
00555 }
00556 gWPNum--;
00557 }
00558
00559 int CreateNewWP_InTrail(vec3_t origin, int flags, int afterindex)
00560 {
00561 int foundindex;
00562 int foundanindex;
00563 int i;
00564
00565 foundindex = 0;
00566 foundanindex = 0;
00567 i = 0;
00568
00569 if (gWPNum >= MAX_WPARRAY_SIZE)
00570 {
00571 if (!g_RMG.integer)
00572 {
00573 G_Printf(S_COLOR_YELLOW "Warning: Waypoint limit hit (%i)\n", MAX_WPARRAY_SIZE);
00574 }
00575 return 0;
00576 }
00577
00578 if (afterindex < 0 || afterindex >= gWPNum)
00579 {
00580 G_Printf(S_COLOR_YELLOW "Waypoint number %i does not exist\n", afterindex);
00581 return 0;
00582 }
00583
00584 while (i < gWPNum)
00585 {
00586 if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index == afterindex)
00587 {
00588 foundindex = i;
00589 foundanindex = 1;
00590 break;
00591 }
00592
00593 i++;
00594 }
00595
00596 if (!foundanindex)
00597 {
00598 G_Printf(S_COLOR_YELLOW "Waypoint index %i should exist, but does not (?)\n", afterindex);
00599 return 0;
00600 }
00601
00602 i = gWPNum;
00603
00604 while (i >= 0)
00605 {
00606 if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index != foundindex)
00607 {
00608 TransferWPData(i, i+1);
00609 }
00610 else if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index == foundindex)
00611 {
00612 i++;
00613
00614 if (!gWPArray[i])
00615 {
00616 gWPArray[i] = (wpobject_t *)B_Alloc(sizeof(wpobject_t));
00617 }
00618
00619 gWPArray[i]->flags = flags;
00620 gWPArray[i]->weight = 0;
00621 gWPArray[i]->associated_entity = ENTITYNUM_NONE;
00622 gWPArray[i]->disttonext = 0;
00623 gWPArray[i]->forceJumpTo = 0;
00624 gWPArray[i]->index = i;
00625 gWPArray[i]->inuse = 1;
00626 VectorCopy(origin, gWPArray[i]->origin);
00627 gWPNum++;
00628 break;
00629 }
00630
00631 i--;
00632 }
00633
00634 return 1;
00635 }
00636
00637 int CreateNewWP_InsertUnder(vec3_t origin, int flags, int afterindex)
00638 {
00639 int foundindex;
00640 int foundanindex;
00641 int i;
00642
00643 foundindex = 0;
00644 foundanindex = 0;
00645 i = 0;
00646
00647 if (gWPNum >= MAX_WPARRAY_SIZE)
00648 {
00649 if (!g_RMG.integer)
00650 {
00651 G_Printf(S_COLOR_YELLOW "Warning: Waypoint limit hit (%i)\n", MAX_WPARRAY_SIZE);
00652 }
00653 return 0;
00654 }
00655
00656 if (afterindex < 0 || afterindex >= gWPNum)
00657 {
00658 G_Printf(S_COLOR_YELLOW "Waypoint number %i does not exist\n", afterindex);
00659 return 0;
00660 }
00661
00662 while (i < gWPNum)
00663 {
00664 if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index == afterindex)
00665 {
00666 foundindex = i;
00667 foundanindex = 1;
00668 break;
00669 }
00670
00671 i++;
00672 }
00673
00674 if (!foundanindex)
00675 {
00676 G_Printf(S_COLOR_YELLOW "Waypoint index %i should exist, but does not (?)\n", afterindex);
00677 return 0;
00678 }
00679
00680 i = gWPNum;
00681
00682 while (i >= 0)
00683 {
00684 if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index != foundindex)
00685 {
00686 TransferWPData(i, i+1);
00687 }
00688 else if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index == foundindex)
00689 {
00690
00691 TransferWPData(i, i+1);
00692
00693 if (!gWPArray[i])
00694 {
00695 gWPArray[i] = (wpobject_t *)B_Alloc(sizeof(wpobject_t));
00696 }
00697
00698 gWPArray[i]->flags = flags;
00699 gWPArray[i]->weight = 0;
00700 gWPArray[i]->associated_entity = ENTITYNUM_NONE;
00701 gWPArray[i]->disttonext = 0;
00702 gWPArray[i]->forceJumpTo = 0;
00703 gWPArray[i]->index = i;
00704 gWPArray[i]->inuse = 1;
00705 VectorCopy(origin, gWPArray[i]->origin);
00706 gWPNum++;
00707 break;
00708 }
00709
00710 i--;
00711 }
00712
00713 return 1;
00714 }
00715
00716 void TeleportToWP(gentity_t *pl, int afterindex)
00717 {
00718 int foundindex;
00719 int foundanindex;
00720 int i;
00721
00722 if (!pl || !pl->client)
00723 {
00724 return;
00725 }
00726
00727 foundindex = 0;
00728 foundanindex = 0;
00729 i = 0;
00730
00731 if (afterindex < 0 || afterindex >= gWPNum)
00732 {
00733 G_Printf(S_COLOR_YELLOW "Waypoint number %i does not exist\n", afterindex);
00734 return;
00735 }
00736
00737 while (i < gWPNum)
00738 {
00739 if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index == afterindex)
00740 {
00741 foundindex = i;
00742 foundanindex = 1;
00743 break;
00744 }
00745
00746 i++;
00747 }
00748
00749 if (!foundanindex)
00750 {
00751 G_Printf(S_COLOR_YELLOW "Waypoint index %i should exist, but does not (?)\n", afterindex);
00752 return;
00753 }
00754
00755 VectorCopy(gWPArray[foundindex]->origin, pl->client->ps.origin);
00756
00757 return;
00758 }
00759
00760 void WPFlagsModify(int wpnum, int flags)
00761 {
00762 if (wpnum < 0 || wpnum >= gWPNum || !gWPArray[wpnum] || !gWPArray[wpnum]->inuse)
00763 {
00764 G_Printf(S_COLOR_YELLOW "WPFlagsModify: Waypoint %i does not exist\n", wpnum);
00765 return;
00766 }
00767
00768 gWPArray[wpnum]->flags = flags;
00769 }
00770
00771 static int NotWithinRange(int base, int extent)
00772 {
00773 if (extent > base && base+5 >= extent)
00774 {
00775 return 0;
00776 }
00777
00778 if (extent < base && base-5 <= extent)
00779 {
00780 return 0;
00781 }
00782
00783 return 1;
00784 }
00785
00786 #ifndef _XBOX
00787 int NodeHere(vec3_t spot)
00788 {
00789 int i;
00790
00791 i = 0;
00792
00793 while (i < nodenum)
00794 {
00795 if ((int)nodetable[i].origin[0] == (int)spot[0] &&
00796 (int)nodetable[i].origin[1] == (int)spot[1])
00797 {
00798 if ((int)nodetable[i].origin[2] == (int)spot[2] ||
00799 ((int)nodetable[i].origin[2] < (int)spot[2] && (int)nodetable[i].origin[2]+5 > (int)spot[2]) ||
00800 ((int)nodetable[i].origin[2] > (int)spot[2] && (int)nodetable[i].origin[2]-5 < (int)spot[2]))
00801 {
00802 return 1;
00803 }
00804 }
00805 i++;
00806 }
00807
00808 return 0;
00809 }
00810 #endif
00811
00812 int CanGetToVector(vec3_t org1, vec3_t org2, vec3_t mins, vec3_t maxs)
00813 {
00814 trace_t tr;
00815
00816 trap_Trace(&tr, org1, mins, maxs, org2, ENTITYNUM_NONE, MASK_SOLID);
00817
00818 if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid)
00819 {
00820 return 1;
00821 }
00822
00823 return 0;
00824 }
00825
00826 #if 0
00827 int CanGetToVectorTravel(vec3_t org1, vec3_t org2, vec3_t mins, vec3_t maxs)
00828 {
00829 trace_t tr;
00830 vec3_t a, ang, fwd;
00831 vec3_t midpos, dmid;
00832 float startheight, midheight, fLen;
00833
00834 mins[2] = -13;
00835 maxs[2] = 13;
00836
00837 trap_Trace(&tr, org1, mins, maxs, org2, ENTITYNUM_NONE, MASK_SOLID);
00838
00839 if (tr.fraction != 1 || tr.startsolid || tr.allsolid)
00840 {
00841 return 0;
00842 }
00843
00844 VectorSubtract(org2, org1, a);
00845
00846 vectoangles(a, ang);
00847
00848 AngleVectors(ang, fwd, NULL, NULL);
00849
00850 fLen = VectorLength(a)/2;
00851
00852 midpos[0] = org1[0] + fwd[0]*fLen;
00853 midpos[1] = org1[1] + fwd[1]*fLen;
00854 midpos[2] = org1[2] + fwd[2]*fLen;
00855
00856 VectorCopy(org1, dmid);
00857 dmid[2] -= 1024;
00858
00859 trap_Trace(&tr, midpos, NULL, NULL, dmid, ENTITYNUM_NONE, MASK_SOLID);
00860
00861 startheight = org1[2] - tr.endpos[2];
00862
00863 VectorCopy(midpos, dmid);
00864 dmid[2] -= 1024;
00865
00866 trap_Trace(&tr, midpos, NULL, NULL, dmid, ENTITYNUM_NONE, MASK_SOLID);
00867
00868 if (tr.startsolid || tr.allsolid)
00869 {
00870 return 1;
00871 }
00872
00873 midheight = midpos[2] - tr.endpos[2];
00874
00875 if (midheight > startheight*2)
00876 {
00877 return 0;
00878 }
00879
00880 return 1;
00881 }
00882 #else
00883 int CanGetToVectorTravel(vec3_t org1, vec3_t moveTo, vec3_t mins, vec3_t maxs)
00884
00885 {
00886 trace_t tr;
00887 vec3_t stepTo;
00888 vec3_t stepSub;
00889 vec3_t stepGoal;
00890 vec3_t workingOrg;
00891 vec3_t lastIncrement;
00892 vec3_t finalMeasure;
00893 float stepSize = 0;
00894 float measureLength = 0;
00895 int didMove = 0;
00896 int traceMask = MASK_PLAYERSOLID;
00897 qboolean initialDone = qfalse;
00898
00899 VectorCopy(org1, workingOrg);
00900 VectorCopy(org1, lastIncrement);
00901
00902 VectorCopy(moveTo, stepTo);
00903 stepTo[2] = workingOrg[2];
00904
00905 VectorSubtract(stepTo, workingOrg, stepSub);
00906 stepSize = VectorLength(stepSub);
00907
00908 VectorNormalize(stepSub);
00909
00910 while (!initialDone || didMove)
00911 {
00912 initialDone = qtrue;
00913 didMove = 0;
00914
00915 stepGoal[0] = workingOrg[0] + stepSub[0]*stepSize;
00916 stepGoal[1] = workingOrg[1] + stepSub[1]*stepSize;
00917 stepGoal[2] = workingOrg[2] + stepSub[2]*stepSize;
00918
00919 trap_Trace(&tr, workingOrg, mins, maxs, stepGoal, ENTITYNUM_NONE, traceMask);
00920
00921 if (!tr.startsolid && !tr.allsolid && tr.fraction)
00922 {
00923 vec3_t vecSub;
00924 VectorSubtract(workingOrg, tr.endpos, vecSub);
00925
00926 if (VectorLength(vecSub) > (stepSize/2))
00927 {
00928 workingOrg[0] = tr.endpos[0];
00929 workingOrg[1] = tr.endpos[1];
00930
00931 didMove = 1;
00932 }
00933 }
00934
00935 if (didMove != 1)
00936 {
00937 vec3_t trFrom;
00938 vec3_t trTo;
00939 vec3_t trDir;
00940 vec3_t vecMeasure;
00941
00942 VectorCopy(tr.endpos, trFrom);
00943 trFrom[2] += 16;
00944
00945 VectorSubtract(stepGoal, workingOrg, trDir);
00946 VectorNormalize(trDir);
00947 trTo[0] = tr.endpos[0] + trDir[0]*2;
00948 trTo[1] = tr.endpos[1] + trDir[1]*2;
00949 trTo[2] = tr.endpos[2] + trDir[2]*2;
00950 trTo[2] += 16;
00951
00952 VectorSubtract(trFrom, trTo, vecMeasure);
00953
00954 if (VectorLength(vecMeasure) > 1)
00955 {
00956 trap_Trace(&tr, trFrom, mins, maxs, trTo, ENTITYNUM_NONE, traceMask);
00957
00958 if (!tr.startsolid && !tr.allsolid && tr.fraction == 1)
00959 {
00960 vec3_t trDown;
00961 vec3_t trUp;
00962 VectorCopy(tr.endpos, trUp);
00963 VectorCopy(tr.endpos, trDown);
00964 trDown[2] -= 16;
00965
00966 trap_Trace(&tr, trFrom, mins, maxs, trTo, ENTITYNUM_NONE, traceMask);
00967
00968 if (!tr.startsolid && !tr.allsolid)
00969 {
00970 VectorCopy(tr.endpos, workingOrg);
00971
00972 didMove = 1;
00973 }
00974 }
00975 }
00976 }
00977
00978 VectorSubtract(lastIncrement, workingOrg, finalMeasure);
00979 measureLength = VectorLength(finalMeasure);
00980
00981 if (!measureLength)
00982 {
00983 break;
00984 }
00985
00986 stepSize -= measureLength;
00987 if (stepSize <= 0)
00988 {
00989 break;
00990 }
00991
00992 VectorCopy(workingOrg, lastIncrement);
00993 }
00994
00995 return didMove;
00996 }
00997 #endif
00998
00999 #ifndef _XBOX
01000 int ConnectTrail(int startindex, int endindex, qboolean behindTheScenes)
01001 {
01002 int foundit;
01003 int cancontinue;
01004 int i;
01005 int failsafe;
01006 int successnodeindex;
01007 int insertindex;
01008 int prenodestart;
01009 byte extendednodes[MAX_NODETABLE_SIZE];
01010 float fvecmeas;
01011 float baseheight;
01012 float branchDistance;
01013 float maxDistFactor = 256;
01014 vec3_t a;
01015 vec3_t startplace, starttrace;
01016 vec3_t mins, maxs;
01017 vec3_t testspot;
01018 vec3_t validspotpos;
01019 trace_t tr;
01020
01021 if (g_RMG.integer)
01022 {
01023 if (!(gWPArray[startindex]->flags & WPFLAG_NEVERONEWAY) &&
01024 !(gWPArray[endindex]->flags & WPFLAG_NEVERONEWAY))
01025 {
01026 gWPArray[startindex]->flags |= WPFLAG_ONEWAY_FWD;
01027 gWPArray[endindex]->flags |= WPFLAG_ONEWAY_BACK;
01028 }
01029 return 0;
01030 }
01031
01032 if (!g_RMG.integer)
01033 {
01034 branchDistance = TABLE_BRANCH_DISTANCE;
01035 }
01036 else
01037 {
01038 branchDistance = 512;
01039 }
01040
01041 if (g_RMG.integer)
01042 {
01043 maxDistFactor = 700;
01044 }
01045
01046 mins[0] = -15;
01047 mins[1] = -15;
01048 mins[2] = 0;
01049 maxs[0] = 15;
01050 maxs[1] = 15;
01051 maxs[2] = 0;
01052
01053 nodenum = 0;
01054 foundit = 0;
01055
01056 i = 0;
01057
01058 successnodeindex = 0;
01059
01060 while (i < MAX_NODETABLE_SIZE)
01061 {
01062 nodetable[i].flags = 0;
01063
01064 nodetable[i].inuse = 0;
01065 nodetable[i].neighbornum = 0;
01066 nodetable[i].origin[0] = 0;
01067 nodetable[i].origin[1] = 0;
01068 nodetable[i].origin[2] = 0;
01069 nodetable[i].weight = 0;
01070
01071 extendednodes[i] = 0;
01072
01073 i++;
01074 }
01075
01076 i = 0;
01077
01078 if (!behindTheScenes)
01079 {
01080 G_Printf(S_COLOR_YELLOW "Point %i is not connected to %i - Repairing...\n", startindex, endindex);
01081 }
01082
01083 VectorCopy(gWPArray[startindex]->origin, startplace);
01084
01085 VectorCopy(startplace, starttrace);
01086
01087 starttrace[2] -= 4096;
01088
01089 trap_Trace(&tr, startplace, NULL, NULL, starttrace, ENTITYNUM_NONE, MASK_SOLID);
01090
01091 baseheight = startplace[2] - tr.endpos[2];
01092
01093 cancontinue = 1;
01094
01095 VectorCopy(startplace, nodetable[nodenum].origin);
01096 nodetable[nodenum].weight = 1;
01097 nodetable[nodenum].inuse = 1;
01098
01099 nodenum++;
01100
01101 while (nodenum < MAX_NODETABLE_SIZE && !foundit && cancontinue)
01102 {
01103 if (g_RMG.integer)
01104 {
01105 vec3_t startDist;
01106 vec3_t endDist;
01107 float startDistf;
01108 float endDistf;
01109
01110 VectorSubtract(nodetable[nodenum-1].origin, gWPArray[startindex]->origin, startDist);
01111 VectorSubtract(nodetable[nodenum-1].origin, gWPArray[endindex]->origin, endDist);
01112
01113 startDistf = VectorLength(startDist);
01114 endDistf = VectorLength(endDist);
01115
01116 if (startDistf < 64 || endDistf < 64)
01117 {
01118 branchDistance = 64;
01119 }
01120 else if (startDistf < 128 || endDistf < 128)
01121 {
01122 branchDistance = 128;
01123 }
01124 else if (startDistf < 256 || endDistf < 256)
01125 {
01126 branchDistance = 256;
01127 }
01128 else if (startDistf < 512 || endDistf < 512)
01129 {
01130 branchDistance = 512;
01131 }
01132 else
01133 {
01134 branchDistance = 800;
01135 }
01136 }
01137 cancontinue = 0;
01138 i = 0;
01139 prenodestart = nodenum;
01140
01141 while (i < prenodestart)
01142 {
01143 if (extendednodes[i] != 1)
01144 {
01145 VectorSubtract(gWPArray[endindex]->origin, nodetable[i].origin, a);
01146 fvecmeas = VectorLength(a);
01147
01148 if (fvecmeas < 128 && CanGetToVector(gWPArray[endindex]->origin, nodetable[i].origin, mins, maxs))
01149 {
01150 foundit = 1;
01151 successnodeindex = i;
01152 break;
01153 }
01154
01155 VectorCopy(nodetable[i].origin, testspot);
01156 testspot[0] += branchDistance;
01157
01158 VectorCopy(testspot, starttrace);
01159
01160 starttrace[2] -= 4096;
01161
01162 trap_Trace(&tr, testspot, NULL, NULL, starttrace, ENTITYNUM_NONE, MASK_SOLID);
01163
01164 testspot[2] = tr.endpos[2]+baseheight;
01165
01166 if (!NodeHere(testspot) && !tr.startsolid && !tr.allsolid && CanGetToVector(nodetable[i].origin, testspot, mins, maxs))
01167 {
01168 VectorCopy(testspot, nodetable[nodenum].origin);
01169 nodetable[nodenum].inuse = 1;
01170
01171 nodetable[nodenum].weight = nodetable[i].weight+1;
01172 nodetable[nodenum].neighbornum = i;
01173 if ((nodetable[i].origin[2] - nodetable[nodenum].origin[2]) > 50)
01174 {
01175 nodetable[nodenum].flags = WPFLAG_ONEWAY_FWD;
01176 }
01177 nodenum++;
01178 cancontinue = 1;
01179 }
01180
01181 if (nodenum >= MAX_NODETABLE_SIZE)
01182 {
01183 break;
01184 }
01185
01186 VectorCopy(nodetable[i].origin, testspot);
01187 testspot[0] -= branchDistance;
01188
01189 VectorCopy(testspot, starttrace);
01190
01191 starttrace[2] -= 4096;
01192
01193 trap_Trace(&tr, testspot, NULL, NULL, starttrace, ENTITYNUM_NONE, MASK_SOLID);
01194
01195 testspot[2] = tr.endpos[2]+baseheight;
01196
01197 if (!NodeHere(testspot) && !tr.startsolid && !tr.allsolid && CanGetToVector(nodetable[i].origin, testspot, mins, maxs))
01198 {
01199 VectorCopy(testspot, nodetable[nodenum].origin);
01200 nodetable[nodenum].inuse = 1;
01201
01202 nodetable[nodenum].weight = nodetable[i].weight+1;
01203 nodetable[nodenum].neighbornum = i;
01204 if ((nodetable[i].origin[2] - nodetable[nodenum].origin[2]) > 50)
01205 {
01206 nodetable[nodenum].flags = WPFLAG_ONEWAY_FWD;
01207 }
01208 nodenum++;
01209 cancontinue = 1;
01210 }
01211
01212 if (nodenum >= MAX_NODETABLE_SIZE)
01213 {
01214 break;
01215 }
01216
01217 VectorCopy(nodetable[i].origin, testspot);
01218 testspot[1] += branchDistance;
01219
01220 VectorCopy(testspot, starttrace);
01221
01222 starttrace[2] -= 4096;
01223
01224 trap_Trace(&tr, testspot, NULL, NULL, starttrace, ENTITYNUM_NONE, MASK_SOLID);
01225
01226 testspot[2] = tr.endpos[2]+baseheight;
01227
01228 if (!NodeHere(testspot) && !tr.startsolid && !tr.allsolid && CanGetToVector(nodetable[i].origin, testspot, mins, maxs))
01229 {
01230 VectorCopy(testspot, nodetable[nodenum].origin);
01231 nodetable[nodenum].inuse = 1;
01232
01233 nodetable[nodenum].weight = nodetable[i].weight+1;
01234 nodetable[nodenum].neighbornum = i;
01235 if ((nodetable[i].origin[2] - nodetable[nodenum].origin[2]) > 50)
01236 {
01237 nodetable[nodenum].flags = WPFLAG_ONEWAY_FWD;
01238 }
01239 nodenum++;
01240 cancontinue = 1;
01241 }
01242
01243 if (nodenum >= MAX_NODETABLE_SIZE)
01244 {
01245 break;
01246 }
01247
01248 VectorCopy(nodetable[i].origin, testspot);
01249 testspot[1] -= branchDistance;
01250
01251 VectorCopy(testspot, starttrace);
01252
01253 starttrace[2] -= 4096;
01254
01255 trap_Trace(&tr, testspot, NULL, NULL, starttrace, ENTITYNUM_NONE, MASK_SOLID);
01256
01257 testspot[2] = tr.endpos[2]+baseheight;
01258
01259 if (!NodeHere(testspot) && !tr.startsolid && !tr.allsolid && CanGetToVector(nodetable[i].origin, testspot, mins, maxs))
01260 {
01261 VectorCopy(testspot, nodetable[nodenum].origin);
01262 nodetable[nodenum].inuse = 1;
01263
01264 nodetable[nodenum].weight = nodetable[i].weight+1;
01265 nodetable[nodenum].neighbornum = i;
01266 if ((nodetable[i].origin[2] - nodetable[nodenum].origin[2]) > 50)
01267 {
01268 nodetable[nodenum].flags = WPFLAG_ONEWAY_FWD;
01269 }
01270 nodenum++;
01271 cancontinue = 1;
01272 }
01273
01274 if (nodenum >= MAX_NODETABLE_SIZE)
01275 {
01276 break;
01277 }
01278
01279 extendednodes[i] = 1;
01280 }
01281
01282 i++;
01283 }
01284 }
01285
01286 if (!foundit)
01287 {
01288 #ifndef _DEBUG //if debug just always print this.
01289 if (!behindTheScenes)
01290 #endif
01291 {
01292 G_Printf(S_COLOR_RED "Could not link %i to %i, unreachable by node branching.\n", startindex, endindex);
01293 }
01294 gWPArray[startindex]->flags |= WPFLAG_ONEWAY_FWD;
01295 gWPArray[endindex]->flags |= WPFLAG_ONEWAY_BACK;
01296 if (!behindTheScenes)
01297 {
01298 G_Printf(S_COLOR_YELLOW "Since points cannot be connected, point %i has been flagged as only-forward and point %i has been flagged as only-backward.\n", startindex, endindex);
01299 }
01300
01301
01302
01303
01304
01305
01306
01307
01308
01309
01310
01311
01312 if (!behindTheScenes)
01313 {
01314 return 0;
01315 }
01316 else
01317 {
01318 vec3_t endDist;
01319 int nCount = 0;
01320 int idealNode = -1;
01321 float bestDist = 0;
01322 float testDist;
01323
01324 if (nodenum <= 10)
01325 {
01326 return 0;
01327 }
01328
01329
01330 while (nCount < nodenum)
01331 {
01332 VectorSubtract(nodetable[nCount].origin, gWPArray[endindex]->origin, endDist);
01333 testDist = VectorLength(endDist);
01334 if (idealNode == -1)
01335 {
01336 idealNode = nCount;
01337 bestDist = testDist;
01338 nCount++;
01339 continue;
01340 }
01341
01342 if (testDist < bestDist)
01343 {
01344 idealNode = nCount;
01345 bestDist = testDist;
01346 }
01347
01348 nCount++;
01349 }
01350
01351 if (idealNode == -1)
01352 {
01353 return 0;
01354 }
01355
01356 successnodeindex = idealNode;
01357 }
01358 }
01359
01360 i = successnodeindex;
01361 insertindex = startindex;
01362 failsafe = 0;
01363 VectorCopy(gWPArray[startindex]->origin, validspotpos);
01364
01365 while (failsafe < MAX_NODETABLE_SIZE && i < MAX_NODETABLE_SIZE && i >= 0)
01366 {
01367 VectorSubtract(validspotpos, nodetable[i].origin, a);
01368 if (!nodetable[nodetable[i].neighbornum].inuse || !CanGetToVectorTravel(validspotpos, nodetable[i].origin, mins, maxs) || VectorLength(a) > maxDistFactor || (!CanGetToVectorTravel(validspotpos, gWPArray[endindex]->origin, mins, maxs) && CanGetToVectorTravel(nodetable[i].origin, gWPArray[endindex]->origin, mins, maxs)) )
01369 {
01370 nodetable[i].flags |= WPFLAG_CALCULATED;
01371 if (!CreateNewWP_InTrail(nodetable[i].origin, nodetable[i].flags, insertindex))
01372 {
01373 if (!behindTheScenes)
01374 {
01375 G_Printf(S_COLOR_RED "Could not link %i to %i, waypoint limit hit.\n", startindex, endindex);
01376 }
01377 return 0;
01378 }
01379
01380 VectorCopy(nodetable[i].origin, validspotpos);
01381 }
01382
01383 if (i == 0)
01384 {
01385 break;
01386 }
01387
01388 i = nodetable[i].neighbornum;
01389
01390 failsafe++;
01391 }
01392
01393 if (!behindTheScenes)
01394 {
01395 G_Printf(S_COLOR_YELLOW "Finished connecting %i to %i.\n", startindex, endindex);
01396 }
01397
01398 return 1;
01399 }
01400 #endif
01401
01402 int OpposingEnds(int start, int end)
01403 {
01404 if (!gWPArray[start] || !gWPArray[start]->inuse || !gWPArray[end] || !gWPArray[end]->inuse)
01405 {
01406 return 0;
01407 }
01408
01409 if ((gWPArray[start]->flags & WPFLAG_ONEWAY_FWD) &&
01410 (gWPArray[end]->flags & WPFLAG_ONEWAY_BACK))
01411 {
01412 return 1;
01413 }
01414
01415 return 0;
01416 }
01417
01418 int DoorBlockingSection(int start, int end)
01419 {
01420 trace_t tr;
01421 gentity_t *testdoor;
01422 int start_trace_index;
01423
01424 if (!gWPArray[start] || !gWPArray[start]->inuse || !gWPArray[end] || !gWPArray[end]->inuse)
01425 {
01426 return 0;
01427 }
01428
01429 trap_Trace(&tr, gWPArray[start]->origin, NULL, NULL, gWPArray[end]->origin, ENTITYNUM_NONE, MASK_SOLID);
01430
01431 if (tr.fraction == 1)
01432 {
01433 return 0;
01434 }
01435
01436 testdoor = &g_entities[tr.entityNum];
01437
01438 if (!testdoor)
01439 {
01440 return 0;
01441 }
01442
01443 if (!strstr(testdoor->classname, "func_"))
01444 {
01445 return 0;
01446 }
01447
01448 start_trace_index = tr.entityNum;
01449
01450 trap_Trace(&tr, gWPArray[end]->origin, NULL, NULL, gWPArray[start]->origin, ENTITYNUM_NONE, MASK_SOLID);
01451
01452 if (tr.fraction == 1)
01453 {
01454 return 0;
01455 }
01456
01457 if (start_trace_index == tr.entityNum)
01458 {
01459 return 1;
01460 }
01461
01462 return 0;
01463 }
01464
01465 #ifndef _XBOX
01466 int RepairPaths(qboolean behindTheScenes)
01467 {
01468 int i;
01469 int preAmount = 0;
01470 int ctRet;
01471 vec3_t a;
01472 float maxDistFactor = 400;
01473
01474 if (!gWPNum)
01475 {
01476 return 0;
01477 }
01478
01479 if (g_RMG.integer)
01480 {
01481 maxDistFactor = 800;
01482 }
01483
01484 i = 0;
01485
01486 preAmount = gWPNum;
01487
01488 trap_Cvar_Update(&bot_wp_distconnect);
01489 trap_Cvar_Update(&bot_wp_visconnect);
01490
01491 while (i < gWPNum)
01492 {
01493 if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i+1] && gWPArray[i+1]->inuse)
01494 {
01495 VectorSubtract(gWPArray[i]->origin, gWPArray[i+1]->origin, a);
01496
01497 if (!(gWPArray[i+1]->flags & WPFLAG_NOVIS) &&
01498 !(gWPArray[i+1]->flags & WPFLAG_JUMP) &&
01499 !(gWPArray[i]->flags & WPFLAG_CALCULATED) &&
01500 !OpposingEnds(i, i+1) &&
01501 ((bot_wp_distconnect.value && VectorLength(a) > maxDistFactor) || (!OrgVisible(gWPArray[i]->origin, gWPArray[i+1]->origin, ENTITYNUM_NONE) && bot_wp_visconnect.value) ) &&
01502 !DoorBlockingSection(i, i+1))
01503 {
01504 ctRet = ConnectTrail(i, i+1, behindTheScenes);
01505
01506 if (gWPNum >= MAX_WPARRAY_SIZE)
01507 {
01508 gWPNum = MAX_WPARRAY_SIZE;
01509 break;
01510 }
01511
01512
01513
01514
01515
01516 }
01517 }
01518
01519 i++;
01520 }
01521
01522 return 1;
01523 }
01524 #endif
01525
01526 int OrgVisibleCurve(vec3_t org1, vec3_t mins, vec3_t maxs, vec3_t org2, int ignore)
01527 {
01528 trace_t tr;
01529 vec3_t evenorg1;
01530
01531 VectorCopy(org1, evenorg1);
01532 evenorg1[2] = org2[2];
01533
01534 trap_Trace(&tr, evenorg1, mins, maxs, org2, ignore, MASK_SOLID);
01535
01536 if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid)
01537 {
01538 trap_Trace(&tr, evenorg1, mins, maxs, org1, ignore, MASK_SOLID);
01539
01540 if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid)
01541 {
01542 return 1;
01543 }
01544 }
01545
01546 return 0;
01547 }
01548
01549 int CanForceJumpTo(int baseindex, int testingindex, float distance)
01550 {
01551 float heightdif;
01552 vec3_t xy_base, xy_test, v, mins, maxs;
01553 wpobject_t *wpBase = gWPArray[baseindex];
01554 wpobject_t *wpTest = gWPArray[testingindex];
01555
01556 mins[0] = -15;
01557 mins[1] = -15;
01558 mins[2] = -15;
01559 maxs[0] = 15;
01560 maxs[1] = 15;
01561 maxs[2] = 15;
01562
01563 if (!wpBase || !wpBase->inuse || !wpTest || !wpTest->inuse)
01564 {
01565 return 0;
01566 }
01567
01568 if (distance > 400)
01569 {
01570 return 0;
01571 }
01572
01573 VectorCopy(wpBase->origin, xy_base);
01574 VectorCopy(wpTest->origin, xy_test);
01575
01576 xy_base[2] = xy_test[2];
01577
01578 VectorSubtract(xy_base, xy_test, v);
01579
01580 if (VectorLength(v) > MAX_NEIGHBOR_LINK_DISTANCE)
01581 {
01582 return 0;
01583 }
01584
01585 if ((int)wpBase->origin[2] < (int)wpTest->origin[2])
01586 {
01587 heightdif = wpTest->origin[2] - wpBase->origin[2];
01588 }
01589 else
01590 {
01591 return 0;
01592 }
01593
01594 if (heightdif < 128)
01595 {
01596 return 0;
01597 }
01598
01599 if (heightdif > 512)
01600 {
01601 return 0;
01602 }
01603
01604 if (!OrgVisibleCurve(wpBase->origin, mins, maxs, wpTest->origin, ENTITYNUM_NONE))
01605 {
01606 return 0;
01607 }
01608
01609 if (heightdif > 400)
01610 {
01611 return 3;
01612 }
01613 else if (heightdif > 256)
01614 {
01615 return 2;
01616 }
01617 else
01618 {
01619 return 1;
01620 }
01621 }
01622
01623 void CalculatePaths(void)
01624 {
01625 int i;
01626 int c;
01627 int forceJumpable;
01628 int maxNeighborDist = MAX_NEIGHBOR_LINK_DISTANCE;
01629 float nLDist;
01630 vec3_t a;
01631 vec3_t mins, maxs;
01632
01633 if (!gWPNum)
01634 {
01635 return;
01636 }
01637
01638 if (g_RMG.integer)
01639 {
01640 maxNeighborDist = DEFAULT_GRID_SPACING + (DEFAULT_GRID_SPACING*0.5);
01641 }
01642
01643 mins[0] = -15;
01644 mins[1] = -15;
01645 mins[2] = -15;
01646 maxs[0] = 15;
01647 maxs[1] = 15;
01648 maxs[2] = 15;
01649
01650
01651 i = 0;
01652
01653 while (i < gWPNum)
01654 {
01655 if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->neighbornum)
01656 {
01657 while (gWPArray[i]->neighbornum >= 0)
01658 {
01659 gWPArray[i]->neighbors[gWPArray[i]->neighbornum].num = 0;
01660 gWPArray[i]->neighbors[gWPArray[i]->neighbornum].forceJumpTo = 0;
01661 gWPArray[i]->neighbornum--;
01662 }
01663 gWPArray[i]->neighbornum = 0;
01664 }
01665
01666 i++;
01667 }
01668
01669 i = 0;
01670
01671 while (i < gWPNum)
01672 {
01673 if (gWPArray[i] && gWPArray[i]->inuse)
01674 {
01675 c = 0;
01676
01677 while (c < gWPNum)
01678 {
01679 if (gWPArray[c] && gWPArray[c]->inuse && i != c &&
01680 NotWithinRange(i, c))
01681 {
01682 VectorSubtract(gWPArray[i]->origin, gWPArray[c]->origin, a);
01683
01684 nLDist = VectorLength(a);
01685 forceJumpable = CanForceJumpTo(i, c, nLDist);
01686
01687 if ((nLDist < maxNeighborDist || forceJumpable) &&
01688 ((int)gWPArray[i]->origin[2] == (int)gWPArray[c]->origin[2] || forceJumpable) &&
01689 (OrgVisibleBox(gWPArray[i]->origin, mins, maxs, gWPArray[c]->origin, ENTITYNUM_NONE) || forceJumpable))
01690 {
01691 gWPArray[i]->neighbors[gWPArray[i]->neighbornum].num = c;
01692 if (forceJumpable && ((int)gWPArray[i]->origin[2] != (int)gWPArray[c]->origin[2] || nLDist < maxNeighborDist))
01693 {
01694 gWPArray[i]->neighbors[gWPArray[i]->neighbornum].forceJumpTo = 999;
01695 }
01696 else
01697 {
01698 gWPArray[i]->neighbors[gWPArray[i]->neighbornum].forceJumpTo = 0;
01699 }
01700 gWPArray[i]->neighbornum++;
01701 }
01702
01703 if (gWPArray[i]->neighbornum >= MAX_NEIGHBOR_SIZE)
01704 {
01705 break;
01706 }
01707 }
01708 c++;
01709 }
01710 }
01711 i++;
01712 }
01713 }
01714
01715 gentity_t *GetObjectThatTargets(gentity_t *ent)
01716 {
01717 gentity_t *next = NULL;
01718
01719 if (!ent->targetname)
01720 {
01721 return NULL;
01722 }
01723
01724 next = G_Find( next, FOFS(target), ent->targetname );
01725
01726 if (next)
01727 {
01728 return next;
01729 }
01730
01731 return NULL;
01732 }
01733
01734 void CalculateSiegeGoals(void)
01735 {
01736 int i = 0;
01737 int looptracker = 0;
01738 int wpindex = 0;
01739 vec3_t dif;
01740 gentity_t *ent;
01741 gentity_t *tent = NULL, *t2ent = NULL;
01742
01743 while (i < level.num_entities)
01744 {
01745 ent = &g_entities[i];
01746
01747 tent = NULL;
01748
01749 if (ent && ent->classname && strcmp(ent->classname, "info_siege_objective") == 0)
01750 {
01751 tent = ent;
01752 t2ent = GetObjectThatTargets(tent);
01753 looptracker = 0;
01754
01755 while (t2ent && looptracker < 2048)
01756 {
01757 tent = t2ent;
01758 t2ent = GetObjectThatTargets(tent);
01759 looptracker++;
01760 }
01761
01762 if (looptracker >= 2048)
01763 {
01764 tent = NULL;
01765 break;
01766 }
01767 }
01768
01769 if (tent && ent && tent != ent)
01770 {
01771 dif[0] = (tent->r.absmax[0]+tent->r.absmin[0])/2;
01772 dif[1] = (tent->r.absmax[1]+tent->r.absmin[1])/2;
01773 dif[2] = (tent->r.absmax[2]+tent->r.absmin[2])/2;
01774
01775 wpindex = GetNearestVisibleWP(dif, tent->s.number);
01776
01777 if (wpindex != -1 && gWPArray[wpindex] && gWPArray[wpindex]->inuse)
01778 {
01779 if (ent->side == SIEGETEAM_TEAM1)
01780 {
01781 gWPArray[wpindex]->flags |= WPFLAG_SIEGE_IMPERIALOBJ;
01782 }
01783 else
01784 {
01785 gWPArray[wpindex]->flags |= WPFLAG_SIEGE_REBELOBJ;
01786 }
01787
01788 gWPArray[wpindex]->associated_entity = tent->s.number;
01789 }
01790 }
01791
01792 i++;
01793 }
01794 }
01795
01796 float botGlobalNavWeaponWeights[WP_NUM_WEAPONS] =
01797 {
01798 0,
01799
01800 0,
01801 0,
01802 0,
01803 0,
01804 3,
01805 5,
01806 4,
01807 6,
01808 7,
01809 8,
01810 9,
01811 3,
01812 3,
01813 3,
01814 0
01815 };
01816
01817 int GetNearestVisibleWPToItem(vec3_t org, int ignore)
01818 {
01819 int i;
01820 float bestdist;
01821 float flLen;
01822 int bestindex;
01823 vec3_t a, mins, maxs;
01824
01825 i = 0;
01826 bestdist = 64;
01827 bestindex = -1;
01828
01829 mins[0] = -15;
01830 mins[1] = -15;
01831 mins[2] = 0;
01832 maxs[0] = 15;
01833 maxs[1] = 15;
01834 maxs[2] = 0;
01835
01836 while (i < gWPNum)
01837 {
01838 if (gWPArray[i] && gWPArray[i]->inuse &&
01839 gWPArray[i]->origin[2]-15 < org[2] &&
01840 gWPArray[i]->origin[2]+15 > org[2])
01841 {
01842 VectorSubtract(org, gWPArray[i]->origin, a);
01843 flLen = VectorLength(a);
01844
01845 if (flLen < bestdist && trap_InPVS(org, gWPArray[i]->origin) && OrgVisibleBox(org, mins, maxs, gWPArray[i]->origin, ignore))
01846 {
01847 bestdist = flLen;
01848 bestindex = i;
01849 }
01850 }
01851
01852 i++;
01853 }
01854
01855 return bestindex;
01856 }
01857
01858 void CalculateWeightGoals(void)
01859 {
01860 int i = 0;
01861 int wpindex = 0;
01862 gentity_t *ent;
01863 float weight;
01864
01865 trap_Cvar_Update(&bot_wp_clearweight);
01866
01867 if (bot_wp_clearweight.integer)
01868 {
01869 while (i < gWPNum)
01870 {
01871 if (gWPArray[i] && gWPArray[i]->inuse)
01872 {
01873 gWPArray[i]->weight = 0;
01874
01875 if (gWPArray[i]->flags & WPFLAG_GOALPOINT)
01876 {
01877 gWPArray[i]->flags -= WPFLAG_GOALPOINT;
01878 }
01879 }
01880
01881 i++;
01882 }
01883 }
01884
01885 i = 0;
01886
01887 while (i < level.num_entities)
01888 {
01889 ent = &g_entities[i];
01890
01891 weight = 0;
01892
01893 if (ent && ent->classname)
01894 {
01895 if (strcmp(ent->classname, "item_seeker") == 0)
01896 {
01897 weight = 2;
01898 }
01899 else if (strcmp(ent->classname, "item_shield") == 0)
01900 {
01901 weight = 2;
01902 }
01903 else if (strcmp(ent->classname, "item_medpac") == 0)
01904 {
01905 weight = 2;
01906 }
01907 else if (strcmp(ent->classname, "item_sentry_gun") == 0)
01908 {
01909 weight = 2;
01910 }
01911 else if (strcmp(ent->classname, "item_force_enlighten_dark") == 0)
01912 {
01913 weight = 5;
01914 }
01915 else if (strcmp(ent->classname, "item_force_enlighten_light") == 0)
01916 {
01917 weight = 5;
01918 }
01919 else if (strcmp(ent->classname, "item_force_boon") == 0)
01920 {
01921 weight = 5;
01922 }
01923 else if (strcmp(ent->classname, "item_ysalimari") == 0)
01924 {
01925 weight = 2;
01926 }
01927 else if (strstr(ent->classname, "weapon_") && ent->item)
01928 {
01929 weight = botGlobalNavWeaponWeights[ent->item->giTag];
01930 }
01931 else if (ent->item && ent->item->giType == IT_AMMO)
01932 {
01933 weight = 3;
01934 }
01935 }
01936
01937 if (ent && weight)
01938 {
01939 wpindex = GetNearestVisibleWPToItem(ent->s.pos.trBase, ent->s.number);
01940
01941 if (wpindex != -1 && gWPArray[wpindex] && gWPArray[wpindex]->inuse)
01942 {
01943 gWPArray[wpindex]->weight = weight;
01944 gWPArray[wpindex]->flags |= WPFLAG_GOALPOINT;
01945 gWPArray[wpindex]->associated_entity = ent->s.number;
01946 }
01947 }
01948
01949 i++;
01950 }
01951 }
01952
01953 void CalculateJumpRoutes(void)
01954 {
01955 int i = 0;
01956 float nheightdif = 0;
01957 float pheightdif = 0;
01958
01959 while (i < gWPNum)
01960 {
01961 if (gWPArray[i] && gWPArray[i]->inuse)
01962 {
01963 if (gWPArray[i]->flags & WPFLAG_JUMP)
01964 {
01965 nheightdif = 0;
01966 pheightdif = 0;
01967
01968 gWPArray[i]->forceJumpTo = 0;
01969
01970 if (gWPArray[i-1] && gWPArray[i-1]->inuse && (gWPArray[i-1]->origin[2]+16) < gWPArray[i]->origin[2])
01971 {
01972 nheightdif = (gWPArray[i]->origin[2] - gWPArray[i-1]->origin[2]);
01973 }
01974
01975 if (gWPArray[i+1] && gWPArray[i+1]->inuse && (gWPArray[i+1]->origin[2]+16) < gWPArray[i]->origin[2])
01976 {
01977 pheightdif = (gWPArray[i]->origin[2] - gWPArray[i+1]->origin[2]);
01978 }
01979
01980 if (nheightdif > pheightdif)
01981 {
01982 pheightdif = nheightdif;
01983 }
01984
01985 if (pheightdif)
01986 {
01987 if (pheightdif > 500)
01988 {
01989 gWPArray[i]->forceJumpTo = 999;
01990 }
01991 else if (pheightdif > 256)
01992 {
01993 gWPArray[i]->forceJumpTo = 999;
01994 }
01995 else if (pheightdif > 128)
01996 {
01997 gWPArray[i]->forceJumpTo = 999;
01998 }
01999 }
02000 }
02001 }
02002
02003 i++;
02004 }
02005 }
02006
02007 int LoadPathData(const char *filename)
02008 {
02009 fileHandle_t f;
02010 char *fileString;
02011 char *currentVar;
02012 char *routePath;
02013 wpobject_t thiswp;
02014 int len;
02015 int i, i_cv;
02016 int nei_num;
02017
02018 i = 0;
02019 i_cv = 0;
02020
02021 routePath = (char *)B_TempAlloc(1024);
02022
02023 Com_sprintf(routePath, 1024, "botroutes/%s.wnt\0", filename);
02024
02025 len = trap_FS_FOpenFile(routePath, &f, FS_READ);
02026
02027 B_TempFree(1024);
02028
02029 if (!f)
02030 {
02031 G_Printf(S_COLOR_YELLOW "Bot route data not found for %s\n", filename);
02032 return 2;
02033 }
02034
02035 if (len >= 524288)
02036 {
02037 G_Printf(S_COLOR_RED "Route file exceeds maximum length\n");
02038 return 0;
02039 }
02040
02041 fileString = (char *)B_TempAlloc(524288);
02042 currentVar = (char *)B_TempAlloc(2048);
02043
02044 trap_FS_Read(fileString, len, f);
02045
02046 if (fileString[i] == 'l')
02047 {
02048 char readLFlags[64];
02049 i_cv = 0;
02050
02051 while (fileString[i] != ' ')
02052 {
02053 i++;
02054 }
02055 i++;
02056 while (fileString[i] != '\n')
02057 {
02058 readLFlags[i_cv] = fileString[i];
02059 i_cv++;
02060 i++;
02061 }
02062 readLFlags[i_cv] = 0;
02063 i++;
02064
02065 gLevelFlags = atoi(readLFlags);
02066 }
02067 else
02068 {
02069 gLevelFlags = 0;
02070 }
02071
02072 while (i < len)
02073 {
02074 i_cv = 0;
02075
02076 thiswp.index = 0;
02077 thiswp.flags = 0;
02078 thiswp.inuse = 0;
02079 thiswp.neighbornum = 0;
02080 thiswp.origin[0] = 0;
02081 thiswp.origin[1] = 0;
02082 thiswp.origin[2] = 0;
02083 thiswp.weight = 0;
02084 thiswp.associated_entity = ENTITYNUM_NONE;
02085 thiswp.forceJumpTo = 0;
02086 thiswp.disttonext = 0;
02087 nei_num = 0;
02088
02089 while (nei_num < MAX_NEIGHBOR_SIZE)
02090 {
02091 thiswp.neighbors[nei_num].num = 0;
02092 thiswp.neighbors[nei_num].forceJumpTo = 0;
02093
02094 nei_num++;
02095 }
02096
02097 while (fileString[i] != ' ')
02098 {
02099 currentVar[i_cv] = fileString[i];
02100 i_cv++;
02101 i++;
02102 }
02103 currentVar[i_cv] = '\0';
02104
02105 thiswp.index = atoi(currentVar);
02106
02107 i_cv = 0;
02108 i++;
02109
02110 while (fileString[i] != ' ')
02111 {
02112 currentVar[i_cv] = fileString[i];
02113 i_cv++;
02114 i++;
02115 }
02116 currentVar[i_cv] = '\0';
02117
02118 thiswp.flags = atoi(currentVar);
02119
02120 i_cv = 0;
02121 i++;
02122
02123 while (fileString[i] != ' ')
02124 {
02125 currentVar[i_cv] = fileString[i];
02126 i_cv++;
02127 i++;
02128 }
02129 currentVar[i_cv] = '\0';
02130
02131 thiswp.weight = atof(currentVar);
02132
02133 i_cv = 0;
02134 i++;
02135 i++;
02136
02137 while (fileString[i] != ' ')
02138 {
02139 currentVar[i_cv] = fileString[i];
02140 i_cv++;
02141 i++;
02142 }
02143 currentVar[i_cv] = '\0';
02144
02145 thiswp.origin[0] = atof(currentVar);
02146
02147 i_cv = 0;
02148 i++;
02149
02150 while (fileString[i] != ' ')
02151 {
02152 currentVar[i_cv] = fileString[i];
02153 i_cv++;
02154 i++;
02155 }
02156 currentVar[i_cv] = '\0';
02157
02158 thiswp.origin[1] = atof(currentVar);
02159
02160 i_cv = 0;
02161 i++;
02162
02163 while (fileString[i] != ')')
02164 {
02165 currentVar[i_cv] = fileString[i];
02166 i_cv++;
02167 i++;
02168 }
02169 currentVar[i_cv] = '\0';
02170
02171 thiswp.origin[2] = atof(currentVar);
02172
02173 i += 4;
02174
02175 while (fileString[i] != '}')
02176 {
02177 i_cv = 0;
02178 while (fileString[i] != ' ' && fileString[i] != '-')
02179 {
02180 currentVar[i_cv] = fileString[i];
02181 i_cv++;
02182 i++;
02183 }
02184 currentVar[i_cv] = '\0';
02185
02186 thiswp.neighbors[thiswp.neighbornum].num = atoi(currentVar);
02187
02188 if (fileString[i] == '-')
02189 {
02190 i_cv = 0;
02191 i++;
02192
02193 while (fileString[i] != ' ')
02194 {
02195 currentVar[i_cv] = fileString[i];
02196 i_cv++;
02197 i++;
02198 }
02199 currentVar[i_cv] = '\0';
02200
02201 thiswp.neighbors[thiswp.neighbornum].forceJumpTo = 999;
02202 }
02203 else
02204 {
02205 thiswp.neighbors[thiswp.neighbornum].forceJumpTo = 0;
02206 }
02207
02208 thiswp.neighbornum++;
02209
02210 i++;
02211 }
02212
02213 i_cv = 0;
02214 i++;
02215 i++;
02216
02217 while (fileString[i] != '\n')
02218 {
02219 currentVar[i_cv] = fileString[i];
02220 i_cv++;
02221 i++;
02222 }
02223 currentVar[i_cv] = '\0';
02224
02225 thiswp.disttonext = atof(currentVar);
02226
02227 CreateNewWP_FromObject(&thiswp);
02228 i++;
02229 }
02230
02231 B_TempFree(524288);
02232 B_TempFree(2048);
02233
02234 trap_FS_FCloseFile(f);
02235
02236 if (g_gametype.integer == GT_SIEGE)
02237 {
02238 CalculateSiegeGoals();
02239 }
02240
02241 CalculateWeightGoals();
02242
02243
02244
02245 CalculateJumpRoutes();
02246
02247
02248
02249 return 1;
02250 }
02251
02252 void FlagObjects(void)
02253 {
02254 int i = 0, bestindex = 0, found = 0;
02255 float bestdist = 999999, tlen = 0;
02256 gentity_t *flag_red, *flag_blue, *ent;
02257 vec3_t a, mins, maxs;
02258 trace_t tr;
02259
02260 flag_red = NULL;
02261 flag_blue = NULL;
02262
02263 mins[0] = -15;
02264 mins[1] = -15;
02265 mins[2] = -5;
02266 maxs[0] = 15;
02267 maxs[1] = 15;
02268 maxs[2] = 5;
02269
02270 while (i < level.num_entities)
02271 {
02272 ent = &g_entities[i];
02273
02274 if (ent && ent->inuse && ent->classname)
02275 {
02276 if (!flag_red && strcmp(ent->classname, "team_CTF_redflag") == 0)
02277 {
02278 flag_red = ent;
02279 }
02280 else if (!flag_blue && strcmp(ent->classname, "team_CTF_blueflag") == 0)
02281 {
02282 flag_blue = ent;
02283 }
02284
02285 if (flag_red && flag_blue)
02286 {
02287 break;
02288 }
02289 }
02290
02291 i++;
02292 }
02293
02294 i = 0;
02295
02296 if (!flag_red || !flag_blue)
02297 {
02298 return;
02299 }
02300
02301 while (i < gWPNum)
02302 {
02303 if (gWPArray[i] && gWPArray[i]->inuse)
02304 {
02305 VectorSubtract(flag_red->s.pos.trBase, gWPArray[i]->origin, a);
02306 tlen = VectorLength(a);
02307
02308 if (tlen < bestdist)
02309 {
02310 trap_Trace(&tr, flag_red->s.pos.trBase, mins, maxs, gWPArray[i]->origin, flag_red->s.number, MASK_SOLID);
02311
02312 if (tr.fraction == 1 || tr.entityNum == flag_red->s.number)
02313 {
02314 bestdist = tlen;
02315 bestindex = i;
02316 found = 1;
02317 }
02318 }
02319
02320 }
02321
02322 i++;
02323 }
02324
02325 if (found)
02326 {
02327 gWPArray[bestindex]->flags |= WPFLAG_RED_FLAG;
02328 flagRed = gWPArray[bestindex];
02329 oFlagRed = flagRed;
02330 eFlagRed = flag_red;
02331 }
02332
02333 bestdist = 999999;
02334 bestindex = 0;
02335 found = 0;
02336 i = 0;
02337
02338 while (i < gWPNum)
02339 {
02340 if (gWPArray[i] && gWPArray[i]->inuse)
02341 {
02342 VectorSubtract(flag_blue->s.pos.trBase, gWPArray[i]->origin, a);
02343 tlen = VectorLength(a);
02344
02345 if (tlen < bestdist)
02346 {
02347 trap_Trace(&tr, flag_blue->s.pos.trBase, mins, maxs, gWPArray[i]->origin, flag_blue->s.number, MASK_SOLID);
02348
02349 if (tr.fraction == 1 || tr.entityNum == flag_blue->s.number)
02350 {
02351 bestdist = tlen;
02352 bestindex = i;
02353 found = 1;
02354 }
02355 }
02356
02357 }
02358
02359 i++;
02360 }
02361
02362 if (found)
02363 {
02364 gWPArray[bestindex]->flags |= WPFLAG_BLUE_FLAG;
02365 flagBlue = gWPArray[bestindex];
02366 oFlagBlue = flagBlue;
02367 eFlagBlue = flag_blue;
02368 }
02369 }
02370
02371 #ifndef _XBOX
02372 int SavePathData(const char *filename)
02373 {
02374 fileHandle_t f;
02375 char *fileString;
02376 char *storeString;
02377 char *routePath;
02378 vec3_t a;
02379 float flLen;
02380 int i, s, n;
02381
02382 fileString = NULL;
02383 i = 0;
02384 s = 0;
02385
02386 if (!gWPNum)
02387 {
02388 return 0;
02389 }
02390
02391 routePath = (char *)B_TempAlloc(1024);
02392
02393 Com_sprintf(routePath, 1024, "botroutes/%s.wnt\0", filename);
02394
02395 trap_FS_FOpenFile(routePath, &f, FS_WRITE);
02396
02397 B_TempFree(1024);
02398
02399 if (!f)
02400 {
02401 G_Printf(S_COLOR_RED "ERROR: Could not open file to write path data\n");
02402 return 0;
02403 }
02404
02405 if (!RepairPaths(qfalse))
02406 {
02407 trap_FS_FCloseFile(f);
02408 return 0;
02409 }
02410
02411 CalculatePaths();
02412
02413 FlagObjects();
02414
02415 fileString = (char *)B_TempAlloc(524288);
02416 storeString = (char *)B_TempAlloc(4096);
02417
02418 Com_sprintf(fileString, 524288, "%i %i %f (%f %f %f) { ", gWPArray[i]->index, gWPArray[i]->flags, gWPArray[i]->weight, gWPArray[i]->origin[0], gWPArray[i]->origin[1], gWPArray[i]->origin[2]);
02419
02420 n = 0;
02421
02422 while (n < gWPArray[i]->neighbornum)
02423 {
02424 if (gWPArray[i]->neighbors[n].forceJumpTo)
02425 {
02426 Com_sprintf(storeString, 4096, "%s%i-%i ", storeString, gWPArray[i]->neighbors[n].num, gWPArray[i]->neighbors[n].forceJumpTo);
02427 }
02428 else
02429 {
02430 Com_sprintf(storeString, 4096, "%s%i ", storeString, gWPArray[i]->neighbors[n].num);
02431 }
02432 n++;
02433 }
02434
02435 if (gWPArray[i+1] && gWPArray[i+1]->inuse && gWPArray[i+1]->index)
02436 {
02437 VectorSubtract(gWPArray[i]->origin, gWPArray[i+1]->origin, a);
02438 flLen = VectorLength(a);
02439 }
02440 else
02441 {
02442 flLen = 0;
02443 }
02444
02445 gWPArray[i]->disttonext = flLen;
02446
02447 Com_sprintf(fileString, 524288, "%s} %f\n", fileString, flLen);
02448
02449 i++;
02450
02451 while (i < gWPNum)
02452 {
02453
02454 Com_sprintf(storeString, 4096, "%i %i %f (%f %f %f) { ", gWPArray[i]->index, gWPArray[i]->flags, gWPArray[i]->weight, gWPArray[i]->origin[0], gWPArray[i]->origin[1], gWPArray[i]->origin[2]);
02455
02456 n = 0;
02457
02458 while (n < gWPArray[i]->neighbornum)
02459 {
02460 if (gWPArray[i]->neighbors[n].forceJumpTo)
02461 {
02462 Com_sprintf(storeString, 4096, "%s%i-%i ", storeString, gWPArray[i]->neighbors[n].num, gWPArray[i]->neighbors[n].forceJumpTo);
02463 }
02464 else
02465 {
02466 Com_sprintf(storeString, 4096, "%s%i ", storeString, gWPArray[i]->neighbors[n].num);
02467 }
02468 n++;
02469 }
02470
02471 if (gWPArray[i+1] && gWPArray[i+1]->inuse && gWPArray[i+1]->index)
02472 {
02473 VectorSubtract(gWPArray[i]->origin, gWPArray[i+1]->origin, a);
02474 flLen = VectorLength(a);
02475 }
02476 else
02477 {
02478 flLen = 0;
02479 }
02480
02481 gWPArray[i]->disttonext = flLen;
02482
02483 Com_sprintf(storeString, 4096, "%s} %f\n", storeString, flLen);
02484
02485 strcat(fileString, storeString);
02486
02487 i++;
02488 }
02489
02490 trap_FS_Write(fileString, strlen(fileString), f);
02491
02492 B_TempFree(524288);
02493 B_TempFree(4096);
02494
02495 trap_FS_FCloseFile(f);
02496
02497 G_Printf("Path data has been saved and updated. You may need to restart the level for some things to be properly calculated.\n");
02498
02499 return 1;
02500 }
02501 #endif
02502
02503
02504
02505 #define MAX_SPAWNPOINT_ARRAY 64
02506 int gSpawnPointNum = 0;
02507 gentity_t *gSpawnPoints[MAX_SPAWNPOINT_ARRAY];
02508
02509 #ifndef _XBOX
02510 int G_NearestNodeToPoint(vec3_t point)
02511 {
02512 vec3_t vSub;
02513 int bestIndex = -1;
02514 int i = 0;
02515 float bestDist = 0;
02516 float testDist = 0;
02517
02518 while (i < nodenum)
02519 {
02520 VectorSubtract(nodetable[i].origin, point, vSub);
02521 testDist = VectorLength(vSub);
02522
02523 if (bestIndex == -1)
02524 {
02525 bestIndex = i;
02526 bestDist = testDist;
02527
02528 i++;
02529 continue;
02530 }
02531
02532 if (testDist < bestDist)
02533 {
02534 bestIndex = i;
02535 bestDist = testDist;
02536 }
02537 i++;
02538 }
02539
02540 return bestIndex;
02541 }
02542 #endif
02543
02544 #ifndef _XBOX
02545 void G_NodeClearForNext(void)
02546 {
02547 int i = 0;
02548
02549 while (i < nodenum)
02550 {
02551 nodetable[i].flags = 0;
02552 nodetable[i].weight = 99999;
02553
02554 i++;
02555 }
02556 }
02557
02558 void G_NodeClearFlags(void)
02559 {
02560 int i = 0;
02561
02562 while (i < nodenum)
02563 {
02564 nodetable[i].flags = 0;
02565
02566 i++;
02567 }
02568 }
02569
02570 int G_NodeMatchingXY(float x, float y)
02571 {
02572 int i = 0;
02573
02574 while (i < nodenum)
02575 {
02576 if (nodetable[i].origin[0] == x &&
02577 nodetable[i].origin[1] == y &&
02578 !nodetable[i].flags)
02579 {
02580 return i;
02581 }
02582
02583 i++;
02584 }
02585
02586 return -1;
02587 }
02588
02589 int G_NodeMatchingXY_BA(int x, int y, int final)
02590 {
02591 int i = 0;
02592 int bestindex = -1;
02593 float bestWeight = 9999;
02594
02595 while (i < nodenum)
02596 {
02597 if ((int)nodetable[i].origin[0] == x &&
02598 (int)nodetable[i].origin[1] == y &&
02599 !nodetable[i].flags &&
02600 ((nodetable[i].weight < bestWeight) || (i == final)))
02601 {
02602 if (i == final)
02603 {
02604 return i;
02605 }
02606 bestindex = i;
02607 bestWeight = nodetable[i].weight;
02608 }
02609
02610 i++;
02611 }
02612
02613 return bestindex;
02614 }
02615
02616 int G_RecursiveConnection(int start, int end, int weight, qboolean traceCheck, float baseHeight)
02617 {
02618 int indexDirections[4];
02619 int recursiveIndex = -1;
02620 int i = 0;
02621 int passWeight = weight;
02622 vec2_t givenXY;
02623 trace_t tr;
02624
02625 passWeight++;
02626 nodetable[start].weight = passWeight;
02627
02628 givenXY[0] = nodetable[start].origin[0];
02629 givenXY[1] = nodetable[start].origin[1];
02630 givenXY[0] -= DEFAULT_GRID_SPACING;
02631 indexDirections[0] = G_NodeMatchingXY(givenXY[0], givenXY[1]);
02632
02633 givenXY[0] = nodetable[start].origin[0];
02634 givenXY[1] = nodetable[start].origin[1];
02635 givenXY[0] += DEFAULT_GRID_SPACING;
02636 indexDirections[1] = G_NodeMatchingXY(givenXY[0], givenXY[1]);
02637
02638 givenXY[0] = nodetable[start].origin[0];
02639 givenXY[1] = nodetable[start].origin[1];
02640 givenXY[1] -= DEFAULT_GRID_SPACING;
02641 indexDirections[2] = G_NodeMatchingXY(givenXY[0], givenXY[1]);
02642
02643 givenXY[0] = nodetable[start].origin[0];
02644 givenXY[1] = nodetable[start].origin[1];
02645 givenXY[1] += DEFAULT_GRID_SPACING;
02646 indexDirections[3] = G_NodeMatchingXY(givenXY[0], givenXY[1]);
02647
02648 i = 0;
02649 while (i < 4)
02650 {
02651 if (indexDirections[i] == end)
02652 {
02653 return indexDirections[i];
02654 }
02655
02656 if (indexDirections[i] != -1 && nodetable[indexDirections[i]].flags)
02657 {
02658 indexDirections[i] = -1;
02659 }
02660 else if (indexDirections[i] != -1)
02661 {
02662 nodetable[indexDirections[i]].flags = 1;
02663 }
02664
02665 if (indexDirections[i] != -1 && traceCheck)
02666 {
02667 trap_Trace(&tr, nodetable[start].origin, NULL, NULL, nodetable[indexDirections[i]].origin, ENTITYNUM_NONE, CONTENTS_SOLID);
02668
02669 if (tr.fraction != 1)
02670 {
02671 indexDirections[i] = -1;
02672 }
02673 }
02674
02675 if (indexDirections[i] != -1)
02676 {
02677 recursiveIndex = G_RecursiveConnection(indexDirections[i], end, passWeight, traceCheck, baseHeight);
02678 }
02679
02680 if (recursiveIndex != -1)
02681 {
02682 return recursiveIndex;
02683 }
02684
02685 i++;
02686 }
02687
02688 return recursiveIndex;
02689 }
02690
02691 #ifdef DEBUG_NODE_FILE
02692 void G_DebugNodeFile()
02693 {
02694 fileHandle_t f;
02695 int i = 0;
02696 float placeX;
02697 char fileString[131072];
02698 gentity_t *terrain = G_Find( NULL, FOFS(classname), "terrain" );
02699
02700 fileString[0] = 0;
02701
02702 placeX = terrain->r.absmin[0];
02703
02704 while (i < nodenum)
02705 {
02706 strcat(fileString, va("%i-%f ", i, nodetable[i].weight));
02707 placeX += DEFAULT_GRID_SPACING;
02708
02709 if (placeX >= terrain->r.absmax[0])
02710 {
02711 strcat(fileString, "\n");
02712 placeX = terrain->r.absmin[0];
02713 }
02714 i++;
02715 }
02716
02717 trap_FS_FOpenFile("ROUTEDEBUG.txt", &f, FS_WRITE);
02718 trap_FS_Write(fileString, strlen(fileString), f);
02719 trap_FS_FCloseFile(f);
02720 }
02721 #endif
02722
02723 #endif
02724
02725
02726
02727 #ifdef ASCII_ART_DEBUG
02728
02729 #define ALLOWABLE_DEBUG_FILE_SIZE 1048576
02730
02731 void CreateAsciiTableRepresentation()
02732 {
02733 fileHandle_t f;
02734 int i = 0;
02735 int sP = 0;
02736 int placeX;
02737 int placeY;
02738 int oldX;
02739 int oldY;
02740 char fileString[ALLOWABLE_DEBUG_FILE_SIZE];
02741 char bChr = '+';
02742 gentity_t *terrain = G_Find( NULL, FOFS(classname), "terrain" );
02743
02744 placeX = terrain->r.absmin[0];
02745 placeY = terrain->r.absmin[1];
02746
02747 oldX = placeX-1;
02748 oldY = placeY-1;
02749
02750 while (placeY < terrain->r.absmax[1])
02751 {
02752 while (placeX < terrain->r.absmax[0])
02753 {
02754 qboolean gotit = qfalse;
02755
02756 i = 0;
02757 while (i < gWPNum)
02758 {
02759 if (((int)gWPArray[i]->origin[0] <= placeX && (int)gWPArray[i]->origin[0] > oldX) &&
02760 ((int)gWPArray[i]->origin[1] <= placeY && (int)gWPArray[i]->origin[1] > oldY))
02761 {
02762 gotit = qtrue;
02763 break;
02764 }
02765 i++;
02766 }
02767
02768 if (gotit)
02769 {
02770 if (gWPArray[i]->flags & WPFLAG_ONEWAY_FWD)
02771 {
02772 bChr = 'F';
02773 }
02774 else if (gWPArray[i]->flags & WPFLAG_ONEWAY_BACK)
02775 {
02776 bChr = 'B';
02777 }
02778 else
02779 {
02780 bChr = '+';
02781 }
02782
02783 if (gWPArray[i]->index < 10)
02784 {
02785 fileString[sP] = bChr;
02786 fileString[sP+1] = '0';
02787 fileString[sP+2] = '0';
02788 fileString[sP+3] = va("%i", gWPArray[i]->index)[0];
02789 }
02790 else if (gWPArray[i]->index < 100)
02791 {
02792 char *vastore = va("%i", gWPArray[i]->index);
02793
02794 fileString[sP] = bChr;
02795 fileString[sP+1] = '0';
02796 fileString[sP+2] = vastore[0];
02797 fileString[sP+3] = vastore[1];
02798 }
02799 else if (gWPArray[i]->index < 1000)
02800 {
02801 char *vastore = va("%i", gWPArray[i]->index);
02802
02803 fileString[sP] = bChr;
02804 fileString[sP+1] = vastore[0];
02805 fileString[sP+2] = vastore[1];
02806 fileString[sP+3] = vastore[2];
02807 }
02808 else
02809 {
02810 fileString[sP] = 'X';
02811 fileString[sP+1] = 'X';
02812 fileString[sP+2] = 'X';
02813 fileString[sP+3] = 'X';
02814 }
02815 }
02816 else
02817 {
02818 fileString[sP] = '-';
02819 fileString[sP+1] = '-';
02820 fileString[sP+2] = '-';
02821 fileString[sP+3] = '-';
02822 }
02823
02824 sP += 4;
02825
02826 if (sP >= ALLOWABLE_DEBUG_FILE_SIZE-16)
02827 {
02828 break;
02829 }
02830 oldX = placeX;
02831 placeX += DEFAULT_GRID_SPACING;
02832 }
02833
02834 placeX = terrain->r.absmin[0];
02835 oldX = placeX-1;
02836 fileString[sP] = '\n';
02837 sP++;
02838
02839 if (sP >= ALLOWABLE_DEBUG_FILE_SIZE-16)
02840 {
02841 break;
02842 }
02843
02844 oldY = placeY;
02845 placeY += DEFAULT_GRID_SPACING;
02846 }
02847
02848 fileString[sP] = 0;
02849
02850 trap_FS_FOpenFile("ROUTEDRAWN.txt", &f, FS_WRITE);
02851 trap_FS_Write(fileString, strlen(fileString), f);
02852 trap_FS_FCloseFile(f);
02853 }
02854
02855 void CreateAsciiNodeTableRepresentation(int start, int end)
02856 {
02857 fileHandle_t f;
02858 int i = 0;
02859 int sP = 0;
02860 int placeX;
02861 int placeY;
02862 int oldX;
02863 int oldY;
02864 char fileString[ALLOWABLE_DEBUG_FILE_SIZE];
02865 gentity_t *terrain = G_Find( NULL, FOFS(classname), "terrain" );
02866
02867 placeX = terrain->r.absmin[0];
02868 placeY = terrain->r.absmin[1];
02869
02870 oldX = placeX-1;
02871 oldY = placeY-1;
02872
02873 while (placeY < terrain->r.absmax[1])
02874 {
02875 while (placeX < terrain->r.absmax[0])
02876 {
02877 qboolean gotit = qfalse;
02878
02879 i = 0;
02880 while (i < nodenum)
02881 {
02882 if (((int)nodetable[i].origin[0] <= placeX && (int)nodetable[i].origin[0] > oldX) &&
02883 ((int)nodetable[i].origin[1] <= placeY && (int)nodetable[i].origin[1] > oldY))
02884 {
02885 gotit = qtrue;
02886 break;
02887 }
02888 i++;
02889 }
02890
02891 if (gotit)
02892 {
02893 if (i == start)
02894 {
02895 fileString[sP] = 'A';
02896 fileString[sP+1] = 'A';
02897 fileString[sP+2] = 'A';
02898 fileString[sP+3] = 'A';
02899 }
02900 else if (i == end)
02901 {
02902 fileString[sP] = 'Z';
02903 fileString[sP+1] = 'Z';
02904 fileString[sP+2] = 'Z';
02905 fileString[sP+3] = 'Z';
02906 }
02907 else if (nodetable[i].weight < 10)
02908 {
02909 fileString[sP] = '+';
02910 fileString[sP+1] = '0';
02911 fileString[sP+2] = '0';
02912 fileString[sP+3] = va("%f", nodetable[i].weight)[0];
02913 }
02914 else if (nodetable[i].weight < 100)
02915 {
02916 char *vastore = va("%f", nodetable[i].weight);
02917
02918 fileString[sP] = '+';
02919 fileString[sP+1] = '0';
02920 fileString[sP+2] = vastore[0];
02921 fileString[sP+3] = vastore[1];
02922 }
02923 else if (nodetable[i].weight < 1000)
02924 {
02925 char *vastore = va("%f", nodetable[i].weight);
02926
02927 fileString[sP] = '+';
02928 fileString[sP+1] = vastore[0];
02929 fileString[sP+2] = vastore[1];
02930 fileString[sP+3] = vastore[2];
02931 }
02932 else
02933 {
02934 fileString[sP] = 'X';
02935 fileString[sP+1] = 'X';
02936 fileString[sP+2] = 'X';
02937 fileString[sP+3] = 'X';
02938 }
02939 }
02940 else
02941 {
02942 fileString[sP] = '-';
02943 fileString[sP+1] = '-';
02944 fileString[sP+2] = '-';
02945 fileString[sP+3] = '-';
02946 }
02947
02948 sP += 4;
02949
02950 if (sP >= ALLOWABLE_DEBUG_FILE_SIZE-16)
02951 {
02952 break;
02953 }
02954 oldX = placeX;
02955 placeX += DEFAULT_GRID_SPACING;
02956 }
02957
02958 placeX = terrain->r.absmin[0];
02959 oldX = placeX-1;
02960 fileString[sP] = '\n';
02961 sP++;
02962
02963 if (sP >= ALLOWABLE_DEBUG_FILE_SIZE-16)
02964 {
02965 break;
02966 }
02967
02968 oldY = placeY;
02969 placeY += DEFAULT_GRID_SPACING;
02970 }
02971
02972 fileString[sP] = 0;
02973
02974 trap_FS_FOpenFile("ROUTEDRAWN.txt", &f, FS_WRITE);
02975 trap_FS_Write(fileString, strlen(fileString), f);
02976 trap_FS_FCloseFile(f);
02977 }
02978 #endif
02979
02980 #ifndef _XBOX
02981 qboolean G_BackwardAttachment(int start, int finalDestination, int insertAfter)
02982 {
02983 int indexDirections[4];
02984 int i = 0;
02985 int lowestWeight = 9999;
02986 int desiredIndex = -1;
02987 vec2_t givenXY;
02988
02989 givenXY[0] = nodetable[start].origin[0];
02990 givenXY[1] = nodetable[start].origin[1];
02991 givenXY[0] -= DEFAULT_GRID_SPACING;
02992 indexDirections[0] = G_NodeMatchingXY_BA(givenXY[0], givenXY[1], finalDestination);
02993
02994 givenXY[0] = nodetable[start].origin[0];
02995 givenXY[1] = nodetable[start].origin[1];
02996 givenXY[0] += DEFAULT_GRID_SPACING;
02997 indexDirections[1] = G_NodeMatchingXY_BA(givenXY[0], givenXY[1], finalDestination);
02998
02999 givenXY[0] = nodetable[start].origin[0];
03000 givenXY[1] = nodetable[start].origin[1];
03001 givenXY[1] -= DEFAULT_GRID_SPACING;
03002 indexDirections[2] = G_NodeMatchingXY_BA(givenXY[0], givenXY[1], finalDestination);
03003
03004 givenXY[0] = nodetable[start].origin[0];
03005 givenXY[1] = nodetable[start].origin[1];
03006 givenXY[1] += DEFAULT_GRID_SPACING;
03007 indexDirections[3] = G_NodeMatchingXY_BA(givenXY[0], givenXY[1], finalDestination);
03008
03009 while (i < 4)
03010 {
03011 if (indexDirections[i] != -1)
03012 {
03013 if (indexDirections[i] == finalDestination)
03014 {
03015 CreateNewWP_InsertUnder(nodetable[start].origin, 0, insertAfter);
03016 CreateNewWP_InsertUnder(nodetable[indexDirections[i]].origin, 0, insertAfter);
03017 return qtrue;
03018 }
03019
03020 if (nodetable[indexDirections[i]].weight < lowestWeight && nodetable[indexDirections[i]].weight && !nodetable[indexDirections[i]].flags )
03021 {
03022 desiredIndex = indexDirections[i];
03023 lowestWeight = nodetable[indexDirections[i]].weight;
03024 }
03025 }
03026 i++;
03027 }
03028
03029 if (desiredIndex != -1)
03030 {
03031 if (gWPNum < 3900)
03032 {
03033 CreateNewWP_InsertUnder(nodetable[start].origin, 0, insertAfter);
03034 }
03035 else
03036 {
03037 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03038 Com_Printf("WAYPOINTS FULL\n");
03039 #endif
03040 return qfalse;
03041 }
03042
03043 nodetable[start].flags = 1;
03044 return G_BackwardAttachment(desiredIndex, finalDestination, insertAfter);
03045 }
03046
03047 return qfalse;
03048 }
03049
03050
03051 #ifdef _DEBUG
03052 #define PATH_TIME_DEBUG
03053 #endif
03054
03055 void G_RMGPathing(void)
03056 {
03057 float placeX, placeY, placeZ;
03058 int i = 0;
03059 int gridSpacing = DEFAULT_GRID_SPACING;
03060 int nearestIndex = 0;
03061 int nearestIndexForNext = 0;
03062 #ifdef PATH_TIME_DEBUG
03063 int startTime = 0;
03064 int endTime = 0;
03065 #endif
03066 vec3_t downVec, trMins, trMaxs;
03067 trace_t tr;
03068 gentity_t *terrain = G_Find( NULL, FOFS(classname), "terrain" );
03069
03070 if (!terrain || !terrain->inuse || terrain->s.eType != ET_TERRAIN)
03071 {
03072 G_Printf("Error: RMG with no terrain!\n");
03073 return;
03074 }
03075
03076 #ifdef PATH_TIME_DEBUG
03077 startTime = trap_Milliseconds();
03078 #endif
03079
03080 nodenum = 0;
03081 memset(&nodetable, 0, sizeof(nodetable));
03082
03083 VectorSet(trMins, -15, -15, DEFAULT_MINS_2);
03084 VectorSet(trMaxs, 15, 15, DEFAULT_MAXS_2);
03085
03086 placeX = terrain->r.absmin[0];
03087 placeY = terrain->r.absmin[1];
03088 placeZ = terrain->r.absmax[2]-400;
03089
03090
03091
03092 while (placeY < terrain->r.absmax[1])
03093 {
03094 if (nodenum >= MAX_NODETABLE_SIZE)
03095 {
03096 break;
03097 }
03098
03099 while (placeX < terrain->r.absmax[0])
03100 {
03101 if (nodenum >= MAX_NODETABLE_SIZE)
03102 {
03103 break;
03104 }
03105
03106 nodetable[nodenum].origin[0] = placeX;
03107 nodetable[nodenum].origin[1] = placeY;
03108 nodetable[nodenum].origin[2] = placeZ;
03109
03110 VectorCopy(nodetable[nodenum].origin, downVec);
03111 downVec[2] -= 3000;
03112 trap_Trace(&tr, nodetable[nodenum].origin, trMins, trMaxs, downVec, ENTITYNUM_NONE, MASK_SOLID);
03113
03114 if ((tr.entityNum >= ENTITYNUM_WORLD || g_entities[tr.entityNum].s.eType == ET_TERRAIN) && tr.endpos[2] < terrain->r.absmin[2]+750)
03115 {
03116 VectorCopy(tr.endpos, nodetable[nodenum].origin);
03117 nodenum++;
03118 }
03119 else
03120 {
03121 VectorClear(nodetable[nodenum].origin);
03122 }
03123
03124 placeX += gridSpacing;
03125 }
03126
03127 placeX = terrain->r.absmin[0];
03128 placeY += gridSpacing;
03129 }
03130
03131 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03132 Com_Printf("NODE GRID PLACED ON TERRAIN\n");
03133 #endif
03134
03135 G_NodeClearForNext();
03136
03137
03138 while (i < gSpawnPointNum-1)
03139 {
03140 if (!gSpawnPoints[i] || !gSpawnPoints[i]->inuse || !gSpawnPoints[i+1] || !gSpawnPoints[i+1]->inuse)
03141 {
03142 i++;
03143 continue;
03144 }
03145
03146 nearestIndex = G_NearestNodeToPoint(gSpawnPoints[i]->s.origin);
03147 nearestIndexForNext = G_NearestNodeToPoint(gSpawnPoints[i+1]->s.origin);
03148
03149 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03150 Com_Printf("%i GOT %i INDEX WITH %i INDEX FOR NEXT\n", nearestIndex, nearestIndexForNext);
03151 #endif
03152
03153 if (nearestIndex == -1 || nearestIndexForNext == -1)
03154 {
03155 i++;
03156 continue;
03157 }
03158
03159 if (nearestIndex == nearestIndexForNext)
03160 {
03161 i++;
03162 continue;
03163 }
03164
03165
03166
03167
03168
03169
03170 if (G_RecursiveConnection(nearestIndex, nearestIndexForNext, 0, qtrue, terrain->r.absmin[2]) != nearestIndexForNext)
03171 {
03172 G_NodeClearForNext();
03173
03174 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03175 Com_Printf("FAILED RECURSIVE WITH TRACES\n");
03176 #endif
03177
03178 if (G_RecursiveConnection(nearestIndex, nearestIndexForNext, 0, qfalse, terrain->r.absmin[2]) != nearestIndexForNext)
03179 {
03180 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03181 Com_Printf("FAILED RECURSIVE -WITHOUT- TRACES (?!?!)\n");
03182 #endif
03183 G_NodeClearForNext();
03184 i++;
03185 continue;
03186 }
03187 }
03188
03189
03190
03191 G_NodeClearFlags();
03192
03193 #ifdef ASCII_ART_DEBUG
03194 #ifdef ASCII_ART_NODE_DEBUG
03195 CreateAsciiNodeTableRepresentation(nearestIndex, nearestIndexForNext);
03196 #endif
03197 #endif
03198 if (G_BackwardAttachment(nearestIndexForNext, nearestIndex, gWPNum-1))
03199 {
03200 if (gSpawnPoints[i+1]->inuse && gSpawnPoints[i+1]->item &&
03201 gSpawnPoints[i+1]->item->giType == IT_TEAM)
03202 {
03203 if (gSpawnPoints[i+1]->item->giTag == PW_REDFLAG || gSpawnPoints[i+1]->item->giTag == PW_BLUEFLAG)
03204 {
03205 CreateNewWP_InsertUnder(gSpawnPoints[i+1]->s.origin, WPFLAG_NEVERONEWAY, gWPNum-1);
03206 }
03207 }
03208
03209 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03210 Com_Printf("BACKWARD ATTACHMENT %i SUCCESS\n", i);
03211 #endif
03212 }
03213 else
03214 {
03215 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03216 Com_Printf("BACKWARD ATTACHMENT FAILED\n");
03217 #endif
03218 break;
03219 }
03220
03221 #ifdef DEBUG_NODE_FILE
03222 G_DebugNodeFile();
03223 #endif
03224
03225 G_NodeClearForNext();
03226 i++;
03227 }
03228
03229 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03230 Com_Printf("FINISHED RMG AUTOPATH\n");
03231 #endif
03232
03233 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03234 Com_Printf("BEGINNING PATH REPAIR...\n");
03235 #endif
03236 RepairPaths(qtrue);
03237 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03238 Com_Printf("FINISHED PATH REPAIR.\n");
03239 #endif
03240
03241 #ifdef PATH_TIME_DEBUG
03242 endTime = trap_Milliseconds();
03243
03244 G_Printf("Total routing time taken: %ims\n", (endTime - startTime));
03245 #endif
03246
03247 #ifdef ASCII_ART_DEBUG
03248 CreateAsciiTableRepresentation();
03249 #endif
03250 }
03251 #endif
03252
03253 #ifndef _XBOX
03254 void BeginAutoPathRoutine(void)
03255 {
03256 int i = 0;
03257 gentity_t *ent = NULL;
03258 vec3_t v;
03259
03260 gSpawnPointNum = 0;
03261
03262 CreateNewWP(vec3_origin, 0);
03263
03264 while (i < level.num_entities)
03265 {
03266 ent = &g_entities[i];
03267
03268 if (ent && ent->inuse && ent->classname && ent->classname[0] && !Q_stricmp(ent->classname, "info_player_deathmatch"))
03269 {
03270 if (ent->s.origin[2] < 1280)
03271 {
03272 gSpawnPoints[gSpawnPointNum] = ent;
03273 gSpawnPointNum++;
03274 }
03275 }
03276 else if (ent && ent->inuse && ent->item && ent->item->giType == IT_TEAM &&
03277 (ent->item->giTag == PW_REDFLAG || ent->item->giTag == PW_BLUEFLAG))
03278 {
03279 gSpawnPoints[gSpawnPointNum] = ent;
03280 gSpawnPointNum++;
03281 }
03282
03283 i++;
03284 }
03285
03286 if (gSpawnPointNum < 1)
03287 {
03288 return;
03289 }
03290
03291 G_RMGPathing();
03292 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03293 Com_Printf("LINKING PATHS...\n");
03294 #endif
03295
03296
03297 trap_Bot_UpdateWaypoints(gWPNum, gWPArray);
03298 trap_Bot_CalculatePaths(g_RMG.integer);
03299
03300
03301 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03302 Com_Printf("FINISHED LINKING PATHS.\n");
03303 #endif
03304
03305 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03306 Com_Printf("FLAGGING OBJECTS...\n");
03307 #endif
03308 FlagObjects();
03309 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03310 Com_Printf("FINISHED FLAGGING OBJECTS.\n");
03311 #endif
03312
03313 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03314 Com_Printf("CALCULATING WAYPOINT DISTANCES...\n");
03315 #endif
03316 i = 0;
03317
03318 while (i < gWPNum-1)
03319 {
03320 VectorSubtract(gWPArray[i]->origin, gWPArray[i+1]->origin, v);
03321 gWPArray[i]->disttonext = VectorLength(v);
03322 i++;
03323 }
03324 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03325 Com_Printf("FINISHED CALCULATING.\n");
03326 #endif
03327
03328 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03329 Com_Printf("FINAL STEP...\n");
03330 #endif
03331 RemoveWP();
03332 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03333 Com_Printf("COMPLETE.\n");
03334 #endif
03335
03336 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03337 if (gWPNum >= 4096-1)
03338 {
03339 Com_Printf("%i waypoints say that YOU ARE A TERRIBLE MAN.\n", gWPNum);
03340 }
03341 #endif
03342 }
03343
03344 #endif
03345 extern vmCvar_t bot_normgpath;
03346
03347 void LoadPath_ThisLevel(void)
03348 {
03349 vmCvar_t mapname;
03350 int i = 0;
03351 gentity_t *ent = NULL;
03352
03353 trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
03354
03355 if (g_RMG.integer)
03356 {
03357 #ifdef _XBOX
03358 assert(0);
03359 #else
03360 trap_Cvar_Register(&bot_normgpath, "bot_normgpath", "1", CVAR_CHEAT);
03361
03362
03363
03364 if (!bot_normgpath.integer)
03365 {
03366 BeginAutoPathRoutine();
03367 }
03368 else
03369 {
03370 LoadPathData(mapname.string);
03371 }
03372
03373 gLevelFlags |= LEVELFLAG_NOPOINTPREDICTION;
03374 #endif
03375 }
03376 else
03377 {
03378 if (LoadPathData(mapname.string) == 2)
03379 {
03380
03381 }
03382 }
03383
03384 trap_Cvar_Update(&bot_wp_edit);
03385
03386 if (bot_wp_edit.value)
03387 {
03388 gBotEdit = 1;
03389 }
03390 else
03391 {
03392 gBotEdit = 0;
03393 }
03394
03395
03396 while (i < level.num_entities)
03397 {
03398 ent = &g_entities[i];
03399
03400 if (ent && ent->inuse && ent->classname)
03401 {
03402 if (!eFlagRed && strcmp(ent->classname, "team_CTF_redflag") == 0)
03403 {
03404 eFlagRed = ent;
03405 }
03406 else if (!eFlagBlue && strcmp(ent->classname, "team_CTF_blueflag") == 0)
03407 {
03408 eFlagBlue = ent;
03409 }
03410
03411 if (eFlagRed && eFlagBlue)
03412 {
03413 break;
03414 }
03415 }
03416
03417 i++;
03418 }
03419
03420 #ifdef PAINFULLY_DEBUGGING_THROUGH_VM
03421 Com_Printf("BOT PATHING IS COMPLETE.\n");
03422 #endif
03423 }
03424
03425 gentity_t *GetClosestSpawn(gentity_t *ent)
03426 {
03427 gentity_t *spawn;
03428 gentity_t *closestSpawn = NULL;
03429 float closestDist = -1;
03430 int i = MAX_CLIENTS;
03431
03432 spawn = NULL;
03433
03434 while (i < level.num_entities)
03435 {
03436 spawn = &g_entities[i];
03437
03438 if (spawn && spawn->inuse && (!Q_stricmp(spawn->classname, "info_player_start") || !Q_stricmp(spawn->classname, "info_player_deathmatch")) )
03439 {
03440 float checkDist;
03441 vec3_t vSub;
03442
03443 VectorSubtract(ent->client->ps.origin, spawn->r.currentOrigin, vSub);
03444 checkDist = VectorLength(vSub);
03445
03446 if (closestDist == -1 || checkDist < closestDist)
03447 {
03448 closestSpawn = spawn;
03449 closestDist = checkDist;
03450 }
03451 }
03452
03453 i++;
03454 }
03455
03456 return closestSpawn;
03457 }
03458
03459 gentity_t *GetNextSpawnInIndex(gentity_t *currentSpawn)
03460 {
03461 gentity_t *spawn;
03462 gentity_t *nextSpawn = NULL;
03463 int i = currentSpawn->s.number+1;
03464
03465 spawn = NULL;
03466
03467 while (i < level.num_entities)
03468 {
03469 spawn = &g_entities[i];
03470
03471 if (spawn && spawn->inuse && (!Q_stricmp(spawn->classname, "info_player_start") || !Q_stricmp(spawn->classname, "info_player_deathmatch")) )
03472 {
03473 nextSpawn = spawn;
03474 break;
03475 }
03476
03477 i++;
03478 }
03479
03480 if (!nextSpawn)
03481 {
03482 i = MAX_CLIENTS;
03483
03484 while (i < level.num_entities)
03485 {
03486 spawn = &g_entities[i];
03487
03488 if (spawn && spawn->inuse && (!Q_stricmp(spawn->classname, "info_player_start") || !Q_stricmp(spawn->classname, "info_player_deathmatch")) )
03489 {
03490 nextSpawn = spawn;
03491 break;
03492 }
03493
03494 i++;
03495 }
03496 }
03497
03498 return nextSpawn;
03499 }
03500
03501 int AcceptBotCommand(char *cmd, gentity_t *pl)
03502 {
03503 int OptionalArgument, i;
03504 int FlagsFromArgument;
03505 char *OptionalSArgument, *RequiredSArgument;
03506 #ifndef _XBOX
03507 vmCvar_t mapname;
03508 #endif
03509
03510 if (!gBotEdit)
03511 {
03512 return 0;
03513 }
03514
03515 OptionalArgument = 0;
03516 i = 0;
03517 FlagsFromArgument = 0;
03518 OptionalSArgument = NULL;
03519 RequiredSArgument = NULL;
03520
03521
03522
03523
03524 if (!pl || !pl->client)
03525 {
03526 return 0;
03527 }
03528
03529 if (Q_stricmp (cmd, "bot_wp_cmdlist") == 0)
03530 {
03531 G_Printf(S_COLOR_YELLOW "bot_wp_add" S_COLOR_WHITE " - Add a waypoint (optional int parameter will insert the point after the specified waypoint index in a trail)\n\n");
03532 G_Printf(S_COLOR_YELLOW "bot_wp_rem" S_COLOR_WHITE " - Remove a waypoint (removes last unless waypoint index is specified as a parameter)\n\n");
03533 G_Printf(S_COLOR_YELLOW "bot_wp_addflagged" S_COLOR_WHITE " - Same as wp_add, but adds a flagged point (type bot_wp_addflagged for help)\n\n");
03534 G_Printf(S_COLOR_YELLOW "bot_wp_switchflags" S_COLOR_WHITE " - Switches flags on an existing waypoint (type bot_wp_switchflags for help)\n\n");
03535 G_Printf(S_COLOR_YELLOW "bot_wp_tele" S_COLOR_WHITE " - Teleport yourself to the specified waypoint's location\n");
03536 G_Printf(S_COLOR_YELLOW "bot_wp_killoneways" S_COLOR_WHITE " - Removes oneway (backward and forward) flags on all waypoints in the level\n\n");
03537 G_Printf(S_COLOR_YELLOW "bot_wp_save" S_COLOR_WHITE " - Saves all waypoint data into a file for later use\n");
03538
03539 return 1;
03540 }
03541
03542 if (Q_stricmp (cmd, "bot_wp_add") == 0)
03543 {
03544 gDeactivated = 1;
03545 OptionalSArgument = ConcatArgs( 1 );
03546
03547 if (OptionalSArgument)
03548 {
03549 OptionalArgument = atoi(OptionalSArgument);
03550 }
03551
03552 if (OptionalSArgument && OptionalSArgument[0])
03553 {
03554 CreateNewWP_InTrail(pl->client->ps.origin, 0, OptionalArgument);
03555 }
03556 else
03557 {
03558 CreateNewWP(pl->client->ps.origin, 0);
03559 }
03560 return 1;
03561 }
03562
03563 if (Q_stricmp (cmd, "bot_wp_rem") == 0)
03564 {
03565 gDeactivated = 1;
03566
03567 OptionalSArgument = ConcatArgs( 1 );
03568
03569 if (OptionalSArgument)
03570 {
03571 OptionalArgument = atoi(OptionalSArgument);
03572 }
03573
03574 if (OptionalSArgument && OptionalSArgument[0])
03575 {
03576 RemoveWP_InTrail(OptionalArgument);
03577 }
03578 else
03579 {
03580 RemoveWP();
03581 }
03582
03583 return 1;
03584 }
03585
03586 if (Q_stricmp (cmd, "bot_wp_tele") == 0)
03587 {
03588 gDeactivated = 1;
03589 OptionalSArgument = ConcatArgs( 1 );
03590
03591 if (OptionalSArgument)
03592 {
03593 OptionalArgument = atoi(OptionalSArgument);
03594 }
03595
03596 if (OptionalSArgument && OptionalSArgument[0])
03597 {
03598 TeleportToWP(pl, OptionalArgument);
03599 }
03600 else
03601 {
03602 G_Printf(S_COLOR_YELLOW "You didn't specify an index. Assuming last.\n");
03603 TeleportToWP(pl, gWPNum-1);
03604 }
03605 return 1;
03606 }
03607
03608 if (Q_stricmp (cmd, "bot_wp_spawntele") == 0)
03609 {
03610 gentity_t *closestSpawn = GetClosestSpawn(pl);
03611
03612 if (!closestSpawn)
03613 {
03614 return 1;
03615 }
03616
03617 closestSpawn = GetNextSpawnInIndex(closestSpawn);
03618
03619 if (closestSpawn)
03620 {
03621 VectorCopy(closestSpawn->r.currentOrigin, pl->client->ps.origin);
03622 }
03623 return 1;
03624 }
03625
03626 if (Q_stricmp (cmd, "bot_wp_addflagged") == 0)
03627 {
03628 gDeactivated = 1;
03629
03630 RequiredSArgument = ConcatArgs( 1 );
03631
03632 if (!RequiredSArgument || !RequiredSArgument[0])
03633 {
03634 G_Printf(S_COLOR_YELLOW "Flag string needed for bot_wp_addflagged\nj - Jump point\nd - Duck point\nc - Snipe or camp standing\nf - Wait for func\nm - Do not move to when func is under\ns - Snipe or camp\nx - Oneway, forward\ny - Oneway, back\ng - Mission goal\nn - No visibility\nExample (for a point the bot would jump at, and reverse on when traveling a trail backwards):\nbot_wp_addflagged jx\n");
03635 return 1;
03636 }
03637
03638 while (RequiredSArgument[i])
03639 {
03640 if (RequiredSArgument[i] == 'j')
03641 {
03642 FlagsFromArgument |= WPFLAG_JUMP;
03643 }
03644 else if (RequiredSArgument[i] == 'd')
03645 {
03646 FlagsFromArgument |= WPFLAG_DUCK;
03647 }
03648 else if (RequiredSArgument[i] == 'c')
03649 {
03650 FlagsFromArgument |= WPFLAG_SNIPEORCAMPSTAND;
03651 }
03652 else if (RequiredSArgument[i] == 'f')
03653 {
03654 FlagsFromArgument |= WPFLAG_WAITFORFUNC;
03655 }
03656 else if (RequiredSArgument[i] == 's')
03657 {
03658 FlagsFromArgument |= WPFLAG_SNIPEORCAMP;
03659 }
03660 else if (RequiredSArgument[i] == 'x')
03661 {
03662 FlagsFromArgument |= WPFLAG_ONEWAY_FWD;
03663 }
03664 else if (RequiredSArgument[i] == 'y')
03665 {
03666 FlagsFromArgument |= WPFLAG_ONEWAY_BACK;
03667 }
03668 else if (RequiredSArgument[i] == 'g')
03669 {
03670 FlagsFromArgument |= WPFLAG_GOALPOINT;
03671 }
03672 else if (RequiredSArgument[i] == 'n')
03673 {
03674 FlagsFromArgument |= WPFLAG_NOVIS;
03675 }
03676 else if (RequiredSArgument[i] == 'm')
03677 {
03678 FlagsFromArgument |= WPFLAG_NOMOVEFUNC;
03679 }
03680
03681 i++;
03682 }
03683
03684 OptionalSArgument = ConcatArgs( 2 );
03685
03686 if (OptionalSArgument)
03687 {
03688 OptionalArgument = atoi(OptionalSArgument);
03689 }
03690
03691 if (OptionalSArgument && OptionalSArgument[0])
03692 {
03693 CreateNewWP_InTrail(pl->client->ps.origin, FlagsFromArgument, OptionalArgument);
03694 }
03695 else
03696 {
03697 CreateNewWP(pl->client->ps.origin, FlagsFromArgument);
03698 }
03699 return 1;
03700 }
03701
03702 if (Q_stricmp (cmd, "bot_wp_switchflags") == 0)
03703 {
03704 gDeactivated = 1;
03705
03706 RequiredSArgument = ConcatArgs( 1 );
03707
03708 if (!RequiredSArgument || !RequiredSArgument[0])
03709 {
03710 G_Printf(S_COLOR_YELLOW "Flag string needed for bot_wp_switchflags\nType bot_wp_addflagged for a list of flags and their corresponding characters, or use 0 for no flags.\nSyntax: bot_wp_switchflags <flags> <n>\n");
03711 return 1;
03712 }
03713
03714 while (RequiredSArgument[i])
03715 {
03716 if (RequiredSArgument[i] == 'j')
03717 {
03718 FlagsFromArgument |= WPFLAG_JUMP;
03719 }
03720 else if (RequiredSArgument[i] == 'd')
03721 {
03722 FlagsFromArgument |= WPFLAG_DUCK;
03723 }
03724 else if (RequiredSArgument[i] == 'c')
03725 {
03726 FlagsFromArgument |= WPFLAG_SNIPEORCAMPSTAND;
03727 }
03728 else if (RequiredSArgument[i] == 'f')
03729 {
03730 FlagsFromArgument |= WPFLAG_WAITFORFUNC;
03731 }
03732 else if (RequiredSArgument[i] == 's')
03733 {
03734 FlagsFromArgument |= WPFLAG_SNIPEORCAMP;
03735 }
03736 else if (RequiredSArgument[i] == 'x')
03737 {
03738 FlagsFromArgument |= WPFLAG_ONEWAY_FWD;
03739 }
03740 else if (RequiredSArgument[i] == 'y')
03741 {
03742 FlagsFromArgument |= WPFLAG_ONEWAY_BACK;
03743 }
03744 else if (RequiredSArgument[i] == 'g')
03745 {
03746 FlagsFromArgument |= WPFLAG_GOALPOINT;
03747 }
03748 else if (RequiredSArgument[i] == 'n')
03749 {
03750 FlagsFromArgument |= WPFLAG_NOVIS;
03751 }
03752 else if (RequiredSArgument[i] == 'm')
03753 {
03754 FlagsFromArgument |= WPFLAG_NOMOVEFUNC;
03755 }
03756
03757 i++;
03758 }
03759
03760 OptionalSArgument = ConcatArgs( 2 );
03761
03762 if (OptionalSArgument)
03763 {
03764 OptionalArgument = atoi(OptionalSArgument);
03765 }
03766
03767 if (OptionalSArgument && OptionalSArgument[0])
03768 {
03769 WPFlagsModify(OptionalArgument, FlagsFromArgument);
03770 }
03771 else
03772 {
03773 G_Printf(S_COLOR_YELLOW "Waypoint number (to modify) needed for bot_wp_switchflags\nSyntax: bot_wp_switchflags <flags> <n>\n");
03774 }
03775 return 1;
03776 }
03777
03778 if (Q_stricmp (cmd, "bot_wp_killoneways") == 0)
03779 {
03780 i = 0;
03781
03782 while (i < gWPNum)
03783 {
03784 if (gWPArray[i] && gWPArray[i]->inuse)
03785 {
03786 if (gWPArray[i]->flags & WPFLAG_ONEWAY_FWD)
03787 {
03788 gWPArray[i]->flags -= WPFLAG_ONEWAY_FWD;
03789 }
03790 if (gWPArray[i]->flags & WPFLAG_ONEWAY_BACK)
03791 {
03792 gWPArray[i]->flags -= WPFLAG_ONEWAY_BACK;
03793 }
03794 }
03795
03796 i++;
03797 }
03798
03799 return 1;
03800 }
03801
03802 #ifndef _XBOX
03803 if (Q_stricmp (cmd, "bot_wp_save") == 0)
03804 {
03805 gDeactivated = 0;
03806 trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
03807 SavePathData(mapname.string);
03808 return 1;
03809 }
03810 #endif
03811
03812 return 0;
03813 }