void CFlex::DoBodyLean( void ) { CAI_NPC *myNpc = MyNPCPointer( ); if (myNpc) { Vector vecDelta; Vector vecPos; Vector vecOrigin = GetAbsOrigin(); if (m_vecPrevOrigin == vec3_origin) { m_vecPrevOrigin = vecOrigin; } vecDelta = vecOrigin - m_vecPrevOrigin; vecDelta.x = clamp( vecDelta.x, -50, 50 ); vecDelta.y = clamp( vecDelta.y, -50, 50 ); vecDelta.z = clamp( vecDelta.z, -50, 50 ); float dt = gpGlobals->curtime - GetLastThink(); bool bSkip = ((GetFlags() & (FL_FLY | FL_SWIM)) != 0) || (GetMoveParent() != NULL) || (GetGroundEntity() == NULL) || (GetGroundEntity()->IsMoving()); bSkip |= myNpc->TaskRanAutomovement() || (myNpc->GetVehicleEntity() != NULL); if (!bSkip) { if (vecDelta.LengthSqr() > m_vecPrevVelocity.LengthSqr()) { float decay = ExponentialDecay( 0.6, 0.1, dt ); m_vecPrevVelocity = m_vecPrevVelocity * (decay) + vecDelta * (1.f - decay); } else { float decay = ExponentialDecay( 0.4, 0.1, dt ); m_vecPrevVelocity = m_vecPrevVelocity * (decay) + vecDelta * (1.f - decay); } vecPos = m_vecPrevOrigin + m_vecPrevVelocity; float decay = ExponentialDecay( 0.5, 0.1, dt ); m_vecShift = m_vecShift * (decay) + (vecOrigin - vecPos) * (1.f - decay); // FIXME: Scale this m_vecLean = (vecOrigin - vecPos) * 1.0; // FIXME: Scale this } else { m_vecPrevVelocity = vecDelta; float decay = ExponentialDecay( 0.5, 0.1, dt ); m_vecShift = m_vecLean * decay; m_vecLean = m_vecShift * decay; } m_vecPrevOrigin = vecOrigin; /* DevMsg( "%.2f %.2f %.2f (%.2f %.2f %.2f)\n", m_vecLean.Get().x, m_vecLean.Get().y, m_vecLean.Get().z, vecDelta.x, vecDelta.y, vecDelta.z ); */ } }
//----------------------------------------------------------------------------- // Purpose: Turns a npc towards its ideal yaw. // Input : yawSpeed - Yaw speed in degrees per 1/10th of a second. // flInterval - Time interval to turn, -1 uses time since last think. // Output : Returns the number of degrees turned. //----------------------------------------------------------------------------- void CAI_Motor::UpdateYaw( int yawSpeed ) { // Don't do this if our yaw is locked if ( IsYawLocked() ) return; GetOuter()->SetUpdatedYaw(); float ideal, current, newYaw; if ( yawSpeed == -1 ) yawSpeed = GetYawSpeed(); // NOTE: GetIdealYaw() will never exactly be reached because UTIL_AngleMod // also truncates the angle to 16 bits of resolution. So lets truncate it here. current = UTIL_AngleMod( GetLocalAngles().y ); ideal = UTIL_AngleMod( GetIdealYaw() ); // FIXME: this needs a proper interval float dt = MIN( 0.2, gpGlobals->curtime - GetLastThink() ); newYaw = AI_ClampYaw( (float)yawSpeed * 10.0, current, ideal, dt ); if (newYaw != current) { QAngle angles = GetLocalAngles(); angles.y = newYaw; SetLocalAngles( angles ); } }
void CWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { // Make sure that we have a caller if (!pActivator) return; // if it's not a player, ignore if ( !pActivator->IsPlayer() ) return; // if there is no juice left, turn it off if (m_iJuice <= 0) { SetFrame( 1 ); Off(); } // if the player doesn't have the suit, or there is no juice left, make the deny noise if( ( m_iJuice <= 0 ) || ( !( pActivator->GetWeapons().Any( 1 << WEAPON_SUIT ) ) ) ) { if (m_flSoundTime <= gpGlobals->time) { m_flSoundTime = gpGlobals->time + 0.62; EMIT_SOUND( this, CHAN_ITEM, "items/medshotno1.wav", 1.0, ATTN_NORM ); } return; } SetNextThink( GetLastThink() + 0.25 ); SetThink(&CWallHealth::Off); // Time to recharge yet? if (m_flNextCharge >= gpGlobals->time) return; // Play the on sound or the looping charging sound if (!m_iOn) { m_iOn++; EMIT_SOUND( this, CHAN_ITEM, "items/medshot4.wav", 1.0, ATTN_NORM ); m_flSoundTime = 0.56 + gpGlobals->time; } if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->time)) { m_iOn++; EMIT_SOUND( this, CHAN_STATIC, "items/medcharge4.wav", 1.0, ATTN_NORM ); } // charge the player if ( pActivator->GiveHealth( 1, DMG_GENERIC ) ) { m_iJuice--; } // govern the rate of charge m_flNextCharge = gpGlobals->time + 0.1; }
//----------------------------------------------------------------------------- // Purpose: Returns the magnitude of the entity's angular velocity. //----------------------------------------------------------------------------- float CPointAngularVelocitySensor::SampleAngularVelocity(CBaseEntity *pEntity) { if (pEntity->GetMoveType() == MOVETYPE_VPHYSICS) { IPhysicsObject *pPhys = pEntity->VPhysicsGetObject(); if (pPhys != NULL) { Vector vecVelocity; AngularImpulse vecAngVelocity; pPhys->GetVelocity(&vecVelocity, &vecAngVelocity); QAngle angles; pPhys->GetPosition( NULL, &angles ); float dt = gpGlobals->curtime - GetLastThink(); if ( dt == 0 ) dt = 0.1; // HACKHACK: We don't expect a real 'delta' orientation here, just enough of an error estimate to tell if this thing // is trying to move, but failing. QAngle delta = angles - m_lastOrientation; if ( ( delta.Length() / dt ) < ( vecAngVelocity.Length() * 0.01 ) ) { return 0.0f; } m_lastOrientation = angles; if ( m_bUseHelper == false ) { return vecAngVelocity.Length(); } else { Vector vLine = m_vecAxis - GetAbsOrigin(); VectorNormalize( vLine ); Vector vecWorldAngVelocity; pPhys->LocalToWorldVector( &vecWorldAngVelocity, vecAngVelocity ); float flDot = DotProduct( vecWorldAngVelocity, vLine ); return flDot; } } } else { QAngle vecAngVel = pEntity->GetLocalAngularVelocity(); float flMax = MAX(fabs(vecAngVel[PITCH]), fabs(vecAngVel[YAW])); return MAX(flMax, fabs(vecAngVel[ROLL])); } return 0; }
void CWallHealth::Off(void) { // Stop looping sound. if (m_iOn > 1) STOP_SOUND( this, CHAN_STATIC, "items/medcharge4.wav" ); m_iOn = 0; if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHealthChargerRechargeTime() ) > 0) ) { SetNextThink( GetLastThink() + m_iReactivate ); SetThink(&CWallHealth::Recharge); } else SetThink( &CWallHealth::SUB_DoNothing ); }
void CAI_Motor::UpdateYaw( int yawSpeed ) { float ideal, current, newYaw; if ( yawSpeed = -1 ) yawSpeed = GetYawSpeed(); // NOTE: GetIdealYaw() will never exactly be reached because UTIL_AngleMod // also truncates the angle to 16 bits of resolution. So lets truncate it here. current = UTIL_AngleMod( GetLocalAngles().y ); ideal = UTIL_AngleMod( GetIdealYaw() ); newYaw = AI_ClampYaw( (float)yawSpeed * 10.0, current, ideal, gpGlobals->curtime - GetLastThink() ); if (newYaw != current) { QAngle angles = GetLocalAngles(); angles.y = newYaw; SetLocalAngles( angles ); // ENTITY MUST BE RELINKED to recompute absangles engine->RelinkEntity( GetEdict(), false ); } }
// // The door has reached the "up" position. Either go back down, or wait for another activation. // void CBaseDoor::DoorHitTop( void ) { if( !GetSpawnFlags().Any( SF_DOOR_SILENT ) ) { STOP_SOUND( this, CHAN_STATIC, ( char* ) STRING( pev->noiseMoving ) ); EMIT_SOUND( this, CHAN_STATIC, ( char* ) STRING( pev->noiseArrived ), 1, ATTN_NORM ); } ASSERT( m_toggle_state == TS_GOING_UP ); m_toggle_state = TS_AT_TOP; // toggle-doors don't come down automatically, they wait for refire. if( GetSpawnFlags().Any( SF_DOOR_NO_AUTO_RETURN ) ) { // Re-instate touch method, movement is complete if( !GetSpawnFlags().Any( SF_DOOR_USE_ONLY ) ) SetTouch( &CBaseDoor::DoorTouch ); } else { // In flWait seconds, DoorGoDown will fire, unless wait is -1, then door stays open SetNextThink( GetLastThink() + m_flWait ); SetThink( &CBaseDoor::DoorGoDown ); if( m_flWait == -1 ) { SetNextThink( -1 ); } } // Fire the close target (if startopen is set, then "top" is closed) - netname is the close target if( HasNetName() && GetSpawnFlags().Any( SF_DOOR_START_OPEN ) ) FireTargets( GetNetName(), m_hActivator, this, USE_TOGGLE, 0 ); SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); // this isn't finished }
//----------------------------------------------------------------------------- // Run all of the AI for elements within the range iStart to iEnd //----------------------------------------------------------------------------- void CNPC_Blob::DoBlobBatchedAI( int iStart, int iEnd ) { float flInterval = gpGlobals->curtime - GetLastThink(); // Local fields for sin-wave movement variance float flMySine; float flAmplitude = npc_blob_sin_amplitude.GetFloat(); float flMyAmplitude; Vector vecRight; Vector vecForward; // Local fields for attract/repel float minDistSqr = Square( m_flMinElementDist ); float flBlobSpeed = blob_element_speed.GetFloat(); float flSpeed; // Local fields for speed limiting float flMinSpeed = blob_element_speed.GetFloat() * 0.5f; float flMaxSpeed = blob_element_speed.GetFloat() * 1.5f; bool bEnforceSpeedLimit; bool bEnforceRelativePositions; bool bDoMovementVariation; bool bDoOrientation = npc_blob_use_orientation.GetBool(); float flIdleSpeedFactor = npc_blob_idle_speed_factor.GetFloat(); // Group cohesion float flBlobRadiusSqr = Square( blob_radius.GetFloat() + 48.0f ); // Four feet of fudge // Build a right-hand vector along which we'll add some sine wave data to give each // element a unique insect-like undulation along an axis perpendicular to their path, // which makes the entire group look far less orderly if( GetEnemy() != NULL ) { // If I have an enemy, the right-hand vector is perpendicular to a straight line // from the group's centroid to the enemy's origin. vecForward = GetEnemy()->GetAbsOrigin() - m_vecCentroid; VectorNormalize( vecForward ); vecRight.x = vecForward.y; vecRight.y = -vecForward.x; } else { // If there is no enemy, wobble along the axis from the centroid to me. vecForward = GetAbsOrigin() - m_vecCentroid; VectorNormalize( vecForward ); vecRight.x = vecForward.y; vecRight.y = -vecForward.x; } //-- // MAIN LOOP - Run all of the elements in the set iStart to iEnd //-- for( int i = iStart ; i < iEnd ; i++ ) { CBlobElement *pThisElement = m_Elements[ i ]; //-- // Initial movement //-- // Start out with bEnforceSpeedLimit set to false. This is because an element // can't overspeed if it's moving undisturbed towards its target entity or // target location. An element can only under or overspeed when it is repelled // by multiple other elements in the group. See "Relative Positions" below. // // Initialize some 'defaults' that may be changed for each iteration of this loop bEnforceSpeedLimit = false; bEnforceRelativePositions = true; bDoMovementVariation = true; flSpeed = flBlobSpeed; switch( pThisElement->GetActiveMovementRule() ) { case BLOB_MOVE_DONT_MOVE: { pThisElement->SetElementVelocity( vec3_origin, true ); trace_t tr; Vector vecOrigin = pThisElement->GetAbsOrigin(); UTIL_TraceLine( vecOrigin, vecOrigin - Vector( 0, 0, 16), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if( tr.fraction < 1.0f ) { QAngle angles; VectorAngles( tr.plane.normal, angles ); float flSwap = angles.x; angles.x = -angles.y; angles.y = flSwap; pThisElement->SetAbsAngles( angles ); } } continue; break; case BLOB_MOVE_TO_TARGET_LOCATION: { Vector vecDiff = pThisElement->GetAbsOrigin() - pThisElement->m_vecTargetLocation; if( vecDiff.Length2DSqr() <= Square(80.0f) ) { // Don't shove this guy around any more, let him get to his goal position. flSpeed *= 0.5f; bEnforceRelativePositions = false; bDoMovementVariation = false; } pThisElement->MoveTowardsTargetLocation( flSpeed ); } break; case BLOB_MOVE_TO_TARGET_ENTITY: { if( !IsMoving() && GetEnemy() == NULL ) { if( pThisElement->GetAbsOrigin().DistToSqr( GetAbsOrigin() ) <= flBlobRadiusSqr ) { flSpeed = (flSpeed * flIdleSpeedFactor) * pThisElement->m_flRandomEightyPercent; } } pThisElement->MoveTowardsTargetEntity( flSpeed ); } break; default: Msg("ERROR: Blob Element with unspecified Movement Rule\n"); break; } //--- // Relative positions //-- // Check this element against ALL other elements. If the two elements are closer // than the allowed minimum distance, repel this element away. (The other element // will repel when its AI runs). A single element can be repelled by many other // elements. This is why bEnforceSpeedLimit is set to true if any of the repelling // code runs for this element. Multiple attempts to repel an element in the same // direction will cause overspeed. Conflicting attempts to repel an element in opposite // directions will cause underspeed. Vector vecDir = Vector( 0, 0, 0 ); Vector vecThisElementOrigin = pThisElement->GetAbsOrigin(); if( bEnforceRelativePositions ) { for( int j = 0 ; j < m_Elements.Count() ; j++ ) { // This is the innermost loop! We should optimize here, if anywhere. // If this element is on the wall, then don't be repelled by anyone. Repelling // elements that are trying to climb a wall usually make them look like they // fall off the wall a few times while climbing. if( pThisElement->m_bOnWall ) continue; CBlobElement *pThatElement = m_Elements[ j ]; if( i != j ) { Vector vecThatElementOrigin = pThatElement->GetAbsOrigin(); float distSqr = vecThisElementOrigin.DistToSqr( vecThatElementOrigin ); if( distSqr < minDistSqr ) { // Too close to the other element. Move away. float flRepelSpeed; Vector vecRepelDir = ( vecThisElementOrigin - vecThatElementOrigin ); vecRepelDir.NormalizeInPlace(); flRepelSpeed = (flSpeed * ( 1.0f - ( distSqr / minDistSqr ) ) ) * pThatElement->GetSinePhase(); pThisElement->AddElementVelocity( vecRepelDir * flRepelSpeed, true ); // Since we altered this element's velocity after it was initially set, there's a chance // that the sums of multiple vectors will cause the element to over or underspeed, so // mark it for speed limit enforcement bEnforceSpeedLimit = true; } } } } //-- // Movement variation //-- if( bDoMovementVariation ) { flMySine = sin( gpGlobals->curtime * pThisElement->GetSineFrequency() ); flMyAmplitude = flAmplitude * pThisElement->GetSineAmplitude(); pThisElement->AddElementVelocity( vecRight * (flMySine * flMyAmplitude), true ); } // Avoidance for( int a = 0 ; a < m_iNumAvoidOrigins ; a++ ) { Vector vecAvoidDir = pThisElement->GetAbsOrigin() - m_vecAvoidOrigin[ a ]; if( vecAvoidDir.LengthSqr() <= (m_flAvoidRadiusSqr * pThisElement->m_flRandomEightyPercent) ) { VectorNormalize( vecAvoidDir ); pThisElement->AddElementVelocity( vecAvoidDir * (flSpeed * 2.0f), true ); break; } } //-- // Speed limits //--- if( bEnforceSpeedLimit == true ) { pThisElement->EnforceSpeedLimits( flMinSpeed, flMaxSpeed ); } //-- // Wall crawling //-- pThisElement->ModifyVelocityForSurface( flInterval, flSpeed ); // For identifying stuck elements. pThisElement->m_vecPrevOrigin = pThisElement->GetAbsOrigin(); pThisElement->m_flDistFromCentroidSqr = pThisElement->m_vecPrevOrigin.DistToSqr( m_vecCentroid ); // Orientation if( bDoOrientation ) { QAngle angles; VectorAngles( pThisElement->GetAbsVelocity(), angles ); pThisElement->SetAbsAngles( angles ); } /* //-- // Stragglers/Group integrity // if( pThisElement->m_flDistFromCentroidSqr > flStragglerDistSqr ) { NDebugOverlay::Line( pThisElement->GetAbsOrigin(), m_vecCentroid, 255, 0, 0, false, 0.025f ); } */ } }
void CASW_Rocket::SeekThink( void ) { // If we have a grace period, go solid when it ends if ( m_flGracePeriodEndsAt ) { if ( m_flGracePeriodEndsAt < gpGlobals->curtime ) { RemoveSolidFlags( FSOLID_NOT_SOLID ); m_flGracePeriodEndsAt = 0; } } Vector vNewVelocity = GetAbsVelocity(); if ( m_bFlyingWild ) { // wobble crazily. Poll for a new target every quarter second, and if none is found, go // careering off. if ( gpGlobals->curtime >= m_flNextWobbleTime ) { Assert( !m_hHomingTarget.Get() ); CBaseEntity *pHomingTarget = FindPotentialTarget(); if ( pHomingTarget ) { SetTarget( pHomingTarget ); m_bFlyingWild = false; } else { // pick a new wobble direction /* m_vWobbleAngles = GetAbsAngles(); m_vWobbleAngles.y = m_vWobbleAngles.y + RandomFloat( -asw_rocket_wobble_amp.GetFloat(), asw_rocket_wobble_amp.GetFloat() ) ; if ( m_vWobbleAngles.y < 0 ) { m_vWobbleAngles.y = 360 + m_vWobbleAngles.y; } else if ( m_vWobbleAngles.y > 360 ) { m_vWobbleAngles.y = fmod( m_vWobbleAngles.y, 360 ); } */ m_vWobbleAngles = GetAbsAngles(); m_vWobbleAngles.y = fmodf( m_vWobbleAngles.y + RandomFloat( -asw_rocket_wobble_amp.GetFloat(), asw_rocket_wobble_amp.GetFloat() ), 360 ); m_flNextWobbleTime = gpGlobals->curtime + asw_rocket_wobble_freq.GetFloat(); } } } if ( !m_bFlyingWild ) { Vector targetPos; FindHomingPosition( &targetPos ); // find target direction Vector vTargetDir; VectorSubtract( targetPos, GetAbsOrigin(), vTargetDir ); float flDist = VectorNormalize( vTargetDir ); // find current direction Vector vDir = GetAbsVelocity(); //float flSpeed = VectorNormalize( vDir ); vNewVelocity = IntegrateRocketThrust( vTargetDir, flDist ); // face direction of movement QAngle finalAngles; VectorAngles( vNewVelocity, finalAngles ); SetAbsAngles( finalAngles ); // set to the new calculated velocity SetAbsVelocity( vNewVelocity ); } else // wobble crazily { #pragma message("TODO: straighten out this math") if ( gpGlobals->curtime >= m_flNextWobbleTime ) { // pick a new wobble direction m_vWobbleAngles = GetAbsAngles(); m_vWobbleAngles.y = fmodf( m_vWobbleAngles.y + RandomFloat( -asw_rocket_wobble_amp.GetFloat(), asw_rocket_wobble_amp.GetFloat() ), 360 ); m_flNextWobbleTime = gpGlobals->curtime + asw_rocket_wobble_freq.GetFloat(); } QAngle finalAngles = GetAbsAngles(); finalAngles.y = ApproachAngle( m_vWobbleAngles.y, finalAngles.y, 360.f * (gpGlobals->curtime - GetLastThink()) ); Vector forward; AngleVectors( finalAngles, &forward ); vNewVelocity = forward * FastSqrtEst( vNewVelocity.LengthSqr() ); if ( IsWallDodging() ) { ComputeWallDodge( vNewVelocity ); finalAngles.y = ApproachAngle( m_vWobbleAngles.y, finalAngles.y, 360.f * (gpGlobals->curtime - GetLastThink()) ); } vNewVelocity = IntegrateRocketThrust( forward, 0 ); // face direction of movement SetAbsAngles( finalAngles ); // set to the new calculated velocity SetAbsVelocity( vNewVelocity ); } // blow us up after our lifetime if (GetLifeFraction() >= 1.0f) { Explode(); } else { // Think as soon as possible SetNextThink( gpGlobals->curtime ); } }
//----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- void CAI_BaseFlyingBot::TurnHeadToTarget(float flInterval, const Vector &MoveTarget ) { float flDestYaw = VecToYaw( MoveTarget - GetLocalOrigin() ); float newYaw = AI_ClampYaw( GetHeadTurnRate() * 10.0f, m_fHeadYaw, flDestYaw, gpGlobals->curtime - GetLastThink() ); if ( newYaw != m_fHeadYaw ) { m_fHeadYaw = newYaw; } // Set us to face that way SetBoneController( 0, m_fHeadYaw ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CNPC_VehicleDriver::OverridePathMove( float flInterval ) { // Setup our initial path data if we've just started running a path if ( !m_pCurrentWaypoint ) { m_vecPrevPoint = GetAbsOrigin(); m_vecPrevPrevPoint = GetAbsOrigin(); m_vecDesiredPosition = GetNavigator()->GetCurWaypointPos(); CalculatePostPoints(); // Init our two waypoints m_Waypoints[0] = new CVehicleWaypoint( m_vecPrevPrevPoint, m_vecPrevPoint, m_vecDesiredPosition, m_vecPostPoint ); m_Waypoints[1] = new CVehicleWaypoint( m_vecPrevPoint, m_vecDesiredPosition, m_vecPostPoint, m_vecPostPostPoint ); m_pCurrentWaypoint = m_Waypoints[0]; m_pNextWaypoint = m_Waypoints[1]; m_flDistanceAlongSpline = 0.2; } // Have we reached our target? See if we've passed the current waypoint's plane. Vector vecAbsMins, vecAbsMaxs; CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs ); if ( BoxOnPlaneSide( vecAbsMins, vecAbsMaxs, &m_pCurrentWaypoint->planeWaypoint ) == 3 ) { if ( WaypointReached() ) return true; } // Did we bypass it and reach the next one already? if ( m_pNextWaypoint && BoxOnPlaneSide( vecAbsMins, vecAbsMaxs, &m_pNextWaypoint->planeWaypoint ) == 3 ) { if ( WaypointReached() ) return true; } // We may have just teleported, so check to make sure we have a waypoint if ( !m_pCurrentWaypoint || !m_pNextWaypoint ) return false; // Figure out which spline we're trucking along CVehicleWaypoint *pCurrentSplineBeingTraversed = m_pCurrentWaypoint; if ( m_flDistanceAlongSpline > 1 ) { pCurrentSplineBeingTraversed = m_pNextWaypoint; } // Get our current speed, and check it against the length of the spline to know how far to advance our marker AngularImpulse angVel; Vector vecVelocity; IPhysicsObject *pVehiclePhysics = m_hVehicleEntity->VPhysicsGetObject(); if( !pVehiclePhysics ) { // I think my vehicle has been destroyed. return false; } pVehiclePhysics->GetVelocity( &vecVelocity, &angVel ); float flSpeed = vecVelocity.Length(); float flIncTime = gpGlobals->curtime - GetLastThink(); float flIncrement = flIncTime * (flSpeed / pCurrentSplineBeingTraversed->GetLength()); // Now advance our point along the spline m_flDistanceAlongSpline = clamp( m_flDistanceAlongSpline + flIncrement, 0, 2); if ( m_flDistanceAlongSpline > 1 ) { // We crossed the spline boundary pCurrentSplineBeingTraversed = m_pNextWaypoint; } Vector vSplinePoint = pCurrentSplineBeingTraversed->GetPointAt( m_flDistanceAlongSpline > 1 ? m_flDistanceAlongSpline-1 : m_flDistanceAlongSpline ); Vector vSplineTangent = pCurrentSplineBeingTraversed->GetTangentAt( m_flDistanceAlongSpline > 1 ? m_flDistanceAlongSpline-1 : m_flDistanceAlongSpline ); // Now that we've got the target spline point & tangent, use it to decide what our desired velocity is. // If we're close to the tangent, just use the tangent. Otherwise, Lerp towards it. Vector vecToDesired = (vSplinePoint - GetAbsOrigin()); float flDistToDesired = VectorNormalize( vecToDesired ); float flTangentLength = VectorNormalize( vSplineTangent ); if ( flDistToDesired > (flTangentLength * 0.75) ) { m_vecDesiredVelocity = vecToDesired * flTangentLength; } else { VectorLerp( vSplineTangent, vecToDesired * flTangentLength, (flDistToDesired / (flTangentLength * 0.5)), m_vecDesiredVelocity ); } // Decrease speed according to the turn we're trying to make Vector vecRight; m_hVehicleEntity->GetVectors( NULL, &vecRight, NULL ); Vector vecNormVel = m_vecDesiredVelocity; VectorNormalize( vecNormVel ); float flDotRight = DotProduct( vecRight, vecNormVel ); flSpeed = (1.0 - fabs(flDotRight)); // Don't go slower than we've been told to go if ( flSpeed < m_flDriversMinSpeed ) { flSpeed = m_flDriversMinSpeed; } m_vecDesiredVelocity = vecNormVel * (flSpeed * m_flMaxSpeed); // Bunch o'debug if ( g_debug_vehicledriver.GetInt() & DRIVER_DEBUG_PATH ) { NDebugOverlay::Box( m_vecPrevPrevPoint, -Vector(15,15,15), Vector(15,15,15), 192,0,0, true, 0.1); NDebugOverlay::Box( m_vecPrevPoint, -Vector(20,20,20), Vector(20,20,20), 255,0,0, true, 0.1); NDebugOverlay::Box( m_vecPostPoint, -Vector(20,20,20), Vector(20,20,20), 0,192,0, true, 0.1); NDebugOverlay::Box( m_vecPostPostPoint, -Vector(20,20,20), Vector(20,20,20), 0,128,0, true, 0.1); NDebugOverlay::Box( vSplinePoint, -Vector(10,10,10), Vector(10,10,10), 0,0,255, true, 0.1); NDebugOverlay::Line( vSplinePoint, vSplinePoint + (vSplineTangent * 40), 0,0,255, true, 0.1); //NDebugOverlay::HorzArrow( pCurrentSplineBeingTraversed->splinePoints[0], pCurrentSplineBeingTraversed->splinePoints[1], 30, 255,255,255,0, false, 0.1f ); //NDebugOverlay::HorzArrow( pCurrentSplineBeingTraversed->splinePoints[1], pCurrentSplineBeingTraversed->splinePoints[2], 20, 255,255,255,0, false, 0.1f ); //NDebugOverlay::HorzArrow( pCurrentSplineBeingTraversed->splinePoints[2], pCurrentSplineBeingTraversed->splinePoints[3], 10, 255,255,255,0, false, 0.1f ); // Draw the plane we're checking against for waypoint passing Vector vecPlaneRight; CrossProduct( m_pCurrentWaypoint->planeWaypoint.normal, Vector(0,0,1), vecPlaneRight ); Vector vecPlane = m_pCurrentWaypoint->splinePoints[2]; NDebugOverlay::Line( vecPlane + (vecPlaneRight * -100), vecPlane + (vecPlaneRight * 100), 255,0,0, true, 0.1); // Draw the next plane too CrossProduct( m_pNextWaypoint->planeWaypoint.normal, Vector(0,0,1), vecPlaneRight ); vecPlane = m_pNextWaypoint->splinePoints[2]; NDebugOverlay::Line( vecPlane + (vecPlaneRight * -100), vecPlane + (vecPlaneRight * 100), 192,0,0, true, 0.1); } if ( g_debug_vehicledriver.GetInt() & DRIVER_DEBUG_PATH_SPLINE ) { for ( int i = 0; i < 10; i++ ) { Vector vecTarget = m_pCurrentWaypoint->GetPointAt( 0.1 * i ); Vector vecTangent = m_pCurrentWaypoint->GetTangentAt( 0.1 * i ); VectorNormalize(vecTangent); NDebugOverlay::Box( vecTarget, -Vector(10,10,10), Vector(10,10,10), 255,0,0, true, 0.1 ); NDebugOverlay::Line( vecTarget, vecTarget + (vecTangent * 10), 255,255,0, true, 0.1); } } return true; }