//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CObjectSentrygun::SetModel( const char *pModel ) { float flPoseParam0 = 0.0; float flPoseParam1 = 0.0; // Save pose parameters across model change if ( m_iPitchPoseParameter >= 0 ) { flPoseParam0 = GetPoseParameter( m_iPitchPoseParameter ); } if ( m_iYawPoseParameter >= 0 ) { flPoseParam1 = GetPoseParameter( m_iYawPoseParameter ); } BaseClass::SetModel( pModel ); // Reset this after model change UTIL_SetSize( this, SENTRYGUN_MINS, SENTRYGUN_MAXS ); SetSolid( SOLID_BBOX ); // Restore pose parameters m_iPitchPoseParameter = LookupPoseParameter( "aim_pitch" ); m_iYawPoseParameter = LookupPoseParameter( "aim_yaw" ); SetPoseParameter( m_iPitchPoseParameter, flPoseParam0 ); SetPoseParameter( m_iYawPoseParameter, flPoseParam1 ); CreateBuildPoints(); ReattachChildren(); ResetSequenceInfo(); }
int CBaseTurret::MoveTurret(void) { bool bDidMove = false; int iPose; matrix3x4_t localToWorld; GetAttachment( LookupAttachment( "eyes" ), localToWorld ); Vector vecGoalDir; AngleVectors( m_vecGoalAngles, &vecGoalDir ); Vector vecGoalLocalDir; VectorIRotate( vecGoalDir, localToWorld, vecGoalLocalDir ); QAngle vecGoalLocalAngles; VectorAngles( vecGoalLocalDir, vecGoalLocalAngles ); float flDiff; QAngle vecNewAngles; // update pitch flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1 * m_iBaseTurnRate ) ); iPose = LookupPoseParameter( TURRET_BC_PITCH ); SetPoseParameter( iPose, GetPoseParameter( iPose ) + flDiff / 1.5 ); if (fabs(flDiff) > 0.1) { bDidMove = true; } // update yaw, with acceleration #if 0 float flDist = AngleNormalize( vecGoalLocalAngles.y ); float flNewDist; float flNewTurnRate; ChangeDistance( 0.1, flDist, 0.0, m_fTurnRate, m_iBaseTurnRate, m_iBaseTurnRate * 4, flNewDist, flNewTurnRate ); m_fTurnRate = flNewTurnRate; flDiff = flDist - flNewDist; #else flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1 * m_iBaseTurnRate ) ); #endif iPose = LookupPoseParameter( TURRET_BC_YAW ); SetPoseParameter( iPose, GetPoseParameter( iPose ) + flDiff / 1.5 ); if (fabs(flDiff) > 0.1) { bDidMove = true; } if (bDidMove) { // DevMsg( "(%.2f, %.2f)\n", AngleNormalize( vecGoalLocalAngles.x ), AngleNormalize( vecGoalLocalAngles.y ) ); } return bDidMove; }
//----------------------------------------------------------------------------- // Purpose: Causes the turret to face its desired angles //----------------------------------------------------------------------------- bool CNPC_CeilingTurret::UpdateFacing( void ) { bool bMoved = false; matrix3x4_t localToWorld; GetAttachment( LookupAttachment( "eyes" ), localToWorld ); Vector vecGoalDir; AngleVectors( m_vecGoalAngles, &vecGoalDir ); Vector vecGoalLocalDir; VectorIRotate( vecGoalDir, localToWorld, vecGoalLocalDir ); if ( g_debug_turret_ceiling.GetBool() ) { Vector vecMuzzle, vecMuzzleDir; QAngle vecMuzzleAng; GetAttachment( "eyes", vecMuzzle, vecMuzzleAng ); AngleVectors( vecMuzzleAng, &vecMuzzleDir ); NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 ); NDebugOverlay::Cross3D( vecMuzzle+(vecMuzzleDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 ); NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecMuzzleDir*256), 255, 255, 0, false, 0.05 ); NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 ); NDebugOverlay::Cross3D( vecMuzzle+(vecGoalDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 ); NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecGoalDir*256), 255, 0, 0, false, 0.05 ); } QAngle vecGoalLocalAngles; VectorAngles( vecGoalLocalDir, vecGoalLocalAngles ); // Update pitch float flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1f * MaxYawSpeed() ) ); SetPoseParameter( m_poseAim_Pitch, GetPoseParameter( m_poseAim_Pitch ) + ( flDiff / 1.5f ) ); if ( fabs( flDiff ) > 0.1f ) { bMoved = true; } // Update yaw flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed() ) ); SetPoseParameter( m_poseAim_Yaw, GetPoseParameter( m_poseAim_Yaw ) + ( flDiff / 1.5f ) ); if ( fabs( flDiff ) > 0.1f ) { bMoved = true; } InvalidateBoneCache(); return bMoved; }
bool CNPC_Infected::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval ) { // required movement direction float flMoveYaw = UTIL_VecToYaw( move.dir ); // FIXME: move this up to navigator so that path goals can ignore these overrides. Vector dir; float flInfluence = GetFacingDirection( dir ); dir = move.facing * (1 - flInfluence) + dir * flInfluence; VectorNormalize( dir ); // ideal facing direction float idealYaw = UTIL_AngleMod( UTIL_VecToYaw( dir ) ); // FIXME: facing has important max velocity issues GetMotor()->SetIdealYawAndUpdate( idealYaw ); // find movement direction to compensate for not being turned far enough float flDiff = UTIL_AngleDiff( flMoveYaw, GetLocalAngles().y ); // Setup the 9-way blend parameters based on our speed and direction. Vector2D vCurMovePose( 0, 0 ); vCurMovePose.x = cos( DEG2RAD( flDiff ) ) * 1.0f; //flPlaybackRate; vCurMovePose.y = -sin( DEG2RAD( flDiff ) ) * 1.0f; //flPlaybackRate; SetPoseParameter( gm_nMoveXPoseParam, vCurMovePose.x ); SetPoseParameter( gm_nMoveYPoseParam, vCurMovePose.y ); // ==== Update Lean pose parameters if ( gm_nLeanYawPoseParam >= 0 ) { float targetLean = GetPoseParameter( gm_nMoveYPoseParam ) * 30.0f; float curLean = GetPoseParameter( gm_nLeanYawPoseParam ); if( curLean < targetLean ) curLean += MIN(fabs(targetLean-curLean), GetAnimTimeInterval()*12.0f); //was 15.0f else curLean -= MIN(fabs(targetLean-curLean), GetAnimTimeInterval()*12.0f); //was 15.0f SetPoseParameter( gm_nLeanYawPoseParam, curLean ); } if( gm_nLeanPitchPoseParam >= 0 ) { float targetLean = GetPoseParameter( gm_nMoveXPoseParam ) * -20.0f; //was -30.0f float curLean = GetPoseParameter( gm_nLeanPitchPoseParam ); if( curLean < targetLean ) curLean += MIN(fabs(targetLean-curLean), GetAnimTimeInterval()*10.0f); //was 15.0f else curLean -= MIN(fabs(targetLean-curLean), GetAnimTimeInterval()*10.0f); //was 15.0f SetPoseParameter( gm_nLeanPitchPoseParam, curLean ); } return true; }
//----------------------------------------------------------------------------- // Purpose: Causes the camera to face its desired angles //----------------------------------------------------------------------------- bool CNPC_CombineCamera::UpdateFacing() { bool bMoved = false; matrix3x4_t localToWorld; GetAttachment(LookupAttachment("eyes"), localToWorld); Vector vecGoalDir; AngleVectors(m_vecGoalAngles, &vecGoalDir ); Vector vecGoalLocalDir; VectorIRotate(vecGoalDir, localToWorld, vecGoalLocalDir); QAngle vecGoalLocalAngles; VectorAngles(vecGoalLocalDir, vecGoalLocalAngles); // Update pitch float flDiff = AngleNormalize(UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1f * MaxYawSpeed())); int iPose = LookupPoseParameter(COMBINE_CAMERA_BC_PITCH); SetPoseParameter(iPose, GetPoseParameter(iPose) + (flDiff / 1.5f)); if (fabs(flDiff) > 0.1f) { bMoved = true; } // Update yaw flDiff = AngleNormalize(UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed())); iPose = LookupPoseParameter(COMBINE_CAMERA_BC_YAW); SetPoseParameter(iPose, GetPoseParameter(iPose) + (flDiff / 1.5f)); if (fabs(flDiff) > 0.1f) { bMoved = true; } if (bMoved && (m_flMoveSoundTime < gpGlobals->curtime)) { EmitSound("NPC_CombineCamera.Move"); m_flMoveSoundTime = gpGlobals->curtime + CAMERA_MOVE_INTERVAL; } // You're going to make decisions based on this info. So bump the bone cache after you calculate everything InvalidateBoneCache(); return bMoved; }
//----------------------------------------------------------------------------- // Primary gun //----------------------------------------------------------------------------- void CPropAPC::AimPrimaryWeapon( const Vector &vecWorldTarget ) { EntityMatrix parentMatrix; parentMatrix.InitFromEntity( this, m_nMachineGunBaseAttachment ); Vector target = parentMatrix.WorldToLocal( vecWorldTarget ); float quadTarget = target.LengthSqr(); float quadTargetXY = target.x*target.x + target.y*target.y; // Target is too close! Can't aim at it if ( quadTarget > m_vecBarrelPos.LengthSqr() ) { // We're trying to aim the offset barrel at an arbitrary point. // To calculate this, I think of the target as being on a sphere with // it's center at the origin of the gun. // The rotation we need is the opposite of the rotation that moves the target // along the surface of that sphere to intersect with the gun's shooting direction // To calculate that rotation, we simply calculate the intersection of the ray // coming out of the barrel with the target sphere (that's the new target position) // and use atan2() to get angles // angles from target pos to center float targetToCenterYaw = atan2( target.y, target.x ); float centerToGunYaw = atan2( m_vecBarrelPos.y, sqrt( quadTarget - (m_vecBarrelPos.y*m_vecBarrelPos.y) ) ); float targetToCenterPitch = atan2( target.z, sqrt( quadTargetXY ) ); float centerToGunPitch = atan2( -m_vecBarrelPos.z, sqrt( quadTarget - (m_vecBarrelPos.z*m_vecBarrelPos.z) ) ); QAngle angles; angles.Init( -RAD2DEG(targetToCenterPitch+centerToGunPitch), RAD2DEG( targetToCenterYaw + centerToGunYaw ), 0 ); SetPoseParameter( "vehicle_weapon_yaw", angles.y ); SetPoseParameter( "vehicle_weapon_pitch", angles.x ); StudioFrameAdvance(); float curPitch = GetPoseParameter( "vehicle_weapon_pitch" ); float curYaw = GetPoseParameter( "vehicle_weapon_yaw" ); m_bInFiringCone = (fabs(curPitch - angles.x) < 1e-3) && (fabs(curYaw - angles.y) < 1e-3); } else { m_bInFiringCone = false; } }
void CPropVehicleManhack::UpdateHead( void ) { float yaw = GetPoseParameter( "head_yaw" ); float pitch = GetPoseParameter( "head_pitch" ); // If we should be watching our enemy, turn our head CNPC_Manhack *pManhack=GetManhack(); if (pManhack != NULL) { Vector vehicleEyeOrigin; QAngle vehicleEyeAngles; GetAttachment( "vehicle_driver_eyes", vehicleEyeOrigin, vehicleEyeAngles ); // FIXME: cache this Vector vBodyDir; AngleVectors( vehicleEyeAngles, &vBodyDir ); Vector manhackDir = pManhack->GetAbsOrigin() - vehicleEyeOrigin; VectorNormalize( manhackDir ); float angle = UTIL_VecToYaw( vBodyDir ); float angleDiff = UTIL_VecToYaw( manhackDir ); angleDiff = UTIL_AngleDiff( angleDiff, angle + yaw ); SetPoseParameter( "head_yaw", UTIL_Approach( yaw + angleDiff, yaw, 1 ) ); angle = UTIL_VecToPitch( vBodyDir ); angleDiff = UTIL_VecToPitch( manhackDir ); angleDiff = UTIL_AngleDiff( angleDiff, angle + pitch ); SetPoseParameter( "head_pitch", UTIL_Approach( pitch + angleDiff, pitch, 1 ) ); } else { // Otherwise turn the head back to its normal position SetPoseParameter( "head_yaw", UTIL_Approach( 0, yaw, 10 ) ); SetPoseParameter( "head_pitch", UTIL_Approach( 0, pitch, 10 ) ); } }
float StudioModel::GetPoseParameter( char const *szName ) { return GetPoseParameter( LookupPoseParameter( szName ) ); }
//----------------------------------------------------------------------------- // Purpose: Render the weapon. Draw the Viewmodel if the weapon's being carried // by this player, otherwise draw the worldmodel. //----------------------------------------------------------------------------- int C_BaseViewModel::DrawModel( int flags, const RenderableInstance_t &instance ) { if ( !m_bReadyToDraw ) return 0; if ( flags & STUDIO_RENDER ) { // Determine blending amount and tell engine float blend = (float)( instance.m_nAlpha / 255.0f ); // Totally gone if ( blend <= 0.0f ) return 0; // Tell engine render->SetBlend( blend ); float color[3]; GetColorModulation( color ); render->SetColorModulation( color ); } CMatRenderContextPtr pRenderContext( materials ); if ( ShouldFlipViewModel() ) pRenderContext->CullMode( MATERIAL_CULLMODE_CW ); int ret = 0; C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); C_BaseCombatWeapon *pWeapon = GetOwningWeapon(); // If the local player's overriding the viewmodel rendering, let him do it if ( pPlayer && pPlayer->IsOverridingViewmodel() ) { ret = pPlayer->DrawOverriddenViewmodel( this, flags, instance ); } else if ( pWeapon && pWeapon->IsOverridingViewmodel() ) { ret = pWeapon->DrawOverriddenViewmodel( this, flags, instance ); } else { ret = BaseClass::DrawModel( flags, instance ); } pRenderContext->CullMode( MATERIAL_CULLMODE_CCW ); // Now that we've rendered, reset the animation restart flag if ( flags & STUDIO_RENDER ) { if ( m_nOldAnimationParity != m_nAnimationParity ) { m_nOldAnimationParity = m_nAnimationParity; } // Tell the weapon itself that we've rendered, in case it wants to do something if ( pWeapon ) { pWeapon->ViewModelDrawn( this ); } if ( vm_debug.GetBool() ) { MDLCACHE_CRITICAL_SECTION(); int line = 16; CStudioHdr *hdr = GetModelPtr(); engine->Con_NPrintf( line++, "%s: %s(%d), cycle: %.2f cyclerate: %.2f playbackrate: %.2f\n", (hdr)?hdr->pszName():"(null)", GetSequenceName( GetSequence() ), GetSequence(), GetCycle(), GetSequenceCycleRate( hdr, GetSequence() ), GetPlaybackRate() ); if ( hdr ) { for( int i=0; i < hdr->GetNumPoseParameters(); ++i ) { const mstudioposeparamdesc_t &Pose = hdr->pPoseParameter( i ); engine->Con_NPrintf( line++, "pose_param %s: %f", Pose.pszName(), GetPoseParameter( i ) ); } } // Determine blending amount and tell engine float blend = (float)( instance.m_nAlpha / 255.0f ); float color[3]; GetColorModulation( color ); engine->Con_NPrintf( line++, "blend=%f, color=%f,%f,%f", blend, color[0], color[1], color[2] ); engine->Con_NPrintf( line++, "GetRenderMode()=%d", GetRenderMode() ); engine->Con_NPrintf( line++, "m_nRenderFX=0x%8.8X", GetRenderFX() ); color24 c = GetRenderColor(); unsigned char a = GetRenderAlpha(); engine->Con_NPrintf( line++, "rendercolor=%d,%d,%d,%d", c.r, c.g, c.b, a ); engine->Con_NPrintf( line++, "origin=%f, %f, %f", GetRenderOrigin().x, GetRenderOrigin().y, GetRenderOrigin().z ); engine->Con_NPrintf( line++, "angles=%f, %f, %f", GetRenderAngles()[0], GetRenderAngles()[1], GetRenderAngles()[2] ); if ( IsEffectActive( EF_NODRAW ) ) { engine->Con_NPrintf( line++, "EF_NODRAW" ); } } } return ret; }
//----------------------------------------------------------------------------- // Purpose: Aim Gun at a target //----------------------------------------------------------------------------- void CASW_PropJeep::AimGunAt( Vector *endPos, float flInterval ) { Vector aimPos = *endPos; // See if the gun should be allowed to aim if ( IsOverturned() || m_bEngineLocked || m_bHasGun == false ) { SetPoseParameter( JEEP_GUN_YAW, 0 ); SetPoseParameter( JEEP_GUN_PITCH, 0 ); SetPoseParameter( JEEP_GUN_SPIN, 0 ); return; // Make the gun go limp and look "down" Vector v_forward, v_up; AngleVectors( GetLocalAngles(), NULL, &v_forward, &v_up ); aimPos = WorldSpaceCenter() + ( v_forward * -32.0f ) - Vector( 0, 0, 128.0f ); } matrix3x4_t gunMatrix; GetAttachment( LookupAttachment("gun_ref"), gunMatrix ); // transform the enemy into gun space Vector localEnemyPosition; VectorITransform( aimPos, gunMatrix, localEnemyPosition ); // do a look at in gun space (essentially a delta-lookat) QAngle localEnemyAngles; VectorAngles( localEnemyPosition, localEnemyAngles ); // convert to +/- 180 degrees localEnemyAngles.x = UTIL_AngleDiff( localEnemyAngles.x, 0 ); localEnemyAngles.y = UTIL_AngleDiff( localEnemyAngles.y, 0 ); float targetYaw = m_aimYaw + localEnemyAngles.y; float targetPitch = m_aimPitch + localEnemyAngles.x; // Constrain our angles float newTargetYaw = clamp( targetYaw, -CANNON_MAX_LEFT_YAW, CANNON_MAX_RIGHT_YAW ); float newTargetPitch = clamp( targetPitch, -CANNON_MAX_DOWN_PITCH, CANNON_MAX_UP_PITCH ); // If the angles have been clamped, we're looking outside of our valid range if ( fabs(newTargetYaw-targetYaw) > 1e-4 || fabs(newTargetPitch-targetPitch) > 1e-4 ) { m_bUnableToFire = true; } targetYaw = newTargetYaw; targetPitch = newTargetPitch; // Exponentially approach the target float yawSpeed = 8; float pitchSpeed = 8; m_aimYaw = UTIL_Approach( targetYaw, m_aimYaw, yawSpeed ); m_aimPitch = UTIL_Approach( targetPitch, m_aimPitch, pitchSpeed ); SetPoseParameter( JEEP_GUN_YAW, -m_aimYaw); SetPoseParameter( JEEP_GUN_PITCH, -m_aimPitch ); InvalidateBoneCache(); // read back to avoid drift when hitting limits // as long as the velocity is less than the delta between the limit and 180, this is fine. m_aimPitch = -GetPoseParameter( JEEP_GUN_PITCH ); m_aimYaw = -GetPoseParameter( JEEP_GUN_YAW ); // Now draw crosshair for actual aiming point Vector vecMuzzle, vecMuzzleDir; QAngle vecMuzzleAng; GetAttachment( "Muzzle", vecMuzzle, vecMuzzleAng ); AngleVectors( vecMuzzleAng, &vecMuzzleDir ); trace_t tr; UTIL_TraceLine( vecMuzzle, vecMuzzle + (vecMuzzleDir * MAX_TRACE_LENGTH), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); // see if we hit something, if so, adjust endPos to hit location if ( tr.fraction < 1.0 ) { m_vecGunCrosshair = vecMuzzle + ( vecMuzzleDir * MAX_TRACE_LENGTH * tr.fraction ); } }
//------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void CNPC_CombineDropship::Flight( void ) { // Only run pose params in some flight states bool bRunPoseParams = ( m_iLandState == LANDING_NO || m_iLandState == LANDING_LEVEL_OUT || m_iLandState == LANDING_LIFTOFF || m_iLandState == LANDING_SWOOPING ); if ( bRunPoseParams ) { if( GetFlags() & FL_ONGROUND ) { //This would be really bad. RemoveFlag( FL_ONGROUND ); } // NDebugOverlay::Line(GetLocalOrigin(), GetDesiredPosition(), 0,0,255, true, 0.1); Vector deltaPos = GetDesiredPosition() - GetLocalOrigin(); // calc desired acceleration float dt = 1.0f; Vector accel; float accelRate = DROPSHIP_ACCEL_RATE; float maxSpeed = m_flMaxSpeed; if ( m_lifeState == LIFE_DYING ) { accelRate *= 5.0; maxSpeed *= 5.0; } float flDist = min( GetAbsVelocity().Length() + accelRate, maxSpeed ); // Only decelerate to our goal if we're going to hit it if ( deltaPos.Length() > flDist * dt ) { float scale = flDist * dt / deltaPos.Length(); deltaPos = deltaPos * scale; } // If we're swooping, floor it if ( m_iLandState == LANDING_SWOOPING ) { VectorNormalize( deltaPos ); deltaPos *= maxSpeed; } // calc goal linear accel to hit deltaPos in dt time. accel.x = 2.0 * (deltaPos.x - GetAbsVelocity().x * dt) / (dt * dt); accel.y = 2.0 * (deltaPos.y - GetAbsVelocity().y * dt) / (dt * dt); accel.z = 2.0 * (deltaPos.z - GetAbsVelocity().z * dt + 0.5 * 384 * dt * dt) / (dt * dt); //NDebugOverlay::Line(GetLocalOrigin(), GetLocalOrigin() + deltaPos, 255,0,0, true, 0.1); //NDebugOverlay::Line(GetLocalOrigin(), GetLocalOrigin() + accel, 0,255,0, true, 0.1); // don't fall faster than 0.2G or climb faster than 2G if ( m_iLandState != LANDING_SWOOPING ) { accel.z = clamp( accel.z, 384 * 0.2, 384 * 2.0 ); } Vector forward, right, up; GetVectors( &forward, &right, &up ); Vector goalUp = accel; VectorNormalize( goalUp ); // calc goal orientation to hit linear accel forces float goalPitch = RAD2DEG( asin( DotProduct( forward, goalUp ) ) ); float goalYaw = UTIL_VecToYaw( m_vecDesiredFaceDir ); float goalRoll = RAD2DEG( asin( DotProduct( right, goalUp ) ) ); // clamp goal orientations goalPitch = clamp( goalPitch, -45, 60 ); goalRoll = clamp( goalRoll, -45, 45 ); // calc angular accel needed to hit goal pitch in dt time. dt = 0.6; QAngle goalAngAccel; goalAngAccel.x = 2.0 * (AngleDiff( goalPitch, AngleNormalize( GetLocalAngles().x ) ) - GetLocalAngularVelocity().x * dt) / (dt * dt); goalAngAccel.y = 2.0 * (AngleDiff( goalYaw, AngleNormalize( GetLocalAngles().y ) ) - GetLocalAngularVelocity().y * dt) / (dt * dt); goalAngAccel.z = 2.0 * (AngleDiff( goalRoll, AngleNormalize( GetLocalAngles().z ) ) - GetLocalAngularVelocity().z * dt) / (dt * dt); goalAngAccel.x = clamp( goalAngAccel.x, -300, 300 ); //goalAngAccel.y = clamp( goalAngAccel.y, -60, 60 ); goalAngAccel.y = clamp( goalAngAccel.y, -120, 120 ); goalAngAccel.z = clamp( goalAngAccel.z, -300, 300 ); // limit angular accel changes to simulate mechanical response times dt = 0.1; QAngle angAccelAccel; angAccelAccel.x = (goalAngAccel.x - m_vecAngAcceleration.x) / dt; angAccelAccel.y = (goalAngAccel.y - m_vecAngAcceleration.y) / dt; angAccelAccel.z = (goalAngAccel.z - m_vecAngAcceleration.z) / dt; angAccelAccel.x = clamp( angAccelAccel.x, -1000, 1000 ); angAccelAccel.y = clamp( angAccelAccel.y, -1000, 1000 ); angAccelAccel.z = clamp( angAccelAccel.z, -1000, 1000 ); m_vecAngAcceleration += angAccelAccel * 0.1; // Msg( "pitch %6.1f (%6.1f:%6.1f) ", goalPitch, GetLocalAngles().x, m_vecAngVelocity.x ); // Msg( "roll %6.1f (%6.1f:%6.1f) : ", goalRoll, GetLocalAngles().z, m_vecAngVelocity.z ); // Msg( "%6.1f %6.1f %6.1f : ", goalAngAccel.x, goalAngAccel.y, goalAngAccel.z ); // Msg( "%6.0f %6.0f %6.0f\n", angAccelAccel.x, angAccelAccel.y, angAccelAccel.z ); ApplySidewaysDrag( right ); ApplyGeneralDrag(); QAngle angVel = GetLocalAngularVelocity(); angVel += m_vecAngAcceleration * 0.1; //angVel.y = clamp( angVel.y, -60, 60 ); //angVel.y = clamp( angVel.y, -120, 120 ); angVel.y = clamp( angVel.y, -120, 120 ); SetLocalAngularVelocity( angVel ); m_flForce = m_flForce * 0.8 + (accel.z + fabs( accel.x ) * 0.1 + fabs( accel.y ) * 0.1) * 0.1 * 0.2; Vector vecImpulse = m_flForce * up; if ( m_lifeState == LIFE_DYING ) { vecImpulse.z = -38.4; // 64ft/sec } else { vecImpulse.z -= 38.4; // 32ft/sec } // Find our acceleration direction Vector vecAccelDir = vecImpulse; VectorNormalize( vecAccelDir ); // Find our current velocity Vector vecVelDir = GetAbsVelocity(); VectorNormalize( vecVelDir ); // Level out our plane of movement vecAccelDir.z = 0.0f; vecVelDir.z = 0.0f; forward.z = 0.0f; right.z = 0.0f; // Find out how "fast" we're moving in relation to facing and acceleration float speed = m_flForce * DotProduct( vecVelDir, vecAccelDir );// * DotProduct( forward, vecVelDir ); // Use the correct pose params char *sBodyAccel; char *sBodySway; if ( m_hContainer || m_iLandState == LANDING_SWOOPING ) { sBodyAccel = "cargo_body_accel"; sBodySway = "cargo_body_sway"; SetPoseParameter( "body_accel", 0 ); SetPoseParameter( "body_sway", 0 ); } else { sBodyAccel = "body_accel"; sBodySway = "body_sway"; SetPoseParameter( "cargo_body_accel", 0 ); SetPoseParameter( "cargo_body_sway", 0 ); } // Apply the acceleration blend to the fins float finAccelBlend = SimpleSplineRemapVal( speed, -60, 60, -1, 1 ); float curFinAccel = GetPoseParameter( sBodyAccel ); curFinAccel = UTIL_Approach( finAccelBlend, curFinAccel, 0.5f ); SetPoseParameter( sBodyAccel, curFinAccel ); speed = m_flForce * DotProduct( vecVelDir, right ); // Apply the spin sway to the fins float finSwayBlend = SimpleSplineRemapVal( speed, -60, 60, -1, 1 ); float curFinSway = GetPoseParameter( sBodySway ); curFinSway = UTIL_Approach( finSwayBlend, curFinSway, 0.5f ); SetPoseParameter( sBodySway, curFinSway ); // Add in our velocity pulse for this frame ApplyAbsVelocityImpulse( vecImpulse ); //Msg("FinAccel: %f, Finsway: %f\n", curFinAccel, curFinSway ); } else { SetPoseParameter( "body_accel", 0 ); SetPoseParameter( "body_sway", 0 ); SetPoseParameter( "cargo_body_accel", 0 ); SetPoseParameter( "cargo_body_sway", 0 ); } }
//----------------------------------------------------------------------------- // Frame-based updating //----------------------------------------------------------------------------- bool CFourWheelVehiclePhysics::Think() { if (!m_pVehicle) return false; // Update sound + physics state const vehicle_operatingparams_t &carState = m_pVehicle->GetOperatingParams(); const vehicleparams_t &vehicleData = m_pVehicle->GetVehicleParams(); // Set save data. float carSpeed = fabs( INS2MPH( carState.speed ) ); m_nLastSpeed = m_nSpeed; m_nSpeed = ( int )carSpeed; m_nRPM = ( int )carState.engineRPM; m_nHasBoost = static_cast<int>(vehicleData.engine.boostDelay); // if we have any boost delay, vehicle has boost ability m_pVehicle->Update( gpGlobals->frametime, m_controls); // boost sounds if( IsBoosting() && !m_bLastBoost ) { m_bLastBoost = true; m_turboTimer = gpGlobals->curtime + 2.75f; // min duration for turbo sound } else if( !IsBoosting() && m_bLastBoost ) { if ( gpGlobals->curtime >= m_turboTimer ) { m_bLastBoost = false; } } m_fLastBoost = carState.boostDelay; m_nBoostTimeLeft = carState.boostTimeLeft; // UNDONE: Use skid info from the physics system? // Only check wheels if we're not being carried by a dropship if ( m_pOuter->VPhysicsGetObject() && !m_pOuter->VPhysicsGetObject()->GetShadowController() ) { // check for skidding, if we're skidding, need to play the sound if ( carState.skidding && m_bIsOn ) { if ( !m_bLastSkid ) // only play sound once { m_bLastSkid = true; CPASAttenuationFilter filter( m_pOuter ); m_pOuterServerVehicle->PlaySound( VS_SKID_FRICTION_NORMAL ); } // kick up dust from the wheels while skidding for ( int i = 0; i < 4; i++ ) { PlaceWheelDust( i, true ); } } else if ( m_bLastSkid == true ) { m_bLastSkid = false; m_pOuterServerVehicle->StopSound( VS_SKID_FRICTION_NORMAL ); } // toss dust up from the wheels of the vehicle if we're moving fast enough if ( m_nSpeed >= DUST_SPEED && vehicleData.steering.dustCloud && m_bIsOn ) { for ( int i = 0; i < 4; i++ ) { PlaceWheelDust( i ); } } } // Make the steering wheel match the input, with a little dampening. #define STEER_DAMPING 0.8 float flSteer = GetPoseParameter( m_poseParameters[VEH_STEER] ); SetPoseParameter( m_poseParameters[VEH_STEER], (STEER_DAMPING * flSteer) + ((1 - STEER_DAMPING) * m_controls.steering) ); m_actionValue += m_actionSpeed * m_actionScale * gpGlobals->frametime; SetPoseParameter( m_poseParameters[VEH_ACTION], m_actionValue ); // setup speedometer if ( m_bIsOn == true ) { float displaySpeed = m_nSpeed / MAX_GUAGE_SPEED; SetPoseParameter( m_poseParameters[VEH_SPEEDO], displaySpeed ); } return m_bIsOn; }