void SingleBarkTask::Restore( idRestoreGame *savefile ) {
	CommunicationTask::Restore( savefile );
	savefile->ReadInt( _startDelay );
	savefile->ReadBool( _allowDuringAnim ); // grayman #3182
	savefile->ReadInt( _endTime );
	bool hasMessage;
	savefile->ReadBool( hasMessage );
	if( hasMessage ) {
		_message = CommMessagePtr( new CommMessage );
		_message->Restore( savefile );
	} else {
		_message = CommMessagePtr();
	}
}
Example #2
0
void PainState::Init( idAI *owner ) {
	// Init base class first
	State::Init( owner );
	DM_LOG( LC_AI, LT_INFO )LOGSTRING( "PainState initialised.\r" );
	assert( owner );
	// grayman #3424 - if already playing a pain anim, skip this one
	if( idStr( owner->WaitState( ANIMCHANNEL_TORSO ) ) == "pain" ) {
		return;
	}
	Memory &memory = owner->GetMemory();
	// Play the animation
	owner->SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 4 );
	owner->SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", 4 );
	owner->SetWaitState( ANIMCHANNEL_TORSO, "pain" );
	owner->SetWaitState( ANIMCHANNEL_LEGS, "pain" );
	// Set end time
	_stateEndTime = gameLocal.time + 5000;
	// grayman #3140 - if drowning, skip issuing a message. The drowning
	// sound effect is handled in idActor::Damage().
	if( memory.causeOfPain != EPC_Drown ) {
		memory.alertPos = owner->GetPhysics()->GetOrigin();
		// Do a single bark and assemble an AI message
		CommMessagePtr message = CommMessagePtr( new CommMessage(
									 CommMessage::DetectedEnemy_CommType,
									 owner, NULL, // from this AI to anyone
									 NULL,
									 memory.alertPos,
									 0
								 ) );
		owner->commSubsystem->AddCommTask( CommunicationTaskPtr( new SingleBarkTask( "snd_pain_large", message ) ) );
		if( cv_ai_debug_transition_barks.GetBool() ) {
			gameLocal.Printf( "%d: %s is hurt, barks 'snd_pain_large'\n", gameLocal.time, owner->GetName() );
		}
	}
}
Example #3
0
// Gets called each time the mind is thinking
void PainState::Think( idAI *owner ) {
	if( ( gameLocal.time >= _stateEndTime ) ||
			( idStr( owner->WaitState( ANIMCHANNEL_TORSO ) ) != "pain" ) ) {
		bool willBark = ( owner->AI_AlertLevel < owner->thresh_5 ); // don't bark a response if in combat
		bool willFlee = ( ( ( owner->GetNumMeleeWeapons() == 0 ) && ( owner->GetNumRangedWeapons() == 0 ) ) ||
						  owner->spawnArgs.GetBool( "is_civilian", "0" ) );
		// grayman #3140 - what caused this pain?
		Memory &memory = owner->GetMemory();
		if( memory.causeOfPain == EPC_Drown ) {
			// no bark and no fleeing if drowning
			willBark = false;
			willFlee = false;
		} else if( memory.causeOfPain == EPC_Projectile ) {
			// If fleeing, snd_taking_fire will be played at the start of the flee state,
			// so there's no reason to play it here.
			// If not fleeing, play snd_taking_fire here.
			willBark = !willFlee;
			memory.posEnemySeen = owner->GetPhysics()->GetOrigin();
		}
		if( willBark ) {
			// grayman #3140 - Emit the snd_taking_fire bark
			// This will hold the message to be delivered with the bark
			CommMessagePtr message;
			message = CommMessagePtr( new CommMessage(
										  CommMessage::RequestForHelp_CommType,
										  owner, NULL, // from this AI to anyone
										  NULL,
										  owner->GetPhysics()->GetOrigin(),
										  0
									  ) );
			owner->commSubsystem->AddCommTask( CommunicationTaskPtr( new SingleBarkTask( "snd_taking_fire", message ) ) );
			if( cv_ai_debug_transition_barks.GetBool() ) {
				gameLocal.Printf( "%d: %s is hurt, barks 'snd_taking_fire'\n", gameLocal.time, owner->GetName() );
			}
		}
		if( willFlee && memory.fleeingDone ) { // grayman #3331 - civilians and unarmed AI should flee // grayman #3847 - already fleeing?
			if( owner->AI_AlertLevel >= owner->thresh_5 ) { // grayman #3847
				owner->fleeingEvent = false; // I'm fleeing an enemy
				owner->fleeingFromPerson = owner->GetEnemy(); // grayman #3847
			} else {
				owner->fleeingEvent = true; // I'm fleeing the scene, not fleeing an enemy
				owner->fleeingFromPerson = NULL; // grayman #3847
			}
			owner->fleeingFrom = owner->GetPhysics()->GetOrigin(); // grayman #3848
			owner->emitFleeBarks = true; // grayman #3474
			if( memory.fleeingDone ) { // grayman #3847 - only flee if not already fleeing
				owner->GetMind()->SwitchState( STATE_FLEE );
			}
			return;
		}
		// End this state
		owner->GetMind()->EndState();
	}
}
Example #4
0
void RepeatedBarkTask::Restore(idRestoreGame* savefile)
{
	CommunicationTask::Restore(savefile);

	savefile->ReadInt(_barkRepeatIntervalMin);
	savefile->ReadInt(_barkRepeatIntervalMax);
	savefile->ReadInt(_nextBarkTime);
	savefile->ReadBool(_prevBarkDone); // grayman #3182

	bool hasMessage;
	savefile->ReadBool(hasMessage);
	if (hasMessage)
	{
		_message = CommMessagePtr(new CommMessage);
		_message->Restore(savefile);
	}
	else
	{
		_message = CommMessagePtr();
	}
}
Example #5
0
// Gets called each time the mind is thinking
void CombatState::Think(idAI* owner)
{
	// Do we have an expiry date?
	if (_endTime > 0)
	{
		if (gameLocal.time >= _endTime)
		{
			owner->GetMind()->EndState();
		}

		return;
	}

	// Ensure we are in the correct alert level
	if (!CheckAlertLevel(owner))
	{
//		owner->GetMind()->EndState(); // grayman #3182 - already done in CheckAlertLevel()
		return;
	}

	// grayman #3331 - make sure you're still fighting the same enemy.
	// grayman #3355 - fight the closest enemy
	idActor* enemy = _enemy.GetEntity();
	idActor* newEnemy = owner->GetEnemy();

	if ( enemy )
	{
		if ( newEnemy && ( newEnemy != enemy ) )
		{
			idVec3 ownerOrigin = owner->GetPhysics()->GetOrigin();
			float dist2EnemySqr = ( enemy->GetPhysics()->GetOrigin() - ownerOrigin ).LengthSqr();
			float dist2NewEnemySqr = ( newEnemy->GetPhysics()->GetOrigin() - ownerOrigin ).LengthSqr();
			if ( dist2NewEnemySqr < dist2EnemySqr )
			{
				owner->GetMind()->EndState();
				return; // state has ended
			}
		}
	}
	else
	{
		enemy = newEnemy;
	}

	if (!CheckEnemyStatus(enemy, owner))
	{
		owner->GetMind()->EndState();
		return; // state has ended
	}

	// grayman #3520 - don't look toward new alerts
	if ( owner->m_lookAtAlertSpot )
	{
		owner->m_lookAtAlertSpot = false;
		owner->m_lookAtPos = idVec3(idMath::INFINITY,idMath::INFINITY,idMath::INFINITY);
	}

	// angua: look at enemy
	owner->Event_LookAtPosition(enemy->GetEyePosition(), gameLocal.msec);

	Memory& memory = owner->GetMemory();

	idVec3 vec2Enemy = enemy->GetPhysics()->GetOrigin() - owner->GetPhysics()->GetOrigin();
	float dist2Enemy = vec2Enemy.LengthFast();

	// grayman #3331 - need to take vertical separation into account. It's possible to have the origins
	// close enough to be w/in the melee zone, but still be unable to hit the enemy.

	bool inMeleeRange = ( dist2Enemy <= ( 3 * owner->GetMeleeRange() ) );

	ECombatType newCombatType;
	
	if ( inMeleeRange && !_meleePossible ) // grayman #3355 - can't fight up close
	{
		owner->fleeingEvent = false; // grayman #3356
		owner->emitFleeBarks = false; // grayman #3474
		owner->GetMind()->SwitchState(STATE_FLEE);
		return;
	}

	if ( !inMeleeRange && _rangedPossible )
	{
		newCombatType = COMBAT_RANGED;
	}
	else
	{
		newCombatType = COMBAT_MELEE;
	}

	// Check for situation where you're in the melee zone, yet you're unable to hit
	// the enemy. This can happen if the enemy is above or below you and you can't
	// reach them.

	switch(_combatSubState)
	{
	case EStateReaction:
		{
		if ( gameLocal.time < _reactionEndTime )
		{
			return; // stay in this state until the reaction time expires
		}

		// Check to see if the enemy is still visible.
		// grayman #2816 - Visibility doesn't matter if you're in combat because
		// you bumped into your enemy.

		idEntity* tactEnt = owner->GetTactEnt();
		if ( ( tactEnt == NULL ) || !tactEnt->IsType(idActor::Type) || ( tactEnt != enemy ) || !owner->AI_TACTALERT ) 
		{
			if ( !owner->CanSee(enemy, true) )
			{
				owner->ClearEnemy();
				owner->SetAlertLevel(owner->thresh_5 - 0.1); // reset alert level just under Combat
				owner->GetMind()->EndState();
				return;
			}
		}

		owner->m_ignorePlayer = false; // grayman #3063 - clear flag that prevents mission statistics on player sightings

		// The AI has processed his reaction, and needs to move into combat, or flee.

		_criticalHealth = owner->spawnArgs.GetInt("health_critical", "0");

		// greebo: Check for weapons and flee if ...
		if ( ( !_meleePossible && !_rangedPossible )		 || // ... I'm unarmed
			 ( owner->spawnArgs.GetBool("is_civilian", "0")) || // ... I'm a civilian, and don't fight
			 ( owner->health < _criticalHealth ) )			    // grayman #3140 ... I'm very damaged and can't afford to engage in combat
		{
			owner->fleeingEvent = false; // grayman #3356
			owner->emitFleeBarks = true; // grayman #3474
			owner->GetMind()->SwitchState(STATE_FLEE);
			return;
		}

		memory.stopRelight = true; // grayman #2603 - abort a relight in progress
		memory.stopExaminingRope = true; // grayman #2872 - stop examining a rope
		memory.stopReactingToHit = true; // grayman #2816

		_combatSubState = EStateDoOnce;
		break;
		}

	case EStateDoOnce:
		{
		// Check for sitting or sleeping

		moveType_t moveType = owner->GetMoveType();
		if (   moveType == MOVETYPE_SIT 
			|| moveType == MOVETYPE_SLEEP
			|| moveType == MOVETYPE_SIT_DOWN
			|| moveType == MOVETYPE_LAY_DOWN )
		{
			owner->GetUp(); // okay if called multiple times
			return;
		}

		// grayman #3009 - check for getting up from sitting or sleeping
		if ( moveType == MOVETYPE_GET_UP || 
			 moveType == MOVETYPE_GET_UP_FROM_LYING )
		{
			return;
		}

		// not sitting or sleeping at this point

		// Stop what you're doing
		owner->StopMove(MOVE_STATUS_DONE);
		owner->movementSubsystem->ClearTasks();
		owner->senseSubsystem->ClearTasks();
		owner->actionSubsystem->ClearTasks();

		// Bark

		// This will hold the message to be delivered with the bark, if appropriate
		CommMessagePtr message;
	
		// Only alert the bystanders if we didn't receive the alert by message ourselves
		if (!memory.alertedDueToCommunication)
		{
			message = CommMessagePtr(new CommMessage(
				CommMessage::DetectedEnemy_CommType, 
				owner, NULL, // from this AI to anyone 
				enemy,
				memory.lastEnemyPos,
				0
			));
		}

		// grayman #3496 - This is an alert bark, but it's not subject to the delay
		// between alert barks because the previous bark will end before we get here.
		// It's also the culmination of a journey up from Idle State, and if the AI is
		// going to go after an enemy, this bark needs to be heard.
		// We'll still reset the last alert bark time, though.

		// The communication system plays starting bark

		// grayman #3343 - accommodate different barks for human and non-human enemies

		idPlayer* player(NULL);
		if (enemy->IsType(idPlayer::Type))
		{
			player = static_cast<idPlayer*>(enemy);
		}

		idStr bark = "";

		if (player && player->m_bShoulderingBody)
		{
			bark = "snd_spotted_player_with_body";
		}
		else if ((MS2SEC(gameLocal.time - memory.lastTimeFriendlyAISeen)) <= MAX_FRIEND_SIGHTING_SECONDS_FOR_ACCOMPANIED_ALERT_BARK)
		{
			idStr enemyAiUse = enemy->spawnArgs.GetString("AIUse");
			if ( ( enemyAiUse == AIUSE_MONSTER ) || ( enemyAiUse == AIUSE_UNDEAD ) )
			{
				bark = "snd_to_combat_company_monster";
			}
			else
			{
				bark = "snd_to_combat_company";
			}
		}
		else
		{
			idStr enemyAiUse = enemy->spawnArgs.GetString("AIUse");
			if ( ( enemyAiUse == AIUSE_MONSTER ) || ( enemyAiUse == AIUSE_UNDEAD ) )
			{
				bark = "snd_to_combat_monster";
			}
			else
			{
				bark = "snd_to_combat";
			}
		}

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

		owner->GetMemory().lastTimeAlertBark = gameLocal.time; // grayman #3496

		if (cv_ai_debug_transition_barks.GetBool())
		{
			gameLocal.Printf("%d: %s starts combat, barks '%s'\n",gameLocal.time,owner->GetName(),bark.c_str());
		}

		_justDrewWeapon = false;
		_combatSubState = EStateCheckWeaponState;
		break;
		}

	case EStateCheckWeaponState:
		// check which combat type we should use
		{
		// Can you continue with your current combat type, and not have to switch weapons?

		// Check for case where melee combat has stalled. You're in the melee zone, but you're
		// unable to hit the enemy. Perhaps he's higher or lower than you and you can't reach him.

		if ( !owner->AI_FORWARD && // not moving
			 ( _combatType == COMBAT_MELEE ) && // in melee combat
			 _rangedPossible &&    // ranged combat is possible
			 !owner->TestMelee() ) // I can't hit the enemy
		{
			float        orgZ = owner->GetPhysics()->GetOrigin().z;
			float      height = owner->GetPhysics()->GetBounds().GetSize().z;
			float   enemyOrgZ = enemy->GetPhysics()->GetOrigin().z;
			float enemyHeight = enemy->GetPhysics()->GetBounds().GetSize().z;
			if ( ( (orgZ + height + owner->melee_range_vert) < enemyOrgZ ) || // enemy too high
							     ( (enemyOrgZ + enemyHeight) < orgZ ) ) // enemy too low
			{
				newCombatType = COMBAT_RANGED;
			}
		}

		if ( newCombatType == _combatType )
		{
			// yes - no need to run weapon-switching animations
			_combatSubState = EStateCombatAndChecks;
			return;
		}

		// Do you need to switch a melee or ranged weapon? You might already have one
		// drawn, or you might have none drawn, or you might have to change weapons,
		// or you might be using unarmed attacks, and you don't need a drawn weapon.

		// Check for unarmed combat.

		if ( _unarmedMelee && ( newCombatType == COMBAT_MELEE ) )
		{
			// unarmed combat doesn't need attached weapons
			_combatType = COMBAT_NONE; // clear ranged combat tasks and start melee combat tasks
			_combatSubState = EStateCombatAndChecks;
			return;
		}

		if ( _unarmedRanged && ( newCombatType == COMBAT_RANGED ) )
		{
			// unarmed combat doesn't need attached weapons
			_combatType = COMBAT_NONE; // clear melee combat tasks and start ranged combat tasks
			_combatSubState = EStateCombatAndChecks;
			return;
		}

		// Do you have a drawn weapon?

		if ( owner->GetAttackFlag(COMBAT_MELEE) && ( newCombatType == COMBAT_MELEE ) )
		{
			// melee weapon is already drawn
			_combatSubState = EStateCombatAndChecks;
			return;
		}

		if ( owner->GetAttackFlag(COMBAT_RANGED) && ( newCombatType == COMBAT_RANGED ) )
		{
			// ranged weapon is already drawn
			_combatSubState = EStateCombatAndChecks;
			return;
		}

		// At this point, we know we need to draw a weapon that's not already drawn.
		// See if you need to sheathe a drawn weapon.

		if ( ( ( newCombatType == COMBAT_RANGED ) && owner->GetAttackFlag(COMBAT_MELEE)  ) ||
		     ( ( newCombatType == COMBAT_MELEE  ) && owner->GetAttackFlag(COMBAT_RANGED) ) )
		{
			// switch from one type of weapon to another
			owner->movementSubsystem->ClearTasks();
			owner->actionSubsystem->ClearTasks();
			_combatType = COMBAT_NONE;

			// sheathe current weapon so you can draw the other weapon
			owner->SheathWeapon();
			_waitEndTime = gameLocal.time + 2000; // safety net
			_combatSubState = EStateSheathingWeapon;
			return;
		}

		// No need to sheathe a weapon
		_combatSubState = EStateDrawWeapon;

		break;
		}

	case EStateSheathingWeapon:
		{
		// if you're sheathing a weapon, stay in this state until it's done, or until the timer expires
		// grayman #3355 - check wait state
		if ( ( gameLocal.time < _waitEndTime ) && ( idStr(owner->WaitState()) == "sheath") )
		{
			return;
		}

		_combatSubState = EStateDrawWeapon;

		break;
		}

	case EStateDrawWeapon:
		{
		// grayman #3331 - if you don't already have the correct weapon drawn,
		// draw a ranged weapon if you're far from the enemy, and you have a 
		// ranged weapon, otherwise draw your melee weapon

		bool drawingWeapon = false;

		if ( !inMeleeRange )
		{
			// beyond melee range
			if ( !owner->GetAttackFlag(COMBAT_RANGED) && _rangedPossible )
			{
				owner->DrawWeapon(COMBAT_RANGED);
				drawingWeapon = true;
			}
			else // no ranged weapon
			{
				owner->DrawWeapon(COMBAT_MELEE);
				drawingWeapon = true;
			}
		}
		else // in melee range
		{
			if ( _meleePossible && !owner->GetAttackFlag(COMBAT_MELEE) )
			{
				owner->DrawWeapon(COMBAT_MELEE);
				drawingWeapon = true;
			}
		}

		// grayman #3331 - if this is the first weapon draw, to make sure the weapon is drawn
		// before starting combat, delay some before starting to chase the enemy.
		// The farther away the enemy is, the better the chance that you'll start chasing before your
		// weapon is drawn. If he's close, this gives you time to completely draw your weapon before
		// engaging him. The interesting distance is how far you have to travel to get w/in melee range.

		if ( _needInitialDrawDelay ) // True if this is the first time through, and you don't already have a raised weapon
		{
			int delay = 0;

			if ( drawingWeapon )
			{
				delay = (int)(2064.0f - 20.0f*(dist2Enemy - owner->GetMeleeRange()));
				if ( delay < 0 )
				{
					delay = gameLocal.random.RandomInt(2064);
				}
				_waitEndTime = gameLocal.time + delay;
				_combatSubState = EStateDrawingWeapon;

				// grayman #3563 - safety net when drawing a weapon
				_drawEndTime = gameLocal.time + MAX_DRAW_DURATION;
			}
			else
			{
				_combatSubState = EStateCombatAndChecks;
			}
			_needInitialDrawDelay = false; // No need to do this again
		}
		else
		{
			if ( drawingWeapon )
			{
				_waitEndTime = gameLocal.time;
				_combatSubState = EStateDrawingWeapon;

				// grayman #3563 - safety net when drawing a weapon
				_drawEndTime = gameLocal.time + MAX_DRAW_DURATION;
			}
			else
			{
				_combatSubState = EStateCombatAndChecks;
			}
		}
		break;
		}

	case EStateDrawingWeapon:
		{
		// grayman #3355 - check wait state
		if ( idStr(owner->WaitState()) == "draw" )
		{
			if ( gameLocal.time < _drawEndTime ) // grayman #3563 - check safety net
			{
				return; // wait until weapon is drawn
			}
		}

		if ( gameLocal.time < _waitEndTime )
		{
			return; // wait until timer expires
		}

		// Weapon is now drawn

		_justDrewWeapon = true;
		_combatSubState = EStateCombatAndChecks;

		break;
		}

	case EStateCombatAndChecks:
		{
		// Need to check if a weapon that was just drawn is correct for the zone you're now in, in case
		// you started drawing the correct weapon for one zone, and while it was drawing, you switched
		// to the other zone.
		
		if ( _justDrewWeapon )
		{
			if ( newCombatType == COMBAT_RANGED )
			{
				// beyond melee range
				if ( !owner->GetAttackFlag(COMBAT_RANGED) && _rangedPossible )
				{
					// wrong weapon raised - go back and get the correct one
					_justDrewWeapon = false;
					_combatSubState = EStateCheckWeaponState;
					return;
				}
			}
			else // in melee combat
			{
				if ( !owner->GetAttackFlag(COMBAT_MELEE) && _meleePossible )
				{
					// wrong weapon raised - go back and get the correct one
					_justDrewWeapon = false;
					_combatSubState = EStateCheckWeaponState;
					return;
				}
			}
		}

		_justDrewWeapon = false;
		if ( _combatType == COMBAT_NONE ) // Either combat hasn't been initially set up, or you're switching weapons
		{
			if ( newCombatType == COMBAT_RANGED )
			{
				// Set up ranged combat
				owner->actionSubsystem->PushTask(RangedCombatTask::CreateInstance());
				owner->movementSubsystem->PushTask(ChaseEnemyRangedTask::CreateInstance());
				_combatType = COMBAT_RANGED;
			}
			else
			{
				// Set up melee combat
				ChaseEnemyTaskPtr chaseEnemy = ChaseEnemyTask::CreateInstance();
				chaseEnemy->SetEnemy(enemy);
				owner->movementSubsystem->PushTask(chaseEnemy);

				owner->actionSubsystem->PushTask(MeleeCombatTask::CreateInstance());
				_combatType = COMBAT_MELEE;
			}

			// Let the AI update their weapons (make them nonsolid)
			owner->UpdateAttachmentContents(false);
		}

		// Check the distance to the enemy, the subsystem tasks need it.
		memory.canHitEnemy = owner->CanHitEntity(enemy, _combatType);
		// grayman #3331 - willBeAbleToHitEnemy is only relevant if canHitEnemy is FALSE
		if ( owner->m_bMeleePredictProximity && !memory.canHitEnemy )
		{
			memory.willBeAbleToHitEnemy = owner->WillBeAbleToHitEntity(enemy, _combatType);
		}

		// Check whether the enemy can hit us in the near future
		memory.canBeHitByEnemy = owner->CanBeHitByEntity(enemy, _combatType);

		if ( !owner->AI_ENEMY_VISIBLE && 
			 ( ( ( _combatType == COMBAT_MELEE )  && !memory.canHitEnemy ) || ( _combatType == COMBAT_RANGED) ) )
		{
			// The enemy is not visible, let's keep track of him for a small amount of time
			if (gameLocal.time - memory.lastTimeEnemySeen < MAX_BLIND_CHASE_TIME)
			{
				// Cheat a bit and take the last reachable position as "visible & reachable"
				owner->lastVisibleReachableEnemyPos = owner->lastReachableEnemyPos;
			}
			else if (owner->ReachedPos(owner->lastVisibleReachableEnemyPos, MOVE_TO_POSITION) || 
					( ( gameLocal.time - memory.lastTimeEnemySeen ) > 2 * MAX_BLIND_CHASE_TIME) )
			{
				// BLIND_CHASE_TIME has expired, we have lost the enemy!
				owner->GetMind()->SwitchState(STATE_LOST_TRACK_OF_ENEMY);
				return;
			}
		}

		// Flee if you're damaged and the current melee action is finished
		if ( ( owner->health < _criticalHealth ) && ( owner->m_MeleeStatus.m_ActionState == MELEEACTION_READY ) )
		{
			DM_LOG(LC_AI, LT_INFO)LOGSTRING("I'm badly hurt, I'm afraid, and am fleeing!\r");
			owner->fleeingEvent = false; // grayman #3182
			owner->emitFleeBarks = true; // grayman #3474
			owner->GetMind()->SwitchState(STATE_FLEE);
			return;
		}

		_combatSubState = EStateCheckWeaponState;

		break;
		}

	default:
		break;
	}
}
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();
}
void AgitatedSearchingState::SetRepeatedBark(idAI* owner)
{
	Memory& memory = owner->GetMemory();
	ERepeatedBarkState newRepeatedBarkState = ERBS_NULL;

	Search *search = gameLocal.m_searchManager->GetSearch(owner->m_searchID);
	if (search) // should always be non-NULL
	{
		Assignment *assignment = gameLocal.m_searchManager->GetAssignment(search,owner);
		int searcherCount = search->_searcherCount;
		idStr soundName;

		if (owner->HasSeenEvidence())
		{
			if (assignment && (assignment->_searcherRole == E_ROLE_SEARCHER))
			{
				if (searcherCount > 1)
				{
					newRepeatedBarkState = ERBS_SEARCHER_MULTIPLE_EVIDENCE;
				}
				else
				{
					newRepeatedBarkState = ERBS_SEARCHER_SINGLE_EVIDENCE;
				}
			}
			else
			{
				newRepeatedBarkState = ERBS_GUARD_OBSERVER;
			}
		}
		else // has not seen evidence
		{
			if (assignment && (assignment->_searcherRole == E_ROLE_SEARCHER))
			{
				if (searcherCount > 1)
				{
					newRepeatedBarkState = ERBS_SEARCHER_MULTIPLE_NO_EVIDENCE;
				}
				else
				{
					newRepeatedBarkState = ERBS_SEARCHER_SINGLE_NO_EVIDENCE;
				}
			}
			else
			{
				newRepeatedBarkState = ERBS_GUARD_OBSERVER;
			}
		}
	}

	if (memory.repeatedBarkState != newRepeatedBarkState)
	{
		memory.repeatedBarkState = newRepeatedBarkState;

		bool sendSuspiciousMessage = true; // whether to send a 'something is suspicious' message or not

		owner->commSubsystem->ClearTasks(); // kill any previous repeated bark task; we'll be starting a new one

		idStr soundName;
		switch (newRepeatedBarkState)
		{
		case ERBS_SEARCHER_SINGLE_NO_EVIDENCE:
			soundName = "snd_state4SeenNoEvidence"; // what to say when you're by yourself and you've seen no evidence
			break;
		case ERBS_SEARCHER_MULTIPLE_NO_EVIDENCE:
			soundName = "snd_state4SeenNoEvidence_c"; // what to say when friends are helping you search and you've seen no evidence
			break;
		case ERBS_SEARCHER_SINGLE_EVIDENCE:
			soundName = "snd_state4SeenEvidence"; // what to say when you're by yourself and you've seen evidence
			break;
		case ERBS_SEARCHER_MULTIPLE_EVIDENCE:
			soundName = "snd_state4SeenEvidence_c"; // what to say when friends are helping you search and you've seen evidence
			break;
		default:
		case ERBS_NULL:
		case ERBS_GUARD_OBSERVER:
			soundName = "snd_state3"; // guards and observers say this, regardless of whether they've seen evidence or not

			// grayman #3857 - "snd_state3" repeated barks are not intended to
			// alert nearby friends. Just send along a blank message.
			sendSuspiciousMessage = false;
			break;
		}

		CommMessagePtr message;

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

		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(soundName, minTime, maxTime, message)));
	}
}