static qboolean GS_CheckBladeAutoAttack( player_state_t *playerState, int timeDelta ) { vec3_t origin, dir, end; trace_t trace; entity_state_t *targ, *player; gs_weapon_definition_t *weapondef = GS_GetWeaponDef( WEAP_GUNBLADE ); if( playerState->POVnum <= 0 || (int)playerState->POVnum > gs.maxclients ) return qfalse; if( !( playerState->pmove.stats[PM_STAT_FEATURES] & PMFEAT_GUNBLADEAUTOATTACK ) ) return qfalse; VectorCopy( playerState->pmove.origin, origin ); origin[2] += playerState->viewheight; AngleVectors( playerState->viewangles, dir, NULL, NULL ); VectorMA( origin, weapondef->firedef_weak.timeout, dir, end ); // check for a player to touch module_Trace( &trace, origin, vec3_origin, vec3_origin, end, playerState->POVnum, CONTENTS_BODY, timeDelta ); if( trace.ent <= 0 || trace.ent > gs.maxclients ) return qfalse; player = module_GetEntityState( playerState->POVnum, 0 ); targ = module_GetEntityState( trace.ent, 0 ); if( !( targ->effects & EF_TAKEDAMAGE ) || targ->type != ET_PLAYER ) return qfalse; if( GS_TeamBasedGametype() && ( targ->team == player->team ) ) return qfalse; return qtrue; }
//================= //GS_SlideMoveClipMove //================= static int GS_SlideMoveClipMove( move_t *move /*, const qboolean stepping*/ ) { vec3_t endpos, startingOrigin, startingVelocity; trace_t trace; int blockedmask = 0; VectorCopy( move->origin, startingOrigin ); VectorCopy( move->velocity, startingVelocity ); VectorMA( move->origin, move->remainingTime, move->velocity, endpos ); module_Trace( &trace, move->origin, move->mins, move->maxs, endpos, move->passent, move->contentmask, 0 ); if( trace.allsolid ) { if( trace.ent > 0 ) GS_AddTouchEnt( move, trace.ent ); return blockedmask|SLIDEMOVEFLAG_TRAPPED; } if( trace.fraction == 1.0f ) { // was able to cleanly perform the full move VectorCopy( trace.endpos, move->origin ); move->remainingTime -= ( trace.fraction * move->remainingTime ); return blockedmask|SLIDEMOVEFLAG_MOVED; } if( trace.fraction < 1.0f ) { // wasn't able to make the full move GS_AddTouchEnt( move, trace.ent ); blockedmask |= SLIDEMOVEFLAG_PLANE_TOUCHED; // move what can be moved if( trace.fraction > 0.0 ) { VectorCopy( trace.endpos, move->origin ); move->remainingTime -= ( trace.fraction * move->remainingTime ); blockedmask |= SLIDEMOVEFLAG_MOVED; } // if the plane is a wall and stepping, try to step it up if( !ISWALKABLEPLANE( trace.plane.normal ) ) { //if( stepping && GS_StepUp( move ) ) { // return blockedmask; // solved : don't add the clipping plane //} //else { blockedmask |= SLIDEMOVEFLAG_WALL_BLOCKED; //} } GS_AddClippingPlane( move, trace.plane.normal ); } return blockedmask; }
/* * GS_GoodPosition */ static qboolean GS_GoodPosition( int snaptorigin[3], vec3_t mins, vec3_t maxs, int passent, int contentmask ) { trace_t trace; vec3_t point; int i; if( !( contentmask & MASK_SOLID ) ) return qtrue; for( i = 0; i < 3; i++ ) point[i] = (float)snaptorigin[i] * ( 1.0/PM_VECTOR_SNAP ); module_Trace( &trace, point, mins, maxs, point, passent, contentmask, 0 ); return !trace.allsolid ? qtrue : qfalse; }
/* * GS_SlideMove */ int GS_SlideMove( move_t *move ) { #define MAX_SLIDEMOVE_ATTEMPTS 8 int count; int blockedmask = 0; vec3_t lastValidOrigin, originalVelocity; // if the velocity is too small, just stop if( VectorLength( move->velocity ) < STOP_EPSILON ) { VectorClear( move->velocity ); move->remainingTime = 0; return 0; } VectorCopy( move->velocity, originalVelocity ); VectorCopy( move->origin, lastValidOrigin ); GS_ClearClippingPlanes( move ); move->numtouch = 0; for( count = 0; count < MAX_SLIDEMOVE_ATTEMPTS; count++ ) { // get the original velocity and clip it to all the planes we got in the list VectorCopy( originalVelocity, move->velocity ); GS_ClipVelocityToClippingPlanes( move ); blockedmask = GS_SlideMoveClipMove( move /*, stepping*/ ); #ifdef CHECK_TRAPPED { trace_t trace; module_Trace( &trace, move->origin, move->mins, move->maxs, move->origin, move->passent, move->contentmask, 0 ); if( trace.startsolid ) { blockedmask |= SLIDEMOVEFLAG_TRAPPED; } } #endif // can't continue if( blockedmask & SLIDEMOVEFLAG_TRAPPED ) { #ifdef CHECK_TRAPPED module_Printf( "GS_SlideMove SLIDEMOVEFLAG_TRAPPED\n" ); #endif move->remainingTime = 0.0f; VectorCopy( lastValidOrigin, move->origin ); return blockedmask; } VectorCopy( move->origin, lastValidOrigin ); // touched a plane, re-clip velocity and retry if( blockedmask & SLIDEMOVEFLAG_PLANE_TOUCHED ) { continue; } // if it didn't touch anything the move should be completed if( move->remainingTime > 0.0f ) { module_Printf( "slidemove finished with remaining time\n" ); move->remainingTime = 0.0f; } break; } // snap GS_SnapPosition( move->origin, move->mins, move->maxs, move->passent, move->contentmask ); GS_SnapVelocity( move->velocity ); return blockedmask; }
/* * GS_TraceBullet */ trace_t *GS_TraceBullet( trace_t *trace, vec3_t start, vec3_t dir, float r, float u, int range, int ignore, int timeDelta ) { mat3_t axis; vec3_t end; qboolean water = qfalse; vec3_t water_start; int content_mask = MASK_SHOT | MASK_WATER; static trace_t water_trace; assert( trace ); VectorNormalizeFast( dir ); NormalVectorToAxis( dir, axis ); if( module_PointContents( start, timeDelta ) & MASK_WATER ) { water = qtrue; VectorCopy( start, water_start ); content_mask &= ~MASK_WATER; // ok, this isn't randomized, but I think we can live with it // the effect on water has never been properly noticed anyway //r *= BULLET_WATER_REFRACTION; //u *= BULLET_WATER_REFRACTION; } VectorMA( start, range, &axis[AXIS_FORWARD], end ); if( r ) VectorMA( end, r, &axis[AXIS_RIGHT], end ); if( u ) VectorMA( end, u, &axis[AXIS_UP], end ); module_Trace( trace, start, vec3_origin, vec3_origin, end, ignore, content_mask, timeDelta ); // see if we hit water if( trace->contents & MASK_WATER ) { water_trace = *trace; VectorCopy( trace->endpos, water_start ); #if 0 if( !VectorCompare( start, trace->endpos ) ) { vec3_t forward, right, up; // change bullet's course when it enters water VectorSubtract( end, start, dir ); VecToAngles( dir, dir ); AngleVectors( dir, forward, right, up ); // ok, this isn't randomized, but I think we can live with it // the effect on water has never been properly noticed anyway r *= BULLET_WATER_REFRACTION; u *= BULLET_WATER_REFRACTION; VectorMA( water_start, range, forward, end ); VectorMA( end, r, right, end ); VectorMA( end, u, up, end ); } #endif // re-trace ignoring water this time module_Trace( trace, water_start, vec3_origin, vec3_origin, end, ignore, MASK_SHOT, timeDelta ); return &water_trace; } if( water ) { water_trace = *trace; VectorCopy( water_start, water_trace.endpos ); return &water_trace; } return NULL; }
void GS_TraceLaserBeam( trace_t *trace, vec3_t origin, vec3_t angles, float range, int ignore, int timeDelta, void ( *impact )( trace_t *tr, vec3_t dir ) ) { vec3_t from, dir, end; int mask = MASK_SHOT; int passthrough = ignore; entity_state_t *hit; vec3_t mins = { -0.5, -0.5, -0.5 }; vec3_t maxs = { 0.5, 0.5, 0.5 }; int hits[MAX_BEAM_HIT_ENTITIES]; int j, numhits; assert( trace ); AngleVectors( angles, dir, NULL, NULL ); VectorCopy( origin, from ); VectorMA( origin, range, dir, end ); trace->ent = 0; numhits = 0; while( trace->ent != -1 ) { module_Trace( trace, from, mins, maxs, end, passthrough, mask, timeDelta ); if( trace->ent != -1 ) { // prevent endless loops by checking whether we have already impacted this entity for( j = 0; j < numhits; j++ ) { if( trace->ent == hits[j] ) break; } if( j < numhits ) break; // callback impact if( impact ) impact( trace, dir ); // check for pass-through hit = module_GetEntityState( trace->ent, timeDelta ); if( trace->ent == 0 || !hit || hit->solid == SOLID_BMODEL ) // can't pass through brush models break; // if trapped inside solid, just forget about it if( trace->fraction == 0 || trace->allsolid || trace->startsolid ) break; // put a limit on number of hit entities if( numhits < MAX_BEAM_HIT_ENTITIES ) hits[numhits++] = trace->ent; else break; passthrough = trace->ent; VectorCopy( trace->endpos, from ); } } if( trace->ent != -1 ) // was interrupted by a brush model impact { } }