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