void CASW_Alien_Jumper::BuildScheduleTestBits( void ) { //Don't allow any modifications when scripted if ( m_NPCState == NPC_STATE_SCRIPT ) return; BaseClass::BuildScheduleTestBits(); //Make sure we interrupt a run schedule if we can jump if ( IsCurSchedule(SCHED_CHASE_ENEMY) && GetNavType() == NAV_GROUND ) { SetCustomInterruptCondition( COND_ASW_ALIEN_CAN_JUMP ); SetCustomInterruptCondition( COND_ENEMY_UNREACHABLE ); } //Interrupt any schedule unless already fleeing, burrowing, burrowed, or unburrowing. if(!IsCurSchedule(SCHED_ASW_ALIEN_JUMP) && ( GetFlags() & FL_ONGROUND ) ) { // Only do these if not jumping as well if (!IsCurSchedule(SCHED_ASW_ALIEN_JUMP)) { if ( GetEnemy() == NULL ) { SetCustomInterruptCondition( COND_HEAR_PHYSICS_DANGER ); } //if ( GetNavType() != NAV_JUMP ) //SetCustomInterruptCondition( COND_ANTLION_RECEIVED_ORDERS ); } //SetCustomInterruptCondition( COND_ANTLION_ON_NPC ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CNPC_Monk::BuildScheduleTestBits( void ) { // FIXME: we need a way to make scenes non-interruptible if ( IsCurSchedule( SCHED_RANGE_ATTACK1 ) || IsCurSchedule( SCHED_SCENE_SEQUENCE ) || IsCurSchedule( SCHED_SCENE_WALK ) ) { ClearCustomInterruptCondition( COND_LIGHT_DAMAGE ); ClearCustomInterruptCondition( COND_HEAVY_DAMAGE ); ClearCustomInterruptCondition( COND_NEW_ENEMY ); ClearCustomInterruptCondition( COND_HEAR_DANGER ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CAI_PolicingBehavior::BuildScheduleTestBits( void ) { if ( IsCurSchedule( SCHED_IDLE_STAND ) || IsCurSchedule( SCHED_ALERT_STAND ) ) { if ( m_flNextHarassTime < gpGlobals->curtime ) { GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_POLICE_TARGET_TOO_CLOSE_HARASS ) ); } GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS ) ); } }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CAI_FearBehavior::BuildScheduleTestBits() { BaseClass::BuildScheduleTestBits(); if( GetOuter()->GetState() != NPC_STATE_SCRIPT ) { // Stop doing ANYTHING if we get scared. //GetOuter()->SetCustomInterruptCondition( COND_HEAR_DANGER ); if( !IsCurSchedule(SCHED_FEAR_MOVE_TO_SAFE_PLACE_RETRY, false) && !IsCurSchedule(SCHED_FEAR_MOVE_TO_SAFE_PLACE, false) ) { GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal(COND_FEAR_SEPARATED_FROM_PLAYER) ); } } }
//----------------------------------------------------------------------------- // Purpose: Allows for modification of the interrupt mask for the current schedule. // In the most cases the base implementation should be called first. //----------------------------------------------------------------------------- void CNPC_PoisonZombie::BuildScheduleTestBits( void ) { BaseClass::BuildScheduleTestBits(); if ( IsCurSchedule( SCHED_CHASE_ENEMY ) ) { SetCustomInterruptCondition( COND_LIGHT_DAMAGE ); SetCustomInterruptCondition( COND_HEAVY_DAMAGE ); } else if ( IsCurSchedule( SCHED_RANGE_ATTACK1 ) || IsCurSchedule( SCHED_RANGE_ATTACK2 ) ) { ClearCustomInterruptCondition( COND_LIGHT_DAMAGE ); ClearCustomInterruptCondition( COND_HEAVY_DAMAGE ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CAI_AssaultBehavior::BuildScheduleTestBits() { BaseClass::BuildScheduleTestBits(); // If we're allowed to divert, add the appropriate interrupts to our movement schedules if ( IsAllowedToDivert() ) { if ( IsCurSchedule( SCHED_MOVE_TO_ASSAULT_POINT ) || IsCurSchedule( SCHED_MOVE_TO_RALLY_POINT ) || IsCurSchedule( SCHED_HOLD_RALLY_POINT ) ) { GetOuter()->SetCustomInterruptCondition( COND_NEW_ENEMY ); GetOuter()->SetCustomInterruptCondition( COND_SEE_ENEMY ); } } }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CAI_OperatorBehavior::GatherConditions( void ) { if( GetGoalEntity() ) { if( GetGoalEntity()->GetState() == OPERATOR_STATE_FINISHED ) { if( IsCurSchedule(SCHED_OPERATOR_OPERATE) ) { // Break us out of the operator schedule if the operation completes. SetCondition(COND_PROVOKED); } m_hGoalEntity.Set(NULL); m_hPositionEnt.Set(NULL); } else { if( CanSeePositionEntity() ) { ClearCondition( COND_OPERATOR_LOST_SIGHT_OF_POSITION ); } else { SetCondition( COND_OPERATOR_LOST_SIGHT_OF_POSITION ); } } } BaseClass::GatherConditions(); // Ignore player pushing. ClearCondition( COND_PLAYER_PUSHING ); }
void CZombie::BuildScheduleTestBits( void ) { BaseClass::BuildScheduleTestBits(); if( !IsCurSchedule( SCHED_FLINCH_PHYSICS ) && !m_ActBusyBehavior.IsActive() ) SetCustomInterruptCondition( COND_PHYSICS_DAMAGE ); }
void CAI_StandoffBehavior::BuildScheduleTestBits() { BaseClass::BuildScheduleTestBits(); if ( IsCurSchedule( SCHED_TAKE_COVER_FROM_ENEMY ) ) GetOuter()->ClearCustomInterruptCondition( COND_NEW_ENEMY ); }
//----------------------------------------------------------------------------- // Purpose: Allows for modification of the interrupt mask for the current schedule. // In the most cases the base implementation should be called first. //----------------------------------------------------------------------------- void CNPC_AntlionGrub::BuildScheduleTestBits( void ) { //Always squirm if we're being squashed if ( !IsCurSchedule( SCHED_SMALL_FLINCH ) ) { SetCustomInterruptCondition( COND_ANTLIONGRUB_BEING_SQUASHED ); } }
void CNPC_SO_BaseZombie::BuildScheduleTestBits( void ) { BaseClass::BuildScheduleTestBits(); // Add a custom rally point entity for NPC spawners if( IsCurSchedule( SCHED_ZOMBIE_AMBUSH_MODE ) ) SetCustomInterruptCondition( COND_RECEIVED_ORDERS ); }
void CASW_Harvester::BuildScheduleTestBits() { BaseClass::BuildScheduleTestBits(); if ( IsCurSchedule( SCHED_RUN_FROM_ENEMY ) ) { SetCustomInterruptCondition( COND_CAN_RANGE_ATTACK1 ); } }
//----------------------------------------------------------------------------- // Purpose: Allows for modification of the interrupt mask for the current schedule. // In the most cases the base implementation should be called first. //----------------------------------------------------------------------------- void CNPC_CombineShot::BuildScheduleTestBits(void) { //Interrupt any schedule with physics danger (as long as I'm not moving or already trying to block) if ( m_flGroundSpeed == 0.0 && !IsCurSchedule( SCHED_FLINCH_PHYSICS ) ) { SetCustomInterruptCondition( COND_HEAR_PHYSICS_DANGER ); } BaseClass::BuildScheduleTestBits(); }
//----------------------------------------------------------------------------- // Purpose: Allows for modification of the interrupt mask for the current schedule. // In the most cases the base implementation should be called first. //----------------------------------------------------------------------------- void CNPC_Monster::BuildScheduleTestBits( void ) { BaseClass::BuildScheduleTestBits(); if ( IsCurSchedule( SCHED_CHASE_ENEMY ) ) { SetCustomInterruptCondition( COND_LIGHT_DAMAGE ); SetCustomInterruptCondition( COND_HEAVY_DAMAGE ); } }
bool CAI_StandoffBehavior::IsValidCover( const Vector &vecCoverLocation, const CAI_Hint *pHint ) { if ( !BaseClass::IsValidCover( vecCoverLocation, pHint ) ) return false; if ( IsCurSchedule( SCHED_TAKE_COVER_FROM_BEST_SOUND ) ) return true; return ( m_fIgnoreFronts || IsBehindBattleLines( vecCoverLocation ) ); }
//----------------------------------------------------------------------------- // Purpose: Handle specific interactions with other NPCs //----------------------------------------------------------------------------- bool CNPC_Bug_Warrior::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sender ) { // Check for a target found while burrowed if ( interactionType == g_interactionBugSquadAttacking ) { // Ignore ones sent by me if ( sender == this ) return false; // Interrupt me if I'm fleeing if ( IsCurSchedule(SCHED_WBUG_FLEE_ENEMY) || IsCurSchedule(SCHED_WBUG_CHASE_ENEMY_FAILED) ) { SetCondition( COND_WBUG_STOP_FLEEING ); } return true; } return false; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CAI_PolicingBehavior::GatherConditions( void ) { BaseClass::GatherConditions(); // Mapmaker may have removed our goal while we're running our schedule if ( !m_hPoliceGoal ) { Disable(); return; } ClearCondition( COND_POLICE_TARGET_TOO_CLOSE_HARASS ); ClearCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS ); CBaseEntity *pTarget = m_hPoliceGoal->GetTarget(); if ( pTarget == NULL ) { DevMsg( "ai_goal_police with NULL target entity!\n" ); return; } // See if we need to knock out our target immediately if ( ShouldKnockOutTarget( pTarget ) ) { SetCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS ); } float flDistSqr = ( m_hPoliceGoal->WorldSpaceCenter() - pTarget->WorldSpaceCenter() ).Length2DSqr(); float radius = ( m_hPoliceGoal->GetRadius() * PATROL_RADIUS_RATIO ); float zDiff = fabs( m_hPoliceGoal->WorldSpaceCenter().z - pTarget->WorldSpaceCenter().z ); // If we're too far away, don't bother if ( flDistSqr < (radius*radius) && zDiff < 32.0f ) { SetCondition( COND_POLICE_TARGET_TOO_CLOSE_HARASS ); if ( flDistSqr < (m_hPoliceGoal->GetRadius()*m_hPoliceGoal->GetRadius()) ) { SetCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS ); } } // If we're supposed to stop chasing (aggression over), return if ( m_bTargetIsHostile && m_flAggressiveTime < gpGlobals->curtime && IsCurSchedule(SCHED_CHASE_ENEMY) ) { // Force me to re-evaluate my schedule GetOuter()->ClearSchedule( "Stopped chasing, aggression over" ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CNPC_Assassin::BuildScheduleTestBits( void ) { SetNextThink( gpGlobals->curtime + 0.05 ); //Don't allow any modifications when scripted if ( m_NPCState == NPC_STATE_SCRIPT ) return; //Become interrupted if we're targetted when shooting an enemy if ( IsCurSchedule( SCHED_RANGE_ATTACK1 ) ) { SetCustomInterruptCondition( COND_ASSASSIN_ENEMY_TARGETTING_ME ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CNPC_Monk::BuildScheduleTestBits( void ) { // FIXME: we need a way to make scenes non-interruptible #if 0 if ( IsCurSchedule( SCHED_RANGE_ATTACK1 ) || IsCurSchedule( SCHED_SCENE_GENERIC ) ) { ClearCustomInterruptCondition( COND_LIGHT_DAMAGE ); ClearCustomInterruptCondition( COND_HEAVY_DAMAGE ); ClearCustomInterruptCondition( COND_NEW_ENEMY ); ClearCustomInterruptCondition( COND_HEAR_DANGER ); } #endif // Don't interrupt while shooting the gun const Task_t* pTask = GetTask(); if ( pTask && (pTask->iTask == TASK_RANGE_ATTACK1) ) { ClearCustomInterruptCondition( COND_HEAVY_DAMAGE ); ClearCustomInterruptCondition( COND_ENEMY_OCCLUDED ); ClearCustomInterruptCondition( COND_HEAR_DANGER ); ClearCustomInterruptCondition( COND_WEAPON_BLOCKED_BY_FRIEND ); ClearCustomInterruptCondition( COND_WEAPON_SIGHT_OCCLUDED ); } }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CAI_AssaultBehavior::GatherConditions( void ) { BaseClass::GatherConditions(); // If this NPC is moving towards an assault point which // a) Has a Next Assault Point, and // b) Is flagged to Clear On Arrival, // then hit and clear the assault point (fire all entity I/O) and move on to the next one without // interrupting the NPC's schedule. This provides a more fluid movement from point to point. if( IsCurSchedule( SCHED_MOVE_TO_ASSAULT_POINT ) && hl2_episodic.GetBool() ) { if( m_hAssaultPoint && m_hAssaultPoint->HasSpawnFlags(SF_ASSAULTPOINT_CLEARONARRIVAL) && m_hAssaultPoint->m_NextAssaultPointName != NULL_STRING ) { float flDist = GetAbsOrigin().DistTo( m_hAssaultPoint->GetAbsOrigin() ); if( flDist <= GetOuter()->GetMotor()->MinStoppingDist() ) { OnHitAssaultPoint(); ClearAssaultPoint(); AI_NavGoal_t goal( m_hAssaultPoint->GetAbsOrigin() ); goal.pTarget = m_hAssaultPoint; if ( GetNavigator()->SetGoal( goal ) == false ) { TaskFail( "Can't refresh assault path" ); } } } if( OnStrictAssault() ) { // Don't get distracted. Die trying if you have to. ClearCondition( COND_HEAR_DANGER ); } } if ( IsForcingCrouch() && GetOuter()->IsCrouching() ) { ClearCondition( COND_HEAR_BULLET_IMPACT ); } }
void CASW_Parasite::BuildScheduleTestBits( void ) { //Don't allow any modifications when scripted if ( m_NPCState == NPC_STATE_SCRIPT ) return; //Make sure we interrupt a run schedule if we can jump if ( IsCurSchedule(SCHED_CHASE_ENEMY) ) { SetCustomInterruptCondition( COND_ENEMY_UNREACHABLE ); } //Interrupt any schedule unless already fleeing, burrowing, burrowed, or unburrowing. if( GetFlags() & FL_ONGROUND ) { if ( GetEnemy() == NULL ) { SetCustomInterruptCondition( COND_HEAR_PHYSICS_DANGER ); } } }
//----------------------------------------------------------------------------- // Purpose: Return true if the player is further ahead on the lead route than I am //----------------------------------------------------------------------------- bool CAI_LeadBehavior::PlayerIsAheadOfMe( bool bForce ) { // Find the nearest point on our route to the player, and see if that's further // ahead of us than our nearest point. // If we're not leading, our route doesn't lead to the goal, so we can't use it. // If we just started leading, go ahead and test, and we'll build a temp route. if ( !m_bInitialAheadTest && !IsCurSchedule( SCHED_LEAD_PLAYER, false ) && !bForce ) return false; m_bInitialAheadTest = false; Vector vecClosestPoint; if ( GetClosestPointOnRoute( AI_GetSinglePlayer()->GetAbsOrigin(), &vecClosestPoint ) ) { // If the closest point is not right next to me, then // the player is somewhere ahead of me on the route. if ( (vecClosestPoint - GetOuter()->GetAbsOrigin()).LengthSqr() > (32*32) ) return true; } return false; }
int CASW_Harvester::SelectFlinchSchedule_ASW() { // only flinch in easy mode if (ASWGameRules() && ASWGameRules()->GetSkillLevel() != 1) return SCHED_NONE; if ( !HasCondition(COND_HEAVY_DAMAGE) && !HasCondition(COND_LIGHT_DAMAGE) ) return SCHED_NONE; if ( IsCurSchedule( SCHED_BIG_FLINCH ) ) return SCHED_NONE; // only flinch if shot during a laying a critter if (! (GetTask() && (GetTask()->iTask == TASK_LAY_CRITTER)) ) return SCHED_NONE; // Any damage. Break out of my current schedule and flinch. Activity iFlinchActivity = GetFlinchActivity( true, false ); if ( HaveSequenceForActivity( iFlinchActivity ) ) return SCHED_BIG_FLINCH; return SCHED_NONE; }
int CAI_LeadBehavior::SelectSchedule() { if ( HasGoal() ) { if( HasCondition(COND_LEAD_SUCCESS) ) { return SCHED_LEAD_SUCCEED; } // Player's here, but does he have the weapon we want him to have? if ( m_weaponname != NULL_STRING ) { CBasePlayer *pFollower = AI_GetSinglePlayer(); if ( pFollower && !pFollower->Weapon_OwnsThisType( STRING(m_weaponname) ) ) { // If the safety timeout has run out, just give the player the weapon if ( !m_flWeaponSafetyTimeOut || (m_flWeaponSafetyTimeOut > gpGlobals->curtime) ) return SCHED_LEAD_PLAYERNEEDSWEAPON; string_t iszItem = AllocPooledString( "weapon_bugbait" ); pFollower->GiveNamedItem( STRING(iszItem) ); } } // If we have a waitpoint, we want to wait at it for the player. if( HasWaitPoint() && !PlayerIsAheadOfMe( true ) ) { bool bKeepWaiting = true; // If we have no wait distance, trigger as soon as the player comes in view if ( !m_waitdistance ) { if ( HasCondition( COND_SEE_PLAYER ) ) { // We've spotted the player, so stop waiting bKeepWaiting = false; } } else { // We have to collect data about the person we're leading around. CBaseEntity *pFollower = AI_GetSinglePlayer(); if( pFollower ) { float flFollowerDist = ( WorldSpaceCenter() - pFollower->WorldSpaceCenter() ).Length(); if ( flFollowerDist < m_waitdistance ) { bKeepWaiting = false; } } } // Player still not here? if ( bKeepWaiting ) return SCHED_LEAD_WAITFORPLAYER; // We're finished waiting m_waitpoint = vec3_origin; Speak( TLK_LEAD_WAITOVER ); // Don't speak the start line, because we've said m_hasspokenstart = true; return SCHED_WAIT_FOR_SPEAK_FINISH; } // If we haven't spoken our start speech, do that first if ( !m_hasspokenstart ) { if ( HasCondition(COND_LEAD_HAVE_FOLLOWER_LOS) && HasCondition(COND_LEAD_FOLLOWER_VERY_CLOSE) ) return SCHED_LEAD_SPEAK_START; // We haven't spoken to him, and we still need to. Go get him. return SCHED_LEAD_RETRIEVE; } if( HasCondition( COND_LEAD_FOLLOWER_LOST ) ) { if( m_args.iRetrievePlayer ) { // If not, we want to go get the player. DevMsg( GetOuter(), "Follower lost. Spoke COMING_BACK.\n"); Speak( TLK_LEAD_COMINGBACK ); m_MoveMonitor.ClearMark(); // If we spoke something, wait for it to finish if ( m_args.iComingBackWaitForSpeak && IsSpeaking() ) return SCHED_LEAD_SPEAK_THEN_RETRIEVE_PLAYER; return SCHED_LEAD_RETRIEVE; } else { // Just stay right here and wait. return SCHED_LEAD_WAITFORPLAYERIDLE; } } if( HasCondition( COND_LEAD_FOLLOWER_LAGGING ) ) { DevMsg( GetOuter(), "Follower lagging. Spoke CATCHUP.\n"); Speak( TLK_LEAD_CATCHUP ); return SCHED_LEAD_PAUSE; } else { // If we're at the goal, wait for the player to get here if ( ( WorldSpaceCenter() - m_goal ).LengthSqr() < (64*64) ) return SCHED_LEAD_AWAIT_SUCCESS; // If we were retrieving the player, speak the resume if ( IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) || IsCurSchedule( SCHED_LEAD_WAITFORPLAYERIDLE, false ) ) { Speak( TLK_LEAD_RETRIEVE ); // If we spoke something, wait for it to finish, if the mapmakers wants us to if ( m_args.iRetrieveWaitForSpeak && IsSpeaking() ) return SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER; } DevMsg( GetOuter(), "Leading Follower.\n"); return SCHED_LEAD_PLAYER; } } return BaseClass::SelectSchedule(); }
//----------------------------------------------------------------------------- // 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 ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CAI_LeadBehavior::GetClosestPointOnRoute( const Vector &targetPos, Vector *pVecClosestPoint ) { AI_Waypoint_t *waypoint = GetOuter()->GetNavigator()->GetPath()->GetCurWaypoint(); AI_Waypoint_t *builtwaypoints = NULL; if ( !waypoint ) { // We arrive here twice when lead behaviour starts: // - When the lead behaviour is first enabled. We have no schedule. We want to know if the player is ahead of us. // - A frame later when we've chosen to lead the player, but we still haven't built our route. We know that the // the player isn't lagging, so it's safe to go ahead and simply say he's ahead of us. This avoids building // the temp route twice. if ( IsCurSchedule( SCHED_LEAD_PLAYER, false ) ) return true; // Build a temp route to the gold and use that builtwaypoints = GetOuter()->GetPathfinder()->BuildRoute( GetOuter()->GetAbsOrigin(), m_goal, NULL, GetOuter()->GetDefaultNavGoalTolerance(), GetOuter()->GetNavType(), true ); if ( !builtwaypoints ) return false; GetOuter()->GetPathfinder()->UnlockRouteNodes( builtwaypoints ); waypoint = builtwaypoints; } // Find the nearest node to the target (going forward) float flNearestDist2D = 999999999; float flNearestDist = 999999999; float flPathDist, flPathDist2D; Vector vecNearestPoint(0, 0, 0); Vector vecPrevPos = GetOuter()->GetAbsOrigin(); for ( ; (waypoint != NULL) ; waypoint = waypoint->GetNext() ) { // Find the closest point on the line segment on the path Vector vecClosest; CalcClosestPointOnLineSegment( targetPos, vecPrevPos, waypoint->GetPos(), vecClosest ); /* if ( builtwaypoints ) { NDebugOverlay::Line( vecPrevPos, waypoint->GetPos(), 0,0,255,true, 10.0 ); } */ vecPrevPos = waypoint->GetPos(); // Find the distance between this test point and our goal point flPathDist2D = vecClosest.AsVector2D().DistToSqr( targetPos.AsVector2D() ); if ( flPathDist2D > flNearestDist2D ) continue; flPathDist = vecClosest.z - targetPos.z; flPathDist *= flPathDist; flPathDist += flPathDist2D; if (( flPathDist2D == flNearestDist2D ) && ( flPathDist >= flNearestDist )) continue; flNearestDist2D = flPathDist2D; flNearestDist = flPathDist; vecNearestPoint = vecClosest; } if ( builtwaypoints ) { //NDebugOverlay::Line( vecNearestPoint, targetPos, 0,255,0,true, 10.0 ); DeleteAll( builtwaypoints ); } *pVecClosestPoint = vecNearestPoint; return true; }
bool CASW_Alien_Jumper::IsJumping() { return IsCurSchedule(SCHED_ASW_ALIEN_JUMP); }
void CAI_LeadBehavior::StartTask( const Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_LEAD_FACE_GOAL: { if ( m_goalyaw != -1 ) { GetMotor()->SetIdealYaw( m_goalyaw ); } TaskComplete(); break; } case TASK_LEAD_SUCCEED: { Speak( TLK_LEAD_SUCCESS ); NotifyEvent( LBE_SUCCESS ); break; } case TASK_LEAD_ARRIVE: { // Only speak the first time we arrive if ( !m_hasspokenarrival ) { Speak( TLK_LEAD_ARRIVAL ); NotifyEvent( LBE_ARRIVAL ); m_hasspokenarrival = true; } else { TaskComplete(); } break; } case TASK_STOP_LEADING: { ClearGoal(); TaskComplete(); break; } case TASK_GET_PATH_TO_LEAD_GOAL: { if ( GetNavigator()->SetGoal( m_goal ) ) { TaskComplete(); } else { TaskFail("NO PATH"); } break; } case TASK_LEAD_GET_PATH_TO_WAITPOINT: { if ( GetNavigator()->SetGoal( m_waitpoint ) ) { TaskComplete(); } else { TaskFail("NO PATH"); } break; } case TASK_LEAD_WALK_PATH: { // If we're leading, and we're supposed to run, run instead of walking if ( m_run && ( IsCurSchedule( SCHED_LEAD_WAITFORPLAYER, false ) || IsCurSchedule( SCHED_LEAD_PLAYER, false ) || IsCurSchedule( SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER, false )|| IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) ) ) { ChainStartTask( TASK_RUN_PATH ); } else { ChainStartTask( TASK_WALK_PATH ); } break; } case TASK_LEAD_WAVE_TO_PLAYER: { // Wave to the player if we can see him. Otherwise, just idle. if ( HasCondition( COND_SEE_PLAYER ) ) { Speak( TLK_LEAD_ATTRACTPLAYER ); if ( HaveSequenceForActivity(ACT_SIGNAL1) ) { SetActivity(ACT_SIGNAL1); } } else { SetActivity(ACT_IDLE); } TaskComplete(); break; } case TASK_LEAD_PLAYER_NEEDS_WEAPON: { float flAvailableTime = GetOuter()->GetExpresser()->GetSemaphoreAvailableTime( GetOuter() ); // if someone else is talking, don't speak if ( flAvailableTime <= gpGlobals->curtime ) { Speak( TLK_LEAD_MISSINGWEAPON ); } SetActivity(ACT_IDLE); TaskComplete(); break; } case TASK_LEAD_SPEAK_START: { m_hasspokenstart = true; Speak( TLK_LEAD_START ); SetActivity(ACT_IDLE); TaskComplete(); break; } case TASK_LEAD_MOVE_TO_RANGE: { // If we haven't spoken our start speech, move closer if ( !m_hasspokenstart) { ChainStartTask( TASK_MOVE_TO_GOAL_RANGE, m_leaddistance - 24 ); } else { ChainStartTask( TASK_MOVE_TO_GOAL_RANGE, m_retrievedistance ); } break; } case TASK_LEAD_RETRIEVE_WAIT: { m_MoveMonitor.SetMark( AI_GetSinglePlayer(), 24 ); ChainStartTask( TASK_WAIT_INDEFINITE ); break; } case TASK_STOP_MOVING: { BaseClass::StartTask( pTask); if ( IsCurSchedule( SCHED_LEAD_PAUSE, false ) && pTask->flTaskData == 1 ) { GetNavigator()->SetArrivalDirection( GetTarget() ); } break; } case TASK_WAIT_FOR_SPEAK_FINISH: { BaseClass::StartTask( pTask); if( GetOuter()->GetState() == NPC_STATE_COMBAT ) { // Don't stand around jabbering in combat. TaskComplete(); } // If we're not supposed to wait for the player, don't wait for speech to finish. // Instead, just wait a wee tad, and then start moving. NPC will speak on the go. if ( TaskIsRunning() && !m_args.iRetrievePlayer ) { if ( gpGlobals->curtime - GetOuter()->GetTimeTaskStarted() > 0.3 ) { TaskComplete(); } } break; } default: BaseClass::StartTask( pTask); } }
void CAI_LeadBehavior::RunTask( const Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_LEAD_SUCCEED: { if ( !IsSpeaking() ) { TaskComplete(); NotifyEvent( LBE_DONE ); } break; } case TASK_LEAD_ARRIVE: { if ( !IsSpeaking() ) { TaskComplete(); NotifyEvent( LBE_ARRIVAL_DONE ); } break; } case TASK_LEAD_MOVE_TO_RANGE: { // If we haven't spoken our start speech, move closer if ( !m_hasspokenstart) { ChainRunTask( TASK_MOVE_TO_GOAL_RANGE, m_leaddistance - 24 ); } else { ChainRunTask( TASK_MOVE_TO_GOAL_RANGE, m_retrievedistance ); if ( !TaskIsComplete() ) { // Transition to a walk when we get near the player // Check Z first, and only check 2d if we're within that Vector vecGoalPos = GetNavigator()->GetGoalPos(); float distance = fabs(vecGoalPos.z - GetLocalOrigin().z); bool bWithinZ = false; if ( distance < m_retrievedistance ) { distance = ( vecGoalPos - GetLocalOrigin() ).Length2D(); bWithinZ = true; } if ( distance > m_retrievedistance ) { Activity followActivity = ACT_WALK; if ( GetOuter()->GetState() == NPC_STATE_COMBAT || ( (!bWithinZ || distance < (m_retrievedistance*4)) && GetOuter()->GetState() != NPC_STATE_COMBAT ) ) { followActivity = ACT_RUN; } // Don't confuse move and shoot by resetting the activity every think Activity curActivity = GetNavigator()->GetMovementActivity(); switch( curActivity ) { case ACT_WALK_AIM: curActivity = ACT_WALK; break; case ACT_RUN_AIM: curActivity = ACT_RUN; break; } if ( curActivity != followActivity ) { GetNavigator()->SetMovementActivity(followActivity); } GetNavigator()->SetArrivalDirection( GetOuter()->GetTarget() ); } } } break; } case TASK_LEAD_RETRIEVE_WAIT: { ChainRunTask( TASK_WAIT_INDEFINITE ); break; } case TASK_LEAD_WALK_PATH: { // If we're leading, and we're supposed to run, run instead of walking if ( m_run && ( IsCurSchedule( SCHED_LEAD_WAITFORPLAYER, false ) || IsCurSchedule( SCHED_LEAD_PLAYER, false ) || IsCurSchedule( SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER, false )|| IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) ) ) { ChainRunTask( TASK_RUN_PATH ); } else { ChainRunTask( TASK_WALK_PATH ); } // While we're walking if ( TaskIsRunning() && IsCurSchedule( SCHED_LEAD_PLAYER, false ) ) { // If we're not speaking, and we haven't tried for a while, try to speak lead idle if ( m_flNextLeadIdle < gpGlobals->curtime && !IsSpeaking() ) { m_flNextLeadIdle = gpGlobals->curtime + RandomFloat( 10,15 ); if ( !m_args.iRetrievePlayer && HasCondition( COND_LEAD_FOLLOWER_LOST ) && HasCondition(COND_SEE_PLAYER) ) { Speak( TLK_LEAD_COMINGBACK ); } else { Speak( TLK_LEAD_IDLE ); } } } break; } default: BaseClass::RunTask( pTask); } }
//----------------------------------------------------------------------------- // Purpose: // Input : *pTask - //----------------------------------------------------------------------------- void CAI_AssaultBehavior::RunTask( const Task_t *pTask ) { switch( pTask->iTask ) { case TASK_WAIT_ASSAULT_DELAY: case TASK_AWAIT_ASSAULT_TIMEOUT: if ( m_hAssaultPoint ) { if ( m_hAssaultPoint->m_bInputForcedClear || (m_hAssaultPoint->m_bClearOnContact && HasCondition( COND_SEE_ENEMY )) ) { // If we're on an assault that should clear on contact, clear when we see an enemy TaskComplete(); } } if( GetOuter()->IsWaitFinished() && ( pTask->iTask == TASK_WAIT_ASSAULT_DELAY || !m_hAssaultPoint->m_bNeverTimeout ) ) { TaskComplete(); } break; case TASK_FACE_RALLY_POINT: case TASK_FACE_ASSAULT_POINT: GetMotor()->UpdateYaw(); if( HasCondition( COND_CAN_RANGE_ATTACK1 ) ) { // Out early if the NPC can attack. TaskComplete(); } if ( GetOuter()->FacingIdeal() ) { TaskComplete(); } break; case TASK_AWAIT_CUE: // If we've lost our rally point, abort if ( !m_hRallyPoint ) { TaskFail("No rally point."); break; } if( PollAssaultCue() ) { TaskComplete(); } if ( IsForcingCrouch() ) break; if( GetOuter()->GetEnemy() && m_hRallyPoint->m_RallySequenceName == NULL_STRING ) { // I have an enemy and I'm NOT playing a custom animation. ChainRunTask( TASK_FACE_ENEMY, 0 ); } break; case TASK_WAIT_FOR_MOVEMENT: if ( ai_debug_assault.GetBool() ) { if ( IsCurSchedule( SCHED_MOVE_TO_ASSAULT_POINT ) ) { NDebugOverlay::Line( WorldSpaceCenter(), GetNavigator()->GetGoalPos(), 255,0,0, true,0.1); NDebugOverlay::Box( GetNavigator()->GetGoalPos(), -Vector(10,10,10), Vector(10,10,10), 255,0,0, 8, 0.1 ); } else if ( IsCurSchedule( SCHED_MOVE_TO_RALLY_POINT ) ) { NDebugOverlay::Line( WorldSpaceCenter(), GetNavigator()->GetGoalPos(), 0,255,0, true,0.1); NDebugOverlay::Box( GetNavigator()->GetGoalPos(), -Vector(10,10,10), Vector(10,10,10), 0,255,0, 8, 0.1 ); } } if ( m_hAssaultPoint && (m_hAssaultPoint->m_bInputForcedClear || (m_hAssaultPoint->m_bClearOnContact && HasCondition( COND_SEE_ENEMY ))) ) { DevMsg( "Assault Cleared due to Contact or Input!\n" ); ClearAssaultPoint(); TaskComplete(); return; } BaseClass::RunTask( pTask ); break; default: BaseClass::RunTask( pTask ); break; } }