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);
	}
}
//=========================================================
// MaintainSchedule - does all the per-think schedule maintenance.
// ensures that the monster leaves this function with a valid
// schedule!
//=========================================================
void CBaseMonster :: MaintainSchedule ( void )
{
	Schedule_t	*pNewSchedule;
	int			i;

	// UNDONE: Tune/fix this 10... This is just here so infinite loops are impossible
	for ( i = 0; i < 10; i++ )
	{
		if ( m_pSchedule != NULL && TaskIsComplete() )
		{
			NextScheduledTask();                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
		}

	// validate existing schedule 
		if ( !FScheduleValid() || m_MonsterState != m_IdealMonsterState )
		{
			// if we come into this block of code, the schedule is going to have to be changed.
			// if the previous schedule was interrupted by a condition, GetIdealState will be 
			// called. Else, a schedule finished normally.

			// Notify the monster that his schedule is changing
			ScheduleChange();

			// Call GetIdealState if we're not dead and one or more of the following...
			// - in COMBAT state with no enemy (it died?)
			// - conditions bits (excluding SCHEDULE_DONE) indicate interruption,
			// - schedule is done but schedule indicates it wants GetIdealState called
			//   after successful completion (by setting bits_COND_SCHEDULE_DONE in iInterruptMask)
			// DEAD & SCRIPT are not suggestions, they are commands!
			if ( m_IdealMonsterState != MONSTERSTATE_DEAD && 
				 (m_IdealMonsterState != MONSTERSTATE_SCRIPT || m_IdealMonsterState == m_MonsterState) )
			{
				// if we're here, then either we're being told to do something (besides dying or playing a script)
				// or our current schedule (besides dying) is invalid. -- LRC
				if (	(m_afConditions && !HasConditions(bits_COND_SCHEDULE_DONE)) ||
						(m_pSchedule && (m_pSchedule->iInterruptMask & bits_COND_SCHEDULE_DONE)) ||
						((m_MonsterState == MONSTERSTATE_COMBAT) && (m_hEnemy == NULL))	)
				{
					GetIdealState();
				}
			}
			if ( HasConditions( bits_COND_TASK_FAILED ) && m_MonsterState == m_IdealMonsterState )
			{
				if ( m_failSchedule != SCHED_NONE )
					pNewSchedule = GetScheduleOfType( m_failSchedule );
				else
					pNewSchedule = GetScheduleOfType( SCHED_FAIL );
				// schedule was invalid because the current task failed to start or complete
				ALERT ( at_aiconsole, "Schedule Failed at %d!\n", m_iScheduleIndex );
				ChangeSchedule( pNewSchedule );
			}
			else
			{
				SetState( m_IdealMonsterState );
				if ( m_MonsterState == MONSTERSTATE_SCRIPT || m_MonsterState == MONSTERSTATE_DEAD )
				{
					pNewSchedule = CBaseMonster::GetSchedule();
				}
				else
					pNewSchedule = GetSchedule();
				ChangeSchedule( pNewSchedule );
			}
		}

		if ( m_iTaskStatus == TASKSTATUS_NEW )
		{	
			Task_t *pTask = GetTask();
			ASSERT( pTask != NULL );
			TaskBegin();
			StartTask( pTask );
		}

		// UNDONE: Twice?!!!
		if ( m_Activity != m_IdealActivity )
		{
			SetActivity ( m_IdealActivity );
		}
		
		if ( !TaskIsComplete() && m_iTaskStatus != TASKSTATUS_NEW )
			break;
	}

	if ( TaskIsRunning() )
	{
		Task_t *pTask = GetTask();
		ASSERT( pTask != NULL );
		RunTask( pTask );
	}

	// UNDONE: We have to do this so that we have an animation set to blend to if RunTask changes the animation
	// RunTask() will always change animations at the end of a script!
	// Don't do this twice
	if ( m_Activity != m_IdealActivity )
	{
		SetActivity ( m_IdealActivity );
	}
}
Beispiel #3
0
void CTalkMonster :: RunTask( const Task_t& task )
{
	switch( task.iTask )
	{
	case TASK_TLK_CLIENT_STARE:
	case TASK_TLK_LOOK_AT_CLIENT:
		{
			edict_t *pPlayer;

			// track head to the client for a while.
			if( m_MonsterState == MONSTERSTATE_IDLE &&
				!IsMoving() &&
				!IsTalking() )
			{
				// Get edict for one player
				pPlayer = g_engfuncs.pfnPEntityOfEntIndex( 1 );

				if( pPlayer )
				{
					IdleHeadTurn( pPlayer->v.origin );
				}
			}
			else
			{
				// started moving or talking
				TaskFail();
				return;
			}

			if( pPlayer )
			{
				if( task.iTask == TASK_TLK_CLIENT_STARE )
				{
					// fail out if the player looks away or moves away.
					if( ( pPlayer->v.origin - GetAbsOrigin() ).Length2D() > TLK_STARE_DIST )
					{
						// player moved away.
						TaskFail();
					}

					UTIL_MakeVectors( pPlayer->v.angles );
					if( UTIL_DotPoints( pPlayer->v.origin, GetAbsOrigin(), gpGlobals->v_forward ) < m_flFieldOfView )
					{
						// player looked away
						TaskFail();
					}
				}
			}
			else
			{
				TaskFail();
			}

			if( gpGlobals->time > m_flWaitFinished )
			{
				TaskComplete();
			}
			break;
		}

	case TASK_FACE_PLAYER:
		{
			// Get edict for one player
			edict_t *pPlayer = g_engfuncs.pfnPEntityOfEntIndex( 1 );

			if( pPlayer )
			{
				MakeIdealYaw( pPlayer->v.origin );
				ChangeYaw( GetYawSpeed() );
				IdleHeadTurn( pPlayer->v.origin );
				if( gpGlobals->time > m_flWaitFinished && FlYawDiff() < 10 )
				{
					TaskComplete();
				}
			}
			else
			{
				TaskFail();
			}

			break;
		}

	case TASK_TLK_EYECONTACT:
		{
			if( !IsMoving() && IsTalking() && m_hTalkTarget != NULL )
			{
				// ALERT( at_console, "waiting %f\n", m_flStopTalkTime - gpGlobals->time );
				IdleHeadTurn( m_hTalkTarget->GetAbsOrigin() );
			}
			else
			{
				TaskComplete();
			}
			break;
		}

	case TASK_WALK_PATH_FOR_UNITS:
		{
			float distance;

			distance = (m_vecLastPosition - GetAbsOrigin()).Length2D();

			// Walk path until far enough away
			if ( distance > task.flData || MovementIsComplete() )
			{
				TaskComplete();
				RouteClear();		// Stop moving
			}

			break;
		}
		
	case TASK_WAIT_FOR_MOVEMENT:
		{
			if( IsTalking() && m_hTalkTarget != NULL )
			{
				// ALERT(at_console, "walking, talking\n");
				IdleHeadTurn( m_hTalkTarget->GetAbsOrigin() );
			}
			else
			{
				IdleHeadTurn( GetAbsOrigin() );
				// override so that during walk, a scientist may talk and greet player
				FIdleHello();
				if( RANDOM_LONG( 0, m_nSpeak * 20 ) == 0 )
				{
					FIdleSpeak();
				}
			}

			CBaseMonster::RunTask( task );
			if( TaskIsComplete() )
				IdleHeadTurn( GetAbsOrigin() );
			break;
		}

	default:
		{
			if( IsTalking() && m_hTalkTarget != NULL )
			{
				IdleHeadTurn( m_hTalkTarget->GetAbsOrigin() );
			}
			else
			{
				SetBoneController( 0, 0 );
			}
			CBaseMonster::RunTask( task );

			break;
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flInterval - 
//			 - 
//			*pTraceResult - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::AutoMovement( float flInterval, CBaseEntity *pTarget, AIMoveTrace_t *pTraceResult )
{
	bool ignored;
	Vector newPos;
	QAngle newAngles;

	if (flInterval <= 0.0)
		return true;

	m_ScheduleState.bTaskRanAutomovement = true;

	if (GetIntervalMovement( flInterval, ignored, newPos, newAngles ))
	{
		// DevMsg( "%.2f : (%.1f) %.1f %.1f %.1f\n", gpGlobals->curtime, (newPos - GetLocalOrigin()).Length(), newPos.x, newPos.y, newAngles.y );
	
		if ( m_hCine )
		{
			m_hCine->ModifyScriptedAutoMovement( &newPos );
		}

		if (GetMoveType() == MOVETYPE_STEP)
		{
			if (!(GetFlags() & FL_FLY))
			{
				if ( !pTarget )
				{
					pTarget = GetNavTargetEntity();
				}

				// allow NPCs to adjust the automatic movement
				if ( ModifyAutoMovement( newPos ) )
				{
					// Set our motor's speed here
					Vector vecOriginalPosition = GetAbsOrigin();
					bool bResult = false;
					if (!TaskIsComplete())
					{
						bResult = ( GetMotor()->MoveGroundStep( newPos, pTarget, newAngles.y, false, true, pTraceResult ) == AIM_SUCCESS );
					}

					Vector change = GetAbsOrigin() - vecOriginalPosition;
					if (flInterval != 0)
					{
						change /= flInterval;
					}

					GetMotor()->SetMoveVel(change);

					return bResult;
				}

				return ( GetMotor()->MoveGroundStep( newPos, pTarget, newAngles.y, false, true, pTraceResult ) == AIM_SUCCESS );
			}
			else
			{
				// FIXME: here's no direct interface to a fly motor, plus this needs to support a state where going through the world is okay.
				// FIXME: add callbacks into the script system for validation
				// FIXME: add function on scripts to force only legal movements
				// FIXME: GetIntervalMovement deals in Local space, nor global.  Currently now way to communicate that through these interfaces.
				SetLocalOrigin( newPos );
				SetLocalAngles( newAngles );
				return true;
			}
		}
		else if (GetMoveType() == MOVETYPE_FLY)
		{
			Vector dist = newPos - GetLocalOrigin();

			VectorScale( dist, 1.0 / flInterval, dist );

			SetLocalVelocity( dist );
			return true;
		}
	}
	return false;
}
void CHL1NPCTalker::RunTask( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
		case TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS:
			{
				float distance;

				distance = (m_vecLastPosition - GetLocalOrigin()).Length2D();

				// Walk path until far enough away
				if ( distance > pTask->flTaskData || 
					 GetNavigator()->GetGoalType() == GOALTYPE_NONE )
				{
					TaskComplete();
					GetNavigator()->ClearGoal();		// Stop moving
				}
				break;
			}



		case TASK_TALKER_CLIENT_STARE:
		case TASK_TALKER_LOOK_AT_CLIENT:
		{
			CBasePlayer *pPlayer;
			
			pPlayer = (CBasePlayer *)CBaseEntity::Instance( engine->PEntityOfEntIndex( 1 ) );

			// track head to the client for a while.
			if ( m_NPCState == NPC_STATE_IDLE		&& 
				 !IsMoving()								&&
				 !GetExpresser()->IsSpeaking() )
			{
			
				if ( pPlayer )
				{
					IdleHeadTurn( pPlayer->GetAbsOrigin() );
				}
			}
			else
			{
				// started moving or talking
				TaskFail( "moved away" );
				return;
			}

			if ( pTask->iTask == TASK_TALKER_CLIENT_STARE )
			{
				// fail out if the player looks away or moves away.
				if ( ( pPlayer->GetAbsOrigin() - GetAbsOrigin() ).Length2D() > TLK_STARE_DIST )
				{
					// player moved away.
					TaskFail( NO_TASK_FAILURE );
				}

				Vector vForward;
				AngleVectors( GetAbsAngles(), &vForward );
				if ( UTIL_DotPoints( pPlayer->GetAbsOrigin(), GetAbsOrigin(), vForward ) < m_flFieldOfView )
				{
					// player looked away
					TaskFail( "looked away" );
				}
			}

			if ( gpGlobals->curtime > m_flWaitFinished )
			{
				TaskComplete( NO_TASK_FAILURE );
			}

			break;
		}

		case TASK_WAIT_FOR_MOVEMENT:
		{
			if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
			{
				// ALERT(at_console, "walking, talking\n");
				IdleHeadTurn( GetSpeechTarget()->GetAbsOrigin(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
			}
			else if ( GetEnemy() )
			{
				IdleHeadTurn( GetEnemy()->GetAbsOrigin() );
			}
			else
				IdleHeadTurn( vec3_origin );

			BaseClass::RunTask( pTask );

			if ( TaskIsComplete() )
				 IdleHeadTurn( vec3_origin );

			break;
		}

		case TASK_FACE_PLAYER:
		{
			CBasePlayer *pPlayer;
			
			pPlayer = (CBasePlayer *)CBaseEntity::Instance( engine->PEntityOfEntIndex( 1 ) );

			if ( pPlayer )
			{
				//GetMotor()->SetIdealYaw( pPlayer->GetAbsOrigin() );
				IdleHeadTurn( pPlayer->GetAbsOrigin() );
				if ( gpGlobals->curtime > m_flWaitFinished && GetMotor()->DeltaIdealYaw() < 10 )
				{
					TaskComplete();
				}
			}
			else
			{
				TaskFail( FAIL_NO_PLAYER );
			}

			break;
		}

		case TASK_TALKER_EYECONTACT:
		{
			if (!IsMoving() && GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
			{
				// ALERT( at_console, "waiting %f\n", m_flStopTalkTime - gpGlobals->time );
				IdleHeadTurn( GetSpeechTarget()->GetAbsOrigin(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
			}
			
			BaseClass::RunTask( pTask );
			
			break;

		}

				
		default:
		{
		
			if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
			{
				IdleHeadTurn( GetSpeechTarget()->GetAbsOrigin(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
			}
			else if ( GetFollowTarget() )
			{
				IdleHeadTurn( GetFollowTarget()->GetAbsOrigin() );
			}
			else if ( GetEnemy() && m_NPCState == NPC_STATE_COMBAT )
			{
				IdleHeadTurn( GetEnemy()->GetAbsOrigin() );
			}
			else
			{
				IdleHeadTurn( vec3_origin );
			}

			BaseClass::RunTask( pTask );
			break;
		}
	}
}