void InvestigateSpotTask::SetNewGoal( const idVec3 &newPos ) { idAI *owner = _owner.GetEntity(); if( owner == NULL ) { return; } // Copy the value _searchSpot = newPos; // Reset the "move started" flag _moveInitiated = false; // Set the exit time back to negative default, so that the AI starts walking again _exitTime = -1; // Check if we can see the point from where we are (only for remote inspection) if( !_investigateClosely && ( ( _searchSpot - owner->GetPhysics()->GetOrigin() ).LengthFast() < INVESTIGATE_SPOT_STOP_DIST ) && // grayman #2640 - reduces AI search freezing owner->CanSeePositionExt( _searchSpot, false, true ) ) { DM_LOG( LC_AI, LT_INFO )LOGVECTOR( "I can see the point...\r", _searchSpot ); if( !owner->CheckFOV( _searchSpot ) ) { // Search spot is not within FOV, turn towards the position owner->TurnToward( _searchSpot ); } // In any case, look at the point to investigate owner->Event_LookAtPosition( _searchSpot, 2.0f ); // Wait a bit _exitTime = static_cast<int>( gameLocal.time + INVESTIGATE_SPOT_TIME_REMOTE * ( 1 + gameLocal.random.RandomFloat() ) // grayman #2640 ); } }
bool InvestigateSpotTask::Perform(Subsystem& subsystem) { DM_LOG(LC_AI, LT_INFO)LOGSTRING("InvestigateSpotTask performing.\r"); idAI* owner = _owner.GetEntity(); assert(owner != NULL); // grayman #3857 - quit if incapable of continuing if (owner->AI_DEAD || owner->AI_KNOCKEDOUT) { return true; } if (owner->GetMemory().stopHidingSpotInvestigation) // grayman #3857 { return true; // told to cancel this task } // grayman #3075 - if we've entered combat mode, we want to // end this task. But first, if we're kneeling, kill the // kneeling animation if ( owner->AI_AlertIndex == ECombat ) { 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); owner->SetWaitState(""); // grayman #3848 } return true; } if (_exitTime > 0) { // Return TRUE if the time is over, else FALSE (continue) return (gameLocal.time >= _exitTime); } // No exit time set, continue with ordinary process if (owner->m_HandlingDoor || owner->m_HandlingElevator) { // Wait, we're busy with a door or elevator return false; } // grayman #3510 if (owner->m_RelightingLight) { // Wait, we're busy relighting a light so we have more light to search by return false; } idVec3 ownerOrigin = owner->GetPhysics()->GetOrigin(); // grayman #3492 if (!_moveInitiated) { idVec3 destPos = _searchSpot; // greebo: For close investigation, don't step up to the very spot, to prevent the AI // from kneeling into bloodspots or corpses idVec3 direction = ownerOrigin - _searchSpot; if (_investigateClosely) { idVec3 dir = direction; dir.NormalizeFast(); // 20 units before the actual spot destPos += dir * 20; } // grayman #2603 - The fix to #2640 had the AI stopping if he's w/in INVESTIGATE_SPOT_STOP_DIST of the // search spot. This caused sudden jerks if he's closer than that at the start of the // move. To prevent that, check if he's close and--if so--don't start the move. // grayman #3756 - If the stimulus location should be walked to, // as in the case of a suspicious door, don't check whether you're // close enough to just look at the spot. Go there. if (!owner->GetMemory().stimulusLocationItselfShouldBeSearched && // grayman #3756 !_investigateClosely && (direction.LengthFast() < INVESTIGATE_SPOT_STOP_DIST)) { // Wait a bit _exitTime = static_cast<int>( gameLocal.time + INVESTIGATE_SPOT_TIME_REMOTE*(1 + gameLocal.random.RandomFloat()) // grayman #2640 ); // Look at the point to investigate owner->Event_LookAtPosition(_searchSpot + idVec3(0,0,60), MS2SEC(_exitTime - gameLocal.time + 100)); // grayman #3857 - look at a point off the floor return false; // grayman #2422 } owner->GetMemory().stimulusLocationItselfShouldBeSearched = false; // grayman #3756 // Let's move // grayman #2422 // Here's the root of the problem. PointReachableAreaNum() // doesn't always look to the side to find the nearest AAS // area at the point's z position. It can move the point to // the AAS area above, or the AAS area below. This makes the AI // run upstairs or downstairs when all we really want him to do // is stay on the same floor. // If the AI is searching and not handling a door or handling // an elevator or resolving a block: If the spot PointReachableAreaNum()/PushPointIntoAreaNum() // wants to move us to is outside the vertical boundaries of the // search volume, consider the point bad. bool pointValid = true; idVec3 goal = destPos; int toAreaNum = owner->PointReachableAreaNum( goal ); if ( toAreaNum == 0 ) { pointValid = false; } else { owner->GetAAS()->PushPointIntoAreaNum( toAreaNum, goal ); // if this point is outside this area, it will be moved to one of the area's edges // double-check reachability toAreaNum = owner->PointReachableAreaNum(goal); if (toAreaNum == 0) { pointValid = false; } /* grayman #3857 - ContainsPoint() already tested before this task was given the point if ( owner->IsSearching() && !owner->movementSubsystem->IsResolvingBlock() && ( owner->AI_AlertIndex < ECombat ) ) // grayman #3070 - point is valid if in combat mode { if ( !assignment->_limits.ContainsPoint(goal) ) { pointValid = false; } } */ } if ( pointValid ) { pointValid = owner->MoveToPosition(goal); } if (!pointValid || (owner->GetMoveStatus() == MOVE_STATUS_DEST_UNREACHABLE)) { // grayman #3857- don't waste time staring at a spot you can't reach or see, because // if you're not going to be able to reach ANY of the spots assigned to you, // you will spend a lot of time doing nothing. // grayman #3492 - look at the spot idVec3 p = _searchSpot; p.z += 60; // look up a bit, to simulate searching for the player's head if (owner->CanSeePositionExt(p, false, false)) { _exitTime = static_cast<int>( gameLocal.time + INVESTIGATE_SPOT_TIME_REMOTE*(1 + gameLocal.random.RandomFloat()) //gameLocal.time + ((float)(INVESTIGATE_SPOT_TIME_REMOTE*(1 + gameLocal.random.RandomFloat()))) / 2.0f // grayman #2640 ); owner->TurnToward(p); owner->Event_LookAtPosition(p, MS2SEC(_exitTime - gameLocal.time)); } else { // Hiding spot not reachable or visible, so terminate task in the next round _exitTime = gameLocal.time; } } else { // Run if the point is more than MAX_TRAVEL_DISTANCE_WALKING // greebo: This is taxing and can be replaced by a simpler distance check // TravelDistance takes about ~0.1 msec on my 2.2 GHz system. // grayman #2422 - not the player = walk, player & combat = run, everything else = run // Also, travelDist is inaccurate when an AAS area is large, so compare // it to the actual distance and use the larger of the two. //gameRenderWorld->DebugArrow(colorYellow, owner->GetEyePosition(), _searchSpot, 1, MS2SEC(_exitTime - gameLocal.time + 100)); _moveInitiated = true; float actualDist = (ownerOrigin - _searchSpot).LengthFast(); owner->AI_RUN = actualDist > MAX_TRAVEL_DISTANCE_WALKING; } return false; } if (owner->GetMoveStatus() == MOVE_STATUS_DEST_UNREACHABLE) { DM_LOG(LC_AI, LT_INFO)LOGVECTOR("Hiding spot unreachable.\r", _searchSpot); return true; } if (owner->GetMoveStatus() == MOVE_STATUS_DONE) { DM_LOG(LC_AI, LT_INFO)LOGVECTOR("Hiding spot investigated: \r", _searchSpot); // grayman #2928 - don't kneel down if you're too far from the original stim float dist = ( ownerOrigin - owner->GetMemory().alertSearchCenter).LengthFast(); // grayman #3563 - don't kneel down if you're drawing a weapon if ( _investigateClosely && ( dist < INVESTIGATE_SPOT_CLOSELY_MAX_DIST ) && ( idStr(owner->WaitState()) != "draw") ) { // Stop previous moves owner->StopMove(MOVE_STATUS_WAITING); // Check the position of the stim, is it closer to the eyes than to the feet? // If it's lower than the eye position, kneel down and investigate //const idVec3& origin = owner->GetPhysics()->GetOrigin(); idVec3 eyePos = owner->GetEyePosition(); if ((_searchSpot - ownerOrigin).LengthSqr() < (_searchSpot - eyePos).LengthSqr()) { // Close to the feet, kneel down and investigate closely owner->SetAnimState(ANIMCHANNEL_TORSO, "Torso_KneelDown", 6); owner->SetAnimState(ANIMCHANNEL_LEGS, "Legs_KneelDown", 6); owner->SetWaitState("kneel_down"); // grayman #3563 } // Wait a bit, setting _exitTime sets the lifetime of this task _exitTime = static_cast<int>( gameLocal.time + INVESTIGATE_SPOT_TIME_CLOSELY*(1 + gameLocal.random.RandomFloat()*0.2f) ); } else { // Wait a bit, setting _exitTime sets the lifetime of this task _exitTime = static_cast<int>( gameLocal.time + INVESTIGATE_SPOT_TIME_STANDARD*(1 + gameLocal.random.RandomFloat()*0.2f) ); } } else { // Can we already see the point? Only stop moving when the spot shouldn't be investigated closely // angua: added distance check to avoid running in circles if the point is too close to a wall. // grayman #2640 - keep moving if you're > INVESTIGATE_SPOT_STOP_DIST from a point you can see bool stopping = false; if (!_investigateClosely) { float distToSpot = (_searchSpot - ownerOrigin).LengthFast(); if (owner->CanSeePositionExt(_searchSpot, true, true)) { if (distToSpot < INVESTIGATE_SPOT_STOP_DIST) { stopping = true; } } else if (distToSpot < INVESTIGATE_SPOT_MIN_DIST) { stopping = true; } } if (stopping) { DM_LOG(LC_AI, LT_INFO)LOGVECTOR("Stop, I can see the point now...\r", _searchSpot); // Stop moving, we can see the point owner->StopMove(MOVE_STATUS_DONE); // grayman #3492 - Look at a random point that may be anywhere // between the search point and a point 1/2 the AI's height // above his eye level. // grayman #3857 - now that AI are allowed closer to the search spot, // they shouldn't look so high up idVec3 p = _searchSpot; //float height = owner->GetPhysics()->GetBounds().GetSize().z; float bottom = p.z; float top = owner->GetEyePosition().z + 20; float dist = top - bottom; dist *= gameLocal.random.RandomFloat(); p.z += dist; // Look at the point to investigate owner->Event_LookAtPosition(p, 3.0f); //owner->Event_LookAtPosition(_searchSpot, 2.0f); // Wait a bit _exitTime = static_cast<int>( gameLocal.time + INVESTIGATE_SPOT_TIME_REMOTE*(1 + gameLocal.random.RandomFloat()) // grayman #2640 ); } // continue walking } return false; // not finished yet }