/* =============== Set muzzle location relative to pivoting eye. =============== */ void G_CalcMuzzlePoint( gentity_t *self, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) { vec3_t normal; VectorCopy( self->client->ps.origin, muzzlePoint ); BG_GetClientNormal( &self->client->ps, normal ); VectorMA( muzzlePoint, self->client->ps.viewheight, normal, muzzlePoint ); VectorMA( muzzlePoint, 1, forward, muzzlePoint ); // snap to integer coordinates for more efficient network bandwidth usage SnapVector( muzzlePoint ); }
// this causes a compiler bug on mac MrC compiler static void CG_StepOffset( void ) { float steptime; int timeDelta; vec3_t normal; playerState_t *ps = &cg.predictedPlayerState; BG_GetClientNormal( ps, normal ); steptime = BG_Class( ps->stats[ STAT_CLASS ] )->steptime; // smooth out stair climbing timeDelta = cg.time - cg.stepTime; if( timeDelta < steptime ) { float stepChange = cg.stepChange * (steptime - timeDelta) / steptime; VectorMA( cg.refdef.vieworg, -stepChange, normal, cg.refdef.vieworg ); } }
// TODO: Use new vector math library for all calculations. void ArmorComponent::HandleApplyDamageModifier(float& damage, Util::optional<Vec3> location, Util::optional<Vec3> direction, int flags, meansOfDeath_t meansOfDeath) { vec3_t origin, bulletPath, bulletAngle, locationRelativeToFloor, floor, normal; // TODO: Remove dependency on client. assert(entity.oldEnt->client); // Use non-regional damage where appropriate. if (flags & DAMAGE_NO_LOCDAMAGE || !location) { // TODO: Move G_GetNonLocDamageMod to ArmorComponent. damage *= GetNonLocationalDamageMod(); return; } // Get hit location relative to the floor beneath. if (g_unlagged.integer && entity.oldEnt->client->unlaggedCalc.used) { VectorCopy(entity.oldEnt->client->unlaggedCalc.origin, origin); } else { VectorCopy(entity.oldEnt->r.currentOrigin, origin); } BG_GetClientNormal(&entity.oldEnt->client->ps, normal); VectorMA(origin, entity.oldEnt->r.mins[2], normal, floor); VectorSubtract(location.value().Data(), floor, locationRelativeToFloor); // Get fraction of height where the hit landed. float height = entity.oldEnt->r.maxs[2] - entity.oldEnt->r.mins[2]; if (!height) height = 1.0f; float hitRatio = Math::Clamp(DotProduct(normal, locationRelativeToFloor) / VectorLength(normal), 0.0f, height) / height; // Get the yaw of the attack relative to the target's view yaw VectorSubtract(location.value().Data(), origin, bulletPath); vectoangles(bulletPath, bulletAngle); int hitRotation = AngleNormalize360(entity.oldEnt->client->ps.viewangles[YAW] - bulletAngle[YAW]); // Use regional modifier. // TODO: Move G_GetPointDamageMod to ArmorComponent. damage *= GetLocationalDamageMod(hitRotation, hitRatio); }
/* ============= CG_DrawDir Draw dot marking the direction to an enemy ============= */ static void CG_DrawDir( rectDef_t *rect, vec3_t origin, vec4_t colour ) { vec3_t drawOrigin; vec3_t noZOrigin; vec3_t normal, antinormal, normalDiff; vec3_t view, noZview; vec3_t up = { 0.0f, 0.0f, 1.0f }; vec3_t top = { 0.0f, -1.0f, 0.0f }; float angle; playerState_t *ps = &cg.snap->ps; BG_GetClientNormal( ps, normal ); AngleVectors( entityPositions.vangles, view, NULL, NULL ); ProjectPointOnPlane( noZOrigin, origin, normal ); ProjectPointOnPlane( noZview, view, normal ); VectorNormalize( noZOrigin ); VectorNormalize( noZview ); //calculate the angle between the images of the blip and the view angle = RAD2DEG( acos( DotProduct( noZOrigin, noZview ) ) ); CrossProduct( noZOrigin, noZview, antinormal ); VectorNormalize( antinormal ); //decide which way to rotate VectorSubtract( normal, antinormal, normalDiff ); if( VectorLength( normalDiff ) < 1.0f ) angle = 360.0f - angle; RotatePointAroundVector( drawOrigin, up, top, angle ); trap_R_SetColor( colour ); CG_DrawPic( rect->x + ( rect->w / 2 ) - ( BLIPX2 / 2 ) - drawOrigin[ 0 ] * ( rect->w / 2 ), rect->y + ( rect->h / 2 ) - ( BLIPY2 / 2 ) + drawOrigin[ 1 ] * ( rect->h / 2 ), BLIPX2, BLIPY2, cgs.media.scannerBlipShader ); trap_R_SetColor( NULL ); }
bool BotPathIsWalkable( gentity_t *self, botTarget_t target ) { vec3_t selfPos, targetPos; vec3_t viewNormal; botTrace_t trace; BG_GetClientNormal( &self->client->ps, viewNormal ); VectorMA( self->s.origin, self->r.mins[2], viewNormal, selfPos ); BotGetTargetPos( target, targetPos ); if ( !trap_BotNavTrace( self->s.number, &trace, selfPos, targetPos ) ) { return false; } if ( trace.frac >= 1.0f ) { return true; } else { return false; } }
/* ================== PM_StepSlideMove ================== */ qboolean PM_StepSlideMove( qboolean gravity, qboolean predictive ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; vec3_t normal; vec3_t step_v, step_vNormal; vec3_t up, down; float stepSize; qboolean stepped = qfalse; BG_GetClientNormal( pm->ps, normal ); VectorCopy( pm->ps->origin, start_o ); VectorCopy( pm->ps->velocity, start_v ); if ( PM_SlideMove( gravity ) == 0 ) { VectorCopy( start_o, down ); VectorMA( down, -STEPSIZE, normal, down ); pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); //we can step down if ( trace.fraction > 0.01f && trace.fraction < 1.0f && !trace.allsolid && pml.groundPlane != qfalse ) { if ( pm->debugLevel > 1 ) { Com_Printf( "%d: step down\n", c_pmove ); } stepped = qtrue; } } else { VectorCopy( start_o, down ); VectorMA( down, -STEPSIZE, normal, down ); pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); // never step up when you still have up velocity if ( DotProduct( trace.plane.normal, pm->ps->velocity ) > 0.0f && ( trace.fraction == 1.0f || DotProduct( trace.plane.normal, normal ) < 0.7f ) ) { return stepped; } VectorCopy( pm->ps->origin, down_o ); VectorCopy( pm->ps->velocity, down_v ); VectorCopy( start_o, up ); VectorMA( up, STEPSIZE, normal, up ); // test the player position if they were a stepheight higher pm->trace( &trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask ); if ( trace.allsolid ) { if ( pm->debugLevel > 1 ) { Com_Printf( "%i:bend can't step\n", c_pmove ); } return stepped; // can't step up } VectorSubtract( trace.endpos, start_o, step_v ); VectorCopy( step_v, step_vNormal ); VectorNormalize( step_vNormal ); stepSize = DotProduct( normal, step_vNormal ) * VectorLength( step_v ); // try slidemove from this position VectorCopy( trace.endpos, pm->ps->origin ); VectorCopy( start_v, pm->ps->velocity ); if ( PM_SlideMove( gravity ) == 0 ) { if ( pm->debugLevel > 1 ) { Com_Printf( "%d: step up\n", c_pmove ); } stepped = qtrue; } // push down the final amount VectorCopy( pm->ps->origin, down ); VectorMA( down, -stepSize, normal, down ); pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); if ( !trace.allsolid ) { VectorCopy( trace.endpos, pm->ps->origin ); } if ( trace.fraction < 1.0f ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity ); } } if ( !predictive && stepped ) { PM_StepEvent( start_o, pm->ps->origin, normal ); } return stepped; }
/* =============== CG_OffsetFirstPersonView =============== */ void CG_OffsetFirstPersonView( void ) { float *origin; float *angles; float bob; float ratio; float delta; float speed; float f; vec3_t predictedVelocity; int timeDelta; float bob2; vec3_t normal, baseOrigin; playerState_t *ps = &cg.predictedPlayerState; BG_GetClientNormal( ps, normal ); if( cg.snap->ps.pm_type == PM_INTERMISSION ) return; origin = cg.refdef.vieworg; angles = cg.refdefViewAngles; VectorCopy( origin, baseOrigin ); // if dead, fix the angle and don't add any kick if( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 ) { angles[ ROLL ] = 40; angles[ PITCH ] = -15; angles[ YAW ] = cg.snap->ps.stats[ STAT_VIEWLOCK ]; origin[ 2 ] += cg.predictedPlayerState.viewheight; return; } // camera shake effect else if( cg.snap->ps.stats[ STAT_SHAKE ] > 0 ) { float fac; fac = (float) cg.snap->ps.stats[ STAT_SHAKE ] * cg_cameraShakeMagnitude.value * 0.15f; angles[ 0 ] += crandom() * fac; angles[ 1 ] += crandom() * fac; angles[ 2 ] += crandom() * fac; } // add angles based on damage kick if( cg.damageTime ) { ratio = cg.time - cg.damageTime; if( ratio < DAMAGE_DEFLECT_TIME ) { ratio /= DAMAGE_DEFLECT_TIME; angles[ PITCH ] += ratio * cg.v_dmg_pitch; angles[ ROLL ] += ratio * cg.v_dmg_roll; } else { ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME; if( ratio > 0 ) { angles[ PITCH ] += ratio * cg.v_dmg_pitch; angles[ ROLL ] += ratio * cg.v_dmg_roll; } } } // add pitch based on fall kick #if 0 ratio = ( cg.time - cg.landTime) / FALL_TIME; if (ratio < 0) ratio = 0; angles[PITCH] += ratio * cg.fall_value; #endif // add angles based on velocity VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity ); delta = DotProduct( predictedVelocity, cg.refdef.viewaxis[ 0 ] ); angles[ PITCH ] += delta * cg_runpitch.value; delta = DotProduct( predictedVelocity, cg.refdef.viewaxis[ 1 ] ); angles[ ROLL ] -= delta * cg_runroll.value; // add angles based on bob // bob amount is class dependant if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) bob2 = 0.0f; else bob2 = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->bob; #define LEVEL4_FEEDBACK 10.0f //give a charging player some feedback if( ps->weapon == WP_ALEVEL4 ) { if( ps->stats[ STAT_MISC ] > 0 ) { float fraction = (float)ps->stats[ STAT_MISC ] / LEVEL4_TRAMPLE_CHARGE_MAX; if( fraction > 1.0f ) fraction = 1.0f; bob2 *= ( 1.0f + fraction * LEVEL4_FEEDBACK ); } } if( bob2 != 0.0f ) { // make sure the bob is visible even at low speeds speed = cg.xyspeed > 200 ? cg.xyspeed : 200; delta = cg.bobfracsin * ( bob2 ) * speed; if( cg.predictedPlayerState.pm_flags & PMF_DUCKED ) delta *= 3; // crouching angles[ PITCH ] += delta; delta = cg.bobfracsin * ( bob2 ) * speed; if( cg.predictedPlayerState.pm_flags & PMF_DUCKED ) delta *= 3; // crouching accentuates roll if( cg.bobcycle & 1 ) delta = -delta; angles[ ROLL ] += delta; } #define LEVEL3_FEEDBACK 20.0f //provide some feedback for pouncing if( ( cg.predictedPlayerState.weapon == WP_ALEVEL3 || cg.predictedPlayerState.weapon == WP_ALEVEL3_UPG ) && cg.predictedPlayerState.stats[ STAT_MISC ] > 0 ) { float fraction1, fraction2; vec3_t forward; AngleVectors( angles, forward, NULL, NULL ); VectorNormalize( forward ); fraction1 = (float)cg.predictedPlayerState.stats[ STAT_MISC ] / LEVEL3_POUNCE_TIME_UPG; if( fraction1 > 1.0f ) fraction1 = 1.0f; fraction2 = -sin( fraction1 * M_PI / 2 ); VectorMA( origin, LEVEL3_FEEDBACK * fraction2, forward, origin ); } #define STRUGGLE_DIST 5.0f #define STRUGGLE_TIME 250 //allow the player to struggle a little whilst grabbed if( cg.predictedPlayerState.pm_type == PM_GRABBED ) { vec3_t forward, right, up; usercmd_t cmd; int cmdNum; float fFraction, rFraction, uFraction; float fFraction2, rFraction2, uFraction2; cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); AngleVectors( angles, forward, right, up ); fFraction = (float)( cg.time - cg.forwardMoveTime ) / STRUGGLE_TIME; rFraction = (float)( cg.time - cg.rightMoveTime ) / STRUGGLE_TIME; uFraction = (float)( cg.time - cg.upMoveTime ) / STRUGGLE_TIME; if( fFraction > 1.0f ) fFraction = 1.0f; if( rFraction > 1.0f ) rFraction = 1.0f; if( uFraction > 1.0f ) uFraction = 1.0f; fFraction2 = -sin( fFraction * M_PI / 2 ); rFraction2 = -sin( rFraction * M_PI / 2 ); uFraction2 = -sin( uFraction * M_PI / 2 ); if( cmd.forwardmove > 0 ) VectorMA( origin, STRUGGLE_DIST * fFraction, forward, origin ); else if( cmd.forwardmove < 0 ) VectorMA( origin, -STRUGGLE_DIST * fFraction, forward, origin ); else cg.forwardMoveTime = cg.time; if( cmd.rightmove > 0 ) VectorMA( origin, STRUGGLE_DIST * rFraction, right, origin ); else if( cmd.rightmove < 0 ) VectorMA( origin, -STRUGGLE_DIST * rFraction, right, origin ); else cg.rightMoveTime = cg.time; if( cmd.upmove > 0 ) VectorMA( origin, STRUGGLE_DIST * uFraction, up, origin ); else if( cmd.upmove < 0 ) VectorMA( origin, -STRUGGLE_DIST * uFraction, up, origin ); else cg.upMoveTime = cg.time; } if( ( cg.predictedPlayerEntity.currentState.eFlags & EF_POISONCLOUDED ) && ( cg.time - cg.poisonedTime < PCLOUD_DISORIENT_DURATION) && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { float scale, fraction, pitchFraction; scale = 1.0f - (float)( cg.time - cg.poisonedTime ) / BG_PlayerPoisonCloudTime( &cg.predictedPlayerState ); if( scale < 0.0f ) scale = 0.0f; fraction = sin( ( cg.time - cg.poisonedTime ) / 500.0f * M_PI * PCLOUD_ROLL_FREQUENCY ) * scale; pitchFraction = sin( ( cg.time - cg.poisonedTime ) / 200.0f * M_PI * PCLOUD_ROLL_FREQUENCY ) * scale; angles[ ROLL ] += fraction * PCLOUD_ROLL_AMPLITUDE; angles[ YAW ] += fraction * PCLOUD_ROLL_AMPLITUDE; angles[ PITCH ] += pitchFraction * PCLOUD_ROLL_AMPLITUDE / 2.0f; } // this *feels* more realisitic for humans if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS && ( cg.predictedPlayerState.pm_type == PM_NORMAL || cg.predictedPlayerState.pm_type == PM_JETPACK ) ) { angles[PITCH] += cg.bobfracsin * bob2 * 0.5; // heavy breathing effects //FIXME: sound if( cg.predictedPlayerState.stats[ STAT_STAMINA ] < STAMINA_BREATHING_LEVEL ) { float deltaBreath = ( cg.predictedPlayerState.stats[ STAT_STAMINA ] - STAMINA_BREATHING_LEVEL ) / -250.0; float deltaAngle = cos( (float)cg.time/150.0 ) * deltaBreath; deltaAngle += ( deltaAngle < 0 ? -deltaAngle : deltaAngle ) * 0.5; angles[ PITCH ] -= deltaAngle; } } //=================================== // add view height VectorMA( origin, ps->viewheight, normal, origin ); // smooth out duck height changes timeDelta = cg.time - cg.duckTime; if( timeDelta < DUCK_TIME) { cg.refdef.vieworg[ 2 ] -= cg.duckChange * ( DUCK_TIME - timeDelta ) / DUCK_TIME; } // add bob height bob = cg.bobfracsin * cg.xyspeed * bob2; if( bob > 6 ) bob = 6; VectorMA( origin, bob, normal, origin ); // add fall height delta = cg.time - cg.landTime; if( delta < LAND_DEFLECT_TIME ) { f = delta / LAND_DEFLECT_TIME; cg.refdef.vieworg[ 2 ] += cg.landChange * f; } else if( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) { delta -= LAND_DEFLECT_TIME; f = 1.0 - ( delta / LAND_RETURN_TIME ); cg.refdef.vieworg[ 2 ] += cg.landChange * f; } // add step offset CG_StepOffset( ); }
/* =============== CG_OffsetThirdPersonView =============== */ void CG_OffsetThirdPersonView( void ) { int i; vec3_t forward, right, up; vec3_t view; trace_t trace; static vec3_t mins = { -8, -8, -8 }; static vec3_t maxs = { 8, 8, 8 }; vec3_t focusPoint; vec3_t surfNormal; int cmdNum; usercmd_t cmd, oldCmd; float range; vec3_t mouseInputAngles; vec3_t rotationAngles; vec3_t axis[ 3 ], rotaxis[ 3 ]; float deltaPitch; static float pitch; static vec3_t killerPos = { 0, 0, 0 }; // If cg_thirdpersonShoulderViewMode == 2, do shoulder view instead // If cg_thirdpersonShoulderViewMode == 1, do shoulder view when chasing // a wallwalker because it's really erratic to watch if( ( cg_thirdPersonShoulderViewMode.integer == 2 ) || ( ( cg_thirdPersonShoulderViewMode.integer == 1 ) && ( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) && ( cg.snap->ps.stats[ STAT_HEALTH ] > 0 ) ) ) { CG_OffsetShoulderView( ); return; } BG_GetClientNormal( &cg.predictedPlayerState, surfNormal ); // Set the view origin to the class's view height VectorMA( cg.refdef.vieworg, cg.predictedPlayerState.viewheight, surfNormal, cg.refdef.vieworg ); // Set the focus point where the camera will look (at the player's vieworg) VectorCopy( cg.refdef.vieworg, focusPoint ); // If player is dead, we want the player to be between us and the killer // so pretend that the player was looking at the killer, then place cam behind them. if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 ) { int killerEntNum = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ]; // already looking at ourself if( killerEntNum != cg.snap->ps.clientNum ) { vec3_t lookDirection; if( cg.wasDeadLastFrame == qfalse || !cg_staticDeathCam.integer ) { VectorCopy( cg_entities[ killerEntNum ].lerpOrigin, killerPos ); cg.wasDeadLastFrame = qtrue; } VectorSubtract( killerPos, cg.refdef.vieworg, lookDirection ); vectoangles( lookDirection, cg.refdefViewAngles ); } } // get and rangecheck cg_thirdPersonRange range = cg_thirdPersonRange.value; if( range > 150.0f ) range = 150.0f; if( range < 30.0f ) range = 30.0f; // Calculate the angle of the camera's position around the player. // Unless in demo, PLAYING in third person, or in dead-third-person cam, allow the player // to control camera position offsets using the mouse position. if( cg.demoPlayback || ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) && ( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) ) ) { // Collect our input values from the mouse. cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); trap_GetUserCmd( cmdNum - 1, &oldCmd ); // Prevent pitch from wrapping and clamp it within a [-75, 90] range. // Cgame has no access to ps.delta_angles[] here, so we need to reproduce // it ourselves. deltaPitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] ); if( fabs(deltaPitch) < 200.0f ) { pitch += deltaPitch; } mouseInputAngles[ PITCH ] = pitch; mouseInputAngles[ YAW ] = -1.0f * SHORT2ANGLE( cmd.angles[ YAW ] ); // yaw is inverted mouseInputAngles[ ROLL ] = 0.0f; for( i = 0; i < 3; i++ ) mouseInputAngles[ i ] = AngleNormalize180( mouseInputAngles[ i ] ); // Set the rotation angles to be the view angles offset by the mouse input // Ignore the original pitch though; it's too jerky otherwise if( !cg_thirdPersonPitchFollow.integer ) cg.refdefViewAngles[ PITCH ] = 0.0f; for( i = 0; i < 3; i++ ) { rotationAngles[ i ] = AngleNormalize180(cg.refdefViewAngles[ i ]) + mouseInputAngles[ i ]; AngleNormalize180( rotationAngles[ i ] ); } // Don't let pitch go too high/too low or the camera flips around and // that's really annoying. // However, when we're not on the floor or ceiling (wallwalk) pitch // may not be pitch, so just let it go. if( surfNormal[ 2 ] > 0.5f || surfNormal[ 2 ] < -0.5f ) { if( rotationAngles[ PITCH ] > 85.0f ) rotationAngles[ PITCH ] = 85.0f; else if( rotationAngles[ PITCH ] < -85.0f ) rotationAngles[ PITCH ] = -85.0f; } // Perform the rotations specified by rotationAngles. AnglesToAxis( rotationAngles, axis ); if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) || !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse, cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) ) AxisCopy( axis, rotaxis ); // Convert the new axis back to angles. AxisToAngles( rotaxis, rotationAngles ); } else { if( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) { // If we're playing the game in third person, the viewangles already // take care of our mouselook, so just use them. for( i = 0; i < 3; i++ ) rotationAngles[ i ] = cg.refdefViewAngles[ i ]; } else // dead { rotationAngles[ PITCH ] = 20.0f; rotationAngles[ YAW ] = cg.refdefViewAngles[ YAW ]; } } rotationAngles[ YAW ] -= cg_thirdPersonAngle.value; // Move the camera range distance back. AngleVectors( rotationAngles, forward, right, up ); VectorCopy( cg.refdef.vieworg, view ); VectorMA( view, -range, forward, view ); // Ensure that the current camera position isn't out of bounds and that there // is nothing between the camera and the player. if( !cg_cameraMode.integer ) { // Trace a ray from the origin to the viewpoint to make sure the view isn't // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); if( trace.fraction != 1.0f ) { VectorCopy( trace.endpos, view ); view[ 2 ] += ( 1.0f - trace.fraction ) * 32; // Try another trace to this position, because a tunnel may have the ceiling // close enogh that this is poking out. CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); VectorCopy( trace.endpos, view ); } } // Set the camera position to what we calculated. VectorCopy( view, cg.refdef.vieworg ); // The above checks may have moved the camera such that the existing viewangles // may not still face the player. Recalculate them to do so. // but if we're dead, don't bother because we'd rather see what killed us if( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) { VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint ); vectoangles( focusPoint, cg.refdefViewAngles ); } }
/* =============== CG_smoothWWTransitions =============== */ static void CG_smoothWWTransitions( playerState_t *ps, const vec3_t in, vec3_t out ) { vec3_t surfNormal, rotAxis, temp; vec3_t refNormal = { 0.0f, 0.0f, 1.0f }; vec3_t ceilingNormal = { 0.0f, 0.0f, -1.0f }; int i; float stLocal, sFraction, rotAngle; float smoothTime, timeMod; qboolean performed = qfalse; vec3_t inAxis[ 3 ], lastAxis[ 3 ], outAxis[ 3 ]; if( cg.snap->ps.pm_flags & PMF_FOLLOW ) { VectorCopy( in, out ); return; } //set surfNormal BG_GetClientNormal( ps, surfNormal ); AnglesToAxis( in, inAxis ); //if we are moving from one surface to another smooth the transition if( !VectorCompare( surfNormal, cg.lastNormal ) ) { //if we moving from the ceiling to the floor special case //( x product of colinear vectors is undefined) if( VectorCompare( ceilingNormal, cg.lastNormal ) && VectorCompare( refNormal, surfNormal ) ) { AngleVectors( in, temp, NULL, NULL ); ProjectPointOnPlane( rotAxis, temp, refNormal ); VectorNormalize( rotAxis ); rotAngle = 180.0f; timeMod = 1.5f; } else { AnglesToAxis( cg.lastVangles, lastAxis ); rotAngle = DotProduct( inAxis[ 0 ], lastAxis[ 0 ] ) + DotProduct( inAxis[ 1 ], lastAxis[ 1 ] ) + DotProduct( inAxis[ 2 ], lastAxis[ 2 ] ); rotAngle = RAD2DEG( acos( ( rotAngle - 1.0f ) / 2.0f ) ); CrossProduct( lastAxis[ 0 ], inAxis[ 0 ], temp ); VectorCopy( temp, rotAxis ); CrossProduct( lastAxis[ 1 ], inAxis[ 1 ], temp ); VectorAdd( rotAxis, temp, rotAxis ); CrossProduct( lastAxis[ 2 ], inAxis[ 2 ], temp ); VectorAdd( rotAxis, temp, rotAxis ); VectorNormalize( rotAxis ); timeMod = 1.0f; } //add the op CG_addSmoothOp( rotAxis, rotAngle, timeMod ); } //iterate through ops for( i = MAXSMOOTHS - 1; i >= 0; i-- ) { smoothTime = (int)( cg_wwSmoothTime.integer * cg.sList[ i ].timeMod ); //if this op has time remaining, perform it if( cg.time < cg.sList[ i ].time + smoothTime ) { stLocal = 1.0f - ( ( ( cg.sList[ i ].time + smoothTime ) - cg.time ) / smoothTime ); sFraction = -( cos( stLocal * M_PI ) + 1.0f ) / 2.0f; RotatePointAroundVector( outAxis[ 0 ], cg.sList[ i ].rotAxis, inAxis[ 0 ], sFraction * cg.sList[ i ].rotAngle ); RotatePointAroundVector( outAxis[ 1 ], cg.sList[ i ].rotAxis, inAxis[ 1 ], sFraction * cg.sList[ i ].rotAngle ); RotatePointAroundVector( outAxis[ 2 ], cg.sList[ i ].rotAxis, inAxis[ 2 ], sFraction * cg.sList[ i ].rotAngle ); AxisCopy( outAxis, inAxis ); performed = qtrue; } } //if we performed any ops then return the smoothed angles //otherwise simply return the in angles if( performed ) AxisToAngles( outAxis, out ); else VectorCopy( in, out ); //copy the current normal to the lastNormal VectorCopy( in, cg.lastVangles ); VectorCopy( surfNormal, cg.lastNormal ); }
static float CalcDamageModifier( vec3_t point, gentity_t *target, class_t pcl, int damageFlags ) { vec3_t targOrigin, bulletPath, bulletAngle, pMINUSfloor, floor, normal; float clientHeight, hitRelative, hitRatio, modifier; int hitRotation; // handle nonlocational damage if ( damageFlags & DAMAGE_NO_LOCDAMAGE ) { return G_GetNonLocDamageMod( pcl ); } // need a valid point for point damage if ( point == NULL ) { return 1.0f; } // Get the point location relative to the floor under the target if ( g_unlagged.integer && target->client && target->client->unlaggedCalc.used ) { VectorCopy( target->client->unlaggedCalc.origin, targOrigin ); } else { VectorCopy( target->r.currentOrigin, targOrigin ); } BG_GetClientNormal( &target->client->ps, normal ); VectorMA( targOrigin, target->r.mins[ 2 ], normal, floor ); VectorSubtract( point, floor, pMINUSfloor ); // Get the proportion of the target height where the hit landed clientHeight = target->r.maxs[ 2 ] - target->r.mins[ 2 ]; if ( !clientHeight ) { clientHeight = 1.0f; } hitRelative = DotProduct( normal, pMINUSfloor ) / VectorLength( normal ); if ( hitRelative < 0.0f ) { hitRelative = 0.0f; } if ( hitRelative > clientHeight ) { hitRelative = clientHeight; } hitRatio = hitRelative / clientHeight; // Get the yaw of the attack relative to the target's view yaw VectorSubtract( point, targOrigin, bulletPath ); vectoangles( bulletPath, bulletAngle ); hitRotation = AngleNormalize360( target->client->ps.viewangles[ YAW ] - bulletAngle[ YAW ] ); // Get damage region modifier modifier = G_GetPointDamageMod( target, pcl, hitRotation, hitRatio ); return modifier; }
/* =============== CG_OffsetFirstPersonView =============== */ void CG_OffsetFirstPersonView( void ) { float *origin; float *angles; float bob; float ratio; float delta; float speed; float f; vec3_t predictedVelocity; int timeDelta; float bob2; vec3_t normal, baseOrigin; playerState_t *ps = &cg.predictedPlayerState; BG_GetClientNormal( ps, normal ); if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { return; } origin = cg.refdef.vieworg; angles = cg.refdefViewAngles; VectorCopy( origin, baseOrigin ); // if dead, fix the angle and don't add any kick if ( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 ) { angles[ ROLL ] = 40; angles[ PITCH ] = -15; angles[ YAW ] = cg.snap->ps.stats[ STAT_VIEWLOCK ]; origin[ 2 ] += cg.predictedPlayerState.viewheight; return; } // add angles based on damage kick if ( cg.damageTime ) { ratio = cg.time - cg.damageTime; if ( ratio < DAMAGE_DEFLECT_TIME ) { ratio /= DAMAGE_DEFLECT_TIME; angles[ PITCH ] += ratio * cg.v_dmg_pitch; angles[ ROLL ] += ratio * cg.v_dmg_roll; } else { ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME; if ( ratio > 0 ) { angles[ PITCH ] += ratio * cg.v_dmg_pitch; angles[ ROLL ] += ratio * cg.v_dmg_roll; } } } // add pitch based on fall kick #if 0 ratio = ( cg.time - cg.landTime ) / FALL_TIME; if ( ratio < 0 ) { ratio = 0; } angles[ PITCH ] += ratio * cg.fall_value; #endif // add angles based on velocity VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity ); delta = DotProduct( predictedVelocity, cg.refdef.viewaxis[ 0 ] ); angles[ PITCH ] += delta * cg_runpitch.value; delta = DotProduct( predictedVelocity, cg.refdef.viewaxis[ 1 ] ); angles[ ROLL ] -= delta * cg_runroll.value; // add angles based on bob // bob amount is class-dependent if ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) { bob2 = 0.0f; } else { bob2 = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->bob; } #define LEVEL4_FEEDBACK 10.0f //give a charging player some feedback if ( ps->weapon == WP_ALEVEL4 ) { if ( ps->stats[ STAT_MISC ] > 0 ) { float fraction = ( float ) ps->stats[ STAT_MISC ] / LEVEL4_TRAMPLE_CHARGE_MAX; if ( fraction > 1.0f ) { fraction = 1.0f; } bob2 *= ( 1.0f + fraction * LEVEL4_FEEDBACK ); } } if ( bob2 != 0.0f ) { // make sure the bob is visible even at low speeds speed = cg.xyspeed > 200 ? cg.xyspeed : 200; delta = cg.bobfracsin * ( bob2 ) * speed; if ( cg.predictedPlayerState.pm_flags & PMF_DUCKED ) { delta *= 3; // crouching } angles[ PITCH ] += delta; delta = cg.bobfracsin * ( bob2 ) * speed; if ( cg.predictedPlayerState.pm_flags & PMF_DUCKED ) { delta *= 3; // crouching accentuates roll } if ( cg.bobcycle & 1 ) { delta = -delta; } angles[ ROLL ] += delta; } #define LEVEL3_FEEDBACK 20.0f //provide some feedback for pouncing if ( ( cg.predictedPlayerState.weapon == WP_ALEVEL3 || cg.predictedPlayerState.weapon == WP_ALEVEL3_UPG ) && cg.predictedPlayerState.stats[ STAT_MISC ] > 0 ) { float fraction1, fraction2; vec3_t forward; AngleVectors( angles, forward, NULL, NULL ); VectorNormalize( forward ); fraction1 = ( float ) cg.predictedPlayerState.stats[ STAT_MISC ] / LEVEL3_POUNCE_TIME_UPG; if ( fraction1 > 1.0f ) { fraction1 = 1.0f; } fraction2 = -sin( fraction1 * M_PI / 2 ); VectorMA( origin, LEVEL3_FEEDBACK * fraction2, forward, origin ); } #define STRUGGLE_DIST 5.0f #define STRUGGLE_TIME 250 //allow the player to struggle a little whilst grabbed if ( cg.predictedPlayerState.pm_type == PM_GRABBED ) { vec3_t forward, right, up; usercmd_t cmd; int cmdNum; float fFraction, rFraction, uFraction; cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); AngleVectors( angles, forward, right, up ); fFraction = ( float )( cg.time - cg.forwardMoveTime ) / STRUGGLE_TIME; rFraction = ( float )( cg.time - cg.rightMoveTime ) / STRUGGLE_TIME; uFraction = ( float )( cg.time - cg.upMoveTime ) / STRUGGLE_TIME; if ( fFraction > 1.0f ) { fFraction = 1.0f; } if ( rFraction > 1.0f ) { rFraction = 1.0f; } if ( uFraction > 1.0f ) { uFraction = 1.0f; } if ( cmd.forwardmove > 0 ) { VectorMA( origin, STRUGGLE_DIST * fFraction, forward, origin ); } else if ( cmd.forwardmove < 0 ) { VectorMA( origin, -STRUGGLE_DIST * fFraction, forward, origin ); } else { cg.forwardMoveTime = cg.time; } if ( cmd.rightmove > 0 ) { VectorMA( origin, STRUGGLE_DIST * rFraction, right, origin ); } else if ( cmd.rightmove < 0 ) { VectorMA( origin, -STRUGGLE_DIST * rFraction, right, origin ); } else { cg.rightMoveTime = cg.time; } if ( cmd.upmove > 0 ) { VectorMA( origin, STRUGGLE_DIST * uFraction, up, origin ); } else if ( cmd.upmove < 0 ) { VectorMA( origin, -STRUGGLE_DIST * uFraction, up, origin ); } else { cg.upMoveTime = cg.time; } } // this *feels* more realisitic for humans <- this comment feels very descriptive if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_HUMANS && cg.predictedPlayerState.pm_type == PM_NORMAL ) { angles[ PITCH ] += cg.bobfracsin * bob2 * 0.5; } // add view height VectorMA( origin, ps->viewheight, normal, origin ); // smooth out duck height changes timeDelta = cg.time - cg.duckTime; if ( timeDelta < DUCK_TIME ) { cg.refdef.vieworg[ 2 ] -= cg.duckChange * ( DUCK_TIME - timeDelta ) / DUCK_TIME; } // add bob height bob = cg.bobfracsin * cg.xyspeed * bob2; if ( bob > 6 ) { bob = 6; } VectorMA( origin, bob, normal, origin ); // add fall height delta = cg.time - cg.landTime; if ( delta < LAND_DEFLECT_TIME ) { f = delta / LAND_DEFLECT_TIME; cg.refdef.vieworg[ 2 ] += cg.landChange * f; } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) { delta -= LAND_DEFLECT_TIME; f = 1.0 - ( delta / LAND_RETURN_TIME ); cg.refdef.vieworg[ 2 ] += cg.landChange * f; } // add step offset CG_StepOffset(); }
/* ================== PM_StepSlideMove ================== */ bool PM_StepSlideMove( bool gravity, bool predictive ) { vec3_t start_o, start_v; #ifndef UNREALARENA vec3_t down_o, down_v; #endif trace_t trace; #ifndef UNREALARENA vec3_t normal; vec3_t step_v, step_vNormal; #endif vec3_t up, down; float stepSize; bool stepped = false; #ifndef UNREALARENA BG_GetClientNormal( pm->ps, normal ); #endif VectorCopy( pm->ps->origin, start_o ); VectorCopy( pm->ps->velocity, start_v ); if ( !PM_SlideMove( gravity ) ) { #ifdef UNREALARENA return stepped; // we got exactly where we wanted to go first try #else VectorCopy( start_o, down ); VectorMA( down, -STEPSIZE, normal, down ); pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, 0 ); //we can step down if ( trace.fraction > 0.01f && trace.fraction < 1.0f && !trace.allsolid && pml.groundPlane ) { if ( pm->debugLevel > 1 ) { Log::Notice( "%d: step down\n", c_pmove ); } stepped = true; } #endif } else { VectorCopy( start_o, down ); #ifdef UNREALARENA down[ 2 ] -= STEPSIZE; #else VectorMA( down, -STEPSIZE, normal, down ); #endif pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, 0 ); #ifdef UNREALARENA VectorSet( up, 0.0f, 0.0f, 1.0f ); #endif // never step up when you still have up velocity #ifdef UNREALARENA if ( pm->ps->velocity[ 2 ] > 0.0f && ( trace.fraction == 1.0f || DotProduct( trace.plane.normal, up ) < 0.7f ) ) #else if ( DotProduct( trace.plane.normal, pm->ps->velocity ) > 0.0f && ( trace.fraction == 1.0f || DotProduct( trace.plane.normal, normal ) < 0.7f ) ) #endif { return stepped; } #ifndef UNREALARENA // never step up when flying upwards with the jetpack if ( pm->ps->velocity[ 2 ] > 0.0f && ( pm->ps->stats[ STAT_STATE2 ] & SS2_JETPACK_ACTIVE ) ) { return stepped; } VectorCopy( pm->ps->origin, down_o ); VectorCopy( pm->ps->velocity, down_v ); #endif VectorCopy( start_o, up ); #ifdef UNREALARENA up[ 2 ] += STEPSIZE; #else VectorMA( up, STEPSIZE, normal, up ); #endif // test the player position if they were a stepheight higher pm->trace( &trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask, 0 ); if ( trace.allsolid ) { if ( pm->debugLevel > 1 ) { Log::Notice( "%i:bend can't step\n", c_pmove ); } return stepped; // can't step up } #ifdef UNREALARENA stepSize = trace.endpos[ 2 ] - start_o[ 2 ]; // if the new position is falling then do nothing if ( PM_CheckFallingFromLedge( trace.endpos ) ) { VectorCopy( start_o, pm->ps->origin ); return stepped; } #else VectorSubtract( trace.endpos, start_o, step_v ); VectorCopy( step_v, step_vNormal ); VectorNormalize( step_vNormal ); stepSize = DotProduct( normal, step_vNormal ) * VectorLength( step_v ); #endif // try slidemove from this position VectorCopy( trace.endpos, pm->ps->origin ); VectorCopy( start_v, pm->ps->velocity ); #ifdef UNREALARENA PM_SlideMove( gravity, stepSize ); #else if ( PM_SlideMove( gravity ) == 0 ) { if ( pm->debugLevel > 1 ) { Log::Notice( "%d: step up\n", c_pmove ); } stepped = true; } #endif // push down the final amount VectorCopy( pm->ps->origin, down ); #ifdef UNREALARENA down[ 2 ] -= stepSize; #else VectorMA( down, -stepSize, normal, down ); #endif pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, 0 ); if ( !trace.allsolid ) { #ifdef UNREALARENA // if the new position is falling then do nothing if ( PM_CheckFallingFromLedge( trace.endpos ) ) { VectorCopy( start_o, pm->ps->origin ); return stepped; } #endif VectorCopy( trace.endpos, pm->ps->origin ); } if ( trace.fraction < 1.0f ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity ); } } #ifdef UNREALARENA if ( !predictive ) { stepped = true; // use the step move float delta; delta = pm->ps->origin[ 2 ] - start_o[ 2 ]; if ( delta > 2.0f ) { if ( delta < 7.0f ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11.0f ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15.0f ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel > 1 ) { Log::Notice( "%i:stepped\n", c_pmove ); } } #else if ( !predictive && stepped ) { PM_StepEvent( start_o, pm->ps->origin, normal ); } #endif return stepped; }