Exemplo n.º 1
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() );
		}
	}
}
Exemplo n.º 2
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();
	}
}
Exemplo n.º 3
0
void IdleSleepState::Init(idAI* owner)
{
	// Init base class first
	State::Init(owner);

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

	// Memory shortcut
	Memory& memory = owner->GetMemory();
	memory.alertClass = EAlertNone;
	memory.alertType = EAlertTypeNone;

	if (owner->HasSeenEvidence() && !owner->spawnArgs.GetBool("disable_alert_idle", "0"))
	{
		owner->GetMind()->SwitchState(STATE_ALERT_IDLE);
		return;
	}

	if (owner->GetMoveType() != MOVETYPE_SLEEP && 
		(!owner->ReachedPos(memory.idlePosition, MOVE_TO_POSITION) 
			|| owner->GetCurrentYaw() != memory.idleYaw))
	{
		// we need to get to the bed first before starting to sleep, back to idle state
		owner->GetMind()->SwitchState(owner->backboneStates[ERelaxed]);
		return;
	}

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

	_startSleeping = owner->spawnArgs.GetBool("sleeping", "0");
	_startSitting = owner->spawnArgs.GetBool("sitting", "0");

	_alertLevelDecreaseRate = 0.01f;

	// owner->SheathWeapon();

	owner->actionSubsystem->ClearTasks();
	owner->senseSubsystem->ClearTasks();
	owner->commSubsystem->ClearTasks();

	InitialiseMovement(owner);

	int idleBarkIntervalMin = SEC2MS(owner->spawnArgs.GetInt("sleep_bark_interval_min", "10"));
	int idleBarkIntervalMax = SEC2MS(owner->spawnArgs.GetInt("sleep_bark_interval_max", "30"));

	owner->commSubsystem->AddCommTask(
		CommunicationTaskPtr(new RepeatedBarkTask("snd_sleeping", idleBarkIntervalMin, idleBarkIntervalMax))
	);

	// Let the AI update their weapons (make them nonsolid)
	owner->UpdateAttachmentContents(false);
}
void AgitatedSearchingStateLanternBot::Init(idAI* owner)
{
	// Init base class first
	State::Init(owner);

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

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

	owner->movementSubsystem->ClearTasks();
	owner->senseSubsystem->ClearTasks();
	owner->actionSubsystem->ClearTasks();
	owner->commSubsystem->ClearTasks(); // grayman #3182
	owner->searchSubsystem->ClearTasks(); // grayman #3857

	owner->StopMove(MOVE_STATUS_DONE);

	// Start with an invalid position
	_curAlertPos = idVec3(idMath::INFINITY, idMath::INFINITY, idMath::INFINITY);

	// Move to a position where we can light up the alert position from
	MoveTowardAlertPos(owner);

	owner->GetMemory().currentSearchEventID = owner->LogSuspiciousEvent( E_EventTypeMisc, owner->GetPhysics()->GetOrigin(), NULL, true ); // grayman #3857

	// This will hold the message to be delivered with the inaudible bark
	CommMessagePtr message(new CommMessage(
		CommMessage::RequestForHelp_CommType, // grayman #3857 - asking for a response
		//CommMessage::DetectedEnemy_CommType,  // grayman #3857 - this does nothing when no entity (parameter 4) is provided
		owner, NULL,// from this AI to anyone 
		NULL,
		_curAlertPos,
		owner->GetMemory().currentSearchEventID // grayman #3857 (was '0')
	));

	// The communication system plays starting bark
	owner->commSubsystem->AddCommTask(
		CommunicationTaskPtr(new RepeatedBarkTask("snd_spotted_noise", 3000, 4000, message))
	);

	// Add the script task blowing the alarm whistle
	owner->actionSubsystem->PushTask(TaskPtr(new ScriptTask("startAlarmWhistle")));
}
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 );
	}
}
Exemplo n.º 6
0
// Gets called each time the mind is thinking
void PocketPickedState::Think(idAI* owner)
{
	owner->PerformVisualScan();	// Let the AI check its senses

	// Check conditions for continuing.
	
	moveType_t moveType = owner->GetMoveType();
	Memory& memory = owner->GetMemory();

	if ( owner->AI_DEAD || // stop reacting if dead
		 owner->AI_KNOCKEDOUT || // or unconscious
		 (owner->AI_AlertIndex >= ESearching) || // stop if alert level is too high
		 memory.stopReactingToPickedPocket || // check if something happened to abort the state
		 (moveType == MOVETYPE_SLEEP) || // or asleep
		 (moveType == MOVETYPE_SIT_DOWN) || // or in the act of sitting down
		 (moveType == MOVETYPE_LAY_DOWN) || // or in the act of lying down to sleep
		 (moveType == MOVETYPE_GET_UP) ||   // or getting up from sitting
		 (moveType == MOVETYPE_GET_UP_FROM_LYING) ) // or getting up from lying down
	{
		Wrapup(owner);
		return;
	}

	switch (_pocketPickedState)
	{
		case EStateReact:
			{
			owner->actionSubsystem->ClearTasks();
			owner->movementSubsystem->ClearTasks();
			owner->m_ReactingToPickedPocket = true;

			// Emit the picked pocket bark if alert level is low, and we're not in Alert Idle
				
			if (owner->AI_AlertIndex < EObservant)
			{
				if (!(owner->HasSeenEvidence() && ( owner->spawnArgs.GetBool("disable_alert_idle", "0") == false) ) )
				{
					CommMessagePtr message;
					owner->commSubsystem->AddCommTask(CommunicationTaskPtr(new SingleBarkTask("snd_notice_pickpocket", message)));
				}
			}

			// If this occurred w/in the original alert window that surrounds the
			// pickpocket event, it's one more piece of evidence of something out of place.
			if (memory.insideAlertWindow)
			{
				memory.insideAlertWindow = false;
				memory.countEvidenceOfIntruders += EVIDENCE_COUNT_INCREASE_SUSPICIOUS;
				memory.posEvidenceIntruders = owner->GetPhysics()->GetOrigin();
				memory.timeEvidenceIntruders = gameLocal.time;
			}

			// Increase the alert level?
			float alertInc = owner->spawnArgs.GetFloat("pickpocket_alert","0");
			if (alertInc > 0.0f)
			{
				// Set the new alert level, but cap it just under Combat.
				// If the new alert level pushes the AI up into
				// Searching or Agitated Searching, the Picked Pocket State will end.

				float newAlertLevel = owner->AI_AlertLevel + alertInc;
				if ( newAlertLevel >= owner->thresh_5 )
				{
					newAlertLevel = owner->thresh_5 - 0.1;
				}
				owner->SetAlertLevel(newAlertLevel);

				// If the alert level is now in Searching or Agitated Searching,
				// create a search area.

				if ( owner->AI_AlertIndex >= ESearching )
				{
					memory.alertPos = owner->GetPhysics()->GetOrigin();
					memory.alertClass = EAlertTactile;
					memory.alertType = EAlertTypeSuspiciousItem;
					memory.alertRadius = AUDIO_ALERT_RADIUS;
					memory.alertSearchVolume = AUDIO_SEARCH_VOLUME;
					memory.alertSearchExclusionVolume.Zero();
					owner->AI_VISALERT = false;
					memory.visualAlert = false;
					memory.mandatory = false;
					memory.stimulusLocationItselfShouldBeSearched = true;
					memory.investigateStimulusLocationClosely = false;
					memory.alertedDueToCommunication = false;
				}
			}

			_pocketPickedState = EStateStopping;
			break;
			}
		case EStateStopping:
			// If AI is sitting, he won't stand or stop moving

			if ( owner->GetMoveType() == MOVETYPE_SIT )
			{
				_waitEndTime = gameLocal.time;
				_pocketPickedState = EStateTurnToward;
			}
			else
			{
				// Stop moving
				owner->StopMove(MOVE_STATUS_DONE);
				_pocketPickedState = EStateStartAnim;
			}
			break;
		case EStateStartAnim:
			// Don't play the animation if you're holding a torch or lantern.
			// If you're holding a weapon, you probably shouldn't be here, but
			// the weapon defs include replacement anims.
			if ( ( owner->GetTorch() == NULL ) && ( owner->GetLantern() == NULL ))
			{
				// Start the animation
				owner->SetAnimState(ANIMCHANNEL_TORSO, "Torso_PocketPicked", 4);
				owner->SetAnimState(ANIMCHANNEL_LEGS, "Legs_PocketPicked", 4);

				owner->SetWaitState("pocketPicked");
				owner->SetWaitState(ANIMCHANNEL_TORSO, "pocketPicked");
				owner->SetWaitState(ANIMCHANNEL_LEGS, "pocketPicked");
			}
			_pocketPickedState = EStatePlayAnim;
			break;
		case EStatePlayAnim:
			if (idStr(owner->WaitState()) != "pocketPicked")
			{
				// Turn to look behind you.
				owner->TurnToward(owner->GetCurrentYaw() + 180);
				_waitEndTime = gameLocal.time + 1000;
				_pocketPickedState = EStateTurnToward;
			}
			break;
		case EStateTurnToward:
			if (gameLocal.time >= _waitEndTime)
			{
				// Done turning.

				idVec3 vec;
				float duration = LOOK_TIME_MIN + gameLocal.random.RandomFloat()*(LOOK_TIME_MAX - LOOK_TIME_MIN);
				if ( owner->GetMoveType() == MOVETYPE_SIT )
				{
					// Stare at the ground to your right or left, as if looking for something

					idAngles angles = owner->viewAxis.ToAngles();
					float forward = angles.yaw;
					float lookAngle = forward + (gameLocal.random.RandomFloat() < 0.5 ? 70 : -70);
					idAngles lookAngles(0.0f, lookAngle, 0.0f);
					vec = lookAngles.ToForward()*100; // ToForward() returns unit vector
				}
				else
				{
					// Stare at the ground in front of you, as if looking for something

					vec = owner->viewAxis.ToAngles().ToForward()*100; // ToForward() returns unit vector
				}
				vec.z = 0;
				idVec3 lookAtMe = owner->GetEyePosition() + vec;
				lookAtMe.z = owner->GetPhysics()->GetOrigin().z;
				owner->Event_LookAtPosition(lookAtMe, MS2SEC(duration));
				_pocketPickedState = EStateLookAt;
				_waitEndTime = gameLocal.time + duration;
			}
			break;
		case EStateLookAt: // look for a while
			if (gameLocal.time >= _waitEndTime)
			{
				// Done looking, end this state
				Wrapup(owner);
				return;
			}
			break;
		default:
			break;
	}
}
Exemplo n.º 7
0
void LostTrackOfEnemyState::Init(idAI* owner)
{
	// Init base class first
	State::Init(owner);

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

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

	owner->SetAlertLevel((owner->thresh_5 + owner->thresh_4) * 0.5);

	// Draw weapon, if we haven't already
	if (!owner->GetAttackFlag(COMBAT_MELEE) && !owner->GetAttackFlag(COMBAT_RANGED))
	{
		// grayman #3331 - draw your ranged weapon if you have one, otherwise draw your melee weapon.
		// Note that either weapon could be drawn, but if we default to melee, AI with ranged and
		// melee weapons will draw their melee weapon, and we'll never see ranged weapons get drawn.
		// Odds are that the enemy is nowhere nearby anyway, since we're just searching.

		if ( ( owner->GetNumRangedWeapons() > 0 ) && !owner->spawnArgs.GetBool("unarmed_ranged","0") )
		{
			owner->DrawWeapon(COMBAT_RANGED);
		}
		else if ( ( owner->GetNumMeleeWeapons() > 0 ) && !owner->spawnArgs.GetBool("unarmed_melee","0") )
		{
			owner->DrawWeapon(COMBAT_MELEE);
		}
	}

	// Setup the search parameters
	memory.alertPos = owner->lastVisibleReachableEnemyPos;
	memory.alertRadius = LOST_ENEMY_ALERT_RADIUS;
	memory.alertSearchVolume = LOST_ENEMY_SEARCH_VOLUME;
	memory.alertSearchExclusionVolume.Zero();

	memory.alertedDueToCommunication = false;
	memory.stimulusLocationItselfShouldBeSearched = true;

	// Forget about the enemy, prevent UpdateEnemyPosition from "cheating".
	owner->ClearEnemy();
	memory.visualAlert = false; // grayman #2422
	memory.mandatory = true;	// grayman #3331

	// Enqueue a lost track of enemy bark
	owner->commSubsystem->AddCommTask(
		CommunicationTaskPtr(new SingleBarkTask("snd_lostTrackOfEnemy"))
	);

	if (cv_ai_debug_transition_barks.GetBool())
	{
		gameLocal.Printf("%d: %s loses track of the enemy, barks 'snd_lostTrackOfEnemy'\n",gameLocal.time,owner->GetName());
	}

	// For now, clear the action tasks and movement tasks
	owner->actionSubsystem->ClearTasks();
	owner->movementSubsystem->ClearTasks();

	owner->GetMind()->EndState();
}
Exemplo n.º 8
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;
	}
}
Exemplo n.º 9
0
void CombatState::Init(idAI* owner)
{
	// Init base class first
	State::Init(owner);

	// Set end time to something invalid
	_endTime = -1;

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

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

	if (!owner->GetMind()->PerformCombatCheck())
	{
		return;
	}

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

	// grayman #3075 - if we're kneeling, doing close inspection of
	// a spot, stop the animation. Otherwise, the kneeling animation gets
	// restarted a few moments later.

	idStr torsoString = "Torso_KneelDown";
	idStr legsString = "Legs_KneelDown";
	bool torsoKneelingAnim = (torsoString.Cmp(owner->GetAnimState(ANIMCHANNEL_TORSO)) == 0);
	bool legsKneelingAnim = (legsString.Cmp(owner->GetAnimState(ANIMCHANNEL_LEGS)) == 0);

	if ( torsoKneelingAnim || legsKneelingAnim )
	{
		// Reset anims
		owner->StopAnim(ANIMCHANNEL_TORSO, 0);
		owner->StopAnim(ANIMCHANNEL_LEGS, 0);
	}

	// grayman #3472 - kill the repeated bark task
	owner->commSubsystem->ClearTasks(); // grayman #3182

	// grayman #3496
	// Enough time passed since last alert bark?

	if ( gameLocal.time >= owner->GetMemory().lastTimeAlertBark + MIN_TIME_BETWEEN_ALERT_BARKS )
	{
		// grayman #3496 - But only bark if you haven't recently spent time in Agitated Search.

		if ( !owner->GetMemory().agitatedSearched )
		{
			// The communication system plays reaction bark
			CommMessagePtr message;
			owner->commSubsystem->AddCommTask(CommunicationTaskPtr(new SingleBarkTask("snd_alert5", message)));

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

			if (cv_ai_debug_transition_barks.GetBool())
			{
				gameLocal.Printf("%d: %s enters Combat State, barks surprised reaction 'snd_alert5'\n",gameLocal.time,owner->GetName());
			}
		}
		else
		{
			if (cv_ai_debug_transition_barks.GetBool())
			{
				gameLocal.Printf("%d: %s enters Combat State after spending time in Agitated Searching, so won't bark 'snd_alert5'\n",gameLocal.time,owner->GetName());
			}
		}
	}
	else
	{
		if (cv_ai_debug_transition_barks.GetBool())
		{
			gameLocal.Printf("%d: %s enters Combat State, can't bark 'snd_alert5' yet\n",gameLocal.time,owner->GetName());
		}
	}

	// All remaining init code is moved into Think() and done in the EStateReaction substate,
	// because the things it does need to occur after the initial reaction delay.

	// grayman #3063
	// Add a delay before you process the remainder of Init().
	// The length of the delay depends on the distance to the enemy.

	// We have an enemy, store the enemy entity locally
	_enemy = owner->GetEnemy();
	idActor* enemy = _enemy.GetEntity();

	// grayman #3331 - clear combat state
	_combatType = COMBAT_NONE;
	
	// get melee possibilities

	_meleePossible  = ( owner->GetNumMeleeWeapons()  > 0 );
	_rangedPossible = ( owner->GetNumRangedWeapons() > 0 );

	/* grayman #3492 - should wait until after the reaction time before fleeing

	// grayman #3355 - flee if you have no weapons

	if (!_meleePossible && !_rangedPossible)
	{
		owner->fleeingEvent = false; // grayman #3356
		owner->emitFleeBarks = true; // grayman #3474
		owner->GetMind()->SwitchState(STATE_FLEE);
		return;
	} */

	// grayman #3331 - save combat possibilities
	_unarmedMelee = owner->spawnArgs.GetBool("unarmed_melee","0");
	_unarmedRanged = owner->spawnArgs.GetBool("unarmed_ranged","0");
	_armedMelee = _meleePossible && !_unarmedMelee;
	_armedRanged = _rangedPossible && !_unarmedRanged;

	// grayman #3331 - do we need an initial delay at weapon drawing?
	_needInitialDrawDelay = !( owner->GetAttackFlag(COMBAT_MELEE) || owner->GetAttackFlag(COMBAT_RANGED) ); // not if we have a weapon raised

	idVec3 vec2Enemy = enemy->GetPhysics()->GetOrigin() - owner->GetPhysics()->GetOrigin();
	//vec2Enemy.z = 0; // ignore vertical component
	float dist2Enemy = vec2Enemy.LengthFast();
	int reactionTime =  REACTION_TIME_MIN + (dist2Enemy*(REACTION_TIME_MAX - REACTION_TIME_MIN))/(cv_ai_sight_combat_cutoff.GetFloat()/s_DOOM_TO_METERS);
	if ( reactionTime > REACTION_TIME_MAX )
	{
		reactionTime = REACTION_TIME_MAX;
	}

	// grayman #3331 - add a bit of variability so multiple AI spotting the enemy in the same frame aren't in sync

	reactionTime += gameLocal.random.RandomInt(REACTION_TIME_MAX/2);

	_combatSubState = EStateReaction;
	_reactionEndTime = gameLocal.time + reactionTime;
}
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)));
	}
}
Exemplo n.º 12
0
void ObservantState::Init(idAI* owner)
{
	// Init base class first
	State::Init(owner);

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

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

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

	float alertTime = owner->atime1 + owner->atime1_fuzzyness * (gameLocal.random.RandomFloat() - 0.5);
	_alertLevelDecreaseRate = (owner->thresh_2 - owner->thresh_1) / alertTime;

	// grayman #2866 - zero alert decrease rate if handling a door or elevator

	memory.savedAlertLevelDecreaseRate = _alertLevelDecreaseRate; // save for restoration later

	if ((owner->m_HandlingDoor) || (owner->m_HandlingElevator))
	{
		_alertLevelDecreaseRate = 0;
	}

	// Stop playing idle animation
	owner->actionSubsystem->ClearTasks();

	// grayman #3438 - kill the repeated bark task
	owner->commSubsystem->ClearTasks();

	owner->searchSubsystem->ClearTasks(); // grayman #3857
	memory.currentSearchEventID = -1;

	// grayman #3472 - Play bark if alert level is ascending
	// grayman #3487 - But not if asleep

	if (owner->GetMoveType() != MOVETYPE_SLEEP)
	{
		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 ) )
			{
				if ( !memory.alertedDueToCommunication && ( memory.alertClass != EAlertVisual_4 ) ) // grayman #2920, grayman #3498
				{
					// barking
					idStr bark;

					if ((memory.alertClass == EAlertVisual_1) ||
						(memory.alertClass == EAlertVisual_2) )
						//(memory.alertClass == EAlertVisual_3) ) // grayman #2603, #3424, grayman #3472 - no longer needed
					{
						bark = "snd_alert1s";
					}
					else if (memory.alertClass == EAlertAudio)
					{
						bark = "snd_alert1h";
					}
					else
					{
						bark = "snd_alert1";
					}

					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 Observant state, barks '%s'\n",gameLocal.time,owner->GetName(),bark.c_str());
					}
				}
				else
				{
					memory.alertedDueToCommunication = false; // reset
				}
			}
			else
			{
				if (cv_ai_debug_transition_barks.GetBool())
				{
					gameLocal.Printf("%d: %s rises to Observant state, can't bark 'snd_alert1s' yet\n",gameLocal.time,owner->GetName());
				}
			}
		}
	}

	// Let the AI update their weapons (make them nonsolid)
	owner->UpdateAttachmentContents(false);
}
Exemplo n.º 13
0
void UnreachableTargetState::Init(idAI* owner)
{
	// Init base class first
	State::Init(owner);

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

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

	idActor* enemy = owner->GetEnemy();

	if (!enemy)
	{
		owner->GetMind()->SwitchState(STATE_LOST_TRACK_OF_ENEMY);
		return;
	}

	_enemy = enemy;

	// This checks if taking cover is possible and enabled for this AI
	_takingCoverPossible = false;
	if (owner->spawnArgs.GetBool("taking_cover_enabled","0"))
	{
		aasGoal_t hideGoal;
		 // grayman #3280 - enemies look with their eyes, not their feet
		_takingCoverPossible = owner->LookForCover(hideGoal, enemy, enemy->GetEyePosition());
		if (_takingCoverPossible)
		{
			// We should not go into TakeCoverState if we are already at a suitable position
			if (hideGoal.origin == owner->GetPhysics()->GetOrigin() )
			{
				_takingCoverPossible = false;
			}
			DM_LOG(LC_AI, LT_INFO)LOGSTRING("Taking Cover Possible: %d \r" , _takingCoverPossible);
		}
	}

	_takeCoverTime = -1;

	// Fill the subsystems with their tasks

	// Create the message
	CommMessagePtr message(new CommMessage(
		CommMessage::RequestForMissileHelp_CommType, 
		owner, NULL, // from this AI to anyone 
		enemy,
		memory.lastEnemyPos,
		0
	));

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

	idStr bark = "";
	idStr enemyAiUse = enemy->spawnArgs.GetString("AIUse");
	if ( ( enemyAiUse == AIUSE_MONSTER ) || ( enemyAiUse == AIUSE_UNDEAD ) )
	{
		bark = "snd_cantReachTargetMonster";
	}
	else
	{
		bark = "snd_cantReachTarget";
	}
	owner->commSubsystem->AddCommTask(CommunicationTaskPtr(new SingleBarkTask(bark, message)));

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

	// The sensory system does nothing so far
	owner->senseSubsystem->ClearTasks();

	owner->StopMove(MOVE_STATUS_DONE);
	owner->movementSubsystem->ClearTasks();
	memory.stopRelight = true; // grayman #2603 - abort a relight in progress
	memory.stopExaminingRope = true; // grayman #2872 - stop examining a rope
	memory.stopReactingToHit = true; // grayman #2816

	owner->actionSubsystem->ClearTasks();

	_moveRequired = false;

	if (owner->spawnArgs.GetBool("outofreach_projectile_enabled", "0"))
	{
		// Check the distance between AI and the player, if it is too large try to move closer
		// Start throwing objects if we are close enough
		idVec3 enemyDirection = enemy->GetPhysics()->GetOrigin() - owner->GetPhysics()->GetOrigin();
		float dist = (enemyDirection).LengthFast();
		
		//TODO: make not hardcoded
		if (dist > 300)
		{
			_moveRequired = true;

			idVec3 throwPos = enemy->GetPhysics()->GetOrigin() - enemyDirection / dist * 300;

			// TODO: Trace to get floor position
			throwPos.z = owner->GetPhysics()->GetOrigin().z;

			owner->movementSubsystem->PushTask(TaskPtr(new MoveToPositionTask(throwPos)));
			owner->AI_MOVE_DONE = false;
		}
		else 
		{
			// greebo: Sheathe weapon before starting to throw // FIXME: put weapon to left hand?
			owner->SheathWeapon();

			owner->FaceEnemy();
			owner->actionSubsystem->PushTask(ThrowObjectTask::CreateInstance());

			// Wait at least 3 sec after starting to throw before taking cover
			// TODO: make not hardcoded, some randomness?
			_takeCoverTime = gameLocal.time + 3000;
		}
		DM_LOG(LC_AI, LT_INFO)LOGSTRING("move required: %d \r" , _moveRequired);
	}
	else
	{
		owner->movementSubsystem->PushTask(
			TaskPtr(new MoveToPositionTask(owner->lastVisibleReachableEnemyPos))
		);
		_takeCoverTime = gameLocal.time + 3000;
	}

	_reachEnemyCheck = 0;
}
Exemplo n.º 14
0
// Gets called each time the mind is thinking
void HitByMoveableState::Think(idAI* owner)
{
	// grayman #3424 - don't process if dead or unconscious
	if ( owner->AI_DEAD || owner->AI_KNOCKEDOUT )
	{
		Wrapup(owner);
		return;
	}

	// check if something happened to abort the state
	if (owner->GetMemory().stopReactingToHit)
	{
		Wrapup(owner);
		return;
	}

	owner->PerformVisualScan();	// Let the AI check its senses
	if (owner->AI_AlertLevel >= owner->thresh_5) // finished if alert level is too high
	{
		Wrapup(owner);
		return;
	}

	idEntity* tactEnt = owner->GetMemory().hitByThisMoveable.GetEntity();
	if ( tactEnt == NULL )
	{
		Wrapup(owner);
		return;
	}

	switch (_hitByMoveableState)
	{
		case EStateSittingSleeping:
			if (gameLocal.time >= _waitEndTime)
			{
				if (owner->AI_MOVE_DONE && (owner->GetMoveType() != MOVETYPE_GET_UP)) // standing yet?
				{
					_hitByMoveableState = EStateTurnToward;
				}
			}
			break;
		case EStateStarting:
			if (gameLocal.time >= _waitEndTime)
			{
				_hitByMoveableState = EStateTurnToward;
			}
			break;
		case EStateTurnToward: // Turn toward the object that hit you.
			{
				// if _lookAtDuration is 0, skip turning toward and looking at the object

				if ( _lookAtDuration == 0 )
				{
					_hitByMoveableState = EStateTurnBack;
				}
				else
				{
					owner->TurnToward(tactEnt->GetPhysics()->GetOrigin());
					_hitByMoveableState = EStateLookAt;
					_waitEndTime = gameLocal.time + 1000;
				}
			}
			break;
		case EStateLookAt: // look at the entity
			if (gameLocal.time >= _waitEndTime)
			{
				float duration = _lookAtDuration + (_lookAtDuration/5)*(gameLocal.random.RandomFloat() - 0.5f );
				owner->Event_LookAtEntity( tactEnt, duration );

				_waitEndTime = gameLocal.time + duration*1000;
				_hitByMoveableState = EStateTurnBack;
			}
			break;
		case EStateTurnBack: // turn toward the direction the entity came from
			if (gameLocal.time >= _waitEndTime)
			{
				owner->TurnToward(_pos);
				_waitEndTime = gameLocal.time + 1000;
				_hitByMoveableState = EStateLookBack;
			}
			break;
		case EStateLookBack: // look in the direction the entity came from
			if (gameLocal.time >= _waitEndTime)
			{
				if ( _lookBackDuration > 0 )
				{
					float duration = _lookBackDuration + (_lookBackDuration/5)*(gameLocal.random.RandomFloat() - 0.5f );
					owner->Event_LookAtPosition( _pos, duration );
					_waitEndTime = gameLocal.time + duration*1000;
				}
				_hitByMoveableState = EStateFinal;
			}
			break;
		case EStateFinal:
			if (gameLocal.time >= _waitEndTime)
			{
				// If we make it to here, we haven't spotted
				// an enemy, otherwise our alert level would
				// have been high enough to abort this state
				// at the beginning of Think().

				// If we see a friend or a neutral, we assume
				// this was a harmless impact, and we can return
				// to what we were doing.

				idVec3 ownerOrg = owner->GetPhysics()->GetOrigin();
				idBounds bounds( ownerOrg - idVec3( HIT_FIND_THROWER_HORZ, HIT_FIND_THROWER_HORZ, HIT_FIND_THROWER_VERT ), ownerOrg + idVec3( HIT_FIND_THROWER_HORZ, HIT_FIND_THROWER_HORZ, HIT_FIND_THROWER_VERT ) );

				idEntity* ents[MAX_GENTITIES];
				int num = gameLocal.clip.EntitiesTouchingBounds( bounds, CONTENTS_BODY, ents, MAX_GENTITIES );

				for ( int i = 0 ; i < num ; i++ )
				{
					idEntity *candidate = ents[i];

					if ( candidate == NULL ) // just in case
					{
						continue;
					}

					if ( candidate == owner ) // skip myself
					{
						continue;
					}

					if ( !candidate->IsType(idActor::Type) ) // skip non-Actors
					{
						continue;
					}

					idActor *candidateActor = static_cast<idActor *>(candidate);

					if ( candidateActor->GetPhysics()->GetMass() <= 5.0 ) // skip actors with small mass (rats, spiders)
					{
						continue;
					}

					if ( candidateActor->IsKnockedOut() || ( candidateActor->health < 0 ) )
					{
						continue; // skip if knocked out or dead
					}

					if ( candidateActor->IsType(idAI::Type) )
					{
						if ( static_cast<idAI*>(candidateActor)->GetMoveType() == MOVETYPE_SLEEP )
						{
							continue; // skip if asleep
						}
					}

					if ( candidateActor->fl.notarget )
					{
						continue; // skip if NOTARGET is set
					}

					if ( owner->IsFriend(candidateActor) || owner->IsNeutral(candidateActor) )
					{
						// Don't admonish the alleged thrower if tactEnt came in from above (dropped)

						if ( _pos.z <= owner->GetEyePosition().z )
						{
							// make sure there's LOS (no walls in between)

							if ( owner->CanSeeExt( candidateActor, false, false ) ) // don't use FOV or lighting, to increase chance of success
							{
								// look at your friend/neutral if they're in your FOV
								int delay = 0; // bark delay
								if ( owner->CanSeeExt( candidateActor, true, false) ) // use FOV this time, but ignore lighting
								{
									owner->Event_LookAtPosition(candidateActor->GetEyePosition(),2.0f);
									delay = 1000; // give head time to move before barking
								}

								// bark an admonishment whether you're facing them or not

								CommMessagePtr message; // no message, but the argument is needed so the start delay can be included
								owner->commSubsystem->AddCommTask(CommunicationTaskPtr(new SingleBarkTask("snd_admonish_friend",message,delay)));

								if (cv_ai_debug_transition_barks.GetBool())
								{
									gameLocal.Printf("%d: %s hit by moveable, barks 'snd_admonish_friend'\n",gameLocal.time,owner->GetName());
								}

								Wrapup(owner);
								return;
							}
						}
					}
				}

				// No one about, so let's cheat. If a friend or neutral threw
				// the object, we return to what we were doing.

				idActor* responsible = _responsibleActor.GetEntity();

				if ( ( responsible == NULL ) || owner->IsFriend(responsible) || owner->IsNeutral(responsible) )
				{
					Wrapup(owner);
					return;
				}

				// No one about. If an enemy threw the object, start searching.

				if ( owner->IsEnemy(responsible) )
				{
					Memory& memory = owner->GetMemory();
					memory.alertType = EAlertTypeSuspicious;
					memory.alertClass = EAlertTactile;
					memory.alertPos = ownerOrg;
					memory.alertRadius = TACTILE_ALERT_RADIUS;
					memory.alertSearchVolume = TACTILE_SEARCH_VOLUME;
					memory.alertSearchExclusionVolume.Zero();
					memory.visualAlert = false;

					// If we got this far, we give the alert
					// Set the alert amount to the according tactile alert value
					float amount = cv_ai_tactalert.GetFloat();

					// NOTE: Latest tactile alert always overrides other alerts
					owner->m_TactAlertEnt = tactEnt;
					owner->m_AlertedByActor = responsible;

					amount *= owner->GetAcuity("tact");
					// grayman #3009 - pass the alert position so the AI can look in the direction of who's responsible
					owner->PreAlertAI("tact", amount, responsible->GetEyePosition()); // grayman #3356

					// Set last visual contact location to this location as that is used in case
					// the target gets away.
					owner->m_LastSight = ownerOrg;

					// If no enemy set so far, set the last visible enemy position.
					if ( owner->GetEnemy() == NULL )
					{
						owner->lastVisibleEnemyPos = ownerOrg;
					}

					owner->AI_TACTALERT = true;
					memory.mandatory = true; // grayman #3331
				}

				Wrapup(owner);
				return;
			}
			break;
		default:
			break;
	}
}