be_aas_reach.c (152877B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 23 /***************************************************************************** 24 * name: be_aas_reach.c 25 * 26 * desc: reachability calculations 27 * 28 * $Archive: /MissionPack/code/botlib/be_aas_reach.c $ 29 * 30 *****************************************************************************/ 31 32 #include "../game/q_shared.h" 33 #include "l_log.h" 34 #include "l_memory.h" 35 #include "l_script.h" 36 #include "l_libvar.h" 37 #include "l_precomp.h" 38 #include "l_struct.h" 39 #include "aasfile.h" 40 #include "../game/botlib.h" 41 #include "../game/be_aas.h" 42 #include "be_aas_funcs.h" 43 #include "be_aas_def.h" 44 45 extern int Sys_MilliSeconds(void); 46 47 48 extern botlib_import_t botimport; 49 50 //#define REACH_DEBUG 51 52 //NOTE: all travel times are in hundreth of a second 53 //maximum number of reachability links 54 #define AAS_MAX_REACHABILITYSIZE 65536 55 //number of areas reachability is calculated for each frame 56 #define REACHABILITYAREASPERCYCLE 15 57 //number of units reachability points are placed inside the areas 58 #define INSIDEUNITS 2 59 #define INSIDEUNITS_WALKEND 5 60 #define INSIDEUNITS_WALKSTART 0.1 61 #define INSIDEUNITS_WATERJUMP 15 62 //area flag used for weapon jumping 63 #define AREA_WEAPONJUMP 8192 //valid area to weapon jump to 64 //number of reachabilities of each type 65 int reach_swim; //swim 66 int reach_equalfloor; //walk on floors with equal height 67 int reach_step; //step up 68 int reach_walk; //walk of step 69 int reach_barrier; //jump up to a barrier 70 int reach_waterjump; //jump out of water 71 int reach_walkoffledge; //walk of a ledge 72 int reach_jump; //jump 73 int reach_ladder; //climb or descent a ladder 74 int reach_teleport; //teleport 75 int reach_elevator; //use an elevator 76 int reach_funcbob; //use a func bob 77 int reach_grapple; //grapple hook 78 int reach_doublejump; //double jump 79 int reach_rampjump; //ramp jump 80 int reach_strafejump; //strafe jump (just normal jump but further) 81 int reach_rocketjump; //rocket jump 82 int reach_bfgjump; //bfg jump 83 int reach_jumppad; //jump pads 84 //if true grapple reachabilities are skipped 85 int calcgrapplereach; 86 //linked reachability 87 typedef struct aas_lreachability_s 88 { 89 int areanum; //number of the reachable area 90 int facenum; //number of the face towards the other area 91 int edgenum; //number of the edge towards the other area 92 vec3_t start; //start point of inter area movement 93 vec3_t end; //end point of inter area movement 94 int traveltype; //type of travel required to get to the area 95 unsigned short int traveltime; //travel time of the inter area movement 96 // 97 struct aas_lreachability_s *next; 98 } aas_lreachability_t; 99 //temporary reachabilities 100 aas_lreachability_t *reachabilityheap; //heap with reachabilities 101 aas_lreachability_t *nextreachability; //next free reachability from the heap 102 aas_lreachability_t **areareachability; //reachability links for every area 103 int numlreachabilities; 104 105 //=========================================================================== 106 // returns the surface area of the given face 107 // 108 // Parameter: - 109 // Returns: - 110 // Changes Globals: - 111 //=========================================================================== 112 float AAS_FaceArea(aas_face_t *face) 113 { 114 int i, edgenum, side; 115 float total; 116 vec_t *v; 117 vec3_t d1, d2, cross; 118 aas_edge_t *edge; 119 120 edgenum = aasworld.edgeindex[face->firstedge]; 121 side = edgenum < 0; 122 edge = &aasworld.edges[abs(edgenum)]; 123 v = aasworld.vertexes[edge->v[side]]; 124 125 total = 0; 126 for (i = 1; i < face->numedges - 1; i++) 127 { 128 edgenum = aasworld.edgeindex[face->firstedge + i]; 129 side = edgenum < 0; 130 edge = &aasworld.edges[abs(edgenum)]; 131 VectorSubtract(aasworld.vertexes[edge->v[side]], v, d1); 132 VectorSubtract(aasworld.vertexes[edge->v[!side]], v, d2); 133 CrossProduct(d1, d2, cross); 134 total += 0.5 * VectorLength(cross); 135 } //end for 136 return total; 137 } //end of the function AAS_FaceArea 138 //=========================================================================== 139 // returns the volume of an area 140 // 141 // Parameter: - 142 // Returns: - 143 // Changes Globals: - 144 //=========================================================================== 145 float AAS_AreaVolume(int areanum) 146 { 147 int i, edgenum, facenum, side; 148 vec_t d, a, volume; 149 vec3_t corner; 150 aas_plane_t *plane; 151 aas_edge_t *edge; 152 aas_face_t *face; 153 aas_area_t *area; 154 155 area = &aasworld.areas[areanum]; 156 facenum = aasworld.faceindex[area->firstface]; 157 face = &aasworld.faces[abs(facenum)]; 158 edgenum = aasworld.edgeindex[face->firstedge]; 159 edge = &aasworld.edges[abs(edgenum)]; 160 // 161 VectorCopy(aasworld.vertexes[edge->v[0]], corner); 162 163 //make tetrahedrons to all other faces 164 volume = 0; 165 for (i = 0; i < area->numfaces; i++) 166 { 167 facenum = abs(aasworld.faceindex[area->firstface + i]); 168 face = &aasworld.faces[facenum]; 169 side = face->backarea != areanum; 170 plane = &aasworld.planes[face->planenum ^ side]; 171 d = -(DotProduct (corner, plane->normal) - plane->dist); 172 a = AAS_FaceArea(face); 173 volume += d * a; 174 } //end for 175 176 volume /= 3; 177 return volume; 178 } //end of the function AAS_AreaVolume 179 //=========================================================================== 180 // 181 // Parameter: - 182 // Returns: - 183 // Changes Globals: - 184 //=========================================================================== 185 int AAS_BestReachableLinkArea(aas_link_t *areas) 186 { 187 aas_link_t *link; 188 189 for (link = areas; link; link = link->next_area) 190 { 191 if (AAS_AreaGrounded(link->areanum) || AAS_AreaSwim(link->areanum)) 192 { 193 return link->areanum; 194 } //end if 195 } //end for 196 // 197 for (link = areas; link; link = link->next_area) 198 { 199 if (link->areanum) return link->areanum; 200 //FIXME: this is a bad idea when the reachability is not yet 201 // calculated when the level items are loaded 202 if (AAS_AreaReachability(link->areanum)) 203 return link->areanum; 204 } //end for 205 return 0; 206 } //end of the function AAS_BestReachableLinkArea 207 //=========================================================================== 208 // 209 // Parameter: - 210 // Returns: - 211 // Changes Globals: - 212 //=========================================================================== 213 int AAS_GetJumpPadInfo(int ent, vec3_t areastart, vec3_t absmins, vec3_t absmaxs, vec3_t velocity) 214 { 215 int modelnum, ent2; 216 float speed, height, gravity, time, dist, forward; 217 vec3_t origin, angles, teststart, ent2origin; 218 aas_trace_t trace; 219 char model[MAX_EPAIRKEY]; 220 char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; 221 222 // 223 AAS_FloatForBSPEpairKey(ent, "speed", &speed); 224 if (!speed) speed = 1000; 225 VectorClear(angles); 226 //get the mins, maxs and origin of the model 227 AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); 228 if (model[0]) modelnum = atoi(model+1); 229 else modelnum = 0; 230 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin); 231 VectorAdd(origin, absmins, absmins); 232 VectorAdd(origin, absmaxs, absmaxs); 233 VectorAdd(absmins, absmaxs, origin); 234 VectorScale (origin, 0.5, origin); 235 236 //get the start areas 237 VectorCopy(origin, teststart); 238 teststart[2] += 64; 239 trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1); 240 if (trace.startsolid) 241 { 242 botimport.Print(PRT_MESSAGE, "trigger_push start solid\n"); 243 VectorCopy(origin, areastart); 244 } //end if 245 else 246 { 247 VectorCopy(trace.endpos, areastart); 248 } //end else 249 areastart[2] += 0.125; 250 // 251 //AAS_DrawPermanentCross(origin, 4, 4); 252 //get the target entity 253 AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY); 254 for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2)) 255 { 256 if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue; 257 if (!strcmp(targetname, target)) break; 258 } //end for 259 if (!ent2) 260 { 261 botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target); 262 return qfalse; 263 } //end if 264 AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin); 265 // 266 height = ent2origin[2] - origin[2]; 267 gravity = aassettings.phys_gravity; 268 time = sqrt( height / ( 0.5 * gravity ) ); 269 if (!time) 270 { 271 botimport.Print(PRT_MESSAGE, "trigger_push without time\n"); 272 return qfalse; 273 } //end if 274 // set s.origin2 to the push velocity 275 VectorSubtract ( ent2origin, origin, velocity); 276 dist = VectorNormalize( velocity); 277 forward = dist / time; 278 //FIXME: why multiply by 1.1 279 forward *= 1.1f; 280 VectorScale(velocity, forward, velocity); 281 velocity[2] = time * gravity; 282 return qtrue; 283 } //end of the function AAS_GetJumpPadInfo 284 //=========================================================================== 285 // 286 // Parameter: - 287 // Returns: - 288 // Changes Globals: - 289 //=========================================================================== 290 int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs) 291 { 292 int area2num, ent, bot_visualizejumppads, bestareanum; 293 float volume, bestareavolume; 294 vec3_t areastart, cmdmove, bboxmins, bboxmaxs; 295 vec3_t absmins, absmaxs, velocity; 296 aas_clientmove_t move; 297 aas_link_t *areas, *link; 298 char classname[MAX_EPAIRKEY]; 299 300 #ifdef BSPC 301 bot_visualizejumppads = 0; 302 #else 303 bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0"); 304 #endif 305 VectorAdd(origin, mins, bboxmins); 306 VectorAdd(origin, maxs, bboxmaxs); 307 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) 308 { 309 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; 310 if (strcmp(classname, "trigger_push")) continue; 311 // 312 if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue; 313 //get the areas the jump pad brush is in 314 areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); 315 for (link = areas; link; link = link->next_area) 316 { 317 if (AAS_AreaJumpPad(link->areanum)) break; 318 } //end for 319 if (!link) 320 { 321 botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n"); 322 AAS_UnlinkFromAreas(areas); 323 continue; 324 } //end if 325 // 326 //botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]); 327 // 328 VectorSet(cmdmove, 0, 0, 0); 329 Com_Memset(&move, 0, sizeof(aas_clientmove_t)); 330 area2num = 0; 331 AAS_ClientMovementHitBBox(&move, -1, areastart, PRESENCE_NORMAL, qfalse, 332 velocity, cmdmove, 0, 30, 0.1f, bboxmins, bboxmaxs, bot_visualizejumppads); 333 if (move.frames < 30) 334 { 335 bestareanum = 0; 336 bestareavolume = 0; 337 for (link = areas; link; link = link->next_area) 338 { 339 if (!AAS_AreaJumpPad(link->areanum)) continue; 340 volume = AAS_AreaVolume(link->areanum); 341 if (volume >= bestareavolume) 342 { 343 bestareanum = link->areanum; 344 bestareavolume = volume; 345 } //end if 346 } //end if 347 AAS_UnlinkFromAreas(areas); 348 return bestareanum; 349 } //end if 350 AAS_UnlinkFromAreas(areas); 351 } //end for 352 return 0; 353 } //end of the function AAS_BestReachableFromJumpPadArea 354 //=========================================================================== 355 // 356 // Parameter: - 357 // Returns: - 358 // Changes Globals: - 359 //=========================================================================== 360 int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin) 361 { 362 int areanum, i, j, k, l; 363 aas_link_t *areas; 364 vec3_t absmins, absmaxs; 365 //vec3_t bbmins, bbmaxs; 366 vec3_t start, end; 367 aas_trace_t trace; 368 369 if (!aasworld.loaded) 370 { 371 botimport.Print(PRT_ERROR, "AAS_BestReachableArea: aas not loaded\n"); 372 return 0; 373 } //end if 374 //find a point in an area 375 VectorCopy(origin, start); 376 areanum = AAS_PointAreaNum(start); 377 //while no area found fudge around a little 378 for (i = 0; i < 5 && !areanum; i++) 379 { 380 for (j = 0; j < 5 && !areanum; j++) 381 { 382 for (k = -1; k <= 1 && !areanum; k++) 383 { 384 for (l = -1; l <= 1 && !areanum; l++) 385 { 386 VectorCopy(origin, start); 387 start[0] += (float) j * 4 * k; 388 start[1] += (float) j * 4 * l; 389 start[2] += (float) i * 4; 390 areanum = AAS_PointAreaNum(start); 391 } //end for 392 } //end for 393 } //end for 394 } //end for 395 //if an area was found 396 if (areanum) 397 { 398 //drop client bbox down and try again 399 VectorCopy(start, end); 400 start[2] += 0.25; 401 end[2] -= 50; 402 trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); 403 if (!trace.startsolid) 404 { 405 areanum = AAS_PointAreaNum(trace.endpos); 406 VectorCopy(trace.endpos, goalorigin); 407 //FIXME: cannot enable next line right now because the reachability 408 // does not have to be calculated when the level items are loaded 409 //if the origin is in an area with reachability 410 //if (AAS_AreaReachability(areanum)) return areanum; 411 if (areanum) return areanum; 412 } //end if 413 else 414 { 415 //it can very well happen that the AAS_PointAreaNum function tells that 416 //a point is in an area and that starting a AAS_TraceClientBBox from that 417 //point will return trace.startsolid qtrue 418 #if 0 419 if (AAS_PointAreaNum(start)) 420 { 421 Log_Write("point %f %f %f in area %d but trace startsolid", start[0], start[1], start[2], areanum); 422 AAS_DrawPermanentCross(start, 4, LINECOLOR_RED); 423 } //end if 424 botimport.Print(PRT_MESSAGE, "AAS_BestReachableArea: start solid\n"); 425 #endif 426 VectorCopy(start, goalorigin); 427 return areanum; 428 } //end else 429 } //end if 430 // 431 //AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); 432 //NOTE: the goal origin does not have to be in the goal area 433 // because the bot will have to move towards the item origin anyway 434 VectorCopy(origin, goalorigin); 435 // 436 VectorAdd(origin, mins, absmins); 437 VectorAdd(origin, maxs, absmaxs); 438 //add bounding box size 439 //VectorSubtract(absmins, bbmaxs, absmins); 440 //VectorSubtract(absmaxs, bbmins, absmaxs); 441 //link an invalid (-1) entity 442 areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); 443 //get the reachable link arae 444 areanum = AAS_BestReachableLinkArea(areas); 445 //unlink the invalid entity 446 AAS_UnlinkFromAreas(areas); 447 // 448 return areanum; 449 } //end of the function AAS_BestReachableArea 450 //=========================================================================== 451 // 452 // Parameter: - 453 // Returns: - 454 // Changes Globals: - 455 //=========================================================================== 456 void AAS_SetupReachabilityHeap(void) 457 { 458 int i; 459 460 reachabilityheap = (aas_lreachability_t *) GetClearedMemory( 461 AAS_MAX_REACHABILITYSIZE * sizeof(aas_lreachability_t)); 462 for (i = 0; i < AAS_MAX_REACHABILITYSIZE-1; i++) 463 { 464 reachabilityheap[i].next = &reachabilityheap[i+1]; 465 } //end for 466 reachabilityheap[AAS_MAX_REACHABILITYSIZE-1].next = NULL; 467 nextreachability = reachabilityheap; 468 numlreachabilities = 0; 469 } //end of the function AAS_InitReachabilityHeap 470 //=========================================================================== 471 // 472 // Parameter: - 473 // Returns: - 474 // Changes Globals: - 475 //=========================================================================== 476 void AAS_ShutDownReachabilityHeap(void) 477 { 478 FreeMemory(reachabilityheap); 479 numlreachabilities = 0; 480 } //end of the function AAS_ShutDownReachabilityHeap 481 //=========================================================================== 482 // returns a reachability link 483 // 484 // Parameter: - 485 // Returns: - 486 // Changes Globals: - 487 //=========================================================================== 488 aas_lreachability_t *AAS_AllocReachability(void) 489 { 490 aas_lreachability_t *r; 491 492 if (!nextreachability) return NULL; 493 //make sure the error message only shows up once 494 if (!nextreachability->next) AAS_Error("AAS_MAX_REACHABILITYSIZE"); 495 // 496 r = nextreachability; 497 nextreachability = nextreachability->next; 498 numlreachabilities++; 499 return r; 500 } //end of the function AAS_AllocReachability 501 //=========================================================================== 502 // frees a reachability link 503 // 504 // Parameter: - 505 // Returns: - 506 // Changes Globals: - 507 //=========================================================================== 508 void AAS_FreeReachability(aas_lreachability_t *lreach) 509 { 510 Com_Memset(lreach, 0, sizeof(aas_lreachability_t)); 511 512 lreach->next = nextreachability; 513 nextreachability = lreach; 514 numlreachabilities--; 515 } //end of the function AAS_FreeReachability 516 //=========================================================================== 517 // returns qtrue if the area has reachability links 518 // 519 // Parameter: - 520 // Returns: - 521 // Changes Globals: - 522 //=========================================================================== 523 int AAS_AreaReachability(int areanum) 524 { 525 if (areanum < 0 || areanum >= aasworld.numareas) 526 { 527 AAS_Error("AAS_AreaReachability: areanum %d out of range", areanum); 528 return 0; 529 } //end if 530 return aasworld.areasettings[areanum].numreachableareas; 531 } //end of the function AAS_AreaReachability 532 //=========================================================================== 533 // returns the surface area of all ground faces together of the area 534 // 535 // Parameter: - 536 // Returns: - 537 // Changes Globals: - 538 //=========================================================================== 539 float AAS_AreaGroundFaceArea(int areanum) 540 { 541 int i; 542 float total; 543 aas_area_t *area; 544 aas_face_t *face; 545 546 total = 0; 547 area = &aasworld.areas[areanum]; 548 for (i = 0; i < area->numfaces; i++) 549 { 550 face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; 551 if (!(face->faceflags & FACE_GROUND)) continue; 552 // 553 total += AAS_FaceArea(face); 554 } //end for 555 return total; 556 } //end of the function AAS_AreaGroundFaceArea 557 //=========================================================================== 558 // returns the center of a face 559 // 560 // Parameter: - 561 // Returns: - 562 // Changes Globals: - 563 //=========================================================================== 564 void AAS_FaceCenter(int facenum, vec3_t center) 565 { 566 int i; 567 float scale; 568 aas_face_t *face; 569 aas_edge_t *edge; 570 571 face = &aasworld.faces[facenum]; 572 573 VectorClear(center); 574 for (i = 0; i < face->numedges; i++) 575 { 576 edge = &aasworld.edges[abs(aasworld.edgeindex[face->firstedge + i])]; 577 VectorAdd(center, aasworld.vertexes[edge->v[0]], center); 578 VectorAdd(center, aasworld.vertexes[edge->v[1]], center); 579 } //end for 580 scale = 0.5 / face->numedges; 581 VectorScale(center, scale, center); 582 } //end of the function AAS_FaceCenter 583 //=========================================================================== 584 // returns the maximum distance a player can fall before being damaged 585 // damage = deltavelocity*deltavelocity * 0.0001 586 // 587 // Parameter: - 588 // Returns: - 589 // Changes Globals: - 590 //=========================================================================== 591 int AAS_FallDamageDistance(void) 592 { 593 float maxzvelocity, gravity, t; 594 595 maxzvelocity = sqrt(30 * 10000); 596 gravity = aassettings.phys_gravity; 597 t = maxzvelocity / gravity; 598 return 0.5 * gravity * t * t; 599 } //end of the function AAS_FallDamageDistance 600 //=========================================================================== 601 // distance = 0.5 * gravity * t * t 602 // vel = t * gravity 603 // damage = vel * vel * 0.0001 604 // 605 // Parameter: - 606 // Returns: - 607 // Changes Globals: - 608 //=========================================================================== 609 float AAS_FallDelta(float distance) 610 { 611 float t, delta, gravity; 612 613 gravity = aassettings.phys_gravity; 614 t = sqrt(fabs(distance) * 2 / gravity); 615 delta = t * gravity; 616 return delta * delta * 0.0001; 617 } //end of the function AAS_FallDelta 618 //=========================================================================== 619 // 620 // Parameter: - 621 // Returns: - 622 // Changes Globals: - 623 //=========================================================================== 624 float AAS_MaxJumpHeight(float phys_jumpvel) 625 { 626 float phys_gravity; 627 628 phys_gravity = aassettings.phys_gravity; 629 //maximum height a player can jump with the given initial z velocity 630 return 0.5 * phys_gravity * (phys_jumpvel / phys_gravity) * (phys_jumpvel / phys_gravity); 631 } //end of the function MaxJumpHeight 632 //=========================================================================== 633 // returns true if a player can only crouch in the area 634 // 635 // Parameter: - 636 // Returns: - 637 // Changes Globals: - 638 //=========================================================================== 639 float AAS_MaxJumpDistance(float phys_jumpvel) 640 { 641 float phys_gravity, phys_maxvelocity, t; 642 643 phys_gravity = aassettings.phys_gravity; 644 phys_maxvelocity = aassettings.phys_maxvelocity; 645 //time a player takes to fall the height 646 t = sqrt(aassettings.rs_maxjumpfallheight / (0.5 * phys_gravity)); 647 //maximum distance 648 return phys_maxvelocity * (t + phys_jumpvel / phys_gravity); 649 } //end of the function AAS_MaxJumpDistance 650 //=========================================================================== 651 // returns true if a player can only crouch in the area 652 // 653 // Parameter: - 654 // Returns: - 655 // Changes Globals: - 656 //=========================================================================== 657 int AAS_AreaCrouch(int areanum) 658 { 659 if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qtrue; 660 else return qfalse; 661 } //end of the function AAS_AreaCrouch 662 //=========================================================================== 663 // returns qtrue if it is possible to swim in the area 664 // 665 // Parameter: - 666 // Returns: - 667 // Changes Globals: - 668 //=========================================================================== 669 int AAS_AreaSwim(int areanum) 670 { 671 if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue; 672 else return qfalse; 673 } //end of the function AAS_AreaSwim 674 //=========================================================================== 675 // returns qtrue if the area contains a liquid 676 // 677 // Parameter: - 678 // Returns: - 679 // Changes Globals: - 680 //=========================================================================== 681 int AAS_AreaLiquid(int areanum) 682 { 683 if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue; 684 else return qfalse; 685 } //end of the function AAS_AreaLiquid 686 //=========================================================================== 687 // 688 // Parameter: - 689 // Returns: - 690 // Changes Globals: - 691 //=========================================================================== 692 int AAS_AreaLava(int areanum) 693 { 694 return (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA); 695 } //end of the function AAS_AreaLava 696 //=========================================================================== 697 // 698 // Parameter: - 699 // Returns: - 700 // Changes Globals: - 701 //=========================================================================== 702 int AAS_AreaSlime(int areanum) 703 { 704 return (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME); 705 } //end of the function AAS_AreaSlime 706 //=========================================================================== 707 // returns qtrue if the area contains ground faces 708 // 709 // Parameter: - 710 // Returns: - 711 // Changes Globals: - 712 //=========================================================================== 713 int AAS_AreaGrounded(int areanum) 714 { 715 return (aasworld.areasettings[areanum].areaflags & AREA_GROUNDED); 716 } //end of the function AAS_AreaGround 717 //=========================================================================== 718 // returns true if the area contains ladder faces 719 // 720 // Parameter: - 721 // Returns: - 722 // Changes Globals: - 723 //=========================================================================== 724 int AAS_AreaLadder(int areanum) 725 { 726 return (aasworld.areasettings[areanum].areaflags & AREA_LADDER); 727 } //end of the function AAS_AreaLadder 728 //=========================================================================== 729 // 730 // Parameter: - 731 // Returns: - 732 // Changes Globals: - 733 //=========================================================================== 734 int AAS_AreaJumpPad(int areanum) 735 { 736 return (aasworld.areasettings[areanum].contents & AREACONTENTS_JUMPPAD); 737 } //end of the function AAS_AreaJumpPad 738 //=========================================================================== 739 // 740 // Parameter: - 741 // Returns: - 742 // Changes Globals: - 743 //=========================================================================== 744 int AAS_AreaTeleporter(int areanum) 745 { 746 return (aasworld.areasettings[areanum].contents & AREACONTENTS_TELEPORTER); 747 } //end of the function AAS_AreaTeleporter 748 //=========================================================================== 749 // 750 // Parameter: - 751 // Returns: - 752 // Changes Globals: - 753 //=========================================================================== 754 int AAS_AreaClusterPortal(int areanum) 755 { 756 return (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL); 757 } //end of the function AAS_AreaClusterPortal 758 //=========================================================================== 759 // 760 // Parameter: - 761 // Returns: - 762 // Changes Globals: - 763 //=========================================================================== 764 int AAS_AreaDoNotEnter(int areanum) 765 { 766 return (aasworld.areasettings[areanum].contents & AREACONTENTS_DONOTENTER); 767 } //end of the function AAS_AreaDoNotEnter 768 //=========================================================================== 769 // returns the time it takes perform a barrier jump 770 // 771 // Parameter: - 772 // Returns: - 773 // Changes Globals: - 774 //=========================================================================== 775 unsigned short int AAS_BarrierJumpTravelTime(void) 776 { 777 return aassettings.phys_jumpvel / (aassettings.phys_gravity * 0.1); 778 } //end op the function AAS_BarrierJumpTravelTime 779 //=========================================================================== 780 // returns true if there already exists a reachability from area1 to area2 781 // 782 // Parameter: - 783 // Returns: - 784 // Changes Globals: - 785 //=========================================================================== 786 qboolean AAS_ReachabilityExists(int area1num, int area2num) 787 { 788 aas_lreachability_t *r; 789 790 for (r = areareachability[area1num]; r; r = r->next) 791 { 792 if (r->areanum == area2num) return qtrue; 793 } //end for 794 return qfalse; 795 } //end of the function AAS_ReachabilityExists 796 //=========================================================================== 797 // returns true if there is a solid just after the end point when going 798 // from start to end 799 // 800 // Parameter: - 801 // Returns: - 802 // Changes Globals: - 803 //=========================================================================== 804 int AAS_NearbySolidOrGap(vec3_t start, vec3_t end) 805 { 806 vec3_t dir, testpoint; 807 int areanum; 808 809 VectorSubtract(end, start, dir); 810 dir[2] = 0; 811 VectorNormalize(dir); 812 VectorMA(end, 48, dir, testpoint); 813 814 areanum = AAS_PointAreaNum(testpoint); 815 if (!areanum) 816 { 817 testpoint[2] += 16; 818 areanum = AAS_PointAreaNum(testpoint); 819 if (!areanum) return qtrue; 820 } //end if 821 VectorMA(end, 64, dir, testpoint); 822 areanum = AAS_PointAreaNum(testpoint); 823 if (areanum) 824 { 825 if (!AAS_AreaSwim(areanum) && !AAS_AreaGrounded(areanum)) return qtrue; 826 } //end if 827 return qfalse; 828 } //end of the function AAS_SolidGapTime 829 //=========================================================================== 830 // searches for swim reachabilities between adjacent areas 831 // 832 // Parameter: - 833 // Returns: - 834 // Changes Globals: - 835 //=========================================================================== 836 int AAS_Reachability_Swim(int area1num, int area2num) 837 { 838 int i, j, face1num, face2num, side1; 839 aas_area_t *area1, *area2; 840 aas_areasettings_t *areasettings; 841 aas_lreachability_t *lreach; 842 aas_face_t *face1; 843 aas_plane_t *plane; 844 vec3_t start; 845 846 if (!AAS_AreaSwim(area1num) || !AAS_AreaSwim(area2num)) return qfalse; 847 //if the second area is crouch only 848 if (!(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) return qfalse; 849 850 area1 = &aasworld.areas[area1num]; 851 area2 = &aasworld.areas[area2num]; 852 853 //if the areas are not near anough 854 for (i = 0; i < 3; i++) 855 { 856 if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; 857 if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; 858 } //end for 859 //find a shared face and create a reachability link 860 for (i = 0; i < area1->numfaces; i++) 861 { 862 face1num = aasworld.faceindex[area1->firstface + i]; 863 side1 = face1num < 0; 864 face1num = abs(face1num); 865 // 866 for (j = 0; j < area2->numfaces; j++) 867 { 868 face2num = abs(aasworld.faceindex[area2->firstface + j]); 869 // 870 if (face1num == face2num) 871 { 872 AAS_FaceCenter(face1num, start); 873 // 874 if (AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) 875 { 876 // 877 face1 = &aasworld.faces[face1num]; 878 areasettings = &aasworld.areasettings[area1num]; 879 //create a new reachability link 880 lreach = AAS_AllocReachability(); 881 if (!lreach) return qfalse; 882 lreach->areanum = area2num; 883 lreach->facenum = face1num; 884 lreach->edgenum = 0; 885 VectorCopy(start, lreach->start); 886 plane = &aasworld.planes[face1->planenum ^ side1]; 887 VectorMA(lreach->start, -INSIDEUNITS, plane->normal, lreach->end); 888 lreach->traveltype = TRAVEL_SWIM; 889 lreach->traveltime = 1; 890 //if the volume of the area is rather small 891 if (AAS_AreaVolume(area2num) < 800) 892 lreach->traveltime += 200; 893 //if (!(AAS_PointContents(start) & MASK_WATER)) lreach->traveltime += 500; 894 //link the reachability 895 lreach->next = areareachability[area1num]; 896 areareachability[area1num] = lreach; 897 reach_swim++; 898 return qtrue; 899 } //end if 900 } //end if 901 } //end for 902 } //end for 903 return qfalse; 904 } //end of the function AAS_Reachability_Swim 905 //=========================================================================== 906 // searches for reachabilities between adjacent areas with equal floor 907 // heights 908 // 909 // Parameter: - 910 // Returns: - 911 // Changes Globals: - 912 //=========================================================================== 913 int AAS_Reachability_EqualFloorHeight(int area1num, int area2num) 914 { 915 int i, j, edgenum, edgenum1, edgenum2, foundreach, side; 916 float height, bestheight, length, bestlength; 917 vec3_t dir, start, end, normal, invgravity, gravitydirection = {0, 0, -1}; 918 vec3_t edgevec; 919 aas_area_t *area1, *area2; 920 aas_face_t *face1, *face2; 921 aas_edge_t *edge; 922 aas_plane_t *plane2; 923 aas_lreachability_t lr, *lreach; 924 925 if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse; 926 927 area1 = &aasworld.areas[area1num]; 928 area2 = &aasworld.areas[area2num]; 929 //if the areas are not near anough in the x-y direction 930 for (i = 0; i < 2; i++) 931 { 932 if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; 933 if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; 934 } //end for 935 //if area 2 is too high above area 1 936 if (area2->mins[2] > area1->maxs[2]) return qfalse; 937 // 938 VectorCopy(gravitydirection, invgravity); 939 VectorInverse(invgravity); 940 // 941 bestheight = 99999; 942 bestlength = 0; 943 foundreach = qfalse; 944 Com_Memset(&lr, 0, sizeof(aas_lreachability_t)); //make the compiler happy 945 // 946 //check if the areas have ground faces with a common edge 947 //if existing use the lowest common edge for a reachability link 948 for (i = 0; i < area1->numfaces; i++) 949 { 950 face1 = &aasworld.faces[abs(aasworld.faceindex[area1->firstface + i])]; 951 if (!(face1->faceflags & FACE_GROUND)) continue; 952 // 953 for (j = 0; j < area2->numfaces; j++) 954 { 955 face2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])]; 956 if (!(face2->faceflags & FACE_GROUND)) continue; 957 //if there is a common edge 958 for (edgenum1 = 0; edgenum1 < face1->numedges; edgenum1++) 959 { 960 for (edgenum2 = 0; edgenum2 < face2->numedges; edgenum2++) 961 { 962 if (abs(aasworld.edgeindex[face1->firstedge + edgenum1]) != 963 abs(aasworld.edgeindex[face2->firstedge + edgenum2])) 964 continue; 965 edgenum = aasworld.edgeindex[face1->firstedge + edgenum1]; 966 side = edgenum < 0; 967 edge = &aasworld.edges[abs(edgenum)]; 968 //get the length of the edge 969 VectorSubtract(aasworld.vertexes[edge->v[1]], 970 aasworld.vertexes[edge->v[0]], dir); 971 length = VectorLength(dir); 972 //get the start point 973 VectorAdd(aasworld.vertexes[edge->v[0]], 974 aasworld.vertexes[edge->v[1]], start); 975 VectorScale(start, 0.5, start); 976 VectorCopy(start, end); 977 //get the end point several units inside area2 978 //and the start point several units inside area1 979 //NOTE: normal is pointing into area2 because the 980 //face edges are stored counter clockwise 981 VectorSubtract(aasworld.vertexes[edge->v[side]], 982 aasworld.vertexes[edge->v[!side]], edgevec); 983 plane2 = &aasworld.planes[face2->planenum]; 984 CrossProduct(edgevec, plane2->normal, normal); 985 VectorNormalize(normal); 986 // 987 //VectorMA(start, -1, normal, start); 988 VectorMA(end, INSIDEUNITS_WALKEND, normal, end); 989 VectorMA(start, INSIDEUNITS_WALKSTART, normal, start); 990 end[2] += 0.125; 991 // 992 height = DotProduct(invgravity, start); 993 //NOTE: if there's nearby solid or a gap area after this area 994 //disabled this crap 995 //if (AAS_NearbySolidOrGap(start, end)) height += 200; 996 //NOTE: disabled because it disables reachabilities to very small areas 997 //if (AAS_PointAreaNum(end) != area2num) continue; 998 //get the longest lowest edge 999 if (height < bestheight || 1000 (height < bestheight + 1 && length > bestlength)) 1001 { 1002 bestheight = height; 1003 bestlength = length; 1004 //create a new reachability link 1005 lr.areanum = area2num; 1006 lr.facenum = 0; 1007 lr.edgenum = edgenum; 1008 VectorCopy(start, lr.start); 1009 VectorCopy(end, lr.end); 1010 lr.traveltype = TRAVEL_WALK; 1011 lr.traveltime = 1; 1012 foundreach = qtrue; 1013 } //end if 1014 } //end for 1015 } //end for 1016 } //end for 1017 } //end for 1018 if (foundreach) 1019 { 1020 //create a new reachability link 1021 lreach = AAS_AllocReachability(); 1022 if (!lreach) return qfalse; 1023 lreach->areanum = lr.areanum; 1024 lreach->facenum = lr.facenum; 1025 lreach->edgenum = lr.edgenum; 1026 VectorCopy(lr.start, lreach->start); 1027 VectorCopy(lr.end, lreach->end); 1028 lreach->traveltype = lr.traveltype; 1029 lreach->traveltime = lr.traveltime; 1030 lreach->next = areareachability[area1num]; 1031 areareachability[area1num] = lreach; 1032 //if going into a crouch area 1033 if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num)) 1034 { 1035 lreach->traveltime += aassettings.rs_startcrouch; 1036 } //end if 1037 /* 1038 //NOTE: if there's nearby solid or a gap area after this area 1039 if (!AAS_NearbySolidOrGap(lreach->start, lreach->end)) 1040 { 1041 lreach->traveltime += 100; 1042 } //end if 1043 */ 1044 //avoid rather small areas 1045 //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100; 1046 // 1047 reach_equalfloor++; 1048 return qtrue; 1049 } //end if 1050 return qfalse; 1051 } //end of the function AAS_Reachability_EqualFloorHeight 1052 //=========================================================================== 1053 // searches step, barrier, waterjump and walk off ledge reachabilities 1054 // 1055 // Parameter: - 1056 // Returns: - 1057 // Changes Globals: - 1058 //=========================================================================== 1059 int AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2num) 1060 { 1061 int i, j, k, l, edge1num, edge2num, areas[10], numareas; 1062 int ground_bestarea2groundedgenum, ground_foundreach; 1063 int water_bestarea2groundedgenum, water_foundreach; 1064 int side1, area1swim, faceside1, groundface1num; 1065 float dist, dist1, dist2, diff, invgravitydot, ortdot; 1066 float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y; 1067 float length, ground_bestlength, water_bestlength, ground_bestdist, water_bestdist; 1068 vec3_t v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2; 1069 vec3_t normal, ort, edgevec, start, end, dir; 1070 vec3_t ground_beststart, ground_bestend, ground_bestnormal; 1071 vec3_t water_beststart, water_bestend, water_bestnormal; 1072 vec3_t invgravity = {0, 0, 1}; 1073 vec3_t testpoint; 1074 aas_plane_t *plane; 1075 aas_area_t *area1, *area2; 1076 aas_face_t *groundface1, *groundface2, *ground_bestface1, *water_bestface1; 1077 aas_edge_t *edge1, *edge2; 1078 aas_lreachability_t *lreach; 1079 aas_trace_t trace; 1080 1081 //must be able to walk or swim in the first area 1082 if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse; 1083 // 1084 if (!AAS_AreaGrounded(area2num) && !AAS_AreaSwim(area2num)) return qfalse; 1085 // 1086 area1 = &aasworld.areas[area1num]; 1087 area2 = &aasworld.areas[area2num]; 1088 //if the first area contains a liquid 1089 area1swim = AAS_AreaSwim(area1num); 1090 //if the areas are not near anough in the x-y direction 1091 for (i = 0; i < 2; i++) 1092 { 1093 if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; 1094 if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; 1095 } //end for 1096 // 1097 ground_foundreach = qfalse; 1098 ground_bestdist = 99999; 1099 ground_bestlength = 0; 1100 ground_bestarea2groundedgenum = 0; 1101 // 1102 water_foundreach = qfalse; 1103 water_bestdist = 99999; 1104 water_bestlength = 0; 1105 water_bestarea2groundedgenum = 0; 1106 // 1107 for (i = 0; i < area1->numfaces; i++) 1108 { 1109 groundface1num = aasworld.faceindex[area1->firstface + i]; 1110 faceside1 = groundface1num < 0; 1111 groundface1 = &aasworld.faces[abs(groundface1num)]; 1112 //if this isn't a ground face 1113 if (!(groundface1->faceflags & FACE_GROUND)) 1114 { 1115 //if we can swim in the first area 1116 if (area1swim) 1117 { 1118 //face plane must be more or less horizontal 1119 plane = &aasworld.planes[groundface1->planenum ^ (!faceside1)]; 1120 if (DotProduct(plane->normal, invgravity) < 0.7) continue; 1121 } //end if 1122 else 1123 { 1124 //if we can't swim in the area it must be a ground face 1125 continue; 1126 } //end else 1127 } //end if 1128 // 1129 for (k = 0; k < groundface1->numedges; k++) 1130 { 1131 edge1num = aasworld.edgeindex[groundface1->firstedge + k]; 1132 side1 = (edge1num < 0); 1133 //NOTE: for water faces we must take the side area 1 is 1134 // on into account because the face is shared and doesn't 1135 // have to be oriented correctly 1136 if (!(groundface1->faceflags & FACE_GROUND)) side1 = (side1 == faceside1); 1137 edge1num = abs(edge1num); 1138 edge1 = &aasworld.edges[edge1num]; 1139 //vertexes of the edge 1140 VectorCopy(aasworld.vertexes[edge1->v[!side1]], v1); 1141 VectorCopy(aasworld.vertexes[edge1->v[side1]], v2); 1142 //get a vertical plane through the edge 1143 //NOTE: normal is pointing into area 2 because the 1144 //face edges are stored counter clockwise 1145 VectorSubtract(v2, v1, edgevec); 1146 CrossProduct(edgevec, invgravity, normal); 1147 VectorNormalize(normal); 1148 dist = DotProduct(normal, v1); 1149 //check the faces from the second area 1150 for (j = 0; j < area2->numfaces; j++) 1151 { 1152 groundface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])]; 1153 //must be a ground face 1154 if (!(groundface2->faceflags & FACE_GROUND)) continue; 1155 //check the edges of this ground face 1156 for (l = 0; l < groundface2->numedges; l++) 1157 { 1158 edge2num = abs(aasworld.edgeindex[groundface2->firstedge + l]); 1159 edge2 = &aasworld.edges[edge2num]; 1160 //vertexes of the edge 1161 VectorCopy(aasworld.vertexes[edge2->v[0]], v3); 1162 VectorCopy(aasworld.vertexes[edge2->v[1]], v4); 1163 //check the distance between the two points and the vertical plane 1164 //through the edge of area1 1165 diff = DotProduct(normal, v3) - dist; 1166 if (diff < -0.1 || diff > 0.1) continue; 1167 diff = DotProduct(normal, v4) - dist; 1168 if (diff < -0.1 || diff > 0.1) continue; 1169 // 1170 //project the two ground edges into the step side plane 1171 //and calculate the shortest distance between the two 1172 //edges if they overlap in the direction orthogonal to 1173 //the gravity direction 1174 CrossProduct(invgravity, normal, ort); 1175 invgravitydot = DotProduct(invgravity, invgravity); 1176 ortdot = DotProduct(ort, ort); 1177 //projection into the step plane 1178 //NOTE: since gravity is vertical this is just the z coordinate 1179 y1 = v1[2];//DotProduct(v1, invgravity) / invgravitydot; 1180 y2 = v2[2];//DotProduct(v2, invgravity) / invgravitydot; 1181 y3 = v3[2];//DotProduct(v3, invgravity) / invgravitydot; 1182 y4 = v4[2];//DotProduct(v4, invgravity) / invgravitydot; 1183 // 1184 x1 = DotProduct(v1, ort) / ortdot; 1185 x2 = DotProduct(v2, ort) / ortdot; 1186 x3 = DotProduct(v3, ort) / ortdot; 1187 x4 = DotProduct(v4, ort) / ortdot; 1188 // 1189 if (x1 > x2) 1190 { 1191 tmp = x1; x1 = x2; x2 = tmp; 1192 tmp = y1; y1 = y2; y2 = tmp; 1193 VectorCopy(v1, tmpv); VectorCopy(v2, v1); VectorCopy(tmpv, v2); 1194 } //end if 1195 if (x3 > x4) 1196 { 1197 tmp = x3; x3 = x4; x4 = tmp; 1198 tmp = y3; y3 = y4; y4 = tmp; 1199 VectorCopy(v3, tmpv); VectorCopy(v4, v3); VectorCopy(tmpv, v4); 1200 } //end if 1201 //if the two projected edge lines have no overlap 1202 if (x2 <= x3 || x4 <= x1) 1203 { 1204 // Log_Write("lines no overlap: from area %d to %d\r\n", area1num, area2num); 1205 continue; 1206 } //end if 1207 //if the two lines fully overlap 1208 if ((x1 - 0.5 < x3 && x4 < x2 + 0.5) && 1209 (x3 - 0.5 < x1 && x2 < x4 + 0.5)) 1210 { 1211 dist1 = y3 - y1; 1212 dist2 = y4 - y2; 1213 VectorCopy(v1, p1area1); 1214 VectorCopy(v2, p2area1); 1215 VectorCopy(v3, p1area2); 1216 VectorCopy(v4, p2area2); 1217 } //end if 1218 else 1219 { 1220 //if the points are equal 1221 if (x1 > x3 - 0.1 && x1 < x3 + 0.1) 1222 { 1223 dist1 = y3 - y1; 1224 VectorCopy(v1, p1area1); 1225 VectorCopy(v3, p1area2); 1226 } //end if 1227 else if (x1 < x3) 1228 { 1229 y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1); 1230 dist1 = y3 - y; 1231 VectorCopy(v3, p1area1); 1232 p1area1[2] = y; 1233 VectorCopy(v3, p1area2); 1234 } //end if 1235 else 1236 { 1237 y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3); 1238 dist1 = y - y1; 1239 VectorCopy(v1, p1area1); 1240 VectorCopy(v1, p1area2); 1241 p1area2[2] = y; 1242 } //end if 1243 //if the points are equal 1244 if (x2 > x4 - 0.1 && x2 < x4 + 0.1) 1245 { 1246 dist2 = y4 - y2; 1247 VectorCopy(v2, p2area1); 1248 VectorCopy(v4, p2area2); 1249 } //end if 1250 else if (x2 < x4) 1251 { 1252 y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3); 1253 dist2 = y - y2; 1254 VectorCopy(v2, p2area1); 1255 VectorCopy(v2, p2area2); 1256 p2area2[2] = y; 1257 } //end if 1258 else 1259 { 1260 y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1); 1261 dist2 = y4 - y; 1262 VectorCopy(v4, p2area1); 1263 p2area1[2] = y; 1264 VectorCopy(v4, p2area2); 1265 } //end else 1266 } //end else 1267 //if both distances are pretty much equal 1268 //then we take the middle of the points 1269 if (dist1 > dist2 - 1 && dist1 < dist2 + 1) 1270 { 1271 dist = dist1; 1272 VectorAdd(p1area1, p2area1, start); 1273 VectorScale(start, 0.5, start); 1274 VectorAdd(p1area2, p2area2, end); 1275 VectorScale(end, 0.5, end); 1276 } //end if 1277 else if (dist1 < dist2) 1278 { 1279 dist = dist1; 1280 VectorCopy(p1area1, start); 1281 VectorCopy(p1area2, end); 1282 } //end else if 1283 else 1284 { 1285 dist = dist2; 1286 VectorCopy(p2area1, start); 1287 VectorCopy(p2area2, end); 1288 } //end else 1289 //get the length of the overlapping part of the edges of the two areas 1290 VectorSubtract(p2area2, p1area2, dir); 1291 length = VectorLength(dir); 1292 // 1293 if (groundface1->faceflags & FACE_GROUND) 1294 { 1295 //if the vertical distance is smaller 1296 if (dist < ground_bestdist || 1297 //or the vertical distance is pretty much the same 1298 //but the overlapping part of the edges is longer 1299 (dist < ground_bestdist + 1 && length > ground_bestlength)) 1300 { 1301 ground_bestdist = dist; 1302 ground_bestlength = length; 1303 ground_foundreach = qtrue; 1304 ground_bestarea2groundedgenum = edge1num; 1305 ground_bestface1 = groundface1; 1306 //best point towards area1 1307 VectorCopy(start, ground_beststart); 1308 //normal is pointing into area2 1309 VectorCopy(normal, ground_bestnormal); 1310 //best point towards area2 1311 VectorCopy(end, ground_bestend); 1312 } //end if 1313 } //end if 1314 else 1315 { 1316 //if the vertical distance is smaller 1317 if (dist < water_bestdist || 1318 //or the vertical distance is pretty much the same 1319 //but the overlapping part of the edges is longer 1320 (dist < water_bestdist + 1 && length > water_bestlength)) 1321 { 1322 water_bestdist = dist; 1323 water_bestlength = length; 1324 water_foundreach = qtrue; 1325 water_bestarea2groundedgenum = edge1num; 1326 water_bestface1 = groundface1; 1327 //best point towards area1 1328 VectorCopy(start, water_beststart); 1329 //normal is pointing into area2 1330 VectorCopy(normal, water_bestnormal); 1331 //best point towards area2 1332 VectorCopy(end, water_bestend); 1333 } //end if 1334 } //end else 1335 } //end for 1336 } //end for 1337 } //end for 1338 } //end for 1339 // 1340 // NOTE: swim reachabilities are already filtered out 1341 // 1342 // Steps 1343 // 1344 // --------- 1345 // | step height -> TRAVEL_WALK 1346 //--------| 1347 // 1348 // --------- 1349 //~~~~~~~~| step height and low water -> TRAVEL_WALK 1350 //--------| 1351 // 1352 //~~~~~~~~~~~~~~~~~~ 1353 // --------- 1354 // | step height and low water up to the step -> TRAVEL_WALK 1355 //--------| 1356 // 1357 //check for a step reachability 1358 if (ground_foundreach) 1359 { 1360 //if area2 is higher but lower than the maximum step height 1361 //NOTE: ground_bestdist >= 0 also catches equal floor reachabilities 1362 if (ground_bestdist >= 0 && ground_bestdist < aassettings.phys_maxstep) 1363 { 1364 //create walk reachability from area1 to area2 1365 lreach = AAS_AllocReachability(); 1366 if (!lreach) return qfalse; 1367 lreach->areanum = area2num; 1368 lreach->facenum = 0; 1369 lreach->edgenum = ground_bestarea2groundedgenum; 1370 VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); 1371 VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); 1372 lreach->traveltype = TRAVEL_WALK; 1373 lreach->traveltime = 0;//1; 1374 //if going into a crouch area 1375 if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num)) 1376 { 1377 lreach->traveltime += aassettings.rs_startcrouch; 1378 } //end if 1379 lreach->next = areareachability[area1num]; 1380 areareachability[area1num] = lreach; 1381 //NOTE: if there's nearby solid or a gap area after this area 1382 /* 1383 if (!AAS_NearbySolidOrGap(lreach->start, lreach->end)) 1384 { 1385 lreach->traveltime += 100; 1386 } //end if 1387 */ 1388 //avoid rather small areas 1389 //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100; 1390 // 1391 reach_step++; 1392 return qtrue; 1393 } //end if 1394 } //end if 1395 // 1396 // Water Jumps 1397 // 1398 // --------- 1399 // | 1400 //~~~~~~~~| 1401 // | 1402 // | higher than step height and water up to waterjump height -> TRAVEL_WATERJUMP 1403 //--------| 1404 // 1405 //~~~~~~~~~~~~~~~~~~ 1406 // --------- 1407 // | 1408 // | 1409 // | 1410 // | higher than step height and low water up to the step -> TRAVEL_WATERJUMP 1411 //--------| 1412 // 1413 //check for a waterjump reachability 1414 if (water_foundreach) 1415 { 1416 //get a test point a little bit towards area1 1417 VectorMA(water_bestend, -INSIDEUNITS, water_bestnormal, testpoint); 1418 //go down the maximum waterjump height 1419 testpoint[2] -= aassettings.phys_maxwaterjump; 1420 //if there IS water the sv_maxwaterjump height below the bestend point 1421 if (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID) 1422 { 1423 //don't create rediculous water jump reachabilities from areas very far below 1424 //the water surface 1425 if (water_bestdist < aassettings.phys_maxwaterjump + 24) 1426 { 1427 //waterjumping from or towards a crouch only area is not possible in Quake2 1428 if ((aasworld.areasettings[area1num].presencetype & PRESENCE_NORMAL) && 1429 (aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) 1430 { 1431 //create water jump reachability from area1 to area2 1432 lreach = AAS_AllocReachability(); 1433 if (!lreach) return qfalse; 1434 lreach->areanum = area2num; 1435 lreach->facenum = 0; 1436 lreach->edgenum = water_bestarea2groundedgenum; 1437 VectorCopy(water_beststart, lreach->start); 1438 VectorMA(water_bestend, INSIDEUNITS_WATERJUMP, water_bestnormal, lreach->end); 1439 lreach->traveltype = TRAVEL_WATERJUMP; 1440 lreach->traveltime = aassettings.rs_waterjump; 1441 lreach->next = areareachability[area1num]; 1442 areareachability[area1num] = lreach; 1443 //we've got another waterjump reachability 1444 reach_waterjump++; 1445 return qtrue; 1446 } //end if 1447 } //end if 1448 } //end if 1449 } //end if 1450 // 1451 // Barrier Jumps 1452 // 1453 // --------- 1454 // | 1455 // | 1456 // | 1457 // | higher than step height lower than barrier height -> TRAVEL_BARRIERJUMP 1458 //--------| 1459 // 1460 // --------- 1461 // | 1462 // | 1463 // | 1464 //~~~~~~~~| higher than step height lower than barrier height 1465 //--------| and a thin layer of water in the area to jump from -> TRAVEL_BARRIERJUMP 1466 // 1467 //check for a barrier jump reachability 1468 if (ground_foundreach) 1469 { 1470 //if area2 is higher but lower than the maximum barrier jump height 1471 if (ground_bestdist > 0 && ground_bestdist < aassettings.phys_maxbarrier) 1472 { 1473 //if no water in area1 or a very thin layer of water on the ground 1474 if (!water_foundreach || (ground_bestdist - water_bestdist < 16)) 1475 { 1476 //cannot perform a barrier jump towards or from a crouch area in Quake2 1477 if (!AAS_AreaCrouch(area1num) && !AAS_AreaCrouch(area2num)) 1478 { 1479 //create barrier jump reachability from area1 to area2 1480 lreach = AAS_AllocReachability(); 1481 if (!lreach) return qfalse; 1482 lreach->areanum = area2num; 1483 lreach->facenum = 0; 1484 lreach->edgenum = ground_bestarea2groundedgenum; 1485 VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); 1486 VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); 1487 lreach->traveltype = TRAVEL_BARRIERJUMP; 1488 lreach->traveltime = aassettings.rs_barrierjump;//AAS_BarrierJumpTravelTime(); 1489 lreach->next = areareachability[area1num]; 1490 areareachability[area1num] = lreach; 1491 //we've got another barrierjump reachability 1492 reach_barrier++; 1493 return qtrue; 1494 } //end if 1495 } //end if 1496 } //end if 1497 } //end if 1498 // 1499 // Walk and Walk Off Ledge 1500 // 1501 //--------| 1502 // | can walk or step back -> TRAVEL_WALK 1503 // --------- 1504 // 1505 //--------| 1506 // | 1507 // | 1508 // | 1509 // | cannot walk/step back -> TRAVEL_WALKOFFLEDGE 1510 // --------- 1511 // 1512 //--------| 1513 // | 1514 // |~~~~~~~~ 1515 // | 1516 // | cannot step back but can waterjump back -> TRAVEL_WALKOFFLEDGE 1517 // --------- FIXME: create TRAVEL_WALK reach?? 1518 // 1519 //check for a walk or walk off ledge reachability 1520 if (ground_foundreach) 1521 { 1522 if (ground_bestdist < 0) 1523 { 1524 if (ground_bestdist > -aassettings.phys_maxstep) 1525 { 1526 //create walk reachability from area1 to area2 1527 lreach = AAS_AllocReachability(); 1528 if (!lreach) return qfalse; 1529 lreach->areanum = area2num; 1530 lreach->facenum = 0; 1531 lreach->edgenum = ground_bestarea2groundedgenum; 1532 VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); 1533 VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); 1534 lreach->traveltype = TRAVEL_WALK; 1535 lreach->traveltime = 1; 1536 lreach->next = areareachability[area1num]; 1537 areareachability[area1num] = lreach; 1538 //we've got another walk reachability 1539 reach_walk++; 1540 return qtrue; 1541 } //end if 1542 // if no maximum fall height set or less than the max 1543 if (!aassettings.rs_maxfallheight || fabs(ground_bestdist) < aassettings.rs_maxfallheight) { 1544 //trace a bounding box vertically to check for solids 1545 VectorMA(ground_bestend, INSIDEUNITS, ground_bestnormal, ground_bestend); 1546 VectorCopy(ground_bestend, start); 1547 start[2] = ground_beststart[2]; 1548 VectorCopy(ground_bestend, end); 1549 end[2] += 4; 1550 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); 1551 //if no solids were found 1552 if (!trace.startsolid && trace.fraction >= 1.0) 1553 { 1554 //the trace end point must be in the goal area 1555 trace.endpos[2] += 1; 1556 if (AAS_PointAreaNum(trace.endpos) == area2num) 1557 { 1558 //if not going through a cluster portal 1559 numareas = AAS_TraceAreas(start, end, areas, NULL, sizeof(areas) / sizeof(int)); 1560 for (i = 0; i < numareas; i++) 1561 if (AAS_AreaClusterPortal(areas[i])) 1562 break; 1563 if (i >= numareas) 1564 { 1565 //create a walk off ledge reachability from area1 to area2 1566 lreach = AAS_AllocReachability(); 1567 if (!lreach) return qfalse; 1568 lreach->areanum = area2num; 1569 lreach->facenum = 0; 1570 lreach->edgenum = ground_bestarea2groundedgenum; 1571 VectorCopy(ground_beststart, lreach->start); 1572 VectorCopy(ground_bestend, lreach->end); 1573 lreach->traveltype = TRAVEL_WALKOFFLEDGE; 1574 lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(ground_bestdist) * 50 / aassettings.phys_gravity; 1575 //if falling from too high and not falling into water 1576 if (!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num)) 1577 { 1578 if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta5) 1579 { 1580 lreach->traveltime += aassettings.rs_falldamage5; 1581 } //end if 1582 if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta10) 1583 { 1584 lreach->traveltime += aassettings.rs_falldamage10; 1585 } //end if 1586 } //end if 1587 lreach->next = areareachability[area1num]; 1588 areareachability[area1num] = lreach; 1589 // 1590 reach_walkoffledge++; 1591 //NOTE: don't create a weapon (rl, bfg) jump reachability here 1592 //because it interferes with other reachabilities 1593 //like the ladder reachability 1594 return qtrue; 1595 } //end if 1596 } //end if 1597 } //end if 1598 } //end if 1599 } //end else 1600 } //end if 1601 return qfalse; 1602 } //end of the function AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge 1603 //=========================================================================== 1604 // returns the distance between the two vectors 1605 // 1606 // Parameter: - 1607 // Returns: - 1608 // Changes Globals: - 1609 //=========================================================================== 1610 float VectorDistance(vec3_t v1, vec3_t v2) 1611 { 1612 vec3_t dir; 1613 1614 VectorSubtract(v2, v1, dir); 1615 return VectorLength(dir); 1616 } //end of the function VectorDistance 1617 //=========================================================================== 1618 // returns true if the first vector is between the last two vectors 1619 // 1620 // Parameter: - 1621 // Returns: - 1622 // Changes Globals: - 1623 //=========================================================================== 1624 int VectorBetweenVectors(vec3_t v, vec3_t v1, vec3_t v2) 1625 { 1626 vec3_t dir1, dir2; 1627 1628 VectorSubtract(v, v1, dir1); 1629 VectorSubtract(v, v2, dir2); 1630 return (DotProduct(dir1, dir2) <= 0); 1631 } //end of the function VectorBetweenVectors 1632 //=========================================================================== 1633 // returns the mid point between the two vectors 1634 // 1635 // Parameter: - 1636 // Returns: - 1637 // Changes Globals: - 1638 //=========================================================================== 1639 void VectorMiddle(vec3_t v1, vec3_t v2, vec3_t middle) 1640 { 1641 VectorAdd(v1, v2, middle); 1642 VectorScale(middle, 0.5, middle); 1643 } //end of the function VectorMiddle 1644 //=========================================================================== 1645 // calculate a range of points closest to each other on both edges 1646 // 1647 // Parameter: beststart1 start of the range of points on edge v1-v2 1648 // beststart2 end of the range of points on edge v1-v2 1649 // bestend1 start of the range of points on edge v3-v4 1650 // bestend2 end of the range of points on edge v3-v4 1651 // bestdist best distance so far 1652 // Returns: - 1653 // Changes Globals: - 1654 //=========================================================================== 1655 /* 1656 float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4, 1657 aas_plane_t *plane1, aas_plane_t *plane2, 1658 vec3_t beststart, vec3_t bestend, float bestdist) 1659 { 1660 vec3_t dir1, dir2, p1, p2, p3, p4; 1661 float a1, a2, b1, b2, dist; 1662 int founddist; 1663 1664 //edge vectors 1665 VectorSubtract(v2, v1, dir1); 1666 VectorSubtract(v4, v3, dir2); 1667 //get the horizontal directions 1668 dir1[2] = 0; 1669 dir2[2] = 0; 1670 // 1671 // p1 = point on an edge vector of area2 closest to v1 1672 // p2 = point on an edge vector of area2 closest to v2 1673 // p3 = point on an edge vector of area1 closest to v3 1674 // p4 = point on an edge vector of area1 closest to v4 1675 // 1676 if (dir2[0]) 1677 { 1678 a2 = dir2[1] / dir2[0]; 1679 b2 = v3[1] - a2 * v3[0]; 1680 //point on the edge vector of area2 closest to v1 1681 p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; 1682 p1[1] = a2 * p1[0] + b2; 1683 //point on the edge vector of area2 closest to v2 1684 p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; 1685 p2[1] = a2 * p2[0] + b2; 1686 } //end if 1687 else 1688 { 1689 //point on the edge vector of area2 closest to v1 1690 p1[0] = v3[0]; 1691 p1[1] = v1[1]; 1692 //point on the edge vector of area2 closest to v2 1693 p2[0] = v3[0]; 1694 p2[1] = v2[1]; 1695 } //end else 1696 // 1697 if (dir1[0]) 1698 { 1699 // 1700 a1 = dir1[1] / dir1[0]; 1701 b1 = v1[1] - a1 * v1[0]; 1702 //point on the edge vector of area1 closest to v3 1703 p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; 1704 p3[1] = a1 * p3[0] + b1; 1705 //point on the edge vector of area1 closest to v4 1706 p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; 1707 p4[1] = a1 * p4[0] + b1; 1708 } //end if 1709 else 1710 { 1711 //point on the edge vector of area1 closest to v3 1712 p3[0] = v1[0]; 1713 p3[1] = v3[1]; 1714 //point on the edge vector of area1 closest to v4 1715 p4[0] = v1[0]; 1716 p4[1] = v4[1]; 1717 } //end else 1718 //start with zero z-coordinates 1719 p1[2] = 0; 1720 p2[2] = 0; 1721 p3[2] = 0; 1722 p4[2] = 0; 1723 //calculate the z-coordinates from the ground planes 1724 p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2]; 1725 p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2]; 1726 p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2]; 1727 p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2]; 1728 // 1729 founddist = qfalse; 1730 // 1731 if (VectorBetweenVectors(p1, v3, v4)) 1732 { 1733 dist = VectorDistance(v1, p1); 1734 if (dist > bestdist - 0.5 && dist < bestdist + 0.5) 1735 { 1736 VectorMiddle(beststart, v1, beststart); 1737 VectorMiddle(bestend, p1, bestend); 1738 } //end if 1739 else if (dist < bestdist) 1740 { 1741 bestdist = dist; 1742 VectorCopy(v1, beststart); 1743 VectorCopy(p1, bestend); 1744 } //end if 1745 founddist = qtrue; 1746 } //end if 1747 if (VectorBetweenVectors(p2, v3, v4)) 1748 { 1749 dist = VectorDistance(v2, p2); 1750 if (dist > bestdist - 0.5 && dist < bestdist + 0.5) 1751 { 1752 VectorMiddle(beststart, v2, beststart); 1753 VectorMiddle(bestend, p2, bestend); 1754 } //end if 1755 else if (dist < bestdist) 1756 { 1757 bestdist = dist; 1758 VectorCopy(v2, beststart); 1759 VectorCopy(p2, bestend); 1760 } //end if 1761 founddist = qtrue; 1762 } //end else if 1763 if (VectorBetweenVectors(p3, v1, v2)) 1764 { 1765 dist = VectorDistance(v3, p3); 1766 if (dist > bestdist - 0.5 && dist < bestdist + 0.5) 1767 { 1768 VectorMiddle(beststart, p3, beststart); 1769 VectorMiddle(bestend, v3, bestend); 1770 } //end if 1771 else if (dist < bestdist) 1772 { 1773 bestdist = dist; 1774 VectorCopy(p3, beststart); 1775 VectorCopy(v3, bestend); 1776 } //end if 1777 founddist = qtrue; 1778 } //end else if 1779 if (VectorBetweenVectors(p4, v1, v2)) 1780 { 1781 dist = VectorDistance(v4, p4); 1782 if (dist > bestdist - 0.5 && dist < bestdist + 0.5) 1783 { 1784 VectorMiddle(beststart, p4, beststart); 1785 VectorMiddle(bestend, v4, bestend); 1786 } //end if 1787 else if (dist < bestdist) 1788 { 1789 bestdist = dist; 1790 VectorCopy(p4, beststart); 1791 VectorCopy(v4, bestend); 1792 } //end if 1793 founddist = qtrue; 1794 } //end else if 1795 //if no shortest distance was found the shortest distance 1796 //is between one of the vertexes of edge1 and one of edge2 1797 if (!founddist) 1798 { 1799 dist = VectorDistance(v1, v3); 1800 if (dist < bestdist) 1801 { 1802 bestdist = dist; 1803 VectorCopy(v1, beststart); 1804 VectorCopy(v3, bestend); 1805 } //end if 1806 dist = VectorDistance(v1, v4); 1807 if (dist < bestdist) 1808 { 1809 bestdist = dist; 1810 VectorCopy(v1, beststart); 1811 VectorCopy(v4, bestend); 1812 } //end if 1813 dist = VectorDistance(v2, v3); 1814 if (dist < bestdist) 1815 { 1816 bestdist = dist; 1817 VectorCopy(v2, beststart); 1818 VectorCopy(v3, bestend); 1819 } //end if 1820 dist = VectorDistance(v2, v4); 1821 if (dist < bestdist) 1822 { 1823 bestdist = dist; 1824 VectorCopy(v2, beststart); 1825 VectorCopy(v4, bestend); 1826 } //end if 1827 } //end if 1828 return bestdist; 1829 } //end of the function AAS_ClosestEdgePoints*/ 1830 1831 float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4, 1832 aas_plane_t *plane1, aas_plane_t *plane2, 1833 vec3_t beststart1, vec3_t bestend1, 1834 vec3_t beststart2, vec3_t bestend2, float bestdist) 1835 { 1836 vec3_t dir1, dir2, p1, p2, p3, p4; 1837 float a1, a2, b1, b2, dist, dist1, dist2; 1838 int founddist; 1839 1840 //edge vectors 1841 VectorSubtract(v2, v1, dir1); 1842 VectorSubtract(v4, v3, dir2); 1843 //get the horizontal directions 1844 dir1[2] = 0; 1845 dir2[2] = 0; 1846 // 1847 // p1 = point on an edge vector of area2 closest to v1 1848 // p2 = point on an edge vector of area2 closest to v2 1849 // p3 = point on an edge vector of area1 closest to v3 1850 // p4 = point on an edge vector of area1 closest to v4 1851 // 1852 if (dir2[0]) 1853 { 1854 a2 = dir2[1] / dir2[0]; 1855 b2 = v3[1] - a2 * v3[0]; 1856 //point on the edge vector of area2 closest to v1 1857 p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; 1858 p1[1] = a2 * p1[0] + b2; 1859 //point on the edge vector of area2 closest to v2 1860 p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; 1861 p2[1] = a2 * p2[0] + b2; 1862 } //end if 1863 else 1864 { 1865 //point on the edge vector of area2 closest to v1 1866 p1[0] = v3[0]; 1867 p1[1] = v1[1]; 1868 //point on the edge vector of area2 closest to v2 1869 p2[0] = v3[0]; 1870 p2[1] = v2[1]; 1871 } //end else 1872 // 1873 if (dir1[0]) 1874 { 1875 // 1876 a1 = dir1[1] / dir1[0]; 1877 b1 = v1[1] - a1 * v1[0]; 1878 //point on the edge vector of area1 closest to v3 1879 p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; 1880 p3[1] = a1 * p3[0] + b1; 1881 //point on the edge vector of area1 closest to v4 1882 p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; 1883 p4[1] = a1 * p4[0] + b1; 1884 } //end if 1885 else 1886 { 1887 //point on the edge vector of area1 closest to v3 1888 p3[0] = v1[0]; 1889 p3[1] = v3[1]; 1890 //point on the edge vector of area1 closest to v4 1891 p4[0] = v1[0]; 1892 p4[1] = v4[1]; 1893 } //end else 1894 //start with zero z-coordinates 1895 p1[2] = 0; 1896 p2[2] = 0; 1897 p3[2] = 0; 1898 p4[2] = 0; 1899 //calculate the z-coordinates from the ground planes 1900 p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2]; 1901 p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2]; 1902 p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2]; 1903 p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2]; 1904 // 1905 founddist = qfalse; 1906 // 1907 if (VectorBetweenVectors(p1, v3, v4)) 1908 { 1909 dist = VectorDistance(v1, p1); 1910 if (dist > bestdist - 0.5 && dist < bestdist + 0.5) 1911 { 1912 dist1 = VectorDistance(beststart1, v1); 1913 dist2 = VectorDistance(beststart2, v1); 1914 if (dist1 > dist2) 1915 { 1916 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart2); 1917 } //end if 1918 else 1919 { 1920 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart1); 1921 } //end else 1922 dist1 = VectorDistance(bestend1, p1); 1923 dist2 = VectorDistance(bestend2, p1); 1924 if (dist1 > dist2) 1925 { 1926 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend2); 1927 } //end if 1928 else 1929 { 1930 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend1); 1931 } //end else 1932 } //end if 1933 else if (dist < bestdist) 1934 { 1935 bestdist = dist; 1936 VectorCopy(v1, beststart1); 1937 VectorCopy(v1, beststart2); 1938 VectorCopy(p1, bestend1); 1939 VectorCopy(p1, bestend2); 1940 } //end if 1941 founddist = qtrue; 1942 } //end if 1943 if (VectorBetweenVectors(p2, v3, v4)) 1944 { 1945 dist = VectorDistance(v2, p2); 1946 if (dist > bestdist - 0.5 && dist < bestdist + 0.5) 1947 { 1948 dist1 = VectorDistance(beststart1, v2); 1949 dist2 = VectorDistance(beststart2, v2); 1950 if (dist1 > dist2) 1951 { 1952 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart2); 1953 } //end if 1954 else 1955 { 1956 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart1); 1957 } //end else 1958 dist1 = VectorDistance(bestend1, p2); 1959 dist2 = VectorDistance(bestend2, p2); 1960 if (dist1 > dist2) 1961 { 1962 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend2); 1963 } //end if 1964 else 1965 { 1966 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend1); 1967 } //end else 1968 } //end if 1969 else if (dist < bestdist) 1970 { 1971 bestdist = dist; 1972 VectorCopy(v2, beststart1); 1973 VectorCopy(v2, beststart2); 1974 VectorCopy(p2, bestend1); 1975 VectorCopy(p2, bestend2); 1976 } //end if 1977 founddist = qtrue; 1978 } //end else if 1979 if (VectorBetweenVectors(p3, v1, v2)) 1980 { 1981 dist = VectorDistance(v3, p3); 1982 if (dist > bestdist - 0.5 && dist < bestdist + 0.5) 1983 { 1984 dist1 = VectorDistance(beststart1, p3); 1985 dist2 = VectorDistance(beststart2, p3); 1986 if (dist1 > dist2) 1987 { 1988 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart2); 1989 } //end if 1990 else 1991 { 1992 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart1); 1993 } //end else 1994 dist1 = VectorDistance(bestend1, v3); 1995 dist2 = VectorDistance(bestend2, v3); 1996 if (dist1 > dist2) 1997 { 1998 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend2); 1999 } //end if 2000 else 2001 { 2002 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend1); 2003 } //end else 2004 } //end if 2005 else if (dist < bestdist) 2006 { 2007 bestdist = dist; 2008 VectorCopy(p3, beststart1); 2009 VectorCopy(p3, beststart2); 2010 VectorCopy(v3, bestend1); 2011 VectorCopy(v3, bestend2); 2012 } //end if 2013 founddist = qtrue; 2014 } //end else if 2015 if (VectorBetweenVectors(p4, v1, v2)) 2016 { 2017 dist = VectorDistance(v4, p4); 2018 if (dist > bestdist - 0.5 && dist < bestdist + 0.5) 2019 { 2020 dist1 = VectorDistance(beststart1, p4); 2021 dist2 = VectorDistance(beststart2, p4); 2022 if (dist1 > dist2) 2023 { 2024 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart2); 2025 } //end if 2026 else 2027 { 2028 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart1); 2029 } //end else 2030 dist1 = VectorDistance(bestend1, v4); 2031 dist2 = VectorDistance(bestend2, v4); 2032 if (dist1 > dist2) 2033 { 2034 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend2); 2035 } //end if 2036 else 2037 { 2038 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend1); 2039 } //end else 2040 } //end if 2041 else if (dist < bestdist) 2042 { 2043 bestdist = dist; 2044 VectorCopy(p4, beststart1); 2045 VectorCopy(p4, beststart2); 2046 VectorCopy(v4, bestend1); 2047 VectorCopy(v4, bestend2); 2048 } //end if 2049 founddist = qtrue; 2050 } //end else if 2051 //if no shortest distance was found the shortest distance 2052 //is between one of the vertexes of edge1 and one of edge2 2053 if (!founddist) 2054 { 2055 dist = VectorDistance(v1, v3); 2056 if (dist < bestdist) 2057 { 2058 bestdist = dist; 2059 VectorCopy(v1, beststart1); 2060 VectorCopy(v1, beststart2); 2061 VectorCopy(v3, bestend1); 2062 VectorCopy(v3, bestend2); 2063 } //end if 2064 dist = VectorDistance(v1, v4); 2065 if (dist < bestdist) 2066 { 2067 bestdist = dist; 2068 VectorCopy(v1, beststart1); 2069 VectorCopy(v1, beststart2); 2070 VectorCopy(v4, bestend1); 2071 VectorCopy(v4, bestend2); 2072 } //end if 2073 dist = VectorDistance(v2, v3); 2074 if (dist < bestdist) 2075 { 2076 bestdist = dist; 2077 VectorCopy(v2, beststart1); 2078 VectorCopy(v2, beststart2); 2079 VectorCopy(v3, bestend1); 2080 VectorCopy(v3, bestend2); 2081 } //end if 2082 dist = VectorDistance(v2, v4); 2083 if (dist < bestdist) 2084 { 2085 bestdist = dist; 2086 VectorCopy(v2, beststart1); 2087 VectorCopy(v2, beststart2); 2088 VectorCopy(v4, bestend1); 2089 VectorCopy(v4, bestend2); 2090 } //end if 2091 } //end if 2092 return bestdist; 2093 } //end of the function AAS_ClosestEdgePoints 2094 //=========================================================================== 2095 // creates possible jump reachabilities between the areas 2096 // 2097 // The two closest points on the ground of the areas are calculated 2098 // One of the points will be on an edge of a ground face of area1 and 2099 // one on an edge of a ground face of area2. 2100 // If there is a range of closest points the point in the middle of this range 2101 // is selected. 2102 // Between these two points there must be one or more gaps. 2103 // If the gaps exist a potential jump is predicted. 2104 // 2105 // Parameter: - 2106 // Returns: - 2107 // Changes Globals: - 2108 //=========================================================================== 2109 int AAS_Reachability_Jump(int area1num, int area2num) 2110 { 2111 int i, j, k, l, face1num, face2num, edge1num, edge2num, traveltype; 2112 int stopevent, areas[10], numareas; 2113 float phys_jumpvel, maxjumpdistance, maxjumpheight, height, bestdist, speed; 2114 vec_t *v1, *v2, *v3, *v4; 2115 vec3_t beststart, beststart2, bestend, bestend2; 2116 vec3_t teststart, testend, dir, velocity, cmdmove, up = {0, 0, 1}, sidewards; 2117 aas_area_t *area1, *area2; 2118 aas_face_t *face1, *face2; 2119 aas_edge_t *edge1, *edge2; 2120 aas_plane_t *plane1, *plane2, *plane; 2121 aas_trace_t trace; 2122 aas_clientmove_t move; 2123 aas_lreachability_t *lreach; 2124 2125 if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse; 2126 //cannot jump from or to a crouch area 2127 if (AAS_AreaCrouch(area1num) || AAS_AreaCrouch(area2num)) return qfalse; 2128 // 2129 area1 = &aasworld.areas[area1num]; 2130 area2 = &aasworld.areas[area2num]; 2131 // 2132 phys_jumpvel = aassettings.phys_jumpvel; 2133 //maximum distance a player can jump 2134 maxjumpdistance = 2 * AAS_MaxJumpDistance(phys_jumpvel); 2135 //maximum height a player can jump with the given initial z velocity 2136 maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel); 2137 2138 //if the areas are not near anough in the x-y direction 2139 for (i = 0; i < 2; i++) 2140 { 2141 if (area1->mins[i] > area2->maxs[i] + maxjumpdistance) return qfalse; 2142 if (area1->maxs[i] < area2->mins[i] - maxjumpdistance) return qfalse; 2143 } //end for 2144 //if area2 is way to high to jump up to 2145 if (area2->mins[2] > area1->maxs[2] + maxjumpheight) return qfalse; 2146 // 2147 bestdist = 999999; 2148 // 2149 for (i = 0; i < area1->numfaces; i++) 2150 { 2151 face1num = aasworld.faceindex[area1->firstface + i]; 2152 face1 = &aasworld.faces[abs(face1num)]; 2153 //if not a ground face 2154 if (!(face1->faceflags & FACE_GROUND)) continue; 2155 // 2156 for (j = 0; j < area2->numfaces; j++) 2157 { 2158 face2num = aasworld.faceindex[area2->firstface + j]; 2159 face2 = &aasworld.faces[abs(face2num)]; 2160 //if not a ground face 2161 if (!(face2->faceflags & FACE_GROUND)) continue; 2162 // 2163 for (k = 0; k < face1->numedges; k++) 2164 { 2165 edge1num = abs(aasworld.edgeindex[face1->firstedge + k]); 2166 edge1 = &aasworld.edges[edge1num]; 2167 for (l = 0; l < face2->numedges; l++) 2168 { 2169 edge2num = abs(aasworld.edgeindex[face2->firstedge + l]); 2170 edge2 = &aasworld.edges[edge2num]; 2171 //calculate the minimum distance between the two edges 2172 v1 = aasworld.vertexes[edge1->v[0]]; 2173 v2 = aasworld.vertexes[edge1->v[1]]; 2174 v3 = aasworld.vertexes[edge2->v[0]]; 2175 v4 = aasworld.vertexes[edge2->v[1]]; 2176 //get the ground planes 2177 plane1 = &aasworld.planes[face1->planenum]; 2178 plane2 = &aasworld.planes[face2->planenum]; 2179 // 2180 bestdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, plane1, plane2, 2181 beststart, bestend, 2182 beststart2, bestend2, bestdist); 2183 } //end for 2184 } //end for 2185 } //end for 2186 } //end for 2187 VectorMiddle(beststart, beststart2, beststart); 2188 VectorMiddle(bestend, bestend2, bestend); 2189 if (bestdist > 4 && bestdist < maxjumpdistance) 2190 { 2191 // Log_Write("shortest distance between %d and %d is %f\r\n", area1num, area2num, bestdist); 2192 // if very close and almost no height difference then the bot can walk 2193 if (bestdist <= 48 && fabs(beststart[2] - bestend[2]) < 8) 2194 { 2195 speed = 400; 2196 traveltype = TRAVEL_WALKOFFLEDGE; 2197 } //end if 2198 else if (AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) 2199 { 2200 //FIXME: why multiply with 1.2??? 2201 speed *= 1.2f; 2202 traveltype = TRAVEL_WALKOFFLEDGE; 2203 } //end else if 2204 else 2205 { 2206 //get the horizontal speed for the jump, if it isn't possible to calculate this 2207 //speed (the jump is not possible) then there's no jump reachability created 2208 if (!AAS_HorizontalVelocityForJump(phys_jumpvel, beststart, bestend, &speed)) 2209 return qfalse; 2210 speed *= 1.05f; 2211 traveltype = TRAVEL_JUMP; 2212 // 2213 //NOTE: test if the horizontal distance isn't too small 2214 VectorSubtract(bestend, beststart, dir); 2215 dir[2] = 0; 2216 if (VectorLength(dir) < 10) 2217 return qfalse; 2218 } //end if 2219 // 2220 VectorSubtract(bestend, beststart, dir); 2221 VectorNormalize(dir); 2222 VectorMA(beststart, 1, dir, teststart); 2223 // 2224 VectorCopy(teststart, testend); 2225 testend[2] -= 100; 2226 trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1); 2227 // 2228 if (trace.startsolid) 2229 return qfalse; 2230 if (trace.fraction < 1) 2231 { 2232 plane = &aasworld.planes[trace.planenum]; 2233 // if the bot can stand on the surface 2234 if (DotProduct(plane->normal, up) >= 0.7) 2235 { 2236 // if no lava or slime below 2237 if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) 2238 { 2239 if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier) 2240 return qfalse; 2241 } //end if 2242 } //end if 2243 } //end if 2244 // 2245 VectorMA(bestend, -1, dir, teststart); 2246 // 2247 VectorCopy(teststart, testend); 2248 testend[2] -= 100; 2249 trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1); 2250 // 2251 if (trace.startsolid) 2252 return qfalse; 2253 if (trace.fraction < 1) 2254 { 2255 plane = &aasworld.planes[trace.planenum]; 2256 // if the bot can stand on the surface 2257 if (DotProduct(plane->normal, up) >= 0.7) 2258 { 2259 // if no lava or slime below 2260 if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) 2261 { 2262 if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier) 2263 return qfalse; 2264 } //end if 2265 } //end if 2266 } //end if 2267 // 2268 // get command movement 2269 VectorClear(cmdmove); 2270 if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) 2271 cmdmove[2] = aassettings.phys_jumpvel; 2272 else 2273 cmdmove[2] = 0; 2274 // 2275 VectorSubtract(bestend, beststart, dir); 2276 dir[2] = 0; 2277 VectorNormalize(dir); 2278 CrossProduct(dir, up, sidewards); 2279 // 2280 stopevent = SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE; 2281 if (!AAS_AreaClusterPortal(area1num) && !AAS_AreaClusterPortal(area2num)) 2282 stopevent |= SE_TOUCHCLUSTERPORTAL; 2283 // 2284 for (i = 0; i < 3; i++) 2285 { 2286 // 2287 if (i == 1) 2288 VectorAdd(testend, sidewards, testend); 2289 else if (i == 2) 2290 VectorSubtract(bestend, sidewards, testend); 2291 else 2292 VectorCopy(bestend, testend); 2293 VectorSubtract(testend, beststart, dir); 2294 dir[2] = 0; 2295 VectorNormalize(dir); 2296 VectorScale(dir, speed, velocity); 2297 // 2298 AAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue, 2299 velocity, cmdmove, 3, 30, 0.1f, 2300 stopevent, 0, qfalse); 2301 // if prediction time wasn't enough to fully predict the movement 2302 if (move.frames >= 30) 2303 return qfalse; 2304 // don't enter slime or lava and don't fall from too high 2305 if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) 2306 return qfalse; 2307 // never jump or fall through a cluster portal 2308 if (move.stopevent & SE_TOUCHCLUSTERPORTAL) 2309 return qfalse; 2310 //the end position should be in area2, also test a little bit back 2311 //because the predicted jump could have rushed through the area 2312 VectorMA(move.endpos, -64, dir, teststart); 2313 teststart[2] += 1; 2314 numareas = AAS_TraceAreas(move.endpos, teststart, areas, NULL, sizeof(areas) / sizeof(int)); 2315 for (j = 0; j < numareas; j++) 2316 { 2317 if (areas[j] == area2num) 2318 break; 2319 } //end for 2320 if (j < numareas) 2321 break; 2322 } 2323 if (i >= 3) 2324 return qfalse; 2325 // 2326 #ifdef REACH_DEBUG 2327 //create the reachability 2328 Log_Write("jump reachability between %d and %d\r\n", area1num, area2num); 2329 #endif //REACH_DEBUG 2330 //create a new reachability link 2331 lreach = AAS_AllocReachability(); 2332 if (!lreach) return qfalse; 2333 lreach->areanum = area2num; 2334 lreach->facenum = 0; 2335 lreach->edgenum = 0; 2336 VectorCopy(beststart, lreach->start); 2337 VectorCopy(bestend, lreach->end); 2338 lreach->traveltype = traveltype; 2339 2340 VectorSubtract(bestend, beststart, dir); 2341 height = dir[2]; 2342 dir[2] = 0; 2343 if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir)) 2344 { 2345 lreach->traveltime = aassettings.rs_startwalkoffledge + height * 50 / aassettings.phys_gravity; 2346 } 2347 else 2348 { 2349 lreach->traveltime = aassettings.rs_startjump + VectorDistance(bestend, beststart) * 240 / aassettings.phys_maxwalkvelocity; 2350 } //end if 2351 // 2352 if (!AAS_AreaJumpPad(area2num)) 2353 { 2354 if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta5) 2355 { 2356 lreach->traveltime += aassettings.rs_falldamage5; 2357 } //end if 2358 else if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta10) 2359 { 2360 lreach->traveltime += aassettings.rs_falldamage10; 2361 } //end if 2362 } //end if 2363 lreach->next = areareachability[area1num]; 2364 areareachability[area1num] = lreach; 2365 // 2366 if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) 2367 reach_jump++; 2368 else 2369 reach_walkoffledge++; 2370 } //end if 2371 return qfalse; 2372 } //end of the function AAS_Reachability_Jump 2373 //=========================================================================== 2374 // create a possible ladder reachability from area1 to area2 2375 // 2376 // Parameter: - 2377 // Returns: - 2378 // Changes Globals: - 2379 //=========================================================================== 2380 int AAS_Reachability_Ladder(int area1num, int area2num) 2381 { 2382 int i, j, k, l, edge1num, edge2num, sharededgenum, lowestedgenum; 2383 int face1num, face2num, ladderface1num, ladderface2num; 2384 int ladderface1vertical, ladderface2vertical, firstv; 2385 float face1area, face2area, bestface1area, bestface2area; 2386 float phys_jumpvel, maxjumpheight; 2387 vec3_t area1point, area2point, v1, v2, up = {0, 0, 1}; 2388 vec3_t mid, lowestpoint, start, end, sharededgevec, dir; 2389 aas_area_t *area1, *area2; 2390 aas_face_t *face1, *face2, *ladderface1, *ladderface2; 2391 aas_plane_t *plane1, *plane2; 2392 aas_edge_t *sharededge, *edge1; 2393 aas_lreachability_t *lreach; 2394 aas_trace_t trace; 2395 2396 if (!AAS_AreaLadder(area1num) || !AAS_AreaLadder(area2num)) return qfalse; 2397 // 2398 phys_jumpvel = aassettings.phys_jumpvel; 2399 //maximum height a player can jump with the given initial z velocity 2400 maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel); 2401 2402 area1 = &aasworld.areas[area1num]; 2403 area2 = &aasworld.areas[area2num]; 2404 // 2405 ladderface1 = NULL; 2406 ladderface2 = NULL; 2407 ladderface1num = 0; //make compiler happy 2408 ladderface2num = 0; //make compiler happy 2409 bestface1area = -9999; 2410 bestface2area = -9999; 2411 sharededgenum = 0; //make compiler happy 2412 lowestedgenum = 0; //make compiler happy 2413 // 2414 for (i = 0; i < area1->numfaces; i++) 2415 { 2416 face1num = aasworld.faceindex[area1->firstface + i]; 2417 face1 = &aasworld.faces[abs(face1num)]; 2418 //if not a ladder face 2419 if (!(face1->faceflags & FACE_LADDER)) continue; 2420 // 2421 for (j = 0; j < area2->numfaces; j++) 2422 { 2423 face2num = aasworld.faceindex[area2->firstface + j]; 2424 face2 = &aasworld.faces[abs(face2num)]; 2425 //if not a ladder face 2426 if (!(face2->faceflags & FACE_LADDER)) continue; 2427 //check if the faces share an edge 2428 for (k = 0; k < face1->numedges; k++) 2429 { 2430 edge1num = aasworld.edgeindex[face1->firstedge + k]; 2431 for (l = 0; l < face2->numedges; l++) 2432 { 2433 edge2num = aasworld.edgeindex[face2->firstedge + l]; 2434 if (abs(edge1num) == abs(edge2num)) 2435 { 2436 //get the face with the largest area 2437 face1area = AAS_FaceArea(face1); 2438 face2area = AAS_FaceArea(face2); 2439 if (face1area > bestface1area && face2area > bestface2area) 2440 { 2441 bestface1area = face1area; 2442 bestface2area = face2area; 2443 ladderface1 = face1; 2444 ladderface2 = face2; 2445 ladderface1num = face1num; 2446 ladderface2num = face2num; 2447 sharededgenum = edge1num; 2448 } //end if 2449 break; 2450 } //end if 2451 } //end for 2452 if (l != face2->numedges) break; 2453 } //end for 2454 } //end for 2455 } //end for 2456 // 2457 if (ladderface1 && ladderface2) 2458 { 2459 //get the middle of the shared edge 2460 sharededge = &aasworld.edges[abs(sharededgenum)]; 2461 firstv = sharededgenum < 0; 2462 // 2463 VectorCopy(aasworld.vertexes[sharededge->v[firstv]], v1); 2464 VectorCopy(aasworld.vertexes[sharededge->v[!firstv]], v2); 2465 VectorAdd(v1, v2, area1point); 2466 VectorScale(area1point, 0.5, area1point); 2467 VectorCopy(area1point, area2point); 2468 // 2469 //if the face plane in area 1 is pretty much vertical 2470 plane1 = &aasworld.planes[ladderface1->planenum ^ (ladderface1num < 0)]; 2471 plane2 = &aasworld.planes[ladderface2->planenum ^ (ladderface2num < 0)]; 2472 // 2473 //get the points really into the areas 2474 VectorSubtract(v2, v1, sharededgevec); 2475 CrossProduct(plane1->normal, sharededgevec, dir); 2476 VectorNormalize(dir); 2477 //NOTE: 32 because that's larger than 16 (bot bbox x,y) 2478 VectorMA(area1point, -32, dir, area1point); 2479 VectorMA(area2point, 32, dir, area2point); 2480 // 2481 ladderface1vertical = abs(DotProduct(plane1->normal, up)) < 0.1; 2482 ladderface2vertical = abs(DotProduct(plane2->normal, up)) < 0.1; 2483 //there's only reachability between vertical ladder faces 2484 if (!ladderface1vertical && !ladderface2vertical) return qfalse; 2485 //if both vertical ladder faces 2486 if (ladderface1vertical && ladderface2vertical 2487 //and the ladder faces do not make a sharp corner 2488 && DotProduct(plane1->normal, plane2->normal) > 0.7 2489 //and the shared edge is not too vertical 2490 && abs(DotProduct(sharededgevec, up)) < 0.7) 2491 { 2492 //create a new reachability link 2493 lreach = AAS_AllocReachability(); 2494 if (!lreach) return qfalse; 2495 lreach->areanum = area2num; 2496 lreach->facenum = ladderface1num; 2497 lreach->edgenum = abs(sharededgenum); 2498 VectorCopy(area1point, lreach->start); 2499 //VectorCopy(area2point, lreach->end); 2500 VectorMA(area2point, -3, plane1->normal, lreach->end); 2501 lreach->traveltype = TRAVEL_LADDER; 2502 lreach->traveltime = 10; 2503 lreach->next = areareachability[area1num]; 2504 areareachability[area1num] = lreach; 2505 // 2506 reach_ladder++; 2507 //create a new reachability link 2508 lreach = AAS_AllocReachability(); 2509 if (!lreach) return qfalse; 2510 lreach->areanum = area1num; 2511 lreach->facenum = ladderface2num; 2512 lreach->edgenum = abs(sharededgenum); 2513 VectorCopy(area2point, lreach->start); 2514 //VectorCopy(area1point, lreach->end); 2515 VectorMA(area1point, -3, plane1->normal, lreach->end); 2516 lreach->traveltype = TRAVEL_LADDER; 2517 lreach->traveltime = 10; 2518 lreach->next = areareachability[area2num]; 2519 areareachability[area2num] = lreach; 2520 // 2521 reach_ladder++; 2522 // 2523 return qtrue; 2524 } //end if 2525 //if the second ladder face is also a ground face 2526 //create ladder end (just ladder) reachability and 2527 //walk off a ladder (ledge) reachability 2528 if (ladderface1vertical && (ladderface2->faceflags & FACE_GROUND)) 2529 { 2530 //create a new reachability link 2531 lreach = AAS_AllocReachability(); 2532 if (!lreach) return qfalse; 2533 lreach->areanum = area2num; 2534 lreach->facenum = ladderface1num; 2535 lreach->edgenum = abs(sharededgenum); 2536 VectorCopy(area1point, lreach->start); 2537 VectorCopy(area2point, lreach->end); 2538 lreach->end[2] += 16; 2539 VectorMA(lreach->end, -15, plane1->normal, lreach->end); 2540 lreach->traveltype = TRAVEL_LADDER; 2541 lreach->traveltime = 10; 2542 lreach->next = areareachability[area1num]; 2543 areareachability[area1num] = lreach; 2544 // 2545 reach_ladder++; 2546 //create a new reachability link 2547 lreach = AAS_AllocReachability(); 2548 if (!lreach) return qfalse; 2549 lreach->areanum = area1num; 2550 lreach->facenum = ladderface2num; 2551 lreach->edgenum = abs(sharededgenum); 2552 VectorCopy(area2point, lreach->start); 2553 VectorCopy(area1point, lreach->end); 2554 lreach->traveltype = TRAVEL_WALKOFFLEDGE; 2555 lreach->traveltime = 10; 2556 lreach->next = areareachability[area2num]; 2557 areareachability[area2num] = lreach; 2558 // 2559 reach_walkoffledge++; 2560 // 2561 return qtrue; 2562 } //end if 2563 // 2564 if (ladderface1vertical) 2565 { 2566 //find lowest edge of the ladder face 2567 lowestpoint[2] = 99999; 2568 for (i = 0; i < ladderface1->numedges; i++) 2569 { 2570 edge1num = abs(aasworld.edgeindex[ladderface1->firstedge + i]); 2571 edge1 = &aasworld.edges[edge1num]; 2572 // 2573 VectorCopy(aasworld.vertexes[edge1->v[0]], v1); 2574 VectorCopy(aasworld.vertexes[edge1->v[1]], v2); 2575 // 2576 VectorAdd(v1, v2, mid); 2577 VectorScale(mid, 0.5, mid); 2578 // 2579 if (mid[2] < lowestpoint[2]) 2580 { 2581 VectorCopy(mid, lowestpoint); 2582 lowestedgenum = edge1num; 2583 } //end if 2584 } //end for 2585 // 2586 plane1 = &aasworld.planes[ladderface1->planenum]; 2587 //trace down in the middle of this edge 2588 VectorMA(lowestpoint, 5, plane1->normal, start); 2589 VectorCopy(start, end); 2590 start[2] += 5; 2591 end[2] -= 100; 2592 //trace without entity collision 2593 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); 2594 // 2595 // 2596 #ifdef REACH_DEBUG 2597 if (trace.startsolid) 2598 { 2599 Log_Write("trace from area %d started in solid\r\n", area1num); 2600 } //end if 2601 #endif //REACH_DEBUG 2602 // 2603 trace.endpos[2] += 1; 2604 area2num = AAS_PointAreaNum(trace.endpos); 2605 // 2606 area2 = &aasworld.areas[area2num]; 2607 for (i = 0; i < area2->numfaces; i++) 2608 { 2609 face2num = aasworld.faceindex[area2->firstface + i]; 2610 face2 = &aasworld.faces[abs(face2num)]; 2611 // 2612 if (face2->faceflags & FACE_LADDER) 2613 { 2614 plane2 = &aasworld.planes[face2->planenum]; 2615 if (abs(DotProduct(plane2->normal, up)) < 0.1) break; 2616 } //end if 2617 } //end for 2618 //if from another area without vertical ladder faces 2619 if (i >= area2->numfaces && area2num != area1num && 2620 //the reachabilities shouldn't exist already 2621 !AAS_ReachabilityExists(area1num, area2num) && 2622 !AAS_ReachabilityExists(area2num, area1num)) 2623 { 2624 //if the height is jumpable 2625 if (start[2] - trace.endpos[2] < maxjumpheight) 2626 { 2627 //create a new reachability link 2628 lreach = AAS_AllocReachability(); 2629 if (!lreach) return qfalse; 2630 lreach->areanum = area2num; 2631 lreach->facenum = ladderface1num; 2632 lreach->edgenum = lowestedgenum; 2633 VectorCopy(lowestpoint, lreach->start); 2634 VectorCopy(trace.endpos, lreach->end); 2635 lreach->traveltype = TRAVEL_LADDER; 2636 lreach->traveltime = 10; 2637 lreach->next = areareachability[area1num]; 2638 areareachability[area1num] = lreach; 2639 // 2640 reach_ladder++; 2641 //create a new reachability link 2642 lreach = AAS_AllocReachability(); 2643 if (!lreach) return qfalse; 2644 lreach->areanum = area1num; 2645 lreach->facenum = ladderface1num; 2646 lreach->edgenum = lowestedgenum; 2647 VectorCopy(trace.endpos, lreach->start); 2648 //get the end point a little bit into the ladder 2649 VectorMA(lowestpoint, -5, plane1->normal, lreach->end); 2650 //get the end point a little higher 2651 lreach->end[2] += 10; 2652 lreach->traveltype = TRAVEL_JUMP; 2653 lreach->traveltime = 10; 2654 lreach->next = areareachability[area2num]; 2655 areareachability[area2num] = lreach; 2656 // 2657 reach_jump++; 2658 // 2659 return qtrue; 2660 #ifdef REACH_DEBUG 2661 Log_Write("jump up to ladder reach between %d and %d\r\n", area2num, area1num); 2662 #endif //REACH_DEBUG 2663 } //end if 2664 #ifdef REACH_DEBUG 2665 else Log_Write("jump too high between area %d and %d\r\n", area2num, area1num); 2666 #endif //REACH_DEBUG 2667 } //end if 2668 /*//if slime or lava below the ladder 2669 //try jump reachability from far towards the ladder 2670 if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME 2671 | AREACONTENTS_LAVA)) 2672 { 2673 for (i = 20; i <= 120; i += 20) 2674 { 2675 //trace down in the middle of this edge 2676 VectorMA(lowestpoint, i, plane1->normal, start); 2677 VectorCopy(start, end); 2678 start[2] += 5; 2679 end[2] -= 100; 2680 //trace without entity collision 2681 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); 2682 // 2683 if (trace.startsolid) break; 2684 trace.endpos[2] += 1; 2685 area2num = AAS_PointAreaNum(trace.endpos); 2686 if (area2num == area1num) continue; 2687 // 2688 if (start[2] - trace.endpos[2] > maxjumpheight) continue; 2689 if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME 2690 | AREACONTENTS_LAVA)) continue; 2691 // 2692 //create a new reachability link 2693 lreach = AAS_AllocReachability(); 2694 if (!lreach) return qfalse; 2695 lreach->areanum = area1num; 2696 lreach->facenum = ladderface1num; 2697 lreach->edgenum = lowestedgenum; 2698 VectorCopy(trace.endpos, lreach->start); 2699 VectorCopy(lowestpoint, lreach->end); 2700 lreach->end[2] += 5; 2701 lreach->traveltype = TRAVEL_JUMP; 2702 lreach->traveltime = 10; 2703 lreach->next = areareachability[area2num]; 2704 areareachability[area2num] = lreach; 2705 // 2706 reach_jump++; 2707 // 2708 Log_Write("jump far to ladder reach between %d and %d\r\n", area2num, area1num); 2709 // 2710 break; 2711 } //end for 2712 } //end if*/ 2713 } //end if 2714 } //end if 2715 return qfalse; 2716 } //end of the function AAS_Reachability_Ladder 2717 //=========================================================================== 2718 // 2719 // Parameter: - 2720 // Returns: - 2721 // Changes Globals: - 2722 //=========================================================================== 2723 int AAS_TravelFlagsForTeam(int ent) 2724 { 2725 int notteam; 2726 2727 if (!AAS_IntForBSPEpairKey(ent, "bot_notteam", ¬team)) 2728 return 0; 2729 if (notteam == 1) 2730 return TRAVELFLAG_NOTTEAM1; 2731 if (notteam == 2) 2732 return TRAVELFLAG_NOTTEAM2; 2733 return 0; 2734 } //end of the function AAS_TravelFlagsForTeam 2735 //=========================================================================== 2736 // create possible teleporter reachabilities 2737 // this is very game dependent.... :( 2738 // 2739 // classname = trigger_multiple or trigger_teleport 2740 // target = "t1" 2741 // 2742 // classname = target_teleporter 2743 // targetname = "t1" 2744 // target = "t2" 2745 // 2746 // classname = misc_teleporter_dest 2747 // targetname = "t2" 2748 // 2749 // Parameter: - 2750 // Returns: - 2751 // Changes Globals: - 2752 //=========================================================================== 2753 void AAS_Reachability_Teleport(void) 2754 { 2755 int area1num, area2num; 2756 char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; 2757 char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; 2758 int ent, dest; 2759 float angle; 2760 vec3_t origin, destorigin, mins, maxs, end, angles; 2761 vec3_t mid, velocity, cmdmove; 2762 aas_lreachability_t *lreach; 2763 aas_clientmove_t move; 2764 aas_trace_t trace; 2765 aas_link_t *areas, *link; 2766 2767 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) 2768 { 2769 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; 2770 if (!strcmp(classname, "trigger_multiple")) 2771 { 2772 AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); 2773 //#ifdef REACH_DEBUG 2774 botimport.Print(PRT_MESSAGE, "trigger_multiple model = \"%s\"\n", model); 2775 //#endif REACH_DEBUG 2776 VectorClear(angles); 2777 AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin); 2778 // 2779 if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) 2780 { 2781 botimport.Print(PRT_ERROR, "trigger_multiple at %1.0f %1.0f %1.0f without target\n", 2782 origin[0], origin[1], origin[2]); 2783 continue; 2784 } //end if 2785 for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest)) 2786 { 2787 if (!AAS_ValueForBSPEpairKey(dest, "classname", classname, MAX_EPAIRKEY)) continue; 2788 if (!strcmp(classname, "target_teleporter")) 2789 { 2790 if (!AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) continue; 2791 if (!strcmp(targetname, target)) 2792 { 2793 break; 2794 } //end if 2795 } //end if 2796 } //end for 2797 if (!dest) 2798 { 2799 continue; 2800 } //end if 2801 if (!AAS_ValueForBSPEpairKey(dest, "target", target, MAX_EPAIRKEY)) 2802 { 2803 botimport.Print(PRT_ERROR, "target_teleporter without target\n"); 2804 continue; 2805 } //end if 2806 } //end else 2807 else if (!strcmp(classname, "trigger_teleport")) 2808 { 2809 AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); 2810 //#ifdef REACH_DEBUG 2811 botimport.Print(PRT_MESSAGE, "trigger_teleport model = \"%s\"\n", model); 2812 //#endif REACH_DEBUG 2813 VectorClear(angles); 2814 AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin); 2815 // 2816 if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) 2817 { 2818 botimport.Print(PRT_ERROR, "trigger_teleport at %1.0f %1.0f %1.0f without target\n", 2819 origin[0], origin[1], origin[2]); 2820 continue; 2821 } //end if 2822 } //end if 2823 else 2824 { 2825 continue; 2826 } //end else 2827 // 2828 for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest)) 2829 { 2830 //classname should be misc_teleporter_dest 2831 //but I've also seen target_position and actually any 2832 //entity could be used... burp 2833 if (AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) 2834 { 2835 if (!strcmp(targetname, target)) 2836 { 2837 break; 2838 } //end if 2839 } //end if 2840 } //end for 2841 if (!dest) 2842 { 2843 botimport.Print(PRT_ERROR, "teleporter without misc_teleporter_dest (%s)\n", target); 2844 continue; 2845 } //end if 2846 if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) 2847 { 2848 botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); 2849 continue; 2850 } //end if 2851 // 2852 area2num = AAS_PointAreaNum(destorigin); 2853 //if not teleported into a teleporter or into a jumppad 2854 if (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num)) 2855 { 2856 VectorCopy(destorigin, end); 2857 end[2] -= 64; 2858 trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); 2859 if (trace.startsolid) 2860 { 2861 botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); 2862 continue; 2863 } //end if 2864 area2num = AAS_PointAreaNum(trace.endpos); 2865 // 2866 /* 2867 if (!AAS_AreaTeleporter(area2num) && 2868 !AAS_AreaJumpPad(area2num) && 2869 !AAS_AreaGrounded(area2num)) 2870 { 2871 VectorCopy(trace.endpos, destorigin); 2872 } 2873 else*/ 2874 { 2875 //predict where you'll end up 2876 AAS_FloatForBSPEpairKey(dest, "angle", &angle); 2877 if (angle) 2878 { 2879 VectorSet(angles, 0, angle, 0); 2880 AngleVectors(angles, velocity, NULL, NULL); 2881 VectorScale(velocity, 400, velocity); 2882 } //end if 2883 else 2884 { 2885 VectorClear(velocity); 2886 } //end else 2887 VectorClear(cmdmove); 2888 AAS_PredictClientMovement(&move, -1, destorigin, PRESENCE_NORMAL, qfalse, 2889 velocity, cmdmove, 0, 30, 0.1f, 2890 SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| 2891 SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, qfalse); //qtrue); 2892 area2num = AAS_PointAreaNum(move.endpos); 2893 if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) 2894 { 2895 botimport.Print(PRT_WARNING, "teleported into slime or lava at dest %s\n", target); 2896 } //end if 2897 VectorCopy(move.endpos, destorigin); 2898 } //end else 2899 } //end if 2900 // 2901 //botimport.Print(PRT_MESSAGE, "teleporter brush origin at %f %f %f\n", origin[0], origin[1], origin[2]); 2902 //botimport.Print(PRT_MESSAGE, "teleporter brush mins = %f %f %f\n", mins[0], mins[1], mins[2]); 2903 //botimport.Print(PRT_MESSAGE, "teleporter brush maxs = %f %f %f\n", maxs[0], maxs[1], maxs[2]); 2904 VectorAdd(origin, mins, mins); 2905 VectorAdd(origin, maxs, maxs); 2906 // 2907 VectorAdd(mins, maxs, mid); 2908 VectorScale(mid, 0.5, mid); 2909 //link an invalid (-1) entity 2910 areas = AAS_LinkEntityClientBBox(mins, maxs, -1, PRESENCE_CROUCH); 2911 if (!areas) botimport.Print(PRT_MESSAGE, "trigger_multiple not in any area\n"); 2912 // 2913 for (link = areas; link; link = link->next_area) 2914 { 2915 //if (!AAS_AreaGrounded(link->areanum)) continue; 2916 if (!AAS_AreaTeleporter(link->areanum)) continue; 2917 // 2918 area1num = link->areanum; 2919 //create a new reachability link 2920 lreach = AAS_AllocReachability(); 2921 if (!lreach) break; 2922 lreach->areanum = area2num; 2923 lreach->facenum = 0; 2924 lreach->edgenum = 0; 2925 VectorCopy(mid, lreach->start); 2926 VectorCopy(destorigin, lreach->end); 2927 lreach->traveltype = TRAVEL_TELEPORT; 2928 lreach->traveltype |= AAS_TravelFlagsForTeam(ent); 2929 lreach->traveltime = aassettings.rs_teleport; 2930 lreach->next = areareachability[area1num]; 2931 areareachability[area1num] = lreach; 2932 // 2933 reach_teleport++; 2934 } //end for 2935 //unlink the invalid entity 2936 AAS_UnlinkFromAreas(areas); 2937 } //end for 2938 } //end of the function AAS_Reachability_Teleport 2939 //=========================================================================== 2940 // create possible elevator (func_plat) reachabilities 2941 // this is very game dependent.... :( 2942 // 2943 // Parameter: - 2944 // Returns: - 2945 // Changes Globals: - 2946 //=========================================================================== 2947 void AAS_Reachability_Elevator(void) 2948 { 2949 int area1num, area2num, modelnum, i, j, k, l, n, p; 2950 float lip, height, speed; 2951 char model[MAX_EPAIRKEY], classname[MAX_EPAIRKEY]; 2952 int ent; 2953 vec3_t mins, maxs, origin, angles = {0, 0, 0}; 2954 vec3_t pos1, pos2, mids, platbottom, plattop; 2955 vec3_t bottomorg, toporg, start, end, dir; 2956 vec_t xvals[8], yvals[8], xvals_top[8], yvals_top[8]; 2957 aas_lreachability_t *lreach; 2958 aas_trace_t trace; 2959 2960 #ifdef REACH_DEBUG 2961 Log_Write("AAS_Reachability_Elevator\r\n"); 2962 #endif //REACH_DEBUG 2963 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) 2964 { 2965 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; 2966 if (!strcmp(classname, "func_plat")) 2967 { 2968 #ifdef REACH_DEBUG 2969 Log_Write("found func plat\r\n"); 2970 #endif //REACH_DEBUG 2971 if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) 2972 { 2973 botimport.Print(PRT_ERROR, "func_plat without model\n"); 2974 continue; 2975 } //end if 2976 //get the model number, and skip the leading * 2977 modelnum = atoi(model+1); 2978 if (modelnum <= 0) 2979 { 2980 botimport.Print(PRT_ERROR, "func_plat with invalid model number\n"); 2981 continue; 2982 } //end if 2983 //get the mins, maxs and origin of the model 2984 //NOTE: the origin is usually (0,0,0) and the mins and maxs 2985 // are the absolute mins and maxs 2986 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); 2987 // 2988 AAS_VectorForBSPEpairKey(ent, "origin", origin); 2989 //pos1 is the top position, pos2 is the bottom 2990 VectorCopy(origin, pos1); 2991 VectorCopy(origin, pos2); 2992 //get the lip of the plat 2993 AAS_FloatForBSPEpairKey(ent, "lip", &lip); 2994 if (!lip) lip = 8; 2995 //get the movement height of the plat 2996 AAS_FloatForBSPEpairKey(ent, "height", &height); 2997 if (!height) height = (maxs[2] - mins[2]) - lip; 2998 //get the speed of the plat 2999 AAS_FloatForBSPEpairKey(ent, "speed", &speed); 3000 if (!speed) speed = 200; 3001 //get bottom position below pos1 3002 pos2[2] -= height; 3003 // 3004 //get a point just above the plat in the bottom position 3005 VectorAdd(mins, maxs, mids); 3006 VectorMA(pos2, 0.5, mids, platbottom); 3007 platbottom[2] = maxs[2] - (pos1[2] - pos2[2]) + 2; 3008 //get a point just above the plat in the top position 3009 VectorAdd(mins, maxs, mids); 3010 VectorMA(pos2, 0.5, mids, plattop); 3011 plattop[2] = maxs[2] + 2; 3012 // 3013 /*if (!area1num) 3014 { 3015 Log_Write("no grounded area near plat bottom\r\n"); 3016 continue; 3017 } //end if*/ 3018 //get the mins and maxs a little larger 3019 for (i = 0; i < 3; i++) 3020 { 3021 mins[i] -= 1; 3022 maxs[i] += 1; 3023 } //end for 3024 // 3025 //botimport.Print(PRT_MESSAGE, "platbottom[2] = %1.1f plattop[2] = %1.1f\n", platbottom[2], plattop[2]); 3026 // 3027 VectorAdd(mins, maxs, mids); 3028 VectorScale(mids, 0.5, mids); 3029 // 3030 xvals[0] = mins[0]; xvals[1] = mids[0]; xvals[2] = maxs[0]; xvals[3] = mids[0]; 3031 yvals[0] = mids[1]; yvals[1] = maxs[1]; yvals[2] = mids[1]; yvals[3] = mins[1]; 3032 // 3033 xvals[4] = mins[0]; xvals[5] = maxs[0]; xvals[6] = maxs[0]; xvals[7] = mins[0]; 3034 yvals[4] = maxs[1]; yvals[5] = maxs[1]; yvals[6] = mins[1]; yvals[7] = mins[1]; 3035 //find adjacent areas around the bottom of the plat 3036 for (i = 0; i < 9; i++) 3037 { 3038 if (i < 8) //check at the sides of the plat 3039 { 3040 bottomorg[0] = origin[0] + xvals[i]; 3041 bottomorg[1] = origin[1] + yvals[i]; 3042 bottomorg[2] = platbottom[2] + 16; 3043 //get a grounded or swim area near the plat in the bottom position 3044 area1num = AAS_PointAreaNum(bottomorg); 3045 for (k = 0; k < 16; k++) 3046 { 3047 if (area1num) 3048 { 3049 if (AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) break; 3050 } //end if 3051 bottomorg[2] += 4; 3052 area1num = AAS_PointAreaNum(bottomorg); 3053 } //end if 3054 //if in solid 3055 if (k >= 16) 3056 { 3057 continue; 3058 } //end if 3059 } //end if 3060 else //at the middle of the plat 3061 { 3062 VectorCopy(plattop, bottomorg); 3063 bottomorg[2] += 24; 3064 area1num = AAS_PointAreaNum(bottomorg); 3065 if (!area1num) continue; 3066 VectorCopy(platbottom, bottomorg); 3067 bottomorg[2] += 24; 3068 } //end else 3069 //look at adjacent areas around the top of the plat 3070 //make larger steps to outside the plat everytime 3071 for (n = 0; n < 3; n++) 3072 { 3073 for (k = 0; k < 3; k++) 3074 { 3075 mins[k] -= 4; 3076 maxs[k] += 4; 3077 } //end for 3078 xvals_top[0] = mins[0]; xvals_top[1] = mids[0]; xvals_top[2] = maxs[0]; xvals_top[3] = mids[0]; 3079 yvals_top[0] = mids[1]; yvals_top[1] = maxs[1]; yvals_top[2] = mids[1]; yvals_top[3] = mins[1]; 3080 // 3081 xvals_top[4] = mins[0]; xvals_top[5] = maxs[0]; xvals_top[6] = maxs[0]; xvals_top[7] = mins[0]; 3082 yvals_top[4] = maxs[1]; yvals_top[5] = maxs[1]; yvals_top[6] = mins[1]; yvals_top[7] = mins[1]; 3083 // 3084 for (j = 0; j < 8; j++) 3085 { 3086 toporg[0] = origin[0] + xvals_top[j]; 3087 toporg[1] = origin[1] + yvals_top[j]; 3088 toporg[2] = plattop[2] + 16; 3089 //get a grounded or swim area near the plat in the top position 3090 area2num = AAS_PointAreaNum(toporg); 3091 for (l = 0; l < 16; l++) 3092 { 3093 if (area2num) 3094 { 3095 if (AAS_AreaGrounded(area2num) || AAS_AreaSwim(area2num)) 3096 { 3097 VectorCopy(plattop, start); 3098 start[2] += 32; 3099 VectorCopy(toporg, end); 3100 end[2] += 1; 3101 trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); 3102 if (trace.fraction >= 1) break; 3103 } //end if 3104 } //end if 3105 toporg[2] += 4; 3106 area2num = AAS_PointAreaNum(toporg); 3107 } //end if 3108 //if in solid 3109 if (l >= 16) continue; 3110 //never create a reachability in the same area 3111 if (area2num == area1num) continue; 3112 //if the area isn't grounded 3113 if (!AAS_AreaGrounded(area2num)) continue; 3114 //if there already exists reachability between the areas 3115 if (AAS_ReachabilityExists(area1num, area2num)) continue; 3116 //if the reachability start is within the elevator bounding box 3117 VectorSubtract(bottomorg, platbottom, dir); 3118 VectorNormalize(dir); 3119 dir[0] = bottomorg[0] + 24 * dir[0]; 3120 dir[1] = bottomorg[1] + 24 * dir[1]; 3121 dir[2] = bottomorg[2]; 3122 // 3123 for (p = 0; p < 3; p++) 3124 if (dir[p] < origin[p] + mins[p] || dir[p] > origin[p] + maxs[p]) break; 3125 if (p >= 3) continue; 3126 //create a new reachability link 3127 lreach = AAS_AllocReachability(); 3128 if (!lreach) continue; 3129 lreach->areanum = area2num; 3130 //the facenum is the model number 3131 lreach->facenum = modelnum; 3132 //the edgenum is the height 3133 lreach->edgenum = (int) height; 3134 // 3135 VectorCopy(dir, lreach->start); 3136 VectorCopy(toporg, lreach->end); 3137 lreach->traveltype = TRAVEL_ELEVATOR; 3138 lreach->traveltype |= AAS_TravelFlagsForTeam(ent); 3139 lreach->traveltime = aassettings.rs_startelevator + height * 100 / speed; 3140 lreach->next = areareachability[area1num]; 3141 areareachability[area1num] = lreach; 3142 //don't go any further to the outside 3143 n = 9999; 3144 // 3145 #ifdef REACH_DEBUG 3146 Log_Write("elevator reach from %d to %d\r\n", area1num, area2num); 3147 #endif //REACH_DEBUG 3148 // 3149 reach_elevator++; 3150 } //end for 3151 } //end for 3152 } //end for 3153 } //end if 3154 } //end for 3155 } //end of the function AAS_Reachability_Elevator 3156 //=========================================================================== 3157 // 3158 // Parameter: - 3159 // Returns: - 3160 // Changes Globals: - 3161 //=========================================================================== 3162 aas_lreachability_t *AAS_FindFaceReachabilities(vec3_t *facepoints, int numpoints, aas_plane_t *plane, int towardsface) 3163 { 3164 int i, j, k, l; 3165 int facenum, edgenum, bestfacenum; 3166 float *v1, *v2, *v3, *v4; 3167 float bestdist, speed, hordist, dist; 3168 vec3_t beststart, beststart2, bestend, bestend2, tmp, hordir, testpoint; 3169 aas_lreachability_t *lreach, *lreachabilities; 3170 aas_area_t *area; 3171 aas_face_t *face; 3172 aas_edge_t *edge; 3173 aas_plane_t *faceplane, *bestfaceplane; 3174 3175 // 3176 lreachabilities = NULL; 3177 bestfacenum = 0; 3178 bestfaceplane = NULL; 3179 // 3180 for (i = 1; i < aasworld.numareas; i++) 3181 { 3182 area = &aasworld.areas[i]; 3183 // get the shortest distance between one of the func_bob start edges and 3184 // one of the face edges of area1 3185 bestdist = 999999; 3186 for (j = 0; j < area->numfaces; j++) 3187 { 3188 facenum = aasworld.faceindex[area->firstface + j]; 3189 face = &aasworld.faces[abs(facenum)]; 3190 //if not a ground face 3191 if (!(face->faceflags & FACE_GROUND)) continue; 3192 //get the ground planes 3193 faceplane = &aasworld.planes[face->planenum]; 3194 // 3195 for (k = 0; k < face->numedges; k++) 3196 { 3197 edgenum = abs(aasworld.edgeindex[face->firstedge + k]); 3198 edge = &aasworld.edges[edgenum]; 3199 //calculate the minimum distance between the two edges 3200 v1 = aasworld.vertexes[edge->v[0]]; 3201 v2 = aasworld.vertexes[edge->v[1]]; 3202 // 3203 for (l = 0; l < numpoints; l++) 3204 { 3205 v3 = facepoints[l]; 3206 v4 = facepoints[(l+1) % numpoints]; 3207 dist = AAS_ClosestEdgePoints(v1, v2, v3, v4, faceplane, plane, 3208 beststart, bestend, 3209 beststart2, bestend2, bestdist); 3210 if (dist < bestdist) 3211 { 3212 bestfacenum = facenum; 3213 bestfaceplane = faceplane; 3214 bestdist = dist; 3215 } //end if 3216 } //end for 3217 } //end for 3218 } //end for 3219 // 3220 if (bestdist > 192) continue; 3221 // 3222 VectorMiddle(beststart, beststart2, beststart); 3223 VectorMiddle(bestend, bestend2, bestend); 3224 // 3225 if (!towardsface) 3226 { 3227 VectorCopy(beststart, tmp); 3228 VectorCopy(bestend, beststart); 3229 VectorCopy(tmp, bestend); 3230 } //end if 3231 // 3232 VectorSubtract(bestend, beststart, hordir); 3233 hordir[2] = 0; 3234 hordist = VectorLength(hordir); 3235 // 3236 if (hordist > 2 * AAS_MaxJumpDistance(aassettings.phys_jumpvel)) continue; 3237 //the end point should not be significantly higher than the start point 3238 if (bestend[2] - 32 > beststart[2]) continue; 3239 //don't fall down too far 3240 if (bestend[2] < beststart[2] - 128) continue; 3241 //the distance should not be too far 3242 if (hordist > 32) 3243 { 3244 //check for walk off ledge 3245 if (!AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) continue; 3246 } //end if 3247 // 3248 beststart[2] += 1; 3249 bestend[2] += 1; 3250 // 3251 if (towardsface) VectorCopy(bestend, testpoint); 3252 else VectorCopy(beststart, testpoint); 3253 testpoint[2] = 0; 3254 testpoint[2] = (bestfaceplane->dist - DotProduct(bestfaceplane->normal, testpoint)) / bestfaceplane->normal[2]; 3255 // 3256 if (!AAS_PointInsideFace(bestfacenum, testpoint, 0.1f)) 3257 { 3258 //if the faces are not overlapping then only go down 3259 if (bestend[2] - 16 > beststart[2]) continue; 3260 } //end if 3261 lreach = AAS_AllocReachability(); 3262 if (!lreach) return lreachabilities; 3263 lreach->areanum = i; 3264 lreach->facenum = 0; 3265 lreach->edgenum = 0; 3266 VectorCopy(beststart, lreach->start); 3267 VectorCopy(bestend, lreach->end); 3268 lreach->traveltype = 0; 3269 lreach->traveltime = 0; 3270 lreach->next = lreachabilities; 3271 lreachabilities = lreach; 3272 #ifndef BSPC 3273 if (towardsface) AAS_PermanentLine(lreach->start, lreach->end, 1); 3274 else AAS_PermanentLine(lreach->start, lreach->end, 2); 3275 #endif 3276 } //end for 3277 return lreachabilities; 3278 } //end of the function AAS_FindFaceReachabilities 3279 //=========================================================================== 3280 // 3281 // Parameter: - 3282 // Returns: - 3283 // Changes Globals: - 3284 //=========================================================================== 3285 void AAS_Reachability_FuncBobbing(void) 3286 { 3287 int ent, spawnflags, modelnum, axis; 3288 int i, numareas, areas[10]; 3289 char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; 3290 vec3_t origin, move_end, move_start, move_start_top, move_end_top; 3291 vec3_t mins, maxs, angles = {0, 0, 0}; 3292 vec3_t start_edgeverts[4], end_edgeverts[4], mid; 3293 vec3_t org, start, end, dir, points[10]; 3294 float height; 3295 aas_plane_t start_plane, end_plane; 3296 aas_lreachability_t *startreach, *endreach, *nextstartreach, *nextendreach, *lreach; 3297 aas_lreachability_t *firststartreach, *firstendreach; 3298 3299 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) 3300 { 3301 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; 3302 if (strcmp(classname, "func_bobbing")) continue; 3303 AAS_FloatForBSPEpairKey(ent, "height", &height); 3304 if (!height) height = 32; 3305 // 3306 if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) 3307 { 3308 botimport.Print(PRT_ERROR, "func_bobbing without model\n"); 3309 continue; 3310 } //end if 3311 //get the model number, and skip the leading * 3312 modelnum = atoi(model+1); 3313 if (modelnum <= 0) 3314 { 3315 botimport.Print(PRT_ERROR, "func_bobbing with invalid model number\n"); 3316 continue; 3317 } //end if 3318 //if the entity has an origin set then use it 3319 if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) 3320 VectorSet(origin, 0, 0, 0); 3321 // 3322 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); 3323 // 3324 VectorAdd(mins, origin, mins); 3325 VectorAdd(maxs, origin, maxs); 3326 // 3327 VectorAdd(mins, maxs, mid); 3328 VectorScale(mid, 0.5, mid); 3329 VectorCopy(mid, origin); 3330 // 3331 VectorCopy(origin, move_end); 3332 VectorCopy(origin, move_start); 3333 // 3334 AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); 3335 // set the axis of bobbing 3336 if (spawnflags & 1) axis = 0; 3337 else if (spawnflags & 2) axis = 1; 3338 else axis = 2; 3339 // 3340 move_start[axis] -= height; 3341 move_end[axis] += height; 3342 // 3343 Log_Write("funcbob model %d, start = {%1.1f, %1.1f, %1.1f} end = {%1.1f, %1.1f, %1.1f}\n", 3344 modelnum, move_start[0], move_start[1], move_start[2], move_end[0], move_end[1], move_end[2]); 3345 // 3346 #ifndef BSPC 3347 /* 3348 AAS_DrawPermanentCross(move_start, 4, 1); 3349 AAS_DrawPermanentCross(move_end, 4, 2); 3350 */ 3351 #endif 3352 // 3353 for (i = 0; i < 4; i++) 3354 { 3355 VectorCopy(move_start, start_edgeverts[i]); 3356 start_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z 3357 start_edgeverts[i][2] += 24; //+ player origin to ground dist 3358 } //end for 3359 start_edgeverts[0][0] += maxs[0] - mid[0]; 3360 start_edgeverts[0][1] += maxs[1] - mid[1]; 3361 start_edgeverts[1][0] += maxs[0] - mid[0]; 3362 start_edgeverts[1][1] += mins[1] - mid[1]; 3363 start_edgeverts[2][0] += mins[0] - mid[0]; 3364 start_edgeverts[2][1] += mins[1] - mid[1]; 3365 start_edgeverts[3][0] += mins[0] - mid[0]; 3366 start_edgeverts[3][1] += maxs[1] - mid[1]; 3367 // 3368 start_plane.dist = start_edgeverts[0][2]; 3369 VectorSet(start_plane.normal, 0, 0, 1); 3370 // 3371 for (i = 0; i < 4; i++) 3372 { 3373 VectorCopy(move_end, end_edgeverts[i]); 3374 end_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z 3375 end_edgeverts[i][2] += 24; //+ player origin to ground dist 3376 } //end for 3377 end_edgeverts[0][0] += maxs[0] - mid[0]; 3378 end_edgeverts[0][1] += maxs[1] - mid[1]; 3379 end_edgeverts[1][0] += maxs[0] - mid[0]; 3380 end_edgeverts[1][1] += mins[1] - mid[1]; 3381 end_edgeverts[2][0] += mins[0] - mid[0]; 3382 end_edgeverts[2][1] += mins[1] - mid[1]; 3383 end_edgeverts[3][0] += mins[0] - mid[0]; 3384 end_edgeverts[3][1] += maxs[1] - mid[1]; 3385 // 3386 end_plane.dist = end_edgeverts[0][2]; 3387 VectorSet(end_plane.normal, 0, 0, 1); 3388 // 3389 #ifndef BSPC 3390 #if 0 3391 for (i = 0; i < 4; i++) 3392 { 3393 AAS_PermanentLine(start_edgeverts[i], start_edgeverts[(i+1)%4], 1); 3394 AAS_PermanentLine(end_edgeverts[i], end_edgeverts[(i+1)%4], 1); 3395 } //end for 3396 #endif 3397 #endif 3398 VectorCopy(move_start, move_start_top); 3399 move_start_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z 3400 VectorCopy(move_end, move_end_top); 3401 move_end_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z 3402 // 3403 if (!AAS_PointAreaNum(move_start_top)) continue; 3404 if (!AAS_PointAreaNum(move_end_top)) continue; 3405 // 3406 for (i = 0; i < 2; i++) 3407 { 3408 firststartreach = firstendreach = NULL; 3409 // 3410 if (i == 0) 3411 { 3412 firststartreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qtrue); 3413 firstendreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qfalse); 3414 } //end if 3415 else 3416 { 3417 firststartreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qtrue); 3418 firstendreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qfalse); 3419 } //end else 3420 // 3421 //create reachabilities from start to end 3422 for (startreach = firststartreach; startreach; startreach = nextstartreach) 3423 { 3424 nextstartreach = startreach->next; 3425 // 3426 //trace = AAS_TraceClientBBox(startreach->start, move_start_top, PRESENCE_NORMAL, -1); 3427 //if (trace.fraction < 1) continue; 3428 // 3429 for (endreach = firstendreach; endreach; endreach = nextendreach) 3430 { 3431 nextendreach = endreach->next; 3432 // 3433 //trace = AAS_TraceClientBBox(endreach->end, move_end_top, PRESENCE_NORMAL, -1); 3434 //if (trace.fraction < 1) continue; 3435 // 3436 Log_Write("funcbob reach from area %d to %d\n", startreach->areanum, endreach->areanum); 3437 // 3438 // 3439 if (i == 0) VectorCopy(move_start_top, org); 3440 else VectorCopy(move_end_top, org); 3441 VectorSubtract(startreach->start, org, dir); 3442 dir[2] = 0; 3443 VectorNormalize(dir); 3444 VectorCopy(startreach->start, start); 3445 VectorMA(startreach->start, 1, dir, start); 3446 start[2] += 1; 3447 VectorMA(startreach->start, 16, dir, end); 3448 end[2] += 1; 3449 // 3450 numareas = AAS_TraceAreas(start, end, areas, points, 10); 3451 if (numareas <= 0) continue; 3452 if (numareas > 1) VectorCopy(points[1], startreach->start); 3453 else VectorCopy(end, startreach->start); 3454 // 3455 if (!AAS_PointAreaNum(startreach->start)) continue; 3456 if (!AAS_PointAreaNum(endreach->end)) continue; 3457 // 3458 lreach = AAS_AllocReachability(); 3459 lreach->areanum = endreach->areanum; 3460 if (i == 0) lreach->edgenum = ((int)move_start[axis] << 16) | ((int) move_end[axis] & 0x0000ffff); 3461 else lreach->edgenum = ((int)move_end[axis] << 16) | ((int) move_start[axis] & 0x0000ffff); 3462 lreach->facenum = (spawnflags << 16) | modelnum; 3463 VectorCopy(startreach->start, lreach->start); 3464 VectorCopy(endreach->end, lreach->end); 3465 #ifndef BSPC 3466 // AAS_DrawArrow(lreach->start, lreach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); 3467 // AAS_PermanentLine(lreach->start, lreach->end, 1); 3468 #endif 3469 lreach->traveltype = TRAVEL_FUNCBOB; 3470 lreach->traveltype |= AAS_TravelFlagsForTeam(ent); 3471 lreach->traveltime = aassettings.rs_funcbob; 3472 reach_funcbob++; 3473 lreach->next = areareachability[startreach->areanum]; 3474 areareachability[startreach->areanum] = lreach; 3475 // 3476 } //end for 3477 } //end for 3478 for (startreach = firststartreach; startreach; startreach = nextstartreach) 3479 { 3480 nextstartreach = startreach->next; 3481 AAS_FreeReachability(startreach); 3482 } //end for 3483 for (endreach = firstendreach; endreach; endreach = nextendreach) 3484 { 3485 nextendreach = endreach->next; 3486 AAS_FreeReachability(endreach); 3487 } //end for 3488 //only go up with func_bobbing entities that go up and down 3489 if (!(spawnflags & 1) && !(spawnflags & 2)) break; 3490 } //end for 3491 } //end for 3492 } //end of the function AAS_Reachability_FuncBobbing 3493 //=========================================================================== 3494 // 3495 // Parameter: - 3496 // Returns: - 3497 // Changes Globals: - 3498 //=========================================================================== 3499 void AAS_Reachability_JumpPad(void) 3500 { 3501 int face2num, i, ret, area2num, visualize, ent, bot_visualizejumppads; 3502 //int modelnum, ent2; 3503 //float dist, time, height, gravity, forward; 3504 float speed, zvel, hordist; 3505 aas_face_t *face2; 3506 aas_area_t *area2; 3507 aas_lreachability_t *lreach; 3508 vec3_t areastart, facecenter, dir, cmdmove; 3509 vec3_t velocity, absmins, absmaxs; 3510 //vec3_t origin, ent2origin, angles, teststart; 3511 aas_clientmove_t move; 3512 //aas_trace_t trace; 3513 aas_link_t *areas, *link; 3514 //char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; 3515 char classname[MAX_EPAIRKEY]; 3516 3517 #ifdef BSPC 3518 bot_visualizejumppads = 0; 3519 #else 3520 bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0"); 3521 #endif 3522 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) 3523 { 3524 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; 3525 if (strcmp(classname, "trigger_push")) continue; 3526 // 3527 if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue; 3528 /* 3529 // 3530 AAS_FloatForBSPEpairKey(ent, "speed", &speed); 3531 if (!speed) speed = 1000; 3532 // AAS_VectorForBSPEpairKey(ent, "angles", angles); 3533 // AAS_SetMovedir(angles, velocity); 3534 // VectorScale(velocity, speed, velocity); 3535 VectorClear(angles); 3536 //get the mins, maxs and origin of the model 3537 AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); 3538 if (model[0]) modelnum = atoi(model+1); 3539 else modelnum = 0; 3540 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin); 3541 VectorAdd(origin, absmins, absmins); 3542 VectorAdd(origin, absmaxs, absmaxs); 3543 // 3544 #ifdef REACH_DEBUG 3545 botimport.Print(PRT_MESSAGE, "absmins = %f %f %f\n", absmins[0], absmins[1], absmins[2]); 3546 botimport.Print(PRT_MESSAGE, "absmaxs = %f %f %f\n", absmaxs[0], absmaxs[1], absmaxs[2]); 3547 #endif REACH_DEBUG 3548 VectorAdd(absmins, absmaxs, origin); 3549 VectorScale (origin, 0.5, origin); 3550 3551 //get the start areas 3552 VectorCopy(origin, teststart); 3553 teststart[2] += 64; 3554 trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1); 3555 if (trace.startsolid) 3556 { 3557 botimport.Print(PRT_MESSAGE, "trigger_push start solid\n"); 3558 VectorCopy(origin, areastart); 3559 } //end if 3560 else 3561 { 3562 VectorCopy(trace.endpos, areastart); 3563 } //end else 3564 areastart[2] += 0.125; 3565 // 3566 //AAS_DrawPermanentCross(origin, 4, 4); 3567 //get the target entity 3568 AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY); 3569 for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2)) 3570 { 3571 if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue; 3572 if (!strcmp(targetname, target)) break; 3573 } //end for 3574 if (!ent2) 3575 { 3576 botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target); 3577 continue; 3578 } //end if 3579 AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin); 3580 // 3581 height = ent2origin[2] - origin[2]; 3582 gravity = aassettings.sv_gravity; 3583 time = sqrt( height / ( 0.5 * gravity ) ); 3584 if (!time) 3585 { 3586 botimport.Print(PRT_MESSAGE, "trigger_push without time\n"); 3587 continue; 3588 } //end if 3589 // set s.origin2 to the push velocity 3590 VectorSubtract ( ent2origin, origin, velocity); 3591 dist = VectorNormalize( velocity); 3592 forward = dist / time; 3593 //FIXME: why multiply by 1.1 3594 forward *= 1.1; 3595 VectorScale(velocity, forward, velocity); 3596 velocity[2] = time * gravity; 3597 */ 3598 //get the areas the jump pad brush is in 3599 areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); 3600 /* 3601 for (link = areas; link; link = link->next_area) 3602 { 3603 if (link->areanum == 563) 3604 { 3605 ret = qfalse; 3606 } 3607 } 3608 */ 3609 for (link = areas; link; link = link->next_area) 3610 { 3611 if (AAS_AreaJumpPad(link->areanum)) break; 3612 } //end for 3613 if (!link) 3614 { 3615 botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n"); 3616 AAS_UnlinkFromAreas(areas); 3617 continue; 3618 } //end if 3619 // 3620 botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]); 3621 //if there is a horizontal velocity check for a reachability without air control 3622 if (velocity[0] || velocity[1]) 3623 { 3624 VectorSet(cmdmove, 0, 0, 0); 3625 //VectorCopy(velocity, cmdmove); 3626 //cmdmove[2] = 0; 3627 Com_Memset(&move, 0, sizeof(aas_clientmove_t)); 3628 area2num = 0; 3629 for (i = 0; i < 20; i++) 3630 { 3631 AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse, 3632 velocity, cmdmove, 0, 30, 0.1f, 3633 SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| 3634 SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, bot_visualizejumppads); 3635 area2num = move.endarea; 3636 for (link = areas; link; link = link->next_area) 3637 { 3638 if (!AAS_AreaJumpPad(link->areanum)) continue; 3639 if (link->areanum == area2num) break; 3640 } //end if 3641 if (!link) break; 3642 VectorCopy(move.endpos, areastart); 3643 VectorCopy(move.velocity, velocity); 3644 } //end for 3645 if (area2num && i < 20) 3646 { 3647 for (link = areas; link; link = link->next_area) 3648 { 3649 if (!AAS_AreaJumpPad(link->areanum)) continue; 3650 if (AAS_ReachabilityExists(link->areanum, area2num)) continue; 3651 //create a rocket or bfg jump reachability from area1 to area2 3652 lreach = AAS_AllocReachability(); 3653 if (!lreach) 3654 { 3655 AAS_UnlinkFromAreas(areas); 3656 return; 3657 } //end if 3658 lreach->areanum = area2num; 3659 //NOTE: the facenum is the Z velocity 3660 lreach->facenum = velocity[2]; 3661 //NOTE: the edgenum is the horizontal velocity 3662 lreach->edgenum = sqrt(velocity[0] * velocity[0] + velocity[1] * velocity[1]); 3663 VectorCopy(areastart, lreach->start); 3664 VectorCopy(move.endpos, lreach->end); 3665 lreach->traveltype = TRAVEL_JUMPPAD; 3666 lreach->traveltype |= AAS_TravelFlagsForTeam(ent); 3667 lreach->traveltime = aassettings.rs_jumppad; 3668 lreach->next = areareachability[link->areanum]; 3669 areareachability[link->areanum] = lreach; 3670 // 3671 reach_jumppad++; 3672 } //end for 3673 } //end if 3674 } //end if 3675 // 3676 if (fabs(velocity[0]) > 100 || fabs(velocity[1]) > 100) continue; 3677 //check for areas we can reach with air control 3678 for (area2num = 1; area2num < aasworld.numareas; area2num++) 3679 { 3680 visualize = qfalse; 3681 /* 3682 if (area2num == 3568) 3683 { 3684 for (link = areas; link; link = link->next_area) 3685 { 3686 if (link->areanum == 3380) 3687 { 3688 visualize = qtrue; 3689 botimport.Print(PRT_MESSAGE, "bah\n"); 3690 } //end if 3691 } //end for 3692 } //end if*/ 3693 //never try to go back to one of the original jumppad areas 3694 //and don't create reachabilities if they already exist 3695 for (link = areas; link; link = link->next_area) 3696 { 3697 if (AAS_ReachabilityExists(link->areanum, area2num)) break; 3698 if (AAS_AreaJumpPad(link->areanum)) 3699 { 3700 if (link->areanum == area2num) break; 3701 } //end if 3702 } //end if 3703 if (link) continue; 3704 // 3705 area2 = &aasworld.areas[area2num]; 3706 for (i = 0; i < area2->numfaces; i++) 3707 { 3708 face2num = aasworld.faceindex[area2->firstface + i]; 3709 face2 = &aasworld.faces[abs(face2num)]; 3710 //if it is not a ground face 3711 if (!(face2->faceflags & FACE_GROUND)) continue; 3712 //get the center of the face 3713 AAS_FaceCenter(face2num, facecenter); 3714 //only go higher up 3715 if (facecenter[2] < areastart[2]) continue; 3716 //get the jumppad jump z velocity 3717 zvel = velocity[2]; 3718 //get the horizontal speed for the jump, if it isn't possible to calculate this 3719 //speed 3720 ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed); 3721 if (ret && speed < 150) 3722 { 3723 //direction towards the face center 3724 VectorSubtract(facecenter, areastart, dir); 3725 dir[2] = 0; 3726 hordist = VectorNormalize(dir); 3727 //if (hordist < 1.6 * facecenter[2] - areastart[2]) 3728 { 3729 //get command movement 3730 VectorScale(dir, speed, cmdmove); 3731 // 3732 AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse, 3733 velocity, cmdmove, 30, 30, 0.1f, 3734 SE_ENTERWATER|SE_ENTERSLIME| 3735 SE_ENTERLAVA|SE_HITGROUNDDAMAGE| 3736 SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_HITGROUNDAREA, area2num, visualize); 3737 //if prediction time wasn't enough to fully predict the movement 3738 //don't enter slime or lava and don't fall from too high 3739 if (move.frames < 30 && 3740 !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) 3741 && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER))) 3742 { 3743 //never go back to the same jumppad 3744 for (link = areas; link; link = link->next_area) 3745 { 3746 if (link->areanum == move.endarea) break; 3747 } 3748 if (!link) 3749 { 3750 for (link = areas; link; link = link->next_area) 3751 { 3752 if (!AAS_AreaJumpPad(link->areanum)) continue; 3753 if (AAS_ReachabilityExists(link->areanum, area2num)) continue; 3754 //create a jumppad reachability from area1 to area2 3755 lreach = AAS_AllocReachability(); 3756 if (!lreach) 3757 { 3758 AAS_UnlinkFromAreas(areas); 3759 return; 3760 } //end if 3761 lreach->areanum = move.endarea; 3762 //NOTE: the facenum is the Z velocity 3763 lreach->facenum = velocity[2]; 3764 //NOTE: the edgenum is the horizontal velocity 3765 lreach->edgenum = sqrt(cmdmove[0] * cmdmove[0] + cmdmove[1] * cmdmove[1]); 3766 VectorCopy(areastart, lreach->start); 3767 VectorCopy(facecenter, lreach->end); 3768 lreach->traveltype = TRAVEL_JUMPPAD; 3769 lreach->traveltype |= AAS_TravelFlagsForTeam(ent); 3770 lreach->traveltime = aassettings.rs_aircontrolledjumppad; 3771 lreach->next = areareachability[link->areanum]; 3772 areareachability[link->areanum] = lreach; 3773 // 3774 reach_jumppad++; 3775 } //end for 3776 } 3777 } //end if 3778 } //end if 3779 } //end for 3780 } //end for 3781 } //end for 3782 AAS_UnlinkFromAreas(areas); 3783 } //end for 3784 } //end of the function AAS_Reachability_JumpPad 3785 //=========================================================================== 3786 // never point at ground faces 3787 // always a higher and pretty far area 3788 // 3789 // Parameter: - 3790 // Returns: - 3791 // Changes Globals: - 3792 //=========================================================================== 3793 int AAS_Reachability_Grapple(int area1num, int area2num) 3794 { 3795 int face2num, i, j, areanum, numareas, areas[20]; 3796 float mingrappleangle, z, hordist; 3797 bsp_trace_t bsptrace; 3798 aas_trace_t trace; 3799 aas_face_t *face2; 3800 aas_area_t *area1, *area2; 3801 aas_lreachability_t *lreach; 3802 vec3_t areastart, facecenter, start, end, dir, down = {0, 0, -1}; 3803 vec_t *v; 3804 3805 //only grapple when on the ground or swimming 3806 if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse; 3807 //don't grapple from a crouch area 3808 if (!(AAS_AreaPresenceType(area1num) & PRESENCE_NORMAL)) return qfalse; 3809 //NOTE: disabled area swim it doesn't work right 3810 if (AAS_AreaSwim(area1num)) return qfalse; 3811 // 3812 area1 = &aasworld.areas[area1num]; 3813 area2 = &aasworld.areas[area2num]; 3814 //don't grapple towards way lower areas 3815 if (area2->maxs[2] < area1->mins[2]) return qfalse; 3816 // 3817 VectorCopy(aasworld.areas[area1num].center, start); 3818 //if not a swim area 3819 if (!AAS_AreaSwim(area1num)) 3820 { 3821 if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num, 3822 start[0], start[1], start[2]); 3823 VectorCopy(start, end); 3824 end[2] -= 1000; 3825 trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); 3826 if (trace.startsolid) return qfalse; 3827 VectorCopy(trace.endpos, areastart); 3828 } //end if 3829 else 3830 { 3831 if (!(AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return qfalse; 3832 } //end else 3833 // 3834 //start is now the start point 3835 // 3836 for (i = 0; i < area2->numfaces; i++) 3837 { 3838 face2num = aasworld.faceindex[area2->firstface + i]; 3839 face2 = &aasworld.faces[abs(face2num)]; 3840 //if it is not a solid face 3841 if (!(face2->faceflags & FACE_SOLID)) continue; 3842 //direction towards the first vertex of the face 3843 v = aasworld.vertexes[aasworld.edges[abs(aasworld.edgeindex[face2->firstedge])].v[0]]; 3844 VectorSubtract(v, areastart, dir); 3845 //if the face plane is facing away 3846 if (DotProduct(aasworld.planes[face2->planenum].normal, dir) > 0) continue; 3847 //get the center of the face 3848 AAS_FaceCenter(face2num, facecenter); 3849 //only go higher up with the grapple 3850 if (facecenter[2] < areastart[2] + 64) continue; 3851 //only use vertical faces or downward facing faces 3852 if (DotProduct(aasworld.planes[face2->planenum].normal, down) < 0) continue; 3853 //direction towards the face center 3854 VectorSubtract(facecenter, areastart, dir); 3855 // 3856 z = dir[2]; 3857 dir[2] = 0; 3858 hordist = VectorLength(dir); 3859 if (!hordist) continue; 3860 //if too far 3861 if (hordist > 2000) continue; 3862 //check the minimal angle of the movement 3863 mingrappleangle = 15; //15 degrees 3864 if (z / hordist < tan(2 * M_PI * mingrappleangle / 360)) continue; 3865 // 3866 VectorCopy(facecenter, start); 3867 VectorMA(facecenter, -500, aasworld.planes[face2->planenum].normal, end); 3868 // 3869 bsptrace = AAS_Trace(start, NULL, NULL, end, 0, CONTENTS_SOLID); 3870 //the grapple won't stick to the sky and the grapple point should be near the AAS wall 3871 if ((bsptrace.surface.flags & SURF_SKY) || (bsptrace.fraction * 500 > 32)) continue; 3872 //trace a full bounding box from the area center on the ground to 3873 //the center of the face 3874 VectorSubtract(facecenter, areastart, dir); 3875 VectorNormalize(dir); 3876 VectorMA(areastart, 4, dir, start); 3877 VectorCopy(bsptrace.endpos, end); 3878 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); 3879 VectorSubtract(trace.endpos, facecenter, dir); 3880 if (VectorLength(dir) > 24) continue; 3881 // 3882 VectorCopy(trace.endpos, start); 3883 VectorCopy(trace.endpos, end); 3884 end[2] -= AAS_FallDamageDistance(); 3885 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); 3886 if (trace.fraction >= 1) continue; 3887 //area to end in 3888 areanum = AAS_PointAreaNum(trace.endpos); 3889 //if not in lava or slime 3890 if (aasworld.areasettings[areanum].contents & (AREACONTENTS_SLIME|AREACONTENTS_LAVA)) 3891 { 3892 continue; 3893 } //end if 3894 //do not go the the source area 3895 if (areanum == area1num) continue; 3896 //don't create reachabilities if they already exist 3897 if (AAS_ReachabilityExists(area1num, areanum)) continue; 3898 //only end in areas we can stand 3899 if (!AAS_AreaGrounded(areanum)) continue; 3900 //never go through cluster portals!! 3901 numareas = AAS_TraceAreas(areastart, bsptrace.endpos, areas, NULL, 20); 3902 if (numareas >= 20) continue; 3903 for (j = 0; j < numareas; j++) 3904 { 3905 if (aasworld.areasettings[areas[j]].contents & AREACONTENTS_CLUSTERPORTAL) break; 3906 } //end for 3907 if (j < numareas) continue; 3908 //create a new reachability link 3909 lreach = AAS_AllocReachability(); 3910 if (!lreach) return qfalse; 3911 lreach->areanum = areanum; 3912 lreach->facenum = face2num; 3913 lreach->edgenum = 0; 3914 VectorCopy(areastart, lreach->start); 3915 //VectorCopy(facecenter, lreach->end); 3916 VectorCopy(bsptrace.endpos, lreach->end); 3917 lreach->traveltype = TRAVEL_GRAPPLEHOOK; 3918 VectorSubtract(lreach->end, lreach->start, dir); 3919 lreach->traveltime = aassettings.rs_startgrapple + VectorLength(dir) * 0.25; 3920 lreach->next = areareachability[area1num]; 3921 areareachability[area1num] = lreach; 3922 // 3923 reach_grapple++; 3924 } //end for 3925 // 3926 return qfalse; 3927 } //end of the function AAS_Reachability_Grapple 3928 //=========================================================================== 3929 // 3930 // Parameter: - 3931 // Returns: - 3932 // Changes Globals: - 3933 //=========================================================================== 3934 void AAS_SetWeaponJumpAreaFlags(void) 3935 { 3936 int ent, i; 3937 vec3_t mins = {-15, -15, -15}, maxs = {15, 15, 15}; 3938 vec3_t origin; 3939 int areanum, weaponjumpareas, spawnflags; 3940 char classname[MAX_EPAIRKEY]; 3941 3942 weaponjumpareas = 0; 3943 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) 3944 { 3945 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; 3946 if ( 3947 !strcmp(classname, "item_armor_body") || 3948 !strcmp(classname, "item_armor_combat") || 3949 !strcmp(classname, "item_health_mega") || 3950 !strcmp(classname, "weapon_grenadelauncher") || 3951 !strcmp(classname, "weapon_rocketlauncher") || 3952 !strcmp(classname, "weapon_lightning") || 3953 !strcmp(classname, "weapon_plasmagun") || 3954 !strcmp(classname, "weapon_railgun") || 3955 !strcmp(classname, "weapon_bfg") || 3956 !strcmp(classname, "item_quad") || 3957 !strcmp(classname, "item_regen") || 3958 !strcmp(classname, "item_invulnerability")) 3959 { 3960 if (AAS_VectorForBSPEpairKey(ent, "origin", origin)) 3961 { 3962 spawnflags = 0; 3963 AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); 3964 //if not a stationary item 3965 if (!(spawnflags & 1)) 3966 { 3967 if (!AAS_DropToFloor(origin, mins, maxs)) 3968 { 3969 botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n", 3970 classname, origin[0], origin[1], origin[2]); 3971 } //end if 3972 } //end if 3973 //areanum = AAS_PointAreaNum(origin); 3974 areanum = AAS_BestReachableArea(origin, mins, maxs, origin); 3975 //the bot may rocket jump towards this area 3976 aasworld.areasettings[areanum].areaflags |= AREA_WEAPONJUMP; 3977 // 3978 //if (!AAS_AreaGrounded(areanum)) 3979 // botimport.Print(PRT_MESSAGE, "area not grounded\n"); 3980 // 3981 weaponjumpareas++; 3982 } //end if 3983 } //end if 3984 } //end for 3985 for (i = 1; i < aasworld.numareas; i++) 3986 { 3987 if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) 3988 { 3989 aasworld.areasettings[i].areaflags |= AREA_WEAPONJUMP; 3990 weaponjumpareas++; 3991 } //end if 3992 } //end for 3993 botimport.Print(PRT_MESSAGE, "%d weapon jump areas\n", weaponjumpareas); 3994 } //end of the function AAS_SetWeaponJumpAreaFlags 3995 //=========================================================================== 3996 // create a possible weapon jump reachability from area1 to area2 3997 // 3998 // check if there's a cool item in the second area 3999 // check if area1 is lower than area2 4000 // check if the bot can rocketjump from area1 to area2 4001 // 4002 // Parameter: - 4003 // Returns: - 4004 // Changes Globals: - 4005 //=========================================================================== 4006 int AAS_Reachability_WeaponJump(int area1num, int area2num) 4007 { 4008 int face2num, i, n, ret, visualize; 4009 float speed, zvel, hordist; 4010 aas_face_t *face2; 4011 aas_area_t *area1, *area2; 4012 aas_lreachability_t *lreach; 4013 vec3_t areastart, facecenter, start, end, dir, cmdmove;// teststart; 4014 vec3_t velocity; 4015 aas_clientmove_t move; 4016 aas_trace_t trace; 4017 4018 visualize = qfalse; 4019 // if (area1num == 4436 && area2num == 4318) 4020 // { 4021 // visualize = qtrue; 4022 // } 4023 if (!AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) return qfalse; 4024 if (!AAS_AreaGrounded(area2num)) return qfalse; 4025 //NOTE: only weapon jump towards areas with an interesting item in it?? 4026 if (!(aasworld.areasettings[area2num].areaflags & AREA_WEAPONJUMP)) return qfalse; 4027 // 4028 area1 = &aasworld.areas[area1num]; 4029 area2 = &aasworld.areas[area2num]; 4030 //don't weapon jump towards way lower areas 4031 if (area2->maxs[2] < area1->mins[2]) return qfalse; 4032 // 4033 VectorCopy(aasworld.areas[area1num].center, start); 4034 //if not a swim area 4035 if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num, 4036 start[0], start[1], start[2]); 4037 VectorCopy(start, end); 4038 end[2] -= 1000; 4039 trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); 4040 if (trace.startsolid) return qfalse; 4041 VectorCopy(trace.endpos, areastart); 4042 // 4043 //areastart is now the start point 4044 // 4045 for (i = 0; i < area2->numfaces; i++) 4046 { 4047 face2num = aasworld.faceindex[area2->firstface + i]; 4048 face2 = &aasworld.faces[abs(face2num)]; 4049 //if it is not a solid face 4050 if (!(face2->faceflags & FACE_GROUND)) continue; 4051 //get the center of the face 4052 AAS_FaceCenter(face2num, facecenter); 4053 //only go higher up with weapon jumps 4054 if (facecenter[2] < areastart[2] + 64) continue; 4055 //NOTE: set to 2 to allow bfg jump reachabilities 4056 for (n = 0; n < 1/*2*/; n++) 4057 { 4058 //get the rocket jump z velocity 4059 if (n) zvel = AAS_BFGJumpZVelocity(areastart); 4060 else zvel = AAS_RocketJumpZVelocity(areastart); 4061 //get the horizontal speed for the jump, if it isn't possible to calculate this 4062 //speed (the jump is not possible) then there's no jump reachability created 4063 ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed); 4064 if (ret && speed < 300) 4065 { 4066 //direction towards the face center 4067 VectorSubtract(facecenter, areastart, dir); 4068 dir[2] = 0; 4069 hordist = VectorNormalize(dir); 4070 //if (hordist < 1.6 * (facecenter[2] - areastart[2])) 4071 { 4072 //get command movement 4073 VectorScale(dir, speed, cmdmove); 4074 VectorSet(velocity, 0, 0, zvel); 4075 /* 4076 //get command movement 4077 VectorScale(dir, speed, velocity); 4078 velocity[2] = zvel; 4079 VectorSet(cmdmove, 0, 0, 0); 4080 */ 4081 // 4082 AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qtrue, 4083 velocity, cmdmove, 30, 30, 0.1f, 4084 SE_ENTERWATER|SE_ENTERSLIME| 4085 SE_ENTERLAVA|SE_HITGROUNDDAMAGE| 4086 SE_TOUCHJUMPPAD|SE_HITGROUND|SE_HITGROUNDAREA, area2num, visualize); 4087 //if prediction time wasn't enough to fully predict the movement 4088 //don't enter slime or lava and don't fall from too high 4089 if (move.frames < 30 && 4090 !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) 4091 && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD))) 4092 { 4093 //create a rocket or bfg jump reachability from area1 to area2 4094 lreach = AAS_AllocReachability(); 4095 if (!lreach) return qfalse; 4096 lreach->areanum = area2num; 4097 lreach->facenum = 0; 4098 lreach->edgenum = 0; 4099 VectorCopy(areastart, lreach->start); 4100 VectorCopy(facecenter, lreach->end); 4101 if (n) 4102 { 4103 lreach->traveltype = TRAVEL_BFGJUMP; 4104 lreach->traveltime = aassettings.rs_bfgjump; 4105 } //end if 4106 else 4107 { 4108 lreach->traveltype = TRAVEL_ROCKETJUMP; 4109 lreach->traveltime = aassettings.rs_rocketjump; 4110 } //end else 4111 lreach->next = areareachability[area1num]; 4112 areareachability[area1num] = lreach; 4113 // 4114 reach_rocketjump++; 4115 return qtrue; 4116 } //end if 4117 } //end if 4118 } //end if 4119 } //end for 4120 } //end for 4121 // 4122 return qfalse; 4123 } //end of the function AAS_Reachability_WeaponJump 4124 //=========================================================================== 4125 // calculates additional walk off ledge reachabilities for the given area 4126 // 4127 // Parameter: - 4128 // Returns: - 4129 // Changes Globals: - 4130 //=========================================================================== 4131 void AAS_Reachability_WalkOffLedge(int areanum) 4132 { 4133 int i, j, k, l, m, n, p, areas[10], numareas; 4134 int face1num, face2num, face3num, edge1num, edge2num, edge3num; 4135 int otherareanum, gap, reachareanum, side; 4136 aas_area_t *area, *area2; 4137 aas_face_t *face1, *face2, *face3; 4138 aas_edge_t *edge; 4139 aas_plane_t *plane; 4140 vec_t *v1, *v2; 4141 vec3_t sharededgevec, mid, dir, testend; 4142 aas_lreachability_t *lreach; 4143 aas_trace_t trace; 4144 4145 if (!AAS_AreaGrounded(areanum) || AAS_AreaSwim(areanum)) return; 4146 // 4147 area = &aasworld.areas[areanum]; 4148 // 4149 for (i = 0; i < area->numfaces; i++) 4150 { 4151 face1num = aasworld.faceindex[area->firstface + i]; 4152 face1 = &aasworld.faces[abs(face1num)]; 4153 //face 1 must be a ground face 4154 if (!(face1->faceflags & FACE_GROUND)) continue; 4155 //go through all the edges of this ground face 4156 for (k = 0; k < face1->numedges; k++) 4157 { 4158 edge1num = aasworld.edgeindex[face1->firstedge + k]; 4159 //find another not ground face using this same edge 4160 for (j = 0; j < area->numfaces; j++) 4161 { 4162 face2num = aasworld.faceindex[area->firstface + j]; 4163 face2 = &aasworld.faces[abs(face2num)]; 4164 //face 2 may not be a ground face 4165 if (face2->faceflags & FACE_GROUND) continue; 4166 //compare all the edges 4167 for (l = 0; l < face2->numedges; l++) 4168 { 4169 edge2num = aasworld.edgeindex[face2->firstedge + l]; 4170 if (abs(edge1num) == abs(edge2num)) 4171 { 4172 //get the area at the other side of the face 4173 if (face2->frontarea == areanum) otherareanum = face2->backarea; 4174 else otherareanum = face2->frontarea; 4175 // 4176 area2 = &aasworld.areas[otherareanum]; 4177 //if the other area is grounded! 4178 if (aasworld.areasettings[otherareanum].areaflags & AREA_GROUNDED) 4179 { 4180 //check for a possible gap 4181 gap = qfalse; 4182 for (n = 0; n < area2->numfaces; n++) 4183 { 4184 face3num = aasworld.faceindex[area2->firstface + n]; 4185 //may not be the shared face of the two areas 4186 if (abs(face3num) == abs(face2num)) continue; 4187 // 4188 face3 = &aasworld.faces[abs(face3num)]; 4189 //find an edge shared by all three faces 4190 for (m = 0; m < face3->numedges; m++) 4191 { 4192 edge3num = aasworld.edgeindex[face3->firstedge + m]; 4193 //but the edge should be shared by all three faces 4194 if (abs(edge3num) == abs(edge1num)) 4195 { 4196 if (!(face3->faceflags & FACE_SOLID)) 4197 { 4198 gap = qtrue; 4199 break; 4200 } //end if 4201 // 4202 if (face3->faceflags & FACE_GROUND) 4203 { 4204 gap = qfalse; 4205 break; 4206 } //end if 4207 //FIXME: there are more situations to be handled 4208 gap = qtrue; 4209 break; 4210 } //end if 4211 } //end for 4212 if (m < face3->numedges) break; 4213 } //end for 4214 if (!gap) break; 4215 } //end if 4216 //check for a walk off ledge reachability 4217 edge = &aasworld.edges[abs(edge1num)]; 4218 side = edge1num < 0; 4219 // 4220 v1 = aasworld.vertexes[edge->v[side]]; 4221 v2 = aasworld.vertexes[edge->v[!side]]; 4222 // 4223 plane = &aasworld.planes[face1->planenum]; 4224 //get the points really into the areas 4225 VectorSubtract(v2, v1, sharededgevec); 4226 CrossProduct(plane->normal, sharededgevec, dir); 4227 VectorNormalize(dir); 4228 // 4229 VectorAdd(v1, v2, mid); 4230 VectorScale(mid, 0.5, mid); 4231 VectorMA(mid, 8, dir, mid); 4232 // 4233 VectorCopy(mid, testend); 4234 testend[2] -= 1000; 4235 trace = AAS_TraceClientBBox(mid, testend, PRESENCE_CROUCH, -1); 4236 // 4237 if (trace.startsolid) 4238 { 4239 //Log_Write("area %d: trace.startsolid\r\n", areanum); 4240 break; 4241 } //end if 4242 reachareanum = AAS_PointAreaNum(trace.endpos); 4243 if (reachareanum == areanum) 4244 { 4245 //Log_Write("area %d: same area\r\n", areanum); 4246 break; 4247 } //end if 4248 if (AAS_ReachabilityExists(areanum, reachareanum)) 4249 { 4250 //Log_Write("area %d: reachability already exists\r\n", areanum); 4251 break; 4252 } //end if 4253 if (!AAS_AreaGrounded(reachareanum) && !AAS_AreaSwim(reachareanum)) 4254 { 4255 //Log_Write("area %d, reach area %d: not grounded and not swim\r\n", areanum, reachareanum); 4256 break; 4257 } //end if 4258 // 4259 if (aasworld.areasettings[reachareanum].contents & (AREACONTENTS_SLIME 4260 | AREACONTENTS_LAVA)) 4261 { 4262 //Log_Write("area %d, reach area %d: lava or slime\r\n", areanum, reachareanum); 4263 break; 4264 } //end if 4265 //if not going through a cluster portal 4266 numareas = AAS_TraceAreas(mid, testend, areas, NULL, sizeof(areas) / sizeof(int)); 4267 for (p = 0; p < numareas; p++) 4268 if (AAS_AreaClusterPortal(areas[p])) 4269 break; 4270 if (p < numareas) 4271 break; 4272 // if a maximum fall height is set and the bot would fall down further 4273 if (aassettings.rs_maxfallheight && fabs(mid[2] - trace.endpos[2]) > aassettings.rs_maxfallheight) 4274 break; 4275 // 4276 lreach = AAS_AllocReachability(); 4277 if (!lreach) break; 4278 lreach->areanum = reachareanum; 4279 lreach->facenum = 0; 4280 lreach->edgenum = edge1num; 4281 VectorCopy(mid, lreach->start); 4282 VectorCopy(trace.endpos, lreach->end); 4283 lreach->traveltype = TRAVEL_WALKOFFLEDGE; 4284 lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.phys_gravity; 4285 if (!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum)) 4286 { 4287 if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta5) 4288 { 4289 lreach->traveltime += aassettings.rs_falldamage5; 4290 } //end if 4291 else if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta10) 4292 { 4293 lreach->traveltime += aassettings.rs_falldamage10; 4294 } //end if 4295 } //end if 4296 lreach->next = areareachability[areanum]; 4297 areareachability[areanum] = lreach; 4298 //we've got another walk off ledge reachability 4299 reach_walkoffledge++; 4300 } //end if 4301 } //end for 4302 } //end for 4303 } //end for 4304 } //end for 4305 } //end of the function AAS_Reachability_WalkOffLedge 4306 //=========================================================================== 4307 // 4308 // Parameter: - 4309 // Returns: - 4310 // Changes Globals: - 4311 //=========================================================================== 4312 void AAS_StoreReachability(void) 4313 { 4314 int i; 4315 aas_areasettings_t *areasettings; 4316 aas_lreachability_t *lreach; 4317 aas_reachability_t *reach; 4318 4319 if (aasworld.reachability) FreeMemory(aasworld.reachability); 4320 aasworld.reachability = (aas_reachability_t *) GetClearedMemory((numlreachabilities + 10) * sizeof(aas_reachability_t)); 4321 aasworld.reachabilitysize = 1; 4322 for (i = 0; i < aasworld.numareas; i++) 4323 { 4324 areasettings = &aasworld.areasettings[i]; 4325 areasettings->firstreachablearea = aasworld.reachabilitysize; 4326 areasettings->numreachableareas = 0; 4327 for (lreach = areareachability[i]; lreach; lreach = lreach->next) 4328 { 4329 reach = &aasworld.reachability[areasettings->firstreachablearea + 4330 areasettings->numreachableareas]; 4331 reach->areanum = lreach->areanum; 4332 reach->facenum = lreach->facenum; 4333 reach->edgenum = lreach->edgenum; 4334 VectorCopy(lreach->start, reach->start); 4335 VectorCopy(lreach->end, reach->end); 4336 reach->traveltype = lreach->traveltype; 4337 reach->traveltime = lreach->traveltime; 4338 // 4339 areasettings->numreachableareas++; 4340 } //end for 4341 aasworld.reachabilitysize += areasettings->numreachableareas; 4342 } //end for 4343 } //end of the function AAS_StoreReachability 4344 //=========================================================================== 4345 // 4346 // TRAVEL_WALK 100% equal floor height + steps 4347 // TRAVEL_CROUCH 100% 4348 // TRAVEL_BARRIERJUMP 100% 4349 // TRAVEL_JUMP 80% 4350 // TRAVEL_LADDER 100% + fall down from ladder + jump up to ladder 4351 // TRAVEL_WALKOFFLEDGE 90% walk off very steep walls? 4352 // TRAVEL_SWIM 100% 4353 // TRAVEL_WATERJUMP 100% 4354 // TRAVEL_TELEPORT 100% 4355 // TRAVEL_ELEVATOR 100% 4356 // TRAVEL_GRAPPLEHOOK 100% 4357 // TRAVEL_DOUBLEJUMP 0% 4358 // TRAVEL_RAMPJUMP 0% 4359 // TRAVEL_STRAFEJUMP 0% 4360 // TRAVEL_ROCKETJUMP 100% (currently limited towards areas with items) 4361 // TRAVEL_BFGJUMP 0% (currently disabled) 4362 // TRAVEL_JUMPPAD 100% 4363 // TRAVEL_FUNCBOB 100% 4364 // 4365 // Parameter: - 4366 // Returns: true if NOT finished 4367 // Changes Globals: - 4368 //=========================================================================== 4369 int AAS_ContinueInitReachability(float time) 4370 { 4371 int i, j, todo, start_time; 4372 static float framereachability, reachability_delay; 4373 static int lastpercentage; 4374 4375 if (!aasworld.loaded) return qfalse; 4376 //if reachability is calculated for all areas 4377 if (aasworld.numreachabilityareas >= aasworld.numareas + 2) return qfalse; 4378 //if starting with area 1 (area 0 is a dummy) 4379 if (aasworld.numreachabilityareas == 1) 4380 { 4381 botimport.Print(PRT_MESSAGE, "calculating reachability...\n"); 4382 lastpercentage = 0; 4383 framereachability = 2000; 4384 reachability_delay = 1000; 4385 } //end if 4386 //number of areas to calculate reachability for this cycle 4387 todo = aasworld.numreachabilityareas + (int) framereachability; 4388 start_time = Sys_MilliSeconds(); 4389 //loop over the areas 4390 for (i = aasworld.numreachabilityareas; i < aasworld.numareas && i < todo; i++) 4391 { 4392 aasworld.numreachabilityareas++; 4393 //only create jumppad reachabilities from jumppad areas 4394 if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) 4395 { 4396 continue; 4397 } //end if 4398 //loop over the areas 4399 for (j = 1; j < aasworld.numareas; j++) 4400 { 4401 if (i == j) continue; 4402 //never create reachabilities from teleporter or jumppad areas to regular areas 4403 if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)) 4404 { 4405 if (!(aasworld.areasettings[j].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))) 4406 { 4407 continue; 4408 } //end if 4409 } //end if 4410 //if there already is a reachability link from area i to j 4411 if (AAS_ReachabilityExists(i, j)) continue; 4412 //check for a swim reachability 4413 if (AAS_Reachability_Swim(i, j)) continue; 4414 //check for a simple walk on equal floor height reachability 4415 if (AAS_Reachability_EqualFloorHeight(i, j)) continue; 4416 //check for step, barrier, waterjump and walk off ledge reachabilities 4417 if (AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(i, j)) continue; 4418 //check for ladder reachabilities 4419 if (AAS_Reachability_Ladder(i, j)) continue; 4420 //check for a jump reachability 4421 if (AAS_Reachability_Jump(i, j)) continue; 4422 } //end for 4423 //never create these reachabilities from teleporter or jumppad areas 4424 if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)) 4425 { 4426 continue; 4427 } //end if 4428 //loop over the areas 4429 for (j = 1; j < aasworld.numareas; j++) 4430 { 4431 if (i == j) continue; 4432 // 4433 if (AAS_ReachabilityExists(i, j)) continue; 4434 //check for a grapple hook reachability 4435 if (calcgrapplereach) AAS_Reachability_Grapple(i, j); 4436 //check for a weapon jump reachability 4437 AAS_Reachability_WeaponJump(i, j); 4438 } //end for 4439 //if the calculation took more time than the max reachability delay 4440 if (Sys_MilliSeconds() - start_time > (int) reachability_delay) break; 4441 // 4442 if (aasworld.numreachabilityareas * 1000 / aasworld.numareas > lastpercentage) break; 4443 } //end for 4444 // 4445 if (aasworld.numreachabilityareas == aasworld.numareas) 4446 { 4447 botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) 100.0); 4448 botimport.Print(PRT_MESSAGE, "\nplease wait while storing reachability...\n"); 4449 aasworld.numreachabilityareas++; 4450 } //end if 4451 //if this is the last step in the reachability calculations 4452 else if (aasworld.numreachabilityareas == aasworld.numareas + 1) 4453 { 4454 //create additional walk off ledge reachabilities for every area 4455 for (i = 1; i < aasworld.numareas; i++) 4456 { 4457 //only create jumppad reachabilities from jumppad areas 4458 if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) 4459 { 4460 continue; 4461 } //end if 4462 AAS_Reachability_WalkOffLedge(i); 4463 } //end for 4464 //create jump pad reachabilities 4465 AAS_Reachability_JumpPad(); 4466 //create teleporter reachabilities 4467 AAS_Reachability_Teleport(); 4468 //create elevator (func_plat) reachabilities 4469 AAS_Reachability_Elevator(); 4470 //create func_bobbing reachabilities 4471 AAS_Reachability_FuncBobbing(); 4472 // 4473 #ifdef DEBUG 4474 botimport.Print(PRT_MESSAGE, "%6d reach swim\n", reach_swim); 4475 botimport.Print(PRT_MESSAGE, "%6d reach equal floor\n", reach_equalfloor); 4476 botimport.Print(PRT_MESSAGE, "%6d reach step\n", reach_step); 4477 botimport.Print(PRT_MESSAGE, "%6d reach barrier\n", reach_barrier); 4478 botimport.Print(PRT_MESSAGE, "%6d reach waterjump\n", reach_waterjump); 4479 botimport.Print(PRT_MESSAGE, "%6d reach walkoffledge\n", reach_walkoffledge); 4480 botimport.Print(PRT_MESSAGE, "%6d reach jump\n", reach_jump); 4481 botimport.Print(PRT_MESSAGE, "%6d reach ladder\n", reach_ladder); 4482 botimport.Print(PRT_MESSAGE, "%6d reach walk\n", reach_walk); 4483 botimport.Print(PRT_MESSAGE, "%6d reach teleport\n", reach_teleport); 4484 botimport.Print(PRT_MESSAGE, "%6d reach funcbob\n", reach_funcbob); 4485 botimport.Print(PRT_MESSAGE, "%6d reach elevator\n", reach_elevator); 4486 botimport.Print(PRT_MESSAGE, "%6d reach grapple\n", reach_grapple); 4487 botimport.Print(PRT_MESSAGE, "%6d reach rocketjump\n", reach_rocketjump); 4488 botimport.Print(PRT_MESSAGE, "%6d reach jumppad\n", reach_jumppad); 4489 #endif 4490 //*/ 4491 //store all the reachabilities 4492 AAS_StoreReachability(); 4493 //free the reachability link heap 4494 AAS_ShutDownReachabilityHeap(); 4495 // 4496 FreeMemory(areareachability); 4497 // 4498 aasworld.numreachabilityareas++; 4499 // 4500 botimport.Print(PRT_MESSAGE, "calculating clusters...\n"); 4501 } //end if 4502 else 4503 { 4504 lastpercentage = aasworld.numreachabilityareas * 1000 / aasworld.numareas; 4505 botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) lastpercentage / 10); 4506 } //end else 4507 //not yet finished 4508 return qtrue; 4509 } //end of the function AAS_ContinueInitReachability 4510 //=========================================================================== 4511 // 4512 // Parameter: - 4513 // Returns: - 4514 // Changes Globals: - 4515 //=========================================================================== 4516 void AAS_InitReachability(void) 4517 { 4518 if (!aasworld.loaded) return; 4519 4520 if (aasworld.reachabilitysize) 4521 { 4522 #ifndef BSPC 4523 if (!((int)LibVarGetValue("forcereachability"))) 4524 { 4525 aasworld.numreachabilityareas = aasworld.numareas + 2; 4526 return; 4527 } //end if 4528 #else 4529 aasworld.numreachabilityareas = aasworld.numareas + 2; 4530 return; 4531 #endif //BSPC 4532 } //end if 4533 #ifndef BSPC 4534 calcgrapplereach = LibVarGetValue("grapplereach"); 4535 #endif 4536 aasworld.savefile = qtrue; 4537 //start with area 1 because area zero is a dummy 4538 aasworld.numreachabilityareas = 1; 4539 ////aasworld.numreachabilityareas = aasworld.numareas + 1; //only calculate entity reachabilities 4540 //setup the heap with reachability links 4541 AAS_SetupReachabilityHeap(); 4542 //allocate area reachability link array 4543 areareachability = (aas_lreachability_t **) GetClearedMemory( 4544 aasworld.numareas * sizeof(aas_lreachability_t *)); 4545 // 4546 AAS_SetWeaponJumpAreaFlags(); 4547 } //end of the function AAS_InitReachable