int CWeaponCrowbar::WeaponMeleeAttack1Condition( float flDot, float flDist ) { // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer(); CBaseEntity *pEnemy = pNPC->GetEnemy(); if (!pEnemy) return COND_NONE; Vector vecVelocity; vecVelocity = pEnemy->GetSmoothedVelocity( ); // Project where the enemy will be in a little while float dt = sk_crowbar_lead_time.GetFloat(); dt += random->RandomFloat( -0.3f, 0.2f ); if ( dt < 0.0f ) dt = 0.0f; Vector vecExtrapolatedPos; VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos ); Vector vecDelta; VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta ); if ( fabs( vecDelta.z ) > 70 ) { return COND_TOO_FAR_TO_ATTACK; } Vector vecForward = pNPC->BodyDirection2D( ); vecDelta.z = 0.0f; float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() ); if ((flDist > 64) && (flExtrapolatedDist > 64)) { return COND_TOO_FAR_TO_ATTACK; } float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() ); if ((flDot < 0.7) && (flExtrapolatedDot < 0.7)) { return COND_NOT_FACING_ATTACK; } return COND_CAN_MELEE_ATTACK1; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CAI_LeadBehavior::GatherConditions( void ) { BaseClass::GatherConditions(); if ( HasGoal() ) { // Fix for bad transition case (to investigate) if ( ( WorldSpaceCenter() - m_goal ).LengthSqr() > (64*64) && IsCurSchedule( SCHED_LEAD_AWAIT_SUCCESS, false) ) { GetOuter()->ClearSchedule( "Lead behavior - bad transition?" ); } // We have to collect data about the person we're leading around. CBaseEntity *pFollower = AI_GetSinglePlayer(); if( pFollower ) { ClearCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ); ClearCondition( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME ); // Check distance to the follower float flFollowerDist = ( WorldSpaceCenter() - pFollower->WorldSpaceCenter() ).Length(); bool bLagging = flFollowerDist > (m_leaddistance*4); if ( bLagging ) { if ( PlayerIsAheadOfMe() ) { bLagging = false; } } // Player heading towards me? // Only factor this in if you're not too far from them if ( flFollowerDist < (m_leaddistance*4) ) { Vector vecVelocity = pFollower->GetSmoothedVelocity(); if ( VectorNormalize(vecVelocity) > 50 ) { Vector vecToPlayer = (GetAbsOrigin() - pFollower->GetAbsOrigin()); VectorNormalize( vecToPlayer ); if ( DotProduct( vecVelocity, vecToPlayer ) > 0.5 ) { SetCondition( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME ); bLagging = false; } } } // If he's outside our lag range, consider him lagging if ( bLagging ) { SetCondition( COND_LEAD_FOLLOWER_LAGGING ); ClearCondition( COND_LEAD_FOLLOWER_NOT_LAGGING ); } else { ClearCondition( COND_LEAD_FOLLOWER_LAGGING ); SetCondition( COND_LEAD_FOLLOWER_NOT_LAGGING ); // If he's really close, note that if ( flFollowerDist < m_leaddistance ) { SetCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ); } } // To be considered not lagging, the follower must be visible, and within the lead distance if ( GetOuter()->FVisible( pFollower ) && GetOuter()->GetSenses()->ShouldSeeEntity( pFollower ) ) { SetCondition( COND_LEAD_HAVE_FOLLOWER_LOS ); m_LostLOSTimer.Stop(); } else { ClearCondition( COND_LEAD_HAVE_FOLLOWER_LOS ); // We don't have a LOS. But if we did have LOS, don't clear it until the timer is up. if ( m_LostLOSTimer.IsRunning() ) { if ( m_LostLOSTimer.Expired() ) { SetCondition( COND_LEAD_FOLLOWER_LAGGING ); ClearCondition( COND_LEAD_FOLLOWER_NOT_LAGGING ); } } else { m_LostLOSTimer.Start(); } } // Now we want to see if the follower is lost. Being lost means being (far away || out of LOS ) // && some time has passed. Also, lagging players are considered lost if the NPC's never delivered // the start speech, because it means the NPC should run to the player to start the lead. if( HasCondition( COND_LEAD_FOLLOWER_LAGGING ) ) { if ( !m_hasspokenstart ) { SetCondition( COND_LEAD_FOLLOWER_LOST ); } else { if ( m_args.bStopScenesWhenPlayerLost ) { // Try and stop me speaking my monolog, if I am if ( !m_hasPausedScenes && IsRunningScriptedScene( GetOuter() ) ) { //Msg("Stopping scenes.\n"); PauseActorsScriptedScenes( GetOuter(), false ); m_hasPausedScenes = true; } } if( m_LostTimer.IsRunning() ) { if( m_LostTimer.Expired() ) { SetCondition( COND_LEAD_FOLLOWER_LOST ); } } else { m_LostTimer.Start(); } } } else { // If I was speaking a monolog, resume it if ( m_args.bStopScenesWhenPlayerLost && m_hasPausedScenes ) { if ( IsRunningScriptedScene( GetOuter() ) ) { //Msg("Resuming scenes.\n"); ResumeActorsScriptedScenes( GetOuter(), false ); } m_hasPausedScenes = false; } m_LostTimer.Stop(); ClearCondition( COND_LEAD_FOLLOWER_LOST ); } // Evaluate for success // Success right now means being stationary, close to the goal, and having the player close by if ( !( m_args.flags & AILF_NO_DEF_SUCCESS ) ) { ClearCondition( COND_LEAD_SUCCESS ); // Check Z first, and only check 2d if we're within that bool bWithinZ = fabs(GetLocalOrigin().z - m_goal.z) < 64; if ( bWithinZ && (GetLocalOrigin() - m_goal).Length2D() <= 64 ) { if ( HasCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ) ) { SetCondition( COND_LEAD_SUCCESS ); } else if ( m_successdistance ) { float flDistSqr = (pFollower->GetAbsOrigin() - GetLocalOrigin()).Length2DSqr(); if ( flDistSqr < (m_successdistance*m_successdistance) ) { SetCondition( COND_LEAD_SUCCESS ); } } } } if ( m_MoveMonitor.IsMarkSet() && m_MoveMonitor.TargetMoved( pFollower ) ) SetCondition( COND_LEAD_FOLLOWER_MOVED_FROM_MARK ); else ClearCondition( COND_LEAD_FOLLOWER_MOVED_FROM_MARK ); } } if( m_args.bLeadDuringCombat ) { ClearCondition( COND_LIGHT_DAMAGE ); ClearCondition( COND_HEAVY_DAMAGE ); } }