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(); } }
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() ); } } }
// 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(); } }
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(); } }
// 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))); } }