Пример #1
0
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
}