00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "../game/q_shared.h"
00033 #include "l_log.h"
00034 #include "l_memory.h"
00035 #include "l_script.h"
00036 #include "l_libvar.h"
00037 #include "l_precomp.h"
00038 #include "l_struct.h"
00039 #include "aasfile.h"
00040 #include "../game/botlib.h"
00041 #include "../game/be_aas.h"
00042 #include "be_aas_funcs.h"
00043 #include "be_aas_def.h"
00044
00045 extern int Sys_MilliSeconds(void);
00046
00047
00048 extern botlib_import_t botimport;
00049
00050
00051
00052
00053
00054 #define AAS_MAX_REACHABILITYSIZE 65536
00055
00056 #define REACHABILITYAREASPERCYCLE 15
00057
00058 #define INSIDEUNITS 2
00059 #define INSIDEUNITS_WALKEND 5
00060 #define INSIDEUNITS_WALKSTART 0.1
00061 #define INSIDEUNITS_WATERJUMP 15
00062
00063 #define AREA_WEAPONJUMP 8192 //valid area to weapon jump to
00064
00065 int reach_swim;
00066 int reach_equalfloor;
00067 int reach_step;
00068 int reach_walk;
00069 int reach_barrier;
00070 int reach_waterjump;
00071 int reach_walkoffledge;
00072 int reach_jump;
00073 int reach_ladder;
00074 int reach_teleport;
00075 int reach_elevator;
00076 int reach_funcbob;
00077 int reach_grapple;
00078 int reach_doublejump;
00079 int reach_rampjump;
00080 int reach_strafejump;
00081 int reach_rocketjump;
00082 int reach_bfgjump;
00083 int reach_jumppad;
00084
00085 int calcgrapplereach;
00086
00087 typedef struct aas_lreachability_s
00088 {
00089 int areanum;
00090 int facenum;
00091 int edgenum;
00092 vec3_t start;
00093 vec3_t end;
00094 int traveltype;
00095 unsigned short int traveltime;
00096
00097 struct aas_lreachability_s *next;
00098 } aas_lreachability_t;
00099
00100 aas_lreachability_t *reachabilityheap;
00101 aas_lreachability_t *nextreachability;
00102 aas_lreachability_t **areareachability;
00103 int numlreachabilities;
00104
00105
00106
00107
00108
00109
00110
00111
00112 float AAS_FaceArea(aas_face_t *face)
00113 {
00114 int i, edgenum, side;
00115 float total;
00116 vec_t *v;
00117 vec3_t d1, d2, cross;
00118 aas_edge_t *edge;
00119
00120 edgenum = aasworld.edgeindex[face->firstedge];
00121 side = edgenum < 0;
00122 edge = &aasworld.edges[abs(edgenum)];
00123 v = aasworld.vertexes[edge->v[side]];
00124
00125 total = 0;
00126 for (i = 1; i < face->numedges - 1; i++)
00127 {
00128 edgenum = aasworld.edgeindex[face->firstedge + i];
00129 side = edgenum < 0;
00130 edge = &aasworld.edges[abs(edgenum)];
00131 VectorSubtract(aasworld.vertexes[edge->v[side]], v, d1);
00132 VectorSubtract(aasworld.vertexes[edge->v[!side]], v, d2);
00133 CrossProduct(d1, d2, cross);
00134 total += 0.5 * VectorLength(cross);
00135 }
00136 return total;
00137 }
00138
00139
00140
00141
00142
00143
00144
00145 float AAS_AreaVolume(int areanum)
00146 {
00147 int i, edgenum, facenum, side;
00148 vec_t d, a, volume;
00149 vec3_t corner;
00150 aas_plane_t *plane;
00151 aas_edge_t *edge;
00152 aas_face_t *face;
00153 aas_area_t *area;
00154
00155 area = &aasworld.areas[areanum];
00156 facenum = aasworld.faceindex[area->firstface];
00157 face = &aasworld.faces[abs(facenum)];
00158 edgenum = aasworld.edgeindex[face->firstedge];
00159 edge = &aasworld.edges[abs(edgenum)];
00160
00161 VectorCopy(aasworld.vertexes[edge->v[0]], corner);
00162
00163
00164 volume = 0;
00165 for (i = 0; i < area->numfaces; i++)
00166 {
00167 facenum = abs(aasworld.faceindex[area->firstface + i]);
00168 face = &aasworld.faces[facenum];
00169 side = face->backarea != areanum;
00170 plane = &aasworld.planes[face->planenum ^ side];
00171 d = -(DotProduct (corner, plane->normal) - plane->dist);
00172 a = AAS_FaceArea(face);
00173 volume += d * a;
00174 }
00175
00176 volume /= 3;
00177 return volume;
00178 }
00179
00180
00181
00182
00183
00184
00185 int AAS_BestReachableLinkArea(aas_link_t *areas)
00186 {
00187 aas_link_t *link;
00188
00189 for (link = areas; link; link = link->next_area)
00190 {
00191 if (AAS_AreaGrounded(link->areanum) || AAS_AreaSwim(link->areanum))
00192 {
00193 return link->areanum;
00194 }
00195 }
00196
00197 for (link = areas; link; link = link->next_area)
00198 {
00199 if (link->areanum) return link->areanum;
00200
00201
00202 if (AAS_AreaReachability(link->areanum))
00203 return link->areanum;
00204 }
00205 return 0;
00206 }
00207
00208
00209
00210
00211
00212
00213 int AAS_GetJumpPadInfo(int ent, vec3_t areastart, vec3_t absmins, vec3_t absmaxs, vec3_t velocity)
00214 {
00215 int modelnum, ent2;
00216 float speed, height, gravity, time, dist, forward;
00217 vec3_t origin, angles, teststart, ent2origin;
00218 aas_trace_t trace;
00219 char model[MAX_EPAIRKEY];
00220 char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY];
00221
00222
00223 AAS_FloatForBSPEpairKey(ent, "speed", &speed);
00224 if (!speed) speed = 1000;
00225 VectorClear(angles);
00226
00227 AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
00228 if (model[0]) modelnum = atoi(model+1);
00229 else modelnum = 0;
00230 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin);
00231 VectorAdd(origin, absmins, absmins);
00232 VectorAdd(origin, absmaxs, absmaxs);
00233 VectorAdd(absmins, absmaxs, origin);
00234 VectorScale (origin, 0.5, origin);
00235
00236
00237 VectorCopy(origin, teststart);
00238 teststart[2] += 64;
00239 trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1);
00240 if (trace.startsolid)
00241 {
00242 botimport.Print(PRT_MESSAGE, "trigger_push start solid\n");
00243 VectorCopy(origin, areastart);
00244 }
00245 else
00246 {
00247 VectorCopy(trace.endpos, areastart);
00248 }
00249 areastart[2] += 0.125;
00250
00251
00252
00253 AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY);
00254 for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2))
00255 {
00256 if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue;
00257 if (!strcmp(targetname, target)) break;
00258 }
00259 if (!ent2)
00260 {
00261 botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target);
00262 return qfalse;
00263 }
00264 AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin);
00265
00266 height = ent2origin[2] - origin[2];
00267 gravity = aassettings.phys_gravity;
00268 time = sqrt( height / ( 0.5 * gravity ) );
00269 if (!time)
00270 {
00271 botimport.Print(PRT_MESSAGE, "trigger_push without time\n");
00272 return qfalse;
00273 }
00274
00275 VectorSubtract ( ent2origin, origin, velocity);
00276 dist = VectorNormalize( velocity);
00277 forward = dist / time;
00278
00279 forward *= 1.1f;
00280 VectorScale(velocity, forward, velocity);
00281 velocity[2] = time * gravity;
00282 return qtrue;
00283 }
00284
00285
00286
00287
00288
00289
00290 int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs)
00291 {
00292 int area2num, ent, bot_visualizejumppads, bestareanum;
00293 float volume, bestareavolume;
00294 vec3_t areastart, cmdmove, bboxmins, bboxmaxs;
00295 vec3_t absmins, absmaxs, velocity;
00296 aas_clientmove_t move;
00297 aas_link_t *areas, *link;
00298 char classname[MAX_EPAIRKEY];
00299
00300 #ifdef BSPC
00301 bot_visualizejumppads = 0;
00302 #else
00303 bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0");
00304 #endif
00305 VectorAdd(origin, mins, bboxmins);
00306 VectorAdd(origin, maxs, bboxmaxs);
00307 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
00308 {
00309 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
00310 if (strcmp(classname, "trigger_push")) continue;
00311
00312 if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue;
00313
00314 areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
00315 for (link = areas; link; link = link->next_area)
00316 {
00317 if (AAS_AreaJumpPad(link->areanum)) break;
00318 }
00319 if (!link)
00320 {
00321 botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n");
00322 AAS_UnlinkFromAreas(areas);
00323 continue;
00324 }
00325
00326
00327
00328 VectorSet(cmdmove, 0, 0, 0);
00329 Com_Memset(&move, 0, sizeof(aas_clientmove_t));
00330 area2num = 0;
00331 AAS_ClientMovementHitBBox(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
00332 velocity, cmdmove, 0, 30, 0.1f, bboxmins, bboxmaxs, bot_visualizejumppads);
00333 if (move.frames < 30)
00334 {
00335 bestareanum = 0;
00336 bestareavolume = 0;
00337 for (link = areas; link; link = link->next_area)
00338 {
00339 if (!AAS_AreaJumpPad(link->areanum)) continue;
00340 volume = AAS_AreaVolume(link->areanum);
00341 if (volume >= bestareavolume)
00342 {
00343 bestareanum = link->areanum;
00344 bestareavolume = volume;
00345 }
00346 }
00347 AAS_UnlinkFromAreas(areas);
00348 return bestareanum;
00349 }
00350 AAS_UnlinkFromAreas(areas);
00351 }
00352 return 0;
00353 }
00354
00355
00356
00357
00358
00359
00360 int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin)
00361 {
00362 int areanum, i, j, k, l;
00363 aas_link_t *areas;
00364 vec3_t absmins, absmaxs;
00365
00366 vec3_t start, end;
00367 aas_trace_t trace;
00368
00369 if (!aasworld.loaded)
00370 {
00371 botimport.Print(PRT_ERROR, "AAS_BestReachableArea: aas not loaded\n");
00372 return 0;
00373 }
00374
00375 VectorCopy(origin, start);
00376 areanum = AAS_PointAreaNum(start);
00377
00378 for (i = 0; i < 5 && !areanum; i++)
00379 {
00380 for (j = 0; j < 5 && !areanum; j++)
00381 {
00382 for (k = -1; k <= 1 && !areanum; k++)
00383 {
00384 for (l = -1; l <= 1 && !areanum; l++)
00385 {
00386 VectorCopy(origin, start);
00387 start[0] += (float) j * 4 * k;
00388 start[1] += (float) j * 4 * l;
00389 start[2] += (float) i * 4;
00390 areanum = AAS_PointAreaNum(start);
00391 }
00392 }
00393 }
00394 }
00395
00396 if (areanum)
00397 {
00398
00399 VectorCopy(start, end);
00400 start[2] += 0.25;
00401 end[2] -= 50;
00402 trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
00403 if (!trace.startsolid)
00404 {
00405 areanum = AAS_PointAreaNum(trace.endpos);
00406 VectorCopy(trace.endpos, goalorigin);
00407
00408
00409
00410
00411 if (areanum) return areanum;
00412 }
00413 else
00414 {
00415
00416
00417
00418 #if 0
00419 if (AAS_PointAreaNum(start))
00420 {
00421 Log_Write("point %f %f %f in area %d but trace startsolid", start[0], start[1], start[2], areanum);
00422 AAS_DrawPermanentCross(start, 4, LINECOLOR_RED);
00423 }
00424 botimport.Print(PRT_MESSAGE, "AAS_BestReachableArea: start solid\n");
00425 #endif
00426 VectorCopy(start, goalorigin);
00427 return areanum;
00428 }
00429 }
00430
00431
00432
00433
00434 VectorCopy(origin, goalorigin);
00435
00436 VectorAdd(origin, mins, absmins);
00437 VectorAdd(origin, maxs, absmaxs);
00438
00439
00440
00441
00442 areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
00443
00444 areanum = AAS_BestReachableLinkArea(areas);
00445
00446 AAS_UnlinkFromAreas(areas);
00447
00448 return areanum;
00449 }
00450
00451
00452
00453
00454
00455
00456 void AAS_SetupReachabilityHeap(void)
00457 {
00458 int i;
00459
00460 reachabilityheap = (aas_lreachability_t *) GetClearedMemory(
00461 AAS_MAX_REACHABILITYSIZE * sizeof(aas_lreachability_t));
00462 for (i = 0; i < AAS_MAX_REACHABILITYSIZE-1; i++)
00463 {
00464 reachabilityheap[i].next = &reachabilityheap[i+1];
00465 }
00466 reachabilityheap[AAS_MAX_REACHABILITYSIZE-1].next = NULL;
00467 nextreachability = reachabilityheap;
00468 numlreachabilities = 0;
00469 }
00470
00471
00472
00473
00474
00475
00476 void AAS_ShutDownReachabilityHeap(void)
00477 {
00478 FreeMemory(reachabilityheap);
00479 numlreachabilities = 0;
00480 }
00481
00482
00483
00484
00485
00486
00487
00488 aas_lreachability_t *AAS_AllocReachability(void)
00489 {
00490 aas_lreachability_t *r;
00491
00492 if (!nextreachability) return NULL;
00493
00494 if (!nextreachability->next) AAS_Error("AAS_MAX_REACHABILITYSIZE");
00495
00496 r = nextreachability;
00497 nextreachability = nextreachability->next;
00498 numlreachabilities++;
00499 return r;
00500 }
00501
00502
00503
00504
00505
00506
00507
00508 void AAS_FreeReachability(aas_lreachability_t *lreach)
00509 {
00510 Com_Memset(lreach, 0, sizeof(aas_lreachability_t));
00511
00512 lreach->next = nextreachability;
00513 nextreachability = lreach;
00514 numlreachabilities--;
00515 }
00516
00517
00518
00519
00520
00521
00522
00523 int AAS_AreaReachability(int areanum)
00524 {
00525 if (areanum < 0 || areanum >= aasworld.numareas)
00526 {
00527 AAS_Error("AAS_AreaReachability: areanum %d out of range", areanum);
00528 return 0;
00529 }
00530 return aasworld.areasettings[areanum].numreachableareas;
00531 }
00532
00533
00534
00535
00536
00537
00538
00539 float AAS_AreaGroundFaceArea(int areanum)
00540 {
00541 int i;
00542 float total;
00543 aas_area_t *area;
00544 aas_face_t *face;
00545
00546 total = 0;
00547 area = &aasworld.areas[areanum];
00548 for (i = 0; i < area->numfaces; i++)
00549 {
00550 face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])];
00551 if (!(face->faceflags & FACE_GROUND)) continue;
00552
00553 total += AAS_FaceArea(face);
00554 }
00555 return total;
00556 }
00557
00558
00559
00560
00561
00562
00563
00564 void AAS_FaceCenter(int facenum, vec3_t center)
00565 {
00566 int i;
00567 float scale;
00568 aas_face_t *face;
00569 aas_edge_t *edge;
00570
00571 face = &aasworld.faces[facenum];
00572
00573 VectorClear(center);
00574 for (i = 0; i < face->numedges; i++)
00575 {
00576 edge = &aasworld.edges[abs(aasworld.edgeindex[face->firstedge + i])];
00577 VectorAdd(center, aasworld.vertexes[edge->v[0]], center);
00578 VectorAdd(center, aasworld.vertexes[edge->v[1]], center);
00579 }
00580 scale = 0.5 / face->numedges;
00581 VectorScale(center, scale, center);
00582 }
00583
00584
00585
00586
00587
00588
00589
00590
00591 int AAS_FallDamageDistance(void)
00592 {
00593 float maxzvelocity, gravity, t;
00594
00595 maxzvelocity = sqrt(30 * 10000);
00596 gravity = aassettings.phys_gravity;
00597 t = maxzvelocity / gravity;
00598 return 0.5 * gravity * t * t;
00599 }
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609 float AAS_FallDelta(float distance)
00610 {
00611 float t, delta, gravity;
00612
00613 gravity = aassettings.phys_gravity;
00614 t = sqrt(fabs(distance) * 2 / gravity);
00615 delta = t * gravity;
00616 return delta * delta * 0.0001;
00617 }
00618
00619
00620
00621
00622
00623
00624 float AAS_MaxJumpHeight(float phys_jumpvel)
00625 {
00626 float phys_gravity;
00627
00628 phys_gravity = aassettings.phys_gravity;
00629
00630 return 0.5 * phys_gravity * (phys_jumpvel / phys_gravity) * (phys_jumpvel / phys_gravity);
00631 }
00632
00633
00634
00635
00636
00637
00638
00639 float AAS_MaxJumpDistance(float phys_jumpvel)
00640 {
00641 float phys_gravity, phys_maxvelocity, t;
00642
00643 phys_gravity = aassettings.phys_gravity;
00644 phys_maxvelocity = aassettings.phys_maxvelocity;
00645
00646 t = sqrt(aassettings.rs_maxjumpfallheight / (0.5 * phys_gravity));
00647
00648 return phys_maxvelocity * (t + phys_jumpvel / phys_gravity);
00649 }
00650
00651
00652
00653
00654
00655
00656
00657 int AAS_AreaCrouch(int areanum)
00658 {
00659 if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qtrue;
00660 else return qfalse;
00661 }
00662
00663
00664
00665
00666
00667
00668
00669 int AAS_AreaSwim(int areanum)
00670 {
00671 if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue;
00672 else return qfalse;
00673 }
00674
00675
00676
00677
00678
00679
00680
00681 int AAS_AreaLiquid(int areanum)
00682 {
00683 if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue;
00684 else return qfalse;
00685 }
00686
00687
00688
00689
00690
00691
00692 int AAS_AreaLava(int areanum)
00693 {
00694 return (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA);
00695 }
00696
00697
00698
00699
00700
00701
00702 int AAS_AreaSlime(int areanum)
00703 {
00704 return (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME);
00705 }
00706
00707
00708
00709
00710
00711
00712
00713 int AAS_AreaGrounded(int areanum)
00714 {
00715 return (aasworld.areasettings[areanum].areaflags & AREA_GROUNDED);
00716 }
00717
00718
00719
00720
00721
00722
00723
00724 int AAS_AreaLadder(int areanum)
00725 {
00726 return (aasworld.areasettings[areanum].areaflags & AREA_LADDER);
00727 }
00728
00729
00730
00731
00732
00733
00734 int AAS_AreaJumpPad(int areanum)
00735 {
00736 return (aasworld.areasettings[areanum].contents & AREACONTENTS_JUMPPAD);
00737 }
00738
00739
00740
00741
00742
00743
00744 int AAS_AreaTeleporter(int areanum)
00745 {
00746 return (aasworld.areasettings[areanum].contents & AREACONTENTS_TELEPORTER);
00747 }
00748
00749
00750
00751
00752
00753
00754 int AAS_AreaClusterPortal(int areanum)
00755 {
00756 return (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL);
00757 }
00758
00759
00760
00761
00762
00763
00764 int AAS_AreaDoNotEnter(int areanum)
00765 {
00766 return (aasworld.areasettings[areanum].contents & AREACONTENTS_DONOTENTER);
00767 }
00768
00769
00770
00771
00772
00773
00774
00775 unsigned short int AAS_BarrierJumpTravelTime(void)
00776 {
00777 return aassettings.phys_jumpvel / (aassettings.phys_gravity * 0.1);
00778 }
00779
00780
00781
00782
00783
00784
00785
00786 qboolean AAS_ReachabilityExists(int area1num, int area2num)
00787 {
00788 aas_lreachability_t *r;
00789
00790 for (r = areareachability[area1num]; r; r = r->next)
00791 {
00792 if (r->areanum == area2num) return qtrue;
00793 }
00794 return qfalse;
00795 }
00796
00797
00798
00799
00800
00801
00802
00803
00804 int AAS_NearbySolidOrGap(vec3_t start, vec3_t end)
00805 {
00806 vec3_t dir, testpoint;
00807 int areanum;
00808
00809 VectorSubtract(end, start, dir);
00810 dir[2] = 0;
00811 VectorNormalize(dir);
00812 VectorMA(end, 48, dir, testpoint);
00813
00814 areanum = AAS_PointAreaNum(testpoint);
00815 if (!areanum)
00816 {
00817 testpoint[2] += 16;
00818 areanum = AAS_PointAreaNum(testpoint);
00819 if (!areanum) return qtrue;
00820 }
00821 VectorMA(end, 64, dir, testpoint);
00822 areanum = AAS_PointAreaNum(testpoint);
00823 if (areanum)
00824 {
00825 if (!AAS_AreaSwim(areanum) && !AAS_AreaGrounded(areanum)) return qtrue;
00826 }
00827 return qfalse;
00828 }
00829
00830
00831
00832
00833
00834
00835
00836 int AAS_Reachability_Swim(int area1num, int area2num)
00837 {
00838 int i, j, face1num, face2num, side1;
00839 aas_area_t *area1, *area2;
00840 aas_areasettings_t *areasettings;
00841 aas_lreachability_t *lreach;
00842 aas_face_t *face1;
00843 aas_plane_t *plane;
00844 vec3_t start;
00845
00846 if (!AAS_AreaSwim(area1num) || !AAS_AreaSwim(area2num)) return qfalse;
00847
00848 if (!(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) return qfalse;
00849
00850 area1 = &aasworld.areas[area1num];
00851 area2 = &aasworld.areas[area2num];
00852
00853
00854 for (i = 0; i < 3; i++)
00855 {
00856 if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
00857 if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
00858 }
00859
00860 for (i = 0; i < area1->numfaces; i++)
00861 {
00862 face1num = aasworld.faceindex[area1->firstface + i];
00863 side1 = face1num < 0;
00864 face1num = abs(face1num);
00865
00866 for (j = 0; j < area2->numfaces; j++)
00867 {
00868 face2num = abs(aasworld.faceindex[area2->firstface + j]);
00869
00870 if (face1num == face2num)
00871 {
00872 AAS_FaceCenter(face1num, start);
00873
00874 if (AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))
00875 {
00876
00877 face1 = &aasworld.faces[face1num];
00878 areasettings = &aasworld.areasettings[area1num];
00879
00880 lreach = AAS_AllocReachability();
00881 if (!lreach) return qfalse;
00882 lreach->areanum = area2num;
00883 lreach->facenum = face1num;
00884 lreach->edgenum = 0;
00885 VectorCopy(start, lreach->start);
00886 plane = &aasworld.planes[face1->planenum ^ side1];
00887 VectorMA(lreach->start, -INSIDEUNITS, plane->normal, lreach->end);
00888 lreach->traveltype = TRAVEL_SWIM;
00889 lreach->traveltime = 1;
00890
00891 if (AAS_AreaVolume(area2num) < 800)
00892 lreach->traveltime += 200;
00893
00894
00895 lreach->next = areareachability[area1num];
00896 areareachability[area1num] = lreach;
00897 reach_swim++;
00898 return qtrue;
00899 }
00900 }
00901 }
00902 }
00903 return qfalse;
00904 }
00905
00906
00907
00908
00909
00910
00911
00912
00913 int AAS_Reachability_EqualFloorHeight(int area1num, int area2num)
00914 {
00915 int i, j, edgenum, edgenum1, edgenum2, foundreach, side;
00916 float height, bestheight, length, bestlength;
00917 vec3_t dir, start, end, normal, invgravity, gravitydirection = {0, 0, -1};
00918 vec3_t edgevec;
00919 aas_area_t *area1, *area2;
00920 aas_face_t *face1, *face2;
00921 aas_edge_t *edge;
00922 aas_plane_t *plane2;
00923 aas_lreachability_t lr, *lreach;
00924
00925 if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse;
00926
00927 area1 = &aasworld.areas[area1num];
00928 area2 = &aasworld.areas[area2num];
00929
00930 for (i = 0; i < 2; i++)
00931 {
00932 if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
00933 if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
00934 }
00935
00936 if (area2->mins[2] > area1->maxs[2]) return qfalse;
00937
00938 VectorCopy(gravitydirection, invgravity);
00939 VectorInverse(invgravity);
00940
00941 bestheight = 99999;
00942 bestlength = 0;
00943 foundreach = qfalse;
00944 Com_Memset(&lr, 0, sizeof(aas_lreachability_t));
00945
00946
00947
00948 for (i = 0; i < area1->numfaces; i++)
00949 {
00950 face1 = &aasworld.faces[abs(aasworld.faceindex[area1->firstface + i])];
00951 if (!(face1->faceflags & FACE_GROUND)) continue;
00952
00953 for (j = 0; j < area2->numfaces; j++)
00954 {
00955 face2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])];
00956 if (!(face2->faceflags & FACE_GROUND)) continue;
00957
00958 for (edgenum1 = 0; edgenum1 < face1->numedges; edgenum1++)
00959 {
00960 for (edgenum2 = 0; edgenum2 < face2->numedges; edgenum2++)
00961 {
00962 if (abs(aasworld.edgeindex[face1->firstedge + edgenum1]) !=
00963 abs(aasworld.edgeindex[face2->firstedge + edgenum2]))
00964 continue;
00965 edgenum = aasworld.edgeindex[face1->firstedge + edgenum1];
00966 side = edgenum < 0;
00967 edge = &aasworld.edges[abs(edgenum)];
00968
00969 VectorSubtract(aasworld.vertexes[edge->v[1]],
00970 aasworld.vertexes[edge->v[0]], dir);
00971 length = VectorLength(dir);
00972
00973 VectorAdd(aasworld.vertexes[edge->v[0]],
00974 aasworld.vertexes[edge->v[1]], start);
00975 VectorScale(start, 0.5, start);
00976 VectorCopy(start, end);
00977
00978
00979
00980
00981 VectorSubtract(aasworld.vertexes[edge->v[side]],
00982 aasworld.vertexes[edge->v[!side]], edgevec);
00983 plane2 = &aasworld.planes[face2->planenum];
00984 CrossProduct(edgevec, plane2->normal, normal);
00985 VectorNormalize(normal);
00986
00987
00988 VectorMA(end, INSIDEUNITS_WALKEND, normal, end);
00989 VectorMA(start, INSIDEUNITS_WALKSTART, normal, start);
00990 end[2] += 0.125;
00991
00992 height = DotProduct(invgravity, start);
00993
00994
00995
00996
00997
00998
00999 if (height < bestheight ||
01000 (height < bestheight + 1 && length > bestlength))
01001 {
01002 bestheight = height;
01003 bestlength = length;
01004
01005 lr.areanum = area2num;
01006 lr.facenum = 0;
01007 lr.edgenum = edgenum;
01008 VectorCopy(start, lr.start);
01009 VectorCopy(end, lr.end);
01010 lr.traveltype = TRAVEL_WALK;
01011 lr.traveltime = 1;
01012 foundreach = qtrue;
01013 }
01014 }
01015 }
01016 }
01017 }
01018 if (foundreach)
01019 {
01020
01021 lreach = AAS_AllocReachability();
01022 if (!lreach) return qfalse;
01023 lreach->areanum = lr.areanum;
01024 lreach->facenum = lr.facenum;
01025 lreach->edgenum = lr.edgenum;
01026 VectorCopy(lr.start, lreach->start);
01027 VectorCopy(lr.end, lreach->end);
01028 lreach->traveltype = lr.traveltype;
01029 lreach->traveltime = lr.traveltime;
01030 lreach->next = areareachability[area1num];
01031 areareachability[area1num] = lreach;
01032
01033 if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num))
01034 {
01035 lreach->traveltime += aassettings.rs_startcrouch;
01036 }
01037
01038
01039
01040
01041
01042
01043
01044
01045
01046
01047 reach_equalfloor++;
01048 return qtrue;
01049 }
01050 return qfalse;
01051 }
01052
01053
01054
01055
01056
01057
01058
01059 int AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2num)
01060 {
01061 int i, j, k, l, edge1num, edge2num, areas[10], numareas;
01062 int ground_bestarea2groundedgenum, ground_foundreach;
01063 int water_bestarea2groundedgenum, water_foundreach;
01064 int side1, area1swim, faceside1, groundface1num;
01065 float dist, dist1, dist2, diff, invgravitydot, ortdot;
01066 float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y;
01067 float length, ground_bestlength, water_bestlength, ground_bestdist, water_bestdist;
01068 vec3_t v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2;
01069 vec3_t normal, ort, edgevec, start, end, dir;
01070 vec3_t ground_beststart, ground_bestend, ground_bestnormal;
01071 vec3_t water_beststart, water_bestend, water_bestnormal;
01072 vec3_t invgravity = {0, 0, 1};
01073 vec3_t testpoint;
01074 aas_plane_t *plane;
01075 aas_area_t *area1, *area2;
01076 aas_face_t *groundface1, *groundface2, *ground_bestface1, *water_bestface1;
01077 aas_edge_t *edge1, *edge2;
01078 aas_lreachability_t *lreach;
01079 aas_trace_t trace;
01080
01081
01082 if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse;
01083
01084 if (!AAS_AreaGrounded(area2num) && !AAS_AreaSwim(area2num)) return qfalse;
01085
01086 area1 = &aasworld.areas[area1num];
01087 area2 = &aasworld.areas[area2num];
01088
01089 area1swim = AAS_AreaSwim(area1num);
01090
01091 for (i = 0; i < 2; i++)
01092 {
01093 if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
01094 if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
01095 }
01096
01097 ground_foundreach = qfalse;
01098 ground_bestdist = 99999;
01099 ground_bestlength = 0;
01100 ground_bestarea2groundedgenum = 0;
01101
01102 water_foundreach = qfalse;
01103 water_bestdist = 99999;
01104 water_bestlength = 0;
01105 water_bestarea2groundedgenum = 0;
01106
01107 for (i = 0; i < area1->numfaces; i++)
01108 {
01109 groundface1num = aasworld.faceindex[area1->firstface + i];
01110 faceside1 = groundface1num < 0;
01111 groundface1 = &aasworld.faces[abs(groundface1num)];
01112
01113 if (!(groundface1->faceflags & FACE_GROUND))
01114 {
01115
01116 if (area1swim)
01117 {
01118
01119 plane = &aasworld.planes[groundface1->planenum ^ (!faceside1)];
01120 if (DotProduct(plane->normal, invgravity) < 0.7) continue;
01121 }
01122 else
01123 {
01124
01125 continue;
01126 }
01127 }
01128
01129 for (k = 0; k < groundface1->numedges; k++)
01130 {
01131 edge1num = aasworld.edgeindex[groundface1->firstedge + k];
01132 side1 = (edge1num < 0);
01133
01134
01135
01136 if (!(groundface1->faceflags & FACE_GROUND)) side1 = (side1 == faceside1);
01137 edge1num = abs(edge1num);
01138 edge1 = &aasworld.edges[edge1num];
01139
01140 VectorCopy(aasworld.vertexes[edge1->v[!side1]], v1);
01141 VectorCopy(aasworld.vertexes[edge1->v[side1]], v2);
01142
01143
01144
01145 VectorSubtract(v2, v1, edgevec);
01146 CrossProduct(edgevec, invgravity, normal);
01147 VectorNormalize(normal);
01148 dist = DotProduct(normal, v1);
01149
01150 for (j = 0; j < area2->numfaces; j++)
01151 {
01152 groundface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])];
01153
01154 if (!(groundface2->faceflags & FACE_GROUND)) continue;
01155
01156 for (l = 0; l < groundface2->numedges; l++)
01157 {
01158 edge2num = abs(aasworld.edgeindex[groundface2->firstedge + l]);
01159 edge2 = &aasworld.edges[edge2num];
01160
01161 VectorCopy(aasworld.vertexes[edge2->v[0]], v3);
01162 VectorCopy(aasworld.vertexes[edge2->v[1]], v4);
01163
01164
01165 diff = DotProduct(normal, v3) - dist;
01166 if (diff < -0.1 || diff > 0.1) continue;
01167 diff = DotProduct(normal, v4) - dist;
01168 if (diff < -0.1 || diff > 0.1) continue;
01169
01170
01171
01172
01173
01174 CrossProduct(invgravity, normal, ort);
01175 invgravitydot = DotProduct(invgravity, invgravity);
01176 ortdot = DotProduct(ort, ort);
01177
01178
01179 y1 = v1[2];
01180 y2 = v2[2];
01181 y3 = v3[2];
01182 y4 = v4[2];
01183
01184 x1 = DotProduct(v1, ort) / ortdot;
01185 x2 = DotProduct(v2, ort) / ortdot;
01186 x3 = DotProduct(v3, ort) / ortdot;
01187 x4 = DotProduct(v4, ort) / ortdot;
01188
01189 if (x1 > x2)
01190 {
01191 tmp = x1; x1 = x2; x2 = tmp;
01192 tmp = y1; y1 = y2; y2 = tmp;
01193 VectorCopy(v1, tmpv); VectorCopy(v2, v1); VectorCopy(tmpv, v2);
01194 }
01195 if (x3 > x4)
01196 {
01197 tmp = x3; x3 = x4; x4 = tmp;
01198 tmp = y3; y3 = y4; y4 = tmp;
01199 VectorCopy(v3, tmpv); VectorCopy(v4, v3); VectorCopy(tmpv, v4);
01200 }
01201
01202 if (x2 <= x3 || x4 <= x1)
01203 {
01204
01205 continue;
01206 }
01207
01208 if ((x1 - 0.5 < x3 && x4 < x2 + 0.5) &&
01209 (x3 - 0.5 < x1 && x2 < x4 + 0.5))
01210 {
01211 dist1 = y3 - y1;
01212 dist2 = y4 - y2;
01213 VectorCopy(v1, p1area1);
01214 VectorCopy(v2, p2area1);
01215 VectorCopy(v3, p1area2);
01216 VectorCopy(v4, p2area2);
01217 }
01218 else
01219 {
01220
01221 if (x1 > x3 - 0.1 && x1 < x3 + 0.1)
01222 {
01223 dist1 = y3 - y1;
01224 VectorCopy(v1, p1area1);
01225 VectorCopy(v3, p1area2);
01226 }
01227 else if (x1 < x3)
01228 {
01229 y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1);
01230 dist1 = y3 - y;
01231 VectorCopy(v3, p1area1);
01232 p1area1[2] = y;
01233 VectorCopy(v3, p1area2);
01234 }
01235 else
01236 {
01237 y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3);
01238 dist1 = y - y1;
01239 VectorCopy(v1, p1area1);
01240 VectorCopy(v1, p1area2);
01241 p1area2[2] = y;
01242 }
01243
01244 if (x2 > x4 - 0.1 && x2 < x4 + 0.1)
01245 {
01246 dist2 = y4 - y2;
01247 VectorCopy(v2, p2area1);
01248 VectorCopy(v4, p2area2);
01249 }
01250 else if (x2 < x4)
01251 {
01252 y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3);
01253 dist2 = y - y2;
01254 VectorCopy(v2, p2area1);
01255 VectorCopy(v2, p2area2);
01256 p2area2[2] = y;
01257 }
01258 else
01259 {
01260 y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1);
01261 dist2 = y4 - y;
01262 VectorCopy(v4, p2area1);
01263 p2area1[2] = y;
01264 VectorCopy(v4, p2area2);
01265 }
01266 }
01267
01268
01269 if (dist1 > dist2 - 1 && dist1 < dist2 + 1)
01270 {
01271 dist = dist1;
01272 VectorAdd(p1area1, p2area1, start);
01273 VectorScale(start, 0.5, start);
01274 VectorAdd(p1area2, p2area2, end);
01275 VectorScale(end, 0.5, end);
01276 }
01277 else if (dist1 < dist2)
01278 {
01279 dist = dist1;
01280 VectorCopy(p1area1, start);
01281 VectorCopy(p1area2, end);
01282 }
01283 else
01284 {
01285 dist = dist2;
01286 VectorCopy(p2area1, start);
01287 VectorCopy(p2area2, end);
01288 }
01289
01290 VectorSubtract(p2area2, p1area2, dir);
01291 length = VectorLength(dir);
01292
01293 if (groundface1->faceflags & FACE_GROUND)
01294 {
01295
01296 if (dist < ground_bestdist ||
01297
01298
01299 (dist < ground_bestdist + 1 && length > ground_bestlength))
01300 {
01301 ground_bestdist = dist;
01302 ground_bestlength = length;
01303 ground_foundreach = qtrue;
01304 ground_bestarea2groundedgenum = edge1num;
01305 ground_bestface1 = groundface1;
01306
01307 VectorCopy(start, ground_beststart);
01308
01309 VectorCopy(normal, ground_bestnormal);
01310
01311 VectorCopy(end, ground_bestend);
01312 }
01313 }
01314 else
01315 {
01316
01317 if (dist < water_bestdist ||
01318
01319
01320 (dist < water_bestdist + 1 && length > water_bestlength))
01321 {
01322 water_bestdist = dist;
01323 water_bestlength = length;
01324 water_foundreach = qtrue;
01325 water_bestarea2groundedgenum = edge1num;
01326 water_bestface1 = groundface1;
01327
01328 VectorCopy(start, water_beststart);
01329
01330 VectorCopy(normal, water_bestnormal);
01331
01332 VectorCopy(end, water_bestend);
01333 }
01334 }
01335 }
01336 }
01337 }
01338 }
01339
01340
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354
01355
01356
01357
01358 if (ground_foundreach)
01359 {
01360
01361
01362 if (ground_bestdist >= 0 && ground_bestdist < aassettings.phys_maxstep)
01363 {
01364
01365 lreach = AAS_AllocReachability();
01366 if (!lreach) return qfalse;
01367 lreach->areanum = area2num;
01368 lreach->facenum = 0;
01369 lreach->edgenum = ground_bestarea2groundedgenum;
01370 VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
01371 VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
01372 lreach->traveltype = TRAVEL_WALK;
01373 lreach->traveltime = 0;
01374
01375 if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num))
01376 {
01377 lreach->traveltime += aassettings.rs_startcrouch;
01378 }
01379 lreach->next = areareachability[area1num];
01380 areareachability[area1num] = lreach;
01381
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391 reach_step++;
01392 return qtrue;
01393 }
01394 }
01395
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
01414 if (water_foundreach)
01415 {
01416
01417 VectorMA(water_bestend, -INSIDEUNITS, water_bestnormal, testpoint);
01418
01419 testpoint[2] -= aassettings.phys_maxwaterjump;
01420
01421 if (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID)
01422 {
01423
01424
01425 if (water_bestdist < aassettings.phys_maxwaterjump + 24)
01426 {
01427
01428 if ((aasworld.areasettings[area1num].presencetype & PRESENCE_NORMAL) &&
01429 (aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL))
01430 {
01431
01432 lreach = AAS_AllocReachability();
01433 if (!lreach) return qfalse;
01434 lreach->areanum = area2num;
01435 lreach->facenum = 0;
01436 lreach->edgenum = water_bestarea2groundedgenum;
01437 VectorCopy(water_beststart, lreach->start);
01438 VectorMA(water_bestend, INSIDEUNITS_WATERJUMP, water_bestnormal, lreach->end);
01439 lreach->traveltype = TRAVEL_WATERJUMP;
01440 lreach->traveltime = aassettings.rs_waterjump;
01441 lreach->next = areareachability[area1num];
01442 areareachability[area1num] = lreach;
01443
01444 reach_waterjump++;
01445 return qtrue;
01446 }
01447 }
01448 }
01449 }
01450
01451
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468 if (ground_foundreach)
01469 {
01470
01471 if (ground_bestdist > 0 && ground_bestdist < aassettings.phys_maxbarrier)
01472 {
01473
01474 if (!water_foundreach || (ground_bestdist - water_bestdist < 16))
01475 {
01476
01477 if (!AAS_AreaCrouch(area1num) && !AAS_AreaCrouch(area2num))
01478 {
01479
01480 lreach = AAS_AllocReachability();
01481 if (!lreach) return qfalse;
01482 lreach->areanum = area2num;
01483 lreach->facenum = 0;
01484 lreach->edgenum = ground_bestarea2groundedgenum;
01485 VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
01486 VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
01487 lreach->traveltype = TRAVEL_BARRIERJUMP;
01488 lreach->traveltime = aassettings.rs_barrierjump;
01489 lreach->next = areareachability[area1num];
01490 areareachability[area1num] = lreach;
01491
01492 reach_barrier++;
01493 return qtrue;
01494 }
01495 }
01496 }
01497 }
01498
01499
01500
01501
01502
01503
01504
01505
01506
01507
01508
01509
01510
01511
01512
01513
01514
01515
01516
01517
01518
01519
01520 if (ground_foundreach)
01521 {
01522 if (ground_bestdist < 0)
01523 {
01524 if (ground_bestdist > -aassettings.phys_maxstep)
01525 {
01526
01527 lreach = AAS_AllocReachability();
01528 if (!lreach) return qfalse;
01529 lreach->areanum = area2num;
01530 lreach->facenum = 0;
01531 lreach->edgenum = ground_bestarea2groundedgenum;
01532 VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
01533 VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
01534 lreach->traveltype = TRAVEL_WALK;
01535 lreach->traveltime = 1;
01536 lreach->next = areareachability[area1num];
01537 areareachability[area1num] = lreach;
01538
01539 reach_walk++;
01540 return qtrue;
01541 }
01542
01543 if (!aassettings.rs_maxfallheight || fabs(ground_bestdist) < aassettings.rs_maxfallheight) {
01544
01545 VectorMA(ground_bestend, INSIDEUNITS, ground_bestnormal, ground_bestend);
01546 VectorCopy(ground_bestend, start);
01547 start[2] = ground_beststart[2];
01548 VectorCopy(ground_bestend, end);
01549 end[2] += 4;
01550 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
01551
01552 if (!trace.startsolid && trace.fraction >= 1.0)
01553 {
01554
01555 trace.endpos[2] += 1;
01556 if (AAS_PointAreaNum(trace.endpos) == area2num)
01557 {
01558
01559 numareas = AAS_TraceAreas(start, end, areas, NULL, sizeof(areas) / sizeof(int));
01560 for (i = 0; i < numareas; i++)
01561 if (AAS_AreaClusterPortal(areas[i]))
01562 break;
01563 if (i >= numareas)
01564 {
01565
01566 lreach = AAS_AllocReachability();
01567 if (!lreach) return qfalse;
01568 lreach->areanum = area2num;
01569 lreach->facenum = 0;
01570 lreach->edgenum = ground_bestarea2groundedgenum;
01571 VectorCopy(ground_beststart, lreach->start);
01572 VectorCopy(ground_bestend, lreach->end);
01573 lreach->traveltype = TRAVEL_WALKOFFLEDGE;
01574 lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(ground_bestdist) * 50 / aassettings.phys_gravity;
01575
01576 if (!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num))
01577 {
01578 if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta5)
01579 {
01580 lreach->traveltime += aassettings.rs_falldamage5;
01581 }
01582 if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta10)
01583 {
01584 lreach->traveltime += aassettings.rs_falldamage10;
01585 }
01586 }
01587 lreach->next = areareachability[area1num];
01588 areareachability[area1num] = lreach;
01589
01590 reach_walkoffledge++;
01591
01592
01593
01594 return qtrue;
01595 }
01596 }
01597 }
01598 }
01599 }
01600 }
01601 return qfalse;
01602 }
01603
01604
01605
01606
01607
01608
01609
01610 float VectorDistance(vec3_t v1, vec3_t v2)
01611 {
01612 vec3_t dir;
01613
01614 VectorSubtract(v2, v1, dir);
01615 return VectorLength(dir);
01616 }
01617
01618
01619
01620
01621
01622
01623
01624 int VectorBetweenVectors(vec3_t v, vec3_t v1, vec3_t v2)
01625 {
01626 vec3_t dir1, dir2;
01627
01628 VectorSubtract(v, v1, dir1);
01629 VectorSubtract(v, v2, dir2);
01630 return (DotProduct(dir1, dir2) <= 0);
01631 }
01632
01633
01634
01635
01636
01637
01638
01639 void VectorMiddle(vec3_t v1, vec3_t v2, vec3_t middle)
01640 {
01641 VectorAdd(v1, v2, middle);
01642 VectorScale(middle, 0.5, middle);
01643 }
01644
01645
01646
01647
01648
01649
01650
01651
01652
01653
01654
01655
01656
01657
01658
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675
01676
01677
01678
01679
01680
01681
01682
01683
01684
01685
01686
01687
01688
01689
01690
01691
01692
01693
01694
01695
01696
01697
01698
01699
01700
01701
01702
01703
01704
01705
01706
01707
01708
01709
01710
01711
01712
01713
01714
01715
01716
01717
01718
01719
01720
01721
01722
01723
01724
01725
01726
01727
01728
01729
01730
01731
01732
01733
01734
01735
01736
01737
01738
01739
01740
01741
01742
01743
01744
01745
01746
01747
01748
01749
01750
01751
01752
01753
01754
01755
01756
01757
01758
01759
01760
01761
01762
01763
01764
01765
01766
01767
01768
01769
01770
01771
01772
01773
01774
01775
01776
01777
01778
01779
01780
01781
01782
01783
01784
01785
01786
01787
01788
01789
01790
01791
01792
01793
01794
01795
01796
01797
01798
01799
01800
01801
01802
01803
01804
01805
01806
01807
01808
01809
01810
01811
01812
01813
01814
01815
01816
01817
01818
01819
01820
01821
01822
01823
01824
01825
01826
01827
01828
01829
01830
01831 float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4,
01832 aas_plane_t *plane1, aas_plane_t *plane2,
01833 vec3_t beststart1, vec3_t bestend1,
01834 vec3_t beststart2, vec3_t bestend2, float bestdist)
01835 {
01836 vec3_t dir1, dir2, p1, p2, p3, p4;
01837 float a1, a2, b1, b2, dist, dist1, dist2;
01838 int founddist;
01839
01840
01841 VectorSubtract(v2, v1, dir1);
01842 VectorSubtract(v4, v3, dir2);
01843
01844 dir1[2] = 0;
01845 dir2[2] = 0;
01846
01847
01848
01849
01850
01851
01852 if (dir2[0])
01853 {
01854 a2 = dir2[1] / dir2[0];
01855 b2 = v3[1] - a2 * v3[0];
01856
01857 p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
01858 p1[1] = a2 * p1[0] + b2;
01859
01860 p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
01861 p2[1] = a2 * p2[0] + b2;
01862 }
01863 else
01864 {
01865
01866 p1[0] = v3[0];
01867 p1[1] = v1[1];
01868
01869 p2[0] = v3[0];
01870 p2[1] = v2[1];
01871 }
01872
01873 if (dir1[0])
01874 {
01875
01876 a1 = dir1[1] / dir1[0];
01877 b1 = v1[1] - a1 * v1[0];
01878
01879 p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
01880 p3[1] = a1 * p3[0] + b1;
01881
01882 p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
01883 p4[1] = a1 * p4[0] + b1;
01884 }
01885 else
01886 {
01887
01888 p3[0] = v1[0];
01889 p3[1] = v3[1];
01890
01891 p4[0] = v1[0];
01892 p4[1] = v4[1];
01893 }
01894
01895 p1[2] = 0;
01896 p2[2] = 0;
01897 p3[2] = 0;
01898 p4[2] = 0;
01899
01900 p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2];
01901 p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2];
01902 p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2];
01903 p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2];
01904
01905 founddist = qfalse;
01906
01907 if (VectorBetweenVectors(p1, v3, v4))
01908 {
01909 dist = VectorDistance(v1, p1);
01910 if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
01911 {
01912 dist1 = VectorDistance(beststart1, v1);
01913 dist2 = VectorDistance(beststart2, v1);
01914 if (dist1 > dist2)
01915 {
01916 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart2);
01917 }
01918 else
01919 {
01920 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart1);
01921 }
01922 dist1 = VectorDistance(bestend1, p1);
01923 dist2 = VectorDistance(bestend2, p1);
01924 if (dist1 > dist2)
01925 {
01926 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend2);
01927 }
01928 else
01929 {
01930 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend1);
01931 }
01932 }
01933 else if (dist < bestdist)
01934 {
01935 bestdist = dist;
01936 VectorCopy(v1, beststart1);
01937 VectorCopy(v1, beststart2);
01938 VectorCopy(p1, bestend1);
01939 VectorCopy(p1, bestend2);
01940 }
01941 founddist = qtrue;
01942 }
01943 if (VectorBetweenVectors(p2, v3, v4))
01944 {
01945 dist = VectorDistance(v2, p2);
01946 if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
01947 {
01948 dist1 = VectorDistance(beststart1, v2);
01949 dist2 = VectorDistance(beststart2, v2);
01950 if (dist1 > dist2)
01951 {
01952 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart2);
01953 }
01954 else
01955 {
01956 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart1);
01957 }
01958 dist1 = VectorDistance(bestend1, p2);
01959 dist2 = VectorDistance(bestend2, p2);
01960 if (dist1 > dist2)
01961 {
01962 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend2);
01963 }
01964 else
01965 {
01966 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend1);
01967 }
01968 }
01969 else if (dist < bestdist)
01970 {
01971 bestdist = dist;
01972 VectorCopy(v2, beststart1);
01973 VectorCopy(v2, beststart2);
01974 VectorCopy(p2, bestend1);
01975 VectorCopy(p2, bestend2);
01976 }
01977 founddist = qtrue;
01978 }
01979 if (VectorBetweenVectors(p3, v1, v2))
01980 {
01981 dist = VectorDistance(v3, p3);
01982 if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
01983 {
01984 dist1 = VectorDistance(beststart1, p3);
01985 dist2 = VectorDistance(beststart2, p3);
01986 if (dist1 > dist2)
01987 {
01988 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart2);
01989 }
01990 else
01991 {
01992 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart1);
01993 }
01994 dist1 = VectorDistance(bestend1, v3);
01995 dist2 = VectorDistance(bestend2, v3);
01996 if (dist1 > dist2)
01997 {
01998 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend2);
01999 }
02000 else
02001 {
02002 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend1);
02003 }
02004 }
02005 else if (dist < bestdist)
02006 {
02007 bestdist = dist;
02008 VectorCopy(p3, beststart1);
02009 VectorCopy(p3, beststart2);
02010 VectorCopy(v3, bestend1);
02011 VectorCopy(v3, bestend2);
02012 }
02013 founddist = qtrue;
02014 }
02015 if (VectorBetweenVectors(p4, v1, v2))
02016 {
02017 dist = VectorDistance(v4, p4);
02018 if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
02019 {
02020 dist1 = VectorDistance(beststart1, p4);
02021 dist2 = VectorDistance(beststart2, p4);
02022 if (dist1 > dist2)
02023 {
02024 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart2);
02025 }
02026 else
02027 {
02028 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart1);
02029 }
02030 dist1 = VectorDistance(bestend1, v4);
02031 dist2 = VectorDistance(bestend2, v4);
02032 if (dist1 > dist2)
02033 {
02034 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend2);
02035 }
02036 else
02037 {
02038 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend1);
02039 }
02040 }
02041 else if (dist < bestdist)
02042 {
02043 bestdist = dist;
02044 VectorCopy(p4, beststart1);
02045 VectorCopy(p4, beststart2);
02046 VectorCopy(v4, bestend1);
02047 VectorCopy(v4, bestend2);
02048 }
02049 founddist = qtrue;
02050 }
02051
02052
02053 if (!founddist)
02054 {
02055 dist = VectorDistance(v1, v3);
02056 if (dist < bestdist)
02057 {
02058 bestdist = dist;
02059 VectorCopy(v1, beststart1);
02060 VectorCopy(v1, beststart2);
02061 VectorCopy(v3, bestend1);
02062 VectorCopy(v3, bestend2);
02063 }
02064 dist = VectorDistance(v1, v4);
02065 if (dist < bestdist)
02066 {
02067 bestdist = dist;
02068 VectorCopy(v1, beststart1);
02069 VectorCopy(v1, beststart2);
02070 VectorCopy(v4, bestend1);
02071 VectorCopy(v4, bestend2);
02072 }
02073 dist = VectorDistance(v2, v3);
02074 if (dist < bestdist)
02075 {
02076 bestdist = dist;
02077 VectorCopy(v2, beststart1);
02078 VectorCopy(v2, beststart2);
02079 VectorCopy(v3, bestend1);
02080 VectorCopy(v3, bestend2);
02081 }
02082 dist = VectorDistance(v2, v4);
02083 if (dist < bestdist)
02084 {
02085 bestdist = dist;
02086 VectorCopy(v2, beststart1);
02087 VectorCopy(v2, beststart2);
02088 VectorCopy(v4, bestend1);
02089 VectorCopy(v4, bestend2);
02090 }
02091 }
02092 return bestdist;
02093 }
02094
02095
02096
02097
02098
02099
02100
02101
02102
02103
02104
02105
02106
02107
02108
02109 int AAS_Reachability_Jump(int area1num, int area2num)
02110 {
02111 int i, j, k, l, face1num, face2num, edge1num, edge2num, traveltype;
02112 int stopevent, areas[10], numareas;
02113 float phys_jumpvel, maxjumpdistance, maxjumpheight, height, bestdist, speed;
02114 vec_t *v1, *v2, *v3, *v4;
02115 vec3_t beststart, beststart2, bestend, bestend2;
02116 vec3_t teststart, testend, dir, velocity, cmdmove, up = {0, 0, 1}, sidewards;
02117 aas_area_t *area1, *area2;
02118 aas_face_t *face1, *face2;
02119 aas_edge_t *edge1, *edge2;
02120 aas_plane_t *plane1, *plane2, *plane;
02121 aas_trace_t trace;
02122 aas_clientmove_t move;
02123 aas_lreachability_t *lreach;
02124
02125 if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse;
02126
02127 if (AAS_AreaCrouch(area1num) || AAS_AreaCrouch(area2num)) return qfalse;
02128
02129 area1 = &aasworld.areas[area1num];
02130 area2 = &aasworld.areas[area2num];
02131
02132 phys_jumpvel = aassettings.phys_jumpvel;
02133
02134 maxjumpdistance = 2 * AAS_MaxJumpDistance(phys_jumpvel);
02135
02136 maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel);
02137
02138
02139 for (i = 0; i < 2; i++)
02140 {
02141 if (area1->mins[i] > area2->maxs[i] + maxjumpdistance) return qfalse;
02142 if (area1->maxs[i] < area2->mins[i] - maxjumpdistance) return qfalse;
02143 }
02144
02145 if (area2->mins[2] > area1->maxs[2] + maxjumpheight) return qfalse;
02146
02147 bestdist = 999999;
02148
02149 for (i = 0; i < area1->numfaces; i++)
02150 {
02151 face1num = aasworld.faceindex[area1->firstface + i];
02152 face1 = &aasworld.faces[abs(face1num)];
02153
02154 if (!(face1->faceflags & FACE_GROUND)) continue;
02155
02156 for (j = 0; j < area2->numfaces; j++)
02157 {
02158 face2num = aasworld.faceindex[area2->firstface + j];
02159 face2 = &aasworld.faces[abs(face2num)];
02160
02161 if (!(face2->faceflags & FACE_GROUND)) continue;
02162
02163 for (k = 0; k < face1->numedges; k++)
02164 {
02165 edge1num = abs(aasworld.edgeindex[face1->firstedge + k]);
02166 edge1 = &aasworld.edges[edge1num];
02167 for (l = 0; l < face2->numedges; l++)
02168 {
02169 edge2num = abs(aasworld.edgeindex[face2->firstedge + l]);
02170 edge2 = &aasworld.edges[edge2num];
02171
02172 v1 = aasworld.vertexes[edge1->v[0]];
02173 v2 = aasworld.vertexes[edge1->v[1]];
02174 v3 = aasworld.vertexes[edge2->v[0]];
02175 v4 = aasworld.vertexes[edge2->v[1]];
02176
02177 plane1 = &aasworld.planes[face1->planenum];
02178 plane2 = &aasworld.planes[face2->planenum];
02179
02180 bestdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, plane1, plane2,
02181 beststart, bestend,
02182 beststart2, bestend2, bestdist);
02183 }
02184 }
02185 }
02186 }
02187 VectorMiddle(beststart, beststart2, beststart);
02188 VectorMiddle(bestend, bestend2, bestend);
02189 if (bestdist > 4 && bestdist < maxjumpdistance)
02190 {
02191
02192
02193 if (bestdist <= 48 && fabs(beststart[2] - bestend[2]) < 8)
02194 {
02195 speed = 400;
02196 traveltype = TRAVEL_WALKOFFLEDGE;
02197 }
02198 else if (AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed))
02199 {
02200
02201 speed *= 1.2f;
02202 traveltype = TRAVEL_WALKOFFLEDGE;
02203 }
02204 else
02205 {
02206
02207
02208 if (!AAS_HorizontalVelocityForJump(phys_jumpvel, beststart, bestend, &speed))
02209 return qfalse;
02210 speed *= 1.05f;
02211 traveltype = TRAVEL_JUMP;
02212
02213
02214 VectorSubtract(bestend, beststart, dir);
02215 dir[2] = 0;
02216 if (VectorLength(dir) < 10)
02217 return qfalse;
02218 }
02219
02220 VectorSubtract(bestend, beststart, dir);
02221 VectorNormalize(dir);
02222 VectorMA(beststart, 1, dir, teststart);
02223
02224 VectorCopy(teststart, testend);
02225 testend[2] -= 100;
02226 trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1);
02227
02228 if (trace.startsolid)
02229 return qfalse;
02230 if (trace.fraction < 1)
02231 {
02232 plane = &aasworld.planes[trace.planenum];
02233
02234 if (DotProduct(plane->normal, up) >= 0.7)
02235 {
02236
02237 if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME)))
02238 {
02239 if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier)
02240 return qfalse;
02241 }
02242 }
02243 }
02244
02245 VectorMA(bestend, -1, dir, teststart);
02246
02247 VectorCopy(teststart, testend);
02248 testend[2] -= 100;
02249 trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1);
02250
02251 if (trace.startsolid)
02252 return qfalse;
02253 if (trace.fraction < 1)
02254 {
02255 plane = &aasworld.planes[trace.planenum];
02256
02257 if (DotProduct(plane->normal, up) >= 0.7)
02258 {
02259
02260 if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME)))
02261 {
02262 if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier)
02263 return qfalse;
02264 }
02265 }
02266 }
02267
02268
02269 VectorClear(cmdmove);
02270 if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)
02271 cmdmove[2] = aassettings.phys_jumpvel;
02272 else
02273 cmdmove[2] = 0;
02274
02275 VectorSubtract(bestend, beststart, dir);
02276 dir[2] = 0;
02277 VectorNormalize(dir);
02278 CrossProduct(dir, up, sidewards);
02279
02280 stopevent = SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE;
02281 if (!AAS_AreaClusterPortal(area1num) && !AAS_AreaClusterPortal(area2num))
02282 stopevent |= SE_TOUCHCLUSTERPORTAL;
02283
02284 for (i = 0; i < 3; i++)
02285 {
02286
02287 if (i == 1)
02288 VectorAdd(testend, sidewards, testend);
02289 else if (i == 2)
02290 VectorSubtract(bestend, sidewards, testend);
02291 else
02292 VectorCopy(bestend, testend);
02293 VectorSubtract(testend, beststart, dir);
02294 dir[2] = 0;
02295 VectorNormalize(dir);
02296 VectorScale(dir, speed, velocity);
02297
02298 AAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue,
02299 velocity, cmdmove, 3, 30, 0.1f,
02300 stopevent, 0, qfalse);
02301
02302 if (move.frames >= 30)
02303 return qfalse;
02304
02305 if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA))
02306 return qfalse;
02307
02308 if (move.stopevent & SE_TOUCHCLUSTERPORTAL)
02309 return qfalse;
02310
02311
02312 VectorMA(move.endpos, -64, dir, teststart);
02313 teststart[2] += 1;
02314 numareas = AAS_TraceAreas(move.endpos, teststart, areas, NULL, sizeof(areas) / sizeof(int));
02315 for (j = 0; j < numareas; j++)
02316 {
02317 if (areas[j] == area2num)
02318 break;
02319 }
02320 if (j < numareas)
02321 break;
02322 }
02323 if (i >= 3)
02324 return qfalse;
02325
02326 #ifdef REACH_DEBUG
02327
02328 Log_Write("jump reachability between %d and %d\r\n", area1num, area2num);
02329 #endif //REACH_DEBUG
02330
02331 lreach = AAS_AllocReachability();
02332 if (!lreach) return qfalse;
02333 lreach->areanum = area2num;
02334 lreach->facenum = 0;
02335 lreach->edgenum = 0;
02336 VectorCopy(beststart, lreach->start);
02337 VectorCopy(bestend, lreach->end);
02338 lreach->traveltype = traveltype;
02339
02340 VectorSubtract(bestend, beststart, dir);
02341 height = dir[2];
02342 dir[2] = 0;
02343 if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir))
02344 {
02345 lreach->traveltime = aassettings.rs_startwalkoffledge + height * 50 / aassettings.phys_gravity;
02346 }
02347 else
02348 {
02349 lreach->traveltime = aassettings.rs_startjump + VectorDistance(bestend, beststart) * 240 / aassettings.phys_maxwalkvelocity;
02350 }
02351
02352 if (!AAS_AreaJumpPad(area2num))
02353 {
02354 if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta5)
02355 {
02356 lreach->traveltime += aassettings.rs_falldamage5;
02357 }
02358 else if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta10)
02359 {
02360 lreach->traveltime += aassettings.rs_falldamage10;
02361 }
02362 }
02363 lreach->next = areareachability[area1num];
02364 areareachability[area1num] = lreach;
02365
02366 if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)
02367 reach_jump++;
02368 else
02369 reach_walkoffledge++;
02370 }
02371 return qfalse;
02372 }
02373
02374
02375
02376
02377
02378
02379
02380 int AAS_Reachability_Ladder(int area1num, int area2num)
02381 {
02382 int i, j, k, l, edge1num, edge2num, sharededgenum, lowestedgenum;
02383 int face1num, face2num, ladderface1num, ladderface2num;
02384 int ladderface1vertical, ladderface2vertical, firstv;
02385 float face1area, face2area, bestface1area, bestface2area;
02386 float phys_jumpvel, maxjumpheight;
02387 vec3_t area1point, area2point, v1, v2, up = {0, 0, 1};
02388 vec3_t mid, lowestpoint, start, end, sharededgevec, dir;
02389 aas_area_t *area1, *area2;
02390 aas_face_t *face1, *face2, *ladderface1, *ladderface2;
02391 aas_plane_t *plane1, *plane2;
02392 aas_edge_t *sharededge, *edge1;
02393 aas_lreachability_t *lreach;
02394 aas_trace_t trace;
02395
02396 if (!AAS_AreaLadder(area1num) || !AAS_AreaLadder(area2num)) return qfalse;
02397
02398 phys_jumpvel = aassettings.phys_jumpvel;
02399
02400 maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel);
02401
02402 area1 = &aasworld.areas[area1num];
02403 area2 = &aasworld.areas[area2num];
02404
02405 ladderface1 = NULL;
02406 ladderface2 = NULL;
02407 ladderface1num = 0;
02408 ladderface2num = 0;
02409 bestface1area = -9999;
02410 bestface2area = -9999;
02411 sharededgenum = 0;
02412 lowestedgenum = 0;
02413
02414 for (i = 0; i < area1->numfaces; i++)
02415 {
02416 face1num = aasworld.faceindex[area1->firstface + i];
02417 face1 = &aasworld.faces[abs(face1num)];
02418
02419 if (!(face1->faceflags & FACE_LADDER)) continue;
02420
02421 for (j = 0; j < area2->numfaces; j++)
02422 {
02423 face2num = aasworld.faceindex[area2->firstface + j];
02424 face2 = &aasworld.faces[abs(face2num)];
02425
02426 if (!(face2->faceflags & FACE_LADDER)) continue;
02427
02428 for (k = 0; k < face1->numedges; k++)
02429 {
02430 edge1num = aasworld.edgeindex[face1->firstedge + k];
02431 for (l = 0; l < face2->numedges; l++)
02432 {
02433 edge2num = aasworld.edgeindex[face2->firstedge + l];
02434 if (abs(edge1num) == abs(edge2num))
02435 {
02436
02437 face1area = AAS_FaceArea(face1);
02438 face2area = AAS_FaceArea(face2);
02439 if (face1area > bestface1area && face2area > bestface2area)
02440 {
02441 bestface1area = face1area;
02442 bestface2area = face2area;
02443 ladderface1 = face1;
02444 ladderface2 = face2;
02445 ladderface1num = face1num;
02446 ladderface2num = face2num;
02447 sharededgenum = edge1num;
02448 }
02449 break;
02450 }
02451 }
02452 if (l != face2->numedges) break;
02453 }
02454 }
02455 }
02456
02457 if (ladderface1 && ladderface2)
02458 {
02459
02460 sharededge = &aasworld.edges[abs(sharededgenum)];
02461 firstv = sharededgenum < 0;
02462
02463 VectorCopy(aasworld.vertexes[sharededge->v[firstv]], v1);
02464 VectorCopy(aasworld.vertexes[sharededge->v[!firstv]], v2);
02465 VectorAdd(v1, v2, area1point);
02466 VectorScale(area1point, 0.5, area1point);
02467 VectorCopy(area1point, area2point);
02468
02469
02470 plane1 = &aasworld.planes[ladderface1->planenum ^ (ladderface1num < 0)];
02471 plane2 = &aasworld.planes[ladderface2->planenum ^ (ladderface2num < 0)];
02472
02473
02474 VectorSubtract(v2, v1, sharededgevec);
02475 CrossProduct(plane1->normal, sharededgevec, dir);
02476 VectorNormalize(dir);
02477
02478 VectorMA(area1point, -32, dir, area1point);
02479 VectorMA(area2point, 32, dir, area2point);
02480
02481 ladderface1vertical = abs(DotProduct(plane1->normal, up)) < 0.1;
02482 ladderface2vertical = abs(DotProduct(plane2->normal, up)) < 0.1;
02483
02484 if (!ladderface1vertical && !ladderface2vertical) return qfalse;
02485
02486 if (ladderface1vertical && ladderface2vertical
02487
02488 && DotProduct(plane1->normal, plane2->normal) > 0.7
02489
02490 && abs(DotProduct(sharededgevec, up)) < 0.7)
02491 {
02492
02493 lreach = AAS_AllocReachability();
02494 if (!lreach) return qfalse;
02495 lreach->areanum = area2num;
02496 lreach->facenum = ladderface1num;
02497 lreach->edgenum = abs(sharededgenum);
02498 VectorCopy(area1point, lreach->start);
02499
02500 VectorMA(area2point, -3, plane1->normal, lreach->end);
02501 lreach->traveltype = TRAVEL_LADDER;
02502 lreach->traveltime = 10;
02503 lreach->next = areareachability[area1num];
02504 areareachability[area1num] = lreach;
02505
02506 reach_ladder++;
02507
02508 lreach = AAS_AllocReachability();
02509 if (!lreach) return qfalse;
02510 lreach->areanum = area1num;
02511 lreach->facenum = ladderface2num;
02512 lreach->edgenum = abs(sharededgenum);
02513 VectorCopy(area2point, lreach->start);
02514
02515 VectorMA(area1point, -3, plane1->normal, lreach->end);
02516 lreach->traveltype = TRAVEL_LADDER;
02517 lreach->traveltime = 10;
02518 lreach->next = areareachability[area2num];
02519 areareachability[area2num] = lreach;
02520
02521 reach_ladder++;
02522
02523 return qtrue;
02524 }
02525
02526
02527
02528 if (ladderface1vertical && (ladderface2->faceflags & FACE_GROUND))
02529 {
02530
02531 lreach = AAS_AllocReachability();
02532 if (!lreach) return qfalse;
02533 lreach->areanum = area2num;
02534 lreach->facenum = ladderface1num;
02535 lreach->edgenum = abs(sharededgenum);
02536 VectorCopy(area1point, lreach->start);
02537 VectorCopy(area2point, lreach->end);
02538 lreach->end[2] += 16;
02539 VectorMA(lreach->end, -15, plane1->normal, lreach->end);
02540 lreach->traveltype = TRAVEL_LADDER;
02541 lreach->traveltime = 10;
02542 lreach->next = areareachability[area1num];
02543 areareachability[area1num] = lreach;
02544
02545 reach_ladder++;
02546
02547 lreach = AAS_AllocReachability();
02548 if (!lreach) return qfalse;
02549 lreach->areanum = area1num;
02550 lreach->facenum = ladderface2num;
02551 lreach->edgenum = abs(sharededgenum);
02552 VectorCopy(area2point, lreach->start);
02553 VectorCopy(area1point, lreach->end);
02554 lreach->traveltype = TRAVEL_WALKOFFLEDGE;
02555 lreach->traveltime = 10;
02556 lreach->next = areareachability[area2num];
02557 areareachability[area2num] = lreach;
02558
02559 reach_walkoffledge++;
02560
02561 return qtrue;
02562 }
02563
02564 if (ladderface1vertical)
02565 {
02566
02567 lowestpoint[2] = 99999;
02568 for (i = 0; i < ladderface1->numedges; i++)
02569 {
02570 edge1num = abs(aasworld.edgeindex[ladderface1->firstedge + i]);
02571 edge1 = &aasworld.edges[edge1num];
02572
02573 VectorCopy(aasworld.vertexes[edge1->v[0]], v1);
02574 VectorCopy(aasworld.vertexes[edge1->v[1]], v2);
02575
02576 VectorAdd(v1, v2, mid);
02577 VectorScale(mid, 0.5, mid);
02578
02579 if (mid[2] < lowestpoint[2])
02580 {
02581 VectorCopy(mid, lowestpoint);
02582 lowestedgenum = edge1num;
02583 }
02584 }
02585
02586 plane1 = &aasworld.planes[ladderface1->planenum];
02587
02588 VectorMA(lowestpoint, 5, plane1->normal, start);
02589 VectorCopy(start, end);
02590 start[2] += 5;
02591 end[2] -= 100;
02592
02593 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
02594
02595
02596 #ifdef REACH_DEBUG
02597 if (trace.startsolid)
02598 {
02599 Log_Write("trace from area %d started in solid\r\n", area1num);
02600 }
02601 #endif //REACH_DEBUG
02602
02603 trace.endpos[2] += 1;
02604 area2num = AAS_PointAreaNum(trace.endpos);
02605
02606 area2 = &aasworld.areas[area2num];
02607 for (i = 0; i < area2->numfaces; i++)
02608 {
02609 face2num = aasworld.faceindex[area2->firstface + i];
02610 face2 = &aasworld.faces[abs(face2num)];
02611
02612 if (face2->faceflags & FACE_LADDER)
02613 {
02614 plane2 = &aasworld.planes[face2->planenum];
02615 if (abs(DotProduct(plane2->normal, up)) < 0.1) break;
02616 }
02617 }
02618
02619 if (i >= area2->numfaces && area2num != area1num &&
02620
02621 !AAS_ReachabilityExists(area1num, area2num) &&
02622 !AAS_ReachabilityExists(area2num, area1num))
02623 {
02624
02625 if (start[2] - trace.endpos[2] < maxjumpheight)
02626 {
02627
02628 lreach = AAS_AllocReachability();
02629 if (!lreach) return qfalse;
02630 lreach->areanum = area2num;
02631 lreach->facenum = ladderface1num;
02632 lreach->edgenum = lowestedgenum;
02633 VectorCopy(lowestpoint, lreach->start);
02634 VectorCopy(trace.endpos, lreach->end);
02635 lreach->traveltype = TRAVEL_LADDER;
02636 lreach->traveltime = 10;
02637 lreach->next = areareachability[area1num];
02638 areareachability[area1num] = lreach;
02639
02640 reach_ladder++;
02641
02642 lreach = AAS_AllocReachability();
02643 if (!lreach) return qfalse;
02644 lreach->areanum = area1num;
02645 lreach->facenum = ladderface1num;
02646 lreach->edgenum = lowestedgenum;
02647 VectorCopy(trace.endpos, lreach->start);
02648
02649 VectorMA(lowestpoint, -5, plane1->normal, lreach->end);
02650
02651 lreach->end[2] += 10;
02652 lreach->traveltype = TRAVEL_JUMP;
02653 lreach->traveltime = 10;
02654 lreach->next = areareachability[area2num];
02655 areareachability[area2num] = lreach;
02656
02657 reach_jump++;
02658
02659 return qtrue;
02660 #ifdef REACH_DEBUG
02661 Log_Write("jump up to ladder reach between %d and %d\r\n", area2num, area1num);
02662 #endif //REACH_DEBUG
02663 }
02664 #ifdef REACH_DEBUG
02665 else Log_Write("jump too high between area %d and %d\r\n", area2num, area1num);
02666 #endif //REACH_DEBUG
02667 }
02668
02669
02670
02671
02672
02673
02674
02675
02676
02677
02678
02679
02680
02681
02682
02683
02684
02685
02686
02687
02688
02689
02690
02691
02692
02693
02694
02695
02696
02697
02698
02699
02700
02701
02702
02703
02704
02705
02706
02707
02708
02709
02710
02711
02712
02713 }
02714 }
02715 return qfalse;
02716 }
02717
02718
02719
02720
02721
02722
02723 int AAS_TravelFlagsForTeam(int ent)
02724 {
02725 int notteam;
02726
02727 if (!AAS_IntForBSPEpairKey(ent, "bot_notteam", ¬team))
02728 return 0;
02729 if (notteam == 1)
02730 return TRAVELFLAG_NOTTEAM1;
02731 if (notteam == 2)
02732 return TRAVELFLAG_NOTTEAM2;
02733 return 0;
02734 }
02735
02736
02737
02738
02739
02740
02741
02742
02743
02744
02745
02746
02747
02748
02749
02750
02751
02752
02753 void AAS_Reachability_Teleport(void)
02754 {
02755 int area1num, area2num;
02756 char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY];
02757 char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
02758 int ent, dest;
02759 float angle;
02760 vec3_t origin, destorigin, mins, maxs, end, angles;
02761 vec3_t mid, velocity, cmdmove;
02762 aas_lreachability_t *lreach;
02763 aas_clientmove_t move;
02764 aas_trace_t trace;
02765 aas_link_t *areas, *link;
02766
02767 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
02768 {
02769 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
02770 if (!strcmp(classname, "trigger_multiple"))
02771 {
02772 AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
02773
02774 botimport.Print(PRT_MESSAGE, "trigger_multiple model = \"%s\"\n", model);
02775
02776 VectorClear(angles);
02777 AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin);
02778
02779 if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY))
02780 {
02781 botimport.Print(PRT_ERROR, "trigger_multiple at %1.0f %1.0f %1.0f without target\n",
02782 origin[0], origin[1], origin[2]);
02783 continue;
02784 }
02785 for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest))
02786 {
02787 if (!AAS_ValueForBSPEpairKey(dest, "classname", classname, MAX_EPAIRKEY)) continue;
02788 if (!strcmp(classname, "target_teleporter"))
02789 {
02790 if (!AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) continue;
02791 if (!strcmp(targetname, target))
02792 {
02793 break;
02794 }
02795 }
02796 }
02797 if (!dest)
02798 {
02799 continue;
02800 }
02801 if (!AAS_ValueForBSPEpairKey(dest, "target", target, MAX_EPAIRKEY))
02802 {
02803 botimport.Print(PRT_ERROR, "target_teleporter without target\n");
02804 continue;
02805 }
02806 }
02807 else if (!strcmp(classname, "trigger_teleport"))
02808 {
02809 AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
02810
02811 botimport.Print(PRT_MESSAGE, "trigger_teleport model = \"%s\"\n", model);
02812
02813 VectorClear(angles);
02814 AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin);
02815
02816 if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY))
02817 {
02818 botimport.Print(PRT_ERROR, "trigger_teleport at %1.0f %1.0f %1.0f without target\n",
02819 origin[0], origin[1], origin[2]);
02820 continue;
02821 }
02822 }
02823 else
02824 {
02825 continue;
02826 }
02827
02828 for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest))
02829 {
02830
02831
02832
02833 if (AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY))
02834 {
02835 if (!strcmp(targetname, target))
02836 {
02837 break;
02838 }
02839 }
02840 }
02841 if (!dest)
02842 {
02843 botimport.Print(PRT_ERROR, "teleporter without misc_teleporter_dest (%s)\n", target);
02844 continue;
02845 }
02846 if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin))
02847 {
02848 botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target);
02849 continue;
02850 }
02851
02852 area2num = AAS_PointAreaNum(destorigin);
02853
02854 if (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num))
02855 {
02856 VectorCopy(destorigin, end);
02857 end[2] -= 64;
02858 trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1);
02859 if (trace.startsolid)
02860 {
02861 botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target);
02862 continue;
02863 }
02864 area2num = AAS_PointAreaNum(trace.endpos);
02865
02866
02867
02868
02869
02870
02871
02872
02873
02874 {
02875
02876 AAS_FloatForBSPEpairKey(dest, "angle", &angle);
02877 if (angle)
02878 {
02879 VectorSet(angles, 0, angle, 0);
02880 AngleVectors(angles, velocity, NULL, NULL);
02881 VectorScale(velocity, 400, velocity);
02882 }
02883 else
02884 {
02885 VectorClear(velocity);
02886 }
02887 VectorClear(cmdmove);
02888 AAS_PredictClientMovement(&move, -1, destorigin, PRESENCE_NORMAL, qfalse,
02889 velocity, cmdmove, 0, 30, 0.1f,
02890 SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|
02891 SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, qfalse);
02892 area2num = AAS_PointAreaNum(move.endpos);
02893 if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA))
02894 {
02895 botimport.Print(PRT_WARNING, "teleported into slime or lava at dest %s\n", target);
02896 }
02897 VectorCopy(move.endpos, destorigin);
02898 }
02899 }
02900
02901
02902
02903
02904 VectorAdd(origin, mins, mins);
02905 VectorAdd(origin, maxs, maxs);
02906
02907 VectorAdd(mins, maxs, mid);
02908 VectorScale(mid, 0.5, mid);
02909
02910 areas = AAS_LinkEntityClientBBox(mins, maxs, -1, PRESENCE_CROUCH);
02911 if (!areas) botimport.Print(PRT_MESSAGE, "trigger_multiple not in any area\n");
02912
02913 for (link = areas; link; link = link->next_area)
02914 {
02915
02916 if (!AAS_AreaTeleporter(link->areanum)) continue;
02917
02918 area1num = link->areanum;
02919
02920 lreach = AAS_AllocReachability();
02921 if (!lreach) break;
02922 lreach->areanum = area2num;
02923 lreach->facenum = 0;
02924 lreach->edgenum = 0;
02925 VectorCopy(mid, lreach->start);
02926 VectorCopy(destorigin, lreach->end);
02927 lreach->traveltype = TRAVEL_TELEPORT;
02928 lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
02929 lreach->traveltime = aassettings.rs_teleport;
02930 lreach->next = areareachability[area1num];
02931 areareachability[area1num] = lreach;
02932
02933 reach_teleport++;
02934 }
02935
02936 AAS_UnlinkFromAreas(areas);
02937 }
02938 }
02939
02940
02941
02942
02943
02944
02945
02946
02947 void AAS_Reachability_Elevator(void)
02948 {
02949 int area1num, area2num, modelnum, i, j, k, l, n, p;
02950 float lip, height, speed;
02951 char model[MAX_EPAIRKEY], classname[MAX_EPAIRKEY];
02952 int ent;
02953 vec3_t mins, maxs, origin, angles = {0, 0, 0};
02954 vec3_t pos1, pos2, mids, platbottom, plattop;
02955 vec3_t bottomorg, toporg, start, end, dir;
02956 vec_t xvals[8], yvals[8], xvals_top[8], yvals_top[8];
02957 aas_lreachability_t *lreach;
02958 aas_trace_t trace;
02959
02960 #ifdef REACH_DEBUG
02961 Log_Write("AAS_Reachability_Elevator\r\n");
02962 #endif //REACH_DEBUG
02963 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
02964 {
02965 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
02966 if (!strcmp(classname, "func_plat"))
02967 {
02968 #ifdef REACH_DEBUG
02969 Log_Write("found func plat\r\n");
02970 #endif //REACH_DEBUG
02971 if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY))
02972 {
02973 botimport.Print(PRT_ERROR, "func_plat without model\n");
02974 continue;
02975 }
02976
02977 modelnum = atoi(model+1);
02978 if (modelnum <= 0)
02979 {
02980 botimport.Print(PRT_ERROR, "func_plat with invalid model number\n");
02981 continue;
02982 }
02983
02984
02985
02986 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
02987
02988 AAS_VectorForBSPEpairKey(ent, "origin", origin);
02989
02990 VectorCopy(origin, pos1);
02991 VectorCopy(origin, pos2);
02992
02993 AAS_FloatForBSPEpairKey(ent, "lip", &lip);
02994 if (!lip) lip = 8;
02995
02996 AAS_FloatForBSPEpairKey(ent, "height", &height);
02997 if (!height) height = (maxs[2] - mins[2]) - lip;
02998
02999 AAS_FloatForBSPEpairKey(ent, "speed", &speed);
03000 if (!speed) speed = 200;
03001
03002 pos2[2] -= height;
03003
03004
03005 VectorAdd(mins, maxs, mids);
03006 VectorMA(pos2, 0.5, mids, platbottom);
03007 platbottom[2] = maxs[2] - (pos1[2] - pos2[2]) + 2;
03008
03009 VectorAdd(mins, maxs, mids);
03010 VectorMA(pos2, 0.5, mids, plattop);
03011 plattop[2] = maxs[2] + 2;
03012
03013
03014
03015
03016
03017
03018
03019 for (i = 0; i < 3; i++)
03020 {
03021 mins[i] -= 1;
03022 maxs[i] += 1;
03023 }
03024
03025
03026
03027 VectorAdd(mins, maxs, mids);
03028 VectorScale(mids, 0.5, mids);
03029
03030 xvals[0] = mins[0]; xvals[1] = mids[0]; xvals[2] = maxs[0]; xvals[3] = mids[0];
03031 yvals[0] = mids[1]; yvals[1] = maxs[1]; yvals[2] = mids[1]; yvals[3] = mins[1];
03032
03033 xvals[4] = mins[0]; xvals[5] = maxs[0]; xvals[6] = maxs[0]; xvals[7] = mins[0];
03034 yvals[4] = maxs[1]; yvals[5] = maxs[1]; yvals[6] = mins[1]; yvals[7] = mins[1];
03035
03036 for (i = 0; i < 9; i++)
03037 {
03038 if (i < 8)
03039 {
03040 bottomorg[0] = origin[0] + xvals[i];
03041 bottomorg[1] = origin[1] + yvals[i];
03042 bottomorg[2] = platbottom[2] + 16;
03043
03044 area1num = AAS_PointAreaNum(bottomorg);
03045 for (k = 0; k < 16; k++)
03046 {
03047 if (area1num)
03048 {
03049 if (AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) break;
03050 }
03051 bottomorg[2] += 4;
03052 area1num = AAS_PointAreaNum(bottomorg);
03053 }
03054
03055 if (k >= 16)
03056 {
03057 continue;
03058 }
03059 }
03060 else
03061 {
03062 VectorCopy(plattop, bottomorg);
03063 bottomorg[2] += 24;
03064 area1num = AAS_PointAreaNum(bottomorg);
03065 if (!area1num) continue;
03066 VectorCopy(platbottom, bottomorg);
03067 bottomorg[2] += 24;
03068 }
03069
03070
03071 for (n = 0; n < 3; n++)
03072 {
03073 for (k = 0; k < 3; k++)
03074 {
03075 mins[k] -= 4;
03076 maxs[k] += 4;
03077 }
03078 xvals_top[0] = mins[0]; xvals_top[1] = mids[0]; xvals_top[2] = maxs[0]; xvals_top[3] = mids[0];
03079 yvals_top[0] = mids[1]; yvals_top[1] = maxs[1]; yvals_top[2] = mids[1]; yvals_top[3] = mins[1];
03080
03081 xvals_top[4] = mins[0]; xvals_top[5] = maxs[0]; xvals_top[6] = maxs[0]; xvals_top[7] = mins[0];
03082 yvals_top[4] = maxs[1]; yvals_top[5] = maxs[1]; yvals_top[6] = mins[1]; yvals_top[7] = mins[1];
03083
03084 for (j = 0; j < 8; j++)
03085 {
03086 toporg[0] = origin[0] + xvals_top[j];
03087 toporg[1] = origin[1] + yvals_top[j];
03088 toporg[2] = plattop[2] + 16;
03089
03090 area2num = AAS_PointAreaNum(toporg);
03091 for (l = 0; l < 16; l++)
03092 {
03093 if (area2num)
03094 {
03095 if (AAS_AreaGrounded(area2num) || AAS_AreaSwim(area2num))
03096 {
03097 VectorCopy(plattop, start);
03098 start[2] += 32;
03099 VectorCopy(toporg, end);
03100 end[2] += 1;
03101 trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
03102 if (trace.fraction >= 1) break;
03103 }
03104 }
03105 toporg[2] += 4;
03106 area2num = AAS_PointAreaNum(toporg);
03107 }
03108
03109 if (l >= 16) continue;
03110
03111 if (area2num == area1num) continue;
03112
03113 if (!AAS_AreaGrounded(area2num)) continue;
03114
03115 if (AAS_ReachabilityExists(area1num, area2num)) continue;
03116
03117 VectorSubtract(bottomorg, platbottom, dir);
03118 VectorNormalize(dir);
03119 dir[0] = bottomorg[0] + 24 * dir[0];
03120 dir[1] = bottomorg[1] + 24 * dir[1];
03121 dir[2] = bottomorg[2];
03122
03123 for (p = 0; p < 3; p++)
03124 if (dir[p] < origin[p] + mins[p] || dir[p] > origin[p] + maxs[p]) break;
03125 if (p >= 3) continue;
03126
03127 lreach = AAS_AllocReachability();
03128 if (!lreach) continue;
03129 lreach->areanum = area2num;
03130
03131 lreach->facenum = modelnum;
03132
03133 lreach->edgenum = (int) height;
03134
03135 VectorCopy(dir, lreach->start);
03136 VectorCopy(toporg, lreach->end);
03137 lreach->traveltype = TRAVEL_ELEVATOR;
03138 lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
03139 lreach->traveltime = aassettings.rs_startelevator + height * 100 / speed;
03140 lreach->next = areareachability[area1num];
03141 areareachability[area1num] = lreach;
03142
03143 n = 9999;
03144
03145 #ifdef REACH_DEBUG
03146 Log_Write("elevator reach from %d to %d\r\n", area1num, area2num);
03147 #endif //REACH_DEBUG
03148
03149 reach_elevator++;
03150 }
03151 }
03152 }
03153 }
03154 }
03155 }
03156
03157
03158
03159
03160
03161
03162 aas_lreachability_t *AAS_FindFaceReachabilities(vec3_t *facepoints, int numpoints, aas_plane_t *plane, int towardsface)
03163 {
03164 int i, j, k, l;
03165 int facenum, edgenum, bestfacenum;
03166 float *v1, *v2, *v3, *v4;
03167 float bestdist, speed, hordist, dist;
03168 vec3_t beststart, beststart2, bestend, bestend2, tmp, hordir, testpoint;
03169 aas_lreachability_t *lreach, *lreachabilities;
03170 aas_area_t *area;
03171 aas_face_t *face;
03172 aas_edge_t *edge;
03173 aas_plane_t *faceplane, *bestfaceplane;
03174
03175
03176 lreachabilities = NULL;
03177 bestfacenum = 0;
03178 bestfaceplane = NULL;
03179
03180 for (i = 1; i < aasworld.numareas; i++)
03181 {
03182 area = &aasworld.areas[i];
03183
03184
03185 bestdist = 999999;
03186 for (j = 0; j < area->numfaces; j++)
03187 {
03188 facenum = aasworld.faceindex[area->firstface + j];
03189 face = &aasworld.faces[abs(facenum)];
03190
03191 if (!(face->faceflags & FACE_GROUND)) continue;
03192
03193 faceplane = &aasworld.planes[face->planenum];
03194
03195 for (k = 0; k < face->numedges; k++)
03196 {
03197 edgenum = abs(aasworld.edgeindex[face->firstedge + k]);
03198 edge = &aasworld.edges[edgenum];
03199
03200 v1 = aasworld.vertexes[edge->v[0]];
03201 v2 = aasworld.vertexes[edge->v[1]];
03202
03203 for (l = 0; l < numpoints; l++)
03204 {
03205 v3 = facepoints[l];
03206 v4 = facepoints[(l+1) % numpoints];
03207 dist = AAS_ClosestEdgePoints(v1, v2, v3, v4, faceplane, plane,
03208 beststart, bestend,
03209 beststart2, bestend2, bestdist);
03210 if (dist < bestdist)
03211 {
03212 bestfacenum = facenum;
03213 bestfaceplane = faceplane;
03214 bestdist = dist;
03215 }
03216 }
03217 }
03218 }
03219
03220 if (bestdist > 192) continue;
03221
03222 VectorMiddle(beststart, beststart2, beststart);
03223 VectorMiddle(bestend, bestend2, bestend);
03224
03225 if (!towardsface)
03226 {
03227 VectorCopy(beststart, tmp);
03228 VectorCopy(bestend, beststart);
03229 VectorCopy(tmp, bestend);
03230 }
03231
03232 VectorSubtract(bestend, beststart, hordir);
03233 hordir[2] = 0;
03234 hordist = VectorLength(hordir);
03235
03236 if (hordist > 2 * AAS_MaxJumpDistance(aassettings.phys_jumpvel)) continue;
03237
03238 if (bestend[2] - 32 > beststart[2]) continue;
03239
03240 if (bestend[2] < beststart[2] - 128) continue;
03241
03242 if (hordist > 32)
03243 {
03244
03245 if (!AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) continue;
03246 }
03247
03248 beststart[2] += 1;
03249 bestend[2] += 1;
03250
03251 if (towardsface) VectorCopy(bestend, testpoint);
03252 else VectorCopy(beststart, testpoint);
03253 testpoint[2] = 0;
03254 testpoint[2] = (bestfaceplane->dist - DotProduct(bestfaceplane->normal, testpoint)) / bestfaceplane->normal[2];
03255
03256 if (!AAS_PointInsideFace(bestfacenum, testpoint, 0.1f))
03257 {
03258
03259 if (bestend[2] - 16 > beststart[2]) continue;
03260 }
03261 lreach = AAS_AllocReachability();
03262 if (!lreach) return lreachabilities;
03263 lreach->areanum = i;
03264 lreach->facenum = 0;
03265 lreach->edgenum = 0;
03266 VectorCopy(beststart, lreach->start);
03267 VectorCopy(bestend, lreach->end);
03268 lreach->traveltype = 0;
03269 lreach->traveltime = 0;
03270 lreach->next = lreachabilities;
03271 lreachabilities = lreach;
03272 #ifndef BSPC
03273 if (towardsface) AAS_PermanentLine(lreach->start, lreach->end, 1);
03274 else AAS_PermanentLine(lreach->start, lreach->end, 2);
03275 #endif
03276 }
03277 return lreachabilities;
03278 }
03279
03280
03281
03282
03283
03284
03285 void AAS_Reachability_FuncBobbing(void)
03286 {
03287 int ent, spawnflags, modelnum, axis;
03288 int i, numareas, areas[10];
03289 char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
03290 vec3_t origin, move_end, move_start, move_start_top, move_end_top;
03291 vec3_t mins, maxs, angles = {0, 0, 0};
03292 vec3_t start_edgeverts[4], end_edgeverts[4], mid;
03293 vec3_t org, start, end, dir, points[10];
03294 float height;
03295 aas_plane_t start_plane, end_plane;
03296 aas_lreachability_t *startreach, *endreach, *nextstartreach, *nextendreach, *lreach;
03297 aas_lreachability_t *firststartreach, *firstendreach;
03298
03299 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
03300 {
03301 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
03302 if (strcmp(classname, "func_bobbing")) continue;
03303 AAS_FloatForBSPEpairKey(ent, "height", &height);
03304 if (!height) height = 32;
03305
03306 if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY))
03307 {
03308 botimport.Print(PRT_ERROR, "func_bobbing without model\n");
03309 continue;
03310 }
03311
03312 modelnum = atoi(model+1);
03313 if (modelnum <= 0)
03314 {
03315 botimport.Print(PRT_ERROR, "func_bobbing with invalid model number\n");
03316 continue;
03317 }
03318
03319 if (!AAS_VectorForBSPEpairKey(ent, "origin", origin))
03320 VectorSet(origin, 0, 0, 0);
03321
03322 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
03323
03324 VectorAdd(mins, origin, mins);
03325 VectorAdd(maxs, origin, maxs);
03326
03327 VectorAdd(mins, maxs, mid);
03328 VectorScale(mid, 0.5, mid);
03329 VectorCopy(mid, origin);
03330
03331 VectorCopy(origin, move_end);
03332 VectorCopy(origin, move_start);
03333
03334 AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
03335
03336 if (spawnflags & 1) axis = 0;
03337 else if (spawnflags & 2) axis = 1;
03338 else axis = 2;
03339
03340 move_start[axis] -= height;
03341 move_end[axis] += height;
03342
03343 Log_Write("funcbob model %d, start = {%1.1f, %1.1f, %1.1f} end = {%1.1f, %1.1f, %1.1f}\n",
03344 modelnum, move_start[0], move_start[1], move_start[2], move_end[0], move_end[1], move_end[2]);
03345
03346 #ifndef BSPC
03347
03348
03349
03350
03351 #endif
03352
03353 for (i = 0; i < 4; i++)
03354 {
03355 VectorCopy(move_start, start_edgeverts[i]);
03356 start_edgeverts[i][2] += maxs[2] - mid[2];
03357 start_edgeverts[i][2] += 24;
03358 }
03359 start_edgeverts[0][0] += maxs[0] - mid[0];
03360 start_edgeverts[0][1] += maxs[1] - mid[1];
03361 start_edgeverts[1][0] += maxs[0] - mid[0];
03362 start_edgeverts[1][1] += mins[1] - mid[1];
03363 start_edgeverts[2][0] += mins[0] - mid[0];
03364 start_edgeverts[2][1] += mins[1] - mid[1];
03365 start_edgeverts[3][0] += mins[0] - mid[0];
03366 start_edgeverts[3][1] += maxs[1] - mid[1];
03367
03368 start_plane.dist = start_edgeverts[0][2];
03369 VectorSet(start_plane.normal, 0, 0, 1);
03370
03371 for (i = 0; i < 4; i++)
03372 {
03373 VectorCopy(move_end, end_edgeverts[i]);
03374 end_edgeverts[i][2] += maxs[2] - mid[2];
03375 end_edgeverts[i][2] += 24;
03376 }
03377 end_edgeverts[0][0] += maxs[0] - mid[0];
03378 end_edgeverts[0][1] += maxs[1] - mid[1];
03379 end_edgeverts[1][0] += maxs[0] - mid[0];
03380 end_edgeverts[1][1] += mins[1] - mid[1];
03381 end_edgeverts[2][0] += mins[0] - mid[0];
03382 end_edgeverts[2][1] += mins[1] - mid[1];
03383 end_edgeverts[3][0] += mins[0] - mid[0];
03384 end_edgeverts[3][1] += maxs[1] - mid[1];
03385
03386 end_plane.dist = end_edgeverts[0][2];
03387 VectorSet(end_plane.normal, 0, 0, 1);
03388
03389 #ifndef BSPC
03390 #if 0
03391 for (i = 0; i < 4; i++)
03392 {
03393 AAS_PermanentLine(start_edgeverts[i], start_edgeverts[(i+1)%4], 1);
03394 AAS_PermanentLine(end_edgeverts[i], end_edgeverts[(i+1)%4], 1);
03395 }
03396 #endif
03397 #endif
03398 VectorCopy(move_start, move_start_top);
03399 move_start_top[2] += maxs[2] - mid[2] + 24;
03400 VectorCopy(move_end, move_end_top);
03401 move_end_top[2] += maxs[2] - mid[2] + 24;
03402
03403 if (!AAS_PointAreaNum(move_start_top)) continue;
03404 if (!AAS_PointAreaNum(move_end_top)) continue;
03405
03406 for (i = 0; i < 2; i++)
03407 {
03408 firststartreach = firstendreach = NULL;
03409
03410 if (i == 0)
03411 {
03412 firststartreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qtrue);
03413 firstendreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qfalse);
03414 }
03415 else
03416 {
03417 firststartreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qtrue);
03418 firstendreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qfalse);
03419 }
03420
03421
03422 for (startreach = firststartreach; startreach; startreach = nextstartreach)
03423 {
03424 nextstartreach = startreach->next;
03425
03426
03427
03428
03429 for (endreach = firstendreach; endreach; endreach = nextendreach)
03430 {
03431 nextendreach = endreach->next;
03432
03433
03434
03435
03436 Log_Write("funcbob reach from area %d to %d\n", startreach->areanum, endreach->areanum);
03437
03438
03439 if (i == 0) VectorCopy(move_start_top, org);
03440 else VectorCopy(move_end_top, org);
03441 VectorSubtract(startreach->start, org, dir);
03442 dir[2] = 0;
03443 VectorNormalize(dir);
03444 VectorCopy(startreach->start, start);
03445 VectorMA(startreach->start, 1, dir, start);
03446 start[2] += 1;
03447 VectorMA(startreach->start, 16, dir, end);
03448 end[2] += 1;
03449
03450 numareas = AAS_TraceAreas(start, end, areas, points, 10);
03451 if (numareas <= 0) continue;
03452 if (numareas > 1) VectorCopy(points[1], startreach->start);
03453 else VectorCopy(end, startreach->start);
03454
03455 if (!AAS_PointAreaNum(startreach->start)) continue;
03456 if (!AAS_PointAreaNum(endreach->end)) continue;
03457
03458 lreach = AAS_AllocReachability();
03459 lreach->areanum = endreach->areanum;
03460 if (i == 0) lreach->edgenum = ((int)move_start[axis] << 16) | ((int) move_end[axis] & 0x0000ffff);
03461 else lreach->edgenum = ((int)move_end[axis] << 16) | ((int) move_start[axis] & 0x0000ffff);
03462 lreach->facenum = (spawnflags << 16) | modelnum;
03463 VectorCopy(startreach->start, lreach->start);
03464 VectorCopy(endreach->end, lreach->end);
03465 #ifndef BSPC
03466
03467
03468 #endif
03469 lreach->traveltype = TRAVEL_FUNCBOB;
03470 lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
03471 lreach->traveltime = aassettings.rs_funcbob;
03472 reach_funcbob++;
03473 lreach->next = areareachability[startreach->areanum];
03474 areareachability[startreach->areanum] = lreach;
03475
03476 }
03477 }
03478 for (startreach = firststartreach; startreach; startreach = nextstartreach)
03479 {
03480 nextstartreach = startreach->next;
03481 AAS_FreeReachability(startreach);
03482 }
03483 for (endreach = firstendreach; endreach; endreach = nextendreach)
03484 {
03485 nextendreach = endreach->next;
03486 AAS_FreeReachability(endreach);
03487 }
03488
03489 if (!(spawnflags & 1) && !(spawnflags & 2)) break;
03490 }
03491 }
03492 }
03493
03494
03495
03496
03497
03498
03499 void AAS_Reachability_JumpPad(void)
03500 {
03501 int face2num, i, ret, area2num, visualize, ent, bot_visualizejumppads;
03502
03503
03504 float speed, zvel, hordist;
03505 aas_face_t *face2;
03506 aas_area_t *area2;
03507 aas_lreachability_t *lreach;
03508 vec3_t areastart, facecenter, dir, cmdmove;
03509 vec3_t velocity, absmins, absmaxs;
03510
03511 aas_clientmove_t move;
03512
03513 aas_link_t *areas, *link;
03514
03515 char classname[MAX_EPAIRKEY];
03516
03517 #ifdef BSPC
03518 bot_visualizejumppads = 0;
03519 #else
03520 bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0");
03521 #endif
03522 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
03523 {
03524 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
03525 if (strcmp(classname, "trigger_push")) continue;
03526
03527 if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue;
03528
03529
03530
03531
03532
03533
03534
03535
03536
03537
03538
03539
03540
03541
03542
03543
03544
03545
03546
03547
03548
03549
03550
03551
03552
03553
03554
03555
03556
03557
03558
03559
03560
03561
03562
03563
03564
03565
03566
03567
03568
03569
03570
03571
03572
03573
03574
03575
03576
03577
03578
03579
03580
03581
03582
03583
03584
03585
03586
03587
03588
03589
03590
03591
03592
03593
03594
03595
03596
03597
03598
03599 areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
03600
03601
03602
03603
03604
03605
03606
03607
03608
03609 for (link = areas; link; link = link->next_area)
03610 {
03611 if (AAS_AreaJumpPad(link->areanum)) break;
03612 }
03613 if (!link)
03614 {
03615 botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n");
03616 AAS_UnlinkFromAreas(areas);
03617 continue;
03618 }
03619
03620 botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]);
03621
03622 if (velocity[0] || velocity[1])
03623 {
03624 VectorSet(cmdmove, 0, 0, 0);
03625
03626
03627 Com_Memset(&move, 0, sizeof(aas_clientmove_t));
03628 area2num = 0;
03629 for (i = 0; i < 20; i++)
03630 {
03631 AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
03632 velocity, cmdmove, 0, 30, 0.1f,
03633 SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|
03634 SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, bot_visualizejumppads);
03635 area2num = move.endarea;
03636 for (link = areas; link; link = link->next_area)
03637 {
03638 if (!AAS_AreaJumpPad(link->areanum)) continue;
03639 if (link->areanum == area2num) break;
03640 }
03641 if (!link) break;
03642 VectorCopy(move.endpos, areastart);
03643 VectorCopy(move.velocity, velocity);
03644 }
03645 if (area2num && i < 20)
03646 {
03647 for (link = areas; link; link = link->next_area)
03648 {
03649 if (!AAS_AreaJumpPad(link->areanum)) continue;
03650 if (AAS_ReachabilityExists(link->areanum, area2num)) continue;
03651
03652 lreach = AAS_AllocReachability();
03653 if (!lreach)
03654 {
03655 AAS_UnlinkFromAreas(areas);
03656 return;
03657 }
03658 lreach->areanum = area2num;
03659
03660 lreach->facenum = velocity[2];
03661
03662 lreach->edgenum = sqrt(velocity[0] * velocity[0] + velocity[1] * velocity[1]);
03663 VectorCopy(areastart, lreach->start);
03664 VectorCopy(move.endpos, lreach->end);
03665 lreach->traveltype = TRAVEL_JUMPPAD;
03666 lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
03667 lreach->traveltime = aassettings.rs_jumppad;
03668 lreach->next = areareachability[link->areanum];
03669 areareachability[link->areanum] = lreach;
03670
03671 reach_jumppad++;
03672 }
03673 }
03674 }
03675
03676 if (fabs(velocity[0]) > 100 || fabs(velocity[1]) > 100) continue;
03677
03678 for (area2num = 1; area2num < aasworld.numareas; area2num++)
03679 {
03680 visualize = qfalse;
03681
03682
03683
03684
03685
03686
03687
03688
03689
03690
03691
03692
03693
03694
03695 for (link = areas; link; link = link->next_area)
03696 {
03697 if (AAS_ReachabilityExists(link->areanum, area2num)) break;
03698 if (AAS_AreaJumpPad(link->areanum))
03699 {
03700 if (link->areanum == area2num) break;
03701 }
03702 }
03703 if (link) continue;
03704
03705 area2 = &aasworld.areas[area2num];
03706 for (i = 0; i < area2->numfaces; i++)
03707 {
03708 face2num = aasworld.faceindex[area2->firstface + i];
03709 face2 = &aasworld.faces[abs(face2num)];
03710
03711 if (!(face2->faceflags & FACE_GROUND)) continue;
03712
03713 AAS_FaceCenter(face2num, facecenter);
03714
03715 if (facecenter[2] < areastart[2]) continue;
03716
03717 zvel = velocity[2];
03718
03719
03720 ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed);
03721 if (ret && speed < 150)
03722 {
03723
03724 VectorSubtract(facecenter, areastart, dir);
03725 dir[2] = 0;
03726 hordist = VectorNormalize(dir);
03727
03728 {
03729
03730 VectorScale(dir, speed, cmdmove);
03731
03732 AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
03733 velocity, cmdmove, 30, 30, 0.1f,
03734 SE_ENTERWATER|SE_ENTERSLIME|
03735 SE_ENTERLAVA|SE_HITGROUNDDAMAGE|
03736 SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_HITGROUNDAREA, area2num, visualize);
03737
03738
03739 if (move.frames < 30 &&
03740 !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
03741 && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER)))
03742 {
03743
03744 for (link = areas; link; link = link->next_area)
03745 {
03746 if (link->areanum == move.endarea) break;
03747 }
03748 if (!link)
03749 {
03750 for (link = areas; link; link = link->next_area)
03751 {
03752 if (!AAS_AreaJumpPad(link->areanum)) continue;
03753 if (AAS_ReachabilityExists(link->areanum, area2num)) continue;
03754
03755 lreach = AAS_AllocReachability();
03756 if (!lreach)
03757 {
03758 AAS_UnlinkFromAreas(areas);
03759 return;
03760 }
03761 lreach->areanum = move.endarea;
03762
03763 lreach->facenum = velocity[2];
03764
03765 lreach->edgenum = sqrt(cmdmove[0] * cmdmove[0] + cmdmove[1] * cmdmove[1]);
03766 VectorCopy(areastart, lreach->start);
03767 VectorCopy(facecenter, lreach->end);
03768 lreach->traveltype = TRAVEL_JUMPPAD;
03769 lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
03770 lreach->traveltime = aassettings.rs_aircontrolledjumppad;
03771 lreach->next = areareachability[link->areanum];
03772 areareachability[link->areanum] = lreach;
03773
03774 reach_jumppad++;
03775 }
03776 }
03777 }
03778 }
03779 }
03780 }
03781 }
03782 AAS_UnlinkFromAreas(areas);
03783 }
03784 }
03785
03786
03787
03788
03789
03790
03791
03792
03793 int AAS_Reachability_Grapple(int area1num, int area2num)
03794 {
03795 int face2num, i, j, areanum, numareas, areas[20];
03796 float mingrappleangle, z, hordist;
03797 bsp_trace_t bsptrace;
03798 aas_trace_t trace;
03799 aas_face_t *face2;
03800 aas_area_t *area1, *area2;
03801 aas_lreachability_t *lreach;
03802 vec3_t areastart, facecenter, start, end, dir, down = {0, 0, -1};
03803 vec_t *v;
03804
03805
03806 if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse;
03807
03808 if (!(AAS_AreaPresenceType(area1num) & PRESENCE_NORMAL)) return qfalse;
03809
03810 if (AAS_AreaSwim(area1num)) return qfalse;
03811
03812 area1 = &aasworld.areas[area1num];
03813 area2 = &aasworld.areas[area2num];
03814
03815 if (area2->maxs[2] < area1->mins[2]) return qfalse;
03816
03817 VectorCopy(aasworld.areas[area1num].center, start);
03818
03819 if (!AAS_AreaSwim(area1num))
03820 {
03821 if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num,
03822 start[0], start[1], start[2]);
03823 VectorCopy(start, end);
03824 end[2] -= 1000;
03825 trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
03826 if (trace.startsolid) return qfalse;
03827 VectorCopy(trace.endpos, areastart);
03828 }
03829 else
03830 {
03831 if (!(AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return qfalse;
03832 }
03833
03834
03835
03836 for (i = 0; i < area2->numfaces; i++)
03837 {
03838 face2num = aasworld.faceindex[area2->firstface + i];
03839 face2 = &aasworld.faces[abs(face2num)];
03840
03841 if (!(face2->faceflags & FACE_SOLID)) continue;
03842
03843 v = aasworld.vertexes[aasworld.edges[abs(aasworld.edgeindex[face2->firstedge])].v[0]];
03844 VectorSubtract(v, areastart, dir);
03845
03846 if (DotProduct(aasworld.planes[face2->planenum].normal, dir) > 0) continue;
03847
03848 AAS_FaceCenter(face2num, facecenter);
03849
03850 if (facecenter[2] < areastart[2] + 64) continue;
03851
03852 if (DotProduct(aasworld.planes[face2->planenum].normal, down) < 0) continue;
03853
03854 VectorSubtract(facecenter, areastart, dir);
03855
03856 z = dir[2];
03857 dir[2] = 0;
03858 hordist = VectorLength(dir);
03859 if (!hordist) continue;
03860
03861 if (hordist > 2000) continue;
03862
03863 mingrappleangle = 15;
03864 if (z / hordist < tan(2 * M_PI * mingrappleangle / 360)) continue;
03865
03866 VectorCopy(facecenter, start);
03867 VectorMA(facecenter, -500, aasworld.planes[face2->planenum].normal, end);
03868
03869 bsptrace = AAS_Trace(start, NULL, NULL, end, 0, CONTENTS_SOLID);
03870
03871 if ((bsptrace.surface.flags & SURF_SKY) || (bsptrace.fraction * 500 > 32)) continue;
03872
03873
03874 VectorSubtract(facecenter, areastart, dir);
03875 VectorNormalize(dir);
03876 VectorMA(areastart, 4, dir, start);
03877 VectorCopy(bsptrace.endpos, end);
03878 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
03879 VectorSubtract(trace.endpos, facecenter, dir);
03880 if (VectorLength(dir) > 24) continue;
03881
03882 VectorCopy(trace.endpos, start);
03883 VectorCopy(trace.endpos, end);
03884 end[2] -= AAS_FallDamageDistance();
03885 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
03886 if (trace.fraction >= 1) continue;
03887
03888 areanum = AAS_PointAreaNum(trace.endpos);
03889
03890 if (aasworld.areasettings[areanum].contents & (AREACONTENTS_SLIME|AREACONTENTS_LAVA))
03891 {
03892 continue;
03893 }
03894
03895 if (areanum == area1num) continue;
03896
03897 if (AAS_ReachabilityExists(area1num, areanum)) continue;
03898
03899 if (!AAS_AreaGrounded(areanum)) continue;
03900
03901 numareas = AAS_TraceAreas(areastart, bsptrace.endpos, areas, NULL, 20);
03902 if (numareas >= 20) continue;
03903 for (j = 0; j < numareas; j++)
03904 {
03905 if (aasworld.areasettings[areas[j]].contents & AREACONTENTS_CLUSTERPORTAL) break;
03906 }
03907 if (j < numareas) continue;
03908
03909 lreach = AAS_AllocReachability();
03910 if (!lreach) return qfalse;
03911 lreach->areanum = areanum;
03912 lreach->facenum = face2num;
03913 lreach->edgenum = 0;
03914 VectorCopy(areastart, lreach->start);
03915
03916 VectorCopy(bsptrace.endpos, lreach->end);
03917 lreach->traveltype = TRAVEL_GRAPPLEHOOK;
03918 VectorSubtract(lreach->end, lreach->start, dir);
03919 lreach->traveltime = aassettings.rs_startgrapple + VectorLength(dir) * 0.25;
03920 lreach->next = areareachability[area1num];
03921 areareachability[area1num] = lreach;
03922
03923 reach_grapple++;
03924 }
03925
03926 return qfalse;
03927 }
03928
03929
03930
03931
03932
03933
03934 void AAS_SetWeaponJumpAreaFlags(void)
03935 {
03936 int ent, i;
03937 vec3_t mins = {-15, -15, -15}, maxs = {15, 15, 15};
03938 vec3_t origin;
03939 int areanum, weaponjumpareas, spawnflags;
03940 char classname[MAX_EPAIRKEY];
03941
03942 weaponjumpareas = 0;
03943 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
03944 {
03945 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
03946 if (
03947 !strcmp(classname, "item_armor_body") ||
03948 !strcmp(classname, "item_armor_combat") ||
03949 !strcmp(classname, "item_health_mega") ||
03950 !strcmp(classname, "weapon_grenadelauncher") ||
03951 !strcmp(classname, "weapon_rocketlauncher") ||
03952 !strcmp(classname, "weapon_lightning") ||
03953 !strcmp(classname, "weapon_plasmagun") ||
03954 !strcmp(classname, "weapon_railgun") ||
03955 !strcmp(classname, "weapon_bfg") ||
03956 !strcmp(classname, "item_quad") ||
03957 !strcmp(classname, "item_regen") ||
03958 !strcmp(classname, "item_invulnerability"))
03959 {
03960 if (AAS_VectorForBSPEpairKey(ent, "origin", origin))
03961 {
03962 spawnflags = 0;
03963 AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
03964
03965 if (!(spawnflags & 1))
03966 {
03967 if (!AAS_DropToFloor(origin, mins, maxs))
03968 {
03969 botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n",
03970 classname, origin[0], origin[1], origin[2]);
03971 }
03972 }
03973
03974 areanum = AAS_BestReachableArea(origin, mins, maxs, origin);
03975
03976 aasworld.areasettings[areanum].areaflags |= AREA_WEAPONJUMP;
03977
03978
03979
03980
03981 weaponjumpareas++;
03982 }
03983 }
03984 }
03985 for (i = 1; i < aasworld.numareas; i++)
03986 {
03987 if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
03988 {
03989 aasworld.areasettings[i].areaflags |= AREA_WEAPONJUMP;
03990 weaponjumpareas++;
03991 }
03992 }
03993 botimport.Print(PRT_MESSAGE, "%d weapon jump areas\n", weaponjumpareas);
03994 }
03995
03996
03997
03998
03999
04000
04001
04002
04003
04004
04005
04006 int AAS_Reachability_WeaponJump(int area1num, int area2num)
04007 {
04008 int face2num, i, n, ret, visualize;
04009 float speed, zvel, hordist;
04010 aas_face_t *face2;
04011 aas_area_t *area1, *area2;
04012 aas_lreachability_t *lreach;
04013 vec3_t areastart, facecenter, start, end, dir, cmdmove;
04014 vec3_t velocity;
04015 aas_clientmove_t move;
04016 aas_trace_t trace;
04017
04018 visualize = qfalse;
04019
04020
04021
04022
04023 if (!AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) return qfalse;
04024 if (!AAS_AreaGrounded(area2num)) return qfalse;
04025
04026 if (!(aasworld.areasettings[area2num].areaflags & AREA_WEAPONJUMP)) return qfalse;
04027
04028 area1 = &aasworld.areas[area1num];
04029 area2 = &aasworld.areas[area2num];
04030
04031 if (area2->maxs[2] < area1->mins[2]) return qfalse;
04032
04033 VectorCopy(aasworld.areas[area1num].center, start);
04034
04035 if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num,
04036 start[0], start[1], start[2]);
04037 VectorCopy(start, end);
04038 end[2] -= 1000;
04039 trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
04040 if (trace.startsolid) return qfalse;
04041 VectorCopy(trace.endpos, areastart);
04042
04043
04044
04045 for (i = 0; i < area2->numfaces; i++)
04046 {
04047 face2num = aasworld.faceindex[area2->firstface + i];
04048 face2 = &aasworld.faces[abs(face2num)];
04049
04050 if (!(face2->faceflags & FACE_GROUND)) continue;
04051
04052 AAS_FaceCenter(face2num, facecenter);
04053
04054 if (facecenter[2] < areastart[2] + 64) continue;
04055
04056 for (n = 0; n < 1; n++)
04057 {
04058
04059 if (n) zvel = AAS_BFGJumpZVelocity(areastart);
04060 else zvel = AAS_RocketJumpZVelocity(areastart);
04061
04062
04063 ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed);
04064 if (ret && speed < 300)
04065 {
04066
04067 VectorSubtract(facecenter, areastart, dir);
04068 dir[2] = 0;
04069 hordist = VectorNormalize(dir);
04070
04071 {
04072
04073 VectorScale(dir, speed, cmdmove);
04074 VectorSet(velocity, 0, 0, zvel);
04075
04076
04077
04078
04079
04080
04081
04082 AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qtrue,
04083 velocity, cmdmove, 30, 30, 0.1f,
04084 SE_ENTERWATER|SE_ENTERSLIME|
04085 SE_ENTERLAVA|SE_HITGROUNDDAMAGE|
04086 SE_TOUCHJUMPPAD|SE_HITGROUND|SE_HITGROUNDAREA, area2num, visualize);
04087
04088
04089 if (move.frames < 30 &&
04090 !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
04091 && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD)))
04092 {
04093
04094 lreach = AAS_AllocReachability();
04095 if (!lreach) return qfalse;
04096 lreach->areanum = area2num;
04097 lreach->facenum = 0;
04098 lreach->edgenum = 0;
04099 VectorCopy(areastart, lreach->start);
04100 VectorCopy(facecenter, lreach->end);
04101 if (n)
04102 {
04103 lreach->traveltype = TRAVEL_BFGJUMP;
04104 lreach->traveltime = aassettings.rs_bfgjump;
04105 }
04106 else
04107 {
04108 lreach->traveltype = TRAVEL_ROCKETJUMP;
04109 lreach->traveltime = aassettings.rs_rocketjump;
04110 }
04111 lreach->next = areareachability[area1num];
04112 areareachability[area1num] = lreach;
04113
04114 reach_rocketjump++;
04115 return qtrue;
04116 }
04117 }
04118 }
04119 }
04120 }
04121
04122 return qfalse;
04123 }
04124
04125
04126
04127
04128
04129
04130
04131 void AAS_Reachability_WalkOffLedge(int areanum)
04132 {
04133 int i, j, k, l, m, n, p, areas[10], numareas;
04134 int face1num, face2num, face3num, edge1num, edge2num, edge3num;
04135 int otherareanum, gap, reachareanum, side;
04136 aas_area_t *area, *area2;
04137 aas_face_t *face1, *face2, *face3;
04138 aas_edge_t *edge;
04139 aas_plane_t *plane;
04140 vec_t *v1, *v2;
04141 vec3_t sharededgevec, mid, dir, testend;
04142 aas_lreachability_t *lreach;
04143 aas_trace_t trace;
04144
04145 if (!AAS_AreaGrounded(areanum) || AAS_AreaSwim(areanum)) return;
04146
04147 area = &aasworld.areas[areanum];
04148
04149 for (i = 0; i < area->numfaces; i++)
04150 {
04151 face1num = aasworld.faceindex[area->firstface + i];
04152 face1 = &aasworld.faces[abs(face1num)];
04153
04154 if (!(face1->faceflags & FACE_GROUND)) continue;
04155
04156 for (k = 0; k < face1->numedges; k++)
04157 {
04158 edge1num = aasworld.edgeindex[face1->firstedge + k];
04159
04160 for (j = 0; j < area->numfaces; j++)
04161 {
04162 face2num = aasworld.faceindex[area->firstface + j];
04163 face2 = &aasworld.faces[abs(face2num)];
04164
04165 if (face2->faceflags & FACE_GROUND) continue;
04166
04167 for (l = 0; l < face2->numedges; l++)
04168 {
04169 edge2num = aasworld.edgeindex[face2->firstedge + l];
04170 if (abs(edge1num) == abs(edge2num))
04171 {
04172
04173 if (face2->frontarea == areanum) otherareanum = face2->backarea;
04174 else otherareanum = face2->frontarea;
04175
04176 area2 = &aasworld.areas[otherareanum];
04177
04178 if (aasworld.areasettings[otherareanum].areaflags & AREA_GROUNDED)
04179 {
04180
04181 gap = qfalse;
04182 for (n = 0; n < area2->numfaces; n++)
04183 {
04184 face3num = aasworld.faceindex[area2->firstface + n];
04185
04186 if (abs(face3num) == abs(face2num)) continue;
04187
04188 face3 = &aasworld.faces[abs(face3num)];
04189
04190 for (m = 0; m < face3->numedges; m++)
04191 {
04192 edge3num = aasworld.edgeindex[face3->firstedge + m];
04193
04194 if (abs(edge3num) == abs(edge1num))
04195 {
04196 if (!(face3->faceflags & FACE_SOLID))
04197 {
04198 gap = qtrue;
04199 break;
04200 }
04201
04202 if (face3->faceflags & FACE_GROUND)
04203 {
04204 gap = qfalse;
04205 break;
04206 }
04207
04208 gap = qtrue;
04209 break;
04210 }
04211 }
04212 if (m < face3->numedges) break;
04213 }
04214 if (!gap) break;
04215 }
04216
04217 edge = &aasworld.edges[abs(edge1num)];
04218 side = edge1num < 0;
04219
04220 v1 = aasworld.vertexes[edge->v[side]];
04221 v2 = aasworld.vertexes[edge->v[!side]];
04222
04223 plane = &aasworld.planes[face1->planenum];
04224
04225 VectorSubtract(v2, v1, sharededgevec);
04226 CrossProduct(plane->normal, sharededgevec, dir);
04227 VectorNormalize(dir);
04228
04229 VectorAdd(v1, v2, mid);
04230 VectorScale(mid, 0.5, mid);
04231 VectorMA(mid, 8, dir, mid);
04232
04233 VectorCopy(mid, testend);
04234 testend[2] -= 1000;
04235 trace = AAS_TraceClientBBox(mid, testend, PRESENCE_CROUCH, -1);
04236
04237 if (trace.startsolid)
04238 {
04239
04240 break;
04241 }
04242 reachareanum = AAS_PointAreaNum(trace.endpos);
04243 if (reachareanum == areanum)
04244 {
04245
04246 break;
04247 }
04248 if (AAS_ReachabilityExists(areanum, reachareanum))
04249 {
04250
04251 break;
04252 }
04253 if (!AAS_AreaGrounded(reachareanum) && !AAS_AreaSwim(reachareanum))
04254 {
04255
04256 break;
04257 }
04258
04259 if (aasworld.areasettings[reachareanum].contents & (AREACONTENTS_SLIME
04260 | AREACONTENTS_LAVA))
04261 {
04262
04263 break;
04264 }
04265
04266 numareas = AAS_TraceAreas(mid, testend, areas, NULL, sizeof(areas) / sizeof(int));
04267 for (p = 0; p < numareas; p++)
04268 if (AAS_AreaClusterPortal(areas[p]))
04269 break;
04270 if (p < numareas)
04271 break;
04272
04273 if (aassettings.rs_maxfallheight && fabs(mid[2] - trace.endpos[2]) > aassettings.rs_maxfallheight)
04274 break;
04275
04276 lreach = AAS_AllocReachability();
04277 if (!lreach) break;
04278 lreach->areanum = reachareanum;
04279 lreach->facenum = 0;
04280 lreach->edgenum = edge1num;
04281 VectorCopy(mid, lreach->start);
04282 VectorCopy(trace.endpos, lreach->end);
04283 lreach->traveltype = TRAVEL_WALKOFFLEDGE;
04284 lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.phys_gravity;
04285 if (!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum))
04286 {
04287 if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta5)
04288 {
04289 lreach->traveltime += aassettings.rs_falldamage5;
04290 }
04291 else if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta10)
04292 {
04293 lreach->traveltime += aassettings.rs_falldamage10;
04294 }
04295 }
04296 lreach->next = areareachability[areanum];
04297 areareachability[areanum] = lreach;
04298
04299 reach_walkoffledge++;
04300 }
04301 }
04302 }
04303 }
04304 }
04305 }
04306
04307
04308
04309
04310
04311
04312 void AAS_StoreReachability(void)
04313 {
04314 int i;
04315 aas_areasettings_t *areasettings;
04316 aas_lreachability_t *lreach;
04317 aas_reachability_t *reach;
04318
04319 if (aasworld.reachability) FreeMemory(aasworld.reachability);
04320 aasworld.reachability = (aas_reachability_t *) GetClearedMemory((numlreachabilities + 10) * sizeof(aas_reachability_t));
04321 aasworld.reachabilitysize = 1;
04322 for (i = 0; i < aasworld.numareas; i++)
04323 {
04324 areasettings = &aasworld.areasettings[i];
04325 areasettings->firstreachablearea = aasworld.reachabilitysize;
04326 areasettings->numreachableareas = 0;
04327 for (lreach = areareachability[i]; lreach; lreach = lreach->next)
04328 {
04329 reach = &aasworld.reachability[areasettings->firstreachablearea +
04330 areasettings->numreachableareas];
04331 reach->areanum = lreach->areanum;
04332 reach->facenum = lreach->facenum;
04333 reach->edgenum = lreach->edgenum;
04334 VectorCopy(lreach->start, reach->start);
04335 VectorCopy(lreach->end, reach->end);
04336 reach->traveltype = lreach->traveltype;
04337 reach->traveltime = lreach->traveltime;
04338
04339 areasettings->numreachableareas++;
04340 }
04341 aasworld.reachabilitysize += areasettings->numreachableareas;
04342 }
04343 }
04344
04345
04346
04347
04348
04349
04350
04351
04352
04353
04354
04355
04356
04357
04358
04359
04360
04361
04362
04363
04364
04365
04366
04367
04368
04369 int AAS_ContinueInitReachability(float time)
04370 {
04371 int i, j, todo, start_time;
04372 static float framereachability, reachability_delay;
04373 static int lastpercentage;
04374
04375 if (!aasworld.loaded) return qfalse;
04376
04377 if (aasworld.numreachabilityareas >= aasworld.numareas + 2) return qfalse;
04378
04379 if (aasworld.numreachabilityareas == 1)
04380 {
04381 botimport.Print(PRT_MESSAGE, "calculating reachability...\n");
04382 lastpercentage = 0;
04383 framereachability = 2000;
04384 reachability_delay = 1000;
04385 }
04386
04387 todo = aasworld.numreachabilityareas + (int) framereachability;
04388 start_time = Sys_MilliSeconds();
04389
04390 for (i = aasworld.numreachabilityareas; i < aasworld.numareas && i < todo; i++)
04391 {
04392 aasworld.numreachabilityareas++;
04393
04394 if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
04395 {
04396 continue;
04397 }
04398
04399 for (j = 1; j < aasworld.numareas; j++)
04400 {
04401 if (i == j) continue;
04402
04403 if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))
04404 {
04405 if (!(aasworld.areasettings[j].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)))
04406 {
04407 continue;
04408 }
04409 }
04410
04411 if (AAS_ReachabilityExists(i, j)) continue;
04412
04413 if (AAS_Reachability_Swim(i, j)) continue;
04414
04415 if (AAS_Reachability_EqualFloorHeight(i, j)) continue;
04416
04417 if (AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(i, j)) continue;
04418
04419 if (AAS_Reachability_Ladder(i, j)) continue;
04420
04421 if (AAS_Reachability_Jump(i, j)) continue;
04422 }
04423
04424 if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))
04425 {
04426 continue;
04427 }
04428
04429 for (j = 1; j < aasworld.numareas; j++)
04430 {
04431 if (i == j) continue;
04432
04433 if (AAS_ReachabilityExists(i, j)) continue;
04434
04435 if (calcgrapplereach) AAS_Reachability_Grapple(i, j);
04436
04437 AAS_Reachability_WeaponJump(i, j);
04438 }
04439
04440 if (Sys_MilliSeconds() - start_time > (int) reachability_delay) break;
04441
04442 if (aasworld.numreachabilityareas * 1000 / aasworld.numareas > lastpercentage) break;
04443 }
04444
04445 if (aasworld.numreachabilityareas == aasworld.numareas)
04446 {
04447 botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) 100.0);
04448 botimport.Print(PRT_MESSAGE, "\nplease wait while storing reachability...\n");
04449 aasworld.numreachabilityareas++;
04450 }
04451
04452 else if (aasworld.numreachabilityareas == aasworld.numareas + 1)
04453 {
04454
04455 for (i = 1; i < aasworld.numareas; i++)
04456 {
04457
04458 if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
04459 {
04460 continue;
04461 }
04462 AAS_Reachability_WalkOffLedge(i);
04463 }
04464
04465 AAS_Reachability_JumpPad();
04466
04467 AAS_Reachability_Teleport();
04468
04469 AAS_Reachability_Elevator();
04470
04471 AAS_Reachability_FuncBobbing();
04472
04473 #ifdef DEBUG
04474 botimport.Print(PRT_MESSAGE, "%6d reach swim\n", reach_swim);
04475 botimport.Print(PRT_MESSAGE, "%6d reach equal floor\n", reach_equalfloor);
04476 botimport.Print(PRT_MESSAGE, "%6d reach step\n", reach_step);
04477 botimport.Print(PRT_MESSAGE, "%6d reach barrier\n", reach_barrier);
04478 botimport.Print(PRT_MESSAGE, "%6d reach waterjump\n", reach_waterjump);
04479 botimport.Print(PRT_MESSAGE, "%6d reach walkoffledge\n", reach_walkoffledge);
04480 botimport.Print(PRT_MESSAGE, "%6d reach jump\n", reach_jump);
04481 botimport.Print(PRT_MESSAGE, "%6d reach ladder\n", reach_ladder);
04482 botimport.Print(PRT_MESSAGE, "%6d reach walk\n", reach_walk);
04483 botimport.Print(PRT_MESSAGE, "%6d reach teleport\n", reach_teleport);
04484 botimport.Print(PRT_MESSAGE, "%6d reach funcbob\n", reach_funcbob);
04485 botimport.Print(PRT_MESSAGE, "%6d reach elevator\n", reach_elevator);
04486 botimport.Print(PRT_MESSAGE, "%6d reach grapple\n", reach_grapple);
04487 botimport.Print(PRT_MESSAGE, "%6d reach rocketjump\n", reach_rocketjump);
04488 botimport.Print(PRT_MESSAGE, "%6d reach jumppad\n", reach_jumppad);
04489 #endif
04490
04491
04492 AAS_StoreReachability();
04493
04494 AAS_ShutDownReachabilityHeap();
04495
04496 FreeMemory(areareachability);
04497
04498 aasworld.numreachabilityareas++;
04499
04500 botimport.Print(PRT_MESSAGE, "calculating clusters...\n");
04501 }
04502 else
04503 {
04504 lastpercentage = aasworld.numreachabilityareas * 1000 / aasworld.numareas;
04505 botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) lastpercentage / 10);
04506 }
04507
04508 return qtrue;
04509 }
04510
04511
04512
04513
04514
04515
04516 void AAS_InitReachability(void)
04517 {
04518 if (!aasworld.loaded) return;
04519
04520 if (aasworld.reachabilitysize)
04521 {
04522 #ifndef BSPC
04523 if (!((int)LibVarGetValue("forcereachability")))
04524 {
04525 aasworld.numreachabilityareas = aasworld.numareas + 2;
04526 return;
04527 }
04528 #else
04529 aasworld.numreachabilityareas = aasworld.numareas + 2;
04530 return;
04531 #endif //BSPC
04532 }
04533 #ifndef BSPC
04534 calcgrapplereach = LibVarGetValue("grapplereach");
04535 #endif
04536 aasworld.savefile = qtrue;
04537
04538 aasworld.numreachabilityareas = 1;
04540
04541 AAS_SetupReachabilityHeap();
04542
04543 areareachability = (aas_lreachability_t **) GetClearedMemory(
04544 aasworld.numareas * sizeof(aas_lreachability_t *));
04545
04546 AAS_SetWeaponJumpAreaFlags();
04547 }