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