// Gets called each time the mind is thinking
void SearchingState::Think( idAI *owner ) {
	UpdateAlertLevel();
	// Ensure we are in the correct alert level
	if( !CheckAlertLevel( owner ) ) {
		return;
	}
	// grayman #3063 - move up so it gets done each time,
	// regardless of what state the hiding spot search is in.
	// Let the AI check its senses
	owner->PerformVisualScan();
	if( owner->GetMoveType() == MOVETYPE_SIT
			|| owner->GetMoveType() == MOVETYPE_SLEEP
			|| owner->GetMoveType() == MOVETYPE_SIT_DOWN
			|| owner->GetMoveType() == MOVETYPE_LAY_DOWN ) {
		owner->GetUp();
		return;
	}
	Memory &memory = owner->GetMemory();
	owner->MarkEventAsSearched( memory.currentSearchEventID ); // grayman #3424
	// grayman #3520 - look at alert spots
	if( owner->m_lookAtAlertSpot ) {
		owner->m_lookAtAlertSpot = false;
		idVec3 alertSpot = owner->m_lookAtPos;
		if( alertSpot.x != idMath::INFINITY ) { // grayman #3438
			if( !owner->CheckFOV( alertSpot ) ) {
				// Search spot is not within FOV, turn towards the position
				owner->TurnToward( alertSpot );
				owner->Event_LookAtPosition( alertSpot, 2.0f );
			} else {
				owner->Event_LookAtPosition( alertSpot, 2.0f );
			}
		}
		owner->m_lookAtPos = idVec3( idMath::INFINITY, idMath::INFINITY, idMath::INFINITY );
	}
	// grayman #3200 - if asked to restart the hiding spot search, don't continue with the current hiding spot search
	if( memory.restartSearchForHidingSpots ) {
		// We should restart the search (probably due to a new incoming stimulus)
		// Setup a new hiding spot search
		StartNewHidingSpotSearch( owner );
	} else if( !memory.hidingSpotSearchDone ) { // Do we have an ongoing hiding spot search?
		// Let the hiding spot search do its task
		PerformHidingSpotSearch( owner );
		// Let the AI check its senses
		//		owner->PerformVisualScan(); // grayman #3063 - moved to front
		/*
				// angua: commented this out, problems with getting up from sitting
				idStr waitState(owner->WaitState());
				if (waitState.IsEmpty())
				{
					// Waitstate is not matching, this means that the animation
					// can be started.
					owner->SetAnimState(ANIMCHANNEL_TORSO, "Torso_LookAround", 5);
					//owner->SetAnimState(ANIMCHANNEL_LEGS, "Legs_LookAround", 5);

					// Set the waitstate, this gets cleared by
					// the script function when the animation is done.
					owner->SetWaitState("look_around");
				}
		*/
	}
	// Is a hiding spot search in progress?
	else if( !memory.hidingSpotInvestigationInProgress ) {
		// Spot search and investigation done, what next?
		// Have run out of hiding spots?
		if( memory.noMoreHidingSpots ) {
			if( gameLocal.time >= memory.nextTime2GenRandomSpot ) {
				memory.nextTime2GenRandomSpot = gameLocal.time + DELAY_RANDOM_SPOT_GEN * ( 1 + ( gameLocal.random.RandomFloat() - 0.5 ) / 3 );
				// grayman #2422
				// Generate a random search point, but make sure it's inside an AAS area
				// and that it's also inside the search volume.
				idVec3 p;		// random point
				int areaNum;	// p's area number
				idVec3 searchSize = owner->m_searchLimits.GetSize();
				idVec3 searchCenter = owner->m_searchLimits.GetCenter();
				//gameRenderWorld->DebugBox(colorWhite, idBox(owner->m_searchLimits), MS2SEC(memory.nextTime2GenRandomSpot - gameLocal.time));
				bool validPoint = false;
				for( int i = 0 ; i < 6 ; i++ ) {
					p = searchCenter;
					p.x += gameLocal.random.RandomFloat() * ( searchSize.x ) - searchSize.x / 2;
					p.y += gameLocal.random.RandomFloat() * ( searchSize.y ) - searchSize.y / 2;
					p.z += gameLocal.random.RandomFloat() * ( searchSize.z ) - searchSize.z / 2;
					//p.z += gameLocal.random.RandomFloat()*(searchSize.z/2) - searchSize.z/4;
					areaNum = owner->PointReachableAreaNum( p );
					if( areaNum == 0 ) {
						//gameRenderWorld->DebugArrow(colorRed, owner->GetEyePosition(), p, 1, MS2SEC(memory.nextTime2GenRandomSpot - gameLocal.time));
						continue;
					}
					owner->GetAAS()->PushPointIntoAreaNum( areaNum, p ); // if this point is outside this area, it will be moved to one of the area's edges
					if( !owner->m_searchLimits.ContainsPoint( p ) ) {
						//gameRenderWorld->DebugArrow(colorPink, owner->GetEyePosition(), p, 1, MS2SEC(memory.nextTime2GenRandomSpot - gameLocal.time));
						continue;
					}
					//gameRenderWorld->DebugArrow(colorGreen, owner->GetEyePosition(), p, 1, MS2SEC(memory.nextTime2GenRandomSpot - gameLocal.time));
					validPoint = true;
					break;
				}
				if( validPoint ) {
					// grayman #2422 - the point chosen
					memory.currentSearchSpot = p;
					// Choose to investigate spots closely on a random basis
					// grayman #2801 - and only if you weren't hit by a projectile
					memory.investigateStimulusLocationClosely = ( ( gameLocal.random.RandomFloat() < 0.3f ) && ( memory.alertType != EAlertTypeHitByProjectile ) );
					owner->actionSubsystem->PushTask( TaskPtr( InvestigateSpotTask::CreateInstance() ) );
					//gameRenderWorld->DebugArrow(colorGreen, owner->GetEyePosition(), memory.currentSearchSpot, 1, 500);
					// Set the flag to TRUE, so that the sensory scan can be performed
					memory.hidingSpotInvestigationInProgress = true;
				}
				if( !validPoint ) { // no valid random point found
					// Stop moving, the algorithm will choose another spot the next round
					owner->StopMove( MOVE_STATUS_DONE );
					memory.StopReacting(); // grayman #3559
					// grayman #2422 - at least turn toward and look at the last invalid point some of the time
					// grayman #3492 - do it every time
					//if ( gameLocal.random.RandomFloat() < 0.5 )
					//{
					p.z += 60; // look up a bit, to simulate searching for the player's head
					if( !owner->CheckFOV( p ) ) {
						owner->TurnToward( p );
					}
					owner->Event_LookAtPosition( p, MS2SEC( memory.nextTime2GenRandomSpot - gameLocal.time + 100 ) );
					//gameRenderWorld->DebugArrow(colorPink, owner->GetEyePosition(), p, 1, MS2SEC(memory.nextTime2GenRandomSpot - gameLocal.time + 100));
					//}
				}
			}
		}
		// We should have more hiding spots, try to get the next one
		else if( !ChooseNextHidingSpotToSearch( owner ) ) {
			// No more hiding spots to search
			DM_LOG( LC_AI, LT_INFO )LOGSTRING( "No more hiding spots!\r" );
			// Stop moving, the algorithm will choose another spot the next round
			owner->StopMove( MOVE_STATUS_DONE );
			memory.StopReacting(); // grayman #3559
		} else {
			// ChooseNextHidingSpot returned TRUE, so we have memory.currentSearchSpot set
			//gameRenderWorld->DebugArrow(colorBlue, owner->GetEyePosition(), memory.currentSearchSpot, 1, 2000);
			// Delegate the spot investigation to a new task, this will take the correct action.
			owner->actionSubsystem->PushTask( InvestigateSpotTask::CreateInstance() );
			// Prevent falling into the same hole twice
			memory.hidingSpotInvestigationInProgress = true;
		}
	}
	/* grayman #3200 - moved up
		else if (memory.restartSearchForHidingSpots)
		{
			// We should restart the search (probably due to a new incoming stimulus)
			// Setup a new hiding spot search
			StartNewHidingSpotSearch(owner);
		}

		else // grayman #3063 - moved to front
		{
			// Move to Hiding spot is ongoing, do additional sensory tasks here

			// Let the AI check its senses
			owner->PerformVisualScan();
		}
	 */
}
void SearchingState::Init( idAI *owner ) {
	// Init base class first
	State::Init( owner );
	DM_LOG( LC_AI, LT_INFO )LOGSTRING( "SearchingState initialised.\r" );
	assert( owner );
	// Ensure we are in the correct alert level
	if( !CheckAlertLevel( owner ) ) {
		return;
	}
	if( owner->GetMoveType() == MOVETYPE_SIT || owner->GetMoveType() == MOVETYPE_SLEEP ) {
		owner->GetUp();
	}
	// Shortcut reference
	Memory &memory = owner->GetMemory();
	float alertTime = owner->atime3 + owner->atime3_fuzzyness * ( gameLocal.random.RandomFloat() - 0.5 );
	_alertLevelDecreaseRate = ( owner->thresh_4 - owner->thresh_3 ) / alertTime;
	if( owner->AlertIndexIncreased() || memory.mandatory ) { // grayman #3331
		// Setup a new hiding spot search
		StartNewHidingSpotSearch( owner );
	}
	if( owner->AlertIndexIncreased() ) {
		// grayman #3423 - when the alert level is ascending, kill the repeated bark task
		owner->commSubsystem->ClearTasks();
		// Play bark if alert level is ascending
		// grayman #3496 - enough time passed since last alert bark?
		if( gameLocal.time >= memory.lastTimeAlertBark + MIN_TIME_BETWEEN_ALERT_BARKS ) {
			idStr bark;
			if( ( memory.alertedDueToCommunication == false ) && ( ( memory.alertType == EAlertTypeSuspicious ) || ( memory.alertType == EAlertTypeEnemy ) ) ) {
				bool friendsNear = ( ( MS2SEC( gameLocal.time - memory.lastTimeFriendlyAISeen ) ) <= MAX_FRIEND_SIGHTING_SECONDS_FOR_ACCOMPANIED_ALERT_BARK );
				if( ( memory.alertClass == EAlertVisual_1 ) ||
						( memory.alertClass == EAlertVisual_2 ) ||    // grayman #2603, #3424
						// (memory.alertClass == EAlertVisual_3) ) || // grayman #3472 - no longer needed
						( memory.alertClass == EAlertVisual_4 ) ) {   // grayman #3498
					if( friendsNear ) {
						bark = "snd_alert3sc";
					} else {
						bark = "snd_alert3s";
					}
				} else if( memory.alertClass == EAlertAudio ) {
					if( friendsNear ) {
						bark = "snd_alert3hc";
					} else {
						bark = "snd_alert3h";
					}
				} else if( friendsNear ) {
					bark = "snd_alert3c";
				} else {
					bark = "snd_alert3";
				}
				// Allocate a SingleBarkTask, set the sound and enqueue it
				owner->commSubsystem->AddCommTask( CommunicationTaskPtr( new SingleBarkTask( bark ) ) );
				memory.lastTimeAlertBark = gameLocal.time; // grayman #3496
				if( cv_ai_debug_transition_barks.GetBool() ) {
					gameLocal.Printf( "%d: %s rises to Searching state, barks '%s'\n", gameLocal.time, owner->GetName(), bark.c_str() );
				}
			}
		} else {
			if( cv_ai_debug_transition_barks.GetBool() ) {
				gameLocal.Printf( "%d: %s rises to Searching state, can't bark 'snd_alert3{s/sc/h/hc/c}' yet\n", gameLocal.time, owner->GetName() );
			}
		}
	} else if( memory.alertType == EAlertTypeEnemy ) {
		// reduce the alert type, so we can react to other alert types (such as a dead person)
		memory.alertType = EAlertTypeSuspicious;
	}
	// grayman #3472 - When ascending, set up a repeated bark
	if( owner->AlertIndexIncreased() ) {
		owner->commSubsystem->AddSilence( 5000 + gameLocal.random.RandomInt( 3000 ) ); // grayman #3424
		// This will hold the message to be delivered with the bark
		CommMessagePtr message( new CommMessage(
									CommMessage::DetectedEnemy_CommType,
									owner, NULL,// from this AI to anyone
									NULL,
									idVec3( idMath::INFINITY, idMath::INFINITY, idMath::INFINITY ),
									0
								) );
		int minTime = SEC2MS( owner->spawnArgs.GetFloat( "searchbark_delay_min", "10" ) );
		int maxTime = SEC2MS( owner->spawnArgs.GetFloat( "searchbark_delay_max", "15" ) );
		owner->commSubsystem->AddCommTask( CommunicationTaskPtr( new RepeatedBarkTask( "snd_state3", minTime, maxTime, message ) ) );
	} else { // descending
		// Allow repeated barks from Agitated Searching to continue.
	}
	if( !owner->HasSeenEvidence() ) {
		owner->SheathWeapon();
		owner->UpdateAttachmentContents( false );
	} else {
		// Let the AI update their weapons (make them solid)
		owner->UpdateAttachmentContents( true );
	}
}
void AgitatedSearchingState::Init(idAI* owner)
{
	// Init base class first (note: we're not calling SearchingState::Init() on purpose here)
	State::Init(owner);

	DM_LOG(LC_AI, LT_INFO)LOGSTRING("AgitatedSearchingState initialised.\r");
	assert(owner);

	// Shortcut reference
	Memory& memory = owner->GetMemory();

	memory.leaveAlertState = false;

	// Ensure we are in the correct alert level
	if ( !CheckAlertLevel(owner) )
	{
		return;
	}

	// grayman #3496 - note that we spent time in Agitated Search

	memory.agitatedSearched = true;

	CalculateAlertDecreaseRate(owner);
	
	if (owner->GetMoveType() == MOVETYPE_SIT || owner->GetMoveType() == MOVETYPE_SLEEP)
	{
		owner->GetUp();
	}

	// Set up a new hiding spot search if not already assigned to one
	if (owner->m_searchID <= 0)
	{
		if (!StartNewHidingSpotSearch(owner)) // grayman #3857 - AI gets his assignment
		{
			// grayman - this section can't run because it causes
			// the stealth score to rise dramatically during player sightings
			//owner->SetAlertLevel(owner->thresh_3 - 0.1); // failed to create a search, so drop down to Suspicious mode
			//owner->GetMind()->EndState();
			//return;
		}
	}

	// kill the repeated and single bark tasks
	owner->commSubsystem->ClearTasks(); // grayman #3182
	memory.repeatedBarkState = ERBS_NULL; // grayman #3857

	if (owner->AlertIndexIncreased())
	{
		// grayman #3496 - enough time passed since last alert bark?
		// grayman #3857 - enough time passed since last visual stim bark?
		if ( ( gameLocal.time >= memory.lastTimeAlertBark + MIN_TIME_BETWEEN_ALERT_BARKS ) &&
			 ( gameLocal.time >= memory.lastTimeVisualStimBark + MIN_TIME_BETWEEN_ALERT_BARKS ) )
		{
			idStr soundName = "";
			if ( ( memory.alertedDueToCommunication == false ) && ( ( memory.alertType == EAlertTypeSuspicious ) || ( memory.alertType == EAlertTypeEnemy ) || ( memory.alertType == EAlertTypeFailedKO ) ) )
			{
				if (owner->HasSeenEvidence())
				{
					soundName = "snd_alert4";
				}
				else
				{
					soundName = "snd_alert4NoEvidence";
				}

				CommMessagePtr message = CommMessagePtr(new CommMessage(
					CommMessage::DetectedSomethingSuspicious_CommType, 
					owner, NULL, // from this AI to anyone
					NULL,
					memory.alertPos,
					memory.currentSearchEventID // grayman #3438
				));

				owner->commSubsystem->AddCommTask(CommunicationTaskPtr(new SingleBarkTask(soundName,message)));

				memory.lastTimeAlertBark = gameLocal.time; // grayman #3496

				if (cv_ai_debug_transition_barks.GetBool())
				{
					gameLocal.Printf("%d: %s rises to Agitated Searching state, barks '%s'\n",gameLocal.time,owner->GetName(),soundName.c_str());
				}
			}
			else if ( memory.respondingToSomethingSuspiciousMsg ) // grayman #3857
			{
				soundName = "snd_helpSearch";

				// Allocate a SingleBarkTask, set the sound and enqueue it
				owner->commSubsystem->AddCommTask(CommunicationTaskPtr(new SingleBarkTask(soundName)));

				memory.lastTimeAlertBark = gameLocal.time; // grayman #3496

				if (cv_ai_debug_transition_barks.GetBool())
				{
					gameLocal.Printf("%d: %s rises to Searching state, barks '%s'\n",gameLocal.time,owner->GetName(),soundName.c_str());
				}
			}
		}
		else
		{
			if (cv_ai_debug_transition_barks.GetBool())
			{
				gameLocal.Printf("%d: %s rises to Agitated Searching state, can't bark 'snd_alert4{NoEvidence}' yet\n",gameLocal.time,owner->GetName());
			}
		}
	}

	owner->commSubsystem->AddSilence(5000 + gameLocal.random.RandomInt(3000)); // grayman #3424

	SetRepeatedBark(owner); // grayman #3857
	
	DrawWeapon(owner); // grayman #3507

	// Let the AI update their weapons (make them solid)
	owner->UpdateAttachmentContents(true);

	// grayman #3857 - no idle search anims in this state
	owner->actionSubsystem->ClearTasks();
}