/* =========================================================================== Copyright (C) 2000 - 2013, Raven Software, Inc. Copyright (C) 2001 - 2013, Activision, Inc. Copyright (C) 2013 - 2015, OpenJK contributors This file is part of the OpenJK source code. OpenJK is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . =========================================================================== */ #include "../qcommon/q_shared.h" #include "../cgame/cg_local.h" #include "b_local.h" extern qboolean G_EntIsUnlockedDoor( int entityNum ); extern qboolean FlyingCreature( gentity_t *ent ); #define MIN_DOOR_BLOCK_DIST 16 #define MIN_DOOR_BLOCK_DIST_SQR ( MIN_DOOR_BLOCK_DIST * MIN_DOOR_BLOCK_DIST ) /* ------------------------- NAV_HitNavGoal ------------------------- */ qboolean NAV_HitNavGoal( vec3_t point, vec3_t mins, vec3_t maxs, vec3_t dest, int radius, qboolean flying ) { vec3_t dmins, dmaxs, pmins, pmaxs; if ( radius ) { //NOTE: This needs to do a DistanceSquared on navgoals that had // a radius manually set! We can't do the smaller navgoals against // walls to get around this because player-sized traces to them // from angles will not work... - MCG if ( !flying ) {//Allow for a little z difference vec3_t diff; VectorSubtract( point, dest, diff ); if ( fabs(diff[2]) <= 24 ) { diff[2] = 0; } return (qboolean)( VectorLengthSquared( diff ) <= (radius*radius) ); } else {//must hit exactly return (qboolean)( DistanceSquared(dest, point) <= (radius*radius) ); } //There is probably a better way to do this, either by preserving the original // mins and maxs of the navgoal and doing this check ONLY if the radius // is non-zero (like the original implementation) or some boolean to // tell us to do this check rather than the fake bbox overlap check... } else { //Construct a dummy bounding box from our radius value VectorSet( dmins, -radius, -radius, -radius ); VectorSet( dmaxs, radius, radius, radius ); //Translate it VectorAdd( dmins, dest, dmins ); VectorAdd( dmaxs, dest, dmaxs ); //Translate the starting box VectorAdd( point, mins, pmins ); VectorAdd( point, maxs, pmaxs ); //See if they overlap return G_BoundsOverlap( pmins, pmaxs, dmins, dmaxs ); } } /* ------------------------- NAV_CheckAhead ------------------------- */ qboolean NAV_CheckAhead( gentity_t *self, vec3_t end, trace_t &trace, int clipmask ) { vec3_t mins; //Offset the step height VectorSet( mins, self->mins[0], self->mins[1], self->mins[2] + STEPSIZE ); gi.trace( &trace, self->currentOrigin, mins, self->maxs, end, self->s.number, clipmask, (EG2_Collision)0, 0 ); if ( trace.startsolid&&(trace.contents&CONTENTS_BOTCLIP) ) {//started inside do not enter, so ignore them clipmask &= ~CONTENTS_BOTCLIP; gi.trace( &trace, self->currentOrigin, mins, self->maxs, end, self->s.number, clipmask, (EG2_Collision)0, 0 ); } //Do a simple check if ( ( trace.allsolid == qfalse ) && ( trace.startsolid == qfalse ) && ( trace.fraction == 1.0f ) ) return qtrue; //See if we're too far above if ( fabs( self->currentOrigin[2] - end[2] ) > 48 ) return qfalse; //This is a work around float radius = ( self->maxs[0] > self->maxs[1] ) ? self->maxs[0] : self->maxs[1]; float dist = Distance( self->currentOrigin, end ); float tFrac = 1.0f - ( radius / dist ); if ( trace.fraction >= tFrac ) return qtrue; //Do a special check for doors if ( trace.entityNum < ENTITYNUM_WORLD ) { gentity_t *blocker = &g_entities[trace.entityNum]; if VALIDSTRING( blocker->classname ) { if ( G_EntIsUnlockedDoor( blocker->s.number ) ) //if ( Q_stricmp( blocker->classname, "func_door" ) == 0 ) { //We're too close, try and avoid the door (most likely stuck on a lip) if ( DistanceSquared( self->currentOrigin, trace.endpos ) < MIN_DOOR_BLOCK_DIST_SQR ) return qfalse; return qtrue; } } } return qfalse; } /* ------------------------- NPC_ClearPathToGoal ------------------------- */ qboolean NPC_ClearPathToGoal( vec3_t dir, gentity_t *goal ) { trace_t trace; //FIXME: What does do about area portals? THIS IS BROKEN //if ( gi.inPVS( NPC->currentOrigin, goal->currentOrigin ) == qfalse ) // return qfalse; //Look ahead and see if we're clear to move to our goal position if ( NAV_CheckAhead( NPC, goal->currentOrigin, trace, ( NPC->clipmask & ~CONTENTS_BODY )|CONTENTS_BOTCLIP ) ) { //VectorSubtract( goal->currentOrigin, NPC->currentOrigin, dir ); return qtrue; } if (!FlyingCreature(NPC)) { //See if we're too far above if ( fabs( NPC->currentOrigin[2] - goal->currentOrigin[2] ) > 48 ) return qfalse; } //This is a work around float radius = ( NPC->maxs[0] > NPC->maxs[1] ) ? NPC->maxs[0] : NPC->maxs[1]; float dist = Distance( NPC->currentOrigin, goal->currentOrigin ); float tFrac = 1.0f - ( radius / dist ); if ( trace.fraction >= tFrac ) return qtrue; //See if we're looking for a navgoal if ( goal->svFlags & SVF_NAVGOAL ) { //Okay, didn't get all the way there, let's see if we got close enough: if ( NAV_HitNavGoal( trace.endpos, NPC->mins, NPC->maxs, goal->currentOrigin, NPCInfo->goalRadius, FlyingCreature( NPC ) ) ) { //VectorSubtract(goal->currentOrigin, NPC->currentOrigin, dir); return qtrue; } } return qfalse; } qboolean NAV_DirSafe( gentity_t *self, vec3_t dir, float dist ) { vec3_t mins, end; trace_t trace; VectorMA( self->currentOrigin, dist, dir, end ); //Offset the step height VectorSet( mins, self->mins[0], self->mins[1], self->mins[2] + STEPSIZE ); gi.trace( &trace, self->currentOrigin, mins, self->maxs, end, self->s.number, CONTENTS_BOTCLIP, (EG2_Collision)0, 0 ); //Do a simple check if ( ( trace.allsolid == qfalse ) && ( trace.startsolid == qfalse ) && ( trace.fraction == 1.0f ) ) { return qtrue; } return qfalse; } qboolean NAV_MoveDirSafe( gentity_t *self, usercmd_t *cmd, float distScale = 1.0f ) { vec3_t moveDir; if ( !self || !self->client ) { return qtrue; } if ( !self->client->ps.speed ) { return qtrue; } if ( FlyingCreature( self ) ) { return qtrue; } if ( VectorCompare( self->client->ps.moveDir, vec3_origin ) ) {//no movedir, build from cmd if ( !cmd->forwardmove && !cmd->rightmove ) {//not moving at all return qtrue; } vec3_t fwd, right, fwdAngs = {0, self->currentAngles[YAW], 0}; AngleVectors( fwdAngs, fwd, right, NULL ); VectorScale( fwd, cmd->forwardmove, fwd ); VectorScale( right, cmd->rightmove, right ); VectorAdd( fwd, right, moveDir ); VectorNormalize( moveDir ); } else { VectorCopy( self->client->ps.moveDir, moveDir ); } return (NAV_DirSafe( self, moveDir, (self->client->ps.speed/10.0f)*distScale )); }